1 /* AirScan (a.k.a. eSCL) backend for SANE
2  *
3  * Copyright (C) 2019 and up by Alexander Pevzner (pzz@apevzner.com)
4  * See LICENSE for license terms and conditions
5  *
6  * HTTP Client
7  */
8 #define _GNU_SOURCE
9 #include <string.h>
10 
11 #define NO_HTTP_STATUS
12 
13 #include "airscan.h"
14 #include "http_parser.h"
15 
16 #include <arpa/inet.h>
17 #include <errno.h>
18 #include <netdb.h>
19 #include <stdlib.h>
20 #include <sys/socket.h>
21 #include <sys/types.h>
22 #include <sys/un.h>
23 #include <unistd.h>
24 
25 #include <gnutls/gnutls.h>
26 
27 /******************** Constants ********************/
28 /* I/O buffer size
29  */
30 #define HTTP_IOBUF_SIZE         65536
31 
32 /* Limit of chained HTTP redirects
33  */
34 #define HTTP_REDIRECT_LIMIT     8
35 
36 /* Default query timeout, milliseconds
37  * Disabled, if negative
38  */
39 #define HTTP_QUERY_TIMEOUT      -1
40 
41 /******************** Static variables ********************/
42 static gnutls_certificate_credentials_t gnutls_cred;
43 
44 /******************** Forward declarations ********************/
45 typedef struct http_multipart http_multipart;
46 
47 static http_data*
48 http_data_new(http_data *parent, const char *bytes, size_t size);
49 
50 static void
51 http_data_set_content_type (http_data *data, const char *content_type);
52 
53 static void
54 http_query_timeout_cancel (http_query *q);
55 
56 static http_query*
57 http_query_by_ll_node (ll_node *node);
58 
59 static void
60 http_query_complete (http_query *q, error err);
61 
62 static void
63 http_query_connect (http_query *q, error err);
64 
65 static void
66 http_query_disconnect (http_query *q);
67 
68 static ssize_t
69 http_query_sock_send (http_query *q, const void *data, size_t size);
70 
71 static ssize_t
72 http_query_sock_recv (http_query *q, void *data, size_t size);
73 
74 static error
75 http_query_sock_err (http_query *q, int rc);
76 
77 static void
78 http_query_cancel (http_query *q);
79 
80 /******************** HTTP URI ********************/
81 /* Type http_uri represents HTTP URI
82  */
83 struct http_uri {
84     struct http_parser_url parsed; /* Parsed URI */
85     const char             *str;   /* URI string */
86     const char             *path;  /* URI path */
87     const char             *host;  /* URI host */
88     HTTP_SCHEME            scheme; /* URI scheme */
89     union {                 /* Host address*/
90         struct sockaddr     sockaddr;
91         struct sockaddr_in  in;
92         struct sockaddr_in6 in6;
93     } addr;
94 };
95 
96 typedef struct {
97     const char *str;
98     size_t     len;
99 } http_uri_field;
100 
101 /* Make http_uri_field
102  */
103 static http_uri_field
http_uri_field_make(const char * str)104 http_uri_field_make (const char *str)
105 {
106     http_uri_field field = {str, strlen(str)};
107     return field;
108 }
109 
110 /* Check if URI has a particular field
111  */
112 static inline bool
http_uri_field_present(const http_uri * uri,int num)113 http_uri_field_present (const http_uri *uri, int num)
114 {
115     return (uri->parsed.field_set & (1 << num)) != 0;
116 }
117 
118 /* Check if URI field is present and non-empty
119  */
120 static inline bool
http_uri_field_nonempty(const http_uri * uri,int num)121 http_uri_field_nonempty (const http_uri *uri, int num)
122 {
123     return uri->parsed.field_data[num].len != 0;
124 }
125 
126 /* Get first character of the field. Returns -1, if
127  * field is empty, or non-negative character code otherwise
128  */
129 static inline int
http_uri_field_begin(const http_uri * uri,int num)130 http_uri_field_begin (const http_uri *uri, int num)
131 {
132     if (http_uri_field_nonempty(uri, num)) {
133         return (unsigned char) uri->str[uri->parsed.field_data[num].off];
134     } else {
135         return -1;
136     }
137 }
138 
139 /* Get field from URI
140  */
141 static http_uri_field
http_uri_field_get(const http_uri * uri,int num)142 http_uri_field_get (const http_uri *uri, int num)
143 {
144     http_uri_field field = {
145         uri->str + uri->parsed.field_data[num].off,
146         uri->parsed.field_data[num].len
147     };
148 
149     return field;
150 }
151 
152 /* Append field to buffer, and return pointer to
153  * updated buffer tail
154  */
155 static inline char *
http_uri_field_append(http_uri_field field,char * buf)156 http_uri_field_append (http_uri_field field, char *buf)
157 {
158     memcpy(buf, field.str, field.len);
159     return buf + field.len;
160 }
161 
162 /* Get field from URI and append to buffer
163  */
164 static inline char*
http_uri_field_copy(const http_uri * uri,int num,char * buf)165 http_uri_field_copy (const http_uri *uri, int num, char *buf)
166 {
167     return http_uri_field_append(http_uri_field_get(uri, num), buf);
168 }
169 
170 /* Get and strdup the field
171  */
172 static char*
http_uri_field_strdup(const http_uri * uri,int num)173 http_uri_field_strdup (const http_uri *uri, int num)
174 {
175     http_uri_field field = http_uri_field_get(uri, num);
176     char           *s = mem_new(char, field.len + 1);
177 
178     memcpy(s, field.str, field.len);
179     s[field.len] = '\0';
180 
181     return s;
182 }
183 
184 /* Check are fields of two URIs are equal
185  */
186 static bool
http_uri_field_equal(const http_uri * uri1,const http_uri * uri2,int num,bool nocase)187 http_uri_field_equal (const http_uri *uri1, const http_uri *uri2,
188         int num, bool nocase)
189 {
190     http_uri_field f1 = http_uri_field_get(uri1, num);
191     http_uri_field f2 = http_uri_field_get(uri2, num);
192 
193     if (f1.len != f2.len) {
194         return false;
195     }
196 
197     if (nocase) {
198         return !strncasecmp(f1.str, f2.str, f1.len);
199     } else {
200         return !memcmp(f1.str, f2.str, f1.len);
201     }
202 }
203 
204 /* Replace particular URI field with val[len] string
205  */
206 static void
http_uri_field_replace_len(http_uri * uri,int num,const char * val,size_t len)207 http_uri_field_replace_len (http_uri *uri, int num, const char *val, size_t len)
208 {
209     static const struct { char *pfx; int num; char *sfx; } fields[] = {
210         {"",  UF_SCHEMA, "://"},
211         {"",  UF_USERINFO, "@"},
212         {"",  UF_HOST, ""},
213         {":", UF_PORT, ""},
214         {"",  UF_PATH, ""},
215         {"?", UF_QUERY, ""},
216         {"#", UF_FRAGMENT, ""},
217         {NULL, -1, NULL}
218     };
219 
220     int      i;
221     char     *buf = alloca(strlen(uri->str) + len + 4);
222     char     *end = buf;
223     http_uri *uri2;
224 
225     /* Rebuild URI string */
226     for (i = 0; fields[i].num != -1; i ++) {
227         http_uri_field field;
228 
229         if (num == fields[i].num) {
230             field.str = val;
231             field.len = len;
232         } else {
233             field = http_uri_field_get(uri, fields[i].num);
234         }
235 
236         if (field.len != 0) {
237             bool ip6_host = false;
238 
239             if (fields[i].num == UF_HOST) {
240                 ip6_host = memchr(field.str, ':', field.len) != NULL;
241             }
242 
243             if (fields[i].pfx != NULL) {
244                 http_uri_field pfx = http_uri_field_make(fields[i].pfx);
245                 end = http_uri_field_append(pfx, end);
246             }
247 
248             if (ip6_host) {
249                 *end ++ = '[';
250             }
251 
252             end = http_uri_field_append(field, end);
253 
254             if (ip6_host) {
255                 *end ++ = ']';
256             }
257 
258             if (fields[i].sfx != NULL) {
259                 http_uri_field sfx = http_uri_field_make(fields[i].sfx);
260                 end = http_uri_field_append(sfx, end);
261             }
262         }
263     }
264 
265     *end = '\0';
266 
267     /* Reconstruct the URI */
268     uri2 = http_uri_new(buf, false);
269     log_assert(NULL, uri2 != NULL);
270 
271     mem_free((char*) uri->str);
272     mem_free((char*) uri->path);
273     mem_free((char*) uri->host);
274 
275     *uri = *uri2;
276     mem_free(uri2);
277 }
278 
279 /* Replace particular URI field
280  */
281 static void
http_uri_field_replace(http_uri * uri,int num,const char * val)282 http_uri_field_replace (http_uri *uri, int num, const char *val)
283 {
284     http_uri_field_replace_len (uri, num, val, strlen(val));
285 }
286 
287 /* Append UF_PATH part of URI to buffer up to the final '/'
288  * character
289  */
290 static char*
http_uri_field_copy_basepath(const http_uri * uri,char * buf)291 http_uri_field_copy_basepath (const http_uri *uri, char *buf)
292 {
293     http_uri_field path = http_uri_field_get(uri, UF_PATH);
294     const char     *end = memrchr(path.str, '/', path.len);
295 
296     path.len = end ? (size_t)(end - path.str) : 0;
297 
298     buf = http_uri_field_append(path, buf);
299     *(buf ++) = '/';
300 
301     return buf;
302 }
303 
304 /* Check if sting has an scheme prefix, where scheme
305  * must be [a-zA-Z][a-zA-Z0-9+-.]*):
306  *
307  * If scheme is found, returns its length. 0 is returned otherwise
308  */
309 static size_t
http_uri_parse_scheme(const char * str)310 http_uri_parse_scheme (const char *str)
311 {
312     char   c = *str;
313     size_t i;
314 
315     if (!(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))) {
316         return 0;
317     }
318 
319     i = 1;
320     for (;;) {
321         c = str[i ++];
322 
323         if (('a' <= c && c <= 'z') ||
324             ('A' <= c && c <= 'Z') ||
325             ('0' <= c && c <= '9') ||
326             c == '+' ||
327             c == '-' ||
328             c == '.') {
329             continue;
330         }
331 
332         break;
333     }
334 
335     return c == ':' ? i : 0;
336 }
337 
338 /* Return error, if UF_HOST field of URI is present and invalid
339  */
340 static error
http_uri_parse_check_host(http_uri * uri)341 http_uri_parse_check_host (http_uri *uri)
342 {
343     http_uri_field  field = http_uri_field_get(uri, UF_HOST);
344     char            *host, *zone;
345     int             rc;
346     struct in6_addr in6;
347 
348     /* If UF_HOST is present and in square brackets, it must
349      * be valid IPv6 literal. Note, http_parser_parse_url()
350      * doesn't check it
351      */
352     if (field.len == 0 || field.str == uri->str || field.str[-1] != '[') {
353         return NULL;
354     }
355 
356     if (field.str[field.len] != ']') {
357         return ERROR("URI: missed ']' in IP6 address literal");
358     }
359 
360     host = alloca(field.len + 1);
361     memcpy(host, field.str, field.len);
362     host[field.len] = '\0';
363 
364     zone = strchr(host, '%');
365     if (zone != NULL) {
366         *zone = '\0';
367     }
368 
369     rc = inet_pton(AF_INET6, host, &in6);
370     if (rc != 1) {
371         return ERROR("URI: invalid IP6 address literal");
372     }
373 
374     return NULL;
375 }
376 
377 /* Parse URI in place. The parsed URI doesn't create
378  * a copy of URI string, and uses supplied string directly
379  */
380 static error
http_uri_parse(http_uri * uri,const char * str)381 http_uri_parse (http_uri *uri, const char *str)
382 {
383     size_t     scheme_len = http_uri_parse_scheme(str);
384     const char *normalized = str;
385     const char *prefix = NULL;
386     size_t     prefix_len = 0;
387     size_t     path_skip = 0;
388     error      err;
389 
390     /* Note, github.com/nodejs/http-parser fails to properly
391      * parse relative URLs (URLs without scheme), so prepend
392      * fake scheme, then remove it
393      */
394     if (scheme_len == 0) {
395         char *s;
396 
397         if (str[0] == '/' && str[1] == '/') {
398             prefix = "s:";
399         } else if (str[0] == '/') {
400             prefix = "s://h";
401         } else {
402             prefix = "s://h/";
403             path_skip = 1;
404         }
405 
406         prefix_len = strlen(prefix);
407         s = alloca(prefix_len + strlen(str) + 1);
408         memcpy(s, prefix, prefix_len);
409         strcpy(s + prefix_len, str);
410 
411         normalized = s;
412     }
413 
414     /* Parse URI */
415     memset(uri, 0, sizeof(*uri));
416     if (http_parser_parse_url(normalized, strlen(normalized),
417             0, &uri->parsed) != 0) {
418         return ERROR("URI: parse error");
419     }
420 
421     uri->str = str;
422 
423     /* Adjust offsets */
424     if (path_skip != 0) {
425         uri->parsed.field_data[UF_PATH].off ++;
426         uri->parsed.field_data[UF_PATH].len --;
427     }
428 
429     if (prefix_len != 0) {
430         unsigned int i;
431 
432         for (i = 0; i < UF_MAX; i ++) {
433             if ((uri->parsed.field_set & (1 << i)) != 0) {
434                 if (uri->parsed.field_data[i].off >= (uint16_t) prefix_len) {
435                     uri->parsed.field_data[i].off -= (uint16_t) prefix_len;
436                 } else {
437                     uri->parsed.field_data[i].off = 0;
438                     uri->parsed.field_data[i].len = 0;
439                     uri->parsed.field_set &= ~ (1 << i);
440                 }
441             }
442         }
443     }
444 
445     /* Decode scheme */
446     uri->scheme = HTTP_SCHEME_UNSET; /* For sanity */
447     if (scheme_len != 0) {
448         if (!strncasecmp(str, "http://", 7)) {
449             uri->scheme = HTTP_SCHEME_HTTP;
450         } else if (!strncasecmp(str, "https://", 8)) {
451             uri->scheme = HTTP_SCHEME_HTTPS;
452         } else if (!strncasecmp(str, "unix://", 7)) {
453             uri->scheme = HTTP_SCHEME_UNIX;
454         } else {
455             return ERROR("URI: invalid scheme");
456         }
457     }
458 
459     /* Check UF_HOST */
460     err = http_uri_parse_check_host(uri);
461     if (err != NULL) {
462         return err;
463     }
464 
465     return NULL;
466 }
467 
468 /* Un-escape host name in place
469  */
470 static void
http_uri_unescape_host(char * host)471 http_uri_unescape_host (char *host)
472 {
473     char *zone = strstr(host, "%25");
474     if (zone != NULL) {
475         memmove(zone + 1, zone + 3, strlen(zone + 3) + 1);
476     }
477 }
478 
479 /* Parse URI address
480  */
481 static void
http_uri_parse_addr(http_uri * uri)482 http_uri_parse_addr (http_uri *uri)
483 {
484     http_uri_field field;
485     char           *host = NULL, *port = NULL;
486     uint16_t       portnum;
487     int            rc;
488 
489     /* Reset address */
490     memset(&uri->addr, 0, sizeof(uri->addr));
491 
492     /* Get host and port */
493     field = http_uri_field_get(uri, UF_HOST);
494     if (field.len != 0) {
495         host = alloca(field.len + 1);
496         memcpy(host, field.str, field.len);
497         host[field.len] = '\0';
498         http_uri_unescape_host(host);
499     }
500 
501     field = http_uri_field_get(uri, UF_PORT);
502     if (field.len != 0) {
503         port = alloca(field.len + 1);
504         memcpy(port, field.str, field.len);
505         port[field.len] = '\0';
506     }
507 
508     if (host == NULL) {
509         return;
510     }
511 
512     /* Parse port number */
513     if (port != NULL) {
514         char          *end;
515         unsigned long val = strtoul(port, &end, 10);
516 
517         if (end == port || *end != '\0' || val > 0xffff) {
518             return;
519         }
520 
521         portnum = htons((uint16_t) val);
522     } else {
523         switch (uri->scheme) {
524         case HTTP_SCHEME_HTTP:
525             portnum = htons(80);
526             break;
527 
528         case HTTP_SCHEME_HTTPS:
529             portnum = htons(443);
530             break;
531 
532         default:
533             return;
534         }
535     }
536 
537     if (strchr(host, ':') != NULL) {
538         struct in6_addr in6;
539 
540         /* Strip zone suffix */
541         char *s = strchr(host, '%');
542         if (s != NULL) {
543             *s = '\0';
544         }
545 
546         rc = inet_pton(AF_INET6, host, &in6);
547         if (rc != 1) {
548             return;
549         }
550 
551         uri->addr.in6.sin6_family = AF_INET6;
552         uri->addr.in6.sin6_addr = in6;
553         uri->addr.in6.sin6_port = portnum;
554     } else {
555         struct in_addr in;
556 
557         rc = inet_pton(AF_INET, host, &in);
558         if (rc != 1) {
559             return;
560         }
561 
562         uri->addr.in.sin_family = AF_INET;
563         uri->addr.in.sin_addr = in;
564         uri->addr.in.sin_port = portnum;
565     }
566 }
567 
568 /* Create new URI, by parsing URI string
569  */
570 http_uri*
http_uri_new(const char * str,bool strip_fragment)571 http_uri_new (const char *str, bool strip_fragment)
572 {
573     http_uri       *uri = mem_new(http_uri, 1);
574     char           *buf;
575     http_uri_field field;
576 
577     /* Parse URI */
578     if (http_uri_parse(uri, str) != NULL) {
579         goto FAIL;
580     }
581 
582     /* Don't allow relative URLs here */
583     if (uri->scheme == HTTP_SCHEME_UNSET) {
584         goto FAIL;
585     }
586 
587     uri->str = buf = str_dup(str);
588 
589     /* Honor strip_fragment flag */
590     if (strip_fragment && http_uri_field_present(uri, UF_FRAGMENT)) {
591         buf[uri->parsed.field_data[UF_FRAGMENT].off - 1] = '\0';
592         uri->parsed.field_set &= ~(1 << UF_FRAGMENT);
593         uri->parsed.field_data[UF_FRAGMENT].off = 0;
594         uri->parsed.field_data[UF_FRAGMENT].len = 0;
595     }
596 
597     /* Prepare addr, path, etc */
598     http_uri_parse_addr(uri);
599     uri->path = http_uri_field_strdup(uri, UF_PATH);
600 
601     field = http_uri_field_get(uri, UF_HOST);
602     if (memchr(field.str, ':', field.len) != NULL) {
603         char *host = mem_resize((char*) NULL, field.len + 2, 1);
604 
605         host[0] = '[';
606         memcpy(host + 1, field.str, field.len);
607         host[field.len + 1] = ']';
608         host[field.len + 2] = '\0';
609         uri->host = host;
610     } else {
611         uri->host = http_uri_field_strdup(uri, UF_HOST);
612     }
613 
614     return uri;
615 
616     /* Error: cleanup and exit */
617 FAIL:
618     mem_free(uri);
619     return NULL;
620 }
621 
622 /* Clone the URI
623  */
624 http_uri*
http_uri_clone(const http_uri * old)625 http_uri_clone (const http_uri *old)
626 {
627     http_uri *uri = mem_new(http_uri, 1);
628 
629     *uri = *old;
630     uri->str = str_dup(uri->str);
631     uri->path = str_dup(uri->path);
632     uri->host = str_dup(uri->host);
633 
634     return uri;
635 }
636 
637 /* Check that string, defined by begin and end pointers,
638  * has a specified prefix
639  */
640 static bool
http_uri_str_prefix(const char * beg,const char * end,const char * pfx)641 http_uri_str_prefix(const char *beg, const char *end, const char *pfx)
642 {
643     size_t len = end - beg;
644     size_t pfx_len = strlen(pfx);
645 
646     return len >= pfx_len && !memcmp(beg, pfx, pfx_len);
647 }
648 
649 /* Check that string, defined by begin and end pointers,
650  * equal to the specified pattern
651  */
652 static bool
http_uri_str_equal(const char * beg,const char * end,const char * pattern)653 http_uri_str_equal(const char *beg, const char *end, const char *pattern)
654 {
655     size_t len = end - beg;
656     size_t plen = strlen(pattern);
657 
658     return len == plen && !memcmp(beg, pattern, len);
659 }
660 
661 /* Find 1st occurrence of the character in the string,
662  * defined by begin and end pointers
663  */
664 static char *
http_uri_str_chr(const char * beg,const char * end,char c)665 http_uri_str_chr(const char *beg, const char *end, char c)
666 {
667     return memchr(beg, c, end - beg);
668 }
669 
670 /* Find last occurrence of the character in the string,
671  * defined by begin and end pointers
672  */
673 static char *
http_uri_str_rchr(const char * beg,const char * end,char c)674 http_uri_str_rchr(const char *beg, const char *end, char c)
675 {
676     return memrchr(beg, c, end - beg);
677 }
678 
679 /* Remove last path segment. Returns pointer to the
680  * path end
681  */
682 static char*
http_uri_remove_last_segment(char * path,char * end)683 http_uri_remove_last_segment (char *path, char *end)
684 {
685     char *s = http_uri_str_rchr(path, end, '/');
686     return s == NULL ? end : s;
687 }
688 
689 /* Remove path dot segments, per rfc3986, 5.2.4.
690  */
691 static char*
http_uri_remove_dot_segments(char * path,char * end)692 http_uri_remove_dot_segments (char *path, char *end)
693 {
694     char *input = path;
695     char *path_end = path;
696 
697     while (input != end) {
698         /* A.  If the input buffer begins with a prefix of "../" or "./",
699          *     then remove that prefix from the input buffer; otherwise,
700          */
701         if (http_uri_str_prefix(input, end, "../")) {
702             input += 3;
703         } else if (http_uri_str_prefix(input, end, "./")) {
704             input += 2;
705         /* B.  if the input buffer begins with a prefix of "/./" or "/.",
706          *     where "." is a complete path segment, then replace that
707          *     prefix with "/" in the input buffer; otherwise,
708          */
709         } else if (http_uri_str_prefix(input, end, "/./")) {
710             input += 2;
711         } else if (http_uri_str_equal(input, end, "/.")) {
712             input ++;
713             input[0] = '/';
714         /* C.  if the input buffer begins with a prefix of "/../" or "/..",
715          *     where ".." is a complete path segment, then replace that
716          *     prefix with "/" in the input buffer and remove the last
717          *     segment and its preceding "/" (if any) from the output
718          *     buffer; otherwise,
719          */
720         } else if (http_uri_str_prefix(input, end, "/../")) {
721             path_end = http_uri_remove_last_segment(path, path_end);
722             input += 3;
723         } else if (http_uri_str_equal(input, end, "/..")) {
724             path_end = http_uri_remove_last_segment(path, path_end);
725             input += 2;
726             input[0] = '/';
727         /* D.  if the input buffer consists only of "." or "..", then remove
728          *     that from the input buffer; otherwise,
729          */
730         } else if (http_uri_str_equal(input, end, ".") ||
731                    http_uri_str_equal(input, end, "..")) {
732             input = end;
733         /* E.  move the first path segment in the input buffer to the end of
734          *     the output buffer, including the initial "/" character (if
735          *     any) and any subsequent characters up to, but not including,
736          *     the next "/" character or the end of the input buffer.
737          */
738         } else {
739             char   *s = http_uri_str_chr(input + 1, end, '/');
740             size_t sz = s ? s - input : end - input;
741 
742             memmove(path_end, input, sz);
743             path_end += sz;
744             input += sz;
745         }
746     }
747 
748     return path_end;
749 }
750 
751 /* Create URI, relative to base URI. If `path_only' is
752  * true, scheme, host and port are taken from the
753  * base URI
754  */
755 http_uri*
http_uri_new_relative(const http_uri * base,const char * path,bool strip_fragment,bool path_only)756 http_uri_new_relative (const http_uri *base, const char *path,
757         bool strip_fragment, bool path_only)
758 {
759     char           *buf = alloca(strlen(base->str) + strlen(path) + 1);
760     char           *end = buf;
761     http_uri       ref;
762     const http_uri *uri;
763     http_uri_field field;
764     char           *path_beg;
765 
766     if (http_uri_parse(&ref, path) != NULL) {
767         return NULL;
768     }
769 
770     /* If the base URI uses the unix:// scheme, don't allow a relative URI to
771      * change the scheme or host.
772      */
773     if (base->scheme == HTTP_SCHEME_UNIX) {
774         path_only = true;
775     }
776 
777     /* Set schema, userinfo, host and port */
778     if (path_only || !http_uri_field_present(&ref, UF_SCHEMA)) {
779         end = http_uri_field_copy(base, UF_SCHEMA, end);
780     } else {
781         end = http_uri_field_copy(&ref, UF_SCHEMA, end);
782     }
783 
784     end = http_uri_field_append(http_uri_field_make("://"), end);
785 
786     if (path_only || !http_uri_field_present(&ref, UF_HOST)) {
787         uri = base;
788     } else {
789         uri = &ref;
790     }
791 
792     if (http_uri_field_present(uri, UF_USERINFO)) {
793         end = http_uri_field_copy(uri, UF_USERINFO, end);
794         end = http_uri_field_append(http_uri_field_make("@"), end);
795     }
796 
797     field = http_uri_field_get(uri, UF_HOST);
798     if (memchr(field.str, ':', field.len) != NULL) {
799         *end ++ = '[';
800         end = http_uri_field_append(field, end);
801         *end ++ = ']';
802     } else {
803         end = http_uri_field_append(field, end);
804     }
805 
806     if (http_uri_field_present(uri, UF_PORT)) {
807         end = http_uri_field_append(http_uri_field_make(":"), end);
808         end = http_uri_field_copy(uri, UF_PORT, end);
809     }
810 
811     /* Set path */
812     path_beg = end;
813     if (!http_uri_field_nonempty(&ref, UF_PATH)) {
814         end = http_uri_field_copy(base, UF_PATH, end);
815     } else {
816         if (http_uri_field_begin(&ref, UF_PATH) != '/') {
817             end = http_uri_field_copy_basepath(base, end);
818         }
819         end = http_uri_field_copy(&ref, UF_PATH, end);
820     }
821 
822     end = http_uri_remove_dot_segments(path_beg, end);
823 
824     /* Query and fragment */
825     if (http_uri_field_present(&ref, UF_QUERY)) {
826         end = http_uri_field_append(http_uri_field_make("?"), end);
827         end = http_uri_field_copy(&ref, UF_QUERY, end);
828     }
829 
830     if (!strip_fragment && http_uri_field_present(&ref, UF_FRAGMENT)) {
831         end = http_uri_field_append(http_uri_field_make("#"), end);
832         end = http_uri_field_copy(&ref, UF_FRAGMENT, end);
833     }
834 
835     *end = '\0';
836 
837     return http_uri_new(buf, false);
838 }
839 
840 /* Free the URI
841  */
842 #ifndef __clang_analyzer__
843 void
http_uri_free(http_uri * uri)844 http_uri_free (http_uri *uri)
845 {
846     if (uri != NULL) {
847         mem_free((char*) uri->str);
848         mem_free((char*) uri->path);
849         mem_free((char*) uri->host);
850         mem_free(uri);
851     }
852 }
853 #endif
854 
855 /* Get URI string
856  */
857 const char*
http_uri_str(http_uri * uri)858 http_uri_str (http_uri *uri)
859 {
860     return uri->str;
861 }
862 
863 /* Get URI's host address. If Host address is not literal, returns NULL
864  */
865 const struct sockaddr*
http_uri_addr(http_uri * uri)866 http_uri_addr (http_uri *uri)
867 {
868     if (uri->addr.sockaddr.sa_family == AF_UNSPEC) {
869         return NULL;
870     }
871 
872     return &uri->addr.sockaddr;
873 }
874 
875 /* Get URI path
876  */
877 const char*
http_uri_get_path(const http_uri * uri)878 http_uri_get_path (const http_uri *uri)
879 {
880     return uri->path;
881 }
882 
883 /* Set URI path
884  */
885 void
http_uri_set_path(http_uri * uri,const char * path)886 http_uri_set_path (http_uri *uri, const char *path)
887 {
888     http_uri_field_replace(uri, UF_PATH, path);
889 }
890 
891 /* Get URI host. It returns only host name, port number is
892  * not included.
893  *
894  * IPv6 literal addresses are returned in square brackets
895  * (i.e., [fe80::217:c8ff:fe7b:6a91%4])
896  *
897  * Note, the subsequent modifications of URI, such as http_uri_fix_host(),
898  * http_uri_fix_ipv6_zone() etc, may make the returned string invalid,
899  * so if you need to keep it for a long time, better make a copy
900  */
901 const char*
http_uri_get_host(const http_uri * uri)902 http_uri_get_host (const http_uri *uri)
903 {
904     return uri->host;
905 }
906 
907 /* Fix URI host: if `match` is NULL or uri's host matches `match`,
908  * replace uri's host and port with values taken from the base_uri
909  */
910 void
http_uri_fix_host(http_uri * uri,const http_uri * base_uri,const char * match)911 http_uri_fix_host (http_uri *uri, const http_uri *base_uri, const char *match)
912 {
913     http_uri_field schema, host, port;
914 
915     if (match != NULL) {
916         host = http_uri_field_get(uri, UF_HOST);
917         if (strncasecmp(host.str, match, host.len)) {
918             return;
919         }
920     }
921 
922     schema = http_uri_field_get(base_uri, UF_SCHEMA);
923     host = http_uri_field_get(base_uri, UF_HOST);
924     port = http_uri_field_get(base_uri, UF_PORT);
925 
926     http_uri_field_replace_len(uri, UF_SCHEMA, schema.str, schema.len);
927     http_uri_field_replace_len(uri, UF_HOST, host.str, host.len);
928     http_uri_field_replace_len(uri, UF_PORT, port.str, port.len);
929 }
930 
931 /* Fix IPv6 address zone suffix
932  */
933 void
http_uri_fix_ipv6_zone(http_uri * uri,int ifindex)934 http_uri_fix_ipv6_zone (http_uri *uri, int ifindex)
935 {
936     http_uri_field field;
937     char           *host;
938 
939     /* Check if we need to change something */
940     if (uri->addr.sockaddr.sa_family != AF_INET6) {
941         return; /* Not IPv6 */
942     }
943 
944     if (!ip_is_linklocal(AF_INET6, &uri->addr.in6.sin6_addr)) {
945         return; /* Not link-local */
946     }
947 
948     field = http_uri_field_get(uri, UF_HOST);
949     if (memchr(field.str, '%', field.len)) {
950         return; /* Already has zone suffix */
951     }
952 
953     /* Obtain writable copy of host name */
954     host = alloca(field.len + 64);
955     memcpy(host, field.str, field.len);
956 
957     /* Append zone suffix */
958     sprintf(host + field.len, "%%25%d", ifindex);
959 
960     /* Update URL's host */
961     http_uri_field_replace(uri, UF_HOST, host);
962     uri->addr.in6.sin6_scope_id = ifindex;
963 }
964 
965 /* Strip zone suffix from literal IPv6 host address
966  *
967  * If address is not IPv6 or doesn't have zone suffix, it is
968  * not changed
969  */
970 void
http_uri_strip_zone_suffux(http_uri * uri)971 http_uri_strip_zone_suffux (http_uri *uri)
972 {
973     http_uri_field field;
974     const char     *suffix;
975     size_t         len;
976     char           *host;
977 
978     /* Check if we need to change something */
979     if (uri->addr.sockaddr.sa_family != AF_INET6) {
980         return; /* Not IPv6 */
981     }
982 
983     field = http_uri_field_get(uri, UF_HOST);
984     suffix = memchr(field.str, '%', field.len);
985     if (suffix == NULL) {
986         return; /* No zone suffix */
987     }
988 
989     len = suffix - field.str;
990 
991     /* Update host */
992     host = alloca(len + 1);
993     memcpy(host, field.str, len);
994     host[len] = '\0';
995 
996     http_uri_field_replace(uri, UF_HOST, host);
997     uri->addr.in6.sin6_scope_id = 0;
998 }
999 
1000 /* Make sure URI's path ends with the slash character
1001  */
1002 void
http_uri_fix_end_slash(http_uri * uri)1003 http_uri_fix_end_slash (http_uri *uri)
1004 {
1005     const char *path = http_uri_get_path(uri);
1006     if (!str_has_suffix(path, "/")) {
1007         size_t len = strlen(path);
1008         char *path2 = alloca(len + 2);
1009         memcpy(path2, path, len);
1010         path2[len] = '/';
1011         path2[len+1] = '\0';
1012         http_uri_set_path(uri, path2);
1013     }
1014 }
1015 
1016 /* Check if 2 URIs are equal
1017  */
1018 bool
http_uri_equal(const http_uri * uri1,const http_uri * uri2)1019 http_uri_equal (const http_uri *uri1, const http_uri *uri2)
1020 {
1021     return uri1->scheme == uri2->scheme &&
1022            http_uri_field_equal(uri1, uri2, UF_HOST, true) &&
1023            http_uri_field_equal(uri1, uri2, UF_PORT, true) &&
1024            http_uri_field_equal(uri1, uri2, UF_PATH, false) &&
1025            http_uri_field_equal(uri1, uri2, UF_QUERY, false) &&
1026            http_uri_field_equal(uri1, uri2, UF_FRAGMENT, false) &&
1027            http_uri_field_equal(uri1, uri2, UF_USERINFO, false);
1028 }
1029 
1030 /******************** HTTP header ********************/
1031 /* http_hdr represents HTTP header
1032  */
1033 typedef struct {
1034     ll_head fields;          /* List of http_hdr_field */
1035 } http_hdr;
1036 
1037 /* http_hdr_field represents a single HTTP header field
1038  */
1039 typedef struct {
1040     char    *name;           /* Header name */
1041     char    *value;          /* Header value, may be NULL */
1042     ll_node chain;           /* In http_hdr::fields */
1043 } http_hdr_field;
1044 
1045 /* Create http_hdr_field. Name can be NULL
1046  */
1047 static http_hdr_field*
http_hdr_field_new(const char * name)1048 http_hdr_field_new (const char *name)
1049 {
1050     http_hdr_field *field = mem_new(http_hdr_field, 1);
1051     field->name = name ? str_dup(name) : str_new();
1052     return field;
1053 }
1054 
1055 /* Destroy http_hdr_field
1056  */
1057 static void
http_hdr_field_free(http_hdr_field * field)1058 http_hdr_field_free (http_hdr_field *field)
1059 {
1060     mem_free(field->name);
1061     mem_free(field->value);
1062     mem_free(field);
1063 }
1064 
1065 /* Initialize http_hdr in place
1066  */
1067 static void
http_hdr_init(http_hdr * hdr)1068 http_hdr_init (http_hdr *hdr)
1069 {
1070     ll_init(&hdr->fields);
1071 }
1072 
1073 /* Cleanup http_hdr in place
1074  */
1075 static void
http_hdr_cleanup(http_hdr * hdr)1076 http_hdr_cleanup (http_hdr *hdr)
1077 {
1078     ll_node *node;
1079 
1080     while ((node = ll_pop_beg(&hdr->fields)) != NULL) {
1081         http_hdr_field *field = OUTER_STRUCT(node, http_hdr_field, chain);
1082         http_hdr_field_free(field);
1083     }
1084 }
1085 
1086 /* Write header to string buffer in wire format
1087  */
1088 static char*
http_hdr_write(const http_hdr * hdr,char * out)1089 http_hdr_write (const http_hdr *hdr, char *out)
1090 {
1091     ll_node *node;
1092 
1093     for (LL_FOR_EACH(node, &hdr->fields)) {
1094         http_hdr_field *field = OUTER_STRUCT(node, http_hdr_field, chain);
1095         out = str_append(out, field->name);
1096         out = str_append(out, ": ");
1097         out = str_append(out, field->value);
1098         out = str_append(out, "\r\n");
1099     }
1100 
1101     return str_append(out, "\r\n");
1102 }
1103 
1104 /* Lookup field in the header
1105  */
1106 static http_hdr_field*
http_hdr_lookup(const http_hdr * hdr,const char * name)1107 http_hdr_lookup (const http_hdr *hdr, const char *name)
1108 {
1109     ll_node *node;
1110 
1111     for (LL_FOR_EACH(node, &hdr->fields)) {
1112         http_hdr_field *field = OUTER_STRUCT(node, http_hdr_field, chain);
1113         if (!strcasecmp(field->name, name)) {
1114             return field;
1115         }
1116     }
1117 
1118     return NULL;
1119 }
1120 
1121 /* Get header field
1122  */
1123 static const char*
http_hdr_get(const http_hdr * hdr,const char * name)1124 http_hdr_get (const http_hdr *hdr, const char *name)
1125 {
1126     http_hdr_field *field = http_hdr_lookup(hdr, name);
1127 
1128     if (field == NULL) {
1129         return NULL;
1130     }
1131 
1132     if (field->value == NULL) {
1133         return "";
1134     }
1135 
1136     return field->value;
1137 }
1138 
1139 /* Set header field
1140  */
1141 static void
http_hdr_set(http_hdr * hdr,const char * name,const char * value)1142 http_hdr_set (http_hdr *hdr, const char *name, const char *value)
1143 {
1144     http_hdr_field *field = http_hdr_lookup(hdr, name);
1145 
1146     if (field == NULL) {
1147         field = http_hdr_field_new(name);
1148         ll_push_end(&hdr->fields, &field->chain);
1149     }
1150 
1151     if (field->value == NULL) {
1152         field->value = str_dup(value);
1153     } else {
1154         field->value = str_assign(field->value, value);
1155     }
1156 }
1157 
1158 /* Del header field
1159  */
1160 static void
http_hdr_del(http_hdr * hdr,const char * name)1161 http_hdr_del (http_hdr *hdr, const char *name)
1162 {
1163     http_hdr_field *field = http_hdr_lookup(hdr, name);
1164 
1165     if (field != NULL) {
1166         ll_del(&field->chain);
1167         http_hdr_field_free(field);
1168     }
1169 }
1170 
1171 /* Handle HTTP parser on_header_field callback
1172  * parser->data must point to the header being parsed
1173  */
1174 static int
http_hdr_on_header_field(http_parser * parser,const char * data,size_t size)1175 http_hdr_on_header_field (http_parser *parser,
1176         const char *data, size_t size)
1177 {
1178     http_hdr       *hdr = parser->data;
1179     ll_node        *node;
1180     http_hdr_field *field = NULL;
1181 
1182     /* Get last field */
1183     node = ll_last(&hdr->fields);
1184     if (node != NULL) {
1185         field = OUTER_STRUCT(node, http_hdr_field, chain);
1186     }
1187 
1188     /* If there is no last field, or last field already
1189      * has value, create a new field
1190      */
1191     if (field == NULL || field->value != NULL) {
1192         field = http_hdr_field_new(NULL);
1193         ll_push_end(&hdr->fields, &field->chain);
1194     }
1195 
1196     /* Append data to the field name */
1197     field->name = str_append_mem(field->name, data, size);
1198 
1199     return 0;
1200 }
1201 
1202 /* Handle HTTP parser on_header_value callback
1203  * parser->data must point to the header being parsed
1204  */
1205 static int
http_hdr_on_header_value(http_parser * parser,const char * data,size_t size)1206 http_hdr_on_header_value (http_parser *parser,
1207         const char *data, size_t size)
1208 {
1209     http_hdr       *hdr = parser->data;
1210     ll_node        *node;
1211     http_hdr_field *field = NULL;
1212 
1213     /* Get last field */
1214     node = ll_last(&hdr->fields);
1215     if (node != NULL) {
1216         field = OUTER_STRUCT(node, http_hdr_field, chain);
1217     }
1218 
1219     /* If there is no last field, just ignore the data.
1220      * Note, it actually should not happen
1221      */
1222     if (field == NULL) {
1223         return 0;
1224     }
1225 
1226     /* Append data to field value */
1227     if (field->value == NULL) {
1228         field->value = str_new();
1229     }
1230 
1231     field->value = str_append_mem(field->value, data, size);
1232 
1233     return 0;
1234 }
1235 
1236 /* Handle HTTP parser on_headers_complete callback
1237  * This is used http_hdr_parse only and returns 1 to
1238  * tell parser to don't attempt to parse message body
1239  */
1240 static int
http_hdr_on_headers_complete(http_parser * parser)1241 http_hdr_on_headers_complete (http_parser *parser)
1242 {
1243     (void) parser;
1244     return 1;
1245 }
1246 
1247 
1248 /* Parse http_hdr from memory buffer
1249  *
1250  * If `skip_line' is true, first line is skipped, which
1251  * is useful if first line contains HTTP request/status
1252  * or a multipart boundary
1253  */
1254 static error
http_hdr_parse(http_hdr * hdr,const char * data,size_t size,bool skip_line)1255 http_hdr_parse (http_hdr *hdr, const char *data, size_t size, bool skip_line)
1256 {
1257     static http_parser_settings callbacks = {
1258         .on_header_field     = http_hdr_on_header_field,
1259         .on_header_value     = http_hdr_on_header_value,
1260         .on_headers_complete = http_hdr_on_headers_complete
1261     };
1262     http_parser parser = {0};
1263     static char prefix[] = "HTTP/1.1 200 OK\r\n";
1264 
1265     /* Skip first line, if requested */
1266     if (skip_line) {
1267         const char *s = memchr(data, '\n', size);
1268         if (s) {
1269             size_t skip = (s - data) + 1;
1270             data += skip;
1271             size -= skip;
1272         }
1273     }
1274 
1275     /* Initialize HTTP parser */
1276     http_parser_init(&parser, HTTP_RESPONSE);
1277     parser.data = hdr;
1278 
1279     /* Note, http_parser unable to parse bare HTTP
1280      * header, without request or status line, so
1281      * we insert fake status line to make it happy
1282      */
1283     http_parser_execute(&parser, &callbacks, prefix, sizeof(prefix) - 1);
1284     http_parser_execute(&parser, &callbacks, data, size);
1285 
1286     if (parser.http_errno != HPE_OK) {
1287         return ERROR(http_errno_description(parser.http_errno));
1288     }
1289 
1290     return NULL;
1291 }
1292 
1293 /* Check if character is special, for http_hdr_params_parse
1294  */
1295 static bool
http_hdr_params_chr_isspec(char c)1296 http_hdr_params_chr_isspec (char c)
1297 {
1298     if (0x20 < c && c < 0x7f) {
1299         switch (c) {
1300         case '(': case ')': case '<': case '>':
1301         case '@': case ',': case ';': case ':':
1302         case '\\': case '\"':
1303         case '/': case '[': case ']': case '?':
1304         case '=':
1305             return true;
1306 
1307         default:
1308             return safe_isspace(c);
1309         }
1310     }
1311 
1312     return true;
1313 }
1314 
1315 /* Check if character is space, for http_hdr_params_parse
1316  */
1317 static bool
http_hdr_params_chr_isspace(char c)1318 http_hdr_params_chr_isspace (char c)
1319 {
1320     switch ((unsigned char) c) {
1321     case '\t': case '\n': case '\v': case '\f':
1322     case '\r': case ' ': case 0x85: case 0xA0:
1323         return true;
1324     }
1325 
1326     return false;
1327 }
1328 
1329 /* Parse HTTP header field with parameters.
1330  * Result is saved into `params' as collection of name/value fields
1331  */
1332 static error
http_hdr_params_parse(http_hdr * params,const char * name,const char * in)1333 http_hdr_params_parse (http_hdr *params, const char *name, const char *in)
1334 {
1335     enum {
1336     /*  params ::= param [';' params]
1337      *  param  ::= SP1 NAME SP2 '=' SP3 value SP4
1338      *  value  ::= TOKEN | STRING
1339      */
1340         SP1, NAME, SP2, EQ, SP3, VALUE, TOKEN, STRING, STRING_BSLASH, SP4, END
1341     } state = SP1;
1342     char           c;
1343     http_hdr_field *field = NULL;
1344 
1345     /* Parameters begin after ';' */
1346     in = strchr(in, ';');
1347     if (in == NULL) {
1348         return NULL;
1349     }
1350 
1351     in ++;
1352 
1353     while ((c = *in) != '\0') {
1354         switch (state) {
1355         case SP1: case SP2: case SP3: case SP4:
1356             if (!http_hdr_params_chr_isspace(c)) {
1357                 state ++;
1358                 continue;
1359             }
1360             break;
1361 
1362         case NAME:
1363             if (!http_hdr_params_chr_isspec(c)) {
1364                 if (field == NULL) {
1365                     field = http_hdr_field_new(NULL);
1366                     field->value = str_new();
1367                     ll_push_end(&params->fields, &field->chain);
1368                 }
1369                 field->name = str_append_c(field->name, c);
1370             } else if (c == ';') {
1371                 state = SP1;
1372                 field = NULL;
1373             } else {
1374                 state = SP2;
1375                 continue;
1376             }
1377             break;
1378 
1379         case EQ:
1380             if (c == '=') {
1381                 state = SP3;
1382             } else if (c == ';') {
1383                 state = SP1;
1384                 field = NULL;
1385             } else {
1386                 return eloop_eprintf(
1387                         "http %s: expected '=' or ';', got: %s", name, in);
1388             }
1389             break;
1390 
1391         case VALUE:
1392             if (c == '"') {
1393                 state = STRING;
1394             } else {
1395                 state = TOKEN;
1396                 continue;
1397             }
1398             break;
1399 
1400         case STRING:
1401             if (c == '\\') {
1402                 state = STRING_BSLASH;
1403             } else if (c == '"') {
1404                 state = SP4;
1405             } else {
1406                 field->value = str_append_c(field->value, c);
1407             }
1408             break;
1409 
1410         case STRING_BSLASH:
1411             field->value = str_append_c(field->value, c);
1412             state = STRING;
1413             break;
1414 
1415         case TOKEN:
1416             if (c != ';' && c != '"' && !safe_isspace(c)) {
1417                 field->value = str_append_c(field->value, c);
1418             } else {
1419                 state = SP4;
1420                 continue;
1421             }
1422             break;
1423 
1424         case END:
1425             if (c != ';') {
1426                 return eloop_eprintf(
1427                         "http %s: expected ';', got: %s", name, in);
1428             }
1429 
1430             state = SP1;
1431             field = NULL;
1432             break;
1433         }
1434 
1435         in ++;
1436     }
1437 
1438     if (state == STRING || state == STRING_BSLASH) {
1439         return eloop_eprintf( "http %s: unterminated quoted string");
1440     }
1441 
1442     return NULL;
1443 }
1444 
1445 /* Call callback for each header field
1446  */
1447 static void
hdr_for_each(const http_hdr * hdr,void (* callback)(const char * name,const char * value,void * ptr),void * ptr)1448 hdr_for_each (const http_hdr *hdr,
1449         void (*callback)(const char *name, const char *value, void *ptr),
1450         void *ptr)
1451 {
1452     ll_node *node;
1453 
1454     for (LL_FOR_EACH(node, &hdr->fields)) {
1455         http_hdr_field *field = OUTER_STRUCT(node, http_hdr_field, chain);
1456 
1457         if (field->value != NULL) {
1458             callback(field->name, field->value, ptr);
1459         }
1460     }
1461 }
1462 
1463 /******************** HTTP multipart ********************/
1464 /* http_multipart represents a decoded multipart message
1465  */
1466 struct http_multipart {
1467     int           count;    /* Count of bodies */
1468     http_data     *data;    /* Response data */
1469     http_data     **bodies; /* Multipart bodies, var-size */
1470 };
1471 
1472 /* Add multipart body
1473  */
1474 static void
http_multipart_add_body(http_multipart * mp,http_data * body)1475 http_multipart_add_body (http_multipart *mp, http_data *body)
1476 {
1477     mp->bodies = mem_resize(mp->bodies, mp->count + 1, 0);
1478     mp->bodies[mp->count ++] = body;
1479 }
1480 
1481 /* Find boundary within the multipart message data
1482  */
1483 static const char*
http_multipart_find_boundary(const char * boundary,size_t boundary_len,const char * data,size_t size)1484 http_multipart_find_boundary (const char *boundary, size_t boundary_len,
1485         const char *data, size_t size) {
1486 
1487     /* Note, per RFC 2046, "the boundary delimiter MUST occur at the beginning
1488      * of a line, i.e., following a CRLF, and the initial CRLF is considered to
1489      * be attached to the boundary delimiter line rather than part of the
1490      * preceding part".
1491      *
1492      * However, Xerox WorkCentre 3025 violates this requirement, and
1493      * puts boundary delimiter without preceding CRLF, so we must relax
1494      * out expectations
1495      */
1496     return memmem(data, size, boundary, boundary_len);
1497 }
1498 
1499 /* Adjust part of multipart message:
1500  *   1) skip header
1501  *   2) fetch content type
1502  */
1503 static error
http_multipart_adjust_part(http_data * part)1504 http_multipart_adjust_part (http_data *part)
1505 {
1506     const char *split;
1507     http_hdr   hdr;
1508     size_t     hdr_len;
1509     error      err;
1510 
1511     /* Locate end of headers */
1512     split = memmem(part->bytes, part->size, "\r\n\r\n", 4);
1513     if (split == NULL) {
1514         return ERROR("http multipart: can't locate end of part headers");
1515     }
1516 
1517     /* Parse headers and obtain content-type */
1518     http_hdr_init(&hdr);
1519     hdr_len = 4 + split - (char*) part->bytes;
1520     err = http_hdr_parse(&hdr, part->bytes, hdr_len - 2, true);
1521 
1522     if (err == NULL) {
1523         const char *ct = http_hdr_get(&hdr, "Content-Type");
1524         http_data_set_content_type(part, ct);
1525     }
1526 
1527     http_hdr_cleanup(&hdr);
1528     if (err != NULL) {
1529         return eloop_eprintf("http multipart: %s", ESTRING(err));
1530     }
1531 
1532     /* Cut of header */
1533     split += 4;
1534     part->size -= (split - (char*) part->bytes);
1535     part->bytes = split;
1536 
1537     /* Strip CR/LF preceding next boundary, if any */
1538     if (split[part->size - 2] == '\r' && split[part->size - 1] == '\n') {
1539         part->size -= 2;
1540     }
1541 
1542     return NULL;
1543 }
1544 
1545 /* Free http_multipart
1546  */
1547 static void
http_multipart_free(http_multipart * mp)1548 http_multipart_free (http_multipart *mp)
1549 {
1550     int i;
1551 
1552     for (i = 0; i < mp->count; i ++) {
1553         http_data_unref(mp->bodies[i]);
1554     }
1555 
1556     mem_free(mp);
1557 }
1558 
1559 /* Parse MIME multipart message body
1560  * Saves result into `out'. Result may be NULL, if no multipart
1561  */
1562 static error
http_multipart_parse(http_multipart ** out,log_ctx * log,http_data * data,const char * content_type)1563 http_multipart_parse (http_multipart **out, log_ctx *log,
1564         http_data *data, const char *content_type)
1565 {
1566     http_multipart *mp;
1567     http_hdr       params;
1568     const char     *boundary;
1569     size_t         boundary_len = 0;
1570     const char     *data_beg, *data_end, *data_prev;
1571     error          err;
1572     ll_node        *node;
1573 
1574     /* Check MIME type */
1575     *out = NULL;
1576     if (strncmp(data->content_type, "multipart/", 10)) {
1577         return NULL;
1578     }
1579 
1580     /* Obtain boundary */
1581     http_hdr_init(&params);
1582     err = http_hdr_params_parse(&params, "Content-Type", content_type);
1583     if (err != NULL) {
1584         http_hdr_cleanup(&params);
1585         return err;
1586     }
1587 
1588     log_debug(log, "http multipart parameters:");
1589     for (LL_FOR_EACH(node, &params.fields)) {
1590         http_hdr_field *field = OUTER_STRUCT(node, http_hdr_field, chain);
1591         log_debug(log, "  %s=\"%s\"", field->name, field->value);
1592     }
1593 
1594     boundary = http_hdr_get(&params, "boundary");
1595     if (boundary) {
1596         char *s;
1597 
1598         boundary_len = strlen(boundary) + 2;
1599         s = alloca(boundary_len + 1);
1600 
1601         s[0] = '-';
1602         s[1] = '-';
1603         strcpy(s + 2, boundary);
1604         boundary = s;
1605     }
1606     http_hdr_cleanup(&params);
1607 
1608     if (!boundary) {
1609         return ERROR("http multipart: missed boundary parameter");
1610     }
1611 
1612     /* Create http_multipart structure */
1613     mp = mem_new(http_multipart, 1);
1614     mp->data = http_data_ref(data);
1615 
1616     /* Split data into parts */
1617     data_beg = data->bytes;
1618     data_end = data_beg + data->size;
1619     data_prev = NULL;
1620 
1621     while (data_beg != data_end) {
1622         const char *part = http_multipart_find_boundary(boundary, boundary_len,
1623             data_beg, data_end - data_beg);
1624         const char *next = data_end;
1625 
1626         if (part != NULL) {
1627             if (data_prev != NULL) {
1628                 http_data *body = http_data_new(data,
1629                         data_prev, part - data_prev);
1630                 http_multipart_add_body(mp, body);
1631 
1632                 err = http_multipart_adjust_part(body);
1633                 if (err != NULL) {
1634                     http_multipart_free(mp);
1635                     return err;
1636                 }
1637             }
1638 
1639             data_prev = part;
1640 
1641             const char *tail = part + boundary_len;
1642             if (data_end - tail >= 2 && tail[0] == '\r' && tail[1] == '\n') {
1643                 next = tail + 2;
1644             }
1645         }
1646 
1647         data_beg = next;
1648     }
1649 
1650     if (mp->count == 0) {
1651         http_multipart_free(mp);
1652         return ERROR("http multipart: no parts found");
1653     }
1654 
1655     if (data_beg != data_end) {
1656         log_debug(log,
1657             "http multipart: found %d bytes of garbage at the end of message",
1658             (int)(data_end - data_beg));
1659     }
1660 
1661     *out = mp;
1662     return NULL;
1663 }
1664 
1665 /******************** HTTP data ********************/
1666 /* http_data + SoupBuffer
1667  */
1668 typedef struct {
1669     http_data              data;    /* HTTP data */
1670     volatile unsigned int  refcnt;  /* Reference counter */
1671     http_data              *parent; /* Parent data buffer */
1672 } http_data_ex;
1673 
1674 
1675 /* Create new http_data
1676  *
1677  * If parent != NULL, supplied bytes buffer must be owned by
1678  * parent. Otherwise, newly created http_data takes ownership
1679  * on the supplied data buffer
1680  */
1681 static http_data*
http_data_new(http_data * parent,const char * bytes,size_t size)1682 http_data_new(http_data *parent, const char *bytes, size_t size)
1683 {
1684     http_data_ex *data_ex = mem_new(http_data_ex, 1);
1685 
1686     if (parent != NULL) {
1687         log_assert(NULL, bytes >= (char*) parent->bytes);
1688         log_assert(NULL,
1689             (bytes + size) <= ((char*) parent->bytes + parent->size));
1690     }
1691 
1692     data_ex->data.content_type = str_new();
1693     data_ex->data.bytes = bytes;
1694     data_ex->data.size = size;
1695 
1696     data_ex->refcnt = 1;
1697     data_ex->parent = parent ? http_data_ref(parent) : NULL;
1698 
1699     return &data_ex->data;
1700 }
1701 
1702 /* Set Content-type
1703  */
1704 static void
http_data_set_content_type(http_data * data,const char * content_type)1705 http_data_set_content_type (http_data *data, const char *content_type)
1706 {
1707     mem_free((char*) data->content_type);
1708 
1709     if (content_type == NULL) {
1710         content_type = str_dup("text/plain");
1711     } else {
1712         char *s;
1713 
1714         content_type = str_dup_tolower(content_type);
1715         s = strchr(content_type, ';');
1716         if (s != NULL) {
1717             *s = '\0';
1718         }
1719     }
1720 
1721     data->content_type = content_type;
1722 }
1723 
1724 /* Dummy http_data in case no data is present
1725  */
1726 static http_data
1727 http_data_empty = {
1728     .content_type = "",
1729     .bytes = "",
1730     .size = 0
1731 };
1732 
1733 
1734 /* Ref http_data
1735  */
1736 http_data*
http_data_ref(http_data * data)1737 http_data_ref (http_data *data)
1738 {
1739     if (data != NULL && data != &http_data_empty) {
1740         http_data_ex *data_ex = OUTER_STRUCT(data, http_data_ex, data);
1741         __sync_fetch_and_add(&data_ex->refcnt, 1);
1742     }
1743     return data;
1744 }
1745 
1746 /* Unref http_data
1747  */
1748 void
http_data_unref(http_data * data)1749 http_data_unref (http_data *data)
1750 {
1751     if (data != NULL && data != &http_data_empty) {
1752         http_data_ex *data_ex = OUTER_STRUCT(data, http_data_ex, data);
1753 
1754         if (__sync_fetch_and_sub(&data_ex->refcnt, 1) == 1) {
1755             if (data_ex->parent != NULL) {
1756                 http_data_unref(data_ex->parent);
1757             } else {
1758                 mem_free((void*) data_ex->data.bytes);
1759             }
1760 
1761             mem_free((char*) data_ex->data.content_type);
1762             mem_free(data_ex);
1763         }
1764     }
1765 }
1766 
1767 /* Append bytes to data. http_data must be owner of its
1768  * own buffer, i.e. it must have no parent
1769  *
1770  * Returns true on success, false on OOM
1771  */
1772 static bool
http_data_append(http_data * data,const char * bytes,size_t size)1773 http_data_append (http_data *data, const char *bytes, size_t size)
1774 {
1775     http_data_ex *data_ex = OUTER_STRUCT(data, http_data_ex, data);
1776     void         *p;
1777 
1778     log_assert(NULL, data_ex->parent == NULL);
1779 
1780     p = mem_try_resize((char*) data->bytes, data->size + size, 0);
1781     if (p == NULL) {
1782         return false;
1783     }
1784 
1785     data->bytes = p;
1786     memcpy((char*) data->bytes + data->size, bytes, size);
1787     data->size += size;
1788 
1789     return true;
1790 }
1791 
1792 /******************** HTTP data queue ********************/
1793 /* http_data_queue represents a queue of http_data items
1794  */
1795 struct http_data_queue {
1796     http_data **items; /* Array of http_data items */
1797 };
1798 
1799 /* Create new http_data_queue
1800  */
1801 http_data_queue*
http_data_queue_new(void)1802 http_data_queue_new (void)
1803 {
1804     http_data_queue *queue = mem_new(http_data_queue, 1);
1805     queue->items = ptr_array_new(http_data*);
1806     return queue;
1807 }
1808 
1809 /* Destroy http_data_queue
1810  */
1811 void
http_data_queue_free(http_data_queue * queue)1812 http_data_queue_free (http_data_queue *queue)
1813 {
1814     http_data_queue_purge(queue);
1815     mem_free(queue->items);
1816     mem_free(queue);
1817 }
1818 
1819 /* Push item into the http_data_queue.
1820  */
1821 void
http_data_queue_push(http_data_queue * queue,http_data * data)1822 http_data_queue_push (http_data_queue *queue, http_data *data)
1823 {
1824     queue->items = ptr_array_append(queue->items, data);
1825 }
1826 
1827 /* Pull an item from the http_data_queue. Returns NULL if queue is empty
1828  */
1829 http_data*
http_data_queue_pull(http_data_queue * queue)1830 http_data_queue_pull (http_data_queue *queue)
1831 {
1832     return ptr_array_del(queue->items, 0);
1833 }
1834 
1835 /* Get queue length
1836  */
1837 int
http_data_queue_len(const http_data_queue * queue)1838 http_data_queue_len (const http_data_queue *queue)
1839 {
1840     return (int) mem_len(queue->items);
1841 }
1842 
1843 /* Purge the queue
1844  */
1845 void
http_data_queue_purge(http_data_queue * queue)1846 http_data_queue_purge (http_data_queue *queue)
1847 {
1848     http_data *data;
1849 
1850     while ((data = http_data_queue_pull(queue)) != NULL) {
1851         http_data_unref(data);
1852     }
1853 }
1854 
1855 /******************** HTTP client ********************/
1856 /* Type http_client represents HTTP client instance
1857  */
1858 struct http_client {
1859     void       *ptr;       /* Callback's user data */
1860     log_ctx    *log;       /* Logging context */
1861     ll_head    pending;    /* Pending queries */
1862     void       (*onerror)( /* Callback to be called on transport error */
1863             void *ptr, error err);
1864 };
1865 
1866 /* Create new http_client
1867  */
1868 http_client*
http_client_new(log_ctx * log,void * ptr)1869 http_client_new (log_ctx *log, void *ptr)
1870 {
1871     http_client *client = mem_new(http_client, 1);
1872 
1873     client->ptr = ptr;
1874     client->log = log;
1875     ll_init(&client->pending);
1876 
1877     return client;
1878 }
1879 
1880 /* Destroy http_client
1881  */
1882 void
http_client_free(http_client * client)1883 http_client_free (http_client *client)
1884 {
1885     log_assert(client->log, ll_empty(&client->pending));
1886 
1887     mem_free(client);
1888 }
1889 
1890 /* Set on-error callback. If this callback is not NULL,
1891  * in a case of transport error it will be called instead
1892  * of the http_query callback
1893  */
1894 void
http_client_onerror(http_client * client,void (* onerror)(void * ptr,error err))1895 http_client_onerror (http_client *client,
1896         void (*onerror)(void *ptr, error err))
1897 {
1898     client->onerror = onerror;
1899 }
1900 
1901 /* Cancel all pending queries, if any
1902  */
1903 void
http_client_cancel(http_client * client)1904 http_client_cancel (http_client *client)
1905 {
1906     ll_node *node;
1907 
1908     while ((node = ll_pop_beg(&client->pending)) != NULL) {
1909          http_query *q;
1910          q = http_query_by_ll_node(node);
1911          http_query_cancel(q);
1912     }
1913 }
1914 
1915 /* Set timeout of all pending queries, if any. Timeout is in milliseconds
1916  */
1917 void
http_client_timeout(http_client * client,int timeout)1918 http_client_timeout (http_client *client, int timeout)
1919 {
1920     ll_node *node;
1921 
1922     while ((node = ll_pop_beg(&client->pending)) != NULL) {
1923          http_query *q;
1924          q = http_query_by_ll_node(node);
1925          http_query_timeout(q, timeout);
1926     }
1927 }
1928 
1929 /* Cancel all pending queries with matching address family and uintptr
1930  */
1931 void
http_client_cancel_af_uintptr(http_client * client,int af,uintptr_t uintptr)1932 http_client_cancel_af_uintptr (http_client *client, int af, uintptr_t uintptr)
1933 {
1934     ll_head leftover;
1935     ll_node *node;
1936 
1937     ll_init(&leftover);
1938 
1939     while ((node = ll_pop_beg(&client->pending)) != NULL) {
1940         http_query *q = http_query_by_ll_node(node);
1941 
1942         if (uintptr == http_query_get_uintptr(q) &&
1943             af == http_uri_af(http_query_uri(q))) {
1944             http_query_cancel(q);
1945         } else {
1946             ll_push_end(&leftover, node);
1947         }
1948     }
1949 
1950     ll_cat(&client->pending, &leftover);
1951 }
1952 
1953 /* Check if client has pending queries
1954  */
1955 bool
http_client_has_pending(const http_client * client)1956 http_client_has_pending (const http_client *client)
1957 {
1958     return !ll_empty(&client->pending);
1959 }
1960 
1961 /******************** HTTP request handling ********************/
1962 /* Type http_query represents HTTP query (both request and response)
1963  */
1964 struct http_query {
1965     /* URI and method */
1966     http_uri          *uri;                     /* Query URI */
1967     http_uri          *real_uri;                /* Real URI, may be NULL */
1968     const char        *method;                  /* Request method */
1969 
1970     /* Request and response headers */
1971     http_hdr          request_header;           /* Request header */
1972     http_hdr          response_header;          /* Response header */
1973     bool              host_inserted;            /* Host: auto-inserted */
1974     bool              force_port;               /* Host: always includes port */
1975 
1976     /* HTTP redirects */
1977     int               redirect_count;           /* Count of redirects */
1978     http_uri          *orig_uri;                /* Original URI */
1979     const char        *orig_method;             /* Original method */
1980 
1981     /* Query timeout */
1982     eloop_timer       *timeout_timer;           /* Timeout timer */
1983     int               timeout_value;            /* In milliseconds */
1984 
1985     /* Low-level I/O */
1986     bool              submitted;                /* http_query_submit() called */
1987     uint64_t          eloop_callid;             /* For eloop_call_cancel */
1988     error             err;                      /* Transport error */
1989     struct addrinfo   *addrs;                   /* Addresses to connect to */
1990     bool              addrs_freeaddrinfo;       /* Use freeaddrinfo(addrs) */
1991     struct addrinfo   *addr_next;               /* Next address to try */
1992     int               sock;                     /* HTTP socket */
1993     gnutls_session_t  tls;                      /* NULL if not TLS */
1994     bool              handshake;                /* TLS handshake in progress */
1995     bool              sending;                  /* We are now sending */
1996     eloop_fdpoll      *fdpoll;                  /* Polls q->sock */
1997     ip_straddr        straddr;                  /* q->sock peer addr, for log */
1998 
1999     char              *rq_buf;                  /* Formatted request */
2000     size_t            rq_off;                   /* send() offset in request */
2001 
2002     /* HTTP parser */
2003     http_parser       http_parser;              /* HTTP parser structure */
2004     bool              http_parser_done;         /* Message parsing done */
2005 
2006     /* Data handling */
2007     http_data         *request_data;            /* NULL if none */
2008     http_data         *response_data;           /* NULL if none */
2009     http_multipart    *response_multipart;      /* NULL if not multipart */
2010 
2011     /* Callbacks and context */
2012     timestamp         timestamp;                /* Submission timestamp */
2013     uintptr_t         uintptr;                  /* User-defined parameter */
2014     void              (*onerror) (void *ptr,    /* On-error callback */
2015                                 error err);
2016     void              (*onredir) (void *ptr,    /* On-redirect callback */
2017                                 http_uri *uri,
2018                                 const http_uri *orig_uri);
2019     void              (*onrxhdr) (void *ptr,    /* On-header reception */
2020                                 http_query *q);
2021     void              (*callback) (void *ptr,   /* Completion callback */
2022                                 http_query *q);
2023 
2024     /* Linkage to http_client */
2025     http_client       *client;                  /* Client that owns the query */
2026     bool              queued;                   /* Query is queued */
2027     ll_node           chain;                    /* In http_client::pending or
2028                                                    http_client::queued */
2029 };
2030 
2031 /* Get http_query* by pointer to its http_query::chain */
2032 static http_query*
http_query_by_ll_node(ll_node * node)2033 http_query_by_ll_node (ll_node *node)
2034 {
2035      return OUTER_STRUCT(node, http_query, chain);
2036 }
2037 
2038 /* Reset query into the state it had before http_query_submit()
2039  */
2040 static void
http_query_reset(http_query * q)2041 http_query_reset (http_query *q)
2042 {
2043     if (q->host_inserted) {
2044         http_hdr_del(&q->request_header, "Host");
2045         q->host_inserted = false;
2046     }
2047 
2048     http_hdr_cleanup(&q->response_header);
2049 
2050     if (q->addrs != NULL) {
2051         if (q->addrs_freeaddrinfo) {
2052             freeaddrinfo(q->addrs);
2053         } else {
2054             mem_free(q->addrs->ai_addr);
2055             mem_free(q->addrs);
2056         }
2057         q->addrs = NULL;
2058         q->addr_next = NULL;
2059     }
2060 
2061     q->handshake = q->sending = false;
2062 
2063     http_query_disconnect(q);
2064 
2065     str_trunc(q->rq_buf);
2066     q->rq_off = 0;
2067 
2068     q->http_parser_done = false;
2069 
2070     http_data_unref(q->response_data);
2071     q->response_data = NULL;
2072 
2073     if (q->response_multipart != NULL) {
2074         http_multipart_free(q->response_multipart);
2075         q->response_multipart = NULL;
2076     }
2077 }
2078 
2079 /* Free http_query
2080  */
2081 static void
http_query_free(http_query * q)2082 http_query_free (http_query *q)
2083 {
2084     http_query_reset(q);
2085 
2086     http_query_timeout_cancel(q);
2087     http_uri_free(q->uri);
2088     http_uri_free(q->real_uri);
2089     http_uri_free(q->orig_uri);
2090     http_hdr_cleanup(&q->request_header);
2091 
2092     mem_free(q->rq_buf);
2093 
2094     http_data_unref(q->request_data);
2095 
2096     mem_free(q);
2097 }
2098 
2099 /* Set Host header in HTTP request
2100  */
2101 static void
http_query_set_host(http_query * q)2102 http_query_set_host (http_query *q)
2103 {
2104     char                  *host, *end, *buf;
2105     size_t                len;
2106     const struct sockaddr *addr = http_uri_addr(q->uri);
2107 
2108     if (q->uri->scheme == HTTP_SCHEME_UNIX) {
2109         http_query_set_request_header(q, "Host", "localhost");
2110         return;
2111     }
2112 
2113     if (addr != NULL) {
2114         ip_straddr s;
2115         int        dport;
2116 
2117         switch (q->uri->scheme) {
2118         case HTTP_SCHEME_HTTP:
2119             dport = 80;
2120             break;
2121 
2122         case HTTP_SCHEME_HTTPS:
2123             dport = 443;
2124             break;
2125 
2126         default:
2127             dport = -1;
2128             break;
2129         }
2130         if (q->force_port) {
2131             dport = -1;
2132         }
2133 
2134         s = ip_straddr_from_sockaddr_dport(addr, dport, false);
2135         http_query_set_request_header(q, "Host", s.text);
2136 
2137         return;
2138     }
2139 
2140     host = strstr(http_uri_str(q->uri), "//") + 2;
2141     end = strchr(host, '/');
2142     len = end ? (size_t) (end - host) : strlen(host);
2143 
2144     buf = alloca(len + 1);
2145     memcpy(buf, host, len);
2146 
2147     buf[len] = '\0';
2148 
2149     http_query_set_request_header(q, "Host", buf);
2150 }
2151 
2152 /* Create new http_query
2153  *
2154  * Newly created http_query takes ownership on uri and body (if not NULL).
2155  * The method and content_type assumed to be constant strings.
2156  */
2157 http_query*
http_query_new(http_client * client,http_uri * uri,const char * method,char * body,const char * content_type)2158 http_query_new (http_client *client, http_uri *uri, const char *method,
2159         char *body, const char *content_type)
2160 {
2161     size_t len = body ? strlen(body) : 0;
2162     return http_query_new_len(client, uri, method, body, len, content_type);
2163 }
2164 
2165 /* Create new http_query
2166  *
2167  * Works like http_query_new(), but request body length is specified
2168  * explicitly
2169  */
2170 http_query*
http_query_new_len(http_client * client,http_uri * uri,const char * method,void * body,size_t body_len,const char * content_type)2171 http_query_new_len (http_client *client, http_uri *uri, const char *method,
2172         void *body, size_t body_len, const char *content_type)
2173 {
2174     http_query *q = mem_new(http_query, 1);
2175 
2176     q->client = client;
2177     q->uri = uri;
2178     q->method = method;
2179 
2180     http_hdr_init(&q->request_header);
2181     http_hdr_init(&q->response_header);
2182 
2183     q->sock = -1;
2184 
2185     q->rq_buf = str_new();
2186 
2187     q->onerror = client->onerror;
2188 
2189     http_parser_init(&q->http_parser, HTTP_RESPONSE);
2190     q->http_parser.data = &q->response_header;
2191 
2192     /* Note, on Kyocera ECOSYS M2040dn connection keep-alive causes
2193      * scanned job to remain in "Processing" state about 10 seconds
2194      * after job has been actually completed, making scanner effectively
2195      * busy.
2196      *
2197      * Looks like Kyocera firmware bug. Force connection to close
2198      * as a workaround
2199      */
2200     http_query_set_request_header(q, "Connection", "close");
2201 
2202     /* Save request body and set Content-Type */
2203     if (body != NULL) {
2204         q->request_data = http_data_new(NULL, body, body_len);
2205         if (content_type != NULL) {
2206             http_query_set_request_header(q, "Content-Type", content_type);
2207             http_data_set_content_type(q->request_data, content_type);
2208         }
2209     }
2210 
2211     /* Set default timeout */
2212     http_query_timeout(q, HTTP_QUERY_TIMEOUT);
2213 
2214     return q;
2215 }
2216 
2217 /* Create new http_query, relative to base URI
2218  *
2219  * Newly created http_query takes ownership on body (if not NULL).
2220  * The method and content_type assumed to be constant strings.
2221  */
2222 http_query*
http_query_new_relative(http_client * client,const http_uri * base_uri,const char * path,const char * method,char * body,const char * content_type)2223 http_query_new_relative(http_client *client,
2224         const http_uri *base_uri, const char *path,
2225         const char *method, char *body, const char *content_type)
2226 {
2227     http_uri *uri = http_uri_new_relative(base_uri, path, true, false);
2228     log_assert(client->log, uri != NULL);
2229     return http_query_new(client, uri, method, body, content_type);
2230 }
2231 
2232 /* http_query_timeout callback
2233  */
2234 static void
http_query_timeout_callback(void * p)2235 http_query_timeout_callback (void *p)
2236 {
2237     http_query *q = (http_query*) p;
2238 
2239     q->timeout_timer = NULL; /* to prevent eloop_timer_cancel() */
2240     http_query_complete(q, ERROR("timeout"));
2241 }
2242 
2243 /* Set query timeout, in milliseconds. Negative timeout means 'infinite'
2244  *
2245  * This function may be called multiple times (each subsequent call overrides
2246  * a previous one)
2247  */
2248 void
http_query_timeout(http_query * q,int timeout)2249 http_query_timeout (http_query *q, int timeout)
2250 {
2251     q->timeout_value = timeout;
2252 
2253     if (q->submitted) {
2254         http_query_timeout_cancel(q);
2255 
2256         if (timeout >= 0) {
2257             log_debug(q->client->log, "HTTP using timeout: %d ms",
2258                 q->timeout_value);
2259 
2260             q->timeout_timer = eloop_timer_new(timeout,
2261                 http_query_timeout_callback, q);
2262         } else {
2263             log_debug(q->client->log, "HTTP using timeout: none");
2264         }
2265     }
2266 }
2267 
2268 /* Cancel query timeout timer
2269  */
2270 static void
http_query_timeout_cancel(http_query * q)2271 http_query_timeout_cancel (http_query *q)
2272 {
2273     if (q->timeout_timer != NULL) {
2274         eloop_timer_cancel(q->timeout_timer);
2275         q->timeout_timer = NULL;
2276     }
2277 }
2278 
2279 /* Set forcing port to be added to the Host header for this query.
2280  *
2281  * This function may be called multiple times (each subsequent call overrides
2282  * a previous one).
2283  */
2284 void
http_query_force_port(http_query * q,bool force_port)2285 http_query_force_port(http_query *q, bool force_port) {
2286     q->force_port = force_port;
2287 }
2288 
2289 /* For this particular query override on-error callback, previously
2290  * set by http_client_onerror()
2291  *
2292  * If canllback is NULL, the completion callback, specified on a
2293  * http_query_submit() call, will be used even in a case of
2294  * transport error.
2295  */
2296 void
http_query_onerror(http_query * q,void (* onerror)(void * ptr,error err))2297 http_query_onerror (http_query *q, void (*onerror)(void *ptr, error err))
2298 {
2299     q->onerror = onerror;
2300 }
2301 
2302 /* Set on-redirect callback. It is called in a case of HTTP
2303  * redirect and may modify the supplied URI
2304  */
2305 void
http_query_onredir(http_query * q,void (* onredir)(void * ptr,http_uri * uri,const http_uri * orig_uri))2306 http_query_onredir (http_query *q,
2307         void (*onredir)(void *ptr, http_uri *uri, const http_uri *orig_uri))
2308 {
2309     q->onredir = onredir;
2310 }
2311 
2312 /* Set callback that will be called, when response headers reception
2313  * is completed
2314  */
2315 void
http_query_onrxhdr(http_query * q,void (* onrxhdr)(void * ptr,http_query * q))2316 http_query_onrxhdr (http_query *q, void (*onrxhdr)(void *ptr, http_query *q))
2317 {
2318     q->onrxhdr = onrxhdr;
2319 }
2320 
2321 /* Choose HTTP redirect method, based on HTTP status code
2322  * Returns NULL for non-redirection status code, and may
2323  * be used to detect if status code implies redirection
2324  */
2325 static const char*
http_query_redirect_method(const http_query * q)2326 http_query_redirect_method (const http_query *q)
2327 {
2328     const char *method = q->orig_method ? q->orig_method : q->method;
2329 
2330     switch(http_query_status(q)) {
2331     case 303:
2332         if (!strcmp(method, "POST") || !strcmp(method, "PUT")) {
2333             method = "GET";
2334         }
2335         break;
2336 
2337     case 301: case 302: case 307: case 308:
2338         break;
2339 
2340     default:
2341         return NULL;
2342     }
2343 
2344     return method;
2345 }
2346 
2347 /* Handle HTTP redirection
2348  */
2349 static error
http_query_redirect(http_query * q,const char * method)2350 http_query_redirect (http_query *q, const char *method)
2351 {
2352     const char *location;
2353     http_uri   *uri;
2354 
2355     /* Check and parse location */
2356     location = http_query_get_response_header(q, "Location");
2357     if (location == NULL || *location == '\0') {
2358         return ERROR("HTTP redirect: missed Location: field");
2359     }
2360 
2361     uri = http_uri_new_relative(q->uri, location, true, false);
2362     if (uri == NULL) {
2363         return ERROR("HTTP redirect: invalid Location: field");
2364     }
2365 
2366     /* Enforce redirects limit */
2367     q->redirect_count ++;
2368     if (q->redirect_count == HTTP_REDIRECT_LIMIT) {
2369         return ERROR("HTTP redirect: too many redirects");
2370     }
2371 
2372     /* Save original URI and method at the first redirect */
2373     if (q->redirect_count == 1) {
2374         q->orig_uri = q->uri;
2375         q->orig_method = q->method;
2376     } else {
2377         http_uri_free(q->uri);
2378         q->uri = NULL; /* Just in case */
2379     }
2380 
2381     /* Issue log message */
2382     log_debug(q->client->log, "HTTP redirect %d: %s %s",
2383         q->redirect_count, method, http_uri_str(uri));
2384 
2385     /* Call user hook, if any */
2386     if (q->onredir != NULL) {
2387         char *old_uri_str = alloca(strlen(uri->str) + 1);
2388 
2389         strcpy(old_uri_str, uri->str);
2390         q->onredir(q->client->ptr, uri, q->orig_uri);
2391         if (strcmp(old_uri_str, uri->str)) {
2392             log_debug(q->client->log, "HTTP redirect override: %s %s",
2393                 method, http_uri_str(uri));
2394         }
2395     }
2396 
2397     /* Perform redirection */
2398     http_query_reset(q);
2399 
2400     q->method = method;
2401     q->uri = uri;
2402 
2403     http_query_submit(q, q->callback);
2404 
2405     return NULL;
2406 }
2407 
2408 /* Complete query processing
2409  */
2410 static void
http_query_complete(http_query * q,error err)2411 http_query_complete (http_query *q, error err)
2412 {
2413     http_client *client = q->client;
2414 
2415     /* Make sure latest response header field is terminated */
2416     http_hdr_on_header_value(&q->http_parser, "", 0);
2417 
2418     /* Unlink query from a client */
2419     ll_del(&q->chain);
2420 
2421     /* Issue log messages */
2422     q->err = err;
2423     if (err != NULL) {
2424         log_debug(client->log, "HTTP %s %s: %s", q->method,
2425                 http_uri_str(q->uri), http_query_status_string(q));
2426     } else {
2427         log_debug(client->log, "HTTP %s %s: %d %s", q->method,
2428                 http_uri_str(q->uri),
2429                 http_query_status(q), http_query_status_string(q));
2430     }
2431 
2432     trace_http_query_hook(log_ctx_trace(client->log), q);
2433 
2434     /* Handle redirection */
2435     if (err == NULL) {
2436         const char *method = http_query_redirect_method(q);
2437 
2438         if (method != NULL) {
2439             q->err = err = http_query_redirect(q, method);
2440             if (err == NULL) {
2441                 return;
2442             }
2443         }
2444 
2445         log_debug(client->log, "HTTP %s %s: %s", q->method,
2446                 http_uri_str(q->uri), http_query_status_string(q));
2447     }
2448 
2449     /* Restore original method and URI, modified in case of redirection */
2450     if (q->orig_uri != NULL) {
2451         q->real_uri = q->uri;
2452         q->uri = q->orig_uri;
2453         q->method = q->orig_method;
2454 
2455         q->orig_uri = NULL;
2456         q->orig_method = NULL;
2457     }
2458 
2459     /* Call user callback */
2460     if (err != NULL && q->onerror != NULL) {
2461         q->onerror(client->ptr, err);
2462     } else if (q->callback != NULL) {
2463         q->callback(client->ptr, q);
2464     }
2465 
2466     http_query_free(q);
2467 }
2468 
2469 /* HTTP parser on_body callback
2470  */
2471 static int
http_query_on_body_callback(http_parser * parser,const char * data,size_t size)2472 http_query_on_body_callback (http_parser *parser,
2473         const char *data, size_t size)
2474 {
2475     http_query *q = OUTER_STRUCT(parser, http_query, http_parser);
2476 
2477     if (size == 0) {
2478         return 0; /* Just in case */
2479     }
2480 
2481     if (q->response_data == NULL) {
2482         q->response_data = http_data_new(NULL, NULL, 0);
2483     }
2484 
2485     if (!http_data_append(q->response_data, data, size)) {
2486         q->err = ERROR_ENOMEM;
2487     }
2488 
2489     return q->err ? 1 : 0;
2490 }
2491 
2492 /* HTTP parser on_headers_complete callback
2493  */
2494 static int
http_query_on_headers_complete(http_parser * parser)2495 http_query_on_headers_complete (http_parser *parser)
2496 {
2497     http_query *q = OUTER_STRUCT(parser, http_query, http_parser);
2498 
2499     if (http_query_redirect_method(q) == NULL) {
2500         log_debug(q->client->log,
2501                 "HTTP %s %s: got response headers (%d)",
2502                 q->method,
2503                 http_uri_str(q->uri),
2504                 http_query_status(q));
2505 
2506         if (q->onrxhdr != NULL) {
2507             q->onrxhdr(q->client->ptr, q);
2508         }
2509     }
2510 
2511     return 0;
2512 }
2513 
2514 /* HTTP parser on_message_complete callback
2515  */
2516 static int
http_query_on_message_complete(http_parser * parser)2517 http_query_on_message_complete (http_parser *parser)
2518 {
2519     http_query *q = OUTER_STRUCT(parser, http_query, http_parser);
2520 
2521     if (q->response_data != NULL) {
2522         const char *content_type;
2523 
2524         content_type = http_query_get_response_header(q, "Content-Type");
2525         if (content_type != NULL) {
2526             http_data_set_content_type(q->response_data, content_type);
2527             q->err = http_multipart_parse(
2528                     &q->response_multipart, q->client->log,
2529                     q->response_data, content_type);
2530         }
2531     }
2532 
2533     q->http_parser_done = true;
2534 
2535     return q->err ? 1 : 0;
2536 }
2537 
2538 /* HTTP parser callbacks
2539  */
2540 static http_parser_settings
2541 http_query_callbacks = {
2542     .on_header_field     = http_hdr_on_header_field,
2543     .on_header_value     = http_hdr_on_header_value,
2544     .on_body             = http_query_on_body_callback,
2545     .on_headers_complete = http_query_on_headers_complete,
2546     .on_message_complete = http_query_on_message_complete
2547 };
2548 
2549 /* Set http_query::fdpoll event mask
2550  */
2551 static void
http_query_fdpoll_set_mask(http_query * q,ELOOP_FDPOLL_MASK mask)2552 http_query_fdpoll_set_mask (http_query *q, ELOOP_FDPOLL_MASK mask)
2553 {
2554     ELOOP_FDPOLL_MASK old_mask = eloop_fdpoll_set_mask(q->fdpoll, mask);
2555     log_debug(q->client->log, "HTTP fdpoll: %s -> %s",
2556         eloop_fdpoll_mask_str(old_mask), eloop_fdpoll_mask_str(mask));
2557 }
2558 
2559 /* http_query::fdpoll callback
2560  */
2561 static void
http_query_fdpoll_callback(int fd,void * data,ELOOP_FDPOLL_MASK mask)2562 http_query_fdpoll_callback (int fd, void *data, ELOOP_FDPOLL_MASK mask)
2563 {
2564     http_query *q = data;
2565     size_t     len = mem_len(q->rq_buf) - q->rq_off;
2566     ssize_t    rc;
2567 
2568     (void) fd;
2569     (void) mask;
2570 
2571     if (q->handshake) {
2572         rc = gnutls_handshake(q->tls);
2573         if (rc < 0) {
2574             error err = http_query_sock_err(q, rc);
2575 
2576             if (err == NULL) {
2577                 return;
2578             }
2579 
2580             log_debug(q->client->log, "HTTP %s: gnutls_handshake(): %s",
2581                 q->straddr.text, ESTRING(err));
2582 
2583             /* TLS handshake failed, try another address, if any */
2584             http_query_disconnect(q);
2585             q->addr_next = q->addr_next->ai_next;
2586             http_query_connect(q, err);
2587 
2588             return;
2589         }
2590 
2591         log_debug(q->client->log, "HTTP done TLS handshake");
2592 
2593         q->handshake = false;
2594         http_query_fdpoll_set_mask(q, ELOOP_FDPOLL_BOTH);
2595     } else if (q->sending) {
2596         rc = http_query_sock_send(q, q->rq_buf + q->rq_off, len);
2597 
2598         if (rc > 0) {
2599             log_debug(q->client->log, "HTTP %d bytes sent", (int) rc);
2600         }
2601 
2602         if (rc < 0) {
2603             error err = http_query_sock_err(q, rc);
2604 
2605             if (err == NULL) {
2606                 return;
2607             }
2608 
2609             log_debug(q->client->log, "HTTP %s: send(): %s",
2610                 q->straddr.text, ESTRING(err));
2611 
2612             http_query_disconnect(q);
2613 
2614             if (q->rq_off == 0) {
2615                 /* None sent, try another address, if any */
2616                 q->addr_next = q->addr_next->ai_next;
2617                 http_query_connect(q, err);
2618             } else {
2619                 /* Sending started and failed */
2620                 http_query_complete(q, err);
2621             }
2622             return;
2623         }
2624 
2625         q->rq_off += rc;
2626 
2627         if (q->rq_off == mem_len(q->rq_buf)) {
2628             log_debug(q->client->log, "HTTP done request sending");
2629 
2630             q->sending = false;
2631             http_query_fdpoll_set_mask(q, ELOOP_FDPOLL_BOTH);
2632 
2633             /* Initialize HTTP parser */
2634             http_parser_init(&q->http_parser, HTTP_RESPONSE);
2635             q->http_parser.data = &q->response_header;
2636         }
2637     } else {
2638         static char io_buf[HTTP_IOBUF_SIZE];
2639 
2640         rc = http_query_sock_recv(q, io_buf, sizeof(io_buf));
2641         if (rc > 0) {
2642             log_debug(q->client->log, "HTTP %d bytes received", (int) rc);
2643         }
2644 
2645         if (rc < 0) {
2646             error err = http_query_sock_err(q, rc);
2647             if (err != NULL) {
2648                 http_query_complete(q, err);
2649             }
2650 
2651             return;
2652         }
2653 
2654         http_parser_execute(&q->http_parser, &http_query_callbacks,
2655                 io_buf, rc);
2656 
2657         if (q->http_parser.http_errno != HPE_OK) {
2658             error err = q->err;
2659             if (err == NULL) {
2660                 err = ERROR(http_errno_description(q->http_parser.http_errno));
2661             }
2662             http_query_complete(q, err);
2663         } else if (q->http_parser_done) {
2664             log_debug(q->client->log, "HTTP done response reception");
2665             http_query_complete(q, NULL);
2666         } else if (rc == 0) {
2667             error err = ERROR("connection closed by device");
2668             http_query_complete(q, err);
2669         }
2670     }
2671 }
2672 
2673 /* Try to connect to the next address. The err parameter is a query
2674  * completion error in a case there are no more addresses to try
2675  */
2676 static void
http_query_connect(http_query * q,error err)2677 http_query_connect (http_query *q, error err)
2678 {
2679     int        rc;
2680 
2681     /* Skip invalid addresses. Check that we have address to try */
2682 AGAIN:
2683     while (q->addr_next != NULL &&
2684            q->addr_next->ai_family != AF_INET &&
2685            q->addr_next->ai_family != AF_INET6 &&
2686            q->addr_next->ai_family != AF_UNIX) {
2687         q->addr_next = q->addr_next->ai_next;
2688     }
2689 
2690     if (q->addr_next == NULL) {
2691         http_query_complete(q, err);
2692         return;
2693     }
2694 
2695     q->straddr = ip_straddr_from_sockaddr(q->addr_next->ai_addr, true);
2696     log_debug(q->client->log, "HTTP trying %s", q->straddr.text);
2697 
2698     /* Create socket and try to connect */
2699     log_assert(q->client->log, q->sock < 0);
2700     q->sock = socket(q->addr_next->ai_family,
2701         q->addr_next->ai_socktype | SOCK_NONBLOCK | SOCK_CLOEXEC,
2702         q->addr_next->ai_protocol);
2703 
2704     if (q->sock == -1) {
2705         err = ERROR(strerror(errno));
2706         log_debug(q->client->log, "HTTP %s: socket(): %s",
2707             q->straddr.text, ESTRING(err));
2708 
2709         q->addr_next = q->addr_next->ai_next;
2710         goto AGAIN;
2711     }
2712 
2713     do {
2714         rc = connect(q->sock, q->addr_next->ai_addr, q->addr_next->ai_addrlen);
2715     } while (rc < 0 && errno == EINTR);
2716 
2717     if (rc < 0 && errno != EINPROGRESS) {
2718         err = ERROR(strerror(errno));
2719         log_debug(q->client->log, "HTTP %s: connect(): %s",
2720             q->straddr.text, ESTRING(err));
2721 
2722         http_query_disconnect(q);
2723 
2724         q->addr_next = q->addr_next->ai_next;
2725         goto AGAIN;
2726     }
2727 
2728     /* Setup TLS, if required */
2729     if (q->uri->scheme == HTTP_SCHEME_HTTPS) {
2730         int rc = gnutls_init(&q->tls,
2731             GNUTLS_CLIENT | GNUTLS_NONBLOCK | GNUTLS_NO_SIGNAL);
2732 
2733         if (rc == GNUTLS_E_SUCCESS) {
2734             rc = gnutls_set_default_priority(q->tls);
2735         }
2736 
2737         if (rc == GNUTLS_E_SUCCESS) {
2738             rc = gnutls_credentials_set(q->tls, GNUTLS_CRD_CERTIFICATE,
2739                     gnutls_cred);
2740         }
2741 
2742         if (rc != GNUTLS_E_SUCCESS) {
2743             err = ERROR(gnutls_strerror(rc));
2744             http_query_disconnect(q);
2745             http_query_complete(q, err);
2746             return;
2747         }
2748 
2749         gnutls_transport_set_int(q->tls, q->sock);
2750     }
2751 
2752     /* Create fdpoll, and we are done */
2753     q->fdpoll = eloop_fdpoll_new(q->sock, http_query_fdpoll_callback, q);
2754     if (q->tls != NULL) {
2755         q->handshake = true;
2756     }
2757     q->sending = true;
2758     http_query_fdpoll_set_mask(q, ELOOP_FDPOLL_WRITE);
2759 }
2760 
2761 /* Close connection to the server, if any
2762  */
2763 static void
http_query_disconnect(http_query * q)2764 http_query_disconnect (http_query *q)
2765 {
2766     if (q->fdpoll != NULL) {
2767         eloop_fdpoll_free(q->fdpoll);
2768         q->fdpoll = NULL;
2769     }
2770 
2771     if (q->tls != NULL) {
2772         gnutls_deinit(q->tls);
2773         q->tls = NULL;
2774     }
2775 
2776     if (q->sock >= 0) {
2777         close(q->sock);
2778         q->sock = -1;
2779     }
2780 }
2781 
2782 /* Send data to socket (either via TCP or TLS)
2783  * On a error, returns negative error code. Use
2784  * http_query_sock_err() to decode it
2785  */
2786 static ssize_t
http_query_sock_send(http_query * q,const void * data,size_t size)2787 http_query_sock_send (http_query *q, const void *data, size_t size)
2788 {
2789     ssize_t rc;
2790 
2791     if (q->tls == NULL) {
2792         rc = send(q->sock, data, size, MSG_NOSIGNAL);
2793         if (rc < 0) {
2794             rc = -errno;
2795         }
2796     } else {
2797         rc = gnutls_record_send(q->tls, data, size);
2798         if (rc < 0) {
2799             gnutls_record_discard_queued(q->tls);
2800         }
2801     }
2802 
2803     return rc;
2804 }
2805 
2806 /* Recv data from socket (either via TCP or TLS)
2807  */
2808 static ssize_t
http_query_sock_recv(http_query * q,void * data,size_t size)2809 http_query_sock_recv (http_query *q, void *data, size_t size)
2810 {
2811     ssize_t rc;
2812 
2813     if (q->tls == NULL) {
2814         rc = recv(q->sock, data, size, MSG_NOSIGNAL);
2815         if (rc < 0) {
2816             rc = -errno;
2817         }
2818     } else {
2819         rc = gnutls_record_recv(q->tls, data, size);
2820     }
2821 
2822     return rc;
2823 }
2824 
2825 /* Get socket error. May return NULL if last operation
2826  * has failed in recoverable manner
2827  */
2828 static error
http_query_sock_err(http_query * q,int rc)2829 http_query_sock_err (http_query *q, int rc)
2830 {
2831     ELOOP_FDPOLL_MASK mask = 0;
2832     error             err = NULL;
2833 
2834     if (q->tls == NULL) {
2835         rc = -rc;
2836         switch (rc) {
2837         case EINTR:
2838             break;
2839 
2840         case EWOULDBLOCK:
2841             mask = q->sending ? ELOOP_FDPOLL_WRITE : ELOOP_FDPOLL_READ;
2842             break;
2843 
2844         default:
2845             err = ERROR(strerror(errno));
2846         }
2847 
2848     } else {
2849         switch (rc) {
2850         case GNUTLS_E_INTERRUPTED:
2851             break;
2852 
2853         case GNUTLS_E_AGAIN:
2854             mask = gnutls_record_get_direction(q->tls) ?
2855                     ELOOP_FDPOLL_WRITE : ELOOP_FDPOLL_READ;
2856             break;
2857 
2858         default:
2859             if (gnutls_error_is_fatal(rc)) {
2860                 err = ERROR(gnutls_strerror(rc));
2861             }
2862         }
2863     }
2864 
2865     if (mask != 0) {
2866         http_query_fdpoll_set_mask(q, mask);
2867     }
2868 
2869     return err;
2870 }
2871 
2872 /* Start query processing. Called via eloop_call()
2873  */
2874 static void
http_query_start_processing(void * p)2875 http_query_start_processing (void *p)
2876 {
2877     http_query      *q = (http_query*) p;
2878     http_uri_field  field;
2879     char            *host, *port;
2880     struct addrinfo hints;
2881     int             rc;
2882 
2883     /* Get host name from the URI */
2884     field = http_uri_field_get(q->uri, UF_HOST);
2885     host = alloca(field.len + 1);
2886     memcpy(host, field.str, field.len);
2887     host[field.len] = '\0';
2888     http_uri_unescape_host(host);
2889 
2890     /* Get port name from the URI */
2891     if (http_uri_field_nonempty(q->uri, UF_PORT)) {
2892         field = http_uri_field_get(q->uri, UF_PORT);
2893         port = alloca(field.len + 1);
2894         memcpy(port, field.str, field.len);
2895         port[field.len] = '\0';
2896     } else {
2897         port = q->uri->scheme == HTTP_SCHEME_HTTP ? "80" : "443";
2898     }
2899 
2900     /* Lookup target addresses */
2901     if (q->uri->scheme != HTTP_SCHEME_UNIX) {
2902         log_debug(q->client->log, "HTTP resolving %s %s", host, port);
2903         memset(&hints, 0, sizeof(hints));
2904         hints.ai_flags = AI_ADDRCONFIG;
2905         hints.ai_family = AF_UNSPEC;
2906         hints.ai_socktype = SOCK_STREAM;
2907         hints.ai_protocol = IPPROTO_TCP;
2908 
2909         q->addrs_freeaddrinfo = true;
2910         rc = getaddrinfo(host, port, &hints, &q->addrs);
2911         if (rc != 0) {
2912             http_query_complete(q, ERROR(gai_strerror(rc)));
2913             return;
2914         }
2915     } else {
2916         struct sockaddr_un *addr;
2917         size_t pathlen = strlen(conf.socket_dir) + 1 /* for / */ + strlen(host);
2918         char *path = alloca(pathlen + 1);
2919         sprintf(path, "%s/%s", conf.socket_dir, host);
2920 
2921         log_debug(q->client->log, "connecting to local socket %s", path);
2922         q->addrs_freeaddrinfo = false;
2923         q->addrs = mem_new(struct addrinfo, 1);
2924         q->addrs->ai_family = AF_UNIX;
2925         q->addrs->ai_socktype = SOCK_STREAM;
2926         q->addrs->ai_protocol = 0;
2927 
2928         addr = mem_new(struct sockaddr_un, 1);
2929         addr->sun_family = AF_UNIX;
2930         strncpy(addr->sun_path, path, sizeof(addr->sun_path)-1);
2931         q->addrs->ai_addrlen = sizeof(struct sockaddr_un);
2932         q->addrs->ai_addr = (struct sockaddr *)addr;
2933 
2934         if (pathlen >= sizeof(addr->sun_path)) {
2935             http_query_complete(q, ERROR("Socket path is too long."));
2936             return;
2937         }
2938     }
2939 
2940     q->addr_next = q->addrs;
2941 
2942     /* Set Host: header, if not set by user */
2943     if (http_hdr_lookup(&q->request_header, "Host") == NULL) {
2944         q->host_inserted = true;
2945         http_query_set_host(q);
2946     }
2947 
2948     /* Format HTTP request */
2949     str_trunc(q->rq_buf);
2950     q->rq_buf = str_append_printf(q->rq_buf, "%s %s HTTP/1.1\r\n",
2951         q->method, http_uri_get_path(q->uri));
2952 
2953     if (q->request_data != NULL) {
2954         char buf[64];
2955         sprintf(buf, "%zd", q->request_data->size);
2956         http_hdr_set(&q->request_header, "Content-Length", buf);
2957     }
2958 
2959     q->rq_buf = http_hdr_write(&q->request_header, q->rq_buf);
2960 
2961     if (q->request_data != NULL) {
2962         q->rq_buf = str_append_mem(q->rq_buf,
2963             q->request_data->bytes, q->request_data->size);
2964     }
2965 
2966     /* Connect to the host */
2967     http_query_connect(q, ERROR("no host addresses available"));
2968 }
2969 
2970 /* Submit the query.
2971  *
2972  * When query is finished, callback will be called. After return from
2973  * callback, memory, owned by http_query will be invalidated
2974  */
2975 void
http_query_submit(http_query * q,void (* callback)(void * ptr,http_query * q))2976 http_query_submit (http_query *q, void (*callback)(void *ptr, http_query *q))
2977 {
2978     q->callback = callback;
2979 
2980     /* Issue log message, set timestamp and start timeout timer */
2981     log_debug(q->client->log, "HTTP %s %s", q->method, http_uri_str(q->uri));
2982 
2983     if (!q->submitted) {
2984         q->submitted = true;
2985         q->timestamp = timestamp_now();
2986 
2987         if (q->timeout_value >= 0) {
2988             http_query_timeout(q, q->timeout_value);
2989         }
2990     }
2991 
2992     /* Submit the query */
2993     log_assert(q->client->log, q->sock == -1);
2994     ll_push_end(&q->client->pending, &q->chain);
2995 
2996     q->eloop_callid = eloop_call(http_query_start_processing, q);
2997 }
2998 
2999 /* Cancel unfinished http_query. Callback will not be called and
3000  * memory owned by the http_query will be released
3001  */
3002 static void
http_query_cancel(http_query * q)3003 http_query_cancel (http_query *q)
3004 {
3005     log_debug(q->client->log, "HTTP %s %s: Cancelled", q->method,
3006             http_uri_str(q->uri));
3007 
3008     ll_del(&q->chain);
3009     eloop_call_cancel(q->eloop_callid);
3010 
3011     http_query_free(q);
3012 }
3013 
3014 /* Get http_query timestamp. Timestamp is set when query is
3015  * submitted. And this function should not be called before
3016  * http_query_submit()
3017  */
3018 timestamp
http_query_timestamp(const http_query * q)3019 http_query_timestamp (const http_query *q)
3020 {
3021     return q->timestamp;
3022 }
3023 
3024 /* Set uintptr_t parameter, associated with query.
3025  * Completion callback may later use http_query_get_uintptr()
3026  * to fetch this value
3027  */
3028 void
http_query_set_uintptr(http_query * q,uintptr_t u)3029 http_query_set_uintptr (http_query *q, uintptr_t u)
3030 {
3031     q->uintptr = u;
3032 }
3033 
3034 /* Get uintptr_t parameter, previously set by http_query_set_uintptr()
3035  */
3036 uintptr_t
http_query_get_uintptr(http_query * q)3037 http_query_get_uintptr (http_query *q)
3038 {
3039     return q->uintptr;
3040 }
3041 
3042 /* Get query error, if any
3043  *
3044  * Both transport errors and erroneous HTTP response codes
3045  * considered as errors here
3046  */
3047 error
http_query_error(const http_query * q)3048 http_query_error (const http_query *q)
3049 {
3050     if (q->err == NULL) {
3051         int status = http_query_status(q);
3052 
3053         if (200 <= status && status < 300) {
3054             return NULL;
3055         }
3056     }
3057 
3058     return ERROR(http_query_status_string(q));
3059 }
3060 
3061 /* Get query transport error, if any
3062  *
3063  * Only transport errors considered errors here
3064  */
3065 error
http_query_transport_error(const http_query * q)3066 http_query_transport_error (const http_query *q)
3067 {
3068     return q->err;
3069 }
3070 
3071 /* Get HTTP status code. Code not available, if query finished
3072  * with transport error
3073  */
3074 int
http_query_status(const http_query * q)3075 http_query_status (const http_query *q)
3076 {
3077     log_assert(q->client->log, q->err == NULL);
3078     return q->http_parser.status_code;
3079 }
3080 
3081 /* Get HTTP status string
3082  */
3083 const char*
http_query_status_string(const http_query * q)3084 http_query_status_string (const http_query *q)
3085 {
3086     if (q->err != NULL) {
3087         return ESTRING(q->err);
3088     }
3089 
3090     return http_status_str(q->http_parser.status_code);
3091 }
3092 
3093 /* Get query URI
3094  *
3095  * It works as http_query_orig_uri() before query is submitted
3096  * or after it is completed, and as http_query_real_uri() in
3097  * between
3098  *
3099  * This function is deprecated, use http_query_orig_uri()
3100  * or http_query_real_uri() instead
3101  */
3102 http_uri*
http_query_uri(const http_query * q)3103 http_query_uri (const http_query *q)
3104 {
3105     return q->uri;
3106 }
3107 
3108 /* Get original URI (the same as used when http_query was created)
3109  */
3110 http_uri*
http_query_orig_uri(const http_query * q)3111 http_query_orig_uri (const http_query *q)
3112 {
3113     return q->orig_uri ? q->orig_uri : q->uri;
3114 }
3115 
3116 /* Get real URI, that can differ from the requested URI
3117  * in a case of HTTP redirection
3118  */
3119 http_uri*
http_query_real_uri(const http_query * q)3120 http_query_real_uri (const http_query *q)
3121 {
3122     return q->real_uri ? q->real_uri : q->uri;
3123 }
3124 
3125 /* Get query method
3126  */
3127 const char*
http_query_method(const http_query * q)3128 http_query_method (const http_query *q)
3129 {
3130     return q->method;
3131 }
3132 
3133 /* Set request header
3134  */
3135 void
http_query_set_request_header(http_query * q,const char * name,const char * value)3136 http_query_set_request_header (http_query *q, const char *name,
3137         const char *value)
3138 {
3139     http_hdr_set(&q->request_header, name, value);
3140 }
3141 
3142 /* Get request header
3143  */
3144 const char*
http_query_get_request_header(const http_query * q,const char * name)3145 http_query_get_request_header (const http_query *q, const char *name)
3146 {
3147     return http_hdr_get(&q->request_header, name);
3148 }
3149 
3150 
3151 /* Get response header
3152  */
3153 const char*
http_query_get_response_header(const http_query * q,const char * name)3154 http_query_get_response_header(const http_query *q, const char *name)
3155 {
3156     return http_hdr_get(&q->response_header, name);
3157 }
3158 
3159 /* Get request data
3160  */
3161 http_data*
http_query_get_request_data(const http_query * q)3162 http_query_get_request_data (const http_query *q)
3163 {
3164     return q->request_data ? q->request_data : &http_data_empty;
3165 }
3166 
3167 /* Get request data
3168  */
3169 http_data*
http_query_get_response_data(const http_query * q)3170 http_query_get_response_data (const http_query *q)
3171 {
3172     return q->response_data ? q->response_data : &http_data_empty;
3173 }
3174 
3175 /* Get multipart response bodies. For non-multipart response
3176  * returns NULL
3177  */
3178 static http_multipart*
http_query_get_mp_response(const http_query * q)3179 http_query_get_mp_response (const http_query *q)
3180 {
3181     return q->response_multipart;
3182 }
3183 
3184 /* Get count of parts of multipart response
3185  */
3186 int
http_query_get_mp_response_count(const http_query * q)3187 http_query_get_mp_response_count (const http_query *q)
3188 {
3189     http_multipart *mp = http_query_get_mp_response(q);
3190     return mp ? mp->count : 0;
3191 }
3192 
3193 /* Get data of Nth part of multipart response
3194  */
3195 http_data*
http_query_get_mp_response_data(const http_query * q,int n)3196 http_query_get_mp_response_data (const http_query *q, int n)
3197 {
3198     http_multipart      *mp = http_query_get_mp_response(q);
3199 
3200     if (mp == NULL || n < 0 || n >= mp->count) {
3201         return NULL;
3202     }
3203 
3204     return mp->bodies[n];
3205 }
3206 
3207 /* Call callback for each request header
3208  */
3209 void
http_query_foreach_request_header(const http_query * q,void (* callback)(const char * name,const char * value,void * ptr),void * ptr)3210 http_query_foreach_request_header (const http_query *q,
3211         void (*callback)(const char *name, const char *value, void *ptr),
3212         void *ptr)
3213 {
3214     hdr_for_each(&q->request_header, callback, ptr);
3215 }
3216 
3217 /* Call callback for each response header
3218  */
3219 void
http_query_foreach_response_header(const http_query * q,void (* callback)(const char * name,const char * value,void * ptr),void * ptr)3220 http_query_foreach_response_header (const http_query *q,
3221         void (*callback)(const char *name, const char *value, void *ptr),
3222         void *ptr)
3223 {
3224     hdr_for_each(&q->response_header, callback, ptr);
3225 }
3226 
3227 /* Decode response part of the query.
3228  * This function is intended for testing purposes, not for regular use
3229  */
3230 error
http_query_test_decode_response(http_query * q,const void * data,size_t size)3231 http_query_test_decode_response (http_query *q, const void *data, size_t size)
3232 {
3233     http_parser_execute(&q->http_parser, &http_query_callbacks, data, size);
3234     if (q->http_parser.http_errno == HPE_OK && !q->http_parser_done) {
3235         http_parser_execute(&q->http_parser, &http_query_callbacks, data, 0);
3236     }
3237 
3238     if (q->http_parser.http_errno != HPE_OK) {
3239         if (q->err != NULL) {
3240             return q->err;
3241         }
3242         return ERROR(http_errno_description(q->http_parser.http_errno));
3243     }
3244 
3245     if (!q->http_parser_done) {
3246         return ERROR("truncated response");
3247     }
3248 
3249     return NULL;
3250 }
3251 
3252 /******************** HTTP initialization & cleanup ********************/
3253 /* Initialize HTTP client
3254  */
3255 SANE_Status
http_init(void)3256 http_init (void)
3257 {
3258     int rc = gnutls_certificate_allocate_credentials(&gnutls_cred);
3259     return rc == GNUTLS_E_SUCCESS ? SANE_STATUS_GOOD : SANE_STATUS_NO_MEM;
3260 }
3261 
3262 /* Initialize HTTP client
3263  */
3264 void
http_cleanup(void)3265 http_cleanup (void)
3266 {
3267     if (gnutls_cred != NULL) {
3268         gnutls_certificate_free_credentials(gnutls_cred);
3269         gnutls_cred = NULL;
3270     }
3271 }
3272 
3273 /* vim:ts=8:sw=4:et
3274  */
3275