mirror of
https://github.com/nmap/nmap.git
synced 2025-12-26 01:19:03 +00:00
Merge r30416:30527 from /nmap-exp/david/ipv6-ranges.
This is a refactoring of target parsing that stores different types of target specifications as different classes. The eventual intention is to allow easy iteration over each specification for the purpose of IPv6 multicast host discovery.
This commit is contained in:
884
TargetGroup.cc
884
TargetGroup.cc
@@ -101,23 +101,16 @@
|
||||
#include "global_structures.h"
|
||||
#include "libnetutil/netutil.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#define BITVECTOR_BITS (sizeof(bitvector_t) * CHAR_BIT)
|
||||
#define BIT_SET(v, n) ((v)[(n) / BITVECTOR_BITS] |= 1UL << ((n) % BITVECTOR_BITS))
|
||||
#define BIT_IS_SET(v, n) (((v)[(n) / BITVECTOR_BITS] & 1UL << ((n) % BITVECTOR_BITS)) != 0)
|
||||
|
||||
extern NmapOps o;
|
||||
|
||||
NewTargets *NewTargets::new_targets;
|
||||
|
||||
TargetGroup::TargetGroup() {
|
||||
Initialize();
|
||||
}
|
||||
|
||||
// Bring back (or start with) original state
|
||||
void TargetGroup::Initialize() {
|
||||
targets_type = TYPE_NONE;
|
||||
memset(addresses, 0, sizeof(addresses));
|
||||
memset(current, 0, sizeof(current));
|
||||
memset(last, 0, sizeof(last));
|
||||
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
|
||||
@@ -142,6 +135,346 @@ static char *split_netmask(const char *expr, int *bits) {
|
||||
return mkstr(expr, slash);
|
||||
}
|
||||
|
||||
/* Parse an IPv4 address with optional ranges and wildcards into bit vectors.
|
||||
Each octet must match the regular expression '(\*|#?(-#?)?(,#?(-#?)?)*)',
|
||||
where '#' stands for an integer between 0 and 255. Return 0 on success, -1 on
|
||||
error. */
|
||||
static int parse_ipv4_ranges(octet_bitvector octets[4], const char *spec) {
|
||||
const char *p;
|
||||
int octet_index, i;
|
||||
|
||||
p = spec;
|
||||
octet_index = 0;
|
||||
while (*p != '\0' && octet_index < 4) {
|
||||
if (*p == '*') {
|
||||
for (i = 0; i < 256; i++)
|
||||
BIT_SET(octets[octet_index], i);
|
||||
p++;
|
||||
} else {
|
||||
for (;;) {
|
||||
long start, end;
|
||||
char *tail;
|
||||
|
||||
errno = 0;
|
||||
start = parse_long(p, &tail);
|
||||
/* Is this a range open on the left? */
|
||||
if (tail == p) {
|
||||
if (*p == '-')
|
||||
start = 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
if (errno != 0 || start < 0 || start > 255)
|
||||
return -1;
|
||||
p = tail;
|
||||
|
||||
/* Look for a range. */
|
||||
if (*p == '-') {
|
||||
p++;
|
||||
errno = 0;
|
||||
end = parse_long(p, &tail);
|
||||
/* Is this range open on the right? */
|
||||
if (tail == p)
|
||||
end = 255;
|
||||
if (errno != 0 || end < 0 || end > 255 || end < start)
|
||||
return -1;
|
||||
p = tail;
|
||||
} else {
|
||||
end = start;
|
||||
}
|
||||
|
||||
/* Fill in the range in the bit vector. */
|
||||
for (i = start; i <= end; i++)
|
||||
BIT_SET(octets[octet_index], i);
|
||||
|
||||
if (*p != ',')
|
||||
break;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
octet_index++;
|
||||
if (octet_index < 4) {
|
||||
if (*p != '.')
|
||||
return -1;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
if (*p != '\0' || octet_index < 4)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NetBlock *parse_expr_without_netmask(const char *hostexp, int af) {
|
||||
struct sockaddr_storage ss;
|
||||
size_t sslen;
|
||||
|
||||
if (af == AF_INET) {
|
||||
NetBlockIPv4Ranges *netblock_ranges;
|
||||
|
||||
/* Check if this is an IPv4 address, with optional ranges and wildcards. */
|
||||
netblock_ranges = new NetBlockIPv4Ranges();
|
||||
if (parse_ipv4_ranges(netblock_ranges->octets, hostexp) == 0)
|
||||
return netblock_ranges;
|
||||
delete netblock_ranges;
|
||||
}
|
||||
|
||||
sslen = sizeof(ss);
|
||||
if (resolve_numeric(hostexp, 0, &ss, &sslen, AF_INET6) == 0) {
|
||||
NetBlockIPv6Netmask *netblock_ipv6;
|
||||
|
||||
netblock_ipv6 = new NetBlockIPv6Netmask();
|
||||
netblock_ipv6->set_addr((struct sockaddr_in6 *) &ss);
|
||||
return netblock_ipv6;
|
||||
}
|
||||
|
||||
return new NetBlockHostname(hostexp, af);
|
||||
}
|
||||
|
||||
/* Parses an expression such as 192.168.0.0/16, 10.1.0-5.1-254, or
|
||||
fe80::202:e3ff:fe14:1102/112 and returns a newly allocated NetBlock. The af
|
||||
parameter is AF_INET or AF_INET6. Returns NULL in case of error. */
|
||||
NetBlock *NetBlock::parse_expr(const char *target_expr, int af) {
|
||||
NetBlock *netblock;
|
||||
char *hostexp;
|
||||
int bits;
|
||||
|
||||
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 && bits > 32) {
|
||||
error("Illegal netmask in \"%s\". Assuming /32 (one host)", target_expr);
|
||||
bits = -1;
|
||||
}
|
||||
|
||||
netblock = parse_expr_without_netmask(hostexp, af);
|
||||
if (netblock == NULL)
|
||||
goto bail;
|
||||
netblock->apply_netmask(bits);
|
||||
|
||||
free(hostexp);
|
||||
return netblock;
|
||||
|
||||
bail:
|
||||
free(hostexp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool NetBlock::is_resolved_address(const struct sockaddr_storage *ss) const {
|
||||
if (this->resolvedaddrs.empty())
|
||||
return false;
|
||||
return sockaddr_storage_equal(&*this->resolvedaddrs.begin(), ss);
|
||||
}
|
||||
|
||||
bool NetBlockIPv4Ranges::next(struct sockaddr_storage *ss) {
|
||||
struct sockaddr_in *sin;
|
||||
unsigned int i;
|
||||
|
||||
/* This first time this is called, the current values of this->counter
|
||||
probably do not point to set bits (they point to 0.0.0.0). Find the first
|
||||
set bit in each bitvector. If any overflow occurs, it means that there is
|
||||
not bit set for one of the octets and therefore there are not addresses
|
||||
overall. */
|
||||
for (i = 0; i < 4; i++) {
|
||||
while (this->counter[i] < 256 && !BIT_IS_SET(this->octets[i], this->counter[i]))
|
||||
this->counter[i]++;
|
||||
if (this->counter[i] >= 256)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Assign the returned address based on current counters. */
|
||||
sin = (struct sockaddr_in *) ss;
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_port = 0;
|
||||
#if HAVE_SOCKADDR_SA_LEN
|
||||
sin->sin_len = sizeof(*ss);
|
||||
#endif
|
||||
sin->sin_addr.s_addr = htonl((this->counter[0] << 24) | (this->counter[1] << 16) | (this->counter[2] << 8) | this->counter[3]);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
bool carry;
|
||||
|
||||
carry = false;
|
||||
do {
|
||||
this->counter[3 - i] = (this->counter[3 - i] + 1) % 256;
|
||||
if (this->counter[3 - i] == 0)
|
||||
carry = true;
|
||||
} while (!BIT_IS_SET(this->octets[3 - i], this->counter[3 - i]));
|
||||
if (!carry)
|
||||
break;
|
||||
}
|
||||
if (i >= 4) {
|
||||
/* We cycled all counters. Mark them invalid for the next call. */
|
||||
this->counter[0] = 256;
|
||||
this->counter[1] = 256;
|
||||
this->counter[2] = 256;
|
||||
this->counter[3] = 256;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Expand a single-octet bit vector to include any additional addresses that
|
||||
result when mask is applied. */
|
||||
static void apply_ipv4_netmask_octet(octet_bitvector bits, uint8_t mask) {
|
||||
unsigned int i, j;
|
||||
uint32_t chunk_size;
|
||||
|
||||
/* Process the bit vector in chunks, first of size 1, then of size 2, up to
|
||||
size 128. Check the next bit of the mask. If it is 1, do nothing.
|
||||
Otherwise, pair up the chunks (first with the second, third with the
|
||||
fourth, etc.). For each pair of chunks, set a bit in one chunk if it is
|
||||
set in the other. chunk_size also serves as an index into the mask. */
|
||||
for (chunk_size = 1; chunk_size < 256; chunk_size <<= 1) {
|
||||
if ((mask & chunk_size) != 0)
|
||||
continue;
|
||||
for (i = 0; i < 256; i += chunk_size * 2) {
|
||||
for (j = 0; j < chunk_size; j++) {
|
||||
if (BIT_IS_SET(bits, i + j))
|
||||
BIT_SET(bits, i + j + chunk_size);
|
||||
else if (BIT_IS_SET(bits, i + j + chunk_size))
|
||||
BIT_SET(bits, i + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Expand IPv4 bit vectors to include any additional addresses that result when
|
||||
the given netmask is applied. The mask is in host byte order. */
|
||||
static void apply_ipv4_netmask(octet_bitvector octets[4], uint32_t mask) {
|
||||
/* Apply the mask one octet at a time. It's done this way because ranges
|
||||
span exactly one octet. */
|
||||
apply_ipv4_netmask_octet(octets[0], (mask & 0xFF000000) >> 24);
|
||||
apply_ipv4_netmask_octet(octets[1], (mask & 0x00FF0000) >> 16);
|
||||
apply_ipv4_netmask_octet(octets[2], (mask & 0x0000FF00) >> 8);
|
||||
apply_ipv4_netmask_octet(octets[3], (mask & 0x000000FF));
|
||||
}
|
||||
|
||||
/* Expand IPv4 bit vectors to include any additional addresses that result from
|
||||
the application of a CIDR-style netmask with the given number of bits. If
|
||||
bits is negative it is taken to be 32. */
|
||||
void NetBlockIPv4Ranges::apply_netmask(int bits) {
|
||||
uint32_t mask;
|
||||
|
||||
if (bits > 32)
|
||||
return;
|
||||
if (bits < 0)
|
||||
bits = 32;
|
||||
|
||||
if (bits == 0)
|
||||
mask = 0x00000000;
|
||||
else
|
||||
mask = 0xFFFFFFFF << (32 - bits);
|
||||
|
||||
apply_ipv4_netmask(this->octets, mask);
|
||||
}
|
||||
|
||||
static std::string bitvector_to_range_string(const octet_bitvector v) {
|
||||
unsigned int i, j;
|
||||
std::ostringstream result;
|
||||
|
||||
i = 0;
|
||||
while (i < 256) {
|
||||
while (i < 256 && !BIT_IS_SET(v, i))
|
||||
i++;
|
||||
if (i >= 256)
|
||||
break;
|
||||
j = i + 1;
|
||||
while (j < 256 && BIT_IS_SET(v, j))
|
||||
j++;
|
||||
|
||||
if (result.tellp() > 0)
|
||||
result << ",";
|
||||
if (i == j - 1)
|
||||
result << i;
|
||||
else if (i + 1 == j - 1)
|
||||
result << i << "," << (j - 1);
|
||||
else
|
||||
result << i << "-" << (j - 1);
|
||||
|
||||
i = j;
|
||||
}
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
std::string NetBlockIPv4Ranges::str() const {
|
||||
std::ostringstream result;
|
||||
|
||||
result << bitvector_to_range_string(this->octets[0]);
|
||||
result << ".";
|
||||
result << bitvector_to_range_string(this->octets[1]);
|
||||
result << ".";
|
||||
result << bitvector_to_range_string(this->octets[2]);
|
||||
result << ".";
|
||||
result << bitvector_to_range_string(this->octets[3]);
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
void NetBlockIPv6Netmask::set_addr(const struct sockaddr_in6 *addr) {
|
||||
this->exhausted = false;
|
||||
this->start = addr->sin6_addr;
|
||||
this->cur = addr->sin6_addr;
|
||||
this->end = addr->sin6_addr;
|
||||
}
|
||||
|
||||
/* Get the sin6_scope_id member of a sockaddr_in6, based on a device name. This
|
||||
is used to assign scope to all addresses that otherwise lack a scope id when
|
||||
the -e option is used. */
|
||||
static int get_scope_id(const char *devname) {
|
||||
struct interface_info *ii;
|
||||
|
||||
if (devname == NULL || devname[0] == '\0')
|
||||
return 0;
|
||||
ii = getInterfaceByName(devname, AF_INET6);
|
||||
if (ii != NULL)
|
||||
return ii->ifindex;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ipv6_equal(const struct in6_addr *a, const struct in6_addr *b) {
|
||||
return memcmp(a->s6_addr, b->s6_addr, 16) == 0;
|
||||
}
|
||||
|
||||
bool NetBlockIPv6Netmask::next(struct sockaddr_storage *ss) {
|
||||
struct sockaddr_in6 *sin6;
|
||||
|
||||
if (this->exhausted)
|
||||
return false;
|
||||
|
||||
sin6 = (struct sockaddr_in6 *) ss;
|
||||
memset(sin6, 0, sizeof(*sin6));
|
||||
|
||||
sin6->sin6_family = AF_INET6;
|
||||
#ifdef SIN_LEN
|
||||
sin6->sin6_len = sizeof(*sin6);
|
||||
#endif
|
||||
|
||||
if (this->addr.sin6_scope_id != 0)
|
||||
sin6->sin6_scope_id = this->addr.sin6_scope_id;
|
||||
else
|
||||
sin6->sin6_scope_id = get_scope_id(o.device);
|
||||
|
||||
sin6->sin6_addr = this->cur;
|
||||
|
||||
if (ipv6_equal(&this->cur, &this->end))
|
||||
exhausted = true;
|
||||
|
||||
/* Increment current address. */
|
||||
for (int i = 15; i >= 0; i--) {
|
||||
this->cur.s6_addr[i]++;
|
||||
if (this->cur.s6_addr[i] > 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
@@ -171,419 +504,141 @@ static void ipv6_or_mask(struct in6_addr *a, const struct in6_addr *mask, const
|
||||
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
|
||||
as 192.168.0.0/16 , 10.1.0-5.1-254 , or fe80::202:e3ff:fe14:1102 .
|
||||
Returns 0 for success */
|
||||
int TargetGroup::parse_expr(const char *target_expr, int af) {
|
||||
void NetBlockIPv6Netmask::apply_netmask(int bits) {
|
||||
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;
|
||||
|
||||
int i = 0, j = 0, k = 0;
|
||||
int start, end;
|
||||
char *r, *s;
|
||||
char *addy[5];
|
||||
char *hostexp;
|
||||
int bits;
|
||||
namedhost = 0;
|
||||
if (bits > 128)
|
||||
return;
|
||||
if (bits < 0)
|
||||
bits = 128;
|
||||
|
||||
if (targets_type != TYPE_NONE)
|
||||
Initialize();
|
||||
|
||||
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);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/*struct in_addr current_in;*/
|
||||
addy[0] = addy[1] = addy[2] = addy[3] = addy[4] = NULL;
|
||||
addy[0] = r = hostexp;
|
||||
|
||||
/* 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]) ||
|
||||
islower((int) (unsigned char) hostexp[i])) {
|
||||
namedhost = 1;
|
||||
break;
|
||||
}
|
||||
if (netmask != 32 || namedhost) {
|
||||
struct addrinfo *addrs, *addr;
|
||||
struct sockaddr_storage ss;
|
||||
size_t sslen;
|
||||
|
||||
targets_type = IPV4_NETMASK;
|
||||
addrs = resolve_all(hostexp, AF_INET);
|
||||
for (addr = addrs; addr != NULL; addr = addr->ai_next) {
|
||||
if (addr->ai_family != AF_INET)
|
||||
continue;
|
||||
if (addr->ai_addrlen < sizeof(ss)) {
|
||||
memcpy(&ss, addr->ai_addr, addr->ai_addrlen);
|
||||
resolvedaddrs.push_back(ss);
|
||||
}
|
||||
}
|
||||
if (addrs != NULL)
|
||||
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.", 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.", hostexp, (unsigned long)resolvedaddrs.size(), inet_ntop_ez(&ss, sslen));
|
||||
|
||||
if (netmask) {
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
|
||||
unsigned long longtmp = ntohl(sin->sin_addr.s_addr);
|
||||
startaddr.s_addr = longtmp & (unsigned long) (0 - (1 << (32 - netmask)));
|
||||
endaddr.s_addr = longtmp | (unsigned long) ((1 << (32 - netmask)) - 1);
|
||||
} else {
|
||||
/* The above calculations don't work for a /0 netmask, though at first
|
||||
* glance it appears that they would
|
||||
*/
|
||||
startaddr.s_addr = 0;
|
||||
endaddr.s_addr = 0xffffffff;
|
||||
}
|
||||
currentaddr = startaddr;
|
||||
if (startaddr.s_addr <= endaddr.s_addr)
|
||||
goto done;
|
||||
fprintf(stderr, "Host specification invalid");
|
||||
goto bail;
|
||||
} else {
|
||||
targets_type = IPV4_RANGES;
|
||||
i = 0;
|
||||
|
||||
while (*r) {
|
||||
if (*r == '.' && ++i < 4) {
|
||||
*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.", hostexp);
|
||||
goto bail;
|
||||
}
|
||||
r++;
|
||||
}
|
||||
if (i != 3) {
|
||||
error("Invalid target host specification: %s", hostexp);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
j = 0;
|
||||
do {
|
||||
s = strchr(addy[i], ',');
|
||||
if (s) *s = '\0';
|
||||
if (*addy[i] == '*') {
|
||||
start = 0;
|
||||
end = 255;
|
||||
} else if (*addy[i] == '-') {
|
||||
start = 0;
|
||||
if (*(addy[i] + 1) == '\0') end = 255;
|
||||
else end = atoi(addy[i] + 1);
|
||||
} else {
|
||||
start = end = atoi(addy[i]);
|
||||
if ((r = strchr(addy[i], '-')) && *(r + 1) ) end = atoi(r + 1);
|
||||
else if (r && !*(r + 1)) end = 255;
|
||||
}
|
||||
/* if (o.debugging > 2)
|
||||
* 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!");
|
||||
goto bail;
|
||||
}
|
||||
if (j + (end - start) > 255) {
|
||||
error("Your host specifications are illegal!");
|
||||
goto bail;
|
||||
}
|
||||
for (k = start; k <= end; k++)
|
||||
addresses[i][j++] = k;
|
||||
last[i] = j - 1;
|
||||
if (s) addy[i] = s + 1;
|
||||
} while (s);
|
||||
}
|
||||
}
|
||||
memset((char *)current, 0, sizeof(current));
|
||||
} else {
|
||||
#if HAVE_IPV6
|
||||
struct addrinfo *addrs, *addr;
|
||||
struct sockaddr_storage ss;
|
||||
size_t sslen;
|
||||
|
||||
assert(af == AF_INET6);
|
||||
|
||||
/* 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_NETMASK;
|
||||
addrs = resolve_all(hostexp, AF_INET6);
|
||||
for (addr = addrs; addr != NULL; addr = addr->ai_next) {
|
||||
if (addr->ai_family != AF_INET6)
|
||||
continue;
|
||||
if (addr->ai_addrlen < sizeof(ss)) {
|
||||
memcpy(&ss, addr->ai_addr, addr->ai_addrlen);
|
||||
resolvedaddrs.push_back(ss);
|
||||
}
|
||||
}
|
||||
if (addrs != NULL)
|
||||
freeaddrinfo(addrs);
|
||||
|
||||
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);
|
||||
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.", hostexp, (unsigned long)resolvedaddrs.size(), inet_ntop_ez(&ss, sslen));
|
||||
|
||||
assert(sizeof(ip6) <= sslen);
|
||||
memcpy(&ip6, &ss, sizeof(ip6));
|
||||
|
||||
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;
|
||||
this->exhausted = false;
|
||||
make_ipv6_netmask(&mask, bits);
|
||||
ipv6_or_mask(&this->start, &mask, &zeros);
|
||||
ipv6_or_mask(&this->end, &mask, &ones);
|
||||
this->cur = this->start;
|
||||
}
|
||||
|
||||
/* Get the sin6_scope_id member of a sockaddr_in6, based on a device name. This
|
||||
is used to assign scope to all addresses that otherwise lack a scope id when
|
||||
the -e option is used. */
|
||||
static int get_scope_id(const char *devname) {
|
||||
struct interface_info *ii;
|
||||
/* a = a & ~b */
|
||||
static void recover_ipv6_netmask(struct in6_addr *a, const struct in6_addr *b) {
|
||||
unsigned int i;
|
||||
|
||||
if (devname == NULL || devname[0] == '\0')
|
||||
return 0;
|
||||
ii = getInterfaceByName(devname, AF_INET6);
|
||||
if (ii != NULL)
|
||||
return ii->ifindex;
|
||||
else
|
||||
return 0;
|
||||
for (i = 0; i < sizeof(a->s6_addr) / sizeof(*a->s6_addr); i++)
|
||||
a->s6_addr[i] = a->s6_addr[i] & ~b->s6_addr[i];
|
||||
}
|
||||
|
||||
/* Grab the next host from this expression (if any) and updates its internal
|
||||
state to reflect that the IP was given out. Returns 0 and
|
||||
fills in ss if successful. ss must point to a pre-allocated
|
||||
sockaddr_storage structure */
|
||||
int TargetGroup::get_next_host(struct sockaddr_storage *ss, size_t *sslen) {
|
||||
static unsigned int count_ipv6_bits(const struct in6_addr *a) {
|
||||
unsigned int i, n;
|
||||
unsigned char mask;
|
||||
|
||||
int octet;
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *) ss;
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) ss;
|
||||
startover: /* to handle nmap --resume where I have already
|
||||
* scanned many of the IPs */
|
||||
assert(ss);
|
||||
assert(sslen);
|
||||
|
||||
|
||||
if (exhausted)
|
||||
return -1;
|
||||
|
||||
if (targets_type == IPV4_NETMASK) {
|
||||
memset(sin, 0, sizeof(struct sockaddr_in));
|
||||
sin->sin_family = AF_INET;
|
||||
*sslen = sizeof(struct sockaddr_in);
|
||||
#if HAVE_SOCKADDR_SA_LEN
|
||||
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__);
|
||||
exhausted = true;
|
||||
return -1;
|
||||
n = 0;
|
||||
for (i = 0; i < sizeof(a->s6_addr) / sizeof(*a->s6_addr); i++) {
|
||||
for (mask = 0x80; mask != 0; mask >>= 1) {
|
||||
if ((a->s6_addr[i] & mask) != 0)
|
||||
n++;
|
||||
}
|
||||
} else if (targets_type == IPV4_RANGES) {
|
||||
memset(sin, 0, sizeof(struct sockaddr_in));
|
||||
sin->sin_family = AF_INET;
|
||||
*sslen = sizeof(struct sockaddr_in);
|
||||
#if HAVE_SOCKADDR_SA_LEN
|
||||
sin->sin_len = *sslen;
|
||||
#endif
|
||||
if (o.debugging > 2) {
|
||||
log_write(LOG_STDOUT, "doing %d.%d.%d.%d = %d.%d.%d.%d\n", current[0], current[1], current[2], current[3], addresses[0][current[0]], addresses[1][current[1]], addresses[2][current[2]], addresses[3][current[3]]);
|
||||
}
|
||||
/* Set the IP to the current value of everything */
|
||||
sin->sin_addr.s_addr = htonl(addresses[0][current[0]] << 24 |
|
||||
addresses[1][current[1]] << 16 |
|
||||
addresses[2][current[2]] << 8 |
|
||||
addresses[3][current[3]]);
|
||||
|
||||
/* Now we nudge up to the next IP */
|
||||
for (octet = 3; octet >= 0; octet--) {
|
||||
if (current[octet] < last[octet]) {
|
||||
/* OK, this is the column I have room to nudge upwards */
|
||||
current[octet]++;
|
||||
break;
|
||||
} else {
|
||||
/* This octet is finished so I reset it to the beginning */
|
||||
current[octet] = 0;
|
||||
}
|
||||
}
|
||||
if (octet == -1) {
|
||||
/* It didn't find anything to bump up, I must have taken the last IP */
|
||||
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];
|
||||
current[1] = last[1];
|
||||
current[2] = last[2];
|
||||
current[3] = last[3] + 1;
|
||||
} else {
|
||||
assert(!exhausted); /* There must be at least one more IP left */
|
||||
}
|
||||
} else {
|
||||
assert(targets_type == IPV6_NETMASK);
|
||||
#if HAVE_IPV6
|
||||
*sslen = sizeof(struct sockaddr_in6);
|
||||
memset(sin6, 0, *sslen);
|
||||
sin6->sin6_family = AF_INET6;
|
||||
#ifdef SIN_LEN
|
||||
sin6->sin6_len = *sslen;
|
||||
#endif /* SIN_LEN */
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/* If we are resuming from a previous scan, we have already finished
|
||||
scans up to o.resume_ip. */
|
||||
if (sin->sin_family == AF_INET && o.resume_ip.s_addr) {
|
||||
if (o.resume_ip.s_addr == sin->sin_addr.s_addr)
|
||||
o.resume_ip.s_addr = 0; /* So that we will KEEP the next one */
|
||||
goto startover; /* Try again */
|
||||
}
|
||||
|
||||
return 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Returns the last given host, so that it will be given again next
|
||||
time get_next_host is called. Obviously, you should only call
|
||||
this if you have fetched at least 1 host since parse_expr() was
|
||||
called */
|
||||
int TargetGroup::return_last_host() {
|
||||
int octet;
|
||||
std::string NetBlockIPv6Netmask::str() const {
|
||||
std::ostringstream result;
|
||||
unsigned int bits;
|
||||
struct in6_addr a;
|
||||
|
||||
exhausted = false;
|
||||
if (targets_type == IPV4_NETMASK) {
|
||||
assert(currentaddr.s_addr > startaddr.s_addr);
|
||||
currentaddr.s_addr--;
|
||||
} else if (targets_type == IPV4_RANGES) {
|
||||
for (octet = 3; octet >= 0; octet--) {
|
||||
if (current[octet] > 0) {
|
||||
/* OK, this is the column I have room to nudge downwards */
|
||||
current[octet]--;
|
||||
break;
|
||||
} else {
|
||||
/* This octet is already at the beginning, so I set it to the end */
|
||||
current[octet] = last[octet];
|
||||
}
|
||||
}
|
||||
assert(octet != -1);
|
||||
} else {
|
||||
assert(targets_type == IPV6_NETMASK);
|
||||
}
|
||||
return 0;
|
||||
a = this->start;
|
||||
recover_ipv6_netmask(&a, &this->end);
|
||||
bits = count_ipv6_bits(&a);
|
||||
|
||||
result << inet_ntop_ez((struct sockaddr_storage *) &this->addr, sizeof(this->addr)) << "/" << bits;
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
/* Returns true iff the given address is the one that was resolved to create
|
||||
this target group; i.e., not one of the addresses derived from it with a
|
||||
netmask. */
|
||||
bool TargetGroup::is_resolved_address(const struct sockaddr_storage *ss) {
|
||||
struct sockaddr_storage resolvedaddr;
|
||||
NetBlock *NetBlockHostname::resolve() const {
|
||||
struct addrinfo *addrs, *addr;
|
||||
std::list<struct sockaddr_storage> resolvedaddrs;
|
||||
NetBlock *netblock;
|
||||
struct sockaddr_storage ss;
|
||||
size_t sslen;
|
||||
|
||||
addrs = resolve_all(this->hostname.c_str(), this->af);
|
||||
for (addr = addrs; addr != NULL; addr = addr->ai_next) {
|
||||
if (addr->ai_addrlen < sizeof(ss)) {
|
||||
memcpy(&ss, addr->ai_addr, addr->ai_addrlen);
|
||||
resolvedaddrs.push_back(ss);
|
||||
}
|
||||
}
|
||||
if (addrs != NULL)
|
||||
freeaddrinfo(addrs);
|
||||
|
||||
if (resolvedaddrs.empty())
|
||||
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_NETMASK))
|
||||
return false;
|
||||
resolvedaddr = *resolvedaddrs.begin();
|
||||
return NULL;
|
||||
|
||||
return sockaddr_storage_equal(&resolvedaddr, ss);
|
||||
ss = *resolvedaddrs.begin();
|
||||
sslen = sizeof(ss);
|
||||
|
||||
if (resolvedaddrs.size() > 1 && o.verbose > 1) {
|
||||
error("Warning: Hostname %s resolves to %lu IPs. Using %s.", this->hostname.c_str(),
|
||||
(unsigned long) resolvedaddrs.size(), inet_ntop_ez(&ss, sslen));
|
||||
}
|
||||
|
||||
netblock = NULL;
|
||||
if (ss.ss_family == AF_INET) {
|
||||
NetBlockIPv4Ranges *netblock_ranges;
|
||||
uint32_t ip;
|
||||
|
||||
ip = ntohl(((struct sockaddr_in *) &ss)->sin_addr.s_addr);
|
||||
netblock_ranges = new NetBlockIPv4Ranges();
|
||||
BIT_SET(netblock_ranges->octets[0], (ip & 0xFF000000) >> 24);
|
||||
BIT_SET(netblock_ranges->octets[1], (ip & 0x00FF0000) >> 16);
|
||||
BIT_SET(netblock_ranges->octets[2], (ip & 0x0000FF00) >> 8);
|
||||
BIT_SET(netblock_ranges->octets[3], (ip & 0x000000FF));
|
||||
netblock = netblock_ranges;
|
||||
} else if (ss.ss_family == AF_INET6) {
|
||||
NetBlockIPv6Netmask *netblock_ipv6;
|
||||
|
||||
netblock_ipv6 = new NetBlockIPv6Netmask();
|
||||
netblock_ipv6->set_addr((struct sockaddr_in6 *) &ss);
|
||||
netblock = netblock_ipv6;
|
||||
}
|
||||
|
||||
if (netblock == NULL)
|
||||
return NULL;
|
||||
|
||||
netblock->hostname = this->hostname;
|
||||
netblock->resolvedaddrs = resolvedaddrs;
|
||||
netblock->apply_netmask(this->bits);
|
||||
|
||||
return netblock;
|
||||
}
|
||||
|
||||
/* Return a string of the name or address that was resolved for this group. */
|
||||
const char *TargetGroup::get_resolved_name(void) {
|
||||
return resolvedname.c_str();
|
||||
NetBlockHostname::NetBlockHostname(const char *hostname, int af) {
|
||||
this->hostname = hostname;
|
||||
this->af = af;
|
||||
this->bits = -1;
|
||||
}
|
||||
|
||||
/* Return the list of addresses that the name for this group resolved to, if
|
||||
it came from a name resolution. */
|
||||
const std::list<struct sockaddr_storage> &TargetGroup::get_resolved_addrs(void) {
|
||||
return resolvedaddrs;
|
||||
bool NetBlockHostname::next(struct sockaddr_storage *ss) {
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetBlockHostname::apply_netmask(int bits) {
|
||||
this->bits = bits;
|
||||
}
|
||||
|
||||
std::string NetBlockHostname::str() const {
|
||||
std::ostringstream result;
|
||||
|
||||
result << this->hostname;
|
||||
if (this->bits >= 0)
|
||||
result << "/" << this->bits;
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
/* debug level for the adding target is: 3 */
|
||||
@@ -686,26 +741,3 @@ unsigned long NewTargets::insert (const char *target) {
|
||||
|
||||
return new_targets->push(target);
|
||||
}
|
||||
|
||||
/* Lookahead is the number of hosts that can be
|
||||
checked (such as ping scanned) in advance. Randomize causes each
|
||||
group of up to lookahead hosts to be internally shuffled around.
|
||||
The target_expressions array MUST REMAIN VALID IN MEMORY as long as
|
||||
this class instance is used -- the array is NOT copied.
|
||||
*/
|
||||
HostGroupState::HostGroupState(int lookahead, int rnd,
|
||||
char *expr[], int numexpr) {
|
||||
assert(lookahead > 0);
|
||||
hostbatch = (Target **) safe_zalloc(sizeof(Target *) * lookahead);
|
||||
max_batch_sz = lookahead;
|
||||
current_batch_sz = 0;
|
||||
next_batch_no = 0;
|
||||
randomize = rnd;
|
||||
target_expressions = expr;
|
||||
num_expressions = numexpr;
|
||||
next_expression = 0;
|
||||
}
|
||||
|
||||
HostGroupState::~HostGroupState() {
|
||||
free(hostbatch);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user