2 * Copyright (c) 1998 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.7.2.13 2003/06/06 06:45:25 des Exp $
29 * $DragonFly: src/lib/libfetch/common.c,v 1.3 2004/08/16 16:19:31 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
[] = {
57 { EAI_NODATA
, FETCH_RESOLV
, "Host not found" },
58 { EAI_AGAIN
, FETCH_TEMP
, "Transient resolver failure" },
59 { EAI_FAIL
, FETCH_RESOLV
, "Non-recoverable resolver failure" },
60 { EAI_NONAME
, FETCH_RESOLV
, "No address record" },
61 { -1, FETCH_UNKNOWN
, "Unknown resolver error" }
65 static const char ENDL
[2] = "\r\n";
68 /*** Error-reporting functions ***********************************************/
71 * Map error code to string
73 static struct fetcherr
*
74 _fetch_finderr(struct fetcherr
*p
, int e
)
76 while (p
->num
!= -1 && p
->num
!= e
)
85 _fetch_seterr(struct fetcherr
*p
, int e
)
87 p
= _fetch_finderr(p
, e
);
88 fetchLastErrCode
= p
->cat
;
89 snprintf(fetchLastErrString
, MAXERRSTRING
, "%s", p
->string
);
93 * Set error code according to errno
100 fetchLastErrCode
= FETCH_OK
;
107 fetchLastErrCode
= FETCH_AUTH
;
110 case EISDIR
: /* XXX */
111 fetchLastErrCode
= FETCH_UNAVAIL
;
114 fetchLastErrCode
= FETCH_MEMORY
;
118 fetchLastErrCode
= FETCH_TEMP
;
121 fetchLastErrCode
= FETCH_EXISTS
;
124 fetchLastErrCode
= FETCH_FULL
;
132 fetchLastErrCode
= FETCH_NETWORK
;
136 fetchLastErrCode
= FETCH_ABORT
;
139 fetchLastErrCode
= FETCH_TIMEOUT
;
143 fetchLastErrCode
= FETCH_DOWN
;
146 fetchLastErrCode
= FETCH_UNKNOWN
;
148 snprintf(fetchLastErrString
, MAXERRSTRING
, "%s", strerror(errno
));
153 * Emit status message
156 _fetch_info(const char *fmt
, ...)
161 vfprintf(stderr
, fmt
, ap
);
167 /*** Network-related utility functions ***************************************/
170 * Return the default port for a scheme
173 _fetch_default_port(const char *scheme
)
177 if ((se
= getservbyname(scheme
, "tcp")) != NULL
)
178 return (ntohs(se
->s_port
));
179 if (strcasecmp(scheme
, SCHEME_FTP
) == 0)
180 return (FTP_DEFAULT_PORT
);
181 if (strcasecmp(scheme
, SCHEME_HTTP
) == 0)
182 return (HTTP_DEFAULT_PORT
);
187 * Return the default proxy port for a scheme
190 _fetch_default_proxy_port(const char *scheme
)
192 if (strcasecmp(scheme
, SCHEME_FTP
) == 0)
193 return (FTP_DEFAULT_PROXY_PORT
);
194 if (strcasecmp(scheme
, SCHEME_HTTP
) == 0)
195 return (HTTP_DEFAULT_PROXY_PORT
);
201 * Create a connection for an existing descriptor.
204 _fetch_reopen(int sd
)
208 /* allocate and fill connection structure */
209 if ((conn
= calloc(1, sizeof(*conn
))) == NULL
)
218 * Bump a connection's reference count.
221 _fetch_ref(conn_t
*conn
)
230 * Bind a socket to a specific local address
233 _fetch_bind(int sd
, int af
, const char *addr
)
235 struct addrinfo hints
, *res
, *res0
;
238 memset(&hints
, 0, sizeof(hints
));
239 hints
.ai_family
= af
;
240 hints
.ai_socktype
= SOCK_STREAM
;
241 hints
.ai_protocol
= 0;
242 if ((err
= getaddrinfo(addr
, NULL
, &hints
, &res0
)) != 0)
244 for (res
= res0
; res
; res
= res
->ai_next
)
245 if (bind(sd
, res
->ai_addr
, res
->ai_addrlen
) == 0)
252 * Establish a TCP connection to the specified port on the specified host.
255 _fetch_connect(const char *host
, int port
, int af
, int verbose
)
259 const char *bindaddr
;
260 struct addrinfo hints
, *res
, *res0
;
263 DEBUG(fprintf(stderr
, "---> %s:%d\n", host
, port
));
266 _fetch_info("looking up %s", host
);
268 /* look up host name and set up socket address structure */
269 snprintf(pbuf
, sizeof(pbuf
), "%d", port
);
270 memset(&hints
, 0, sizeof(hints
));
271 hints
.ai_family
= af
;
272 hints
.ai_socktype
= SOCK_STREAM
;
273 hints
.ai_protocol
= 0;
274 if ((err
= getaddrinfo(host
, pbuf
, &hints
, &res0
)) != 0) {
278 bindaddr
= getenv("FETCH_BIND_ADDRESS");
281 _fetch_info("connecting to %s:%d", host
, port
);
284 for (sd
= -1, res
= res0
; res
; sd
= -1, res
= res
->ai_next
) {
285 if ((sd
= socket(res
->ai_family
, res
->ai_socktype
,
286 res
->ai_protocol
)) == -1)
288 if (bindaddr
!= NULL
&& *bindaddr
!= '\0' &&
289 _fetch_bind(sd
, res
->ai_family
, bindaddr
) != 0) {
290 _fetch_info("failed to bind to '%s'", bindaddr
);
294 if (connect(sd
, res
->ai_addr
, res
->ai_addrlen
) == 0)
304 if ((conn
= _fetch_reopen(sd
)) == NULL
) {
313 * Enable SSL on a connection.
316 _fetch_ssl(conn_t
*conn
, int verbose
)
320 /* Init the SSL library and context */
321 if (!SSL_library_init()){
322 fprintf(stderr
, "SSL library init failed\n");
326 SSL_load_error_strings();
328 conn
->ssl_meth
= SSLv23_client_method();
329 conn
->ssl_ctx
= SSL_CTX_new(conn
->ssl_meth
);
330 SSL_CTX_set_mode(conn
->ssl_ctx
, SSL_MODE_AUTO_RETRY
);
332 conn
->ssl
= SSL_new(conn
->ssl_ctx
);
333 if (conn
->ssl
== NULL
){
334 fprintf(stderr
, "SSL context creation failed\n");
337 SSL_set_fd(conn
->ssl
, conn
->sd
);
338 if (SSL_connect(conn
->ssl
) == -1){
339 ERR_print_errors_fp(stderr
);
347 fprintf(stderr
, "SSL connection established using %s\n",
348 SSL_get_cipher(conn
->ssl
));
349 conn
->ssl_cert
= SSL_get_peer_certificate(conn
->ssl
);
350 name
= X509_get_subject_name(conn
->ssl_cert
);
351 str
= X509_NAME_oneline(name
, 0, 0);
352 printf("Certificate subject: %s\n", str
);
354 name
= X509_get_issuer_name(conn
->ssl_cert
);
355 str
= X509_NAME_oneline(name
, 0, 0);
356 printf("Certificate issuer: %s\n", str
);
364 fprintf(stderr
, "SSL support disabled\n");
371 * Read a character from a connection w/ timeout
374 _fetch_read(conn_t
*conn
, char *buf
, size_t len
)
376 struct timeval now
, timeout
, wait
;
383 gettimeofday(&timeout
, NULL
);
384 timeout
.tv_sec
+= fetchTimeout
;
389 while (fetchTimeout
&& !FD_ISSET(conn
->sd
, &readfds
)) {
390 FD_SET(conn
->sd
, &readfds
);
391 gettimeofday(&now
, NULL
);
392 wait
.tv_sec
= timeout
.tv_sec
- now
.tv_sec
;
393 wait
.tv_usec
= timeout
.tv_usec
- now
.tv_usec
;
394 if (wait
.tv_usec
< 0) {
395 wait
.tv_usec
+= 1000000;
398 if (wait
.tv_sec
< 0) {
404 r
= select(conn
->sd
+ 1, &readfds
, NULL
, NULL
, &wait
);
406 if (errno
== EINTR
&& fetchRestartCalls
)
413 if (conn
->ssl
!= NULL
)
414 rlen
= SSL_read(conn
->ssl
, buf
, len
);
417 rlen
= read(conn
->sd
, buf
, len
);
421 if (errno
== EINTR
&& fetchRestartCalls
)
434 * Read a line of text from a connection w/ timeout
436 #define MIN_BUF_SIZE 1024
439 _fetch_getln(conn_t
*conn
)
446 if (conn
->buf
== NULL
) {
447 if ((conn
->buf
= malloc(MIN_BUF_SIZE
)) == NULL
) {
451 conn
->bufsize
= MIN_BUF_SIZE
;
458 len
= _fetch_read(conn
, &c
, 1);
463 conn
->buf
[conn
->buflen
++] = c
;
464 if (conn
->buflen
== conn
->bufsize
) {
466 tmpsize
= conn
->bufsize
* 2 + 1;
467 if ((tmp
= realloc(tmp
, tmpsize
)) == NULL
) {
472 conn
->bufsize
= tmpsize
;
476 conn
->buf
[conn
->buflen
] = '\0';
477 DEBUG(fprintf(stderr
, "<<< %s", conn
->buf
));
483 * Write to a connection w/ timeout
486 _fetch_write(conn_t
*conn
, const char *buf
, size_t len
)
490 /* This is correct, because writev doesn't change the buffer */
491 iov
.iov_base
= __DECONST(char *, buf
);
493 return _fetch_writev(conn
, &iov
, 1);
497 * Write a vector to a connection w/ timeout
498 * Note: can modify the iovec.
501 _fetch_writev(conn_t
*conn
, struct iovec
*iov
, int iovcnt
)
503 struct timeval now
, timeout
, wait
;
510 gettimeofday(&timeout
, NULL
);
511 timeout
.tv_sec
+= fetchTimeout
;
516 while (fetchTimeout
&& !FD_ISSET(conn
->sd
, &writefds
)) {
517 FD_SET(conn
->sd
, &writefds
);
518 gettimeofday(&now
, NULL
);
519 wait
.tv_sec
= timeout
.tv_sec
- now
.tv_sec
;
520 wait
.tv_usec
= timeout
.tv_usec
- now
.tv_usec
;
521 if (wait
.tv_usec
< 0) {
522 wait
.tv_usec
+= 1000000;
525 if (wait
.tv_sec
< 0) {
531 r
= select(conn
->sd
+ 1, NULL
, &writefds
, NULL
, &wait
);
533 if (errno
== EINTR
&& fetchRestartCalls
)
540 if (conn
->ssl
!= NULL
)
541 wlen
= SSL_write(conn
->ssl
,
542 iov
->iov_base
, iov
->iov_len
);
545 wlen
= writev(conn
->sd
, iov
, iovcnt
);
547 /* we consider a short write a failure */
553 if (errno
== EINTR
&& fetchRestartCalls
)
558 while (iovcnt
> 0 && wlen
>= (ssize_t
)iov
->iov_len
) {
559 wlen
-= iov
->iov_len
;
564 iov
->iov_len
-= wlen
;
565 iov
->iov_base
= (char *)iov
->iov_base
+ wlen
;
573 * Write a line of text to a connection w/ timeout
576 _fetch_putln(conn_t
*conn
, const char *str
, size_t len
)
581 DEBUG(fprintf(stderr
, ">>> %s\n", str
));
582 /* This is correct, because writev doesn't change the buffer */
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
);
613 /*** Directory-related utility functions *************************************/
616 _fetch_add_entry(struct url_ent
**p
, int *size
, int *len
,
617 const char *name
, struct url_stat
*us
)
626 if (*len
>= *size
- 1) {
627 tmp
= realloc(*p
, (*size
* 2 + 1) * sizeof(**p
));
633 *size
= (*size
* 2 + 1);
638 snprintf(tmp
->name
, PATH_MAX
, "%s", name
);
639 bcopy(us
, &tmp
->stat
, sizeof(*us
));
642 (++tmp
)->name
[0] = 0;
648 /*** Authentication-related utility functions ********************************/
651 _fetch_read_word(FILE *f
)
653 static char word
[1024];
655 if (fscanf(f
, " %1024s ", word
) != 1)
661 * Get authentication data for a URL from .netrc
664 _fetch_netrc_auth(struct url
*url
)
671 if ((p
= getenv("NETRC")) != NULL
) {
672 if (snprintf(fn
, sizeof(fn
), "%s", p
) >= (int)sizeof(fn
)) {
673 _fetch_info("$NETRC specifies a file name "
674 "longer than PATH_MAX");
678 if ((p
= getenv("HOME")) != NULL
) {
681 if ((pwd
= getpwuid(getuid())) == NULL
||
682 (p
= pwd
->pw_dir
) == NULL
)
685 if (snprintf(fn
, sizeof(fn
), "%s/.netrc", p
) >= (int)sizeof(fn
))
689 if ((f
= fopen(fn
, "r")) == NULL
)
691 while ((word
= _fetch_read_word(f
)) != NULL
) {
692 if (strcmp(word
, "default") == 0) {
693 DEBUG(_fetch_info("Using default .netrc settings"));
696 if (strcmp(word
, "machine") == 0 &&
697 (word
= _fetch_read_word(f
)) != NULL
&&
698 strcasecmp(word
, url
->host
) == 0) {
699 DEBUG(_fetch_info("Using .netrc settings for %s", word
));
705 while ((word
= _fetch_read_word(f
)) != NULL
) {
706 if (strcmp(word
, "login") == 0) {
707 if ((word
= _fetch_read_word(f
)) == NULL
)
709 if (snprintf(url
->user
, sizeof(url
->user
),
710 "%s", word
) > (int)sizeof(url
->user
)) {
711 _fetch_info("login name in .netrc is too long");
714 } else if (strcmp(word
, "password") == 0) {
715 if ((word
= _fetch_read_word(f
)) == NULL
)
717 if (snprintf(url
->pwd
, sizeof(url
->pwd
),
718 "%s", word
) > (int)sizeof(url
->pwd
)) {
719 _fetch_info("password in .netrc is too long");
722 } else if (strcmp(word
, "account") == 0) {
723 if ((word
= _fetch_read_word(f
)) == NULL
)
725 /* XXX not supported! */