1 /* 2 * HTTP support routines for CUPS. 3 * 4 * Copyright 2007-2017 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@ @exclude all@ 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 * The caller must initialize "outlen" to the maximum size of the decoded 570 * string before calling @code httpDecode64_2@. On return "outlen" contains the 571 * decoded length of the string. 572 * 573 * @since CUPS 1.1.21/macOS 10.4@ 574 */ 575 576 char * /* O - Decoded string */ 577 httpDecode64_2(char *out, /* I - String to write to */ 578 int *outlen, /* IO - Size of output string */ 579 const char *in) /* I - String to read from */ 580 { 581 int pos; /* Bit position */ 582 unsigned base64; /* Value of this character */ 583 char *outptr, /* Output pointer */ 584 *outend; /* End of output buffer */ 585 586 587 /* 588 * Range check input... 589 */ 590 591 if (!out || !outlen || *outlen < 1 || !in) 592 return (NULL); 593 594 if (!*in) 595 { 596 *out = '\0'; 597 *outlen = 0; 598 599 return (out); 600 } 601 602 /* 603 * Convert from base-64 to bytes... 604 */ 605 606 for (outptr = out, outend = out + *outlen - 1, pos = 0; *in != '\0'; in ++) 607 { 608 /* 609 * Decode this character into a number from 0 to 63... 610 */ 611 612 if (*in >= 'A' && *in <= 'Z') 613 base64 = (unsigned)(*in - 'A'); 614 else if (*in >= 'a' && *in <= 'z') 615 base64 = (unsigned)(*in - 'a' + 26); 616 else if (*in >= '0' && *in <= '9') 617 base64 = (unsigned)(*in - '0' + 52); 618 else if (*in == '+') 619 base64 = 62; 620 else if (*in == '/') 621 base64 = 63; 622 else if (*in == '=') 623 break; 624 else 625 continue; 626 627 /* 628 * Store the result in the appropriate chars... 629 */ 630 631 switch (pos) 632 { 633 case 0 : 634 if (outptr < outend) 635 *outptr = (char)(base64 << 2); 636 pos ++; 637 break; 638 case 1 : 639 if (outptr < outend) 640 *outptr++ |= (char)((base64 >> 4) & 3); 641 if (outptr < outend) 642 *outptr = (char)((base64 << 4) & 255); 643 pos ++; 644 break; 645 case 2 : 646 if (outptr < outend) 647 *outptr++ |= (char)((base64 >> 2) & 15); 648 if (outptr < outend) 649 *outptr = (char)((base64 << 6) & 255); 650 pos ++; 651 break; 652 case 3 : 653 if (outptr < outend) 654 *outptr++ |= (char)base64; 655 pos = 0; 656 break; 657 } 658 } 659 660 *outptr = '\0'; 661 662 /* 663 * Return the decoded string and size... 664 */ 665 666 *outlen = (int)(outptr - out); 667 668 return (out); 669 } 670 671 672 /* 673 * 'httpEncode64()' - Base64-encode a string. 674 * 675 * This function is deprecated. Use the httpEncode64_2() function instead 676 * which provides buffer length arguments. 677 * 678 * @deprecated@ @exclude all@ 679 */ 680 681 char * /* O - Encoded string */ 682 httpEncode64(char *out, /* I - String to write to */ 683 const char *in) /* I - String to read from */ 684 { 685 return (httpEncode64_2(out, 512, in, (int)strlen(in))); 686 } 687 688 689 /* 690 * 'httpEncode64_2()' - Base64-encode a string. 691 * 692 * @since CUPS 1.1.21/macOS 10.4@ 693 */ 694 695 char * /* O - Encoded string */ 696 httpEncode64_2(char *out, /* I - String to write to */ 697 int outlen, /* I - Maximum size of output string */ 698 const char *in, /* I - String to read from */ 699 int inlen) /* I - Size of input string */ 700 { 701 char *outptr, /* Output pointer */ 702 *outend; /* End of output buffer */ 703 static const char base64[] = /* Base64 characters... */ 704 { 705 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 706 "abcdefghijklmnopqrstuvwxyz" 707 "0123456789" 708 "+/" 709 }; 710 711 712 /* 713 * Range check input... 714 */ 715 716 if (!out || outlen < 1 || !in) 717 return (NULL); 718 719 /* 720 * Convert bytes to base-64... 721 */ 722 723 for (outptr = out, outend = out + outlen - 1; inlen > 0; in ++, inlen --) 724 { 725 /* 726 * Encode the up to 3 characters as 4 Base64 numbers... 727 */ 728 729 if (outptr < outend) 730 *outptr ++ = base64[(in[0] & 255) >> 2]; 731 732 if (outptr < outend) 733 { 734 if (inlen > 1) 735 *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63]; 736 else 737 *outptr ++ = base64[((in[0] & 255) << 4) & 63]; 738 } 739 740 in ++; 741 inlen --; 742 if (inlen <= 0) 743 { 744 if (outptr < outend) 745 *outptr ++ = '='; 746 if (outptr < outend) 747 *outptr ++ = '='; 748 break; 749 } 750 751 if (outptr < outend) 752 { 753 if (inlen > 1) 754 *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63]; 755 else 756 *outptr ++ = base64[((in[0] & 255) << 2) & 63]; 757 } 758 759 in ++; 760 inlen --; 761 if (inlen <= 0) 762 { 763 if (outptr < outend) 764 *outptr ++ = '='; 765 break; 766 } 767 768 if (outptr < outend) 769 *outptr ++ = base64[in[0] & 63]; 770 } 771 772 *outptr = '\0'; 773 774 /* 775 * Return the encoded string... 776 */ 777 778 return (out); 779 } 780 781 782 /* 783 * 'httpGetDateString()' - Get a formatted date/time string from a time value. 784 * 785 * @deprecated@ @exclude all@ 786 */ 787 788 const char * /* O - Date/time string */ 789 httpGetDateString(time_t t) /* I - Time in seconds */ 790 { 791 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ 792 793 794 return (httpGetDateString2(t, cg->http_date, sizeof(cg->http_date))); 795 } 796 797 798 /* 799 * 'httpGetDateString2()' - Get a formatted date/time string from a time value. 800 * 801 * @since CUPS 1.2/macOS 10.5@ 802 */ 803 804 const char * /* O - Date/time string */ 805 httpGetDateString2(time_t t, /* I - Time in seconds */ 806 char *s, /* I - String buffer */ 807 int slen) /* I - Size of string buffer */ 808 { 809 struct tm *tdate; /* UNIX date/time data */ 810 811 812 tdate = gmtime(&t); 813 if (tdate) 814 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); 815 else 816 s[0] = '\0'; 817 818 return (s); 819 } 820 821 822 /* 823 * 'httpGetDateTime()' - Get a time value from a formatted date/time string. 824 */ 825 826 time_t /* O - Time in seconds */ 827 httpGetDateTime(const char *s) /* I - Date/time string */ 828 { 829 int i; /* Looping var */ 830 char mon[16]; /* Abbreviated month name */ 831 int day, year; /* Day of month and year */ 832 int hour, min, sec; /* Time */ 833 int days; /* Number of days since 1970 */ 834 static const int normal_days[] = /* Days to a month, normal years */ 835 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; 836 static const int leap_days[] = /* Days to a month, leap years */ 837 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; 838 839 840 DEBUG_printf(("2httpGetDateTime(s=\"%s\")", s)); 841 842 /* 843 * Extract the date and time from the formatted string... 844 */ 845 846 if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6) 847 return (0); 848 849 DEBUG_printf(("4httpGetDateTime: day=%d, mon=\"%s\", year=%d, hour=%d, " 850 "min=%d, sec=%d", day, mon, year, hour, min, sec)); 851 852 /* 853 * Convert the month name to a number from 0 to 11. 854 */ 855 856 for (i = 0; i < 12; i ++) 857 if (!_cups_strcasecmp(mon, http_months[i])) 858 break; 859 860 if (i >= 12) 861 return (0); 862 863 DEBUG_printf(("4httpGetDateTime: i=%d", i)); 864 865 /* 866 * Now convert the date and time to a UNIX time value in seconds since 867 * 1970. We can't use mktime() since the timezone may not be UTC but 868 * the date/time string *is* UTC. 869 */ 870 871 if ((year & 3) == 0 && ((year % 100) != 0 || (year % 400) == 0)) 872 days = leap_days[i] + day - 1; 873 else 874 days = normal_days[i] + day - 1; 875 876 DEBUG_printf(("4httpGetDateTime: days=%d", days)); 877 878 days += (year - 1970) * 365 + /* 365 days per year (normally) */ 879 ((year - 1) / 4 - 492) - /* + leap days */ 880 ((year - 1) / 100 - 19) + /* - 100 year days */ 881 ((year - 1) / 400 - 4); /* + 400 year days */ 882 883 DEBUG_printf(("4httpGetDateTime: days=%d\n", days)); 884 885 return (days * 86400 + hour * 3600 + min * 60 + sec); 886 } 887 888 889 /* 890 * 'httpSeparate()' - Separate a Universal Resource Identifier into its 891 * components. 892 * 893 * This function is deprecated; use the httpSeparateURI() function instead. 894 * 895 * @deprecated@ @exclude all@ 896 */ 897 898 void 899 httpSeparate(const char *uri, /* I - Universal Resource Identifier */ 900 char *scheme, /* O - Scheme [32] (http, https, etc.) */ 901 char *username, /* O - Username [1024] */ 902 char *host, /* O - Hostname [1024] */ 903 int *port, /* O - Port number to use */ 904 char *resource) /* O - Resource/filename [1024] */ 905 { 906 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 32, username, 907 HTTP_MAX_URI, host, HTTP_MAX_URI, port, resource, 908 HTTP_MAX_URI); 909 } 910 911 912 /* 913 * 'httpSeparate2()' - Separate a Universal Resource Identifier into its 914 * components. 915 * 916 * This function is deprecated; use the httpSeparateURI() function instead. 917 * 918 * @since CUPS 1.1.21/macOS 10.4@ 919 * @deprecated@ @exclude all@ 920 */ 921 922 void 923 httpSeparate2(const char *uri, /* I - Universal Resource Identifier */ 924 char *scheme, /* O - Scheme (http, https, etc.) */ 925 int schemelen, /* I - Size of scheme buffer */ 926 char *username, /* O - Username */ 927 int usernamelen, /* I - Size of username buffer */ 928 char *host, /* O - Hostname */ 929 int hostlen, /* I - Size of hostname buffer */ 930 int *port, /* O - Port number to use */ 931 char *resource, /* O - Resource/filename */ 932 int resourcelen) /* I - Size of resource buffer */ 933 { 934 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, schemelen, username, 935 usernamelen, host, hostlen, port, resource, resourcelen); 936 } 937 938 939 /* 940 * 'httpSeparateURI()' - Separate a Universal Resource Identifier into its 941 * components. 942 * 943 * @since CUPS 1.2/macOS 10.5@ 944 */ 945 946 http_uri_status_t /* O - Result of separation */ 947 httpSeparateURI( 948 http_uri_coding_t decoding, /* I - Decoding flags */ 949 const char *uri, /* I - Universal Resource Identifier */ 950 char *scheme, /* O - Scheme (http, https, etc.) */ 951 int schemelen, /* I - Size of scheme buffer */ 952 char *username, /* O - Username */ 953 int usernamelen, /* I - Size of username buffer */ 954 char *host, /* O - Hostname */ 955 int hostlen, /* I - Size of hostname buffer */ 956 int *port, /* O - Port number to use */ 957 char *resource, /* O - Resource/filename */ 958 int resourcelen) /* I - Size of resource buffer */ 959 { 960 char *ptr, /* Pointer into string... */ 961 *end; /* End of string */ 962 const char *sep; /* Separator character */ 963 http_uri_status_t status; /* Result of separation */ 964 965 966 /* 967 * Initialize everything to blank... 968 */ 969 970 if (scheme && schemelen > 0) 971 *scheme = '\0'; 972 973 if (username && usernamelen > 0) 974 *username = '\0'; 975 976 if (host && hostlen > 0) 977 *host = '\0'; 978 979 if (port) 980 *port = 0; 981 982 if (resource && resourcelen > 0) 983 *resource = '\0'; 984 985 /* 986 * Range check input... 987 */ 988 989 if (!uri || !port || !scheme || schemelen <= 0 || !username || 990 usernamelen <= 0 || !host || hostlen <= 0 || !resource || 991 resourcelen <= 0) 992 return (HTTP_URI_STATUS_BAD_ARGUMENTS); 993 994 if (!*uri) 995 return (HTTP_URI_STATUS_BAD_URI); 996 997 /* 998 * Grab the scheme portion of the URI... 999 */ 1000 1001 status = HTTP_URI_STATUS_OK; 1002 1003 if (!strncmp(uri, "//", 2)) 1004 { 1005 /* 1006 * Workaround for HP IPP client bug... 1007 */ 1008 1009 strlcpy(scheme, "ipp", (size_t)schemelen); 1010 status = HTTP_URI_STATUS_MISSING_SCHEME; 1011 } 1012 else if (*uri == '/') 1013 { 1014 /* 1015 * Filename... 1016 */ 1017 1018 strlcpy(scheme, "file", (size_t)schemelen); 1019 status = HTTP_URI_STATUS_MISSING_SCHEME; 1020 } 1021 else 1022 { 1023 /* 1024 * Standard URI with scheme... 1025 */ 1026 1027 for (ptr = scheme, end = scheme + schemelen - 1; 1028 *uri && *uri != ':' && ptr < end;) 1029 if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" 1030 "abcdefghijklmnopqrstuvwxyz" 1031 "0123456789-+.", *uri) != NULL) 1032 *ptr++ = *uri++; 1033 else 1034 break; 1035 1036 *ptr = '\0'; 1037 1038 if (*uri != ':') 1039 { 1040 *scheme = '\0'; 1041 return (HTTP_URI_STATUS_BAD_SCHEME); 1042 } 1043 1044 uri ++; 1045 } 1046 1047 /* 1048 * Set the default port number... 1049 */ 1050 1051 if (!strcmp(scheme, "http")) 1052 *port = 80; 1053 else if (!strcmp(scheme, "https")) 1054 *port = 443; 1055 else if (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) 1056 *port = 631; 1057 else if (!_cups_strcasecmp(scheme, "lpd")) 1058 *port = 515; 1059 else if (!strcmp(scheme, "socket")) /* Not yet registered with IANA... */ 1060 *port = 9100; 1061 else if (strcmp(scheme, "file") && strcmp(scheme, "mailto") && strcmp(scheme, "tel")) 1062 status = HTTP_URI_STATUS_UNKNOWN_SCHEME; 1063 1064 /* 1065 * Now see if we have a hostname... 1066 */ 1067 1068 if (!strncmp(uri, "//", 2)) 1069 { 1070 /* 1071 * Yes, extract it... 1072 */ 1073 1074 uri += 2; 1075 1076 /* 1077 * Grab the username, if any... 1078 */ 1079 1080 if ((sep = strpbrk(uri, "@/")) != NULL && *sep == '@') 1081 { 1082 /* 1083 * Get a username:password combo... 1084 */ 1085 1086 uri = http_copy_decode(username, uri, usernamelen, "@", 1087 decoding & HTTP_URI_CODING_USERNAME); 1088 1089 if (!uri) 1090 { 1091 *username = '\0'; 1092 return (HTTP_URI_STATUS_BAD_USERNAME); 1093 } 1094 1095 uri ++; 1096 } 1097 1098 /* 1099 * Then the hostname/IP address... 1100 */ 1101 1102 if (*uri == '[') 1103 { 1104 /* 1105 * Grab IPv6 address... 1106 */ 1107 1108 uri ++; 1109 if (*uri == 'v') 1110 { 1111 /* 1112 * Skip IPvFuture ("vXXXX.") prefix... 1113 */ 1114 1115 uri ++; 1116 1117 while (isxdigit(*uri & 255)) 1118 uri ++; 1119 1120 if (*uri != '.') 1121 { 1122 *host = '\0'; 1123 return (HTTP_URI_STATUS_BAD_HOSTNAME); 1124 } 1125 1126 uri ++; 1127 } 1128 1129 uri = http_copy_decode(host, uri, hostlen, "]", 1130 decoding & HTTP_URI_CODING_HOSTNAME); 1131 1132 if (!uri) 1133 { 1134 *host = '\0'; 1135 return (HTTP_URI_STATUS_BAD_HOSTNAME); 1136 } 1137 1138 /* 1139 * Validate value... 1140 */ 1141 1142 if (*uri != ']') 1143 { 1144 *host = '\0'; 1145 return (HTTP_URI_STATUS_BAD_HOSTNAME); 1146 } 1147 1148 uri ++; 1149 1150 for (ptr = host; *ptr; ptr ++) 1151 if (*ptr == '+') 1152 { 1153 /* 1154 * Convert zone separator to % and stop here... 1155 */ 1156 1157 *ptr = '%'; 1158 break; 1159 } 1160 else if (*ptr == '%') 1161 { 1162 /* 1163 * Stop at zone separator (RFC 6874) 1164 */ 1165 1166 break; 1167 } 1168 else if (*ptr != ':' && *ptr != '.' && !isxdigit(*ptr & 255)) 1169 { 1170 *host = '\0'; 1171 return (HTTP_URI_STATUS_BAD_HOSTNAME); 1172 } 1173 } 1174 else 1175 { 1176 /* 1177 * Validate the hostname or IPv4 address first... 1178 */ 1179 1180 for (ptr = (char *)uri; *ptr; ptr ++) 1181 if (strchr(":?/", *ptr)) 1182 break; 1183 else if (!strchr("abcdefghijklmnopqrstuvwxyz" /* unreserved */ 1184 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* unreserved */ 1185 "0123456789" /* unreserved */ 1186 "-._~" /* unreserved */ 1187 "%" /* pct-encoded */ 1188 "!$&'()*+,;=" /* sub-delims */ 1189 "\\", *ptr)) /* SMB domain */ 1190 { 1191 *host = '\0'; 1192 return (HTTP_URI_STATUS_BAD_HOSTNAME); 1193 } 1194 1195 /* 1196 * Then copy the hostname or IPv4 address to the buffer... 1197 */ 1198 1199 uri = http_copy_decode(host, uri, hostlen, ":?/", 1200 decoding & HTTP_URI_CODING_HOSTNAME); 1201 1202 if (!uri) 1203 { 1204 *host = '\0'; 1205 return (HTTP_URI_STATUS_BAD_HOSTNAME); 1206 } 1207 } 1208 1209 /* 1210 * Validate hostname for file scheme - only empty and localhost are 1211 * acceptable. 1212 */ 1213 1214 if (!strcmp(scheme, "file") && strcmp(host, "localhost") && host[0]) 1215 { 1216 *host = '\0'; 1217 return (HTTP_URI_STATUS_BAD_HOSTNAME); 1218 } 1219 1220 /* 1221 * See if we have a port number... 1222 */ 1223 1224 if (*uri == ':') 1225 { 1226 /* 1227 * Yes, collect the port number... 1228 */ 1229 1230 if (!isdigit(uri[1] & 255)) 1231 { 1232 *port = 0; 1233 return (HTTP_URI_STATUS_BAD_PORT); 1234 } 1235 1236 *port = (int)strtol(uri + 1, (char **)&uri, 10); 1237 1238 if (*port <= 0 || *port > 65535) 1239 { 1240 *port = 0; 1241 return (HTTP_URI_STATUS_BAD_PORT); 1242 } 1243 1244 if (*uri != '/' && *uri) 1245 { 1246 *port = 0; 1247 return (HTTP_URI_STATUS_BAD_PORT); 1248 } 1249 } 1250 } 1251 1252 /* 1253 * The remaining portion is the resource string... 1254 */ 1255 1256 if (*uri == '?' || !*uri) 1257 { 1258 /* 1259 * Hostname but no path... 1260 */ 1261 1262 status = HTTP_URI_STATUS_MISSING_RESOURCE; 1263 *resource = '/'; 1264 1265 /* 1266 * Copy any query string... 1267 */ 1268 1269 if (*uri == '?') 1270 uri = http_copy_decode(resource + 1, uri, resourcelen - 1, NULL, 1271 decoding & HTTP_URI_CODING_QUERY); 1272 else 1273 resource[1] = '\0'; 1274 } 1275 else 1276 { 1277 uri = http_copy_decode(resource, uri, resourcelen, "?", 1278 decoding & HTTP_URI_CODING_RESOURCE); 1279 1280 if (uri && *uri == '?') 1281 { 1282 /* 1283 * Concatenate any query string... 1284 */ 1285 1286 char *resptr = resource + strlen(resource); 1287 1288 uri = http_copy_decode(resptr, uri, 1289 resourcelen - (int)(resptr - resource), NULL, 1290 decoding & HTTP_URI_CODING_QUERY); 1291 } 1292 } 1293 1294 if (!uri) 1295 { 1296 *resource = '\0'; 1297 return (HTTP_URI_STATUS_BAD_RESOURCE); 1298 } 1299 1300 /* 1301 * Return the URI separation status... 1302 */ 1303 1304 return (status); 1305 } 1306 1307 1308 /* 1309 * 'httpStateString()' - Return the string describing a HTTP state value. 1310 * 1311 * @since CUPS 2.0/OS 10.10@ 1312 */ 1313 1314 const char * /* O - State string */ 1315 httpStateString(http_state_t state) /* I - HTTP state value */ 1316 { 1317 if (state < HTTP_STATE_ERROR || state > HTTP_STATE_UNKNOWN_VERSION) 1318 return ("HTTP_STATE_???"); 1319 else 1320 return (http_states[state - HTTP_STATE_ERROR]); 1321 } 1322 1323 1324 /* 1325 * '_httpStatus()' - Return the localized string describing a HTTP status code. 1326 * 1327 * The returned string is localized using the passed message catalog. 1328 */ 1329 1330 const char * /* O - Localized status string */ 1331 _httpStatus(cups_lang_t *lang, /* I - Language */ 1332 http_status_t status) /* I - HTTP status code */ 1333 { 1334 const char *s; /* Status string */ 1335 1336 1337 switch (status) 1338 { 1339 case HTTP_STATUS_ERROR : 1340 s = strerror(errno); 1341 break; 1342 case HTTP_STATUS_CONTINUE : 1343 s = _("Continue"); 1344 break; 1345 case HTTP_STATUS_SWITCHING_PROTOCOLS : 1346 s = _("Switching Protocols"); 1347 break; 1348 case HTTP_STATUS_OK : 1349 s = _("OK"); 1350 break; 1351 case HTTP_STATUS_CREATED : 1352 s = _("Created"); 1353 break; 1354 case HTTP_STATUS_ACCEPTED : 1355 s = _("Accepted"); 1356 break; 1357 case HTTP_STATUS_NO_CONTENT : 1358 s = _("No Content"); 1359 break; 1360 case HTTP_STATUS_MOVED_PERMANENTLY : 1361 s = _("Moved Permanently"); 1362 break; 1363 case HTTP_STATUS_SEE_OTHER : 1364 s = _("See Other"); 1365 break; 1366 case HTTP_STATUS_NOT_MODIFIED : 1367 s = _("Not Modified"); 1368 break; 1369 case HTTP_STATUS_BAD_REQUEST : 1370 s = _("Bad Request"); 1371 break; 1372 case HTTP_STATUS_UNAUTHORIZED : 1373 case HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED : 1374 s = _("Unauthorized"); 1375 break; 1376 case HTTP_STATUS_FORBIDDEN : 1377 s = _("Forbidden"); 1378 break; 1379 case HTTP_STATUS_NOT_FOUND : 1380 s = _("Not Found"); 1381 break; 1382 case HTTP_STATUS_REQUEST_TOO_LARGE : 1383 s = _("Request Entity Too Large"); 1384 break; 1385 case HTTP_STATUS_URI_TOO_LONG : 1386 s = _("URI Too Long"); 1387 break; 1388 case HTTP_STATUS_UPGRADE_REQUIRED : 1389 s = _("Upgrade Required"); 1390 break; 1391 case HTTP_STATUS_NOT_IMPLEMENTED : 1392 s = _("Not Implemented"); 1393 break; 1394 case HTTP_STATUS_NOT_SUPPORTED : 1395 s = _("Not Supported"); 1396 break; 1397 case HTTP_STATUS_EXPECTATION_FAILED : 1398 s = _("Expectation Failed"); 1399 break; 1400 case HTTP_STATUS_SERVICE_UNAVAILABLE : 1401 s = _("Service Unavailable"); 1402 break; 1403 case HTTP_STATUS_SERVER_ERROR : 1404 s = _("Internal Server Error"); 1405 break; 1406 case HTTP_STATUS_CUPS_PKI_ERROR : 1407 s = _("SSL/TLS Negotiation Error"); 1408 break; 1409 case HTTP_STATUS_CUPS_WEBIF_DISABLED : 1410 s = _("Web Interface is Disabled"); 1411 break; 1412 1413 default : 1414 s = _("Unknown"); 1415 break; 1416 } 1417 1418 return (_cupsLangString(lang, s)); 1419 } 1420 1421 1422 /* 1423 * 'httpStatus()' - Return a short string describing a HTTP status code. 1424 * 1425 * The returned string is localized to the current POSIX locale and is based 1426 * on the status strings defined in RFC 7231. 1427 */ 1428 1429 const char * /* O - Localized status string */ 1430 httpStatus(http_status_t status) /* I - HTTP status code */ 1431 { 1432 _cups_globals_t *cg = _cupsGlobals(); /* Global data */ 1433 1434 1435 if (!cg->lang_default) 1436 cg->lang_default = cupsLangDefault(); 1437 1438 return (_httpStatus(cg->lang_default, status)); 1439 } 1440 1441 /* 1442 * 'httpURIStatusString()' - Return a string describing a URI status code. 1443 * 1444 * @since CUPS 2.0/OS 10.10@ 1445 */ 1446 1447 const char * /* O - Localized status string */ 1448 httpURIStatusString( 1449 http_uri_status_t status) /* I - URI status code */ 1450 { 1451 const char *s; /* Status string */ 1452 _cups_globals_t *cg = _cupsGlobals(); /* Global data */ 1453 1454 1455 if (!cg->lang_default) 1456 cg->lang_default = cupsLangDefault(); 1457 1458 switch (status) 1459 { 1460 case HTTP_URI_STATUS_OVERFLOW : 1461 s = _("URI too large"); 1462 break; 1463 case HTTP_URI_STATUS_BAD_ARGUMENTS : 1464 s = _("Bad arguments to function"); 1465 break; 1466 case HTTP_URI_STATUS_BAD_RESOURCE : 1467 s = _("Bad resource in URI"); 1468 break; 1469 case HTTP_URI_STATUS_BAD_PORT : 1470 s = _("Bad port number in URI"); 1471 break; 1472 case HTTP_URI_STATUS_BAD_HOSTNAME : 1473 s = _("Bad hostname/address in URI"); 1474 break; 1475 case HTTP_URI_STATUS_BAD_USERNAME : 1476 s = _("Bad username in URI"); 1477 break; 1478 case HTTP_URI_STATUS_BAD_SCHEME : 1479 s = _("Bad scheme in URI"); 1480 break; 1481 case HTTP_URI_STATUS_BAD_URI : 1482 s = _("Bad/empty URI"); 1483 break; 1484 case HTTP_URI_STATUS_OK : 1485 s = _("OK"); 1486 break; 1487 case HTTP_URI_STATUS_MISSING_SCHEME : 1488 s = _("Missing scheme in URI"); 1489 break; 1490 case HTTP_URI_STATUS_UNKNOWN_SCHEME : 1491 s = _("Unknown scheme in URI"); 1492 break; 1493 case HTTP_URI_STATUS_MISSING_RESOURCE : 1494 s = _("Missing resource in URI"); 1495 break; 1496 1497 default: 1498 s = _("Unknown"); 1499 break; 1500 } 1501 1502 return (_cupsLangString(cg->lang_default, s)); 1503 } 1504 1505 1506 #ifndef HAVE_HSTRERROR 1507 /* 1508 * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others. 1509 */ 1510 1511 const char * /* O - Error string */ 1512 _cups_hstrerror(int error) /* I - Error number */ 1513 { 1514 static const char * const errors[] = /* Error strings */ 1515 { 1516 "OK", 1517 "Host not found.", 1518 "Try again.", 1519 "Unrecoverable lookup error.", 1520 "No data associated with name." 1521 }; 1522 1523 1524 if (error < 0 || error > 4) 1525 return ("Unknown hostname lookup error."); 1526 else 1527 return (errors[error]); 1528 } 1529 #endif /* !HAVE_HSTRERROR */ 1530 1531 1532 /* 1533 * '_httpDecodeURI()' - Percent-decode a HTTP request URI. 1534 */ 1535 1536 char * /* O - Decoded URI or NULL on error */ 1537 _httpDecodeURI(char *dst, /* I - Destination buffer */ 1538 const char *src, /* I - Source URI */ 1539 size_t dstsize) /* I - Size of destination buffer */ 1540 { 1541 if (http_copy_decode(dst, src, (int)dstsize, NULL, 1)) 1542 return (dst); 1543 else 1544 return (NULL); 1545 } 1546 1547 1548 /* 1549 * '_httpEncodeURI()' - Percent-encode a HTTP request URI. 1550 */ 1551 1552 char * /* O - Encoded URI */ 1553 _httpEncodeURI(char *dst, /* I - Destination buffer */ 1554 const char *src, /* I - Source URI */ 1555 size_t dstsize) /* I - Size of destination buffer */ 1556 { 1557 http_copy_encode(dst, src, dst + dstsize - 1, NULL, NULL, 1); 1558 return (dst); 1559 } 1560 1561 1562 /* 1563 * '_httpResolveURI()' - Resolve a DNS-SD URI. 1564 */ 1565 1566 const char * /* O - Resolved URI */ 1567 _httpResolveURI( 1568 const char *uri, /* I - DNS-SD URI */ 1569 char *resolved_uri, /* I - Buffer for resolved URI */ 1570 size_t resolved_size, /* I - Size of URI buffer */ 1571 int options, /* I - Resolve options */ 1572 int (*cb)(void *context), /* I - Continue callback function */ 1573 void *context) /* I - Context pointer for callback */ 1574 { 1575 char scheme[32], /* URI components... */ 1576 userpass[256], 1577 hostname[1024], 1578 resource[1024]; 1579 int port; 1580 #ifdef DEBUG 1581 http_uri_status_t status; /* URI decode status */ 1582 #endif /* DEBUG */ 1583 1584 1585 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)); 1586 1587 /* 1588 * Get the device URI... 1589 */ 1590 1591 #ifdef DEBUG 1592 if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 1593 sizeof(scheme), userpass, sizeof(userpass), 1594 hostname, sizeof(hostname), &port, resource, 1595 sizeof(resource))) < HTTP_URI_STATUS_OK) 1596 #else 1597 if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 1598 sizeof(scheme), userpass, sizeof(userpass), 1599 hostname, sizeof(hostname), &port, resource, 1600 sizeof(resource)) < HTTP_URI_STATUS_OK) 1601 #endif /* DEBUG */ 1602 { 1603 if (options & _HTTP_RESOLVE_STDERR) 1604 _cupsLangPrintFilter(stderr, "ERROR", _("Bad device-uri \"%s\"."), uri); 1605 1606 DEBUG_printf(("2_httpResolveURI: httpSeparateURI returned %d!", status)); 1607 DEBUG_puts("2_httpResolveURI: Returning NULL"); 1608 return (NULL); 1609 } 1610 1611 /* 1612 * Resolve it as needed... 1613 */ 1614 1615 if (strstr(hostname, "._tcp")) 1616 { 1617 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) 1618 char *regtype, /* Pointer to type in hostname */ 1619 *domain, /* Pointer to domain in hostname */ 1620 *uuid, /* Pointer to UUID in URI */ 1621 *uuidend; /* Pointer to end of UUID in URI */ 1622 _http_uribuf_t uribuf; /* URI buffer */ 1623 int offline = 0; /* offline-report state set? */ 1624 # ifdef HAVE_DNSSD 1625 # ifdef WIN32 1626 # pragma comment(lib, "dnssd.lib") 1627 # endif /* WIN32 */ 1628 DNSServiceRef ref, /* DNS-SD master service reference */ 1629 domainref = NULL,/* DNS-SD service reference for domain */ 1630 ippref = NULL, /* DNS-SD service reference for network IPP */ 1631 ippsref = NULL, /* DNS-SD service reference for network IPPS */ 1632 localref; /* DNS-SD service reference for .local */ 1633 int extrasent = 0; /* Send the domain/IPP/IPPS resolves? */ 1634 # ifdef HAVE_POLL 1635 struct pollfd polldata; /* Polling data */ 1636 # else /* select() */ 1637 fd_set input_set; /* Input set for select() */ 1638 struct timeval stimeout; /* Timeout value for select() */ 1639 # endif /* HAVE_POLL */ 1640 # elif defined(HAVE_AVAHI) 1641 AvahiClient *client; /* Client information */ 1642 int error; /* Status */ 1643 # endif /* HAVE_DNSSD */ 1644 1645 if (options & _HTTP_RESOLVE_STDERR) 1646 fprintf(stderr, "DEBUG: Resolving \"%s\"...\n", hostname); 1647 1648 /* 1649 * Separate the hostname into service name, registration type, and domain... 1650 */ 1651 1652 for (regtype = strstr(hostname, "._tcp") - 2; 1653 regtype > hostname; 1654 regtype --) 1655 if (regtype[0] == '.' && regtype[1] == '_') 1656 { 1657 /* 1658 * Found ._servicetype in front of ._tcp... 1659 */ 1660 1661 *regtype++ = '\0'; 1662 break; 1663 } 1664 1665 if (regtype <= hostname) 1666 { 1667 DEBUG_puts("2_httpResolveURI: Bad hostname, returning NULL"); 1668 return (NULL); 1669 } 1670 1671 for (domain = strchr(regtype, '.'); 1672 domain; 1673 domain = strchr(domain + 1, '.')) 1674 if (domain[1] != '_') 1675 break; 1676 1677 if (domain) 1678 *domain++ = '\0'; 1679 1680 if ((uuid = strstr(resource, "?uuid=")) != NULL) 1681 { 1682 *uuid = '\0'; 1683 uuid += 6; 1684 if ((uuidend = strchr(uuid, '&')) != NULL) 1685 *uuidend = '\0'; 1686 } 1687 1688 resolved_uri[0] = '\0'; 1689 1690 uribuf.buffer = resolved_uri; 1691 uribuf.bufsize = resolved_size; 1692 uribuf.options = options; 1693 uribuf.resource = resource; 1694 uribuf.uuid = uuid; 1695 1696 DEBUG_printf(("2_httpResolveURI: Resolving hostname=\"%s\", regtype=\"%s\", " 1697 "domain=\"%s\"\n", hostname, regtype, domain)); 1698 if (options & _HTTP_RESOLVE_STDERR) 1699 { 1700 fputs("STATE: +connecting-to-device\n", stderr); 1701 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"%s\", " 1702 "domain=\"local.\"...\n", hostname, regtype); 1703 } 1704 1705 uri = NULL; 1706 1707 # ifdef HAVE_DNSSD 1708 if (DNSServiceCreateConnection(&ref) == kDNSServiceErr_NoError) 1709 { 1710 uint32_t myinterface = kDNSServiceInterfaceIndexAny; 1711 /* Lookup on any interface */ 1712 1713 if (!strcmp(scheme, "ippusb")) 1714 myinterface = kDNSServiceInterfaceIndexLocalOnly; 1715 1716 localref = ref; 1717 if (DNSServiceResolve(&localref, 1718 kDNSServiceFlagsShareConnection, myinterface, 1719 hostname, regtype, "local.", http_resolve_cb, 1720 &uribuf) == kDNSServiceErr_NoError) 1721 { 1722 int fds; /* Number of ready descriptors */ 1723 time_t timeout, /* Poll timeout */ 1724 start_time = time(NULL),/* Start time */ 1725 end_time = start_time + 90; 1726 /* End time */ 1727 1728 while (time(NULL) < end_time) 1729 { 1730 if (options & _HTTP_RESOLVE_STDERR) 1731 _cupsLangPrintFilter(stderr, "INFO", _("Looking for printer.")); 1732 1733 if (cb && !(*cb)(context)) 1734 { 1735 DEBUG_puts("2_httpResolveURI: callback returned 0 (stop)"); 1736 break; 1737 } 1738 1739 /* 1740 * Wakeup every 2 seconds to emit a "looking for printer" message... 1741 */ 1742 1743 if ((timeout = end_time - time(NULL)) > 2) 1744 timeout = 2; 1745 1746 # ifdef HAVE_POLL 1747 polldata.fd = DNSServiceRefSockFD(ref); 1748 polldata.events = POLLIN; 1749 1750 fds = poll(&polldata, 1, (int)(1000 * timeout)); 1751 1752 # else /* select() */ 1753 FD_ZERO(&input_set); 1754 FD_SET(DNSServiceRefSockFD(ref), &input_set); 1755 1756 # ifdef WIN32 1757 stimeout.tv_sec = (long)timeout; 1758 # else 1759 stimeout.tv_sec = timeout; 1760 # endif /* WIN32 */ 1761 stimeout.tv_usec = 0; 1762 1763 fds = select(DNSServiceRefSockFD(ref)+1, &input_set, NULL, NULL, 1764 &stimeout); 1765 # endif /* HAVE_POLL */ 1766 1767 if (fds < 0) 1768 { 1769 if (errno != EINTR && errno != EAGAIN) 1770 { 1771 DEBUG_printf(("2_httpResolveURI: poll error: %s", strerror(errno))); 1772 break; 1773 } 1774 } 1775 else if (fds == 0) 1776 { 1777 /* 1778 * Wait 2 seconds for a response to the local resolve; if nothing 1779 * comes in, do an additional domain resolution... 1780 */ 1781 1782 if (extrasent == 0 && domain && _cups_strcasecmp(domain, "local.")) 1783 { 1784 if (options & _HTTP_RESOLVE_STDERR) 1785 fprintf(stderr, 1786 "DEBUG: Resolving \"%s\", regtype=\"%s\", " 1787 "domain=\"%s\"...\n", hostname, regtype, 1788 domain ? domain : ""); 1789 1790 domainref = ref; 1791 if (DNSServiceResolve(&domainref, 1792 kDNSServiceFlagsShareConnection, 1793 myinterface, hostname, regtype, domain, 1794 http_resolve_cb, 1795 &uribuf) == kDNSServiceErr_NoError) 1796 extrasent = 1; 1797 } 1798 else if (extrasent == 0 && !strcmp(scheme, "ippusb")) 1799 { 1800 if (options & _HTTP_RESOLVE_STDERR) 1801 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"_ipps._tcp\", domain=\"local.\"...\n", hostname); 1802 1803 ippsref = ref; 1804 if (DNSServiceResolve(&ippsref, 1805 kDNSServiceFlagsShareConnection, 1806 kDNSServiceInterfaceIndexAny, hostname, 1807 "_ipps._tcp", domain, http_resolve_cb, 1808 &uribuf) == kDNSServiceErr_NoError) 1809 extrasent = 1; 1810 } 1811 else if (extrasent == 1 && !strcmp(scheme, "ippusb")) 1812 { 1813 if (options & _HTTP_RESOLVE_STDERR) 1814 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"_ipp._tcp\", domain=\"local.\"...\n", hostname); 1815 1816 ippref = ref; 1817 if (DNSServiceResolve(&ippref, 1818 kDNSServiceFlagsShareConnection, 1819 kDNSServiceInterfaceIndexAny, hostname, 1820 "_ipp._tcp", domain, http_resolve_cb, 1821 &uribuf) == kDNSServiceErr_NoError) 1822 extrasent = 2; 1823 } 1824 1825 /* 1826 * If it hasn't resolved within 5 seconds set the offline-report 1827 * printer-state-reason... 1828 */ 1829 1830 if ((options & _HTTP_RESOLVE_STDERR) && offline == 0 && 1831 time(NULL) > (start_time + 5)) 1832 { 1833 fputs("STATE: +offline-report\n", stderr); 1834 offline = 1; 1835 } 1836 } 1837 else 1838 { 1839 if (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError && 1840 resolved_uri[0]) 1841 { 1842 uri = resolved_uri; 1843 break; 1844 } 1845 } 1846 } 1847 1848 if (extrasent) 1849 { 1850 if (domainref) 1851 DNSServiceRefDeallocate(domainref); 1852 if (ippref) 1853 DNSServiceRefDeallocate(ippref); 1854 if (ippsref) 1855 DNSServiceRefDeallocate(ippsref); 1856 } 1857 1858 DNSServiceRefDeallocate(localref); 1859 } 1860 1861 DNSServiceRefDeallocate(ref); 1862 } 1863 # else /* HAVE_AVAHI */ 1864 if ((uribuf.poll = avahi_simple_poll_new()) != NULL) 1865 { 1866 avahi_simple_poll_set_func(uribuf.poll, http_poll_cb, NULL); 1867 1868 if ((client = avahi_client_new(avahi_simple_poll_get(uribuf.poll), 1869 0, http_client_cb, 1870 &uribuf, &error)) != NULL) 1871 { 1872 if (avahi_service_resolver_new(client, AVAHI_IF_UNSPEC, 1873 AVAHI_PROTO_UNSPEC, hostname, 1874 regtype, "local.", AVAHI_PROTO_UNSPEC, 0, 1875 http_resolve_cb, &uribuf) != NULL) 1876 { 1877 time_t start_time = time(NULL), 1878 /* Start time */ 1879 end_time = start_time + 90; 1880 /* End time */ 1881 int pstatus; /* Poll status */ 1882 1883 pstatus = avahi_simple_poll_iterate(uribuf.poll, 2000); 1884 1885 if (pstatus == 0 && !resolved_uri[0] && domain && 1886 _cups_strcasecmp(domain, "local.")) 1887 { 1888 /* 1889 * Resolve for .local hasn't returned anything, try the listed 1890 * domain... 1891 */ 1892 1893 avahi_service_resolver_new(client, AVAHI_IF_UNSPEC, 1894 AVAHI_PROTO_UNSPEC, hostname, 1895 regtype, domain, AVAHI_PROTO_UNSPEC, 0, 1896 http_resolve_cb, &uribuf); 1897 } 1898 1899 while (!pstatus && !resolved_uri[0] && time(NULL) < end_time) 1900 { 1901 if ((pstatus = avahi_simple_poll_iterate(uribuf.poll, 2000)) != 0) 1902 break; 1903 1904 /* 1905 * If it hasn't resolved within 5 seconds set the offline-report 1906 * printer-state-reason... 1907 */ 1908 1909 if ((options & _HTTP_RESOLVE_STDERR) && offline == 0 && 1910 time(NULL) > (start_time + 5)) 1911 { 1912 fputs("STATE: +offline-report\n", stderr); 1913 offline = 1; 1914 } 1915 } 1916 1917 /* 1918 * Collect the result (if we got one). 1919 */ 1920 1921 if (resolved_uri[0]) 1922 uri = resolved_uri; 1923 } 1924 1925 avahi_client_free(client); 1926 } 1927 1928 avahi_simple_poll_free(uribuf.poll); 1929 } 1930 # endif /* HAVE_DNSSD */ 1931 1932 if (options & _HTTP_RESOLVE_STDERR) 1933 { 1934 if (uri) 1935 { 1936 fprintf(stderr, "DEBUG: Resolved as \"%s\"...\n", uri); 1937 fputs("STATE: -connecting-to-device,offline-report\n", stderr); 1938 } 1939 else 1940 { 1941 fputs("DEBUG: Unable to resolve URI\n", stderr); 1942 fputs("STATE: -connecting-to-device\n", stderr); 1943 } 1944 } 1945 1946 #else /* HAVE_DNSSD || HAVE_AVAHI */ 1947 /* 1948 * No DNS-SD support... 1949 */ 1950 1951 uri = NULL; 1952 #endif /* HAVE_DNSSD || HAVE_AVAHI */ 1953 1954 if ((options & _HTTP_RESOLVE_STDERR) && !uri) 1955 _cupsLangPrintFilter(stderr, "INFO", _("Unable to find printer.")); 1956 } 1957 else 1958 { 1959 /* 1960 * Nothing more to do... 1961 */ 1962 1963 strlcpy(resolved_uri, uri, resolved_size); 1964 uri = resolved_uri; 1965 } 1966 1967 DEBUG_printf(("2_httpResolveURI: Returning \"%s\"", uri)); 1968 1969 return (uri); 1970 } 1971 1972 1973 #ifdef HAVE_AVAHI 1974 /* 1975 * 'http_client_cb()' - Client callback for resolving URI. 1976 */ 1977 1978 static void 1979 http_client_cb( 1980 AvahiClient *client, /* I - Client information */ 1981 AvahiClientState state, /* I - Current state */ 1982 void *context) /* I - Pointer to URI buffer */ 1983 { 1984 DEBUG_printf(("7http_client_cb(client=%p, state=%d, context=%p)", client, 1985 state, context)); 1986 1987 /* 1988 * If the connection drops, quit. 1989 */ 1990 1991 if (state == AVAHI_CLIENT_FAILURE) 1992 { 1993 _http_uribuf_t *uribuf = (_http_uribuf_t *)context; 1994 /* URI buffer */ 1995 1996 avahi_simple_poll_quit(uribuf->poll); 1997 } 1998 } 1999 #endif /* HAVE_AVAHI */ 2000 2001 2002 /* 2003 * 'http_copy_decode()' - Copy and decode a URI. 2004 */ 2005 2006 static const char * /* O - New source pointer or NULL on error */ 2007 http_copy_decode(char *dst, /* O - Destination buffer */ 2008 const char *src, /* I - Source pointer */ 2009 int dstsize, /* I - Destination size */ 2010 const char *term, /* I - Terminating characters */ 2011 int decode) /* I - Decode %-encoded values */ 2012 { 2013 char *ptr, /* Pointer into buffer */ 2014 *end; /* End of buffer */ 2015 int quoted; /* Quoted character */ 2016 2017 2018 /* 2019 * Copy the src to the destination until we hit a terminating character 2020 * or the end of the string. 2021 */ 2022 2023 for (ptr = dst, end = dst + dstsize - 1; 2024 *src && (!term || !strchr(term, *src)); 2025 src ++) 2026 if (ptr < end) 2027 { 2028 if (*src == '%' && decode) 2029 { 2030 if (isxdigit(src[1] & 255) && isxdigit(src[2] & 255)) 2031 { 2032 /* 2033 * Grab a hex-encoded character... 2034 */ 2035 2036 src ++; 2037 if (isalpha(*src)) 2038 quoted = (tolower(*src) - 'a' + 10) << 4; 2039 else 2040 quoted = (*src - '0') << 4; 2041 2042 src ++; 2043 if (isalpha(*src)) 2044 quoted |= tolower(*src) - 'a' + 10; 2045 else 2046 quoted |= *src - '0'; 2047 2048 *ptr++ = (char)quoted; 2049 } 2050 else 2051 { 2052 /* 2053 * Bad hex-encoded character... 2054 */ 2055 2056 *ptr = '\0'; 2057 return (NULL); 2058 } 2059 } 2060 else if ((*src & 255) <= 0x20 || (*src & 255) >= 0x7f) 2061 { 2062 *ptr = '\0'; 2063 return (NULL); 2064 } 2065 else 2066 *ptr++ = *src; 2067 } 2068 2069 *ptr = '\0'; 2070 2071 return (src); 2072 } 2073 2074 2075 /* 2076 * 'http_copy_encode()' - Copy and encode a URI. 2077 */ 2078 2079 static char * /* O - End of current URI */ 2080 http_copy_encode(char *dst, /* O - Destination buffer */ 2081 const char *src, /* I - Source pointer */ 2082 char *dstend, /* I - End of destination buffer */ 2083 const char *reserved, /* I - Extra reserved characters */ 2084 const char *term, /* I - Terminating characters */ 2085 int encode) /* I - %-encode reserved chars? */ 2086 { 2087 static const char hex[] = "0123456789ABCDEF"; 2088 2089 2090 while (*src && dst < dstend) 2091 { 2092 if (term && *src == *term) 2093 return (dst); 2094 2095 if (encode && (*src == '%' || *src <= ' ' || *src & 128 || 2096 (reserved && strchr(reserved, *src)))) 2097 { 2098 /* 2099 * Hex encode reserved characters... 2100 */ 2101 2102 if ((dst + 2) >= dstend) 2103 break; 2104 2105 *dst++ = '%'; 2106 *dst++ = hex[(*src >> 4) & 15]; 2107 *dst++ = hex[*src & 15]; 2108 2109 src ++; 2110 } 2111 else 2112 *dst++ = *src++; 2113 } 2114 2115 *dst = '\0'; 2116 2117 if (*src) 2118 return (NULL); 2119 else 2120 return (dst); 2121 } 2122 2123 2124 #ifdef HAVE_DNSSD 2125 /* 2126 * 'http_resolve_cb()' - Build a device URI for the given service name. 2127 */ 2128 2129 static void DNSSD_API 2130 http_resolve_cb( 2131 DNSServiceRef sdRef, /* I - Service reference */ 2132 DNSServiceFlags flags, /* I - Results flags */ 2133 uint32_t interfaceIndex, /* I - Interface number */ 2134 DNSServiceErrorType errorCode, /* I - Error, if any */ 2135 const char *fullName, /* I - Full service name */ 2136 const char *hostTarget, /* I - Hostname */ 2137 uint16_t port, /* I - Port number */ 2138 uint16_t txtLen, /* I - Length of TXT record */ 2139 const unsigned char *txtRecord, /* I - TXT record data */ 2140 void *context) /* I - Pointer to URI buffer */ 2141 { 2142 _http_uribuf_t *uribuf = (_http_uribuf_t *)context; 2143 /* URI buffer */ 2144 const char *scheme, /* URI scheme */ 2145 *hostptr, /* Pointer into hostTarget */ 2146 *reskey, /* "rp" or "rfo" */ 2147 *resdefault; /* Default path */ 2148 char resource[257], /* Remote path */ 2149 fqdn[256]; /* FQDN of the .local name */ 2150 const void *value; /* Value from TXT record */ 2151 uint8_t valueLen; /* Length of value */ 2152 2153 2154 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)); 2155 2156 /* 2157 * If we have a UUID, compare it... 2158 */ 2159 2160 if (uribuf->uuid && 2161 (value = TXTRecordGetValuePtr(txtLen, txtRecord, "UUID", 2162 &valueLen)) != NULL) 2163 { 2164 char uuid[256]; /* UUID value */ 2165 2166 memcpy(uuid, value, valueLen); 2167 uuid[valueLen] = '\0'; 2168 2169 if (_cups_strcasecmp(uuid, uribuf->uuid)) 2170 { 2171 if (uribuf->options & _HTTP_RESOLVE_STDERR) 2172 fprintf(stderr, "DEBUG: Found UUID %s, looking for %s.", uuid, 2173 uribuf->uuid); 2174 2175 DEBUG_printf(("5http_resolve_cb: Found UUID %s, looking for %s.", uuid, 2176 uribuf->uuid)); 2177 return; 2178 } 2179 } 2180 2181 /* 2182 * Figure out the scheme from the full name... 2183 */ 2184 2185 if (strstr(fullName, "._ipps") || strstr(fullName, "._ipp-tls")) 2186 scheme = "ipps"; 2187 else if (strstr(fullName, "._ipp") || strstr(fullName, "._fax-ipp")) 2188 scheme = "ipp"; 2189 else if (strstr(fullName, "._http.")) 2190 scheme = "http"; 2191 else if (strstr(fullName, "._https.")) 2192 scheme = "https"; 2193 else if (strstr(fullName, "._printer.")) 2194 scheme = "lpd"; 2195 else if (strstr(fullName, "._pdl-datastream.")) 2196 scheme = "socket"; 2197 else 2198 scheme = "riousbprint"; 2199 2200 /* 2201 * Extract the "remote printer" key from the TXT record... 2202 */ 2203 2204 if ((uribuf->options & _HTTP_RESOLVE_FAXOUT) && 2205 (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) && 2206 !TXTRecordGetValuePtr(txtLen, txtRecord, "printer-type", &valueLen)) 2207 { 2208 reskey = "rfo"; 2209 resdefault = "/ipp/faxout"; 2210 } 2211 else 2212 { 2213 reskey = "rp"; 2214 resdefault = "/"; 2215 } 2216 2217 if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, reskey, 2218 &valueLen)) != NULL) 2219 { 2220 if (((char *)value)[0] == '/') 2221 { 2222 /* 2223 * Value (incorrectly) has a leading slash already... 2224 */ 2225 2226 memcpy(resource, value, valueLen); 2227 resource[valueLen] = '\0'; 2228 } 2229 else 2230 { 2231 /* 2232 * Convert to resource by concatenating with a leading "/"... 2233 */ 2234 2235 resource[0] = '/'; 2236 memcpy(resource + 1, value, valueLen); 2237 resource[valueLen + 1] = '\0'; 2238 } 2239 } 2240 else 2241 { 2242 /* 2243 * Use the default value... 2244 */ 2245 2246 strlcpy(resource, resdefault, sizeof(resource)); 2247 } 2248 2249 /* 2250 * Lookup the FQDN if needed... 2251 */ 2252 2253 if ((uribuf->options & _HTTP_RESOLVE_FQDN) && 2254 (hostptr = hostTarget + strlen(hostTarget) - 7) > hostTarget && 2255 !_cups_strcasecmp(hostptr, ".local.")) 2256 { 2257 /* 2258 * OK, we got a .local name but the caller needs a real domain. Start by 2259 * getting the IP address of the .local name and then do reverse-lookups... 2260 */ 2261 2262 http_addrlist_t *addrlist, /* List of addresses */ 2263 *addr; /* Current address */ 2264 2265 DEBUG_printf(("5http_resolve_cb: Looking up \"%s\".", hostTarget)); 2266 2267 snprintf(fqdn, sizeof(fqdn), "%d", ntohs(port)); 2268 if ((addrlist = httpAddrGetList(hostTarget, AF_UNSPEC, fqdn)) != NULL) 2269 { 2270 for (addr = addrlist; addr; addr = addr->next) 2271 { 2272 int error = getnameinfo(&(addr->addr.addr), (socklen_t)httpAddrLength(&(addr->addr)), fqdn, sizeof(fqdn), NULL, 0, NI_NAMEREQD); 2273 2274 if (!error) 2275 { 2276 DEBUG_printf(("5http_resolve_cb: Found \"%s\".", fqdn)); 2277 2278 if ((hostptr = fqdn + strlen(fqdn) - 6) <= fqdn || 2279 _cups_strcasecmp(hostptr, ".local")) 2280 { 2281 hostTarget = fqdn; 2282 break; 2283 } 2284 } 2285 #ifdef DEBUG 2286 else 2287 DEBUG_printf(("5http_resolve_cb: \"%s\" did not resolve: %d", 2288 httpAddrString(&(addr->addr), fqdn, sizeof(fqdn)), 2289 error)); 2290 #endif /* DEBUG */ 2291 } 2292 2293 httpAddrFreeList(addrlist); 2294 } 2295 } 2296 2297 /* 2298 * Assemble the final device URI... 2299 */ 2300 2301 if ((!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) && 2302 !strcmp(uribuf->resource, "/cups")) 2303 httpAssembleURIf(HTTP_URI_CODING_ALL, uribuf->buffer, (int)uribuf->bufsize, scheme, NULL, hostTarget, ntohs(port), "%s?snmp=false", resource); 2304 else 2305 httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, (int)uribuf->bufsize, scheme, NULL, hostTarget, ntohs(port), resource); 2306 2307 DEBUG_printf(("5http_resolve_cb: Resolved URI is \"%s\"...", uribuf->buffer)); 2308 } 2309 2310 #elif defined(HAVE_AVAHI) 2311 /* 2312 * 'http_poll_cb()' - Wait for input on the specified file descriptors. 2313 * 2314 * Note: This function is needed because avahi_simple_poll_iterate is broken 2315 * and always uses a timeout of 0 (!) milliseconds. 2316 * (Avahi Ticket #364) 2317 * 2318 * @private@ 2319 */ 2320 2321 static int /* O - Number of file descriptors matching */ 2322 http_poll_cb( 2323 struct pollfd *pollfds, /* I - File descriptors */ 2324 unsigned int num_pollfds, /* I - Number of file descriptors */ 2325 int timeout, /* I - Timeout in milliseconds (used) */ 2326 void *context) /* I - User data (unused) */ 2327 { 2328 (void)timeout; 2329 (void)context; 2330 2331 return (poll(pollfds, num_pollfds, 2000)); 2332 } 2333 2334 2335 /* 2336 * 'http_resolve_cb()' - Build a device URI for the given service name. 2337 */ 2338 2339 static void 2340 http_resolve_cb( 2341 AvahiServiceResolver *resolver, /* I - Resolver (unused) */ 2342 AvahiIfIndex interface, /* I - Interface index (unused) */ 2343 AvahiProtocol protocol, /* I - Network protocol (unused) */ 2344 AvahiResolverEvent event, /* I - Event (found, etc.) */ 2345 const char *name, /* I - Service name */ 2346 const char *type, /* I - Registration type */ 2347 const char *domain, /* I - Domain (unused) */ 2348 const char *hostTarget, /* I - Hostname */ 2349 const AvahiAddress *address, /* I - Address (unused) */ 2350 uint16_t port, /* I - Port number */ 2351 AvahiStringList *txt, /* I - TXT record */ 2352 AvahiLookupResultFlags flags, /* I - Lookup flags (unused) */ 2353 void *context) /* I - Pointer to URI buffer */ 2354 { 2355 _http_uribuf_t *uribuf = (_http_uribuf_t *)context; 2356 /* URI buffer */ 2357 const char *scheme, /* URI scheme */ 2358 *hostptr, /* Pointer into hostTarget */ 2359 *reskey, /* "rp" or "rfo" */ 2360 *resdefault; /* Default path */ 2361 char resource[257], /* Remote path */ 2362 fqdn[256]; /* FQDN of the .local name */ 2363 AvahiStringList *pair; /* Current TXT record key/value pair */ 2364 char *value; /* Value for "rp" key */ 2365 size_t valueLen = 0; /* Length of "rp" key */ 2366 2367 2368 DEBUG_printf(("4http_resolve_cb(resolver=%p, " 2369 "interface=%d, protocol=%d, event=%d, name=\"%s\", " 2370 "type=\"%s\", domain=\"%s\", hostTarget=\"%s\", address=%p, " 2371 "port=%d, txt=%p, flags=%d, context=%p)", 2372 resolver, interface, protocol, event, name, type, domain, 2373 hostTarget, address, port, txt, flags, context)); 2374 2375 if (event != AVAHI_RESOLVER_FOUND) 2376 { 2377 avahi_service_resolver_free(resolver); 2378 avahi_simple_poll_quit(uribuf->poll); 2379 return; 2380 } 2381 2382 /* 2383 * If we have a UUID, compare it... 2384 */ 2385 2386 if (uribuf->uuid && (pair = avahi_string_list_find(txt, "UUID")) != NULL) 2387 { 2388 char uuid[256]; /* UUID value */ 2389 2390 avahi_string_list_get_pair(pair, NULL, &value, &valueLen); 2391 2392 memcpy(uuid, value, valueLen); 2393 uuid[valueLen] = '\0'; 2394 2395 if (_cups_strcasecmp(uuid, uribuf->uuid)) 2396 { 2397 if (uribuf->options & _HTTP_RESOLVE_STDERR) 2398 fprintf(stderr, "DEBUG: Found UUID %s, looking for %s.", uuid, 2399 uribuf->uuid); 2400 2401 DEBUG_printf(("5http_resolve_cb: Found UUID %s, looking for %s.", uuid, 2402 uribuf->uuid)); 2403 return; 2404 } 2405 } 2406 2407 /* 2408 * Figure out the scheme from the full name... 2409 */ 2410 2411 if (strstr(type, "_ipp.")) 2412 scheme = "ipp"; 2413 else if (strstr(type, "_printer.")) 2414 scheme = "lpd"; 2415 else if (strstr(type, "_pdl-datastream.")) 2416 scheme = "socket"; 2417 else 2418 scheme = "riousbprint"; 2419 2420 if (!strncmp(type, "_ipps.", 6) || !strncmp(type, "_ipp-tls.", 9)) 2421 scheme = "ipps"; 2422 else if (!strncmp(type, "_ipp.", 5) || !strncmp(type, "_fax-ipp.", 9)) 2423 scheme = "ipp"; 2424 else if (!strncmp(type, "_http.", 6)) 2425 scheme = "http"; 2426 else if (!strncmp(type, "_https.", 7)) 2427 scheme = "https"; 2428 else if (!strncmp(type, "_printer.", 9)) 2429 scheme = "lpd"; 2430 else if (!strncmp(type, "_pdl-datastream.", 16)) 2431 scheme = "socket"; 2432 else 2433 { 2434 avahi_service_resolver_free(resolver); 2435 avahi_simple_poll_quit(uribuf->poll); 2436 return; 2437 } 2438 2439 /* 2440 * Extract the remote resource key from the TXT record... 2441 */ 2442 2443 if ((uribuf->options & _HTTP_RESOLVE_FAXOUT) && 2444 (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) && 2445 !avahi_string_list_find(txt, "printer-type")) 2446 { 2447 reskey = "rfo"; 2448 resdefault = "/ipp/faxout"; 2449 } 2450 else 2451 { 2452 reskey = "rp"; 2453 resdefault = "/"; 2454 } 2455 2456 if ((pair = avahi_string_list_find(txt, reskey)) != NULL) 2457 { 2458 avahi_string_list_get_pair(pair, NULL, &value, &valueLen); 2459 2460 if (value[0] == '/') 2461 { 2462 /* 2463 * Value (incorrectly) has a leading slash already... 2464 */ 2465 2466 memcpy(resource, value, valueLen); 2467 resource[valueLen] = '\0'; 2468 } 2469 else 2470 { 2471 /* 2472 * Convert to resource by concatenating with a leading "/"... 2473 */ 2474 2475 resource[0] = '/'; 2476 memcpy(resource + 1, value, valueLen); 2477 resource[valueLen + 1] = '\0'; 2478 } 2479 } 2480 else 2481 { 2482 /* 2483 * Use the default value... 2484 */ 2485 2486 strlcpy(resource, resdefault, sizeof(resource)); 2487 } 2488 2489 /* 2490 * Lookup the FQDN if needed... 2491 */ 2492 2493 if ((uribuf->options & _HTTP_RESOLVE_FQDN) && 2494 (hostptr = hostTarget + strlen(hostTarget) - 6) > hostTarget && 2495 !_cups_strcasecmp(hostptr, ".local")) 2496 { 2497 /* 2498 * OK, we got a .local name but the caller needs a real domain. Start by 2499 * getting the IP address of the .local name and then do reverse-lookups... 2500 */ 2501 2502 http_addrlist_t *addrlist, /* List of addresses */ 2503 *addr; /* Current address */ 2504 2505 DEBUG_printf(("5http_resolve_cb: Looking up \"%s\".", hostTarget)); 2506 2507 snprintf(fqdn, sizeof(fqdn), "%d", ntohs(port)); 2508 if ((addrlist = httpAddrGetList(hostTarget, AF_UNSPEC, fqdn)) != NULL) 2509 { 2510 for (addr = addrlist; addr; addr = addr->next) 2511 { 2512 int error = getnameinfo(&(addr->addr.addr), (socklen_t)httpAddrLength(&(addr->addr)), fqdn, sizeof(fqdn), NULL, 0, NI_NAMEREQD); 2513 2514 if (!error) 2515 { 2516 DEBUG_printf(("5http_resolve_cb: Found \"%s\".", fqdn)); 2517 2518 if ((hostptr = fqdn + strlen(fqdn) - 6) <= fqdn || 2519 _cups_strcasecmp(hostptr, ".local")) 2520 { 2521 hostTarget = fqdn; 2522 break; 2523 } 2524 } 2525 #ifdef DEBUG 2526 else 2527 DEBUG_printf(("5http_resolve_cb: \"%s\" did not resolve: %d", 2528 httpAddrString(&(addr->addr), fqdn, sizeof(fqdn)), 2529 error)); 2530 #endif /* DEBUG */ 2531 } 2532 2533 httpAddrFreeList(addrlist); 2534 } 2535 } 2536 2537 /* 2538 * Assemble the final device URI using the resolved hostname... 2539 */ 2540 2541 httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, (int)uribuf->bufsize, scheme, 2542 NULL, hostTarget, port, resource); 2543 DEBUG_printf(("5http_resolve_cb: Resolved URI is \"%s\".", uribuf->buffer)); 2544 2545 avahi_simple_poll_quit(uribuf->poll); 2546 } 2547 #endif /* HAVE_DNSSD */ 2548