2 * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer
10 * in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 * $FreeBSD: src/lib/libfetch/common.c,v 1.50 2005/02/16 12:46:46 des Exp $
29 * $DragonFly: src/lib/libfetch/common.c,v 1.5 2008/04/02 14:46:37 joerg Exp $
32 #include <sys/param.h>
33 #include <sys/socket.h>
36 #include <netinet/in.h>
51 /*** Local data **************************************************************/
54 * Error messages for resolver errors
56 static struct fetcherr _netdb_errlist
[] = {
58 { EAI_NODATA
, FETCH_RESOLV
, "Host not found" },
60 { EAI_AGAIN
, FETCH_TEMP
, "Transient resolver failure" },
61 { EAI_FAIL
, FETCH_RESOLV
, "Non-recoverable resolver failure" },
62 { EAI_NONAME
, FETCH_RESOLV
, "No address record" },
63 { -1, FETCH_UNKNOWN
, "Unknown resolver error" }
67 static const char ENDL
[2] = "\r\n";
70 /*** Error-reporting functions ***********************************************/
73 * Map error code to string
75 static struct fetcherr
*
76 _fetch_finderr(struct fetcherr
*p
, int e
)
78 while (p
->num
!= -1 && p
->num
!= e
)
87 _fetch_seterr(struct fetcherr
*p
, int e
)
89 p
= _fetch_finderr(p
, e
);
90 fetchLastErrCode
= p
->cat
;
91 snprintf(fetchLastErrString
, MAXERRSTRING
, "%s", p
->string
);
95 * Set error code according to errno
102 fetchLastErrCode
= FETCH_OK
;
109 fetchLastErrCode
= FETCH_AUTH
;
112 case EISDIR
: /* XXX */
113 fetchLastErrCode
= FETCH_UNAVAIL
;
116 fetchLastErrCode
= FETCH_MEMORY
;
120 fetchLastErrCode
= FETCH_TEMP
;
123 fetchLastErrCode
= FETCH_EXISTS
;
126 fetchLastErrCode
= FETCH_FULL
;
134 fetchLastErrCode
= FETCH_NETWORK
;
138 fetchLastErrCode
= FETCH_ABORT
;
141 fetchLastErrCode
= FETCH_TIMEOUT
;
145 fetchLastErrCode
= FETCH_DOWN
;
148 fetchLastErrCode
= FETCH_UNKNOWN
;
150 snprintf(fetchLastErrString
, MAXERRSTRING
, "%s", strerror(errno
));
155 * Emit status message
158 _fetch_info(const char *fmt
, ...)
163 vfprintf(stderr
, fmt
, ap
);
169 /*** Network-related utility functions ***************************************/
172 * Return the default port for a scheme
175 _fetch_default_port(const char *scheme
)
179 if ((se
= getservbyname(scheme
, "tcp")) != NULL
)
180 return (ntohs(se
->s_port
));
181 if (strcasecmp(scheme
, SCHEME_FTP
) == 0)
182 return (FTP_DEFAULT_PORT
);
183 if (strcasecmp(scheme
, SCHEME_HTTP
) == 0)
184 return (HTTP_DEFAULT_PORT
);
189 * Return the default proxy port for a scheme
192 _fetch_default_proxy_port(const char *scheme
)
194 if (strcasecmp(scheme
, SCHEME_FTP
) == 0)
195 return (FTP_DEFAULT_PROXY_PORT
);
196 if (strcasecmp(scheme
, SCHEME_HTTP
) == 0)
197 return (HTTP_DEFAULT_PROXY_PORT
);
203 * Create a connection for an existing descriptor.
206 _fetch_reopen(int sd
)
210 /* allocate and fill connection structure */
211 if ((conn
= calloc(1, sizeof(*conn
))) == NULL
)
220 * Bump a connection's reference count.
223 _fetch_ref(conn_t
*conn
)
232 * Bind a socket to a specific local address
235 _fetch_bind(int sd
, int af
, const char *addr
)
237 struct addrinfo hints
, *res
, *res0
;
240 memset(&hints
, 0, sizeof(hints
));
241 hints
.ai_family
= af
;
242 hints
.ai_socktype
= SOCK_STREAM
;
243 hints
.ai_protocol
= 0;
244 if ((err
= getaddrinfo(addr
, NULL
, &hints
, &res0
)) != 0)
246 for (res
= res0
; res
; res
= res
->ai_next
)
247 if (bind(sd
, res
->ai_addr
, res
->ai_addrlen
) == 0)
254 * Establish a TCP connection to the specified port on the specified host.
257 _fetch_connect(const char *host
, int port
, int af
, int verbose
)
261 const char *bindaddr
;
262 struct addrinfo hints
, *res
, *res0
;
265 DEBUG(fprintf(stderr
, "---> %s:%d\n", host
, port
));
268 _fetch_info("looking up %s", host
);
270 /* look up host name and set up socket address structure */
271 snprintf(pbuf
, sizeof(pbuf
), "%d", port
);
272 memset(&hints
, 0, sizeof(hints
));
273 hints
.ai_family
= af
;
274 hints
.ai_socktype
= SOCK_STREAM
;
275 hints
.ai_protocol
= 0;
276 if ((err
= getaddrinfo(host
, pbuf
, &hints
, &res0
)) != 0) {
280 bindaddr
= getenv("FETCH_BIND_ADDRESS");
283 _fetch_info("connecting to %s:%d", host
, port
);
286 for (sd
= -1, res
= res0
; res
; sd
= -1, res
= res
->ai_next
) {
287 if ((sd
= socket(res
->ai_family
, res
->ai_socktype
,
288 res
->ai_protocol
)) == -1)
290 if (bindaddr
!= NULL
&& *bindaddr
!= '\0' &&
291 _fetch_bind(sd
, res
->ai_family
, bindaddr
) != 0) {
292 _fetch_info("failed to bind to '%s'", bindaddr
);
296 if (connect(sd
, res
->ai_addr
, res
->ai_addrlen
) == 0)
306 if ((conn
= _fetch_reopen(sd
)) == NULL
) {
315 * Enable SSL on a connection.
318 _fetch_ssl(conn_t
*conn
, int verbose
)
322 /* Init the SSL library and context */
323 if (!SSL_library_init()){
324 fprintf(stderr
, "SSL library init failed\n");
328 SSL_load_error_strings();
330 conn
->ssl_meth
= SSLv23_client_method();
331 conn
->ssl_ctx
= SSL_CTX_new(conn
->ssl_meth
);
332 SSL_CTX_set_mode(conn
->ssl_ctx
, SSL_MODE_AUTO_RETRY
);
334 conn
->ssl
= SSL_new(conn
->ssl_ctx
);
335 if (conn
->ssl
== NULL
){
336 fprintf(stderr
, "SSL context creation failed\n");
339 SSL_set_fd(conn
->ssl
, conn
->sd
);
340 if (SSL_connect(conn
->ssl
) == -1){
341 ERR_print_errors_fp(stderr
);
349 fprintf(stderr
, "SSL connection established using %s\n",
350 SSL_get_cipher(conn
->ssl
));
351 conn
->ssl_cert
= SSL_get_peer_certificate(conn
->ssl
);
352 name
= X509_get_subject_name(conn
->ssl_cert
);
353 str
= X509_NAME_oneline(name
, 0, 0);
354 printf("Certificate subject: %s\n", str
);
356 name
= X509_get_issuer_name(conn
->ssl_cert
);
357 str
= X509_NAME_oneline(name
, 0, 0);
358 printf("Certificate issuer: %s\n", str
);
366 fprintf(stderr
, "SSL support disabled\n");
373 * Read a character from a connection w/ timeout
376 _fetch_read(conn_t
*conn
, char *buf
, size_t len
)
378 struct timeval now
, timeout
, wait
;
385 gettimeofday(&timeout
, NULL
);
386 timeout
.tv_sec
+= fetchTimeout
;
391 while (fetchTimeout
&& !FD_ISSET(conn
->sd
, &readfds
)) {
392 FD_SET(conn
->sd
, &readfds
);
393 gettimeofday(&now
, NULL
);
394 wait
.tv_sec
= timeout
.tv_sec
- now
.tv_sec
;
395 wait
.tv_usec
= timeout
.tv_usec
- now
.tv_usec
;
396 if (wait
.tv_usec
< 0) {
397 wait
.tv_usec
+= 1000000;
400 if (wait
.tv_sec
< 0) {
406 r
= select(conn
->sd
+ 1, &readfds
, NULL
, NULL
, &wait
);
408 if (errno
== EINTR
&& fetchRestartCalls
)
415 if (conn
->ssl
!= NULL
)
416 rlen
= SSL_read(conn
->ssl
, buf
, len
);
419 rlen
= read(conn
->sd
, buf
, len
);
423 if (errno
== EINTR
&& fetchRestartCalls
)
436 * Read a line of text from a connection w/ timeout
438 #define MIN_BUF_SIZE 1024
441 _fetch_getln(conn_t
*conn
)
448 if (conn
->buf
== NULL
) {
449 if ((conn
->buf
= malloc(MIN_BUF_SIZE
)) == NULL
) {
453 conn
->bufsize
= MIN_BUF_SIZE
;
460 len
= _fetch_read(conn
, &c
, 1);
465 conn
->buf
[conn
->buflen
++] = c
;
466 if (conn
->buflen
== conn
->bufsize
) {
468 tmpsize
= conn
->bufsize
* 2 + 1;
469 if ((tmp
= realloc(tmp
, tmpsize
)) == NULL
) {
474 conn
->bufsize
= tmpsize
;
478 conn
->buf
[conn
->buflen
] = '\0';
479 DEBUG(fprintf(stderr
, "<<< %s", conn
->buf
));
485 * Write to a connection w/ timeout
488 _fetch_write(conn_t
*conn
, const char *buf
, size_t len
)
492 iov
.iov_base
= __DECONST(char *, buf
);
494 return _fetch_writev(conn
, &iov
, 1);
498 * Write a vector to a connection w/ timeout
499 * Note: can modify the iovec.
502 _fetch_writev(conn_t
*conn
, struct iovec
*iov
, int iovcnt
)
504 struct timeval now
, timeout
, wait
;
511 gettimeofday(&timeout
, NULL
);
512 timeout
.tv_sec
+= fetchTimeout
;
517 while (fetchTimeout
&& !FD_ISSET(conn
->sd
, &writefds
)) {
518 FD_SET(conn
->sd
, &writefds
);
519 gettimeofday(&now
, NULL
);
520 wait
.tv_sec
= timeout
.tv_sec
- now
.tv_sec
;
521 wait
.tv_usec
= timeout
.tv_usec
- now
.tv_usec
;
522 if (wait
.tv_usec
< 0) {
523 wait
.tv_usec
+= 1000000;
526 if (wait
.tv_sec
< 0) {
532 r
= select(conn
->sd
+ 1, NULL
, &writefds
, NULL
, &wait
);
534 if (errno
== EINTR
&& fetchRestartCalls
)
541 if (conn
->ssl
!= NULL
)
542 wlen
= SSL_write(conn
->ssl
,
543 iov
->iov_base
, iov
->iov_len
);
546 wlen
= writev(conn
->sd
, iov
, iovcnt
);
548 /* we consider a short write a failure */
554 if (errno
== EINTR
&& fetchRestartCalls
)
559 while (iovcnt
> 0 && wlen
>= (ssize_t
)iov
->iov_len
) {
560 wlen
-= iov
->iov_len
;
565 iov
->iov_len
-= wlen
;
566 iov
->iov_base
= __DECONST(char *, iov
->iov_base
) + wlen
;
574 * Write a line of text to a connection w/ timeout
577 _fetch_putln(conn_t
*conn
, const char *str
, size_t len
)
582 DEBUG(fprintf(stderr
, ">>> %s\n", str
));
583 iov
[0].iov_base
= __DECONST(char *, str
);
584 iov
[0].iov_len
= len
;
585 iov
[1].iov_base
= __DECONST(char *, ENDL
);
586 iov
[1].iov_len
= sizeof(ENDL
);
588 ret
= _fetch_writev(conn
, &iov
[1], 1);
590 ret
= _fetch_writev(conn
, iov
, 2);
601 _fetch_close(conn_t
*conn
)
607 ret
= close(conn
->sd
);
614 /*** Directory-related utility functions *************************************/
617 _fetch_add_entry(struct url_ent
**p
, int *size
, int *len
,
618 const char *name
, struct url_stat
*us
)
627 if (*len
>= *size
- 1) {
628 tmp
= realloc(*p
, (*size
* 2 + 1) * sizeof(**p
));
634 *size
= (*size
* 2 + 1);
639 snprintf(tmp
->name
, PATH_MAX
, "%s", name
);
640 bcopy(us
, &tmp
->stat
, sizeof(*us
));
643 (++tmp
)->name
[0] = 0;
649 /*** Authentication-related utility functions ********************************/
652 _fetch_read_word(FILE *f
)
654 static char word
[1024];
656 if (fscanf(f
, " %1023s ", word
) != 1)
662 * Get authentication data for a URL from .netrc
665 _fetch_netrc_auth(struct url
*url
)
672 if ((p
= getenv("NETRC")) != NULL
) {
673 if (snprintf(fn
, sizeof(fn
), "%s", p
) >= (int)sizeof(fn
)) {
674 _fetch_info("$NETRC specifies a file name "
675 "longer than PATH_MAX");
679 if ((p
= getenv("HOME")) != NULL
) {
682 if ((pwd
= getpwuid(getuid())) == NULL
||
683 (p
= pwd
->pw_dir
) == NULL
)
686 if (snprintf(fn
, sizeof(fn
), "%s/.netrc", p
) >= (int)sizeof(fn
))
690 if ((f
= fopen(fn
, "r")) == NULL
)
692 while ((word
= _fetch_read_word(f
)) != NULL
) {
693 if (strcmp(word
, "default") == 0) {
694 DEBUG(_fetch_info("Using default .netrc settings"));
697 if (strcmp(word
, "machine") == 0 &&
698 (word
= _fetch_read_word(f
)) != NULL
&&
699 strcasecmp(word
, url
->host
) == 0) {
700 DEBUG(_fetch_info("Using .netrc settings for %s", word
));
706 while ((word
= _fetch_read_word(f
)) != NULL
) {
707 if (strcmp(word
, "login") == 0) {
708 if ((word
= _fetch_read_word(f
)) == NULL
)
710 if (snprintf(url
->user
, sizeof(url
->user
),
711 "%s", word
) > (int)sizeof(url
->user
)) {
712 _fetch_info("login name in .netrc is too long");
715 } else if (strcmp(word
, "password") == 0) {
716 if ((word
= _fetch_read_word(f
)) == NULL
)
718 if (snprintf(url
->pwd
, sizeof(url
->pwd
),
719 "%s", word
) > (int)sizeof(url
->pwd
)) {
720 _fetch_info("password in .netrc is too long");
723 } else if (strcmp(word
, "account") == 0) {
724 if ((word
= _fetch_read_word(f
)) == NULL
)
726 /* XXX not supported! */