mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
Ability to control hostname resolution for ncat proxy destinations
Closes #1214, fixes #1230, closes #1439
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
#Nmap Changelog ($Id$); -*-text-*-
|
||||
|
||||
o [ncat][GH#1214][GH#1230][GH#1439] New ncat option provides control over
|
||||
whether proxy destinations are resolved by the remote proxy server or
|
||||
locally, by Ncat itself. See option --proxy-dns. [nnposter]
|
||||
|
||||
o [NSE][GH#1478] Updated script ftp-syst to prevent potential endless looping.
|
||||
[nnposter]
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ Options taking a time assume seconds. Append 'ms' for milliseconds,
|
||||
--proxy <addr[:port]> Specify address of host to proxy through
|
||||
--proxy-type <type> Specify proxy type ("http", "socks4", "socks5")
|
||||
--proxy-auth <auth> Authenticate with HTTP or SOCKS proxy server
|
||||
--proxy-dns <type> Specify where to resolve proxy destination
|
||||
--ssl Connect or listen with SSL
|
||||
--ssl-cert Specify SSL certificate file (PEM) for listening
|
||||
--ssl-key Specify SSL private key (PEM) for listening
|
||||
|
||||
@@ -468,6 +468,38 @@
|
||||
<option>--proxy-type socks4</option>, it should be a username only.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<option>--proxy-dns <replaceable>type</replaceable></option> (Specify where to resolve proxy destination)
|
||||
<indexterm><primary><option>--proxy-dns</option> (Ncat option)</primary></indexterm>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>In connect mode, it provides control over whether proxy
|
||||
destination hostnames are resolved by the remote proxy server or
|
||||
locally, by Ncat itself.
|
||||
Possible values for <replaceable>type</replaceable> are:</para>
|
||||
|
||||
<para><literal>local</literal> - Hostnames are resolved locally on
|
||||
the Ncat host. Ncat exits with error if the hostname cannot be
|
||||
resolved.</para>
|
||||
|
||||
<para><literal>remote</literal> - Hostnames are passed directly onto
|
||||
the remote proxy server. This is the default behavior.</para>
|
||||
|
||||
<para><literal>both</literal> - Hostname resolution is first
|
||||
attempted on the Ncat host. Unresolvable hostnames are passed onto
|
||||
the remote proxy server.</para>
|
||||
|
||||
<para><literal>none</literal> - Hostname resolution is completely
|
||||
disabled. Only a literal IPv4 or IPv6 address can be used as
|
||||
the proxy destination.</para>
|
||||
|
||||
<para>Local hostname resolution generally respects IP version
|
||||
specified with options <option>-4</option> or <option>-6</option>,
|
||||
except for SOCKS4, which is incompatible with IPv6.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
@@ -817,7 +849,9 @@
|
||||
<listitem>
|
||||
<para>Completely disable hostname resolution across all Ncat options,
|
||||
such as the destination, source address, source routing hops, and
|
||||
the proxy. All addresses must be specified numerically.</para>
|
||||
the proxy. All addresses must be specified numerically.
|
||||
(Note that resolution of proxy destinations is controlled separately
|
||||
via option <option>--proxy-dns</option>.)</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
@@ -438,6 +438,15 @@ static int do_proxy_http(void)
|
||||
size_t len;
|
||||
int sd, code;
|
||||
int n;
|
||||
char *target;
|
||||
union sockaddr_u addr;
|
||||
size_t sslen;
|
||||
void *addrbuf;
|
||||
char addrstr[INET6_ADDRSTRLEN];
|
||||
|
||||
request = NULL;
|
||||
status_line = NULL;
|
||||
header = NULL;
|
||||
|
||||
sd = do_connect(SOCK_STREAM);
|
||||
if (sd == -1) {
|
||||
@@ -445,12 +454,35 @@ static int do_proxy_http(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
request = NULL;
|
||||
status_line = NULL;
|
||||
header = NULL;
|
||||
if (proxyresolve(o.target, 0, &addr.storage, &sslen, o.af)) {
|
||||
/* target resolution has failed, possibly because it is disabled */
|
||||
if (!(o.proxydns & PROXYDNS_REMOTE)) {
|
||||
loguser("Error: Failed to resolve host %s locally.\n", o.target);
|
||||
goto bail;
|
||||
}
|
||||
if (o.verbose)
|
||||
loguser("Host %s will be resolved by the proxy.\n", o.target);
|
||||
target = o.target;
|
||||
} else {
|
||||
/* addr is now populated with either sockaddr_in or sockaddr_in6 */
|
||||
switch (addr.sockaddr.sa_family) {
|
||||
case AF_INET:
|
||||
addrbuf = &addr.in.sin_addr;
|
||||
break;
|
||||
case AF_INET6:
|
||||
addrbuf = &addr.in6.sin6_addr;
|
||||
break;
|
||||
default:
|
||||
ncat_assert(0);
|
||||
}
|
||||
inet_ntop(addr.sockaddr.sa_family, addrbuf, addrstr, sizeof(addrstr));
|
||||
target = addrstr;
|
||||
if (o.verbose && getaddrfamily(o.target) == -1)
|
||||
loguser("Host %s locally resolved to %s.\n", o.target, target);
|
||||
}
|
||||
|
||||
/* First try a request with no authentication. */
|
||||
request = http_connect_request(o.target,o.portno, &n);
|
||||
request = http_connect_request(target, o.portno, &n);
|
||||
if (send(sd, request, n, 0) < 0) {
|
||||
loguser("Error sending proxy request: %s.\n", socket_strerror(socket_errno()));
|
||||
goto bail;
|
||||
@@ -501,7 +533,7 @@ static int do_proxy_http(void)
|
||||
goto bail;
|
||||
}
|
||||
|
||||
request = http_connect_request_auth(o.target,o.portno, &n, &challenge);
|
||||
request = http_connect_request_auth(target, o.portno, &n, &challenge);
|
||||
if (request == NULL) {
|
||||
loguser("Error building Proxy-Authorization header.\n");
|
||||
http_challenge_free(&challenge);
|
||||
@@ -569,9 +601,18 @@ bail:
|
||||
static int do_proxy_socks4(void)
|
||||
{
|
||||
struct socket_buffer stateful_buf;
|
||||
struct socks4_data socks4msg;
|
||||
char socksbuf[8];
|
||||
int sd,len = 9;
|
||||
struct socks4_data socks4msg;
|
||||
size_t datalen;
|
||||
char *username = o.proxy_auth != NULL ? o.proxy_auth : "";
|
||||
union sockaddr_u addr;
|
||||
size_t sslen;
|
||||
int sd;
|
||||
|
||||
if (getaddrfamily(o.target) == 2) {
|
||||
loguser("Error: IPv6 addresses are not supported with Socks4.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sd = do_connect(SOCK_STREAM);
|
||||
if (sd == -1) {
|
||||
@@ -591,46 +632,40 @@ static int do_proxy_socks4(void)
|
||||
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");
|
||||
if (strlen(username) >= sizeof(socks4msg.data)) {
|
||||
loguser("Error: username is too long.\n");
|
||||
close(sd);
|
||||
return -1;
|
||||
}
|
||||
strcpy(socks4msg.data, username);
|
||||
datalen = strlen(username) + 1;
|
||||
|
||||
case -1: // fqdn
|
||||
|
||||
if (proxyresolve(o.target, 0, &addr.storage, &sslen, AF_INET)) {
|
||||
/* target resolution has failed, possibly because it is disabled */
|
||||
if (!(o.proxydns & PROXYDNS_REMOTE)) {
|
||||
loguser("Error: Failed to resolve host %s locally.\n", o.target);
|
||||
close(sd);
|
||||
return -1;
|
||||
}
|
||||
if (o.verbose)
|
||||
loguser("Host %s will be resolved by the proxy.\n", o.target);
|
||||
socks4msg.address = inet_addr("0.0.0.1");
|
||||
|
||||
if (strlen(o.target) > SOCKS_BUFF_SIZE-2) {
|
||||
if (datalen + strlen(o.target) >= sizeof(socks4msg.data)) {
|
||||
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;
|
||||
strcpy(socks4msg.data + datalen, o.target);
|
||||
datalen += strlen(o.target) + 1;
|
||||
} else {
|
||||
/* addr is now populated with sockaddr_in */
|
||||
socks4msg.address = addr.in.sin_addr.s_addr;
|
||||
if (o.verbose && getaddrfamily(o.target) == -1)
|
||||
loguser("Host %s locally resolved to %s.\n", o.target,
|
||||
inet_ntoa(addr.in.sin_addr));
|
||||
}
|
||||
|
||||
if (send(sd, (char *) &socks4msg, len, 0) < 0) {
|
||||
if (send(sd, (char *)&socks4msg, offsetof(struct socks4_data, data) + datalen, 0) < 0) {
|
||||
loguser("Error: sending proxy request: %s.\n", socket_strerror(socket_errno()));
|
||||
close(sd);
|
||||
return -1;
|
||||
@@ -659,11 +694,8 @@ static int do_proxy_socks4(void)
|
||||
*/
|
||||
static int do_proxy_socks5(void)
|
||||
{
|
||||
|
||||
struct socket_buffer stateful_buf;
|
||||
struct socks5_connect socks5msg;
|
||||
uint32_t inetaddr;
|
||||
char inet6addr[16];
|
||||
uint16_t proxyport = htons(o.portno);
|
||||
char socksbuf[8];
|
||||
int sd;
|
||||
@@ -672,6 +704,11 @@ static int do_proxy_socks5(void)
|
||||
struct socks5_auth socks5auth;
|
||||
char *uptr, *pptr;
|
||||
size_t authlen, ulen, plen;
|
||||
union sockaddr_u addr;
|
||||
size_t sslen;
|
||||
void *addrbuf;
|
||||
size_t addrlen;
|
||||
char addrstr[INET6_ADDRSTRLEN];
|
||||
|
||||
sd = do_connect(SOCK_STREAM);
|
||||
if (sd == -1) {
|
||||
@@ -814,23 +851,15 @@ static int do_proxy_socks5(void)
|
||||
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);
|
||||
dstlen = 4;
|
||||
break;
|
||||
|
||||
case 2: // IPv6 address family
|
||||
socks5msg2.atyp = SOCKS5_ATYP_IPv6;
|
||||
inet_pton(AF_INET6,o.target,&inet6addr);
|
||||
memcpy(socks5msg2.dst, inet6addr,16);
|
||||
dstlen = 16;
|
||||
break;
|
||||
|
||||
case -1: // FQDN
|
||||
if (proxyresolve(o.target, 0, &addr.storage, &sslen, o.af)) {
|
||||
/* target resolution has failed, possibly because it is disabled */
|
||||
if (!(o.proxydns & PROXYDNS_REMOTE)) {
|
||||
loguser("Error: Failed to resolve host %s locally.\n", o.target);
|
||||
close(sd);
|
||||
return -1;
|
||||
}
|
||||
if (o.verbose)
|
||||
loguser("Host %s will be resolved by the proxy.\n", o.target);
|
||||
socks5msg2.atyp = SOCKS5_ATYP_NAME;
|
||||
targetlen=strlen(o.target);
|
||||
if (targetlen > SOCKS5_DST_MAXLEN){
|
||||
@@ -842,11 +871,28 @@ static int do_proxy_socks5(void)
|
||||
socks5msg2.dst[dstlen++] = targetlen;
|
||||
memcpy(socks5msg2.dst + dstlen, o.target, targetlen);
|
||||
dstlen += targetlen;
|
||||
} else {
|
||||
/* addr is now populated with either sockaddr_in or sockaddr_in6 */
|
||||
switch (addr.sockaddr.sa_family) {
|
||||
case AF_INET:
|
||||
socks5msg2.atyp = SOCKS5_ATYP_IPv4;
|
||||
addrbuf = &addr.in.sin_addr;
|
||||
addrlen = 4;
|
||||
break;
|
||||
|
||||
default: // this shall not happen
|
||||
case AF_INET6:
|
||||
socks5msg2.atyp = SOCKS5_ATYP_IPv6;
|
||||
addrbuf = &addr.in6.sin6_addr;
|
||||
addrlen = 16;
|
||||
break;
|
||||
default:
|
||||
ncat_assert(0);
|
||||
}
|
||||
memcpy(socks5msg2.dst, addrbuf, addrlen);
|
||||
dstlen = addrlen;
|
||||
if (o.verbose && getaddrfamily(o.target) == -1)
|
||||
loguser("Host %s locally resolved to %s.\n", o.target,
|
||||
inet_ntop(addr.sockaddr.sa_family, addrbuf, addrstr, sizeof(addrstr)));
|
||||
}
|
||||
|
||||
memcpy(socks5msg2.dst + dstlen, &proxyport, 2);
|
||||
dstlen += 2;
|
||||
|
||||
@@ -207,6 +207,7 @@ void options_init(void)
|
||||
o.proxy_auth = NULL;
|
||||
o.proxytype = NULL;
|
||||
o.proxyaddr = NULL;
|
||||
o.proxydns = PROXYDNS_REMOTE;
|
||||
o.zerobyte = 0;
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
@@ -297,6 +298,32 @@ int resolve(const char *hostname, unsigned short port,
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Resolves the given hostname or IP address with getaddrinfo, and stores the
|
||||
first result (if any) in *ss and *sslen. The value of port will be set in the
|
||||
appropriate place in *ss; set to 0 if you don't care. af may be AF_UNSPEC, in
|
||||
which case getaddrinfo may return e.g. both IPv4 and IPv6 results; which one
|
||||
is first depends on the system configuration. Returns 0 on success, or a
|
||||
getaddrinfo return code (suitable for passing to gai_strerror) on failure.
|
||||
*ss and *sslen are always defined when this function returns 0.
|
||||
|
||||
Resolve the hostname with DNS only if global o.proxydns includes PROXYDNS_LOCAL. */
|
||||
int proxyresolve(const char *hostname, unsigned short port,
|
||||
struct sockaddr_storage *ss, size_t *sslen, int af)
|
||||
{
|
||||
int flags;
|
||||
struct sockaddr_list sl;
|
||||
int result;
|
||||
|
||||
flags = 0;
|
||||
if (!(o.proxydns & PROXYDNS_LOCAL))
|
||||
flags |= AI_NUMERICHOST;
|
||||
|
||||
result = resolve_internal(hostname, port, &sl, af, flags, 0);
|
||||
*ss = sl.addr.storage;
|
||||
*sslen = sl.addrlen;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Resolves the given hostname or IP address with getaddrinfo, and stores
|
||||
all results into a linked list.
|
||||
The rest of the behavior is same as resolve(). */
|
||||
|
||||
@@ -160,6 +160,10 @@ enum exec_mode {
|
||||
EXEC_LUA,
|
||||
};
|
||||
|
||||
/* Proxy DNS resolution options (mask bits) */
|
||||
#define PROXYDNS_LOCAL 1
|
||||
#define PROXYDNS_REMOTE 2
|
||||
|
||||
struct options {
|
||||
unsigned short portno;
|
||||
|
||||
@@ -211,6 +215,7 @@ struct options {
|
||||
char *proxy_auth;
|
||||
char *proxytype;
|
||||
char *proxyaddr;
|
||||
int proxydns;
|
||||
|
||||
int ssl;
|
||||
char *sslcert;
|
||||
@@ -242,6 +247,18 @@ void options_init(void);
|
||||
int resolve(const char *hostname, unsigned short port,
|
||||
struct sockaddr_storage *ss, size_t *sslen, int af);
|
||||
|
||||
/* Resolves the given hostname or IP address with getaddrinfo, and stores the
|
||||
first result (if any) in *ss and *sslen. The value of port will be set in the
|
||||
appropriate place in *ss; set to 0 if you don't care. af may be AF_UNSPEC, in
|
||||
which case getaddrinfo may return e.g. both IPv4 and IPv6 results; which one
|
||||
is first depends on the system configuration. Returns 0 on success, or a
|
||||
getaddrinfo return code (suitable for passing to gai_strerror) on failure.
|
||||
*ss and *sslen are always defined when this function returns 0.
|
||||
|
||||
Resolve the hostname with DNS only if global o.proxydns includes PROXYDNS_LOCAL. */
|
||||
int proxyresolve(const char *hostname, unsigned short port,
|
||||
struct sockaddr_storage *ss, size_t *sslen, int af);
|
||||
|
||||
/* Resolves the given hostname or IP address with getaddrinfo, and stores
|
||||
all results into a linked list.
|
||||
The rest of behavior is same as resolve(). */
|
||||
|
||||
@@ -327,6 +327,7 @@ int main(int argc, char *argv[])
|
||||
{"proxy", required_argument, NULL, 0},
|
||||
{"proxy-type", required_argument, NULL, 0},
|
||||
{"proxy-auth", required_argument, NULL, 0},
|
||||
{"proxy-dns", required_argument, NULL, 0},
|
||||
{"nsock-engine", required_argument, NULL, 0},
|
||||
{"test", no_argument, NULL, 0},
|
||||
{"ssl", no_argument, &o.ssl, 1},
|
||||
@@ -490,6 +491,17 @@ int main(int argc, char *argv[])
|
||||
if (o.proxy_auth)
|
||||
bye("You can't specify more than one --proxy-auth.");
|
||||
o.proxy_auth = optarg;
|
||||
} else if (strcmp(long_options[option_index].name, "proxy-dns") == 0) {
|
||||
if (strcmp(optarg, "none") == 0)
|
||||
o.proxydns = 0;
|
||||
else if (strcmp(optarg, "local") == 0)
|
||||
o.proxydns = PROXYDNS_LOCAL;
|
||||
else if (strcmp(optarg, "remote") == 0)
|
||||
o.proxydns = PROXYDNS_REMOTE;
|
||||
else if (strcmp(optarg, "both") == 0)
|
||||
o.proxydns = PROXYDNS_LOCAL | PROXYDNS_REMOTE;
|
||||
else
|
||||
bye("Invalid proxy DNS type.");
|
||||
} 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);
|
||||
@@ -647,6 +659,7 @@ int main(int argc, char *argv[])
|
||||
" --proxy <addr[:port]> Specify address of host to proxy through\n"
|
||||
" --proxy-type <type> Specify proxy type (\"http\", \"socks4\", \"socks5\")\n"
|
||||
" --proxy-auth <auth> Authenticate with HTTP or SOCKS proxy server\n"
|
||||
" --proxy-dns <type> Specify where to resolve proxy destination\n"
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
" --ssl Connect or listen with SSL\n"
|
||||
|
||||
Reference in New Issue
Block a user