summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2014-07-09 16:42:06 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2014-07-09 16:42:06 +0000
commit6581036fa87b031b42e6fb09a7be534f271b3558 (patch)
treed3a1e72d1d971ca9841c3915d96105eff75d8fc4 /usr.sbin
parent80cffbe9964236d1735bdb0e892108f50a57ae28 (diff)
Replace the protocol directives for HTTP with a new generic filtering
language. The grammar is inspired by pf and allows to write versatile last-matching filter rules in protocol sections starting with the "pass", "block" or "match" keywords. This work was started almost two years ago and replaces large parts of relayd(8)'s HTTP and filtering code. The initial version reimplements and extends HTTP filtering, but will be improved to support generic TCP and other protocols later. With some testing, feedback, and help from benno@ and andre@. OK benno@
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/relayctl/relayctl.c9
-rw-r--r--usr.sbin/relayd/config.c255
-rw-r--r--usr.sbin/relayd/http.h371
-rw-r--r--usr.sbin/relayd/name2id.c51
-rw-r--r--usr.sbin/relayd/parse.y628
-rw-r--r--usr.sbin/relayd/pfe.c4
-rw-r--r--usr.sbin/relayd/relay.c319
-rw-r--r--usr.sbin/relayd/relay_http.c1540
-rw-r--r--usr.sbin/relayd/relay_udp.c6
-rw-r--r--usr.sbin/relayd/relayd.c620
-rw-r--r--usr.sbin/relayd/relayd.conf.5632
-rw-r--r--usr.sbin/relayd/relayd.h319
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 *);