diff options
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/relayctl/relayctl.c | 9 | ||||
-rw-r--r-- | usr.sbin/relayd/config.c | 255 | ||||
-rw-r--r-- | usr.sbin/relayd/http.h | 371 | ||||
-rw-r--r-- | usr.sbin/relayd/name2id.c | 51 | ||||
-rw-r--r-- | usr.sbin/relayd/parse.y | 628 | ||||
-rw-r--r-- | usr.sbin/relayd/pfe.c | 4 | ||||
-rw-r--r-- | usr.sbin/relayd/relay.c | 319 | ||||
-rw-r--r-- | usr.sbin/relayd/relay_http.c | 1540 | ||||
-rw-r--r-- | usr.sbin/relayd/relay_udp.c | 6 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.c | 620 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.conf.5 | 632 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.h | 319 |
12 files changed, 3125 insertions, 1629 deletions
diff --git a/usr.sbin/relayctl/relayctl.c b/usr.sbin/relayctl/relayctl.c index 62eec9e40a6..9c57db4da43 100644 --- a/usr.sbin/relayctl/relayctl.c +++ b/usr.sbin/relayctl/relayctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relayctl.c,v 1.50 2014/06/25 11:12:45 reyk Exp $ */ +/* $OpenBSD: relayctl.c,v 1.51 2014/07/09 16:42:05 reyk Exp $ */ /* * Copyright (c) 2007 - 2013 Reyk Floeter <reyk@openbsd.org> @@ -116,7 +116,7 @@ main(int argc, char *argv[]) bzero(&sun, sizeof(sun)); sun.sun_family = AF_UNIX; - strlcpy(sun.sun_path, RELAYD_SOCKET, sizeof(sun.sun_path)); + (void)strlcpy(sun.sun_path, RELAYD_SOCKET, sizeof(sun.sun_path)); reconnect: if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) { /* Keep retrying if running in monitor mode */ @@ -449,8 +449,9 @@ show_session_msg(struct imsg *imsg) print_time(&tv_now, &con->se_tv_last, b, sizeof(b)); printf("\tage %s, idle %s, relay %u, pid %u", a, b, con->se_relayid, con->se_pid); - if (con->se_mark) - printf(", mark %u", con->se_mark); + /* XXX grab tagname instead of tag id */ + if (con->se_tag) + printf(", tag (id) %u", con->se_tag); printf("\n"); break; case IMSG_CTL_END: diff --git a/usr.sbin/relayd/config.c b/usr.sbin/relayd/config.c index 51adfe950b7..6fe4fa09789 100644 --- a/usr.sbin/relayd/config.c +++ b/usr.sbin/relayd/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.14 2014/05/04 16:38:19 reyk Exp $ */ +/* $OpenBSD: config.c,v 1.15 2014/07/09 16:42:05 reyk Exp $ */ /* * Copyright (c) 2011 - 2014 Reyk Floeter <reyk@openbsd.org> @@ -121,8 +121,6 @@ config_init(struct relayd *env) env->sc_proto_default.type = RELAY_PROTO_TCP; (void)strlcpy(env->sc_proto_default.name, "default", sizeof(env->sc_proto_default.name)); - RB_INIT(&env->sc_proto_default.request_tree); - RB_INIT(&env->sc_proto_default.response_tree); } if (what & CONFIG_RTS) { if ((env->sc_rts = @@ -148,6 +146,7 @@ config_purge(struct relayd *env, u_int reset) struct rdr *rdr; struct address *virt; struct protocol *proto; + struct relay_rule *rule; struct relay *rlay; struct netroute *nr; struct router *rt; @@ -186,8 +185,14 @@ config_purge(struct relayd *env, u_int reset) if (what & CONFIG_PROTOS && env->sc_protos != NULL) { while ((proto = TAILQ_FIRST(env->sc_protos)) != NULL) { TAILQ_REMOVE(env->sc_protos, proto, entry); - purge_tree(&proto->request_tree); - purge_tree(&proto->response_tree); + while ((rule = TAILQ_FIRST(&proto->rules)) != NULL) + rule_delete(&proto->rules, rule); + proto->rulecount = 0; + } + } + if (what & CONFIG_PROTOS && env->sc_protos != NULL) { + while ((proto = TAILQ_FIRST(env->sc_protos)) != NULL) { + TAILQ_REMOVE(env->sc_protos, proto, entry); if (proto->style != NULL) free(proto->style); if (proto->sslcapass != NULL) @@ -597,7 +602,7 @@ config_setproto(struct relayd *env, struct protocol *proto) { struct privsep *ps = env->sc_ps; int id; - struct iovec iov[2]; + struct iovec iov[IOV_MAX]; size_t c; for (id = 0; id < PROC_MAX; id++) { @@ -617,15 +622,60 @@ config_setproto(struct relayd *env, struct protocol *proto) iov[c++].iov_len = strlen(proto->style); } - /* XXX struct protocol should be split */ proc_composev_imsg(ps, id, -1, IMSG_CFG_PROTO, -1, iov, c); + } - /* Now send all the protocol key/value nodes */ - if (config_setprotonode(env, id, proto, - RELAY_DIR_REQUEST) == -1 || - config_setprotonode(env, id, proto, - RELAY_DIR_RESPONSE) == -1) - return (-1); + return (0); +} + +int +config_setrule(struct relayd *env, struct protocol *proto) +{ + struct privsep *ps = env->sc_ps; + struct relay_rule *rule; + struct iovec iov[IOV_MAX]; + int id; + size_t c, i; + + for (id = 0; id < PROC_MAX; id++) { + if ((ps->ps_what[id] & CONFIG_PROTOS) == 0 || + id == privsep_process) + continue; + + DPRINTF("%s: sending rules %s to %s", __func__, + proto->name, ps->ps_title[id]); + + /* Now send all the rules */ + TAILQ_FOREACH(rule, &proto->rules, rule_entry) { + rule->rule_protoid = proto->id; + bzero(&rule->rule_ctl, sizeof(rule->rule_ctl)); + c = 0; + iov[c].iov_base = rule; + iov[c++].iov_len = sizeof(*rule); + for (i = 1; i < KEY_TYPE_MAX; i++) { + if (rule->rule_kv[i].kv_key != NULL) { + rule->rule_ctl.kvlen[i].key = + strlen(rule->rule_kv[i].kv_key); + iov[c].iov_base = + rule->rule_kv[i].kv_key; + iov[c++].iov_len = + rule->rule_ctl.kvlen[i].key; + } else + rule->rule_ctl.kvlen[i].key = -1; + if (rule->rule_kv[i].kv_value != NULL) { + rule->rule_ctl.kvlen[i].value = + strlen(rule->rule_kv[i].kv_value); + iov[c].iov_base = + rule->rule_kv[i].kv_value; + iov[c++].iov_len = + rule->rule_ctl.kvlen[i].value; + } else + rule->rule_ctl.kvlen[i].value = -1; + } + + proc_composev_imsg(ps, id, -1, + IMSG_CFG_RULE, -1, iov, c); + } } return (0); @@ -654,11 +704,8 @@ config_getproto(struct relayd *env, struct imsg *imsg) } } - proto->request_nodes = 0; - proto->response_nodes = 0; + TAILQ_INIT(&proto->rules); proto->sslcapass = NULL; - RB_INIT(&proto->request_tree); - RB_INIT(&proto->response_tree); TAILQ_INSERT_TAIL(env->sc_protos, proto, entry); @@ -672,144 +719,68 @@ config_getproto(struct relayd *env, struct imsg *imsg) } int -config_setprotonode(struct relayd *env, enum privsep_procid id, - struct protocol *proto, enum direction dir) +config_getrule(struct relayd *env, struct imsg *imsg) { - struct privsep *ps = env->sc_ps; - struct iovec iov[IOV_MAX]; - size_t c, sz; - struct protonode *proot, *pn; - struct proto_tree *tree; - - if (dir == RELAY_DIR_RESPONSE) - tree = &proto->response_tree; - else - tree = &proto->request_tree; - - sz = c = 0; - RB_FOREACH(proot, proto_tree, tree) { - PROTONODE_FOREACH(pn, proot, entry) { - pn->conf.protoid = proto->id; - pn->conf.dir = dir; - pn->conf.keylen = pn->key ? strlen(pn->key) : 0; - pn->conf.valuelen = pn->value ? strlen(pn->value) : 0; - if (pn->label != 0 && pn->labelname == NULL) - pn->labelname = strdup(pn_id2name(pn->label)); - pn->conf.labelnamelen = pn->labelname ? - strlen(pn->labelname) : 0; - - pn->conf.len = sizeof(*pn) + - pn->conf.keylen + pn->conf.valuelen + - pn->conf.labelnamelen; - - if (pn->conf.len > (MAX_IMSGSIZE - IMSG_HEADER_SIZE)) - return (-1); - - if (c && ((c + 3) >= IOV_MAX || (sz + pn->conf.len) > - (MAX_IMSGSIZE - IMSG_HEADER_SIZE))) { - proc_composev_imsg(ps, id, -1, - IMSG_CFG_PROTONODE, -1, iov, c); - c = sz = 0; - } - - iov[c].iov_base = pn; - iov[c++].iov_len = sizeof(*pn); - if (pn->conf.keylen) { - iov[c].iov_base = pn->key; - iov[c++].iov_len = pn->conf.keylen; - } - if (pn->conf.valuelen) { - iov[c].iov_base = pn->value; - iov[c++].iov_len = pn->conf.valuelen; - } - if (pn->conf.labelnamelen) { - iov[c].iov_base = pn->labelname; - iov[c++].iov_len = pn->conf.labelnamelen; - } - sz += pn->conf.len; - } - } + struct protocol *proto; + struct relay_rule *rule; + size_t s, i; + u_int8_t *p = imsg->data; + ssize_t len; - if (c && sz) - proc_composev_imsg(ps, id, -1, IMSG_CFG_PROTONODE, -1, iov, c); + if ((rule = calloc(1, sizeof(*rule))) == NULL) + return (-1); - return (0); -} + IMSG_SIZE_CHECK(imsg, rule); + memcpy(rule, p, sizeof(*rule)); + s = sizeof(*rule); + len = IMSG_DATA_SIZE(imsg) - s; -int -config_getprotonode(struct relayd *env, struct imsg *imsg) -{ - struct protocol *proto = NULL; - struct protonode pn; - size_t z, s, c = 0; - u_int8_t *p = imsg->data; + if ((proto = proto_find(env, rule->rule_protoid)) == NULL) { + free(rule); + return (-1); + } - bzero(&pn, sizeof(pn)); +#define GETKV(_n, _f) { \ + if (rule->rule_ctl.kvlen[_n]._f >= 0) { \ + /* Also accept "empty" 0-length strings */ \ + if ((len < rule->rule_ctl.kvlen[_n]._f) || \ + (rule->rule_kv[_n].kv_##_f = \ + get_string(p + s, \ + rule->rule_ctl.kvlen[_n]._f)) == NULL) { \ + free(rule); \ + return (-1); \ + } \ + s += rule->rule_ctl.kvlen[_n]._f; \ + len -= rule->rule_ctl.kvlen[_n]._f; \ + \ + DPRINTF("%s: %s %s (len %ld, option %d): %s", __func__, \ + #_n, #_f, rule->rule_ctl.kvlen[_n]._f, \ + rule->rule_kv[_n].kv_option, \ + rule->rule_kv[_n].kv_##_f); \ + } \ +} - IMSG_SIZE_CHECK(imsg, &pn); - for (z = 0; z < (IMSG_DATA_SIZE(imsg) - sizeof(pn)); z += pn.conf.len) { - s = z; - memcpy(&pn, p + s, sizeof(pn)); - s += sizeof(pn); + for (i = 1; i < KEY_TYPE_MAX; i++) { + GETKV(i, key); + GETKV(i, value); + } - if ((proto = proto_find(env, pn.conf.protoid)) == NULL) { - log_debug("%s: unknown protocol %d", __func__, - pn.conf.protoid); - return (-1); - } + if (rule->rule_labelname[0]) + rule->rule_label = label_name2id(rule->rule_labelname); - pn.key = pn.value = pn.labelname = NULL; - bzero(&pn.entry, sizeof(pn.entry)); - bzero(&pn.nodes, sizeof(pn.nodes)); - bzero(&pn.head, sizeof(pn.head)); + if (rule->rule_tagname[0]) + rule->rule_tag = tag_name2id(rule->rule_tagname); - if (pn.conf.keylen) { - if ((pn.key = get_string(p + s, - pn.conf.keylen)) == NULL) { - log_debug("%s: failed to get key", __func__); - return (-1); - } - s += pn.conf.keylen; - } - if (pn.conf.valuelen) { - if ((pn.value = get_string(p + s, - pn.conf.valuelen)) == NULL) { - log_debug("%s: failed to get value", __func__); - if (pn.key != NULL) - free(pn.key); - return (-1); - } - s += pn.conf.valuelen; - } - if (pn.conf.labelnamelen) { - if ((pn.labelname = get_string(p + s, - pn.conf.labelnamelen)) == NULL) { - log_debug("%s: failed to get labelname", - __func__); - return (-1); - } - s += pn.conf.labelnamelen; - } + if (rule->rule_taggedname[0]) + rule->rule_tagged = tag_name2id(rule->rule_taggedname); - if (protonode_add(pn.conf.dir, proto, &pn) == -1) { - if (pn.key != NULL) - free(pn.key); - if (pn.value != NULL) - free(pn.value); - if (pn.labelname != NULL) - free(pn.labelname); - log_debug("%s: failed to add protocol node", __func__); - return (-1); - } - c++; - } + rule->rule_id = proto->rulecount++; - if (!c) - return (0); + TAILQ_INSERT_TAIL(&proto->rules, rule, rule_entry); - DPRINTF("%s: %s %d received %lu nodes for protocol %s", __func__, + DPRINTF("%s: %s %d received rule %u for protocol %s", __func__, env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance, - c, proto->name); + rule->rule_id, proto->name); return (0); } diff --git a/usr.sbin/relayd/http.h b/usr.sbin/relayd/http.h new file mode 100644 index 00000000000..cd7a931030b --- /dev/null +++ b/usr.sbin/relayd/http.h @@ -0,0 +1,371 @@ +/* $OpenBSD: http.h,v 1.1 2014/07/09 16:42:05 reyk Exp $ */ + +/* + * Copyright (c) 2012 - 2014 Reyk Floeter <reyk@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _RELAYD_HTTP_H +#define _RELAYD_HTTP_H + +enum httpmethod { + HTTP_METHOD_NONE = 0, + + /* HTTP/1.1, RFC 2616 */ + HTTP_METHOD_GET, + HTTP_METHOD_HEAD, + HTTP_METHOD_POST, + HTTP_METHOD_PUT, + HTTP_METHOD_DELETE, + HTTP_METHOD_OPTIONS, + HTTP_METHOD_TRACE, + HTTP_METHOD_CONNECT, + + /* WebDAV, RFC 4918 */ + HTTP_METHOD_PROPFIND, + HTTP_METHOD_PROPPATCH, + HTTP_METHOD_MKCOL, + HTTP_METHOD_COPY, + HTTP_METHOD_MOVE, + HTTP_METHOD_LOCK, + HTTP_METHOD_UNLOCK, + + /* PATCH, RFC 5789 */ + HTTP_METHOD_PATCH, + + /* Server response (internal value) */ + HTTP_METHOD_RESPONSE +}; + +struct http_method { + enum httpmethod method_id; + const char *method_name; +}; +#define HTTP_METHODS { \ + { HTTP_METHOD_GET, "GET" }, \ + { HTTP_METHOD_HEAD, "HEAD" }, \ + { HTTP_METHOD_POST, "POST" }, \ + { HTTP_METHOD_PUT, "PUT" }, \ + { HTTP_METHOD_DELETE, "DELETE" }, \ + { HTTP_METHOD_OPTIONS, "OPTIONS" }, \ + { HTTP_METHOD_TRACE, "TRACE" }, \ + { HTTP_METHOD_CONNECT, "CONNECT" }, \ + { HTTP_METHOD_PROPFIND, "PROPFIND" }, \ + { HTTP_METHOD_PROPPATCH, "PROPPATCH" }, \ + { HTTP_METHOD_MKCOL, "MKCOL" }, \ + { HTTP_METHOD_COPY, "COPY" }, \ + { HTTP_METHOD_MOVE, "MOVE" }, \ + { HTTP_METHOD_LOCK, "LOCK" }, \ + { HTTP_METHOD_UNLOCK, "UNLOCK" }, \ + { HTTP_METHOD_PATCH, "PATCH" }, \ + { HTTP_METHOD_NONE, NULL } \ +} + +enum httpheader { + HTTP_HEADER_NONE = 0, + + /* HTTP Header Field Registration, RFC 4229 */ + HTTP_HEADER_A_IM, + HTTP_HEADER_ACCEPT, + HTTP_HEADER_ACCEPT_ADDITIONS, + HTTP_HEADER_ACCEPT_CHARSET, + HTTP_HEADER_ACCEPT_ENCODING, + HTTP_HEADER_ACCEPT_FEATURES, + HTTP_HEADER_ACCEPT_LANGUAGE, + HTTP_HEADER_ACCEPT_RANGES, + HTTP_HEADER_AGE, + HTTP_HEADER_ALLOW, + HTTP_HEADER_ALTERNATES, + HTTP_HEADER_AUTHENTICATION_INFO, + HTTP_HEADER_AUTHORIZATION, + HTTP_HEADER_C_EXT, + HTTP_HEADER_C_MAN, + HTTP_HEADER_C_OPT, + HTTP_HEADER_C_PEP, + HTTP_HEADER_C_PEP_INFO, + HTTP_HEADER_CACHE_CONTROL, + HTTP_HEADER_CONNECTION, + HTTP_HEADER_CONTENT_BASE, + HTTP_HEADER_CONTENT_DISPOSITION, + HTTP_HEADER_CONTENT_ENCODING, + HTTP_HEADER_CONTENT_ID, + HTTP_HEADER_CONTENT_LANGUAGE, + HTTP_HEADER_CONTENT_LENGTH, + HTTP_HEADER_CONTENT_LOCATION, + HTTP_HEADER_CONTENT_MD5, + HTTP_HEADER_CONTENT_RANGE, + HTTP_HEADER_CONTENT_SCRIPT_TYPE, + HTTP_HEADER_CONTENT_STYLE_TYPE, + HTTP_HEADER_CONTENT_TYPE, + HTTP_HEADER_CONTENT_VERSION, + HTTP_HEADER_COOKIE, + HTTP_HEADER_COOKIE2, + HTTP_HEADER_DAV, + HTTP_HEADER_DATE, + HTTP_HEADER_DEFAULT_STYLE, + HTTP_HEADER_DELTA_BASE, + HTTP_HEADER_DEPTH, + HTTP_HEADER_DERIVED_FROM, + HTTP_HEADER_DESTINATION, + HTTP_HEADER_DIFFERENTIAL_ID, + HTTP_HEADER_DIGEST, + HTTP_HEADER_ETAG, + HTTP_HEADER_EXPECT, + HTTP_HEADER_EXPIRES, + HTTP_HEADER_EXT, + HTTP_HEADER_FROM, + HTTP_HEADER_GETPROFILE, + HTTP_HEADER_HOST, + HTTP_HEADER_IM, + HTTP_HEADER_IF, + HTTP_HEADER_IF_MATCH, + HTTP_HEADER_IF_MODIFIED_SINCE, + HTTP_HEADER_IF_NONE_MATCH, + HTTP_HEADER_IF_RANGE, + HTTP_HEADER_IF_UNMODIFIED_SINCE, + HTTP_HEADER_KEEP_ALIVE, + HTTP_HEADER_LABEL, + HTTP_HEADER_LAST_MODIFIED, + HTTP_HEADER_LINK, + HTTP_HEADER_LOCATION, + HTTP_HEADER_LOCK_TOKEN, + HTTP_HEADER_MIME_VERSION, + HTTP_HEADER_MAN, + HTTP_HEADER_MAX_FORWARDS, + HTTP_HEADER_METER, + HTTP_HEADER_NEGOTIATE, + HTTP_HEADER_OPT, + HTTP_HEADER_ORDERING_TYPE, + HTTP_HEADER_OVERWRITE, + HTTP_HEADER_P3P, + HTTP_HEADER_PEP, + HTTP_HEADER_PICS_LABEL, + HTTP_HEADER_PEP_INFO, + HTTP_HEADER_POSITION, + HTTP_HEADER_PRAGMA, + HTTP_HEADER_PROFILEOBJECT, + HTTP_HEADER_PROTOCOL, + HTTP_HEADER_PROTOCOL_INFO, + HTTP_HEADER_PROTOCOL_QUERY, + HTTP_HEADER_PROTOCOL_REQUEST, + HTTP_HEADER_PROXY_AUTHENTICATE, + HTTP_HEADER_PROXY_AUTHENTICATION_INFO, + HTTP_HEADER_PROXY_AUTHORIZATION, + HTTP_HEADER_PROXY_FEATURES, + HTTP_HEADER_PROXY_INSTRUCTION, + HTTP_HEADER_PUBLIC, + HTTP_HEADER_RANGE, + HTTP_HEADER_REFERER, + HTTP_HEADER_RETRY_AFTER, + HTTP_HEADER_SAFE, + HTTP_HEADER_SECURITY_SCHEME, + HTTP_HEADER_SERVER, + HTTP_HEADER_SET_COOKIE, + HTTP_HEADER_SET_COOKIE2, + HTTP_HEADER_SETPROFILE, + HTTP_HEADER_SOAPACTION, + HTTP_HEADER_STATUS_URI, + HTTP_HEADER_SURROGATE_CAPABILITY, + HTTP_HEADER_SURROGATE_CONTROL, + HTTP_HEADER_TCN, + HTTP_HEADER_TE, + HTTP_HEADER_TIMEOUT, + HTTP_HEADER_TRAILER, + HTTP_HEADER_TRANSFER_ENCODING, + HTTP_HEADER_URI, + HTTP_HEADER_UPGRADE, + HTTP_HEADER_USER_AGENT, + HTTP_HEADER_VARIANT_VARY, + HTTP_HEADER_VARY, + HTTP_HEADER_VIA, + HTTP_HEADER_WWW_AUTHENTICATE, + HTTP_HEADER_WANT_DIGEST, + HTTP_HEADER_WARNING, + + /* PATCH, RFC 5789 */ + HTTP_HEADER_ACCEPT_PATCH, + + /* Other extensions */ + HTTP_HEADER_X_REQUESTED_WITH, /* AJAX */ + HTTP_HEADER_X_FORWARDED_FOR, + HTTP_HEADER_X_FORWARDED_BY, + HTTP_HEADER_X_POWERED_BY, + HTTP_HEADER_X_XSS_PROTECTION, + HTTP_HEADER_ORIGIN, /* Anti-XSRF */ + HTTP_HEADER_DNT, /* Do-Not-Track */ + + /* Array size */ + HTTP_HEADER_MAX, +#define HTTP_HEADER_OTHER HTTP_HEADER_MAX +}; + +struct http_header { + enum httpheader header_id; + const char *header_name; + int header_isused; +}; +/* Has to be sorted alphabetically */ +#define HTTP_HEADERS { \ + { HTTP_HEADER_ACCEPT, "Accept" }, \ + { HTTP_HEADER_ACCEPT_ADDITIONS, "Accept-Additions" }, \ + { HTTP_HEADER_ACCEPT_CHARSET, "Accept-Charset" }, \ + { HTTP_HEADER_ACCEPT_ENCODING, "Accept-Encoding" }, \ + { HTTP_HEADER_ACCEPT_FEATURES, "Accept-Features" }, \ + { HTTP_HEADER_ACCEPT_LANGUAGE, "Accept-Language" }, \ + { HTTP_HEADER_ACCEPT_PATCH, "Accept-Patch" }, \ + { HTTP_HEADER_ACCEPT_RANGES, "Accept-Ranges" }, \ + { HTTP_HEADER_AGE, "Age" }, \ + { HTTP_HEADER_ALLOW, "Allow" }, \ + { HTTP_HEADER_ALTERNATES, "Alternates" }, \ + { HTTP_HEADER_AUTHENTICATION_INFO, "Authentication-Info" },\ + { HTTP_HEADER_AUTHORIZATION, "Authorization" }, \ + { HTTP_HEADER_A_IM, "A-IM" }, \ + { HTTP_HEADER_CACHE_CONTROL, "Cache-Control" }, \ + { HTTP_HEADER_CONNECTION, "Connection" }, \ + { HTTP_HEADER_CONTENT_BASE, "Content-Base" }, \ + { HTTP_HEADER_CONTENT_DISPOSITION, "Content-Disposition" },\ + { HTTP_HEADER_CONTENT_ENCODING, "Content-Encoding" }, \ + { HTTP_HEADER_CONTENT_ID, "Content-ID" }, \ + { HTTP_HEADER_CONTENT_LANGUAGE, "Content-Language" }, \ + { HTTP_HEADER_CONTENT_LENGTH, "Content-Length" }, \ + { HTTP_HEADER_CONTENT_LOCATION, "Content-Location" }, \ + { HTTP_HEADER_CONTENT_MD5, "Content-MD5" }, \ + { HTTP_HEADER_CONTENT_RANGE, "Content-Range" }, \ + { HTTP_HEADER_CONTENT_SCRIPT_TYPE, "Content-Script-Type" },\ + { HTTP_HEADER_CONTENT_STYLE_TYPE, "Content-Style-Type" }, \ + { HTTP_HEADER_CONTENT_TYPE, "Content-Type" }, \ + { HTTP_HEADER_CONTENT_VERSION, "Content-Version" }, \ + { HTTP_HEADER_COOKIE, "Cookie" }, \ + { HTTP_HEADER_COOKIE2, "Cookie2" }, \ + { HTTP_HEADER_C_EXT, "C-Ext" }, \ + { HTTP_HEADER_C_MAN, "C-Man" }, \ + { HTTP_HEADER_C_OPT, "C-Opt" }, \ + { HTTP_HEADER_C_PEP, "C-PEP" }, \ + { HTTP_HEADER_C_PEP_INFO, "C-PEP-Info" }, \ + { HTTP_HEADER_DATE, "Date" }, \ + { HTTP_HEADER_DAV, "DAV" }, \ + { HTTP_HEADER_DEFAULT_STYLE, "Default-Style" }, \ + { HTTP_HEADER_DELTA_BASE, "Delta-Base" }, \ + { HTTP_HEADER_DEPTH, "Depth" }, \ + { HTTP_HEADER_DERIVED_FROM, "Derived-From" }, \ + { HTTP_HEADER_DESTINATION, "Destination" }, \ + { HTTP_HEADER_DIFFERENTIAL_ID, "Differential-Id" }, \ + { HTTP_HEADER_DIGEST, "Digest" }, \ + { HTTP_HEADER_DNT, "DNT" }, \ + { HTTP_HEADER_ETAG, "ETag" }, \ + { HTTP_HEADER_EXPECT, "Expect" }, \ + { HTTP_HEADER_EXPIRES, "Expires" }, \ + { HTTP_HEADER_EXT, "Ext" }, \ + { HTTP_HEADER_FROM, "From" }, \ + { HTTP_HEADER_GETPROFILE, "GetProfile" }, \ + { HTTP_HEADER_HOST, "Host", 1 /* used */ }, \ + { HTTP_HEADER_IF, "If" }, \ + { HTTP_HEADER_IF_MATCH, "If-Match" }, \ + { HTTP_HEADER_IF_MODIFIED_SINCE, "If-Modified-Since" }, \ + { HTTP_HEADER_IF_NONE_MATCH, "If-None-Match" }, \ + { HTTP_HEADER_IF_RANGE, "If-Range" }, \ + { HTTP_HEADER_IF_UNMODIFIED_SINCE, "If-Unmodified-Since" },\ + { HTTP_HEADER_IM, "IM" }, \ + { HTTP_HEADER_KEEP_ALIVE, "Keep-Alive" }, \ + { HTTP_HEADER_LABEL, "Label" }, \ + { HTTP_HEADER_LAST_MODIFIED, "Last-Modified" }, \ + { HTTP_HEADER_LINK, "Link" }, \ + { HTTP_HEADER_LOCATION, "Location" }, \ + { HTTP_HEADER_LOCK_TOKEN, "Lock-Token" }, \ + { HTTP_HEADER_MAN, "Man" }, \ + { HTTP_HEADER_MAX_FORWARDS, "Max-Forwards" }, \ + { HTTP_HEADER_METER, "Meter" }, \ + { HTTP_HEADER_MIME_VERSION, "MIME-Version" }, \ + { HTTP_HEADER_NEGOTIATE, "Negotiate" }, \ + { HTTP_HEADER_OPT, "Opt" }, \ + { HTTP_HEADER_ORDERING_TYPE, "Ordering-Type" }, \ + { HTTP_HEADER_ORIGIN, "Origin" }, \ + { HTTP_HEADER_OVERWRITE, "Overwrite" }, \ + { HTTP_HEADER_P3P, "P3P" }, \ + { HTTP_HEADER_PEP, "PEP" }, \ + { HTTP_HEADER_PEP_INFO, "Pep-Info" }, \ + { HTTP_HEADER_PICS_LABEL, "PICS-Label" }, \ + { HTTP_HEADER_POSITION, "Position" }, \ + { HTTP_HEADER_PRAGMA, "Pragma" }, \ + { HTTP_HEADER_PROFILEOBJECT, "ProfileObject" }, \ + { HTTP_HEADER_PROTOCOL, "Protocol" }, \ + { HTTP_HEADER_PROTOCOL_INFO, "Protocol-Info" }, \ + { HTTP_HEADER_PROTOCOL_QUERY, "Protocol-Query" }, \ + { HTTP_HEADER_PROTOCOL_REQUEST, "Protocol-Request" }, \ + { HTTP_HEADER_PROXY_AUTHENTICATE, "Proxy-Authenticate" }, \ + { HTTP_HEADER_PROXY_AUTHENTICATION_INFO,"Proxy-Authenticate-Info" },\ + { HTTP_HEADER_PROXY_AUTHORIZATION, "Proxy-Authorization" },\ + { HTTP_HEADER_PROXY_FEATURES, "Proxy-Features" }, \ + { HTTP_HEADER_PROXY_INSTRUCTION, "Proxy-Instruction" }, \ + { HTTP_HEADER_PUBLIC, "Public" }, \ + { HTTP_HEADER_RANGE, "Range" }, \ + { HTTP_HEADER_REFERER, "Referer" }, \ + { HTTP_HEADER_RETRY_AFTER, "Retry-After" }, \ + { HTTP_HEADER_SAFE, "Safe" }, \ + { HTTP_HEADER_SECURITY_SCHEME, "Security-Scheme" }, \ + { HTTP_HEADER_SERVER, "Server" }, \ + { HTTP_HEADER_SETPROFILE, "SetProfile" }, \ + { HTTP_HEADER_SET_COOKIE, "Set-Cookie" }, \ + { HTTP_HEADER_SET_COOKIE2, "Set-Cookie2" }, \ + { HTTP_HEADER_SOAPACTION, "SoapAction" }, \ + { HTTP_HEADER_STATUS_URI, "Status-URI" }, \ + { HTTP_HEADER_SURROGATE_CAPABILITY, "Surrogate-Capability" },\ + { HTTP_HEADER_SURROGATE_CONTROL, "Surrogate-Control" }, \ + { HTTP_HEADER_TCN, "TCN" }, \ + { HTTP_HEADER_TE, "TE" }, \ + { HTTP_HEADER_TIMEOUT, "Timeout" }, \ + { HTTP_HEADER_TRAILER, "Trailer" }, \ + { HTTP_HEADER_TRANSFER_ENCODING, "Transfer-Encoding" }, \ + { HTTP_HEADER_UPGRADE, "Upgrade" }, \ + { HTTP_HEADER_URI, "URI" }, \ + { HTTP_HEADER_USER_AGENT, "User-Agent" }, \ + { HTTP_HEADER_VARIANT_VARY, "Variant-Vary" }, \ + { HTTP_HEADER_VARY, "Vary" }, \ + { HTTP_HEADER_VIA, "Via" }, \ + { HTTP_HEADER_WANT_DIGEST, "Want-Digest" }, \ + { HTTP_HEADER_WARNING, "Warning" }, \ + { HTTP_HEADER_WWW_AUTHENTICATE, "WWW-Authenticate" }, \ + { HTTP_HEADER_X_FORWARDED_BY, "X-Forwarded-By" }, \ + { HTTP_HEADER_X_FORWARDED_FOR, "X-Forwarded-For" }, \ + { HTTP_HEADER_X_POWERED_BY, "X-Powered-By" }, \ + { HTTP_HEADER_X_REQUESTED_WITH, "X-Requested-With" }, \ + { HTTP_HEADER_X_XSS_PROTECTION, "X-XSS-Protection" }, \ + { HTTP_HEADER_OTHER, NULL } \ +} + +/* Used during runtime */ +struct http_descriptor { + struct kv http_pathquery; + struct kv http_matchquery; +#define http_path http_pathquery.kv_key +#define http_query http_pathquery.kv_value +#define http_rescode http_pathquery.kv_key +#define http_resmesg http_pathquery.kv_value +#define query_key http_matchquery.kv_key +#define query_val http_matchquery.kv_value + + char *http_version; + enum httpmethod http_method; + int http_chunked; + + /* + * A linked list of headers and an array with pointers + * pointing to well-known headers that have been found to + * speed up lookups. + */ + struct kvlist http_headers; + struct kv *http_header[HTTP_HEADER_MAX]; +}; + +#endif /* _RELAYD_HTTP_H */ diff --git a/usr.sbin/relayd/name2id.c b/usr.sbin/relayd/name2id.c index fb8f1054b0c..4009fc6488b 100644 --- a/usr.sbin/relayd/name2id.c +++ b/usr.sbin/relayd/name2id.c @@ -1,4 +1,4 @@ -/* $OpenBSD: name2id.c,v 1.2 2007/12/07 17:17:00 reyk Exp $ */ +/* $OpenBSD: name2id.c,v 1.3 2014/07/09 16:42:05 reyk Exp $ */ /* * Copyright (c) 2004, 2005 Henning Brauer <henning@openbsd.org> @@ -16,16 +16,15 @@ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <sys/param.h> +#include <sys/types.h> #include <sys/socket.h> #include <sys/queue.h> #include <net/if.h> +#include <errno.h> #include <stdlib.h> -#include <stdio.h> #include <string.h> -#include <errno.h> #include <event.h> #include <openssl/ssl.h> @@ -48,31 +47,55 @@ const char *_id2name(struct n2id_labels *, u_int16_t); void _unref(struct n2id_labels *, u_int16_t); void _ref(struct n2id_labels *, u_int16_t); -/* for protocolnode labels */ -struct n2id_labels pn_labels = TAILQ_HEAD_INITIALIZER(pn_labels); +struct n2id_labels relay_labels = TAILQ_HEAD_INITIALIZER(relay_labels); +struct n2id_labels relay_tags = TAILQ_HEAD_INITIALIZER(relay_tags); + +u_int16_t +tag_name2id(const char *name) +{ + return (_name2id(&relay_tags, name)); +} + +const char * +tag_id2name(u_int16_t id) +{ + return (_id2name(&relay_tags, id)); +} + +void +tag_unref(u_int16_t id) +{ + _unref(&relay_tags, id); +} + +void +tag_ref(u_int16_t id) +{ + _ref(&relay_tags, id); +} u_int16_t -pn_name2id(const char *name) +label_name2id(const char *name) { - return (_name2id(&pn_labels, name)); + return (_name2id(&relay_labels, name)); } const char * -pn_id2name(u_int16_t id) +label_id2name(u_int16_t id) { - return (_id2name(&pn_labels, id)); + return (_id2name(&relay_labels, id)); } void -pn_unref(u_int16_t id) +label_unref(u_int16_t id) { - _unref(&pn_labels, id); + _unref(&relay_labels, id); } void -pn_ref(u_int16_t id) +label_ref(u_int16_t id) { - _ref(&pn_labels, id); + _ref(&relay_labels, id); } u_int16_t diff --git a/usr.sbin/relayd/parse.y b/usr.sbin/relayd/parse.y index fe6ced2b953..624ea6cb5f2 100644 --- a/usr.sbin/relayd/parse.y +++ b/usr.sbin/relayd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.183 2014/06/25 11:05:15 reyk Exp $ */ +/* $OpenBSD: parse.y,v 1.184 2014/07/09 16:42:05 reyk Exp $ */ /* * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org> @@ -56,6 +56,7 @@ #include <openssl/ssl.h> #include "relayd.h" +#include "http.h" #include "snmp.h" TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); @@ -107,12 +108,16 @@ static struct relay *rlay = NULL; static struct host *hst = NULL; struct relaylist relays; static struct protocol *proto = NULL; -static struct protonode node; +static struct relay_rule *rule = NULL; static struct router *router = NULL; -static u_int16_t label = 0; +static int label = 0; +static int tagged = 0; +static int tag = 0; static in_port_t tableport = 0; -static int nodedirection; static int dstmode; +static enum key_type keytype = KEY_TYPE_NONE; +static enum direction dir = RELAY_DIR_ANY; +static char *rulefile = NULL; struct address *host_v4(const char *); struct address *host_v6(const char *); @@ -138,6 +143,7 @@ typedef struct { struct timeval tv; struct table *table; struct portrange port; + enum direction dir; struct { struct sockaddr_storage ss; char name[MAXHOSTNAMELEN]; @@ -152,30 +158,32 @@ typedef struct { %} -%token ALL APPEND BACKLOG BACKUP BUFFER CA CACHE CHANGE CHECK -%token CIPHERS CODE COOKIE DEMOTE DIGEST DISABLE ERROR EXPECT -%token EXTERNAL FILENAME FILTER FORWARD FROM HASH HEADER HOST ICMP -%token INCLUDE INET INET6 INTERFACE INTERVAL IP LABEL LISTEN -%token LOADBALANCE LOG LOOKUP MARK MARKED MODE NAT NO DESTINATION -%token NODELAY NOTHING ON PARENT PATH PORT PREFORK PRIORITY PROTO -%token QUERYSTR REAL REDIRECT RELAY REMOVE REQUEST RESPONSE RETRY +%token ALL APPEND BACKLOG BACKUP BUFFER CA CACHE SET CHECK +%token CIPHERS CODE COOKIE DEMOTE DIGEST DISABLE ERROR EXPECT PASS BLOCK +%token EXTERNAL FILENAME FORWARD FROM HASH HEADER HOST ICMP +%token INCLUDE INET INET6 INTERFACE INTERVAL IP LABEL LISTEN VALUE +%token LOADBALANCE LOG LOOKUP METHOD MODE NAT NO DESTINATION +%token NODELAY NOTHING ON PARENT PATH PFTAG PORT PREFORK PRIORITY PROTO +%token QUERYSTR REAL REDIRECT RELAY REMOVE REQUEST RESPONSE RETRY QUICK %token RETURN ROUNDROBIN ROUTE SACK SCRIPT SEND SESSION SNMP SOCKET SPLICE -%token SSL STICKYADDR STYLE TABLE TAG TCP TIMEOUT TO ROUTER RTLABEL +%token SSL STICKYADDR STYLE TABLE TAG TAGGED TCP TIMEOUT TO ROUTER RTLABEL %token TRANSPARENT TRAP UPDATES URL VIRTUAL WITH TTL RTABLE MATCH %token RANDOM LEASTSTATES SRCHASH KEY CERTIFICATE PASSWORD ECDH CURVE %token <v.string> STRING %token <v.number> NUMBER -%type <v.string> hostname interface table optstring -%type <v.number> http_type loglevel mark trap -%type <v.number> direction dstmode flag forwardmode retry +%type <v.string> hostname interface table value optstring +%type <v.number> http_type loglevel quick trap +%type <v.number> dstmode flag forwardmode retry %type <v.number> optssl optsslclient sslcache %type <v.number> redirect_proto relay_proto match +%type <v.number> action ruleaf key_option %type <v.port> port %type <v.host> host %type <v.addr> address %type <v.tv> timeout -%type <v.digest> digest +%type <v.digest> digest optdigest %type <v.table> tablespec +%type <v.dir> dir %% @@ -541,7 +549,7 @@ rdroptsl : forwardmode TO tablespec interface { } | DISABLE { rdr->conf.flags |= F_DISABLE; } | STICKYADDR { rdr->conf.flags |= F_STICKY; } - | match TAG STRING { + | match PFTAG STRING { conf->sc_flags |= F_NEEDPF; if (strlcpy(rdr->conf.tag, $3, sizeof(rdr->conf.tag)) >= @@ -663,6 +671,7 @@ tablespec : table { } free($1); table = tb; + dstmode = RELAY_DSTMODE_DEFAULT; } tableopts_l { struct table *tb; if (table->conf.port == 0) @@ -845,6 +854,16 @@ digest : DIGEST STRING } ; +optdigest : digest { + $$.digest = $1.digest; + $$.type = $1.type; + } + | STRING { + $$.digest = $1; + $$.type = DIGEST_NONE; + } + ; + proto : relay_proto PROTO STRING { struct protocol *p; @@ -882,6 +901,7 @@ proto : relay_proto PROTO STRING { p->tcpflags = TCPFLAG_DEFAULT; p->sslflags = SSLFLAG_DEFAULT; p->tcpbacklog = RELAY_BACKLOG; + TAILQ_INIT(&p->rules); (void)strlcpy(p->sslciphers, SSLCIPHERS_DEFAULT, sizeof(p->sslciphers)); p->sslecdhcurve = SSLECDHCURVE_DEFAULT; @@ -890,8 +910,6 @@ proto : relay_proto PROTO STRING { free(p); YYERROR; } - RB_INIT(&p->request_tree); - RB_INIT(&p->response_tree); proto = p; } protopts_n { conf->sc_protocount++; @@ -920,37 +938,10 @@ protoptsl : SSL sslflags | TCP '{' tcpflags_l '}' | RETURN ERROR opteflags { proto->flags |= F_RETURN; } | RETURN ERROR '{' eflags_l '}' { proto->flags |= F_RETURN; } - | LABEL STRING { - label = pn_name2id($2); - free($2); - if (label == 0) { - yyerror("invalid protocol action label"); - YYERROR; - } - } - | NO LABEL { - label = 0; - } - | direction { - node.label = label; - node.labelname = NULL; - nodedirection = $1; - } protonode { - if (nodedirection != -1 && - protonode_add(nodedirection, proto, &node) == -1) { - yyerror("failed to add protocol node"); - YYERROR; - } - bzero(&node, sizeof(node)); - } + | filterrule | include ; -direction : /* empty */ { $$ = RELAY_DIR_REQUEST; } - | REQUEST { $$ = RELAY_DIR_REQUEST; } - | RESPONSE { $$ = RELAY_DIR_RESPONSE; } - ; - tcpflags_l : tcpflags comma tcpflags_l | tcpflags ; @@ -1079,246 +1070,387 @@ flag : STRING { } ; -protonode : nodetype APPEND STRING TO STRING nodeopts { - if (node.type != NODE_TYPE_HEADER) { - yyerror("action only supported for headers"); - free($5); - free($3); +sslcache : NUMBER { + if ($1 < 0) { + yyerror("invalid sslcache value: %d", $1); YYERROR; } - node.action = NODE_ACTION_APPEND; - node.key = strdup($5); - node.value = strdup($3); - if (node.key == NULL || node.value == NULL) + $$ = $1; + } + | DISABLE { $$ = -2; } + ; + +filterrule : action dir quick ruleaf rulesrc ruledst { + if ((rule = calloc(1, sizeof(*rule))) == NULL) fatal("out of memory"); - if (strchr(node.value, '$') != NULL) - node.flags |= PNFLAG_MACRO; - free($5); - free($3); + + rule->rule_action = $1; + rule->rule_proto = proto->type; + rule->rule_dir = $2; + rule->rule_flags |= $3; + rule->rule_af = $4; + + rulefile = NULL; + } ruleopts_l { + if (rule_add(proto, rule, rulefile) == -1) { + if (rulefile == NULL) { + yyerror("failed to load rule"); + } else { + yyerror("failed to load rules from %s", + rulefile); + free(rulefile); + } + rule_free(rule); + free(rule); + YYERROR; + } + if (rulefile) + free(rulefile); + rulefile = NULL; + rule = NULL; + keytype = KEY_TYPE_NONE; } - | nodetype CHANGE STRING TO STRING nodeopts { - if (node.type != NODE_TYPE_HEADER) { - yyerror("action only supported for headers"); - free($5); - free($3); + ; + +action : PASS { $$ = RULE_ACTION_PASS; } + | BLOCK { $$ = RULE_ACTION_BLOCK; } + | MATCH { $$ = RULE_ACTION_MATCH; } + ; + +dir : /* empty */ { + $$ = dir = RELAY_DIR_REQUEST; + } + | REQUEST { + $$ = dir = RELAY_DIR_REQUEST; + } + | RESPONSE { + $$ = dir = RELAY_DIR_RESPONSE; + } + ; + +quick : /* empty */ { $$ = 0; } + | QUICK { $$ = RULE_FLAG_QUICK; } + ; + +ruleaf : /* empty */ { $$ = AF_UNSPEC; } + | INET6 { $$ = AF_INET6; } + | INET { $$ = AF_INET; } + ; + +rulesrc : /* XXX */ + ; + +ruledst : /* XXX */ + ; + +ruleopts_l : /* empty */ + | ruleopts_t + ; + +ruleopts_t : ruleopts ruleopts_t + | ruleopts + ; + +ruleopts : METHOD STRING { + u_int id; + if ((id = relay_httpmethod_byname($2)) == + HTTP_HEADER_NONE) { + yyerror("unknown HTTP method currently not " + "supported"); + free($2); YYERROR; } - node.action = NODE_ACTION_CHANGE; - node.key = strdup($3); - node.value = strdup($5); - if (node.key == NULL || node.value == NULL) + rule->rule_method = id; + free($2); + } + | COOKIE key_option STRING value { + keytype = KEY_TYPE_COOKIE; + rule->rule_kv[keytype].kv_key = strdup($3); + rule->rule_kv[keytype].kv_option = $2; + rule->rule_kv[keytype].kv_value = (($4 != NULL) ? + strdup($4) : strdup("*")); + if (rule->rule_kv[keytype].kv_key == NULL || + rule->rule_kv[keytype].kv_value == NULL) + fatal("out of memory"); + free($3); + if ($4) + free($4); + rule->rule_kv[keytype].kv_type = keytype; + } + | COOKIE key_option { + keytype = KEY_TYPE_COOKIE; + rule->rule_kv[keytype].kv_option = $2; + rule->rule_kv[keytype].kv_type = keytype; + } + | HEADER key_option STRING value { + keytype = KEY_TYPE_HEADER; + memset(&rule->rule_kv[keytype], 0, + sizeof(rule->rule_kv[keytype])); + rule->rule_kv[keytype].kv_header_id = + relay_httpheader_byname($3); + rule->rule_kv[keytype].kv_option = $2; + rule->rule_kv[keytype].kv_key = strdup($3); + rule->rule_kv[keytype].kv_value = (($4 != NULL) ? + strdup($4) : strdup("*")); + if (rule->rule_kv[keytype].kv_key == NULL || + rule->rule_kv[keytype].kv_value == NULL) fatal("out of memory"); - if (strchr(node.value, '$') != NULL) - node.flags |= PNFLAG_MACRO; - free($5); free($3); + if ($4) + free($4); + rule->rule_kv[keytype].kv_type = keytype; + } + | HEADER key_option { + keytype = KEY_TYPE_HEADER; + rule->rule_kv[keytype].kv_option = $2; + rule->rule_kv[keytype].kv_type = keytype; + } + | PATH key_option STRING value { + keytype = KEY_TYPE_PATH; + rule->rule_kv[keytype].kv_option = $2; + rule->rule_kv[keytype].kv_key = strdup($3); + rule->rule_kv[keytype].kv_value = (($4 != NULL) ? + strdup($4) : strdup("*")); + if (rule->rule_kv[keytype].kv_key == NULL || + rule->rule_kv[keytype].kv_value == NULL) + fatal("out of memory"); + free($3); + if ($4) + free($4); + rule->rule_kv[keytype].kv_type = keytype; + } + | PATH key_option { + keytype = KEY_TYPE_PATH; + rule->rule_kv[keytype].kv_option = $2; + rule->rule_kv[keytype].kv_type = keytype; } - | nodetype REMOVE STRING nodeopts { - if (node.type != NODE_TYPE_HEADER) { - yyerror("action only supported for headers"); + | QUERYSTR key_option STRING value { + switch ($2) { + case KEY_OPTION_APPEND: + case KEY_OPTION_SET: + case KEY_OPTION_REMOVE: + yyerror("combining query type and the given " + "option is not supported"); free($3); + if ($4) + free($4); YYERROR; + break; } - node.action = NODE_ACTION_REMOVE; - node.key = strdup($3); - node.value = NULL; - if (node.key == NULL) + keytype = KEY_TYPE_QUERY; + rule->rule_kv[keytype].kv_option = $2; + rule->rule_kv[keytype].kv_key = strdup($3); + rule->rule_kv[keytype].kv_value = (($4 != NULL) ? + strdup($4) : strdup("*")); + if (rule->rule_kv[keytype].kv_key == NULL || + rule->rule_kv[keytype].kv_value == NULL) fatal("out of memory"); free($3); + if ($4) + free($4); + rule->rule_kv[keytype].kv_type = keytype; } - | nodetype REMOVE { - if (node.type != NODE_TYPE_HEADER) { - yyerror("action only supported for headers"); + | QUERYSTR key_option { + switch ($2) { + case KEY_OPTION_APPEND: + case KEY_OPTION_SET: + case KEY_OPTION_REMOVE: + yyerror("combining query type and the given " + "option is not supported"); YYERROR; + break; } - node.action = NODE_ACTION_REMOVE; - node.key = NULL; - node.value = NULL; - } nodefile - | nodetype EXPECT STRING FROM STRING nodeopts { - node.action = NODE_ACTION_EXPECT; - node.key = strdup($5); - node.value = strdup($3); - if (node.key == NULL || node.value == NULL) - fatal("out of memory"); - free($5); - free($3); - proto->lateconnect++; + keytype = KEY_TYPE_QUERY; + rule->rule_kv[keytype].kv_option = $2; + rule->rule_kv[keytype].kv_type = keytype; } - | nodetype EXPECT STRING nodeopts { - node.action = NODE_ACTION_EXPECT; - node.key = strdup($3); - node.value = strdup("*"); - if (node.key == NULL || node.value == NULL) - fatal("out of memory"); - free($3); - proto->lateconnect++; - } - | nodetype EXPECT { - node.action = NODE_ACTION_EXPECT; - node.key = NULL; - node.value = "*"; - proto->lateconnect++; - } nodefile - | nodetype EXPECT digest nodeopts { - if (node.type != NODE_TYPE_URL) { - yyerror("digest not supported for this type"); + | URL key_option optdigest value { + switch ($2) { + case KEY_OPTION_APPEND: + case KEY_OPTION_SET: + case KEY_OPTION_REMOVE: + yyerror("combining url type and the given " + "option is not supported"); free($3.digest); + free($4); YYERROR; + break; } - node.action = NODE_ACTION_EXPECT; - node.key = strdup($3.digest); - node.flags |= PNFLAG_LOOKUP_DIGEST($3.type); - node.value = strdup("*"); - if (node.key == NULL || node.value == NULL) + keytype = KEY_TYPE_URL; + rule->rule_kv[keytype].kv_option = $2; + rule->rule_kv[keytype].kv_key = strdup($3.digest); + rule->rule_kv[keytype].kv_digest = $3.type; + rule->rule_kv[keytype].kv_value = (($4 != NULL) ? + strdup($4) : strdup("*")); + if (rule->rule_kv[keytype].kv_key == NULL || + rule->rule_kv[keytype].kv_value == NULL) fatal("out of memory"); free($3.digest); - proto->lateconnect++; - } - | nodetype FILTER STRING FROM STRING nodeopts { - node.action = NODE_ACTION_FILTER; - node.key = strdup($5); - node.value = strdup($3); - if (node.key == NULL || node.value == NULL) - fatal("out of memory"); - free($5); - free($3); - proto->lateconnect++; + if ($4) + free($4); + rule->rule_kv[keytype].kv_type = keytype; } - | nodetype FILTER STRING nodeopts { - node.action = NODE_ACTION_FILTER; - node.key = strdup($3); - node.value = strdup("*"); - if (node.key == NULL || node.value == NULL) - fatal("out of memory"); - free($3); - proto->lateconnect++; - } - | nodetype FILTER { - node.action = NODE_ACTION_FILTER; - node.key = NULL; - node.value = "*"; - proto->lateconnect++; - } nodefile - | nodetype FILTER digest nodeopts { - if (node.type != NODE_TYPE_URL) { - yyerror("digest not supported for this type"); - free($3.digest); + | URL key_option { + switch ($2) { + case KEY_OPTION_APPEND: + case KEY_OPTION_SET: + case KEY_OPTION_REMOVE: + yyerror("combining url type and the given " + "option is not supported"); YYERROR; + break; } - node.action = NODE_ACTION_FILTER; - node.key = strdup($3.digest); - node.flags |= PNFLAG_LOOKUP_DIGEST($3.type); - node.value = strdup("*"); - if (node.key == NULL || node.value == NULL) - fatal("out of memory"); - free($3.digest); - proto->lateconnect++; + keytype = KEY_TYPE_URL; + rule->rule_kv[keytype].kv_option = $2; + rule->rule_kv[keytype].kv_type = keytype; } - | nodetype HASH STRING nodeopts { - node.action = NODE_ACTION_HASH; - node.key = strdup($3); - node.value = NULL; - if (node.key == NULL) - fatal("out of memory"); - free($3); - proto->lateconnect++; - } - | nodetype LOG STRING nodeopts { - node.action = NODE_ACTION_LOG; - node.key = strdup($3); - node.value = NULL; - node.flags |= PNFLAG_LOG; - if (node.key == NULL) - fatal("out of memory"); - free($3); - } - | nodetype LOG { - node.action = NODE_ACTION_LOG; - node.key = NULL; - node.value = NULL; - node.flags |= PNFLAG_LOG; - } nodefile - | nodetype MARK STRING FROM STRING WITH mark log { - node.action = NODE_ACTION_MARK; - node.key = strdup($5); - node.value = strdup($3); - node.mark = $7; - if (node.key == NULL || node.value == NULL) - fatal("out of memory"); - free($3); - free($5); - } - | nodetype MARK STRING WITH mark nodeopts { - if (node.mark) { - yyerror("either mark or marked"); + | FORWARD TO table { + if (table_findbyname(conf, $3) == NULL) { + yyerror("undefined forward table"); + free($3); + YYERROR; + } + if (strlcpy(rule->rule_tablename, $3, + sizeof(rule->rule_tablename)) >= + sizeof(rule->rule_tablename)) { + yyerror("invalid forward table name"); free($3); YYERROR; } - node.action = NODE_ACTION_MARK; - node.key = strdup($3); - node.value = strdup("*"); - node.mark = $5; /* overwrite */ - if (node.key == NULL || node.value == NULL) - fatal("out of memory"); free($3); } - ; - -nodefile : FILENAME STRING nodeopts { - if (protonode_load(nodedirection, - proto, &node, $2) == -1) { - yyerror("failed to load from file: %s", $2); + | TAG STRING { + tag = tag_name2id($2); + if (rule->rule_tag) { + yyerror("tag already defined"); + free($2); + rule_free(rule); + free(rule); + YYERROR; + } + if (tag == 0) { + yyerror("invalid tag"); + free($2); + rule_free(rule); + free(rule); + YYERROR; + } + rule->rule_tag = tag; + if (strlcpy(rule->rule_tagname, $2, + sizeof(rule->rule_tagname)) >= + sizeof(rule->rule_tagname)) { + yyerror("tag truncated"); free($2); + rule_free(rule); + free(rule); YYERROR; } free($2); - nodedirection = -1; /* don't add template node */ } - ; - -nodeopts : marked log - ; - -marked : /* empty */ - | MARKED mark { node.mark = $2; } - ; - -log : /* empty */ - | LOG { node.flags |= PNFLAG_LOG; } - ; - -mark : NUMBER { - if ($1 <= 0 || $1 >= (int)USHRT_MAX) { - yyerror("invalid mark: %d", $1); + | NO TAG { + if (tag == 0) { + yyerror("no tag defined"); YYERROR; } - $$ = $1; + rule->rule_tag = -1; + memset(rule->rule_tagname, 0, + sizeof(rule->rule_tagname)); } - ; - -nodetype : HEADER { - node.type = NODE_TYPE_HEADER; + | TAGGED STRING { + tagged = tag_name2id($2); + if (rule->rule_tagged) { + yyerror("tagged already defined"); + free($2); + rule_free(rule); + free(rule); + YYERROR; + } + if (tagged == 0) { + yyerror("invalid tag"); + free($2); + rule_free(rule); + free(rule); + YYERROR; + } + rule->rule_tagged = tagged; + if (strlcpy(rule->rule_taggedname, $2, + sizeof(rule->rule_taggedname)) >= + sizeof(rule->rule_taggedname)) { + yyerror("tagged truncated"); + free($2); + rule_free(rule); + free(rule); + YYERROR; + } + free($2); } - | QUERYSTR { node.type = NODE_TYPE_QUERY; } - | COOKIE { - node.type = NODE_TYPE_COOKIE; + | LABEL STRING { + label = label_name2id($2); + if (rule->rule_label) { + yyerror("label already defined"); + free($2); + rule_free(rule); + free(rule); + YYERROR; + } + if (label == 0) { + yyerror("invalid label"); + free($2); + rule_free(rule); + free(rule); + YYERROR; + } + rule->rule_label = label; + if (strlcpy(rule->rule_labelname, $2, + sizeof(rule->rule_labelname)) >= + sizeof(rule->rule_labelname)) { + yyerror("label truncated"); + free($2); + rule_free(rule); + free(rule); + YYERROR; + } + free($2); } - | PATH { - proto->flags |= F_LOOKUP_PATH; - node.type = NODE_TYPE_PATH; + | NO LABEL { + if (label == 0) { + yyerror("no label defined"); + YYERROR; + } + rule->rule_label = -1; + memset(rule->rule_labelname, 0, + sizeof(rule->rule_labelname)); } - | URL { node.type = NODE_TYPE_URL; } - ; - -sslcache : NUMBER { - if ($1 < 0) { - yyerror("invalid sslcache value: %d", $1); + | FILENAME STRING value { + if (rulefile != NULL) { + yyerror("only one file per rule supported"); + free($2); + free($3); + rule_free(rule); + free(rule); YYERROR; } - $$ = $1; + if ($3) { + if ((rule->rule_kv[keytype].kv_value = + strdup($3)) == NULL) + fatal("out of memory"); + free($3); + } else + rule->rule_kv[keytype].kv_value = NULL; + rulefile = $2; } - | DISABLE { $$ = -2; } + ; + +value : /* empty */ { $$ = NULL; } + | VALUE STRING { $$ = $2; } + ; + +key_option : /* empty */ { $$ = KEY_OPTION_NONE; } + | APPEND { $$ = KEY_OPTION_APPEND; } + | SET { $$ = KEY_OPTION_SET; } + | REMOVE { $$ = KEY_OPTION_REMOVE; } + | HASH { $$ = KEY_OPTION_HASH; } + | LOG { $$ = KEY_OPTION_LOG; } ; relay : RELAY STRING { @@ -1740,7 +1872,7 @@ dstaf : /* empty */ { } ; -interface : /*empty*/ { $$ = NULL; } +interface : /* empty */ { $$ = NULL; } | INTERFACE STRING { $$ = $2; } ; @@ -1914,11 +2046,11 @@ lookup(char *s) { "append", APPEND }, { "backlog", BACKLOG }, { "backup", BACKUP }, + { "block", BLOCK }, { "buffer", BUFFER }, { "ca", CA }, { "cache", CACHE }, { "cert", CERTIFICATE }, - { "change", CHANGE }, { "check", CHECK }, { "ciphers", CIPHERS }, { "code", CODE }, @@ -1933,7 +2065,6 @@ lookup(char *s) { "expect", EXPECT }, { "external", EXTERNAL }, { "file", FILENAME }, - { "filter", FILTER }, { "forward", FORWARD }, { "from", FROM }, { "hash", HASH }, @@ -1953,9 +2084,8 @@ lookup(char *s) { "loadbalance", LOADBALANCE }, { "log", LOG }, { "lookup", LOOKUP }, - { "mark", MARK }, - { "marked", MARKED }, { "match", MATCH }, + { "method", METHOD }, { "mode", MODE }, { "nat", NAT }, { "no", NO }, @@ -1963,13 +2093,16 @@ lookup(char *s) { "nothing", NOTHING }, { "on", ON }, { "parent", PARENT }, + { "pass", PASS }, { "password", PASSWORD }, { "path", PATH }, + { "pftag", PFTAG }, { "port", PORT }, { "prefork", PREFORK }, { "priority", PRIORITY }, { "protocol", PROTO }, { "query", QUERYSTR }, + { "quick", QUICK }, { "random", RANDOM }, { "real", REAL }, { "redirect", REDIRECT }, @@ -1988,6 +2121,7 @@ lookup(char *s) { "script", SCRIPT }, { "send", SEND }, { "session", SESSION }, + { "set", SET }, { "snmp", SNMP }, { "socket", SOCKET }, { "source-hash", SRCHASH }, @@ -1997,6 +2131,7 @@ lookup(char *s) { "style", STYLE }, { "table", TABLE }, { "tag", TAG }, + { "tagged", TAGGED }, { "tcp", TCP }, { "timeout", TIMEOUT }, { "to", TO }, @@ -2005,6 +2140,7 @@ lookup(char *s) { "ttl", TTL }, { "updates", UPDATES }, { "url", URL }, + { "value", VALUE }, { "virtual", VIRTUAL }, { "with", WITH } }; diff --git a/usr.sbin/relayd/pfe.c b/usr.sbin/relayd/pfe.c index 4c0f04f8a7a..03865fc23b0 100644 --- a/usr.sbin/relayd/pfe.c +++ b/usr.sbin/relayd/pfe.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfe.c,v 1.74 2013/03/10 23:32:53 reyk Exp $ */ +/* $OpenBSD: pfe.c,v 1.75 2014/07/09 16:42:05 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -194,8 +194,6 @@ pfe_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) case IMSG_CFG_PROTO: config_getproto(env, imsg); break; - case IMSG_CFG_PROTONODE: - break; case IMSG_CFG_RELAY: config_getrelay(env, imsg); break; diff --git a/usr.sbin/relayd/relay.c b/usr.sbin/relayd/relay.c index 4375cca391c..09642f5b9cd 100644 --- a/usr.sbin/relayd/relay.c +++ b/usr.sbin/relayd/relay.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay.c,v 1.171 2014/06/27 07:49:08 andre Exp $ */ +/* $OpenBSD: relay.c,v 1.172 2014/07/09 16:42:05 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org> @@ -56,8 +56,8 @@ int relay_dispatch_ca(int, struct privsep_proc *, struct imsg *); void relay_shutdown(void); -void relay_nodedebug(const char *, struct protonode *); void relay_protodebug(struct relay *); +void relay_ruledebug(struct relay_rule *); void relay_init(struct privsep *, struct privsep_proc *p, void *); void relay_launch(void); int relay_socket(struct sockaddr_storage *, in_port_t, @@ -83,8 +83,6 @@ void relay_ssl_readcb(int, short, void *); void relay_ssl_writecb(int, short, void *); char *relay_load_file(const char *, off_t *); -static __inline int - relay_proto_cmp(struct protonode *, struct protonode *); extern void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *); @@ -104,8 +102,11 @@ static struct privsep_proc procs[] = { pid_t relay(struct privsep *ps, struct privsep_proc *p) { + pid_t pid; env = ps->ps_env; - return (proc_run(ps, p, procs, nitems(procs), relay_init, NULL)); + pid = proc_run(ps, p, procs, nitems(procs), relay_init, NULL); + relay_http(env); + return (pid); } void @@ -116,74 +117,125 @@ relay_shutdown(void) } void -relay_nodedebug(const char *name, struct protonode *pn) +relay_ruledebug(struct relay_rule *rule) { - const char *s; - int digest; - - if (pn->action == NODE_ACTION_NONE) - return; + struct kv *kv = NULL; + u_int i; fprintf(stderr, "\t\t"); - fprintf(stderr, "%s ", name); - switch (pn->type) { - case NODE_TYPE_HEADER: - break; - case NODE_TYPE_QUERY: - fprintf(stderr, "query "); + switch (rule->rule_action) { + case RULE_ACTION_MATCH: + fprintf(stderr, "match "); break; - case NODE_TYPE_COOKIE: - fprintf(stderr, "cookie "); + case RULE_ACTION_BLOCK: + fprintf(stderr, "block "); break; - case NODE_TYPE_PATH: - fprintf(stderr, "path "); - break; - case NODE_TYPE_URL: - fprintf(stderr, "url "); + case RULE_ACTION_PASS: + fprintf(stderr, "pass "); break; } - switch (pn->action) { - case NODE_ACTION_APPEND: - fprintf(stderr, "append \"%s\" to \"%s\"", - pn->value, pn->key); - break; - case NODE_ACTION_CHANGE: - fprintf(stderr, "change \"%s\" to \"%s\"", - pn->key, pn->value); + switch (rule->rule_dir) { + case RELAY_DIR_ANY: break; - case NODE_ACTION_REMOVE: - fprintf(stderr, "remove \"%s\"", - pn->key); + case RELAY_DIR_REQUEST: + fprintf(stderr, "request "); break; - case NODE_ACTION_EXPECT: - case NODE_ACTION_FILTER: - s = pn->action == NODE_ACTION_EXPECT ? "expect" : "filter"; - digest = pn->flags & PNFLAG_LOOKUP_URL_DIGEST; - if (strcmp(pn->value, "*") == 0) - fprintf(stderr, "%s %s\"%s\"", s, - digest ? "digest " : "", pn->key); - else - fprintf(stderr, "%s \"%s\" from \"%s\"", s, - pn->value, pn->key); - break; - case NODE_ACTION_HASH: - fprintf(stderr, "hash \"%s\"", pn->key); - break; - case NODE_ACTION_LOG: - fprintf(stderr, "log \"%s\"", pn->key); - break; - case NODE_ACTION_MARK: - if (strcmp(pn->value, "*") == 0) - fprintf(stderr, "mark \"%s\"", pn->key); - else - fprintf(stderr, "mark \"%s\" from \"%s\"", - pn->value, pn->key); + case RELAY_DIR_RESPONSE: + fprintf(stderr, "response "); break; - case NODE_ACTION_NONE: + default: + return; + /* NOTREACHED */ break; } + + if (rule->rule_flags & RULE_FLAG_QUICK) + fprintf(stderr, "quick "); + + for (i = 1; i < KEY_TYPE_MAX; i++) { + kv = &rule->rule_kv[i]; + if (kv->kv_type != i) + continue; + + switch (kv->kv_type) { + case KEY_TYPE_COOKIE: + fprintf(stderr, "cookie "); + break; + case KEY_TYPE_HEADER: + fprintf(stderr, "header "); + break; + case KEY_TYPE_PATH: + fprintf(stderr, "path "); + break; + case KEY_TYPE_QUERY: + fprintf(stderr, "query "); + break; + case KEY_TYPE_URL: + fprintf(stderr, "url "); + break; + default: + continue; + } + + switch (kv->kv_option) { + case KEY_OPTION_APPEND: + fprintf(stderr, "append "); + break; + case KEY_OPTION_SET: + fprintf(stderr, "set "); + break; + case KEY_OPTION_REMOVE: + fprintf(stderr, "remove "); + break; + case KEY_OPTION_HASH: + fprintf(stderr, "hash "); + break; + case KEY_OPTION_LOG: + fprintf(stderr, "log "); + break; + case KEY_OPTION_NONE: + break; + } + + switch (kv->kv_digest) { + case DIGEST_SHA1: + case DIGEST_MD5: + fprintf(stderr, "digest "); + break; + default: + break; + } + + fprintf(stderr, "%s%s%s%s%s%s ", + kv->kv_key == NULL ? "" : "\"", + kv->kv_key == NULL ? "" : kv->kv_key, + kv->kv_key == NULL ? "" : "\"", + kv->kv_value == NULL ? "" : " value \"", + kv->kv_value == NULL ? "" : kv->kv_value, + kv->kv_value == NULL ? "" : "\""); + } + + if (rule->rule_tablename[0]) + fprintf(stderr, "forward to <%s> ", rule->rule_tablename); + + if (rule->rule_tag == -1) + fprintf(stderr, "no tag "); + else if (rule->rule_tag && rule->rule_tagname[0]) + fprintf(stderr, "tag \"%s\" ", + rule->rule_tagname); + + if (rule->rule_tagged && rule->rule_taggedname[0]) + fprintf(stderr, "tagged \"%s\" ", + rule->rule_taggedname); + + if (rule->rule_label == -1) + fprintf(stderr, "no label "); + else if (rule->rule_label && rule->rule_labelname[0]) + fprintf(stderr, "label \"%s\" ", + rule->rule_labelname); + fprintf(stderr, "\n"); } @@ -191,10 +243,7 @@ void relay_protodebug(struct relay *rlay) { struct protocol *proto = rlay->rl_proto; - struct protonode *proot, *pn; - struct proto_tree *tree; - const char *name; - int i; + struct relay_rule *rule = NULL; fprintf(stderr, "protocol %d: name %s\n", proto->id, proto->name); @@ -222,32 +271,10 @@ relay_protodebug(struct relay *rlay) break; } - name = "request"; - tree = &proto->request_tree; - show: - i = 0; - RB_FOREACH(proot, proto_tree, tree) { -#if DEBUG > 1 - i = 0; -#endif - PROTONODE_FOREACH(pn, proot, entry) { -#if DEBUG > 1 - i = 0; -#endif - if (++i > 100) - break; - relay_nodedebug(name, pn); - } - /* Limit the number of displayed lines */ - if (++i > 100) { - fprintf(stderr, "\t\t...\n"); - break; - } - } - if (tree == &proto->request_tree) { - name = "response"; - tree = &proto->response_tree; - goto show; + rule = TAILQ_FIRST(&proto->rules); + while (rule != NULL) { + relay_ruledebug(rule); + rule = TAILQ_NEXT(rule, rule_entry); } } @@ -266,8 +293,8 @@ relay_privinit(struct relay *rlay) relay_udp_privinit(env, rlay); break; case RELAY_PROTO_TCP: + break; case RELAY_PROTO_HTTP: - /* Use defaults */ break; } @@ -384,6 +411,13 @@ relay_launch(void) fatal("relay_init: failed to create SSL context"); TAILQ_FOREACH(rlt, &rlay->rl_tables, rlt_entry) { + /* + * set rule->rule_table in advance and save time + * looking up for this later on rule/connection + * evalution + */ + rule_settable(&rlay->rl_proto->rules, rlt); + switch (rlt->rlt_mode) { case RELAY_DSTMODE_ROUNDROBIN: case RELAY_DSTMODE_RANDOM: @@ -418,6 +452,7 @@ relay_launch(void) break; case RELAY_PROTO_TCP: case RELAY_PROTO_HTTP: + relay_http_init(rlay); /* Use defaults */ break; } @@ -608,7 +643,6 @@ relay_connected(int fd, short sig, void *arg) { struct rsession *con = arg; struct relay *rlay = con->se_relay; - struct protocol *proto = rlay->rl_proto; evbuffercb outrd = relay_read; evbuffercb outwr = relay_write; struct bufferevent *bev; @@ -635,22 +669,17 @@ relay_connected(int fd, short sig, void *arg) return; } - DPRINTF("%s: session %d: %ssuccessful", __func__, - con->se_id, rlay->rl_proto->lateconnect ? "late connect " : ""); + DPRINTF("%s: session %d: successful", __func__, con->se_id); switch (rlay->rl_proto->type) { case RELAY_PROTO_HTTP: - /* Check the servers's HTTP response */ - if (!RB_EMPTY(&rlay->rl_proto->response_tree)) { - con->se_out.toread = TOREAD_HTTP_HEADER; - outrd = relay_read_http; - if ((con->se_out.nodes = calloc(proto->response_nodes, - sizeof(u_int8_t))) == NULL) { - relay_abort_http(con, 500, - "failed to allocate nodes", 0); - return; - } + if (relay_httpdesc_init(out) == -1) { + relay_close(con, + "failed to allocate http descriptor"); + return; } + con->se_out.toread = TOREAD_HTTP_HEADER; + outrd = relay_read_http; break; case RELAY_PROTO_TCP: /* Use defaults */ @@ -690,23 +719,18 @@ void relay_input(struct rsession *con) { struct relay *rlay = con->se_relay; - struct protocol *proto = rlay->rl_proto; evbuffercb inrd = relay_read; evbuffercb inwr = relay_write; switch (rlay->rl_proto->type) { case RELAY_PROTO_HTTP: - /* Check the client's HTTP request */ - if (!RB_EMPTY(&rlay->rl_proto->request_tree) || - proto->lateconnect) { - con->se_in.toread = TOREAD_HTTP_HEADER; - inrd = relay_read_http; - if ((con->se_in.nodes = calloc(proto->request_nodes, - sizeof(u_int8_t))) == NULL) { - relay_close(con, "failed to allocate nodes"); - return; - } + if (relay_httpdesc_init(&con->se_in) == -1) { + relay_close(con, + "failed to allocate http descriptor"); + return; } + con->se_in.toread = TOREAD_HTTP_HEADER; + inrd = relay_read_http; break; case RELAY_PROTO_TCP: /* Use defaults */ @@ -801,26 +825,6 @@ relay_read(struct bufferevent *bev, void *arg) } int -relay_lognode(struct rsession *con, struct protonode *pn, struct protonode *pk, - char *buf, size_t len) -{ - const char *label = NULL; - - if ((pn->flags & PNFLAG_LOG) == 0) - return (0); - bzero(buf, len); - if (pn->label != 0) - label = pn_id2name(pn->label); - if (snprintf(buf, len, " [%s%s%s: %s]", - label == NULL ? "" : label, - label == NULL ? "" : ", ", - pk->key, pk->value) == -1 || - evbuffer_add(con->se_log, buf, strlen(buf)) == -1) - return (-1); - return (0); -} - -int relay_splice(struct ctl_relay_event *cre) { struct rsession *con = cre->con; @@ -857,7 +861,7 @@ relay_splice(struct ctl_relay_event *cre) bzero(&sp, sizeof(sp)); sp.sp_fd = cre->dst->s; sp.sp_max = cre->toread > 0 ? cre->toread : 0; - sp.sp_idle = rlay->rl_conf.timeout; + bcopy(&rlay->rl_conf.timeout, &sp.sp_idle, sizeof(sp.sp_idle)); if (setsockopt(cre->s, SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)) == -1) { log_debug("%s: session %d: splice dir %d failed: %s", __func__, con->se_id, cre->dir, strerror(errno)); @@ -991,7 +995,6 @@ void relay_accept(int fd, short event, void *arg) { struct relay *rlay = arg; - struct protocol *proto = rlay->rl_proto; struct rsession *con = NULL; struct ctl_natlook *cnl = NULL; socklen_t slen; @@ -1045,8 +1048,6 @@ relay_accept(int fd, short event, void *arg) con->se_id = ++relay_conid; con->se_relayid = rlay->rl_conf.id; con->se_pid = getpid(); - con->se_in.tree = &proto->request_tree; - con->se_out.tree = &proto->response_tree; con->se_in.dir = RELAY_DIR_REQUEST; con->se_out.dir = RELAY_DIR_RESPONSE; con->se_retry = rlay->rl_conf.dstretry; @@ -1250,7 +1251,8 @@ relay_from_table(struct rsession *con) host = TAILQ_NEXT(host, entry); } TAILQ_FOREACH(host, &table->hosts, entry) { - DPRINTF("%s: next host %s", __func__, host->conf.name); + DPRINTF("%s: session %d: next host %s", + __func__, con->se_id, host->conf.name); if (!table->conf.check || host->up == HOST_UP) goto found; } @@ -1323,7 +1325,7 @@ relay_session(struct rsession *con) return; } - if (!rlay->rl_proto->lateconnect) { + if (rlay->rl_proto->type != RELAY_PROTO_HTTP) { if (rlay->rl_conf.fwdmode == FWD_TRANS) relay_bindanyreq(con, 0, IPPROTO_TCP); else if (relay_connect(con) == -1) { @@ -1561,6 +1563,7 @@ relay_close(struct rsession *con, const char *msg) { char ibuf[128], obuf[128], *ptr = NULL; struct relay *rlay = con->se_relay; + struct protocol *proto = rlay->rl_proto; SPLAY_REMOVE(session_tree, &rlay->rl_sessions, con); @@ -1579,14 +1582,18 @@ relay_close(struct rsession *con, const char *msg) evbuffer_add_printf(con->se_log, "\r\n") != -1) ptr = evbuffer_readline(con->se_log); log_info("relay %s, " - "session %d (%d active), %d, %s -> %s:%d, " + "session %d (%d active), %s, %s -> %s:%d, " "%s%s%s", rlay->rl_conf.name, con->se_id, relay_sessions, - con->se_mark, ibuf, obuf, ntohs(con->se_out.port), msg, - ptr == NULL ? "" : ",", ptr == NULL ? "" : ptr); + con->se_tag != 0 ? tag_id2name(con->se_tag) : "0", ibuf, + obuf, ntohs(con->se_out.port), msg, ptr == NULL ? "" : ",", + ptr == NULL ? "" : ptr); if (ptr != NULL) free(ptr); } + if (proto->close != NULL) + (*proto->close)(con); + if (con->se_priv != NULL) free(con->se_priv); if (con->se_in.bev != NULL) @@ -1613,12 +1620,8 @@ relay_close(struct rsession *con, const char *msg) __func__, relay_inflight); } } - if (con->se_in.path != NULL) - free(con->se_in.path); if (con->se_in.buf != NULL) free(con->se_in.buf); - if (con->se_in.nodes != NULL) - free(con->se_in.nodes); if (con->se_out.bev != NULL) bufferevent_free(con->se_out.bev); @@ -1642,12 +1645,8 @@ relay_close(struct rsession *con, const char *msg) } } - if (con->se_out.path != NULL) - free(con->se_out.path); if (con->se_out.buf != NULL) free(con->se_out.buf); - if (con->se_out.nodes != NULL) - free(con->se_out.nodes); if (con->se_log != NULL) evbuffer_free(con->se_log); @@ -1823,8 +1822,9 @@ relay_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) case IMSG_CFG_PROTO: config_getproto(env, imsg); break; - case IMSG_CFG_PROTONODE: - return (config_getprotonode(env, imsg)); + case IMSG_CFG_RULE: + config_getrule(env, imsg); + break; case IMSG_CFG_RELAY: config_getrelay(env, imsg); break; @@ -2338,7 +2338,7 @@ relay_bufferevent_printf(struct ctl_relay_event *cre, const char *fmt, ...) #endif int -relay_bufferevent_print(struct ctl_relay_event *cre, char *str) +relay_bufferevent_print(struct ctl_relay_event *cre, const char *str) { if (cre->bev == NULL) return (evbuffer_add(cre->output, str, strlen(str))); @@ -2511,16 +2511,6 @@ relay_load_certfiles(struct relay *rlay) return (0); } -static __inline int -relay_proto_cmp(struct protonode *a, struct protonode *b) -{ - int ret; - ret = strcasecmp(a->key, b->key); - if (ret == 0) - ret = (int)a->type - b->type; - return (ret); -} - int relay_session_cmp(struct rsession *a, struct rsession *b) { @@ -2533,5 +2523,4 @@ relay_session_cmp(struct rsession *a, struct rsession *b) return ((int)a->se_id - b->se_id); } -RB_GENERATE(proto_tree, protonode, nodes, relay_proto_cmp); SPLAY_GENERATE(session_tree, rsession, se_nodes, relay_session_cmp); diff --git a/usr.sbin/relayd/relay_http.c b/usr.sbin/relayd/relay_http.c index bdb3d8778f4..239aaa09dec 100644 --- a/usr.sbin/relayd/relay_http.c +++ b/usr.sbin/relayd/relay_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay_http.c,v 1.19 2014/06/25 11:05:15 reyk Exp $ */ +/* $OpenBSD: relay_http.c,v 1.20 2014/07/09 16:42:05 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org> @@ -46,42 +46,140 @@ #include <openssl/ssl.h> #include "relayd.h" +#include "http.h" -int relay_resolve(struct ctl_relay_event *, - struct protonode *, struct protonode *); -int relay_handle_http(struct ctl_relay_event *, - struct protonode *, struct protonode *, - struct protonode *, int); static int _relay_lookup_url(struct ctl_relay_event *, char *, char *, - char *, enum digest_type); + char *, struct kv *); int relay_lookup_url(struct ctl_relay_event *, - const char *, enum digest_type); -int relay_lookup_query(struct ctl_relay_event *); -int relay_lookup_cookie(struct ctl_relay_event *, const char *); + const char *, struct kv *); +int relay_lookup_query(struct ctl_relay_event *, struct kv *); +int relay_lookup_cookie(struct ctl_relay_event *, const char *, + struct kv *); void relay_read_httpcontent(struct bufferevent *, void *); void relay_read_httpchunks(struct bufferevent *, void *); char *relay_expand_http(struct ctl_relay_event *, char *, char *, size_t); -void relay_http_request_close(struct ctl_relay_event *); +int relay_writeheader_http(struct ctl_relay_event *, + struct ctl_relay_event *); +int relay_writerequest_http(struct ctl_relay_event *, + struct ctl_relay_event *); +int relay_writeresponse_http(struct ctl_relay_event *, + struct ctl_relay_event *); +void relay_reset_http(struct ctl_relay_event *); +static int relay_httpmethod_cmp(const void *, const void *); +static int relay_httpheader_cmp(const void *, const void *); +int relay_httpquery_test(struct ctl_relay_event *, + struct relay_rule *, struct kvlist *); +int relay_httpheader_test(struct ctl_relay_event *, + struct relay_rule *, struct kvlist *); +int relay_httppath_test(struct ctl_relay_event *, + struct relay_rule *, struct kvlist *); +int relay_httpurl_test(struct ctl_relay_event *, + struct relay_rule *, struct kvlist *); +int relay_httpcookie_test(struct ctl_relay_event *, + struct relay_rule *, struct kvlist *); +int relay_apply_actions(struct ctl_relay_event *, struct kvlist *); +int relay_match_actions(struct ctl_relay_event *, + struct relay_rule *, struct kvlist *, struct kvlist *); +void relay_httpdesc_free(struct http_descriptor *); + +static struct relayd *env = NULL; + +static struct http_method http_methods[] = HTTP_METHODS; +static struct http_header http_headers[] = HTTP_HEADERS; + +void +relay_http(struct relayd *x_env) +{ + if (x_env != NULL) + env = x_env; + + DPRINTF("%s: sorting lookup tables, pid %d", __func__, getpid()); + + /* Sort the HTTP lookup arrays */ + qsort(http_methods, sizeof(http_methods) / + sizeof(http_methods[0]) - 1, + sizeof(http_methods[0]), relay_httpmethod_cmp); + qsort(http_headers, sizeof(http_headers) / + sizeof(http_headers[0]) - 1, + sizeof(http_headers[0]), relay_httpheader_cmp); +} + +void +relay_http_init(struct relay *rlay) +{ + rlay->rl_proto->close = relay_close_http; + + relay_http(NULL); + + /* Calculate skip step for the filter rules (may take a while) */ + relay_calc_skip_steps(&rlay->rl_proto->rules); +} + +int +relay_httpdesc_init(struct ctl_relay_event *cre) +{ + struct http_descriptor *desc; + + if ((desc = calloc(1, sizeof(*desc))) == NULL) + return (-1); + TAILQ_INIT(&desc->http_headers); + cre->desc = desc; + + return (0); +} + +void +relay_httpdesc_free(struct http_descriptor *desc) +{ + int i; + + if (desc->http_path != NULL) { + free(desc->http_path); + desc->http_path = NULL; + } + if (desc->http_query != NULL) { + free(desc->http_query); + desc->http_query = NULL; + } + if (desc->http_version != NULL) { + free(desc->http_version); + desc->http_version = NULL; + } + if (desc->query_key != NULL) { + free(desc->query_key); + desc->query_key = NULL; + } + if (desc->query_val != NULL) { + free(desc->query_val); + desc->query_val = NULL; + } + for (i = HTTP_HEADER_NONE; i < HTTP_HEADER_MAX; i++) + desc->http_header[i] = NULL; + kv_purge(&desc->http_headers); +} void relay_read_http(struct bufferevent *bev, void *arg) { struct ctl_relay_event *cre = arg; + struct http_descriptor *desc = cre->desc; struct rsession *con = cre->con; struct relay *rlay = con->se_relay; struct protocol *proto = rlay->rl_proto; struct evbuffer *src = EVBUFFER_INPUT(bev); - struct protonode *pn, pk, *proot, *pnv = NULL, pkv; - char *line; - int header = 0, ret, pass = 0; + char *line = NULL, *key, *value; + int action; const char *errstr; size_t size; + struct kv *hdr = NULL; + enum httpheader hdrid; getmonotime(&con->se_tv_last); size = EVBUFFER_LENGTH(src); - DPRINTF("%s: size %lu, to read %lld", __func__, size, cre->toread); + DPRINTF("%s: session %d: size %lu, to read %lld", + __func__, con->se_id, size, cre->toread); if (!size) { if (cre->dir == RELAY_DIR_RESPONSE) return; @@ -89,8 +187,6 @@ relay_read_http(struct bufferevent *bev, void *arg) goto done; } - pk.type = NODE_TYPE_HEADER; - while (!cre->done && (line = evbuffer_readline(src)) != NULL) { /* * An empty line indicates the end of the request. @@ -101,125 +197,125 @@ relay_read_http(struct bufferevent *bev, void *arg) free(line); break; } - pk.key = line; + key = line; /* * The first line is the GET/POST/PUT/... request, * subsequent lines are HTTP headers. */ - if (++cre->line == 1) { - pk.value = strchr(pk.key, ' '); - } else - pk.value = strchr(pk.key, ':'); - if (pk.value == NULL || strlen(pk.value) < 3) { + if (++cre->line == 1) + value = strchr(key, ' '); + else if (*key == ' ' || *key == '\t') + /* Multiline headers wrap with a space or tab */ + value = NULL; + else + value = strchr(key, ':'); + if (value == NULL) { if (cre->line == 1) { free(line); relay_abort_http(con, 400, "malformed", 0); return; } - DPRINTF("%s: request '%s'", __func__, line); - /* Append line to the output buffer */ - if (relay_bufferevent_print(cre->dst, line) == -1 || - relay_bufferevent_print(cre->dst, "\r\n") == -1) { + /* Append line to the last header, if present */ + if (kv_extend(&desc->http_headers, line) == NULL) { free(line); goto fail; } + free(line); continue; } - if (*pk.value == ':') { - *pk.value++ = '\0'; - pk.value += strspn(pk.value, " \t\r\n"); - header = 1; + if (*value == ':') { + *value++ = '\0'; + value += strspn(value, " \t\r\n"); } else { - *pk.value++ = '\0'; - header = 0; + *value++ = '\0'; } - DPRINTF("%s: header '%s: %s'", __func__, pk.key, pk.value); + DPRINTF("%s: session %d: header '%s: %s'", __func__, + con->se_id, key, value); /* * Identify and handle specific HTTP request methods */ - if (cre->line == 1) { - if (cre->dir == RELAY_DIR_RESPONSE) { - cre->method = HTTP_METHOD_RESPONSE; - goto lookup; - } else if (strcmp("HEAD", pk.key) == 0) - cre->method = HTTP_METHOD_HEAD; - else if (strcmp("POST", pk.key) == 0) - cre->method = HTTP_METHOD_POST; - else if (strcmp("PUT", pk.key) == 0) - cre->method = HTTP_METHOD_PUT; - else if (strcmp("DELETE", pk.key) == 0) - cre->method = HTTP_METHOD_DELETE; - else if (strcmp("OPTIONS", pk.key) == 0) - cre->method = HTTP_METHOD_OPTIONS; - else if (strcmp("TRACE", pk.key) == 0) - cre->method = HTTP_METHOD_TRACE; - else if (strcmp("CONNECT", pk.key) == 0) - cre->method = HTTP_METHOD_CONNECT; - else { - /* Use GET method as the default */ - cre->method = HTTP_METHOD_GET; - } - + if (cre->line == 1 && cre->dir == RELAY_DIR_RESPONSE) { + desc->http_method = HTTP_METHOD_RESPONSE; /* - * Decode the path and query + * Decode response path and query */ - cre->path = strdup(pk.value); - if (cre->path == NULL) { + desc->http_version = strdup(line); + if (desc->http_version == NULL) { free(line); goto fail; } - cre->version = strchr(cre->path, ' '); - if (cre->version != NULL) - *cre->version++ = '\0'; - cre->args = strchr(cre->path, '?'); - if (cre->args != NULL) - *cre->args++ = '\0'; -#ifdef DEBUG - char buf[BUFSIZ]; - if (snprintf(buf, sizeof(buf), " \"%s\"", - cre->path) == -1 || - evbuffer_add(con->se_log, buf, strlen(buf)) == -1) { + desc->http_rescode = strdup(value); + if (desc->http_rescode == NULL) { free(line); goto fail; } -#endif - + desc->http_resmesg = strchr(desc->http_rescode, ' '); + if (desc->http_resmesg == NULL) { + free(line); + goto fail; + } + *desc->http_resmesg++ = '\0'; + if ((desc->http_resmesg = strdup(desc->http_resmesg)) + == NULL) { + free(line); + goto fail; + } + DPRINTF("http_version %s http_rescode %s " + "http_resmesg %s", desc->http_version, + desc->http_rescode, desc->http_resmesg); + goto lookup; + } else if (cre->line == 1 && cre->dir == RELAY_DIR_REQUEST) { + if ((desc->http_method = relay_httpmethod_byname(key)) + == HTTP_METHOD_NONE) + goto fail; /* - * Lookup protocol handlers in the URL path + * Decode request path and query */ - if ((proto->flags & F_LOOKUP_PATH) == 0) - goto lookup; - - pkv.key = cre->path; - pkv.type = NODE_TYPE_PATH; - pkv.value = cre->args == NULL ? "" : cre->args; - - DPRINTF("%s: lookup path '%s: %s'", - __func__, pkv.key, pkv.value); - - if ((proot = RB_FIND(proto_tree, - cre->tree, &pkv)) == NULL) - goto lookup; + desc->http_path = strdup(value); + if (desc->http_path == NULL) { + free(line); + goto fail; + } + desc->http_version = strchr(desc->http_path, ' '); + if (desc->http_version != NULL) + *desc->http_version++ = '\0'; + desc->http_query = strchr(desc->http_path, '?'); + if (desc->http_query != NULL) + *desc->http_query++ = '\0'; - PROTONODE_FOREACH(pnv, proot, entry) { - ret = relay_handle_http(cre, proot, - pnv, &pkv, 0); - if (ret == PN_FAIL) - goto abort; + /* + * Have to allocate the strings because they could + * be changed independetly by the filters later. + */ + if (desc->http_version != NULL && + (desc->http_version = + strdup(desc->http_version)) == NULL) { + free(line); + goto fail; + } + if (desc->http_query != NULL && + (desc->http_query = + strdup(desc->http_query)) == NULL) { + free(line); + goto fail; + } + } else if (desc->http_method != HTTP_METHOD_NONE && + strcasecmp("Content-Length", key) == 0) { + if (desc->http_method == HTTP_METHOD_TRACE || + desc->http_method == HTTP_METHOD_CONNECT) { + /* + * These method should not have a body + * and thus no Content-Length header. + */ + relay_abort_http(con, 400, "malformed", 0); + goto abort; } - } else if ((cre->method == HTTP_METHOD_DELETE || - cre->method == HTTP_METHOD_GET || - cre->method == HTTP_METHOD_HEAD || - cre->method == HTTP_METHOD_OPTIONS || - cre->method == HTTP_METHOD_POST || - cre->method == HTTP_METHOD_PUT || - cre->method == HTTP_METHOD_RESPONSE) && - strcasecmp("Content-Length", pk.key) == 0) { + /* * Need to read data from the client after the * HTTP header. @@ -227,94 +323,59 @@ relay_read_http(struct bufferevent *bev, void *arg) * the carriage return? And some browsers seem to * include the line length in the content-length. */ - cre->toread = strtonum(pk.value, 0, LLONG_MAX, + cre->toread = strtonum(value, 0, LLONG_MAX, &errstr); if (errstr) { relay_abort_http(con, 500, errstr, 0); goto abort; } - } else if ((cre->method == HTTP_METHOD_TRACE) && - strcasecmp("Content-Length", pk.key) == 0) { - /* - * This method should not have a body and thus no - * Content-Length header. - */ - relay_abort_http(con, 400, "malformed", 0); - goto abort; } lookup: - if (strcasecmp("Transfer-Encoding", pk.key) == 0 && - strcasecmp("chunked", pk.value) == 0) - cre->chunked = 1; - - /* Match the HTTP header */ - if ((pn = RB_FIND(proto_tree, cre->tree, &pk)) == NULL) - goto next; - - if (cre->dir == RELAY_DIR_RESPONSE) - goto handle; - - if (pn->flags & PNFLAG_LOOKUP_URL) { - /* - * Lookup the URL of type example.com/path?args. - * Either as a plain string or SHA1/MD5 digest. - */ - if ((pn->flags & PNFLAG_LOOKUP_DIGEST(0)) && - relay_lookup_url(cre, pk.value, - DIGEST_NONE) == PN_FAIL) - goto abort; - if ((pn->flags & PNFLAG_LOOKUP_DIGEST(DIGEST_SHA1)) && - relay_lookup_url(cre, pk.value, - DIGEST_SHA1) == PN_FAIL) - goto abort; - if ((pn->flags & PNFLAG_LOOKUP_DIGEST(DIGEST_MD5)) && - relay_lookup_url(cre, pk.value, - DIGEST_MD5) == PN_FAIL) - goto abort; - } else if (pn->flags & PNFLAG_LOOKUP_QUERY) { - /* Lookup the HTTP query arguments */ - if (relay_lookup_query(cre) == PN_FAIL) - goto abort; - } else if (pn->flags & PNFLAG_LOOKUP_COOKIE) { - /* Lookup the HTTP cookie */ - if (relay_lookup_cookie(cre, pk.value) == PN_FAIL) - goto abort; - } - - handle: - pass = 0; - PROTONODE_FOREACH(pnv, pn, entry) { - ret = relay_handle_http(cre, pn, pnv, &pk, header); - if (ret == PN_PASS) - pass = 1; - else if (ret == PN_FAIL) - goto abort; - } + if (strcasecmp("Transfer-Encoding", key) == 0 && + strcasecmp("chunked", value) == 0) + desc->http_chunked = 1; - if (pass) { - next: - if (relay_bufferevent_print(cre->dst, pk.key) == -1 || - relay_bufferevent_print(cre->dst, - header ? ": " : " ") == -1 || - relay_bufferevent_print(cre->dst, pk.value) == -1 || - relay_bufferevent_print(cre->dst, "\r\n") == -1) { + if (cre->line != 1) { + if ((hdr = kv_add(&desc->http_headers, key, + value)) == NULL) { free(line); goto fail; } + /* + * Remember the header if it is known to us for a + * quicker lookup later. + */ + if ((hdrid = relay_httpheader_byname(key)) != + HTTP_HEADER_OTHER) { + if (desc->http_header[hdrid] != NULL) { + kv_delete(&desc->http_headers, hdr); + free(line); + relay_abort_http(con, 400, "repeated " + "header line", 0); + return; + } + desc->http_header[hdrid] = hdr; + } } + free(line); } if (cre->done) { - RB_FOREACH(proot, proto_tree, cre->tree) { - PROTONODE_FOREACH(pn, proot, entry) - if (relay_resolve(cre, proot, pn) != 0) - return; + if (desc->http_method == HTTP_METHOD_NONE) { + relay_abort_http(con, 406, "no method", 0); + return; } - switch (cre->method) { - case HTTP_METHOD_NONE: - relay_abort_http(con, 406, "no method", 0); + action = relay_test(proto, cre); + if (action == RES_FAIL) { + relay_close(con, "filter rule failed"); + return; + } else if (action != RES_PASS) { + relay_abort_http(con, 403, "Forbidden", con->se_label); return; + } + + switch (desc->http_method) { case HTTP_METHOD_CONNECT: /* Data stream */ cre->toread = TOREAD_UNLIMITED; @@ -345,21 +406,28 @@ relay_read_http(struct bufferevent *bev, void *arg) bev->readcb = relay_read_http; break; } - if (cre->chunked) { + if (desc->http_chunked) { /* Chunked transfer encoding */ cre->toread = TOREAD_HTTP_CHUNK_LENGTH; bev->readcb = relay_read_httpchunks; } - /* Write empty newline and switch to relay mode */ - if (relay_bufferevent_print(cre->dst, "\r\n") == -1) + if (cre->dir == RELAY_DIR_REQUEST) { + if (relay_writerequest_http(cre->dst, cre) == -1) + goto fail; + } else { + if (relay_writeresponse_http(cre->dst, cre) == -1) + goto fail; + } + if (relay_bufferevent_print(cre->dst, "\r\n") == -1 || + relay_writeheader_http(cre->dst, cre) == -1 || + relay_bufferevent_print(cre->dst, "\r\n") == -1) goto fail; - relay_http_request_close(cre); - + relay_reset_http(cre); done: if (cre->dir == RELAY_DIR_REQUEST && cre->toread <= 0 && - proto->lateconnect && cre->dst->bev == NULL) { + cre->dst->bev == NULL) { if (rlay->rl_conf.fwdmode == FWD_TRANS) { relay_bindanyreq(con, 0, IPPROTO_TCP); return; @@ -397,8 +465,8 @@ relay_read_httpcontent(struct bufferevent *bev, void *arg) getmonotime(&con->se_tv_last); size = EVBUFFER_LENGTH(src); - DPRINTF("%s: dir %d, size %lu, to read %lld", - __func__, cre->dir, size, cre->toread); + DPRINTF("%s: session %d: size %lu, to read %lld", __func__, + con->se_id, size, cre->toread); if (!size) return; if (relay_spliceadjust(cre) == -1) @@ -450,8 +518,8 @@ relay_read_httpchunks(struct bufferevent *bev, void *arg) getmonotime(&con->se_tv_last); size = EVBUFFER_LENGTH(src); - DPRINTF("%s: dir %d, size %lu, to read %lld", - __func__, cre->dir, size, cre->toread); + DPRINTF("%s: session %d: size %lu, to read %lld", __func__, + con->se_id, size, cre->toread); if (!size) return; if (relay_spliceadjust(cre) == -1) @@ -503,7 +571,6 @@ relay_read_httpchunks(struct bufferevent *bev, void *arg) } free(line); - /* Last chunk is 0 bytes followed by optional trailer */ if ((cre->toread = llval) == 0) { DPRINTF("%s: last chunk", __func__); cre->toread = TOREAD_HTTP_CHUNK_TRAILER; @@ -556,75 +623,64 @@ relay_read_httpchunks(struct bufferevent *bev, void *arg) } void -relay_http_request_close(struct ctl_relay_event *cre) +relay_reset_http(struct ctl_relay_event *cre) { - if (cre->path != NULL) { - free(cre->path); - cre->path = NULL; - } - - cre->args = NULL; - cre->version = NULL; + struct http_descriptor *desc = cre->desc; + relay_httpdesc_free(desc); if (cre->buf != NULL) { free(cre->buf); cre->buf = NULL; cre->buflen = 0; } - + desc->http_method = 0; + desc->http_chunked = 0; cre->line = 0; - cre->method = 0; cre->done = 0; - cre->chunked = 0; } static int _relay_lookup_url(struct ctl_relay_event *cre, char *host, char *path, - char *query, enum digest_type type) + char *query, struct kv *kv) { struct rsession *con = cre->con; - struct protonode *proot, *pnv, pkv; char *val, *md = NULL; - int ret = PN_FAIL; + int ret = RES_FAIL; + const char *str = NULL; if (asprintf(&val, "%s%s%s%s", host, path, query == NULL ? "" : "?", query == NULL ? "" : query) == -1) { relay_abort_http(con, 500, "failed to allocate URL", 0); - return (PN_FAIL); + return (RES_FAIL); } - DPRINTF("%s: %s", __func__, val); - - switch (type) { + switch (kv->kv_digest) { case DIGEST_SHA1: case DIGEST_MD5: - if ((md = digeststr(type, val, strlen(val), NULL)) == NULL) { + if ((md = digeststr(kv->kv_digest, + val, strlen(val), NULL)) == NULL) { relay_abort_http(con, 500, "failed to allocate digest", 0); goto fail; } - pkv.key = md; + str = md; break; case DIGEST_NONE: - pkv.key = val; + str = val; break; } - pkv.type = NODE_TYPE_URL; - pkv.value = ""; - if ((proot = RB_FIND(proto_tree, cre->tree, &pkv)) == NULL) - goto done; + DPRINTF("%s: session %d: %s, %s: %d", __func__, con->se_id, + str, kv->kv_key, strcasecmp(kv->kv_key, str)); - PROTONODE_FOREACH(pnv, proot, entry) { - ret = relay_handle_http(cre, proot, pnv, &pkv, 0); - if (ret == PN_FAIL) - goto fail; + if (strcasecmp(kv->kv_key, str) == 0) { + ret = RES_DROP; + goto fail; } - done: - ret = PN_PASS; + ret = RES_PASS; fail: if (md != NULL) free(md); @@ -633,17 +689,17 @@ _relay_lookup_url(struct ctl_relay_event *cre, char *host, char *path, } int -relay_lookup_url(struct ctl_relay_event *cre, const char *str, - enum digest_type type) +relay_lookup_url(struct ctl_relay_event *cre, const char *host, struct kv *kv) { - struct rsession *con = cre->con; - int i, j, dots; - char *hi[RELAY_MAXLOOKUPLEVELS], *p, *pp, *c, ch; - char ph[MAXHOSTNAMELEN]; - int ret; + struct rsession *con = cre->con; + struct http_descriptor *desc = (struct http_descriptor *)cre->desc; + int i, j, dots; + char *hi[RELAY_MAXLOOKUPLEVELS], *p, *pp, *c, ch; + char ph[MAXHOSTNAMELEN]; + int ret; - if (cre->path == NULL) - return (PN_PASS); + if (desc->http_path == NULL) + return (RES_PASS); /* * This is an URL lookup algorithm inspired by @@ -651,12 +707,13 @@ relay_lookup_url(struct ctl_relay_event *cre, const char *str, * developers_guide.html#PerformingLookups */ - DPRINTF("%s: host: '%s', path: '%s', query: '%s'", __func__, - str, cre->path, cre->args == NULL ? "" : cre->args); + DPRINTF("%s: session %d: host '%s', path '%s', query '%s'", + __func__, con->se_id, host, desc->http_path, + desc->http_query == NULL ? "" : desc->http_query); - if (canonicalize_host(str, ph, sizeof(ph)) == NULL) { + if (canonicalize_host(host, ph, sizeof(ph)) == NULL) { relay_abort_http(con, 400, "invalid host name", 0); - return (PN_FAIL); + return (RES_FAIL); } bzero(hi, sizeof(hi)); @@ -670,140 +727,153 @@ relay_lookup_url(struct ctl_relay_event *cre, const char *str, dots = 0; hi[dots] = ph; - if ((pp = strdup(cre->path)) == NULL) { + if ((pp = strdup(desc->http_path)) == NULL) { relay_abort_http(con, 500, "failed to allocate path", 0); - return (PN_FAIL); + return (RES_FAIL); } for (i = (RELAY_MAXLOOKUPLEVELS - 1); i >= 0; i--) { if (hi[i] == NULL) continue; /* 1. complete path with query */ - if (cre->args != NULL) + if (desc->http_query != NULL) if ((ret = _relay_lookup_url(cre, hi[i], - pp, cre->args, type)) != PN_PASS) + pp, desc->http_query, kv)) != RES_PASS) goto done; /* 2. complete path without query */ if ((ret = _relay_lookup_url(cre, hi[i], - pp, NULL, type)) != PN_PASS) + pp, NULL, kv)) != RES_PASS) goto done; /* 3. traverse path */ for (j = 0, p = strchr(pp, '/'); p != NULL; p = strchr(p, '/'), j++) { - if (j > (RELAY_MAXLOOKUPLEVELS - 2) || ++p == '\0') + if (j > (RELAY_MAXLOOKUPLEVELS - 2) || *(++p) == '\0') break; c = &pp[p - pp]; ch = *c; *c = '\0'; if ((ret = _relay_lookup_url(cre, hi[i], - pp, NULL, type)) != PN_PASS) + pp, NULL, kv)) != RES_PASS) goto done; *c = ch; } } - ret = PN_PASS; + ret = RES_PASS; done: free(pp); return (ret); } int -relay_lookup_query(struct ctl_relay_event *cre) +relay_lookup_cookie(struct ctl_relay_event *cre, const char *str, + struct kv *kv) { struct rsession *con = cre->con; - struct protonode *proot, *pnv, pkv; - char *val, *ptr; + char *val, *ptr, *key, *value; int ret; - if (cre->path == NULL || cre->args == NULL || strlen(cre->args) < 2) - return (PN_PASS); - if ((val = strdup(cre->args)) == NULL) { - relay_abort_http(con, 500, "failed to allocate query", 0); - return (PN_FAIL); + if ((val = strdup(str)) == NULL) { + relay_abort_http(con, 500, "failed to allocate cookie", 0); + return (RES_FAIL); } - ptr = val; - while (ptr != NULL && strlen(ptr)) { - pkv.key = ptr; - pkv.type = NODE_TYPE_QUERY; - if ((ptr = strchr(ptr, '&')) != NULL) + for (ptr = val; ptr != NULL && strlen(ptr);) { + if (*ptr == ' ') *ptr++ = '\0'; - if ((pkv.value = - strchr(pkv.key, '=')) == NULL || - strlen(pkv.value) < 1) + key = ptr; + if ((ptr = strchr(ptr, ';')) != NULL) + *ptr++ = '\0'; + /* + * XXX We do not handle attributes + * ($Path, $Domain, or $Port) + */ + if (*key == '$') continue; - *pkv.value++ = '\0'; - if ((proot = RB_FIND(proto_tree, cre->tree, &pkv)) == NULL) + if ((value = + strchr(key, '=')) == NULL || + strlen(value) < 1) continue; - PROTONODE_FOREACH(pnv, proot, entry) { - ret = relay_handle_http(cre, proot, - pnv, &pkv, 0); - if (ret == PN_FAIL) - goto done; + *value++ = '\0'; + if (*value == '"') + *value++ = '\0'; + if (value[strlen(value) - 1] == '"') + value[strlen(value) - 1] = '\0'; + + DPRINTF("%s: session %d: %s = %s, %s = %s : %d", + __func__, con->se_id, + key, value, kv->kv_key, kv->kv_value, + strcasecmp(kv->kv_key, key)); + + if (strcasecmp(kv->kv_key, key) == 0 && + ((kv->kv_value == NULL) || + (fnmatch(kv->kv_value, value, + FNM_CASEFOLD) != FNM_NOMATCH))) { + ret = RES_DROP; + goto done; } } - ret = PN_PASS; + ret = RES_PASS; + done: free(val); return (ret); } int -relay_lookup_cookie(struct ctl_relay_event *cre, const char *str) +relay_lookup_query(struct ctl_relay_event *cre, struct kv *kv) { - struct rsession *con = cre->con; - struct protonode *proot, *pnv, pkv; - char *val, *ptr; - int ret; + struct http_descriptor *desc = cre->desc; + struct kv *match = &desc->http_matchquery; + char *val, *ptr, *tmpkey = NULL, *tmpval = NULL; + int ret = -1; - if ((val = strdup(str)) == NULL) { - relay_abort_http(con, 500, "failed to allocate cookie", 0); - return (PN_FAIL); + if (desc->http_query == NULL) + return (-1); + if ((val = strdup(desc->http_query)) == NULL) { + relay_abort_http(cre->con, 500, "failed to allocate query", 0); + return (-1); } - for (ptr = val; ptr != NULL && strlen(ptr);) { - if (*ptr == ' ') - *ptr++ = '\0'; - pkv.key = ptr; - pkv.type = NODE_TYPE_COOKIE; - if ((ptr = strchr(ptr, ';')) != NULL) + ptr = val; + while (ptr != NULL && strlen(ptr)) { + tmpkey = ptr; + if ((ptr = strchr(ptr, '&')) != NULL) *ptr++ = '\0'; - /* - * XXX We do not handle attributes - * ($Path, $Domain, or $Port) - */ - if (*pkv.key == '$') + if ((tmpval = strchr(tmpkey, '=')) == NULL || strlen(tmpval) + < 1) continue; + *tmpval++ = '\0'; - if ((pkv.value = - strchr(pkv.key, '=')) == NULL || - strlen(pkv.value) < 1) - continue; - *pkv.value++ = '\0'; - if (*pkv.value == '"') - *pkv.value++ = '\0'; - if (pkv.value[strlen(pkv.value) - 1] == '"') - pkv.value[strlen(pkv.value) - 1] = '\0'; - if ((proot = RB_FIND(proto_tree, cre->tree, &pkv)) == NULL) - continue; - PROTONODE_FOREACH(pnv, proot, entry) { - ret = relay_handle_http(cre, proot, pnv, &pkv, 0); - if (ret == PN_FAIL) - goto done; - } + if (fnmatch(kv->kv_key, tmpkey, 0) != FNM_NOMATCH && + (kv->kv_value == NULL || fnmatch(kv->kv_value, tmpval, 0) + != FNM_NOMATCH)) + break; + else + tmpkey = NULL; } - ret = PN_PASS; + if (tmpkey == NULL || tmpval == NULL) + goto done; + + match->kv_key = strdup(tmpkey); + if (match->kv_key == NULL) + goto done; + match->kv_value = strdup(tmpval); + if (match->kv_key == NULL) + goto done; + ret = 0; + done: free(val); return (ret); } + void relay_abort_http(struct rsession *con, u_int code, const char *msg, u_int16_t labelid) @@ -817,6 +887,9 @@ relay_abort_http(struct rsession *con, u_int code, const char *msg, char tmbuf[32], hbuf[128]; const char *style, *label = NULL; + if (labelid != 0) + label = label_id2name(labelid); + /* In some cases this function may be called from generic places */ if (rlay->rl_proto->type != RELAY_PROTO_HTTP || (rlay->rl_proto->flags & F_RETURN) == 0) { @@ -841,8 +914,6 @@ relay_abort_http(struct rsession *con, u_int code, const char *msg, /* Do not send details of the Internal Server Error */ if (code != 500) text = msg; - if (labelid != 0) - label = pn_id2name(labelid); /* A CSS stylesheet allows minimal customization by the user */ if ((style = rlay->rl_proto->style) == NULL) @@ -889,8 +960,25 @@ relay_abort_http(struct rsession *con, u_int code, const char *msg, } } +void +relay_close_http(struct rsession *con) +{ + struct http_descriptor *desc[2] = { + con->se_in.desc, con->se_out.desc + }; + int i; + + for (i = 0; i < 2; i++) { + if (desc[i] == NULL) + continue; + relay_httpdesc_free(desc[i]); + free(desc[i]); + } +} + char * -relay_expand_http(struct ctl_relay_event *cre, char *val, char *buf, size_t len) +relay_expand_http(struct ctl_relay_event *cre, char *val, char *buf, + size_t len) { struct rsession *con = cre->con; struct relay *rlay = con->se_relay; @@ -947,185 +1035,743 @@ relay_expand_http(struct ctl_relay_event *cre, char *val, char *buf, size_t len) } int -relay_resolve(struct ctl_relay_event *cre, - struct protonode *proot, struct protonode *pn) +relay_writerequest_http(struct ctl_relay_event *dst, + struct ctl_relay_event *cre) { - struct rsession *con = cre->con; - char buf[IBUF_READ_SIZE], *ptr; - int id; + struct http_descriptor *desc = (struct http_descriptor *)cre->desc; + const char *name = NULL; - if (pn->mark && (pn->mark != con->se_mark)) - return (0); + if ((name = relay_httpmethod_byid(desc->http_method)) == NULL) + return (-1); - switch (pn->action) { - case NODE_ACTION_FILTER: - id = cre->nodes[proot->id]; - if (SIMPLEQ_NEXT(pn, entry) == NULL) - cre->nodes[proot->id] = 0; - if (id <= 1) - return (0); - break; - case NODE_ACTION_EXPECT: - id = cre->nodes[proot->id]; - if (SIMPLEQ_NEXT(pn, entry) == NULL) - cre->nodes[proot->id] = 0; - if (id > 1) - return (0); - break; - default: - if (cre->nodes[pn->id]) { - cre->nodes[pn->id] = 0; - return (0); + if (relay_bufferevent_print(dst, name) == -1 || + relay_bufferevent_print(dst, " ") == -1 || + relay_bufferevent_print(dst, desc->http_path) == -1 || + (desc->http_query != NULL && + (relay_bufferevent_print(dst, "?") == -1 || + relay_bufferevent_print(dst, desc->http_query) == -1)) || + relay_bufferevent_print(dst, " ") == -1 || + relay_bufferevent_print(dst, desc->http_version) == -1) + return (-1); + + return (0); +} + +int +relay_writeresponse_http(struct ctl_relay_event *dst, + struct ctl_relay_event *cre) +{ + struct http_descriptor *desc = (struct http_descriptor *)cre->desc; + + DPRINTF("version: %s rescode: %s resmsg: %s", desc->http_version, + desc->http_rescode, desc->http_resmesg); + + if (relay_bufferevent_print(dst, desc->http_version) == -1 || + relay_bufferevent_print(dst, " ") == -1 || + relay_bufferevent_print(dst, desc->http_rescode) == -1 || + relay_bufferevent_print(dst, " ") == -1 || + relay_bufferevent_print(dst, desc->http_resmesg) == -1) + return (-1); + + return (0); +} + +int +relay_writeheader_http(struct ctl_relay_event *dst, struct ctl_relay_event + *cre) +{ + struct kv *hdr; + struct http_descriptor *desc = (struct http_descriptor *)cre->desc; + char *ptr; + + TAILQ_FOREACH(hdr, &desc->http_headers, kv_entry) { + if (hdr->kv_flags & KV_FLAG_INVALID) + continue; + ptr = hdr->kv_value; + DPRINTF("%s: ptr %s", __func__, ptr); + if (relay_bufferevent_print(dst, hdr->kv_key) == -1 || + (ptr != NULL && + (relay_bufferevent_print(dst, ": ") == -1 || + relay_bufferevent_print(dst, ptr) == -1 || + relay_bufferevent_print(dst, "\r\n") == -1))) + return (-1); + DPRINTF("%s: %s: %s", __func__, hdr->kv_key, + hdr->kv_value == NULL ? "" : hdr->kv_value); + } + + return (0); +} + +enum httpmethod +relay_httpmethod_byname(const char *name) +{ + enum httpmethod id = HTTP_METHOD_NONE; + struct http_method method, *res = NULL; + + /* Set up key */ + method.method_name = name; + + /* + * RFC 2616 section 5.1.1 says that the method is case + * sensitive so we don't do a strcasecmp here. + */ + if ((res = bsearch(&method, http_methods, + sizeof(http_methods) / sizeof(http_methods[0]) - 1, + sizeof(http_methods[0]), relay_httpmethod_cmp)) != NULL) + id = res->method_id; + + return (id); +} + +const char * +relay_httpmethod_byid(u_int id) +{ + const char *name = NULL; + int i; + + for (i = 0; http_methods[i].method_name != NULL; i++) { + if (http_methods[i].method_id == id) { + name = http_methods[i].method_name; + break; } - break; } - switch (pn->action) { - case NODE_ACTION_APPEND: - case NODE_ACTION_CHANGE: - ptr = pn->value; - if ((pn->flags & PNFLAG_MACRO) && - (ptr = relay_expand_http(cre, pn->value, - buf, sizeof(buf))) == NULL) + + return (name); +} + +const char * +relay_httpheader_byid(u_int id) +{ + const char *name = NULL; + int i; + + for (i = 0; http_headers[i].header_name != NULL; i++) { + if (http_headers[i].header_id == id) { + name = http_headers[i].header_name; break; - if (relay_bufferevent_print(cre->dst, pn->key) == -1 || - relay_bufferevent_print(cre->dst, ": ") == -1 || - relay_bufferevent_print(cre->dst, ptr) == -1 || - relay_bufferevent_print(cre->dst, "\r\n") == -1) { - relay_abort_http(con, 500, - "failed to modify header", 0); - return (-1); } - DPRINTF("%s: add '%s: %s'", __func__, pn->key, ptr); - break; - case NODE_ACTION_EXPECT: - DPRINTF("%s: missing '%s: %s'", __func__, pn->key, pn->value); - relay_abort_http(con, 403, "incomplete request", pn->label); + } + + return (name); +} + +u_int +relay_httpheader_byname(const char *name) +{ + enum httpheader id = HTTP_HEADER_OTHER; + struct http_header header, *res = NULL; + + /* Set up key */ + header.header_name = name; + + /* + * In contrast to HTTP methods, HTTP header names are + * case-insensitive (see RFC 2616 section 2.4). + */ + if ((res = bsearch(&header, http_headers, + sizeof(http_headers) / sizeof(http_headers[0]) - 1, + sizeof(http_headers[0]), relay_httpheader_cmp)) != NULL) + id = res->header_id; + + return (id); +} + +static int +relay_httpmethod_cmp(const void *a, const void *b) +{ + const struct http_method *ma = a; + const struct http_method *mb = b; + return (strcmp(ma->method_name, mb->method_name)); +} + +static int +relay_httpheader_cmp(const void *a, const void *b) +{ + const struct http_header *ha = a; + const struct http_header *hb = b; + return (strcasecmp(ha->header_name, hb->header_name)); +} + +int +relay_httpquery_test(struct ctl_relay_event *cre, struct relay_rule *rule, + struct kvlist *actions) +{ + struct http_descriptor *desc = cre->desc; + struct kv *match = &desc->http_matchquery; + struct kv *kv = &rule->rule_kv[KEY_TYPE_QUERY]; + + if (cre->dir == RELAY_DIR_RESPONSE || kv->kv_type != KEY_TYPE_QUERY) + return (0); + else if (kv->kv_key == NULL) + return (0); + else if (relay_lookup_query(cre, kv)) + return (-1); + + relay_match(actions, kv, match, NULL); + + return (0); +} + +int +relay_httpheader_test(struct ctl_relay_event *cre, struct relay_rule *rule, + struct kvlist *actions) +{ + struct http_descriptor *desc = cre->desc; + struct kv *kv = &rule->rule_kv[KEY_TYPE_HEADER]; + struct kv *match; + u_int id = kv->kv_header_id; + const char *value; + + kv->kv_matchptr = NULL; + + if (id == HTTP_HEADER_NONE || kv->kv_type != KEY_TYPE_HEADER) + return (0); + else if (id < HTTP_HEADER_MAX) { + match = desc->http_header[id]; + DPRINTF("%s: standard header %s: %p", __func__, + kv->kv_key, match); + if (match != NULL) + kv->kv_matchptr = &desc->http_header[id]; + } else { + DPRINTF("%s: other header %s", __func__, kv->kv_key); + TAILQ_FOREACH_REVERSE(match, &desc->http_headers, + kvlist, kv_entry) { + if (strcasecmp(match->kv_key, kv->kv_key) == 0 || + fnmatch(kv->kv_key, match->kv_key, FNM_CASEFOLD) != + FNM_NOMATCH) + goto done; + } + match = NULL; + } + + done: + if (kv->kv_option == KEY_OPTION_APPEND || + kv->kv_option == KEY_OPTION_SET) { + /* header can be NULL and will be added later */ + } else if (match == NULL) { + /* Fail if header doesn't exist */ return (-1); - case NODE_ACTION_FILTER: - DPRINTF("%s: filtered '%s: %s'", __func__, pn->key, pn->value); - relay_abort_http(con, 403, "rejecting request", pn->label); + } else if (kv->kv_value != NULL) { + /* Test header value using shell globbing rules */ + value = match->kv_value == NULL ? "" : match->kv_value; + if (fnmatch(kv->kv_value, value, FNM_CASEFOLD) == FNM_NOMATCH) + return (-1); + } + + relay_match(actions, kv, match, &desc->http_headers); + + return (0); +} + +int +relay_httppath_test(struct ctl_relay_event *cre, struct relay_rule *rule, + struct kvlist *actions) +{ + struct http_descriptor *desc = cre->desc; + struct kv *kv = &rule->rule_kv[KEY_TYPE_PATH]; + struct kv *match = &desc->http_pathquery; + const char *query; + + if (cre->dir == RELAY_DIR_RESPONSE || kv->kv_type != KEY_TYPE_PATH) + return (0); + else if (kv->kv_key == NULL) + return (0); + else if (fnmatch(kv->kv_key, desc->http_path, 0) == FNM_NOMATCH) return (-1); - default: - break; + else if (kv->kv_value != NULL && kv->kv_option == KEY_OPTION_NONE) { + query = desc->http_query == NULL ? "" : desc->http_query; + if (fnmatch(kv->kv_value, query, FNM_CASEFOLD) == FNM_NOMATCH) + return (-1); } + + relay_match(actions, kv, match, NULL); + return (0); } int -relay_handle_http(struct ctl_relay_event *cre, struct protonode *proot, - struct protonode *pn, struct protonode *pk, int header) +relay_httpurl_test(struct ctl_relay_event *cre, struct relay_rule *rule, + struct kvlist *actions) { - struct rsession *con = cre->con; - char buf[IBUF_READ_SIZE], *ptr; - int ret = PN_DROP, mark = 0; - struct protonode *next; + struct http_descriptor *desc = cre->desc; + struct kv *host = desc->http_header[HTTP_HEADER_HOST]; + struct kv *kv = &rule->rule_kv[KEY_TYPE_URL]; + struct kv *match = &desc->http_pathquery; + + if (cre->dir == RELAY_DIR_RESPONSE || kv->kv_type != KEY_TYPE_URL) + return (0); + else if (kv->kv_key == NULL || host == NULL || host->kv_value == NULL) + return (0); + else if (rule->rule_action != RULE_ACTION_BLOCK && + kv->kv_option == KEY_OPTION_LOG && + fnmatch(kv->kv_key, match->kv_key, FNM_CASEFOLD) != FNM_NOMATCH) { + /* fnmatch url only for logging */ + } else if (relay_lookup_url(cre, host->kv_value, kv) != 0) + return (-1); - /* Check if this action depends on a marked session */ - if (pn->mark != 0) - mark = pn->mark == con->se_mark ? 1 : -1; + relay_match(actions, kv, match, NULL); - switch (pn->action) { - case NODE_ACTION_EXPECT: - case NODE_ACTION_FILTER: - case NODE_ACTION_MARK: + return (0); +} + +int +relay_httpcookie_test(struct ctl_relay_event *cre, struct relay_rule *rule, + struct kvlist *actions) +{ + struct http_descriptor *desc = cre->desc; + struct kv *kv = &rule->rule_kv[KEY_TYPE_COOKIE]; + u_int id = HTTP_HEADER_NONE; + struct kv *match = NULL; + + if (kv->kv_type != KEY_TYPE_COOKIE) + return (0); + + switch (cre->dir) { + case RELAY_DIR_REQUEST: + id = HTTP_HEADER_COOKIE; + break; + case RELAY_DIR_RESPONSE: + id = HTTP_HEADER_SET_COOKIE; break; default: - if (mark == -1) - return (PN_PASS); + return (0); + /* NOTREACHED */ break; } + if (kv->kv_option == KEY_OPTION_APPEND || + kv->kv_option == KEY_OPTION_SET) { + kv->kv_header_id = id; + /* no cookie, can be NULL and will be added later */ + } else if (id == HTTP_HEADER_NONE) { + /* Fail if cookie doesn't exist */ + return (-1); + } else { + match = desc->http_header[id]; + if (match == NULL) + return (-1); + kv->kv_matchptr = &desc->http_header[id]; + if (kv->kv_key == NULL || match->kv_value == NULL) + return (0); + else if (relay_lookup_cookie(cre, match->kv_value, kv) != 0) + return (-1); + } + + relay_match(actions, kv, match, &desc->http_headers); + + return (0); +} + +int +relay_match_actions(struct ctl_relay_event *cre, struct relay_rule *rule, + struct kvlist *matches, struct kvlist *actions) +{ + struct rsession *con = cre->con; + struct kv *kv; + + /* + * Apply the following options instantly (action per match). + */ + if (rule->rule_table != NULL) + con->se_table = rule->rule_table; + + if (rule->rule_tag != 0) + con->se_tag = rule->rule_tag == -1 ? 0 : rule->rule_tag; + + if (rule->rule_label != 0) + con->se_label = rule->rule_label == -1 ? 0 : rule->rule_label; + + /* + * Apply the remaining options once after evaluation. + */ + if (matches == NULL) { + /* 'pass' or 'block' rule */ + TAILQ_FOREACH(kv, &rule->rule_kvlist, kv_rule_entry) { + TAILQ_INSERT_TAIL(actions, kv, kv_entry); + TAILQ_REMOVE(&rule->rule_kvlist, kv, kv_rule_entry); + } + } else { + /* 'match' rule */ + TAILQ_FOREACH(kv, matches, kv_match_entry) { + TAILQ_INSERT_TAIL(actions, kv, kv_entry); + } + } + + return (0); +} + +int +relay_apply_actions(struct ctl_relay_event *cre, struct kvlist *actions) +{ + struct rsession *con = cre->con; + struct http_descriptor *desc = cre->desc; + struct kv *host = NULL; + const char *value; + struct kv *kv, *match, *kp, *mp, kvcopy, matchcopy; + int httpindex, addkv, ret; + char buf[IBUF_READ_SIZE], *ptr; + enum httpheader hdrid; + + ret = -1; + kp = mp = NULL; + TAILQ_FOREACH(kv, actions, kv_entry) { + kp = NULL; + match = kv->kv_match; + httpindex = addkv = 0; - switch (pn->action) { - case NODE_ACTION_APPEND: - if (!header) - return (PN_PASS); - ptr = pn->value; - if ((pn->flags & PNFLAG_MACRO) && - (ptr = relay_expand_http(cre, pn->value, - buf, sizeof(buf))) == NULL) - break; - if (relay_bufferevent_print(cre->dst, pn->key) == -1 || - relay_bufferevent_print(cre->dst, ": ") == -1 || - relay_bufferevent_print(cre->dst, pk->value) == -1 || - relay_bufferevent_print(cre->dst, ", ") == -1 || - relay_bufferevent_print(cre->dst, ptr) == -1 || - relay_bufferevent_print(cre->dst, "\r\n") == -1) - goto fail; - cre->nodes[pn->id] = 1; - DPRINTF("%s: append '%s: %s, %s'", __func__, - pk->key, pk->value, ptr); - break; - case NODE_ACTION_CHANGE: - case NODE_ACTION_REMOVE: - if (!header) - return (PN_PASS); - DPRINTF("%s: change/remove '%s: %s'", __func__, - pk->key, pk->value); - break; - case NODE_ACTION_EXPECT: /* - * A client may specify the header line for multiple times - * trying to circumvent the filter. + * Although marked as deleted, give a chance to non-critical + * actions, ie. log, to be performed */ - if (cre->nodes[proot->id] > 1) { - relay_abort_http(con, 400, "repeated header line", 0); - return (PN_FAIL); + if (match != NULL && (match->kv_flags & KV_FLAG_INVALID)) + goto matchdel; + + switch (kv->kv_option) { + case KEY_OPTION_APPEND: + case KEY_OPTION_SET: + switch (kv->kv_type) { + case KEY_TYPE_PATH: + if (kv->kv_option == KEY_OPTION_APPEND) { + if (kv_setkey(match, "%s%s", + match->kv_key, kv->kv_key) == -1) + goto fail; + } else { + if (kv_setkey(match, "%s", + kv->kv_value) == -1) + goto fail; + } + break; + case KEY_TYPE_COOKIE: + kp = &kvcopy; + if (kv_inherit(kp, kv) == NULL) + goto fail; + if (kv_set(kp, "%s=%s;", kp->kv_key, + kp->kv_value) == -1) + goto fail; + if (kv->kv_header_id >= HTTP_HEADER_MAX || + kv->kv_header_id <= HTTP_HEADER_NONE) + goto fail; + if (kv_setkey(kp, "%s", + relay_httpheader_byid(kp->kv_header_id)) == + -1) + goto fail; + /* FALLTHROUGH cookie is a header */ + case KEY_TYPE_HEADER: + httpindex = 1; + if (match == NULL) { + addkv = 1; + break; + } + if (match->kv_value == NULL || + kv->kv_option == KEY_OPTION_SET) { + if (kv_set(match, "%s", + kv->kv_value) == -1) + goto fail; + } else { + if (kv_setkey(match, "%s,%s", + match->kv_key, kv->kv_key) == -1) + goto fail; + } + break; + default: + /* query, url not supported */ + break; + } + break; + case KEY_OPTION_REMOVE: + switch (kv->kv_type) { + case KEY_TYPE_PATH: + if (kv_setkey(match, "/") == -1) + goto fail; + break; + case KEY_TYPE_COOKIE: + case KEY_TYPE_HEADER: + if (kv->kv_matchlist != NULL) + match->kv_flags |= KV_FLAG_INVALID; + else + kv_free(match); + if (kv->kv_matchptr) + *kv->kv_matchptr = NULL; + match = kv->kv_match = NULL; + break; + default: + /* query and url not supported */ + break; + } + break; + case KEY_OPTION_HASH: + switch (kv->kv_type) { + case KEY_TYPE_PATH: + value = match->kv_key; + break; + default: + value = match->kv_value; + break; + } + if (!con->se_hashkeyset) + con->se_hashkey = HASHINIT; + con->se_hashkey = hash32_str(value, con->se_hashkey); + con->se_hashkeyset = 1; + log_debug("%s: hashkey 0x%04x", __func__, + con->se_hashkey); + break; + case KEY_OPTION_LOG: + /* perform this later */ + break; + default: + fatalx("relay_action: invalid action"); + /* NOTREACHED */ + } + + /* from now on, reads from kp writes to kv */ + if (kp == NULL) + kp = kv; + if (addkv && kv->kv_matchlist != NULL) { + /* Add new entry to the list (eg. new HTTP header) */ + if ((match = kv_add(kv->kv_matchlist, kp->kv_key, + kp->kv_value)) == NULL) + goto fail; + match->kv_option = kp->kv_option; + match->kv_type = kp->kv_type; + kv->kv_match = match; + } + if (httpindex && kv->kv_matchptr != NULL) { + /* Re-index the fast lookup method */ + if ((hdrid = relay_httpheader_byname(kp->kv_key)) != + HTTP_HEADER_OTHER) { + desc->http_header[hdrid] = match; + kv->kv_matchptr = &desc->http_header[hdrid]; + } else + kv->kv_matchptr = NULL; + } + if (match != NULL && kp->kv_flags & KV_FLAG_MACRO) { + bzero(buf, sizeof(buf)); + if ((ptr = relay_expand_http(cre, kp->kv_value, buf, + sizeof(buf))) == NULL) + goto fail; + if (kv_set(match, ptr) == -1) + goto fail; } - /* FALLTHROUGH */ - case NODE_ACTION_FILTER: - DPRINTF("%s: %s '%s: %s'", __func__, - (pn->action == NODE_ACTION_EXPECT) ? "expect" : "filter", - pn->key, pn->value); - - /* Do not drop the entity */ - ret = PN_PASS; - - if (mark != -1 && - fnmatch(pn->value, pk->value, FNM_CASEFOLD) == 0) { - cre->nodes[proot->id] = 1; - - /* Fail instantly */ - if (pn->action == NODE_ACTION_FILTER) { - (void)relay_lognode(con, pn, pk, - buf, sizeof(buf)); - relay_abort_http(con, 403, - "rejecting request", pn->label); - return (PN_FAIL); + matchdel: + switch(kv->kv_option) { + case KEY_OPTION_LOG: + if (match == NULL) + break; + mp = &matchcopy; + if (kv_inherit(mp, match) == NULL) + goto fail; + if (mp->kv_flags & KV_FLAG_INVALID) { + if (kv_set(mp, "%s*removed*", + mp->kv_value) == -1) + goto fail; + } + switch(kv->kv_type) { + case KEY_TYPE_URL: + host = desc->http_header[HTTP_HEADER_HOST]; + switch (kv->kv_digest) { + case DIGEST_NONE: + if (host == NULL || + host->kv_value == NULL) + break; + if (kv_setkey(mp, "%s%s", + host->kv_value, mp->kv_key) == + -1) + goto fail; + break; + default: + if (kv_setkey(mp, "%s", kv->kv_key) + == -1) + goto fail; + break; + } + break; + default: + break; } + if (kv_log(con->se_log, mp, con->se_label) == -1) + goto fail; + break; + default: + break; + } + + /* actions applied, cleanup kv */ + kv->kv_match = NULL; + kv->kv_matchlist = NULL; + TAILQ_REMOVE(actions, kv, kv_match_entry); + + kv_free(&kvcopy); + if (mp != NULL) { + kv_free(mp); + mp = NULL; } - next = SIMPLEQ_NEXT(pn, entry); - if (next == NULL || next->action != pn->action) - cre->nodes[proot->id]++; - break; - case NODE_ACTION_HASH: - DPRINTF("%s: hash '%s: %s'", __func__, - pn->key, pk->value); - if (!con->se_hashkeyset) - con->se_hashkey = HASHINIT; - con->se_hashkey = hash32_str(pk->value, con->se_hashkey); - con->se_hashkeyset = 1; - log_debug("%s: hash 0x%04x", __func__, con->se_hashkey); - ret = PN_PASS; - break; - case NODE_ACTION_LOG: - log_debug("%s: log '%s: %s'", __func__, pn->key, pk->value); - ret = PN_PASS; - break; - case NODE_ACTION_MARK: - DPRINTF("%s: mark '%s: %s'", __func__, - pn->key, pk->value); - if (fnmatch(pn->value, pk->value, FNM_CASEFOLD) == 0) - con->se_mark = pn->mark; - ret = PN_PASS; - break; - case NODE_ACTION_NONE: - return (PN_PASS); } - if (mark != -1 && relay_lognode(con, pn, pk, buf, sizeof(buf)) == -1) - goto fail; - return (ret); + ret = 0; fail: - relay_abort_http(con, 500, strerror(errno), 0); - return (PN_FAIL); + kv_free(&kvcopy); + kv_free(&matchcopy); + + return (ret); +} + +#define RELAY_GET_SKIP_STEP(i) \ + do { \ + r = r->rule_skip[i]; \ + DPRINTF("%s:%d: skip %d rules", __func__, __LINE__, i); \ + } while (0) + +#define RELAY_GET_NEXT_STEP \ + do { \ + DPRINTF("%s:%d: next rule", __func__, __LINE__); \ + goto nextrule; \ + } while (0) + +int +relay_test(struct protocol *proto, struct ctl_relay_event *cre) +{ + struct rsession *con; + struct http_descriptor *desc = cre->desc; + struct relay_rule *r = NULL, *rule = NULL; + u_int cnt = 0; + u_int action = RES_PASS; + struct kvlist actions, matches; + struct kv *kv; + + con = cre->con; + TAILQ_INIT(&actions); + + r = TAILQ_FIRST(&proto->rules); + while (r != NULL) { + cnt++; + TAILQ_INIT(&matches); + TAILQ_INIT(&r->rule_kvlist); + if (r->rule_dir && r->rule_dir != cre->dir) + RELAY_GET_SKIP_STEP(RULE_SKIP_DIR); + else if (proto->type != r->rule_proto) + RELAY_GET_SKIP_STEP(RULE_SKIP_PROTO); + else if (r->rule_af != AF_UNSPEC && + (cre->ss.ss_family != r->rule_af || + cre->dst->ss.ss_family != r->rule_af)) + RELAY_GET_SKIP_STEP(RULE_SKIP_AF); + else if (RELAY_ADDR_CMP(&r->rule_src, &cre->ss) != 0) + RELAY_GET_SKIP_STEP(RULE_SKIP_SRC); + else if (RELAY_ADDR_CMP(&r->rule_dst, &cre->dst->ss) != 0) + RELAY_GET_SKIP_STEP(RULE_SKIP_DST); + else if (r->rule_method != HTTP_METHOD_NONE && + (desc->http_method == HTTP_METHOD_RESPONSE || + desc->http_method != r->rule_method)) + RELAY_GET_SKIP_STEP(RULE_SKIP_METHOD); + else if (r->rule_tagged && con->se_tag != r->rule_tagged) + RELAY_GET_NEXT_STEP; + else if (relay_httpheader_test(cre, r, &matches) != 0) + RELAY_GET_NEXT_STEP; + else if (relay_httpquery_test(cre, r, &matches) != 0) + 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) + RELAY_GET_NEXT_STEP; + else if (relay_httpcookie_test(cre, r, &matches) != 0) + RELAY_GET_NEXT_STEP; + else { + DPRINTF("%s: session %d: matched rule %d", + __func__, con->se_id, r->rule_id); + + if (r->rule_action == RULE_ACTION_MATCH) { + if (relay_match_actions(cre, r, &matches, + &actions) != 0) { + /* Something bad happened, drop */ + action = RES_DROP; + break; + } + RELAY_GET_NEXT_STEP; + } else if (r->rule_action == RULE_ACTION_BLOCK) + action = RES_DROP; + else if (r->rule_action == RULE_ACTION_PASS) + action = RES_PASS; + + /* Rule matched */ + rule = r; + + /* Temporarily save actions */ + TAILQ_FOREACH(kv, &matches, kv_match_entry) { + TAILQ_INSERT_TAIL(&rule->rule_kvlist, + kv, kv_rule_entry); + } + + if (rule->rule_flags & RULE_FLAG_QUICK) + break; + + nextrule: + /* Continue to find last matching policy */ + r = TAILQ_NEXT(r, rule_entry); + } + } + + if (rule != NULL && + relay_match_actions(cre, rule, NULL, &actions) != 0) { + /* Something bad happened, drop */ + action = RES_DROP; + } + + if (relay_apply_actions(cre, &actions) != 0) { + /* Something bad happened, drop */ + action = RES_DROP; + } + + DPRINTF("%s: session %d: action %d", __func__, + con->se_id, action); + + return (action); +} + +#define RELAY_SET_SKIP_STEPS(i) \ + do { \ + while (head[i] != cur) { \ + head[i]->rule_skip[i] = cur; \ + head[i] = TAILQ_NEXT(head[i], rule_entry); \ + } \ + } while (0) + +/* This code is derived from pf_calc_skip_steps() from pf.c */ +void +relay_calc_skip_steps(struct relay_rules *rules) +{ + struct relay_rule *head[RULE_SKIP_COUNT], *cur, *prev; + int i; + + cur = TAILQ_FIRST(rules); + prev = cur; + for (i = 0; i < RULE_SKIP_COUNT; ++i) + head[i] = cur; + while (cur != NULL) { + if (cur->rule_dir != prev->rule_dir) + RELAY_SET_SKIP_STEPS(RULE_SKIP_DIR); + else if (cur->rule_proto != prev->rule_proto) + RELAY_SET_SKIP_STEPS(RULE_SKIP_PROTO); + else if (cur->rule_af != prev->rule_af) + RELAY_SET_SKIP_STEPS(RULE_SKIP_AF); + else if (RELAY_ADDR_NEQ(&cur->rule_src, &prev->rule_src)) + RELAY_SET_SKIP_STEPS(RULE_SKIP_SRC); + else if (RELAY_ADDR_NEQ(&cur->rule_dst, &prev->rule_dst)) + RELAY_SET_SKIP_STEPS(RULE_SKIP_DST); + else if (cur->rule_method != prev->rule_method) + RELAY_SET_SKIP_STEPS(RULE_SKIP_METHOD); + + prev = cur; + cur = TAILQ_NEXT(cur, rule_entry); + } + for (i = 0; i < RULE_SKIP_COUNT; ++i) + RELAY_SET_SKIP_STEPS(i); +} + +void +relay_match(struct kvlist *actions, struct kv *kv, struct kv *match, + struct kvlist *matchlist) +{ + if (kv->kv_option != KEY_OPTION_NONE) { + kv->kv_match = match; + kv->kv_matchlist = matchlist; + TAILQ_INSERT_TAIL(actions, kv, kv_match_entry); + } } diff --git a/usr.sbin/relayd/relay_udp.c b/usr.sbin/relayd/relay_udp.c index 18f7e7c6ae7..9d871c5af87 100644 --- a/usr.sbin/relayd/relay_udp.c +++ b/usr.sbin/relayd/relay_udp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay_udp.c,v 1.29 2014/06/25 11:05:15 reyk Exp $ */ +/* $OpenBSD: relay_udp.c,v 1.30 2014/07/09 16:42:05 reyk Exp $ */ /* * Copyright (c) 2007 - 2013 Reyk Floeter <reyk@openbsd.org> @@ -52,7 +52,7 @@ extern objid_t relay_conid; extern int proc_id; extern int debug; -struct relayd *env = NULL; +static struct relayd *env = NULL; struct shuffle relay_shuffle; int relay_udp_socket(struct sockaddr_storage *, in_port_t, @@ -249,8 +249,6 @@ relay_udp_server(int fd, short sig, void *arg) con->se_out.con = con; con->se_relay = rlay; con->se_id = ++relay_conid; - con->se_in.tree = &proto->request_tree; - con->se_out.tree = &proto->response_tree; con->se_in.dir = RELAY_DIR_REQUEST; con->se_out.dir = RELAY_DIR_RESPONSE; con->se_retry = rlay->rl_conf.dstretry; diff --git a/usr.sbin/relayd/relayd.c b/usr.sbin/relayd/relayd.c index 4189dbf3747..c102357a360 100644 --- a/usr.sbin/relayd/relayd.c +++ b/usr.sbin/relayd/relayd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.c,v 1.125 2014/06/27 07:49:08 andre Exp $ */ +/* $OpenBSD: relayd.c,v 1.126 2014/07/09 16:42:05 reyk Exp $ */ /* * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org> @@ -296,6 +296,8 @@ parent_configure(struct relayd *env) config_setrt(env, rt); TAILQ_FOREACH(proto, env->sc_protos, entry) config_setproto(env, proto); + TAILQ_FOREACH(proto, env->sc_protos, entry) + config_setrule(env, proto); TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) { /* Check for SSL Inspection */ if ((rlay->rl_conf.flags & (F_SSL|F_SSLCLIENT)) == @@ -537,33 +539,6 @@ parent_dispatch_ca(int fd, struct privsep_proc *p, struct imsg *imsg) } void -purge_tree(struct proto_tree *tree) -{ - struct protonode *proot, *pn; - - while ((proot = RB_ROOT(tree)) != NULL) { - RB_REMOVE(proto_tree, tree, proot); - if (proot->key != NULL) - free(proot->key); - if (proot->value != NULL) - free(proot->value); - while ((pn = SIMPLEQ_FIRST(&proot->head)) != NULL) { - SIMPLEQ_REMOVE_HEAD(&proot->head, entry); - if (pn->key != NULL) - free(pn->key); - if (pn->value != NULL) - free(pn->value); - if (pn->labelname != NULL) - free(pn->labelname); - if (pn->label != 0) - pn_unref(pn->label); - free(pn); - } - free(proot); - } -} - -void purge_table(struct tablelist *head, struct table *table) { struct host *host; @@ -660,6 +635,335 @@ purge_relay(struct relayd *env, struct relay *rlay) free(rlay); } + +struct kv * +kv_add(struct kvlist *keys, char *key, char *value) +{ + struct kv *kv; + + if (key == NULL) + return (NULL); + if ((kv = calloc(1, sizeof(*kv))) == NULL) + return (NULL); + if ((kv->kv_key = strdup(key)) == NULL) { + free(kv); + return (NULL); + } + if (value != NULL && + (kv->kv_value = strdup(value)) == NULL) { + free(kv->kv_key); + free(kv); + return (NULL); + } + + TAILQ_INSERT_TAIL(keys, kv, kv_entry); + + return (kv); +} + +int +kv_set(struct kv *kv, char *fmt, ...) +{ + va_list ap; + char *value = NULL; + + va_start(ap, fmt); + if (vasprintf(&value, fmt, ap) == -1) + return (-1); + va_end(ap); + + if (kv->kv_value != NULL) + free(kv->kv_value); + kv->kv_value = value; + + return (0); +} + +int +kv_setkey(struct kv *kv, char *fmt, ...) +{ + va_list ap; + char *key = NULL; + + va_start(ap, fmt); + if (vasprintf(&key, fmt, ap) == -1) + return (-1); + va_end(ap); + + if (kv->kv_key != NULL) + free(kv->kv_key); + kv->kv_key = key; + + return (0); +} + +void +kv_delete(struct kvlist *keys, struct kv *kv) +{ + TAILQ_REMOVE(keys, kv, kv_entry); + kv_free(kv); + free(kv); +} + +struct kv * +kv_extend(struct kvlist *keys, char *value) +{ + struct kv *kv; + char *newvalue; + + if ((kv = TAILQ_LAST(keys, kvlist)) == NULL) + return (NULL); + + if (kv->kv_value == NULL) { + if ((kv->kv_value = strdup(value)) == NULL) + return (NULL); + } else if (asprintf(&newvalue, "%s%s", kv->kv_value, value) == -1) + return (NULL); + + free(kv->kv_value); + kv->kv_value = newvalue; + + return (kv); +} + +void +kv_purge(struct kvlist *keys) +{ + struct kv *kv; + + while ((kv = TAILQ_FIRST(keys))) + kv_delete(keys, kv); +} + +void +kv_free(struct kv *kv) +{ + if (kv->kv_key != NULL) { + free(kv->kv_key); + } + kv->kv_key = NULL; + if (kv->kv_value != NULL) { + free(kv->kv_value); + } + kv->kv_value = NULL; + kv->kv_matchlist = NULL; + kv->kv_matchptr = NULL; + kv->kv_match = NULL; + memset(kv, 0, sizeof(*kv)); +} + +struct kv * +kv_inherit(struct kv *dst, struct kv *src) +{ + memset(dst, 0, sizeof(*dst)); + memcpy(dst, src, sizeof(*dst)); + + if (src->kv_key != NULL) { + if ((dst->kv_key = strdup(src->kv_key)) == NULL) { + kv_free(dst); + return (NULL); + } + } + if (src->kv_value != NULL) { + if ((dst->kv_value = strdup(src->kv_value)) == NULL) { + kv_free(dst); + return (NULL); + } + } + + if (src->kv_match != NULL) + dst->kv_match = src->kv_match; + if (src->kv_matchptr != NULL) + dst->kv_matchptr = src->kv_matchptr; + if (src->kv_matchlist != NULL) + dst->kv_matchlist = src->kv_matchlist; + + return (dst); +} + +int +kv_log(struct evbuffer *log, struct kv *kv, u_int16_t labelid) +{ + char *msg; + + if (log == NULL) + return (0); + if (asprintf(&msg, " [%s%s%s%s%s]", + labelid == 0 ? "" : label_id2name(labelid), + labelid == 0 ? "" : ", ", + kv->kv_key == NULL ? "(unknown)" : kv->kv_key, + kv->kv_value == NULL ? "" : ": ", + kv->kv_value == NULL ? "" : kv->kv_value) == -1) + return (-1); + if (evbuffer_add(log, msg, strlen(msg)) == -1) { + free(msg); + return (-1); + } + free(msg); + + return (0); +} + +int +rule_add(struct protocol *proto, struct relay_rule *rule, const char *rulefile) +{ + struct relay_rule *r = NULL; + struct kv *kv = NULL; + FILE *fp = NULL; + char buf[BUFSIZ]; + int ret = -1; + u_int i; + + for (i = 0; i < KEY_TYPE_MAX; i++) { + kv = &rule->rule_kv[i]; + if (kv->kv_type != i) + continue; + + if (kv->kv_value != NULL && strchr(kv->kv_value, '$') != NULL) + kv->kv_flags |= KV_FLAG_MACRO; + + switch (kv->kv_option) { + case KEY_OPTION_LOG: + /* log action needs a key or a file to be specified */ + if (kv->kv_key == NULL && rulefile == NULL && + (kv->kv_key = strdup("*")) == NULL) + goto fail; + break; + default: + break; + } + + switch (kv->kv_type) { + case KEY_TYPE_QUERY: + case KEY_TYPE_PATH: + case KEY_TYPE_URL: + if (rule->rule_dir != RELAY_DIR_REQUEST) + goto fail; + break; + default: + break; + } + } + + if (rulefile == NULL) { + TAILQ_INSERT_TAIL(&proto->rules, rule, rule_entry); + return (0); + } + + if ((fp = fopen(rulefile, "r")) == NULL) + goto fail; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + /* strip whitespace and newline characters */ + buf[strcspn(buf, "\r\n\t ")] = '\0'; + if (!strlen(buf) || buf[0] == '#') + continue; + + if ((r = rule_inherit(rule)) == NULL) + goto fail; + + for (i = 0; i < KEY_TYPE_MAX; i++) { + kv = &r->rule_kv[i]; + if (kv->kv_type != i) + continue; + if (kv->kv_key != NULL) + free(kv->kv_key); + if ((kv->kv_key = strdup(buf)) == NULL) { + rule_free(r); + free(r); + goto fail; + } + } + + TAILQ_INSERT_TAIL(&proto->rules, r, rule_entry); + } + + ret = 0; + rule_free(rule); + free(rule); + + fail: + if (fp != NULL) + fclose(fp); + return (ret); +} + +struct relay_rule * +rule_inherit(struct relay_rule *rule) +{ + struct relay_rule *r; + u_int i; + struct kv *kv; + + if ((r = calloc(1, sizeof(*r))) == NULL) + return (NULL); + memcpy(r, rule, sizeof(*r)); + + for (i = 0; i < KEY_TYPE_MAX; i++) { + kv = &rule->rule_kv[i]; + if (kv->kv_type != i) + continue; + if (kv_inherit(&r->rule_kv[i], kv) == NULL) { + free(r); + return(NULL); + } + } + + if (r->rule_label > 0) + label_ref(r->rule_label); + if (r->rule_tag > 0) + tag_ref(r->rule_tag); + if (r->rule_tagged > 0) + tag_ref(r->rule_tagged); + + return (r); +} + +void +rule_free(struct relay_rule *rule) +{ + u_int i; + + for (i = 0; i < KEY_TYPE_MAX; i++) + kv_free(&rule->rule_kv[i]); + if (rule->rule_label > 0) + label_unref(rule->rule_label); + if (rule->rule_tag > 0) + tag_unref(rule->rule_tag); + if (rule->rule_tagged > 0) + tag_unref(rule->rule_tagged); +} + +void +rule_delete(struct relay_rules *rules, struct relay_rule *rule) +{ + TAILQ_REMOVE(rules, rule, rule_entry); + rule_free(rule); + free(rule); +} + +void +rule_settable(struct relay_rules *rules, struct relay_table *rlt) +{ + struct relay_rule *r; + char pname[TABLE_NAME_SIZE]; + + if (rlt->rlt_table == NULL || strlcpy(pname, rlt->rlt_table->conf.name, + sizeof(pname)) >= sizeof(pname)) + return; + + pname[strcspn(pname, ":")] = '\0'; + + TAILQ_FOREACH(r, rules, rule_entry) { + if (r->rule_tablename[0] && + strcmp(pname, r->rule_tablename) == 0) { + r->rule_table = rlt; + } else { + r->rule_table = NULL; + } + } +} + /* * Utility functions */ @@ -857,7 +1161,7 @@ struct ca_pkey * pkey_add(struct relayd *env, EVP_PKEY *pkey, objid_t id) { struct ca_pkey *ca_pkey; - + if (env->sc_pkeys == NULL) fatalx("pkeys"); @@ -1023,168 +1327,6 @@ canonicalize_host(const char *host, char *name, size_t len) return (NULL); } -struct protonode * -protonode_header(enum direction dir, struct protocol *proto, - struct protonode *pk) -{ - struct protonode *pn; - struct proto_tree *tree; - - if (dir == RELAY_DIR_RESPONSE) - tree = &proto->response_tree; - else - tree = &proto->request_tree; - - pn = RB_FIND(proto_tree, tree, pk); - if (pn != NULL) - return (pn); - if ((pn = calloc(1, sizeof(*pn))) == NULL) { - log_warn("%s: calloc", __func__); - return (NULL); - } - pn->key = strdup(pk->key); - if (pn->key == NULL) { - free(pn); - log_warn("%s: strdup", __func__); - return (NULL); - } - pn->value = NULL; - pn->action = NODE_ACTION_NONE; - pn->type = pk->type; - SIMPLEQ_INIT(&pn->head); - if (dir == RELAY_DIR_RESPONSE) - pn->id = - proto->response_nodes++; - else - pn->id = proto->request_nodes++; - if (pn->id == INT_MAX) { - log_warnx("%s: too many protocol " - "nodes defined", __func__); - return (NULL); - } - RB_INSERT(proto_tree, tree, pn); - return (pn); -} - -int -protonode_add(enum direction dir, struct protocol *proto, - struct protonode *node) -{ - struct protonode *pn, *proot, pk; - struct proto_tree *tree; - - if (dir == RELAY_DIR_RESPONSE) - tree = &proto->response_tree; - else - tree = &proto->request_tree; - - if ((pn = calloc(1, sizeof (*pn))) == NULL) { - log_warn("%s: calloc", __func__); - return (-1); - } - bcopy(node, pn, sizeof(*pn)); - pn->key = node->key; - pn->value = node->value; - pn->labelname = NULL; - if (node->labelname != NULL) - pn->label = pn_name2id(node->labelname); - SIMPLEQ_INIT(&pn->head); - if (dir == RELAY_DIR_RESPONSE) - pn->id = proto->response_nodes++; - else - pn->id = proto->request_nodes++; - if (pn->id == INT_MAX) { - log_warnx("%s: too many protocol nodes defined", __func__); - free(pn); - return (-1); - } - if ((proot = - RB_INSERT(proto_tree, tree, pn)) != NULL) { - /* - * A protocol node with the same key already - * exists, append it to a queue behind the - * existing node-> - */ - if (SIMPLEQ_EMPTY(&proot->head)) - SIMPLEQ_NEXT(proot, entry) = pn; - SIMPLEQ_INSERT_TAIL(&proot->head, pn, entry); - } - if (node->type == NODE_TYPE_COOKIE) - pk.key = "Cookie"; - else if (node->type == NODE_TYPE_URL) - pk.key = "Host"; - else - pk.key = "GET"; - if (node->type != NODE_TYPE_HEADER) { - pk.type = NODE_TYPE_HEADER; - pn = protonode_header(dir, proto, &pk); - if (pn == NULL) - return (-1); - switch (node->type) { - case NODE_TYPE_QUERY: - pn->flags |= PNFLAG_LOOKUP_QUERY; - break; - case NODE_TYPE_COOKIE: - pn->flags |= PNFLAG_LOOKUP_COOKIE; - break; - case NODE_TYPE_URL: - if (node->flags & - PNFLAG_LOOKUP_URL_DIGEST) - pn->flags |= node->flags & - PNFLAG_LOOKUP_URL_DIGEST; - else - pn->flags |= - PNFLAG_LOOKUP_DIGEST(0); - break; - default: - break; - } - } - - return (0); -} - -int -protonode_load(enum direction dir, struct protocol *proto, - struct protonode *node, const char *name) -{ - FILE *fp; - char buf[BUFSIZ]; - int ret = -1; - struct protonode pn; - - bcopy(node, &pn, sizeof(pn)); - pn.key = pn.value = NULL; - - if ((fp = fopen(name, "r")) == NULL) - return (-1); - - while (fgets(buf, sizeof(buf), fp) != NULL) { - /* strip whitespace and newline characters */ - buf[strcspn(buf, "\r\n\t ")] = '\0'; - if (!strlen(buf) || buf[0] == '#') - continue; - pn.key = strdup(buf); - if (node->value != NULL) - pn.value = strdup(node->value); - if (pn.key == NULL || - (node->value != NULL && pn.value == NULL)) - goto fail; - if (protonode_add(dir, proto, &pn) == -1) - goto fail; - pn.key = pn.value = NULL; - } - - ret = 0; - fail: - if (pn.key != NULL) - free(pn.key); - if (pn.value != NULL) - free(pn.value); - fclose(fp); - return (ret); -} - int bindany(struct ctl_bindany *bnd) { @@ -1316,6 +1458,102 @@ get_data(u_int8_t *ptr, size_t len) } int +sockaddr_cmp(struct sockaddr *a, struct sockaddr *b, int prefixlen) +{ + struct sockaddr_in *a4, *b4; + struct sockaddr_in6 *a6, *b6; + u_int32_t av[4], bv[4], mv[4]; + + if (a->sa_family == AF_UNSPEC || b->sa_family == AF_UNSPEC) + return (0); + else if (a->sa_family > b->sa_family) + return (1); + else if (a->sa_family < b->sa_family) + return (-1); + + if (prefixlen == -1) + memset(&mv, 0xff, sizeof(mv)); + + switch (a->sa_family) { + case AF_INET: + a4 = (struct sockaddr_in *)a; + b4 = (struct sockaddr_in *)b; + + av[0] = a4->sin_addr.s_addr; + bv[0] = b4->sin_addr.s_addr; + if (prefixlen != -1) + mv[0] = prefixlen2mask(prefixlen); + + if ((av[0] & mv[0]) > (bv[0] & mv[0])) + return (1); + if ((av[0] & mv[0]) < (bv[0] & mv[0])) + return (-1); + break; + case AF_INET6: + a6 = (struct sockaddr_in6 *)a; + b6 = (struct sockaddr_in6 *)b; + + memcpy(&av, &a6->sin6_addr.s6_addr, 16); + memcpy(&bv, &b6->sin6_addr.s6_addr, 16); + if (prefixlen != -1) + prefixlen2mask6(prefixlen, mv); + + if ((av[3] & mv[3]) > (bv[3] & mv[3])) + return (1); + if ((av[3] & mv[3]) < (bv[3] & mv[3])) + return (-1); + if ((av[2] & mv[2]) > (bv[2] & mv[2])) + return (1); + if ((av[2] & mv[2]) < (bv[2] & mv[2])) + return (-1); + if ((av[1] & mv[1]) > (bv[1] & mv[1])) + return (1); + if ((av[1] & mv[1]) < (bv[1] & mv[1])) + return (-1); + if ((av[0] & mv[0]) > (bv[0] & mv[0])) + return (1); + if ((av[0] & mv[0]) < (bv[0] & mv[0])) + return (-1); + break; + } + + return (0); +} + +u_int32_t +prefixlen2mask(u_int8_t prefixlen) +{ + if (prefixlen == 0) + return (0); + + if (prefixlen > 32) + prefixlen = 32; + + return (htonl(0xffffffff << (32 - prefixlen))); +} + +struct in6_addr * +prefixlen2mask6(u_int8_t prefixlen, u_int32_t *mask) +{ + static struct in6_addr s6; + int i; + + if (prefixlen > 128) + prefixlen = 128; + + bzero(&s6, sizeof(s6)); + for (i = 0; i < prefixlen / 8; i++) + s6.s6_addr[i] = 0xff; + i = prefixlen % 8; + if (i) + s6.s6_addr[prefixlen / 8] = 0xff00 >> i; + + memcpy(mask, &s6, sizeof(s6)); + + return (&s6); +} + +int accept_reserve(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int reserve, volatile int *counter) { diff --git a/usr.sbin/relayd/relayd.conf.5 b/usr.sbin/relayd/relayd.conf.5 index 7814eb96a71..2ee4bd491a8 100644 --- a/usr.sbin/relayd/relayd.conf.5 +++ b/usr.sbin/relayd/relayd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: relayd.conf.5,v 1.143 2014/06/25 11:05:15 reyk Exp $ +.\" $OpenBSD: relayd.conf.5,v 1.144 2014/07/09 16:42:05 reyk Exp $ .\" .\" Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org> .\" Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: June 25 2014 $ +.Dd $Mdocdate: July 9 2014 $ .Dt RELAYD.CONF 5 .Os .Sh NAME @@ -54,7 +54,7 @@ health-checked table on layer 3. Relays allow application layer load balancing, SSL acceleration, and general purpose TCP proxying on layer 7. .It Sy Protocols -Protocols are predefined protocol handlers and settings for relays. +Protocols are predefined settings and filter rules for relays. .It Sy Routers Routers are used to insert routes with health-checked gateways for (WAN) link balancing. @@ -765,9 +765,9 @@ Now it finally accepts the SSL connection from the diverted client using the updated certificate and continues to handle the connection and to connect to the remote server. .Sh PROTOCOLS -Protocols are templates defining actions and settings for relays. -They allow setting generic TCP options, SSL settings, and actions -specific to the selected application layer protocol. +Protocols are templates defining settings and rules for relays. +They allow setting generic TCP options, SSL settings, and rules +for the selected application layer protocol. .Pp The protocol directive is available for a number of different application layer protocols. @@ -796,282 +796,14 @@ This is the default. The available configuration directives are described below: .Bl -tag -width Ds .It Xo -.Op Ar direction -.Op Ar type -.Ar action -.Op Ic marked Ar id -.Op Ic log +.Pq Ic block Ns | Ns Ic pass Ns | Ns Ic match +.Op Ar rule .Xc -Define an action for the selected entity. -The optional -.Ic log -keyword will log the entity name and the value and -the optional -.Ic marked -keyword requires that the session has been marked with a given -identifier in order to execute the action. -The actions are dependent on the underlying application -.Ic protocol . -.El -.Pp -.Bq Ar direction -may be one of: -.Bl -tag -width Ds -.It Ic request -Handle the data stream from the client to the relay, like HTTP -requests. -This is the default if the -.Ar direction -directive is omitted. -.It Ic response -Handle the data stream from the target host to the relay, like -HTTP server replies. -.El -.Pp -.Bq Ar type -may be one of: -.Bl -tag -width Ds -.It Ic cookie -Look up the entity as a value in the Cookie header when using the -.Ic http -protocol. -This type is only available with the direction -.Ic request . -.It Ic header -Look up the entity in the application protocol headers, like HTTP -headers in -.Ic http -mode. -.It Ic path -Look up the entity as a value in the URL path when using the -.Ic http -protocol. -This type is only available with the direction -.Ic request . -The -.Ar key -will match the path of the requested URL without the hostname -and query and the value will match the complete query, -for example: -.Bd -literal -offset indent -request path filter "/index.html" -request path filter "foo=bar*" from "/cgi-bin/t.cgi" -.Ed -.It Ic query -Look up the entity as a query variable in the URL when using the -.Ic http -protocol. -This type is only available with the direction -.Ic request , -for example: -.Bd -literal -offset indent -# Will match /cgi-bin/example.pl?foo=bar&ok=yes -request query expect "bar" from "foo" -.Ed -.It Ic url -Look up the entity as a URL suffix/prefix expression consisting of a -canonicalized hostname without port or suffix and a path name or -prefix when using the -.Ic http -protocol. -This type is only available with the direction -.Ic request , -for example: -.Bd -literal -offset indent -request url filter "example.com/index.html" -request url filter "example.com/test.cgi?val=1" -.Ed -.Pp -.Xr relayd 8 -will match the full URL and different possible suffix/prefix -combinations by stripping subdomains and path components (up to 5 -levels), and the query string. -For example, the following -lookups will be done for -.Ar http://www.example.com:81/1/2/3/4/5.html?query=yes : -.Bd -literal -offset indent -www.example.com/1/2/3/4/5.html?query=yes -www.example.com/1/2/3/4/5.html -www.example.com/ -www.example.com/1/ -www.example.com/1/2/ -www.example.com/1/2/3/ -example.com/1/2/3/4/5.html?query=yes -example.com/1/2/3/4/5.html -example.com/ -example.com/1/ -example.com/1/2/ -example.com/1/2/3/ -.Ed -.El -.Pp -.Bq Ar action -may be one of: -.Bl -tag -width Ds -.It Ic append Ar value Ic to Ar key -Append the specified value to a protocol entity with the selected name. -When using the -.Ic http -protocol, -.Ic key -will indicate a specified HTTP header. -If -.Ar key -does not exist in the request, it will be created with the value -set to -.Ar value . -.Pp -The -.Ar value -string -may contain predefined macros that will be expanded at runtime: -.Pp -.Bl -tag -width $SERVER_ADDR -offset indent -compact -.It Ic $REMOTE_ADDR -The IP address of the connected client. -.It Ic $REMOTE_PORT -The TCP source port of the connected client. -.It Ic $SERVER_ADDR -The configured IP address of the relay. -.It Ic $SERVER_PORT -The configured TCP server port of the relay. -.It Ic $SERVER_NAME -The server software name of -.Xr relayd 8 . -.It Ic $TIMEOUT -The configured session timeout of the relay. -.El -.It Ic change Ar key Ic to Ar value -Like the -.Ic append -directive above, but change the contents of the specified entity. -If -.Ar key -does not exist in the request, it will be created with the value -set to -.Ar value . -.Pp -The -.Ar value -string -may contain predefined macros that will be expanded at runtime, -as detailed for the -.Ic append -directive above. -.It Ic expect Ar value Ic from Ar key -Expect an entity -.Ar key -and match against -.Ar value -using shell globbing rules. -If the entity is not present or the value doesn't match, the connection -will be dropped. -.It Xo -.Ic expect -.Op Ic digest -.Ar key -.Xc -Expect an entity -.Ar key -with any possible value. -This is the short form of -.Ic expect Ar * Ic from Ar key . -.Pp -If the -.Ic digest -keyword is specified, -compare the message digest of the entity against the defined string. -The algorithm used is determined by the string length of the -.Ar key -argument, either SHA1 (40 characters) or MD5 (32 characters). -To compute the digest, use this simple command: -.Bd -literal -offset indent -$ echo -n "example.com/path/?args" | sha1 -.Ed -.It Ic expect file Ar path -Like the directive above, but load the non-digest keys from an -external file with the specified -.Ar path -containing one key per line. -Lines will be stripped at the first whitespace or newline character. -Any empty lines or lines beginning with a hash mark -.Pq Sq # -will be ignored. -.It Ic filter Ar value Ic from Ar key -Like the -.Ic expect Ar .. Ic from -directive above, but drop any connections with the specified entity -.Ar key -and a matching -.Ar value . -.It Xo -.Ic filter -.Op Ic digest -.Ar key -.Xc -Like the -.Ic expect -directive above, but drop any connections with the specified entity -.Ar key -and any possible value. -This is the short form of -.Ic filter Ar * Ic from Ar key . -.It Ic filter file Ar path -Like the directive above, but load the non-digest keys from -.Ar path . -See -.Ic expect file Ar path -for more information. -.It Ic hash Ar key -Feed the value of the selected entity into the load balancing hash to -select the target host. -See the -.Ic table -keyword in the -.Sx RELAYS -section above. -.It Ic log Ar key -Log the name and the value of the entity. -.It Ic log file Ar path -Like the directive above, but load the keys from -.Ar path . -See -.Ic expect file Ar path -for more information. -.It Xo -.Ic mark -.Op Ar value Ic from -.Ar key Ic with Ar id -.Xc -Mark the session with the specified identifier (a positive number -between 1 and 65535) if the specified condition matches. -Note that the -.Ic mark -action does not accept the -.Ic marked -option (see above). -.It Ic label Ar string -Add a label to subsequently added actions. -The label will be printed as part of the error message if the -.Ic return error -option is set and may contain HTML tags, for example: -.Bd -literal -offset indent -label "\*(Lta href='http://example.com/advisory.pl?id=7359'\*(Gt\e - Advisory provided by example.com\*(Lt/a\*(Gt" -url filter digest 5c1e03f58f8ce0b457474ffb371fd1ef -url filter digest 80c1a7b8337462093ef8359c57b4d56a -no label -.Ed -.It Ic no label -Do not set a label for subsequently added actions; this is the default. -.It Ic remove Ar key -Remove the entity with the selected name. -.It Ic remove file Ar path -Like the directive above, but load the keys from -.Ar path . -See -.Ic expect file Ar path -for more information. +Specify one ore more rules to filter connections based on their +network or application layer headers; +see the +.Sx FILTER RULES +section for more details. .It Ic return error Op Ar option Return an error response to the client if an internal operation or the forward connection to the client failed. @@ -1222,6 +954,322 @@ Use socket splicing for zero-copy data transfer. This option is enabled by default. .El .El +.Sh FILTER RULES +Relays have the ability have the ability to filter connections based +on their network or application layer headers. +Filter rules apply options to connections based on the specified +filter parameters. +.Pp +For each connection that is processed by a relay, the filter rules are +evaluated in sequential order, from first to last. +For +.Ar block +and +.Ar pass , +the last matching rule decides what action is taken; +if no rule matches the connection, the default action is to establish +the connection without any additional action. +For +.Ar match , +rules are evaluated every time they match; +the pass/block state of a connection remains unchanged. +.Pp +The filter action may be one of the following: +.Bl -tag -width Ds +.It Ic block +The connection is blocked. +If a +.Ic block +rule matches a new connection attempt, it will not be established. +.Ic block +rules can also trigger for existing connections after evaluating +application layer parameters; +any connection of the relay session will be instantly dropped. +.It Ic match +The connection is matched. +This action does not alter the connection state, but allows to apply +additional parameters to the connection. +.It Ic pass +The connection is passed; +.Xr relayd 8 +will continue to process the relay session normally. +.El +.Pp +These filter parameters can be used in the rules: +.Bl -tag -width Ds +.It Ic request No or Ic response +A relay session always consists of two connections: +the +.Ic request , +a client initiating a new connection to a server via the relay, +and the +.Ic response , +the server accepting the connection. +Depending on the protocol, +an established session can be purely request/response-based (like +HTTP), exchange data in a bidirectional way (like arbitrary TCP +sessions), or just contain a single datagram and an optional response +(like UDP-based protocols). +But the client always +.Ar requests +to communicate with a remote peer; the server. +.It Ic quick +If a connection is matched by a rule with the +.Ic quick +option set, +the rule is considered to be the last matching rule and any further +evaluation is skipped. +.It Ic inet No or Ic inet6 +Only match connections with the specified address family, +either of type IPv4 or IPv6. +.\" XXX .It Ic from +.\" XXX .It Ic to +.It Ic label Ar string +The label will be printed as part of the error message if the +.Ic return error +option is set and may contain HTML tags, for example: +.Bd -literal -offset indent +block request url digest 5c1e03f58f8ce0b457474ffb371fd1ef \e + label "\*(Lta href='http://example.com/adv.pl?id=7359'\*(Gt\e + Advisory provided by example.com\*(Lt/a\*(Gt" +.Ed +.It Ic no Ar parameter +Reset a sticky parameter that was previously set by a matching rule. +The +.Ar parameter +is a keyword that can be either +.Ic label +or +.Ic tag . +.It Ic tag Ar string +Add a "sticky" tag to connections matching this filter rule. +Tags can be used to filter the connection by further rules using the +.Ic tagged +option. +Only one tag is assigned per connection; +the tag will be replaced if the connection is already tagged. +.It Ic tagged Ar string +Match the connection if it is already tagged with a given tag by a +previous rule. +.El +.Pp +The following parameters are available when using the +.Ic http +protocol: +.Bl -tag -width Ds +.It Ic method Ar NAME +Match the HTTP request method. +The method is specified by +.Ar name +and can be either +.Ic CONNECT , +.Ic COPY , +.Ic DELETE , +.Ic GET , +.Ic HEAD , +.Ic LOCK , +.Ic MKCOL , +.Ic MOVE , +.Ic OPTIONS , +.Ic PATCH , +.Ic POST , +.Ic PROPFIND , +.Ic PROPPATCH , +.Ic PUT , +.Ic TRACE , +or +.Ic UNLOCK . +.It Xo +.Ar type Ar option +.Oo Oo Ic digest Oc +.Pq Ar key Ns | Ns Ic file Ar path +.Oo Ic value Ar value Oc Oc +.Xc +Match a specified HTTP header entity and an optional +.Ic key +and +.Ic value . +An +.Ic option +can be specified to modify the matched entity or to trigger an event. +The entity is extracted from the HTTP request or response header and +can be either of +.Ar type +.Ic cookie , +.Ic header , +.Ic path , +.Ic query , +or +.Ic url . +.Pp +Instead of a single +.Ar key, +multiple keys can be loaded from a +.Ic file +specified by +.Ar path +that contains one key per line. +Lines will be stripped at the first whitespace or newline character +and any empty lines or lines beginning with a hash mark (`#') will be +ignored. +.Pp +If the +.Ic digest +keyword is specified, +compare the message digest of the key against the defined string. +The algorithm used is determined by the string length of the +.Ar key +argument, either SHA1 (40 characters) or MD5 (32 characters). +To compute the digest, +for example for an +.Ic url , +use this simple command: +.Bd -literal -offset indent +$ echo -n "example.com/path/?args" | sha1 +.Ed +.El +.Pp +.Bq Ar type +may be one of: +.Bl -tag -width Ds +.It Ic cookie Ar option Oo Ar key Oo Ic value Ar value Oc Oc +Look up the entity as a value in the Cookie header. +This type is only available with the direction +.Ic request . +.It Ic header Ar option Oo Ar key Oo Ic value Ar value Oc Oc +Look up the entity in the application protocol headers, like HTTP +headers in +.Ic http +mode. +.It Ic path Ar option Oo Ar key Oo Ic value Ar value Oc Oc +Look up the entity as a value in the URL path when using the +.Ic http +protocol. +This type is only available with the direction +.Ic request . +The +.Ar key +will match the path of the requested URL without the hostname +and query and the value will match the complete query, +for example: +.Bd -literal -offset indent +block path "/index.html" +block path "/cgi-bin/t.cgi" value "foo=bar*" +.Ed +.It Ic query Ar option Oo Ar key Oo Ic value Ar value Oc Oc +Look up the entity as a query variable in the URL when using the +.Ic http +protocol. +This type is only available with the direction +.Ic request , +for example: +.Bd -literal -offset indent +# Will match /cgi-bin/example.pl?foo=bar&ok=yes +request query expect "bar" from "foo" +.Ed +.It Ic url Ar option Oo Oo Ic digest Oc Ar key Oo Ic value Ar value Oc Oc +Look up the entity as a URL suffix/prefix expression consisting of a +canonicalized hostname without port or suffix and a path name or +prefix when using the +.Ic http +protocol. +This type is only available with the direction +.Ic request , +for example: +.Bd -literal -offset indent +block url "example.com/index.html" +block url "example.com/test.cgi?val=1" +.Ed +.Pp +.Xr relayd 8 +will match the full URL and different possible suffix/prefix +combinations by stripping subdomains and path components (up to 5 +levels), and the query string. +For example, the following +lookups will be done for +.Ar http://www.example.com:81/1/2/3/4/5.html?query=yes : +.Bd -literal -offset indent +www.example.com/1/2/3/4/5.html?query=yes +www.example.com/1/2/3/4/5.html +www.example.com/ +www.example.com/1/ +www.example.com/1/2/ +www.example.com/1/2/3/ +example.com/1/2/3/4/5.html?query=yes +example.com/1/2/3/4/5.html +example.com/ +example.com/1/ +example.com/1/2/ +example.com/1/2/3/ +.Ed +.El +.Pp +.Bq Ar option +may be one of: +.Bl -tag -width Ds +.It Ic append +Append the specified +.Ar value +to a protocol entity with the selected +.Ar key +name. +If it does not exist, it will be created with the new value. +.Pp +The value string may contain predefined macros that will be expanded +at runtime: +.Pp +.Bl -tag -width $SERVER_ADDR -offset indent -compact +.It Ic $REMOTE_ADDR +The IP address of the connected client. +.It Ic $REMOTE_PORT +The TCP source port of the connected client. +.It Ic $SERVER_ADDR +The configured IP address of the relay. +.It Ic $SERVER_PORT +The configured TCP server port of the relay. +.It Ic $SERVER_NAME +The server software name of +.Xr relayd 8 . +.It Ic $TIMEOUT +The configured session timeout of the relay. +.El +.It Ic hash +Feed the +.Ar value +of the selected entity into the load balancing hash to select the +target host. +See the +.Ic table +keyword in the +.Sx RELAYS +section above. +.It Ic log +Log the +.Ar key +name and the +.Ar value +of the entity. +.It Ic remove +Remove the entity with the selected +.Ar key +name. +.It Ic set +Like the +.Ic append +directive above, but change the contents of the specified entity. +If +.Ar key +does not exist in the request, it will be created with the new +.Ar value . +.Pp +The +.Ar value +string +may contain predefined macros that will be expanded at runtime, +as detailed for the +.Ic append +directive above. +.El .Sh ROUTERS Routers represent routing table entries in the kernel forwarding database, see @@ -1358,12 +1406,17 @@ and include the variable in the hash to calculate the target host: .Bd -literal -offset indent http protocol "http_ssl" { - header append "$REMOTE_ADDR" to "X-Forwarded-For" - header append "$SERVER_ADDR:$SERVER_PORT" to "X-Forwarded-By" - header change "Keep-Alive" to "$TIMEOUT" - query hash "sessid" - cookie hash "sessid" - path filter "*command=*" from "/cgi-bin/index.cgi" + match header append "X-Forwarded-For" \e + value "$REMOTE_ADDR" + match header append "X-Forwarded-By" \e + value "$REMOTE_ADDR:$SERVER_PORT" + match header set "Keep-Alive" value "$TIMEOUT" + + match query hash "sessid" + match hash "sessid" + + pass + block path "/cgi-bin/index.cgi" value "*command=*" ssl { sslv2, ciphers "MEDIUM:HIGH" } } @@ -1426,8 +1479,9 @@ And finally configure the SSL inspection in http protocol httpfilter { return error - label "Prohibited!" - request url filter "social.network.example.com/" + pass + match label "Prohibited!" + block url "social.network.example.com/" # New configuration directives for SSL Interception ssl ca key "/etc/ssl/private/ca.key" password "password123" diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h index ef41a00c933..35e8b0b014c 100644 --- a/usr.sbin/relayd/relayd.h +++ b/usr.sbin/relayd/relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.181 2014/06/27 07:49:08 andre Exp $ */ +/* $OpenBSD: relayd.h,v 1.182 2014/07/09 16:42:05 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org> @@ -41,8 +41,10 @@ #define CHECK_INTERVAL 10 #define EMPTY_TABLE UINT_MAX #define EMPTY_ID UINT_MAX +#define LABEL_NAME_SIZE 1024 +#define TAG_NAME_SIZE 64 #define TABLE_NAME_SIZE 64 -#define TAG_NAME_SIZE 64 +#define RD_TAG_NAME_SIZE 64 #define RT_LABEL_SIZE 32 #define SRV_NAME_SIZE 64 #define MAX_NAME_SIZE 64 @@ -90,7 +92,6 @@ struct shuffle { int isindex; }; - typedef u_int32_t objid_t; struct ctl_flags { @@ -153,22 +154,11 @@ struct ctl_tcp_event { SSL *ssl; }; -enum httpmethod { - HTTP_METHOD_NONE = 0, - HTTP_METHOD_GET = 1, - HTTP_METHOD_HEAD = 2, - HTTP_METHOD_POST = 3, - HTTP_METHOD_PUT = 4, - HTTP_METHOD_DELETE = 5, - HTTP_METHOD_OPTIONS = 6, - HTTP_METHOD_TRACE = 7, - HTTP_METHOD_CONNECT = 8, - HTTP_METHOD_RESPONSE = 9 /* Server response */ -}; - enum direction { - RELAY_DIR_REQUEST = 0, - RELAY_DIR_RESPONSE = 1 + RELAY_DIR_INVALID = -1, + RELAY_DIR_ANY = 0, + RELAY_DIR_REQUEST = 1, + RELAY_DIR_RESPONSE = 2 }; struct ctl_relay_event { @@ -181,23 +171,18 @@ struct ctl_relay_event { struct rsession *con; SSL *ssl; X509 *sslcert; - u_int8_t *nodes; - struct proto_tree *tree; - - char *path; - char *args; - char *version; off_t splicelen; int line; off_t toread; - int chunked; int done; - enum httpmethod method; enum direction dir; u_int8_t *buf; int buflen; + + /* protocol-specific descriptor */ + void *desc; }; enum httpchunk { @@ -253,6 +238,64 @@ struct ctl_stats { u_int32_t last_day; }; +enum key_option { + KEY_OPTION_NONE = 0, + KEY_OPTION_APPEND, + KEY_OPTION_SET, + KEY_OPTION_REMOVE, + KEY_OPTION_HASH, + KEY_OPTION_LOG +}; + +enum key_type { + KEY_TYPE_NONE = 0, + KEY_TYPE_COOKIE, + KEY_TYPE_HEADER, + KEY_TYPE_PATH, + KEY_TYPE_QUERY, + KEY_TYPE_URL, + KEY_TYPE_MAX +}; + +struct ctl_kvlen { + ssize_t key; + ssize_t value; +}; + +struct ctl_rule { + struct ctl_kvlen kvlen[KEY_TYPE_MAX]; +}; + +enum digest_type { + DIGEST_NONE = 0, + DIGEST_SHA1 = 1, + DIGEST_MD5 = 2 +}; + +struct kv { + char *kv_key; + char *kv_value; + + enum key_type kv_type; + enum key_option kv_option; + enum digest_type kv_digest; + u_int kv_header_id; + +#define KV_FLAG_MACRO 0x01 +#define KV_FLAG_INVALID 0x02 + u_int8_t kv_flags; + + /* A few pointers used by the rule actions */ + struct kv *kv_match; + struct kvlist *kv_matchlist; + struct kv **kv_matchptr; + + TAILQ_ENTRY(kv) kv_match_entry; + TAILQ_ENTRY(kv) kv_rule_entry; + TAILQ_ENTRY(kv) kv_entry; +}; +TAILQ_HEAD(kvlist, kv); + struct portrange { in_port_t val[2]; u_int8_t op; @@ -382,12 +425,6 @@ enum host_status { }; #define HOST_ISUP(x) (x == HOST_UP) -enum digest_type { - DIGEST_NONE = 0, - DIGEST_SHA1 = 1, - DIGEST_MD5 = 2 -}; - struct table_config { objid_t id; objid_t rdrid; @@ -400,6 +437,7 @@ struct table_config { int retcode; int skip_cnt; char name[TABLE_NAME_SIZE]; + size_t name_len; char path[MAXPATHLEN]; char exbuf[64]; char digest[41]; /* length of sha1 digest * 2 */ @@ -436,7 +474,7 @@ struct rdr_config { objid_t backup_id; int mode; char name[SRV_NAME_SIZE]; - char tag[TAG_NAME_SIZE]; + char tag[RD_TAG_NAME_SIZE]; struct timeval timeout; }; @@ -469,11 +507,12 @@ struct rsession { int se_retry; int se_retrycount; int se_connectcount; - u_int16_t se_mark; struct evbuffer *se_log; struct relay *se_relay; struct ctl_natlook *se_cnl; int se_bnds; + u_int16_t se_tag; + u_int16_t se_label; int se_cid; pid_t se_pid; @@ -481,78 +520,86 @@ struct rsession { }; SPLAY_HEAD(session_tree, rsession); -enum nodeaction { - NODE_ACTION_NONE = 0, - NODE_ACTION_APPEND = 1, - NODE_ACTION_CHANGE = 2, - NODE_ACTION_REMOVE = 3, - NODE_ACTION_EXPECT = 4, - NODE_ACTION_FILTER = 5, - NODE_ACTION_HASH = 6, - NODE_ACTION_LOG = 7, - NODE_ACTION_MARK = 8 -}; - -enum nodetype { - NODE_TYPE_HEADER = 0, - NODE_TYPE_QUERY = 1, - NODE_TYPE_COOKIE = 2, - NODE_TYPE_PATH = 3, - NODE_TYPE_URL = 4 +enum prototype { + RELAY_PROTO_TCP = 0, + RELAY_PROTO_HTTP, + RELAY_PROTO_DNS }; -#define PNFLAG_MACRO 0x01 -#define PNFLAG_MARK 0x02 -#define PNFLAG_LOG 0x04 -#define PNFLAG_LOOKUP_QUERY 0x08 -#define PNFLAG_LOOKUP_COOKIE 0x10 -#define PNFLAG_LOOKUP_URL 0xe0 -#define PNFLAG_LOOKUP_URL_DIGEST 0xc0 -#define PNFLAG_LOOKUP_DIGEST(x) (0x20 << x) - -enum noderesult { - PN_DROP = 0, - PN_PASS = 1, - PN_FAIL = -1 +enum relay_result { + RES_DROP = 0, + RES_PASS = 1, + RES_FAIL = -1 }; -struct protonode_config { - objid_t protoid; - size_t keylen; - size_t valuelen; - size_t len; - size_t labelnamelen; - u_int dir; +enum rule_action { + RULE_ACTION_MATCH = 0, + RULE_ACTION_PASS, + RULE_ACTION_BLOCK }; -struct protonode { - struct protonode_config conf; - objid_t id; - enum nodeaction action; - u_int8_t flags; - enum nodetype type; - u_int16_t mark; - u_int16_t label; - - char *labelname; - char *key; - char *value; - - SIMPLEQ_HEAD(, protonode) head; - SIMPLEQ_ENTRY(protonode) entry; - - RB_ENTRY(protonode) nodes; +struct rule_addr { + int addr_af; + struct sockaddr_storage addr; + u_int8_t addr_mask; + int addr_net; + in_port_t addr_port; }; -RB_HEAD(proto_tree, protonode); -#define PROTONODE_FOREACH(elm, root, field) \ - for (elm = root; elm != NULL; elm = SIMPLEQ_NEXT(elm, entry)) \ - -enum prototype { - RELAY_PROTO_TCP = 0, - RELAY_PROTO_HTTP = 1, - RELAY_PROTO_DNS = 2 +#define RELAY_ADDR_EQ(_a, _b) \ + ((_a)->addr_mask == (_b)->addr_mask && \ + sockaddr_cmp((struct sockaddr *)&(_a)->addr, \ + (struct sockaddr *)&(_b)->addr, (_a)->addr_mask) == 0) + +#define RELAY_ADDR_CMP(_a, _b) \ + sockaddr_cmp((struct sockaddr *)&(_a)->addr, \ + (struct sockaddr *)(_b), (_a)->addr_mask) + +#define RELAY_ADDR_NEQ(_a, _b) \ + ((_a)->addr_mask != (_b)->addr_mask || \ + sockaddr_cmp((struct sockaddr *)&(_a)->addr, \ + (struct sockaddr *)&(_b)->addr, (_a)->addr_mask) != 0) + +struct relay_rule { + objid_t rule_id; + objid_t rule_protoid; + + u_int rule_action; +#define RULE_SKIP_PROTO 0 +#define RULE_SKIP_DIR 1 +#define RULE_SKIP_AF 2 +#define RULE_SKIP_SRC 3 +#define RULE_SKIP_DST 4 +#define RULE_SKIP_METHOD 5 +#define RULE_SKIP_COUNT 6 + struct relay_rule *rule_skip[RULE_SKIP_COUNT]; + +#define RULE_FLAG_QUICK 0x01 + u_int8_t rule_flags; + + int rule_label; + int rule_tag; + int rule_tagged; + enum direction rule_dir; + u_int rule_proto; + int rule_af; + struct rule_addr rule_src; + struct rule_addr rule_dst; + struct relay_table *rule_table; + + u_int rule_method; + char rule_labelname[LABEL_NAME_SIZE]; + char rule_tablename[TABLE_NAME_SIZE]; + char rule_taggedname[TAG_NAME_SIZE]; + char rule_tagname[TAG_NAME_SIZE]; + + struct ctl_rule rule_ctl; + struct kv rule_kv[KEY_TYPE_MAX]; + struct kvlist rule_kvlist; + + TAILQ_ENTRY(relay_rule) rule_entry; }; +TAILQ_HEAD(relay_rules, relay_rule); #define TCPFLAG_NODELAY 0x01 #define TCPFLAG_NNODELAY 0x02 @@ -598,19 +645,17 @@ struct protocol { char name[MAX_NAME_SIZE]; int cache; enum prototype type; - int lateconnect; char *style; - int request_nodes; - struct proto_tree request_tree; - int response_nodes; - struct proto_tree response_tree; - int (*cmp)(struct rsession *, struct rsession *); void *(*validate)(struct rsession *, struct relay *, struct sockaddr_storage *, u_int8_t *, size_t); int (*request)(struct rsession *); + void (*close)(struct rsession *); + + struct relay_rules rules; + int rulecount; TAILQ_ENTRY(protocol) entry; }; @@ -855,7 +900,7 @@ enum imsg_type { IMSG_CFG_ROUTER, IMSG_CFG_ROUTE, IMSG_CFG_PROTO, - IMSG_CFG_PROTONODE, + IMSG_CFG_RULE, IMSG_CFG_RELAY, IMSG_CFG_RELAY_TABLE, IMSG_CFG_DONE, @@ -1059,8 +1104,6 @@ int relay_splice(struct ctl_relay_event *); int relay_splicelen(struct ctl_relay_event *); int relay_spliceadjust(struct ctl_relay_event *); void relay_error(struct bufferevent *, short, void *); -int relay_lognode(struct rsession *, - struct protonode *, struct protonode *, char *, size_t); int relay_preconnect(struct rsession *); int relay_connect(struct rsession *); void relay_connected(int, short, void *); @@ -1068,21 +1111,34 @@ void relay_bindanyreq(struct rsession *, in_port_t, int); void relay_bindany(int, short, void *); void relay_dump(struct ctl_relay_event *, const void *, size_t); int relay_bufferevent_add(struct event *, int); -int relay_bufferevent_print(struct ctl_relay_event *, char *); +int relay_bufferevent_print(struct ctl_relay_event *, const char *); int relay_bufferevent_write_buffer(struct ctl_relay_event *, struct evbuffer *); int relay_bufferevent_write_chunk(struct ctl_relay_event *, struct evbuffer *, size_t); int relay_bufferevent_write(struct ctl_relay_event *, void *, size_t); +int relay_test(struct protocol *, struct ctl_relay_event *); +void relay_calc_skip_steps(struct relay_rules *); +void relay_match(struct kvlist *, struct kv *, struct kv *, + struct kvlist *); -RB_PROTOTYPE(proto_tree, protonode, se_nodes, relay_proto_cmp); SPLAY_PROTOTYPE(session_tree, rsession, se_nodes, relay_session_cmp); /* relay_http.c */ +void relay_http(struct relayd *); +void relay_http_init(struct relay *); void relay_abort_http(struct rsession *, u_int, const char *, u_int16_t); void relay_read_http(struct bufferevent *, void *); +void relay_close_http(struct rsession *); +u_int relay_httpmethod_byname(const char *); +const char + *relay_httpmethod_byid(u_int); +u_int relay_httpheader_byname(const char *); +const char + *relay_httpheader_byid(u_int id); +int relay_httpdesc_init(struct ctl_relay_event *); /* relay_udp.c */ void relay_udp_privinit(struct relayd *, struct relay *); @@ -1147,17 +1203,10 @@ struct ca_pkey *pkey_add(struct relayd *, EVP_PKEY *, objid_t); int expand_string(char *, size_t, const char *, const char *); void translate_string(char *); void purge_key(char **, off_t); -void purge_tree(struct proto_tree *); void purge_table(struct tablelist *, struct table *); void purge_relay(struct relayd *, struct relay *); char *digeststr(enum digest_type, const u_int8_t *, size_t, char *); const char *canonicalize_host(const char *, char *, size_t); -struct protonode *protonode_header(enum direction, struct protocol *, - struct protonode *); -int protonode_add(enum direction, struct protocol *, - struct protonode *); -int protonode_load(enum direction, struct protocol *, - struct protonode *, const char *); int map6to4(struct sockaddr_storage *); int map4to6(struct sockaddr_storage *, struct sockaddr_storage *); void imsg_event_add(struct imsgev *); @@ -1166,8 +1215,27 @@ int imsg_compose_event(struct imsgev *, u_int16_t, u_int32_t, void socket_rlimit(int); char *get_string(u_int8_t *, size_t); void *get_data(u_int8_t *, size_t); +int sockaddr_cmp(struct sockaddr *, struct sockaddr *, int); +struct in6_addr *prefixlen2mask6(u_int8_t, u_int32_t *); +u_int32_t prefixlen2mask(u_int8_t); int accept_reserve(int, struct sockaddr *, socklen_t *, int, volatile int *); +struct kv *kv_add(struct kvlist *, char *, char *); +int kv_set(struct kv *, char *, ...); +int kv_setkey(struct kv *, char *, ...); +void kv_delete(struct kvlist *, struct kv *); +struct kv *kv_extend(struct kvlist *, char *); +void kv_purge(struct kvlist *); +void kv_free(struct kv *); +struct kv *kv_inherit(struct kv *, struct kv *); +int kv_log(struct evbuffer *, struct kv *, u_int16_t); +int rule_add(struct protocol *, struct relay_rule *, const char + *); +void rule_delete(struct relay_rules *, struct relay_rule *); +void rule_free(struct relay_rule *); +struct relay_rule + *rule_inherit(struct relay_rule *); +void rule_settable(struct relay_rules *, struct relay_table *); /* carp.c */ int carp_demote_init(char *, int); @@ -1177,10 +1245,14 @@ int carp_demote_set(char *, int); int carp_demote_reset(char *, int); /* name2id.c */ -u_int16_t pn_name2id(const char *); -const char *pn_id2name(u_int16_t); -void pn_unref(u_int16_t); -void pn_ref(u_int16_t); +u_int16_t label_name2id(const char *); +const char *label_id2name(u_int16_t); +void label_unref(u_int16_t); +void label_ref(u_int16_t); +u_int16_t tag_name2id(const char *); +const char *tag_id2name(u_int16_t); +void tag_unref(u_int16_t); +void tag_ref(u_int16_t); /* snmp.c */ void snmp_init(struct relayd *, enum privsep_procid); @@ -1243,12 +1315,11 @@ int config_getvirt(struct relayd *, struct imsg *); int config_setrt(struct relayd *, struct router *); int config_getrt(struct relayd *, struct imsg *); int config_getroute(struct relayd *, struct imsg *); -int config_setproto(struct relayd *env, struct protocol *); +int config_setproto(struct relayd *, struct protocol *); int config_getproto(struct relayd *, struct imsg *); -int config_setprotonode(struct relayd *, enum privsep_procid, - struct protocol *, enum direction); -int config_getprotonode(struct relayd *, struct imsg *); -int config_setrelay(struct relayd *env, struct relay *); +int config_setrule(struct relayd *, struct protocol *); +int config_getrule(struct relayd *, struct imsg *); +int config_setrelay(struct relayd *, struct relay *); int config_getrelay(struct relayd *, struct imsg *); int config_getrelaytable(struct relayd *, struct imsg *); |