12#if PC_ENABLE_PROXY_PROTOCOL
17static const uint8_t kV2Sig[PROXY_V2_SIG_LEN] = {0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D,
18 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A};
20static uint16_t rd16(
const uint8_t *p)
22 return (uint16_t)(((uint16_t)p[0] << 8) | p[1]);
25static uint32_t rd32(
const uint8_t *p)
27 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | p[3];
31static bool parse_ipv4(
const char *s,
size_t n, uint32_t *out)
38 if (i >= n || s[i] <
'0' || s[i] >
'9')
44 while (i < n && s[i] >=
'0' && s[i] <=
'9')
46 o = o * 10 + (uint32_t)(s[i] -
'0');
48 if (++digits > 3 || o > 255)
57 if (i >= n || s[i] !=
'.')
72static bool parse_u16(
const char *s,
size_t n, uint16_t *out)
81 for (
size_t i = 0; i < n; i++)
83 if (s[i] <
'0' || s[i] >
'9')
87 v = v * 10 + (uint32_t)(s[i] -
'0');
98static bool parse_v1(
const uint8_t *buf,
size_t len, ProxyInfo *out,
size_t *consumed)
102 size_t scan = len < 108 ? len : 108;
103 for (
size_t i = 0; i + 1 < scan; i++)
105 if (buf[i] ==
'\r' && buf[i + 1] ==
'\n')
116 const char *s = (
const char *)buf;
122 while (i < crlf && ntok < 6)
124 while (i < crlf && s[i] ==
' ')
133 while (i < crlf && s[i] !=
' ')
137 tok[ntok] = s + start;
138 tlen[ntok] = i - start;
142 out->has_addr =
false;
143 out->src_addr = out->dst_addr = 0;
144 out->src_port = out->dst_port = 0;
145 *consumed = crlf + 2;
147 if (ntok == 6 && tlen[1] == 4 && memcmp(tok[1],
"TCP4", 4) == 0)
149 if (parse_ipv4(tok[2], tlen[2], &out->src_addr) && parse_ipv4(tok[3], tlen[3], &out->dst_addr) &&
150 parse_u16(tok[4], tlen[4], &out->src_port) && parse_u16(tok[5], tlen[5], &out->dst_port))
152 out->has_addr =
true;
158bool proxy_parse(
const uint8_t *buf,
size_t len, ProxyInfo *out,
size_t *consumed)
160 if (!buf || !out || !consumed)
166 if (len >= PROXY_V2_SIG_LEN && memcmp(buf, kV2Sig, PROXY_V2_SIG_LEN) == 0)
172 uint8_t ver_cmd = buf[12];
173 uint8_t fam = buf[13];
174 uint16_t addr_len = rd16(buf + 14);
175 size_t total = 16 + (size_t)addr_len;
180 if ((ver_cmd & 0xF0) != 0x20)
185 out->has_addr =
false;
186 out->src_addr = out->dst_addr = 0;
187 out->src_port = out->dst_port = 0;
188 if (ver_cmd == PROXY_V2_VER_CMD_PROXY && fam == PROXY_V2_FAM_TCP4 && addr_len >= 12)
190 out->src_addr = rd32(buf + 16);
191 out->dst_addr = rd32(buf + 20);
192 out->src_port = rd16(buf + 24);
193 out->dst_port = rd16(buf + 26);
194 out->has_addr =
true;
201 if (len >= 6 && memcmp(buf,
"PROXY ", 6) == 0)
203 return parse_v1(buf, len, out, consumed);
209size_t proxy_v1_build(
char *buf,
size_t cap, uint32_t src_addr, uint32_t dst_addr, uint16_t src_port, uint16_t dst_port)
215 pc_sb sb_buf = {buf, cap, 0,
true};
217 pc_sb_u32(&sb_buf, (uint32_t)((
unsigned)((src_addr >> 24) & 0xFF)));
219 pc_sb_u32(&sb_buf, (uint32_t)((
unsigned)((src_addr >> 16) & 0xFF)));
221 pc_sb_u32(&sb_buf, (uint32_t)((
unsigned)((src_addr >> 8) & 0xFF)));
223 pc_sb_u32(&sb_buf, (uint32_t)((
unsigned)(src_addr & 0xFF)));
225 pc_sb_u32(&sb_buf, (uint32_t)((
unsigned)((dst_addr >> 24) & 0xFF)));
227 pc_sb_u32(&sb_buf, (uint32_t)((
unsigned)((dst_addr >> 16) & 0xFF)));
229 pc_sb_u32(&sb_buf, (uint32_t)((
unsigned)((dst_addr >> 8) & 0xFF)));
231 pc_sb_u32(&sb_buf, (uint32_t)((
unsigned)(dst_addr & 0xFF)));
233 pc_sb_u32(&sb_buf, (uint32_t)((
unsigned)src_port));
235 pc_sb_u32(&sb_buf, (uint32_t)((
unsigned)dst_port));
240 if (n < 0 || (
size_t)n >= cap)
247size_t proxy_v2_build(uint8_t *buf,
size_t cap, uint32_t src_addr, uint32_t dst_addr, uint16_t src_port,
250 const size_t total = 16 + 12;
251 if (!buf || cap < total)
255 memcpy(buf, kV2Sig, PROXY_V2_SIG_LEN);
256 buf[12] = PROXY_V2_VER_CMD_PROXY;
257 buf[13] = PROXY_V2_FAM_TCP4;
260 buf[16] = (uint8_t)(src_addr >> 24);
261 buf[17] = (uint8_t)(src_addr >> 16);
262 buf[18] = (uint8_t)(src_addr >> 8);
263 buf[19] = (uint8_t)(src_addr);
264 buf[20] = (uint8_t)(dst_addr >> 24);
265 buf[21] = (uint8_t)(dst_addr >> 16);
266 buf[22] = (uint8_t)(dst_addr >> 8);
267 buf[23] = (uint8_t)(dst_addr);
268 buf[24] = (uint8_t)(src_port >> 8);
269 buf[25] = (uint8_t)(src_port);
270 buf[26] = (uint8_t)(dst_port >> 8);
271 buf[27] = (uint8_t)(dst_port);
HAProxy PROXY protocol codec (PC_ENABLE_PROXY_PROTOCOL) - zero-heap parser + builder for the v1 (text...
Bounded no-heap string builder that fails closed on overflow (one shared copy).
size_t pc_sb_finish(pc_sb *b)
NUL-terminate and return the built length, or 0 if the build overflowed.
void pc_sb_put(pc_sb *b, const char *s)
Append NUL-terminated s; leaves the buffer untouched and clears ok if it would not fit.
void pc_sb_u32(pc_sb *b, uint32_t v)
Append v as decimal (no leading zeros; "0" for zero).
Bump-append target; ok latches false once an append would overflow cap.