Skip to content

Commit

Permalink
add support for using the proxy protocol v1 when proxying too
Browse files Browse the repository at this point in the history
This is symmetrical to the support for *incoming* requests.  The
new regress case uses this to proxy to itself using the proxy-protocol
v1.

Fixes #31
  • Loading branch information
omar-polo committed Aug 3, 2024
1 parent 9360fb1 commit 040f7aa
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 12 deletions.
6 changes: 5 additions & 1 deletion gmid.conf.5
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.Dd June 28, 2024
.Dd August 3, 2024
.Dt GMID.CONF 5
.Os
.Sh NAME
Expand Down Expand Up @@ -521,6 +521,10 @@ Refer to the
.Xr tls_config_parse_protocols 3
function for the valid protocol string values.
By default, both TLSv1.2 and TLSv1.3 are enabled.
.It Ic proxy-v1
Use the proxy protocol v1.
If supported by the remote server, this is useful to propagate the
information about the originating IP address and port.
.It Ic relay-to Ar host Op Cm port Ar port
Relay the request to the given
.Ar host
Expand Down
3 changes: 2 additions & 1 deletion gmid.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2021, 2022, 2023 Omar Polo <op@omarpolo.com>
* Copyright (c) 2020-2024 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
Expand Down Expand Up @@ -168,6 +168,7 @@ struct proxy {
size_t keylen;
char *reqca_path;
X509_STORE *reqca;
int proxy; /* use the proxy protocol */

TAILQ_ENTRY(proxy) proxies;
};
Expand Down
3 changes: 3 additions & 0 deletions parse.y
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,9 @@ proxy_opt : CERT string {
yyerror("invalid protocols string \"%s\"", $2);
free($2);
}
| PROXYV1 {
proxy->proxy = 1;
}
| RELAY_TO string proxy_port {
if (strlcpy(proxy->host, $2, sizeof(proxy->host))
>= sizeof(proxy->host))
Expand Down
45 changes: 42 additions & 3 deletions proxy.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2022, 2023 Omar Polo <op@omarpolo.com>
* Copyright (c) 2021-2024 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
Expand Down Expand Up @@ -237,6 +237,11 @@ proxy_enqueue_req(struct client *c)
if (c->proxybev == NULL)
fatal("can't allocate bufferevent");

evb = EVBUFFER_OUTPUT(c->proxybev);

if (p->notls && p->proxy)
evbuffer_add(evb, c->buf.data, c->buf.len);

if (!p->notls) {
event_set(&c->proxybev->ev_read, c->pfd, EV_READ,
proxy_tls_readcb, c->proxybev);
Expand All @@ -251,7 +256,6 @@ proxy_enqueue_req(struct client *c)

serialize_iri(&c->iri, iribuf, sizeof(iribuf));

evb = EVBUFFER_OUTPUT(c->proxybev);
evbuffer_add_printf(evb, "%s\r\n", iribuf);

bufferevent_enable(c->proxybev, EV_READ|EV_WRITE);
Expand Down Expand Up @@ -287,6 +291,40 @@ proxy_handshake(int fd, short event, void *d)
proxy_enqueue_req(c);
}

static ssize_t
proxy_read_cb(struct tls *ctx, void *buf, size_t buflen, void *cb_arg)
{
struct client *c = cb_arg;
ssize_t ret;

ret = read(c->pfd, buf, buflen);
if (ret == -1 && errno == EAGAIN)
ret = TLS_WANT_POLLIN;
return ret;
}

static ssize_t
proxy_write_cb(struct tls *ctx, const void *buf, size_t len, void *cb_arg)
{
struct client *c = cb_arg;
ssize_t ret;
size_t left;

while ((left = c->buf.len - c->buf.read_pos) > 0) {
ret = write(c->pfd, c->buf.data + c->buf.read_pos, left);
if (ret == -1 && errno == EAGAIN)
return (TLS_WANT_POLLOUT);
if (ret <= 0)
return (ret);
c->buf.read_pos += ret;
}

ret = write(c->pfd, buf, len);
if (ret == -1 && errno == EAGAIN)
ret = TLS_WANT_POLLOUT;
return (ret);
}

static int
proxy_setup_tls(struct client *c)
{
Expand Down Expand Up @@ -323,7 +361,8 @@ proxy_setup_tls(struct client *c)

if (*(hn = p->sni) == '\0')
hn = p->host;
if (tls_connect_socket(c->proxyctx, c->pfd, hn) == -1)
if (tls_connect_cbs(c->proxyctx, proxy_read_cb, proxy_write_cb, c, hn)
== -1)
goto err;

c->proxyevset = 1;
Expand Down
3 changes: 2 additions & 1 deletion regress/lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ run_test() {
host="$REGRESS_HOST"
port=10965
proxy_port=10966
proxy=
config_common="log syslog off"
hdr=
body=
Expand Down Expand Up @@ -72,7 +73,7 @@ server "$server_name" {
cert "$PWD/localhost.pem"
key "$PWD/localhost.key"
root "$PWD/testdata"
listen on $host port $port
listen on $host port $port $proxy
$2
}
EOF
Expand Down
1 change: 1 addition & 0 deletions regress/regress
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ run_test test_ipv4_addr
run_test test_ipv6_addr need_ipv6
run_test test_ipv6_server need_ipv6
run_test test_high_prefork
run_test test_proxy_protocol_v1

# TODO: add test that uses only a TLSv1.2 or TLSv1.3
# TODO: add a test that attempt to serve a non-regular file
Expand Down
29 changes: 29 additions & 0 deletions regress/tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -533,3 +533,32 @@ test_high_prefork() {
dont_check_server_alive=yes
kill "$(cat gmid.pid)" 2>/dev/null || true
}

test_proxy_protocol_v1() {
rm -f log
proxy=proxy-v1
gen_config '
log access "'$PWD'/log"
log style legacy' ''
set_proxy 'proxy-v1'
run

ggflags="-P localhost:$proxy_port -H localhost.local"

# This will generate two log entry: one for the "frontend"
# and one for the proxied request. Both should have exactly
# the same IP and port.
fetch_hdr /
check_reply '20 text/gemini'

n=$(awk '{print $1}' log | uniq | wc -l)
if [ "$n" -ne 1 ]; then
# keep the log for post-mortem analysis
echo
echo "n is $n"
return 1
fi

rm -f log
return 0
}
34 changes: 28 additions & 6 deletions server.c
Original file line number Diff line number Diff line change
Expand Up @@ -556,21 +556,23 @@ check_matching_certificate(X509_STORE *store, struct client *c)
}

static int
proxy_socket(struct client *c, const char *host, const char *port)
proxy_socket(struct client *c, struct proxy *p)
{
struct addrinfo hints, *res, *res0;
int r, sock, save_errno;
const char *cause = NULL;
char to[NI_MAXHOST], to_port[NI_MAXSERV];
int err;

memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

/* XXX: asr_run? :> */
r = getaddrinfo(host, port, &hints, &res0);
r = getaddrinfo(p->host, p->port, &hints, &res0);
if (r != 0) {
log_warnx("getaddrinfo(\"%s\", \"%s\"): %s",
host, port, gai_strerror(r));
p->host, p->port, gai_strerror(r));
return -1;
}

Expand All @@ -595,10 +597,30 @@ proxy_socket(struct client *c, const char *host, const char *port)
}

if (sock == -1)
log_warn("can't connect to %s:%s: %s", host, port, cause);
log_warn("can't connect to %s:%s: %s", p->host, p->port, cause);

if (res && sock != -1 && p->proxy) {
err = getnameinfo(res->ai_addr, res->ai_addrlen,
to, sizeof(to), to_port, sizeof(to_port),
NI_NUMERICHOST|NI_NUMERICSERV);
if (err != 0) {
log_warnx("getnameinfo failed: %s", gai_strerror(err));
strlcpy(to, c->rhost, sizeof(to));
strlcpy(to_port, c->rserv, sizeof(to_port));
}

freeaddrinfo(res0);
r = snprintf(c->buf.data, sizeof(c->buf.data),
"PROXY TCP%c %s %s %s %s\r\n",
c->addr->ai_family == AF_INET ? '4' : '6',
c->rhost, to, c->rserv, to_port);
if (r < 0 || (size_t)r >= sizeof(c->buf.data)) {
log_warnx("failed serialize info for the proxy protocol");
c->buf.data[0] = '\0';
} else
c->buf.len = r;
}

freeaddrinfo(res0);
return sock;
}

Expand All @@ -618,7 +640,7 @@ apply_reverse_proxy(struct client *c)

log_debug("opening proxy connection for %s:%s", p->host, p->port);

if ((c->pfd = proxy_socket(c, p->host, p->port)) == -1) {
if ((c->pfd = proxy_socket(c, p)) == -1) {
start_reply(c, PROXY_ERROR, "proxy error");
return 1;
}
Expand Down

0 comments on commit 040f7aa

Please sign in to comment.