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_v2_read(ngx_connection_t *c, u_char *buf,
44 u_char *last);
45
46
47 u_char *
ngx_proxy_protocol_read(ngx_connection_t * c,u_char * buf,u_char * last)48 ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last)
49 {
50 size_t len;
51 u_char ch, *p, *addr, *port;
52 ngx_int_t n;
53
54 static const u_char signature[] = "\r\n\r\n\0\r\nQUIT\n";
55
56 p = buf;
57 len = last - buf;
58
59 if (len >= sizeof(ngx_proxy_protocol_header_t)
60 && memcmp(p, signature, sizeof(signature) - 1) == 0)
61 {
62 return ngx_proxy_protocol_v2_read(c, buf, last);
63 }
64
65 if (len < 8 || ngx_strncmp(p, "PROXY ", 6) != 0) {
66 goto invalid;
67 }
68
69 p += 6;
70 len -= 6;
71
72 if (len >= 7 && ngx_strncmp(p, "UNKNOWN", 7) == 0) {
73 ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0,
74 "PROXY protocol unknown protocol");
75 p += 7;
76 goto skip;
77 }
78
79 if (len < 5 || ngx_strncmp(p, "TCP", 3) != 0
80 || (p[3] != '4' && p[3] != '6') || p[4] != ' ')
81 {
82 goto invalid;
83 }
84
85 p += 5;
86 addr = p;
87
88 for ( ;; ) {
89 if (p == last) {
90 goto invalid;
91 }
92
93 ch = *p++;
94
95 if (ch == ' ') {
96 break;
97 }
98
99 if (ch != ':' && ch != '.'
100 && (ch < 'a' || ch > 'f')
101 && (ch < 'A' || ch > 'F')
102 && (ch < '0' || ch > '9'))
103 {
104 goto invalid;
105 }
106 }
107
108 len = p - addr - 1;
109 c->proxy_protocol_addr.data = ngx_pnalloc(c->pool, len);
110
111 if (c->proxy_protocol_addr.data == NULL) {
112 return NULL;
113 }
114
115 ngx_memcpy(c->proxy_protocol_addr.data, addr, len);
116 c->proxy_protocol_addr.len = len;
117
118 for ( ;; ) {
119 if (p == last) {
120 goto invalid;
121 }
122
123 if (*p++ == ' ') {
124 break;
125 }
126 }
127
128 port = p;
129
130 for ( ;; ) {
131 if (p == last) {
132 goto invalid;
133 }
134
135 if (*p++ == ' ') {
136 break;
137 }
138 }
139
140 len = p - port - 1;
141
142 n = ngx_atoi(port, len);
143
144 if (n < 0 || n > 65535) {
145 goto invalid;
146 }
147
148 c->proxy_protocol_port = (in_port_t) n;
149
150 ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
151 "PROXY protocol address: %V %d", &c->proxy_protocol_addr,
152 c->proxy_protocol_port);
153
154 skip:
155
156 for ( /* void */ ; p < last - 1; p++) {
157 if (p[0] == CR && p[1] == LF) {
158 return p + 2;
159 }
160 }
161
162 invalid:
163
164 ngx_log_error(NGX_LOG_ERR, c->log, 0,
165 "broken header: \"%*s\"", (size_t) (last - buf), buf);
166
167 return NULL;
168 }
169
170
171 u_char *
ngx_proxy_protocol_write(ngx_connection_t * c,u_char * buf,u_char * last)172 ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last)
173 {
174 ngx_uint_t port, lport;
175
176 if (last - buf < NGX_PROXY_PROTOCOL_MAX_HEADER) {
177 return NULL;
178 }
179
180 if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
181 return NULL;
182 }
183
184 switch (c->sockaddr->sa_family) {
185
186 case AF_INET:
187 buf = ngx_cpymem(buf, "PROXY TCP4 ", sizeof("PROXY TCP4 ") - 1);
188 break;
189
190 #if (NGX_HAVE_INET6)
191 case AF_INET6:
192 buf = ngx_cpymem(buf, "PROXY TCP6 ", sizeof("PROXY TCP6 ") - 1);
193 break;
194 #endif
195
196 default:
197 return ngx_cpymem(buf, "PROXY UNKNOWN" CRLF,
198 sizeof("PROXY UNKNOWN" CRLF) - 1);
199 }
200
201 buf += ngx_sock_ntop(c->sockaddr, c->socklen, buf, last - buf, 0);
202
203 *buf++ = ' ';
204
205 buf += ngx_sock_ntop(c->local_sockaddr, c->local_socklen, buf, last - buf,
206 0);
207
208 port = ngx_inet_get_port(c->sockaddr);
209 lport = ngx_inet_get_port(c->local_sockaddr);
210
211 return ngx_slprintf(buf, last, " %ui %ui" CRLF, port, lport);
212 }
213
214
215 static u_char *
ngx_proxy_protocol_v2_read(ngx_connection_t * c,u_char * buf,u_char * last)216 ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf, u_char *last)
217 {
218 u_char *end;
219 size_t len;
220 socklen_t socklen;
221 ngx_uint_t version, command, family, transport;
222 ngx_sockaddr_t sockaddr;
223 ngx_proxy_protocol_header_t *header;
224 ngx_proxy_protocol_inet_addrs_t *in;
225 #if (NGX_HAVE_INET6)
226 ngx_proxy_protocol_inet6_addrs_t *in6;
227 #endif
228
229 header = (ngx_proxy_protocol_header_t *) buf;
230
231 buf += sizeof(ngx_proxy_protocol_header_t);
232
233 version = header->version_command >> 4;
234
235 if (version != 2) {
236 ngx_log_error(NGX_LOG_ERR, c->log, 0,
237 "unknown PROXY protocol version: %ui", version);
238 return NULL;
239 }
240
241 len = ngx_proxy_protocol_parse_uint16(header->len);
242
243 if ((size_t) (last - buf) < len) {
244 ngx_log_error(NGX_LOG_ERR, c->log, 0, "header is too large");
245 return NULL;
246 }
247
248 end = buf + len;
249
250 command = header->version_command & 0x0f;
251
252 /* only PROXY is supported */
253 if (command != 1) {
254 ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
255 "PROXY protocol v2 unsupported command %ui", command);
256 return end;
257 }
258
259 transport = header->family_transport & 0x0f;
260
261 /* only STREAM is supported */
262 if (transport != 1) {
263 ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
264 "PROXY protocol v2 unsupported transport %ui",
265 transport);
266 return end;
267 }
268
269 family = header->family_transport >> 4;
270
271 switch (family) {
272
273 case NGX_PROXY_PROTOCOL_AF_INET:
274
275 if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_inet_addrs_t)) {
276 return NULL;
277 }
278
279 in = (ngx_proxy_protocol_inet_addrs_t *) buf;
280
281 sockaddr.sockaddr_in.sin_family = AF_INET;
282 sockaddr.sockaddr_in.sin_port = 0;
283 memcpy(&sockaddr.sockaddr_in.sin_addr, in->src_addr, 4);
284
285 c->proxy_protocol_port = ngx_proxy_protocol_parse_uint16(in->src_port);
286
287 socklen = sizeof(struct sockaddr_in);
288
289 buf += sizeof(ngx_proxy_protocol_inet_addrs_t);
290
291 break;
292
293 #if (NGX_HAVE_INET6)
294
295 case NGX_PROXY_PROTOCOL_AF_INET6:
296
297 if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_inet6_addrs_t)) {
298 return NULL;
299 }
300
301 in6 = (ngx_proxy_protocol_inet6_addrs_t *) buf;
302
303 sockaddr.sockaddr_in6.sin6_family = AF_INET6;
304 sockaddr.sockaddr_in6.sin6_port = 0;
305 memcpy(&sockaddr.sockaddr_in6.sin6_addr, in6->src_addr, 16);
306
307 c->proxy_protocol_port = ngx_proxy_protocol_parse_uint16(in6->src_port);
308
309 socklen = sizeof(struct sockaddr_in6);
310
311 buf += sizeof(ngx_proxy_protocol_inet6_addrs_t);
312
313 break;
314
315 #endif
316
317 default:
318 ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
319 "PROXY protocol v2 unsupported address family %ui",
320 family);
321 return end;
322 }
323
324 c->proxy_protocol_addr.data = ngx_pnalloc(c->pool, NGX_SOCKADDR_STRLEN);
325 if (c->proxy_protocol_addr.data == NULL) {
326 return NULL;
327 }
328
329 c->proxy_protocol_addr.len = ngx_sock_ntop(&sockaddr.sockaddr, socklen,
330 c->proxy_protocol_addr.data,
331 NGX_SOCKADDR_STRLEN, 0);
332
333 ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
334 "PROXY protocol v2 address: %V %d", &c->proxy_protocol_addr,
335 c->proxy_protocol_port);
336
337 if (buf < end) {
338 ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
339 "PROXY protocol v2 %z bytes of tlv ignored", end - buf);
340 }
341
342 return end;
343 }
344