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