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