implement digest support
[project/uclient.git] / uclient-http.c
1 #include <stdio.h>
2 #include <ctype.h>
3 #include <unistd.h>
4 #include <stdint.h>
5
6 #include <libubox/ustream.h>
7 #include <libubox/ustream-ssl.h>
8 #include <libubox/usock.h>
9 #include <libubox/blobmsg.h>
10
11 #include "uclient.h"
12 #include "uclient-utils.h"
13 #include "uclient-backend.h"
14
15 static struct ustream_ssl_ctx *ssl_ctx;
16 static uint32_t nc;
17
18 enum auth_type {
19         AUTH_TYPE_UNKNOWN,
20         AUTH_TYPE_NONE,
21         AUTH_TYPE_BASIC,
22         AUTH_TYPE_DIGEST,
23 };
24
25 enum request_type {
26         REQ_GET,
27         REQ_HEAD,
28         REQ_POST,
29         __REQ_MAX
30 };
31
32 enum http_state {
33         HTTP_STATE_INIT,
34         HTTP_STATE_HEADERS_SENT,
35         HTTP_STATE_REQUEST_DONE,
36         HTTP_STATE_RECV_HEADERS,
37         HTTP_STATE_RECV_DATA,
38         HTTP_STATE_ERROR,
39 };
40
41 static const char * const request_types[__REQ_MAX] = {
42         [REQ_GET] = "GET",
43         [REQ_HEAD] = "HEAD",
44         [REQ_POST] = "POST",
45 };
46
47 struct uclient_http {
48         struct uclient uc;
49
50         struct ustream *us;
51
52         struct ustream_fd ufd;
53         struct ustream_ssl ussl;
54
55         bool ssl;
56         bool eof;
57         bool connection_close;
58         enum request_type req_type;
59         enum http_state state;
60
61         enum auth_type auth_type;
62         char *auth_str;
63
64         long read_chunked;
65         long content_length;
66
67         struct blob_buf headers;
68         struct blob_buf meta;
69 };
70
71 enum {
72         PREFIX_HTTP,
73         PREFIX_HTTPS,
74         __PREFIX_MAX,
75 };
76
77 static const char * const uclient_http_prefix[] = {
78         [PREFIX_HTTP] = "http://",
79         [PREFIX_HTTPS] = "https://",
80         [__PREFIX_MAX] = NULL
81 };
82
83 static int uclient_do_connect(struct uclient_http *uh, const char *port)
84 {
85         int fd;
86
87         if (uh->uc.url->port)
88                 port = uh->uc.url->port;
89
90         fd = usock(USOCK_TCP | USOCK_NONBLOCK, uh->uc.url->host, port);
91         if (fd < 0)
92                 return -1;
93
94         ustream_fd_init(&uh->ufd, fd);
95         return 0;
96 }
97
98 static void uclient_http_disconnect(struct uclient_http *uh)
99 {
100         if (!uh->us)
101                 return;
102
103         if (uh->ssl)
104                 ustream_free(&uh->ussl.stream);
105         ustream_free(&uh->ufd.stream);
106         close(uh->ufd.fd.fd);
107         uh->us = NULL;
108 }
109
110 static void uclient_http_free_url_state(struct uclient *cl)
111 {
112         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
113
114         uh->auth_type = AUTH_TYPE_UNKNOWN;
115         free(uh->auth_str);
116         uh->auth_str = NULL;
117         uclient_http_disconnect(uh);
118 }
119
120 static void uclient_notify_eof(struct uclient_http *uh)
121 {
122         struct ustream *us = uh->us;
123
124         if (!uh->eof) {
125                 if (!us->eof && !us->write_error)
126                         return;
127
128                 if (ustream_pending_data(us, false))
129                         return;
130         }
131
132         uclient_backend_set_eof(&uh->uc);
133
134         if (uh->connection_close)
135                 uclient_http_disconnect(uh);
136 }
137
138 static void uclient_http_reset_state(struct uclient_http *uh)
139 {
140         uclient_backend_reset_state(&uh->uc);
141         uh->read_chunked = -1;
142         uh->content_length = -1;
143         uh->eof = false;
144         uh->connection_close = false;
145         uh->state = HTTP_STATE_INIT;
146
147         if (uh->auth_type == AUTH_TYPE_UNKNOWN && !uh->uc.url->auth)
148                 uh->auth_type = AUTH_TYPE_NONE;
149 }
150
151 static void uclient_http_init_request(struct uclient_http *uh)
152 {
153         uclient_http_reset_state(uh);
154         blob_buf_init(&uh->meta, 0);
155 }
156
157 static enum auth_type
158 uclient_http_update_auth_type(struct uclient_http *uh)
159 {
160         if (!uh->auth_str)
161                 return AUTH_TYPE_NONE;
162
163         if (!strncasecmp(uh->auth_str, "basic", 5))
164                 return AUTH_TYPE_BASIC;
165
166         if (!strncasecmp(uh->auth_str, "digest", 6))
167                 return AUTH_TYPE_DIGEST;
168
169         return AUTH_TYPE_NONE;
170 }
171
172 static void uclient_http_process_headers(struct uclient_http *uh)
173 {
174         enum {
175                 HTTP_HDR_TRANSFER_ENCODING,
176                 HTTP_HDR_CONNECTION,
177                 HTTP_HDR_CONTENT_LENGTH,
178                 HTTP_HDR_AUTH,
179                 __HTTP_HDR_MAX,
180         };
181         static const struct blobmsg_policy hdr_policy[__HTTP_HDR_MAX] = {
182 #define hdr(_name) { .name = _name, .type = BLOBMSG_TYPE_STRING }
183                 [HTTP_HDR_TRANSFER_ENCODING] = hdr("transfer-encoding"),
184                 [HTTP_HDR_CONNECTION] = hdr("connection"),
185                 [HTTP_HDR_CONTENT_LENGTH] = hdr("content-length"),
186                 [HTTP_HDR_AUTH] = hdr("www-authenticate"),
187 #undef hdr
188         };
189         struct blob_attr *tb[__HTTP_HDR_MAX];
190         struct blob_attr *cur;
191
192         blobmsg_parse(hdr_policy, __HTTP_HDR_MAX, tb, blob_data(uh->meta.head), blob_len(uh->meta.head));
193
194         cur = tb[HTTP_HDR_TRANSFER_ENCODING];
195         if (cur && strstr(blobmsg_data(cur), "chunked"))
196                 uh->read_chunked = 0;
197
198         cur = tb[HTTP_HDR_CONNECTION];
199         if (cur && strstr(blobmsg_data(cur), "close"))
200                 uh->connection_close = true;
201
202         cur = tb[HTTP_HDR_CONTENT_LENGTH];
203         if (cur)
204                 uh->content_length = strtoul(blobmsg_data(cur), NULL, 10);
205
206         cur = tb[HTTP_HDR_AUTH];
207         if (cur) {
208                 free(uh->auth_str);
209                 uh->auth_str = strdup(blobmsg_data(cur));
210         }
211
212         uh->auth_type = uclient_http_update_auth_type(uh);
213 }
214
215 static void
216 uclient_http_add_auth_basic(struct uclient_http *uh)
217 {
218         struct uclient_url *url = uh->uc.url;
219         int auth_len = strlen(url->auth);
220         char *auth_buf;
221
222         if (auth_len > 512)
223                 return;
224
225         auth_buf = alloca(base64_len(auth_len) + 1);
226         base64_encode(url->auth, auth_len, auth_buf);
227         ustream_printf(uh->us, "Authorization: Basic %s\r\n", auth_buf);
228 }
229
230 static char *digest_unquote_sep(char **str)
231 {
232         char *cur = *str + 1;
233         char *start = cur;
234         char *out;
235
236         if (**str != '"')
237                 return NULL;
238
239         out = cur;
240         while (1) {
241                 if (!*cur)
242                         return NULL;
243
244                 if (*cur == '"') {
245                         cur++;
246                         break;
247                 }
248
249                 if (*cur == '\\')
250                         cur++;
251
252                 *(out++) = *(cur++);
253         }
254
255         if (*cur == ',')
256                 cur++;
257
258         *out = 0;
259         *str = cur;
260
261         return start;
262 }
263
264 static bool strmatch(char **str, const char *prefix)
265 {
266         int len = strlen(prefix);
267
268         if (strncmp(*str, prefix, len) != 0 || (*str)[len] != '=')
269                 return false;
270
271         *str += len + 1;
272         return true;
273 }
274
275 static void
276 get_cnonce(char *dest)
277 {
278         uint32_t val = nc;
279         FILE *f;
280
281         f = fopen("/dev/urandom", "r");
282         if (f) {
283                 fread(&val, sizeof(val), 1, f);
284                 fclose(f);
285         }
286
287         bin_to_hex(dest, &val, sizeof(val));
288 }
289
290 static void add_field(char **buf, int *ofs, int *len, const char *name, const char *val)
291 {
292         int available = *len - *ofs;
293         int required;
294         const char *next;
295         char *cur;
296
297         if (*len && !*buf)
298                 return;
299
300         required = strlen(name) + 4 + strlen(val) * 2;
301         if (required > available)
302                 *len += required - available + 64;
303
304         *buf = realloc(*buf, *len);
305         if (!*buf)
306                 return;
307
308         cur = *buf + *ofs;
309         cur += sprintf(cur, ", %s=\"", name);
310
311         while ((next = strchr(val, '"'))) {
312                 if (next > val) {
313                         memcpy(cur, val, next - val);
314                         cur += next - val;
315                 }
316
317                 cur += sprintf(cur, "\\\"");
318                 val = next + 1;
319         }
320
321         cur += sprintf(cur, "%s\"", val);
322         *ofs = cur - *buf;
323 }
324
325 static void
326 uclient_http_add_auth_digest(struct uclient_http *uh)
327 {
328         struct uclient_url *url = uh->uc.url;
329         const char *realm = NULL, *opaque = NULL;
330         const char *user, *password;
331         char *buf, *next;
332         int len, ofs;
333
334         char cnonce_str[9];
335         char nc_str[9];
336         char ahash[33];
337         char hash[33];
338
339         struct http_digest_data data = {
340                 .nc = nc_str,
341                 .cnonce = cnonce_str,
342                 .auth_hash = ahash,
343         };
344
345         len = strlen(uh->auth_str) + 1;
346         if (len > 512)
347                 return;
348
349         buf = alloca(len);
350         strcpy(buf, uh->auth_str);
351
352         /* skip auth type */
353         strsep(&buf, " ");
354
355         next = buf;
356         while (*next) {
357                 const char **dest = NULL;
358
359                 while (isspace(*next))
360                         next++;
361
362                 if (strmatch(&next, "realm"))
363                         dest = &realm;
364                 else if (strmatch(&next, "qop"))
365                         dest = &data.qop;
366                 else if (strmatch(&next, "nonce"))
367                         dest = &data.nonce;
368                 else if (strmatch(&next, "opaque"))
369                         dest = &opaque;
370                 else
371                         return;
372
373                 *dest = digest_unquote_sep(&next);
374         }
375
376         if (!realm || !data.qop || !data.nonce)
377                 return;
378
379         sprintf(nc_str, "%08x", nc++);
380         get_cnonce(cnonce_str);
381
382         data.qop = "auth";
383         data.uri = url->location;
384         data.method = request_types[uh->req_type];
385
386         password = strchr(url->auth, ':');
387         if (password) {
388                 char *user_buf;
389
390                 len = password - url->auth;
391                 if (len > 256)
392                         return;
393
394                 user_buf = alloca(len + 1);
395                 strncpy(user_buf, url->auth, len);
396                 user_buf[len] = 0;
397                 user = user_buf;
398                 password++;
399         } else {
400                 user = url->auth;
401                 password = "";
402         }
403
404         http_digest_calculate_auth_hash(ahash, user, realm, password);
405         http_digest_calculate_response(hash, &data);
406
407         buf = NULL;
408         len = 0;
409         ofs = 0;
410
411         add_field(&buf, &ofs, &len, "username", user);
412         add_field(&buf, &ofs, &len, "realm", realm);
413         add_field(&buf, &ofs, &len, "nonce", data.nonce);
414         add_field(&buf, &ofs, &len, "uri", data.uri);
415         add_field(&buf, &ofs, &len, "cnonce", data.cnonce);
416         add_field(&buf, &ofs, &len, "response", hash);
417         if (opaque)
418                 add_field(&buf, &ofs, &len, "opaque", opaque);
419
420         ustream_printf(uh->us, "Authorization: Digest nc=%s, qop=%s%s\r\n", data.nc, data.qop, buf);
421         free(buf);
422 }
423
424 static void
425 uclient_http_add_auth_header(struct uclient_http *uh)
426 {
427         if (!uh->uc.url->auth)
428                 return;
429
430         switch (uh->auth_type) {
431         case AUTH_TYPE_UNKNOWN:
432         case AUTH_TYPE_NONE:
433                 break;
434         case AUTH_TYPE_BASIC:
435                 uclient_http_add_auth_basic(uh);
436                 break;
437         case AUTH_TYPE_DIGEST:
438                 uclient_http_add_auth_digest(uh);
439                 break;
440         }
441 }
442
443 static void
444 uclient_http_send_headers(struct uclient_http *uh)
445 {
446         struct uclient_url *url = uh->uc.url;
447         struct blob_attr *cur;
448         enum request_type req_type = uh->req_type;
449         int rem;
450
451         if (uh->state >= HTTP_STATE_HEADERS_SENT)
452                 return;
453
454         if (uh->auth_type == AUTH_TYPE_UNKNOWN)
455                 req_type = REQ_HEAD;
456
457         ustream_printf(uh->us,
458                 "%s %s HTTP/1.1\r\n"
459                 "Host: %s\r\n",
460                 request_types[req_type],
461                 url->location, url->host);
462
463         blobmsg_for_each_attr(cur, uh->headers.head, rem)
464                 ustream_printf(uh->us, "%s: %s\n", blobmsg_name(cur), (char *) blobmsg_data(cur));
465
466         if (uh->req_type == REQ_POST)
467                 ustream_printf(uh->us, "Transfer-Encoding: chunked\r\n");
468
469         uclient_http_add_auth_header(uh);
470
471         ustream_printf(uh->us, "\r\n");
472 }
473
474 static void uclient_http_headers_complete(struct uclient_http *uh)
475 {
476         enum auth_type auth_type = uh->auth_type;
477
478         uh->state = HTTP_STATE_RECV_DATA;
479         uh->uc.meta = uh->meta.head;
480         uclient_http_process_headers(uh);
481
482         if (auth_type == AUTH_TYPE_UNKNOWN) {
483                 uclient_http_init_request(uh);
484                 uclient_http_send_headers(uh);
485                 uh->state = HTTP_STATE_REQUEST_DONE;
486                 return;
487         }
488
489         if (uh->uc.cb->header_done)
490                 uh->uc.cb->header_done(&uh->uc);
491
492         if (uh->req_type == REQ_HEAD) {
493                 uh->eof = true;
494                 uclient_notify_eof(uh);
495         }
496 }
497
498 static void uclient_parse_http_line(struct uclient_http *uh, char *data)
499 {
500         char *name;
501         char *sep;
502
503         if (uh->state == HTTP_STATE_REQUEST_DONE) {
504                 uh->state = HTTP_STATE_RECV_HEADERS;
505                 return;
506         }
507
508         if (!*data) {
509                 uclient_http_headers_complete(uh);
510                 return;
511         }
512
513         sep = strchr(data, ':');
514         if (!sep)
515                 return;
516
517         *(sep++) = 0;
518
519         for (name = data; *name; name++)
520                 *name = tolower(*name);
521
522         name = data;
523         while (isspace(*sep))
524                 sep++;
525
526         blobmsg_add_string(&uh->meta, name, sep);
527 }
528
529 static void __uclient_notify_read(struct uclient_http *uh)
530 {
531         struct uclient *uc = &uh->uc;
532         char *data;
533         int len;
534
535         if (uh->state < HTTP_STATE_REQUEST_DONE)
536                 return;
537
538         data = ustream_get_read_buf(uh->us, &len);
539         if (!data || !len)
540                 return;
541
542         if (uh->state < HTTP_STATE_RECV_DATA) {
543                 char *sep;
544                 int cur_len;
545
546                 do {
547                         sep = strstr(data, "\r\n");
548                         if (!sep)
549                                 break;
550
551                         /* Check for multi-line HTTP headers */
552                         if (sep > data) {
553                                 if (!sep[2])
554                                         return;
555
556                                 if (isspace(sep[2]) && sep[2] != '\r') {
557                                         sep[0] = ' ';
558                                         sep[1] = ' ';
559                                         continue;
560                                 }
561                         }
562
563                         *sep = 0;
564                         cur_len = sep + 2 - data;
565                         uclient_parse_http_line(uh, data);
566                         ustream_consume(uh->us, cur_len);
567                         len -= cur_len;
568
569                         data = ustream_get_read_buf(uh->us, &len);
570                 } while (data && uh->state < HTTP_STATE_RECV_DATA);
571
572                 if (!len)
573                         return;
574         }
575
576         if (uh->state == HTTP_STATE_RECV_DATA && uc->cb->data_read)
577                 uc->cb->data_read(uc);
578 }
579
580 static void uclient_notify_read(struct ustream *us, int bytes)
581 {
582         struct uclient_http *uh = container_of(us, struct uclient_http, ufd.stream);
583
584         __uclient_notify_read(uh);
585 }
586
587 static void uclient_notify_state(struct ustream *us)
588 {
589         struct uclient_http *uh = container_of(us, struct uclient_http, ufd.stream);
590
591         uclient_notify_eof(uh);
592 }
593
594 static int uclient_setup_http(struct uclient_http *uh)
595 {
596         struct ustream *us = &uh->ufd.stream;
597         int ret;
598
599         uh->us = us;
600         us->string_data = true;
601         us->notify_state = uclient_notify_state;
602         us->notify_read = uclient_notify_read;
603
604         ret = uclient_do_connect(uh, "80");
605         if (ret)
606                 return ret;
607
608         return 0;
609 }
610
611 static void uclient_ssl_notify_read(struct ustream *us, int bytes)
612 {
613         struct uclient_http *uh = container_of(us, struct uclient_http, ussl.stream);
614
615         __uclient_notify_read(uh);
616 }
617
618 static void uclient_ssl_notify_state(struct ustream *us)
619 {
620         struct uclient_http *uh = container_of(us, struct uclient_http, ussl.stream);
621
622         uclient_notify_eof(uh);
623 }
624
625 static int uclient_setup_https(struct uclient_http *uh)
626 {
627         struct ustream *us = &uh->ussl.stream;
628         int ret;
629
630         uh->ssl = true;
631         uh->us = us;
632
633         ret = uclient_do_connect(uh, "443");
634         if (ret)
635                 return ret;
636
637         if (!ssl_ctx)
638                 ssl_ctx = ustream_ssl_context_new(false);
639
640         us->string_data = true;
641         us->notify_state = uclient_ssl_notify_state;
642         us->notify_read = uclient_ssl_notify_read;
643         ustream_ssl_init(&uh->ussl, &uh->ufd.stream, ssl_ctx, false);
644
645         return 0;
646 }
647
648 static int uclient_http_connect(struct uclient *cl)
649 {
650         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
651
652         uclient_http_init_request(uh);
653
654         if (uh->us)
655                 return 0;
656
657         uh->ssl = cl->url->prefix == PREFIX_HTTPS;
658
659         if (uh->ssl)
660                 return uclient_setup_https(uh);
661         else
662                 return uclient_setup_http(uh);
663 }
664
665 static struct uclient *uclient_http_alloc(void)
666 {
667         struct uclient_http *uh;
668
669         uh = calloc_a(sizeof(*uh));
670         blob_buf_init(&uh->headers, 0);
671
672         return &uh->uc;
673 }
674
675 static void uclient_http_free(struct uclient *cl)
676 {
677         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
678
679         uclient_http_free_url_state(cl);
680         blob_buf_free(&uh->headers);
681         blob_buf_free(&uh->meta);
682         free(uh);
683 }
684
685 int
686 uclient_http_set_request_type(struct uclient *cl, const char *type)
687 {
688         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
689         int i;
690
691         if (cl->backend != &uclient_backend_http)
692                 return -1;
693
694         if (uh->state > HTTP_STATE_INIT)
695                 return -1;
696
697         for (i = 0; i < ARRAY_SIZE(request_types); i++) {
698                 if (strcmp(request_types[i], type) != 0)
699                         continue;
700
701                 uh->req_type = i;
702                 return 0;
703         }
704
705         return -1;
706 }
707
708 int
709 uclient_http_reset_headers(struct uclient *cl, const char *name, const char *value)
710 {
711         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
712
713         blob_buf_init(&uh->headers, 0);
714
715         return 0;
716 }
717
718 int
719 uclient_http_set_header(struct uclient *cl, const char *name, const char *value)
720 {
721         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
722
723         if (cl->backend != &uclient_backend_http)
724                 return -1;
725
726         if (uh->state > HTTP_STATE_INIT)
727                 return -1;
728
729         blobmsg_add_string(&uh->headers, name, value);
730         return 0;
731 }
732
733 static int
734 uclient_http_send_data(struct uclient *cl, char *buf, unsigned int len)
735 {
736         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
737
738         if (uh->state >= HTTP_STATE_REQUEST_DONE)
739                 return -1;
740
741         uclient_http_send_headers(uh);
742
743         ustream_printf(uh->us, "%X\r\n", len);
744         ustream_write(uh->us, buf, len, false);
745         ustream_printf(uh->us, "\r\n");
746
747         return len;
748 }
749
750 static int
751 uclient_http_request_done(struct uclient *cl)
752 {
753         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
754
755         if (uh->state >= HTTP_STATE_REQUEST_DONE)
756                 return -1;
757
758         uclient_http_send_headers(uh);
759         uh->state = HTTP_STATE_REQUEST_DONE;
760
761         return 0;
762 }
763
764 static int
765 uclient_http_read(struct uclient *cl, char *buf, unsigned int len)
766 {
767         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
768         int read_len = 0;
769         char *data, *data_end;
770
771         if (uh->state < HTTP_STATE_RECV_DATA || !uh->us)
772                 return 0;
773
774         data = ustream_get_read_buf(uh->us, &read_len);
775         if (!data || !read_len)
776                 return 0;
777
778         data_end = data + read_len;
779         read_len = 0;
780
781         if (uh->read_chunked == 0) {
782                 char *sep;
783
784                 if (data[0] == '\r' && data[1] == '\n') {
785                         data += 2;
786                         read_len += 2;
787                 }
788
789                 sep = strstr(data, "\r\n");
790                 if (!sep)
791                         return 0;
792
793                 *sep = 0;
794                 uh->read_chunked = strtoul(data, NULL, 16);
795
796                 read_len += sep + 2 - data;
797                 data = sep + 2;
798
799                 if (!uh->read_chunked)
800                         uh->eof = true;
801         }
802
803         if (len > data_end - data)
804                 len = data_end - data;
805
806         if (uh->read_chunked >= 0) {
807                 if (len > uh->read_chunked)
808                         len = uh->read_chunked;
809
810                 uh->read_chunked -= len;
811         } else if (uh->content_length >= 0) {
812                 if (len > uh->content_length)
813                         len = uh->content_length;
814
815                 uh->content_length -= len;
816                 if (!uh->content_length)
817                         uh->eof = true;
818         }
819
820         if (len > 0) {
821                 read_len += len;
822                 memcpy(buf, data, len);
823         }
824
825         if (read_len > 0)
826                 ustream_consume(uh->us, read_len);
827
828         uclient_notify_eof(uh);
829
830         return len;
831 }
832
833 const struct uclient_backend uclient_backend_http __hidden = {
834         .prefix = uclient_http_prefix,
835
836         .alloc = uclient_http_alloc,
837         .free = uclient_http_free,
838         .connect = uclient_http_connect,
839         .update_url = uclient_http_free_url_state,
840
841         .read = uclient_http_read,
842         .write = uclient_http_send_data,
843         .request = uclient_http_request_done,
844 };