Merge pull request #191 from Duncaen/socks5
lib/fetch: add socks5 support
This commit is contained in:
commit
2aa538bf35
@ -205,6 +205,8 @@ fetch_default_port(const char *scheme)
|
|||||||
return (HTTP_DEFAULT_PORT);
|
return (HTTP_DEFAULT_PORT);
|
||||||
if (strcasecmp(scheme, SCHEME_HTTPS) == 0)
|
if (strcasecmp(scheme, SCHEME_HTTPS) == 0)
|
||||||
return (HTTPS_DEFAULT_PORT);
|
return (HTTPS_DEFAULT_PORT);
|
||||||
|
if (strcasecmp(scheme, SCHEME_SOCKS5) == 0)
|
||||||
|
return (SOCKS5_DEFAULT_PORT);
|
||||||
if ((se = getservbyname(scheme, "tcp")) != NULL)
|
if ((se = getservbyname(scheme, "tcp")) != NULL)
|
||||||
return (ntohs(se->s_port));
|
return (ntohs(se->s_port));
|
||||||
return (0);
|
return (0);
|
||||||
@ -269,6 +271,151 @@ fetch_bind(int sd, int af, const char *addr)
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
fetch_socks5(conn_t *conn, struct url *url, struct url *socks, int verbose)
|
||||||
|
{
|
||||||
|
char buf[16];
|
||||||
|
uint8_t auth;
|
||||||
|
size_t alen;
|
||||||
|
|
||||||
|
alen = strlen(url->host);
|
||||||
|
auth = (*socks->user != '\0' && *socks->pwd != '\0')
|
||||||
|
? SOCKS5_USER_PASS : SOCKS5_NO_AUTH;
|
||||||
|
|
||||||
|
buf[0] = SOCKS5_VERSION;
|
||||||
|
buf[1] = 0x01; /* number of auth methods */
|
||||||
|
buf[2] = auth;
|
||||||
|
if (fetch_write(conn, buf, 3) != 3)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (fetch_read(conn, buf, 2) != 2)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (buf[0] != SOCKS5_VERSION) {
|
||||||
|
if (verbose)
|
||||||
|
fetch_info("socks5 version not recognized");
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((uint8_t)buf[1] == SOCKS5_NO_METHOD) {
|
||||||
|
if (verbose)
|
||||||
|
fetch_info("no acceptable socks5 authentication method");
|
||||||
|
errno = EPERM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (buf[1]) {
|
||||||
|
case SOCKS5_USER_PASS:
|
||||||
|
if (verbose)
|
||||||
|
fetch_info("authenticate socks5 user '%s'", socks->user);
|
||||||
|
buf[0] = SOCKS5_PASS_VERSION;
|
||||||
|
buf[1] = strlen(socks->user);
|
||||||
|
if (fetch_write(conn, buf, 2) != 2)
|
||||||
|
return -1;
|
||||||
|
if (fetch_write(conn, socks->user, buf[1]) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
buf[0] = strlen(socks->pwd);
|
||||||
|
if (fetch_write(conn, buf, 1) != 1)
|
||||||
|
return -1;
|
||||||
|
if (fetch_write(conn, socks->pwd, buf[0]) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (fetch_read(conn, buf, 2) != 2)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (buf[0] != SOCKS5_PASS_VERSION) {
|
||||||
|
if (verbose)
|
||||||
|
fetch_info("socks5 password version not recognized");
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
fetch_info("socks5 authentication response %d", buf[1]);
|
||||||
|
|
||||||
|
if (buf[1] != SOCKS5_AUTH_SUCCESS) {
|
||||||
|
if (verbose)
|
||||||
|
fetch_info("socks5 authentication failed");
|
||||||
|
errno = EPERM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
fetch_info("connecting socks5 to %s:%d", url->host, url->port);
|
||||||
|
|
||||||
|
/* write request */
|
||||||
|
buf[0] = SOCKS5_VERSION;
|
||||||
|
buf[1] = SOCKS5_TCP_STREAM;
|
||||||
|
buf[2] = 0x00;
|
||||||
|
buf[3] = SOCKS5_ATYPE_DOMAIN;
|
||||||
|
buf[4] = alen;
|
||||||
|
if (fetch_write(conn, buf, 5) != 5)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (fetch_write(conn, url->host, alen) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
buf[0] = (url->port >> 0x08);
|
||||||
|
buf[1] = (url->port & 0xFF);
|
||||||
|
if (fetch_write(conn, buf, 2) != 2)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* read answer */
|
||||||
|
if (fetch_read(conn, buf, 4) != 4)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (buf[0] != SOCKS5_VERSION) {
|
||||||
|
if (verbose)
|
||||||
|
fetch_info("socks5 version not recognized");
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* answer status */
|
||||||
|
if (buf[1] != SOCKS5_REPLY_SUCCESS) {
|
||||||
|
if (verbose)
|
||||||
|
fetch_info("socks5 response status %d", buf[1]);
|
||||||
|
switch (buf[1]) {
|
||||||
|
case SOCKS5_REPLY_DENY: errno = EACCES; break;
|
||||||
|
case SOCKS5_REPLY_NO_NET: errno = ENETUNREACH; break;
|
||||||
|
case SOCKS5_REPLY_NO_HOST: errno = EHOSTUNREACH; break;
|
||||||
|
case SOCKS5_REPLY_REFUSED: errno = ECONNREFUSED; break;
|
||||||
|
case SOCKS5_REPLY_TIMEOUT: errno = ETIMEDOUT; break;
|
||||||
|
case SOCKS5_REPLY_CMD_NOTSUP: errno = ENOTSUP; break;
|
||||||
|
case SOCKS5_REPLY_ADR_NOTSUP: errno = ENOTSUP; break;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (buf[3]) {
|
||||||
|
case SOCKS5_ATYPE_IPV4:
|
||||||
|
if (fetch_read(conn, buf, 4) != 4)
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
case SOCKS5_ATYPE_DOMAIN:
|
||||||
|
if (fetch_read(conn, buf, 1) != 1 &&
|
||||||
|
fetch_read(conn, buf, buf[0]) != buf[0])
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
case SOCKS5_ATYPE_IPV6:
|
||||||
|
if (fetch_read(conn, buf, 16) != 16)
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// port
|
||||||
|
if (fetch_read(conn, buf, 2) != 2)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Establish a TCP connection to the specified port on the specified host.
|
* Establish a TCP connection to the specified port on the specified host.
|
||||||
@ -278,27 +425,45 @@ fetch_connect(struct url *url, int af, int verbose)
|
|||||||
{
|
{
|
||||||
conn_t *conn;
|
conn_t *conn;
|
||||||
char pbuf[10];
|
char pbuf[10];
|
||||||
const char *bindaddr;
|
struct url *socks_url, *connurl;
|
||||||
|
const char *bindaddr, *socks_proxy;
|
||||||
struct addrinfo hints, *res, *res0;
|
struct addrinfo hints, *res, *res0;
|
||||||
int sd, error;
|
int sd, error;
|
||||||
|
|
||||||
|
socks_url = NULL;
|
||||||
|
socks_proxy = getenv("SOCKS_PROXY");
|
||||||
|
if (socks_proxy != NULL && *socks_proxy != '\0') {
|
||||||
|
if (!(socks_url = fetchParseURL(socks_proxy)))
|
||||||
|
return NULL;
|
||||||
|
if (strcasecmp(socks_url->scheme, SCHEME_SOCKS5) != 0) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
fetch_info("looking up %s", url->host);
|
fetch_info("SOCKS_PROXY scheme '%s' not supported", socks_url->scheme);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!socks_url->port)
|
||||||
|
socks_url->port = fetch_default_port(socks_url->scheme);
|
||||||
|
connurl = socks_url;
|
||||||
|
} else {
|
||||||
|
connurl = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
fetch_info("looking up %s", connurl->host);
|
||||||
|
|
||||||
/* look up host name and set up socket address structure */
|
/* look up host name and set up socket address structure */
|
||||||
snprintf(pbuf, sizeof(pbuf), "%d", url->port);
|
snprintf(pbuf, sizeof(pbuf), "%d", connurl->port);
|
||||||
memset(&hints, 0, sizeof(hints));
|
memset(&hints, 0, sizeof(hints));
|
||||||
hints.ai_family = af;
|
hints.ai_family = af;
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
hints.ai_protocol = 0;
|
hints.ai_protocol = 0;
|
||||||
if ((error = getaddrinfo(url->host, pbuf, &hints, &res0)) != 0) {
|
if ((error = getaddrinfo(connurl->host, pbuf, &hints, &res0)) != 0) {
|
||||||
netdb_seterr(error);
|
netdb_seterr(error);
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
bindaddr = getenv("FETCH_BIND_ADDRESS");
|
bindaddr = getenv("FETCH_BIND_ADDRESS");
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
fetch_info("connecting to %s:%d", url->host, url->port);
|
fetch_info("connecting to %s:%d", connurl->host, connurl->port);
|
||||||
|
|
||||||
/* try to connect */
|
/* try to connect */
|
||||||
for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
|
for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
|
||||||
@ -326,6 +491,16 @@ fetch_connect(struct url *url, int af, int verbose)
|
|||||||
close(sd);
|
close(sd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (socks_url) {
|
||||||
|
if (strcasecmp(socks_url->scheme, SCHEME_SOCKS5) == 0) {
|
||||||
|
if (fetch_socks5(conn, url, socks_url, verbose) != 0) {
|
||||||
|
fetch_syserr();
|
||||||
|
close(sd);
|
||||||
|
free(conn);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
conn->cache_url = fetchCopyURL(url);
|
conn->cache_url = fetchCopyURL(url);
|
||||||
conn->cache_af = af;
|
conn->cache_af = af;
|
||||||
return (conn);
|
return (conn);
|
||||||
|
@ -36,6 +36,31 @@
|
|||||||
#define HTTPS_DEFAULT_PORT 443
|
#define HTTPS_DEFAULT_PORT 443
|
||||||
#define FTP_DEFAULT_PROXY_PORT 21
|
#define FTP_DEFAULT_PROXY_PORT 21
|
||||||
#define HTTP_DEFAULT_PROXY_PORT 3128
|
#define HTTP_DEFAULT_PROXY_PORT 3128
|
||||||
|
#define SOCKS5_DEFAULT_PORT 1080
|
||||||
|
|
||||||
|
#define SOCKS5_VERSION 0x05
|
||||||
|
#define SOCKS5_PASS_VERSION 0x01
|
||||||
|
|
||||||
|
#define SOCKS5_NO_AUTH 0x00
|
||||||
|
#define SOCKS5_USER_PASS 0x02
|
||||||
|
#define SOCKS5_AUTH_SUCCESS 0x00
|
||||||
|
#define SOCKS5_NO_METHOD 0xFF
|
||||||
|
|
||||||
|
#define SOCKS5_TCP_STREAM 0x01
|
||||||
|
|
||||||
|
#define SOCKS5_ATYPE_IPV4 0x01
|
||||||
|
#define SOCKS5_ATYPE_DOMAIN 0x03
|
||||||
|
#define SOCKS5_ATYPE_IPV6 0x04
|
||||||
|
|
||||||
|
#define SOCKS5_REPLY_SUCCESS 0x00
|
||||||
|
#define SOCKS5_REPLY_FAILURE 0x01
|
||||||
|
#define SOCKS5_REPLY_DENY 0x02
|
||||||
|
#define SOCKS5_REPLY_NO_NET 0x03
|
||||||
|
#define SOCKS5_REPLY_NO_HOST 0x04
|
||||||
|
#define SOCKS5_REPLY_REFUSED 0x05
|
||||||
|
#define SOCKS5_REPLY_TIMEOUT 0x06
|
||||||
|
#define SOCKS5_REPLY_CMD_NOTSUP 0x07
|
||||||
|
#define SOCKS5_REPLY_ADR_NOTSUP 0x08
|
||||||
|
|
||||||
#ifdef WITH_SSL
|
#ifdef WITH_SSL
|
||||||
#include <openssl/crypto.h>
|
#include <openssl/crypto.h>
|
||||||
@ -98,6 +123,7 @@ int fetch_default_proxy_port(const char *);
|
|||||||
int fetch_bind(int, int, const char *);
|
int fetch_bind(int, int, const char *);
|
||||||
conn_t *fetch_cache_get(const struct url *, int);
|
conn_t *fetch_cache_get(const struct url *, int);
|
||||||
void fetch_cache_put(conn_t *, int (*)(conn_t *));
|
void fetch_cache_put(conn_t *, int (*)(conn_t *));
|
||||||
|
int fetch_socks5(conn_t *, struct url *, struct url *, int);
|
||||||
conn_t *fetch_connect(struct url *, int, int);
|
conn_t *fetch_connect(struct url *, int, int);
|
||||||
conn_t *fetch_reopen(int);
|
conn_t *fetch_reopen(int);
|
||||||
#ifdef WITH_SSL
|
#ifdef WITH_SSL
|
||||||
|
@ -459,6 +459,17 @@ fetchParseURL(const char *URL)
|
|||||||
URL += 2;
|
URL += 2;
|
||||||
goto find_user;
|
goto find_user;
|
||||||
}
|
}
|
||||||
|
if (strncmp(URL, "socks5:", 7) == 0) {
|
||||||
|
pre_quoted = 1;
|
||||||
|
strcpy(u->scheme, SCHEME_SOCKS5);
|
||||||
|
URL += 7;
|
||||||
|
if (URL[0] != '/' || URL[1] != '/') {
|
||||||
|
url_seterr(URL_MALFORMED);
|
||||||
|
goto ouch;
|
||||||
|
}
|
||||||
|
URL += 2;
|
||||||
|
goto find_user;
|
||||||
|
}
|
||||||
|
|
||||||
url_seterr(URL_BAD_SCHEME);
|
url_seterr(URL_BAD_SCHEME);
|
||||||
goto ouch;
|
goto ouch;
|
||||||
|
@ -72,6 +72,7 @@ struct url_list {
|
|||||||
#define SCHEME_HTTP "http"
|
#define SCHEME_HTTP "http"
|
||||||
#define SCHEME_HTTPS "https"
|
#define SCHEME_HTTPS "https"
|
||||||
#define SCHEME_FILE "file"
|
#define SCHEME_FILE "file"
|
||||||
|
#define SCHEME_SOCKS5 "socks5"
|
||||||
|
|
||||||
/* Error codes */
|
/* Error codes */
|
||||||
#define FETCH_ABORT 1
|
#define FETCH_ABORT 1
|
||||||
|
Loading…
Reference in New Issue
Block a user