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