Skip to content

Commit

Permalink
Merge pull request #691 from Frky/fix-echo-ip6
Browse files Browse the repository at this point in the history
Fix bug in --echo for IPv6 ranges & add --echo-cidr option
  • Loading branch information
robertdavidgraham authored Nov 1, 2023
2 parents 66ca4df + 0ba7561 commit 6bd2af0
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 29 deletions.
179 changes: 150 additions & 29 deletions src/main-conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,43 +226,111 @@ print_nmap_help(void)
/***************************************************************************
***************************************************************************/
static unsigned
count_cidr_bits(struct Range range)
count_cidr_bits(struct Range *range, bool *exact)
{
/* if range is covered by exactly one cidr prefix,
* exact is set to true and the prefix is outputted.
* If not, exact is set to false and the output
* cidr is the biggest one that starts at
* range.begin and that is included in range
*/
*exact = false;
unsigned i;

for (i=0; i<32; i++) {
unsigned mask = 0xFFFFFFFF >> i;

if ((range.begin & ~mask) == (range.end & ~mask)) {
if ((range.begin & mask) == 0 && (range.end & mask) == mask)
return i;
/* example:
* - beg = 1.1.0.0
* - mask of 1 bit: beg & mask == 1.1.0.0 & 0.1.1.1 = 0.1.0.0 => != 0
* - mask of 2 bits: beg & mask == 1.1.0.0 & 0.0.1.1 = 0.0.0.0
*/
if ((range->begin & mask) != 0) {
continue;
}
}

return 0;
/* if subnets are equal
* example:
* - beg = 1.1.0.0
* - end = 1.1.1.0
* - mask of 2 bits: beg & ~mask == end & ~mask
*/
if ((range->begin & ~mask) == (range->end & ~mask)) {
/* example:
* - beg = 1.1.0.0
* - end = 1.1.1.0
* - mask of 2 bits: beg & ~mask == end & ~mask
* BUT end & mask = 0.0.1.0 != mask
* => we include to many IPs => must reduce the mask
* => therefore, one more loop iteration (at least)
*/
if ((range->end & mask) == mask) {
/* mask is exact, we englobe the whole range */
*exact = true;
return i;
}
} else {
/* if subnets are different, we are not exact
* example:
* - beg = 0.1.0.0
* - end = 1.1.1.0
* - mask of 2 bits: beg & ~mask = 0.1.0.0
* end & ~mask = 1.1.0.0
* => mask of 2 bits does not cover the whole range
* must start again from 1.0.0.0 (first IP not covered)
*/
*exact = false;
/* set the new range begining (that is not included
* in the mask we return) */
range->begin = range->begin + mask + 1;
return i;
}
}
range->begin += 1;
return 32;
}

