2 * Copyright (c) 2026 Christoph Liebender <christoph@liebender.dev>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #define PROXY_V2_CMD_PROXY 0x01
21 #define PROXY_V2_FAM_UNSPEC 0x00
22 #define PROXY_V2_FAM_TCP4 0x11
23 #define PROXY_V2_FAM_UDP4 0x12
24 #define PROXY_V2_FAM_TCP6 0x21
25 #define PROXY_V2_FAM_UDP6 0x22
27 static const u_int8_t PROXY_V2_SIG[12] = {
28 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A
46 u_int8_t src_addr[16];
47 u_int8_t dst_addr[16];
54 proxy_protocol_v1(struct rsession *con, struct evbuffer *dstout)
56 char ibuf[128], obuf[128];
57 const char *proxyproto;
60 bzero(&ibuf, sizeof(ibuf));
61 bzero(&obuf, sizeof(obuf));
63 if (print_host(&con->se_in.ss, ibuf, sizeof(ibuf)) == NULL ||
64 print_host(&con->se_sockname, obuf, sizeof(obuf)) == NULL)
67 if (con->se_relay->rl_conf.flags & F_UDP)
68 proxyproto = "UNKNOWN";
70 switch (con->se_in.ss.ss_family) {
78 proxyproto = "UNKNOWN";
83 ret = evbuffer_add_printf(dstout,
84 "PROXY %s %s %s %d %d\r\n", proxyproto, ibuf, obuf,
85 ntohs(con->se_in.port), ntohs(con->se_relay->rl_conf.port));
87 return ret == -1 ? -1 : 0;
91 proxy_protocol_v2(struct rsession *con, struct evbuffer *dstout)
93 union proxy_v2_addr addr;
94 struct proxy_v2_hdr hdr;
95 const struct relay_config *conf = &con->se_relay->rl_conf;
96 const struct sockaddr_storage *srcss = &con->se_in.ss;
97 const struct sockaddr_storage *dstss = &con->se_sockname;
99 in_port_t srcport = con->se_in.port;
100 in_port_t dstport = conf->port;
103 bcopy(PROXY_V2_SIG, hdr.sig, sizeof(hdr.sig));
104 hdr.ver_cmd = 0x20 | PROXY_V2_CMD_PROXY;
106 switch (dstss->ss_family) {
108 hdr.fam = (conf->flags & F_UDP) ?
109 PROXY_V2_FAM_UDP4 : PROXY_V2_FAM_TCP4;
110 len = sizeof(addr.ipv4_addr);
111 addr.ipv4_addr.src_addr =
112 ((const struct sockaddr_in *)srcss)->sin_addr.s_addr;
113 addr.ipv4_addr.dst_addr =
114 ((const struct sockaddr_in *)dstss)->sin_addr.s_addr;
115 addr.ipv4_addr.src_port = srcport;
116 addr.ipv4_addr.dst_port = dstport;
119 hdr.fam = (conf->flags & F_UDP) ?
120 PROXY_V2_FAM_UDP6 : PROXY_V2_FAM_TCP6;
121 len = sizeof(addr.ipv6_addr);
122 bcopy(&((const struct sockaddr_in6 *)srcss)->sin6_addr,
123 addr.ipv6_addr.src_addr, sizeof(addr.ipv6_addr.src_addr));
124 bcopy(&((const struct sockaddr_in6 *)dstss)->sin6_addr,
125 addr.ipv6_addr.dst_addr, sizeof(addr.ipv6_addr.dst_addr));
126 addr.ipv6_addr.src_port = srcport;
127 addr.ipv6_addr.dst_port = dstport;
135 hdr.len = htons(len);
137 if ((error = evbuffer_add(dstout, &hdr, sizeof(hdr))) != 0)
140 if (len > 0 && (error = evbuffer_add(dstout, &addr, len)) != 0)