summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
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 *);