xref: /openbsd-src/usr.sbin/acme-client/http.c (revision bc25f96dc3113b6979fb82cc1a9aa620bba7f47f)
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