Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
libfetch: sync with FreeBSD
Baptiste Daroussin committed 3 years ago
commit 2d83819102dddcc9d8e304c15c301e1e6e65074c
parent a275d7a
5 files changed +404 -192
modified external/libfetch/common.c
@@ -30,7 +30,7 @@
 */

#include "bsd_compat.h"
-
__FBSDID("$FreeBSD: head/lib/libfetch/common.c 347050 2019-05-03 06:06:39Z adrian $");
+
__FBSDID("$FreeBSD$");

#include <sys/param.h>
#include <sys/socket.h>
@@ -42,7 +42,9 @@ __FBSDID("$FreeBSD: head/lib/libfetch/common.c 347050 2019-05-03 06:06:39Z adria
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
+
#include <inttypes.h>
#include <netdb.h>
+
#include <paths.h>
#include <poll.h>
#include <pwd.h>
#include <stdarg.h>
@@ -77,6 +79,64 @@ static struct fetcherr netdb_errlist[] = {
	{ -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
};

+
/*
+
 * SOCKS5 error enumerations
+
 */
+
enum SOCKS5_ERR {
+
/* Protocol errors */
+
	SOCKS5_ERR_SELECTION,
+
	SOCKS5_ERR_READ_METHOD,
+
	SOCKS5_ERR_VER5_ONLY,
+
	SOCKS5_ERR_NOMETHODS,
+
	SOCKS5_ERR_NOTIMPLEMENTED,
+
	SOCKS5_ERR_HOSTNAME_SIZE,
+
	SOCKS5_ERR_REQUEST,
+
	SOCKS5_ERR_REPLY,
+
	SOCKS5_ERR_NON_VER5_RESP,
+
	SOCKS5_ERR_GENERAL,
+
	SOCKS5_ERR_NOT_ALLOWED,
+
	SOCKS5_ERR_NET_UNREACHABLE,
+
	SOCKS5_ERR_HOST_UNREACHABLE,
+
	SOCKS5_ERR_CONN_REFUSED,
+
	SOCKS5_ERR_TTL_EXPIRED,
+
	SOCKS5_ERR_COM_UNSUPPORTED,
+
	SOCKS5_ERR_ADDR_UNSUPPORTED,
+
	SOCKS5_ERR_UNSPECIFIED,
+
/* Configuration errors */
+
	SOCKS5_ERR_BAD_HOST,
+
	SOCKS5_ERR_BAD_PROXY_FORMAT,
+
	SOCKS5_ERR_BAD_PORT
+
};
+

+
/*
+
 * Error messages for SOCKS5 errors
+
 */
+
static struct fetcherr socks5_errlist[] = {
+
/* SOCKS5 protocol errors */
+
	{ SOCKS5_ERR_SELECTION,		FETCH_ABORT,	"SOCKS5: Failed to send selection method" },
+
	{ SOCKS5_ERR_READ_METHOD,	FETCH_ABORT,	"SOCKS5: Failed to read method" },
+
	{ SOCKS5_ERR_VER5_ONLY,		FETCH_PROTO,	"SOCKS5: Only version 5 is implemented" },
+
	{ SOCKS5_ERR_NOMETHODS,		FETCH_PROTO,	"SOCKS5: No acceptable methods" },
+
	{ SOCKS5_ERR_NOTIMPLEMENTED,	FETCH_PROTO,	"SOCKS5: Method currently not implemented" },
+
	{ SOCKS5_ERR_HOSTNAME_SIZE,	FETCH_PROTO,	"SOCKS5: Hostname size is above 256 bytes" },
+
	{ SOCKS5_ERR_REQUEST,		FETCH_PROTO,	"SOCKS5: Failed to request" },
+
	{ SOCKS5_ERR_REPLY,		FETCH_PROTO,	"SOCKS5: Failed to receive reply" },
+
	{ SOCKS5_ERR_NON_VER5_RESP,	FETCH_PROTO,	"SOCKS5: Server responded with a non-version 5 response" },
+
	{ SOCKS5_ERR_GENERAL,		FETCH_ABORT,	"SOCKS5: General server failure" },
+
	{ SOCKS5_ERR_NOT_ALLOWED,	FETCH_AUTH,	"SOCKS5: Connection not allowed by ruleset" },
+
	{ SOCKS5_ERR_NET_UNREACHABLE,	FETCH_NETWORK,	"SOCKS5: Network unreachable" },
+
	{ SOCKS5_ERR_HOST_UNREACHABLE,	FETCH_ABORT,	"SOCKS5: Host unreachable" },
+
	{ SOCKS5_ERR_CONN_REFUSED,	FETCH_ABORT,	"SOCKS5: Connection refused" },
+
	{ SOCKS5_ERR_TTL_EXPIRED,	FETCH_TIMEOUT,	"SOCKS5: TTL expired" },
+
	{ SOCKS5_ERR_COM_UNSUPPORTED,	FETCH_PROTO,	"SOCKS5: Command not supported" },
+
	{ SOCKS5_ERR_ADDR_UNSUPPORTED,	FETCH_ABORT,	"SOCKS5: Address type not supported" },
+
	{ SOCKS5_ERR_UNSPECIFIED,	FETCH_UNKNOWN,	"SOCKS5: Unspecified error" },
+
/* Configuration error */
+
	{ SOCKS5_ERR_BAD_HOST,		FETCH_ABORT,	"SOCKS5: Bad proxy host" },
+
	{ SOCKS5_ERR_BAD_PROXY_FORMAT,	FETCH_ABORT,	"SOCKS5: Bad proxy format" },
+
	{ SOCKS5_ERR_BAD_PORT,		FETCH_ABORT,	"SOCKS5: Bad port" }
+
};
+

/* End-of-Line */
static const char ENDL[2] = "\r\n";

@@ -326,7 +386,6 @@ syserr:
}


-

/*
 * Bind a socket to a specific local address
 */
@@ -349,31 +408,241 @@ fetch_bind(int sd, int af, const char *addr)


/*
+
 * SOCKS5 connection initiation, based on RFC 1928
+
 * Default DNS resolution over SOCKS5
+
 */
+
int
+
fetch_socks5_init(conn_t *conn, const char *host, int port, int verbose)
+
{
+
	/*
+
	 * Size is based on largest packet prefix (4 bytes) +
+
	 * Largest FQDN (256) + one byte size (1) +
+
	 * Port (2)
+
	 */
+
	unsigned char buf[BUFF_SIZE];
+
	unsigned char *ptr;
+
	int ret = 1;
+

+
	if (verbose)
+
		fetch_info("Initializing SOCKS5 connection: %s:%d", host, port);
+

+
	/* Connection initialization */
+
	ptr = buf;
+
	*ptr++ = SOCKS_VERSION_5;
+
	*ptr++ = SOCKS_CONNECTION;
+
	*ptr++ = SOCKS_RSV;
+

+
	if (fetch_write(conn, buf, 3) != 3) {
+
		ret = SOCKS5_ERR_SELECTION;
+
		goto fail;
+
	}
+

+
	/* Verify response from SOCKS5 server */
+
	if (fetch_read(conn, buf, 2) != 2) {
+
		ret = SOCKS5_ERR_READ_METHOD;
+
		goto fail;
+
	}
+

+
	ptr = buf;
+
	if (ptr[0] != SOCKS_VERSION_5) {
+
		ret = SOCKS5_ERR_VER5_ONLY;
+
		goto fail;
+
	}
+
	if (ptr[1] == SOCKS_NOMETHODS) {
+
		ret = SOCKS5_ERR_NOMETHODS;
+
		goto fail;
+
	}
+
	else if (ptr[1] != SOCKS5_NOTIMPLEMENTED) {
+
		ret = SOCKS5_ERR_NOTIMPLEMENTED;
+
		goto fail;
+
	}
+

+
	/* Send Request */
+
	*ptr++ = SOCKS_VERSION_5;
+
	*ptr++ = SOCKS_CONNECTION;
+
	*ptr++ = SOCKS_RSV;
+
	/* Encode all targets as a hostname to avoid DNS leaks */
+
	*ptr++ = SOCKS_ATYP_DOMAINNAME;
+
	if (strlen(host) > FQDN_SIZE) {
+
		ret = SOCKS5_ERR_HOSTNAME_SIZE;
+
		goto fail;
+
	}
+
	*ptr++ = strlen(host);
+
	strncpy(ptr, host, strlen(host));
+
	ptr = ptr + strlen(host);
+

+
	port = htons(port);
+
	*ptr++ = port & 0x00ff;
+
	*ptr++ = (port & 0xff00) >> 8;
+

+
	if (fetch_write(conn, buf, ptr - buf) != ptr - buf) {
+
		ret = SOCKS5_ERR_REQUEST;
+
		goto fail;
+
	}
+

+
	/* BND.ADDR is variable length, read the largest on non-blocking socket */
+
	if (!fetch_read(conn, buf, BUFF_SIZE)) {
+
		ret = SOCKS5_ERR_REPLY;
+
		goto fail;
+
	}
+

+
	ptr = buf;
+
	if (*ptr++ != SOCKS_VERSION_5) {
+
		ret = SOCKS5_ERR_NON_VER5_RESP;
+
		goto fail;
+
	}
+

+
	switch(*ptr++) {
+
	case SOCKS_SUCCESS:
+
		break;
+
	case SOCKS_GENERAL_FAILURE:
+
		ret = SOCKS5_ERR_GENERAL;
+
		goto fail;
+
	case SOCKS_CONNECTION_NOT_ALLOWED:
+
		ret = SOCKS5_ERR_NOT_ALLOWED;
+
		goto fail;
+
	case SOCKS_NETWORK_UNREACHABLE:
+
		ret = SOCKS5_ERR_NET_UNREACHABLE;
+
		goto fail;
+
	case SOCKS_HOST_UNREACHABLE:
+
		ret = SOCKS5_ERR_HOST_UNREACHABLE;
+
		goto fail;
+
	case SOCKS_CONNECTION_REFUSED:
+
		ret = SOCKS5_ERR_CONN_REFUSED;
+
		goto fail;
+
	case SOCKS_TTL_EXPIRED:
+
		ret = SOCKS5_ERR_TTL_EXPIRED;
+
		goto fail;
+
	case SOCKS_COMMAND_NOT_SUPPORTED:
+
		ret = SOCKS5_ERR_COM_UNSUPPORTED;
+
		goto fail;
+
	case SOCKS_ADDRESS_NOT_SUPPORTED:
+
		ret = SOCKS5_ERR_ADDR_UNSUPPORTED;
+
		goto fail;
+
	default:
+
		ret = SOCKS5_ERR_UNSPECIFIED;
+
		goto fail;
+
	}
+

+
	return (ret);
+

+
fail:
+
	socks5_seterr(ret);
+
	return (0);
+
}
+

+
/*
+
 * Perform SOCKS5 initialization
+
 */
+
int
+
fetch_socks5_getenv(char **host, int *port)
+
{
+
	char *socks5env, *endptr, *ext;
+
	const char *portDelim;
+
	size_t slen;
+

+
	portDelim = ":";
+
	if ((socks5env = getenv("SOCKS5_PROXY")) == NULL || *socks5env == '\0') {
+
		*host = NULL;
+
		*port = -1;
+
		return (-1);
+
	}
+

+
	/*
+
	 * IPv6 addresses begin and end in brackets.  Set the port delimiter
+
	 * accordingly and search for it so we can do appropriate validation.
+
	 */
+
	if (socks5env[0] == '[')
+
		portDelim = "]:";
+

+
	slen = strlen(socks5env);
+
	ext = strstr(socks5env, portDelim);
+
	if (socks5env[0] == '[') {
+
		if (socks5env[slen - 1] == ']') {
+
			*host = strndup(socks5env, slen);
+
		} else if (ext != NULL) {
+
			*host = strndup(socks5env, ext - socks5env + 1);
+
		} else {
+
			socks5_seterr(SOCKS5_ERR_BAD_PROXY_FORMAT);
+
			return (0);
+
		}
+
	} else {
+
		*host = strndup(socks5env, ext - socks5env);
+
	}
+

+
	if (*host == NULL) {
+
		fprintf(stderr, "Failure to allocate memory, exiting.\n");
+
		return (-1);
+
	}
+
	if (ext == NULL) {
+
		*port = 1080; /* Default port as defined in RFC1928 */
+
	} else {
+
		ext += strlen(portDelim);
+
		errno = 0;
+
		*port = strtoimax(ext, (char **)&endptr, 10);
+
		if (*endptr != '\0' || errno != 0 || *port < 0 ||
+
		    *port > 65535) {
+
			free(*host);
+
			*host = NULL;
+
			socks5_seterr(SOCKS5_ERR_BAD_PORT);
+
			return (0);
+
		}
+
	}
+

+
	return (2);
+
}
+

+

+
/*
 * Establish a TCP connection to the specified port on the specified host.
 */
conn_t *
-
fetch_connect(struct url *u, int af, int verbose)
+
fetch_connect(const char *host, int port, int af, int verbose)
{
	struct addrinfo *cais = NULL, *sais = NULL, *cai, *sai;
	const char *bindaddr;
	conn_t *conn = NULL;
	int err = 0, sd = -1;
+
	char *sockshost;
+
	int socksport;

-
	DEBUGF("---> %s:%d\n", u->host, u->port);
+
	DEBUGF("---> %s:%d\n", host, port);

-
	/* resolve server address */
-
	if (verbose)
-
		fetch_info("resolving server address: %s:%d", u->host, u->port);
-
	if ((sais = fetch_resolve(u->host, u->port, af)) == NULL)
+
	/*
+
	 * Check if SOCKS5_PROXY env variable is set.  fetch_socks5_getenv
+
	 * will either set sockshost = NULL or allocate memory in all cases.
+
	 */
+
	sockshost = NULL;
+
	if (!fetch_socks5_getenv(&sockshost, &socksport))
		goto fail;

-
	/* resolve client address */
-
	bindaddr = getenv("FETCH_BIND_ADDRESS");
-
	if (bindaddr != NULL && *bindaddr != '\0') {
+
	/* Not using SOCKS5 proxy */
+
	if (sockshost == NULL) {
+
		/* resolve server address */
+
		if (verbose)
+
			fetch_info("resolving server address: %s:%d", host,
+
			    port);
+
		if ((sais = fetch_resolve(host, port, af)) == NULL)
+
			goto fail;
+

+
		/* resolve client address */
+
		bindaddr = getenv("FETCH_BIND_ADDRESS");
+
		if (bindaddr != NULL && *bindaddr != '\0') {
+
			if (verbose)
+
				fetch_info("resolving client address: %s",
+
				    bindaddr);
+
			if ((cais = fetch_resolve(bindaddr, 0, af)) == NULL)
+
				goto fail;
+
		}
+
	} else {
+
		/* resolve socks5 proxy address */
		if (verbose)
-
			fetch_info("resolving client address: %s", bindaddr);
-
		if ((cais = fetch_resolve(bindaddr, 0, af)) == NULL)
+
			fetch_info("resolving SOCKS5 server address: %s:%d",
+
			    sockshost, socksport);
+
		if ((sais = fetch_resolve(sockshost, socksport, af)) == NULL) {
+
			socks5_seterr(SOCKS5_ERR_BAD_HOST);
			goto fail;
+
		}
	}

	/* try each server address in turn */
@@ -401,21 +670,27 @@ fetch_connect(struct url *u, int af, int verbose)
		sd = -1;
	}
	if (err != 0) {
-
		if (verbose)
-
			fetch_info("failed to connect to %s:%d", u->host, u->port);
+
		if (verbose && sockshost == NULL) {
+
			fetch_info("failed to connect to %s:%d", host, port);
+
			goto syserr;
+
		} else if (sockshost != NULL) {
+
			if (verbose)
+
				fetch_info(
+
				    "failed to connect to SOCKS5 server %s:%d",
+
				    sockshost, socksport);
+
			socks5_seterr(SOCKS5_ERR_CONN_REFUSED);
+
			goto fail;
+
		}
		goto syserr;
	}

	if ((conn = fetch_reopen(sd)) == NULL)
		goto syserr;

-
	strlcpy(conn->scheme, u->scheme, sizeof(conn->scheme));
-
	strlcpy(conn->host, u->host, sizeof(conn->host));
-
	strlcpy(conn->user, u->user, sizeof(conn->user));
-
	strlcpy(conn->pwd, u->pwd, sizeof(conn->pwd));
-
	conn->port = u->port;
-
	conn->af = af;
-

+
	if (sockshost)
+
		if (!fetch_socks5_init(conn, host, port, verbose))
+
			goto fail;
+
	free(sockshost);
	if (cais != NULL)
		freeaddrinfo(cais);
	if (sais != NULL)
@@ -423,9 +698,12 @@ fetch_connect(struct url *u, int af, int verbose)
	return (conn);
syserr:
	fetch_syserr();
-
	goto fail;
fail:
-
	if (sd >= 0)
+
	free(sockshost);
+
	/* Fully close if it was opened; otherwise just don't leak the fd. */
+
	if (conn != NULL)
+
		fetch_close(conn);
+
	else if (sd >= 0)
		close(sd);
	if (cais != NULL)
		freeaddrinfo(cais);
@@ -433,103 +711,6 @@ fail:
		freeaddrinfo(sais);
	return (NULL);
}
-
static conn_t *connection_cache;
-
static int cache_global_limit = 0;
-
static int cache_per_host_limit = 0;
-

-
/*
-
 * Initialise cache with the given limits.
-
 */
-
void
-
fetchConnectionCacheInit(int global_limit, int per_host_limit)
-
{
-

-
	if (global_limit < 0)
-
		cache_global_limit = INT_MAX;
-
	else if (per_host_limit > global_limit)
-
		cache_global_limit = per_host_limit;
-
	else
-
		cache_global_limit = global_limit;
-
	if (per_host_limit < 0)
-
		cache_per_host_limit = INT_MAX;
-
	else
-
		cache_per_host_limit = per_host_limit;
-
}
-

-
/*
-
 * Flush cache and free all associated resources.
-
 */
-
void
-
fetchConnectionCacheClose(void)
-
{
-
	conn_t *conn;
-

-
	while ((conn = connection_cache) != NULL) {
-
		connection_cache = conn->next;
-
		(*conn->close)(conn);
-
	}
-
}
-

-
/*
-
 * Check connection cache for an existing entry matching
-
 * protocol/host/port/user/password/family.
-
 */
-
conn_t *
-
fetch_cache_get(const struct url *url, int af)
-
{
-
	conn_t *conn, *last_conn = NULL;
-

-
	for (conn = connection_cache; conn; conn = conn->next) {
-
		if (conn->port == url->port &&
-
		    strcmp(conn->scheme, url->scheme) == 0 &&
-
		    strcmp(conn->host, url->host) == 0 &&
-
		    strcmp(conn->user, url->user) == 0 &&
-
		    strcmp(conn->pwd, url->pwd) == 0 &&
-
		    (conn->af == AF_UNSPEC || af == AF_UNSPEC ||
-
		     conn->af == af)) {
-
			if (last_conn != NULL)
-
				last_conn->next = conn->next;
-
			else
-
				connection_cache = conn->next;
-

-
			return conn;
-
		}
-
	}
-

-
	return NULL;
-
}
-

-
/*
-
 * Put the connection back into the cache for reuse.
-
 */
-
void
-
fetch_cache_put(conn_t *conn, int (*closecb)(conn_t *))
-
{
-
	conn_t *iter, *last;
-
	int global_count, host_count;
-

-
	global_count = host_count = 0;
-
	last = NULL;
-
	for (iter = connection_cache; iter;
-
	    last = iter, iter = iter->next) {
-
		++global_count;
-
		if (strcmp(conn->host, iter->host) == 0)
-
			++host_count;
-
		if (global_count < cache_global_limit &&
-
		    host_count < cache_per_host_limit)
-
			continue;
-
		--global_count;
-
		if (last != NULL)
-
			last->next = iter->next;
-
		else
-
			connection_cache = iter->next;
-
		(*iter->close)(iter);
-
	}
-

-
	conn->close = closecb;
-
	conn->next = connection_cache;
-
	connection_cache = conn;
-
}

#ifdef WITH_SSL
/*
@@ -885,9 +1066,7 @@ fetch_ssl_setup_transport_layer(SSL_CTX *ctx, int verbose)
{
	long ssl_ctx_options;

-
	ssl_ctx_options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_TICKET;
-
	if (getenv("SSL_ALLOW_SSL3") == NULL)
-
		ssl_ctx_options |= SSL_OP_NO_SSLv3;
+
	ssl_ctx_options = SSL_OP_ALL | SSL_OP_NO_SSLv3 | SSL_OP_NO_TICKET;
	if (getenv("SSL_NO_TLS1") != NULL)
		ssl_ctx_options |= SSL_OP_NO_TLSv1;
	if (getenv("SSL_NO_TLS1_1") != NULL)
modified external/libfetch/common.h
@@ -61,14 +61,6 @@ struct fetchconn {
	const SSL_METHOD *ssl_meth;	/* SSL method */
#endif
	int		 ref;		/* reference count */
-
	char		 scheme[URL_SCHEMELEN+1];
-
	char		 user[URL_USERLEN+1];
-
	char		 pwd[URL_PWDLEN+1];
-
	char		 host[MAXHOSTNAMELEN+1];
-
	int		 port;
-
	int		 af;
-
	int		(*close)(conn_t *);
-
	conn_t		*next;
};

