xref: /openbsd/usr.sbin/relayd/relay_http.c (revision 998de4a5)
1 /*	$OpenBSD: relay_http.c,v 1.62 2016/08/01 21:25:53 benno Exp $	*/
2 
3 /*
4  * Copyright (c) 2006 - 2016 Reyk Floeter <reyk@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 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/time.h>
22 #include <sys/socket.h>
23 #include <sys/tree.h>
24 
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 
28 #include <limits.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <time.h>
34 #include <event.h>
35 #include <fnmatch.h>
36 #include <siphash.h>
37 #include <imsg.h>
38 #include <unistd.h>
39 
40 #include "relayd.h"
41 #include "http.h"
42 
43 static int	_relay_lookup_url(struct ctl_relay_event *, char *, char *,
44 		    char *, struct kv *);
45 int		 relay_lookup_url(struct ctl_relay_event *,
46 		    const char *, struct kv *);
47 int		 relay_lookup_query(struct ctl_relay_event *, struct kv *);
48 int		 relay_lookup_cookie(struct ctl_relay_event *, const char *,
49 		    struct kv *);
50 void		 relay_read_httpcontent(struct bufferevent *, void *);
51 void		 relay_read_httpchunks(struct bufferevent *, void *);
52 char		*relay_expand_http(struct ctl_relay_event *, char *,
53 		    char *, size_t);
54 int		 relay_writeheader_kv(struct ctl_relay_event *, struct kv *);
55 int		 relay_writeheader_http(struct ctl_relay_event *,
56 		    struct ctl_relay_event *);
57 int		 relay_writerequest_http(struct ctl_relay_event *,
58 		    struct ctl_relay_event *);
59 int		 relay_writeresponse_http(struct ctl_relay_event *,
60 		    struct ctl_relay_event *);
61 void		 relay_reset_http(struct ctl_relay_event *);
62 static int	 relay_httpmethod_cmp(const void *, const void *);
63 static int	 relay_httperror_cmp(const void *, const void *);
64 int		 relay_httpquery_test(struct ctl_relay_event *,
65 		    struct relay_rule *, struct kvlist *);
66 int		 relay_httpheader_test(struct ctl_relay_event *,
67 		    struct relay_rule *, struct kvlist *);
68 int		 relay_httppath_test(struct ctl_relay_event *,
69 		    struct relay_rule *, struct kvlist *);
70 int		 relay_httpurl_test(struct ctl_relay_event *,
71 		    struct relay_rule *, struct kvlist *);
72 int		 relay_httpcookie_test(struct ctl_relay_event *,
73 		    struct relay_rule *, struct kvlist *);
74 int		 relay_apply_actions(struct ctl_relay_event *, struct kvlist *);
75 int		 relay_match_actions(struct ctl_relay_event *,
76 		    struct relay_rule *, struct kvlist *, struct kvlist *);
77 void		 relay_httpdesc_free(struct http_descriptor *);
78 
79 static struct relayd	*env = NULL;
80 
81 static struct http_method	 http_methods[] = HTTP_METHODS;
82 static struct http_error	 http_errors[] = HTTP_ERRORS;
83 
84 void
85 relay_http(struct relayd *x_env)
86 {
87 	if (x_env != NULL)
88 		env = x_env;
89 
90 	DPRINTF("%s: sorting lookup tables, pid %d", __func__, getpid());
91 
92 	/* Sort the HTTP lookup arrays */
93 	qsort(http_methods, sizeof(http_methods) /
94 	    sizeof(http_methods[0]) - 1,
95 	    sizeof(http_methods[0]), relay_httpmethod_cmp);
96 	qsort(http_errors, sizeof(http_errors) /
97 	    sizeof(http_errors[0]) - 1,
98 	    sizeof(http_errors[0]), relay_httperror_cmp);
99 }
100 
101 void
102 relay_http_init(struct relay *rlay)
103 {
104 	rlay->rl_proto->close = relay_close_http;
105 
106 	relay_http(NULL);
107 
108 	/* Calculate skip step for the filter rules (may take a while) */
109 	relay_calc_skip_steps(&rlay->rl_proto->rules);
110 }
111 
112 int
113 relay_httpdesc_init(struct ctl_relay_event *cre)
114 {
115 	struct http_descriptor	*desc;
116 
117 	if ((desc = calloc(1, sizeof(*desc))) == NULL)
118 		return (-1);
119 
120 	RB_INIT(&desc->http_headers);
121 	cre->desc = desc;
122 
123 	return (0);
124 }
125 
126 void
127 relay_httpdesc_free(struct http_descriptor *desc)
128 {
129 	free(desc->http_path);
130 	desc->http_path = NULL;
131 	free(desc->http_query);
132 	desc->http_query = NULL;
133 	free(desc->http_version);
134 	desc->http_version = NULL;
135 	free(desc->query_key);
136 	desc->query_key = NULL;
137 	free(desc->query_val);
138 	desc->query_val = NULL;
139 	kv_purge(&desc->http_headers);
140 	desc->http_lastheader = NULL;
141 }
142 
143 void
144 relay_read_http(struct bufferevent *bev, void *arg)
145 {
146 	struct ctl_relay_event	*cre = arg;
147 	struct http_descriptor	*desc = cre->desc;
148 	struct rsession		*con = cre->con;
149 	struct relay		*rlay = con->se_relay;
150 	struct protocol		*proto = rlay->rl_proto;
151 	struct evbuffer		*src = EVBUFFER_INPUT(bev);
152 	char			*line = NULL, *key, *value;
153 	char			*urlproto, *host, *path;
154 	int			 action, unique, ret;
155 	const char		*errstr;
156 	size_t			 size, linelen;
157 	struct kv		*hdr = NULL;
158 
159 	getmonotime(&con->se_tv_last);
160 	cre->timedout = 0;
161 
162 	size = EVBUFFER_LENGTH(src);
163 	DPRINTF("%s: session %d: size %lu, to read %lld",
164 	    __func__, con->se_id, size, cre->toread);
165 	if (!size) {
166 		if (cre->dir == RELAY_DIR_RESPONSE)
167 			return;
168 		cre->toread = TOREAD_HTTP_HEADER;
169 		goto done;
170 	}
171 
172 	while (!cre->done && (line = evbuffer_readline(src)) != NULL) {
173 		linelen = strlen(line);
174 
175 		/*
176 		 * An empty line indicates the end of the request.
177 		 * libevent already stripped the \r\n for us.
178 		 */
179 		if (!linelen) {
180 			cre->done = 1;
181 			free(line);
182 			break;
183 		}
184 		key = line;
185 
186 		/* Limit the total header length minus \r\n */
187 		cre->headerlen += linelen;
188 		if (cre->headerlen > RELAY_MAXHEADERLENGTH) {
189 			free(line);
190 			relay_abort_http(con, 413, "request too large", 0);
191 			return;
192 		}
193 
194 		/*
195 		 * The first line is the GET/POST/PUT/... request,
196 		 * subsequent lines are HTTP headers.
197 		 */
198 		if (++cre->line == 1)
199 			value = strchr(key, ' ');
200 		else if (*key == ' ' || *key == '\t')
201 			/* Multiline headers wrap with a space or tab */
202 			value = NULL;
203 		else
204 			value = strchr(key, ':');
205 		if (value == NULL) {
206 			if (cre->line <= 2) {
207 				free(line);
208 				relay_abort_http(con, 400, "malformed", 0);
209 				return;
210 			}
211 
212 			/* Append line to the last header, if present */
213 			if (kv_extend(&desc->http_headers,
214 			    desc->http_lastheader, line) == NULL) {
215 				free(line);
216 				goto fail;
217 			}
218 
219 			free(line);
220 			continue;
221 		}
222 		if (*value == ':') {
223 			*value++ = '\0';
224 			value += strspn(value, " \t\r\n");
225 		} else {
226 			*value++ = '\0';
227 		}
228 
229 		DPRINTF("%s: session %d: header '%s: %s'", __func__,
230 		    con->se_id, key, value);
231 
232 		/*
233 		 * Identify and handle specific HTTP request methods
234 		 */
235 		if (cre->line == 1 && cre->dir == RELAY_DIR_RESPONSE) {
236 			desc->http_method = HTTP_METHOD_RESPONSE;
237 			/*
238 			 * Decode response path and query
239 			 */
240 			desc->http_version = strdup(line);
241 			if (desc->http_version == NULL) {
242 				free(line);
243 				goto fail;
244 			}
245 			desc->http_rescode = strdup(value);
246 			if (desc->http_rescode == NULL) {
247 				free(line);
248 				goto fail;
249 			}
250 			desc->http_resmesg = strchr(desc->http_rescode, ' ');
251 			if (desc->http_resmesg == NULL) {
252 				free(line);
253 				goto fail;
254 			}
255 			*desc->http_resmesg++ = '\0';
256 			if ((desc->http_resmesg = strdup(desc->http_resmesg))
257 			    == NULL) {
258 				free(line);
259 				goto fail;
260 			}
261 			desc->http_status = strtonum(desc->http_rescode, 100,
262 			    599, &errstr);
263 			if (errstr) {
264 				DPRINTF("%s: http_status %s: errno %d, %s",
265 				    __func__, desc->http_rescode, errno,
266 				    errstr);
267 				free(line);
268 				goto fail;
269 			}
270 			DPRINTF("http_version %s http_rescode %s "
271 			    "http_resmesg %s", desc->http_version,
272 			    desc->http_rescode, desc->http_resmesg);
273 			goto lookup;
274 		} else if (cre->line == 1 && cre->dir == RELAY_DIR_REQUEST) {
275 			if ((desc->http_method = relay_httpmethod_byname(key))
276 			    == HTTP_METHOD_NONE) {
277 				free(line);
278 				goto fail;
279 			}
280 			/*
281 			 * Decode request path and query
282 			 */
283 			desc->http_path = strdup(value);
284 			if (desc->http_path == NULL) {
285 				free(line);
286 				goto fail;
287 			}
288 			desc->http_version = strchr(desc->http_path, ' ');
289 			if (desc->http_version == NULL) {
290 				free(line);
291 				goto fail;
292 			}
293 			*desc->http_version++ = '\0';
294 			desc->http_query = strchr(desc->http_path, '?');
295 			if (desc->http_query != NULL)
296 				*desc->http_query++ = '\0';
297 
298 			/*
299 			 * Have to allocate the strings because they could
300 			 * be changed independently by the filters later.
301 			 */
302 			if ((desc->http_version =
303 			    strdup(desc->http_version)) == NULL) {
304 				free(line);
305 				goto fail;
306 			}
307 			if (desc->http_query != NULL &&
308 			    (desc->http_query =
309 			    strdup(desc->http_query)) == NULL) {
310 				free(line);
311 				goto fail;
312 			}
313 		} else if (desc->http_method != HTTP_METHOD_NONE &&
314 		    strcasecmp("Content-Length", key) == 0) {
315 			/*
316 			 * These methods should not have a body
317 			 * and thus no Content-Length header.
318 			 */
319 			if (desc->http_method == HTTP_METHOD_TRACE ||
320 			    desc->http_method == HTTP_METHOD_CONNECT) {
321 				relay_abort_http(con, 400, "malformed", 0);
322 				goto abort;
323 			}
324 			/*
325 			 * response with a status code of 1xx
326 			 * (Informational) or 204 (No Content) MUST
327 			 * not have a Content-Length (rfc 7230 3.3.3)
328 			 */
329 			if (desc->http_method == HTTP_METHOD_RESPONSE && (
330 			    ((desc->http_status >= 100 &&
331 			    desc->http_status < 200) ||
332 			    desc->http_status == 204))) {
333 				relay_abort_http(con, 500,
334 				    "Internal Server Error", 0);
335 				goto abort;
336 			}
337 			/*
338 			 * Need to read data from the client after the
339 			 * HTTP header.
340 			 * XXX What about non-standard clients not using
341 			 * the carriage return? And some browsers seem to
342 			 * include the line length in the content-length.
343 			 */
344 			cre->toread = strtonum(value, 0, LLONG_MAX, &errstr);
345 			if (errstr) {
346 				relay_abort_http(con, 500, errstr, 0);
347 				goto abort;
348 			}
349 		}
350  lookup:
351 		if (strcasecmp("Transfer-Encoding", key) == 0 &&
352 		    strcasecmp("chunked", value) == 0)
353 			desc->http_chunked = 1;
354 
355 		/* The following header should only occur once */
356 		if (strcasecmp("Host", key) == 0) {
357 			unique = 1;
358 
359 			/*
360 			 * The path may contain a URL.  The host in the
361 			 * URL has to match the Host: value.
362 			 */
363 			if (parse_url(desc->http_path,
364 			    &urlproto, &host, &path) == 0) {
365 				ret = strcasecmp(host, value);
366 				free(urlproto);
367 				free(host);
368 				free(path);
369 				if (ret != 0) {
370 					relay_abort_http(con, 400,
371 					    "malformed host", 0);
372 					goto abort;
373 				}
374 			}
375 		} else
376 			unique = 0;
377 
378 		if (cre->line != 1) {
379 			if ((hdr = kv_add(&desc->http_headers, key,
380 			    value, unique)) == NULL) {
381 				relay_abort_http(con, 400,
382 				    "malformed header", 0);
383 				goto abort;
384 			}
385 			desc->http_lastheader = hdr;
386 		}
387 
388 		free(line);
389 	}
390 	if (cre->done) {
391 		if (desc->http_method == HTTP_METHOD_NONE) {
392 			relay_abort_http(con, 406, "no method", 0);
393 			return;
394 		}
395 
396 		action = relay_test(proto, cre);
397 		switch (action) {
398 		case RES_FAIL:
399 			relay_close(con, "filter rule failed");
400 			return;
401 		case RES_BAD:
402 			relay_abort_http(con, 400, "Bad Request",
403 			    con->se_label);
404 			return;
405 		case RES_INTERNAL:
406 			relay_abort_http(con, 500, "Internal Server Error",
407 			    con->se_label);
408 			return;
409 		}
410 		if (action != RES_PASS) {
411 			relay_abort_http(con, 403, "Forbidden", con->se_label);
412 			return;
413 		}
414 
415 		switch (desc->http_method) {
416 		case HTTP_METHOD_CONNECT:
417 			/* Data stream */
418 			cre->toread = TOREAD_UNLIMITED;
419 			bev->readcb = relay_read;
420 			break;
421 		case HTTP_METHOD_DELETE:
422 		case HTTP_METHOD_GET:
423 		case HTTP_METHOD_HEAD:
424 		/* WebDAV methods */
425 		case HTTP_METHOD_COPY:
426 		case HTTP_METHOD_MOVE:
427 			cre->toread = 0;
428 			break;
429 		case HTTP_METHOD_OPTIONS:
430 		case HTTP_METHOD_POST:
431 		case HTTP_METHOD_PUT:
432 		case HTTP_METHOD_RESPONSE:
433 		/* WebDAV methods */
434 		case HTTP_METHOD_PROPFIND:
435 		case HTTP_METHOD_PROPPATCH:
436 		case HTTP_METHOD_MKCOL:
437 		case HTTP_METHOD_LOCK:
438 		case HTTP_METHOD_UNLOCK:
439 		case HTTP_METHOD_VERSION_CONTROL:
440 		case HTTP_METHOD_REPORT:
441 		case HTTP_METHOD_CHECKOUT:
442 		case HTTP_METHOD_CHECKIN:
443 		case HTTP_METHOD_UNCHECKOUT:
444 		case HTTP_METHOD_MKWORKSPACE:
445 		case HTTP_METHOD_UPDATE:
446 		case HTTP_METHOD_LABEL:
447 		case HTTP_METHOD_MERGE:
448 		case HTTP_METHOD_BASELINE_CONTROL:
449 		case HTTP_METHOD_MKACTIVITY:
450 		case HTTP_METHOD_ORDERPATCH:
451 		case HTTP_METHOD_ACL:
452 		case HTTP_METHOD_MKREDIRECTREF:
453 		case HTTP_METHOD_UPDATEREDIRECTREF:
454 		case HTTP_METHOD_SEARCH:
455 		case HTTP_METHOD_PATCH:
456 			/* HTTP request payload */
457 			if (cre->toread > 0)
458 				bev->readcb = relay_read_httpcontent;
459 
460 			/* Single-pass HTTP body */
461 			if (cre->toread < 0) {
462 				cre->toread = TOREAD_UNLIMITED;
463 				bev->readcb = relay_read;
464 			}
465 			break;
466 		default:
467 			/* HTTP handler */
468 			cre->toread = TOREAD_HTTP_HEADER;
469 			bev->readcb = relay_read_http;
470 			break;
471 		}
472 		if (desc->http_chunked) {
473 			/* Chunked transfer encoding */
474 			cre->toread = TOREAD_HTTP_CHUNK_LENGTH;
475 			bev->readcb = relay_read_httpchunks;
476 		}
477 
478 		if (cre->dir == RELAY_DIR_REQUEST) {
479 			if (relay_writerequest_http(cre->dst, cre) == -1)
480 			    goto fail;
481 		} else {
482 			if (relay_writeresponse_http(cre->dst, cre) == -1)
483 			    goto fail;
484 		}
485 		if (relay_bufferevent_print(cre->dst, "\r\n") == -1 ||
486 		    relay_writeheader_http(cre->dst, cre) == -1 ||
487 		    relay_bufferevent_print(cre->dst, "\r\n") == -1)
488 			goto fail;
489 
490 		relay_reset_http(cre);
491  done:
492 		if (cre->dir == RELAY_DIR_REQUEST && cre->toread <= 0 &&
493 		    cre->dst->state != STATE_CONNECTED) {
494 			if (rlay->rl_conf.fwdmode == FWD_TRANS) {
495 				relay_bindanyreq(con, 0, IPPROTO_TCP);
496 				return;
497 			}
498 			if (relay_connect(con) == -1) {
499 				relay_abort_http(con, 502, "session failed", 0);
500 				return;
501 			}
502 		}
503 	}
504 	if (con->se_done) {
505 		relay_close(con, "last http read (done)");
506 		return;
507 	}
508 	switch (relay_splice(cre)) {
509 	case -1:
510 		relay_close(con, strerror(errno));
511 	case 1:
512 		return;
513 	case 0:
514 		break;
515 	}
516 	bufferevent_enable(bev, EV_READ);
517 	if (EVBUFFER_LENGTH(src) && bev->readcb != relay_read_http)
518 		bev->readcb(bev, arg);
519 	/* The callback readcb() might have freed the session. */
520 	return;
521  fail:
522 	relay_abort_http(con, 500, strerror(errno), 0);
523 	return;
524  abort:
525 	free(line);
526 }
527 
528 void
529 relay_read_httpcontent(struct bufferevent *bev, void *arg)
530 {
531 	struct ctl_relay_event	*cre = arg;
532 	struct rsession		*con = cre->con;
533 	struct protocol		*proto = con->se_relay->rl_proto;
534 
535 	struct evbuffer		*src = EVBUFFER_INPUT(bev);
536 	size_t			 size;
537 
538 	getmonotime(&con->se_tv_last);
539 	cre->timedout = 0;
540 
541 	size = EVBUFFER_LENGTH(src);
542 	DPRINTF("%s: session %d: size %lu, to read %lld", __func__,
543 	    con->se_id, size, cre->toread);
544 	if (!size)
545 		return;
546 	if (relay_spliceadjust(cre) == -1)
547 		goto fail;
548 
549 	if (cre->toread > 0) {
550 		/* Read content data */
551 		if ((off_t)size > cre->toread) {
552 			size = cre->toread;
553 			if (relay_bufferevent_write_chunk(cre->dst, src, size)
554 			    == -1)
555 				goto fail;
556 			cre->toread = 0;
557 		} else {
558 			if (relay_bufferevent_write_buffer(cre->dst, src) == -1)
559 				goto fail;
560 			cre->toread -= size;
561 		}
562 		DPRINTF("%s: done, size %lu, to read %lld", __func__,
563 		    size, cre->toread);
564 	}
565 	if (cre->toread == 0) {
566 		cre->toread = TOREAD_HTTP_HEADER;
567 		bev->readcb = relay_read_http;
568 	}
569 	if (con->se_done)
570 		goto done;
571 	bufferevent_enable(bev, EV_READ);
572 
573 	if (cre->dst->bev && EVBUFFER_LENGTH(EVBUFFER_OUTPUT(cre->dst->bev)) >
574 	    (size_t)RELAY_MAX_PREFETCH * proto->tcpbufsiz)
575 		bufferevent_disable(cre->bev, EV_READ);
576 
577 	if (bev->readcb != relay_read_httpcontent)
578 		bev->readcb(bev, arg);
579 	/* The callback readcb() might have freed the session. */
580 	return;
581  done:
582 	relay_close(con, "last http content read");
583 	return;
584  fail:
585 	relay_close(con, strerror(errno));
586 }
587 
588 void
589 relay_read_httpchunks(struct bufferevent *bev, void *arg)
590 {
591 	struct ctl_relay_event	*cre = arg;
592 	struct rsession		*con = cre->con;
593 	struct protocol		*proto = con->se_relay->rl_proto;
594 	struct evbuffer		*src = EVBUFFER_INPUT(bev);
595 	char			*line;
596 	long long		 llval;
597 	size_t			 size;
598 
599 	getmonotime(&con->se_tv_last);
600 	cre->timedout = 0;
601 
602 	size = EVBUFFER_LENGTH(src);
603 	DPRINTF("%s: session %d: size %lu, to read %lld", __func__,
604 	    con->se_id, size, cre->toread);
605 	if (!size)
606 		return;
607 	if (relay_spliceadjust(cre) == -1)
608 		goto fail;
609 
610 	if (cre->toread > 0) {
611 		/* Read chunk data */
612 		if ((off_t)size > cre->toread) {
613 			size = cre->toread;
614 			if (relay_bufferevent_write_chunk(cre->dst, src, size)
615 			    == -1)
616 				goto fail;
617 			cre->toread = 0;
618 		} else {
619 			if (relay_bufferevent_write_buffer(cre->dst, src) == -1)
620 				goto fail;
621 			cre->toread -= size;
622 		}
623 		DPRINTF("%s: done, size %lu, to read %lld", __func__,
624 		    size, cre->toread);
625 	}
626 	switch (cre->toread) {
627 	case TOREAD_HTTP_CHUNK_LENGTH:
628 		line = evbuffer_readline(src);
629 		if (line == NULL) {
630 			/* Ignore empty line, continue */
631 			bufferevent_enable(bev, EV_READ);
632 			return;
633 		}
634 		if (strlen(line) == 0) {
635 			free(line);
636 			goto next;
637 		}
638 
639 		/*
640 		 * Read prepended chunk size in hex, ignore the trailer.
641 		 * The returned signed value must not be negative.
642 		 */
643 		if (sscanf(line, "%llx", &llval) != 1 || llval < 0) {
644 			free(line);
645 			relay_close(con, "invalid chunk size");
646 			return;
647 		}
648 
649 		if (relay_bufferevent_print(cre->dst, line) == -1 ||
650 		    relay_bufferevent_print(cre->dst, "\r\n") == -1) {
651 			free(line);
652 			goto fail;
653 		}
654 		free(line);
655 
656 		if ((cre->toread = llval) == 0) {
657 			DPRINTF("%s: last chunk", __func__);
658 			cre->toread = TOREAD_HTTP_CHUNK_TRAILER;
659 		}
660 		break;
661 	case TOREAD_HTTP_CHUNK_TRAILER:
662 		/* Last chunk is 0 bytes followed by trailer and empty line */
663 		line = evbuffer_readline(src);
664 		if (line == NULL) {
665 			/* Ignore empty line, continue */
666 			bufferevent_enable(bev, EV_READ);
667 			return;
668 		}
669 		if (relay_bufferevent_print(cre->dst, line) == -1 ||
670 		    relay_bufferevent_print(cre->dst, "\r\n") == -1) {
671 			free(line);
672 			goto fail;
673 		}
674 		if (strlen(line) == 0) {
675 			/* Switch to HTTP header mode */
676 			cre->toread = TOREAD_HTTP_HEADER;
677 			bev->readcb = relay_read_http;
678 		}
679 		free(line);
680 		break;
681 	case 0:
682 		/* Chunk is terminated by an empty newline */
683 		line = evbuffer_readline(src);
684 		free(line);
685 		if (relay_bufferevent_print(cre->dst, "\r\n") == -1)
686 			goto fail;
687 		cre->toread = TOREAD_HTTP_CHUNK_LENGTH;
688 		break;
689 	}
690 
691  next:
692 	if (con->se_done)
693 		goto done;
694 	bufferevent_enable(bev, EV_READ);
695 
696 	if (cre->dst->bev && EVBUFFER_LENGTH(EVBUFFER_OUTPUT(cre->dst->bev)) >
697 	    (size_t)RELAY_MAX_PREFETCH * proto->tcpbufsiz)
698 		bufferevent_disable(cre->bev, EV_READ);
699 
700 	if (EVBUFFER_LENGTH(src))
701 		bev->readcb(bev, arg);
702 	/* The callback readcb() might have freed the session. */
703 	return;
704 
705  done:
706 	relay_close(con, "last http chunk read (done)");
707 	return;
708  fail:
709 	relay_close(con, strerror(errno));
710 }
711 
712 void
713 relay_reset_http(struct ctl_relay_event *cre)
714 {
715 	struct http_descriptor	*desc = cre->desc;
716 
717 	relay_httpdesc_free(desc);
718 	if (cre->buf != NULL) {
719 		free(cre->buf);
720 		cre->buf = NULL;
721 		cre->buflen = 0;
722 	}
723 	desc->http_method = 0;
724 	desc->http_chunked = 0;
725 	cre->headerlen = 0;
726 	cre->line = 0;
727 	cre->done = 0;
728 }
729 
730 static int
731 _relay_lookup_url(struct ctl_relay_event *cre, char *host, char *path,
732     char *query, struct kv *kv)
733 {
734 	struct rsession		*con = cre->con;
735 	char			*val, *md = NULL;
736 	int			 ret = RES_FAIL;
737 	const char		*str = NULL;
738 
739 	if (asprintf(&val, "%s%s%s%s",
740 	    host, path,
741 	    query == NULL ? "" : "?",
742 	    query == NULL ? "" : query) == -1) {
743 		relay_abort_http(con, 500, "failed to allocate URL", 0);
744 		return (RES_FAIL);
745 	}
746 
747 	switch (kv->kv_digest) {
748 	case DIGEST_SHA1:
749 	case DIGEST_MD5:
750 		if ((md = digeststr(kv->kv_digest,
751 		    val, strlen(val), NULL)) == NULL) {
752 			relay_abort_http(con, 500,
753 			    "failed to allocate digest", 0);
754 			goto fail;
755 		}
756 		str = md;
757 		break;
758 	case DIGEST_NONE:
759 		str = val;
760 		break;
761 	}
762 
763 	DPRINTF("%s: session %d: %s, %s: %d", __func__, con->se_id,
764 	    str, kv->kv_key, strcasecmp(kv->kv_key, str));
765 
766 	if (strcasecmp(kv->kv_key, str) == 0) {
767 		ret = RES_DROP;
768 		goto fail;
769 	}
770 
771 	ret = RES_PASS;
772  fail:
773 	free(md);
774 	free(val);
775 	return (ret);
776 }
777 
778 int
779 relay_lookup_url(struct ctl_relay_event *cre, const char *host, struct kv *kv)
780 {
781 	struct http_descriptor	*desc = (struct http_descriptor *)cre->desc;
782 	int			 i, j, dots;
783 	char			*hi[RELAY_MAXLOOKUPLEVELS], *p, *pp, *c, ch;
784 	char			 ph[HOST_NAME_MAX+1];
785 	int			 ret;
786 
787 	if (desc->http_path == NULL)
788 		return (RES_PASS);
789 
790 	/*
791 	 * This is an URL lookup algorithm inspired by
792 	 * http://code.google.com/apis/safebrowsing/
793 	 *     developers_guide.html#PerformingLookups
794 	 */
795 
796 	DPRINTF("%s: host '%s', path '%s', query '%s'",
797 	    __func__, host, desc->http_path,
798 	    desc->http_query == NULL ? "" : desc->http_query);
799 
800 	if (canonicalize_host(host, ph, sizeof(ph)) == NULL) {
801 		return (RES_BAD);
802 	}
803 
804 	bzero(hi, sizeof(hi));
805 	for (dots = -1, i = strlen(ph) - 1; i > 0; i--) {
806 		if (ph[i] == '.' && ++dots)
807 			hi[dots - 1] = &ph[i + 1];
808 		if (dots > (RELAY_MAXLOOKUPLEVELS - 2))
809 			break;
810 	}
811 	if (dots == -1)
812 		dots = 0;
813 	hi[dots] = ph;
814 
815 	if ((pp = strdup(desc->http_path)) == NULL) {
816 		return (RES_INTERNAL);
817 	}
818 	for (i = (RELAY_MAXLOOKUPLEVELS - 1); i >= 0; i--) {
819 		if (hi[i] == NULL)
820 			continue;
821 
822 		/* 1. complete path with query */
823 		if (desc->http_query != NULL)
824 			if ((ret = _relay_lookup_url(cre, hi[i],
825 			    pp, desc->http_query, kv)) != RES_PASS)
826 				goto done;
827 
828 		/* 2. complete path without query */
829 		if ((ret = _relay_lookup_url(cre, hi[i],
830 		    pp, NULL, kv)) != RES_PASS)
831 			goto done;
832 
833 		/* 3. traverse path */
834 		for (j = 0, p = strchr(pp, '/');
835 		    p != NULL; p = strchr(p, '/'), j++) {
836 			if (j > (RELAY_MAXLOOKUPLEVELS - 2) || *(++p) == '\0')
837 				break;
838 			c = &pp[p - pp];
839 			ch = *c;
840 			*c = '\0';
841 			if ((ret = _relay_lookup_url(cre, hi[i],
842 			    pp, NULL, kv)) != RES_PASS)
843 				goto done;
844 			*c = ch;
845 		}
846 	}
847 
848 	ret = RES_PASS;
849  done:
850 	free(pp);
851 	return (ret);
852 }
853 
854 int
855 relay_lookup_cookie(struct ctl_relay_event *cre, const char *str,
856     struct kv *kv)
857 {
858 	char			*val, *ptr, *key, *value;
859 	int			 ret;
860 
861 	if ((val = strdup(str)) == NULL) {
862 		return (RES_INTERNAL);
863 	}
864 
865 	for (ptr = val; ptr != NULL && strlen(ptr);) {
866 		if (*ptr == ' ')
867 			*ptr++ = '\0';
868 		key = ptr;
869 		if ((ptr = strchr(ptr, ';')) != NULL)
870 			*ptr++ = '\0';
871 		/*
872 		 * XXX We do not handle attributes
873 		 * ($Path, $Domain, or $Port)
874 		 */
875 		if (*key == '$')
876 			continue;
877 
878 		if ((value =
879 		    strchr(key, '=')) == NULL ||
880 		    strlen(value) < 1)
881 			continue;
882 		*value++ = '\0';
883 		if (*value == '"')
884 			*value++ = '\0';
885 		if (value[strlen(value) - 1] == '"')
886 			value[strlen(value) - 1] = '\0';
887 
888 		DPRINTF("%s: key %s = %s, %s = %s : %d",
889 		    __func__, key, value, kv->kv_key, kv->kv_value,
890 		    strcasecmp(kv->kv_key, key));
891 
892 		if (strcasecmp(kv->kv_key, key) == 0 &&
893 		    ((kv->kv_value == NULL) ||
894 		    (fnmatch(kv->kv_value, value,
895 		    FNM_CASEFOLD) != FNM_NOMATCH))) {
896 			ret = RES_DROP;
897 			goto done;
898 		}
899 	}
900 
901 	ret = RES_PASS;
902 
903  done:
904 	free(val);
905 	return (ret);
906 }
907 
908 int
909 relay_lookup_query(struct ctl_relay_event *cre, struct kv *kv)
910 {
911 	struct http_descriptor	*desc = cre->desc;
912 	struct kv		*match = &desc->http_matchquery;
913 	char			*val, *ptr, *tmpkey = NULL, *tmpval = NULL;
914 	int			 ret = -1;
915 
916 	if (desc->http_query == NULL)
917 		return (-1);
918 	if ((val = strdup(desc->http_query)) == NULL) {
919 		return (RES_INTERNAL);
920 	}
921 
922 	ptr = val;
923 	while (ptr != NULL && strlen(ptr)) {
924 		tmpkey = ptr;
925 		if ((ptr = strchr(ptr, '&')) != NULL)
926 			*ptr++ = '\0';
927 		if ((tmpval = strchr(tmpkey, '=')) == NULL || strlen(tmpval)
928 		    < 1)
929 			continue;
930 		*tmpval++ = '\0';
931 
932 		if (fnmatch(kv->kv_key, tmpkey, 0) != FNM_NOMATCH &&
933 		    (kv->kv_value == NULL || fnmatch(kv->kv_value, tmpval, 0)
934 		    != FNM_NOMATCH))
935 			break;
936 		else
937 			tmpkey = NULL;
938 	}
939 
940 	if (tmpkey == NULL || tmpval == NULL)
941 		goto done;
942 
943 	match->kv_key = strdup(tmpkey);
944 	if (match->kv_key == NULL)
945 		goto done;
946 	match->kv_value = strdup(tmpval);
947 	if (match->kv_key == NULL)
948 		goto done;
949 	ret = 0;
950 
951  done:
952 	free(val);
953 	return (ret);
954 }
955 
956 ssize_t
957 relay_http_time(time_t t, char *tmbuf, size_t len)
958 {
959 	struct tm		 tm;
960 
961 	/* New HTTP/1.1 RFC 7231 prefers IMF-fixdate from RFC 5322 */
962 	if (t == -1 || gmtime_r(&t, &tm) == NULL)
963 		return (-1);
964 	else
965 		return (strftime(tmbuf, len, "%a, %d %h %Y %T %Z", &tm));
966 }
967 
968 void
969 relay_abort_http(struct rsession *con, u_int code, const char *msg,
970     u_int16_t labelid)
971 {
972 	struct relay		*rlay = con->se_relay;
973 	struct bufferevent	*bev = con->se_in.bev;
974 	const char		*httperr = NULL, *text = "";
975 	char			*httpmsg, *body = NULL;
976 	char			 tmbuf[32], hbuf[128];
977 	const char		*style, *label = NULL;
978 	int			 bodylen;
979 
980 	if ((httperr = relay_httperror_byid(code)) == NULL)
981 		httperr = "Unknown Error";
982 
983 	if (labelid != 0)
984 		label = label_id2name(labelid);
985 
986 	/* In some cases this function may be called from generic places */
987 	if (rlay->rl_proto->type != RELAY_PROTO_HTTP ||
988 	    (rlay->rl_proto->flags & F_RETURN) == 0) {
989 		relay_close(con, msg);
990 		return;
991 	}
992 
993 	if (bev == NULL)
994 		goto done;
995 
996 	/* Some system information */
997 	if (print_host(&rlay->rl_conf.ss, hbuf, sizeof(hbuf)) == NULL)
998 		goto done;
999 
1000 	if (relay_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0)
1001 		goto done;
1002 
1003 	/* Do not send details of the Internal Server Error */
1004 	switch (code) {
1005 	case 500:
1006 		break;
1007 	default:
1008 		text = msg;
1009 		break;
1010 	}
1011 
1012 	/* A CSS stylesheet allows minimal customization by the user */
1013 	style = (rlay->rl_proto->style != NULL) ? rlay->rl_proto->style :
1014 	    "body { background-color: #a00000; color: white; font-family: "
1015 	    "'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }\n"
1016 	    "hr { border: 0; border-bottom: 1px dashed; }\n";
1017 
1018 	/* Generate simple HTTP+HTML error document */
1019 	if ((bodylen = asprintf(&body,
1020 	    "<!DOCTYPE html>\n"
1021 	    "<html>\n"
1022 	    "<head>\n"
1023 	    "<title>%03d %s</title>\n"
1024 	    "<style type=\"text/css\"><!--\n%s\n--></style>\n"
1025 	    "</head>\n"
1026 	    "<body>\n"
1027 	    "<h1>%s</h1>\n"
1028 	    "<div id='m'>%s</div>\n"
1029 	    "<div id='l'>%s</div>\n"
1030 	    "<hr><address>%s at %s port %d</address>\n"
1031 	    "</body>\n"
1032 	    "</html>\n",
1033 	    code, httperr, style, httperr, text,
1034 	    label == NULL ? "" : label,
1035 	    RELAYD_SERVERNAME, hbuf, ntohs(rlay->rl_conf.port))) == -1)
1036 		goto done;
1037 
1038 	/* Generate simple HTTP+HTML error document */
1039 	if (asprintf(&httpmsg,
1040 	    "HTTP/1.0 %03d %s\r\n"
1041 	    "Date: %s\r\n"
1042 	    "Server: %s\r\n"
1043 	    "Connection: close\r\n"
1044 	    "Content-Type: text/html\r\n"
1045 	    "Content-Length: %d\r\n"
1046 	    "\r\n"
1047 	    "%s",
1048 	    code, httperr, tmbuf, RELAYD_SERVERNAME, bodylen, body) == -1)
1049 		goto done;
1050 
1051 	/* Dump the message without checking for success */
1052 	relay_dump(&con->se_in, httpmsg, strlen(httpmsg));
1053 	free(httpmsg);
1054 
1055  done:
1056 	free(body);
1057 	if (asprintf(&httpmsg, "%s (%03d %s)", msg, code, httperr) == -1)
1058 		relay_close(con, msg);
1059 	else {
1060 		relay_close(con, httpmsg);
1061 		free(httpmsg);
1062 	}
1063 }
1064 
1065 void
1066 relay_close_http(struct rsession *con)
1067 {
1068 	struct http_descriptor	*desc[2] = {
1069 		con->se_in.desc, con->se_out.desc
1070 	};
1071 	int			 i;
1072 
1073 	for (i = 0; i < 2; i++) {
1074 		if (desc[i] == NULL)
1075 			continue;
1076 		relay_httpdesc_free(desc[i]);
1077 		free(desc[i]);
1078 	}
1079 }
1080 
1081 char *
1082 relay_expand_http(struct ctl_relay_event *cre, char *val, char *buf,
1083     size_t len)
1084 {
1085 	struct rsession	*con = cre->con;
1086 	struct relay	*rlay = con->se_relay;
1087 	char		 ibuf[128];
1088 
1089 	if (strlcpy(buf, val, len) >= len)
1090 		return (NULL);
1091 
1092 	if (strstr(val, "$REMOTE_") != NULL) {
1093 		if (strstr(val, "$REMOTE_ADDR") != NULL) {
1094 			if (print_host(&cre->ss, ibuf, sizeof(ibuf)) == NULL)
1095 				return (NULL);
1096 			if (expand_string(buf, len,
1097 			    "$REMOTE_ADDR", ibuf) != 0)
1098 				return (NULL);
1099 		}
1100 		if (strstr(val, "$REMOTE_PORT") != NULL) {
1101 			snprintf(ibuf, sizeof(ibuf), "%u", ntohs(cre->port));
1102 			if (expand_string(buf, len,
1103 			    "$REMOTE_PORT", ibuf) != 0)
1104 				return (NULL);
1105 		}
1106 	}
1107 	if (strstr(val, "$SERVER_") != NULL) {
1108 		if (strstr(val, "$SERVER_ADDR") != NULL) {
1109 			if (print_host(&rlay->rl_conf.ss,
1110 			    ibuf, sizeof(ibuf)) == NULL)
1111 				return (NULL);
1112 			if (expand_string(buf, len,
1113 			    "$SERVER_ADDR", ibuf) != 0)
1114 				return (NULL);
1115 		}
1116 		if (strstr(val, "$SERVER_PORT") != NULL) {
1117 			snprintf(ibuf, sizeof(ibuf), "%u",
1118 			    ntohs(rlay->rl_conf.port));
1119 			if (expand_string(buf, len,
1120 			    "$SERVER_PORT", ibuf) != 0)
1121 				return (NULL);
1122 		}
1123 		if (strstr(val, "$SERVER_NAME") != NULL) {
1124 			if (expand_string(buf, len,
1125 			    "$SERVER_NAME", RELAYD_SERVERNAME) != 0)
1126 				return (NULL);
1127 		}
1128 	}
1129 	if (strstr(val, "$TIMEOUT") != NULL) {
1130 		snprintf(ibuf, sizeof(ibuf), "%lld",
1131 		    (long long)rlay->rl_conf.timeout.tv_sec);
1132 		if (expand_string(buf, len, "$TIMEOUT", ibuf) != 0)
1133 			return (NULL);
1134 	}
1135 
1136 	return (buf);
1137 }
1138 
1139 int
1140 relay_writerequest_http(struct ctl_relay_event *dst,
1141     struct ctl_relay_event *cre)
1142 {
1143 	struct http_descriptor	*desc = (struct http_descriptor *)cre->desc;
1144 	const char		*name = NULL;
1145 
1146 	if ((name = relay_httpmethod_byid(desc->http_method)) == NULL)
1147 		return (-1);
1148 
1149 	if (relay_bufferevent_print(dst, name) == -1 ||
1150 	    relay_bufferevent_print(dst, " ") == -1 ||
1151 	    relay_bufferevent_print(dst, desc->http_path) == -1 ||
1152 	    (desc->http_query != NULL &&
1153 	    (relay_bufferevent_print(dst, "?") == -1 ||
1154 	    relay_bufferevent_print(dst, desc->http_query) == -1)) ||
1155 	    relay_bufferevent_print(dst, " ") == -1 ||
1156 	    relay_bufferevent_print(dst, desc->http_version) == -1)
1157 		return (-1);
1158 
1159 	return (0);
1160 }
1161 
1162 int
1163 relay_writeresponse_http(struct ctl_relay_event *dst,
1164     struct ctl_relay_event *cre)
1165 {
1166 	struct http_descriptor	*desc = (struct http_descriptor *)cre->desc;
1167 
1168 	DPRINTF("version: %s rescode: %s resmsg: %s", desc->http_version,
1169 	    desc->http_rescode, desc->http_resmesg);
1170 
1171 	if (relay_bufferevent_print(dst, desc->http_version) == -1 ||
1172 	    relay_bufferevent_print(dst, " ") == -1 ||
1173 	    relay_bufferevent_print(dst, desc->http_rescode) == -1 ||
1174 	    relay_bufferevent_print(dst, " ") == -1 ||
1175 	    relay_bufferevent_print(dst, desc->http_resmesg) == -1)
1176 		return (-1);
1177 
1178 	return (0);
1179 }
1180 
1181 int
1182 relay_writeheader_kv(struct ctl_relay_event *dst, struct kv *hdr)
1183 {
1184 	char			*ptr;
1185 	const char		*key;
1186 
1187 	if (hdr->kv_flags & KV_FLAG_INVALID)
1188 		return (0);
1189 
1190 	/* The key might have been updated in the parent */
1191 	if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL)
1192 		key = hdr->kv_parent->kv_key;
1193 	else
1194 		key = hdr->kv_key;
1195 
1196 	ptr = hdr->kv_value;
1197 	if (relay_bufferevent_print(dst, key) == -1 ||
1198 	    (ptr != NULL &&
1199 	    (relay_bufferevent_print(dst, ": ") == -1 ||
1200 	    relay_bufferevent_print(dst, ptr) == -1 ||
1201 	    relay_bufferevent_print(dst, "\r\n") == -1)))
1202 		return (-1);
1203 	DPRINTF("%s: %s: %s", __func__, key,
1204 	    hdr->kv_value == NULL ? "" : hdr->kv_value);
1205 
1206 	return (0);
1207 }
1208 
1209 int
1210 relay_writeheader_http(struct ctl_relay_event *dst, struct ctl_relay_event
1211     *cre)
1212 {
1213 	struct kv		*hdr, *kv;
1214 	struct http_descriptor	*desc = (struct http_descriptor *)cre->desc;
1215 
1216 	RB_FOREACH(hdr, kvtree, &desc->http_headers) {
1217 		if (relay_writeheader_kv(dst, hdr) == -1)
1218 			return (-1);
1219 		TAILQ_FOREACH(kv, &hdr->kv_children, kv_entry) {
1220 			if (relay_writeheader_kv(dst, kv) == -1)
1221 				return (-1);
1222 		}
1223 	}
1224 
1225 	return (0);
1226 }
1227 
1228 enum httpmethod
1229 relay_httpmethod_byname(const char *name)
1230 {
1231 	enum httpmethod		 id = HTTP_METHOD_NONE;
1232 	struct http_method	 method, *res = NULL;
1233 
1234 	/* Set up key */
1235 	method.method_name = name;
1236 
1237 	if ((res = bsearch(&method, http_methods,
1238 	    sizeof(http_methods) / sizeof(http_methods[0]) - 1,
1239 	    sizeof(http_methods[0]), relay_httpmethod_cmp)) != NULL)
1240 		id = res->method_id;
1241 
1242 	return (id);
1243 }
1244 
1245 const char *
1246 relay_httpmethod_byid(u_int id)
1247 {
1248 	const char	*name = NULL;
1249 	int		 i;
1250 
1251 	for (i = 0; http_methods[i].method_name != NULL; i++) {
1252 		if (http_methods[i].method_id == id) {
1253 			name = http_methods[i].method_name;
1254 			break;
1255 		}
1256 	}
1257 
1258 	return (name);
1259 }
1260 
1261 static int
1262 relay_httpmethod_cmp(const void *a, const void *b)
1263 {
1264 	const struct http_method *ma = a;
1265 	const struct http_method *mb = b;
1266 
1267 	/*
1268 	 * RFC 2616 section 5.1.1 says that the method is case
1269 	 * sensitive so we don't do a strcasecmp here.
1270 	 */
1271 	return (strcmp(ma->method_name, mb->method_name));
1272 }
1273 
1274 const char *
1275 relay_httperror_byid(u_int id)
1276 {
1277 	struct http_error	 error, *res = NULL;
1278 
1279 	/* Set up key */
1280 	error.error_code = (int)id;
1281 
1282 	res = bsearch(&error, http_errors,
1283 	    sizeof(http_errors) / sizeof(http_errors[0]) - 1,
1284 	    sizeof(http_errors[0]), relay_httperror_cmp);
1285 
1286 	return (res->error_name);
1287 }
1288 
1289 static int
1290 relay_httperror_cmp(const void *a, const void *b)
1291 {
1292 	const struct http_error *ea = a;
1293 	const struct http_error *eb = b;
1294 	return (ea->error_code - eb->error_code);
1295 }
1296 
1297 int
1298 relay_httpquery_test(struct ctl_relay_event *cre, struct relay_rule *rule,
1299     struct kvlist *actions)
1300 {
1301 	struct http_descriptor	*desc = cre->desc;
1302 	struct kv		*match = &desc->http_matchquery;
1303 	struct kv		*kv = &rule->rule_kv[KEY_TYPE_QUERY];
1304 	int			 res = 0;
1305 
1306 	if (cre->dir == RELAY_DIR_RESPONSE || kv->kv_type != KEY_TYPE_QUERY)
1307 		return (0);
1308 	else if (kv->kv_key == NULL)
1309 		return (0);
1310 	else if ((res = relay_lookup_query(cre, kv)) != 0)
1311 		return (res);
1312 
1313 	relay_match(actions, kv, match, NULL);
1314 
1315 	return (0);
1316 }
1317 
1318 int
1319 relay_httpheader_test(struct ctl_relay_event *cre, struct relay_rule *rule,
1320     struct kvlist *actions)
1321 {
1322 	struct http_descriptor	*desc = cre->desc;
1323 	struct kv		*kv = &rule->rule_kv[KEY_TYPE_HEADER];
1324 	struct kv		*match;
1325 
1326 	if (kv->kv_type != KEY_TYPE_HEADER)
1327 		return (0);
1328 
1329 	match = kv_find(&desc->http_headers, kv);
1330 
1331 	if (kv->kv_option == KEY_OPTION_APPEND ||
1332 	    kv->kv_option == KEY_OPTION_SET) {
1333 		/* header can be NULL and will be added later */
1334 	} else if (match == NULL) {
1335 		/* Fail if header doesn't exist */
1336 		return (-1);
1337 	} else {
1338 		if (fnmatch(kv->kv_key, match->kv_key,
1339 		    FNM_CASEFOLD) == FNM_NOMATCH)
1340 			return (-1);
1341 		if (kv->kv_value != NULL &&
1342 		    match->kv_value != NULL &&
1343 		    fnmatch(kv->kv_value, match->kv_value, 0) == FNM_NOMATCH)
1344 			return (-1);
1345 	}
1346 
1347 	relay_match(actions, kv, match, &desc->http_headers);
1348 
1349 	return (0);
1350 }
1351 
1352 int
1353 relay_httppath_test(struct ctl_relay_event *cre, struct relay_rule *rule,
1354     struct kvlist *actions)
1355 {
1356 	struct http_descriptor	*desc = cre->desc;
1357 	struct kv		*kv = &rule->rule_kv[KEY_TYPE_PATH];
1358 	struct kv		*match = &desc->http_pathquery;
1359 	const char		*query;
1360 
1361 	if (cre->dir == RELAY_DIR_RESPONSE || kv->kv_type != KEY_TYPE_PATH)
1362 		return (0);
1363 	else if (kv->kv_key == NULL)
1364 		return (0);
1365 	else if (fnmatch(kv->kv_key, desc->http_path, 0) == FNM_NOMATCH)
1366 		return (-1);
1367 	else if (kv->kv_value != NULL && kv->kv_option == KEY_OPTION_NONE) {
1368 		query = desc->http_query == NULL ? "" : desc->http_query;
1369 		if (fnmatch(kv->kv_value, query, FNM_CASEFOLD) == FNM_NOMATCH)
1370 			return (-1);
1371 	}
1372 
1373 	relay_match(actions, kv, match, NULL);
1374 
1375 	return (0);
1376 }
1377 
1378 int
1379 relay_httpurl_test(struct ctl_relay_event *cre, struct relay_rule *rule,
1380     struct kvlist *actions)
1381 {
1382 	struct http_descriptor	*desc = cre->desc;
1383 	struct kv		*host, key;
1384 	struct kv		*kv = &rule->rule_kv[KEY_TYPE_URL];
1385 	struct kv		*match = &desc->http_pathquery;
1386 	int			 res;
1387 
1388 	if (cre->dir == RELAY_DIR_RESPONSE || kv->kv_type != KEY_TYPE_URL ||
1389 	    kv->kv_key == NULL)
1390 		return (0);
1391 
1392 	key.kv_key = "Host";
1393 	host = kv_find(&desc->http_headers, &key);
1394 
1395 	if (host == NULL || host->kv_value == NULL)
1396 		return (0);
1397 	else if (rule->rule_action != RULE_ACTION_BLOCK &&
1398 	    kv->kv_option == KEY_OPTION_LOG &&
1399 	    fnmatch(kv->kv_key, match->kv_key, FNM_CASEFOLD) != FNM_NOMATCH) {
1400 		/* fnmatch url only for logging */
1401 	} else if ((res = relay_lookup_url(cre, host->kv_value, kv)) != 0)
1402 		return (res);
1403 	relay_match(actions, kv, match, NULL);
1404 
1405 	return (0);
1406 }
1407 
1408 int
1409 relay_httpcookie_test(struct ctl_relay_event *cre, struct relay_rule *rule,
1410     struct kvlist *actions)
1411 {
1412 	struct http_descriptor	*desc = cre->desc;
1413 	struct kv		*kv = &rule->rule_kv[KEY_TYPE_COOKIE], key;
1414 	struct kv		*match = NULL;
1415 	int			 res;
1416 
1417 	if (kv->kv_type != KEY_TYPE_COOKIE)
1418 		return (0);
1419 
1420 	switch (cre->dir) {
1421 	case RELAY_DIR_REQUEST:
1422 		key.kv_key = "Cookie";
1423 		break;
1424 	case RELAY_DIR_RESPONSE:
1425 		key.kv_key = "Set-Cookie";
1426 		break;
1427 	default:
1428 		return (0);
1429 		/* NOTREACHED */
1430 		break;
1431 	}
1432 
1433 	if (kv->kv_option == KEY_OPTION_APPEND ||
1434 	    kv->kv_option == KEY_OPTION_SET) {
1435 		/* no cookie, can be NULL and will be added later */
1436 	} else {
1437 		match = kv_find(&desc->http_headers, &key);
1438 		if (match == NULL)
1439 			return (-1);
1440 		if (kv->kv_key == NULL || match->kv_value == NULL)
1441 			return (0);
1442 		else if ((res = relay_lookup_cookie(cre, match->kv_value,
1443 		    kv)) != 0)
1444 			return (res);
1445 	}
1446 
1447 	relay_match(actions, kv, match, &desc->http_headers);
1448 
1449 	return (0);
1450 }
1451 
1452 int
1453 relay_match_actions(struct ctl_relay_event *cre, struct relay_rule *rule,
1454     struct kvlist *matches, struct kvlist *actions)
1455 {
1456 	struct rsession		*con = cre->con;
1457 	struct kv		*kv, *tmp;
1458 
1459 	/*
1460 	 * Apply the following options instantly (action per match).
1461 	 */
1462 	if (rule->rule_table != NULL)
1463 		con->se_table = rule->rule_table;
1464 
1465 	if (rule->rule_tag != 0)
1466 		con->se_tag = rule->rule_tag == -1 ? 0 : rule->rule_tag;
1467 
1468 	if (rule->rule_label != 0)
1469 		con->se_label = rule->rule_label == -1 ? 0 : rule->rule_label;
1470 
1471 	/*
1472 	 * Apply the remaining options once after evaluation.
1473 	 */
1474 	if (matches == NULL) {
1475 		/* 'pass' or 'block' rule */
1476 		TAILQ_FOREACH_SAFE(kv, &rule->rule_kvlist, kv_rule_entry, tmp) {
1477 			TAILQ_INSERT_TAIL(actions, kv, kv_action_entry);
1478 			TAILQ_REMOVE(&rule->rule_kvlist, kv, kv_rule_entry);
1479 		}
1480 	} else {
1481 		/* 'match' rule */
1482 		TAILQ_FOREACH(kv, matches, kv_match_entry) {
1483 			TAILQ_INSERT_TAIL(actions, kv, kv_action_entry);
1484 		}
1485 	}
1486 
1487 	return (0);
1488 }
1489 
1490 int
1491 relay_apply_actions(struct ctl_relay_event *cre, struct kvlist *actions)
1492 {
1493 	struct rsession		*con = cre->con;
1494 	struct http_descriptor	*desc = cre->desc;
1495 	struct kv		*host = NULL;
1496 	const char		*value;
1497 	struct kv		*kv, *match, *kp, *mp, kvcopy, matchcopy, key;
1498 	int			 addkv, ret;
1499 	char			 buf[IBUF_READ_SIZE], *ptr;
1500 	char			*msg = NULL;
1501 	const char		*meth = NULL;
1502 
1503 	memset(&kvcopy, 0, sizeof(kvcopy));
1504 	memset(&matchcopy, 0, sizeof(matchcopy));
1505 
1506 	ret = -1;
1507 	kp = mp = NULL;
1508 	TAILQ_FOREACH(kv, actions, kv_action_entry) {
1509 		kp = NULL;
1510 		match = kv->kv_match;
1511 		addkv = 0;
1512 
1513 		/*
1514 		 * Although marked as deleted, give a chance to non-critical
1515 		 * actions, ie. log, to be performed
1516 		 */
1517 		if (match != NULL && (match->kv_flags & KV_FLAG_INVALID))
1518 			goto matchdel;
1519 
1520 		switch (kv->kv_option) {
1521 		case KEY_OPTION_APPEND:
1522 		case KEY_OPTION_SET:
1523 			switch (kv->kv_type) {
1524 			case KEY_TYPE_PATH:
1525 				if (kv->kv_option == KEY_OPTION_APPEND) {
1526 					if (kv_setkey(match, "%s%s",
1527 					    match->kv_key, kv->kv_key) == -1)
1528 						goto fail;
1529 				} else {
1530 					if (kv_setkey(match, "%s",
1531 					    kv->kv_value) == -1)
1532 						goto fail;
1533 				}
1534 				break;
1535 			case KEY_TYPE_COOKIE:
1536 				kp = &kvcopy;
1537 				if (kv_inherit(kp, kv) == NULL)
1538 					goto fail;
1539 				if (kv_set(kp, "%s=%s;", kp->kv_key,
1540 				    kp->kv_value) == -1)
1541 					goto fail;
1542 				if (kv_setkey(kp, "%s", cre->dir ==
1543 				    RELAY_DIR_REQUEST ?
1544 				    "Cookie" : "Set-Cookie") == -1)
1545 					goto fail;
1546 				/* FALLTHROUGH cookie is a header */
1547 			case KEY_TYPE_HEADER:
1548 				if (match == NULL) {
1549 					addkv = 1;
1550 					break;
1551 				}
1552 				if (match->kv_value == NULL ||
1553 				    kv->kv_option == KEY_OPTION_SET) {
1554 					if (kv_set(match, "%s",
1555 					    kv->kv_value) == -1)
1556 						goto fail;
1557 				} else
1558 					addkv = 1;
1559 				break;
1560 			default:
1561 				/* query, url not supported */
1562 				break;
1563 			}
1564 			break;
1565 		case KEY_OPTION_REMOVE:
1566 			switch (kv->kv_type) {
1567 			case KEY_TYPE_PATH:
1568 				if (kv_setkey(match, "/") == -1)
1569 					goto fail;
1570 				break;
1571 			case KEY_TYPE_COOKIE:
1572 			case KEY_TYPE_HEADER:
1573 				if (kv->kv_matchtree != NULL)
1574 					match->kv_flags |= KV_FLAG_INVALID;
1575 				else
1576 					kv_free(match);
1577 				match = kv->kv_match = NULL;
1578 				break;
1579 			default:
1580 				/* query and url not supported */
1581 				break;
1582 			}
1583 			break;
1584 		case KEY_OPTION_HASH:
1585 			switch (kv->kv_type) {
1586 			case KEY_TYPE_PATH:
1587 				value = match->kv_key;
1588 				break;
1589 			default:
1590 				value = match->kv_value;
1591 				break;
1592 			}
1593 			SipHash24_Update(&con->se_siphashctx,
1594 			    value, strlen(value));
1595 			break;
1596 		case KEY_OPTION_LOG:
1597 			/* perform this later */
1598 			break;
1599 		default:
1600 			fatalx("relay_action: invalid action");
1601 			/* NOTREACHED */
1602 		}
1603 
1604 		/* from now on, reads from kp writes to kv */
1605 		if (kp == NULL)
1606 			kp = kv;
1607 		if (addkv && kv->kv_matchtree != NULL) {
1608 			/* Add new entry to the list (eg. new HTTP header) */
1609 			if ((match = kv_add(kv->kv_matchtree, kp->kv_key,
1610 			    kp->kv_value, 0)) == NULL)
1611 				goto fail;
1612 			match->kv_option = kp->kv_option;
1613 			match->kv_type = kp->kv_type;
1614 			kv->kv_match = match;
1615 		}
1616 		if (match != NULL && kp->kv_flags & KV_FLAG_MACRO) {
1617 			bzero(buf, sizeof(buf));
1618 			if ((ptr = relay_expand_http(cre, kp->kv_value, buf,
1619 			    sizeof(buf))) == NULL)
1620 				goto fail;
1621 			if (kv_set(match, ptr) == -1)
1622 				goto fail;
1623 		}
1624 
1625  matchdel:
1626 		switch (kv->kv_option) {
1627 		case KEY_OPTION_LOG:
1628 			if (match == NULL)
1629 				break;
1630 			mp = &matchcopy;
1631 			if (kv_inherit(mp, match) == NULL)
1632 				goto fail;
1633 			if (mp->kv_flags & KV_FLAG_INVALID) {
1634 				if (kv_set(mp, "%s (removed)",
1635 				    mp->kv_value) == -1)
1636 					goto fail;
1637 			}
1638 			switch (kv->kv_type) {
1639 			case KEY_TYPE_URL:
1640 				key.kv_key = "Host";
1641 				host = kv_find(&desc->http_headers, &key);
1642 				switch (kv->kv_digest) {
1643 				case DIGEST_NONE:
1644 					if (host == NULL ||
1645 					    host->kv_value == NULL)
1646 						break;
1647 					if (kv_setkey(mp, "%s%s",
1648 					    host->kv_value, mp->kv_key) ==
1649 					    -1)
1650 						goto fail;
1651 					break;
1652 				default:
1653 					if (kv_setkey(mp, "%s", kv->kv_key)
1654 					    == -1)
1655 						goto fail;
1656 					break;
1657 				}
1658 				break;
1659 			default:
1660 				break;
1661 			}
1662 			if (kv_log(con, mp, con->se_label, cre->dir)
1663 			    == -1)
1664 				goto fail;
1665 			break;
1666 		default:
1667 			break;
1668 		}
1669 
1670 		/* actions applied, cleanup kv */
1671 		kv->kv_match = NULL;
1672 		kv->kv_matchtree = NULL;
1673 		TAILQ_REMOVE(actions, kv, kv_match_entry);
1674 
1675 		kv_free(&kvcopy);
1676 		kv_free(&matchcopy);
1677 	}
1678 
1679 	/*
1680 	 * log tag for request and response, request method
1681 	 * and end of request marker ","
1682 	 */
1683 	if ((con->se_log != NULL) &&
1684 	    ((meth = relay_httpmethod_byid(desc->http_method)) != NULL) &&
1685 	    (asprintf(&msg, " %s",meth) >= 0))
1686 		evbuffer_add(con->se_log, msg, strlen(msg));
1687 	free(msg);
1688 	relay_log(con, cre->dir == RELAY_DIR_REQUEST ? "" : ";");
1689 	ret = 0;
1690  fail:
1691 	kv_free(&kvcopy);
1692 	kv_free(&matchcopy);
1693 
1694 	return (ret);
1695 }
1696 
1697 #define	RELAY_GET_SKIP_STEP(i)						\
1698 	do {								\
1699 		r = r->rule_skip[i];					\
1700 		DPRINTF("%s:%d: skip %d rules", __func__, __LINE__, i);	\
1701 	} while (0)
1702 
1703 #define	RELAY_GET_NEXT_STEP						\
1704 	do {								\
1705 		DPRINTF("%s:%d: next rule", __func__, __LINE__);	\
1706 		goto nextrule;						\
1707 	} while (0)
1708 
1709 int
1710 relay_test(struct protocol *proto, struct ctl_relay_event *cre)
1711 {
1712 	struct rsession		*con;
1713 	struct http_descriptor	*desc = cre->desc;
1714 	struct relay_rule	*r = NULL, *rule = NULL;
1715 	u_int			 cnt = 0;
1716 	u_int			 action = RES_PASS;
1717 	struct kvlist		 actions, matches;
1718 	struct kv		*kv;
1719 	int			 res = 0;
1720 
1721 	con = cre->con;
1722 	TAILQ_INIT(&actions);
1723 
1724 	r = TAILQ_FIRST(&proto->rules);
1725 	while (r != NULL) {
1726 		cnt++;
1727 
1728 		TAILQ_INIT(&matches);
1729 		TAILQ_INIT(&r->rule_kvlist);
1730 
1731 		if (r->rule_dir && r->rule_dir != cre->dir)
1732 			RELAY_GET_SKIP_STEP(RULE_SKIP_DIR);
1733 		else if (proto->type != r->rule_proto)
1734 			RELAY_GET_SKIP_STEP(RULE_SKIP_PROTO);
1735 		else if (r->rule_af != AF_UNSPEC &&
1736 		    (cre->ss.ss_family != r->rule_af ||
1737 		     cre->dst->ss.ss_family != r->rule_af))
1738 			RELAY_GET_SKIP_STEP(RULE_SKIP_AF);
1739 		else if (RELAY_ADDR_CMP(&r->rule_src, &cre->ss) != 0)
1740 			RELAY_GET_SKIP_STEP(RULE_SKIP_SRC);
1741 		else if (RELAY_ADDR_CMP(&r->rule_dst, &cre->dst->ss) != 0)
1742 			RELAY_GET_SKIP_STEP(RULE_SKIP_DST);
1743 		else if (r->rule_method != HTTP_METHOD_NONE &&
1744 		    (desc->http_method == HTTP_METHOD_RESPONSE ||
1745 		     desc->http_method != r->rule_method))
1746 			RELAY_GET_SKIP_STEP(RULE_SKIP_METHOD);
1747 		else if (r->rule_tagged && con->se_tag != r->rule_tagged)
1748 			RELAY_GET_NEXT_STEP;
1749 		else if (relay_httpheader_test(cre, r, &matches) != 0)
1750 			RELAY_GET_NEXT_STEP;
1751 		else if ((res = relay_httpquery_test(cre, r, &matches)) != 0)
1752 			RELAY_GET_NEXT_STEP;
1753 		else if (relay_httppath_test(cre, r, &matches) != 0)
1754 			RELAY_GET_NEXT_STEP;
1755 		else if ((res = relay_httpurl_test(cre, r, &matches)) != 0)
1756 			RELAY_GET_NEXT_STEP;
1757 		else if ((res = relay_httpcookie_test(cre, r, &matches)) != 0)
1758 			RELAY_GET_NEXT_STEP;
1759 		else {
1760 			DPRINTF("%s: session %d: matched rule %d",
1761 			    __func__, con->se_id, r->rule_id);
1762 
1763 			if (r->rule_action == RULE_ACTION_MATCH) {
1764 				if (relay_match_actions(cre, r, &matches,
1765 				    &actions) != 0) {
1766 					/* Something bad happened, drop */
1767 					action = RES_DROP;
1768 					break;
1769 				}
1770 				RELAY_GET_NEXT_STEP;
1771 			} else if (r->rule_action == RULE_ACTION_BLOCK)
1772 				action = RES_DROP;
1773 			else if (r->rule_action == RULE_ACTION_PASS)
1774 				action = RES_PASS;
1775 
1776 			/* Rule matched */
1777 			rule = r;
1778 
1779 			/* Temporarily save actions */
1780 			TAILQ_FOREACH(kv, &matches, kv_match_entry) {
1781 				TAILQ_INSERT_TAIL(&rule->rule_kvlist,
1782 				    kv, kv_rule_entry);
1783 			}
1784 
1785 			if (rule->rule_flags & RULE_FLAG_QUICK)
1786 				break;
1787 
1788  nextrule:
1789 			/* Continue to find last matching policy */
1790 			DPRINTF("%s: session %d, res %d", __func__,
1791 			    con->se_id, res);
1792 			if (res == RES_BAD || res == RES_INTERNAL)
1793 				return(res);
1794 			res = 0;
1795 			r = TAILQ_NEXT(r, rule_entry);
1796 		}
1797 	}
1798 
1799 	if (rule != NULL && relay_match_actions(cre, rule, NULL, &actions)
1800 	    != 0) {
1801 		/* Something bad happened, drop */
1802 		action = RES_DROP;
1803 	}
1804 
1805 	if (relay_apply_actions(cre, &actions) != 0) {
1806 		/* Something bad happened, drop */
1807 		action = RES_DROP;
1808 	}
1809 
1810 	DPRINTF("%s: session %d: action %d", __func__,
1811 	    con->se_id, action);
1812 
1813 	return (action);
1814 }
1815 
1816 #define	RELAY_SET_SKIP_STEPS(i)						\
1817 	do {								\
1818 		while (head[i] != cur) {				\
1819 			head[i]->rule_skip[i] = cur;			\
1820 			head[i] = TAILQ_NEXT(head[i], rule_entry);	\
1821 		}							\
1822 	} while (0)
1823 
1824 /* This code is derived from pf_calc_skip_steps() from pf.c */
1825 void
1826 relay_calc_skip_steps(struct relay_rules *rules)
1827 {
1828 	struct relay_rule	*head[RULE_SKIP_COUNT], *cur, *prev;
1829 	int			 i;
1830 
1831 	cur = TAILQ_FIRST(rules);
1832 	prev = cur;
1833 	for (i = 0; i < RULE_SKIP_COUNT; ++i)
1834 		head[i] = cur;
1835 	while (cur != NULL) {
1836 		if (cur->rule_dir != prev->rule_dir)
1837 			RELAY_SET_SKIP_STEPS(RULE_SKIP_DIR);
1838 		else if (cur->rule_proto != prev->rule_proto)
1839 			RELAY_SET_SKIP_STEPS(RULE_SKIP_PROTO);
1840 		else if (cur->rule_af != prev->rule_af)
1841 			RELAY_SET_SKIP_STEPS(RULE_SKIP_AF);
1842 		else if (RELAY_ADDR_NEQ(&cur->rule_src, &prev->rule_src))
1843 			RELAY_SET_SKIP_STEPS(RULE_SKIP_SRC);
1844 		else if (RELAY_ADDR_NEQ(&cur->rule_dst, &prev->rule_dst))
1845 			RELAY_SET_SKIP_STEPS(RULE_SKIP_DST);
1846 		else if (cur->rule_method != prev->rule_method)
1847 			RELAY_SET_SKIP_STEPS(RULE_SKIP_METHOD);
1848 
1849 		prev = cur;
1850 		cur = TAILQ_NEXT(cur, rule_entry);
1851 	}
1852 	for (i = 0; i < RULE_SKIP_COUNT; ++i)
1853 		RELAY_SET_SKIP_STEPS(i);
1854 }
1855 
1856 void
1857 relay_match(struct kvlist *actions, struct kv *kv, struct kv *match,
1858     struct kvtree *matchtree)
1859 {
1860 	if (kv->kv_option != KEY_OPTION_NONE) {
1861 		kv->kv_match = match;
1862 		kv->kv_matchtree = matchtree;
1863 		TAILQ_INSERT_TAIL(actions, kv, kv_match_entry);
1864 	}
1865 }
1866