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