summaryrefslogtreecommitdiff
path: root/lib/libfetch
diff options
context:
space:
mode:
authorDag-Erling Smørgrav <des@FreeBSD.org>2003-06-06 06:45:25 +0000
committerDag-Erling Smørgrav <des@FreeBSD.org>2003-06-06 06:45:25 +0000
commitc8f917b63af1e5deeb5c4e3e19bd48cc465d3440 (patch)
treedbe60d62fdbb88a865a5e56488d0a1ecb60b6244 /lib/libfetch
parent65650f20962d6a32092be2cba47ef0c730342802 (diff)
Notes
Diffstat (limited to 'lib/libfetch')
-rw-r--r--lib/libfetch/common.c134
-rw-r--r--lib/libfetch/common.h2
-rw-r--r--lib/libfetch/fetch.313
-rw-r--r--lib/libfetch/fetch.c8
-rw-r--r--lib/libfetch/ftp.c25
-rw-r--r--lib/libfetch/http.c13
6 files changed, 173 insertions, 22 deletions
diff --git a/lib/libfetch/common.c b/lib/libfetch/common.c
index 5a2313d914ab..ac67fd0192c6 100644
--- a/lib/libfetch/common.c
+++ b/lib/libfetch/common.c
@@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$");
#include <errno.h>
#include <netdb.h>
+#include <pwd.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
@@ -226,6 +227,28 @@ _fetch_ref(conn_t *conn)
/*
+ * Bind a socket to a specific local address
+ */
+int
+_fetch_bind(int sd, int af, const char *addr)
+{
+ struct addrinfo hints, *res, *res0;
+ int err;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0)
+ return (-1);
+ for (res = res0; res; res = res->ai_next)
+ if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
+ return (0);
+ return (-1);
+}
+
+
+/*
* Establish a TCP connection to the specified port on the specified host.
*/
conn_t *
@@ -233,6 +256,7 @@ _fetch_connect(const char *host, int port, int af, int verbose)
{
conn_t *conn;
char pbuf[10];
+ const char *bindaddr;
struct addrinfo hints, *res, *res0;
int sd, err;
@@ -251,19 +275,25 @@ _fetch_connect(const char *host, int port, int af, int verbose)
_netdb_seterr(err);
return (NULL);
}
+ bindaddr = getenv("FETCH_BIND_ADDRESS");
if (verbose)
_fetch_info("connecting to %s:%d", host, port);
/* try to connect */
- for (sd = -1, res = res0; res; res = res->ai_next) {
+ for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
if ((sd = socket(res->ai_family, res->ai_socktype,
res->ai_protocol)) == -1)
continue;
- if (connect(sd, res->ai_addr, res->ai_addrlen) != -1)
+ if (bindaddr != NULL && *bindaddr != '\0' &&
+ _fetch_bind(sd, res->ai_family, bindaddr) != 0) {
+ _fetch_info("failed to bind to '%s'", bindaddr);
+ close(sd);
+ continue;
+ }
+ if (connect(sd, res->ai_addr, res->ai_addrlen) == 0)
break;
close(sd);
- sd = -1;
}
freeaddrinfo(res0);
if (sd == -1) {
@@ -457,7 +487,7 @@ _fetch_write(conn_t *conn, const char *buf, size_t len)
{
struct iovec iov;
- iov.iov_base = buf;
+ iov.iov_base = (char *)buf;
iov.iov_len = len;
return _fetch_writev(conn, &iov, 1);
}
@@ -531,7 +561,7 @@ _fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
}
if (iovcnt > 0) {
iov->iov_len -= wlen;
- iov->iov_base = iov->iov_base + wlen;
+ iov->iov_base = (char *)iov->iov_base + wlen;
}
}
return (total);
@@ -548,9 +578,9 @@ _fetch_putln(conn_t *conn, const char *str, size_t len)
int ret;
DEBUG(fprintf(stderr, ">>> %s\n", str));
- iov[0].iov_base = str;
+ iov[0].iov_base = (char *)str;
iov[0].iov_len = len;
- iov[1].iov_base = ENDL;
+ iov[1].iov_base = (char *)ENDL;
iov[1].iov_len = sizeof(ENDL);
if (len == 0)
ret = _fetch_writev(conn, &iov[1], 1);
@@ -611,3 +641,93 @@ _fetch_add_entry(struct url_ent **p, int *size, int *len,
return (0);
}
+
+
+/*** Authentication-related utility functions ********************************/
+
+static const char *
+_fetch_read_word(FILE *f)
+{
+ static char word[1024];
+
+ if (fscanf(f, " %1024s ", word) != 1)
+ return (NULL);
+ return (word);
+}
+
+/*
+ * Get authentication data for a URL from .netrc
+ */
+int
+_fetch_netrc_auth(struct url *url)
+{
+ char fn[PATH_MAX];
+ const char *word;
+ char *p;
+ FILE *f;
+
+ if ((p = getenv("NETRC")) != NULL) {
+ if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
+ _fetch_info("$NETRC specifies a file name "
+ "longer than PATH_MAX");
+ return (-1);
+ }
+ } else {
+ if ((p = getenv("HOME")) != NULL) {
+ struct passwd *pwd;
+
+ if ((pwd = getpwuid(getuid())) == NULL ||
+ (p = pwd->pw_dir) == NULL)
+ return (-1);
+ }
+ if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
+ return (-1);
+ }
+
+ if ((f = fopen(fn, "r")) == NULL)
+ return (-1);
+ while ((word = _fetch_read_word(f)) != NULL) {
+ if (strcmp(word, "default") == 0) {
+ DEBUG(_fetch_info("Using default .netrc settings"));
+ break;
+ }
+ if (strcmp(word, "machine") == 0 &&
+ (word = _fetch_read_word(f)) != NULL &&
+ strcasecmp(word, url->host) == 0) {
+ DEBUG(_fetch_info("Using .netrc settings for %s", word));
+ break;
+ }
+ }
+ if (word == NULL)
+ goto ferr;
+ while ((word = _fetch_read_word(f)) != NULL) {
+ if (strcmp(word, "login") == 0) {
+ if ((word = _fetch_read_word(f)) == NULL)
+ goto ferr;
+ if (snprintf(url->user, sizeof(url->user),
+ "%s", word) > (int)sizeof(url->user)) {
+ _fetch_info("login name in .netrc is too long");
+ url->user[0] = '\0';
+ }
+ } else if (strcmp(word, "password") == 0) {
+ if ((word = _fetch_read_word(f)) == NULL)
+ goto ferr;
+ if (snprintf(url->pwd, sizeof(url->pwd),
+ "%s", word) > (int)sizeof(url->pwd)) {
+ _fetch_info("password in .netrc is too long");
+ url->pwd[0] = '\0';
+ }
+ } else if (strcmp(word, "account") == 0) {
+ if ((word = _fetch_read_word(f)) == NULL)
+ goto ferr;
+ /* XXX not supported! */
+ } else {
+ break;
+ }
+ }
+ fclose(f);
+ return (0);
+ ferr:
+ fclose(f);
+ return (-1);
+}
diff --git a/lib/libfetch/common.h b/lib/libfetch/common.h
index 4b42bf2324f8..6f652593076a 100644
--- a/lib/libfetch/common.h
+++ b/lib/libfetch/common.h
@@ -76,6 +76,7 @@ void _fetch_syserr(void);
void _fetch_info(const char *, ...);
int _fetch_default_port(const char *);
int _fetch_default_proxy_port(const char *);
+int _fetch_bind(int, int, const char *);
conn_t *_fetch_connect(const char *, int, int, int);
conn_t *_fetch_reopen(int);
conn_t *_fetch_ref(conn_t *);
@@ -88,6 +89,7 @@ int _fetch_putln(conn_t *, const char *, size_t);
int _fetch_close(conn_t *);
int _fetch_add_entry(struct url_ent **, int *, int *,
const char *, struct url_stat *);
+int _fetch_netrc_auth(struct url *url);
#define _ftp_seterr(n) _fetch_seterr(_ftp_errlist, n)
#define _http_seterr(n) _fetch_seterr(_http_errlist, n)
diff --git a/lib/libfetch/fetch.3 b/lib/libfetch/fetch.3
index 4bd63bde9594..0e107d07f97a 100644
--- a/lib/libfetch/fetch.3
+++ b/lib/libfetch/fetch.3
@@ -444,7 +444,10 @@ Invalid URL
The accompanying error message includes a protocol-specific error code
and message, e.g. "File is not available (404 Not Found)"
.Sh ENVIRONMENT
-.Bl -tag -width FTP_PASSIVE_MODE
+.Bl -tag -width ".Ev FETCH_BIND_ADDRESS"
+.It Ev FETCH_BIND_ADDRESS
+Specifies a hostname or IP address to which sockets used for outgoing
+connections will be bound.
.It Ev FTP_LOGIN
Default FTP login if none was provided in the URL.
.It Ev FTP_PASSIVE_MODE
@@ -520,6 +523,14 @@ the document URL will be used as referrer URL.
Specifies the User-Agent string to use for HTTP requests.
This can be useful when working with HTTP origin or proxy servers that
differentiate between user agents.
+.It Ev NETRC
+Specifies a file to use instead of
+.Pa ~/.netrc
+to look up login names and passwords for FTP sites.
+See
+.Xr ftp 1
+for a description of the file format.
+This feature is experimental.
.El
.Sh SEE ALSO
.Xr fetch 1 ,
diff --git a/lib/libfetch/fetch.c b/lib/libfetch/fetch.c
index a7d310bfd39c..59ee8a849f5f 100644
--- a/lib/libfetch/fetch.c
+++ b/lib/libfetch/fetch.c
@@ -77,6 +77,10 @@ fetchXGet(struct url *URL, struct url_stat *us, const char *flags)
int direct;
direct = CHECK_FLAG('d');
+ if (us != NULL) {
+ us->size = -1;
+ us->atime = us->mtime = 0;
+ }
if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
return (fetchXGetFile(URL, us, flags));
else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0)
@@ -131,6 +135,10 @@ fetchStat(struct url *URL, struct url_stat *us, const char *flags)
int direct;
direct = CHECK_FLAG('d');
+ if (us != NULL) {
+ us->size = -1;
+ us->atime = us->mtime = 0;
+ }
if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
return (fetchStatFile(URL, us, flags));
else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0)
diff --git a/lib/libfetch/ftp.c b/lib/libfetch/ftp.c
index a24a67d90875..91940d3af22b 100644
--- a/lib/libfetch/ftp.c
+++ b/lib/libfetch/ftp.c
@@ -730,10 +730,12 @@ _ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)
/* XXX FTP_AUTH, and maybe .netrc */
/* send user name and password */
+ if (url->user[0] == '\0')
+ _fetch_netrc_auth(url);
user = url->user;
- if (!user || !*user)
+ if (*user == '\0')
user = getenv("FTP_LOGIN");
- if (!user || !*user)
+ if (user == NULL || *user == '\0')
user = FTP_ANONYMOUS_USER;
if (purl && url->port == _fetch_default_port(url->scheme))
e = _ftp_cmd(conn, "USER %s@%s", user, url->host);
@@ -745,9 +747,9 @@ _ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)
/* did the server request a password? */
if (e == FTP_NEED_PASSWORD) {
pwd = url->pwd;
- if (!pwd || !*pwd)
+ if (*pwd == '\0')
pwd = getenv("FTP_PASSWORD");
- if (!pwd || !*pwd) {
+ if (pwd == NULL || *pwd == '\0') {
if ((logname = getlogin()) == 0)
logname = FTP_ANONYMOUS_USER;
if ((len = snprintf(pbuf, MAXLOGNAME + 1, "%s@", logname)) < 0)
@@ -887,11 +889,13 @@ _ftp_cached_connect(struct url *url, struct url *purl, const char *flags)
* Check the proxy settings
*/
static struct url *
-_ftp_get_proxy(void)
+_ftp_get_proxy(const char *flags)
{
struct url *purl;
char *p;
+ if (flags != NULL && strchr(flags, 'd') != NULL)
+ return (NULL);
if (((p = getenv("FTP_PROXY")) || (p = getenv("ftp_proxy")) ||
(p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) &&
*p && (purl = fetchParseURL(p)) != NULL) {
@@ -968,7 +972,7 @@ _ftp_request(struct url *url, const char *op, struct url_stat *us,
FILE *
fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags)
{
- return (_ftp_request(url, "RETR", us, _ftp_get_proxy(), flags));
+ return (_ftp_request(url, "RETR", us, _ftp_get_proxy(flags), flags));
}
/*
@@ -987,8 +991,8 @@ FILE *
fetchPutFTP(struct url *url, const char *flags)
{
- return _ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL,
- _ftp_get_proxy(), flags);
+ return (_ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL,
+ _ftp_get_proxy(flags), flags));
}
/*
@@ -997,9 +1001,12 @@ fetchPutFTP(struct url *url, const char *flags)
int
fetchStatFTP(struct url *url, struct url_stat *us, const char *flags)
{
+ FILE *f;
- if (_ftp_request(url, "STAT", us, _ftp_get_proxy(), flags) == NULL)
+ f = _ftp_request(url, "STAT", us, _ftp_get_proxy(flags), flags);
+ if (f == NULL)
return (-1);
+ fclose(f);
return (0);
}
diff --git a/lib/libfetch/http.c b/lib/libfetch/http.c
index 7dbcdd75392d..86f7075268fd 100644
--- a/lib/libfetch/http.c
+++ b/lib/libfetch/http.c
@@ -696,11 +696,13 @@ _http_connect(struct url *URL, struct url *purl, const char *flags)
}
static struct url *
-_http_get_proxy(void)
+_http_get_proxy(const char *flags)
{
struct url *purl;
char *p;
+ if (flags != NULL && strchr(flags, 'd') != NULL)
+ return (NULL);
if (((p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) &&
(purl = fetchParseURL(p))) {
if (!*purl->scheme)
@@ -887,7 +889,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
_http_cmd(conn, "User-Agent: %s", p);
else
_http_cmd(conn, "User-Agent: %s " _LIBFETCH_VER, getprogname());
- if (url->offset)
+ if (url->offset > 0)
_http_cmd(conn, "Range: bytes=%lld-", (long long)url->offset);
_http_cmd(conn, "Connection: close");
_http_cmd(conn, "");
@@ -1059,7 +1061,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
}
/* too far? */
- if (offset > URL->offset) {
+ if (URL->offset > 0 && offset > URL->offset) {
_http_seterr(HTTP_PROTOCOL_ERROR);
goto ouch;
}
@@ -1108,7 +1110,7 @@ ouch:
FILE *
fetchXGetHTTP(struct url *URL, struct url_stat *us, const char *flags)
{
- return (_http_request(URL, "GET", us, _http_get_proxy(), flags));
+ return (_http_request(URL, "GET", us, _http_get_proxy(flags), flags));
}
/*
@@ -1138,7 +1140,8 @@ fetchStatHTTP(struct url *URL, struct url_stat *us, const char *flags)
{
FILE *f;
- if ((f = _http_request(URL, "HEAD", us, _http_get_proxy(), flags)) == NULL)
+ f = _http_request(URL, "HEAD", us, _http_get_proxy(flags), flags);
+ if (f == NULL)
return (-1);
fclose(f);
return (0);