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