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