xref: /unit/src/nxt_sockaddr.c (revision 10:a8e68ed06863)
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 length)
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) + length);
32 
33     if (nxt_fast_path(sa != NULL)) {
34         nxt_socklen_set(sa, length);
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 length)
44 {
45     size_t          size, copy;
46     nxt_sockaddr_t  *sa;
47 
48     size = length;
49     copy = length;
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 (length == 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          length;
128     nxt_sockaddr_t  *dst;
129 
130     length = offsetof(nxt_sockaddr_t, u) + nxt_socklen(src);
131 
132     dst = nxt_mem_alloc(mp, length);
133 
134     if (nxt_fast_path(dst != NULL)) {
135         nxt_memcpy(dst, src, length);
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  length;
168     u_char  *p;
169     u_char  buf[NXT_SOCKADDR_STR_LEN + NXT_SOCKPORT_STR_LEN];
170 
171     length = NXT_SOCKADDR_STR_LEN + NXT_SOCKPORT_STR_LEN;
172 
173     length = nxt_sockaddr_ntop(sa, buf, buf + length, port);
174 
175     p = nxt_mem_alloc(mp, length);
176 
177     if (nxt_fast_path(p != NULL)) {
178 
179         sa->text = p;
180         sa->text_len = length;
181         nxt_memcpy(p, buf, length);
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  length;
266 
267             length = 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, length)
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  length;
347 
348             /* Linux abstract socket address has no trailing zero. */
349 
350             length = nxt_socklen(sa)
351                      - offsetof(struct sockaddr_un, sun_path) - 1;
352             p = nxt_sprintf(buf, end, "unix:\\0%*s", length, p + 1);
353 
354         } else {
355             p = nxt_sprintf(buf, end, "unix:%s", p);
356         }
357 
358 #else  /* !(NXT_LINUX) */
359 
360         p = nxt_sprintf(buf, end, "unix:%s", sa->u.sockaddr_un.sun_path);
361 
362 #endif
363 
364         return p - buf;
365 
366 #endif  /* NXT_HAVE_UNIX_DOMAIN */
367 
368     default:
369         return 0;
370     }
371 }
372 
373 
374 #if (NXT_INET6)
375 
376 static u_char *
377 nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end)
378 {
379     u_char      *p;
380     size_t      zero_groups, last_zero_groups, ipv6_bytes;
381     nxt_uint_t  i, zero_start, last_zero_start;
382 
383     if (buf + NXT_INET6_ADDR_STR_LEN > end) {
384         return buf;
385     }
386 
387     zero_start = 8;
388     zero_groups = 0;
389     last_zero_start = 8;
390     last_zero_groups = 0;
391 
392     for (i = 0; i < 16; i += 2) {
393 
394         if (addr[i] == 0 && addr[i + 1] == 0) {
395 
396             if (last_zero_groups == 0) {
397                 last_zero_start = i;
398             }
399 
400             last_zero_groups++;
401 
402         } else {
403             if (zero_groups < last_zero_groups) {
404                 zero_groups = last_zero_groups;
405                 zero_start = last_zero_start;
406             }
407 
408             last_zero_groups = 0;
409         }
410     }
411 
412     if (zero_groups < last_zero_groups) {
413         zero_groups = last_zero_groups;
414         zero_start = last_zero_start;
415     }
416 
417     ipv6_bytes = 16;
418     p = buf;
419 
420     if (zero_start == 0) {
421 
422                /* IPv4-mapped address */
423         if ((zero_groups == 5 && addr[10] == 0xff && addr[11] == 0xff)
424                /* IPv4-compatible address */
425             || (zero_groups == 6)
426                /* not IPv6 loopback address */
427             || (zero_groups == 7 && addr[14] != 0 && addr[15] != 1))
428         {
429             ipv6_bytes = 12;
430         }
431 
432         *p++ = ':';
433     }
434 
435     for (i = 0; i < ipv6_bytes; i += 2) {
436 
437         if (i == zero_start) {
438             /* Output maximum number of consecutive zero groups as "::". */
439             i += (zero_groups - 1) * 2;
440             *p++ = ':';
441             continue;
442         }
443 
444         p = nxt_sprintf(p, end, "%uxd", (addr[i] << 8) + addr[i + 1]);
445 
446         if (i < 14) {
447             *p++ = ':';
448         }
449     }
450 
451     if (ipv6_bytes == 12) {
452         p = nxt_sprintf(p, end, "%ud.%ud.%ud.%ud",
453                         addr[12], addr[13], addr[14], addr[15]);
454     }
455 
456     return p;
457 }
458 
459 #endif
460 
461 
462 void
463 nxt_job_sockaddr_parse(nxt_job_sockaddr_parse_t *jbs)
464 {
465     u_char              *p;
466     size_t              length;
467     nxt_int_t           ret;
468     nxt_work_handler_t  handler;
469 
470     nxt_job_set_name(&jbs->resolve.job, "job sockaddr parse");
471 
472     length = jbs->addr.length;
473     p = jbs->addr.start;
474 
475     if (length > 6 && nxt_memcmp(p, (u_char *) "unix:", 5) == 0) {
476         ret = nxt_job_sockaddr_unix_parse(jbs);
477 
478     } else if (length != 0 && *p == '[') {
479         ret = nxt_job_sockaddr_inet6_parse(jbs);
480 
481     } else {
482         ret = nxt_job_sockaddr_inet_parse(jbs);
483     }
484 
485     switch (ret) {
486 
487     case NXT_OK:
488         handler = jbs->resolve.ready_handler;
489         break;
490 
491     case NXT_ERROR:
492         handler = jbs->resolve.error_handler;
493         break;
494 
495     default: /* NXT_AGAIN */
496         return;
497     }
498 
499     nxt_job_return(jbs->resolve.job.task, &jbs->resolve.job, handler);
500 }
501 
502 
503 static nxt_int_t
504 nxt_job_sockaddr_unix_parse(nxt_job_sockaddr_parse_t *jbs)
505 {
506 #if (NXT_HAVE_UNIX_DOMAIN)
507     size_t          length, socklen;
508     u_char          *path;
509     nxt_mem_pool_t  *mp;
510     nxt_sockaddr_t  *sa;
511 
512     /*
513      * Actual sockaddr_un length can be lesser or even larger than defined
514      * struct sockaddr_un length (see comment in unix/nxt_socket.h).  So
515      * limit maximum Unix domain socket address length by defined sun_path[]
516      * length because some OSes accept addresses twice larger than defined
517      * struct sockaddr_un.  Also reserve space for a trailing zero to avoid
518      * ambiguity, since many OSes accept Unix domain socket addresses
519      * without a trailing zero.
520      */
521     const size_t max_len = sizeof(struct sockaddr_un)
522                            - offsetof(struct sockaddr_un, sun_path) - 1;
523 
524     /* cutting "unix:" */
525     length = jbs->addr.length - 5;
526     path = jbs->addr.start + 5;
527 
528     if (length > max_len) {
529         nxt_thread_log_error(jbs->resolve.log_level,
530                              "unix domain socket \"%V\" name is too long",
531                              &jbs->addr);
532         return NXT_ERROR;
533     }
534 
535     socklen = offsetof(struct sockaddr_un, sun_path) + length + 1;
536 
537 #if (NXT_LINUX)
538 
539     /*
540      * Linux unix(7):
541      *
542      *   abstract: an abstract socket address is distinguished by the fact
543      *   that sun_path[0] is a null byte ('\0').  The socket's address in
544      *   this namespace is given by the additional bytes in sun_path that
545      *   are covered by the specified length of the address structure.
546      *   (Null bytes in the name have no special significance.)
547      */
548     if (path[0] == '\0') {
549         socklen--;
550     }
551 
552 #endif
553 
554     mp = jbs->resolve.job.mem_pool;
555 
556     jbs->resolve.sockaddrs = nxt_mem_alloc(mp, sizeof(void *));
557 
558     if (nxt_fast_path(jbs->resolve.sockaddrs != NULL)) {
559         sa = nxt_sockaddr_alloc(mp, socklen);
560 
561         if (nxt_fast_path(sa != NULL)) {
562             jbs->resolve.count = 1;
563             jbs->resolve.sockaddrs[0] = sa;
564 
565             sa->u.sockaddr_un.sun_family = AF_UNIX;
566             nxt_memcpy(sa->u.sockaddr_un.sun_path, path, length);
567 
568             return NXT_OK;
569         }
570     }
571 
572     return NXT_ERROR;
573 
574 #else  /* !(NXT_HAVE_UNIX_DOMAIN) */
575 
576     nxt_thread_log_error(jbs->resolve.log_level,
577                          "unix domain socket \"%V\" is not supported",
578                          &jbs->addr);
579     return NXT_ERROR;
580 
581 #endif
582 }
583 
584 
585 static nxt_int_t
586 nxt_job_sockaddr_inet6_parse(nxt_job_sockaddr_parse_t *jbs)
587 {
588 #if (NXT_INET6)
589     u_char           *p, *addr, *addr_end;
590     size_t           length;
591     nxt_int_t        port;
592     nxt_mem_pool_t   *mp;
593     nxt_sockaddr_t   *sa;
594     struct in6_addr  *in6_addr;
595 
596     length = jbs->addr.length - 1;
597     addr = jbs->addr.start + 1;
598 
599     addr_end = nxt_memchr(addr, ']', length);
600 
601     if (addr_end == NULL) {
602         goto invalid_address;
603     }
604 
605     mp = jbs->resolve.job.mem_pool;
606 
607     jbs->resolve.sockaddrs = nxt_mem_alloc(mp, sizeof(void *));
608 
609     if (nxt_slow_path(jbs->resolve.sockaddrs == NULL)) {
610         return NXT_ERROR;
611     }
612 
613     sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6));
614 
615     if (nxt_slow_path(sa == NULL)) {
616         return NXT_ERROR;
617     }
618 
619     jbs->resolve.count = 1;
620     jbs->resolve.sockaddrs[0] = sa;
621 
622     in6_addr = &sa->u.sockaddr_in6.sin6_addr;
623 
624     if (nxt_inet6_addr(in6_addr, addr, addr_end - addr) != NXT_OK) {
625         goto invalid_address;
626     }
627 
628     p = addr_end + 1;
629     length = (addr + length) - p;
630 
631     if (length == 0) {
632         jbs->no_port = 1;
633         port = jbs->resolve.port;
634         goto found;
635     }
636 
637     if (*p == ':') {
638         port = nxt_int_parse(p + 1, length - 1);
639 
640         if (port >= 1 && port <= 65535) {
641             port = htons((in_port_t) port);
642             goto found;
643         }
644     }
645 
646     nxt_thread_log_error(jbs->resolve.log_level,
647                          "invalid port in \"%V\"", &jbs->addr);
648 
649     return NXT_ERROR;
650 
651 found:
652 
653     sa->u.sockaddr_in6.sin6_family = AF_INET6;
654     sa->u.sockaddr_in6.sin6_port = (in_port_t) port;
655 
656     if (IN6_IS_ADDR_UNSPECIFIED(in6_addr)) {
657         jbs->wildcard = 1;
658     }
659 
660     return NXT_OK;
661 
662 invalid_address:
663 
664     nxt_thread_log_error(jbs->resolve.log_level,
665                          "invalid IPv6 address in \"%V\"", &jbs->addr);
666     return NXT_ERROR;
667 
668 #else
669 
670     nxt_thread_log_error(jbs->resolve.log_level,
671                          "IPv6 socket \"%V\" is not supported", &jbs->addr);
672     return NXT_ERROR;
673 
674 #endif
675 }
676 
677 
678 static nxt_int_t
679 nxt_job_sockaddr_inet_parse(nxt_job_sockaddr_parse_t *jbs)
680 {
681     u_char          *p, *host;
682     size_t          length;
683     in_addr_t       addr;
684     nxt_int_t       port;
685     nxt_mem_pool_t  *mp;
686     nxt_sockaddr_t  *sa;
687 
688     addr = INADDR_ANY;
689 
690     length = jbs->addr.length;
691     host = jbs->addr.start;
692 
693     p = nxt_memchr(host, ':', length);
694 
695     if (p == NULL) {
696 
697         /* single value port, address, or host name */
698 
699         port = nxt_int_parse(host, length);
700 
701         if (port > 0) {
702             if (port < 1 || port > 65535) {
703                 goto invalid_port;
704             }
705 
706             /* "*:XX" */
707             port = htons((in_port_t) port);
708             jbs->resolve.port = (in_port_t) port;
709 
710         } else {
711             jbs->no_port = 1;
712 
713             addr = nxt_inet_addr(host, length);
714 
715             if (addr == INADDR_NONE) {
716                 jbs->resolve.name.length = length;
717                 jbs->resolve.name.start = host;
718 
719                 nxt_job_resolve(&jbs->resolve);
720                 return NXT_AGAIN;
721             }
722 
723             /* "x.x.x.x" */
724             port = jbs->resolve.port;
725         }
726 
727     } else {
728 
729         /* x.x.x.x:XX or host:XX */
730 
731         p++;
732         length = (host + length) - p;
733         port = nxt_int_parse(p, length);
734 
735         if (port < 1 || port > 65535) {
736             goto invalid_port;
737         }
738 
739         port = htons((in_port_t) port);
740 
741         length = (p - 1) - host;
742 
743         if (length != 1 || host[0] != '*') {
744             addr = nxt_inet_addr(host, length);
745 
746             if (addr == INADDR_NONE) {
747                 jbs->resolve.name.length = length;
748                 jbs->resolve.name.start = host;
749                 jbs->resolve.port = (in_port_t) port;
750 
751                 nxt_job_resolve(&jbs->resolve);
752                 return NXT_AGAIN;
753             }
754 
755             /* "x.x.x.x:XX" */
756         }
757     }
758 
759     mp = jbs->resolve.job.mem_pool;
760 
761     jbs->resolve.sockaddrs = nxt_mem_alloc(mp, sizeof(void *));
762     if (nxt_slow_path(jbs->resolve.sockaddrs == NULL)) {
763         return NXT_ERROR;
764     }
765 
766     sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in));
767 
768     if (nxt_fast_path(sa != NULL)) {
769         jbs->resolve.count = 1;
770         jbs->resolve.sockaddrs[0] = sa;
771 
772         jbs->wildcard = (addr == INADDR_ANY);
773 
774         sa->u.sockaddr_in.sin_family = AF_INET;
775         sa->u.sockaddr_in.sin_port = (in_port_t) port;
776         sa->u.sockaddr_in.sin_addr.s_addr = addr;
777 
778         return NXT_OK;
779     }
780 
781     return NXT_ERROR;
782 
783 invalid_port:
784 
785     nxt_thread_log_error(jbs->resolve.log_level,
786                          "invalid port in \"%V\"", &jbs->addr);
787 
788     return NXT_ERROR;
789 }
790 
791 
792 in_addr_t
793 nxt_inet_addr(u_char *buf, size_t length)
794 {
795     u_char      c, *end;
796     in_addr_t   addr;
797     nxt_uint_t  digit, octet, dots;
798 
799     addr = 0;
800     octet = 0;
801     dots = 0;
802 
803     end = buf + length;
804 
805     while (buf < end) {
806 
807         c = *buf++;
808 
809         digit = c - '0';
810         /* values below '0' become large unsigned integers */
811 
812         if (digit < 10) {
813             octet = octet * 10 + digit;
814             continue;
815         }
816 
817         if (c == '.' && octet < 256) {
818             addr = (addr << 8) + octet;
819             octet = 0;
820             dots++;
821             continue;
822         }
823 
824         return INADDR_NONE;
825     }
826 
827     if (dots == 3 && octet < 256) {
828         addr = (addr << 8) + octet;
829         return htonl(addr);
830     }
831 
832     return INADDR_NONE;
833 }
834 
835 
836 #if (NXT_INET6)
837 
838 nxt_int_t
839 nxt_inet6_addr(struct in6_addr *in6_addr, u_char *buf, size_t length)
840 {
841     u_char      c, *addr, *zero_start, *ipv4, *dst, *src, *end;
842     nxt_uint_t  digit, group, nibbles, groups_left;
843 
844     if (length == 0) {
845         return NXT_ERROR;
846     }
847 
848     end = buf + length;
849 
850     if (buf[0] == ':') {
851         buf++;
852     }
853 
854     addr = in6_addr->s6_addr;
855     zero_start = NULL;
856     groups_left = 8;
857     nibbles = 0;
858     group = 0;
859     ipv4 = NULL;
860 
861     while (buf < end) {
862         c = *buf++;
863 
864         if (c == ':') {
865             if (nibbles != 0) {
866                 ipv4 = buf;
867 
868                 *addr++ = (u_char) (group >> 8);
869                 *addr++ = (u_char) (group & 0xff);
870                 groups_left--;
871 
872                 if (groups_left != 0) {
873                     nibbles = 0;
874                     group = 0;
875                     continue;
876                 }
877 
878             } else {
879                 if (zero_start == NULL) {
880                     ipv4 = buf;
881                     zero_start = addr;
882                     continue;
883                 }
884             }
885 
886             return NXT_ERROR;
887         }
888 
889         if (c == '.' && nibbles != 0) {
890 
891             if (groups_left < 2 || ipv4 == NULL) {
892                 return NXT_ERROR;
893             }
894 
895             group = nxt_inet_addr(ipv4, end - ipv4);
896             if (group == INADDR_NONE) {
897                 return NXT_ERROR;
898             }
899 
900             group = ntohl(group);
901 
902             *addr++ = (u_char) ((group >> 24) & 0xff);
903             *addr++ = (u_char) ((group >> 16) & 0xff);
904             groups_left--;
905 
906             /* the low 16-bit are copied below */
907             break;
908         }
909 
910         nibbles++;
911 
912         if (nibbles > 4) {
913             return NXT_ERROR;
914         }
915 
916         group <<= 4;
917 
918         digit = c - '0';
919         /* values below '0' become large unsigned integers */
920 
921         if (digit < 10) {
922             group += digit;
923             continue;
924         }
925 
926         c |= 0x20;
927         digit = c - 'a';
928         /* values below 'a' become large unsigned integers */
929 
930         if (digit < 6) {
931             group += 10 + digit;
932             continue;
933         }
934 
935         return NXT_ERROR;
936     }
937 
938     if (nibbles == 0 && zero_start == NULL) {
939         return NXT_ERROR;
940     }
941 
942     *addr++ = (u_char) (group >> 8);
943     *addr++ = (u_char) (group & 0xff);
944     groups_left--;
945 
946     if (groups_left != 0) {
947 
948         if (zero_start != NULL) {
949 
950             /* moving part before consecutive zero groups to the end */
951 
952             groups_left *= 2;
953             src = addr - 1;
954             dst = src + groups_left;
955 
956             while (src >= zero_start) {
957                 *dst-- = *src--;
958             }
959 
960             nxt_memzero(zero_start, groups_left);
961 
962             return NXT_OK;
963         }
964 
965     } else {
966         if (zero_start == NULL) {
967             return NXT_OK;
968         }
969     }
970 
971     return NXT_ERROR;
972 }
973 
974 #endif
975