xref: /aosp_15_r20/external/elfutils/debuginfod/debuginfod-client.c (revision 7304104da70ce23c86437a01be71edd1a2d7f37e)
1 /* Retrieve ELF / DWARF / source files from the debuginfod.
2    Copyright (C) 2019-2021 Red Hat, Inc.
3    Copyright (C) 2021, 2022 Mark J. Wielaard <[email protected]>
4    This file is part of elfutils.
5 
6    This file is free software; you can redistribute it and/or modify
7    it under the terms of either
8 
9      * the GNU Lesser General Public License as published by the Free
10        Software Foundation; either version 3 of the License, or (at
11        your option) any later version
12 
13    or
14 
15      * the GNU General Public License as published by the Free
16        Software Foundation; either version 2 of the License, or (at
17        your option) any later version
18 
19    or both in parallel, as here.
20 
21    elfutils is distributed in the hope that it will be useful, but
22    WITHOUT ANY WARRANTY; without even the implied warranty of
23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24    General Public License for more details.
25 
26    You should have received copies of the GNU General Public License and
27    the GNU Lesser General Public License along with this program.  If
28    not, see <http://www.gnu.org/licenses/>.  */
29 
30 
31 /* cargo-cult from libdwfl linux-kernel-modules.c */
32 /* In case we have a bad fts we include this before config.h because it
33    can't handle _FILE_OFFSET_BITS.
34    Everything we need here is fine if its declarations just come first.
35    Also, include sys/types.h before fts. On some systems fts.h is not self
36    contained. */
37 #ifdef BAD_FTS
38   #include <sys/types.h>
39   #include <fts.h>
40 #endif
41 
42 #include "config.h"
43 #include "debuginfod.h"
44 #include "system.h"
45 #include <ctype.h>
46 #include <errno.h>
47 #include <stdlib.h>
48 #include <gelf.h>
49 
50 /* We might be building a bootstrap dummy library, which is really simple. */
51 #ifdef DUMMY_LIBDEBUGINFOD
52 
debuginfod_begin(void)53 debuginfod_client *debuginfod_begin (void) { errno = ENOSYS; return NULL; }
debuginfod_find_debuginfo(debuginfod_client * c,const unsigned char * b,int s,char ** p)54 int debuginfod_find_debuginfo (debuginfod_client *c, const unsigned char *b,
55                                int s, char **p) { return -ENOSYS; }
debuginfod_find_executable(debuginfod_client * c,const unsigned char * b,int s,char ** p)56 int debuginfod_find_executable (debuginfod_client *c, const unsigned char *b,
57                                 int s, char **p) { return -ENOSYS; }
debuginfod_find_source(debuginfod_client * c,const unsigned char * b,int s,const char * f,char ** p)58 int debuginfod_find_source (debuginfod_client *c, const unsigned char *b,
59                             int s, const char *f, char **p)  { return -ENOSYS; }
debuginfod_find_section(debuginfod_client * c,const unsigned char * b,int s,const char * scn,char ** p)60 int debuginfod_find_section (debuginfod_client *c, const unsigned char *b,
61 			     int s, const char *scn, char **p)
62 			      { return -ENOSYS; }
debuginfod_set_progressfn(debuginfod_client * c,debuginfod_progressfn_t fn)63 void debuginfod_set_progressfn(debuginfod_client *c,
64 			       debuginfod_progressfn_t fn) { }
debuginfod_set_verbose_fd(debuginfod_client * c,int fd)65 void debuginfod_set_verbose_fd(debuginfod_client *c, int fd) { }
debuginfod_set_user_data(debuginfod_client * c,void * d)66 void debuginfod_set_user_data (debuginfod_client *c, void *d) { }
debuginfod_get_user_data(debuginfod_client * c)67 void* debuginfod_get_user_data (debuginfod_client *c) { return NULL; }
debuginfod_get_url(debuginfod_client * c)68 const char* debuginfod_get_url (debuginfod_client *c) { return NULL; }
debuginfod_add_http_header(debuginfod_client * c,const char * h)69 int debuginfod_add_http_header (debuginfod_client *c,
70 				const char *h) { return -ENOSYS; }
debuginfod_get_headers(debuginfod_client * c)71 const char* debuginfod_get_headers (debuginfod_client *c) { return NULL; }
72 
debuginfod_end(debuginfod_client * c)73 void debuginfod_end (debuginfod_client *c) { }
74 
75 #else /* DUMMY_LIBDEBUGINFOD */
76 
77 #include <assert.h>
78 #include <dirent.h>
79 #include <stdio.h>
80 #include <errno.h>
81 #include <unistd.h>
82 #include <fcntl.h>
83 #include <fts.h>
84 #include <regex.h>
85 #include <string.h>
86 #include <stdbool.h>
87 #include <linux/limits.h>
88 #include <time.h>
89 #include <utime.h>
90 #include <sys/syscall.h>
91 #include <sys/types.h>
92 #include <sys/stat.h>
93 #include <sys/utsname.h>
94 #include <curl/curl.h>
95 
96 /* If fts.h is included before config.h, its indirect inclusions may not
97    give us the right LFS aliases of these functions, so map them manually.  */
98 #ifdef BAD_FTS
99   #ifdef _FILE_OFFSET_BITS
100     #define open open64
101     #define fopen fopen64
102   #endif
103 #else
104   #include <sys/types.h>
105   #include <fts.h>
106 #endif
107 
108 /* Older curl.h don't define CURL_AT_LEAST_VERSION.  */
109 #ifndef CURL_AT_LEAST_VERSION
110   #define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z))
111   #define CURL_AT_LEAST_VERSION(x,y,z) \
112     (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
113 #endif
114 
115 #include <pthread.h>
116 
117 static pthread_once_t init_control = PTHREAD_ONCE_INIT;
118 
119 static void
libcurl_init(void)120 libcurl_init(void)
121 {
122   curl_global_init(CURL_GLOBAL_DEFAULT);
123 }
124 
125 struct debuginfod_client
126 {
127   /* Progress/interrupt callback function. */
128   debuginfod_progressfn_t progressfn;
129 
130   /* Stores user data. */
131   void* user_data;
132 
133   /* Stores current/last url, if any. */
134   char* url;
135 
136   /* Accumulates outgoing http header names/values. */
137   int user_agent_set_p; /* affects add_default_headers */
138   struct curl_slist *headers;
139 
140   /* Flags the default_progressfn having printed something that
141      debuginfod_end needs to terminate. */
142   int default_progressfn_printed_p;
143 
144   /* Indicates whether the last query was cancelled by progressfn.  */
145   bool progressfn_cancel;
146 
147   /* File descriptor to output any verbose messages if > 0.  */
148   int verbose_fd;
149 
150   /* Maintain a long-lived curl multi-handle, which keeps a
151      connection/tls/dns cache to recently seen servers. */
152   CURLM *server_mhandle;
153 
154   /* Can contain all other context, like cache_path, server_urls,
155      timeout or other info gotten from environment variables, the
156      handle data, etc. So those don't have to be reparsed and
157      recreated on each request.  */
158   char * winning_headers;
159 };
160 
161 /* The cache_clean_interval_s file within the debuginfod cache specifies
162    how frequently the cache should be cleaned. The file's st_mtime represents
163    the time of last cleaning.  */
164 static const char *cache_clean_interval_filename = "cache_clean_interval_s";
165 static const long cache_clean_default_interval_s = 86400; /* 1 day */
166 
167 /* The cache_miss_default_s within the debuginfod cache specifies how
168    frequently the empty file should be released.*/
169 static const long cache_miss_default_s = 600; /* 10 min */
170 static const char *cache_miss_filename = "cache_miss_s";
171 
172 /* The cache_max_unused_age_s file within the debuginfod cache specifies the
173    the maximum time since last access that a file will remain in the cache.  */
174 static const char *cache_max_unused_age_filename = "max_unused_age_s";
175 static const long cache_default_max_unused_age_s = 604800; /* 1 week */
176 
177 /* Location of the cache of files downloaded from debuginfods.
178    The default parent directory is $HOME, or '/' if $HOME doesn't exist.  */
179 static const char *cache_default_name = ".debuginfod_client_cache";
180 static const char *cache_xdg_name = "debuginfod_client";
181 
182 /* URLs of debuginfods, separated by url_delim. */
183 static const char *url_delim =  " ";
184 
185 /* Timeout for debuginfods, in seconds (to get at least 100K). */
186 static const long default_timeout = 90;
187 
188 /* Default retry count for download error. */
189 static const long default_retry_limit = 2;
190 
191 /* Data associated with a particular CURL easy handle. Passed to
192    the write callback.  */
193 struct handle_data
194 {
195   /* Cache file to be written to in case query is successful.  */
196   int fd;
197 
198   /* URL queried by this handle.  */
199   char url[PATH_MAX];
200 
201   /* error buffer for this handle.  */
202   char errbuf[CURL_ERROR_SIZE];
203 
204   /* This handle.  */
205   CURL *handle;
206 
207   /* The client object whom we're serving. */
208   debuginfod_client *client;
209 
210   /* Pointer to handle that should write to fd. Initially points to NULL,
211      then points to the first handle that begins writing the target file
212      to the cache. Used to ensure that a file is not downloaded from
213      multiple servers unnecessarily.  */
214   CURL **target_handle;
215   /* Response http headers for this client handle, sent from the server */
216   char *response_data;
217   size_t response_data_size;
218 };
219 
220 static size_t
debuginfod_write_callback(char * ptr,size_t size,size_t nmemb,void * data)221 debuginfod_write_callback (char *ptr, size_t size, size_t nmemb, void *data)
222 {
223   ssize_t count = size * nmemb;
224 
225   struct handle_data *d = (struct handle_data*)data;
226 
227   /* Indicate to other handles that they can abort their transfer.  */
228   if (*d->target_handle == NULL)
229     {
230       *d->target_handle = d->handle;
231       /* update the client object */
232       const char *url = NULL;
233       CURLcode curl_res = curl_easy_getinfo (d->handle,
234                                              CURLINFO_EFFECTIVE_URL, &url);
235       if (curl_res == CURLE_OK && url)
236         {
237           free (d->client->url);
238           d->client->url = strdup(url); /* ok if fails */
239         }
240     }
241 
242   /* If this handle isn't the target handle, abort transfer.  */
243   if (*d->target_handle != d->handle)
244     return -1;
245 
246   return (size_t) write(d->fd, (void*)ptr, count);
247 }
248 
249 /* handle config file read and write */
250 static int
debuginfod_config_cache(debuginfod_client * c,char * config_path,long cache_config_default_s,struct stat * st)251 debuginfod_config_cache(debuginfod_client *c, char *config_path,
252 			long cache_config_default_s,
253 			struct stat *st)
254 {
255   int fd = open(config_path, O_CREAT | O_RDWR, DEFFILEMODE);
256   if (fd < 0)
257     return -errno;
258 
259   if (fstat (fd, st) < 0)
260     {
261       int ret = -errno;
262       close (fd);
263       return ret;
264     }
265 
266   if (st->st_size == 0)
267     {
268       if (dprintf(fd, "%ld", cache_config_default_s) < 0)
269 	{
270 	  int ret = -errno;
271 	  close (fd);
272 	  return ret;
273 	}
274 
275       close (fd);
276       return cache_config_default_s;
277     }
278 
279   long cache_config;
280   /* PR29696 - NB: When using fdopen, the file descriptor is NOT
281      dup'ed and will be closed when the stream is closed. Manually
282      closing fd after fclose is called will lead to a race condition
283      where, if reused, the file descriptor will compete for its
284      regular use before being incorrectly closed here.  */
285   FILE *config_file = fdopen(fd, "r");
286   if (config_file)
287     {
288       if (fscanf(config_file, "%ld", &cache_config) != 1)
289 	cache_config = cache_config_default_s;
290       if (0 != fclose (config_file) && c->verbose_fd >= 0)
291 	dprintf (c->verbose_fd, "fclose failed with %s (err=%d)\n",
292 		 strerror (errno), errno);
293     }
294   else
295     {
296       cache_config = cache_config_default_s;
297       if (0 != close (fd) && c->verbose_fd >= 0)
298 	dprintf (c->verbose_fd, "close failed with %s (err=%d)\n",
299 		 strerror (errno), errno);
300     }
301   return cache_config;
302 }
303 
304 /* Delete any files that have been unmodied for a period
305    longer than $DEBUGINFOD_CACHE_CLEAN_INTERVAL_S.  */
306 static int
debuginfod_clean_cache(debuginfod_client * c,char * cache_path,char * interval_path,char * max_unused_path)307 debuginfod_clean_cache(debuginfod_client *c,
308 		       char *cache_path, char *interval_path,
309 		       char *max_unused_path)
310 {
311   time_t clean_interval, max_unused_age;
312   int rc = -1;
313   struct stat st;
314 
315   /* Create new interval file.  */
316   rc = debuginfod_config_cache(c, interval_path,
317 			       cache_clean_default_interval_s, &st);
318   if (rc < 0)
319     return rc;
320   clean_interval = (time_t)rc;
321 
322   /* Check timestamp of interval file to see whether cleaning is necessary.  */
323   if (time(NULL) - st.st_mtime < clean_interval)
324     /* Interval has not passed, skip cleaning.  */
325     return 0;
326 
327   /* Update timestamp representing when the cache was last cleaned.
328      Do it at the start to reduce the number of threads trying to do a
329      cleanup simultaneously.  */
330   utime (interval_path, NULL);
331 
332   /* Read max unused age value from config file.  */
333   rc = debuginfod_config_cache(c, max_unused_path,
334 			       cache_default_max_unused_age_s, &st);
335   if (rc < 0)
336     return rc;
337   max_unused_age = (time_t)rc;
338 
339   char * const dirs[] = { cache_path, NULL, };
340 
341   FTS *fts = fts_open(dirs, 0, NULL);
342   if (fts == NULL)
343     return -errno;
344 
345   regex_t re;
346   const char * pattern = ".*/[a-f0-9]+(/debuginfo|/executable|/source.*|)$"; /* include dirs */
347   if (regcomp (&re, pattern, REG_EXTENDED | REG_NOSUB) != 0)
348     return -ENOMEM;
349 
350   FTSENT *f;
351   long files = 0;
352   time_t now = time(NULL);
353   while ((f = fts_read(fts)) != NULL)
354     {
355       /* ignore any files that do not match the pattern.  */
356       if (regexec (&re, f->fts_path, 0, NULL, 0) != 0)
357         continue;
358 
359       files++;
360       if (c->progressfn) /* inform/check progress callback */
361         if ((c->progressfn) (c, files, 0))
362           break;
363 
364       switch (f->fts_info)
365         {
366         case FTS_F:
367           /* delete file if max_unused_age has been met or exceeded w.r.t. atime.  */
368           if (now - f->fts_statp->st_atime >= max_unused_age)
369             (void) unlink (f->fts_path);
370           break;
371 
372         case FTS_DP:
373           /* Remove if old & empty.  Weaken race against concurrent creation by
374              checking mtime. */
375           if (now - f->fts_statp->st_mtime >= max_unused_age)
376             (void) rmdir (f->fts_path);
377           break;
378 
379         default:
380           ;
381         }
382     }
383   fts_close (fts);
384   regfree (&re);
385 
386   return 0;
387 }
388 
389 
390 #define MAX_BUILD_ID_BYTES 64
391 
392 
393 static void
add_default_headers(debuginfod_client * client)394 add_default_headers(debuginfod_client *client)
395 {
396   if (client->user_agent_set_p)
397     return;
398 
399   /* Compute a User-Agent: string to send.  The more accurately this
400      describes this host, the likelier that the debuginfod servers
401      might be able to locate debuginfo for us. */
402 
403   char* utspart = NULL;
404   struct utsname uts;
405   int rc = 0;
406   rc = uname (&uts);
407   if (rc == 0)
408     rc = asprintf(& utspart, "%s/%s", uts.sysname, uts.machine);
409   if (rc < 0)
410     utspart = NULL;
411 
412   FILE *f = fopen ("/etc/os-release", "r");
413   if (f == NULL)
414     f = fopen ("/usr/lib/os-release", "r");
415   char *id = NULL;
416   char *version = NULL;
417   if (f != NULL)
418     {
419       while (id == NULL || version == NULL)
420         {
421           char buf[128];
422           char *s = &buf[0];
423           if (fgets (s, sizeof(buf), f) == NULL)
424             break;
425 
426           int len = strlen (s);
427           if (len < 3)
428             continue;
429           if (s[len - 1] == '\n')
430             {
431               s[len - 1] = '\0';
432               len--;
433             }
434 
435           char *v = strchr (s, '=');
436           if (v == NULL || strlen (v) < 2)
437             continue;
438 
439           /* Split var and value. */
440           *v = '\0';
441           v++;
442 
443           /* Remove optional quotes around value string. */
444           if (*v == '"' || *v == '\'')
445             {
446               v++;
447               s[len - 1] = '\0';
448             }
449           if (strcmp (s, "ID") == 0)
450             id = strdup (v);
451           if (strcmp (s, "VERSION_ID") == 0)
452             version = strdup (v);
453         }
454       fclose (f);
455     }
456 
457   char *ua = NULL;
458   rc = asprintf(& ua, "User-Agent: %s/%s,%s,%s/%s",
459                 PACKAGE_NAME, PACKAGE_VERSION,
460                 utspart ?: "",
461                 id ?: "",
462                 version ?: "");
463   if (rc < 0)
464     ua = NULL;
465 
466   if (ua)
467     (void) debuginfod_add_http_header (client, ua);
468 
469   free (ua);
470   free (id);
471   free (version);
472   free (utspart);
473 }
474 
475 /* Add HTTP headers found in the given file, one per line. Blank lines or invalid
476  * headers are ignored.
477  */
478 static void
add_headers_from_file(debuginfod_client * client,const char * filename)479 add_headers_from_file(debuginfod_client *client, const char* filename)
480 {
481   int vds = client->verbose_fd;
482   FILE *f = fopen (filename, "r");
483   if (f == NULL)
484     {
485       if (vds >= 0)
486 	dprintf(vds, "header file %s: %s\n", filename, strerror(errno));
487       return;
488     }
489 
490   while (1)
491     {
492       char buf[8192];
493       char *s = &buf[0];
494       if (feof(f))
495         break;
496       if (fgets (s, sizeof(buf), f) == NULL)
497         break;
498       for (char *c = s; *c != '\0'; ++c)
499         if (!isspace(*c))
500           goto nonempty;
501       continue;
502     nonempty:
503       ;
504       size_t last = strlen(s)-1;
505       if (s[last] == '\n')
506         s[last] = '\0';
507       int rc = debuginfod_add_http_header(client, s);
508       if (rc < 0 && vds >= 0)
509         dprintf(vds, "skipping bad header: %s\n", strerror(-rc));
510     }
511   fclose (f);
512 }
513 
514 
515 #define xalloc_str(p, fmt, args...)        \
516   do                                       \
517     {                                      \
518       if (asprintf (&p, fmt, args) < 0)    \
519         {                                  \
520           p = NULL;                        \
521           rc = -ENOMEM;                    \
522           goto out;                        \
523         }                                  \
524     } while (0)
525 
526 
527 /* Offer a basic form of progress tracing */
528 static int
default_progressfn(debuginfod_client * c,long a,long b)529 default_progressfn (debuginfod_client *c, long a, long b)
530 {
531   const char* url = debuginfod_get_url (c);
532   int len = 0;
533 
534   /* We prefer to print the host part of the URL to keep the
535      message short. */
536   if (url != NULL)
537     {
538       const char* buildid = strstr(url, "buildid/");
539       if (buildid != NULL)
540         len = (buildid - url);
541       else
542         len = strlen(url);
543     }
544 
545   if (b == 0 || url==NULL) /* early stage */
546     dprintf(STDERR_FILENO,
547             "\rDownloading %c", "-/|\\"[a % 4]);
548   else if (b < 0) /* download in progress but unknown total length */
549     dprintf(STDERR_FILENO,
550             "\rDownloading from %.*s %ld",
551             len, url, a);
552   else /* download in progress, and known total length */
553     dprintf(STDERR_FILENO,
554             "\rDownloading from %.*s %ld/%ld",
555             len, url, a, b);
556   c->default_progressfn_printed_p = 1;
557 
558   return 0;
559 }
560 
561 /* This is a callback function that receives http response headers in buffer for use
562  * in this program. https://curl.se/libcurl/c/CURLOPT_HEADERFUNCTION.html is the
563  * online documentation.
564  */
565 static size_t
header_callback(char * buffer,size_t size,size_t numitems,void * userdata)566 header_callback (char * buffer, size_t size, size_t numitems, void * userdata)
567 {
568   struct handle_data *data = (struct handle_data *) userdata;
569   if (size != 1)
570     return 0;
571   if (data->client
572       && data->client->verbose_fd >= 0
573       && numitems > 2)
574     dprintf (data->client->verbose_fd, "header %.*s", (int)numitems, buffer);
575   // Some basic checks to ensure the headers received are of the expected format
576   if (strncasecmp(buffer, "X-DEBUGINFOD", 11)
577       || buffer[numitems-2] != '\r'
578       || buffer[numitems-1] != '\n'
579       || (buffer == strstr(buffer, ":")) ){
580     return numitems;
581   }
582   /* Temporary buffer for realloc */
583   char *temp = NULL;
584   if (data->response_data == NULL)
585     {
586       temp = malloc(numitems);
587       if (temp == NULL)
588         return 0;
589     }
590   else
591     {
592       temp = realloc(data->response_data, data->response_data_size + numitems);
593       if (temp == NULL)
594         return 0;
595     }
596 
597   memcpy(temp + data->response_data_size, buffer, numitems-1);
598   data->response_data = temp;
599   data->response_data_size += numitems-1;
600   data->response_data[data->response_data_size-1] = '\n';
601   data->response_data[data->response_data_size] = '\0';
602   return numitems;
603 }
604 
605 /* Copy SRC to DEST, s,/,#,g */
606 
607 static void
path_escape(const char * src,char * dest)608 path_escape (const char *src, char *dest)
609 {
610   unsigned q = 0;
611 
612   for (unsigned fi=0; q < PATH_MAX-2; fi++) /* -2, escape is 2 chars.  */
613     switch (src[fi])
614       {
615       case '\0':
616         dest[q] = '\0';
617 	return;
618       case '/': /* escape / to prevent dir escape */
619         dest[q++]='#';
620         dest[q++]='#';
621         break;
622       case '#': /* escape # to prevent /# vs #/ collisions */
623         dest[q++]='#';
624         dest[q++]='_';
625         break;
626       default:
627         dest[q++]=src[fi];
628       }
629 
630   dest[q] = '\0';
631 }
632 
633 /* Attempt to update the atime */
634 static void
update_atime(int fd)635 update_atime (int fd)
636 {
637   struct timespec tvs[2];
638 
639   tvs[0].tv_sec = tvs[1].tv_sec = 0;
640   tvs[0].tv_nsec = UTIME_NOW;
641   tvs[1].tv_nsec = UTIME_OMIT;
642 
643   (void) futimens (fd, tvs);  /* best effort */
644 }
645 
646 /* Attempt to read an ELF/DWARF section with name SECTION from FD and write
647    it to a separate file in the debuginfod cache.  If successful the absolute
648    path of the separate file containing SECTION will be stored in USR_PATH.
649    FD_PATH is the absolute path for FD.
650 
651    If the section cannot be extracted, then return a negative error code.
652    -ENOENT indicates that the parent file was able to be read but the
653    section name was not found.  -EEXIST indicates that the section was
654    found but had type SHT_NOBITS.  */
655 
656 static int
extract_section(int fd,const char * section,char * fd_path,char ** usr_path)657 extract_section (int fd, const char *section, char *fd_path, char **usr_path)
658 {
659   elf_version (EV_CURRENT);
660 
661   Elf *elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
662   if (elf == NULL)
663     return -EIO;
664 
665   size_t shstrndx;
666   int rc = elf_getshdrstrndx (elf, &shstrndx);
667   if (rc < 0)
668     {
669       rc = -EIO;
670       goto out;
671     }
672 
673   int sec_fd = -1;
674   char *escaped_name = NULL;
675   char *sec_path_tmp = NULL;
676   Elf_Scn *scn = NULL;
677 
678   /* Try to find the target section and copy the contents into a
679      separate file.  */
680   while (true)
681     {
682       scn = elf_nextscn (elf, scn);
683       if (scn == NULL)
684 	{
685 	  rc = -ENOENT;
686 	  goto out;
687 	}
688       GElf_Shdr shdr_storage;
689       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_storage);
690       if (shdr == NULL)
691 	{
692 	  rc = -EIO;
693 	  goto out;
694 	}
695 
696       const char *scn_name = elf_strptr (elf, shstrndx, shdr->sh_name);
697       if (scn_name == NULL)
698 	{
699 	  rc = -EIO;
700 	  goto out;
701 	}
702       if (strcmp (scn_name, section) == 0)
703 	{
704 	  /* We found the desired section.  */
705 	  if (shdr->sh_type == SHT_NOBITS)
706 	    {
707 	      rc = -EEXIST;
708 	      goto out;
709 	    }
710 
711 	  Elf_Data *data = NULL;
712 	  data = elf_rawdata (scn, NULL);
713 	  if (data == NULL)
714 	    {
715 	      rc = -EIO;
716 	      goto out;
717 	    }
718 
719 	  if (data->d_buf == NULL)
720 	    {
721 	      rc = -EIO;
722 	      goto out;
723 	    }
724 
725 	  /* Compute the absolute filename we'll write the section to.
726 	     Replace the last component of FD_PATH with the path-escaped
727 	     section filename.  */
728 	  int i = strlen (fd_path);
729           while (i >= 0)
730 	    {
731 	      if (fd_path[i] == '/')
732 		{
733 		  fd_path[i] = '\0';
734 		  break;
735 		}
736 	      --i;
737 	    }
738 
739 	  escaped_name = malloc (strlen (section) * 2 + 1);
740 	  if (escaped_name == NULL)
741 	    {
742 	      rc = -ENOMEM;
743 	      goto out;
744 	    }
745 	  path_escape (section, escaped_name);
746 
747 	  rc = asprintf (&sec_path_tmp, "%s/section-%s.XXXXXX",
748 			 fd_path, escaped_name);
749 	  if (rc == -1)
750 	    {
751 	      rc = -ENOMEM;
752 	      goto out1;
753 	    }
754 
755 	  sec_fd = mkstemp (sec_path_tmp);
756 	  if (sec_fd < 0)
757 	    {
758 	      rc = -EIO;
759 	      goto out2;
760 	    }
761 
762 	  ssize_t res = write_retry (sec_fd, data->d_buf, data->d_size);
763 	  if (res < 0 || (size_t) res != data->d_size)
764 	    {
765 	      rc = -EIO;
766 	      goto out3;
767 	    }
768 
769 	  /* Success.  Rename tmp file and update USR_PATH.  */
770 	  char *sec_path;
771 	  if (asprintf (&sec_path, "%s/section-%s", fd_path, section) == -1)
772 	    {
773 	      rc = -ENOMEM;
774 	      goto out3;
775 	    }
776 
777 	  rc = rename (sec_path_tmp, sec_path);
778 	  if (rc < 0)
779 	    {
780 	      free (sec_path);
781 	      rc = -EIO;
782 	      goto out3;
783 	    }
784 
785 	  if (usr_path != NULL)
786 	    *usr_path = sec_path;
787 	  else
788 	    free (sec_path);
789 	  update_atime(fd);
790 	  rc = sec_fd;
791 	  goto out2;
792 	}
793     }
794 
795 out3:
796   close (sec_fd);
797   unlink (sec_path_tmp);
798 
799 out2:
800   free (sec_path_tmp);
801 
802 out1:
803   free (escaped_name);
804 
805 out:
806   elf_end (elf);
807   return rc;
808 }
809 
810 /* Search TARGET_CACHE_DIR for a debuginfo or executable file containing
811    an ELF/DWARF section with name SCN_NAME.  If found, extract the section
812    to a separate file in TARGET_CACHE_DIR and return a file descriptor
813    for the section file. The path for this file will be stored in USR_PATH.
814    Return a negative errno if unsuccessful.  -ENOENT indicates that SCN_NAME
815    is confirmed to not exist.  */
816 
817 static int
cache_find_section(const char * scn_name,const char * target_cache_dir,char ** usr_path)818 cache_find_section (const char *scn_name, const char *target_cache_dir,
819 		    char **usr_path)
820 {
821   int debug_fd;
822   int rc = -EEXIST;
823   char parent_path[PATH_MAX];
824 
825   /* Check the debuginfo first.  */
826   snprintf (parent_path, PATH_MAX, "%s/debuginfo", target_cache_dir);
827   debug_fd = open (parent_path, O_RDONLY);
828   if (debug_fd >= 0)
829     {
830       rc = extract_section (debug_fd, scn_name, parent_path, usr_path);
831       close (debug_fd);
832     }
833 
834   /* If the debuginfo file couldn't be found or the section type was
835      SHT_NOBITS, check the executable.  */
836   if (rc == -EEXIST)
837     {
838       snprintf (parent_path, PATH_MAX, "%s/executable", target_cache_dir);
839       int exec_fd = open (parent_path, O_RDONLY);
840 
841       if (exec_fd >= 0)
842 	{
843 	  rc = extract_section (exec_fd, scn_name, parent_path, usr_path);
844 	  close (exec_fd);
845 
846 	  /* Don't return -ENOENT if the debuginfo wasn't opened.  The
847 	     section may exist in the debuginfo but not the executable.  */
848 	  if (debug_fd < 0 && rc == -ENOENT)
849 	    rc = -EREMOTE;
850 	}
851     }
852 
853   return rc;
854 }
855 
856 /* Query each of the server URLs found in $DEBUGINFOD_URLS for the file
857    with the specified build-id and type (debuginfo, executable, source or
858    section).  If type is source, then type_arg should be a filename.  If
859    type is section, then type_arg should be the name of an ELF/DWARF
860    section.  Otherwise type_arg may be NULL.  Return a file descriptor
861    for the target if successful, otherwise return an error code.
862 */
863 static int
debuginfod_query_server(debuginfod_client * c,const unsigned char * build_id,int build_id_len,const char * type,const char * type_arg,char ** path)864 debuginfod_query_server (debuginfod_client *c,
865 			 const unsigned char *build_id,
866                          int build_id_len,
867                          const char *type,
868                          const char *type_arg,
869                          char **path)
870 {
871   char *server_urls;
872   char *urls_envvar;
873   const char *section = NULL;
874   const char *filename = NULL;
875   char *cache_path = NULL;
876   char *maxage_path = NULL;
877   char *interval_path = NULL;
878   char *cache_miss_path = NULL;
879   char *target_cache_dir = NULL;
880   char *target_cache_path = NULL;
881   char *target_cache_tmppath = NULL;
882   char suffix[PATH_MAX + 1]; /* +1 for zero terminator.  */
883   char build_id_bytes[MAX_BUILD_ID_BYTES * 2 + 1];
884   int vfd = c->verbose_fd;
885   int rc;
886 
887   c->progressfn_cancel = false;
888 
889   if (strcmp (type, "source") == 0)
890     filename = type_arg;
891   else if (strcmp (type, "section") == 0)
892     {
893       section = type_arg;
894       if (section == NULL)
895 	return -EINVAL;
896     }
897 
898   if (vfd >= 0)
899     {
900       dprintf (vfd, "debuginfod_find_%s ", type);
901       if (build_id_len == 0) /* expect clean hexadecimal */
902 	dprintf (vfd, "%s", (const char *) build_id);
903       else
904 	for (int i = 0; i < build_id_len; i++)
905 	  dprintf (vfd, "%02x", build_id[i]);
906       if (filename != NULL)
907 	dprintf (vfd, " %s\n", filename);
908       dprintf (vfd, "\n");
909     }
910 
911   /* Is there any server we can query?  If not, don't do any work,
912      just return with ENOSYS.  Don't even access the cache.  */
913   urls_envvar = getenv(DEBUGINFOD_URLS_ENV_VAR);
914   if (vfd >= 0)
915     dprintf (vfd, "server urls \"%s\"\n",
916 	     urls_envvar != NULL ? urls_envvar : "");
917   if (urls_envvar == NULL || urls_envvar[0] == '\0')
918     {
919       rc = -ENOSYS;
920       goto out;
921     }
922 
923   /* Clear the obsolete data from a previous _find operation. */
924   free (c->url);
925   c->url = NULL;
926   free (c->winning_headers);
927   c->winning_headers = NULL;
928 
929   /* PR 27982: Add max size if DEBUGINFOD_MAXSIZE is set. */
930   long maxsize = 0;
931   const char *maxsize_envvar;
932   maxsize_envvar = getenv(DEBUGINFOD_MAXSIZE_ENV_VAR);
933   if (maxsize_envvar != NULL)
934     maxsize = atol (maxsize_envvar);
935 
936   /* PR 27982: Add max time if DEBUGINFOD_MAXTIME is set. */
937   long maxtime = 0;
938   const char *maxtime_envvar;
939   maxtime_envvar = getenv(DEBUGINFOD_MAXTIME_ENV_VAR);
940   if (maxtime_envvar != NULL)
941     maxtime = atol (maxtime_envvar);
942   if (maxtime && vfd >= 0)
943     dprintf(vfd, "using max time %lds\n", maxtime);
944 
945   const char *headers_file_envvar;
946   headers_file_envvar = getenv(DEBUGINFOD_HEADERS_FILE_ENV_VAR);
947   if (headers_file_envvar != NULL)
948     add_headers_from_file(c, headers_file_envvar);
949 
950   /* Maxsize is valid*/
951   if (maxsize > 0)
952     {
953       if (vfd)
954         dprintf (vfd, "using max size %ldB\n", maxsize);
955       char *size_header = NULL;
956       rc = asprintf (&size_header, "X-DEBUGINFOD-MAXSIZE: %ld", maxsize);
957       if (rc < 0)
958         {
959           rc = -ENOMEM;
960           goto out;
961         }
962       rc = debuginfod_add_http_header(c, size_header);
963       free(size_header);
964       if (rc < 0)
965         goto out;
966     }
967   add_default_headers(c);
968 
969   /* Copy lowercase hex representation of build_id into buf.  */
970   if (vfd >= 0)
971     dprintf (vfd, "checking build-id\n");
972   if ((build_id_len >= MAX_BUILD_ID_BYTES) ||
973       (build_id_len == 0 &&
974        strlen ((const char *) build_id) > MAX_BUILD_ID_BYTES*2))
975     {
976       rc = -EINVAL;
977       goto out;
978     }
979 
980   if (build_id_len == 0) /* expect clean hexadecimal */
981     strcpy (build_id_bytes, (const char *) build_id);
982   else
983     for (int i = 0; i < build_id_len; i++)
984       sprintf(build_id_bytes + (i * 2), "%02x", build_id[i]);
985 
986   if (filename != NULL)
987     {
988       if (vfd >= 0)
989 	dprintf (vfd, "checking filename\n");
990       if (filename[0] != '/') // must start with /
991 	{
992 	  rc = -EINVAL;
993 	  goto out;
994 	}
995 
996       path_escape (filename, suffix);
997       /* If the DWARF filenames are super long, this could exceed
998          PATH_MAX and truncate/collide.  Oh well, that'll teach
999          them! */
1000     }
1001   else if (section != NULL)
1002     path_escape (section, suffix);
1003   else
1004     suffix[0] = '\0';
1005 
1006   if (suffix[0] != '\0' && vfd >= 0)
1007     dprintf (vfd, "suffix %s\n", suffix);
1008 
1009   /* set paths needed to perform the query
1010 
1011      example format
1012      cache_path:        $HOME/.cache
1013      target_cache_dir:  $HOME/.cache/0123abcd
1014      target_cache_path: $HOME/.cache/0123abcd/debuginfo
1015      target_cache_path: $HOME/.cache/0123abcd/source#PATH#TO#SOURCE ?
1016 
1017      $XDG_CACHE_HOME takes priority over $HOME/.cache.
1018      $DEBUGINFOD_CACHE_PATH takes priority over $HOME/.cache and $XDG_CACHE_HOME.
1019   */
1020 
1021   /* Determine location of the cache. The path specified by the debuginfod
1022      cache environment variable takes priority.  */
1023   char *cache_var = getenv(DEBUGINFOD_CACHE_PATH_ENV_VAR);
1024   if (cache_var != NULL && strlen (cache_var) > 0)
1025     xalloc_str (cache_path, "%s", cache_var);
1026   else
1027     {
1028       /* If a cache already exists in $HOME ('/' if $HOME isn't set), then use
1029          that. Otherwise use the XDG cache directory naming format.  */
1030       xalloc_str (cache_path, "%s/%s", getenv ("HOME") ?: "/", cache_default_name);
1031 
1032       struct stat st;
1033       if (stat (cache_path, &st) < 0)
1034         {
1035           char cachedir[PATH_MAX];
1036           char *xdg = getenv ("XDG_CACHE_HOME");
1037 
1038           if (xdg != NULL && strlen (xdg) > 0)
1039             snprintf (cachedir, PATH_MAX, "%s", xdg);
1040           else
1041             snprintf (cachedir, PATH_MAX, "%s/.cache", getenv ("HOME") ?: "/");
1042 
1043           /* Create XDG cache directory if it doesn't exist.  */
1044           if (stat (cachedir, &st) == 0)
1045             {
1046               if (! S_ISDIR (st.st_mode))
1047                 {
1048                   rc = -EEXIST;
1049                   goto out;
1050                 }
1051             }
1052           else
1053             {
1054               rc = mkdir (cachedir, 0700);
1055 
1056               /* Also check for EEXIST and S_ISDIR in case another client just
1057                  happened to create the cache.  */
1058               if (rc < 0
1059                   && (errno != EEXIST
1060                       || stat (cachedir, &st) != 0
1061                       || ! S_ISDIR (st.st_mode)))
1062                 {
1063                   rc = -errno;
1064                   goto out;
1065                 }
1066             }
1067 
1068           free (cache_path);
1069           xalloc_str (cache_path, "%s/%s", cachedir, cache_xdg_name);
1070         }
1071     }
1072 
1073   xalloc_str (target_cache_dir, "%s/%s", cache_path, build_id_bytes);
1074   if (section != NULL)
1075     xalloc_str (target_cache_path, "%s/%s-%s", target_cache_dir, type, suffix);
1076   else
1077     xalloc_str (target_cache_path, "%s/%s%s", target_cache_dir, type, suffix);
1078   xalloc_str (target_cache_tmppath, "%s.XXXXXX", target_cache_path);
1079 
1080   /* XXX combine these */
1081   xalloc_str (interval_path, "%s/%s", cache_path, cache_clean_interval_filename);
1082   xalloc_str (cache_miss_path, "%s/%s", cache_path, cache_miss_filename);
1083   xalloc_str (maxage_path, "%s/%s", cache_path, cache_max_unused_age_filename);
1084 
1085   if (vfd >= 0)
1086     dprintf (vfd, "checking cache dir %s\n", cache_path);
1087 
1088   /* Make sure cache dir exists. debuginfo_clean_cache will then make
1089      sure the interval, cache_miss and maxage files exist.  */
1090   if (mkdir (cache_path, ACCESSPERMS) != 0
1091       && errno != EEXIST)
1092     {
1093       rc = -errno;
1094       goto out;
1095     }
1096 
1097   rc = debuginfod_clean_cache(c, cache_path, interval_path, maxage_path);
1098   if (rc != 0)
1099     goto out;
1100 
1101   /* Check if the target is already in the cache. */
1102   int fd = open(target_cache_path, O_RDONLY);
1103   if (fd >= 0)
1104     {
1105       struct stat st;
1106       if (fstat(fd, &st) != 0)
1107         {
1108           rc = -errno;
1109           close (fd);
1110           goto out;
1111         }
1112 
1113       /* If the file is non-empty, then we are done. */
1114       if (st.st_size > 0)
1115         {
1116           if (path != NULL)
1117             {
1118               *path = strdup(target_cache_path);
1119               if (*path == NULL)
1120                 {
1121                   rc = -errno;
1122                   close (fd);
1123                   goto out;
1124                 }
1125             }
1126           /* Success!!!! */
1127           update_atime(fd);
1128           rc = fd;
1129           goto out;
1130         }
1131       else
1132         {
1133           /* The file is empty. Attempt to download only if enough time
1134              has passed since the last attempt. */
1135           time_t cache_miss;
1136           time_t target_mtime = st.st_mtime;
1137 
1138           close(fd); /* no need to hold onto the negative-hit file descriptor */
1139 
1140           rc = debuginfod_config_cache(c, cache_miss_path,
1141                                        cache_miss_default_s, &st);
1142           if (rc < 0)
1143             goto out;
1144 
1145           cache_miss = (time_t)rc;
1146           if (time(NULL) - target_mtime <= cache_miss)
1147             {
1148               rc = -ENOENT;
1149               goto out;
1150             }
1151           else
1152             /* TOCTOU non-problem: if another task races, puts a working
1153                download or an empty file in its place, unlinking here just
1154                means WE will try to download again as uncached. */
1155             unlink(target_cache_path);
1156         }
1157     }
1158   else if (errno == EACCES)
1159     /* Ensure old 000-permission files are not lingering in the cache. */
1160     unlink(target_cache_path);
1161 
1162   if (section != NULL)
1163     {
1164       /* Try to extract the section from a cached file before querying
1165 	 any servers.  */
1166       rc = cache_find_section (section, target_cache_dir, path);
1167 
1168       /* If the section was found or confirmed to not exist, then we
1169 	 are done.  */
1170       if (rc >= 0 || rc == -ENOENT)
1171 	goto out;
1172     }
1173 
1174   long timeout = default_timeout;
1175   const char* timeout_envvar = getenv(DEBUGINFOD_TIMEOUT_ENV_VAR);
1176   if (timeout_envvar != NULL)
1177     timeout = atoi (timeout_envvar);
1178 
1179   if (vfd >= 0)
1180     dprintf (vfd, "using timeout %ld\n", timeout);
1181 
1182   /* make a copy of the envvar so it can be safely modified.  */
1183   server_urls = strdup(urls_envvar);
1184   if (server_urls == NULL)
1185     {
1186       rc = -ENOMEM;
1187       goto out;
1188     }
1189   /* thereafter, goto out0 on error*/
1190 
1191   /* Because of a race with cache cleanup / rmdir, try to mkdir/mkstemp up to twice. */
1192   for(int i=0; i<2; i++) {
1193     /* (re)create target directory in cache */
1194     (void) mkdir(target_cache_dir, 0700); /* files will be 0400 later */
1195 
1196     /* NB: write to a temporary file first, to avoid race condition of
1197        multiple clients checking the cache, while a partially-written or empty
1198        file is in there, being written from libcurl. */
1199     fd = mkstemp (target_cache_tmppath);
1200     if (fd >= 0) break;
1201   }
1202   if (fd < 0) /* Still failed after two iterations. */
1203     {
1204       rc = -errno;
1205       goto out0;
1206     }
1207 
1208   /* Initialize the memory to zero */
1209   char *strtok_saveptr;
1210   char **server_url_list = NULL;
1211   char *server_url = strtok_r(server_urls, url_delim, &strtok_saveptr);
1212   /* Count number of URLs.  */
1213   int num_urls = 0;
1214 
1215   while (server_url != NULL)
1216     {
1217       /* PR 27983: If the url is already set to be used use, skip it */
1218       char *slashbuildid;
1219       if (strlen(server_url) > 1 && server_url[strlen(server_url)-1] == '/')
1220         slashbuildid = "buildid";
1221       else
1222         slashbuildid = "/buildid";
1223 
1224       char *tmp_url;
1225       if (asprintf(&tmp_url, "%s%s", server_url, slashbuildid) == -1)
1226         {
1227           rc = -ENOMEM;
1228           goto out1;
1229         }
1230       int url_index;
1231       for (url_index = 0; url_index < num_urls; ++url_index)
1232         {
1233           if(strcmp(tmp_url, server_url_list[url_index]) == 0)
1234             {
1235               url_index = -1;
1236               break;
1237             }
1238         }
1239       if (url_index == -1)
1240         {
1241           if (vfd >= 0)
1242             dprintf(vfd, "duplicate url: %s, skipping\n", tmp_url);
1243           free(tmp_url);
1244         }
1245       else
1246         {
1247           num_urls++;
1248           char ** realloc_ptr;
1249           realloc_ptr = reallocarray(server_url_list, num_urls,
1250                                          sizeof(char*));
1251           if (realloc_ptr == NULL)
1252             {
1253               free (tmp_url);
1254               rc = -ENOMEM;
1255               goto out1;
1256             }
1257           server_url_list = realloc_ptr;
1258           server_url_list[num_urls-1] = tmp_url;
1259         }
1260       server_url = strtok_r(NULL, url_delim, &strtok_saveptr);
1261     }
1262 
1263   int retry_limit = default_retry_limit;
1264   const char* retry_limit_envvar = getenv(DEBUGINFOD_RETRY_LIMIT_ENV_VAR);
1265   if (retry_limit_envvar != NULL)
1266     retry_limit = atoi (retry_limit_envvar);
1267 
1268   CURLM *curlm = c->server_mhandle;
1269   assert (curlm != NULL);
1270 
1271   /* Tracks which handle should write to fd. Set to the first
1272      handle that is ready to write the target file to the cache.  */
1273   CURL *target_handle = NULL;
1274   struct handle_data *data = malloc(sizeof(struct handle_data) * num_urls);
1275   if (data == NULL)
1276     {
1277       rc = -ENOMEM;
1278       goto out1;
1279     }
1280 
1281   /* thereafter, goto out2 on error.  */
1282 
1283  /*The beginning of goto block query_in_parallel.*/
1284  query_in_parallel:
1285   rc = -ENOENT; /* Reset rc to default.*/
1286 
1287   /* Initialize handle_data with default values. */
1288   for (int i = 0; i < num_urls; i++)
1289     {
1290       data[i].handle = NULL;
1291       data[i].fd = -1;
1292       data[i].errbuf[0] = '\0';
1293       data[i].response_data = NULL;
1294       data[i].response_data_size = 0;
1295     }
1296 
1297   char *escaped_string = NULL;
1298   size_t escaped_strlen = 0;
1299   if (filename)
1300     {
1301       escaped_string = curl_easy_escape(&target_handle, filename+1, 0);
1302       if (!escaped_string)
1303         {
1304           rc = -ENOMEM;
1305           goto out2;
1306         }
1307       char *loc = escaped_string;
1308       escaped_strlen = strlen(escaped_string);
1309       while ((loc = strstr(loc, "%2F")))
1310         {
1311           loc[0] = '/';
1312           //pull the string back after replacement
1313           // loc-escaped_string finds the distance from the origin to the new location
1314           // - 2 accounts for the 2F which remain and don't need to be measured.
1315           // The two above subtracted from escaped_strlen yields the remaining characters
1316           // in the string which we want to pull back
1317           memmove(loc+1, loc+3,escaped_strlen - (loc-escaped_string) - 2);
1318           //Because the 2F was overwritten in the memmove (as desired) escaped_strlen is
1319           // now two shorter.
1320           escaped_strlen -= 2;
1321         }
1322     }
1323   /* Initialize each handle.  */
1324   for (int i = 0; i < num_urls; i++)
1325     {
1326       if ((server_url = server_url_list[i]) == NULL)
1327         break;
1328       if (vfd >= 0)
1329 	dprintf (vfd, "init server %d %s\n", i, server_url);
1330 
1331       data[i].fd = fd;
1332       data[i].target_handle = &target_handle;
1333       data[i].handle = curl_easy_init();
1334       if (data[i].handle == NULL)
1335         {
1336           if (filename) curl_free (escaped_string);
1337           rc = -ENETUNREACH;
1338           goto out2;
1339         }
1340       data[i].client = c;
1341 
1342       if (filename) /* must start with / */
1343         {
1344           /* PR28034 escape characters in completed url to %hh format. */
1345           snprintf(data[i].url, PATH_MAX, "%s/%s/%s/%s", server_url,
1346                    build_id_bytes, type, escaped_string);
1347         }
1348       else if (section)
1349 	snprintf(data[i].url, PATH_MAX, "%s/%s/%s/%s", server_url,
1350 		 build_id_bytes, type, section);
1351       else
1352         snprintf(data[i].url, PATH_MAX, "%s/%s/%s", server_url, build_id_bytes, type);
1353       if (vfd >= 0)
1354 	dprintf (vfd, "url %d %s\n", i, data[i].url);
1355 
1356       /* Some boilerplate for checking curl_easy_setopt.  */
1357 #define curl_easy_setopt_ck(H,O,P) do {			\
1358       CURLcode curl_res = curl_easy_setopt (H,O,P);	\
1359       if (curl_res != CURLE_OK)				\
1360 	{						\
1361 	  if (vfd >= 0)					\
1362 	    dprintf (vfd,				\
1363 	             "Bad curl_easy_setopt: %s\n",	\
1364 		     curl_easy_strerror(curl_res));	\
1365 	  rc = -EINVAL;					\
1366 	  goto out2;					\
1367 	}						\
1368       } while (0)
1369 
1370       /* Only allow http:// + https:// + file:// so we aren't being
1371 	 redirected to some unsupported protocol.  */
1372 #if CURL_AT_LEAST_VERSION(7, 85, 0)
1373       curl_easy_setopt_ck(data[i].handle, CURLOPT_PROTOCOLS_STR,
1374 			  "http,https,file");
1375 #else
1376       curl_easy_setopt_ck(data[i].handle, CURLOPT_PROTOCOLS,
1377 			  (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FILE));
1378 #endif
1379       curl_easy_setopt_ck(data[i].handle, CURLOPT_URL, data[i].url);
1380       if (vfd >= 0)
1381 	curl_easy_setopt_ck(data[i].handle, CURLOPT_ERRORBUFFER,
1382 			    data[i].errbuf);
1383       curl_easy_setopt_ck(data[i].handle,
1384 			  CURLOPT_WRITEFUNCTION,
1385 			  debuginfod_write_callback);
1386       curl_easy_setopt_ck(data[i].handle, CURLOPT_WRITEDATA, (void*)&data[i]);
1387       if (timeout > 0)
1388 	{
1389 	  /* Make sure there is at least some progress,
1390 	     try to get at least 100K per timeout seconds.  */
1391 	  curl_easy_setopt_ck (data[i].handle, CURLOPT_LOW_SPEED_TIME,
1392 			       timeout);
1393 	  curl_easy_setopt_ck (data[i].handle, CURLOPT_LOW_SPEED_LIMIT,
1394 			       100 * 1024L);
1395 	}
1396       curl_easy_setopt_ck(data[i].handle, CURLOPT_FILETIME, (long) 1);
1397       curl_easy_setopt_ck(data[i].handle, CURLOPT_FOLLOWLOCATION, (long) 1);
1398       curl_easy_setopt_ck(data[i].handle, CURLOPT_FAILONERROR, (long) 1);
1399       curl_easy_setopt_ck(data[i].handle, CURLOPT_NOSIGNAL, (long) 1);
1400       curl_easy_setopt_ck(data[i].handle, CURLOPT_HEADERFUNCTION,
1401 			  header_callback);
1402       curl_easy_setopt_ck(data[i].handle, CURLOPT_HEADERDATA,
1403 			  (void *) &(data[i]));
1404 #if LIBCURL_VERSION_NUM >= 0x072a00 /* 7.42.0 */
1405       curl_easy_setopt_ck(data[i].handle, CURLOPT_PATH_AS_IS, (long) 1);
1406 #else
1407       /* On old curl; no big deal, canonicalization here is almost the
1408          same, except perhaps for ? # type decorations at the tail. */
1409 #endif
1410       curl_easy_setopt_ck(data[i].handle, CURLOPT_AUTOREFERER, (long) 1);
1411       curl_easy_setopt_ck(data[i].handle, CURLOPT_ACCEPT_ENCODING, "");
1412       curl_easy_setopt_ck(data[i].handle, CURLOPT_HTTPHEADER, c->headers);
1413 
1414       curl_multi_add_handle(curlm, data[i].handle);
1415     }
1416 
1417   if (filename) curl_free(escaped_string);
1418   /* Query servers in parallel.  */
1419   if (vfd >= 0)
1420     dprintf (vfd, "query %d urls in parallel\n", num_urls);
1421   int still_running;
1422   long loops = 0;
1423   int committed_to = -1;
1424   bool verbose_reported = false;
1425   struct timespec start_time, cur_time;
1426 
1427   free (c->winning_headers);
1428   c->winning_headers = NULL;
1429   if ( maxtime > 0 && clock_gettime(CLOCK_MONOTONIC_RAW, &start_time) == -1)
1430     {
1431       rc = -errno;
1432       goto out2;
1433     }
1434   long delta = 0;
1435   do
1436     {
1437       /* Check to see how long querying is taking. */
1438       if (maxtime > 0)
1439         {
1440           if (clock_gettime(CLOCK_MONOTONIC_RAW, &cur_time) == -1)
1441             {
1442               rc = -errno;
1443               goto out2;
1444             }
1445           delta = cur_time.tv_sec - start_time.tv_sec;
1446           if ( delta >  maxtime)
1447             {
1448               dprintf(vfd, "Timeout with max time=%lds and transfer time=%lds\n", maxtime, delta );
1449               rc = -ETIME;
1450               goto out2;
1451             }
1452         }
1453       /* Wait 1 second, the minimum DEBUGINFOD_TIMEOUT.  */
1454       curl_multi_wait(curlm, NULL, 0, 1000, NULL);
1455       CURLMcode curlm_res = curl_multi_perform(curlm, &still_running);
1456 
1457       /* If the target file has been found, abort the other queries.  */
1458       if (target_handle != NULL)
1459 	{
1460 	  for (int i = 0; i < num_urls; i++)
1461 	    if (data[i].handle != target_handle)
1462 	      curl_multi_remove_handle(curlm, data[i].handle);
1463 	    else
1464               {
1465 	        committed_to = i;
1466                 if (c->winning_headers == NULL)
1467                   {
1468                     c->winning_headers = data[committed_to].response_data;
1469                     data[committed_to].response_data = NULL;
1470                     data[committed_to].response_data_size = 0;
1471                   }
1472 
1473               }
1474 	}
1475 
1476       if (vfd >= 0 && !verbose_reported && committed_to >= 0)
1477 	{
1478 	  bool pnl = (c->default_progressfn_printed_p && vfd == STDERR_FILENO);
1479 	  dprintf (vfd, "%scommitted to url %d\n", pnl ? "\n" : "",
1480 		   committed_to);
1481 	  if (pnl)
1482 	    c->default_progressfn_printed_p = 0;
1483 	  verbose_reported = true;
1484 	}
1485 
1486       if (curlm_res != CURLM_OK)
1487         {
1488           switch (curlm_res)
1489             {
1490             case CURLM_CALL_MULTI_PERFORM: continue;
1491             case CURLM_OUT_OF_MEMORY: rc = -ENOMEM; break;
1492             default: rc = -ENETUNREACH; break;
1493             }
1494           goto out2;
1495         }
1496 
1497       long dl_size = -1;
1498       if (target_handle && (c->progressfn || maxsize > 0))
1499         {
1500           /* Get size of file being downloaded. NB: If going through
1501              deflate-compressing proxies, this number is likely to be
1502              unavailable, so -1 may show. */
1503           CURLcode curl_res;
1504 #if CURL_AT_LEAST_VERSION(7, 55, 0)
1505           curl_off_t cl;
1506           curl_res = curl_easy_getinfo(target_handle,
1507                                        CURLINFO_CONTENT_LENGTH_DOWNLOAD_T,
1508                                        &cl);
1509           if (curl_res == CURLE_OK && cl >= 0)
1510             dl_size = (cl > LONG_MAX ? LONG_MAX : (long)cl);
1511 #else
1512           double cl;
1513           curl_res = curl_easy_getinfo(target_handle,
1514                                        CURLINFO_CONTENT_LENGTH_DOWNLOAD,
1515                                        &cl);
1516           if (curl_res == CURLE_OK && cl >= 0)
1517             dl_size = (cl >= (double)(LONG_MAX+1UL) ? LONG_MAX : (long)cl);
1518 #endif
1519           /* If Content-Length is -1, try to get the size from
1520              X-Debuginfod-Size */
1521           if (dl_size == -1 && c->winning_headers != NULL)
1522             {
1523               long xdl;
1524               char *hdr = strcasestr(c->winning_headers, "x-debuginfod-size");
1525               size_t off = strlen("x-debuginfod-size:");
1526 
1527               if (hdr != NULL && sscanf(hdr + off, "%ld", &xdl) == 1)
1528                 dl_size = xdl;
1529             }
1530         }
1531 
1532       if (c->progressfn) /* inform/check progress callback */
1533         {
1534           loops ++;
1535           long pa = loops; /* default param for progress callback */
1536           if (target_handle) /* we've committed to a server; report its download progress */
1537             {
1538               /* PR30809: Check actual size of cached file.  This same
1539                  fd is shared by all the multi-curl handles (but only
1540                  one will end up writing to it).  Another way could be
1541                  to tabulate totals in debuginfod_write_callback(). */
1542               struct stat cached;
1543               int statrc = fstat(fd, &cached);
1544               if (statrc == 0)
1545                 pa = (long) cached.st_size;
1546               else
1547                 {
1548                   /* Otherwise, query libcurl for its tabulated total.
1549                      However, that counts http body length, not
1550                      decoded/decompressed content length, so does not
1551                      measure quite the same thing as dl. */
1552                   CURLcode curl_res;
1553 #if CURL_AT_LEAST_VERSION(7, 55, 0)
1554                   curl_off_t dl;
1555                   curl_res = curl_easy_getinfo(target_handle,
1556                                                CURLINFO_SIZE_DOWNLOAD_T,
1557                                                &dl);
1558                   if (curl_res == 0 && dl >= 0)
1559                     pa = (dl > LONG_MAX ? LONG_MAX : (long)dl);
1560 #else
1561                   double dl;
1562                   curl_res = curl_easy_getinfo(target_handle,
1563                                                CURLINFO_SIZE_DOWNLOAD,
1564                                                &dl);
1565                   if (curl_res == 0)
1566                     pa = (dl >= (double)(LONG_MAX+1UL) ? LONG_MAX : (long)dl);
1567 #endif
1568                 }
1569             }
1570 
1571           if ((*c->progressfn) (c, pa, dl_size == -1 ? 0 : dl_size))
1572 	    {
1573 	      c->progressfn_cancel = true;
1574               break;
1575 	    }
1576         }
1577 
1578       /* Check to see if we are downloading something which exceeds maxsize, if set.*/
1579       if (target_handle && dl_size > maxsize && maxsize > 0)
1580         {
1581           if (vfd >=0)
1582             dprintf(vfd, "Content-Length too large.\n");
1583           rc = -EFBIG;
1584           goto out2;
1585         }
1586     } while (still_running);
1587 
1588   /* Check whether a query was successful. If so, assign its handle
1589      to verified_handle.  */
1590   int num_msg;
1591   rc = -ENOENT;
1592   CURL *verified_handle = NULL;
1593   do
1594     {
1595       CURLMsg *msg;
1596 
1597       msg = curl_multi_info_read(curlm, &num_msg);
1598       if (msg != NULL && msg->msg == CURLMSG_DONE)
1599         {
1600 	  if (vfd >= 0)
1601 	    {
1602 	      bool pnl = (c->default_progressfn_printed_p
1603 			  && vfd == STDERR_FILENO);
1604 	      dprintf (vfd, "%sserver response %s\n", pnl ? "\n" : "",
1605 		       curl_easy_strerror (msg->data.result));
1606 	      if (pnl)
1607 		c->default_progressfn_printed_p = 0;
1608 	      for (int i = 0; i < num_urls; i++)
1609 		if (msg->easy_handle == data[i].handle)
1610 		  {
1611 		    if (strlen (data[i].errbuf) > 0)
1612 		      dprintf (vfd, "url %d %s\n", i, data[i].errbuf);
1613 		    break;
1614 		  }
1615 	    }
1616 
1617           if (msg->data.result != CURLE_OK)
1618             {
1619               long resp_code;
1620               CURLcode ok0;
1621               /* Unsuccessful query, determine error code.  */
1622               switch (msg->data.result)
1623                 {
1624                 case CURLE_COULDNT_RESOLVE_HOST: rc = -EHOSTUNREACH; break; // no NXDOMAIN
1625                 case CURLE_URL_MALFORMAT: rc = -EINVAL; break;
1626                 case CURLE_COULDNT_CONNECT: rc = -ECONNREFUSED; break;
1627                 case CURLE_PEER_FAILED_VERIFICATION: rc = -ECONNREFUSED; break;
1628                 case CURLE_REMOTE_ACCESS_DENIED: rc = -EACCES; break;
1629                 case CURLE_WRITE_ERROR: rc = -EIO; break;
1630                 case CURLE_OUT_OF_MEMORY: rc = -ENOMEM; break;
1631                 case CURLE_TOO_MANY_REDIRECTS: rc = -EMLINK; break;
1632                 case CURLE_SEND_ERROR: rc = -ECONNRESET; break;
1633                 case CURLE_RECV_ERROR: rc = -ECONNRESET; break;
1634                 case CURLE_OPERATION_TIMEDOUT: rc = -ETIME; break;
1635                 case CURLE_HTTP_RETURNED_ERROR:
1636                   ok0 = curl_easy_getinfo (msg->easy_handle,
1637                                           CURLINFO_RESPONSE_CODE,
1638 				          &resp_code);
1639                   /* 406 signals that the requested file was too large */
1640                   if ( ok0 == CURLE_OK && resp_code == 406)
1641                     rc = -EFBIG;
1642 		  else if (section != NULL && resp_code == 503)
1643 		    rc = -EINVAL;
1644                   else
1645                     rc = -ENOENT;
1646                   break;
1647                 default: rc = -ENOENT; break;
1648                 }
1649             }
1650           else
1651             {
1652               /* Query completed without an error. Confirm that the
1653                  response code is 200 when using HTTP/HTTPS and 0 when
1654                  using file:// and set verified_handle.  */
1655 
1656               if (msg->easy_handle != NULL)
1657                 {
1658                   char *effective_url = NULL;
1659                   long resp_code = 500;
1660                   CURLcode ok1 = curl_easy_getinfo (target_handle,
1661 						    CURLINFO_EFFECTIVE_URL,
1662 						    &effective_url);
1663                   CURLcode ok2 = curl_easy_getinfo (target_handle,
1664 						    CURLINFO_RESPONSE_CODE,
1665 						    &resp_code);
1666                   if(ok1 == CURLE_OK && ok2 == CURLE_OK && effective_url)
1667                     {
1668                       if (strncasecmp (effective_url, "HTTP", 4) == 0)
1669                         if (resp_code == 200)
1670                           {
1671                             verified_handle = msg->easy_handle;
1672                             break;
1673                           }
1674                       if (strncasecmp (effective_url, "FILE", 4) == 0)
1675                         if (resp_code == 0)
1676                           {
1677                             verified_handle = msg->easy_handle;
1678                             break;
1679                           }
1680                     }
1681                   /* - libcurl since 7.52.0 version start to support
1682                        CURLINFO_SCHEME;
1683                      - before 7.61.0, effective_url would give us a
1684                        url with upper case SCHEME added in the front;
1685                      - effective_url between 7.61 and 7.69 can be lack
1686                        of scheme if the original url doesn't include one;
1687                      - since version 7.69 effective_url will be provide
1688                        a scheme in lower case.  */
1689                   #if LIBCURL_VERSION_NUM >= 0x073d00 /* 7.61.0 */
1690                   #if LIBCURL_VERSION_NUM <= 0x074500 /* 7.69.0 */
1691                   char *scheme = NULL;
1692                   CURLcode ok3 = curl_easy_getinfo (target_handle,
1693                                                     CURLINFO_SCHEME,
1694                                                     &scheme);
1695                   if(ok3 == CURLE_OK && scheme)
1696                     {
1697                       if (startswith (scheme, "HTTP"))
1698                         if (resp_code == 200)
1699                           {
1700                             verified_handle = msg->easy_handle;
1701                             break;
1702                           }
1703                     }
1704                   #endif
1705                   #endif
1706                 }
1707             }
1708         }
1709     } while (num_msg > 0);
1710 
1711   /* Create an empty file in the cache if the query fails with ENOENT and
1712      it wasn't cancelled early.  */
1713   if (rc == -ENOENT && !c->progressfn_cancel)
1714     {
1715       int efd = open (target_cache_path, O_CREAT|O_EXCL, DEFFILEMODE);
1716       if (efd >= 0)
1717         close(efd);
1718     }
1719   else if (rc == -EFBIG)
1720     goto out2;
1721 
1722   /* If the verified_handle is NULL and rc != -ENOENT, the query fails with
1723    * an error code other than 404, then do several retry within the retry_limit.
1724    * Clean up all old handles and jump back to the beginning of query_in_parallel,
1725    * reinitialize handles and query again.*/
1726   if (verified_handle == NULL)
1727     {
1728       if (rc != -ENOENT && retry_limit-- > 0)
1729         {
1730 	  if (vfd >= 0)
1731             dprintf (vfd, "Retry failed query, %d attempt(s) remaining\n", retry_limit);
1732 	  /* remove all handles from multi */
1733           for (int i = 0; i < num_urls; i++)
1734             {
1735               curl_multi_remove_handle(curlm, data[i].handle); /* ok to repeat */
1736               curl_easy_cleanup (data[i].handle);
1737               free(data[i].response_data);
1738             }
1739             free(c->winning_headers);
1740             c->winning_headers = NULL;
1741 	    goto query_in_parallel;
1742 	}
1743       else
1744 	goto out2;
1745     }
1746 
1747   if (vfd >= 0)
1748     {
1749       bool pnl = c->default_progressfn_printed_p && vfd == STDERR_FILENO;
1750       dprintf (vfd, "%sgot file from server\n", pnl ? "\n" : "");
1751       if (pnl)
1752 	c->default_progressfn_printed_p = 0;
1753     }
1754 
1755   /* we've got one!!!! */
1756   time_t mtime;
1757 #if defined(_TIME_BITS) && _TIME_BITS == 64
1758   CURLcode curl_res = curl_easy_getinfo(verified_handle, CURLINFO_FILETIME_T, (void*) &mtime);
1759 #else
1760   CURLcode curl_res = curl_easy_getinfo(verified_handle, CURLINFO_FILETIME, (void*) &mtime);
1761 #endif
1762   if (curl_res == CURLE_OK)
1763     {
1764       struct timespec tvs[2];
1765       tvs[0].tv_sec = 0;
1766       tvs[0].tv_nsec = UTIME_OMIT;
1767       tvs[1].tv_sec = mtime;
1768       tvs[1].tv_nsec = 0;
1769       (void) futimens (fd, tvs);  /* best effort */
1770     }
1771 
1772   /* PR27571: make cache files casually unwriteable; dirs are already 0700 */
1773   (void) fchmod(fd, 0400);
1774   /* PR31248: lseek back to beginning */
1775   (void) lseek(fd, 0, SEEK_SET);
1776 
1777   /* rename tmp->real */
1778   rc = rename (target_cache_tmppath, target_cache_path);
1779   if (rc < 0)
1780     {
1781       rc = -errno;
1782       goto out2;
1783       /* Perhaps we need not give up right away; could retry or something ... */
1784     }
1785 
1786   /* remove all handles from multi */
1787   for (int i = 0; i < num_urls; i++)
1788     {
1789       curl_multi_remove_handle(curlm, data[i].handle); /* ok to repeat */
1790       curl_easy_cleanup (data[i].handle);
1791       free (data[i].response_data);
1792     }
1793 
1794   for (int i = 0; i < num_urls; ++i)
1795     free(server_url_list[i]);
1796   free(server_url_list);
1797   free (data);
1798   free (server_urls);
1799 
1800   /* don't close fd - we're returning it */
1801   /* don't unlink the tmppath; it's already been renamed. */
1802   if (path != NULL)
1803    *path = strdup(target_cache_path);
1804 
1805   rc = fd;
1806   goto out;
1807 
1808 /* error exits */
1809  out2:
1810   /* remove all handles from multi */
1811   for (int i = 0; i < num_urls; i++)
1812     {
1813       if (data[i].handle != NULL)
1814 	{
1815 	  curl_multi_remove_handle(curlm, data[i].handle); /* ok to repeat */
1816 	  curl_easy_cleanup (data[i].handle);
1817 	  free (data[i].response_data);
1818 	}
1819     }
1820 
1821   unlink (target_cache_tmppath);
1822   close (fd); /* before the rmdir, otherwise it'll fail */
1823   (void) rmdir (target_cache_dir); /* nop if not empty */
1824   free(data);
1825 
1826  out1:
1827   for (int i = 0; i < num_urls; ++i)
1828     free(server_url_list[i]);
1829   free(server_url_list);
1830 
1831  out0:
1832   free (server_urls);
1833 
1834 /* general purpose exit */
1835  out:
1836   /* Reset sent headers */
1837   curl_slist_free_all (c->headers);
1838   c->headers = NULL;
1839   c->user_agent_set_p = 0;
1840 
1841   /* Conclude the last \r status line */
1842   /* Another possibility is to use the ANSI CSI n K EL "Erase in Line"
1843      code.  That way, the previously printed messages would be erased,
1844      and without a newline. */
1845   if (c->default_progressfn_printed_p)
1846     dprintf(STDERR_FILENO, "\n");
1847 
1848   if (vfd >= 0)
1849     {
1850       if (rc < 0)
1851 	dprintf (vfd, "not found %s (err=%d)\n", strerror (-rc), rc);
1852       else
1853 	dprintf (vfd, "found %s (fd=%d)\n", target_cache_path, rc);
1854     }
1855 
1856   free (cache_path);
1857   free (maxage_path);
1858   free (interval_path);
1859   free (cache_miss_path);
1860   free (target_cache_dir);
1861   free (target_cache_path);
1862   free (target_cache_tmppath);
1863   return rc;
1864 }
1865 
1866 
1867 
1868 /* See debuginfod.h  */
1869 debuginfod_client  *
debuginfod_begin(void)1870 debuginfod_begin (void)
1871 {
1872   /* Initialize libcurl lazily, but only once.  */
1873   pthread_once (&init_control, libcurl_init);
1874 
1875   debuginfod_client *client;
1876   size_t size = sizeof (struct debuginfod_client);
1877   client = calloc (1, size);
1878 
1879   if (client != NULL)
1880     {
1881       if (getenv(DEBUGINFOD_PROGRESS_ENV_VAR))
1882 	client->progressfn = default_progressfn;
1883       if (getenv(DEBUGINFOD_VERBOSE_ENV_VAR))
1884 	client->verbose_fd = STDERR_FILENO;
1885       else
1886 	client->verbose_fd = -1;
1887 
1888       // allocate 1 curl multi handle
1889       client->server_mhandle = curl_multi_init ();
1890       if (client->server_mhandle == NULL)
1891 	goto out1;
1892     }
1893 
1894   // extra future initialization
1895 
1896   goto out;
1897 
1898  out1:
1899   free (client);
1900   client = NULL;
1901 
1902  out:
1903   return client;
1904 }
1905 
1906 void
debuginfod_set_user_data(debuginfod_client * client,void * data)1907 debuginfod_set_user_data(debuginfod_client *client,
1908                          void *data)
1909 {
1910   client->user_data = data;
1911 }
1912 
1913 void *
debuginfod_get_user_data(debuginfod_client * client)1914 debuginfod_get_user_data(debuginfod_client *client)
1915 {
1916   return client->user_data;
1917 }
1918 
1919 const char *
debuginfod_get_url(debuginfod_client * client)1920 debuginfod_get_url(debuginfod_client *client)
1921 {
1922   return client->url;
1923 }
1924 
1925 const char *
debuginfod_get_headers(debuginfod_client * client)1926 debuginfod_get_headers(debuginfod_client *client)
1927 {
1928   return client->winning_headers;
1929 }
1930 
1931 void
debuginfod_end(debuginfod_client * client)1932 debuginfod_end (debuginfod_client *client)
1933 {
1934   if (client == NULL)
1935     return;
1936 
1937   curl_multi_cleanup (client->server_mhandle);
1938   curl_slist_free_all (client->headers);
1939   free (client->winning_headers);
1940   free (client->url);
1941   free (client);
1942 }
1943 
1944 int
debuginfod_find_debuginfo(debuginfod_client * client,const unsigned char * build_id,int build_id_len,char ** path)1945 debuginfod_find_debuginfo (debuginfod_client *client,
1946 			   const unsigned char *build_id, int build_id_len,
1947                            char **path)
1948 {
1949   return debuginfod_query_server(client, build_id, build_id_len,
1950                                  "debuginfo", NULL, path);
1951 }
1952 
1953 
1954 /* See debuginfod.h  */
1955 int
debuginfod_find_executable(debuginfod_client * client,const unsigned char * build_id,int build_id_len,char ** path)1956 debuginfod_find_executable(debuginfod_client *client,
1957 			   const unsigned char *build_id, int build_id_len,
1958                            char **path)
1959 {
1960   return debuginfod_query_server(client, build_id, build_id_len,
1961                                  "executable", NULL, path);
1962 }
1963 
1964 /* See debuginfod.h  */
debuginfod_find_source(debuginfod_client * client,const unsigned char * build_id,int build_id_len,const char * filename,char ** path)1965 int debuginfod_find_source(debuginfod_client *client,
1966 			   const unsigned char *build_id, int build_id_len,
1967                            const char *filename, char **path)
1968 {
1969   return debuginfod_query_server(client, build_id, build_id_len,
1970                                  "source", filename, path);
1971 }
1972 
1973 int
debuginfod_find_section(debuginfod_client * client,const unsigned char * build_id,int build_id_len,const char * section,char ** path)1974 debuginfod_find_section (debuginfod_client *client,
1975 			 const unsigned char *build_id, int build_id_len,
1976 			 const char *section, char **path)
1977 {
1978   int rc = debuginfod_query_server(client, build_id, build_id_len,
1979 				   "section", section, path);
1980   if (rc != -EINVAL)
1981     return rc;
1982 
1983   /* The servers may have lacked support for section queries.  Attempt to
1984      download the debuginfo or executable containing the section in order
1985      to extract it.  */
1986   rc = -EEXIST;
1987   int fd = -1;
1988   char *tmp_path = NULL;
1989 
1990   fd = debuginfod_find_debuginfo (client, build_id, build_id_len, &tmp_path);
1991   if (client->progressfn_cancel)
1992     {
1993       if (fd >= 0)
1994 	{
1995 	  /* This shouldn't happen, but we'll check this condition
1996 	     just in case.  */
1997 	  close (fd);
1998 	  free (tmp_path);
1999 	}
2000       return -ENOENT;
2001     }
2002   if (fd >= 0)
2003     {
2004       rc = extract_section (fd, section, tmp_path, path);
2005       close (fd);
2006     }
2007 
2008   if (rc == -EEXIST)
2009     {
2010       /* Either the debuginfo couldn't be found or the section should
2011 	 be in the executable.  */
2012       fd = debuginfod_find_executable (client, build_id,
2013 				       build_id_len, &tmp_path);
2014       if (fd >= 0)
2015 	{
2016 	  rc = extract_section (fd, section, tmp_path, path);
2017 	  close (fd);
2018 	}
2019       else
2020 	/* Update rc so that we return the most recent error code.  */
2021 	rc = fd;
2022     }
2023 
2024   free (tmp_path);
2025   return rc;
2026 }
2027 
2028 /* Add an outgoing HTTP header.  */
debuginfod_add_http_header(debuginfod_client * client,const char * header)2029 int debuginfod_add_http_header (debuginfod_client *client, const char* header)
2030 {
2031   /* Sanity check header value is of the form Header: Value.
2032      It should contain at least one colon that isn't the first or
2033      last character.  */
2034   char *colon = strchr (header, ':'); /* first colon */
2035   if (colon == NULL /* present */
2036       || colon == header /* not at beginning - i.e., have a header name */
2037       || *(colon + 1) == '\0') /* not at end - i.e., have a value */
2038     /* NB: but it's okay for a value to contain other colons! */
2039     return -EINVAL;
2040 
2041   struct curl_slist *temp = curl_slist_append (client->headers, header);
2042   if (temp == NULL)
2043     return -ENOMEM;
2044 
2045   /* Track if User-Agent: is being set.  If so, signal not to add the
2046      default one. */
2047   if (startswith (header, "User-Agent:"))
2048     client->user_agent_set_p = 1;
2049 
2050   client->headers = temp;
2051   return 0;
2052 }
2053 
2054 
2055 void
debuginfod_set_progressfn(debuginfod_client * client,debuginfod_progressfn_t fn)2056 debuginfod_set_progressfn(debuginfod_client *client,
2057 			  debuginfod_progressfn_t fn)
2058 {
2059   client->progressfn = fn;
2060 }
2061 
2062 void
debuginfod_set_verbose_fd(debuginfod_client * client,int fd)2063 debuginfod_set_verbose_fd(debuginfod_client *client, int fd)
2064 {
2065   client->verbose_fd = fd;
2066 }
2067 
2068 #endif /* DUMMY_LIBDEBUGINFOD */
2069