1/* 2 * "$Id: http-addr.c 11693 2014-03-11 01:24:45Z msweet $" 3 * 4 * HTTP address routines for CUPS. 5 * 6 * Copyright 2007-2013 by Apple Inc. 7 * Copyright 1997-2006 by Easy Software Products, all rights reserved. 8 * 9 * These coded instructions, statements, and computer programs are the 10 * property of Apple Inc. and are protected by Federal copyright 11 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 12 * which should have been included with this file. If this file is 13 * file is missing or damaged, see the license at "http://www.cups.org/". 14 * 15 * This file is subject to the Apple OS-Developed Software exception. 16 */ 17 18/* 19 * Include necessary headers... 20 */ 21 22#include "cups-private.h" 23#ifdef HAVE_RESOLV_H 24# include <resolv.h> 25#endif /* HAVE_RESOLV_H */ 26#ifdef __APPLE__ 27# include <CoreFoundation/CoreFoundation.h> 28# include <SystemConfiguration/SystemConfiguration.h> 29#endif /* __APPLE__ */ 30 31 32/* 33 * 'httpAddrAny()' - Check for the "any" address. 34 * 35 * @since CUPS 1.2/OS X 10.5@ 36 */ 37 38int /* O - 1 if "any", 0 otherwise */ 39httpAddrAny(const http_addr_t *addr) /* I - Address to check */ 40{ 41 if (!addr) 42 return (0); 43 44#ifdef AF_INET6 45 if (addr->addr.sa_family == AF_INET6 && 46 IN6_IS_ADDR_UNSPECIFIED(&(addr->ipv6.sin6_addr))) 47 return (1); 48#endif /* AF_INET6 */ 49 50 if (addr->addr.sa_family == AF_INET && 51 ntohl(addr->ipv4.sin_addr.s_addr) == 0x00000000) 52 return (1); 53 54 return (0); 55} 56 57 58/* 59 * 'httpAddrEqual()' - Compare two addresses. 60 * 61 * @since CUPS 1.2/OS X 10.5@ 62 */ 63 64int /* O - 1 if equal, 0 if not */ 65httpAddrEqual(const http_addr_t *addr1, /* I - First address */ 66 const http_addr_t *addr2) /* I - Second address */ 67{ 68 if (!addr1 && !addr2) 69 return (1); 70 71 if (!addr1 || !addr2) 72 return (0); 73 74 if (addr1->addr.sa_family != addr2->addr.sa_family) 75 return (0); 76 77#ifdef AF_LOCAL 78 if (addr1->addr.sa_family == AF_LOCAL) 79 return (!strcmp(addr1->un.sun_path, addr2->un.sun_path)); 80#endif /* AF_LOCAL */ 81 82#ifdef AF_INET6 83 if (addr1->addr.sa_family == AF_INET6) 84 return (!memcmp(&(addr1->ipv6.sin6_addr), &(addr2->ipv6.sin6_addr), 16)); 85#endif /* AF_INET6 */ 86 87 return (addr1->ipv4.sin_addr.s_addr == addr2->ipv4.sin_addr.s_addr); 88} 89 90 91/* 92 * 'httpAddrLength()' - Return the length of the address in bytes. 93 * 94 * @since CUPS 1.2/OS X 10.5@ 95 */ 96 97int /* O - Length in bytes */ 98httpAddrLength(const http_addr_t *addr) /* I - Address */ 99{ 100 if (!addr) 101 return (0); 102 103#ifdef AF_INET6 104 if (addr->addr.sa_family == AF_INET6) 105 return (sizeof(addr->ipv6)); 106 else 107#endif /* AF_INET6 */ 108#ifdef AF_LOCAL 109 if (addr->addr.sa_family == AF_LOCAL) 110 return (offsetof(struct sockaddr_un, sun_path) + 111 strlen(addr->un.sun_path) + 1); 112 else 113#endif /* AF_LOCAL */ 114 if (addr->addr.sa_family == AF_INET) 115 return (sizeof(addr->ipv4)); 116 else 117 return (0); 118 119} 120 121 122/* 123 * 'httpAddrListen()' - Create a listening socket bound to the specified 124 * address and port. 125 * 126 * @since CUPS 1.7/OS X 10.9@ 127 */ 128 129int /* O - Socket or -1 on error */ 130httpAddrListen(http_addr_t *addr, /* I - Address to bind to */ 131 int port) /* I - Port number to bind to */ 132{ 133 int fd = -1, /* Socket */ 134 val; /* Socket value */ 135 136 137 /* 138 * Range check input... 139 */ 140 141 if (!addr || port <= 0) 142 return (-1); 143 144 if ((fd = socket(addr->addr.sa_family, SOCK_STREAM, 0)) < 0) 145 { 146 _cupsSetHTTPError(HTTP_STATUS_ERROR); 147 return (-1); 148 } 149 150 val = 1; 151 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, CUPS_SOCAST &val, sizeof(val)); 152 153#ifdef IPV6_V6ONLY 154 if (addr->addr.sa_family == AF_INET6) 155 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, CUPS_SOCAST &val, sizeof(val)); 156#endif /* IPV6_V6ONLY */ 157 158 _httpAddrSetPort(addr, port); 159 160 if (bind(fd, (struct sockaddr *)addr, httpAddrLength(addr))) 161 { 162 _cupsSetHTTPError(HTTP_STATUS_ERROR); 163 164 close(fd); 165 166 return (-1); 167 } 168 169 if (listen(fd, 5)) 170 { 171 _cupsSetHTTPError(HTTP_STATUS_ERROR); 172 173 close(fd); 174 175 return (-1); 176 } 177 178#ifdef SO_NOSIGPIPE 179 /* 180 * Disable SIGPIPE for this socket. 181 */ 182 183 val = 1; 184 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val)); 185#endif /* SO_NOSIGPIPE */ 186 187 return (fd); 188} 189 190 191/* 192 * 'httpAddrLocalhost()' - Check for the local loopback address. 193 * 194 * @since CUPS 1.2/OS X 10.5@ 195 */ 196 197int /* O - 1 if local host, 0 otherwise */ 198httpAddrLocalhost( 199 const http_addr_t *addr) /* I - Address to check */ 200{ 201 if (!addr) 202 return (1); 203 204#ifdef AF_INET6 205 if (addr->addr.sa_family == AF_INET6 && 206 IN6_IS_ADDR_LOOPBACK(&(addr->ipv6.sin6_addr))) 207 return (1); 208#endif /* AF_INET6 */ 209 210#ifdef AF_LOCAL 211 if (addr->addr.sa_family == AF_LOCAL) 212 return (1); 213#endif /* AF_LOCAL */ 214 215 if (addr->addr.sa_family == AF_INET && 216 (ntohl(addr->ipv4.sin_addr.s_addr) & 0xff000000) == 0x7f000000) 217 return (1); 218 219 return (0); 220} 221 222 223/* 224 * 'httpAddrLookup()' - Lookup the hostname associated with the address. 225 * 226 * @since CUPS 1.2/OS X 10.5@ 227 */ 228 229char * /* O - Host name */ 230httpAddrLookup( 231 const http_addr_t *addr, /* I - Address to lookup */ 232 char *name, /* I - Host name buffer */ 233 int namelen) /* I - Size of name buffer */ 234{ 235 _cups_globals_t *cg = _cupsGlobals(); 236 /* Global data */ 237 238 239 DEBUG_printf(("httpAddrLookup(addr=%p, name=%p, namelen=%d)", addr, name, 240 namelen)); 241 242 /* 243 * Range check input... 244 */ 245 246 if (!addr || !name || namelen <= 2) 247 { 248 if (name && namelen >= 1) 249 *name = '\0'; 250 251 return (NULL); 252 } 253 254#ifdef AF_LOCAL 255 if (addr->addr.sa_family == AF_LOCAL) 256 { 257 strlcpy(name, addr->un.sun_path, namelen); 258 return (name); 259 } 260#endif /* AF_LOCAL */ 261 262 /* 263 * Optimize lookups for localhost/loopback addresses... 264 */ 265 266 if (httpAddrLocalhost(addr)) 267 { 268 strlcpy(name, "localhost", namelen); 269 return (name); 270 } 271 272#ifdef HAVE_RES_INIT 273 /* 274 * STR #2920: Initialize resolver after failure in cups-polld 275 * 276 * If the previous lookup failed, re-initialize the resolver to prevent 277 * temporary network errors from persisting. This *should* be handled by 278 * the resolver libraries, but apparently the glibc folks do not agree. 279 * 280 * We set a flag at the end of this function if we encounter an error that 281 * requires reinitialization of the resolver functions. We then call 282 * res_init() if the flag is set on the next call here or in httpAddrLookup(). 283 */ 284 285 if (cg->need_res_init) 286 { 287 res_init(); 288 289 cg->need_res_init = 0; 290 } 291#endif /* HAVE_RES_INIT */ 292 293#ifdef HAVE_GETNAMEINFO 294 { 295 /* 296 * STR #2486: httpAddrLookup() fails when getnameinfo() returns EAI_AGAIN 297 * 298 * FWIW, I think this is really a bug in the implementation of 299 * getnameinfo(), but falling back on httpAddrString() is easy to 300 * do... 301 */ 302 303 int error = getnameinfo(&addr->addr, httpAddrLength(addr), name, namelen, 304 NULL, 0, 0); 305 306 if (error) 307 { 308 if (error == EAI_FAIL) 309 cg->need_res_init = 1; 310 311 return (httpAddrString(addr, name, namelen)); 312 } 313 } 314#else 315 { 316 struct hostent *host; /* Host from name service */ 317 318 319# ifdef AF_INET6 320 if (addr->addr.sa_family == AF_INET6) 321 host = gethostbyaddr((char *)&(addr->ipv6.sin6_addr), 322 sizeof(struct in_addr), AF_INET6); 323 else 324# endif /* AF_INET6 */ 325 host = gethostbyaddr((char *)&(addr->ipv4.sin_addr), 326 sizeof(struct in_addr), AF_INET); 327 328 if (host == NULL) 329 { 330 /* 331 * No hostname, so return the raw address... 332 */ 333 334 if (h_errno == NO_RECOVERY) 335 cg->need_res_init = 1; 336 337 return (httpAddrString(addr, name, namelen)); 338 } 339 340 strlcpy(name, host->h_name, namelen); 341 } 342#endif /* HAVE_GETNAMEINFO */ 343 344 DEBUG_printf(("1httpAddrLookup: returning \"%s\"...", name)); 345 346 return (name); 347} 348 349 350/* 351 * 'httpAddrPort()' - Get the port number associated with an address. 352 * 353 * @since CUPS 1.7/OS X 10.9@ 354 */ 355 356int /* O - Port number */ 357httpAddrPort(http_addr_t *addr) /* I - Address */ 358{ 359 if (!addr) 360 return (ippPort()); 361#ifdef AF_INET6 362 else if (addr->addr.sa_family == AF_INET6) 363 return (ntohs(addr->ipv6.sin6_port)); 364#endif /* AF_INET6 */ 365 else if (addr->addr.sa_family == AF_INET) 366 return (ntohs(addr->ipv4.sin_port)); 367 else 368 return (ippPort()); 369} 370 371/* For OS X 10.8 and earlier */ 372int _httpAddrPort(http_addr_t *addr) { return (httpAddrPort(addr)); } 373 374 375/* 376 * '_httpAddrSetPort()' - Set the port number associated with an address. 377 */ 378 379void 380_httpAddrSetPort(http_addr_t *addr, /* I - Address */ 381 int port) /* I - Port */ 382{ 383 if (!addr || port <= 0) 384 return; 385 386#ifdef AF_INET6 387 if (addr->addr.sa_family == AF_INET6) 388 addr->ipv6.sin6_port = htons(port); 389 else 390#endif /* AF_INET6 */ 391 if (addr->addr.sa_family == AF_INET) 392 addr->ipv4.sin_port = htons(port); 393} 394 395 396/* 397 * 'httpAddrString()' - Convert an address to a numeric string. 398 * 399 * @since CUPS 1.2/OS X 10.5@ 400 */ 401 402char * /* O - Numeric address string */ 403httpAddrString(const http_addr_t *addr, /* I - Address to convert */ 404 char *s, /* I - String buffer */ 405 int slen) /* I - Length of string */ 406{ 407 DEBUG_printf(("httpAddrString(addr=%p, s=%p, slen=%d)", addr, s, slen)); 408 409 /* 410 * Range check input... 411 */ 412 413 if (!addr || !s || slen <= 2) 414 { 415 if (s && slen >= 1) 416 *s = '\0'; 417 418 return (NULL); 419 } 420 421#ifdef AF_LOCAL 422 if (addr->addr.sa_family == AF_LOCAL) 423 { 424 if (addr->un.sun_path[0] == '/') 425 strlcpy(s, addr->un.sun_path, slen); 426 else 427 strlcpy(s, "localhost", slen); 428 } 429 else 430#endif /* AF_LOCAL */ 431 if (addr->addr.sa_family == AF_INET) 432 { 433 unsigned temp; /* Temporary address */ 434 435 436 temp = ntohl(addr->ipv4.sin_addr.s_addr); 437 438 snprintf(s, slen, "%d.%d.%d.%d", (temp >> 24) & 255, 439 (temp >> 16) & 255, (temp >> 8) & 255, temp & 255); 440 } 441#ifdef AF_INET6 442 else if (addr->addr.sa_family == AF_INET6) 443 { 444 char *sptr, /* Pointer into string */ 445 temps[64]; /* Temporary string for address */ 446 447# ifdef HAVE_GETNAMEINFO 448 if (getnameinfo(&addr->addr, httpAddrLength(addr), temps, sizeof(temps), 449 NULL, 0, NI_NUMERICHOST)) 450 { 451 /* 452 * If we get an error back, then the address type is not supported 453 * and we should zero out the buffer... 454 */ 455 456 s[0] = '\0'; 457 458 return (NULL); 459 } 460 else if ((sptr = strchr(temps, '%')) != NULL) 461 { 462 /* 463 * Convert "%zone" to "+zone" to match URI form... 464 */ 465 466 *sptr = '+'; 467 } 468 469# else 470 int i; /* Looping var */ 471 unsigned temp; /* Current value */ 472 const char *prefix; /* Prefix for address */ 473 474 475 prefix = ""; 476 for (sptr = temps, i = 0; i < 4 && addr->ipv6.sin6_addr.s6_addr32[i]; i ++) 477 { 478 temp = ntohl(addr->ipv6.sin6_addr.s6_addr32[i]); 479 480 snprintf(sptr, sizeof(temps) - (sptr - temps), "%s%x", prefix, 481 (temp >> 16) & 0xffff); 482 prefix = ":"; 483 sptr += strlen(sptr); 484 485 temp &= 0xffff; 486 487 if (temp || i == 3 || addr->ipv6.sin6_addr.s6_addr32[i + 1]) 488 { 489 snprintf(sptr, sizeof(temps) - (sptr - temps), "%s%x", prefix, temp); 490 sptr += strlen(sptr); 491 } 492 } 493 494 if (i < 4) 495 { 496 while (i < 4 && !addr->ipv6.sin6_addr.s6_addr32[i]) 497 i ++; 498 499 if (i < 4) 500 { 501 snprintf(sptr, sizeof(temps) - (sptr - temps), "%s:", prefix); 502 prefix = ":"; 503 sptr += strlen(sptr); 504 505 for (; i < 4; i ++) 506 { 507 temp = ntohl(addr->ipv6.sin6_addr.s6_addr32[i]); 508 509 if ((temp & 0xffff0000) || 510 (i > 0 && addr->ipv6.sin6_addr.s6_addr32[i - 1])) 511 { 512 snprintf(sptr, sizeof(temps) - (sptr - temps), "%s%x", prefix, 513 (temp >> 16) & 0xffff); 514 sptr += strlen(sptr); 515 } 516 517 snprintf(sptr, sizeof(temps) - (sptr - temps), "%s%x", prefix, 518 temp & 0xffff); 519 sptr += strlen(sptr); 520 } 521 } 522 else if (sptr == s) 523 { 524 /* 525 * Empty address... 526 */ 527 528 strlcpy(temps, "::", sizeof(temps)); 529 } 530 else 531 { 532 /* 533 * Empty at end... 534 */ 535 536 strlcpy(sptr, "::", sizeof(temps) - (sptr - temps)); 537 } 538 } 539# endif /* HAVE_GETNAMEINFO */ 540 541 /* 542 * Add "[v1." and "]" around IPv6 address to convert to URI form. 543 */ 544 545 snprintf(s, slen, "[v1.%s]", temps); 546 } 547#endif /* AF_INET6 */ 548 else 549 strlcpy(s, "UNKNOWN", slen); 550 551 DEBUG_printf(("1httpAddrString: returning \"%s\"...", s)); 552 553 return (s); 554} 555 556 557/* 558 * 'httpGetHostByName()' - Lookup a hostname or IPv4 address, and return 559 * address records for the specified name. 560 * 561 * @deprecated@ 562 */ 563 564struct hostent * /* O - Host entry */ 565httpGetHostByName(const char *name) /* I - Hostname or IP address */ 566{ 567 const char *nameptr; /* Pointer into name */ 568 unsigned ip[4]; /* IP address components */ 569 _cups_globals_t *cg = _cupsGlobals(); 570 /* Pointer to library globals */ 571 572 573 DEBUG_printf(("httpGetHostByName(name=\"%s\")", name)); 574 575 /* 576 * Avoid lookup delays and configuration problems when connecting 577 * to the localhost address... 578 */ 579 580 if (!strcmp(name, "localhost")) 581 name = "127.0.0.1"; 582 583 /* 584 * This function is needed because some operating systems have a 585 * buggy implementation of gethostbyname() that does not support 586 * IP addresses. If the first character of the name string is a 587 * number, then sscanf() is used to extract the IP components. 588 * We then pack the components into an IPv4 address manually, 589 * since the inet_aton() function is deprecated. We use the 590 * htonl() macro to get the right byte order for the address. 591 * 592 * We also support domain sockets when supported by the underlying 593 * OS... 594 */ 595 596#ifdef AF_LOCAL 597 if (name[0] == '/') 598 { 599 /* 600 * A domain socket address, so make an AF_LOCAL entry and return it... 601 */ 602 603 cg->hostent.h_name = (char *)name; 604 cg->hostent.h_aliases = NULL; 605 cg->hostent.h_addrtype = AF_LOCAL; 606 cg->hostent.h_length = strlen(name) + 1; 607 cg->hostent.h_addr_list = cg->ip_ptrs; 608 cg->ip_ptrs[0] = (char *)name; 609 cg->ip_ptrs[1] = NULL; 610 611 DEBUG_puts("1httpGetHostByName: returning domain socket address..."); 612 613 return (&cg->hostent); 614 } 615#endif /* AF_LOCAL */ 616 617 for (nameptr = name; isdigit(*nameptr & 255) || *nameptr == '.'; nameptr ++); 618 619 if (!*nameptr) 620 { 621 /* 622 * We have an IPv4 address; break it up and provide the host entry 623 * to the caller. 624 */ 625 626 if (sscanf(name, "%u.%u.%u.%u", ip, ip + 1, ip + 2, ip + 3) != 4) 627 return (NULL); /* Must have 4 numbers */ 628 629 if (ip[0] > 255 || ip[1] > 255 || ip[2] > 255 || ip[3] > 255) 630 return (NULL); /* Invalid byte ranges! */ 631 632 cg->ip_addr = htonl((((((((unsigned)ip[0] << 8) | (unsigned)ip[1]) << 8) | 633 (unsigned)ip[2]) << 8) | 634 (unsigned)ip[3])); 635 636 /* 637 * Fill in the host entry and return it... 638 */ 639 640 cg->hostent.h_name = (char *)name; 641 cg->hostent.h_aliases = NULL; 642 cg->hostent.h_addrtype = AF_INET; 643 cg->hostent.h_length = 4; 644 cg->hostent.h_addr_list = cg->ip_ptrs; 645 cg->ip_ptrs[0] = (char *)&(cg->ip_addr); 646 cg->ip_ptrs[1] = NULL; 647 648 DEBUG_puts("1httpGetHostByName: returning IPv4 address..."); 649 650 return (&cg->hostent); 651 } 652 else 653 { 654 /* 655 * Use the gethostbyname() function to get the IPv4 address for 656 * the name... 657 */ 658 659 DEBUG_puts("1httpGetHostByName: returning domain lookup address(es)..."); 660 661 return (gethostbyname(name)); 662 } 663} 664 665 666/* 667 * 'httpGetHostname()' - Get the FQDN for the connection or local system. 668 * 669 * When "http" points to a connected socket, return the hostname or 670 * address that was used in the call to httpConnect() or httpConnectEncrypt(). 671 * Otherwise, return the FQDN for the local system using both gethostname() 672 * and gethostbyname() to get the local hostname with domain. 673 * 674 * @since CUPS 1.2/OS X 10.5@ 675 */ 676 677const char * /* O - FQDN for connection or system */ 678httpGetHostname(http_t *http, /* I - HTTP connection or NULL */ 679 char *s, /* I - String buffer for name */ 680 int slen) /* I - Size of buffer */ 681{ 682 if (!s || slen <= 1) 683 return (NULL); 684 685 if (http) 686 { 687 if (http->hostname[0] == '/') 688 strlcpy(s, "localhost", slen); 689 else 690 strlcpy(s, http->hostname, slen); 691 } 692 else 693 { 694 /* 695 * Get the hostname... 696 */ 697 698 if (gethostname(s, slen) < 0) 699 strlcpy(s, "localhost", slen); 700 701 if (!strchr(s, '.')) 702 { 703#ifdef HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME 704 /* 705 * The hostname is not a FQDN, so use the local hostname from the 706 * SystemConfiguration framework... 707 */ 708 709 SCDynamicStoreRef sc = SCDynamicStoreCreate(kCFAllocatorDefault, 710 CFSTR("libcups"), NULL, NULL); 711 /* System configuration data */ 712 CFStringRef local = sc ? SCDynamicStoreCopyLocalHostName(sc) : NULL; 713 /* Local host name */ 714 char localStr[1024]; /* Local host name C string */ 715 716 if (local && CFStringGetCString(local, localStr, sizeof(localStr), 717 kCFStringEncodingUTF8)) 718 { 719 /* 720 * Append ".local." to the hostname we get... 721 */ 722 723 snprintf(s, slen, "%s.local.", localStr); 724 } 725 726 if (local) 727 CFRelease(local); 728 if (sc) 729 CFRelease(sc); 730 731#else 732 /* 733 * The hostname is not a FQDN, so look it up... 734 */ 735 736 struct hostent *host; /* Host entry to get FQDN */ 737 738 if ((host = gethostbyname(s)) != NULL && host->h_name) 739 { 740 /* 741 * Use the resolved hostname... 742 */ 743 744 strlcpy(s, host->h_name, slen); 745 } 746#endif /* HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME */ 747 } 748 } 749 750 /* 751 * Return the hostname with as much domain info as we have... 752 */ 753 754 return (s); 755} 756 757 758/* 759 * End of "$Id: http-addr.c 11693 2014-03-11 01:24:45Z msweet $". 760 */ 761