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