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(nxt_thread(), &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