/* Structure used for error message lists */
@@ -78,17 +70,52 @@ struct fetcherr {
	const char	*string;
};

+
/* For SOCKS header size */
+
#define HEAD_SIZE	4
+
#define FQDN_SIZE	256
+
#define PACK_SIZE	1
+
#define PORT_SIZE	2
+
#define BUFF_SIZE	HEAD_SIZE + FQDN_SIZE + PACK_SIZE + PORT_SIZE
+

+
/* SOCKS5 Request Header */
+
#define SOCKS_VERSION_5		0x05
+
/* SOCKS5 CMD */
+
#define SOCKS_CONNECTION	0x01
+
#define SOCKS_BIND		0x02
+
#define SOCKS_UDP		0x03
+
#define SOCKS_NOMETHODS		0xFF
+
#define SOCKS5_NOTIMPLEMENTED	0x00
+
/* SOCKS5 Reserved */
+
#define SOCKS_RSV		0x00
+
/* SOCKS5 Address Type */
+
#define SOCKS_ATYP_IPV4		0x01
+
#define SOCKS_ATYP_DOMAINNAME	0x03
+
#define SOCKS_ATYP_IPV6		0x04
+
/* SOCKS5 Reply Field */
+
#define SOCKS_SUCCESS			0x00
+
#define SOCKS_GENERAL_FAILURE		0x01
+
#define SOCKS_CONNECTION_NOT_ALLOWED	0x02
+
#define SOCKS_NETWORK_UNREACHABLE	0x03
+
#define SOCKS_HOST_UNREACHABLE		0x04
+
#define SOCKS_CONNECTION_REFUSED	0x05
+
#define SOCKS_TTL_EXPIRED		0x06
+
#define SOCKS_COMMAND_NOT_SUPPORTED	0x07
+
#define SOCKS_ADDRESS_NOT_SUPPORTED	0x08
+

