1 /* $NetBSD: common.c,v 1.27 2010/06/13 21:38:09 joerg Exp $ */
3 * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
4 * Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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 $
36 #if !defined(NETBSD) && !defined(__minix)
40 #include <sys/types.h>
41 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
50 #if defined(HAVE_INTTYPES_H) || defined(NETBSD)
53 #if !defined(NETBSD) && !defined(__minix)
54 #include <nbcompat/netdb.h>
71 /*** Local data **************************************************************/
74 * Error messages for resolver errors
76 static struct fetcherr netdb_errlist
[] = {
78 { EAI_NODATA
, FETCH_RESOLV
, "Host not found" },
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
)
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
118 fetchLastErrCode
= FETCH_OK
;
129 fetchLastErrCode
= FETCH_AUTH
;
132 case EISDIR
: /* XXX */
133 fetchLastErrCode
= FETCH_UNAVAIL
;
136 fetchLastErrCode
= FETCH_MEMORY
;
140 fetchLastErrCode
= FETCH_TEMP
;
143 fetchLastErrCode
= FETCH_EXISTS
;
146 fetchLastErrCode
= FETCH_FULL
;
152 #if defined(ENETRESET)
156 fetchLastErrCode
= FETCH_NETWORK
;
158 #if defined(ECONNABORTED)
162 fetchLastErrCode
= FETCH_ABORT
;
165 fetchLastErrCode
= FETCH_TIMEOUT
;
168 #if defined(EHOSTDOWN)
171 fetchLastErrCode
= FETCH_DOWN
;
174 fetchLastErrCode
= FETCH_UNKNOWN
;
176 snprintf(fetchLastErrString
, MAXERRSTRING
, "%s", strerror(errno
));
181 * Emit status message
184 fetch_info(const char *fmt
, ...)
189 vfprintf(stderr
, fmt
, ap
);
195 /*** Network-related utility functions ***************************************/
198 * Return the default port for a scheme
201 fetch_default_port(const char *scheme
)
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
);
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
);
229 * Create a connection for an existing descriptor.
236 /* allocate and fill connection structure */
237 if ((conn
= calloc(1, sizeof(*conn
))) == NULL
)
239 conn
->ftp_home
= NULL
;
240 conn
->cache_url
= NULL
;
241 conn
->next_buf
= NULL
;
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
))
262 for (res
= res0
; res
; res
= res
->ai_next
) {
263 if (bind(sd
, res
->ai_addr
, res
->ai_addrlen
) == 0)
271 * Establish a TCP connection to the specified port on the specified host.
274 fetch_connect(struct url
*url
, int af
, int verbose
)
278 const char *bindaddr
;
279 struct addrinfo hints
, *res
, *res0
;
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) {
295 bindaddr
= getenv("FETCH_BIND_ADDRESS");
298 fetch_info("connecting to %s:%d", url
->host
, url
->port
);
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)
305 if (bindaddr
!= NULL
&& *bindaddr
!= '\0' &&
306 fetch_bind(sd
, res
->ai_family
, bindaddr
) != 0) {
307 fetch_info("failed to bind to '%s'", bindaddr
);
311 if (connect(sd
, res
->ai_addr
, res
->ai_addrlen
) == 0)
321 if ((conn
= fetch_reopen(sd
)) == NULL
) {
326 conn
->cache_url
= fetchCopyURL(url
);
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.
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
;
347 cache_global_limit
= global_limit
;
348 if (per_host_limit
< 0)
349 cache_per_host_limit
= INT_MAX
;
351 cache_per_host_limit
= per_host_limit
;
355 * Flush cache and free all associated resources.
358 fetchConnectionCacheClose(void)
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.
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
;
388 connection_cache
= conn
->next_cached
;
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.
402 fetch_cache_put(conn_t
*conn
, int (*closecb
)(conn_t
*))
405 int global_count
, host_count
;
407 if (conn
->cache_url
== NULL
|| cache_global_limit
== 0) {
412 global_count
= host_count
= 0;
414 for (iter
= connection_cache
; iter
;
415 last
= iter
, iter
= iter
->next_cached
) {
417 if (strcmp(conn
->cache_url
->host
, iter
->cache_url
->host
) == 0)
419 if (global_count
< cache_global_limit
&&
420 host_count
< cache_per_host_limit
)
424 last
->next_cached
= iter
->next_cached
;
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
)
443 /* Init the SSL library and context */
444 if (!SSL_library_init()){
445 fprintf(stderr
, "SSL library init failed\n");
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");
460 SSL_set_fd(conn
->ssl
, conn
->sd
);
461 if (SSL_connect(conn
->ssl
) == -1){
462 ERR_print_errors_fp(stderr
);
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
);
477 name
= X509_get_issuer_name(conn
->ssl_cert
);
478 str
= X509_NAME_oneline(name
, 0, 0);
479 printf("Certificate issuer: %s\n", str
);
487 fprintf(stderr
, "SSL support disabled\n");
494 * Read a character from a connection w/ timeout
497 fetch_read(conn_t
*conn
, char *buf
, size_t len
)
499 struct timeval now
, timeout
, waittv
;
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
;
518 gettimeofday(&timeout
, NULL
);
519 timeout
.tv_sec
+= fetchTimeout
;
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;
532 if (waittv
.tv_sec
< 0) {
538 r
= select(conn
->sd
+ 1, &readfds
, NULL
, NULL
, &waittv
);
540 if (errno
== EINTR
&& fetchRestartCalls
)
547 if (conn
->ssl
!= NULL
)
548 rlen
= SSL_read(conn
->ssl
, buf
, len
);
551 rlen
= read(conn
->sd
, buf
, len
);
555 if (errno
!= EINTR
|| !fetchRestartCalls
)
563 * Read a line of text from a connection w/ timeout
565 #define MIN_BUF_SIZE 1024
568 fetch_getln(conn_t
*conn
)
574 if (conn
->buf
== NULL
) {
575 if ((conn
->buf
= malloc(MIN_BUF_SIZE
)) == NULL
) {
579 conn
->bufsize
= MIN_BUF_SIZE
;
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
);
597 next
= memchr(conn
->buf
+ conn
->buflen
, '\n', len
);
599 if (conn
->buflen
== conn
->bufsize
&& next
== NULL
) {
601 tmpsize
= conn
->bufsize
* 2;
602 if (tmpsize
< conn
->bufsize
) {
606 if ((tmp
= realloc(tmp
, tmpsize
)) == NULL
) {
611 conn
->bufsize
= tmpsize
;
613 } while (next
== NULL
);
617 conn
->next_buf
= next
+ 1;
618 conn
->next_len
= conn
->buflen
- (conn
->next_buf
- conn
->buf
);
619 conn
->buflen
= next
- conn
->buf
;
621 conn
->buf
[conn
->buflen
] = '\0';
628 * Write a vector to a connection w/ timeout
629 * Note: can modify the iovec.
632 fetch_write(conn_t
*conn
, const void *buf
, size_t len
)
634 struct timeval now
, timeout
, waittv
;
639 static int killed_sigpipe
;
643 if (!killed_sigpipe
) {
644 signal(SIGPIPE
, SIG_IGN
);
652 gettimeofday(&timeout
, NULL
);
653 timeout
.tv_sec
+= fetchTimeout
;
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;
667 if (waittv
.tv_sec
< 0) {
673 r
= select(conn
->sd
+ 1, NULL
, &writefds
, NULL
, &waittv
);
675 if (errno
== EINTR
&& fetchRestartCalls
)
682 if (conn
->ssl
!= NULL
)
683 wlen
= SSL_write(conn
->ssl
, buf
, len
);
687 wlen
= send(conn
->sd
, buf
, len
, 0);
689 wlen
= send(conn
->sd
, buf
, len
, MSG_NOSIGNAL
);
692 /* we consider a short write a failure */
698 if (errno
== EINTR
&& fetchRestartCalls
)
703 buf
= (const char *)buf
+ wlen
;
714 fetch_close(conn_t
*conn
)
718 ret
= close(conn
->sd
);
720 fetchFreeURL(conn
->cache_url
);
721 free(conn
->ftp_home
);
728 /*** Directory-related utility functions *************************************/
731 fetch_add_entry(struct url_list
*ue
, struct url
*base
, const char *name
,
736 size_t base_doc_len
, name_len
, i
;
739 if (strchr(name
, '/') != NULL
||
740 strcmp(name
, "..") == 0 ||
741 strcmp(name
, ".") == 0)
744 if (strcmp(base
->doc
, "/") == 0)
747 base_doc_len
= strlen(base
->doc
);
750 for (i
= 0; name
[i
] != '\0'; ++i
) {
751 if ((!pre_quoted
&& name
[i
] == '%') ||
752 !fetch_urlpath_safe(name
[i
]))
758 tmp_name
= malloc( base_doc_len
+ name_len
+ 1);
759 if (tmp_name
== NULL
) {
765 if (ue
->length
+ 1 >= ue
->alloc_size
) {
766 tmp
= realloc(ue
->urls
, (ue
->alloc_size
* 2 + 1) * sizeof(*tmp
));
773 ue
->alloc_size
= ue
->alloc_size
* 2 + 1;
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
;
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
)) {
791 c
= (unsigned char)*name
/ 16;
793 tmp
->doc
[i
++] = '0' + c
;
795 tmp
->doc
[i
++] = 'a' - 10 + c
;
796 c
= (unsigned char)*name
% 16;
798 tmp
->doc
[i
++] = '0' + c
;
800 tmp
->doc
[i
++] = 'a' - 10 + c
;
802 tmp
->doc
[i
++] = *name
;
809 tmp
->last_modified
= -1;
817 fetchInitURLList(struct url_list
*ue
)
819 ue
->length
= ue
->alloc_size
= 0;
824 fetchAppendURLList(struct url_list
*dst
, const struct url_list
*src
)
828 len
= dst
->length
+ src
->length
;
829 if (len
> dst
->alloc_size
) {
832 tmp
= realloc(dst
->urls
, len
* sizeof(*tmp
));
838 dst
->alloc_size
= len
;
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
) {
847 free(dst
->urls
[j
].doc
);
858 fetchFreeURLList(struct url_list
*ue
)
862 for (i
= 0; i
< ue
->length
; ++i
)
863 free(ue
->urls
[i
].doc
);
865 ue
->length
= ue
->alloc_size
= 0;
869 /*** Authentication-related utility functions ********************************/
872 fetch_read_word(FILE *f
)
874 static char word
[1024];
876 if (fscanf(f
, " %1023s ", word
) != 1)
882 * Get authentication data for a URL from .netrc
885 fetch_netrc_auth(struct url
*url
)
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");
899 if ((p
= getenv("HOME")) != NULL
) {
902 if ((pwd
= getpwuid(getuid())) == NULL
||
903 (p
= pwd
->pw_dir
) == NULL
)
906 if (snprintf(fn
, sizeof(fn
), "%s/.netrc", p
) >= (int)sizeof(fn
))
910 if ((f
= fopen(fn
, "r")) == NULL
)
912 while ((word
= fetch_read_word(f
)) != NULL
) {
913 if (strcmp(word
, "default") == 0)
915 if (strcmp(word
, "machine") == 0 &&
916 (word
= fetch_read_word(f
)) != NULL
&&
917 strcasecmp(word
, url
->host
) == 0) {
923 while ((word
= fetch_read_word(f
)) != NULL
) {
924 if (strcmp(word
, "login") == 0) {
925 if ((word
= fetch_read_word(f
)) == NULL
)
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");
932 } else if (strcmp(word
, "password") == 0) {
933 if ((word
= fetch_read_word(f
)) == NULL
)
935 if (snprintf(url
->pwd
, sizeof(url
->pwd
),
936 "%s", word
) > (int)sizeof(url
->pwd
)) {
937 fetch_info("password in .netrc is too long");
940 } else if (strcmp(word
, "account") == 0) {
941 if ((word
= fetch_read_word(f
)) == NULL
)
943 /* XXX not supported! */
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
;
969 if ((no_proxy
= getenv("NO_PROXY")) == NULL
&&
970 (no_proxy
= getenv("no_proxy")) == NULL
)
973 /* asterisk matches any hostname */
974 if (strcmp(no_proxy
, "*") == 0)
977 h_len
= strlen(host
);
980 /* position p at the beginning of a domain suffix */
981 while (*p
== ',' || isspace((unsigned char)*p
))
984 /* position q at the first separator character */
986 if (*q
== ',' || isspace((unsigned char)*q
))
990 if (d_len
> 0 && h_len
> d_len
&&
991 strncasecmp(host
+ h_len
- d_len
,
993 /* domain name matches */
1005 ssize_t (*io_read
)(void *, void *, size_t);
1006 ssize_t (*io_write
)(void *, const void *, size_t);
1007 void (*io_close
)(void *);
1011 fetchIO_close(fetchIO
*f
)
1013 if (f
->io_close
!= NULL
)
1014 (*f
->io_close
)(f
->io_cookie
);
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 *))
1026 f
= malloc(sizeof(*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
;
1039 fetchIO_read(fetchIO
*f
, void *buf
, size_t len
)
1041 if (f
->io_read
== NULL
)
1043 return (*f
->io_read
)(f
->io_cookie
, buf
, len
);
1047 fetchIO_write(fetchIO
*f
, const void *buf
, size_t len
)
1049 if (f
->io_read
== NULL
)
1051 return (*f
->io_write
)(f
->io_cookie
, buf
, len
);