From: Aurelien DARRAGON Date: Mon, 9 Jan 2023 10:09:03 +0000 (+0100) Subject: MEDIUM: proxy/http_ext: implement dynamic http_ext X-Git-Tag: v2.8-dev3~62 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b2e2ec51b36f99369c1f32cbdf4698cdb9b18264;p=thirdparty%2Fhaproxy.git MEDIUM: proxy/http_ext: implement dynamic http_ext proxy http-only options implemented in http_ext were statically stored within proxy struct. We're making some changes so that http_ext are now stored in a dynamically allocated structs. http_ext related structs are only allocated when needed to save some space whenever possible, and they are automatically freed upon proxy deletion. Related PX_O_HTTP{7239,XFF,XOT) option flags were removed because we're now considering an http_ext option as 'active' if it is allocated (ptr is not NULL) A few checks (and BUG_ON) were added to make these changes safe because it adds some (acceptable) complexity to the previous design. Also, proxy.http was renamed to proxy.http_ext to make things more explicit. --- diff --git a/include/haproxy/http_ext-t.h b/include/haproxy/http_ext-t.h index ab83b35441..68eb047e28 100644 --- a/include/haproxy/http_ext-t.h +++ b/include/haproxy/http_ext-t.h @@ -132,4 +132,18 @@ struct http_ext_xot { struct net_addr except_net; /* don't forward x-original-to for this address. */ }; +/* http_ext options */ +struct http_ext { + /* forwarded header (RFC 7239) */ + struct http_ext_7239 *fwd; + /* x-forward-for: + * conditionally insert x-forwarded-for with client address + */ + struct http_ext_xff *xff; + /* x-original-to: + * insert x-original-to with destination address + */ + struct http_ext_xot *xot; +}; + #endif /* !_HAPROXY_HTTPEXT_T_H */ diff --git a/include/haproxy/http_ext.h b/include/haproxy/http_ext.h index 63ecafb2b7..53764a26c0 100644 --- a/include/haproxy/http_ext.h +++ b/include/haproxy/http_ext.h @@ -33,17 +33,26 @@ int http_handle_7239_header(struct stream *s, struct channel *req); int http_handle_xff_header(struct stream *s, struct channel *req); int http_handle_xot_header(struct stream *s, struct channel *req); -void http_ext_7239_clean(struct http_ext_7239 *); -void http_ext_xff_clean(struct http_ext_xff *); -void http_ext_xot_clean(struct http_ext_xot *); - -void http_ext_7239_copy(struct http_ext_7239 *dest, const struct http_ext_7239 *orig); -void http_ext_xff_copy(struct http_ext_xff *dest, const struct http_ext_xff *orig); -void http_ext_xot_copy(struct http_ext_xot *dest, const struct http_ext_xot *orig); - int proxy_http_parse_7239(char **args, int cur_arg, struct proxy *curproxy, const struct proxy *defpx, const char *file, int linenum); int proxy_http_compile_7239(struct proxy *curproxy); int proxy_http_parse_xff(char **args, int cur_arg, struct proxy *curproxy, const struct proxy *defpx, const char *file, int linenum); int proxy_http_parse_xot(char **args, int cur_arg, struct proxy *curproxy, const struct proxy *defpx, const char *file, int linenum); +int http_ext_7239_prepare(struct proxy *cur); +int http_ext_xff_prepare(struct proxy *cur); +int http_ext_xot_prepare(struct proxy *cur); + +void http_ext_7239_dup(const struct proxy *def, struct proxy *cpy); +void http_ext_xff_dup(const struct proxy *def, struct proxy *cpy); +void http_ext_xot_dup(const struct proxy *def, struct proxy *cpy); + +void http_ext_7239_clean(struct proxy *cur); +void http_ext_xff_clean(struct proxy *cur); +void http_ext_xot_clean(struct proxy *cur); + +int http_ext_prepare(struct proxy *cur); +void http_ext_dup(const struct proxy *def, struct proxy *cpy); +void http_ext_clean(struct proxy *cur); +void http_ext_softclean(struct proxy *cur); + #endif /* !_HAPROXY_HTTPEXT_H */ diff --git a/include/haproxy/proxy-t.h b/include/haproxy/proxy-t.h index 8391f9fbc3..3db2d3e188 100644 --- a/include/haproxy/proxy-t.h +++ b/include/haproxy/proxy-t.h @@ -88,7 +88,7 @@ enum PR_SRV_STATE_FILE { #define PR_O_PREF_LAST 0x00000020 /* prefer last server */ #define PR_O_DISPATCH 0x00000040 /* use dispatch mode */ #define PR_O_FORCED_ID 0x00000080 /* proxy's ID was forced in the configuration */ -#define PR_O_HTTP_XFF 0x00000100 /* conditionally insert x-forwarded-for with client address */ +/* unused: 0x00000100 */ #define PR_O_IGNORE_PRB 0x00000200 /* ignore empty requests (aborts and timeouts) */ #define PR_O_NULLNOLOG 0x00000400 /* a connect without request will not be logged */ #define PR_O_WREQ_BODY 0x00000800 /* always wait for the HTTP request body */ @@ -101,7 +101,7 @@ enum PR_SRV_STATE_FILE { #define PR_O_TCP_CLI_KA 0x00040000 /* enable TCP keep-alive on client-side streams */ #define PR_O_TCP_SRV_KA 0x00080000 /* enable TCP keep-alive on server-side streams */ #define PR_O_USE_ALL_BK 0x00100000 /* load-balance between backup servers */ -#define PR_O_HTTP_7239 0x00200000 /* insert 7239 forwarded header */ +/* unused: 0x00200000 */ #define PR_O_TCP_NOLING 0x00400000 /* disable lingering on client and server connections */ #define PR_O_ABRT_CLOSE 0x00800000 /* immediately abort request when client closes */ @@ -115,7 +115,7 @@ enum PR_SRV_STATE_FILE { #define PR_O_CONTSTATS 0x10000000 /* continuous counters */ /* unused: 0x20000000 */ #define PR_O_DISABLE404 0x40000000 /* Disable a server on a 404 response to a health-check */ -#define PR_O_HTTP_XOT 0x80000000 /* insert x-original-to with destination address */ +/* unused: 0x80000000 */ /* bits for proxy->options2 */ #define PR_O2_SPLIC_REQ 0x00000001 /* transfer requests using linux kernel's splice() */ @@ -267,16 +267,6 @@ struct error_snapshot { char buf[VAR_ARRAY]; /* copy of the beginning of the message for bufsize bytes */ }; -/* http options */ -struct proxy_http { - /* forwarded header (RFC 7239) */ - struct http_ext_7239 fwd; - /* x-forward-for */ - struct http_ext_xff xff; - /* x-original-to */ - struct http_ext_xot xot; -}; - struct proxy { enum obj_type obj_type; /* object type == OBJ_TYPE_PROXY */ char flags; /* bit field PR_FL_* */ @@ -448,7 +438,7 @@ struct proxy { char *elfs_file; int elfs_line; } conf; /* config information */ - struct proxy_http http; /* http only options */ + struct http_ext *http_ext; /* http ext options */ struct eb_root used_server_addr; /* list of server addresses in use */ void *parent; /* parent of the proxy when applicable */ struct comp *comp; /* http compression */ diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index 20965c8774..485b0b36c3 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -2038,7 +2038,8 @@ stats_error_parsing: goto out; } else if (kwm == KWM_NO) { - curproxy->options &= ~PR_O_HTTP_7239; + if (curproxy->http_ext) + http_ext_7239_clean(curproxy); goto out; } } diff --git a/src/cfgparse.c b/src/cfgparse.c index ecb75a73af..c207c6e01a 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -3670,8 +3670,9 @@ out_uri_auth_compat: else curproxy->http_needed |= !!(curproxy->lbprm.expr->fetch->use & SMP_USE_HTTP_ANY); } + /* option "forwarded" may need to compile its expressions */ - if ((curproxy->mode == PR_MODE_HTTP) && curproxy->options & PR_O_HTTP_7239) + if ((curproxy->mode == PR_MODE_HTTP) && curproxy->http_ext && curproxy->http_ext->fwd) cfgerr += proxy_http_compile_7239(curproxy); /* only now we can check if some args remain unresolved. @@ -3968,25 +3969,26 @@ out_uri_auth_compat: err_code |= ERR_WARN; } - if (curproxy->options & PR_O_HTTP_7239) { - ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n", - "forwarded", proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - curproxy->options &= ~PR_O_HTTP_7239; - } - - if (curproxy->options & PR_O_HTTP_XFF) { - ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n", - "forwardfor", proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - curproxy->options &= ~PR_O_HTTP_XFF; - } - - if (curproxy->options & PR_O_HTTP_XOT) { - ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n", - "originalto", proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - curproxy->options &= ~PR_O_HTTP_XOT; + if (curproxy->http_ext) { + /* consistency checks for http_ext */ + if (curproxy->http_ext->fwd) { + ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n", + "forwarded", proxy_type_str(curproxy), curproxy->id); + err_code |= ERR_WARN; + http_ext_7239_clean(curproxy); + } + if (curproxy->http_ext->xff) { + ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n", + "forwardfor", proxy_type_str(curproxy), curproxy->id); + err_code |= ERR_WARN; + http_ext_xff_clean(curproxy); + } + if (curproxy->http_ext->xot) { + ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n", + "originalto", proxy_type_str(curproxy), curproxy->id); + err_code |= ERR_WARN; + http_ext_xot_clean(curproxy); + } } for (optnum = 0; cfg_opts[optnum].name; optnum++) { @@ -4221,6 +4223,8 @@ out_uri_auth_compat: rules->flags = 0; } } + /* http_ext post init early cleanup */ + http_ext_softclean(curproxy); } /* diff --git a/src/http_ana.c b/src/http_ana.c index fe5044e068..4f1decdbbd 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -662,29 +662,13 @@ int http_process_request(struct stream *s, struct channel *req, int an_bit) goto return_fail_rewrite; } - /* add forwarded header (RFC 7239) (ignored for frontends) */ - if (s->be->options & PR_O_HTTP_7239) { - if (unlikely(!http_handle_7239_header(s, req))) - goto return_fail_rewrite; - } - - /* - * add X-Forwarded-For if either the frontend or the backend - * asks for it. - */ - if ((sess->fe->options | s->be->options) & PR_O_HTTP_XFF) { - if (unlikely(!http_handle_xff_header(s, req))) - goto return_fail_rewrite; - } - - /* - * add X-Original-To if either the frontend or the backend - * asks for it. - */ - if ((sess->fe->options | s->be->options) & PR_O_HTTP_XOT) { - if (unlikely(!http_handle_xot_header(s, req))) - goto return_fail_rewrite; - } + /* handle http extensions (if configured) */ + if (unlikely(!http_handle_7239_header(s, req))) + goto return_fail_rewrite; + if (unlikely(!http_handle_xff_header(s, req))) + goto return_fail_rewrite; + if (unlikely(!http_handle_xot_header(s, req))) + goto return_fail_rewrite; /* Filter the request headers if there are filters attached to the * stream. diff --git a/src/http_ext.c b/src/http_ext.c index f2e3781c59..0dcafc5dc6 100644 --- a/src/http_ext.c +++ b/src/http_ext.c @@ -611,7 +611,7 @@ static inline void http_build_7239_header_node(struct buffer *out, if (forby->np_mode) chunk_appendf(out, "\""); offset_save = out->data; - http_build_7239_header_node(out, s, curproxy, addr, &curproxy->http.fwd.p_by); + http_build_7239_header_nodename(out, s, curproxy, addr, forby); if (offset_save == out->data) { /* could not build nodename, either because some * data is not available or user is providing bad input @@ -621,7 +621,7 @@ static inline void http_build_7239_header_node(struct buffer *out, if (forby->np_mode) { chunk_appendf(out, ":"); offset_save = out->data; - http_build_7239_header_nodeport(out, s, curproxy, addr, &curproxy->http.fwd.p_by); + http_build_7239_header_nodeport(out, s, curproxy, addr, forby); if (offset_save == out->data) { /* could not build nodeport, either because some data is * not available or user is providing bad input @@ -682,31 +682,30 @@ static int http_build_7239_header(struct buffer *out, { struct connection *cli_conn = objt_conn(strm_sess(s)->origin); - if (curproxy->http.fwd.p_proto) { + if (curproxy->http_ext->fwd->p_proto) { chunk_appendf(out, "%sproto=%s", ((out->data) ? ";" : ""), ((conn_is_ssl(cli_conn)) ? "https" : "http")); } - if (curproxy->http.fwd.p_host.mode) { + if (curproxy->http_ext->fwd->p_host.mode) { /* always add quotes for host parameter to make output compliancy checks simpler */ chunk_appendf(out, "%shost=\"", ((out->data) ? ";" : "")); /* ignore return value for now, but could be useful some day */ - http_build_7239_header_host(out, s, curproxy, htx, - &curproxy->http.fwd.p_host); + http_build_7239_header_host(out, s, curproxy, htx, &curproxy->http_ext->fwd->p_host); chunk_appendf(out, "\""); } - if (curproxy->http.fwd.p_by.nn_mode) { + if (curproxy->http_ext->fwd->p_by.nn_mode) { const struct sockaddr_storage *dst = sc_dst(s->scf); chunk_appendf(out, "%sby=", ((out->data) ? ";" : "")); - http_build_7239_header_node(out, s, curproxy, dst, &curproxy->http.fwd.p_by); + http_build_7239_header_node(out, s, curproxy, dst, &curproxy->http_ext->fwd->p_by); } - if (curproxy->http.fwd.p_for.nn_mode) { + if (curproxy->http_ext->fwd->p_for.nn_mode) { const struct sockaddr_storage *src = sc_src(s->scf); chunk_appendf(out, "%sfor=", ((out->data) ? ";" : "")); - http_build_7239_header_node(out, s, curproxy, src, &curproxy->http.fwd.p_for); + http_build_7239_header_node(out, s, curproxy, src, &curproxy->http_ext->fwd->p_for); } if (unlikely(out->data == out->size)) { /* not enough space in buffer, error */ @@ -715,177 +714,196 @@ static int http_build_7239_header(struct buffer *out, return 1; } -/* This function will try to inject 7239 forwarded header +/* This function will try to inject RFC 7239 forwarded header if + * configured on the backend (ignored for frontends). + * Will do nothing if the option is not enabled on the proxy. * Returns 1 for success and 0 for failure */ int http_handle_7239_header(struct stream *s, struct channel *req) { - struct htx *htx = htxbuf(&req->buf); struct proxy *curproxy = s->be; /* ignore frontend */ - int validate = 1; - struct http_hdr_ctx find = { .blk = NULL }; - struct http_hdr_ctx last = { .blk = NULL}; - struct ist hdr = ist("forwarded"); - BUG_ON(!(curproxy->options & PR_O_HTTP_7239)); /* should not happen */ + if (curproxy->http_ext && curproxy->http_ext->fwd) { + struct htx *htx = htxbuf(&req->buf); + int validate = 1; + struct http_hdr_ctx find = { .blk = NULL }; + struct http_hdr_ctx last = { .blk = NULL}; + struct ist hdr = ist("forwarded"); - /* ok, let's build forwarded header */ - chunk_reset(&trash); - if (unlikely(!http_build_7239_header(&trash, s, curproxy, htx))) - return 0; /* error when building header (bad user conf or memory error) */ + /* ok, let's build forwarded header */ + chunk_reset(&trash); + if (unlikely(!http_build_7239_header(&trash, s, curproxy, htx))) + return 0; /* error when building header (bad user conf or memory error) */ - /* validate existing forwarded header (including multiple values), - * hard stop if error is encountered - */ - while (http_find_header(htx, hdr, &find, 0)) { - /* validate current header chunk */ - if (!http_validate_7239_header(find.value, FORWARDED_HEADER_ALL, NULL)) { - /* at least one error, existing forwarded header not OK, add our own - * forwarded header, so that it can be trusted - */ - validate = 0; - break; + /* validate existing forwarded header (including multiple values), + * hard stop if error is encountered + */ + while (http_find_header(htx, hdr, &find, 0)) { + /* validate current header chunk */ + if (!http_validate_7239_header(find.value, FORWARDED_HEADER_ALL, NULL)) { + /* at least one error, existing forwarded header not OK, add our own + * forwarded header, so that it can be trusted + */ + validate = 0; + break; + } + last = find; + } + /* no errors, append our data at the end of existing header */ + if (last.blk && validate) { + if (unlikely(!http_append_header_value(htx, &last, ist2(trash.area, trash.data)))) + return 0; /* htx error */ + } + else { + if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data)))) + return 0; /* htx error */ } - last = find; - } - /* no errors, append our data at the end of existing header */ - if (last.blk && validate) { - if (unlikely(!http_append_header_value(htx, &last, ist2(trash.area, trash.data)))) - return 0; /* htx error */ - } - else { - if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data)))) - return 0; /* htx error */ } return 1; } -/* This function will try to inject x-forwarded-for header if - * configured on the frontend or the backend (or both) +/* + * add X-Forwarded-For if either the frontend or the backend + * asks for it. * Returns 1 for success and 0 for failure */ int http_handle_xff_header(struct stream *s, struct channel *req) { struct session *sess = s->sess; - struct htx *htx = htxbuf(&req->buf); - const struct sockaddr_storage *src = sc_src(s->scf); - struct http_hdr_ctx ctx = { .blk = NULL }; - struct http_ext_xff *f_xff = ((sess->fe->options & PR_O_HTTP_XFF) ? &sess->fe->http.xff : NULL); - struct http_ext_xff *b_xff = ((s->be->options & PR_O_HTTP_XFF) ? &s->be->http.xff : NULL); - struct ist hdr; - - /* xff is expected to be enabled on be, or fe, or both */ - BUG_ON(!f_xff && !b_xff); + struct http_ext_xff *f_xff = NULL; + struct http_ext_xff *b_xff = NULL; - hdr = ((b_xff) ? b_xff->hdr_name : f_xff->hdr_name); - - if (f_xff && f_xff->mode == HTTP_XFF_IFNONE && - b_xff && b_xff->mode == HTTP_XFF_IFNONE && - http_find_header(htx, hdr, &ctx, 0)) { - /* The header is set to be added only if none is present - * and we found it, so don't do anything. - */ + if (sess->fe->http_ext && sess->fe->http_ext->xff) { + /* frontend */ + f_xff = sess->fe->http_ext->xff; } - else if (src && src->ss_family == AF_INET) { - /* Add an X-Forwarded-For header unless the source IP is - * in the 'except' network range. - */ - if ((!f_xff || ipcmp2net(src, &f_xff->except_net)) && - (!b_xff || ipcmp2net(src, &b_xff->except_net))) { - unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)src)->sin_addr; - - /* Note: we rely on the backend to get the header name to be used for - * x-forwarded-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->http_ext && s->be->http_ext->xff) { + /* backend */ + b_xff = s->be->http_ext->xff; + } + + if (f_xff || b_xff) { + struct htx *htx = htxbuf(&req->buf); + const struct sockaddr_storage *src = sc_src(s->scf); + struct http_hdr_ctx ctx = { .blk = NULL }; + struct ist hdr = ((b_xff) ? b_xff->hdr_name : f_xff->hdr_name); + + if (f_xff && f_xff->mode == HTTP_XFF_IFNONE && + b_xff && b_xff->mode == HTTP_XFF_IFNONE && + http_find_header(htx, hdr, &ctx, 0)) { + /* The header is set to be added only if none is present + * and we found it, so don't do anything. */ - chunk_printf(&trash, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]); - if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data)))) - return 0; } - } - else if (src && src->ss_family == AF_INET6) { - /* Add an X-Forwarded-For header unless the source IP is - * in the 'except' network range. - */ - if ((!f_xff || ipcmp2net(src, &f_xff->except_net)) && - (!b_xff || ipcmp2net(src, &b_xff->except_net))) { - char pn[INET6_ADDRSTRLEN]; - - inet_ntop(AF_INET6, - (const void *)&((struct sockaddr_in6 *)(src))->sin6_addr, - pn, sizeof(pn)); - - /* Note: we rely on the backend to get the header name to be used for - * x-forwarded-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. + else if (src && src->ss_family == AF_INET) { + /* Add an X-Forwarded-For header unless the source IP is + * in the 'except' network range. */ - chunk_printf(&trash, "%s", pn); - if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data)))) - return 0; + if ((!f_xff || ipcmp2net(src, &f_xff->except_net)) && + (!b_xff || ipcmp2net(src, &b_xff->except_net))) { + unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)src)->sin_addr; + + /* Note: we rely on the backend to get the header name to be used for + * x-forwarded-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. + */ + chunk_printf(&trash, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]); + if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data)))) + return 0; + } } - } + else if (src && src->ss_family == AF_INET6) { + /* Add an X-Forwarded-For header unless the source IP is + * in the 'except' network range. + */ + if ((!f_xff || ipcmp2net(src, &f_xff->except_net)) && + (!b_xff || ipcmp2net(src, &b_xff->except_net))) { + char pn[INET6_ADDRSTRLEN]; + + inet_ntop(AF_INET6, + (const void *)&((struct sockaddr_in6 *)(src))->sin6_addr, + pn, sizeof(pn)); + /* Note: we rely on the backend to get the header name to be used for + * x-forwarded-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. + */ + chunk_printf(&trash, "%s", pn); + if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data)))) + return 0; + } + } + } return 1; } -/* This function will try to inject x-original-to header if - * configured on the frontend or the backend (or both) +/* + * add X-Original-To if either the frontend or the backend + * asks for it. * Returns 1 for success and 0 for failure */ int http_handle_xot_header(struct stream *s, struct channel *req) { struct session *sess = s->sess; - struct htx *htx = htxbuf(&req->buf); - const struct sockaddr_storage *dst = sc_dst(s->scf); - struct http_ext_xot *f_xot = ((sess->fe->options & PR_O_HTTP_XOT) ? &sess->fe->http.xot : NULL); - struct http_ext_xot *b_xot = ((s->be->options & PR_O_HTTP_XOT) ? &s->be->http.xot : NULL); - struct ist hdr; + struct http_ext_xot *f_xot = NULL; + struct http_ext_xot *b_xot = NULL; - /* xot is expected to be enabled on be, or fe, or both */ - BUG_ON(!f_xot && !b_xot); + if (sess->fe->http_ext && sess->fe->http_ext->xot) { + /* frontend */ + f_xot = sess->fe->http_ext->xot; + } + if (s->be->http_ext && s->be->http_ext->xot) { + /* backend */ + BUG_ON(!s->be->http_ext); + b_xot = s->be->http_ext->xot; + } - hdr = ((b_xot) ? b_xot->hdr_name : f_xot->hdr_name); + if (f_xot || b_xot) { + struct htx *htx = htxbuf(&req->buf); + const struct sockaddr_storage *dst = sc_dst(s->scf); + struct ist hdr = ((b_xot) ? b_xot->hdr_name : f_xot->hdr_name); - if (dst && dst->ss_family == AF_INET) { - /* Add an X-Original-To header unless the destination IP is - * in the 'except' network range. - */ - if ((!f_xot || ipcmp2net(dst, &f_xot->except_net)) && - (!b_xot || ipcmp2net(dst, &b_xot->except_net))) { - unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)dst)->sin_addr; - - /* Note: we rely on the backend to get the header name to be used for - * x-original-to, 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 (dst && dst->ss_family == AF_INET) { + /* Add an X-Original-To header unless the destination IP is + * in the 'except' network range. */ - chunk_printf(&trash, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]); - if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data)))) - return 0; + if ((!f_xot || ipcmp2net(dst, &f_xot->except_net)) && + (!b_xot || ipcmp2net(dst, &b_xot->except_net))) { + unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)dst)->sin_addr; + + /* Note: we rely on the backend to get the header name to be used for + * x-original-to, 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. + */ + chunk_printf(&trash, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]); + if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data)))) + return 0; + } } - } - else if (dst && dst->ss_family == AF_INET6) { - /* Add an X-Original-To header unless the source IP is - * in the 'except' network range. - */ - if ((!f_xot || ipcmp2net(dst, &f_xot->except_net)) && - (!b_xot || ipcmp2net(dst, &b_xot->except_net))) { - char pn[INET6_ADDRSTRLEN]; - - inet_ntop(AF_INET6, - (const void *)&((struct sockaddr_in6 *)dst)->sin6_addr, - pn, sizeof(pn)); - - /* Note: we rely on the backend to get the header name to be used for - * x-forwarded-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. + else if (dst && dst->ss_family == AF_INET6) { + /* Add an X-Original-To header unless the source IP is + * in the 'except' network range. */ - chunk_printf(&trash, "%s", pn); - if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data)))) - return 0; + if ((!f_xot || ipcmp2net(dst, &f_xot->except_net)) && + (!b_xot || ipcmp2net(dst, &b_xot->except_net))) { + char pn[INET6_ADDRSTRLEN]; + + inet_ntop(AF_INET6, + (const void *)&((struct sockaddr_in6 *)dst)->sin6_addr, + pn, sizeof(pn)); + + /* Note: we rely on the backend to get the header name to be used for + * x-forwarded-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. + */ + chunk_printf(&trash, "%s", pn); + if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data)))) + return 0; + } } } return 1; @@ -930,6 +948,7 @@ int proxy_http_parse_7239(char **args, int cur_arg, struct proxy *curproxy, const struct proxy *defpx, const char *file, int linenum) { + struct http_ext_7239 *fwd; int err_code = 0; if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, "option forwarded", NULL)) { @@ -938,73 +957,77 @@ int proxy_http_parse_7239(char **args, int cur_arg, goto out; } - curproxy->options |= PR_O_HTTP_7239; - curproxy->http.fwd.p_proto = 0; - curproxy->http.fwd.p_host.mode = 0; - curproxy->http.fwd.p_for.nn_mode = 0; - curproxy->http.fwd.p_for.np_mode = 0; - curproxy->http.fwd.p_by.nn_mode = 0; - curproxy->http.fwd.p_by.np_mode = 0; - ha_free(&curproxy->http.fwd.c_file); - curproxy->http.fwd.c_file = strdup(file); - curproxy->http.fwd.c_line = linenum; + if (!http_ext_7239_prepare(curproxy)) + return proxy_http_parse_oom(file, linenum); + + fwd = curproxy->http_ext->fwd; + + fwd->p_proto = 0; + fwd->p_host.mode = 0; + fwd->p_for.nn_mode = 0; + fwd->p_for.np_mode = 0; + fwd->p_by.nn_mode = 0; + fwd->p_by.np_mode = 0; + ha_free(&fwd->c_file); + fwd->c_file = strdup(file); + fwd->c_line = linenum; /* start at 2, since 0+1 = "option" "forwarded" */ cur_arg = 2; if (!*(args[cur_arg])) { /* no optional argument provided, use default settings */ - curproxy->http.fwd.p_for.nn_mode = HTTP_7239_FORBY_ORIG; /* enable for and mimic xff */ - curproxy->http.fwd.p_proto = 1; /* enable proto */ + fwd->p_for.nn_mode = HTTP_7239_FORBY_ORIG; /* enable for and mimic xff */ + fwd->p_proto = 1; /* enable proto */ goto out; } /* loop to go through optional arguments */ while (*(args[cur_arg])) { if (strcmp(args[cur_arg], "proto") == 0) { - curproxy->http.fwd.p_proto = 1; + fwd->p_proto = 1; cur_arg += 1; } else if (strcmp(args[cur_arg], "host") == 0) { - curproxy->http.fwd.p_host.mode = HTTP_7239_HOST_ORIG; + fwd->p_host.mode = HTTP_7239_HOST_ORIG; cur_arg += 1; } else if (strcmp(args[cur_arg], "host-expr") == 0) { - curproxy->http.fwd.p_host.mode = HTTP_7239_HOST_SMP; + fwd->p_host.mode = HTTP_7239_HOST_SMP; err_code |= _proxy_http_parse_7239_expr(args, &cur_arg, file, linenum, - &curproxy->http.fwd.p_host.expr_s); + &fwd->p_host.expr_s); if (err_code & ERR_FATAL) goto out; } else if (strcmp(args[cur_arg], "by") == 0) { - curproxy->http.fwd.p_by.nn_mode = HTTP_7239_FORBY_ORIG; + fwd->p_by.nn_mode = HTTP_7239_FORBY_ORIG; cur_arg += 1; } else if (strcmp(args[cur_arg], "by-expr") == 0) { - curproxy->http.fwd.p_by.nn_mode = HTTP_7239_FORBY_SMP; + fwd->p_by.nn_mode = HTTP_7239_FORBY_SMP; err_code |= _proxy_http_parse_7239_expr(args, &cur_arg, file, linenum, - &curproxy->http.fwd.p_by.nn_expr_s); + &fwd->p_by.nn_expr_s); if (err_code & ERR_FATAL) goto out; } else if (strcmp(args[cur_arg], "for") == 0) { - curproxy->http.fwd.p_for.nn_mode = HTTP_7239_FORBY_ORIG; + fwd->p_for.nn_mode = HTTP_7239_FORBY_ORIG; cur_arg += 1; } else if (strcmp(args[cur_arg], "for-expr") == 0) { - curproxy->http.fwd.p_for.nn_mode = HTTP_7239_FORBY_SMP; + fwd->p_for.nn_mode = HTTP_7239_FORBY_SMP; err_code |= _proxy_http_parse_7239_expr(args, &cur_arg, file, linenum, - &curproxy->http.fwd.p_for.nn_expr_s); + &fwd->p_for.nn_expr_s); if (err_code & ERR_FATAL) goto out; } else if (strcmp(args[cur_arg], "by_port") == 0) { - curproxy->http.fwd.p_by.np_mode = HTTP_7239_FORBY_ORIG; + fwd->p_by.np_mode = HTTP_7239_FORBY_ORIG; cur_arg += 1; } else if (strcmp(args[cur_arg], "by_port-expr") == 0) { - curproxy->http.fwd.p_by.np_mode = HTTP_7239_FORBY_SMP; + fwd->p_by.np_mode = HTTP_7239_FORBY_SMP; err_code |= _proxy_http_parse_7239_expr(args, &cur_arg, file, linenum, - &curproxy->http.fwd.p_by.np_expr_s); + &fwd->p_by.np_expr_s); if (err_code & ERR_FATAL) goto out; } else if (strcmp(args[cur_arg], "for_port") == 0) { - curproxy->http.fwd.p_for.np_mode = HTTP_7239_FORBY_ORIG; + fwd->p_for.np_mode = HTTP_7239_FORBY_ORIG; cur_arg += 1; } else if (strcmp(args[cur_arg], "for_port-expr") == 0) { - curproxy->http.fwd.p_for.np_mode = HTTP_7239_FORBY_SMP; + fwd->p_for.np_mode = HTTP_7239_FORBY_SMP; err_code |= _proxy_http_parse_7239_expr(args, &cur_arg, file, linenum, - &curproxy->http.fwd.p_for.np_expr_s); + &fwd->p_for.np_expr_s); if (err_code & ERR_FATAL) goto out; } else { @@ -1019,24 +1042,24 @@ int proxy_http_parse_7239(char **args, int cur_arg, } /* end while loop */ /* consistency check */ - if (curproxy->http.fwd.p_by.np_mode && - !curproxy->http.fwd.p_by.nn_mode) { - curproxy->http.fwd.p_by.np_mode = 0; - ha_free(&curproxy->http.fwd.p_by.np_expr_s); + if (fwd->p_by.np_mode && + !fwd->p_by.nn_mode) { + fwd->p_by.np_mode = 0; + ha_free(&fwd->p_by.np_expr_s); ha_warning("parsing [%s:%d] : '%s %s' : '%s' will be ignored because both 'by' " "and 'by-expr' are unset\n", file, linenum, args[0], args[1], - ((curproxy->http.fwd.p_by.np_mode == HTTP_7239_FORBY_ORIG) ? "by_port" : "by_port-expr")); + ((fwd->p_by.np_mode == HTTP_7239_FORBY_ORIG) ? "by_port" : "by_port-expr")); err_code |= ERR_WARN; } - if (curproxy->http.fwd.p_for.np_mode && - !curproxy->http.fwd.p_for.nn_mode) { - curproxy->http.fwd.p_for.np_mode = 0; - ha_free(&curproxy->http.fwd.p_for.np_expr_s); + if (fwd->p_for.np_mode && + !fwd->p_for.nn_mode) { + fwd->p_for.np_mode = 0; + ha_free(&fwd->p_for.np_expr_s); ha_warning("parsing [%s:%d] : '%s %s' : '%s' will be ignored because both 'for' " "and 'for-expr' are unset\n", file, linenum, args[0], args[1], - ((curproxy->http.fwd.p_for.np_mode == HTTP_7239_FORBY_ORIG) ? "for_port" : "for_port-expr")); + ((fwd->p_for.np_mode == HTTP_7239_FORBY_ORIG) ? "for_port" : "for_port-expr")); err_code |= ERR_WARN; } @@ -1049,21 +1072,22 @@ int proxy_http_parse_7239(char **args, int cur_arg, */ int proxy_http_compile_7239(struct proxy *curproxy) { + struct http_ext_7239 *fwd; int cfgerr = 0; int loop; - BUG_ON(!(curproxy->options & PR_O_HTTP_7239)); /* should not happen */ if (!(curproxy->cap & PR_CAP_BE)) { - /* no backend cap: not supported (ie: frontend) - * Moreover, 7239 settings are only inherited from default - * if proxy is backend capable.. going further would result in - * undefined behavior */ + /* no backend cap: not supported (ie: frontend) */ goto out; } + /* should not happen (test should be performed after BE cap test) */ + BUG_ON(!curproxy->http_ext || !curproxy->http_ext->fwd); + curproxy->conf.args.ctx = ARGC_OPT; /* option */ - curproxy->conf.args.file = curproxy->http.fwd.c_file; - curproxy->conf.args.line = curproxy->http.fwd.c_line; + curproxy->conf.args.file = curproxy->http_ext->fwd->c_file; + curproxy->conf.args.line = curproxy->http_ext->fwd->c_line; + fwd = curproxy->http_ext->fwd; /* it is important that we keep iterating on error to make sure * all fwd config fields are in the same state (post-parsing state) @@ -1079,38 +1103,33 @@ int proxy_http_compile_7239(struct proxy *curproxy) switch (loop) { case 0: /* host */ - expr_str = &curproxy->http.fwd.p_host.expr_s; - expr = &curproxy->http.fwd.p_host.expr; - smp = (curproxy->http.fwd.p_host.mode == - HTTP_7239_HOST_SMP); + expr_str = &fwd->p_host.expr_s; + expr = &fwd->p_host.expr; + smp = (fwd->p_host.mode == HTTP_7239_HOST_SMP); break; case 1: /* by->node */ - expr_str = &curproxy->http.fwd.p_by.nn_expr_s; - expr = &curproxy->http.fwd.p_by.nn_expr; - smp = (curproxy->http.fwd.p_by.nn_mode == - HTTP_7239_FORBY_SMP); + expr_str = &fwd->p_by.nn_expr_s; + expr = &fwd->p_by.nn_expr; + smp = (fwd->p_by.nn_mode == HTTP_7239_FORBY_SMP); break; case 2: /* by->nodeport */ - expr_str = &curproxy->http.fwd.p_by.np_expr_s; - expr = &curproxy->http.fwd.p_by.np_expr; - smp = (curproxy->http.fwd.p_by.np_mode == - HTTP_7239_FORBY_SMP); + expr_str = &fwd->p_by.np_expr_s; + expr = &fwd->p_by.np_expr; + smp = (fwd->p_by.np_mode == HTTP_7239_FORBY_SMP); break; case 3: /* for->node */ - expr_str = &curproxy->http.fwd.p_for.nn_expr_s; - expr = &curproxy->http.fwd.p_for.nn_expr; - smp = (curproxy->http.fwd.p_for.nn_mode == - HTTP_7239_FORBY_SMP); + expr_str = &fwd->p_for.nn_expr_s; + expr = &fwd->p_for.nn_expr; + smp = (fwd->p_for.nn_mode == HTTP_7239_FORBY_SMP); break; case 4: /* for->nodeport */ - expr_str = &curproxy->http.fwd.p_for.np_expr_s; - expr = &curproxy->http.fwd.p_for.np_expr; - smp = (curproxy->http.fwd.p_for.np_mode == - HTTP_7239_FORBY_SMP); + expr_str = &fwd->p_for.np_expr_s; + expr = &fwd->p_for.np_expr; + smp = (fwd->p_for.np_mode == HTTP_7239_FORBY_SMP); break; } if (!smp) @@ -1123,7 +1142,7 @@ int proxy_http_compile_7239(struct proxy *curproxy) /* should not happen unless system memory exhaustion */ ha_alert("%s '%s' [%s:%d]: failed to parse 'option forwarded' expression : %s.\n", proxy_type_str(curproxy), curproxy->id, - curproxy->http.fwd.c_file, curproxy->http.fwd.c_line, + fwd->c_file, fwd->c_line, "memory error"); cfgerr++; continue; @@ -1131,14 +1150,14 @@ int proxy_http_compile_7239(struct proxy *curproxy) cur_expr = sample_parse_expr((char*[]){*expr_str, NULL}, &idx, - curproxy->http.fwd.c_file, - curproxy->http.fwd.c_line, + fwd->c_file, + fwd->c_line, &err, &curproxy->conf.args, NULL); if (!cur_expr) { ha_alert("%s '%s' [%s:%d]: failed to parse 'option forwarded' expression '%s' in : %s.\n", proxy_type_str(curproxy), curproxy->id, - curproxy->http.fwd.c_file, curproxy->http.fwd.c_line, + fwd->c_file, fwd->c_line, *expr_str, err); ha_free(&err); cfgerr++; @@ -1153,7 +1172,7 @@ int proxy_http_compile_7239(struct proxy *curproxy) "some args extract information from '%s', " "none of which is available here.\n", proxy_type_str(curproxy), curproxy->id, - curproxy->http.fwd.c_file, curproxy->http.fwd.c_line, + fwd->c_file, fwd->c_line, *expr_str, sample_ckp_names(cur_expr->fetch->use)); } /* post parsing individual expr cleanup */ @@ -1166,10 +1185,10 @@ int proxy_http_compile_7239(struct proxy *curproxy) curproxy->conf.args.line = 0; /* post parsing general cleanup */ - ha_free(&curproxy->http.fwd.c_file); - curproxy->http.fwd.c_line = 0; + ha_free(&fwd->c_file); + fwd->c_line = 0; - curproxy->http.fwd.c_mode = 1; /* parsing completed */ + fwd->c_mode = 1; /* parsing completed */ out: return cfgerr; @@ -1180,21 +1199,25 @@ int proxy_http_parse_xff(char **args, int cur_arg, struct proxy *curproxy, const struct proxy *defpx, const char *file, int linenum) { + struct http_ext_xff *xff; int err_code = 0; + if (!http_ext_xff_prepare(curproxy)) + return proxy_http_parse_oom(file, linenum); + + xff = curproxy->http_ext->xff; + /* insert x-forwarded-for field, but not for the IP address listed as an except. * set default options (ie: bitfield, header name, etc) */ - curproxy->options |= PR_O_HTTP_XFF; + xff->mode = HTTP_XFF_ALWAYS; - curproxy->http.xff.mode = HTTP_XFF_ALWAYS; - - istfree(&curproxy->http.xff.hdr_name); - curproxy->http.xff.hdr_name = istdup(ist(DEF_XFORWARDFOR_HDR)); - if (!isttest(curproxy->http.xff.hdr_name)) + istfree(&xff->hdr_name); + xff->hdr_name = istdup(ist(DEF_XFORWARDFOR_HDR)); + if (!isttest(xff->hdr_name)) return proxy_http_parse_oom(file, linenum); - curproxy->http.xff.except_net.family = AF_UNSPEC; + xff->except_net.family = AF_UNSPEC; /* loop to go through arguments - start at 2, since 0+1 = "option" "forwardfor" */ cur_arg = 2; @@ -1205,16 +1228,16 @@ int proxy_http_parse_xff(char **args, int cur_arg, /* suboption except - needs additional argument for it */ if (*(args[cur_arg+1]) && - str2net(args[cur_arg+1], 1, &curproxy->http.xff.except_net.addr.v4.ip, &curproxy->http.xff.except_net.addr.v4.mask)) { - curproxy->http.xff.except_net.family = AF_INET; - curproxy->http.xff.except_net.addr.v4.ip.s_addr &= curproxy->http.xff.except_net.addr.v4.mask.s_addr; + str2net(args[cur_arg+1], 1, &xff->except_net.addr.v4.ip, &xff->except_net.addr.v4.mask)) { + xff->except_net.family = AF_INET; + xff->except_net.addr.v4.ip.s_addr &= xff->except_net.addr.v4.mask.s_addr; } else if (*(args[cur_arg+1]) && - str62net(args[cur_arg+1], &curproxy->http.xff.except_net.addr.v6.ip, &mask)) { - curproxy->http.xff.except_net.family = AF_INET6; - len2mask6(mask, &curproxy->http.xff.except_net.addr.v6.mask); + str62net(args[cur_arg+1], &xff->except_net.addr.v6.ip, &mask)) { + xff->except_net.family = AF_INET6; + len2mask6(mask, &xff->except_net.addr.v6.mask); for (i = 0; i < 16; i++) - curproxy->http.xff.except_net.addr.v6.ip.s6_addr[i] &= curproxy->http.xff.except_net.addr.v6.mask.s6_addr[i]; + xff->except_net.addr.v6.ip.s6_addr[i] &= xff->except_net.addr.v6.mask.s6_addr[i]; } else { ha_alert("parsing [%s:%d] : '%s %s %s' expects
[/mask] as argument.\n", @@ -1232,13 +1255,13 @@ int proxy_http_parse_xff(char **args, int cur_arg, err_code |= ERR_ALERT | ERR_FATAL; goto out; } - istfree(&curproxy->http.xff.hdr_name); - curproxy->http.xff.hdr_name = istdup(ist(args[cur_arg+1])); - if (!isttest(curproxy->http.xff.hdr_name)) + istfree(&xff->hdr_name); + xff->hdr_name = istdup(ist(args[cur_arg+1])); + if (!isttest(xff->hdr_name)) return proxy_http_parse_oom(file, linenum); cur_arg += 2; } else if (strcmp(args[cur_arg], "if-none") == 0) { - curproxy->http.xff.mode = HTTP_XFF_IFNONE; + xff->mode = HTTP_XFF_IFNONE; cur_arg += 1; } else { /* unknown suboption - catchall */ @@ -1257,19 +1280,23 @@ int proxy_http_parse_xot(char **args, int cur_arg, struct proxy *curproxy, const struct proxy *defpx, const char *file, int linenum) { + struct http_ext_xot *xot; int err_code = 0; + if (!http_ext_xot_prepare(curproxy)) + return proxy_http_parse_oom(file, linenum); + + xot = curproxy->http_ext->xot; + /* insert x-original-to field, but not for the IP address listed as an except. * set default options (ie: bitfield, header name, etc) */ - curproxy->options |= PR_O_HTTP_XOT; - - istfree(&curproxy->http.xot.hdr_name); - curproxy->http.xot.hdr_name = istdup(ist(DEF_XORIGINALTO_HDR)); - if (!isttest(curproxy->http.xot.hdr_name)) + istfree(&xot->hdr_name); + xot->hdr_name = istdup(ist(DEF_XORIGINALTO_HDR)); + if (!isttest(xot->hdr_name)) return proxy_http_parse_oom(file, linenum); - curproxy->http.xot.except_net.family = AF_UNSPEC; + xot->except_net.family = AF_UNSPEC; /* loop to go through arguments - start at 2, since 0+1 = "option" "originalto" */ cur_arg = 2; @@ -1280,16 +1307,16 @@ int proxy_http_parse_xot(char **args, int cur_arg, /* suboption except - needs additional argument for it */ if (*(args[cur_arg+1]) && - str2net(args[cur_arg+1], 1, &curproxy->http.xot.except_net.addr.v4.ip, &curproxy->http.xot.except_net.addr.v4.mask)) { - curproxy->http.xot.except_net.family = AF_INET; - curproxy->http.xot.except_net.addr.v4.ip.s_addr &= curproxy->http.xot.except_net.addr.v4.mask.s_addr; + str2net(args[cur_arg+1], 1, &xot->except_net.addr.v4.ip, &xot->except_net.addr.v4.mask)) { + xot->except_net.family = AF_INET; + xot->except_net.addr.v4.ip.s_addr &= xot->except_net.addr.v4.mask.s_addr; } else if (*(args[cur_arg+1]) && - str62net(args[cur_arg+1], &curproxy->http.xot.except_net.addr.v6.ip, &mask)) { - curproxy->http.xot.except_net.family = AF_INET6; - len2mask6(mask, &curproxy->http.xot.except_net.addr.v6.mask); + str62net(args[cur_arg+1], &xot->except_net.addr.v6.ip, &mask)) { + xot->except_net.family = AF_INET6; + len2mask6(mask, &xot->except_net.addr.v6.mask); for (i = 0; i < 16; i++) - curproxy->http.xot.except_net.addr.v6.ip.s6_addr[i] &= curproxy->http.xot.except_net.addr.v6.mask.s6_addr[i]; + xot->except_net.addr.v6.ip.s6_addr[i] &= xot->except_net.addr.v6.mask.s6_addr[i]; } else { ha_alert("parsing [%s:%d] : '%s %s %s' expects
[/mask] as argument.\n", @@ -1306,9 +1333,9 @@ int proxy_http_parse_xot(char **args, int cur_arg, err_code |= ERR_ALERT | ERR_FATAL; goto out; } - istfree(&curproxy->http.xot.hdr_name); - curproxy->http.xot.hdr_name = istdup(ist(args[cur_arg+1])); - if (!isttest(curproxy->http.xot.hdr_name)) + istfree(&xot->hdr_name); + xot->hdr_name = istdup(ist(args[cur_arg+1])); + if (!isttest(xot->hdr_name)) return proxy_http_parse_oom(file, linenum); cur_arg += 2; } else { @@ -1329,8 +1356,105 @@ int proxy_http_parse_xot(char **args, int cur_arg, * below are helpers to manage http ext options */ -void http_ext_7239_clean(struct http_ext_7239 *clean) +/* Ensure http_ext->fwd is properly allocated and + * initialized for . + * The function will leverage http_ext_prepare() to make + * sure http_ext is properly allocated and initialized as well. + * Returns 1 for success and 0 for failure (memory error) + */ +int http_ext_7239_prepare(struct proxy *curproxy) +{ + struct http_ext_7239 *fwd; + + if (!http_ext_prepare(curproxy)) + return 0; + if (curproxy->http_ext->fwd) + return 1; /* nothing to do */ + + fwd = malloc(sizeof(*fwd)); + if (!fwd) + return 0; + /* initialize fwd mandatory fields */ + fwd->c_mode = 0; /* pre-compile (parse) time */ + fwd->c_file = NULL; + fwd->p_host.expr_s = NULL; + fwd->p_by.nn_expr_s = NULL; + fwd->p_by.np_expr_s = NULL; + fwd->p_for.nn_expr_s = NULL; + fwd->p_for.np_expr_s = NULL; + /* assign */ + curproxy->http_ext->fwd = fwd; + return 1; +} + +/* Ensure http_ext->xff is properly allocated and + * initialized for . + * The function will leverage http_ext_prepare() to make + * sure http_ext is properly allocated and initialized as well. + * Returns 1 for success and 0 for failure (memory error) + */ +int http_ext_xff_prepare(struct proxy *curproxy) +{ + struct http_ext_xff *xff; + + if (!http_ext_prepare(curproxy)) + return 0; + if (curproxy->http_ext->xff) + return 1; /* nothing to do */ + + xff = malloc(sizeof(*xff)); + if (!xff) + return 0; + /* initialize xff mandatory fields */ + xff->hdr_name = IST_NULL; + /* assign */ + curproxy->http_ext->xff = xff; + return 1; +} + +/* Ensure http_ext->xot is properly allocated and + * initialized for . + * The function will leverage http_ext_prepare() to make + * sure http_ext is properly allocated and initialized as well. + * Returns 1 for success and 0 for failure (memory error) + */ +int http_ext_xot_prepare(struct proxy *curproxy) +{ + struct http_ext_xot *xot; + + if (!http_ext_prepare(curproxy)) + return 0; + if (curproxy->http_ext->xot) + return 1; /* nothing to do */ + + xot = malloc(sizeof(*xot)); + if (!xot) + return 0; + /* initialize xot mandatory fields */ + xot->hdr_name = IST_NULL; + /* assign */ + curproxy->http_ext->xot = xot; + return 1; +} + +/* deep clean http_ext->fwd parameter for + * http_ext->fwd will be freed + * clean behavior will differ depending on http_ext->fwd + * state. If fwd is in 'parsed' state, parsing hints will be + * cleaned. Else, it means fwd is in 'compiled' state, in this + * case we're cleaning compiled results. + * This is because parse and compile memory areas are shared in + * a single union to optimize struct http_ext_7239 size. + */ +void http_ext_7239_clean(struct proxy *curproxy) { + struct http_ext_7239 *clean; + + if (!curproxy->http_ext) + return; + clean = curproxy->http_ext->fwd; + if (!clean) + return; /* nothing to do */ if (!clean->c_mode) { /* parsed */ ha_free(&clean->c_file); @@ -1353,22 +1477,69 @@ void http_ext_7239_clean(struct http_ext_7239 *clean) release_sample_expr(clean->p_for.np_expr); clean->p_for.np_expr = NULL; } + /* free fwd */ + ha_free(&curproxy->http_ext->fwd); } -void http_ext_xff_clean(struct http_ext_xff *clean) +/* deep clean http_ext->xff parameter for + * http_ext->xff will be freed + */ +void http_ext_xff_clean(struct proxy *curproxy) { + struct http_ext_xff *clean; + + if (!curproxy->http_ext) + return; + clean = curproxy->http_ext->xff; + if (!clean) + return; /* nothing to do */ istfree(&clean->hdr_name); + /* free xff */ + ha_free(&curproxy->http_ext->xff); } -void http_ext_xot_clean(struct http_ext_xot *clean) +/* deep clean http_ext->xot parameter for + * http_ext->xot will be freed + */ +void http_ext_xot_clean(struct proxy *curproxy) { + struct http_ext_xot *clean; + + if (!curproxy->http_ext) + return; + clean = curproxy->http_ext->xot; + if (!clean) + return; /* nothing to do */ istfree(&clean->hdr_name); + /* free xot */ + ha_free(&curproxy->http_ext->xot); } -void http_ext_7239_copy(struct http_ext_7239 *dest, const struct http_ext_7239 *orig) +/* duplicate http_ext->fwd parameters from to + * performs the required memory allocation and initialization + */ +void http_ext_7239_dup(const struct proxy *def, struct proxy *cpy) { + struct http_ext_7239 *dest = NULL; + struct http_ext_7239 *orig = NULL; + + /* feature requires backend cap */ + if (!(cpy->cap & PR_CAP_BE)) + return; + + if (def->http_ext == NULL || def->http_ext->fwd == NULL) + return; + + orig = def->http_ext->fwd; + if (orig->c_mode) return; /* copy not supported once compiled */ + + if (!http_ext_7239_prepare(cpy)) + return; + + dest = cpy->http_ext->fwd; + if (orig->c_file) dest->c_file = strdup(orig->c_file); dest->c_line = orig->c_line; @@ -1396,21 +1567,104 @@ void http_ext_7239_copy(struct http_ext_7239 *dest, const struct http_ext_7239 * dest->p_for.np_expr_s = strdup(orig->p_for.np_expr_s); } -void http_ext_xff_copy(struct http_ext_xff *dest, const struct http_ext_xff *orig) +/* duplicate http_ext->xff parameters from to + * performs the required memory allocation and initialization + */ +void http_ext_xff_dup(const struct proxy *def, struct proxy *cpy) { + struct http_ext_xff *dest = NULL; + struct http_ext_xff *orig = NULL; + + if (def->http_ext == NULL || def->http_ext->xff == NULL || + !http_ext_xff_prepare(cpy)) + return; + + orig = def->http_ext->xff; + dest = cpy->http_ext->xff; + if (isttest(orig->hdr_name)) dest->hdr_name = istdup(orig->hdr_name); dest->mode = orig->mode; dest->except_net = orig->except_net; } -void http_ext_xot_copy(struct http_ext_xot *dest, const struct http_ext_xot *orig) +/* duplicate http_ext->xot parameters from to + * performs the required memory allocation and initialization + */ +void http_ext_xot_dup(const struct proxy *def, struct proxy *cpy) { + struct http_ext_xot *dest = NULL; + struct http_ext_xot *orig = NULL; + + if (def->http_ext == NULL || def->http_ext->xot == NULL || + !http_ext_xot_prepare(cpy)) + return; + + orig = def->http_ext->xot; + dest = cpy->http_ext->xot; + if (isttest(orig->hdr_name)) dest->hdr_name = istdup(orig->hdr_name); dest->except_net = orig->except_net; } +/* Allocate new http_ext and initialize it + * if needed + * Returns 1 for success and 0 for failure + */ +int http_ext_prepare(struct proxy *curproxy) +{ + if (curproxy->http_ext) + return 1; /* nothing to do */ + + curproxy->http_ext = malloc(sizeof(*curproxy->http_ext)); + if (!curproxy->http_ext) + return 0; /* failure */ + /* first init, set supported ext to NULL */ + curproxy->http_ext->fwd = NULL; + curproxy->http_ext->xff = NULL; + curproxy->http_ext->xot = NULL; + return 1; +} + +/* duplicate existing http_ext from to + */ +void http_ext_dup(const struct proxy *defproxy, struct proxy *curproxy) +{ + /* copy defproxy.http_ext members */ + http_ext_7239_dup(defproxy, curproxy); + http_ext_xff_dup(defproxy, curproxy); + http_ext_xot_dup(defproxy, curproxy); +} + +/* deep clean http_ext for (if previously allocated) + */ +void http_ext_clean(struct proxy *curproxy) +{ + if (!curproxy->http_ext) + return; /* nothing to do */ + /* first, free supported ext */ + http_ext_7239_clean(curproxy); + http_ext_xff_clean(curproxy); + http_ext_xot_clean(curproxy); + + /* then, free http_ext */ + ha_free(&curproxy->http_ext); +} + +/* soft clean (only clean http_ext if no more options are used) */ +void http_ext_softclean(struct proxy *curproxy) +{ + if (!curproxy->http_ext) + return; /* nothing to do */ + if (!curproxy->http_ext->fwd && + !curproxy->http_ext->xff && + !curproxy->http_ext->xot) { + /* no more use for http_ext, all options are disabled */ + http_ext_clean(curproxy); + } +} + /* * =========== CONV =========== * related converters diff --git a/src/proxy.c b/src/proxy.c index efb8ddbe1b..43a35f5463 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -352,9 +352,8 @@ void free_proxy(struct proxy *p) pxdf->fct(p); free(p->desc); - http_ext_7239_clean(&p->http.fwd); - http_ext_xff_clean(&p->http.xff); - http_ext_xot_clean(&p->http.xot); + + http_ext_clean(p); task_destroy(p->task); @@ -1467,9 +1466,7 @@ void proxy_free_defaults(struct proxy *defproxy) ha_free(&defproxy->conn_src.iface_name); istfree(&defproxy->server_id_hdr_name); - http_ext_7239_clean(&defproxy->http.fwd); - http_ext_xff_clean(&defproxy->http.xff); - http_ext_xot_clean(&defproxy->http.xot); + http_ext_clean(defproxy); list_for_each_entry_safe(acl, aclb, &defproxy->acl, list) { LIST_DELETE(&acl->list); @@ -1650,10 +1647,8 @@ static int proxy_defproxy_cpy(struct proxy *curproxy, const struct proxy *defpro curproxy->tcp_req.inspect_delay = defproxy->tcp_req.inspect_delay; curproxy->tcp_rep.inspect_delay = defproxy->tcp_rep.inspect_delay; - if (defproxy->options & PR_O_HTTP_XFF) - http_ext_xff_copy(&curproxy->http.xff, &defproxy->http.xff); - if (defproxy->options & PR_O_HTTP_XOT) - http_ext_xot_copy(&curproxy->http.xot, &defproxy->http.xot); + http_ext_clean(curproxy); + http_ext_dup(defproxy, curproxy); if (isttest(defproxy->server_id_hdr_name)) curproxy->server_id_hdr_name = istdup(defproxy->server_id_hdr_name); @@ -1697,8 +1692,6 @@ static int proxy_defproxy_cpy(struct proxy *curproxy, const struct proxy *defpro } curproxy->ck_opts = defproxy->ck_opts; - if (defproxy->options & PR_O_HTTP_7239) - http_ext_7239_copy(&curproxy->http.fwd, &defproxy->http.fwd); if (defproxy->cookie_name) curproxy->cookie_name = strdup(defproxy->cookie_name);