summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/libfetch/common.c82
-rw-r--r--lib/libfetch/common.h5
-rw-r--r--lib/libfetch/fetch.c1
-rw-r--r--lib/libfetch/fetch.h1
-rw-r--r--lib/libfetch/ftp.c216
5 files changed, 206 insertions, 99 deletions
diff --git a/lib/libfetch/common.c b/lib/libfetch/common.c
index 8b3b891..ed5072c 100644
--- a/lib/libfetch/common.c
+++ b/lib/libfetch/common.c
@@ -30,6 +30,7 @@
#include <sys/param.h>
#include <sys/socket.h>
+#include <sys/time.h>
#include <netinet/in.h>
#include <com_err.h>
@@ -242,6 +243,87 @@ _fetch_connect(char *host, int port, int verbose)
}
+/*
+ * Read a line of text from a socket w/ timeout
+ */
+#define MIN_BUF_SIZE 1024
+
+int
+_fetch_getln(int fd, char **buf, size_t *size, size_t *len)
+{
+ struct timeval now, timeout, wait;
+ fd_set readfds;
+ int r;
+ char c;
+
+ if (*buf == NULL) {
+ if ((*buf = malloc(MIN_BUF_SIZE)) == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ *size = MIN_BUF_SIZE;
+ }
+
+ **buf = '\0';
+ *len = 0;
+
+ if (fetchTimeout) {
+ gettimeofday(&timeout, NULL);
+ timeout.tv_sec += fetchTimeout;
+ FD_ZERO(&readfds);
+ }
+
+ do {
+ if (fetchTimeout) {
+ FD_SET(fd, &readfds);
+ gettimeofday(&now, NULL);
+ wait.tv_sec = timeout.tv_sec - now.tv_sec;
+ wait.tv_usec = timeout.tv_usec - now.tv_usec;
+ if (wait.tv_usec < 0) {
+ wait.tv_usec += 1000000;
+ wait.tv_sec--;
+ }
+ if (wait.tv_sec < 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ r = select(fd+1, &readfds, NULL, NULL, &wait);
+ if (r == -1) {
+ if (errno == EINTR)
+ continue;
+ /* EBADF or EINVAL: shouldn't happen */
+ return -1;
+ }
+ if (!FD_ISSET(fd, &readfds))
+ continue;
+ }
+ r = read(fd, &c, 1);
+ if (r == 0)
+ break;
+ if (r == -1) {
+ if (errno == EINTR)
+ continue;
+ /* any other error is bad news */
+ return -1;
+ }
+ (*buf)[*len] = c;
+ *len += 1;
+ if (*len == *size) {
+ char *tmp;
+
+ if ((tmp = realloc(*buf, *size * 2 + 1)) == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ *buf = tmp;
+ *size = *size * 2 + 1;
+ }
+ } while (c != '\n');
+
+ return 0;
+}
+
+
/*** Directory-related utility functions *************************************/
int
diff --git a/lib/libfetch/common.h b/lib/libfetch/common.h
index aa2059c..1762a00 100644
--- a/lib/libfetch/common.h
+++ b/lib/libfetch/common.h
@@ -38,10 +38,11 @@ struct fetcherr {
const char *string;
};
-void _fetch_seterr(struct fetcherr *, int);
+void _fetch_seterr(struct fetcherr *p, int e);
void _fetch_syserr(void);
int _fetch_info(char *fmt, ...);
-int _fetch_connect(char *, int, int);
+int _fetch_connect(char *host, int port, int verbose);
+int _fetch_getln(int fd, char **buf, size_t *size, size_t *len);
int _fetch_add_entry(struct url_ent **p, int *size, int *len,
char *name, struct url_stat *stat);
diff --git a/lib/libfetch/fetch.c b/lib/libfetch/fetch.c
index bf4476f..3c944a1 100644
--- a/lib/libfetch/fetch.c
+++ b/lib/libfetch/fetch.c
@@ -41,6 +41,7 @@
int fetchLastErrCode;
+int fetchTimeout;
/*** Local data **************************************************************/
diff --git a/lib/libfetch/fetch.h b/lib/libfetch/fetch.h
index df34509..2366791 100644
--- a/lib/libfetch/fetch.h
+++ b/lib/libfetch/fetch.h
@@ -91,5 +91,6 @@ struct url_ent *fetchList(struct url *, char *);
/* Last error code */
extern int fetchLastErrCode;
+extern int fetchTimeout;
#endif
diff --git a/lib/libfetch/ftp.c b/lib/libfetch/ftp.c
index 7631155..9f0a8ac 100644
--- a/lib/libfetch/ftp.c
+++ b/lib/libfetch/ftp.c
@@ -57,9 +57,11 @@
#include <sys/param.h>
#include <sys/socket.h>
+#include <sys/uio.h>
#include <netinet/in.h>
#include <ctype.h>
+#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -84,74 +86,96 @@
#define FTP_FILE_ACTION_OK 250
#define FTP_NEED_PASSWORD 331
#define FTP_NEED_ACCOUNT 332
+#define FTP_SYNTAX_ERROR 500
-#define ENDL "\r\n"
+static char ENDL[2] = "\r\n";
static struct url cached_host;
-static FILE *cached_socket;
+static int cached_socket;
-static char _ftp_last_reply[1024];
+static char *last_reply;
+static size_t lr_size, lr_length;
+static int last_code;
+
+#define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
+ && isdigit(foo[2]) && foo[3] == ' ')
+#define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
+ && isdigit(foo[2]) && foo[3] == '-')
/*
- * Get server response, check that first digit is a '2'
+ * Get server response
*/
static int
-_ftp_chkerr(FILE *s)
+_ftp_chkerr(int cd)
{
- char *line;
- size_t len;
-
do {
- if (((line = fgetln(s, &len)) == NULL) || (len < 4)) {
+ if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) {
_fetch_syserr();
return -1;
}
- } while (len >= 4 && line[3] == '-');
-
- while (len && isspace(line[len-1]))
- len--;
- snprintf(_ftp_last_reply, sizeof(_ftp_last_reply),
- "%*.*s", (int)len, (int)len, line);
-
#ifndef NDEBUG
- fprintf(stderr, "\033[1m<<< ");
- fprintf(stderr, "%*.*s\n", (int)len, (int)len, line);
- fprintf(stderr, "\033[m");
+ _fetch_info("got reply '%.*s'", lr_length - 2, last_reply);
#endif
+ } while (isftpinfo(last_reply));
+
+ while (lr_length && isspace(last_reply[lr_length-1]))
+ lr_length--;
+ last_reply[lr_length] = 0;
- if (len < 4 || !isdigit(line[1]) || !isdigit(line[1])
- || !isdigit(line[2]) || (line[3] != ' ')) {
+ if (!isftpreply(last_reply)) {
+ _ftp_seterr(999);
return -1;
}
- return (line[0] - '0') * 100 + (line[1] - '0') * 10 + (line[2] - '0');
+ last_code = (last_reply[0] - '0') * 100
+ + (last_reply[1] - '0') * 10
+ + (last_reply[2] - '0');
+
+ return last_code;
}
/*
* Send a command and check reply
*/
static int
-_ftp_cmd(FILE *f, char *fmt, ...)
+_ftp_cmd(int cd, char *fmt, ...)
{
va_list ap;
+ struct iovec iov[2];
+ char *msg;
+ int r;
va_start(ap, fmt);
- vfprintf(f, fmt, ap);
+ vasprintf(&msg, fmt, ap);
+ va_end(ap);
+
+ if (msg == NULL) {
+ errno = ENOMEM;
+ _fetch_syserr();
+ return -1;
+ }
#ifndef NDEBUG
- fprintf(stderr, "\033[1m>>> ");
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\033[m");
+ _fetch_info("sending '%s'", msg);
#endif
- va_end(ap);
+ iov[0].iov_base = msg;
+ iov[0].iov_len = strlen(msg);
+ iov[1].iov_base = ENDL;
+ iov[1].iov_len = sizeof(ENDL);
+ r = writev(cd, iov, 2);
+ free(msg);
+ if (r == -1) {
+ _fetch_syserr();
+ return -1;
+ }
- return _ftp_chkerr(f);
+ return _ftp_chkerr(cd);
}
/*
* Transfer file
*/
static FILE *
-_ftp_transfer(FILE *cf, char *oper, char *file, char *mode, char *flags)
+_ftp_transfer(int cd, char *oper, char *file, char *mode, char *flags)
{
struct sockaddr_in sin;
int pasv, high, verbose;
@@ -170,17 +194,19 @@ _ftp_transfer(FILE *cf, char *oper, char *file, char *mode, char *flags)
*s = 0;
if (verbose)
_fetch_info("changing directory to %s", file);
- if ((e = _ftp_cmd(cf, "CWD %s" ENDL, file)) != FTP_FILE_ACTION_OK) {
+ if ((e = _ftp_cmd(cd, "CWD %s", file)) != FTP_FILE_ACTION_OK) {
*s = '/';
- _ftp_seterr(e);
+ if (e != -1)
+ _ftp_seterr(e);
return NULL;
}
*s++ = '/';
} else {
if (verbose)
_fetch_info("changing directory to /");
- if ((e = _ftp_cmd(cf, "CWD /" ENDL)) != FTP_FILE_ACTION_OK) {
- _ftp_seterr(e);
+ if ((e = _ftp_cmd(cd, "CWD /")) != FTP_FILE_ACTION_OK) {
+ if (e != -1)
+ _ftp_seterr(e);
return NULL;
}
}
@@ -201,14 +227,14 @@ _ftp_transfer(FILE *cf, char *oper, char *file, char *mode, char *flags)
/* send PASV command */
if (verbose)
_fetch_info("setting passive mode");
- if ((e = _ftp_cmd(cf, "PASV" ENDL)) != FTP_PASSIVE_MODE)
+ if ((e = _ftp_cmd(cd, "PASV")) != FTP_PASSIVE_MODE)
goto ouch;
/*
* Find address and port number. The reply to the PASV command
* is IMHO the one and only weak point in the FTP protocol.
*/
- ln = _ftp_last_reply;
+ ln = last_reply;
for (p = ln + 3; !isdigit(*p); p++)
/* nothing */ ;
for (p--, i = 0; i < 6; i++) {
@@ -218,7 +244,7 @@ _ftp_transfer(FILE *cf, char *oper, char *file, char *mode, char *flags)
/* construct sockaddr for data socket */
l = sizeof(sin);
- if (getpeername(fileno(cf), (struct sockaddr *)&sin, &l) == -1)
+ if (getpeername(cd, (struct sockaddr *)&sin, &l) == -1)
goto sysouch;
bcopy(addr, (char *)&sin.sin_addr, 4);
bcopy(addr + 4, (char *)&sin.sin_port, 2);
@@ -232,7 +258,7 @@ _ftp_transfer(FILE *cf, char *oper, char *file, char *mode, char *flags)
/* make the server initiate the transfer */
if (verbose)
_fetch_info("initiating transfer");
- e = _ftp_cmd(cf, "%s %s" ENDL, oper, s);
+ e = _ftp_cmd(cd, "%s %s", oper, s);
if (e != FTP_OPEN_DATA_CONNECTION)
goto ouch;
@@ -243,7 +269,7 @@ _ftp_transfer(FILE *cf, char *oper, char *file, char *mode, char *flags)
/* find our own address, bind, and listen */
l = sizeof(sin);
- if (getsockname(fileno(cf), (struct sockaddr *)&sin, &l) == -1)
+ if (getsockname(cd, (struct sockaddr *)&sin, &l) == -1)
goto sysouch;
sin.sin_port = 0;
arg = high ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT;
@@ -262,7 +288,7 @@ _ftp_transfer(FILE *cf, char *oper, char *file, char *mode, char *flags)
goto sysouch;
a = ntohl(sin.sin_addr.s_addr);
p = ntohs(sin.sin_port);
- e = _ftp_cmd(cf, "PORT %d,%d,%d,%d,%d,%d" ENDL,
+ e = _ftp_cmd(cd, "PORT %d,%d,%d,%d,%d,%d",
(a >> 24) & 0xff, (a >> 16) & 0xff,
(a >> 8) & 0xff, a & 0xff,
(p >> 8) & 0xff, p & 0xff);
@@ -272,7 +298,7 @@ _ftp_transfer(FILE *cf, char *oper, char *file, char *mode, char *flags)
/* make the server initiate the transfer */
if (verbose)
_fetch_info("initiating transfer");
- e = _ftp_cmd(cf, "%s %s" ENDL, oper, s);
+ e = _ftp_cmd(cd, "%s %s", oper, s);
if (e != FTP_OPEN_DATA_CONNECTION)
goto ouch;
@@ -293,7 +319,8 @@ sysouch:
return NULL;
ouch:
- _ftp_seterr(e);
+ if (e != -1)
+ _ftp_seterr(e);
close(sd);
return NULL;
}
@@ -301,12 +328,11 @@ ouch:
/*
* Log on to FTP server
*/
-static FILE *
+static int
_ftp_connect(char *host, int port, char *user, char *pwd, char *flags)
{
- int sd, e, pp = FTP_DEFAULT_PORT, direct, verbose;
+ int cd, e, pp = FTP_DEFAULT_PORT, direct, verbose;
char *p, *q;
- FILE *f;
direct = (flags && strchr(flags, 'd'));
verbose = (flags && strchr(flags, 'v'));
@@ -319,43 +345,36 @@ _ftp_connect(char *host, int port, char *user, char *pwd, char *flags)
}
if (q)
*q = 0;
- sd = _fetch_connect(p, pp, verbose);
+ cd = _fetch_connect(p, pp, verbose);
if (q)
*q = ':';
} else {
/* no proxy, go straight to target */
- sd = _fetch_connect(host, port, verbose);
+ cd = _fetch_connect(host, port, verbose);
p = NULL;
}
/* check connection */
- if (sd == -1) {
+ if (cd == -1) {
_fetch_syserr();
return NULL;
}
- /* streams make life easier */
- if ((f = fdopen(sd, "r+")) == NULL) {
- _fetch_syserr();
- close(sd);
- return NULL;
- }
-
/* expect welcome message */
- if ((e = _ftp_chkerr(f)) != FTP_SERVICE_READY)
+ if ((e = _ftp_chkerr(cd)) != FTP_SERVICE_READY)
goto fouch;
/* send user name and password */
if (!user || !*user)
user = FTP_ANONYMOUS_USER;
- e = p ? _ftp_cmd(f, "USER %s@%s@%d" ENDL, user, host, port)
- : _ftp_cmd(f, "USER %s" ENDL, user);
+ e = p ? _ftp_cmd(cd, "USER %s@%s@%d", user, host, port)
+ : _ftp_cmd(cd, "USER %s", user);
/* did the server request a password? */
if (e == FTP_NEED_PASSWORD) {
if (!pwd || !*pwd)
pwd = FTP_ANONYMOUS_PASSWORD;
- e = _ftp_cmd(f, "PASS %s" ENDL, pwd);
+ e = _ftp_cmd(cd, "PASS %s", pwd);
}
/* did the server request an account? */
@@ -368,18 +387,19 @@ _ftp_connect(char *host, int port, char *user, char *pwd, char *flags)
/* might as well select mode and type at once */
#ifdef FTP_FORCE_STREAM_MODE
- if ((e = _ftp_cmd(f, "MODE S" ENDL)) != FTP_OK) /* default is S */
+ if ((e = _ftp_cmd(cd, "MODE S")) != FTP_OK) /* default is S */
goto fouch;
#endif
- if ((e = _ftp_cmd(f, "TYPE I" ENDL)) != FTP_OK) /* default is A */
+ if ((e = _ftp_cmd(cd, "TYPE I")) != FTP_OK) /* default is A */
goto fouch;
/* done */
- return f;
+ return cd;
fouch:
- _ftp_seterr(e);
- fclose(f);
+ if (e != -1)
+ _ftp_seterr(e);
+ close(cd);
return NULL;
}
@@ -387,10 +407,10 @@ fouch:
* Disconnect from server
*/
static void
-_ftp_disconnect(FILE *f)
+_ftp_disconnect(int cd)
{
- (void)_ftp_cmd(f, "QUIT" ENDL);
- fclose(f);
+ (void)_ftp_cmd(cd, "QUIT");
+ close(cd);
}
/*
@@ -409,34 +429,36 @@ _ftp_isconnected(struct url *url)
/*
* Check the cache, reconnect if no luck
*/
-static FILE *
+static int
_ftp_cached_connect(struct url *url, char *flags)
{
- FILE *cf;
+ int e, cd;
- cf = NULL;
+ cd = -1;
/* set default port */
if (!url->port)
url->port = FTP_DEFAULT_PORT;
/* try to use previously cached connection */
- if (_ftp_isconnected(url))
- if (_ftp_cmd(cached_socket, "NOOP" ENDL) != -1)
- cf = cached_socket;
+ if (_ftp_isconnected(url)) {
+ e = _ftp_cmd(cached_socket, "NOOP");
+ if (e == FTP_OK || e == FTP_SYNTAX_ERROR)
+ cd = cached_socket;
+ }
/* connect to server */
- if (!cf) {
- cf = _ftp_connect(url->host, url->port, url->user, url->pwd, flags);
- if (!cf)
- return NULL;
+ if (cd == -1) {
+ cd = _ftp_connect(url->host, url->port, url->user, url->pwd, flags);
+ if (cd == -1)
+ return -1;
if (cached_socket)
_ftp_disconnect(cached_socket);
- cached_socket = cf;
+ cached_socket = cd;
memcpy(&cached_host, url, sizeof(struct url));
}
- return cf;
+ return cd;
}
/*
@@ -445,14 +467,14 @@ _ftp_cached_connect(struct url *url, char *flags)
FILE *
fetchGetFTP(struct url *url, char *flags)
{
- FILE *cf;
-
+ int cd;
+
/* connect to server */
- if ((cf = _ftp_cached_connect(url, flags)) == NULL)
+ if ((cd = _ftp_cached_connect(url, flags)) == NULL)
return NULL;
/* initiate the transfer */
- return _ftp_transfer(cf, "RETR", url->doc, "r", flags);
+ return _ftp_transfer(cd, "RETR", url->doc, "r", flags);
}
/*
@@ -461,14 +483,14 @@ fetchGetFTP(struct url *url, char *flags)
FILE *
fetchPutFTP(struct url *url, char *flags)
{
- FILE *cf;
+ int cd;
/* connect to server */
- if ((cf = _ftp_cached_connect(url, flags)) == NULL)
+ if ((cd = _ftp_cached_connect(url, flags)) == NULL)
return NULL;
/* initiate the transfer */
- return _ftp_transfer(cf, (flags && strchr(flags, 'a')) ? "APPE" : "STOR",
+ return _ftp_transfer(cd, (flags && strchr(flags, 'a')) ? "APPE" : "STOR",
url->doc, "w", flags);
}
@@ -478,45 +500,44 @@ fetchPutFTP(struct url *url, char *flags)
int
fetchStatFTP(struct url *url, struct url_stat *us, char *flags)
{
- FILE *cf;
char *ln, *s;
struct tm tm;
time_t t;
- int e;
+ int e, cd;
/* connect to server */
- if ((cf = _ftp_cached_connect(url, flags)) == NULL)
+ if ((cd = _ftp_cached_connect(url, flags)) == NULL)
return -1;
/* change directory */
if (((s = strrchr(url->doc, '/')) != NULL) && (s != url->doc)) {
*s = 0;
- if ((e = _ftp_cmd(cf, "CWD %s" ENDL, url->doc)) != FTP_FILE_ACTION_OK) {
+ if ((e = _ftp_cmd(cd, "CWD %s", url->doc)) != FTP_FILE_ACTION_OK) {
*s = '/';
goto ouch;
}
*s++ = '/';
} else {
- if ((e = _ftp_cmd(cf, "CWD /" ENDL)) != FTP_FILE_ACTION_OK)
+ if ((e = _ftp_cmd(cd, "CWD /")) != FTP_FILE_ACTION_OK)
goto ouch;
}
/* s now points to file name */
- if (_ftp_cmd(cf, "SIZE %s" ENDL, s) != FTP_FILE_STATUS)
+ if (_ftp_cmd(cd, "SIZE %s", s) != FTP_FILE_STATUS)
goto ouch;
- for (ln = _ftp_last_reply + 4; *ln && isspace(*ln); ln++)
+ for (ln = last_reply + 4; *ln && isspace(*ln); ln++)
/* nothing */ ;
for (us->size = 0; *ln && isdigit(*ln); ln++)
us->size = us->size * 10 + *ln - '0';
if (*ln && !isspace(*ln)) {
- _ftp_seterr(999); /* XXX should signal a FETCH_PROTO error */
+ _ftp_seterr(999);
return -1;
}
- if ((e = _ftp_cmd(cf, "MDTM %s" ENDL, s)) != FTP_FILE_STATUS)
+ if ((e = _ftp_cmd(cd, "MDTM %s", s)) != FTP_FILE_STATUS)
goto ouch;
- for (ln = _ftp_last_reply + 4; *ln && isspace(*ln); ln++)
+ for (ln = last_reply + 4; *ln && isspace(*ln); ln++)
/* nothing */ ;
t = time(NULL);
us->mtime = localtime(&t)->tm_gmtoff;
@@ -533,7 +554,8 @@ fetchStatFTP(struct url *url, struct url_stat *us, char *flags)
return 0;
ouch:
- _ftp_seterr(e);
+ if (e != -1)
+ _ftp_seterr(e);
return -1;
}
OpenPOWER on IntegriCloud