nxt_sockaddr.c (611:323e11065f83) nxt_sockaddr.c (703:2d536dde84d2)
1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) NGINX, Inc.
5 */
6
7#include <nxt_main.h>
8
9
10#if (NXT_INET6)
11static u_char *nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end);
12#endif
13
14static nxt_sockaddr_t *nxt_sockaddr_unix_parse(nxt_mp_t *mp, nxt_str_t *addr);
15static nxt_sockaddr_t *nxt_sockaddr_inet6_parse(nxt_mp_t *mp, nxt_str_t *addr);
16static nxt_sockaddr_t *nxt_sockaddr_inet_parse(nxt_mp_t *mp, nxt_str_t *addr);
17
18static nxt_int_t nxt_job_sockaddr_unix_parse(nxt_job_sockaddr_parse_t *jbs);
19static nxt_int_t nxt_job_sockaddr_inet6_parse(nxt_job_sockaddr_parse_t *jbs);
20static nxt_int_t nxt_job_sockaddr_inet_parse(nxt_job_sockaddr_parse_t *jbs);
21
22
23nxt_sockaddr_t *
24nxt_sockaddr_cache_alloc(nxt_event_engine_t *engine, nxt_listen_socket_t *ls)
25{
26 uint8_t hint;
27 size_t size;
28 nxt_sockaddr_t *sa;
29
30 hint = (uint8_t) -1;
31 size = offsetof(nxt_sockaddr_t, u) + ls->socklen + ls->address_length;
32
33 sa = nxt_event_engine_mem_alloc(engine, &hint, size);
34
35 if (nxt_fast_path(sa != NULL)) {
36 /* Zero only beginning of structure up to sockaddr_un.sun_path[1]. */
37 nxt_memzero(sa, offsetof(nxt_sockaddr_t, u.sockaddr.sa_data[1]));
38
39 sa->cache_hint = hint;
40 sa->socklen = ls->socklen;
41 sa->length = ls->address_length;
42
43 sa->type = ls->sockaddr->type;
44 /*
45 * Set address family for unspecified Unix domain socket,
46 * because these sockaddr's are not updated by old BSD systems,
47 * see comment in nxt_conn_io_accept().
48 */
49 sa->u.sockaddr.sa_family = ls->sockaddr->u.sockaddr.sa_family;
50 }
51
52 return sa;
53}
54
55
56void
57nxt_sockaddr_cache_free(nxt_event_engine_t *engine, nxt_conn_t *c)
58{
59 nxt_event_engine_mem_free(engine, &c->remote->cache_hint, c->remote);
60}
61
62
63nxt_sockaddr_t *
64nxt_sockaddr_alloc(nxt_mp_t *mp, socklen_t socklen, size_t address_length)
65{
66 size_t size;
67 nxt_sockaddr_t *sa;
68
69 size = offsetof(nxt_sockaddr_t, u) + socklen + address_length;
70
71 /*
72 * The current struct sockaddr's define 32-bit fields at maximum
73 * and may define 64-bit AF_INET6 fields in the future. Alignment
74 * of memory allocated by nxt_mp_zalloc() is enough for these fields.
75 * If 128-bit alignment will be required then nxt_mem_malloc() and
76 * nxt_memzero() should be used instead.
77 */
78
79 sa = nxt_mp_zalloc(mp, size);
80
81 if (nxt_fast_path(sa != NULL)) {
82 sa->socklen = socklen;
83 sa->length = address_length;
84 }
85
86 return sa;
87}
88
89
90nxt_sockaddr_t *
91nxt_sockaddr_create(nxt_mp_t *mp, struct sockaddr *sockaddr, socklen_t length,
92 size_t address_length)
93{
94 size_t size, copy;
95 nxt_sockaddr_t *sa;
96
97 size = length;
98 copy = length;
99
100#if (NXT_HAVE_UNIX_DOMAIN)
101
102 /*
103 * Unspecified Unix domain sockaddr_un form and length are very
104 * platform depended (see comment in nxt_socket.h). Here they are
105 * normalized to the sockaddr_un with single zero byte sun_path[].
106 */
107
108 if (size <= offsetof(struct sockaddr_un, sun_path)) {
109 /*
110 * Small socket length means a short unspecified Unix domain
111 * socket address:
112 *
113 * getsockname() and getpeername() on OpenBSD prior to 5.3
114 * return zero length and does not update a passed sockaddr
115 * buffer at all.
116 *
117 * Linux returns length equal to 2, i.e. sockaddr_un without
118 * sun_path[], unix(7):
119 *
120 * unnamed: A stream socket that has not been bound
121 * to a pathname using bind(2) has no name. Likewise,
122 * the two sockets created by socketpair(2) are unnamed.
123 * When the address of an unnamed socket is returned by
124 * getsockname(2), getpeername(2), and accept(2), its
125 * length is sizeof(sa_family_t), and sun_path should
126 * not be inspected.
127 */
128 size = offsetof(struct sockaddr_un, sun_path) + 1;
129
130#if !(NXT_LINUX)
131
132 } else if (sockaddr->sa_family == AF_UNIX && sockaddr->sa_data[0] == '\0') {
133 /*
134 * Omit nonsignificant zeros of the unspecified Unix domain socket
135 * address. This test is disabled for Linux since Linux abstract
136 * socket address also starts with zero. However Linux unspecified
137 * Unix domain socket address is short and is handled above.
138 */
139 size = offsetof(struct sockaddr_un, sun_path) + 1;
140 copy = size;
141
142#endif
143 }
144
145#endif /* NXT_HAVE_UNIX_DOMAIN */
146
147 sa = nxt_sockaddr_alloc(mp, size, address_length);
148
149 if (nxt_fast_path(sa != NULL)) {
150 nxt_memcpy(&sa->u.sockaddr, sockaddr, copy);
151
152#if (NXT_HAVE_UNIX_DOMAIN && NXT_OPENBSD)
153
154 if (length == 0) {
155 sa->u.sockaddr.sa_family = AF_UNIX;
156 }
157
158#endif
159 }
160
161 return sa;
162}
163
164
165nxt_sockaddr_t *
166nxt_sockaddr_copy(nxt_mp_t *mp, nxt_sockaddr_t *src)
167{
168 size_t length;
169 nxt_sockaddr_t *dst;
170
171 length = offsetof(nxt_sockaddr_t, u) + src->socklen;
172
173 dst = nxt_mp_alloc(mp, length);
174
175 if (nxt_fast_path(dst != NULL)) {
176 nxt_memcpy(dst, src, length);
177 }
178
179 return dst;
180}
181
182
183nxt_sockaddr_t *
184nxt_getsockname(nxt_task_t *task, nxt_mp_t *mp, nxt_socket_t s)
185{
186 int ret;
187 size_t length;
188 socklen_t socklen;
189 nxt_sockaddr_buf_t sockaddr;
190
191 socklen = NXT_SOCKADDR_LEN;
192
193 ret = getsockname(s, &sockaddr.buf, &socklen);
194
195 if (nxt_fast_path(ret == 0)) {
196
197 switch (sockaddr.buf.sa_family) {
198#if (NXT_INET6)
199 case AF_INET6:
200 length = NXT_INET6_ADDR_STR_LEN;
201 break;
202#endif
203
204#if (NXT_HAVE_UNIX_DOMAIN)
205 case AF_UNIX:
1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) NGINX, Inc.
5 */
6
7#include <nxt_main.h>
8
9
10#if (NXT_INET6)
11static u_char *nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end);
12#endif
13
14static nxt_sockaddr_t *nxt_sockaddr_unix_parse(nxt_mp_t *mp, nxt_str_t *addr);
15static nxt_sockaddr_t *nxt_sockaddr_inet6_parse(nxt_mp_t *mp, nxt_str_t *addr);
16static nxt_sockaddr_t *nxt_sockaddr_inet_parse(nxt_mp_t *mp, nxt_str_t *addr);
17
18static nxt_int_t nxt_job_sockaddr_unix_parse(nxt_job_sockaddr_parse_t *jbs);
19static nxt_int_t nxt_job_sockaddr_inet6_parse(nxt_job_sockaddr_parse_t *jbs);
20static nxt_int_t nxt_job_sockaddr_inet_parse(nxt_job_sockaddr_parse_t *jbs);
21
22
23nxt_sockaddr_t *
24nxt_sockaddr_cache_alloc(nxt_event_engine_t *engine, nxt_listen_socket_t *ls)
25{
26 uint8_t hint;
27 size_t size;
28 nxt_sockaddr_t *sa;
29
30 hint = (uint8_t) -1;
31 size = offsetof(nxt_sockaddr_t, u) + ls->socklen + ls->address_length;
32
33 sa = nxt_event_engine_mem_alloc(engine, &hint, size);
34
35 if (nxt_fast_path(sa != NULL)) {
36 /* Zero only beginning of structure up to sockaddr_un.sun_path[1]. */
37 nxt_memzero(sa, offsetof(nxt_sockaddr_t, u.sockaddr.sa_data[1]));
38
39 sa->cache_hint = hint;
40 sa->socklen = ls->socklen;
41 sa->length = ls->address_length;
42
43 sa->type = ls->sockaddr->type;
44 /*
45 * Set address family for unspecified Unix domain socket,
46 * because these sockaddr's are not updated by old BSD systems,
47 * see comment in nxt_conn_io_accept().
48 */
49 sa->u.sockaddr.sa_family = ls->sockaddr->u.sockaddr.sa_family;
50 }
51
52 return sa;
53}
54
55
56void
57nxt_sockaddr_cache_free(nxt_event_engine_t *engine, nxt_conn_t *c)
58{
59 nxt_event_engine_mem_free(engine, &c->remote->cache_hint, c->remote);
60}
61
62
63nxt_sockaddr_t *
64nxt_sockaddr_alloc(nxt_mp_t *mp, socklen_t socklen, size_t address_length)
65{
66 size_t size;
67 nxt_sockaddr_t *sa;
68
69 size = offsetof(nxt_sockaddr_t, u) + socklen + address_length;
70
71 /*
72 * The current struct sockaddr's define 32-bit fields at maximum
73 * and may define 64-bit AF_INET6 fields in the future. Alignment
74 * of memory allocated by nxt_mp_zalloc() is enough for these fields.
75 * If 128-bit alignment will be required then nxt_mem_malloc() and
76 * nxt_memzero() should be used instead.
77 */
78
79 sa = nxt_mp_zalloc(mp, size);
80
81 if (nxt_fast_path(sa != NULL)) {
82 sa->socklen = socklen;
83 sa->length = address_length;
84 }
85
86 return sa;
87}
88
89
90nxt_sockaddr_t *
91nxt_sockaddr_create(nxt_mp_t *mp, struct sockaddr *sockaddr, socklen_t length,
92 size_t address_length)
93{
94 size_t size, copy;
95 nxt_sockaddr_t *sa;
96
97 size = length;
98 copy = length;
99
100#if (NXT_HAVE_UNIX_DOMAIN)
101
102 /*
103 * Unspecified Unix domain sockaddr_un form and length are very
104 * platform depended (see comment in nxt_socket.h). Here they are
105 * normalized to the sockaddr_un with single zero byte sun_path[].
106 */
107
108 if (size <= offsetof(struct sockaddr_un, sun_path)) {
109 /*
110 * Small socket length means a short unspecified Unix domain
111 * socket address:
112 *
113 * getsockname() and getpeername() on OpenBSD prior to 5.3
114 * return zero length and does not update a passed sockaddr
115 * buffer at all.
116 *
117 * Linux returns length equal to 2, i.e. sockaddr_un without
118 * sun_path[], unix(7):
119 *
120 * unnamed: A stream socket that has not been bound
121 * to a pathname using bind(2) has no name. Likewise,
122 * the two sockets created by socketpair(2) are unnamed.
123 * When the address of an unnamed socket is returned by
124 * getsockname(2), getpeername(2), and accept(2), its
125 * length is sizeof(sa_family_t), and sun_path should
126 * not be inspected.
127 */
128 size = offsetof(struct sockaddr_un, sun_path) + 1;
129
130#if !(NXT_LINUX)
131
132 } else if (sockaddr->sa_family == AF_UNIX && sockaddr->sa_data[0] == '\0') {
133 /*
134 * Omit nonsignificant zeros of the unspecified Unix domain socket
135 * address. This test is disabled for Linux since Linux abstract
136 * socket address also starts with zero. However Linux unspecified
137 * Unix domain socket address is short and is handled above.
138 */
139 size = offsetof(struct sockaddr_un, sun_path) + 1;
140 copy = size;
141
142#endif
143 }
144
145#endif /* NXT_HAVE_UNIX_DOMAIN */
146
147 sa = nxt_sockaddr_alloc(mp, size, address_length);
148
149 if (nxt_fast_path(sa != NULL)) {
150 nxt_memcpy(&sa->u.sockaddr, sockaddr, copy);
151
152#if (NXT_HAVE_UNIX_DOMAIN && NXT_OPENBSD)
153
154 if (length == 0) {
155 sa->u.sockaddr.sa_family = AF_UNIX;
156 }
157
158#endif
159 }
160
161 return sa;
162}
163
164
165nxt_sockaddr_t *
166nxt_sockaddr_copy(nxt_mp_t *mp, nxt_sockaddr_t *src)
167{
168 size_t length;
169 nxt_sockaddr_t *dst;
170
171 length = offsetof(nxt_sockaddr_t, u) + src->socklen;
172
173 dst = nxt_mp_alloc(mp, length);
174
175 if (nxt_fast_path(dst != NULL)) {
176 nxt_memcpy(dst, src, length);
177 }
178
179 return dst;
180}
181
182
183nxt_sockaddr_t *
184nxt_getsockname(nxt_task_t *task, nxt_mp_t *mp, nxt_socket_t s)
185{
186 int ret;
187 size_t length;
188 socklen_t socklen;
189 nxt_sockaddr_buf_t sockaddr;
190
191 socklen = NXT_SOCKADDR_LEN;
192
193 ret = getsockname(s, &sockaddr.buf, &socklen);
194
195 if (nxt_fast_path(ret == 0)) {
196
197 switch (sockaddr.buf.sa_family) {
198#if (NXT_INET6)
199 case AF_INET6:
200 length = NXT_INET6_ADDR_STR_LEN;
201 break;
202#endif
203
204#if (NXT_HAVE_UNIX_DOMAIN)
205 case AF_UNIX:
206 length = sizeof("unix:") - 1 + socklen;
206 length = nxt_length("unix:") + socklen;
207#endif
208 break;
209
210 case AF_INET:
211 length = NXT_INET_ADDR_STR_LEN;
212 break;
213
214 default:
215 length = 0;
216 break;
217 }
218
219 return nxt_sockaddr_create(mp, &sockaddr.buf, socklen, length);
220 }
221
222 nxt_log(task, NXT_LOG_ERR, "getsockname(%d) failed %E", s, nxt_errno);
223
224 return NULL;
225}
226
227
228void
229nxt_sockaddr_text(nxt_sockaddr_t *sa)
230{
231 size_t offset;
232 u_char *p, *start, *end, *octet;
233 uint32_t port;
234
235 offset = offsetof(nxt_sockaddr_t, u) + sa->socklen;
236 sa->start = offset;
237 sa->port_start = offset;
238
239 start = nxt_pointer_to(sa, offset);
240 end = start + sa->length;
241
242 switch (sa->u.sockaddr.sa_family) {
243
244 case AF_INET:
245 sa->address_start = offset;
246
247 octet = (u_char *) &sa->u.sockaddr_in.sin_addr;
248
249 p = nxt_sprintf(start, end, "%ud.%ud.%ud.%ud",
250 octet[0], octet[1], octet[2], octet[3]);
251
252 sa->address_length = p - start;
253 sa->port_start += sa->address_length + 1;
254
255 port = sa->u.sockaddr_in.sin_port;
256
257 break;
258
259#if (NXT_INET6)
260
261 case AF_INET6:
262 sa->address_start = offset + 1;
263
264 p = start;
265 *p++ = '[';
266
267 p = nxt_inet6_ntop(sa->u.sockaddr_in6.sin6_addr.s6_addr, p, end);
268
269 sa->address_length = p - (start + 1);
270 sa->port_start += sa->address_length + 3;
271
272 *p++ = ']';
273
274 port = sa->u.sockaddr_in6.sin6_port;
275
276 break;
277
278#endif
279
280#if (NXT_HAVE_UNIX_DOMAIN)
281
282 case AF_UNIX:
283 sa->address_start = offset;
284
285 p = (u_char *) sa->u.sockaddr_un.sun_path;
286
287#if (NXT_LINUX)
288
289 if (p[0] == '\0') {
290 size_t length;
291
292 /* Linux abstract socket address has no trailing zero. */
293 length = sa->socklen - offsetof(struct sockaddr_un, sun_path);
294
295 p = nxt_sprintf(start, end, "unix:@%*s", length - 1, p + 1);
296
297 } else {
298 p = nxt_sprintf(start, end, "unix:%s", p);
299 }
300
301#else /* !(NXT_LINUX) */
302
303 p = nxt_sprintf(start, end, "unix:%s", p);
304
305#endif
306
307 sa->address_length = p - start;
308 sa->port_start += sa->address_length;
309 sa->length = p - start;
310
311 return;
312
313#endif /* NXT_HAVE_UNIX_DOMAIN */
314
315 default:
316 return;
317 }
318
319 p = nxt_sprintf(p, end, ":%d", ntohs(port));
320
321 sa->length = p - start;
322}
323
324
325uint32_t
326nxt_sockaddr_port_number(nxt_sockaddr_t *sa)
327{
328 uint32_t port;
329
330 switch (sa->u.sockaddr.sa_family) {
331
332#if (NXT_INET6)
333
334 case AF_INET6:
335 port = sa->u.sockaddr_in6.sin6_port;
336 break;
337
338#endif
339
340#if (NXT_HAVE_UNIX_DOMAIN)
341
342 case AF_UNIX:
343 return 0;
344
345#endif
346
347 default:
348 port = sa->u.sockaddr_in.sin_port;
349 break;
350 }
351
352 return ntohs((uint16_t) port);
353}
354
355
356nxt_bool_t
357nxt_sockaddr_cmp(nxt_sockaddr_t *sa1, nxt_sockaddr_t *sa2)
358{
359 if (sa1->socklen != sa2->socklen) {
360 return 0;
361 }
362
363 if (sa1->type != sa2->type) {
364 return 0;
365 }
366
367 if (sa1->u.sockaddr.sa_family != sa2->u.sockaddr.sa_family) {
368 return 0;
369 }
370
371 /*
372 * sockaddr struct's cannot be compared in whole since kernel
373 * may fill some fields in inherited sockaddr struct's.
374 */
375
376 switch (sa1->u.sockaddr.sa_family) {
377
378#if (NXT_INET6)
379
380 case AF_INET6:
381 if (sa1->u.sockaddr_in6.sin6_port != sa2->u.sockaddr_in6.sin6_port) {
382 return 0;
383 }
384
385 if (nxt_memcmp(&sa1->u.sockaddr_in6.sin6_addr,
386 &sa2->u.sockaddr_in6.sin6_addr, 16)
387 != 0)
388 {
389 return 0;
390 }
391
392 return 1;
393
394#endif
395
396#if (NXT_HAVE_UNIX_DOMAIN)
397
398 case AF_UNIX:
399 {
400 size_t length;
401
402 length = sa1->socklen - offsetof(struct sockaddr_un, sun_path);
403
404 if (nxt_memcmp(&sa1->u.sockaddr_un.sun_path,
405 &sa2->u.sockaddr_un.sun_path, length)
406 != 0)
407 {
408 return 0;
409 }
410
411 return 1;
412 }
413
414#endif
415
416 default: /* AF_INET */
417 if (sa1->u.sockaddr_in.sin_port != sa2->u.sockaddr_in.sin_port) {
418 return 0;
419 }
420
421 if (sa1->u.sockaddr_in.sin_addr.s_addr
422 != sa2->u.sockaddr_in.sin_addr.s_addr)
423 {
424 return 0;
425 }
426
427 return 1;
428 }
429}
430
431
432size_t
433nxt_sockaddr_ntop(nxt_sockaddr_t *sa, u_char *buf, u_char *end, nxt_bool_t port)
434{
435 u_char *p;
436
437 switch (sa->u.sockaddr.sa_family) {
438
439 case AF_INET:
440 p = (u_char *) &sa->u.sockaddr_in.sin_addr;
441
442 if (port) {
443 p = nxt_sprintf(buf, end, "%ud.%ud.%ud.%ud:%d",
444 p[0], p[1], p[2], p[3],
445 ntohs(sa->u.sockaddr_in.sin_port));
446 } else {
447 p = nxt_sprintf(buf, end, "%ud.%ud.%ud.%ud",
448 p[0], p[1], p[2], p[3]);
449 }
450
451 return p - buf;
452
453#if (NXT_INET6)
454
455 case AF_INET6:
456 p = buf;
457
458 if (port) {
459 *p++ = '[';
460 }
461
462 p = nxt_inet6_ntop(sa->u.sockaddr_in6.sin6_addr.s6_addr, p, end);
463
464 if (port) {
465 p = nxt_sprintf(p, end, "]:%d",
466 ntohs(sa->u.sockaddr_in6.sin6_port));
467 }
468
469 return p - buf;
470#endif
471
472#if (NXT_HAVE_UNIX_DOMAIN)
473
474 case AF_UNIX:
475
476#if (NXT_LINUX)
477
478 p = (u_char *) sa->u.sockaddr_un.sun_path;
479
480 if (p[0] == '\0') {
481 size_t length;
482
483 /* Linux abstract socket address has no trailing zero. */
484
485 length = sa->socklen - offsetof(struct sockaddr_un, sun_path) - 1;
486 p = nxt_sprintf(buf, end, "unix:\\0%*s", length, p + 1);
487
488 } else {
489 p = nxt_sprintf(buf, end, "unix:%s", p);
490 }
491
492#else /* !(NXT_LINUX) */
493
494 p = nxt_sprintf(buf, end, "unix:%s", sa->u.sockaddr_un.sun_path);
495
496#endif
497
498 return p - buf;
499
500#endif /* NXT_HAVE_UNIX_DOMAIN */
501
502 default:
503 return 0;
504 }
505}
506
507
508#if (NXT_INET6)
509
510static u_char *
511nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end)
512{
513 u_char *p;
514 size_t zero_groups, last_zero_groups, ipv6_bytes;
515 nxt_uint_t i, zero_start, last_zero_start;
516
517 const size_t max_inet6_length =
207#endif
208 break;
209
210 case AF_INET:
211 length = NXT_INET_ADDR_STR_LEN;
212 break;
213
214 default:
215 length = 0;
216 break;
217 }
218
219 return nxt_sockaddr_create(mp, &sockaddr.buf, socklen, length);
220 }
221
222 nxt_log(task, NXT_LOG_ERR, "getsockname(%d) failed %E", s, nxt_errno);
223
224 return NULL;
225}
226
227
228void
229nxt_sockaddr_text(nxt_sockaddr_t *sa)
230{
231 size_t offset;
232 u_char *p, *start, *end, *octet;
233 uint32_t port;
234
235 offset = offsetof(nxt_sockaddr_t, u) + sa->socklen;
236 sa->start = offset;
237 sa->port_start = offset;
238
239 start = nxt_pointer_to(sa, offset);
240 end = start + sa->length;
241
242 switch (sa->u.sockaddr.sa_family) {
243
244 case AF_INET:
245 sa->address_start = offset;
246
247 octet = (u_char *) &sa->u.sockaddr_in.sin_addr;
248
249 p = nxt_sprintf(start, end, "%ud.%ud.%ud.%ud",
250 octet[0], octet[1], octet[2], octet[3]);
251
252 sa->address_length = p - start;
253 sa->port_start += sa->address_length + 1;
254
255 port = sa->u.sockaddr_in.sin_port;
256
257 break;
258
259#if (NXT_INET6)
260
261 case AF_INET6:
262 sa->address_start = offset + 1;
263
264 p = start;
265 *p++ = '[';
266
267 p = nxt_inet6_ntop(sa->u.sockaddr_in6.sin6_addr.s6_addr, p, end);
268
269 sa->address_length = p - (start + 1);
270 sa->port_start += sa->address_length + 3;
271
272 *p++ = ']';
273
274 port = sa->u.sockaddr_in6.sin6_port;
275
276 break;
277
278#endif
279
280#if (NXT_HAVE_UNIX_DOMAIN)
281
282 case AF_UNIX:
283 sa->address_start = offset;
284
285 p = (u_char *) sa->u.sockaddr_un.sun_path;
286
287#if (NXT_LINUX)
288
289 if (p[0] == '\0') {
290 size_t length;
291
292 /* Linux abstract socket address has no trailing zero. */
293 length = sa->socklen - offsetof(struct sockaddr_un, sun_path);
294
295 p = nxt_sprintf(start, end, "unix:@%*s", length - 1, p + 1);
296
297 } else {
298 p = nxt_sprintf(start, end, "unix:%s", p);
299 }
300
301#else /* !(NXT_LINUX) */
302
303 p = nxt_sprintf(start, end, "unix:%s", p);
304
305#endif
306
307 sa->address_length = p - start;
308 sa->port_start += sa->address_length;
309 sa->length = p - start;
310
311 return;
312
313#endif /* NXT_HAVE_UNIX_DOMAIN */
314
315 default:
316 return;
317 }
318
319 p = nxt_sprintf(p, end, ":%d", ntohs(port));
320
321 sa->length = p - start;
322}
323
324
325uint32_t
326nxt_sockaddr_port_number(nxt_sockaddr_t *sa)
327{
328 uint32_t port;
329
330 switch (sa->u.sockaddr.sa_family) {
331
332#if (NXT_INET6)
333
334 case AF_INET6:
335 port = sa->u.sockaddr_in6.sin6_port;
336 break;
337
338#endif
339
340#if (NXT_HAVE_UNIX_DOMAIN)
341
342 case AF_UNIX:
343 return 0;
344
345#endif
346
347 default:
348 port = sa->u.sockaddr_in.sin_port;
349 break;
350 }
351
352 return ntohs((uint16_t) port);
353}
354
355
356nxt_bool_t
357nxt_sockaddr_cmp(nxt_sockaddr_t *sa1, nxt_sockaddr_t *sa2)
358{
359 if (sa1->socklen != sa2->socklen) {
360 return 0;
361 }
362
363 if (sa1->type != sa2->type) {
364 return 0;
365 }
366
367 if (sa1->u.sockaddr.sa_family != sa2->u.sockaddr.sa_family) {
368 return 0;
369 }
370
371 /*
372 * sockaddr struct's cannot be compared in whole since kernel
373 * may fill some fields in inherited sockaddr struct's.
374 */
375
376 switch (sa1->u.sockaddr.sa_family) {
377
378#if (NXT_INET6)
379
380 case AF_INET6:
381 if (sa1->u.sockaddr_in6.sin6_port != sa2->u.sockaddr_in6.sin6_port) {
382 return 0;
383 }
384
385 if (nxt_memcmp(&sa1->u.sockaddr_in6.sin6_addr,
386 &sa2->u.sockaddr_in6.sin6_addr, 16)
387 != 0)
388 {
389 return 0;
390 }
391
392 return 1;
393
394#endif
395
396#if (NXT_HAVE_UNIX_DOMAIN)
397
398 case AF_UNIX:
399 {
400 size_t length;
401
402 length = sa1->socklen - offsetof(struct sockaddr_un, sun_path);
403
404 if (nxt_memcmp(&sa1->u.sockaddr_un.sun_path,
405 &sa2->u.sockaddr_un.sun_path, length)
406 != 0)
407 {
408 return 0;
409 }
410
411 return 1;
412 }
413
414#endif
415
416 default: /* AF_INET */
417 if (sa1->u.sockaddr_in.sin_port != sa2->u.sockaddr_in.sin_port) {
418 return 0;
419 }
420
421 if (sa1->u.sockaddr_in.sin_addr.s_addr
422 != sa2->u.sockaddr_in.sin_addr.s_addr)
423 {
424 return 0;
425 }
426
427 return 1;
428 }
429}
430
431
432size_t
433nxt_sockaddr_ntop(nxt_sockaddr_t *sa, u_char *buf, u_char *end, nxt_bool_t port)
434{
435 u_char *p;
436
437 switch (sa->u.sockaddr.sa_family) {
438
439 case AF_INET:
440 p = (u_char *) &sa->u.sockaddr_in.sin_addr;
441
442 if (port) {
443 p = nxt_sprintf(buf, end, "%ud.%ud.%ud.%ud:%d",
444 p[0], p[1], p[2], p[3],
445 ntohs(sa->u.sockaddr_in.sin_port));
446 } else {
447 p = nxt_sprintf(buf, end, "%ud.%ud.%ud.%ud",
448 p[0], p[1], p[2], p[3]);
449 }
450
451 return p - buf;
452
453#if (NXT_INET6)
454
455 case AF_INET6:
456 p = buf;
457
458 if (port) {
459 *p++ = '[';
460 }
461
462 p = nxt_inet6_ntop(sa->u.sockaddr_in6.sin6_addr.s6_addr, p, end);
463
464 if (port) {
465 p = nxt_sprintf(p, end, "]:%d",
466 ntohs(sa->u.sockaddr_in6.sin6_port));
467 }
468
469 return p - buf;
470#endif
471
472#if (NXT_HAVE_UNIX_DOMAIN)
473
474 case AF_UNIX:
475
476#if (NXT_LINUX)
477
478 p = (u_char *) sa->u.sockaddr_un.sun_path;
479
480 if (p[0] == '\0') {
481 size_t length;
482
483 /* Linux abstract socket address has no trailing zero. */
484
485 length = sa->socklen - offsetof(struct sockaddr_un, sun_path) - 1;
486 p = nxt_sprintf(buf, end, "unix:\\0%*s", length, p + 1);
487
488 } else {
489 p = nxt_sprintf(buf, end, "unix:%s", p);
490 }
491
492#else /* !(NXT_LINUX) */
493
494 p = nxt_sprintf(buf, end, "unix:%s", sa->u.sockaddr_un.sun_path);
495
496#endif
497
498 return p - buf;
499
500#endif /* NXT_HAVE_UNIX_DOMAIN */
501
502 default:
503 return 0;
504 }
505}
506
507
508#if (NXT_INET6)
509
510static u_char *
511nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end)
512{
513 u_char *p;
514 size_t zero_groups, last_zero_groups, ipv6_bytes;
515 nxt_uint_t i, zero_start, last_zero_start;
516
517 const size_t max_inet6_length =
518 sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") - 1;
518 nxt_length("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
519
520 if (buf + max_inet6_length > end) {
521 return buf;
522 }
523
524 zero_start = 8;
525 zero_groups = 0;
526 last_zero_start = 8;
527 last_zero_groups = 0;
528
529 for (i = 0; i < 16; i += 2) {
530
531 if (addr[i] == 0 && addr[i + 1] == 0) {
532
533 if (last_zero_groups == 0) {
534 last_zero_start = i;
535 }
536
537 last_zero_groups++;
538
539 } else {
540 if (zero_groups < last_zero_groups) {
541 zero_groups = last_zero_groups;
542 zero_start = last_zero_start;
543 }
544
545 last_zero_groups = 0;
546 }
547 }
548
549 if (zero_groups < last_zero_groups) {
550 zero_groups = last_zero_groups;
551 zero_start = last_zero_start;
552 }
553
554 ipv6_bytes = 16;
555 p = buf;
556
557 if (zero_start == 0) {
558
559 /* IPv4-mapped address */
560 if ((zero_groups == 5 && addr[10] == 0xFF && addr[11] == 0xFF)
561 /* IPv4-compatible address */
562 || (zero_groups == 6)
563 /* not IPv6 loopback address */
564 || (zero_groups == 7 && addr[14] != 0 && addr[15] != 1))
565 {
566 ipv6_bytes = 12;
567 }
568
569 *p++ = ':';
570 }
571
572 for (i = 0; i < ipv6_bytes; i += 2) {
573
574 if (i == zero_start) {
575 /* Output maximum number of consecutive zero groups as "::". */
576 i += (zero_groups - 1) * 2;
577 *p++ = ':';
578 continue;
579 }
580
581 p = nxt_sprintf(p, end, "%uxd", (addr[i] << 8) + addr[i + 1]);
582
583 if (i < 14) {
584 *p++ = ':';
585 }
586 }
587
588 if (ipv6_bytes == 12) {
589 p = nxt_sprintf(p, end, "%ud.%ud.%ud.%ud",
590 addr[12], addr[13], addr[14], addr[15]);
591 }
592
593 return p;
594}
595
596#endif
597
598
599nxt_sockaddr_t *
600nxt_sockaddr_parse(nxt_mp_t *mp, nxt_str_t *addr)
601{
602 nxt_sockaddr_t *sa;
603
604 if (addr->length > 6 && nxt_memcmp(addr->start, "unix:", 5) == 0) {
605 sa = nxt_sockaddr_unix_parse(mp, addr);
606
607 } else if (addr->length != 0 && addr->start[0] == '[') {
608 sa = nxt_sockaddr_inet6_parse(mp, addr);
609
610 } else {
611 sa = nxt_sockaddr_inet_parse(mp, addr);
612 }
613
614 if (nxt_fast_path(sa != NULL)) {
615 nxt_sockaddr_text(sa);
616 }
617
618 return sa;
619}
620
621
622static nxt_sockaddr_t *
623nxt_sockaddr_unix_parse(nxt_mp_t *mp, nxt_str_t *addr)
624{
625#if (NXT_HAVE_UNIX_DOMAIN)
626 size_t length, socklen;
627 u_char *path;
628 nxt_sockaddr_t *sa;
629
630 /*
631 * Actual sockaddr_un length can be lesser or even larger than defined
632 * struct sockaddr_un length (see comment in nxt_socket.h). So
633 * limit maximum Unix domain socket address length by defined sun_path[]
634 * length because some OSes accept addresses twice larger than defined
635 * struct sockaddr_un. Also reserve space for a trailing zero to avoid
636 * ambiguity, since many OSes accept Unix domain socket addresses
637 * without a trailing zero.
638 */
639 const size_t max_len = sizeof(struct sockaddr_un)
640 - offsetof(struct sockaddr_un, sun_path) - 1;
641
642 /* Cutting "unix:". */
643 length = addr->length - 5;
644 path = addr->start + 5;
645
646 if (length > max_len) {
647 nxt_thread_log_error(NXT_LOG_ERR,
648 "unix domain socket \"%V\" name is too long",
649 addr);
650 return NULL;
651 }
652
653 socklen = offsetof(struct sockaddr_un, sun_path) + length + 1;
654
655#if (NXT_LINUX)
656
657 /*
658 * Linux unix(7):
659 *
660 * abstract: an abstract socket address is distinguished by the fact
661 * that sun_path[0] is a null byte ('\0'). The socket's address in
662 * this namespace is given by the additional bytes in sun_path that
663 * are covered by the specified length of the address structure.
664 * (Null bytes in the name have no special significance.)
665 */
666 if (path[0] == '@') {
667 path[0] = '\0';
668 socklen--;
669 }
670
671#endif
672
673 sa = nxt_sockaddr_alloc(mp, socklen, addr->length);
674
675 if (nxt_fast_path(sa != NULL)) {
676 sa->u.sockaddr_un.sun_family = AF_UNIX;
677 nxt_memcpy(sa->u.sockaddr_un.sun_path, path, length);
678 }
679
680 return sa;
681
682#else /* !(NXT_HAVE_UNIX_DOMAIN) */
683
684 nxt_thread_log_error(NXT_LOG_ERR,
685 "unix domain socket \"%V\" is not supported", addr);
686
687 return NULL;
688
689#endif
690}
691
692
693static nxt_sockaddr_t *
694nxt_sockaddr_inet6_parse(nxt_mp_t *mp, nxt_str_t *addr)
695{
696#if (NXT_INET6)
697 u_char *p, *start, *end;
698 size_t length;
699 nxt_int_t ret, port;
700 nxt_sockaddr_t *sa;
701
702 length = addr->length - 1;
703 start = addr->start + 1;
704
705 end = nxt_memchr(start, ']', length);
706
707 if (end != NULL) {
708 sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6),
709 NXT_INET6_ADDR_STR_LEN);
710 if (nxt_slow_path(sa == NULL)) {
711 return NULL;
712 }
713
714 ret = nxt_inet6_addr(&sa->u.sockaddr_in6.sin6_addr, start, end - start);
715
716 if (nxt_fast_path(ret == NXT_OK)) {
717 p = end + 1;
718 length = (start + length) - p;
719
720 if (length > 2 && *p == ':') {
721 port = nxt_int_parse(p + 1, length - 1);
722
723 if (port > 0 && port < 65536) {
724 sa->u.sockaddr_in6.sin6_port = htons((in_port_t) port);
725 sa->u.sockaddr_in6.sin6_family = AF_INET6;
726
727 return sa;
728 }
729 }
730
731 nxt_thread_log_error(NXT_LOG_ERR, "invalid port in \"%V\"", addr);
732
733 return NULL;
734 }
735 }
736
737 nxt_thread_log_error(NXT_LOG_ERR, "invalid IPv6 address in \"%V\"", addr);
738
739 return NULL;
740
741#else /* !(NXT_INET6) */
742
743 nxt_thread_log_error(NXT_LOG_ERR, "IPv6 socket \"%V\" is not supported",
744 addr);
745 return NULL;
746
747#endif
748}
749
750
751static nxt_sockaddr_t *
752nxt_sockaddr_inet_parse(nxt_mp_t *mp, nxt_str_t *addr)
753{
754 u_char *p;
755 size_t length;
756 nxt_int_t port;
757 in_addr_t inaddr;
758 nxt_sockaddr_t *sa;
759
760 p = nxt_memchr(addr->start, ':', addr->length);
761
762 if (nxt_fast_path(p != NULL)) {
763 inaddr = INADDR_ANY;
764 length = p - addr->start;
765
766 if (length != 1 || addr->start[0] != '*') {
767 inaddr = nxt_inet_addr(addr->start, length);
768
769 if (nxt_slow_path(inaddr == INADDR_NONE)) {
770 nxt_thread_log_error(NXT_LOG_ERR, "invalid address \"%V\"",
771 addr);
772 return NULL;
773 }
774 }
775
776 p++;
777 length = (addr->start + addr->length) - p;
778 port = nxt_int_parse(p, length);
779
780 if (port > 0 && port < 65536) {
781 sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in),
782 NXT_INET_ADDR_STR_LEN);
783
784 if (nxt_slow_path(sa != NULL)) {
785 sa->u.sockaddr_in.sin_family = AF_INET;
786 sa->u.sockaddr_in.sin_port = htons((in_port_t) port);
787 sa->u.sockaddr_in.sin_addr.s_addr = inaddr;
788 }
789
790 return sa;
791 }
792 }
793
794 nxt_thread_log_error(NXT_LOG_ERR, "invalid port in \"%V\"", addr);
795
796 return NULL;
797}
798
799
800void
801nxt_job_sockaddr_parse(nxt_job_sockaddr_parse_t *jbs)
802{
803 u_char *p;
804 size_t length;
805 nxt_int_t ret;
806 nxt_work_handler_t handler;
807
808 nxt_job_set_name(&jbs->resolve.job, "job sockaddr parse");
809
810 length = jbs->addr.length;
811 p = jbs->addr.start;
812
813 if (length > 6 && nxt_memcmp(p, "unix:", 5) == 0) {
814 ret = nxt_job_sockaddr_unix_parse(jbs);
815
816 } else if (length != 0 && *p == '[') {
817 ret = nxt_job_sockaddr_inet6_parse(jbs);
818
819 } else {
820 ret = nxt_job_sockaddr_inet_parse(jbs);
821 }
822
823 switch (ret) {
824
825 case NXT_OK:
826 handler = jbs->resolve.ready_handler;
827 break;
828
829 case NXT_ERROR:
830 handler = jbs->resolve.error_handler;
831 break;
832
833 default: /* NXT_AGAIN */
834 return;
835 }
836
837 nxt_job_return(jbs->resolve.job.task, &jbs->resolve.job, handler);
838}
839
840
841static nxt_int_t
842nxt_job_sockaddr_unix_parse(nxt_job_sockaddr_parse_t *jbs)
843{
844#if (NXT_HAVE_UNIX_DOMAIN)
845 size_t length, socklen;
846 u_char *path;
847 nxt_mp_t *mp;
848 nxt_sockaddr_t *sa;
849
850 /*
851 * Actual sockaddr_un length can be lesser or even larger than defined
852 * struct sockaddr_un length (see comment in nxt_socket.h). So
853 * limit maximum Unix domain socket address length by defined sun_path[]
854 * length because some OSes accept addresses twice larger than defined
855 * struct sockaddr_un. Also reserve space for a trailing zero to avoid
856 * ambiguity, since many OSes accept Unix domain socket addresses
857 * without a trailing zero.
858 */
859 const size_t max_len = sizeof(struct sockaddr_un)
860 - offsetof(struct sockaddr_un, sun_path) - 1;
861
862 /* cutting "unix:" */
863 length = jbs->addr.length - 5;
864 path = jbs->addr.start + 5;
865
866 if (length > max_len) {
867 nxt_thread_log_error(jbs->resolve.log_level,
868 "unix domain socket \"%V\" name is too long",
869 &jbs->addr);
870 return NXT_ERROR;
871 }
872
873 socklen = offsetof(struct sockaddr_un, sun_path) + length + 1;
874
875#if (NXT_LINUX)
876
877 /*
878 * Linux unix(7):
879 *
880 * abstract: an abstract socket address is distinguished by the fact
881 * that sun_path[0] is a null byte ('\0'). The socket's address in
882 * this namespace is given by the additional bytes in sun_path that
883 * are covered by the specified length of the address structure.
884 * (Null bytes in the name have no special significance.)
885 */
886 if (path[0] == '\0') {
887 socklen--;
888 }
889
890#endif
891
892 mp = jbs->resolve.job.mem_pool;
893
894 jbs->resolve.sockaddrs = nxt_mp_alloc(mp, sizeof(void *));
895
896 if (nxt_fast_path(jbs->resolve.sockaddrs != NULL)) {
897 sa = nxt_sockaddr_alloc(mp, socklen, jbs->addr.length);
898
899 if (nxt_fast_path(sa != NULL)) {
900 jbs->resolve.count = 1;
901 jbs->resolve.sockaddrs[0] = sa;
902
903 sa->u.sockaddr_un.sun_family = AF_UNIX;
904 nxt_memcpy(sa->u.sockaddr_un.sun_path, path, length);
905
906 return NXT_OK;
907 }
908 }
909
910 return NXT_ERROR;
911
912#else /* !(NXT_HAVE_UNIX_DOMAIN) */
913
914 nxt_thread_log_error(jbs->resolve.log_level,
915 "unix domain socket \"%V\" is not supported",
916 &jbs->addr);
917 return NXT_ERROR;
918
919#endif
920}
921
922
923static nxt_int_t
924nxt_job_sockaddr_inet6_parse(nxt_job_sockaddr_parse_t *jbs)
925{
926#if (NXT_INET6)
927 u_char *p, *addr, *addr_end;
928 size_t length;
929 nxt_mp_t *mp;
930 nxt_int_t port;
931 nxt_sockaddr_t *sa;
932 struct in6_addr *in6_addr;
933
934 length = jbs->addr.length - 1;
935 addr = jbs->addr.start + 1;
936
937 addr_end = nxt_memchr(addr, ']', length);
938
939 if (addr_end == NULL) {
940 goto invalid_address;
941 }
942
943 mp = jbs->resolve.job.mem_pool;
944
945 jbs->resolve.sockaddrs = nxt_mp_alloc(mp, sizeof(void *));
946
947 if (nxt_slow_path(jbs->resolve.sockaddrs == NULL)) {
948 return NXT_ERROR;
949 }
950
951 sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6),
952 NXT_INET6_ADDR_STR_LEN);
953
954 if (nxt_slow_path(sa == NULL)) {
955 return NXT_ERROR;
956 }
957
958 jbs->resolve.count = 1;
959 jbs->resolve.sockaddrs[0] = sa;
960
961 in6_addr = &sa->u.sockaddr_in6.sin6_addr;
962
963 if (nxt_inet6_addr(in6_addr, addr, addr_end - addr) != NXT_OK) {
964 goto invalid_address;
965 }
966
967 p = addr_end + 1;
968 length = (addr + length) - p;
969
970 if (length == 0) {
971 jbs->no_port = 1;
972 port = jbs->resolve.port;
973 goto found;
974 }
975
976 if (*p == ':') {
977 port = nxt_int_parse(p + 1, length - 1);
978
979 if (port >= 1 && port <= 65535) {
980 port = htons((in_port_t) port);
981 goto found;
982 }
983 }
984
985 nxt_thread_log_error(jbs->resolve.log_level,
986 "invalid port in \"%V\"", &jbs->addr);
987
988 return NXT_ERROR;
989
990found:
991
992 sa->u.sockaddr_in6.sin6_family = AF_INET6;
993 sa->u.sockaddr_in6.sin6_port = (in_port_t) port;
994
995 if (IN6_IS_ADDR_UNSPECIFIED(in6_addr)) {
996 jbs->wildcard = 1;
997 }
998
999 return NXT_OK;
1000
1001invalid_address:
1002
1003 nxt_thread_log_error(jbs->resolve.log_level,
1004 "invalid IPv6 address in \"%V\"", &jbs->addr);
1005 return NXT_ERROR;
1006
1007#else
1008
1009 nxt_thread_log_error(jbs->resolve.log_level,
1010 "IPv6 socket \"%V\" is not supported", &jbs->addr);
1011 return NXT_ERROR;
1012
1013#endif
1014}
1015
1016
1017static nxt_int_t
1018nxt_job_sockaddr_inet_parse(nxt_job_sockaddr_parse_t *jbs)
1019{
1020 u_char *p, *host;
1021 size_t length;
1022 nxt_mp_t *mp;
1023 nxt_int_t port;
1024 in_addr_t addr;
1025 nxt_sockaddr_t *sa;
1026
1027 addr = INADDR_ANY;
1028
1029 length = jbs->addr.length;
1030 host = jbs->addr.start;
1031
1032 p = nxt_memchr(host, ':', length);
1033
1034 if (p == NULL) {
1035
1036 /* single value port, address, or host name */
1037
1038 port = nxt_int_parse(host, length);
1039
1040 if (port > 0) {
1041 if (port < 1 || port > 65535) {
1042 goto invalid_port;
1043 }
1044
1045 /* "*:XX" */
1046 port = htons((in_port_t) port);
1047 jbs->resolve.port = (in_port_t) port;
1048
1049 } else {
1050 jbs->no_port = 1;
1051
1052 addr = nxt_inet_addr(host, length);
1053
1054 if (addr == INADDR_NONE) {
1055 jbs->resolve.name.length = length;
1056 jbs->resolve.name.start = host;
1057
1058 nxt_job_resolve(&jbs->resolve);
1059 return NXT_AGAIN;
1060 }
1061
1062 /* "x.x.x.x" */
1063 port = jbs->resolve.port;
1064 }
1065
1066 } else {
1067
1068 /* x.x.x.x:XX or host:XX */
1069
1070 p++;
1071 length = (host + length) - p;
1072 port = nxt_int_parse(p, length);
1073
1074 if (port < 1 || port > 65535) {
1075 goto invalid_port;
1076 }
1077
1078 port = htons((in_port_t) port);
1079
1080 length = (p - 1) - host;
1081
1082 if (length != 1 || host[0] != '*') {
1083 addr = nxt_inet_addr(host, length);
1084
1085 if (addr == INADDR_NONE) {
1086 jbs->resolve.name.length = length;
1087 jbs->resolve.name.start = host;
1088 jbs->resolve.port = (in_port_t) port;
1089
1090 nxt_job_resolve(&jbs->resolve);
1091 return NXT_AGAIN;
1092 }
1093
1094 /* "x.x.x.x:XX" */
1095 }
1096 }
1097
1098 mp = jbs->resolve.job.mem_pool;
1099
1100 jbs->resolve.sockaddrs = nxt_mp_alloc(mp, sizeof(void *));
1101 if (nxt_slow_path(jbs->resolve.sockaddrs == NULL)) {
1102 return NXT_ERROR;
1103 }
1104
1105 sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in),
1106 NXT_INET_ADDR_STR_LEN);
1107
1108 if (nxt_fast_path(sa != NULL)) {
1109 jbs->resolve.count = 1;
1110 jbs->resolve.sockaddrs[0] = sa;
1111
1112 jbs->wildcard = (addr == INADDR_ANY);
1113
1114 sa->u.sockaddr_in.sin_family = AF_INET;
1115 sa->u.sockaddr_in.sin_port = (in_port_t) port;
1116 sa->u.sockaddr_in.sin_addr.s_addr = addr;
1117
1118 return NXT_OK;
1119 }
1120
1121 return NXT_ERROR;
1122
1123invalid_port:
1124
1125 nxt_thread_log_error(jbs->resolve.log_level,
1126 "invalid port in \"%V\"", &jbs->addr);
1127
1128 return NXT_ERROR;
1129}
1130
1131
1132in_addr_t
1133nxt_inet_addr(u_char *buf, size_t length)
1134{
1135 u_char c, *end;
1136 in_addr_t addr;
1137 nxt_uint_t digit, octet, dots;
1138
1139 addr = 0;
1140 octet = 0;
1141 dots = 0;
1142
1143 end = buf + length;
1144
1145 while (buf < end) {
1146
1147 c = *buf++;
1148
1149 digit = c - '0';
1150 /* values below '0' become large unsigned integers */
1151
1152 if (digit < 10) {
1153 octet = octet * 10 + digit;
1154 continue;
1155 }
1156
1157 if (c == '.' && octet < 256) {
1158 addr = (addr << 8) + octet;
1159 octet = 0;
1160 dots++;
1161 continue;
1162 }
1163
1164 return INADDR_NONE;
1165 }
1166
1167 if (dots == 3 && octet < 256) {
1168 addr = (addr << 8) + octet;
1169 return htonl(addr);
1170 }
1171
1172 return INADDR_NONE;
1173}
1174
1175
1176#if (NXT_INET6)
1177
1178nxt_int_t
1179nxt_inet6_addr(struct in6_addr *in6_addr, u_char *buf, size_t length)
1180{
1181 u_char c, *addr, *zero_start, *ipv4, *dst, *src, *end;
1182 nxt_uint_t digit, group, nibbles, groups_left;
1183
1184 if (length == 0) {
1185 return NXT_ERROR;
1186 }
1187
1188 end = buf + length;
1189
1190 if (buf[0] == ':') {
1191 buf++;
1192 }
1193
1194 addr = in6_addr->s6_addr;
1195 zero_start = NULL;
1196 groups_left = 8;
1197 nibbles = 0;
1198 group = 0;
1199 ipv4 = NULL;
1200
1201 while (buf < end) {
1202 c = *buf++;
1203
1204 if (c == ':') {
1205 if (nibbles != 0) {
1206 ipv4 = buf;
1207
1208 *addr++ = (u_char) (group >> 8);
1209 *addr++ = (u_char) (group & 0xFF);
1210 groups_left--;
1211
1212 if (groups_left != 0) {
1213 nibbles = 0;
1214 group = 0;
1215 continue;
1216 }
1217
1218 } else {
1219 if (zero_start == NULL) {
1220 ipv4 = buf;
1221 zero_start = addr;
1222 continue;
1223 }
1224 }
1225
1226 return NXT_ERROR;
1227 }
1228
1229 if (c == '.' && nibbles != 0) {
1230
1231 if (groups_left < 2 || ipv4 == NULL) {
1232 return NXT_ERROR;
1233 }
1234
1235 group = nxt_inet_addr(ipv4, end - ipv4);
1236 if (group == INADDR_NONE) {
1237 return NXT_ERROR;
1238 }
1239
1240 group = ntohl(group);
1241
1242 *addr++ = (u_char) ((group >> 24) & 0xFF);
1243 *addr++ = (u_char) ((group >> 16) & 0xFF);
1244 groups_left--;
1245
1246 /* the low 16-bit are copied below */
1247 break;
1248 }
1249
1250 nibbles++;
1251
1252 if (nibbles > 4) {
1253 return NXT_ERROR;
1254 }
1255
1256 group <<= 4;
1257
1258 digit = c - '0';
1259 /* values below '0' become large unsigned integers */
1260
1261 if (digit < 10) {
1262 group += digit;
1263 continue;
1264 }
1265
1266 c |= 0x20;
1267 digit = c - 'a';
1268 /* values below 'a' become large unsigned integers */
1269
1270 if (digit < 6) {
1271 group += 10 + digit;
1272 continue;
1273 }
1274
1275 return NXT_ERROR;
1276 }
1277
1278 if (nibbles == 0 && zero_start == NULL) {
1279 return NXT_ERROR;
1280 }
1281
1282 *addr++ = (u_char) (group >> 8);
1283 *addr++ = (u_char) (group & 0xFF);
1284 groups_left--;
1285
1286 if (groups_left != 0) {
1287
1288 if (zero_start != NULL) {
1289
1290 /* moving part before consecutive zero groups to the end */
1291
1292 groups_left *= 2;
1293 src = addr - 1;
1294 dst = src + groups_left;
1295
1296 while (src >= zero_start) {
1297 *dst-- = *src--;
1298 }
1299
1300 nxt_memzero(zero_start, groups_left);
1301
1302 return NXT_OK;
1303 }
1304
1305 } else {
1306 if (zero_start == NULL) {
1307 return NXT_OK;
1308 }
1309 }
1310
1311 return NXT_ERROR;
1312}
1313
1314#endif
519
520 if (buf + max_inet6_length > end) {
521 return buf;
522 }
523
524 zero_start = 8;
525 zero_groups = 0;
526 last_zero_start = 8;
527 last_zero_groups = 0;
528
529 for (i = 0; i < 16; i += 2) {
530
531 if (addr[i] == 0 && addr[i + 1] == 0) {
532
533 if (last_zero_groups == 0) {
534 last_zero_start = i;
535 }
536
537 last_zero_groups++;
538
539 } else {
540 if (zero_groups < last_zero_groups) {
541 zero_groups = last_zero_groups;
542 zero_start = last_zero_start;
543 }
544
545 last_zero_groups = 0;
546 }
547 }
548
549 if (zero_groups < last_zero_groups) {
550 zero_groups = last_zero_groups;
551 zero_start = last_zero_start;
552 }
553
554 ipv6_bytes = 16;
555 p = buf;
556
557 if (zero_start == 0) {
558
559 /* IPv4-mapped address */
560 if ((zero_groups == 5 && addr[10] == 0xFF && addr[11] == 0xFF)
561 /* IPv4-compatible address */
562 || (zero_groups == 6)
563 /* not IPv6 loopback address */
564 || (zero_groups == 7 && addr[14] != 0 && addr[15] != 1))
565 {
566 ipv6_bytes = 12;
567 }
568
569 *p++ = ':';
570 }
571
572 for (i = 0; i < ipv6_bytes; i += 2) {
573
574 if (i == zero_start) {
575 /* Output maximum number of consecutive zero groups as "::". */
576 i += (zero_groups - 1) * 2;
577 *p++ = ':';
578 continue;
579 }
580
581 p = nxt_sprintf(p, end, "%uxd", (addr[i] << 8) + addr[i + 1]);
582
583 if (i < 14) {
584 *p++ = ':';
585 }
586 }
587
588 if (ipv6_bytes == 12) {
589 p = nxt_sprintf(p, end, "%ud.%ud.%ud.%ud",
590 addr[12], addr[13], addr[14], addr[15]);
591 }
592
593 return p;
594}
595
596#endif
597
598
599nxt_sockaddr_t *
600nxt_sockaddr_parse(nxt_mp_t *mp, nxt_str_t *addr)
601{
602 nxt_sockaddr_t *sa;
603
604 if (addr->length > 6 && nxt_memcmp(addr->start, "unix:", 5) == 0) {
605 sa = nxt_sockaddr_unix_parse(mp, addr);
606
607 } else if (addr->length != 0 && addr->start[0] == '[') {
608 sa = nxt_sockaddr_inet6_parse(mp, addr);
609
610 } else {
611 sa = nxt_sockaddr_inet_parse(mp, addr);
612 }
613
614 if (nxt_fast_path(sa != NULL)) {
615 nxt_sockaddr_text(sa);
616 }
617
618 return sa;
619}
620
621
622static nxt_sockaddr_t *
623nxt_sockaddr_unix_parse(nxt_mp_t *mp, nxt_str_t *addr)
624{
625#if (NXT_HAVE_UNIX_DOMAIN)
626 size_t length, socklen;
627 u_char *path;
628 nxt_sockaddr_t *sa;
629
630 /*
631 * Actual sockaddr_un length can be lesser or even larger than defined
632 * struct sockaddr_un length (see comment in nxt_socket.h). So
633 * limit maximum Unix domain socket address length by defined sun_path[]
634 * length because some OSes accept addresses twice larger than defined
635 * struct sockaddr_un. Also reserve space for a trailing zero to avoid
636 * ambiguity, since many OSes accept Unix domain socket addresses
637 * without a trailing zero.
638 */
639 const size_t max_len = sizeof(struct sockaddr_un)
640 - offsetof(struct sockaddr_un, sun_path) - 1;
641
642 /* Cutting "unix:". */
643 length = addr->length - 5;
644 path = addr->start + 5;
645
646 if (length > max_len) {
647 nxt_thread_log_error(NXT_LOG_ERR,
648 "unix domain socket \"%V\" name is too long",
649 addr);
650 return NULL;
651 }
652
653 socklen = offsetof(struct sockaddr_un, sun_path) + length + 1;
654
655#if (NXT_LINUX)
656
657 /*
658 * Linux unix(7):
659 *
660 * abstract: an abstract socket address is distinguished by the fact
661 * that sun_path[0] is a null byte ('\0'). The socket's address in
662 * this namespace is given by the additional bytes in sun_path that
663 * are covered by the specified length of the address structure.
664 * (Null bytes in the name have no special significance.)
665 */
666 if (path[0] == '@') {
667 path[0] = '\0';
668 socklen--;
669 }
670
671#endif
672
673 sa = nxt_sockaddr_alloc(mp, socklen, addr->length);
674
675 if (nxt_fast_path(sa != NULL)) {
676 sa->u.sockaddr_un.sun_family = AF_UNIX;
677 nxt_memcpy(sa->u.sockaddr_un.sun_path, path, length);
678 }
679
680 return sa;
681
682#else /* !(NXT_HAVE_UNIX_DOMAIN) */
683
684 nxt_thread_log_error(NXT_LOG_ERR,
685 "unix domain socket \"%V\" is not supported", addr);
686
687 return NULL;
688
689#endif
690}
691
692
693static nxt_sockaddr_t *
694nxt_sockaddr_inet6_parse(nxt_mp_t *mp, nxt_str_t *addr)
695{
696#if (NXT_INET6)
697 u_char *p, *start, *end;
698 size_t length;
699 nxt_int_t ret, port;
700 nxt_sockaddr_t *sa;
701
702 length = addr->length - 1;
703 start = addr->start + 1;
704
705 end = nxt_memchr(start, ']', length);
706
707 if (end != NULL) {
708 sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6),
709 NXT_INET6_ADDR_STR_LEN);
710 if (nxt_slow_path(sa == NULL)) {
711 return NULL;
712 }
713
714 ret = nxt_inet6_addr(&sa->u.sockaddr_in6.sin6_addr, start, end - start);
715
716 if (nxt_fast_path(ret == NXT_OK)) {
717 p = end + 1;
718 length = (start + length) - p;
719
720 if (length > 2 && *p == ':') {
721 port = nxt_int_parse(p + 1, length - 1);
722
723 if (port > 0 && port < 65536) {
724 sa->u.sockaddr_in6.sin6_port = htons((in_port_t) port);
725 sa->u.sockaddr_in6.sin6_family = AF_INET6;
726
727 return sa;
728 }
729 }
730
731 nxt_thread_log_error(NXT_LOG_ERR, "invalid port in \"%V\"", addr);
732
733 return NULL;
734 }
735 }
736
737 nxt_thread_log_error(NXT_LOG_ERR, "invalid IPv6 address in \"%V\"", addr);
738
739 return NULL;
740
741#else /* !(NXT_INET6) */
742
743 nxt_thread_log_error(NXT_LOG_ERR, "IPv6 socket \"%V\" is not supported",
744 addr);
745 return NULL;
746
747#endif
748}
749
750
751static nxt_sockaddr_t *
752nxt_sockaddr_inet_parse(nxt_mp_t *mp, nxt_str_t *addr)
753{
754 u_char *p;
755 size_t length;
756 nxt_int_t port;
757 in_addr_t inaddr;
758 nxt_sockaddr_t *sa;
759
760 p = nxt_memchr(addr->start, ':', addr->length);
761
762 if (nxt_fast_path(p != NULL)) {
763 inaddr = INADDR_ANY;
764 length = p - addr->start;
765
766 if (length != 1 || addr->start[0] != '*') {
767 inaddr = nxt_inet_addr(addr->start, length);
768
769 if (nxt_slow_path(inaddr == INADDR_NONE)) {
770 nxt_thread_log_error(NXT_LOG_ERR, "invalid address \"%V\"",
771 addr);
772 return NULL;
773 }
774 }
775
776 p++;
777 length = (addr->start + addr->length) - p;
778 port = nxt_int_parse(p, length);
779
780 if (port > 0 && port < 65536) {
781 sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in),
782 NXT_INET_ADDR_STR_LEN);
783
784 if (nxt_slow_path(sa != NULL)) {
785 sa->u.sockaddr_in.sin_family = AF_INET;
786 sa->u.sockaddr_in.sin_port = htons((in_port_t) port);
787 sa->u.sockaddr_in.sin_addr.s_addr = inaddr;
788 }
789
790 return sa;
791 }
792 }
793
794 nxt_thread_log_error(NXT_LOG_ERR, "invalid port in \"%V\"", addr);
795
796 return NULL;
797}
798
799
800void
801nxt_job_sockaddr_parse(nxt_job_sockaddr_parse_t *jbs)
802{
803 u_char *p;
804 size_t length;
805 nxt_int_t ret;
806 nxt_work_handler_t handler;
807
808 nxt_job_set_name(&jbs->resolve.job, "job sockaddr parse");
809
810 length = jbs->addr.length;
811 p = jbs->addr.start;
812
813 if (length > 6 && nxt_memcmp(p, "unix:", 5) == 0) {
814 ret = nxt_job_sockaddr_unix_parse(jbs);
815
816 } else if (length != 0 && *p == '[') {
817 ret = nxt_job_sockaddr_inet6_parse(jbs);
818
819 } else {
820 ret = nxt_job_sockaddr_inet_parse(jbs);
821 }
822
823 switch (ret) {
824
825 case NXT_OK:
826 handler = jbs->resolve.ready_handler;
827 break;
828
829 case NXT_ERROR:
830 handler = jbs->resolve.error_handler;
831 break;
832
833 default: /* NXT_AGAIN */
834 return;
835 }
836
837 nxt_job_return(jbs->resolve.job.task, &jbs->resolve.job, handler);
838}
839
840
841static nxt_int_t
842nxt_job_sockaddr_unix_parse(nxt_job_sockaddr_parse_t *jbs)
843{
844#if (NXT_HAVE_UNIX_DOMAIN)
845 size_t length, socklen;
846 u_char *path;
847 nxt_mp_t *mp;
848 nxt_sockaddr_t *sa;
849
850 /*
851 * Actual sockaddr_un length can be lesser or even larger than defined
852 * struct sockaddr_un length (see comment in nxt_socket.h). So
853 * limit maximum Unix domain socket address length by defined sun_path[]
854 * length because some OSes accept addresses twice larger than defined
855 * struct sockaddr_un. Also reserve space for a trailing zero to avoid
856 * ambiguity, since many OSes accept Unix domain socket addresses
857 * without a trailing zero.
858 */
859 const size_t max_len = sizeof(struct sockaddr_un)
860 - offsetof(struct sockaddr_un, sun_path) - 1;
861
862 /* cutting "unix:" */
863 length = jbs->addr.length - 5;
864 path = jbs->addr.start + 5;
865
866 if (length > max_len) {
867 nxt_thread_log_error(jbs->resolve.log_level,
868 "unix domain socket \"%V\" name is too long",
869 &jbs->addr);
870 return NXT_ERROR;
871 }
872
873 socklen = offsetof(struct sockaddr_un, sun_path) + length + 1;
874
875#if (NXT_LINUX)
876
877 /*
878 * Linux unix(7):
879 *
880 * abstract: an abstract socket address is distinguished by the fact
881 * that sun_path[0] is a null byte ('\0'). The socket's address in
882 * this namespace is given by the additional bytes in sun_path that
883 * are covered by the specified length of the address structure.
884 * (Null bytes in the name have no special significance.)
885 */
886 if (path[0] == '\0') {
887 socklen--;
888 }
889
890#endif
891
892 mp = jbs->resolve.job.mem_pool;
893
894 jbs->resolve.sockaddrs = nxt_mp_alloc(mp, sizeof(void *));
895
896 if (nxt_fast_path(jbs->resolve.sockaddrs != NULL)) {
897 sa = nxt_sockaddr_alloc(mp, socklen, jbs->addr.length);
898
899 if (nxt_fast_path(sa != NULL)) {
900 jbs->resolve.count = 1;
901 jbs->resolve.sockaddrs[0] = sa;
902
903 sa->u.sockaddr_un.sun_family = AF_UNIX;
904 nxt_memcpy(sa->u.sockaddr_un.sun_path, path, length);
905
906 return NXT_OK;
907 }
908 }
909
910 return NXT_ERROR;
911
912#else /* !(NXT_HAVE_UNIX_DOMAIN) */
913
914 nxt_thread_log_error(jbs->resolve.log_level,
915 "unix domain socket \"%V\" is not supported",
916 &jbs->addr);
917 return NXT_ERROR;
918
919#endif
920}
921
922
923static nxt_int_t
924nxt_job_sockaddr_inet6_parse(nxt_job_sockaddr_parse_t *jbs)
925{
926#if (NXT_INET6)
927 u_char *p, *addr, *addr_end;
928 size_t length;
929 nxt_mp_t *mp;
930 nxt_int_t port;
931 nxt_sockaddr_t *sa;
932 struct in6_addr *in6_addr;
933
934 length = jbs->addr.length - 1;
935 addr = jbs->addr.start + 1;
936
937 addr_end = nxt_memchr(addr, ']', length);
938
939 if (addr_end == NULL) {
940 goto invalid_address;
941 }
942
943 mp = jbs->resolve.job.mem_pool;
944
945 jbs->resolve.sockaddrs = nxt_mp_alloc(mp, sizeof(void *));
946
947 if (nxt_slow_path(jbs->resolve.sockaddrs == NULL)) {
948 return NXT_ERROR;
949 }
950
951 sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6),
952 NXT_INET6_ADDR_STR_LEN);
953
954 if (nxt_slow_path(sa == NULL)) {
955 return NXT_ERROR;
956 }
957
958 jbs->resolve.count = 1;
959 jbs->resolve.sockaddrs[0] = sa;
960
961 in6_addr = &sa->u.sockaddr_in6.sin6_addr;
962
963 if (nxt_inet6_addr(in6_addr, addr, addr_end - addr) != NXT_OK) {
964 goto invalid_address;
965 }
966
967 p = addr_end + 1;
968 length = (addr + length) - p;
969
970 if (length == 0) {
971 jbs->no_port = 1;
972 port = jbs->resolve.port;
973 goto found;
974 }
975
976 if (*p == ':') {
977 port = nxt_int_parse(p + 1, length - 1);
978
979 if (port >= 1 && port <= 65535) {
980 port = htons((in_port_t) port);
981 goto found;
982 }
983 }
984
985 nxt_thread_log_error(jbs->resolve.log_level,
986 "invalid port in \"%V\"", &jbs->addr);
987
988 return NXT_ERROR;
989
990found:
991
992 sa->u.sockaddr_in6.sin6_family = AF_INET6;
993 sa->u.sockaddr_in6.sin6_port = (in_port_t) port;
994
995 if (IN6_IS_ADDR_UNSPECIFIED(in6_addr)) {
996 jbs->wildcard = 1;
997 }
998
999 return NXT_OK;
1000
1001invalid_address:
1002
1003 nxt_thread_log_error(jbs->resolve.log_level,
1004 "invalid IPv6 address in \"%V\"", &jbs->addr);
1005 return NXT_ERROR;
1006
1007#else
1008
1009 nxt_thread_log_error(jbs->resolve.log_level,
1010 "IPv6 socket \"%V\" is not supported", &jbs->addr);
1011 return NXT_ERROR;
1012
1013#endif
1014}
1015
1016
1017static nxt_int_t
1018nxt_job_sockaddr_inet_parse(nxt_job_sockaddr_parse_t *jbs)
1019{
1020 u_char *p, *host;
1021 size_t length;
1022 nxt_mp_t *mp;
1023 nxt_int_t port;
1024 in_addr_t addr;
1025 nxt_sockaddr_t *sa;
1026
1027 addr = INADDR_ANY;
1028
1029 length = jbs->addr.length;
1030 host = jbs->addr.start;
1031
1032 p = nxt_memchr(host, ':', length);
1033
1034 if (p == NULL) {
1035
1036 /* single value port, address, or host name */
1037
1038 port = nxt_int_parse(host, length);
1039
1040 if (port > 0) {
1041 if (port < 1 || port > 65535) {
1042 goto invalid_port;
1043 }
1044
1045 /* "*:XX" */
1046 port = htons((in_port_t) port);
1047 jbs->resolve.port = (in_port_t) port;
1048
1049 } else {
1050 jbs->no_port = 1;
1051
1052 addr = nxt_inet_addr(host, length);
1053
1054 if (addr == INADDR_NONE) {
1055 jbs->resolve.name.length = length;
1056 jbs->resolve.name.start = host;
1057
1058 nxt_job_resolve(&jbs->resolve);
1059 return NXT_AGAIN;
1060 }
1061
1062 /* "x.x.x.x" */
1063 port = jbs->resolve.port;
1064 }
1065
1066 } else {
1067
1068 /* x.x.x.x:XX or host:XX */
1069
1070 p++;
1071 length = (host + length) - p;
1072 port = nxt_int_parse(p, length);
1073
1074 if (port < 1 || port > 65535) {
1075 goto invalid_port;
1076 }
1077
1078 port = htons((in_port_t) port);
1079
1080 length = (p - 1) - host;
1081
1082 if (length != 1 || host[0] != '*') {
1083 addr = nxt_inet_addr(host, length);
1084
1085 if (addr == INADDR_NONE) {
1086 jbs->resolve.name.length = length;
1087 jbs->resolve.name.start = host;
1088 jbs->resolve.port = (in_port_t) port;
1089
1090 nxt_job_resolve(&jbs->resolve);
1091 return NXT_AGAIN;
1092 }
1093
1094 /* "x.x.x.x:XX" */
1095 }
1096 }
1097
1098 mp = jbs->resolve.job.mem_pool;
1099
1100 jbs->resolve.sockaddrs = nxt_mp_alloc(mp, sizeof(void *));
1101 if (nxt_slow_path(jbs->resolve.sockaddrs == NULL)) {
1102 return NXT_ERROR;
1103 }
1104
1105 sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in),
1106 NXT_INET_ADDR_STR_LEN);
1107
1108 if (nxt_fast_path(sa != NULL)) {
1109 jbs->resolve.count = 1;
1110 jbs->resolve.sockaddrs[0] = sa;
1111
1112 jbs->wildcard = (addr == INADDR_ANY);
1113
1114 sa->u.sockaddr_in.sin_family = AF_INET;
1115 sa->u.sockaddr_in.sin_port = (in_port_t) port;
1116 sa->u.sockaddr_in.sin_addr.s_addr = addr;
1117
1118 return NXT_OK;
1119 }
1120
1121 return NXT_ERROR;
1122
1123invalid_port:
1124
1125 nxt_thread_log_error(jbs->resolve.log_level,
1126 "invalid port in \"%V\"", &jbs->addr);
1127
1128 return NXT_ERROR;
1129}
1130
1131
1132in_addr_t
1133nxt_inet_addr(u_char *buf, size_t length)
1134{
1135 u_char c, *end;
1136 in_addr_t addr;
1137 nxt_uint_t digit, octet, dots;
1138
1139 addr = 0;
1140 octet = 0;
1141 dots = 0;
1142
1143 end = buf + length;
1144
1145 while (buf < end) {
1146
1147 c = *buf++;
1148
1149 digit = c - '0';
1150 /* values below '0' become large unsigned integers */
1151
1152 if (digit < 10) {
1153 octet = octet * 10 + digit;
1154 continue;
1155 }
1156
1157 if (c == '.' && octet < 256) {
1158 addr = (addr << 8) + octet;
1159 octet = 0;
1160 dots++;
1161 continue;
1162 }
1163
1164 return INADDR_NONE;
1165 }
1166
1167 if (dots == 3 && octet < 256) {
1168 addr = (addr << 8) + octet;
1169 return htonl(addr);
1170 }
1171
1172 return INADDR_NONE;
1173}
1174
1175
1176#if (NXT_INET6)
1177
1178nxt_int_t
1179nxt_inet6_addr(struct in6_addr *in6_addr, u_char *buf, size_t length)
1180{
1181 u_char c, *addr, *zero_start, *ipv4, *dst, *src, *end;
1182 nxt_uint_t digit, group, nibbles, groups_left;
1183
1184 if (length == 0) {
1185 return NXT_ERROR;
1186 }
1187
1188 end = buf + length;
1189
1190 if (buf[0] == ':') {
1191 buf++;
1192 }
1193
1194 addr = in6_addr->s6_addr;
1195 zero_start = NULL;
1196 groups_left = 8;
1197 nibbles = 0;
1198 group = 0;
1199 ipv4 = NULL;
1200
1201 while (buf < end) {
1202 c = *buf++;
1203
1204 if (c == ':') {
1205 if (nibbles != 0) {
1206 ipv4 = buf;
1207
1208 *addr++ = (u_char) (group >> 8);
1209 *addr++ = (u_char) (group & 0xFF);
1210 groups_left--;
1211
1212 if (groups_left != 0) {
1213 nibbles = 0;
1214 group = 0;
1215 continue;
1216 }
1217
1218 } else {
1219 if (zero_start == NULL) {
1220 ipv4 = buf;
1221 zero_start = addr;
1222 continue;
1223 }
1224 }
1225
1226 return NXT_ERROR;
1227 }
1228
1229 if (c == '.' && nibbles != 0) {
1230
1231 if (groups_left < 2 || ipv4 == NULL) {
1232 return NXT_ERROR;
1233 }
1234
1235 group = nxt_inet_addr(ipv4, end - ipv4);
1236 if (group == INADDR_NONE) {
1237 return NXT_ERROR;
1238 }
1239
1240 group = ntohl(group);
1241
1242 *addr++ = (u_char) ((group >> 24) & 0xFF);
1243 *addr++ = (u_char) ((group >> 16) & 0xFF);
1244 groups_left--;
1245
1246 /* the low 16-bit are copied below */
1247 break;
1248 }
1249
1250 nibbles++;
1251
1252 if (nibbles > 4) {
1253 return NXT_ERROR;
1254 }
1255
1256 group <<= 4;
1257
1258 digit = c - '0';
1259 /* values below '0' become large unsigned integers */
1260
1261 if (digit < 10) {
1262 group += digit;
1263 continue;
1264 }
1265
1266 c |= 0x20;
1267 digit = c - 'a';
1268 /* values below 'a' become large unsigned integers */
1269
1270 if (digit < 6) {
1271 group += 10 + digit;
1272 continue;
1273 }
1274
1275 return NXT_ERROR;
1276 }
1277
1278 if (nibbles == 0 && zero_start == NULL) {
1279 return NXT_ERROR;
1280 }
1281
1282 *addr++ = (u_char) (group >> 8);
1283 *addr++ = (u_char) (group & 0xFF);
1284 groups_left--;
1285
1286 if (groups_left != 0) {
1287
1288 if (zero_start != NULL) {
1289
1290 /* moving part before consecutive zero groups to the end */
1291
1292 groups_left *= 2;
1293 src = addr - 1;
1294 dst = src + groups_left;
1295
1296 while (src >= zero_start) {
1297 *dst-- = *src--;
1298 }
1299
1300 nxt_memzero(zero_start, groups_left);
1301
1302 return NXT_OK;
1303 }
1304
1305 } else {
1306 if (zero_start == NULL) {
1307 return NXT_OK;
1308 }
1309 }
1310
1311 return NXT_ERROR;
1312}
1313
1314#endif