diff --git a/common.c b/common.c index 4bea124..680e755 100644 --- a/common.c +++ b/common.c @@ -18,6 +18,7 @@ #include "probe.h" #include "log.h" #include "sslh-conf.h" +#include "proxyprotocol.h" #if HAVE_LIBCAP #include @@ -480,8 +481,11 @@ static int connect_unix(struct connection *cnx, int fd_from, connect_blocking bl /* * Connect to the first backend server address that works and updates the *cnx * object accordingly (in cnx->q[1].fd). Set that to -1 in case of failure. + * * If transparent proxying is on, use fd_from peer address on external address - * of new file descriptor. */ + * of new file descriptor. + * If proxyprotocol is used, write header on new backend server connection + * */ void connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking) { int fd; @@ -492,6 +496,13 @@ void connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking fd = connect_inet(cnx, fd_from, blocking); } cnx->q[1].fd = fd; + + if (cnx->proto->proxyprotocol_is_present) { + int res = pp_write_header(cnx->proto->proxyprotocol, cnx); + /* If pp_write_header() fails, it already logs a message and there is + * nothing much we can do. The server side will probably close the + * connection */ + } } /* Store some data to write to the queue later */ @@ -513,6 +524,27 @@ int defer_write(struct queue *q, void* data, ssize_t data_size) return 0; } +/* Store some data to write *before* what's already in the queue */ +int defer_write_before(struct queue *q, void* data, ssize_t data_size) +{ + char *p; + + print_message(msg_fd, "writing deferred to beginning on fd %d\n", q->fd); + p = malloc(q->deferred_data_size + data_size); + CHECK_ALLOC(p, "malloc"); + + memcpy(p, data, data_size); + memcpy(p + data_size, q->deferred_data, q->deferred_data_size); + + free(q->begin_deferred_data); + + q->begin_deferred_data = p; + q->deferred_data = p; + q->deferred_data_size += (int)data_size; + + return 0; +} + /* tries to flush some of the data for specified queue * Upon success, the number of bytes written is returned. * Upon failure, -1 returned (e.g. connexion closed) diff --git a/common.h b/common.h index 762f49f..3503a95 100644 --- a/common.h +++ b/common.h @@ -175,6 +175,7 @@ int resolve_split_name(struct addrinfo **out, char* hostname, char* port); int start_listen_sockets(struct listen_endpoint *sockfd[]); int defer_write(struct queue *q, void* data, ssize_t data_size); +int defer_write_before(struct queue *q, void* data, ssize_t data_size); int flush_deferred(struct queue *q); extern struct sslhcfg_item cfg; diff --git a/echosrv-conf.c b/echosrv-conf.c index 38ca0ed..8747a31 100644 --- a/echosrv-conf.c +++ b/echosrv-conf.c @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sun Dec 22 22:40:51 2024. + * on Mon Feb 24 18:37:24 2025. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2024 Yves Rutschle diff --git a/echosrv-conf.h b/echosrv-conf.h index 3ea2b29..1436146 100644 --- a/echosrv-conf.h +++ b/echosrv-conf.h @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sun Dec 22 22:40:51 2024. + * on Mon Feb 24 18:37:24 2025. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2024 Yves Rutschle diff --git a/proxyprotocol.c b/proxyprotocol.c new file mode 100644 index 0000000..549d3dc --- /dev/null +++ b/proxyprotocol.c @@ -0,0 +1,92 @@ +/* +# proxyprotocol: Support for HAProxy's proxyprotocol +# +# Copyright (C) 2025 Yves Rutschle +# +# This program is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more +# details. +# +# The full text for the General Public License is here: +# http://www.gnu.org/licenses/gpl.html + +*/ + +#include +#include "common.h" +#include "log.h" + + +/* Converts socket family to libproxyprotocol family */ +static int family_to_pp(int af_family) +{ + switch (af_family) { + case AF_INET: + return ADDR_FAMILY_INET; + + case AF_INET6: + return ADDR_FAMILY_INET6; + + case AF_UNIX: + return ADDR_FAMILY_UNIX; + + default: + print_message(msg_int_error, "Unknown internal socket family %d\n", af_family); + return -1; + } +} + +int pp_write_header(int pp_version, struct connection* cnx) +{ + pp_info_t pp_info_in_v1 = { + .transport_protocol = TRANSPORT_PROTOCOL_STREAM, + }; + uint16_t pp1_hdr_len; + int32_t error; + + struct sockaddr_storage ss; + struct addrinfo addr; + char host[NI_MAXHOST], serv[NI_MAXSERV]; + int res; + + addr.ai_addr = (struct sockaddr*)&ss; + addr.ai_addrlen = sizeof(ss); + + res = getpeername(cnx->q[0].fd, addr.ai_addr, &addr.ai_addrlen); + res = getnameinfo(addr.ai_addr, addr.ai_addrlen, + host, sizeof(host), + serv, sizeof(serv), + NI_NUMERICHOST | NI_NUMERICSERV ); + memcpy(pp_info_in_v1.src_addr, host, sizeof(pp_info_in_v1.src_addr)); + pp_info_in_v1.src_port = atoi(serv); + pp_info_in_v1.address_family = family_to_pp(addr.ai_addr->sa_family); + + res = getpeername(cnx->q[1].fd, addr.ai_addr, &addr.ai_addrlen); + res = getnameinfo(addr.ai_addr, addr.ai_addrlen, + host, sizeof(host), + serv, sizeof(serv), + NI_NUMERICHOST | NI_NUMERICSERV ); + memcpy(pp_info_in_v1.dst_addr, host, sizeof(pp_info_in_v1.dst_addr)); + pp_info_in_v1.dst_port = atoi(serv); + + uint8_t *pp1_hdr = pp_create_hdr(pp_version, &pp_info_in_v1, &pp1_hdr_len, &error); + + if (!pp1_hdr) { + print_message(msg_system_error, "pp_create_hrd:%d:%s\n", error, pp_strerror(error)); + return -1; + } + defer_write_before(&cnx->q[1], pp1_hdr, pp1_hdr_len); + + pp_info_clear(&pp_info_in_v1); + free(pp1_hdr); + + return 0; +} diff --git a/proxyprotocol.h b/proxyprotocol.h new file mode 100644 index 0000000..fab0157 --- /dev/null +++ b/proxyprotocol.h @@ -0,0 +1,15 @@ +#ifndef PROXYPROTOCOL_H +#define PROXYPROTOCOL_H + + +#if HAVE_PROXYPROTOCOL +int pp_write_header(int pp_version, struct connection* cnx); + + +#else /* HAVE_PROXYPROTOCOL */ + +static inline int pp_write_header(int pp_version, struct connection* cnx) {} + +#endif /* HAVE_PROXYPROTOCOL */ + +#endif /* PROXYPROTOCOL_H */ diff --git a/sslh-conf.c b/sslh-conf.c index e19c74b..a7edc8f 100644 --- a/sslh-conf.c +++ b/sslh-conf.c @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sun Dec 22 22:40:51 2024. + * on Mon Feb 24 18:37:24 2025. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2024 Yves Rutschle @@ -500,7 +500,7 @@ struct arg_file* sslhcfg_conffile; struct arg_str* sslhcfg_anyprot; struct arg_end* sslhcfg_end; - + static struct config_desc table_sslhcfg_protocols[] = { @@ -775,6 +775,22 @@ static struct config_desc table_sslhcfg_protocols[] = { /* optional */ 1, /* default_val*/ .default_val.def_int = 0 }, + + { + /* name */ "proxyprotocol", + /* type */ CFG_INT, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_protocols_item, proxyprotocol), + /* offset_len */ 0, + /* offset_present */ offsetof(struct sslhcfg_protocols_item, proxyprotocol_is_present), + /* size */ sizeof(int), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 1, + /* default_val*/ .default_val.def_int = 0 + }, { 0 } }; @@ -2427,6 +2443,11 @@ static void sslhcfg_protocols_fprint( if (! sslhcfg_protocols->minlength_is_present) fprintf(out, " "); fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "proxyprotocol: %d", sslhcfg_protocols->proxyprotocol); + if (! sslhcfg_protocols->proxyprotocol_is_present) + fprintf(out, " "); + fprintf(out, "\n"); } static void sslhcfg_listen_fprint( diff --git a/sslh-conf.h b/sslh-conf.h index 0abfe8c..55c63e9 100644 --- a/sslh-conf.h +++ b/sslh-conf.h @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sun Dec 22 22:40:51 2024. + * on Mon Feb 24 18:37:24 2025. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2024 Yves Rutschle @@ -71,6 +71,8 @@ struct sslhcfg_protocols_item { char** regex_patterns; int minlength_is_present; int minlength; + int proxyprotocol_is_present; + int proxyprotocol; T_PROBE* probe; struct addrinfo* saddr; void* data; diff --git a/sslhconf.cfg b/sslhconf.cfg index 76eca5d..6b8444f 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -136,6 +136,7 @@ config: { element_type: "string" }, { name: "minlength"; type: "int"; optional: true }, + { name: "proxyprotocol"; type: "int"; optional: true }, # Runtime data { name: "probe"; type: "runtime"; c_type: "T_PROBE*" }, diff --git a/test.cfg b/test.cfg index fe8c50e..314b420 100644 --- a/test.cfg +++ b/test.cfg @@ -45,7 +45,7 @@ protocols: ( { name: "ssh"; host: "localhost"; port: "9000"; fork: true; transparent: true; resolve_on_forward: true; }, { name: "socks5"; host: "localhost"; port: "9001"; }, - { name: "http"; is_unix: true; host: "/tmp/nginx.sock"; port: ""; }, + { name: "http"; host: "www.lemonde.fr"; port: "80"; proxyprotocol: 1; }, { name: "tinc"; host: "localhost"; port: "9003"; }, { name: "openvpn"; host: "localhost"; port: "9004"; }, { name: "xmpp"; host: "localhost"; port: "9009"; }, diff --git a/version.h b/version.h index 0768109..5eadcd4 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #ifndef VERSION_H #define VERSION_H -#define VERSION "v2.1.4-35-g7a6673a-dirty" +#define VERSION "v2.1.4-37-g951b708-dirty" #endif