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