1 /* $NetBSD: common.c,v 1.6 2024/01/03 11:40:38 riastradh Exp $ */ 2 /*- 3 * Copyright (c) 1998-2004 Dag-Erling Co�dan Sm�rgrav 4 * Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg@NetBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer 12 * in this position and unchanged. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * $FreeBSD: common.c,v 1.53 2007/12/19 00:26:36 des Exp $ 31 */ 32 33 #if HAVE_CONFIG_H 34 #include "config.h" 35 #endif 36 #ifndef NETBSD 37 #include <nbcompat.h> 38 #endif 39 40 #include <sys/types.h> 41 #include <sys/socket.h> 42 #include <sys/time.h> 43 #include <sys/uio.h> 44 45 #include <netinet/in.h> 46 #include <arpa/inet.h> 47 48 #include <ctype.h> 49 #include <errno.h> 50 #if defined(HAVE_INTTYPES_H) || defined(NETBSD) 51 #include <inttypes.h> 52 #endif 53 #ifndef NETBSD 54 #include <nbcompat/netdb.h> 55 #else 56 #include <netdb.h> 57 #endif 58 #include <pwd.h> 59 #include <stdarg.h> 60 #include <stdlib.h> 61 #include <stdio.h> 62 #include <string.h> 63 #include <unistd.h> 64 65 #ifndef MSG_NOSIGNAL 66 #include <signal.h> 67 #endif 68 69 #include "fetch.h" 70 #include "common.h" 71 72 /*** Local data **************************************************************/ 73 74 /* 75 * Error messages for resolver errors 76 */ 77 static struct fetcherr netdb_errlist[] = { 78 #ifdef EAI_NODATA 79 { EAI_NODATA, FETCH_RESOLV, "Host not found" }, 80 #endif 81 { EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" }, 82 { EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" }, 83 { EAI_NONAME, FETCH_RESOLV, "No address record" }, 84 { -1, FETCH_UNKNOWN, "Unknown resolver error" } 85 }; 86 87 /*** Error-reporting functions ***********************************************/ 88 89 /* 90 * Map error code to string 91 */ 92 static struct fetcherr * 93 fetch_finderr(struct fetcherr *p, int e) 94 { 95 while (p->num != -1 && p->num != e) 96 p++; 97 return (p); 98 } 99 100 /* 101 * Set error code 102 */ 103 void 104 fetch_seterr(struct fetcherr *p, int e) 105 { 106 p = fetch_finderr(p, e); 107 fetchLastErrCode = p->cat; 108 snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string); 109 } 110 111 /* 112 * Set error code according to errno 113 */ 114 void 115 fetch_syserr(void) 116 { 117 switch (errno) { 118 case 0: 119 fetchLastErrCode = FETCH_OK; 120 break; 121 case EPERM: 122 case EACCES: 123 case EROFS: 124 #ifdef EAUTH 125 case EAUTH: 126 #endif 127 #ifdef ENEEDAUTH 128 case ENEEDAUTH: 129 #endif 130 fetchLastErrCode = FETCH_AUTH; 131 break; 132 case ENOENT: 133 case EISDIR: /* XXX */ 134 fetchLastErrCode = FETCH_UNAVAIL; 135 break; 136 case ENOMEM: 137 fetchLastErrCode = FETCH_MEMORY; 138 break; 139 case EBUSY: 140 case EAGAIN: 141 fetchLastErrCode = FETCH_TEMP; 142 break; 143 case EEXIST: 144 fetchLastErrCode = FETCH_EXISTS; 145 break; 146 case ENOSPC: 147 fetchLastErrCode = FETCH_FULL; 148 break; 149 case EADDRINUSE: 150 case EADDRNOTAVAIL: 151 case ENETDOWN: 152 case ENETUNREACH: 153 case ENETRESET: 154 case EHOSTUNREACH: 155 fetchLastErrCode = FETCH_NETWORK; 156 break; 157 case ECONNABORTED: 158 case ECONNRESET: 159 fetchLastErrCode = FETCH_ABORT; 160 break; 161 case ETIMEDOUT: 162 fetchLastErrCode = FETCH_TIMEOUT; 163 break; 164 case ECONNREFUSED: 165 case EHOSTDOWN: 166 fetchLastErrCode = FETCH_DOWN; 167 break; 168 default: 169 fetchLastErrCode = FETCH_UNKNOWN; 170 } 171 snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno)); 172 } 173 174 175 /* 176 * Emit status message 177 */ 178 void 179 fetch_info(const char *fmt, ...) 180 { 181 va_list ap; 182 183 va_start(ap, fmt); 184 vfprintf(stderr, fmt, ap); 185 va_end(ap); 186 fputc('\n', stderr); 187 } 188 189 190 /*** Network-related utility functions ***************************************/ 191 192 /* 193 * Return the default port for a scheme 194 */ 195 int 196 fetch_default_port(const char *scheme) 197 { 198 struct servent *se; 199 200 if ((se = getservbyname(scheme, "tcp")) != NULL) 201 return (ntohs(se->s_port)); 202 if (strcasecmp(scheme, SCHEME_FTP) == 0) 203 return (FTP_DEFAULT_PORT); 204 if (strcasecmp(scheme, SCHEME_HTTP) == 0) 205 return (HTTP_DEFAULT_PORT); 206 return (0); 207 } 208 209 /* 210 * Return the default proxy port for a scheme 211 */ 212 int 213 fetch_default_proxy_port(const char *scheme) 214 { 215 if (strcasecmp(scheme, SCHEME_FTP) == 0) 216 return (FTP_DEFAULT_PROXY_PORT); 217 if (strcasecmp(scheme, SCHEME_HTTP) == 0) 218 return (HTTP_DEFAULT_PROXY_PORT); 219 return (0); 220 } 221 222 223 /* 224 * Create a connection for an existing descriptor. 225 */ 226 conn_t * 227 fetch_reopen(int sd) 228 { 229 conn_t *conn; 230 231 /* allocate and fill connection structure */ 232 if ((conn = calloc(1, sizeof(*conn))) == NULL) 233 return (NULL); 234 conn->ftp_home = NULL; 235 conn->cache_url = NULL; 236 conn->next_buf = NULL; 237 conn->next_len = 0; 238 conn->sd = sd; 239 return (conn); 240 } 241 242 243 /* 244 * Bind a socket to a specific local address 245 */ 246 int 247 fetch_bind(int sd, int af, const char *addr) 248 { 249 struct addrinfo hints, *res, *res0; 250 251 memset(&hints, 0, sizeof(hints)); 252 hints.ai_family = af; 253 hints.ai_socktype = SOCK_STREAM; 254 hints.ai_protocol = 0; 255 if (getaddrinfo(addr, NULL, &hints, &res0)) 256 return (-1); 257 for (res = res0; res; res = res->ai_next) { 258 if (bind(sd, res->ai_addr, res->ai_addrlen) == 0) 259 return (0); 260 } 261 return (-1); 262 } 263 264 265 /* 266 * Establish a TCP connection to the specified port on the specified host. 267 */ 268 conn_t * 269 fetch_connect(struct url *url, int af, int verbose) 270 { 271 conn_t *conn; 272 char pbuf[10]; 273 const char *bindaddr; 274 struct addrinfo hints, *res, *res0; 275 int sd, error; 276 277 if (verbose) 278 fetch_info("looking up %s", url->host); 279 280 /* look up host name and set up socket address structure */ 281 snprintf(pbuf, sizeof(pbuf), "%d", url->port); 282 memset(&hints, 0, sizeof(hints)); 283 hints.ai_family = af; 284 hints.ai_socktype = SOCK_STREAM; 285 hints.ai_protocol = 0; 286 if ((error = getaddrinfo(url->host, pbuf, &hints, &res0)) != 0) { 287 netdb_seterr(error); 288 return (NULL); 289 } 290 bindaddr = getenv("FETCH_BIND_ADDRESS"); 291 292 if (verbose) 293 fetch_info("connecting to %s:%d", url->host, url->port); 294 295 /* try to connect */ 296 for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) { 297 if ((sd = socket(res->ai_family, res->ai_socktype, 298 res->ai_protocol)) == -1) 299 continue; 300 if (bindaddr != NULL && *bindaddr != '\0' && 301 fetch_bind(sd, res->ai_family, bindaddr) != 0) { 302 fetch_info("failed to bind to '%s'", bindaddr); 303 close(sd); 304 continue; 305 } 306 if (connect(sd, res->ai_addr, res->ai_addrlen) == 0) 307 break; 308 close(sd); 309 } 310 freeaddrinfo(res0); 311 if (sd == -1) { 312 fetch_syserr(); 313 return (NULL); 314 } 315 316 if ((conn = fetch_reopen(sd)) == NULL) { 317 fetch_syserr(); 318 close(sd); 319 } 320 conn->cache_url = fetchCopyURL(url); 321 conn->cache_af = af; 322 return (conn); 323 } 324 325 static conn_t *connection_cache; 326 static int cache_global_limit = 0; 327 static int cache_per_host_limit = 0; 328 329 /* 330 * Initialise cache with the given limits. 331 */ 332 void 333 fetchConnectionCacheInit(int global_limit, int per_host_limit) 334 { 335 336 if (global_limit < 0) 337 cache_global_limit = INT_MAX; 338 else if (per_host_limit > global_limit) 339 cache_global_limit = per_host_limit; 340 else 341 cache_global_limit = global_limit; 342 if (per_host_limit < 0) 343 cache_per_host_limit = INT_MAX; 344 else 345 cache_per_host_limit = per_host_limit; 346 } 347 348 /* 349 * Flush cache and free all associated resources. 350 */ 351 void 352 fetchConnectionCacheClose(void) 353 { 354 conn_t *conn; 355 356 while ((conn = connection_cache) != NULL) { 357 connection_cache = conn->next_cached; 358 (*conn->cache_close)(conn); 359 } 360 } 361 362 /* 363 * Check connection cache for an existing entry matching 364 * protocol/host/port/user/password/family. 365 */ 366 conn_t * 367 fetch_cache_get(const struct url *url, int af) 368 { 369 conn_t *conn, *last_conn = NULL; 370 371 for (conn = connection_cache; conn; last_conn = conn, 372 conn = conn->next_cached) 373 { 374 if (conn->cache_url->port == url->port && 375 strcmp(conn->cache_url->scheme, url->scheme) == 0 && 376 strcmp(conn->cache_url->host, url->host) == 0 && 377 strcmp(conn->cache_url->user, url->user) == 0 && 378 strcmp(conn->cache_url->pwd, url->pwd) == 0 && 379 (conn->cache_af == AF_UNSPEC || af == AF_UNSPEC || 380 conn->cache_af == af)) { 381 if (last_conn != NULL) 382 last_conn->next_cached = conn->next_cached; 383 else 384 connection_cache = conn->next_cached; 385 return conn; 386 } 387 } 388 389 return NULL; 390 } 391 392 /* 393 * Put the connection back into the cache for reuse. 394 * If the connection is freed due to LRU or if the cache 395 * is explicitly closed, the given callback is called. 396 */ 397 void 398 fetch_cache_put(conn_t *conn, int (*closecb)(conn_t *)) 399 { 400 conn_t *iter, *last, *oiter; 401 int global_count, host_count; 402 403 if (conn->cache_url == NULL || cache_global_limit == 0) { 404 (*closecb)(conn); 405 return; 406 } 407 408 global_count = host_count = 0; 409 last = NULL; 410 for (iter = connection_cache; iter; ) { 411 ++global_count; 412 if (strcmp(conn->cache_url->host, iter->cache_url->host) == 0) 413 ++host_count; 414 if (global_count < cache_global_limit && 415 host_count < cache_per_host_limit) 416 oiter = NULL; 417 else { 418 --global_count; 419 if (last != NULL) 420 last->next_cached = iter->next_cached; 421 else 422 connection_cache = iter->next_cached; 423 oiter = iter; 424 } 425 last = iter; 426 iter = iter->next_cached; 427 if (oiter) 428 (*oiter->cache_close)(oiter); 429 } 430 431 conn->cache_close = closecb; 432 conn->next_cached = connection_cache; 433 connection_cache = conn; 434 } 435 436 /* 437 * Enable SSL on a connection. 438 */ 439 int 440 fetch_ssl(conn_t *conn, int verbose) 441 { 442 443 #ifdef WITH_SSL 444 /* Init the SSL library and context */ 445 if (!SSL_library_init()){ 446 fprintf(stderr, "SSL library init failed\n"); 447 return (-1); 448 } 449 450 SSL_load_error_strings(); 451 452 conn->ssl_meth = SSLv23_client_method(); 453 conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth); 454 SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY); 455 if (getenv("SSL_NO_VERIFY_PEER") == NULL) { 456 SSL_CTX_set_default_verify_paths(conn->ssl_ctx); 457 SSL_CTX_set_verify(conn->ssl_ctx, SSL_VERIFY_PEER, NULL); 458 } 459 460 conn->ssl = SSL_new(conn->ssl_ctx); 461 if (conn->ssl == NULL){ 462 fprintf(stderr, "SSL context creation failed\n"); 463 return (-1); 464 } 465 SSL_set_fd(conn->ssl, conn->sd); 466 if (!SSL_set_tlsext_host_name(conn->ssl, conn->cache_url->host)) { 467 fprintf(stderr, "SSL hostname setting failed\n"); 468 return (-1); 469 } 470 if (SSL_connect(conn->ssl) == -1){ 471 ERR_print_errors_fp(stderr); 472 return (-1); 473 } 474 475 if (verbose) { 476 X509_NAME *name; 477 char *str; 478 479 fprintf(stderr, "SSL connection established using %s\n", 480 SSL_get_cipher(conn->ssl)); 481 conn->ssl_cert = SSL_get_peer_certificate(conn->ssl); 482 name = X509_get_subject_name(conn->ssl_cert); 483 str = X509_NAME_oneline(name, 0, 0); 484 printf("Certificate subject: %s\n", str); 485 free(str); 486 name = X509_get_issuer_name(conn->ssl_cert); 487 str = X509_NAME_oneline(name, 0, 0); 488 printf("Certificate issuer: %s\n", str); 489 free(str); 490 } 491 492 return (0); 493 #else 494 (void)conn; 495 (void)verbose; 496 fprintf(stderr, "SSL support disabled\n"); 497 return (-1); 498 #endif 499 } 500 501 502 /* 503 * Read a character from a connection w/ timeout 504 */ 505 ssize_t 506 fetch_read(conn_t *conn, char *buf, size_t len) 507 { 508 struct timeval now, timeout, waittv; 509 fd_set readfds; 510 ssize_t rlen; 511 int r; 512 513 if (len == 0) 514 return 0; 515 516 if (conn->next_len != 0) { 517 if (conn->next_len < len) 518 len = conn->next_len; 519 memmove(buf, conn->next_buf, len); 520 conn->next_len -= len; 521 conn->next_buf += len; 522 return len; 523 } 524 525 if (fetchTimeout) { 526 FD_ZERO(&readfds); 527 gettimeofday(&timeout, NULL); 528 timeout.tv_sec += fetchTimeout; 529 } 530 531 for (;;) { 532 while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) { 533 FD_SET(conn->sd, &readfds); 534 gettimeofday(&now, NULL); 535 waittv.tv_sec = timeout.tv_sec - now.tv_sec; 536 waittv.tv_usec = timeout.tv_usec - now.tv_usec; 537 if (waittv.tv_usec < 0) { 538 waittv.tv_usec += 1000000; 539 waittv.tv_sec--; 540 } 541 if (waittv.tv_sec < 0) { 542 errno = ETIMEDOUT; 543 fetch_syserr(); 544 return (-1); 545 } 546 errno = 0; 547 r = select(conn->sd + 1, &readfds, NULL, NULL, &waittv); 548 if (r == -1) { 549 if (errno == EINTR && fetchRestartCalls) 550 continue; 551 fetch_syserr(); 552 return (-1); 553 } 554 } 555 #ifdef WITH_SSL 556 if (conn->ssl != NULL) 557 rlen = SSL_read(conn->ssl, buf, (int)len); 558 else 559 #endif 560 rlen = read(conn->sd, buf, len); 561 if (rlen >= 0) 562 break; 563 564 if (errno != EINTR || !fetchRestartCalls) 565 return (-1); 566 } 567 return (rlen); 568 } 569 570 571 /* 572 * Read a line of text from a connection w/ timeout 573 */ 574 #define MIN_BUF_SIZE 1024 575 576 int 577 fetch_getln(conn_t *conn) 578 { 579 char *tmp, *next; 580 size_t tmpsize; 581 ssize_t len; 582 583 if (conn->buf == NULL) { 584 if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) { 585 errno = ENOMEM; 586 return (-1); 587 } 588 conn->bufsize = MIN_BUF_SIZE; 589 } 590 591 conn->buflen = 0; 592 next = NULL; 593 594 do { 595 /* 596 * conn->bufsize != conn->buflen at this point, 597 * so the buffer can be NUL-terminated below for 598 * the case of len == 0. 599 */ 600 len = fetch_read(conn, conn->buf + conn->buflen, 601 conn->bufsize - conn->buflen); 602 if (len == -1) 603 return (-1); 604 if (len == 0) 605 break; 606 next = memchr(conn->buf + conn->buflen, '\n', (size_t)len); 607 conn->buflen += len; 608 if (conn->buflen == conn->bufsize && next == NULL) { 609 tmp = conn->buf; 610 tmpsize = conn->bufsize * 2; 611 if (tmpsize < conn->bufsize) { 612 errno = ENOMEM; 613 return (-1); 614 } 615 if ((tmp = realloc(tmp, tmpsize)) == NULL) { 616 errno = ENOMEM; 617 return (-1); 618 } 619 conn->buf = tmp; 620 conn->bufsize = tmpsize; 621 } 622 } while (next == NULL); 623 624 if (next != NULL) { 625 *next = '\0'; 626 conn->next_buf = next + 1; 627 conn->next_len = conn->buflen - (conn->next_buf - conn->buf); 628 conn->buflen = next - conn->buf; 629 } else { 630 conn->buf[conn->buflen] = '\0'; 631 conn->next_len = 0; 632 } 633 return (0); 634 } 635 636 /* 637 * Write a vector to a connection w/ timeout 638 * Note: can modify the iovec. 639 */ 640 ssize_t 641 fetch_write(conn_t *conn, const void *buf, size_t len) 642 { 643 struct timeval now, timeout, waittv; 644 fd_set writefds; 645 ssize_t wlen, total; 646 int r; 647 #ifndef MSG_NOSIGNAL 648 static int killed_sigpipe; 649 #endif 650 651 #ifndef MSG_NOSIGNAL 652 if (!killed_sigpipe) { 653 signal(SIGPIPE, SIG_IGN); 654 killed_sigpipe = 1; 655 } 656 #endif 657 658 659 if (fetchTimeout) { 660 FD_ZERO(&writefds); 661 gettimeofday(&timeout, NULL); 662 timeout.tv_sec += fetchTimeout; 663 } 664 665 total = 0; 666 while (len) { 667 while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) { 668 FD_SET(conn->sd, &writefds); 669 gettimeofday(&now, NULL); 670 waittv.tv_sec = timeout.tv_sec - now.tv_sec; 671 waittv.tv_usec = timeout.tv_usec - now.tv_usec; 672 if (waittv.tv_usec < 0) { 673 waittv.tv_usec += 1000000; 674 waittv.tv_sec--; 675 } 676 if (waittv.tv_sec < 0) { 677 errno = ETIMEDOUT; 678 fetch_syserr(); 679 return (-1); 680 } 681 errno = 0; 682 r = select(conn->sd + 1, NULL, &writefds, NULL, &waittv); 683 if (r == -1) { 684 if (errno == EINTR && fetchRestartCalls) 685 continue; 686 return (-1); 687 } 688 } 689 errno = 0; 690 #ifdef WITH_SSL 691 if (conn->ssl != NULL) 692 wlen = SSL_write(conn->ssl, buf, (int)len); 693 else 694 #endif 695 #ifndef MSG_NOSIGNAL 696 wlen = send(conn->sd, buf, len, 0); 697 #else 698 wlen = send(conn->sd, buf, len, MSG_NOSIGNAL); 699 #endif 700 if (wlen == 0) { 701 /* we consider a short write a failure */ 702 errno = EPIPE; 703 fetch_syserr(); 704 return (-1); 705 } 706 if (wlen < 0) { 707 if (errno == EINTR && fetchRestartCalls) 708 continue; 709 return (-1); 710 } 711 total += wlen; 712 buf = (const char *)buf + wlen; 713 len -= wlen; 714 } 715 return (total); 716 } 717 718 719 /* 720 * Close connection 721 */ 722 int 723 fetch_close(conn_t *conn) 724 { 725 int ret; 726 727 #ifdef WITH_SSL 728 if (conn->ssl) { 729 SSL_shutdown(conn->ssl); 730 SSL_set_connect_state(conn->ssl); 731 SSL_free(conn->ssl); 732 conn->ssl = NULL; 733 } 734 if (conn->ssl_ctx) { 735 SSL_CTX_free(conn->ssl_ctx); 736 conn->ssl_ctx = NULL; 737 } 738 if (conn->ssl_cert) { 739 X509_free(conn->ssl_cert); 740 conn->ssl_cert = NULL; 741 } 742 #endif 743 ret = close(conn->sd); 744 if (conn->cache_url) 745 fetchFreeURL(conn->cache_url); 746 free(conn->ftp_home); 747 free(conn->buf); 748 free(conn); 749 return (ret); 750 } 751 752 753 /*** Directory-related utility functions *************************************/ 754 755 int 756 fetch_add_entry(struct url_list *ue, struct url *base, const char *name, 757 int pre_quoted) 758 { 759 struct url *tmp; 760 char *tmp_name; 761 size_t base_doc_len, name_len, i; 762 unsigned char c; 763 764 if (strchr(name, '/') != NULL || 765 strcmp(name, "..") == 0 || 766 strcmp(name, ".") == 0) 767 return 0; 768 769 if (strcmp(base->doc, "/") == 0) 770 base_doc_len = 0; 771 else 772 base_doc_len = strlen(base->doc); 773 774 name_len = 1; 775 for (i = 0; name[i] != '\0'; ++i) { 776 if ((!pre_quoted && name[i] == '%') || 777 !fetch_urlpath_safe(name[i])) 778 name_len += 3; 779 else 780 ++name_len; 781 } 782 783 tmp_name = malloc( base_doc_len + name_len + 1); 784 if (tmp_name == NULL) { 785 errno = ENOMEM; 786 fetch_syserr(); 787 return (-1); 788 } 789 790 if (ue->length + 1 >= ue->alloc_size) { 791 tmp = realloc(ue->urls, (ue->alloc_size * 2 + 1) * sizeof(*tmp)); 792 if (tmp == NULL) { 793 free(tmp_name); 794 errno = ENOMEM; 795 fetch_syserr(); 796 return (-1); 797 } 798 ue->alloc_size = ue->alloc_size * 2 + 1; 799 ue->urls = tmp; 800 } 801 802 tmp = ue->urls + ue->length; 803 strcpy(tmp->scheme, base->scheme); 804 strcpy(tmp->user, base->user); 805 strcpy(tmp->pwd, base->pwd); 806 strcpy(tmp->host, base->host); 807 tmp->port = base->port; 808 tmp->doc = tmp_name; 809 memcpy(tmp->doc, base->doc, base_doc_len); 810 tmp->doc[base_doc_len] = '/'; 811 812 for (i = base_doc_len + 1; *name != '\0'; ++name) { 813 if ((!pre_quoted && *name == '%') || 814 !fetch_urlpath_safe(*name)) { 815 tmp->doc[i++] = '%'; 816 c = (unsigned char)*name / 16; 817 if (c < 10) 818 tmp->doc[i++] = '0' + c; 819 else 820 tmp->doc[i++] = 'a' - 10 + c; 821 c = (unsigned char)*name % 16; 822 if (c < 10) 823 tmp->doc[i++] = '0' + c; 824 else 825 tmp->doc[i++] = 'a' - 10 + c; 826 } else { 827 tmp->doc[i++] = *name; 828 } 829 } 830 tmp->doc[i] = '\0'; 831 832 tmp->offset = 0; 833 tmp->length = 0; 834 tmp->last_modified = -1; 835 836 ++ue->length; 837 838 return (0); 839 } 840 841 void 842 fetchInitURLList(struct url_list *ue) 843 { 844 ue->length = ue->alloc_size = 0; 845 ue->urls = NULL; 846 } 847 848 int 849 fetchAppendURLList(struct url_list *dst, const struct url_list *src) 850 { 851 size_t i, j, len; 852 853 len = dst->length + src->length; 854 if (len > dst->alloc_size) { 855 struct url *tmp; 856 857 tmp = realloc(dst->urls, len * sizeof(*tmp)); 858 if (tmp == NULL) { 859 errno = ENOMEM; 860 fetch_syserr(); 861 return (-1); 862 } 863 dst->alloc_size = len; 864 dst->urls = tmp; 865 } 866 867 for (i = 0, j = dst->length; i < src->length; ++i, ++j) { 868 dst->urls[j] = src->urls[i]; 869 dst->urls[j].doc = strdup(src->urls[i].doc); 870 if (dst->urls[j].doc == NULL) { 871 while (i-- > 0) 872 free(dst->urls[j].doc); 873 fetch_syserr(); 874 return -1; 875 } 876 } 877 dst->length = len; 878 879 return 0; 880 } 881 882 void 883 fetchFreeURLList(struct url_list *ue) 884 { 885 size_t i; 886 887 for (i = 0; i < ue->length; ++i) 888 free(ue->urls[i].doc); 889 free(ue->urls); 890 ue->length = ue->alloc_size = 0; 891 } 892 893 894 /*** Authentication-related utility functions ********************************/ 895 896 static const char * 897 fetch_read_word(FILE *f) 898 { 899 static char word[1024]; 900 901 if (fscanf(f, " %1023s ", word) != 1) 902 return (NULL); 903 return (word); 904 } 905 906 /* 907 * Get authentication data for a URL from .netrc 908 */ 909 int 910 fetch_netrc_auth(struct url *url) 911 { 912 char fn[PATH_MAX]; 913 const char *word; 914 char *p; 915 FILE *f; 916 917 if ((p = getenv("NETRC")) != NULL) { 918 if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) { 919 fetch_info("$NETRC specifies a file name " 920 "longer than PATH_MAX"); 921 return (-1); 922 } 923 } else { 924 if ((p = getenv("HOME")) != NULL) { 925 struct passwd *pwd; 926 927 if ((pwd = getpwuid(getuid())) == NULL || 928 (p = pwd->pw_dir) == NULL) 929 return (-1); 930 } 931 if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn)) 932 return (-1); 933 } 934 935 if ((f = fopen(fn, "r")) == NULL) 936 return (-1); 937 while ((word = fetch_read_word(f)) != NULL) { 938 if (strcmp(word, "default") == 0) 939 break; 940 if (strcmp(word, "machine") == 0 && 941 (word = fetch_read_word(f)) != NULL && 942 strcasecmp(word, url->host) == 0) { 943 break; 944 } 945 } 946 if (word == NULL) 947 goto ferr; 948 while ((word = fetch_read_word(f)) != NULL) { 949 if (strcmp(word, "login") == 0) { 950 if ((word = fetch_read_word(f)) == NULL) 951 goto ferr; 952 if (snprintf(url->user, sizeof(url->user), 953 "%s", word) > (int)sizeof(url->user)) { 954 fetch_info("login name in .netrc is too long"); 955 url->user[0] = '\0'; 956 } 957 } else if (strcmp(word, "password") == 0) { 958 if ((word = fetch_read_word(f)) == NULL) 959 goto ferr; 960 if (snprintf(url->pwd, sizeof(url->pwd), 961 "%s", word) > (int)sizeof(url->pwd)) { 962 fetch_info("password in .netrc is too long"); 963 url->pwd[0] = '\0'; 964 } 965 } else if (strcmp(word, "account") == 0) { 966 if ((word = fetch_read_word(f)) == NULL) 967 goto ferr; 968 /* XXX not supported! */ 969 } else { 970 break; 971 } 972 } 973 fclose(f); 974 return (0); 975 ferr: 976 fclose(f); 977 return (-1); 978 } 979 980 /* 981 * The no_proxy environment variable specifies a set of domains for 982 * which the proxy should not be consulted; the contents is a comma-, 983 * or space-separated list of domain names. A single asterisk will 984 * override all proxy variables and no transactions will be proxied 985 * (for compatability with lynx and curl, see the discussion at 986 * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>). 987 */ 988 int 989 fetch_no_proxy_match(const char *host) 990 { 991 const char *no_proxy, *p, *q; 992 size_t h_len, d_len; 993 994 if ((no_proxy = getenv("NO_PROXY")) == NULL && 995 (no_proxy = getenv("no_proxy")) == NULL) 996 return (0); 997 998 /* asterisk matches any hostname */ 999 if (strcmp(no_proxy, "*") == 0) 1000 return (1); 1001 1002 h_len = strlen(host); 1003 p = no_proxy; 1004 do { 1005 /* position p at the beginning of a domain suffix */ 1006 while (*p == ',' || isspace((unsigned char)*p)) 1007 p++; 1008 1009 /* position q at the first separator character */ 1010 for (q = p; *q; ++q) 1011 if (*q == ',' || isspace((unsigned char)*q)) 1012 break; 1013 1014 d_len = q - p; 1015 if (d_len > 0 && h_len > d_len && 1016 strncasecmp(host + h_len - d_len, 1017 p, d_len) == 0) { 1018 /* domain name matches */ 1019 return (1); 1020 } 1021 1022 p = q + 1; 1023 } while (*q); 1024 1025 return (0); 1026 } 1027 1028 struct fetchIO { 1029 void *io_cookie; 1030 ssize_t (*io_read)(void *, void *, size_t); 1031 ssize_t (*io_write)(void *, const void *, size_t); 1032 void (*io_close)(void *); 1033 }; 1034 1035 void 1036 fetchIO_close(fetchIO *f) 1037 { 1038 if (f->io_close != NULL) 1039 (*f->io_close)(f->io_cookie); 1040 1041 free(f); 1042 } 1043 1044 fetchIO * 1045 fetchIO_unopen(void *io_cookie, ssize_t (*io_read)(void *, void *, size_t), 1046 ssize_t (*io_write)(void *, const void *, size_t), 1047 void (*io_close)(void *)) 1048 { 1049 fetchIO *f; 1050 1051 f = malloc(sizeof(*f)); 1052 if (f == NULL) 1053 return f; 1054 1055 f->io_cookie = io_cookie; 1056 f->io_read = io_read; 1057 f->io_write = io_write; 1058 f->io_close = io_close; 1059 1060 return f; 1061 } 1062 1063 ssize_t 1064 fetchIO_read(fetchIO *f, void *buf, size_t len) 1065 { 1066 if (f->io_read == NULL) 1067 return EBADF; 1068 return (*f->io_read)(f->io_cookie, buf, len); 1069 } 1070 1071 ssize_t 1072 fetchIO_write(fetchIO *f, const void *buf, size_t len) 1073 { 1074 if (f->io_read == NULL) 1075 return EBADF; 1076 return (*f->io_write)(f->io_cookie, buf, len); 1077 } 1078