/***************************************************************************
***************************************************************************/
static unsigned
count_cidr6_bits(struct Range6 range)
count_cidr6_bits(struct Range6 *range, bool *exact)
{
/* for the comments of this function, see count_cidr_bits */
*exact = false;
uint64_t i;

/* Kludge: can't handle more than 64-bits of CIDR ranges */
if (range.begin.hi != range.begin.lo)
return 0;

for (i=0; i<64; i++) {
uint64_t mask = 0xFFFFFFFFffffffffull >> i;

if ((range.begin.lo & ~mask) == (range.end.lo & ~mask)) {
if ((range.begin.lo & mask) == 0 && (range.end.lo & mask) == mask)
return (unsigned)i;
for (i=0; i<128; i++) {
uint64_t mask_hi;
uint64_t mask_lo;
if (i < 64) {
mask_hi = 0xFFFFFFFFffffffffull >> i;
mask_lo = 0xFFFFFFFFffffffffull;
} else {
mask_hi = 0;
mask_lo = 0xFFFFFFFFffffffffull >> (i - 64);
}
if ((range->begin.hi & mask_hi) != 0 || (range->begin.lo & mask_lo) != 0) {
continue;
}
if ((range->begin.hi & ~mask_hi) == (range->end.hi & ~mask_hi) &&
(range->begin.lo & ~mask_lo) == (range->end.lo & ~mask_lo)) {
if (((range->end.hi & mask_hi) == mask_hi) && ((range->end.lo & mask_lo) == mask_lo)) {
*exact = true;
return (unsigned) i;
}
} else {
*exact = false;
range->begin.hi = range->begin.hi + mask_hi;
if (range->begin.lo >= 0xffffffffffffffff - 1 - mask_lo) {
range->begin.hi += 1;
}
range->begin.lo = range->begin.lo + mask_lo + 1;
return (unsigned) i;
}
}

return 0;
range->begin.lo = range->begin.lo + 1;
if (range->begin.lo == 0) {
range->begin.hi = range->begin.hi + 1;
}
return 128;
}


Expand Down Expand Up @@ -2330,6 +2398,8 @@ masscan_set_parameter(struct Masscan *masscan,
masscan->op = Operation_Echo;
} else if (EQUALS("echo-all", name)) {
masscan->op = Operation_EchoAll;
} else if (EQUALS("echo-cidr", name)) {
masscan->op = Operation_EchoCidr;
} else if (EQUALS("excludefile", name)) {
unsigned count1 = masscan->exclude.ipv4.count;
unsigned count2;
Expand Down Expand Up @@ -2588,7 +2658,7 @@ static int
is_singleton(const char *name)
{
static const char *singletons[] = {
"echo", "echo-all", "selftest", "self-test", "regress",
"echo", "echo-all", "echo-cidr", "selftest", "self-test", "regress",
"benchmark",
"system-dns", "traceroute", "version",
"version-light",
Expand Down Expand Up @@ -3208,6 +3278,7 @@ masscan_echo(struct Masscan *masscan, FILE *fp, unsigned is_echo_all)
}
fprintf(fp, "\n");
for (i=0; i<masscan->targets.ipv4.count; i++) {
bool exact = false;
struct Range range = masscan->targets.ipv4.list[i];
fprintf(fp, "range = ");
fprintf(fp, "%u.%u.%u.%u",
Expand All @@ -3217,9 +3288,9 @@ masscan_echo(struct Masscan *masscan, FILE *fp, unsigned is_echo_all)
(range.begin>> 0)&0xFF
);
if (range.begin != range.end) {
unsigned cidr_bits = count_cidr_bits(range);
if (cidr_bits) {
unsigned cidr_bits = count_cidr_bits(&range, &exact);

if (exact && cidr_bits) {
fprintf(fp, "/%u", cidr_bits);
} else
fprintf(fp, "-%u.%u.%u.%u",
Expand All @@ -3232,14 +3303,15 @@ masscan_echo(struct Masscan *masscan, FILE *fp, unsigned is_echo_all)
fprintf(fp, "\n");
}
for (i=0; i<masscan->targets.ipv6.count; i++) {
bool exact = false;
struct Range6 range = masscan->targets.ipv6.list[i];
ipaddress_formatted_t fmt = ipv6address_fmt(range.begin);

fprintf(fp, "range = %s", fmt.string);
if (!ipv6address_is_equal(range.begin, range.end)) {
unsigned cidr_bits = count_cidr6_bits(range);
unsigned cidr_bits = count_cidr6_bits(&range, &exact);

if (cidr_bits) {
if (exact && cidr_bits) {
fprintf(fp, "/%u", cidr_bits);
} else {
fmt = ipv6address_fmt(range.end);
Expand All @@ -3250,6 +3322,53 @@ masscan_echo(struct Masscan *masscan, FILE *fp, unsigned is_echo_all)
}
}

/***************************************************************************
* Prints the list of CIDR to scan to the command-line then exits.
* Use: provide this list to other tools. Unlike masscan -sL, it keeps
* the CIDR aggretated format, and does not randomize the order of output.
***************************************************************************/
void
masscan_echo_cidr(struct Masscan *masscan, FILE *fp, unsigned is_echo_all)
{
unsigned i;
masscan->echo = fp;
for (i=0; i<masscan->targets.ipv4.count; i++) {
struct Range range = masscan->targets.ipv4.list[i];
bool exact = false;
while (!exact) {
fprintf(fp, "%u.%u.%u.%u",
(range.begin>>24)&0xFF,
(range.begin>>16)&0xFF,
(range.begin>> 8)&0xFF,
(range.begin>> 0)&0xFF
);
if (range.begin == range.end) {
fprintf(fp, "/32");
exact = true;
} else {
unsigned cidr_bits = count_cidr_bits(&range, &exact);
fprintf(fp, "/%u", cidr_bits);
}
fprintf(fp, "\n");
}
}
for (i=0; i<masscan->targets.ipv6.count; i++) {
struct Range6 range = masscan->targets.ipv6.list[i];
bool exact = false;
while (!exact) {
ipaddress_formatted_t fmt = ipv6address_fmt(range.begin);
fprintf(fp, "%s", fmt.string);
if (range.begin.hi == range.end.hi && range.begin.lo == range.end.lo) {
fprintf(fp, "/128");
exact = true;
} else {
unsigned cidr_bits = count_cidr6_bits(&range, &exact);
fprintf(fp, "/%u", cidr_bits);
}
fprintf(fp, "\n");
}
}
}

/***************************************************************************
* remove leading/trailing whitespace
Expand Down Expand Up @@ -3343,12 +3462,14 @@ mainconf_selftest()

range.begin = 16;
range.end = 32-1;
if (count_cidr_bits(range) != 28)
bool exact = false;
if (count_cidr_bits(&range, &exact) != 28 || !exact)
return 1;

exact = true;
range.begin = 1;
range.end = 13;
if (count_cidr_bits(range) != 0)
if (count_cidr_bits(&range, &exact) != 0 || !exact)
return 1;


Expand Down
5 changes: 5 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1800,6 +1800,11 @@ int main(int argc, char *argv[])
exit(0);
break;

case Operation_EchoCidr:
masscan_echo_cidr(masscan, stdout, 0);
exit(0);
break;

case Operation_Selftest:
/*
* Do a regression test of all the significant units
Expand Down
7 changes: 7 additions & 0 deletions src/masscan.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ enum Operation {
Operation_Benchmark = 8, /* --benchmark */
Operation_Echo = 9, /* --echo */
Operation_EchoAll = 10, /* --echo-all */
Operation_EchoCidr = 11, /* --echo-cidr */
};

/**
Expand Down Expand Up @@ -536,4 +537,10 @@ masscan_initialize_adapter(
void
masscan_echo(struct Masscan *masscan, FILE *fp, unsigned is_echo_all);

/**
* Echoes the list of CIDR ranges to scan.
*/
void
masscan_echo_cidr(struct Masscan *masscan, FILE *fp, unsigned is_echo_all);

#endif

0 comments on commit 6bd2af0

Please sign in to comment.