License: GPL-3.0+
License: GPL-3.0+
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
.
On Debian systems, the complete text of the GNU General
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
================================================
FILE: debian/masscan.dirs
================================================
usr/bin
================================================
FILE: debian/masscan.docs
================================================
README.md
VULNINFO.md
doc/algorithm.js
doc/bot.html
================================================
FILE: debian/masscan.install
================================================
bin/masscan usr/bin/
================================================
FILE: debian/masscan.manpages
================================================
doc/masscan.8
================================================
FILE: debian/rules
================================================
#!/usr/bin/make -f
# -*- makefile -*-
# Sample debian/rules that uses debhelper.
# This file was originally written by Joey Hess and Craig Small.
# As a special exception, when this file is copied by dh-make into a
# dh-make output file, you may use that output file without restriction.
# This special exception was added by Craig Small in version 0.37 of dh-make.
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
%:
dh $@
override_dh_auto_test:
$(MAKE) regress
================================================
FILE: debian/source/format
================================================
3.0 (quilt)
================================================
FILE: debian/watch
================================================
version=3
# use the github release pages
https://github.com/robertdavidgraham/masscan/releases .*/(\d\S*)\.(?:tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))
================================================
FILE: doc/algorithm.js
================================================
/*
This is an implementation of the core Masscan scanning algorithm
in JavaScript/NodeJS. The core scanning algorithm is what makes
Masscan unique from other scanners, so it's worth highlighting
separately in a sample program.
REVIEW OF SCANNERS
The most famous port-scanner is "nmap". However, it is a
"host-at-a-time" scanner, and struggles at scanning large networks.
Masscan is an asynchronous, probe-at-a-time scanner. It spews out
probes to different ports, without caring if two probes happen to
be send to the same host. If the user wants a list of all ports
open on a single host, they have to post-process the masscan output
themselves, because masscan doesn't do it.
There are other asynchronous port-scanners, like scanrand, unicornscan,
and zmap. However, they have limitations in the way they do randomization
of their scans. They have limitations on the ranges of addresses and
ports that they'll accept, try to store an individual memory record
for everything scanned, or only partly randomize their scans.
THE WAY MASSCAN WORKS
Masscan first stores the targets as a "list of ranges". IP address
ranges are stored in one structure, and port ranges are stored
in another structure.
Then, a single index variable is used to enumerate the set of all
IP:port combinations. The scan works by simply incrementing the
index variable from 0 to the total number of probes (the 'range').
Then, before the enumeration step, the index is permuted into another
random index within the same range, in a 1-to-1 mapping. In other
words, the algorithm is theoretically reversable: given the output
of the permutation function, we can obtain the original index.
EXAMPLE
This program can be run like the following:
node patent.js 10.0.0.0-10.0.0.5 192.168.0.0/31 80,U:161
10.0.0.0-10.0.0.5
192.168.0.0-192.168.0.1
0.0.0.80-0.0.0.80
0.1.0.161-0.1.0.161
--> 10.0.0.4 udp:161
--> 10.0.0.0 udp:161
--> 10.0.0.1 udp:161
--> 10.0.0.4 tcp:80
--> 192.168.0.1 tcp:80
--> 10.0.0.0 tcp:80
--> 10.0.0.2 udp:161
--> 10.0.0.5 udp:161
--> 192.168.0.0 tcp:80
--> 192.168.0.0 udp:161
--> 10.0.0.1 tcp:80
--> 10.0.0.3 udp:161
--> 10.0.0.2 tcp:80
--> 10.0.0.5 tcp:80
--> 192.168.0.1 udp:161
--> 10.0.0.3 tcp:80
What you see first is the target ranges being echoed back that it scans,
first the IP address ranges, followed by the port ranges. The port ranges
are in weird decimal-dot notation because they share the same code
as for IPv4 addresses.
Then we see the randomized output, where individual probes are sent to a
random IP address and port.
TransmitThread
All the majic happens in the "TransmitThread()" function near the bottom
of this file.
We first see how the index variable 'i' is incremented from 0 to the
total number of packets that will be sent. We then see how first this
index is permuted to 'xXx', then this variable is separated into
one index for the IP address and another index for the port. Then,
those indexes are used to enumerate one of the IP addresses and
one of the ports.
Blackrock
This is the permutation function. It implements an encryption algorithm
based on DES (Data Encryption Standard). However, the use of real DES
would impose a restricting on the range that it be an even power of 2.
In the above example, with 14 total probes, this doesn't apply.
Therefore, we have to change binary operators like XOR with their
non-binary equivelents.
The upshot is that we first initialize Blackrock with the range (and
a seed/key), and then shuffle the index. The process is stateless,
meaning that any time we shuffle the number '5' we always get the
same result, regardless of what has happened before.
Targets, RangeList, Range
A Range is just a begin/end of an integer. We use this both for
IPv4 addresses (which are just 32-bit integers) and ports
(which are 16 bit integers).
A RangeList is just an array of Ranges. In Masscan, this object
sorts and combines ranges, making sure there are no duplicates,
but that isn't used in this example.
The RangeList object shows how an index can enumerate the
individual addresses/ports. This is down by walking the list
and subtracting from the index the size of each range, until
we reach a range that is larger than the index.
The Targets object just holds both the IP and port lists.
*/
function Range(begin, end) {
if (typeof begin == 'undefined' && typeof end == 'undefined') {
this.begin = 0xFFFFFFFF;
this.end = 0;
} else if (typeof end == 'undefined') {
this.begin = begin;
this.end = begin;
} else {
this.begin = begin;
this.end = end;
}
this.toString = function () {
return ((this.begin >> 24) & 0xFF)
+ "." + ((this.begin >> 16) & 0xFF)
+ "." + ((this.begin >> 8) & 0xFF)
+ "." + ((this.begin >> 0) & 0xFF)
+ "-" + ((this.end >> 24) & 0xFF)
+ "." + ((this.end >> 16) & 0xFF)
+ "." + ((this.end >> 8) & 0xFF)
+ "." + ((this.end >> 0) & 0xFF);
}
this.count = function () {
return this.end - this.begin + 1;
}
this.pick = function (index) {
return this.begin + index;
}
return this;
}
function RangeList() {
this.list = [];
this.total_count = 0;
this.push = function (range) {
this.list.push(range);
this.total_count += range.count();
}
this.count = function () {
return this.total_count;
}
this.pick = function (index) {
for (var i in this.list) {
var item = this.list[i];
if (index < item.count())
return item.pick(index);
else
index -= item.count();
}
return null;
}
}
function Targets() {
this.ports = new RangeList();
this.ips = new RangeList();
this.parse_ip = function (text) {
var x = text.split(".");
var result = 0;
result |= parseInt(x[0]) << 24;
result |= parseInt(x[1]) << 16;
result |= parseInt(x[2]) << 8;
result |= parseInt(x[3]) << 0;
return result;
}
this.parse_ports = function (arg) {
var offset = 0;
if (arg.indexOf(":") !== -1) {
var x = arg.split(":");
if (x[0] == "U")
offset = 65536;
else if (x[0] == "S")
offset = 65536 * 2;
arg = x[1];
}
var target;
if (arg.indexOf("-") !== -1) {
var x = arg.split("-");
target = new Range(parseInt(x[0]), parseInt(x[1]));
} else
target = new Range(parseInt(arg));
target.begin += offset;
target.end += offset;
this.ports.push(target);
}
this.parse_args = function (argv) {
for (var i in argv) {
var arg = argv[i];
if (arg.indexOf(",") !== -1) {
var x = arg.split(",");
for (var j in x)
this.parse_ports(x[j]);
} else if (arg.indexOf("/") !== -1) {
var x = arg.split("/");
var address = this.parse_ip(x[0]);
var prefix = parseInt(x[1]);
var mask = 0xFFFFFFFF << (32 - prefix);
address = address & mask;
var target = new Range(address, address | ~mask);
this.ips.push(target);
} else if (arg.indexOf("-") !== -1) {
var x = arg.split("-");
var begin = this.parse_ip(x[0]);
var end = this.parse_ip(x[1]);
var target = new Range(begin, end);
this.ips.push(target);
} else if (arg.indexOf(".") !== -1) {
var target = new Range(this.parse_ip(arg));
this.ips.push(target);
} else {
this.parse_ports(arg);
}
}
}
this.print = function () {
var i;
for (i in this.ips.list) {
console.log(this.ips.list[i].toString());
}
for (i in this.ports.list) {
console.log(this.ports.list[i].toString());
}
}
return this;
}
function Blackrock(range, seed) {
var split = Math.floor(Math.sqrt(range * 1.0));
this.rounds = 3;
this.seed = seed;
this.range = range;
this.a = split - 1;
this.b = split + 1;
while (this.a * this.b <= range)
this.b++;
/** Inner permutation function */
this.F = function (j, R, seed) {
var primes = [961752031, 982324657, 15485843, 961752031];
R = ((R << (R & 0x4)) + R + seed);
return Math.abs((((primes[j] * R + 25) ^ R) + j));
}
/** Outer feistal construction */
this.fe = function (r, a, b, m, seed) {
var L, R;
var j;
var tmp;
L = m % a;
R = Math.floor(m / a);
for (j = 1; j <= r; j++) {
if (j & 1) {
tmp = (L + this.F(j, R, seed)) % a;
} else {
tmp = (L + this.F(j, R, seed)) % b;
}
L = R;
R = tmp;
}
if (r & 1) {
return a * L + R;
} else {
return a * R + L;
}
}
/** Outer reverse feistal construction */
this.unfe = function (r, a, b, m, seed) {
var L, R;
var j;
var tmp;
if (r & 1) {
R = m % a;
L = Math.floor(m / a);
} else {
L = m % a;
R = Math.floor(m / a);
}
for (j = r; j >= 1; j--) {
if (j & 1) {
tmp = this.F(j, L, seed);
if (tmp > R) {
tmp = (tmp - R);
tmp = a - (tmp % a);
if (tmp == a)
tmp = 0;
} else {
tmp = (R - tmp);
tmp %= a;
}
} else {
tmp = this.F(j, L, seed);
if (tmp > R) {
tmp = (tmp - R);
tmp = b - (tmp % b);
if (tmp == b)
tmp = 0;
} else {
tmp = (R - tmp);
tmp %= b;
}
}
R = L;
L = tmp;
}
return a * R + L;
}
this.shuffle = function (m) {
var c;
c = this.fe(this.rounds, this.a, this.b, m, this.seed);
while (c >= this.range)
c = this.fe(this.rounds, this.a, this.b, c, this.seed);
return c;
}
this.unshuffle = function (m) {
var c;
c = unfe(this.rounds, this.a, this.b, m, this.seed);
while (c >= this.range)
c = unfe(this.rounds, this.a, this.b, c, this.seed);
return c;
}
return this;
}
function TransmitThread(targets, transmit, seed) {
var range = targets.ips.count() * targets.ports.count();
var b = Blackrock(range, seed);
for (var i = 0; i < range; i++) {
var xXx = b.shuffle(i);
var ip_index = Math.floor(xXx / targets.ports.count());
var port_index = Math.floor(xXx % targets.ports.count());
var ip = targets.ips.pick(ip_index);
var port = targets.ports.pick(port_index);
transmit(ip, port);
}
}
function Transmit2Thread(targets, transmit, seed, start, stop, increment) {
var range = targets.ips.count() * targets.ports.count();
var b = Blackrock(range, seed);
for (var i = start; i < range && i < stop; i += increment) {
var xXx = b.shuffle(i);
var ip_index = Math.floor(xXx / targets.ports.count());
var port_index = Math.floor(xXx % targets.ports.count());
var ip = targets.ips.pick(ip_index);
var port = targets.ports.pick(port_index);
transmit(ip, port);
}
}
function transmit(ip, port) {
var proto = "tcp";
if (port > 65536 * 2) {
proto = "sctp";
port -= 65536 * 2;
} else if (port > 65536) {
proto = "udp";
port -= 65536;
}
var ipstring = ((ip >> 24) & 0xFF)
+ "." + ((ip >> 16) & 0xFF)
+ "." + ((ip >> 8) & 0xFF)
+ "." + ((ip >> 0) & 0xFF)
console.log("--> " + ipstring + " " + proto + ":" + port);
}
var targets = new Targets();
targets.parse_args(process.argv.splice(2));
targets.print();
TransmitThread(targets, transmit, 42);
================================================
FILE: doc/bot.html
================================================
Masscan/1.0 - fast port scanner
Masscan/1.0 - fast port scanner
This tool is not a web spider, but a port scanner. It'll make only one request to a website, usually for the root / webpage. It then records the Server: field from the HTTP header, the <title> from the page contents, and possibly a few other interesting fields.
This does not follow links, it doesn't scan your web pages, but the ports on your machine.
The source code for this tool is at https://github.com/robertdavidgraham/masscan/. This is an open source project, so that this means it's not me (Robert Graham) who is using this tool to scan your website, but likely somebody else. I can't speak for their intentions, but this tool is more useful at doing surveys of the Internet than trying to hack in (tools like 'nmap' or 'nessus' are more often used for that).
================================================
FILE: doc/faq/FAQ0001-slow.md
================================================
# Why is it not as fast as I expect?
## Question
Why is scanning speed only around 100,000 packets-per-second instead of a million packets-per-second?
## Answer
I don't know.
If you have the latest Linux distro on the latest hardware, you can sometime
see scanning speeds of 1 million packets-per-second, even when virtualized.
However, sometimes you also see only 100,000 packets-per-second.
I've spent a lot of time trying to diagnose this situation and cannot
figure out what's going on. The box I use in a colo does 500,000 packets-per-second.
A relatively slow machine in my home lab does 1.2 million packets-per-second.
The speed is determined by the operating system. The amount of CPU used by `masscan`
itself is insignificant.
My theory is various configuration options within the operating system that can make
packet transmission very slow. Simple features that would not otherwise impact network
stacks that run at lower rates become really important at high rates.
One way around this is to install `PF_RING` and dedicate a network adapter to packet
transmission completely bypassing the operating system. In that case, packet transmission
rates can reach 15 million packets-per-second.
================================================
FILE: doc/faq/FAQ0002-drops.md
================================================
# Why are many results missing that I expect?
# Question
When I do a scan, results are missing that I know are there.
They show up when I repeat the scan, but then others are missing.
The faster I scan, the more results are missing.
# Answer
Network infrastructure does not like high rates of small packets.
Even though they can handle high **bit-rates** then cannot handle
high **packet-rates**.
This is what makes `masscan` so unique. It transmits packets at rates
far higher than other things can cope with. It often crashes networks.
Therefore, the faster you transmit packets, the more it overloads network
equipment, causing the packets to be dropped, causing probes to fail.
As the issue #546 below indicates, they are experiencing this at very low
rates of less than 10,000 packets-per-second. That seems excessively low.
I assume the actual reason is because of policy enforcement limiting traffic
rates rather than overloading network equipment.
# Issues
- [#546 fast scan get result](https://github.com/robertdavidgraham/masscan/issues/546)
================================================
FILE: doc/faq/FAQ0003-excludelist.md
================================================
# How can I add my IP address to an exclude list so that people stop scanning me?
# Question
I hate everyone probing me all the time and want them to stop.
How can I add my IP address ranges to an exclude list?
# Answer
You can't.
First of all, nobody is going to pay attention to a sample exclude list
within this project. Sure, I can add IP addresses to the list, but that
won't help you.
Second, there's no way I can confirm who you are. So I can't simply
add to an exclude list just because you ask.
Thirdly, it'll just make you more of a target, as smart hackers know to
use the exclude-list as one of their first include-lists, as it marks
people who have something to hide.
Fourthly, and most importantly, it's Wrong Think on how to manage your
network.
================================================
FILE: doc/faq/FAQ0004-serverlogs.md
================================================
# Why is masscan in my server logs?
## Question
Some example questions:
* Why is `masscan` appearing in my server logs?
* Why are you scanning me?
* Why is my server trying to connect to this github repo?
## Answer
When `masscan` connections to a webserver, it puts a link
back to this repo in the `User-Agent` field.
Since lots of people run Internet-wide scans using this tool,
and an Internet wide scan hits every public web server, you'll
see this appear in your web server logs several times a day.
It's the **end-to-end** principle of the Internet. Having a public
webserver on the Internet means that anybody can and will try to
connect to the web server.
It's nothing necessarily malicious. Lots of people run Internet-wide
scans to gather information about the state of the Internet. Of course,
some are indeed malicious, scanning to find vulnerabilities. However,
even when malicious, they probably aren't targetting you in particular,
but are instead scanning everybody.
================================================
FILE: doc/faq/README.md
================================================
# FAQs (frequently asked questions)
This directory contains some documents discussing frequently asked
questions
- 1 - [Why is it not as fast as I expect?](FAQ0001-slow.md)
- 2 - [Why are many results missing that I expect?](FAQ0002-drops.md)
- 3 - [How can I add my IPs to an official exclude list, to get people to stop scanning me?](FAQ0003-excludelist.md)
- 4 - [Why is this in my server logs?](FAQ0004-serverlogs.md)
================================================
FILE: doc/howto-afl.md
================================================
Using afl fuzzer against masscan
================================
AFL (American-Fuzzy-Lop) is an automated *fuzzer*. It takes existing
input to a program, then morphs that input in order to test new
code paths through the code. It's extremely successful at finding
bugs as well as *vulnerabilities*.
There are two inputs to *masscan*. One is the files it reads, which
come in various formats. The second is input from the network, in
response to network probes, which consist of various network protocols.
For AFL, there is also the issue of how *masscan* crashes. It tries to
print a backtrace. This causes AFL to falsely mark this as a "hang"
rather than a "crash". To fix this, run *masscan* with the *--nobacktrace*
option.
## Fuzzing file formats ##
The *masscan* program reads the following files. You can set the fuzzer
at each one of these to fuzz how it parses the contents.
*-c *
*Masscan* can read its configuration either from the command-line or from a file.
To create a file, run *masscan* as normal, then hit . This will save all
it's settings, even default values, into a file. It's a good starting point for
fuzzing.
*--readscan *
One of the possible outputs of *masscan* is in a proprietary binary format. You
can then run *masscan* to convert to any other output format.
In other words, you can run masscan to output XML like:
masscan -oX scan.xml
Or, in a two step process:
masscan -ob scan.mass
masscan --readscan scan.mass -oX scan.xml
*--exclude-file *
*Masscan* can scan large ragnes, like *0.0.0.0/0* (the entire Internet). You may
want to exclude specific addresses or ranges. These are configured in the
"exclude file".
You can also read ranges using *--include-file * or
*-iL *, but as far as fuzzing, I'm pretty sure it'll
be the same results.
*--hello-file[] *
This file is read, then dumped blindly across a TCP connection, in order
to say "hello" to a service. Since there's no parsing here, I'm not sure
you'll find anything fuzzing this.
*--nmap-payloads *
This file specifies the default payloads for UDP. It's in the same file
format as for *nmap*. There's some juicy parsing here that may lead
to bugs.
*--pcap-payloads *
This is the same as *--nmap-payloads*, but reads the UDP payloads from
a *libpcap* file.
## Fuzzing network protocols ##
*Masscan* parses several network protocols. It also must reassemble
fragmented responses over TCP for any application protocol. Remember:
*masscan* has it's own stack, so must parse everything that comes
over the network.
AFL has no ability to read from the network at this time. Moreover,
even then it wouldn't work easily, since *masscan* has a network stack
rather than just an application layer to deal with.
The trick is to first use *masscan* on a target that responds back
on a protocol, then save just the response side of the TCP connection
to a file. Then, when running *masscan* under AFL, read in that file
using the option *--adapter file:*. Then, and this is
critical, you must hard code all the TCP stack values to match those
of the original connection.
I generated the file *masscan/data/afl-http.pcap* as an example file
to read for fuzzing the parsing of HTTP. The command-line parameters
to use are:
bin/masscan --nobacktrace --adapter file:data/afl-http.pcap --source-ip 10.20.30.200 --source-port 6000 --source-mac 00-11-22-33-44-55 --router-mac c0-c1-c0-a0-9b-9d --seed 0 --banners -p80 74.125.196.147 --nostatus
The explanation are:
*--nobacktrace*: so that AFL correctly marks crashes as crashes
and not as hangs.
*--adapter file:*: This option normally specifies the adapter,
like *eth0* or *en1*. By putting the *file:* prefix on an adapter name,
it'll use a file (in *libpcap* format) to use instead. In this case,
transmits are dropped, and any packets are read from a file.
*--source-ip*, *--source-port*, *--source-mac*, *--router-mac*:
These are the hard-coded TCP/IP stack settings that must match the packets
in the file.
*--seed*: This must match the randomization seed in the packet file. Since
everything else is hardcoded, I think the only thing this will control
will be the sequence number of the TCP connection.
*--banners*: this tell the scanner to not simply find open ports, but also
establish a TCP connection and interact with the protocol, and report on
the results.
*-p*: The destination IP address to connect to.
**: The IP address to connect to
This should produce an output like the following. If you get the
banner back, then you know you've successfully done everything
correctly. Conversely, if you set *--seed 1*, then it won't work,
because it'll reject responses that match the wrong seed.
Starting masscan 1.0.3 (http://bit.ly/14GZzcT) at 2016-06-06 05:19:03 GMT
-- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [1 port/host]
Discovered open port 80/tcp on 74.125.196.147
Banner on port 80/tcp on 74.125.196.147: [title] Google
Banner on port 80/tcp on 74.125.196.147: [http] HTTP/1.0 200 OK\x0d\x0a...
...
(Additional output is truncated -- you get the idea).
The problem with this is that *masscan* will take about 10 seconds before it
produces the results. When it establishes a connection, it waits a few seconds
for the other side to transmit, then sends it's "hello", then waits many
seconds for all output to be received. I don't know if this messes AFL up,
whether I need to add additional options to truncate any waiting.
================================================
FILE: doc/masscan.8
================================================
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "MASSCAN" "8" "January 2014" "" ""
.
.SH "NAME"
\fBmasscan\fR \- Fast scan of the Internet
.
.SH "SYNOPSIS"
masscan [\fIoptions\fR] [... \-p \fIPORT\fR[,\fIPORT\fR...]]
.
.SH "DESCRIPTION"
\fBmasscan\fR is an Internet\-scale port scanner, useful for large scale surveys of the Internet, or of internal networks\. While the default transmit rate is only 100 packets/second, it can optional go as fast as 25 million packets/second, a rate sufficient to scan the Internet in 3 minutes for one port\.
.
.SH "OPTIONS"
.
.IP "\(bu" 4
\fB\fR: anything on the command\-line not prefixed with a \'\-\' is assumed to be an IP address or range\. There are three valid formats\. The first is a single IPv4 address like "192\.168\.0\.1"\. The second is a range like "10\.0\.0\.1\-10\.0\.0\.100"\. The third is a CIDR address, like "0\.0\.0\.0/0"\. At least one target must be specified\. Multiple targets can be specified\. This can be specified as multiple options separated by space, or can be separated by a comma as a single option, such as \fB10\.0\.0\.0/8,192\.168\.0\.1\fR\.
.
.IP "\(bu" 4
\fB\-\-range \fR: the same as target range spec described above, except as a named parameter instead of an unnamed one\.
.
.IP "\(bu" 4
\fB\-p PORT[,PORT...]\fR, \fB\-\-ports PORT[,PORT...]\fR: specifies the port(s) to be scanned\. A single port can be specified, like \fB\-p80\fR\. A range of ports can be specified, like \fB\-p 20\-25\fR\. A list of ports/ranges can be specified, like \fB\-p80,20\-25\fR\. UDP ports can also be specified, like \fB\-\-ports U:161,U:1024\-1100\fR\.
.
.IP "\(bu" 4
\fB\-\-banners\fR: specifies that banners should be grabbed, like HTTP server versions, HTML title fields, and so forth\. Only a few protocols are supported\.
.
.IP "\(bu" 4
\fB\-\-rate RATE\fR: specifies the desired rate for transmitting packets\. This can be very small numbers, like \fB0\.1\fR for transmitting packets at rates of one every 10 seconds, for very large numbers like 10000000, which attempts to transmit at 10 million packets/second\. In my experience, Windows and can do 250 thousand packets per second, and latest versions of Linux can do 2\.5 million packets per second\. The PF_RING driver is needed to get to 25 million packets/second\.
.
.IP "\(bu" 4
\fB\-c FILE\fR, \fB\-\-conf FILE\fR: reads in a configuration file\. The format of the configuration file is described below\.
.
.IP "\(bu" 4
\fB\-\-resume FILE\fR: the same as \fB\-\-conf\fR, except that a few options are automatically set, such as \fB\-\-append\-output\fR\. The format of the configuration file is described below\.
.
.IP "\(bu" 4
\fB\-\-echo\fR: don\'t run, but instead dump the current configuration to a file\. This file can then be used with the \fB\-c\fR option\. The format of this output is described below under \'CONFIGURATION FILE\'\.
.
.IP "\(bu" 4
\fB\-e IFNAME\fR, \fB\-\-adapter IFNAME\fR: use the named raw network interface, such as "eth0" or "dna1"\. If not specified, the first network interface found with a default gateway will be used\.
.
.IP "\(bu" 4
\fB\-\-adapter\-ip IP\fR: send packets using this IP address\. If not specified, then the first IP address bound to the network interface will be used\. Instead of a single IP address, a range may be specified\. NOTE: The size of the range must be an even power of 2, such as 1, 2, 4, 8, 16, 1024 etc\. addresses\.
.
.IP "\(bu" 4
\fB\-\-adapter\-port PORT\fR: send packets using this port number as the source\. If not specified, a random port will be chosen in the range 40000 through 60000\. This port should be filtered by the host firewall (like iptables) to prevent the host network stack from interfering with arriving packets\. Instead of a single port, a range can be specified, like \fB40000\-40003\fR\. NOTE: The size of the range must be an even power of 2, such as the example above that has a total of 4 addresses\.
.
.IP "\(bu" 4
\fB\-\-adapter\-mac MAC\fR: send packets using this as the source MAC address\. If not specified, then the first MAC address bound to the network interface will be used\.
.
.IP "\(bu" 4
\fB\-\-router\-mac MAC\fR: send packets to this MAC address as the destination\. If not specified, then the gateway address of the network interface will be ARPed\.
.
.IP "\(bu" 4
\fB\-\-ping\fR: indicates that the scan should include an ICMP echo request\. This may be included with TCP and UDP scanning\.
.
.IP "\(bu" 4
\fB\-\-exclude \fR: blacklist an IP address or range, preventing it from being scanned\. This overrides any target specification, guaranteeing that this address/range won\'t be scanned\. This has the same format as the normal target specification\.
.
.IP "\(bu" 4
\fB\-\-excludefile FILE\fR: reads in a list of exclude ranges, in the same target format described above\. These ranges override any targets, preventing them from being scanned\.
.
.IP "\(bu" 4
\fB\-\-append\-output\fR: causes output to append to the file, rather than overwriting the file\.
.
.IP "\(bu" 4
\fB\-\-iflist\fR: list the available network interfaces, and then exits\.
.
.IP "\(bu" 4
\fB\-\-retries NUM\fR: the number of retries to send, at 1 second intervals\. Note that since this scanner is stateless, retries are sent regardless if replies have already been received\.
.
.IP "\(bu" 4
\fB\-\-nmap\fR: print help about nmap\-compatibility alternatives for these options\.
.
.IP "\(bu" 4
\fB\-\-pcap\-payloads FILE\fR: read packets from a libpcap file containing packets and extract the UDP payloads, and associate those payloads with the destination port\. These payloads will then be used when sending UDP packets with the matching destination port\. Only one payload will be remembered per port\. Similar to \fB\-\-nmap\-payloads\fR\.
.
.IP "\(bu" 4
\fB\-\-nmap\-payloads FILE\fR: read in a file in the same format as the nmap file \fBnmap\-payloads\fR\. This contains UDP payload, so that we can send useful UDP packets instead of empty ones\. Similar to \fB\-\-pcap\-payloads\fR\.
.
.IP "\(bu" 4
\fB\-\-http\-user\-agent USER_AGENT\fR: replaces the existing user\-agent field with the indicated value when doing HTTP requests\.
.
.IP "\(bu" 4
\fB\-\-open\-only\fR: report only open ports, not closed ports\.
.
.IP "\(bu" 4
\fB\-\-pcap FILE\fR: saves received packets (but not transmitted packets) to the libpcap\-format file\.
.
.IP "\(bu" 4
\fB\-\-packet\-trace\fR: prints a summary of those packets sent and received\. This is useful at low rates, like a few packets per second, but will overwhelm the terminal at high rates\.
.
.IP "\(bu" 4
\fB\-\-pfring\fR: force the use of the PF_RING driver\. The program will exit if PF_RING DNA drvers are not available\.
.
.IP "\(bu" 4
\fB\-\-resume\-index INDEX\fR: the point in the scan at when it was paused\.
.
.IP "\(bu" 4
\fB\-\-resume\-count NUM\fR: the maximum number of probes to send before exiting\. This is useful with the \fB\-\-resume\-index\fR to chop up a scan and split it among multiple instances, though the \fB\-\-shards\fR option might be better\.
.
.IP "\(bu" 4
\fB\-\-shards X/Y\fR: splits the scan among instances\. \fBx\fR is the id for this scan, while \fBy\fR is the total number of instances\. For example, \fB\-\-shards 1/2\fR tells an instance to send every other packet, starting with index 0\. Likewise, \fB\-\-shards 2/2\fR sends every other packet, but starting with index 1, so that it doesn\'t overlap with the first example\.
.
.IP "\(bu" 4
\fB\-\-rotate TIME\fR: rotates the output file, renaming it with the current timestamp, moving it to a separate directory\. The time is specified in number of seconds, like "3600" for an hour\. Or, units of time can be specified, such as "hourly", or "6hours", or "10min"\. Times are aligned on an even boundary, so if "daily" is specified, then the file will be rotated every day at midnight\.
.
.IP "\(bu" 4
\fB\-\-rotate\-offset TIME\fR: an offset in the time\. This is to accommodate timezones\.
.
.IP "\(bu" 4
\fB\-\-rotate\-dir DIR\fR: when rotating the file, this specifies which directory to move the file to\. A useful directory is \fB/var/log/masscan\fR\.
.
.IP "\(bu" 4
\fB\-\-seed INT\fR: an integer that seeds the random number generator\. Using a different seed will cause packets to be sent in a different random order\. Instead of an integer, the string \fBtime\fR can be specified, which seeds using the local timestamp, automatically generating a differnet random order of scans\. If no seed specified, \fBtime\fR is the default\.
.
.IP "\(bu" 4
\fB\-\-regress\fR: run a regression test, returns \'0\' on success and \'1\' on failure\.
.
.IP "\(bu" 4
\fB\-\-ttl NUM\fR: specifies the TTL of outgoing packets, defaults to 255\.
.
.IP "\(bu" 4
\fB\-\-wait SECONDS\fR: specifies the number of seconds after transmit is done to wait for receiving packets before exiting the program\. The default is 10 seconds\. The string \fBforever\fR can be specified to never terminate\.
.
.IP "\(bu" 4
\fB\-\-offline\fR: don\'t actually transmit packets\. This is useful with a low rate and \fB\-\-packet\-trace\fR to look at what packets might\'ve been transmitted\. Or, it\'s useful with \fB\-\-rate 100000000\fR in order to benchmark how fast transmit would work (assuming a zero\-overhead driver)\. PF_RING is about 20% slower than the benchmark result from offline mode\.
.
.IP "\(bu" 4
\fB\-sL\fR: this doesn\'t do a scan, but instead creates a list of random addresses\. This is useful for importing into other tools\. The options \fB\-\-shard\fR, \fB\-\-resume\-index\fR, and \fB\-\-resume\-count\fR can be useful with this feature\.
.
.IP "\(bu" 4
\fB\-\-interactive\fR: show the results in realtime on the console\. It has no effect if used with \-\-output\-format or \-\-output\-filename\.
.
.IP "\(bu" 4
\fB\-\-output\-format FMT\fR: indicates the format of the output file, which can be \fBxml\fR, \fBbinary\fR, \fBgrepable\fR, \fBlist\fR, or \fBJSON\fR\. The option \fB\-\-output\-filename\fR must be specified\.
.
.IP "\(bu" 4
\fB\-\-output\-filename FILE\fR: the file which to save results to\. If the parameter \fB\-\-output\-format\fR is not specified, then the default of \fBxml\fR will be used\.
.
.IP "\(bu" 4
\fB\-oB FILE\fR: sets the output format to binary and saves the output in the given filename\. This is equivelent to using the \fB\-\-output\-format\fR and \fB\-\-output\-filename\fR parameters\. The option \fB\-\-readscan\fR can then be used to read the binary file\. Binary files are much smaller than their XML equivelents, but require a separate step to convert back into XML or another readable format\.
.
.IP "\(bu" 4
\fB\-oX FILE\fR: sets the output format to XML and saves the output in the given filename\. This is equivelent to using the \fB\-\-output\-format xml\fR and \fB\-\-output\-filename\fR parameters\.
.
.IP "\(bu" 4
\fB\-oG FILE\fR: sets the output format to grepable and saves the output in the given filename\. This is equivelent to using the \-\-output\-format grepable and \-\-output\-filename parameters\.
.
.IP "\(bu" 4
\fB\-oJ FILE\fR: sets the output format to JSON and saves the output in the given filename\. This is equivelent to using the \-\-output\-format json and \-\-output\-filename parameters\.
.
.IP "\(bu" 4
\fB\-oL FILE\fR: sets the output format to a simple list format and saves the output in the given filename\. This is equivelent to using the \-\-output\-format list and \-\-output\-filename parameters\.
.
.IP "\(bu" 4
\fB\-\-readscan FILE\fR: reads the files created by the \fB\-oB\fR option from a scan, then outputs them in one of the other formats, depending on command\-line parameters\. In other words, it can take the binary version of the output and convert it to an XML or JSON format\.
.
.IP "" 0
.
.SH "CONFIGURATION FILE FORMAT"
The configuration file uses the same parameter names as on the commandline, but without the \fB\-\-\fR prefix, and with an \fB=\fR sign between the name and the value\. An example configuration file might be:
.
.IP "" 4
.
.nf
# targets
range = 10\.0\.0\.0/8,192\.168\.0\.0/16
range = 172\.16\.0\.0/14
ports = 20\-25,80,U:53
ping = true
# adapter
adapter = eth0
adapter\-ip = 192\.168\.0\.1
router\-mac = 66\-55\-44\-33\-22\-11
# other
exclude\-file = /etc/masscan/exludes\.txt
.
.fi
.
.IP "" 0
.
.P
By default, the program will read default configuration from the file \fB/etc/masscan/masscan\.conf\fR\. This is useful for system\-specific settings, such as the \fB\-\-adapter\-xxx\fR options\. This is also useful for excluded IP addresses, so that you can scan the entire Internet, while skipping dangerous addresses, like those owned by the DoD, and not make an accidental mistake\.
.
.SH "CONTROL\-C BEHAVIOR"
When the user presses \fIctrl\-c\fR, the scan will stop, and the current state of the scan will be saved in the file \'paused\.conf\'\. The scan can be resumed with the \fB\-\-resume\fR option:
.
.IP "" 4
.
.nf
# masscan \-\-resume paused\.conf
.
.fi
.
.IP "" 0
.
.P
The program will not exit immediately, but will wait a default of 10 seconds to receive results from the Internet and save the results before exiting completely\. This time can be changed with the \fB\-\-wait\fR option\.
.
.SH "SIMPLE EXAMPLES"
The following example scans all private networks for webservers, and prints all open ports that were found\.
.
.IP "" 4
.
.nf
# masscan 10\.0\.0\.0/8 192\.168\.0\.0/16 172\.16\.0\.0/12 \-p80 \-\-open\-only
.
.fi
.
.IP "" 0
.
.P
The following example scans the entire Internet for DNS servers, grabbing their versions, then saves the results in an XML file\.
.
.IP "" 4
.
.nf
# masscan 0\.0\.0\.0/0 \-\-excludefile no\-dod\.txt \-pU:53 \-\-banners \-\-output\-filename dns\.xml
.
.fi
.
.IP "" 0
.
.P
You should be able to import the XML into databases and such\.
.
.P
The following example reads a binary scan results file called bin\-test\.scan and prints results to console\.
.
.IP "" 4
.
.nf
# masscan \-\-readscan bin\-test\.scan
.
.fi
.
.IP "" 0
.
.P
The following example reads a binary scan results file called bin\-test\.scan and creates an XML output file called bin\-test\.xml\.
.
.IP "" 4
.
.nf
# masscan \-\-readscan bin\-test\.scan \-oX bin\-test\.xml
.
.fi
.
.IP "" 0
.
.SH "ADVANCED EXAMPLES"
Let\'s say that you want to scan the entire Internet and spread the scan across three machines\. Masscan would be launched on all three machines using the following command\-lines:
.
.IP "" 4
.
.nf
# masscan 0\.0\.0\.0/0 \-p0\-65535 \-\-shard 1/3
# masscan 0\.0\.0\.0/0 \-p0\-65535 \-\-shard 2/3
# masscan 0\.0\.0\.0/0 \-p0\-65535 \-\-shard 3/3
.
.fi
.
.IP "" 0
.
.P
An alternative is with the "resume" feature\. A scan has an internal index that goes from zero to the number of ports times then number of IP addresses\. The following example shows splitting up a scan into chunks of a 1000 items each:
.
.IP "" 4
.
.nf
# masscan 0\.0\.0\.0/0 \-p0\-65535 \-\-resume\-index 0 \-\-resume\-count 1000
# masscan 0\.0\.0\.0/0 \-p0\-65535 \-\-resume\-index 1000 \-\-resume\-count 1000
# masscan 0\.0\.0\.0/0 \-p0\-65535 \-\-resume\-index 2000 \-\-resume\-count 1000
# masscan 0\.0\.0\.0/0 \-p0\-65535 \-\-resume\-index 3000 \-\-resume\-count 1000
.
.fi
.
.IP "" 0
.
.P
A script can use this to split smaller tasks across many other machines, such as Amazon EC2 instances\. As each instance completes a job, the script might send a request to a central coordinating server for more work\.
.
.SH "SPURIOUS RESETS"
When scanning TCP using the default IP address of your adapter, the built\-in stack will generate RST packets\. This will prevent banner grabbing\. There are are two ways to solve this\. The first way is to create a firewall rule to block that port from being seen by the stack\. How this works is dependent on the operating system, but on Linux this looks something like:
.
.IP "" 4
.
.nf
# iptables \-A INPUT \-p tcp \-i eth0 \-\-dport 61234 \-j DROP
.
.fi
.
.IP "" 0
.
.P
Then, when scanning, that same port must be used as the source:
.
.IP "" 4
.
.nf
# masscan 10\.0\.0\.0/8 \-p80 \-\-banners \-\-adapter\-port 61234
.
.fi
.
.IP "" 0
.
.P
An alternative is to "spoof" a different IP address\. This IP address must be within the range of the local network, but must not otherwise be in use by either your own computer or another computer on the network\. An example of this would look like:
.
.IP "" 4
.
.nf
# masscan 10\.0\.0\.0/8 \-p80 \-\-banners \-\-adapter\-ip 192\.168\.1\.101
.
.fi
.
.IP "" 0
.
.P
Setting your source IP address this way is the preferred way of running this scanner\.
.
.SH "ABUSE COMPLAINTS"
This scanner is designed for large\-scale surveys, of either an organization, or of the Internet as a whole\. This scanning will be noticed by those monitoring their logs, which will generate complaints\.
.
.P
If you are scanning your own organization, this may lead to you being fired\. Never scan outside your local subnet without getting permission from your boss, with a clear written declaration of why you are scanning\.
.
.P
The same applies to scanning the Internet from your employer\. This is another good way to get fired, as your IT department gets flooded with complaints as to why your organization is hacking them\.
.
.P
When scanning on your own, such as your home Internet or ISP, this will likely cause them to cancel your account due to the abuse complaints\.
.
.P
One solution is to work with your ISP, to be clear about precisely what we are doing, to prove to them that we are researching the Internet, not "hacking" it\. We have our ISP send the abuse complaints directly to us\. For anyone that asks, we add them to our "\-\-excludefile", blacklisting them so that we won\'t scan them again\. While interacting with such people, some instead add us to their whitelist, so that their firewalls won\'t log us anymore (they\'ll still block us, of course, they just won\'t log that fact to avoid filling up their logs with our scans)\.
.
.P
Ultimately, I don\'t know if it\'s possible to completely solve this problem\. Despite the Internet being a public, end\-to\-end network, you are still "guilty until proven innocent" when you do a scan\.
.
.SH "COMPATIBILITY"
While not listed in this document, a lot of parameters compatible with \fBnmap\fR will also work\.
.
.SH "SEE ALSO"
nmap(8), pcap(3)
.
.SH "AUTHORS"
This tool was written by Robert Graham\. The source code is available at https://github\.com/robertdavidgraham/masscan\.
================================================
FILE: doc/masscan.8.markdown
================================================
masscan(8) -- Fast scan of the Internet
=======================================
## SYNOPSIS
masscan \[options\] \[... -p PORT\[,PORT...\]\]
## DESCRIPTION
**masscan** is an Internet-scale port scanner, useful for large scale surveys
of the Internet, or of internal networks. While the default transmit rate
is only 100 packets/second, it can optionally go as fast as 25 million
packets/second, a rate sufficient to scan the Internet in 3 minutes for
one port.
## OPTIONS
* ``: anything on the command-line not prefixed with a '-' is
assumed to be an IP address or range. There are three valid formats.
The first is a single IP address like `192.168.0.1` or `2001:db8::1`. The second
is a range like `10.0.0.1-10.0.0.100`. The third is a CIDR address,
like `0.0.0.0/0` or `2001:db8::/90`. At least one target must be specified. Multiple
targets can be specified. This can be specified as multiple options
separated by space, or can be separated by a comma as a single option,
such as `10.0.0.0/8,192.168.0.1,2001:db8::1`.
* `--range `: the same as target range spec described above,
except as a named parameter instead of an unnamed one.
* `-p PORT[,PORT..]`, `--ports PORT[,PORT...]`: specifies the port(s) to be scanned. A
single port can be specified, like `-p80`. A range of ports can be
specified, like `-p 20-25`. A list of ports/ranges can be specified, like
`-p80,20-25`. UDP ports can also be specified, like
`--ports U:161,U:1024-1100`.
* `--banners`: specifies that banners should be grabbed after establishing
a TCP connection. Protocols supported include HTTP, FTP, IMAP4, memcached,
POP3, SMTP, SSH, SSL, SMB, Telnet, RDP, and VNC.
* `--rate RATE`: specifies the desired rate for transmitting
packets. This can be very small numbers, like `0.1` for transmitting
packets at rates of one every 10 seconds, for very large numbers like
10000000, which attempts to transmit at 10 million packets/second. In my
experience, Windows and can do 250 thousand packets per second, and latest
versions of Linux can do 2.5 million packets per second. The PF_RING driver
is needed to get to 25 million packets/second.
* `-c FILE`, `--conf FILE`: reads in a configuration file.
If not specified, then will read from `/etc/masscan/masscan.conf` by default.
The format is described below under 'CONFIGURATION FILE'.
* `--resume FILE`: the same as `--conf`, except that a few options
are automatically set, such as `--append-output`. The format of the
configuration file is described below. The purpose is to resume a scan
saved in `paused.conf` that was interupted with [ctrl-c].
* `--echo`: don't run, but instead dump the current configuration to a file.
This file can then be used with the `-c` option. The format of this
output is described below under 'CONFIGURATION FILE'.
* `-e IFNAME`, `--adapter IFNAME`: use the named raw network interface,
such as "eth0" or "dna1". If not specified, the first network interface
found with a default gateway will be used.
* `--adapter-ip IP`, `--source-ip IP`: send packets using this IP address. If not
specified, then the first IP address bound to the network interface
will be used. Instead of a single IP address, a range may be specified.
NOTE: The size of the range must be an even power of 2, such as 1, 2, 4,
8, 16, 1024 etc. addresses.
* `--adapter-port PORT`: send packets using this port number as the
source. If not specified, a random port will be chosen in the range 40000
through 60000. This port should be filtered by the host firewall (like
iptables) to prevent the host network stack from interfering with arriving
packets. Instead of a single port, a range can be specified, like
`40000-40003`. NOTE: The size of the range must be an even power of 2,
such as the example above that has a total of 4 addresses.
* `--adapter-mac MAC`: send packets using this as the source MAC
address. If not specified, then the first MAC address bound to the network
interface will be used.
* `--adapter-vlan VLANID`: send packets using this 802.1q VLAN ID
* `--router-mac MAC`: send packets to this MAC address as the
destination. If not specified, then the gateway address of the network
interface will be ARPed.
* `--ping`: indicates that the scan should include an ICMP echo request.
This may be included with TCP and UDP scanning.
* `--exclude `: blacklist an IP address or range, preventing it
from being scanned. This overrides any target specification, guaranteeing
that this address/range won't be scanned. This has the same format
as the normal target specification.
* `--excludefile FILE`: reads in a list of exclude ranges, in the same
target format described above. These ranges override any targets,
preventing them from being scanned.
* `-iL FILE`, `--includefile FILE`: reads in a list of ranges to scan, in the same
target format described above for IP addresses and ranges. This file can contain
millions of addresses and ranges.
* `--append-output`: causes output to append to the file, rather than
overwriting the file. Useful for when resumeing scans (see `--resume`).
* `--iflist`: list the available network interfaces, and then exits. The
`-e IFNAME` can then be used with one of the listed adapters.
* `--retries `: the number of retries to send, at 1 second intervals. Note
that since this scanner is stateless, retries are sent regardless if
replies have already been received.
* `--nmap`: print help about nmap-compatibility alternatives for these
options.
* `--pcap-payloads FILE`: read packets from a libpcap file containing packets
and extract the UDP payloads, and associate those payloads with the
destination port. These payloads will then be used when sending UDP
packets with the matching destination port. Only one payload will
be remembered per port. Similar to `--nmap-payloads`.
* `--nmap-payloads FILE`: read in a file in the same format as
the nmap file `nmap-payloads`. This contains UDP payload, so that we
can send useful UDP packets instead of empty ones. Similar to
`--pcap-payloads`.
* `--http-* HEADER`: replaces the existing field in the HTTP header
with a new one. Fields that can be replaced are `--http-method`, `--http-url`,
`--http-version`,`--http-host`, and `--http-user-agent`.
Example: `--http-user-agent Keurig K575 Coffee Maker`. See also `--http-field` and `--http-cookie`.
* `--http-field NAME:VALUE`: replaces the existing HTTP header field,
or inserts a new one if the field doesn't exist, given as a `name:value` pair.
Cannot be used to replace the fields in the request-line (method, url, version).
Example: `--http-field Accept:image/gif`.
* `--http-field-remove NAME`: removes the first field from the header that matches
(may be needed multiple times for fields like `Cookie` that can exist multiple times)
* `--http-cookie VALUE`: adds a `Cookie:` field to the HTTP header, even
if other cookie fields exist. The other `--http-*` options replace existing
fields in the HTTP header, this one adds more even if some already exist.
* `--http-payload STR`: adds a payload string after the header; this will
automatically add a `--http-field Content-Length:LEN` field to match the length of the string,
but the user will have to add their own `--http-field Content-Type:TYPE` field to match
the string. Presumably, the user will also change the method to something like
`--http-method POST`. Common conntent types would be `application/x-www-form-urlencoded`,
`application/json`, or `text/xml`.
* `--show [open|closed]`: tells which port status to display, such
as 'open' for those ports that respond with a SYN-ACK on TCP, or
'closed' for those ports that repsond with RST. The default is
only to display 'open' ports.
* `--noshow [open|closed]`: disables a port status to display, such
as to no longer display 'open' ports.
* `--pcap FILE`: saves received packets (but not transmitted
packets) to the libpcap-format file.
* `--packet-trace`: prints a summary of those packets sent and received.
This is useful at low rates, like a few packets per second, but will
overwhelm the terminal at high rates.
* `--pfring`: force the use of the PF_RING driver. The program will exit
if PF_RING DNA drvers are not available.
* `--resume-index INDEX`: the point in the scan at when it was paused.
* `--resume-count NUM`: the maximum number of probes to send before exiting.
This is useful with the `--resume-index` to chop up a scan and split
it among multiple instances, though the `--shards` option might be
better.
* `--shards X/Y`: splits the scan among instances. `x` is the id
for this scan, while `y` is the total number of instances. For example,
`--shards 1/2` tells an instance to send every other packet, starting
with index 0. Likewise, `--shards 2/2` sends every other packet, but
starting with index 1, so that it doesn't overlap with the first example.
* `--rotate TIME`: rotates the output file, renaming it with the
current timestamp, moving it to a separate directory. The time is
specified in number of seconds, like "3600" for an hour. Or, units
of time can be specified, such as "hourly", or "6hours", or "10min".
Times are aligned on an even boundary, so if "daily" is specified,
then the file will be rotated every day at midnight.
* `--rotate-offset TIME`: an offset in the time. This is to accomodate
timezones.
* `--rotate-size SIZE`: rotates the output file when it exceeds the
given size. Typical suffixes can be applied (k,m,g,t) for kilo, mega,
giga, tera.
* `--rotate-dir DIR`: when rotating the file, this specifies which
directory to move the file to. A useful directory is `/var/log/masscan`.
* `--seed INT`: an integer that seeds the random number generator.
Using a different seed will cause packets to be sent in a different
random order. Instead of an integer, the string `time` can be specified,
which seeds using the local timestamp, automatically generating a
different random order of scans. If no seed specified, `time` is the
default.
* `--regress`: run a regression test, returns '0' on success and '1' on
failure.
* `--ttl NUM`: specifies the TTL of outgoing packets, defaults to 255.
* `--wait SECONDS`: specifies the number of seconds after transmit is
done to wait for receiving packets before exiting the program. The default
is 10 seconds. The string `forever` can be specified to never terminate.
* `--offline`: don't actually transmit packets. This is useful with
a low rate and `--packet-trace` to look at what packets might've been
transmitted. Or, it's useful with `--rate 100000000` in order to
benchmark how fast transmit would work (assuming a zero-overhead
driver). PF_RING is about 20% slower than the benchmark result from
offline mode.
* `-sL`: this doesn't do a scan, but instead creates a list of random
addresses. This is useful for importing into other tools. The options
`--shard`, `--resume-index`, and `--resume-count` can be useful with
this feature.
* `--interactive`: show the results in realtime on the console. It has
no effect if used with --output-format or --output-filename.
* `--output-format FMT`: indicates the format of the output file, which
can be `xml`, `binary`, `grepable`, `list`, or `JSON`. The
option `--output-filename` must be specified.
* `--output-filename FILE`: the file which to save results to. If
the parameter `--output-format` is not specified, then the default of
`xml` will be used.
* `-oB FILE`: sets the output format to binary and saves the output in
the given filename. This is equivalent to using the `--output-format` and
`--output-filename` parameters. The option `--readscan` can then be used to
read the binary file. Binary files are mush smaller than their XML
equivalents, but require a separate step to convert back into XML or
another readable format.
* `-oX FILE`: sets the output format to XML and saves the output in the
given filename. This is equivalent to using the `--output-format xml` and
`--output-filename` parameters.
* `-oG FILE`: sets the output format to grepable and saves the output
in the given filename. This is equivalent to using the --output-format grepable
and --output-filename parameters.
* `-oJ FILE`: sets the output format to JSON and saves the output in
the given filename. This is equivalent to using the --output-format json
and --output-filename parameters.
* `-oL FILE`: sets the output format to a simple list format and saves
the output in the given filename. This is equivalent to using
the --output-format list and --output-filename parameters.
* `--readscan FILE`: reads the files created by the `-oB` option
from a scan, then outputs them in one of the other formats, depending
on command-line parameters. In other words, it can take the binary
version of the output and convert it to an XML or JSON format. When this option
is given, defaults from `/etc/masscan/masscan.conf` will not be read.
* `--connection-timeout SECS`: when doing banner checks, this specifies the
maximum number of seconds that a TCP connection can be held open. The default
is 30 seconds. Increase this time if banners are incomplete. For example,
we have to increase the timeout when downloading all the SSL certs from
the Internet, because some sites take that long to deliver all the certs
in the chain. However, beware that when this is set to a large value, it'll
consume a lot of memory on fast scans. While the code may handle millions of
open TCP connections, you may not have enough memory for that.
* `--hello-file[PORT] FILE`: send the contents of the file once the
TCP connection has been established with the given port. Requires that
`--banners` also be set. Heuristics will be performed on the reponse in
an attempt to discover what protocol, so HTTP responses will be parsed
differently than other protocols.
* `--hello-string[PORT] BASE64`: same as `--hello-file` except that the
contents of the BASE64 encoded string are decoded, then used as the hello
string that greets the server.
* `--capture TYPE` or `--nocapture TYPE`: when doing banners (`--banner`), this
determines what to capture from the banners. By default, only the TITLE field from
HTML documents is captured, to get the entire document, use `--capture html`.
By default, the entire certificate from SSL is captured, to disable this, use
`--nocapture cert`. Currently, only the values `html` and `cert` are currently
supported for this option, but many more will be added in the future.
## CONFIGURATION FILE FORMAT
The configuration file uses the same parameter names as on the
commandline, but without the `--` prefix, and with an `=` sign
between the name and the value. An example configuration file
might be:
# targets
range = 10.0.0.0/8,192.168.0.0/16
range = 172.16.0.0/12
ports = 20-25,80,U:53
ping = true
# adapter
adapter = eth0
adapter-ip = 192.168.0.1
router-mac = 66-55-44-33-22-11
# other
exclude-file = /etc/masscan/exludes.txt
By default, the program will read default configuration from the file
`/etc/masscan/masscan.conf`. This is useful for system-specific settings,
such as the `--adapter-xxx` options. This is also useful for
excluded IP addresses, so that you can scan the entire Internet,
while skipping dangerous addresses, like those owned by the DoD,
and not make an accidental mistake.
## CONTROL-C BEHAVIOR
When the user presses , the scan will stop, and the current
state of the scan will be saved in the file 'paused.conf'. The scan
can be resumed with the `--resume` option:
# masscan --resume paused.conf
The program will not exit immediately, but will wait a default of 10
seconds to receive results from the Internet and save the results before
exiting completely. This time can be changed with the `--wait` option.
## USER-MODE STACK
Masscan has a user-mode TCP/IP stack that co-exists with the operating-system's
stack. Normally, this works fine but sometimes can cause problems, especially
with the `--banners` option that establishes a TCP/IP connection. In some
cases, all the stack's parameters will have to be specified separately:
--adapter-port PORT
--adapter-ip IP
--adapter-mac MAC
--adapter-vlan VLANID
--router-mac MAC
If the user-mode stack shares the same IP address as the operating-system,
then the kernel will send RST packets during a scan. This can cause
unnecessary traffic during a simple port scan, and will terminate TCP
connections when doing a `--banners` scan. To prevent, this, the built-in
firewall should be used to filter the source ports. On Linux, this can be done
by doing something like:
# iptables -A INPUT -i eth0 -p tcp --dport 44444 -j DROP
This will prevent the Linux kernel from processing incoming packets to port
44444, but `masscan` will still see the packets. Set the maching parameter
of `--adapter-port 44444` to force `masscan` to use that port instead of
a random port.
## SIMPLE EXAMPLES
The following example scans all private networks for webservers, and prints
all open ports that were found.
# masscan 10.0.0.0/8 192.168.0.0/16 172.16.0.0/12 -p80 --open-only
The following example scans the entire Internet for DNS servers, grabbing
their versions, then saves the results in an XML file.
# masscan 0.0.0.0/0 --excludefile no-dod.txt -pU:53 --banners --output-filename dns.xml
You should be able to import the XML into databases and such.
The following example reads a binary scan results file called bin-test.scan and prints
results to console.
# masscan --readscan bin-test.scan
The following example reads a binary scan results file called bin-test.scan and creates
an XML output file called bin-test.xml.
# masscan --readscan bin-test.scan -oX bin-test.xml
## ADVANCED EXAMPLES
Let's say that you want to scan the entire Internet and spread the scan
across three machines. Masscan would be launched on all three machines
using the following command-lines:
# masscan 0.0.0.0/0 -p0-65535 --shard 1/3
# masscan 0.0.0.0/0 -p0-65535 --shard 2/3
# masscan 0.0.0.0/0 -p0-65535 --shard 3/3
An alternative is with the "resume" feature. A scan has an internal index that
goes from zero to the number of ports times the number of IP addresses. The
following example shows splitting up a scan into chunks of a 1000 items each:
# masscan 0.0.0.0/0 -p0-65535 --resume-index 0 --resume-count 1000
# masscan 0.0.0.0/0 -p0-65535 --resume-index 1000 --resume-count 1000
# masscan 0.0.0.0/0 -p0-65535 --resume-index 2000 --resume-count 1000
# masscan 0.0.0.0/0 -p0-65535 --resume-index 3000 --resume-count 1000
A script can use this to split smaller tasks across many other machines,
such as Amazon EC2 instances. As each instance completes a job, the
script might send a request to a central coordinating server for more
work.
## SPURIOUS RESETS
When scanning TCP using the default IP address of your adapter, the built-in
stack will generate RST packets. This will prevent banner grabbing. There are
are two ways to solve this. The first way is to create a firewall rule
to block that port from being seen by the stack. How this works is dependent
on the operating system, but on Linux this looks something like:
# iptables -A INPUT -p tcp -i eth0 --dport 61234 -j DROP
Then, when scanning, that same port must be used as the source:
# masscan 10.0.0.0/8 -p80 --banners --adapter-port 61234
An alternative is to "spoof" a different IP address. This IP address must be
within the range of the local network, but must not otherwise be in use by
either your own computer or another computer on the network. An example of this
would look like:
# masscan 10.0.0.0/8 -p80 --banners --adapter-ip 192.168.1.101
Setting your source IP address this way is the preferred way of running this
scanner.
## ABUSE COMPLAINTS
This scanner is designed for large-scale surveys, of either an organization,
or of the Internet as a whole. This scanning will be noticed by those
monitoring their logs, which will generate complaints.
If you are scanning your own organization, this may lead to you being fired.
Never scan outside your local subnet without getting permission from your boss,
with a clear written declaration of why you are scanning.
The same applies to scanning the Internet from your employer. This is another
good way to get fired, as your IT department gets flooded with complaints as
to why your organization is hacking them.
When scanning on your own, such as your home Internet or ISP, this will likely
cause them to cancel your account due to the abuse complaints.
One solution is to work with your ISP, to be clear about precisely what we are
doing, to prove to them that we are researching the Internet, not "hacking" it.
We have our ISP send the abuse complaints directly to us. For anyone that asks,
we add them to our "--excludefile", blacklisting them so that we won't scan
them again. While interacting with such people, some instead add us to their
whitelist, so that their firewalls won't log us anymore (they'll still block
us, of course, they just won't log that fact to avoid filling up their logs
with our scans).
Ultimately, I don't know if it's possible to completely solve this problem.
Despite the Internet being a public, end-to-end network, you are still
"guilty until proven innocent" when you do a scan.
## COMPATIBILITY
While not listed in this document, a lot of parameters compatible with
`nmap` will also work. It runs on macOS, Linux, and Windows. It's compiled
in fairly portable C language. It supports IPv4 and IPv6.
## SEE ALSO
nmap(8), pcap(3)
## AUTHORS
This tool was written by Robert Graham. The source code is available at
https://github.com/robertdavidgraham/masscan.
================================================
FILE: src/crypto-base64.c
================================================
#include "crypto-base64.h"
#include
#include
#include
#include
#include
/*****************************************************************************
*****************************************************************************/
size_t
base64_encode(void *vdst, size_t sizeof_dst,
const void *vsrc, size_t sizeof_src)
{
static const char *b64 =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"+/";
size_t i = 0;
size_t d = 0;
unsigned char *dst = (unsigned char *)vdst;
const unsigned char *src = (const unsigned char *)vsrc;
/* encode every 3 bytes of source into 4 bytes of destination text */
while (i + 3 <= sizeof_src) {
unsigned n;
/* make sure there is enough space */
if (d + 4 > sizeof_dst)
return d;
/* convert the chars */
n = src[i]<<16 | src[i+1]<<8 | src[i+2];
dst[d+0] = b64[ (n>>18) & 0x3F ];
dst[d+1] = b64[ (n>>12) & 0x3F ];
dst[d+2] = b64[ (n>> 6) & 0x3F ];
dst[d+3] = b64[ (n>> 0) & 0x3F ];
i += 3;
d += 4;
}
/* If the source text isn't an even multiple of 3 characters, then we'll
* have to append a '=' or '==' to the output to compensate */
if (i + 2 <= sizeof_src && d + 4 <= sizeof_dst) {
unsigned n = src[i]<<16 | src[i+1]<<8;
dst[d+0] = b64[ (n>>18) & 0x3F ];
dst[d+1] = b64[ (n>>12) & 0x3F ];
dst[d+2] = b64[ (n>> 6) & 0x3F ];
dst[d+3] = '=';
d += 4;
} else if (i + 1 <= sizeof_src && d + 4 <= sizeof_dst) {
unsigned n = src[i]<<16;
dst[d+0] = b64[ (n>>18) & 0x3F ];
dst[d+1] = b64[ (n>>12) & 0x3F ];
dst[d+2] = '=';
dst[d+3] = '=';
d += 4;
}
return d;
}
/*****************************************************************************
*****************************************************************************/
size_t
base64_decode(void *vdst, size_t sizeof_dst,
const void *vsrc, size_t sizeof_src)
{
static const unsigned char rstr[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 62, 0xFF, 0xFF, 0xFF, 63,
52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};
size_t i = 0;
size_t d = 0;
unsigned char *dst = (unsigned char *)vdst;
const unsigned char *src = (const unsigned char *)vsrc;
while (i < sizeof_src) {
unsigned b;
unsigned c=0;
/* byte#1 */
while (i 64)
i++;
if (src[i] == '=' || i++ >= sizeof_src)
break;
b = (c << 2) & 0xfc;
while (i 64)
i++;
if (src[i] == '=' || i++ >= sizeof_src)
break;
b |= (c>>4) & 0x03;
if (d=sizeof_src)
break;
/* byte#2 */
b = (c<<4) & 0xF0;
while (i 64)
;
if (src[i] == '=' || i++ >= sizeof_src)
break;
b |= (c>>2) & 0x0F;
if (d=sizeof_src)
break;
/* byte#3*/
b = (c<<6) & 0xC0;
while (i 64)
;
if (src[i] == '=' || i++ >= sizeof_src)
break;
b |= c;
if (d=sizeof_src)
break;
}
if (d>16 & 0x7fff;
}
/*****************************************************************************
*****************************************************************************/
int
base64_selftest(void)
{
char buf[100];
char buf2[100];
char buf3[100];
size_t buf_len;
size_t buf2_len;
unsigned i;
unsigned seed = (unsigned)time(0);
buf_len = base64_encode(buf, sizeof(buf), "hello", 5);
buf2_len = base64_decode(buf2, sizeof(buf2), buf, buf_len);
if (buf2_len != 5 && memcmp(buf2, "hello", 5) != 0) {
fprintf(stderr, "base64: selftest failed\n");
return 1;
}
/*
* Generate a bunch of random strings, encode them, then decode them,
* making sure the final result matches the original string
*/
for (i=0; i<100; i++) {
unsigned j;
size_t buf3_len;
/* create a string of random bytes */
buf_len = r_rand(&seed) % 50;
for (j=0; j
size_t base64_decode(void *dst, size_t sizeof_dst, const void *src, size_t sizeof_src);
size_t base64_encode(void *dst, size_t sizeof_dst, const void *src, size_t sizeof_src);
int base64_selftest(void);
#endif
================================================
FILE: src/crypto-blackrock.c
================================================
/*
BlackRock cipher
(h/t Marsh Ray @marshray for this idea)
This is a randomization/reshuffling function based on a crypto
"Feistel network" as described in the paper:
'Ciphers with Arbitrary Finite Domains'
by John Black and Phillip Rogaway
http://www.cs.ucdavis.edu/~rogaway/papers/subset.pdf
This is a crypto-like construction that encrypts an arbitrary sized
range. Given a number in the range [0..9999], it'll produce a mapping
to a distinct different number in the same range (and back again).
In other words, it randomizes the order of numbers in a sequence.
For example, it can be used to randomize the sequence [0..9]:
0 -> 6
1 -> 4
2 -> 8
3 -> 1
4 -> 9
5 -> 3
6 -> 0
7 -> 5
8 -> 2
9 -> 7
As you can see on the right hand side, the numbers are in random
order, and they don't repeat.
This is create for port scanning. We can take an index variable
and increment it during a scan, then use this function to
randomize it, yet be assured that we've probed every IP and port
within the range.
The cryptographic strength of this construction depends upon the
number of rounds, and the exact nature of the inner "READ()" function.
Because it's a Feistel network, that "READ()" function can be almost
anything.
We don't care about cryptographic strength, just speed, so we are
using a trivial READ() function.
This is a class of "format-preserving encryption". There are
probably better constructions than what I'm using.
*/
#include "crypto-blackrock.h"
#include "pixie-timer.h"
#include "util-malloc.h"
#include
#include
#include
#include
#include
#include
#include
#if defined(_MSC_VER)
#define inline _inline
#endif
/***************************************************************************
* It's an s-box. You gotta have an s-box
***************************************************************************/
const unsigned char sbox[256] = {
0x91, 0x58, 0xb3, 0x31, 0x6c, 0x33, 0xda, 0x88,
0x57, 0xdd, 0x8c, 0xf2, 0x29, 0x5a, 0x08, 0x9f,
0x49, 0x34, 0xce, 0x99, 0x9e, 0xbf, 0x0f, 0x81,
0xd4, 0x2f, 0x92, 0x3f, 0x95, 0xf5, 0x23, 0x00,
0x0d, 0x3e, 0xa8, 0x90, 0x98, 0xdd, 0x20, 0x00,
0x03, 0x69, 0x0a, 0xca, 0xba, 0x12, 0x08, 0x41,
0x6e, 0xb9, 0x86, 0xe4, 0x50, 0xf0, 0x84, 0xe2,
0xb3, 0xb3, 0xc8, 0xb5, 0xb2, 0x2d, 0x18, 0x70,
0x0a, 0xd7, 0x92, 0x90, 0x9e, 0x1e, 0x0c, 0x1f,
0x08, 0xe8, 0x06, 0xfd, 0x85, 0x2f, 0xaa, 0x5d,
0xcf, 0xf9, 0xe3, 0x55, 0xb9, 0xfe, 0xa6, 0x7f,
0x44, 0x3b, 0x4a, 0x4f, 0xc9, 0x2f, 0xd2, 0xd3,
0x8e, 0xdc, 0xae, 0xba, 0x4f, 0x02, 0xb4, 0x76,
0xba, 0x64, 0x2d, 0x07, 0x9e, 0x08, 0xec, 0xbd,
0x52, 0x29, 0x07, 0xbb, 0x9f, 0xb5, 0x58, 0x6f,
0x07, 0x55, 0xb0, 0x34, 0x74, 0x9f, 0x05, 0xb2,
0xdf, 0xa9, 0xc6, 0x2a, 0xa3, 0x5d, 0xff, 0x10,
0x40, 0xb3, 0xb7, 0xb4, 0x63, 0x6e, 0xf4, 0x3e,
0xee, 0xf6, 0x49, 0x52, 0xe3, 0x11, 0xb3, 0xf1,
0xfb, 0x60, 0x48, 0xa1, 0xa4, 0x19, 0x7a, 0x2e,
0x90, 0x28, 0x90, 0x8d, 0x5e, 0x8c, 0x8c, 0xc4,
0xf2, 0x4a, 0xf6, 0xb2, 0x19, 0x83, 0xea, 0xed,
0x6d, 0xba, 0xfe, 0xd8, 0xb6, 0xa3, 0x5a, 0xb4,
0x48, 0xfa, 0xbe, 0x5c, 0x69, 0xac, 0x3c, 0x8f,
0x63, 0xaf, 0xa4, 0x42, 0x25, 0x50, 0xab, 0x65,
0x80, 0x65, 0xb9, 0xfb, 0xc7, 0xf2, 0x2d, 0x5c,
0xe3, 0x4c, 0xa4, 0xa6, 0x8e, 0x07, 0x9c, 0xeb,
0x41, 0x93, 0x65, 0x44, 0x4a, 0x86, 0xc1, 0xf6,
0x2c, 0x97, 0xfd, 0xf4, 0x6c, 0xdc, 0xe1, 0xe0,
0x28, 0xd9, 0x89, 0x7b, 0x09, 0xe2, 0xa0, 0x38,
0x74, 0x4a, 0xa6, 0x5e, 0xd2, 0xe2, 0x4d, 0xf3,
0xf4, 0xc6, 0xbc, 0xa2, 0x51, 0x58, 0xe8, 0xae,
};
/***************************************************************************
***************************************************************************/
void
blackrock_init(struct BlackRock *br, uint64_t range, uint64_t seed, unsigned rounds)
{
double foo = sqrt(range * 1.0);
/* This algorithm gets very non-random at small numbers, so I'm going
* to try to fix some constants here to make it work. It doesn't have
* to be good, since it's kinda pointless having ranges this small */
switch (range) {
case 0:
br->a = 0;
br->b = 0;
break;
case 1:
br->a = 1;
br->b = 1;
break;
case 2:
br->a = 1;
br->b = 2;
break;
case 3:
br->a = 2;
br->b = 2;
break;
case 4:
case 5:
case 6:
br->a = 2;
br->b = 3;
break;
case 7:
case 8:
br->a = 3;
br->b = 3;
break;
default:
br->range = range;
br->a = (uint64_t)(foo - 2);
br->b = (uint64_t)(foo + 3);
break;
}
while (br->a * br->b <= range)
br->b++;
br->rounds = rounds;
br->seed = seed;
br->range = range;
}
/***************************************************************************
* The inner round/mixer function. In DES, it's a series of S-box lookups,
* which
***************************************************************************/
static inline uint64_t
READ(uint64_t r, uint64_t R, uint64_t seed)
{
uint64_t r0, r1, r2, r3;
#define GETBYTE(R,n) ((((R)>>(n*8))^seed^r)&0xFF)
R ^= (seed << r) ^ (seed >> (64 - r));
r0 = sbox[GETBYTE(R,0)]<< 0 | sbox[GETBYTE(R,1)]<< 8;
r1 = (sbox[GETBYTE(R,2)]<<16UL | sbox[GETBYTE(R,3)]<<24UL)&0x0ffffFFFFUL;
r2 = sbox[GETBYTE(R,4)]<< 0 | sbox[GETBYTE(R,5)]<< 8;
r3 = (sbox[GETBYTE(R,6)]<<16UL | sbox[GETBYTE(R,7)]<<24UL)&0x0ffffFFFFUL;
R = r0 ^ r1 ^ r2<<23UL ^ r3<<33UL;
return R;
}
/***************************************************************************
*
* NOTE:
* the names in this function are cryptic in order to match as closely
* as possible the pseudocode in the following paper:
* http://www.cs.ucdavis.edu/~rogaway/papers/subset.pdf
* Read that paper in order to understand this code.
***************************************************************************/
static inline uint64_t
ENCRYPT(unsigned r, uint64_t a, uint64_t b, uint64_t m, uint64_t seed)
{
uint64_t L, R;
unsigned j;
uint64_t tmp;
L = m % a;
R = m / a;
for (j=1; j<=r; j++) {
if (j & 1) {
tmp = (L + READ(j, R, seed)) % a;
} else {
tmp = (L + READ(j, R, seed)) % b;
}
L = R;
R = tmp;
}
if (r & 1) {
return a * L + R;
} else {
return a * R + L;
}
}
/***************************************************************************
***************************************************************************/
static inline uint64_t
UNENCRYPT(unsigned r, uint64_t a, uint64_t b, uint64_t m, uint64_t seed)
{
uint64_t L, R;
unsigned j;
uint64_t tmp;
if (r & 1) {
R = m % a;
L = m / a;
} else {
L = m % a;
R = m / a;
}
for (j=r; j>=1; j--) {
if (j & 1) {
tmp = READ(j, L, seed);
if (tmp > R) {
tmp = (tmp - R);
tmp = a - (tmp%a);
if (tmp == a)
tmp = 0;
} else {
tmp = (R - tmp);
tmp %= a;
}
} else {
tmp = READ(j, L, seed);
if (tmp > R) {
tmp = (tmp - R);
tmp = b - (tmp%b);
if (tmp == b)
tmp = 0;
} else {
tmp = (R - tmp);
tmp %= b;
}
}
R = L;
L = tmp;
}
return a * R + L;
}
/***************************************************************************
***************************************************************************/
uint64_t
blackrock_shuffle(const struct BlackRock *br, uint64_t m)
{
uint64_t c;
c = ENCRYPT(br->rounds, br->a, br->b, m, br->seed);
while (c >= br->range)
c = ENCRYPT(br->rounds, br->a, br->b, c, br->seed);
return c;
}
/***************************************************************************
***************************************************************************/
uint64_t
blackrock_unshuffle(const struct BlackRock *br, uint64_t m)
{
uint64_t c;
c = UNENCRYPT(br->rounds, br->a, br->b, m, br->seed);
while (c >= br->range)
c = UNENCRYPT(br->rounds, br->a, br->b, c, br->seed);
return c;
}
/***************************************************************************
* This function called only during selftest/regression-test.
***************************************************************************/
static unsigned
blackrock_verify(struct BlackRock *br, uint64_t max)
{
unsigned char *list;
uint64_t i;
unsigned is_success = 1;
uint64_t range = br->range;
/* Allocate a list of 1-byte counters */
list = CALLOC(1, (size_t)((range
struct BlackRock {
uint64_t range;
uint64_t a;
uint64_t b;
uint64_t seed;
unsigned rounds;
uint64_t a_bits;
uint64_t a_mask;
uint64_t b_bits;
uint64_t b_mask;
};
/**
* Initializes a structure for shuffling numbers within
* a range.
*
* @param range
* The size of the range of numbers needing to be
* shuffled/randomized.
*/
void
blackrock_init(struct BlackRock *br, uint64_t range, uint64_t seed, unsigned rounds);
void
blackrock2_init(struct BlackRock *br, uint64_t range, uint64_t seed, unsigned rounds);
/**
* Given a number within a range, produce a different number with
* the same range. There is a 1-to-1 mapping between the two,
* so when linearly incrementing through the range, the output
* of this function won't repeat. In other words, encrypt the index variable.
* @param br
* The randomization parameters created with 'blackrock_init()'
* @param index
* An input within the specified range. We call it an 'index' variable
* because that's how we intend to use this function, shuffling a
* monotonically increasing index variable, but in truth, any sort
* of integer can be used. This must be within the 'range' specified
* during the call to blackrock_init(), or the results are undefined.
* @return
* A one-to-one matching index that's in the same range.
*/
uint64_t
blackrock_shuffle(const struct BlackRock *br, uint64_t index);
uint64_t
blackrock2_shuffle(const struct BlackRock *br, uint64_t index);
/**
* The reverse of the shuffle function above: given the shuffled/encrypted
* integer, return the original index value before the shuffling/encryption.
*/
uint64_t
blackrock_unshuffle(const struct BlackRock *br, uint64_t m);
uint64_t
blackrock2_unshuffle(const struct BlackRock *br, uint64_t m);
/**
* Do a regression test.
* @return
* 0 of the regression test succeeds or non-zero if it fails
*/
int
blackrock_selftest(void);
int
blackrock2_selftest(void);
/**
* Do a benchmark of this module regression test.
*/
void
blackrock_benchmark(unsigned rounds);
void
blackrock2_benchmark(unsigned rounds);
#endif
================================================
FILE: src/crypto-blackrock2.c
================================================
#include "crypto-blackrock.h"
#include "pixie-timer.h"
#include "unusedparm.h"
#include "util-malloc.h"
#include "util-safefunc.h"
#include
#include
#include
#include
#include
#include
#include
#if defined(_MSC_VER)
#define inline _inline
#endif
/*
* Expanded DES S-boxes
*/
static const uint32_t SB1[64] =
{
0x01010400, 0x00000000, 0x00010000, 0x01010404,
0x01010004, 0x00010404, 0x00000004, 0x00010000,
0x00000400, 0x01010400, 0x01010404, 0x00000400,
0x01000404, 0x01010004, 0x01000000, 0x00000004,
0x00000404, 0x01000400, 0x01000400, 0x00010400,
0x00010400, 0x01010000, 0x01010000, 0x01000404,
0x00010004, 0x01000004, 0x01000004, 0x00010004,
0x00000000, 0x00000404, 0x00010404, 0x01000000,
0x00010000, 0x01010404, 0x00000004, 0x01010000,
0x01010400, 0x01000000, 0x01000000, 0x00000400,
0x01010004, 0x00010000, 0x00010400, 0x01000004,
0x00000400, 0x00000004, 0x01000404, 0x00010404,
0x01010404, 0x00010004, 0x01010000, 0x01000404,
0x01000004, 0x00000404, 0x00010404, 0x01010400,
0x00000404, 0x01000400, 0x01000400, 0x00000000,
0x00010004, 0x00010400, 0x00000000, 0x01010004
};
static const uint32_t SB2[64] =
{
0x80108020, 0x80008000, 0x00008000, 0x00108020,
0x00100000, 0x00000020, 0x80100020, 0x80008020,
0x80000020, 0x80108020, 0x80108000, 0x80000000,
0x80008000, 0x00100000, 0x00000020, 0x80100020,
0x00108000, 0x00100020, 0x80008020, 0x00000000,
0x80000000, 0x00008000, 0x00108020, 0x80100000,
0x00100020, 0x80000020, 0x00000000, 0x00108000,
0x00008020, 0x80108000, 0x80100000, 0x00008020,
0x00000000, 0x00108020, 0x80100020, 0x00100000,
0x80008020, 0x80100000, 0x80108000, 0x00008000,
0x80100000, 0x80008000, 0x00000020, 0x80108020,
0x00108020, 0x00000020, 0x00008000, 0x80000000,
0x00008020, 0x80108000, 0x00100000, 0x80000020,
0x00100020, 0x80008020, 0x80000020, 0x00100020,
0x00108000, 0x00000000, 0x80008000, 0x00008020,
0x80000000, 0x80100020, 0x80108020, 0x00108000
};
static const uint32_t SB3[64] =
{
0x00000208, 0x08020200, 0x00000000, 0x08020008,
0x08000200, 0x00000000, 0x00020208, 0x08000200,
0x00020008, 0x08000008, 0x08000008, 0x00020000,
0x08020208, 0x00020008, 0x08020000, 0x00000208,
0x08000000, 0x00000008, 0x08020200, 0x00000200,
0x00020200, 0x08020000, 0x08020008, 0x00020208,
0x08000208, 0x00020200, 0x00020000, 0x08000208,
0x00000008, 0x08020208, 0x00000200, 0x08000000,
0x08020200, 0x08000000, 0x00020008, 0x00000208,
0x00020000, 0x08020200, 0x08000200, 0x00000000,
0x00000200, 0x00020008, 0x08020208, 0x08000200,
0x08000008, 0x00000200, 0x00000000, 0x08020008,
0x08000208, 0x00020000, 0x08000000, 0x08020208,
0x00000008, 0x00020208, 0x00020200, 0x08000008,
0x08020000, 0x08000208, 0x00000208, 0x08020000,
0x00020208, 0x00000008, 0x08020008, 0x00020200
};
static const uint32_t SB4[64] =
{
0x00802001, 0x00002081, 0x00002081, 0x00000080,
0x00802080, 0x00800081, 0x00800001, 0x00002001,
0x00000000, 0x00802000, 0x00802000, 0x00802081,
0x00000081, 0x00000000, 0x00800080, 0x00800001,
0x00000001, 0x00002000, 0x00800000, 0x00802001,
0x00000080, 0x00800000, 0x00002001, 0x00002080,
0x00800081, 0x00000001, 0x00002080, 0x00800080,
0x00002000, 0x00802080, 0x00802081, 0x00000081,
0x00800080, 0x00800001, 0x00802000, 0x00802081,
0x00000081, 0x00000000, 0x00000000, 0x00802000,
0x00002080, 0x00800080, 0x00800081, 0x00000001,
0x00802001, 0x00002081, 0x00002081, 0x00000080,
0x00802081, 0x00000081, 0x00000001, 0x00002000,
0x00800001, 0x00002001, 0x00802080, 0x00800081,
0x00002001, 0x00002080, 0x00800000, 0x00802001,
0x00000080, 0x00800000, 0x00002000, 0x00802080
};
static const uint32_t SB5[64] =
{
0x00000100, 0x02080100, 0x02080000, 0x42000100,
0x00080000, 0x00000100, 0x40000000, 0x02080000,
0x40080100, 0x00080000, 0x02000100, 0x40080100,
0x42000100, 0x42080000, 0x00080100, 0x40000000,
0x02000000, 0x40080000, 0x40080000, 0x00000000,
0x40000100, 0x42080100, 0x42080100, 0x02000100,
0x42080000, 0x40000100, 0x00000000, 0x42000000,
0x02080100, 0x02000000, 0x42000000, 0x00080100,
0x00080000, 0x42000100, 0x00000100, 0x02000000,
0x40000000, 0x02080000, 0x42000100, 0x40080100,
0x02000100, 0x40000000, 0x42080000, 0x02080100,
0x40080100, 0x00000100, 0x02000000, 0x42080000,
0x42080100, 0x00080100, 0x42000000, 0x42080100,
0x02080000, 0x00000000, 0x40080000, 0x42000000,
0x00080100, 0x02000100, 0x40000100, 0x00080000,
0x00000000, 0x40080000, 0x02080100, 0x40000100
};
static const uint32_t SB6[64] =
{
0x20000010, 0x20400000, 0x00004000, 0x20404010,
0x20400000, 0x00000010, 0x20404010, 0x00400000,
0x20004000, 0x00404010, 0x00400000, 0x20000010,
0x00400010, 0x20004000, 0x20000000, 0x00004010,
0x00000000, 0x00400010, 0x20004010, 0x00004000,
0x00404000, 0x20004010, 0x00000010, 0x20400010,
0x20400010, 0x00000000, 0x00404010, 0x20404000,
0x00004010, 0x00404000, 0x20404000, 0x20000000,
0x20004000, 0x00000010, 0x20400010, 0x00404000,
0x20404010, 0x00400000, 0x00004010, 0x20000010,
0x00400000, 0x20004000, 0x20000000, 0x00004010,
0x20000010, 0x20404010, 0x00404000, 0x20400000,
0x00404010, 0x20404000, 0x00000000, 0x20400010,
0x00000010, 0x00004000, 0x20400000, 0x00404010,
0x00004000, 0x00400010, 0x20004010, 0x00000000,
0x20404000, 0x20000000, 0x00400010, 0x20004010
};
static const uint32_t SB7[64] =
{
0x00200000, 0x04200002, 0x04000802, 0x00000000,
0x00000800, 0x04000802, 0x00200802, 0x04200800,
0x04200802, 0x00200000, 0x00000000, 0x04000002,
0x00000002, 0x04000000, 0x04200002, 0x00000802,
0x04000800, 0x00200802, 0x00200002, 0x04000800,
0x04000002, 0x04200000, 0x04200800, 0x00200002,
0x04200000, 0x00000800, 0x00000802, 0x04200802,
0x00200800, 0x00000002, 0x04000000, 0x00200800,
0x04000000, 0x00200800, 0x00200000, 0x04000802,
0x04000802, 0x04200002, 0x04200002, 0x00000002,
0x00200002, 0x04000000, 0x04000800, 0x00200000,
0x04200800, 0x00000802, 0x00200802, 0x04200800,
0x00000802, 0x04000002, 0x04200802, 0x04200000,
0x00200800, 0x00000000, 0x00000002, 0x04200802,
0x00000000, 0x00200802, 0x04200000, 0x00000800,
0x04000002, 0x04000800, 0x00000800, 0x00200002
};
static const uint32_t SB8[64] =
{
0x10001040, 0x00001000, 0x00040000, 0x10041040,
0x10000000, 0x10001040, 0x00000040, 0x10000000,
0x00040040, 0x10040000, 0x10041040, 0x00041000,
0x10041000, 0x00041040, 0x00001000, 0x00000040,
0x10040000, 0x10000040, 0x10001000, 0x00001040,
0x00041000, 0x00040040, 0x10040040, 0x10041000,
0x00001040, 0x00000000, 0x00000000, 0x10040040,
0x10000040, 0x10001000, 0x00041040, 0x00040000,
0x00041040, 0x00040000, 0x10041000, 0x00001000,
0x00000040, 0x10040040, 0x00001000, 0x00041040,
0x10001000, 0x00000040, 0x10000040, 0x10040000,
0x10040040, 0x10000000, 0x00040000, 0x10001040,
0x00000000, 0x10041040, 0x00040040, 0x10000040,
0x10040000, 0x10001000, 0x10001040, 0x00000000,
0x10041040, 0x00041000, 0x00041000, 0x00001040,
0x00001040, 0x00040040, 0x10000000, 0x10041000
};
/***************************************************************************
* It's an s-box. You gotta have an s-box
***************************************************************************/
const unsigned char sbox2[] = {
0x91, 0x58, 0xb3, 0x31, 0x6c, 0x33, 0xda, 0x88,
0x57, 0xdd, 0x8c, 0xf2, 0x29, 0x5a, 0x08, 0x9f,
0x49, 0x34, 0xce, 0x99, 0x9e, 0xbf, 0x0f, 0x81,
0xd4, 0x2f, 0x92, 0x3f, 0x95, 0xf5, 0x23, 0x00,
0x0d, 0x3e, 0xa8, 0x90, 0x98, 0xdd, 0x20, 0x00,
0x03, 0x69, 0x0a, 0xca, 0xba, 0x12, 0x08, 0x41,
0x6e, 0xb9, 0x86, 0xe4, 0x50, 0xf0, 0x84, 0xe2,
0xb3, 0xb3, 0xc8, 0xb5, 0xb2, 0x2d, 0x18, 0x70,
0x0a, 0xd7, 0x92, 0x90, 0x9e, 0x1e, 0x0c, 0x1f,
0x08, 0xe8, 0x06, 0xfd, 0x85, 0x2f, 0xaa, 0x5d,
0xcf, 0xf9, 0xe3, 0x55, 0xb9, 0xfe, 0xa6, 0x7f,
0x44, 0x3b, 0x4a, 0x4f, 0xc9, 0x2f, 0xd2, 0xd3,
0x8e, 0xdc, 0xae, 0xba, 0x4f, 0x02, 0xb4, 0x76,
0xba, 0x64, 0x2d, 0x07, 0x9e, 0x08, 0xec, 0xbd,
0x52, 0x29, 0x07, 0xbb, 0x9f, 0xb5, 0x58, 0x6f,
0x07, 0x55, 0xb0, 0x34, 0x74, 0x9f, 0x05, 0xb2,
0xdf, 0xa9, 0xc6, 0x2a, 0xa3, 0x5d, 0xff, 0x10,
0x40, 0xb3, 0xb7, 0xb4, 0x63, 0x6e, 0xf4, 0x3e,
0xee, 0xf6, 0x49, 0x52, 0xe3, 0x11, 0xb3, 0xf1,
0xfb, 0x60, 0x48, 0xa1, 0xa4, 0x19, 0x7a, 0x2e,
0x90, 0x28, 0x90, 0x8d, 0x5e, 0x8c, 0x8c, 0xc4,
0xf2, 0x4a, 0xf6, 0xb2, 0x19, 0x83, 0xea, 0xed,
0x6d, 0xba, 0xfe, 0xd8, 0xb6, 0xa3, 0x5a, 0xb4,
0x48, 0xfa, 0xbe, 0x5c, 0x69, 0xac, 0x3c, 0x8f,
0x63, 0xaf, 0xa4, 0x42, 0x25, 0x50, 0xab, 0x65,
0x80, 0x65, 0xb9, 0xfb, 0xc7, 0xf2, 0x2d, 0x5c,
0xe3, 0x4c, 0xa4, 0xa6, 0x8e, 0x07, 0x9c, 0xeb,
0x41, 0x93, 0x65, 0x44, 0x4a, 0x86, 0xc1, 0xf6,
0x2c, 0x97, 0xfd, 0xf4, 0x6c, 0xdc, 0xe1, 0xe0,
0x28, 0xd9, 0x89, 0x7b, 0x09, 0xe2, 0xa0, 0x38,
0x74, 0x4a, 0xa6, 0x5e, 0xd2, 0xe2, 0x4d, 0xf3,
0xf4, 0xc6, 0xbc, 0xa2, 0x51, 0x58, 0xe8, 0xae,
0x91, 0x58, 0xb3, 0x31, 0x6c, 0x33, 0xda, 0x88,
};
/****************************************************************************
* Given a number, figure out the nearest power-of-two (16,32,64,128,etc.)
* that can hold that number. We do this so that we can convert multiplies
* into shifts.
****************************************************************************/
static uint64_t
next_power_of_two(uint64_t num)
{
uint64_t power_of_two = 1;
num++;
while ((uint64_t)(1ULL << power_of_two) < num)
power_of_two++;
return (1ULL << power_of_two);
}
static uint64_t
bit_count(uint64_t num)
{
uint64_t bits = 0;
while ((num >> bits) > 1)
bits++;
return bits;
}
/***************************************************************************
***************************************************************************/
void
blackrock2_init(struct BlackRock *br, uint64_t range, uint64_t seed, unsigned rounds)
{
uint64_t a;
uint64_t b;
a = next_power_of_two(
(uint64_t)sqrt(range * 1.0)
);
b = next_power_of_two(range/a);
//printf("a=%llu b=%llu seed = 0x%llu\n", a, b, seed);
br->range = range;
br->a = a;
br->a_bits = bit_count(br->a);
br->a_mask = br->a - 1ULL;
br->b = b;
br->b_bits = bit_count(br->b);
br->b_mask = br->b - 1ULL;
//printf("a: 0x%llx / %llu\n", br->a_mask, br->a_bits);
//printf("b: 0x%llx / %llu\n", br->b_mask, br->b_bits);
br->rounds = rounds;
br->seed = seed;
br->range = range;
}
/***************************************************************************
* The inner round/mixer function. In DES, it's a series of S-box lookups,
* which
***************************************************************************/
static inline uint64_t
ROUND(uint64_t r, uint64_t R, uint64_t seed)
{
#define GETBYTE(R,n) ((uint64_t)(((((R)>>(n*8ULL)))&0xFFULL)))
#if 0
uint64_t r0, r1, r2, r3;
#endif
uint64_t T, Y;
T = R ^ ((seed>>r) | (seed<<(64-r)));
if (r & 1) {
Y = SB8[ (T ) & 0x3F ] ^ \
SB6[ (T >> 8) & 0x3F ] ^ \
SB4[ (T >> 16) & 0x3F ] ^ \
SB2[ (T >> 24) & 0x3F ]; \
} else {
Y = SB7[ (T ) & 0x3F ] ^ \
SB5[ (T >> 8) & 0x3F ] ^ \
SB3[ (T >> 16) & 0x3F ] ^ \
SB1[ (T >> 24) & 0x3F ];
}
return Y;
#if 0
r0 = sbox2[GETBYTE(R,0)]<< 6 | sbox2[GETBYTE(R,1)]<< 0;
r1 = sbox2[GETBYTE(R,2)]<< 6 | sbox2[GETBYTE(R,5)]<< 0;
r2 = sbox2[GETBYTE(R,4)]<< 6 | sbox2[GETBYTE(R,5)]<< 0;
r3 = sbox2[GETBYTE(R,6)]<< 6 | sbox2[GETBYTE(R,7)]<< 0;
R = r0 ^ (r1<<12) * (r2 << 24) ^ (r3 << 36) * r;
return R;
/*return((uint64_t)sbox2[GETBYTE(R,7ULL)]<< 0ULL)
| ((uint64_t)sbox2[GETBYTE(R,6ULL)]<< 8ULL)
| ((uint64_t)sbox2[GETBYTE(R,5ULL)]<<16ULL)
| ((uint64_t)sbox2[GETBYTE(R,4ULL)]<<24ULL)
| ((uint64_t)sbox2[GETBYTE(R,3ULL)]<<32ULL)
| ((uint64_t)sbox2[GETBYTE(R,2ULL)]<<40ULL)
| ((uint64_t)sbox2[GETBYTE(R,1ULL)]<<48ULL)
| ((uint64_t)sbox2[GETBYTE(R,0ULL)]<<56ULL)
;*/
return R;
#endif
}
/***************************************************************************
***************************************************************************/
static inline uint64_t
ENCRYPT(unsigned r, uint64_t a_bits, uint64_t a_mask, uint64_t b_bits, uint64_t b_mask, uint64_t m, uint64_t seed)
{
uint64_t L, R;
unsigned j = 1;
uint64_t tmp;
UNUSEDPARM(b_bits);
L = m & a_mask;
R = m >> a_bits;
for (j=1; j<=r; j++) {
tmp = (L + ROUND(j, R, seed)) & a_mask;
L = R;
R = tmp;
j++;
tmp = (L + ROUND(j, R, seed)) & b_mask;
L = R;
R = tmp;
}
if ((j-1) & 1) {
return (L << (a_bits)) + R;
} else {
return (R << (a_bits)) + L;
}
}
static inline uint64_t
DECRYPT(unsigned r, uint64_t a, uint64_t b, uint64_t m, uint64_t seed)
{
uint64_t L, R;
unsigned j;
uint64_t tmp;
if (r & 1) {
R = m % a;
L = m / a;
} else {
L = m % a;
R = m / a;
}
for (j=r; j>=1; j--) {
if (j & 1) {
tmp = ROUND(j, L, seed);
if (tmp > R) {
tmp = (tmp - R);
tmp = a - (tmp%a);
if (tmp == a)
tmp = 0;
} else {
tmp = (R - tmp);
tmp %= a;
}
} else {
tmp = ROUND(j, L, seed);
if (tmp > R) {
tmp = (tmp - R);
tmp = b - (tmp%b);
if (tmp == b)
tmp = 0;
} else {
tmp = (R - tmp);
tmp %= b;
}
}
R = L;
L = tmp;
}
return a * R + L;
}
/***************************************************************************
***************************************************************************/
uint64_t
blackrock2_shuffle(const struct BlackRock *br, uint64_t m)
{
uint64_t c;
c = ENCRYPT(br->rounds, br->a_bits, br->a_mask, br->b_bits, br->b_mask, m, br->seed);
while (c >= br->range)
c = ENCRYPT(br->rounds, br->a_bits, br->a_mask, br->b_bits, br->b_mask, c, br->seed);
return c;
}
/***************************************************************************
***************************************************************************/
uint64_t
blackrock2_unshuffle(const struct BlackRock *br, uint64_t m)
{
uint64_t c;
c = DECRYPT(br->rounds, br->a, br->b, m, br->seed);
while (c >= br->range)
c = DECRYPT(br->rounds, br->a, br->b, c, br->seed);
return c;
}
/***************************************************************************
* This function called only during selftest/regression-test.
***************************************************************************/
static unsigned
verify(struct BlackRock *br, uint64_t max)
{
unsigned char *list;
uint64_t i;
unsigned is_success = 1;
uint64_t range = br->range;
/* Allocate a list of 1-byte counters */
list = CALLOC(1, (size_t)((range /* for 'sqrt()', may need -lm for gcc */
#include
#include
#include
#include
#include
/**
* A 64 bit number can't have more than 16 prime factors. The first factors
* are:
* 2*3*5*7*11*13*17*19*23*29*31*37*41*43*47*53 = 0xC443F2F861D29C3A
* 0123456789abcdef
* We zero termiante this list, so we are going to reserve 20 slots.
*/
typedef uint64_t PRIMEFACTORS[20];
/****************************************************************************
* Break down the number into prime factors using DJB's sieve code, which
* is about 5 to 10 times faster than the Sieve of Eratosthenes.
*
* @param number
* The integer that we are factoring. It can be any value up to 64 bits
* in size.
* @param factors
* The list of all the prime factors, zero terminated.
* @param non_factors
* A list of smallest numbers that aren't prime factors. We return
* this because we are going to use prime non-factors for finding
* interesting numbers.
****************************************************************************/
static unsigned
sieve_prime_factors(uint64_t number, PRIMEFACTORS factors,
PRIMEFACTORS non_factors, double *elapsed)
{
primegen pg;
clock_t start;
clock_t stop;
uint64_t prime;
uint64_t max;
unsigned factor_count = 0;
unsigned non_factor_count = 0;
/*
* We only need to sieve up to the square-root of the target number. Only
* one prime factor can be bigger than the square root, so once we find
* all the other primes, the square root is the only one left.
* Note: you have to link to the 'm' math library for some gcc platforms.
*/
max = (uint64_t)sqrt(number + 1.0);
/*
* Init the DJB primegen library.
*/
primegen_init(&pg);
/*
* Enumerate all the primes starting with 2
*/
start = clock();
for (;;) {
/* Sieve the next prime */
prime = primegen_next(&pg);
/* If we've reached the square root, then that's as far as we need
* to go */
if (prime > max)
break;
/* If this prime is not a factor (evenly divisible with no remainder)
* then loop back and get the next prime */
if ((number % prime) != 0) {
if (non_factor_count < 12)
non_factors[non_factor_count++] = prime;
continue;
}
/* Else we've found a prime factor, so add this to the list of primes */
factors[factor_count++] = prime;
/* At the end, we may have one prime factor left that's bigger than the
* sqrt. Therefore, as we go along, divide the original number
* (possibly several times) by the prime factor so that this large
* remaining factor will be the only one left */
while ((number % prime) == 0)
number /= prime;
/* exit early if we've found all prime factors. comment out this
* code if you want to benchmark it */
if (number == 1 && non_factor_count > 10)
break;
}
/*
* See if there is one last prime that's bigger than the square root.
* Note: This is the only number that can be larger than 32-bits in the
* way this code is written.
*/
if (number != 1)
factors[factor_count++] = number;
/*
* Zero terminate the results.
*/
factors[factor_count] = 0;
non_factors[non_factor_count] = 0;
/*
* Since prime factorization takes a long time, especially on slow
* CPUs, we benchmark it to keep track of performance.
*/
stop = clock();
if (elapsed)
*elapsed = ((double)stop - (double)start)/(double)CLOCKS_PER_SEC;
/* should always be at least 1, because if the number itself is prime,
* then that's it's only prime factor */
return factor_count;
}
/****************************************************************************
* Do a pseudo-random 1-to-1 translation of a number within a range to
* another number in that range.
*
* The constants 'a' and 'c' must be chosen to match the LCG algorithm
* to fit 'm' (range).
*
* This the same as the function 'rand()', except all the constants and
* seeds are specified as parameters.
*
* @param index
* The index within the range that we are randomizing.
* @param a
* The 'multiplier' of the LCG algorithm.
* @param c
* The 'increment' of the LCG algorithm.
* @param range
* The 'modulus' of the LCG algorithm.
****************************************************************************/
uint64_t
lcg_rand(uint64_t index, uint64_t a, uint64_t c, uint64_t range)
{
return (index * a + c) % range;
}
/****************************************************************************
* Verify the LCG algorithm. You shouldn't do this for large ranges,
* because we'll run out of memory. Therefore, this algorithm allocates
* a buffer only up to a smaller range. We still have to traverse the
* entire range of numbers, but we only need store values for a smaller
* range. If 10% of the range checks out, then there's a good chance
* it applies to the other 90% as well.
*
* This works by counting the results of rand(), which should be produced
* exactly once.
****************************************************************************/
static unsigned
lcg_verify(uint64_t a, uint64_t c, uint64_t range, uint64_t max)
{
unsigned char *list;
uint64_t i;
unsigned is_success = 1;
/* Allocate a list of 1-byte counters */
list = CALLOC(1, (size_t)((range= 70) {
count = 0;
printf("\n");
}
}
printf("\n");
}
}
*out_a = a;
*inout_c = c;
}
/***************************************************************************
***************************************************************************/
int
lcg_selftest(void)
{
unsigned i;
int is_success = 0;
uint64_t m, a, c;
m = 3015 * 3;
for (i=0; i<5; i++) {
a = 0;
c = 0;
m += 10 + i;
lcg_calculate_constants(m, &a, &c, 0);
is_success = lcg_verify(a, c, m, m);
if (!is_success) {
fprintf(stderr, "LCG: randomization failed\n");
return 1; /*fail*/
}
}
return 0; /*success*/
}
================================================
FILE: src/crypto-lcg.h
================================================
#ifndef RAND_LCG_H
#define RAND_LCG_H
#include
void
lcg_calculate_constants(uint64_t m, uint64_t *out_a, uint64_t *inout_c, int is_debug);
uint64_t
lcg_rand(uint64_t index, uint64_t a, uint64_t c, uint64_t range);
/**
* Performs a regression test on this module.
* @return
* 0 on success, or a positive integer on failure
*/
int
lcg_selftest(void);
#endif
================================================
FILE: src/crypto-primegen.c
================================================
/*
This is DJB's code for calculating primes, with a few modifications,
such as making it work with Microsoft's compiler on Windows, and
getting rid of warnings.
*/
#include "crypto-primegen.h"
/*
B is 32 times X.
Total memory use for one generator is 2B bytes = 64X bytes.
Covers primes in an interval of length 1920X.
Working set size for one generator is B bits = 4X bytes.
Speedup by a factor of 2 or 3 for L1 cache instead of L2 cache.
Slowdown by a factor of roughly n for primes past (nB)^2.
Possible choices of X:
2002 to fit inside an 8K L1 cache (e.g., Pentium).
4004 to fit inside a 16K L1 cache (e.g., Pentium II).
64064 to fit inside a 256K L2 cache.
There are various word-size limits on X; 1000000 should still be okay.
*/
#define B32 PRIMEGEN_WORDS
#define B (PRIMEGEN_WORDS * 32)
#ifdef _MSC_VER
#pragma warning(disable:4244)
#endif
static const uint32_t two[32] = {
0x00000001 , 0x00000002 , 0x00000004 , 0x00000008
, 0x00000010 , 0x00000020 , 0x00000040 , 0x00000080
, 0x00000100 , 0x00000200 , 0x00000400 , 0x00000800
, 0x00001000 , 0x00002000 , 0x00004000 , 0x00008000
, 0x00010000 , 0x00020000 , 0x00040000 , 0x00080000
, 0x00100000 , 0x00200000 , 0x00400000 , 0x00800000
, 0x01000000 , 0x02000000 , 0x04000000 , 0x08000000
, 0x10000000 , 0x20000000 , 0x40000000 , 0x80000000
} ;
static void clear(register uint32_t (*buf)[B32])
{
register int i;
register int j;
for (j = 0;j < 16;++j) {
for (i = 0;i < B32;++i)
(*buf)[i] = (uint32_t)~0;
++buf;
}
}
static void doit4(register uint32_t *a,register long x,register long y,int64_t start)
{
long i0;
long y0;
register long i;
register uint32_t data;
register uint32_t pos;
register uint32_t bits;
x += x; x += 15;
y += 15;
start += 1000000000;
while (start < 0) { start += x; x += 30; }
start -= 1000000000;
i = start;
while (i < B) { i += x; x += 30; }
for (;;) {
x -= 30;
if (x <= 15) return;
i -= x;
while (i < 0) { i += y; y += 30; }
i0 = i; y0 = y;
while (i < B) {
pos = (uint32_t)i; data = (uint32_t)i;
pos >>= 5; data &= 31;
i += y; y += 30;
bits = a[pos]; data = two[data];
bits ^= data;
a[pos] = bits;
}
i = i0; y = y0;
}
}
static void doit6(register uint32_t *a,register long x,register long y,int64_t start)
{
long i0;
long y0;
register long i;
register uint32_t data;
register uint32_t pos;
register uint32_t bits;
x += 5;
y += 15;
start += 1000000000;
while (start < 0) { start += x; x += 10; }
start -= 1000000000;
i = start;
while (i < B) { i += x; x += 10; }
for (;;) {
x -= 10;
if (x <= 5) return;
i -= x;
while (i < 0) { i += y; y += 30; }
i0 = i; y0 = y;
while (i < B) {
pos = (uint32_t)i; data = (uint32_t)i;
pos >>= 5; data &= 31;
i += y; y += 30;
bits = a[pos]; data = two[data];
bits ^= data;
a[pos] = bits;
}
i = i0; y = y0;
}
}
static void doit12(register uint32_t *a,register long x,register long y,int64_t start)
{
register long i;
register uint32_t data;
register uint32_t bits;
x += 5;
start += 1000000000;
while (start < 0) { start += x; x += 10; }
start -= 1000000000;
i = start;
while (i < 0) { i += x; x += 10; }
y += 15;
x += 10;
for (;;) {
long i0;
long y0;
while (i >= B) {
if (x <= y) return;
i -= y;
y += 30;
}
i0 = i;
y0 = y;
while ((i >= 0) && (y < x)) {
register uint32_t pos;
pos = (uint32_t)i; data = (uint32_t)i;
pos >>= 5; data &= 31;
i -= y;
y += 30;
bits = a[pos]; data = two[data];
bits ^= data;
a[pos] = bits;
}
i = i0;
y = y0;
i += x - 10;
x += 10;
}
}
static const int deltainverse[60] = {
-1,B32 * 0,-1,-1,-1,-1,-1,B32 * 1,-1,-1,-1,B32 * 2,-1,B32 * 3,-1
,-1,-1,B32 * 4,-1,B32 * 5,-1,-1,-1,B32 * 6,-1,-1,-1,-1,-1,B32 * 7
,-1,B32 * 8,-1,-1,-1,-1,-1,B32 * 9,-1,-1,-1,B32 * 10,-1,B32 * 11,-1
,-1,-1,B32 * 12,-1,B32 * 13,-1,-1,-1,B32 * 14,-1,-1,-1,-1,-1,B32 * 15
} ;
static void squarefree1big(uint32_t (*buf)[B32],uint64_t base,uint32_t q,uint64_t qq)
{
uint64_t i;
uint32_t pos;
int n;
uint64_t bound = base + 60 * B;
while (qq < bound) {
if (bound < 2000000000)
i = qq - (((uint32_t) base) % ((uint32_t) qq));
else
i = qq - (base % qq);
if (!(i & 1)) i += qq;
if (i < B * 60) {
pos = (uint32_t)i;
n = deltainverse[pos % 60];
if (n >= 0) {
pos /= 60;
(*buf)[n + (pos >> 5)] |= two[pos & 31];
}
}
qq += q; q += 1800;
}
}
static void squarefree1(register uint32_t (*buf)[B32],uint64_t L,uint32_t q)
{
uint32_t qq;
register uint32_t qqhigh;
uint32_t i;
register uint32_t ilow;
register uint32_t ihigh;
register int n;
uint64_t base;
base = 60 * L;
qq = q * q;
q = 60 * q + 900;
while (qq < B * 60) {
if (base < 2000000000)
i = qq - (((uint32_t) base) % qq);
else
i = qq - (base % qq);
if (!(i & 1)) i += qq;
if (i < B * 60) {
qqhigh = qq / 60;
ilow = i % 60; ihigh = i / 60;
qqhigh += qqhigh;
while (ihigh < B) {
n = deltainverse[ilow];
if (n >= 0)
(*buf)[n + (ihigh >> 5)] |= two[ihigh & 31];
ilow += 2; ihigh += qqhigh;
if (ilow >= 60) { ilow -= 60; ihigh += 1; }
}
}
qq += q; q += 1800;
}
squarefree1big(buf,base,q,qq);
}
static void squarefree49big(uint32_t (*buf)[B32],uint64_t base,uint32_t q,uint64_t qq)
{
uint64_t i;
uint32_t pos;
int n;
uint64_t bound = base + 60 * B;
while (qq < bound) {
if (bound < 2000000000)
i = qq - (((uint32_t) base) % ((uint32_t) qq));
else
i = qq - (base % qq);
if (!(i & 1)) i += qq;
if (i < B * 60) {
pos = (uint32_t)i;
n = deltainverse[pos % 60];
if (n >= 0) {
pos /= 60;
(*buf)[n + (pos >> 5)] |= two[pos & 31];
}
}
qq += q; q += 1800;
}
}
static void squarefree49(register uint32_t (*buf)[B32],uint64_t L,uint32_t q)
{
uint32_t qq;
register uint32_t qqhigh;
uint32_t i;
register uint32_t ilow;
register uint32_t ihigh;
register int n;
uint64_t base = 60 * L;
qq = q * q;
q = 60 * q + 900;
while (qq < B * 60) {
if (base < 2000000000)
i = qq - (((uint32_t) base) % qq);
else
i = qq - (base % qq);
if (!(i & 1)) i += qq;
if (i < B * 60) {
qqhigh = qq / 60;
ilow = i % 60; ihigh = i / 60;
qqhigh += qqhigh;
qqhigh += 1;
while (ihigh < B) {
n = deltainverse[ilow];
if (n >= 0)
(*buf)[n + (ihigh >> 5)] |= two[ihigh & 31];
ilow += 38; ihigh += qqhigh;
if (ilow >= 60) { ilow -= 60; ihigh += 1; }
}
}
qq += q; q += 1800;
}
squarefree49big(buf,base,q,qq);
}
/* squares of primes >= 7, < 240 */
uint32_t qqtab[49] = {
49,121,169,289,361,529,841,961,1369,1681
,1849,2209,2809,3481,3721,4489,5041,5329,6241,6889
,7921,9409,10201,10609,11449,11881,12769,16129,17161,18769
,19321,22201,22801,24649,26569,27889,29929,32041,32761,36481
,37249,38809,39601,44521,49729,51529,52441,54289,57121
} ;
/* (qq * 11 + 1) / 60 or (qq * 59 + 1) / 60 */
uint32_t qq60tab[49] = {
9,119,31,53,355,97,827,945,251,1653
,339,405,515,3423,3659,823,4957,977,6137
,1263,7789,1725,10031,1945,2099,11683,2341,2957
,16875,3441,18999,21831,22421,4519,4871,5113,5487
,31507,32215,35873,6829,7115,38941,43779,9117,9447,51567,9953,56169
} ;
static void squarefreetiny(register uint32_t *a,uint32_t *Lmodqq,int d)
{
int j;
for (j = 0;j < 49;++j) {
register uint32_t k;
register uint32_t qq;
qq = qqtab[j];
k = qq - 1 - ((Lmodqq[j] + qq60tab[j] * d - 1) % qq);
while (k < B) {
register uint32_t pos;
register uint32_t data;
register uint32_t bits;
pos = k;
data = k;
pos >>= 5;
data &= 31;
k += qq;
bits = a[pos];
data = two[data];
bits |= data;
a[pos] = bits;
}
}
}
typedef struct { char index; char f; char g; char k; } todo;
static const todo for4[] = {
{0,2,15,4} , {0,3,5,1} , {0,3,25,11} , {0,5,9,3}
, {0,5,21,9} , {0,7,15,7} , {0,8,15,8} , {0,10,9,8}
, {0,10,21,14} , {0,12,5,10} , {0,12,25,20} , {0,13,15,15}
, {0,15,1,15} , {0,15,11,17} , {0,15,19,21} , {0,15,29,29}
, {3,1,3,0} , {3,1,27,12} , {3,4,3,1} , {3,4,27,13}
, {3,6,7,3} , {3,6,13,5} , {3,6,17,7} , {3,6,23,11}
, {3,9,7,6} , {3,9,13,8} , {3,9,17,10} , {3,9,23,14}
, {3,11,3,8} , {3,11,27,20} , {3,14,3,13} , {3,14,27,25}
, {4,2,1,0} , {4,2,11,2} , {4,2,19,6} , {4,2,29,14}
, {4,7,1,3} , {4,7,11,5} , {4,7,19,9} , {4,7,29,17}
, {4,8,1,4} , {4,8,11,6} , {4,8,19,10} , {4,8,29,18}
, {4,13,1,11} , {4,13,11,13} , {4,13,19,17} , {4,13,29,25}
, {7,1,5,0} , {7,1,25,10} , {7,4,5,1} , {7,4,25,11}
, {7,5,7,2} , {7,5,13,4} , {7,5,17,6} , {7,5,23,10}
, {7,10,7,7} , {7,10,13,9} , {7,10,17,11} , {7,10,23,15}
, {7,11,5,8} , {7,11,25,18} , {7,14,5,13} , {7,14,25,23}
, {9,2,9,1} , {9,2,21,7} , {9,3,1,0} , {9,3,11,2}
, {9,3,19,6} , {9,3,29,14} , {9,7,9,4} , {9,7,21,10}
, {9,8,9,5} , {9,8,21,11} , {9,12,1,9} , {9,12,11,11}
, {9,12,19,15} , {9,12,29,23} , {9,13,9,12} , {9,13,21,18}
, {10,2,5,0} , {10,2,25,10} , {10,5,1,1} , {10,5,11,3}
, {10,5,19,7} , {10,5,29,15} , {10,7,5,3} , {10,7,25,13}
, {10,8,5,4} , {10,8,25,14} , {10,10,1,6} , {10,10,11,8}
, {10,10,19,12} , {10,10,29,20} , {10,13,5,11} , {10,13,25,21}
, {13,1,15,3} , {13,4,15,4} , {13,5,3,1} , {13,5,27,13}
, {13,6,5,2} , {13,6,25,12} , {13,9,5,5} , {13,9,25,15}
, {13,10,3,6} , {13,10,27,18} , {13,11,15,11} , {13,14,15,16}
, {13,15,7,15} , {13,15,13,17} , {13,15,17,19} , {13,15,23,23}
, {14,1,7,0} , {14,1,13,2} , {14,1,17,4} , {14,1,23,8}
, {14,4,7,1} , {14,4,13,3} , {14,4,17,5} , {14,4,23,9}
, {14,11,7,8} , {14,11,13,10} , {14,11,17,12} , {14,11,23,16}
, {14,14,7,13} , {14,14,13,15} , {14,14,17,17} , {14,14,23,21}
} ;
static const todo for6[] = {
{1,1,2,0} , {1,1,8,1} , {1,1,22,8} , {1,1,28,13}
, {1,3,10,2} , {1,3,20,7} , {1,7,10,4} , {1,7,20,9}
, {1,9,2,4} , {1,9,8,5} , {1,9,22,12} , {1,9,28,17}
, {5,1,4,0} , {5,1,14,3} , {5,1,16,4} , {5,1,26,11}
, {5,5,2,1} , {5,5,8,2} , {5,5,22,9} , {5,5,28,14}
, {5,9,4,4} , {5,9,14,7} , {5,9,16,8} , {5,9,26,15}
, {8,3,2,0} , {8,3,8,1} , {8,3,22,8} , {8,3,28,13}
, {8,5,4,1} , {8,5,14,4} , {8,5,16,5} , {8,5,26,12}
, {8,7,2,2} , {8,7,8,3} , {8,7,22,10} , {8,7,28,15}
, {11,1,10,1} , {11,1,20,6} , {11,3,4,0} , {11,3,14,3}
, {11,3,16,4} , {11,3,26,11} , {11,7,4,2} , {11,7,14,5}
, {11,7,16,6} , {11,7,26,13} , {11,9,10,5} , {11,9,20,10}
} ;
static const todo for12[] = {
{2,2,1,0} , {2,2,11,-2} , {2,2,19,-6} , {2,2,29,-14}
, {2,3,4,0} , {2,3,14,-3} , {2,3,16,-4} , {2,3,26,-11}
, {2,5,2,1} , {2,5,8,0} , {2,5,22,-7} , {2,5,28,-12}
, {2,7,4,2} , {2,7,14,-1} , {2,7,16,-2} , {2,7,26,-9}
, {2,8,1,3} , {2,8,11,1} , {2,8,19,-3} , {2,8,29,-11}
, {2,10,7,4} , {2,10,13,2} , {2,10,17,0} , {2,10,23,-4}
, {6,1,10,-2} , {6,1,20,-7} , {6,2,7,-1} , {6,2,13,-3}
, {6,2,17,-5} , {6,2,23,-9} , {6,3,2,0} , {6,3,8,-1}
, {6,3,22,-8} , {6,3,28,-13} , {6,4,5,0} , {6,4,25,-10}
, {6,6,5,1} , {6,6,25,-9} , {6,7,2,2} , {6,7,8,1}
, {6,7,22,-6} , {6,7,28,-11} , {6,8,7,2} , {6,8,13,0}
, {6,8,17,-2} , {6,8,23,-6} , {6,9,10,2} , {6,9,20,-3}
, {12,1,4,-1} , {12,1,14,-4} , {12,1,16,-5} , {12,1,26,-12}
, {12,2,5,-1} , {12,2,25,-11} , {12,3,10,-2} , {12,3,20,-7}
, {12,4,1,0} , {12,4,11,-2} , {12,4,19,-6} , {12,4,29,-14}
, {12,6,1,1} , {12,6,11,-1} , {12,6,19,-5} , {12,6,29,-13}
, {12,7,10,0} , {12,7,20,-5} , {12,8,5,2} , {12,8,25,-8}
, {12,9,4,3} , {12,9,14,0} , {12,9,16,-1} , {12,9,26,-8}
, {15,1,2,-1} , {15,1,8,-2} , {15,1,22,-9} , {15,1,28,-14}
, {15,4,7,-1} , {15,4,13,-3} , {15,4,17,-5} , {15,4,23,-9}
, {15,5,4,0} , {15,5,14,-3} , {15,5,16,-4} , {15,5,26,-11}
, {15,6,7,0} , {15,6,13,-2} , {15,6,17,-4} , {15,6,23,-8}
, {15,9,2,3} , {15,9,8,2} , {15,9,22,-5} , {15,9,28,-10}
, {15,10,1,4} , {15,10,11,2} , {15,10,19,-2} , {15,10,29,-10}
} ;
void primegen_sieve(primegen *pg)
{
uint32_t (*buf)[B32];
uint64_t L;
int i;
uint32_t Lmodqq[49];
buf = pg->buf;
L = pg->L;
if (L > 2000000000)
for (i = 0;i < 49;++i)
Lmodqq[i] = L % qqtab[i];
else
for (i = 0;i < 49;++i)
Lmodqq[i] = ((uint32_t) L) % qqtab[i];
clear(buf);
for (i = 0;i < 16;++i)
doit4(buf[0],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[0],Lmodqq,1);
for (;i < 32;++i)
doit4(buf[3],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[3],Lmodqq,13);
for (;i < 48;++i)
doit4(buf[4],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[4],Lmodqq,17);
for (;i < 64;++i)
doit4(buf[7],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[7],Lmodqq,29);
for (;i < 80;++i)
doit4(buf[9],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[9],Lmodqq,37);
for (;i < 96;++i)
doit4(buf[10],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[10],Lmodqq,41);
for (;i < 112;++i)
doit4(buf[13],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[13],Lmodqq,49);
for (;i < 128;++i)
doit4(buf[14],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[14],Lmodqq,53);
for (i = 0;i < 12;++i)
doit6(buf[1],for6[i].f,for6[i].g,(int64_t) for6[i].k - L);
squarefreetiny(buf[1],Lmodqq,7);
for (;i < 24;++i)
doit6(buf[5],for6[i].f,for6[i].g,(int64_t) for6[i].k - L);
squarefreetiny(buf[5],Lmodqq,19);
for (;i < 36;++i)
doit6(buf[8],for6[i].f,for6[i].g,(int64_t) for6[i].k - L);
squarefreetiny(buf[8],Lmodqq,31);
for (;i < 48;++i)
doit6(buf[11],for6[i].f,for6[i].g,(int64_t) for6[i].k - L);
squarefreetiny(buf[11],Lmodqq,43);
for (i = 0;i < 24;++i)
doit12(buf[2],for12[i].f,for12[i].g,(int64_t) for12[i].k - L);
squarefreetiny(buf[2],Lmodqq,11);
for (;i < 48;++i)
doit12(buf[6],for12[i].f,for12[i].g,(int64_t) for12[i].k - L);
squarefreetiny(buf[6],Lmodqq,23);
for (;i < 72;++i)
doit12(buf[12],for12[i].f,for12[i].g,(int64_t) for12[i].k - L);
squarefreetiny(buf[12],Lmodqq,47);
for (;i < 96;++i)
doit12(buf[15],for12[i].f,for12[i].g,(int64_t) for12[i].k - L);
squarefreetiny(buf[15],Lmodqq,59);
squarefree49(buf,L,247);
squarefree49(buf,L,253);
squarefree49(buf,L,257);
squarefree49(buf,L,263);
squarefree1(buf,L,241);
squarefree1(buf,L,251);
squarefree1(buf,L,259);
squarefree1(buf,L,269);
}
void primegen_fill(primegen *pg)
{
int i;
uint32_t mask;
uint32_t bits0, bits1, bits2, bits3, bits4, bits5, bits6, bits7;
uint32_t bits8, bits9, bits10, bits11, bits12, bits13, bits14, bits15;
uint64_t base;
i = pg->pos;
if (i == B32) {
primegen_sieve(pg);
pg->L += B;
i = 0;
}
pg->pos = i + 1;
bits0 = ~pg->buf[0][i];
bits1 = ~pg->buf[1][i];
bits2 = ~pg->buf[2][i];
bits3 = ~pg->buf[3][i];
bits4 = ~pg->buf[4][i];
bits5 = ~pg->buf[5][i];
bits6 = ~pg->buf[6][i];
bits7 = ~pg->buf[7][i];
bits8 = ~pg->buf[8][i];
bits9 = ~pg->buf[9][i];
bits10 = ~pg->buf[10][i];
bits11 = ~pg->buf[11][i];
bits12 = ~pg->buf[12][i];
bits13 = ~pg->buf[13][i];
bits14 = ~pg->buf[14][i];
bits15 = ~pg->buf[15][i];
base = pg->base + 1920;
pg->base = base;
pg->num = 0;
for (mask = 0x80000000;mask;mask >>= 1) {
base -= 60;
if (bits15 & mask) pg->p[pg->num++] = base + 59;
if (bits14 & mask) pg->p[pg->num++] = base + 53;
if (bits13 & mask) pg->p[pg->num++] = base + 49;
if (bits12 & mask) pg->p[pg->num++] = base + 47;
if (bits11 & mask) pg->p[pg->num++] = base + 43;
if (bits10 & mask) pg->p[pg->num++] = base + 41;
if (bits9 & mask) pg->p[pg->num++] = base + 37;
if (bits8 & mask) pg->p[pg->num++] = base + 31;
if (bits7 & mask) pg->p[pg->num++] = base + 29;
if (bits6 & mask) pg->p[pg->num++] = base + 23;
if (bits5 & mask) pg->p[pg->num++] = base + 19;
if (bits4 & mask) pg->p[pg->num++] = base + 17;
if (bits3 & mask) pg->p[pg->num++] = base + 13;
if (bits2 & mask) pg->p[pg->num++] = base + 11;
if (bits1 & mask) pg->p[pg->num++] = base + 7;
if (bits0 & mask) pg->p[pg->num++] = base + 1;
}
}
uint64_t primegen_next(primegen *pg)
{
while (!pg->num)
primegen_fill(pg);
return pg->p[--pg->num];
}
uint64_t primegen_peek(primegen *pg)
{
while (!pg->num)
primegen_fill(pg);
return pg->p[pg->num - 1];
}
void primegen_init(primegen *pg)
{
pg->L = 1;
pg->base = 60;
pg->pos = PRIMEGEN_WORDS;
pg->p[0] = 59;
pg->p[1] = 53;
pg->p[2] = 47;
pg->p[3] = 43;
pg->p[4] = 41;
pg->p[5] = 37;
pg->p[6] = 31;
pg->p[7] = 29;
pg->p[8] = 23;
pg->p[9] = 19;
pg->p[10] = 17;
pg->p[11] = 13;
pg->p[12] = 11;
pg->p[13] = 7;
pg->p[14] = 5;
pg->p[15] = 3;
pg->p[16] = 2;
pg->num = 17;
}
static const unsigned long pop[256] = {
0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5
,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6
,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6
,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7
,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6
,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7
,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7
,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
};
uint64_t primegen_count(primegen *pg,uint64_t to)
{
uint64_t count = 0;
for (;;) {
register int pos;
register int j;
register uint32_t bits;
register uint32_t smallcount;
while (pg->num) {
if (pg->p[pg->num - 1] >= to) return count;
++count;
--pg->num;
}
smallcount = 0;
pos = pg->pos;
while ((pos < B32) && (pg->base + 1920 < to)) {
for (j = 0;j < 16;++j) {
bits = ~pg->buf[j][pos];
smallcount += pop[bits & 255]; bits >>= 8;
smallcount += pop[bits & 255]; bits >>= 8;
smallcount += pop[bits & 255]; bits >>= 8;
smallcount += pop[bits & 255];
}
pg->base += 1920;
++pos;
}
pg->pos = pos;
count += smallcount;
if (pos == B32)
while (pg->base + B * 60 < to) {
primegen_sieve(pg);
pg->L += B;
smallcount = 0;
for (j = 0;j < 16;++j)
for (pos = 0;pos < B32;++pos) {
bits = ~pg->buf[j][pos];
smallcount += pop[bits & 255]; bits >>= 8;
smallcount += pop[bits & 255]; bits >>= 8;
smallcount += pop[bits & 255]; bits >>= 8;
smallcount += pop[bits & 255];
}
count += smallcount;
pg->base += B * 60;
}
primegen_fill(pg);
}
}
void primegen_skipto(primegen *pg,uint64_t to)
{
for (;;) {
int pos;
while (pg->num) {
if (pg->p[pg->num - 1] >= to) return;
--pg->num;
}
pos = pg->pos;
while ((pos < B32) && (pg->base + 1920 < to)) {
pg->base += 1920;
++pos;
}
pg->pos = pos;
if (pos == B32)
while (pg->base + B * 60 < to) {
pg->L += B;
pg->base += B * 60;
}
primegen_fill(pg);
}
}
================================================
FILE: src/crypto-primegen.h
================================================
#ifndef PRIMEGEN_H
#define PRIMEGEN_H
#include
/**
* This is B/32: the number of 32-bit words of space used in the primegen
* inner loop. This should fit into the CPU's level-1 cache.
*
* 2048 works well on a Pentium-100.
* 3600 works well on a Pentium II-350
* 4004 works well on an UltraSPARC-I/167
*
* 2012-nov (Rob): This code was written 15 years ago. Processor caches
* haven't really gotten any larger. A number like 8008 works slightly
* better on an Ivy Bridge CPU, but works noticeably worse on an Atom
* or ARM processor. The value 4004 seems to be a good compromise for
* all these processors. In any case, modern CPUs will automatically
* prefetch the buffers anyway, significantly lessoning the impact of
* having a poor number defined here. I tried 16016, but it crashed, and
* I don't know why, but I don't care because I'm not going to use such a
* large size.
*/
#define PRIMEGEN_WORDS 4004
typedef struct {
uint32_t buf[16][PRIMEGEN_WORDS];
uint64_t p[512]; /* p[num-1] ... p[0], in that order */
int num;
int pos; /* next entry to use in buf; WORDS to restart */
uint64_t base;
uint64_t L;
} primegen;
extern void primegen_sieve(primegen *);
extern void primegen_fill(primegen *);
extern void primegen_init(primegen *);
extern uint64_t primegen_next(primegen *);
extern uint64_t primegen_peek(primegen *);
extern uint64_t primegen_count(primegen *,uint64_t to);
extern void primegen_skipto(primegen *,uint64_t to);
#endif
================================================
FILE: src/crypto-siphash24.c
================================================
/*
SipHash reference C implementation
Written in 2012 by
Jean-Philippe Aumasson
Daniel J. Bernstein
To the extent possible under law, the author(s) have dedicated all copyright
and related and neighboring rights to this software to the public domain
worldwide. This software is distributed without any warranty.
You should have received a copy of the CC0 Public Domain Dedication along with
this software. If not, see .
*/
#include
#include
#include
#include "crypto-siphash24.h"
typedef uint64_t u64;
typedef uint32_t u32;
typedef uint8_t u8;
#define ROTL(x,b) (u64)( ((x) << (b)) | ( (x) >> (64 - (b))) )
#define U32TO8_LE(p, v) \
(p)[0] = (u8)((v) ); (p)[1] = (u8)((v) >> 8); \
(p)[2] = (u8)((v) >> 16); (p)[3] = (u8)((v) >> 24);
#define U64TO8_LE(p, v) \
U32TO8_LE((p), (u32)((v) )); \
U32TO8_LE((p) + 4, (u32)((v) >> 32));
#define U8TO64_LE(p) \
(((u64)((p)[0]) ) | \
((u64)((p)[1]) << 8) | \
((u64)((p)[2]) << 16) | \
((u64)((p)[3]) << 24) | \
((u64)((p)[4]) << 32) | \
((u64)((p)[5]) << 40) | \
((u64)((p)[6]) << 48) | \
((u64)((p)[7]) << 56))
#define SIPROUND \
{ \
v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \
v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \
v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
}
/* SipHash-2-4 */
static int crypto_auth( unsigned char *out, const unsigned char *in, unsigned long long inlen, const unsigned char *k )
{
/* "somepseudorandomlygeneratedbytes" */
u64 v0 = 0x736f6d6570736575ULL;
u64 v1 = 0x646f72616e646f6dULL;
u64 v2 = 0x6c7967656e657261ULL;
u64 v3 = 0x7465646279746573ULL;
u64 b;
u64 k0 = U8TO64_LE( k );
u64 k1 = U8TO64_LE( k + 8 );
const u8 *end = in + inlen - ( inlen % sizeof( u64 ) );
const int left = inlen & 7;
b = ( ( u64 )inlen ) << 56;
v3 ^= k1;
v2 ^= k0;
v1 ^= k1;
v0 ^= k0;
for ( ; in != end; in += 8 )
{
u64 m;
m = U8TO64_LE( in );
#ifdef xxxDEBUG
printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
printf( "(%3d) compress %08x %08x\n", ( int )inlen, ( u32 )( m >> 32 ), ( u32 )m );
#endif
v3 ^= m;
SIPROUND;
SIPROUND;
v0 ^= m;
}
switch( left )
{
case 7: b |= ( ( u64 )in[ 6] ) << 48;
case 6: b |= ( ( u64 )in[ 5] ) << 40;
case 5: b |= ( ( u64 )in[ 4] ) << 32;
case 4: b |= ( ( u64 )in[ 3] ) << 24;
case 3: b |= ( ( u64 )in[ 2] ) << 16;
case 2: b |= ( ( u64 )in[ 1] ) << 8;
case 1: b |= ( ( u64 )in[ 0] ); break;
case 0: break;
}
#ifdef xxxDEBUG
printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
printf( "(%3d) padding %08x %08x\n", ( int )inlen, ( u32 )( b >> 32 ), ( u32 )b );
#endif
v3 ^= b;
SIPROUND;
SIPROUND;
v0 ^= b;
#ifdef xxxDEBUG
printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
#endif
v2 ^= 0xff;
SIPROUND;
SIPROUND;
SIPROUND;
SIPROUND;
b = v0 ^ v1 ^ v2 ^ v3;
U64TO8_LE( out, b );
return 0;
}
uint64_t
siphash24(const void *in, size_t inlen, const uint64_t key[2])
{
uint64_t result;
crypto_auth((unsigned char*)&result,
(const unsigned char *)in,
inlen,
(const unsigned char *)&key[0]);
return result;
}
/*
SipHash-2-4 output with
k = 00 01 02 ...
and
in = (empty string)
in = 00 (1 byte)
in = 00 01 (2 bytes)
in = 00 01 02 (3 bytes)
...
in = 00 01 02 ... 3e (63 bytes)
*/
static u8 vectors[64][8] =
{
{ 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, },
{ 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, },
{ 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, },
{ 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, },
{ 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, },
{ 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, },
{ 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, },
{ 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, },
{ 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, },
{ 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, },
{ 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, },
{ 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, },
{ 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, },
{ 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, },
{ 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, },
{ 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, },
{ 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, },
{ 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, },
{ 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, },
{ 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, },
{ 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, },
{ 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, },
{ 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, },
{ 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, },
{ 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, },
{ 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, },
{ 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, },
{ 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, },
{ 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, },
{ 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, },
{ 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, },
{ 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, },
{ 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, },
{ 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, },
{ 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, },
{ 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, },
{ 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, },
{ 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, },
{ 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, },
{ 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, },
{ 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, },
{ 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, },
{ 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, },
{ 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, },
{ 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, },
{ 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, },
{ 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, },
{ 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, },
{ 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, },
{ 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, },
{ 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, },
{ 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, },
{ 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, },
{ 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, },
{ 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, },
{ 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, },
{ 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, },
{ 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, },
{ 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, },
{ 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, },
{ 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, },
{ 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, },
{ 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, },
{ 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, }
};
static int
test_vectors()
{
#define MAXLEN 64
u8 in[MAXLEN], out[8], k[16];
int i;
int ok = 1;
for( i = 0; i < 16; ++i ) k[i] = (u8)i;
for( i = 0; i < MAXLEN; ++i )
{
in[i] = (u8)i;
crypto_auth( out, in, i, k );
if ( memcmp( out, vectors[i], 8 ) )
{
printf( "test vector failed for %d bytes\n", i );
ok = 0;
}
}
return ok;
}
int
siphash24_selftest(void)
{
if (test_vectors())
return 0;
else
return 1;
}
================================================
FILE: src/crypto-siphash24.h
================================================
#ifndef CRYPTO_SIPHASH24_H
#define CRYPTO_SIPHASH24_H
#include
uint64_t
siphash24(const void *in, size_t inlen, const uint64_t key[2]);
/**
* Regression-test this module.
* @return
* 0 on success, a positive integer otherwise.
*/
int
siphash24_selftest(void);
#endif
================================================
FILE: src/event-timeout.c
================================================
/*
Event timeout
This is for the user-mode TCP stack. We need to mark timeouts in the
future when we'll re-visit a connection/tcb. For example, when we
send a packet, we need to resend it in the future in case we don't
get a response.
This design creates a large "ring" of timeouts, and then cycles
again and again through the ring. This is a fairly high granularity,
just has hundreds, thousands, or 10 thousand entries per second.
(I keep adjusting the granularity up and down). Not that at any
slot in the ring, there may be entries from the far future.
NOTE: a big feature of this system is that the structure that tracks
the timeout is actually held within the TCB structure. In other
words, each TCB can have one-and-only-one timeout.
NOTE: a recurring bug is that the TCP code removes a TCB from the
timeout ring and forgets to put it back somewhere else. Since the
TCB is cleaned up on a timeout, such TCBs never get cleaned up,
leading to a memory leak. I keep fixing this bug, then changing the
code and causing the bug to come back again.
*/
#include "event-timeout.h"
#include "util-logger.h"
#include "util-malloc.h"
#include
#include
#include
#include
/***************************************************************************
* The timeout system is a circular ring. We move an index around the
* ring. At each slot in the ring is a linked-list of all entries at
* that time index. Because the ring can wrap, not everything at a given
* entry will be the same timestamp. Therefore, when doing the timeout
* logic at a slot, we have to doublecheck the actual timestamp, and skip
* those things that are further in the future.
***************************************************************************/
struct Timeouts {
/**
* This index is a monotonically increasing number, modulus the mask.
* Every time we check timeouts, we simply move it forward in time.
*/
uint64_t current_index;
/**
* Counts the number of outstanding timeouts. Adding a timeout increments
* this number, and removing a timeout decrements this number. The
* program shouldn't exit until this number is zero.
*/
uint64_t outstanding_count;
/**
* The number of slots is a power-of-2, so the mask is just this
* number minus 1
*/
unsigned mask;
/**
* The ring of entries.
*/
struct TimeoutEntry *slots[1024*1024];
};
/***************************************************************************
***************************************************************************/
struct Timeouts *
timeouts_create(uint64_t timestamp)
{
struct Timeouts *timeouts;
/*
* Allocate memory and initialize it to zero
*/
timeouts = CALLOC(1, sizeof(*timeouts));
/*
* We just mask off the low order bits to determine wrap. I'm using
* a variable here because one of these days I'm going to make
* the size of the ring dynamically adjustable depending upon
* the speed of the scan.
*/
timeouts->mask = sizeof(timeouts->slots)/sizeof(timeouts->slots[0]) - 1;
/*
* Set the index to the current time. Note that this timestamp is
* the 'time_t' value multiplied by the number of ticks-per-second,
* where 'ticks' is something I've defined for scanning. Right now
* I hard-code in the size of the ticks, but eventually they'll be
* dynamically resized depending upon the speed of the scan.
*/
timeouts->current_index = timestamp;
return timeouts;
}
/***************************************************************************
* This inserts the timeout entry into the appropriate place in the
* timeout ring.
***************************************************************************/
void
timeouts_add(struct Timeouts *timeouts, struct TimeoutEntry *entry,
size_t offset, uint64_t timestamp)
{
unsigned index;
/* Unlink from wherever the entry came from */
if (entry->timestamp)
timeouts->outstanding_count--;
timeout_unlink(entry);
if (entry->prev) {
LOG(1, "***CHANGE %d-seconds\n",
(int)((timestamp-entry->timestamp)/TICKS_PER_SECOND));
}
/* Initialize the new entry */
entry->timestamp = timestamp;
entry->offset = (unsigned)offset;
/* Link it into it's new location */
index = timestamp & timeouts->mask;
entry->next = timeouts->slots[index];
timeouts->slots[index] = entry;
entry->prev = &timeouts->slots[index];
if (entry->next)
entry->next->prev = &entry->next;
timeouts->outstanding_count++;
}
/***************************************************************************
* Remove the next event that it older than the specified timestamp
***************************************************************************/
void *
timeouts_remove(struct Timeouts *timeouts, uint64_t timestamp)
{
struct TimeoutEntry *entry = NULL;
/* Search until we find one */
while (timeouts->current_index <= timestamp) {
/* Start at the current slot */
entry = timeouts->slots[timeouts->current_index & timeouts->mask];
/* enumerate through the linked list until we find a used slot */
while (entry && entry->timestamp > timestamp)
entry = entry->next;
if (entry)
break;
/* found nothing at this slot, so move to next slot */
timeouts->current_index++;
}
if (entry == NULL) {
/* we've caught up to the current time, and there's nothing
* left to timeout, so return NULL */
return NULL;
}
/* unlink this entry from the timeout system */
timeouts--;
timeout_unlink(entry);
/* return a pointer to the structure holding this entry */
return ((char*)entry) - entry->offset;
}
================================================
FILE: src/event-timeout.h
================================================
#ifndef EVENT_TIMEOUT_H
#define EVENT_TIMEOUT_H
#include
#include
#include /* offsetof*/
#include "util-bool.h" /* */
#if defined(_MSC_VER)
#undef inline
#define inline _inline
#endif
struct Timeouts;
/***************************************************************************
***************************************************************************/
struct TimeoutEntry {
/**
* In units of 1/16384 of a second. We use power-of-two units here
* to make the "modulus" operation a simple binary "and".
* See the TICKS_FROM_TV() macro for getting the timestamp from
* the current time.
*/
uint64_t timestamp;
/** we build a doubly-linked list */
struct TimeoutEntry *next;
struct TimeoutEntry **prev;
/** The timeout entry is never allocated by itself, but instead
* lives inside another data structure. This stores the value of
* 'offsetof()', so given a pointer to this structure, we can find
* the original structure that contains it */
unsigned offset;
};
/***************************************************************************
***************************************************************************/
static inline bool
timeout_is_unlinked(const struct TimeoutEntry *entry) {
if (entry->prev == 0 || entry->next == 0)
return true;
else
return false;
}
/***************************************************************************
***************************************************************************/
static inline void
timeout_unlink(struct TimeoutEntry *entry)
{
if (entry->prev == 0 && entry->next == 0)
return;
*(entry->prev) = entry->next;
if (entry->next)
entry->next->prev = entry->prev;
entry->next = 0;
entry->prev = 0;
entry->timestamp = 0;
}
/***************************************************************************
***************************************************************************/
static inline void
timeout_init(struct TimeoutEntry *entry)
{
entry->next = 0;
entry->prev = 0;
}
/**
* Create a timeout subsystem.
* @param timestamp_now
* The current timestamp indicating "now" when the thing starts.
* This should be 'time(0) * TICKS_PER_SECOND'.
*/
struct Timeouts *
timeouts_create(uint64_t timestamp_now);
/**
* Insert the timeout 'entry' into the future location in the timeout
* ring, as determined by the timestamp.
* @param timeouts
* A ring of timeouts, with each slot corresponding to a specific
* time in the future.
* @param entry
* The entry that we are going to insert into the ring. If it's
* already in the ring, it'll be removed from the old location
* first before inserting into the new location.
* @param offset
* The 'entry' field above is part of an existing structure. This
* tells the offset_of() from the beginning of that structure.
* In other words, this tells us the pointer to the object that
* that is the subject of the timeout.
* @param timestamp_expires
* When this timeout will expire. This is in terms of internal
* ticks, which in units of TICKS_PER_SECOND.
*/
void
timeouts_add(struct Timeouts *timeouts, struct TimeoutEntry *entry,
size_t offset, uint64_t timestamp_expires);
/**
* Remove an object from the timestamp system that is older than than
* the specified timestamp. This function must be called repeatedly
* until it returns NULL to remove all the objects that are older
* than the given timestamp.
* @param timeouts
* A ring of timeouts. We'll walk the ring until we've caught
* up with the current time.
* @param timestamp_now
* Usually, this timestmap will be "now", the current time,
* and anything older than this will be aged out.
* @return
* an object older than the specified timestamp, or NULL
* if there are no more objects to be found
*/
void *
timeouts_remove(struct Timeouts *timeouts, uint64_t timestamp_now);
/*
* This macros convert a normal "timeval" structure into the timestamp
* that we use for timeouts. The timeval structure probably will come
* from the packets that we are capturing.
*/
#define TICKS_PER_SECOND (16384ULL)
#define TICKS_FROM_SECS(secs) ((secs)*16384ULL)
#define TICKS_FROM_USECS(usecs) ((usecs)/16384ULL)
#define TICKS_FROM_TV(secs,usecs) (TICKS_FROM_SECS(secs)+TICKS_FROM_USECS(usecs))
#endif
================================================
FILE: src/in-binary.c
================================================
/*
Read in the binary file produced by "out-binary.c". This allows you to
translate the "binary" format into any of the other output formats.
*/
#include "massip-addr.h"
#include "in-binary.h"
#include "masscan.h"
#include "masscan-app.h"
#include "masscan-status.h"
#include "main-globals.h"
#include "output.h"
#include "util-safefunc.h"
#include "in-filter.h"
#include "in-report.h"
#include "util-malloc.h"
#include "util-logger.h"
#include
#include
#ifdef _MSC_VER
#pragma warning(disable:4996)
#endif
static const size_t BUF_MAX = 1024*1024;
struct MasscanRecord {
unsigned timestamp;
ipaddress ip;
unsigned char ip_proto;
unsigned short port;
unsigned char reason;
unsigned char ttl;
unsigned char mac[6];
enum ApplicationProtocol app_proto;
};
/***************************************************************************
***************************************************************************/
static void
parse_status(struct Output *out,
enum PortStatus status, /* open/closed */
const unsigned char *buf, size_t buf_length)
{
struct MasscanRecord record;
if (buf_length < 12)
return;
/* parse record */
record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3];
record.ip.ipv4 = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7];
record.ip.version = 4;
record.port = buf[8]<<8 | buf[9];
record.reason = buf[10];
record.ttl = buf[11];
/* if ARP, then there will be a MAC address */
if (record.ip.ipv4 == 0 && buf_length >= 12+6)
memcpy(record.mac, buf+12, 6);
else
memset(record.mac, 0, 6);
if (out->when_scan_started == 0)
out->when_scan_started = record.timestamp;
switch (record.port) {
case 53:
case 123:
case 137:
case 161:
record.ip_proto = 17;
break;
case 36422:
case 36412:
case 2905:
record.ip_proto = 132;
break;
default:
record.ip_proto = 6;
break;
}
/*
* Now report the result
*/
output_report_status(out,
record.timestamp,
status,
record.ip,
record.ip_proto,
record.port,
record.reason,
record.ttl,
record.mac);
}
/***************************************************************************
***************************************************************************/
static void
parse_status2(struct Output *out,
enum PortStatus status, /* open/closed */
const unsigned char *buf, size_t buf_length,
struct MassIP *filter)
{
struct MasscanRecord record;
if (buf_length < 13)
return;
/* parse record */
record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3];
record.ip.ipv4 = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7];
record.ip.version = 4;
record.ip_proto = buf[8];
record.port = buf[9]<<8 | buf[10];
record.reason = buf[11];
record.ttl = buf[12];
/* if ARP, then there will be a MAC address */
if (record.ip.ipv4 == 0 && buf_length >= 13+6)
memcpy(record.mac, buf+13, 6);
else
memset(record.mac, 0, 6);
if (out->when_scan_started == 0)
out->when_scan_started = record.timestamp;
/* Filter for known IP/ports, if specified on command-line */
if (filter && filter->count_ipv4s) {
if (!massip_has_ip(filter, record.ip))
return;
}
if (filter && filter->count_ports) {
if (!massip_has_port(filter, record.port))
return;
}
/*
* Now report the result
*/
output_report_status(out,
record.timestamp,
status,
record.ip,
record.ip_proto,
record.port,
record.reason,
record.ttl,
record.mac);
}
static unsigned char
_get_byte(const unsigned char *buf, size_t length, size_t *offset)
{
unsigned char result;
if (*offset < length) {
result = buf[*offset];
} else {
result = 0xFF;
}
(*offset)++;
return result;
}
static unsigned
_get_integer(const unsigned char *buf, size_t length, size_t *r_offset)
{
unsigned result;
size_t offset = *r_offset;
(*r_offset) += 4;
if (offset + 4 <= length) {
result = buf[offset+0]<<24
| buf[offset+1]<<16
| buf[offset+2]<<8
| buf[offset+3]<<0;
} else {
result = 0xFFFFFFFF;
}
return result;
}
static unsigned short
_get_short(const unsigned char *buf, size_t length, size_t *r_offset)
{
unsigned short result;
size_t offset = *r_offset;
(*r_offset) += 2;
if (offset + 2 <= length) {
result = buf[offset+0]<<8
| buf[offset+1]<<0;
} else {
result = 0xFFFF;
}
return result;
}
static unsigned long long
_get_long(const unsigned char *buf, size_t length, size_t *r_offset)
{
unsigned long long result;
size_t offset = *r_offset;
(*r_offset) += 8;
if (offset + 8 <= length) {
result =
(unsigned long long)buf[offset+0]<<56ULL
| (unsigned long long)buf[offset+1]<<48ULL
| (unsigned long long)buf[offset+2]<<40ULL
| (unsigned long long)buf[offset+3]<<32ULL
| (unsigned long long)buf[offset+4]<<24ULL
| (unsigned long long)buf[offset+5]<<16ULL
| (unsigned long long)buf[offset+6]<<8ULL
| (unsigned long long)buf[offset+7]<<0ULL;
} else {
result = 0xFFFFFFFFffffffffULL;
}
return result;
}
/***************************************************************************
***************************************************************************/
static void
parse_status6(struct Output *out,
enum PortStatus status, /* open/closed */
const unsigned char *buf, size_t length,
struct MassIP *filter)
{
struct MasscanRecord record;
size_t offset = 0;
/* parse record */
record.timestamp = _get_integer(buf, length, &offset);
record.ip_proto = _get_byte(buf, length, &offset);
record.port = _get_short(buf, length, &offset);
record.reason = _get_byte(buf, length, &offset);
record.ttl = _get_byte(buf, length, &offset);
record.ip.version= _get_byte(buf, length, &offset);
if (record.ip.version != 6) {
fprintf(stderr, "[-] corrupt record\n");
return;
}
record.ip.ipv6.hi = _get_long(buf, length, &offset);
record.ip.ipv6.lo = _get_long(buf, length, &offset);
if (out->when_scan_started == 0)
out->when_scan_started = record.timestamp;
/* Filter for known IP/ports, if specified on command-line */
if (filter && filter->count_ipv4s) {
if (!massip_has_ip(filter, record.ip))
return;
}
if (filter && filter->count_ports) {
if (!massip_has_port(filter, record.port))
return;
}
/*
* Now report the result
*/
output_report_status(out,
record.timestamp,
status,
record.ip,
record.ip_proto,
record.port,
record.reason,
record.ttl,
record.mac);
}
/***************************************************************************
***************************************************************************/
static void
parse_banner6(struct Output *out, unsigned char *buf, size_t length,
const struct MassIP *filter,
const struct RangeList *btypes)
{
struct MasscanRecord record;
size_t offset = 0;
/*
* Parse the parts that are common to most records
*/
record.timestamp = _get_integer(buf, length, &offset);
record.ip_proto = _get_byte(buf, length, &offset);
record.port = _get_short(buf, length, &offset);
record.app_proto = _get_short(buf, length, &offset);
record.ttl = _get_byte(buf, length, &offset);
record.ip.version= _get_byte(buf, length, &offset);
if (record.ip.version != 6) {
fprintf(stderr, "[-] corrupt record\n");
return;
}
record.ip.ipv6.hi = _get_long(buf, length, &offset);
record.ip.ipv6.lo = _get_long(buf, length, &offset);
if (out->when_scan_started == 0)
out->when_scan_started = record.timestamp;
/*
* Filter out records if requested
*/
if (!readscan_filter_pass(record.ip, record.port, record.app_proto,
filter, btypes))
return;
/*
* Now print the output
*/
if (offset > length)
return;
output_report_banner(
out,
record.timestamp,
record.ip,
record.ip_proto, /* TCP=6, UDP=17 */
record.port,
record.app_proto, /* HTTP, SSL, SNMP, etc. */
record.ttl, /* ttl */
buf+offset, (unsigned)(length-offset)
);
}
/***************************************************************************
* [OBSOLETE]
* This parses an old version of the banner record. I've still got files
* hanging around with this version, so I'm keeping it in the code for
* now, but eventually I'll get rid of it.
***************************************************************************/
static void
parse_banner3(struct Output *out, unsigned char *buf, size_t buf_length)
{
struct MasscanRecord record;
/*
* Parse the parts that are common to most records
*/
record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3];
record.ip.ipv4 = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7];
record.ip.version = 4;
record.port = buf[8]<<8 | buf[9];
record.app_proto = buf[10]<<8 | buf[11];
if (out->when_scan_started == 0)
out->when_scan_started = record.timestamp;
/*
* Now print the output
*/
output_report_banner(
out,
record.timestamp,
record.ip,
6, /* this is always TCP */
record.port,
record.app_proto,
0, /* ttl */
buf+12, (unsigned)buf_length-12
);
}
/***************************************************************************
* Parse the BANNER record, extracting the timestamp, IP address, and port
* number. We also convert the banner string into a safer form.
***************************************************************************/
static void
parse_banner4(struct Output *out, unsigned char *buf, size_t buf_length)
{
struct MasscanRecord record;
if (buf_length < 13)
return;
/*
* Parse the parts that are common to most records
*/
record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3];
record.ip.ipv4 = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7];
record.ip.version = 4;
record.ip_proto = buf[8];
record.port = buf[9]<<8 | buf[10];
record.app_proto = buf[11]<<8 | buf[12];
if (out->when_scan_started == 0)
out->when_scan_started = record.timestamp;
/*
* Now print the output
*/
output_report_banner(
out,
record.timestamp,
record.ip,
record.ip_proto, /* TCP=6, UDP=17 */
record.port,
record.app_proto, /* HTTP, SSL, SNMP, etc. */
0, /* ttl */
buf+13, (unsigned)buf_length-13
);
}
/***************************************************************************
***************************************************************************/
static void
parse_banner9(struct Output *out, unsigned char *buf, size_t buf_length,
const struct MassIP *filter,
const struct RangeList *btypes)
{
struct MasscanRecord record;
unsigned char *data = buf+14;
size_t data_length = buf_length-14;
if (buf_length < 14)
return;
/*
* Parse the parts that are common to most records
*/
record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3];
record.ip.ipv4 = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7];
record.ip.version = 4;
record.ip_proto = buf[8];
record.port = buf[9]<<8 | buf[10];
record.app_proto = buf[11]<<8 | buf[12];
record.ttl = buf[13];
if (out->when_scan_started == 0)
out->when_scan_started = record.timestamp;
/*
* KLUDGE: when doing SSL stuff, add a IP:name pair to a database
* so we can annotate [VULN] strings with this information
*/
//readscan_report(record.ip, record.app_proto, &data, &data_length);
/*
* Filter out records if requested
*/
if (!readscan_filter_pass(record.ip, record.port, record.app_proto,
filter, btypes))
return;
/*
* Now print the output
*/
output_report_banner(
out,
record.timestamp,
record.ip,
record.ip_proto, /* TCP=6, UDP=17 */
record.port,
record.app_proto, /* HTTP, SSL, SNMP, etc. */
record.ttl, /* ttl */
data, (unsigned)data_length
);
}
/***************************************************************************
* Read in the file, one record at a time.
***************************************************************************/
static uint64_t
_binaryfile_parse(struct Output *out, const char *filename,
struct MassIP *filter,
const struct RangeList *btypes)
{
FILE *fp = 0;
unsigned char *buf = 0;
size_t bytes_read;
uint64_t total_records = 0;
/* Allocate a buffer of up to one megabyte per record */
buf = MALLOC(BUF_MAX);
/* Open the file */
fp = fopen(filename, "rb");
if (fp == NULL) {
fprintf(stderr, "[-] FAIL: --readscan\n");
fprintf(stderr, "[-] %s: %s\n", filename, strerror(errno));
goto end;
}
LOG(0, "[+] --readscan %s\n", filename);
if (feof(fp)) {
LOG(0, "[-] %s: file is empty\n", filename);
goto end;
}
/* first record is pseudo-record */
bytes_read = fread(buf, 1, 'a'+2, fp);
if (bytes_read < 'a'+2) {
LOG(0, "[-] %s: %s\n", filename, strerror(errno));
goto end;
}
/* Make sure it's got the format string */
if (memcmp(buf, "masscan/1.1", 11) != 0) {
LOG(0,
"[-] %s: unknown file format (expeced \"masscan/1.1\")\n",
filename);
goto end;
}
/*
* Look for start time
*/
if (buf[11] == '.' && strtoul((char*)buf+12,0,0) >= 2) {
unsigned i;
/* move to next field */
for (i=0; i<'a' && buf[i] && buf[i] != '\n'; i++)
;
i++;
if (buf[i] == 's')
i++;
if (buf[i] == ':')
i++;
/* extract timestamp */
if (i < 'a')
out->when_scan_started = strtoul((char*)buf+i,0,0);
}
/* Now read all records */
for (;;) {
unsigned type;
unsigned length;
/* [TYPE]
* This is one or more bytes indicating the type of type of the
* record
*/
bytes_read = fread(buf, 1, 1, fp);
if (bytes_read != 1)
break;
type = buf[0] & 0x7F;
while (buf[0] & 0x80) {
bytes_read = fread(buf, 1, 1, fp);
if (bytes_read != 1)
break;
type = (type << 7) | (buf[0] & 0x7F);
}
/* [LENGTH]
* Is one byte for lengths smaller than 127 bytes, or two
* bytes for lengths up to 16384.
*/
bytes_read = fread(buf, 1, 1, fp);
if (bytes_read != 1)
break;
length = buf[0] & 0x7F;
while (buf[0] & 0x80) {
bytes_read = fread(buf, 1, 1, fp);
if (bytes_read != 1)
break;
length = (length << 7) | (buf[0] & 0x7F);
}
if (length > BUF_MAX) {
LOG(0, "[-] file corrupt\n");
goto end;
}
/* get the remainder of the record */
bytes_read = fread(buf, 1, length, fp);
if (bytes_read < length)
break; /* eof */
/* Depending on record type, do something different */
switch (type) {
case 1: /* STATUS: open */
if (!btypes->count)
parse_status(out, PortStatus_Open, buf, bytes_read);
break;
case 2: /* STATUS: closed */
if (!btypes->count)
parse_status(out, PortStatus_Closed, buf, bytes_read);
break;
case 3: /* BANNER */
parse_banner3(out, buf, bytes_read);
break;
case 4:
if (fread(buf+bytes_read,1,1,fp) != 1) {
LOG(0, "[-] read() error\n");
exit(1);
}
bytes_read++;
parse_banner4(out, buf, bytes_read);
break;
case 5:
parse_banner4(out, buf, bytes_read);
break;
case 6: /* STATUS: open */
if (!btypes->count)
parse_status2(out, PortStatus_Open, buf, bytes_read, filter);
break;
case 7: /* STATUS: closed */
if (!btypes->count)
parse_status2(out, PortStatus_Closed, buf, bytes_read, filter);
break;
case 9:
parse_banner9(out, buf, bytes_read, filter, btypes);
break;
case 10: /* Open6 */
if (!btypes->count)
parse_status6(out, PortStatus_Open, buf, bytes_read, filter);
break;
case 11: /* Closed6 */
if (!btypes->count)
parse_status6(out, PortStatus_Closed, buf, bytes_read, filter);
break;
case 13: /* Banner6 */
parse_banner6(out, buf, bytes_read, filter, btypes);
break;
case 'm': /* FILEHEADER */
//goto end;
break;
default:
LOG(0, "[-] file corrupt: unknown type %u\n", type);
goto end;
}
total_records++;
if ((total_records & 0xFFFF) == 0)
LOG(0, "[+] %s: %8" PRIu64 "\r", filename, total_records);
}
end:
if (buf)
free(buf);
if (fp)
fclose(fp);
return total_records;
}
/*****************************************************************************
* When masscan is called with the "--readscan" parameter, it doesn't
* do a scan of the live network, but instead reads scan results from
* a file. Those scan results can then be written out in any of the
* other formats. This preserves the original timestamps.
*****************************************************************************/
void
readscan_binary_scanfile(struct Masscan *masscan,
int arg_first, int arg_max, char *argv[])
{
struct Output *out;
int i;
/*
* Create the output system, such as XML or JSON output
*/
out = output_create(masscan, 0);
/*
* Set the start time to zero. We'll read it from the first file
* that we parse
*/
out->when_scan_started = 0;
/*
* We don't parse the entire argument list, just a subrange
* containing the list of files. The 'arg_first' parameter
* points to the first filename after the '--readscan'
* parameter, and 'arg_max' is the parameter after
* the last filename. For example, consider an argument list that
* looks like:
* masscan --foo --readscan file1.scan file2.scan --bar
* Then arg_first=3 and arg_max=5.
*/
for (i=arg_first; itargets, &masscan->banner_types);
}
/* Done! */
output_destroy(out);
}
================================================
FILE: src/in-binary.h
================================================
#ifndef IN_BINARY_H
#define IN_BINARY_H
struct Masscan;
/**
* Read that output of previous scans that were saved in the binary format
* (i.e. using the -oB parameter or the '--output-format binary' parameter).
* The intent is that the user can then re-output in another format like
* JSON or XML.
*/
void
readscan_binary_scanfile(struct Masscan *masscan,
int arg_first, int arg_max, char *argv[]);
#endif
================================================
FILE: src/in-filter.c
================================================
#include "in-filter.h"
#include "massip.h"
int
readscan_filter_pass(ipaddress ip, unsigned port, unsigned type,
const struct MassIP *filter,
const struct RangeList *btypes)
{
if (filter && filter->count_ipv4s) {
if (!massip_has_ip(filter, ip))
return 0;
}
if (filter && filter->count_ports) {
if (!massip_has_port(filter, port))
return 0;
}
if (btypes && btypes->count) {
if (!rangelist_is_contains(btypes, type))
return 0;
}
return 1;
}
================================================
FILE: src/in-filter.h
================================================
/*
This is for filtering input in the "--readscan" feature
*/
#ifndef IN_FILTER_H
#define IN_FILTER_H
#include "massip-addr.h"
struct RangeList;
struct Range6List;
struct MassIP;
/**
* Filters readscan record by IP address, port number,
* or banner-type.
*/
int
readscan_filter_pass(ipaddress ip, unsigned port, unsigned type,
const struct MassIP *massip,
const struct RangeList *btypes);
#endif
================================================
FILE: src/in-report.c
================================================
#include "in-report.h"
#include "masscan-app.h"
#include "crypto-base64.h"
#include "proto-x509.h"
#include "proto-banout.h"
#include "smack.h"
#include "util-malloc.h"
#include
#include
#include
struct CNDB_Entry {
unsigned ip;
char *name;
struct CNDB_Entry *next;
};
struct CNDB_Database {
struct CNDB_Entry *entries[65536];
};
/***************************************************************************
***************************************************************************/
static struct CNDB_Database *db = NULL;
/***************************************************************************
***************************************************************************/
static const char *
cndb_lookup(unsigned ip)
{
const struct CNDB_Entry *entry;
if (db == NULL)
return 0;
entry = db->entries[ip&0xFFFF];
while (entry && entry->ip != ip)
entry = entry->next;
if (entry)
return entry->name;
else {
return 0;
}
}
/***************************************************************************
***************************************************************************/
static void
cndb_add(unsigned ip, const unsigned char *name, size_t name_length)
{
struct CNDB_Entry *entry;
if (name_length == 0)
return;
if (db == NULL) {
db = CALLOC(1, sizeof(*db));
}
entry = MALLOC(sizeof(*entry));
entry->ip =ip;
entry->name = MALLOC(name_length+1);
memcpy(entry->name, name, name_length+1);
entry->name[name_length] = '\0';
entry->next = db->entries[ip&0xFFFF];
db->entries[ip&0xFFFF] = entry;
}
/***************************************************************************
***************************************************************************/
#if 0
static void
cndb_add_cn(unsigned ip, const unsigned char *data, size_t length)
{
size_t offset = 0;
size_t name_offset;
size_t name_length;
if (length < 7)
return;
/*cipher:0x39 , safe-we1.dyndns.org*/
if (memcmp(data+offset, "cipher:", 7) != 0)
return;
offset += 7;
/* skip to name */
while (offset < length && data[offset] != ',')
offset++;
if (offset >= length)
return;
else
offset++; /* skip ',' */
while (offset < length && data[offset] == ' ')
offset++;
if (offset >= length)
return;
/* we should have a good name */
name_offset = offset;
while (offset < length && data[offset] != ',')
offset++;
name_length = offset - name_offset;
/* now insert into database */
cndb_add(ip, data+name_offset, name_length);
}
#endif
/***************************************************************************
***************************************************************************/
#if 0
static unsigned
found(const char *str, size_t str_len, const unsigned char *p, size_t length)
{
size_t i;
if (str_len > length)
return 0;
for (i=0; i
void
readscan_report( unsigned ip,
unsigned app_proto,
unsigned char **data,
size_t *data_length);
void
readscan_report_init(void);
void
readscan_report_print(void);
#endif
================================================
FILE: src/main-conf.c
================================================
/*
Read in the configuration for MASSCAN.
Configuration parameters can be read either from the command-line
or a configuration file. Long parameters of the --xxxx variety have
the same name in both.
Most of the code in this module is for 'nmap' options we don't support.
That's because we support some 'nmap' options, and I wanted to give
more feedback for some of them why they don't work as expected, such
as reminding people that this is an asynchronous scanner.
*/
#include "masscan.h"
#include "massip-addr.h"
#include "masscan-version.h"
#include "util-safefunc.h"
#include "util-logger.h"
#include "proto-banner1.h"
#include "templ-payloads.h"
#include "crypto-base64.h"
#include "vulncheck.h"
#include "masscan-app.h"
#include "unusedparm.h"
#include "read-service-probes.h"
#include "util-malloc.h"
#include "massip.h"
#include "massip-parse.h"
#include "massip-port.h"
#include "templ-opts.h"
#include
#include
#ifdef WIN32
#include
#define getcwd _getcwd
#else
#include
#endif
#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif
#if defined(_MSC_VER)
#define strdup _strdup
#endif
/***************************************************************************
***************************************************************************/
/*static struct Range top_ports_tcp[] = {
{80, 80},{23, 23}, {443,443},{21,22},{25,25},{3389,3389},{110,110},
{445,445},
};
static struct Range top_ports_udp[] = {
{161, 161}, {631, 631}, {137,138},{123,123},{1434},{445,445},{135,135},
{67,67},
};
static struct Range top_ports_sctp[] = {
{7, 7},{9, 9},{20,22},{80,80},{179,179},{443,443},{1167,1167},
};*/
/***************************************************************************
***************************************************************************/
void
masscan_usage(void)
{
printf("usage: masscan [options] [... -pPORT[,PORT...]]\n");
printf("\n");
printf("examples:\n");
printf(" masscan -p80,8000-8100 10.0.0.0/8 --rate=10000\n");
printf(" scan some web ports on 10.x.x.x at 10kpps\n");
printf("\n");
printf(" masscan --nmap\n");
printf(" list those options that are compatible with nmap\n");
printf("\n");
printf(" masscan -p80 10.0.0.0/8 --banners -oB \n");
printf(" save results of scan in binary format to \n");
printf("\n");
printf(" masscan --open --banners --readscan -oX \n");
printf(" read binary scan results in and save them as xml in \n");
exit(1);
}
/***************************************************************************
***************************************************************************/
static void
print_version()
{
const char *cpu = "unknown";
const char *compiler = "unknown";
const char *compiler_version = "unknown";
const char *os = "unknown";
printf("\n");
printf("Masscan version %s ( %s )\n",
MASSCAN_VERSION,
"https://github.com/robertdavidgraham/masscan"
);
printf("Compiled on: %s %s\n", __DATE__, __TIME__);
#if defined(__x86_64) || defined(__x86_64__)
cpu = "x86";
#endif
#if defined(_MSC_VER)
#if defined(_M_AMD64) || defined(_M_X64)
cpu = "x86";
#elif defined(_M_IX86)
cpu = "x86";
#elif defined (_M_ARM_FP)
cpu = "arm";
#endif
{
int msc_ver = _MSC_VER;
compiler = "VisualStudio";
if (msc_ver < 1500)
compiler_version = "pre2008";
else if (msc_ver == 1500)
compiler_version = "2008";
else if (msc_ver == 1600)
compiler_version = "2010";
else if (msc_ver == 1700)
compiler_version = "2012";
else if (msc_ver == 1800)
compiler_version = "2013";
else
compiler_version = "post-2013";
}
#elif defined(__GNUC__)
# if defined(__clang__)
compiler = "clang";
# else
compiler = "gcc";
# endif
compiler_version = __VERSION__;
#if defined(i386) || defined(__i386) || defined(__i386__)
cpu = "x86";
#endif
#if defined(__corei7) || defined(__corei7__)
cpu = "x86-Corei7";
#endif
#endif
#if defined(WIN32)
os = "Windows";
#elif defined(__linux__)
os = "Linux";
#elif defined(__APPLE__)
os = "Apple";
#elif defined(__MACH__)
os = "MACH";
#elif defined(__FreeBSD__)
os = "FreeBSD";
#elif defined(__NetBSD__)
os = "NetBSD";
#elif defined(unix) || defined(__unix) || defined(__unix__)
os = "Unix";
#endif
printf("Compiler: %s %s\n", compiler, compiler_version);
printf("OS: %s\n", os);
printf("CPU: %s (%u bits)\n", cpu, (unsigned)(sizeof(void*))*8);
#if defined(GIT)
printf("GIT version: %s\n", GIT);
#endif
}
/***************************************************************************
***************************************************************************/
static void
print_nmap_help(void)
{
printf("Masscan (https://github.com/robertdavidgraham/masscan)\n"
"Usage: masscan [Options] -p{Target-Ports} {Target-IP-Ranges}\n"
"TARGET SPECIFICATION:\n"
" Can pass only IPv4/IPv6 address, CIDR networks, or ranges (non-nmap style)\n"
" Ex: 10.0.0.0/8, 192.168.0.1, 10.0.0.1-10.0.0.254\n"
" -iL : Input from list of hosts/networks\n"
" --exclude : Exclude hosts/networks\n"
" --excludefile : Exclude list from file\n"
" --randomize-hosts: Randomize order of hosts (default)\n"
"HOST DISCOVERY:\n"
" -Pn: Treat all hosts as online (default)\n"
" -n: Never do DNS resolution (default)\n"
"SCAN TECHNIQUES:\n"
" -sS: TCP SYN (always on, default)\n"
"SERVICE/VERSION DETECTION:\n"
" --banners: get the banners of the listening service if available. The\n"
" default timeout for waiting to receive data is 30 seconds.\n"
"PORT SPECIFICATION AND SCAN ORDER:\n"
" -p : Only scan specified ports\n"
" Ex: -p22; -p1-65535; -p 111,137,80,139,8080\n"
"TIMING AND PERFORMANCE:\n"
" --max-rate : Send packets no faster than per second\n"
" --connection-timeout : time in seconds a TCP connection will\n"
" timeout while waiting for banner data from a port.\n"
"FIREWALL/IDS EVASION AND SPOOFING:\n"
" -S/--source-ip : Spoof source address\n"
" -e : Use specified interface\n"
" -g/--source-port : Use given port number\n"
" --ttl : Set IP time-to-live field\n"
" --spoof-mac : Spoof your MAC address\n"
"OUTPUT:\n"
" --output-format : Sets output to binary/list/unicornscan/json/ndjson/grepable/xml\n"
" --output-file : Write scan results to file. If --output-format is\n"
" not given default is xml\n"
" -oL/-oJ/-oD/-oG/-oB/-oX/-oU : Output scan in List/JSON/nDjson/Grepable/Binary/XML/Unicornscan format,\n"
" respectively, to the given filename. Shortcut for\n"
" --output-format --output-file \n"
" -v: Increase verbosity level (use -vv or more for greater effect)\n"
" -d: Increase debugging level (use -dd or more for greater effect)\n"
" --open: Only show open (or possibly open) ports\n"
" --packet-trace: Show all packets sent and received\n"
" --iflist: Print host interfaces and routes (for debugging)\n"
" --append-output: Append to rather than clobber specified output files\n"
" --resume : Resume an aborted scan\n"
"MISC:\n"
" --send-eth: Send using raw ethernet frames (default)\n"
" -V: Print version number\n"
" -h: Print this help summary page.\n"
"EXAMPLES:\n"
" masscan -v -sS 192.168.0.0/16 10.0.0.0/8 -p 80\n"
" masscan 23.0.0.0/0 -p80 --banners -output-format binary --output-filename internet.scan\n"
" masscan --open --banners --readscan internet.scan -oG internet_scan.grepable\n"
"SEE (https://github.com/robertdavidgraham/masscan) FOR MORE HELP\n"
"\n");
}
/***************************************************************************
***************************************************************************/
static unsigned
count_cidr6_bits(struct Range6 *range, bool *exact)
{
uint64_t i;
/* for the comments of this function, see count_cidr_bits */
*exact = false;
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;
}
}
range->begin.lo = range->begin.lo + 1;
if (range->begin.lo == 0) {
range->begin.hi = range->begin.hi + 1;
}
return 128;
}
/***************************************************************************
* Echoes the configuration for one NIC
***************************************************************************/
static void
masscan_echo_nic(struct Masscan *masscan, FILE *fp, unsigned i)
{
char idx_str[64];
/* If we have only one adapter, then don't print the array indexes.
* Otherwise, we need to print the array indexes to distinguish
* the NICs from each other */
if (masscan->nic_count <= 1)
idx_str[0] = '\0';
else
snprintf(idx_str, sizeof(idx_str), "[%u]", i);
if (masscan->nic[i].ifname[0])
fprintf(fp, "adapter%s = %s\n", idx_str, masscan->nic[i].ifname);
if (masscan->nic[i].src.ipv4.first != 0 || masscan->nic[i].src.ipv4.last != 0) {
/**
* FIX 495.1 for issue #495: Single adapter-ip is not saved at all
*
* The else case handles a simple invocation of one adapter-ip:
*
* 1. masscan ... --adapter-ip 1.2.3.1 ... [BROKEN]
*
* This looks like it was just copy pasta/typo. If the first ip is the same
* as the last ip, it is a single adapter-ip
*
* This never worked as it was before so paused.conf would never save the
* adapter-ip as it fell through this if/else if into nowhere. It probably
* went undetected because in simple environments and/or in simple scans,
* masscan is able to intelligently determine the adapter-ip and only
* advanced usage requires overriding the chosen value. In addition to
* that, it is probably relatively uncommon to interrupt a scan as not many
* users are doing multi-hour / multi-day scans, having them paused and
* then resuming them (apparently)
*/
if (masscan->nic[i].src.ipv4.first == masscan->nic[i].src.ipv4.last) {
ipaddress_formatted_t fmt = ipv4address_fmt(masscan->nic[i].src.ipv4.first);
fprintf(fp, "adapter-ip%s = %s\n", idx_str, fmt.string);
}
/**
* FIX 495.2 for issue #495: Ranges of size two don't print. When 495.1 is
* added, ranges of size two print as only the first value in the range
* Before 495.1, they didn't print at all, so this is not a bug that is
* introduced by 495.1, just noticed while applying that fix
*
* The first if case here is for handling when adapter-ip is a range
*
* Examples of the multiple/range case:
*
* 1. masscan ... --adapter-ip 1.2.3.1-1.2.3.2 ... [BROKEN]
* 2. masscan ... --adapter-ip 1.2.3.1-1.2.3.4 ... [OK]
*
* If the range spans exactly two adapter-ips, it will not hit the range
* printing logic case here because of an off-by-one
*
* Changing it from < to <= fixes that issue and both of the above cases
* now print the correct range as expected
*/
else if (masscan->nic[i].src.ipv4.first < masscan->nic[i].src.ipv4.last) {
ipaddress_formatted_t fmt1 = ipv4address_fmt(masscan->nic[i].src.ipv4.first);
ipaddress_formatted_t fmt2 = ipv4address_fmt(masscan->nic[i].src.ipv4.last);
fprintf(fp, "adapter-ip%s = %s-%s\n", idx_str, fmt1.string, fmt2.string);
}
}
if (masscan->nic[i].src.ipv6.range) {
if (ipv6address_is_lessthan(masscan->nic[i].src.ipv6.first, masscan->nic[i].src.ipv6.last)) {
ipaddress_formatted_t fmt1 = ipv6address_fmt(masscan->nic[i].src.ipv6.first);
ipaddress_formatted_t fmt2 = ipv6address_fmt(masscan->nic[i].src.ipv6.last);
fprintf(fp, "adapter-ip%s = %s-%s\n", idx_str, fmt1.string, fmt2.string);
} else {
ipaddress_formatted_t fmt = ipv6address_fmt(masscan->nic[i].src.ipv6.first);
fprintf(fp, "adapter-ip%s = %s\n", idx_str, fmt.string);
}
}
if (masscan->nic[i].my_mac_count) {
ipaddress_formatted_t fmt = macaddress_fmt(masscan->nic[i].source_mac);
fprintf(fp, "adapter-mac%s = %s\n", idx_str, fmt.string);
}
if (masscan->nic[i].router_ip) {
ipaddress_formatted_t fmt = ipv4address_fmt(masscan->nic[i].router_ip);
fprintf(fp, "router-ip%s = %s\n", idx_str, fmt.string);
}
if (!macaddress_is_zero(masscan->nic[i].router_mac_ipv4)) {
ipaddress_formatted_t fmt = macaddress_fmt(masscan->nic[i].router_mac_ipv4);
fprintf(fp, "router-mac-ipv4%s = %s\n", idx_str, fmt.string);
}
if (!macaddress_is_zero(masscan->nic[i].router_mac_ipv6)) {
ipaddress_formatted_t fmt = macaddress_fmt(masscan->nic[i].router_mac_ipv6);
fprintf(fp, "router-mac-ipv6%s = %s\n", idx_str, fmt.string);
}
}
/***************************************************************************
***************************************************************************/
void
masscan_save_state(struct Masscan *masscan)
{
char filename[512];
FILE *fp;
safe_strcpy(filename, sizeof(filename), "paused.conf");
fprintf(stderr, " "
" \r");
fprintf(stderr, "saving resume file to: %s\n", filename);
fp = fopen(filename, "wt");
if (fp == NULL) {
fprintf(stderr, "[-] FAIL: saving resume file\n");
fprintf(stderr, "[-] %s: %s\n", filename, strerror(errno));
return;
}
masscan_echo(masscan, fp, 0);
fclose(fp);
}
#if 0
/*****************************************************************************
* Read in ranges from a file
*
* There can be multiple ranges on a line, delimited by spaces. In fact,
* millions of ranges can be on a line: there is limit to the line length.
* That makes reading the file a little bit squirrelly. From one perspective
* this parser doesn't treat the new-line '\n' any different than other
* space. But, from another perspective, it has to, because things like
* comments are terminated by a newline. Also, it has to count the number
* of lines correctly to print error messages.
*****************************************************************************/
static void
ranges_from_file(struct RangeList *ranges, const char *filename)
{
FILE *fp;
unsigned line_number = 0;
fp = fopen(filename, "rt");
if (fp) {
perror(filename);
exit(1); /* HARD EXIT: because if it's an exclusion file, we don't
* want to continue. We don't want ANY chance of
* accidentally scanning somebody */
}
while (!feof(fp)) {
int c = '\n';
/* remove leading whitespace */
while (!feof(fp)) {
c = getc(fp);
line_number += (c == '\n');
if (!isspace(c&0xFF))
break;
}
/* If this is a punctuation, like '#', then it's a comment */
if (ispunct(c&0xFF)) {
while (!feof(fp)) {
c = getc(fp);
line_number += (c == '\n');
if (c == '\n') {
break;
}
}
/* Loop back to the begining state at the start of a line */
continue;
}
if (c == '\n') {
continue;
}
/*
* Read in a single entry
*/
if (!feof(fp)) {
char address[64];
size_t i;
struct Range range;
unsigned offset = 0;
/* Grab all bytes until the next space or comma */
address[0] = (char)c;
i = 1;
while (!feof(fp)) {
c = getc(fp);
if (c == EOF)
break;
line_number += (c == '\n');
if (isspace(c&0xFF) || c == ',') {
break;
}
if (i+1 >= sizeof(address)) {
LOG(0, "%s:%u:%u: bad address spec: \"%.*s\"\n",
filename, line_number, offset, (int)i, address);
exit(1);
} else
address[i] = (char)c;
i++;
}
address[i] = '\0';
/* parse the address range */
range = range_parse_ipv4(address, &offset, (unsigned)i);
if (range.begin == 0xFFFFFFFF && range.end == 0) {
LOG(0, "%s:%u:%u: bad range spec: \"%.*s\"\n",
filename, line_number, offset, (int)i, address);
exit(1);
} else {
rangelist_add_range(ranges, range.begin, range.end);
}
}
}
fclose(fp);
/* Target list must be sorted every time it's been changed,
* before it can be used */
rangelist_sort(ranges);
}
#endif
/***************************************************************************
***************************************************************************/
static unsigned
hexval(char c)
{
if ('0' <= c && c <= '9')
return (unsigned)(c - '0');
if ('a' <= c && c <= 'f')
return (unsigned)(c - 'a' + 10);
if ('A' <= c && c <= 'F')
return (unsigned)(c - 'A' + 10);
return 0xFF;
}
/***************************************************************************
***************************************************************************/
static int
parse_mac_address(const char *text, macaddress_t *mac)
{
unsigned i;
for (i=0; i<6; i++) {
unsigned x;
char c;
while (isspace(*text & 0xFF) && ispunct(*text & 0xFF))
text++;
c = *text;
if (!isxdigit(c&0xFF))
return -1;
x = hexval(c)<<4;
text++;
c = *text;
if (!isxdigit(c&0xFF))
return -1;
x |= hexval(c);
text++;
mac->addr[i] = (unsigned char)x;
if (ispunct(*text & 0xFF))
text++;
}
return 0;
}
/***************************************************************************
***************************************************************************/
static uint64_t
parseInt(const char *str)
{
uint64_t result = 0;
while (*str && isdigit(*str & 0xFF)) {
result = result * 10 + (*str - '0');
str++;
}
return result;
}
/**
* a stricter function for determining if something is boolean.
*/
static bool
isBoolean(const char *str) {
size_t length = str?strlen(str):0;
if (length == 0)
return false;
/* "0" or "1" is boolean */
if (isdigit(str[0])) {
if (strtoul(str,0,0) == 0)
return true;
else if (strtoul(str,0,0) == 1)
return true;
else
return false;
}
switch (str[0]) {
case 'e':
case 'E':
if (memcasecmp("enable", str, length)==0)
return true;
if (memcasecmp("enabled", str, length)==0)
return true;
return false;
case 'd':
case 'D':
if (memcasecmp("disable", str, length)==0)
return true;
if (memcasecmp("disabled", str, length)==0)
return true;
return false;
case 't':
case 'T':
if (memcasecmp("true", str, length)==0)
return true;
return false;
case 'f':
case 'F':
if (memcasecmp("false", str, length)==0)
return true;
return false;
case 'o':
case 'O':
if (memcasecmp("on", str, length)==0)
return true;
if (memcasecmp("off", str, length)==0)
return true;
return false;
case 'Y':
case 'y':
if (memcasecmp("yes", str, length)==0)
return true;
return false;
case 'n':
case 'N':
if (memcasecmp("no", str, length)==0)
return true;
return false;
default:
return false;
}
}
static unsigned
parseBoolean(const char *str)
{
if (str == NULL || str[0] == 0)
return 1;
if (isdigit(str[0])) {
if (strtoul(str,0,0) == 0)
return 0;
else
return 1;
}
switch (str[0]) {
case 'e': /* enable */
case 'E':
return 1;
case 'd': /* disable */
case 'D':
return 0;
case 't': /* true */
case 'T':
return 1;
case 'f': /* false */
case 'F':
return 0;
case 'o': /* on or off */
case 'O':
if (str[1] == 'f' || str[1] == 'F')
return 0;
else
return 1;
break;
case 'Y': /* yes */
case 'y':
return 1;
case 'n': /* no */
case 'N':
return 0;
}
return 1;
}
/***************************************************************************
* Parses the number of seconds (for rotating files mostly). We do a little
* more than just parse an integer. We support strings like:
*
* hourly
* daily
* Week
* 5days
* 10-months
* 3600
***************************************************************************/
static uint64_t
parseTime(const char *value)
{
uint64_t num = 0;
unsigned is_negative = 0;
while (*value == '-') {
is_negative = 1;
value++;
}
while (isdigit(value[0]&0xFF)) {
num = num*10 + (value[0] - '0');
value++;
}
while (ispunct(value[0]) || isspace(value[0]))
value++;
if (isalpha(value[0]) && num == 0)
num = 1;
if (value[0] == '\0')
return num;
switch (tolower(value[0])) {
case 's':
num *= 1;
break;
case 'm':
num *= 60;
break;
case 'h':
num *= 60*60;
break;
case 'd':
num *= 24*60*60;
break;
case 'w':
num *= 24*60*60*7;
break;
default:
fprintf(stderr, "--rotate-offset: unknown character\n");
exit(1);
}
if (num >= 24*60*60) {
fprintf(stderr, "--rotate-offset: value is greater than 1 day\n");
exit(1);
}
if (is_negative)
num = 24*60*60 - num;
return num;
}
/***************************************************************************
* Parses a size integer, which can be suffixed with "tera", "giga",
* "mega", and "kilo". These numbers are in units of 1024 so suck it.
***************************************************************************/
static uint64_t
parseSize(const char *value)
{
uint64_t num = 0;
while (isdigit(value[0]&0xFF)) {
num = num*10 + (value[0] - '0');
value++;
}
while (ispunct(value[0]) || isspace(value[0]))
value++;
if (isalpha(value[0]) && num == 0)
num = 1;
if (value[0] == '\0')
return num;
switch (tolower(value[0])) {
case 'k': /* kilobyte */
num *= 1024ULL;
break;
case 'm': /* megabyte */
num *= 1024ULL * 1024ULL;
break;
case 'g': /* gigabyte */
num *= 1024ULL * 1024ULL * 1024ULL;
break;
case 't': /* terabyte, 'cause we roll that way */
num *= 1024ULL * 1024ULL * 1024ULL * 1024ULL;
break;
case 'p': /* petabyte, 'cause we are awesome */
num *= 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL;
break;
case 'e': /* exabyte, now that's just silly */
num *= 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL;
break;
default:
fprintf(stderr, "--rotate-size: unknown character\n");
exit(1);
}
return num;
}
/***************************************************************************
***************************************************************************/
static int
is_power_of_two(uint64_t x)
{
while ((x&1) == 0)
x >>= 1;
return x == 1;
}
/***************************************************************************
* Tests if the named parameter on the command-line. We do a little
* more than a straight string compare, because I get confused
* whether parameter have punctuation. Is it "--excludefile" or
* "--exclude-file"? I don't know if it's got that dash. Screw it,
* I'll just make the code so it don't care.
***************************************************************************/
static int
EQUALS(const char *lhs, const char *rhs)
{
for (;;) {
while (*lhs == '-' || *lhs == '.' || *lhs == '_')
lhs++;
while (*rhs == '-' || *rhs == '.' || *rhs == '_')
rhs++;
if (*lhs == '\0' && *rhs == '[')
return 1; /*arrays*/
if (tolower(*lhs & 0xFF) != tolower(*rhs & 0xFF))
return 0;
if (*lhs == '\0')
return 1;
lhs++;
rhs++;
}
}
static int
EQUALSx(const char *lhs, const char *rhs, size_t rhs_length)
{
for (;;) {
while (*lhs == '-' || *lhs == '.' || *lhs == '_')
lhs++;
while (*rhs == '-' || *rhs == '.' || *rhs == '_')
rhs++;
if (*lhs == '\0' && *rhs == '[')
return 1; /*arrays*/
if (tolower(*lhs & 0xFF) != tolower(*rhs & 0xFF))
return 0;
if (*lhs == '\0')
return 1;
lhs++;
rhs++;
if (--rhs_length == 0)
return 1;
}
}
static unsigned
INDEX_OF(const char *str, char c)
{
unsigned i;
for (i=0; str[i] && str[i] != c; i++)
;
return i;
}
static unsigned
ARRAY(const char *rhs)
{
const char *p = strchr(rhs, '[');
if (p == NULL)
return 0;
else
p++;
return (unsigned)parseInt(p);
}
/**
* Called if user specified `--top-ports` on the command-line.
*/
static void
config_top_ports(struct Masscan *masscan, unsigned maxports)
{
unsigned i;
static const unsigned short top_udp_ports[] = {
161, /* SNMP - should be found on all network equipment */
135, /* MS-RPC - should be found on all modern Windows */
500, /* ISAKMP - for establishing IPsec tunnels */
137, /* NetBIOS-NameService - should be found on old Windows */
138, /* NetBIOS-Datagram - should be found on old Windows */
445, /* SMB datagram service */
67, /* DHCP */
53, /* DNS */
1900, /* UPnP - Microsoft-focused local discovery */
5353, /* mDNS - Apple-focused local discovery */
4500, /* nat-t-ike - IPsec NAT traversal */
514, /* syslog - all Unix machiens */
69, /* TFTP */
49152, /* first of modern ephemeral ports */
631, /* IPP - printing protocol for Linux */
123, /* NTP network time protocol */
1434, /* MS-SQL server*/
520, /* RIP - routers use this protocol sometimes */
7, /* Echo */
111, /* SunRPC portmapper */
2049, /* SunRPC NFS */
5683, /* COAP */
11211, /* memcached */
1701, /* L2TP */
27960, /* quaked amplifier */
1645, /* RADIUS */
1812, /* RADIUS */
1646, /* RADIUS */
1813, /* RADIUS */
3343, /* Microsoft Cluster Services */
2535, /* MADCAP rfc2730 TODO FIXME */
};
static const unsigned short top_tcp_ports[] = {
80, 443, 8080, /* also web */
21, 990, /* FTP, oldie but goodie */
22, /* SSH, so much infrastructure */
23, 992, /* Telnet, oldie but still around*/
24, /* people put things here instead of TelnetSSH*/
25, 465, 587, 2525, /* SMTP email*/
5800, 5900, 5901, /* VNC */
111, /* SunRPC */
139, 445, /* Microsoft Windows networking */
135, /* DCEPRC, more Microsoft Windows */
3389, /* Microsoft Windows RDP */
88, /* Kerberos, also Microsoft windows */
389, 636, /* LDAP and MS Win */
1433, /* MS SQL */
53, /* DNS */
2083, 2096, /* cPanel */
9050, /* ToR */
8140, /* Puppet */
11211, /* memcached */
1098, 1099, /* Java RMI */
6000, 6001, /* XWindows */
5060, 5061, /* SIP - session initiation protocool */
554, /* RTSP */
548, /* AFP */
1,3,4,6,7,9,13,17,19,20,26,30,32,33,37,42,43,49,70,
79,81,82,83,84,85,89,90,99,100,106,109,110,113,119,125,
143,144,146,161,163,179,199,211,212,222,254,255,256,259,264,280,
301,306,311,340,366,406,407,416,417,425,427,444,458,464,
465,481,497,500,512,513,514,515,524,541,543,544,545,554,555,563,
593,616,617,625,631,646,648,666,667,668,683,687,691,700,705,
711,714,720,722,726,749,765,777,783,787,800,801,808,843,873,880,888,
898,900,901,902,903,911,912,981,987,993,995,999,1000,1001,
1002,1007,1009,1010,1011,1021,1022,1023,1024,1025,1026,1027,1028,
1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,
1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,
1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,
1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,
1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,
1094,1095,1096,1097,1100,1102,1104,1105,1106,1107,1108,
1110,1111,1112,1113,1114,1117,1119,1121,1122,1123,1124,1126,1130,
1131,1132,1137,1138,1141,1145,1147,1148,1149,1151,1152,1154,1163,
1164,1165,1166,1169,1174,1175,1183,1185,1186,1187,1192,1198,1199,
1201,1213,1216,1217,1218,1233,1234,1236,1244,1247,1248,1259,1271,
1272,1277,1287,1296,1300,1301,1309,1310,1311,1322,1328,1334,1352,
1417,1434,1443,1455,1461,1494,1500,1501,1503,1521,1524,1533,
1556,1580,1583,1594,1600,1641,1658,1666,1687,1688,1700,1717,1718,
1719,1720,1721,1723,1755,1761,1782,1783,1801,1805,1812,1839,1840,
1862,1863,1864,1875,1900,1914,1935,1947,1971,1972,1974,1984,1998,
1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2013,
2020,2021,2022,2030,2033,2034,2035,2038,2040,2041,2042,2043,2045,
2046,2047,2048,2049,2065,2068,2099,2100,2103,2105,2106,2107,2111,
2119,2121,2126,2135,2144,2160,2161,2170,2179,2190,2191,2196,2200,
2222,2251,2260,2288,2301,2323,2366,2381,2382,2383,2393,2394,2399,
2401,2492,2500,2522,2557,2601,2602,2604,2605,2607,2608,2638,
2701,2702,2710,2717,2718,2725,2800,2809,2811,2869,2875,2909,2910,
2920,2967,2968,2998,3000,3001,3003,3005,3006,3007,3011,3013,3017,
3030,3031,3052,3071,3077,3128,3168,3211,3221,3260,3261,3268,3269,
3283,3300,3301,3306,3322,3323,3324,3325,3333,3351,3367,3369,3370,
3371,3372,3389,3390,3404,3476,3493,3517,3527,3546,3551,3580,3659,
3689,3690,3703,3737,3766,3784,3800,3801,3809,3814,3826,3827,3828,
3851,3869,3871,3878,3880,3889,3905,3914,3918,3920,3945,3971,3986,
3995,3998,4000,4001,4002,4003,4004,4005,4006,4045,4111,4125,4126,
4129,4224,4242,4279,4321,4343,4443,4444,4445,4446,4449,4550,4567,
4662,4848,4899,4900,4998,5000,5001,5002,5003,5004,5009,5030,5033,
5050,5051,5054,5080,5087,5100,5101,5102,5120,5190,5200,
5214,5221,5222,5225,5226,5269,5280,5298,5357,5405,5414,5431,5432,
5440,5500,5510,5544,5550,5555,5560,5566,5631,5633,5666,5678,5679,
5718,5730,5801,5802,5810,5811,5815,5822,5825,5850,5859,5862,
5877,5902,5903,5904,5906,5907,5910,5911,5915,5922,5925,
5950,5952,5959,5960,5961,5962,5963,5987,5988,5989,5998,5999,
6002,6003,6004,6005,6006,6007,6009,6025,6059,6100,6101,6106,
6112,6123,6129,6156,6346,6389,6502,6510,6543,6547,6565,6566,6567,
6580,6646,6666,6667,6668,6669,6689,6692,6699,6779,6788,6789,6792,
6839,6881,6901,6969,7000,7001,7002,7004,7007,7019,7025,7070,7100,
7103,7106,7200,7201,7402,7435,7443,7496,7512,7625,7627,7676,7741,
7777,7778,7800,7911,7920,7921,7937,7938,7999,8000,8001,8002,8007,
8008,8009,8010,8011,8021,8022,8031,8042,8045,8080,8081,8082,8083,
8084,8085,8086,8087,8088,8089,8090,8093,8099,8100,8180,8181,8192,
8193,8194,8200,8222,8254,8290,8291,8292,8300,8333,8383,8400,8402,
8443,8500,8600,8649,8651,8652,8654,8701,8800,8873,8888,8899,8994,
9000,9001,9002,9003,9009,9010,9011,9040,9071,9080,9081,9090,
9091,9099,9100,9101,9102,9103,9110,9111,9200,9207,9220,9290,9415,
9418,9485,9500,9502,9503,9535,9575,9593,9594,9595,9618,9666,9876,
9877,9878,9898,9900,9917,9929,9943,9944,9968,9998,9999,10000,10001,
10002,10003,10004,10009,10010,10012,10024,10025,10082,10180,10215,
10243,10566,10616,10617,10621,10626,10628,10629,10778,11110,11111,
11967,12000,12174,12265,12345,13456,13722,13782,13783,14000,14238,
14441,14442,15000,15002,15003,15004,15660,15742,16000,16001,16012,
16016,16018,16080,16113,16992,16993,17877,17988,18040,18101,18988,
19101,19283,19315,19350,19780,19801,19842,20000,20005,20031,20221,
20222,20828,21571,22939,23502,24444,24800,25734,25735,26214,27000,
27352,27353,27355,27356,27715,28201,30000,30718,30951,31038,31337,
32768,32769,32770,32771,32772,32773,32774,32775,32776,32777,32778,
32779,32780,32781,32782,32783,32784,32785,33354,33899,34571,34572,
34573,35500,38292,40193,40911,41511,42510,44176,44442,44443,44501,
45100,48080,49152,49153,49154,49155,49156,49157,49158,49159,49160,
49161,49163,49165,49167,49175,49176,49400,49999,50000,50001,50002,
50003,50006,50300,50389,50500,50636,50800,51103,51493,52673,52822,
52848,52869,54045,54328,55055,55056,55555,55600,56737,56738,57294,
57797,58080,60020,60443,61532,61900,62078,63331,64623,64680,65000,
65129,65389};
struct RangeList *ports = &masscan->targets.ports;
static const unsigned max_tcp_ports = sizeof(top_tcp_ports)/sizeof(top_tcp_ports[0]);
static const unsigned max_udp_ports = sizeof(top_udp_ports)/sizeof(top_udp_ports[0]);
if (masscan->scan_type.tcp) {
LOG(2, "[+] adding TCP top-ports = %u\n", maxports);
for (i=0; iscan_type.udp) {
LOG(2, "[+] adding UDP top-ports = %u\n", maxports);
for (i=0; iecho) {
if (masscan->scan_type.arp || masscan->echo_all)
fprintf(masscan->echo, "arpscan = %s\n", masscan->scan_type.arp?"true":"false");
return 0;
}
range.begin = Templ_ARP;
range.end = Templ_ARP;
rangelist_add_range(&masscan->targets.ports, range.begin, range.end);
rangelist_sort(&masscan->targets.ports);
masscan_set_parameter(masscan, "router-mac", "ff-ff-ff-ff-ff-ff");
masscan->scan_type.arp = 1;
LOG(5, "--arpscan\n");
return CONF_OK;
}
static int SET_banners(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->is_banners || masscan->echo_all)
fprintf(masscan->echo, "banners = %s\n", masscan->is_banners?"true":"false");
return 0;
}
masscan->is_banners = parseBoolean(value);
return CONF_OK;
}
static int SET_banners_rawudp(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->is_banners_rawudp || masscan->echo_all)
fprintf(masscan->echo, "rawudp = %s\n", masscan->is_banners_rawudp?"true":"false");
return 0;
}
masscan->is_banners_rawudp = parseBoolean(value);
if (masscan->is_banners_rawudp)
masscan->is_banners = true;
return CONF_OK;
}
static int SET_capture(struct Masscan *masscan, const char *name, const char *value)
{
if (masscan->echo) {
if (!masscan->is_capture_cert || masscan->echo_all)
fprintf(masscan->echo, "%scapture = cert\n", masscan->is_capture_cert?"":"no");
if (!masscan->is_capture_servername || masscan->echo_all)
fprintf(masscan->echo, "%scapture = servername\n", masscan->is_capture_servername?"":"no");
if (masscan->is_capture_html || masscan->echo_all)
fprintf(masscan->echo, "%scapture = html\n", masscan->is_capture_html?"":"no");
if (masscan->is_capture_heartbleed || masscan->echo_all)
fprintf(masscan->echo, "%scapture = heartbleed\n", masscan->is_capture_heartbleed?"":"no");
if (masscan->is_capture_ticketbleed || masscan->echo_all)
fprintf(masscan->echo, "%scapture = ticketbleed\n", masscan->is_capture_ticketbleed?"":"no");
return 0;
}
if (EQUALS("capture", name)) {
if (EQUALS("cert", value))
masscan->is_capture_cert = 1;
else if (EQUALS("servername", value))
masscan->is_capture_servername = 1;
else if (EQUALS("html", value))
masscan->is_capture_html = 1;
else if (EQUALS("heartbleed", value))
masscan->is_capture_heartbleed = 1;
else if (EQUALS("ticketbleed", value))
masscan->is_capture_ticketbleed = 1;
else {
fprintf(stderr, "FAIL: %s: unknown capture type\n", value);
return CONF_ERR;
}
} else if (EQUALS("nocapture", name)) {
if (EQUALS("cert", value))
masscan->is_capture_cert = 0;
else if (EQUALS("servername", value))
masscan->is_capture_servername = 0;
else if (EQUALS("html", value))
masscan->is_capture_html = 0;
else if (EQUALS("heartbleed", value))
masscan->is_capture_heartbleed = 0;
else if (EQUALS("ticketbleed", value))
masscan->is_capture_ticketbleed = 0;
else {
fprintf(stderr, "FAIL: %s: unknown nocapture type\n", value);
return CONF_ERR;
}
}
return CONF_OK;
}
static int SET_hello(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->is_hello_ssl) {
fprintf(masscan->echo, "hello = ssl\n");
} else if (masscan->is_hello_smbv1) {
fprintf(masscan->echo, "hello = smbv1\n");
} else if (masscan->is_hello_http) {
fprintf(masscan->echo, "hello = http\n");
}
return 0;
}
if (EQUALS("ssl", value))
masscan->is_hello_ssl = 1;
else if (EQUALS("smbv1", value))
masscan->is_hello_smbv1 = 1;
else if (EQUALS("http", value))
masscan->is_hello_http = 1;
else {
fprintf(stderr, "FAIL: %s: unknown hello type\n", value);
return CONF_ERR;
}
return CONF_OK;
}
static int SET_hello_file(struct Masscan *masscan, const char *name, const char *value)
{
unsigned index;
FILE *fp;
char buf[16384];
char buf2[16384];
size_t bytes_read;
size_t bytes_encoded;
char foo[64];
if (masscan->echo) {
//Echoed as a string "hello-string" that was originally read
//from a file, not the "hello-filename"
return 0;
}
index = ARRAY(name);
if (index >= 65536) {
fprintf(stderr, "%s: bad index\n", name);
return CONF_ERR;
}
/* When connecting via TCP, send this file */
fp = fopen(value, "rb");
if (fp == NULL) {
LOG(0, "[-] [FAILED] --hello-file\n");
LOG(0, "[-] %s: %s\n", value, strerror(errno));
return CONF_ERR;
}
bytes_read = fread(buf, 1, sizeof(buf), fp);
if (bytes_read == 0) {
LOG(0, "[FAILED] could not read hello file\n");
perror(value);
fclose(fp);
return CONF_ERR;
}
fclose(fp);
bytes_encoded = base64_encode(buf2, sizeof(buf2)-1, buf, bytes_read);
buf2[bytes_encoded] = '\0';
snprintf(foo, sizeof(foo), "hello-string[%u]", (unsigned)index);
masscan_set_parameter(masscan, foo, buf2);
return CONF_OK;
}
static int SET_hello_string(struct Masscan *masscan, const char *name, const char *value)
{
unsigned index;
char *value2;
struct TcpCfgPayloads *pay;
if (masscan->echo) {
for (pay = masscan->payloads.tcp; pay; pay = pay->next) {
fprintf(masscan->echo, "hello-string[%u] = %s\n",
pay->port, pay->payload_base64);
}
return 0;
}
index = ARRAY(name);
if (index >= 65536) {
fprintf(stderr, "%s: bad index\n", name);
exit(1);
}
value2 = STRDUP(value);
pay = MALLOC(sizeof(*pay));
pay->payload_base64 = value2;
pay->port = index;
pay->next = masscan->payloads.tcp;
masscan->payloads.tcp = pay;
return CONF_OK;
}
static int SET_hello_timeout(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->tcp_hello_timeout || masscan->echo_all)
fprintf(masscan->echo, "hello-timeout = %u\n", masscan->tcp_hello_timeout);
return 0;
}
masscan->tcp_hello_timeout = (unsigned)parseInt(value);
return CONF_OK;
}
static int SET_http_cookie(struct Masscan *masscan, const char *name, const char *value)
{
unsigned char *newvalue;
size_t value_length;
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->http.cookies_count || masscan->echo_all) {
size_t i;
for (i=0; ihttp.cookies_count; i++) {
fprintf(masscan->echo,
"http-cookie = %.*s\n",
(unsigned)masscan->http.cookies[i].value_length,
masscan->http.cookies[i].value);
}
}
return 0;
}
/* allocate new value */
value_length = strlen(value);
newvalue = MALLOC(value_length+1);
memcpy(newvalue, value, value_length+1);
newvalue[value_length] = '\0';
/* Add to our list of headers */
if (masscan->http.cookies_count < sizeof(masscan->http.cookies)/sizeof(masscan->http.cookies[0])) {
size_t x = masscan->http.cookies_count;
masscan->http.cookies[x].value = newvalue;
masscan->http.cookies[x].value_length = value_length;
masscan->http.cookies_count++;
}
return CONF_OK;
}
static int SET_http_header(struct Masscan *masscan, const char *name, const char *value)
{
unsigned name_length;
char *newname;
unsigned char *newvalue;
size_t value_length;
if (masscan->echo) {
if (masscan->http.headers_count || masscan->echo_all) {
size_t i;
for (i=0; ihttp.headers_count; i++) {
if (masscan->http.headers[i].name == 0)
continue;
fprintf(masscan->echo,
"http-header = %s:%.*s\n",
masscan->http.headers[i].name,
(unsigned)masscan->http.headers[i].value_length,
masscan->http.headers[i].value);
}
}
return 0;
}
/*
* allocate a new name
*/
name += 11;
if (*name == '[') {
/* Specified as: "--http-header[name] value" */
while (ispunct(*name))
name++;
name_length = (unsigned)strlen(name);
while (name_length && ispunct(name[name_length-1]))
name_length--;
newname = MALLOC(name_length+1);
memcpy(newname, name, name_length+1);
newname[name_length] = '\0';
} else if (strchr(value, ':')) {
/* Specified as: "--http-header Name:value" */
name_length = INDEX_OF(value, ':');
newname = MALLOC(name_length + 1);
memcpy(newname, value, name_length + 1);
/* Trim the value */
value = value + name_length + 1;
while (*value && isspace(*value & 0xFF))
value++;
/* Trim the name */
while (name_length && isspace(newname[name_length-1]&0xFF))
name_length--;
newname[name_length] = '\0';
} else {
fprintf(stderr, "[-] --http-header needs both a name and value\n");
fprintf(stderr, " hint: \"--http-header Name:value\"\n");
exit(1);
}
/* allocate new value */
value_length = strlen(value);
newvalue = MALLOC(value_length+1);
memcpy(newvalue, value, value_length+1);
newvalue[value_length] = '\0';
/* Add to our list of headers */
if (masscan->http.headers_count < sizeof(masscan->http.headers)/sizeof(masscan->http.headers[0])) {
size_t x = masscan->http.headers_count;
masscan->http.headers[x].name = newname;
masscan->http.headers[x].value = newvalue;
masscan->http.headers[x].value_length = value_length;
masscan->http.headers_count++;
}
return CONF_OK;
}
static int SET_http_method(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->http.method || masscan->echo_all)
fprintf(masscan->echo, "http-method = %.*s\n", (unsigned)masscan->http.method_length, masscan->http.method);
return 0;
}
if (masscan->http.method)
free(masscan->http.method);
masscan->http.method_length = strlen(value);
masscan->http.method = MALLOC(masscan->http.method_length+1);
memcpy(masscan->http.method, value, masscan->http.method_length+1);
return CONF_OK;
}
static int SET_http_url(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->http.url || masscan->echo_all)
fprintf(masscan->echo, "http-url = %.*s\n", (unsigned)masscan->http.url_length, masscan->http.url);
return 0;
}
if (masscan->http.url)
free(masscan->http.url);
masscan->http.url_length = strlen(value);
masscan->http.url = MALLOC(masscan->http.url_length+1);
memcpy(masscan->http.url, value, masscan->http.url_length+1);
return CONF_OK;
}
static int SET_http_version(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->http.version || masscan->echo_all)
fprintf(masscan->echo, "http-version = %.*s\n", (unsigned)masscan->http.version_length, masscan->http.version);
return 0;
}
if (masscan->http.version)
free(masscan->http.version);
masscan->http.version_length = strlen(value);
masscan->http.version = MALLOC(masscan->http.version_length+1);
memcpy(masscan->http.version, value, masscan->http.version_length+1);
return CONF_OK;
}
static int SET_http_host(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->http.host || masscan->echo_all)
fprintf(masscan->echo, "http-host = %.*s\n", (unsigned)masscan->http.host_length, masscan->http.host);
return 0;
}
if (masscan->http.host)
free(masscan->http.host);
masscan->http.host_length = strlen(value);
masscan->http.host = MALLOC(masscan->http.host_length+1);
memcpy(masscan->http.host, value, masscan->http.host_length+1);
return CONF_OK;
}
static int SET_http_user_agent(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->http.user_agent || masscan->echo_all)
fprintf(masscan->echo, "http-user-agent = %.*s\n", (unsigned)masscan->http.user_agent_length, masscan->http.user_agent);
return 0;
}
if (masscan->http.user_agent)
free(masscan->http.user_agent);
masscan->http.user_agent_length = strlen(value);
masscan->http.user_agent = MALLOC(masscan->http.user_agent_length+1);
memcpy( masscan->http.user_agent,
value,
masscan->http.user_agent_length+1
);
return CONF_OK;
}
static int SET_http_payload(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->http.payload || masscan->echo_all)
fprintf(masscan->echo, "http-payload = %.*s\n", (unsigned)masscan->http.payload_length, masscan->http.payload);
return 0;
}
masscan->http.payload_length = strlen(value);
masscan->http.payload = REALLOC(masscan->http.payload, masscan->http.payload_length+1);
memcpy( masscan->http.payload,
value,
masscan->http.payload_length+1
);
return CONF_OK;
}
static int SET_status_ndjson(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->output.is_status_ndjson || masscan->echo_all)
fprintf(masscan->echo, "ndjson-status = %s\n", masscan->output.is_status_ndjson?"true":"false");
return 0;
}
masscan->output.is_status_ndjson = parseBoolean(value);
return CONF_OK;
}
static int SET_status_json(struct Masscan *masscan, const char *name, const char *value)
{
/* NOTE: this is here just to warn people they mistyped it */
UNUSEDPARM(name);
UNUSEDPARM(value);
if (masscan->echo) {
return 0;
}
fprintf(stderr, "[-] FAIL: %s not supported, use --status-ndjson\n", name);
fprintf(stderr, " hint: new-line delimited JSON status is what we use\n");
return CONF_ERR;
}
static int SET_min_packet(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->min_packet_size != 60 || masscan->echo_all)
fprintf(masscan->echo, "min-packet = %u\n", masscan->min_packet_size);
return 0;
}
masscan->min_packet_size = (unsigned)parseInt(value);
return CONF_OK;
}
static int SET_nobanners(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
return 0;
}
masscan->is_banners = !parseBoolean(value);
return CONF_OK;
}
static int SET_noreset(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->is_noreset || masscan->echo_all)
fprintf(masscan->echo, "noreset = %s\n", masscan->is_noreset?"true":"false");
return 0;
}
masscan->is_noreset = parseBoolean(value);
return CONF_OK;
}
static int SET_nmap_payloads(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if ((masscan->payloads.nmap_payloads_filename && masscan->payloads.nmap_payloads_filename[0]) || masscan->echo_all)
fprintf(masscan->echo, "nmap-payloads = %s\n", masscan->payloads.nmap_payloads_filename);
return 0;
}
if (masscan->payloads.nmap_payloads_filename)
free(masscan->payloads.nmap_payloads_filename);
masscan->payloads.nmap_payloads_filename = strdup(value);
return CONF_OK;
}
static int SET_nmap_service_probes(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if ((masscan->payloads.nmap_service_probes_filename && masscan->payloads.nmap_service_probes_filename[0]) || masscan->echo_all)
fprintf(masscan->echo, "nmap-service-probes = %s\n", masscan->payloads.nmap_service_probes_filename);
return 0;
}
if (masscan->payloads.nmap_service_probes_filename)
free(masscan->payloads.nmap_service_probes_filename);
masscan->payloads.nmap_service_probes_filename = strdup(value);
return CONF_OK;
}
static int SET_offline(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->is_offline || masscan->echo_all)
fprintf(masscan->echo, "offline = %s\n", masscan->is_offline?"true":"false");
return 0;
}
masscan->is_offline = parseBoolean(value);
return CONF_OK;
}
static int SET_output_append(struct Masscan *masscan, const char *name, const char *value)
{
if (masscan->echo) {
if (masscan->output.is_append || masscan->echo_all)
fprintf(masscan->echo, "output-append = %s\n",
masscan->output.is_append?"true":"false");
return 0;
}
if (EQUALS("overwrite", name) || !parseBoolean(value))
masscan->output.is_append = 0;
else
masscan->output.is_append = 1;
return CONF_OK;
}
static int SET_output_filename(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->output.filename[0] || masscan->echo_all)
fprintf(masscan->echo, "output-filename = %s\n", masscan->output.filename);
return 0;
}
if (masscan->output.format == 0)
masscan->output.format = Output_XML; /*TODO: Why is the default XML?*/
safe_strcpy(masscan->output.filename,
sizeof(masscan->output.filename),
value);
return CONF_OK;
}
static int SET_output_format(struct Masscan *masscan, const char *name, const char *value)
{
enum OutputFormat x = 0;
UNUSEDPARM(name);
if (masscan->echo) {
FILE *fp = masscan->echo;
ipaddress_formatted_t fmt;
switch (masscan->output.format) {
case Output_Default: if (masscan->echo_all) fprintf(fp, "output-format = interactive\n"); break;
case Output_Interactive:fprintf(fp, "output-format = interactive\n"); break;
case Output_List: fprintf(fp, "output-format = list\n"); break;
case Output_Unicornscan:fprintf(fp, "output-format = unicornscan\n"); break;
case Output_XML: fprintf(fp, "output-format = xml\n"); break;
case Output_Binary: fprintf(fp, "output-format = binary\n"); break;
case Output_Grepable: fprintf(fp, "output-format = grepable\n"); break;
case Output_JSON: fprintf(fp, "output-format = json\n"); break;
case Output_NDJSON: fprintf(fp, "output-format = ndjson\n"); break;
case Output_Certs: fprintf(fp, "output-format = certs\n"); break;
case Output_None: fprintf(fp, "output-format = none\n"); break;
case Output_Hostonly: fprintf(fp, "output-format = hostonly\n"); break;
case Output_Redis:
fmt = ipaddress_fmt(masscan->redis.ip);
fprintf(fp, "output-format = redis\n");
fprintf(fp, "redis = %s %u\n", fmt.string, masscan->redis.port);
break;
default:
fprintf(fp, "output-format = unknown(%u)\n", masscan->output.format);
break;
}
return 0;
}
if (EQUALS("unknown(0)", value)) x = Output_Interactive;
else if (EQUALS("interactive", value)) x = Output_Interactive;
else if (EQUALS("list", value)) x = Output_List;
else if (EQUALS("unicornscan", value)) x = Output_Unicornscan;
else if (EQUALS("xml", value)) x = Output_XML;
else if (EQUALS("binary", value)) x = Output_Binary;
else if (EQUALS("greppable", value)) x = Output_Grepable;
else if (EQUALS("grepable", value)) x = Output_Grepable;
else if (EQUALS("json", value)) x = Output_JSON;
else if (EQUALS("ndjson", value)) x = Output_NDJSON;
else if (EQUALS("certs", value)) x = Output_Certs;
else if (EQUALS("none", value)) x = Output_None;
else if (EQUALS("redis", value)) x = Output_Redis;
else if (EQUALS("hostonly", value)) x = Output_Hostonly;
else {
LOG(0, "FAIL: unknown output-format: %s\n", value);
LOG(0, " hint: 'binary', 'xml', 'grepable', ...\n");
return CONF_ERR;
}
masscan->output.format = x;
return CONF_OK;
}
static int SET_output_noshow(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->echo_all) {
fprintf(masscan->echo, "output-noshow = %s%s%s\n",
(!masscan->output.is_show_open)?"open,":"",
(!masscan->output.is_show_closed)?"closed,":"",
(!masscan->output.is_show_host)?"host,":""
);
}
return 0;
}
for (;;) {
const char *val2 = value;
unsigned val2_len = INDEX_OF(val2, ',');
if (val2_len == 0)
break;
if (EQUALSx("open", val2, val2_len))
masscan->output.is_show_open = 0;
else if (EQUALSx("closed", val2, val2_len) || EQUALSx("close", val2, val2_len))
masscan->output.is_show_closed = 0;
else if (EQUALSx("open", val2, val2_len))
masscan->output.is_show_host = 0;
else if (EQUALSx("all",val2,val2_len)) {
masscan->output.is_show_open = 0;
masscan->output.is_show_host = 0;
masscan->output.is_show_closed = 0;
}
else {
LOG(0, "FAIL: unknown 'noshow' spec: %.*s\n", val2_len, val2);
exit(1);
}
value += val2_len;
while (*value == ',')
value++;
}
return CONF_OK;
}
static int SET_output_show(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->echo_all) {
fprintf(masscan->echo, "output-show = %s%s%s\n",
masscan->output.is_show_open?"open,":"",
masscan->output.is_show_closed?"closed,":"",
masscan->output.is_show_host?"host,":""
);
}
return 0;
}
for (;;) {
const char *val2 = value;
unsigned val2_len = INDEX_OF(val2, ',');
if (val2_len == 0)
break;
if (EQUALSx("open", val2, val2_len))
masscan->output.is_show_open = 1;
else if (EQUALSx("closed", val2, val2_len) || EQUALSx("close", val2, val2_len))
masscan->output.is_show_closed = 1;
else if (EQUALSx("open", val2, val2_len))
masscan->output.is_show_host = 1;
else if (EQUALSx("all",val2,val2_len)) {
masscan->output.is_show_open = 1;
masscan->output.is_show_host = 1;
masscan->output.is_show_closed = 1;
}
else {
LOG(0, "FAIL: unknown 'show' spec: %.*s\n", val2_len, val2);
exit(1);
}
value += val2_len;
while (*value == ',')
value++;
}
return CONF_OK;
}
static int SET_output_show_open(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
UNUSEDPARM(value);
if (masscan->echo) {
return 0;
}
/* "open" "open-only" */
masscan->output.is_show_open = 1;
masscan->output.is_show_closed = 0;
masscan->output.is_show_host = 0;
return CONF_OK;
}
/* Specifies a 'libpcap' file where the received packets will be written.
* This is useful while debugging so that we can see what exactly is
* going on. It's also an alternate mode for getting output from this
* program. Instead of relying upon this program's determination of what
* ports are open or closed, you can instead simply parse this capture
* file yourself and make your own determination */
static int SET_pcap_filename(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->pcap_filename[0])
fprintf(masscan->echo, "pcap-filename = %s\n", masscan->pcap_filename);
return 0;
}
if (value)
safe_strcpy(masscan->pcap_filename, sizeof(masscan->pcap_filename), value);
return CONF_OK;
}
/* Specifies a 'libpcap' file from which to read packet-payloads. The payloads found
* in this file will serve as the template for spewing out custom packets. There are
* other options that can set payloads as well, like "--nmap-payloads" for reading
* their custom payload file, as well as the various "hello" options for specifying
* the string sent to the server once a TCP connection has been established. */
static int SET_pcap_payloads(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if ((masscan->payloads.pcap_payloads_filename && masscan->payloads.pcap_payloads_filename[0]) || masscan->echo_all)
fprintf(masscan->echo, "pcap-payloads = %s\n", masscan->payloads.pcap_payloads_filename);
return 0;
}
if (masscan->payloads.pcap_payloads_filename)
free(masscan->payloads.pcap_payloads_filename);
masscan->payloads.pcap_payloads_filename = strdup(value);
/* file will be loaded in "masscan_load_database_files()" */
return CONF_OK;
}
static int SET_randomize_hosts(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
UNUSEDPARM(value);
if (masscan->echo) {
//fprintf(masscan->echo, "randomize-hosts = true\n");
return 0;
}
return CONF_OK;
}
static int SET_rate(struct Masscan *masscan, const char *name, const char *value)
{
double rate = 0.0;
double point = 10.0;
unsigned i;
if (masscan->echo) {
if ((unsigned)(masscan->max_rate * 100000) % 100000) {
/* print as floating point number, which is rare */
fprintf(masscan->echo, "rate = %f\n", masscan->max_rate);
} else {
/* pretty print as just an integer, which is what most people
* expect */
fprintf(masscan->echo, "rate = %-10.0f\n", masscan->max_rate);
}
return 0;
}
for (i=0; value[i] && value[i] != '.'; i++) {
char c = value[i];
if (c < '0' || '9' < c) {
fprintf(stderr, "CONF: non-digit in rate spec: %s=%s\n", name, value);
return CONF_ERR;
}
rate = rate * 10.0 + (c - '0');
}
if (value[i] == '.') {
i++;
while (value[i]) {
char c = value[i];
if (c < '0' || '9' < c) {
fprintf(stderr, "CONF: non-digit in rate spec: %s=%s\n",
name, value);
return CONF_ERR;
}
rate += (c - '0')/point;
point *= 10.0;
value++;
}
}
masscan->max_rate = rate;
return CONF_OK;
}
static int SET_resume_count(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->resume.count || masscan->echo_all) {
fprintf(masscan->echo, "resume-count = %" PRIu64 "\n", masscan->resume.count);
}
return 0;
}
masscan->resume.count = parseInt(value);
return CONF_OK;
}
static int SET_resume_index(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->resume.index || masscan->echo_all) {
fprintf(masscan->echo, "\n# resume information\n");
fprintf(masscan->echo, "resume-index = %" PRIu64 "\n", masscan->resume.index);
}
return 0;
}
masscan->resume.index = parseInt(value);
return CONF_OK;
}
static int SET_retries(struct Masscan *masscan, const char *name, const char *value)
{
uint64_t x;
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->retries || masscan->echo_all)
fprintf(masscan->echo, "retries = %u\n", masscan->retries);
return 0;
}
x = strtoul(value, 0, 0);
if (x >= 1000) {
fprintf(stderr, "FAIL: retries=: expected number less than 1000\n");
return CONF_ERR;
}
masscan->retries = (unsigned)x;
return CONF_OK;
}
static int SET_rotate_time(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->output.rotate.timeout || masscan->echo_all)
fprintf(masscan->echo, "rotate = %u\n", masscan->output.rotate.timeout);
return 0;
}
masscan->output.rotate.timeout = (unsigned)parseTime(value);
return CONF_OK;
}
static int SET_rotate_directory(struct Masscan *masscan, const char *name, const char *value)
{
char *p;
UNUSEDPARM(name);
if (masscan->echo) {
if (memcmp(masscan->output.rotate.directory, ".",2) != 0 || masscan->echo_all) {
fprintf(masscan->echo, "rotate-dir = %s\n", masscan->output.rotate.directory);
}
return 0;
}
safe_strcpy( masscan->output.rotate.directory,
sizeof(masscan->output.rotate.directory),
value);
/* strip trailing slashes */
p = masscan->output.rotate.directory;
while (*p && (p[strlen(p)-1] == '/' || p[strlen(p)-1] == '\\')) /* Fix for #561 */
p[strlen(p)-1] = '\0';
return CONF_OK;
}
static int SET_rotate_offset(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
/* Time offset, otherwise output files are aligned to nearest time
* interval, e.g. at the start of the hour for "hourly" */
if (masscan->echo) {
if (masscan->output.rotate.offset || masscan->echo_all)
fprintf(masscan->echo, "rotate-offset = %u\n", masscan->output.rotate.offset);
return 0;
}
masscan->output.rotate.offset = (unsigned)parseTime(value);
return CONF_OK;
}
static int SET_rotate_filesize(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->output.rotate.filesize || masscan->echo_all)
fprintf(masscan->echo, "rotate-size = %" PRIu64 "\n", masscan->output.rotate.filesize);
return 0;
}
masscan->output.rotate.filesize = parseSize(value);
return CONF_OK;
}
static int SET_script(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if ((masscan->scripting.name && masscan->scripting.name[0]) || masscan->echo_all)
fprintf(masscan->echo, "script = %s\n", masscan->scripting.name);
return 0;
}
if (value && value[0])
masscan->is_scripting = 1;
else
masscan->is_scripting = 0;
if (masscan->scripting.name)
free(masscan->scripting.name);
masscan->scripting.name = strdup(value);
return CONF_OK;
}
static int SET_seed(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
fprintf(masscan->echo, "seed = %" PRIu64 "\n", masscan->seed);
return 0;
}
if (EQUALS("time", value))
masscan->seed = time(0);
else
masscan->seed = parseInt(value);
return CONF_OK;
}
static int SET_space(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
UNUSEDPARM(value);
if (masscan->echo) {
fprintf(masscan->echo, "\n");
return 0;
}
return CONF_OK;
}
static int SET_shard(struct Masscan *masscan, const char *name, const char *value)
{
unsigned one = 0;
unsigned of = 0;
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->shard.of > 0 || masscan->echo_all)
fprintf(masscan->echo, "shard = %u/%u\n", masscan->shard.one, masscan->shard.of);
return 0;
}
while (isdigit(*value))
one = one*10 + (*(value++)) - '0';
while (ispunct(*value))
value++;
while (isdigit(*value))
of = of*10 + (*(value++)) - '0';
if (one < 1) {
LOG(0, "FAIL: shard index can't be zero\n");
LOG(0, "hint it goes like 1/4 2/4 3/4 4/4\n");
return CONF_ERR;
}
if (one > of) {
LOG(0, "FAIL: shard spec is wrong\n");
LOG(0, "hint it goes like 1/4 2/4 3/4 4/4\n");
return CONF_ERR;
}
masscan->shard.one = one;
masscan->shard.of = of;
return CONF_OK;
}
static int SET_output_stylesheet(struct Masscan *masscan, const char *name, const char *value)
{
UNUSEDPARM(name);
if (masscan->echo) {
if (masscan->output.stylesheet[0] || masscan->echo_all)
fprintf(masscan->echo, "stylesheet = %s\n", masscan->output.stylesheet);
return 0;
}
if (masscan->output.format == 0)
masscan->output.format = Output_XML;
safe_strcpy(masscan->output.stylesheet, sizeof(masscan->output.stylesheet), value);
return CONF_OK;
}
static int SET_topports(struct Masscan *masscan, const char *name, const char *value)
{
unsigned default_value = 20;
if (masscan->echo) {
/* don't echo: this instead triggers filling the `--port`
* list, so the ports themselves will be echoed, not this
* parameter */
return 0;
}
if (value == 0 || value[0] == '\0') {
/* can be specified by itself on the command-line, alone
* without a following parameter */
/* ex: `--top-ports` */
masscan->top_ports = default_value;
} else if (isBoolean(value)) {
/* ex: `--top-ports enable` */
if (parseBoolean(value))
masscan->top_ports = default_value;
else
masscan->top_ports = 0;
} else if (isInteger(value)) {
/* ex: `--top-ports 5` */
uint64_t num = parseInt(value);
masscan->top_ports = (unsigned)num;
} else {
fprintf(stderr, "[-] %s: bad value: %s\n", name, value);
return CONF_ERR;
}
return CONF_OK;
}
static int SET_tcp_mss(struct Masscan *masscan, const char *name, const char *value)
{
/* h/t @IvreRocks */
static const unsigned default_mss = 1460;
if (masscan->echo) {
if (masscan->templ_opts) {
switch (masscan->templ_opts->tcp.is_mss) {
case Default:
break;
case Add:
if (masscan->templ_opts->tcp.mss == default_mss)
fprintf(masscan->echo, "tcp-mss = %s\n", "enable");
else
fprintf(masscan->echo, "tcp-mss = %u\n",
masscan->templ_opts->tcp.mss);
break;
case Remove:
fprintf(masscan->echo, "tcp-mss = %s\n", "disable");
break;
default:
break;
}
}
return 0;
}
if (masscan->templ_opts == NULL)
masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts));
if (value == 0 || value[0] == '\0') {
/* no following parameter, so interpret this to mean "enable" */
masscan->templ_opts->tcp.is_mss = Add;
masscan->templ_opts->tcp.mss = default_mss; /* 1460 */
} else if (isBoolean(value)) {
/* looking for "enable" or "disable", but any boolean works,
* like "true/false" or "off/on" */
if (parseBoolean(value)) {
masscan->templ_opts->tcp.is_mss = Add;
masscan->templ_opts->tcp.mss = default_mss; /* 1460 */
} else
masscan->templ_opts->tcp.is_mss = Remove;
} else if (isInteger(value)) {
/* A specific number was specified */
uint64_t num = parseInt(value);
if (num >= 0x10000)
goto fail;
masscan->templ_opts->tcp.is_mss = Add;
masscan->templ_opts->tcp.mss = (unsigned)num;
} else
goto fail;
return CONF_OK;
fail:
fprintf(stderr, "[-] %s: bad value: %s\n", name, value);
return CONF_ERR;
}
static int SET_tcp_wscale(struct Masscan *masscan, const char *name, const char *value)
{
static const unsigned default_value = 3;
if (masscan->echo) {
if (masscan->templ_opts) {
switch (masscan->templ_opts->tcp.is_wscale) {
case Default:
break;
case Add:
if (masscan->templ_opts->tcp.wscale == default_value)
fprintf(masscan->echo, "tcp-wscale = %s\n", "enable");
else
fprintf(masscan->echo, "tcp-wscale = %u\n",
masscan->templ_opts->tcp.wscale);
break;
case Remove:
fprintf(masscan->echo, "tcp-wscale = %s\n", "disable");
break;
default:
break;
}
}
return 0;
}
if (masscan->templ_opts == NULL)
masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts));
if (value == 0 || value[0] == '\0') {
masscan->templ_opts->tcp.is_wscale = Add;
masscan->templ_opts->tcp.wscale = default_value;
} else if (isBoolean(value)) {
if (parseBoolean(value)) {
masscan->templ_opts->tcp.is_wscale = Add;
masscan->templ_opts->tcp.wscale = default_value;
} else
masscan->templ_opts->tcp.is_wscale = Remove;
} else if (isInteger(value)) {
uint64_t num = parseInt(value);
if (num >= 255)
goto fail;
masscan->templ_opts->tcp.is_wscale = Add;
masscan->templ_opts->tcp.wscale = (unsigned)num;
} else
goto fail;
return CONF_OK;
fail:
fprintf(stderr, "[-] %s: bad value: %s\n", name, value);
return CONF_ERR;
}
static int SET_tcp_tsecho(struct Masscan *masscan, const char *name, const char *value)
{
static const unsigned default_value = 0x12345678;
if (masscan->echo) {
if (masscan->templ_opts) {
switch (masscan->templ_opts->tcp.is_tsecho) {
case Default:
break;
case Add:
if (masscan->templ_opts->tcp.tsecho == default_value)
fprintf(masscan->echo, "tcp-tsecho = %s\n", "enable");
else
fprintf(masscan->echo, "tcp-tsecho = %u\n",
masscan->templ_opts->tcp.tsecho);
break;
case Remove:
fprintf(masscan->echo, "tcp-tsecho = %s\n", "disable");
break;
default:
break;
}
}
return 0;
}
if (masscan->templ_opts == NULL)
masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts));
if (value == 0 || value[0] == '\0') {
masscan->templ_opts->tcp.is_tsecho = Add;
masscan->templ_opts->tcp.tsecho = default_value;
} else if (isBoolean(value)) {
if (parseBoolean(value)) {
masscan->templ_opts->tcp.is_tsecho = Add;
masscan->templ_opts->tcp.tsecho = default_value;
} else
masscan->templ_opts->tcp.is_tsecho = Remove;
} else if (isInteger(value)) {
uint64_t num = parseInt(value);
if (num >= 255)
goto fail;
masscan->templ_opts->tcp.is_tsecho = Add;
masscan->templ_opts->tcp.tsecho = (unsigned)num;
} else
goto fail;
return CONF_OK;
fail:
fprintf(stderr, "[-] %s: bad value: %s\n", name, value);
return CONF_ERR;
}
static int SET_tcp_sackok(struct Masscan *masscan, const char *name, const char *value)
{
if (masscan->echo) {
if (masscan->templ_opts) {
switch (masscan->templ_opts->tcp.is_sackok) {
case Default:
break;
case Add:
fprintf(masscan->echo, "tcp-sackok = %s\n", "enable");
break;
case Remove:
fprintf(masscan->echo, "tcp-sackok = %s\n", "disable");
break;
default:
break;
}
}
return 0;
}
if (masscan->templ_opts == NULL)
masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts));
if (value == 0 || value[0] == '\0') {
masscan->templ_opts->tcp.is_sackok = Add;
} else if (isBoolean(value)) {
if (parseBoolean(value)) {
masscan->templ_opts->tcp.is_sackok = Add;
} else
masscan->templ_opts->tcp.is_sackok = Remove;
} else if (isInteger(value)) {
if (parseInt(value) != 0)
masscan->templ_opts->tcp.is_sackok = Add;
} else
goto fail;
return CONF_OK;
fail:
fprintf(stderr, "[-] %s: bad value: %s\n", name, value);
return CONF_ERR;
}
static int SET_debug_tcp(struct Masscan *masscan, const char *name, const char *value) {
extern int is_tcp_debug; /* global */
UNUSEDPARM(name);
UNUSEDPARM(masscan);
if (value == 0 || value[0] == '\0')
is_tcp_debug = 1;
else
is_tcp_debug = parseBoolean(value);
return CONF_OK;
}
struct ConfigParameter {
const char *name;
SET_PARAMETER set;
unsigned flags;
const char *alts[6];
};
enum {F_NONE, F_BOOL=1, F_NUMABLE=2};
struct ConfigParameter config_parameters[] = {
{"resume-index", SET_resume_index, 0, {0}},
{"resume-count", SET_resume_count, 0, {0}},
{"seed", SET_seed, 0, {0}},
{"arpscan", SET_arpscan, F_BOOL, {"arp",0}},
{"randomize-hosts", SET_randomize_hosts, F_BOOL, {0}},
{"rate", SET_rate, 0, {"max-rate",0}},
{"shard", SET_shard, 0, {"shards",0}},
{"banners", SET_banners, F_BOOL, {"banner",0}}, /* --banners */
{"rawudp", SET_banners_rawudp, F_BOOL, {"rawudp",0}}, /* --rawudp */
{"nobanners", SET_nobanners, F_BOOL, {"nobanner",0}},
{"retries", SET_retries, 0, {"retry", "max-retries", "max-retry", 0}},
{"noreset", SET_noreset, F_BOOL, {0}},
{"nmap-payloads", SET_nmap_payloads, 0, {"nmap-payload",0}},
{"nmap-service-probes",SET_nmap_service_probes, 0, {"nmap-service-probe",0}},
{"offline", SET_offline, F_BOOL, {"notransmit", "nosend", "dry-run", 0}},
{"pcap-filename", SET_pcap_filename, 0, {"pcap",0}},
{"pcap-payloads", SET_pcap_payloads, 0, {"pcap-payload",0}},
{"hello", SET_hello, 0, {0}},
{"hello-file", SET_hello_file, 0, {"hello-filename",0}},
{"hello-string", SET_hello_string, 0, {0}},
{"hello-timeout", SET_hello_timeout, 0, {0}},
{"http-cookie", SET_http_cookie, 0, {0}},
{"http-header", SET_http_header, 0, {"http-field", 0}},
{"http-method", SET_http_method, 0, {0}},
{"http-version", SET_http_version, 0, {0}},
{"http-url", SET_http_url, 0, {"http-uri",0}},
{"http-user-agent", SET_http_user_agent, 0, {"http-useragent",0}},
{"http-host", SET_http_host, 0, {0}},
{"http-payload", SET_http_payload, 0, {0}},
{"ndjson-status", SET_status_ndjson, F_BOOL, {"status-ndjson", 0}},
{"json-status", SET_status_json, F_BOOL, {"status-json", 0}},
{"min-packet", SET_min_packet, 0, {"min-pkt",0}},
{"capture", SET_capture, 0, {0}},
{"nocapture", SET_capture, 0, {"no-capture", 0}},
{"SPACE", SET_space, 0, {0}},
{"output-filename", SET_output_filename, 0, {"output-file",0}},
{"output-format", SET_output_format, 0, {0}},
{"output-show", SET_output_show, 0, {"output-status", "show",0}},
{"output-noshow", SET_output_noshow, 0, {"noshow",0}},
{"output-show-open",SET_output_show_open, F_BOOL, {"open", "open-only", 0}},
{"output-append", SET_output_append, 0, {"append-output",0}},
{"rotate", SET_rotate_time, 0, {"output-rotate", "rotate-output", "rotate-time", 0}},
{"rotate-dir", SET_rotate_directory, 0, {"output-rotate-dir", "rotate-directory", 0}},
{"rotate-offset", SET_rotate_offset, 0, {"output-rotate-offset", 0}},
{"rotate-size", SET_rotate_filesize, 0, {"output-rotate-filesize", "rotate-filesize", 0}},
{"stylesheet", SET_output_stylesheet, 0, {0}},
{"script", SET_script, 0, {0}},
{"SPACE", SET_space, 0, {0}},
{"tcp-mss", SET_tcp_mss, F_NUMABLE, {"tcpmss",0}},
{"tcp-wscale", SET_tcp_wscale, F_NUMABLE, {0}},
{"tcp-tsecho", SET_tcp_tsecho, F_NUMABLE, {0}},
{"tcp-sackok", SET_tcp_sackok, F_BOOL, {0}},
{"top-ports", SET_topports, F_NUMABLE, {"top-port",0}},
{"debug-tcp", SET_debug_tcp, F_BOOL, {"tcp-debug", 0}},
{0}
};
/***************************************************************************
* Called either from the "command-line" parser when it sees a --param,
* or from the "config-file" parser for normal options.
***************************************************************************/
void
masscan_set_parameter(struct Masscan *masscan,
const char *name, const char *value)
{
unsigned index = ARRAY(name);
if (index >= 65536) {
fprintf(stderr, "%s: bad index\n", name);
exit(1);
}
/*
* NEW:
* Go through configured list of parameters
*/
{
size_t i;
for (i=0; config_parameters[i].name; i++) {
if (EQUALS(config_parameters[i].name, name)) {
config_parameters[i].set(masscan, name, value);
return;
} else {
size_t j;
for (j=0; config_parameters[i].alts[j]; j++) {
if (EQUALS(config_parameters[i].alts[j], name)) {
config_parameters[i].set(masscan, name, value);
return;
}
}
}
}
}
/*
* OLD:
* Configure the old parameters, the ones we don't have in the new config
* system yet (see the NEW part above).
* TODO: transition all these old params to the new system
*/
if (EQUALS("conf", name) || EQUALS("config", name)) {
masscan_read_config_file(masscan, value);
} else if (EQUALS("adapter", name) || EQUALS("if", name) || EQUALS("interface", name)) {
if (masscan->nic[index].ifname[0]) {
fprintf(stderr, "CONF: overwriting \"adapter=%s\"\n", masscan->nic[index].ifname);
}
if (masscan->nic_count < index + 1)
masscan->nic_count = index + 1;
snprintf( masscan->nic[index].ifname,
sizeof(masscan->nic[index].ifname),
"%s",
value);
}
else if (EQUALS("adapter-ip", name) || EQUALS("source-ip", name)
|| EQUALS("source-address", name) || EQUALS("spoof-ip", name)
|| EQUALS("spoof-address", name) || EQUALS("src-ip", name)) {
/* Send packets FROM this IP address */
struct Range range;
struct Range6 range6;
int err;
/* Grab the next IPv4 or IPv6 range */
err = massip_parse_range(value, 0, 0, &range, &range6);
switch (err) {
case Ipv4_Address:
/* If more than one IP address given, make the range is
* an even power of two (1, 2, 4, 8, 16, ...) */
if (!is_power_of_two((uint64_t)range.end - range.begin + 1)) {
LOG(0, "FAIL: range must be even power of two: %s=%s\n",
name, value);
exit(1);
}
masscan->nic[index].src.ipv4.first = range.begin;
masscan->nic[index].src.ipv4.last = range.end;
masscan->nic[index].src.ipv4.range = range.end - range.begin + 1;
break;
case Ipv6_Address:
masscan->nic[index].src.ipv6.first = range6.begin;
masscan->nic[index].src.ipv6.last = range6.end;
masscan->nic[index].src.ipv6.range = 1; /* TODO: add support for more than one source */
break;
default:
LOG(0, "FAIL: bad source IP address: %s=%s\n",
name, value);
LOG(0, "hint addresses look like \"192.168.1.23\" or \"2001:db8:1::1ce9\".\n");
exit(1);
}
} else if (EQUALS("adapter-port", name) || EQUALS("source-port", name)
|| EQUALS("src-port", name)) {
/* Send packets FROM this port number */
unsigned is_error = 0;
struct RangeList ports = {0};
memset(&ports, 0, sizeof(ports));
rangelist_parse_ports(&ports, value, &is_error, 0);
/* Check if there was an error in parsing */
if (is_error) {
LOG(0, "FAIL: bad source port specification: %s\n",
name);
exit(1);
}
/* Only allow one range of ports */
if (ports.count != 1) {
LOG(0, "FAIL: only one '%s' range may be specified, found %u ranges\n",
name, ports.count);
exit(1);
}
/* verify range is even power of 2 (1, 2, 4, 8, 16, ...) */
if (!is_power_of_two(ports.list[0].end - ports.list[0].begin + 1)) {
LOG(0, "FAIL: source port range must be even power of two: %s=%s\n",
name, value);
exit(1);
}
masscan->nic[index].src.port.first = ports.list[0].begin;
masscan->nic[index].src.port.last = ports.list[0].end;
masscan->nic[index].src.port.range = ports.list[0].end - ports.list[0].begin + 1;
} else if (EQUALS("adapter-mac", name) || EQUALS("spoof-mac", name)
|| EQUALS("source-mac", name) || EQUALS("src-mac", name)) {
/* Send packets FROM this MAC address */
macaddress_t source_mac;
int err;
err = parse_mac_address(value, &source_mac);
if (err) {
LOG(0, "[-] CONF: bad MAC address: %s = %s\n", name, value);
return;
}
/* Check for duplicates */
if (macaddress_is_equal(masscan->nic[index].source_mac, source_mac)) {
/* suppresses warning message about duplicate MAC addresses if
* they are in fact the same */
return;
}
/* Warn if we are overwriting a Mac address */
if (masscan->nic[index].my_mac_count != 0) {
ipaddress_formatted_t fmt1 = macaddress_fmt(masscan->nic[index].source_mac);
ipaddress_formatted_t fmt2 = macaddress_fmt(source_mac);
LOG(0, "[-] WARNING: overwriting MAC address, was %s, now %s\n",
fmt1.string,
fmt2.string);
}
masscan->nic[index].source_mac = source_mac;
masscan->nic[index].my_mac_count = 1;
}
else if (EQUALS("router-mac", name) || EQUALS("router", name)
|| EQUALS("dest-mac", name) || EQUALS("destination-mac", name)
|| EQUALS("dst-mac", name) || EQUALS("target-mac", name)) {
macaddress_t router_mac;
int err;
err = parse_mac_address(value, &router_mac);
if (err) {
fprintf(stderr, "[-] CONF: bad MAC address: %s = %s\n", name, value);
return;
}
masscan->nic[index].router_mac_ipv4 = router_mac;
masscan->nic[index].router_mac_ipv6 = router_mac;
}
else if (EQUALS("router-mac-ipv4", name) || EQUALS("router-ipv4", name)) {
macaddress_t router_mac;
int err;
err = parse_mac_address(value, &router_mac);
if (err) {
fprintf(stderr, "[-] CONF: bad MAC address: %s = %s\n", name, value);
return;
}
masscan->nic[index].router_mac_ipv4 = router_mac;
}
else if (EQUALS("router-mac-ipv6", name) || EQUALS("router-ipv6", name)) {
macaddress_t router_mac;
int err;
err = parse_mac_address(value, &router_mac);
if (err) {
fprintf(stderr, "[-] CONF: bad MAC address: %s = %s\n", name, value);
return;
}
masscan->nic[index].router_mac_ipv6 = router_mac;
}
else if (EQUALS("router-ip", name)) {
/* Send packets FROM this IP address */
struct Range range;
range = range_parse_ipv4(value, 0, 0);
/* Check for bad format */
if (range.begin != range.end) {
LOG(0, "FAIL: bad source IPv4 address: %s=%s\n",
name, value);
LOG(0, "hint addresses look like \"19.168.1.23\"\n");
exit(1);
}
masscan->nic[index].router_ip = range.begin;
}
else if (EQUALS("udp-ports", name) || EQUALS("udp-port", name)) {
unsigned is_error = 0;
masscan->scan_type.udp = 1;
rangelist_parse_ports(&masscan->targets.ports, value, &is_error, Templ_UDP);
if (masscan->op == 0)
masscan->op = Operation_Scan;
}
else if (EQUALS("oprotos", name) || EQUALS("oproto", name)) {
unsigned is_error = 0;
masscan->scan_type.oproto = 1;
rangelist_parse_ports(&masscan->targets.ports, value, &is_error, Templ_Oproto_first);
if (masscan->op == 0)
masscan->op = Operation_Scan;
}
else if (EQUALS("tcp-ports", name) || EQUALS("tcp-port", name)) {
unsigned is_error = 0;
masscan->scan_type.tcp = 1;
rangelist_parse_ports(&masscan->targets.ports, value, &is_error, Templ_TCP);
if (masscan->op == 0)
masscan->op = Operation_Scan;
}
else if (EQUALS("ports", name) || EQUALS("port", name)
|| EQUALS("dst-port", name) || EQUALS("dest-port", name)
|| EQUALS("destination-port", name)
|| EQUALS("target-port", name)) {
unsigned defaultrange = 0;
int err;
if (masscan->scan_type.udp)
defaultrange = Templ_UDP;
else if (masscan->scan_type.sctp)
defaultrange = Templ_SCTP;
err = massip_add_port_string(&masscan->targets, value, defaultrange);
if (err) {
fprintf(stderr, "[-] FAIL: bad target port: %s\n", value);
fprintf(stderr, " Hint: a port is a number [0..65535]\n");
exit(1);
}
if (masscan->op == 0)
masscan->op = Operation_Scan; }
else if (EQUALS("banner-types", name) || EQUALS("banner-type", name)
|| EQUALS("banner-apps", name) || EQUALS("banner-app", name)
) {
enum ApplicationProtocol app;
app = masscan_string_to_app(value);
if (app) {
rangelist_add_range(&masscan->banner_types, app, app);
rangelist_sort(&masscan->banner_types);
} else {
LOG(0, "FAIL: bad banner app: %s\n", value);
fprintf(stderr, "err\n");
exit(1);
}
} else if (EQUALS("exclude-ports", name) || EQUALS("exclude-port", name)) {
unsigned defaultrange = 0;
int err;
if (masscan->scan_type.udp)
defaultrange = Templ_UDP;
else if (masscan->scan_type.sctp)
defaultrange = Templ_SCTP;
err = massip_add_port_string(&masscan->exclude, value, defaultrange);
if (err) {
fprintf(stderr, "[-] FAIL: bad exclude port: %s\n", value);
fprintf(stderr, " Hint: a port is a number [0..65535]\n");
exit(1);
}
if (masscan->op == 0)
masscan->op = Operation_Scan;
} else if (EQUALS("bpf", name)) {
size_t len = strlen(value) + 1;
if (masscan->bpf_filter)
free(masscan->bpf_filter);
masscan->bpf_filter = MALLOC(len);
memcpy(masscan->bpf_filter, value, len);
} else if (EQUALS("ping", name) || EQUALS("ping-sweep", name)) {
/* Add ICMP ping request */
struct Range range;
range.begin = Templ_ICMP_echo;
range.end = Templ_ICMP_echo;
rangelist_add_range(&masscan->targets.ports, range.begin, range.end);
rangelist_sort(&masscan->targets.ports);
masscan->scan_type.ping = 1;
LOG(5, "--ping\n");
} else if (EQUALS("range", name) || EQUALS("ranges", name)
|| EQUALS("ip", name) || EQUALS("ipv4", name)
|| EQUALS("dst-ip", name) || EQUALS("dest-ip", name)
|| EQUALS("destination-ip", name)
|| EQUALS("target-ip", name)) {
int err;
err = massip_add_target_string(&masscan->targets, value);
if (err) {
fprintf(stderr, "ERROR: bad IP address/range: %s\n", value);
}
if (masscan->op == 0)
masscan->op = Operation_Scan;
}
else if (
EQUALS("exclude", name) ||
EQUALS("exclude-range", name) ||
EQUALS("exclude-ranges", name) ||
EQUALS("exclude-ip", name) ||
EQUALS("exclude-ipv4", name)
) {
int err;
err = massip_add_target_string(&masscan->exclude, value);
if (err) {
fprintf(stderr, "ERROR: bad exclude address/range: %s\n", value);
}
if (masscan->op == 0)
masscan->op = Operation_Scan;
} else if (EQUALS("badsum", name)) {
masscan->nmap.badsum = 1;
} else if (EQUALS("banner1", name)) {
banner1_test(value);
exit(1);
} else if (EQUALS("blackrock-rounds", name)) {
masscan->blackrock_rounds = (unsigned)parseInt(value);
} else if (EQUALS("connection-timeout", name) || EQUALS("tcp-timeout", name)) {
/* The timeout for banners TCP connections */
masscan->tcp_connection_timeout = (unsigned)parseInt(value);
} else if (EQUALS("datadir", name)) {
safe_strcpy(masscan->nmap.datadir, sizeof(masscan->nmap.datadir), value);
} else if (EQUALS("data-length", name)) {
unsigned x = (unsigned)strtoul(value, 0, 0);
if (x >= 1514 - 14 - 40) {
fprintf(stderr, "error: %s=: expected number less than 1500\n", name);
} else {
masscan->nmap.data_length = x;
}
} else if (EQUALS("debug", name)) {
if (EQUALS("if", value)) {
masscan->op = Operation_DebugIF;
}
} else if (EQUALS("dns-servers", name)) {
fprintf(stderr, "nmap(%s): unsupported: DNS lookups too synchronous\n",
name);
exit(1);
} else if (EQUALS("echo", name)) {
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;
int err;
const char *filename = value;
LOG(1, "EXCLUDING: %s\n", value);
err = massip_parse_file(&masscan->exclude, filename);
if (err) {
LOG(0, "[-] FAIL: error reading from exclude file\n");
exit(1);
}
/* Detect if this file has made any change, otherwise don't print
* a message */
count2 = masscan->exclude.ipv4.count;
if (count2 - count1)
fprintf(stderr, "%s: excluding %u ranges from file\n",
value, count2 - count1);
} else if (EQUALS("heartbleed", name)) {
masscan->is_heartbleed = 1;
masscan_set_parameter(masscan, "no-capture", "cert");
masscan_set_parameter(masscan, "no-capture", "heartbleed");
masscan_set_parameter(masscan, "banners", "true");
} else if (EQUALS("ticketbleed", name)) {
masscan->is_ticketbleed = 1;
masscan_set_parameter(masscan, "no-capture", "cert");
masscan_set_parameter(masscan, "no-capture", "ticketbleed");
masscan_set_parameter(masscan, "banners", "true");
} else if (EQUALS("host-timeout", name)) {
fprintf(stderr, "nmap(%s): unsupported: this is an asynchronous tool, so no timeouts\n", name);
exit(1);
} else if (EQUALS("iflist", name)) {
masscan->op = Operation_List_Adapters;
} else if (EQUALS("includefile", name)) {
int err;
const char *filename = value;
err = massip_parse_file(&masscan->targets, filename);
if (err) {
LOG(0, "[-] FAIL: error reading from include file\n");
exit(1);
}
if (masscan->op == 0)
masscan->op = Operation_Scan;
} else if (EQUALS("infinite", name)) {
masscan->is_infinite = 1;
} else if (EQUALS("interactive", name)) {
masscan->output.is_interactive = 1;
} else if (EQUALS("nointeractive", name)) {
masscan->output.is_interactive = 0;
} else if (EQUALS("status", name)) {
masscan->output.is_status_updates = 1;
} else if (EQUALS("nostatus", name)) {
masscan->output.is_status_updates = 0;
} else if (EQUALS("ip-options", name)) {
fprintf(stderr, "nmap(%s): unsupported: maybe soon\n", name);
exit(1);
} else if (EQUALS("log-errors", name)) {
fprintf(stderr, "nmap(%s): unsupported: maybe soon\n", name);
exit(1);
} else if (EQUALS("min-hostgroup", name) || EQUALS("max-hostgroup", name)) {
fprintf(stderr, "nmap(%s): unsupported: we randomize all the groups!\n", name);
exit(1);
} else if (EQUALS("min-parallelism", name) || EQUALS("max-parallelism", name)) {
fprintf(stderr, "nmap(%s): unsupported: we all the parallel!\n", name);
exit(1);
} else if (EQUALS("min-rtt-timeout", name) || EQUALS("max-rtt-timeout", name) || EQUALS("initial-rtt-timeout", name)) {
fprintf(stderr, "nmap(%s): unsupported: we are asynchronous, so no timeouts, no RTT tracking!\n", name);
exit(1);
} else if (EQUALS("min-rate", name)) {
fprintf(stderr, "nmap(%s): unsupported, we go as fast as --max-rate allows\n", name);
/* no exit here, since it's just info */
} else if (EQUALS("mtu", name)) {
fprintf(stderr, "nmap(%s): fragmentation not yet supported\n", name);
exit(1);
} else if (EQUALS("nmap", name)) {
print_nmap_help();
exit(1);
} else if (EQUALS("osscan-limit", name)) {
fprintf(stderr, "nmap(%s): OS scanning unsupported\n", name);
exit(1);
} else if (EQUALS("osscan-guess", name)) {
fprintf(stderr, "nmap(%s): OS scanning unsupported\n", name);
exit(1);
} else if (EQUALS("packet-trace", name) || EQUALS("trace-packet", name)) {
masscan->nmap.packet_trace = 1;
} else if (EQUALS("privileged", name) || EQUALS("unprivileged", name)) {
fprintf(stderr, "nmap(%s): unsupported\n", name);
exit(1);
} else if (EQUALS("pfring", name)) {
masscan->is_pfring = 1;
} else if (EQUALS("port-ratio", name)) {
fprintf(stderr, "nmap(%s): unsupported\n", name);
exit(1);
} else if (EQUALS("readrange", name) || EQUALS("readranges", name)) {
masscan->op = Operation_ReadRange;
} else if (EQUALS("reason", name)) {
masscan->output.is_reason = 1;
} else if (EQUALS("redis", name)) {
struct Range range;
unsigned offset = 0;
unsigned max_offset = (unsigned)strlen(value);
unsigned port = 6379;
range = range_parse_ipv4(value, &offset, max_offset);
if ((range.begin == 0 && range.end == 0) || range.begin != range.end) {
LOG(0, "FAIL: bad redis IP address: %s\n", value);
exit(1);
}
if (offset < max_offset) {
while (offset < max_offset && isspace(value[offset]))
offset++;
if (offset+1 < max_offset && value[offset] == ':' && isdigit(value[offset+1]&0xFF)) {
port = (unsigned)strtoul(value+offset+1, 0, 0);
if (port > 65535 || port == 0) {
LOG(0, "FAIL: bad redis port: %s\n", value+offset+1);
exit(1);
}
}
}
/* TODO: add support for connecting to IPv6 addresses here */
masscan->redis.ip.ipv4 = range.begin;
masscan->redis.ip.version = 4;
masscan->redis.port = port;
masscan->output.format = Output_Redis;
safe_strcpy(masscan->output.filename,
sizeof(masscan->output.filename),
"");
} else if(EQUALS("redis-pwd", name)) {
masscan->redis.password = strdup(value);
} else if (EQUALS("release-memory", name)) {
fprintf(stderr, "nmap(%s): this is our default option\n", name);
} else if (EQUALS("resume", name)) {
masscan_read_config_file(masscan, value);
masscan_set_parameter(masscan, "output-append", "true");
} else if (EQUALS("vuln", name)) {
if (EQUALS("heartbleed", value)) {
masscan_set_parameter(masscan, "heartbleed", "true");
return;
} else if (EQUALS("ticketbleed", value)) {
masscan_set_parameter(masscan, "ticketbleed", "true");
return;
} else if (EQUALS("poodle", value) || EQUALS("sslv3", value)) {
masscan->is_poodle_sslv3 = 1;
masscan_set_parameter(masscan, "no-capture", "cert");
masscan_set_parameter(masscan, "banners", "true");
return;
}
if (!vulncheck_lookup(value)) {
fprintf(stderr, "FAIL: vuln check '%s' does not exist\n", value);
fprintf(stderr, " hint: use '--vuln list' to list available scripts\n");
exit(1);
}
if (masscan->vuln_name != NULL) {
if (strcmp(masscan->vuln_name, value) == 0)
return; /* ok */
else {
fprintf(stderr, "FAIL: only one vuln check supported at a time\n");
fprintf(stderr, " hint: '%s' is existing vuln check, '%s' is new vuln check\n",
masscan->vuln_name, value);
exit(1);
}
}
masscan->vuln_name = vulncheck_lookup(value)->name;
} else if (EQUALS("scan-delay", name) || EQUALS("max-scan-delay", name)) {
fprintf(stderr, "nmap(%s): unsupported: we do timing VASTLY differently!\n", name);
exit(1);
} else if (EQUALS("scanflags", name)) {
fprintf(stderr, "nmap(%s): TCP scan flags not yet supported\n", name);
exit(1);
} else if (EQUALS("sendq", name) || EQUALS("sendqueue", name)) {
masscan->is_sendq = 1;
} else if (EQUALS("send-eth", name)) {
fprintf(stderr, "nmap(%s): unnecessary, we always do --send-eth\n", name);
} else if (EQUALS("send-ip", name)) {
fprintf(stderr, "nmap(%s): unsupported, we only do --send-eth\n", name);
exit(1);
} else if (EQUALS("selftest", name) || EQUALS("self-test", name) || EQUALS("regress", name)) {
masscan->op = Operation_Selftest;
return;
} else if (EQUALS("benchmark", name)) {
masscan->op = Operation_Benchmark;
return;
} else if (EQUALS("source-port", name) || EQUALS("sourceport", name)) {
masscan_set_parameter(masscan, "adapter-port", value);
} else if (EQUALS("nobacktrace", name) || EQUALS("backtrace", name)) {
;
} else if (EQUALS("no-stylesheet", name)) {
masscan->output.stylesheet[0] = '\0';
} else if (EQUALS("system-dns", name)) {
fprintf(stderr, "nmap(%s): DNS lookups will never be supported by this code\n", name);
exit(1);
} else if (EQUALS("top-ports", name)) {
unsigned n = (unsigned)parseInt(value);
if (!isInteger(value))
n = 100;
LOG(2, "top-ports = %u\n", n);
masscan->top_ports = n;
} else if (EQUALS("traceroute", name)) {
fprintf(stderr, "nmap(%s): unsupported\n", name);
exit(1);
} else if (EQUALS("test", name)) {
if (EQUALS("csv", value))
masscan->is_test_csv = 1;
} else if (EQUALS("notest", name)) {
if (EQUALS("csv", value))
masscan->is_test_csv = 0;
} else if (EQUALS("ttl", name)) {
unsigned x = (unsigned)strtoul(value, 0, 0);
if (x >= 256) {
fprintf(stderr, "error: %s=: expected number less than 256\n", name);
} else {
masscan->nmap.ttl = x;
}
} else if (EQUALS("version", name)) {
print_version();
exit(1);
} else if (EQUALS("version-intensity", name)) {
fprintf(stderr, "nmap(%s): unsupported\n", name);
exit(1);
} else if (EQUALS("version-light", name)) {
fprintf(stderr, "nmap(%s): unsupported\n", name);
exit(1);
} else if (EQUALS("version-all", name)) {
fprintf(stderr, "nmap(%s): unsupported\n", name);
exit(1);
} else if (EQUALS("version-trace", name)) {
fprintf(stderr, "nmap(%s): unsupported\n", name);
exit(1);
} else if (EQUALS("vlan", name) || EQUALS("adapter-vlan", name)) {
masscan->nic[index].is_vlan = 1;
masscan->nic[index].vlan_id = (unsigned)parseInt(value);
} else if (EQUALS("wait", name)) {
if (EQUALS("forever", value))
masscan->wait = INT_MAX;
else
masscan->wait = (unsigned)parseInt(value);
} else if (EQUALS("webxml", name)) {
masscan_set_parameter(masscan, "stylesheet", "http://nmap.org/svn/docs/nmap.xsl");
} else {
fprintf(stderr, "CONF: unknown config option: %s=%s\n", name, value);
exit(1);
}
}
static bool
is_numable(const char *name) {
size_t i;
for (i=0; config_parameters[i].name; i++) {
if (EQUALS(config_parameters[i].name, name)) {
return (config_parameters[i].flags & F_NUMABLE) == F_NUMABLE;
} else {
size_t j;
for (j=0; config_parameters[i].alts[j]; j++) {
if (EQUALS(config_parameters[i].alts[j], name)) {
return (config_parameters[i].flags & F_NUMABLE) == F_NUMABLE;
}
}
}
}
return false;
}
/***************************************************************************
* Command-line parsing code assumes every --parm is followed by a value.
* This is a list of the parameters that don't follow the default.
***************************************************************************/
static int
is_singleton(const char *name)
{
static const char *singletons[] = {
"echo", "echo-all", "echo-cidr", "selftest", "self-test", "regress",
"benchmark",
"system-dns", "traceroute", "version",
"version-light",
"version-all", "version-trace",
"osscan-limit", "osscan-guess",
"badsum", "reason", "open", "open-only",
"packet-trace", "release-memory",
"log-errors", "append-output", "webxml",
"no-stylesheet", "heartbleed", "ticketbleed",
"send-eth", "send-ip", "iflist",
"nmap", "trace-packet", "pfring", "sendq",
"ping", "ping-sweep", "nobacktrace", "backtrace",
"infinite", "nointeractive", "interactive", "status", "nostatus",
"read-range", "read-ranges", "readrange", "read-ranges",
0};
size_t i;
for (i=0; singletons[i]; i++) {
if (EQUALS(singletons[i], name))
return 1;
}
for (i=0; config_parameters[i].name; i++) {
if (EQUALS(config_parameters[i].name, name)) {
return (config_parameters[i].flags & F_BOOL) == F_BOOL;
} else {
size_t j;
for (j=0; config_parameters[i].alts[j]; j++) {
if (EQUALS(config_parameters[i].alts[j], name)) {
return (config_parameters[i].flags & F_BOOL) == F_BOOL;
}
}
}
}
return 0;
}
/*****************************************************************************
*****************************************************************************/
static void
masscan_help()
{
printf(
"usage: masscan [options] [... -pPORT[,PORT...]]\n"
"MASSCAN is a fast port scanner. The primary input parameters are the\n"
"IP addresses/ranges you want to scan, and the port numbers. An example\n"
"is the following, which scans the 10.x.x.x network for web servers:\n"
"\n"
" masscan 10.0.0.0/8 -p80\n"
"\n"
"The program auto-detects network interface/adapter settings. If this\n"
"fails, you'll have to set these manually. The following is an\n"
"example of all the parameters that are needed:\n"
"\n"
" --adapter-ip 192.168.10.123\n"
" --adapter-mac 00-11-22-33-44-55\n"
" --router-mac 66-55-44-33-22-11\n"
"\n"
"Parameters can be set either via the command-line or config-file. The\n"
"names are the same for both. Thus, the above adapter settings would\n"
"appear as follows in a configuration file:\n"
"\n"
" adapter-ip = 192.168.10.123\n"
" adapter-mac = 00-11-22-33-44-55\n"
" router-mac = 66-55-44-33-22-11\n"
"\n"
"All single-dash parameters have a spelled out double-dash equivalent,\n"
"so '-p80' is the same as '--ports 80' (or 'ports = 80' in config file).\n"
"To use the config file, type:\n"
"\n"
" masscan -c \n"
"\n"
"To generate a config-file from the current settings, use the --echo\n"
"option. This stops the program from actually running, and just echoes\n"
"the current configuration instead. This is a useful way to generate\n"
"your first config file, or see a list of parameters you didn't know\n"
"about. I suggest you try it now:\n"
"\n"
" masscan -p1234 --echo\n"
"\n");
exit(1);
}
/***************************************************************************
***************************************************************************/
void
masscan_load_database_files(struct Masscan *masscan)
{
const char *filename;
/*
* "pcap-payloads"
*/
filename = masscan->payloads.pcap_payloads_filename;
if (filename) {
if (masscan->payloads.udp == NULL)
masscan->payloads.udp = payloads_udp_create();
if (masscan->payloads.oproto == NULL)
masscan->payloads.oproto = payloads_udp_create();
payloads_read_pcap(filename, masscan->payloads.udp, masscan->payloads.oproto);
}
/*
* `--nmap-payloads`
*/
filename = masscan->payloads.nmap_payloads_filename;
if (filename) {
FILE *fp;
fp = fopen(filename, "rt");
if (fp == NULL) {
fprintf(stderr, "[-] FAIL: --nmap-payloads\n");
fprintf(stderr, "[-] %s:%s\n", filename, strerror(errno));
} else {
if (masscan->payloads.udp == NULL)
masscan->payloads.udp = payloads_udp_create();
payloads_udp_readfile(fp, filename, masscan->payloads.udp);
fclose(fp);
}
}
/*
* "nmap-service-probes"
*/
filename = masscan->payloads.nmap_service_probes_filename;
if (filename) {
if (masscan->payloads.probes)
nmapserviceprobes_free(masscan->payloads.probes);
masscan->payloads.probes = nmapserviceprobes_read_file(filename);
}
}
/***************************************************************************
* Read the configuration from the command-line.
* Called by 'main()' when starting up.
***************************************************************************/
void
masscan_command_line(struct Masscan *masscan, int argc, char *argv[])
{
int i;
for (i=1; i sizeof(name2) - 1) {
fprintf(stderr, "%.*s: name too long\n", name_length, name);
name_length = sizeof(name2) - 1;
}
memcpy(name2, name, name_length);
name2[name_length] = '\0';
masscan_set_parameter(masscan, name2, value);
} else if (EQUALS("readscan", argv[i]+2)) {
/* Read in a binary file instead of scanning the network*/
masscan->op = Operation_ReadScan;
/* Default to reading banners */
masscan->is_banners = true;
masscan->is_banners_rawudp = true;
/* This option may be followed by many filenames, therefore,
* skip forward in the argument list until the next
* argument */
while (i+1 < argc && argv[i+1][0] != '-')
i++;
continue;
} else {
char name2[64];
char *name = argv[i] + 2;
unsigned name_length;
const char *value;
value = strchr(&argv[i][2], '=');
if (value == NULL)
value = strchr(&argv[i][2], ':');
if (value == NULL) {
if (is_singleton(name))
value = "";
else
value = argv[++i];
name_length = (unsigned)strlen(name);
} else {
name_length = (unsigned)(value - name);
value++;
}
if (i >= argc) {
fprintf(stderr, "%.*s: empty parameter\n", name_length, name);
break;
}
if (name_length > sizeof(name2) - 1) {
fprintf(stderr, "%.*s: name too long\n", name_length, name);
name_length = sizeof(name2) - 1;
}
memcpy(name2, name, name_length);
name2[name_length] = '\0';
masscan_set_parameter(masscan, name2, value);
}
continue;
}
/* For for a single-dash parameter */
if (argv[i][0] == '-') {
const char *arg;
switch (argv[i][1]) {
case '6':
/* Silently ignore this: IPv6 features enabled all the time */
break;
case 'A':
fprintf(stderr, "nmap(%s): unsupported: this tool only does SYN scan\n", argv[i]);
exit(1);
case 'b':
fprintf(stderr, "nmap(%s): FTP bounce scans will never be supported\n", argv[i]);
exit(1);
case 'c':
if (argv[i][2])
arg = argv[i]+2;
else
arg = argv[++i];
masscan_read_config_file(masscan, arg);
break;
case 'd': /* just do same as verbosity level */
{
int v;
for (v=1; argv[i][v] == 'd'; v++) {
LOG_add_level(1);
}
}
break;
case 'e':
if (argv[i][2])
arg = argv[i]+2;
else
arg = argv[++i];
masscan_set_parameter(masscan, "adapter", arg);
break;
case 'f':
fprintf(stderr, "nmap(%s): fragmentation not yet supported\n", argv[i]);
exit(1);
case 'F':
fprintf(stderr, "nmap(%s): unsupported, no slow/fast mode\n", argv[i]);
exit(1);
case 'g':
if (argv[i][2])
arg = argv[i]+2;
else
arg = argv[++i];
masscan_set_parameter(masscan, "adapter-port", arg);
break;
case 'h':
case '?':
masscan_usage();
break;
case 'i':
if (argv[i][3] == '\0' && !isdigit(argv[i][2]&0xFF)) {
/* This looks like an nmap option*/
switch (argv[i][2]) {
case 'L':
masscan_set_parameter(masscan, "includefile", argv[++i]);
break;
case 'R':
/* -iR in nmap makes it randomize addresses completely. Thus,
* it's nearest equivalent is scanning the entire Internet range */
masscan_set_parameter(masscan, "include", "0.0.0.0/0");
break;
default:
fprintf(stderr, "nmap(%s): unsupported option\n", argv[i]);
exit(1);
}
} else {
if (argv[i][2])
arg = argv[i]+2;
else
arg = argv[++i];
masscan_set_parameter(masscan, "adapter", arg);
}
break;
case 'n':
/* This looks like an nmap option*/
/* Do nothing: this code never does DNS lookups anyway */
break;
case 'o': /* nmap output format */
switch (argv[i][2]) {
case 'A':
masscan->output.format = Output_All;
fprintf(stderr, "nmap(%s): unsupported output format\n", argv[i]);
exit(1);
break;
case 'B':
masscan->output.format = Output_Binary;
break;
case 'D':
masscan->output.format = Output_NDJSON;
break;
case 'J':
masscan->output.format = Output_JSON;
break;
case 'N':
masscan->output.format = Output_Nmap;
fprintf(stderr, "nmap(%s): unsupported output format\n", argv[i]);
exit(1);
break;
case 'X':
masscan->output.format = Output_XML;
break;
case 'R':
masscan->output.format = Output_Redis;
if (i+1 < argc && argv[i+1][0] != '-')
masscan_set_parameter(masscan, "redis", argv[i+1]);
break;
case 'S':
masscan->output.format = Output_ScriptKiddie;
fprintf(stderr, "nmap(%s): unsupported output format\n", argv[i]);
exit(1);
break;
case 'G':
masscan->output.format = Output_Grepable;
break;
case 'L':
masscan_set_parameter(masscan, "output-format", "list");
break;
case 'U':
masscan_set_parameter(masscan, "output-format", "unicornscan");
break;
case 'H':
masscan_set_parameter(masscan, "output-format", "hostonly");
break;
default:
fprintf(stderr, "nmap(%s): unknown output format\n", argv[i]);
exit(1);
}
++i;
if (i >= argc || (argv[i][0] == '-' && argv[i][1] != '\0')) {
fprintf(stderr, "missing output filename\n");
exit(1);
}
masscan_set_parameter(masscan, "output-filename", argv[i]);
break;
case 'O':
fprintf(stderr, "nmap(%s): unsupported, OS detection is too complex\n", argv[i]);
exit(1);
break;
case 'p':
if (argv[i][2])
arg = argv[i]+2;
else
arg = argv[++i];
if (i >= argc || arg[0] == 0) { // if string is empty
fprintf(stderr, "%s: empty parameter\n", argv[i]);
} else
masscan_set_parameter(masscan, "ports", arg);
break;
case 'P':
switch (argv[i][2]) {
case 'n':
/* we already do this */
break;
default:
fprintf(stderr, "nmap(%s): unsupported option, maybe in future\n", argv[i]);
exit(1);
}
break;
case 'r':
/* This looks like an nmap option*/
fprintf(stderr, "nmap(%s): wat? randomization is our raison d'etre!! rethink prease\n", argv[i]);
exit(1);
break;
case 'R':
/* This looks like an nmap option*/
fprintf(stderr, "nmap(%s): unsupported. This code will never do DNS lookups.\n", argv[i]);
exit(1);
break;
case 's': /* NMAP: scan type */
if (argv[i][3] == '\0' && !isdigit(argv[i][2]&0xFF)) {
unsigned j;
for (j=2; argv[i][j]; j++)
switch (argv[i][j]) {
case 'A':
fprintf(stderr, "nmap(%s): ACK scan not yet supported\n", argv[i]);
exit(1);
case 'C':
fprintf(stderr, "nmap(%s): unsupported\n", argv[i]);
exit(1);
case 'F':
fprintf(stderr, "nmap(%s): FIN scan not yet supported\n", argv[i]);
exit(1);
case 'I':
fprintf(stderr, "nmap(%s): Zombie scans will never be supported\n", argv[i]);
exit(1);
case 'L': /* List Scan - simply list targets to scan */
masscan->op = Operation_ListScan;
break;
case 'M':
fprintf(stderr, "nmap(%s): Maimon scan not yet supported\n", argv[i]);
exit(1);
case 'n': /* Ping Scan - disable port scan */
fprintf(stderr, "nmap(%s): ping-sweeps not yet supported\n", argv[i]);
exit(1);
case 'N':
fprintf(stderr, "nmap(%s): NULL scan not yet supported\n", argv[i]);
exit(1);
case 'O': /* Other IP protocols (not ICMP, UDP, TCP, or SCTP) */
masscan->scan_type.oproto = 1;
break;
case 'S': /* TCP SYN scan - THIS IS WHAT WE DO! */
masscan->scan_type.tcp = 1;
break;
case 'T': /* TCP connect scan */
fprintf(stderr, "nmap(%s): connect() is too synchronous for cool kids\n", argv[i]);
fprintf(stderr, "WARNING: doing SYN scan (-sS) anyway, ignoring (-sT)\n");
break;
case 'U': /* UDP scan */
masscan->scan_type.udp = 1;
break;
case 'V':
fprintf(stderr, "nmap(%s): unlikely this will be supported\n", argv[i]);
exit(1);
case 'W':
fprintf(stderr, "nmap(%s): Windows scan not yet supported\n", argv[i]);
exit(1);
case 'X':
fprintf(stderr, "nmap(%s): Xmas scan not yet supported\n", argv[i]);
exit(1);
case 'Y':
break;
case 'Z':
masscan->scan_type.sctp = 1;
break;
default:
fprintf(stderr, "nmap(%s): unsupported option\n", argv[i]);
exit(1);
}
} else {
fprintf(stderr, "%s: unknown parameter\n", argv[i]);
exit(1);
}
break;
case 'S':
if (argv[i][2])
arg = argv[i]+2;
else
arg = argv[++i];
masscan_set_parameter(masscan, "adapter-ip", arg);
break;
case 'v':
{
int v;
for (v=1; argv[i][v] == 'v'; v++)
LOG_add_level(1);
}
break;
case 'V': /* print version and exit */
masscan_set_parameter(masscan, "version", "");
break;
case 'W':
masscan->op = Operation_List_Adapters;
return;
case 'T':
fprintf(stderr, "nmap(%s): unsupported, we do timing WAY different than nmap\n", argv[i]);
exit(1);
return;
default:
LOG(0, "FAIL: unknown option: -%s\n", argv[i]);
LOG(0, " [hint] try \"--help\"\n");
LOG(0, " [hint] ...or, to list nmap-compatible options, try \"--nmap\"\n");
exit(1);
}
continue;
}
if (!isdigit(argv[i][0]) && argv[i][0] != ':' && argv[i][0] != '[') {
fprintf(stderr, "FAIL: unknown command-line parameter \"%s\"\n", argv[i]);
fprintf(stderr, " [hint] did you want \"--%s\"?\n", argv[i]);
exit(1);
}
/* If parameter doesn't start with '-', assume it's an
* IPv4 range
*/
masscan_set_parameter(masscan, "range", argv[i]);
}
/*
* If no other "scan type" found, then default to TCP
*/
if (masscan->scan_type.udp == 0 && masscan->scan_type.sctp == 0
&& masscan->scan_type.ping == 0 && masscan->scan_type.arp == 0
&& masscan->scan_type.oproto == 0)
masscan->scan_type.tcp = 1;
/*
* If "top-ports" specified, then add all those ports. This may be in
* addition to any other ports
*/
if (masscan->top_ports) {
config_top_ports(masscan, masscan->top_ports);
}
if (masscan->shard.of < masscan->shard.one) {
fprintf(stderr, "[-] WARNING: the shard number must be less than the total shard count: %u/%u\n",
masscan->shard.one, masscan->shard.of);
}
if (masscan->shard.of > 1 && masscan->seed == 0) {
fprintf(stderr, "[-] WARNING: --seed is not specified\n HINT: all shards must share the same seed\n");
}
}
/***************************************************************************
* Prints the current configuration to the command-line then exits.
* Use#1: create a template file of all settable parameters.
* Use#2: make sure your configuration was interpreted correctly.
***************************************************************************/
void
masscan_echo(struct Masscan *masscan, FILE *fp, unsigned is_echo_all)
{
unsigned i;
unsigned l = 0;
/*
* NEW:
* Print all configuration parameters
*/
masscan->echo = fp;
masscan->echo_all = is_echo_all;
for (i=0; config_parameters[i].name; i++) {
config_parameters[i].set(masscan, 0, 0);
}
masscan->echo = 0;
masscan->echo_all = 0;
/*
* OLD:
* Things here below are the old way of echoing parameters.
* TODO: cleanup this code, replacing with the new way.
*/
if (masscan->nic_count == 0)
masscan_echo_nic(masscan, fp, 0);
else {
for (i=0; inic_count; i++)
masscan_echo_nic(masscan, fp, i);
}
/**
* Fix for #737, save adapter-port/source-port value or range
*/
if (masscan->nic[0].src.port.first != 0) {
fprintf(fp, "adapter-port = %d", masscan->nic[0].src.port.first);
if (masscan->nic[0].src.port.first != masscan->nic[0].src.port.last) {
/* --adapter-port - */
fprintf(fp, "-%d", masscan->nic[0].src.port.last);
}
fprintf(fp, "\n");
}
/*
* Targets
*/
fprintf(fp, "# TARGET SELECTION (IP, PORTS, EXCLUDES)\n");
fprintf(fp, "ports = ");
/* Disable comma generation for the first element */
l = 0;
for (i=0; itargets.ports.count; i++) {
struct Range range = masscan->targets.ports.list[i];
do {
struct Range rrange = range;
unsigned done = 0;
if (l)
fprintf(fp, ",");
l = 1;
if (rrange.begin >= Templ_ICMP_echo) {
rrange.begin -= Templ_ICMP_echo;
rrange.end -= Templ_ICMP_echo;
fprintf(fp,"I:");
done = 1;
} else if (rrange.begin >= Templ_SCTP) {
rrange.begin -= Templ_SCTP;
rrange.end -= Templ_SCTP;
fprintf(fp,"S:");
range.begin = Templ_ICMP_echo;
} else if (rrange.begin >= Templ_UDP) {
rrange.begin -= Templ_UDP;
rrange.end -= Templ_UDP;
fprintf(fp,"U:");
range.begin = Templ_SCTP;
} else if (Templ_Oproto_first <= rrange.begin && rrange.begin <= Templ_Oproto_last) {
rrange.begin -= Templ_Oproto_first;
rrange.end -= Templ_Oproto_first;
fprintf(fp, "O:");
range.begin = Templ_Oproto_first;
} else
range.begin = Templ_UDP;
rrange.end = min(rrange.end, 65535);
if (rrange.begin == rrange.end)
fprintf(fp, "%u", rrange.begin);
else
fprintf(fp, "%u-%u", rrange.begin, rrange.end);
if (done)
break;
} while (range.begin <= range.end);
}
fprintf(fp, "\n");
/*
* IPv4 address targets
*/
for (i=0; itargets.ipv4.count; i++) {
unsigned prefix_bits;
struct Range range = masscan->targets.ipv4.list[i];
if (range.begin == range.end) {
fprintf(fp, "range = %u.%u.%u.%u",
(range.begin>>24)&0xFF,
(range.begin>>16)&0xFF,
(range.begin>> 8)&0xFF,
(range.begin>> 0)&0xFF
);
} else if (range_is_cidr(range, &prefix_bits)) {
fprintf(fp, "range = %u.%u.%u.%u/%u",
(range.begin>>24)&0xFF,
(range.begin>>16)&0xFF,
(range.begin>> 8)&0xFF,
(range.begin>> 0)&0xFF,
prefix_bits
);
} else {
fprintf(fp, "range = %u.%u.%u.%u-%u.%u.%u.%u",
(range.begin>>24)&0xFF,
(range.begin>>16)&0xFF,
(range.begin>> 8)&0xFF,
(range.begin>> 0)&0xFF,
(range.end>>24)&0xFF,
(range.end>>16)&0xFF,
(range.end>> 8)&0xFF,
(range.end>> 0)&0xFF
);
}
fprintf(fp, "\n");
}
for (i=0; itargets.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, &exact);
if (exact && cidr_bits) {
fprintf(fp, "/%u", cidr_bits);
} else {
fmt = ipv6address_fmt(range.end);
fprintf(fp, "-%s", fmt.string);
}
}
fprintf(fp, "\n");
}
}
/***************************************************************************
* 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.
* For example, given the starting range of [10.0.0.1-10.0.0.255], this will
* print all the CIDR ranges that make this up:
* 10.0.0.1/32
* 10.0.0.2/31
* 10.0.0.4/30
* 10.0.0.8/29
* 10.0.0.16/28
* 10.0.0.32/27
* 10.0.0.64/26
* 10.0.0.128/25
***************************************************************************/
void
masscan_echo_cidr(struct Masscan *masscan, FILE *fp, unsigned is_echo_all)
{
unsigned i;
UNUSEDPARM(is_echo_all);
masscan->echo = fp;
/*
* For all IPv4 ranges ...
*/
for (i=0; itargets.ipv4.count; i++) {
/* Get the next range in the list */
struct Range range = masscan->targets.ipv4.list[i];
/* If not a single CIDR range, print all the CIDR ranges
* needed to completely represent this addres */
for (;;) {
unsigned prefix_length;
struct Range cidr;
/* Find the largest CIDR range (one that can be specified
* with a /prefix) at the start of this range. */
cidr = range_first_cidr(range, &prefix_length);
fprintf(fp, "%u.%u.%u.%u/%u\n",
(cidr.begin>>24)&0xFF,
(cidr.begin>>16)&0xFF,
(cidr.begin>> 8)&0xFF,
(cidr.begin>> 0)&0xFF,
prefix_length
);
/* If this is the last range, then stop. There are multiple
* ways to gets to see if we get to the end, but I think
* this is the best. */
if (cidr.end >= range.end)
break;
/* If the CIDR range didn't cover the entire range,
* then remove it from the beginning of the range
* and process the remainder */
range.begin = cidr.end+1;
}
}
/*
* For all IPv6 ranges...
*/
for (i=0; itargets.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
***************************************************************************/
static void
trim(char *line, size_t sizeof_line)
{
if (sizeof_line > strlen(line))
sizeof_line = strlen(line);
while (isspace(*line & 0xFF))
memmove(line, line+1, sizeof_line--);
while (*line && isspace(line[sizeof_line-1] & 0xFF))
line[--sizeof_line] = '\0';
}
/***************************************************************************
***************************************************************************/
void
masscan_read_config_file(struct Masscan *masscan, const char *filename)
{
FILE *fp;
char line[65536];
fp = fopen(filename, "rt");
if (fp == NULL) {
char dir[512];
char *x;
fprintf(stderr, "[-] FAIL: reading configuration file\n");
fprintf(stderr, "[-] %s: %s\n", filename, strerror(errno));
x = getcwd(dir, sizeof(dir));
if (x)
fprintf(stderr, "[-] cwd = %s\n", dir);
return;
}
while (fgets(line, sizeof(line), fp)) {
char *name;
char *value;
trim(line, sizeof(line));
if (ispunct(line[0] & 0xFF) || line[0] == '\0')
continue;
name = line;
value = strchr(line, '=');
if (value == NULL)
continue;
*value = '\0';
value++;
trim(name, sizeof(line));
trim(value, sizeof(line));
masscan_set_parameter(masscan, name, value);
}
fclose(fp);
}
/***************************************************************************
***************************************************************************/
int masscan_conf_contains(const char *x, int argc, char **argv)
{
int i;
for (i=0; i
#include
#include
#include "syn-cookie.h"
/**
* This is the number of entries in our table. More entries does a better job at the
* cost of using more memory.
*/
#define DEDUP_ENTRIES 65536
struct DedupEntry_IPv4
{
unsigned ip_them;
unsigned port_them;
unsigned ip_me;
unsigned port_me;
};
struct DedupEntry_IPv6
{
ipv6address ip_them;
ipv6address ip_me;
unsigned short port_them;
unsigned short port_me;
};
/**
* This is simply the array of entries. We have two arrays, one for IPv4
* and another for IPv6.
*/
struct DedupTable
{
struct DedupEntry_IPv4 entries[DEDUP_ENTRIES][4];
struct DedupEntry_IPv6 entries6[DEDUP_ENTRIES][4];
};
/**
* We use the FNv1a hash algorithm, which starts with this seed value.
*/
const unsigned fnv1a_seed = 0x811C9DC5; /* 2166136261 */
/**
* Hash one byte, the other hash functions of multiple bytes call this function.
* @param hash
* The current hash value that we keep updating as we repeatedly
* call this function, or the `fnv1a_seed value on the first call to
* this function.
*/
static inline unsigned fnv1a(unsigned char c, unsigned hash)
{
const unsigned prime = 0x01000193; /* 16777619 */
return (c ^ hash) * prime;
}
static unsigned fnv1a_string(const void *v_buf, size_t length, unsigned hash)
{
const unsigned char *buf = (const unsigned char *)v_buf;
size_t i;
for (i=0; i>0)&0xFF, hash);
hash = fnv1a((data>>8)&0xFF, hash);
return hash;
}
static inline unsigned fnv1a_longlong(unsigned long long data, unsigned hash)
{
return fnv1a_string(&data, 8, hash);
}
/**
* Create a new table, which means simply allocating the object
* and setting it to zero.
*/
struct DedupTable *
dedup_create(void)
{
struct DedupTable *dedup;
dedup = CALLOC(1, sizeof(*dedup));
return dedup;
}
/**
* There's nothing special we need to do to free the structure
* since it's all contained in the single allocation.
*/
void
dedup_destroy(struct DedupTable *dedup)
{
free(dedup);
}
/**
* Create a hash of the IPv6 socket. This doesn't have to be
* cryptographically secure, so we are going to use the FNv1a algorithm.
*/
static inline unsigned
dedup_hash_ipv6(ipaddress ip_them, unsigned port_them, ipaddress ip_me, unsigned port_me)
{
unsigned hash = fnv1a_seed;
hash = fnv1a_longlong(ip_them.ipv6.hi, hash);
hash = fnv1a_longlong(ip_them.ipv6.lo, hash);
hash = fnv1a_short(port_them, hash);
hash = fnv1a_longlong(ip_me.ipv6.hi, hash);
hash = fnv1a_longlong(ip_me.ipv6.lo, hash);
hash = fnv1a_short(port_me, hash);
return hash;
}
/**
* If two IPv6 addresses are equal.
*/
static inline int
is_equal6(ipv6address lhs, ipv6address rhs)
{
return lhs.hi == rhs.hi && lhs.lo == rhs.lo;
}
/**
* Swap two addresses in the table. This uses the classic XOR trick
* rather than using a swap variable.
*/
static inline void
swap6(struct DedupEntry_IPv6 *lhs, struct DedupEntry_IPv6 *rhs)
{
lhs->ip_them.hi ^= rhs->ip_them.hi;
lhs->ip_them.lo ^= rhs->ip_them.lo;
lhs->port_them ^= rhs->port_them;
lhs->ip_me.hi ^= rhs->ip_me.hi;
lhs->ip_me.lo ^= rhs->ip_me.lo;
lhs->port_me ^= rhs->port_me;
rhs->ip_them.hi ^= lhs->ip_them.hi;
rhs->ip_them.lo ^= lhs->ip_them.lo;
rhs->port_them ^= lhs->port_them;
rhs->ip_me.hi ^= lhs->ip_me.hi;
rhs->ip_me.lo ^= lhs->ip_me.lo;
rhs->port_me ^= lhs->port_me;
lhs->ip_them.hi ^= rhs->ip_them.hi;
lhs->ip_them.lo ^= rhs->ip_them.lo;
lhs->port_them ^= rhs->port_them;
lhs->ip_me.hi ^= rhs->ip_me.hi;
lhs->ip_me.lo ^= rhs->ip_me.lo;
lhs->port_me ^= rhs->port_me;
}
/**
* This implements the same algorithm as for IPv4 addresses, but for
* IPv6 addresses instead.
*/
static unsigned
dedup_is_duplicate_ipv6(struct DedupTable *dedup,
ipaddress ip_them, unsigned port_them,
ipaddress ip_me, unsigned port_me)
{
unsigned hash;
struct DedupEntry_IPv6 *bucket;
unsigned i;
/* THREAT: probably need to secure this hash, though the syn-cookies
* provides some protection */
hash = dedup_hash_ipv6(ip_them, port_them, ip_me, port_me);
hash &= DEDUP_ENTRIES-1;
/* Search in this bucket */
bucket = dedup->entries6[hash];
/* If we find the entry in our table, move it to the front, so
* that it won't be aged out as quickly. We keep prepending new
* addresses to front, aging older addresses that haven't been
* seen in a while. */
for (i = 0; i < 4; i++) {
if (is_equal6(bucket[i].ip_them, ip_them.ipv6) && bucket[i].port_them == port_them
&& is_equal6(bucket[i].ip_me, ip_me.ipv6) && bucket[i].port_me == port_me) {
/* move to end of list so constant repeats get ignored */
if (i > 0) {
swap6(&bucket[0], &bucket[i]);
}
return 1;
}
}
/* We didn't find it, so add it to our list. This will push
* older entries at this bucket off the list */
memmove(bucket, bucket+1, 3*sizeof(*bucket));
bucket[0].ip_them.hi = ip_them.ipv6.hi;
bucket[0].ip_them.lo = ip_them.ipv6.lo;
bucket[0].port_them = (unsigned short)port_them;
bucket[0].ip_me.hi = ip_me.ipv6.hi;
bucket[0].ip_me.lo = ip_me.ipv6.lo;
bucket[0].port_me = (unsigned short)port_me;
return 0;
}
/***************************************************************************
***************************************************************************/
static unsigned
dedup_is_duplicate_ipv4(struct DedupTable *dedup,
ipaddress ip_them, unsigned port_them,
ipaddress ip_me, unsigned port_me)
{
unsigned hash;
struct DedupEntry_IPv4 *bucket;
unsigned i;
/* THREAT: probably need to secure this hash, though the syn-cookies
* provides some protection */
hash = (ip_them.ipv4 + port_them) ^ ((ip_me.ipv4) + (ip_them.ipv4>>16)) ^ (ip_them.ipv4>>24) ^ port_me;
hash &= DEDUP_ENTRIES-1;
/* Search in this bucket */
bucket = dedup->entries[hash];
/* If we find the entry in our table, move it to the front, so
* that it won't be aged out as quickly. We keep prepending new
* addresses to front, aging older addresses that haven't been
* seen in a while. */
for (i = 0; i < 4; i++) {
if (bucket[i].ip_them == ip_them.ipv4 && bucket[i].port_them == port_them
&& bucket[i].ip_me == ip_me.ipv4 && bucket[i].port_me == port_me) {
/* move to end of list so constant repeats get ignored */
if (i > 0) {
bucket[i].ip_them ^= bucket[0].ip_them;
bucket[i].port_them ^= bucket[0].port_them;
bucket[i].ip_me ^= bucket[0].ip_me;
bucket[i].port_me ^= bucket[0].port_me;
bucket[0].ip_them ^= bucket[i].ip_them;
bucket[0].port_them ^= bucket[i].port_them;
bucket[0].ip_me ^= bucket[i].ip_me;
bucket[0].port_me ^= bucket[i].port_me;
bucket[i].ip_them ^= bucket[0].ip_them;
bucket[i].port_them ^= bucket[0].port_them;
bucket[i].ip_me ^= bucket[0].ip_me;
bucket[i].port_me ^= bucket[0].port_me;
}
return 1;
}
}
/* We didn't find it, so add it to our list. This will push
* older entries at this bucket off the list */
memmove(bucket, bucket+1, 3*sizeof(*bucket));
bucket[0].ip_them = ip_them.ipv4;
bucket[0].port_them = port_them;
bucket[0].ip_me = ip_me.ipv4;
bucket[0].port_me = port_me;
return 0;
}
/***************************************************************************
***************************************************************************/
unsigned
dedup_is_duplicate(struct DedupTable *dedup,
ipaddress ip_them, unsigned port_them,
ipaddress ip_me, unsigned port_me)
{
if (ip_them.version == 6)
return dedup_is_duplicate_ipv6(dedup, ip_them, port_them, ip_me, port_me);
else
return dedup_is_duplicate_ipv4(dedup, ip_them, port_them, ip_me, port_me);
}
/**
* My own deterministic rand() function for testing this module
*/
static unsigned
_rand(unsigned *seed)
{
static const unsigned a = 214013;
static const unsigned c = 2531011;
*seed = (*seed) * a + c;
return (*seed)>>16 & 0x7fff;
}
/*
* Provide a simple unit test for this module.
*
* This is a pretty lame test. I'm going to generate
* a set of random addresses, tweaked so that they aren't
* too random, so that I get around 30 to 50 expected
* duplicates. If I get zero duplicates, or if I get too
* many duplicates in the test, then I know it's failed.
*
* This is in no way a reliable test that deterministically
* tests the functionality. It's a crappy non-deterministic
* test.
*
* We also do a simple deterministic test, but this still
* is insufficient testing how duplicates age out and such.
*/
int
dedup_selftest(void)
{
struct DedupTable *dedup;
unsigned seed = 0;
size_t i;
unsigned found_match = 0;
unsigned line = 0;
dedup = dedup_create();
/* Deterministic test.
*
* The first time we check on a socket combo, there should
* be no duplicate. The second time we check, however, there should
* be a duplicate.
*/
{
ipaddress ip_me;
ipaddress ip_them;
unsigned port_me;
unsigned port_them;
ip_me.version = 4;
ip_them.version = 4;
ip_me.ipv4 = 0x12345678;
ip_them.ipv4 = 0xabcdef0;
port_me = 0x1234;
port_them = 0xfedc;
if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) {
line = __LINE__;
goto fail;
}
if (!dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) {
line = __LINE__;
goto fail;
}
ip_me.version = 6;
ip_them.version = 6;
ip_me.ipv6.hi = 0x12345678;
ip_me.ipv6.lo = 0x12345678;
ip_them.ipv6.hi = 0xabcdef0;
ip_them.ipv6.lo = 0xabcdef0;
if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) {
line = __LINE__;
goto fail;
}
if (!dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) {
ipaddress_formatted_t fmt1 = ipaddress_fmt(ip_them);
ipaddress_formatted_t fmt2 = ipaddress_fmt(ip_me);
fprintf(stderr, "[-] [%s]:%u -> [%s]:%u\n",
fmt1.string, port_them,
fmt2.string, port_me);
line = __LINE__;
goto fail;
}
}
/* Test IPv4 addresses */
for (i=0; i<100000; i++) {
ipaddress ip_me;
ipaddress ip_them;
unsigned port_me;
unsigned port_them;
ip_me.version = 4;
ip_them.version = 4;
/* Instead of completely random numbers over the entire
* range, each port/IP is restricted to just 512
* random combinations. This should statistically
* give us around 10 matches*/
ip_me.ipv4 = _rand(&seed) & 0xFF800000;
ip_them.ipv4 = _rand(&seed) & 0x1FF;
port_me = _rand(&seed) & 0xFF80;
port_them = _rand(&seed) & 0x1FF;
if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) {
found_match++;
}
}
/* Approximately 30 matches should be found. If we couldn't
* find any, or if we've found too many, then the test has
* failed. */
if (found_match == 0 || found_match > 200) {
line = __LINE__;
goto fail;
}
/* Now do IPv6 */
found_match = 0;
seed = 0;
/* Test IPv4 addresses */
for (i=0; i<100000; i++) {
ipaddress ip_me;
ipaddress ip_them;
unsigned port_me;
unsigned port_them;
ip_me.version = 6;
ip_them.version = 6;
/* Instead of completely random numbers over the entire
* range, each port/IP is restricted to just 512
* random combinations. This should statistically
* give us around 10 matches*/
ip_me.ipv6.hi = _rand(&seed) & 0xFF800000;
ip_them.ipv6.lo = _rand(&seed) & 0x1FF;
port_me = _rand(&seed) & 0xFF80;
port_them = _rand(&seed) & 0x1FF;
if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) {
found_match++;
}
}
/* The result should be same as for IPv4, around 30 matches found. */
if (found_match == 0 || found_match > 200) {
line = __LINE__;
goto fail;
}
/* All tests have passed */
return 0; /* success :) */
fail:
fprintf(stderr, "[-] selftest: 'dedup' failed, file=%s, line=%u\n", __FILE__, line);
return 1;
}
================================================
FILE: src/main-dedup.h
================================================
#ifndef MAIN_DEDUP_H
#define MAIN_DEDUP_H
#include "massip-addr.h"
struct DedupTable *
dedup_create(void);
void
dedup_destroy(struct DedupTable *table);
unsigned
dedup_is_duplicate( struct DedupTable *dedup,
ipaddress ip_them, unsigned port_them,
ipaddress ip_me, unsigned port_me);
/**
* Simple unit test
* @return 0 on success, 1 on failure.
*/
int dedup_selftest(void);
#endif
================================================
FILE: src/main-globals.h
================================================
#ifndef MAIN_GLOBALS_H
#define MAIN_GLOBALS_H
#include
extern unsigned volatile is_tx_done;
extern unsigned volatile is_rx_done;
extern time_t global_now;
#endif
================================================
FILE: src/main-initadapter.c
================================================
#include "masscan.h"
#include "util-logger.h"
#include "rawsock.h"
#include "rawsock-adapter.h"
#include "stack-arpv4.h"
#include "stack-ndpv6.h"
#include "stub-pcap-dlt.h"
/***************************************************************************
* Initialize the network adapter.
*
* This requires finding things like our IP address, MAC address, and router
* MAC address. The user could configure these things manually instead.
*
* Note that we don't update the "static" configuration with the discovered
* values, but instead return them as the "running" configuration. That's
* so if we pause and resume a scan, auto discovered values don't get saved
* in the configuration file.
***************************************************************************/
int
masscan_initialize_adapter(
struct Masscan *masscan,
unsigned index,
macaddress_t *source_mac,
macaddress_t *router_mac_ipv4,
macaddress_t *router_mac_ipv6
)
{
char *ifname;
char ifname2[256];
unsigned adapter_ip = 0;
unsigned is_usable_ipv4 = !massip_has_ipv4_targets(&masscan->targets); /* I don't understand this line, seems opposite */
unsigned is_usable_ipv6 = !massip_has_ipv6_targets(&masscan->targets); /* I don't understand this line, seems opposite */
ipaddress_formatted_t fmt;
/*
* ADAPTER/NETWORK-INTERFACE
*
* If no network interface was configured, we need to go hunt down
* the best Interface to use. We do this by choosing the first
* interface with a "default route" (aka. "gateway") defined
*/
if (masscan->nic[index].ifname[0])
ifname = masscan->nic[index].ifname;
else {
/* no adapter specified, so find a default one */
int err;
ifname2[0] = '\0';
err = rawsock_get_default_interface(ifname2, sizeof(ifname2));
if (err || ifname2[0] == '\0') {
LOG(0, "[-] FAIL: could not determine default interface\n");
LOG(0, " [hint] try \"--interface ethX\"\n");
return -1;
}
ifname = ifname2;
}
LOG(1, "[+] interface = %s\n", ifname);
/*
* START ADAPTER
*
* Once we've figured out which adapter to use, we now need to
* turn it on.
*/
masscan->nic[index].adapter = rawsock_init_adapter(
ifname,
masscan->is_pfring,
masscan->is_sendq,
masscan->nmap.packet_trace,
masscan->is_offline,
(void*)masscan->bpf_filter,
masscan->nic[index].is_vlan,
masscan->nic[index].vlan_id);
if (masscan->nic[index].adapter == 0) {
LOG(0, "[-] if:%s:init: failed\n", ifname);
return -1;
}
masscan->nic[index].link_type = masscan->nic[index].adapter->link_type;
LOG(1, "[+] interface-type = %u\n", masscan->nic[index].link_type);
rawsock_ignore_transmits(masscan->nic[index].adapter, ifname);
/*
* MAC ADDRESS
*
* This is the address we send packets from. It actually doesn't really
* matter what this address is, but to be a "responsible" citizen we
* try to use the hardware address in the network card.
*/
if (masscan->nic[index].link_type == PCAP_DLT_NULL) {
LOG(1, "[+] source-mac = %s\n", "none");
} else if (masscan->nic[index].link_type == PCAP_DLT_RAW) {
LOG(1, "[+] source-mac = %s\n", "none");
} else {
*source_mac = masscan->nic[index].source_mac;
if (masscan->nic[index].my_mac_count == 0) {
if (macaddress_is_zero(*source_mac)) {
rawsock_get_adapter_mac(ifname, source_mac->addr);
}
/* If still zero, then print error message */
if (macaddress_is_zero(*source_mac)) {
fprintf(stderr, "[-] FAIL: failed to detect MAC address of interface:"
" \"%s\"\n", ifname);
fprintf(stderr, " [hint] try something like "
"\"--source-mac 00-11-22-33-44-55\"\n");
return -1;
}
}
fmt = macaddress_fmt(*source_mac);
LOG(1, "[+] source-mac = %s\n", fmt.string);
}
/*
* IPv4 ADDRESS
*
* We need to figure out that IP address to send packets from. This
* is done by querying the adapter (or configured by user). If the
* adapter doesn't have one, then the user must configure one.
*/
if (massip_has_ipv4_targets(&masscan->targets)) {
adapter_ip = masscan->nic[index].src.ipv4.first;
if (adapter_ip == 0) {
adapter_ip = rawsock_get_adapter_ip(ifname);
masscan->nic[index].src.ipv4.first = adapter_ip;
masscan->nic[index].src.ipv4.last = adapter_ip;
masscan->nic[index].src.ipv4.range = 1;
}
if (adapter_ip == 0) {
/* We appear to have IPv4 targets, yet we cannot find an adapter
* to use for those targets. We are having trouble querying the
* operating system stack. */
LOG(0, "[-] FAIL: failed to detect IP of interface \"%s\"\n", ifname);
LOG(0, " [hint] did you spell the name correctly?\n");
LOG(0, " [hint] if it has no IP address, manually set with something like "
"\"--source-ip 198.51.100.17\"\n");
if (massip_has_ipv4_targets(&masscan->targets)) {
return -1;
}
}
fmt = ipv4address_fmt(adapter_ip);
LOG(1, "[+] source-ip = %s\n", fmt.string);
if (adapter_ip != 0)
is_usable_ipv4 = 1;
/*
* ROUTER MAC ADDRESS
*
* NOTE: this is one of the least understood aspects of the code. We must
* send packets to the local router, which means the MAC address (not
* IP address) of the router.
*
* Note: in order to ARP the router, we need to first enable the libpcap
* code above.
*/
*router_mac_ipv4 = masscan->nic[index].router_mac_ipv4;
if (masscan->is_offline) {
/* If we are doing offline benchmarking/testing, then create
* a fake MAC address fro the router */
memcpy(router_mac_ipv4->addr, "\x66\x55\x44\x33\x22\x11", 6);
} else if (masscan->nic[index].link_type == PCAP_DLT_NULL) {
/* If it's a VPN tunnel, then there is no Ethernet MAC address */
LOG(1, "[+] router-mac-ipv4 = %s\n", "implicit");
} else if (masscan->nic[index].link_type == PCAP_DLT_RAW) {
/* If it's a VPN tunnel, then there is no Ethernet MAC address */
LOG(1, "[+] router-mac-ipv4 = %s\n", "implicit");
} else if (macaddress_is_zero(*router_mac_ipv4)) {
ipv4address_t router_ipv4 = masscan->nic[index].router_ip;
int err = 0;
LOG(2, "[+] if(%s): looking for default gateway\n", ifname);
if (router_ipv4 == 0)
err = rawsock_get_default_gateway(ifname, &router_ipv4);
if (err == 0) {
fmt = ipv4address_fmt(router_ipv4);
LOG(1, "[+] router-ip = %s\n", fmt.string);
LOG(2, "[+] if(%s):arp: resolving IPv4 address\n", ifname);
stack_arp_resolve(
masscan->nic[index].adapter,
adapter_ip,
*source_mac,
router_ipv4,
router_mac_ipv4);
}
fmt = macaddress_fmt(*router_mac_ipv4);
LOG(1, "[+] router-mac-ipv4 = %s\n", fmt.string);
if (macaddress_is_zero(*router_mac_ipv4)) {
fmt = ipv4address_fmt(masscan->nic[index].router_ip);
LOG(0, "[-] FAIL: ARP timed-out resolving MAC address for router %s: \"%s\"\n", ifname, fmt.string);
LOG(0, " [hint] try \"--router ip 192.0.2.1\" to specify different router\n");
LOG(0, " [hint] try \"--router-mac 66-55-44-33-22-11\" instead to bypass ARP\n");
LOG(0, " [hint] try \"--interface eth0\" to change interface\n");
return -1;
}
}
}
/*
* IPv6 ADDRESS
*
* We need to figure out that IPv6 address to send packets from. This
* is done by querying the adapter (or configured by user). If the
* adapter doesn't have one, then the user must configure one.
*/
if (massip_has_ipv6_targets(&masscan->targets)) {
ipv6address adapter_ipv6 = masscan->nic[index].src.ipv6.first;
if (ipv6address_is_zero(adapter_ipv6)) {
adapter_ipv6 = rawsock_get_adapter_ipv6(ifname);
masscan->nic[index].src.ipv6.first = adapter_ipv6;
masscan->nic[index].src.ipv6.last = adapter_ipv6;
masscan->nic[index].src.ipv6.range = 1;
}
if (ipv6address_is_zero(adapter_ipv6)) {
fprintf(stderr, "[-] FAIL: failed to detect IPv6 address of interface \"%s\"\n",
ifname);
fprintf(stderr, " [hint] did you spell the name correctly?\n");
fprintf(stderr, " [hint] if it has no IP address, manually set with something like "
"\"--source-ip 2001:3b8::1234\"\n");
return -1;
}
fmt = ipv6address_fmt(adapter_ipv6);
LOG(1, "[+] source-ip = [%s]\n", fmt.string);
is_usable_ipv6 = 1;
/*
* ROUTER MAC ADDRESS
*/
*router_mac_ipv6 = masscan->nic[index].router_mac_ipv6;
if (masscan->is_offline) {
memcpy(router_mac_ipv6->addr, "\x66\x55\x44\x33\x22\x11", 6);
}
if (macaddress_is_zero(*router_mac_ipv6)) {
/* [synchronous]
* Wait for router neighbor notification. This may take
* some time */
stack_ndpv6_resolve(
masscan->nic[index].adapter,
adapter_ipv6,
*source_mac,
router_mac_ipv6);
}
fmt = macaddress_fmt(*router_mac_ipv6);
LOG(1, "[+] router-mac-ipv6 = %s\n", fmt.string);
if (macaddress_is_zero(*router_mac_ipv6)) {
fmt = ipv4address_fmt(masscan->nic[index].router_ip);
LOG(0, "[-] FAIL: NDP timed-out resolving MAC address for router %s: \"%s\"\n", ifname, fmt.string);
LOG(0, " [hint] try \"--router-mac-ipv6 66-55-44-33-22-11\" instead to bypass ARP\n");
LOG(0, " [hint] try \"--interface eth0\" to change interface\n");
return -1;
}
}
masscan->nic[index].is_usable = (is_usable_ipv4 & is_usable_ipv6);
LOG(2, "[+] if(%s): initialization done.\n", ifname);
return 0;
}
================================================
FILE: src/main-listscan.c
================================================
#include "masscan.h"
#include "util-logger.h"
#include "crypto-blackrock.h"
void
main_listscan(struct Masscan *masscan)
{
uint64_t i;
uint64_t range;
uint64_t start;
uint64_t end;
struct BlackRock blackrock;
unsigned increment = masscan->shard.of;
uint64_t seed = masscan->seed;
/* If called with no ports, then create a pseudo-port needed
* for the internal algorithm. */
if (!massip_has_target_ports(&masscan->targets))
rangelist_add_range(&masscan->targets.ports, 80, 80);
massip_optimize(&masscan->targets);
/* The "range" is the total number of IP/port combinations that
* the scan can produce */
range = massip_range(&masscan->targets).lo;
infinite:
blackrock_init(&blackrock, range, seed, masscan->blackrock_rounds);
start = masscan->resume.index + (masscan->shard.one-1);
end = range;
if (masscan->resume.count && end > start + masscan->resume.count)
end = start + masscan->resume.count;
end += (uint64_t)(masscan->retries * masscan->max_rate);
for (i=start; itargets, xXx, &addr, &port);
if (masscan->is_test_csv) {
/* [KLUDGE] [TEST]
* For testing randomness output, prints last two bytes of
* IP address as CSV format for import into spreadsheet
*/
printf("%u,%u\n",(addr.ipv4>>8)&0xFF, (addr.ipv4>>0)&0xFF);
} else if (masscan->targets.count_ports == 1) {
ipaddress_formatted_t fmt = ipaddress_fmt(addr);
/* This is the normal case */
printf("%s\n", fmt.string);
} else {
ipaddress_formatted_t fmt = ipaddress_fmt(addr);
if (addr.version == 6)
printf("[%s]:%u\n", fmt.string, port);
else
printf("%s:%u\n", fmt.string, port);
}
i += increment; /* <------ increment by 1 normally, more with shards/NICs */
}
if (masscan->is_infinite) {
seed++;
goto infinite;
}
}
================================================
FILE: src/main-ptrace.c
================================================
#include "main-ptrace.h"
#include "proto-preprocess.h"
#include "pixie-timer.h"
#include "util-safefunc.h"
/***************************************************************************
* Print packet info, when using nmap-style --packet-trace option
***************************************************************************/
void
packet_trace(FILE *fp, double pt_start, const unsigned char *px, size_t length, unsigned is_sent)
{
unsigned x;
struct PreprocessedInfo parsed;
char from[64];
char to[64];
char sz_type[32];
unsigned type;
double timestamp = 1.0 * pixie_gettime() / 1000000.0;
unsigned offset;
const char *direction;
ipaddress_formatted_t fmt;
if (is_sent)
direction = "SENT";
else
direction = "RCVD";
/* parse the packet */
x = preprocess_frame(px, (unsigned)length, 1, &parsed);
if (!x)
return;
offset = parsed.found_offset;
/* format the IP addresses into fixed-width fields */
fmt = ipaddress_fmt(parsed.src_ip);
snprintf(from, sizeof(from), "[%s]:%u", fmt.string, parsed.port_src);
fmt = ipaddress_fmt(parsed.dst_ip);
snprintf(to, sizeof(to), "[%s]:%u", fmt.string, parsed.port_dst);
switch (parsed.found) {
case FOUND_ARP:
type = px[offset+6]<<8 | px[offset+7];
*strchr(to, ':') = '\0';
*strchr(from, ':') = '\0';
switch (type) {
case 1:safe_strcpy(sz_type, sizeof(sz_type), "request"); break;
case 2:safe_strcpy(sz_type, sizeof(sz_type), "response"); break;
default: snprintf(sz_type, sizeof(sz_type), "unknown(%u)", type); break;
}
fprintf(fp, "%s (%5.4f) ARP %-21s > %-21s %s\n", direction,
timestamp - pt_start, from, to, sz_type);
break;
case FOUND_DNS:
case FOUND_UDP:
fprintf(fp, "%s (%5.4f) UDP %-21s > %-21s \n", direction,
timestamp - pt_start, from, to);
break;
case FOUND_ICMP:
fprintf(fp, "%s (%5.4f) ICMP %-21s > %-21s \n", direction,
timestamp - pt_start, from, to);
break;
case FOUND_TCP:
type = px[offset+13];
switch (type) {
case 0x00: safe_strcpy(sz_type, sizeof(sz_type), "NULL"); break;
case 0x01: safe_strcpy(sz_type, sizeof(sz_type), "FIN"); break;
case 0x11: safe_strcpy(sz_type, sizeof(sz_type), "FIN-ACK"); break;
case 0x19: safe_strcpy(sz_type, sizeof(sz_type), "FIN-ACK-PSH"); break;
case 0x02: safe_strcpy(sz_type, sizeof(sz_type), "SYN"); break;
case 0x12: safe_strcpy(sz_type, sizeof(sz_type), "SYN-ACK"); break;
case 0x04: safe_strcpy(sz_type, sizeof(sz_type), "RST"); break;
case 0x14: safe_strcpy(sz_type, sizeof(sz_type), "RST-ACK"); break;
case 0x15: safe_strcpy(sz_type, sizeof(sz_type), "RST-FIN-ACK"); break;
case 0x10: safe_strcpy(sz_type, sizeof(sz_type), "ACK"); break;
case 0x18: safe_strcpy(sz_type, sizeof(sz_type), "ACK-PSH"); break;
default:
snprintf(sz_type, sizeof(sz_type),
"%s%s%s%s%s%s%s%s",
(type&0x01)?"FIN":"",
(type&0x02)?"SYN":"",
(type&0x04)?"RST":"",
(type&0x08)?"PSH":"",
(type&0x10)?"ACK":"",
(type&0x20)?"URG":"",
(type&0x40)?"ECE":"",
(type&0x80)?"CWR":""
);
break;
}
if (parsed.app_length)
fprintf(fp, "%s (%5.4f) TCP %-21s > %-21s %s %u-bytes\n", direction,
timestamp - pt_start, from, to, sz_type, parsed.app_length);
else
fprintf(fp, "%s (%5.4f) TCP %-21s > %-21s %s\n", direction,
timestamp - pt_start, from, to, sz_type);
break;
case FOUND_IPV6:
break;
default:
fprintf(fp, "%s (%5.4f) UNK %-21s > %-21s [%u]\n", direction,
timestamp - pt_start, from, to, parsed.found);
break;
}
}
================================================
FILE: src/main-ptrace.h
================================================
#ifndef masscan_main_ptrace_h
#define masscan_main_ptrace_h
#include
#include
void packet_trace(FILE *fp, double pt_trace, const unsigned char *px, size_t length, unsigned is_sent);
#endif
================================================
FILE: src/main-readrange.c
================================================
#include "main-readrange.h"
#include "masscan.h"
#include
/***************************************************************************
***************************************************************************/
/*static unsigned
count_cidr_bits(struct Range range)
{
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;
}
}
return 0;
}*/
/***************************************************************************
***************************************************************************/
static unsigned
count_cidr6_bits(struct Range6 range)
{
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;
}
}
return 0;
}
/***************************************************************************
***************************************************************************/
void
main_readrange(struct Masscan *masscan)
{
struct RangeList *list4 = &masscan->targets.ipv4;
struct Range6List *list6 = &masscan->targets.ipv6;
unsigned i;
FILE *fp = stdout;
for (i=0; icount; i++) {
unsigned prefix_length;
struct Range range = list4->list[i];
if (range.begin == range.end) {
fprintf(fp, "%u.%u.%u.%u\n",
(range.begin>>24)&0xFF,
(range.begin>>16)&0xFF,
(range.begin>> 8)&0xFF,
(range.begin>> 0)&0xFF
);
} else if (range_is_cidr(range, &prefix_length)) {
fprintf(fp, "%u.%u.%u.%u/%u\n",
(range.begin>>24)&0xFF,
(range.begin>>16)&0xFF,
(range.begin>> 8)&0xFF,
(range.begin>> 0)&0xFF,
prefix_length
);
} else {
fprintf(fp, "%u.%u.%u.%u-%u.%u.%u.%u\n",
(range.begin>>24)&0xFF,
(range.begin>>16)&0xFF,
(range.begin>> 8)&0xFF,
(range.begin>> 0)&0xFF,
(range.end>>24)&0xFF,
(range.end>>16)&0xFF,
(range.end>> 8)&0xFF,
(range.end>> 0)&0xFF
);
}
}
for (i=0; icount; i++) {
struct Range6 range = list6->list[i];
ipaddress_formatted_t fmt = ipv6address_fmt(range.begin);
fprintf(fp, "%s", fmt.string);
if (!ipv6address_is_equal(range.begin, range.end)) {
unsigned cidr_bits = count_cidr6_bits(range);
if (cidr_bits) {
fprintf(fp, "/%u", cidr_bits);
} else {
fmt = ipv6address_fmt(range.end);
fprintf(fp, "-%s", fmt.string);
}
}
fprintf(fp, "\n");
}
}
================================================
FILE: src/main-readrange.h
================================================
#ifndef MAIN_READRANGE_H
#define MAIN_READRANGE_H
struct Masscan;
void
main_readrange(struct Masscan *masscan);
#endif
================================================
FILE: src/main-status.c
================================================
/*
prints "status" message once per second to the commandline
The status message indicates:
- the rate in packets-per-second
- %done
- estimated time remaining of the scan
- number of 'tcbs' (TCP control blocks) of active TCP connections
*/
#include "main-status.h"
#include "pixie-timer.h"
#include "unusedparm.h"
#include "main-globals.h"
#include "util-safefunc.h"
#include "util-bool.h"
#include
/***************************************************************************
* Print a status message about once-per-second to the command-line. This
* algorithm is a little funky because checking the timestamp on EVERY
* packet is slow.
***************************************************************************/
void
status_print(
struct Status *status,
uint64_t count,
uint64_t max_count,
double pps,
uint64_t total_tcbs,
uint64_t total_synacks,
uint64_t total_syns,
uint64_t exiting,
bool json_status)
{
double elapsed_time;
double rate;
double now;
double percent_done;
double time_remaining;
uint64_t current_tcbs = 0;
uint64_t current_synacks = 0;
uint64_t current_syns = 0;
double tcb_rate = 0.0;
double synack_rate = 0.0;
double syn_rate = 0.0;
double kpps = pps / 1000;
const char *fmt;
/* Support for --json-status; does not impact legacy/default output */
/**
* {"state":"*","rate":{"kpps":24.99,"pps":24985.49,"synps": 27763,"ackps":4,"tcbps":4},"tcb": 33,"syn":246648}
*/
const char* json_fmt_infinite =
"{"
"\"state\":\"*\","
"\"rate\":"
"{"
"\"kpps\":%.2f,"
"\"pps\":%6$.2f,"
"\"synps\":%.0f,"
"\"ackps\":%.0f,"
"\"tcbps\":%.0f"
"},"
"\"tcb\":%5$" PRIu64 ","
"\"syn\":%7$" PRIu64
"}\n";
/**
* {"state":"waiting","rate":{"kpps":0.00,"pps":0.00},"progress":{"percent":21.87,"seconds":4,"found":56,"syn":{"sent": 341436,"total":1561528,"remaining":1220092}}}
*/
const char *json_fmt_waiting =
"{"
"\"state\":\"waiting\","
"\"rate\":"
"{"
"\"kpps\":%.2f,"
"\"pps\":%5$.2f"
"},"
"\"progress\":"
"{"
"\"percent\":%.2f,"
"\"seconds\":%d,"
"\"found\":%" PRIu64 ","
"\"syn\":"
"{"
"\"sent\":%6$" PRIu64 ","
"\"total\":%7$" PRIu64 ","
"\"remaining\":%8$" PRIu64
"}"
"}"
"}\n";
/**
* {"state":"running","rate":{"kpps":24.92,"pps":24923.07},"progress":{"percent":9.77,"eta":{
* "hours":0,"mins":0,"seconds":55},"syn":{"sent": 152510,"total": 1561528,"remaining": 1409018},"found": 27}}
*/
const char *json_fmt_running =
"{"
"\"state\":\"running\","
"\"rate\":"
"{"
"\"kpps\":%.2f,"
"\"pps\":%7$.2f"
"},"
"\"progress\":"
"{"
"\"percent\":%.2f,"
"\"eta\":"
"{"
"\"hours\":%u,"
"\"mins\":%u,"
"\"seconds\":%u"
"},"
"\"syn\":"
"{"
"\"sent\":%8$" PRIu64 ","
"\"total\":%9$" PRIu64 ","
"\"remaining\":%10$" PRIu64
"},"
"\"found\":%6$" PRIu64
"}"
"}\n";
/*
* #### FUGGLY TIME HACK ####
*
* PF_RING doesn't timestamp packets well, so we can't base time from
* incoming packets. Checking the time ourself is too ugly on per-packet
* basis. Therefore, we are going to create a global variable that keeps
* the time, and update that variable whenever it's convenient. This
* is one of those convenient places.
*/
global_now = time(0);
/* Get the time. NOTE: this is CLOCK_MONOTONIC_RAW on Linux, not
* wall-clock time. */
now = (double)pixie_gettime();
/* Figure how many SECONDS have elapsed, in a floating point value.
* Since the above timestamp is in microseconds, we need to
* shift it by 1-million
*/
elapsed_time = (now - status->last.clock)/1000000.0;
if (elapsed_time <= 0)
return;
/* Figure out the "packets-per-second" number, which is just:
*
* rate = packets_sent / elapsed_time;
*/
rate = (count - status->last.count)*1.0/elapsed_time;
/*
* Smooth the number by averaging over the last 8 seconds
*/
status->last_rates[status->last_count++ & 0x7] = rate;
rate = status->last_rates[0]
+ status->last_rates[1]
+ status->last_rates[2]
+ status->last_rates[3]
+ status->last_rates[4]
+ status->last_rates[5]
+ status->last_rates[6]
+ status->last_rates[7]
;
rate /= 8;
/*if (rate == 0)
return;*/
/*
* Calculate "percent-done", which is just the total number of
* packets sent divided by the number we need to send.
*/
percent_done = (double)(count*100.0/max_count);
/*
* Calculate the time remaining in the scan
*/
time_remaining = (1.0 - percent_done/100.0) * (max_count / rate);
/*
* some other stats
*/
if (total_tcbs) {
current_tcbs = total_tcbs - status->total_tcbs;
status->total_tcbs = total_tcbs;
tcb_rate = (1.0*current_tcbs)/elapsed_time;
}
if (total_synacks) {
current_synacks = total_synacks - status->total_synacks;
status->total_synacks = total_synacks;
synack_rate = (1.0*current_synacks)/elapsed_time;
}
if (total_syns) {
current_syns = total_syns - status->total_syns;
status->total_syns = total_syns;
syn_rate = (1.0*current_syns)/elapsed_time;
}
/*
* Print the message to so that can be redirected
* to a file ( reports what systems were found).
*/
if (status->is_infinite) {
if (json_status == 1)
fmt = json_fmt_infinite;
else
fmt = "rate:%6.2f-kpps, syn/s=%.0f ack/s=%.0f tcb-rate=%.0f, %" PRIu64 "-tcbs, \r";
fprintf(stderr,
fmt,
kpps,
syn_rate,
synack_rate,
tcb_rate,
total_tcbs,
pps,
count);
} else {
if (is_tx_done) {
if (json_status == 1)
fmt = json_fmt_waiting;
else
fmt = "rate:%6.2f-kpps, %5.2f%% done, waiting %d-secs, found=%" PRIu64 " \r";
fprintf(stderr,
fmt,
pps/1000.0,
percent_done,
(int)exiting,
total_synacks,
pps,
count,
max_count,
max_count-count);
} else {
if (json_status == 1)
fmt = json_fmt_running;
else
fmt = "rate:%6.2f-kpps, %5.2f%% done,%4u:%02u:%02u remaining, found=%" PRIu64 " \r";
fprintf(stderr,
fmt,
pps/1000.0,
percent_done,
(unsigned)(time_remaining/60/60),
(unsigned)(time_remaining/60)%60,
(unsigned)(time_remaining)%60,
total_synacks,
pps,
count,
max_count,
max_count-count);
}
}
fflush(stderr);
/*
* Remember the values to be diffed against the next time around
*/
status->last.clock = now;
status->last.count = count;
}
/***************************************************************************
***************************************************************************/
void
status_finish(struct Status *status)
{
UNUSEDPARM(status);
fprintf(stderr,
" \r");
}
/***************************************************************************
***************************************************************************/
void
status_start(struct Status *status)
{
memset(status, 0, sizeof(*status));
status->last.clock = clock();
status->last.time = time(0);
status->last.count = 0;
status->timer = 0x1;
}
================================================
FILE: src/main-status.h
================================================
#ifndef MAIN_STATUS_H
#define MAIN_STATUS_H
#include
#include
#include "util-bool.h"
struct Status
{
struct {
double clock;
time_t time;
uint64_t count;
} last;
uint64_t timer;
unsigned charcount;
double last_rates[8];
unsigned last_count;
unsigned is_infinite:1;
uint64_t total_tcbs;
uint64_t total_synacks;
uint64_t total_syns;
};
void status_print(struct Status *status, uint64_t count, uint64_t max_count, double x, uint64_t total_tcbs, uint64_t total_synacks, uint64_t total_syns, uint64_t exiting, bool json_status);
void status_finish(struct Status *status);
void status_start(struct Status *status);
#endif
================================================
FILE: src/main-throttle.c
================================================
/*
Rate-limit/throttler: stops us from transmitting too fast.
We can send packets at millions of packets/second. This will
melt most networks. Therefore, we need to throttle or rate-limit
how fast we go.
Since we are sending packet at a rate of 10-million-per-second, we
the calculations need to be done in a light-weight manner. For one
thing, we can't do a system-call per packet.
NOTE: one complication to watch for is the difference between clock
time and elapsed time, and that they change. We have to avoid a problem
where somebody suspends the computer for a few days, then wake it up,
at which point the system tries sending a million packets/second instead
of the desired thousand packets/second.
*/
#include "main-throttle.h"
#include "pixie-timer.h"
#include "util-logger.h"
#include
#include
/***************************************************************************
***************************************************************************/
void
throttler_start(struct Throttler *throttler, double max_rate)
{
unsigned i;
memset(throttler, 0, sizeof(*throttler));
throttler->max_rate = max_rate;
for (i=0; ibuckets)/sizeof(throttler->buckets[0]); i++) {
throttler->buckets[i].timestamp = pixie_gettime();
throttler->buckets[i].packet_count = 0;
}
throttler->batch_size = 1;
LOG(1, "[+] starting throttler: rate = %0.2f-pps\n", throttler->max_rate);
}
/***************************************************************************
* We return the number of packets that can be sent in a batch. Thus,
* instead of trying to throttle each packet individually, which has a
* high per-packet cost, we try to throttle a bunch at a time. Normally,
* this function will return 1, only at high rates does it return larger
* numbers.
*
* NOTE: The minimum value this returns is 1. When it's less than that,
* it'll pause and wait until it's ready to send a packet.
***************************************************************************/
uint64_t
throttler_next_batch(struct Throttler *throttler, uint64_t packet_count)
{
uint64_t timestamp;
uint64_t index;
uint64_t old_timestamp;
uint64_t old_packet_count;
double current_rate;
double max_rate = throttler->max_rate;
again:
/* NOTE: this uses CLOCK_MONOTONIC_RAW on Linux, so the timstamp doesn't
* move forward when the machine is suspended */
timestamp = pixie_gettime();
/*
* We record that last 256 buckets, and average the rate over all of
* them.
*/
index = (throttler->index) & 0xFF;
throttler->buckets[index].timestamp = timestamp;
throttler->buckets[index].packet_count = packet_count;
index = (++throttler->index) & 0xFF;
old_timestamp = throttler->buckets[index].timestamp;
old_packet_count = throttler->buckets[index].packet_count;
/*
* If the delay is more than 1-second, then we should reset the system
* in order to avoid transmitting too fast.
*/
if (timestamp - old_timestamp > 1000000) {
//throttler_start(throttler, throttler->max_rate);
throttler->batch_size = 1;
goto again;
}
/*
* Calculate the recent rate.
* NOTE: this isn't the rate "since start", but only the "recent" rate.
* That's so that if the system pauses for a while, we don't flood the
* network trying to catch up.
*/
current_rate = 1.0*(packet_count - old_packet_count)/((timestamp - old_timestamp)/1000000.0);
/*
* If we've been going too fast, then for a moment, then
* try again.
*/
if (current_rate > max_rate) {
double waittime;
/* calculate waittime, in seconds */
waittime = (current_rate - max_rate) / throttler->max_rate;
/* At higher rates of speed, we don't actually need to wait the full
* interval. It's better to have a much smaller interval, so that
* we converge back on the true rate faster */
waittime *= 0.1;
/* This is in case of gross failure of the system. This should never
* actually happen, unless there is a bug. Really, I ought to make
* this an 'assert()' instead to fail and fix the bug rather than
* silently continuing, but I'm too lazy */
if (waittime > 0.1)
waittime = 0.1;
/* Since we've exceeded the speed limit, we should reduce the
* batch size slightly. We don't do it only by a little bit to
* avoid over-correcting. We want to converge on the correct
* speed gradually. Note that since this happens hundreds or
* thousands of times a second, the convergence is very fast
* even with 0.1% adjustment */
throttler->batch_size *= 0.999;
/* Now we wait for a bit */
pixie_usleep((uint64_t)(waittime * 1000000.0));
/* There are two choices here. We could either return immediately,
* or we can loop around again. Right now, the code loops around
* again in order to support very slow rates, such as 0.5 packets
* per second. Nobody would want to run a scanner that slowly of
* course, but it's great for testing */
//return (uint64_t)throttler->batch_size;
goto again;
}
/*
* Calculate how many packets are needed to catch up again to the current
* rate, and return that.
*
* NOTE: this is almost always going to have the value of 1 (one). Only at
* very high speeds (above 100,000 packets/second) will this value get
* larger.
*/
throttler->batch_size *= 1.005;
if (throttler->batch_size > 10000)
throttler->batch_size = 10000;
throttler->current_rate = current_rate;
throttler->test_timestamp = timestamp;
throttler->test_packet_count = packet_count;
return (uint64_t)throttler->batch_size;
}
================================================
FILE: src/main-throttle.h
================================================
#ifndef MAIN_THROTTLE_H
#define MAIN_THROTTLE_H
#include
struct Throttler
{
double max_rate;
double current_rate;
double batch_size;
unsigned index;
struct {
uint64_t timestamp;
uint64_t packet_count;
} buckets[256];
uint64_t test_timestamp;
uint64_t test_packet_count;
};
uint64_t throttler_next_batch(struct Throttler *throttler, uint64_t count);
void throttler_start(struct Throttler *status, double max_rate);
#endif
================================================
FILE: src/main.c
================================================
/*
main
This includes:
* main()
* transmit_thread() - transmits probe packets
* receive_thread() - receives response packets
You'll be wanting to study the transmit/receive threads, because that's
where all the action is.
This is the lynch-pin of the entire program, so it includes a heckuva lot
of headers, and the functions have a lot of local variables. I'm trying
to make this file relative "flat" this way so that everything is visible.
*/
#include "masscan.h"
#include "masscan-version.h"
#include "masscan-status.h" /* open or closed */
#include "massip-parse.h"
#include "massip-port.h"
#include "main-status.h" /* printf() regular status updates */
#include "main-throttle.h" /* rate limit */
#include "main-dedup.h" /* ignore duplicate responses */
#include "main-ptrace.h" /* for nmap --packet-trace feature */
#include "main-globals.h" /* all the global variables in the program */
#include "main-readrange.h"
#include "crypto-siphash24.h" /* hash function, for hash tables */
#include "crypto-blackrock.h" /* the BlackRock shuffling func */
#include "crypto-lcg.h" /* the LCG randomization func */
#include "crypto-base64.h" /* base64 encode/decode */
#include "templ-pkt.h" /* packet template, that we use to send */
#include "util-logger.h" /* adjust with -v command-line opt */
#include "stack-ndpv6.h" /* IPv6 Neighbor Discovery Protocol */
#include "stack-arpv4.h" /* Handle ARP resolution and requests */
#include "rawsock.h" /* API on top of Linux, Windows, Mac OS X*/
#include "rawsock-adapter.h" /* Get Ethernet adapter configuration */
#include "rawsock-pcapfile.h" /* for saving pcap files w/ raw packets */
#include "syn-cookie.h" /* for SYN-cookies on send */
#include "output.h" /* for outputting results */
#include "rte-ring.h" /* producer/consumer ring buffer */
#include "stub-pcap.h" /* dynamically load libpcap library */
#include "smack.h" /* Aho-corasick state-machine pattern-matcher */
#include "pixie-timer.h" /* portable time functions */
#include "pixie-threads.h" /* portable threads */
#include "pixie-backtrace.h" /* maybe print backtrace on crash */
#include "templ-payloads.h" /* UDP packet payloads */
#include "in-binary.h" /* convert binary output to XML/JSON */
#include "vulncheck.h" /* checking vulns like monlist, poodle, heartblee */
#include "scripting.h"
#include "read-service-probes.h"
#include "misc-rstfilter.h"
#include "proto-x509.h"
#include "proto-arp.h" /* for responding to ARP requests */
#include "proto-banner1.h" /* for snatching banners from systems */
#include "stack-tcp-core.h" /* for TCP/IP connection table */
#include "proto-preprocess.h" /* quick parse of packets */
#include "proto-icmp.h" /* handle ICMP responses */
#include "proto-udp.h" /* handle UDP responses */
#include "proto-snmp.h" /* parse SNMP responses */
#include "proto-ntp.h" /* parse NTP responses */
#include "proto-coap.h" /* CoAP selftest */
#include "proto-zeroaccess.h"
#include "proto-sctp.h"
#include "proto-oproto.h" /* Other protocols on top of IP */
#include "util-malloc.h"
#include "util-checksum.h"
#include
#include
#include
#include
#include
#include
#include
#if defined(WIN32)
#include
#if defined(_MSC_VER)
#pragma comment(lib, "Ws2_32.lib")
#endif
#else
#include
#include
#include
#endif
/*
* yea I know globals suck
*/
unsigned volatile is_tx_done = 0;
unsigned volatile is_rx_done = 0;
time_t global_now;
uint64_t usec_start;
/***************************************************************************
* We create a pair of transmit/receive threads for each network adapter.
* This structure contains the parameters we send to each pair.
***************************************************************************/
struct ThreadPair {
/** This points to the central configuration. Note that it's 'const',
* meaning that the thread cannot change the contents. That'd be
* unsafe */
const struct Masscan *masscan;
/** The adapter used by the thread-pair. Normally, thread-pairs have
* their own network adapter, especially when doing PF_RING
* clustering. */
struct Adapter *adapter;
struct stack_t *stack;
/**
* The index of the network adapter that we are using for this
* thread-pair. This is an index into the "masscan->nic[]"
* array.
*
* NOTE: this is also the "thread-id", because we create one
* transmit/receive thread pair per NIC.
*/
unsigned nic_index;
/**
* A copy of the master 'index' variable. This is just advisory for
* other threads, to tell them how far we've gotten.
*/
volatile uint64_t my_index;
/* This is used both by the transmit and receive thread for
* formatting packets */
struct TemplateSet tmplset[1];
/**
* The current IP address we are using for transmit/receive.
*/
struct stack_src_t _src_;
macaddress_t source_mac;
macaddress_t router_mac_ipv4;
macaddress_t router_mac_ipv6;
unsigned done_transmitting;
unsigned done_receiving;
double pt_start;
struct Throttler throttler[1];
uint64_t *total_synacks;
uint64_t *total_tcbs;
uint64_t *total_syns;
size_t thread_handle_xmit;
size_t thread_handle_recv;
};
struct source_t {
unsigned ipv4;
unsigned ipv4_mask;
unsigned port;
unsigned port_mask;
ipv6address ipv6;
ipv6address ipv6_mask;
};
/***************************************************************************
* We support a range of source IP/port. This function converts that
* range into useful variables we can use to pick things form that range.
***************************************************************************/
static void
adapter_get_source_addresses(const struct Masscan *masscan,
unsigned nic_index,
struct source_t *src)
{
const struct stack_src_t *ifsrc = &masscan->nic[nic_index].src;
static ipv6address mask = {~0ULL, ~0ULL};
src->ipv4 = ifsrc->ipv4.first;
src->ipv4_mask = ifsrc->ipv4.last - ifsrc->ipv4.first;
src->port = ifsrc->port.first;
src->port_mask = ifsrc->port.last - ifsrc->port.first;
src->ipv6 = ifsrc->ipv6.first;
/* TODO: currently supports only a single address. This needs to
* be fixed to support a list of addresses */
src->ipv6_mask = mask;
}
/***************************************************************************
* This thread spews packets as fast as it can
*
* THIS IS WHERE ALL THE EXCITEMENT HAPPENS!!!!
* 90% of CPU cycles are in the function.
*
***************************************************************************/
static void
transmit_thread(void *v) /*aka. scanning_thread() */
{
struct ThreadPair *parms = (struct ThreadPair *)v;
uint64_t i;
uint64_t start;
uint64_t end;
const struct Masscan *masscan = parms->masscan;
uint64_t retries = masscan->retries;
uint64_t rate = (uint64_t)masscan->max_rate;
unsigned r = (unsigned)retries + 1;
uint64_t range;
uint64_t range_ipv6;
struct BlackRock blackrock;
uint64_t count_ipv4 = rangelist_count(&masscan->targets.ipv4);
uint64_t count_ipv6 = range6list_count(&masscan->targets.ipv6).lo;
struct Throttler *throttler = parms->throttler;
struct TemplateSet pkt_template = templ_copy(parms->tmplset);
struct Adapter *adapter = parms->adapter;
uint64_t packets_sent = 0;
unsigned increment = masscan->shard.of * masscan->nic_count;
struct source_t src;
uint64_t seed = masscan->seed;
uint64_t repeats = 0; /* --infinite repeats */
uint64_t *status_syn_count;
uint64_t entropy = masscan->seed;
/* Wait to make sure receive_thread is ready */
pixie_usleep(1000000);
LOG(1, "[+] starting transmit thread #%u\n", parms->nic_index);
/* export a pointer to this variable outside this threads so
* that the 'status' system can print the rate of syns we are
* sending */
status_syn_count = MALLOC(sizeof(uint64_t));
*status_syn_count = 0;
parms->total_syns = status_syn_count;
/* Normally, we have just one source address. In special cases, though
* we can have multiple. */
adapter_get_source_addresses(masscan, parms->nic_index, &src);
/* "THROTTLER" rate-limits how fast we transmit, set with the
* --max-rate parameter */
throttler_start(throttler, masscan->max_rate/masscan->nic_count);
infinite:
/* Create the shuffler/randomizer. This creates the 'range' variable,
* which is simply the number of IP addresses times the number of
* ports.
* IPv6: low index will pick addresses from the IPv6 ranges, and high
* indexes will pick addresses from the IPv4 ranges. */
range = count_ipv4 * rangelist_count(&masscan->targets.ports)
+ count_ipv6 * rangelist_count(&masscan->targets.ports);
range_ipv6 = count_ipv6 * rangelist_count(&masscan->targets.ports);
blackrock_init(&blackrock, range, seed, masscan->blackrock_rounds);
/* Calculate the 'start' and 'end' of a scan. One reason to do this is
* to support --shard, so that multiple machines can co-operate on
* the same scan. Another reason to do this is so that we can bleed
* a little bit past the end when we have --retries. Yet another
* thing to do here is deal with multiple network adapters, which
* is essentially the same logic as shards. */
start = masscan->resume.index + (masscan->shard.one-1) * masscan->nic_count + parms->nic_index;
end = range;
if (masscan->resume.count && end > start + masscan->resume.count)
end = start + masscan->resume.count;
end += retries * range;
/* -----------------
* the main loop
* -----------------*/
LOG(3, "THREAD: xmit: starting main loop: [%llu..%llu]\n", start, end);
for (i=start; istack, adapter,
&packets_sent, &batch_size);
/*
* Transmit a bunch of packets. At any rate slower than 100,000
* packets/second, the 'batch_size' is likely to be 1. At higher
* rates, we can't afford to throttle on a per-packet basis and
* instead throttle on a per-batch basis. In other words, throttle
* based on 2-at-a-time, 3-at-time, and so on, with the batch
* size increasing as the packet rate increases. This gives us
* very precise packet-timing for low rates below 100,000 pps,
* while not incurring the overhead for high packet rates.
*/
while (batch_size && i < end) {
uint64_t xXx;
uint64_t cookie;
/*
* RANDOMIZE THE TARGET:
* This is kinda a tricky bit that picks a random IP and port
* number in order to scan. We monotonically increment the
* index 'i' from [0..range]. We then shuffle (randomly transmog)
* that index into some other, but unique/1-to-1, number in the
* same range. That way we visit all targets, but in a random
* order. Then, once we've shuffled the index, we "pick" the
* IP address and port that the index refers to.
*/
xXx = (i + (r--) * rate);
if (rate > range)
xXx %= range;
else
while (xXx >= range)
xXx -= range;
xXx = blackrock_shuffle(&blackrock, xXx);
if (xXx < range_ipv6) {
ipv6address ip_them;
unsigned port_them;
ipv6address ip_me;
unsigned port_me;
ip_them = range6list_pick(&masscan->targets.ipv6, xXx % count_ipv6);
port_them = rangelist_pick(&masscan->targets.ports, xXx / count_ipv6);
ip_me = src.ipv6;
port_me = src.port;
cookie = syn_cookie_ipv6(ip_them, port_them, ip_me, port_me, entropy);
rawsock_send_probe_ipv6(
adapter,
ip_them, port_them,
ip_me, port_me,
(unsigned)cookie,
!batch_size, /* flush queue on last packet in batch */
&pkt_template
);
/* Our index selects an IPv6 target */
} else {
/* Our index selects an IPv4 target. In other words, low numbers
* index into the IPv6 ranges, and high numbers index into the
* IPv4 ranges. */
ipv4address ip_them;
ipv4address port_them;
unsigned ip_me;
unsigned port_me;
xXx -= range_ipv6;
ip_them = rangelist_pick(&masscan->targets.ipv4, xXx % count_ipv4);
port_them = rangelist_pick(&masscan->targets.ports, xXx / count_ipv4);
/*
* SYN-COOKIE LOGIC
* Figure out the source IP/port, and the SYN cookie
*/
if (src.ipv4_mask > 1 || src.port_mask > 1) {
uint64_t ck = syn_cookie_ipv4((unsigned)(i+repeats),
(unsigned)((i+repeats)>>32),
(unsigned)xXx, (unsigned)(xXx>>32),
entropy);
port_me = src.port + (ck & src.port_mask);
ip_me = src.ipv4 + ((ck>>16) & src.ipv4_mask);
} else {
ip_me = src.ipv4;
port_me = src.port;
}
cookie = syn_cookie_ipv4(ip_them, port_them, ip_me, port_me, entropy);
/*
* SEND THE PROBE
* This is sorta the entire point of the program, but little
* exciting happens here. The thing to note that this may
* be a "raw" transmit that bypasses the kernel, meaning
* we can call this function millions of times a second.
*/
rawsock_send_probe_ipv4(
adapter,
ip_them, port_them,
ip_me, port_me,
(unsigned)cookie,
!batch_size, /* flush queue on last packet in batch */
&pkt_template
);
}
batch_size--;
packets_sent++;
(*status_syn_count)++;
/*
* SEQUENTIALLY INCREMENT THROUGH THE RANGE
* Yea, I know this is a puny 'i++' here, but it's a core feature
* of the system that is linearly increments through the range,
* but produces from that a shuffled sequence of targets (as
* described above). Because we are linearly incrementing this
* number, we can do lots of creative stuff, like doing clever
* retransmits and sharding.
*/
if (r == 0) {
i += increment; /* <------ increment by 1 normally, more with shards/nics */
r = (unsigned)retries + 1;
}
} /* end of batch */
/* save our current location for resuming, if the user pressed
* to exit early */
parms->my_index = i;
/* If the user pressed , then we need to exit. In case
* the user wants to --resume the scan later, we save the current
* state in a file */
if (is_tx_done) {
break;
}
}
/*
* --infinite
* For load testing, go around and do this again
*/
if (masscan->is_infinite && !is_tx_done) {
seed++;
repeats++;
goto infinite;
}
/*
* Flush any untransmitted packets. High-speed mechanisms like Windows
* "sendq" and Linux's "PF_RING" queue packets and transmit many together,
* so there may be some packets that we've queued but not yet transmitted.
* This call makes sure they are transmitted.
*/
rawsock_flush(adapter);
/*
* Wait until the receive thread realizes the scan is over
*/
LOG(1, "[+] transmit thread #%u complete\n", parms->nic_index);
/*
* We are done transmitting. However, response packets will take several
* seconds to arrive. Therefore, sit in short loop waiting for those
* packets to arrive. Pressing a second time will exit this
* prematurely.
*/
while (!is_rx_done) {
unsigned k;
uint64_t batch_size;
for (k=0; k<1000; k++) {
/*
* Only send a few packets at a time, throttled according to the max
* --max-rate set by the user
*/
batch_size = throttler_next_batch(throttler, packets_sent);
/* Transmit packets from the receive thread */
stack_flush_packets( parms->stack, adapter,
&packets_sent,
&batch_size);
/* Make sure they've actually been transmitted, not just queued up for
* transmit */
rawsock_flush(adapter);
pixie_usleep(100);
}
}
/* Thread is about to exit */
parms->done_transmitting = 1;
LOG(1, "[+] exiting transmit thread #%u \n", parms->nic_index);
}
/***************************************************************************
***************************************************************************/
static unsigned
is_nic_port(const struct Masscan *masscan, unsigned ip)
{
unsigned i;
for (i=0; inic_count; i++)
if (is_my_port(&masscan->nic[i].src, ip))
return 1;
return 0;
}
static unsigned
is_ipv6_multicast(ipaddress ip_me)
{
/* If this is an IPv6 multicast packet, one sent to the IPv6
* address with a prefix of FF02::/16 */
return ip_me.version == 6 && (ip_me.ipv6.hi>>48ULL) == 0xFF02;
}
/***************************************************************************
*
* Asynchronous receive thread
*
* The transmit and receive threads run independently of each other. There
* is no record what was transmitted. Instead, the transmit thread sets a
* "SYN-cookie" in transmitted packets, which the receive thread will then
* use to match up requests with responses.
***************************************************************************/
static void
receive_thread(void *v)
{
struct ThreadPair *parms = (struct ThreadPair *)v;
const struct Masscan *masscan = parms->masscan;
struct Adapter *adapter = parms->adapter;
int data_link = stack_if_datalink(adapter);
struct Output *out;
struct DedupTable *dedup;
struct PcapFile *pcapfile = NULL;
struct TCP_ConnectionTable *tcpcon = 0;
uint64_t *status_synack_count;
uint64_t *status_tcb_count;
uint64_t entropy = masscan->seed;
struct ResetFilter *rf;
struct stack_t *stack = parms->stack;
struct source_t src = {0};
/* For reducing RST responses, see rstfilter_is_filter() below */
rf = rstfilter_create(entropy, 16384);
/* some status variables */
status_synack_count = MALLOC(sizeof(uint64_t));
*status_synack_count = 0;
parms->total_synacks = status_synack_count;
status_tcb_count = MALLOC(sizeof(uint64_t));
*status_tcb_count = 0;
parms->total_tcbs = status_tcb_count;
LOG(1, "[+] starting receive thread #%u\n", parms->nic_index);
/* Lock this thread to a CPU. Transmit threads are on even CPUs,
* receive threads on odd CPUs */
if (pixie_cpu_get_count() > 1) {
unsigned cpu_count = pixie_cpu_get_count();
unsigned cpu = parms->nic_index * 2 + 1;
while (cpu >= cpu_count) {
cpu -= cpu_count;
cpu++;
}
//TODO:
//pixie_cpu_set_affinity(cpu);
}
/*
* If configured, open a --pcap file for saving raw packets. This is
* so that we can debug scans, but also so that we can look at the
* strange things people send us. Note that we don't record transmitted
* packets, just the packets we've received.
*/
if (masscan->pcap_filename[0]) {
pcapfile = pcapfile_openwrite(masscan->pcap_filename, 1);
}
/*
* Open output. This is where results are reported when saving
* the --output-format to the --output-filename
*/
out = output_create(masscan, parms->nic_index);
/*
* Create deduplication table. This is so when somebody sends us
* multiple responses, we only record the first one.
*/
dedup = dedup_create();
/*
* Create a TCP connection table (per thread pair) for interacting with live
* connections when doing --banners
*/
if (masscan->is_banners) {
struct TcpCfgPayloads *pay;
size_t i;
/*
* Create TCP connection table
*/
tcpcon = tcpcon_create_table(
(size_t)((masscan->max_rate/5) / masscan->nic_count),
parms->stack,
&parms->tmplset->pkts[Proto_TCP],
output_report_banner,
out,
masscan->tcb.timeout,
masscan->seed
);
/*
* Initialize TCP scripting
*/
scripting_init_tcp(tcpcon, masscan->scripting.L);
/*
* Get the possible source IP addresses and ports that masscan
* might be using to transmit from.
*/
adapter_get_source_addresses(masscan, parms->nic_index, &src);
/*
* Set some flags [kludge]
*/
tcpcon_set_banner_flags(tcpcon,
masscan->is_capture_cert,
masscan->is_capture_servername,
masscan->is_capture_html,
masscan->is_capture_heartbleed,
masscan->is_capture_ticketbleed);
if (masscan->is_hello_smbv1)
tcpcon_set_parameter(tcpcon, "hello", 1, "smbv1");
if (masscan->is_hello_http)
tcpcon_set_parameter(tcpcon, "hello", 1, "http");
if (masscan->is_hello_ssl)
tcpcon_set_parameter(tcpcon, "hello", 1, "ssl");
if (masscan->is_heartbleed)
tcpcon_set_parameter(tcpcon, "heartbleed", 1, "1");
if (masscan->is_ticketbleed)
tcpcon_set_parameter(tcpcon, "ticketbleed", 1, "1");
if (masscan->is_poodle_sslv3)
tcpcon_set_parameter(tcpcon, "sslv3", 1, "1");
if (masscan->http.payload)
tcpcon_set_parameter( tcpcon,
"http-payload",
masscan->http.payload_length,
masscan->http.payload);
if (masscan->http.user_agent)
tcpcon_set_parameter( tcpcon,
"http-user-agent",
masscan->http.user_agent_length,
masscan->http.user_agent);
if (masscan->http.host)
tcpcon_set_parameter( tcpcon,
"http-host",
masscan->http.host_length,
masscan->http.host);
if (masscan->http.method)
tcpcon_set_parameter( tcpcon,
"http-method",
masscan->http.method_length,
masscan->http.method);
if (masscan->http.url)
tcpcon_set_parameter( tcpcon,
"http-url",
masscan->http.url_length,
masscan->http.url);
if (masscan->http.version)
tcpcon_set_parameter( tcpcon,
"http-version",
masscan->http.version_length,
masscan->http.version);
if (masscan->tcp_connection_timeout) {
char foo[64];
snprintf(foo, sizeof(foo), "%u", masscan->tcp_connection_timeout);
tcpcon_set_parameter( tcpcon,
"timeout",
strlen(foo),
foo);
}
if (masscan->tcp_hello_timeout) {
char foo[64];
snprintf(foo, sizeof(foo), "%u", masscan->tcp_hello_timeout);
tcpcon_set_parameter( tcpcon,
"hello-timeout",
strlen(foo),
foo);
}
for (i=0; ihttp.headers_count; i++) {
tcpcon_set_http_header(tcpcon,
masscan->http.headers[i].name,
masscan->http.headers[i].value_length,
masscan->http.headers[i].value,
http_field_replace);
}
for (i=0; ihttp.cookies_count; i++) {
tcpcon_set_http_header(tcpcon,
"Cookie",
masscan->http.cookies[i].value_length,
masscan->http.cookies[i].value,
http_field_add);
}
for (i=0; ihttp.remove_count; i++) {
tcpcon_set_http_header(tcpcon,
masscan->http.headers[i].name,
0,
0,
http_field_remove);
}
for (pay = masscan->payloads.tcp; pay; pay = pay->next) {
char name[64];
snprintf(name, sizeof(name), "hello-string[%u]", pay->port);
tcpcon_set_parameter( tcpcon,
name,
strlen(pay->payload_base64),
pay->payload_base64);
}
}
/*
* In "offline" mode, we don't have any receive threads, so simply
* wait until transmitter thread is done then go to the end
*/
if (masscan->is_offline) {
while (!is_rx_done)
pixie_usleep(10000);
parms->done_receiving = 1;
goto end;
}
/*
* Receive packets. This is where we catch any responses and print
* them to the terminal.
*/
LOG(2, "[+] THREAD: recv: starting main loop\n");
while (!is_rx_done) {
int status;
unsigned length;
unsigned secs;
unsigned usecs;
const unsigned char *px;
int err;
unsigned x;
struct PreprocessedInfo parsed;
ipaddress ip_me;
unsigned port_me;
ipaddress ip_them;
unsigned port_them;
unsigned seqno_me;
unsigned seqno_them;
unsigned cookie;
unsigned Q = 0;
/*
* RECEIVE
*
* This is the boring part of actually receiving a packet
*/
err = rawsock_recv_packet(
adapter,
&length,
&secs,
&usecs,
&px);
if (err != 0) {
if (tcpcon)
tcpcon_timeouts(tcpcon, (unsigned)time(0), 0);
continue;
}
/*
* Do any TCP event timeouts based on the current timestamp from
* the packet. For example, if the connection has been open for
* around 10 seconds, we'll close the connection. (--banners)
*/
if (tcpcon) {
tcpcon_timeouts(tcpcon, secs, usecs);
}
if (length > 1514)
continue;
/*
* "Preprocess" the response packet. This means to go through and
* figure out where the TCP/IP headers are and the locations of
* some fields, like IP address and port numbers.
*/
x = preprocess_frame(px, length, data_link, &parsed);
if (!x)
continue; /* corrupt packet */
ip_me = parsed.dst_ip;
ip_them = parsed.src_ip;
port_me = parsed.port_dst;
port_them = parsed.port_src;
seqno_them = TCP_SEQNO(px, parsed.transport_offset);
seqno_me = TCP_ACKNO(px, parsed.transport_offset);
assert(ip_me.version != 0);
assert(ip_them.version != 0);
switch (parsed.ip_protocol) {
case 132: /* SCTP */
cookie = syn_cookie(ip_them, port_them | (Proto_SCTP<<16), ip_me, port_me, entropy) & 0xFFFFFFFF;
break;
default:
cookie = syn_cookie(ip_them, port_them, ip_me, port_me, entropy) & 0xFFFFFFFF;
}
/* verify: my IP address */
if (!is_my_ip(stack->src, ip_me)) {
/* NDP Neighbor Solicitations don't come to our IP address, but to
* a multicast address */
if (is_ipv6_multicast(ip_me)) {
if (parsed.found == FOUND_NDPv6 && parsed.opcode == 135) {
stack_ndpv6_incoming_request(stack, &parsed, px, length);
}
}
continue;
}
/*
* Handle non-TCP protocols
*/
switch (parsed.found) {
case FOUND_NDPv6:
switch (parsed.opcode) {
case 133: /* Router Solicitation */
/* Ignore router solicitations, since we aren't a router */
continue;
case 134: /* Router advertisement */
/* TODO: We need to process router advertisements while scanning
* so that we can print warning messages if router information
* changes while scanning. */
continue;
case 135: /* Neighbor Solicitation */
/* When responses come back from our scans, the router will send us
* these packets. We need to respond to them, so that the router
* can then forward the packets to us. If we don't respond, we'll
* get no responses. */
stack_ndpv6_incoming_request(stack, &parsed, px, length);
continue;
case 136: /* Neighbor Advertisement */
/* TODO: If doing an --ndpscan, the scanner subsystem needs to deal
* with these */
continue;
case 137: /* Redirect */
/* We ignore these, since we really don't have the capability to send
* packets to one router for some destinations and to another router
* for other destinations */
continue;
default:
break;
}
continue;
case FOUND_ARP:
LOGip(2, ip_them, 0, "-> ARP [%u] \n", px[parsed.found_offset]);
switch (parsed.opcode) {
case 1: /* request */
/* This function will transmit a "reply" to somebody's ARP request
* for our IP address (as part of our user-mode TCP/IP).
* Since we completely bypass the TCP/IP stack, we have to handle ARPs
* ourself, or the router will lose track of us.*/
stack_arp_incoming_request(stack,
ip_me.ipv4,
parms->source_mac,
px, length);
break;
case 2: /* response */
/* This is for "arp scan" mode, where we are ARPing targets rather
* than port scanning them */
/* If we aren't doing an ARP scan, then ignore ARP responses */
if (!masscan->scan_type.arp)
break;
/* If this response isn't in our range, then ignore it */
if (!rangelist_is_contains(&masscan->targets.ipv4, ip_them.ipv4))
break;
/* Ignore duplicates */
if (dedup_is_duplicate(dedup, ip_them, 0, ip_me, 0))
continue;
/* ...everything good, so now report this response */
arp_recv_response(out, secs, px, length, &parsed);
break;
}
continue;
case FOUND_UDP:
case FOUND_DNS:
if (!is_nic_port(masscan, port_me))
continue;
if (parms->masscan->nmap.packet_trace)
packet_trace(stdout, parms->pt_start, px, length, 0);
handle_udp(out, secs, px, length, &parsed, entropy);
continue;
case FOUND_ICMP:
handle_icmp(out, secs, px, length, &parsed, entropy);
continue;
case FOUND_SCTP:
handle_sctp(out, secs, px, length, cookie, &parsed, entropy);
break;
case FOUND_OPROTO: /* other IP proto */
handle_oproto(out, secs, px, length, &parsed, entropy);
break;
case FOUND_TCP:
/* fall down to below */
break;
default:
continue;
}
/* verify: my port number */
if (!is_my_port(stack->src, port_me))
continue;
if (parms->masscan->nmap.packet_trace)
packet_trace(stdout, parms->pt_start, px, length, 0);
Q = 0;
/* Save raw packet in --pcap file */
if (pcapfile) {
pcapfile_writeframe(
pcapfile,
px,
length,
length,
secs,
usecs);
}
{
char buf[64];
LOGip(5, ip_them, port_them, "-> TCP ackno=0x%08x flags=0x%02x(%s)\n",
seqno_me,
TCP_FLAGS(px, parsed.transport_offset),
reason_string(TCP_FLAGS(px, parsed.transport_offset), buf, sizeof(buf)));
}
/* If recording --banners, create a new "TCP Control Block (TCB)" */
if (tcpcon) {
struct TCP_Control_Block *tcb;
/* does a TCB already exist for this connection? */
tcb = tcpcon_lookup_tcb(tcpcon,
ip_me, ip_them,
port_me, port_them);
if (TCP_IS_SYNACK(px, parsed.transport_offset)) {
if (cookie != seqno_me - 1) {
ipaddress_formatted_t fmt = ipaddress_fmt(ip_them);
LOG(0, "%s - bad cookie: ackno=0x%08x expected=0x%08x\n",
fmt.string, seqno_me-1, cookie);
continue;
}
if (tcb == NULL) {
tcb = tcpcon_create_tcb(tcpcon,
ip_me, ip_them,
port_me, port_them,
seqno_me, seqno_them+1,
parsed.ip_ttl, NULL,
secs, usecs);
(*status_tcb_count)++;
}
Q += stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_SYNACK,
0, 0, secs, usecs, seqno_them+1, seqno_me);
} else if (tcb) {
/* If this is an ACK, then handle that first */
if (TCP_IS_ACK(px, parsed.transport_offset)) {
Q += stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_ACK,
0, 0, secs, usecs, seqno_them, seqno_me);
}
/* If this contains payload, handle that second */
if (parsed.app_length) {
Q += stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_DATA,
px + parsed.app_offset, parsed.app_length,
secs, usecs, seqno_them, seqno_me);
}
/* If this is a FIN, handle that. Note that ACK +
* payload + FIN can come together */
if (TCP_IS_FIN(px, parsed.transport_offset)
&& !TCP_IS_RST(px, parsed.transport_offset)) {
Q += stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_FIN,
0, 0,
secs, usecs,
seqno_them + parsed.app_length, /* the FIN comes after any data in the packet */
seqno_me);
}
/* If this is a RST, then we'll be closing the connection */
if (TCP_IS_RST(px, parsed.transport_offset)) {
Q += stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_RST,
0, 0, secs, usecs, seqno_them, seqno_me);
}
} else if (TCP_IS_FIN(px, parsed.transport_offset)) {
ipaddress_formatted_t fmt;
/*
* NO TCB!
* This happens when we've sent a FIN, deleted our connection,
* but the other side didn't get the packet.
*/
fmt = ipaddress_fmt(ip_them);
LOG(4, "%s: received FIN but no TCB\n", fmt.string);
if (TCP_IS_RST(px, parsed.transport_offset))
; /* ignore if it's own TCP flag is set */
else {
int is_suppress;
is_suppress = rstfilter_is_filter(rf, ip_me, port_me, ip_them, port_them);
if (!is_suppress)
tcpcon_send_RST(
tcpcon,
ip_me, ip_them,
port_me, port_them,
seqno_them, seqno_me);
}
}
}
if (Q == 0)
; //printf("\nerr\n");
if (TCP_IS_SYNACK(px, parsed.transport_offset)
|| TCP_IS_RST(px, parsed.transport_offset)) {
/* figure out the status */
status = PortStatus_Unknown;
if (TCP_IS_SYNACK(px, parsed.transport_offset))
status = PortStatus_Open;
if (TCP_IS_RST(px, parsed.transport_offset)) {
status = PortStatus_Closed;
}
/* verify: syn-cookies */
if (cookie != seqno_me - 1) {
ipaddress_formatted_t fmt = ipaddress_fmt(ip_them);
LOG(2, "%s - bad cookie: ackno=0x%08x expected=0x%08x\n",
fmt.string, seqno_me-1, cookie);
continue;
}
/* verify: ignore duplicates */
if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me))
continue;
/* keep statistics on number received */
if (TCP_IS_SYNACK(px, parsed.transport_offset))
(*status_synack_count)++;
/*
* This is where we do the output
*/
output_report_status(
out,
global_now,
status,
ip_them,
6, /* ip proto = tcp */
port_them,
px[parsed.transport_offset + 13], /* tcp flags */
parsed.ip_ttl,
parsed.mac_src
);
/*
* Send RST so other side isn't left hanging (only doing this in
* complete stateless mode where we aren't tracking banners)
*/
if (tcpcon == NULL && !masscan->is_noreset)
tcp_send_RST(
&parms->tmplset->pkts[Proto_TCP],
parms->stack,
ip_them, ip_me,
port_them, port_me,
0, seqno_me);
}
}
LOG(1, "[+] exiting receive thread #%u \n", parms->nic_index);
/*
* cleanup
*/
end:
if (tcpcon)
tcpcon_destroy_table(tcpcon);
dedup_destroy(dedup);
output_destroy(out);
if (pcapfile)
pcapfile_close(pcapfile);
/*TODO: free stack packet buffers */
/* Thread is about to exit */
parms->done_receiving = 1;
}
/***************************************************************************
* We trap the so that instead of exiting immediately, we sit in
* a loop for a few seconds waiting for any late response. But, the user
* can press a second time to exit that waiting.
***************************************************************************/
static void control_c_handler(int x)
{
static unsigned control_c_pressed = 0;
static unsigned control_c_pressed_again = 0;
if (control_c_pressed == 0) {
fprintf(stderr,
"waiting several seconds to exit..."
" \n"
);
fflush(stderr);
control_c_pressed = 1+x;
is_tx_done = control_c_pressed;
} else {
if (is_rx_done) {
fprintf(stderr, "\nERROR: threads not exiting %d\n", is_rx_done);
if (is_rx_done++ > 1)
exit(1);
} else {
control_c_pressed_again = 1;
is_rx_done = control_c_pressed_again;
}
}
}
/***************************************************************************
* Called from main() to initiate the scan.
* Launches the 'transmit_thread()' and 'receive_thread()' and waits for
* them to exit.
***************************************************************************/
static int
main_scan(struct Masscan *masscan)
{
struct ThreadPair parms_array[8];
uint64_t count_ips;
uint64_t count_ports;
uint64_t range;
unsigned index;
time_t now = time(0);
struct Status status;
uint64_t min_index = UINT64_MAX;
struct MassVulnCheck *vulncheck = NULL;
struct stack_t *stack;
memset(parms_array, 0, sizeof(parms_array));
/*
* Vuln check initialization
*/
if (masscan->vuln_name) {
unsigned i;
unsigned is_error;
vulncheck = vulncheck_lookup(masscan->vuln_name);
/* If no ports specified on command-line, grab default ports */
is_error = 0;
if (rangelist_count(&masscan->targets.ports) == 0)
rangelist_parse_ports(&masscan->targets.ports, vulncheck->ports, &is_error, 0);
/* Kludge: change normal port range to vulncheck range */
for (i=0; itargets.ports.count; i++) {
struct Range *r = &masscan->targets.ports.list[i];
r->begin = (r->begin&0xFFFF) | Templ_VulnCheck;
r->end = (r->end & 0xFFFF) | Templ_VulnCheck;
}
}
/*
* Initialize the task size
*/
count_ips = rangelist_count(&masscan->targets.ipv4) + range6list_count(&masscan->targets.ipv6).lo;
if (count_ips == 0) {
LOG(0, "FAIL: target IP address list empty\n");
LOG(0, " [hint] try something like \"--range 10.0.0.0/8\"\n");
LOG(0, " [hint] try something like \"--range 192.168.0.100-192.168.0.200\"\n");
return 1;
}
count_ports = rangelist_count(&masscan->targets.ports);
if (count_ports == 0) {
LOG(0, "FAIL: no ports were specified\n");
LOG(0, " [hint] try something like \"-p80,8000-9000\"\n");
LOG(0, " [hint] try something like \"--ports 0-65535\"\n");
return 1;
}
range = count_ips * count_ports;
range += (uint64_t)(masscan->retries * range);
/*
* If doing an ARP scan, then don't allow port scanning
*/
if (rangelist_is_contains(&masscan->targets.ports, Templ_ARP)) {
if (masscan->targets.ports.count != 1) {
LOG(0, "FAIL: cannot arpscan and portscan at the same time\n");
return 1;
}
}
/*
* If the IP address range is very big, then require that that the
* user apply an exclude range
*/
if (count_ips > 1000000000ULL && rangelist_count(&masscan->exclude.ipv4) == 0) {
LOG(0, "FAIL: range too big, need confirmation\n");
LOG(0, " [hint] to prevent accidents, at least one --exclude must be specified\n");
LOG(0, " [hint] use \"--exclude 255.255.255.255\" as a simple confirmation\n");
exit(1);
}
/*
* trim the nmap UDP payloads down to only those ports we are using. This
* makes lookups faster at high packet rates.
*/
payloads_udp_trim(masscan->payloads.udp, &masscan->targets);
payloads_oproto_trim(masscan->payloads.oproto, &masscan->targets);
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif
/*
* Start scanning threats for each adapter
*/
for (index=0; indexnic_count; index++) {
struct ThreadPair *parms = &parms_array[index];
int err;
parms->masscan = masscan;
parms->nic_index = index;
parms->my_index = masscan->resume.index;
parms->done_transmitting = 0;
parms->done_receiving = 0;
/* needed for --packet-trace option so that we know when we started
* the scan */
parms->pt_start = 1.0 * pixie_gettime() / 1000000.0;
/*
* Turn the adapter on, and get the running configuration
*/
err = masscan_initialize_adapter(
masscan,
index,
&parms->source_mac,
&parms->router_mac_ipv4,
&parms->router_mac_ipv6
);
if (err != 0)
exit(1);
parms->adapter = masscan->nic[index].adapter;
if (!masscan->nic[index].is_usable) {
LOG(0, "FAIL: failed to detect IP of interface\n");
LOG(0, " [hint] did you spell the name correctly?\n");
LOG(0, " [hint] if it has no IP address, "
"manually set with \"--adapter-ip 192.168.100.5\"\n");
exit(1);
}
/*
* Initialize the TCP packet template. The way this works is that
* we parse an existing TCP packet, and use that as the template for
* scanning. Then, we adjust the template with additional features,
* such as the IP address and so on.
*/
parms->tmplset->vulncheck = vulncheck;
template_packet_init(
parms->tmplset,
parms->source_mac,
parms->router_mac_ipv4,
parms->router_mac_ipv6,
masscan->payloads.udp,
masscan->payloads.oproto,
stack_if_datalink(masscan->nic[index].adapter),
masscan->seed,
masscan->templ_opts);
/*
* Set the "source port" of everything we transmit.
*/
if (masscan->nic[index].src.port.range == 0) {
unsigned port = 40000 + now % 20000;
masscan->nic[index].src.port.first = port;
masscan->nic[index].src.port.last = port + 16;
masscan->nic[index].src.port.range = 16;
}
stack = stack_create(parms->source_mac, &masscan->nic[index].src);
parms->stack = stack;
/*
* Set the "TTL" (IP time-to-live) of everything we send.
*/
if (masscan->nmap.ttl)
template_set_ttl(parms->tmplset, masscan->nmap.ttl);
if (masscan->nic[0].is_vlan)
template_set_vlan(parms->tmplset, masscan->nic[0].vlan_id);
/*
* trap to pause
*/
signal(SIGINT, control_c_handler);
}
/*
* Print helpful text
*/
{
char buffer[80];
struct tm x;
now = time(0);
safe_gmtime(&x, &now);
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S GMT", &x);
LOG(0, "Starting masscan " MASSCAN_VERSION " (http://bit.ly/14GZzcT) at %s\n",
buffer);
if (count_ports == 1 && \
masscan->targets.ports.list->begin == Templ_ICMP_echo && \
masscan->targets.ports.list->end == Templ_ICMP_echo)
{ /* ICMP only */
//LOG(0, " -- forced options: -sn -n --randomize-hosts -v --send-eth\n");
LOG(0, "Initiating ICMP Echo Scan\n");
LOG(0, "Scanning %u hosts\n",(unsigned)count_ips);
}
else /* This could actually also be a UDP only or mixed UDP/TCP/ICMP scan */
{
//LOG(0, " -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth\n");
LOG(0, "Initiating SYN Stealth Scan\n");
LOG(0, "Scanning %u hosts [%u port%s/host]\n",
(unsigned)count_ips, (unsigned)count_ports, (count_ports==1)?"":"s");
}
}
/*
* Start all the threads
*/
for (index=0; indexnic_count; index++) {
struct ThreadPair *parms = &parms_array[index];
/*
* Start the scanning thread.
* THIS IS WHERE THE PROGRAM STARTS SPEWING OUT PACKETS AT A HIGH
* RATE OF SPEED.
*/
parms->thread_handle_xmit = pixie_begin_thread(transmit_thread, 0, parms);
/*
* Start the MATCHING receive thread. Transmit and receive threads
* come in matching pairs.
*/
parms->thread_handle_recv = pixie_begin_thread(receive_thread, 0, parms);
}
/*
* Now wait for to be pressed OR for threads to exit
*/
pixie_usleep(1000 * 100);
LOG(1, "[+] waiting for threads to finish\n");
status_start(&status);
status.is_infinite = masscan->is_infinite;
while (!is_tx_done && masscan->output.is_status_updates) {
unsigned i;
double rate = 0;
uint64_t total_tcbs = 0;
uint64_t total_synacks = 0;
uint64_t total_syns = 0;
/* Find the minimum index of all the threads */
min_index = UINT64_MAX;
for (i=0; inic_count; i++) {
struct ThreadPair *parms = &parms_array[i];
if (min_index > parms->my_index)
min_index = parms->my_index;
rate += parms->throttler->current_rate;
if (parms->total_tcbs)
total_tcbs += *parms->total_tcbs;
if (parms->total_synacks)
total_synacks += *parms->total_synacks;
if (parms->total_syns)
total_syns += *parms->total_syns;
}
if (min_index >= range && !masscan->is_infinite) {
/* Note: This is how we can tell the scan has ended */
is_tx_done = 1;
}
/*
* update screen about once per second with statistics,
* namely packets/second.
*/
if (masscan->output.is_status_updates)
status_print(&status, min_index, range, rate,
total_tcbs, total_synacks, total_syns,
0, masscan->output.is_status_ndjson);
/* Sleep for almost a second */
pixie_mssleep(750);
}
/*
* If we haven't completed the scan, then save the resume
* information.
*/
if (min_index < count_ips * count_ports) {
masscan->resume.index = min_index;
/* Write current settings to "paused.conf" so that the scan can be restarted */
masscan_save_state(masscan);
}
/*
* Now wait for all threads to exit
*/
now = time(0);
for (;;) {
unsigned transmit_count = 0;
unsigned receive_count = 0;
unsigned i;
double rate = 0;
uint64_t total_tcbs = 0;
uint64_t total_synacks = 0;
uint64_t total_syns = 0;
/* Find the minimum index of all the threads */
min_index = UINT64_MAX;
for (i=0; inic_count; i++) {
struct ThreadPair *parms = &parms_array[i];
if (min_index > parms->my_index)
min_index = parms->my_index;
rate += parms->throttler->current_rate;
if (parms->total_tcbs)
total_tcbs += *parms->total_tcbs;
if (parms->total_synacks)
total_synacks += *parms->total_synacks;
if (parms->total_syns)
total_syns += *parms->total_syns;
}
if (time(0) - now >= masscan->wait) {
is_rx_done = 1;
}
if (time(0) - now - 10 > masscan->wait) {
LOG(0, "[-] Passed the wait window but still running, forcing exit...\n");
exit(0);
}
if (masscan->output.is_status_updates) {
status_print(&status, min_index, range, rate,
total_tcbs, total_synacks, total_syns,
masscan->wait - (time(0) - now),
masscan->output.is_status_ndjson);
for (i=0; inic_count; i++) {
struct ThreadPair *parms = &parms_array[i];
transmit_count += parms->done_transmitting;
receive_count += parms->done_receiving;
}
pixie_mssleep(250);
if (transmit_count < masscan->nic_count)
continue;
is_tx_done = 1;
is_rx_done = 1;
if (receive_count < masscan->nic_count)
continue;
} else {
/* [AFL-fuzz]
* Join the threads, which doesn't allow us to print out
* status messages, but allows us to exit cleanly without
* any waiting */
for (i=0; inic_count; i++) {
struct ThreadPair *parms = &parms_array[i];
pixie_thread_join(parms->thread_handle_xmit);
parms->thread_handle_xmit = 0;
pixie_thread_join(parms->thread_handle_recv);
parms->thread_handle_recv = 0;
}
is_tx_done = 1;
is_rx_done = 1;
}
break;
}
/*
* Now cleanup everything
*/
status_finish(&status);
if (!masscan->output.is_status_updates) {
uint64_t usec_now = pixie_gettime();
printf("%u milliseconds elapsed\n", (unsigned)((usec_now - usec_start)/1000));
}
LOG(1, "[+] all threads have exited \n");
return 0;
}
/***************************************************************************
***************************************************************************/
int main(int argc, char *argv[])
{
struct Masscan masscan[1];
unsigned i;
int has_target_addresses = 0;
int has_target_ports = 0;
usec_start = pixie_gettime();
#if defined(WIN32)
{WSADATA x; WSAStartup(0x101, &x);}
#endif
global_now = time(0);
/* Set system to report debug information on crash */
{
int is_backtrace = 1;
for (i=1; i<(unsigned)argc; i++) {
if (strcmp(argv[i], "--nobacktrace") == 0)
is_backtrace = 0;
}
if (is_backtrace)
pixie_backtrace_init(argv[0]);
}
/*
* Initialize those defaults that aren't zero
*/
memset(masscan, 0, sizeof(*masscan));
/* 14 rounds seem to give way better statistical distribution than 4 with a
very low impact on scan rate */
masscan->blackrock_rounds = 14;
masscan->output.is_show_open = 1; /* default: show syn-ack, not rst */
masscan->output.is_status_updates = 1; /* default: show status updates */
masscan->wait = 10; /* how long to wait for responses when done */
masscan->max_rate = 100.0; /* max rate = hundred packets-per-second */
masscan->nic_count = 1;
masscan->shard.one = 1;
masscan->shard.of = 1;
masscan->min_packet_size = 60;
masscan->redis.password = NULL;
masscan->payloads.udp = payloads_udp_create();
masscan->payloads.oproto = payloads_oproto_create();
safe_strcpy( masscan->output.rotate.directory,
sizeof(masscan->output.rotate.directory),
".");
masscan->is_capture_cert = 1;
/*
* Pre-parse the command-line
*/
if (masscan_conf_contains("--readscan", argc, argv)) {
masscan->is_readscan = 1;
}
/*
* On non-Windows systems, read the defaults from the file in
* the /etc directory. These defaults will contain things
* like the output directory, max packet rates, and so on. Most
* importantly, the master "--excludefile" might be placed here,
* so that blacklisted ranges won't be scanned, even if the user
* makes a mistake
*/
#if !defined(WIN32)
if (!masscan->is_readscan) {
if (access("/etc/masscan/masscan.conf", 0) == 0) {
masscan_read_config_file(masscan, "/etc/masscan/masscan.conf");
}
}
#endif
/*
* Read in the configuration from the command-line. We are looking for
* either options or a list of IPv4 address ranges.
*/
masscan_command_line(masscan, argc, argv);
if (masscan->seed == 0)
masscan->seed = get_entropy(); /* entropy for randomness */
/*
* Load database files like "nmap-payloads" and "nmap-service-probes"
*/
masscan_load_database_files(masscan);
/*
* Load the scripting engine if needed and run those that were
* specified.
*/
if (masscan->is_scripting)
scripting_init(masscan);
/* We need to do a separate "raw socket" initialization step. This is
* for Windows and PF_RING. */
if (pcap_init() != 0)
LOG(2, "libpcap: failed to load\n");
rawsock_init();
/* Init some protocol parser data structures */
snmp_init();
x509_init();
/*
* Apply excludes. People ask us not to scan them, so we maintain a list
* of their ranges, and when doing wide scans, add the exclude list to
* prevent them from being scanned.
*/
has_target_addresses = massip_has_ipv4_targets(&masscan->targets) || massip_has_ipv6_targets(&masscan->targets);
has_target_ports = massip_has_target_ports(&masscan->targets);
massip_apply_excludes(&masscan->targets, &masscan->exclude);
if (!has_target_ports && masscan->op == Operation_ListScan)
massip_add_port_string(&masscan->targets, "80", 0);
/* Optimize target selection so it's a quick binary search instead
* of walking large memory tables. When we scan the entire Internet
* our --excludefile will chop up our pristine 0.0.0.0/0 range into
* hundreds of subranges. This allows us to grab addresses faster. */
massip_optimize(&masscan->targets);
/* FIXME: we only support 63-bit scans at the current time.
* This is big enough for the IPv4 Internet, where scanning
* for all TCP ports on all IPv4 addresses results in a 48-bit
* scan, but this isn't big enough even for a single port on
* an IPv6 subnet (which are 64-bits in size, usually). However,
* even at millions of packets per second scanning rate, you still
* can't complete a 64-bit scan in a reasonable amount of time.
* Nor would you want to attempt the feat, as it would overload
* the target IPv6 subnet. Since implementing this would be
* difficult for 32-bit processors, for now, I'm going to stick
* to a simple 63-bit scan.
*/
if (massint128_bitcount(massip_range(&masscan->targets)) > 63) {
fprintf(stderr, "[-] FAIL: scan range too large, max is 63-bits, requested is %u bits\n",
massint128_bitcount(massip_range(&masscan->targets)));
fprintf(stderr, " Hint: scan range is number of IP addresses times number of ports\n");
fprintf(stderr, " Hint: IPv6 subnet must be at least /66 \n");
exit(1);
}
/*
* Once we've read in the configuration, do the operation that was
* specified
*/
switch (masscan->op) {
case Operation_Default:
/* Print usage info and exit */
masscan_usage();
break;
case Operation_Scan:
/*
* THIS IS THE NORMAL THING
*/
if (rangelist_count(&masscan->targets.ipv4) == 0 && massint128_is_zero(range6list_count(&masscan->targets.ipv6))) {
/* We check for an empty target list here first, before the excludes,
* so that we can differentiate error messages after excludes, in case
* the user specified addresses, but they were removed by excludes. */
LOG(0, "FAIL: target IP address list empty\n");
if (has_target_addresses) {
LOG(0, " [hint] all addresses were removed by exclusion ranges\n");
} else {
LOG(0, " [hint] try something like \"--range 10.0.0.0/8\"\n");
LOG(0, " [hint] try something like \"--range 192.168.0.100-192.168.0.200\"\n");
}
exit(1);
}
if (rangelist_count(&masscan->targets.ports) == 0) {
if (has_target_ports) {
LOG(0, " [hint] all ports were removed by exclusion ranges\n");
} else {
LOG(0, "FAIL: no ports were specified\n");
LOG(0, " [hint] try something like \"-p80,8000-9000\"\n");
LOG(0, " [hint] try something like \"--ports 0-65535\"\n");
}
return 1;
}
return main_scan(masscan);
case Operation_ListScan:
/* Create a randomized list of IP addresses */
main_listscan(masscan);
return 0;
case Operation_List_Adapters:
/* List the network adapters we might want to use for scanning */
rawsock_list_adapters();
break;
case Operation_DebugIF:
for (i=0; inic_count; i++)
rawsock_selftest_if(masscan->nic[i].ifname);
return 0;
case Operation_ReadRange:
main_readrange(masscan);
return 0;
case Operation_ReadScan:
{
unsigned start;
unsigned stop;
/* find first file */
for (start=1; start<(unsigned)argc; start++) {
if (memcmp(argv[start], "--readscan", 10) == 0) {
start++;
break;
}
}
/* find last file */
for (stop=start+1; stop<(unsigned)argc && argv[stop][0] != '-'; stop++)
;
/*
* read the binary files, and output them again depending upon
* the output parameters
*/
readscan_binary_scanfile(masscan, start, stop, argv);
}
break;
case Operation_Benchmark:
printf("=== benchmarking (%u-bits) ===\n\n", (unsigned)sizeof(void*)*8);
blackrock_benchmark(masscan->blackrock_rounds);
blackrock2_benchmark(masscan->blackrock_rounds);
smack_benchmark();
exit(1);
break;
case Operation_Echo:
masscan_echo(masscan, stdout, 0);
exit(0);
break;
case Operation_EchoAll:
masscan_echo(masscan, stdout, 0);
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
*/
{
int x = 0;
extern int proto_isakmp_selftest(void);
x += massip_selftest();
x += ranges6_selftest();
x += dedup_selftest();
x += checksum_selftest();
x += ipv4address_selftest();
x += ipv6address_selftest();
x += proto_coap_selftest();
x += smack_selftest();
x += sctp_selftest();
x += base64_selftest();
x += banner1_selftest();
x += output_selftest();
x += siphash24_selftest();
x += ntp_selftest();
x += snmp_selftest();
x += proto_isakmp_selftest();
x += templ_payloads_selftest();
x += blackrock_selftest();
x += rawsock_selftest();
x += lcg_selftest();
x += template_selftest();
x += ranges_selftest();
x += massip_parse_selftest();
x += pixie_time_selftest();
x += rte_ring_selftest();
x += mainconf_selftest();
x += zeroaccess_selftest();
x += nmapserviceprobes_selftest();
x += rstfilter_selftest();
x += masscan_app_selftest();
if (x != 0) {
/* one of the selftests failed, so return error */
fprintf(stderr, "regression test: failed :( \n");
return 1;
} else {
fprintf(stderr, "regression test: success!\n");
return 0;
}
}
break;
}
return 0;
}
================================================
FILE: src/masscan-app.c
================================================
#include "masscan-app.h"
#include "util-safefunc.h"
/******************************************************************************
* When outputting results, we call this function to print out the type of
* banner that we've collected
******************************************************************************/
const char *
masscan_app_to_string(enum ApplicationProtocol proto)
{
static char tmp[64];
switch (proto) {
case PROTO_NONE: return "unknown";
case PROTO_HEUR: return "unknown";
case PROTO_SSH1: return "ssh";
case PROTO_SSH2: return "ssh";
case PROTO_HTTP: return "http";
case PROTO_FTP: return "ftp";
case PROTO_DNS_VERSIONBIND: return "dns-ver";
case PROTO_SNMP: return "snmp";
case PROTO_NBTSTAT: return "nbtstat";
case PROTO_SSL3: return "ssl";
case PROTO_SMB: return "smb";
case PROTO_SMTP: return "smtp";
case PROTO_POP3: return "pop";
case PROTO_IMAP4: return "imap";
case PROTO_UDP_ZEROACCESS: return "zeroaccess";
case PROTO_X509_CERT: return "X509";
case PROTO_X509_CACERT: return "X509CA";
case PROTO_HTML_TITLE: return "title";
case PROTO_HTML_FULL: return "html";
case PROTO_NTP: return "ntp";
case PROTO_VULN: return "vuln";
case PROTO_HEARTBLEED: return "heartbleed";
case PROTO_TICKETBLEED: return "ticketbleed";
case PROTO_VNC_OLD: return "vnc";
case PROTO_SAFE: return "safe";
case PROTO_MEMCACHED: return "memcached";
case PROTO_SCRIPTING: return "scripting";
case PROTO_VERSIONING: return "versioning";
case PROTO_COAP: return "coap";
case PROTO_TELNET: return "telnet";
case PROTO_RDP: return "rdp";
case PROTO_HTTP_SERVER: return "http.server";
case PROTO_MC: return "minecraft";
case PROTO_VNC_RFB: return "vnc";
case PROTO_VNC_INFO: return "vnc-info";
case PROTO_ISAKMP: return "isakmp";
case PROTO_ERROR: return "error";
default:
snprintf(tmp, sizeof(tmp), "(%u)", proto);
return tmp;
}
}
/******************************************************************************
******************************************************************************/
enum ApplicationProtocol
masscan_string_to_app(const char *str)
{
const static struct {
const char *name;
enum ApplicationProtocol value;
} list[] = {
{"ssh1", PROTO_SSH1},
{"ssh2", PROTO_SSH2},
{"ssh", PROTO_SSH2},
{"http", PROTO_HTTP},
{"ftp", PROTO_FTP},
{"dns-ver", PROTO_DNS_VERSIONBIND},
{"snmp", PROTO_SNMP},
{"nbtstat", PROTO_NBTSTAT},
{"ssl", PROTO_SSL3},
{"smtp", PROTO_SMTP},
{"smb", PROTO_SMB},
{"pop", PROTO_POP3},
{"imap", PROTO_IMAP4},
{"x509", PROTO_X509_CERT},
{"x509ca", PROTO_X509_CACERT},
{"zeroaccess", PROTO_UDP_ZEROACCESS},
{"title", PROTO_HTML_TITLE},
{"html", PROTO_HTML_FULL},
{"ntp", PROTO_NTP},
{"vuln", PROTO_VULN},
{"heartbleed", PROTO_HEARTBLEED},
{"ticketbleed", PROTO_TICKETBLEED},
{"vnc-old", PROTO_VNC_OLD},
{"safe", PROTO_SAFE},
{"memcached", PROTO_MEMCACHED},
{"scripting", PROTO_SCRIPTING},
{"versioning", PROTO_VERSIONING},
{"coap", PROTO_COAP},
{"telnet", PROTO_TELNET},
{"rdp", PROTO_RDP},
{"http.server", PROTO_HTTP_SERVER},
{"minecraft", PROTO_MC},
{"vnc", PROTO_VNC_RFB},
{"vnc-info", PROTO_VNC_INFO},
{"isakmp", PROTO_ISAKMP},
{0,0}
};
size_t i;
for (i=0; list[i].name; i++) {
if (strcmp(str, list[i].name) == 0)
return list[i].value;
}
return 0;
}
int
masscan_app_selftest(void) {
static const struct {
unsigned enumid;
unsigned expected;
} tests[] = {
{PROTO_SNMP, 7},
{PROTO_X509_CERT, 15},
{PROTO_HTTP_SERVER, 31},
{0,0}
};
size_t i;
/* The ENUM contains fixed values in external files,
* so programmers should only add onto its end, not
* the middle. This self-test will verify that
* a programmer hasn't made this mistake.
*/
for (i=0; tests[i].enumid != 0; i++) {
unsigned enumid = tests[i].enumid;
unsigned expected = tests[i].expected;
/* YOU ADDED AN ENUM IN THE MIDDLE INSTEAD ON THE END OF THE LIST */
if (enumid != expected) {
fprintf(stderr, "[-] %s:%u fail\n", __FILE__, (unsigned)__LINE__);
fprintf(stderr, "[-] enum expected=%u, found=%u\n", 30, PROTO_HTTP_SERVER);
return 1;
}
}
return 0;
}
================================================
FILE: src/masscan-app.h
================================================
#ifndef MASSCAN_APP_H
#define MASSCAN_APP_H
/*
* WARNING: these constants are used in files, so don't change the values.
* Add new ones onto the end
*/
enum ApplicationProtocol {
PROTO_NONE,
PROTO_HEUR,
PROTO_SSH1,
PROTO_SSH2,
PROTO_HTTP,
PROTO_FTP,
PROTO_DNS_VERSIONBIND,
PROTO_SNMP, /* 7 - simple network management protocol, udp/161 */
PROTO_NBTSTAT, /* 8 - netbios, udp/137 */
PROTO_SSL3,
PROTO_SMB, /* 10 - SMB tcp/139 and tcp/445 */
PROTO_SMTP, /* 11 - transfering email */
PROTO_POP3, /* 12 - fetching email */
PROTO_IMAP4, /* 13 - fetching email */
PROTO_UDP_ZEROACCESS,
PROTO_X509_CERT, /* 15 - just the cert */
PROTO_X509_CACERT,
PROTO_HTML_TITLE,
PROTO_HTML_FULL,
PROTO_NTP, /* 19 - network time protocol, udp/123 */
PROTO_VULN,
PROTO_HEARTBLEED,
PROTO_TICKETBLEED,
PROTO_VNC_OLD,
PROTO_SAFE,
PROTO_MEMCACHED, /* 25 - memcached */
PROTO_SCRIPTING,
PROTO_VERSIONING,
PROTO_COAP, /* 28 - constrained app proto, udp/5683, RFC7252 */
PROTO_TELNET, /* 29 - ye old remote terminal */
PROTO_RDP, /* 30 - Microsoft Remote Desktop Protocol tcp/3389 */
PROTO_HTTP_SERVER, /* 31 - HTTP "Server:" field */
PROTO_MC, /* 32 - Minecraft server */
PROTO_VNC_RFB,
PROTO_VNC_INFO,
PROTO_ISAKMP, /* 35 - IPsec key exchange */
PROTO_ERROR,
PROTO_end_of_list /* must be last one */
};
const char *
masscan_app_to_string(enum ApplicationProtocol proto);
enum ApplicationProtocol
masscan_string_to_app(const char *str);
int
masscan_app_selftest(void);
#endif
================================================
FILE: src/masscan-status.h
================================================
#ifndef MASSCAN_STATUS_H
#define MASSCAN_STATUS_H
#if 0
enum PortStatus {
Port_Unknown,
Port_Open,
Port_Closed,
Port_IcmpEchoResponse,
Port_UdpOpen,
Port_UdpClosed,
Port_SctpOpen,
Port_SctpClosed,
Port_ArpOpen,
};
#endif
enum PortStatus {
PortStatus_Unknown,
PortStatus_Open,
PortStatus_Closed,
PortStatus_Arp,
PortStatus_Count
};
#endif
================================================
FILE: src/masscan-version.h
================================================
#ifndef MASSCAN_VERSION
#define MASSCAN_VERSION "1.3.9-integration"
#endif
================================================
FILE: src/masscan.h
================================================
#ifndef MASSCAN_H
#define MASSCAN_H
#include "massip-addr.h"
#include "util-safefunc.h"
#include "stack-src.h"
#include "massip.h"
#include "util-bool.h"
#include
#include
#include
#include
#include "massip.h"
#include "stack-queue.h"
struct Adapter;
struct TemplateSet;
struct Banner1;
struct TemplateOptions;
/**
* This is the "operation" to be performed by masscan, which is almost always
* to "scan" the network. However, there are some lesser operations to do
* instead, like run a "regression self test", or "debug", or something else
* instead of scanning. We parse the command-line in order to figure out the
* proper operation
*/
enum Operation {
Operation_Default = 0, /* nothing specified, so print usage */
Operation_List_Adapters = 1, /* --listif */
Operation_Selftest = 2, /* --selftest or --regress */
Operation_Scan = 3, /* this is what you expect */
Operation_DebugIF = 4, /* --debug if */
Operation_ListScan = 5, /* -sL */
Operation_ReadScan = 6, /* --readscan */
Operation_ReadRange = 7, /* --readrange */
Operation_Benchmark = 8, /* --benchmark */
Operation_Echo = 9, /* --echo */
Operation_EchoAll = 10, /* --echo-all */
Operation_EchoCidr = 11, /* --echo-cidr */
};
/**
* The format of the output. If nothing is specified, then the default will
* be "--interactive", meaning that we'll print to the command-line live as
* results come in. Only one output format can be specified, except that
* "--interactive" can be specified alongside any of the other ones.
*/
enum OutputFormat {
Output_Default = 0x0000,
Output_Interactive = 0x0001, /* --interactive, print to cmdline */
Output_List = 0x0002,
Output_Binary = 0x0004, /* -oB, "binary", the primary format */
Output_XML = 0x0008, /* -oX, "xml" */
Output_JSON = 0x0010, /* -oJ, "json" */
Output_NDJSON = 0x0011, /* -oD, "ndjson" */
Output_Nmap = 0x0020,
Output_ScriptKiddie = 0x0040,
Output_Grepable = 0x0080, /* -oG, "grepable" */
Output_Redis = 0x0100,
Output_Unicornscan = 0x0200, /* -oU, "unicornscan" */
Output_None = 0x0400,
Output_Certs = 0x0800,
Output_Hostonly = 0x1000, /* -oH, "hostonly" */
Output_All = 0xFFBF, /* not supported */
};
/**
* Holds the list of TCP "hello" payloads, specified with the "--hello-file"
* or "--hello-string" options
*/
struct TcpCfgPayloads
{
/** The "hello" data in base64 format. This is either the base64 string
* specified in the cmdline/cfgfile with "--hello-string", or the
* contents of a file specified with "--hello-file" that we've converted
* into base64 */
char *payload_base64;
/** The TCP port that this hello belongs to */
unsigned port;
/** These configuration options are stored as a linked-list */
struct TcpCfgPayloads *next;
};
/**
* This is the master MASSCAN configuration structure. It is created on startup
* by reading the command-line and parsing configuration files.
*
* Once read in at the start, this structure doesn't change. The transmit
* and receive threads have only a "const" pointer to this structure.
*/
struct Masscan
{
/**
* What this program is doing, which is normally "Operation_Scan", but
* which can be other things, like "Operation_SelfTest"
*/
enum Operation op;
struct {
unsigned tcp:1;
unsigned udp:1; /* -sU */
unsigned sctp:1;
unsigned ping:1; /* --ping, ICMP echo */
unsigned arp:1; /* --arp, local ARP scan */
unsigned oproto:1; /* -sO */
} scan_type;
/**
* After scan type has been configured, add these ports. In other words,
* the user may specify `-sU` or `-sT` after the `--top-ports` parameter,
* so we have to wait until after parsing arguments to fill in the ports.
*/
unsigned top_ports;
/**
* Temporary file to echo parameters to, used for saving configuration
* to a file
*/
FILE *echo;
unsigned echo_all;
/**
* One or more network adapters that we'll use for scanning. Each adapter
* should have a separate set of IP source addresses, except in the case
* of PF_RING dnaX:Y adapters.
*/
struct {
char ifname[256];
struct Adapter *adapter;
struct stack_src_t src;
macaddress_t source_mac;
macaddress_t router_mac_ipv4;
macaddress_t router_mac_ipv6;
ipv4address_t router_ip;
int link_type; /* libpcap definitions */
unsigned char my_mac_count; /*is there a MAC address? */
unsigned vlan_id;
unsigned is_vlan:1;
unsigned is_usable:1;
} nic[8];
unsigned nic_count;
/**
* The target ranges of IPv4 addresses that are included in the scan.
* The user can specify anything here, and we'll resolve all overlaps
* and such, and sort the target ranges.
*/
struct MassIP targets;
/**
* IPv4 addresses/ranges that are to be excluded from the scan. This takes
* precedence over any 'include' statement. What happens is this: after
* all the configuration has been read, we then apply the exclude/blacklist
* on top of the target/whitelist, leaving only a target/whitelist left.
* Thus, during the scan, we only choose from the target/whitelist and
* don't consult the exclude/blacklist.
*/
struct MassIP exclude;
/**
* Only output these types of banners
*/
struct RangeList banner_types;
/**
* Maximum rate, in packets-per-second (--rate parameter). This can be
* a fraction of a packet-per-second, or be as high as 30000000.0 (or
* more actually, but I've only tested to 30megapps).
*/
double max_rate;
/**
* Number of retries (--retries or --max-retries parameter). Retries
* happen a few seconds apart.
*/
unsigned retries;
unsigned is_pfring:1; /* --pfring */
unsigned is_sendq:1; /* --sendq */
unsigned is_banners:1; /* --banners */
unsigned is_banners_rawudp:1; /* --rawudp */
unsigned is_offline:1; /* --offline */
unsigned is_noreset:1; /* --noreset, don't transmit RST */
unsigned is_gmt:1; /* --gmt, all times in GMT */
unsigned is_capture_cert:1; /* --capture cert */
unsigned is_capture_html:1; /* --capture html */
unsigned is_capture_heartbleed:1; /* --capture heartbleed */
unsigned is_capture_ticketbleed:1; /* --capture ticket */
unsigned is_test_csv:1; /* (temporary testing feature) */
unsigned is_infinite:1; /* -infinite */
unsigned is_readscan:1; /* --readscan, Operation_Readscan */
unsigned is_heartbleed:1; /* --heartbleed, scan for this vuln */
unsigned is_ticketbleed:1; /* --ticketbleed, scan for this vuln */
unsigned is_poodle_sslv3:1; /* --vuln poodle, scan for this vuln */
unsigned is_hello_ssl:1; /* --ssl, use SSL HELLO on all ports */
unsigned is_hello_smbv1:1; /* --smbv1, use SMBv1 hello, instead of v1/v2 hello */
unsigned is_hello_http:1; /* --hello=http, use HTTP on all ports */
unsigned is_scripting:1; /* whether scripting is needed */
unsigned is_capture_servername:1; /* --capture servername */
/** Packet template options, such as whether we should add a TCP MSS
* value, or remove it from the packet */
struct TemplateOptions *templ_opts; /* e.g. --tcpmss */
/**
* Wait forever for responses, instead of the default 10 seconds
*/
unsigned wait;
/**
* --resume
* This structure contains options for pausing the scan (by exiting the
* program) and restarting it later.
*/
struct {
/** --resume-index */
uint64_t index;
/** --resume-count */
uint64_t count;
/** Derives the --resume-index from the target ip:port */
struct {
unsigned ip;
unsigned port;
} target;
} resume;
/**
* --shard n/m
* This is used for distributing a scan across multiple "shards". Every
* shard in the scan must know the total number of shards, and must also
* know which of those shards is it's identity. Thus, shard 1/5 scans
* a different range than 2/5. These numbers start at 1, so it's
* 1/3 (#1 out of three), 2/3, and 3/3 (but not 0/3).
*/
struct {
unsigned one;
unsigned of;
} shard;
/**
* The packet template set we are current using. We store a binary template
* for TCP, UDP, SCTP, ICMP, and so on. All the scans using that protocol
* are then scanned using that basic template. IP and TCP options can be
* added to the basic template without affecting any other component
* of the system.
*/
struct TemplateSet *pkt_template;
/**
* A random seed for randomization if zero, otherwise we'll use
* the configured seed for repeatable tests.
*/
uint64_t seed;
/**
* This block configures what we do for the output files
*/
struct OutputStuff {
/**
* --output-format
* Examples are "xml", "binary", "json", "ndjson", "grepable", and so on.
*/
enum OutputFormat format;
/**
* --output-filename
* The name of the file where we are storing scan results.
* Note: the filename "-" means that we should send the file to
* rather than to a file.
*/
char filename[256];
/**
* A feature of the XML output where we can insert an optional
* stylesheet into the file for better rendering on web browsers
*/
char stylesheet[256];
/**
* --append
* We should append to the output file rather than overwriting it.
*/
unsigned is_append:1;
/**
* --json-status
* Print each status update line to stderr as JSON ending with a newline
*
* This only applies to the three types of status lines that are printed
* in status_print(); it does *not* apply to things like startup messages,
* error messages or discovery of individual ports
*
*/
bool is_status_ndjson;
/**
* --open
* --open-only
* --show open
* Whether to show open ports
*/
unsigned is_show_open:1;
/**
* --show closed
* Whether to show closed ports (i.e. RSTs)
*/
unsigned is_show_closed:1;
/**
* --show host
* Whether to show host messages other than closed ports
*/
unsigned is_show_host:1;
/**
* print reason port is open, which is redundant for us
*/
unsigned is_reason:1;
/**
* --interactive
* Print to command-line while also writing to output file. This isn't
* needed if the output format is already 'interactive' (the default),
* but only if the default output format is anything else, and the
* user also wants interactivity.
*/
unsigned is_interactive:1;
/**
* Print state updates
*/
unsigned is_status_updates:1;
struct {
/**
* When we should rotate output into the target directory
*/
unsigned timeout;
/**
* When doing "--rotate daily", the rotation is done at GMT. In
* order to fix this, add an offset.
*/
unsigned offset;
/**
* Instead of rotating by timeout, we can rotate by filesize
*/
uint64_t filesize;
/**
* The directory to which we store rotated files
*/
char directory[256];
} rotate;
} output;
struct {
unsigned data_length; /* number of bytes to randomly append */
unsigned ttl; /* starting IP TTL field */
unsigned badsum; /* bad TCP/UDP/SCTP checksum */
unsigned packet_trace:1; /* print transmit messages */
char datadir[256];
} nmap;
char pcap_filename[256];
struct {
unsigned timeout;
} tcb;
struct {
char *pcap_payloads_filename;
char *nmap_payloads_filename;
char *nmap_service_probes_filename;
struct PayloadsUDP *udp;
struct PayloadsUDP *oproto;
struct TcpCfgPayloads *tcp;
struct NmapServiceProbeList *probes;
} payloads;
/** Reconfigure the HTTP header */
struct {
/* Method */
unsigned char *method;
size_t method_length;
/* URL */
unsigned char *url;
size_t url_length;
/* Version */
unsigned char *version;
size_t version_length;
/* Host */
unsigned char *host;
size_t host_length;
/* User-Agent */
unsigned char *user_agent;
size_t user_agent_length;
/* Payload after the header*/
unsigned char *payload;
size_t payload_length;
/* Headers */
struct {
const char *name;
unsigned char *value;
size_t value_length;
} headers[16];
size_t headers_count;
/* Cookies */
struct {
unsigned char *value;
size_t value_length;
} cookies[16];
size_t cookies_count;
/* Remove */
struct {
unsigned char *name;
} remove[16];
size_t remove_count;
} http;
unsigned tcp_connection_timeout;
/** Number of seconds to wait for a 'hello' from the server before
* giving up and sending a 'hello' from the client. Should be a small
* value when doing scans that expect client-side hellos, like HTTP or
* SSL, but should be a longer value when doing scans that expect server
* hellos, such as FTP or VNC */
unsigned tcp_hello_timeout;
char *bpf_filter;
struct {
ipaddress ip;
char *password;
unsigned port;
} redis;
/**
* --min-packet
*/
unsigned min_packet_size;
/**
* Number of rounds for randomization
* --blackrock-rounds
*/
unsigned blackrock_rounds;
/**
* --script
*/
struct {
/* The name (filename) of the script to run */
char *name;
/* The script VM */
struct lua_State *L;
} scripting;
/**
* --vuln
* The name of a vuln to check, like "poodle"
*/
const char *vuln_name;
};
int mainconf_selftest(void);
void masscan_read_config_file(struct Masscan *masscan, const char *filename);
void masscan_command_line(struct Masscan *masscan, int argc, char *argv[]);
void masscan_usage(void);
void masscan_save_state(struct Masscan *masscan);
void main_listscan(struct Masscan *masscan);
/**
* Load databases, such as:
* - nmap-payloads
* - nmap-service-probes
* - pcap-payloads
*/
void masscan_load_database_files(struct Masscan *masscan);
/**
* Pre-scan the command-line looking for options that may affect how
* previous options are handled. This is a bit of a kludge, really.
*/
int masscan_conf_contains(const char *x, int argc, char **argv);
/**
* Called to set a pair.
*/
void
masscan_set_parameter(struct Masscan *masscan,
const char *name, const char *value);
/**
* Discover the local network adapter parameters, such as which
* MAC address we are using and the MAC addresses of the
* local routers.
*/
int
masscan_initialize_adapter(
struct Masscan *masscan,
unsigned index,
macaddress_t *source_mac,
macaddress_t *router_mac_ipv4,
macaddress_t *router_mac_ipv6);
/**
* Echoes the settings to the command-line. By default, echoes only
* non-default values. With "echo-all", everything is echoed.
*/
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
================================================
FILE: src/massip-addr.c
================================================
#include "massip-addr.h"
#include
/**
* Holds the output string, so that we can append to it without
* overflowing buffers. The _append_xxx() functions below append
* to this string.
*/
typedef struct stream_t {
char *buf;
size_t offset;
size_t length;
} stream_t;
/**
* Append a character to the output string. All the other _append_xxx()
* functions call this one, so this is the only one where a
* buffer-overflow can occur.
*/
static void
_append_char(stream_t *out, char c)
{
if (out->offset < out->length)
out->buf[out->offset++] = c;
/* keep the string nul terminated as we build it */
if (out->offset < out->length)
out->buf[out->offset] = '\0';
}
static void
_append_ipv6(stream_t *out, const unsigned char *ipv6)
{
static const char hex[] = "0123456789abcdef";
size_t i;
int is_ellision = 0;
/* An IPv6 address is printed as a series of 2-byte hex words
* separated by colons :, for a total of 16-bytes */
for (i = 0; i < 16; i += 2) {
unsigned n = ipv6[i] << 8 | ipv6[i + 1];
/* Handle the ellision case. A series of words with a value
* of 0 can be removed completely, replaced by an extra colon */
if (n == 0 && !is_ellision) {
is_ellision = 1;
while (i < 13 && ipv6[i + 2] == 0 && ipv6[i + 3] == 0)
i += 2;
_append_char(out, ':');
/* test for all-zero address, in which case the output
* will be "::". */
while (i == 14 && ipv6[i] == 0 && ipv6[i + 1] == 0){
i=16;
_append_char(out, ':');
}
continue;
}
/* Print the colon between numbers. Fence-post alert: only colons
* between numbers are printed, not at the beginning or end of the
* string */
if (i)
_append_char(out, ':');
/* Print the digits. Leading zeroes are not printed */
if (n >> 12)
_append_char(out, hex[(n >> 12) & 0xF]);
if (n >> 8)
_append_char(out, hex[(n >> 8) & 0xF]);
if (n >> 4)
_append_char(out, hex[(n >> 4) & 0xF]);
_append_char(out, hex[(n >> 0) & 0xF]);
}
}
struct ipaddress_formatted ipv6address_fmt(ipv6address a)
{
struct ipaddress_formatted out;
unsigned char tmp[16];
size_t i;
stream_t s;
/*
* Convert address into a sequence of bytes. Our code
* here represents an IPv6 address as two 64-bit numbers, but
* the formatting code above that we copied from a different
* project represents it as an array of bytes.
*/
for (i=0; i<16; i++) {
uint64_t x;
if (i<8)
x = a.hi;
else
x = a.lo;
x >>= (7 - (i%8)) * 8;
tmp[i] = (unsigned char)(x & 0xFF);
}
/* Call the formatting function */
s.buf = out.string;
s.offset = 0;
s.length = sizeof(out.string);
_append_ipv6(&s, tmp);
return out;
}
/**
* Append a decimal integer.
*/
static void
_append_decimal(stream_t *out, unsigned long long n)
{
char tmp[64];
size_t tmp_offset = 0;
/* Create temporary string */
while (n >= 10) {
unsigned digit = n % 10;
n /= 10;
tmp[tmp_offset++] = (char)('0' + digit);
}
/* the final digit, may be zero */
tmp[tmp_offset++] = (char)('0' + n);
/* Copy the result backwards */
while (tmp_offset)
_append_char(out, tmp[--tmp_offset]);
}
static void
_append_hex2(stream_t *out, unsigned long long n)
{
static const char hex[17] = "0123456789abcdef";
_append_char(out, hex[(n>>4)&0xF]);
_append_char(out, hex[(n>>0)&0xF]);
}
struct ipaddress_formatted ipv4address_fmt(ipv4address ip)
{
struct ipaddress_formatted out;
stream_t s;
/* Call the formatting function */
s.buf = out.string;
s.offset = 0;
s.length = sizeof(out.string);
_append_decimal(&s, (ip >> 24) & 0xFF);
_append_char(&s, '.');
_append_decimal(&s, (ip >> 16) & 0xFF);
_append_char(&s, '.');
_append_decimal(&s, (ip >> 8) & 0xFF);
_append_char(&s, '.');
_append_decimal(&s, (ip >> 0) & 0xFF);
return out;
}
struct ipaddress_formatted macaddress_fmt(macaddress_t mac)
{
struct ipaddress_formatted out;
stream_t s;
/* Call the formatting function */
s.buf = out.string;
s.offset = 0;
s.length = sizeof(out.string);
_append_hex2(&s, mac.addr[0]);
_append_char(&s, '-');
_append_hex2(&s, mac.addr[1]);
_append_char(&s, '-');
_append_hex2(&s, mac.addr[2]);
_append_char(&s, '-');
_append_hex2(&s, mac.addr[3]);
_append_char(&s, '-');
_append_hex2(&s, mac.addr[4]);
_append_char(&s, '-');
_append_hex2(&s, mac.addr[5]);
return out;
}
struct ipaddress_formatted ipaddress_fmt(ipaddress a)
{
struct ipaddress_formatted out;
stream_t s;
ipv4address ip = a.ipv4;
if (a.version == 6) {
return ipv6address_fmt(a.ipv6);
}
/* Call the formatting function */
s.buf = out.string;
s.offset = 0;
s.length = sizeof(out.string);
_append_decimal(&s, (ip >> 24) & 0xFF);
_append_char(&s, '.');
_append_decimal(&s, (ip >> 16) & 0xFF);
_append_char(&s, '.');
_append_decimal(&s, (ip >> 8) & 0xFF);
_append_char(&s, '.');
_append_decimal(&s, (ip >> 0) & 0xFF);
return out;
}
static unsigned _count_long(uint64_t number)
{
unsigned i;
unsigned count = 0;
for (i=0; i<64; i++) {
if ((number >> i) & 1)
count = i + 1;
}
return count;
}
/**
* Find the number of bits needed to hold the integer. In other words,
* the number 0x64 would need 7 bits to store it.
*
* We use this to count the size of scans. We currently only support
* scan sizes up to 63 bits.
*/
unsigned massint128_bitcount(massint128_t number)
{
if (number.hi)
return _count_long(number.hi) + 64;
else
return _count_long(number.lo);
}
ipv6address_t ipv6address_add_uint64(ipv6address_t lhs, uint64_t rhs) {
lhs.lo += rhs;
if (lhs.lo < rhs) {
lhs.hi += 1;
}
return lhs;
}
ipv6address_t ipv6address_subtract(ipv6address_t lhs, ipv6address_t rhs) {
ipv6address_t difference;
difference.hi = lhs.hi - rhs.hi;
difference.lo = lhs.lo - rhs.lo;
/* check for underflow */
if (difference.lo > lhs.lo)
difference.hi -= 1;
return difference;
}
ipv6address_t ipv6address_add(ipv6address_t lhs, ipv6address_t rhs) {
ipv6address_t sum;
sum.hi = lhs.hi + rhs.hi;
sum.lo = lhs.lo - rhs.lo;
/* check for underflow */
if (sum.lo > lhs.lo)
sum.hi += 1;
return sum;
}
int ipv6address_selftest(void)
{
struct test_pair {
const char *name; // Human-readable IPv6 address string
struct ipaddress ip_addr; // IP address (union)
};
/* Probably overkill, added while investigating issue #796 */
struct test_pair tests[] = {
{"2001:db8:ac10:fe01::2", {.ipv6 = {0x20010db8ac10fe01, 0x0000000000000002}, .version = 6}},
{"2607:f8b0:4000::1", {.ipv6 = {0x2607f8b040000000, 0x0000000000000001}, .version = 6}},
{"fd12:3456:7890:abcd:ef00::1", {.ipv6 = {0xfd1234567890abcd, 0xef00000000000001}, .version = 6}},
{"::1", {.ipv6 = {0x0000000000000000, 0x0000000000000001}, .version = 6}},
{"1::", {.ipv6 = {0x0001000000000000, 0x0000000000000000}, .version = 6}},
{"1::2", {.ipv6 = {0x0001000000000000, 0x0000000000000002}, .version = 6}},
{"2::1", {.ipv6 = {0x0002000000000000, 0x0000000000000001}, .version = 6}},
{"1:2::", {.ipv6 = {0x0001000200000000, 0x0000000000000000}, .version = 6}},
{NULL, {{0, 0}, 0}}
};
int x = 0;
ipaddress ip;
struct ipaddress_formatted fmt;
for (int i = 0; tests[i].name != NULL; i++) {
fmt = ipaddress_fmt(tests[i].ip_addr);
if (strcmp(fmt.string, tests[i].name) != 0)
x++;
}
return x;
}
int ipv4address_selftest(void)
{
int x = 0;
ipaddress ip;
struct ipaddress_formatted fmt;
ip.version = 4;
ip.ipv4 = 0x01FF00A3;
fmt = ipaddress_fmt(ip);
if (strcmp(fmt.string, "1.255.0.163") != 0)
x++;
return x;
}
int ipv6address_is_equal_prefixed(ipv6address_t lhs, ipv6address_t rhs, unsigned prefix)
{
ipv6address mask;
/* If the prefix is bad, then the answer is 'no'. */
if (prefix > 128) {
return 0;
}
/* Create the mask from the prefix */
if (prefix > 64)
mask.hi = ~0ULL;
else if (prefix == 0)
mask.hi = 0;
else
mask.hi = ~0ULL << (64 - prefix);
if (prefix > 64)
mask.lo = ~0ULL << (128 - prefix);
else
mask.lo = 0;
/* Mask off any non-zero bits from both addresses */
lhs.hi &= mask.hi;
lhs.lo &= mask.lo;
rhs.hi &= mask.hi;
rhs.lo &= mask.lo;
/* Now do a normal compare */
return ipv6address_is_equal(lhs, rhs);
}
================================================
FILE: src/massip-addr.h
================================================
/*
Simple module for handling addresses (IPv6, IPv4, MAC).
Also implements a 128-bit type for dealing with addresses.
This is the module that almost all the other code depends
upon, because everything else deals with the IP address
types defined here.
*/
#ifndef MASSIP_ADDR_H
#define MASSIP_ADDR_H
#include
#include
#if defined(_MSC_VER) && !defined(inline)
#define inline __inline
#endif
#if defined(_MSC_VER)
#pragma warning(disable: 4201)
#endif
/**
* An IPv6 address is represented as two 64-bit integers instead of a single
* 128-bit integer. This is because currently (year 2020) most compilers
* do not support the `uint128_t` type, but all relevant ones do support
* the `uint64_t` type.
*/
struct ipv6address {uint64_t hi; uint64_t lo;};
typedef struct ipv6address ipv6address;
typedef struct ipv6address ipv6address_t;
/**
* IPv4 addresses are represented simply with an integer.
*/
typedef unsigned ipv4address;
typedef ipv4address ipv4address_t;
/**
* MAC address (layer 2). Since we have canonical types for IPv4/IPv6
* addresses, we may as well have a canonical type for MAC addresses,
* too.
*/
struct macaddress_t {unsigned char addr[6];};
typedef struct macaddress_t macaddress_t;
/**
* In many cases we need to do arithmetic on IPv6 addresses, treating
* them as a large 128-bit integer. Thus, we declare our own 128-bit
* integer type (and some accompanying math functions). But it's
* still just the same as a 128-bit integer.
*/
typedef ipv6address massint128_t;
/**
* Most of the code in this project is agnostic to the version of IP
* addresses (IPv4 or IPv6). Therefore, we represent them as a union
* distinguished by a version number. The `version` is an integer
* with a value of either 4 or 6.
*/
struct ipaddress {
union {
unsigned ipv4;
ipv6address ipv6;
};
unsigned char version;
};
typedef struct ipaddress ipaddress;
/** @return true if the IPv6 address is zero [::] */
static inline int ipv6address_is_zero(ipv6address_t a) {
return a.hi == 0 && a.lo == 0;
}
#define massint128_is_zero ipv6address_is_zero
/** The IPv6 address [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]
* is invalid */
static inline int ipv6address_is_invalid(ipv6address_t a) {
return a.hi == ~0ULL && a.lo == ~0ULL;
}
/** Compare two IPv6 addresses */
static inline int ipv6address_is_equal(ipv6address_t a, ipv6address_t b) {
return a.hi == b.hi && a.lo == b.lo;
}
static inline int ipaddress_is_equal(ipaddress a, ipaddress b) {
if (a.version != b.version)
return 0;
if (a.version == 4) {
return a.ipv4 == b.ipv4;
} else if (a.version == 6) {
return ipv6address_is_equal(a.ipv6, b.ipv6);
} else
return 0;
}
/** Compare two IPv6 addresses, to see which one comes frist. This is used
* in sorting the addresses
* @return true if a < b, false otherwise */
static inline int ipv6address_is_lessthan(ipv6address_t a, ipv6address_t b) {
return (a.hi == b.hi)?(a.lo < b.lo):(a.hi < b.hi);
}
/**
* Mask the lower bits of each address and test if the upper bits are equal
*/
int ipv6address_is_equal_prefixed(ipv6address_t lhs, ipv6address_t rhs, unsigned prefix);
ipv6address_t ipv6address_add_uint64(ipv6address_t lhs, uint64_t rhs);
ipv6address_t ipv6address_subtract(ipv6address_t lhs, ipv6address_t rhs);
ipv6address_t ipv6address_add(ipv6address_t lhs, ipv6address_t rhs);
/**
* Given a typical EXTERNAL representation of an IPv6 address, which is
* an array of 16 bytes, convert to the canonical INTERNAL address.
*/
static inline ipv6address ipv6address_from_bytes(const unsigned char *buf) {
ipv6address addr;
addr.hi = (uint64_t)buf[ 0] << 56
| (uint64_t)buf[ 1] << 48
| (uint64_t)buf[ 2] << 40
| (uint64_t)buf[ 3] << 32
| (uint64_t)buf[ 4] << 24
| (uint64_t)buf[ 5] << 16
| (uint64_t)buf[ 6] << 8
| (uint64_t)buf[ 7] << 0;
addr.lo = (uint64_t)buf[ 8] << 56
| (uint64_t)buf[ 9] << 48
| (uint64_t)buf[10] << 40
| (uint64_t)buf[11] << 32
| (uint64_t)buf[12] << 24
| (uint64_t)buf[13] << 16
| (uint64_t)buf[14] << 8
| (uint64_t)buf[15] << 0;
return addr;
}
/**
* Given a typical EXTERNAL representation of an Ethernet MAC address,
* which is an array of 6 bytes, convert to the canonical INTERNAL address.
*/
static inline macaddress_t macaddress_from_bytes(const void *vbuf)
{
const unsigned char *buf = (const unsigned char *)vbuf;
macaddress_t result;
result.addr[0] = buf[0];
result.addr[1] = buf[1];
result.addr[2] = buf[2];
result.addr[3] = buf[3];
result.addr[4] = buf[4];
result.addr[5] = buf[5];
return result;
}
/** Test if the Ethernet MAC address is all zeroes */
static inline int macaddress_is_zero(macaddress_t mac)
{
return mac.addr[0] == 0
&& mac.addr[1] == 0
&& mac.addr[2] == 0
&& mac.addr[3] == 0
&& mac.addr[4] == 0
&& mac.addr[5] == 0;
}
/** Compare two Ethernet MAC addresses to see if they are equal */
static inline int macaddress_is_equal(macaddress_t lhs, macaddress_t rhs)
{
return lhs.addr[0] == rhs.addr[0]
&& lhs.addr[1] == rhs.addr[1]
&& lhs.addr[2] == rhs.addr[2]
&& lhs.addr[3] == rhs.addr[3]
&& lhs.addr[4] == rhs.addr[4]
&& lhs.addr[5] == rhs.addr[5];
}
/**
* Return a buffer with the formatted address
*/
typedef struct ipaddress_formatted {
char string[48];
} ipaddress_formatted_t;
struct ipaddress_formatted ipv6address_fmt(ipv6address a);
struct ipaddress_formatted ipv4address_fmt(ipv4address a);
struct ipaddress_formatted ipaddress_fmt(ipaddress a);
struct ipaddress_formatted macaddress_fmt(macaddress_t a);
unsigned massint128_bitcount(massint128_t num);
/**
* @return 0 on success, 1 on failure
*/
int ipv6address_selftest(void);
int ipv4address_selftest(void);
#endif
================================================
FILE: src/massip-parse.c
================================================
/*
massip-parse
This module parses IPv4 and IPv6 addresses.
It's not a typical parser. It's optimized around parsing large
files containing millions of addresses and ranges using a
"state-machine parser".
*/
#include "massip.h"
#include "massip-parse.h"
#include "massip-rangesv4.h"
#include "massip-rangesv6.h"
#include "util-logger.h"
#include "util-bool.h"
#include "util-malloc.h"
#include "util-safefunc.h"
#include "unusedparm.h"
#include
struct massip_parser
{
unsigned long long line_number;
unsigned long long char_number;
unsigned state;
unsigned tmp;
unsigned char digit_count;
unsigned addr;
unsigned begin;
unsigned end;
struct {
ipv6address _begin;
ipv6address _end;
unsigned short tmp[8];
unsigned char index;
unsigned char ellision_index;
unsigned is_bracket:1;
unsigned is_second:1;
} ipv6;
};
/***************************************************************************
***************************************************************************/
static struct massip_parser *
_parser_init(struct massip_parser *p)
{
memset(p, 0, sizeof(*p));
p->line_number = 1;
p->ipv6.ellision_index = 8;
return p;
}
/***************************************************************************
***************************************************************************/
static void
_parser_destroy(struct massip_parser *p)
{
UNUSEDPARM(p);
}
/***************************************************************************
***************************************************************************/
static void
_parser_err(struct massip_parser *p, unsigned long long *line_number, unsigned long long *charindex)
{
*line_number = p->line_number;
*charindex = p->char_number;
}
/**
* Called before parsing the first address in a pair, and also
* after the first address, to prepare for parsing the next
* address
*/
static void
_init_next_address(struct massip_parser *p, int is_second)
{
p->tmp = 0;
p->ipv6.ellision_index = 8;
p->ipv6.index = 0;
p->ipv6.is_bracket = 0;
p->digit_count = 0;
p->ipv6.is_second = is_second;
}
static unsigned
_parser_finish_ipv6(struct massip_parser *p)
{
unsigned index = p->ipv6.index;
unsigned ellision = p->ipv6.ellision_index;
/* We must have seen 8 numbers, or an ellision */
if (index < 8 && ellision >= 8)
return 1;
/* Handle ellision */
memmove(
&p->ipv6.tmp[8-(index-ellision)],
&p->ipv6.tmp[ellision],
sizeof(p->ipv6.tmp[0]) * (index-ellision)
);
memset(
&p->ipv6.tmp[ellision],
0,
sizeof(p->ipv6.tmp[0]) * (8 - index)
);
/* Copy over to begin/end. We parse the address as a series of 16-bit
* integers, but return the result as two 64-bit integers */
{
ipv6address a;
a.hi = (uint64_t)p->ipv6.tmp[0] << 48ULL
| (uint64_t)p->ipv6.tmp[1] << 32ULL
| (uint64_t)p->ipv6.tmp[2] << 16ULL
| (uint64_t)p->ipv6.tmp[3] << 0ULL;
a.lo = (uint64_t)p->ipv6.tmp[4] << 48ULL
| (uint64_t)p->ipv6.tmp[5] << 32ULL
| (uint64_t)p->ipv6.tmp[6] << 16ULL
| (uint64_t)p->ipv6.tmp[7] << 0ULL;
if (p->ipv6.is_second)
p->ipv6._end = a;
else {
p->ipv6._begin = a;
/* Set this here in case there is no 'end' address */
p->ipv6._end = a;
}
}
/* Reset the parser to start parsing the next address */
_init_next_address(p, 1);
return 0;
}
/***************************************************************************
* We store the IPv6 addresses that we are building inside the 'state'
* of the state-machine. This function copies them out of the opaque
* state into discrete values.
***************************************************************************/
static void
_parser_get_ipv6(struct massip_parser *state, ipv6address *begin, ipv6address *end)
{
*begin = state->ipv6._begin;
*end = state->ipv6._end;
}
enum parser_state_t {
LINE_START, ADDR_START,
COMMENT,
NUMBER0, NUMBER1, NUMBER2, NUMBER3, NUMBER_ERR,
SECOND0, SECOND1, SECOND2, SECOND3, SECOND_ERR,
IPV4_CIDR_NUM,
UNIDASH1, UNIDASH2,
IPV6_BEGIN, IPV6_COLON, IPV6_CIDR, IPV6_CIDR_NUM,
IPV6_NEXT,
IPV6_END,
ERROR
};
/***************************************************************************
* When we start parsing an address, we don't know whether it's going to
* be IPv4 or IPv6. We assume IPv4, but when we hit a condition indicating
* that it's IPv6 instead, we need change the temporary number we
* are working on from decimal to hex, then move from the middle of
* parsing an IPv4 address to the middle of parsing an IPv6 address.
***************************************************************************/
static int
_switch_to_ipv6(struct massip_parser *p, int old_state)
{
unsigned num = p->tmp;
num = ((num/1000)%10) * 16 * 16 * 16
+ ((num/100)%10) * 16 * 16
+ ((num/10)%10) * 16
+ (num % 10);
//printf("%u -> 0x%x\n", p->tmp, num);
p->tmp = num;
return old_state;
}
enum {
IPV4_n, IPV4_nn, IPV4_nnn, IPV4_nnn_,
IPV4_nnn_n, IPV4_nnn_nn, IPV4_nnn_nnn, IPV4_nnn_nnn_,
IPV4_nnn_nnn_n, IPV4_nnn_nnn_nn, IPV4_nnn_nnn_nnn, IPV4_nnn_nnn_nnn_,
IPV4_nnn_nnn_nnn_n, IPV4_nnn_nnn_nnn_nn, IPV4_nnn_nnn_nnn_nnn, IPV4_nnn_nnn_nnn_nnn_,
IPV4e_n, IPV4e_nn, IPV4e_nnn, IPV4e_nnn_,
IPV4e_nnn_n, IPV4e_nnn_nn, IPV4e_nnn_nnn, IPV4e_nnn_nnn_,
IPV4e_nnn_nnn_n, IPV4e_nnn_nnn_nn, IPV4e_nnn_nnn_nnn, IPV4e_nnn_nnn_nnn_,
IPV4e_nnn_nnn_nnn_n, IPV4e_nnn_nnn_nnn_nn, IPV4e_nnn_nnn_nnn_nnn, IPV4e_nnn_nnn_nnn_nnn_,
};
/**
* Applies a CIDR mask to an IPv4 address to create a begin/end address.
*/
static void
_ipv4_apply_cidr(unsigned *begin, unsigned *end, unsigned bitcount)
{
unsigned long long mask = 0xFFFFFFFF00000000ULL >> bitcount;
/* mask off low-order bits */
*begin &= (unsigned)mask;
/* Set all suffix bits to 1, so that 192.168.1.0/24 has
* an ending address of 192.168.1.255. */
*end = *begin | (unsigned)~mask;
}
/**
* Given an address 'being' and a 'prefix', return the 'begin' and 'end' address of the range.
* @param begin
* An in/out parameter. This may have some extra bits somewhere in the range.
* These will be masked off and set to zero when the function returns.
* @param end
* An out parameter. This will be set to the last address of the range, meaning
* that all the trailing bits will be set to '1'.
* @parame prefix
* The number of bits of the prefix, from [0..128]. If the value is 0,
* then the 'begin' address will be set to all zeroes and the 'end'
* address will be set to all ones. If the value is 128,
* the 'begin' address is unchanged and the 'end' address
* is set to the same as 'begin'.
*/
static void
_ipv6_apply_cidr(ipv6address *begin, ipv6address *end, unsigned prefix)
{
ipv6address mask;
/* For bad prefixes, make sure we return an invalid address */
if (prefix > 128) {
static const ipv6address invalid = {~0ULL, ~0ULL};
*begin = invalid;
*end = invalid;
return;
};
/* Create the mask from the prefix */
if (prefix > 64)
mask.hi = ~0ULL;
else if (prefix == 0)
mask.hi = 0;
else
mask.hi = ~0ULL << (64 - prefix);
if (prefix > 64)
mask.lo = ~0ULL << (128 - prefix);
else
mask.lo = 0;
/* Mask off any non-zero bits from the start
* TODO print warning */
begin->hi &= mask.hi;
begin->lo &= mask.lo;
/* Set all suffix bits to 1, so that 192.168.1.0/24 has
* an ending address of 192.168.1.255. */
end->hi = begin->hi | ~mask.hi;
end->lo = begin->lo | ~mask.lo;
}
/***************************************************************************
* Parse the next IPv4/IPv6 address from a text stream, using a
* 'state-machine parser'.
***************************************************************************/
static enum {Still_Working, Found_Error, Found_IPv4, Found_IPv6}
_parser_next(struct massip_parser *p, const char *buf, size_t *r_offset, size_t length,
unsigned *r_begin, unsigned *r_end)
{
size_t i;
enum parser_state_t state = p->state;
int result = Still_Working;
/* The 'offset' parameter is optional. If NULL, then set it to zero */
if (r_offset)
i = *r_offset;
else
i = 0;
/* For all bytes in this chunk. This loop will exit early once
* we've found a complete IP address. */
while (i < length) {
unsigned char c = buf[i++];
p->char_number++;
switch (state) {
case LINE_START:
case ADDR_START:
_init_next_address(p, 0);
switch (c) {
case ' ': case '\t': case '\r':
/* ignore leading whitespace */
continue;
case '\n':
p->line_number++;
p->char_number = 0;
continue;
case '#': case ';': case '/': case '-':
state = COMMENT;
continue;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
p->tmp = (c - '0');
p->digit_count = 1;
state = NUMBER0;
break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
p->tmp = (c - 'a' + 10);
p->digit_count = 1;
state = IPV6_BEGIN;
break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
p->tmp = (c - 'A' + 10);
p->digit_count = 1;
state = IPV6_BEGIN;
break;
case ':':
p->ipv6.tmp[p->ipv6.index++] = 0;
state = IPV6_COLON;
break;
case '[':
p->ipv6.is_bracket = 1;
state = IPV6_BEGIN;
break;
default:
state = ERROR;
length = i; /* break out of loop */
break;
}
break;
case IPV6_CIDR:
p->digit_count = 0;
p->tmp = 0;
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
p->tmp = (c - '0');
p->digit_count = 1;
state = IPV6_CIDR_NUM;
break;
default:
state = ERROR;
length = i; /* break out of loop */
break;
}
break;
case IPV6_COLON:
p->digit_count = 0;
p->tmp = 0;
if (c == ':') {
if (p->ipv6.ellision_index < 8) {
state = ERROR;
length = i;
} else {
p->ipv6.ellision_index = p->ipv6.index;
state = IPV6_COLON;
}
break;
}
state = IPV6_BEGIN;
/* drop down */
case IPV6_BEGIN:
case IPV6_NEXT:
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if (p->digit_count >= 4) {
state = ERROR;
length = i;
} else {
p->tmp = p->tmp * 16 + (c - '0');
p->digit_count++;
}
break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
if (p->digit_count >= 4) {
state = ERROR;
length = i;
} else {
p->tmp = p->tmp * 16 + (c - 'a' + 10);
p->digit_count++;
}
break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
if (p->digit_count >= 4) {
state = ERROR;
length = i;
} else {
p->tmp = p->tmp * 16 + (c - 'A' + 10);
p->digit_count++;
}
break;
case ':':
if (p->ipv6.index >= 8) {
state = ERROR;
length = i;
} else {
p->ipv6.tmp[p->ipv6.index++] = (unsigned short)p->tmp;
state = IPV6_COLON;
}
break;
case ']':
if (!p->ipv6.is_bracket) {
state = ERROR;
length = i;
} else {
state = IPV6_END;
}
break;
case '[':
if (p->ipv6.is_bracket) {
state = ERROR;
length = i;
} else {
p->ipv6.is_bracket = 1;
}
break;
case '/':
case ' ':
case '\t':
case '\r':
case '\n':
case ',':
case '-':
i--; /* push back */
state = IPV6_END;
continue;
default:
state = ERROR;
length = i;
break;
}
break;
case IPV6_END:
/* Finish off the trailing number */
p->ipv6.tmp[p->ipv6.index++] = (unsigned short)p->tmp;
/* Do the final processing of this IPv6 address and
* prepare for the next one */
if (_parser_finish_ipv6(p) != 0) {
state = ERROR;
length = i;
continue;
}
/* Now decide the next state, whether this is a single
* address, an address range, or a CIDR address */
switch (c) {
case '/':
result = Still_Working;
state = IPV6_CIDR;
break;
case '-':
result = Still_Working;
state = IPV6_NEXT;
break;
case '\n':
p->line_number++;
p->char_number = 0;
/* drop down */
case ' ':
case '\t':
case '\r':
case ',':
result = Found_IPv6;
state = 0;
length = i; /* shorten the end to break out of loop */
break;
default:
state = ERROR;
length = i;
break;
}
break;
case COMMENT:
if (c == '\n') {
state = LINE_START;
p->line_number++;
p->char_number = 0;
} else
state = COMMENT;
break;
case IPV6_CIDR_NUM:
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if (p->digit_count == 4) {
state = ERROR;
length = i; /* break out of loop */
} else {
p->digit_count++;
p->tmp = p->tmp * 10 + (c - '0');
if (p->tmp > 128) {
state = ERROR;
length = i;
}
continue;
}
break;
case ':':
case ',':
case ' ':
case '\t':
case '\r':
case '\n':
{
_ipv6_apply_cidr(&p->ipv6._begin, &p->ipv6._end, p->tmp);
state = ADDR_START;
length = i; /* break out of loop */
if (c == '\n') {
p->line_number++;
p->char_number = 0;
}
*r_begin = p->begin;
*r_end = p->end;
result = Found_IPv6;
}
break;
default:
state = ERROR;
length = i; /* break out of loop */
break;
}
break;
case IPV4_CIDR_NUM:
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if (p->digit_count == 3) {
state = ERROR;
length = i; /* break out of loop */
} else {
p->digit_count++;
p->tmp = p->tmp * 10 + (c - '0');
if (p->tmp > 32) {
state = ERROR;
length = i;
}
continue;
}
break;
case ':':
case ',':
case ' ':
case '\t':
case '\r':
case '\n':
{
_ipv4_apply_cidr(&p->begin, &p->end, p->tmp);
state = ADDR_START;
length = i; /* break out of loop */
if (c == '\n') {
p->line_number++;
p->char_number = 0;
}
*r_begin = p->begin;
*r_end = p->end;
result = Found_IPv4;
}
break;
default:
state = ERROR;
length = i; /* break out of loop */
break;
}
break;
case UNIDASH1:
if (c == 0x80)
state = UNIDASH2;
else {
state = ERROR;
length = i; /* break out of loop */
}
break;
case UNIDASH2:
/* This covers:
* U+2010 HYPHEN
* U+2011 NON-BREAKING HYPHEN
* U+2012 FIGURE DASH
* U+2013 EN DASH
* U+2014 EM DASH
* U+2015 HORIZONTAL BAR
*/
if (c < 0x90 || 0x95 < c) {
state = ERROR;
length = i; /* break out of loop */
} else {
c = '-';
state = NUMBER3;
/* drop down */
}
case NUMBER0:
case NUMBER1:
case NUMBER2:
case NUMBER3:
case SECOND0:
case SECOND1:
case SECOND2:
case SECOND3:
switch (c) {
case '.':
p->addr = (p->addr << 8) | p->tmp;
p->tmp = 0;
p->digit_count = 0;
if (state == NUMBER3 || state == SECOND3) {
length = i;
state = ERROR;
} else
state++;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
p->digit_count++;
p->tmp = p->tmp * 10 + (c - '0');
if (p->tmp > 255 || p->digit_count > 3) {
if (state == NUMBER0) {
/* Assume that we've actually got an
* IPv6 number */
_switch_to_ipv6(p, state);
state = IPV6_BEGIN;
} else {
state = ERROR;
length = i;
}
}
continue;
break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
if (state == NUMBER0 || state == SECOND0) {
/* Assume that we've actually got an
* IPv6 number */
_switch_to_ipv6(p, state);
state = IPV6_BEGIN;
i--; /* go back one character */
} else {
state = ERROR;
length = i; /* break out of loop */
}
break;
case 0xe2:
if (state == NUMBER3) {
state = UNIDASH1;
} else {
state = ERROR;
length = i; /* break out of loop */
}
break;
case '-':
case 0x96: /* long dash, comes from copy/pasting into exclude files */
if (state == NUMBER3) {
p->begin = (p->addr << 8) | p->tmp;
p->tmp = 0;
p->digit_count = 0;
p->addr = 0;
state = SECOND0;
} else {
state = NUMBER_ERR;
length = i;
}
break;
case '/':
if (state == NUMBER3) {
p->begin = (p->addr << 8) | p->tmp;
p->tmp = 0;
p->digit_count = 0;
p->addr = 0;
state = IPV4_CIDR_NUM;
} else {
state = NUMBER_ERR;
length = i; /* break out of loop */
}
break;
case ':':
if (state == NUMBER0) {
/* Assume this is an IPv6 address instead of an IPv4 address */
_switch_to_ipv6(p, state);
state = IPV6_BEGIN;
i--;
break;
}
case ',':
case ' ':
case '\t':
case '\r':
case '\n':
if (state == NUMBER3) {
p->begin = (p->addr << 8) | p->tmp;
p->end = p->begin;
p->tmp = 0;
p->digit_count = 0;
p->addr = 0;
state = ADDR_START;
length = i; /* break out of loop */
if (c == '\n') {
p->line_number++;
p->char_number = 0;
}
*r_begin = p->begin;
*r_end = p->end;
result = Found_IPv4;
} else if (state == SECOND3) {
p->end = (p->addr << 8) | p->tmp;
p->tmp = 0;
p->digit_count = 0;
p->addr = 0;
state = ADDR_START;
length = i; /* break out of loop */
if (c == '\n') {
p->line_number++;
p->char_number = 0;
}
*r_begin = p->begin;
*r_end = p->end;
result = Found_IPv4;
} else {
state = NUMBER_ERR;
length = i;
}
break;
default:
state = ERROR;
length = i; /* break out of loop */
break;
}
break;
default:
case ERROR:
case NUMBER_ERR:
case SECOND_ERR:
state = ERROR;
length = i; /* break */
break;
}
}
/* The 'offset' parameter is optional. If NULL, then
* we don't return a value */
if (r_offset)
*r_offset = i;
p->state = state;
if (state == ERROR || state == NUMBER_ERR || state == SECOND_ERR)
result = Found_Error;
return result;
}
/***************************************************************************
* Test errors. We should get exactly which line-number and which character
* in the line caused the error
***************************************************************************/
static int
rangefile_test_error(const char *buf, unsigned long long in_line_number, unsigned long long in_char_number, unsigned which_test)
{
size_t length = strlen(buf);
size_t offset = 0;
struct massip_parser p[1];
unsigned out_begin = 0xa3a3a3a3;
unsigned out_end = 0xa3a3a3a3;
unsigned long long out_line_number;
unsigned long long out_char_number;
int x;
/* test the entire buffer */
_parser_init(p);
x = _parser_next(p, buf, &offset, length, &out_begin, &out_end);
if (x != Found_Error)
goto fail;
_parser_err(p, &out_line_number, &out_char_number);
if (in_line_number != out_line_number || in_char_number != out_char_number)
goto fail;
/* test one byte at a time */
_parser_destroy(p);
_parser_init(p);
offset = 0;
out_begin = 0xa3a3a3a3;
out_end = 0xa3a3a3a3;
x = 0;
while (offset < length) {
x = _parser_next(p, buf, &offset, offset+1, &out_begin, &out_end);
if (x == Found_Error)
break;
}
if (x != Found_Error)
goto fail;
_parser_err(p, &out_line_number, &out_char_number);
if (in_line_number != out_line_number || in_char_number != out_char_number)
goto fail;
_parser_destroy(p);
return 0;
fail:
_parser_destroy(p);
fprintf(stderr, "[-] rangefile test fail, line=%u\n", which_test);
return 1;
}
/***************************************************************************
***************************************************************************/
int
massip_parse_file(struct MassIP *massip, const char *filename)
{
struct RangeList *targets_ipv4 = &massip->ipv4;
struct Range6List *targets_ipv6 = &massip->ipv6;
struct massip_parser p[1];
char buf[65536];
FILE *fp = NULL;
bool is_error = false;
unsigned addr_count = 0;
unsigned long long line_number, char_number;
/* Kludge: should never happen, should fix this when reading in
* config, not this deep in the code. */
if (filename == 0 || filename[0] == '\0') {
fprintf(stderr, "[-] missing filename for ranges\n");
exit(1);
}
/*
* Open the file containing IP addresses, which can potentially be
* many megabytes in size
*/
if (strcmp(filename, "-") == 0) {
fp = stdin;
} else {
fp = fopen(filename, "rb");
if (fp == NULL) {
fprintf(stderr, "[-] FAIL: parsing IP addresses\n");
fprintf(stderr, "[-] %s: %s\n", filename, strerror(errno));
exit(1);
}
}
/*
* Create a parser for reading in the IP addresses using a state
* machine parser
*/
_parser_init(p);
/*
* Read in the data a block at a time, parsing according to the state
* machine.
*/
while (!is_error) {
size_t count;
size_t offset;
count = fread(buf, 1, sizeof(buf), fp);
if (count <= 0)
break;
offset = 0;
while (offset < count) {
unsigned begin, end;
int err;
err = _parser_next(p, buf, &offset, count, &begin, &end);
switch (err) {
case Still_Working:
if (offset < count) {
/* We reached this somehow in the middle of the buffer, but
* this return is only possible at the end of the buffer */
fprintf(stderr, "[-] rangeparse_next(): unknown coding failure\n");
}
break;
case Found_Error:
default:
_parser_err(p, &line_number, &char_number);
fprintf(stderr, "[-] %s:%llu:%llu: invalid IP address on line #%llu\n", filename, line_number, char_number, line_number);
is_error = true;
count = offset;
break;
case Found_IPv4:
rangelist_add_range(targets_ipv4, begin, end);
addr_count++;
break;
case Found_IPv6:
{
ipv6address found_begin, found_end;
_parser_get_ipv6(p, &found_begin, &found_end);
range6list_add_range(targets_ipv6, found_begin, found_end);
addr_count++;
}
break;
}
}
}
/* Close the file, unless we are reading from */
if (fp != stdin && fp != NULL)
fclose(fp);
/* In case the file doesn't end with a newline '\n', then artificially
* add one to the end. This is just a repeat of the code above */
if (!is_error) {
size_t offset = 0;
unsigned begin, end;
int err;
err = _parser_next(p, "\n", &offset, 1, &begin, &end);
switch (err) {
case Still_Working:
break;
case Found_Error:
default:
_parser_err(p, &line_number, &char_number);
fprintf(stderr, "[-] %s:%llu:%llu: invalid IP address on line #%llu\n", filename, line_number, char_number, line_number);
is_error = true;
break;
case Found_IPv4:
rangelist_add_range(targets_ipv4, begin, end);
addr_count++;
break;
case Found_IPv6:
{
ipv6address found_begin, found_end;
_parser_get_ipv6(p, &found_begin, &found_end);
range6list_add_range(targets_ipv6, found_begin, found_end);
addr_count++;
}
break;
}
}
LOG(1, "[+] %s: %u addresses read\n", filename, addr_count);
/* Target list must be sorted every time it's been changed,
* before it can be used */
rangelist_sort(targets_ipv4);
if (is_error)
return -1; /* fail */
else
return 0; /* success*/
}
ipv6address
massip_parse_ipv6(const char *line)
{
struct massip_parser p[1];
size_t count = strlen(line);
size_t offset = 0;
int err;
unsigned begin, end;
ipv6address result;
ipv6address range;
_parser_init(p);
err = _parser_next(p, line, &offset, count, &begin, &end);
again:
switch (err) {
case Still_Working:
if (offset < count) {
/* We reached this somehow in the middle of the buffer, but
* this return is only possible at the end of the buffer */
fprintf(stderr, "[-] _parser_next(): unknown coding failure\n");
goto fail;
} else {
err = _parser_next(p, "\n", 0, 1, &begin, &end);
if (err == Still_Working) {
fprintf(stderr, "[-] _parser_next(): unknown coding failure\n");
goto fail;
} else {
goto again;
}
}
break;
case Found_Error:
default:
goto fail;
case Found_IPv4:
goto fail;
case Found_IPv6:
_parser_get_ipv6(p, &result, &range);
if (!ipv6address_is_equal(result, range))
goto fail;
return result;
}
fail:
result.hi = ~0ULL;
result.lo = ~0ULL;
return result;
}
unsigned
massip_parse_ipv4(const char *line)
{
struct massip_parser p[1];
size_t count = strlen(line);
size_t offset = 0;
int err;
unsigned begin, end;
_parser_init(p);
err = _parser_next(p, line, &offset, count, &begin, &end);
again:
switch (err) {
case Still_Working:
if (offset < count) {
/* We reached this somehow in the middle of the buffer, but
* this return is only possible at the end of the buffer */
fprintf(stderr, "[-] _parser_next(): unknown coding failure\n");
goto fail;
} else {
err = _parser_next(p, "\n", 0, 1, &begin, &end);
if (err == Still_Working) {
fprintf(stderr, "[-] _parser_next(): unknown coding failure\n");
goto fail;
} else {
goto again;
}
}
break;
case Found_Error:
default:
goto fail;
case Found_IPv6:
goto fail;
case Found_IPv4:
if (begin != end)
goto fail;
return begin;
}
fail:
return 0xFFFFFFFF;
}
enum RangeParseResult
massip_parse_range(const char *line, size_t *offset, size_t count, struct Range *ipv4, struct Range6 *ipv6)
{
struct massip_parser p[1];
int err;
unsigned begin, end;
size_t tmp_offset = 0;
/* The 'count' (length of the string) is an optional parameter. If
* zero, and also the offset is NULL, then set it to the string length */
if (count == 0 && offset == NULL)
count = strlen(line);
/* The offset is an optional parameter. If NULL, then we set
* it to point to a value on the stack instead */
if (offset == NULL)
offset = &tmp_offset;
/* Create e parser object */
_parser_init(p);
/* Parse the next range from the input */
err = _parser_next(p, line, offset, count, &begin, &end);
again:
switch (err) {
case Still_Working:
if (*offset < count) {
/* We reached this somehow in the middle of the buffer, but
* this return is only possible at the end of the buffer */
fprintf(stderr, "[-] _parser_next(): unknown coding failure\n");
return Bad_Address;
} else {
err = _parser_next(p, "\n", 0, 1, &begin, &end);
if (err == Still_Working) {
fprintf(stderr, "[-] _parser_next(): unknown coding failure\n");
return Bad_Address;
} else {
goto again;
}
}
break;
case Found_Error:
default:
return Bad_Address;
case Found_IPv4:
ipv4->begin = begin;
ipv4->end = end;
return Ipv4_Address;
case Found_IPv6:
_parser_get_ipv6(p, &ipv6->begin, &ipv6->end);
return Ipv6_Address;
}
}
/**
* This tests parsing when addresses/ranges are specified on the command-line
* or configuration files, rather than the other test-cases which test parsing
* when the IP addresses are specified in a file. The thing we are looking for
* here is specifically when users separate addresses with things like
* commas and spaces.
*/
static int
selftest_massip_parse_range(void)
{
struct testcases {
const char *line;
union {
struct Range ipv4;
struct Range6 ipv6;
} list[4];
} cases[] = {
{"0.0.1.0/24,0.0.3.0-0.0.4.0", {{{0x100,0x1ff}}, {{0x300,0x400}}}},
{"0.0.1.0-0.0.1.255,0.0.3.0-0.0.4.0", {{{0x100,0x1ff}}, {{0x300,0x400}}}},
{"0.0.1.0/24 0.0.3.0-0.0.4.0", {{{0x100,0x1ff}}, {{0x300,0x400}}}},
{0}
};
size_t i;
for (i=0; cases[i].line; i++) {
size_t length = strlen(cases[i].line);
size_t offset = 0;
size_t j = 0;
struct Range6 range6;
struct Range range4;
while (offset < length) {
int x;
x = massip_parse_range(cases[i].line, &offset, length, &range4, &range6);
switch (x) {
default:
case Bad_Address:
fprintf(stdout, "[-] selftest_massip_parse_range[%u] fail\n", (unsigned)i);
return 1;
case Ipv4_Address:
if (cases[i].list[j].ipv4.begin != range4.begin
|| cases[i].list[j].ipv4.end != range4.end) {
fprintf(stdout, "[-] %u.%u.%u.%u - %u.%u.%u.%u\n",
(unsigned char)(range4.begin>>24),
(unsigned char)(range4.begin>>16),
(unsigned char)(range4.begin>> 8),
(unsigned char)(range4.begin>> 0),
(unsigned char)(range4.end>>24),
(unsigned char)(range4.end>>16),
(unsigned char)(range4.end>> 8),
(unsigned char)(range4.end>> 0)
);
fprintf(stdout, "[-] selftest_massip_parse_range[%u] fail\n", (unsigned)i);
return 1;
}
break;
}
j++;
}
/* Make sure we have found all the expected cases */
if (cases[i].list[j].ipv4.begin != 0) {
fprintf(stdout, "[-] selftest_massip_parse_range[%u] fail\n", (unsigned)i);
return 1;
}
}
return 0;
}
/***************************************************************************
***************************************************************************/
static int
rangefile6_test_buffer(struct massip_parser *parser,
const char *buf,
ipv6address expected_begin,
ipv6address expected_end)
{
size_t length = strlen(buf);
size_t offset = 0;
ipv6address found_begin = {1,2};
ipv6address found_end = {1,2};
unsigned tmp1, tmp2;
int err;
/* test the entire buffer */
err = _parser_next(parser, buf, &offset, length, &tmp1, &tmp2);
if (err == Still_Working)
err = _parser_next(parser, "\n", 0, 1, &tmp1, &tmp2);
switch (err) {
case Found_IPv6:
/* Extract the resulting IPv6 address from the state structure */
_parser_get_ipv6(parser, &found_begin, &found_end);
/* Test to see if the parsed address equals the expected address */
if (!ipv6address_is_equal(found_begin, expected_begin)) {
ipaddress_formatted_t fmt1 = ipv6address_fmt(found_begin);
ipaddress_formatted_t fmt2 = ipv6address_fmt(expected_begin);
fprintf(stderr, "[-] begin mismatch: found=[%s], expected=[%s]\n", fmt1.string, fmt2.string);
goto fail;
}
if (!ipv6address_is_equal(found_end, expected_end)) {
ipaddress_formatted_t fmt1 = ipv6address_fmt(found_end);
ipaddress_formatted_t fmt2 = ipv6address_fmt(expected_end);
fprintf(stderr, "[-] end mismatch: found=[%s], expected=[%s]\n", fmt1.string, fmt2.string);
goto fail;
}
break;
case Found_IPv4:
if (expected_begin.hi != 0 || expected_end.hi != 0)
goto fail;
if (tmp1 != expected_begin.lo || tmp2 != expected_end.lo)
goto fail;
break;
case Still_Working:
/* Found a partial address, which is a normal result in the
* real world at buffer boundaries, but which is an error
* here */
goto fail;
case Found_Error:
default:
goto fail;
}
return 0; /* success */
fail:
return 1; /* failure */
}
/***************************************************************************
* List of test cases. Each test case contains three parts:
* - the string representation of an address, as read from a file, meaning
* that it can contain additional things like comment strings
* - the first address of a range, which in the case of IPv6 addresses
* will be two 64-bit numbers, but an IPv4 address have a high-order
* number set to zero and the low-order number set to the IPv4 address
* - the second address of a range, which in the case of individual
* addresses, will be equal to the first number
***************************************************************************/
struct {
const char *string;
ipv6address begin;
ipv6address end;
} test_cases[] = {
{"[1::1]/126", {0x0001000000000000ULL, 0ULL}, {0x0001000000000000ULL, 3ULL}},
{"1::1/126", {0x0001000000000000ULL, 0ULL}, {0x0001000000000000ULL, 3ULL}},
{"[1::1]-[2::3]", {0x0001000000000000ULL, 1ULL}, {0x0002000000000000ULL, 3ULL}},
{"1::1-2::3", {0x0001000000000000ULL, 1ULL}, {0x0002000000000000ULL, 3ULL}},
{"[1234:5678:9abc:def0:0fed:cba9:8765:4321]", {0x123456789abcdef0ULL, 0x0fedcba987654321ULL}, {0x123456789abcdef0ULL, 0x0fedcba987654321ULL}},
{"22ab::1", {0x22ab000000000000ULL, 1ULL}, {0x22ab000000000000ULL, 1ULL}},
{"240e:33c:2:c080:d08:d0e:b53:e74e", {0x240e033c0002c080ULL, 0x0d080d0e0b53e74eULL}, {0x240e033c0002c080ULL, 0x0d080d0e0b53e74eULL}},
{"2a03:90c0:105::9", {0x2a0390c001050000ULL, 9ULL}, {0x2a0390c001050000ULL, 9ULL}},
{"2a03:9060:0:400::2", {0x2a03906000000400ULL, 2ULL}, {0x2a03906000000400ULL, 2ULL}},
{"2c0f:ff00:0:a:face:b00c:0:a7", {0x2c0fff000000000aULL, 0xfaceb00c000000a7ULL}, {0x2c0fff000000000aULL, 0xfaceb00c000000a7ULL}},
{"2a01:5b40:0:4a01:0:e21d:789f:59b1", {0x2a015b4000004a01ULL, 0x0000e21d789f59b1ULL}, {0x2a015b4000004a01ULL, 0x0000e21d789f59b1ULL}},
{"2001:1200:10::1", {0x2001120000100000ULL, 1ULL}, {0x2001120000100000ULL, 1ULL}},
{"fec0:0:0:ffff::1", {0xfec000000000ffffULL, 1ULL}, {0xfec000000000ffffULL, 1ULL}},
{"1234:5678:9abc:def0:0fed:cba9:8765:4321", {0x123456789abcdef0ULL, 0x0fedcba987654321ULL}, {0x123456789abcdef0ULL, 0x0fedcba987654321ULL}},
{"[1111:2222:3333:4444:5555:6666:7777:8888]", {0x1111222233334444ULL, 0x5555666677778888ULL}, {0x1111222233334444ULL, 0x5555666677778888ULL}},
{"1::1", {0x0001000000000000ULL, 1ULL}, {0x0001000000000000ULL, 1ULL}},
{"1.2.3.4", {0, 0x01020304}, {0, 0x01020304}},
{"#test\n 97.86.162.161" "\x96" "97.86.162.175\n", {0, 0x6156a2a1}, {0, 0x6156a2af}},
{"1.2.3.4/24\n", {0, 0x01020300}, {0, 0x010203ff}},
{" 1.2.3.4-1.2.3.5\n", {0, 0x01020304}, {0, 0x01020305}},
{0,{0,0},{0,0}}
};
/***************************************************************************
* Called during "make test" to run a regression test over this module.
***************************************************************************/
int
massip_parse_selftest(void)
{
int x = 0;
size_t i;
struct massip_parser parser[1];
/* Run through the test cases, stopping at the first failure */
_parser_init(parser);
for (i=0; test_cases[i].string; i++) {
x += rangefile6_test_buffer(parser,
test_cases[i].string,
test_cases[i].begin,
test_cases[i].end);
if (x) {
fprintf(stderr, "[-] failed: %u: %s\n", (unsigned)i, test_cases[i].string);
break;
}
}
_parser_destroy(parser);
/* First, do the single line test */
x += selftest_massip_parse_range();
if (x)
return x;
x += rangefile_test_error("#bad ipv4\n 257.1.1.1\n", 2, 5, __LINE__);
x += rangefile_test_error("#bad ipv4\n 1.257.1.1.1\n", 2, 6, __LINE__);
x += rangefile_test_error("#bad ipv4\n 1.10.257.1.1.1\n", 2, 9, __LINE__);
x += rangefile_test_error("#bad ipv4\n 1.10.255.256.1.1.1\n", 2, 13, __LINE__);
x += rangefile_test_error("#bad ipv4\n 1.1.1.1.1\n", 2, 9, __LINE__);
if (x)
LOG(0, "[-] rangefile_selftest: fail\n");
return x;
}
================================================
FILE: src/massip-parse.h
================================================
/*
massip-parse
This module parses IPv4 and IPv6 addresses.
It's not a typical parser. It's optimized around parsing large
files containing millions of addresses and ranges using a
"state-machine parser".
*/
#ifndef MASSIP_PARSE_H
#define MASSIP_PARSE_H
#include "massip-addr.h"
struct MassIP;
struct Range;
struct Range6;
/**
* Parse a file, extracting all the IPv4 and IPv6 addresses and ranges.
* This is optimized for speed, handling millions of entries in under
* a second. This is especially tuned for IPv6 addresses, as while IPv4
* scanning is mostly done with target rnages, IPv6 scanning is mostly
* done with huge lists of target addresses.
* @param filename
* The name of the file that we'll open, parse, and close.
* @param targets_ipv4
* The list of IPv4 targets that we append any IPv4 addresses to.
* @param targets_ipv6
* The list of IPv6 targets that we append any IPv6 addresses/ranges to.
* @return
0 on success, any other number on failure.
*/
int
massip_parse_file(struct MassIP *massip, const char *filename);
enum RangeParseResult {
Bad_Address,
Ipv4_Address=4,
Ipv6_Address=6,
};
/**
* Parse the next IPv4/IPv6 range from a string. This is called
* when parsing strings from the command-line.
*/
enum RangeParseResult
massip_parse_range(const char *line, size_t *inout_offset, size_t max, struct Range *ipv4, struct Range6 *ipv6);
/**
* Parse a single IPv6 address. This is called when working with
* the operating system stack, when querying addresses from
* the local network adapters.
*/
ipv6address_t
massip_parse_ipv6(const char *buf);
ipv4address_t
massip_parse_ipv4(const char *buf);
/**
* Do a simplistic unit test of the parser.
* @return 0 on success, 1 on failure
*/
int
massip_parse_selftest(void);
#endif
================================================
FILE: src/massip-port.h
================================================
#ifndef MASSIP_PORT_H
#define MASSIP_PORT_H
/*
* Ports are 16-bit numbers ([0..65535], but different
* transports (TCP, UDP, SCTP) are distinct port ranges. Thus, we
* instead of three 64k ranges we could instead treat this internally
* as a 192k port range. We can expand this range to include other
* things we scan for, such as ICMP pings or ARP requests.
*/
enum {
Templ_TCP = 0,
Templ_TCP_last = 65535,
Templ_UDP = 65536,
Templ_UDP_last = 65536 + 65535,
Templ_SCTP = 65536*2,
Templ_SCTP_last = 65536*2 + 65535,
Templ_ICMP_echo = 65536*3+0,
Templ_ICMP_timestamp = 65536*3+1,
Templ_ARP = 65536*3+2,
Templ_Oproto_first = 65536*3 + 256,
Templ_Oproto_last = 65536*3 + 256 + 255,
Templ_VulnCheck = 65536*4,
};
#endif
================================================
FILE: src/massip-rangesv4.c
================================================
/*
IPv4 and port ranges
This is one of the more integral concepts to how masscan works internally.
We combine all the input addresses and address ranges into a sorted list
of 'target' IP addresses. This allows us to enumerate all the addresses
in order by incrementing a simple index. It is that index that we randomize
in order to produce random output, but internally, everything is sorted.
Sorting the list allows us to remove duplicates. It also allows us to
apply the 'excludes' directly to the input list. In other words, other
scanners typically work by selecting an IP address at random, then checking
to see if it's been excluded, then skipping it. In this scanner, however,
we remove all the excluded address from the targets list before we start
scanning.
This module has been tuned to support mass lists of millions of target
IPv4 addresses and excludes. This has required:
- a fast way to parse the address from a file (see range-file.c)
- fast sort (just using qsort() from the standard C library)
- fast application of exludes, using an optimal O(n + m) linear
algorithm, where 'n' is the number of targets, and 'm' is the
number of excluded ranges.
Large lists can still take a bit to process. On a fast server with
7-million input ranges/addresses and 5000 exclude ranges/addresses,
it takes almost 3 seconds to process everything before starting.
*/
#include "massip-rangesv4.h"
#include "massip-port.h"
#include "util-logger.h"
#include "util-bool.h"
#include "util-malloc.h"
#include
#include
#include
#include
#include
#include
#ifdef _MSC_VER
#pragma warning(disable:4204)
#endif
#define BUCKET_COUNT 16
#define REGRESS(x) if (!(x)) return (fprintf(stderr, "regression failed %s:%d\n", __FILE__, __LINE__)|1)
/* An invalid range, where begin comes after the end */
static struct Range INVALID_RANGE = {2,1};
/***************************************************************************
* Does a linear search to see if the list contains the address/port.
* FIXME: This should be upgraded to a binary search. However, we don't
* really use it in any performance critical code, so it's okay
* as a linear search.
***************************************************************************/
int
rangelist_is_contains(const struct RangeList *targets, unsigned addr)
{
unsigned i;
for (i=0; icount; i++) {
struct Range *range = &targets->list[i];
if (range->begin <= addr && addr <= range->end)
return 1;
}
return 0;
}
/***************************************************************************
* Returns the first CIDR range (which can be specified with prefix bits)
* that fits within the input range. For example, consider the range
* [10.0.0.4->10.0.0.255]. This does't match the bigger CIDR range.
* The first range that would fit would be [10.0.0.04/30], or
* [10.0.0.4->10.0.0.7].
*
* Using this function allows us to decompose
***************************************************************************/
struct Range
range_first_cidr(const struct Range range, unsigned *prefix_bits) {
struct Range result = {range.begin, range.end};
unsigned zbits = 0;
/* Kludge: Special Case:
* All inputs work but the boundary case of [0.0.0.0/0] or
* [0.0.0.0-255.255.255.255]. I can't be bothered to figure out
* why the algorithm doesn't work with this range, so I'm just
* going to special case it here*/
if (range.begin == 0 && range.end == 0xFFFFffff) {
if (prefix_bits != NULL)
*prefix_bits = 0;
return range;
}
/* Count the number of trailing/suffix zeros, which may be range
* from none (0) to 32 (all bits are 0) */
for (zbits = 0; zbits <= 32; zbits++) {
if ((range.begin & (1< 0) {
unsigned mask = ~(0xFFFFFFFF << zbits);
if (range.begin + mask > range.end)
zbits--;
else
break;
}
result.begin = range.begin;
result.end = range.begin + ~(0xFFFFffff << zbits);
if (prefix_bits != NULL)
*prefix_bits = 32-zbits;
return result;
}
bool
range_is_cidr(const struct Range range, unsigned *prefix_bits) {
struct Range out = range_first_cidr(range, prefix_bits);
if (out.begin == range.begin && out.end == range.end)
return true;
else {
if (prefix_bits != NULL)
*prefix_bits = 0xFFFFFFFF;
return false;
}
}
/***************************************************************************
* Selftest for the above function.
***************************************************************************/
static int
selftest_range_first_cidr(void) {
static struct {
struct Range in;
struct Range out;
unsigned prefix_bits;
} tests[] = {
{{0x00000000, 0xffffffff}, {0x00000000, 0xffffffff}, 0},
{{0x00000001, 0xffffffff}, {0x00000001, 0x00000001}, 32},
{{0xffffffff, 0xffffffff}, {0xffffffff, 0xffffffff}, 32},
{{0xfffffffe, 0xfffffffe}, {0xfffffffe, 0xfffffffe}, 32},
{{0x0A000000, 0x0A0000Ff}, {0x0A000000, 0x0A0000ff}, 24},
{{0x0A0000ff, 0x0A0000Ff}, {0x0A0000ff, 0x0A0000ff}, 32},
{{0x0A000000, 0x0A0000Ff}, {0x0A000000, 0x0A0000Ff}, 24},
{{0x0A000001, 0x0A0000Fe}, {0x0A000001, 0x0A000001}, 32},
{{0x0A000008, 0x0A0000Fe}, {0x0A000008, 0x0A00000f}, 29},
{{0x0A000080, 0x0A0000Fe}, {0x0A000080, 0x0A0000bf}, 26},
{{0x0A0000c0, 0x0A0000Fe}, {0x0A0000c0, 0x0A0000df}, 27},
{{0x0A0000c1, 0x0A0000Fe}, {0x0A0000c1, 0x0A0000c1}, 32},
{{0x0A0000fe, 0x0A0000Fe}, {0x0A0000fe, 0x0A0000fe}, 32},
{{0,0}, {0,0}}
};
size_t i;
for (i=0; tests[i].in.end != 0; i++) {
unsigned prefix_bits = 0xFFFFFFFF;
struct Range out = range_first_cidr(tests[i].in, &prefix_bits);
if (out.begin != tests[i].out.begin
|| out.end != tests[i].out.end
|| prefix_bits != tests[i].prefix_bits) {
fprintf(stderr, "[%u] 0x%08x->0x%08x /%u 0x%08x->0x%08x /%u\n",
(unsigned)i,
out.begin,
out.end,
prefix_bits,
tests[i].out.begin,
tests[i].out.end,
tests[i].prefix_bits);
return 1;
}
}
return 0;
}
/***************************************************************************
* Test if two ranges overlap.
* FIXME: I need to change this so that it (a) doesn't trigger on invalid
* ranges (those where begin>end) and (b) use a simpler algorithm
***************************************************************************/
static int
range_is_overlap(struct Range lhs, struct Range rhs)
{
if (lhs.begin < rhs.begin) {
if (lhs.end == 0xFFFFFFFF || lhs.end + 1 >= rhs.begin)
return 1;
}
if (lhs.begin >= rhs.begin) {
if (lhs.end <= rhs.end)
return 1;
}
if (rhs.begin < lhs.begin) {
if (rhs.end == 0xFFFFFFFF || rhs.end + 1 >= lhs.begin)
return 1;
}
if (rhs.begin >= lhs.begin) {
if (rhs.end <= lhs.end)
return 1;
}
return 0;
}
/***************************************************************************
* Combine two ranges, such as when they overlap.
***************************************************************************/
static void
range_combine(struct Range *lhs, struct Range rhs)
{
if (lhs->begin > rhs.begin)
lhs->begin = rhs.begin;
if (lhs->end < rhs.end)
lhs->end = rhs.end;
}
/***************************************************************************
* Callback for qsort() for comparing two ranges
***************************************************************************/
static int
range_compare(const void *lhs, const void *rhs)
{
struct Range *left = (struct Range *)lhs;
struct Range *right = (struct Range *)rhs;
if (left->begin < right->begin)
return -1;
else if (left->begin > right->begin)
return 1;
else
return 0;
}
/***************************************************************************
***************************************************************************/
static void
rangelist_remove_at(struct RangeList *targets, size_t index)
{
memmove(&targets->list[index],
&targets->list[index+1],
(targets->count - index) * sizeof(targets->list[index])
);
targets->count--;
}
/***************************************************************************
***************************************************************************/
void
rangelist_sort(struct RangeList *targets)
{
size_t i;
struct RangeList newlist = {0};
unsigned original_count = targets->count;
/* Empty lists are, of course, sorted. We need to set this
* to avoid an error later on in the code which asserts that
* the lists are sorted */
if (targets->count == 0) {
targets->is_sorted = 1;
return;
}
/* If it's already sorted, then skip this */
if (targets->is_sorted) {
return;
}
/* First, sort the list */
LOG(3, "[+] range:sort: sorting...\n");
qsort( targets->list, /* the array to sort */
targets->count, /* number of elements to sort */
sizeof(targets->list[0]), /* size of element */
range_compare);
/* Second, combine all overlapping ranges. We do this by simply creating
* a new list from a sorted list, so we don't have to remove things in the
* middle when collapsing overlapping entries together, which is painfully
* slow. */
LOG(3, "[+] range:sort: combining...\n");
for (i=0; icount; i++) {
rangelist_add_range(&newlist, targets->list[i].begin, targets->list[i].end);
}
LOG(3, "[+] range:sort: combined from %u elements to %u elements\n", original_count, newlist.count);
free(targets->list);
targets->list = newlist.list;
targets->count = newlist.count;
newlist.list = 0;
LOG(2, "[+] range:sort: done...\n");
targets->is_sorted = 1;
}
/***************************************************************************
* Add the IPv4 range to our list of ranges.
***************************************************************************/
void
rangelist_add_range(struct RangeList *targets, unsigned begin, unsigned end)
{
struct Range range;
range.begin = begin;
range.end = end;
/* auto-expand the list if necessary */
if (targets->count + 1 >= targets->max) {
targets->max = targets->max * 2 + 1;
targets->list = REALLOCARRAY(targets->list, targets->max, sizeof(targets->list[0]));
}
/* If empty list, then add this one */
if (targets->count == 0) {
targets->list[0] = range;
targets->count++;
targets->is_sorted = 1;
return;
}
/* If new range overlaps the last range in the list, then combine it
* rather than appending it. This is an optimization for the fact that
* we often read in sequential addresses */
if (range_is_overlap(targets->list[targets->count - 1], range)) {
range_combine(&targets->list[targets->count - 1], range);
targets->is_sorted = 0;
return;
}
/* append to the end of our list */
targets->list[targets->count] = range;
targets->count++;
targets->is_sorted = 0;
}
/** Use this when adding TCP ports, to avoid the comoplication of how
* ports are stored */
void
rangelist_add_range_tcp(struct RangeList *targets, unsigned begin, unsigned end) {
rangelist_add_range(targets,
Templ_TCP + begin,
Templ_TCP + end);
}
/** Use this when adding UDP ports, to avoid the comoplication of how
* ports are stored */
void
rangelist_add_range_udp(struct RangeList *targets, unsigned begin, unsigned end) {
rangelist_add_range(targets,
Templ_UDP + begin,
Templ_UDP + end);
}
/***************************************************************************
* This is the "free" function for the list, freeing up any memory we've
* allocated.
***************************************************************************/
void
rangelist_remove_all(struct RangeList *targets)
{
free(targets->list);
free(targets->picker);
memset(targets, 0, sizeof(*targets));
}
/***************************************************************************
***************************************************************************/
void
rangelist_merge(struct RangeList *list1, const struct RangeList *list2)
{
unsigned i;
for (i=0; icount; i++) {
rangelist_add_range(list1, list2->list[i].begin, list2->list[i].end);
}
rangelist_sort(list1);
}
/***************************************************************************
* This searches a range list and removes that range of IP addresses, if
* they exist. Since the input range can overlap multiple entries, then
* more than one entry can be removed, or truncated. Since the range
* can be in the middle of an entry in the list, it can actually increase
* the list size by one, as that entry is split into two entries.
* DEPRECATED: this function is deprecated, and will be removed at some
* point. It's only remaining in order to serve as a regression test for
* its replacement.
***************************************************************************/
static void
rangelist_remove_range(struct RangeList *targets, unsigned begin, unsigned end)
{
unsigned i;
struct Range x;
x.begin = begin;
x.end = end;
/* See if the range overlaps any exist range already in the
* list */
for (i = 0; i < targets->count; i++) {
if (!range_is_overlap(targets->list[i], x))
continue;
/* If the removal-range wholly covers the range, delete
* it completely */
if (begin <= targets->list[i].begin && end >= targets->list[i].end) {
rangelist_remove_at(targets, i);
i--;
continue;
}
/* If the removal-range bisects the target-rage, truncate
* the lower end and add a new high-end */
if (begin > targets->list[i].begin && end < targets->list[i].end) {
struct Range newrange;
newrange.begin = end+1;
newrange.end = targets->list[i].end;
targets->list[i].end = begin-1;
rangelist_add_range(targets, newrange.begin, newrange.end);
i--;
continue;
}
/* If overlap on the lower side */
if (end >= targets->list[i].begin && end < targets->list[i].end) {
targets->list[i].begin = end+1;
}
/* If overlap on the upper side */
if (begin > targets->list[i].begin && begin <= targets->list[i].end) {
targets->list[i].end = begin-1;
}
//assert(!"impossible");
}
}
static void
rangelist_add_range2(struct RangeList *targets, struct Range range)
{
rangelist_add_range(targets, range.begin, range.end);
}
static void
rangelist_remove_range2(struct RangeList *targets, struct Range range)
{
rangelist_remove_range(targets, range.begin, range.end);
}
/***************************************************************************
* Parse an IPv4 address from a line of text, moving the offset forward
* to the first non-IPv4 character
***************************************************************************/
static int
parse_ipv4(const char *line, unsigned *inout_offset, unsigned max, unsigned *ipv4)
{
unsigned offset = *inout_offset;
unsigned result = 0;
unsigned i;
for (i=0; i<4; i++) {
unsigned x = 0;
unsigned digits = 0;
if (offset >= max)
return -4;
if (!isdigit(line[offset]&0xFF))
return -1;
/* clear leading zeros */
while (offset < max && line[offset] == '0')
offset++;
/* parse maximum of 3 digits */
while (offset < max && isdigit(line[offset]&0xFF)) {
x = x * 10 + (line[offset] - '0');
offset++;
if (++digits > 3)
return -2;
}
if (x > 255)
return -5;
result = result * 256 + (x & 0xFF);
if (i == 3)
break;
if (line[offset] != '.')
return -3;
offset++; /* skip dot */
}
*inout_offset = offset;
*ipv4 = result;
return 0; /* parse OK */
}
/****************************************************************************
* Parse from text an IPv4 address range. This can be in one of several
* formats:
* - '192.168.1.1" - a single address
* - '192.168.1.0/24" - a CIDR spec
* - '192.168.1.0-192.168.1.255' - a range
* @param line
* Part of a line of text, probably read from a commandline or conf
* file.
* @param inout_offset
* On input, the offset from the start of the line where the address
* starts. On output, the offset of the first character after the
* range, or equal to 'max' if the line prematurely ended.
* @param max
* The maximum length of the line.
* @return
* The first and last address of the range, inclusive.
****************************************************************************/
struct Range
range_parse_ipv4(const char *line, unsigned *inout_offset, unsigned max)
{
unsigned offset;
struct Range result;
static const struct Range badrange = {0xFFFFFFFF, 0};
int err;
if (line == NULL)
return badrange;
if (inout_offset == NULL) {
inout_offset = &offset;
offset = 0;
max = (unsigned)strlen(line);
} else
offset = *inout_offset;
/* trim whitespace */
while (offset < max && isspace(line[offset]&0xFF))
offset++;
/* get the first IP address */
err = parse_ipv4(line, &offset, max, &result.begin);
if (err) {
return badrange;
}
result.end = result.begin;
/* trim whitespace */
while (offset < max && isspace(line[offset]&0xFF))
offset++;
/* If only one IP address, return that */
if (offset >= max)
goto end;
/*
* Handle CIDR address of the form "10.0.0.0/8"
*/
if (line[offset] == '/') {
uint64_t prefix = 0;
uint64_t mask = 0;
unsigned digits = 0;
/* skip slash */
offset++;
if (!isdigit(line[offset]&0xFF)) {
return badrange;
}
/* strip leading zeroes */
while (offset 2)
return badrange;
}
if (prefix > 32)
return badrange;
/* Create the mask from the prefix */
mask = 0xFFFFFFFF00000000ULL >> prefix;
/* Mask off any non-zero bits from the start
* TODO print warning */
result.begin &= mask;
/* Set all suffix bits to 1, so that 192.168.1.0/24 has
* an ending address of 192.168.1.255. */
result.end = result.begin | (unsigned)~mask;
goto end;
}
/*
* Handle a dashed range like "10.0.0.100-10.0.0.200"
*/
if (offset>24)&0xFF), ((ip>>16)&0xFF), ((ip>>8)&0xFF), ((ip>>0)&0xFF),
((result.begin>>24)&0xFF), ((result.begin>>16)&0xFF), ((result.begin>>8)&0xFF), ((result.begin>>0)&0xFF)
);
} else
result.end = ip;
goto end;
}
end:
*inout_offset = offset;
return result;
}
/***************************************************************************
* This is the old algorithm for applying exclude ranges, very slow
* for large lists. We keep it around for verifying correctness of the
* new replacement algorithm.
***************************************************************************/
static void
rangelist_exclude2( struct RangeList *targets,
const struct RangeList *excludes)
{
unsigned i;
for (i=0; icount; i++) {
struct Range range = excludes->list[i];
rangelist_remove_range(targets, range.begin, range.end);
}
/* Since chopping up large ranges can split ranges, this can
* grow the list so we need to re-sort it */
rangelist_sort(targets);
}
/**
* Applies the (presumably overlapping) exclude range to the target. This can have
* four outcomes:
* - there is no overlap, in which case 'target' is unchanged, and 'split'
* is set to INVALID.
* - the entire target is excluded, in which case it's set to INVALID.
* - the overlap is at the beginning, in which case the 'begin' is increased.
* - the overlap is at the end, in which case 'end' is reduced.
* - the overlap is in the middle, in which case the target is split
* in two, with 'target' becoming the low addresses, and 'split' becoming
* the high addresses.
*/
static void
range_apply_exclude(const struct Range exclude, struct Range *target, struct Range *split)
{
/* Set 'split' to invalid to start with */
split->begin = 2;
split->end = 1;
/* Case 1: no overlap */
if (target->begin > exclude.end || target->end < exclude.begin) {
return;
}
/* Case 2: complete overlap, mark target as invalid and return */
if (target->begin >= exclude.begin && target->end <= exclude.end) {
target->begin = 2;
target->end = 1;
return;
}
/* Case 3: overlap at start */
if (target->begin >= exclude.begin && target->end > exclude.end) {
target->begin = exclude.end + 1;
return;
}
/* Case 4: overlap at end */
if (target->begin < exclude.begin && target->end <= exclude.end) {
target->end = exclude.begin - 1;
return;
}
/* Case 5: this range needs to be split */
if (target->begin < exclude.begin && target->end > exclude.end) {
split->end = target->end;
split->begin = exclude.end + 1;
target->end = exclude.begin - 1;
return;
}
/* No other condition should be possible */
assert(!"possible");
}
/***************************************************************************
***************************************************************************/
int
range_is_valid(struct Range range)
{
return range.begin <= range.end;
}
/***************************************************************************
* Apply the exclude ranges, which means removing everything from "targets"
* that's also in "exclude". This can make the target list even bigger
* as individually excluded address chop up large ranges.
***************************************************************************/
void
rangelist_exclude( struct RangeList *targets,
struct RangeList *excludes)
{
unsigned i;
unsigned x;
struct RangeList newlist = {0};
/* Both lists must be sorted */
rangelist_sort(targets);
rangelist_sort(excludes);
/* Go through all target ranges, apply excludes to them
* (which may split into two ranges), and add them to
* the new target list */
x = 0;
for (i=0; icount; i++) {
struct Range range = targets->list[i];
/* Move the exclude forward until we find a potentially
* overlapping candidate */
while (x < excludes->count && excludes->list[x].end < range.begin)
x++;
/* Keep applying excludes to this range as long as there are overlaps */
while (x < excludes->count && excludes->list[x].begin <= range.end) {
struct Range split = INVALID_RANGE;
range_apply_exclude(excludes->list[x], &range, &split);
/* If there is a split, then add the original range to our list
* and then set that range to the split-ed portion */
if (range_is_valid(split)) {
rangelist_add_range(&newlist, range.begin, range.end);
memcpy(&range, &split, sizeof(range));
}
if (excludes->list[x].begin > range.end)
break;
x++;
}
/* If the range hasn't been completely excluded, then add the remnants */
if (range_is_valid(range)) {
rangelist_add_range(&newlist, range.begin, range.end);
}
}
/* Now free the old list and move over the new list */
free(targets->list);
targets->list = newlist.list;
targets->count = newlist.count;
newlist.list = NULL;
newlist.count = 0;
/* Since chopping up large ranges can split ranges, this can
* grow the list so we need to re-sort it */
rangelist_sort(targets);
}
/***************************************************************************
* Counts the total number of addresses in all the ranges combined.
* For 0.0.0.0/0, this will be 0x100000000, which means we have to use a
* larger number than 32-bit to return the result. This assumes that
* all overlaps have been resolved in the list (i.e. it's been sorted).
***************************************************************************/
uint64_t
rangelist_count(const struct RangeList *targets)
{
unsigned i;
uint64_t result = 0;
for (i=0; icount; i++) {
result += (uint64_t)targets->list[i].end - (uint64_t)targets->list[i].begin + 1UL;
}
return result;
}
/***************************************************************************
* Get's the indexed port/address.
*
* Note that this requires a search of all the ranges. Currently, this is
* done by a learn search of the ranges. This needs to change, because
* once we start adding in a lot of "exclude ranges", the address space
* will get fragmented, and the linear search will take too long.
***************************************************************************/
static unsigned
rangelist_pick_linearsearch(const struct RangeList *targets, uint64_t index)
{
unsigned i;
for (i=0; i