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