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