Skip to content
This repository has been archived by the owner on May 24, 2022. It is now read-only.

PROXY v2 binary Protocol implementation and support for unix domain socket backend #142

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ core
core.*
*.o
stud

*.swp
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
35 changes: 31 additions & 4 deletions configuration.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -560,7 +562,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);
Expand Down Expand Up @@ -684,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);
}
Expand Down Expand Up @@ -922,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));
Expand Down Expand Up @@ -1093,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));
Expand All @@ -1103,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));
Expand Down Expand Up @@ -1163,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' },
Expand Down
7 changes: 7 additions & 0 deletions configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -41,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;
Expand All @@ -50,6 +56,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;
Expand Down
123 changes: 112 additions & 11 deletions stud.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>
#include <sys/wait.h>
#include <netinet/in.h>
Expand Down Expand Up @@ -114,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 {
Expand All @@ -138,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
*
Expand Down Expand Up @@ -171,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(...) \
Expand Down Expand Up @@ -778,13 +813,23 @@ 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,
sizeof(tcp_proxy_line),
"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 ) {
Expand All @@ -795,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 {
Expand Down Expand Up @@ -851,16 +900,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;
Expand Down Expand Up @@ -1066,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);
Expand Down Expand Up @@ -1388,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);

Expand Down Expand Up @@ -1565,16 +1645,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
Expand Down