Created
April 4, 2011 08:08
-
-
Save vr/901278 to your computer and use it in GitHub Desktop.
Patch accept-proxy (proxy protocol) for HAProxy 1.4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 5167395d0b4b5f88181d3fd40420ecd2036440a8 Mon Sep 17 00:00:00 2001 | |
From: =?iso-8859-15?q?Herv=E9=20COMMOWICK?= <[email protected]> | |
Date: Mon, 7 Feb 2011 16:40:41 +0100 | |
Subject: [PATCH 1/4] PATCH : add accept-proxy support | |
--- | |
doc/configuration.txt | 26 ++++++- | |
include/common/standard.h | 25 ++++++- | |
include/proto/client.h | 1 + | |
include/types/buffers.h | 20 +++--- | |
include/types/protocols.h | 1 + | |
src/cfgparse.c | 15 ++++- | |
src/client.c | 186 +++++++++++++++++++++++++++++++++++++++++++++ | |
src/proto_http.c | 4 +- | |
src/session.c | 7 ++ | |
src/standard.c | 9 ++- | |
10 files changed, 275 insertions(+), 19 deletions(-) | |
diff --git a/doc/configuration.txt b/doc/configuration.txt | |
index 8233f02..9139d85 100644 | |
--- a/doc/configuration.txt | |
+++ b/doc/configuration.txt | |
@@ -1318,6 +1318,7 @@ bind [<address>]:<port_range> [, ...] transparent | |
bind [<address>]:<port_range> [, ...] id <id> | |
bind [<address>]:<port_range> [, ...] name <name> | |
bind [<address>]:<port_range> [, ...] defer-accept | |
+bind [<address>]:<port_range> [, ...] accept-proxy | |
Define one or several listening addresses and/or ports in a frontend. | |
May be used in sections : defaults | frontend | listen | backend | |
no | yes | yes | no | |
@@ -1398,6 +1399,19 @@ bind [<address>]:<port_range> [, ...] defer-accept | |
with front firewalls which would see an established | |
connection while the proxy will only see it in SYN_RECV. | |
+ accept-proxy is an optional keyword which enforces use of the PROXY | |
+ protocol over any connection accepted by this listener. The | |
+ PROXY protocol dictates the layer 3/4 addresses of the | |
+ incoming connection to be used everywhere an address is used, | |
+ with the only exception of "tcp-request connection" rules | |
+ which will only see the real connection address. Logs will | |
+ reflect the addresses indicated in the protocol, unless it is | |
+ violated, in which case the real address will still be used. | |
+ This keyword combined with support from external components | |
+ can be used as an efficient and reliable alternative to the | |
+ X-Forwarded-For mechanism which is not always reliable and | |
+ not even always usable. | |
+ | |
It is possible to specify a list of address:port combinations delimited by | |
commas. The frontend will then listen on all of these addresses. There is no | |
fixed limit to the number of addresses and ports which can be listened on in | |
@@ -1408,8 +1422,10 @@ bind [<address>]:<port_range> [, ...] defer-accept | |
listen http_proxy | |
bind :80,:443 | |
bind 10.0.0.1:10080,10.0.0.1:10443 | |
+ bind 127.0.0.1:8443 accept-proxy | |
- See also : "source". | |
+ See also : "source", "option forwardfor" and the PROXY protocol | |
+ documentation. | |
bind-process [ all | odd | even | <number 1-32> ] ... | |
@@ -7116,7 +7132,9 @@ marked with a star ('*') after the field name below. | |
Detailed fields description : | |
- "client_ip" is the IP address of the client which initiated the TCP | |
- connection to haproxy. | |
+ connection to haproxy. Note that when the connection is accepted on a | |
+ socket configured with "accept-proxy" and the PROXY protocol is correctly | |
+ used, then the logs will reflect the forwarded connection's information. | |
- "client_port" is the TCP port of the client which initiated the connection. | |
@@ -7289,7 +7307,9 @@ with a star ('*') after the field name below. | |
Detailed fields description : | |
- "client_ip" is the IP address of the client which initiated the TCP | |
- connection to haproxy. | |
+ connection to haproxy. Note that when the connection is accepted on a | |
+ socket configured with "accept-proxy" and the PROXY protocol is correctly | |
+ used, then the logs will reflect the forwarded connection's information. | |
- "client_port" is the TCP port of the client which initiated the connection. | |
diff --git a/include/common/standard.h b/include/common/standard.h | |
index 762fab5..6ff8268 100644 | |
--- a/include/common/standard.h | |
+++ b/include/common/standard.h | |
@@ -262,6 +262,28 @@ static inline unsigned int __strl2uic(const char *s, int len) | |
return i; | |
} | |
+/* This function reads an unsigned integer from the string pointed to by <s> | |
+ * and returns it. The <s> pointer is adjusted to point to the first unread | |
+ * char. The function automatically stops at <end>. | |
+ */ | |
+static inline unsigned int __read_uint(const char **s, const char *end) | |
+{ | |
+ const char *ptr = *s; | |
+ unsigned int i = 0; | |
+ unsigned int j, k; | |
+ | |
+ while (ptr < end) { | |
+ j = *ptr - '0'; | |
+ k = i * 10; | |
+ if (j > 9) | |
+ break; | |
+ i = k + j; | |
+ ptr++; | |
+ } | |
+ *s = ptr; | |
+ return i; | |
+} | |
+ | |
extern unsigned int str2ui(const char *s); | |
extern unsigned int str2uic(const char *s); | |
extern unsigned int strl2ui(const char *s, int len); | |
@@ -269,9 +291,10 @@ extern unsigned int strl2uic(const char *s, int len); | |
extern int strl2ic(const char *s, int len); | |
extern int strl2irc(const char *s, int len, int *ret); | |
extern int strl2llrc(const char *s, int len, long long *ret); | |
+extern unsigned int read_uint(const char **s, const char *end); | |
unsigned int inetaddr_host(const char *text); | |
unsigned int inetaddr_host_lim(const char *text, const char *stop); | |
-unsigned int inetaddr_host_lim_ret(const char *text, char *stop, const char **ret); | |
+unsigned int inetaddr_host_lim_ret(char *text, char *stop, char **ret); | |
static inline char *cut_crlf(char *s) { | |
diff --git a/include/proto/client.h b/include/proto/client.h | |
index 1d368a4..ae11a09 100644 | |
--- a/include/proto/client.h | |
+++ b/include/proto/client.h | |
@@ -25,6 +25,7 @@ | |
#include <common/config.h> | |
#include <types/session.h> | |
+int frontend_decode_proxy_request(struct session *s, struct buffer *req, int an_bit); | |
void get_frt_addr(struct session *s); | |
int event_accept(int fd); | |
diff --git a/include/types/buffers.h b/include/types/buffers.h | |
index 59e6d29..47ee7b3 100644 | |
--- a/include/types/buffers.h | |
+++ b/include/types/buffers.h | |
@@ -134,16 +134,16 @@ | |
* The field is blanked by buffer_init() and only by analysers themselves | |
* afterwards. | |
*/ | |
-#define AN_REQ_INSPECT 0x00000001 /* inspect request contents */ | |
-#define AN_REQ_WAIT_HTTP 0x00000002 /* wait for an HTTP request */ | |
-#define AN_REQ_HTTP_PROCESS_FE 0x00000004 /* process the frontend's HTTP part */ | |
-#define AN_REQ_SWITCHING_RULES 0x00000008 /* apply the switching rules */ | |
-#define AN_REQ_HTTP_PROCESS_BE 0x00000010 /* process the backend's HTTP part */ | |
-#define AN_REQ_HTTP_INNER 0x00000020 /* inner processing of HTTP request */ | |
-#define AN_REQ_HTTP_TARPIT 0x00000040 /* wait for end of HTTP tarpit */ | |
-#define AN_REQ_HTTP_BODY 0x00000080 /* inspect HTTP request body */ | |
-#define AN_REQ_STICKING_RULES 0x00000100 /* table persistence matching */ | |
-/* unused: 0x200 */ | |
+#define AN_REQ_DECODE_PROXY 0x00000001 /* take the proxied address from a 'PROXY' line */ | |
+#define AN_REQ_INSPECT 0x00000002 /* inspect request contents */ | |
+#define AN_REQ_WAIT_HTTP 0x00000004 /* wait for an HTTP request */ | |
+#define AN_REQ_HTTP_PROCESS_FE 0x00000008 /* process the frontend's HTTP part */ | |
+#define AN_REQ_SWITCHING_RULES 0x00000010 /* apply the switching rules */ | |
+#define AN_REQ_HTTP_PROCESS_BE 0x00000020 /* process the backend's HTTP part */ | |
+#define AN_REQ_HTTP_INNER 0x00000040 /* inner processing of HTTP request */ | |
+#define AN_REQ_HTTP_TARPIT 0x00000080 /* wait for end of HTTP tarpit */ | |
+#define AN_REQ_HTTP_BODY 0x00000100 /* inspect HTTP request body */ | |
+#define AN_REQ_STICKING_RULES 0x00000200 /* table persistence matching */ | |
#define AN_REQ_PRST_RDP_COOKIE 0x00000400 /* persistence on rdp cookie */ | |
#define AN_REQ_HTTP_XFER_BODY 0x00000800 /* forward request body */ | |
diff --git a/include/types/protocols.h b/include/types/protocols.h | |
index c13c73d..ed0d403 100644 | |
--- a/include/types/protocols.h | |
+++ b/include/types/protocols.h | |
@@ -72,6 +72,7 @@ | |
#define LI_O_FOREIGN 0x0002 /* permit listening on foreing addresses */ | |
#define LI_O_NOQUICKACK 0x0004 /* disable quick ack of immediate data (linux) */ | |
#define LI_O_DEF_ACCEPT 0x0008 /* wait up to 1 second for data before accepting */ | |
+#define LI_O_ACC_PROXY 0x0010 /* find the proxied address in the first request line */ | |
/* The listener will be directly referenced by the fdtab[] which holds its | |
* socket. The listener provides the protocol-specific accept() function to | |
diff --git a/src/cfgparse.c b/src/cfgparse.c | |
index 1dc8362..0eafce7 100644 | |
--- a/src/cfgparse.c | |
+++ b/src/cfgparse.c | |
@@ -1413,6 +1413,16 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) | |
#endif | |
} | |
+ if (!strcmp(args[cur_arg], "accept-proxy")) { /* expect a 'PROXY' line first */ | |
+ struct listener *l; | |
+ | |
+ for (l = curproxy->listen; l != last_listen; l = l->next) | |
+ l->options |= LI_O_ACC_PROXY; | |
+ | |
+ cur_arg ++; | |
+ continue; | |
+ } | |
+ | |
if (!strcmp(args[cur_arg], "name")) { | |
struct listener *l; | |
@@ -1465,7 +1475,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) | |
continue; | |
} | |
- Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'defer-accept', 'name', 'id', 'mss' and 'interface' options.\n", | |
+ Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'accept-proxy', 'defer-accept', 'name', 'id', 'mss' and 'interface' options.\n", | |
file, linenum, args[0]); | |
err_code |= ERR_ALERT | ERR_FATAL; | |
goto out; | |
@@ -5621,6 +5631,9 @@ out_uri_auth_compat: | |
listener->handler = process_session; | |
listener->analysers |= curproxy->fe_req_ana; | |
+ if (listener->options & LI_O_ACC_PROXY) | |
+ listener->analysers |= AN_REQ_DECODE_PROXY; | |
+ | |
/* smart accept mode is automatic in HTTP mode */ | |
if ((curproxy->options2 & PR_O2_SMARTACC) || | |
(curproxy->mode == PR_MODE_HTTP && | |
diff --git a/src/client.c b/src/client.c | |
index cd9af14..385a136 100644 | |
--- a/src/client.c | |
+++ b/src/client.c | |
@@ -22,6 +22,7 @@ | |
#include <common/compat.h> | |
#include <common/config.h> | |
+#include <common/debug.h> | |
#include <common/time.h> | |
#include <types/global.h> | |
@@ -43,6 +44,191 @@ | |
#include <proto/task.h> | |
+/* This analyser tries to fetch a line from the request buffer which looks like : | |
+ * | |
+ * "PROXY" <SP> PROTO <SP> SRC3 <SP> DST3 <SP> SRC4 <SP> <DST4> "\r\n" | |
+ * | |
+ * There must be exactly one space between each field. Fields are : | |
+ * - PROTO : layer 4 protocol, which must be "TCP4" or "TCP6". | |
+ * - SRC3 : layer 3 (eg: IP) source address in standard text form | |
+ * - DST3 : layer 3 (eg: IP) destination address in standard text form | |
+ * - SRC4 : layer 4 (eg: TCP port) source address in standard text form | |
+ * - DST4 : layer 4 (eg: TCP port) destination address in standard text form | |
+ * | |
+ * This line MUST be at the beginning of the buffer and MUST NOT wrap. | |
+ * | |
+ * Once the data is fetched, the values are set in the session's field and data | |
+ * are removed from the buffer. The function returns zero if it needs to wait | |
+ * for more data (max: timeout_client), or 1 if it has finished and removed itself. | |
+ */ | |
+int frontend_decode_proxy_request(struct session *s, struct buffer *req, int an_bit) | |
+{ | |
+ char *line = req->data; | |
+ char *end = req->data + req->l; | |
+ int len; | |
+ | |
+ 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); | |
+ | |
+ if (req->flags & (BF_READ_ERROR|BF_READ_TIMEOUT)) | |
+ goto fail; | |
+ | |
+ len = MIN(req->l, 6); | |
+ if (!len) | |
+ goto missing; | |
+ | |
+ /* Decode a possible proxy request, fail early if it does not match */ | |
+ if (strncmp(line, "PROXY ", len) != 0) | |
+ goto fail; | |
+ | |
+ line += 6; | |
+ if (req->l < 18) /* shortest possible line */ | |
+ goto missing; | |
+ | |
+ if (!memcmp(line, "TCP4 ", 5) != 0) { | |
+ u32 src3, dst3, sport, dport; | |
+ | |
+ line += 5; | |
+ | |
+ src3 = inetaddr_host_lim_ret(line, end, &line); | |
+ if (line == end) | |
+ goto missing; | |
+ if (*line++ != ' ') | |
+ goto fail; | |
+ | |
+ dst3 = inetaddr_host_lim_ret(line, end, &line); | |
+ if (line == end) | |
+ goto missing; | |
+ if (*line++ != ' ') | |
+ goto fail; | |
+ | |
+ sport = read_uint((const char **)&line, end); | |
+ if (line == end) | |
+ goto missing; | |
+ if (*line++ != ' ') | |
+ goto fail; | |
+ | |
+ dport = read_uint((const char **)&line, end); | |
+ if (line > end - 2) | |
+ goto missing; | |
+ if (*line++ != '\r') | |
+ goto fail; | |
+ if (*line++ != '\n') | |
+ goto fail; | |
+ | |
+ /* update the session's addresses and mark them set */ | |
+ ((struct sockaddr_in *)&s->cli_addr)->sin_family = AF_INET; | |
+ ((struct sockaddr_in *)&s->cli_addr)->sin_addr.s_addr = htonl(src3); | |
+ ((struct sockaddr_in *)&s->cli_addr)->sin_port = htons(sport); | |
+ | |
+ ((struct sockaddr_in *)&s->frt_addr)->sin_family = AF_INET; | |
+ ((struct sockaddr_in *)&s->frt_addr)->sin_addr.s_addr = htonl(dst3); | |
+ ((struct sockaddr_in *)&s->frt_addr)->sin_port = htons(dport); | |
+ s->flags |= SN_FRT_ADDR_SET; | |
+ | |
+ } | |
+ else if (!memcmp(line, "TCP6 ", 5) != 0) { | |
+ u32 sport, dport; | |
+ char *src_s; | |
+ char *dst_s, *sport_s, *dport_s; | |
+ struct in6_addr src3, dst3; | |
+ | |
+ line+=5; | |
+ | |
+ src_s = line; | |
+ dst_s = sport_s = dport_s = NULL; | |
+ while (1) { | |
+ if (line > end - 2) { | |
+ goto missing; | |
+ } | |
+ else if (*line == '\r') { | |
+ *line = 0; | |
+ line++; | |
+ if (*line++ != '\n') | |
+ goto fail; | |
+ break; | |
+ } | |
+ | |
+ if (*line == ' ') { | |
+ *line = 0; | |
+ if (!dst_s) | |
+ dst_s = line+1; | |
+ else if (!sport_s) | |
+ sport_s = line+1; | |
+ else if (!dport_s) | |
+ dport_s = line+1; | |
+ } | |
+ line++; | |
+ } | |
+ | |
+ if (!dst_s || !sport_s || !dport_s) | |
+ goto fail; | |
+ | |
+ sport = read_uint((const char **)&sport_s,dport_s-1); | |
+ if ( *sport_s != 0 ) | |
+ goto fail; | |
+ | |
+ dport = read_uint((const char **)&dport_s,line-2); | |
+ if ( *dport_s != 0 ) | |
+ goto fail; | |
+ | |
+ if (inet_pton(AF_INET6, src_s, (void *)&src3) != 1) | |
+ goto fail; | |
+ | |
+ if (inet_pton(AF_INET6, dst_s, (void *)&dst3) != 1) | |
+ goto fail; | |
+ | |
+ /* update the session's addresses and mark them set */ | |
+ ((struct sockaddr_in6 *)&s->cli_addr)->sin6_family = AF_INET6; | |
+ memcpy(&((struct sockaddr_in6 *)&s->cli_addr)->sin6_addr, &src3, sizeof(struct in6_addr)); | |
+ ((struct sockaddr_in6 *)&s->cli_addr)->sin6_port = htons(sport); | |
+ | |
+ ((struct sockaddr_in6 *)&s->frt_addr)->sin6_family = AF_INET6; | |
+ memcpy(&((struct sockaddr_in6 *)&s->frt_addr)->sin6_addr, &dst3, sizeof(struct in6_addr)); | |
+ ((struct sockaddr_in6 *)&s->frt_addr)->sin6_port = htons(dport); | |
+ s->flags |= SN_FRT_ADDR_SET; | |
+ } | |
+ else { | |
+ goto fail; | |
+ } | |
+ | |
+ /* remove the PROXY line from the request */ | |
+ len = line - req->data; | |
+ buffer_replace2(req, req->data, line, NULL, 0); | |
+ req->total -= len; /* don't count the header line */ | |
+ | |
+ req->analysers &= ~an_bit; | |
+ return 1; | |
+ | |
+ missing: | |
+ if (!(req->flags & (BF_SHUTR|BF_FULL))) { | |
+ buffer_dont_connect(s->req); | |
+ return 0; | |
+ } | |
+ /* missing data and buffer is either full or shutdown => fail */ | |
+ | |
+ fail: | |
+ buffer_abort(req); | |
+ buffer_abort(s->rep); | |
+ req->analysers = 0; | |
+ | |
+ s->fe->counters.failed_req++; | |
+ if (s->listener->counters) | |
+ s->listener->counters->failed_req++; | |
+ | |
+ if (!(s->flags & SN_ERR_MASK)) | |
+ s->flags |= SN_ERR_PRXCOND; | |
+ if (!(s->flags & SN_FINST_MASK)) | |
+ s->flags |= SN_FINST_R; | |
+ return 0; | |
+} | |
+ | |
/* Retrieves the original destination address used by the client, and sets the | |
* SN_FRT_ADDR_SET flag. | |
*/ | |
diff --git a/src/proto_http.c b/src/proto_http.c | |
index 3ea4c8a..8170c1a 100644 | |
--- a/src/proto_http.c | |
+++ b/src/proto_http.c | |
@@ -3911,7 +3911,8 @@ void http_end_txn_clean_session(struct session *s) | |
if (s->rep->lr >= s->rep->data + s->rep->size) | |
s->rep->lr -= s->req->size; | |
- s->req->analysers |= s->fe->fe_req_ana; | |
+ s->req->analysers = s->fe->fe_req_ana; | |
+ s->req->analysers &= ~AN_REQ_DECODE_PROXY; | |
s->rep->analysers = 0; | |
http_silent_debug(__LINE__, s); | |
@@ -7278,7 +7279,6 @@ void http_reset_txn(struct session *s) | |
http_init_txn(s); | |
s->be = s->fe; | |
- s->req->analysers = s->listener->analysers; | |
s->logs.logwait = s->fe->to_log; | |
s->srv = s->prev_srv = s->srv_conn = NULL; | |
/* re-init store persistence */ | |
diff --git a/src/session.c b/src/session.c | |
index 5d089b2..0c78b13 100644 | |
--- a/src/session.c | |
+++ b/src/session.c | |
@@ -34,6 +34,7 @@ | |
#include <proto/proxy.h> | |
#include <proto/queue.h> | |
#include <proto/server.h> | |
+#include <proto/client.h> | |
#include <proto/stick_table.h> | |
#include <proto/stream_interface.h> | |
#include <proto/stream_sock.h> | |
@@ -1052,6 +1053,12 @@ resync_stream_interface: | |
while (ana_list && max_loops--) { | |
/* Warning! ensure that analysers are always placed in ascending order! */ | |
+ if (ana_list & AN_REQ_DECODE_PROXY) { | |
+ if (!frontend_decode_proxy_request(s, s->req, AN_REQ_DECODE_PROXY)) | |
+ break; | |
+ UPDATE_ANALYSERS(s->req->analysers, ana_list, ana_back, AN_REQ_DECODE_PROXY); | |
+ } | |
+ | |
if (ana_list & AN_REQ_INSPECT) { | |
if (!tcp_inspect_request(s, s->req, AN_REQ_INSPECT)) | |
break; | |
diff --git a/src/standard.c b/src/standard.c | |
index c8b0d51..5a80627 100644 | |
--- a/src/standard.c | |
+++ b/src/standard.c | |
@@ -535,6 +535,11 @@ unsigned int strl2uic(const char *s, int len) | |
return __strl2uic(s, len); | |
} | |
+unsigned int read_uint(const char **s, const char *end) | |
+{ | |
+ return __read_uint(s, end); | |
+} | |
+ | |
/* This one is 7 times faster than strtol() on athlon with checks. | |
* It returns the value of the number composed of all valid digits read, | |
* and can process negative numbers too. | |
@@ -959,12 +964,12 @@ unsigned int inetaddr_host_lim(const char *text, const char *stop) | |
* Idem except the pointer to first unparsed byte is returned into <ret> which | |
* must not be NULL. | |
*/ | |
-unsigned int inetaddr_host_lim_ret(const char *text, char *stop, const char **ret) | |
+unsigned int inetaddr_host_lim_ret(char *text, char *stop, char **ret) | |
{ | |
const unsigned int ascii_zero = ('0' << 24) | ('0' << 16) | ('0' << 8) | '0'; | |
register unsigned int dig100, dig10, dig1; | |
int s; | |
- const char *p, *d; | |
+ char *p, *d; | |
dig1 = dig10 = dig100 = ascii_zero; | |
s = 24; | |
-- | |
1.7.4.1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 456b2bfd2a9e52aa8c75bb55f8bb326430014a1d Mon Sep 17 00:00:00 2001 | |
From: =?iso-8859-15?q?Herv=E9=20COMMOWICK?= <[email protected]> | |
Date: Thu, 24 Feb 2011 16:55:46 +0100 | |
Subject: [PATCH] PATCH: add bind unix socket support | |
Commit ID from haproxy 1.5 dev git | |
cf20bf1c1c2318bab62c4811795572fe3e143ebb | |
4ab9262894daffd032678344d1957dd151f283f5 | |
837ca52de38f59f76f74edeac12a73eb15990fc1 | |
ab844ea9e15d14f857e90102beab4b629d83ba53 | |
ec810d1dc76f05002ebe0508243a70d3d27fe572 | |
0aaccf88f980b0eaa6f2a7235accbaba464f0c79 | |
5bd86a8ff5865214bfa42eddecd81c6afb39f628 | |
ed76092e10b2b542e99701a716912db7189ccb71 | |
d55c3feca69d86a0483c8edd1bf4190ff532412d | |
ceb24bc7b4c62b446c761dc998f7afffc33928ce | |
43ba1b331c957e1f78b3309716f9bb38dfc32422 | |
b40dc94a9a4717ae09f2ae66f150b491c4a2de02 | |
e4cbbe2a0eb8a2af0f74bdbbf927cfe89c01b91d | |
1f5848a460a6e462fefb0a60a28a6c60a8705cdd | |
--- | |
doc/configuration.txt | 95 ++++++++++++-- | |
include/proto/proto_tcp.h | 1 - | |
include/proto/protocols.h | 2 +- | |
include/types/global.h | 9 ++ | |
include/types/protocols.h | 3 +- | |
src/backend.c | 15 ++- | |
src/cfgparse.c | 321 ++++++++++++++++++++++++++++++++++++++------- | |
src/client.c | 19 ++- | |
src/dumpstats.c | 11 ++- | |
src/haproxy.c | 18 +++- | |
src/log.c | 10 +- | |
src/proto_http.c | 35 +++-- | |
src/proto_tcp.c | 29 ++++- | |
src/proto_uxst.c | 209 +++++++++++++++--------------- | |
src/protocols.c | 20 ++- | |
src/proxy.c | 26 +++- | |
16 files changed, 601 insertions(+), 222 deletions(-) | |
diff --git a/doc/configuration.txt b/doc/configuration.txt | |
index 9139d85..6e0928d 100644 | |
--- a/doc/configuration.txt | |
+++ b/doc/configuration.txt | |
@@ -440,6 +440,7 @@ The following keywords are supported in the "global" section : | |
- stats | |
- node | |
- description | |
+ - unix-bind | |
* Performance tuning | |
- maxconn | |
@@ -584,6 +585,20 @@ ulimit-n <number> | |
default, it is automatically computed, so it is recommended not to use this | |
option. | |
+unix-bind [ prefix <prefix> ] [ mode <mode> ] [ user <user> ] [ uid <uid> ] | |
+ [ group <group> ] [ gid <gid> ] | |
+ | |
+ Fixes common settings to UNIX listening sockets declared in "bind" statements. | |
+ This is mainly used to simplify declaration of those UNIX sockets and reduce | |
+ the risk of errors, since those settings are most commonly required but are | |
+ also process-specific. The <prefix> setting can be used to force all socket | |
+ path to be relative to that directory. This might be needed to access another | |
+ component's chroot. Note that those paths are resolved before haproxy chroots | |
+ itself, so they are absolute. The <mode>, <user>, <uid>, <group> and <gid> | |
+ all have the same meaning as their homonyms used by the "bind" statement. If | |
+ both are specified, the "bind" statement has priority, meaning that the | |
+ "unix-bind" settings may be seen as process-wide default settings. | |
+ | |
user <user name> | |
Similar to "uid" but uses the UID of user name <user name> from /etc/passwd. | |
See also "uid" and "group". | |
@@ -1319,6 +1334,10 @@ bind [<address>]:<port_range> [, ...] id <id> | |
bind [<address>]:<port_range> [, ...] name <name> | |
bind [<address>]:<port_range> [, ...] defer-accept | |
bind [<address>]:<port_range> [, ...] accept-proxy | |
+bind /<path> [, ...] | |
+bind /<path> [, ...] mode <mode> | |
+bind /<path> [, ...] [ user <user> | uid <uid> ] | |
+bind /<path> [, ...] [ group <user> | gid <gid> ] | |
Define one or several listening addresses and/or ports in a frontend. | |
May be used in sections : defaults | frontend | listen | backend | |
no | yes | yes | no | |
@@ -1331,9 +1350,9 @@ bind [<address>]:<port_range> [, ...] accept-proxy | |
<port_range> is either a unique TCP port, or a port range for which the | |
proxy will accept connections for the IP address specified | |
- above. The port is mandatory. Note that in the case of an | |
- IPv6 address, the port is always the number after the last | |
- colon (':'). A range can either be : | |
+ above. The port is mandatory for TCP listeners. Note that in | |
+ the case of an IPv6 address, the port is always the number | |
+ after the last colon (':'). A range can either be : | |
- a numerical port (ex: '80') | |
- a dash-delimited ports range explicitly stating the lower | |
and upper bounds (ex: '2000-2100') which are included in | |
@@ -1349,6 +1368,15 @@ bind [<address>]:<port_range> [, ...] accept-proxy | |
privileges to start the program, which are independant of | |
the 'uid' parameter. | |
+ <path> is a UNIX socket path beginning with a slash ('/'). This is | |
+ alternative to the TCP listening port. Haproxy will then | |
+ receive UNIX connections on the socket located at this place. | |
+ The path must begin with a slash and by default is absolute. | |
+ It can be relative to the prefix defined by "unix-bind" in | |
+ the global section. Note that the total length of the prefix | |
+ followed by the socket path cannot exceed some system limits | |
+ for UNIX sockets, which commonly are set to 107 characters. | |
+ | |
<interface> is an optional physical interface name. This is currently | |
only supported on Linux. The interface must be a physical | |
interface, not an aliased interface. When specified, all | |
@@ -1357,7 +1385,8 @@ bind [<address>]:<port_range> [, ...] accept-proxy | |
interface. It is also possible to bind multiple frontends to | |
the same address if they are bound to different interfaces. | |
Note that binding to a physical interface requires root | |
- privileges. | |
+ privileges. This parameter is only compatible with TCP | |
+ sockets. | |
<maxseg> is an optional TCP Maximum Segment Size (MSS) value to be | |
advertised on incoming connections. This can be used to force | |
@@ -1367,6 +1396,7 @@ bind [<address>]:<port_range> [, ...] accept-proxy | |
was buggy in all versions prior to 2.6.28. It may or may not | |
work on other operating systems. The commonly advertised | |
value on Ethernet networks is 1460 = 1500(MTU) - 40(IP+TCP). | |
+ This parameter is only compatible with TCP sockets. | |
<id> is a persistent value for socket ID. Must be positive and | |
unique in the proxy. An unused value will automatically be | |
@@ -1375,6 +1405,31 @@ bind [<address>]:<port_range> [, ...] accept-proxy | |
<name> is an optional name provided for stats | |
+ <mode> is the octal mode used to define access permissions on the | |
+ UNIX socket. It can also be set by default in the global | |
+ section's "unix-bind" statement. Note that some platforms | |
+ simply ignore this. | |
+ | |
+ <user> is the name of user that will be marked owner of the UNIX | |
+ socket. It can also be set by default in the global | |
+ section's "unix-bind" statement. Note that some platforms | |
+ simply ignore this. | |
+ | |
+ <group> is the name of a group that will be used to create the UNIX | |
+ socket. It can also be set by default in the global section's | |
+ "unix-bind" statement. Note that some platforms simply ignore | |
+ this. | |
+ | |
+ <uid> is the uid of user that will be marked owner of the UNIX | |
+ socket. It can also be set by default in the global section's | |
+ "unix-bind" statement. Note that some platforms simply ignore | |
+ this. | |
+ | |
+ <gid> is the gid of a group that will be used to create the UNIX | |
+ socket. It can also be set by default in the global section's | |
+ "unix-bind" statement. Note that some platforms simply ignore | |
+ this. | |
+ | |
transparent is an optional keyword which is supported only on certain | |
Linux kernels. It indicates that the addresses will be bound | |
even if they do not belong to the local machine. Any packet | |
@@ -1383,7 +1438,8 @@ bind [<address>]:<port_range> [, ...] accept-proxy | |
that IP forwarding is enabled. Caution! do not use this with | |
the default address '*', as it would redirect any traffic for | |
the specified port. This keyword is available only when | |
- HAProxy is built with USE_LINUX_TPROXY=1. | |
+ HAProxy is built with USE_LINUX_TPROXY=1. This parameter is | |
+ only compatible with TCP sockets. | |
defer-accept is an optional keyword which is supported only on certain | |
Linux kernels. It states that a connection will only be | |
@@ -1423,8 +1479,9 @@ bind [<address>]:<port_range> [, ...] accept-proxy | |
bind :80,:443 | |
bind 10.0.0.1:10080,10.0.0.1:10443 | |
bind 127.0.0.1:8443 accept-proxy | |
+ bind /var/run/ssl-frontend.sock user root mode 600 accept-proxy | |
- See also : "source", "option forwardfor" and the PROXY protocol | |
+ See also : "source", "option forwardfor", "unix-bind" and the PROXY protocol | |
documentation. | |
@@ -7084,6 +7141,10 @@ Detailed fields description : | |
and processed the connection. | |
- "mode is the mode the frontend is operating (TCP or HTTP). | |
+In case of a UNIX socket, the source and destination addresses are marked as | |
+"unix:" and the ports reflect the internal ID of the socket which accepted the | |
+connection (the same ID as reported in the stats). | |
+ | |
It is advised not to use this deprecated format for newer installations as it | |
will eventually disappear. | |
@@ -7132,11 +7193,16 @@ marked with a star ('*') after the field name below. | |
Detailed fields description : | |
- "client_ip" is the IP address of the client which initiated the TCP | |
- connection to haproxy. Note that when the connection is accepted on a | |
- socket configured with "accept-proxy" and the PROXY protocol is correctly | |
- used, then the logs will reflect the forwarded connection's information. | |
+ connection to haproxy. If the connection was accepted on a UNIX socket | |
+ instead, the IP address would be replaced with the word "unix". Note that | |
+ when the connection is accepted on a socket configured with "accept-proxy" | |
+ and the PROXY protocol is correctly used, then the logs will reflect the | |
+ forwarded connection's information. | |
- "client_port" is the TCP port of the client which initiated the connection. | |
+ If the connection was accepted on a UNIX socket instead, the port would be | |
+ replaced with the ID of the accepting socket, which is also reported in the | |
+ stats interface. | |
- "accept_date" is the exact date when the connection was received by haproxy | |
(which might be very slightly different from the date observed on the | |
@@ -7307,11 +7373,16 @@ with a star ('*') after the field name below. | |
Detailed fields description : | |
- "client_ip" is the IP address of the client which initiated the TCP | |
- connection to haproxy. Note that when the connection is accepted on a | |
- socket configured with "accept-proxy" and the PROXY protocol is correctly | |
- used, then the logs will reflect the forwarded connection's information. | |
+ connection to haproxy. If the connection was accepted on a UNIX socket | |
+ instead, the IP address would be replaced with the word "unix". Note that | |
+ when the connection is accepted on a socket configured with "accept-proxy" | |
+ and the PROXY protocol is correctly used, then the logs will reflect the | |
+ forwarded connection's information. | |
- "client_port" is the TCP port of the client which initiated the connection. | |
+ If the connection was accepted on a UNIX socket instead, the port would be | |
+ replaced with the ID of the accepting socket, which is also reported in the | |
+ stats interface. | |
- "accept_date" is the exact date when the TCP connection was received by | |
haproxy (which might be very slightly different from the date observed on | |
diff --git a/include/proto/proto_tcp.h b/include/proto/proto_tcp.h | |
index ba48f4f..ef43098 100644 | |
--- a/include/proto/proto_tcp.h | |
+++ b/include/proto/proto_tcp.h | |
@@ -30,7 +30,6 @@ int tcp_event_accept(int fd); | |
int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct sockaddr_in *remote); | |
void tcpv4_add_listener(struct listener *listener); | |
void tcpv6_add_listener(struct listener *listener); | |
-int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen); | |
int tcpv4_connect_server(struct stream_interface *si, | |
struct proxy *be, struct server *srv, | |
struct sockaddr *srv_addr, struct sockaddr *from_addr); | |
diff --git a/include/proto/protocols.h b/include/proto/protocols.h | |
index cd54ec8..d0364c0 100644 | |
--- a/include/proto/protocols.h | |
+++ b/include/proto/protocols.h | |
@@ -82,7 +82,7 @@ void protocol_unregister(struct protocol *proto); | |
/* binds all listeneres of all registered protocols. Returns a composition | |
* of ERR_NONE, ERR_RETRYABLE, ERR_FATAL. | |
*/ | |
-int protocol_bind_all(void); | |
+int protocol_bind_all(char *errmsg, int errlen); | |
/* unbinds all listeners of all registered protocols. They are also closed. | |
* This must be performed before calling exit() in order to get a chance to | |
diff --git a/include/types/global.h b/include/types/global.h | |
index 6d6e267..476a40e 100644 | |
--- a/include/types/global.h | |
+++ b/include/types/global.h | |
@@ -93,6 +93,15 @@ struct global { | |
int server_rcvbuf; /* set server rcvbuf to this value if not null */ | |
int chksize; /* check buffer size in bytes, defaults to BUFSIZE */ | |
} tune; | |
+ struct { | |
+ char *prefix; /* path prefix of unix bind socket */ | |
+ struct { /* UNIX socket permissions */ | |
+ uid_t uid; /* -1 to leave unchanged */ | |
+ gid_t gid; /* -1 to leave unchanged */ | |
+ mode_t mode; /* 0 to leave unchanged */ | |
+ int level; /* access level (ACCESS_LVL_*) */ | |
+ } ux; | |
+ } unix_bind; | |
struct listener stats_sock; /* unix socket listener for statistics */ | |
struct proxy *stats_fe; /* the frontend holding the stats settings */ | |
}; | |
diff --git a/include/types/protocols.h b/include/types/protocols.h | |
index ed0d403..731f36c 100644 | |
--- a/include/types/protocols.h | |
+++ b/include/types/protocols.h | |
@@ -132,7 +132,8 @@ struct protocol { | |
int l3_addrlen; /* layer3 address length, used by hashes */ | |
int (*read)(int fd); /* generic read function */ | |
int (*write)(int fd); /* generic write function */ | |
- int (*bind_all)(struct protocol *proto); /* bind all unbound listeners */ | |
+ int (*bind)(struct listener *l, char *errmsg, int errlen); /* bind a listener */ | |
+ int (*bind_all)(struct protocol *proto, char *errmsg, int errlen); /* bind all unbound listeners */ | |
int (*unbind_all)(struct protocol *proto); /* unbind all bound listeners */ | |
int (*enable_all)(struct protocol *proto); /* enable all bound listeners */ | |
int (*disable_all)(struct protocol *proto); /* disable all bound listeners */ | |
diff --git a/src/backend.c b/src/backend.c | |
index 1f67778..2a5fba8 100644 | |
--- a/src/backend.c | |
+++ b/src/backend.c | |
@@ -668,7 +668,9 @@ int assign_server_address(struct session *s) | |
if (!(s->be->options & PR_O_TRANSP) && !(s->flags & SN_FRT_ADDR_SET)) | |
get_frt_addr(s); | |
- s->srv_addr.sin_addr = ((struct sockaddr_in *)&s->frt_addr)->sin_addr; | |
+ if (s->frt_addr.ss_family == AF_INET) { | |
+ s->srv_addr.sin_addr = ((struct sockaddr_in *)&s->frt_addr)->sin_addr; | |
+ } | |
} | |
/* if this server remaps proxied ports, we'll use | |
@@ -679,7 +681,8 @@ int assign_server_address(struct session *s) | |
if (s->frt_addr.ss_family == AF_INET) { | |
s->srv_addr.sin_port = htons(ntohs(s->srv_addr.sin_port) + | |
ntohs(((struct sockaddr_in *)&s->frt_addr)->sin_port)); | |
- } else { | |
+ } | |
+ else if (s->frt_addr.ss_family == AF_INET6) { | |
s->srv_addr.sin_port = htons(ntohs(s->srv_addr.sin_port) + | |
ntohs(((struct sockaddr_in6 *)&s->frt_addr)->sin6_port)); | |
} | |
@@ -694,7 +697,9 @@ int assign_server_address(struct session *s) | |
if (!(s->flags & SN_FRT_ADDR_SET)) | |
get_frt_addr(s); | |
- memcpy(&s->srv_addr, &s->frt_addr, MIN(sizeof(s->srv_addr), sizeof(s->frt_addr))); | |
+ if (s->frt_addr.ss_family == AF_INET) { | |
+ memcpy(&s->srv_addr, &s->frt_addr, MIN(sizeof(s->srv_addr), sizeof(s->frt_addr))); | |
+ } | |
/* when we support IPv6 on the backend, we may add other tests */ | |
//qfprintf(stderr, "Cannot get original server address.\n"); | |
//return SRV_STATUS_INTERNAL; | |
@@ -845,7 +850,7 @@ static void assign_tproxy_address(struct session *s) | |
break; | |
case SRV_TPROXY_CLI: | |
case SRV_TPROXY_CIP: | |
- /* FIXME: what can we do if the client connects in IPv6 ? */ | |
+ /* FIXME: what can we do if the client connects in IPv6 or unix socket ? */ | |
s->from_addr = *(struct sockaddr_in *)&s->cli_addr; | |
break; | |
case SRV_TPROXY_DYN: | |
@@ -870,7 +875,7 @@ static void assign_tproxy_address(struct session *s) | |
break; | |
case PR_O_TPXY_CLI: | |
case PR_O_TPXY_CIP: | |
- /* FIXME: what can we do if the client connects in IPv6 ? */ | |
+ /* FIXME: what can we do if the client connects in IPv6 or socket unix? */ | |
s->from_addr = *(struct sockaddr_in *)&s->cli_addr; | |
break; | |
case PR_O_TPXY_DYN: | |
diff --git a/src/cfgparse.c b/src/cfgparse.c | |
index 0eafce7..9ffadde 100644 | |
--- a/src/cfgparse.c | |
+++ b/src/cfgparse.c | |
@@ -52,6 +52,7 @@ | |
#include <proto/port_range.h> | |
#include <proto/protocols.h> | |
#include <proto/proto_tcp.h> | |
+#include <proto/proto_uxst.h> | |
#include <proto/proto_http.h> | |
#include <proto/proxy.h> | |
#include <proto/server.h> | |
@@ -190,63 +191,87 @@ static int str2listener(char *str, struct proxy *curproxy) | |
*next++ = 0; | |
} | |
- /* 2) look for the addr/port delimiter, it's the last colon. */ | |
- if ((range = strrchr(str, ':')) == NULL) { | |
- Alert("Missing port number: '%s'\n", str); | |
- goto fail; | |
- } | |
+ if (*str == '/') { | |
+ /* sun_path during a soft_stop rename is <unix_bind_prefix><path>.<pid>.<bak|tmp> */ | |
+ /* so compute max path */ | |
+ int prefix_path_len = global.unix_bind.prefix ? strlen(global.unix_bind.prefix) : 0; | |
+ int max_path_len = (sizeof(((struct sockaddr_un *)&ss)->sun_path) - 1) - (prefix_path_len + 1 + 5 + 1 + 3); | |
- *range++ = 0; | |
+ if (strlen(str) > max_path_len) { | |
+ Alert("Socket path '%s' too long (max %d)\n", str, max_path_len); | |
+ goto fail; | |
+ } | |
- if (strrchr(str, ':') != NULL) { | |
- /* IPv6 address contains ':' */ | |
memset(&ss, 0, sizeof(ss)); | |
- ss.ss_family = AF_INET6; | |
- | |
- if (!inet_pton(ss.ss_family, str, &((struct sockaddr_in6 *)&ss)->sin6_addr)) { | |
- Alert("Invalid server address: '%s'\n", str); | |
- goto fail; | |
+ ss.ss_family = AF_UNIX; | |
+ if (global.unix_bind.prefix) { | |
+ memcpy(((struct sockaddr_un *)&ss)->sun_path, global.unix_bind.prefix, prefix_path_len); | |
+ strcpy(((struct sockaddr_un *)&ss)->sun_path+prefix_path_len, str); | |
} | |
+ else { | |
+ strcpy(((struct sockaddr_un *)&ss)->sun_path, str); | |
+ } | |
+ port = end = 0; | |
} | |
else { | |
- memset(&ss, 0, sizeof(ss)); | |
- ss.ss_family = AF_INET; | |
- | |
- if (*str == '*' || *str == '\0') { /* INADDR_ANY */ | |
- ((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY; | |
+ /* 2) look for the addr/port delimiter, it's the last colon. */ | |
+ if ((range = strrchr(str, ':')) == NULL) { | |
+ Alert("Missing port number: '%s'\n", str); | |
+ goto fail; | |
} | |
- else if (!inet_pton(ss.ss_family, str, &((struct sockaddr_in *)&ss)->sin_addr)) { | |
- struct hostent *he; | |
- | |
- if ((he = gethostbyname(str)) == NULL) { | |
- Alert("Invalid server name: '%s'\n", str); | |
+ | |
+ *range++ = 0; | |
+ | |
+ if (strrchr(str, ':') != NULL) { | |
+ /* IPv6 address contains ':' */ | |
+ memset(&ss, 0, sizeof(ss)); | |
+ ss.ss_family = AF_INET6; | |
+ | |
+ if (!inet_pton(ss.ss_family, str, &((struct sockaddr_in6 *)&ss)->sin6_addr)) { | |
+ Alert("Invalid server address: '%s'\n", str); | |
goto fail; | |
} | |
- else | |
- ((struct sockaddr_in *)&ss)->sin_addr = | |
- *(struct in_addr *) *(he->h_addr_list); | |
} | |
- } | |
+ else { | |
+ memset(&ss, 0, sizeof(ss)); | |
+ ss.ss_family = AF_INET; | |
- /* 3) look for the port-end delimiter */ | |
- if ((c = strchr(range, '-')) != NULL) { | |
- *c++ = 0; | |
- end = atol(c); | |
- } | |
- else { | |
- end = atol(range); | |
- } | |
+ if (*str == '*' || *str == '\0') { /* INADDR_ANY */ | |
+ ((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY; | |
+ } | |
+ else if (!inet_pton(ss.ss_family, str, &((struct sockaddr_in *)&ss)->sin_addr)) { | |
+ struct hostent *he; | |
- port = atol(range); | |
+ if ((he = gethostbyname(str)) == NULL) { | |
+ Alert("Invalid server name: '%s'\n", str); | |
+ goto fail; | |
+ } | |
+ else | |
+ ((struct sockaddr_in *)&ss)->sin_addr = | |
+ *(struct in_addr *) *(he->h_addr_list); | |
+ } | |
+ } | |
- if (port < 1 || port > 65535) { | |
- Alert("Invalid port '%d' specified for address '%s'.\n", port, str); | |
- goto fail; | |
- } | |
+ /* 3) look for the port-end delimiter */ | |
+ if ((c = strchr(range, '-')) != NULL) { | |
+ *c++ = 0; | |
+ end = atol(c); | |
+ } | |
+ else { | |
+ end = atol(range); | |
+ } | |
- if (end < 1 || end > 65535) { | |
- Alert("Invalid port '%d' specified for address '%s'.\n", end, str); | |
- goto fail; | |
+ port = atol(range); | |
+ | |
+ if (port < 1 || port > 65535) { | |
+ Alert("Invalid port '%d' specified for address '%s'.\n", port, str); | |
+ goto fail; | |
+ } | |
+ | |
+ if (end < 1 || end > 65535) { | |
+ Alert("Invalid port '%d' specified for address '%s'.\n", end, str); | |
+ goto fail; | |
+ } | |
} | |
for (; port <= end; port++) { | |
@@ -258,13 +283,19 @@ static int str2listener(char *str, struct proxy *curproxy) | |
l->addr = ss; | |
l->state = LI_INIT; | |
- if (ss.ss_family == AF_INET6) { | |
- ((struct sockaddr_in6 *)(&l->addr))->sin6_port = htons(port); | |
- tcpv6_add_listener(l); | |
- } else { | |
+ if(ss.ss_family == AF_INET) { | |
((struct sockaddr_in *)(&l->addr))->sin_port = htons(port); | |
tcpv4_add_listener(l); | |
} | |
+ else if (ss.ss_family == AF_INET6) { | |
+ ((struct sockaddr_in6 *)(&l->addr))->sin6_port = htons(port); | |
+ tcpv6_add_listener(l); | |
+ } | |
+ else { | |
+ l->perm.ux.gid = l->perm.ux.uid = -1; | |
+ l->perm.ux.mode = 0; | |
+ uxst_add_listener(l); | |
+ } | |
listeners++; | |
} /* end for(port) */ | |
@@ -768,6 +799,86 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm) | |
} | |
global.pidfile = strdup(args[1]); | |
} | |
+ else if (!strcmp(args[0], "unix-bind")) { | |
+ int cur_arg = 1; | |
+ while (*(args[cur_arg])) { | |
+ if (!strcmp(args[cur_arg], "prefix")) { | |
+ if (global.unix_bind.prefix != NULL) { | |
+ Alert("parsing [%s:%d] : unix-bind '%s' already specified. Continuing.\n", file, linenum, args[cur_arg]); | |
+ err_code |= ERR_ALERT; | |
+ cur_arg += 2; | |
+ continue; | |
+ } | |
+ | |
+ if (*(args[cur_arg+1]) == 0) { | |
+ Alert("parsing [%s:%d] : unix_bind '%s' expects a path as an argument.\n", file, linenum, args[cur_arg]); | |
+ err_code |= ERR_ALERT | ERR_FATAL; | |
+ goto out; | |
+ } | |
+ global.unix_bind.prefix = strdup(args[cur_arg+1]); | |
+ cur_arg += 2; | |
+ continue; | |
+ } | |
+ | |
+ if (!strcmp(args[cur_arg], "mode")) { | |
+ | |
+ global.unix_bind.ux.mode = strtol(args[cur_arg + 1], NULL, 8); | |
+ cur_arg += 2; | |
+ continue; | |
+ } | |
+ | |
+ if (!strcmp(args[cur_arg], "uid")) { | |
+ | |
+ global.unix_bind.ux.uid = atol(args[cur_arg + 1 ]); | |
+ cur_arg += 2; | |
+ continue; | |
+ } | |
+ | |
+ if (!strcmp(args[cur_arg], "gid")) { | |
+ | |
+ global.unix_bind.ux.gid = atol(args[cur_arg + 1 ]); | |
+ cur_arg += 2; | |
+ continue; | |
+ } | |
+ | |
+ if (!strcmp(args[cur_arg], "user")) { | |
+ struct passwd *user; | |
+ | |
+ user = getpwnam(args[cur_arg + 1]); | |
+ if (!user) { | |
+ Alert("parsing [%s:%d] : '%s' : '%s' unknown user.\n", | |
+ file, linenum, args[0], args[cur_arg + 1 ]); | |
+ err_code |= ERR_ALERT | ERR_FATAL; | |
+ goto out; | |
+ } | |
+ | |
+ global.unix_bind.ux.uid = user->pw_uid; | |
+ cur_arg += 2; | |
+ continue; | |
+ } | |
+ | |
+ if (!strcmp(args[cur_arg], "group")) { | |
+ struct group *group; | |
+ | |
+ group = getgrnam(args[cur_arg + 1]); | |
+ if (!group) { | |
+ Alert("parsing [%s:%d] : '%s' : '%s' unknown group.\n", | |
+ file, linenum, args[0], args[cur_arg + 1 ]); | |
+ err_code |= ERR_ALERT | ERR_FATAL; | |
+ goto out; | |
+ } | |
+ | |
+ global.unix_bind.ux.gid = group->gr_gid; | |
+ cur_arg += 2; | |
+ continue; | |
+ } | |
+ | |
+ Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'accept-proxy', 'defer-accept', 'name', 'id', 'mss' and 'interface' options.\n", | |
+ file, linenum, args[0]); | |
+ err_code |= ERR_ALERT | ERR_FATAL; | |
+ goto out; | |
+ } | |
+ } | |
else if (!strcmp(args[0], "log")) { /* syslog server address */ | |
struct logsrv logsrv; | |
int facility, level, minlvl; | |
@@ -1299,8 +1410,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) | |
if (warnifnotcap(curproxy, PR_CAP_FE, file, linenum, args[0], NULL)) | |
err_code |= ERR_WARN; | |
- if (strchr(args[1], ':') == NULL) { | |
- Alert("parsing [%s:%d] : '%s' expects [addr1]:port1[-end1]{,[addr]:port[-end]}... as arguments.\n", | |
+ if ( *(args[1]) != '/' && strchr(args[1], ':') == NULL) { | |
+ Alert("parsing [%s:%d] : '%s' expects {<path>|[addr1]:port1[-end1]}{,[addr]:port[-end]}... as arguments.\n", | |
file, linenum, args[0]); | |
err_code |= ERR_ALERT | ERR_FATAL; | |
goto out; | |
@@ -1319,12 +1430,23 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) | |
new_listen = new_listen->next; | |
} | |
+ /* Set default global rights and owner for unix bind */ | |
+ if (curproxy->listen->addr.ss_family == AF_UNIX) { | |
+ memcpy(&(curproxy->listen->perm.ux), &(global.unix_bind.ux), sizeof(global.unix_bind.ux)); | |
+ } | |
cur_arg = 2; | |
while (*(args[cur_arg])) { | |
if (!strcmp(args[cur_arg], "interface")) { /* specifically bind to this interface */ | |
#ifdef SO_BINDTODEVICE | |
struct listener *l; | |
+ if (curproxy->listen->addr.ss_family == AF_UNIX) { | |
+ Alert("parsing [%s:%d] : '%s' : '%s' option not supported on unix sockets.\n", | |
+ file, linenum, args[0], args[cur_arg]); | |
+ err_code |= ERR_ALERT | ERR_FATAL; | |
+ goto out; | |
+ } | |
+ | |
if (!*args[cur_arg + 1]) { | |
Alert("parsing [%s:%d] : '%s' : missing interface name.\n", | |
file, linenum, args[0]); | |
@@ -1351,6 +1473,13 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) | |
struct listener *l; | |
int mss; | |
+ if (curproxy->listen->addr.ss_family == AF_UNIX) { | |
+ Alert("parsing [%s:%d] : '%s' : '%s' option not supported on unix sockets.\n", | |
+ file, linenum, args[0], args[cur_arg]); | |
+ err_code |= ERR_ALERT | ERR_FATAL; | |
+ goto out; | |
+ } | |
+ | |
if (!*args[cur_arg + 1]) { | |
Alert("parsing [%s:%d] : '%s' : missing MSS value.\n", | |
file, linenum, args[0]); | |
@@ -1400,6 +1529,13 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) | |
#ifdef CONFIG_HAP_LINUX_TPROXY | |
struct listener *l; | |
+ if (curproxy->listen->addr.ss_family == AF_UNIX) { | |
+ Alert("parsing [%s:%d] : '%s' : '%s' option not supported on unix sockets.\n", | |
+ file, linenum, args[0], args[cur_arg]); | |
+ err_code |= ERR_ALERT | ERR_FATAL; | |
+ goto out; | |
+ } | |
+ | |
for (l = curproxy->listen; l != last_listen; l = l->next) | |
l->options |= LI_O_FOREIGN; | |
@@ -1475,6 +1611,93 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) | |
continue; | |
} | |
+ if (!strcmp(args[cur_arg], "mode")) { | |
+ | |
+ if (curproxy->listen->addr.ss_family != AF_UNIX) { | |
+ Alert("parsing [%s:%d] : '%s' : '%s' option only supported on unix sockets.\n", | |
+ file, linenum, args[0], args[cur_arg]); | |
+ err_code |= ERR_ALERT | ERR_FATAL; | |
+ goto out; | |
+ } | |
+ | |
+ curproxy->listen->perm.ux.mode = strtol(args[cur_arg + 1], NULL, 8); | |
+ | |
+ cur_arg += 2; | |
+ continue; | |
+ } | |
+ | |
+ if (!strcmp(args[cur_arg], "uid")) { | |
+ | |
+ if (curproxy->listen->addr.ss_family != AF_UNIX) { | |
+ Alert("parsing [%s:%d] : '%s' : '%s' option only supported on unix sockets.\n", | |
+ file, linenum, args[0], args[cur_arg]); | |
+ err_code |= ERR_ALERT | ERR_FATAL; | |
+ goto out; | |
+ } | |
+ | |
+ curproxy->listen->perm.ux.uid = atol(args[cur_arg + 1 ]); | |
+ cur_arg += 2; | |
+ continue; | |
+ } | |
+ | |
+ if (!strcmp(args[cur_arg], "gid")) { | |
+ | |
+ if (curproxy->listen->addr.ss_family != AF_UNIX) { | |
+ Alert("parsing [%s:%d] : '%s' : '%s' option only supported on unix sockets.\n", | |
+ file, linenum, args[0], args[cur_arg]); | |
+ err_code |= ERR_ALERT | ERR_FATAL; | |
+ goto out; | |
+ } | |
+ | |
+ curproxy->listen->perm.ux.gid = atol(args[cur_arg + 1 ]); | |
+ cur_arg += 2; | |
+ continue; | |
+ } | |
+ | |
+ if (!strcmp(args[cur_arg], "user")) { | |
+ struct passwd *user; | |
+ | |
+ if (curproxy->listen->addr.ss_family != AF_UNIX) { | |
+ Alert("parsing [%s:%d] : '%s' : '%s' option only supported on unix sockets.\n", | |
+ file, linenum, args[0], args[cur_arg]); | |
+ err_code |= ERR_ALERT | ERR_FATAL; | |
+ goto out; | |
+ } | |
+ user = getpwnam(args[cur_arg + 1]); | |
+ if (!user) { | |
+ Alert("parsing [%s:%d] : '%s' : '%s' unknown user.\n", | |
+ file, linenum, args[0], args[cur_arg + 1 ]); | |
+ err_code |= ERR_ALERT | ERR_FATAL; | |
+ goto out; | |
+ } | |
+ | |
+ curproxy->listen->perm.ux.uid = user->pw_uid; | |
+ cur_arg += 2; | |
+ continue; | |
+ } | |
+ | |
+ if (!strcmp(args[cur_arg], "group")) { | |
+ struct group *group; | |
+ | |
+ if (curproxy->listen->addr.ss_family != AF_UNIX) { | |
+ Alert("parsing [%s:%d] : '%s' : '%s' option only supported on unix sockets.\n", | |
+ file, linenum, args[0], args[cur_arg]); | |
+ err_code |= ERR_ALERT | ERR_FATAL; | |
+ goto out; | |
+ } | |
+ group = getgrnam(args[cur_arg + 1]); | |
+ if (!group) { | |
+ Alert("parsing [%s:%d] : '%s' : '%s' unknown group.\n", | |
+ file, linenum, args[0], args[cur_arg + 1 ]); | |
+ err_code |= ERR_ALERT | ERR_FATAL; | |
+ goto out; | |
+ } | |
+ | |
+ curproxy->listen->perm.ux.gid = group->gr_gid; | |
+ cur_arg += 2; | |
+ continue; | |
+ } | |
+ | |
Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'accept-proxy', 'defer-accept', 'name', 'id', 'mss' and 'interface' options.\n", | |
file, linenum, args[0]); | |
err_code |= ERR_ALERT | ERR_FATAL; | |
diff --git a/src/client.c b/src/client.c | |
index 385a136..ee90271 100644 | |
--- a/src/client.c | |
+++ b/src/client.c | |
@@ -348,8 +348,8 @@ int event_accept(int fd) { | |
} | |
if ((fcntl(cfd, F_SETFL, O_NONBLOCK) == -1) || | |
- (setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, | |
- (char *) &one, sizeof(one)) == -1)) { | |
+ ((s->listener->addr.ss_family != AF_UNIX) && (setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, | |
+ (char *) &one, sizeof(one)) == -1))) { | |
Alert("accept(): cannot set the socket in non blocking mode. Giving up\n"); | |
goto out_free_task; | |
} | |
@@ -518,7 +518,7 @@ int event_accept(int fd) { | |
p->id, (p->mode == PR_MODE_HTTP) ? "HTTP" : "TCP"); | |
} | |
} | |
- else { | |
+ else if (s->cli_addr.ss_family == AF_INET6) { | |
char pn[INET6_ADDRSTRLEN], sn[INET6_ADDRSTRLEN]; | |
if (!(s->flags & SN_FRT_ADDR_SET)) | |
@@ -534,6 +534,12 @@ int event_accept(int fd) { | |
p->id, (p->mode == PR_MODE_HTTP) ? "HTTP" : "TCP"); | |
} | |
} | |
+ else { | |
+ /* UNIX socket, only the destination is known */ | |
+ send_log(s->fe, LOG_INFO, "Connect to unix:%d (%s/%s)\n", | |
+ s->listener->luid, | |
+ s->fe->id, (s->fe->mode == PR_MODE_HTTP) ? "HTTP" : "TCP"); | |
+ } | |
} | |
if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) { | |
@@ -552,7 +558,7 @@ int event_accept(int fd) { | |
s->uniq_id, p->id, (unsigned short)fd, (unsigned short)cfd, | |
pn, ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port)); | |
} | |
- else { | |
+ else if (s->cli_addr.ss_family == AF_INET6) { | |
char pn[INET6_ADDRSTRLEN]; | |
inet_ntop(AF_INET6, | |
(const void *)&((struct sockaddr_in6 *)(&s->cli_addr))->sin6_addr, | |
@@ -562,6 +568,11 @@ int event_accept(int fd) { | |
s->uniq_id, p->id, (unsigned short)fd, (unsigned short)cfd, | |
pn, ntohs(((struct sockaddr_in6 *)(&s->cli_addr))->sin6_port)); | |
} | |
+ else { | |
+ len = sprintf(trash, "%08x:%s.accept(%04x)=%04x from [unix:%d]\n", | |
+ s->uniq_id, s->fe->id, (unsigned short)s->listener->fd, (unsigned short)cfd, | |
+ s->listener->luid); | |
+ } | |
write(1, trash, len); | |
} | |
diff --git a/src/dumpstats.c b/src/dumpstats.c | |
index b3a3ff6..af39bec 100644 | |
--- a/src/dumpstats.c | |
+++ b/src/dumpstats.c | |
@@ -2530,6 +2530,9 @@ int stats_dump_full_sess_to_buffer(struct session *s, struct buffer *rep) | |
ntohs(((struct sockaddr_in6 *)&sess->cli_addr)->sin6_port)); | |
break; | |
case AF_UNIX: | |
+ chunk_printf(&msg, | |
+ " source=unix:%d\n", sess->listener->luid); | |
+ break; | |
default: | |
/* no more information to print right now */ | |
chunk_printf(&msg, "\n"); | |
@@ -2775,7 +2778,13 @@ int stats_dump_sess_to_buffer(struct session *s, struct buffer *rep) | |
break; | |
case AF_UNIX: | |
- /* no more information to print right now */ | |
+ chunk_printf(&msg, | |
+ " src=unix:%d fe=%s be=%s srv=%s", | |
+ curr_sess->listener->luid, | |
+ curr_sess->fe->id, | |
+ curr_sess->be->id, | |
+ curr_sess->srv ? curr_sess->srv->id : "<none>" | |
+ ); | |
break; | |
} | |
diff --git a/src/haproxy.c b/src/haproxy.c | |
index d251897..de9904d 100644 | |
--- a/src/haproxy.c | |
+++ b/src/haproxy.c | |
@@ -115,6 +115,13 @@ struct global global = { | |
} | |
} | |
}, | |
+ .unix_bind = { | |
+ .ux = { | |
+ .uid = -1, | |
+ .gid = -1, | |
+ .mode = 0, | |
+ } | |
+ }, | |
.tune = { | |
.bufsize = BUFSIZE, | |
.maxrewrite = MAXREWRITE, | |
@@ -993,8 +1000,9 @@ int main(int argc, char **argv) | |
int err, retry; | |
struct rlimit limit; | |
FILE *pidfile = NULL; | |
- init(argc, argv); | |
+ char errmsg[100]; | |
+ init(argc, argv); | |
signal_register(SIGQUIT, dump); | |
signal_register(SIGUSR1, sig_soft_stop); | |
signal_register(SIGHUP, sig_dump_state); | |
@@ -1056,12 +1064,18 @@ int main(int argc, char **argv) | |
exit(1); | |
} | |
- if ((protocol_bind_all() & ~ERR_WARN) != ERR_NONE) { | |
+ err = protocol_bind_all(errmsg, sizeof(errmsg)); | |
+ if ((err & ~ERR_WARN) != ERR_NONE) { | |
+ if ((err & ERR_ALERT) || (err & ERR_WARN)) | |
+ Alert("[%s.main()] %s.\n", argv[0], errmsg); | |
+ | |
Alert("[%s.main()] Some protocols failed to start their listeners! Exiting.\n", argv[0]); | |
protocol_unbind_all(); /* cleanup everything we can */ | |
if (nb_oldpids) | |
tell_old_pids(SIGTTIN); | |
exit(1); | |
+ } else if (err & ERR_WARN) { | |
+ Alert("[%s.main()] %s.\n", argv[0], errmsg); | |
} | |
/* prepare pause/play signals */ | |
diff --git a/src/log.c b/src/log.c | |
index b323d7e..d86e2b2 100644 | |
--- a/src/log.c | |
+++ b/src/log.c | |
@@ -335,7 +335,7 @@ void tcp_sess_log(struct session *s) | |
inet_ntop(AF_INET, | |
(const void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr, | |
pn, sizeof(pn)); | |
- else | |
+ else if (s->cli_addr.ss_family == AF_INET6) | |
inet_ntop(AF_INET6, | |
(const void *)&((struct sockaddr_in6 *)(&s->cli_addr))->sin6_addr, | |
pn, sizeof(pn)); | |
@@ -356,10 +356,10 @@ void tcp_sess_log(struct session *s) | |
send_log(prx_log, level, "%s:%d [%02d/%s/%04d:%02d:%02d:%02d.%03d]" | |
" %s %s/%s %ld/%ld/%s%ld %s%lld" | |
" %c%c %d/%d/%d/%d/%s%u %ld/%ld\n", | |
- pn, | |
- (s->cli_addr.ss_family == AF_INET) ? | |
- ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port) : | |
- ntohs(((struct sockaddr_in6 *)&s->cli_addr)->sin6_port), | |
+ s->cli_addr.ss_family == AF_UNIX ? "unix" : pn, | |
+ s->cli_addr.ss_family == AF_UNIX ? s->listener->luid : (ntohs((s->cli_addr.ss_family == AF_INET) ? | |
+ ((struct sockaddr_in *)&s->cli_addr)->sin_port : | |
+ ((struct sockaddr_in6 *)&s->cli_addr)->sin6_port)), | |
tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900, | |
tm.tm_hour, tm.tm_min, tm.tm_sec, (int)s->logs.tv_accept.tv_usec/1000, | |
fe->id, be->id, svid, | |
diff --git a/src/proto_http.c b/src/proto_http.c | |
index 8170c1a..d84abca 100644 | |
--- a/src/proto_http.c | |
+++ b/src/proto_http.c | |
@@ -869,10 +869,12 @@ void http_sess_clflog(struct session *s) | |
inet_ntop(AF_INET, | |
(const void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr, | |
pn, sizeof(pn)); | |
- else | |
+ else if (s->cli_addr.ss_family == AF_INET6) | |
inet_ntop(AF_INET6, | |
(const void *)&((struct sockaddr_in6 *)(&s->cli_addr))->sin6_addr, | |
pn, sizeof(pn)); | |
+ else | |
+ snprintf(pn, sizeof(pn), "unix:%d", s->listener->luid); | |
get_gmtime(s->logs.accept_date.tv_sec, &tm); | |
@@ -912,9 +914,10 @@ void http_sess_clflog(struct session *s) | |
w = snprintf(h, sizeof(tmpline) - (h - tmpline), | |
" %d %03d", | |
- (s->cli_addr.ss_family == AF_INET) ? | |
- ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port) : | |
- ntohs(((struct sockaddr_in6 *)&s->cli_addr)->sin6_port), | |
+ s->cli_addr.ss_family == AF_UNIX ? s->listener->luid : | |
+ ntohs((s->cli_addr.ss_family == AF_INET) ? | |
+ ((struct sockaddr_in *)&s->cli_addr)->sin_port : | |
+ ((struct sockaddr_in6 *)&s->cli_addr)->sin6_port), | |
(int)s->logs.accept_date.tv_usec/1000); | |
if (w < 0 || w >= sizeof(tmpline) - (h - tmpline)) | |
goto trunc; | |
@@ -1100,7 +1103,7 @@ void http_sess_log(struct session *s) | |
inet_ntop(AF_INET, | |
(const void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr, | |
pn, sizeof(pn)); | |
- else | |
+ else if (s->cli_addr.ss_family == AF_INET6) | |
inet_ntop(AF_INET6, | |
(const void *)&((struct sockaddr_in6 *)(&s->cli_addr))->sin6_addr, | |
pn, sizeof(pn)); | |
@@ -1167,10 +1170,11 @@ void http_sess_log(struct session *s) | |
"%s:%d [%02d/%s/%04d:%02d:%02d:%02d.%03d]" | |
" %s %s/%s %d/%ld/%ld/%ld/%s%ld %d %s%lld" | |
" %s %s %c%c%c%c %d/%d/%d/%d/%s%u %ld/%ld%s\n", | |
- pn, | |
- (s->cli_addr.ss_family == AF_INET) ? | |
- ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port) : | |
- ntohs(((struct sockaddr_in6 *)&s->cli_addr)->sin6_port), | |
+ (s->cli_addr.ss_family == AF_UNIX) ? "unix" : pn, | |
+ (s->cli_addr.ss_family == AF_UNIX) ? s->listener->luid : | |
+ ntohs((s->cli_addr.ss_family == AF_INET) ? | |
+ ((struct sockaddr_in *)&s->cli_addr)->sin_port : | |
+ ((struct sockaddr_in6 *)&s->cli_addr)->sin6_port), | |
tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900, | |
tm.tm_hour, tm.tm_min, tm.tm_sec, (int)s->logs.accept_date.tv_usec/1000, | |
fe->id, be->id, svid, | |
@@ -3508,12 +3512,13 @@ int http_process_request(struct session *s, struct buffer *req, int an_bit) | |
if (!(s->flags & SN_FRT_ADDR_SET)) | |
get_frt_addr(s); | |
- if ((!s->fe->except_mask_to.s_addr || | |
- (((struct sockaddr_in *)&s->frt_addr)->sin_addr.s_addr & s->fe->except_mask_to.s_addr) | |
- != s->fe->except_to.s_addr) && | |
- (!s->be->except_mask_to.s_addr || | |
- (((struct sockaddr_in *)&s->frt_addr)->sin_addr.s_addr & s->be->except_mask_to.s_addr) | |
- != s->be->except_to.s_addr)) { | |
+ if (s->frt_addr.ss_family == AF_INET && | |
+ ((!s->fe->except_mask_to.s_addr || | |
+ (((struct sockaddr_in *)&s->frt_addr)->sin_addr.s_addr & s->fe->except_mask_to.s_addr) | |
+ != s->fe->except_to.s_addr) && | |
+ (!s->be->except_mask_to.s_addr || | |
+ (((struct sockaddr_in *)&s->frt_addr)->sin_addr.s_addr & s->be->except_mask_to.s_addr) | |
+ != s->be->except_to.s_addr))) { | |
int len; | |
unsigned char *pn; | |
pn = (unsigned char *)&((struct sockaddr_in *)&s->frt_addr)->sin_addr; | |
diff --git a/src/proto_tcp.c b/src/proto_tcp.c | |
index 37d9054..f79ea90 100644 | |
--- a/src/proto_tcp.c | |
+++ b/src/proto_tcp.c | |
@@ -59,7 +59,8 @@ | |
#include <import/ip_tproxy.h> | |
#endif | |
-static int tcp_bind_listeners(struct protocol *proto); | |
+static int tcp_bind_listeners(struct protocol *proto, char *errmsg, int errlen); | |
+static int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen); | |
/* Note: must not be declared <const> as its list will be overwritten */ | |
static struct protocol proto_tcpv4 = { | |
@@ -72,6 +73,7 @@ static struct protocol proto_tcpv4 = { | |
.l3_addrlen = 32/8, | |
.read = &stream_sock_read, | |
.write = &stream_sock_write, | |
+ .bind = tcp_bind_listener, | |
.bind_all = tcp_bind_listeners, | |
.unbind_all = unbind_all_listeners, | |
.enable_all = enable_all_listeners, | |
@@ -90,6 +92,7 @@ static struct protocol proto_tcpv6 = { | |
.l3_addrlen = 128/8, | |
.read = &stream_sock_read, | |
.write = &stream_sock_write, | |
+ .bind = tcp_bind_listener, | |
.bind_all = tcp_bind_listeners, | |
.unbind_all = unbind_all_listeners, | |
.enable_all = enable_all_listeners, | |
@@ -560,8 +563,22 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen) | |
fdinfo[fd].peeraddr = NULL; | |
fdinfo[fd].peerlen = 0; | |
tcp_return: | |
- if (msg && errlen) | |
- strlcpy2(errmsg, msg, errlen); | |
+ if (msg && errlen) { | |
+ char pn[INET6_ADDRSTRLEN]; | |
+ | |
+ if (listener->addr.ss_family == AF_INET) { | |
+ inet_ntop(AF_INET, | |
+ (const void *)&((struct sockaddr_in *)&listener->addr)->sin_addr, | |
+ pn, sizeof(pn)); | |
+ snprintf(errmsg, errlen, "%s [%s:%d]", msg, pn, ntohs(((struct sockaddr_in *)&listener->addr)->sin_port)); | |
+ } | |
+ else { | |
+ inet_ntop(AF_INET6, | |
+ (const void *)&((struct sockaddr_in6 *)(&listener->addr))->sin6_addr, | |
+ pn, sizeof(pn)); | |
+ snprintf(errmsg, errlen, "%s [%s:%d]", msg, pn, ntohs(((struct sockaddr_in6 *)&listener->addr)->sin6_port)); | |
+ } | |
+ } | |
return err; | |
tcp_close_return: | |
@@ -575,14 +592,14 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen) | |
* loose them across the fork(). A call to enable_all_listeners() is needed | |
* to complete initialization. The return value is composed from ERR_*. | |
*/ | |
-static int tcp_bind_listeners(struct protocol *proto) | |
+static int tcp_bind_listeners(struct protocol *proto, char *errmsg, int errlen) | |
{ | |
struct listener *listener; | |
int err = ERR_NONE; | |
list_for_each_entry(listener, &proto->listeners, proto_list) { | |
- err |= tcp_bind_listener(listener, NULL, 0); | |
- if ((err & ERR_CODE) == ERR_ABORT) | |
+ err |= tcp_bind_listener(listener, errmsg, errlen); | |
+ if (err & ERR_ABORT) | |
break; | |
} | |
diff --git a/src/proto_uxst.c b/src/proto_uxst.c | |
index eb4a53d..b84f8bb 100644 | |
--- a/src/proto_uxst.c | |
+++ b/src/proto_uxst.c | |
@@ -56,7 +56,8 @@ | |
#define MAXPATHLEN 128 | |
#endif | |
-static int uxst_bind_listeners(struct protocol *proto); | |
+static int uxst_bind_listener(struct listener *listener, char *errmsg, int errlen); | |
+static int uxst_bind_listeners(struct protocol *proto, char *errmsg, int errlen); | |
static int uxst_unbind_listeners(struct protocol *proto); | |
/* Note: must not be declared <const> as its list will be overwritten */ | |
@@ -70,6 +71,7 @@ static struct protocol proto_unix = { | |
.l3_addrlen = sizeof(((struct sockaddr_un*)0)->sun_path),/* path len */ | |
.read = &stream_sock_read, | |
.write = &stream_sock_write, | |
+ .bind = uxst_bind_listener, | |
.bind_all = uxst_bind_listeners, | |
.unbind_all = uxst_unbind_listeners, | |
.enable_all = enable_all_listeners, | |
@@ -83,53 +85,101 @@ static struct protocol proto_unix = { | |
********************************/ | |
-/* This function creates a named PF_UNIX stream socket at address <path>. Note | |
- * that the path cannot be NULL nor empty. <uid> and <gid> different of -1 will | |
- * be used to change the socket owner. If <mode> is not 0, it will be used to | |
- * restrict access to the socket. While it is known not to be portable on every | |
- * OS, it's still useful where it works. | |
- * It returns the assigned file descriptor, or -1 in the event of an error. | |
+/* Tries to destroy the UNIX stream socket <path>. The socket must not be used | |
+ * anymore. It practises best effort, and no error is returned. | |
+ */ | |
+static void destroy_uxst_socket(const char *path) | |
+{ | |
+ struct sockaddr_un addr; | |
+ int sock, ret; | |
+ | |
+ /* We might have been chrooted, so we may not be able to access the | |
+ * socket. In order to avoid bothering the other end, we connect with a | |
+ * wrong protocol, namely SOCK_DGRAM. The return code from connect() | |
+ * is enough to know if the socket is still live or not. If it's live | |
+ * in mode SOCK_STREAM, we get EPROTOTYPE or anything else but not | |
+ * ECONNREFUSED. In this case, we do not touch it because it's used | |
+ * by some other process. | |
+ */ | |
+ sock = socket(PF_UNIX, SOCK_DGRAM, 0); | |
+ if (sock < 0) | |
+ return; | |
+ | |
+ addr.sun_family = AF_UNIX; | |
+ strncpy(addr.sun_path, path, sizeof(addr.sun_path)); | |
+ addr.sun_path[sizeof(addr.sun_path) - 1] = 0; | |
+ ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr)); | |
+ if (ret < 0 && errno == ECONNREFUSED) { | |
+ /* Connect failed: the socket still exists but is not used | |
+ * anymore. Let's remove this socket now. | |
+ */ | |
+ unlink(path); | |
+ } | |
+ close(sock); | |
+} | |
+ | |
+ | |
+/******************************** | |
+ * 2) listener-oriented functions | |
+ ********************************/ | |
+ | |
+ | |
+/* This function creates a UNIX socket associated to the listener. It changes | |
+ * the state from ASSIGNED to LISTEN. The socket is NOT enabled for polling. | |
+ * The return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL. | |
*/ | |
-static int create_uxst_socket(const char *path, uid_t uid, gid_t gid, mode_t mode) | |
+static int uxst_bind_listener(struct listener *listener, char *errmsg, int errlen) | |
{ | |
+ int fd; | |
char tempname[MAXPATHLEN]; | |
char backname[MAXPATHLEN]; | |
struct sockaddr_un addr; | |
+ const char *msg = NULL; | |
+ const char *path; | |
+ | |
+ int ret; | |
+ | |
+ /* ensure we never return garbage */ | |
+ if (errmsg && errlen) | |
+ *errmsg = 0; | |
+ | |
+ if (listener->state != LI_ASSIGNED) | |
+ return ERR_NONE; /* already bound */ | |
- int ret, sock; | |
+ path = ((struct sockaddr_un *)&listener->addr)->sun_path; | |
/* 1. create socket names */ | |
if (!path[0]) { | |
- Alert("Invalid empty name for a UNIX socket. Aborting.\n"); | |
+ msg = "Invalid empty name for a UNIX socket"; | |
goto err_return; | |
} | |
ret = snprintf(tempname, MAXPATHLEN, "%s.%d.tmp", path, pid); | |
if (ret < 0 || ret >= MAXPATHLEN) { | |
- Alert("name too long for UNIX socket (%s). Aborting.\n", path); | |
+ msg = "name too long for UNIX socket"; | |
goto err_return; | |
} | |
ret = snprintf(backname, MAXPATHLEN, "%s.%d.bak", path, pid); | |
if (ret < 0 || ret >= MAXPATHLEN) { | |
- Alert("name too long for UNIX socket (%s). Aborting.\n", path); | |
+ msg = "name too long for UNIX socket"; | |
goto err_return; | |
} | |
/* 2. clean existing orphaned entries */ | |
if (unlink(tempname) < 0 && errno != ENOENT) { | |
- Alert("error when trying to unlink previous UNIX socket (%s). Aborting.\n", path); | |
+ msg = "error when trying to unlink previous UNIX socket"; | |
goto err_return; | |
} | |
if (unlink(backname) < 0 && errno != ENOENT) { | |
- Alert("error when trying to unlink previous UNIX socket (%s). Aborting.\n", path); | |
+ msg = "error when trying to unlink previous UNIX socket"; | |
goto err_return; | |
} | |
/* 3. backup existing socket */ | |
if (link(path, backname) < 0 && errno != ENOENT) { | |
- Alert("error when trying to preserve previous UNIX socket (%s). Aborting.\n", path); | |
+ msg = "error when trying to preserve previous UNIX socket"; | |
goto err_return; | |
} | |
@@ -138,36 +188,42 @@ static int create_uxst_socket(const char *path, uid_t uid, gid_t gid, mode_t mod | |
strncpy(addr.sun_path, tempname, sizeof(addr.sun_path)); | |
addr.sun_path[sizeof(addr.sun_path) - 1] = 0; | |
- sock = socket(PF_UNIX, SOCK_STREAM, 0); | |
- if (sock < 0) { | |
- Alert("cannot create socket for UNIX listener (%s). Aborting.\n", path); | |
+ fd = socket(PF_UNIX, SOCK_STREAM, 0); | |
+ if (fd < 0) { | |
+ msg = "cannot create UNIX socket"; | |
goto err_unlink_back; | |
} | |
- if (sock >= global.maxsock) { | |
- Alert("socket(): not enough free sockets for UNIX listener (%s). Raise -n argument. Aborting.\n", path); | |
+ if (fd >= global.maxsock) { | |
+ msg = "socket(): not enough free sockets, raise -n argument"; | |
goto err_unlink_temp; | |
} | |
- if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { | |
- Alert("cannot make UNIX socket non-blocking. Aborting.\n"); | |
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { | |
+ msg = "cannot make UNIX socket non-blocking"; | |
goto err_unlink_temp; | |
} | |
- if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { | |
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { | |
/* note that bind() creates the socket <tempname> on the file system */ | |
- Alert("cannot bind socket for UNIX listener (%s). Aborting.\n", path); | |
+ msg = "cannot bind UNIX socket"; | |
goto err_unlink_temp; | |
} | |
- if (((uid != -1 || gid != -1) && (chown(tempname, uid, gid) == -1)) || | |
- (mode != 0 && chmod(tempname, mode) == -1)) { | |
- Alert("cannot change UNIX socket ownership (%s). Aborting.\n", path); | |
+ /* <uid> and <gid> different of -1 will be used to change the socket owner. | |
+ * If <mode> is not 0, it will be used to restrict access to the socket. | |
+ * While it is known not to be portable on every OS, it's still useful | |
+ * where it works. | |
+ */ | |
+ if (((listener->perm.ux.uid != -1 || listener->perm.ux.gid != -1) && | |
+ (chown(tempname, listener->perm.ux.uid, listener->perm.ux.gid) == -1)) || | |
+ (listener->perm.ux.mode != 0 && chmod(tempname, listener->perm.ux.mode) == -1)) { | |
+ msg = "cannot change UNIX socket ownership"; | |
goto err_unlink_temp; | |
} | |
- if (listen(sock, 0) < 0) { | |
- Alert("cannot listen to socket for UNIX listener (%s). Aborting.\n", path); | |
+ if (listen(fd, listener->backlog ? listener->backlog : listener->maxconn) < 0) { | |
+ msg = "cannot listen to UNIX socket"; | |
goto err_unlink_temp; | |
} | |
@@ -177,85 +233,13 @@ static int create_uxst_socket(const char *path, uid_t uid, gid_t gid, mode_t mod | |
* backname. | |
*/ | |
if (rename(tempname, path) < 0) { | |
- Alert("cannot switch final and temporary sockets for UNIX listener (%s). Aborting.\n", path); | |
+ msg = "cannot switch final and temporary UNIX sockets"; | |
goto err_rename; | |
} | |
/* 6. cleanup */ | |
unlink(backname); /* no need to keep this one either */ | |
- return sock; | |
- | |
- err_rename: | |
- ret = rename(backname, path); | |
- if (ret < 0 && errno == ENOENT) | |
- unlink(path); | |
- err_unlink_temp: | |
- unlink(tempname); | |
- close(sock); | |
- err_unlink_back: | |
- unlink(backname); | |
- err_return: | |
- return -1; | |
-} | |
- | |
-/* Tries to destroy the UNIX stream socket <path>. The socket must not be used | |
- * anymore. It practises best effort, and no error is returned. | |
- */ | |
-static void destroy_uxst_socket(const char *path) | |
-{ | |
- struct sockaddr_un addr; | |
- int sock, ret; | |
- | |
- /* We might have been chrooted, so we may not be able to access the | |
- * socket. In order to avoid bothering the other end, we connect with a | |
- * wrong protocol, namely SOCK_DGRAM. The return code from connect() | |
- * is enough to know if the socket is still live or not. If it's live | |
- * in mode SOCK_STREAM, we get EPROTOTYPE or anything else but not | |
- * ECONNREFUSED. In this case, we do not touch it because it's used | |
- * by some other process. | |
- */ | |
- sock = socket(PF_UNIX, SOCK_DGRAM, 0); | |
- if (sock < 0) | |
- return; | |
- | |
- addr.sun_family = AF_UNIX; | |
- strncpy(addr.sun_path, path, sizeof(addr.sun_path)); | |
- addr.sun_path[sizeof(addr.sun_path) - 1] = 0; | |
- ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr)); | |
- if (ret < 0 && errno == ECONNREFUSED) { | |
- /* Connect failed: the socket still exists but is not used | |
- * anymore. Let's remove this socket now. | |
- */ | |
- unlink(path); | |
- } | |
- close(sock); | |
-} | |
- | |
- | |
-/******************************** | |
- * 2) listener-oriented functions | |
- ********************************/ | |
- | |
- | |
-/* This function creates the UNIX socket associated to the listener. It changes | |
- * the state from ASSIGNED to LISTEN. The socket is NOT enabled for polling. | |
- * The return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL. | |
- */ | |
-static int uxst_bind_listener(struct listener *listener) | |
-{ | |
- int fd; | |
- | |
- if (listener->state != LI_ASSIGNED) | |
- return ERR_NONE; /* already bound */ | |
- | |
- fd = create_uxst_socket(((struct sockaddr_un *)&listener->addr)->sun_path, | |
- listener->perm.ux.uid, | |
- listener->perm.ux.gid, | |
- listener->perm.ux.mode); | |
- if (fd == -1) | |
- return ERR_FATAL; | |
- | |
/* the socket is now listening */ | |
listener->fd = fd; | |
listener->state = LI_LISTEN; | |
@@ -270,6 +254,19 @@ static int uxst_bind_listener(struct listener *listener) | |
fdinfo[fd].peeraddr = NULL; | |
fdinfo[fd].peerlen = 0; | |
return ERR_NONE; | |
+ err_rename: | |
+ ret = rename(backname, path); | |
+ if (ret < 0 && errno == ENOENT) | |
+ unlink(path); | |
+ err_unlink_temp: | |
+ unlink(tempname); | |
+ close(fd); | |
+ err_unlink_back: | |
+ unlink(backname); | |
+ err_return: | |
+ if (msg && errlen) | |
+ snprintf(errmsg, errlen, "%s [%s]", msg, path); | |
+ return ERR_FATAL | ERR_ALERT; | |
} | |
/* This function closes the UNIX sockets for the specified listener. | |
@@ -315,15 +312,15 @@ void uxst_add_listener(struct listener *listener) | |
* | |
* The return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL. | |
*/ | |
-static int uxst_bind_listeners(struct protocol *proto) | |
+static int uxst_bind_listeners(struct protocol *proto, char *errmsg, int errlen) | |
{ | |
struct listener *listener; | |
int err = ERR_NONE; | |
list_for_each_entry(listener, &proto->listeners, proto_list) { | |
- err |= uxst_bind_listener(listener); | |
- if (err != ERR_NONE) | |
- continue; | |
+ err |= uxst_bind_listener(listener, errmsg, errlen); | |
+ if (err & ERR_ABORT) | |
+ break; | |
} | |
return err; | |
} | |
diff --git a/src/protocols.c b/src/protocols.c | |
index 735c384..f6c329f 100644 | |
--- a/src/protocols.c | |
+++ b/src/protocols.c | |
@@ -143,15 +143,18 @@ void protocol_unregister(struct protocol *proto) | |
/* binds all listeners of all registered protocols. Returns a composition | |
* of ERR_NONE, ERR_RETRYABLE, ERR_FATAL. | |
*/ | |
-int protocol_bind_all(void) | |
+int protocol_bind_all(char *errmsg, int errlen) | |
{ | |
struct protocol *proto; | |
int err; | |
err = 0; | |
list_for_each_entry(proto, &protocols, list) { | |
- if (proto->bind_all) | |
- err |= proto->bind_all(proto); | |
+ if (proto->bind_all) { | |
+ err |= proto->bind_all(proto, errmsg, errlen); | |
+ if ( err & ERR_ABORT ) | |
+ break; | |
+ } | |
} | |
return err; | |
} | |
@@ -159,7 +162,7 @@ int protocol_bind_all(void) | |
/* unbinds all listeners of all registered protocols. They are also closed. | |
* This must be performed before calling exit() in order to get a chance to | |
* remove file-system based sockets and pipes. | |
- * Returns a composition of ERR_NONE, ERR_RETRYABLE, ERR_FATAL. | |
+ * Returns a composition of ERR_NONE, ERR_RETRYABLE, ERR_FATAL, ERR_ABORT. | |
*/ | |
int protocol_unbind_all(void) | |
{ | |
@@ -168,8 +171,9 @@ int protocol_unbind_all(void) | |
err = 0; | |
list_for_each_entry(proto, &protocols, list) { | |
- if (proto->unbind_all) | |
+ if (proto->unbind_all) { | |
err |= proto->unbind_all(proto); | |
+ } | |
} | |
return err; | |
} | |
@@ -185,8 +189,9 @@ int protocol_enable_all(void) | |
err = 0; | |
list_for_each_entry(proto, &protocols, list) { | |
- if (proto->enable_all) | |
+ if (proto->enable_all) { | |
err |= proto->enable_all(proto); | |
+ } | |
} | |
return err; | |
} | |
@@ -202,8 +207,9 @@ int protocol_disable_all(void) | |
err = 0; | |
list_for_each_entry(proto, &protocols, list) { | |
- if (proto->disable_all) | |
+ if (proto->disable_all) { | |
err |= proto->disable_all(proto); | |
+ } | |
} | |
return err; | |
} | |
diff --git a/src/proxy.c b/src/proxy.c | |
index 7971a7c..ffc2671 100644 | |
--- a/src/proxy.c | |
+++ b/src/proxy.c | |
@@ -427,7 +427,7 @@ int start_proxies(int verbose) | |
if (listener->state != LI_ASSIGNED) | |
continue; /* already started */ | |
- lerr = tcp_bind_listener(listener, msg, sizeof(msg)); | |
+ lerr = listener->proto->bind(listener, msg, sizeof(msg)); | |
/* errors are reported if <verbose> is set or if they are fatal */ | |
if (verbose || (lerr & (ERR_FATAL | ERR_ABORT))) { | |
@@ -684,15 +684,27 @@ void listen_proxies(void) | |
} else { | |
int port; | |
- if (l->addr.ss_family == AF_INET6) | |
+ if (l->addr.ss_family == AF_INET6) { | |
port = ntohs(((struct sockaddr_in6 *)(&l->addr))->sin6_port); | |
- else | |
+ Warning("Port %d busy while trying to enable %s %s.\n", | |
+ port, proxy_cap_str(p->cap), p->id); | |
+ send_log(p, LOG_WARNING, "Port %d busy while trying to enable %s %s.\n", | |
+ port, proxy_cap_str(p->cap), p->id); | |
+ } | |
+ else if (l->addr.ss_family == AF_INET) { | |
port = ntohs(((struct sockaddr_in *)(&l->addr))->sin_port); | |
+ Warning("Port %d busy while trying to enable %s %s.\n", | |
+ port, proxy_cap_str(p->cap), p->id); | |
+ send_log(p, LOG_WARNING, "Port %d busy while trying to enable %s %s.\n", | |
+ port, proxy_cap_str(p->cap), p->id); | |
+ } | |
+ else { | |
+ Warning("Bind on socket %d busy while trying to enable %s %s.\n", | |
+ l->luid, proxy_cap_str(p->cap), p->id); | |
+ send_log(p, LOG_WARNING, "Bind on socket %d busy while trying to enable %s %s.\n", | |
+ l->luid, proxy_cap_str(p->cap), p->id); | |
+ } | |
- Warning("Port %d busy while trying to enable %s %s.\n", | |
- port, proxy_cap_str(p->cap), p->id); | |
- send_log(p, LOG_WARNING, "Port %d busy while trying to enable %s %s.\n", | |
- port, proxy_cap_str(p->cap), p->id); | |
/* Another port might have been enabled. Let's stop everything. */ | |
pause_proxy(p); | |
break; | |
-- | |
1.7.4.1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment