1
0
mirror of https://github.com/nmap/nmap.git synced 2026-01-05 22:19:03 +00:00

Ncat supports DNS failover, including fallback to IPv4 from IPv6

This commit is contained in:
dmiller
2017-03-16 21:54:26 +00:00
parent bbfb5dfed6
commit 4629f6d836
8 changed files with 223 additions and 97 deletions

View File

@@ -1,5 +1,10 @@
# Nmap Changelog ($Id$); -*-text-*-
o [Ncat][GH#157] Ncat will now continue trying to connect to each resolved
address for a hostname before declaring the connection refused, allowing it
to fallback from IPv6 to IPv4 or to connect to names that use DNS failover.
[Jaromir Koncicky, Michal Hlavinka]
o [NSE][GH#743] New script broadcast-ospf2-discover discovers OSPF 2 routers
and neighbors. OSPFv2 authentication is supported. [Emiliano Ticci]

View File

@@ -137,16 +137,19 @@ the tool? Many of these examples suppose a Unix environment. -->
</para>
<para>
By default, Ncat uses TCP and IPv4. The option
By default, Ncat uses TCP. The option
<option>--udp</option><indexterm><primary><option>--udp</option> (Ncat option)</primary></indexterm>
or
<option>-u</option><indexterm><primary><option>-u</option> (Ncat option)</primary><see><option>--udp</option></see></indexterm>
enables UDP instead,
<option>--sctp</option><indexterm><primary><option>--sctp</option> (Ncat option)</primary></indexterm>
enables SCTP,<indexterm><primary>SCTP</primary><secondary>in Ncat</secondary></indexterm>
and
<option>--sctp</option><indexterm><primary><option>--sctp</option> (Ncat option)</primary></indexterm>
enables SCTP.<indexterm><primary>SCTP</primary><secondary>in Ncat</secondary></indexterm>
Ncat listens on both IPv4 and IPv6, and connects to either address family as well. The
<option>-6</option><indexterm><primary><option>-6</option> (Ncat option)</primary></indexterm>
enables IPv6. See <xref linkend="ncat-protocols"/> for more details.
option forces IPv6-only, and
<option>-4</option><indexterm><primary><option>-4</option> (Ncat option)</primary></indexterm>
forces IPv4-only. See <xref linkend="ncat-protocols"/> for more details.
The rest of this guide documents all the Ncat options through
descriptions and examples. For a quick summary of options at any time,
run
@@ -331,8 +334,8 @@ Content-Type: text/html; charset=UTF-8
<para>
IPv4,<indexterm significance="preferred"><primary>IPv4</primary><secondary>in Ncat</secondary></indexterm>
the Internet Protocol version 4, is the dominant version of the
Internet Protocol in use. Ncat uses it by default. Using the
the Internet Protocol version 4, is the most popular version of the
Internet Protocol in use. Using the
<option>-4</option><indexterm><primary><option>-4 (Ncat option)</option></primary></indexterm>
puts Ncat into IPv4-only mode; only IPv4 addresses will be used even
if, for example, as hostname resolves to IPv6 addresses as well.
@@ -343,6 +346,8 @@ Content-Type: text/html; charset=UTF-8
is the lesser-used successor to IPv4. Use
<option>-6</option><indexterm><primary><option>-6</option> (Ncat option)</primary></indexterm>
to put Ncat into IPv6-only mode.
By default, Ncat will listen on both IPv4 and IPv6, and will connect to
resolved addresses in the order they are returned by the operating system.
</para>
</sect1>

View File

@@ -171,6 +171,7 @@ static struct conn_state cs = {
0
};
static void try_nsock_connect(nsock_pool nsp, struct sockaddr_list *conn_addr);
static void connect_handler(nsock_pool nsp, nsock_event evt, void *data);
static void post_connect(nsock_pool nsp, nsock_iod iod);
static void read_stdin_handler(nsock_pool nsp, nsock_event evt, void *data);
@@ -551,8 +552,8 @@ static int do_proxy_socks4(void)
socket_buffer_init(&stateful_buf, sd);
if (o.verbose) {
loguser("Connected to proxy %s:%hu\n", inet_socktop(&targetss),
inet_port(&targetss));
loguser("Connected to proxy %s:%hu\n", inet_socktop(&targetaddrs->addr),
inet_port(&targetaddrs->addr));
}
/* Fill the socks4_data struct */
@@ -652,8 +653,8 @@ static int do_proxy_socks5(void)
socket_buffer_init(&stateful_buf, sd);
if (o.verbose) {
loguser("Connected to proxy %s:%hu\n", inet_socktop(&targetss),
inet_port(&targetss));
loguser("Connected to proxy %s:%hu\n", inet_socktop(&targetaddrs->addr),
inet_port(&targetaddrs->addr));
}
zmem(&socks5msg,sizeof(socks5msg));
@@ -983,7 +984,7 @@ int ncat_connect(void)
if (o.af != AF_INET)
bye("Sorry, -g can only currently be used with IPv4.");
ipopts = buildsrcrte(targetss.in.sin_addr, o.srcrtes, o.numsrcrtes, o.srcrteptr, &ipoptslen);
ipopts = buildsrcrte(targetaddrs->addr.in.sin_addr, o.srcrtes, o.numsrcrtes, o.srcrteptr, &ipoptslen);
nsock_iod_set_ipoptions(cs.sock_nsi, ipopts, ipoptslen);
free(ipopts); /* Nsock has its own copy */
@@ -993,49 +994,18 @@ int ncat_connect(void)
if (o.af == AF_UNIX) {
if (o.proto == IPPROTO_UDP) {
nsock_connect_unixsock_datagram(mypool, cs.sock_nsi, connect_handler, NULL,
&targetss.sockaddr,
SUN_LEN((struct sockaddr_un *)&targetss.sockaddr));
&targetaddrs->addr.sockaddr,
SUN_LEN((struct sockaddr_un *)&targetaddrs->addr.sockaddr));
} else {
nsock_connect_unixsock_stream(mypool, cs.sock_nsi, connect_handler, o.conntimeout,
NULL, &targetss.sockaddr,
SUN_LEN((struct sockaddr_un *)&targetss.sockaddr));
NULL, &targetaddrs->addr.sockaddr,
SUN_LEN((struct sockaddr_un *)&targetaddrs->addr.sockaddr));
}
} else
#endif
if (o.proto == IPPROTO_UDP) {
nsock_connect_udp(mypool, cs.sock_nsi, connect_handler,
NULL, &targetss.sockaddr, targetsslen,
inet_port(&targetss));
}
#ifdef HAVE_OPENSSL
else if (o.proto == IPPROTO_SCTP && o.ssl) {
nsock_connect_ssl(mypool, cs.sock_nsi, connect_handler,
o.conntimeout, NULL,
&targetss.sockaddr, targetsslen,
IPPROTO_SCTP, inet_port(&targetss),
NULL);
}
#endif
else if (o.proto == IPPROTO_SCTP) {
nsock_connect_sctp(mypool, cs.sock_nsi, connect_handler,
o.conntimeout, NULL,
&targetss.sockaddr, targetsslen,
inet_port(&targetss));
}
#ifdef HAVE_OPENSSL
else if (o.ssl) {
nsock_connect_ssl(mypool, cs.sock_nsi, connect_handler,
o.conntimeout, NULL,
&targetss.sockaddr, targetsslen,
IPPROTO_TCP, inet_port(&targetss),
NULL);
}
#endif
else {
nsock_connect_tcp(mypool, cs.sock_nsi, connect_handler,
o.conntimeout, NULL,
&targetss.sockaddr, targetsslen,
inet_port(&targetss));
{
/* Add connection to first resolved address. */
try_nsock_connect(mypool, targetaddrs);
}
} else {
/* A proxy connection. */
@@ -1071,6 +1041,8 @@ int ncat_connect(void)
/* connect */
rc = nsock_loop(mypool, -1);
free_sockaddr_list(targetaddrs);
if (o.verbose) {
struct timeval end_time;
double time;
@@ -1094,6 +1066,45 @@ int ncat_connect(void)
return rc == NSOCK_LOOP_ERROR ? 1 : 0;
}
static void try_nsock_connect(nsock_pool nsp, struct sockaddr_list *conn_addr)
{
if (o.proto == IPPROTO_UDP) {
nsock_connect_udp(nsp, cs.sock_nsi, connect_handler, (void *)conn_addr->next,
&conn_addr->addr.sockaddr, conn_addr->addrlen,
inet_port(&conn_addr->addr));
}
#ifdef HAVE_OPENSSL
else if (o.proto == IPPROTO_SCTP && o.ssl) {
nsock_connect_ssl(nsp, cs.sock_nsi, connect_handler,
o.conntimeout, (void *)conn_addr->next,
&conn_addr->addr.sockaddr, conn_addr->addrlen,
IPPROTO_SCTP, inet_port(&conn_addr->addr),
NULL);
}
#endif
else if (o.proto == IPPROTO_SCTP) {
nsock_connect_sctp(nsp, cs.sock_nsi, connect_handler,
o.conntimeout, (void *)conn_addr->next,
&conn_addr->addr.sockaddr, conn_addr->addrlen,
inet_port(&conn_addr->addr));
}
#ifdef HAVE_OPENSSL
else if (o.ssl) {
nsock_connect_ssl(nsp, cs.sock_nsi, connect_handler,
o.conntimeout, (void *)conn_addr->next,
&conn_addr->addr.sockaddr, conn_addr->addrlen,
IPPROTO_TCP, inet_port(&conn_addr->addr),
NULL);
}
#endif
else {
nsock_connect_tcp(nsp, cs.sock_nsi, connect_handler,
o.conntimeout, (void *)conn_addr->next,
&conn_addr->addr.sockaddr, conn_addr->addrlen,
inet_port(&conn_addr->addr));
}
}
static void send_udp_null(nsock_pool nsp)
{
char *NULL_PROBE = "\0";
@@ -1105,17 +1116,31 @@ static void connect_handler(nsock_pool nsp, nsock_event evt, void *data)
{
enum nse_status status = nse_status(evt);
enum nse_type type = nse_type(evt);
struct sockaddr_list *next_addr = (struct sockaddr_list *)data;
ncat_assert(type == NSE_TYPE_CONNECT || type == NSE_TYPE_CONNECT_SSL);
if (status == NSE_STATUS_ERROR) {
if (!o.zerobyte||o.verbose)
loguser("%s.\n", socket_strerror(nse_errorcode(evt)));
exit(1);
} else if (status == NSE_STATUS_TIMEOUT) {
if (!o.zerobyte||o.verbose)
loguser("%s.\n", socket_strerror(ETIMEDOUT));
exit(1);
if (status == NSE_STATUS_ERROR || status == NSE_STATUS_TIMEOUT) {
int errcode = (status == NSE_STATUS_TIMEOUT)?ETIMEDOUT:nse_errorcode(evt);
/* If there are more resolved addresses, try connecting to next one */
if (next_addr != NULL) {
if (o.verbose) {
union sockaddr_u peer;
zmem(&peer, sizeof(peer.storage));
nsock_iod_get_communication_info(cs.sock_nsi, NULL, NULL, NULL,
&peer.sockaddr, sizeof(peer.storage));
loguser("Connection to %s failed: %s.\n", inet_socktop(&peer), socket_strerror(errcode));
loguser("Trying next address...\n");
}
try_nsock_connect(nsp, next_addr);
return;
}
else {
free_sockaddr_list(targetaddrs);
if (!o.zerobyte||o.verbose)
loguser("%s.\n", socket_strerror(errcode));
exit(1);
}
} else {
ncat_assert(status == NSE_STATUS_SUCCESS);
}

View File

@@ -153,8 +153,7 @@ int num_listenaddrs = 0;
union sockaddr_u srcaddr;
size_t srcaddrlen;
union sockaddr_u targetss;
size_t targetsslen;
struct sockaddr_list *targetaddrs;
/* Global options structure. */
struct options o;
@@ -220,18 +219,22 @@ void options_init(void)
}
/* Internal helper for resolve and resolve_numeric. addl_flags is ored into
hints.ai_flags, so you can add AI_NUMERICHOST. */
hints.ai_flags, so you can add AI_NUMERICHOST.
sl is a pointer to first element of sockaddr linked list, which is always
statically allocated. Next list elements are dynamically allocated.
If multiple_addrs is false then only first address is returned. */
static int resolve_internal(const char *hostname, unsigned short port,
struct sockaddr_storage *ss, size_t *sslen, int af, int addl_flags)
struct sockaddr_list *sl, int af, int addl_flags, int multiple_addrs)
{
struct addrinfo hints;
struct addrinfo *result;
struct addrinfo *next;
struct sockaddr_list **item_ptr = &sl;
struct sockaddr_list *new_item;
char portbuf[16];
int rc;
ncat_assert(hostname != NULL);
ncat_assert(ss != NULL);
ncat_assert(sslen != NULL);
memset(&hints, 0, sizeof(hints));
hints.ai_family = af;
@@ -248,8 +251,19 @@ static int resolve_internal(const char *hostname, unsigned short port,
if (result == NULL)
return EAI_NONAME;
ncat_assert(result->ai_addrlen > 0 && result->ai_addrlen <= (int) sizeof(struct sockaddr_storage));
*sslen = result->ai_addrlen;
memcpy(ss, result->ai_addr, *sslen);
for (next = result; next != NULL; next = next->ai_next) {
if (*item_ptr == NULL)
{
*item_ptr = (struct sockaddr_list *)safe_malloc(sizeof(struct sockaddr_list));
(**item_ptr).next = NULL;
}
new_item = *item_ptr;
new_item->addrlen = next->ai_addrlen;
memcpy(&new_item->addr.storage, next->ai_addr, next->ai_addrlen);
if (!multiple_addrs)
break;
item_ptr = &new_item->next;
}
freeaddrinfo(result);
return 0;
@@ -268,12 +282,42 @@ int resolve(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.nodns)
flags |= AI_NUMERICHOST;
return resolve_internal(hostname, port, ss, sslen, af, flags);
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(). */
int resolve_multi(const char *hostname, unsigned short port,
struct sockaddr_list *sl, int af)
{
int flags;
flags = 0;
if (o.nodns)
flags |= AI_NUMERICHOST;
return resolve_internal(hostname, port, sl, af, flags, 1);
}
void free_sockaddr_list(struct sockaddr_list *sl)
{
struct sockaddr_list *current, *next = sl;
while (next != NULL) {
current = next;
next = current->next;
free(current);
}
}
int fdinfo_close(struct fdinfo *fdn)

View File

@@ -139,14 +139,20 @@
a IPV4 INADDR_ANY and a IPV6 in6addr_any at most or a user defined address */
#define NUM_LISTEN_ADDRS 2
/* Structure to store a linked list of resolved addresses. */
struct sockaddr_list {
union sockaddr_u addr;
size_t addrlen;
struct sockaddr_list* next;
};
extern union sockaddr_u listenaddrs[NUM_LISTEN_ADDRS];
extern int num_listenaddrs;
extern union sockaddr_u srcaddr;
extern size_t srcaddrlen;
extern union sockaddr_u targetss;
extern size_t targetsslen;
extern struct sockaddr_list *targetaddrs;
enum exec_mode {
EXEC_PLAIN,
@@ -235,6 +241,14 @@ 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
all results into a linked list.
The rest of behavior is same as resolve(). */
int resolve_multi(const char *hostname, unsigned short port,
struct sockaddr_list *sl, int af);
void free_sockaddr_list(struct sockaddr_list *sl);
int fdinfo_close(struct fdinfo *fdn);
int fdinfo_recv(struct fdinfo *fdn, char *buf, size_t size);
int fdinfo_send(struct fdinfo *fdn, const char *buf, size_t size);

View File

@@ -688,15 +688,18 @@ int main(int argc, char *argv[])
}
#endif /* HAVE_SYS_UN_H */
/* Create a static target address, because at least one target address must be always allocated */
targetaddrs = (struct sockaddr_list *)safe_zalloc(sizeof(struct sockaddr_list));
/* 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;
srcaddr.storage = targetss.storage;
memset(&srcaddr.storage, 0, sizeof(srcaddr.storage));
srcaddr.storage.ss_family = AF_UNSPEC;
targetaddrs->addr.storage = srcaddr.storage;
/* Clear the listenaddrs array */
int i;
for (i = 0; i < NUM_LISTEN_ADDRS; i++) {
listenaddrs[i].storage = targetss.storage;
listenaddrs[i].storage = srcaddr.storage;
}
if (o.proxyaddr) {
@@ -712,12 +715,12 @@ int main(int argc, char *argv[])
* (due to the colons in the IPv6 address and host:port separator).
*/
targetsslen = parseproxy(o.proxyaddr,
&targetss.storage, &targetsslen, &proxyport);
targetaddrs->addrlen = parseproxy(o.proxyaddr,
&targetaddrs->addr.storage, &targetaddrs->addrlen, &proxyport);
if (o.af == AF_INET) {
targetss.in.sin_port = htons(proxyport);
targetaddrs->addr.in.sin_port = htons(proxyport);
} else { // might modify to else if and test AF_{INET6|UNIX|UNSPEC}
targetss.in6.sin6_port = htons(proxyport);
targetaddrs->addr.in6.sin6_port = htons(proxyport);
}
} else {
bye("Invalid proxy type \"%s\".", o.proxytype);
@@ -796,10 +799,10 @@ int main(int argc, char *argv[])
} else {
#if HAVE_SYS_UN_H
if (o.af == AF_UNIX) {
memset(&targetss.storage, 0, sizeof(struct sockaddr_un));
targetss.un.sun_family = AF_UNIX;
strncpy(targetss.un.sun_path, argv[optind], sizeof(targetss.un.sun_path));
targetsslen = SUN_LEN(&targetss.un);
memset(&targetaddrs->addr.storage, 0, sizeof(struct sockaddr_un));
targetaddrs->addr.un.sun_family = AF_UNIX;
strncpy(targetaddrs->addr.un.sun_path, argv[optind], sizeof(targetaddrs->addr.un.sun_path));
targetaddrs->addrlen = SUN_LEN(&targetaddrs->addr.un);
o.target = argv[optind];
optind++;
} else
@@ -813,7 +816,7 @@ int main(int argc, char *argv[])
* 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)
&& (rc = resolve_multi(o.target, 0, targetaddrs, o.af)) != 0)
bye("Could not resolve hostname \"%s\": %s.", o.target, gai_strerror(rc));
optind++;
@@ -851,21 +854,28 @@ int main(int argc, char *argv[])
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);
else {
struct sockaddr_list *targetaddrs_item = targetaddrs;
while (targetaddrs_item != NULL)
{
if (targetaddrs_item->addr.storage.ss_family == AF_INET)
targetaddrs_item->addr.in.sin_port = htons(o.portno);
#ifdef HAVE_IPV6
else if (targetss.storage.ss_family == AF_INET6)
targetss.in6.sin6_port = htons(o.portno);
else if (targetaddrs_item->addr.storage.ss_family == AF_INET6)
targetaddrs_item->addr.in6.sin6_port = htons(o.portno);
#endif
#if HAVE_SYS_UN_H
/* If we use Unix domain sockets, we have to count with them. */
else if (targetss.storage.ss_family == AF_UNIX)
; /* Do nothing. */
/* If we use Unix domain sockets, we have to count with them. */
else if (targetaddrs_item->addr.storage.ss_family == AF_UNIX)
; /* Do nothing. */
#endif
else if (targetss.storage.ss_family == AF_UNSPEC)
; /* Leave unspecified. */
else
bye("Unknown address family %d.", targetss.storage.ss_family);
else if (targetaddrs_item->addr.storage.ss_family == AF_UNSPEC)
; /* Leave unspecified. */
else
bye("Unknown address family %d.", targetaddrs_item->addr.storage.ss_family);
targetaddrs_item = targetaddrs_item->next;
}
}
if (srcport != -1) {
if (o.listen) {
@@ -877,7 +887,7 @@ int main(int argc, char *argv[])
/* We have a source port but not an explicit source address;
fill in an unspecified address of the same family as the
target. */
srcaddr.storage.ss_family = targetss.storage.ss_family;
srcaddr.storage.ss_family = targetaddrs->addr.storage.ss_family;
if (srcaddr.storage.ss_family == AF_INET)
srcaddr.in.sin_addr.s_addr = INADDR_ANY;
else if (srcaddr.storage.ss_family == AF_INET6)
@@ -969,8 +979,8 @@ static int ncat_listen_mode(void)
bye("/bin/sh is not executable, so `-c' won't work.");
#endif
if (targetss.storage.ss_family != AF_UNSPEC) {
listenaddrs[num_listenaddrs++] = targetss;
if (targetaddrs->addr.storage.ss_family != AF_UNSPEC) {
listenaddrs[num_listenaddrs++] = targetaddrs->addr;
} else {
size_t ss_len;
int rc;

View File

@@ -600,6 +600,29 @@ sub {
};
kill_children;
($s_pid, $s_out, $s_in) = ncat("-4", "-lk");
test "Connect fallback with IPv4 server",
sub {
my $resp;
my ($c_pid, $c_out, $c_in) = ncat("localhost");
syswrite($c_in, "abc\n");
$resp = timeout_read($s_out);
$resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
};
($s_pid, $s_out, $s_in) = ncat("-6", "-lk");
test "Connect fallback with IPv6 server",
sub {
my $resp;
my ($c_pid, $c_out, $c_in) = ncat("localhost");
syswrite($c_in, "abc\n");
$resp = timeout_read($s_out);
$resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
};
kill_children;
# Test UNIX domain sockets listening
{
local $xfail = 1 if !$HAVE_UNIXSOCK;

View File

@@ -510,7 +510,7 @@ int do_connect(int type)
/* We need a socket that can be inherited by child processes in
ncat_exec_win.c, for --exec and --sh-exec. inheritable_socket is from
nbase. */
sock = inheritable_socket(targetss.storage.ss_family, type, 0);
sock = inheritable_socket(targetaddrs->addr.storage.ss_family, type, 0);
if (srcaddr.storage.ss_family != AF_UNSPEC) {
size_t sa_len;
@@ -527,7 +527,7 @@ int do_connect(int type)
}
if (sock != -1) {
if (connect(sock, &targetss.sockaddr, (int) targetsslen) != -1)
if (connect(sock, &targetaddrs->addr.sockaddr, (int) targetaddrs->addrlen) != -1)
return sock;
else if (socket_errno() == EINPROGRESS || socket_errno() == EAGAIN)
return sock;