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