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