summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authordes <des@FreeBSD.org>2002-06-05 12:19:08 +0000
committerdes <des@FreeBSD.org>2002-06-05 12:19:08 +0000
commit0961c455f8e5016fb94b035bc9a95dc3ebf3d391 (patch)
tree9d2f71cb2f7d26ae6d8b7352243fb47c47f38c15 /lib
parent02a39226769d736e4560560ca6ad1f323c528744 (diff)
downloadFreeBSD-src-0961c455f8e5016fb94b035bc9a95dc3ebf3d391.zip
FreeBSD-src-0961c455f8e5016fb94b035bc9a95dc3ebf3d391.tar.gz
Wrap everything in struct connection, and enforce timeouts everywhere
(except for DNS operations). Always use funopen() for HTTP, to support both timeouts and SSL.
Diffstat (limited to 'lib')
-rw-r--r--lib/libfetch/common.c191
-rw-r--r--lib/libfetch/common.h9
-rw-r--r--lib/libfetch/ftp.c32
-rw-r--r--lib/libfetch/http.c85
4 files changed, 204 insertions, 113 deletions
diff --git a/lib/libfetch/common.c b/lib/libfetch/common.c
index 18b3a09..6f49709 100644
--- a/lib/libfetch/common.c
+++ b/lib/libfetch/common.c
@@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
#include <sys/uio.h>
#include <netinet/in.h>
+#include <ctype.h> /* XXX */
#include <errno.h>
#include <netdb.h>
#include <stdarg.h>
@@ -196,6 +197,22 @@ _fetch_default_proxy_port(const char *scheme)
}
/*
+ * Create a connection for an existing descriptor.
+ */
+conn_t *
+_fetch_reopen(int sd)
+{
+ conn_t *conn;
+
+ /* allocate and fill connection structure */
+ if ((conn = calloc(1, sizeof *conn)) == NULL)
+ return (NULL);
+ conn->sd = sd;
+ return (conn);
+}
+
+
+/*
* Establish a TCP connection to the specified port on the specified host.
*/
conn_t *
@@ -241,55 +258,32 @@ _fetch_connect(const char *host, int port, int af, int verbose)
return (NULL);
}
- /* allocate and fill connection structure */
- if ((conn = calloc(1, sizeof *conn)) == NULL) {
+ if ((conn = _fetch_reopen(sd)) == NULL)
close(sd);
- return (NULL);
- }
- if ((conn->host = strdup(host)) == NULL) {
- free(conn);
- close(sd);
- return (NULL);
- }
- conn->port = port;
- conn->af = af;
- conn->sd = sd;
return (conn);
}
/*
- * Read a line of text from a socket w/ timeout
+ * Read a character from a connection w/ timeout
*/
-#define MIN_BUF_SIZE 1024
-
-int
-_fetch_getln(conn_t *conn)
+ssize_t
+_fetch_read(conn_t *conn, char *buf, size_t len)
{
struct timeval now, timeout, wait;
fd_set readfds;
+ ssize_t rlen, total;
int r;
- char c;
-
- if (conn->buf == NULL) {
- if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
- errno = ENOMEM;
- return (-1);
- }
- conn->bufsize = MIN_BUF_SIZE;
- }
-
- conn->buf[0] = '\0';
- conn->buflen = 0;
if (fetchTimeout) {
+ FD_ZERO(&readfds);
gettimeofday(&timeout, NULL);
timeout.tv_sec += fetchTimeout;
- FD_ZERO(&readfds);
}
- do {
- if (fetchTimeout) {
+ total = 0;
+ while (len > 0) {
+ while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
FD_SET(conn->sd, &readfds);
gettimeofday(&now, NULL);
wait.tv_sec = timeout.tv_sec - now.tv_sec;
@@ -298,34 +292,62 @@ _fetch_getln(conn_t *conn)
wait.tv_usec += 1000000;
wait.tv_sec--;
}
- if (wait.tv_sec < 0) {
- errno = ETIMEDOUT;
- return (-1);
- }
+ if (wait.tv_sec < 0)
+ return (rlen);
+ errno = 0;
r = select(conn->sd + 1, &readfds, NULL, NULL, &wait);
if (r == -1) {
if (errno == EINTR && fetchRestartCalls)
continue;
- /* EBADF or EINVAL: shouldn't happen */
return (-1);
}
- if (!FD_ISSET(conn->sd, &readfds))
- continue;
}
- r = read(conn->sd, &c, 1);
- if (r == 0)
+ if (conn->ssl != NULL)
+ rlen = SSL_read(conn->ssl, buf, len);
+ else
+ rlen = read(conn->sd, buf, len);
+ if (rlen == 0)
break;
- if (r == -1) {
+ if (rlen < 0) {
if (errno == EINTR && fetchRestartCalls)
continue;
- /* any other error is bad news */
return (-1);
}
+ len -= rlen;
+ buf += rlen;
+ total += rlen;
+ }
+ return (total);
+}
+
+/*
+ * Read a line of text from a connection w/ timeout
+ */
+#define MIN_BUF_SIZE 1024
+
+int
+_fetch_getln(conn_t *conn)
+{
+ char *tmp;
+ size_t tmpsize;
+ char c;
+
+ if (conn->buf == NULL) {
+ if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ conn->bufsize = MIN_BUF_SIZE;
+ }
+
+ conn->buf[0] = '\0';
+ conn->buflen = 0;
+
+ do {
+ if (_fetch_read(conn, &c, 1) == -1)
+ return (-1);
conn->buf[conn->buflen++] = c;
if (conn->buflen == conn->bufsize) {
- char *tmp;
- size_t tmpsize;
-
tmp = conn->buf;
tmpsize = conn->bufsize * 2 + 1;
if ((tmp = realloc(tmp, tmpsize)) == NULL) {
@@ -344,25 +366,73 @@ _fetch_getln(conn_t *conn)
/*
- * Write a line of text to a socket w/ timeout
- * XXX currently does not enforce timeout
+ * Write to a connection w/ timeout
+ */
+ssize_t
+_fetch_write(conn_t *conn, const char *buf, size_t len)
+{
+ struct timeval now, timeout, wait;
+ fd_set writefds;
+ ssize_t wlen, total;
+ int r;
+
+ if (fetchTimeout) {
+ FD_ZERO(&writefds);
+ gettimeofday(&timeout, NULL);
+ timeout.tv_sec += fetchTimeout;
+ }
+
+ while (len > 0) {
+ while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
+ FD_SET(conn->sd, &writefds);
+ 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);
+ }
+ errno = 0;
+ r = select(conn->sd + 1, NULL, &writefds, NULL, &wait);
+ if (r == -1) {
+ if (errno == EINTR && fetchRestartCalls)
+ continue;
+ return (-1);
+ }
+ }
+ errno = 0;
+ if (conn->ssl != NULL)
+ wlen = SSL_write(conn->ssl, buf, len);
+ else
+ wlen = write(conn->sd, buf, len);
+ if (wlen == 0)
+ /* we consider a short write a failure */
+ return (-1);
+ if (wlen < 0) {
+ if (errno == EINTR && fetchRestartCalls)
+ continue;
+ return (-1);
+ }
+ len -= wlen;
+ buf += wlen;
+ total += wlen;
+ }
+ return (total);
+}
+
+/*
+ * Write a line of text to a connection w/ timeout
*/
int
_fetch_putln(conn_t *conn, const char *str, size_t len)
{
- struct iovec iov[2];
- ssize_t wlen;
-
- /* XXX should enforce timeout */
- iov[0].iov_base = (char *)str;
- iov[0].iov_len = len;
- iov[1].iov_base = (char *)ENDL;
- iov[1].iov_len = sizeof ENDL;
- len += sizeof ENDL;
- wlen = writev(conn->sd, iov, 2);
- if (wlen < 0 || (size_t)wlen != len)
+ if (_fetch_write(conn, str, len) == -1 ||
+ _fetch_write(conn, ENDL, sizeof ENDL) == -1)
return (-1);
- DEBUG(fprintf(stderr, ">>> %.*s\n", (int)len, str));
return (0);
}
@@ -376,7 +446,6 @@ _fetch_close(conn_t *conn)
int ret;
ret = close(conn->sd);
- free(conn->host);
free(conn);
return (ret);
}
diff --git a/lib/libfetch/common.h b/lib/libfetch/common.h
index 45c90bc..8b2a026 100644
--- a/lib/libfetch/common.h
+++ b/lib/libfetch/common.h
@@ -45,15 +45,13 @@
/* Connection */
typedef struct fetchconn conn_t;
struct fetchconn {
- char *host; /* host name */
- int port; /* port */
- int af; /* address family */
int sd; /* socket descriptor */
char *buf; /* buffer */
size_t bufsize; /* buffer size */
size_t buflen; /* length of buffer contents */
int err; /* last protocol reply code */
- SSL *ssl_ctx; /* SSL context if needed */
+ SSL *ssl; /* SSL handle */
+ SSL_CTX *ssl_ctx; /* SSL context */
X509 *ssl_cert; /* server certificate */
SSL_METHOD *ssl_meth; /* SSL method */
};
@@ -71,7 +69,10 @@ void _fetch_info(const char *, ...);
int _fetch_default_port(const char *);
int _fetch_default_proxy_port(const char *);
conn_t *_fetch_connect(const char *, int, int, int);
+conn_t *_fetch_reopen(int sd);
+ssize_t _fetch_read(conn_t *, char *, size_t);
int _fetch_getln(conn_t *);
+ssize_t _fetch_write(conn_t *, const char *, size_t);
int _fetch_putln(conn_t *, const char *, size_t);
int _fetch_close(conn_t *);
int _fetch_add_entry(struct url_ent **, int *, int *,
diff --git a/lib/libfetch/ftp.c b/lib/libfetch/ftp.c
index 734ec09..7b1bece 100644
--- a/lib/libfetch/ftp.c
+++ b/lib/libfetch/ftp.c
@@ -311,8 +311,8 @@ _ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
* I/O functions for FTP
*/
struct ftpio {
- conn_t *conn; /* Control connection */
- int dsd; /* Data socket descriptor */
+ conn_t *cconn; /* Control connection */
+ conn_t *dconn; /* Data connection */
int dir; /* Direction */
int eof; /* EOF reached */
int err; /* Error code */
@@ -334,7 +334,7 @@ _ftp_readfn(void *v, char *buf, int len)
errno = EBADF;
return (-1);
}
- if (io->conn == NULL || io->dsd == -1 || io->dir == O_WRONLY) {
+ if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) {
errno = EBADF;
return (-1);
}
@@ -344,7 +344,7 @@ _ftp_readfn(void *v, char *buf, int len)
}
if (io->eof)
return (0);
- r = read(io->dsd, buf, len);
+ r = _fetch_read(io->dconn, buf, len);
if (r > 0)
return (r);
if (r == 0) {
@@ -367,7 +367,7 @@ _ftp_writefn(void *v, const char *buf, int len)
errno = EBADF;
return (-1);
}
- if (io->conn == NULL || io->dsd == -1 || io->dir == O_RDONLY) {
+ if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) {
errno = EBADF;
return (-1);
}
@@ -375,7 +375,7 @@ _ftp_writefn(void *v, const char *buf, int len)
errno = io->err;
return (-1);
}
- w = write(io->dsd, buf, len);
+ w = _fetch_write(io->dconn, buf, len);
if (w >= 0)
return (w);
if (errno != EINTR)
@@ -410,30 +410,32 @@ _ftp_closefn(void *v)
}
if (io->dir == -1)
return (0);
- if (io->conn == NULL || io->dsd == -1) {
+ if (io->cconn == NULL || io->dconn == NULL) {
errno = EBADF;
return (-1);
}
- close(io->dsd);
+ _fetch_close(io->dconn);
io->dir = -1;
- io->dsd = -1;
+ io->dconn = NULL;
DEBUG(fprintf(stderr, "Waiting for final status\n"));
- r = _ftp_chkerr(io->conn);
- _fetch_close(io->conn);
+ r = _ftp_chkerr(io->cconn);
+ _fetch_close(io->cconn);
free(io);
return (r == FTP_TRANSFER_COMPLETE) ? 0 : -1;
}
static FILE *
-_ftp_setup(conn_t *conn, int dsd, int mode)
+_ftp_setup(conn_t *cconn, conn_t *dconn, int mode)
{
struct ftpio *io;
FILE *f;
+ if (cconn == NULL || dconn == NULL)
+ return (NULL);
if ((io = malloc(sizeof *io)) == NULL)
return (NULL);
- io->conn = conn;
- io->dsd = dsd;
+ io->cconn = cconn;
+ io->dconn = dconn;
io->dir = mode;
io->eof = io->err = 0;
f = funopen(io, _ftp_readfn, _ftp_writefn, _ftp_seekfn, _ftp_closefn);
@@ -694,7 +696,7 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
sd = d;
}
- if ((df = _ftp_setup(conn, sd, mode)) == NULL)
+ if ((df = _ftp_setup(conn, _fetch_reopen(sd), mode)) == NULL)
goto sysouch;
return (df);
diff --git a/lib/libfetch/http.c b/lib/libfetch/http.c
index b47fdec..8639e00 100644
--- a/lib/libfetch/http.c
+++ b/lib/libfetch/http.c
@@ -107,10 +107,11 @@ __FBSDID("$FreeBSD$");
struct httpio
{
conn_t *conn; /* connection */
+ int chunked; /* chunked mode */
char *buf; /* chunk buffer */
- size_t b_size; /* size of chunk buffer */
- ssize_t b_len; /* amount of data currently in buffer */
- int b_pos; /* current read offset in buffer */
+ size_t bufsize; /* size of chunk buffer */
+ ssize_t buflen; /* amount of data currently in buffer */
+ int bufpos; /* current read offset in buffer */
int eof; /* end-of-file flag */
int error; /* error flag */
size_t chunksize; /* remaining size of current chunk */
@@ -164,16 +165,42 @@ _http_new_chunk(struct httpio *io)
}
/*
+ * Grow the input buffer to at least len bytes
+ */
+static inline int
+_http_growbuf(struct httpio *io, size_t len)
+{
+ char *tmp;
+
+ if (io->bufsize >= len)
+ return (0);
+
+ if ((tmp = realloc(io->buf, len)) == NULL)
+ return (-1);
+ io->buf = tmp;
+ io->bufsize = len;
+}
+
+/*
* Fill the input buffer, do chunk decoding on the fly
*/
static int
-_http_fillbuf(struct httpio *io)
+_http_fillbuf(struct httpio *io, size_t len)
{
if (io->error)
return (-1);
if (io->eof)
return (0);
+ if (io->chunked == 0) {
+ if (_http_growbuf(io, len) == -1)
+ return (-1);
+ if ((io->buflen = _fetch_read(io->conn, io->buf, len)) == -1)
+ return (-1);
+ io->bufpos = 0;
+ return (io->buflen);
+ }
+
if (io->chunksize == 0) {
switch (_http_new_chunk(io)) {
case -1:
@@ -185,31 +212,25 @@ _http_fillbuf(struct httpio *io)
}
}
- if (io->b_size < io->chunksize) {
- char *tmp;
-
- if ((tmp = realloc(io->buf, io->chunksize)) == NULL)
- return (-1);
- io->buf = tmp;
- io->b_size = io->chunksize;
- }
-
- if ((io->b_len = read(io->conn->sd, io->buf, io->chunksize)) == -1)
+ if (len > io->chunksize)
+ len = io->chunksize;
+ if (_http_growbuf(io, len) == -1)
return (-1);
- io->chunksize -= io->b_len;
+ if ((io->buflen = _fetch_read(io->conn, io->buf, len)) == -1)
+ return (-1);
+ io->chunksize -= io->buflen;
if (io->chunksize == 0) {
char endl[2];
- if (read(io->conn->sd, &endl[0], 1) == -1 ||
- read(io->conn->sd, &endl[1], 1) == -1 ||
+ if (_fetch_read(io->conn, endl, 2) != 2 ||
endl[0] != '\r' || endl[1] != '\n')
return (-1);
}
- io->b_pos = 0;
+ io->bufpos = 0;
- return (io->b_len);
+ return (io->buflen);
}
/*
@@ -228,14 +249,14 @@ _http_readfn(void *v, char *buf, int len)
for (pos = 0; len > 0; pos += l, len -= l) {
/* empty buffer */
- if (!io->buf || io->b_pos == io->b_len)
- if (_http_fillbuf(io) < 1)
+ if (!io->buf || io->bufpos == io->buflen)
+ if (_http_fillbuf(io, len) < 1)
break;
- l = io->b_len - io->b_pos;
+ l = io->buflen - io->bufpos;
if (len < l)
l = len;
- bcopy(io->buf + io->b_pos, buf + pos, l);
- io->b_pos += l;
+ bcopy(io->buf + io->bufpos, buf + pos, l);
+ io->bufpos += l;
}
if (!pos && io->error)
@@ -251,7 +272,7 @@ _http_writefn(void *v, const char *buf, int len)
{
struct httpio *io = (struct httpio *)v;
- return (write(io->conn->sd, buf, len));
+ return (_fetch_write(io->conn, buf, len));
}
/*
@@ -274,7 +295,7 @@ _http_closefn(void *v)
* Wrap a file descriptor up
*/
static FILE *
-_http_funopen(conn_t *conn)
+_http_funopen(conn_t *conn, int chunked)
{
struct httpio *io;
FILE *f;
@@ -284,6 +305,7 @@ _http_funopen(conn_t *conn)
return (NULL);
}
io->conn = conn;
+ io->chunked = chunked;
f = funopen(io, _http_readfn, _http_writefn, NULL, _http_closefn);
if (f == NULL) {
_fetch_syserr();
@@ -725,6 +747,9 @@ _http_print_html(FILE *out, FILE *in)
/*
* Send a request and process the reply
+ *
+ * XXX This function is way too long, the do..while loop should be split
+ * XXX off into a separate function.
*/
FILE *
_http_request(struct url *URL, const char *op, struct url_stat *us,
@@ -1020,13 +1045,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
URL->length = clength;
/* wrap it up in a FILE */
- if (chunked) {
- f = _http_funopen(conn);
- } else {
- f = fdopen(dup(conn->sd), "r");
- _fetch_close(conn);
- }
- if (f == NULL) {
+ if ((f = _http_funopen(conn, chunked)) == NULL) {
_fetch_syserr();
goto ouch;
}
OpenPOWER on IntegriCloud