kernel debug: priv can be NULL early on
[minix.git] / lib / libfetch / common.c
blob72988e5ebdc3687d246dfc514a792d188bee1edb
1 /* $NetBSD: common.c,v 1.27 2010/06/13 21:38:09 joerg 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 #if !defined(NETBSD) && !defined(__minix)
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 #if !defined(NETBSD) && !defined(__minix)
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 "common.h"
71 /*** Local data **************************************************************/
74 * Error messages for resolver errors
76 static struct fetcherr netdb_errlist[] = {
77 #ifdef EAI_NODATA
78 { EAI_NODATA, FETCH_RESOLV, "Host not found" },
79 #endif
80 { EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" },
81 { EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" },
82 { EAI_NONAME, FETCH_RESOLV, "No address record" },
83 { -1, FETCH_UNKNOWN, "Unknown resolver error" }
86 /*** Error-reporting functions ***********************************************/
89 * Map error code to string
91 static struct fetcherr *
92 fetch_finderr(struct fetcherr *p, int e)
94 while (p->num != -1 && p->num != e)
95 p++;
96 return (p);
100 * Set error code
102 void
103 fetch_seterr(struct fetcherr *p, int e)
105 p = fetch_finderr(p, e);
106 fetchLastErrCode = p->cat;
107 snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
111 * Set error code according to errno
113 void
114 fetch_syserr(void)
116 switch (errno) {
117 case 0:
118 fetchLastErrCode = FETCH_OK;
119 break;
120 case EPERM:
121 case EACCES:
122 case EROFS:
123 #ifdef EAUTH
124 case EAUTH:
125 #endif
126 #ifdef ENEEDAUTH
127 case ENEEDAUTH:
128 #endif
129 fetchLastErrCode = FETCH_AUTH;
130 break;
131 case ENOENT:
132 case EISDIR: /* XXX */
133 fetchLastErrCode = FETCH_UNAVAIL;
134 break;
135 case ENOMEM:
136 fetchLastErrCode = FETCH_MEMORY;
137 break;
138 case EBUSY:
139 case EAGAIN:
140 fetchLastErrCode = FETCH_TEMP;
141 break;
142 case EEXIST:
143 fetchLastErrCode = FETCH_EXISTS;
144 break;
145 case ENOSPC:
146 fetchLastErrCode = FETCH_FULL;
147 break;
148 case EADDRINUSE:
149 case EADDRNOTAVAIL:
150 case ENETDOWN:
151 case ENETUNREACH:
152 #if defined(ENETRESET)
153 case ENETRESET:
154 #endif
155 case EHOSTUNREACH:
156 fetchLastErrCode = FETCH_NETWORK;
157 break;
158 #if defined(ECONNABORTED)
159 case ECONNABORTED:
160 #endif
161 case ECONNRESET:
162 fetchLastErrCode = FETCH_ABORT;
163 break;
164 case ETIMEDOUT:
165 fetchLastErrCode = FETCH_TIMEOUT;
166 break;
167 case ECONNREFUSED:
168 #if defined(EHOSTDOWN)
169 case EHOSTDOWN:
170 #endif
171 fetchLastErrCode = FETCH_DOWN;
172 break;
173 default:
174 fetchLastErrCode = FETCH_UNKNOWN;
176 snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
181 * Emit status message
183 void
184 fetch_info(const char *fmt, ...)
186 va_list ap;
188 va_start(ap, fmt);
189 vfprintf(stderr, fmt, ap);
190 va_end(ap);
191 fputc('\n', stderr);
195 /*** Network-related utility functions ***************************************/
198 * Return the default port for a scheme
201 fetch_default_port(const char *scheme)
203 struct servent *se;
205 if ((se = getservbyname(scheme, "tcp")) != NULL)
206 return (ntohs(se->s_port));
207 if (strcasecmp(scheme, SCHEME_FTP) == 0)
208 return (FTP_DEFAULT_PORT);
209 if (strcasecmp(scheme, SCHEME_HTTP) == 0)
210 return (HTTP_DEFAULT_PORT);
211 return (0);
215 * Return the default proxy port for a scheme
218 fetch_default_proxy_port(const char *scheme)
220 if (strcasecmp(scheme, SCHEME_FTP) == 0)
221 return (FTP_DEFAULT_PROXY_PORT);
222 if (strcasecmp(scheme, SCHEME_HTTP) == 0)
223 return (HTTP_DEFAULT_PROXY_PORT);
224 return (0);
229 * Create a connection for an existing descriptor.
231 conn_t *
232 fetch_reopen(int sd)
234 conn_t *conn;
236 /* allocate and fill connection structure */
237 if ((conn = calloc(1, sizeof(*conn))) == NULL)
238 return (NULL);
239 conn->ftp_home = NULL;
240 conn->cache_url = NULL;
241 conn->next_buf = NULL;
242 conn->next_len = 0;
243 conn->sd = sd;
244 return (conn);
249 * Bind a socket to a specific local address
252 fetch_bind(int sd, int af, const char *addr)
254 struct addrinfo hints, *res, *res0;
256 memset(&hints, 0, sizeof(hints));
257 hints.ai_family = af;
258 hints.ai_socktype = SOCK_STREAM;
259 hints.ai_protocol = 0;
260 if (getaddrinfo(addr, NULL, &hints, &res0))
261 return (-1);
262 for (res = res0; res; res = res->ai_next) {
263 if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
264 return (0);
266 return (-1);
271 * Establish a TCP connection to the specified port on the specified host.
273 conn_t *
274 fetch_connect(struct url *url, int af, int verbose)
276 conn_t *conn;
277 char pbuf[10];
278 const char *bindaddr;
279 struct addrinfo hints, *res, *res0;
280 int sd, error;
282 if (verbose)
283 fetch_info("looking up %s", url->host);
285 /* look up host name and set up socket address structure */
286 snprintf(pbuf, sizeof(pbuf), "%d", url->port);
287 memset(&hints, 0, sizeof(hints));
288 hints.ai_family = af;
289 hints.ai_socktype = SOCK_STREAM;
290 hints.ai_protocol = 0;
291 if ((error = getaddrinfo(url->host, pbuf, &hints, &res0)) != 0) {
292 netdb_seterr(error);
293 return (NULL);
295 bindaddr = getenv("FETCH_BIND_ADDRESS");
297 if (verbose)
298 fetch_info("connecting to %s:%d", url->host, url->port);
300 /* try to connect */
301 for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
302 if ((sd = socket(res->ai_family, res->ai_socktype,
303 res->ai_protocol)) == -1)
304 continue;
305 if (bindaddr != NULL && *bindaddr != '\0' &&
306 fetch_bind(sd, res->ai_family, bindaddr) != 0) {
307 fetch_info("failed to bind to '%s'", bindaddr);
308 close(sd);
309 continue;
311 if (connect(sd, res->ai_addr, res->ai_addrlen) == 0)
312 break;
313 close(sd);
315 freeaddrinfo(res0);
316 if (sd == -1) {
317 fetch_syserr();
318 return (NULL);
321 if ((conn = fetch_reopen(sd)) == NULL) {
322 fetch_syserr();
323 close(sd);
324 return (NULL);
326 conn->cache_url = fetchCopyURL(url);
327 conn->cache_af = af;
328 return (conn);
331 static conn_t *connection_cache;
332 static int cache_global_limit = 0;
333 static int cache_per_host_limit = 0;
336 * Initialise cache with the given limits.
338 void
339 fetchConnectionCacheInit(int global_limit, int per_host_limit)
342 if (global_limit < 0)
343 cache_global_limit = INT_MAX;
344 else if (per_host_limit > global_limit)
345 cache_global_limit = per_host_limit;
346 else
347 cache_global_limit = global_limit;
348 if (per_host_limit < 0)
349 cache_per_host_limit = INT_MAX;
350 else
351 cache_per_host_limit = per_host_limit;
355 * Flush cache and free all associated resources.
357 void
358 fetchConnectionCacheClose(void)
360 conn_t *conn;
362 while ((conn = connection_cache) != NULL) {
363 connection_cache = conn->next_cached;
364 (*conn->cache_close)(conn);
369 * Check connection cache for an existing entry matching
370 * protocol/host/port/user/password/family.
372 conn_t *
373 fetch_cache_get(const struct url *url, int af)
375 conn_t *conn, *last_conn = NULL;
377 for (conn = connection_cache; conn; conn = conn->next_cached) {
378 if (conn->cache_url->port == url->port &&
379 strcmp(conn->cache_url->scheme, url->scheme) == 0 &&
380 strcmp(conn->cache_url->host, url->host) == 0 &&
381 strcmp(conn->cache_url->user, url->user) == 0 &&
382 strcmp(conn->cache_url->pwd, url->pwd) == 0 &&
383 (conn->cache_af == AF_UNSPEC || af == AF_UNSPEC ||
384 conn->cache_af == af)) {
385 if (last_conn != NULL)
386 last_conn->next_cached = conn->next_cached;
387 else
388 connection_cache = conn->next_cached;
389 return conn;
393 return NULL;
397 * Put the connection back into the cache for reuse.
398 * If the connection is freed due to LRU or if the cache
399 * is explicitly closed, the given callback is called.
401 void
402 fetch_cache_put(conn_t *conn, int (*closecb)(conn_t *))
404 conn_t *iter, *last;
405 int global_count, host_count;
407 if (conn->cache_url == NULL || cache_global_limit == 0) {
408 (*closecb)(conn);
409 return;
412 global_count = host_count = 0;
413 last = NULL;
414 for (iter = connection_cache; iter;
415 last = iter, iter = iter->next_cached) {
416 ++global_count;
417 if (strcmp(conn->cache_url->host, iter->cache_url->host) == 0)
418 ++host_count;
419 if (global_count < cache_global_limit &&
420 host_count < cache_per_host_limit)
421 continue;
422 --global_count;
423 if (last != NULL)
424 last->next_cached = iter->next_cached;
425 else
426 connection_cache = iter->next_cached;
427 (*iter->cache_close)(iter);
430 conn->cache_close = closecb;
431 conn->next_cached = connection_cache;
432 connection_cache = conn;
436 * Enable SSL on a connection.
439 fetch_ssl(conn_t *conn, int verbose)
442 #ifdef WITH_SSL
443 /* Init the SSL library and context */
444 if (!SSL_library_init()){
445 fprintf(stderr, "SSL library init failed\n");
446 return (-1);
449 SSL_load_error_strings();
451 conn->ssl_meth = SSLv23_client_method();
452 conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
453 SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
455 conn->ssl = SSL_new(conn->ssl_ctx);
456 if (conn->ssl == NULL){
457 fprintf(stderr, "SSL context creation failed\n");
458 return (-1);
460 SSL_set_fd(conn->ssl, conn->sd);
461 if (SSL_connect(conn->ssl) == -1){
462 ERR_print_errors_fp(stderr);
463 return (-1);
466 if (verbose) {
467 X509_NAME *name;
468 char *str;
470 fprintf(stderr, "SSL connection established using %s\n",
471 SSL_get_cipher(conn->ssl));
472 conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
473 name = X509_get_subject_name(conn->ssl_cert);
474 str = X509_NAME_oneline(name, 0, 0);
475 printf("Certificate subject: %s\n", str);
476 free(str);
477 name = X509_get_issuer_name(conn->ssl_cert);
478 str = X509_NAME_oneline(name, 0, 0);
479 printf("Certificate issuer: %s\n", str);
480 free(str);
483 return (0);
484 #else
485 (void)conn;
486 (void)verbose;
487 fprintf(stderr, "SSL support disabled\n");
488 return (-1);
489 #endif
494 * Read a character from a connection w/ timeout
496 ssize_t
497 fetch_read(conn_t *conn, char *buf, size_t len)
499 struct timeval now, timeout, waittv;
500 fd_set readfds;
501 ssize_t rlen;
502 int r;
504 if (len == 0)
505 return 0;
507 if (conn->next_len != 0) {
508 if (conn->next_len < len)
509 len = conn->next_len;
510 memmove(buf, conn->next_buf, len);
511 conn->next_len -= len;
512 conn->next_buf += len;
513 return len;
516 if (fetchTimeout) {
517 FD_ZERO(&readfds);
518 gettimeofday(&timeout, NULL);
519 timeout.tv_sec += fetchTimeout;
522 for (;;) {
523 while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
524 FD_SET(conn->sd, &readfds);
525 gettimeofday(&now, NULL);
526 waittv.tv_sec = timeout.tv_sec - now.tv_sec;
527 waittv.tv_usec = timeout.tv_usec - now.tv_usec;
528 if (waittv.tv_usec < 0) {
529 waittv.tv_usec += 1000000;
530 waittv.tv_sec--;
532 if (waittv.tv_sec < 0) {
533 errno = ETIMEDOUT;
534 fetch_syserr();
535 return (-1);
537 errno = 0;
538 r = select(conn->sd + 1, &readfds, NULL, NULL, &waittv);
539 if (r == -1) {
540 if (errno == EINTR && fetchRestartCalls)
541 continue;
542 fetch_syserr();
543 return (-1);
546 #ifdef WITH_SSL
547 if (conn->ssl != NULL)
548 rlen = SSL_read(conn->ssl, buf, len);
549 else
550 #endif
551 rlen = read(conn->sd, buf, len);
552 if (rlen >= 0)
553 break;
555 if (errno != EINTR || !fetchRestartCalls)
556 return (-1);
558 return (rlen);
563 * Read a line of text from a connection w/ timeout
565 #define MIN_BUF_SIZE 1024
568 fetch_getln(conn_t *conn)
570 char *tmp, *next;
571 size_t tmpsize;
572 ssize_t len;
574 if (conn->buf == NULL) {
575 if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
576 errno = ENOMEM;
577 return (-1);
579 conn->bufsize = MIN_BUF_SIZE;
582 conn->buflen = 0;
583 next = NULL;
585 do {
587 * conn->bufsize != conn->buflen at this point,
588 * so the buffer can be NUL-terminated below for
589 * the case of len == 0.
591 len = fetch_read(conn, conn->buf + conn->buflen,
592 conn->bufsize - conn->buflen);
593 if (len == -1)
594 return (-1);
595 if (len == 0)
596 break;
597 next = memchr(conn->buf + conn->buflen, '\n', len);
598 conn->buflen += len;
599 if (conn->buflen == conn->bufsize && next == NULL) {
600 tmp = conn->buf;
601 tmpsize = conn->bufsize * 2;
602 if (tmpsize < conn->bufsize) {
603 errno = ENOMEM;
604 return (-1);
606 if ((tmp = realloc(tmp, tmpsize)) == NULL) {
607 errno = ENOMEM;
608 return (-1);
610 conn->buf = tmp;
611 conn->bufsize = tmpsize;
613 } while (next == NULL);
615 if (next != NULL) {
616 *next = '\0';
617 conn->next_buf = next + 1;
618 conn->next_len = conn->buflen - (conn->next_buf - conn->buf);
619 conn->buflen = next - conn->buf;
620 } else {
621 conn->buf[conn->buflen] = '\0';
622 conn->next_len = 0;
624 return (0);
628 * Write a vector to a connection w/ timeout
629 * Note: can modify the iovec.
631 ssize_t
632 fetch_write(conn_t *conn, const void *buf, size_t len)
634 struct timeval now, timeout, waittv;
635 fd_set writefds;
636 ssize_t wlen, total;
637 int r;
638 #ifndef MSG_NOSIGNAL
639 static int killed_sigpipe;
640 #endif
642 #ifndef MSG_NOSIGNAL
643 if (!killed_sigpipe) {
644 signal(SIGPIPE, SIG_IGN);
645 killed_sigpipe = 1;
647 #endif
650 if (fetchTimeout) {
651 FD_ZERO(&writefds);
652 gettimeofday(&timeout, NULL);
653 timeout.tv_sec += fetchTimeout;
656 total = 0;
657 while (len) {
658 while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
659 FD_SET(conn->sd, &writefds);
660 gettimeofday(&now, NULL);
661 waittv.tv_sec = timeout.tv_sec - now.tv_sec;
662 waittv.tv_usec = timeout.tv_usec - now.tv_usec;
663 if (waittv.tv_usec < 0) {
664 waittv.tv_usec += 1000000;
665 waittv.tv_sec--;
667 if (waittv.tv_sec < 0) {
668 errno = ETIMEDOUT;
669 fetch_syserr();
670 return (-1);
672 errno = 0;
673 r = select(conn->sd + 1, NULL, &writefds, NULL, &waittv);
674 if (r == -1) {
675 if (errno == EINTR && fetchRestartCalls)
676 continue;
677 return (-1);
680 errno = 0;
681 #ifdef WITH_SSL
682 if (conn->ssl != NULL)
683 wlen = SSL_write(conn->ssl, buf, len);
684 else
685 #endif
686 #ifndef MSG_NOSIGNAL
687 wlen = send(conn->sd, buf, len, 0);
688 #else
689 wlen = send(conn->sd, buf, len, MSG_NOSIGNAL);
690 #endif
691 if (wlen == 0) {
692 /* we consider a short write a failure */
693 errno = EPIPE;
694 fetch_syserr();
695 return (-1);
697 if (wlen < 0) {
698 if (errno == EINTR && fetchRestartCalls)
699 continue;
700 return (-1);
702 total += wlen;
703 buf = (const char *)buf + wlen;
704 len -= wlen;
706 return (total);
711 * Close connection
714 fetch_close(conn_t *conn)
716 int ret;
718 ret = close(conn->sd);
719 if (conn->cache_url)
720 fetchFreeURL(conn->cache_url);
721 free(conn->ftp_home);
722 free(conn->buf);
723 free(conn);
724 return (ret);
728 /*** Directory-related utility functions *************************************/
731 fetch_add_entry(struct url_list *ue, struct url *base, const char *name,
732 int pre_quoted)
734 struct url *tmp;
735 char *tmp_name;
736 size_t base_doc_len, name_len, i;
737 unsigned char c;
739 if (strchr(name, '/') != NULL ||
740 strcmp(name, "..") == 0 ||
741 strcmp(name, ".") == 0)
742 return 0;
744 if (strcmp(base->doc, "/") == 0)
745 base_doc_len = 0;
746 else
747 base_doc_len = strlen(base->doc);
749 name_len = 1;
750 for (i = 0; name[i] != '\0'; ++i) {
751 if ((!pre_quoted && name[i] == '%') ||
752 !fetch_urlpath_safe(name[i]))
753 name_len += 3;
754 else
755 ++name_len;
758 tmp_name = malloc( base_doc_len + name_len + 1);
759 if (tmp_name == NULL) {
760 errno = ENOMEM;
761 fetch_syserr();
762 return (-1);
765 if (ue->length + 1 >= ue->alloc_size) {
766 tmp = realloc(ue->urls, (ue->alloc_size * 2 + 1) * sizeof(*tmp));
767 if (tmp == NULL) {
768 free(tmp_name);
769 errno = ENOMEM;
770 fetch_syserr();
771 return (-1);
773 ue->alloc_size = ue->alloc_size * 2 + 1;
774 ue->urls = tmp;
777 tmp = ue->urls + ue->length;
778 strcpy(tmp->scheme, base->scheme);
779 strcpy(tmp->user, base->user);
780 strcpy(tmp->pwd, base->pwd);
781 strcpy(tmp->host, base->host);
782 tmp->port = base->port;
783 tmp->doc = tmp_name;
784 memcpy(tmp->doc, base->doc, base_doc_len);
785 tmp->doc[base_doc_len] = '/';
787 for (i = base_doc_len + 1; *name != '\0'; ++name) {
788 if ((!pre_quoted && *name == '%') ||
789 !fetch_urlpath_safe(*name)) {
790 tmp->doc[i++] = '%';
791 c = (unsigned char)*name / 16;
792 if (c < 10)
793 tmp->doc[i++] = '0' + c;
794 else
795 tmp->doc[i++] = 'a' - 10 + c;
796 c = (unsigned char)*name % 16;
797 if (c < 10)
798 tmp->doc[i++] = '0' + c;
799 else
800 tmp->doc[i++] = 'a' - 10 + c;
801 } else {
802 tmp->doc[i++] = *name;
805 tmp->doc[i] = '\0';
807 tmp->offset = 0;
808 tmp->length = 0;
809 tmp->last_modified = -1;
811 ++ue->length;
813 return (0);
816 void
817 fetchInitURLList(struct url_list *ue)
819 ue->length = ue->alloc_size = 0;
820 ue->urls = NULL;
824 fetchAppendURLList(struct url_list *dst, const struct url_list *src)
826 size_t i, j, len;
828 len = dst->length + src->length;
829 if (len > dst->alloc_size) {
830 struct url *tmp;
832 tmp = realloc(dst->urls, len * sizeof(*tmp));
833 if (tmp == NULL) {
834 errno = ENOMEM;
835 fetch_syserr();
836 return (-1);
838 dst->alloc_size = len;
839 dst->urls = tmp;
842 for (i = 0, j = dst->length; i < src->length; ++i, ++j) {
843 dst->urls[j] = src->urls[i];
844 dst->urls[j].doc = strdup(src->urls[i].doc);
845 if (dst->urls[j].doc == NULL) {
846 while (i-- > 0)
847 free(dst->urls[j].doc);
848 fetch_syserr();
849 return -1;
852 dst->length = len;
854 return 0;
857 void
858 fetchFreeURLList(struct url_list *ue)
860 size_t i;
862 for (i = 0; i < ue->length; ++i)
863 free(ue->urls[i].doc);
864 free(ue->urls);
865 ue->length = ue->alloc_size = 0;
869 /*** Authentication-related utility functions ********************************/
871 static const char *
872 fetch_read_word(FILE *f)
874 static char word[1024];
876 if (fscanf(f, " %1023s ", word) != 1)
877 return (NULL);
878 return (word);
882 * Get authentication data for a URL from .netrc
885 fetch_netrc_auth(struct url *url)
887 char fn[PATH_MAX];
888 const char *word;
889 char *p;
890 FILE *f;
892 if ((p = getenv("NETRC")) != NULL) {
893 if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
894 fetch_info("$NETRC specifies a file name "
895 "longer than PATH_MAX");
896 return (-1);
898 } else {
899 if ((p = getenv("HOME")) != NULL) {
900 struct passwd *pwd;
902 if ((pwd = getpwuid(getuid())) == NULL ||
903 (p = pwd->pw_dir) == NULL)
904 return (-1);
906 if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
907 return (-1);
910 if ((f = fopen(fn, "r")) == NULL)
911 return (-1);
912 while ((word = fetch_read_word(f)) != NULL) {
913 if (strcmp(word, "default") == 0)
914 break;
915 if (strcmp(word, "machine") == 0 &&
916 (word = fetch_read_word(f)) != NULL &&
917 strcasecmp(word, url->host) == 0) {
918 break;
921 if (word == NULL)
922 goto ferr;
923 while ((word = fetch_read_word(f)) != NULL) {
924 if (strcmp(word, "login") == 0) {
925 if ((word = fetch_read_word(f)) == NULL)
926 goto ferr;
927 if (snprintf(url->user, sizeof(url->user),
928 "%s", word) > (int)sizeof(url->user)) {
929 fetch_info("login name in .netrc is too long");
930 url->user[0] = '\0';
932 } else if (strcmp(word, "password") == 0) {
933 if ((word = fetch_read_word(f)) == NULL)
934 goto ferr;
935 if (snprintf(url->pwd, sizeof(url->pwd),
936 "%s", word) > (int)sizeof(url->pwd)) {
937 fetch_info("password in .netrc is too long");
938 url->pwd[0] = '\0';
940 } else if (strcmp(word, "account") == 0) {
941 if ((word = fetch_read_word(f)) == NULL)
942 goto ferr;
943 /* XXX not supported! */
944 } else {
945 break;
948 fclose(f);
949 return (0);
950 ferr:
951 fclose(f);
952 return (-1);
956 * The no_proxy environment variable specifies a set of domains for
957 * which the proxy should not be consulted; the contents is a comma-,
958 * or space-separated list of domain names. A single asterisk will
959 * override all proxy variables and no transactions will be proxied
960 * (for compatability with lynx and curl, see the discussion at
961 * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>).
964 fetch_no_proxy_match(const char *host)
966 const char *no_proxy, *p, *q;
967 size_t h_len, d_len;
969 if ((no_proxy = getenv("NO_PROXY")) == NULL &&
970 (no_proxy = getenv("no_proxy")) == NULL)
971 return (0);
973 /* asterisk matches any hostname */
974 if (strcmp(no_proxy, "*") == 0)
975 return (1);
977 h_len = strlen(host);
978 p = no_proxy;
979 do {
980 /* position p at the beginning of a domain suffix */
981 while (*p == ',' || isspace((unsigned char)*p))
982 p++;
984 /* position q at the first separator character */
985 for (q = p; *q; ++q)
986 if (*q == ',' || isspace((unsigned char)*q))
987 break;
989 d_len = q - p;
990 if (d_len > 0 && h_len > d_len &&
991 strncasecmp(host + h_len - d_len,
992 p, d_len) == 0) {
993 /* domain name matches */
994 return (1);
997 p = q + 1;
998 } while (*q);
1000 return (0);
1003 struct fetchIO {
1004 void *io_cookie;
1005 ssize_t (*io_read)(void *, void *, size_t);
1006 ssize_t (*io_write)(void *, const void *, size_t);
1007 void (*io_close)(void *);
1010 void
1011 fetchIO_close(fetchIO *f)
1013 if (f->io_close != NULL)
1014 (*f->io_close)(f->io_cookie);
1016 free(f);
1019 fetchIO *
1020 fetchIO_unopen(void *io_cookie, ssize_t (*io_read)(void *, void *, size_t),
1021 ssize_t (*io_write)(void *, const void *, size_t),
1022 void (*io_close)(void *))
1024 fetchIO *f;
1026 f = malloc(sizeof(*f));
1027 if (f == NULL)
1028 return f;
1030 f->io_cookie = io_cookie;
1031 f->io_read = io_read;
1032 f->io_write = io_write;
1033 f->io_close = io_close;
1035 return f;
1038 ssize_t
1039 fetchIO_read(fetchIO *f, void *buf, size_t len)
1041 if (f->io_read == NULL)
1042 return EBADF;
1043 return (*f->io_read)(f->io_cookie, buf, len);
1046 ssize_t
1047 fetchIO_write(fetchIO *f, const void *buf, size_t len)
1049 if (f->io_read == NULL)
1050 return EBADF;
1051 return (*f->io_write)(f->io_cookie, buf, len);