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