xref: /openbsd-src/usr.sbin/ocspcheck/http.c (revision 6bae335dd015f9e023db26fea05e06c52cf1d0d7)
1*6bae335dSjsg /*	$Id: http.c,v 1.17 2023/04/19 12:58:16 jsg Exp $ */
2471c6e53Sbeck /*
3471c6e53Sbeck  * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
4471c6e53Sbeck  *
5471c6e53Sbeck  * Permission to use, copy, modify, and distribute this software for any
6471c6e53Sbeck  * purpose with or without fee is hereby granted, provided that the above
7471c6e53Sbeck  * copyright notice and this permission notice appear in all copies.
8471c6e53Sbeck  *
9471c6e53Sbeck  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10471c6e53Sbeck  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11471c6e53Sbeck  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12471c6e53Sbeck  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13471c6e53Sbeck  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14471c6e53Sbeck  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15471c6e53Sbeck  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16471c6e53Sbeck  */
17471c6e53Sbeck 
18a37de076Sguenther #include <sys/types.h>
19a37de076Sguenther #include <sys/socket.h>
20a37de076Sguenther 
21dc0b7803Sbeck #include <arpa/inet.h>
22dc0b7803Sbeck #include <netinet/in.h>
23471c6e53Sbeck 
24471c6e53Sbeck #include <ctype.h>
25471c6e53Sbeck #include <err.h>
26471c6e53Sbeck #include <limits.h>
27471c6e53Sbeck #include <netdb.h>
28471c6e53Sbeck #include <stdio.h>
29471c6e53Sbeck #include <stdint.h>
30471c6e53Sbeck #include <stdlib.h>
31471c6e53Sbeck #include <string.h>
32471c6e53Sbeck #include <tls.h>
33471c6e53Sbeck #include <unistd.h>
34471c6e53Sbeck 
35471c6e53Sbeck #include "http.h"
36471c6e53Sbeck 
37471c6e53Sbeck /*
38471c6e53Sbeck  * A buffer for transferring HTTP/S data.
39471c6e53Sbeck  */
40471c6e53Sbeck struct	httpxfer {
41471c6e53Sbeck 	char		*hbuf;    /* header transfer buffer */
42471c6e53Sbeck 	size_t		 hbufsz;  /* header buffer size */
43471c6e53Sbeck 	int		 headok;  /* header has been parsed */
44471c6e53Sbeck 	char		*bbuf;    /* body transfer buffer */
45471c6e53Sbeck 	size_t		 bbufsz;  /* body buffer size */
46471c6e53Sbeck 	int		 bodyok;  /* body has been parsed */
47471c6e53Sbeck 	char		*headbuf; /* lookaside buffer for headers */
48471c6e53Sbeck 	struct httphead	*head;    /* parsed headers */
49471c6e53Sbeck 	size_t		 headsz;  /* number of headers */
50471c6e53Sbeck };
51471c6e53Sbeck 
52471c6e53Sbeck /*
53471c6e53Sbeck  * An HTTP/S connection object.
54471c6e53Sbeck  */
55471c6e53Sbeck struct	http {
56471c6e53Sbeck 	int		   fd;     /* connected socket */
57471c6e53Sbeck 	short		   port;   /* port number */
58471c6e53Sbeck 	struct source	   src;    /* endpoint (raw) host */
59471c6e53Sbeck 	char		  *path;   /* path to request */
60471c6e53Sbeck 	char		  *host;   /* name of endpoint host */
61471c6e53Sbeck 	struct tls	  *ctx;    /* if TLS */
62471c6e53Sbeck 	writefp		   writer; /* write function */
63471c6e53Sbeck 	readfp		   reader; /* read function */
64471c6e53Sbeck };
65471c6e53Sbeck 
66471c6e53Sbeck struct tls_config *tlscfg;
67471c6e53Sbeck 
68471c6e53Sbeck static ssize_t
dosysread(char * buf,size_t sz,const struct http * http)69471c6e53Sbeck dosysread(char *buf, size_t sz, const struct http *http)
70471c6e53Sbeck {
71471c6e53Sbeck 	ssize_t	 rc;
72471c6e53Sbeck 
73471c6e53Sbeck 	rc = read(http->fd, buf, sz);
74df69c215Sderaadt 	if (rc == -1)
75471c6e53Sbeck 		warn("%s: read", http->src.ip);
769994a505Sbenno 	return rc;
77471c6e53Sbeck }
78471c6e53Sbeck 
79471c6e53Sbeck static ssize_t
dosyswrite(const void * buf,size_t sz,const struct http * http)80471c6e53Sbeck dosyswrite(const void *buf, size_t sz, const struct http *http)
81471c6e53Sbeck {
82471c6e53Sbeck 	ssize_t	 rc;
83471c6e53Sbeck 
84471c6e53Sbeck 	rc = write(http->fd, buf, sz);
85df69c215Sderaadt 	if (rc == -1)
86471c6e53Sbeck 		warn("%s: write", http->src.ip);
879994a505Sbenno 	return rc;
88471c6e53Sbeck }
89471c6e53Sbeck 
90471c6e53Sbeck static ssize_t
dotlsread(char * buf,size_t sz,const struct http * http)91471c6e53Sbeck dotlsread(char *buf, size_t sz, const struct http *http)
92471c6e53Sbeck {
93471c6e53Sbeck 	ssize_t	 rc;
94471c6e53Sbeck 
95471c6e53Sbeck 	do {
96471c6e53Sbeck 		rc = tls_read(http->ctx, buf, sz);
974aad657bSderaadt 	} while (rc == TLS_WANT_POLLIN || rc == TLS_WANT_POLLOUT);
98471c6e53Sbeck 
99df69c215Sderaadt 	if (rc == -1)
100471c6e53Sbeck 		warnx("%s: tls_read: %s", http->src.ip,
101471c6e53Sbeck 		    tls_error(http->ctx));
1029994a505Sbenno 	return rc;
103471c6e53Sbeck }
104471c6e53Sbeck 
105471c6e53Sbeck static ssize_t
dotlswrite(const void * buf,size_t sz,const struct http * http)106471c6e53Sbeck dotlswrite(const void *buf, size_t sz, const struct http *http)
107471c6e53Sbeck {
108471c6e53Sbeck 	ssize_t	 rc;
109471c6e53Sbeck 
110471c6e53Sbeck 	do {
111471c6e53Sbeck 		rc = tls_write(http->ctx, buf, sz);
1124aad657bSderaadt 	} while (rc == TLS_WANT_POLLIN || rc == TLS_WANT_POLLOUT);
113471c6e53Sbeck 
114df69c215Sderaadt 	if (rc == -1)
115471c6e53Sbeck 		warnx("%s: tls_write: %s", http->src.ip,
116471c6e53Sbeck 		    tls_error(http->ctx));
1179994a505Sbenno 	return rc;
118471c6e53Sbeck }
119471c6e53Sbeck 
120471c6e53Sbeck int
http_init(void)121e0575c3eStb http_init(void)
122471c6e53Sbeck {
1234aad657bSderaadt 	if (tlscfg != NULL)
1249994a505Sbenno 		return 0;
125471c6e53Sbeck 
126471c6e53Sbeck 	tlscfg = tls_config_new();
1274aad657bSderaadt 	if (tlscfg == NULL) {
128471c6e53Sbeck 		warn("tls_config_new");
129471c6e53Sbeck 		goto err;
130471c6e53Sbeck 	}
131471c6e53Sbeck 
132fb0a89eeStedu 	if (tls_config_set_ca_file(tlscfg, tls_default_ca_cert_file()) == -1) {
133471c6e53Sbeck 		warn("tls_config_set_ca_file: %s", tls_config_error(tlscfg));
134471c6e53Sbeck 		goto err;
135471c6e53Sbeck 	}
136471c6e53Sbeck 
1379994a505Sbenno 	return 0;
138471c6e53Sbeck 
139471c6e53Sbeck  err:
140471c6e53Sbeck 	tls_config_free(tlscfg);
141471c6e53Sbeck 	tlscfg = NULL;
142471c6e53Sbeck 
1439994a505Sbenno 	return -1;
144471c6e53Sbeck }
145471c6e53Sbeck 
146471c6e53Sbeck static ssize_t
http_read(char * buf,size_t sz,const struct http * http)147471c6e53Sbeck http_read(char *buf, size_t sz, const struct http *http)
148471c6e53Sbeck {
149471c6e53Sbeck 	ssize_t	 ssz, xfer;
150471c6e53Sbeck 
151471c6e53Sbeck 	xfer = 0;
152471c6e53Sbeck 	do {
153471c6e53Sbeck 		if ((ssz = http->reader(buf, sz, http)) < 0)
1549994a505Sbenno 			return -1;
1554aad657bSderaadt 		if (ssz == 0)
156471c6e53Sbeck 			break;
157471c6e53Sbeck 		xfer += ssz;
158471c6e53Sbeck 		sz -= ssz;
159471c6e53Sbeck 		buf += ssz;
160471c6e53Sbeck 	} while (ssz > 0 && sz > 0);
161471c6e53Sbeck 
1629994a505Sbenno 	return xfer;
163471c6e53Sbeck }
164471c6e53Sbeck 
165471c6e53Sbeck static int
http_write(const char * buf,size_t sz,const struct http * http)166471c6e53Sbeck http_write(const char *buf, size_t sz, const struct http *http)
167471c6e53Sbeck {
168471c6e53Sbeck 	ssize_t	 ssz, xfer;
169471c6e53Sbeck 
170471c6e53Sbeck 	xfer = sz;
171471c6e53Sbeck 	while (sz > 0) {
172471c6e53Sbeck 		if ((ssz = http->writer(buf, sz, http)) < 0)
1739994a505Sbenno 			return -1;
174471c6e53Sbeck 		sz -= ssz;
175471c6e53Sbeck 		buf += (size_t)ssz;
176471c6e53Sbeck 	}
1779994a505Sbenno 	return xfer;
178471c6e53Sbeck }
179471c6e53Sbeck 
180471c6e53Sbeck void
http_disconnect(struct http * http)181471c6e53Sbeck http_disconnect(struct http *http)
182471c6e53Sbeck {
183471c6e53Sbeck 	int rc;
184471c6e53Sbeck 
1854aad657bSderaadt 	if (http->ctx != NULL) {
186471c6e53Sbeck 		/* TLS connection. */
187471c6e53Sbeck 		do {
188471c6e53Sbeck 			rc = tls_close(http->ctx);
1894aad657bSderaadt 		} while (rc == TLS_WANT_POLLIN || rc == TLS_WANT_POLLOUT);
190471c6e53Sbeck 
191471c6e53Sbeck 		if (rc < 0)
192471c6e53Sbeck 			warnx("%s: tls_close: %s", http->src.ip,
193471c6e53Sbeck 			    tls_error(http->ctx));
194471c6e53Sbeck 
195471c6e53Sbeck 		tls_free(http->ctx);
196471c6e53Sbeck 	}
1974aad657bSderaadt 	if (http->fd != -1) {
1984aad657bSderaadt 		if (close(http->fd) == -1)
199471c6e53Sbeck 			warn("%s: close", http->src.ip);
200471c6e53Sbeck 	}
201471c6e53Sbeck 
202471c6e53Sbeck 	http->fd = -1;
203471c6e53Sbeck 	http->ctx = NULL;
204471c6e53Sbeck }
205471c6e53Sbeck 
206471c6e53Sbeck void
http_free(struct http * http)207471c6e53Sbeck http_free(struct http *http)
208471c6e53Sbeck {
209471c6e53Sbeck 
2104aad657bSderaadt 	if (http == NULL)
211471c6e53Sbeck 		return;
212471c6e53Sbeck 	http_disconnect(http);
213471c6e53Sbeck 	free(http->host);
214471c6e53Sbeck 	free(http->path);
215471c6e53Sbeck 	free(http->src.ip);
216471c6e53Sbeck 	free(http);
217471c6e53Sbeck }
218471c6e53Sbeck 
219471c6e53Sbeck struct http *
http_alloc(const struct source * addrs,size_t addrsz,const char * host,short port,const char * path)220471c6e53Sbeck http_alloc(const struct source *addrs, size_t addrsz,
221471c6e53Sbeck     const char *host, short port, const char *path)
222471c6e53Sbeck {
223471c6e53Sbeck 	struct sockaddr_storage ss;
224471c6e53Sbeck 	int		 family, fd, c;
225471c6e53Sbeck 	socklen_t	 len;
226471c6e53Sbeck 	size_t		 cur, i = 0;
227471c6e53Sbeck 	struct http	*http;
228471c6e53Sbeck 
229471c6e53Sbeck 	/* Do this while we still have addresses to connect. */
230471c6e53Sbeck again:
231471c6e53Sbeck 	if (i == addrsz)
2329994a505Sbenno 		return NULL;
233471c6e53Sbeck 	cur = i++;
234471c6e53Sbeck 
235471c6e53Sbeck 	/* Convert to PF_INET or PF_INET6 address from string. */
236471c6e53Sbeck 
237471c6e53Sbeck 	memset(&ss, 0, sizeof(struct sockaddr_storage));
238471c6e53Sbeck 
2394aad657bSderaadt 	if (addrs[cur].family == 4) {
240471c6e53Sbeck 		family = PF_INET;
241471c6e53Sbeck 		((struct sockaddr_in *)&ss)->sin_family = AF_INET;
242471c6e53Sbeck 		((struct sockaddr_in *)&ss)->sin_port = htons(port);
243471c6e53Sbeck 		c = inet_pton(AF_INET, addrs[cur].ip,
244471c6e53Sbeck 		    &((struct sockaddr_in *)&ss)->sin_addr);
245471c6e53Sbeck 		len = sizeof(struct sockaddr_in);
2464aad657bSderaadt 	} else if (addrs[cur].family == 6) {
247471c6e53Sbeck 		family = PF_INET6;
248471c6e53Sbeck 		((struct sockaddr_in6 *)&ss)->sin6_family = AF_INET6;
249471c6e53Sbeck 		((struct sockaddr_in6 *)&ss)->sin6_port = htons(port);
250471c6e53Sbeck 		c = inet_pton(AF_INET6, addrs[cur].ip,
251471c6e53Sbeck 		    &((struct sockaddr_in6 *)&ss)->sin6_addr);
252471c6e53Sbeck 		len = sizeof(struct sockaddr_in6);
253471c6e53Sbeck 	} else {
254471c6e53Sbeck 		warnx("%s: unknown family", addrs[cur].ip);
255471c6e53Sbeck 		goto again;
256471c6e53Sbeck 	}
257471c6e53Sbeck 
258471c6e53Sbeck 	if (c < 0) {
259471c6e53Sbeck 		warn("%s: inet_ntop", addrs[cur].ip);
260471c6e53Sbeck 		goto again;
2614aad657bSderaadt 	} else if (c == 0) {
262471c6e53Sbeck 		warnx("%s: inet_ntop", addrs[cur].ip);
263471c6e53Sbeck 		goto again;
264471c6e53Sbeck 	}
265471c6e53Sbeck 
266471c6e53Sbeck 	/* Create socket and connect. */
267471c6e53Sbeck 
268471c6e53Sbeck 	fd = socket(family, SOCK_STREAM, 0);
2694aad657bSderaadt 	if (fd == -1) {
270471c6e53Sbeck 		warn("%s: socket", addrs[cur].ip);
271471c6e53Sbeck 		goto again;
2724aad657bSderaadt 	} else if (connect(fd, (struct sockaddr *)&ss, len) == -1) {
273471c6e53Sbeck 		warn("%s: connect", addrs[cur].ip);
274471c6e53Sbeck 		close(fd);
275471c6e53Sbeck 		goto again;
276471c6e53Sbeck 	}
277471c6e53Sbeck 
278471c6e53Sbeck 	/* Allocate the communicator. */
279471c6e53Sbeck 
280471c6e53Sbeck 	http = calloc(1, sizeof(struct http));
2814aad657bSderaadt 	if (http == NULL) {
282471c6e53Sbeck 		warn("calloc");
283471c6e53Sbeck 		close(fd);
2849994a505Sbenno 		return NULL;
285471c6e53Sbeck 	}
286471c6e53Sbeck 	http->fd = fd;
287471c6e53Sbeck 	http->port = port;
288471c6e53Sbeck 	http->src.family = addrs[cur].family;
289471c6e53Sbeck 	http->src.ip = strdup(addrs[cur].ip);
290471c6e53Sbeck 	http->host = strdup(host);
291471c6e53Sbeck 	http->path = strdup(path);
2924aad657bSderaadt 	if (http->src.ip == NULL || http->host == NULL || http->path == NULL) {
293471c6e53Sbeck 		warn("strdup");
294471c6e53Sbeck 		goto err;
295471c6e53Sbeck 	}
296471c6e53Sbeck 
297471c6e53Sbeck 	/* If necessary, do our TLS setup. */
298471c6e53Sbeck 
2994aad657bSderaadt 	if (port != 443) {
300471c6e53Sbeck 		http->writer = dosyswrite;
301471c6e53Sbeck 		http->reader = dosysread;
3029994a505Sbenno 		return http;
303471c6e53Sbeck 	}
304471c6e53Sbeck 
305471c6e53Sbeck 	http->writer = dotlswrite;
306471c6e53Sbeck 	http->reader = dotlsread;
307471c6e53Sbeck 
3084aad657bSderaadt 	if ((http->ctx = tls_client()) == NULL) {
309471c6e53Sbeck 		warn("tls_client");
310471c6e53Sbeck 		goto err;
3114aad657bSderaadt 	} else if (tls_configure(http->ctx, tlscfg) == -1) {
312471c6e53Sbeck 		warnx("%s: tls_configure: %s",
313471c6e53Sbeck 			http->src.ip, tls_error(http->ctx));
314471c6e53Sbeck 		goto err;
315471c6e53Sbeck 	}
316471c6e53Sbeck 
3174aad657bSderaadt 	if (tls_connect_socket(http->ctx, http->fd, http->host) != 0) {
318471c6e53Sbeck 		warnx("%s: tls_connect_socket: %s, %s", http->src.ip,
319471c6e53Sbeck 		    http->host, tls_error(http->ctx));
320471c6e53Sbeck 		goto err;
321471c6e53Sbeck 	}
322471c6e53Sbeck 
3239994a505Sbenno 	return http;
324471c6e53Sbeck err:
325471c6e53Sbeck 	http_free(http);
3269994a505Sbenno 	return NULL;
327471c6e53Sbeck }
328471c6e53Sbeck 
329471c6e53Sbeck struct httpxfer *
http_open(const struct http * http,const void * p,size_t psz)330471c6e53Sbeck http_open(const struct http *http, const void *p, size_t psz)
331471c6e53Sbeck {
332471c6e53Sbeck 	char		*req;
333471c6e53Sbeck 	int		 c;
334471c6e53Sbeck 	struct httpxfer	*trans;
335471c6e53Sbeck 
3364aad657bSderaadt 	if (p == NULL) {
337471c6e53Sbeck 		c = asprintf(&req,
338471c6e53Sbeck 		    "GET %s HTTP/1.0\r\n"
339471c6e53Sbeck 		    "Host: %s\r\n"
340471c6e53Sbeck 		    "\r\n",
341471c6e53Sbeck 		    http->path, http->host);
342471c6e53Sbeck 	} else {
343471c6e53Sbeck 		c = asprintf(&req,
344471c6e53Sbeck 		    "POST %s HTTP/1.0\r\n"
345471c6e53Sbeck 		    "Host: %s\r\n"
3464599153bSsthen 		    "Content-Type: application/ocsp-request\r\n"
347471c6e53Sbeck 		    "Content-Length: %zu\r\n"
348471c6e53Sbeck 		    "\r\n",
349471c6e53Sbeck 		    http->path, http->host, psz);
350471c6e53Sbeck 	}
3514aad657bSderaadt 	if (c == -1) {
352471c6e53Sbeck 		warn("asprintf");
3539994a505Sbenno 		return NULL;
354471c6e53Sbeck 	} else if (!http_write(req, c, http)) {
355471c6e53Sbeck 		free(req);
3569994a505Sbenno 		return NULL;
3574aad657bSderaadt 	} else if (p != NULL && !http_write(p, psz, http)) {
358471c6e53Sbeck 		free(req);
3599994a505Sbenno 		return NULL;
360471c6e53Sbeck 	}
361471c6e53Sbeck 
362471c6e53Sbeck 	free(req);
363471c6e53Sbeck 
364471c6e53Sbeck 	trans = calloc(1, sizeof(struct httpxfer));
3654aad657bSderaadt 	if (trans == NULL)
366471c6e53Sbeck 		warn("calloc");
3679994a505Sbenno 	return trans;
368471c6e53Sbeck }
369471c6e53Sbeck 
370471c6e53Sbeck void
http_close(struct httpxfer * x)371471c6e53Sbeck http_close(struct httpxfer *x)
372471c6e53Sbeck {
373471c6e53Sbeck 
3744aad657bSderaadt 	if (x == NULL)
375471c6e53Sbeck 		return;
376471c6e53Sbeck 	free(x->hbuf);
377471c6e53Sbeck 	free(x->bbuf);
378471c6e53Sbeck 	free(x->headbuf);
379471c6e53Sbeck 	free(x->head);
380471c6e53Sbeck 	free(x);
381471c6e53Sbeck }
382471c6e53Sbeck 
383471c6e53Sbeck /*
384471c6e53Sbeck  * Read the HTTP body from the wire.
385471c6e53Sbeck  * If invoked multiple times, this will return the same pointer with the
386471c6e53Sbeck  * same data (or NULL, if the original invocation returned NULL).
387471c6e53Sbeck  * Returns NULL if read or allocation errors occur.
388471c6e53Sbeck  * You must not free the returned pointer.
389471c6e53Sbeck  */
390471c6e53Sbeck char *
http_body_read(const struct http * http,struct httpxfer * trans,size_t * sz)391471c6e53Sbeck http_body_read(const struct http *http, struct httpxfer *trans, size_t *sz)
392471c6e53Sbeck {
393471c6e53Sbeck 	char		 buf[BUFSIZ];
394471c6e53Sbeck 	ssize_t		 ssz;
395471c6e53Sbeck 	void		*pp;
396471c6e53Sbeck 	size_t		 szp;
397471c6e53Sbeck 
3984aad657bSderaadt 	if (sz == NULL)
399471c6e53Sbeck 		sz = &szp;
400471c6e53Sbeck 
401471c6e53Sbeck 	/* Have we already parsed this? */
402471c6e53Sbeck 
403471c6e53Sbeck 	if (trans->bodyok > 0) {
404471c6e53Sbeck 		*sz = trans->bbufsz;
4059994a505Sbenno 		return trans->bbuf;
406471c6e53Sbeck 	} else if (trans->bodyok < 0)
4079994a505Sbenno 		return NULL;
408471c6e53Sbeck 
409471c6e53Sbeck 	*sz = 0;
410471c6e53Sbeck 	trans->bodyok = -1;
411471c6e53Sbeck 
412471c6e53Sbeck 	do {
413471c6e53Sbeck 		/* If less than sizeof(buf), at EOF. */
414471c6e53Sbeck 		if ((ssz = http_read(buf, sizeof(buf), http)) < 0)
4159994a505Sbenno 			return NULL;
4164aad657bSderaadt 		else if (ssz == 0)
417471c6e53Sbeck 			break;
418425fd4bbSderaadt 
419425fd4bbSderaadt 		pp = recallocarray(trans->bbuf,
420425fd4bbSderaadt 		    trans->bbufsz, trans->bbufsz + ssz, 1);
4214aad657bSderaadt 		if (pp == NULL) {
422425fd4bbSderaadt 			warn("recallocarray");
4239994a505Sbenno 			return NULL;
424471c6e53Sbeck 		}
425471c6e53Sbeck 		trans->bbuf = pp;
426471c6e53Sbeck 		memcpy(trans->bbuf + trans->bbufsz, buf, ssz);
427471c6e53Sbeck 		trans->bbufsz += ssz;
4289994a505Sbenno 	} while (ssz == sizeof(buf));
429471c6e53Sbeck 
430471c6e53Sbeck 	trans->bodyok = 1;
431471c6e53Sbeck 	*sz = trans->bbufsz;
4329994a505Sbenno 	return trans->bbuf;
433471c6e53Sbeck }
434471c6e53Sbeck 
435471c6e53Sbeck struct httphead *
http_head_get(const char * v,struct httphead * h,size_t hsz)436471c6e53Sbeck http_head_get(const char *v, struct httphead *h, size_t hsz)
437471c6e53Sbeck {
438471c6e53Sbeck 	size_t	 i;
439471c6e53Sbeck 
440471c6e53Sbeck 	for (i = 0; i < hsz; i++) {
441471c6e53Sbeck 		if (strcmp(h[i].key, v))
442471c6e53Sbeck 			continue;
4439994a505Sbenno 		return &h[i];
444471c6e53Sbeck 	}
4459994a505Sbenno 	return NULL;
446471c6e53Sbeck }
447471c6e53Sbeck 
448471c6e53Sbeck /*
449471c6e53Sbeck  * Look through the headers and determine our HTTP code.
450471c6e53Sbeck  * This will return -1 on failure, otherwise the code.
451471c6e53Sbeck  */
452471c6e53Sbeck int
http_head_status(const struct http * http,struct httphead * h,size_t sz)453471c6e53Sbeck http_head_status(const struct http *http, struct httphead *h, size_t sz)
454471c6e53Sbeck {
455471c6e53Sbeck 	int		 rc;
456471c6e53Sbeck 	unsigned int	 code;
457471c6e53Sbeck 	struct httphead *st;
458471c6e53Sbeck 
4594aad657bSderaadt 	if ((st = http_head_get("Status", h, sz)) == NULL) {
460471c6e53Sbeck 		warnx("%s: no status header", http->src.ip);
4619994a505Sbenno 		return -1;
462471c6e53Sbeck 	}
463471c6e53Sbeck 
464471c6e53Sbeck 	rc = sscanf(st->val, "%*s %u %*s", &code);
465471c6e53Sbeck 	if (rc < 0) {
466471c6e53Sbeck 		warn("sscanf");
4679994a505Sbenno 		return -1;
4684aad657bSderaadt 	} else if (rc != 1) {
469471c6e53Sbeck 		warnx("%s: cannot convert status header", http->src.ip);
4709994a505Sbenno 		return -1;
471471c6e53Sbeck 	}
4729994a505Sbenno 	return code;
473471c6e53Sbeck }
474471c6e53Sbeck 
475471c6e53Sbeck /*
476471c6e53Sbeck  * Parse headers from the transfer.
477471c6e53Sbeck  * Malformed headers are skipped.
478471c6e53Sbeck  * A special "Status" header is added for the HTTP status line.
479471c6e53Sbeck  * This can only happen once http_head_read has been called with
480471c6e53Sbeck  * success.
481471c6e53Sbeck  * This can be invoked multiple times: it will only parse the headers
482471c6e53Sbeck  * once and after that it will just return the cache.
483471c6e53Sbeck  * You must not free the returned pointer.
484471c6e53Sbeck  * If the original header parse failed, or if memory allocation fails
485471c6e53Sbeck  * internally, this returns NULL.
486471c6e53Sbeck  */
487471c6e53Sbeck struct httphead *
http_head_parse(const struct http * http,struct httpxfer * trans,size_t * sz)488471c6e53Sbeck http_head_parse(const struct http *http, struct httpxfer *trans, size_t *sz)
489471c6e53Sbeck {
490471c6e53Sbeck 	size_t		 hsz, szp;
491471c6e53Sbeck 	struct httphead	*h;
492471c6e53Sbeck 	char		*cp, *ep, *ccp, *buf;
493471c6e53Sbeck 
4944aad657bSderaadt 	if (sz == NULL)
495471c6e53Sbeck 		sz = &szp;
496471c6e53Sbeck 
497471c6e53Sbeck 	/*
498471c6e53Sbeck 	 * If we've already parsed the headers, return the
499471c6e53Sbeck 	 * previously-parsed buffer now.
500471c6e53Sbeck 	 * If we have errors on the stream, return NULL now.
501471c6e53Sbeck 	 */
502471c6e53Sbeck 
5034aad657bSderaadt 	if (trans->head != NULL) {
504471c6e53Sbeck 		*sz = trans->headsz;
5059994a505Sbenno 		return trans->head;
506471c6e53Sbeck 	} else if (trans->headok <= 0)
5079994a505Sbenno 		return NULL;
508471c6e53Sbeck 
5094aad657bSderaadt 	if ((buf = strdup(trans->hbuf)) == NULL) {
510471c6e53Sbeck 		warn("strdup");
5119994a505Sbenno 		return NULL;
512471c6e53Sbeck 	}
513471c6e53Sbeck 	hsz = 0;
514471c6e53Sbeck 	cp = buf;
515471c6e53Sbeck 
516471c6e53Sbeck 	do {
5174aad657bSderaadt 		if ((cp = strstr(cp, "\r\n")) != NULL)
518471c6e53Sbeck 			cp += 2;
519471c6e53Sbeck 		hsz++;
5204aad657bSderaadt 	} while (cp != NULL);
521471c6e53Sbeck 
522471c6e53Sbeck 	/*
523471c6e53Sbeck 	 * Allocate headers, then step through the data buffer, parsing
524471c6e53Sbeck 	 * out headers as we have them.
525fa0b7f9cSderaadt 	 * We know at this point that the buffer is NUL-terminated in
526471c6e53Sbeck 	 * the usual way.
527471c6e53Sbeck 	 */
528471c6e53Sbeck 
529471c6e53Sbeck 	h = calloc(hsz, sizeof(struct httphead));
5304aad657bSderaadt 	if (h == NULL) {
531471c6e53Sbeck 		warn("calloc");
532471c6e53Sbeck 		free(buf);
5339994a505Sbenno 		return NULL;
534471c6e53Sbeck 	}
535471c6e53Sbeck 
536471c6e53Sbeck 	*sz = hsz;
537471c6e53Sbeck 	hsz = 0;
538471c6e53Sbeck 	cp = buf;
539471c6e53Sbeck 
540471c6e53Sbeck 	do {
5414aad657bSderaadt 		if ((ep = strstr(cp, "\r\n")) != NULL) {
542471c6e53Sbeck 			*ep = '\0';
543471c6e53Sbeck 			ep += 2;
544471c6e53Sbeck 		}
5454aad657bSderaadt 		if (hsz == 0) {
546471c6e53Sbeck 			h[hsz].key = "Status";
547471c6e53Sbeck 			h[hsz++].val = cp;
548471c6e53Sbeck 			continue;
549471c6e53Sbeck 		}
550471c6e53Sbeck 
551471c6e53Sbeck 		/* Skip bad headers. */
5524aad657bSderaadt 		if ((ccp = strchr(cp, ':')) == NULL) {
553471c6e53Sbeck 			warnx("%s: header without separator", http->src.ip);
554471c6e53Sbeck 			continue;
555471c6e53Sbeck 		}
556471c6e53Sbeck 
557471c6e53Sbeck 		*ccp++ = '\0';
558fa433bfeSflorian 		while (isspace((unsigned char)*ccp))
559471c6e53Sbeck 			ccp++;
560471c6e53Sbeck 		h[hsz].key = cp;
561471c6e53Sbeck 		h[hsz++].val = ccp;
5624aad657bSderaadt 	} while ((cp = ep) != NULL);
563471c6e53Sbeck 
564471c6e53Sbeck 	trans->headbuf = buf;
565471c6e53Sbeck 	trans->head = h;
566471c6e53Sbeck 	trans->headsz = hsz;
5679994a505Sbenno 	return h;
568471c6e53Sbeck }
569471c6e53Sbeck 
570471c6e53Sbeck /*
571471c6e53Sbeck  * Read the HTTP headers from the wire.
572471c6e53Sbeck  * If invoked multiple times, this will return the same pointer with the
573471c6e53Sbeck  * same data (or NULL, if the original invocation returned NULL).
574471c6e53Sbeck  * Returns NULL if read or allocation errors occur.
575471c6e53Sbeck  * You must not free the returned pointer.
576471c6e53Sbeck  */
577471c6e53Sbeck char *
http_head_read(const struct http * http,struct httpxfer * trans,size_t * sz)578471c6e53Sbeck http_head_read(const struct http *http, struct httpxfer *trans, size_t *sz)
579471c6e53Sbeck {
580471c6e53Sbeck 	char		 buf[BUFSIZ];
581471c6e53Sbeck 	ssize_t		 ssz;
582471c6e53Sbeck 	char		*ep;
583471c6e53Sbeck 	void		*pp;
584471c6e53Sbeck 	size_t		 szp;
585471c6e53Sbeck 
5864aad657bSderaadt 	if (sz == NULL)
587471c6e53Sbeck 		sz = &szp;
588471c6e53Sbeck 
589471c6e53Sbeck 	/* Have we already parsed this? */
590471c6e53Sbeck 
591471c6e53Sbeck 	if (trans->headok > 0) {
592471c6e53Sbeck 		*sz = trans->hbufsz;
5939994a505Sbenno 		return trans->hbuf;
594471c6e53Sbeck 	} else if (trans->headok < 0)
5959994a505Sbenno 		return NULL;
596471c6e53Sbeck 
597471c6e53Sbeck 	*sz = 0;
598471c6e53Sbeck 	ep = NULL;
599471c6e53Sbeck 	trans->headok = -1;
600471c6e53Sbeck 
601471c6e53Sbeck 	/*
602471c6e53Sbeck 	 * Begin by reading by BUFSIZ blocks until we reach the header
603471c6e53Sbeck 	 * termination marker (two CRLFs).
604471c6e53Sbeck 	 * We might read into our body, but that's ok: we'll copy out
605471c6e53Sbeck 	 * the body parts into our body buffer afterward.
606471c6e53Sbeck 	 */
607471c6e53Sbeck 
608471c6e53Sbeck 	do {
609471c6e53Sbeck 		/* If less than sizeof(buf), at EOF. */
610471c6e53Sbeck 		if ((ssz = http_read(buf, sizeof(buf), http)) < 0)
6119994a505Sbenno 			return NULL;
6124aad657bSderaadt 		else if (ssz == 0)
613471c6e53Sbeck 			break;
614471c6e53Sbeck 		pp = realloc(trans->hbuf, trans->hbufsz + ssz);
6154aad657bSderaadt 		if (pp == NULL) {
616471c6e53Sbeck 			warn("realloc");
6179994a505Sbenno 			return NULL;
618471c6e53Sbeck 		}
619471c6e53Sbeck 		trans->hbuf = pp;
620471c6e53Sbeck 		memcpy(trans->hbuf + trans->hbufsz, buf, ssz);
621471c6e53Sbeck 		trans->hbufsz += ssz;
622471c6e53Sbeck 		/* Search for end of headers marker. */
623471c6e53Sbeck 		ep = memmem(trans->hbuf, trans->hbufsz, "\r\n\r\n", 4);
6244aad657bSderaadt 	} while (ep == NULL && ssz == sizeof(buf));
625471c6e53Sbeck 
6264aad657bSderaadt 	if (ep == NULL) {
627471c6e53Sbeck 		warnx("%s: partial transfer", http->src.ip);
6289994a505Sbenno 		return NULL;
629471c6e53Sbeck 	}
630471c6e53Sbeck 	*ep = '\0';
631471c6e53Sbeck 
632471c6e53Sbeck 	/*
633471c6e53Sbeck 	 * The header data is invalid if it has any binary characters in
634471c6e53Sbeck 	 * it: check that now.
635471c6e53Sbeck 	 * This is important because we want to guarantee that all
636fa0b7f9cSderaadt 	 * header keys and pairs are properly NUL-terminated.
637471c6e53Sbeck 	 */
638471c6e53Sbeck 
639471c6e53Sbeck 	if (strlen(trans->hbuf) != (uintptr_t)(ep - trans->hbuf)) {
640471c6e53Sbeck 		warnx("%s: binary data in header", http->src.ip);
6419994a505Sbenno 		return NULL;
642471c6e53Sbeck 	}
643471c6e53Sbeck 
644471c6e53Sbeck 	/*
645471c6e53Sbeck 	 * Copy remaining buffer into body buffer.
646471c6e53Sbeck 	 */
647471c6e53Sbeck 
648471c6e53Sbeck 	ep += 4;
649471c6e53Sbeck 	trans->bbufsz = (trans->hbuf + trans->hbufsz) - ep;
650471c6e53Sbeck 	trans->bbuf = malloc(trans->bbufsz);
6514aad657bSderaadt 	if (trans->bbuf == NULL) {
652471c6e53Sbeck 		warn("malloc");
6539994a505Sbenno 		return NULL;
654471c6e53Sbeck 	}
655471c6e53Sbeck 	memcpy(trans->bbuf, ep, trans->bbufsz);
656471c6e53Sbeck 
657471c6e53Sbeck 	trans->headok = 1;
658471c6e53Sbeck 	*sz = trans->hbufsz;
6599994a505Sbenno 	return trans->hbuf;
660471c6e53Sbeck }
661471c6e53Sbeck 
662471c6e53Sbeck void
http_get_free(struct httpget * g)663471c6e53Sbeck http_get_free(struct httpget *g)
664471c6e53Sbeck {
665471c6e53Sbeck 
6664aad657bSderaadt 	if (g == NULL)
667471c6e53Sbeck 		return;
668471c6e53Sbeck 	http_close(g->xfer);
669471c6e53Sbeck 	http_free(g->http);
670471c6e53Sbeck 	free(g);
671471c6e53Sbeck }
672471c6e53Sbeck 
673471c6e53Sbeck 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)674471c6e53Sbeck http_get(const struct source *addrs, size_t addrsz, const char *domain,
675471c6e53Sbeck     short port, const char *path, const void *post, size_t postsz)
676471c6e53Sbeck {
677471c6e53Sbeck 	struct http	*h;
678471c6e53Sbeck 	struct httpxfer	*x;
679471c6e53Sbeck 	struct httpget	*g;
680471c6e53Sbeck 	struct httphead	*head;
681471c6e53Sbeck 	size_t		 headsz, bodsz, headrsz;
682471c6e53Sbeck 	int		 code;
683471c6e53Sbeck 	char		*bod, *headr;
684471c6e53Sbeck 
685471c6e53Sbeck 	h = http_alloc(addrs, addrsz, domain, port, path);
6864aad657bSderaadt 	if (h == NULL)
6879994a505Sbenno 		return NULL;
688471c6e53Sbeck 
6894aad657bSderaadt 	if ((x = http_open(h, post, postsz)) == NULL) {
690471c6e53Sbeck 		http_free(h);
6919994a505Sbenno 		return NULL;
6924aad657bSderaadt 	} else if ((headr = http_head_read(h, x, &headrsz)) == NULL) {
693471c6e53Sbeck 		http_close(x);
694471c6e53Sbeck 		http_free(h);
6959994a505Sbenno 		return NULL;
6964aad657bSderaadt 	} else if ((bod = http_body_read(h, x, &bodsz)) == NULL) {
697471c6e53Sbeck 		http_close(x);
698471c6e53Sbeck 		http_free(h);
6999994a505Sbenno 		return NULL;
700471c6e53Sbeck 	}
701471c6e53Sbeck 
702471c6e53Sbeck 	http_disconnect(h);
703471c6e53Sbeck 
7044aad657bSderaadt 	if ((head = http_head_parse(h, x, &headsz)) == NULL) {
705471c6e53Sbeck 		http_close(x);
706471c6e53Sbeck 		http_free(h);
7079994a505Sbenno 		return NULL;
708471c6e53Sbeck 	} else if ((code = http_head_status(h, head, headsz)) < 0) {
709471c6e53Sbeck 		http_close(x);
710471c6e53Sbeck 		http_free(h);
7119994a505Sbenno 		return NULL;
712471c6e53Sbeck 	}
713471c6e53Sbeck 
7144aad657bSderaadt 	if ((g = calloc(1, sizeof(struct httpget))) == NULL) {
715471c6e53Sbeck 		warn("calloc");
716471c6e53Sbeck 		http_close(x);
717471c6e53Sbeck 		http_free(h);
7189994a505Sbenno 		return NULL;
719471c6e53Sbeck 	}
720471c6e53Sbeck 
721471c6e53Sbeck 	g->headpart = headr;
722471c6e53Sbeck 	g->headpartsz = headrsz;
723471c6e53Sbeck 	g->bodypart = bod;
724471c6e53Sbeck 	g->bodypartsz = bodsz;
725471c6e53Sbeck 	g->head = head;
726471c6e53Sbeck 	g->headsz = headsz;
727471c6e53Sbeck 	g->code = code;
728471c6e53Sbeck 	g->xfer = x;
729471c6e53Sbeck 	g->http = h;
7309994a505Sbenno 	return g;
731471c6e53Sbeck }
732471c6e53Sbeck 
733471c6e53Sbeck #if 0
734471c6e53Sbeck int
735471c6e53Sbeck main(void)
736471c6e53Sbeck {
737471c6e53Sbeck 	struct httpget	*g;
738471c6e53Sbeck 	struct httphead	*httph;
739471c6e53Sbeck 	size_t		 i, httphsz;
740471c6e53Sbeck 	struct source	 addrs[2];
741471c6e53Sbeck 	size_t		 addrsz;
742471c6e53Sbeck 
743471c6e53Sbeck #if 0
744471c6e53Sbeck 	addrs[0].ip = "127.0.0.1";
745471c6e53Sbeck 	addrs[0].family = 4;
746471c6e53Sbeck 	addrsz = 1;
747471c6e53Sbeck #else
748471c6e53Sbeck 	addrs[0].ip = "2a00:1450:400a:806::2004";
749471c6e53Sbeck 	addrs[0].family = 6;
750471c6e53Sbeck 	addrs[1].ip = "193.135.3.123";
751471c6e53Sbeck 	addrs[1].family = 4;
752471c6e53Sbeck 	addrsz = 2;
753471c6e53Sbeck #endif
754471c6e53Sbeck 
755471c6e53Sbeck 	if (http_init() == -1)
756471c6e53Sbeck 		errx(EXIT_FAILURE, "http_init");
757471c6e53Sbeck 
758471c6e53Sbeck #if 0
759471c6e53Sbeck 	g = http_get(addrs, addrsz, "localhost", 80, "/index.html");
760471c6e53Sbeck #else
761471c6e53Sbeck 	g = http_get(addrs, addrsz, "www.google.ch", 80, "/index.html",
762471c6e53Sbeck 	    NULL, 0);
763471c6e53Sbeck #endif
764471c6e53Sbeck 
7654aad657bSderaadt 	if (g == NULL)
766471c6e53Sbeck 		errx(EXIT_FAILURE, "http_get");
767471c6e53Sbeck 
768471c6e53Sbeck 	httph = http_head_parse(g->http, g->xfer, &httphsz);
769471c6e53Sbeck 	warnx("code: %d", g->code);
770471c6e53Sbeck 
771471c6e53Sbeck 	for (i = 0; i < httphsz; i++)
772471c6e53Sbeck 		warnx("head: [%s]=[%s]", httph[i].key, httph[i].val);
773471c6e53Sbeck 
774471c6e53Sbeck 	http_get_free(g);
775471c6e53Sbeck 	return (EXIT_SUCCESS);
776471c6e53Sbeck }
777471c6e53Sbeck #endif
778