nxt_sockaddr.c (4:76c63e9b6322) nxt_sockaddr.c (10:a8e68ed06863)
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_int_t nxt_job_sockaddr_unix_parse(nxt_job_sockaddr_parse_t *jbs);
15static nxt_int_t nxt_job_sockaddr_inet6_parse(nxt_job_sockaddr_parse_t *jbs);
16static nxt_int_t nxt_job_sockaddr_inet_parse(nxt_job_sockaddr_parse_t *jbs);
17
18
19nxt_sockaddr_t *
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_int_t nxt_job_sockaddr_unix_parse(nxt_job_sockaddr_parse_t *jbs);
15static nxt_int_t nxt_job_sockaddr_inet6_parse(nxt_job_sockaddr_parse_t *jbs);
16static nxt_int_t nxt_job_sockaddr_inet_parse(nxt_job_sockaddr_parse_t *jbs);
17
18
19nxt_sockaddr_t *
20nxt_sockaddr_alloc(nxt_mem_pool_t *mp, socklen_t len)
20nxt_sockaddr_alloc(nxt_mem_pool_t *mp, socklen_t length)
21{
22 nxt_sockaddr_t *sa;
23
24 /*
25 * The current struct sockaddr's define 32-bit fields at maximum
26 * and may define 64-bit AF_INET6 fields in the future. Alignment
27 * of memory allocated by nxt_mem_zalloc() is enough for these fields.
28 * If 128-bit alignment will be required then nxt_mem_malloc() and
29 * nxt_memzero() should be used instead.
30 */
21{
22 nxt_sockaddr_t *sa;
23
24 /*
25 * The current struct sockaddr's define 32-bit fields at maximum
26 * and may define 64-bit AF_INET6 fields in the future. Alignment
27 * of memory allocated by nxt_mem_zalloc() is enough for these fields.
28 * If 128-bit alignment will be required then nxt_mem_malloc() and
29 * nxt_memzero() should be used instead.
30 */
31 sa = nxt_mem_zalloc(mp, offsetof(nxt_sockaddr_t, u) + len);
31 sa = nxt_mem_zalloc(mp, offsetof(nxt_sockaddr_t, u) + length);
32
33 if (nxt_fast_path(sa != NULL)) {
32
33 if (nxt_fast_path(sa != NULL)) {
34 nxt_socklen_set(sa, len);
34 nxt_socklen_set(sa, length);
35 }
36
37 return sa;
38}
39
40
41nxt_sockaddr_t *
42nxt_sockaddr_create(nxt_mem_pool_t *mp, struct sockaddr *sockaddr,
35 }
36
37 return sa;
38}
39
40
41nxt_sockaddr_t *
42nxt_sockaddr_create(nxt_mem_pool_t *mp, struct sockaddr *sockaddr,
43 socklen_t len)
43 socklen_t length)
44{
45 size_t size, copy;
46 nxt_sockaddr_t *sa;
47
44{
45 size_t size, copy;
46 nxt_sockaddr_t *sa;
47
48 size = len;
49 copy = len;
48 size = length;
49 copy = length;
50
51#if (NXT_HAVE_UNIX_DOMAIN)
52
53 /*
54 * Unspecified Unix domain sockaddr_un form and length are very
55 * platform depended (see comment in unix/socket.h). Here they are
56 * normalized to the sockaddr_un with single zero byte sun_path[].
57 */
58
59 if (size <= offsetof(struct sockaddr_un, sun_path)) {
60 /*
61 * Small socket length means a short unspecified Unix domain
62 * socket address:
63 *
64 * getsockname() and getpeername() on OpenBSD prior to 5.3
65 * return zero length and does not update a passed sockaddr
66 * buffer at all.
67 *
68 * Linux returns length equal to 2, i.e. sockaddr_un without
69 * sun_path[], unix(7):
70 *
71 * unnamed: A stream socket that has not been bound
72 * to a pathname using bind(2) has no name. Likewise,
73 * the two sockets created by socketpair(2) are unnamed.
74 * When the address of an unnamed socket is returned by
75 * getsockname(2), getpeername(2), and accept(2), its
76 * length is sizeof(sa_family_t), and sun_path should
77 * not be inspected.
78 */
79 size = offsetof(struct sockaddr_un, sun_path) + 1;
80
81#if !(NXT_LINUX)
82
83 } else if (sockaddr->sa_family == AF_UNIX && sockaddr->sa_data[0] == '\0') {
84 /*
85 * Omit nonsignificant zeros of the unspecified Unix domain socket
86 * address. This test is disabled for Linux since Linux abstract
87 * socket address also starts with zero. However Linux unspecified
88 * Unix domain socket address is short and is handled above.
89 */
90 size = offsetof(struct sockaddr_un, sun_path) + 1;
91 copy = size;
92
93#endif
94 }
95
96#endif /* NXT_HAVE_UNIX_DOMAIN */
97
98 sa = nxt_sockaddr_alloc(mp, size);
99
100 if (nxt_fast_path(sa != NULL)) {
101
102 nxt_memcpy(&sa->u.sockaddr, sockaddr, copy);
103
104#if (NXT_SOCKADDR_SA_LEN)
105
106 /* Update shortcut sockaddr length overwritten by nxt_memcpy(). */
107 nxt_socklen_set(sa, size);
108
109#endif
110
111#if (NXT_HAVE_UNIX_DOMAIN && NXT_OPENBSD)
112
50
51#if (NXT_HAVE_UNIX_DOMAIN)
52
53 /*
54 * Unspecified Unix domain sockaddr_un form and length are very
55 * platform depended (see comment in unix/socket.h). Here they are
56 * normalized to the sockaddr_un with single zero byte sun_path[].
57 */
58
59 if (size <= offsetof(struct sockaddr_un, sun_path)) {
60 /*
61 * Small socket length means a short unspecified Unix domain
62 * socket address:
63 *
64 * getsockname() and getpeername() on OpenBSD prior to 5.3
65 * return zero length and does not update a passed sockaddr
66 * buffer at all.
67 *
68 * Linux returns length equal to 2, i.e. sockaddr_un without
69 * sun_path[], unix(7):
70 *
71 * unnamed: A stream socket that has not been bound
72 * to a pathname using bind(2) has no name. Likewise,
73 * the two sockets created by socketpair(2) are unnamed.
74 * When the address of an unnamed socket is returned by
75 * getsockname(2), getpeername(2), and accept(2), its
76 * length is sizeof(sa_family_t), and sun_path should
77 * not be inspected.
78 */
79 size = offsetof(struct sockaddr_un, sun_path) + 1;
80
81#if !(NXT_LINUX)
82
83 } else if (sockaddr->sa_family == AF_UNIX && sockaddr->sa_data[0] == '\0') {
84 /*
85 * Omit nonsignificant zeros of the unspecified Unix domain socket
86 * address. This test is disabled for Linux since Linux abstract
87 * socket address also starts with zero. However Linux unspecified
88 * Unix domain socket address is short and is handled above.
89 */
90 size = offsetof(struct sockaddr_un, sun_path) + 1;
91 copy = size;
92
93#endif
94 }
95
96#endif /* NXT_HAVE_UNIX_DOMAIN */
97
98 sa = nxt_sockaddr_alloc(mp, size);
99
100 if (nxt_fast_path(sa != NULL)) {
101
102 nxt_memcpy(&sa->u.sockaddr, sockaddr, copy);
103
104#if (NXT_SOCKADDR_SA_LEN)
105
106 /* Update shortcut sockaddr length overwritten by nxt_memcpy(). */
107 nxt_socklen_set(sa, size);
108
109#endif
110
111#if (NXT_HAVE_UNIX_DOMAIN && NXT_OPENBSD)
112
113 if (len == 0) {
113 if (length == 0) {
114 sa->u.sockaddr.sa_family = AF_UNIX;
115 }
116
117#endif
118 }
119
120 return sa;
121}
122
123
124nxt_sockaddr_t *
125nxt_sockaddr_copy(nxt_mem_pool_t *mp, nxt_sockaddr_t *src)
126{
114 sa->u.sockaddr.sa_family = AF_UNIX;
115 }
116
117#endif
118 }
119
120 return sa;
121}
122
123
124nxt_sockaddr_t *
125nxt_sockaddr_copy(nxt_mem_pool_t *mp, nxt_sockaddr_t *src)
126{
127 size_t len;
127 size_t length;
128 nxt_sockaddr_t *dst;
129
128 nxt_sockaddr_t *dst;
129
130 len = offsetof(nxt_sockaddr_t, u) + nxt_socklen(src);
130 length = offsetof(nxt_sockaddr_t, u) + nxt_socklen(src);
131
131
132 dst = nxt_mem_alloc(mp, len);
132 dst = nxt_mem_alloc(mp, length);
133
134 if (nxt_fast_path(dst != NULL)) {
133
134 if (nxt_fast_path(dst != NULL)) {
135 nxt_memcpy(dst, src, len);
135 nxt_memcpy(dst, src, length);
136 }
137
138 return dst;
139}
140
141
142nxt_sockaddr_t *
143nxt_getsockname(nxt_mem_pool_t *mp, nxt_socket_t s)
144{
145 int ret;
146 socklen_t socklen;
147 nxt_sockaddr_buf_t sockaddr;
148
149 socklen = NXT_SOCKADDR_LEN;
150
151 ret = getsockname(s, &sockaddr.buf, &socklen);
152
153 if (nxt_fast_path(ret == 0)) {
154 return nxt_sockaddr_create(mp, &sockaddr.buf, socklen);
155 }
156
157 nxt_thread_log_error(NXT_LOG_ERR, "getsockname(%d) failed %E",
158 s, nxt_errno);
159
160 return NULL;
161}
162
163
164nxt_int_t
165nxt_sockaddr_text(nxt_mem_pool_t *mp, nxt_sockaddr_t *sa, nxt_bool_t port)
166{
136 }
137
138 return dst;
139}
140
141
142nxt_sockaddr_t *
143nxt_getsockname(nxt_mem_pool_t *mp, nxt_socket_t s)
144{
145 int ret;
146 socklen_t socklen;
147 nxt_sockaddr_buf_t sockaddr;
148
149 socklen = NXT_SOCKADDR_LEN;
150
151 ret = getsockname(s, &sockaddr.buf, &socklen);
152
153 if (nxt_fast_path(ret == 0)) {
154 return nxt_sockaddr_create(mp, &sockaddr.buf, socklen);
155 }
156
157 nxt_thread_log_error(NXT_LOG_ERR, "getsockname(%d) failed %E",
158 s, nxt_errno);
159
160 return NULL;
161}
162
163
164nxt_int_t
165nxt_sockaddr_text(nxt_mem_pool_t *mp, nxt_sockaddr_t *sa, nxt_bool_t port)
166{
167 size_t len;
167 size_t length;
168 u_char *p;
169 u_char buf[NXT_SOCKADDR_STR_LEN + NXT_SOCKPORT_STR_LEN];
170
168 u_char *p;
169 u_char buf[NXT_SOCKADDR_STR_LEN + NXT_SOCKPORT_STR_LEN];
170
171 len = NXT_SOCKADDR_STR_LEN + NXT_SOCKPORT_STR_LEN;
171 length = NXT_SOCKADDR_STR_LEN + NXT_SOCKPORT_STR_LEN;
172
172
173 len = nxt_sockaddr_ntop(sa, buf, buf + len, port);
173 length = nxt_sockaddr_ntop(sa, buf, buf + length, port);
174
174
175 p = nxt_mem_alloc(mp, len);
175 p = nxt_mem_alloc(mp, length);
176
177 if (nxt_fast_path(p != NULL)) {
178
179 sa->text = p;
176
177 if (nxt_fast_path(p != NULL)) {
178
179 sa->text = p;
180 sa->text_len = len;
181 nxt_memcpy(p, buf, len);
180 sa->text_len = length;
181 nxt_memcpy(p, buf, length);
182
183 return NXT_OK;
184 }
185
186 return NXT_ERROR;
187}
188
189
190uint32_t
191nxt_sockaddr_port(nxt_sockaddr_t *sa)
192{
193 uint32_t port;
194
195 switch (sa->u.sockaddr.sa_family) {
196
197#if (NXT_INET6)
198
199 case AF_INET6:
200 port = sa->u.sockaddr_in6.sin6_port;
201 break;
202
203#endif
204
205#if (NXT_HAVE_UNIX_DOMAIN)
206
207 case AF_UNIX:
208 return 0;
209
210#endif
211
212 default:
213 port = sa->u.sockaddr_in.sin_port;
214 break;
215 }
216
217 return ntohs((uint16_t) port);
218}
219
220
221nxt_bool_t
222nxt_sockaddr_cmp(nxt_sockaddr_t *sa1, nxt_sockaddr_t *sa2)
223{
224 if (nxt_socklen(sa1) != nxt_socklen(sa2)) {
225 return 0;
226 }
227
228 if (sa1->type != sa2->type) {
229 return 0;
230 }
231
232 if (sa1->u.sockaddr.sa_family != sa2->u.sockaddr.sa_family) {
233 return 0;
234 }
235
236 /*
237 * sockaddr struct's cannot be compared in whole since kernel
238 * may fill some fields in inherited sockaddr struct's.
239 */
240
241 switch (sa1->u.sockaddr.sa_family) {
242
243#if (NXT_INET6)
244
245 case AF_INET6:
246 if (sa1->u.sockaddr_in6.sin6_port != sa2->u.sockaddr_in6.sin6_port) {
247 return 0;
248 }
249
250 if (nxt_memcmp(&sa1->u.sockaddr_in6.sin6_addr,
251 &sa2->u.sockaddr_in6.sin6_addr, 16)
252 != 0)
253 {
254 return 0;
255 }
256
257 return 1;
258
259#endif
260
261#if (NXT_HAVE_UNIX_DOMAIN)
262
263 case AF_UNIX:
264 {
182
183 return NXT_OK;
184 }
185
186 return NXT_ERROR;
187}
188
189
190uint32_t
191nxt_sockaddr_port(nxt_sockaddr_t *sa)
192{
193 uint32_t port;
194
195 switch (sa->u.sockaddr.sa_family) {
196
197#if (NXT_INET6)
198
199 case AF_INET6:
200 port = sa->u.sockaddr_in6.sin6_port;
201 break;
202
203#endif
204
205#if (NXT_HAVE_UNIX_DOMAIN)
206
207 case AF_UNIX:
208 return 0;
209
210#endif
211
212 default:
213 port = sa->u.sockaddr_in.sin_port;
214 break;
215 }
216
217 return ntohs((uint16_t) port);
218}
219
220
221nxt_bool_t
222nxt_sockaddr_cmp(nxt_sockaddr_t *sa1, nxt_sockaddr_t *sa2)
223{
224 if (nxt_socklen(sa1) != nxt_socklen(sa2)) {
225 return 0;
226 }
227
228 if (sa1->type != sa2->type) {
229 return 0;
230 }
231
232 if (sa1->u.sockaddr.sa_family != sa2->u.sockaddr.sa_family) {
233 return 0;
234 }
235
236 /*
237 * sockaddr struct's cannot be compared in whole since kernel
238 * may fill some fields in inherited sockaddr struct's.
239 */
240
241 switch (sa1->u.sockaddr.sa_family) {
242
243#if (NXT_INET6)
244
245 case AF_INET6:
246 if (sa1->u.sockaddr_in6.sin6_port != sa2->u.sockaddr_in6.sin6_port) {
247 return 0;
248 }
249
250 if (nxt_memcmp(&sa1->u.sockaddr_in6.sin6_addr,
251 &sa2->u.sockaddr_in6.sin6_addr, 16)
252 != 0)
253 {
254 return 0;
255 }
256
257 return 1;
258
259#endif
260
261#if (NXT_HAVE_UNIX_DOMAIN)
262
263 case AF_UNIX:
264 {
265 size_t len;
265 size_t length;
266
266
267 len = nxt_socklen(sa1) - offsetof(struct sockaddr_un, sun_path);
267 length = nxt_socklen(sa1) - offsetof(struct sockaddr_un, sun_path);
268
269 if (nxt_memcmp(&sa1->u.sockaddr_un.sun_path,
268
269 if (nxt_memcmp(&sa1->u.sockaddr_un.sun_path,
270 &sa2->u.sockaddr_un.sun_path, len)
270 &sa2->u.sockaddr_un.sun_path, length)
271 != 0)
272 {
273 return 0;
274 }
275
276 return 1;
277 }
278
279#endif
280
281 default: /* AF_INET */
282 if (sa1->u.sockaddr_in.sin_port != sa2->u.sockaddr_in.sin_port) {
283 return 0;
284 }
285
286 if (sa1->u.sockaddr_in.sin_addr.s_addr
287 != sa2->u.sockaddr_in.sin_addr.s_addr)
288 {
289 return 0;
290 }
291
292 return 1;
293 }
294}
295
296
297size_t
298nxt_sockaddr_ntop(nxt_sockaddr_t *sa, u_char *buf, u_char *end, nxt_bool_t port)
299{
300 u_char *p;
301
302 switch (sa->u.sockaddr.sa_family) {
303
304 case AF_INET:
305 p = (u_char *) &sa->u.sockaddr_in.sin_addr;
306
307 if (port) {
308 p = nxt_sprintf(buf, end, "%ud.%ud.%ud.%ud:%d",
309 p[0], p[1], p[2], p[3],
310 ntohs(sa->u.sockaddr_in.sin_port));
311 } else {
312 p = nxt_sprintf(buf, end, "%ud.%ud.%ud.%ud",
313 p[0], p[1], p[2], p[3]);
314 }
315
316 return p - buf;
317
318#if (NXT_INET6)
319
320 case AF_INET6:
321 p = buf;
322
323 if (port) {
324 *p++ = '[';
325 }
326
327 p = nxt_inet6_ntop(sa->u.sockaddr_in6.sin6_addr.s6_addr, p, end);
328
329 if (port) {
330 p = nxt_sprintf(p, end, "]:%d",
331 ntohs(sa->u.sockaddr_in6.sin6_port));
332 }
333
334 return p - buf;
335#endif
336
337#if (NXT_HAVE_UNIX_DOMAIN)
338
339 case AF_UNIX:
340
341#if (NXT_LINUX)
342
343 p = (u_char *) sa->u.sockaddr_un.sun_path;
344
345 if (p[0] == '\0') {
271 != 0)
272 {
273 return 0;
274 }
275
276 return 1;
277 }
278
279#endif
280
281 default: /* AF_INET */
282 if (sa1->u.sockaddr_in.sin_port != sa2->u.sockaddr_in.sin_port) {
283 return 0;
284 }
285
286 if (sa1->u.sockaddr_in.sin_addr.s_addr
287 != sa2->u.sockaddr_in.sin_addr.s_addr)
288 {
289 return 0;
290 }
291
292 return 1;
293 }
294}
295
296
297size_t
298nxt_sockaddr_ntop(nxt_sockaddr_t *sa, u_char *buf, u_char *end, nxt_bool_t port)
299{
300 u_char *p;
301
302 switch (sa->u.sockaddr.sa_family) {
303
304 case AF_INET:
305 p = (u_char *) &sa->u.sockaddr_in.sin_addr;
306
307 if (port) {
308 p = nxt_sprintf(buf, end, "%ud.%ud.%ud.%ud:%d",
309 p[0], p[1], p[2], p[3],
310 ntohs(sa->u.sockaddr_in.sin_port));
311 } else {
312 p = nxt_sprintf(buf, end, "%ud.%ud.%ud.%ud",
313 p[0], p[1], p[2], p[3]);
314 }
315
316 return p - buf;
317
318#if (NXT_INET6)
319
320 case AF_INET6:
321 p = buf;
322
323 if (port) {
324 *p++ = '[';
325 }
326
327 p = nxt_inet6_ntop(sa->u.sockaddr_in6.sin6_addr.s6_addr, p, end);
328
329 if (port) {
330 p = nxt_sprintf(p, end, "]:%d",
331 ntohs(sa->u.sockaddr_in6.sin6_port));
332 }
333
334 return p - buf;
335#endif
336
337#if (NXT_HAVE_UNIX_DOMAIN)
338
339 case AF_UNIX:
340
341#if (NXT_LINUX)
342
343 p = (u_char *) sa->u.sockaddr_un.sun_path;
344
345 if (p[0] == '\0') {
346 int len;
346 int length;
347
348 /* Linux abstract socket address has no trailing zero. */
349
347
348 /* Linux abstract socket address has no trailing zero. */
349
350 len = nxt_socklen(sa) - offsetof(struct sockaddr_un, sun_path) - 1;
351 p = nxt_sprintf(buf, end, "unix:\\0%*s", len, p + 1);
350 length = nxt_socklen(sa)
351 - offsetof(struct sockaddr_un, sun_path) - 1;
352 p = nxt_sprintf(buf, end, "unix:\\0%*s", length, p + 1);
352
353 } else {
354 p = nxt_sprintf(buf, end, "unix:%s", p);
355 }
356
357#else /* !(NXT_LINUX) */
358
359 p = nxt_sprintf(buf, end, "unix:%s", sa->u.sockaddr_un.sun_path);
360
361#endif
362
363 return p - buf;
364
365#endif /* NXT_HAVE_UNIX_DOMAIN */
366
367 default:
368 return 0;
369 }
370}
371
372
373#if (NXT_INET6)
374
375static u_char *
376nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end)
377{
378 u_char *p;
379 size_t zero_groups, last_zero_groups, ipv6_bytes;
380 nxt_uint_t i, zero_start, last_zero_start;
381
382 if (buf + NXT_INET6_ADDR_STR_LEN > end) {
383 return buf;
384 }
385
386 zero_start = 8;
387 zero_groups = 0;
388 last_zero_start = 8;
389 last_zero_groups = 0;
390
391 for (i = 0; i < 16; i += 2) {
392
393 if (addr[i] == 0 && addr[i + 1] == 0) {
394
395 if (last_zero_groups == 0) {
396 last_zero_start = i;
397 }
398
399 last_zero_groups++;
400
401 } else {
402 if (zero_groups < last_zero_groups) {
403 zero_groups = last_zero_groups;
404 zero_start = last_zero_start;
405 }
406
407 last_zero_groups = 0;
408 }
409 }
410
411 if (zero_groups < last_zero_groups) {
412 zero_groups = last_zero_groups;
413 zero_start = last_zero_start;
414 }
415
416 ipv6_bytes = 16;
417 p = buf;
418
419 if (zero_start == 0) {
420
421 /* IPv4-mapped address */
422 if ((zero_groups == 5 && addr[10] == 0xff && addr[11] == 0xff)
423 /* IPv4-compatible address */
424 || (zero_groups == 6)
425 /* not IPv6 loopback address */
426 || (zero_groups == 7 && addr[14] != 0 && addr[15] != 1))
427 {
428 ipv6_bytes = 12;
429 }
430
431 *p++ = ':';
432 }
433
434 for (i = 0; i < ipv6_bytes; i += 2) {
435
436 if (i == zero_start) {
437 /* Output maximum number of consecutive zero groups as "::". */
438 i += (zero_groups - 1) * 2;
439 *p++ = ':';
440 continue;
441 }
442
443 p = nxt_sprintf(p, end, "%uxd", (addr[i] << 8) + addr[i + 1]);
444
445 if (i < 14) {
446 *p++ = ':';
447 }
448 }
449
450 if (ipv6_bytes == 12) {
451 p = nxt_sprintf(p, end, "%ud.%ud.%ud.%ud",
452 addr[12], addr[13], addr[14], addr[15]);
453 }
454
455 return p;
456}
457
458#endif
459
460
461void
462nxt_job_sockaddr_parse(nxt_job_sockaddr_parse_t *jbs)
463{
464 u_char *p;
353
354 } else {
355 p = nxt_sprintf(buf, end, "unix:%s", p);
356 }
357
358#else /* !(NXT_LINUX) */
359
360 p = nxt_sprintf(buf, end, "unix:%s", sa->u.sockaddr_un.sun_path);
361
362#endif
363
364 return p - buf;
365
366#endif /* NXT_HAVE_UNIX_DOMAIN */
367
368 default:
369 return 0;
370 }
371}
372
373
374#if (NXT_INET6)
375
376static u_char *
377nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end)
378{
379 u_char *p;
380 size_t zero_groups, last_zero_groups, ipv6_bytes;
381 nxt_uint_t i, zero_start, last_zero_start;
382
383 if (buf + NXT_INET6_ADDR_STR_LEN > end) {
384 return buf;
385 }
386
387 zero_start = 8;
388 zero_groups = 0;
389 last_zero_start = 8;
390 last_zero_groups = 0;
391
392 for (i = 0; i < 16; i += 2) {
393
394 if (addr[i] == 0 && addr[i + 1] == 0) {
395
396 if (last_zero_groups == 0) {
397 last_zero_start = i;
398 }
399
400 last_zero_groups++;
401
402 } else {
403 if (zero_groups < last_zero_groups) {
404 zero_groups = last_zero_groups;
405 zero_start = last_zero_start;
406 }
407
408 last_zero_groups = 0;
409 }
410 }
411
412 if (zero_groups < last_zero_groups) {
413 zero_groups = last_zero_groups;
414 zero_start = last_zero_start;
415 }
416
417 ipv6_bytes = 16;
418 p = buf;
419
420 if (zero_start == 0) {
421
422 /* IPv4-mapped address */
423 if ((zero_groups == 5 && addr[10] == 0xff && addr[11] == 0xff)
424 /* IPv4-compatible address */
425 || (zero_groups == 6)
426 /* not IPv6 loopback address */
427 || (zero_groups == 7 && addr[14] != 0 && addr[15] != 1))
428 {
429 ipv6_bytes = 12;
430 }
431
432 *p++ = ':';
433 }
434
435 for (i = 0; i < ipv6_bytes; i += 2) {
436
437 if (i == zero_start) {
438 /* Output maximum number of consecutive zero groups as "::". */
439 i += (zero_groups - 1) * 2;
440 *p++ = ':';
441 continue;
442 }
443
444 p = nxt_sprintf(p, end, "%uxd", (addr[i] << 8) + addr[i + 1]);
445
446 if (i < 14) {
447 *p++ = ':';
448 }
449 }
450
451 if (ipv6_bytes == 12) {
452 p = nxt_sprintf(p, end, "%ud.%ud.%ud.%ud",
453 addr[12], addr[13], addr[14], addr[15]);
454 }
455
456 return p;
457}
458
459#endif
460
461
462void
463nxt_job_sockaddr_parse(nxt_job_sockaddr_parse_t *jbs)
464{
465 u_char *p;
465 size_t len;
466 size_t length;
466 nxt_int_t ret;
467 nxt_work_handler_t handler;
468
469 nxt_job_set_name(&jbs->resolve.job, "job sockaddr parse");
470
467 nxt_int_t ret;
468 nxt_work_handler_t handler;
469
470 nxt_job_set_name(&jbs->resolve.job, "job sockaddr parse");
471
471 len = jbs->addr.len;
472 p = jbs->addr.data;
472 length = jbs->addr.length;
473 p = jbs->addr.start;
473
474
474 if (len > 6 && nxt_memcmp(p, (u_char *) "unix:", 5) == 0) {
475 if (length > 6 && nxt_memcmp(p, (u_char *) "unix:", 5) == 0) {
475 ret = nxt_job_sockaddr_unix_parse(jbs);
476
476 ret = nxt_job_sockaddr_unix_parse(jbs);
477
477 } else if (len != 0 && *p == '[') {
478 } else if (length != 0 && *p == '[') {
478 ret = nxt_job_sockaddr_inet6_parse(jbs);
479
480 } else {
481 ret = nxt_job_sockaddr_inet_parse(jbs);
482 }
483
484 switch (ret) {
485
486 case NXT_OK:
487 handler = jbs->resolve.ready_handler;
488 break;
489
490 case NXT_ERROR:
491 handler = jbs->resolve.error_handler;
492 break;
493
494 default: /* NXT_AGAIN */
495 return;
496 }
497
498 nxt_job_return(jbs->resolve.job.task, &jbs->resolve.job, handler);
499}
500
501
502static nxt_int_t
503nxt_job_sockaddr_unix_parse(nxt_job_sockaddr_parse_t *jbs)
504{
505#if (NXT_HAVE_UNIX_DOMAIN)
479 ret = nxt_job_sockaddr_inet6_parse(jbs);
480
481 } else {
482 ret = nxt_job_sockaddr_inet_parse(jbs);
483 }
484
485 switch (ret) {
486
487 case NXT_OK:
488 handler = jbs->resolve.ready_handler;
489 break;
490
491 case NXT_ERROR:
492 handler = jbs->resolve.error_handler;
493 break;
494
495 default: /* NXT_AGAIN */
496 return;
497 }
498
499 nxt_job_return(jbs->resolve.job.task, &jbs->resolve.job, handler);
500}
501
502
503static nxt_int_t
504nxt_job_sockaddr_unix_parse(nxt_job_sockaddr_parse_t *jbs)
505{
506#if (NXT_HAVE_UNIX_DOMAIN)
506 size_t len, socklen;
507 size_t length, socklen;
507 u_char *path;
508 nxt_mem_pool_t *mp;
509 nxt_sockaddr_t *sa;
510
511 /*
512 * Actual sockaddr_un length can be lesser or even larger than defined
513 * struct sockaddr_un length (see comment in unix/nxt_socket.h). So
514 * limit maximum Unix domain socket address length by defined sun_path[]
515 * length because some OSes accept addresses twice larger than defined
516 * struct sockaddr_un. Also reserve space for a trailing zero to avoid
517 * ambiguity, since many OSes accept Unix domain socket addresses
518 * without a trailing zero.
519 */
520 const size_t max_len = sizeof(struct sockaddr_un)
521 - offsetof(struct sockaddr_un, sun_path) - 1;
522
523 /* cutting "unix:" */
508 u_char *path;
509 nxt_mem_pool_t *mp;
510 nxt_sockaddr_t *sa;
511
512 /*
513 * Actual sockaddr_un length can be lesser or even larger than defined
514 * struct sockaddr_un length (see comment in unix/nxt_socket.h). So
515 * limit maximum Unix domain socket address length by defined sun_path[]
516 * length because some OSes accept addresses twice larger than defined
517 * struct sockaddr_un. Also reserve space for a trailing zero to avoid
518 * ambiguity, since many OSes accept Unix domain socket addresses
519 * without a trailing zero.
520 */
521 const size_t max_len = sizeof(struct sockaddr_un)
522 - offsetof(struct sockaddr_un, sun_path) - 1;
523
524 /* cutting "unix:" */
524 len = jbs->addr.len - 5;
525 path = jbs->addr.data + 5;
525 length = jbs->addr.length - 5;
526 path = jbs->addr.start + 5;
526
527
527 if (len > max_len) {
528 if (length > max_len) {
528 nxt_thread_log_error(jbs->resolve.log_level,
529 "unix domain socket \"%V\" name is too long",
530 &jbs->addr);
531 return NXT_ERROR;
532 }
533
529 nxt_thread_log_error(jbs->resolve.log_level,
530 "unix domain socket \"%V\" name is too long",
531 &jbs->addr);
532 return NXT_ERROR;
533 }
534
534 socklen = offsetof(struct sockaddr_un, sun_path) + len + 1;
535 socklen = offsetof(struct sockaddr_un, sun_path) + length + 1;
535
536#if (NXT_LINUX)
537
538 /*
539 * Linux unix(7):
540 *
541 * abstract: an abstract socket address is distinguished by the fact
542 * that sun_path[0] is a null byte ('\0'). The socket's address in
543 * this namespace is given by the additional bytes in sun_path that
544 * are covered by the specified length of the address structure.
545 * (Null bytes in the name have no special significance.)
546 */
547 if (path[0] == '\0') {
548 socklen--;
549 }
550
551#endif
552
553 mp = jbs->resolve.job.mem_pool;
554
555 jbs->resolve.sockaddrs = nxt_mem_alloc(mp, sizeof(void *));
556
557 if (nxt_fast_path(jbs->resolve.sockaddrs != NULL)) {
558 sa = nxt_sockaddr_alloc(mp, socklen);
559
560 if (nxt_fast_path(sa != NULL)) {
561 jbs->resolve.count = 1;
562 jbs->resolve.sockaddrs[0] = sa;
563
564 sa->u.sockaddr_un.sun_family = AF_UNIX;
536
537#if (NXT_LINUX)
538
539 /*
540 * Linux unix(7):
541 *
542 * abstract: an abstract socket address is distinguished by the fact
543 * that sun_path[0] is a null byte ('\0'). The socket's address in
544 * this namespace is given by the additional bytes in sun_path that
545 * are covered by the specified length of the address structure.
546 * (Null bytes in the name have no special significance.)
547 */
548 if (path[0] == '\0') {
549 socklen--;
550 }
551
552#endif
553
554 mp = jbs->resolve.job.mem_pool;
555
556 jbs->resolve.sockaddrs = nxt_mem_alloc(mp, sizeof(void *));
557
558 if (nxt_fast_path(jbs->resolve.sockaddrs != NULL)) {
559 sa = nxt_sockaddr_alloc(mp, socklen);
560
561 if (nxt_fast_path(sa != NULL)) {
562 jbs->resolve.count = 1;
563 jbs->resolve.sockaddrs[0] = sa;
564
565 sa->u.sockaddr_un.sun_family = AF_UNIX;
565 nxt_memcpy(sa->u.sockaddr_un.sun_path, path, len);
566 nxt_memcpy(sa->u.sockaddr_un.sun_path, path, length);
566
567 return NXT_OK;
568 }
569 }
570
571 return NXT_ERROR;
572
573#else /* !(NXT_HAVE_UNIX_DOMAIN) */
574
575 nxt_thread_log_error(jbs->resolve.log_level,
576 "unix domain socket \"%V\" is not supported",
577 &jbs->addr);
578 return NXT_ERROR;
579
580#endif
581}
582
583
584static nxt_int_t
585nxt_job_sockaddr_inet6_parse(nxt_job_sockaddr_parse_t *jbs)
586{
587#if (NXT_INET6)
588 u_char *p, *addr, *addr_end;
567
568 return NXT_OK;
569 }
570 }
571
572 return NXT_ERROR;
573
574#else /* !(NXT_HAVE_UNIX_DOMAIN) */
575
576 nxt_thread_log_error(jbs->resolve.log_level,
577 "unix domain socket \"%V\" is not supported",
578 &jbs->addr);
579 return NXT_ERROR;
580
581#endif
582}
583
584
585static nxt_int_t
586nxt_job_sockaddr_inet6_parse(nxt_job_sockaddr_parse_t *jbs)
587{
588#if (NXT_INET6)
589 u_char *p, *addr, *addr_end;
589 size_t len;
590 size_t length;
590 nxt_int_t port;
591 nxt_mem_pool_t *mp;
592 nxt_sockaddr_t *sa;
593 struct in6_addr *in6_addr;
594
591 nxt_int_t port;
592 nxt_mem_pool_t *mp;
593 nxt_sockaddr_t *sa;
594 struct in6_addr *in6_addr;
595
595 len = jbs->addr.len - 1;
596 addr = jbs->addr.data + 1;
596 length = jbs->addr.length - 1;
597 addr = jbs->addr.start + 1;
597
598
598 addr_end = nxt_memchr(addr, ']', len);
599 addr_end = nxt_memchr(addr, ']', length);
599
600 if (addr_end == NULL) {
601 goto invalid_address;
602 }
603
604 mp = jbs->resolve.job.mem_pool;
605
606 jbs->resolve.sockaddrs = nxt_mem_alloc(mp, sizeof(void *));
607
608 if (nxt_slow_path(jbs->resolve.sockaddrs == NULL)) {
609 return NXT_ERROR;
610 }
611
612 sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6));
613
614 if (nxt_slow_path(sa == NULL)) {
615 return NXT_ERROR;
616 }
617
618 jbs->resolve.count = 1;
619 jbs->resolve.sockaddrs[0] = sa;
620
621 in6_addr = &sa->u.sockaddr_in6.sin6_addr;
622
623 if (nxt_inet6_addr(in6_addr, addr, addr_end - addr) != NXT_OK) {
624 goto invalid_address;
625 }
626
627 p = addr_end + 1;
600
601 if (addr_end == NULL) {
602 goto invalid_address;
603 }
604
605 mp = jbs->resolve.job.mem_pool;
606
607 jbs->resolve.sockaddrs = nxt_mem_alloc(mp, sizeof(void *));
608
609 if (nxt_slow_path(jbs->resolve.sockaddrs == NULL)) {
610 return NXT_ERROR;
611 }
612
613 sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6));
614
615 if (nxt_slow_path(sa == NULL)) {
616 return NXT_ERROR;
617 }
618
619 jbs->resolve.count = 1;
620 jbs->resolve.sockaddrs[0] = sa;
621
622 in6_addr = &sa->u.sockaddr_in6.sin6_addr;
623
624 if (nxt_inet6_addr(in6_addr, addr, addr_end - addr) != NXT_OK) {
625 goto invalid_address;
626 }
627
628 p = addr_end + 1;
628 len = (addr + len) - p;
629 length = (addr + length) - p;
629
630
630 if (len == 0) {
631 if (length == 0) {
631 jbs->no_port = 1;
632 port = jbs->resolve.port;
633 goto found;
634 }
635
636 if (*p == ':') {
632 jbs->no_port = 1;
633 port = jbs->resolve.port;
634 goto found;
635 }
636
637 if (*p == ':') {
637 port = nxt_int_parse(p + 1, len - 1);
638 port = nxt_int_parse(p + 1, length - 1);
638
639 if (port >= 1 && port <= 65535) {
640 port = htons((in_port_t) port);
641 goto found;
642 }
643 }
644
645 nxt_thread_log_error(jbs->resolve.log_level,
646 "invalid port in \"%V\"", &jbs->addr);
647
648 return NXT_ERROR;
649
650found:
651
652 sa->u.sockaddr_in6.sin6_family = AF_INET6;
653 sa->u.sockaddr_in6.sin6_port = (in_port_t) port;
654
655 if (IN6_IS_ADDR_UNSPECIFIED(in6_addr)) {
656 jbs->wildcard = 1;
657 }
658
659 return NXT_OK;
660
661invalid_address:
662
663 nxt_thread_log_error(jbs->resolve.log_level,
664 "invalid IPv6 address in \"%V\"", &jbs->addr);
665 return NXT_ERROR;
666
667#else
668
669 nxt_thread_log_error(jbs->resolve.log_level,
670 "IPv6 socket \"%V\" is not supported", &jbs->addr);
671 return NXT_ERROR;
672
673#endif
674}
675
676
677static nxt_int_t
678nxt_job_sockaddr_inet_parse(nxt_job_sockaddr_parse_t *jbs)
679{
680 u_char *p, *host;
639
640 if (port >= 1 && port <= 65535) {
641 port = htons((in_port_t) port);
642 goto found;
643 }
644 }
645
646 nxt_thread_log_error(jbs->resolve.log_level,
647 "invalid port in \"%V\"", &jbs->addr);
648
649 return NXT_ERROR;
650
651found:
652
653 sa->u.sockaddr_in6.sin6_family = AF_INET6;
654 sa->u.sockaddr_in6.sin6_port = (in_port_t) port;
655
656 if (IN6_IS_ADDR_UNSPECIFIED(in6_addr)) {
657 jbs->wildcard = 1;
658 }
659
660 return NXT_OK;
661
662invalid_address:
663
664 nxt_thread_log_error(jbs->resolve.log_level,
665 "invalid IPv6 address in \"%V\"", &jbs->addr);
666 return NXT_ERROR;
667
668#else
669
670 nxt_thread_log_error(jbs->resolve.log_level,
671 "IPv6 socket \"%V\" is not supported", &jbs->addr);
672 return NXT_ERROR;
673
674#endif
675}
676
677
678static nxt_int_t
679nxt_job_sockaddr_inet_parse(nxt_job_sockaddr_parse_t *jbs)
680{
681 u_char *p, *host;
681 size_t len;
682 size_t length;
682 in_addr_t addr;
683 nxt_int_t port;
684 nxt_mem_pool_t *mp;
685 nxt_sockaddr_t *sa;
686
687 addr = INADDR_ANY;
688
683 in_addr_t addr;
684 nxt_int_t port;
685 nxt_mem_pool_t *mp;
686 nxt_sockaddr_t *sa;
687
688 addr = INADDR_ANY;
689
689 len = jbs->addr.len;
690 host = jbs->addr.data;
690 length = jbs->addr.length;
691 host = jbs->addr.start;
691
692
692 p = nxt_memchr(host, ':', len);
693 p = nxt_memchr(host, ':', length);
693
694 if (p == NULL) {
695
696 /* single value port, address, or host name */
697
694
695 if (p == NULL) {
696
697 /* single value port, address, or host name */
698
698 port = nxt_int_parse(host, len);
699 port = nxt_int_parse(host, length);
699
700 if (port > 0) {
701 if (port < 1 || port > 65535) {
702 goto invalid_port;
703 }
704
705 /* "*:XX" */
706 port = htons((in_port_t) port);
707 jbs->resolve.port = (in_port_t) port;
708
709 } else {
710 jbs->no_port = 1;
711
700
701 if (port > 0) {
702 if (port < 1 || port > 65535) {
703 goto invalid_port;
704 }
705
706 /* "*:XX" */
707 port = htons((in_port_t) port);
708 jbs->resolve.port = (in_port_t) port;
709
710 } else {
711 jbs->no_port = 1;
712
712 addr = nxt_inet_addr(host, len);
713 addr = nxt_inet_addr(host, length);
713
714 if (addr == INADDR_NONE) {
714
715 if (addr == INADDR_NONE) {
715 jbs->resolve.name.len = len;
716 jbs->resolve.name.data = host;
716 jbs->resolve.name.length = length;
717 jbs->resolve.name.start = host;
717
718 nxt_job_resolve(&jbs->resolve);
719 return NXT_AGAIN;
720 }
721
722 /* "x.x.x.x" */
723 port = jbs->resolve.port;
724 }
725
726 } else {
727
728 /* x.x.x.x:XX or host:XX */
729
730 p++;
718
719 nxt_job_resolve(&jbs->resolve);
720 return NXT_AGAIN;
721 }
722
723 /* "x.x.x.x" */
724 port = jbs->resolve.port;
725 }
726
727 } else {
728
729 /* x.x.x.x:XX or host:XX */
730
731 p++;
731 len = (host + len) - p;
732 port = nxt_int_parse(p, len);
732 length = (host + length) - p;
733 port = nxt_int_parse(p, length);
733
734 if (port < 1 || port > 65535) {
735 goto invalid_port;
736 }
737
738 port = htons((in_port_t) port);
739
734
735 if (port < 1 || port > 65535) {
736 goto invalid_port;
737 }
738
739 port = htons((in_port_t) port);
740
740 len = (p - 1) - host;
741 length = (p - 1) - host;
741
742
742 if (len != 1 || host[0] != '*') {
743 addr = nxt_inet_addr(host, len);
743 if (length != 1 || host[0] != '*') {
744 addr = nxt_inet_addr(host, length);
744
745 if (addr == INADDR_NONE) {
745
746 if (addr == INADDR_NONE) {
746 jbs->resolve.name.len = len;
747 jbs->resolve.name.data = host;
747 jbs->resolve.name.length = length;
748 jbs->resolve.name.start = host;
748 jbs->resolve.port = (in_port_t) port;
749
750 nxt_job_resolve(&jbs->resolve);
751 return NXT_AGAIN;
752 }
753
754 /* "x.x.x.x:XX" */
755 }
756 }
757
758 mp = jbs->resolve.job.mem_pool;
759
760 jbs->resolve.sockaddrs = nxt_mem_alloc(mp, sizeof(void *));
761 if (nxt_slow_path(jbs->resolve.sockaddrs == NULL)) {
762 return NXT_ERROR;
763 }
764
765 sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in));
766
767 if (nxt_fast_path(sa != NULL)) {
768 jbs->resolve.count = 1;
769 jbs->resolve.sockaddrs[0] = sa;
770
771 jbs->wildcard = (addr == INADDR_ANY);
772
773 sa->u.sockaddr_in.sin_family = AF_INET;
774 sa->u.sockaddr_in.sin_port = (in_port_t) port;
775 sa->u.sockaddr_in.sin_addr.s_addr = addr;
776
777 return NXT_OK;
778 }
779
780 return NXT_ERROR;
781
782invalid_port:
783
784 nxt_thread_log_error(jbs->resolve.log_level,
785 "invalid port in \"%V\"", &jbs->addr);
786
787 return NXT_ERROR;
788}
789
790
791in_addr_t
749 jbs->resolve.port = (in_port_t) port;
750
751 nxt_job_resolve(&jbs->resolve);
752 return NXT_AGAIN;
753 }
754
755 /* "x.x.x.x:XX" */
756 }
757 }
758
759 mp = jbs->resolve.job.mem_pool;
760
761 jbs->resolve.sockaddrs = nxt_mem_alloc(mp, sizeof(void *));
762 if (nxt_slow_path(jbs->resolve.sockaddrs == NULL)) {
763 return NXT_ERROR;
764 }
765
766 sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in));
767
768 if (nxt_fast_path(sa != NULL)) {
769 jbs->resolve.count = 1;
770 jbs->resolve.sockaddrs[0] = sa;
771
772 jbs->wildcard = (addr == INADDR_ANY);
773
774 sa->u.sockaddr_in.sin_family = AF_INET;
775 sa->u.sockaddr_in.sin_port = (in_port_t) port;
776 sa->u.sockaddr_in.sin_addr.s_addr = addr;
777
778 return NXT_OK;
779 }
780
781 return NXT_ERROR;
782
783invalid_port:
784
785 nxt_thread_log_error(jbs->resolve.log_level,
786 "invalid port in \"%V\"", &jbs->addr);
787
788 return NXT_ERROR;
789}
790
791
792in_addr_t
792nxt_inet_addr(u_char *buf, size_t len)
793nxt_inet_addr(u_char *buf, size_t length)
793{
794 u_char c, *end;
795 in_addr_t addr;
796 nxt_uint_t digit, octet, dots;
797
798 addr = 0;
799 octet = 0;
800 dots = 0;
801
794{
795 u_char c, *end;
796 in_addr_t addr;
797 nxt_uint_t digit, octet, dots;
798
799 addr = 0;
800 octet = 0;
801 dots = 0;
802
802 end = buf + len;
803 end = buf + length;
803
804 while (buf < end) {
805
806 c = *buf++;
807
808 digit = c - '0';
809 /* values below '0' become large unsigned integers */
810
811 if (digit < 10) {
812 octet = octet * 10 + digit;
813 continue;
814 }
815
816 if (c == '.' && octet < 256) {
817 addr = (addr << 8) + octet;
818 octet = 0;
819 dots++;
820 continue;
821 }
822
823 return INADDR_NONE;
824 }
825
826 if (dots == 3 && octet < 256) {
827 addr = (addr << 8) + octet;
828 return htonl(addr);
829 }
830
831 return INADDR_NONE;
832}
833
834
835#if (NXT_INET6)
836
837nxt_int_t
804
805 while (buf < end) {
806
807 c = *buf++;
808
809 digit = c - '0';
810 /* values below '0' become large unsigned integers */
811
812 if (digit < 10) {
813 octet = octet * 10 + digit;
814 continue;
815 }
816
817 if (c == '.' && octet < 256) {
818 addr = (addr << 8) + octet;
819 octet = 0;
820 dots++;
821 continue;
822 }
823
824 return INADDR_NONE;
825 }
826
827 if (dots == 3 && octet < 256) {
828 addr = (addr << 8) + octet;
829 return htonl(addr);
830 }
831
832 return INADDR_NONE;
833}
834
835
836#if (NXT_INET6)
837
838nxt_int_t
838nxt_inet6_addr(struct in6_addr *in6_addr, u_char *buf, size_t len)
839nxt_inet6_addr(struct in6_addr *in6_addr, u_char *buf, size_t length)
839{
840 u_char c, *addr, *zero_start, *ipv4, *dst, *src, *end;
841 nxt_uint_t digit, group, nibbles, groups_left;
842
840{
841 u_char c, *addr, *zero_start, *ipv4, *dst, *src, *end;
842 nxt_uint_t digit, group, nibbles, groups_left;
843
843 if (len == 0) {
844 if (length == 0) {
844 return NXT_ERROR;
845 }
846
845 return NXT_ERROR;
846 }
847
847 end = buf + len;
848 end = buf + length;
848
849 if (buf[0] == ':') {
850 buf++;
851 }
852
853 addr = in6_addr->s6_addr;
854 zero_start = NULL;
855 groups_left = 8;
856 nibbles = 0;
857 group = 0;
858 ipv4 = NULL;
859
860 while (buf < end) {
861 c = *buf++;
862
863 if (c == ':') {
864 if (nibbles != 0) {
865 ipv4 = buf;
866
867 *addr++ = (u_char) (group >> 8);
868 *addr++ = (u_char) (group & 0xff);
869 groups_left--;
870
871 if (groups_left != 0) {
872 nibbles = 0;
873 group = 0;
874 continue;
875 }
876
877 } else {
878 if (zero_start == NULL) {
879 ipv4 = buf;
880 zero_start = addr;
881 continue;
882 }
883 }
884
885 return NXT_ERROR;
886 }
887
888 if (c == '.' && nibbles != 0) {
889
890 if (groups_left < 2 || ipv4 == NULL) {
891 return NXT_ERROR;
892 }
893
894 group = nxt_inet_addr(ipv4, end - ipv4);
895 if (group == INADDR_NONE) {
896 return NXT_ERROR;
897 }
898
899 group = ntohl(group);
900
901 *addr++ = (u_char) ((group >> 24) & 0xff);
902 *addr++ = (u_char) ((group >> 16) & 0xff);
903 groups_left--;
904
905 /* the low 16-bit are copied below */
906 break;
907 }
908
909 nibbles++;
910
911 if (nibbles > 4) {
912 return NXT_ERROR;
913 }
914
915 group <<= 4;
916
917 digit = c - '0';
918 /* values below '0' become large unsigned integers */
919
920 if (digit < 10) {
921 group += digit;
922 continue;
923 }
924
925 c |= 0x20;
926 digit = c - 'a';
927 /* values below 'a' become large unsigned integers */
928
929 if (digit < 6) {
930 group += 10 + digit;
931 continue;
932 }
933
934 return NXT_ERROR;
935 }
936
937 if (nibbles == 0 && zero_start == NULL) {
938 return NXT_ERROR;
939 }
940
941 *addr++ = (u_char) (group >> 8);
942 *addr++ = (u_char) (group & 0xff);
943 groups_left--;
944
945 if (groups_left != 0) {
946
947 if (zero_start != NULL) {
948
949 /* moving part before consecutive zero groups to the end */
950
951 groups_left *= 2;
952 src = addr - 1;
953 dst = src + groups_left;
954
955 while (src >= zero_start) {
956 *dst-- = *src--;
957 }
958
959 nxt_memzero(zero_start, groups_left);
960
961 return NXT_OK;
962 }
963
964 } else {
965 if (zero_start == NULL) {
966 return NXT_OK;
967 }
968 }
969
970 return NXT_ERROR;
971}
972
973#endif
849
850 if (buf[0] == ':') {
851 buf++;
852 }
853
854 addr = in6_addr->s6_addr;
855 zero_start = NULL;
856 groups_left = 8;
857 nibbles = 0;
858 group = 0;
859 ipv4 = NULL;
860
861 while (buf < end) {
862 c = *buf++;
863
864 if (c == ':') {
865 if (nibbles != 0) {
866 ipv4 = buf;
867
868 *addr++ = (u_char) (group >> 8);
869 *addr++ = (u_char) (group & 0xff);
870 groups_left--;
871
872 if (groups_left != 0) {
873 nibbles = 0;
874 group = 0;
875 continue;
876 }
877
878 } else {
879 if (zero_start == NULL) {
880 ipv4 = buf;
881 zero_start = addr;
882 continue;
883 }
884 }
885
886 return NXT_ERROR;
887 }
888
889 if (c == '.' && nibbles != 0) {
890
891 if (groups_left < 2 || ipv4 == NULL) {
892 return NXT_ERROR;
893 }
894
895 group = nxt_inet_addr(ipv4, end - ipv4);
896 if (group == INADDR_NONE) {
897 return NXT_ERROR;
898 }
899
900 group = ntohl(group);
901
902 *addr++ = (u_char) ((group >> 24) & 0xff);
903 *addr++ = (u_char) ((group >> 16) & 0xff);
904 groups_left--;
905
906 /* the low 16-bit are copied below */
907 break;
908 }
909
910 nibbles++;
911
912 if (nibbles > 4) {
913 return NXT_ERROR;
914 }
915
916 group <<= 4;
917
918 digit = c - '0';
919 /* values below '0' become large unsigned integers */
920
921 if (digit < 10) {
922 group += digit;
923 continue;
924 }
925
926 c |= 0x20;
927 digit = c - 'a';
928 /* values below 'a' become large unsigned integers */
929
930 if (digit < 6) {
931 group += 10 + digit;
932 continue;
933 }
934
935 return NXT_ERROR;
936 }
937
938 if (nibbles == 0 && zero_start == NULL) {
939 return NXT_ERROR;
940 }
941
942 *addr++ = (u_char) (group >> 8);
943 *addr++ = (u_char) (group & 0xff);
944 groups_left--;
945
946 if (groups_left != 0) {
947
948 if (zero_start != NULL) {
949
950 /* moving part before consecutive zero groups to the end */
951
952 groups_left *= 2;
953 src = addr - 1;
954 dst = src + groups_left;
955
956 while (src >= zero_start) {
957 *dst-- = *src--;
958 }
959
960 nxt_memzero(zero_start, groups_left);
961
962 return NXT_OK;
963 }
964
965 } else {
966 if (zero_start == NULL) {
967 return NXT_OK;
968 }
969 }
970
971 return NXT_ERROR;
972}
973
974#endif