Wrap everything in struct connection, and enforce timeouts everywhere
(except for DNS operations). Always use funopen() for HTTP, to support both timeouts and SSL.
This commit is contained in:
parent
af53575a04
commit
9601e333a8
@ -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>
|
||||
@ -195,6 +196,22 @@ _fetch_default_proxy_port(const char *scheme)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
@ -241,34 +258,78 @@ _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
|
||||
*/
|
||||
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;
|
||||
|
||||
if (fetchTimeout) {
|
||||
FD_ZERO(&readfds);
|
||||
gettimeofday(&timeout, NULL);
|
||||
timeout.tv_sec += 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;
|
||||
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)
|
||||
return (rlen);
|
||||
errno = 0;
|
||||
r = select(conn->sd + 1, &readfds, NULL, NULL, &wait);
|
||||
if (r == -1) {
|
||||
if (errno == EINTR && fetchRestartCalls)
|
||||
continue;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
if (conn->ssl != NULL)
|
||||
rlen = SSL_read(conn->ssl, buf, len);
|
||||
else
|
||||
rlen = read(conn->sd, buf, len);
|
||||
if (rlen == 0)
|
||||
break;
|
||||
if (rlen < 0) {
|
||||
if (errno == EINTR && fetchRestartCalls)
|
||||
continue;
|
||||
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)
|
||||
{
|
||||
struct timeval now, timeout, wait;
|
||||
fd_set readfds;
|
||||
int r;
|
||||
char *tmp;
|
||||
size_t tmpsize;
|
||||
char c;
|
||||
|
||||
if (conn->buf == NULL) {
|
||||
@ -282,50 +343,11 @@ _fetch_getln(conn_t *conn)
|
||||
conn->buf[0] = '\0';
|
||||
conn->buflen = 0;
|
||||
|
||||
if (fetchTimeout) {
|
||||
gettimeofday(&timeout, NULL);
|
||||
timeout.tv_sec += fetchTimeout;
|
||||
FD_ZERO(&readfds);
|
||||
}
|
||||
|
||||
do {
|
||||
if (fetchTimeout) {
|
||||
FD_SET(conn->sd, &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(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)
|
||||
break;
|
||||
if (r == -1) {
|
||||
if (errno == EINTR && fetchRestartCalls)
|
||||
continue;
|
||||
/* any other error is bad news */
|
||||
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);
|
||||
}
|
||||
|
@ -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 *,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
@ -163,17 +164,43 @@ _http_new_chunk(struct httpio *io)
|
||||
return (io->chunksize);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user