Hi! I was testing relayd and found an issue which would make relayd crash.
Reproduce: Test config /etc/relayd.conf: http protocol "test" { pass response block url "host" return error } relay "testing" { listen on "127.0.0.1" port 8080 protocol "test" forward to www.openbsd.org port 80 } # relayd -vv -d -f /etc/relayd.conf $ curl -H 'Host: ' 127.0.0.1:8080/ or $ printf 'GET HTTP/1.0\r\nHost: \r\nConnection: close\r\n\r\n' | nc 127.0.0.1 8080 Result: relayd crashes: startup protocol 1: name test flags: used, return, relay flags: type: http pass response block request url "host" value "*" relay testing, session 1 (1 active), 0, 127.0.0.1 -> :80, invalid host name (400 Bad Request) ca exiting, pid 15473 hce exiting, pid 21139 ca exiting, pid 31583 ca exiting, pid 4581 pfe exiting, pid 29863 relay exiting, pid 25103 relay exiting, pid 10471 parent terminating, pid 22282 After a bit of searching I found in relay_lookup_url(): ... if (canonicalize_host(host, ph, sizeof(ph)) == NULL) { relay_abort_http(con, 400, "invalid host name", 0); return (RES_FAIL); } ... relay_abort_http will call relay_close and close the connection and free() it. When this happens relay_test() will still use it after relay_close in relay_apply_actions() and after relay_test is called in relay_http.c: relay_read_http(). The patch below is a bit messy, it sets cre->con to NULL after relay_abort_http (which free'd the connection) and checks this state later. I follow relayd and httpd with great interest and like how httpd is rapidly improving lately, nice work! Kind regards, Hiltjo Patch: Index: relay_http.c =================================================================== RCS file: /cvs/src/usr.sbin/relayd/relay_http.c,v retrieving revision 1.52 diff -u -p -r1.52 relay_http.c --- relay_http.c 28 Jul 2015 10:24:26 -0000 1.52 +++ relay_http.c 8 Aug 2015 14:09:12 -0000 @@ -365,7 +365,8 @@ relay_read_http(struct bufferevent *bev, relay_close(con, "filter rule failed"); return; } else if (action != RES_PASS) { - relay_abort_http(con, 403, "Forbidden", con->se_label); + if (cre->con) + relay_abort_http(cre->con, 403, "Forbidden", con->se_label); return; } @@ -733,7 +734,7 @@ relay_lookup_url(struct ctl_relay_event if (canonicalize_host(host, ph, sizeof(ph)) == NULL) { relay_abort_http(con, 400, "invalid host name", 0); - return (RES_FAIL); + goto fail; } bzero(hi, sizeof(hi)); @@ -749,7 +750,7 @@ relay_lookup_url(struct ctl_relay_event if ((pp = strdup(desc->http_path)) == NULL) { relay_abort_http(con, 500, "failed to allocate path", 0); - return (RES_FAIL); + goto fail; } for (i = (RELAY_MAXLOOKUPLEVELS - 1); i >= 0; i--) { if (hi[i] == NULL) @@ -785,6 +786,9 @@ relay_lookup_url(struct ctl_relay_event done: free(pp); return (ret); + fail: + cre->con = NULL; + return (RES_FAIL); } int @@ -1653,6 +1657,7 @@ relay_test(struct protocol *proto, struc u_int action = RES_PASS; struct kvlist actions, matches; struct kv *kv; + int ret; con = cre->con; TAILQ_INIT(&actions); @@ -1686,9 +1691,11 @@ relay_test(struct protocol *proto, struc RELAY_GET_NEXT_STEP; else if (relay_httppath_test(cre, r, &matches) != 0) RELAY_GET_NEXT_STEP; - else if (relay_httpurl_test(cre, r, &matches) != 0) + else if ((ret = relay_httpurl_test(cre, r, &matches)) != 0) { + if (ret == -1) + return (RES_DROP); RELAY_GET_NEXT_STEP; - else if (relay_httpcookie_test(cre, r, &matches) != 0) + } else if (relay_httpcookie_test(cre, r, &matches) != 0) RELAY_GET_NEXT_STEP; else { DPRINTF("%s: session %d: matched rule %d",