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