/* for fetch_writev */
struct iovec;

void		 fetch_seterr(struct fetcherr *, int);
void		 fetch_syserr(void);
void		 fetch_info(const char *, ...) __printflike(1, 2);
+
int		 fetch_socks5_getenv(char **host, int *port);
+
int		 fetch_socks5_init(conn_t *conn, const char *host,
+
		     int port, int verbose);
int		 fetch_default_port(const char *);
int		 fetch_default_proxy_port(const char *);
struct addrinfo *fetch_resolve(const char *, int, int);
int		 fetch_bind(int, int, const char *);
-
conn_t		*fetch_connect(struct url *, int, int);
+
conn_t		*fetch_connect(const char *, int, int, int);
conn_t		*fetch_reopen(int);
conn_t		*fetch_ref(conn_t *);
#ifdef WITH_SSL
@@ -105,13 +132,12 @@ int fetch_add_entry(struct url_ent **, int *, int *,
		     const char *, struct url_stat *);
int		 fetch_netrc_auth(struct url *url);
int		 fetch_no_proxy_match(const char *);
-
conn_t		*fetch_cache_get(const struct url *, int);
-
void		 fetch_cache_put(conn_t *conn, int (*closecb)(conn_t *));

#define ftp_seterr(n)	 fetch_seterr(ftp_errlist, n)
#define http_seterr(n)	 fetch_seterr(http_errlist, n)
#define netdb_seterr(n)	 fetch_seterr(netdb_errlist, n)
#define url_seterr(n)	 fetch_seterr(url_errlist, n)
+
#define socks5_seterr(n) fetch_seterr(socks5_errlist, n)

