First step towards SSL support: wrap connections in a 'struct connection'

which contains the socket descriptor, the input buffer and (yet unused)
SSL state variables.  This has the neat side effect of greatly improving
reentrance (though we're not *quite* there yet) and opening the door to
HTTP connection caching.

This commit is inspired by email conversations with and patches from
Henry Whincup <henry@techiebod.com> last fall.
This commit is contained in:
des 2002-06-05 10:05:03 +00:00
parent 306d84207e
commit e07ac3bab3
4 changed files with 255 additions and 196 deletions

View File

@ -198,9 +198,10 @@ _fetch_default_proxy_port(const char *scheme)
/*
* Establish a TCP connection to the specified port on the specified host.
*/
int
conn_t *
_fetch_connect(const char *host, int port, int af, int verbose)
{
conn_t *conn;
char pbuf[10];
struct addrinfo hints, *res, *res0;
int sd, err;
@ -218,7 +219,7 @@ _fetch_connect(const char *host, int port, int af, int verbose)
hints.ai_protocol = 0;
if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
_netdb_seterr(err);
return (-1);
return (NULL);
}
if (verbose)
@ -237,10 +238,23 @@ _fetch_connect(const char *host, int port, int af, int verbose)
freeaddrinfo(res0);
if (sd == -1) {
_fetch_syserr();
return (-1);
return (NULL);
}
return (sd);
/* allocate and fill connection structure */
if ((conn = calloc(1, sizeof *conn)) == 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);
}
@ -250,23 +264,23 @@ _fetch_connect(const char *host, int port, int af, int verbose)
#define MIN_BUF_SIZE 1024
int
_fetch_getln(int fd, char **buf, size_t *size, size_t *len)
_fetch_getln(conn_t *conn)
{
struct timeval now, timeout, wait;
fd_set readfds;
int r;
char c;
if (*buf == NULL) {
if ((*buf = malloc(MIN_BUF_SIZE)) == NULL) {
if (conn->buf == NULL) {
if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
errno = ENOMEM;
return (-1);
}
*size = MIN_BUF_SIZE;
conn->bufsize = MIN_BUF_SIZE;
}
**buf = '\0';
*len = 0;
conn->buf[0] = '\0';
conn->buflen = 0;
if (fetchTimeout) {
gettimeofday(&timeout, NULL);
@ -276,7 +290,7 @@ _fetch_getln(int fd, char **buf, size_t *size, size_t *len)
do {
if (fetchTimeout) {
FD_SET(fd, &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;
@ -288,17 +302,17 @@ _fetch_getln(int fd, char **buf, size_t *size, size_t *len)
errno = ETIMEDOUT;
return (-1);
}
r = select(fd+1, &readfds, NULL, NULL, &wait);
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(fd, &readfds))
if (!FD_ISSET(conn->sd, &readfds))
continue;
}
r = read(fd, &c, 1);
r = read(conn->sd, &c, 1);
if (r == 0)
break;
if (r == -1) {
@ -307,21 +321,24 @@ _fetch_getln(int fd, char **buf, size_t *size, size_t *len)
/* any other error is bad news */
return (-1);
}
(*buf)[*len] = c;
*len += 1;
if (*len == *size) {
conn->buf[conn->buflen++] = c;
if (conn->buflen == conn->bufsize) {
char *tmp;
size_t tmpsize;
if ((tmp = realloc(*buf, *size * 2 + 1)) == NULL) {
tmp = conn->buf;
tmpsize = conn->bufsize * 2 + 1;
if ((tmp = realloc(tmp, tmpsize)) == NULL) {
errno = ENOMEM;
return (-1);
}
*buf = tmp;
*size = *size * 2 + 1;
conn->buf = tmp;
conn->bufsize = tmpsize;
}
} while (c != '\n');
DEBUG(fprintf(stderr, "<<< %.*s", (int)*len, *buf));
conn->buf[conn->buflen] = '\0';
DEBUG(fprintf(stderr, "<<< %s", conn->buf));
return (0);
}
@ -331,7 +348,7 @@ _fetch_getln(int fd, char **buf, size_t *size, size_t *len)
* XXX currently does not enforce timeout
*/
int
_fetch_putln(int fd, const char *str, size_t len)
_fetch_putln(conn_t *conn, const char *str, size_t len)
{
struct iovec iov[2];
ssize_t wlen;
@ -342,14 +359,29 @@ _fetch_putln(int fd, const char *str, size_t len)
iov[1].iov_base = (char *)ENDL;
iov[1].iov_len = sizeof ENDL;
len += sizeof ENDL;
wlen = writev(fd, iov, 2);
wlen = writev(conn->sd, iov, 2);
if (wlen < 0 || (size_t)wlen != len)
return (-1);
DEBUG(fprintf(stderr, ">>> %s\n", str));
DEBUG(fprintf(stderr, ">>> %.*s\n", (int)len, str));
return (0);
}
/*
* Close connection
*/
int
_fetch_close(conn_t *conn)
{
int ret;
ret = close(conn->sd);
free(conn->host);
free(conn);
return (ret);
}
/*** Directory-related utility functions *************************************/
int

View File

@ -36,6 +36,28 @@
#define FTP_DEFAULT_PROXY_PORT 21
#define HTTP_DEFAULT_PROXY_PORT 3128
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
/* 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 */
X509 *ssl_cert; /* server certificate */
SSL_METHOD *ssl_meth; /* SSL method */
};
/* Structure used for error message lists */
struct fetcherr {
const int num;
@ -48,9 +70,10 @@ void _fetch_syserr(void);
void _fetch_info(const char *, ...);
int _fetch_default_port(const char *);
int _fetch_default_proxy_port(const char *);
int _fetch_connect(const char *, int, int, int);
int _fetch_getln(int, char **, size_t *, size_t *);
int _fetch_putln(int, const char *, size_t);
conn_t *_fetch_connect(const char *, int, int, int);
int _fetch_getln(conn_t *);
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 *);

View File

@ -66,6 +66,7 @@ __FBSDID("$FreeBSD$");
#include <fcntl.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -96,12 +97,7 @@ __FBSDID("$FreeBSD$");
#define FTP_PROTOCOL_ERROR 999
static struct url cached_host;
static int cached_socket;
static char *last_reply;
static size_t lr_size;
static size_t lr_length;
static int last_code;
static conn_t *cached_connection;
#define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
&& isdigit(foo[2]) \
@ -136,43 +132,42 @@ unmappedaddr(struct sockaddr_in6 *sin6)
* Get server response
*/
static int
_ftp_chkerr(int cd)
_ftp_chkerr(conn_t *conn)
{
if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) {
if (_fetch_getln(conn) == -1) {
_fetch_syserr();
return (-1);
}
if (isftpinfo(last_reply)) {
while (lr_length && !isftpreply(last_reply)) {
if (_fetch_getln(cd, &last_reply,
&lr_size, &lr_length) == -1) {
if (isftpinfo(conn->buf)) {
while (conn->buflen && !isftpreply(conn->buf)) {
if (_fetch_getln(conn) == -1) {
_fetch_syserr();
return (-1);
}
}
}
while (lr_length && isspace(last_reply[lr_length-1]))
lr_length--;
last_reply[lr_length] = 0;
while (conn->buflen && isspace(conn->buf[conn->buflen - 1]))
conn->buflen--;
conn->buf[conn->buflen] = '\0';
if (!isftpreply(last_reply)) {
if (!isftpreply(conn->buf)) {
_ftp_seterr(FTP_PROTOCOL_ERROR);
return (-1);
}
last_code = (last_reply[0] - '0') * 100
+ (last_reply[1] - '0') * 10
+ (last_reply[2] - '0');
conn->err = (conn->buf[0] - '0') * 100
+ (conn->buf[1] - '0') * 10
+ (conn->buf[2] - '0');
return (last_code);
return (conn->err);
}
/*
* Send a command and check reply
*/
static int
_ftp_cmd(int cd, const char *fmt, ...)
_ftp_cmd(conn_t *conn, const char *fmt, ...)
{
va_list ap;
size_t len;
@ -189,7 +184,7 @@ _ftp_cmd(int cd, const char *fmt, ...)
return (-1);
}
r = _fetch_putln(cd, msg, len);
r = _fetch_putln(conn, msg, len);
free(msg);
if (r == -1) {
@ -197,7 +192,7 @@ _ftp_cmd(int cd, const char *fmt, ...)
return (-1);
}
return (_ftp_chkerr(cd));
return (_ftp_chkerr(conn));
}
/*
@ -219,15 +214,15 @@ _ftp_filename(const char *file)
* file.
*/
static int
_ftp_cwd(int cd, const char *file)
_ftp_cwd(conn_t *conn, const char *file)
{
char *s;
int e;
if ((s = strrchr(file, '/')) == NULL || s == file) {
e = _ftp_cmd(cd, "CWD /");
e = _ftp_cmd(conn, "CWD /");
} else {
e = _ftp_cmd(cd, "CWD %.*s", s - file, file);
e = _ftp_cmd(conn, "CWD %.*s", s - file, file);
}
if (e != FTP_FILE_ACTION_OK) {
_ftp_seterr(e);
@ -240,7 +235,7 @@ _ftp_cwd(int cd, const char *file)
* Request and parse file stats
*/
static int
_ftp_stat(int cd, const char *file, struct url_stat *us)
_ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
{
char *ln;
const char *s;
@ -256,11 +251,11 @@ _ftp_stat(int cd, const char *file, struct url_stat *us)
else
++s;
if ((e = _ftp_cmd(cd, "SIZE %s", s)) != FTP_FILE_STATUS) {
if ((e = _ftp_cmd(conn, "SIZE %s", s)) != FTP_FILE_STATUS) {
_ftp_seterr(e);
return (-1);
}
for (ln = last_reply + 4; *ln && isspace(*ln); ln++)
for (ln = conn->buf + 4; *ln && isspace(*ln); ln++)
/* nothing */ ;
for (us->size = 0; *ln && isdigit(*ln); ln++)
us->size = us->size * 10 + *ln - '0';
@ -273,11 +268,11 @@ _ftp_stat(int cd, const char *file, struct url_stat *us)
us->size = -1;
DEBUG(fprintf(stderr, "size: [%lld]\n", (long long)us->size));
if ((e = _ftp_cmd(cd, "MDTM %s", s)) != FTP_FILE_STATUS) {
if ((e = _ftp_cmd(conn, "MDTM %s", s)) != FTP_FILE_STATUS) {
_ftp_seterr(e);
return (-1);
}
for (ln = last_reply + 4; *ln && isspace(*ln); ln++)
for (ln = conn->buf + 4; *ln && isspace(*ln); ln++)
/* nothing */ ;
switch (strspn(ln, "0123456789")) {
case 14:
@ -316,7 +311,7 @@ _ftp_stat(int cd, const char *file, struct url_stat *us)
* I/O functions for FTP
*/
struct ftpio {
int csd; /* Control socket descriptor */
conn_t *conn; /* Control connection */
int dsd; /* Data socket descriptor */
int dir; /* Direction */
int eof; /* EOF reached */
@ -339,7 +334,7 @@ _ftp_readfn(void *v, char *buf, int len)
errno = EBADF;
return (-1);
}
if (io->csd == -1 || io->dsd == -1 || io->dir == O_WRONLY) {
if (io->conn == NULL || io->dsd == -1 || io->dir == O_WRONLY) {
errno = EBADF;
return (-1);
}
@ -372,7 +367,7 @@ _ftp_writefn(void *v, const char *buf, int len)
errno = EBADF;
return (-1);
}
if (io->csd == -1 || io->dsd == -1 || io->dir == O_RDONLY) {
if (io->conn == NULL || io->dsd == -1 || io->dir == O_RDONLY) {
errno = EBADF;
return (-1);
}
@ -415,7 +410,7 @@ _ftp_closefn(void *v)
}
if (io->dir == -1)
return (0);
if (io->csd == -1 || io->dsd == -1) {
if (io->conn == NULL || io->dsd == -1) {
errno = EBADF;
return (-1);
}
@ -423,21 +418,21 @@ _ftp_closefn(void *v)
io->dir = -1;
io->dsd = -1;
DEBUG(fprintf(stderr, "Waiting for final status\n"));
r = _ftp_chkerr(io->csd);
close(io->csd);
r = _ftp_chkerr(io->conn);
_fetch_close(io->conn);
free(io);
return (r == FTP_TRANSFER_COMPLETE) ? 0 : -1;
}
static FILE *
_ftp_setup(int csd, int dsd, int mode)
_ftp_setup(conn_t *conn, int dsd, int mode)
{
struct ftpio *io;
FILE *f;
if ((io = malloc(sizeof *io)) == NULL)
return (NULL);
io->csd = dup(csd);
io->conn = conn;
io->dsd = dsd;
io->dir = mode;
io->eof = io->err = 0;
@ -451,7 +446,7 @@ _ftp_setup(int csd, int dsd, int mode)
* Transfer file
*/
static FILE *
_ftp_transfer(int cd, const char *oper, const char *file,
_ftp_transfer(conn_t *conn, const char *oper, const char *file,
int mode, off_t offset, const char *flags)
{
struct sockaddr_storage sa;
@ -475,7 +470,7 @@ _ftp_transfer(int cd, const char *oper, const char *file,
/* find our own address, bind, and listen */
l = sizeof sa;
if (getsockname(cd, (struct sockaddr *)&sa, &l) == -1)
if (getsockname(conn->sd, (struct sockaddr *)&sa, &l) == -1)
goto sysouch;
if (sa.ss_family == AF_INET6)
unmappedaddr((struct sockaddr_in6 *)&sa);
@ -497,14 +492,15 @@ _ftp_transfer(int cd, const char *oper, const char *file,
_fetch_info("setting passive mode");
switch (sa.ss_family) {
case AF_INET:
if ((e = _ftp_cmd(cd, "PASV")) != FTP_PASSIVE_MODE)
if ((e = _ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE)
goto ouch;
break;
case AF_INET6:
if ((e = _ftp_cmd(cd, "EPSV")) != FTP_EPASSIVE_MODE) {
if ((e = _ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) {
if (e == -1)
goto ouch;
if ((e = _ftp_cmd(cd, "LPSV")) != FTP_LPASSIVE_MODE)
if ((e = _ftp_cmd(conn, "LPSV")) !=
FTP_LPASSIVE_MODE)
goto ouch;
}
break;
@ -517,7 +513,7 @@ _ftp_transfer(int cd, const char *oper, const char *file,
* Find address and port number. The reply to the PASV command
* is IMHO the one and only weak point in the FTP protocol.
*/
ln = last_reply;
ln = conn->buf;
switch (e) {
case FTP_PASSIVE_MODE:
case FTP_LPASSIVE_MODE:
@ -555,12 +551,12 @@ _ftp_transfer(int cd, const char *oper, const char *file,
/* seek to required offset */
if (offset)
if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK)
if (_ftp_cmd(conn, "REST %lu", (u_long)offset) != FTP_FILE_OK)
goto sysouch;
/* construct sockaddr for data socket */
l = sizeof sa;
if (getpeername(cd, (struct sockaddr *)&sa, &l) == -1)
if (getpeername(conn->sd, (struct sockaddr *)&sa, &l) == -1)
goto sysouch;
if (sa.ss_family == AF_INET6)
unmappedaddr((struct sockaddr_in6 *)&sa);
@ -597,7 +593,7 @@ _ftp_transfer(int cd, const char *oper, const char *file,
/* make the server initiate the transfer */
if (verbose)
_fetch_info("initiating transfer");
e = _ftp_cmd(cd, "%s %s", oper, _ftp_filename(file));
e = _ftp_cmd(conn, "%s %s", oper, _ftp_filename(file));
if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
goto ouch;
@ -641,7 +637,7 @@ _ftp_transfer(int cd, const char *oper, const char *file,
sin4 = (struct sockaddr_in *)&sa;
a = ntohl(sin4->sin_addr.s_addr);
p = ntohs(sin4->sin_port);
e = _ftp_cmd(cd, "PORT %d,%d,%d,%d,%d,%d",
e = _ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d",
(a >> 24) & 0xff, (a >> 16) & 0xff,
(a >> 8) & 0xff, a & 0xff,
(p >> 8) & 0xff, p & 0xff);
@ -653,14 +649,14 @@ _ftp_transfer(int cd, const char *oper, const char *file,
if (getnameinfo((struct sockaddr *)&sa, sa.ss_len,
hname, sizeof(hname),
NULL, 0, NI_NUMERICHOST) == 0) {
e = _ftp_cmd(cd, "EPRT |%d|%s|%d|", 2, hname,
e = _ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname,
htons(sin6->sin6_port));
if (e == -1)
goto ouch;
}
if (e != FTP_OK) {
ap = (char *)&sin6->sin6_addr;
e = _ftp_cmd(cd,
e = _ftp_cmd(conn,
"LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
6, 16,
UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]),
@ -681,13 +677,13 @@ _ftp_transfer(int cd, const char *oper, const char *file,
/* seek to required offset */
if (offset)
if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK)
if (_ftp_cmd(conn, "REST %ju", (uintmax_t)offset) != FTP_FILE_OK)
goto sysouch;
/* make the server initiate the transfer */
if (verbose)
_fetch_info("initiating transfer");
e = _ftp_cmd(cd, "%s %s", oper, _ftp_filename(file));
e = _ftp_cmd(conn, "%s %s", oper, _ftp_filename(file));
if (e != FTP_OPEN_DATA_CONNECTION)
goto ouch;
@ -698,7 +694,7 @@ _ftp_transfer(int cd, const char *oper, const char *file,
sd = d;
}
if ((df = _ftp_setup(cd, sd, mode)) == NULL)
if ((df = _ftp_setup(conn, sd, mode)) == NULL)
goto sysouch;
return (df);
@ -720,7 +716,7 @@ _ftp_transfer(int cd, const char *oper, const char *file,
* Authenticate
*/
static int
_ftp_authenticate(int cd, struct url *url, struct url *purl)
_ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)
{
const char *user, *pwd, *logname;
char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1];
@ -735,11 +731,11 @@ _ftp_authenticate(int cd, struct url *url, struct url *purl)
if (!user || !*user)
user = FTP_ANONYMOUS_USER;
if (purl && url->port == _fetch_default_port(url->scheme))
e = _ftp_cmd(cd, "USER %s@%s", user, url->host);
e = _ftp_cmd(conn, "USER %s@%s", user, url->host);
else if (purl)
e = _ftp_cmd(cd, "USER %s@%s@%d", user, url->host, url->port);
e = _ftp_cmd(conn, "USER %s@%s@%d", user, url->host, url->port);
else
e = _ftp_cmd(cd, "USER %s", user);
e = _ftp_cmd(conn, "USER %s", user);
/* did the server request a password? */
if (e == FTP_NEED_PASSWORD) {
@ -756,7 +752,7 @@ _ftp_authenticate(int cd, struct url *url, struct url *purl)
gethostname(pbuf + len, sizeof pbuf - len);
pwd = pbuf;
}
e = _ftp_cmd(cd, "PASS %s", pwd);
e = _ftp_cmd(conn, "PASS %s", pwd);
}
return (e);
@ -765,10 +761,11 @@ _ftp_authenticate(int cd, struct url *url, struct url *purl)
/*
* Log on to FTP server
*/
static int
static conn_t *
_ftp_connect(struct url *url, struct url *purl, const char *flags)
{
int cd, e, direct, verbose;
conn_t *conn;
int e, direct, verbose;
#ifdef INET6
int af = AF_UNSPEC;
#else
@ -788,42 +785,42 @@ _ftp_connect(struct url *url, struct url *purl, const char *flags)
/* check for proxy */
if (purl) {
/* XXX proxy authentication! */
cd = _fetch_connect(purl->host, purl->port, af, verbose);
conn = _fetch_connect(purl->host, purl->port, af, verbose);
} else {
/* no proxy, go straight to target */
cd = _fetch_connect(url->host, url->port, af, verbose);
conn = _fetch_connect(url->host, url->port, af, verbose);
purl = NULL;
}
/* check connection */
if (cd == -1) {
if (conn == NULL) {
_fetch_syserr();
return (NULL);
}
/* expect welcome message */
if ((e = _ftp_chkerr(cd)) != FTP_SERVICE_READY)
if ((e = _ftp_chkerr(conn)) != FTP_SERVICE_READY)
goto fouch;
/* authenticate */
if ((e = _ftp_authenticate(cd, url, purl)) != FTP_LOGGED_IN)
if ((e = _ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN)
goto fouch;
/* might as well select mode and type at once */
#ifdef FTP_FORCE_STREAM_MODE
if ((e = _ftp_cmd(cd, "MODE S")) != FTP_OK) /* default is S */
if ((e = _ftp_cmd(conn, "MODE S")) != FTP_OK) /* default is S */
goto fouch;
#endif
if ((e = _ftp_cmd(cd, "TYPE I")) != FTP_OK) /* default is A */
if ((e = _ftp_cmd(conn, "TYPE I")) != FTP_OK) /* default is A */
goto fouch;
/* done */
return (cd);
return (conn);
fouch:
if (e != -1)
_ftp_seterr(e);
close(cd);
_fetch_close(conn);
return (NULL);
}
@ -831,10 +828,10 @@ _ftp_connect(struct url *url, struct url *purl, const char *flags)
* Disconnect from server
*/
static void
_ftp_disconnect(int cd)
_ftp_disconnect(conn_t *conn)
{
(void)_ftp_cmd(cd, "QUIT");
close(cd);
(void)_ftp_cmd(conn, "QUIT");
_fetch_close(conn);
}
/*
@ -843,7 +840,7 @@ _ftp_disconnect(int cd)
static int
_ftp_isconnected(struct url *url)
{
return (cached_socket
return (cached_connection
&& (strcmp(url->host, cached_host.host) == 0)
&& (strcmp(url->user, cached_host.user) == 0)
&& (strcmp(url->pwd, cached_host.pwd) == 0)
@ -853,12 +850,11 @@ _ftp_isconnected(struct url *url)
/*
* Check the cache, reconnect if no luck
*/
static int
static conn_t *
_ftp_cached_connect(struct url *url, struct url *purl, const char *flags)
{
int e, cd;
cd = -1;
conn_t *conn;
int e;
/* set default port */
if (!url->port)
@ -866,19 +862,19 @@ _ftp_cached_connect(struct url *url, struct url *purl, const char *flags)
/* try to use previously cached connection */
if (_ftp_isconnected(url)) {
e = _ftp_cmd(cached_socket, "NOOP");
e = _ftp_cmd(cached_connection, "NOOP");
if (e == FTP_OK || e == FTP_SYNTAX_ERROR)
return (cached_socket);
return (cached_connection);
}
/* connect to server */
if ((cd = _ftp_connect(url, purl, flags)) == -1)
return (-1);
if (cached_socket)
_ftp_disconnect(cached_socket);
cached_socket = cd;
if ((conn = _ftp_connect(url, purl, flags)) == NULL)
return (NULL);
if (cached_connection)
_ftp_disconnect(cached_connection);
cached_connection = conn;
memcpy(&cached_host, url, sizeof *url);
return (cd);
return (conn);
}
/*
@ -916,7 +912,8 @@ FILE *
_ftp_request(struct url *url, const char *op, struct url_stat *us,
struct url *purl, const char *flags)
{
int cd, oflag;
conn_t *conn;
int oflag;
/* check if we should use HTTP instead */
if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) {
@ -931,18 +928,18 @@ _ftp_request(struct url *url, const char *op, struct url_stat *us,
}
/* connect to server */
cd = _ftp_cached_connect(url, purl, flags);
conn = _ftp_cached_connect(url, purl, flags);
if (purl)
fetchFreeURL(purl);
if (cd == NULL)
if (conn == NULL)
return (NULL);
/* change directory */
if (_ftp_cwd(cd, url->doc) == -1)
if (_ftp_cwd(conn, url->doc) == -1)
return (NULL);
/* stat file */
if (us && _ftp_stat(cd, url->doc, us) == -1
if (us && _ftp_stat(conn, url->doc, us) == -1
&& fetchLastErrCode != FETCH_PROTO
&& fetchLastErrCode != FETCH_UNAVAIL)
return (NULL);
@ -956,7 +953,7 @@ _ftp_request(struct url *url, const char *op, struct url_stat *us,
oflag = O_RDONLY;
/* initiate the transfer */
return (_ftp_transfer(cd, op, url->doc, oflag, url->offset, flags));
return (_ftp_transfer(conn, op, url->doc, oflag, url->offset, flags));
}
/*

View File

@ -106,7 +106,7 @@ __FBSDID("$FreeBSD$");
struct cookie
{
int fd;
conn_t *conn;
char *buf;
size_t b_size;
ssize_t b_len;
@ -127,13 +127,13 @@ _http_new_chunk(struct cookie *c)
{
char *p;
if (_fetch_getln(c->fd, &c->buf, &c->b_size, &c->b_len) == -1)
if (_fetch_getln(c->conn) == -1)
return (-1);
if (c->b_len < 2 || !ishexnumber(*c->buf))
if (c->b_len < 2 || !ishexnumber(*c->conn->buf))
return (-1);
for (p = c->buf; !isspace(*p) && p < c->buf + c->b_len; ++p) {
for (p = c->conn->buf; *p && !isspace(*p); ++p) {
if (*p == ';')
break;
if (!ishexnumber(*p))
@ -194,14 +194,16 @@ _http_fillbuf(struct cookie *c)
c->b_size = c->chunksize;
}
if ((c->b_len = read(c->fd, c->buf, c->chunksize)) == -1)
if ((c->b_len = read(c->conn->sd, c->buf, c->chunksize)) == -1)
return (-1);
c->chunksize -= c->b_len;
if (c->chunksize == 0) {
char endl;
if (read(c->fd, &endl, 1) == -1 ||
read(c->fd, &endl, 1) == -1)
char endl[2];
if (read(c->conn->sd, &endl[0], 1) == -1 ||
read(c->conn->sd, &endl[1], 1) == -1 ||
endl[0] != '\r' || endl[1] != '\n')
return (-1);
}
@ -249,7 +251,7 @@ _http_writefn(void *v, const char *buf, int len)
{
struct cookie *c = (struct cookie *)v;
return (write(c->fd, buf, len));
return (write(c->conn->sd, buf, len));
}
/*
@ -261,7 +263,7 @@ _http_closefn(void *v)
struct cookie *c = (struct cookie *)v;
int r;
r = close(c->fd);
r = _fetch_close(c->conn);
if (c->buf)
free(c->buf);
free(c);
@ -272,7 +274,7 @@ _http_closefn(void *v)
* Wrap a file descriptor up
*/
static FILE *
_http_funopen(int fd)
_http_funopen(conn_t *conn)
{
struct cookie *c;
FILE *f;
@ -281,7 +283,7 @@ _http_funopen(int fd)
_fetch_syserr();
return (NULL);
}
c->fd = fd;
c->conn = conn;
f = funopen(c, _http_readfn, _http_writefn, NULL, _http_closefn);
if (f == NULL) {
_fetch_syserr();
@ -324,15 +326,11 @@ static struct {
{ hdr_unknown, NULL },
};
static char *reply_buf;
static size_t reply_size;
static size_t reply_length;
/*
* Send a formatted line; optionally echo to terminal
*/
static int
_http_cmd(int fd, const char *fmt, ...)
_http_cmd(conn_t *conn, const char *fmt, ...)
{
va_list ap;
size_t len;
@ -349,7 +347,7 @@ _http_cmd(int fd, const char *fmt, ...)
return (-1);
}
r = _fetch_putln(fd, msg, len);
r = _fetch_putln(conn, msg, len);
free(msg);
if (r == -1) {
@ -364,11 +362,11 @@ _http_cmd(int fd, const char *fmt, ...)
* Get and parse status line
*/
static int
_http_get_reply(int fd)
_http_get_reply(conn_t *conn)
{
char *p;
if (_fetch_getln(fd, &reply_buf, &reply_size, &reply_length) == -1)
if (_fetch_getln(conn) == -1)
return (-1);
/*
* A valid status line looks like "HTTP/m.n xyz reason" where m
@ -379,9 +377,9 @@ _http_get_reply(int fd)
* on finding one, but if we do, insist on it being 1.0 or 1.1.
* We don't care about the reason phrase.
*/
if (strncmp(reply_buf, "HTTP", 4) != 0)
if (strncmp(conn->buf, "HTTP", 4) != 0)
return (HTTP_PROTOCOL_ERROR);
p = reply_buf + 4;
p = conn->buf + 4;
if (*p == '/') {
if (p[1] != '1' || p[2] != '.' || (p[3] != '0' && p[3] != '1'))
return (HTTP_PROTOCOL_ERROR);
@ -390,7 +388,8 @@ _http_get_reply(int fd)
if (*p != ' ' || !isdigit(p[1]) || !isdigit(p[2]) || !isdigit(p[3]))
return (HTTP_PROTOCOL_ERROR);
return ((p[1] - '0') * 100 + (p[2] - '0') * 10 + (p[3] - '0'));
conn->err = (p[1] - '0') * 100 + (p[2] - '0') * 10 + (p[3] - '0');
return (conn->err);
}
/*
@ -413,17 +412,17 @@ _http_match(const char *str, const char *hdr)
* Get the next header and return the appropriate symbolic code.
*/
static hdr_t
_http_next_header(int fd, const char **p)
_http_next_header(conn_t *conn, const char **p)
{
int i;
if (_fetch_getln(fd, &reply_buf, &reply_size, &reply_length) == -1)
if (_fetch_getln(conn) == -1)
return (hdr_syserror);
while (reply_length && isspace(reply_buf[reply_length-1]))
reply_length--;
reply_buf[reply_length] = 0;
if (reply_length == 0)
return (hdr_end);
while (conn->buflen && isspace(conn->buf[conn->buflen - 1]))
conn->buflen--;
conn->buf[conn->buflen] = '\0';
if (conn->buflen == 0)
return (hdr_end);
/*
* We could check for malformed headers but we don't really care.
* A valid header starts with a token immediately followed by a
@ -431,7 +430,7 @@ _http_next_header(int fd, const char **p)
* characters except "()<>@,;:\\\"{}".
*/
for (i = 0; hdr_names[i].num != hdr_unknown; i++)
if ((*p = _http_match(hdr_names[i].name, reply_buf)) != NULL)
if ((*p = _http_match(hdr_names[i].name, conn->buf)) != NULL)
return (hdr_names[i].num);
return (hdr_unknown);
}
@ -573,7 +572,7 @@ _http_base64(const char *src)
* Encode username and password
*/
static int
_http_basic_auth(int fd, const char *hdr, const char *usr, const char *pwd)
_http_basic_auth(conn_t *conn, const char *hdr, const char *usr, const char *pwd)
{
char *upw, *auth;
int r;
@ -586,7 +585,7 @@ _http_basic_auth(int fd, const char *hdr, const char *usr, const char *pwd)
free(upw);
if (auth == NULL)
return (-1);
r = _http_cmd(fd, "%s: Basic %s", hdr, auth);
r = _http_cmd(conn, "%s: Basic %s", hdr, auth);
free(auth);
return (r);
}
@ -595,7 +594,7 @@ _http_basic_auth(int fd, const char *hdr, const char *usr, const char *pwd)
* Send an authorization header
*/
static int
_http_authorize(int fd, const char *hdr, const char *p)
_http_authorize(conn_t *conn, const char *hdr, const char *p)
{
/* basic authorization */
if (strncasecmp(p, "basic:", 6) == 0) {
@ -612,7 +611,7 @@ _http_authorize(int fd, const char *hdr, const char *p)
user = str;
pwd = strchr(str, ':');
*pwd++ = '\0';
r = _http_basic_auth(fd, hdr, user, pwd);
r = _http_basic_auth(conn, hdr, user, pwd);
free(str);
return (r);
}
@ -627,11 +626,12 @@ _http_authorize(int fd, const char *hdr, const char *p)
/*
* Connect to the correct HTTP server or proxy.
*/
static int
static conn_t *
_http_connect(struct url *URL, struct url *purl, const char *flags)
{
conn_t *conn;
int verbose;
int af, fd;
int af;
#ifdef INET6
af = AF_UNSPEC;
@ -652,13 +652,13 @@ _http_connect(struct url *URL, struct url *purl, const char *flags)
} else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) {
/* can't talk http to an ftp server */
/* XXX should set an error code */
return (-1);
return (NULL);
}
if ((fd = _fetch_connect(URL->host, URL->port, af, verbose)) == -1)
if ((conn = _fetch_connect(URL->host, URL->port, af, verbose)) == NULL)
/* _fetch_connect() has already set an error code */
return (-1);
return (fd);
return (NULL);
return (conn);
}
static struct url *
@ -730,9 +730,10 @@ FILE *
_http_request(struct url *URL, const char *op, struct url_stat *us,
struct url *purl, const char *flags)
{
conn_t *conn;
struct url *url, *new;
int chunked, direct, need_auth, noredirect, verbose;
int code, fd, i, n;
int i, n;
off_t offset, clength, length, size;
time_t mtime;
const char *p;
@ -782,7 +783,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
}
/* connect to server or proxy */
if ((fd = _http_connect(url, purl, flags)) == -1)
if ((conn = _http_connect(url, purl, flags)) == NULL)
goto ouch;
host = url->host;
@ -798,36 +799,36 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
_fetch_info("requesting %s://%s:%d%s",
url->scheme, host, url->port, url->doc);
if (purl) {
_http_cmd(fd, "%s %s://%s:%d%s HTTP/1.1",
_http_cmd(conn, "%s %s://%s:%d%s HTTP/1.1",
op, url->scheme, host, url->port, url->doc);
} else {
_http_cmd(fd, "%s %s HTTP/1.1",
_http_cmd(conn, "%s %s HTTP/1.1",
op, url->doc);
}
/* virtual host */
if (url->port == _fetch_default_port(url->scheme))
_http_cmd(fd, "Host: %s", host);
_http_cmd(conn, "Host: %s", host);
else
_http_cmd(fd, "Host: %s:%d", host, url->port);
_http_cmd(conn, "Host: %s:%d", host, url->port);
/* proxy authorization */
if (purl) {
if (*purl->user || *purl->pwd)
_http_basic_auth(fd, "Proxy-Authorization",
_http_basic_auth(conn, "Proxy-Authorization",
purl->user, purl->pwd);
else if ((p = getenv("HTTP_PROXY_AUTH")) != NULL && *p != '\0')
_http_authorize(fd, "Proxy-Authorization", p);
_http_authorize(conn, "Proxy-Authorization", p);
}
/* server authorization */
if (need_auth || *url->user || *url->pwd) {
if (*url->user || *url->pwd)
_http_basic_auth(fd, "Authorization", url->user, url->pwd);
_http_basic_auth(conn, "Authorization", url->user, url->pwd);
else if ((p = getenv("HTTP_AUTH")) != NULL && *p != '\0')
_http_authorize(fd, "Authorization", p);
_http_authorize(conn, "Authorization", p);
else if (fetchAuthMethod && fetchAuthMethod(url) == 0) {
_http_basic_auth(fd, "Authorization", url->user, url->pwd);
_http_basic_auth(conn, "Authorization", url->user, url->pwd);
} else {
_http_seterr(HTTP_NEED_AUTH);
goto ouch;
@ -836,16 +837,16 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
/* other headers */
if ((p = getenv("HTTP_USER_AGENT")) != NULL && *p != '\0')
_http_cmd(fd, "User-Agent: %s", p);
_http_cmd(conn, "User-Agent: %s", p);
else
_http_cmd(fd, "User-Agent: %s " _LIBFETCH_VER, _getprogname());
_http_cmd(conn, "User-Agent: %s " _LIBFETCH_VER, getprogname());
if (url->offset)
_http_cmd(fd, "Range: bytes=%lld-", (long long)url->offset);
_http_cmd(fd, "Connection: close");
_http_cmd(fd, "");
_http_cmd(conn, "Range: bytes=%lld-", (long long)url->offset);
_http_cmd(conn, "Connection: close");
_http_cmd(conn, "");
/* get reply */
switch ((code = _http_get_reply(fd))) {
switch (_http_get_reply(conn)) {
case HTTP_OK:
case HTTP_PARTIAL:
/* fine */
@ -864,7 +865,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
* We already sent out authorization code, so there's
* nothing more we can do.
*/
_http_seterr(code);
_http_seterr(conn->err);
goto ouch;
}
/* try again, but send the password this time */
@ -876,7 +877,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
* If we're talking to a proxy, we already sent our proxy
* authorization code, so there's nothing more we can do.
*/
_http_seterr(code);
_http_seterr(conn->err);
goto ouch;
case HTTP_PROTOCOL_ERROR:
/* fall through */
@ -884,7 +885,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
_fetch_syserr();
goto ouch;
default:
_http_seterr(code);
_http_seterr(conn->err);
if (!verbose)
goto ouch;
/* fall through so we can get the full error message */
@ -892,7 +893,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
/* get headers */
do {
switch ((h = _http_next_header(fd, &p))) {
switch ((h = _http_next_header(conn, &p))) {
case hdr_syserror:
_fetch_syserr();
goto ouch;
@ -909,12 +910,12 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
_http_parse_mtime(p, &mtime);
break;
case hdr_location:
if (!HTTP_REDIRECT(code))
if (!HTTP_REDIRECT(conn->err))
break;
if (new)
free(new);
if (verbose)
_fetch_info("%d redirect to %s", code, p);
_fetch_info("%d redirect to %s", conn->err, p);
if (*p == '/')
/* absolute path */
new = fetchMakeURL(url->scheme, url->host, url->port, p,
@ -938,7 +939,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
chunked = (strcasecmp(p, "chunked") == 0);
break;
case hdr_www_authenticate:
if (code != HTTP_NEED_AUTH)
if (conn->err != HTTP_NEED_AUTH)
break;
/* if we were smarter, we'd check the method and realm */
break;
@ -951,21 +952,21 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
} while (h > hdr_end);
/* we have a hit or an error */
if (code == HTTP_OK || code == HTTP_PARTIAL || HTTP_ERROR(code))
if (conn->err == HTTP_OK || conn->err == HTTP_PARTIAL || HTTP_ERROR(conn->err))
break;
/* we need to provide authentication */
if (code == HTTP_NEED_AUTH) {
if (conn->err == HTTP_NEED_AUTH) {
need_auth = 1;
close(fd);
fd = -1;
_fetch_close(conn);
conn = NULL;
continue;
}
/* all other cases: we got a redirect */
need_auth = 0;
close(fd);
fd = -1;
_fetch_close(conn);
conn = NULL;
if (!new) {
DEBUG(fprintf(stderr, "redirect with no new location\n"));
break;
@ -976,8 +977,8 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
} while (++i < n);
/* we failed, or ran out of retries */
if (fd == -1) {
_http_seterr(code);
if (conn == NULL) {
_http_seterr(conn->err);
goto ouch;
}
@ -1019,7 +1020,13 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
URL->length = clength;
/* wrap it up in a FILE */
if ((f = chunked ? _http_funopen(fd) : fdopen(fd, "r")) == NULL) {
if (chunked) {
f = _http_funopen(conn);
} else {
f = fdopen(dup(conn->sd), "r");
_fetch_close(conn);
}
if (f == NULL) {
_fetch_syserr();
goto ouch;
}
@ -1029,7 +1036,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
if (purl)
fetchFreeURL(purl);
if (HTTP_ERROR(code)) {
if (HTTP_ERROR(conn->err)) {
_http_print_html(stderr, f);
fclose(f);
f = NULL;
@ -1042,8 +1049,8 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
fetchFreeURL(url);
if (purl)
fetchFreeURL(purl);
if (fd != -1)
close(fd);
if (conn != NULL)
_fetch_close(conn);
return (NULL);
}