diff -Nur haproxy-1.3.17/include/proto/proto_tcp.h haproxy-1.3.17-tcp-inject/include/proto/proto_tcp.h --- haproxy-1.3.17/include/proto/proto_tcp.h 2009-03-29 15:26:57.000000000 +0200 +++ haproxy-1.3.17-tcp-inject/include/proto/proto_tcp.h 2009-04-17 02:37:44.000000000 +0200 @@ -33,6 +33,7 @@ void tcpv6_add_listener(struct listener *listener); int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen); int tcp_inspect_request(struct session *s, struct buffer *req); +int tcp_inject_request(struct session *s, struct buffer *req); #endif /* _PROTO_PROTO_TCP_H */ diff -Nur haproxy-1.3.17/include/types/buffers.h haproxy-1.3.17-tcp-inject/include/types/buffers.h --- haproxy-1.3.17/include/types/buffers.h 2009-03-29 15:26:57.000000000 +0200 +++ haproxy-1.3.17-tcp-inject/include/types/buffers.h 2009-04-17 02:37:44.000000000 +0200 @@ -111,6 +111,7 @@ #define AN_REQ_HTTP_TARPIT 0x00000008 /* wait for end of HTTP tarpit */ #define AN_RTR_HTTP_HDR 0x00000010 /* inspect HTTP response headers */ #define AN_REQ_UNIX_STATS 0x00000020 /* process unix stats socket request */ +#define AN_REQ_INJECT 0x00000040 /* inject header lines into tcp session */ /* describes a chunk of string */ struct chunk { diff -Nur haproxy-1.3.17/src/cfgparse.c haproxy-1.3.17-tcp-inject/src/cfgparse.c --- haproxy-1.3.17/src/cfgparse.c 2009-03-29 15:26:57.000000000 +0200 +++ haproxy-1.3.17-tcp-inject/src/cfgparse.c 2009-04-17 04:12:18.000000000 +0200 @@ -1604,6 +1604,78 @@ } } } + else if (!strcmp(args[1], "inject")) { + int cur_arg; + + /* inject x-forwarded-for field at the beginning of tcp session. */ + curproxy->options |= PR_O_FWDFOR; + + free(curproxy->fwdfor_hdr_name); + curproxy->fwdfor_hdr_name = strdup(DEF_XFORWARDFOR_HDR); + curproxy->fwdfor_hdr_len = strlen(DEF_XFORWARDFOR_HDR); + + /* next argument must be "req" or "rep" */ + if (!strcmp(args[2], "req")) { + curproxy->listen->analysers |= AN_REQ_INJECT; + } else if (!strcmp(args[2], "rsp")) { +// TODO: implement it + Alert("parsing [%s:%d] : '%s %s %s' not yet implemented.\n", + file, linenum, args[0], args[1], args[2]); + return -1; + } else { + Alert("parsing [%s:%d] : '%s %s' expects 'req' or 'rsp' as argument.\n", + file, linenum, args[0], args[1]); + return -1; + } + + /* next argument is the inject order. */ +// TODO: implement it atol(args[3]); + + /* check what data should be injected. */ + if (!strcmp(args[4], "forwardfor")) { + + /* loop to go through arguments - start at 2, since 0+1 = "option" "inject" */ + cur_arg = 5; + while (*(args[cur_arg])) { + if (!strcmp(args[cur_arg], "except")) { + /* suboption except - needs additional argument for it */ + if (!*(args[cur_arg+1]) || !str2net(args[cur_arg+1], &curproxy->except_net, &curproxy->except_mask)) { + Alert("parsing [%s:%d] : '%s %s %s %s %s %s' expects
[/mask] as argument.\n", + file, linenum, args[0], args[1], args[2], args[3], args[4], args[cur_arg]); + return -1; + } + /* flush useless bits */ + curproxy->except_net.s_addr &= curproxy->except_mask.s_addr; + cur_arg += 2; + } else if (!strcmp(args[cur_arg], "header")) { + /* suboption header - needs additional argument for it */ + if (*(args[cur_arg+1]) == 0) { + Alert("parsing [%s:%d] : '%s %s %s %s %s %s' expects as argument.\n", + file, linenum, args[0], args[1], args[2], args[3], args[4], args[cur_arg]); + return -1; + } + free(curproxy->fwdfor_hdr_name); + curproxy->fwdfor_hdr_name = strdup(args[cur_arg+1]); + curproxy->fwdfor_hdr_len = strlen(curproxy->fwdfor_hdr_name); + cur_arg += 2; + } else { + /* unknown suboption - catchall */ + Alert("parsing [%s:%d] : '%s %s' only supports optional values: 'except' and 'header'.\n", + file, linenum, args[0], args[1]); + return -1; + } + } /* end while loop */ + } else if (!strcmp(args[4], "data")) { +// TODO: implement it + Alert("parsing [%s:%d] : '%s %s %s %s %s' not yet implemented.\n", + file, linenum, args[0], args[1], args[2], args[3], args[4]); + return -1; + } else { + Alert("parsing [%s:%d] : '%s %s %s %s' expects 'forwardfor' or 'data' as argument.\n", + file, linenum, args[0], args[1], args[2], args[3]); + return -1; + } + } else if (!strcmp(args[1], "forwardfor")) { int cur_arg; diff -Nur haproxy-1.3.17/src/proto_tcp.c haproxy-1.3.17-tcp-inject/src/proto_tcp.c --- haproxy-1.3.17/src/proto_tcp.c 2009-03-29 15:26:57.000000000 +0200 +++ haproxy-1.3.17-tcp-inject/src/proto_tcp.c 2009-04-17 02:37:44.000000000 +0200 @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -428,6 +429,136 @@ return 1; } +/* Add some data in the current server TCP session. This makes a backend + * behind us able to parse them and make decisions based on it. + */ +int tcp_inject_request(struct session *s, struct buffer *req) +{ + + /* position of buffer. */ + int buf_pos = 0; + + DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n", + now_ms, __FUNCTION__, + s, + req, + req->rex, req->wex, + req->flags, + req->l, + req->analysers); + + /* FIXME: I have no real idea how the timeouts are handled here, + * so is it required to add some handler here? + */ + + /* 1: add X-Forward-For if either the frontend or the backend asks for it. (IPv4) */ + if (((s->fe->options | s->be->options) & PR_O_FWDFOR) != 0 && s->cli_addr.ss_family == AF_INET) { + + /* add an X-Forward-For header unless the source ip is in the 'except' network range. */ + if (((s->fe->except_mask.s_addr == 0) || (((struct sockaddr_in *)&s->cli_addr)->sin_addr.s_addr & s->fe->except_mask.s_addr) != s->fe->except_net.s_addr) && + ((s->be->except_mask.s_addr == 0) || (((struct sockaddr_in *)&s->cli_addr)->sin_addr.s_addr & s->be->except_mask.s_addr) != s->be->except_net.s_addr)) { + + /* variables to build the header entry. */ + int len; + unsigned char *pn; + pn = (unsigned char *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr; + + /* Note: we rely on the backend to get the header name to be used for + * X-Forward-For, because the header is really meant for the backends. + * However, if the backend did not specify any option, we have to rely + * on the frontend's header name. + */ + if (s->be->fwdfor_hdr_len) { + len = s->be->fwdfor_hdr_len; + memcpy(trash, s->be->fwdfor_hdr_name, len); + } else { + len = s->fe->fwdfor_hdr_len; + memcpy(trash, s->fe->fwdfor_hdr_name, len); + } + + /* fetch length of complete header. */ + len += sprintf(trash + len, ": %d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]); + + /* copy the header into connection. */ + if ((buf_pos = buffer_insert_line2(req, req->data, trash, len)) < 0) { + + /* marks the buffer as "shutdown" ASAP in both directions. */ + buffer_abort(req); + buffer_abort(s->rep); + req->analysers = 0; + s->fe->failed_req++; + + if ((s->flags & SN_ERR_MASK) == 0) { + s->flags |= SN_ERR_PRXCOND; + } + + if ((s->flags & SN_FINST_MASK) == 0) { + s->flags |= SN_FINST_R; + } + + /* error occured. */ + return 0; + } + } + } + + /* IPv6. */ + else if (((s->fe->options | s->be->options) & PR_O_FWDFOR) != 0 && s->cli_addr.ss_family == AF_INET6) { + + /* FIXME: for the sake of completeness, we should also support + * 'except' here, although it is mostly useless in this case. + */ + + /* variables to build the header entry. */ + int len; + char pn[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, (const void *)&((struct sockaddr_in6 *)(&s->cli_addr))->sin6_addr, pn, sizeof(pn)); + + /* Note: we rely on the backend to get the header name to be used for + * X-Forward-For, because the header is really meant for the backends. + * However, if the backend did not specify any option, we have to rely + * on the frontend's header name. + */ + if (s->be->fwdfor_hdr_len) { + len = s->be->fwdfor_hdr_len; + memcpy(trash, s->be->fwdfor_hdr_name, len); + } else { + len = s->fe->fwdfor_hdr_len; + memcpy(trash, s->fe->fwdfor_hdr_name, len); + } + + /* fetch length of complete header. */ + len += sprintf(trash + len, ": %s", pn); + + /* copy the header into connection. */ + if ((buf_pos = buffer_insert_line2(req, req->data, trash, len)) < 0) { + + /* marks the buffer as "shutdown" ASAP in both directions. */ + buffer_abort(req); + buffer_abort(s->rep); + req->analysers = 0; + s->fe->failed_req++; + + if ((s->flags & SN_ERR_MASK) == 0) { + s->flags |= SN_ERR_PRXCOND; + } + + if ((s->flags & SN_FINST_MASK) == 0) { + s->flags |= SN_FINST_R; + } + + /* error occured. */ + return 0; + } + } + + /* if we get there, it means we have no rule which matches, or + * we have an explicit accept, so we apply the default accept. + */ + req->analysers &= ~AN_REQ_INJECT; + req->analyse_exp = TICK_ETERNITY; + return 1; +} /* This function should be called to parse a line starting with the "tcp-request" * keyword. diff -Nur haproxy-1.3.17/src/session.c haproxy-1.3.17-tcp-inject/src/session.c --- haproxy-1.3.17/src/session.c 2009-03-29 15:26:57.000000000 +0200 +++ haproxy-1.3.17-tcp-inject/src/session.c 2009-04-17 02:37:44.000000000 +0200 @@ -755,8 +755,12 @@ if (!http_process_request_body(s, s->req)) break; + if (s->req->analysers & AN_REQ_INJECT) + if (!tcp_inject_request(s, s->req)) + break; + /* Just make sure that nobody set a wrong flag causing an endless loop */ - s->req->analysers &= AN_REQ_INSPECT | AN_REQ_HTTP_HDR | AN_REQ_HTTP_TARPIT | AN_REQ_HTTP_BODY; + s->req->analysers &= AN_REQ_INSPECT | AN_REQ_HTTP_HDR | AN_REQ_HTTP_TARPIT | AN_REQ_HTTP_BODY | AN_REQ_INJECT; /* we don't want to loop anyway */ break;