1 /* $Id: http.c,v 1.17 2023/04/19 12:58:16 jsg Exp $ */
2 /*
3 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/types.h>
19 #include <sys/socket.h>
20
21 #include <arpa/inet.h>
22 #include <netinet/in.h>
23
24 #include <ctype.h>
25 #include <err.h>
26 #include <limits.h>
27 #include <netdb.h>
28 #include <stdio.h>
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <tls.h>
33 #include <unistd.h>
34
35 #include "http.h"
36
37 /*
38 * A buffer for transferring HTTP/S data.
39 */
40 struct httpxfer {
41 char *hbuf; /* header transfer buffer */
42 size_t hbufsz; /* header buffer size */
43 int headok; /* header has been parsed */
44 char *bbuf; /* body transfer buffer */
45 size_t bbufsz; /* body buffer size */
46 int bodyok; /* body has been parsed */
47 char *headbuf; /* lookaside buffer for headers */
48 struct httphead *head; /* parsed headers */
49 size_t headsz; /* number of headers */
50 };
51
52 /*
53 * An HTTP/S connection object.
54 */
55 struct http {
56 int fd; /* connected socket */
57 short port; /* port number */
58 struct source src; /* endpoint (raw) host */
59 char *path; /* path to request */
60 char *host; /* name of endpoint host */
61 struct tls *ctx; /* if TLS */
62 writefp writer; /* write function */
63 readfp reader; /* read function */
64 };
65
66 struct tls_config *tlscfg;
67
68 static ssize_t
dosysread(char * buf,size_t sz,const struct http * http)69 dosysread(char *buf, size_t sz, const struct http *http)
70 {
71 ssize_t rc;
72
73 rc = read(http->fd, buf, sz);
74 if (rc == -1)
75 warn("%s: read", http->src.ip);
76 return rc;
77 }
78
79 static ssize_t
dosyswrite(const void * buf,size_t sz,const struct http * http)80 dosyswrite(const void *buf, size_t sz, const struct http *http)
81 {
82 ssize_t rc;
83
84 rc = write(http->fd, buf, sz);
85 if (rc == -1)
86 warn("%s: write", http->src.ip);
87 return rc;
88 }
89
90 static ssize_t
dotlsread(char * buf,size_t sz,const struct http * http)91 dotlsread(char *buf, size_t sz, const struct http *http)
92 {
93 ssize_t rc;
94
95 do {
96 rc = tls_read(http->ctx, buf, sz);
97 } while (rc == TLS_WANT_POLLIN || rc == TLS_WANT_POLLOUT);
98
99 if (rc == -1)
100 warnx("%s: tls_read: %s", http->src.ip,
101 tls_error(http->ctx));
102 return rc;
103 }
104
105 static ssize_t
dotlswrite(const void * buf,size_t sz,const struct http * http)106 dotlswrite(const void *buf, size_t sz, const struct http *http)
107 {
108 ssize_t rc;
109
110 do {
111 rc = tls_write(http->ctx, buf, sz);
112 } while (rc == TLS_WANT_POLLIN || rc == TLS_WANT_POLLOUT);
113
114 if (rc == -1)
115 warnx("%s: tls_write: %s", http->src.ip,
116 tls_error(http->ctx));
117 return rc;
118 }
119
120 int
http_init(void)121 http_init(void)
122 {
123 if (tlscfg != NULL)
124 return 0;
125
126 tlscfg = tls_config_new();
127 if (tlscfg == NULL) {
128 warn("tls_config_new");
129 goto err;
130 }
131
132 if (tls_config_set_ca_file(tlscfg, tls_default_ca_cert_file()) == -1) {
133 warn("tls_config_set_ca_file: %s", tls_config_error(tlscfg));
134 goto err;
135 }
136
137 return 0;
138
139 err:
140 tls_config_free(tlscfg);
141 tlscfg = NULL;
142
143 return -1;
144 }
145
146 static ssize_t
http_read(char * buf,size_t sz,const struct http * http)147 http_read(char *buf, size_t sz, const struct http *http)
148 {
149 ssize_t ssz, xfer;
150
151 xfer = 0;
152 do {
153 if ((ssz = http->reader(buf, sz, http)) < 0)
154 return -1;
155 if (ssz == 0)
156 break;
157 xfer += ssz;
158 sz -= ssz;
159 buf += ssz;
160 } while (ssz > 0 && sz > 0);
161
162 return xfer;
163 }
164
165 static int
http_write(const char * buf,size_t sz,const struct http * http)166 http_write(const char *buf, size_t sz, const struct http *http)
167 {
168 ssize_t ssz, xfer;
169
170 xfer = sz;
171 while (sz > 0) {
172 if ((ssz = http->writer(buf, sz, http)) < 0)
173 return -1;
174 sz -= ssz;
175 buf += (size_t)ssz;
176 }
177 return xfer;
178 }
179
180 void
http_disconnect(struct http * http)181 http_disconnect(struct http *http)
182 {
183 int rc;
184
185 if (http->ctx != NULL) {
186 /* TLS connection. */
187 do {
188 rc = tls_close(http->ctx);
189 } while (rc == TLS_WANT_POLLIN || rc == TLS_WANT_POLLOUT);
190
191 if (rc < 0)
192 warnx("%s: tls_close: %s", http->src.ip,
193 tls_error(http->ctx));
194
195 tls_free(http->ctx);
196 }
197 if (http->fd != -1) {
198 if (close(http->fd) == -1)
199 warn("%s: close", http->src.ip);
200 }
201
202 http->fd = -1;
203 http->ctx = NULL;
204 }
205
206 void
http_free(struct http * http)207 http_free(struct http *http)
208 {
209
210 if (http == NULL)
211 return;
212 http_disconnect(http);
213 free(http->host);
214 free(http->path);
215 free(http->src.ip);
216 free(http);
217 }
218
219 struct http *
http_alloc(const struct source * addrs,size_t addrsz,const char * host,short port,const char * path)220 http_alloc(const struct source *addrs, size_t addrsz,
221 const char *host, short port, const char *path)
222 {
223 struct sockaddr_storage ss;
224 int family, fd, c;
225 socklen_t len;
226 size_t cur, i = 0;
227 struct http *http;
228
229 /* Do this while we still have addresses to connect. */
230 again:
231 if (i == addrsz)
232 return NULL;
233 cur = i++;
234
235 /* Convert to PF_INET or PF_INET6 address from string. */
236
237 memset(&ss, 0, sizeof(struct sockaddr_storage));
238
239 if (addrs[cur].family == 4) {
240 family = PF_INET;
241 ((struct sockaddr_in *)&ss)->sin_family = AF_INET;
242 ((struct sockaddr_in *)&ss)->sin_port = htons(port);
243 c = inet_pton(AF_INET, addrs[cur].ip,
244 &((struct sockaddr_in *)&ss)->sin_addr);
245 len = sizeof(struct sockaddr_in);
246 } else if (addrs[cur].family == 6) {
247 family = PF_INET6;
248 ((struct sockaddr_in6 *)&ss)->sin6_family = AF_INET6;
249 ((struct sockaddr_in6 *)&ss)->sin6_port = htons(port);
250 c = inet_pton(AF_INET6, addrs[cur].ip,
251 &((struct sockaddr_in6 *)&ss)->sin6_addr);
252 len = sizeof(struct sockaddr_in6);
253 } else {
254 warnx("%s: unknown family", addrs[cur].ip);
255 goto again;
256 }
257
258 if (c < 0) {
259 warn("%s: inet_ntop", addrs[cur].ip);
260 goto again;
261 } else if (c == 0) {
262 warnx("%s: inet_ntop", addrs[cur].ip);
263 goto again;
264 }
265
266 /* Create socket and connect. */
267
268 fd = socket(family, SOCK_STREAM, 0);
269 if (fd == -1) {
270 warn("%s: socket", addrs[cur].ip);
271 goto again;
272 } else if (connect(fd, (struct sockaddr *)&ss, len) == -1) {
273 warn("%s: connect", addrs[cur].ip);
274 close(fd);
275 goto again;
276 }
277
278 /* Allocate the communicator. */
279
280 http = calloc(1, sizeof(struct http));
281 if (http == NULL) {
282 warn("calloc");
283 close(fd);
284 return NULL;
285 }
286 http->fd = fd;
287 http->port = port;
288 http->src.family = addrs[cur].family;
289 http->src.ip = strdup(addrs[cur].ip);
290 http->host = strdup(host);
291 http->path = strdup(path);
292 if (http->src.ip == NULL || http->host == NULL || http->path == NULL) {
293 warn("strdup");
294 goto err;
295 }
296
297 /* If necessary, do our TLS setup. */
298
299 if (port != 443) {
300 http->writer = dosyswrite;
301 http->reader = dosysread;
302 return http;
303 }
304
305 http->writer = dotlswrite;
306 http->reader = dotlsread;
307
308 if ((http->ctx = tls_client()) == NULL) {
309 warn("tls_client");
310 goto err;
311 } else if (tls_configure(http->ctx, tlscfg) == -1) {
312 warnx("%s: tls_configure: %s",
313 http->src.ip, tls_error(http->ctx));
314 goto err;
315 }
316
317 if (tls_connect_socket(http->ctx, http->fd, http->host) != 0) {
318 warnx("%s: tls_connect_socket: %s, %s", http->src.ip,
319 http->host, tls_error(http->ctx));
320 goto err;
321 }
322
323 return http;
324 err:
325 http_free(http);
326 return NULL;
327 }
328
329 struct httpxfer *
http_open(const struct http * http,const void * p,size_t psz)330 http_open(const struct http *http, const void *p, size_t psz)
331 {
332 char *req;
333 int c;
334 struct httpxfer *trans;
335
336 if (p == NULL) {
337 c = asprintf(&req,
338 "GET %s HTTP/1.0\r\n"
339 "Host: %s\r\n"
340 "\r\n",
341 http->path, http->host);
342 } else {
343 c = asprintf(&req,
344 "POST %s HTTP/1.0\r\n"
345 "Host: %s\r\n"
346 "Content-Type: application/ocsp-request\r\n"
347 "Content-Length: %zu\r\n"
348 "\r\n",
349 http->path, http->host, psz);
350 }
351 if (c == -1) {
352 warn("asprintf");
353 return NULL;
354 } else if (!http_write(req, c, http)) {
355 free(req);
356 return NULL;
357 } else if (p != NULL && !http_write(p, psz, http)) {
358 free(req);
359 return NULL;
360 }
361
362 free(req);
363
364 trans = calloc(1, sizeof(struct httpxfer));
365 if (trans == NULL)
366 warn("calloc");
367 return trans;
368 }
369
370 void
http_close(struct httpxfer * x)371 http_close(struct httpxfer *x)
372 {
373
374 if (x == NULL)
375 return;
376 free(x->hbuf);
377 free(x->bbuf);
378 free(x->headbuf);
379 free(x->head);
380 free(x);
381 }
382
383 /*
384 * Read the HTTP body from the wire.
385 * If invoked multiple times, this will return the same pointer with the
386 * same data (or NULL, if the original invocation returned NULL).
387 * Returns NULL if read or allocation errors occur.
388 * You must not free the returned pointer.
389 */
390 char *
http_body_read(const struct http * http,struct httpxfer * trans,size_t * sz)391 http_body_read(const struct http *http, struct httpxfer *trans, size_t *sz)
392 {
393 char buf[BUFSIZ];
394 ssize_t ssz;
395 void *pp;
396 size_t szp;
397
398 if (sz == NULL)
399 sz = &szp;
400
401 /* Have we already parsed this? */
402
403 if (trans->bodyok > 0) {
404 *sz = trans->bbufsz;
405 return trans->bbuf;
406 } else if (trans->bodyok < 0)
407 return NULL;
408
409 *sz = 0;
410 trans->bodyok = -1;
411
412 do {
413 /* If less than sizeof(buf), at EOF. */
414 if ((ssz = http_read(buf, sizeof(buf), http)) < 0)
415 return NULL;
416 else if (ssz == 0)
417 break;
418
419 pp = recallocarray(trans->bbuf,
420 trans->bbufsz, trans->bbufsz + ssz, 1);
421 if (pp == NULL) {
422 warn("recallocarray");
423 return NULL;
424 }
425 trans->bbuf = pp;
426 memcpy(trans->bbuf + trans->bbufsz, buf, ssz);
427 trans->bbufsz += ssz;
428 } while (ssz == sizeof(buf));
429
430 trans->bodyok = 1;
431 *sz = trans->bbufsz;
432 return trans->bbuf;
433 }
434
435 struct httphead *
http_head_get(const char * v,struct httphead * h,size_t hsz)436 http_head_get(const char *v, struct httphead *h, size_t hsz)
437 {
438 size_t i;
439
440 for (i = 0; i < hsz; i++) {
441 if (strcmp(h[i].key, v))
442 continue;
443 return &h[i];
444 }
445 return NULL;
446 }
447
448 /*
449 * Look through the headers and determine our HTTP code.
450 * This will return -1 on failure, otherwise the code.
451 */
452 int
http_head_status(const struct http * http,struct httphead * h,size_t sz)453 http_head_status(const struct http *http, struct httphead *h, size_t sz)
454 {
455 int rc;
456 unsigned int code;
457 struct httphead *st;
458
459 if ((st = http_head_get("Status", h, sz)) == NULL) {
460 warnx("%s: no status header", http->src.ip);
461 return -1;
462 }
463
464 rc = sscanf(st->val, "%*s %u %*s", &code);
465 if (rc < 0) {
466 warn("sscanf");
467 return -1;
468 } else if (rc != 1) {
469 warnx("%s: cannot convert status header", http->src.ip);
470 return -1;
471 }
472 return code;
473 }
474
475 /*
476 * Parse headers from the transfer.
477 * Malformed headers are skipped.
478 * A special "Status" header is added for the HTTP status line.
479 * This can only happen once http_head_read has been called with
480 * success.
481 * This can be invoked multiple times: it will only parse the headers
482 * once and after that it will just return the cache.
483 * You must not free the returned pointer.
484 * If the original header parse failed, or if memory allocation fails
485 * internally, this returns NULL.
486 */
487 struct httphead *
http_head_parse(const struct http * http,struct httpxfer * trans,size_t * sz)488 http_head_parse(const struct http *http, struct httpxfer *trans, size_t *sz)
489 {
490 size_t hsz, szp;
491 struct httphead *h;
492 char *cp, *ep, *ccp, *buf;
493
494 if (sz == NULL)
495 sz = &szp;
496
497 /*
498 * If we've already parsed the headers, return the
499 * previously-parsed buffer now.
500 * If we have errors on the stream, return NULL now.
501 */
502
503 if (trans->head != NULL) {
504 *sz = trans->headsz;
505 return trans->head;
506 } else if (trans->headok <= 0)
507 return NULL;
508
509 if ((buf = strdup(trans->hbuf)) == NULL) {
510 warn("strdup");
511 return NULL;
512 }
513 hsz = 0;
514 cp = buf;
515
516 do {
517 if ((cp = strstr(cp, "\r\n")) != NULL)
518 cp += 2;
519 hsz++;
520 } while (cp != NULL);
521
522 /*
523 * Allocate headers, then step through the data buffer, parsing
524 * out headers as we have them.
525 * We know at this point that the buffer is NUL-terminated in
526 * the usual way.
527 */
528
529 h = calloc(hsz, sizeof(struct httphead));
530 if (h == NULL) {
531 warn("calloc");
532 free(buf);
533 return NULL;
534 }
535
536 *sz = hsz;
537 hsz = 0;
538 cp = buf;
539
540 do {
541 if ((ep = strstr(cp, "\r\n")) != NULL) {
542 *ep = '\0';
543 ep += 2;
544 }
545 if (hsz == 0) {
546 h[hsz].key = "Status";
547 h[hsz++].val = cp;
548 continue;
549 }
550
551 /* Skip bad headers. */
552 if ((ccp = strchr(cp, ':')) == NULL) {
553 warnx("%s: header without separator", http->src.ip);
554 continue;
555 }
556
557 *ccp++ = '\0';
558 while (isspace((unsigned char)*ccp))
559 ccp++;
560 h[hsz].key = cp;
561 h[hsz++].val = ccp;
562 } while ((cp = ep) != NULL);
563
564 trans->headbuf = buf;
565 trans->head = h;
566 trans->headsz = hsz;
567 return h;
568 }
569
570 /*
571 * Read the HTTP headers from the wire.
572 * If invoked multiple times, this will return the same pointer with the
573 * same data (or NULL, if the original invocation returned NULL).
574 * Returns NULL if read or allocation errors occur.
575 * You must not free the returned pointer.
576 */
577 char *
http_head_read(const struct http * http,struct httpxfer * trans,size_t * sz)578 http_head_read(const struct http *http, struct httpxfer *trans, size_t *sz)
579 {
580 char buf[BUFSIZ];
581 ssize_t ssz;
582 char *ep;
583 void *pp;
584 size_t szp;
585
586 if (sz == NULL)
587 sz = &szp;
588
589 /* Have we already parsed this? */
590
591 if (trans->headok > 0) {
592 *sz = trans->hbufsz;
593 return trans->hbuf;
594 } else if (trans->headok < 0)
595 return NULL;
596
597 *sz = 0;
598 ep = NULL;
599 trans->headok = -1;
600
601 /*
602 * Begin by reading by BUFSIZ blocks until we reach the header
603 * termination marker (two CRLFs).
604 * We might read into our body, but that's ok: we'll copy out
605 * the body parts into our body buffer afterward.
606 */
607
608 do {
609 /* If less than sizeof(buf), at EOF. */
610 if ((ssz = http_read(buf, sizeof(buf), http)) < 0)
611 return NULL;
612 else if (ssz == 0)
613 break;
614 pp = realloc(trans->hbuf, trans->hbufsz + ssz);
615 if (pp == NULL) {
616 warn("realloc");
617 return NULL;
618 }
619 trans->hbuf = pp;
620 memcpy(trans->hbuf + trans->hbufsz, buf, ssz);
621 trans->hbufsz += ssz;
622 /* Search for end of headers marker. */
623 ep = memmem(trans->hbuf, trans->hbufsz, "\r\n\r\n", 4);
624 } while (ep == NULL && ssz == sizeof(buf));
625
626 if (ep == NULL) {
627 warnx("%s: partial transfer", http->src.ip);
628 return NULL;
629 }
630 *ep = '\0';
631
632 /*
633 * The header data is invalid if it has any binary characters in
634 * it: check that now.
635 * This is important because we want to guarantee that all
636 * header keys and pairs are properly NUL-terminated.
637 */
638
639 if (strlen(trans->hbuf) != (uintptr_t)(ep - trans->hbuf)) {
640 warnx("%s: binary data in header", http->src.ip);
641 return NULL;
642 }
643
644 /*
645 * Copy remaining buffer into body buffer.
646 */
647
648 ep += 4;
649 trans->bbufsz = (trans->hbuf + trans->hbufsz) - ep;
650 trans->bbuf = malloc(trans->bbufsz);
651 if (trans->bbuf == NULL) {
652 warn("malloc");
653 return NULL;
654 }
655 memcpy(trans->bbuf, ep, trans->bbufsz);
656
657 trans->headok = 1;
658 *sz = trans->hbufsz;
659 return trans->hbuf;
660 }
661
662 void
http_get_free(struct httpget * g)663 http_get_free(struct httpget *g)
664 {
665
666 if (g == NULL)
667 return;
668 http_close(g->xfer);
669 http_free(g->http);
670 free(g);
671 }
672
673 struct httpget *
http_get(const struct source * addrs,size_t addrsz,const char * domain,short port,const char * path,const void * post,size_t postsz)674 http_get(const struct source *addrs, size_t addrsz, const char *domain,
675 short port, const char *path, const void *post, size_t postsz)
676 {
677 struct http *h;
678 struct httpxfer *x;
679 struct httpget *g;
680 struct httphead *head;
681 size_t headsz, bodsz, headrsz;
682 int code;
683 char *bod, *headr;
684
685 h = http_alloc(addrs, addrsz, domain, port, path);
686 if (h == NULL)
687 return NULL;
688
689 if ((x = http_open(h, post, postsz)) == NULL) {
690 http_free(h);
691 return NULL;
692 } else if ((headr = http_head_read(h, x, &headrsz)) == NULL) {
693 http_close(x);
694 http_free(h);
695 return NULL;
696 } else if ((bod = http_body_read(h, x, &bodsz)) == NULL) {
697 http_close(x);
698 http_free(h);
699 return NULL;
700 }
701
702 http_disconnect(h);
703
704 if ((head = http_head_parse(h, x, &headsz)) == NULL) {
705 http_close(x);
706 http_free(h);
707 return NULL;
708 } else if ((code = http_head_status(h, head, headsz)) < 0) {
709 http_close(x);
710 http_free(h);
711 return NULL;
712 }
713
714 if ((g = calloc(1, sizeof(struct httpget))) == NULL) {
715 warn("calloc");
716 http_close(x);
717 http_free(h);
718 return NULL;
719 }
720
721 g->headpart = headr;
722 g->headpartsz = headrsz;
723 g->bodypart = bod;
724 g->bodypartsz = bodsz;
725 g->head = head;
726 g->headsz = headsz;
727 g->code = code;
728 g->xfer = x;
729 g->http = h;
730 return g;
731 }
732
733 #if 0
734 int
735 main(void)
736 {
737 struct httpget *g;
738 struct httphead *httph;
739 size_t i, httphsz;
740 struct source addrs[2];
741 size_t addrsz;
742
743 #if 0
744 addrs[0].ip = "127.0.0.1";
745 addrs[0].family = 4;
746 addrsz = 1;
747 #else
748 addrs[0].ip = "2a00:1450:400a:806::2004";
749 addrs[0].family = 6;
750 addrs[1].ip = "193.135.3.123";
751 addrs[1].family = 4;
752 addrsz = 2;
753 #endif
754
755 if (http_init() == -1)
756 errx(EXIT_FAILURE, "http_init");
757
758 #if 0
759 g = http_get(addrs, addrsz, "localhost", 80, "/index.html");
760 #else
761 g = http_get(addrs, addrsz, "www.google.ch", 80, "/index.html",
762 NULL, 0);
763 #endif
764
765 if (g == NULL)
766 errx(EXIT_FAILURE, "http_get");
767
768 httph = http_head_parse(g->http, g->xfer, &httphsz);
769 warnx("code: %d", g->code);
770
771 for (i = 0; i < httphsz; i++)
772 warnx("head: [%s]=[%s]", httph[i].key, httph[i].val);
773
774 http_get_free(g);
775 return (EXIT_SUCCESS);
776 }
777 #endif
778