1*914f0ba5Schristos /* $NetBSD: common.c,v 1.7 2024/02/02 22:19:05 christos Exp $ */
2fe618babSjoerg /*-
3fe618babSjoerg * Copyright (c) 1998-2004 Dag-Erling Co�dan Sm�rgrav
470160d70Sjoerg * Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
5fe618babSjoerg * All rights reserved.
6fe618babSjoerg *
7fe618babSjoerg * Redistribution and use in source and binary forms, with or without
8fe618babSjoerg * modification, are permitted provided that the following conditions
9fe618babSjoerg * are met:
10fe618babSjoerg * 1. Redistributions of source code must retain the above copyright
11fe618babSjoerg * notice, this list of conditions and the following disclaimer
12fe618babSjoerg * in this position and unchanged.
13fe618babSjoerg * 2. Redistributions in binary form must reproduce the above copyright
14fe618babSjoerg * notice, this list of conditions and the following disclaimer in the
15fe618babSjoerg * documentation and/or other materials provided with the distribution.
16fe618babSjoerg * 3. The name of the author may not be used to endorse or promote products
17fe618babSjoerg * derived from this software without specific prior written permission
18fe618babSjoerg *
19fe618babSjoerg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20fe618babSjoerg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21fe618babSjoerg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22fe618babSjoerg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23fe618babSjoerg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24fe618babSjoerg * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25fe618babSjoerg * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26fe618babSjoerg * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27fe618babSjoerg * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28fe618babSjoerg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29fe618babSjoerg *
30fe618babSjoerg * $FreeBSD: common.c,v 1.53 2007/12/19 00:26:36 des Exp $
31fe618babSjoerg */
32fe618babSjoerg
33fe618babSjoerg #if HAVE_CONFIG_H
34fe618babSjoerg #include "config.h"
35fe618babSjoerg #endif
36fe618babSjoerg #ifndef NETBSD
37fe618babSjoerg #include <nbcompat.h>
38fe618babSjoerg #endif
39fe618babSjoerg
40fe618babSjoerg #include <sys/types.h>
41fe618babSjoerg #include <sys/socket.h>
42fe618babSjoerg #include <sys/time.h>
43fe618babSjoerg #include <sys/uio.h>
44*914f0ba5Schristos #if defined(HAVE_POLL_H) || defined(NETBSD)
45*914f0ba5Schristos #include <poll.h>
46*914f0ba5Schristos #define HAVE_POLL
47*914f0ba5Schristos #elif HAVE_SYS_POLL_H
48*914f0ba5Schristos #define HAVE_POLL
49*914f0ba5Schristos #include <sys/poll.h>
50*914f0ba5Schristos #endif
51fe618babSjoerg
52fe618babSjoerg #include <netinet/in.h>
537bacb5afSjoerg #include <arpa/inet.h>
54fe618babSjoerg
55fe618babSjoerg #include <ctype.h>
56fe618babSjoerg #include <errno.h>
57fe618babSjoerg #if defined(HAVE_INTTYPES_H) || defined(NETBSD)
58fe618babSjoerg #include <inttypes.h>
59fe618babSjoerg #endif
60fe618babSjoerg #ifndef NETBSD
61fe618babSjoerg #include <nbcompat/netdb.h>
62fe618babSjoerg #else
63fe618babSjoerg #include <netdb.h>
64fe618babSjoerg #endif
65fe618babSjoerg #include <pwd.h>
66fe618babSjoerg #include <stdarg.h>
67fe618babSjoerg #include <stdlib.h>
68fe618babSjoerg #include <stdio.h>
69fe618babSjoerg #include <string.h>
70fe618babSjoerg #include <unistd.h>
71fe618babSjoerg
7270160d70Sjoerg #ifndef MSG_NOSIGNAL
7370160d70Sjoerg #include <signal.h>
7470160d70Sjoerg #endif
7570160d70Sjoerg
76fe618babSjoerg #include "fetch.h"
77fe618babSjoerg #include "common.h"
78fe618babSjoerg
79fe618babSjoerg /*** Local data **************************************************************/
80fe618babSjoerg
81fe618babSjoerg /*
82fe618babSjoerg * Error messages for resolver errors
83fe618babSjoerg */
84fe618babSjoerg static struct fetcherr netdb_errlist[] = {
85fe618babSjoerg #ifdef EAI_NODATA
86fe618babSjoerg { EAI_NODATA, FETCH_RESOLV, "Host not found" },
87fe618babSjoerg #endif
88fe618babSjoerg { EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" },
89fe618babSjoerg { EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" },
90fe618babSjoerg { EAI_NONAME, FETCH_RESOLV, "No address record" },
91fe618babSjoerg { -1, FETCH_UNKNOWN, "Unknown resolver error" }
92fe618babSjoerg };
93fe618babSjoerg
94fe618babSjoerg /*** Error-reporting functions ***********************************************/
95fe618babSjoerg
96fe618babSjoerg /*
97fe618babSjoerg * Map error code to string
98fe618babSjoerg */
99fe618babSjoerg static struct fetcherr *
fetch_finderr(struct fetcherr * p,int e)100fe618babSjoerg fetch_finderr(struct fetcherr *p, int e)
101fe618babSjoerg {
102fe618babSjoerg while (p->num != -1 && p->num != e)
103fe618babSjoerg p++;
104fe618babSjoerg return (p);
105fe618babSjoerg }
106fe618babSjoerg
107fe618babSjoerg /*
108fe618babSjoerg * Set error code
109fe618babSjoerg */
110fe618babSjoerg void
fetch_seterr(struct fetcherr * p,int e)111fe618babSjoerg fetch_seterr(struct fetcherr *p, int e)
112fe618babSjoerg {
113fe618babSjoerg p = fetch_finderr(p, e);
114fe618babSjoerg fetchLastErrCode = p->cat;
115fe618babSjoerg snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
116fe618babSjoerg }
117fe618babSjoerg
118fe618babSjoerg /*
119fe618babSjoerg * Set error code according to errno
120fe618babSjoerg */
121fe618babSjoerg void
fetch_syserr(void)122fe618babSjoerg fetch_syserr(void)
123fe618babSjoerg {
124fe618babSjoerg switch (errno) {
125fe618babSjoerg case 0:
126fe618babSjoerg fetchLastErrCode = FETCH_OK;
127fe618babSjoerg break;
128fe618babSjoerg case EPERM:
129fe618babSjoerg case EACCES:
130fe618babSjoerg case EROFS:
131fe618babSjoerg #ifdef EAUTH
132fe618babSjoerg case EAUTH:
133fe618babSjoerg #endif
134fe618babSjoerg #ifdef ENEEDAUTH
135fe618babSjoerg case ENEEDAUTH:
136fe618babSjoerg #endif
137fe618babSjoerg fetchLastErrCode = FETCH_AUTH;
138fe618babSjoerg break;
139fe618babSjoerg case ENOENT:
140fe618babSjoerg case EISDIR: /* XXX */
141fe618babSjoerg fetchLastErrCode = FETCH_UNAVAIL;
142fe618babSjoerg break;
143fe618babSjoerg case ENOMEM:
144fe618babSjoerg fetchLastErrCode = FETCH_MEMORY;
145fe618babSjoerg break;
146fe618babSjoerg case EBUSY:
147fe618babSjoerg case EAGAIN:
148fe618babSjoerg fetchLastErrCode = FETCH_TEMP;
149fe618babSjoerg break;
150fe618babSjoerg case EEXIST:
151fe618babSjoerg fetchLastErrCode = FETCH_EXISTS;
152fe618babSjoerg break;
153fe618babSjoerg case ENOSPC:
154fe618babSjoerg fetchLastErrCode = FETCH_FULL;
155fe618babSjoerg break;
156fe618babSjoerg case EADDRINUSE:
157fe618babSjoerg case EADDRNOTAVAIL:
158fe618babSjoerg case ENETDOWN:
159fe618babSjoerg case ENETUNREACH:
160fe618babSjoerg case ENETRESET:
161fe618babSjoerg case EHOSTUNREACH:
162fe618babSjoerg fetchLastErrCode = FETCH_NETWORK;
163fe618babSjoerg break;
164fe618babSjoerg case ECONNABORTED:
165fe618babSjoerg case ECONNRESET:
166fe618babSjoerg fetchLastErrCode = FETCH_ABORT;
167fe618babSjoerg break;
168fe618babSjoerg case ETIMEDOUT:
169fe618babSjoerg fetchLastErrCode = FETCH_TIMEOUT;
170fe618babSjoerg break;
171fe618babSjoerg case ECONNREFUSED:
172fe618babSjoerg case EHOSTDOWN:
173fe618babSjoerg fetchLastErrCode = FETCH_DOWN;
174fe618babSjoerg break;
175fe618babSjoerg default:
176fe618babSjoerg fetchLastErrCode = FETCH_UNKNOWN;
177fe618babSjoerg }
178fe618babSjoerg snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
179fe618babSjoerg }
180fe618babSjoerg
181fe618babSjoerg
182fe618babSjoerg /*
183fe618babSjoerg * Emit status message
184fe618babSjoerg */
185fe618babSjoerg void
fetch_info(const char * fmt,...)186fe618babSjoerg fetch_info(const char *fmt, ...)
187fe618babSjoerg {
188fe618babSjoerg va_list ap;
189fe618babSjoerg
190fe618babSjoerg va_start(ap, fmt);
191fe618babSjoerg vfprintf(stderr, fmt, ap);
192fe618babSjoerg va_end(ap);
193fe618babSjoerg fputc('\n', stderr);
194fe618babSjoerg }
195fe618babSjoerg
196fe618babSjoerg
197fe618babSjoerg /*** Network-related utility functions ***************************************/
198fe618babSjoerg
199fe618babSjoerg /*
200fe618babSjoerg * Return the default port for a scheme
201fe618babSjoerg */
202fe618babSjoerg int
fetch_default_port(const char * scheme)203fe618babSjoerg fetch_default_port(const char *scheme)
204fe618babSjoerg {
205fe618babSjoerg struct servent *se;
206fe618babSjoerg
207fe618babSjoerg if ((se = getservbyname(scheme, "tcp")) != NULL)
208fe618babSjoerg return (ntohs(se->s_port));
209fe618babSjoerg if (strcasecmp(scheme, SCHEME_FTP) == 0)
210fe618babSjoerg return (FTP_DEFAULT_PORT);
211fe618babSjoerg if (strcasecmp(scheme, SCHEME_HTTP) == 0)
212fe618babSjoerg return (HTTP_DEFAULT_PORT);
213fe618babSjoerg return (0);
214fe618babSjoerg }
215fe618babSjoerg
216fe618babSjoerg /*
217fe618babSjoerg * Return the default proxy port for a scheme
218fe618babSjoerg */
219fe618babSjoerg int
fetch_default_proxy_port(const char * scheme)220fe618babSjoerg fetch_default_proxy_port(const char *scheme)
221fe618babSjoerg {
222fe618babSjoerg if (strcasecmp(scheme, SCHEME_FTP) == 0)
223fe618babSjoerg return (FTP_DEFAULT_PROXY_PORT);
224fe618babSjoerg if (strcasecmp(scheme, SCHEME_HTTP) == 0)
225fe618babSjoerg return (HTTP_DEFAULT_PROXY_PORT);
226fe618babSjoerg return (0);
227fe618babSjoerg }
228fe618babSjoerg
229fe618babSjoerg
230fe618babSjoerg /*
231fe618babSjoerg * Create a connection for an existing descriptor.
232fe618babSjoerg */
233fe618babSjoerg conn_t *
fetch_reopen(int sd)234fe618babSjoerg fetch_reopen(int sd)
235fe618babSjoerg {
236fe618babSjoerg conn_t *conn;
237fe618babSjoerg
238fe618babSjoerg /* allocate and fill connection structure */
239fe618babSjoerg if ((conn = calloc(1, sizeof(*conn))) == NULL)
240fe618babSjoerg return (NULL);
2410ab1cb6dSjoerg conn->ftp_home = NULL;
24270160d70Sjoerg conn->cache_url = NULL;
243f298aa92Sjoerg conn->next_buf = NULL;
244f298aa92Sjoerg conn->next_len = 0;
245fe618babSjoerg conn->sd = sd;
246*914f0ba5Schristos #ifdef HAVE_POLL
247*914f0ba5Schristos conn->buf_events = POLLIN;
248*914f0ba5Schristos #endif
249fe618babSjoerg return (conn);
250fe618babSjoerg }
251fe618babSjoerg
252fe618babSjoerg
253fe618babSjoerg /*
254fe618babSjoerg * Bind a socket to a specific local address
255fe618babSjoerg */
256fe618babSjoerg int
fetch_bind(int sd,int af,const char * addr)257fe618babSjoerg fetch_bind(int sd, int af, const char *addr)
258fe618babSjoerg {
259fe618babSjoerg struct addrinfo hints, *res, *res0;
260fe618babSjoerg
261fe618babSjoerg memset(&hints, 0, sizeof(hints));
262fe618babSjoerg hints.ai_family = af;
263fe618babSjoerg hints.ai_socktype = SOCK_STREAM;
264fe618babSjoerg hints.ai_protocol = 0;
265ebe76b0cSjoerg if (getaddrinfo(addr, NULL, &hints, &res0))
266fe618babSjoerg return (-1);
267ebe76b0cSjoerg for (res = res0; res; res = res->ai_next) {
268fe618babSjoerg if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
269fe618babSjoerg return (0);
270ebe76b0cSjoerg }
271fe618babSjoerg return (-1);
272fe618babSjoerg }
273fe618babSjoerg
274fe618babSjoerg
275fe618babSjoerg /*
276fe618babSjoerg * Establish a TCP connection to the specified port on the specified host.
277fe618babSjoerg */
278fe618babSjoerg conn_t *
fetch_connect(struct url * url,int af,int verbose)27970160d70Sjoerg fetch_connect(struct url *url, int af, int verbose)
280fe618babSjoerg {
281fe618babSjoerg conn_t *conn;
282fe618babSjoerg char pbuf[10];
283fe618babSjoerg const char *bindaddr;
284fe618babSjoerg struct addrinfo hints, *res, *res0;
285fe618babSjoerg int sd, error;
286fe618babSjoerg
287fe618babSjoerg if (verbose)
28870160d70Sjoerg fetch_info("looking up %s", url->host);
289fe618babSjoerg
290fe618babSjoerg /* look up host name and set up socket address structure */
29170160d70Sjoerg snprintf(pbuf, sizeof(pbuf), "%d", url->port);
292fe618babSjoerg memset(&hints, 0, sizeof(hints));
293fe618babSjoerg hints.ai_family = af;
294fe618babSjoerg hints.ai_socktype = SOCK_STREAM;
295fe618babSjoerg hints.ai_protocol = 0;
29670160d70Sjoerg if ((error = getaddrinfo(url->host, pbuf, &hints, &res0)) != 0) {
297fe618babSjoerg netdb_seterr(error);
298fe618babSjoerg return (NULL);
299fe618babSjoerg }
300fe618babSjoerg bindaddr = getenv("FETCH_BIND_ADDRESS");
301fe618babSjoerg
302fe618babSjoerg if (verbose)
30370160d70Sjoerg fetch_info("connecting to %s:%d", url->host, url->port);
304fe618babSjoerg
305fe618babSjoerg /* try to connect */
306fe618babSjoerg for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
307fe618babSjoerg if ((sd = socket(res->ai_family, res->ai_socktype,
308fe618babSjoerg res->ai_protocol)) == -1)
309fe618babSjoerg continue;
310fe618babSjoerg if (bindaddr != NULL && *bindaddr != '\0' &&
311fe618babSjoerg fetch_bind(sd, res->ai_family, bindaddr) != 0) {
312fe618babSjoerg fetch_info("failed to bind to '%s'", bindaddr);
313fe618babSjoerg close(sd);
314fe618babSjoerg continue;
315fe618babSjoerg }
316fe618babSjoerg if (connect(sd, res->ai_addr, res->ai_addrlen) == 0)
317fe618babSjoerg break;
318fe618babSjoerg close(sd);
319fe618babSjoerg }
320fe618babSjoerg freeaddrinfo(res0);
321fe618babSjoerg if (sd == -1) {
322fe618babSjoerg fetch_syserr();
323fe618babSjoerg return (NULL);
324fe618babSjoerg }
325fe618babSjoerg
326fe618babSjoerg if ((conn = fetch_reopen(sd)) == NULL) {
327fe618babSjoerg fetch_syserr();
328fe618babSjoerg close(sd);
329*914f0ba5Schristos return (NULL);
330fe618babSjoerg }
33170160d70Sjoerg conn->cache_url = fetchCopyURL(url);
33270160d70Sjoerg conn->cache_af = af;
333fe618babSjoerg return (conn);
334fe618babSjoerg }
335fe618babSjoerg
33670160d70Sjoerg static conn_t *connection_cache;
33770160d70Sjoerg static int cache_global_limit = 0;
33870160d70Sjoerg static int cache_per_host_limit = 0;
33970160d70Sjoerg
34070160d70Sjoerg /*
34170160d70Sjoerg * Initialise cache with the given limits.
34270160d70Sjoerg */
34370160d70Sjoerg void
fetchConnectionCacheInit(int global_limit,int per_host_limit)34470160d70Sjoerg fetchConnectionCacheInit(int global_limit, int per_host_limit)
34570160d70Sjoerg {
34670160d70Sjoerg
34770160d70Sjoerg if (global_limit < 0)
34870160d70Sjoerg cache_global_limit = INT_MAX;
34970160d70Sjoerg else if (per_host_limit > global_limit)
35070160d70Sjoerg cache_global_limit = per_host_limit;
35170160d70Sjoerg else
35270160d70Sjoerg cache_global_limit = global_limit;
35370160d70Sjoerg if (per_host_limit < 0)
35470160d70Sjoerg cache_per_host_limit = INT_MAX;
35570160d70Sjoerg else
35670160d70Sjoerg cache_per_host_limit = per_host_limit;
35770160d70Sjoerg }
35870160d70Sjoerg
35970160d70Sjoerg /*
36070160d70Sjoerg * Flush cache and free all associated resources.
36170160d70Sjoerg */
36270160d70Sjoerg void
fetchConnectionCacheClose(void)36370160d70Sjoerg fetchConnectionCacheClose(void)
36470160d70Sjoerg {
36570160d70Sjoerg conn_t *conn;
36670160d70Sjoerg
36770160d70Sjoerg while ((conn = connection_cache) != NULL) {
36870160d70Sjoerg connection_cache = conn->next_cached;
36970160d70Sjoerg (*conn->cache_close)(conn);
37070160d70Sjoerg }
37170160d70Sjoerg }
37270160d70Sjoerg
37370160d70Sjoerg /*
37470160d70Sjoerg * Check connection cache for an existing entry matching
37570160d70Sjoerg * protocol/host/port/user/password/family.
37670160d70Sjoerg */
37770160d70Sjoerg conn_t *
fetch_cache_get(const struct url * url,int af)37870160d70Sjoerg fetch_cache_get(const struct url *url, int af)
37970160d70Sjoerg {
38070160d70Sjoerg conn_t *conn, *last_conn = NULL;
38170160d70Sjoerg
382e7a5d890Schristos for (conn = connection_cache; conn; last_conn = conn,
383e7a5d890Schristos conn = conn->next_cached)
384e7a5d890Schristos {
38570160d70Sjoerg if (conn->cache_url->port == url->port &&
38670160d70Sjoerg strcmp(conn->cache_url->scheme, url->scheme) == 0 &&
38770160d70Sjoerg strcmp(conn->cache_url->host, url->host) == 0 &&
38870160d70Sjoerg strcmp(conn->cache_url->user, url->user) == 0 &&
38970160d70Sjoerg strcmp(conn->cache_url->pwd, url->pwd) == 0 &&
39070160d70Sjoerg (conn->cache_af == AF_UNSPEC || af == AF_UNSPEC ||
39170160d70Sjoerg conn->cache_af == af)) {
39270160d70Sjoerg if (last_conn != NULL)
39370160d70Sjoerg last_conn->next_cached = conn->next_cached;
39470160d70Sjoerg else
39570160d70Sjoerg connection_cache = conn->next_cached;
39670160d70Sjoerg return conn;
39770160d70Sjoerg }
39870160d70Sjoerg }
39970160d70Sjoerg
40070160d70Sjoerg return NULL;
40170160d70Sjoerg }
40270160d70Sjoerg
40370160d70Sjoerg /*
40470160d70Sjoerg * Put the connection back into the cache for reuse.
40570160d70Sjoerg * If the connection is freed due to LRU or if the cache
40670160d70Sjoerg * is explicitly closed, the given callback is called.
40770160d70Sjoerg */
40870160d70Sjoerg void
fetch_cache_put(conn_t * conn,int (* closecb)(conn_t *))40970160d70Sjoerg fetch_cache_put(conn_t *conn, int (*closecb)(conn_t *))
41070160d70Sjoerg {
411e7a5d890Schristos conn_t *iter, *last, *oiter;
412*914f0ba5Schristos int global_count, host_count, added;
41370160d70Sjoerg
41470160d70Sjoerg if (conn->cache_url == NULL || cache_global_limit == 0) {
41570160d70Sjoerg (*closecb)(conn);
41670160d70Sjoerg return;
41770160d70Sjoerg }
41870160d70Sjoerg
41970160d70Sjoerg global_count = host_count = 0;
42070160d70Sjoerg last = NULL;
421e7a5d890Schristos for (iter = connection_cache; iter; ) {
42270160d70Sjoerg ++global_count;
423*914f0ba5Schristos added = !strcmp(conn->cache_url->host, iter->cache_url->host);
424*914f0ba5Schristos if (added)
42570160d70Sjoerg ++host_count;
42670160d70Sjoerg if (global_count < cache_global_limit &&
427*914f0ba5Schristos host_count < cache_per_host_limit) {
428e7a5d890Schristos oiter = NULL;
429*914f0ba5Schristos last = iter;
430*914f0ba5Schristos } else {
43170160d70Sjoerg --global_count;
432*914f0ba5Schristos if (added)
433*914f0ba5Schristos --host_count;
43470160d70Sjoerg if (last != NULL)
43570160d70Sjoerg last->next_cached = iter->next_cached;
43670160d70Sjoerg else
43770160d70Sjoerg connection_cache = iter->next_cached;
438e7a5d890Schristos oiter = iter;
439e7a5d890Schristos }
440e7a5d890Schristos iter = iter->next_cached;
441e7a5d890Schristos if (oiter)
442e7a5d890Schristos (*oiter->cache_close)(oiter);
44370160d70Sjoerg }
44470160d70Sjoerg
44570160d70Sjoerg conn->cache_close = closecb;
44670160d70Sjoerg conn->next_cached = connection_cache;
44770160d70Sjoerg connection_cache = conn;
44870160d70Sjoerg }
449fe618babSjoerg
450fe618babSjoerg /*
451fe618babSjoerg * Enable SSL on a connection.
452fe618babSjoerg */
453fe618babSjoerg int
fetch_ssl(conn_t * conn,const struct url * URL,int verbose)454*914f0ba5Schristos fetch_ssl(conn_t *conn, const struct url *URL, int verbose)
455fe618babSjoerg {
456fe618babSjoerg
457fe618babSjoerg #ifdef WITH_SSL
458fe618babSjoerg /* Init the SSL library and context */
459fe618babSjoerg if (!SSL_library_init()){
460fe618babSjoerg fprintf(stderr, "SSL library init failed\n");
461fe618babSjoerg return (-1);
462fe618babSjoerg }
463fe618babSjoerg
464fe618babSjoerg SSL_load_error_strings();
465fe618babSjoerg
466fe618babSjoerg conn->ssl_meth = SSLv23_client_method();
467fe618babSjoerg conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
468fe618babSjoerg SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
4695defc0dfSriastradh if (getenv("SSL_NO_VERIFY_PEER") == NULL) {
4705defc0dfSriastradh SSL_CTX_set_default_verify_paths(conn->ssl_ctx);
4715defc0dfSriastradh SSL_CTX_set_verify(conn->ssl_ctx, SSL_VERIFY_PEER, NULL);
4725defc0dfSriastradh }
473fe618babSjoerg
474fe618babSjoerg conn->ssl = SSL_new(conn->ssl_ctx);
475fe618babSjoerg if (conn->ssl == NULL){
476fe618babSjoerg fprintf(stderr, "SSL context creation failed\n");
477fe618babSjoerg return (-1);
478fe618babSjoerg }
479*914f0ba5Schristos conn->buf_events = 0;
480fe618babSjoerg SSL_set_fd(conn->ssl, conn->sd);
481*914f0ba5Schristos #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
482*914f0ba5Schristos if (!SSL_set_tlsext_host_name(conn->ssl, (char *)(uintptr_t)URL->host))
483*914f0ba5Schristos {
484*914f0ba5Schristos fprintf(stderr,
485*914f0ba5Schristos "TLS server name indication extension failed for host %s\n",
486*914f0ba5Schristos URL->host);
487e60dc8a7Smlelstv return (-1);
488e60dc8a7Smlelstv }
489*914f0ba5Schristos #endif
490fe618babSjoerg if (SSL_connect(conn->ssl) == -1){
491fe618babSjoerg ERR_print_errors_fp(stderr);
492fe618babSjoerg return (-1);
493fe618babSjoerg }
494fe618babSjoerg
495fe618babSjoerg if (verbose) {
496fe618babSjoerg X509_NAME *name;
497fe618babSjoerg char *str;
498fe618babSjoerg
499fe618babSjoerg fprintf(stderr, "SSL connection established using %s\n",
500fe618babSjoerg SSL_get_cipher(conn->ssl));
501fe618babSjoerg conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
502fe618babSjoerg name = X509_get_subject_name(conn->ssl_cert);
503fe618babSjoerg str = X509_NAME_oneline(name, 0, 0);
504fe618babSjoerg printf("Certificate subject: %s\n", str);
505fe618babSjoerg free(str);
506fe618babSjoerg name = X509_get_issuer_name(conn->ssl_cert);
507fe618babSjoerg str = X509_NAME_oneline(name, 0, 0);
508fe618babSjoerg printf("Certificate issuer: %s\n", str);
509fe618babSjoerg free(str);
510fe618babSjoerg }
511fe618babSjoerg
512fe618babSjoerg return (0);
513fe618babSjoerg #else
514fe618babSjoerg (void)conn;
515fe618babSjoerg (void)verbose;
516fe618babSjoerg fprintf(stderr, "SSL support disabled\n");
517fe618babSjoerg return (-1);
518fe618babSjoerg #endif
519fe618babSjoerg }
520fe618babSjoerg
521*914f0ba5Schristos #ifdef HAVE_POLL
522*914f0ba5Schristos static int
compute_timeout(const struct timeval * tv)523*914f0ba5Schristos compute_timeout(const struct timeval *tv)
524*914f0ba5Schristos {
525*914f0ba5Schristos struct timeval cur;
526*914f0ba5Schristos
527*914f0ba5Schristos gettimeofday(&cur, NULL);
528*914f0ba5Schristos return (tv->tv_sec - cur.tv_sec) * 1000
529*914f0ba5Schristos + (tv->tv_usec - cur.tv_usec) / 1000;
530*914f0ba5Schristos }
531*914f0ba5Schristos #endif
532fe618babSjoerg
533fe618babSjoerg /*
534fe618babSjoerg * Read a character from a connection w/ timeout
535fe618babSjoerg */
536fe618babSjoerg ssize_t
fetch_read(conn_t * conn,char * buf,size_t len)537fe618babSjoerg fetch_read(conn_t *conn, char *buf, size_t len)
538fe618babSjoerg {
539*914f0ba5Schristos struct timeval timeout_end;
540*914f0ba5Schristos #ifdef HAVE_POLL
541*914f0ba5Schristos struct pollfd pfd;
542*914f0ba5Schristos #else
543fe618babSjoerg fd_set readfds;
544*914f0ba5Schristos #endif
545*914f0ba5Schristos int timeout_cur;
546fe618babSjoerg ssize_t rlen;
547fe618babSjoerg int r;
548fe618babSjoerg
549fe618babSjoerg if (len == 0)
550fe618babSjoerg return 0;
551fe618babSjoerg
552f298aa92Sjoerg if (conn->next_len != 0) {
553f298aa92Sjoerg if (conn->next_len < len)
554f298aa92Sjoerg len = conn->next_len;
555f298aa92Sjoerg memmove(buf, conn->next_buf, len);
556f298aa92Sjoerg conn->next_len -= len;
557f298aa92Sjoerg conn->next_buf += len;
558f298aa92Sjoerg return len;
559f298aa92Sjoerg }
560f298aa92Sjoerg
561fe618babSjoerg if (fetchTimeout) {
562*914f0ba5Schristos #ifndef HAVE_POLL
563fe618babSjoerg FD_ZERO(&readfds);
564*914f0ba5Schristos #endif
565*914f0ba5Schristos gettimeofday(&timeout_end, NULL);
566*914f0ba5Schristos timeout_end.tv_sec += fetchTimeout;
567fe618babSjoerg }
568fe618babSjoerg
569fe618babSjoerg for (;;) {
570*914f0ba5Schristos #ifdef HAVE_POLL
571*914f0ba5Schristos pfd.fd = conn->sd;
572*914f0ba5Schristos pfd.events = conn->buf_events;
573*914f0ba5Schristos if (fetchTimeout && pfd.events) {
574*914f0ba5Schristos do {
575*914f0ba5Schristos timeout_cur = compute_timeout(&timeout_end);
576*914f0ba5Schristos if (timeout_cur < 0) {
577*914f0ba5Schristos errno = ETIMEDOUT;
578*914f0ba5Schristos fetch_syserr();
579*914f0ba5Schristos return (-1);
580*914f0ba5Schristos }
581*914f0ba5Schristos errno = 0;
582*914f0ba5Schristos r = poll(&pfd, 1, timeout_cur);
583*914f0ba5Schristos if (r == -1) {
584*914f0ba5Schristos if (errno == EINTR && fetchRestartCalls)
585*914f0ba5Schristos continue;
586*914f0ba5Schristos fetch_syserr();
587*914f0ba5Schristos return (-1);
588*914f0ba5Schristos }
589*914f0ba5Schristos } while (pfd.revents == 0);
590*914f0ba5Schristos #else
591fe618babSjoerg while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
592*914f0ba5Schristos struct timeval waittv, now;
593fe618babSjoerg FD_SET(conn->sd, &readfds);
594fe618babSjoerg gettimeofday(&now, NULL);
595*914f0ba5Schristos waittv.tv_sec = timeout_end.tv_sec - now.tv_sec;
596*914f0ba5Schristos waittv.tv_usec = timeout_end.tv_usec - now.tv_usec;
597fe618babSjoerg if (waittv.tv_usec < 0) {
598fe618babSjoerg waittv.tv_usec += 1000000;
599fe618babSjoerg waittv.tv_sec--;
600fe618babSjoerg }
601fe618babSjoerg if (waittv.tv_sec < 0) {
602fe618babSjoerg errno = ETIMEDOUT;
603fe618babSjoerg fetch_syserr();
604fe618babSjoerg return (-1);
605fe618babSjoerg }
606fe618babSjoerg errno = 0;
607fe618babSjoerg r = select(conn->sd + 1, &readfds, NULL, NULL, &waittv);
608fe618babSjoerg if (r == -1) {
609fe618babSjoerg if (errno == EINTR && fetchRestartCalls)
610fe618babSjoerg continue;
611fe618babSjoerg fetch_syserr();
612fe618babSjoerg return (-1);
613fe618babSjoerg }
614*914f0ba5Schristos #endif
615fe618babSjoerg }
616fe618babSjoerg #ifdef WITH_SSL
617*914f0ba5Schristos if (conn->ssl != NULL) {
618*914f0ba5Schristos rlen = SSL_read(conn->ssl, buf, len);
619*914f0ba5Schristos if (rlen == -1) {
620*914f0ba5Schristos switch (SSL_get_error(conn->ssl, rlen)) {
621*914f0ba5Schristos case SSL_ERROR_WANT_READ:
622*914f0ba5Schristos conn->buf_events = POLLIN;
623*914f0ba5Schristos break;
624*914f0ba5Schristos case SSL_ERROR_WANT_WRITE:
625*914f0ba5Schristos conn->buf_events = POLLOUT;
626*914f0ba5Schristos break;
627*914f0ba5Schristos default:
628*914f0ba5Schristos errno = EIO;
629*914f0ba5Schristos fetch_syserr();
630*914f0ba5Schristos return -1;
631*914f0ba5Schristos }
632*914f0ba5Schristos } else {
633*914f0ba5Schristos /* Assume buffering on the SSL layer. */
634*914f0ba5Schristos conn->buf_events = 0;
635*914f0ba5Schristos }
636*914f0ba5Schristos } else
637fe618babSjoerg #endif
638fe618babSjoerg rlen = read(conn->sd, buf, len);
639fe618babSjoerg if (rlen >= 0)
640fe618babSjoerg break;
641fe618babSjoerg
642fe618babSjoerg if (errno != EINTR || !fetchRestartCalls)
643fe618babSjoerg return (-1);
644fe618babSjoerg }
645fe618babSjoerg return (rlen);
646fe618babSjoerg }
647fe618babSjoerg
648fe618babSjoerg
649fe618babSjoerg /*
650fe618babSjoerg * Read a line of text from a connection w/ timeout
651fe618babSjoerg */
652fe618babSjoerg #define MIN_BUF_SIZE 1024
653fe618babSjoerg
654fe618babSjoerg int
655fe618babSjoerg fetch_getln(conn_t *conn)
656fe618babSjoerg {
657f298aa92Sjoerg char *tmp, *next;
658fe618babSjoerg size_t tmpsize;
659fe618babSjoerg ssize_t len;
660fe618babSjoerg
661fe618babSjoerg if (conn->buf == NULL) {
662ebe76b0cSjoerg if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
663fe618babSjoerg errno = ENOMEM;
664fe618babSjoerg return (-1);
665fe618babSjoerg }
666fe618babSjoerg conn->bufsize = MIN_BUF_SIZE;
667fe618babSjoerg }
668fe618babSjoerg
669fe618babSjoerg conn->buflen = 0;
670f298aa92Sjoerg next = NULL;
671fe618babSjoerg
672fe618babSjoerg do {
673ebe76b0cSjoerg /*
674ebe76b0cSjoerg * conn->bufsize != conn->buflen at this point,
675ebe76b0cSjoerg * so the buffer can be NUL-terminated below for
676ebe76b0cSjoerg * the case of len == 0.
677ebe76b0cSjoerg */
678fe618babSjoerg len = fetch_read(conn, conn->buf + conn->buflen,
679fe618babSjoerg conn->bufsize - conn->buflen);
680fe618babSjoerg if (len == -1)
681fe618babSjoerg return (-1);
682fe618babSjoerg if (len == 0)
683fe618babSjoerg break;
684d2b73467Schristos next = memchr(conn->buf + conn->buflen, '\n', (size_t)len);
685fe618babSjoerg conn->buflen += len;
686ebe76b0cSjoerg if (conn->buflen == conn->bufsize && next == NULL) {
687fe618babSjoerg tmp = conn->buf;
688ebe76b0cSjoerg tmpsize = conn->bufsize * 2;
689ebe76b0cSjoerg if (tmpsize < conn->bufsize) {
690ebe76b0cSjoerg errno = ENOMEM;
691ebe76b0cSjoerg return (-1);
692ebe76b0cSjoerg }
693fe618babSjoerg if ((tmp = realloc(tmp, tmpsize)) == NULL) {
694fe618babSjoerg errno = ENOMEM;
695fe618babSjoerg return (-1);
696fe618babSjoerg }
697fe618babSjoerg conn->buf = tmp;
698fe618babSjoerg conn->bufsize = tmpsize;
699fe618babSjoerg }
700f298aa92Sjoerg } while (next == NULL);
701fe618babSjoerg
702f298aa92Sjoerg if (next != NULL) {
703ebe76b0cSjoerg *next = '\0';
704f298aa92Sjoerg conn->next_buf = next + 1;
705f298aa92Sjoerg conn->next_len = conn->buflen - (conn->next_buf - conn->buf);
706f298aa92Sjoerg conn->buflen = next - conn->buf;
707ebe76b0cSjoerg } else {
708fe618babSjoerg conn->buf[conn->buflen] = '\0';
709ebe76b0cSjoerg conn->next_len = 0;
710ebe76b0cSjoerg }
711fe618babSjoerg return (0);
712fe618babSjoerg }
713fe618babSjoerg
714fe618babSjoerg /*
715fe618babSjoerg * Write a vector to a connection w/ timeout
716fe618babSjoerg * Note: can modify the iovec.
717fe618babSjoerg */
718fe618babSjoerg ssize_t
71970160d70Sjoerg fetch_write(conn_t *conn, const void *buf, size_t len)
720fe618babSjoerg {
721fe618babSjoerg struct timeval now, timeout, waittv;
722fe618babSjoerg fd_set writefds;
723fe618babSjoerg ssize_t wlen, total;
724fe618babSjoerg int r;
72570160d70Sjoerg #ifndef MSG_NOSIGNAL
72670160d70Sjoerg static int killed_sigpipe;
72770160d70Sjoerg #endif
72870160d70Sjoerg
72970160d70Sjoerg #ifndef MSG_NOSIGNAL
73070160d70Sjoerg if (!killed_sigpipe) {
73170160d70Sjoerg signal(SIGPIPE, SIG_IGN);
73270160d70Sjoerg killed_sigpipe = 1;
73370160d70Sjoerg }
73470160d70Sjoerg #endif
73570160d70Sjoerg
736fe618babSjoerg
737fe618babSjoerg if (fetchTimeout) {
738fe618babSjoerg FD_ZERO(&writefds);
739fe618babSjoerg gettimeofday(&timeout, NULL);
740fe618babSjoerg timeout.tv_sec += fetchTimeout;
741fe618babSjoerg }
742fe618babSjoerg
743fe618babSjoerg total = 0;
74470160d70Sjoerg while (len) {
745fe618babSjoerg while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
746fe618babSjoerg FD_SET(conn->sd, &writefds);
747fe618babSjoerg gettimeofday(&now, NULL);
748fe618babSjoerg waittv.tv_sec = timeout.tv_sec - now.tv_sec;
749fe618babSjoerg waittv.tv_usec = timeout.tv_usec - now.tv_usec;
750fe618babSjoerg if (waittv.tv_usec < 0) {
751fe618babSjoerg waittv.tv_usec += 1000000;
752fe618babSjoerg waittv.tv_sec--;
753fe618babSjoerg }
754fe618babSjoerg if (waittv.tv_sec < 0) {
755fe618babSjoerg errno = ETIMEDOUT;
756fe618babSjoerg fetch_syserr();
757fe618babSjoerg return (-1);
758fe618babSjoerg }
759fe618babSjoerg errno = 0;
760fe618babSjoerg r = select(conn->sd + 1, NULL, &writefds, NULL, &waittv);
761fe618babSjoerg if (r == -1) {
762fe618babSjoerg if (errno == EINTR && fetchRestartCalls)
763fe618babSjoerg continue;
764fe618babSjoerg return (-1);
765fe618babSjoerg }
766fe618babSjoerg }
767fe618babSjoerg errno = 0;
768fe618babSjoerg #ifdef WITH_SSL
769fe618babSjoerg if (conn->ssl != NULL)
770d2b73467Schristos wlen = SSL_write(conn->ssl, buf, (int)len);
771fe618babSjoerg else
772fe618babSjoerg #endif
77370160d70Sjoerg #ifndef MSG_NOSIGNAL
77470160d70Sjoerg wlen = send(conn->sd, buf, len, 0);
77570160d70Sjoerg #else
77670160d70Sjoerg wlen = send(conn->sd, buf, len, MSG_NOSIGNAL);
77770160d70Sjoerg #endif
778fe618babSjoerg if (wlen == 0) {
779fe618babSjoerg /* we consider a short write a failure */
780fe618babSjoerg errno = EPIPE;
781fe618babSjoerg fetch_syserr();
782fe618babSjoerg return (-1);
783fe618babSjoerg }
784fe618babSjoerg if (wlen < 0) {
785fe618babSjoerg if (errno == EINTR && fetchRestartCalls)
786fe618babSjoerg continue;
787fe618babSjoerg return (-1);
788fe618babSjoerg }
789fe618babSjoerg total += wlen;
79070160d70Sjoerg buf = (const char *)buf + wlen;
79170160d70Sjoerg len -= wlen;
792fe618babSjoerg }
793fe618babSjoerg return (total);
794fe618babSjoerg }
795fe618babSjoerg
796fe618babSjoerg
797fe618babSjoerg /*
798fe618babSjoerg * Close connection
799fe618babSjoerg */
800fe618babSjoerg int
801fe618babSjoerg fetch_close(conn_t *conn)
802fe618babSjoerg {
803fe618babSjoerg int ret;
804fe618babSjoerg
8054e9a22b9Smlelstv #ifdef WITH_SSL
8064e9a22b9Smlelstv if (conn->ssl) {
8074e9a22b9Smlelstv SSL_shutdown(conn->ssl);
8084e9a22b9Smlelstv SSL_set_connect_state(conn->ssl);
8094e9a22b9Smlelstv SSL_free(conn->ssl);
8104e9a22b9Smlelstv conn->ssl = NULL;
8114e9a22b9Smlelstv }
8124e9a22b9Smlelstv if (conn->ssl_ctx) {
8134e9a22b9Smlelstv SSL_CTX_free(conn->ssl_ctx);
8144e9a22b9Smlelstv conn->ssl_ctx = NULL;
8154e9a22b9Smlelstv }
8164e9a22b9Smlelstv if (conn->ssl_cert) {
8174e9a22b9Smlelstv X509_free(conn->ssl_cert);
8184e9a22b9Smlelstv conn->ssl_cert = NULL;
8194e9a22b9Smlelstv }
8204e9a22b9Smlelstv #endif
821fe618babSjoerg ret = close(conn->sd);
82270160d70Sjoerg if (conn->cache_url)
82370160d70Sjoerg fetchFreeURL(conn->cache_url);
8240ab1cb6dSjoerg free(conn->ftp_home);
825fe618babSjoerg free(conn->buf);
826fe618babSjoerg free(conn);
827fe618babSjoerg return (ret);
828fe618babSjoerg }
829fe618babSjoerg
830fe618babSjoerg
831fe618babSjoerg /*** Directory-related utility functions *************************************/
832fe618babSjoerg
833fe618babSjoerg int
834fe618babSjoerg fetch_add_entry(struct url_list *ue, struct url *base, const char *name,
835fe618babSjoerg int pre_quoted)
836fe618babSjoerg {
837fe618babSjoerg struct url *tmp;
838fe618babSjoerg char *tmp_name;
839fe618babSjoerg size_t base_doc_len, name_len, i;
840fe618babSjoerg unsigned char c;
841fe618babSjoerg
842fe618babSjoerg if (strchr(name, '/') != NULL ||
843fe618babSjoerg strcmp(name, "..") == 0 ||
844fe618babSjoerg strcmp(name, ".") == 0)
845fe618babSjoerg return 0;
846fe618babSjoerg
847fe618babSjoerg if (strcmp(base->doc, "/") == 0)
848fe618babSjoerg base_doc_len = 0;
849fe618babSjoerg else
850fe618babSjoerg base_doc_len = strlen(base->doc);
851fe618babSjoerg
852fe618babSjoerg name_len = 1;
853fe618babSjoerg for (i = 0; name[i] != '\0'; ++i) {
854fe618babSjoerg if ((!pre_quoted && name[i] == '%') ||
855fe618babSjoerg !fetch_urlpath_safe(name[i]))
856fe618babSjoerg name_len += 3;
857fe618babSjoerg else
858fe618babSjoerg ++name_len;
859fe618babSjoerg }
860fe618babSjoerg
861fe618babSjoerg tmp_name = malloc( base_doc_len + name_len + 1);
862fe618babSjoerg if (tmp_name == NULL) {
863fe618babSjoerg errno = ENOMEM;
864fe618babSjoerg fetch_syserr();
865fe618babSjoerg return (-1);
866fe618babSjoerg }
867fe618babSjoerg
868fe618babSjoerg if (ue->length + 1 >= ue->alloc_size) {
869fe618babSjoerg tmp = realloc(ue->urls, (ue->alloc_size * 2 + 1) * sizeof(*tmp));
870fe618babSjoerg if (tmp == NULL) {
871fe618babSjoerg free(tmp_name);
872fe618babSjoerg errno = ENOMEM;
873fe618babSjoerg fetch_syserr();
874fe618babSjoerg return (-1);
875fe618babSjoerg }
876fe618babSjoerg ue->alloc_size = ue->alloc_size * 2 + 1;
877fe618babSjoerg ue->urls = tmp;
878fe618babSjoerg }
879fe618babSjoerg
880fe618babSjoerg tmp = ue->urls + ue->length;
881fe618babSjoerg strcpy(tmp->scheme, base->scheme);
882fe618babSjoerg strcpy(tmp->user, base->user);
883fe618babSjoerg strcpy(tmp->pwd, base->pwd);
884fe618babSjoerg strcpy(tmp->host, base->host);
885fe618babSjoerg tmp->port = base->port;
886fe618babSjoerg tmp->doc = tmp_name;
887fe618babSjoerg memcpy(tmp->doc, base->doc, base_doc_len);
888fe618babSjoerg tmp->doc[base_doc_len] = '/';
889fe618babSjoerg
890fe618babSjoerg for (i = base_doc_len + 1; *name != '\0'; ++name) {
891fe618babSjoerg if ((!pre_quoted && *name == '%') ||
892fe618babSjoerg !fetch_urlpath_safe(*name)) {
893fe618babSjoerg tmp->doc[i++] = '%';
894fe618babSjoerg c = (unsigned char)*name / 16;
895fe618babSjoerg if (c < 10)
896fe618babSjoerg tmp->doc[i++] = '0' + c;
897fe618babSjoerg else
898fe618babSjoerg tmp->doc[i++] = 'a' - 10 + c;
899fe618babSjoerg c = (unsigned char)*name % 16;
900fe618babSjoerg if (c < 10)
901fe618babSjoerg tmp->doc[i++] = '0' + c;
902fe618babSjoerg else
903fe618babSjoerg tmp->doc[i++] = 'a' - 10 + c;
904fe618babSjoerg } else {
905fe618babSjoerg tmp->doc[i++] = *name;
906fe618babSjoerg }
907fe618babSjoerg }
908fe618babSjoerg tmp->doc[i] = '\0';
909fe618babSjoerg
910fe618babSjoerg tmp->offset = 0;
911fe618babSjoerg tmp->length = 0;
9129da2cc5cSjoerg tmp->last_modified = -1;
913fe618babSjoerg
914fe618babSjoerg ++ue->length;
915fe618babSjoerg
916fe618babSjoerg return (0);
917fe618babSjoerg }
918fe618babSjoerg
919fe618babSjoerg void
920fe618babSjoerg fetchInitURLList(struct url_list *ue)
921fe618babSjoerg {
922fe618babSjoerg ue->length = ue->alloc_size = 0;
923fe618babSjoerg ue->urls = NULL;
924fe618babSjoerg }
925fe618babSjoerg
926eace2ff2Sjoerg int
927eace2ff2Sjoerg fetchAppendURLList(struct url_list *dst, const struct url_list *src)
928eace2ff2Sjoerg {
929eace2ff2Sjoerg size_t i, j, len;
930eace2ff2Sjoerg
931eace2ff2Sjoerg len = dst->length + src->length;
932eace2ff2Sjoerg if (len > dst->alloc_size) {
933eace2ff2Sjoerg struct url *tmp;
934eace2ff2Sjoerg
935eace2ff2Sjoerg tmp = realloc(dst->urls, len * sizeof(*tmp));
936eace2ff2Sjoerg if (tmp == NULL) {
937eace2ff2Sjoerg errno = ENOMEM;
938eace2ff2Sjoerg fetch_syserr();
939eace2ff2Sjoerg return (-1);
940eace2ff2Sjoerg }
941eace2ff2Sjoerg dst->alloc_size = len;
942eace2ff2Sjoerg dst->urls = tmp;
943eace2ff2Sjoerg }
944eace2ff2Sjoerg
945eace2ff2Sjoerg for (i = 0, j = dst->length; i < src->length; ++i, ++j) {
946eace2ff2Sjoerg dst->urls[j] = src->urls[i];
947eace2ff2Sjoerg dst->urls[j].doc = strdup(src->urls[i].doc);
948eace2ff2Sjoerg if (dst->urls[j].doc == NULL) {
949eace2ff2Sjoerg while (i-- > 0)
950eace2ff2Sjoerg free(dst->urls[j].doc);
951eace2ff2Sjoerg fetch_syserr();
952eace2ff2Sjoerg return -1;
953eace2ff2Sjoerg }
954eace2ff2Sjoerg }
955eace2ff2Sjoerg dst->length = len;
956eace2ff2Sjoerg
957eace2ff2Sjoerg return 0;
958eace2ff2Sjoerg }
959eace2ff2Sjoerg
960fe618babSjoerg void
961fe618babSjoerg fetchFreeURLList(struct url_list *ue)
962fe618babSjoerg {
963fe618babSjoerg size_t i;
964fe618babSjoerg
965fe618babSjoerg for (i = 0; i < ue->length; ++i)
966fe618babSjoerg free(ue->urls[i].doc);
967fe618babSjoerg free(ue->urls);
968fe618babSjoerg ue->length = ue->alloc_size = 0;
969fe618babSjoerg }
970fe618babSjoerg
971fe618babSjoerg
972fe618babSjoerg /*** Authentication-related utility functions ********************************/
973fe618babSjoerg
974fe618babSjoerg static const char *
975fe618babSjoerg fetch_read_word(FILE *f)
976fe618babSjoerg {
977fe618babSjoerg static char word[1024];
978fe618babSjoerg
979fe618babSjoerg if (fscanf(f, " %1023s ", word) != 1)
980fe618babSjoerg return (NULL);
981fe618babSjoerg return (word);
982fe618babSjoerg }
983fe618babSjoerg
984fe618babSjoerg /*
985fe618babSjoerg * Get authentication data for a URL from .netrc
986fe618babSjoerg */
987fe618babSjoerg int
988fe618babSjoerg fetch_netrc_auth(struct url *url)
989fe618babSjoerg {
990fe618babSjoerg char fn[PATH_MAX];
991fe618babSjoerg const char *word;
992fe618babSjoerg char *p;
993fe618babSjoerg FILE *f;
994fe618babSjoerg
995fe618babSjoerg if ((p = getenv("NETRC")) != NULL) {
996fe618babSjoerg if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
997fe618babSjoerg fetch_info("$NETRC specifies a file name "
998fe618babSjoerg "longer than PATH_MAX");
999fe618babSjoerg return (-1);
1000fe618babSjoerg }
1001fe618babSjoerg } else {
1002fe618babSjoerg if ((p = getenv("HOME")) != NULL) {
1003fe618babSjoerg struct passwd *pwd;
1004fe618babSjoerg
1005fe618babSjoerg if ((pwd = getpwuid(getuid())) == NULL ||
1006fe618babSjoerg (p = pwd->pw_dir) == NULL)
1007fe618babSjoerg return (-1);
1008fe618babSjoerg }
1009fe618babSjoerg if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
1010fe618babSjoerg return (-1);
1011fe618babSjoerg }
1012fe618babSjoerg
1013fe618babSjoerg if ((f = fopen(fn, "r")) == NULL)
1014fe618babSjoerg return (-1);
1015fe618babSjoerg while ((word = fetch_read_word(f)) != NULL) {
1016fe618babSjoerg if (strcmp(word, "default") == 0)
1017fe618babSjoerg break;
1018fe618babSjoerg if (strcmp(word, "machine") == 0 &&
1019fe618babSjoerg (word = fetch_read_word(f)) != NULL &&
1020fe618babSjoerg strcasecmp(word, url->host) == 0) {
1021fe618babSjoerg break;
1022fe618babSjoerg }
1023fe618babSjoerg }
1024fe618babSjoerg if (word == NULL)
1025fe618babSjoerg goto ferr;
1026fe618babSjoerg while ((word = fetch_read_word(f)) != NULL) {
1027fe618babSjoerg if (strcmp(word, "login") == 0) {
1028fe618babSjoerg if ((word = fetch_read_word(f)) == NULL)
1029fe618babSjoerg goto ferr;
1030fe618babSjoerg if (snprintf(url->user, sizeof(url->user),
1031fe618babSjoerg "%s", word) > (int)sizeof(url->user)) {
1032fe618babSjoerg fetch_info("login name in .netrc is too long");
1033fe618babSjoerg url->user[0] = '\0';
1034fe618babSjoerg }
1035fe618babSjoerg } else if (strcmp(word, "password") == 0) {
1036fe618babSjoerg if ((word = fetch_read_word(f)) == NULL)
1037fe618babSjoerg goto ferr;
1038fe618babSjoerg if (snprintf(url->pwd, sizeof(url->pwd),
1039fe618babSjoerg "%s", word) > (int)sizeof(url->pwd)) {
1040fe618babSjoerg fetch_info("password in .netrc is too long");
1041fe618babSjoerg url->pwd[0] = '\0';
1042fe618babSjoerg }
1043fe618babSjoerg } else if (strcmp(word, "account") == 0) {
1044fe618babSjoerg if ((word = fetch_read_word(f)) == NULL)
1045fe618babSjoerg goto ferr;
1046fe618babSjoerg /* XXX not supported! */
1047fe618babSjoerg } else {
1048fe618babSjoerg break;
1049fe618babSjoerg }
1050fe618babSjoerg }
1051fe618babSjoerg fclose(f);
1052fe618babSjoerg return (0);
1053fe618babSjoerg ferr:
1054fe618babSjoerg fclose(f);
1055fe618babSjoerg return (-1);
1056fe618babSjoerg }
1057fe618babSjoerg
1058fe618babSjoerg /*
1059fe618babSjoerg * The no_proxy environment variable specifies a set of domains for
1060fe618babSjoerg * which the proxy should not be consulted; the contents is a comma-,
1061fe618babSjoerg * or space-separated list of domain names. A single asterisk will
1062fe618babSjoerg * override all proxy variables and no transactions will be proxied
1063fe618babSjoerg * (for compatability with lynx and curl, see the discussion at
1064fe618babSjoerg * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>).
1065fe618babSjoerg */
1066fe618babSjoerg int
1067fe618babSjoerg fetch_no_proxy_match(const char *host)
1068fe618babSjoerg {
1069fe618babSjoerg const char *no_proxy, *p, *q;
1070fe618babSjoerg size_t h_len, d_len;
1071fe618babSjoerg
1072fe618babSjoerg if ((no_proxy = getenv("NO_PROXY")) == NULL &&
1073fe618babSjoerg (no_proxy = getenv("no_proxy")) == NULL)
1074fe618babSjoerg return (0);
1075fe618babSjoerg
1076fe618babSjoerg /* asterisk matches any hostname */
1077fe618babSjoerg if (strcmp(no_proxy, "*") == 0)
1078fe618babSjoerg return (1);
1079fe618babSjoerg
1080fe618babSjoerg h_len = strlen(host);
1081fe618babSjoerg p = no_proxy;
1082fe618babSjoerg do {
1083fe618babSjoerg /* position p at the beginning of a domain suffix */
1084fe618babSjoerg while (*p == ',' || isspace((unsigned char)*p))
1085fe618babSjoerg p++;
1086fe618babSjoerg
1087fe618babSjoerg /* position q at the first separator character */
1088fe618babSjoerg for (q = p; *q; ++q)
1089fe618babSjoerg if (*q == ',' || isspace((unsigned char)*q))
1090fe618babSjoerg break;
1091fe618babSjoerg
1092fe618babSjoerg d_len = q - p;
1093fe618babSjoerg if (d_len > 0 && h_len > d_len &&
1094fe618babSjoerg strncasecmp(host + h_len - d_len,
1095fe618babSjoerg p, d_len) == 0) {
1096fe618babSjoerg /* domain name matches */
1097fe618babSjoerg return (1);
1098fe618babSjoerg }
1099fe618babSjoerg
1100fe618babSjoerg p = q + 1;
1101fe618babSjoerg } while (*q);
1102fe618babSjoerg
1103fe618babSjoerg return (0);
1104fe618babSjoerg }
1105fe618babSjoerg
1106fe618babSjoerg struct fetchIO {
1107fe618babSjoerg void *io_cookie;
1108fe618babSjoerg ssize_t (*io_read)(void *, void *, size_t);
1109fe618babSjoerg ssize_t (*io_write)(void *, const void *, size_t);
1110fe618babSjoerg void (*io_close)(void *);
1111fe618babSjoerg };
1112fe618babSjoerg
1113fe618babSjoerg void
1114fe618babSjoerg fetchIO_close(fetchIO *f)
1115fe618babSjoerg {
1116fe618babSjoerg if (f->io_close != NULL)
1117fe618babSjoerg (*f->io_close)(f->io_cookie);
1118fe618babSjoerg
1119fe618babSjoerg free(f);
1120fe618babSjoerg }
1121fe618babSjoerg
1122fe618babSjoerg fetchIO *
1123fe618babSjoerg fetchIO_unopen(void *io_cookie, ssize_t (*io_read)(void *, void *, size_t),
1124fe618babSjoerg ssize_t (*io_write)(void *, const void *, size_t),
1125fe618babSjoerg void (*io_close)(void *))
1126fe618babSjoerg {
1127fe618babSjoerg fetchIO *f;
1128fe618babSjoerg
1129fe618babSjoerg f = malloc(sizeof(*f));
1130fe618babSjoerg if (f == NULL)
1131fe618babSjoerg return f;
1132fe618babSjoerg
1133fe618babSjoerg f->io_cookie = io_cookie;
1134fe618babSjoerg f->io_read = io_read;
1135fe618babSjoerg f->io_write = io_write;
1136fe618babSjoerg f->io_close = io_close;
1137fe618babSjoerg
1138fe618babSjoerg return f;
1139fe618babSjoerg }
1140fe618babSjoerg
1141fe618babSjoerg ssize_t
1142fe618babSjoerg fetchIO_read(fetchIO *f, void *buf, size_t len)
1143fe618babSjoerg {
1144fe618babSjoerg if (f->io_read == NULL)
1145fe618babSjoerg return EBADF;
1146fe618babSjoerg return (*f->io_read)(f->io_cookie, buf, len);
1147fe618babSjoerg }
1148fe618babSjoerg
1149fe618babSjoerg ssize_t
1150fe618babSjoerg fetchIO_write(fetchIO *f, const void *buf, size_t len)
1151fe618babSjoerg {
1152fe618babSjoerg if (f->io_read == NULL)
1153fe618babSjoerg return EBADF;
1154fe618babSjoerg return (*f->io_write)(f->io_cookie, buf, len);
1155fe618babSjoerg }
1156