lib/fetch: default port, error checks and authentication support

This commit is contained in:
Duncaen 2016-09-02 17:43:17 +02:00
parent 30ace44394
commit 08b9ed878f
2 changed files with 97 additions and 33 deletions

View File

@ -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);
@ -270,36 +272,79 @@ fetch_bind(int sd, int af, const char *addr)
} }
int int
fetch_socks5(conn_t *conn, struct url *url, int verbose) fetch_socks5(conn_t *conn, struct url *url, struct url *socks, int verbose)
{ {
char buf[16]; char buf[16];
uint8_t auth; uint8_t auth;
size_t alen; size_t alen;
alen = strlen(url->host); alen = strlen(url->host);
/* auth = (*socks->user != '\0' && *socks->pwd != '\0')
auth = (*url->user != '\0' && *url->pwd != '\0')
? SOCKS5_USER_PASS : SOCKS5_NO_AUTH; ? SOCKS5_USER_PASS : SOCKS5_NO_AUTH;
*/
auth = SOCKS5_NO_AUTH;
buf[0] = SOCKS5_VERSION; buf[0] = SOCKS5_VERSION;
buf[1] = 0x01; /* number of auth methods */ buf[1] = 0x01; /* number of auth methods */
buf[2] = auth; buf[2] = auth;
// XXX: support user/pass auth
if (fetch_write(conn, buf, 3) != 3) if (fetch_write(conn, buf, 3) != 3)
return -1; return -1;
if (fetch_read(conn, buf, 2) != 2) if (fetch_read(conn, buf, 2) != 2)
return -1; return -1;
if (buf[0] != SOCKS5_VERSION || buf[1] != auth) { if (buf[0] != SOCKS5_VERSION) {
if (verbose) if (verbose)
fetch_info("socks version or auth method not recognized"); fetch_info("socks5 version not recognized");
errno = EINVAL; errno = EINVAL;
return -1; 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) if (verbose)
fetch_info("connecting socks5 to %s:%d", url->host, url->port); fetch_info("connecting socks5 to %s:%d", url->host, url->port);
@ -308,7 +353,6 @@ fetch_socks5(conn_t *conn, struct url *url, int verbose)
buf[1] = SOCKS5_TCP_STREAM; buf[1] = SOCKS5_TCP_STREAM;
buf[2] = 0x00; buf[2] = 0x00;
buf[3] = SOCKS5_ATYPE_DOMAIN; buf[3] = SOCKS5_ATYPE_DOMAIN;
// XXX: support other address types
buf[4] = alen; buf[4] = alen;
if (fetch_write(conn, buf, 5) != 5) if (fetch_write(conn, buf, 5) != 5)
return -1; return -1;
@ -327,12 +371,15 @@ fetch_socks5(conn_t *conn, struct url *url, int verbose)
if (buf[0] != SOCKS5_VERSION) { if (buf[0] != SOCKS5_VERSION) {
if (verbose) if (verbose)
fetch_info("socks version not recognized"); fetch_info("socks5 version not recognized");
errno = EINVAL;
return -1; return -1;
} }
/* answer status */ /* answer status */
if (buf[1] != SOCKS5_REPLY_SUCCESS) { if (buf[1] != SOCKS5_REPLY_SUCCESS) {
if (verbose)
fetch_info("socks5 response status %d", buf[1]);
switch (buf[1]) { switch (buf[1]) {
case SOCKS5_REPLY_DENY: errno = EACCES; break; case SOCKS5_REPLY_DENY: errno = EACCES; break;
case SOCKS5_REPLY_NO_NET: errno = ENETUNREACH; break; case SOCKS5_REPLY_NO_NET: errno = ENETUNREACH; break;
@ -385,11 +432,20 @@ fetch_connect(struct url *url, int af, int verbose)
socks_url = NULL; socks_url = NULL;
socks_proxy = getenv("SOCKS_PROXY"); socks_proxy = getenv("SOCKS_PROXY");
if (socks_proxy != NULL && *socks_proxy != '\0' && if (socks_proxy != NULL && *socks_proxy != '\0') {
(socks_url = fetchParseURL(socks_proxy)) != NULL) if (!(socks_url = fetchParseURL(socks_proxy)))
return NULL;
if (strcasecmp(socks_url->scheme, SCHEME_SOCKS5) != 0) {
if (verbose)
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; connurl = socks_url;
else } else {
connurl = url; connurl = url;
}
if (verbose) if (verbose)
fetch_info("looking up %s", connurl->host); fetch_info("looking up %s", connurl->host);
@ -435,10 +491,15 @@ fetch_connect(struct url *url, int af, int verbose)
close(sd); close(sd);
return NULL; return NULL;
} }
if (socks_url && fetch_socks5(conn, url, verbose) != 0) { if (socks_url) {
fetch_syserr(); if (strcasecmp(socks_url->scheme, SCHEME_SOCKS5) == 0) {
close(sd); if (fetch_socks5(conn, url, socks_url, verbose) != 0) {
return NULL; 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;

View File

@ -36,26 +36,29 @@
#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_VERSION 0x05
#define SOCKS5_PASS_VERSION 0x01
#define SOCKS5_NO_AUTH 0x00 #define SOCKS5_NO_AUTH 0x00
#define SOCKS5_USER_PASS 0x02 #define SOCKS5_USER_PASS 0x02
#define SOCKS5_NO_ACCEPT_METHOD 0xFF #define SOCKS5_AUTH_SUCCESS 0x00
#define SOCKS5_NO_METHOD 0xFF
#define SOCKS5_TCP_STREAM 0x01 #define SOCKS5_TCP_STREAM 0x01
#define SOCKS5_ATYPE_IPV4 0x01 #define SOCKS5_ATYPE_IPV4 0x01
#define SOCKS5_ATYPE_DOMAIN 0x03 #define SOCKS5_ATYPE_DOMAIN 0x03
#define SOCKS5_ATYPE_IPV6 0x04 #define SOCKS5_ATYPE_IPV6 0x04
#define SOCKS5_REPLY_SUCCESS 0x00 #define SOCKS5_REPLY_SUCCESS 0x00
#define SOCKS5_REPLY_FAILURE 0x01 #define SOCKS5_REPLY_FAILURE 0x01
#define SOCKS5_REPLY_DENY 0x02 #define SOCKS5_REPLY_DENY 0x02
#define SOCKS5_REPLY_NO_NET 0x03 #define SOCKS5_REPLY_NO_NET 0x03
#define SOCKS5_REPLY_NO_HOST 0x04 #define SOCKS5_REPLY_NO_HOST 0x04
#define SOCKS5_REPLY_REFUSED 0x05 #define SOCKS5_REPLY_REFUSED 0x05
#define SOCKS5_REPLY_TIMEOUT 0x06 #define SOCKS5_REPLY_TIMEOUT 0x06
#define SOCKS5_REPLY_CMD_NOTSUP 0x07 #define SOCKS5_REPLY_CMD_NOTSUP 0x07
#define SOCKS5_REPLY_ADR_NOTSUP 0x08 #define SOCKS5_REPLY_ADR_NOTSUP 0x08
@ -120,7 +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 *, int); 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