Page MenuHomeFreeBSD

D18908.id61029.diff
No OneTemporary

D18908.id61029.diff

Index: lib/libfetch/common.h
===================================================================
--- lib/libfetch/common.h
+++ lib/libfetch/common.h
@@ -70,12 +70,47 @@
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);
Index: lib/libfetch/common.c
===================================================================
--- lib/libfetch/common.c
+++ lib/libfetch/common.c
@@ -42,6 +42,7 @@
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <netdb.h>
#include <poll.h>
#include <pwd.h>
@@ -314,7 +315,6 @@
}
-
/*
* Bind a socket to a specific local address
*/
@@ -336,6 +336,191 @@
}
+/*
+ * 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;
+
+ 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) {
+ fprintf(stderr, "SOCKS5: Failed to send selection method.\n");
+ goto fail;
+ }
+
+ /* Verify response from SOCKS5 server */
+ if (fetch_read(conn, buf, 2) != 2) {
+ fprintf(stderr, "SOCKS5: Failed to read method.\n");
+ goto fail;
+ }
+
+ ptr = buf;
+ if (ptr[0] != SOCKS_VERSION_5) {
+ fprintf(stderr, "SOCKS5: Currently only version 5 is implemented.\n");
+ goto fail;
+ }
+ if (ptr[1] == SOCKS_NOMETHODS) {
+ fprintf(stderr, "SOCKS5: No acceptable methods. Disconnecting.\n");
+ goto fail;
+ }
+ else if (ptr[1] != SOCKS5_NOTIMPLEMENTED) {
+ fprintf(stderr, "SOCKS5: Method currently not implemented. Disconnecting.\n");
+ 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) {
+ fprintf(stderr, "Hostname above 256 bytes, exiting.\n");
+ 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) {
+ fprintf(stderr, "SOCKS5: Failed to request.\n");
+ goto fail;
+ }
+
+ /* BND.ADDR is variable length, read the largest on non-blocking socket */
+ if (!fetch_read(conn, buf, BUFF_SIZE)) {
+ fprintf(stderr, "SOCKS5: Failed to receive reply.\n");
+ goto fail;
+ }
+
+ ptr = buf;
+ if (*ptr++ != SOCKS_VERSION_5) {
+ fprintf(stderr, "SOCKS5: Server responded with a non-version 5 response.\n");
+ goto fail;
+ }
+
+ switch(*ptr++) {
+ case SOCKS_SUCCESS:
+ break;
+ case SOCKS_GENERAL_FAILURE:
+ fprintf(stderr, "SOCKS5: General server failure\n");
+ goto fail;
+ case SOCKS_CONNECTION_NOT_ALLOWED:
+ fprintf(stderr, "SOCKS5: Connection not allowed by ruleset.\n");
+ goto fail;
+ case SOCKS_NETWORK_UNREACHABLE:
+ fprintf(stderr, "SOCKS5: Network unreachable.\n");
+ goto fail;
+ case SOCKS_HOST_UNREACHABLE:
+ fprintf(stderr, "SOCKS5: Host unreachable.\n");
+ goto fail;
+ case SOCKS_CONNECTION_REFUSED:
+ fprintf(stderr, "SOCKS5: Connection refused.\n");
+ goto fail;
+ case SOCKS_TTL_EXPIRED:
+ fprintf(stderr, "SOCKS5: TTL expired.\n");
+ goto fail;
+ case SOCKS_COMMAND_NOT_SUPPORTED:
+ fprintf(stderr, "SOCKS5: Command not supported.\n");
+ goto fail;
+ case SOCKS_ADDRESS_NOT_SUPPORTED:
+ fprintf(stderr, "SOCKS5: Address type not supported.\n");
+ goto fail;
+ default:
+ fprintf(stderr, "SOCKS5: Unspecified failure.\n");
+ goto fail;
+ }
+
+ return (1);
+
+fail:
+ return (0);
+}
+
+/*
+ * Perform SOCKS5 initialization
+ */
+int
+fetch_socks5_getenv(char **host, int *port)
+{
+ char *socks5env, *endptr, *ext;
+
+ if ((socks5env = getenv("SOCKS5_PROXY")) == NULL || *socks5env == '\0') {
+ *host = NULL;
+ *port = -1;
+ return (-1);
+ }
+
+ /* IPv6 addresses begin and end in brackets */
+ if (socks5env[0] == '[') {
+ if (socks5env[strlen(socks5env) - 1] == ']') {
+ *host = strndup(socks5env, strlen(socks5env));
+ if (*host == NULL)
+ goto fail;
+ *port = 1080; /* Default port as defined in RFC1928 */
+ } else {
+ ext = strstr(socks5env, "]:");
+ if (ext == NULL) {
+ fprintf(stderr, "Bad SOCKS5_PROXY format, missing closing ']': %s\n",
+ socks5env);
+ return (0);
+ }
+ ext=ext+1;
+ *host = strndup(socks5env, ext - socks5env);
+ if (*host == NULL)
+ goto fail;
+ *port = strtoimax(ext + 1, (char **)&endptr, 10);
+ if (*port == 0 && errno == EINVAL) {
+ fprintf(stderr, "Bad SOCKS5_PROXY port: %s\n", socks5env);
+ return (0);
+ }
+ }
+ } else {
+ ext = strrchr(socks5env, ':');
+ if (ext == NULL) {
+ *host = strdup(socks5env);
+ *port = 1080;
+ } else {
+ *host = strndup(socks5env, ext-socks5env);
+ if (*host == NULL)
+ goto fail;
+ *port = strtoimax(ext + 1, (char **)&endptr, 10);
+ if (*port == 0 && errno == EINVAL) {
+ fprintf(stderr, "Bad SOCKS5_PROXY port: %s\n", socks5env);
+ return (0);
+ }
+ }
+ }
+
+ return (2);
+
+fail:
+ fprintf(stderr, "Failure to allocate memory, exiting.\n");
+ return (-1);
+}
+
+
/*
* Establish a TCP connection to the specified port on the specified host.
*/
@@ -346,22 +531,38 @@
const char *bindaddr;
conn_t *conn = NULL;
int err = 0, sd = -1;
+ char *sockshost;
+ int socksport;
DEBUGF("---> %s:%d\n", host, port);
- /* resolve server address */
- if (verbose)
- fetch_info("resolving server address: %s:%d", host, port);
- if ((sais = fetch_resolve(host, port, af)) == NULL)
+ /* Check if SOCKS5_PROXY env variable is set */
+ 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 client address: %s", bindaddr);
- if ((cais = fetch_resolve(bindaddr, 0, af)) == NULL)
+ 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 SOCKS5 server address: %s:%d", sockshost, socksport);
+ if ((sais = fetch_resolve(sockshost, socksport, af)) == NULL) {
+ goto fail;
+ }
}
/* try each server address in turn */
@@ -389,13 +590,19 @@
sd = -1;
}
if (err != 0) {
- if (verbose)
+ if (verbose && sockshost == NULL)
fetch_info("failed to connect to %s:%d", host, port);
+ else if (verbose && sockshost)
+ fetch_info("failed to connect to SOCKS5 server %s:%d", sockshost, socksport);
goto syserr;
}
if ((conn = fetch_reopen(sd)) == NULL)
goto syserr;
+
+ if (sockshost)
+ if (!fetch_socks5_init(conn, host, port, verbose))
+ goto fail;
if (cais != NULL)
freeaddrinfo(cais);
if (sais != NULL)
Index: lib/libfetch/fetch.3
===================================================================
--- lib/libfetch/fetch.3
+++ lib/libfetch/fetch.3
@@ -26,7 +26,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd March 18, 2016
+.Dd June 19, 2019
.Dt FETCH 3
.Os
.Sh NAME
@@ -652,6 +652,13 @@
Same as
.Ev NO_PROXY ,
for compatibility.
+.It Ev SOCKS5_PROXY
+Uses SOCKS version 5 to make connection.
+The format must be the IP or hostname followed by a colon for the port.
+IPv6 addresses must enclose the address in brackets.
+If no port is specified, the default is 1080.
+This setting will supercede a connection to an
+.Ev HTTP_PROXY .
.It Ev SSL_ALLOW_SSL3
Allow SSL version 3 when negotiating the connection (not recommended).
.It Ev SSL_CA_CERT_FILE
@@ -710,6 +717,21 @@
NO_PROXY=localhost,127.0.0.1
.Ed
.Pp
+To use a SOCKS5 proxy, set the
+.Ev SOCKS5_PROXY
+environment variable to a
+valid host or IP followed by an optional colon and the port.
+IPv6 addresses must be enclosed in brackets.
+The following are examples of valid settings:
+.Bd -literal -offset indent
+SOCKS5_PROXY=proxy.example.com
+SOCKS5_PROXY=proxy.example.com:1080
+SOCKS5_PROXY=192.0.2.0
+SOCKS5_PROXY=198.51.100.0:1080
+SOCKS5_PROXY=[2001:db8::1]
+SOCKS5_PROXY=[2001:db8::2]:1080
+.Ed
+.Pp
Access HTTPS website without any certificate verification whatsoever:
.Bd -literal -offset indent
SSL_NO_VERIFY_PEER=1

File Metadata

Mime Type
text/plain
Expires
Thu, Jan 22, 8:19 PM (4 h, 32 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27861767
Default Alt Text
D18908.id61029.diff (10 KB)

Event Timeline