From 9fc87c5928dd9604505325e7496d4c96bdf20793 Mon Sep 17 00:00:00 2001 From: Vincent Chinedu Okonkwo Date: Sat, 15 Feb 2014 21:28:00 +0000 Subject: [PATCH 1/3] added unix domain socket support --- configuration.c | 10 +++++++++- configuration.h | 6 ++++++ stud.c | 45 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/configuration.c b/configuration.c index 25fead7..bcebe71 100644 --- a/configuration.c +++ b/configuration.c @@ -560,7 +560,15 @@ void config_param_validate (char *k, char *v, stud_config *cfg, char *file, int r = config_param_host_port_wildcard(v, &cfg->FRONT_IP, &cfg->FRONT_PORT, 1); } else if (strcmp(k, CFG_BACKEND) == 0) { - r = config_param_host_port(v, &cfg->BACK_IP, &cfg->BACK_PORT); + int vlen = 0; + + cfg->BACK_CONN_MODE = CONN_INET; + if(v != NULL && (vlen = strlen(v)) > 7 && strncasecmp(v, "pipe://", 6) == 0) { + cfg->BACK_CONN_MODE = CONN_PIPE; + config_assign_str(&cfg->BACK_IP,(v+7)); + if(cfg->BACK_IP[0] == '@') cfg->BACK_IP[0] = '\0'; + } + else r = config_param_host_port(v, &cfg->BACK_IP, &cfg->BACK_PORT); } else if (strcmp(k, CFG_WORKERS) == 0) { r = config_param_val_intl_pos(v, &cfg->NCORES); diff --git a/configuration.h b/configuration.h index 3ea68b0..9dec86f 100644 --- a/configuration.h +++ b/configuration.h @@ -31,6 +31,11 @@ typedef enum { SSL_CLIENT } PROXY_MODE; +typedef enum { + CONN_INET, + CONN_PIPE +} BACK_CONNECTION_MODE; + struct cert_files { char *CERT_FILE; struct cert_files *NEXT; @@ -50,6 +55,7 @@ struct __stud_config { char *FRONT_PORT; char *BACK_IP; char *BACK_PORT; + BACK_CONNECTION_MODE BACK_CONN_MODE; long NCORES; struct cert_files *CERT_FILES; char *CIPHER_SUITE; diff --git a/stud.c b/stud.c index 1e83617..45fe6dd 100644 --- a/stud.c +++ b/stud.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -851,16 +852,19 @@ static int create_main_socket() { /* Initiate a clear-text nonblocking connect() to the backend IP on behalf * of a newly connected upstream (encrypted) client*/ static int create_back_socket() { - int s = socket(backaddr->ai_family, SOCK_STREAM, IPPROTO_TCP); + int s = socket(backaddr->ai_family, SOCK_STREAM, CONFIG->BACK_CONN_MODE == CONN_PIPE ? 0 : IPPROTO_TCP); if (s == -1) return -1; - int flag = 1; - int ret = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); - if (ret == -1) { - perror("Couldn't setsockopt to backend (TCP_NODELAY)\n"); + if (CONFIG->BACK_CONN_MODE != CONN_PIPE) { + int flag = 1; + int ret = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); + if (ret == -1) { + perror("Couldn't setsockopt to backend (TCP_NODELAY)\n"); + } } + setnonblocking(s); return s; @@ -1565,16 +1569,37 @@ void drop_privileges() { void init_globals() { /* backaddr */ + struct addrinfo hints; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; - const int gai_err = getaddrinfo(CONFIG->BACK_IP, CONFIG->BACK_PORT, - &hints, &backaddr); - if (gai_err != 0) { - ERR("{getaddrinfo}: [%s]", gai_strerror(gai_err)); - exit(1); + + if (CONFIG->BACK_CONN_MODE == CONN_PIPE) { + backaddr = (struct addrinfo *)malloc(sizeof(struct addrinfo)); + if (backaddr == 0) { + ERR("{malloc}: [%s]", "allocate sockaddr_un failed"); + exit(1); + } + + memset(backaddr, 0, sizeof(struct addrinfo)); + + backaddr->ai_socktype = SOCK_STREAM; + backaddr->ai_addrlen = sizeof(struct sockaddr_un); + struct sockaddr_un* addr = backaddr->ai_addr = (struct sockaddr*)malloc(backaddr->ai_addrlen); + backaddr->ai_family = addr->sun_family = AF_UNIX; + + strncpy(addr->sun_path, CONFIG->BACK_IP, sizeof(addr->sun_path)); + } + else { + + const int gai_err = getaddrinfo(CONFIG->BACK_IP, CONFIG->BACK_PORT, + &hints, &backaddr); + if (gai_err != 0) { + ERR("{getaddrinfo}: [%s]", gai_strerror(gai_err)); + exit(1); + } } #ifdef USE_SHARED_CACHE From e522ee67f00efc893da4c911b383cc73c3899292 Mon Sep 17 00:00:00 2001 From: Vincent Chinedu Okonkwo Date: Sat, 15 Feb 2014 21:40:20 +0000 Subject: [PATCH 2/3] don't commit vim .swp files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index b472e44..a77389a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ core core.* *.o stud + +*.swp From 69ee99e7f6270703a233bf8ed0dd32fb9880ae0e Mon Sep 17 00:00:00 2001 From: Vincent Chinedu Okonkwo Date: Fri, 21 Feb 2014 16:32:33 +0000 Subject: [PATCH 3/3] added support for ha proxy v2 binary protocol. see http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt --- README.md | 3 ++ configuration.c | 25 ++++++++++++++-- configuration.h | 1 + stud.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 103 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b43fe94..35316cf 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,9 @@ Detail about the entire set of options can be found by invoking `stud -h`: --write-proxy Write HaProxy's PROXY (IPv4 or IPv6) protocol line before actual data (Default: off) + --write-proxy-v2 Write HaProxy's PROXY v2 binary (IPv4 or IPv6) protocol line + before actual data + (Default: off) --proxy-proxy Proxy HaProxy's PROXY (IPv4 or IPv6) protocol line before actual data (Default: off) diff --git a/configuration.c b/configuration.c index bcebe71..5e37987 100644 --- a/configuration.c +++ b/configuration.c @@ -45,6 +45,7 @@ #define CFG_DAEMON "daemon" #define CFG_WRITE_IP "write-ip" #define CFG_WRITE_PROXY "write-proxy" +#define CFG_WRITE_PROXY_V2 "write-proxy-v2" #define CFG_PEM_FILE "pem-file" #define CFG_PROXY_PROXY "proxy-proxy" @@ -115,6 +116,7 @@ stud_config * config_new (void) { r->PMODE = SSL_SERVER; r->WRITE_IP_OCTET = 0; r->WRITE_PROXY_LINE = 0; + r->WRITE_PROXY_LINE_V2= 0; r->PROXY_PROXY_LINE = 0; r->CHROOT = NULL; r->UID = 0; @@ -692,6 +694,9 @@ void config_param_validate (char *k, char *v, stud_config *cfg, char *file, int else if (strcmp(k, CFG_WRITE_PROXY) == 0) { r = config_param_val_bool(v, &cfg->WRITE_PROXY_LINE); } + else if (strcmp(k, CFG_WRITE_PROXY_V2) == 0) { + r = config_param_val_bool(v, &cfg->WRITE_PROXY_LINE_V2); + } else if (strcmp(k, CFG_PROXY_PROXY) == 0) { r = config_param_val_bool(v, &cfg->PROXY_PROXY_LINE); } @@ -930,6 +935,9 @@ void config_print_usage_fd (char *prog, stud_config *cfg, FILE *out) { fprintf(out, " --write-proxy Write HaProxy's PROXY (IPv4 or IPv6) protocol line\n" ); fprintf(out, " before actual data\n"); fprintf(out, " (Default: %s)\n", config_disp_bool(cfg->WRITE_PROXY_LINE)); + fprintf(out, " --write-proxy-v2 Write HaProxy's PROXY v2 binary (IPv4 or IPv6) protocol line\n" ); + fprintf(out, " before actual data\n"); + fprintf(out, " (Default: %s)\n", config_disp_bool(cfg->WRITE_PROXY_LINE_V2)); fprintf(out, " --proxy-proxy Proxy HaProxy's PROXY (IPv4 or IPv6) protocol line\n" ); fprintf(out, " before actual data\n"); fprintf(out, " (Default: %s)\n", config_disp_bool(cfg->PROXY_PROXY_LINE)); @@ -1101,7 +1109,7 @@ void config_print_default (FILE *fd, stud_config *cfg) { fprintf(fd, "# Report client address by writing IP before sending data\n"); fprintf(fd, "#\n"); - fprintf(fd, "# NOTE: This option is mutually exclusive with option %s and %s.\n", CFG_WRITE_PROXY, CFG_PROXY_PROXY); + fprintf(fd, "# NOTE: This option is mutually exclusive with option %s, %s and %s.\n", CFG_WRITE_PROXY_V2, CFG_WRITE_PROXY, CFG_PROXY_PROXY); fprintf(fd, "#\n"); fprintf(fd, "# type: boolean\n"); fprintf(fd, FMT_STR, CFG_WRITE_IP, config_disp_bool(cfg->WRITE_IP_OCTET)); @@ -1111,15 +1119,25 @@ void config_print_default (FILE *fd, stud_config *cfg) { fprintf(fd, "# http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt\n"); fprintf(fd, "# for details.\n"); fprintf(fd, "#\n"); - fprintf(fd, "# NOTE: This option is mutually exclusive with option %s and %s.\n", CFG_WRITE_IP, CFG_PROXY_PROXY); + fprintf(fd, "# NOTE: This option is mutually exclusive with option %s, %s and %s.\n", CFG_WRITE_PROXY_V2, CFG_WRITE_IP, CFG_PROXY_PROXY); fprintf(fd, "#\n"); fprintf(fd, "# type: boolean\n"); fprintf(fd, FMT_STR, CFG_WRITE_PROXY, config_disp_bool(cfg->WRITE_PROXY_LINE)); fprintf(fd, "\n"); + fprintf(fd, "# Report client address using SENDPROXY v2 binary protocol, see\n"); + fprintf(fd, "# http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt\n"); + fprintf(fd, "# for details.\n"); + fprintf(fd, "#\n"); + fprintf(fd, "# NOTE: This option is mutually exclusive with option %s, %s and %s.\n", CFG_WRITE_IP, CFG_WRITE_PROXY, CFG_PROXY_PROXY); + fprintf(fd, "#\n"); + fprintf(fd, "# type: boolean\n"); + fprintf(fd, FMT_STR, CFG_WRITE_PROXY_V2, config_disp_bool(cfg->WRITE_PROXY_LINE_V2)); + fprintf(fd, "\n"); + fprintf(fd, "# Proxy an existing SENDPROXY protocol header through this request.\n"); fprintf(fd, "#\n"); - fprintf(fd, "# NOTE: This option is mutually exclusive with option %s and %s.\n", CFG_WRITE_IP, CFG_WRITE_PROXY); + fprintf(fd, "# NOTE: This option is mutually exclusive with option %s, %s and %s.\n", CFG_WRITE_PROXY_V2, CFG_WRITE_IP, CFG_WRITE_PROXY); fprintf(fd, "#\n"); fprintf(fd, "# type: boolean\n"); fprintf(fd, FMT_STR, CFG_PROXY_PROXY, config_disp_bool(cfg->PROXY_PROXY_LINE)); @@ -1171,6 +1189,7 @@ void config_parse_cli(int argc, char **argv, stud_config *cfg) { { CFG_DAEMON, 0, &cfg->DAEMONIZE, 1 }, { CFG_WRITE_IP, 0, &cfg->WRITE_IP_OCTET, 1 }, { CFG_WRITE_PROXY, 0, &cfg->WRITE_PROXY_LINE, 1 }, + { CFG_WRITE_PROXY_V2, 0, &cfg->WRITE_PROXY_LINE_V2, 1 }, { CFG_PROXY_PROXY, 0, &cfg->PROXY_PROXY_LINE, 1 }, { "test", 0, NULL, 't' }, diff --git a/configuration.h b/configuration.h index 9dec86f..3729c41 100644 --- a/configuration.h +++ b/configuration.h @@ -46,6 +46,7 @@ struct __stud_config { ENC_TYPE ETYPE; PROXY_MODE PMODE; int WRITE_IP_OCTET; + int WRITE_PROXY_LINE_V2; int WRITE_PROXY_LINE; int PROXY_PROXY_LINE; char *CHROOT; diff --git a/stud.c b/stud.c index 45fe6dd..5ecdfc0 100644 --- a/stud.c +++ b/stud.c @@ -115,6 +115,7 @@ stud_config *CONFIG; static char tcp_proxy_line[128] = ""; + /* What agent/state requests the shutdown--for proper half-closed * handling */ typedef enum _SHUTDOWN_REQUESTOR { @@ -139,6 +140,37 @@ static ctx_list *sni_ctxs; #endif /* OPENSSL_NO_TLSEXT */ + +union ha_proxy_v2_addr { + struct { /* for TCP/UDP over IPv4, len = 12 */ + uint32_t src_addr; + uint32_t dst_addr; + uint16_t src_port; + uint16_t dst_port; + } ipv4_addr; + struct { /* for TCP/UDP over IPv6, len = 36 */ + uint8_t src_addr[16]; + uint8_t dst_addr[16]; + uint16_t src_port; + uint16_t dst_port; + } ipv6_addr; + struct { /* for AF_UNIX sockets, len = 216 */ + uint8_t src_addr[108]; + uint8_t dst_addr[108]; + } unix_addr; +}; + + +struct ha_proxy_v2_hdr { + uint8_t sig[12]; // = {0x0D, 0x0A, 0x0D,0x0A,0x00,0x0D,0x0A,0x51,0x55,0x49,0x54,0x0A}; + uint8_t ver; // = 0x02; /* hex 02 */ + uint8_t cmd; // = 0x01; /* We only support PROXY Command*/ + uint8_t fam; /* protocol family and address */ + uint8_t len; /* number of following bytes part of the header */ +}; + +static struct ha_proxy_v2_hdr header_proxy_v2; +static union ha_proxy_v2_addr frontend_addr; /* * Proxied State * @@ -172,6 +204,8 @@ typedef struct proxystate { SSL *ssl; /* OpenSSL SSL state */ struct sockaddr_storage remote_ip; /* Remote ip returned from `accept` */ + + union ha_proxy_v2_addr proxy_addr; /* proxy v2 protocol struct */ } proxystate; #define LOG(...) \ @@ -779,6 +813,12 @@ static void prepare_proxy_line(struct sockaddr* ai_addr) { tcp_proxy_line[0] = 0; char tcp6_address_string[INET6_ADDRSTRLEN]; + memcpy(&header_proxy_v2.sig,"\r\n\r\n\0\r\nQUIT\n", 12); + header_proxy_v2.ver = 0x02; + header_proxy_v2.cmd = 0x01; + header_proxy_v2.fam = ai_addr->sa_family == AF_INET ? 0x11 : 0x21; + header_proxy_v2.len = ai_addr->sa_family == AF_INET ? 12 : 36; + if (ai_addr->sa_family == AF_INET) { struct sockaddr_in* addr = (struct sockaddr_in*)ai_addr; size_t res = snprintf(tcp_proxy_line, @@ -786,6 +826,10 @@ static void prepare_proxy_line(struct sockaddr* ai_addr) { "PROXY %%s %%s %s %%hu %hu\r\n", inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); + + memcpy(&frontend_addr.ipv4_addr.dst_addr, &addr->sin_addr, sizeof(struct in_addr)); + frontend_addr.ipv4_addr.dst_port = addr->sin_port; + assert(res < sizeof(tcp_proxy_line)); } else if (ai_addr->sa_family == AF_INET6 ) { @@ -796,6 +840,10 @@ static void prepare_proxy_line(struct sockaddr* ai_addr) { "PROXY %%s %%s %s %%hu %hu\r\n", tcp6_address_string, ntohs(addr->sin6_port)); + + memcpy(&frontend_addr.ipv6_addr.dst_addr,&addr->sin6_addr, sizeof(struct in6_addr)); + frontend_addr.ipv6_addr.dst_port = addr->sin6_port; + assert(res < sizeof(tcp_proxy_line)); } else { @@ -1070,7 +1118,22 @@ static void end_handshake(proxystate *ps) { /* Check if clear side is connected */ if (!ps->clear_connected) { - if (CONFIG->WRITE_PROXY_LINE) { + + if (CONFIG->WRITE_PROXY_LINE_V2) { + + char *ring_pnt = ringbuffer_write_ptr(&ps->ring_ssl2clear); + assert(ps->remote_ip.ss_family == AF_INET || + ps->remote_ip.ss_family == AF_INET6); + + + memcpy(ring_pnt, &header_proxy_v2, sizeof(header_proxy_v2)); + memcpy(ring_pnt+sizeof(header_proxy_v2), &ps->proxy_addr, header_proxy_v2.len); + + + + ringbuffer_write_append(&ps->ring_ssl2clear, header_proxy_v2.len+sizeof(header_proxy_v2)); + } + else if (CONFIG->WRITE_PROXY_LINE) { char *ring_pnt = ringbuffer_write_ptr(&ps->ring_ssl2clear); assert(ps->remote_ip.ss_family == AF_INET || ps->remote_ip.ss_family == AF_INET6); @@ -1392,6 +1455,19 @@ static void handle_accept(struct ev_loop *loop, ev_io *w, int revents) { ps->ev_r_handshake.data = ps; ps->ev_w_handshake.data = ps; + memcpy(&ps->proxy_addr, &frontend_addr, sizeof(frontend_addr)); + + if(addr.ss_family == AF_INET) { + struct sockaddr_in* saddr = (struct sockaddr_in*)&addr; + memcpy(&ps->proxy_addr.ipv4_addr.src_addr, &saddr->sin_addr, sizeof(struct in_addr)); + ps->proxy_addr.ipv4_addr.src_port = saddr->sin_port; + } else if(addr.ss_family == AF_INET6) { + struct sockaddr_in6* saddr = (struct sockaddr_in6*)&addr; + memcpy(&ps->proxy_addr.ipv6_addr.src_addr, &saddr->sin6_addr, sizeof(struct in6_addr)); + ps->proxy_addr.ipv6_addr.src_port = saddr->sin6_port; + } + + /* Link back proxystate to SSL state */ SSL_set_app_data(ssl, ps);