1*bc25f96dSflorian /* $Id: http.c,v 1.32 2022/12/14 18:32:26 florian Exp $ */
2de579d12Sflorian /*
3de579d12Sflorian * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
4de579d12Sflorian *
5de579d12Sflorian * Permission to use, copy, modify, and distribute this software for any
6de579d12Sflorian * purpose with or without fee is hereby granted, provided that the above
7de579d12Sflorian * copyright notice and this permission notice appear in all copies.
8de579d12Sflorian *
9de579d12Sflorian * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10de579d12Sflorian * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11de579d12Sflorian * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12de579d12Sflorian * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13de579d12Sflorian * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14de579d12Sflorian * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15de579d12Sflorian * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16de579d12Sflorian */
17de579d12Sflorian
18a37de076Sguenther #include <sys/types.h>
19de579d12Sflorian #include <sys/socket.h>
20a37de076Sguenther
21de579d12Sflorian #include <arpa/inet.h>
22a37de076Sguenther #include <netinet/in.h>
23de579d12Sflorian
24de579d12Sflorian #include <ctype.h>
25de579d12Sflorian #include <err.h>
26de579d12Sflorian #include <limits.h>
27de579d12Sflorian #include <netdb.h>
28de579d12Sflorian #include <stdio.h>
29de579d12Sflorian #include <stdint.h>
30de579d12Sflorian #include <stdlib.h>
31de579d12Sflorian #include <string.h>
32de579d12Sflorian #include <tls.h>
33de579d12Sflorian #include <unistd.h>
34de579d12Sflorian
35de579d12Sflorian #include "http.h"
36de579d12Sflorian #include "extern.h"
37de579d12Sflorian
38de579d12Sflorian /*
39de579d12Sflorian * A buffer for transferring HTTP/S data.
40de579d12Sflorian */
41de579d12Sflorian struct httpxfer {
42de579d12Sflorian char *hbuf; /* header transfer buffer */
43de579d12Sflorian size_t hbufsz; /* header buffer size */
44de579d12Sflorian int headok; /* header has been parsed */
45de579d12Sflorian char *bbuf; /* body transfer buffer */
46de579d12Sflorian size_t bbufsz; /* body buffer size */
47de579d12Sflorian int bodyok; /* body has been parsed */
48de579d12Sflorian char *headbuf; /* lookaside buffer for headers */
49de579d12Sflorian struct httphead *head; /* parsed headers */
50de579d12Sflorian size_t headsz; /* number of headers */
51de579d12Sflorian };
52de579d12Sflorian
53de579d12Sflorian /*
54de579d12Sflorian * An HTTP/S connection object.
55de579d12Sflorian */
56de579d12Sflorian struct http {
57de579d12Sflorian int fd; /* connected socket */
58de579d12Sflorian short port; /* port number */
59de579d12Sflorian struct source src; /* endpoint (raw) host */
60de579d12Sflorian char *path; /* path to request */
61de579d12Sflorian char *host; /* name of endpoint host */
62de579d12Sflorian struct tls *ctx; /* if TLS */
63de579d12Sflorian writefp writer; /* write function */
64de579d12Sflorian readfp reader; /* read function */
65de579d12Sflorian };
66de579d12Sflorian
6725ca385bSjsing struct tls_config *tlscfg;
6825ca385bSjsing
69de579d12Sflorian static ssize_t
dosysread(char * buf,size_t sz,const struct http * http)70de579d12Sflorian dosysread(char *buf, size_t sz, const struct http *http)
71de579d12Sflorian {
72de579d12Sflorian ssize_t rc;
73de579d12Sflorian
74de579d12Sflorian rc = read(http->fd, buf, sz);
75df69c215Sderaadt if (rc == -1)
76de579d12Sflorian warn("%s: read", http->src.ip);
7734335c11Sjsing return rc;
78de579d12Sflorian }
79de579d12Sflorian
80de579d12Sflorian static ssize_t
dosyswrite(const void * buf,size_t sz,const struct http * http)81de579d12Sflorian dosyswrite(const void *buf, size_t sz, const struct http *http)
82de579d12Sflorian {
83de579d12Sflorian ssize_t rc;
84de579d12Sflorian
85de579d12Sflorian rc = write(http->fd, buf, sz);
86df69c215Sderaadt if (rc == -1)
87de579d12Sflorian warn("%s: write", http->src.ip);
8834335c11Sjsing return rc;
89de579d12Sflorian }
90de579d12Sflorian
91de579d12Sflorian static ssize_t
dotlsread(char * buf,size_t sz,const struct http * http)92de579d12Sflorian dotlsread(char *buf, size_t sz, const struct http *http)
93de579d12Sflorian {
94de579d12Sflorian ssize_t rc;
95de579d12Sflorian
96da0cef53Sderaadt do {
97de579d12Sflorian rc = tls_read(http->ctx, buf, sz);
987cd8f039Sjsing } while (rc == TLS_WANT_POLLIN || rc == TLS_WANT_POLLOUT);
99de579d12Sflorian
100df69c215Sderaadt if (rc == -1)
10155599e87Sderaadt warnx("%s: tls_read: %s", http->src.ip,
102de579d12Sflorian tls_error(http->ctx));
10334335c11Sjsing return rc;
104de579d12Sflorian }
105de579d12Sflorian
106de579d12Sflorian static ssize_t
dotlswrite(const void * buf,size_t sz,const struct http * http)107de579d12Sflorian dotlswrite(const void *buf, size_t sz, const struct http *http)
108de579d12Sflorian {
109de579d12Sflorian ssize_t rc;
110de579d12Sflorian
111da0cef53Sderaadt do {
112de579d12Sflorian rc = tls_write(http->ctx, buf, sz);
1137cd8f039Sjsing } while (rc == TLS_WANT_POLLIN || rc == TLS_WANT_POLLOUT);
114de579d12Sflorian
115df69c215Sderaadt if (rc == -1)
11655599e87Sderaadt warnx("%s: tls_write: %s", http->src.ip,
117de579d12Sflorian tls_error(http->ctx));
11834335c11Sjsing return rc;
119de579d12Sflorian }
120de579d12Sflorian
12125ca385bSjsing int
http_init(void)122e0575c3eStb http_init(void)
12325ca385bSjsing {
1247cd8f039Sjsing if (tlscfg != NULL)
12534335c11Sjsing return 0;
12625ca385bSjsing
12725ca385bSjsing tlscfg = tls_config_new();
1287cd8f039Sjsing if (tlscfg == NULL) {
12925ca385bSjsing warn("tls_config_new");
13025ca385bSjsing goto err;
13125ca385bSjsing }
13225ca385bSjsing
133fb0a89eeStedu if (tls_config_set_ca_file(tlscfg, tls_default_ca_cert_file()) == -1) {
13425ca385bSjsing warn("tls_config_set_ca_file: %s", tls_config_error(tlscfg));
13525ca385bSjsing goto err;
13625ca385bSjsing }
13725ca385bSjsing
13834335c11Sjsing return 0;
13925ca385bSjsing
14025ca385bSjsing err:
14125ca385bSjsing tls_config_free(tlscfg);
14225ca385bSjsing tlscfg = NULL;
14325ca385bSjsing
14434335c11Sjsing return -1;
14525ca385bSjsing }
14625ca385bSjsing
147de579d12Sflorian static ssize_t
http_read(char * buf,size_t sz,const struct http * http)148de579d12Sflorian http_read(char *buf, size_t sz, const struct http *http)
149de579d12Sflorian {
150de579d12Sflorian ssize_t ssz, xfer;
151de579d12Sflorian
152de579d12Sflorian xfer = 0;
153de579d12Sflorian do {
154de579d12Sflorian if ((ssz = http->reader(buf, sz, http)) < 0)
15534335c11Sjsing return -1;
1567cd8f039Sjsing if (ssz == 0)
157de579d12Sflorian break;
158de579d12Sflorian xfer += ssz;
159de579d12Sflorian sz -= ssz;
160de579d12Sflorian buf += ssz;
161de579d12Sflorian } while (ssz > 0 && sz > 0);
162de579d12Sflorian
16334335c11Sjsing return xfer;
164de579d12Sflorian }
165de579d12Sflorian
166de579d12Sflorian static int
http_write(const char * buf,size_t sz,const struct http * http)167a725b595Sderaadt http_write(const char *buf, size_t sz, const struct http *http)
168de579d12Sflorian {
169de579d12Sflorian ssize_t ssz, xfer;
170de579d12Sflorian
171de579d12Sflorian xfer = sz;
172de579d12Sflorian while (sz > 0) {
173de579d12Sflorian if ((ssz = http->writer(buf, sz, http)) < 0)
17434335c11Sjsing return -1;
175de579d12Sflorian sz -= ssz;
176a725b595Sderaadt buf += (size_t)ssz;
177de579d12Sflorian }
17834335c11Sjsing return xfer;
179de579d12Sflorian }
180de579d12Sflorian
181de579d12Sflorian void
http_disconnect(struct http * http)182de579d12Sflorian http_disconnect(struct http *http)
183de579d12Sflorian {
184761a2111Sjsing int rc;
185de579d12Sflorian
1867cd8f039Sjsing if (http->ctx != NULL) {
187de579d12Sflorian /* TLS connection. */
188761a2111Sjsing do {
189761a2111Sjsing rc = tls_close(http->ctx);
1907cd8f039Sjsing } while (rc == TLS_WANT_POLLIN || rc == TLS_WANT_POLLOUT);
191761a2111Sjsing
192de579d12Sflorian tls_free(http->ctx);
193c9376684Sjsing }
1947cd8f039Sjsing if (http->fd != -1) {
1957cd8f039Sjsing if (close(http->fd) == -1)
196de579d12Sflorian warn("%s: close", http->src.ip);
197de579d12Sflorian }
198de579d12Sflorian
199de579d12Sflorian http->fd = -1;
200de579d12Sflorian http->ctx = NULL;
201de579d12Sflorian }
202de579d12Sflorian
203de579d12Sflorian void
http_free(struct http * http)204de579d12Sflorian http_free(struct http *http)
205de579d12Sflorian {
206de579d12Sflorian
2077cd8f039Sjsing if (http == NULL)
208de579d12Sflorian return;
209de579d12Sflorian http_disconnect(http);
210de579d12Sflorian free(http->host);
211de579d12Sflorian free(http->path);
212de579d12Sflorian free(http->src.ip);
213de579d12Sflorian free(http);
214de579d12Sflorian }
215de579d12Sflorian
216de579d12Sflorian struct http *
http_alloc(const struct source * addrs,size_t addrsz,const char * host,short port,const char * path)217de579d12Sflorian http_alloc(const struct source *addrs, size_t addrsz,
218de579d12Sflorian const char *host, short port, const char *path)
219de579d12Sflorian {
220de579d12Sflorian struct sockaddr_storage ss;
221de579d12Sflorian int family, fd, c;
222de579d12Sflorian socklen_t len;
223de579d12Sflorian size_t cur, i = 0;
224de579d12Sflorian struct http *http;
225de579d12Sflorian
226de579d12Sflorian /* Do this while we still have addresses to connect. */
227de579d12Sflorian again:
228de579d12Sflorian if (i == addrsz)
22934335c11Sjsing return NULL;
230de579d12Sflorian cur = i++;
231de579d12Sflorian
232de579d12Sflorian /* Convert to PF_INET or PF_INET6 address from string. */
233de579d12Sflorian
234de579d12Sflorian memset(&ss, 0, sizeof(struct sockaddr_storage));
235de579d12Sflorian
2367cd8f039Sjsing if (addrs[cur].family == 4) {
237de579d12Sflorian family = PF_INET;
238de579d12Sflorian ((struct sockaddr_in *)&ss)->sin_family = AF_INET;
239de579d12Sflorian ((struct sockaddr_in *)&ss)->sin_port = htons(port);
240de579d12Sflorian c = inet_pton(AF_INET, addrs[cur].ip,
241de579d12Sflorian &((struct sockaddr_in *)&ss)->sin_addr);
242de579d12Sflorian len = sizeof(struct sockaddr_in);
2437cd8f039Sjsing } else if (addrs[cur].family == 6) {
244de579d12Sflorian family = PF_INET6;
245de579d12Sflorian ((struct sockaddr_in6 *)&ss)->sin6_family = AF_INET6;
246de579d12Sflorian ((struct sockaddr_in6 *)&ss)->sin6_port = htons(port);
247de579d12Sflorian c = inet_pton(AF_INET6, addrs[cur].ip,
248de579d12Sflorian &((struct sockaddr_in6 *)&ss)->sin6_addr);
249de579d12Sflorian len = sizeof(struct sockaddr_in6);
250de579d12Sflorian } else {
251de579d12Sflorian warnx("%s: unknown family", addrs[cur].ip);
252de579d12Sflorian goto again;
253de579d12Sflorian }
254de579d12Sflorian
255de579d12Sflorian if (c < 0) {
256de579d12Sflorian warn("%s: inet_ntop", addrs[cur].ip);
257de579d12Sflorian goto again;
2587cd8f039Sjsing } else if (c == 0) {
259de579d12Sflorian warnx("%s: inet_ntop", addrs[cur].ip);
260de579d12Sflorian goto again;
261de579d12Sflorian }
262de579d12Sflorian
263de579d12Sflorian /* Create socket and connect. */
264de579d12Sflorian
265de579d12Sflorian fd = socket(family, SOCK_STREAM, 0);
2667cd8f039Sjsing if (fd == -1) {
267de579d12Sflorian warn("%s: socket", addrs[cur].ip);
268de579d12Sflorian goto again;
2697cd8f039Sjsing } else if (connect(fd, (struct sockaddr *)&ss, len) == -1) {
270de579d12Sflorian warn("%s: connect", addrs[cur].ip);
271de579d12Sflorian close(fd);
272de579d12Sflorian goto again;
273de579d12Sflorian }
274de579d12Sflorian
275de579d12Sflorian /* Allocate the communicator. */
276de579d12Sflorian
277de579d12Sflorian http = calloc(1, sizeof(struct http));
2787cd8f039Sjsing if (http == NULL) {
279de579d12Sflorian warn("calloc");
280de579d12Sflorian close(fd);
28134335c11Sjsing return NULL;
282de579d12Sflorian }
283de579d12Sflorian http->fd = fd;
284de579d12Sflorian http->port = port;
285de579d12Sflorian http->src.family = addrs[cur].family;
286de579d12Sflorian http->src.ip = strdup(addrs[cur].ip);
287de579d12Sflorian http->host = strdup(host);
288de579d12Sflorian http->path = strdup(path);
2897cd8f039Sjsing if (http->src.ip == NULL || http->host == NULL || http->path == NULL) {
290de579d12Sflorian warn("strdup");
291de579d12Sflorian goto err;
292de579d12Sflorian }
293de579d12Sflorian
294de579d12Sflorian /* If necessary, do our TLS setup. */
295de579d12Sflorian
2967cd8f039Sjsing if (port != 443) {
297de579d12Sflorian http->writer = dosyswrite;
298de579d12Sflorian http->reader = dosysread;
29934335c11Sjsing return http;
300de579d12Sflorian }
301de579d12Sflorian
302de579d12Sflorian http->writer = dotlswrite;
303de579d12Sflorian http->reader = dotlsread;
304de579d12Sflorian
3057cd8f039Sjsing if ((http->ctx = tls_client()) == NULL) {
306de579d12Sflorian warn("tls_client");
307de579d12Sflorian goto err;
3087cd8f039Sjsing } else if (tls_configure(http->ctx, tlscfg) == -1) {
309de579d12Sflorian warnx("%s: tls_configure: %s",
310de579d12Sflorian http->src.ip, tls_error(http->ctx));
311de579d12Sflorian goto err;
312de579d12Sflorian }
313de579d12Sflorian
3147cd8f039Sjsing if (tls_connect_socket(http->ctx, http->fd, http->host) != 0) {
31555599e87Sderaadt warnx("%s: tls_connect_socket: %s, %s", http->src.ip,
31655599e87Sderaadt http->host, tls_error(http->ctx));
317de579d12Sflorian goto err;
318de579d12Sflorian }
319de579d12Sflorian
32034335c11Sjsing return http;
321de579d12Sflorian err:
322de579d12Sflorian http_free(http);
32334335c11Sjsing return NULL;
324de579d12Sflorian }
325de579d12Sflorian
326de579d12Sflorian struct httpxfer *
http_open(const struct http * http,int headreq,const void * p,size_t psz)3277b00f4e9Sflorian http_open(const struct http *http, int headreq, const void *p, size_t psz)
328de579d12Sflorian {
329de579d12Sflorian char *req;
330de579d12Sflorian int c;
331de579d12Sflorian struct httpxfer *trans;
332de579d12Sflorian
3337cd8f039Sjsing if (p == NULL) {
3347b00f4e9Sflorian if (headreq)
3357b00f4e9Sflorian c = asprintf(&req,
3367b00f4e9Sflorian "HEAD %s HTTP/1.0\r\n"
3377b00f4e9Sflorian "Host: %s\r\n"
3388d6bdbaeSflorian "User-Agent: OpenBSD-acme-client\r\n"
3397b00f4e9Sflorian "\r\n",
3407b00f4e9Sflorian http->path, http->host);
3417b00f4e9Sflorian else
342de579d12Sflorian c = asprintf(&req,
343de579d12Sflorian "GET %s HTTP/1.0\r\n"
344de579d12Sflorian "Host: %s\r\n"
3458d6bdbaeSflorian "User-Agent: OpenBSD-acme-client\r\n"
346de579d12Sflorian "\r\n",
347de579d12Sflorian http->path, http->host);
348de579d12Sflorian } else {
349de579d12Sflorian c = asprintf(&req,
350de579d12Sflorian "POST %s HTTP/1.0\r\n"
351de579d12Sflorian "Host: %s\r\n"
352de579d12Sflorian "Content-Length: %zu\r\n"
3537b00f4e9Sflorian "Content-Type: application/jose+json\r\n"
3548d6bdbaeSflorian "User-Agent: OpenBSD-acme-client\r\n"
355de579d12Sflorian "\r\n",
356de579d12Sflorian http->path, http->host, psz);
357de579d12Sflorian }
3587b00f4e9Sflorian
3597cd8f039Sjsing if (c == -1) {
360de579d12Sflorian warn("asprintf");
36134335c11Sjsing return NULL;
362de579d12Sflorian } else if (!http_write(req, c, http)) {
363de579d12Sflorian free(req);
36434335c11Sjsing return NULL;
3657cd8f039Sjsing } else if (p != NULL && !http_write(p, psz, http)) {
366de579d12Sflorian free(req);
36734335c11Sjsing return NULL;
368de579d12Sflorian }
369de579d12Sflorian
370de579d12Sflorian free(req);
371de579d12Sflorian
372de579d12Sflorian trans = calloc(1, sizeof(struct httpxfer));
3737cd8f039Sjsing if (trans == NULL)
374de579d12Sflorian warn("calloc");
37534335c11Sjsing return trans;
376de579d12Sflorian }
377de579d12Sflorian
378de579d12Sflorian void
http_close(struct httpxfer * x)379de579d12Sflorian http_close(struct httpxfer *x)
380de579d12Sflorian {
381de579d12Sflorian
3827cd8f039Sjsing if (x == NULL)
383de579d12Sflorian return;
384de579d12Sflorian free(x->hbuf);
385de579d12Sflorian free(x->bbuf);
386de579d12Sflorian free(x->headbuf);
387de579d12Sflorian free(x->head);
388de579d12Sflorian free(x);
389de579d12Sflorian }
390de579d12Sflorian
391de579d12Sflorian /*
392de579d12Sflorian * Read the HTTP body from the wire.
393de579d12Sflorian * If invoked multiple times, this will return the same pointer with the
394de579d12Sflorian * same data (or NULL, if the original invocation returned NULL).
395de579d12Sflorian * Returns NULL if read or allocation errors occur.
396de579d12Sflorian * You must not free the returned pointer.
397de579d12Sflorian */
398de579d12Sflorian char *
http_body_read(const struct http * http,struct httpxfer * trans,size_t * sz)39955599e87Sderaadt http_body_read(const struct http *http, struct httpxfer *trans, size_t *sz)
400de579d12Sflorian {
401de579d12Sflorian char buf[BUFSIZ];
402de579d12Sflorian ssize_t ssz;
403de579d12Sflorian void *pp;
404de579d12Sflorian size_t szp;
405de579d12Sflorian
4067cd8f039Sjsing if (sz == NULL)
407de579d12Sflorian sz = &szp;
408de579d12Sflorian
409de579d12Sflorian /* Have we already parsed this? */
410de579d12Sflorian
411de579d12Sflorian if (trans->bodyok > 0) {
412de579d12Sflorian *sz = trans->bbufsz;
41334335c11Sjsing return trans->bbuf;
414de579d12Sflorian } else if (trans->bodyok < 0)
41534335c11Sjsing return NULL;
416de579d12Sflorian
417de579d12Sflorian *sz = 0;
418de579d12Sflorian trans->bodyok = -1;
419de579d12Sflorian
420de579d12Sflorian do {
421de579d12Sflorian /* If less than sizeof(buf), at EOF. */
422de579d12Sflorian if ((ssz = http_read(buf, sizeof(buf), http)) < 0)
42334335c11Sjsing return NULL;
4247cd8f039Sjsing else if (ssz == 0)
425de579d12Sflorian break;
426425fd4bbSderaadt pp = recallocarray(trans->bbuf,
427425fd4bbSderaadt trans->bbufsz, trans->bbufsz + ssz, 1);
4287cd8f039Sjsing if (pp == NULL) {
429425fd4bbSderaadt warn("recallocarray");
43034335c11Sjsing return NULL;
431de579d12Sflorian }
432de579d12Sflorian trans->bbuf = pp;
433de579d12Sflorian memcpy(trans->bbuf + trans->bbufsz, buf, ssz);
434de579d12Sflorian trans->bbufsz += ssz;
4357cd8f039Sjsing } while (ssz == sizeof(buf));
436de579d12Sflorian
437de579d12Sflorian trans->bodyok = 1;
438de579d12Sflorian *sz = trans->bbufsz;
43934335c11Sjsing return trans->bbuf;
440de579d12Sflorian }
441de579d12Sflorian
442de579d12Sflorian struct httphead *
http_head_get(const char * v,struct httphead * h,size_t hsz)443de579d12Sflorian http_head_get(const char *v, struct httphead *h, size_t hsz)
444de579d12Sflorian {
445de579d12Sflorian size_t i;
446de579d12Sflorian
447de579d12Sflorian for (i = 0; i < hsz; i++) {
448bbcf33d8Sflorian if (strcasecmp(h[i].key, v) == 0)
44934335c11Sjsing return &h[i];
450de579d12Sflorian }
45134335c11Sjsing return NULL;
452de579d12Sflorian }
453de579d12Sflorian
454de579d12Sflorian /*
455de579d12Sflorian * Look through the headers and determine our HTTP code.
456de579d12Sflorian * This will return -1 on failure, otherwise the code.
457de579d12Sflorian */
458de579d12Sflorian int
http_head_status(const struct http * http,struct httphead * h,size_t sz)45955599e87Sderaadt http_head_status(const struct http *http, struct httphead *h, size_t sz)
460de579d12Sflorian {
461de579d12Sflorian int rc;
462de579d12Sflorian unsigned int code;
463de579d12Sflorian struct httphead *st;
464de579d12Sflorian
4657cd8f039Sjsing if ((st = http_head_get("Status", h, sz)) == NULL) {
466de579d12Sflorian warnx("%s: no status header", http->src.ip);
46734335c11Sjsing return -1;
468de579d12Sflorian }
469de579d12Sflorian
470de579d12Sflorian rc = sscanf(st->val, "%*s %u %*s", &code);
471de579d12Sflorian if (rc < 0) {
472de579d12Sflorian warn("sscanf");
47334335c11Sjsing return -1;
4747cd8f039Sjsing } else if (rc != 1) {
47555599e87Sderaadt warnx("%s: cannot convert status header", http->src.ip);
47634335c11Sjsing return -1;
477de579d12Sflorian }
47834335c11Sjsing return code;
479de579d12Sflorian }
480de579d12Sflorian
481de579d12Sflorian /*
482de579d12Sflorian * Parse headers from the transfer.
483de579d12Sflorian * Malformed headers are skipped.
484de579d12Sflorian * A special "Status" header is added for the HTTP status line.
485de579d12Sflorian * This can only happen once http_head_read has been called with
486de579d12Sflorian * success.
487de579d12Sflorian * This can be invoked multiple times: it will only parse the headers
488de579d12Sflorian * once and after that it will just return the cache.
489de579d12Sflorian * You must not free the returned pointer.
490de579d12Sflorian * If the original header parse failed, or if memory allocation fails
491de579d12Sflorian * internally, this returns NULL.
492de579d12Sflorian */
493de579d12Sflorian struct httphead *
http_head_parse(const struct http * http,struct httpxfer * trans,size_t * sz)49455599e87Sderaadt http_head_parse(const struct http *http, struct httpxfer *trans, size_t *sz)
495de579d12Sflorian {
496de579d12Sflorian size_t hsz, szp;
497de579d12Sflorian struct httphead *h;
498de579d12Sflorian char *cp, *ep, *ccp, *buf;
499de579d12Sflorian
5007cd8f039Sjsing if (sz == NULL)
501de579d12Sflorian sz = &szp;
502de579d12Sflorian
503de579d12Sflorian /*
504de579d12Sflorian * If we've already parsed the headers, return the
505de579d12Sflorian * previously-parsed buffer now.
506de579d12Sflorian * If we have errors on the stream, return NULL now.
507de579d12Sflorian */
508de579d12Sflorian
5097cd8f039Sjsing if (trans->head != NULL) {
510de579d12Sflorian *sz = trans->headsz;
51134335c11Sjsing return trans->head;
512de579d12Sflorian } else if (trans->headok <= 0)
51334335c11Sjsing return NULL;
514de579d12Sflorian
5157cd8f039Sjsing if ((buf = strdup(trans->hbuf)) == NULL) {
516de579d12Sflorian warn("strdup");
51734335c11Sjsing return NULL;
518de579d12Sflorian }
519de579d12Sflorian hsz = 0;
520de579d12Sflorian cp = buf;
521de579d12Sflorian
522de579d12Sflorian do {
5237cd8f039Sjsing if ((cp = strstr(cp, "\r\n")) != NULL)
524de579d12Sflorian cp += 2;
525de579d12Sflorian hsz++;
5267cd8f039Sjsing } while (cp != NULL);
527de579d12Sflorian
528de579d12Sflorian /*
529de579d12Sflorian * Allocate headers, then step through the data buffer, parsing
530de579d12Sflorian * out headers as we have them.
531b8ee2fe2Sderaadt * We know at this point that the buffer is NUL-terminated in
532de579d12Sflorian * the usual way.
533de579d12Sflorian */
534de579d12Sflorian
535de579d12Sflorian h = calloc(hsz, sizeof(struct httphead));
5367cd8f039Sjsing if (h == NULL) {
537de579d12Sflorian warn("calloc");
538de579d12Sflorian free(buf);
53934335c11Sjsing return NULL;
540de579d12Sflorian }
541de579d12Sflorian
542de579d12Sflorian *sz = hsz;
543de579d12Sflorian hsz = 0;
544de579d12Sflorian cp = buf;
545de579d12Sflorian
546de579d12Sflorian do {
5477cd8f039Sjsing if ((ep = strstr(cp, "\r\n")) != NULL) {
548de579d12Sflorian *ep = '\0';
549de579d12Sflorian ep += 2;
550de579d12Sflorian }
5517cd8f039Sjsing if (hsz == 0) {
552de579d12Sflorian h[hsz].key = "Status";
553de579d12Sflorian h[hsz++].val = cp;
554de579d12Sflorian continue;
555de579d12Sflorian }
556de579d12Sflorian
557de579d12Sflorian /* Skip bad headers. */
5587cd8f039Sjsing if ((ccp = strchr(cp, ':')) == NULL) {
55955599e87Sderaadt warnx("%s: header without separator", http->src.ip);
560de579d12Sflorian continue;
561de579d12Sflorian }
562de579d12Sflorian
563de579d12Sflorian *ccp++ = '\0';
564*bc25f96dSflorian while (isspace((unsigned char)*ccp))
565de579d12Sflorian ccp++;
566de579d12Sflorian h[hsz].key = cp;
567de579d12Sflorian h[hsz++].val = ccp;
5687cd8f039Sjsing } while ((cp = ep) != NULL);
569de579d12Sflorian
570de579d12Sflorian trans->headbuf = buf;
571de579d12Sflorian trans->head = h;
572de579d12Sflorian trans->headsz = hsz;
57334335c11Sjsing return h;
574de579d12Sflorian }
575de579d12Sflorian
576de579d12Sflorian /*
577de579d12Sflorian * Read the HTTP headers from the wire.
578de579d12Sflorian * If invoked multiple times, this will return the same pointer with the
579de579d12Sflorian * same data (or NULL, if the original invocation returned NULL).
580de579d12Sflorian * Returns NULL if read or allocation errors occur.
581de579d12Sflorian * You must not free the returned pointer.
582de579d12Sflorian */
583de579d12Sflorian char *
http_head_read(const struct http * http,struct httpxfer * trans,size_t * sz)58455599e87Sderaadt http_head_read(const struct http *http, struct httpxfer *trans, size_t *sz)
585de579d12Sflorian {
586de579d12Sflorian char buf[BUFSIZ];
587de579d12Sflorian ssize_t ssz;
588de579d12Sflorian char *ep;
589de579d12Sflorian void *pp;
590de579d12Sflorian size_t szp;
591de579d12Sflorian
5927cd8f039Sjsing if (sz == NULL)
593de579d12Sflorian sz = &szp;
594de579d12Sflorian
595de579d12Sflorian /* Have we already parsed this? */
596de579d12Sflorian
597de579d12Sflorian if (trans->headok > 0) {
598de579d12Sflorian *sz = trans->hbufsz;
59934335c11Sjsing return trans->hbuf;
600de579d12Sflorian } else if (trans->headok < 0)
60134335c11Sjsing return NULL;
602de579d12Sflorian
603de579d12Sflorian *sz = 0;
604de579d12Sflorian ep = NULL;
605de579d12Sflorian trans->headok = -1;
606de579d12Sflorian
607de579d12Sflorian /*
608de579d12Sflorian * Begin by reading by BUFSIZ blocks until we reach the header
609de579d12Sflorian * termination marker (two CRLFs).
610de579d12Sflorian * We might read into our body, but that's ok: we'll copy out
611de579d12Sflorian * the body parts into our body buffer afterward.
612de579d12Sflorian */
613de579d12Sflorian
614de579d12Sflorian do {
615de579d12Sflorian /* If less than sizeof(buf), at EOF. */
616de579d12Sflorian if ((ssz = http_read(buf, sizeof(buf), http)) < 0)
61734335c11Sjsing return NULL;
6187cd8f039Sjsing else if (ssz == 0)
619de579d12Sflorian break;
620425fd4bbSderaadt pp = recallocarray(trans->hbuf,
621425fd4bbSderaadt trans->hbufsz, trans->hbufsz + ssz, 1);
6227cd8f039Sjsing if (pp == NULL) {
623425fd4bbSderaadt warn("recallocarray");
62434335c11Sjsing return NULL;
625de579d12Sflorian }
626de579d12Sflorian trans->hbuf = pp;
627de579d12Sflorian memcpy(trans->hbuf + trans->hbufsz, buf, ssz);
628de579d12Sflorian trans->hbufsz += ssz;
629de579d12Sflorian /* Search for end of headers marker. */
630de579d12Sflorian ep = memmem(trans->hbuf, trans->hbufsz, "\r\n\r\n", 4);
6317cd8f039Sjsing } while (ep == NULL && ssz == sizeof(buf));
632de579d12Sflorian
6337cd8f039Sjsing if (ep == NULL) {
634de579d12Sflorian warnx("%s: partial transfer", http->src.ip);
63534335c11Sjsing return NULL;
636de579d12Sflorian }
637de579d12Sflorian *ep = '\0';
638de579d12Sflorian
639de579d12Sflorian /*
640de579d12Sflorian * The header data is invalid if it has any binary characters in
641de579d12Sflorian * it: check that now.
642de579d12Sflorian * This is important because we want to guarantee that all
643b8ee2fe2Sderaadt * header keys and pairs are properly NUL-terminated.
644de579d12Sflorian */
645de579d12Sflorian
646de579d12Sflorian if (strlen(trans->hbuf) != (uintptr_t)(ep - trans->hbuf)) {
647de579d12Sflorian warnx("%s: binary data in header", http->src.ip);
64834335c11Sjsing return NULL;
649de579d12Sflorian }
650de579d12Sflorian
651de579d12Sflorian /*
652de579d12Sflorian * Copy remaining buffer into body buffer.
653de579d12Sflorian */
654de579d12Sflorian
655de579d12Sflorian ep += 4;
656de579d12Sflorian trans->bbufsz = (trans->hbuf + trans->hbufsz) - ep;
657de579d12Sflorian trans->bbuf = malloc(trans->bbufsz);
6587cd8f039Sjsing if (trans->bbuf == NULL) {
659de579d12Sflorian warn("malloc");
66034335c11Sjsing return NULL;
661de579d12Sflorian }
662de579d12Sflorian memcpy(trans->bbuf, ep, trans->bbufsz);
663de579d12Sflorian
664de579d12Sflorian trans->headok = 1;
665de579d12Sflorian *sz = trans->hbufsz;
66634335c11Sjsing return trans->hbuf;
667de579d12Sflorian }
668de579d12Sflorian
669de579d12Sflorian void
http_get_free(struct httpget * g)670de579d12Sflorian http_get_free(struct httpget *g)
671de579d12Sflorian {
672de579d12Sflorian
6737cd8f039Sjsing if (g == NULL)
674de579d12Sflorian return;
675de579d12Sflorian http_close(g->xfer);
676de579d12Sflorian http_free(g->http);
677de579d12Sflorian free(g);
678de579d12Sflorian }
679de579d12Sflorian
680de579d12Sflorian struct httpget *
http_get(const struct source * addrs,size_t addrsz,const char * domain,short port,const char * path,int headreq,const void * post,size_t postsz)68155599e87Sderaadt http_get(const struct source *addrs, size_t addrsz, const char *domain,
6827b00f4e9Sflorian short port, const char *path, int headreq, const void *post, size_t postsz)
683de579d12Sflorian {
684de579d12Sflorian struct http *h;
685de579d12Sflorian struct httpxfer *x;
686de579d12Sflorian struct httpget *g;
687de579d12Sflorian struct httphead *head;
688de579d12Sflorian size_t headsz, bodsz, headrsz;
689de579d12Sflorian int code;
690de579d12Sflorian char *bod, *headr;
691de579d12Sflorian
692de579d12Sflorian h = http_alloc(addrs, addrsz, domain, port, path);
6937cd8f039Sjsing if (h == NULL)
69434335c11Sjsing return NULL;
695de579d12Sflorian
6967b00f4e9Sflorian if ((x = http_open(h, headreq, post, postsz)) == NULL) {
697de579d12Sflorian http_free(h);
69834335c11Sjsing return NULL;
6997cd8f039Sjsing } else if ((headr = http_head_read(h, x, &headrsz)) == NULL) {
700de579d12Sflorian http_close(x);
701de579d12Sflorian http_free(h);
70234335c11Sjsing return NULL;
7037cd8f039Sjsing } else if ((bod = http_body_read(h, x, &bodsz)) == NULL) {
704de579d12Sflorian http_close(x);
705de579d12Sflorian http_free(h);
70634335c11Sjsing return NULL;
707de579d12Sflorian }
708de579d12Sflorian
709de579d12Sflorian http_disconnect(h);
710de579d12Sflorian
7117cd8f039Sjsing if ((head = http_head_parse(h, x, &headsz)) == NULL) {
712de579d12Sflorian http_close(x);
713de579d12Sflorian http_free(h);
71434335c11Sjsing return NULL;
715de579d12Sflorian } else if ((code = http_head_status(h, head, headsz)) < 0) {
716de579d12Sflorian http_close(x);
717de579d12Sflorian http_free(h);
71834335c11Sjsing return NULL;
719de579d12Sflorian }
720de579d12Sflorian
7217cd8f039Sjsing if ((g = calloc(1, sizeof(struct httpget))) == NULL) {
722de579d12Sflorian warn("calloc");
723de579d12Sflorian http_close(x);
724de579d12Sflorian http_free(h);
72534335c11Sjsing return NULL;
726de579d12Sflorian }
727de579d12Sflorian
728de579d12Sflorian g->headpart = headr;
729de579d12Sflorian g->headpartsz = headrsz;
730de579d12Sflorian g->bodypart = bod;
731de579d12Sflorian g->bodypartsz = bodsz;
732de579d12Sflorian g->head = head;
733de579d12Sflorian g->headsz = headsz;
734de579d12Sflorian g->code = code;
735de579d12Sflorian g->xfer = x;
736de579d12Sflorian g->http = h;
73734335c11Sjsing return g;
738de579d12Sflorian }
739