diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2007-11-19 14:48:20 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2007-11-19 14:48:20 +0000 |
commit | 4c531e2a02bcf90b8cc73c8795d1a1039207821d (patch) | |
tree | 620120625ab3b1b1e0fd3549faf91b479570eb6e /usr.sbin/relayd | |
parent | c42b469a54c61a3d1eefce70aa16636281d6d6d7 (diff) |
rework the internal handling of protocol actions a little bit:
- allow to use a key for multiple times by appending a queue of
additional matches to the tree node. for example, this allows to
specify multiple "expect" or "filter" actions to white-/black-list
a list of HTTP-headers, URLs, ..
- prevent specifing an HTTP header for multiple times when using the
expect action.
- minor code shuffling
Diffstat (limited to 'usr.sbin/relayd')
-rw-r--r-- | usr.sbin/relayd/parse.y | 23 | ||||
-rw-r--r-- | usr.sbin/relayd/relay.c | 361 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.c | 12 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.h | 22 |
4 files changed, 247 insertions, 171 deletions
diff --git a/usr.sbin/relayd/parse.y b/usr.sbin/relayd/parse.y index 01495ccc16d..4bb3bd2a9b3 100644 --- a/usr.sbin/relayd/parse.y +++ b/usr.sbin/relayd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.81 2007/11/19 14:41:05 reyk Exp $ */ +/* $OpenBSD: parse.y,v 1.82 2007/11/19 14:48:19 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -641,19 +641,13 @@ protoptsl : SSL sslflags | TCP '{' tcpflags_l '}' | PROTO proto_type { proto->type = $2; } | direction protonode log { - struct protonode *pn, pk; + struct protonode *pn, *proot, pk; struct proto_tree *tree; if ($1 == RELAY_DIR_RESPONSE) tree = &proto->response_tree; else tree = &proto->request_tree; - pn = RB_FIND(proto_tree, tree, &node); - if (pn != NULL) { - yyerror("protocol node %s defined twice", - node.key); - YYERROR; - } if ((pn = calloc(1, sizeof (*pn))) == NULL) fatal("out of memory"); @@ -661,6 +655,7 @@ protoptsl : SSL sslflags pn->key = node.key; pn->value = node.value; pn->type = node.type; + SIMPLEQ_INIT(&pn->head); if ($1 == RELAY_DIR_RESPONSE) pn->id = proto->response_nodes++; else @@ -671,7 +666,17 @@ protoptsl : SSL sslflags yyerror("too many protocol nodes defined"); YYERROR; } - RB_INSERT(proto_tree, tree, pn); + 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"; diff --git a/usr.sbin/relayd/relay.c b/usr.sbin/relayd/relay.c index f42f9efed69..06f1e64619f 100644 --- a/usr.sbin/relayd/relay.c +++ b/usr.sbin/relayd/relay.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay.c,v 1.53 2007/10/22 17:14:10 reyk Exp $ */ +/* $OpenBSD: relay.c,v 1.54 2007/11/19 14:48:19 reyk Exp $ */ /* * Copyright (c) 2006, 2007 Reyk Floeter <reyk@openbsd.org> @@ -56,6 +56,7 @@ void relay_dispatch_parent(int, short, void *); void relay_shutdown(void); void relay_privinit(void); +void relay_nodedebug(const char *, struct protonode *); void relay_protodebug(struct relay *); void relay_init(void); void relay_launch(void); @@ -83,8 +84,12 @@ void relay_write(struct bufferevent *, void *); void relay_read(struct bufferevent *, void *); void relay_error(struct bufferevent *, short, void *); + +int relay_resolve(struct ctl_relay_event *, + struct protonode *, struct protonode *); int relay_handle_http(struct ctl_relay_event *, - struct protonode *, struct protonode *, int); + struct protonode *, struct protonode *, + struct protonode *, int); void relay_read_http(struct bufferevent *, void *); void relay_read_httpcontent(struct bufferevent *, void *); void relay_read_httpchunks(struct bufferevent *, void *); @@ -273,12 +278,66 @@ relay_shutdown(void) } void +relay_nodedebug(const char *name, struct protonode *pn) +{ + fprintf(stderr, "\t\t"); + fprintf(stderr, "%s ", name); + + switch (pn->type) { + case NODE_TYPE_HEADER: + break; + case NODE_TYPE_URL: + fprintf(stderr, "url "); + break; + case NODE_TYPE_COOKIE: + fprintf(stderr, "cookie "); + break; + case NODE_TYPE_PATH: + fprintf(stderr, "path "); + 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); + break; + case NODE_ACTION_REMOVE: + fprintf(stderr, "remove \"%s\"", + pn->key); + break; + case NODE_ACTION_EXPECT: + fprintf(stderr, "expect \"%s\" from \"%s\"", + pn->value, pn->key); + break; + case NODE_ACTION_FILTER: + fprintf(stderr, "filter \"%s\" from \"%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_NONE: + fprintf(stderr, "none \"%s\"", pn->key); + break; + } + fprintf(stderr, "\n"); +} + +void relay_protodebug(struct relay *rlay) { - struct protocol *proto = rlay->proto; - struct protonode *pn; - struct proto_tree *tree; - const char *name; + struct protocol *proto = rlay->proto; + struct protonode *proot, *pn; + struct proto_tree *tree; + const char *name; fprintf(stderr, "protocol %d: name %s\n", proto->id, proto->name); fprintf(stderr, "\tflags: 0x%04x\n", proto->flags); @@ -300,57 +359,9 @@ relay_protodebug(struct relay *rlay) name = "request"; tree = &proto->request_tree; show: - RB_FOREACH(pn, proto_tree, tree) { - fprintf(stderr, "\t\t"); - - fprintf(stderr, "%s ", name); - - switch (pn->type) { - case NODE_TYPE_HEADER: - break; - case NODE_TYPE_URL: - fprintf(stderr, "url "); - break; - case NODE_TYPE_COOKIE: - fprintf(stderr, "cookie "); - break; - case NODE_TYPE_PATH: - fprintf(stderr, "path "); - 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); - break; - case NODE_ACTION_REMOVE: - fprintf(stderr, "remove \"%s\"", - pn->key); - break; - case NODE_ACTION_EXPECT: - fprintf(stderr, "expect \"%s\" from \"%s\"", - pn->value, pn->key); - break; - case NODE_ACTION_FILTER: - fprintf(stderr, "filter \"%s\" from \"%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_NONE: - fprintf(stderr, "none \"%s\"", pn->key); - break; - } - fprintf(stderr, "\n"); + RB_FOREACH(proot, proto_tree, tree) { + PROTONODE_FOREACH(pn, proot, entry) + relay_nodedebug(name, pn); } if (tree == &proto->request_tree) { name = "response"; @@ -809,6 +820,76 @@ relay_read(struct bufferevent *bev, void *arg) relay_close(con, strerror(errno)); } +int +relay_resolve(struct ctl_relay_event *cre, + struct protonode *proot, struct protonode *pn) +{ + struct session *con = (struct session *)cre->con; + char buf[READ_BUF_SIZE], *ptr; + + switch (pn->action) { + case NODE_ACTION_FILTER: + if (!cre->nodes[pn->id]) + return (0); + cre->nodes[pn->id] = 0; + break; + case NODE_ACTION_EXPECT: + if (proot == pn) + cre->nodes[proot->id]--; + if (cre->nodes[proot->id]) { + if (SIMPLEQ_NEXT(pn, entry) == NULL) + cre->nodes[proot->id] = 0; + return (0); + } + break; + default: + if (cre->nodes[pn->id]) { + cre->nodes[pn->id] = 0; + return (0); + } + break; + } + switch (pn->action) { + case NODE_ACTION_APPEND: + case NODE_ACTION_CHANGE: + ptr = pn->value; + if ((pn->flags & PNFLAG_MARK) && + cre->marked == 0) + break; + 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, ptr) == -1 || + relay_bufferevent_print(cre->dst, "\r\n") == -1) { + relay_close(con, "failed to modify header (done)"); + return (-1); + } + DPRINTF("relay_resolve: add '%s: %s'", + pn->key, ptr); + break; + case NODE_ACTION_EXPECT: + if (pn->flags & PNFLAG_MARK) + break; + DPRINTF("relay_resolve: missing '%s: %s'", + pn->key, pn->value); + relay_close(con, "incomplete header (done)"); + return (-1); + case NODE_ACTION_FILTER: + if (pn->flags & PNFLAG_MARK) + break; + DPRINTF("relay_resolve: filtered '%s: %s'", + pn->key, pn->value); + relay_close(con, "rejecting header (done)"); + return (-1); + default: + break; + } + return (0); +} + char * relay_expand_http(struct ctl_relay_event *cre, char *val, char *buf, size_t len) { @@ -861,8 +942,8 @@ relay_expand_http(struct ctl_relay_event *cre, char *val, char *buf, size_t len) int -relay_handle_http(struct ctl_relay_event *cre, struct protonode *pn, - struct protonode *pk, int header) +relay_handle_http(struct ctl_relay_event *cre, struct protonode *proot, + struct protonode *pn, struct protonode *pk, int header) { struct session *con = (struct session *)cre->con; char buf[READ_BUF_SIZE], *ptr; @@ -896,6 +977,14 @@ relay_handle_http(struct ctl_relay_event *cre, struct protonode *pn, pk->key, pk->value); break; case NODE_ACTION_EXPECT: + /* + * A client may specify the header line for multiple times + * trying to circumvent the filter. + */ + if (cre->nodes[proot->id]) { + relay_close(con, "repeated header line (done)"); + return (PN_FAIL); + } ret = PN_PASS; /* FALLTHROUGH */ case NODE_ACTION_FILTER: @@ -905,8 +994,10 @@ relay_handle_http(struct ctl_relay_event *cre, struct protonode *pn, if (fnmatch(pn->value, pk->value, FNM_CASEFOLD) == 0) { if (pn->flags & PNFLAG_MARK) cre->marked++; - cre->nodes[pn->id] = 1; + cre->nodes[proot->id] = 1; } + if (SIMPLEQ_NEXT(pn, entry) == NULL) + cre->nodes[proot->id]++; break; case NODE_ACTION_HASH: if ((pn->flags & PNFLAG_MARK) && cre->marked == 0) @@ -1080,9 +1171,9 @@ relay_read_http(struct bufferevent *bev, void *arg) struct relay *rlay = (struct relay *)con->relay; struct protocol *proto = rlay->proto; struct evbuffer *src = EVBUFFER_INPUT(bev); - struct protonode *pn, pk, *pnv, pkv; - char *line, buf[READ_BUF_SIZE], *ptr, *val; - int header = 0, ret; + struct protonode *pn, pk, *proot, *pnv = NULL, pkv; + char *line, *ptr, *val; + int header = 0, ret, pass = 0; const char *errstr; size_t size; @@ -1116,6 +1207,11 @@ relay_read_http(struct bufferevent *bev, void *arg) } else pk.value = strchr(pk.key, ':'); if (pk.value == NULL || strlen(pk.value) < 3) { + if (cre->line == 1) { + free(line); + goto fail; + } + DPRINTF("relay_read_http: request '%s'", line); /* Append line to the output buffer */ if (relay_bufferevent_print(cre->dst, line) == -1 || @@ -1176,6 +1272,7 @@ relay_read_http(struct bufferevent *bev, void *arg) if (cre->args != NULL) *cre->args++ = '\0'; #ifdef DEBUG + char buf[BUFSIZ]; if (snprintf(buf, sizeof(buf), " \"%s\"", cre->path) == -1 || evbuffer_add(con->log, buf, strlen(buf)) == -1) { @@ -1197,14 +1294,17 @@ relay_read_http(struct bufferevent *bev, void *arg) DPRINTF("relay_read_http: " "lookup path '%s: %s'", pkv.key, pkv.value); - if ((pnv = RB_FIND(proto_tree, + if ((proot = RB_FIND(proto_tree, cre->tree, &pkv)) == NULL) goto lookup; - ret = relay_handle_http(cre, pnv, &pkv, 0); - if (ret == PN_FAIL) { - free(line); - goto fail; + PROTONODE_FOREACH(pnv, proot, entry) { + ret = relay_handle_http(cre, proot, + pnv, &pkv, 0); + if (ret == PN_FAIL) { + free(line); + return; + } } } else if ((cre->method == HTTP_METHOD_POST || cre->method == HTTP_METHOD_PUT || @@ -1254,16 +1354,17 @@ relay_read_http(struct bufferevent *bev, void *arg) continue; *pkv.value++ = '\0'; - if ((pnv = RB_FIND(proto_tree, + if ((proot = RB_FIND(proto_tree, cre->tree, &pkv)) == NULL) continue; - ret = relay_handle_http(cre, pnv, &pkv, 0); - if (ret == PN_PASS) - continue; - else if (ret == PN_FAIL) { - free(val); - free(line); - return; + PROTONODE_FOREACH(pnv, proot, entry) { + ret = relay_handle_http(cre, proot, + pnv, &pkv, 0); + if (ret == PN_FAIL) { + free(val); + free(line); + return; + } } } free(val); @@ -1299,97 +1400,53 @@ relay_read_http(struct bufferevent *bev, void *arg) if (pkv.value[strlen(pkv.value) - 1] == '"') pkv.value[strlen(pkv.value) - 1] = '\0'; - if ((pnv = RB_FIND(proto_tree, + if ((proot = RB_FIND(proto_tree, cre->tree, &pkv)) == NULL) continue; - ret = relay_handle_http(cre, pnv, &pkv, 0); - if (ret == PN_PASS) - continue; - else if (ret == PN_FAIL) { - free(val); - free(line); - return; + PROTONODE_FOREACH(pnv, proot, entry) { + ret = relay_handle_http(cre, proot, + pnv, &pkv, 0); + if (ret == PN_FAIL) { + free(val); + free(line); + return; + } } } free(val); } handle: - ret = relay_handle_http(cre, pn, &pk, header); - if (ret == PN_PASS) - goto next; - free(line); - if (ret == PN_FAIL) - return; - continue; + 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) { + free(line); + return; + } + } + 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) { - free(line); - goto fail; + 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) { + free(line); + goto fail; + } } free(line); continue; } if (cre->done) { - RB_FOREACH(pn, proto_tree, cre->tree) { - switch (pn->action) { - case NODE_ACTION_FILTER: - if (!cre->nodes[pn->id]) - continue; - cre->nodes[pn->id] = 0; - break; - default: - if (cre->nodes[pn->id]) { - cre->nodes[pn->id] = 0; - continue; - } - break; - } - switch (pn->action) { - case NODE_ACTION_APPEND: - case NODE_ACTION_CHANGE: - ptr = pn->value; - if ((pn->flags & PNFLAG_MARK) && - cre->marked == 0) - break; - 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, - ptr) == -1 || - relay_bufferevent_print(cre->dst, - "\r\n") == -1) - goto fail; - DPRINTF("relay_read_http: add '%s: %s'", - pn->key, ptr); - break; - case NODE_ACTION_EXPECT: - if (pn->flags & PNFLAG_MARK) - break; - DPRINTF("relay_read_http: missing '%s: %s'", - pn->key, pn->value); - relay_close(con, "incomplete header (done)"); - return; - case NODE_ACTION_FILTER: - if (pn->flags & PNFLAG_MARK) - break; - DPRINTF("relay_read_http: filtered '%s: %s'", - pn->key, pn->value); - relay_close(con, "rejecting header (done)"); - return; - default: - break; - } + RB_FOREACH(proot, proto_tree, cre->tree) { + PROTONODE_FOREACH(pn, proot, entry) + if (relay_resolve(cre, proot, pn) != 0) + return; } switch (cre->method) { diff --git a/usr.sbin/relayd/relayd.c b/usr.sbin/relayd/relayd.c index 82b31811c15..8c1dc61e355 100644 --- a/usr.sbin/relayd/relayd.c +++ b/usr.sbin/relayd/relayd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.c,v 1.52 2007/11/19 11:39:49 reyk Exp $ */ +/* $OpenBSD: relayd.c,v 1.53 2007/11/19 14:48:19 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -510,7 +510,7 @@ purge_config(struct hoststated *env, u_int8_t what) void purge_tree(struct proto_tree *tree) { - struct protonode *proot; + struct protonode *proot, *pn; while ((proot = RB_ROOT(tree)) != NULL) { RB_REMOVE(proto_tree, tree, proot); @@ -518,6 +518,14 @@ purge_tree(struct proto_tree *tree) 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); + free(pn); + } free(proot); } } diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h index f4fba6829de..1a4ec09e124 100644 --- a/usr.sbin/relayd/relayd.h +++ b/usr.sbin/relayd/relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.72 2007/11/14 10:59:01 pyr Exp $ */ +/* $OpenBSD: relayd.h,v 1.73 2007/11/19 14:48:19 reyk Exp $ */ /* * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -442,17 +442,23 @@ enum noderesult { }; struct protonode { - objid_t id; - char *key; - enum nodeaction action; - char *value; - u_int8_t flags; - enum nodetype type; + objid_t id; + char *key; + enum nodeaction action; + char *value; + u_int8_t flags; + enum nodetype type; - RB_ENTRY(protonode) nodes; + SIMPLEQ_HEAD(, protonode) head; + SIMPLEQ_ENTRY(protonode) entry; + + RB_ENTRY(protonode) nodes; }; 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, |