xref: /openbsd/usr.sbin/rpki-client/http.c (revision 097a140d)
1 /*	$OpenBSD: http.c,v 1.32 2021/04/20 14:32:49 claudio Exp $  */
2 /*
3  * Copyright (c) 2020 Nils Fisher <nils_fisher@hotmail.com>
4  * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*-
20  * Copyright (c) 1997 The NetBSD Foundation, Inc.
21  * All rights reserved.
22  *
23  * This code is derived from software contributed to The NetBSD Foundation
24  * by Jason Thorpe and Luke Mewburn.
25  *
26  * Redistribution and use in source and binary forms, with or without
27  * modification, are permitted provided that the following conditions
28  * are met:
29  * 1. Redistributions of source code must retain the above copyright
30  *    notice, this list of conditions and the following disclaimer.
31  * 2. Redistributions in binary form must reproduce the above copyright
32  *    notice, this list of conditions and the following disclaimer in the
33  *    documentation and/or other materials provided with the distribution.
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
36  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
37  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
38  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
39  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
40  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
41  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
42  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
43  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
45  * POSSIBILITY OF SUCH DAMAGE.
46  */
47 #include <sys/types.h>
48 #include <sys/queue.h>
49 #include <sys/socket.h>
50 
51 #include <assert.h>
52 #include <ctype.h>
53 #include <err.h>
54 #include <errno.h>
55 #include <limits.h>
56 #include <netdb.h>
57 #include <poll.h>
58 #include <signal.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 #include <vis.h>
64 #include <imsg.h>
65 
66 #include <tls.h>
67 
68 #include "extern.h"
69 
70 #define HTTP_USER_AGENT		"OpenBSD rpki-client"
71 #define HTTP_BUF_SIZE		(32 * 1024)
72 #define HTTP_IDLE_TIMEOUT	10
73 #define MAX_CONNECTIONS		64
74 #define NPFDS			(MAX_CONNECTIONS + 1)
75 
76 enum res {
77 	DONE,
78 	WANT_POLLIN,
79 	WANT_POLLOUT,
80 };
81 
82 enum http_state {
83 	STATE_FREE,
84 	STATE_CONNECT,
85 	STATE_TLSCONNECT,
86 	STATE_REQUEST,
87 	STATE_RESPONSE_STATUS,
88 	STATE_RESPONSE_HEADER,
89 	STATE_RESPONSE_DATA,
90 	STATE_RESPONSE_CHUNKED_HEADER,
91 	STATE_RESPONSE_CHUNKED_TRAILER,
92 	STATE_WRITE_DATA,
93 	STATE_IDLE,
94 	STATE_CLOSE,
95 };
96 
97 struct http_proxy {
98 	char	*proxyhost;
99 	char	*proxyuser;
100 	char	*proxypw;
101 };
102 
103 struct http_connection {
104 	LIST_ENTRY(http_connection)	entry;
105 	char			*host;
106 	char			*port;
107 	char			*last_modified;
108 	char			*redir_uri;
109 	struct http_request	*req;
110 	struct pollfd		*pfd;
111 	struct addrinfo		*res0;
112 	struct addrinfo		*res;
113 	struct tls		*tls;
114 	char			*buf;
115 	size_t			bufsz;
116 	size_t			bufpos;
117 	off_t			iosz;
118 	time_t			idle_time;
119 	int			status;
120 	int			fd;
121 	int			chunked;
122 	int			keep_alive;
123 	short			events;
124 	enum http_state		state;
125 };
126 
127 LIST_HEAD(http_conn_list, http_connection);
128 
129 struct http_request {
130 	TAILQ_ENTRY(http_request)	entry;
131 	char			*uri;
132 	char			*modified_since;
133 	char			*host;
134 	char			*port;
135 	const char		*path;	/* points into uri */
136 	size_t			 id;
137 	int			 outfd;
138 	int			 redirect_loop;
139 };
140 
141 TAILQ_HEAD(http_req_queue, http_request);
142 
143 static struct http_conn_list	active = LIST_HEAD_INITIALIZER(active);
144 static struct http_conn_list	idle = LIST_HEAD_INITIALIZER(idle);
145 static struct http_req_queue	queue = TAILQ_HEAD_INITIALIZER(queue);
146 static size_t http_conn_count;
147 
148 static struct msgbuf msgq;
149 static struct sockaddr_storage http_bindaddr;
150 static struct tls_config *tls_config;
151 static uint8_t *tls_ca_mem;
152 static size_t tls_ca_size;
153 
154 /* HTTP request API */
155 static void	http_req_new(size_t, char *, char *, int);
156 static void	http_req_free(struct http_request *);
157 static void	http_req_done(size_t, enum http_result, const char *);
158 static void	http_req_fail(size_t);
159 static int	http_req_schedule(struct http_request *);
160 
161 /* HTTP connection API */
162 static void	http_new(struct http_request *);
163 static void	http_free(struct http_connection *);
164 
165 static enum res http_done(struct http_connection *, enum http_result);
166 static enum res http_failed(struct http_connection *);
167 
168 /* HTTP connection FSM functions */
169 static void	http_do(struct http_connection *,
170 		    enum res (*)(struct http_connection *));
171 
172 /* These functions can be used with http_do() */
173 static enum res	http_connect(struct http_connection *);
174 static enum res	http_request(struct http_connection *);
175 static enum res	http_close(struct http_connection *);
176 static enum res http_handle(struct http_connection *);
177 
178 /* Internal state functions used by the above functions */
179 static enum res	http_finish_connect(struct http_connection *);
180 static enum res	http_tls_connect(struct http_connection *);
181 static enum res	http_tls_handshake(struct http_connection *);
182 static enum res	http_read(struct http_connection *);
183 static enum res	http_write(struct http_connection *);
184 static enum res	data_write(struct http_connection *);
185 
186 static time_t
187 getmonotime(void)
188 {
189 	struct timespec ts;
190 
191 	if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
192 		err(1, "clock_gettime");
193 	return (ts.tv_sec);
194 }
195 
196 /*
197  * Return a string that can be used in error message to identify the
198  * connection.
199  */
200 static const char *
201 http_info(const char *uri)
202 {
203 	static char buf[80];
204 
205 	if (strnvis(buf, uri, sizeof buf, VIS_SAFE) >= (int)sizeof buf) {
206 		/* overflow, add indicator */
207 		memcpy(buf + sizeof buf - 4, "...", 4);
208 	}
209 
210 	return buf;
211 }
212 
213 /*
214  * Determine whether the character needs encoding, per RFC1738:
215  *	- No corresponding graphic US-ASCII.
216  *	- Unsafe characters.
217  */
218 static int
219 unsafe_char(const char *c0)
220 {
221 	const char *unsafe_chars = " <>\"#{}|\\^~[]`";
222 	const unsigned char *c = (const unsigned char *)c0;
223 
224 	/*
225 	 * No corresponding graphic US-ASCII.
226 	 * Control characters and octets not used in US-ASCII.
227 	 */
228 	return (iscntrl(*c) || !isascii(*c) ||
229 
230 	    /*
231 	     * Unsafe characters.
232 	     * '%' is also unsafe, if is not followed by two
233 	     * hexadecimal digits.
234 	     */
235 	    strchr(unsafe_chars, *c) != NULL ||
236 	    (*c == '%' && (!isxdigit(c[1]) || !isxdigit(c[2]))));
237 }
238 
239 /*
240  * Encode given URL, per RFC1738.
241  * Allocate and return string to the caller.
242  */
243 static char *
244 url_encode(const char *path)
245 {
246 	size_t i, length, new_length;
247 	char *epath, *epathp;
248 
249 	length = new_length = strlen(path);
250 
251 	/*
252 	 * First pass:
253 	 * Count unsafe characters, and determine length of the
254 	 * final URL.
255 	 */
256 	for (i = 0; i < length; i++)
257 		if (unsafe_char(path + i))
258 			new_length += 2;
259 
260 	epath = epathp = malloc(new_length + 1);	/* One more for '\0'. */
261 	if (epath == NULL)
262 		err(1, NULL);
263 
264 	/*
265 	 * Second pass:
266 	 * Encode, and copy final URL.
267 	 */
268 	for (i = 0; i < length; i++)
269 		if (unsafe_char(path + i)) {
270 			snprintf(epathp, 4, "%%" "%02x",
271 			    (unsigned char)path[i]);
272 			epathp += 3;
273 		} else
274 			*(epathp++) = path[i];
275 
276 	*epathp = '\0';
277 	return (epath);
278 }
279 
280 /*
281  * Parse a URI and split it up into host, port and path.
282  * Does some basic URI validation. Both host and port need to be freed
283  * by the caller whereas path points into the uri.
284  */
285 static int
286 http_parse_uri(char *uri, char **ohost, char **oport, char **opath)
287 {
288 	char *host, *port = NULL, *path;
289 	char *hosttail;
290 
291 	if (strncasecmp(uri, "https://", 8) != 0) {
292 		warnx("%s: not using https schema", http_info(uri));
293 		return -1;
294 	}
295 	host = uri + 8;
296 	if ((path = strchr(host, '/')) == NULL) {
297 		warnx("%s: missing https path", http_info(uri));
298 		return -1;
299 	}
300 	if (path - uri > INT_MAX - 1) {
301 		warnx("%s: preposterous host length", http_info(uri));
302 		return -1;
303 	}
304 	if (*host == '[') {
305 		char *scope;
306 		if ((hosttail = memrchr(host, ']', path - host)) == NULL) {
307 			warnx("%s: unmatched opening bracket", http_info(uri));
308 			return -1;
309 		}
310 		if (hosttail[1] == '/' || hosttail[1] == ':')
311 			host++;
312 		if (hosttail[1] == ':')
313 			port = hosttail + 2;
314 		if ((scope = memchr(host, '%', hosttail - host)) != NULL)
315 			hosttail = scope;
316 	} else {
317 		if ((hosttail = memrchr(host, ':', path - host)) != NULL)
318 			port = hosttail + 1;
319 		else
320 			hosttail = path;
321 	}
322 
323 	if ((host = strndup(host, hosttail - host)) == NULL)
324 		err(1, NULL);
325 	if (port != NULL) {
326 		if ((port = strndup(port, path - port)) == NULL)
327 			err(1, NULL);
328 	} else {
329 		if ((port = strdup("443")) == NULL)
330 			err(1, NULL);
331 	}
332 	/* do not include the initial / in path */
333 	path++;
334 
335 	*ohost = host;
336 	*oport = port;
337 	*opath = path;
338 
339 	return 0;
340 }
341 
342 /*
343  * Lookup the IP addresses for host:port.
344  * Returns 0 on success and -1 on failure.
345  */
346 static int
347 http_resolv(struct addrinfo **res, const char *host, const char *port)
348 {
349 	struct addrinfo hints;
350 	int error;
351 
352 	memset(&hints, 0, sizeof(hints));
353 	hints.ai_family = PF_UNSPEC;
354 	hints.ai_socktype = SOCK_STREAM;
355 	error = getaddrinfo(host, port, &hints, res);
356 	/*
357 	 * If the services file is corrupt/missing, fall back
358 	 * on our hard-coded defines.
359 	 */
360 	if (error == EAI_SERVICE)
361 		error = getaddrinfo(host, "443", &hints, res);
362 	if (error != 0) {
363 		warnx("%s: %s", host, gai_strerror(error));
364 		return -1;
365 	}
366 
367 	return 0;
368 }
369 
370 /*
371  * Create and queue a new request.
372  */
373 static void
374 http_req_new(size_t id, char *uri, char *modified_since, int outfd)
375 {
376 	struct http_request *req;
377 	char *host, *port, *path;
378 
379 	if (http_parse_uri(uri, &host, &port, &path) == -1) {
380 		free(uri);
381 		free(modified_since);
382 		close(outfd);
383 		http_req_fail(id);
384 		return;
385 	}
386 
387 	if ((req = calloc(1, sizeof(*req))) == NULL)
388 		err(1, NULL);
389 
390 	req->id = id;
391 	req->outfd = outfd;
392 	req->host = host;
393 	req->port = port;
394 	req->path = path;
395 	req->uri = uri;
396 	req->modified_since = modified_since;
397 
398 	TAILQ_INSERT_TAIL(&queue, req, entry);
399 }
400 
401 /*
402  * Free a request, request is not allowed to be on the req queue.
403  */
404 static void
405 http_req_free(struct http_request *req)
406 {
407 	if (req == NULL)
408 		return;
409 
410 	free(req->host);
411 	free(req->port);
412 	/* no need to free req->path it points into req->uri */
413 	free(req->uri);
414 	free(req->modified_since);
415 
416 	if (req->outfd != -1)
417 		close(req->outfd);
418 }
419 
420 /*
421  * Enqueue request response
422  */
423 static void
424 http_req_done(size_t id, enum http_result res, const char *last_modified)
425 {
426 	struct ibuf *b;
427 
428 	if ((b = ibuf_dynamic(64, UINT_MAX)) == NULL)
429 		err(1, NULL);
430 	io_simple_buffer(b, &id, sizeof(id));
431 	io_simple_buffer(b, &res, sizeof(res));
432 	io_str_buffer(b, last_modified);
433 	ibuf_close(&msgq, b);
434 }
435 
436 /*
437  * Enqueue request failure response
438  */
439 static void
440 http_req_fail(size_t id)
441 {
442 	struct ibuf *b;
443 	enum http_result res = HTTP_FAILED;
444 
445 	if ((b = ibuf_dynamic(8, UINT_MAX)) == NULL)
446 		err(1, NULL);
447 	io_simple_buffer(b, &id, sizeof(id));
448 	io_simple_buffer(b, &res, sizeof(res));
449 	io_str_buffer(b, NULL);
450 	ibuf_close(&msgq, b);
451 }
452 
453 /*
454  * Schedule new requests until maximum number of connections is reached.
455  * Try to reuse an idle connection if one exists that matches host and port.
456  */
457 static int
458 http_req_schedule(struct http_request *req)
459 {
460 	struct http_connection *conn;
461 
462 	TAILQ_REMOVE(&queue, req, entry);
463 
464 	/* check list of idle connections first */
465 	LIST_FOREACH(conn, &idle, entry) {
466 		if (strcmp(conn->host, req->host) != 0)
467 			continue;
468 		if (strcmp(conn->port, req->port) != 0)
469 			continue;
470 
471 		LIST_REMOVE(conn, entry);
472 		LIST_INSERT_HEAD(&active, conn, entry);
473 
474 		/* use established connection */
475 		conn->req = req;
476 		conn->idle_time = 0;
477 
478 		/* start request */
479 		http_do(conn, http_request);
480 		if (conn->state == STATE_FREE)
481 			http_free(conn);
482 		return 1;
483 	}
484 
485 	if (http_conn_count < MAX_CONNECTIONS) {
486 		http_new(req);
487 		return 1;
488 	}
489 
490 	/* no more slots free, requeue */
491 	TAILQ_INSERT_HEAD(&queue, req, entry);
492 	return 0;
493 }
494 
495 /*
496  * Create a new HTTP connection which will be used for the HTTP request req.
497  * On errors a req faulure is issued and both connection and request are freed.
498  */
499 static void
500 http_new(struct http_request *req)
501 {
502 	struct http_connection *conn;
503 
504 	if ((conn = calloc(1, sizeof(*conn))) == NULL)
505 		err(1, NULL);
506 
507 	conn->fd = -1;
508 	conn->req = req;
509 	if ((conn->host = strdup(req->host)) == NULL)
510 		err(1, NULL);
511 	if ((conn->port = strdup(req->port)) == NULL)
512 		err(1, NULL);
513 
514 	LIST_INSERT_HEAD(&active, conn, entry);
515 	http_conn_count++;
516 
517 	/* TODO proxy support (overload of host and port) */
518 
519 	if (http_resolv(&conn->res0, conn->host, conn->port) == -1) {
520 		http_req_fail(req->id);
521 		http_free(conn);
522 		return;
523 	}
524 
525 	/* connect and start request */
526 	http_do(conn, http_connect);
527 	if (conn->state == STATE_FREE)
528 		http_free(conn);
529 }
530 
531 /*
532  * Free a no longer active connection, releasing all memory and closing
533  * any open file descriptor.
534  */
535 static void
536 http_free(struct http_connection *conn)
537 {
538 	assert(conn->state == STATE_FREE);
539 
540 	LIST_REMOVE(conn, entry);
541 	http_conn_count--;
542 
543 	http_req_free(conn->req);
544 	free(conn->host);
545 	free(conn->port);
546 	free(conn->last_modified);
547 	free(conn->redir_uri);
548 	free(conn->buf);
549 
550 	if (conn->res0 != NULL)
551 		freeaddrinfo(conn->res0);
552 
553 	tls_free(conn->tls);
554 
555 	if (conn->fd != -1)
556 		close(conn->fd);
557 	free(conn);
558 }
559 
560 /*
561  * Called when a request on this connection is finished.
562  * Move connection into idle state and onto idle queue.
563  * If there is a request connected to it send back a response
564  * with http_result res, else ignore the res.
565  */
566 static enum res
567 http_done(struct http_connection *conn, enum http_result res)
568 {
569 	assert(conn->bufpos == 0);
570 	assert(conn->iosz == 0);
571 	assert(conn->chunked == 0);
572 	assert(conn->redir_uri == NULL);
573 
574 	conn->state = STATE_IDLE;
575 	conn->idle_time = getmonotime() + HTTP_IDLE_TIMEOUT;
576 
577 	if (conn->req) {
578 		http_req_done(conn->req->id, res, conn->last_modified);
579 		http_req_free(conn->req);
580 		conn->req = NULL;
581 	}
582 
583 	if (!conn->keep_alive)
584 		return http_close(conn);
585 
586 	LIST_REMOVE(conn, entry);
587 	LIST_INSERT_HEAD(&idle, conn, entry);
588 
589 	/* reset status and keep-alive for good measures */
590 	conn->status = 0;
591 	conn->keep_alive = 0;
592 
593 	return WANT_POLLIN;
594 }
595 
596 /*
597  * Called in case of error, moves connection into free state.
598  * This will skip proper shutdown of the TLS session.
599  * If a request is pending fail and free the request.
600  */
601 static enum res
602 http_failed(struct http_connection *conn)
603 {
604 	conn->state = STATE_FREE;
605 
606 	if (conn->req) {
607 		http_req_fail(conn->req->id);
608 		http_req_free(conn->req);
609 		conn->req = NULL;
610 	}
611 
612 	return DONE;
613 }
614 
615 /*
616  * Call the function f and update the connection events based
617  * on the return value.
618  */
619 static void
620 http_do(struct http_connection *conn, enum res (*f)(struct http_connection *))
621 {
622 	switch (f(conn)) {
623 	case DONE:
624 		conn->events = 0;
625 		break;
626 	case WANT_POLLIN:
627 		conn->events = POLLIN;
628 		break;
629 	case WANT_POLLOUT:
630 		conn->events = POLLOUT;
631 		break;
632 	default:
633 		errx(1, "%s: unexpected function return",
634 		    http_info(conn->host));
635 	}
636 }
637 
638 /*
639  * Connection successfully establish, initiate TLS handshake.
640  */
641 static enum res
642 http_connect_done(struct http_connection *conn)
643 {
644 	freeaddrinfo(conn->res0);
645 	conn->res0 = NULL;
646 	conn->res = NULL;
647 
648 #if 0
649 	/* TODO proxy connect */
650 	if (proxyenv)
651 		proxy_connect(conn->fd, sslhost, proxy_credentials); */
652 #endif
653 
654 	return http_tls_connect(conn);
655 }
656 
657 /*
658  * Start an asynchronous connect.
659  */
660 static enum res
661 http_connect(struct http_connection *conn)
662 {
663 	const char *cause = NULL;
664 
665 	assert(conn->fd == -1);
666 	conn->state = STATE_CONNECT;
667 
668 	/* start the loop below with first or next address */
669 	if (conn->res == NULL)
670 		conn->res = conn->res0;
671 	else
672 		conn->res = conn->res->ai_next;
673 	for (; conn->res != NULL; conn->res = conn->res->ai_next) {
674 		struct addrinfo *res = conn->res;
675 		int fd, save_errno;
676 
677 		fd = socket(res->ai_family,
678 		    res->ai_socktype | SOCK_NONBLOCK, res->ai_protocol);
679 		if (fd == -1) {
680 			cause = "socket";
681 			continue;
682 		}
683 		conn->fd = fd;
684 
685 		if (http_bindaddr.ss_family == res->ai_family) {
686 			if (bind(conn->fd, (struct sockaddr *)&http_bindaddr,
687 			    res->ai_addrlen) == -1) {
688 				save_errno = errno;
689 				close(conn->fd);
690 				conn->fd = -1;
691 				errno = save_errno;
692 				cause = "bind";
693 				continue;
694 			}
695 		}
696 
697 		if (connect(conn->fd, res->ai_addr, res->ai_addrlen) == -1) {
698 			if (errno == EINPROGRESS) {
699 				/* wait for async connect to finish. */
700 				return WANT_POLLOUT;
701 			} else {
702 				save_errno = errno;
703 				close(conn->fd);
704 				conn->fd = -1;
705 				errno = save_errno;
706 				cause = "connect";
707 				continue;
708 			}
709 		}
710 
711 		break;	/* okay we got one */
712 	}
713 
714 	if (conn->fd == -1) {
715 		if (cause != NULL)
716 			warn("%s: %s", http_info(conn->req->uri), cause);
717 		return http_failed(conn);
718 	}
719 
720 	return http_connect_done(conn);
721 }
722 
723 /*
724  * Called once an asynchronus connect request finished.
725  */
726 static enum res
727 http_finish_connect(struct http_connection *conn)
728 {
729 	int error = 0;
730 	socklen_t len;
731 
732 	len = sizeof(error);
733 	if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) {
734 		warn("%s: getsockopt SO_ERROR", http_info(conn->req->uri));
735 		goto fail;
736 	}
737 	if (error != 0) {
738 		errno = error;
739 		warn("%s: connect", http_info(conn->req->uri));
740 		goto fail;
741 	}
742 
743 	return http_connect_done(conn);
744 
745 fail:
746 	close(conn->fd);
747 	conn->fd = -1;
748 
749 	return http_connect(conn);
750 }
751 
752 /*
753  * Initiate TLS session on a new connection.
754  */
755 static enum res
756 http_tls_connect(struct http_connection *conn)
757 {
758 	assert(conn->state == STATE_CONNECT);
759 	conn->state = STATE_TLSCONNECT;
760 
761 	if ((conn->tls = tls_client()) == NULL) {
762 		warn("tls_client");
763 		return http_failed(conn);
764 	}
765 	if (tls_configure(conn->tls, tls_config) == -1) {
766 		warnx("%s: TLS configuration: %s\n", http_info(conn->req->uri),
767 		    tls_error(conn->tls));
768 		return http_failed(conn);
769 	}
770 	if (tls_connect_socket(conn->tls, conn->fd, conn->host) == -1) {
771 		warnx("%s: TLS connect: %s\n", http_info(conn->req->uri),
772 		    tls_error(conn->tls));
773 		return http_failed(conn);
774 	}
775 
776 	return http_tls_handshake(conn);
777 }
778 
779 /*
780  * Do the tls_handshake and then send out the HTTP request.
781  */
782 static enum res
783 http_tls_handshake(struct http_connection *conn)
784 {
785 	switch (tls_handshake(conn->tls)) {
786 	case -1:
787 		warnx("%s: TLS handshake: %s", http_info(conn->req->uri),
788 		    tls_error(conn->tls));
789 		return http_failed(conn);
790 	case TLS_WANT_POLLIN:
791 		return WANT_POLLIN;
792 	case TLS_WANT_POLLOUT:
793 		return WANT_POLLOUT;
794 	}
795 
796 	/* ready to send request */
797 	return http_request(conn);
798 }
799 
800 /*
801  * Build the HTTP request and send it out.
802  */
803 static enum res
804 http_request(struct http_connection *conn)
805 {
806 	char *host, *epath, *modified_since;
807 	int r, with_port = 0;
808 
809 	assert(conn->state == STATE_IDLE || conn->state == STATE_TLSCONNECT);
810 	conn->state = STATE_REQUEST;
811 
812 	/* TODO adjust request for HTTP proxy setups */
813 
814 	/*
815 	 * Send port number only if it's specified and does not equal
816 	 * the default. Some broken HTTP servers get confused if you explicitly
817 	 * send them the port number.
818 	 */
819 	if (strcmp(conn->port, "443") != 0)
820 		with_port = 1;
821 
822 	/* Construct the Host header from host and port info */
823 	if (strchr(conn->host, ':')) {
824 		if (asprintf(&host, "[%s]%s%s", conn->host,
825 		    with_port ? ":" : "", with_port ? conn->port : "") == -1)
826 			err(1, NULL);
827 
828 	} else {
829 		if (asprintf(&host, "%s%s%s", conn->host,
830 		    with_port ? ":" : "", with_port ? conn->port : "") == -1)
831 			err(1, NULL);
832 	}
833 
834 	/*
835 	 * Construct and send the request. Proxy requests don't want leading /.
836 	 */
837 	epath = url_encode(conn->req->path);
838 
839 	modified_since = NULL;
840 	if (conn->req->modified_since != NULL) {
841 		if (asprintf(&modified_since, "If-Modified-Since: %s\r\n",
842 		    conn->req->modified_since) == -1)
843 			err(1, NULL);
844 	}
845 
846 	free(conn->buf);
847 	conn->bufpos = 0;
848 	if ((r = asprintf(&conn->buf,
849 	    "GET /%s HTTP/1.1\r\n"
850 	    "Connection: keep-alive\r\n"
851 	    "User-Agent: " HTTP_USER_AGENT "\r\n"
852 	    "Host: %s\r\n%s\r\n",
853 	    epath, host,
854 	    modified_since ? modified_since : "")) == -1)
855 		err(1, NULL);
856 	conn->bufsz = r;
857 
858 	free(epath);
859 	free(host);
860 	free(modified_since);
861 
862 	return http_write(conn);
863 }
864 
865 /*
866  * Parse the HTTP status line.
867  * Return 0 for status codes 200, 301-304, 307-308.
868  * Failure codes and other errors return -1.
869  * The redirect loop limit is enforced here.
870  */
871 static int
872 http_parse_status(struct http_connection *conn, char *buf)
873 {
874 	const char *errstr;
875 	char *cp, ststr[4];
876 	char gerror[200];
877 	int status;
878 
879 	cp = strchr(buf, ' ');
880 	if (cp == NULL) {
881 		warnx("Improper response from %s", http_info(conn->host));
882 		return -1;
883 	} else
884 		cp++;
885 
886 	strlcpy(ststr, cp, sizeof(ststr));
887 	status = strtonum(ststr, 200, 599, &errstr);
888 	if (errstr != NULL) {
889 		strnvis(gerror, cp, sizeof gerror, VIS_SAFE);
890 		warnx("Error retrieving %s: %s", http_info(conn->host),
891 		    gerror);
892 		return -1;
893 	}
894 
895 	switch (status) {
896 	case 301:
897 	case 302:
898 	case 303:
899 	case 307:
900 	case 308:
901 		if (conn->req->redirect_loop++ > 10) {
902 			warnx("%s: Too many redirections requested",
903 			    http_info(conn->host));
904 			return -1;
905 		}
906 		/* FALLTHROUGH */
907 	case 200:
908 	case 304:
909 		conn->status = status;
910 		break;
911 	default:
912 		strnvis(gerror, cp, sizeof gerror, VIS_SAFE);
913 		warnx("Error retrieving %s: %s", http_info(conn->host),
914 		    gerror);
915 		return -1;
916 	}
917 
918 	return 0;
919 }
920 
921 /*
922  * Returns true if the connection status is any of the redirect codes.
923  */
924 static inline int
925 http_isredirect(struct http_connection *conn)
926 {
927 	if ((conn->status >= 301 && conn->status <= 303) ||
928 	    conn->status == 307 || conn->status == 308)
929 		return 1;
930 	return 0;
931 }
932 
933 static void
934 http_redirect(struct http_connection *conn)
935 {
936 	char *uri, *mod_since = NULL;
937 	int outfd;
938 
939 	/* move uri and fd out for new request */
940 	outfd = conn->req->outfd;
941 	conn->req->outfd = -1;
942 
943 	uri = conn->redir_uri;
944 	conn->redir_uri = NULL;
945 
946 	if (conn->req->modified_since)
947 		if ((mod_since = strdup(conn->req->modified_since)) == NULL)
948 			err(1, NULL);
949 
950 	logx("redirect to %s", http_info(uri));
951 	http_req_new(conn->req->id, uri, mod_since, outfd);
952 
953 	/* clear request before moving connection to idle */
954 	http_req_free(conn->req);
955 	conn->req = NULL;
956 }
957 
958 static int
959 http_parse_header(struct http_connection *conn, char *buf)
960 {
961 #define CONTENTLEN "Content-Length: "
962 #define LOCATION "Location: "
963 #define CONNECTION "Connection: "
964 #define TRANSFER_ENCODING "Transfer-Encoding: "
965 #define LAST_MODIFIED "Last-Modified: "
966 	const char *errstr;
967 	char *cp, *redirurl;
968 	char *locbase, *loctail;
969 
970 	cp = buf;
971 	/* empty line, end of header */
972 	if (*cp == '\0')
973 		return 0;
974 	else if (strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0) {
975 		size_t s;
976 		cp += sizeof(CONTENTLEN) - 1;
977 		if ((s = strcspn(cp, " \t")) != 0)
978 			*(cp+s) = 0;
979 		conn->iosz = strtonum(cp, 0, LLONG_MAX, &errstr);
980 		if (errstr != NULL) {
981 			warnx("Content-Length of %s is %s",
982 			    http_info(conn->req->uri), errstr);
983 			return -1;
984 		}
985 	} else if (http_isredirect(conn) &&
986 	    strncasecmp(cp, LOCATION, sizeof(LOCATION) - 1) == 0) {
987 		cp += sizeof(LOCATION) - 1;
988 		/*
989 		 * If there is a colon before the first slash, this URI
990 		 * is not relative. RFC 3986 4.2
991 		 */
992 		if (cp[strcspn(cp, ":/")] != ':') {
993 			/* XXX doesn't handle protocol-relative URIs */
994 			if (*cp == '/') {
995 				locbase = NULL;
996 				cp++;
997 			} else {
998 				locbase = strdup(conn->req->path);
999 				if (locbase == NULL)
1000 					err(1, NULL);
1001 				loctail = strchr(locbase, '#');
1002 				if (loctail != NULL)
1003 					*loctail = '\0';
1004 				loctail = strchr(locbase, '?');
1005 				if (loctail != NULL)
1006 					*loctail = '\0';
1007 				loctail = strrchr(locbase, '/');
1008 				if (loctail == NULL) {
1009 					free(locbase);
1010 					locbase = NULL;
1011 				} else
1012 					loctail[1] = '\0';
1013 			}
1014 			/* Construct URL from relative redirect */
1015 			if (asprintf(&redirurl, "%.*s/%s%s",
1016 			    (int)(conn->req->path - conn->req->uri),
1017 			    conn->req->uri, locbase ? locbase : "", cp) == -1)
1018 				err(1, "Cannot build redirect URL");
1019 			free(locbase);
1020 		} else if ((redirurl = strdup(cp)) == NULL)
1021 			err(1, "Cannot build redirect URL");
1022 		loctail = strchr(redirurl, '#');
1023 		if (loctail != NULL)
1024 			*loctail = '\0';
1025 		conn->redir_uri = redirurl;
1026 	} else if (strncasecmp(cp, TRANSFER_ENCODING,
1027 	    sizeof(TRANSFER_ENCODING) - 1) == 0) {
1028 		cp += sizeof(TRANSFER_ENCODING) - 1;
1029 		cp[strcspn(cp, " \t")] = '\0';
1030 		if (strcasecmp(cp, "chunked") == 0)
1031 			conn->chunked = 1;
1032 	} else if (strncasecmp(cp, CONNECTION, sizeof(CONNECTION) - 1) == 0) {
1033 		cp += sizeof(CONNECTION) - 1;
1034 		cp[strcspn(cp, " \t")] = '\0';
1035 		if (strcasecmp(cp, "keep-alive") == 0)
1036 			conn->keep_alive = 1;
1037 	} else if (strncasecmp(cp, LAST_MODIFIED,
1038 	    sizeof(LAST_MODIFIED) - 1) == 0) {
1039 		cp += sizeof(LAST_MODIFIED) - 1;
1040 		if ((conn->last_modified = strdup(cp)) == NULL)
1041 			err(1, NULL);
1042 	}
1043 
1044 	return 1;
1045 }
1046 
1047 /*
1048  * Return one line from the HTTP response.
1049  * The line returned has any possible '\r' and '\n' at the end stripped.
1050  * The buffer is advanced to the start of the next line.
1051  * If there is currently no full line in the buffer NULL is returned.
1052  */
1053 static char *
1054 http_get_line(struct http_connection *conn)
1055 {
1056 	char *end, *line;
1057 	size_t len;
1058 
1059 	end = memchr(conn->buf, '\n', conn->bufpos);
1060 	if (end == NULL)
1061 		return NULL;
1062 
1063 	len = end - conn->buf;
1064 	while (len > 0 && conn->buf[len - 1] == '\r')
1065 		--len;
1066 	if ((line = strndup(conn->buf, len)) == NULL)
1067 		err(1, NULL);
1068 
1069 	/* consume line including \n */
1070 	end++;
1071 	conn->bufpos -= end - conn->buf;
1072 	memmove(conn->buf, end, conn->bufpos);
1073 
1074 	return line;
1075 }
1076 
1077 /*
1078  * Parse the header between data chunks during chunked transfers.
1079  * Returns 0 if a new chunk size could be correctly read.
1080  * Returns 1 for the empty trailer lines.
1081  * If the chuck size could not be converted properly -1 is returned.
1082  */
1083 static int
1084 http_parse_chunked(struct http_connection *conn, char *buf)
1085 {
1086 	char *header = buf;
1087 	char *end;
1088 	unsigned long chunksize;
1089 
1090 	/* empty lines are used as trailer */
1091 	if (*header == '\0')
1092 		return 1;
1093 
1094 	/* strip CRLF and any optional chunk extension */
1095 	header[strcspn(header, ";\r\n")] = '\0';
1096 	errno = 0;
1097 	chunksize = strtoul(header, &end, 16);
1098 	if (header[0] == '\0' || *end != '\0' || (errno == ERANGE &&
1099 	    chunksize == ULONG_MAX) || chunksize > INT_MAX)
1100 		return -1;
1101 
1102 	conn->iosz = chunksize;
1103 	return 0;
1104 }
1105 
1106 static enum res
1107 http_read(struct http_connection *conn)
1108 {
1109 	ssize_t s;
1110 	char *buf;
1111 	int done;
1112 
1113 read_more:
1114 	s = tls_read(conn->tls, conn->buf + conn->bufpos,
1115 	    conn->bufsz - conn->bufpos);
1116 	if (s == -1) {
1117 		warn("%s: TLS read: %s", http_info(conn->host),
1118 		    tls_error(conn->tls));
1119 		return http_failed(conn);
1120 	} else if (s == TLS_WANT_POLLIN) {
1121 		return WANT_POLLIN;
1122 	} else if (s == TLS_WANT_POLLOUT) {
1123 		return WANT_POLLOUT;
1124 	}
1125 
1126 	if (s == 0 && conn->bufpos == 0) {
1127 		if (conn->req)
1128 			warnx("%s: short read, connection closed",
1129 			    http_info(conn->req->uri));
1130 		return http_failed(conn);
1131 	}
1132 
1133 	conn->bufpos += s;
1134 
1135 again:
1136 	switch (conn->state) {
1137 	case STATE_RESPONSE_STATUS:
1138 		buf = http_get_line(conn);
1139 		if (buf == NULL)
1140 			goto read_more;
1141 		if (http_parse_status(conn, buf) == -1) {
1142 			free(buf);
1143 			return http_failed(conn);
1144 		}
1145 		free(buf);
1146 		conn->state = STATE_RESPONSE_HEADER;
1147 		goto again;
1148 	case STATE_RESPONSE_HEADER:
1149 		done = 0;
1150 		while (!done) {
1151 			int rv;
1152 
1153 			buf = http_get_line(conn);
1154 			if (buf == NULL)
1155 				goto read_more;
1156 
1157 			rv = http_parse_header(conn, buf);
1158 			free(buf);
1159 
1160 			if (rv == -1)
1161 				return http_failed(conn);
1162 			if (rv ==  0)
1163 				done = 1;
1164 		}
1165 
1166 		/* Check status header and decide what to do next */
1167 		if (conn->status == 200 || http_isredirect(conn)) {
1168 			if (http_isredirect(conn))
1169 				http_redirect(conn);
1170 
1171 			if (conn->chunked)
1172 				conn->state = STATE_RESPONSE_CHUNKED_HEADER;
1173 			else
1174 				conn->state = STATE_RESPONSE_DATA;
1175 			goto again;
1176 		} else if (conn->status == 304) {
1177 			return http_done(conn, HTTP_NOT_MOD);
1178 		}
1179 
1180 		return http_failed(conn);
1181 	case STATE_RESPONSE_DATA:
1182 		if (conn->bufpos != conn->bufsz &&
1183 		    conn->iosz > (off_t)conn->bufpos)
1184 			goto read_more;
1185 
1186 		/* got a full buffer full of data */
1187 		if (conn->req == NULL) {
1188 			/*
1189 			 * After redirects all data needs to be discarded.
1190 			 */
1191 			if (conn->iosz < (off_t)conn->bufpos) {
1192 				conn->bufpos  -= conn->iosz;
1193 				conn->iosz = 0;
1194 			} else {
1195 				conn->iosz  -= conn->bufpos;
1196 				conn->bufpos = 0;
1197 			}
1198 			if (conn->chunked)
1199 				conn->state = STATE_RESPONSE_CHUNKED_TRAILER;
1200 			else
1201 				conn->state = STATE_RESPONSE_DATA;
1202 			goto read_more;
1203 		}
1204 
1205 		conn->state = STATE_WRITE_DATA;
1206 		return WANT_POLLOUT;
1207 	case STATE_RESPONSE_CHUNKED_HEADER:
1208 		assert(conn->iosz == 0);
1209 
1210 		buf = http_get_line(conn);
1211 		if (buf == NULL)
1212 			goto read_more;
1213 		if (http_parse_chunked(conn, buf) != 0) {
1214 			warnx("%s: bad chunk encoding", http_info(conn->host));
1215 			free(buf);
1216 			return http_failed(conn);
1217 		}
1218 
1219 		/*
1220 		 * check if transfer is done, in which case the last trailer
1221 		 * still needs to be processed.
1222 		 */
1223 		if (conn->iosz == 0) {
1224 			conn->chunked = 0;
1225 			conn->state = STATE_RESPONSE_CHUNKED_TRAILER;
1226 			goto again;
1227 		}
1228 
1229 		conn->state = STATE_RESPONSE_DATA;
1230 		goto again;
1231 	case STATE_RESPONSE_CHUNKED_TRAILER:
1232 		buf = http_get_line(conn);
1233 		if (buf == NULL)
1234 			goto read_more;
1235 		if (http_parse_chunked(conn, buf) != 1) {
1236 			warnx("%s: bad chunk encoding", http_info(conn->host));
1237 			free(buf);
1238 			return http_failed(conn);
1239 		}
1240 		free(buf);
1241 
1242 		/* if chunked got cleared then the transfer is over */
1243 		if (conn->chunked == 0)
1244 			return http_done(conn, HTTP_OK);
1245 
1246 		conn->state = STATE_RESPONSE_CHUNKED_HEADER;
1247 		goto again;
1248 	default:
1249 		errx(1, "unexpected http state");
1250 	}
1251 }
1252 
1253 /*
1254  * Send out the HTTP request. When done, replace buffer with the read buffer.
1255  */
1256 static enum res
1257 http_write(struct http_connection *conn)
1258 {
1259 	ssize_t s;
1260 
1261 	assert(conn->state == STATE_REQUEST);
1262 
1263 	while (conn->bufpos < conn->bufsz) {
1264 		s = tls_write(conn->tls, conn->buf + conn->bufpos,
1265 		    conn->bufsz - conn->bufpos);
1266 		if (s == -1) {
1267 			warnx("%s: TLS write: %s", http_info(conn->host),
1268 			    tls_error(conn->tls));
1269 			return http_failed(conn);
1270 		} else if (s == TLS_WANT_POLLIN) {
1271 			return WANT_POLLIN;
1272 		} else if (s == TLS_WANT_POLLOUT) {
1273 			return WANT_POLLOUT;
1274 		}
1275 
1276 		conn->bufpos += s;
1277 	}
1278 
1279 	/* done writing, first thing we need the status */
1280 	conn->state = STATE_RESPONSE_STATUS;
1281 
1282 	/* free write buffer and allocate the read buffer */
1283 	free(conn->buf);
1284 	conn->bufpos = 0;
1285 	conn->bufsz = HTTP_BUF_SIZE;
1286 	if ((conn->buf = malloc(conn->bufsz)) == NULL)
1287 		err(1, NULL);
1288 
1289 	return http_read(conn);
1290 }
1291 
1292 /*
1293  * Properly shutdown the TLS session else move connection into free state.
1294  */
1295 static enum res
1296 http_close(struct http_connection *conn)
1297 {
1298 	assert(conn->state == STATE_IDLE || conn->state == STATE_CLOSE);
1299 
1300 	conn->state = STATE_CLOSE;
1301 
1302 	if (conn->tls != NULL) {
1303 		switch (tls_close(conn->tls)) {
1304 		case TLS_WANT_POLLIN:
1305 			return WANT_POLLIN;
1306 		case TLS_WANT_POLLOUT:
1307 			return WANT_POLLOUT;
1308 		case 0:
1309 		case -1:
1310 			break;
1311 		}
1312 	}
1313 
1314 	conn->state = STATE_FREE;
1315 	return DONE;
1316 }
1317 
1318 /*
1319  * Write data into provided file descriptor. If all data got written
1320  * the connection may change into idle state.
1321  */
1322 static enum res
1323 data_write(struct http_connection *conn)
1324 {
1325 	ssize_t s;
1326 	size_t bsz = conn->bufpos;
1327 
1328 	assert(conn->state == STATE_WRITE_DATA);
1329 
1330 	if (conn->iosz < (off_t)bsz)
1331 		bsz = conn->iosz;
1332 
1333 	s = write(conn->req->outfd, conn->buf, bsz);
1334 
1335 	if (s == -1) {
1336 		warn("%s: data write", http_info(conn->req->uri));
1337 		return http_failed(conn);
1338 	}
1339 
1340 	conn->bufpos -= s;
1341 	conn->iosz -= s;
1342 	memmove(conn->buf, conn->buf + s, conn->bufpos);
1343 
1344 	/* check if regular file transfer is finished */
1345 	if (!conn->chunked && conn->iosz == 0)
1346 		return http_done(conn, HTTP_OK);
1347 
1348 	/* all data written, switch back to read */
1349 	if (conn->bufpos == 0 || conn->iosz == 0) {
1350 		if (conn->chunked)
1351 			conn->state = STATE_RESPONSE_CHUNKED_TRAILER;
1352 		else
1353 			conn->state = STATE_RESPONSE_DATA;
1354 		return http_read(conn);
1355 	}
1356 
1357 	/* still more data to write in buffer */
1358 	return WANT_POLLOUT;
1359 }
1360 
1361 /*
1362  * Do one IO call depending on the connection state.
1363  * Return WANT_POLLIN or WANT_POLLOUT to poll for more data.
1364  * If 0 is returned this stage is finished and the protocol should move
1365  * to the next stage by calling http_nextstep(). On error return -1.
1366  */
1367 static enum res
1368 http_handle(struct http_connection *conn)
1369 {
1370 	assert (conn->pfd != NULL && conn->pfd->revents != 0);
1371 
1372 	switch (conn->state) {
1373 	case STATE_CONNECT:
1374 		return http_finish_connect(conn);
1375 	case STATE_TLSCONNECT:
1376 		return http_tls_handshake(conn);
1377 	case STATE_REQUEST:
1378 		return http_write(conn);
1379 	case STATE_RESPONSE_STATUS:
1380 	case STATE_RESPONSE_HEADER:
1381 	case STATE_RESPONSE_DATA:
1382 	case STATE_RESPONSE_CHUNKED_HEADER:
1383 	case STATE_RESPONSE_CHUNKED_TRAILER:
1384 		return http_read(conn);
1385 	case STATE_WRITE_DATA:
1386 		return data_write(conn);
1387 	case STATE_CLOSE:
1388 		return http_close(conn);
1389 	case STATE_IDLE:
1390 		conn->state = STATE_RESPONSE_HEADER;
1391 		return http_read(conn);
1392 	case STATE_FREE:
1393 		errx(1, "bad http state");
1394 	}
1395 	errx(1, "unknown http state");
1396 }
1397 
1398 /*
1399  * Initialisation done before pledge() call to load certificates.
1400  */
1401 static void
1402 http_setup(void)
1403 {
1404 	tls_config = tls_config_new();
1405 	if (tls_config == NULL)
1406 		errx(1, "tls config failed");
1407 
1408 #if 0
1409 	/* TODO Should we allow extra protos and ciphers? */
1410 	if (tls_config_set_protocols(tls_config, TLS_PROTOCOLS_ALL) == -1)
1411 		errx(1, "tls set protocols failed: %s",
1412 		    tls_config_error(tls_config));
1413 	if (tls_config_set_ciphers(tls_config, "legacy") == -1)
1414 		errx(1, "tls set ciphers failed: %s",
1415 		    tls_config_error(tls_config));
1416 #endif
1417 
1418 	/* load cert file from disk now */
1419 	tls_ca_mem = tls_load_file(tls_default_ca_cert_file(),
1420 	    &tls_ca_size, NULL);
1421 	if (tls_ca_mem == NULL)
1422 		err(1, "tls_load_file: %s", tls_default_ca_cert_file());
1423 	tls_config_set_ca_mem(tls_config, tls_ca_mem, tls_ca_size);
1424 
1425 	/* TODO initalize proxy settings */
1426 }
1427 
1428 void
1429 proc_http(char *bind_addr, int fd)
1430 {
1431 	struct pollfd pfds[NPFDS];
1432 	struct http_connection *conn, *nc;
1433 	struct http_request *req, *nr;
1434 
1435 	if (bind_addr != NULL) {
1436 		struct addrinfo hints, *res;
1437 
1438 		bzero(&hints, sizeof(hints));
1439 		hints.ai_family = AF_UNSPEC;
1440 		hints.ai_socktype = SOCK_DGRAM; /*dummy*/
1441 		hints.ai_flags = AI_NUMERICHOST;
1442 		if (getaddrinfo(bind_addr, NULL, &hints, &res) == 0) {
1443 			memcpy(&http_bindaddr, res->ai_addr, res->ai_addrlen);
1444 			freeaddrinfo(res);
1445 		}
1446 	}
1447 	http_setup();
1448 
1449 	if (pledge("stdio inet dns recvfd", NULL) == -1)
1450 		err(1, "pledge");
1451 
1452 	memset(&pfds, 0, sizeof(pfds));
1453 
1454 	msgbuf_init(&msgq);
1455 	msgq.fd = fd;
1456 
1457 	for (;;) {
1458 		time_t now;
1459 		int timeout;
1460 		size_t i;
1461 
1462 		pfds[0].fd = fd;
1463 		pfds[0].events = POLLIN;
1464 		if (msgq.queued)
1465 			pfds[0].events |= POLLOUT;
1466 
1467 		i = 1;
1468 		timeout = INFTIM;
1469 		now = getmonotime();
1470 		LIST_FOREACH(conn, &active, entry) {
1471 			if (conn->state == STATE_WRITE_DATA)
1472 				pfds[i].fd = conn->req->outfd;
1473 			else
1474 				pfds[i].fd = conn->fd;
1475 
1476 			pfds[i].events = conn->events;
1477 			conn->pfd = &pfds[i];
1478 			i++;
1479 			if (i > NPFDS)
1480 				errx(1, "too many connections");
1481 		}
1482 		LIST_FOREACH(conn, &idle, entry) {
1483 			if (conn->idle_time <= now)
1484 				timeout = 0;
1485 			else {
1486 				int diff = conn->idle_time - now;
1487 				diff *= 1000;
1488 				if (timeout == INFTIM || diff < timeout)
1489 					timeout = diff;
1490 			}
1491 			pfds[i].fd = conn->fd;
1492 			pfds[i].events = POLLIN;
1493 			conn->pfd = &pfds[i];
1494 			i++;
1495 			if (i > NPFDS)
1496 				errx(1, "too many connections");
1497 		}
1498 
1499 		if (poll(pfds, i, timeout) == -1)
1500 			err(1, "poll");
1501 
1502 		if (pfds[0].revents & POLLHUP)
1503 			break;
1504 		if (pfds[0].revents & POLLOUT) {
1505 			switch (msgbuf_write(&msgq)) {
1506 			case 0:
1507 				errx(1, "write: connection closed");
1508 			case -1:
1509 				err(1, "write");
1510 			}
1511 		}
1512 		if (pfds[0].revents & POLLIN) {
1513 			size_t id;
1514 			int outfd;
1515 			char *uri;
1516 			char *mod;
1517 
1518 			outfd = io_recvfd(fd, &id, sizeof(id));
1519 			io_str_read(fd, &uri);
1520 			io_str_read(fd, &mod);
1521 
1522 			/* queue up new requests */
1523 			http_req_new(id, uri, mod, outfd);
1524 		}
1525 
1526 		now = getmonotime();
1527 		/* process idle connections */
1528 		LIST_FOREACH_SAFE(conn, &idle, entry, nc) {
1529 			if (conn->pfd != NULL && conn->pfd->revents != 0)
1530 				http_do(conn, http_handle);
1531 			else if (conn->idle_time <= now)
1532 				http_do(conn, http_close);
1533 
1534 			if (conn->state == STATE_FREE)
1535 				http_free(conn);
1536 		}
1537 
1538 		/* then active http requests */
1539 		LIST_FOREACH_SAFE(conn, &active, entry, nc) {
1540 			/* check if event is ready */
1541 			if (conn->pfd != NULL && conn->pfd->revents != 0)
1542 				http_do(conn, http_handle);
1543 
1544 			if (conn->state == STATE_FREE)
1545 				http_free(conn);
1546 		}
1547 
1548 
1549 		TAILQ_FOREACH_SAFE(req, &queue, entry, nr)
1550 			if (!http_req_schedule(req))
1551 				break;
1552 	}
1553 
1554 	exit(0);
1555 }
1556