Blob


1 /*
2 * Copyright (c) 2026 Christoph Liebender <christoph@liebender.dev>
3 *
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.
7 *
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.
15 */
17 #include "relayd.h"
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
29 };
31 struct proxy_v2_hdr {
32 u_int8_t sig[12];
33 u_int8_t ver_cmd;
34 u_int8_t fam;
35 u_int16_t len;
36 };
38 union proxy_v2_addr {
39 struct {
40 u_int32_t src_addr;
41 u_int32_t dst_addr;
42 in_port_t src_port;
43 in_port_t dst_port;
44 } ipv4_addr;
45 struct {
46 u_int8_t src_addr[16];
47 u_int8_t dst_addr[16];
48 in_port_t src_port;
49 in_port_t dst_port;
50 } ipv6_addr;
51 };
53 int
54 proxy_protocol_v1(struct rsession *con, struct evbuffer *dstout)
55 {
56 char ibuf[128], obuf[128];
57 const char *proxyproto;
58 int ret;
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)
65 return -1;
67 if (con->se_relay->rl_conf.flags & F_UDP)
68 proxyproto = "UNKNOWN";
69 else {
70 switch (con->se_in.ss.ss_family) {
71 case AF_INET:
72 proxyproto = "TCP4";
73 break;
74 case AF_INET6:
75 proxyproto = "TCP6";
76 break;
77 default:
78 proxyproto = "UNKNOWN";
79 break;
80 }
81 }
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;
88 }
90 int
91 proxy_protocol_v2(struct rsession *con, struct evbuffer *dstout)
92 {
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;
98 int error;
99 in_port_t srcport = con->se_in.port;
100 in_port_t dstport = conf->port;
101 u_int16_t len;
103 bcopy(PROXY_V2_SIG, hdr.sig, sizeof(hdr.sig));
104 hdr.ver_cmd = 0x20 | PROXY_V2_CMD_PROXY;
106 switch (dstss->ss_family) {
107 case AF_INET:
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;
117 break;
118 case AF_INET6:
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;
128 break;
129 default:
130 hdr.fam = 0x00;
131 len = 0;
132 break;
135 hdr.len = htons(len);
137 if ((error = evbuffer_add(dstout, &hdr, sizeof(hdr))) != 0)
138 return error;
140 if (len > 0 && (error = evbuffer_add(dstout, &addr, len)) != 0)
141 return error;
143 return 0;