diff options
author | des <des@FreeBSD.org> | 2002-06-05 12:19:08 +0000 |
---|---|---|
committer | des <des@FreeBSD.org> | 2002-06-05 12:19:08 +0000 |
commit | 0961c455f8e5016fb94b035bc9a95dc3ebf3d391 (patch) | |
tree | 9d2f71cb2f7d26ae6d8b7352243fb47c47f38c15 /lib | |
parent | 02a39226769d736e4560560ca6ad1f323c528744 (diff) | |
download | FreeBSD-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.c | 191 | ||||
-rw-r--r-- | lib/libfetch/common.h | 9 | ||||
-rw-r--r-- | lib/libfetch/ftp.c | 32 | ||||
-rw-r--r-- | lib/libfetch/http.c | 85 |
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; } |