1
0
mirror of https://github.com/nmap/nmap.git synced 2026-02-07 05:56:34 +00:00

Merge 30432:30436 from /nmap-exp/david/ipv6-ranges.

This is simple IPv6 unicast ranges. For example,
nmap -6 en.wikipedia.org/120 -sn

The other, more complicated part of this overall change is automatic
multicast scanning of large local subnets. That part isn't done yet.
This commit is contained in:
david
2012-12-19 01:10:39 +00:00
parent f5de2d9419
commit 93b978fba8
7 changed files with 169 additions and 102 deletions

View File

@@ -115,7 +115,60 @@ void TargetGroup::Initialize() {
memset(addresses, 0, sizeof(addresses));
memset(current, 0, sizeof(current));
memset(last, 0, sizeof(last));
ipsleft = 0;
exhausted = true;
}
/* Return a newly allocated string containing the part of expr up to the last
'/' (or a copy of the whole string if there is no slash). *bits will contain
the number after the slash, or -1 if there was no slash. In case of error
return NULL; *bits is then undefined. */
static char *split_netmask(const char *expr, int *bits) {
const char *slash;
slash = strrchr(expr, '/');
if (slash != NULL) {
long l;
char *tail;
l = parse_long(slash + 1, &tail);
if (tail == slash + 1 || *tail != '\0' || l < 0 || l > INT_MAX)
return NULL;
*bits = (int) l;
} else {
slash = expr + strlen(expr);
*bits = -1;
}
return mkstr(expr, slash);
}
/* Fill in an in6_addr with a CIDR-style netmask with the given number of bits. */
static void make_ipv6_netmask(struct in6_addr *mask, int bits) {
unsigned int i;
memset(mask, 0, sizeof(*mask));
if (bits < 0)
bits = 0;
else if (bits > 128)
bits = 128;
if (bits == 0)
return;
i = 0;
/* 0 < bits <= 128, so this loop goes at most 15 times. */
for ( ; bits > 8; bits -= 8)
mask->s6_addr[i++] = 0xFF;
mask->s6_addr[i] = 0xFF << (8 - bits);
}
/* a = (a & mask) | (b & ~mask) */
static void ipv6_or_mask(struct in6_addr *a, const struct in6_addr *mask, const struct in6_addr *b) {
unsigned int i;
for (i = 0; i < sizeof(a->s6_addr) / sizeof(*a->s6_addr); i++)
a->s6_addr[i] = (a->s6_addr[i] & mask->s6_addr[i]) | (b->s6_addr[i] & ~mask->s6_addr[i]);
}
/* Initializes (or reinitializes) the object with a new expression, such
@@ -125,46 +178,44 @@ int TargetGroup::parse_expr(const char *target_expr, int af) {
int i = 0, j = 0, k = 0;
int start, end;
char *r, *s, *target_net;
char *r, *s;
char *addy[5];
char *hostexp = strdup(target_expr);
char *hostexp;
int bits;
namedhost = 0;
if (targets_type != TYPE_NONE)
Initialize();
ipsleft = 0;
resolvedaddrs.clear();
hostexp = split_netmask(target_expr, &bits);
if (hostexp == NULL) {
error("Unable to split netmask from target expression: \"%s\"", target_expr);
goto bail;
}
if (af == AF_INET) {
if (strchr(hostexp, ':')) {
error("Invalid host expression: %s -- colons only allowed in IPv6 addresses, and then you need the -6 switch", hostexp);
return 1;
goto bail;
}
/*struct in_addr current_in;*/
addy[0] = addy[1] = addy[2] = addy[3] = addy[4] = NULL;
addy[0] = r = hostexp;
/* First we break the expression up into the four parts of the IP address
+ the optional '/mask' */
target_net = hostexp;
s = strchr(hostexp, '/'); /* Find the slash if there is one */
if (s) {
char *tail;
long netmask_long;
*s = '\0'; /* Make sure target_net is terminated before the /## */
s++; /* Point s at the netmask */
netmask_long = parse_long(s, (char**) &tail);
if (*tail != '\0' || tail == s || netmask_long < 0 || netmask_long > 32) {
error("Illegal netmask value, must be /0 - /32 . Assuming /32 (one host)");
netmask = 32;
} else
netmask = (u32) netmask_long;
} else
/* Check the netmask. */
if (bits == -1) {
netmask = 32;
} else if (0 <= bits && bits <= 32) {
netmask = bits;
} else {
error("Illegal netmask value, must be /0 - /32 . Assuming /32 (one host)");
netmask = 32;
}
resolvedname = hostexp;
for (i = 0; hostexp[i]; i++)
if (isupper((int) (unsigned char) hostexp[i]) ||
@@ -178,7 +229,7 @@ int TargetGroup::parse_expr(const char *target_expr, int af) {
size_t sslen;
targets_type = IPV4_NETMASK;
addrs = resolve_all(target_net, AF_INET);
addrs = resolve_all(hostexp, AF_INET);
for (addr = addrs; addr != NULL; addr = addr->ai_next) {
if (addr->ai_family != AF_INET)
continue;
@@ -191,16 +242,15 @@ int TargetGroup::parse_expr(const char *target_expr, int af) {
freeaddrinfo(addrs);
if (resolvedaddrs.empty()) {
error("Failed to resolve given hostname/IP: %s. Note that you can't use '/mask' AND '1-4,7,100-' style IP ranges. If the machine only has an IPv6 address, add the Nmap -6 flag to scan that.", target_net);
free(hostexp);
return 1;
error("Failed to resolve given hostname/IP: %s. Note that you can't use '/mask' AND '1-4,7,100-' style IP ranges. If the machine only has an IPv6 address, add the Nmap -6 flag to scan that.", hostexp);
goto bail;
} else {
ss = *resolvedaddrs.begin();
sslen = sizeof(ss);
}
if (resolvedaddrs.size() > 1 && o.verbose > 1)
error("Warning: Hostname %s resolves to %lu IPs. Using %s.", target_net, (unsigned long)resolvedaddrs.size(), inet_ntop_ez(&ss, sslen));
error("Warning: Hostname %s resolves to %lu IPs. Using %s.", hostexp, (unsigned long)resolvedaddrs.size(), inet_ntop_ez(&ss, sslen));
if (netmask) {
struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
@@ -215,14 +265,10 @@ int TargetGroup::parse_expr(const char *target_expr, int af) {
endaddr.s_addr = 0xffffffff;
}
currentaddr = startaddr;
if (startaddr.s_addr <= endaddr.s_addr) {
ipsleft = ((unsigned long long) (endaddr.s_addr - startaddr.s_addr)) + 1;
free(hostexp);
return 0;
}
if (startaddr.s_addr <= endaddr.s_addr)
goto done;
fprintf(stderr, "Host specification invalid");
free(hostexp);
return 1;
goto bail;
} else {
targets_type = IPV4_RANGES;
i = 0;
@@ -232,14 +278,14 @@ int TargetGroup::parse_expr(const char *target_expr, int af) {
*r = '\0';
addy[i] = r + 1;
} else if (*r != '*' && *r != ',' && *r != '-' && !isdigit((int) (unsigned char) *r)) {
error("Invalid character in host specification: %s. Note in particular that square brackets [] are no longer allowed. They were redundant and can simply be removed.", target_expr);
return 1;
error("Invalid character in host specification: %s. Note in particular that square brackets [] are no longer allowed. They were redundant and can simply be removed.", hostexp);
goto bail;
}
r++;
}
if (i != 3) {
error("Invalid target host specification: %s", target_expr);
return 1;
error("Invalid target host specification: %s", hostexp);
goto bail;
}
for (i = 0; i < 4; i++) {
@@ -263,11 +309,11 @@ int TargetGroup::parse_expr(const char *target_expr, int af) {
* log_write(LOG_STDOUT, "The first host is %d, and the last one is %d\n", start, end); */
if (start < 0 || start > end || start > 255 || end > 255) {
error("Your host specifications are illegal!");
return 1;
goto bail;
}
if (j + (end - start) > 255) {
error("Your host specifications are illegal!");
return 1;
goto bail;
}
for (k = start; k <= end; k++)
addresses[i][j++] = k;
@@ -277,10 +323,6 @@ int TargetGroup::parse_expr(const char *target_expr, int af) {
}
}
memset((char *)current, 0, sizeof(current));
ipsleft = (unsigned long long) (last[0] + 1) *
(unsigned long long) (last[1] + 1) *
(unsigned long long) (last[2] + 1) *
(unsigned long long) (last[3] + 1);
} else {
#if HAVE_IPV6
struct addrinfo *addrs, *addr;
@@ -288,16 +330,21 @@ int TargetGroup::parse_expr(const char *target_expr, int af) {
size_t sslen;
assert(af == AF_INET6);
if (strchr(hostexp, '/')) {
error("Invalid host expression: %s -- slash not allowed. IPv6 addresses can currently only be specified individually", hostexp);
free(hostexp);
return 1;
/* Check the netmask. */
if (bits == -1) {
netmask = 128;
} else if (0 <= bits && bits <= 128) {
netmask = bits;
} else {
error("Illegal netmask value, must be /0 - /128 . Assuming /128 (one host)");
netmask = 128;
}
resolvedname = hostexp;
if (strchr(hostexp, ':') == NULL)
namedhost = 1;
targets_type = IPV6_ADDRESS;
targets_type = IPV6_NETMASK;
addrs = resolve_all(hostexp, AF_INET6);
for (addr = addrs; addr != NULL; addr = addr->ai_next) {
if (addr->ai_family != AF_INET6)
@@ -312,8 +359,7 @@ int TargetGroup::parse_expr(const char *target_expr, int af) {
if (resolvedaddrs.empty()) {
error("Failed to resolve given IPv6 hostname/IP: %s. Note that you can't use '/mask' or '[1-4,7,100-]' style ranges for IPv6.", hostexp);
free(hostexp);
return 1;
goto bail;
} else {
ss = *resolvedaddrs.begin();
sslen = sizeof(ss);
@@ -325,14 +371,32 @@ int TargetGroup::parse_expr(const char *target_expr, int af) {
assert(sizeof(ip6) <= sslen);
memcpy(&ip6, &ss, sizeof(ip6));
ipsleft = 1;
const struct in6_addr zeros = { { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} } };
const struct in6_addr ones = { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} } };
struct in6_addr mask;
make_ipv6_netmask(&mask, netmask);
startaddr6 = ((struct sockaddr_in6 *) &ss)->sin6_addr;
ipv6_or_mask(&startaddr6, &mask, &zeros);
endaddr6 = ((struct sockaddr_in6 *) &ss)->sin6_addr;
ipv6_or_mask(&endaddr6, &mask, &ones);
currentaddr6 = startaddr6;
#else // HAVE_IPV6
fatal("IPv6 not supported on your platform");
#endif // HAVE_IPV6
}
done:
free(hostexp);
exhausted = false;
return 0;
bail:
free(hostexp);
exhausted = true;
return 1;
}
/* Get the sin6_scope_id member of a sockaddr_in6, based on a device name. This
@@ -365,7 +429,7 @@ startover: /* to handle nmap --resume where I have already
assert(sslen);
if (ipsleft == 0)
if (exhausted)
return -1;
if (targets_type == IPV4_NETMASK) {
@@ -376,11 +440,13 @@ startover: /* to handle nmap --resume where I have already
sin->sin_len = *sslen;
#endif
if (currentaddr.s_addr == endaddr.s_addr)
exhausted = true;
if (currentaddr.s_addr <= endaddr.s_addr) {
sin->sin_addr.s_addr = htonl(currentaddr.s_addr++);
} else {
error("Bogus target structure passed to %s", __func__);
ipsleft = 0;
exhausted = true;
return -1;
}
} else if (targets_type == IPV4_RANGES) {
@@ -412,7 +478,7 @@ startover: /* to handle nmap --resume where I have already
}
if (octet == -1) {
/* It didn't find anything to bump up, I must have taken the last IP */
assert(ipsleft == 1);
exhausted = true;
/* So I set current to last with the very final octet up one ... */
/* Note that this may make current[3] == 256 */
current[0] = last[0];
@@ -420,11 +486,10 @@ startover: /* to handle nmap --resume where I have already
current[2] = last[2];
current[3] = last[3] + 1;
} else {
assert(ipsleft > 1); /* There must be at least one more IP left */
assert(!exhausted); /* There must be at least one more IP left */
}
} else {
assert(targets_type == IPV6_ADDRESS);
assert(ipsleft == 1);
assert(targets_type == IPV6_NETMASK);
#if HAVE_IPV6
*sslen = sizeof(struct sockaddr_in6);
memset(sin6, 0, *sslen);
@@ -432,16 +497,26 @@ startover: /* to handle nmap --resume where I have already
#ifdef SIN_LEN
sin6->sin6_len = *sslen;
#endif /* SIN_LEN */
memcpy(sin6->sin6_addr.s6_addr, ip6.sin6_addr.s6_addr, 16);
if (memcmp(currentaddr6.s6_addr, endaddr6.s6_addr, 16) == 0)
exhausted = true;
sin6->sin6_addr = currentaddr6;
if (ip6.sin6_scope_id == 0)
sin6->sin6_scope_id = get_scope_id(o.device);
else
sin6->sin6_scope_id = ip6.sin6_scope_id;
/* Increment current address. */
for (int i = 15; i >= 0; i--) {
currentaddr6.s6_addr[i]++;
if (currentaddr6.s6_addr[i] > 0)
break;
}
#else
fatal("IPV6 not supported on this platform");
#endif // HAVE_IPV6
}
ipsleft--;
/* If we are resuming from a previous scan, we have already finished
scans up to o.resume_ip. */
@@ -461,7 +536,7 @@ startover: /* to handle nmap --resume where I have already
int TargetGroup::return_last_host() {
int octet;
ipsleft++;
exhausted = false;
if (targets_type == IPV4_NETMASK) {
assert(currentaddr.s_addr > startaddr.s_addr);
currentaddr.s_addr--;
@@ -478,8 +553,7 @@ int TargetGroup::return_last_host() {
}
assert(octet != -1);
} else {
assert(targets_type == IPV6_ADDRESS);
assert(ipsleft == 1);
assert(targets_type == IPV6_NETMASK);
}
return 0;
}
@@ -494,7 +568,7 @@ bool TargetGroup::is_resolved_address(const struct sockaddr_storage *ss) {
return false;
/* We only have a single distinguished address for these target types.
IPV4_RANGES doesn't, for example. */
if (!(targets_type == IPV4_NETMASK || targets_type == IPV6_ADDRESS))
if (!(targets_type == IPV4_NETMASK || targets_type == IPV6_NETMASK))
return false;
resolvedaddr = *resolvedaddrs.begin();

View File

@@ -107,7 +107,7 @@
class TargetGroup {
public:
/* used by get_target_types */
enum _targets_types { TYPE_NONE, IPV4_NETMASK, IPV4_RANGES, IPV6_ADDRESS };
enum _targets_types { TYPE_NONE, IPV4_NETMASK, IPV4_RANGES, IPV6_NETMASK };
/* used as input to skip range */
enum _octet_nums { FIRST_OCTET, SECOND_OCTET, THIRD_OCTET };
TargetGroup();
@@ -152,16 +152,13 @@ private:
enum _targets_types targets_type;
void Initialize();
#if HAVE_IPV6
struct sockaddr_in6 ip6;
#endif
std::list<struct sockaddr_storage> resolvedaddrs;
u32 netmask;
std::string resolvedname;
/* These are used for the '/mask' style of specifying target
net (IPV4_NETMASK) */
u32 netmask;
std::string resolvedname;
struct in_addr startaddr;
struct in_addr currentaddr;
struct in_addr endaddr;
@@ -171,9 +168,18 @@ private:
unsigned int current[4];
u8 last[4];
/* Number of IPs left in this structure -- set to 0 if the fields are not
valid */
unsigned long long ipsleft;
#if HAVE_IPV6
/* These are used for the '/mask' style of specifying target
net (IPV6_NETMASK) */
struct sockaddr_in6 ip6;
struct in6_addr startaddr6;
struct in6_addr currentaddr6;
struct in6_addr endaddr6;
#endif
/* Is set to true iff all the addresses in this group have already been
returned. */
bool exhausted;
/* is the current target expression a named host? */
int namedhost;

View File

@@ -399,6 +399,8 @@ int Vsnprintf(char *, size_t, const char *, va_list)
int Snprintf(char *, size_t, const char *, ...)
__attribute__ ((format (printf, 3, 4)));
char *mkstr(const char *start, const char *end);
int alloc_vsprintf(char **strp, const char *fmt, va_list va)
__attribute__ ((format (printf, 2, 0)));

View File

@@ -93,6 +93,7 @@
/* $Id$ */
#include "nbase.h"
#include <assert.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
@@ -164,6 +165,18 @@ int Snprintf(char *s, size_t n, const char *fmt, ...)
return ret;
}
/* Make a new allocated null-terminated string from the bytes [start, end). */
char *mkstr(const char *start, const char *end) {
char *s;
assert(end >= start);
s = (char *) safe_malloc(end - start + 1);
memcpy(s, start, end - start);
s[end - start] = '\0';
return s;
}
/* vsprintf into a dynamically allocated buffer, similar to asprintf in
Glibc. Return the length of the buffer or -1 on error. */
int alloc_vsprintf(char **strp, const char *fmt, va_list va) {

View File

@@ -263,19 +263,6 @@ int strbuf_sprintf(char **buf, size_t *size, size_t *offset, const char *fmt, ..
return n;
}
/* Make a new allocated null-terminated string from the bytes [start, end). */
char *mkstr(const char *start, const char *end)
{
char *s;
assert(end >= start);
s = (char *) safe_malloc(end - start + 1);
memcpy(s, start, end - start);
s[end - start] = '\0';
return s;
}
/* Return true if the given address is a local one. */
int addr_is_local(const union sockaddr_u *su)
{

View File

@@ -142,8 +142,6 @@ int strbuf_append_str(char **buf, size_t *size, size_t *offset, const char *s);
int strbuf_sprintf(char **buf, size_t *size, size_t *offset, const char *fmt, ...)
__attribute__ ((format (printf, 4, 5)));
char *mkstr(const char *start, const char *end);
int addr_is_local(const union sockaddr_u *su);
const char *inet_socktop(const union sockaddr_u *su);

View File

@@ -290,19 +290,6 @@ ServiceProbeMatch::~ServiceProbeMatch() {
matchops_anchor = -1;
}
/* Make a new allocated null-terminated string from the bytes [start, end). */
static char *mkstr(const char *start, const char *end)
{
char *s;
assert(end >= start);
s = (char *) safe_malloc(end - start + 1);
memcpy(s, start, end - start);
s[end - start] = '\0';
return s;
}
/* Realloc a malloc-allocated string and put a given prefix at the front. */
static char *string_prefix(char *string, const char *prefix)
{