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