1
0
mirror of https://github.com/nmap/nmap.git synced 2026-01-19 12:49:02 +00:00

[Ncat] Added support for socks5 and corresponding regression tests.

[Marek Lucaszuk, Petr Stodulka]
This commit is contained in:
henri
2014-02-09 14:10:04 +00:00
parent a95788c783
commit 21b7e3818d
7 changed files with 776 additions and 146 deletions

View File

@@ -1,5 +1,8 @@
# Nmap Changelog ($Id$); -*-text-*-
o [Ncat] Added support for socks5 and corresponding regression tests.
[Marek Lucaszuk, Petr Stodulka]
o [NSE] Add http-ntlm-info script for getting server information from Web
servers that require NTLM authentication. [Justin Cacak]

View File

@@ -154,6 +154,8 @@
#endif
#endif
#define SOCKS_BUFF_SIZE 512
/* structs */
#ifdef WIN32
@@ -163,8 +165,27 @@ struct socks4_data {
char version;
char type;
unsigned short port;
unsigned long address;
char username[256];
uint32_t address;
char data[SOCKS_BUFF_SIZE]; // this has to be able to hold FQDN and username
} __attribute__((packed));
struct socks5_connect {
char ver;
char nmethods;
char methods[3];
} __attribute__((packed));
struct socks5_auth {
char ver; // must be always 1
char data[SOCKS_BUFF_SIZE];
} __attribute__((packed));
struct socks5_request {
char ver;
char cmd;
char rsv;
char atyp;
char dst[SOCKS_BUFF_SIZE]; // addr/name and port info
} __attribute__((packed));
#ifdef WIN32
#pragma pack()
@@ -192,6 +213,10 @@ struct socks4_data {
/* Default port for SOCKS4 */
#define DEFAULT_SOCKS4_PORT 1080
/* Default port for SOCKS5 */
#define DEFAULT_SOCKS5_PORT 1080
/* The default port Ncat will connect to when trying to connect to an HTTP
* proxy server. The current setting is the default for squid and probably
* other HTTP proxies. But it may also be 8080, 8888, etc.
@@ -217,10 +242,21 @@ struct socks4_data {
#define SOCKS4_VERSION 4
#define SOCKS_CONNECT 1
#define SOCKS_BIND 2
#define SOCKS_CONN_ACC 90 /* woot */
#define SOCKS_CONN_REF 91
#define SOCKS_CONN_IDENT 92
#define SOCKS_CONN_IDENTDIFF 93
#define SOCKS4_CONN_ACC 90 /* woot */
#define SOCKS4_CONN_REF 91
#define SOCKS4_CONN_IDENT 92
#define SOCKS4_CONN_IDENTDIFF 93
/* SOCKS5 protocol */
#define SOCKS5_VERSION 5
#define SOCKS5_AUTH_NONE 0
#define SOCKS5_AUTH_GSSAPI 1
#define SOCKS5_AUTH_USERPASS 2
#define SOCKS5_AUTH_FAILED 255
#define SOCKS5_ATYP_IPv4 1
#define SOCKS5_ATYP_NAME 3
#define SOCKS5_ATYP_IPv6 4
/* Length of IPv6 address */
#ifndef INET6_ADDRSTRLEN

View File

@@ -298,50 +298,48 @@ static void connect_report(nsock_iod nsi)
}
/* Just like inet_socktop, but it puts IPv6 addresses in square brackets. */
static const char *sock_to_url(const union sockaddr_u *su)
static const char *sock_to_url(char *host_str, unsigned short port)
{
static char buf[INET6_ADDRSTRLEN + 32];
const char *host_str;
unsigned short port;
static char buf[512];
host_str = inet_socktop(su);
port = inet_port(su);
if (su->storage.ss_family == AF_INET)
Snprintf(buf, sizeof(buf), "%s:%hu", host_str, port);
else if (su->storage.ss_family == AF_INET6)
Snprintf(buf, sizeof(buf), "[%s]:%hu", host_str, port);
else
bye("Unknown address family in sock_to_url_host.");
switch(getaddrfamily(host_str)) {
case -1:
case 1:
Snprintf(buf, sizeof(buf), "%s:%hu", host_str, port);
break;
case 2:
Snprintf(buf, sizeof(buf), "[%s]:%hu]", host_str, port);
}
return buf;
}
static int append_connect_request_line(char **buf, size_t *size, size_t *offset,
const union sockaddr_u *su)
char* host_str,unsigned short port)
{
return strbuf_sprintf(buf, size, offset, "CONNECT %s HTTP/1.0\r\n",
sock_to_url(su));
sock_to_url(host_str,port));
}
static char *http_connect_request(const union sockaddr_u *su, int *n)
static char *http_connect_request(char* host_str, unsigned short port, int *n)
{
char *buf = NULL;
size_t size = 0, offset = 0;
append_connect_request_line(&buf, &size, &offset, su);
append_connect_request_line(&buf, &size, &offset, host_str, port);
strbuf_append_str(&buf, &size, &offset, "\r\n");
*n = offset;
return buf;
}
static char *http_connect_request_auth(const union sockaddr_u *su, int *n,
static char *http_connect_request_auth(char* host_str, unsigned short port, int *n,
struct http_challenge *challenge)
{
char *buf = NULL;
size_t size = 0, offset = 0;
append_connect_request_line(&buf, &size, &offset, su);
append_connect_request_line(&buf, &size, &offset, host_str, port);
strbuf_append_str(&buf, &size, &offset, "Proxy-Authorization:");
if (challenge->scheme == AUTH_BASIC) {
char *auth_str;
@@ -364,7 +362,7 @@ static char *http_connect_request_auth(const union sockaddr_u *su, int *n,
return NULL;
}
response_hdr = http_digest_proxy_authorization(challenge,
username, password, "CONNECT", sock_to_url(&httpconnect));
username, password, "CONNECT", sock_to_url(o.target,o.portno));
if (response_hdr == NULL) {
free(proxy_auth);
return NULL;
@@ -405,7 +403,7 @@ static int do_proxy_http(void)
header = NULL;
/* First try a request with no authentication. */
request = http_connect_request(&httpconnect, &n);
request = http_connect_request(o.target,o.portno, &n);
if (send(sd, request, n, 0) < 0) {
loguser("Error sending proxy request: %s.\n", socket_strerror(socket_errno()));
free(request);
@@ -455,7 +453,7 @@ static int do_proxy_http(void)
goto bail;
}
request = http_connect_request_auth(&httpconnect, &n, &challenge);
request = http_connect_request_auth(o.target,o.portno, &n, &challenge);
if (request == NULL) {
loguser("Error building Proxy-Authorization header.\n");
http_challenge_free(&challenge);
@@ -511,6 +509,355 @@ bail:
return -1;
}
/* SOCKS4a support
* Return a usable socket descriptor after
* proxy negotiation, or -1 on any error.
*/
static int do_proxy_socks4(void)
{
struct socket_buffer stateful_buf;
struct socks4_data socks4msg;
char socksbuf[8];
int sd,len = 9;
sd = do_connect(SOCK_STREAM);
if (sd == -1) {
loguser("Proxy connection failed: %s.\n", socket_strerror(socket_errno()));
return sd;
}
socket_buffer_init(&stateful_buf, sd);
if (o.verbose) {
loguser("Connected to proxy %s:%hu\n", inet_socktop(&targetss),
inet_port(&targetss));
}
/* Fill the socks4_data struct */
zmem(&socks4msg, sizeof(socks4msg));
socks4msg.version = SOCKS4_VERSION;
socks4msg.type = SOCKS_CONNECT;
socks4msg.port = htons(o.portno);
switch(getaddrfamily(o.target)) {
case 1: // IPv4 address family
socks4msg.address = inet_addr(o.target);
if (o.proxy_auth){
memcpy(socks4msg.data, o.proxy_auth, strlen(o.proxy_auth));
len += strlen(o.proxy_auth);
}
break;
case 2: // IPv6 address family
loguser("Error: IPv6 addresses are not supported with Socks4.\n");
close(sd);
return -1;
case -1: // fqdn
socks4msg.address = inet_addr("0.0.0.1");
if (strlen(o.target) > SOCKS_BUFF_SIZE-2) {
loguser("Error: host name is too long.\n");
close(sd);
return -1;
}
if (o.proxy_auth){
if (strlen(o.target)+strlen(o.proxy_auth) > SOCKS_BUFF_SIZE-2) {
loguser("Error: host name and username are too long.\n");
close(sd);
return -1;
}
Strncpy(socks4msg.data,o.proxy_auth,sizeof(socks4msg.data));
len += strlen(o.proxy_auth);
}
memcpy(socks4msg.data+(len-8), o.target, strlen(o.target));
len += strlen(o.target)+1;
}
if (send(sd, (char *) &socks4msg, len, 0) < 0) {
loguser("Error: sending proxy request: %s.\n", socket_strerror(socket_errno()));
close(sd);
return -1;
}
/* The size of the socks4 response is 8 bytes. So read exactly
8 bytes from the buffer */
if (socket_buffer_readcount(&stateful_buf, socksbuf, 8) < 0) {
loguser("Error: short response from proxy.\n");
close(sd);
return -1;
}
if (sd != -1 && socksbuf[1] != SOCKS4_CONN_ACC) {
loguser("Proxy connection failed.\n");
close(sd);
return -1;
}
return sd;
}
/* SOCKS5 support
* Return a usable socket descriptor after
* proxy negotiation, or -1 on any error.
*/
static int do_proxy_socks5(void)
{
struct socket_buffer stateful_buf;
struct socks5_connect socks5msg;
uint32_t inetaddr;
char inet6addr[16];
unsigned short proxyport = htons(o.portno);
char socksbuf[8];
int sd,len,lenfqdn;
sd = do_connect(SOCK_STREAM);
if (sd == -1) {
loguser("Proxy connection failed: %s.\n", socket_strerror(socket_errno()));
return sd;
}
socket_buffer_init(&stateful_buf, sd);
if (o.verbose) {
loguser("Connected to proxy %s:%hu\n", inet_socktop(&targetss),
inet_port(&targetss));
}
zmem(&socks5msg,sizeof(socks5msg));
socks5msg.ver = SOCKS5_VERSION;
socks5msg.nmethods = 1;
socks5msg.methods[0] = SOCKS5_AUTH_NONE;
len = 3;
if (o.proxy_auth){
socks5msg.nmethods ++;
socks5msg.methods[1] = SOCKS5_AUTH_USERPASS;
len ++;
}
if (send(sd, (char *) &socks5msg, len, 0) < 0) {
loguser("Error: proxy request: %s.\n", socket_strerror(socket_errno()));
close(sd);
return -1;
}
/* first response just two bytes, version and auth method */
if (socket_buffer_readcount(&stateful_buf, socksbuf, 2) < 0) {
loguser("Error: malformed first response from proxy.\n");
close(sd);
return -1;
}
if (socksbuf[0] != 5){
loguser("Error: got wrong server version in response.\n");
close(sd);
return -1;
}
switch(socksbuf[1]) {
case SOCKS5_AUTH_NONE:
if (o.verbose)
loguser("No authentication needed.\n");
break;
case SOCKS5_AUTH_GSSAPI:
loguser("GSSAPI authentication method not supported.\n");
close(sd);
return -1;
case SOCKS5_AUTH_USERPASS:
if (o.verbose)
loguser("Doing username and password authentication.\n");
if(!o.proxy_auth){
loguser("Error: proxy requested to do authentication, but no credentials were provided.\n");
close(sd);
return -1;
}
if (strlen(o.proxy_auth) > SOCKS_BUFF_SIZE-2){
loguser("Error: username and password are too long to fit into buffer.\n");
close(sd);
return -1;
}
char *proxy_auth;
char *username, *password;
/* Split up the proxy auth argument. */
proxy_auth = Strdup(o.proxy_auth);
username = strtok(proxy_auth, ":");
password = strtok(NULL, ":");
if (password == NULL || username == NULL) {
free(proxy_auth);
loguser("Error: empty username or password.\n");
close(sd);
return -1;
}
/*
* For username/password authentication the client's authentication request is
* field 1: version number, 1 byte (must be 0x01 -- version of subnegotion)
* field 2: username length, 1 byte
* field 3: username
* field 4: password length, 1 byte
* field 5: password
*
* Server response for username/password authentication:
* field 1: version, 1 byte
* field 2: status code, 1 byte.
* 0x00 = success
* any other value = failure, connection must be closed
*/
struct socks5_auth socks5auth;
socks5auth.ver = 1;
socks5auth.data[0] = strlen(username);
memcpy(socks5auth.data+1,username,strlen(username));
len = 2 + strlen(username); // (version + strlen) + username
socks5auth.data[len]=strlen(password);
memcpy(socks5auth.data+len,password,strlen(password));
len += 1 + strlen(password);
if (send(sd, (char *) &socks5auth, len, 0) < 0) {
loguser("Error: sending proxy authentication.\n");
close(sd);
return -1;
}
if (socket_buffer_readcount(&stateful_buf, socksbuf, 2) < 0) {
loguser("Error: malformed proxy authentication response.\n");
close(sd);
return -1;
}
if (socksbuf[0] != 1 || socksbuf[1] != 0) {
loguser("Error: authentication failed.\n");
close(sd);
return -1;
}
break;
default:
loguser("Error - can't choose any authentication method.\n");
close(sd);
return -1;
}
struct socks5_request socks5msg2;
zmem(&socks5msg2,sizeof(socks5msg2));
socks5msg2.ver = SOCKS5_VERSION;
socks5msg2.cmd = SOCKS_CONNECT;
socks5msg2.rsv = 0;
switch(getaddrfamily(o.target)) {
case 1: // IPv4 address family
socks5msg2.atyp = SOCKS5_ATYP_IPv4;
inetaddr = inet_addr(o.target);
memcpy(socks5msg2.dst, &inetaddr, 4);
len = 4;
break;
case 2: // IPv6 address family
socks5msg2.atyp = SOCKS5_ATYP_IPv6;
inet_pton(AF_INET6,o.target,&inet6addr);
memcpy(socks5msg2.dst, inet6addr,16);
len = 16;
break;
case -1: // FQDN
socks5msg2.atyp = SOCKS5_ATYP_NAME;
lenfqdn=strlen(o.target);
if (lenfqdn > SOCKS_BUFF_SIZE-5){
loguser("Error: host name too long.\n");
close(sd);
return -1;
}
socks5msg2.dst[0]=lenfqdn;
memcpy(socks5msg2.dst+1,o.target,lenfqdn);
len = 1 + lenfqdn;
}
memcpy(socks5msg2.dst+len, &proxyport, sizeof(proxyport));
len += 2 + 1 + 3;
if (len > sizeof(socks5msg2)){
loguser("Error: address information too large.\n");
close(sd);
return -1;
}
if (send(sd, (char *) &socks5msg2, len, 0) < 0) {
loguser("Error: sending proxy request: %s.\n", socket_strerror(socket_errno()));
close(sd);
return -1;
}
/* TODO just two bytes for now, need to read more for bind */
if (socket_buffer_readcount(&stateful_buf, socksbuf, 2) < 0) {
loguser("Error: malformed second response from proxy.\n");
close(sd);
return -1;
}
switch(socksbuf[1]) {
case 0:
if (o.verbose)
loguser("connection succeeded.\n");
break;
case 1:
loguser("Error: general SOCKS server failure.\n");
close(sd);
return -1;
case 2:
loguser("Error: connection not allowed by ruleset.\n");
close(sd);
return -1;
case 3:
loguser("Error: Network unreachable.\n");
close(sd);
return -1;
case 4:
loguser("Error: Host unreachable.\n");
close(sd);
return -1;
case 5:
loguser("Error: Connection refused.\n");
close(sd);
return -1;
case 6:
loguser("Error: TTL expired.\n");
close(sd);
return -1;
case 7:
loguser("Error: Command not supported.\n");
close(sd);
return -1;
case 8:
loguser("Error: Address type not supported.\n");
close(sd);
return -1;
default:
loguser("Error: unassigned value in the reply.\n");
close(sd);
return -1;
}
return(sd);
}
int ncat_connect(void)
{
nsock_pool mypool;
@@ -543,8 +890,7 @@ int ncat_connect(void)
set_ssl_ctx_options((SSL_CTX *) nsp_ssl_init(mypool));
#endif
if (httpconnect.storage.ss_family == AF_UNSPEC
&& socksconnect.storage.ss_family == AF_UNSPEC) {
if (!o.proxytype) {
/* A non-proxy connection. Create an iod for a new socket. */
cs.sock_nsi = nsi_new(mypool, NULL);
if (cs.sock_nsi == NULL)
@@ -640,65 +986,23 @@ int ncat_connect(void)
} else {
/* A proxy connection. */
static int connect_socket;
int len;
char *line;
size_t n;
if (httpconnect.storage.ss_family != AF_UNSPEC) {
if (strcmp(o.proxytype, "http") == 0) {
connect_socket = do_proxy_http();
if (connect_socket == -1)
return 1;
} else if (socksconnect.storage.ss_family != AF_UNSPEC) {
struct socket_buffer stateful_buf;
struct socks4_data socks4msg;
char socksbuf[8];
connect_socket = do_connect(SOCK_STREAM);
if (connect_socket == -1) {
loguser("Proxy connection failed: %s.\n", socket_strerror(socket_errno()));
return 1;
}
socket_buffer_init(&stateful_buf, connect_socket);
if (o.verbose) {
loguser("Connected to proxy %s:%hu\n", inet_socktop(&targetss),
inet_port(&targetss));
}
/* Fill the socks4_data struct */
zmem(&socks4msg, sizeof(socks4msg));
socks4msg.version = SOCKS4_VERSION;
socks4msg.type = SOCKS_CONNECT;
socks4msg.port = socksconnect.in.sin_port;
socks4msg.address = socksconnect.in.sin_addr.s_addr;
if (o.proxy_auth)
Strncpy(socks4msg.username, (char *) o.proxy_auth, sizeof(socks4msg.username));
len = 8 + strlen(socks4msg.username) + 1;
if (send(connect_socket, (char *) &socks4msg, len, 0) < 0) {
loguser("Error sending proxy request: %s.\n", socket_strerror(socket_errno()));
return 1;
}
/* The size of the socks4 response is 8 bytes. So read exactly
8 bytes from the buffer */
if (socket_buffer_readcount(&stateful_buf, socksbuf, 8) < 0) {
loguser("Error: short reponse from proxy.\n");
return 1;
}
if (socksbuf[1] != 90) {
loguser("Proxy connection failed.\n");
return 1;
}
/* Clear out whatever is left in the socket buffer which may be
already sent by proxy server along with http response headers. */
line = socket_buffer_remainder(&stateful_buf, &n);
/* Write the leftover data to stdout. */
Write(STDOUT_FILENO, line, n);
} else if (strcmp(o.proxytype, "socks4") == 0) {
connect_socket = do_proxy_socks4();
} else if (strcmp(o.proxytype, "socks5") == 0) {
connect_socket = do_proxy_socks5();
}
if (connect_socket == -1)
return 1;
/* Clear out whatever is left in the socket buffer which may be
already sent by proxy server along with http response headers. */
//line = socket_buffer_remainder(&stateful_buf, &n);
/* Write the leftover data to stdout. */
//Write(STDOUT_FILENO, line, n);
/* Once the proxy negotiation is done, Nsock takes control of the
socket. */
cs.sock_nsi = nsi_new2(mypool, connect_socket, NULL);

View File

@@ -150,9 +150,6 @@ size_t srcaddrlen;
union sockaddr_u targetss;
size_t targetsslen;
union sockaddr_u httpconnect, socksconnect;
size_t httpconnectlen, socksconnectlen;
/* Global options structure. */
struct options o;
@@ -536,6 +533,32 @@ static int ncat_hexdump(int logfd, const char *data, int len)
return 1;
}
/* this function will return in what format the target
* host is specified. It will return:
* 1 - for ipv4,
* 2 - for ipv6,
* -1 - for hostname
* this has to work even if there is no IPv6 support on
* local system, proxy may support it.
*/
int getaddrfamily(const char *addr)
{
int ret;
if (strchr(addr,':'))
return 2;
struct addrinfo hint, *info =0;
zmem(&hint,sizeof(hint));
hint.ai_family = AF_UNSPEC;
hint.ai_flags = AI_NUMERICHOST;
ret = getaddrinfo(addr, 0, &hint, &info);
if (ret)
return -1;
freeaddrinfo(info);
return 1;
}
void setup_environment(struct fdinfo *info)
{
union sockaddr_u su;

View File

@@ -139,9 +139,6 @@ extern size_t srcaddrlen;
extern union sockaddr_u targetss;
extern size_t targetsslen;
extern union sockaddr_u httpconnect, socksconnect;
extern size_t httpconnectlen, socksconnectlen;
enum exec_mode {
EXEC_PLAIN,
EXEC_SHELL,
@@ -197,6 +194,7 @@ struct options {
enum exec_mode execmode;
char *proxy_auth;
char *proxytype;
char *proxyaddr;
int ssl;
char *sslcert;
@@ -265,5 +263,6 @@ extern int ncat_hostaccess(char *matchaddr, char *filename, char *remoteip);
Defined in ncat_posix.c and ncat_win.c. */
extern void set_lf_mode(void);
extern int getaddrfamily(const char *addr);
extern int setenv_portable(const char *name, const char *value);
extern void setup_environment(struct fdinfo *fdinfo);

View File

@@ -154,12 +154,10 @@ static int ncat_connect_mode(void);
static int ncat_listen_mode(void);
/* Determines if it's parsing HTTP or SOCKS by looking at defport */
static size_t parseproxy(char *str, struct sockaddr_storage *ss, unsigned short defport)
static size_t parseproxy(char *str, struct sockaddr_storage *ss,
size_t *sslen, unsigned short *portno)
{
char *c = strrchr(str, ':'), *ptr;
int httpproxy = (defport == DEFAULT_PROXY_PORT);
unsigned short portno;
size_t sslen;
int rc;
ptr = str;
@@ -168,19 +166,17 @@ static size_t parseproxy(char *str, struct sockaddr_storage *ss, unsigned short
*c = 0;
if (c && strlen((c + 1)))
portno = (unsigned short) atoi(c + 1);
else
portno = defport;
*portno = (unsigned short) atoi(c + 1);
rc = resolve(ptr, portno, ss, &sslen, o.af);
rc = resolve(ptr, *portno, ss, sslen, o.af);
if (rc != 0) {
loguser("Could not resolve proxy \"%s\": %s.\n", ptr, gai_strerror(rc));
if (o.af == AF_INET6 && httpproxy)
if (o.af == AF_INET6 && *portno)
loguser("Did you specify the port number? It's required for IPv6.\n");
exit(EXIT_FAILURE);
}
return sslen;
return *sslen;
}
/* These functions implement a simple linked list to hold allow/deny
@@ -259,9 +255,9 @@ int main(int argc, char *argv[])
struct host_list_node *allow_host_list = NULL;
struct host_list_node *deny_host_list = NULL;
unsigned short proxyport = DEFAULT_PROXY_PORT;
int srcport = -1;
char *source = NULL;
char *proxyaddr = NULL;
struct option long_options[] = {
{"4", no_argument, NULL, '4'},
@@ -457,17 +453,17 @@ int main(int argc, char *argv[])
print_banner();
exit(EXIT_SUCCESS);
} else if (strcmp(long_options[option_index].name, "proxy") == 0) {
if (proxyaddr)
if (o.proxyaddr)
bye("You can't specify more than one --proxy.");
proxyaddr = Strdup(optarg);
o.proxyaddr = optarg;
} else if (strcmp(long_options[option_index].name, "proxy-type") == 0) {
if (o.proxytype)
bye("You can't specify more than one --proxy-type.");
o.proxytype = Strdup(optarg);
o.proxytype = optarg;
} else if (strcmp(long_options[option_index].name, "proxy-auth") == 0) {
if (o.proxy_auth)
bye("You can't specify more than one --proxy-auth.");
o.proxy_auth = Strdup(optarg);
o.proxy_auth = optarg;
} else if (strcmp(long_options[option_index].name, "nsock-engine") == 0) {
if (nsock_set_default_engine(optarg) < 0)
bye("Unknown or non-available engine: %s.", optarg);
@@ -595,8 +591,9 @@ int main(int argc, char *argv[])
" --broker Enable Ncat's connection brokering mode\n"
" --chat Start a simple Ncat chat server\n"
" --proxy <addr[:port]> Specify address of host to proxy through\n"
" --proxy-type <type> Specify proxy type (\"http\" or \"socks4\")\n"
" --proxy-type <type> Specify proxy type (\"http\" or \"socks4\" or \"socks5\")\n"
" --proxy-auth <auth> Authenticate with HTTP or SOCKS proxy server\n"
#ifdef HAVE_OPENSSL
" --ssl Connect or listen with SSL\n"
" --ssl-cert Specify SSL certificate file (PEM) for listening\n"
@@ -634,7 +631,7 @@ int main(int argc, char *argv[])
#if HAVE_SYS_UN_H
/* Using Unix domain sockets, so do the checks now */
if (o.af == AF_UNIX) {
if (proxyaddr || o.proxytype)
if (o.proxyaddr || o.proxytype)
bye("Proxy option not supported when using Unix domain sockets.");
#ifdef HAVE_OPENSSL
if (o.ssl)
@@ -652,7 +649,7 @@ int main(int argc, char *argv[])
/* Will be AF_INET or AF_INET6 or AF_UNIX when valid */
memset(&targetss.storage, 0, sizeof(targetss.storage));
targetss.storage.ss_family = AF_UNSPEC;
httpconnect.storage = socksconnect.storage = srcaddr.storage = targetss.storage;
srcaddr.storage = targetss.storage;
/* Clear the listenaddrs array */
int i;
@@ -660,28 +657,32 @@ int main(int argc, char *argv[])
listenaddrs[i].storage = targetss.storage;
}
if (proxyaddr) {
if (o.proxyaddr) {
if (!o.proxytype)
o.proxytype = Strdup("http");
if (!strcmp(o.proxytype, "http")) {
/* Parse HTTP proxy address and temporarily store it in httpconnect. If
* the proxy server is given as an IPv6 address (not hostname), the port
* number MUST be specified as well or parsing will break (due to the
* colons in the IPv6 address and host:port separator).
if (!strcmp(o.proxytype, "http") ||
!strcmp(o.proxytype, "socks4") || !strcmp(o.proxytype, "4") ||
!strcmp(o.proxytype, "socks5") || !strcmp(o.proxytype, "5")) {
/* Parse HTTP/SOCKS proxy address and store it in targetss.
* If the proxy server is given as an IPv6 address (not hostname),
* the port number MUST be specified as well or parsing will break
* (due to the colons in the IPv6 address and host:port separator).
*/
httpconnectlen = parseproxy(proxyaddr, &httpconnect.storage, DEFAULT_PROXY_PORT);
} else if (!strcmp(o.proxytype, "socks4") || !strcmp(o.proxytype, "4")) {
/* Parse SOCKS proxy address and temporarily store it in socksconnect */
socksconnectlen = parseproxy(proxyaddr, &socksconnect.storage, DEFAULT_SOCKS4_PORT);
targetsslen = parseproxy(o.proxyaddr,
&targetss.storage, &targetsslen, &proxyport);
if (o.af == AF_INET) {
targetss.in.sin_port = htons(proxyport);
} else { // might modify to else if and test AF_{INET6|UNIX|UNSPEC}
targetss.in6.sin6_port = htons(proxyport);
}
} else {
bye("Invalid proxy type \"%s\".", o.proxytype);
}
free(o.proxytype);
free(proxyaddr);
if (o.listen)
bye("Invalid option combination: --proxy and -l.");
} else {
if (o.proxytype) {
if (!o.listen)
@@ -692,7 +693,10 @@ int main(int argc, char *argv[])
}
/* Default port */
o.portno = DEFAULT_NCAT_PORT;
if (o.listen && o.proxytype && !o.portno && srcport == -1)
o.portno = DEFAULT_PROXY_PORT;
else
o.portno = DEFAULT_NCAT_PORT;
/* Resolve the given source address */
if (source) {
@@ -753,9 +757,12 @@ int main(int argc, char *argv[])
int rc;
o.target = argv[optind];
/* resolve hostname */
rc = resolve(o.target, 0, &targetss.storage, &targetsslen, o.af);
if (rc != 0)
/* resolve hostname only if o.proxytype == NULL
* targetss contains data already and you don't want remove them
*/
if( !o.proxytype
&& (rc = resolve(o.target, 0, &targetss.storage, &targetsslen, o.af)) != 0)
bye("Could not resolve hostname \"%s\": %s.", o.target, gai_strerror(rc));
optind++;
} else {
@@ -790,7 +797,9 @@ int main(int argc, char *argv[])
o.portno = (unsigned short) long_port;
}
if (targetss.storage.ss_family == AF_INET)
if (o.proxytype && !o.listen)
; /* Do nothing - port is already set to proxyport */
else if (targetss.storage.ss_family == AF_INET)
targetss.in.sin_port = htons(o.portno);
#ifdef HAVE_IPV6
else if (targetss.storage.ss_family == AF_INET6)
@@ -831,25 +840,6 @@ int main(int argc, char *argv[])
}
}
/* Since the host we're actually *connecting* to is the proxy server, we
* need to reverse these address structures to avoid any further confusion
*/
if (httpconnect.storage.ss_family != AF_UNSPEC) {
union sockaddr_u tmp = targetss;
size_t tmplen = targetsslen;
targetss = httpconnect;
targetsslen = httpconnectlen;
httpconnect = tmp;
httpconnectlen = tmplen;
} else if (socksconnect.storage.ss_family != AF_UNSPEC) {
union sockaddr_u tmp = targetss;
size_t tmplen = targetsslen;
targetss = socksconnect;
targetsslen = socksconnectlen;
socksconnect = tmp;
socksconnectlen = tmplen;
}
if (o.proto == IPPROTO_UDP) {
/* Don't allow a false sense of security if someone tries SSL over UDP. */
if (o.ssl)
@@ -904,7 +894,7 @@ static int ncat_connect_mode(void)
static int ncat_listen_mode(void)
{
/* Can't 'listen' AND 'connect' to a proxy server at the same time. */
if (httpconnect.storage.ss_family != AF_UNSPEC || socksconnect.storage.ss_family != AF_UNSPEC)
if (o.proxyaddr != NULL)
bye("Invalid option combination: --proxy and -l.");
if (o.broker && o.cmdexec != NULL)

View File

@@ -10,8 +10,10 @@ use File::Temp qw/ tempfile /;
use URI::Escape;
use Data::Dumper;
use Socket;
use Socket6;
use Digest::MD5 qw/md5_hex/;
use POSIX ":sys_wait_h";
use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
use IPC::Open3;
use strict;
@@ -2767,6 +2769,279 @@ sub {
};
kill_children;
# expand IPv6
sub ipv6_expand {
local($_) = shift;
s/^:/0:/;
s/:$/:0/;
s/(^|:)([^:]{1,3})(?=:|$)/$1.substr("0000$2", -4)/ge;
my $c = tr/:/:/;
s/::/":".("0000:" x (8-$c))/e;
return $_;
}
sub socks5_auth {
my ($pid,$code);
my $buf="";
my @Barray;
my $auth_data = shift;
my $ipvx = shift;
my $dest_addr = shift;
my $passed = 0;
my $username= "";
my $passwd= "";
my $recv_addr = "";
my $recv_port;
my ($pf,$s_addr);
local $SIG{CHLD} = sub { };
local *SOCK;
local *S;
if ($ipvx eq -4) {
$pf = PF_INET;
$s_addr = sockaddr_in($PROXY_PORT, INADDR_ANY);
} else {
$pf = PF_INET6;
$s_addr = sockaddr_in6($PROXY_PORT, inet_pton(PF_INET6, "::1"));
}
socket(SOCK, $pf, SOCK_STREAM, getprotobyname("tcp")) or die;
setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die;
bind(SOCK, $s_addr) or die;
listen(SOCK, 1) or die;
my ($c_pid, $c_out, $c_in) = ncat("--proxy-type", "socks5", "--proxy", "localhost:$PROXY_PORT", @$auth_data, $ipvx, $dest_addr, $PORT);
accept(S, SOCK) or die "Client not connected";
binmode(S);
sysread(S, $buf, 10) or die "Connection closed";
@Barray = map hex($_), unpack("H*", $buf) =~ /(..)/g;
die "wrong request format" if scalar(@Barray) < 3;
die "wrong protocol version" if $Barray[0] != 5;
if(scalar(@$auth_data) > 0) {
# subnegotiation for authentication
for(my $i=2; $i < scalar(@Barray); $i++) {
if($Barray[$i] == 2) {
$passed = 1;
}
}
die "Client did not sent required authentication method x02" if $passed == 0;
send(S, "\x05\x02",0) or die "Send: Connection closed";
sysread(S, $buf, $BUFSIZ) or die "Read: Connection closed";
@Barray = map hex($_), unpack("H*", $buf) =~ /(..)/g;
die "wrong request format - small length" if scalar(@Barray) < 5;
die "wrong request format - wrong version" if $Barray[0] != 1;
die "wrong request format - username legth longer then packet size"
if $Barray[1] >= scalar(@Barray);
# get username
for (my $i=2; $i < $Barray[1]+2; $i++) {
$username .= chr($Barray[$i]);
}
#get password
for (my $i=3+$Barray[1]; $i < scalar(@Barray); $i++) {
$passwd .= chr($Barray[$i]);
}
if ($username ne "vasek" or $passwd ne "admin") {
send(S, "\x01\x11", 0);
# do not close connection - we can check if client try continue
} else {
send(S, "\x01\x00",0);
}
} else {
# no authentication
send(S, "\x05\x00",0) or die "Send: Connection closed";
}
sysread(S, $buf, $BUFSIZ) or die "Read: connection closed";
@Barray = map hex($_), unpack("H*", $buf) =~ /(..)/g;
die "wrong request length format" if scalar(@Barray) < 10;
die "wrong protocol version after success authentication" if $Barray[0] != 5;
die "expected connect cmd" if $Barray[1] != 1;
if($Barray[3] == 1) {
# IPv4
$recv_addr = $Barray[4] .".". $Barray[5] .".". $Barray[6] .".". $Barray[7];
die "received wrong destination IPv4" if $recv_addr ne $dest_addr;
} elsif ($Barray[3] == 4) {
#IPv6
for(my $i=4; $i<20;$i++) {
if($i > 4 and $i % 2 == 0) {
$recv_addr .= ":";
}
$recv_addr .= sprintf("%02X",$Barray[$i]);
}
die "received wrong destination IPv6" if $recv_addr ne ipv6_expand($dest_addr);
} elsif ($Barray[3] == 3) {
# domaint name
for my $i (@Barray[5..(scalar(@Barray)-3)]) {
$recv_addr .= chr($i);
}
die "received wrong destination domain name" if $recv_addr ne $dest_addr;
die "received wrong length of domain name" if length($recv_addr) != $Barray[4];
} else {
die "unknown ATYP: $Barray[3]";
}
$recv_port = $Barray[-2]*256 + $Barray[-1];
die "received wrong destination port" if $recv_port ne $PORT;
send(S, "\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00", 0);
# check if connection is still open
syswrite($c_in, "abc\n");
sysread(S, $buf, 10) or die "Connection closed";
close(S);
close(SOCK);
};
test "SOCKS5 client, server require auth username/password (access allowed), IPv4",
sub { socks5_auth(["--proxy-auth","vasek:admin"], "-4", "127.0.0.1"); };
kill_children;
test "SOCKS5 client, server require auth username/password (access allowed), IPv6",
sub { socks5_auth(["--proxy-auth","vasek:admin"], "-6", "::1"); };
kill_children;
test "SOCKS5 client, server require auth username/password (access allowed), domain",
sub { socks5_auth(["--proxy-auth","vasek:admin"], "-4", "www.seznam.cz"); };
kill_children;
test "SOCKS5 client, server allows connection - no auth",
sub { socks5_auth([], "-4", "127.0.0.1")};
kill_children;
{
local $xfail = 1;
test "SOCKS5 client, server require auth username/password (access denied)",
sub { socks5_auth(["--proxy-auth","klara:admin"], "-4", "127.0.0.1"); };
kill_children;
test "SOCKS5 client, server require auth username/password (too long login)",
sub { socks5_auth(["--proxy-auth",'monika' x 100 . ':admindd'], "-4", "127.0.0.1");};
kill_children;
}
{
local $xfail = 1;
test "SOCKS5 client, server sends short response",
sub {
my ($pid,$code);
my $buf="";
local $SIG{CHLD} = sub { };
local *SOCK;
local *S;
socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname("tcp")) or die;
setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die;
bind(SOCK, sockaddr_in($PROXY_PORT, INADDR_ANY)) or die;
listen(SOCK, 1) or die;
my ($c_pid, $c_out, $c_in) = ncat("-4","--proxy-type", "socks5", "--proxy", "$HOST:$PROXY_PORT", "127.0.0.1", $PORT);
accept(S, SOCK) or die "Client not connected";
binmode(S);
sysread(S, $buf, 10) or die "Connection closed";
# not important received data now,
# when we know that's ok from test above
# we need O_NONBLOCK for read/write actions else
# client block us until we kill process manually
fcntl(S, F_SETFL, O_NONBLOCK) or
die "Can't set flags for the socket: $!\n";
send(S, "\x05", 0) or die "Send: Connection closed";
sysread(S, $buf, $BUFSIZ) or die "Connection closed";
close(S);
close(SOCK);
};
kill_children;
}
{
local $xfail = 1;
test "SOCKS5 client, server sends no acceptable auth method",
sub {
my ($pid,$code);
my $buf="";
my ($my_addr,$recv_addr,$recv_port);
local $SIG{CHLD} = sub { };
local *SOCK;
local *S;
socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname("tcp")) or die;
setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die;
bind(SOCK, sockaddr_in($PROXY_PORT, INADDR_ANY)) or die;
listen(SOCK, 1) or die;
my ($c_pid, $c_out, $c_in) = ncat("-4","--proxy-type", "socks5", "--proxy", "$HOST:$PROXY_PORT", "127.0.0.1", $PORT);
accept(S, SOCK) or die "Client not connected";
binmode(S);
sysread(S, $buf, 10) or die "Connection closed";
send(S, "\x05\xFF",0) or die "Send: Connection closed";
sysread(S, $buf, $BUFSIZ) or die "Connection closed";
close(S);
close(SOCK);
};
kill_children;
}
{
local $xfail = 1;
test "SOCKS5 client, server sends unkown code",
sub {
my ($pid,$code);
my $buf="";
my ($my_addr,$recv_addr,$recv_port);
local $SIG{CHLD} = sub { };
local *SOCK;
local *S;
socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname("tcp")) or die;
setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die;
bind(SOCK, sockaddr_in($PROXY_PORT, INADDR_ANY)) or die;
listen(SOCK, 1) or die;
my ($c_pid, $c_out, $c_in) = ncat("-4","--proxy-type", "socks5", "--proxy", "$HOST:$PROXY_PORT", "127.0.0.1", $PORT);
accept(S, SOCK) or die "Client not connected";
binmode(S);
sysread(S, $buf, 10) or die "Connection closed";
send(S, "\x05\xAA",0) or die "Send: Connection closed";
sysread(S, $buf, $BUFSIZ) or die "Connection closed";
close(S);
close(SOCK);
};
kill_children;
}
for my $count (0, 1, 10) {
max_conns_test_tcp_sctp_ssl("--max-conns $count --keep-open", ["--keep-open"], [], $count);
}