Improve the process for GNU tools
[minix3.git] / external / bsd / fetch / dist / libfetch / common.c
blobc39168857e67651b1ced992c6454c386548154c1
1 /* $NetBSD: common.c,v 1.2 2011/06/25 20:27:01 christos 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.
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
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.
30 * $FreeBSD: common.c,v 1.53 2007/12/19 00:26:36 des Exp $
33 #if HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 #ifndef NETBSD
37 #include <nbcompat.h>
38 #endif
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <sys/time.h>
43 #include <sys/uio.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
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>
65 #ifndef MSG_NOSIGNAL
66 #include <signal.h>
67 #endif
69 #include "fetch.h"
70 #include "common.h"
72 /*** Local data **************************************************************/
75 * Error messages for resolver errors
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" }
87 /*** Error-reporting functions ***********************************************/
90 * Map error code to string
92 static struct fetcherr *
93 fetch_finderr(struct fetcherr *p, int e)
95 while (p->num != -1 && p->num != e)
96 p++;
97 return (p);
101 * Set error code
103 void
104 fetch_seterr(struct fetcherr *p, int e)
106 p = fetch_finderr(p, e);
107 fetchLastErrCode = p->cat;
108 snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
112 * Set error code according to errno
114 void
115 fetch_syserr(void)
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;
171 snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
176 * Emit status message
178 void
179 fetch_info(const char *fmt, ...)
181 va_list ap;
183 va_start(ap, fmt);
184 vfprintf(stderr, fmt, ap);
185 va_end(ap);
186 fputc('\n', stderr);
190 /*** Network-related utility functions ***************************************/
193 * Return the default port for a scheme
196 fetch_default_port(const char *scheme)
198 struct servent *se;
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);
210 * Return the default proxy port for a scheme
213 fetch_default_proxy_port(const char *scheme)
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);
224 * Create a connection for an existing descriptor.
226 conn_t *
227 fetch_reopen(int sd)
229 conn_t *conn;
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);
244 * Bind a socket to a specific local address
247 fetch_bind(int sd, int af, const char *addr)
249 struct addrinfo hints, *res, *res0;
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);
261 return (-1);
266 * Establish a TCP connection to the specified port on the specified host.
268 conn_t *
269 fetch_connect(struct url *url, int af, int verbose)
271 conn_t *conn;
272 char pbuf[10];
273 const char *bindaddr;
274 struct addrinfo hints, *res, *res0;
275 int sd, error;
277 if (verbose)
278 fetch_info("looking up %s", url->host);
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);
290 bindaddr = getenv("FETCH_BIND_ADDRESS");
292 if (verbose)
293 fetch_info("connecting to %s:%d", url->host, url->port);
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;
306 if (connect(sd, res->ai_addr, res->ai_addrlen) == 0)
307 break;
308 close(sd);
310 freeaddrinfo(res0);
311 if (sd == -1) {
312 fetch_syserr();
313 return (NULL);
316 if ((conn = fetch_reopen(sd)) == NULL) {
317 fetch_syserr();
318 close(sd);
320 conn->cache_url = fetchCopyURL(url);
321 conn->cache_af = af;
322 return (conn);
325 static conn_t *connection_cache;
326 static int cache_global_limit = 0;
327 static int cache_per_host_limit = 0;
330 * Initialise cache with the given limits.
332 void
333 fetchConnectionCacheInit(int global_limit, int per_host_limit)
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;
349 * Flush cache and free all associated resources.
351 void
352 fetchConnectionCacheClose(void)
354 conn_t *conn;
356 while ((conn = connection_cache) != NULL) {
357 connection_cache = conn->next_cached;
358 (*conn->cache_close)(conn);
363 * Check connection cache for an existing entry matching
364 * protocol/host/port/user/password/family.
366 conn_t *
367 fetch_cache_get(const struct url *url, int af)
369 conn_t *conn, *last_conn = NULL;
371 for (conn = connection_cache; conn; conn = conn->next_cached) {
372 if (conn->cache_url->port == url->port &&
373 strcmp(conn->cache_url->scheme, url->scheme) == 0 &&
374 strcmp(conn->cache_url->host, url->host) == 0 &&
375 strcmp(conn->cache_url->user, url->user) == 0 &&
376 strcmp(conn->cache_url->pwd, url->pwd) == 0 &&
377 (conn->cache_af == AF_UNSPEC || af == AF_UNSPEC ||
378 conn->cache_af == af)) {
379 if (last_conn != NULL)
380 last_conn->next_cached = conn->next_cached;
381 else
382 connection_cache = conn->next_cached;
383 return conn;
387 return NULL;
391 * Put the connection back into the cache for reuse.
392 * If the connection is freed due to LRU or if the cache
393 * is explicitly closed, the given callback is called.
395 void
396 fetch_cache_put(conn_t *conn, int (*closecb)(conn_t *))
398 conn_t *iter, *last;
399 int global_count, host_count;
401 if (conn->cache_url == NULL || cache_global_limit == 0) {
402 (*closecb)(conn);
403 return;
406 global_count = host_count = 0;
407 last = NULL;
408 for (iter = connection_cache; iter;
409 last = iter, iter = iter->next_cached) {
410 ++global_count;
411 if (strcmp(conn->cache_url->host, iter->cache_url->host) == 0)
412 ++host_count;
413 if (global_count < cache_global_limit &&
414 host_count < cache_per_host_limit)
415 continue;
416 --global_count;
417 if (last != NULL)
418 last->next_cached = iter->next_cached;
419 else
420 connection_cache = iter->next_cached;
421 (*iter->cache_close)(iter);
424 conn->cache_close = closecb;
425 conn->next_cached = connection_cache;
426 connection_cache = conn;
430 * Enable SSL on a connection.
433 fetch_ssl(conn_t *conn, int verbose)
436 #ifdef WITH_SSL
437 /* Init the SSL library and context */
438 if (!SSL_library_init()){
439 fprintf(stderr, "SSL library init failed\n");
440 return (-1);
443 SSL_load_error_strings();
445 conn->ssl_meth = SSLv23_client_method();
446 conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
447 SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
449 conn->ssl = SSL_new(conn->ssl_ctx);
450 if (conn->ssl == NULL){
451 fprintf(stderr, "SSL context creation failed\n");
452 return (-1);
454 SSL_set_fd(conn->ssl, conn->sd);
455 if (SSL_connect(conn->ssl) == -1){
456 ERR_print_errors_fp(stderr);
457 return (-1);
460 if (verbose) {
461 X509_NAME *name;
462 char *str;
464 fprintf(stderr, "SSL connection established using %s\n",
465 SSL_get_cipher(conn->ssl));
466 conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
467 name = X509_get_subject_name(conn->ssl_cert);
468 str = X509_NAME_oneline(name, 0, 0);
469 printf("Certificate subject: %s\n", str);
470 free(str);
471 name = X509_get_issuer_name(conn->ssl_cert);
472 str = X509_NAME_oneline(name, 0, 0);
473 printf("Certificate issuer: %s\n", str);
474 free(str);
477 return (0);
478 #else
479 (void)conn;
480 (void)verbose;
481 fprintf(stderr, "SSL support disabled\n");
482 return (-1);
483 #endif
488 * Read a character from a connection w/ timeout
490 ssize_t
491 fetch_read(conn_t *conn, char *buf, size_t len)
493 struct timeval now, timeout, waittv;
494 fd_set readfds;
495 ssize_t rlen;
496 int r;
498 if (len == 0)
499 return 0;
501 if (conn->next_len != 0) {
502 if (conn->next_len < len)
503 len = conn->next_len;
504 memmove(buf, conn->next_buf, len);
505 conn->next_len -= len;
506 conn->next_buf += len;
507 return len;
510 if (fetchTimeout) {
511 FD_ZERO(&readfds);
512 gettimeofday(&timeout, NULL);
513 timeout.tv_sec += fetchTimeout;
516 for (;;) {
517 while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
518 FD_SET(conn->sd, &readfds);
519 gettimeofday(&now, NULL);
520 waittv.tv_sec = timeout.tv_sec - now.tv_sec;
521 waittv.tv_usec = timeout.tv_usec - now.tv_usec;
522 if (waittv.tv_usec < 0) {
523 waittv.tv_usec += 1000000;
524 waittv.tv_sec--;
526 if (waittv.tv_sec < 0) {
527 errno = ETIMEDOUT;
528 fetch_syserr();
529 return (-1);
531 errno = 0;
532 r = select(conn->sd + 1, &readfds, NULL, NULL, &waittv);
533 if (r == -1) {
534 if (errno == EINTR && fetchRestartCalls)
535 continue;
536 fetch_syserr();
537 return (-1);
540 #ifdef WITH_SSL
541 if (conn->ssl != NULL)
542 rlen = SSL_read(conn->ssl, buf, (int)len);
543 else
544 #endif
545 rlen = read(conn->sd, buf, len);
546 if (rlen >= 0)
547 break;
549 if (errno != EINTR || !fetchRestartCalls)
550 return (-1);
552 return (rlen);
557 * Read a line of text from a connection w/ timeout
559 #define MIN_BUF_SIZE 1024
562 fetch_getln(conn_t *conn)
564 char *tmp, *next;
565 size_t tmpsize;
566 ssize_t len;
568 if (conn->buf == NULL) {
569 if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
570 errno = ENOMEM;
571 return (-1);
573 conn->bufsize = MIN_BUF_SIZE;
576 conn->buflen = 0;
577 next = NULL;
579 do {
581 * conn->bufsize != conn->buflen at this point,
582 * so the buffer can be NUL-terminated below for
583 * the case of len == 0.
585 len = fetch_read(conn, conn->buf + conn->buflen,
586 conn->bufsize - conn->buflen);
587 if (len == -1)
588 return (-1);
589 if (len == 0)
590 break;
591 next = memchr(conn->buf + conn->buflen, '\n', (size_t)len);
592 conn->buflen += len;
593 if (conn->buflen == conn->bufsize && next == NULL) {
594 tmp = conn->buf;
595 tmpsize = conn->bufsize * 2;
596 if (tmpsize < conn->bufsize) {
597 errno = ENOMEM;
598 return (-1);
600 if ((tmp = realloc(tmp, tmpsize)) == NULL) {
601 errno = ENOMEM;
602 return (-1);
604 conn->buf = tmp;
605 conn->bufsize = tmpsize;
607 } while (next == NULL);
609 if (next != NULL) {
610 *next = '\0';
611 conn->next_buf = next + 1;
612 conn->next_len = conn->buflen - (conn->next_buf - conn->buf);
613 conn->buflen = next - conn->buf;
614 } else {
615 conn->buf[conn->buflen] = '\0';
616 conn->next_len = 0;
618 return (0);
622 * Write a vector to a connection w/ timeout
623 * Note: can modify the iovec.
625 ssize_t
626 fetch_write(conn_t *conn, const void *buf, size_t len)
628 struct timeval now, timeout, waittv;
629 fd_set writefds;
630 ssize_t wlen, total;
631 int r;
632 #ifndef MSG_NOSIGNAL
633 static int killed_sigpipe;
634 #endif
636 #ifndef MSG_NOSIGNAL
637 if (!killed_sigpipe) {
638 signal(SIGPIPE, SIG_IGN);
639 killed_sigpipe = 1;
641 #endif
644 if (fetchTimeout) {
645 FD_ZERO(&writefds);
646 gettimeofday(&timeout, NULL);
647 timeout.tv_sec += fetchTimeout;
650 total = 0;
651 while (len) {
652 while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
653 FD_SET(conn->sd, &writefds);
654 gettimeofday(&now, NULL);
655 waittv.tv_sec = timeout.tv_sec - now.tv_sec;
656 waittv.tv_usec = timeout.tv_usec - now.tv_usec;
657 if (waittv.tv_usec < 0) {
658 waittv.tv_usec += 1000000;
659 waittv.tv_sec--;
661 if (waittv.tv_sec < 0) {
662 errno = ETIMEDOUT;
663 fetch_syserr();
664 return (-1);
666 errno = 0;
667 r = select(conn->sd + 1, NULL, &writefds, NULL, &waittv);
668 if (r == -1) {
669 if (errno == EINTR && fetchRestartCalls)
670 continue;
671 return (-1);
674 errno = 0;
675 #ifdef WITH_SSL
676 if (conn->ssl != NULL)
677 wlen = SSL_write(conn->ssl, buf, (int)len);
678 else
679 #endif
680 #ifndef MSG_NOSIGNAL
681 wlen = send(conn->sd, buf, len, 0);
682 #else
683 wlen = send(conn->sd, buf, len, MSG_NOSIGNAL);
684 #endif
685 if (wlen == 0) {
686 /* we consider a short write a failure */
687 errno = EPIPE;
688 fetch_syserr();
689 return (-1);
691 if (wlen < 0) {
692 if (errno == EINTR && fetchRestartCalls)
693 continue;
694 return (-1);
696 total += wlen;
697 buf = (const char *)buf + wlen;
698 len -= wlen;
700 return (total);
705 * Close connection
708 fetch_close(conn_t *conn)
710 int ret;
712 ret = close(conn->sd);
713 if (conn->cache_url)
714 fetchFreeURL(conn->cache_url);
715 free(conn->ftp_home);
716 free(conn->buf);
717 free(conn);
718 return (ret);
722 /*** Directory-related utility functions *************************************/
725 fetch_add_entry(struct url_list *ue, struct url *base, const char *name,
726 int pre_quoted)
728 struct url *tmp;
729 char *tmp_name;
730 size_t base_doc_len, name_len, i;
731 unsigned char c;
733 if (strchr(name, '/') != NULL ||
734 strcmp(name, "..") == 0 ||
735 strcmp(name, ".") == 0)
736 return 0;
738 if (strcmp(base->doc, "/") == 0)
739 base_doc_len = 0;
740 else
741 base_doc_len = strlen(base->doc);
743 name_len = 1;
744 for (i = 0; name[i] != '\0'; ++i) {
745 if ((!pre_quoted && name[i] == '%') ||
746 !fetch_urlpath_safe(name[i]))
747 name_len += 3;
748 else
749 ++name_len;
752 tmp_name = malloc( base_doc_len + name_len + 1);
753 if (tmp_name == NULL) {
754 errno = ENOMEM;
755 fetch_syserr();
756 return (-1);
759 if (ue->length + 1 >= ue->alloc_size) {
760 tmp = realloc(ue->urls, (ue->alloc_size * 2 + 1) * sizeof(*tmp));
761 if (tmp == NULL) {
762 free(tmp_name);
763 errno = ENOMEM;
764 fetch_syserr();
765 return (-1);
767 ue->alloc_size = ue->alloc_size * 2 + 1;
768 ue->urls = tmp;
771 tmp = ue->urls + ue->length;
772 strcpy(tmp->scheme, base->scheme);
773 strcpy(tmp->user, base->user);
774 strcpy(tmp->pwd, base->pwd);
775 strcpy(tmp->host, base->host);
776 tmp->port = base->port;
777 tmp->doc = tmp_name;
778 memcpy(tmp->doc, base->doc, base_doc_len);
779 tmp->doc[base_doc_len] = '/';
781 for (i = base_doc_len + 1; *name != '\0'; ++name) {
782 if ((!pre_quoted && *name == '%') ||
783 !fetch_urlpath_safe(*name)) {
784 tmp->doc[i++] = '%';
785 c = (unsigned char)*name / 16;
786 if (c < 10)
787 tmp->doc[i++] = '0' + c;
788 else
789 tmp->doc[i++] = 'a' - 10 + c;
790 c = (unsigned char)*name % 16;
791 if (c < 10)
792 tmp->doc[i++] = '0' + c;
793 else
794 tmp->doc[i++] = 'a' - 10 + c;
795 } else {
796 tmp->doc[i++] = *name;
799 tmp->doc[i] = '\0';
801 tmp->offset = 0;
802 tmp->length = 0;
803 tmp->last_modified = -1;
805 ++ue->length;
807 return (0);
810 void
811 fetchInitURLList(struct url_list *ue)
813 ue->length = ue->alloc_size = 0;
814 ue->urls = NULL;
818 fetchAppendURLList(struct url_list *dst, const struct url_list *src)
820 size_t i, j, len;
822 len = dst->length + src->length;
823 if (len > dst->alloc_size) {
824 struct url *tmp;
826 tmp = realloc(dst->urls, len * sizeof(*tmp));
827 if (tmp == NULL) {
828 errno = ENOMEM;
829 fetch_syserr();
830 return (-1);
832 dst->alloc_size = len;
833 dst->urls = tmp;
836 for (i = 0, j = dst->length; i < src->length; ++i, ++j) {
837 dst->urls[j] = src->urls[i];
838 dst->urls[j].doc = strdup(src->urls[i].doc);
839 if (dst->urls[j].doc == NULL) {
840 while (i-- > 0)
841 free(dst->urls[j].doc);
842 fetch_syserr();
843 return -1;
846 dst->length = len;
848 return 0;
851 void
852 fetchFreeURLList(struct url_list *ue)
854 size_t i;
856 for (i = 0; i < ue->length; ++i)
857 free(ue->urls[i].doc);
858 free(ue->urls);
859 ue->length = ue->alloc_size = 0;
863 /*** Authentication-related utility functions ********************************/
865 static const char *
866 fetch_read_word(FILE *f)
868 static char word[1024];
870 if (fscanf(f, " %1023s ", word) != 1)
871 return (NULL);
872 return (word);
876 * Get authentication data for a URL from .netrc
879 fetch_netrc_auth(struct url *url)
881 char fn[PATH_MAX];
882 const char *word;
883 char *p;
884 FILE *f;
886 if ((p = getenv("NETRC")) != NULL) {
887 if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
888 fetch_info("$NETRC specifies a file name "
889 "longer than PATH_MAX");
890 return (-1);
892 } else {
893 if ((p = getenv("HOME")) != NULL) {
894 struct passwd *pwd;
896 if ((pwd = getpwuid(getuid())) == NULL ||
897 (p = pwd->pw_dir) == NULL)
898 return (-1);
900 if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
901 return (-1);
904 if ((f = fopen(fn, "r")) == NULL)
905 return (-1);
906 while ((word = fetch_read_word(f)) != NULL) {
907 if (strcmp(word, "default") == 0)
908 break;
909 if (strcmp(word, "machine") == 0 &&
910 (word = fetch_read_word(f)) != NULL &&
911 strcasecmp(word, url->host) == 0) {
912 break;
915 if (word == NULL)
916 goto ferr;
917 while ((word = fetch_read_word(f)) != NULL) {
918 if (strcmp(word, "login") == 0) {
919 if ((word = fetch_read_word(f)) == NULL)
920 goto ferr;
921 if (snprintf(url->user, sizeof(url->user),
922 "%s", word) > (int)sizeof(url->user)) {
923 fetch_info("login name in .netrc is too long");
924 url->user[0] = '\0';
926 } else if (strcmp(word, "password") == 0) {
927 if ((word = fetch_read_word(f)) == NULL)
928 goto ferr;
929 if (snprintf(url->pwd, sizeof(url->pwd),
930 "%s", word) > (int)sizeof(url->pwd)) {
931 fetch_info("password in .netrc is too long");
932 url->pwd[0] = '\0';
934 } else if (strcmp(word, "account") == 0) {
935 if ((word = fetch_read_word(f)) == NULL)
936 goto ferr;
937 /* XXX not supported! */
938 } else {
939 break;
942 fclose(f);
943 return (0);
944 ferr:
945 fclose(f);
946 return (-1);
950 * The no_proxy environment variable specifies a set of domains for
951 * which the proxy should not be consulted; the contents is a comma-,
952 * or space-separated list of domain names. A single asterisk will
953 * override all proxy variables and no transactions will be proxied
954 * (for compatability with lynx and curl, see the discussion at
955 * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>).
958 fetch_no_proxy_match(const char *host)
960 const char *no_proxy, *p, *q;
961 size_t h_len, d_len;
963 if ((no_proxy = getenv("NO_PROXY")) == NULL &&
964 (no_proxy = getenv("no_proxy")) == NULL)
965 return (0);
967 /* asterisk matches any hostname */
968 if (strcmp(no_proxy, "*") == 0)
969 return (1);
971 h_len = strlen(host);
972 p = no_proxy;
973 do {
974 /* position p at the beginning of a domain suffix */
975 while (*p == ',' || isspace((unsigned char)*p))
976 p++;
978 /* position q at the first separator character */
979 for (q = p; *q; ++q)
980 if (*q == ',' || isspace((unsigned char)*q))
981 break;
983 d_len = q - p;
984 if (d_len > 0 && h_len > d_len &&
985 strncasecmp(host + h_len - d_len,
986 p, d_len) == 0) {
987 /* domain name matches */
988 return (1);
991 p = q + 1;
992 } while (*q);
994 return (0);
997 struct fetchIO {
998 void *io_cookie;
999 ssize_t (*io_read)(void *, void *, size_t);
1000 ssize_t (*io_write)(void *, const void *, size_t);
1001 void (*io_close)(void *);
1004 void
1005 fetchIO_close(fetchIO *f)
1007 if (f->io_close != NULL)
1008 (*f->io_close)(f->io_cookie);
1010 free(f);
1013 fetchIO *
1014 fetchIO_unopen(void *io_cookie, ssize_t (*io_read)(void *, void *, size_t),
1015 ssize_t (*io_write)(void *, const void *, size_t),
1016 void (*io_close)(void *))
1018 fetchIO *f;
1020 f = malloc(sizeof(*f));
1021 if (f == NULL)
1022 return f;
1024 f->io_cookie = io_cookie;
1025 f->io_read = io_read;
1026 f->io_write = io_write;
1027 f->io_close = io_close;
1029 return f;
1032 ssize_t
1033 fetchIO_read(fetchIO *f, void *buf, size_t len)
1035 if (f->io_read == NULL)
1036 return EBADF;
1037 return (*f->io_read)(f->io_cookie, buf, len);
1040 ssize_t
1041 fetchIO_write(fetchIO *f, const void *buf, size_t len)
1043 if (f->io_read == NULL)
1044 return EBADF;
1045 return (*f->io_write)(f->io_cookie, buf, len);