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