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