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(¶ms->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(¶ms);
1582 err = http_hdr_params_parse(¶ms, "Content-Type", content_type);
1583 if (err != NULL) {
1584 http_hdr_cleanup(¶ms);
1585 return err;
1586 }
1587
1588 log_debug(log, "http multipart parameters:");
1589 for (LL_FOR_EACH(node, ¶ms.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(¶ms, "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(¶ms);
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