1 /* 2 * HTTP support routines for CUPS. 3 * 4 * Copyright 2007-2016 by Apple Inc. 5 * Copyright 1997-2007 by Easy Software Products, all rights reserved. 6 * 7 * These coded instructions, statements, and computer programs are the 8 * property of Apple Inc. and are protected by Federal copyright 9 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 10 * which should have been included with this file. If this file is 11 * missing or damaged, see the license at "http://www.cups.org/". 12 * 13 * This file is subject to the Apple OS-Developed Software exception. 14 */ 15 16 /* 17 * Include necessary headers... 18 */ 19 20 #include "cups-private.h" 21 #ifdef HAVE_DNSSD 22 # include <dns_sd.h> 23 # ifdef WIN32 24 # include <io.h> 25 # elif defined(HAVE_POLL) 26 # include <poll.h> 27 # else 28 # include <sys/select.h> 29 # endif /* WIN32 */ 30 #elif defined(HAVE_AVAHI) 31 # include <avahi-client/client.h> 32 # include <avahi-client/lookup.h> 33 # include <avahi-common/simple-watch.h> 34 #endif /* HAVE_DNSSD */ 35 36 37 /* 38 * Local types... 39 */ 40 41 typedef struct _http_uribuf_s /* URI buffer */ 42 { 43 #ifdef HAVE_AVAHI 44 AvahiSimplePoll *poll; /* Poll state */ 45 #endif /* HAVE_AVAHI */ 46 char *buffer; /* Pointer to buffer */ 47 size_t bufsize; /* Size of buffer */ 48 int options; /* Options passed to _httpResolveURI */ 49 const char *resource; /* Resource from URI */ 50 const char *uuid; /* UUID from URI */ 51 } _http_uribuf_t; 52 53 54 /* 55 * Local globals... 56 */ 57 58 static const char * const http_days[7] =/* Days of the week */ 59 { 60 "Sun", 61 "Mon", 62 "Tue", 63 "Wed", 64 "Thu", 65 "Fri", 66 "Sat" 67 }; 68 static const char * const http_months[12] = 69 { /* Months of the year */ 70 "Jan", 71 "Feb", 72 "Mar", 73 "Apr", 74 "May", 75 "Jun", 76 "Jul", 77 "Aug", 78 "Sep", 79 "Oct", 80 "Nov", 81 "Dec" 82 }; 83 static const char * const http_states[] = 84 { /* HTTP state strings */ 85 "HTTP_STATE_ERROR", 86 "HTTP_STATE_WAITING", 87 "HTTP_STATE_OPTIONS", 88 "HTTP_STATE_GET", 89 "HTTP_STATE_GET_SEND", 90 "HTTP_STATE_HEAD", 91 "HTTP_STATE_POST", 92 "HTTP_STATE_POST_RECV", 93 "HTTP_STATE_POST_SEND", 94 "HTTP_STATE_PUT", 95 "HTTP_STATE_PUT_RECV", 96 "HTTP_STATE_DELETE", 97 "HTTP_STATE_TRACE", 98 "HTTP_STATE_CONNECT", 99 "HTTP_STATE_STATUS", 100 "HTTP_STATE_UNKNOWN_METHOD", 101 "HTTP_STATE_UNKNOWN_VERSION" 102 }; 103 104 105 /* 106 * Local functions... 107 */ 108 109 static const char *http_copy_decode(char *dst, const char *src, 110 int dstsize, const char *term, 111 int decode); 112 static char *http_copy_encode(char *dst, const char *src, 113 char *dstend, const char *reserved, 114 const char *term, int encode); 115 #ifdef HAVE_DNSSD 116 static void DNSSD_API http_resolve_cb(DNSServiceRef sdRef, 117 DNSServiceFlags flags, 118 uint32_t interfaceIndex, 119 DNSServiceErrorType errorCode, 120 const char *fullName, 121 const char *hostTarget, 122 uint16_t port, uint16_t txtLen, 123 const unsigned char *txtRecord, 124 void *context); 125 #endif /* HAVE_DNSSD */ 126 127 #ifdef HAVE_AVAHI 128 static void http_client_cb(AvahiClient *client, 129 AvahiClientState state, void *simple_poll); 130 static int http_poll_cb(struct pollfd *pollfds, unsigned int num_pollfds, 131 int timeout, void *context); 132 static void http_resolve_cb(AvahiServiceResolver *resolver, 133 AvahiIfIndex interface, 134 AvahiProtocol protocol, 135 AvahiResolverEvent event, 136 const char *name, const char *type, 137 const char *domain, const char *host_name, 138 const AvahiAddress *address, uint16_t port, 139 AvahiStringList *txt, 140 AvahiLookupResultFlags flags, void *context); 141 #endif /* HAVE_AVAHI */ 142 143 144 /* 145 * 'httpAssembleURI()' - Assemble a uniform resource identifier from its 146 * components. 147 * 148 * This function escapes reserved characters in the URI depending on the 149 * value of the "encoding" argument. You should use this function in 150 * place of traditional string functions whenever you need to create a 151 * URI string. 152 * 153 * @since CUPS 1.2/macOS 10.5@ 154 */ 155 156 http_uri_status_t /* O - URI status */ 157 httpAssembleURI( 158 http_uri_coding_t encoding, /* I - Encoding flags */ 159 char *uri, /* I - URI buffer */ 160 int urilen, /* I - Size of URI buffer */ 161 const char *scheme, /* I - Scheme name */ 162 const char *username, /* I - Username */ 163 const char *host, /* I - Hostname or address */ 164 int port, /* I - Port number */ 165 const char *resource) /* I - Resource */ 166 { 167 char *ptr, /* Pointer into URI buffer */ 168 *end; /* End of URI buffer */ 169 170 171 /* 172 * Range check input... 173 */ 174 175 if (!uri || urilen < 1 || !scheme || port < 0) 176 { 177 if (uri) 178 *uri = '\0'; 179 180 return (HTTP_URI_STATUS_BAD_ARGUMENTS); 181 } 182 183 /* 184 * Assemble the URI starting with the scheme... 185 */ 186 187 end = uri + urilen - 1; 188 ptr = http_copy_encode(uri, scheme, end, NULL, NULL, 0); 189 190 if (!ptr) 191 goto assemble_overflow; 192 193 if (!strcmp(scheme, "geo") || !strcmp(scheme, "mailto") || !strcmp(scheme, "tel")) 194 { 195 /* 196 * geo:, mailto:, and tel: only have :, no //... 197 */ 198 199 if (ptr < end) 200 *ptr++ = ':'; 201 else 202 goto assemble_overflow; 203 } 204 else 205 { 206 /* 207 * Schemes other than geo:, mailto:, and tel: typically have //... 208 */ 209 210 if ((ptr + 2) < end) 211 { 212 *ptr++ = ':'; 213 *ptr++ = '/'; 214 *ptr++ = '/'; 215 } 216 else 217 goto assemble_overflow; 218 } 219 220 /* 221 * Next the username and hostname, if any... 222 */ 223 224 if (host) 225 { 226 const char *hostptr; /* Pointer into hostname */ 227 int have_ipv6; /* Do we have an IPv6 address? */ 228 229 if (username && *username) 230 { 231 /* 232 * Add username@ first... 233 */ 234 235 ptr = http_copy_encode(ptr, username, end, "/?#[]@", NULL, 236 encoding & HTTP_URI_CODING_USERNAME); 237 238 if (!ptr) 239 goto assemble_overflow; 240 241 if (ptr < end) 242 *ptr++ = '@'; 243 else 244 goto assemble_overflow; 245 } 246 247 /* 248 * Then add the hostname. Since IPv6 is a particular pain to deal 249 * with, we have several special cases to deal with. If we get 250 * an IPv6 address with brackets around it, assume it is already in 251 * URI format. Since DNS-SD service names can sometimes look like 252 * raw IPv6 addresses, we specifically look for "._tcp" in the name, 253 * too... 254 */ 255 256 for (hostptr = host, 257 have_ipv6 = strchr(host, ':') && !strstr(host, "._tcp"); 258 *hostptr && have_ipv6; 259 hostptr ++) 260 if (*hostptr != ':' && !isxdigit(*hostptr & 255)) 261 { 262 have_ipv6 = *hostptr == '%'; 263 break; 264 } 265 266 if (have_ipv6) 267 { 268 /* 269 * We have a raw IPv6 address... 270 */ 271 272 if (strchr(host, '%') && !(encoding & HTTP_URI_CODING_RFC6874)) 273 { 274 /* 275 * We have a link-local address, add "[v1." prefix... 276 */ 277 278 if ((ptr + 4) < end) 279 { 280 *ptr++ = '['; 281 *ptr++ = 'v'; 282 *ptr++ = '1'; 283 *ptr++ = '.'; 284 } 285 else 286 goto assemble_overflow; 287 } 288 else 289 { 290 /* 291 * We have a normal (or RFC 6874 link-local) address, add "[" prefix... 292 */ 293 294 if (ptr < end) 295 *ptr++ = '['; 296 else 297 goto assemble_overflow; 298 } 299 300 /* 301 * Copy the rest of the IPv6 address, and terminate with "]". 302 */ 303 304 while (ptr < end && *host) 305 { 306 if (*host == '%') 307 { 308 /* 309 * Convert/encode zone separator 310 */ 311 312 if (encoding & HTTP_URI_CODING_RFC6874) 313 { 314 if (ptr >= (end - 2)) 315 goto assemble_overflow; 316 317 *ptr++ = '%'; 318 *ptr++ = '2'; 319 *ptr++ = '5'; 320 } 321 else 322 *ptr++ = '+'; 323 324 host ++; 325 } 326 else 327 *ptr++ = *host++; 328 } 329 330 if (*host) 331 goto assemble_overflow; 332 333 if (ptr < end) 334 *ptr++ = ']'; 335 else 336 goto assemble_overflow; 337 } 338 else 339 { 340 /* 341 * Otherwise, just copy the host string (the extra chars are not in the 342 * "reg-name" ABNF rule; anything <= SP or >= DEL plus % gets automatically 343 * percent-encoded. 344 */ 345 346 ptr = http_copy_encode(ptr, host, end, "\"#/:<>?@[\\]^`{|}", NULL, 347 encoding & HTTP_URI_CODING_HOSTNAME); 348 349 if (!ptr) 350 goto assemble_overflow; 351 } 352 353 /* 354 * Finish things off with the port number... 355 */ 356 357 if (port > 0) 358 { 359 snprintf(ptr, (size_t)(end - ptr + 1), ":%d", port); 360 ptr += strlen(ptr); 361 362 if (ptr >= end) 363 goto assemble_overflow; 364 } 365 } 366 367 /* 368 * Last but not least, add the resource string... 369 */ 370 371 if (resource) 372 { 373 char *query; /* Pointer to query string */ 374 375 376 /* 377 * Copy the resource string up to the query string if present... 378 */ 379 380 query = strchr(resource, '?'); 381 ptr = http_copy_encode(ptr, resource, end, NULL, "?", 382 encoding & HTTP_URI_CODING_RESOURCE); 383 if (!ptr) 384 goto assemble_overflow; 385 386 if (query) 387 { 388 /* 389 * Copy query string without encoding... 390 */ 391 392 ptr = http_copy_encode(ptr, query, end, NULL, NULL, 393 encoding & HTTP_URI_CODING_QUERY); 394 if (!ptr) 395 goto assemble_overflow; 396 } 397 } 398 else if (ptr < end) 399 *ptr++ = '/'; 400 else 401 goto assemble_overflow; 402 403 /* 404 * Nul-terminate the URI buffer and return with no errors... 405 */ 406 407 *ptr = '\0'; 408 409 return (HTTP_URI_STATUS_OK); 410 411 /* 412 * Clear the URI string and return an overflow error; I don't usually 413 * like goto's, but in this case it makes sense... 414 */ 415 416 assemble_overflow: 417 418 *uri = '\0'; 419 return (HTTP_URI_STATUS_OVERFLOW); 420 } 421 422 423 /* 424 * 'httpAssembleURIf()' - Assemble a uniform resource identifier from its 425 * components with a formatted resource. 426 * 427 * This function creates a formatted version of the resource string 428 * argument "resourcef" and escapes reserved characters in the URI 429 * depending on the value of the "encoding" argument. You should use 430 * this function in place of traditional string functions whenever 431 * you need to create a URI string. 432 * 433 * @since CUPS 1.2/macOS 10.5@ 434 */ 435 436 http_uri_status_t /* O - URI status */ 437 httpAssembleURIf( 438 http_uri_coding_t encoding, /* I - Encoding flags */ 439 char *uri, /* I - URI buffer */ 440 int urilen, /* I - Size of URI buffer */ 441 const char *scheme, /* I - Scheme name */ 442 const char *username, /* I - Username */ 443 const char *host, /* I - Hostname or address */ 444 int port, /* I - Port number */ 445 const char *resourcef, /* I - Printf-style resource */ 446 ...) /* I - Additional arguments as needed */ 447 { 448 va_list ap; /* Pointer to additional arguments */ 449 char resource[1024]; /* Formatted resource string */ 450 int bytes; /* Bytes in formatted string */ 451 452 453 /* 454 * Range check input... 455 */ 456 457 if (!uri || urilen < 1 || !scheme || port < 0 || !resourcef) 458 { 459 if (uri) 460 *uri = '\0'; 461 462 return (HTTP_URI_STATUS_BAD_ARGUMENTS); 463 } 464 465 /* 466 * Format the resource string and assemble the URI... 467 */ 468 469 va_start(ap, resourcef); 470 bytes = vsnprintf(resource, sizeof(resource), resourcef, ap); 471 va_end(ap); 472 473 if ((size_t)bytes >= sizeof(resource)) 474 { 475 *uri = '\0'; 476 return (HTTP_URI_STATUS_OVERFLOW); 477 } 478 else 479 return (httpAssembleURI(encoding, uri, urilen, scheme, username, host, 480 port, resource)); 481 } 482 483 484 /* 485 * 'httpAssembleUUID()' - Assemble a name-based UUID URN conforming to RFC 4122. 486 * 487 * This function creates a unique 128-bit identifying number using the server 488 * name, port number, random data, and optionally an object name and/or object 489 * number. The result is formatted as a UUID URN as defined in RFC 4122. 490 * 491 * The buffer needs to be at least 46 bytes in size. 492 * 493 * @since CUPS 1.7/macOS 10.9@ 494 */ 495 496 char * /* I - UUID string */ 497 httpAssembleUUID(const char *server, /* I - Server name */ 498 int port, /* I - Port number */ 499 const char *name, /* I - Object name or NULL */ 500 int number, /* I - Object number or 0 */ 501 char *buffer, /* I - String buffer */ 502 size_t bufsize) /* I - Size of buffer */ 503 { 504 char data[1024]; /* Source string for MD5 */ 505 _cups_md5_state_t md5state; /* MD5 state */ 506 unsigned char md5sum[16]; /* MD5 digest/sum */ 507 508 509 /* 510 * Build a version 3 UUID conforming to RFC 4122. 511 * 512 * Start with the MD5 sum of the server, port, object name and 513 * number, and some random data on the end. 514 */ 515 516 snprintf(data, sizeof(data), "%s:%d:%s:%d:%04x:%04x", server, 517 port, name ? name : server, number, 518 (unsigned)CUPS_RAND() & 0xffff, (unsigned)CUPS_RAND() & 0xffff); 519 520 _cupsMD5Init(&md5state); 521 _cupsMD5Append(&md5state, (unsigned char *)data, (int)strlen(data)); 522 _cupsMD5Finish(&md5state, md5sum); 523 524 /* 525 * Generate the UUID from the MD5... 526 */ 527 528 snprintf(buffer, bufsize, 529 "urn:uuid:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" 530 "%02x%02x%02x%02x%02x%02x", 531 md5sum[0], md5sum[1], md5sum[2], md5sum[3], md5sum[4], md5sum[5], 532 (md5sum[6] & 15) | 0x30, md5sum[7], (md5sum[8] & 0x3f) | 0x40, 533 md5sum[9], md5sum[10], md5sum[11], md5sum[12], md5sum[13], 534 md5sum[14], md5sum[15]); 535 536 return (buffer); 537 } 538 539 540 /* 541 * 'httpDecode64()' - Base64-decode a string. 542 * 543 * This function is deprecated. Use the httpDecode64_2() function instead 544 * which provides buffer length arguments. 545 * 546 * @deprecated@ 547 */ 548 549 char * /* O - Decoded string */ 550 httpDecode64(char *out, /* I - String to write to */ 551 const char *in) /* I - String to read from */ 552 { 553 int outlen; /* Output buffer length */ 554 555 556 /* 557 * Use the old maximum buffer size for binary compatibility... 558 */ 559 560 outlen = 512; 561 562 return (httpDecode64_2(out, &outlen, in)); 563 } 564 565 566 /* 567 * 'httpDecode64_2()' - Base64-decode a string. 568 * 569 * @since CUPS 1.1.21/macOS 10.4@ 570 */ 571 572 char * /* O - Decoded string */ 573 httpDecode64_2(char *out, /* I - String to write to */ 574 int *outlen, /* IO - Size of output string */ 575 const char *in) /* I - String to read from */ 576 { 577 int pos; /* Bit position */ 578 unsigned base64; /* Value of this character */ 579 char *outptr, /* Output pointer */ 580 *outend; /* End of output buffer */ 581 582 583 /* 584 * Range check input... 585 */ 586 587 if (!out || !outlen || *outlen < 1 || !in) 588 return (NULL); 589 590 if (!*in) 591 { 592 *out = '\0'; 593 *outlen = 0; 594 595 return (out); 596 } 597 598 /* 599 * Convert from base-64 to bytes... 600 */ 601 602 for (outptr = out, outend = out + *outlen - 1, pos = 0; *in != '\0'; in ++) 603 { 604 /* 605 * Decode this character into a number from 0 to 63... 606 */ 607 608 if (*in >= 'A' && *in <= 'Z') 609 base64 = (unsigned)(*in - 'A'); 610 else if (*in >= 'a' && *in <= 'z') 611 base64 = (unsigned)(*in - 'a' + 26); 612 else if (*in >= '0' && *in <= '9') 613 base64 = (unsigned)(*in - '0' + 52); 614 else if (*in == '+') 615 base64 = 62; 616 else if (*in == '/') 617 base64 = 63; 618 else if (*in == '=') 619 break; 620 else 621 continue; 622 623 /* 624 * Store the result in the appropriate chars... 625 */ 626 627 switch (pos) 628 { 629 case 0 : 630 if (outptr < outend) 631 *outptr = (char)(base64 << 2); 632 pos ++; 633 break; 634 case 1 : 635 if (outptr < outend) 636 *outptr++ |= (char)((base64 >> 4) & 3); 637 if (outptr < outend) 638 *outptr = (char)((base64 << 4) & 255); 639 pos ++; 640 break; 641 case 2 : 642 if (outptr < outend) 643 *outptr++ |= (char)((base64 >> 2) & 15); 644 if (outptr < outend) 645 *outptr = (char)((base64 << 6) & 255); 646 pos ++; 647 break; 648 case 3 : 649 if (outptr < outend) 650 *outptr++ |= (char)base64; 651 pos = 0; 652 break; 653 } 654 } 655 656 *outptr = '\0'; 657 658 /* 659 * Return the decoded string and size... 660 */ 661 662 *outlen = (int)(outptr - out); 663 664 return (out); 665 } 666 667 668 /* 669 * 'httpEncode64()' - Base64-encode a string. 670 * 671 * This function is deprecated. Use the httpEncode64_2() function instead 672 * which provides buffer length arguments. 673 * 674 * @deprecated@ 675 */ 676 677 char * /* O - Encoded string */ 678 httpEncode64(char *out, /* I - String to write to */ 679 const char *in) /* I - String to read from */ 680 { 681 return (httpEncode64_2(out, 512, in, (int)strlen(in))); 682 } 683 684 685 /* 686 * 'httpEncode64_2()' - Base64-encode a string. 687 * 688 * @since CUPS 1.1.21/macOS 10.4@ 689 */ 690 691 char * /* O - Encoded string */ 692 httpEncode64_2(char *out, /* I - String to write to */ 693 int outlen, /* I - Size of output string */ 694 const char *in, /* I - String to read from */ 695 int inlen) /* I - Size of input string */ 696 { 697 char *outptr, /* Output pointer */ 698 *outend; /* End of output buffer */ 699 static const char base64[] = /* Base64 characters... */ 700 { 701 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 702 "abcdefghijklmnopqrstuvwxyz" 703 "0123456789" 704 "+/" 705 }; 706 707 708 /* 709 * Range check input... 710 */ 711 712 if (!out || outlen < 1 || !in) 713 return (NULL); 714 715 /* 716 * Convert bytes to base-64... 717 */ 718 719 for (outptr = out, outend = out + outlen - 1; inlen > 0; in ++, inlen --) 720 { 721 /* 722 * Encode the up to 3 characters as 4 Base64 numbers... 723 */ 724 725 if (outptr < outend) 726 *outptr ++ = base64[(in[0] & 255) >> 2]; 727 728 if (outptr < outend) 729 { 730 if (inlen > 1) 731 *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63]; 732 else 733 *outptr ++ = base64[((in[0] & 255) << 4) & 63]; 734 } 735 736 in ++; 737 inlen --; 738 if (inlen <= 0) 739 { 740 if (outptr < outend) 741 *outptr ++ = '='; 742 if (outptr < outend) 743 *outptr ++ = '='; 744 break; 745 } 746 747 if (outptr < outend) 748 { 749 if (inlen > 1) 750 *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63]; 751 else 752 *outptr ++ = base64[((in[0] & 255) << 2) & 63]; 753 } 754 755 in ++; 756 inlen --; 757 if (inlen <= 0) 758 { 759 if (outptr < outend) 760 *outptr ++ = '='; 761 break; 762 } 763 764 if (outptr < outend) 765 *outptr ++ = base64[in[0] & 63]; 766 } 767 768 *outptr = '\0'; 769 770 /* 771 * Return the encoded string... 772 */ 773 774 return (out); 775 } 776 777 778 /* 779 * 'httpGetDateString()' - Get a formatted date/time string from a time value. 780 * 781 * @deprecated@ 782 */ 783 784 const char * /* O - Date/time string */ 785 httpGetDateString(time_t t) /* I - UNIX time */ 786 { 787 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ 788 789 790 return (httpGetDateString2(t, cg->http_date, sizeof(cg->http_date))); 791 } 792 793 794 /* 795 * 'httpGetDateString2()' - Get a formatted date/time string from a time value. 796 * 797 * @since CUPS 1.2/macOS 10.5@ 798 */ 799 800 const char * /* O - Date/time string */ 801 httpGetDateString2(time_t t, /* I - UNIX time */ 802 char *s, /* I - String buffer */ 803 int slen) /* I - Size of string buffer */ 804 { 805 struct tm *tdate; /* UNIX date/time data */ 806 807 808 tdate = gmtime(&t); 809 if (tdate) 810 snprintf(s, (size_t)slen, "%s, %02d %s %d %02d:%02d:%02d GMT", http_days[tdate->tm_wday], tdate->tm_mday, http_months[tdate->tm_mon], tdate->tm_year + 1900, tdate->tm_hour, tdate->tm_min, tdate->tm_sec); 811 else 812 s[0] = '\0'; 813 814 return (s); 815 } 816 817 818 /* 819 * 'httpGetDateTime()' - Get a time value from a formatted date/time string. 820 */ 821 822 time_t /* O - UNIX time */ 823 httpGetDateTime(const char *s) /* I - Date/time string */ 824 { 825 int i; /* Looping var */ 826 char mon[16]; /* Abbreviated month name */ 827 int day, year; /* Day of month and year */ 828 int hour, min, sec; /* Time */ 829 int days; /* Number of days since 1970 */ 830 static const int normal_days[] = /* Days to a month, normal years */ 831 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; 832 static const int leap_days[] = /* Days to a month, leap years */ 833 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; 834 835 836 DEBUG_printf(("2httpGetDateTime(s=\"%s\")", s)); 837 838 /* 839 * Extract the date and time from the formatted string... 840 */ 841 842 if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6) 843 return (0); 844 845 DEBUG_printf(("4httpGetDateTime: day=%d, mon=\"%s\", year=%d, hour=%d, " 846 "min=%d, sec=%d", day, mon, year, hour, min, sec)); 847 848 /* 849 * Convert the month name to a number from 0 to 11. 850 */ 851 852 for (i = 0; i < 12; i ++) 853 if (!_cups_strcasecmp(mon, http_months[i])) 854 break; 855 856 if (i >= 12) 857 return (0); 858 859 DEBUG_printf(("4httpGetDateTime: i=%d", i)); 860 861 /* 862 * Now convert the date and time to a UNIX time value in seconds since 863 * 1970. We can't use mktime() since the timezone may not be UTC but 864 * the date/time string *is* UTC. 865 */ 866 867 if ((year & 3) == 0 && ((year % 100) != 0 || (year % 400) == 0)) 868 days = leap_days[i] + day - 1; 869 else 870 days = normal_days[i] + day - 1; 871 872 DEBUG_printf(("4httpGetDateTime: days=%d", days)); 873 874 days += (year - 1970) * 365 + /* 365 days per year (normally) */ 875 ((year - 1) / 4 - 492) - /* + leap days */ 876 ((year - 1) / 100 - 19) + /* - 100 year days */ 877 ((year - 1) / 400 - 4); /* + 400 year days */ 878 879 DEBUG_printf(("4httpGetDateTime: days=%d\n", days)); 880 881 return (days * 86400 + hour * 3600 + min * 60 + sec); 882 } 883 884 885 /* 886 * 'httpSeparate()' - Separate a Universal Resource Identifier into its 887 * components. 888 * 889 * This function is deprecated; use the httpSeparateURI() function instead. 890 * 891 * @deprecated@ 892 */ 893 894 void 895 httpSeparate(const char *uri, /* I - Universal Resource Identifier */ 896 char *scheme, /* O - Scheme [32] (http, https, etc.) */ 897 char *username, /* O - Username [1024] */ 898 char *host, /* O - Hostname [1024] */ 899 int *port, /* O - Port number to use */ 900 char *resource) /* O - Resource/filename [1024] */ 901 { 902 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 32, username, 903 HTTP_MAX_URI, host, HTTP_MAX_URI, port, resource, 904 HTTP_MAX_URI); 905 } 906 907 908 /* 909 * 'httpSeparate2()' - Separate a Universal Resource Identifier into its 910 * components. 911 * 912 * This function is deprecated; use the httpSeparateURI() function instead. 913 * 914 * @since CUPS 1.1.21/macOS 10.4@ 915 * @deprecated@ 916 */ 917 918 void 919 httpSeparate2(const char *uri, /* I - Universal Resource Identifier */ 920 char *scheme, /* O - Scheme (http, https, etc.) */ 921 int schemelen, /* I - Size of scheme buffer */ 922 char *username, /* O - Username */ 923 int usernamelen, /* I - Size of username buffer */ 924 char *host, /* O - Hostname */ 925 int hostlen, /* I - Size of hostname buffer */ 926 int *port, /* O - Port number to use */ 927 char *resource, /* O - Resource/filename */ 928 int resourcelen) /* I - Size of resource buffer */ 929 { 930 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, schemelen, username, 931 usernamelen, host, hostlen, port, resource, resourcelen); 932 } 933 934 935 /* 936 * 'httpSeparateURI()' - Separate a Universal Resource Identifier into its 937 * components. 938 * 939 * @since CUPS 1.2/macOS 10.5@ 940 */ 941 942 http_uri_status_t /* O - Result of separation */ 943 httpSeparateURI( 944 http_uri_coding_t decoding, /* I - Decoding flags */ 945 const char *uri, /* I - Universal Resource Identifier */ 946 char *scheme, /* O - Scheme (http, https, etc.) */ 947 int schemelen, /* I - Size of scheme buffer */ 948 char *username, /* O - Username */ 949 int usernamelen, /* I - Size of username buffer */ 950 char *host, /* O - Hostname */ 951 int hostlen, /* I - Size of hostname buffer */ 952 int *port, /* O - Port number to use */ 953 char *resource, /* O - Resource/filename */ 954 int resourcelen) /* I - Size of resource buffer */ 955 { 956 char *ptr, /* Pointer into string... */ 957 *end; /* End of string */ 958 const char *sep; /* Separator character */ 959 http_uri_status_t status; /* Result of separation */ 960 961 962 /* 963 * Initialize everything to blank... 964 */ 965 966 if (scheme && schemelen > 0) 967 *scheme = '\0'; 968 969 if (username && usernamelen > 0) 970 *username = '\0'; 971 972 if (host && hostlen > 0) 973 *host = '\0'; 974 975 if (port) 976 *port = 0; 977 978 if (resource && resourcelen > 0) 979 *resource = '\0'; 980 981 /* 982 * Range check input... 983 */ 984 985 if (!uri || !port || !scheme || schemelen <= 0 || !username || 986 usernamelen <= 0 || !host || hostlen <= 0 || !resource || 987 resourcelen <= 0) 988 return (HTTP_URI_STATUS_BAD_ARGUMENTS); 989 990 if (!*uri) 991 return (HTTP_URI_STATUS_BAD_URI); 992 993 /* 994 * Grab the scheme portion of the URI... 995 */ 996 997 status = HTTP_URI_STATUS_OK; 998 999 if (!strncmp(uri, "//", 2)) 1000 { 1001 /* 1002 * Workaround for HP IPP client bug... 1003 */ 1004 1005 strlcpy(scheme, "ipp", (size_t)schemelen); 1006 status = HTTP_URI_STATUS_MISSING_SCHEME; 1007 } 1008 else if (*uri == '/') 1009 { 1010 /* 1011 * Filename... 1012 */ 1013 1014 strlcpy(scheme, "file", (size_t)schemelen); 1015 status = HTTP_URI_STATUS_MISSING_SCHEME; 1016 } 1017 else 1018 { 1019 /* 1020 * Standard URI with scheme... 1021 */ 1022 1023 for (ptr = scheme, end = scheme + schemelen - 1; 1024 *uri && *uri != ':' && ptr < end;) 1025 if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" 1026 "abcdefghijklmnopqrstuvwxyz" 1027 "0123456789-+.", *uri) != NULL) 1028 *ptr++ = *uri++; 1029 else 1030 break; 1031 1032 *ptr = '\0'; 1033 1034 if (*uri != ':') 1035 { 1036 *scheme = '\0'; 1037 return (HTTP_URI_STATUS_BAD_SCHEME); 1038 } 1039 1040 uri ++; 1041 } 1042 1043 /* 1044 * Set the default port number... 1045 */ 1046 1047 if (!strcmp(scheme, "http")) 1048 *port = 80; 1049 else if (!strcmp(scheme, "https")) 1050 *port = 443; 1051 else if (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) 1052 *port = 631; 1053 else if (!_cups_strcasecmp(scheme, "lpd")) 1054 *port = 515; 1055 else if (!strcmp(scheme, "socket")) /* Not yet registered with IANA... */ 1056 *port = 9100; 1057 else if (strcmp(scheme, "file") && strcmp(scheme, "mailto") && strcmp(scheme, "tel")) 1058 status = HTTP_URI_STATUS_UNKNOWN_SCHEME; 1059 1060 /* 1061 * Now see if we have a hostname... 1062 */ 1063 1064 if (!strncmp(uri, "//", 2)) 1065 { 1066 /* 1067 * Yes, extract it... 1068 */ 1069 1070 uri += 2; 1071 1072 /* 1073 * Grab the username, if any... 1074 */ 1075 1076 if ((sep = strpbrk(uri, "@/")) != NULL && *sep == '@') 1077 { 1078 /* 1079 * Get a username:password combo... 1080 */ 1081 1082 uri = http_copy_decode(username, uri, usernamelen, "@", 1083 decoding & HTTP_URI_CODING_USERNAME); 1084 1085 if (!uri) 1086 { 1087 *username = '\0'; 1088 return (HTTP_URI_STATUS_BAD_USERNAME); 1089 } 1090 1091 uri ++; 1092 } 1093 1094 /* 1095 * Then the hostname/IP address... 1096 */ 1097 1098 if (*uri == '[') 1099 { 1100 /* 1101 * Grab IPv6 address... 1102 */ 1103 1104 uri ++; 1105 if (*uri == 'v') 1106 { 1107 /* 1108 * Skip IPvFuture ("vXXXX.") prefix... 1109 */ 1110 1111 uri ++; 1112 1113 while (isxdigit(*uri & 255)) 1114 uri ++; 1115 1116 if (*uri != '.') 1117 { 1118 *host = '\0'; 1119 return (HTTP_URI_STATUS_BAD_HOSTNAME); 1120 } 1121 1122 uri ++; 1123 } 1124 1125 uri = http_copy_decode(host, uri, hostlen, "]", 1126 decoding & HTTP_URI_CODING_HOSTNAME); 1127 1128 if (!uri) 1129 { 1130 *host = '\0'; 1131 return (HTTP_URI_STATUS_BAD_HOSTNAME); 1132 } 1133 1134 /* 1135 * Validate value... 1136 */ 1137 1138 if (*uri != ']') 1139 { 1140 *host = '\0'; 1141 return (HTTP_URI_STATUS_BAD_HOSTNAME); 1142 } 1143 1144 uri ++; 1145 1146 for (ptr = host; *ptr; ptr ++) 1147 if (*ptr == '+') 1148 { 1149 /* 1150 * Convert zone separator to % and stop here... 1151 */ 1152 1153 *ptr = '%'; 1154 break; 1155 } 1156 else if (*ptr == '%') 1157 { 1158 /* 1159 * Stop at zone separator (RFC 6874) 1160 */ 1161 1162 break; 1163 } 1164 else if (*ptr != ':' && *ptr != '.' && !isxdigit(*ptr & 255)) 1165 { 1166 *host = '\0'; 1167 return (HTTP_URI_STATUS_BAD_HOSTNAME); 1168 } 1169 } 1170 else 1171 { 1172 /* 1173 * Validate the hostname or IPv4 address first... 1174 */ 1175 1176 for (ptr = (char *)uri; *ptr; ptr ++) 1177 if (strchr(":?/", *ptr)) 1178 break; 1179 else if (!strchr("abcdefghijklmnopqrstuvwxyz" /* unreserved */ 1180 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* unreserved */ 1181 "0123456789" /* unreserved */ 1182 "-._~" /* unreserved */ 1183 "%" /* pct-encoded */ 1184 "!$&'()*+,;=" /* sub-delims */ 1185 "\\", *ptr)) /* SMB domain */ 1186 { 1187 *host = '\0'; 1188 return (HTTP_URI_STATUS_BAD_HOSTNAME); 1189 } 1190 1191 /* 1192 * Then copy the hostname or IPv4 address to the buffer... 1193 */ 1194 1195 uri = http_copy_decode(host, uri, hostlen, ":?/", 1196 decoding & HTTP_URI_CODING_HOSTNAME); 1197 1198 if (!uri) 1199 { 1200 *host = '\0'; 1201 return (HTTP_URI_STATUS_BAD_HOSTNAME); 1202 } 1203 } 1204 1205 /* 1206 * Validate hostname for file scheme - only empty and localhost are 1207 * acceptable. 1208 */ 1209 1210 if (!strcmp(scheme, "file") && strcmp(host, "localhost") && host[0]) 1211 { 1212 *host = '\0'; 1213 return (HTTP_URI_STATUS_BAD_HOSTNAME); 1214 } 1215 1216 /* 1217 * See if we have a port number... 1218 */ 1219 1220 if (*uri == ':') 1221 { 1222 /* 1223 * Yes, collect the port number... 1224 */ 1225 1226 if (!isdigit(uri[1] & 255)) 1227 { 1228 *port = 0; 1229 return (HTTP_URI_STATUS_BAD_PORT); 1230 } 1231 1232 *port = (int)strtol(uri + 1, (char **)&uri, 10); 1233 1234 if (*port <= 0 || *port > 65535) 1235 { 1236 *port = 0; 1237 return (HTTP_URI_STATUS_BAD_PORT); 1238 } 1239 1240 if (*uri != '/' && *uri) 1241 { 1242 *port = 0; 1243 return (HTTP_URI_STATUS_BAD_PORT); 1244 } 1245 } 1246 } 1247 1248 /* 1249 * The remaining portion is the resource string... 1250 */ 1251 1252 if (*uri == '?' || !*uri) 1253 { 1254 /* 1255 * Hostname but no path... 1256 */ 1257 1258 status = HTTP_URI_STATUS_MISSING_RESOURCE; 1259 *resource = '/'; 1260 1261 /* 1262 * Copy any query string... 1263 */ 1264 1265 if (*uri == '?') 1266 uri = http_copy_decode(resource + 1, uri, resourcelen - 1, NULL, 1267 decoding & HTTP_URI_CODING_QUERY); 1268 else 1269 resource[1] = '\0'; 1270 } 1271 else 1272 { 1273 uri = http_copy_decode(resource, uri, resourcelen, "?", 1274 decoding & HTTP_URI_CODING_RESOURCE); 1275 1276 if (uri && *uri == '?') 1277 { 1278 /* 1279 * Concatenate any query string... 1280 */ 1281 1282 char *resptr = resource + strlen(resource); 1283 1284 uri = http_copy_decode(resptr, uri, 1285 resourcelen - (int)(resptr - resource), NULL, 1286 decoding & HTTP_URI_CODING_QUERY); 1287 } 1288 } 1289 1290 if (!uri) 1291 { 1292 *resource = '\0'; 1293 return (HTTP_URI_STATUS_BAD_RESOURCE); 1294 } 1295 1296 /* 1297 * Return the URI separation status... 1298 */ 1299 1300 return (status); 1301 } 1302 1303 1304 /* 1305 * 'httpStateString()' - Return the string describing a HTTP state value. 1306 * 1307 * @since CUPS 2.0/OS 10.10@ 1308 */ 1309 1310 const char * /* O - State string */ 1311 httpStateString(http_state_t state) /* I - HTTP state value */ 1312 { 1313 if (state < HTTP_STATE_ERROR || state > HTTP_STATE_UNKNOWN_VERSION) 1314 return ("HTTP_STATE_???"); 1315 else 1316 return (http_states[state - HTTP_STATE_ERROR]); 1317 } 1318 1319 1320 /* 1321 * '_httpStatus()' - Return the localized string describing a HTTP status code. 1322 * 1323 * The returned string is localized using the passed message catalog. 1324 */ 1325 1326 const char * /* O - Localized status string */ 1327 _httpStatus(cups_lang_t *lang, /* I - Language */ 1328 http_status_t status) /* I - HTTP status code */ 1329 { 1330 const char *s; /* Status string */ 1331 1332 1333 switch (status) 1334 { 1335 case HTTP_STATUS_ERROR : 1336 s = strerror(errno); 1337 break; 1338 case HTTP_STATUS_CONTINUE : 1339 s = _("Continue"); 1340 break; 1341 case HTTP_STATUS_SWITCHING_PROTOCOLS : 1342 s = _("Switching Protocols"); 1343 break; 1344 case HTTP_STATUS_OK : 1345 s = _("OK"); 1346 break; 1347 case HTTP_STATUS_CREATED : 1348 s = _("Created"); 1349 break; 1350 case HTTP_STATUS_ACCEPTED : 1351 s = _("Accepted"); 1352 break; 1353 case HTTP_STATUS_NO_CONTENT : 1354 s = _("No Content"); 1355 break; 1356 case HTTP_STATUS_MOVED_PERMANENTLY : 1357 s = _("Moved Permanently"); 1358 break; 1359 case HTTP_STATUS_SEE_OTHER : 1360 s = _("See Other"); 1361 break; 1362 case HTTP_STATUS_NOT_MODIFIED : 1363 s = _("Not Modified"); 1364 break; 1365 case HTTP_STATUS_BAD_REQUEST : 1366 s = _("Bad Request"); 1367 break; 1368 case HTTP_STATUS_UNAUTHORIZED : 1369 case HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED : 1370 s = _("Unauthorized"); 1371 break; 1372 case HTTP_STATUS_FORBIDDEN : 1373 s = _("Forbidden"); 1374 break; 1375 case HTTP_STATUS_NOT_FOUND : 1376 s = _("Not Found"); 1377 break; 1378 case HTTP_STATUS_REQUEST_TOO_LARGE : 1379 s = _("Request Entity Too Large"); 1380 break; 1381 case HTTP_STATUS_URI_TOO_LONG : 1382 s = _("URI Too Long"); 1383 break; 1384 case HTTP_STATUS_UPGRADE_REQUIRED : 1385 s = _("Upgrade Required"); 1386 break; 1387 case HTTP_STATUS_NOT_IMPLEMENTED : 1388 s = _("Not Implemented"); 1389 break; 1390 case HTTP_STATUS_NOT_SUPPORTED : 1391 s = _("Not Supported"); 1392 break; 1393 case HTTP_STATUS_EXPECTATION_FAILED : 1394 s = _("Expectation Failed"); 1395 break; 1396 case HTTP_STATUS_SERVICE_UNAVAILABLE : 1397 s = _("Service Unavailable"); 1398 break; 1399 case HTTP_STATUS_SERVER_ERROR : 1400 s = _("Internal Server Error"); 1401 break; 1402 case HTTP_STATUS_CUPS_PKI_ERROR : 1403 s = _("SSL/TLS Negotiation Error"); 1404 break; 1405 case HTTP_STATUS_CUPS_WEBIF_DISABLED : 1406 s = _("Web Interface is Disabled"); 1407 break; 1408 1409 default : 1410 s = _("Unknown"); 1411 break; 1412 } 1413 1414 return (_cupsLangString(lang, s)); 1415 } 1416 1417 1418 /* 1419 * 'httpStatus()' - Return a short string describing a HTTP status code. 1420 * 1421 * The returned string is localized to the current POSIX locale and is based 1422 * on the status strings defined in RFC 2616. 1423 */ 1424 1425 const char * /* O - Localized status string */ 1426 httpStatus(http_status_t status) /* I - HTTP status code */ 1427 { 1428 _cups_globals_t *cg = _cupsGlobals(); /* Global data */ 1429 1430 1431 if (!cg->lang_default) 1432 cg->lang_default = cupsLangDefault(); 1433 1434 return (_httpStatus(cg->lang_default, status)); 1435 } 1436 1437 /* 1438 * 'httpURIStatusString()' - Return a string describing a URI status code. 1439 * 1440 * @since CUPS 2.0/OS 10.10@ 1441 */ 1442 1443 const char * /* O - Localized status string */ 1444 httpURIStatusString( 1445 http_uri_status_t status) /* I - URI status code */ 1446 { 1447 const char *s; /* Status string */ 1448 _cups_globals_t *cg = _cupsGlobals(); /* Global data */ 1449 1450 1451 if (!cg->lang_default) 1452 cg->lang_default = cupsLangDefault(); 1453 1454 switch (status) 1455 { 1456 case HTTP_URI_STATUS_OVERFLOW : 1457 s = _("URI too large"); 1458 break; 1459 case HTTP_URI_STATUS_BAD_ARGUMENTS : 1460 s = _("Bad arguments to function"); 1461 break; 1462 case HTTP_URI_STATUS_BAD_RESOURCE : 1463 s = _("Bad resource in URI"); 1464 break; 1465 case HTTP_URI_STATUS_BAD_PORT : 1466 s = _("Bad port number in URI"); 1467 break; 1468 case HTTP_URI_STATUS_BAD_HOSTNAME : 1469 s = _("Bad hostname/address in URI"); 1470 break; 1471 case HTTP_URI_STATUS_BAD_USERNAME : 1472 s = _("Bad username in URI"); 1473 break; 1474 case HTTP_URI_STATUS_BAD_SCHEME : 1475 s = _("Bad scheme in URI"); 1476 break; 1477 case HTTP_URI_STATUS_BAD_URI : 1478 s = _("Bad/empty URI"); 1479 break; 1480 case HTTP_URI_STATUS_OK : 1481 s = _("OK"); 1482 break; 1483 case HTTP_URI_STATUS_MISSING_SCHEME : 1484 s = _("Missing scheme in URI"); 1485 break; 1486 case HTTP_URI_STATUS_UNKNOWN_SCHEME : 1487 s = _("Unknown scheme in URI"); 1488 break; 1489 case HTTP_URI_STATUS_MISSING_RESOURCE : 1490 s = _("Missing resource in URI"); 1491 break; 1492 1493 default: 1494 s = _("Unknown"); 1495 break; 1496 } 1497 1498 return (_cupsLangString(cg->lang_default, s)); 1499 } 1500 1501 1502 #ifndef HAVE_HSTRERROR 1503 /* 1504 * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others. 1505 */ 1506 1507 const char * /* O - Error string */ 1508 _cups_hstrerror(int error) /* I - Error number */ 1509 { 1510 static const char * const errors[] = /* Error strings */ 1511 { 1512 "OK", 1513 "Host not found.", 1514 "Try again.", 1515 "Unrecoverable lookup error.", 1516 "No data associated with name." 1517 }; 1518 1519 1520 if (error < 0 || error > 4) 1521 return ("Unknown hostname lookup error."); 1522 else 1523 return (errors[error]); 1524 } 1525 #endif /* !HAVE_HSTRERROR */ 1526 1527 1528 /* 1529 * '_httpDecodeURI()' - Percent-decode a HTTP request URI. 1530 */ 1531 1532 char * /* O - Decoded URI or NULL on error */ 1533 _httpDecodeURI(char *dst, /* I - Destination buffer */ 1534 const char *src, /* I - Source URI */ 1535 size_t dstsize) /* I - Size of destination buffer */ 1536 { 1537 if (http_copy_decode(dst, src, (int)dstsize, NULL, 1)) 1538 return (dst); 1539 else 1540 return (NULL); 1541 } 1542 1543 1544 /* 1545 * '_httpEncodeURI()' - Percent-encode a HTTP request URI. 1546 */ 1547 1548 char * /* O - Encoded URI */ 1549 _httpEncodeURI(char *dst, /* I - Destination buffer */ 1550 const char *src, /* I - Source URI */ 1551 size_t dstsize) /* I - Size of destination buffer */ 1552 { 1553 http_copy_encode(dst, src, dst + dstsize - 1, NULL, NULL, 1); 1554 return (dst); 1555 } 1556 1557 1558 /* 1559 * '_httpResolveURI()' - Resolve a DNS-SD URI. 1560 */ 1561 1562 const char * /* O - Resolved URI */ 1563 _httpResolveURI( 1564 const char *uri, /* I - DNS-SD URI */ 1565 char *resolved_uri, /* I - Buffer for resolved URI */ 1566 size_t resolved_size, /* I - Size of URI buffer */ 1567 int options, /* I - Resolve options */ 1568 int (*cb)(void *context), /* I - Continue callback function */ 1569 void *context) /* I - Context pointer for callback */ 1570 { 1571 char scheme[32], /* URI components... */ 1572 userpass[256], 1573 hostname[1024], 1574 resource[1024]; 1575 int port; 1576 #ifdef DEBUG 1577 http_uri_status_t status; /* URI decode status */ 1578 #endif /* DEBUG */ 1579 1580 1581 DEBUG_printf(("_httpResolveURI(uri=\"%s\", resolved_uri=%p, resolved_size=" CUPS_LLFMT ", options=0x%x, cb=%p, context=%p)", uri, (void *)resolved_uri, CUPS_LLCAST resolved_size, options, (void *)cb, context)); 1582 1583 /* 1584 * Get the device URI... 1585 */ 1586 1587 #ifdef DEBUG 1588 if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 1589 sizeof(scheme), userpass, sizeof(userpass), 1590 hostname, sizeof(hostname), &port, resource, 1591 sizeof(resource))) < HTTP_URI_STATUS_OK) 1592 #else 1593 if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 1594 sizeof(scheme), userpass, sizeof(userpass), 1595 hostname, sizeof(hostname), &port, resource, 1596 sizeof(resource)) < HTTP_URI_STATUS_OK) 1597 #endif /* DEBUG */ 1598 { 1599 if (options & _HTTP_RESOLVE_STDERR) 1600 _cupsLangPrintFilter(stderr, "ERROR", _("Bad device-uri \"%s\"."), uri); 1601 1602 DEBUG_printf(("2_httpResolveURI: httpSeparateURI returned %d!", status)); 1603 DEBUG_puts("2_httpResolveURI: Returning NULL"); 1604 return (NULL); 1605 } 1606 1607 /* 1608 * Resolve it as needed... 1609 */ 1610 1611 if (strstr(hostname, "._tcp")) 1612 { 1613 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) 1614 char *regtype, /* Pointer to type in hostname */ 1615 *domain, /* Pointer to domain in hostname */ 1616 *uuid, /* Pointer to UUID in URI */ 1617 *uuidend; /* Pointer to end of UUID in URI */ 1618 _http_uribuf_t uribuf; /* URI buffer */ 1619 int offline = 0; /* offline-report state set? */ 1620 # ifdef HAVE_DNSSD 1621 # ifdef WIN32 1622 # pragma comment(lib, "dnssd.lib") 1623 # endif /* WIN32 */ 1624 DNSServiceRef ref, /* DNS-SD master service reference */ 1625 domainref = NULL,/* DNS-SD service reference for domain */ 1626 ippref = NULL, /* DNS-SD service reference for network IPP */ 1627 ippsref = NULL, /* DNS-SD service reference for network IPPS */ 1628 localref; /* DNS-SD service reference for .local */ 1629 int extrasent = 0; /* Send the domain/IPP/IPPS resolves? */ 1630 # ifdef HAVE_POLL 1631 struct pollfd polldata; /* Polling data */ 1632 # else /* select() */ 1633 fd_set input_set; /* Input set for select() */ 1634 struct timeval stimeout; /* Timeout value for select() */ 1635 # endif /* HAVE_POLL */ 1636 # elif defined(HAVE_AVAHI) 1637 AvahiClient *client; /* Client information */ 1638 int error; /* Status */ 1639 # endif /* HAVE_DNSSD */ 1640 1641 if (options & _HTTP_RESOLVE_STDERR) 1642 fprintf(stderr, "DEBUG: Resolving \"%s\"...\n", hostname); 1643 1644 /* 1645 * Separate the hostname into service name, registration type, and domain... 1646 */ 1647 1648 for (regtype = strstr(hostname, "._tcp") - 2; 1649 regtype > hostname; 1650 regtype --) 1651 if (regtype[0] == '.' && regtype[1] == '_') 1652 { 1653 /* 1654 * Found ._servicetype in front of ._tcp... 1655 */ 1656 1657 *regtype++ = '\0'; 1658 break; 1659 } 1660 1661 if (regtype <= hostname) 1662 { 1663 DEBUG_puts("2_httpResolveURI: Bad hostname, returning NULL"); 1664 return (NULL); 1665 } 1666 1667 for (domain = strchr(regtype, '.'); 1668 domain; 1669 domain = strchr(domain + 1, '.')) 1670 if (domain[1] != '_') 1671 break; 1672 1673 if (domain) 1674 *domain++ = '\0'; 1675 1676 if ((uuid = strstr(resource, "?uuid=")) != NULL) 1677 { 1678 *uuid = '\0'; 1679 uuid += 6; 1680 if ((uuidend = strchr(uuid, '&')) != NULL) 1681 *uuidend = '\0'; 1682 } 1683 1684 resolved_uri[0] = '\0'; 1685 1686 uribuf.buffer = resolved_uri; 1687 uribuf.bufsize = resolved_size; 1688 uribuf.options = options; 1689 uribuf.resource = resource; 1690 uribuf.uuid = uuid; 1691 1692 DEBUG_printf(("2_httpResolveURI: Resolving hostname=\"%s\", regtype=\"%s\", " 1693 "domain=\"%s\"\n", hostname, regtype, domain)); 1694 if (options & _HTTP_RESOLVE_STDERR) 1695 { 1696 fputs("STATE: +connecting-to-device\n", stderr); 1697 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"%s\", " 1698 "domain=\"local.\"...\n", hostname, regtype); 1699 } 1700 1701 uri = NULL; 1702 1703 # ifdef HAVE_DNSSD 1704 if (DNSServiceCreateConnection(&ref) == kDNSServiceErr_NoError) 1705 { 1706 uint32_t myinterface = kDNSServiceInterfaceIndexAny; 1707 /* Lookup on any interface */ 1708 1709 if (!strcmp(scheme, "ippusb")) 1710 myinterface = kDNSServiceInterfaceIndexLocalOnly; 1711 1712 localref = ref; 1713 if (DNSServiceResolve(&localref, 1714 kDNSServiceFlagsShareConnection, myinterface, 1715 hostname, regtype, "local.", http_resolve_cb, 1716 &uribuf) == kDNSServiceErr_NoError) 1717 { 1718 int fds; /* Number of ready descriptors */ 1719 time_t timeout, /* Poll timeout */ 1720 start_time = time(NULL),/* Start time */ 1721 end_time = start_time + 90; 1722 /* End time */ 1723 1724 while (time(NULL) < end_time) 1725 { 1726 if (options & _HTTP_RESOLVE_STDERR) 1727 _cupsLangPrintFilter(stderr, "INFO", _("Looking for printer.")); 1728 1729 if (cb && !(*cb)(context)) 1730 { 1731 DEBUG_puts("2_httpResolveURI: callback returned 0 (stop)"); 1732 break; 1733 } 1734 1735 /* 1736 * Wakeup every 2 seconds to emit a "looking for printer" message... 1737 */ 1738 1739 if ((timeout = end_time - time(NULL)) > 2) 1740 timeout = 2; 1741 1742 # ifdef HAVE_POLL 1743 polldata.fd = DNSServiceRefSockFD(ref); 1744 polldata.events = POLLIN; 1745 1746 fds = poll(&polldata, 1, (int)(1000 * timeout)); 1747 1748 # else /* select() */ 1749 FD_ZERO(&input_set); 1750 FD_SET(DNSServiceRefSockFD(ref), &input_set); 1751 1752 # ifdef WIN32 1753 stimeout.tv_sec = (long)timeout; 1754 # else 1755 stimeout.tv_sec = timeout; 1756 # endif /* WIN32 */ 1757 stimeout.tv_usec = 0; 1758 1759 fds = select(DNSServiceRefSockFD(ref)+1, &input_set, NULL, NULL, 1760 &stimeout); 1761 # endif /* HAVE_POLL */ 1762 1763 if (fds < 0) 1764 { 1765 if (errno != EINTR && errno != EAGAIN) 1766 { 1767 DEBUG_printf(("2_httpResolveURI: poll error: %s", strerror(errno))); 1768 break; 1769 } 1770 } 1771 else if (fds == 0) 1772 { 1773 /* 1774 * Wait 2 seconds for a response to the local resolve; if nothing 1775 * comes in, do an additional domain resolution... 1776 */ 1777 1778 if (extrasent == 0 && domain && _cups_strcasecmp(domain, "local.")) 1779 { 1780 if (options & _HTTP_RESOLVE_STDERR) 1781 fprintf(stderr, 1782 "DEBUG: Resolving \"%s\", regtype=\"%s\", " 1783 "domain=\"%s\"...\n", hostname, regtype, 1784 domain ? domain : ""); 1785 1786 domainref = ref; 1787 if (DNSServiceResolve(&domainref, 1788 kDNSServiceFlagsShareConnection, 1789 myinterface, hostname, regtype, domain, 1790 http_resolve_cb, 1791 &uribuf) == kDNSServiceErr_NoError) 1792 extrasent = 1; 1793 } 1794 else if (extrasent == 0 && !strcmp(scheme, "ippusb")) 1795 { 1796 if (options & _HTTP_RESOLVE_STDERR) 1797 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"_ipps._tcp\", domain=\"local.\"...\n", hostname); 1798 1799 ippsref = ref; 1800 if (DNSServiceResolve(&ippsref, 1801 kDNSServiceFlagsShareConnection, 1802 kDNSServiceInterfaceIndexAny, hostname, 1803 "_ipps._tcp", domain, http_resolve_cb, 1804 &uribuf) == kDNSServiceErr_NoError) 1805 extrasent = 1; 1806 } 1807 else if (extrasent == 1 && !strcmp(scheme, "ippusb")) 1808 { 1809 if (options & _HTTP_RESOLVE_STDERR) 1810 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"_ipp._tcp\", domain=\"local.\"...\n", hostname); 1811 1812 ippref = ref; 1813 if (DNSServiceResolve(&ippref, 1814 kDNSServiceFlagsShareConnection, 1815 kDNSServiceInterfaceIndexAny, hostname, 1816 "_ipp._tcp", domain, http_resolve_cb, 1817 &uribuf) == kDNSServiceErr_NoError) 1818 extrasent = 2; 1819 } 1820 1821 /* 1822 * If it hasn't resolved within 5 seconds set the offline-report 1823 * printer-state-reason... 1824 */ 1825 1826 if ((options & _HTTP_RESOLVE_STDERR) && offline == 0 && 1827 time(NULL) > (start_time + 5)) 1828 { 1829 fputs("STATE: +offline-report\n", stderr); 1830 offline = 1; 1831 } 1832 } 1833 else 1834 { 1835 if (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError && 1836 resolved_uri[0]) 1837 { 1838 uri = resolved_uri; 1839 break; 1840 } 1841 } 1842 } 1843 1844 if (extrasent) 1845 { 1846 if (domainref) 1847 DNSServiceRefDeallocate(domainref); 1848 if (ippref) 1849 DNSServiceRefDeallocate(ippref); 1850 if (ippsref) 1851 DNSServiceRefDeallocate(ippsref); 1852 } 1853 1854 DNSServiceRefDeallocate(localref); 1855 } 1856 1857 DNSServiceRefDeallocate(ref); 1858 } 1859 # else /* HAVE_AVAHI */ 1860 if ((uribuf.poll = avahi_simple_poll_new()) != NULL) 1861 { 1862 avahi_simple_poll_set_func(uribuf.poll, http_poll_cb, NULL); 1863 1864 if ((client = avahi_client_new(avahi_simple_poll_get(uribuf.poll), 1865 0, http_client_cb, 1866 &uribuf, &error)) != NULL) 1867 { 1868 if (avahi_service_resolver_new(client, AVAHI_IF_UNSPEC, 1869 AVAHI_PROTO_UNSPEC, hostname, 1870 regtype, "local.", AVAHI_PROTO_UNSPEC, 0, 1871 http_resolve_cb, &uribuf) != NULL) 1872 { 1873 time_t start_time = time(NULL), 1874 /* Start time */ 1875 end_time = start_time + 90; 1876 /* End time */ 1877 int pstatus; /* Poll status */ 1878 1879 pstatus = avahi_simple_poll_iterate(uribuf.poll, 2000); 1880 1881 if (pstatus == 0 && !resolved_uri[0] && domain && 1882 _cups_strcasecmp(domain, "local.")) 1883 { 1884 /* 1885 * Resolve for .local hasn't returned anything, try the listed 1886 * domain... 1887 */ 1888 1889 avahi_service_resolver_new(client, AVAHI_IF_UNSPEC, 1890 AVAHI_PROTO_UNSPEC, hostname, 1891 regtype, domain, AVAHI_PROTO_UNSPEC, 0, 1892 http_resolve_cb, &uribuf); 1893 } 1894 1895 while (!pstatus && !resolved_uri[0] && time(NULL) < end_time) 1896 { 1897 if ((pstatus = avahi_simple_poll_iterate(uribuf.poll, 2000)) != 0) 1898 break; 1899 1900 /* 1901 * If it hasn't resolved within 5 seconds set the offline-report 1902 * printer-state-reason... 1903 */ 1904 1905 if ((options & _HTTP_RESOLVE_STDERR) && offline == 0 && 1906 time(NULL) > (start_time + 5)) 1907 { 1908 fputs("STATE: +offline-report\n", stderr); 1909 offline = 1; 1910 } 1911 } 1912 1913 /* 1914 * Collect the result (if we got one). 1915 */ 1916 1917 if (resolved_uri[0]) 1918 uri = resolved_uri; 1919 } 1920 1921 avahi_client_free(client); 1922 } 1923 1924 avahi_simple_poll_free(uribuf.poll); 1925 } 1926 # endif /* HAVE_DNSSD */ 1927 1928 if (options & _HTTP_RESOLVE_STDERR) 1929 { 1930 if (uri) 1931 { 1932 fprintf(stderr, "DEBUG: Resolved as \"%s\"...\n", uri); 1933 fputs("STATE: -connecting-to-device,offline-report\n", stderr); 1934 } 1935 else 1936 { 1937 fputs("DEBUG: Unable to resolve URI\n", stderr); 1938 fputs("STATE: -connecting-to-device\n", stderr); 1939 } 1940 } 1941 1942 #else /* HAVE_DNSSD || HAVE_AVAHI */ 1943 /* 1944 * No DNS-SD support... 1945 */ 1946 1947 uri = NULL; 1948 #endif /* HAVE_DNSSD || HAVE_AVAHI */ 1949 1950 if ((options & _HTTP_RESOLVE_STDERR) && !uri) 1951 _cupsLangPrintFilter(stderr, "INFO", _("Unable to find printer.")); 1952 } 1953 else 1954 { 1955 /* 1956 * Nothing more to do... 1957 */ 1958 1959 strlcpy(resolved_uri, uri, resolved_size); 1960 uri = resolved_uri; 1961 } 1962 1963 DEBUG_printf(("2_httpResolveURI: Returning \"%s\"", uri)); 1964 1965 return (uri); 1966 } 1967 1968 1969 #ifdef HAVE_AVAHI 1970 /* 1971 * 'http_client_cb()' - Client callback for resolving URI. 1972 */ 1973 1974 static void 1975 http_client_cb( 1976 AvahiClient *client, /* I - Client information */ 1977 AvahiClientState state, /* I - Current state */ 1978 void *context) /* I - Pointer to URI buffer */ 1979 { 1980 DEBUG_printf(("7http_client_cb(client=%p, state=%d, context=%p)", client, 1981 state, context)); 1982 1983 /* 1984 * If the connection drops, quit. 1985 */ 1986 1987 if (state == AVAHI_CLIENT_FAILURE) 1988 { 1989 _http_uribuf_t *uribuf = (_http_uribuf_t *)context; 1990 /* URI buffer */ 1991 1992 avahi_simple_poll_quit(uribuf->poll); 1993 } 1994 } 1995 #endif /* HAVE_AVAHI */ 1996 1997 1998 /* 1999 * 'http_copy_decode()' - Copy and decode a URI. 2000 */ 2001 2002 static const char * /* O - New source pointer or NULL on error */ 2003 http_copy_decode(char *dst, /* O - Destination buffer */ 2004 const char *src, /* I - Source pointer */ 2005 int dstsize, /* I - Destination size */ 2006 const char *term, /* I - Terminating characters */ 2007 int decode) /* I - Decode %-encoded values */ 2008 { 2009 char *ptr, /* Pointer into buffer */ 2010 *end; /* End of buffer */ 2011 int quoted; /* Quoted character */ 2012 2013 2014 /* 2015 * Copy the src to the destination until we hit a terminating character 2016 * or the end of the string. 2017 */ 2018 2019 for (ptr = dst, end = dst + dstsize - 1; 2020 *src && (!term || !strchr(term, *src)); 2021 src ++) 2022 if (ptr < end) 2023 { 2024 if (*src == '%' && decode) 2025 { 2026 if (isxdigit(src[1] & 255) && isxdigit(src[2] & 255)) 2027 { 2028 /* 2029 * Grab a hex-encoded character... 2030 */ 2031 2032 src ++; 2033 if (isalpha(*src)) 2034 quoted = (tolower(*src) - 'a' + 10) << 4; 2035 else 2036 quoted = (*src - '0') << 4; 2037 2038 src ++; 2039 if (isalpha(*src)) 2040 quoted |= tolower(*src) - 'a' + 10; 2041 else 2042 quoted |= *src - '0'; 2043 2044 *ptr++ = (char)quoted; 2045 } 2046 else 2047 { 2048 /* 2049 * Bad hex-encoded character... 2050 */ 2051 2052 *ptr = '\0'; 2053 return (NULL); 2054 } 2055 } 2056 else if ((*src & 255) <= 0x20 || (*src & 255) >= 0x7f) 2057 { 2058 *ptr = '\0'; 2059 return (NULL); 2060 } 2061 else 2062 *ptr++ = *src; 2063 } 2064 2065 *ptr = '\0'; 2066 2067 return (src); 2068 } 2069 2070 2071 /* 2072 * 'http_copy_encode()' - Copy and encode a URI. 2073 */ 2074 2075 static char * /* O - End of current URI */ 2076 http_copy_encode(char *dst, /* O - Destination buffer */ 2077 const char *src, /* I - Source pointer */ 2078 char *dstend, /* I - End of destination buffer */ 2079 const char *reserved, /* I - Extra reserved characters */ 2080 const char *term, /* I - Terminating characters */ 2081 int encode) /* I - %-encode reserved chars? */ 2082 { 2083 static const char hex[] = "0123456789ABCDEF"; 2084 2085 2086 while (*src && dst < dstend) 2087 { 2088 if (term && *src == *term) 2089 return (dst); 2090 2091 if (encode && (*src == '%' || *src <= ' ' || *src & 128 || 2092 (reserved && strchr(reserved, *src)))) 2093 { 2094 /* 2095 * Hex encode reserved characters... 2096 */ 2097 2098 if ((dst + 2) >= dstend) 2099 break; 2100 2101 *dst++ = '%'; 2102 *dst++ = hex[(*src >> 4) & 15]; 2103 *dst++ = hex[*src & 15]; 2104 2105 src ++; 2106 } 2107 else 2108 *dst++ = *src++; 2109 } 2110 2111 *dst = '\0'; 2112 2113 if (*src) 2114 return (NULL); 2115 else 2116 return (dst); 2117 } 2118 2119 2120 #ifdef HAVE_DNSSD 2121 /* 2122 * 'http_resolve_cb()' - Build a device URI for the given service name. 2123 */ 2124 2125 static void DNSSD_API 2126 http_resolve_cb( 2127 DNSServiceRef sdRef, /* I - Service reference */ 2128 DNSServiceFlags flags, /* I - Results flags */ 2129 uint32_t interfaceIndex, /* I - Interface number */ 2130 DNSServiceErrorType errorCode, /* I - Error, if any */ 2131 const char *fullName, /* I - Full service name */ 2132 const char *hostTarget, /* I - Hostname */ 2133 uint16_t port, /* I - Port number */ 2134 uint16_t txtLen, /* I - Length of TXT record */ 2135 const unsigned char *txtRecord, /* I - TXT record data */ 2136 void *context) /* I - Pointer to URI buffer */ 2137 { 2138 _http_uribuf_t *uribuf = (_http_uribuf_t *)context; 2139 /* URI buffer */ 2140 const char *scheme, /* URI scheme */ 2141 *hostptr, /* Pointer into hostTarget */ 2142 *reskey, /* "rp" or "rfo" */ 2143 *resdefault; /* Default path */ 2144 char resource[257], /* Remote path */ 2145 fqdn[256]; /* FQDN of the .local name */ 2146 const void *value; /* Value from TXT record */ 2147 uint8_t valueLen; /* Length of value */ 2148 2149 2150 DEBUG_printf(("4http_resolve_cb(sdRef=%p, flags=%x, interfaceIndex=%u, errorCode=%d, fullName=\"%s\", hostTarget=\"%s\", port=%u, txtLen=%u, txtRecord=%p, context=%p)", (void *)sdRef, flags, interfaceIndex, errorCode, fullName, hostTarget, port, txtLen, (void *)txtRecord, context)); 2151 2152 /* 2153 * If we have a UUID, compare it... 2154 */ 2155 2156 if (uribuf->uuid && 2157 (value = TXTRecordGetValuePtr(txtLen, txtRecord, "UUID", 2158 &valueLen)) != NULL) 2159 { 2160 char uuid[256]; /* UUID value */ 2161 2162 memcpy(uuid, value, valueLen); 2163 uuid[valueLen] = '\0'; 2164 2165 if (_cups_strcasecmp(uuid, uribuf->uuid)) 2166 { 2167 if (uribuf->options & _HTTP_RESOLVE_STDERR) 2168 fprintf(stderr, "DEBUG: Found UUID %s, looking for %s.", uuid, 2169 uribuf->uuid); 2170 2171 DEBUG_printf(("5http_resolve_cb: Found UUID %s, looking for %s.", uuid, 2172 uribuf->uuid)); 2173 return; 2174 } 2175 } 2176 2177 /* 2178 * Figure out the scheme from the full name... 2179 */ 2180 2181 if (strstr(fullName, "._ipps") || strstr(fullName, "._ipp-tls")) 2182 scheme = "ipps"; 2183 else if (strstr(fullName, "._ipp") || strstr(fullName, "._fax-ipp")) 2184 scheme = "ipp"; 2185 else if (strstr(fullName, "._http.")) 2186 scheme = "http"; 2187 else if (strstr(fullName, "._https.")) 2188 scheme = "https"; 2189 else if (strstr(fullName, "._printer.")) 2190 scheme = "lpd"; 2191 else if (strstr(fullName, "._pdl-datastream.")) 2192 scheme = "socket"; 2193 else 2194 scheme = "riousbprint"; 2195 2196 /* 2197 * Extract the "remote printer" key from the TXT record... 2198 */ 2199 2200 if ((uribuf->options & _HTTP_RESOLVE_FAXOUT) && 2201 (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) && 2202 !TXTRecordGetValuePtr(txtLen, txtRecord, "printer-type", &valueLen)) 2203 { 2204 reskey = "rfo"; 2205 resdefault = "/ipp/faxout"; 2206 } 2207 else 2208 { 2209 reskey = "rp"; 2210 resdefault = "/"; 2211 } 2212 2213 if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, reskey, 2214 &valueLen)) != NULL) 2215 { 2216 if (((char *)value)[0] == '/') 2217 { 2218 /* 2219 * Value (incorrectly) has a leading slash already... 2220 */ 2221 2222 memcpy(resource, value, valueLen); 2223 resource[valueLen] = '\0'; 2224 } 2225 else 2226 { 2227 /* 2228 * Convert to resource by concatenating with a leading "/"... 2229 */ 2230 2231 resource[0] = '/'; 2232 memcpy(resource + 1, value, valueLen); 2233 resource[valueLen + 1] = '\0'; 2234 } 2235 } 2236 else 2237 { 2238 /* 2239 * Use the default value... 2240 */ 2241 2242 strlcpy(resource, resdefault, sizeof(resource)); 2243 } 2244 2245 /* 2246 * Lookup the FQDN if needed... 2247 */ 2248 2249 if ((uribuf->options & _HTTP_RESOLVE_FQDN) && 2250 (hostptr = hostTarget + strlen(hostTarget) - 7) > hostTarget && 2251 !_cups_strcasecmp(hostptr, ".local.")) 2252 { 2253 /* 2254 * OK, we got a .local name but the caller needs a real domain. Start by 2255 * getting the IP address of the .local name and then do reverse-lookups... 2256 */ 2257 2258 http_addrlist_t *addrlist, /* List of addresses */ 2259 *addr; /* Current address */ 2260 2261 DEBUG_printf(("5http_resolve_cb: Looking up \"%s\".", hostTarget)); 2262 2263 snprintf(fqdn, sizeof(fqdn), "%d", ntohs(port)); 2264 if ((addrlist = httpAddrGetList(hostTarget, AF_UNSPEC, fqdn)) != NULL) 2265 { 2266 for (addr = addrlist; addr; addr = addr->next) 2267 { 2268 int error = getnameinfo(&(addr->addr.addr), (socklen_t)httpAddrLength(&(addr->addr)), fqdn, sizeof(fqdn), NULL, 0, NI_NAMEREQD); 2269 2270 if (!error) 2271 { 2272 DEBUG_printf(("5http_resolve_cb: Found \"%s\".", fqdn)); 2273 2274 if ((hostptr = fqdn + strlen(fqdn) - 6) <= fqdn || 2275 _cups_strcasecmp(hostptr, ".local")) 2276 { 2277 hostTarget = fqdn; 2278 break; 2279 } 2280 } 2281 #ifdef DEBUG 2282 else 2283 DEBUG_printf(("5http_resolve_cb: \"%s\" did not resolve: %d", 2284 httpAddrString(&(addr->addr), fqdn, sizeof(fqdn)), 2285 error)); 2286 #endif /* DEBUG */ 2287 } 2288 2289 httpAddrFreeList(addrlist); 2290 } 2291 } 2292 2293 /* 2294 * Assemble the final device URI... 2295 */ 2296 2297 if ((!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) && 2298 !strcmp(uribuf->resource, "/cups")) 2299 httpAssembleURIf(HTTP_URI_CODING_ALL, uribuf->buffer, (int)uribuf->bufsize, scheme, NULL, hostTarget, ntohs(port), "%s?snmp=false", resource); 2300 else 2301 httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, (int)uribuf->bufsize, scheme, NULL, hostTarget, ntohs(port), resource); 2302 2303 DEBUG_printf(("5http_resolve_cb: Resolved URI is \"%s\"...", uribuf->buffer)); 2304 } 2305 2306 #elif defined(HAVE_AVAHI) 2307 /* 2308 * 'http_poll_cb()' - Wait for input on the specified file descriptors. 2309 * 2310 * Note: This function is needed because avahi_simple_poll_iterate is broken 2311 * and always uses a timeout of 0 (!) milliseconds. 2312 * (Avahi Ticket #364) 2313 */ 2314 2315 static int /* O - Number of file descriptors matching */ 2316 http_poll_cb( 2317 struct pollfd *pollfds, /* I - File descriptors */ 2318 unsigned int num_pollfds, /* I - Number of file descriptors */ 2319 int timeout, /* I - Timeout in milliseconds (used) */ 2320 void *context) /* I - User data (unused) */ 2321 { 2322 (void)timeout; 2323 (void)context; 2324 2325 return (poll(pollfds, num_pollfds, 2000)); 2326 } 2327 2328 2329 /* 2330 * 'http_resolve_cb()' - Build a device URI for the given service name. 2331 */ 2332 2333 static void 2334 http_resolve_cb( 2335 AvahiServiceResolver *resolver, /* I - Resolver (unused) */ 2336 AvahiIfIndex interface, /* I - Interface index (unused) */ 2337 AvahiProtocol protocol, /* I - Network protocol (unused) */ 2338 AvahiResolverEvent event, /* I - Event (found, etc.) */ 2339 const char *name, /* I - Service name */ 2340 const char *type, /* I - Registration type */ 2341 const char *domain, /* I - Domain (unused) */ 2342 const char *hostTarget, /* I - Hostname */ 2343 const AvahiAddress *address, /* I - Address (unused) */ 2344 uint16_t port, /* I - Port number */ 2345 AvahiStringList *txt, /* I - TXT record */ 2346 AvahiLookupResultFlags flags, /* I - Lookup flags (unused) */ 2347 void *context) /* I - Pointer to URI buffer */ 2348 { 2349 _http_uribuf_t *uribuf = (_http_uribuf_t *)context; 2350 /* URI buffer */ 2351 const char *scheme, /* URI scheme */ 2352 *hostptr, /* Pointer into hostTarget */ 2353 *reskey, /* "rp" or "rfo" */ 2354 *resdefault; /* Default path */ 2355 char resource[257], /* Remote path */ 2356 fqdn[256]; /* FQDN of the .local name */ 2357 AvahiStringList *pair; /* Current TXT record key/value pair */ 2358 char *value; /* Value for "rp" key */ 2359 size_t valueLen = 0; /* Length of "rp" key */ 2360 2361 2362 DEBUG_printf(("4http_resolve_cb(resolver=%p, " 2363 "interface=%d, protocol=%d, event=%d, name=\"%s\", " 2364 "type=\"%s\", domain=\"%s\", hostTarget=\"%s\", address=%p, " 2365 "port=%d, txt=%p, flags=%d, context=%p)", 2366 resolver, interface, protocol, event, name, type, domain, 2367 hostTarget, address, port, txt, flags, context)); 2368 2369 if (event != AVAHI_RESOLVER_FOUND) 2370 { 2371 avahi_service_resolver_free(resolver); 2372 avahi_simple_poll_quit(uribuf->poll); 2373 return; 2374 } 2375 2376 /* 2377 * If we have a UUID, compare it... 2378 */ 2379 2380 if (uribuf->uuid && (pair = avahi_string_list_find(txt, "UUID")) != NULL) 2381 { 2382 char uuid[256]; /* UUID value */ 2383 2384 avahi_string_list_get_pair(pair, NULL, &value, &valueLen); 2385 2386 memcpy(uuid, value, valueLen); 2387 uuid[valueLen] = '\0'; 2388 2389 if (_cups_strcasecmp(uuid, uribuf->uuid)) 2390 { 2391 if (uribuf->options & _HTTP_RESOLVE_STDERR) 2392 fprintf(stderr, "DEBUG: Found UUID %s, looking for %s.", uuid, 2393 uribuf->uuid); 2394 2395 DEBUG_printf(("5http_resolve_cb: Found UUID %s, looking for %s.", uuid, 2396 uribuf->uuid)); 2397 return; 2398 } 2399 } 2400 2401 /* 2402 * Figure out the scheme from the full name... 2403 */ 2404 2405 if (strstr(type, "_ipp.")) 2406 scheme = "ipp"; 2407 else if (strstr(type, "_printer.")) 2408 scheme = "lpd"; 2409 else if (strstr(type, "_pdl-datastream.")) 2410 scheme = "socket"; 2411 else 2412 scheme = "riousbprint"; 2413 2414 if (!strncmp(type, "_ipps.", 6) || !strncmp(type, "_ipp-tls.", 9)) 2415 scheme = "ipps"; 2416 else if (!strncmp(type, "_ipp.", 5) || !strncmp(type, "_fax-ipp.", 9)) 2417 scheme = "ipp"; 2418 else if (!strncmp(type, "_http.", 6)) 2419 scheme = "http"; 2420 else if (!strncmp(type, "_https.", 7)) 2421 scheme = "https"; 2422 else if (!strncmp(type, "_printer.", 9)) 2423 scheme = "lpd"; 2424 else if (!strncmp(type, "_pdl-datastream.", 16)) 2425 scheme = "socket"; 2426 else 2427 { 2428 avahi_service_resolver_free(resolver); 2429 avahi_simple_poll_quit(uribuf->poll); 2430 return; 2431 } 2432 2433 /* 2434 * Extract the remote resource key from the TXT record... 2435 */ 2436 2437 if ((uribuf->options & _HTTP_RESOLVE_FAXOUT) && 2438 (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) && 2439 !avahi_string_list_find(txt, "printer-type")) 2440 { 2441 reskey = "rfo"; 2442 resdefault = "/ipp/faxout"; 2443 } 2444 else 2445 { 2446 reskey = "rp"; 2447 resdefault = "/"; 2448 } 2449 2450 if ((pair = avahi_string_list_find(txt, reskey)) != NULL) 2451 { 2452 avahi_string_list_get_pair(pair, NULL, &value, &valueLen); 2453 2454 if (value[0] == '/') 2455 { 2456 /* 2457 * Value (incorrectly) has a leading slash already... 2458 */ 2459 2460 memcpy(resource, value, valueLen); 2461 resource[valueLen] = '\0'; 2462 } 2463 else 2464 { 2465 /* 2466 * Convert to resource by concatenating with a leading "/"... 2467 */ 2468 2469 resource[0] = '/'; 2470 memcpy(resource + 1, value, valueLen); 2471 resource[valueLen + 1] = '\0'; 2472 } 2473 } 2474 else 2475 { 2476 /* 2477 * Use the default value... 2478 */ 2479 2480 strlcpy(resource, resdefault, sizeof(resource)); 2481 } 2482 2483 /* 2484 * Lookup the FQDN if needed... 2485 */ 2486 2487 if ((uribuf->options & _HTTP_RESOLVE_FQDN) && 2488 (hostptr = hostTarget + strlen(hostTarget) - 6) > hostTarget && 2489 !_cups_strcasecmp(hostptr, ".local")) 2490 { 2491 /* 2492 * OK, we got a .local name but the caller needs a real domain. Start by 2493 * getting the IP address of the .local name and then do reverse-lookups... 2494 */ 2495 2496 http_addrlist_t *addrlist, /* List of addresses */ 2497 *addr; /* Current address */ 2498 2499 DEBUG_printf(("5http_resolve_cb: Looking up \"%s\".", hostTarget)); 2500 2501 snprintf(fqdn, sizeof(fqdn), "%d", ntohs(port)); 2502 if ((addrlist = httpAddrGetList(hostTarget, AF_UNSPEC, fqdn)) != NULL) 2503 { 2504 for (addr = addrlist; addr; addr = addr->next) 2505 { 2506 int error = getnameinfo(&(addr->addr.addr), (socklen_t)httpAddrLength(&(addr->addr)), fqdn, sizeof(fqdn), NULL, 0, NI_NAMEREQD); 2507 2508 if (!error) 2509 { 2510 DEBUG_printf(("5http_resolve_cb: Found \"%s\".", fqdn)); 2511 2512 if ((hostptr = fqdn + strlen(fqdn) - 6) <= fqdn || 2513 _cups_strcasecmp(hostptr, ".local")) 2514 { 2515 hostTarget = fqdn; 2516 break; 2517 } 2518 } 2519 #ifdef DEBUG 2520 else 2521 DEBUG_printf(("5http_resolve_cb: \"%s\" did not resolve: %d", 2522 httpAddrString(&(addr->addr), fqdn, sizeof(fqdn)), 2523 error)); 2524 #endif /* DEBUG */ 2525 } 2526 2527 httpAddrFreeList(addrlist); 2528 } 2529 } 2530 2531 /* 2532 * Assemble the final device URI using the resolved hostname... 2533 */ 2534 2535 httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, (int)uribuf->bufsize, scheme, 2536 NULL, hostTarget, port, resource); 2537 DEBUG_printf(("5http_resolve_cb: Resolved URI is \"%s\".", uribuf->buffer)); 2538 2539 avahi_simple_poll_quit(uribuf->poll); 2540 } 2541 #endif /* HAVE_DNSSD */ 2542