1
2 /*
3 * Copyright (C) Roman Arutyunyan
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10
11
12 #define NGX_PROXY_PROTOCOL_AF_INET 1
13 #define NGX_PROXY_PROTOCOL_AF_INET6 2
14
15
16 #define ngx_proxy_protocol_parse_uint16(p) ((p)[0] << 8 | (p)[1])
17
18
19 typedef struct {
20 u_char signature[12];
21 u_char version_command;
22 u_char family_transport;
23 u_char len[2];
24 } ngx_proxy_protocol_header_t;
25
26
27 typedef struct {
28 u_char src_addr[4];
29 u_char dst_addr[4];
30 u_char src_port[2];
31 u_char dst_port[2];
32 } ngx_proxy_protocol_inet_addrs_t;
33
34
35 typedef struct {
36 u_char src_addr[16];
37 u_char dst_addr[16];
38 u_char src_port[2];
39 u_char dst_port[2];
40 } ngx_proxy_protocol_inet6_addrs_t;
41
42
43 static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p,
44 u_char *last, ngx_str_t *addr);
45 static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last,
46 in_port_t *port, u_char sep);
47 static u_char *ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf,
48 u_char *last);
49
50
51 u_char *
ngx_proxy_protocol_read(ngx_connection_t * c,u_char * buf,u_char * last)52 ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last)
53 {
54 size_t len;
55 u_char *p;
56 ngx_proxy_protocol_t *pp;
57
58 static const u_char signature[] = "\r\n\r\n\0\r\nQUIT\n";
59
60 p = buf;
61 len = last - buf;
62
63 if (len >= sizeof(ngx_proxy_protocol_header_t)
64 && memcmp(p, signature, sizeof(signature) - 1) == 0)
65 {
66 return ngx_proxy_protocol_v2_read(c, buf, last);
67 }
68
69 if (len < 8 || ngx_strncmp(p, "PROXY ", 6) != 0) {
70 goto invalid;
71 }
72
73 p += 6;
74 len -= 6;
75
76 if (len >= 7 && ngx_strncmp(p, "UNKNOWN", 7) == 0) {
77 ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0,
78 "PROXY protocol unknown protocol");
79 p += 7;
80 goto skip;
81 }
82
83 if (len < 5 || ngx_strncmp(p, "TCP", 3) != 0
84 || (p[3] != '4' && p[3] != '6') || p[4] != ' ')
85 {
86 goto invalid;
87 }
88
89 p += 5;
90
91 pp = ngx_pcalloc(c->pool, sizeof(ngx_proxy_protocol_t));
92 if (pp == NULL) {
93 return NULL;
94 }
95
96 p = ngx_proxy_protocol_read_addr(c, p, last, &pp->src_addr);
97 if (p == NULL) {
98 goto invalid;
99 }
100
101 p = ngx_proxy_protocol_read_addr(c, p, last, &pp->dst_addr);
102 if (p == NULL) {
103 goto invalid;
104 }
105
106 p = ngx_proxy_protocol_read_port(p, last, &pp->src_port, ' ');
107 if (p == NULL) {
108 goto invalid;
109 }
110
111 p = ngx_proxy_protocol_read_port(p, last, &pp->dst_port, CR);
112 if (p == NULL) {
113 goto invalid;
114 }
115
116 if (p == last) {
117 goto invalid;
118 }
119
120 if (*p++ != LF) {
121 goto invalid;
122 }
123
124 ngx_log_debug4(NGX_LOG_DEBUG_CORE, c->log, 0,
125 "PROXY protocol src: %V %d, dst: %V %d",
126 &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port);
127
128 c->proxy_protocol = pp;
129
130 return p;
131
132 skip:
133
134 for ( /* void */ ; p < last - 1; p++) {
135 if (p[0] == CR && p[1] == LF) {
136 return p + 2;
137 }
138 }
139
140 invalid:
141
142 ngx_log_error(NGX_LOG_ERR, c->log, 0,
143 "broken header: \"%*s\"", (size_t) (last - buf), buf);
144
145 return NULL;
146 }
147
148
149 static u_char *
ngx_proxy_protocol_read_addr(ngx_connection_t * c,u_char * p,u_char * last,ngx_str_t * addr)150 ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p, u_char *last,
151 ngx_str_t *addr)
152 {
153 size_t len;
154 u_char ch, *pos;
155
156 pos = p;
157
158 for ( ;; ) {
159 if (p == last) {
160 return NULL;
161 }
162
163 ch = *p++;
164
165 if (ch == ' ') {
166 break;
167 }
168
169 if (ch != ':' && ch != '.'
170 && (ch < 'a' || ch > 'f')
171 && (ch < 'A' || ch > 'F')
172 && (ch < '0' || ch > '9'))
173 {
174 return NULL;
175 }
176 }
177
178 len = p - pos - 1;
179
180 addr->data = ngx_pnalloc(c->pool, len);
181 if (addr->data == NULL) {
182 return NULL;
183 }
184
185 ngx_memcpy(addr->data, pos, len);
186 addr->len = len;
187
188 return p;
189 }
190
191
192 static u_char *
ngx_proxy_protocol_read_port(u_char * p,u_char * last,in_port_t * port,u_char sep)193 ngx_proxy_protocol_read_port(u_char *p, u_char *last, in_port_t *port,
194 u_char sep)
195 {
196 size_t len;
197 u_char *pos;
198 ngx_int_t n;
199
200 pos = p;
201
202 for ( ;; ) {
203 if (p == last) {
204 return NULL;
205 }
206
207 if (*p++ == sep) {
208 break;
209 }
210 }
211
212 len = p - pos - 1;
213
214 n = ngx_atoi(pos, len);
215 if (n < 0 || n > 65535) {
216 return NULL;
217 }
218
219 *port = (in_port_t) n;
220
221 return p;
222 }
223
224
225 u_char *
ngx_proxy_protocol_write(ngx_connection_t * c,u_char * buf,u_char * last)226 ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last)
227 {
228 ngx_uint_t port, lport;
229
230 if (last - buf < NGX_PROXY_PROTOCOL_MAX_HEADER) {
231 return NULL;
232 }
233
234 if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
235 return NULL;
236 }
237
238 switch (c->sockaddr->sa_family) {
239
240 case AF_INET:
241 buf = ngx_cpymem(buf, "PROXY TCP4 ", sizeof("PROXY TCP4 ") - 1);
242 break;
243
244 #if (NGX_HAVE_INET6)
245 case AF_INET6:
246 buf = ngx_cpymem(buf, "PROXY TCP6 ", sizeof("PROXY TCP6 ") - 1);
247 break;
248 #endif
249
250 default:
251 return ngx_cpymem(buf, "PROXY UNKNOWN" CRLF,
252 sizeof("PROXY UNKNOWN" CRLF) - 1);
253 }
254
255 buf += ngx_sock_ntop(c->sockaddr, c->socklen, buf, last - buf, 0);
256
257 *buf++ = ' ';
258
259 buf += ngx_sock_ntop(c->local_sockaddr, c->local_socklen, buf, last - buf,
260 0);
261
262 port = ngx_inet_get_port(c->sockaddr);
263 lport = ngx_inet_get_port(c->local_sockaddr);
264
265 return ngx_slprintf(buf, last, " %ui %ui" CRLF, port, lport);
266 }
267
268
269 static u_char *
ngx_proxy_protocol_v2_read(ngx_connection_t * c,u_char * buf,u_char * last)270 ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf, u_char *last)
271 {
272 u_char *end;
273 size_t len;
274 socklen_t socklen;
275 ngx_uint_t version, command, family, transport;
276 ngx_sockaddr_t src_sockaddr, dst_sockaddr;
277 ngx_proxy_protocol_t *pp;
278 ngx_proxy_protocol_header_t *header;
279 ngx_proxy_protocol_inet_addrs_t *in;
280 #if (NGX_HAVE_INET6)
281 ngx_proxy_protocol_inet6_addrs_t *in6;
282 #endif
283
284 header = (ngx_proxy_protocol_header_t *) buf;
285
286 buf += sizeof(ngx_proxy_protocol_header_t);
287
288 version = header->version_command >> 4;
289
290 if (version != 2) {
291 ngx_log_error(NGX_LOG_ERR, c->log, 0,
292 "unknown PROXY protocol version: %ui", version);
293 return NULL;
294 }
295
296 len = ngx_proxy_protocol_parse_uint16(header->len);
297
298 if ((size_t) (last - buf) < len) {
299 ngx_log_error(NGX_LOG_ERR, c->log, 0, "header is too large");
300 return NULL;
301 }
302
303 end = buf + len;
304
305 command = header->version_command & 0x0f;
306
307 /* only PROXY is supported */
308 if (command != 1) {
309 ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
310 "PROXY protocol v2 unsupported command %ui", command);
311 return end;
312 }
313
314 transport = header->family_transport & 0x0f;
315
316 /* only STREAM is supported */
317 if (transport != 1) {
318 ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
319 "PROXY protocol v2 unsupported transport %ui",
320 transport);
321 return end;
322 }
323
324 pp = ngx_pcalloc(c->pool, sizeof(ngx_proxy_protocol_t));
325 if (pp == NULL) {
326 return NULL;
327 }
328
329 family = header->family_transport >> 4;
330
331 switch (family) {
332
333 case NGX_PROXY_PROTOCOL_AF_INET:
334
335 if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_inet_addrs_t)) {
336 return NULL;
337 }
338
339 in = (ngx_proxy_protocol_inet_addrs_t *) buf;
340
341 src_sockaddr.sockaddr_in.sin_family = AF_INET;
342 src_sockaddr.sockaddr_in.sin_port = 0;
343 memcpy(&src_sockaddr.sockaddr_in.sin_addr, in->src_addr, 4);
344
345 dst_sockaddr.sockaddr_in.sin_family = AF_INET;
346 dst_sockaddr.sockaddr_in.sin_port = 0;
347 memcpy(&dst_sockaddr.sockaddr_in.sin_addr, in->dst_addr, 4);
348
349 pp->src_port = ngx_proxy_protocol_parse_uint16(in->src_port);
350 pp->dst_port = ngx_proxy_protocol_parse_uint16(in->dst_port);
351
352 socklen = sizeof(struct sockaddr_in);
353
354 buf += sizeof(ngx_proxy_protocol_inet_addrs_t);
355
356 break;
357
358 #if (NGX_HAVE_INET6)
359
360 case NGX_PROXY_PROTOCOL_AF_INET6:
361
362 if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_inet6_addrs_t)) {
363 return NULL;
364 }
365
366 in6 = (ngx_proxy_protocol_inet6_addrs_t *) buf;
367
368 src_sockaddr.sockaddr_in6.sin6_family = AF_INET6;
369 src_sockaddr.sockaddr_in6.sin6_port = 0;
370 memcpy(&src_sockaddr.sockaddr_in6.sin6_addr, in6->src_addr, 16);
371
372 dst_sockaddr.sockaddr_in6.sin6_family = AF_INET6;
373 dst_sockaddr.sockaddr_in6.sin6_port = 0;
374 memcpy(&dst_sockaddr.sockaddr_in6.sin6_addr, in6->dst_addr, 16);
375
376 pp->src_port = ngx_proxy_protocol_parse_uint16(in6->src_port);
377 pp->dst_port = ngx_proxy_protocol_parse_uint16(in6->dst_port);
378
379 socklen = sizeof(struct sockaddr_in6);
380
381 buf += sizeof(ngx_proxy_protocol_inet6_addrs_t);
382
383 break;
384
385 #endif
386
387 default:
388 ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
389 "PROXY protocol v2 unsupported address family %ui",
390 family);
391 return end;
392 }
393
394 pp->src_addr.data = ngx_pnalloc(c->pool, NGX_SOCKADDR_STRLEN);
395 if (pp->src_addr.data == NULL) {
396 return NULL;
397 }
398
399 pp->src_addr.len = ngx_sock_ntop(&src_sockaddr.sockaddr, socklen,
400 pp->src_addr.data, NGX_SOCKADDR_STRLEN, 0);
401
402 pp->dst_addr.data = ngx_pnalloc(c->pool, NGX_SOCKADDR_STRLEN);
403 if (pp->dst_addr.data == NULL) {
404 return NULL;
405 }
406
407 pp->dst_addr.len = ngx_sock_ntop(&dst_sockaddr.sockaddr, socklen,
408 pp->dst_addr.data, NGX_SOCKADDR_STRLEN, 0);
409
410 ngx_log_debug4(NGX_LOG_DEBUG_CORE, c->log, 0,
411 "PROXY protocol v2 src: %V %d, dst: %V %d",
412 &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port);
413
414 if (buf < end) {
415 ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
416 "PROXY protocol v2 %z bytes of tlv ignored", end - buf);
417 }
418
419 c->proxy_protocol = pp;
420
421 return end;
422 }
423