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