preserve the contents of the www-authenticate header and use them to detect authentic...
[project/uclient.git] / uclient-http.c
1 #include <stdio.h>
2 #include <ctype.h>
3 #include <unistd.h>
4
5 #include <libubox/ustream.h>
6 #include <libubox/ustream-ssl.h>
7 #include <libubox/usock.h>
8 #include <libubox/blobmsg.h>
9
10 #include "uclient.h"
11 #include "uclient-utils.h"
12 #include "uclient-backend.h"
13
14 static struct ustream_ssl_ctx *ssl_ctx;
15
16 enum auth_type {
17         AUTH_TYPE_UNKNOWN,
18         AUTH_TYPE_NONE,
19         AUTH_TYPE_BASIC,
20 };
21
22 enum request_type {
23         REQ_GET,
24         REQ_HEAD,
25         REQ_POST,
26         __REQ_MAX
27 };
28
29 enum http_state {
30         HTTP_STATE_INIT,
31         HTTP_STATE_HEADERS_SENT,
32         HTTP_STATE_REQUEST_DONE,
33         HTTP_STATE_RECV_HEADERS,
34         HTTP_STATE_RECV_DATA,
35         HTTP_STATE_ERROR,
36 };
37
38 static const char * const request_types[__REQ_MAX] = {
39         [REQ_GET] = "GET",
40         [REQ_HEAD] = "HEAD",
41         [REQ_POST] = "POST",
42 };
43
44 struct uclient_http {
45         struct uclient uc;
46
47         struct ustream *us;
48
49         struct ustream_fd ufd;
50         struct ustream_ssl ussl;
51
52         bool ssl;
53         bool eof;
54         bool connection_close;
55         enum request_type req_type;
56         enum http_state state;
57
58         enum auth_type auth_type;
59         char *auth_str;
60
61         long read_chunked;
62         long content_length;
63
64         struct blob_buf headers;
65         struct blob_buf meta;
66 };
67
68 enum {
69         PREFIX_HTTP,
70         PREFIX_HTTPS,
71         __PREFIX_MAX,
72 };
73
74 static const char * const uclient_http_prefix[] = {
75         [PREFIX_HTTP] = "http://",
76         [PREFIX_HTTPS] = "https://",
77         [__PREFIX_MAX] = NULL
78 };
79
80 static int uclient_do_connect(struct uclient_http *uh, const char *port)
81 {
82         int fd;
83
84         if (uh->uc.url->port)
85                 port = uh->uc.url->port;
86
87         fd = usock(USOCK_TCP | USOCK_NONBLOCK, uh->uc.url->host, port);
88         if (fd < 0)
89                 return -1;
90
91         ustream_fd_init(&uh->ufd, fd);
92         return 0;
93 }
94
95 static void uclient_http_disconnect(struct uclient_http *uh)
96 {
97         if (!uh->us)
98                 return;
99
100         if (uh->ssl)
101                 ustream_free(&uh->ussl.stream);
102         ustream_free(&uh->ufd.stream);
103         close(uh->ufd.fd.fd);
104         uh->us = NULL;
105 }
106
107 static void uclient_http_free_url_state(struct uclient *cl)
108 {
109         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
110
111         uh->auth_type = AUTH_TYPE_UNKNOWN;
112         free(uh->auth_str);
113         uh->auth_str = NULL;
114         uclient_http_disconnect(uh);
115 }
116
117 static void uclient_notify_eof(struct uclient_http *uh)
118 {
119         struct ustream *us = uh->us;
120
121         if (!uh->eof) {
122                 if (!us->eof && !us->write_error)
123                         return;
124
125                 if (ustream_pending_data(us, false))
126                         return;
127         }
128
129         uclient_backend_set_eof(&uh->uc);
130
131         if (uh->connection_close)
132                 uclient_http_disconnect(uh);
133 }
134
135 static void uclient_http_reset_state(struct uclient_http *uh)
136 {
137         uclient_backend_reset_state(&uh->uc);
138         uh->read_chunked = -1;
139         uh->content_length = -1;
140         uh->eof = false;
141         uh->connection_close = false;
142         uh->state = HTTP_STATE_INIT;
143
144         if (uh->auth_type == AUTH_TYPE_UNKNOWN && !uh->uc.url->auth)
145                 uh->auth_type = AUTH_TYPE_NONE;
146 }
147
148 static void uclient_http_init_request(struct uclient_http *uh)
149 {
150         uclient_http_reset_state(uh);
151         blob_buf_init(&uh->meta, 0);
152 }
153
154 static enum auth_type
155 uclient_http_update_auth_type(struct uclient_http *uh)
156 {
157         if (!uh->auth_str)
158                 return AUTH_TYPE_NONE;
159
160         if (!strncasecmp(uh->auth_str, "basic", 5))
161                 return AUTH_TYPE_BASIC;
162
163         return AUTH_TYPE_NONE;
164 }
165
166 static void uclient_http_process_headers(struct uclient_http *uh)
167 {
168         enum {
169                 HTTP_HDR_TRANSFER_ENCODING,
170                 HTTP_HDR_CONNECTION,
171                 HTTP_HDR_CONTENT_LENGTH,
172                 HTTP_HDR_AUTH,
173                 __HTTP_HDR_MAX,
174         };
175         static const struct blobmsg_policy hdr_policy[__HTTP_HDR_MAX] = {
176 #define hdr(_name) { .name = _name, .type = BLOBMSG_TYPE_STRING }
177                 [HTTP_HDR_TRANSFER_ENCODING] = hdr("transfer-encoding"),
178                 [HTTP_HDR_CONNECTION] = hdr("connection"),
179                 [HTTP_HDR_CONTENT_LENGTH] = hdr("content-length"),
180                 [HTTP_HDR_AUTH] = hdr("www-authenticate"),
181 #undef hdr
182         };
183         struct blob_attr *tb[__HTTP_HDR_MAX];
184         struct blob_attr *cur;
185
186         blobmsg_parse(hdr_policy, __HTTP_HDR_MAX, tb, blob_data(uh->meta.head), blob_len(uh->meta.head));
187
188         cur = tb[HTTP_HDR_TRANSFER_ENCODING];
189         if (cur && strstr(blobmsg_data(cur), "chunked"))
190                 uh->read_chunked = 0;
191
192         cur = tb[HTTP_HDR_CONNECTION];
193         if (cur && strstr(blobmsg_data(cur), "close"))
194                 uh->connection_close = true;
195
196         cur = tb[HTTP_HDR_CONTENT_LENGTH];
197         if (cur)
198                 uh->content_length = strtoul(blobmsg_data(cur), NULL, 10);
199
200         cur = tb[HTTP_HDR_AUTH];
201         if (cur) {
202                 free(uh->auth_str);
203                 uh->auth_str = strdup(blobmsg_data(cur));
204         }
205
206         uh->auth_type = uclient_http_update_auth_type(uh);
207 }
208
209 static void
210 uclient_http_add_auth_header(struct uclient_http *uh)
211 {
212         struct uclient_url *url = uh->uc.url;
213         char *auth_buf;
214         int auth_len;
215
216         if (!url->auth)
217                 return;
218
219         auth_len = strlen(url->auth);
220         if (auth_len > 512)
221                 return;
222
223         auth_buf = alloca(base64_len(auth_len) + 1);
224         base64_encode(url->auth, auth_len, auth_buf);
225         ustream_printf(uh->us, "Authorization: Basic %s\r\n", auth_buf);
226 }
227
228 static void
229 uclient_http_send_headers(struct uclient_http *uh)
230 {
231         struct uclient_url *url = uh->uc.url;
232         struct blob_attr *cur;
233         enum request_type req_type = uh->req_type;
234         int rem;
235
236         if (uh->state >= HTTP_STATE_HEADERS_SENT)
237                 return;
238
239         if (uh->auth_type == AUTH_TYPE_UNKNOWN)
240                 req_type = REQ_HEAD;
241
242         ustream_printf(uh->us,
243                 "%s /%s HTTP/1.1\r\n"
244                 "Host: %s\r\n",
245                 request_types[req_type],
246                 url->location, url->host);
247
248         blobmsg_for_each_attr(cur, uh->headers.head, rem)
249                 ustream_printf(uh->us, "%s: %s\n", blobmsg_name(cur), (char *) blobmsg_data(cur));
250
251         if (uh->req_type == REQ_POST)
252                 ustream_printf(uh->us, "Transfer-Encoding: chunked\r\n");
253
254         uclient_http_add_auth_header(uh);
255
256         ustream_printf(uh->us, "\r\n");
257 }
258
259 static void uclient_http_headers_complete(struct uclient_http *uh)
260 {
261         enum auth_type auth_type = uh->auth_type;
262
263         uh->state = HTTP_STATE_RECV_DATA;
264         uh->uc.meta = uh->meta.head;
265         uclient_http_process_headers(uh);
266
267         if (auth_type == AUTH_TYPE_UNKNOWN) {
268                 uclient_http_init_request(uh);
269                 uclient_http_send_headers(uh);
270                 uh->state = HTTP_STATE_REQUEST_DONE;
271                 return;
272         }
273
274         if (uh->uc.cb->header_done)
275                 uh->uc.cb->header_done(&uh->uc);
276
277         if (uh->req_type == REQ_HEAD) {
278                 uh->eof = true;
279                 uclient_notify_eof(uh);
280         }
281 }
282
283 static void uclient_parse_http_line(struct uclient_http *uh, char *data)
284 {
285         char *name;
286         char *sep;
287
288         if (uh->state == HTTP_STATE_REQUEST_DONE) {
289                 uh->state = HTTP_STATE_RECV_HEADERS;
290                 return;
291         }
292
293         if (!*data) {
294                 uclient_http_headers_complete(uh);
295                 return;
296         }
297
298         sep = strchr(data, ':');
299         if (!sep)
300                 return;
301
302         *(sep++) = 0;
303
304         for (name = data; *name; name++)
305                 *name = tolower(*name);
306
307         name = data;
308         while (isspace(*sep))
309                 sep++;
310
311         blobmsg_add_string(&uh->meta, name, sep);
312 }
313
314 static void __uclient_notify_read(struct uclient_http *uh)
315 {
316         struct uclient *uc = &uh->uc;
317         char *data;
318         int len;
319
320         if (uh->state < HTTP_STATE_REQUEST_DONE)
321                 return;
322
323         data = ustream_get_read_buf(uh->us, &len);
324         if (!data || !len)
325                 return;
326
327         if (uh->state < HTTP_STATE_RECV_DATA) {
328                 char *sep;
329                 int cur_len;
330
331                 do {
332                         sep = strstr(data, "\r\n");
333                         if (!sep)
334                                 break;
335
336                         /* Check for multi-line HTTP headers */
337                         if (sep > data) {
338                                 if (!sep[2])
339                                         return;
340
341                                 if (isspace(sep[2]) && sep[2] != '\r') {
342                                         sep[0] = ' ';
343                                         sep[1] = ' ';
344                                         continue;
345                                 }
346                         }
347
348                         *sep = 0;
349                         cur_len = sep + 2 - data;
350                         uclient_parse_http_line(uh, data);
351                         ustream_consume(uh->us, cur_len);
352                         len -= cur_len;
353
354                         data = ustream_get_read_buf(uh->us, &len);
355                 } while (data && uh->state < HTTP_STATE_RECV_DATA);
356
357                 if (!len)
358                         return;
359         }
360
361         if (uh->state == HTTP_STATE_RECV_DATA && uc->cb->data_read)
362                 uc->cb->data_read(uc);
363 }
364
365 static void uclient_notify_read(struct ustream *us, int bytes)
366 {
367         struct uclient_http *uh = container_of(us, struct uclient_http, ufd.stream);
368
369         __uclient_notify_read(uh);
370 }
371
372 static void uclient_notify_state(struct ustream *us)
373 {
374         struct uclient_http *uh = container_of(us, struct uclient_http, ufd.stream);
375
376         uclient_notify_eof(uh);
377 }
378
379 static int uclient_setup_http(struct uclient_http *uh)
380 {
381         struct ustream *us = &uh->ufd.stream;
382         int ret;
383
384         uh->us = us;
385         us->string_data = true;
386         us->notify_state = uclient_notify_state;
387         us->notify_read = uclient_notify_read;
388
389         ret = uclient_do_connect(uh, "80");
390         if (ret)
391                 return ret;
392
393         return 0;
394 }
395
396 static void uclient_ssl_notify_read(struct ustream *us, int bytes)
397 {
398         struct uclient_http *uh = container_of(us, struct uclient_http, ussl.stream);
399
400         __uclient_notify_read(uh);
401 }
402
403 static void uclient_ssl_notify_state(struct ustream *us)
404 {
405         struct uclient_http *uh = container_of(us, struct uclient_http, ussl.stream);
406
407         uclient_notify_eof(uh);
408 }
409
410 static int uclient_setup_https(struct uclient_http *uh)
411 {
412         struct ustream *us = &uh->ussl.stream;
413         int ret;
414
415         uh->ssl = true;
416         uh->us = us;
417
418         ret = uclient_do_connect(uh, "443");
419         if (ret)
420                 return ret;
421
422         if (!ssl_ctx)
423                 ssl_ctx = ustream_ssl_context_new(false);
424
425         us->string_data = true;
426         us->notify_state = uclient_ssl_notify_state;
427         us->notify_read = uclient_ssl_notify_read;
428         ustream_ssl_init(&uh->ussl, &uh->ufd.stream, ssl_ctx, false);
429
430         return 0;
431 }
432
433 static int uclient_http_connect(struct uclient *cl)
434 {
435         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
436
437         uclient_http_init_request(uh);
438
439         if (uh->us)
440                 return 0;
441
442         uh->ssl = cl->url->prefix == PREFIX_HTTPS;
443
444         if (uh->ssl)
445                 return uclient_setup_https(uh);
446         else
447                 return uclient_setup_http(uh);
448 }
449
450 static struct uclient *uclient_http_alloc(void)
451 {
452         struct uclient_http *uh;
453
454         uh = calloc_a(sizeof(*uh));
455         blob_buf_init(&uh->headers, 0);
456
457         return &uh->uc;
458 }
459
460 static void uclient_http_free(struct uclient *cl)
461 {
462         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
463
464         uclient_http_free_url_state(cl);
465         blob_buf_free(&uh->headers);
466         blob_buf_free(&uh->meta);
467         free(uh);
468 }
469
470 int
471 uclient_http_set_request_type(struct uclient *cl, const char *type)
472 {
473         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
474         int i;
475
476         if (cl->backend != &uclient_backend_http)
477                 return -1;
478
479         if (uh->state > HTTP_STATE_INIT)
480                 return -1;
481
482         for (i = 0; i < ARRAY_SIZE(request_types); i++) {
483                 if (strcmp(request_types[i], type) != 0)
484                         continue;
485
486                 uh->req_type = i;
487                 return 0;
488         }
489
490         return -1;
491 }
492
493 int
494 uclient_http_reset_headers(struct uclient *cl, const char *name, const char *value)
495 {
496         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
497
498         blob_buf_init(&uh->headers, 0);
499
500         return 0;
501 }
502
503 int
504 uclient_http_set_header(struct uclient *cl, const char *name, const char *value)
505 {
506         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
507
508         if (cl->backend != &uclient_backend_http)
509                 return -1;
510
511         if (uh->state > HTTP_STATE_INIT)
512                 return -1;
513
514         blobmsg_add_string(&uh->headers, name, value);
515         return 0;
516 }
517
518 static int
519 uclient_http_send_data(struct uclient *cl, char *buf, unsigned int len)
520 {
521         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
522
523         if (uh->state >= HTTP_STATE_REQUEST_DONE)
524                 return -1;
525
526         uclient_http_send_headers(uh);
527
528         ustream_printf(uh->us, "%X\r\n", len);
529         ustream_write(uh->us, buf, len, false);
530         ustream_printf(uh->us, "\r\n");
531
532         return len;
533 }
534
535 static int
536 uclient_http_request_done(struct uclient *cl)
537 {
538         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
539
540         if (uh->state >= HTTP_STATE_REQUEST_DONE)
541                 return -1;
542
543         uclient_http_send_headers(uh);
544         uh->state = HTTP_STATE_REQUEST_DONE;
545
546         return 0;
547 }
548
549 static int
550 uclient_http_read(struct uclient *cl, char *buf, unsigned int len)
551 {
552         struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
553         int read_len = 0;
554         char *data, *data_end;
555
556         if (uh->state < HTTP_STATE_RECV_DATA || !uh->us)
557                 return 0;
558
559         data = ustream_get_read_buf(uh->us, &read_len);
560         if (!data || !read_len)
561                 return 0;
562
563         data_end = data + read_len;
564         read_len = 0;
565
566         if (uh->read_chunked == 0) {
567                 char *sep;
568
569                 if (data[0] == '\r' && data[1] == '\n') {
570                         data += 2;
571                         read_len += 2;
572                 }
573
574                 sep = strstr(data, "\r\n");
575                 if (!sep)
576                         return 0;
577
578                 *sep = 0;
579                 uh->read_chunked = strtoul(data, NULL, 16);
580
581                 read_len += sep + 2 - data;
582                 data = sep + 2;
583
584                 if (!uh->read_chunked)
585                         uh->eof = true;
586         }
587
588         if (len > data_end - data)
589                 len = data_end - data;
590
591         if (uh->read_chunked >= 0) {
592                 if (len > uh->read_chunked)
593                         len = uh->read_chunked;
594
595                 uh->read_chunked -= len;
596         } else if (uh->content_length >= 0) {
597                 if (len > uh->content_length)
598                         len = uh->content_length;
599
600                 uh->content_length -= len;
601                 if (!uh->content_length)
602                         uh->eof = true;
603         }
604
605         if (len > 0) {
606                 read_len += len;
607                 memcpy(buf, data, len);
608         }
609
610         if (read_len > 0)
611                 ustream_consume(uh->us, read_len);
612
613         uclient_notify_eof(uh);
614
615         return len;
616 }
617
618 const struct uclient_backend uclient_backend_http __hidden = {
619         .prefix = uclient_http_prefix,
620
621         .alloc = uclient_http_alloc,
622         .free = uclient_http_free,
623         .connect = uclient_http_connect,
624         .update_url = uclient_http_free_url_state,
625
626         .read = uclient_http_read,
627         .write = uclient_http_send_data,
628         .request = uclient_http_request_done,
629 };