#ifndef NDEBUG
#define DEBUGF(...)							\
modified external/libfetch/fetch.h
@@ -133,17 +133,12 @@ struct url_ent *fetchList(struct url *, const char *);
struct url	*fetchMakeURL(const char *, const char *, int,
		     const char *, const char *, const char *);
struct url	*fetchParseURL(const char *);
-
struct url	*fetchDupURL(struct url *);
void		 fetchFreeURL(struct url *);

#ifdef __cplusplus
}
#endif

-
/* Connection caching */
-
void fetchConnectionCacheInit(int, int);
-
void fetchConnectionCacheClose(void);
-

/* Authentication */
typedef int (*auth_t)(struct url *);
extern auth_t		 fetchAuthMethod;
modified external/libfetch/http.c
@@ -73,6 +73,7 @@ __FBSDID("$FreeBSD: head/lib/libfetch/http.c 351573 2019-08-28 17:01:28Z markj $
#include <locale.h>
#include <netdb.h>
#include <stdarg.h>
+
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -131,7 +132,6 @@ struct httpio
{
	conn_t		*conn;		/* connection */
	int		 chunked;	/* chunked mode */
-
	int		 keep_alive;	/* keep-alive mode */
	char		*buf;		/* chunk buffer */
	size_t		 bufsize;	/* size of chunk buffer */
	size_t		 buflen;	/* amount of data currently in buffer */
@@ -317,24 +317,11 @@ static int
http_closefn(void *v)
{
	struct httpio *io = (struct httpio *)v;
-
	int r, val;
-

-
	if (io->keep_alive) {
-
		val = 0;
-
		setsockopt(io->conn->sd, IPPROTO_TCP, TCP_NODELAY, &val,
-
		    sizeof(val));
-
		fetch_cache_put(io->conn, fetch_close);
-
#ifdef TCP_NOPUSH
-
		val = 1;
-
		setsockopt(io->conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val,
-
		    sizeof(val));
-
#endif
-
		r = 0;
-
	} else {
-
		r = fetch_close(io->conn);
-
	}
+
	int r;

-
	free(io->buf);
+
	r = fetch_close(io->conn);
+
	if (io->buf)
+
		free(io->buf);
	free(io);
	return (r);
}
@@ -343,7 +330,7 @@ http_closefn(void *v)
 * Wrap a file descriptor up
 */
static FILE *
-
http_funopen(conn_t *conn, int chunked, int keep_alive)
+
http_funopen(conn_t *conn, int chunked)
{
	struct httpio *io;
	FILE *f;
@@ -354,7 +341,6 @@ http_funopen(conn_t *conn, int chunked, int keep_alive)
	}
	io->conn = conn;
	io->chunked = chunked;
-
	io->keep_alive = keep_alive;
	f = funopen(io, http_readfn, http_writefn, NULL, http_closefn);
	if (f == NULL) {
		fetch_syserr();
@@ -375,7 +361,6 @@ typedef enum {
	hdr_error = -1,
	hdr_end = 0,
	hdr_unknown = 1,
-
	hdr_connection,
	hdr_content_length,
	hdr_content_range,
	hdr_last_modified,
@@ -390,7 +375,6 @@ static struct {
	hdr_t		 num;
	const char	*name;
} hdr_names[] = {
-
	{ hdr_connection,		"Connection" },
	{ hdr_content_length,		"Content-Length" },
	{ hdr_content_range,		"Content-Range" },
	{ hdr_last_modified,		"Last-Modified" },
@@ -992,13 +976,12 @@ http_base64(const char *src)
	    "0123456789+/";
	char *str, *dst;
	size_t l;
-
	int t, r;
+
	int t;

	l = strlen(src);
	if ((str = malloc(((l + 2) / 3) * 4 + 1)) == NULL)
		return (NULL);
	dst = str;
-
	r = 0;

	while (l >= 3) {
		t = (src[0] << 16) | (src[1] << 8) | src[2];
@@ -1007,7 +990,7 @@ http_base64(const char *src)
		dst[2] = base64[(t >> 6) & 0x3f];
		dst[3] = base64[(t >> 0) & 0x3f];
		src += 3; l -= 3;
-
		dst += 4; r += 4;
+
		dst += 4;
	}

	switch (l) {
@@ -1018,7 +1001,6 @@ http_base64(const char *src)
		dst[2] = base64[(t >> 6) & 0x3f];
		dst[3] = '=';
		dst += 4;
-
		r += 4;
		break;
	case 1:
		t = src[0] << 16;
@@ -1026,7 +1008,6 @@ http_base64(const char *src)
		dst[1] = base64[(t >> 12) & 0x3f];
		dst[2] = dst[3] = '=';
		dst += 4;
-
		r += 4;
		break;
	case 0:
		break;
@@ -1394,7 +1375,7 @@ http_authorize(conn_t *conn, const char *hdr, http_auth_challenges_t *cs,
 * Connect to the correct HTTP server or proxy.
 */
static conn_t *
-
http_connect(struct url *URL, struct url *purl, const char *flags, int *cached)
+
http_connect(struct url *URL, struct url *purl, const char *flags)
{
	struct url *curl;
	conn_t *conn;
@@ -1404,6 +1385,8 @@ http_connect(struct url *URL, struct url *purl, const char *flags, int *cached)
	int verbose;
	int af, val;
	int serrno;
+
	bool isproxyauth = false;
+
	http_auth_challenges_t proxy_challenges;

#ifdef INET6
	af = AF_UNSPEC;
@@ -1421,23 +1404,58 @@ http_connect(struct url *URL, struct url *purl, const char *flags, int *cached)

	curl = (purl != NULL) ? purl : URL;

-
	if ((conn = fetch_cache_get(curl, af)) != NULL) {
-
		*cached = 1;
-
		return (conn);
-
	}
-

-
	if ((conn = fetch_connect(curl, af, verbose)) == NULL)
+
retry:
+
	if ((conn = fetch_connect(curl->host, curl->port, af, verbose)) == NULL)
		/* fetch_connect() has already set an error code */
		return (NULL);
	init_http_headerbuf(&headerbuf);
	if (strcmp(URL->scheme, SCHEME_HTTPS) == 0 && purl) {
-
		http_cmd(conn, "CONNECT %s:%d HTTP/1.1",
-
		    URL->host, URL->port);
-
		http_cmd(conn, "Host: %s:%d",
-
		    URL->host, URL->port);
+
		init_http_auth_challenges(&proxy_challenges);
+
		http_cmd(conn, "CONNECT %s:%d HTTP/1.1", URL->host, URL->port);
+
		http_cmd(conn, "Host: %s:%d", URL->host, URL->port);
+
		if (isproxyauth) {
+
			http_auth_params_t aparams;
+
			init_http_auth_params(&aparams);
+
			if (*purl->user || *purl->pwd) {
+
				aparams.user = strdup(purl->user);
+
				aparams.password = strdup(purl->pwd);
+
			} else if ((p = getenv("HTTP_PROXY_AUTH")) != NULL &&
+
				    *p != '\0') {
+
				if (http_authfromenv(p, &aparams) < 0) {
+
					http_seterr(HTTP_NEED_PROXY_AUTH);
+
					fetch_syserr();
+
					goto ouch;
+
				}
+
			} else if (fetch_netrc_auth(purl) == 0) {
+
				aparams.user = strdup(purl->user);
+
				aparams.password = strdup(purl->pwd);
+
			} else {
+
				/*
+
				 * No auth information found in system - exiting
+
				 * with warning.
+
				 */
+
				warnx("Missing username and/or password set");
+
				fetch_syserr();
+
				goto ouch;
+
			}
+
			http_authorize(conn, "Proxy-Authorization",
+
			    &proxy_challenges, &aparams, purl);
+
			clean_http_auth_params(&aparams);
+
		}
		http_cmd(conn, "");
-
		if (http_get_reply(conn) != HTTP_OK) {
-
			http_seterr(conn->err);
+
		/* Get reply from CONNECT Tunnel attempt */
+
		int httpreply = http_get_reply(conn);
+
		if (httpreply != HTTP_OK) {
+
			http_seterr(httpreply);
+
			/* If the error is a 407/HTTP_NEED_PROXY_AUTH */
+
			if (httpreply == HTTP_NEED_PROXY_AUTH &&
+
			    ! isproxyauth) {
+
				/* Try again with authentication. */
+
				clean_http_headerbuf(&headerbuf);
+
				fetch_close(conn);
+
				isproxyauth = true;
+
				goto retry;
+
			}
			goto ouch;
		}
		/* Read and discard the rest of the proxy response */
@@ -1507,12 +1525,13 @@ http_get_proxy(struct url * url, const char *flags)
static void
http_print_html(FILE *out, FILE *in)
{
-
	size_t len = 0;
+
	ssize_t len = 0;
+
	size_t cap;
	char *line = NULL, *p, *q;
	int comment, tag;

	comment = tag = 0;
-
	while (getline(&line, &len, in) >= 0) {
+
	while ((len = getline(&line, &cap, in)) >= 0) {
		while (len && isspace((unsigned char)line[len - 1]))
			--len;
		for (p = q = line; q < line + len; ++q) {
@@ -1572,7 +1591,7 @@ http_request_body(struct url *URL, const char *op, struct url_stat *us,
	char hbuf[MAXHOSTNAMELEN + 7], *host;
	conn_t *conn;
	struct url *url, *new;
-
	int chunked, direct, ims, keep_alive, noredirect, verbose, cached;
+
	int chunked, direct, ims, noredirect, verbose;
	int e, i, n, val;
	off_t offset, clength, length, size;
	time_t mtime;
@@ -1594,7 +1613,6 @@ http_request_body(struct url *URL, const char *op, struct url_stat *us,
	noredirect = CHECK_FLAG('A');
	verbose = CHECK_FLAG('v');
	ims = CHECK_FLAG('i');
-
	keep_alive = 0;

	if (direct && purl) {
		fetchFreeURL(purl);
@@ -1616,14 +1634,13 @@ http_request_body(struct url *URL, const char *op, struct url_stat *us,
		length = -1;
		size = -1;
		mtime = 0;
-
		cached = 0;

		/* check port */
		if (!url->port)
			url->port = fetch_default_port(url->scheme);

		/* connect to server or proxy */
-
		if ((conn = http_connect(url, purl, flags, &cached)) == NULL)
+
		if ((conn = http_connect(url, purl, flags)) == NULL)
			goto ouch;

		/* append port number only if necessary */
@@ -1850,10 +1867,6 @@ http_request_body(struct url *URL, const char *op, struct url_stat *us,
			case hdr_error:
				http_seterr(HTTP_PROTOCOL_ERROR);
				goto ouch;
-
			case hdr_connection:
-
				/* XXX too weak? */
-
				keep_alive = (strcasecmp(p, "keep-alive") == 0);
-
				break;
			case hdr_content_length:
				http_parse_length(p, &clength);
				break;
@@ -2028,7 +2041,7 @@ http_request_body(struct url *URL, const char *op, struct url_stat *us,
	URL->length = clength;

	/* wrap it up in a FILE */
-
	if ((f = http_funopen(conn, chunked, keep_alive)) == NULL) {
+
	if ((f = http_funopen(conn, chunked)) == NULL) {
		fetch_syserr();
		goto ouch;
	}
modified libpkg/fetch_libfetch.c
@@ -105,7 +105,6 @@ fetch_connect(struct pkg_repo *repo, struct url *u)
	max_retry = pkg_object_int(pkg_config_get("FETCH_RETRY"));
	fetch_timeout = pkg_object_int(pkg_config_get("FETCH_TIMEOUT"));

-
	fetchConnectionCacheInit(-1, -1);
	fetchTimeout = (int)MIN(fetch_timeout, INT_MAX);

	repourl = fetchParseURL(repo->url);