inital public release
This commit is contained in:
commit
490054d239
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.pyc
|
||||||
|
*~
|
||||||
|
\#*
|
||||||
|
src/zmap
|
3
AUTHORS
Normal file
3
AUTHORS
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Zakir Durumeric <zakird@umich.edu>
|
||||||
|
J. Alex Halderman <jhalderm@umich.edu>
|
||||||
|
Eric Wustrow <ewust@umich.edu>
|
25
INSTALL
Normal file
25
INSTALL
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
ZMap is designed to run on GNU/Linux systems and can be built with
|
||||||
|
most recent versions of gcc. ZMap requires GMP, a free library for
|
||||||
|
arbitrary precision arithmetic, gengetopt, and libpcap.
|
||||||
|
|
||||||
|
These packages can be installed on Debian-based systems by running:
|
||||||
|
|
||||||
|
sudo apt-get install libgmp3-dev gengetopt libpcap-dev
|
||||||
|
|
||||||
|
or on RHEL- and Fedora-based systems by running:
|
||||||
|
|
||||||
|
sudo yum install gmp gmp-devel gengetopt libpcap-devel
|
||||||
|
|
||||||
|
Once these prerequisites have been installed, ZMap can be installed by running:
|
||||||
|
|
||||||
|
cd src
|
||||||
|
make
|
||||||
|
|
||||||
|
followed by:
|
||||||
|
|
||||||
|
sudo make install
|
||||||
|
|
||||||
|
Redis support is not enabled by default. If you are want to use ZMap
|
||||||
|
with Redis, you will first need to install Hiredis. Then, rebuild
|
||||||
|
ZMap with the command "make REDIS=true".
|
||||||
|
|
177
LICENSE
Normal file
177
LICENSE
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
25
README
Normal file
25
README
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
ZMap is a fast network scanner designed for Internet-wide network surveys. On a
|
||||||
|
typical desktop computer with a gigabit Ethernet connection, ZMap is capable
|
||||||
|
scanning the entire public IPv4 address space in under 45 minutes.
|
||||||
|
|
||||||
|
While previous network tools have been designed to scan small network segments,
|
||||||
|
ZMap is specifically architected to scan the entire address space. It is built
|
||||||
|
in a modular manner in order to allow incorporation with other network survey
|
||||||
|
tools. ZMap operates on GNU/Linux and supports TCP SYN and ICMP echo request
|
||||||
|
scanning out of the box.
|
||||||
|
|
||||||
|
Documentation and examples can be found at https://zmap.io/.
|
||||||
|
|
||||||
|
--
|
||||||
|
|
||||||
|
ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See LICENSE for the specific
|
||||||
|
language governing permissions and limitations under the License.
|
||||||
|
|
25
conf/blacklist.conf
Normal file
25
conf/blacklist.conf
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# From IANA IPv4 Special-Purpose Address Registry
|
||||||
|
# http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
|
||||||
|
# Updated 2013-05-22
|
||||||
|
|
||||||
|
0.0.0.0/8 # RFC1122: "This host on this network"
|
||||||
|
10.0.0.0/8 # RFC1918: Private-Use
|
||||||
|
100.64.0.0/10 # RFC6598: Shared Address Space
|
||||||
|
127.0.0.0/8 # RFC1122: Loopback
|
||||||
|
169.254.0.0/16 # RFC3927: Link Local
|
||||||
|
172.16.0.0/12 # RFC1918: Private-Use
|
||||||
|
192.0.0.0/24 # RFC6890: IETF Protocol Assignments
|
||||||
|
192.0.2.0/24 # RFC5737: Documentation (TEST-NET-1)
|
||||||
|
192.88.99.0/24 # RFC3068: 6to4 Relay Anycast
|
||||||
|
192.168.0.0/16 # RFC1918: Private-Use
|
||||||
|
192.18.0.0/15 # RFC2544: Benchmarking
|
||||||
|
198.51.100.0/24 # RFC5737: Documentation (TEST-NET-2)
|
||||||
|
203.0.113.0/24 # RFC5737: Documentation (TEST-NET-3)
|
||||||
|
240.0.0.0/4 # RFC1112: Reserved
|
||||||
|
255.255.255.255/32 # RFC0919: Limited Broadcast
|
||||||
|
|
||||||
|
# From IANA Multicast Address Space Registry
|
||||||
|
# http://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml
|
||||||
|
# Updated 2013-06-25
|
||||||
|
|
||||||
|
224.0.0.0/4 # RFC5771: Multicast/Reserved
|
22
conf/zmap.conf
Normal file
22
conf/zmap.conf
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
### Probe Module to use
|
||||||
|
#probe-module tcp_synscan
|
||||||
|
|
||||||
|
|
||||||
|
### Destination port to scan
|
||||||
|
#target-port 443
|
||||||
|
|
||||||
|
### Scan rate in packets/sec
|
||||||
|
#rate 10000
|
||||||
|
|
||||||
|
### Scan rate in bandwidth (bits/sec); overrides `rate`
|
||||||
|
#bandwidth 1M # 1mbps
|
||||||
|
|
||||||
|
|
||||||
|
### Blacklist file to use. We encourage you to exclude
|
||||||
|
### RFC1918, IANA reserved, and multicast networks,
|
||||||
|
### in addition to those who have opted out of your
|
||||||
|
### network scans.
|
||||||
|
blacklist-file "/etc/zmap/blacklist.conf"
|
||||||
|
|
||||||
|
### Optionally print a summary at the end
|
||||||
|
#summary
|
22
examples/banner-grab/Makefile
Normal file
22
examples/banner-grab/Makefile
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
|
||||||
|
CFLAGS+=-I../../lib/ -Wall
|
||||||
|
LDFLAGS+=-lpcap -levent -levent_extra -lm
|
||||||
|
VPATH=../../lib/
|
||||||
|
|
||||||
|
# from dpkg-buildflags --get CFLAGS, but use stack-protector-all and fPIC
|
||||||
|
GCCHARDENING=-g -O2 -fstack-protector-all --param=ssp-buffer-size=4 -Wformat -Wformat-security -Werror=format-security -fPIC
|
||||||
|
# from gpkg-buildflags --get LDFLAGS, + -z,now
|
||||||
|
LDHARDENING=-Wl,-Bsymbolic-functions -Wl,-z,relro,-z,now
|
||||||
|
|
||||||
|
CFLAGS+=$(GCCHARDENING)
|
||||||
|
LDFLAGS+=$(LDHARDENING)
|
||||||
|
|
||||||
|
|
||||||
|
all: banner-grab-tcp
|
||||||
|
|
||||||
|
banner-grab-tcp: banner-grab-tcp.o logger.o
|
||||||
|
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f banner-grab-tcp *.o
|
54
examples/banner-grab/README
Normal file
54
examples/banner-grab/README
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
TCP Banner Grab
|
||||||
|
======
|
||||||
|
|
||||||
|
This utility will connect (TCP) to ip addresses provide over stdin, optionally
|
||||||
|
send them a small message, and wait for their response. The response is then
|
||||||
|
printed along with their IP address on stdout. Status messages appear on stderr.
|
||||||
|
|
||||||
|
|
||||||
|
USING:
|
||||||
|
-----
|
||||||
|
make
|
||||||
|
#echo -e -n "GET / HTTP/1.1\r\nHost: %s\r\n\r\n" > http-req
|
||||||
|
zmap -p 80 -N 1000 -o - | ./banner-grab-tcp -p 80 -c 100 -d http-req > http-banners.out
|
||||||
|
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
-----
|
||||||
|
-c, --concurent Number of connections that can be going on at once.
|
||||||
|
This, combined with timeouts, will decide the maximum
|
||||||
|
rate at which banners are grabbed. If this value
|
||||||
|
is set higher than 1000, you should use
|
||||||
|
`ulimit -SSn 1000000` and `ulimit -SHn 1000000` to
|
||||||
|
avoid running out of file descriptors (typically capped
|
||||||
|
at 1024).
|
||||||
|
|
||||||
|
-p, --port The port which to connect to hosts on
|
||||||
|
|
||||||
|
-t, --conn-timeout Connection timeout (seconds). Give up on a host if connect
|
||||||
|
has not completed by this time. Default: 4 seconds.
|
||||||
|
|
||||||
|
-r, --read-timeout Read timeout (seconds). Give up on a host if after
|
||||||
|
connecting (and optionally sending data), it does
|
||||||
|
not send any response by this time. Default: 4 seconds.
|
||||||
|
|
||||||
|
-v, --verbosity Set status verbosity. Status/error messages are outputed
|
||||||
|
on stderr. This value can be 0-5, with 5 being the most
|
||||||
|
verbose (LOG_TRACE). Default: 3 (LOG_INFO)
|
||||||
|
|
||||||
|
-f, --format Format to output banner responses. One of 'hex', 'ascii',
|
||||||
|
or 'base64'.
|
||||||
|
'hex' outputs ascii hex characters, e.g. 48656c6c6f.
|
||||||
|
'ascii' outputs ascii, without separators, e.g. Hello
|
||||||
|
'base64' outputs base64 encoding, e.g. SGVsbG8=
|
||||||
|
Default is base64.
|
||||||
|
|
||||||
|
-d, --data Optional data file. This data will be sent to each host
|
||||||
|
upon successful connection. Currently, this file does
|
||||||
|
not allow null characters, but supports up to 4
|
||||||
|
occurances of the current host's IP address, by replacing
|
||||||
|
%s with the string (inet_ntoa) of that host's IP address.
|
||||||
|
|
||||||
|
|
||||||
|
|
440
examples/banner-grab/banner-grab-tcp.c
Normal file
440
examples/banner-grab/banner-grab-tcp.c
Normal file
@ -0,0 +1,440 @@
|
|||||||
|
/*
|
||||||
|
* Banner Grab Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
#include <event.h>
|
||||||
|
#include <event2/bufferevent_ssl.h>
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ulimit.h>
|
||||||
|
|
||||||
|
#define MAX_BANNER_LEN 1024
|
||||||
|
#define BASE64_ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||||
|
|
||||||
|
struct config {
|
||||||
|
uint16_t port;
|
||||||
|
int connect_timeout; // how long to wait for connecting (seconds)
|
||||||
|
int read_timeout; // how long to wait once connected for the banner (seconds)
|
||||||
|
int current_running;
|
||||||
|
int max_concurrent;
|
||||||
|
struct event_base *base;
|
||||||
|
struct bufferevent *stdin_bev;
|
||||||
|
int stdin_closed;
|
||||||
|
enum {FORMAT_HEX, FORMAT_BASE64, FORMAT_ASCII} format;
|
||||||
|
char *send_str;
|
||||||
|
long send_str_size;
|
||||||
|
|
||||||
|
struct stats_st {
|
||||||
|
int init_connected_hosts; // Number of hosts we have even tried to connect to
|
||||||
|
int connected_hosts; // # hosts that picked up
|
||||||
|
int conn_timed_out; // # hosts that timed out during connection
|
||||||
|
int read_timed_out; // # hosts that connected, but sent no data (banner)
|
||||||
|
int timed_out; // # hosts that timed out at all (conn_timed_out+read_timed_out)?
|
||||||
|
int completed_hosts; // # hosts that presented a banner
|
||||||
|
} stats;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct state {
|
||||||
|
struct config *conf;
|
||||||
|
uint32_t ip;
|
||||||
|
enum {CONNECTING, CONNECTED, RECEIVED} state;
|
||||||
|
};
|
||||||
|
|
||||||
|
void stdin_readcb(struct bufferevent *bev, void *arg);
|
||||||
|
|
||||||
|
void print_status(evutil_socket_t fd, short events, void *arg)
|
||||||
|
{
|
||||||
|
struct event *ev;
|
||||||
|
struct config *conf = arg;
|
||||||
|
struct event_base *base = conf->base;
|
||||||
|
struct timeval status_timeout = {1, 0};
|
||||||
|
ev = evtimer_new(base, print_status, conf);
|
||||||
|
evtimer_add(ev, &status_timeout);
|
||||||
|
(void)fd; (void)events;
|
||||||
|
|
||||||
|
log_info("banner-grab-tcp", "(%d/%d in use) - Totals: %d inited, %d connected, %d conn timeout, %d read timeout %d completed",
|
||||||
|
conf->current_running, conf->max_concurrent,
|
||||||
|
conf->stats.init_connected_hosts,
|
||||||
|
conf->stats.connected_hosts, conf->stats.conn_timed_out,
|
||||||
|
conf->stats.read_timed_out, conf->stats.completed_hosts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void decrement_cur_running(struct state *st)
|
||||||
|
{
|
||||||
|
struct config *conf = st->conf;
|
||||||
|
conf->current_running--;
|
||||||
|
log_debug("banner-grab-tcp", "done, down to %d",
|
||||||
|
conf->current_running);
|
||||||
|
if (evbuffer_get_length(bufferevent_get_input(conf->stdin_bev)) > 0) {
|
||||||
|
stdin_readcb(conf->stdin_bev, conf);
|
||||||
|
}
|
||||||
|
free(st);
|
||||||
|
|
||||||
|
if (conf->stdin_closed && conf->current_running == 0) {
|
||||||
|
// Done
|
||||||
|
log_info("banner-grab-tcp", "done");
|
||||||
|
print_status(0, 0, conf);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void connect_cb(struct bufferevent *bev, short events, void *arg)
|
||||||
|
{
|
||||||
|
struct state *st = arg;
|
||||||
|
struct config *conf = st->conf;
|
||||||
|
struct in_addr addr;
|
||||||
|
addr.s_addr = st->ip;
|
||||||
|
if (events & BEV_EVENT_CONNECTED) {
|
||||||
|
struct timeval tv = {st->conf->read_timeout, 0};
|
||||||
|
log_debug("banner-grab-tcp", "%s connected", inet_ntoa(addr));
|
||||||
|
|
||||||
|
// Send data
|
||||||
|
if (conf->send_str) {
|
||||||
|
struct evbuffer *evout = bufferevent_get_output(bev);
|
||||||
|
// HACK!!! TODO: make some messy parser that replaces ${IP} with IP etc
|
||||||
|
// and allow null characters
|
||||||
|
evbuffer_add_printf(evout, conf->send_str,
|
||||||
|
inet_ntoa(addr), inet_ntoa(addr), inet_ntoa(addr), inet_ntoa(addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change from connect timeout to read timeout
|
||||||
|
bufferevent_set_timeouts(bev, &tv, &tv);
|
||||||
|
|
||||||
|
// Update state/stats
|
||||||
|
st->state = CONNECTED;
|
||||||
|
st->conf->stats.connected_hosts++;
|
||||||
|
} else {
|
||||||
|
if (st->state == CONNECTED) {
|
||||||
|
// Print out that we just didn't receive data
|
||||||
|
printf("%s X\n", inet_ntoa(addr));
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
st->conf->stats.read_timed_out++;
|
||||||
|
} else {
|
||||||
|
st->conf->stats.conn_timed_out++;
|
||||||
|
}
|
||||||
|
log_debug("banner-grab-tcp", "%s bailing..", inet_ntoa(addr));
|
||||||
|
bufferevent_free(bev);
|
||||||
|
st->conf->stats.timed_out++;
|
||||||
|
decrement_cur_running(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab these bytes, and close the connection.
|
||||||
|
// Even if we don't need to read any bytes,
|
||||||
|
// we have to have this so that libevent thinks we have
|
||||||
|
// a read event, so that it can timeout TCP connects
|
||||||
|
// (as a read timeout)
|
||||||
|
void read_cb(struct bufferevent *bev, void *arg)
|
||||||
|
{
|
||||||
|
struct evbuffer *in = bufferevent_get_input(bev);
|
||||||
|
struct state *st = arg;
|
||||||
|
size_t len = evbuffer_get_length(in);
|
||||||
|
struct in_addr addr;
|
||||||
|
addr.s_addr = st->ip;
|
||||||
|
|
||||||
|
log_debug("banner-grab-tcp", "read_cb for %s", inet_ntoa(addr));
|
||||||
|
|
||||||
|
if (len > MAX_BANNER_LEN) {
|
||||||
|
len = MAX_BANNER_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len > 0) {
|
||||||
|
// Grab the banner
|
||||||
|
unsigned int i;
|
||||||
|
unsigned char *buf = malloc(len+1);
|
||||||
|
|
||||||
|
st->state = RECEIVED;
|
||||||
|
|
||||||
|
if (!buf) {
|
||||||
|
log_fatal("banner-grab-tcp", "cannot alloc %d byte buf", len+1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
evbuffer_remove(in, buf, len);
|
||||||
|
|
||||||
|
printf("%s ", inet_ntoa(addr));
|
||||||
|
|
||||||
|
if (st->conf->format == FORMAT_ASCII) {
|
||||||
|
// Ascii
|
||||||
|
buf[len] = '\0';
|
||||||
|
printf("%s\n", buf);
|
||||||
|
} else if (st->conf->format == FORMAT_HEX) {
|
||||||
|
// Hex output
|
||||||
|
for (i=0; i<len; i++) {
|
||||||
|
printf("%02x", buf[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
} else if (st->conf->format == FORMAT_BASE64) {
|
||||||
|
// Base64
|
||||||
|
int i=0;
|
||||||
|
char out[4] = {0,0,0,0};
|
||||||
|
while (i < len) {
|
||||||
|
uint32_t value = 0;
|
||||||
|
value += (i < len) ? buf[i++] << 16 : 0;
|
||||||
|
value += (i < len) ? buf[i++] << 8 : 0;
|
||||||
|
value += (i < len) ? buf[i++] : 0;
|
||||||
|
out[0] = BASE64_ALPHABET[(value >> 18) & 0x3F];
|
||||||
|
out[1] = BASE64_ALPHABET[(value >> 12) & 0x3F];
|
||||||
|
out[2] = BASE64_ALPHABET[(value >> 6) & 0x3F];
|
||||||
|
out[3] = BASE64_ALPHABET[(value ) & 0x3F];
|
||||||
|
if (i < len) {
|
||||||
|
printf("%c%c%c%c", out[0], out[1], out[2], out[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (len > 0) {
|
||||||
|
switch (len % 3) {
|
||||||
|
case 1:
|
||||||
|
out[2] = '=';
|
||||||
|
case 2:
|
||||||
|
out[3] = '=';
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
printf("%c%c%c%c\n", out[0], out[1], out[2], out[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
st->conf->stats.completed_hosts++;
|
||||||
|
}
|
||||||
|
bufferevent_free(bev);
|
||||||
|
decrement_cur_running(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void grab_banner(struct state *st)
|
||||||
|
{
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
struct bufferevent *bev;
|
||||||
|
struct timeval read_to = {st->conf->connect_timeout, 0};
|
||||||
|
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(st->conf->port);
|
||||||
|
addr.sin_addr.s_addr = st->ip;
|
||||||
|
log_debug("banner-grab-tcp", "connect %s:%d",
|
||||||
|
inet_ntoa(addr.sin_addr), st->conf->port);
|
||||||
|
|
||||||
|
bev = bufferevent_socket_new(st->conf->base, -1, BEV_OPT_CLOSE_ON_FREE);
|
||||||
|
|
||||||
|
bufferevent_set_timeouts(bev, &read_to, &read_to);
|
||||||
|
|
||||||
|
bufferevent_setcb(bev, read_cb, NULL, connect_cb, st);
|
||||||
|
bufferevent_enable(bev, EV_READ);
|
||||||
|
|
||||||
|
st->state = CONNECTING;
|
||||||
|
|
||||||
|
st->conf->stats.init_connected_hosts++;
|
||||||
|
|
||||||
|
if (bufferevent_socket_connect(bev,
|
||||||
|
(struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||||
|
log_warn("banner-grab-tcp", "could not connect socket %d (%d open) %d %d",
|
||||||
|
bufferevent_getfd(bev), st->conf->current_running, errno, ENFILE);
|
||||||
|
perror("connect");
|
||||||
|
|
||||||
|
|
||||||
|
bufferevent_free(bev);
|
||||||
|
decrement_cur_running(st);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stdin_eventcb(struct bufferevent *bev, short events, void *ptr) {
|
||||||
|
struct config *conf = ptr;
|
||||||
|
|
||||||
|
if (events & BEV_EVENT_EOF) {
|
||||||
|
log_debug("banner-grab-tcp",
|
||||||
|
"received EOF; quitting after buffer empties");
|
||||||
|
conf->stdin_closed = 1;
|
||||||
|
if (conf->current_running == 0) {
|
||||||
|
log_info("banner-grab-tcp", "done");
|
||||||
|
print_status(0, 0, conf);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stdin_readcb(struct bufferevent *bev, void *arg)
|
||||||
|
{
|
||||||
|
struct evbuffer *in = bufferevent_get_input(bev);
|
||||||
|
struct config *conf = arg;
|
||||||
|
|
||||||
|
log_debug("banner-grab-tcp", "stdin cb %d < %d ?",
|
||||||
|
conf->current_running, conf->max_concurrent);
|
||||||
|
|
||||||
|
while (conf->current_running < conf->max_concurrent &&
|
||||||
|
evbuffer_get_length(in) > 0) {
|
||||||
|
char *ip_str;
|
||||||
|
size_t line_len;
|
||||||
|
char *line = evbuffer_readln(in, &line_len, EVBUFFER_EOL_LF);
|
||||||
|
struct state *st;
|
||||||
|
if (!line)
|
||||||
|
break;
|
||||||
|
log_debug("banner-grab-tcp", "line: %s", line);
|
||||||
|
|
||||||
|
ip_str = line;
|
||||||
|
/*
|
||||||
|
port_str = strstr(line, ":") + 1;
|
||||||
|
if (!port_str)
|
||||||
|
port_str = strstr(line, " ") + 1;
|
||||||
|
if (!port_str)
|
||||||
|
break;
|
||||||
|
|
||||||
|
*(port_str-1) = '\0';
|
||||||
|
port = atoi(port_str);
|
||||||
|
*/
|
||||||
|
//printf("scanning %s:%d\n", ip
|
||||||
|
|
||||||
|
conf->current_running++;
|
||||||
|
st = malloc(sizeof(*st));
|
||||||
|
st->conf = conf;
|
||||||
|
st->ip = inet_addr(ip_str);
|
||||||
|
grab_banner(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct event_base *base;
|
||||||
|
struct event *status_timer;
|
||||||
|
struct timeval status_timeout = {1, 0};
|
||||||
|
int c;
|
||||||
|
struct option long_options[] = {
|
||||||
|
{"concurrent", required_argument, 0, 'c'},
|
||||||
|
{"port", required_argument, 0, 'p'},
|
||||||
|
{"conn-timeout", required_argument, 0, 't'},
|
||||||
|
{"read-timeout", required_argument, 0, 'r'},
|
||||||
|
{"verbosity", required_argument, 0, 'v'},
|
||||||
|
{"format", no_argument, 0, 'f'},
|
||||||
|
{"data", required_argument, 0, 'd'},
|
||||||
|
{0, 0, 0, 0} };
|
||||||
|
|
||||||
|
struct config conf;
|
||||||
|
int ret;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
log_init(stderr, LOG_INFO);
|
||||||
|
|
||||||
|
ret = ulimit(4, 1000000); // Allow us to open 1 million fds (instead of 1024)
|
||||||
|
if (ret < 0) {
|
||||||
|
log_fatal("banner-grab-tcp", "cannot set ulimit");
|
||||||
|
perror("ulimit");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
base = event_base_new();
|
||||||
|
conf.base = base;
|
||||||
|
|
||||||
|
// buffer stdin as an event
|
||||||
|
conf.stdin_bev = bufferevent_socket_new(base, 0, BEV_OPT_DEFER_CALLBACKS);
|
||||||
|
bufferevent_setcb(conf.stdin_bev, stdin_readcb, NULL, stdin_eventcb, &conf);
|
||||||
|
bufferevent_enable(conf.stdin_bev, EV_READ);
|
||||||
|
|
||||||
|
// Status timer
|
||||||
|
status_timer = evtimer_new(base, print_status, &conf);
|
||||||
|
evtimer_add(status_timer, &status_timeout);
|
||||||
|
|
||||||
|
// Defaults
|
||||||
|
conf.max_concurrent = 1;
|
||||||
|
conf.current_running = 0;
|
||||||
|
memset(&conf.stats, 0, sizeof(conf.stats));
|
||||||
|
conf.connect_timeout = 4;
|
||||||
|
conf.read_timeout = 4;
|
||||||
|
conf.stdin_closed = 0;
|
||||||
|
conf.format = FORMAT_BASE64;
|
||||||
|
conf.send_str = NULL;
|
||||||
|
|
||||||
|
// Parse command line args
|
||||||
|
while (1) {
|
||||||
|
int option_index = 0;
|
||||||
|
c = getopt_long(argc, argv, "c:p:t:r:v:f:d:",
|
||||||
|
long_options, &option_index);
|
||||||
|
|
||||||
|
if (c < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case 'c':
|
||||||
|
conf.max_concurrent = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
conf.port = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
conf.connect_timeout = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
conf.read_timeout = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
if (atoi(optarg) >= 0 && atoi(optarg) <= 5) {
|
||||||
|
log_init(stderr, atoi(optarg));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
if (strcmp(optarg, "hex") == 0) {
|
||||||
|
conf.format = FORMAT_HEX;
|
||||||
|
} else if (strcmp(optarg, "base64") == 0) {
|
||||||
|
conf.format = FORMAT_BASE64;
|
||||||
|
} else if (strcmp(optarg, "ascii") == 0) {
|
||||||
|
conf.format = FORMAT_ASCII;
|
||||||
|
} else {
|
||||||
|
log_fatal("banner-grab-tcp", "Unknown format '%s'; use 'hex', 'base64', or 'ascii'",
|
||||||
|
optarg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
fp = fopen(optarg, "r");
|
||||||
|
if (!fp) {
|
||||||
|
log_error("banner-grab-tcp", "Could not open send data file '%s':", optarg);
|
||||||
|
perror("fopen");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
fseek(fp, 0L, SEEK_END);
|
||||||
|
conf.send_str_size = ftell(fp);
|
||||||
|
fseek(fp, 0L, SEEK_SET);
|
||||||
|
//assert(conf.send_str_size < 10000); // jumbo frames?
|
||||||
|
conf.send_str = malloc(conf.send_str_size+1);
|
||||||
|
if (!conf.send_str) {
|
||||||
|
log_fatal("banner-grab-tcp", "Could not malloc %d bytes", conf.send_str_size+1);
|
||||||
|
}
|
||||||
|
if (fread(conf.send_str, conf.send_str_size, 1, fp) != 1) {
|
||||||
|
log_fatal("banner-grab-tcp", "Couldn't read from send data file '%s':", optarg);
|
||||||
|
}
|
||||||
|
conf.send_str[conf.send_str_size] = '\0';
|
||||||
|
fclose(fp);
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
printf("Usage:\n");
|
||||||
|
printf("\t%s [-c max_concurrency] [-t connect_timeout] [-r read_timeout] \n\t"
|
||||||
|
"[-v verbosity=0-5] [-d send_data_file] [-f ascii|hex|base64] -p port\n", argv[0]);
|
||||||
|
exit(1);
|
||||||
|
default:
|
||||||
|
log_info("banner-grab-tcp", "hmmm..");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info("banner-grab-tcp", "Using port %d with max_concurrency %d, %d s conn timeout, %d s read timeout",
|
||||||
|
conf.port, conf.max_concurrent, conf.connect_timeout, conf.read_timeout);
|
||||||
|
|
||||||
|
event_base_dispatch(base);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
3
examples/banner-grab/http-req
Normal file
3
examples/banner-grab/http-req
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
GET / HTTP/1.1
|
||||||
|
Host: %s
|
||||||
|
|
22
examples/forge-socket/Makefile
Normal file
22
examples/forge-socket/Makefile
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
|
||||||
|
CFLAGS+=-I../../lib/ -I../../forge_socket -Wall
|
||||||
|
LDFLAGS+=-lpcap -levent -levent_extra -lm
|
||||||
|
VPATH=../../lib/
|
||||||
|
|
||||||
|
# from dpkg-buildflags --get CFLAGS, but use stack-protector-all and fPIC
|
||||||
|
GCCHARDENING=-g -O2 -fstack-protector-all --param=ssp-buffer-size=4 -Wformat -Wformat-security -Werror=format-security -fPIC
|
||||||
|
# from gpkg-buildflags --get LDFLAGS, + -z,now
|
||||||
|
LDHARDENING=-Wl,-Bsymbolic-functions -Wl,-z,relro,-z,now
|
||||||
|
|
||||||
|
CFLAGS+=$(GCCHARDENING)
|
||||||
|
LDFLAGS+=$(LDHARDENING)
|
||||||
|
|
||||||
|
|
||||||
|
all: forge-socket
|
||||||
|
|
||||||
|
forge-socket: forge-socket.o logger.o
|
||||||
|
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f forge-socket *.o
|
73
examples/forge-socket/README
Normal file
73
examples/forge-socket/README
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
Forge-socket banner grab
|
||||||
|
======
|
||||||
|
|
||||||
|
This utility, in combination with a kernel module
|
||||||
|
(https://github.com/ewust/forge_socket/) will complete the half-open connection
|
||||||
|
created by ZMap during a TCP-scan, optionally send a small message, and wait
|
||||||
|
for the hosts response. The response is then printed along with their IP
|
||||||
|
address on stdout. Periodic status messages appear on stderr.
|
||||||
|
|
||||||
|
This utility is functionally equivalent to banner-grab-tcp, however, instead of
|
||||||
|
having the kernel send a RST packet for the server's SYN+ACK, and
|
||||||
|
banner-grab-tcp attempting to start a fresh TCP connection with the host,
|
||||||
|
forge-socket will take the parameters of the SYN+ACK packet, and use a kernel
|
||||||
|
module to add it as an ESTABLISHED TCP connection socket. Then, the
|
||||||
|
forge-socket user-space program can use this socket to send() and recv() as
|
||||||
|
normal, and completes the banner-grab process (optionally send a small message,
|
||||||
|
and receive the server's response).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
USING:
|
||||||
|
-----
|
||||||
|
# Install forge-socket to the ZMap root directory:
|
||||||
|
cd ./zmap/
|
||||||
|
git clone git@github.com:ewust/forge_socket.git
|
||||||
|
cd forge_socket
|
||||||
|
make
|
||||||
|
sudo insmod forge_socket.ko
|
||||||
|
|
||||||
|
# Don't send RST packets (forge-socket will complete these connections instead)
|
||||||
|
sudo iptables -A OUTPUT -p tcp -m tcp --tcp-flags RST,RST RST,RST -j DROP
|
||||||
|
|
||||||
|
# Use ZMap + forge-socket simultaneously:
|
||||||
|
make
|
||||||
|
#echo -e -n "GET / HTTP/1.1\r\nHost: %s\r\n\r\n" > http-req
|
||||||
|
sudo su
|
||||||
|
ulimit -SHn 1000000 && ulimit -SSn 1000000
|
||||||
|
zmap -p 80 -B 50M -N 1000 -O extended_file -o - | ./forge-socket -c 8000 -d http-req > http-banners.out
|
||||||
|
|
||||||
|
|
||||||
|
The options are similar to banner-grab-tcp, except there is no connection timeout :)
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
-----
|
||||||
|
-c, --concurent Number of connections that can be going on at once.
|
||||||
|
This, combined with timeouts, will decide the maximum
|
||||||
|
rate at which banners are grabbed. If this value
|
||||||
|
is set higher than 1000, you should use
|
||||||
|
`ulimit -SSn 1000000` and `ulimit -SHn 1000000` to
|
||||||
|
avoid running out of file descriptors (typically capped
|
||||||
|
at 1024).
|
||||||
|
|
||||||
|
-r, --read-timeout Read timeout (seconds). Give up on a host if after
|
||||||
|
connecting (and optionally sending data), it does
|
||||||
|
not send any response by this time. Default: 4 seconds.
|
||||||
|
|
||||||
|
-v, --verbosity Set status verbosity. Status/error messages are outputed
|
||||||
|
on stderr. This value can be 0-5, with 5 being the most
|
||||||
|
verbose (LOG_TRACE). Default: 3 (LOG_INFO)
|
||||||
|
|
||||||
|
-f, --format Format to output banner responses. One of 'hex', 'ascii',
|
||||||
|
or 'base64'.
|
||||||
|
'hex' outputs ascii hex characters, e.g. 48656c6c6f.
|
||||||
|
'ascii' outputs ascii, without separators, e.g. Hello
|
||||||
|
'base64' outputs base64 encoding, e.g. SGVsbG8=
|
||||||
|
Default is base64.
|
||||||
|
|
||||||
|
-d, --data Optional data file. This data will be sent to each host
|
||||||
|
upon successful connection. Currently, this file does
|
||||||
|
not allow null characters, but supports up to 4
|
||||||
|
occurances of the current host's IP address, by replacing
|
||||||
|
%s with the string (inet_ntoa) of that host's IP address.
|
||||||
|
|
496
examples/forge-socket/forge-socket.c
Normal file
496
examples/forge-socket/forge-socket.c
Normal file
@ -0,0 +1,496 @@
|
|||||||
|
/*
|
||||||
|
* Forge Socket Banner Grab Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
#include <event.h>
|
||||||
|
#include <event2/bufferevent_ssl.h>
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ulimit.h>
|
||||||
|
|
||||||
|
#include "forge_socket.h"
|
||||||
|
|
||||||
|
#define MAX_BANNER_LEN 1024
|
||||||
|
#define BASE64_ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||||
|
|
||||||
|
struct config {
|
||||||
|
int read_timeout; // how long to wait once connected for the banner (seconds)
|
||||||
|
int current_running;
|
||||||
|
int max_concurrent;
|
||||||
|
struct event_base *base;
|
||||||
|
struct bufferevent *stdin_bev;
|
||||||
|
int stdin_closed;
|
||||||
|
enum {FORMAT_HEX, FORMAT_BASE64, FORMAT_ASCII} format;
|
||||||
|
char *send_str;
|
||||||
|
long send_str_size;
|
||||||
|
|
||||||
|
struct stats_st {
|
||||||
|
int init_connected_hosts; // Number of hosts we have even tried to connect to
|
||||||
|
int connected_hosts; // # hosts that picked up
|
||||||
|
int conn_timed_out; // # hosts that timed out during connection
|
||||||
|
int read_timed_out; // # hosts that connected, but sent no data (banner)
|
||||||
|
int timed_out; // # hosts that timed out at all (conn_timed_out+read_timed_out)?
|
||||||
|
int completed_hosts; // # hosts that presented a banner
|
||||||
|
} stats;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct state {
|
||||||
|
struct config *conf;
|
||||||
|
uint32_t src_ip;
|
||||||
|
uint32_t dst_ip;
|
||||||
|
uint16_t sport;
|
||||||
|
uint16_t dport;
|
||||||
|
uint32_t seq;
|
||||||
|
uint32_t seq_ack;
|
||||||
|
enum {CONNECTING, CONNECTED, RECEIVED} state;
|
||||||
|
};
|
||||||
|
|
||||||
|
void stdin_readcb(struct bufferevent *bev, void *arg);
|
||||||
|
|
||||||
|
void print_status(evutil_socket_t fd, short events, void *arg)
|
||||||
|
{
|
||||||
|
struct event *ev;
|
||||||
|
struct config *conf = arg;
|
||||||
|
struct event_base *base = conf->base;
|
||||||
|
struct timeval status_timeout = {1, 0};
|
||||||
|
ev = evtimer_new(base, print_status, conf);
|
||||||
|
evtimer_add(ev, &status_timeout);
|
||||||
|
(void)fd; (void)events;
|
||||||
|
|
||||||
|
log_info("forge-socket", "(%d/%d in use) - Totals: %d inited, %d connected, %d conn timeout, %d read timeout %d completed",
|
||||||
|
conf->current_running, conf->max_concurrent,
|
||||||
|
conf->stats.init_connected_hosts,
|
||||||
|
conf->stats.connected_hosts, conf->stats.conn_timed_out,
|
||||||
|
conf->stats.read_timed_out, conf->stats.completed_hosts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void decrement_cur_running(struct state *st)
|
||||||
|
{
|
||||||
|
struct config *conf = st->conf;
|
||||||
|
conf->current_running--;
|
||||||
|
log_debug("forge-socket", "done, down to %d",
|
||||||
|
conf->current_running);
|
||||||
|
if (evbuffer_get_length(bufferevent_get_input(conf->stdin_bev)) > 0) {
|
||||||
|
stdin_readcb(conf->stdin_bev, conf);
|
||||||
|
}
|
||||||
|
free(st);
|
||||||
|
|
||||||
|
if (conf->stdin_closed && conf->current_running == 0) {
|
||||||
|
// Done
|
||||||
|
log_info("forge-socket", "done");
|
||||||
|
print_status(0, 0, conf);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void event_cb(struct bufferevent *bev, short events, void *arg)
|
||||||
|
{
|
||||||
|
struct state *st = arg;
|
||||||
|
struct config *conf = st->conf;
|
||||||
|
struct in_addr addr;
|
||||||
|
addr.s_addr = st->src_ip;
|
||||||
|
if (events & BEV_EVENT_CONNECTED) {
|
||||||
|
log_error("forge-socket", "%s connected - wat?", inet_ntoa(addr));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (st->state == CONNECTED) {
|
||||||
|
// Print out that we just didn't receive data
|
||||||
|
printf("%s X\n", inet_ntoa(addr));
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
conf->stats.read_timed_out++;
|
||||||
|
} else {
|
||||||
|
conf->stats.conn_timed_out++;
|
||||||
|
}
|
||||||
|
log_debug("forge-socket", "%s bailing..", inet_ntoa(addr));
|
||||||
|
bufferevent_free(bev);
|
||||||
|
conf->stats.timed_out++;
|
||||||
|
decrement_cur_running(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab these bytes, and close the connection.
|
||||||
|
// Even if we don't need to read any bytes,
|
||||||
|
// we have to have this so that libevent thinks we have
|
||||||
|
// a read event, so that it can timeout TCP connects
|
||||||
|
// (as a read timeout)
|
||||||
|
void read_cb(struct bufferevent *bev, void *arg)
|
||||||
|
{
|
||||||
|
struct evbuffer *in = bufferevent_get_input(bev);
|
||||||
|
struct state *st = arg;
|
||||||
|
size_t len = evbuffer_get_length(in);
|
||||||
|
struct in_addr addr;
|
||||||
|
addr.s_addr = st->src_ip;
|
||||||
|
|
||||||
|
log_debug("forge-socket", "read_cb for %s", inet_ntoa(addr));
|
||||||
|
|
||||||
|
if (len > MAX_BANNER_LEN) {
|
||||||
|
len = MAX_BANNER_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len > 0) {
|
||||||
|
// Grab the banner
|
||||||
|
unsigned int i;
|
||||||
|
unsigned char *buf = malloc(len+1);
|
||||||
|
|
||||||
|
st->state = RECEIVED;
|
||||||
|
|
||||||
|
if (!buf) {
|
||||||
|
log_fatal("forge-socket", "cannot alloc %d byte buf", len+1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
evbuffer_remove(in, buf, len);
|
||||||
|
|
||||||
|
printf("%s ", inet_ntoa(addr));
|
||||||
|
|
||||||
|
if (st->conf->format == FORMAT_ASCII) {
|
||||||
|
// Ascii
|
||||||
|
buf[len] = '\0';
|
||||||
|
printf("%s\n", buf);
|
||||||
|
} else if (st->conf->format == FORMAT_HEX) {
|
||||||
|
// Hex output
|
||||||
|
for (i=0; i<len; i++) {
|
||||||
|
printf("%02x", buf[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
} else if (st->conf->format == FORMAT_BASE64) {
|
||||||
|
// Base64
|
||||||
|
int i=0;
|
||||||
|
char out[4] = {0,0,0,0};
|
||||||
|
while (i < len) {
|
||||||
|
uint32_t value = 0;
|
||||||
|
value += (i < len) ? buf[i++] << 16 : 0;
|
||||||
|
value += (i < len) ? buf[i++] << 8 : 0;
|
||||||
|
value += (i < len) ? buf[i++] : 0;
|
||||||
|
out[0] = BASE64_ALPHABET[(value >> 18) & 0x3F];
|
||||||
|
out[1] = BASE64_ALPHABET[(value >> 12) & 0x3F];
|
||||||
|
out[2] = BASE64_ALPHABET[(value >> 6) & 0x3F];
|
||||||
|
out[3] = BASE64_ALPHABET[(value ) & 0x3F];
|
||||||
|
if (i < len) {
|
||||||
|
printf("%c%c%c%c", out[0], out[1], out[2], out[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (len > 0) {
|
||||||
|
switch (len % 3) {
|
||||||
|
case 1:
|
||||||
|
out[2] = '=';
|
||||||
|
case 2:
|
||||||
|
out[3] = '=';
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
printf("%c%c%c%c\n", out[0], out[1], out[2], out[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
st->conf->stats.completed_hosts++;
|
||||||
|
}
|
||||||
|
bufferevent_free(bev);
|
||||||
|
decrement_cur_running(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_sock_state(int sock, struct tcp_state *st)
|
||||||
|
{
|
||||||
|
struct sockaddr_in sin;
|
||||||
|
sin.sin_family = AF_INET;
|
||||||
|
sin.sin_addr.s_addr = st->src_ip;
|
||||||
|
sin.sin_port = st->sport;
|
||||||
|
|
||||||
|
int value = 1;
|
||||||
|
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) < 0) {
|
||||||
|
perror("setsockopt SO_REUSEADDR");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setsockopt(sock, SOL_IP, IP_TRANSPARENT, &value, sizeof(value)) < 0) {
|
||||||
|
perror("setsockopt IP_TRANSPARENT");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
|
||||||
|
perror("bind");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setsockopt(sock, IPPROTO_TCP, TCP_STATE, st, sizeof(struct tcp_state)) < 0) {
|
||||||
|
perror("setsockopt TCP_STATE");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void grab_banner(struct state *st)
|
||||||
|
{
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
struct bufferevent *bev;
|
||||||
|
struct timeval read_to = {st->conf->read_timeout, 0};
|
||||||
|
struct tcp_state tcp_st;
|
||||||
|
int sock = socket(AF_INET, SOCK_FORGE, 0);
|
||||||
|
|
||||||
|
addr.sin_addr.s_addr = st->src_ip;
|
||||||
|
|
||||||
|
if (sock < 0) {
|
||||||
|
perror("SOCK_FORGE socket");
|
||||||
|
log_fatal("forge_socket", "(did you insmod forge_socket.ko?)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&tcp_st, 0, sizeof(tcp_st));
|
||||||
|
|
||||||
|
// These need to be in network order for forge socket"
|
||||||
|
tcp_st.src_ip = st->dst_ip;
|
||||||
|
tcp_st.dst_ip = st->src_ip;
|
||||||
|
tcp_st.sport = htons(st->dport);
|
||||||
|
tcp_st.dport = htons(st->sport);
|
||||||
|
|
||||||
|
// This should be in ???
|
||||||
|
tcp_st.seq = st->seq_ack;
|
||||||
|
tcp_st.ack = (st->seq + 1);
|
||||||
|
|
||||||
|
tcp_st.snd_wnd = 0x1000;
|
||||||
|
tcp_st.rcv_wnd = 0x1000;
|
||||||
|
|
||||||
|
tcp_st.snd_una = tcp_st.seq;
|
||||||
|
st->state = CONNECTING;
|
||||||
|
st->conf->stats.init_connected_hosts++;
|
||||||
|
|
||||||
|
// consider this a non-blocking, but completed "connect()". heh.
|
||||||
|
if (set_sock_state(sock, &tcp_st) != 0) {
|
||||||
|
log_error("forge_socket", "set_sock_state failed\n");
|
||||||
|
decrement_cur_running(st);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
evutil_make_socket_nonblocking(sock);
|
||||||
|
|
||||||
|
bev = bufferevent_socket_new(st->conf->base, sock, BEV_OPT_CLOSE_ON_FREE);
|
||||||
|
|
||||||
|
bufferevent_set_timeouts(bev, &read_to, &read_to);
|
||||||
|
|
||||||
|
bufferevent_setcb(bev, read_cb, NULL, event_cb, st);
|
||||||
|
bufferevent_enable(bev, EV_READ);
|
||||||
|
|
||||||
|
|
||||||
|
// Send data
|
||||||
|
if (st->conf->send_str) {
|
||||||
|
struct evbuffer *evout = bufferevent_get_output(bev);
|
||||||
|
// HACK!!! TODO: make some messy parser that replaces ${IP} with IP etc
|
||||||
|
// and allow null characters
|
||||||
|
evbuffer_add_printf(evout, st->conf->send_str,
|
||||||
|
inet_ntoa(addr.sin_addr), inet_ntoa(addr.sin_addr),
|
||||||
|
inet_ntoa(addr.sin_addr), inet_ntoa(addr.sin_addr));
|
||||||
|
log_trace("forge-socket", "sent str to %s", inet_ntoa(addr.sin_addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update state/stats
|
||||||
|
st->state = CONNECTED;
|
||||||
|
st->conf->stats.connected_hosts++;
|
||||||
|
|
||||||
|
log_trace("forge-socket", "go %s go! read a byte!!", inet_ntoa(addr.sin_addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
void stdin_eventcb(struct bufferevent *bev, short events, void *ptr) {
|
||||||
|
struct config *conf = ptr;
|
||||||
|
|
||||||
|
if (events & BEV_EVENT_EOF) {
|
||||||
|
log_debug("forge-socket",
|
||||||
|
"received EOF; quitting after buffer empties");
|
||||||
|
conf->stdin_closed = 1;
|
||||||
|
if (conf->current_running == 0) {
|
||||||
|
log_info("forge-socket", "done");
|
||||||
|
print_status(0, 0, conf);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stdin_readcb(struct bufferevent *bev, void *arg)
|
||||||
|
{
|
||||||
|
struct evbuffer *in = bufferevent_get_input(bev);
|
||||||
|
struct config *conf = arg;
|
||||||
|
|
||||||
|
log_debug("forge-socket", "stdin cb %d < %d ?",
|
||||||
|
conf->current_running, conf->max_concurrent);
|
||||||
|
|
||||||
|
while (conf->current_running < conf->max_concurrent &&
|
||||||
|
evbuffer_get_length(in) > 0) {
|
||||||
|
size_t line_len;
|
||||||
|
char *line = evbuffer_readln(in, &line_len, EVBUFFER_EOL_LF);
|
||||||
|
struct state *st;
|
||||||
|
if (!line)
|
||||||
|
break;
|
||||||
|
log_debug("forge-socket", "line: '%s'", line);
|
||||||
|
|
||||||
|
//synack, 77.176.116.205, 141.212.121.125, 443, 49588, 3628826326, 3441755636, 0, 0,2013-08-11 19:16:05.799
|
||||||
|
char synack[12];
|
||||||
|
char srcip[INET_ADDRSTRLEN], dstip[INET_ADDRSTRLEN];
|
||||||
|
uint32_t seq, seq_ack;
|
||||||
|
uint16_t sport, dport;
|
||||||
|
int cooldown, repeat=1;
|
||||||
|
|
||||||
|
|
||||||
|
int ret = sscanf(line, "%11[^,], %15[^,], %15[^,], %hu, %hu, %u, %u, %d, %d,%*s",
|
||||||
|
synack, srcip, dstip, &sport, &dport, &seq, &seq_ack, &cooldown, &repeat);
|
||||||
|
|
||||||
|
log_trace("forge-socket", "%d '%s' sip: '%s', dip: '%s', sport: %d, dport: %d, seq: %d, seq_ack: %d",
|
||||||
|
ret, synack, srcip, dstip, sport, dport, seq, seq_ack);
|
||||||
|
|
||||||
|
if (ret==9 && !repeat && strcmp(synack, "synack") == 0) {
|
||||||
|
st = malloc(sizeof(*st));
|
||||||
|
st->conf = conf;
|
||||||
|
st->src_ip = inet_addr(srcip);
|
||||||
|
st->dst_ip = inet_addr(dstip);
|
||||||
|
st->sport = sport;
|
||||||
|
st->dport = dport;
|
||||||
|
st->seq = seq;
|
||||||
|
st->seq_ack = seq_ack;
|
||||||
|
|
||||||
|
conf->current_running++;
|
||||||
|
grab_banner(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct event_base *base;
|
||||||
|
struct event *status_timer;
|
||||||
|
struct timeval status_timeout = {1, 0};
|
||||||
|
int c;
|
||||||
|
struct option long_options[] = {
|
||||||
|
{"concurrent", required_argument, 0, 'c'},
|
||||||
|
{"read-timeout", required_argument, 0, 'r'},
|
||||||
|
{"verbosity", required_argument, 0, 'v'},
|
||||||
|
{"format", no_argument, 0, 'f'},
|
||||||
|
{"data", required_argument, 0, 'd'},
|
||||||
|
{0, 0, 0, 0} };
|
||||||
|
|
||||||
|
struct config conf;
|
||||||
|
int ret;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
log_init(stderr, LOG_INFO);
|
||||||
|
|
||||||
|
ret = ulimit(4, 1000000); // Allow us to open 1 million fds (instead of 1024)
|
||||||
|
if (ret < 0) {
|
||||||
|
log_fatal("forge-socket", "cannot set ulimit");
|
||||||
|
perror("ulimit");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
base = event_base_new();
|
||||||
|
conf.base = base;
|
||||||
|
|
||||||
|
// buffer stdin as an event
|
||||||
|
conf.stdin_bev = bufferevent_socket_new(base, 0, BEV_OPT_DEFER_CALLBACKS);
|
||||||
|
bufferevent_setcb(conf.stdin_bev, stdin_readcb, NULL, stdin_eventcb, &conf);
|
||||||
|
bufferevent_enable(conf.stdin_bev, EV_READ);
|
||||||
|
|
||||||
|
// Status timer
|
||||||
|
status_timer = evtimer_new(base, print_status, &conf);
|
||||||
|
evtimer_add(status_timer, &status_timeout);
|
||||||
|
|
||||||
|
// Defaults
|
||||||
|
conf.max_concurrent = 1;
|
||||||
|
conf.current_running = 0;
|
||||||
|
memset(&conf.stats, 0, sizeof(conf.stats));
|
||||||
|
conf.read_timeout = 4;
|
||||||
|
conf.stdin_closed = 0;
|
||||||
|
conf.format = FORMAT_BASE64;
|
||||||
|
conf.send_str = NULL;
|
||||||
|
|
||||||
|
// Parse command line args
|
||||||
|
while (1) {
|
||||||
|
int option_index = 0;
|
||||||
|
c = getopt_long(argc, argv, "c:t:r:v:f:d:",
|
||||||
|
long_options, &option_index);
|
||||||
|
|
||||||
|
if (c < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case 'c':
|
||||||
|
conf.max_concurrent = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
conf.read_timeout = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
if (atoi(optarg) >= 0 && atoi(optarg) <= 5) {
|
||||||
|
log_init(stderr, atoi(optarg));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
if (strcmp(optarg, "hex") == 0) {
|
||||||
|
conf.format = FORMAT_HEX;
|
||||||
|
} else if (strcmp(optarg, "base64") == 0) {
|
||||||
|
conf.format = FORMAT_BASE64;
|
||||||
|
} else if (strcmp(optarg, "ascii") == 0) {
|
||||||
|
conf.format = FORMAT_ASCII;
|
||||||
|
} else {
|
||||||
|
log_fatal("forge-socket", "Unknown format '%s'; use 'hex', 'base64', or 'ascii'",
|
||||||
|
optarg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
fp = fopen(optarg, "r");
|
||||||
|
if (!fp) {
|
||||||
|
log_error("forge-socket", "Could not open send data file '%s':", optarg);
|
||||||
|
perror("fopen");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
fseek(fp, 0L, SEEK_END);
|
||||||
|
conf.send_str_size = ftell(fp);
|
||||||
|
fseek(fp, 0L, SEEK_SET);
|
||||||
|
//assert(conf.send_str_size < 10000); // jumbo frames?
|
||||||
|
conf.send_str = malloc(conf.send_str_size+1);
|
||||||
|
if (!conf.send_str) {
|
||||||
|
log_fatal("forge-socket", "Could not malloc %d bytes", conf.send_str_size+1);
|
||||||
|
}
|
||||||
|
if (fread(conf.send_str, conf.send_str_size, 1, fp) != 1) {
|
||||||
|
log_fatal("forge-socket", "Couldn't read from send data file '%s':", optarg);
|
||||||
|
}
|
||||||
|
conf.send_str[conf.send_str_size] = '\0';
|
||||||
|
fclose(fp);
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
printf("Usage:\n");
|
||||||
|
printf("\t%s [-c max_concurrency] [-r read_timeout] \n\t"
|
||||||
|
"[-v verbosity=0-5] [-d send_data_file] [-f ascii|hex|base64]\n", argv[0]);
|
||||||
|
exit(1);
|
||||||
|
default:
|
||||||
|
log_info("forge-socket", "hmmm..");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info("forge-socket", "Using max_concurrency %d, %d s read timeout",
|
||||||
|
conf.max_concurrent, conf.read_timeout);
|
||||||
|
|
||||||
|
event_base_dispatch(base);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
129
lib/blacklist.c
Normal file
129
lib/blacklist.c
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Blacklist Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "blacklist.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include "constraint.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
#define ADDR_DISALLOWED 0
|
||||||
|
#define ADDR_ALLOWED 1
|
||||||
|
|
||||||
|
static constraint_t *constraint = NULL;
|
||||||
|
|
||||||
|
// check whether a single IP address is allowed to be scanned.
|
||||||
|
// 1 => is allowed
|
||||||
|
// 0 => is not allowed
|
||||||
|
int blacklist_is_allowed(uint32_t s_addr) {
|
||||||
|
return constraint_lookup_ip(constraint, ntohl(s_addr)) == ADDR_ALLOWED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// blacklist a CIDR network allocation
|
||||||
|
// e.g. blacklist_add("128.255.134.0", 24)
|
||||||
|
void blacklist_prefix(char *ip, int prefix_len)
|
||||||
|
{
|
||||||
|
assert(constraint);
|
||||||
|
constraint_set(constraint, ntohl(inet_addr(ip)), prefix_len, ADDR_DISALLOWED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// whitelist a CIDR network allocation
|
||||||
|
void whitelist_prefix(char *ip, int prefix_len)
|
||||||
|
{
|
||||||
|
assert(constraint);
|
||||||
|
constraint_set(constraint, ntohl(inet_addr(ip)), prefix_len, ADDR_ALLOWED);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init(char *file, const char *name, int value)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char line[1000];
|
||||||
|
|
||||||
|
fp = fopen(file, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
log_fatal(name, "Unable to open %s file: %s: %s", name, file, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||||
|
char *comment = strchr(line, '#');
|
||||||
|
if (comment) {
|
||||||
|
*comment = '\0';
|
||||||
|
}
|
||||||
|
char ip[33];
|
||||||
|
if ((sscanf(line, "%32s", ip)) == EOF) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int prefix_len = 32;
|
||||||
|
char *slash = strchr(ip, '/');
|
||||||
|
if (slash) { // split apart network and prefix length
|
||||||
|
*slash = '\0';
|
||||||
|
char *end;
|
||||||
|
char *len = slash+1;
|
||||||
|
errno = 0;
|
||||||
|
prefix_len = strtol(len, &end, 10);
|
||||||
|
if (end == len || errno != 0 || prefix_len < 0 || prefix_len > 32) {
|
||||||
|
log_fatal(name, "Unable to parse %s file: %s ('%s' is not a valid prefix length)", name, file, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct in_addr addr;
|
||||||
|
if (inet_aton(ip, &addr) == 0) {
|
||||||
|
log_fatal(name, "Unable to parse %s file: %s ('%s' is not a valid IP address)", name, file, ip);
|
||||||
|
}
|
||||||
|
constraint_set(constraint, ntohl(addr.s_addr), prefix_len, value);
|
||||||
|
log_trace(name, "%sing %s/%i",
|
||||||
|
name, ip, prefix_len);
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t blacklist_count_allowed()
|
||||||
|
{
|
||||||
|
assert(constraint);
|
||||||
|
return constraint_count_ips(constraint, ADDR_ALLOWED);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t blacklist_count_not_allowed()
|
||||||
|
{
|
||||||
|
assert(constraint);
|
||||||
|
return constraint_count_ips(constraint, ADDR_DISALLOWED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Initialize address constraints from whitelist and blacklist files.
|
||||||
|
// Either can be set to NULL to omit.
|
||||||
|
int blacklist_init_from_files(char *whitelist_filename, char *blacklist_filename)
|
||||||
|
{
|
||||||
|
assert(!constraint);
|
||||||
|
if (whitelist_filename) {
|
||||||
|
// using a whitelist, so default to allowing nothing
|
||||||
|
constraint = constraint_init(ADDR_DISALLOWED);
|
||||||
|
log_trace("whitelist", "blacklisting 0.0.0.0/0");
|
||||||
|
init(whitelist_filename, "whitelist", ADDR_ALLOWED);
|
||||||
|
} else {
|
||||||
|
// no whitelist, so default to allowing everything
|
||||||
|
constraint = constraint_init(ADDR_ALLOWED);
|
||||||
|
}
|
||||||
|
if (blacklist_filename) {
|
||||||
|
init(blacklist_filename, "blacklist", ADDR_DISALLOWED);
|
||||||
|
}
|
||||||
|
constraint_optimize(constraint);
|
||||||
|
uint64_t allowed = blacklist_count_allowed();
|
||||||
|
log_debug("blacklist", "%lu addresses allowed to be scanned (%0.0f%% of address space)",
|
||||||
|
allowed, allowed*100./(1L << 32));
|
||||||
|
return 0;
|
||||||
|
}
|
13
lib/blacklist.h
Normal file
13
lib/blacklist.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifndef _BLACKLIST_H
|
||||||
|
#define _BLACKLIST_H
|
||||||
|
|
||||||
|
int blacklist_is_allowed(uint32_t s_addr);
|
||||||
|
void blacklist_prefix(char *ip, int prefix_len);
|
||||||
|
void whitelist_prefix(char *ip, int prefix_len);
|
||||||
|
int blacklist_init_from_files(char *whitelist, char*blacklist);
|
||||||
|
uint64_t blacklist_count_allowed();
|
||||||
|
uint64_t blacklist_count_not_allowed();
|
||||||
|
|
||||||
|
#endif
|
384
lib/constraint.c
Normal file
384
lib/constraint.c
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "../lib/constraint.h"
|
||||||
|
#include "../lib/logger.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Efficient address-space constraints (AH 7/2013)
|
||||||
|
//
|
||||||
|
// This module uses a tree-based representation to efficiently
|
||||||
|
// manipulate and query constraints on the address space to be
|
||||||
|
// scanned. It provides a value for every IP address, and these
|
||||||
|
// values are applied by setting them for network prefixes. Order
|
||||||
|
// matters: setting a value replaces any existing value for that
|
||||||
|
// prefix or subsets of it. We use this to implement network
|
||||||
|
// whitelisting and blacklisting.
|
||||||
|
//
|
||||||
|
// Think of setting values in this structure like painting
|
||||||
|
// subnets with different colors. We can paint subnets black to
|
||||||
|
// exclude them and white to allow them. Only the top color shows.
|
||||||
|
// This makes for potentially very powerful constraint specifications.
|
||||||
|
//
|
||||||
|
// Internally, this is implemented using a binary tree, where each
|
||||||
|
// node corresponds to a network prefix. (E.g., the root is
|
||||||
|
// 0.0.0.0/0, and its children, if present, are 0.0.0.0/1 and
|
||||||
|
// 128.0.0.0/1.) Each leaf of the tree stores the value that applies
|
||||||
|
// to every address within the leaf's portion of the prefix space.
|
||||||
|
//
|
||||||
|
// As an optimization, after all values are set, we look up the
|
||||||
|
// value or subtree for every /16 prefix and cache them as an array.
|
||||||
|
// This lets subsequent lookups bypass the bottom half of the tree.
|
||||||
|
//
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constraint Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct node {
|
||||||
|
struct node *l;
|
||||||
|
struct node *r;
|
||||||
|
value_t value;
|
||||||
|
} node_t;
|
||||||
|
|
||||||
|
// As an optimization, we precompute lookups for every prefix of this
|
||||||
|
// length:
|
||||||
|
#define RADIX_LENGTH 16
|
||||||
|
|
||||||
|
struct _constraint {
|
||||||
|
node_t *root; // root node of the tree
|
||||||
|
node_t **radix; // array of nodes for every RADIX_LENGTH prefix
|
||||||
|
int optimized; // is radix populated and up-to-date?
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tree operations respect the invariant that every node that isn't a
|
||||||
|
// leaf has exactly two children.
|
||||||
|
#define IS_LEAF(node) ((node)->l == NULL)
|
||||||
|
|
||||||
|
// Allocate a new leaf with the given value
|
||||||
|
static node_t* _create_leaf(value_t value)
|
||||||
|
{
|
||||||
|
node_t *node = malloc(sizeof(node_t));
|
||||||
|
assert(node);
|
||||||
|
node->l = NULL;
|
||||||
|
node->r = NULL;
|
||||||
|
node->value = value;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the subtree rooted at node.
|
||||||
|
static void _destroy_subtree(node_t *node)
|
||||||
|
{
|
||||||
|
if (node == NULL)
|
||||||
|
return;
|
||||||
|
_destroy_subtree(node->l);
|
||||||
|
_destroy_subtree(node->r);
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from an internal node to a leaf.
|
||||||
|
static void _convert_to_leaf(node_t *node)
|
||||||
|
{
|
||||||
|
assert(node);
|
||||||
|
assert(!IS_LEAF(node));
|
||||||
|
_destroy_subtree(node->l);
|
||||||
|
_destroy_subtree(node->r);
|
||||||
|
node->l = NULL;
|
||||||
|
node->r = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursive function to set value for a given network prefix within
|
||||||
|
// the tree. (Note: prefix must be in host byte order.)
|
||||||
|
static void _set_recurse(node_t *node, uint32_t prefix, int len, value_t value)
|
||||||
|
{
|
||||||
|
assert(node);
|
||||||
|
assert(0 <= len && len <= 32);
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
// We're at the end of the prefix; make this a leaf and set the value.
|
||||||
|
if (!IS_LEAF(node)) {
|
||||||
|
_convert_to_leaf(node);
|
||||||
|
}
|
||||||
|
node->value = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_LEAF(node)) {
|
||||||
|
// We're not at the end of the prefix, but we hit a leaf.
|
||||||
|
if (node->value == value) {
|
||||||
|
// A larger prefix has the same value, so we're done.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// The larger prefix has a different value, so we need to convert it
|
||||||
|
// into an internal node and continue processing on one of the leaves.
|
||||||
|
node->l = _create_leaf(node->value);
|
||||||
|
node->r = _create_leaf(node->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're not at the end of the prefix, and we're at an internal
|
||||||
|
// node. Recurse on the left or right subtree.
|
||||||
|
if (prefix & 0x80000000) {
|
||||||
|
_set_recurse(node->r, prefix << 1, len - 1, value);
|
||||||
|
} else {
|
||||||
|
_set_recurse(node->l, prefix << 1, len - 1, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, we're an internal node, and the value is set
|
||||||
|
// by one of our children or its descendent. If both children are
|
||||||
|
// leaves with the same value, we can discard them and become a left.
|
||||||
|
if (IS_LEAF(node->r) && IS_LEAF(node->l) && node->r->value == node->l->value) {
|
||||||
|
node->value = node->l->value;
|
||||||
|
_convert_to_leaf(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the value for a given network prefix, overwriting any existing
|
||||||
|
// values on that prefix or subsets of it.
|
||||||
|
// (Note: prefix must be in host byte order.)
|
||||||
|
void constraint_set(constraint_t *con, uint32_t prefix, int len, value_t value)
|
||||||
|
{
|
||||||
|
assert(con);
|
||||||
|
_set_recurse(con->root, prefix, len, value);
|
||||||
|
con->optimized = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the value pertaining to an address, according to the tree
|
||||||
|
// starting at given root. (Note: address must be in host byte order.)
|
||||||
|
static int _lookup_ip(node_t *root, uint32_t address)
|
||||||
|
{
|
||||||
|
assert(root);
|
||||||
|
node_t *node = root;
|
||||||
|
uint32_t mask = 0x80000000;
|
||||||
|
for (;;) {
|
||||||
|
if (IS_LEAF(node)) {
|
||||||
|
return node->value;
|
||||||
|
}
|
||||||
|
if (address & mask) {
|
||||||
|
node = node->r;
|
||||||
|
} else {
|
||||||
|
node = node->l;
|
||||||
|
}
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Return the value pertaining to an address.
|
||||||
|
// (Note: address must be in host byte order.)
|
||||||
|
int constraint_lookup_ip(constraint_t *con, uint32_t address)
|
||||||
|
{
|
||||||
|
assert(con);
|
||||||
|
if (con->optimized) {
|
||||||
|
// Use radix optimization
|
||||||
|
node_t *node = con->radix[address >> (32 - RADIX_LENGTH)];
|
||||||
|
if (IS_LEAF(node)) {
|
||||||
|
return node->value;
|
||||||
|
}
|
||||||
|
return _lookup_ip(node, address << RADIX_LENGTH);
|
||||||
|
} else {
|
||||||
|
// Do a full lookup using the tree
|
||||||
|
log_trace("constraint", "Unoptimized lookup");
|
||||||
|
return _lookup_ip(con->root, address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement count_ips by recursing on halves of the tree. Size represents
|
||||||
|
// the number of addresses in a prefix at the current level of the tree.
|
||||||
|
static uint64_t _count_ips_recurse(node_t *node, value_t value, uint64_t size)
|
||||||
|
{
|
||||||
|
assert(node);
|
||||||
|
if (IS_LEAF(node)) {
|
||||||
|
if (node->value == value) {
|
||||||
|
return size;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _count_ips_recurse(node->l, value, size >> 1) +
|
||||||
|
_count_ips_recurse(node->r, value, size >> 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the number of addresses that have a given value.
|
||||||
|
uint64_t constraint_count_ips(constraint_t *con, value_t value)
|
||||||
|
{
|
||||||
|
assert(con);
|
||||||
|
return _count_ips_recurse(con->root, value, (uint64_t)1 << 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the tree.
|
||||||
|
// All addresses will initally have the given value.
|
||||||
|
constraint_t* constraint_init(value_t value)
|
||||||
|
{
|
||||||
|
log_trace("constraint", "Initializing");
|
||||||
|
constraint_t* con = malloc(sizeof(constraint_t));
|
||||||
|
con->root = _create_leaf(value);
|
||||||
|
con->radix = calloc(sizeof(node_t *), 1 << RADIX_LENGTH);
|
||||||
|
assert(con->radix);
|
||||||
|
con->optimized = 0;
|
||||||
|
return con;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deinitialize and free the tree.
|
||||||
|
void constraint_free(constraint_t *con)
|
||||||
|
{
|
||||||
|
assert(con);
|
||||||
|
log_trace("constraint", "Cleaning up");
|
||||||
|
_destroy_subtree(con->root);
|
||||||
|
free(con->radix);
|
||||||
|
free(con);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a node that determines the values for the addresses with
|
||||||
|
// the given prefix. This is either the internal node that
|
||||||
|
// corresponds to the end of the prefix or a leaf node that
|
||||||
|
// encompasses the prefix. (Note: prefix must be in host byte order.)
|
||||||
|
static node_t* _lookup_node(node_t *root, uint32_t prefix, int len)
|
||||||
|
{
|
||||||
|
assert(root);
|
||||||
|
assert(0 <= len && len <= 32);
|
||||||
|
|
||||||
|
node_t *node = root;
|
||||||
|
uint32_t mask = 0x80000000;
|
||||||
|
|
||||||
|
for (int i=0; i < len; i++) {
|
||||||
|
if (IS_LEAF(node)) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
if (prefix & mask) {
|
||||||
|
node = node->r;
|
||||||
|
} else {
|
||||||
|
node = node->l;
|
||||||
|
}
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After values have been set, precompute prefix lookups.
|
||||||
|
void constraint_optimize(constraint_t *con)
|
||||||
|
{
|
||||||
|
assert(con);
|
||||||
|
if (con->optimized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log_trace("constraint", "Optimizing constraints");
|
||||||
|
for (uint32_t i=0; i < (1 << RADIX_LENGTH); i++) {
|
||||||
|
uint32_t prefix = i << (32 - RADIX_LENGTH);
|
||||||
|
con->radix[i] = _lookup_node(con->root, prefix, RADIX_LENGTH);
|
||||||
|
}
|
||||||
|
con->optimized = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
log_init(stderr, LOG_DEBUG);
|
||||||
|
|
||||||
|
constraint_t *con = constraint_init(0);
|
||||||
|
constraint_set(con, ntohl(inet_addr("128.128.0.0")), 1, 22);
|
||||||
|
constraint_set(con, ntohl(inet_addr("128.128.0.0")), 1, 1);
|
||||||
|
constraint_set(con, ntohl(inet_addr("128.0.0.0")), 1, 1);
|
||||||
|
constraint_set(con, ntohl(inet_addr("10.0.0.0")), 24, 1);
|
||||||
|
constraint_set(con, ntohl(inet_addr("10.0.0.0")), 24, 0);
|
||||||
|
constraint_set(con, ntohl(inet_addr("10.11.12.0")), 24, 1);
|
||||||
|
constraint_set(con, ntohl(inet_addr("141.212.0.0")), 16, 0);
|
||||||
|
|
||||||
|
for (int x=1; x < 2; x++) {
|
||||||
|
if (x == 1) {
|
||||||
|
constraint_optimize(con);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("count(0)=%ld\n", constraint_count_ips(con, 0));
|
||||||
|
printf("count(1)=%ld\n", constraint_count_ips(con, 1));
|
||||||
|
printf("%d\n", constraint_lookup_ip(con,ntohl(inet_addr("10.11.12.0"))));
|
||||||
|
assert(constraint_count_ips(con, 0) + constraint_count_ips(con, 1) == (uint64_t)1 << 32);
|
||||||
|
|
||||||
|
uint32_t i=0, count=0;
|
||||||
|
do {
|
||||||
|
if (constraint_lookup_ip(con, i))
|
||||||
|
count++;
|
||||||
|
} while (++i != 0);
|
||||||
|
printf("derived count(1)=%u\n", count);
|
||||||
|
}
|
||||||
|
|
||||||
|
constraint_free(con);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
static int init(constraint_t *con, char *file, const char *name, value_t value)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char line[1000];
|
||||||
|
int blocked = 0;
|
||||||
|
|
||||||
|
fp = fopen(file, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
log_fatal(name, "Unable to open %s file: %s: %s",
|
||||||
|
name, file, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||||
|
char *comment = strchr(line, '#');
|
||||||
|
if (comment) {
|
||||||
|
*comment = '\0';
|
||||||
|
}
|
||||||
|
char ip[33];
|
||||||
|
if ((sscanf(line, "%32s", ip)) == EOF) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int prefix_len;
|
||||||
|
char *slash = strchr(ip, '/');
|
||||||
|
if (slash == NULL) {
|
||||||
|
log_fatal(name,
|
||||||
|
"Unable to parse %s file: %s",
|
||||||
|
name, file);
|
||||||
|
}
|
||||||
|
// split apart network and prefix length
|
||||||
|
*slash = '\0';
|
||||||
|
prefix_len = atoi(&slash[1]);
|
||||||
|
constraint_set(con, ntohl(inet_addr(ip)), prefix_len, value);
|
||||||
|
|
||||||
|
blocked++;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
log_init(stderr, LOG_TRACE);
|
||||||
|
|
||||||
|
constraint_t *con = constraint_init(1);
|
||||||
|
init(con, "blacklist.prefixes", "blacklist", 0);
|
||||||
|
//constraint_optimize(con);
|
||||||
|
|
||||||
|
printf("count(0)=%lu\n", constraint_count_ips(con, 0));
|
||||||
|
printf("count(1)=%lu\n", constraint_count_ips(con, 1));
|
||||||
|
|
||||||
|
uint32_t i=0, count=0;
|
||||||
|
do {
|
||||||
|
if (constraint_lookup_ip(con, i))
|
||||||
|
count++;
|
||||||
|
} while (++i != 0);
|
||||||
|
printf("derived count(1)=%u\n", count);
|
||||||
|
|
||||||
|
constraint_free(con);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
14
lib/constraint.h
Normal file
14
lib/constraint.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef _CONSTRAINT_H
|
||||||
|
#define _CONSTRAINT_H
|
||||||
|
|
||||||
|
typedef struct _constraint constraint_t;
|
||||||
|
typedef int value_t;
|
||||||
|
|
||||||
|
constraint_t* constraint_init(value_t value);
|
||||||
|
void constraint_free(constraint_t *con);
|
||||||
|
void constraint_set(constraint_t *con, uint32_t prefix, int len, value_t value);
|
||||||
|
void constraint_optimize(constraint_t *con);
|
||||||
|
int constraint_lookup_ip(constraint_t *con, uint32_t address);
|
||||||
|
uint64_t constraint_count_ips(constraint_t *con, value_t value);
|
||||||
|
|
||||||
|
#endif //_CONSTRAINT_H
|
126
lib/logger.c
Normal file
126
lib/logger.c
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* Logger Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
#ifndef HEADER_ZUTIL_LOGGER_H
|
||||||
|
#define HEADER_ZUTIL_LOGGER_H
|
||||||
|
|
||||||
|
static enum LogLevel log_output_level = LOG_INFO;
|
||||||
|
|
||||||
|
static FILE *log_output_stream = NULL;
|
||||||
|
|
||||||
|
static const char *log_level_name[] = {
|
||||||
|
"FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE" };
|
||||||
|
|
||||||
|
static int LogLogVA(enum LogLevel level, const char *loggerName,
|
||||||
|
const char *logMessage, va_list args)
|
||||||
|
{
|
||||||
|
if (!log_output_stream) {
|
||||||
|
log_output_stream = stdout;
|
||||||
|
}
|
||||||
|
if (level <= log_output_level) {
|
||||||
|
const char *levelName = log_level_name[level];
|
||||||
|
assert(level < sizeof(log_level_name));
|
||||||
|
|
||||||
|
struct timeval now;
|
||||||
|
char timestamp[256];
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
time_t sec = now.tv_sec;
|
||||||
|
struct tm* ptm = localtime(&sec);
|
||||||
|
strftime(timestamp, 20, "%b %d %H:%M:%S", ptm);
|
||||||
|
fprintf(log_output_stream, "%s.%03ld [%s] ",
|
||||||
|
timestamp, now.tv_usec/1000, levelName);
|
||||||
|
if (loggerName) {
|
||||||
|
fprintf(log_output_stream, "%s: ", loggerName);
|
||||||
|
}
|
||||||
|
if (logMessage) {
|
||||||
|
vfprintf(log_output_stream, logMessage, args);
|
||||||
|
}
|
||||||
|
if (loggerName || logMessage) {
|
||||||
|
fputs("\n", log_output_stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_fatal(const char *name, const char *message, ...) {
|
||||||
|
va_list va; va_start(va, message);
|
||||||
|
LogLogVA(LOG_FATAL, name, message, va);
|
||||||
|
va_end(va);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_error(const char *name, const char *message, ...) {
|
||||||
|
va_list va; va_start(va, message);
|
||||||
|
int ret = LogLogVA(LOG_ERROR, name, message, va);
|
||||||
|
va_end(va);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_warn(const char *name, const char *message, ...) {
|
||||||
|
va_list va; va_start(va, message);
|
||||||
|
int ret = LogLogVA(LOG_WARN, name, message, va);
|
||||||
|
va_end(va);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_info(const char *name, const char *message, ...) {
|
||||||
|
va_list va; va_start(va, message);
|
||||||
|
int ret = LogLogVA(LOG_INFO, name, message, va);
|
||||||
|
va_end(va);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_debug(const char *name, const char *message, ...) {
|
||||||
|
va_list va; va_start(va, message);
|
||||||
|
int ret = LogLogVA(LOG_DEBUG, name, message, va);
|
||||||
|
va_end(va);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int log_trace(const char *name, const char *message, ...) {
|
||||||
|
va_list va; va_start(va, message);
|
||||||
|
int ret = LogLogVA(LOG_TRACE, name, message, va);
|
||||||
|
va_end(va);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_init(FILE *stream, enum LogLevel level)
|
||||||
|
{
|
||||||
|
log_output_stream = stream;
|
||||||
|
log_output_level = level;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double now(void)
|
||||||
|
{
|
||||||
|
struct timeval now;
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
return (double)now.tv_sec + (double)now.tv_usec/1000000.;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t dstrftime(char *buf, size_t maxsize, const char *format, double tm)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
double tm_floor;
|
||||||
|
tm_floor = floor(tm);
|
||||||
|
tv.tv_sec = (long) tm_floor;
|
||||||
|
tv.tv_usec = (long) (tm - floor(tm)) * 1000000;
|
||||||
|
return strftime(buf, maxsize, format, localtime((const time_t*) &tv));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
29
lib/logger.h
Normal file
29
lib/logger.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#ifndef _LOGGER_H
|
||||||
|
#define _LOGGER_H
|
||||||
|
|
||||||
|
enum LogLevel { LOG_FATAL, LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG, LOG_TRACE,
|
||||||
|
NUM_LOGLEVELS };
|
||||||
|
|
||||||
|
int log_fatal(const char *loggerName, const char *logMessage, ...) __attribute__((noreturn));
|
||||||
|
|
||||||
|
int log_error(const char *loggerName, const char *logMessage, ...);
|
||||||
|
|
||||||
|
int log_warn(const char *loggerName, const char *logMessage, ...);
|
||||||
|
|
||||||
|
int log_info(const char *loggerName, const char *logMessage, ...);
|
||||||
|
|
||||||
|
int log_debug(const char *loggerName, const char *logMessage, ...);
|
||||||
|
|
||||||
|
int log_trace(const char *loggerName, const char *logMessage, ...);
|
||||||
|
|
||||||
|
int log_init(FILE *stream, enum LogLevel level);
|
||||||
|
|
||||||
|
size_t dstrftime(char *, size_t, const char *, double);
|
||||||
|
|
||||||
|
double now();
|
||||||
|
|
||||||
|
#endif // _LOGGER_H
|
||||||
|
|
28
lib/random.c
Normal file
28
lib/random.c
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "random.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define RANDSRC "/dev/urandom"
|
||||||
|
|
||||||
|
int random_bytes(void *dst, size_t n)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(RANDSRC, "rb");
|
||||||
|
assert(f);
|
||||||
|
size_t r = fread(dst, n, 1, f);
|
||||||
|
fclose(f);
|
||||||
|
if (r < 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
9
lib/random.h
Normal file
9
lib/random.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifndef _RANDOM_H
|
||||||
|
#define _RANDOM_H
|
||||||
|
|
||||||
|
int random_bytes(void *dst, size_t n);
|
||||||
|
|
||||||
|
#endif
|
261
lib/redis.c
Normal file
261
lib/redis.c
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Redis Helpers Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "redis.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "assert.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <hiredis/hiredis.h>
|
||||||
|
|
||||||
|
#define REDIS_UNIX_PATH "/tmp/redis.sock"
|
||||||
|
#define REDIS_TIMEOUT 2
|
||||||
|
|
||||||
|
#undef MIN
|
||||||
|
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
|
||||||
|
|
||||||
|
static redisContext *rctx;
|
||||||
|
|
||||||
|
static redisContext* redis_connect(void)
|
||||||
|
{
|
||||||
|
struct timeval timeout;
|
||||||
|
timeout.tv_sec = REDIS_TIMEOUT;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
return (redisContext*) redisConnectUnixWithTimeout(REDIS_UNIX_PATH,
|
||||||
|
timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int chkerr(redisReply *reply)
|
||||||
|
{
|
||||||
|
assert(rctx);
|
||||||
|
if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {
|
||||||
|
log_error("redis", "an error occurred when "
|
||||||
|
"retreiving item from redis: %s",
|
||||||
|
rctx->errstr);
|
||||||
|
if (reply) {
|
||||||
|
freeReplyObject(reply);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int redis_init(void)
|
||||||
|
{
|
||||||
|
rctx = redis_connect();
|
||||||
|
if (!rctx) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int redis_close(void)
|
||||||
|
{
|
||||||
|
redisFree(rctx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
redisContext* redis_get_context(void)
|
||||||
|
{
|
||||||
|
return rctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
int redis_flush(void)
|
||||||
|
{
|
||||||
|
redisReply *reply = (redisReply*) redisCommand(rctx, "FLUSHDB");
|
||||||
|
if (chkerr(reply)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
freeReplyObject(reply);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int redis_existconf(const char *name)
|
||||||
|
{
|
||||||
|
assert(rctx);
|
||||||
|
redisReply *reply = (redisReply*) redisCommand(rctx, "EXISTS %s", name);
|
||||||
|
if (chkerr(reply)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int v = reply->integer;
|
||||||
|
freeReplyObject(reply);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int redis_delconf(const char *name)
|
||||||
|
{
|
||||||
|
assert(rctx);
|
||||||
|
redisReply *reply = (redisReply*) redisCommand(rctx, "DEL %s", name);
|
||||||
|
if (chkerr(reply)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
freeReplyObject(reply);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int redis_setconf(const char *name, char *value)
|
||||||
|
{
|
||||||
|
assert(rctx);
|
||||||
|
redisReply *reply = (redisReply*) redisCommand(rctx, "SET %s %s",
|
||||||
|
name, value);
|
||||||
|
if (chkerr(reply)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
freeReplyObject(reply);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int redis_getconf(const char *name, char *buf, size_t maxlen)
|
||||||
|
{
|
||||||
|
assert(rctx);
|
||||||
|
redisReply *reply = (redisReply*) redisCommand(rctx, "GET %s", name);
|
||||||
|
if (chkerr(reply)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
strncpy(buf, reply->str, maxlen);
|
||||||
|
freeReplyObject(reply);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t redis_getconf_uint32_t(const char *key)
|
||||||
|
{
|
||||||
|
assert(rctx);
|
||||||
|
char buf[50];
|
||||||
|
redis_getconf(key, buf, 50);
|
||||||
|
return atoi(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
int redis_setconf_uint32_t(const char *key, uint32_t value)
|
||||||
|
{
|
||||||
|
assert(rctx);
|
||||||
|
char buf[50];
|
||||||
|
sprintf(buf, "%u", value);
|
||||||
|
return redis_setconf(key, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static long redis_get_sizeof(const char *cmd, const char *name)
|
||||||
|
{
|
||||||
|
assert(rctx);
|
||||||
|
redisReply *reply;
|
||||||
|
reply = (redisReply*) redisCommand(rctx, "%s %s", cmd, name);
|
||||||
|
assert(reply);
|
||||||
|
assert(reply->type == REDIS_REPLY_INTEGER);
|
||||||
|
long rtr = reply->integer;
|
||||||
|
freeReplyObject(reply);
|
||||||
|
return rtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
long redis_get_sizeof_list(const char *name)
|
||||||
|
{
|
||||||
|
return redis_get_sizeof("LLEN", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
long redis_get_sizeof_set(const char *name)
|
||||||
|
{
|
||||||
|
return redis_get_sizeof("SCARD", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int redis_pull(char *redisqueuename, void *buf,
|
||||||
|
int maxload, size_t obj_size, int *numloaded, const char* cmd)
|
||||||
|
{
|
||||||
|
assert(rctx);
|
||||||
|
long elems_in_redis = redis_get_sizeof_list(redisqueuename);
|
||||||
|
long num_to_add = MIN(elems_in_redis, maxload);
|
||||||
|
log_info("redis", "INFO: redis load called on %s. Transfering %li "
|
||||||
|
"of %li elements to in-memory queue.",
|
||||||
|
redisqueuename,
|
||||||
|
num_to_add, elems_in_redis);
|
||||||
|
for(int i=0; i < num_to_add; i++) {
|
||||||
|
redisAppendCommand(rctx, "%s %s", cmd, redisqueuename);
|
||||||
|
}
|
||||||
|
for(int i=0; i < num_to_add; i++) {
|
||||||
|
redisReply *reply;
|
||||||
|
int rc = redisGetReply(rctx, (void**) &reply);
|
||||||
|
if (rc != REDIS_OK) {
|
||||||
|
log_fatal("redis", "response from redis != REDIS_OK");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!reply) {
|
||||||
|
log_fatal("redis", "no reply provided by redis.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (reply->type != REDIS_REPLY_STRING) {
|
||||||
|
log_fatal("redis",
|
||||||
|
"unxpected reply type from redis.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ((size_t)reply->len != obj_size) {
|
||||||
|
log_fatal("redis", "ERROR: unexpected lengthed "
|
||||||
|
"object provided by redis.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy((void*)((intptr_t)buf+i*obj_size), reply->str, obj_size);
|
||||||
|
freeReplyObject(reply);
|
||||||
|
}
|
||||||
|
*numloaded = num_to_add;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int redis_lpull(char *redisqueuename, void *buf,
|
||||||
|
int maxload, size_t obj_size, int *numloaded)
|
||||||
|
{
|
||||||
|
return redis_pull(redisqueuename, buf,
|
||||||
|
maxload, obj_size, numloaded, "LPOP");
|
||||||
|
}
|
||||||
|
|
||||||
|
int redis_spull(char *redisqueuename, void *buf,
|
||||||
|
int maxload, size_t obj_size, int *numloaded)
|
||||||
|
{
|
||||||
|
return redis_pull(redisqueuename, buf,
|
||||||
|
maxload, obj_size, numloaded, "SRAND");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int redis_push(char *redisqueuename,
|
||||||
|
void *buf, int num, size_t len, const char *cmd)
|
||||||
|
{
|
||||||
|
assert(rctx);
|
||||||
|
for (int i=0; i < num; i++) {
|
||||||
|
void* load = (void*)((intptr_t)buf + i*len);
|
||||||
|
int rc = redisAppendCommand(rctx, "%s %s %b",
|
||||||
|
cmd, redisqueuename, load, len);
|
||||||
|
if (rc != REDIS_OK || rctx->err) {
|
||||||
|
log_fatal("redis", "%s", rctx->errstr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
redisReply *reply;
|
||||||
|
for (int i=0; i < num; i++) {
|
||||||
|
if (redisGetReply(rctx, (void**) &reply) != REDIS_OK
|
||||||
|
|| rctx->err) {
|
||||||
|
log_fatal("redis","%s", rctx->errstr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (reply->type == REDIS_REPLY_ERROR) {
|
||||||
|
log_fatal("redis", "%s", rctx->errstr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
freeReplyObject(reply);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int redis_lpush(char *redisqueuename,
|
||||||
|
void *buf, int num, size_t len)
|
||||||
|
{
|
||||||
|
return redis_push(redisqueuename, buf, num, len, "RPUSH");
|
||||||
|
}
|
||||||
|
|
||||||
|
int redis_spush(char *redisqueuename,
|
||||||
|
void *buf, int num, size_t len)
|
||||||
|
{
|
||||||
|
return redis_push(redisqueuename, buf, num, len, "SADD");
|
||||||
|
}
|
||||||
|
|
40
lib/redis.h
Normal file
40
lib/redis.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <hiredis/hiredis.h>
|
||||||
|
|
||||||
|
#ifndef _REDIS_ZHELPERS_H
|
||||||
|
#define _REDIS_ZHELPERS_H
|
||||||
|
|
||||||
|
int redis_init(void);
|
||||||
|
|
||||||
|
int redis_close(void);
|
||||||
|
|
||||||
|
int redis_existconf(const char*);
|
||||||
|
|
||||||
|
int redis_flush(void);
|
||||||
|
|
||||||
|
int redis_delconf(const char*);
|
||||||
|
|
||||||
|
int redis_setconf(const char*, char*);
|
||||||
|
|
||||||
|
int redis_getconf(const char*, char*, size_t);
|
||||||
|
|
||||||
|
long redis_get_sizeof_list(const char*);
|
||||||
|
|
||||||
|
long redis_get_sizeof_set(const char*);
|
||||||
|
|
||||||
|
int redis_lpush(char*, void*, int, size_t);
|
||||||
|
|
||||||
|
int redis_lpull(char*, void*, int, size_t, int*);
|
||||||
|
|
||||||
|
int redis_spull(char*, void*, int, size_t, int*);
|
||||||
|
|
||||||
|
int redis_spush(char*, void*, int, size_t);
|
||||||
|
|
||||||
|
redisContext* redis_get_context(void);
|
||||||
|
|
||||||
|
uint32_t redis_getconf_uint32_t(const char*);
|
||||||
|
|
||||||
|
int redis_setconf_uint32_t(const char*, uint32_t);
|
||||||
|
|
||||||
|
#endif // _REDIS_ZHELPERS_H
|
1400
lib/rijndael-alg-fst.c
Normal file
1400
lib/rijndael-alg-fst.c
Normal file
File diff suppressed because it is too large
Load Diff
47
lib/rijndael-alg-fst.h
Normal file
47
lib/rijndael-alg-fst.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* rijndael-alg-fst.h
|
||||||
|
*
|
||||||
|
* @version 3.0 (December 2000)
|
||||||
|
*
|
||||||
|
* Optimised ANSI C code for the Rijndael cipher (now AES)
|
||||||
|
*
|
||||||
|
* @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
|
||||||
|
* @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
|
||||||
|
* @author Paulo Barreto <paulo.barreto@terra.com.br>
|
||||||
|
*
|
||||||
|
* This code is hereby placed in the public domain.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||||
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||||
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef __RIJNDAEL_ALG_FST_H
|
||||||
|
#define __RIJNDAEL_ALG_FST_H
|
||||||
|
|
||||||
|
#define MAXKC (256/32)
|
||||||
|
#define MAXKB (256/8)
|
||||||
|
#define MAXNR 14
|
||||||
|
|
||||||
|
typedef unsigned char u8;
|
||||||
|
typedef unsigned short u16;
|
||||||
|
typedef unsigned int u32;
|
||||||
|
|
||||||
|
int rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits);
|
||||||
|
int rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits);
|
||||||
|
void rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 pt[16], u8 ct[16]);
|
||||||
|
void rijndaelDecrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 ct[16], u8 pt[16]);
|
||||||
|
|
||||||
|
#ifdef INTERMEDIATE_VALUE_KAT
|
||||||
|
void rijndaelEncryptRound(const u32 rk[/*4*(Nr + 1)*/], int Nr, u8 block[16], int rounds);
|
||||||
|
void rijndaelDecryptRound(const u32 rk[/*4*(Nr + 1)*/], int Nr, u8 block[16], int rounds);
|
||||||
|
#endif /* INTERMEDIATE_VALUE_KAT */
|
||||||
|
|
||||||
|
#endif /* __RIJNDAEL_ALG_FST_H */
|
66
src/Makefile
Normal file
66
src/Makefile
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
CC=gcc
|
||||||
|
|
||||||
|
CFLAGS=-Wall -pedantic -Wextra -std=gnu99 -I../lib -I./ -Ioutput_modules -O2 -g
|
||||||
|
LDFLAGS=-g -pthread
|
||||||
|
LDLIBS= -lpcap -lgmp -lm
|
||||||
|
TARGETS=zmap
|
||||||
|
VPATH=../lib:output_modules:probe_modules
|
||||||
|
PREFIX=/usr/local
|
||||||
|
|
||||||
|
INSTALL=install
|
||||||
|
INSTALLDATA=install -m 644
|
||||||
|
mandir=/usr/share/man/man1/
|
||||||
|
bindir=$(PREFIX)/sbin
|
||||||
|
|
||||||
|
# Hardening and warnings for building with gcc
|
||||||
|
#M aybe add -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations
|
||||||
|
#-Wold-style-definition -Wswitch-enum
|
||||||
|
GCCWARNINGS = -Wall -fno-strict-aliasing -W -Wfloat-equal -Wundef \
|
||||||
|
-Wpointer-arith \
|
||||||
|
-Wwrite-strings -Wredundant-decls -Wchar-subscripts -Wcomment \
|
||||||
|
-Wformat=2 -Wwrite-strings -Wredundant-decls -Wno-unused-but-set-variable \
|
||||||
|
-Wnested-externs -Wbad-function-cast -Winit-self \
|
||||||
|
-Wmissing-field-initializers \
|
||||||
|
-Waddress -Wmissing-noreturn -Wnormalized=id \
|
||||||
|
-Woverride-init -Wstrict-overflow=1 -Wextra -Warray-bounds \
|
||||||
|
-Wstack-protector -Wformat -Wformat-security -Wpointer-sign -Wno-format-nonliteral -Wno-format-y2k
|
||||||
|
GCCHARDENING=-D_FORTIFY_SOURCE=2 -fstack-protector-all -fwrapv -fPIC --param ssp-buffer-size=1
|
||||||
|
LDHARDENING=-z relro -z now
|
||||||
|
|
||||||
|
EXTRACFLAGS=-g -O2 $(EXTRA_CFLAGS) $(GCCHARDENING) $(GCCWARNINGS) -Werror
|
||||||
|
EXTRALDFLAGS= $(LDHARDENING)
|
||||||
|
|
||||||
|
CFLAGS+=-Wall $(INCLUDE) $(EXTRACFLAGS)
|
||||||
|
LDFLAGS+=$(EXTRALDFLAGS)
|
||||||
|
|
||||||
|
modules=module_tcp_synscan.o module_icmp_echo.o module_udp.o #ADD YOUR MODULE HERE
|
||||||
|
|
||||||
|
objects=constraint.o blacklist.o cyclic.o logger.o send.o recv.o state.o monitor.o zopt.o zmap.o random.o output_modules.o module_simple_file.o module_extended_file.o packet.o probe_modules.o ${modules} validate.o rijndael-alg-fst.o get_gateway.o aesrand.o
|
||||||
|
|
||||||
|
ifeq ($(REDIS), true)
|
||||||
|
LDLIBS+=-lhiredis
|
||||||
|
objects+=module_redis.o redis.o module_ssldb.o
|
||||||
|
CFLAGS+=-DREDIS
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: $(TARGETS)
|
||||||
|
|
||||||
|
$(TARGETS):
|
||||||
|
$(CC) $(CFLAGS) $(DFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS)
|
||||||
|
|
||||||
|
zmap: $(objects)
|
||||||
|
|
||||||
|
zopt.c zopt.h: zopt.ggo
|
||||||
|
gengetopt -C --no-help --no-version -i $^ -F $*
|
||||||
|
|
||||||
|
install: zmap
|
||||||
|
$(INSTALL) zmap $(bindir)/zmap
|
||||||
|
test -d /etc/zmap || (mkdir /etc/zmap && $(INSTALLDATA) ../conf/* /etc/zmap/)
|
||||||
|
$(INSTALLDATA) ./zmap.1 $(mandir)
|
||||||
|
echo "\n\n\n\n**************\nSuccess! ZMap is installed. Try running (as root):\nzmap -p 80 -N 10 -B 1M -o -\n**************"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm -f $(objects) $(TARGETS)
|
||||||
|
|
||||||
|
.PHONY: install clean
|
||||||
|
|
57
src/aesrand.c
Normal file
57
src/aesrand.c
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../lib/rijndael-alg-fst.h"
|
||||||
|
#include "../lib/random.h"
|
||||||
|
#include "../lib/logger.h"
|
||||||
|
|
||||||
|
#define AES_ROUNDS 10
|
||||||
|
#define AES_BLOCK_WORDS 4
|
||||||
|
#define AES_KEY_BYTES 16
|
||||||
|
#define OUTPUT_BYTES 16
|
||||||
|
|
||||||
|
static uint32_t aes_input[AES_BLOCK_WORDS];
|
||||||
|
static uint32_t aes_sched[(AES_ROUNDS+1)*4];
|
||||||
|
static uint8_t aes_output[OUTPUT_BYTES];
|
||||||
|
static int init = 0;
|
||||||
|
|
||||||
|
void aesrand_init(uint32_t seed)
|
||||||
|
{
|
||||||
|
memset(&aes_input, 0, sizeof(aes_input));
|
||||||
|
uint8_t key[AES_KEY_BYTES];
|
||||||
|
if (seed) {
|
||||||
|
memset(key, 0, AES_KEY_BYTES*sizeof(uint8_t));
|
||||||
|
memcpy(key, &seed, sizeof(uint32_t));
|
||||||
|
} else {
|
||||||
|
if (!random_bytes(key, AES_KEY_BYTES)) {
|
||||||
|
log_fatal("aesrand", "couldn't get random bytes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rijndaelKeySetupEnc(aes_sched, key, AES_KEY_BYTES*8) != AES_ROUNDS) {
|
||||||
|
log_fatal("aesrand", "could not initialize AES key");
|
||||||
|
}
|
||||||
|
memset(aes_output, 0, OUTPUT_BYTES*sizeof(uint8_t));
|
||||||
|
init = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t aesrand_getword(void)
|
||||||
|
{
|
||||||
|
assert(init);
|
||||||
|
memcpy(aes_input, aes_output, sizeof(aes_input));
|
||||||
|
rijndaelEncrypt(aes_sched, AES_ROUNDS,
|
||||||
|
(uint8_t *)aes_input, aes_output);
|
||||||
|
uint64_t retval;
|
||||||
|
memcpy(&retval, aes_output, sizeof(retval));
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
18
src/aesrand.h
Normal file
18
src/aesrand.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifndef _AESRAND_H
|
||||||
|
#define _AESRAND_H
|
||||||
|
|
||||||
|
void aesrand_init(uint32_t seed);
|
||||||
|
|
||||||
|
uint64_t aesrand_getword(void);
|
||||||
|
|
||||||
|
#endif
|
184
src/cyclic.c
Normal file
184
src/cyclic.c
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cyclic provides an inexpensive approach to iterating over the IPv4 address
|
||||||
|
* space in a random(-ish) manner such that we connect to every host once in
|
||||||
|
* a scan execution without having to keep track of the IPs that have been
|
||||||
|
* scanned or need to be scanned and such that each scan has a different
|
||||||
|
* ordering. We accomplish this by utilizing a cyclic multiplicative group
|
||||||
|
* of integers modulo a prime and generating a new primitive root (generator)
|
||||||
|
* for each scan.
|
||||||
|
*
|
||||||
|
* We know that 3 is a generator of (Z mod 2^32 + 15 - {0}, *)
|
||||||
|
* and that we have coverage over the entire address space because 2**32 + 15
|
||||||
|
* is prime and ||(Z mod PRIME - {0}, *)|| == PRIME - 1. Therefore, we
|
||||||
|
* just need to find a new generator (primitive root) of the cyclic group for
|
||||||
|
* each scan that we perform.
|
||||||
|
*
|
||||||
|
* Because generators map to generators over an isomorphism, we can efficiently
|
||||||
|
* find random primitive roots of our mult. group by finding random generators
|
||||||
|
* of the group (Zp-1, +) which is isomorphic to (Zp*, *). Specifically the
|
||||||
|
* generators of (Zp-1, +) are { s | (s, p-1) == 1 } which implies that
|
||||||
|
* the generators of (Zp*, *) are { d^s | (s, p-1) == 1 }. where d is a known
|
||||||
|
* generator of the multiplicative group. We efficiently find
|
||||||
|
* generators of the additive group by precalculating the psub1_f of
|
||||||
|
* p - 1 and randomly checking random numbers against the psub1_f until
|
||||||
|
* we find one that is coprime and map it into Zp*. Because
|
||||||
|
* totient(totient(p)) ~= 10^9, this should take relatively few
|
||||||
|
* iterations to find a new generator.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cyclic.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include <gmp.h>
|
||||||
|
|
||||||
|
#include "../lib/logger.h"
|
||||||
|
#include "../lib/blacklist.h"
|
||||||
|
|
||||||
|
#include "state.h"
|
||||||
|
#include "aesrand.h"
|
||||||
|
|
||||||
|
#define LSRC "cyclic"
|
||||||
|
#define PRIME 4294967311 // 2^32 + 15
|
||||||
|
#define KNOWN_PRIMROOT 3
|
||||||
|
|
||||||
|
// distinct prime factors of 2^32 + 15
|
||||||
|
static const uint64_t psub1_f[] = { 2, 3, 5, 131, 364289 };
|
||||||
|
|
||||||
|
// selected primitive root that we'll use as the generator
|
||||||
|
static uint64_t primroot = 0;
|
||||||
|
static uint64_t current = 0;
|
||||||
|
|
||||||
|
#define COPRIME 1
|
||||||
|
#define NOT_COPRIME 0
|
||||||
|
|
||||||
|
// check whether two integers are coprime
|
||||||
|
static int check_coprime(uint64_t check)
|
||||||
|
{
|
||||||
|
for (unsigned i=0; i < sizeof(psub1_f)/sizeof(psub1_f[0]); i++) {
|
||||||
|
if (psub1_f[i] > check && !(psub1_f[i] % check)) {
|
||||||
|
return NOT_COPRIME;
|
||||||
|
} else if (psub1_f[i] < check && !(check % psub1_f[i])) {
|
||||||
|
return NOT_COPRIME;
|
||||||
|
} else if (psub1_f[i] == check) {
|
||||||
|
return NOT_COPRIME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return COPRIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find gen of cyclic group Z modulo PRIME
|
||||||
|
static uint64_t find_primroot(void)
|
||||||
|
{
|
||||||
|
// what luck, rand() returns a uint32_t!
|
||||||
|
uint32_t candidate = (uint32_t) aesrand_getword() & 0xFFFF;
|
||||||
|
while(check_coprime(candidate) != COPRIME) {
|
||||||
|
++candidate;
|
||||||
|
}
|
||||||
|
// pre-modded result is gigantic so use GMP
|
||||||
|
mpz_t base, power, prime, primroot;
|
||||||
|
mpz_init_set_d(base, (double) KNOWN_PRIMROOT);
|
||||||
|
mpz_init_set_d(power, (double) candidate);
|
||||||
|
mpz_init_set_d(prime, (double) PRIME);
|
||||||
|
mpz_init(primroot);
|
||||||
|
mpz_powm(primroot, base, power, prime);
|
||||||
|
uint64_t retv = (uint64_t) mpz_get_ui(primroot);
|
||||||
|
mpz_clear(base);
|
||||||
|
mpz_clear(power);
|
||||||
|
mpz_clear(prime);
|
||||||
|
mpz_clear(primroot);
|
||||||
|
return retv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cyclic_init(uint32_t primroot_, uint32_t current_)
|
||||||
|
{
|
||||||
|
assert(!(!primroot_ && current_));
|
||||||
|
|
||||||
|
if (zconf.use_seed) {
|
||||||
|
aesrand_init(zconf.seed+1);
|
||||||
|
} else {
|
||||||
|
aesrand_init(0);
|
||||||
|
}
|
||||||
|
if (!primroot_) {
|
||||||
|
do {
|
||||||
|
primroot = find_primroot();
|
||||||
|
} while (primroot >= (1LL << 32));
|
||||||
|
log_debug(LSRC, "primitive root: %lld", primroot);
|
||||||
|
current = (uint32_t) aesrand_getword() & 0xFFFF;
|
||||||
|
log_debug(LSRC, "starting point: %lld", current);
|
||||||
|
} else {
|
||||||
|
primroot = primroot_;
|
||||||
|
log_debug(LSRC, "primitive root %lld specified by caller",
|
||||||
|
primroot);
|
||||||
|
if (!current_) {
|
||||||
|
current = (uint32_t) aesrand_getword() & 0xFFFF;
|
||||||
|
log_debug(LSRC, "no cyclic starting point, "
|
||||||
|
"selected random startpoint: %lld",
|
||||||
|
current);
|
||||||
|
} else {
|
||||||
|
current = current_;
|
||||||
|
log_debug(LSRC, "starting point %lld specified by caller",
|
||||||
|
current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zconf.generator = primroot;
|
||||||
|
if (blacklist_init_from_files(zconf.whitelist_filename,
|
||||||
|
zconf.blacklist_filename)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure current is an allowed ip
|
||||||
|
cyclic_get_next_ip();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t cyclic_get_curr_ip(void)
|
||||||
|
{
|
||||||
|
return (uint32_t) current;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t cyclic_get_primroot(void)
|
||||||
|
{
|
||||||
|
return (uint32_t) primroot;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t cyclic_get_next_elem(void)
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
current *= primroot;
|
||||||
|
current %= PRIME;
|
||||||
|
} while (current >= (1LL << 32));
|
||||||
|
return (uint32_t) current;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t cyclic_get_next_ip(void)
|
||||||
|
{
|
||||||
|
while (1) {
|
||||||
|
uint32_t candidate = cyclic_get_next_elem();
|
||||||
|
if (!blacklist_is_allowed(candidate)) {
|
||||||
|
zsend.blacklisted++;
|
||||||
|
} else {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
25
src/cyclic.h
Normal file
25
src/cyclic.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifndef _CYCLIC_H
|
||||||
|
#define _CYCLIC_H
|
||||||
|
|
||||||
|
int cyclic_init(uint32_t, uint32_t);
|
||||||
|
|
||||||
|
// get next IP address to scan
|
||||||
|
uint32_t cyclic_get_next_ip(void);
|
||||||
|
|
||||||
|
// what IP address was returned last
|
||||||
|
uint32_t cyclic_get_curr_ip(void);
|
||||||
|
|
||||||
|
// what primitive root was generated for this current scan
|
||||||
|
uint32_t cyclic_get_primroot(void);
|
||||||
|
|
||||||
|
#endif
|
237
src/get_gateway.c
Normal file
237
src/get_gateway.c
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
#include <linux/rtnetlink.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "../lib/logger.h"
|
||||||
|
|
||||||
|
int read_nl_sock(int sock, char *buf, int buf_len)
|
||||||
|
{
|
||||||
|
int msg_len = 0;
|
||||||
|
char *pbuf = buf;
|
||||||
|
do {
|
||||||
|
int len = recv(sock, pbuf, buf_len - msg_len, 0);
|
||||||
|
if (len <= 0) {
|
||||||
|
log_debug("get-gw", "recv failed: %s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
struct nlmsghdr *nlhdr = (struct nlmsghdr *)pbuf;
|
||||||
|
if (NLMSG_OK(nlhdr, ((unsigned int)len)) == 0 ||
|
||||||
|
nlhdr->nlmsg_type == NLMSG_ERROR) {
|
||||||
|
log_debug("get-gw", "recv failed: %s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (nlhdr->nlmsg_type == NLMSG_DONE) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
msg_len += len;
|
||||||
|
pbuf += len;
|
||||||
|
}
|
||||||
|
if ((nlhdr->nlmsg_flags & NLM_F_MULTI) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
return msg_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int send_nl_req(uint16_t msg_type, uint32_t seq,
|
||||||
|
void *payload, uint32_t payload_len)
|
||||||
|
{
|
||||||
|
int sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
|
||||||
|
if (sock < 0) {
|
||||||
|
log_error("get-gw", "unable to get socket: %s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (NLMSG_SPACE(payload_len) < payload_len) {
|
||||||
|
// Integer overflow
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
struct nlmsghdr *nlmsg;
|
||||||
|
nlmsg = malloc(NLMSG_SPACE(payload_len));
|
||||||
|
if (!nlmsg) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(nlmsg, 0, sizeof(nlmsg));
|
||||||
|
memcpy(NLMSG_DATA(nlmsg), payload, payload_len);
|
||||||
|
nlmsg->nlmsg_type = msg_type;
|
||||||
|
nlmsg->nlmsg_len = NLMSG_LENGTH(payload_len);
|
||||||
|
nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
|
||||||
|
nlmsg->nlmsg_seq = seq;
|
||||||
|
nlmsg->nlmsg_pid = getpid();
|
||||||
|
|
||||||
|
if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) {
|
||||||
|
log_error("get-gw", "failure sending: %s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
free(nlmsg);
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_hw_addr(struct in_addr *gw_ip, char *iface, unsigned char *hw_mac)
|
||||||
|
{
|
||||||
|
char buf[8192];
|
||||||
|
struct ndmsg req;
|
||||||
|
struct nlmsghdr *nlhdr;
|
||||||
|
|
||||||
|
if (!gw_ip || !hw_mac) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Send RTM_GETNEIGH request
|
||||||
|
req.ndm_family = AF_INET;
|
||||||
|
req.ndm_ifindex = if_nametoindex(iface);
|
||||||
|
req.ndm_state = NUD_REACHABLE;
|
||||||
|
req.ndm_type = NDA_LLADDR;
|
||||||
|
|
||||||
|
int sock = send_nl_req(RTM_GETNEIGH, 1, &req, sizeof(req));
|
||||||
|
|
||||||
|
// Read responses
|
||||||
|
unsigned nl_len = read_nl_sock(sock, buf, sizeof(buf));
|
||||||
|
if (nl_len <= 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Parse responses
|
||||||
|
nlhdr = (struct nlmsghdr *)buf;
|
||||||
|
while (NLMSG_OK(nlhdr, nl_len)) {
|
||||||
|
struct rtattr *rt_attr;
|
||||||
|
struct rtmsg *rt_msg;
|
||||||
|
int rt_len;
|
||||||
|
unsigned char mac[6];
|
||||||
|
struct in_addr dst_ip;
|
||||||
|
int correct_ip = 0;
|
||||||
|
|
||||||
|
rt_msg = (struct rtmsg *) NLMSG_DATA(nlhdr);
|
||||||
|
|
||||||
|
if ((rt_msg->rtm_family != AF_INET)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rt_attr = (struct rtattr *) RTM_RTA(rt_msg);
|
||||||
|
rt_len = RTM_PAYLOAD(nlhdr);
|
||||||
|
while (RTA_OK(rt_attr, rt_len)) {
|
||||||
|
switch (rt_attr->rta_type) {
|
||||||
|
case NDA_LLADDR:
|
||||||
|
assert(RTA_PAYLOAD(rt_attr) == IFHWADDRLEN);
|
||||||
|
memcpy(mac, RTA_DATA(rt_attr), IFHWADDRLEN);
|
||||||
|
break;
|
||||||
|
case NDA_DST:
|
||||||
|
assert(RTA_PAYLOAD(rt_attr) == sizeof(dst_ip));
|
||||||
|
memcpy(&dst_ip, RTA_DATA(rt_attr), sizeof(dst_ip));
|
||||||
|
if (memcmp(&dst_ip, gw_ip, sizeof(dst_ip)) == 0) {
|
||||||
|
correct_ip = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rt_attr = RTA_NEXT(rt_attr, rt_len);
|
||||||
|
}
|
||||||
|
if (correct_ip) {
|
||||||
|
memcpy(hw_mac, mac, IFHWADDRLEN);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
nlhdr = NLMSG_NEXT(nlhdr, nl_len);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// gw and iface[IF_NAMESIZE] MUST be allocated
|
||||||
|
int get_default_gw(struct in_addr *gw, char *iface)
|
||||||
|
{
|
||||||
|
struct rtmsg req;
|
||||||
|
unsigned int nl_len;
|
||||||
|
char buf[8192];
|
||||||
|
struct nlmsghdr *nlhdr;
|
||||||
|
|
||||||
|
if (!gw || !iface) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send RTM_GETROUTE request
|
||||||
|
memset(&req, 0, sizeof(req));
|
||||||
|
int sock = send_nl_req(RTM_GETROUTE, 0, &req, sizeof(req));
|
||||||
|
|
||||||
|
// Read responses
|
||||||
|
nl_len = read_nl_sock(sock, buf, sizeof(buf));
|
||||||
|
if (nl_len <= 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse responses
|
||||||
|
nlhdr = (struct nlmsghdr *)buf;
|
||||||
|
while (NLMSG_OK(nlhdr, nl_len)) {
|
||||||
|
struct rtattr *rt_attr;
|
||||||
|
struct rtmsg *rt_msg;
|
||||||
|
int rt_len;
|
||||||
|
int has_gw = 0;
|
||||||
|
|
||||||
|
rt_msg = (struct rtmsg *) NLMSG_DATA(nlhdr);
|
||||||
|
|
||||||
|
if ((rt_msg->rtm_family != AF_INET) || (rt_msg->rtm_table != RT_TABLE_MAIN)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rt_attr = (struct rtattr *) RTM_RTA(rt_msg);
|
||||||
|
rt_len = RTM_PAYLOAD(nlhdr);
|
||||||
|
while (RTA_OK(rt_attr, rt_len)) {
|
||||||
|
switch (rt_attr->rta_type) {
|
||||||
|
case RTA_OIF:
|
||||||
|
if_indextoname(*(int *) RTA_DATA(rt_attr), iface);
|
||||||
|
break;
|
||||||
|
case RTA_GATEWAY:
|
||||||
|
gw->s_addr = *(unsigned int *) RTA_DATA(rt_attr);
|
||||||
|
has_gw = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rt_attr = RTA_NEXT(rt_attr, rt_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_gw) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
nlhdr = NLMSG_NEXT(nlhdr, nl_len);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the first IP address for a given iface
|
||||||
|
int get_iface_ip(char *iface, struct in_addr *ip)
|
||||||
|
{
|
||||||
|
int sock;
|
||||||
|
struct ifreq ifr;
|
||||||
|
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (sock < 0) {
|
||||||
|
log_error("get-gw", "failure opening socket: %s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ifr.ifr_addr.sa_family = AF_INET;
|
||||||
|
strncpy(ifr.ifr_name, iface, IFNAMSIZ-1);
|
||||||
|
|
||||||
|
if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) {
|
||||||
|
log_error("get-gw", "ioctl failure: %s", strerror(errno));
|
||||||
|
close(sock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
close(sock);
|
||||||
|
memcpy(ip, &((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr, sizeof(*ip));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
18
src/get_gateway.h
Normal file
18
src/get_gateway.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _GET_GATEWAY_H
|
||||||
|
#define _GET_GATEWAY_H
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
int get_hw_addr(struct in_addr *gw_ip, char *iface, unsigned char *hw_mac);
|
||||||
|
int get_default_gw(struct in_addr *gw, char *iface);
|
||||||
|
int get_iface_ip(char *iface, struct in_addr *ip);
|
||||||
|
|
||||||
|
#endif
|
231
src/monitor.c
Normal file
231
src/monitor.c
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
// module responsible for printing on-screen updates during the scan process
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "recv.h"
|
||||||
|
#include "monitor.h"
|
||||||
|
#include "state.h"
|
||||||
|
|
||||||
|
#include "../lib/logger.h"
|
||||||
|
|
||||||
|
#define UPDATE_INTERVAL 1 //seconds
|
||||||
|
|
||||||
|
static double last_now = 0.0;
|
||||||
|
static uint32_t last_sent = 0;
|
||||||
|
static uint32_t last_rcvd = 0;
|
||||||
|
static uint32_t last_drop = 0;
|
||||||
|
static uint32_t last_failures = 0;
|
||||||
|
|
||||||
|
static double min_d(double array[], int n)
|
||||||
|
{
|
||||||
|
double value=INFINITY;
|
||||||
|
for (int i=0; i<n; i++) {
|
||||||
|
if (array[i] < value) {
|
||||||
|
value = array[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pretty print elapsed (or estimated) number of seconds
|
||||||
|
static void time_string(uint32_t time, int est, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
int y = time / 31556736;
|
||||||
|
int d = (time % 31556736) / 86400;
|
||||||
|
int h = (time % 86400) / 3600;
|
||||||
|
int m = (time % 3600) / 60;
|
||||||
|
int s = time % 60;
|
||||||
|
|
||||||
|
if (est) {
|
||||||
|
if (y > 0) {
|
||||||
|
snprintf(buf, len, "%d years", y);
|
||||||
|
} else if (d > 9) {
|
||||||
|
snprintf(buf, len, "%dd", d);
|
||||||
|
} else if (d > 0) {
|
||||||
|
snprintf(buf, len, "%dd%02dh", d, h);
|
||||||
|
} else if (h > 9) {
|
||||||
|
snprintf(buf, len, "%dh", h);
|
||||||
|
} else if (h > 0) {
|
||||||
|
snprintf(buf, len, "%dh%02dm", h, m);
|
||||||
|
} else if (m > 9) {
|
||||||
|
snprintf(buf, len, "%dm", m);
|
||||||
|
} else if (m > 0) {
|
||||||
|
snprintf(buf, len, "%dm%02ds", m, s);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, len, "%ds", s);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (d > 0) {
|
||||||
|
snprintf(buf, len, "%dd%d:%02d:%02d", d, h, m, s);
|
||||||
|
} else if (h > 0) {
|
||||||
|
snprintf(buf, len, "%d:%02d:%02d", h, m, s);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, len, "%d:%02d", m, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pretty print quantities
|
||||||
|
static void number_string(uint32_t n, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
int figs = 0;
|
||||||
|
if (n < 1000) {
|
||||||
|
snprintf(buf, len, "%u ", n);
|
||||||
|
} else if (n < 1000000) {
|
||||||
|
if (n < 10000) {
|
||||||
|
figs = 2;
|
||||||
|
} else if (n < 100000) {
|
||||||
|
figs = 1;
|
||||||
|
}
|
||||||
|
snprintf(buf, len, "%0.*f K", figs, (float)n/1000.);
|
||||||
|
} else {
|
||||||
|
if (figs < 10000000) {
|
||||||
|
figs = 2;
|
||||||
|
} else if (figs < 100000000) {
|
||||||
|
figs = 1;
|
||||||
|
}
|
||||||
|
snprintf(buf, len, "%0.*f M", figs, (float)n/1000000.);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// estimate time remaining time based on config and state
|
||||||
|
double compute_remaining_time(double age)
|
||||||
|
{
|
||||||
|
if (!zsend.complete) {
|
||||||
|
double remaining[] = {INFINITY, INFINITY, INFINITY};
|
||||||
|
if (zsend.targets) {
|
||||||
|
double done = (double)zsend.sent/zsend.targets;
|
||||||
|
remaining[0] = (1. - done)*(age/done) + zconf.cooldown_secs;
|
||||||
|
}
|
||||||
|
if (zconf.max_runtime) {
|
||||||
|
remaining[1] = (zconf.max_runtime - age)+zconf.cooldown_secs;
|
||||||
|
}
|
||||||
|
if (zconf.max_results) {
|
||||||
|
double done = (double)zrecv.success_unique/zconf.max_results;
|
||||||
|
remaining[2] = (1. - done)*(age/done);
|
||||||
|
}
|
||||||
|
return min_d(remaining, sizeof(remaining)/sizeof(double));
|
||||||
|
} else {
|
||||||
|
return zconf.cooldown_secs - (now() - zsend.finish);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void monitor_update(void)
|
||||||
|
{
|
||||||
|
if (last_now > 0.0) {
|
||||||
|
double age = now() - zsend.start;
|
||||||
|
double delta = now() - last_now;
|
||||||
|
double remaining_secs = compute_remaining_time(age);
|
||||||
|
double percent_complete = 100.*age/(age + remaining_secs);
|
||||||
|
|
||||||
|
// ask pcap for fresh values
|
||||||
|
recv_update_pcap_stats();
|
||||||
|
|
||||||
|
// format times for display
|
||||||
|
char time_left[20];
|
||||||
|
if (age < 5) {
|
||||||
|
time_left[0] = '\0';
|
||||||
|
} else {
|
||||||
|
char buf[20];
|
||||||
|
time_string((int)remaining_secs, 1, buf, sizeof(buf));
|
||||||
|
snprintf(time_left, sizeof(time_left), " (%s left)", buf);
|
||||||
|
}
|
||||||
|
char time_past[20];
|
||||||
|
time_string((int)age, 0, time_past, sizeof(time_past));
|
||||||
|
|
||||||
|
char send_rate[20], send_avg[20],
|
||||||
|
recv_rate[20], recv_avg[20],
|
||||||
|
pcap_drop[20], pcap_drop_avg[20];
|
||||||
|
// recv stats
|
||||||
|
number_string((zrecv.success_unique - last_rcvd)/delta,
|
||||||
|
recv_rate, sizeof(recv_rate));
|
||||||
|
number_string((zrecv.success_unique/age), recv_avg, sizeof(recv_avg));
|
||||||
|
// dropped stats
|
||||||
|
number_string((zrecv.pcap_drop + zrecv.pcap_ifdrop - last_drop)/delta,
|
||||||
|
pcap_drop, sizeof(pcap_drop));
|
||||||
|
number_string(((zrecv.pcap_drop + zrecv.pcap_ifdrop)/age),
|
||||||
|
pcap_drop_avg, sizeof(pcap_drop_avg));
|
||||||
|
|
||||||
|
// Warn if we drop > 5% of our average receive rate
|
||||||
|
uint32_t drop_rate = (uint32_t)((zrecv.pcap_drop + zrecv.pcap_ifdrop - last_drop) / delta);
|
||||||
|
if (drop_rate > (uint32_t)((zrecv.success_unique - last_rcvd) / delta) / 20) {
|
||||||
|
log_warn("monitor", "Dropped %d packets in the last second, (%d total dropped (pcap: %d + iface: %d))",
|
||||||
|
drop_rate, zrecv.pcap_drop + zrecv.pcap_ifdrop, zrecv.pcap_drop, zrecv.pcap_ifdrop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn if we fail to send > 1% of our average send rate
|
||||||
|
uint32_t fail_rate = (uint32_t)((zsend.sendto_failures - last_failures) / delta); // failures/sec
|
||||||
|
if (fail_rate > ((zsend.sent / age) / 100)) {
|
||||||
|
log_warn("monitor", "Failed to send %d packets/sec (%d total failures)",
|
||||||
|
fail_rate, zsend.sendto_failures);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!zsend.complete) {
|
||||||
|
// main display (during sending)
|
||||||
|
number_string((zsend.sent - last_sent)/delta,
|
||||||
|
send_rate, sizeof(send_rate));
|
||||||
|
number_string((zsend.sent/age), send_avg, sizeof(send_avg));
|
||||||
|
fprintf(stderr,
|
||||||
|
"%5s %0.0f%%%s; send: %u %sp/s (%sp/s avg); "
|
||||||
|
"recv: %u %sp/s (%sp/s avg); "
|
||||||
|
"drops: %sp/s (%sp/s avg); "
|
||||||
|
"hits: %0.2f%%\n",
|
||||||
|
time_past,
|
||||||
|
percent_complete,
|
||||||
|
time_left,
|
||||||
|
zsend.sent,
|
||||||
|
send_rate,
|
||||||
|
send_avg,
|
||||||
|
zrecv.success_unique,
|
||||||
|
recv_rate,
|
||||||
|
recv_avg,
|
||||||
|
pcap_drop,
|
||||||
|
pcap_drop_avg,
|
||||||
|
zrecv.success_unique*100./zsend.sent);
|
||||||
|
} else {
|
||||||
|
// alternate display (during cooldown)
|
||||||
|
number_string((zsend.sent/(zsend.finish - zsend.start)), send_avg, sizeof(send_avg));
|
||||||
|
fprintf(stderr,
|
||||||
|
"%5s %0.0f%%%s; send: %u done (%sp/s avg); "
|
||||||
|
"recv: %u %sp/s (%sp/s avg); "
|
||||||
|
"drops: %sp/s (%sp/s avg); "
|
||||||
|
"hits: %0.2f%%\n",
|
||||||
|
time_past,
|
||||||
|
percent_complete,
|
||||||
|
time_left,
|
||||||
|
zsend.sent,
|
||||||
|
send_avg,
|
||||||
|
zrecv.success_unique,
|
||||||
|
recv_rate,
|
||||||
|
recv_avg,
|
||||||
|
pcap_drop,
|
||||||
|
pcap_drop_avg,
|
||||||
|
zrecv.success_unique*100./zsend.sent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last_now = now();
|
||||||
|
last_sent = zsend.sent;
|
||||||
|
last_rcvd = zrecv.success_unique;
|
||||||
|
last_drop = zrecv.pcap_drop + zrecv.pcap_ifdrop;
|
||||||
|
last_failures = zsend.sendto_failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
void monitor_run(void)
|
||||||
|
{
|
||||||
|
while (!(zsend.complete && zrecv.complete)) {
|
||||||
|
monitor_update();
|
||||||
|
sleep(UPDATE_INTERVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
14
src/monitor.h
Normal file
14
src/monitor.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _MONITOR_H
|
||||||
|
#define _MONITOR_H
|
||||||
|
|
||||||
|
void monitor_run();
|
||||||
|
|
||||||
|
#endif
|
109
src/output_modules/module_extended_file.c
Normal file
109
src/output_modules/module_extended_file.c
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "../../lib/logger.h"
|
||||||
|
|
||||||
|
#include "output_modules.h"
|
||||||
|
|
||||||
|
static FILE *file = NULL;
|
||||||
|
#define UNUSED __attribute__((unused))
|
||||||
|
|
||||||
|
int extendedfile_init(struct state_conf *conf)
|
||||||
|
{
|
||||||
|
assert(conf);
|
||||||
|
if (conf->output_filename) {
|
||||||
|
if (!strcmp(conf->output_filename, "-")) {
|
||||||
|
file = stdout;
|
||||||
|
} else {
|
||||||
|
if (!(file = fopen(conf->output_filename, "w"))) {
|
||||||
|
perror("Couldn't open output file");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(file, "response, saddr, daddr, sport, "
|
||||||
|
"dport, seq, ack, in_cooldown, is_repeat, timestamp\n");
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fprint_tv(FILE *f, struct timeval *tv)
|
||||||
|
{
|
||||||
|
char time_string[40];
|
||||||
|
struct tm *ptm = localtime(&tv->tv_sec);
|
||||||
|
strftime(time_string, sizeof (time_string),
|
||||||
|
"%Y-%m-%d %H:%M:%S", ptm);
|
||||||
|
long milliseconds = tv->tv_usec / 1000;
|
||||||
|
fprintf(f, "%s.%03ld\n", time_string, milliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int extendedfile_ip(ipaddr_n_t saddr, ipaddr_n_t daddr,
|
||||||
|
const char *response_type, int is_repeat,
|
||||||
|
int in_cooldown, const u_char *packet, size_t buflen)
|
||||||
|
{
|
||||||
|
struct iphdr *ip_hdr = (struct iphdr *)&packet[sizeof(struct ethhdr)];
|
||||||
|
if (buflen < (sizeof(struct ethhdr) + ip_hdr->ihl*4 + sizeof(struct tcphdr)))
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
struct tcphdr *tcp = (struct tcphdr *)((char *)ip_hdr + ip_hdr->ihl * 4);
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
struct in_addr addr;
|
||||||
|
addr.s_addr = saddr;
|
||||||
|
// inet_ntoa returns a <<const>> char *
|
||||||
|
fprintf(file, "%s, %s, ",
|
||||||
|
response_type,
|
||||||
|
inet_ntoa(addr));
|
||||||
|
addr.s_addr = daddr;
|
||||||
|
fprintf(file, "%s, %u, %u, %u, %u, %i, %i,",
|
||||||
|
inet_ntoa(addr),
|
||||||
|
ntohs(tcp->source),
|
||||||
|
ntohs(tcp->dest),
|
||||||
|
ntohl(tcp->seq),
|
||||||
|
ntohl(tcp->ack_seq),
|
||||||
|
in_cooldown,
|
||||||
|
is_repeat);
|
||||||
|
struct timeval t;
|
||||||
|
gettimeofday(&t, NULL);
|
||||||
|
fprint_tv(file, &t);
|
||||||
|
fflush(file);
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int extendedfile_close(UNUSED struct state_conf* c,
|
||||||
|
UNUSED struct state_send* s, UNUSED struct state_recv* r)
|
||||||
|
{
|
||||||
|
if (file) {
|
||||||
|
fflush(file);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
output_module_t module_extended_file = {
|
||||||
|
.name = "extended_file",
|
||||||
|
.init = &extendedfile_init,
|
||||||
|
.start = NULL,
|
||||||
|
.update = NULL,
|
||||||
|
.update_interval = 0,
|
||||||
|
.close = &extendedfile_close,
|
||||||
|
.success_ip = &extendedfile_ip,
|
||||||
|
.other_ip = &extendedfile_ip
|
||||||
|
};
|
||||||
|
|
20
src/output_modules/module_extended_file.h
Normal file
20
src/output_modules/module_extended_file.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <output_modules.h>
|
||||||
|
|
||||||
|
int extendedfile_init(struct state_conf *conf);
|
||||||
|
|
||||||
|
int extendedfile_ip(ipaddr_n_t saddr, ipaddr_n_t daddr,
|
||||||
|
port_n_t sport, port_n_t dport, struct timeval* t,
|
||||||
|
const char *response_type, int is_repeat,
|
||||||
|
int in_cooldown, const u_char *packet);
|
||||||
|
|
||||||
|
int extendedfile_close(struct state_conf* c, struct state_send* s,
|
||||||
|
struct state_recv* r);
|
||||||
|
|
96
src/output_modules/module_redis.c
Normal file
96
src/output_modules/module_redis.c
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "../../lib/zdlibc/logger.h"
|
||||||
|
#include "../../lib/zdlibc/redis.h"
|
||||||
|
|
||||||
|
#include "output_modules.h"
|
||||||
|
|
||||||
|
#define UNUSED __attribute__((unused))
|
||||||
|
|
||||||
|
typedef struct scannable_t {
|
||||||
|
in_addr_t ip_address;
|
||||||
|
uint8_t source;
|
||||||
|
} scannable_t;
|
||||||
|
|
||||||
|
#define QUEUE_NAME "zmap_results"
|
||||||
|
#define BUFFER_SIZE 500
|
||||||
|
#define SOURCE_ZMAP 0
|
||||||
|
|
||||||
|
static scannable_t* buffer;
|
||||||
|
static int buffer_fill = 0;
|
||||||
|
|
||||||
|
int redismodule_init(UNUSED struct state_conf *conf)
|
||||||
|
{
|
||||||
|
buffer = calloc(BUFFER_SIZE, sizeof(scannable_t));
|
||||||
|
assert(buffer);
|
||||||
|
buffer_fill = 0;
|
||||||
|
return redis_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
int redismodule_flush(void)
|
||||||
|
{
|
||||||
|
if (redis_lpush(QUEUE_NAME, buffer,
|
||||||
|
buffer_fill, sizeof(scannable_t))) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
buffer_fill = 0;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int redismodule_newip(ipaddr_n_t saddr, UNUSED ipaddr_n_t daddr,
|
||||||
|
UNUSED port_n_t sport, UNUSED port_n_t dport,
|
||||||
|
UNUSED const char *response_type, int is_repeat,
|
||||||
|
UNUSED int in_cooldown, UNUSED const u_char *packet)
|
||||||
|
{
|
||||||
|
if (!is_repeat) {
|
||||||
|
buffer[buffer_fill].ip_address = saddr;
|
||||||
|
buffer[buffer_fill].source = SOURCE_ZMAP;
|
||||||
|
|
||||||
|
if (++buffer_fill == BUFFER_SIZE) {
|
||||||
|
if (redismodule_flush()) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int redismodule_close(UNUSED struct state_conf* c,
|
||||||
|
UNUSED struct state_send* s,
|
||||||
|
UNUSED struct state_recv* r)
|
||||||
|
{
|
||||||
|
if (redismodule_flush()) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (redis_close()) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
output_module_t module_redis = {
|
||||||
|
.name = "redis",
|
||||||
|
.init = &redismodule_init,
|
||||||
|
.start = NULL,
|
||||||
|
.update = NULL,
|
||||||
|
.update_interval = 0,
|
||||||
|
.close = &redismodule_close,
|
||||||
|
.success_ip = &redismodule_newip,
|
||||||
|
.other_ip = NULL
|
||||||
|
};
|
||||||
|
|
19
src/output_modules/module_redis.h
Normal file
19
src/output_modules/module_redis.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <output_modules.h>
|
||||||
|
|
||||||
|
int redismodule_init(struct state_conf *conf);
|
||||||
|
|
||||||
|
int redismodule_newip(ipaddr_n_t saddr, ipaddr_n_t daddr,
|
||||||
|
port_n_t sport, port_n_t dport, struct timeval* t,
|
||||||
|
const char *response_type, int is_repeat,
|
||||||
|
int in_cooldown, const u_char *packet);
|
||||||
|
|
||||||
|
int redismodule_close(struct state_conf* c,
|
||||||
|
struct state_send* s, struct state_recv* r);
|
75
src/output_modules/module_simple_file.c
Normal file
75
src/output_modules/module_simple_file.c
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <logger.h>
|
||||||
|
|
||||||
|
#include "output_modules.h"
|
||||||
|
|
||||||
|
#define UNUSED __attribute__((unused))
|
||||||
|
|
||||||
|
static FILE *file = NULL;
|
||||||
|
|
||||||
|
int simplefile_init(struct state_conf *conf)
|
||||||
|
{
|
||||||
|
assert(conf);
|
||||||
|
if (conf->output_filename) {
|
||||||
|
if (!strcmp(conf->output_filename, "-")) {
|
||||||
|
file = stdout;
|
||||||
|
} else {
|
||||||
|
if (!(file = fopen(conf->output_filename, "w"))) {
|
||||||
|
perror("Couldn't open output file");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int simplefile_synack_newip(ipaddr_n_t saddr, UNUSED ipaddr_n_t daddr,
|
||||||
|
UNUSED const char *response_type,
|
||||||
|
int is_repeat, UNUSED int in_cooldown, UNUSED const u_char *packet,
|
||||||
|
UNUSED size_t buflen)
|
||||||
|
{
|
||||||
|
if (file && !is_repeat) {
|
||||||
|
struct in_addr addr;
|
||||||
|
addr.s_addr = saddr;
|
||||||
|
fprintf(file, "%s\n", inet_ntoa(addr));
|
||||||
|
}
|
||||||
|
fflush(file);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int simplefile_close(UNUSED struct state_conf* c,
|
||||||
|
UNUSED struct state_send* s,
|
||||||
|
UNUSED struct state_recv* r)
|
||||||
|
{
|
||||||
|
if (file) {
|
||||||
|
fflush(file);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
output_module_t module_simple_file = {
|
||||||
|
.name = "simple_file",
|
||||||
|
.init = &simplefile_init,
|
||||||
|
.start = NULL,
|
||||||
|
.update = NULL,
|
||||||
|
.update_interval = 0,
|
||||||
|
.close = &simplefile_close,
|
||||||
|
.success_ip = &simplefile_synack_newip,
|
||||||
|
.other_ip = NULL,
|
||||||
|
};
|
54
src/output_modules/output_modules.c
Normal file
54
src/output_modules/output_modules.c
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "output_modules.h"
|
||||||
|
|
||||||
|
extern output_module_t module_simple_file;
|
||||||
|
extern output_module_t module_extended_file;
|
||||||
|
// ADD YOUR MODULE HERE
|
||||||
|
|
||||||
|
#ifdef REDIS
|
||||||
|
extern output_module_t module_redis;
|
||||||
|
extern output_module_t module_ssldbfeed;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
output_module_t* output_modules[] = {
|
||||||
|
&module_simple_file,
|
||||||
|
&module_extended_file,
|
||||||
|
#ifdef REDIS
|
||||||
|
&module_redis,
|
||||||
|
&module_ssldbfeed,
|
||||||
|
#endif
|
||||||
|
// ADD YOUR MODULE HERE
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output_module_t* get_output_module_by_name(const char* name)
|
||||||
|
{
|
||||||
|
int num_modules = (int) (sizeof(output_modules)/sizeof(output_modules[0]));
|
||||||
|
for (int i=0; i < num_modules; i++) {
|
||||||
|
if (!strcmp(output_modules[i]->name, name)) {
|
||||||
|
return output_modules[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_output_modules(void)
|
||||||
|
{
|
||||||
|
int num_modules = (int) (sizeof(output_modules)/sizeof(output_modules[0]));
|
||||||
|
for (int i=0; i < num_modules; i++) {
|
||||||
|
printf("%s\n", output_modules[i]->name);
|
||||||
|
}
|
||||||
|
}
|
44
src/output_modules/output_modules.h
Normal file
44
src/output_modules/output_modules.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HEADER_OUTPUT_MODULES_H
|
||||||
|
#define HEADER_OUTPUT_MODULES_H
|
||||||
|
|
||||||
|
#include "../state.h"
|
||||||
|
|
||||||
|
// called at scanner initialization
|
||||||
|
typedef int (*output_init_cb)(struct state_conf *);
|
||||||
|
|
||||||
|
// called on packet receipt
|
||||||
|
typedef int (*output_packet_cb)(ipaddr_n_t src_ip, ipaddr_n_t dst_ip,
|
||||||
|
const char* response_type,
|
||||||
|
int is_repeat, int in_cooldown,
|
||||||
|
const u_char* packetbuf, size_t buflen);
|
||||||
|
|
||||||
|
// called periodically during the scan
|
||||||
|
typedef int (*output_update_cb)(struct state_conf*, struct state_send*, struct state_recv*);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct output_module {
|
||||||
|
const char *name;
|
||||||
|
unsigned update_interval;
|
||||||
|
|
||||||
|
output_init_cb init;
|
||||||
|
output_update_cb start;
|
||||||
|
output_update_cb update;
|
||||||
|
output_update_cb close;
|
||||||
|
output_packet_cb success_ip;
|
||||||
|
output_packet_cb other_ip;
|
||||||
|
|
||||||
|
} output_module_t;
|
||||||
|
|
||||||
|
|
||||||
|
output_module_t* get_output_module_by_name(const char*);
|
||||||
|
void print_output_modules(void);
|
||||||
|
|
||||||
|
#endif // HEADER_OUTPUT_MODULES_H
|
219
src/probe_modules/module_icmp_echo.c
Normal file
219
src/probe_modules/module_icmp_echo.c
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
// probe module for performing ICMP echo request (ping) scans
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <netinet/ether.h>
|
||||||
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/ip_icmp.h>
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "probe_modules.h"
|
||||||
|
#include "packet.h"
|
||||||
|
#include "validate.h"
|
||||||
|
|
||||||
|
probe_module_t module_icmp_echo;
|
||||||
|
|
||||||
|
int icmp_echo_init_perthread(void* buf, macaddr_t *src,
|
||||||
|
macaddr_t *gw, __attribute__((unused)) port_h_t dst_port)
|
||||||
|
{
|
||||||
|
memset(buf, 0, MAX_PACKET_SIZE);
|
||||||
|
|
||||||
|
struct ethhdr *eth_header = (struct ethhdr *)buf;
|
||||||
|
make_eth_header(eth_header, src, gw);
|
||||||
|
|
||||||
|
struct iphdr *ip_header = (struct iphdr*)(ð_header[1]);
|
||||||
|
uint16_t len = htons(sizeof(struct iphdr) + sizeof(struct icmp) - 8);
|
||||||
|
make_ip_header(ip_header, IPPROTO_ICMP, len);
|
||||||
|
|
||||||
|
struct icmp *icmp_header = (struct icmp*)(&ip_header[1]);
|
||||||
|
make_icmp_header(icmp_header);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int icmp_echo_make_packet(void *buf, ipaddr_n_t src_ip, ipaddr_n_t dst_ip,
|
||||||
|
uint32_t *validation, __attribute__((unused))int probe_num)
|
||||||
|
{
|
||||||
|
struct ethhdr *eth_header = (struct ethhdr *)buf;
|
||||||
|
struct iphdr *ip_header = (struct iphdr*)(ð_header[1]);
|
||||||
|
struct icmp *icmp_header = (struct icmp*)(&ip_header[1]);
|
||||||
|
uint16_t icmp_idnum = validation[2] & 0xFFFF;
|
||||||
|
|
||||||
|
ip_header->saddr = src_ip;
|
||||||
|
ip_header->daddr = dst_ip;
|
||||||
|
|
||||||
|
icmp_header->icmp_id = icmp_idnum;
|
||||||
|
icmp_header->icmp_cksum = 0;
|
||||||
|
icmp_header->icmp_cksum = icmp_checksum((unsigned short *) icmp_header);
|
||||||
|
|
||||||
|
ip_header->check = 0;
|
||||||
|
ip_header->check = ip_checksum((unsigned short *) ip_header);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void icmp_echo_print_packet(FILE *fp, void* packet)
|
||||||
|
{
|
||||||
|
struct ethhdr *ethh = (struct ethhdr *) packet;
|
||||||
|
struct iphdr *iph = (struct iphdr *) ðh[1];
|
||||||
|
struct icmp *icmp_header = (struct icmp*)(&iph[1]);
|
||||||
|
|
||||||
|
fprintf(fp, "icmp { type: %u | code: %u "
|
||||||
|
"| checksum: %u | id: %u | seq: %u }\n",
|
||||||
|
icmp_header->icmp_type,
|
||||||
|
icmp_header->icmp_code,
|
||||||
|
ntohs(icmp_header->icmp_cksum),
|
||||||
|
ntohs(icmp_header->icmp_id),
|
||||||
|
ntohs(icmp_header->icmp_seq));
|
||||||
|
struct in_addr *s = (struct in_addr *) &(iph->saddr);
|
||||||
|
struct in_addr *d = (struct in_addr *) &(iph->daddr);
|
||||||
|
char srcip[20];
|
||||||
|
char dstip[20];
|
||||||
|
// inet_ntoa is a const char * so we if just call it in
|
||||||
|
// fprintf, you'll get back wrong results since we're
|
||||||
|
// calling it twice.
|
||||||
|
strncpy(srcip, inet_ntoa(*s), 19);
|
||||||
|
strncpy(dstip, inet_ntoa(*d), 19);
|
||||||
|
fprintf(fp, "ip { saddr: %s | daddr: %s | checksum: %u }\n",
|
||||||
|
srcip,
|
||||||
|
dstip,
|
||||||
|
ntohl(iph->check));
|
||||||
|
fprintf(fp, "eth { shost: %02x:%02x:%02x:%02x:%02x:%02x | "
|
||||||
|
"dhost: %02x:%02x:%02x:%02x:%02x:%02x }\n",
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[0],
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[1],
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[2],
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[3],
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[4],
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[5],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[0],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[1],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[2],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[3],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[4],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[5]);
|
||||||
|
fprintf(fp, "------------------------------------------------------\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
response_type_t* icmp_echo_classify_packet(const u_char *packet, uint32_t len)
|
||||||
|
{
|
||||||
|
(void)len;
|
||||||
|
struct iphdr *ip_hdr = (struct iphdr *)&packet[sizeof(struct ethhdr)];
|
||||||
|
struct icmp *icmp_hdr = (struct icmp*)((char *)ip_hdr
|
||||||
|
+ sizeof(struct iphdr));
|
||||||
|
switch (icmp_hdr->icmp_type) {
|
||||||
|
case ICMP_ECHOREPLY:
|
||||||
|
return &(module_icmp_echo.responses[0]);
|
||||||
|
case ICMP_UNREACH:
|
||||||
|
return &(module_icmp_echo.responses[1]);
|
||||||
|
case ICMP_SOURCEQUENCH:
|
||||||
|
return &(module_icmp_echo.responses[2]);
|
||||||
|
case ICMP_REDIRECT:
|
||||||
|
return &(module_icmp_echo.responses[3]);
|
||||||
|
case ICMP_TIMXCEED:
|
||||||
|
return &(module_icmp_echo.responses[4]);
|
||||||
|
default:
|
||||||
|
return &(module_icmp_echo.responses[5]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int icmp_validate_packet(const struct iphdr *ip_hdr, uint32_t len, uint32_t *src_ip, uint32_t *validation)
|
||||||
|
{
|
||||||
|
if (ip_hdr->protocol != IPPROTO_ICMP) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((4*ip_hdr->ihl + sizeof(struct icmphdr)) > len) {
|
||||||
|
// buffer not large enough to contain expected icmp header
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct icmphdr *icmp_h = (struct icmphdr*)((char *)ip_hdr + 4*ip_hdr->ihl);
|
||||||
|
uint16_t icmp_idnum = icmp_h->un.echo.id;
|
||||||
|
|
||||||
|
// ICMP validation is tricky: for some packet types, we must look inside
|
||||||
|
// the payload
|
||||||
|
if (icmp_h->type == ICMP_TIME_EXCEEDED || icmp_h->type == ICMP_DEST_UNREACH) {
|
||||||
|
if ((4*ip_hdr->ihl + sizeof(struct icmphdr) +
|
||||||
|
sizeof(struct iphdr)) > len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
struct iphdr *ip_inner = (struct iphdr *)(icmp_h + 1);
|
||||||
|
if ((4*ip_hdr->ihl + sizeof(struct icmphdr) +
|
||||||
|
4*ip_inner->ihl + sizeof(struct icmphdr)) > len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
struct icmphdr *icmp_inner = (struct icmphdr*)((char *)ip_inner + 4 *ip_hdr->ihl);
|
||||||
|
|
||||||
|
// Regenerate validation and icmp id based off inner payload
|
||||||
|
icmp_idnum = icmp_inner->un.echo.id;
|
||||||
|
*src_ip = ip_inner->daddr;
|
||||||
|
validate_gen(ip_hdr->daddr, ip_inner->daddr, (uint8_t *)validation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate icmp id
|
||||||
|
if (icmp_idnum != (validation[2] & 0xFFFF)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static response_type_t responses[] = {
|
||||||
|
{
|
||||||
|
.name = "echoreply",
|
||||||
|
.is_success = 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "unreach",
|
||||||
|
.is_success = 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "sourcequench",
|
||||||
|
.is_success = 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "redirect",
|
||||||
|
.is_success = 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "timxceed",
|
||||||
|
.is_success = 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "other",
|
||||||
|
.is_success = 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
probe_module_t module_icmp_echo = {
|
||||||
|
.name = "icmp_echoscan",
|
||||||
|
.packet_length = 62,
|
||||||
|
.pcap_filter = "icmp and icmp[0]!=8",
|
||||||
|
.pcap_snaplen = 96,
|
||||||
|
.port_args = 0,
|
||||||
|
.thread_initialize = &icmp_echo_init_perthread,
|
||||||
|
.make_packet = &icmp_echo_make_packet,
|
||||||
|
.print_packet = &icmp_echo_print_packet,
|
||||||
|
.classify_packet = &icmp_echo_classify_packet,
|
||||||
|
.validate_packet = &icmp_validate_packet,
|
||||||
|
.close = NULL,
|
||||||
|
.responses = responses
|
||||||
|
};
|
||||||
|
|
196
src/probe_modules/module_tcp_synscan.c
Normal file
196
src/probe_modules/module_tcp_synscan.c
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
// probe module for performing TCP SYN scans
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <netinet/ether.h>
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "probe_modules.h"
|
||||||
|
#include "packet.h"
|
||||||
|
|
||||||
|
probe_module_t module_tcp_synscan;
|
||||||
|
uint32_t num_ports = 1;
|
||||||
|
|
||||||
|
int synscan_init_perthread(void* buf, macaddr_t *src,
|
||||||
|
macaddr_t *gw, port_h_t dst_port)
|
||||||
|
{
|
||||||
|
memset(buf, 0, MAX_PACKET_SIZE);
|
||||||
|
struct ethhdr *eth_header = (struct ethhdr *)buf;
|
||||||
|
make_eth_header(eth_header, src, gw);
|
||||||
|
struct iphdr *ip_header = (struct iphdr*)(ð_header[1]);
|
||||||
|
uint16_t len = htons(sizeof(struct iphdr) + sizeof(struct tcphdr));
|
||||||
|
make_ip_header(ip_header, IPPROTO_TCP, len);
|
||||||
|
struct tcphdr *tcp_header = (struct tcphdr*)(&ip_header[1]);
|
||||||
|
make_tcp_header(tcp_header, dst_port);
|
||||||
|
num_ports = zconf.source_port_last - zconf.source_port_first + 1;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int synscan_make_packet(void *buf, ipaddr_n_t src_ip, ipaddr_n_t dst_ip,
|
||||||
|
uint32_t *validation, int probe_num)
|
||||||
|
{
|
||||||
|
struct ethhdr *eth_header = (struct ethhdr *)buf;
|
||||||
|
struct iphdr *ip_header = (struct iphdr*)(ð_header[1]);
|
||||||
|
struct tcphdr *tcp_header = (struct tcphdr*)(&ip_header[1]);
|
||||||
|
uint16_t src_port = zconf.source_port_first
|
||||||
|
+ ((validation[1] + probe_num) % num_ports);
|
||||||
|
uint32_t tcp_seq = validation[0];
|
||||||
|
|
||||||
|
|
||||||
|
ip_header->saddr = src_ip;
|
||||||
|
ip_header->daddr = dst_ip;
|
||||||
|
|
||||||
|
tcp_header->source = htons(src_port);
|
||||||
|
tcp_header->seq = tcp_seq;
|
||||||
|
tcp_header->check = 0;
|
||||||
|
tcp_header->check = tcp_checksum(sizeof(struct tcphdr),
|
||||||
|
ip_header->saddr, ip_header->daddr, tcp_header);
|
||||||
|
|
||||||
|
ip_header->check = 0;
|
||||||
|
ip_header->check = ip_checksum((unsigned short *) ip_header);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void synscan_print_packet(FILE *fp, void* packet)
|
||||||
|
{
|
||||||
|
struct ethhdr *ethh = (struct ethhdr *) packet;
|
||||||
|
struct iphdr *iph = (struct iphdr *) ðh[1];
|
||||||
|
struct tcphdr *tcph = (struct tcphdr *) &iph[1];
|
||||||
|
fprintf(fp, "tcp { source: %u | dest: %u | seq: %u | checksum: %u }\n",
|
||||||
|
ntohs(tcph->source),
|
||||||
|
ntohs(tcph->dest),
|
||||||
|
ntohl(tcph->seq),
|
||||||
|
ntohl(tcph->check));
|
||||||
|
struct in_addr *s = (struct in_addr *) &(iph->saddr);
|
||||||
|
struct in_addr *d = (struct in_addr *) &(iph->daddr);
|
||||||
|
char srcip[20];
|
||||||
|
char dstip[20];
|
||||||
|
// inet_ntoa is a const char * so we if just call it in
|
||||||
|
// fprintf, you'll get back wrong results since we're
|
||||||
|
// calling it twice.
|
||||||
|
strncpy(srcip, inet_ntoa(*s), 19);
|
||||||
|
strncpy(dstip, inet_ntoa(*d), 19);
|
||||||
|
fprintf(fp, "ip { saddr: %s | daddr: %s | checksum: %u }\n",
|
||||||
|
srcip,
|
||||||
|
dstip,
|
||||||
|
ntohl(iph->check));
|
||||||
|
fprintf(fp, "eth { shost: %02x:%02x:%02x:%02x:%02x:%02x | "
|
||||||
|
"dhost: %02x:%02x:%02x:%02x:%02x:%02x }\n",
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[0],
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[1],
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[2],
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[3],
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[4],
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[5],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[0],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[1],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[2],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[3],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[4],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[5]);
|
||||||
|
fprintf(fp, "------------------------------------------------------\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
response_type_t* synscan_classify_packet(const u_char *packet, uint32_t len)
|
||||||
|
{
|
||||||
|
(void)len;
|
||||||
|
struct iphdr *ip_hdr = (struct iphdr *)&packet[sizeof(struct ethhdr)];
|
||||||
|
struct tcphdr *tcp = (struct tcphdr*)((char *)ip_hdr
|
||||||
|
+ (sizeof(struct iphdr)));
|
||||||
|
if (tcp->rst) { // RST packet
|
||||||
|
return &(module_tcp_synscan.responses[1]);
|
||||||
|
} else { // SYNACK packet
|
||||||
|
return &(module_tcp_synscan.responses[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Returns 0 if dst_port is outside the expected valid range, non-zero otherwise
|
||||||
|
static inline int check_dst_port(uint16_t port, uint32_t *validation)
|
||||||
|
{
|
||||||
|
if (port > zconf.source_port_last
|
||||||
|
|| port < zconf.source_port_first) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
int32_t to_validate = port - zconf.source_port_first;
|
||||||
|
int32_t min = validation[1] % num_ports;
|
||||||
|
int32_t max = (validation[1] + zconf.packet_streams - 1) % num_ports;
|
||||||
|
|
||||||
|
return (((max - min) % num_ports) >= ((to_validate - min) % num_ports));
|
||||||
|
}
|
||||||
|
|
||||||
|
int synscan_validate_packet(const struct iphdr *ip_hdr, uint32_t len,
|
||||||
|
__attribute__((unused))uint32_t *src_ip, uint32_t *validation)
|
||||||
|
{
|
||||||
|
if (ip_hdr->protocol != IPPROTO_TCP) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((4*ip_hdr->ihl + sizeof(struct tcphdr)) > len) {
|
||||||
|
// buffer not large enough to contain expected tcp header
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
struct tcphdr *tcp = (struct tcphdr*)((char *)ip_hdr + 4*ip_hdr->ihl);
|
||||||
|
uint16_t sport = tcp->source;
|
||||||
|
uint16_t dport = tcp->dest;
|
||||||
|
|
||||||
|
// validate source port
|
||||||
|
if (ntohs(sport) != zconf.target_port) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate destination port
|
||||||
|
if (!check_dst_port(ntohs(dport), validation)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate tcp acknowledgement number
|
||||||
|
if (htonl(tcp->ack_seq) != htonl(validation[0])+1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static response_type_t responses[] = {
|
||||||
|
{
|
||||||
|
.is_success = 1,
|
||||||
|
.name = "synack"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.is_success = 0,
|
||||||
|
.name = "rst"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
probe_module_t module_tcp_synscan = {
|
||||||
|
.name = "tcp_synscan",
|
||||||
|
.packet_length = 54,
|
||||||
|
.pcap_filter = "tcp && tcp[13] & 4 != 0 || tcp[13] == 18",
|
||||||
|
.pcap_snaplen = 96,
|
||||||
|
.port_args = 1,
|
||||||
|
.thread_initialize = &synscan_init_perthread,
|
||||||
|
.make_packet = &synscan_make_packet,
|
||||||
|
.print_packet = &synscan_print_packet,
|
||||||
|
.classify_packet = &synscan_classify_packet,
|
||||||
|
.validate_packet = &synscan_validate_packet,
|
||||||
|
.close = NULL,
|
||||||
|
.responses = responses,
|
||||||
|
};
|
||||||
|
|
227
src/probe_modules/module_udp.c
Normal file
227
src/probe_modules/module_udp.c
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* send module for performing TCP SYN scans */
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <netinet/udp.h>
|
||||||
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/ether.h>
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "probe_modules.h"
|
||||||
|
#include "packet.h"
|
||||||
|
|
||||||
|
const char *udp_send_msg = "GET / HTTP/1.1\r\n\r\n"; // Must be null-terminated
|
||||||
|
static int num_ports = 1;
|
||||||
|
|
||||||
|
probe_module_t module_udp;
|
||||||
|
|
||||||
|
int udp_init_perthread(void* buf, macaddr_t *src,
|
||||||
|
macaddr_t *gw, __attribute__((unused)) port_h_t dst_port)
|
||||||
|
{
|
||||||
|
memset(buf, 0, MAX_PACKET_SIZE);
|
||||||
|
struct ethhdr *eth_header = (struct ethhdr *)buf;
|
||||||
|
make_eth_header(eth_header, src, gw);
|
||||||
|
struct iphdr *ip_header = (struct iphdr*)(ð_header[1]);
|
||||||
|
uint16_t len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + strlen(udp_send_msg));
|
||||||
|
make_ip_header(ip_header, IPPROTO_UDP, len);
|
||||||
|
|
||||||
|
struct udphdr *udp_header = (struct udphdr*)(&ip_header[1]);
|
||||||
|
len = sizeof(struct udphdr) + strlen(udp_send_msg);
|
||||||
|
make_udp_header(udp_header, zconf.target_port, len);
|
||||||
|
|
||||||
|
char* payload = (char*)(&udp_header[1]);
|
||||||
|
|
||||||
|
module_udp.packet_length = sizeof(struct ethhdr) + sizeof(struct iphdr)
|
||||||
|
+ sizeof(struct udphdr) + strlen(udp_send_msg);
|
||||||
|
assert(module_udp.packet_length <= MAX_PACKET_SIZE);
|
||||||
|
|
||||||
|
strcpy(payload, udp_send_msg);
|
||||||
|
|
||||||
|
num_ports = zconf.source_port_last - zconf.source_port_first + 1;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int udp_make_packet(void *buf, ipaddr_n_t src_ip, ipaddr_n_t dst_ip,
|
||||||
|
uint32_t *validation, int probe_num)
|
||||||
|
{
|
||||||
|
struct ethhdr *eth_header = (struct ethhdr *)buf;
|
||||||
|
struct iphdr *ip_header = (struct iphdr*)(ð_header[1]);
|
||||||
|
struct udphdr *udp_header = (struct udphdr*)(&ip_header[1]);
|
||||||
|
uint16_t src_port = zconf.source_port_first
|
||||||
|
+ ((validation[1] + probe_num) % num_ports);
|
||||||
|
|
||||||
|
ip_header->saddr = src_ip;
|
||||||
|
ip_header->daddr = dst_ip;
|
||||||
|
udp_header->source = src_port;
|
||||||
|
|
||||||
|
ip_header->check = 0;
|
||||||
|
ip_header->check = ip_checksum((unsigned short *) ip_header);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void udp_print_packet(FILE *fp, void* packet)
|
||||||
|
{
|
||||||
|
struct ethhdr *ethh = (struct ethhdr *) packet;
|
||||||
|
struct iphdr *iph = (struct iphdr *) ðh[1];
|
||||||
|
struct udphdr *udph = (struct udphdr*)(&iph[1]);
|
||||||
|
fprintf(fp, "udp { source: %u | dest: %u | checksum: %u }\n",
|
||||||
|
ntohs(udph->source),
|
||||||
|
ntohs(udph->dest),
|
||||||
|
ntohl(udph->check));
|
||||||
|
//ip_header = (struct iphdr*)(ð_header[1])
|
||||||
|
struct in_addr *s = (struct in_addr *) &(iph->saddr);
|
||||||
|
struct in_addr *d = (struct in_addr *) &(iph->daddr);
|
||||||
|
char srcip[20];
|
||||||
|
char dstip[20];
|
||||||
|
// inet_ntoa is a const char * so we if just call it in
|
||||||
|
// fprintf, you'll get back wrong results since we're
|
||||||
|
// calling it twice.
|
||||||
|
strncpy(srcip, inet_ntoa(*s), 19);
|
||||||
|
strncpy(dstip, inet_ntoa(*d), 19);
|
||||||
|
fprintf(fp, "ip { saddr: %s | daddr: %s | checksum: %u }\n",
|
||||||
|
srcip,
|
||||||
|
dstip,
|
||||||
|
ntohl(iph->check));
|
||||||
|
fprintf(fp, "eth { shost: %02x:%02x:%02x:%02x:%02x:%02x | "
|
||||||
|
"dhost: %02x:%02x:%02x:%02x:%02x:%02x }\n",
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[0],
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[1],
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[2],
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[3],
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[4],
|
||||||
|
(int) ((unsigned char *) ethh->h_source)[5],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[0],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[1],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[2],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[3],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[4],
|
||||||
|
(int) ((unsigned char *) ethh->h_dest)[5]);
|
||||||
|
fprintf(fp, "------------------------------------------------------\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
response_type_t* udp_classify_packet(const u_char *packet, uint32_t len)
|
||||||
|
{
|
||||||
|
(void)len;
|
||||||
|
struct iphdr *ip_hdr = (struct iphdr *)&packet[sizeof(struct ethhdr)];
|
||||||
|
if (ip_hdr->protocol == IPPROTO_UDP) {
|
||||||
|
return &(module_udp.responses[0]);
|
||||||
|
} else if (ip_hdr->protocol == IPPROTO_ICMP) {
|
||||||
|
return &(module_udp.responses[1]);
|
||||||
|
} else {
|
||||||
|
return &(module_udp.responses[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 0 if dst_port is outside the expected valid range, non-zero otherwise
|
||||||
|
static inline int check_dst_port(uint16_t port, uint32_t *validation)
|
||||||
|
{
|
||||||
|
if (port > zconf.source_port_last
|
||||||
|
|| port < zconf.source_port_first) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
int32_t to_validate = port - zconf.source_port_first;
|
||||||
|
int32_t min = validation[1] % num_ports;
|
||||||
|
int32_t max = (validation[1] + zconf.packet_streams - 1) % num_ports;
|
||||||
|
|
||||||
|
return (((max - min) % num_ports) >= ((to_validate - min) % num_ports));
|
||||||
|
}
|
||||||
|
|
||||||
|
int udp_validate_packet(const struct iphdr *ip_hdr, uint32_t len,
|
||||||
|
__attribute__((unused))uint32_t *src_ip, uint32_t *validation)
|
||||||
|
{
|
||||||
|
uint16_t dport, sport;
|
||||||
|
if (ip_hdr->protocol == IPPROTO_UDP) {
|
||||||
|
if ((4*ip_hdr->ihl + sizeof(struct udphdr)) > len) {
|
||||||
|
// buffer not large enough to contain expected udp header
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
struct udphdr *udp = (struct udphdr*)((char *)ip_hdr + 4*ip_hdr->ihl);
|
||||||
|
|
||||||
|
sport = ntohs(udp->dest);
|
||||||
|
dport = ntohs(udp->source);
|
||||||
|
} else if (ip_hdr->protocol == IPPROTO_ICMP) {
|
||||||
|
// UDP can return ICMP Destination unreach
|
||||||
|
// IP( ICMP( IP( UDP ) ) ) for a destination unreach
|
||||||
|
uint32_t min_len = 4*ip_hdr->ihl + sizeof(struct icmphdr)
|
||||||
|
+ sizeof(struct iphdr) + sizeof(struct udphdr);
|
||||||
|
if (len < min_len) {
|
||||||
|
// Not enough information for us to validate
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct icmphdr *icmp = (struct icmphdr*)((char *)ip_hdr + 4*ip_hdr->ihl);
|
||||||
|
if (icmp->type != ICMP_DEST_UNREACH) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct iphdr *ip_inner = (struct iphdr*)&icmp[1];
|
||||||
|
// Now we know the actual inner ip length, we should recheck the buffer
|
||||||
|
if (len < 4*ip_inner->ihl - sizeof(struct iphdr) + min_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// This is the packet we sent
|
||||||
|
struct udphdr *udp = (struct udphdr *)((char*)ip_inner + 4*ip_inner->ihl);
|
||||||
|
|
||||||
|
sport = ntohs(udp->source);
|
||||||
|
dport = ntohs(udp->dest);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (dport != zconf.target_port) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!check_dst_port(sport, validation)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static response_type_t responses[] = {
|
||||||
|
{
|
||||||
|
.is_success = 1,
|
||||||
|
.name = "data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.is_success = 0,
|
||||||
|
.name = "port-unreach"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.is_success = 0,
|
||||||
|
.name = "invalid"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
probe_module_t module_udp = {
|
||||||
|
.name = "udp",
|
||||||
|
.packet_length = 96,
|
||||||
|
.pcap_filter = "udp || icmp",
|
||||||
|
.pcap_snaplen = 96,
|
||||||
|
.port_args = 1,
|
||||||
|
.thread_initialize = &udp_init_perthread,
|
||||||
|
.make_packet = &udp_make_packet,
|
||||||
|
.print_packet = &udp_print_packet,
|
||||||
|
.validate_packet = &udp_validate_packet,
|
||||||
|
.classify_packet = &udp_classify_packet,
|
||||||
|
.close = NULL,
|
||||||
|
.responses = responses
|
||||||
|
};
|
||||||
|
|
89
src/probe_modules/packet.c
Normal file
89
src/probe_modules/packet.c
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "packet.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <netinet/ether.h>
|
||||||
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <netinet/udp.h>
|
||||||
|
#include <netinet/ip_icmp.h>
|
||||||
|
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "state.h"
|
||||||
|
|
||||||
|
void print_macaddr(struct ifreq* i)
|
||||||
|
{
|
||||||
|
printf("Device %s -> Ethernet %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||||
|
i->ifr_name,
|
||||||
|
(int) ((unsigned char *) &i->ifr_hwaddr.sa_data)[0],
|
||||||
|
(int) ((unsigned char *) &i->ifr_hwaddr.sa_data)[1],
|
||||||
|
(int) ((unsigned char *) &i->ifr_hwaddr.sa_data)[2],
|
||||||
|
(int) ((unsigned char *) &i->ifr_hwaddr.sa_data)[3],
|
||||||
|
(int) ((unsigned char *) &i->ifr_hwaddr.sa_data)[4],
|
||||||
|
(int) ((unsigned char *) &i->ifr_hwaddr.sa_data)[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_eth_header(struct ethhdr *ethh, macaddr_t *src, macaddr_t *dst)
|
||||||
|
{
|
||||||
|
memcpy(ethh->h_source, src, ETH_ALEN);
|
||||||
|
memcpy(ethh->h_dest, dst, ETH_ALEN);
|
||||||
|
ethh->h_proto = htons(ETH_P_IP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_ip_header(struct iphdr *iph, uint8_t protocol, uint16_t len)
|
||||||
|
{
|
||||||
|
iph->ihl = 5; // Internet Header Length
|
||||||
|
iph->version = 4; // IPv4
|
||||||
|
iph->tos = 0; // Type of Service
|
||||||
|
iph->tot_len = len;
|
||||||
|
iph->id = htons(54321); // identification number
|
||||||
|
iph->frag_off = 0; //fragmentation falg
|
||||||
|
iph->ttl = MAXTTL; // time to live (TTL)
|
||||||
|
iph->protocol = protocol; // upper layer protocol => TCP
|
||||||
|
// we set the checksum = 0 for now because that's
|
||||||
|
// what it needs to be when we run the IP checksum
|
||||||
|
iph->check = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_icmp_header(struct icmp *buf)
|
||||||
|
{
|
||||||
|
buf->icmp_type = ICMP_ECHO;
|
||||||
|
buf->icmp_code = 0;
|
||||||
|
buf->icmp_seq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_tcp_header(struct tcphdr *tcp_header, port_h_t dest_port)
|
||||||
|
{
|
||||||
|
tcp_header->seq = random();
|
||||||
|
tcp_header->ack_seq = 0;
|
||||||
|
tcp_header->res2 = 0;
|
||||||
|
tcp_header->doff = 5; // data offset
|
||||||
|
tcp_header->syn = 1;
|
||||||
|
tcp_header->window = htons(65535); // largest possible window
|
||||||
|
tcp_header->check = 0;
|
||||||
|
tcp_header->urg_ptr = 0;
|
||||||
|
tcp_header->dest = htons(dest_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_udp_header(struct udphdr *udp_header, port_h_t dest_port,
|
||||||
|
uint16_t len)
|
||||||
|
{
|
||||||
|
udp_header->dest = htons(dest_port);
|
||||||
|
udp_header->len = htons(len);
|
||||||
|
// checksum ignored in IPv4 if 0
|
||||||
|
udp_header->check = 0;
|
||||||
|
}
|
||||||
|
|
82
src/probe_modules/packet.h
Normal file
82
src/probe_modules/packet.h
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#include "state.h"
|
||||||
|
|
||||||
|
#include <netinet/ether.h>
|
||||||
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <netinet/ip_icmp.h>
|
||||||
|
#include <netinet/udp.h>
|
||||||
|
|
||||||
|
#ifndef HEADER_ZMAP_PACKET_H
|
||||||
|
#define HEADER_ZMAP_PACKET_H
|
||||||
|
|
||||||
|
#define MAX_PACKET_SIZE 4096
|
||||||
|
|
||||||
|
typedef unsigned short __attribute__((__may_alias__)) alias_unsigned_short;
|
||||||
|
|
||||||
|
void make_eth_header(struct ethhdr *ethh, macaddr_t *src, macaddr_t *dst);
|
||||||
|
|
||||||
|
void make_ip_header(struct iphdr *iph, uint8_t, uint16_t);
|
||||||
|
void make_tcp_header(struct tcphdr*, port_h_t);
|
||||||
|
void make_icmp_header(struct icmp *);
|
||||||
|
void make_udp_header(struct udphdr *udp_header, port_h_t dest_port,
|
||||||
|
uint16_t len);
|
||||||
|
|
||||||
|
static inline unsigned short in_checksum(unsigned short *ip_pkt, int len)
|
||||||
|
{
|
||||||
|
unsigned long sum = 0;
|
||||||
|
for (int nwords = len/2; nwords > 0; nwords--) {
|
||||||
|
sum += *ip_pkt++;
|
||||||
|
}
|
||||||
|
sum = (sum >> 16) + (sum & 0xffff);
|
||||||
|
sum += (sum >> 16);
|
||||||
|
return (unsigned short) (~sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline unsigned short ip_checksum(
|
||||||
|
unsigned short *buf)
|
||||||
|
{
|
||||||
|
return in_checksum(buf, (int) sizeof(struct iphdr));
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline unsigned short icmp_checksum(
|
||||||
|
unsigned short *buf)
|
||||||
|
{
|
||||||
|
return in_checksum(buf, (int) sizeof(struct icmp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static __attribute__((unused)) uint16_t tcp_checksum(unsigned short len_tcp,
|
||||||
|
uint32_t saddr, uint32_t daddr, struct tcphdr *tcp_pkt)
|
||||||
|
{
|
||||||
|
alias_unsigned_short *src_addr = (alias_unsigned_short *) &saddr;
|
||||||
|
alias_unsigned_short *dest_addr = (alias_unsigned_short *) &daddr;
|
||||||
|
|
||||||
|
unsigned char prot_tcp = 6;
|
||||||
|
unsigned long sum = 0;
|
||||||
|
int nleft = len_tcp;
|
||||||
|
unsigned short *w;
|
||||||
|
|
||||||
|
w = (unsigned short *) tcp_pkt;
|
||||||
|
// calculate the checksum for the tcp header and tcp data
|
||||||
|
while(nleft > 1) {
|
||||||
|
sum += *w++;
|
||||||
|
nleft -= 2;
|
||||||
|
}
|
||||||
|
// if nleft is 1 there ist still on byte left.
|
||||||
|
// We add a padding byte (0xFF) to build a 16bit word
|
||||||
|
if (nleft > 0) {
|
||||||
|
sum += *w & ntohs(0xFF00);
|
||||||
|
}
|
||||||
|
// add the pseudo header
|
||||||
|
sum += src_addr[0];
|
||||||
|
sum += src_addr[1];
|
||||||
|
sum += dest_addr[0];
|
||||||
|
sum += dest_addr[1];
|
||||||
|
sum += htons(len_tcp);
|
||||||
|
sum += htons(prot_tcp);
|
||||||
|
sum = (sum >> 16) + (sum & 0xFFFF);
|
||||||
|
sum += (sum >> 16);
|
||||||
|
// Take the one's complement of sum
|
||||||
|
return (unsigned short) (~sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
42
src/probe_modules/probe_modules.c
Normal file
42
src/probe_modules/probe_modules.c
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "probe_modules.h"
|
||||||
|
|
||||||
|
extern probe_module_t module_tcp_synscan;
|
||||||
|
extern probe_module_t module_icmp_echo;
|
||||||
|
extern probe_module_t module_udp;
|
||||||
|
// ADD YOUR MODULE HERE
|
||||||
|
|
||||||
|
probe_module_t* probe_modules[] = {
|
||||||
|
&module_tcp_synscan,
|
||||||
|
&module_icmp_echo,
|
||||||
|
&module_udp
|
||||||
|
// ADD YOUR MODULE HERE
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
probe_module_t* get_probe_module_by_name(const char* name)
|
||||||
|
{
|
||||||
|
for (int i=0; i < (int) (sizeof(probe_modules)/sizeof(probe_modules[0])); i++) {
|
||||||
|
if (!strcmp(probe_modules[i]->name, name)) {
|
||||||
|
return probe_modules[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_probe_modules(void)
|
||||||
|
{
|
||||||
|
for (int i=0; i < (int) (sizeof(probe_modules)/sizeof(probe_modules[0])); i++) {
|
||||||
|
printf("%s\n", probe_modules[i]->name);
|
||||||
|
}
|
||||||
|
}
|
45
src/probe_modules/probe_modules.h
Normal file
45
src/probe_modules/probe_modules.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#include "../state.h"
|
||||||
|
|
||||||
|
#ifndef HEADER_PROBE_MODULES_H
|
||||||
|
#define HEADER_PROBE_MODULES_H
|
||||||
|
|
||||||
|
typedef struct probe_response_type {
|
||||||
|
const uint8_t is_success;
|
||||||
|
const char *name;
|
||||||
|
} response_type_t;
|
||||||
|
|
||||||
|
typedef int (*probe_global_init_cb)(struct state_conf *);
|
||||||
|
typedef int (*probe_thread_init_cb)(void* packetbuf, macaddr_t* src_mac, macaddr_t* gw_mac, port_n_t src_port);
|
||||||
|
typedef int (*probe_make_packet_cb)(void* packetbuf, ipaddr_n_t src_ip, ipaddr_n_t dst_ip,
|
||||||
|
uint32_t *validation, int probe_num);
|
||||||
|
typedef void (*probe_print_packet_cb)(FILE *, void* packetbuf);
|
||||||
|
typedef int (*probe_close_cb)(struct state_conf*, struct state_send*, struct state_recv*);
|
||||||
|
typedef response_type_t* (*probe_classify_packet_cb)(const u_char* packetbuf, uint32_t len);
|
||||||
|
typedef int (*probe_validate_packet_cb)(const struct iphdr *ip_hdr, uint32_t len, uint32_t *src_ip, uint32_t *validation);
|
||||||
|
|
||||||
|
typedef struct probe_module {
|
||||||
|
const char *name;
|
||||||
|
size_t packet_length;
|
||||||
|
const char *pcap_filter;
|
||||||
|
size_t pcap_snaplen;
|
||||||
|
|
||||||
|
// Should ZMap complain if the user hasn't specified valid
|
||||||
|
// source and target port numbers?
|
||||||
|
uint8_t port_args;
|
||||||
|
|
||||||
|
response_type_t *responses;
|
||||||
|
|
||||||
|
probe_global_init_cb global_initialize;
|
||||||
|
probe_thread_init_cb thread_initialize;
|
||||||
|
probe_make_packet_cb make_packet;
|
||||||
|
probe_print_packet_cb print_packet;
|
||||||
|
probe_validate_packet_cb validate_packet;
|
||||||
|
probe_classify_packet_cb classify_packet;
|
||||||
|
probe_close_cb close;
|
||||||
|
|
||||||
|
} probe_module_t;
|
||||||
|
|
||||||
|
probe_module_t* get_probe_module_by_name(const char*);
|
||||||
|
void print_probe_modules(void);
|
||||||
|
|
||||||
|
#endif // HEADER_PROBE_MODULES_H
|
195
src/recv.c
Normal file
195
src/recv.c
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include <pcap.h>
|
||||||
|
#include <pcap/pcap.h>
|
||||||
|
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <netinet/ip_icmp.h>
|
||||||
|
#include <netinet/udp.h>
|
||||||
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
|
||||||
|
#include "../lib/logger.h"
|
||||||
|
|
||||||
|
#include "state.h"
|
||||||
|
#include "validate.h"
|
||||||
|
#include "probe_modules/probe_modules.h"
|
||||||
|
#include "output_modules/output_modules.h"
|
||||||
|
|
||||||
|
#define PCAP_PROMISC 1
|
||||||
|
#define PCAP_TIMEOUT 1000
|
||||||
|
|
||||||
|
static uint32_t num_src_ports;
|
||||||
|
static pcap_t *pc = NULL;
|
||||||
|
|
||||||
|
// bitmap of observed IP addresses
|
||||||
|
static uint64_t *ip_seen = NULL;
|
||||||
|
static const int IP_SEEN_SIZE = 0x4000000; // == 2^32/64
|
||||||
|
|
||||||
|
// check if we've received a response from this address previously
|
||||||
|
static inline int check_ip(uint32_t ip)
|
||||||
|
{
|
||||||
|
return (ip_seen[ip >> 6] >> (ip & 0x3F)) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set that we've received a response from the address
|
||||||
|
static inline void set_ip(uint32_t ip)
|
||||||
|
{
|
||||||
|
ip_seen[ip >> 6] |= (uint64_t)1 << (ip & 0x3F);
|
||||||
|
}
|
||||||
|
|
||||||
|
void packet_cb(u_char __attribute__((__unused__)) *user,
|
||||||
|
const struct pcap_pkthdr *p, const u_char *bytes)
|
||||||
|
{
|
||||||
|
if (!p) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (zrecv.success_unique >= zconf.max_results) {
|
||||||
|
// Libpcap can process multiple packets per pcap_dispatch;
|
||||||
|
// we need to throw out results once we've
|
||||||
|
// gotten our --max-results worth.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// length of entire packet captured by libpcap
|
||||||
|
uint32_t buflen = (uint32_t) p->caplen;
|
||||||
|
|
||||||
|
if ((sizeof(struct iphdr) + sizeof(struct ethhdr)) > buflen) {
|
||||||
|
// buffer not large enough to contain ethernet
|
||||||
|
// and ip headers. further action would overrun buf
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct iphdr *ip_hdr = (struct iphdr *)&bytes[sizeof(struct ethhdr)];
|
||||||
|
|
||||||
|
uint32_t src_ip = ip_hdr->saddr;
|
||||||
|
|
||||||
|
uint32_t validation[VALIDATE_BYTES/sizeof(uint8_t)];
|
||||||
|
// TODO: for TTL exceeded messages, ip_hdr->saddr is going to be different
|
||||||
|
// and we must calculate off potential payload message instead
|
||||||
|
validate_gen(ip_hdr->daddr, ip_hdr->saddr, (uint8_t *)validation);
|
||||||
|
|
||||||
|
if (!zconf.probe_module->validate_packet(ip_hdr, buflen - sizeof(struct ethhdr),
|
||||||
|
&src_ip, validation)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_repeat = check_ip(src_ip);
|
||||||
|
response_type_t *r = zconf.probe_module->classify_packet(bytes, buflen);
|
||||||
|
|
||||||
|
if (r->is_success) {
|
||||||
|
zrecv.success_total++;
|
||||||
|
if (!is_repeat) {
|
||||||
|
zrecv.success_unique++;
|
||||||
|
set_ip(src_ip);
|
||||||
|
}
|
||||||
|
if (zsend.complete) {
|
||||||
|
zrecv.cooldown_total++;
|
||||||
|
if (!is_repeat) {
|
||||||
|
zrecv.cooldown_unique++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (zconf.output_module && zconf.output_module->success_ip) {
|
||||||
|
zconf.output_module->success_ip(
|
||||||
|
ip_hdr->saddr, ip_hdr->daddr,
|
||||||
|
r->name, is_repeat, zsend.complete, bytes, buflen);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
zrecv.failure_total++;
|
||||||
|
if (zconf.output_module && zconf.output_module->other_ip) {
|
||||||
|
zconf.output_module->other_ip(
|
||||||
|
ip_hdr->saddr, ip_hdr->daddr,
|
||||||
|
r->name, is_repeat, zsend.complete, bytes, buflen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zconf.output_module && zconf.output_module->update
|
||||||
|
&& !(zrecv.success_unique % zconf.output_module->update_interval)) {
|
||||||
|
zconf.output_module->update(&zconf, &zsend, &zrecv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int recv_update_pcap_stats(void)
|
||||||
|
{
|
||||||
|
if (!pc) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
struct pcap_stat pcst;
|
||||||
|
if (pcap_stats(pc, &pcst)) {
|
||||||
|
log_error("recv", "unable to retrieve pcap statistics: %s",
|
||||||
|
pcap_geterr(pc));
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else {
|
||||||
|
zrecv.pcap_recv = pcst.ps_recv;
|
||||||
|
zrecv.pcap_drop = pcst.ps_drop;
|
||||||
|
zrecv.pcap_ifdrop = pcst.ps_ifdrop;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int recv_run(pthread_mutex_t *recv_ready_mutex)
|
||||||
|
{
|
||||||
|
log_debug("recv", "thread started");
|
||||||
|
num_src_ports = zconf.source_port_last - zconf.source_port_first + 1;
|
||||||
|
ip_seen = calloc(IP_SEEN_SIZE, sizeof(uint64_t));
|
||||||
|
if (!ip_seen) {
|
||||||
|
log_fatal("recv", "couldn't allocate address bitmap");
|
||||||
|
}
|
||||||
|
log_debug("recv", "using dev %s", zconf.iface);
|
||||||
|
char errbuf[PCAP_ERRBUF_SIZE];
|
||||||
|
pc = pcap_open_live(zconf.iface, zconf.probe_module->pcap_snaplen,
|
||||||
|
PCAP_PROMISC, PCAP_TIMEOUT, errbuf);
|
||||||
|
if (pc == NULL) {
|
||||||
|
log_fatal("recv", "couldn't open device %s: %s",
|
||||||
|
zconf.iface, errbuf);
|
||||||
|
}
|
||||||
|
struct bpf_program bpf;
|
||||||
|
if (pcap_compile(pc, &bpf, zconf.probe_module->pcap_filter, 1, 0) < 0) {
|
||||||
|
log_fatal("recv", "couldn't compile filter");
|
||||||
|
}
|
||||||
|
if (pcap_setfilter(pc, &bpf) < 0) {
|
||||||
|
log_fatal("recv", "couldn't install filter");
|
||||||
|
}
|
||||||
|
log_debug("recv", "receiver ready");
|
||||||
|
pthread_mutex_lock(recv_ready_mutex);
|
||||||
|
zconf.recv_ready = 1;
|
||||||
|
pthread_mutex_unlock(recv_ready_mutex);
|
||||||
|
zrecv.start = now();
|
||||||
|
if (zconf.max_results == 0) {
|
||||||
|
zconf.max_results = -1;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
if (pcap_dispatch(pc, 0, packet_cb, NULL) == -1) {
|
||||||
|
log_fatal("recv", "pcap_dispatch error");
|
||||||
|
}
|
||||||
|
if (zconf.max_results && zrecv.success_unique >= zconf.max_results) {
|
||||||
|
zsend.complete = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (!(zsend.complete && (now()-zsend.finish > zconf.cooldown_secs)));
|
||||||
|
zrecv.finish = now();
|
||||||
|
// get final pcap statistics before closing
|
||||||
|
recv_update_pcap_stats();
|
||||||
|
pcap_close(pc);
|
||||||
|
zrecv.complete = 1;
|
||||||
|
log_debug("recv", "thread finished");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
16
src/recv.h
Normal file
16
src/recv.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _RECV_H
|
||||||
|
#define _RECV_H
|
||||||
|
|
||||||
|
int recv_update_pcap_stats(void);
|
||||||
|
|
||||||
|
int recv_run();
|
||||||
|
|
||||||
|
#endif //_RECV_H
|
295
src/send.c
Normal file
295
src/send.c
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "send.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <linux/if_packet.h>
|
||||||
|
|
||||||
|
#include "../lib/logger.h"
|
||||||
|
#include "../lib/random.h"
|
||||||
|
#include "../lib/blacklist.h"
|
||||||
|
|
||||||
|
#include "cyclic.h"
|
||||||
|
#include "state.h"
|
||||||
|
#include "probe_modules/packet.h"
|
||||||
|
#include "probe_modules/probe_modules.h"
|
||||||
|
#include "validate.h"
|
||||||
|
|
||||||
|
|
||||||
|
// lock to manage access to share send state (e.g. counters and cyclic)
|
||||||
|
pthread_mutex_t send_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
// lock to provide thread safety to the user provided send callback
|
||||||
|
pthread_mutex_t syncb_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
// globals to handle sending from multiple ip addresses (shared across threads)
|
||||||
|
static uint16_t num_src_ports;
|
||||||
|
static uint32_t num_addrs;
|
||||||
|
static in_addr_t srcip_first;
|
||||||
|
static in_addr_t srcip_last;
|
||||||
|
// offset send addresses according to a random chosen per scan execution
|
||||||
|
// in order to help prevent cross-scan interference
|
||||||
|
static uint32_t srcip_offset;
|
||||||
|
|
||||||
|
// global sender initialize (not thread specific)
|
||||||
|
int send_init(void)
|
||||||
|
{
|
||||||
|
// generate a new primitive root and starting position
|
||||||
|
cyclic_init(0, 0);
|
||||||
|
zsend.first_scanned = cyclic_get_curr_ip();
|
||||||
|
|
||||||
|
// compute number of targets
|
||||||
|
uint64_t allowed = blacklist_count_allowed();
|
||||||
|
if (allowed == (1LL << 32)) {
|
||||||
|
zsend.targets = 0xFFFFFFFF;
|
||||||
|
} else {
|
||||||
|
zsend.targets = allowed;
|
||||||
|
}
|
||||||
|
if (zsend.targets > zconf.max_targets) {
|
||||||
|
zsend.targets = zconf.max_targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process the dotted-notation addresses passed to ZMAP and determine
|
||||||
|
// the source addresses from which we'll send packets;
|
||||||
|
srcip_first = inet_addr(zconf.source_ip_first);
|
||||||
|
if (srcip_first == INADDR_NONE) {
|
||||||
|
log_fatal("send", "invalid begin source ip address: `%s'",
|
||||||
|
zconf.source_ip_first);
|
||||||
|
}
|
||||||
|
srcip_last = inet_addr(zconf.source_ip_last);
|
||||||
|
if (srcip_last == INADDR_NONE) {
|
||||||
|
log_fatal("send", "invalid end source ip address: `%s'",
|
||||||
|
zconf.source_ip_last);
|
||||||
|
}
|
||||||
|
if (srcip_first == srcip_last) {
|
||||||
|
srcip_offset = 0;
|
||||||
|
num_addrs = 1;
|
||||||
|
} else {
|
||||||
|
srcip_offset = rand() % (srcip_last - srcip_first);
|
||||||
|
num_addrs = ntohl(srcip_last) - ntohl(srcip_first) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process the source port range that ZMap is allowed to use
|
||||||
|
num_src_ports = zconf.source_port_last - zconf.source_port_first + 1;
|
||||||
|
log_debug("send", "will send from %i address%s on %u source ports",
|
||||||
|
num_addrs, ((num_addrs==1)?"":"es"), num_src_ports);
|
||||||
|
|
||||||
|
// global initialization for send module
|
||||||
|
assert(zconf.probe_module);
|
||||||
|
if (zconf.probe_module->global_initialize) {
|
||||||
|
zconf.probe_module->global_initialize(&zconf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// concert specified bandwidth to packet rate
|
||||||
|
if (zconf.bandwidth > 0) {
|
||||||
|
int pkt_len = zconf.probe_module->packet_length;
|
||||||
|
pkt_len *= 8;
|
||||||
|
pkt_len += 8*24; // 7 byte MAC preamble, 1 byte Start frame,
|
||||||
|
// 4 byte CRC, 12 byte inter-frame gap
|
||||||
|
if (pkt_len < 84*8) {
|
||||||
|
pkt_len = 84*8;
|
||||||
|
}
|
||||||
|
if (zconf.bandwidth / pkt_len > 0xFFFFFFFF) {
|
||||||
|
zconf.rate = 0;
|
||||||
|
} else {
|
||||||
|
zconf.rate = zconf.bandwidth / pkt_len;
|
||||||
|
if (zconf.rate == 0) {
|
||||||
|
log_warn("send", "bandwidth %lu bit/s is slower than 1 pkt/s, "
|
||||||
|
"setting rate to 1 pkt/s", zconf.bandwidth);
|
||||||
|
zconf.rate = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_debug("send", "using bandwidth %lu bits/s, rate set to %d pkt/s",
|
||||||
|
zconf.bandwidth, zconf.rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zconf.dryrun) {
|
||||||
|
log_info("send", "dryrun mode -- won't actually send packets");
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize random validation key
|
||||||
|
validate_init();
|
||||||
|
|
||||||
|
zsend.start = now();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_socket(void)
|
||||||
|
{
|
||||||
|
int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
|
||||||
|
if (sock <= 0) {
|
||||||
|
log_fatal("send", "couldn't create socket. "
|
||||||
|
"Are you root? Error: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ipaddr_n_t get_src_ip(ipaddr_n_t dst, int local_offset)
|
||||||
|
{
|
||||||
|
if (srcip_first == srcip_last) {
|
||||||
|
return srcip_first;
|
||||||
|
}
|
||||||
|
return htonl(((ntohl(dst) + srcip_offset + local_offset)
|
||||||
|
% num_addrs)) + srcip_first;
|
||||||
|
}
|
||||||
|
|
||||||
|
// one sender thread
|
||||||
|
int send_run(void)
|
||||||
|
{
|
||||||
|
log_debug("send", "thread started");
|
||||||
|
pthread_mutex_lock(&send_mutex);
|
||||||
|
int sock = get_socket();
|
||||||
|
struct sockaddr_ll sockaddr;
|
||||||
|
// get source interface index
|
||||||
|
struct ifreq if_idx;
|
||||||
|
memset(&if_idx, 0, sizeof(struct ifreq));
|
||||||
|
if (strlen(zconf.iface) >= IFNAMSIZ) {
|
||||||
|
log_error("send", "device interface name (%s) too long\n",
|
||||||
|
zconf.iface);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
strncpy(if_idx.ifr_name, zconf.iface, IFNAMSIZ-1);
|
||||||
|
if (ioctl(sock, SIOCGIFINDEX, &if_idx) < 0) {
|
||||||
|
perror("SIOCGIFINDEX");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int ifindex = if_idx.ifr_ifindex;
|
||||||
|
// get source interface mac
|
||||||
|
struct ifreq if_mac;
|
||||||
|
memset(&if_mac, 0, sizeof(struct ifreq));
|
||||||
|
strncpy(if_mac.ifr_name, zconf.iface, IFNAMSIZ-1);
|
||||||
|
if (ioctl(sock, SIOCGIFHWADDR, &if_mac) < 0) {
|
||||||
|
perror("SIOCGIFHWADDR");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// find source IP address associated with the dev from which we're sending.
|
||||||
|
// while we won't use this address for sending packets, we need the address
|
||||||
|
// to set certain socket options and it's easiest to just use the primary
|
||||||
|
// address the OS believes is associated.
|
||||||
|
struct ifreq if_ip;
|
||||||
|
memset(&if_ip, 0, sizeof(struct ifreq));
|
||||||
|
strncpy(if_ip.ifr_name, zconf.iface, IFNAMSIZ-1);
|
||||||
|
if (ioctl(sock, SIOCGIFADDR, &if_ip) < 0) {
|
||||||
|
perror("SIOCGIFADDR");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// destination address for the socket
|
||||||
|
memset((void*) &sockaddr, 0, sizeof(struct sockaddr_ll));
|
||||||
|
sockaddr.sll_ifindex = ifindex;
|
||||||
|
sockaddr.sll_halen = ETH_ALEN;
|
||||||
|
memcpy(sockaddr.sll_addr, zconf.gw_mac, ETH_ALEN);
|
||||||
|
|
||||||
|
char buf[MAX_PACKET_SIZE];
|
||||||
|
memset(buf, 0, MAX_PACKET_SIZE);
|
||||||
|
zconf.probe_module->thread_initialize(buf,
|
||||||
|
(unsigned char *)if_mac.ifr_hwaddr.sa_data,
|
||||||
|
zconf.gw_mac, zconf.target_port);
|
||||||
|
pthread_mutex_unlock(&send_mutex);
|
||||||
|
|
||||||
|
// adaptive timing to hit target rate
|
||||||
|
uint32_t count = 0;
|
||||||
|
uint32_t last_count = count;
|
||||||
|
double last_time = now();
|
||||||
|
uint32_t delay = 0;
|
||||||
|
int interval = 0;
|
||||||
|
volatile int vi;
|
||||||
|
if (zconf.rate > 0) {
|
||||||
|
// estimate initial rate
|
||||||
|
delay = 10000;
|
||||||
|
for (vi = delay; vi--; )
|
||||||
|
;
|
||||||
|
delay *= 1 / (now() - last_time) / (zconf.rate / zconf.senders);
|
||||||
|
interval = (zconf.rate / zconf.senders) / 20;
|
||||||
|
last_time = now();
|
||||||
|
}
|
||||||
|
while (1) {
|
||||||
|
// adaptive timing delay
|
||||||
|
if (delay > 0) {
|
||||||
|
count++;
|
||||||
|
for (vi = delay; vi--; )
|
||||||
|
;
|
||||||
|
if (!interval || (count % interval == 0)) {
|
||||||
|
double t = now();
|
||||||
|
delay *= (double)(count - last_count)
|
||||||
|
/ (t - last_time) / (zconf.rate / zconf.senders);
|
||||||
|
if (delay < 1)
|
||||||
|
delay = 1;
|
||||||
|
last_count = count;
|
||||||
|
last_time = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// generate next ip from cyclic group and update global state
|
||||||
|
// (everything locked happens here)
|
||||||
|
pthread_mutex_lock(&send_mutex);
|
||||||
|
if (zsend.complete) {
|
||||||
|
pthread_mutex_unlock(&send_mutex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (zsend.sent >= zconf.max_targets) {
|
||||||
|
zsend.complete = 1;
|
||||||
|
zsend.finish = now();
|
||||||
|
pthread_mutex_unlock(&send_mutex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (zconf.max_runtime && zconf.max_runtime <= now() - zsend.start) {
|
||||||
|
zsend.complete = 1;
|
||||||
|
zsend.finish = now();
|
||||||
|
pthread_mutex_unlock(&send_mutex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
uint32_t curr = cyclic_get_next_ip();
|
||||||
|
if (curr == zsend.first_scanned) {
|
||||||
|
zsend.complete = 1;
|
||||||
|
zsend.finish = now();
|
||||||
|
}
|
||||||
|
zsend.sent++;
|
||||||
|
pthread_mutex_unlock(&send_mutex);
|
||||||
|
for (int i=0; i < zconf.packet_streams; i++) {
|
||||||
|
uint32_t src_ip = get_src_ip(curr, i);
|
||||||
|
|
||||||
|
uint32_t validation[VALIDATE_BYTES/sizeof(uint32_t)];
|
||||||
|
validate_gen(src_ip, curr, (uint8_t *)validation);
|
||||||
|
zconf.probe_module->make_packet(buf, src_ip, curr, validation, i);
|
||||||
|
|
||||||
|
if (zconf.dryrun) {
|
||||||
|
zconf.probe_module->print_packet(stdout, buf);
|
||||||
|
} else {
|
||||||
|
int l = zconf.probe_module->packet_length;
|
||||||
|
int rc = sendto(sock, buf,
|
||||||
|
l, 0,
|
||||||
|
(struct sockaddr *)&sockaddr,
|
||||||
|
sizeof(struct sockaddr_ll));
|
||||||
|
if (rc < 0) {
|
||||||
|
struct in_addr addr;
|
||||||
|
addr.s_addr = curr;
|
||||||
|
log_debug("send", "sendto failed for %s. %s",
|
||||||
|
inet_ntoa(addr), strerror(errno));
|
||||||
|
pthread_mutex_lock(&send_mutex);
|
||||||
|
zsend.sendto_failures++;
|
||||||
|
pthread_mutex_unlock(&send_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_debug("send", "thread finished");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
15
src/send.h
Normal file
15
src/send.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SEND_H
|
||||||
|
#define _SEND_H
|
||||||
|
|
||||||
|
int send_init(void);
|
||||||
|
int send_run(void);
|
||||||
|
|
||||||
|
#endif //_SEND_H
|
70
src/state.c
Normal file
70
src/state.c
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "state.h"
|
||||||
|
#include "../lib/logger.h"
|
||||||
|
|
||||||
|
// global configuration and defaults
|
||||||
|
struct state_conf zconf = {
|
||||||
|
.log_level = LOG_INFO,
|
||||||
|
.source_port_first = 32768, // (these are the default
|
||||||
|
.source_port_last = 61000, // ephemeral range on Linux)
|
||||||
|
.output_filename = NULL,
|
||||||
|
.blacklist_filename = NULL,
|
||||||
|
.whitelist_filename = NULL,
|
||||||
|
.target_port = 0,
|
||||||
|
.max_targets = 0xFFFFFFFF,
|
||||||
|
.max_runtime = 0,
|
||||||
|
.max_results = 0,
|
||||||
|
.iface = NULL,
|
||||||
|
.rate = 0,
|
||||||
|
.bandwidth = 0,
|
||||||
|
.cooldown_secs = 0,
|
||||||
|
.senders = 1,
|
||||||
|
.packet_streams = 1,
|
||||||
|
.use_seed = 0,
|
||||||
|
.seed = 0,
|
||||||
|
.output_module = NULL,
|
||||||
|
.output_args = NULL,
|
||||||
|
.probe_module = NULL,
|
||||||
|
.probe_args = NULL,
|
||||||
|
.gw_mac = {0},
|
||||||
|
.gw_mac_set = 0,
|
||||||
|
.source_ip_first = NULL,
|
||||||
|
.source_ip_last = NULL,
|
||||||
|
.dryrun = 0,
|
||||||
|
.quiet = 0,
|
||||||
|
.summary = 0,
|
||||||
|
.recv_ready = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// global sender stats and defaults
|
||||||
|
struct state_send zsend = {
|
||||||
|
.start = 0.0,
|
||||||
|
.finish = 0.0,
|
||||||
|
.sent = 0,
|
||||||
|
.blacklisted = 0,
|
||||||
|
.complete = 0,
|
||||||
|
.sendto_failures = 0,
|
||||||
|
.targets = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// global receiver stats and defaults
|
||||||
|
struct state_recv zrecv = {
|
||||||
|
.success_unique = 0,
|
||||||
|
.success_total = 0,
|
||||||
|
.cooldown_unique = 0,
|
||||||
|
.cooldown_total = 0,
|
||||||
|
.failure_total = 0,
|
||||||
|
.complete = 0,
|
||||||
|
.pcap_recv = 0,
|
||||||
|
.pcap_drop = 0,
|
||||||
|
.pcap_ifdrop = 0,
|
||||||
|
};
|
||||||
|
|
124
src/state.h
Normal file
124
src/state.h
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <netinet/ether.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
|
||||||
|
#ifndef _STATE_H
|
||||||
|
#define _STATE_H
|
||||||
|
|
||||||
|
#define MAX_PACKET_SIZE 4096
|
||||||
|
|
||||||
|
typedef uint32_t ipaddr_n_t; // IPv4 address network order
|
||||||
|
typedef uint32_t ipaddr_h_t; // IPv4 address host order
|
||||||
|
typedef uint16_t port_n_t; // port network order
|
||||||
|
typedef uint16_t port_h_t; // port host order
|
||||||
|
typedef unsigned char macaddr_t;
|
||||||
|
|
||||||
|
struct probe_module;
|
||||||
|
struct output_module;
|
||||||
|
|
||||||
|
// global configuration
|
||||||
|
struct state_conf {
|
||||||
|
int log_level;
|
||||||
|
port_h_t target_port;
|
||||||
|
port_h_t source_port_first;
|
||||||
|
port_h_t source_port_last;
|
||||||
|
// maximum number of packets that the scanner will send before terminating
|
||||||
|
uint32_t max_targets;
|
||||||
|
// maximum number of seconds that scanner will run before terminating
|
||||||
|
uint32_t max_runtime;
|
||||||
|
// maximum number of results before terminating
|
||||||
|
uint32_t max_results;
|
||||||
|
// name of network interface that
|
||||||
|
// will be utilized for sending/receiving
|
||||||
|
char *iface;
|
||||||
|
// rate in packets per second
|
||||||
|
// that the sender will maintain
|
||||||
|
int rate;
|
||||||
|
// rate in bits per second
|
||||||
|
uint64_t bandwidth;
|
||||||
|
// how many seconds after the termination of the sender will the receiver
|
||||||
|
// continue to process responses
|
||||||
|
int cooldown_secs;
|
||||||
|
// number of sending threads
|
||||||
|
int senders;
|
||||||
|
// should use CLI provided randomization seed instead of generating
|
||||||
|
// a random seed.
|
||||||
|
int use_seed;
|
||||||
|
uint32_t seed;
|
||||||
|
// generator of the cyclic multiplicative group that is utilized for
|
||||||
|
// address generation
|
||||||
|
uint32_t generator;
|
||||||
|
int packet_streams;
|
||||||
|
struct probe_module *probe_module;
|
||||||
|
struct output_module *output_module;
|
||||||
|
char *probe_args;
|
||||||
|
char *output_args;
|
||||||
|
macaddr_t gw_mac[IFHWADDRLEN];
|
||||||
|
int gw_mac_set;
|
||||||
|
char *source_ip_first;
|
||||||
|
char *source_ip_last;
|
||||||
|
char *output_filename;
|
||||||
|
char *blacklist_filename;
|
||||||
|
char *whitelist_filename;
|
||||||
|
int dryrun;
|
||||||
|
int summary;
|
||||||
|
int quiet;
|
||||||
|
int recv_ready;
|
||||||
|
};
|
||||||
|
extern struct state_conf zconf;
|
||||||
|
|
||||||
|
// global sender stats
|
||||||
|
struct state_send {
|
||||||
|
double start;
|
||||||
|
double finish;
|
||||||
|
uint32_t sent;
|
||||||
|
uint32_t blacklisted;
|
||||||
|
int complete;
|
||||||
|
uint32_t first_scanned;
|
||||||
|
uint32_t targets;
|
||||||
|
uint32_t sendto_failures;
|
||||||
|
};
|
||||||
|
extern struct state_send zsend;
|
||||||
|
|
||||||
|
// global receiver stats
|
||||||
|
struct state_recv {
|
||||||
|
// valid responses classified as "success"
|
||||||
|
uint32_t success_total;
|
||||||
|
// unique IPs that sent valid responses classified as "success"
|
||||||
|
uint32_t success_unique;
|
||||||
|
// valid responses classified as "success" received during cooldown
|
||||||
|
uint32_t cooldown_total;
|
||||||
|
// unique IPs that first sent valid "success"es during cooldown
|
||||||
|
uint32_t cooldown_unique;
|
||||||
|
// valid responses NOT classified as "success"
|
||||||
|
uint32_t failure_total;
|
||||||
|
|
||||||
|
int complete; // has the scanner finished sending?
|
||||||
|
double start; // timestamp of when recv started
|
||||||
|
double finish; // timestamp of when recv terminated
|
||||||
|
|
||||||
|
// number of packets captured by pcap filter
|
||||||
|
uint32_t pcap_recv;
|
||||||
|
// number of packets dropped because there was no room in
|
||||||
|
// the operating system's buffer when they arrived, because
|
||||||
|
// packets weren't being read fast enough
|
||||||
|
uint32_t pcap_drop;
|
||||||
|
// number of packets dropped by the network interface or its driver.
|
||||||
|
uint32_t pcap_ifdrop;
|
||||||
|
|
||||||
|
};
|
||||||
|
extern struct state_recv zrecv;
|
||||||
|
|
||||||
|
#endif // _STATE_H
|
||||||
|
|
47
src/validate.c
Normal file
47
src/validate.c
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "../lib/rijndael-alg-fst.h"
|
||||||
|
#include "../lib/random.h"
|
||||||
|
#include "../lib/logger.h"
|
||||||
|
#include "validate.h"
|
||||||
|
|
||||||
|
#define AES_ROUNDS 10
|
||||||
|
#define AES_BLOCK_WORDS 4
|
||||||
|
#define AES_KEY_BYTES 16
|
||||||
|
|
||||||
|
static int inited = 0;
|
||||||
|
static uint32_t aes_input[AES_BLOCK_WORDS];
|
||||||
|
static uint32_t aes_sched[(AES_ROUNDS+1)*4];
|
||||||
|
|
||||||
|
void validate_init()
|
||||||
|
{
|
||||||
|
for (int i=0; i < AES_BLOCK_WORDS; i++) {
|
||||||
|
aes_input[i] = 0;
|
||||||
|
}
|
||||||
|
uint8_t key[AES_KEY_BYTES];
|
||||||
|
if (!random_bytes(key, AES_KEY_BYTES)) {
|
||||||
|
log_fatal("validate", "couldn't get random bytes");
|
||||||
|
}
|
||||||
|
if (rijndaelKeySetupEnc(aes_sched, key, AES_KEY_BYTES*8) != AES_ROUNDS) {
|
||||||
|
log_fatal("validate", "couldn't initialize AES key");
|
||||||
|
}
|
||||||
|
inited = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void validate_gen(const uint32_t src, const uint32_t dst,
|
||||||
|
uint8_t output[VALIDATE_BYTES])
|
||||||
|
{
|
||||||
|
assert(inited);
|
||||||
|
aes_input[0] = src;
|
||||||
|
aes_input[1] = dst;
|
||||||
|
rijndaelEncrypt(aes_sched, AES_ROUNDS, (uint8_t *)aes_input, output);
|
||||||
|
}
|
||||||
|
|
17
src/validate.h
Normal file
17
src/validate.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _VALIDATE_H
|
||||||
|
#define _VALIDATE_H
|
||||||
|
|
||||||
|
#define VALIDATE_BYTES 16
|
||||||
|
|
||||||
|
void validate_init();
|
||||||
|
void validate_gen(const uint32_t src, const uint32_t dst, uint8_t output[VALIDATE_BYTES]);
|
||||||
|
|
||||||
|
#endif//_VALIDATE_H
|
155
src/zmap.1
Normal file
155
src/zmap.1
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
.TH zmap 1 "06 Aug 2013" "1.0" "zmap man page"
|
||||||
|
.SH NAME
|
||||||
|
zmap \- A fast Internet-wide scanner
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B zmap
|
||||||
|
[
|
||||||
|
.I "OPTIONS \&..."
|
||||||
|
]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.I ZMap
|
||||||
|
is a network tool for scanning the entire Internet (or large samples).
|
||||||
|
.SH OPTIONS
|
||||||
|
.SS "Basic options"
|
||||||
|
.TP
|
||||||
|
.B \-p, --target-port=port
|
||||||
|
TCP port number to scan (for SYN scans)
|
||||||
|
.TP
|
||||||
|
.B \-o, --output-file=name
|
||||||
|
When using an output module that uses a file (such as the default),
|
||||||
|
write results to this file. Use - for stdout.
|
||||||
|
.TP
|
||||||
|
.B \-b, --blacklist-file=path
|
||||||
|
File of subnets to exclude, in CIDR notation (e.g. 192.168.0.0/16),
|
||||||
|
one-per line. It is recommended you use this to exclude RFC 1918
|
||||||
|
addresses, multicast, IANA reserved space, and other IANA
|
||||||
|
special-purpose addresses. An example blacklist file is provided in
|
||||||
|
.B conf/blacklist.example
|
||||||
|
for this purpose.
|
||||||
|
.TP
|
||||||
|
.B -w, --whitelist-file=path
|
||||||
|
File of subnets to constrain scan to, in CIDR
|
||||||
|
notation, e.g. 192.168.0.0/16
|
||||||
|
|
||||||
|
.SS "Scan options"
|
||||||
|
.TP
|
||||||
|
.B \-n, --max-targets=n
|
||||||
|
Cap number of targets to probe (as a number or
|
||||||
|
a percentage of the address space)
|
||||||
|
.TP
|
||||||
|
.B \-N, --max-results=n
|
||||||
|
Cap number of results to return
|
||||||
|
.TP
|
||||||
|
.B \-t, --max-runtime=secs
|
||||||
|
Cap length of time for sending packets
|
||||||
|
.TP
|
||||||
|
.B \-r, --rate=pps
|
||||||
|
Set send rate in packets/sec
|
||||||
|
.TP
|
||||||
|
.B \-B, --bandwidth=bps
|
||||||
|
Set send rate in bits/second (supports suffixes G, M and K). This
|
||||||
|
overrides the
|
||||||
|
.B --rate
|
||||||
|
flag.
|
||||||
|
.TP
|
||||||
|
.B \-c, --cooldown-time=secs
|
||||||
|
How long to continue receiving after sending
|
||||||
|
last probe (default=8)
|
||||||
|
.TP
|
||||||
|
.B \-e, --seed=n
|
||||||
|
Seed used to select address permutation. Specify the same seed in order to scan the same sample repeatedly.
|
||||||
|
.TP
|
||||||
|
.B \-T, --sender-threads=n
|
||||||
|
Threads used to send packets (default=1)
|
||||||
|
.TP
|
||||||
|
.B \-P, --probes=n
|
||||||
|
Number of probes to send to each IP
|
||||||
|
(default=1)
|
||||||
|
.TP
|
||||||
|
.B \-d, --dryrun
|
||||||
|
Print out each packet to stdout instead of sending it.
|
||||||
|
(May be useful for debugging.)
|
||||||
|
|
||||||
|
.SS "Network options"
|
||||||
|
.TP
|
||||||
|
.B \-s, --source-port=port|range
|
||||||
|
Source port(s) for scan packets
|
||||||
|
.TP
|
||||||
|
.B \-S, --source-ip=ip|range
|
||||||
|
Source address(es) for scan packets
|
||||||
|
.TP
|
||||||
|
.B \-G, --gateway-mac=addr
|
||||||
|
Specify gateway MAC address. All packets will be sent to this
|
||||||
|
Ethernet address.
|
||||||
|
.TP
|
||||||
|
.B \-i, --interface=name
|
||||||
|
Specify network interface to use.
|
||||||
|
|
||||||
|
.SS "Advanced options"
|
||||||
|
.TP
|
||||||
|
.B \-M, --probe-module=name
|
||||||
|
Select probe module (default=tcp_synscan)
|
||||||
|
.TP
|
||||||
|
.B \-O, --output-module=name
|
||||||
|
Select output module (default=simple_file)
|
||||||
|
.TP
|
||||||
|
.B --probe-args=args
|
||||||
|
Arguments to pass to probe module
|
||||||
|
.TP
|
||||||
|
.B --output-args=args
|
||||||
|
Arguments to pass to output module
|
||||||
|
.TP
|
||||||
|
.B --list-output-modules
|
||||||
|
List available output modules
|
||||||
|
.TP
|
||||||
|
.B --list-probe-modules
|
||||||
|
List available probe modules
|
||||||
|
|
||||||
|
.SS "Additional options"
|
||||||
|
.TP
|
||||||
|
.B \-C, --config=filename
|
||||||
|
Read a configuration file, which can specify
|
||||||
|
any of these options (default=zmap.conf)
|
||||||
|
.TP
|
||||||
|
.B \-q, --quiet
|
||||||
|
Do not print status updates
|
||||||
|
.TP
|
||||||
|
.B \-g, --summary
|
||||||
|
Print configuration and summary at end of scan
|
||||||
|
.TP
|
||||||
|
.B \-v, --verbosity=n
|
||||||
|
Level of log detail (0-5) (default=3)
|
||||||
|
.TP
|
||||||
|
.B \-h, --help
|
||||||
|
Print help and exit
|
||||||
|
.TP
|
||||||
|
.B \-V, --version
|
||||||
|
Print version and exit
|
||||||
|
|
||||||
|
.SH EXAMPLES
|
||||||
|
Scan the whole Internet for hosts with port 443 open (results discarded):
|
||||||
|
.PP
|
||||||
|
.\" -p: example of
|
||||||
|
.B zmap \-p 443
|
||||||
|
.PP
|
||||||
|
Find 5 HTTP servers (port 80), scanning at 10 Mb/s, print the results to stdout:
|
||||||
|
.PP
|
||||||
|
.\" -N: example of
|
||||||
|
.\" -B: example of
|
||||||
|
.B zmap -N 5 -B 10M -p 80 -o -
|
||||||
|
|
||||||
|
.SH WARNING
|
||||||
|
By default, ZMap attempts to scan at the line speed of your Ethernet
|
||||||
|
interface and can easily use 1 Gbit/second of bandwidth. If your
|
||||||
|
network is not able to support sending packets this quickly, your
|
||||||
|
local network may become congested, causing connectivity problems for
|
||||||
|
you and those around you. Use the -B (--bandwidth) option to set
|
||||||
|
ZMap's maximum bandwidth to an appropriate limit for your network and
|
||||||
|
upstream connection.
|
||||||
|
.
|
||||||
|
.SH AUTHOR
|
||||||
|
Zakir Durumeric,
|
||||||
|
Eric Wustrow,
|
||||||
|
J. Alex Halderman
|
||||||
|
.B (https://www.zmap.io)
|
||||||
|
|
483
src/zmap.c
Normal file
483
src/zmap.c
Normal file
@ -0,0 +1,483 @@
|
|||||||
|
/*
|
||||||
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <pcap/pcap.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "../lib/logger.h"
|
||||||
|
#include "../lib/random.h"
|
||||||
|
|
||||||
|
#include "zopt.h"
|
||||||
|
#include "send.h"
|
||||||
|
#include "recv.h"
|
||||||
|
#include "state.h"
|
||||||
|
#include "monitor.h"
|
||||||
|
#include "output_modules/output_modules.h"
|
||||||
|
#include "probe_modules/probe_modules.h"
|
||||||
|
#include "get_gateway.h"
|
||||||
|
|
||||||
|
pthread_mutex_t cpu_affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
pthread_mutex_t recv_ready_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
static void set_cpu(void)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&cpu_affinity_mutex);
|
||||||
|
static int core=0;
|
||||||
|
int num_cores = sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
|
cpu_set_t cpuset;
|
||||||
|
CPU_ZERO(&cpuset);
|
||||||
|
CPU_SET(core, &cpuset);
|
||||||
|
if (pthread_setaffinity_np(pthread_self(),
|
||||||
|
sizeof(cpu_set_t), &cpuset) != 0) {
|
||||||
|
log_error("zmap", "can't set thread CPU affinity");
|
||||||
|
}
|
||||||
|
log_trace("zmap", "set thread %u affinity to core %d",
|
||||||
|
pthread_self(), core);
|
||||||
|
core = (core + 1) % num_cores;
|
||||||
|
pthread_mutex_unlock(&cpu_affinity_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* start_send(__attribute__((unused)) void *arg)
|
||||||
|
{
|
||||||
|
set_cpu();
|
||||||
|
send_run();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* start_recv(__attribute__((unused)) void *arg)
|
||||||
|
{
|
||||||
|
set_cpu();
|
||||||
|
recv_run(&recv_ready_mutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *start_mon(__attribute__((unused)) void *arg)
|
||||||
|
{
|
||||||
|
set_cpu();
|
||||||
|
monitor_run();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SI(w,x,y) printf("%s\t%s\t%i\n", w, x, y);
|
||||||
|
#define SD(w,x,y) printf("%s\t%s\t%f\n", w, x, y);
|
||||||
|
#define SU(w,x,y) printf("%s\t%s\t%u\n", w, x, y);
|
||||||
|
#define SLU(w,x,y) printf("%s\t%s\t%lu\n", w, x, y);
|
||||||
|
#define SS(w,x,y) printf("%s\t%s\t%s\n", w, x, y);
|
||||||
|
#define STRTIME_LEN 1024
|
||||||
|
|
||||||
|
static void summary(void)
|
||||||
|
{
|
||||||
|
char send_start_time[STRTIME_LEN+1];
|
||||||
|
assert(dstrftime(send_start_time, STRTIME_LEN, "%c", zsend.start));
|
||||||
|
char send_end_time[STRTIME_LEN+1];
|
||||||
|
assert(dstrftime(send_end_time, STRTIME_LEN, "%c", zsend.finish));
|
||||||
|
char recv_start_time[STRTIME_LEN+1];
|
||||||
|
assert(dstrftime(recv_start_time, STRTIME_LEN, "%c", zrecv.start));
|
||||||
|
char recv_end_time[STRTIME_LEN+1];
|
||||||
|
assert(dstrftime(recv_end_time, STRTIME_LEN, "%c", zrecv.finish));
|
||||||
|
double hitrate = ((double) 100 * zrecv.success_unique)/((double)zsend.sent);
|
||||||
|
|
||||||
|
SU("cnf", "target-port", zconf.target_port);
|
||||||
|
SU("cnf", "source-port-range-begin", zconf.source_port_first);
|
||||||
|
SU("cnf", "source-port-range-end", zconf.source_port_last);
|
||||||
|
SS("cnf", "source-addr-range-begin", zconf.source_ip_first);
|
||||||
|
SS("cnf", "source-addr-range-end", zconf.source_ip_last);
|
||||||
|
SU("cnf", "maximum-targets", zconf.max_targets);
|
||||||
|
SU("cnf", "maximum-runtime", zconf.max_runtime);
|
||||||
|
SU("cnf", "maximum-results", zconf.max_results);
|
||||||
|
SU("cnf", "permutation-seed", zconf.seed);
|
||||||
|
SI("cnf", "cooldown-period", zconf.cooldown_secs);
|
||||||
|
SS("cnf", "send-interface", zconf.iface);
|
||||||
|
SI("cnf", "rate", zconf.rate);
|
||||||
|
SLU("cnf", "bandwidth", zconf.bandwidth);
|
||||||
|
SU("env", "nprocessors", (unsigned) sysconf(_SC_NPROCESSORS_ONLN));
|
||||||
|
SS("exc", "send-start-time", send_start_time);
|
||||||
|
SS("exc", "send-end-time", send_end_time);
|
||||||
|
SS("exc", "recv-start-time", recv_start_time);
|
||||||
|
SS("exc", "recv-end-time", recv_end_time);
|
||||||
|
SU("exc", "sent", zsend.sent);
|
||||||
|
SU("exc", "blacklisted", zsend.blacklisted);
|
||||||
|
SU("exc", "first-scanned", zsend.first_scanned);
|
||||||
|
SD("exc", "hit-rate", hitrate);
|
||||||
|
SU("exc", "success-total", zrecv.success_total);
|
||||||
|
SU("exc", "success-unique", zrecv.success_unique);
|
||||||
|
SU("exc", "success-cooldown-total", zrecv.cooldown_total);
|
||||||
|
SU("exc", "success-cooldown-unique", zrecv.cooldown_unique);
|
||||||
|
SU("exc", "failure-total", zrecv.failure_total);
|
||||||
|
SU("exc", "sendto-failures", zsend.sendto_failures);
|
||||||
|
SU("adv", "permutation-gen", zconf.generator);
|
||||||
|
SS("exc", "scan-type", zconf.probe_module->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start_zmap(void)
|
||||||
|
{
|
||||||
|
log_init(stderr, zconf.log_level);
|
||||||
|
log_info("zmap", "started");
|
||||||
|
|
||||||
|
// finish setting up configuration
|
||||||
|
if (zconf.iface == NULL) {
|
||||||
|
char errbuf[PCAP_ERRBUF_SIZE];
|
||||||
|
char *iface = pcap_lookupdev(errbuf);
|
||||||
|
if (iface == NULL) {
|
||||||
|
log_fatal("zmap", "could not detect default network interface "
|
||||||
|
"(e.g. eth0). Try running as root or setting"
|
||||||
|
" interface using -i flag.");
|
||||||
|
}
|
||||||
|
log_debug("zmap", "no interface provided. will use %s", iface);
|
||||||
|
zconf.iface = iface;
|
||||||
|
}
|
||||||
|
if (zconf.source_ip_first == NULL) {
|
||||||
|
struct in_addr default_ip;
|
||||||
|
zconf.source_ip_first = malloc(INET_ADDRSTRLEN);
|
||||||
|
zconf.source_ip_last = zconf.source_ip_first;
|
||||||
|
if (get_iface_ip(zconf.iface, &default_ip) < 0) {
|
||||||
|
log_fatal("zmap", "could not detect default IP address for for %s."
|
||||||
|
" Try specifying a source address (-S).", zconf.iface);
|
||||||
|
}
|
||||||
|
inet_ntop(AF_INET, &default_ip, zconf.source_ip_first, INET_ADDRSTRLEN);
|
||||||
|
log_debug("zmap", "no source IP address given. will use %s",
|
||||||
|
zconf.source_ip_first);
|
||||||
|
}
|
||||||
|
if (!zconf.gw_mac_set) {
|
||||||
|
struct in_addr gw_ip;
|
||||||
|
char iface[IF_NAMESIZE];
|
||||||
|
if (get_default_gw(&gw_ip, iface) < 0) {
|
||||||
|
log_fatal("zmap", "could not detect default gateway address for %i."
|
||||||
|
" Try setting default gateway mac address (-G).");
|
||||||
|
}
|
||||||
|
log_debug("zmap", "found gateway IP %s on %s", inet_ntoa(gw_ip), iface);
|
||||||
|
if (get_hw_addr(&gw_ip, iface, zconf.gw_mac) < 0) {
|
||||||
|
log_fatal("zmap", "could not detect GW MAC address for %s on %s."
|
||||||
|
" Try setting default gateway mac address (-G).",
|
||||||
|
inet_ntoa(gw_ip), zconf.iface);
|
||||||
|
}
|
||||||
|
zconf.gw_mac_set = 1;
|
||||||
|
log_debug("zmap", "using default gateway MAC %02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
zconf.gw_mac[0], zconf.gw_mac[1], zconf.gw_mac[2],
|
||||||
|
zconf.gw_mac[3], zconf.gw_mac[4], zconf.gw_mac[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialization
|
||||||
|
if (zconf.output_module && zconf.output_module->init) {
|
||||||
|
zconf.output_module->init(&zconf);
|
||||||
|
}
|
||||||
|
if (send_init()) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (zconf.output_module && zconf.output_module->start) {
|
||||||
|
zconf.output_module->start(&zconf, &zsend, &zrecv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// start threads
|
||||||
|
pthread_t *tsend, trecv, tmon;
|
||||||
|
int r = pthread_create(&trecv, NULL, start_recv, NULL);
|
||||||
|
if (r != 0) {
|
||||||
|
log_fatal("zmap", "unable to create recv thread");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
pthread_mutex_lock(&recv_ready_mutex);
|
||||||
|
if (zconf.recv_ready) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&recv_ready_mutex);
|
||||||
|
}
|
||||||
|
tsend = malloc(zconf.senders * sizeof(pthread_t));
|
||||||
|
assert(tsend);
|
||||||
|
log_debug("zmap", "using %d sender threads", zconf.senders);
|
||||||
|
for (int i=0; i < zconf.senders; i++) {
|
||||||
|
r = pthread_create(&tsend[i], NULL, start_send, NULL);
|
||||||
|
if (r != 0) {
|
||||||
|
log_fatal("zmap", "unable to create send thread");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!zconf.quiet) {
|
||||||
|
r = pthread_create(&tmon, NULL, start_mon, NULL);
|
||||||
|
if (r != 0) {
|
||||||
|
log_fatal("zmap", "unable to create monitor thread");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for completion
|
||||||
|
for (int i=0; i < zconf.senders; i++) {
|
||||||
|
pthread_join(tsend[i], NULL);
|
||||||
|
if (r != 0) {
|
||||||
|
log_fatal("zmap", "unable to join send thread");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_debug("zmap", "senders finished");
|
||||||
|
pthread_join(trecv, NULL);
|
||||||
|
if (r != 0) {
|
||||||
|
log_fatal("zmap", "unable to join recv thread");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (!zconf.quiet) {
|
||||||
|
pthread_join(tmon, NULL);
|
||||||
|
if (r != 0) {
|
||||||
|
log_fatal("zmap", "unable to join monitor thread");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// finished
|
||||||
|
if (zconf.summary) {
|
||||||
|
summary();
|
||||||
|
}
|
||||||
|
if (zconf.output_module && zconf.output_module->close) {
|
||||||
|
zconf.output_module->close(&zconf, &zsend, &zrecv);
|
||||||
|
}
|
||||||
|
if (zconf.probe_module && zconf.probe_module->close) {
|
||||||
|
zconf.probe_module->close(&zconf, &zsend, &zrecv);
|
||||||
|
}
|
||||||
|
log_info("zmap", "completed");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void enforce_range(const char *name, int v, int min, int max)
|
||||||
|
{
|
||||||
|
if (v < min || v > max) {
|
||||||
|
fprintf(stderr, "%s: argument `%s' must be between %d and %d\n",
|
||||||
|
CMDLINE_PARSER_PACKAGE, name, min, max);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int file_exists(char *name)
|
||||||
|
{
|
||||||
|
FILE *file = fopen(name, "r");
|
||||||
|
if (!file)
|
||||||
|
return 0;
|
||||||
|
fclose(file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAC_LEN IFHWADDRLEN
|
||||||
|
int parse_mac(macaddr_t *out, char *in)
|
||||||
|
{
|
||||||
|
if (strlen(in) < MAC_LEN*3-1)
|
||||||
|
return 0;
|
||||||
|
char octet[3];
|
||||||
|
octet[2] = '\0';
|
||||||
|
for (int i=0; i < MAC_LEN; i++) {
|
||||||
|
if (i < MAC_LEN-1 && in[i*3+2] != ':') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
strncpy(octet, &in[i*3], 2);
|
||||||
|
char *err = NULL;
|
||||||
|
long b = strtol(octet, &err, 16);
|
||||||
|
if (err && *err != '\0') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
out[i] = b & 0xFF;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SET_IF_GIVEN(DST,ARG) \
|
||||||
|
{ if (args.ARG##_given) { (DST) = args.ARG##_arg; }; }
|
||||||
|
#define SET_BOOL(DST,ARG) \
|
||||||
|
{ if (args.ARG##_given) { (DST) = 1; }; }
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct gengetopt_args_info args;
|
||||||
|
struct cmdline_parser_params *params;
|
||||||
|
params = cmdline_parser_params_create();
|
||||||
|
params->initialize = 1;
|
||||||
|
params->override = 0;
|
||||||
|
params->check_required = 0;
|
||||||
|
if (cmdline_parser_ext(argc, argv, &args, params) != 0) {
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
if (args.help_given) {
|
||||||
|
cmdline_parser_print_help();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
if (args.version_given) {
|
||||||
|
cmdline_parser_print_version();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
if (args.list_output_modules_given) {
|
||||||
|
print_output_modules();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
if (args.list_probe_modules_given) {
|
||||||
|
print_probe_modules();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
if (args.config_given || file_exists(args.config_arg)) {
|
||||||
|
params->initialize = 0;
|
||||||
|
params->override = 0;
|
||||||
|
if (cmdline_parser_config_file(args.config_arg, &args, params)
|
||||||
|
!= 0) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cmdline_parser_required(&args, CMDLINE_PARSER_PACKAGE) != 0) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
SET_IF_GIVEN(zconf.output_filename, output_file);
|
||||||
|
SET_IF_GIVEN(zconf.blacklist_filename, blacklist_file);
|
||||||
|
SET_IF_GIVEN(zconf.whitelist_filename, whitelist_file);
|
||||||
|
SET_IF_GIVEN(zconf.probe_args, probe_args);
|
||||||
|
SET_IF_GIVEN(zconf.output_args, output_args);
|
||||||
|
SET_IF_GIVEN(zconf.iface, interface);
|
||||||
|
SET_IF_GIVEN(zconf.max_runtime, max_runtime);
|
||||||
|
SET_IF_GIVEN(zconf.max_results, max_results);
|
||||||
|
SET_IF_GIVEN(zconf.rate, rate);
|
||||||
|
SET_IF_GIVEN(zconf.packet_streams, probes);
|
||||||
|
SET_BOOL(zconf.dryrun, dryrun);
|
||||||
|
SET_BOOL(zconf.quiet, quiet);
|
||||||
|
SET_BOOL(zconf.summary, summary);
|
||||||
|
zconf.cooldown_secs = args.cooldown_time_arg;
|
||||||
|
zconf.senders = args.sender_threads_arg;
|
||||||
|
zconf.log_level = args.verbosity_arg;
|
||||||
|
|
||||||
|
zconf.output_module = get_output_module_by_name(args.output_module_arg);
|
||||||
|
if (!zconf.output_module) {
|
||||||
|
fprintf(stderr, "%s: specified output module (%s) does not exist\n",
|
||||||
|
args.output_module_arg, CMDLINE_PARSER_PACKAGE);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
zconf.probe_module = get_probe_module_by_name(args.probe_module_arg);
|
||||||
|
if (!zconf.probe_module) {
|
||||||
|
fprintf(stderr, "%s: specified probe module (%s) does not exist\n",
|
||||||
|
CMDLINE_PARSER_PACKAGE, args.probe_module_arg);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (zconf.probe_module->port_args) {
|
||||||
|
if (args.source_port_given) {
|
||||||
|
char *dash = strchr(args.source_port_arg, '-');
|
||||||
|
if (dash) { // range
|
||||||
|
*dash = '\0';
|
||||||
|
zconf.source_port_first = atoi(args.source_port_arg);
|
||||||
|
enforce_range("starting source-port", zconf.source_port_first, 0, 0xFFFF);
|
||||||
|
zconf.source_port_last = atoi(dash+1);
|
||||||
|
enforce_range("ending source-port", zconf.source_port_last, 0, 0xFFFF);
|
||||||
|
if (zconf.source_port_first > zconf.source_port_last) {
|
||||||
|
fprintf(stderr, "%s: invalid source port range: "
|
||||||
|
"last port is less than first port\n",
|
||||||
|
CMDLINE_PARSER_PACKAGE);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
} else { // single port
|
||||||
|
int port = atoi(args.source_port_arg);
|
||||||
|
enforce_range("source-port", port, 0, 0xFFFF);
|
||||||
|
zconf.source_port_first = port;
|
||||||
|
zconf.source_port_last = port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!args.target_port_given) {
|
||||||
|
fprintf(stderr, "%s: target port is required for this type of probe\n",
|
||||||
|
CMDLINE_PARSER_PACKAGE);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
enforce_range("target-port", args.target_port_arg, 0, 0xFFFF);
|
||||||
|
zconf.target_port = args.target_port_arg;
|
||||||
|
}
|
||||||
|
if (args.source_ip_given) {
|
||||||
|
char *dash = strchr(args.source_ip_arg, '-');
|
||||||
|
if (dash) { // range
|
||||||
|
*dash = '\0';
|
||||||
|
zconf.source_ip_first = args.source_ip_arg;
|
||||||
|
zconf.source_ip_last = dash+1;
|
||||||
|
} else { // single address
|
||||||
|
zconf.source_ip_first = args.source_ip_arg;
|
||||||
|
zconf.source_ip_last = args.source_ip_arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (args.gateway_mac_given) {
|
||||||
|
if (!parse_mac(zconf.gw_mac, args.gateway_mac_arg)) {
|
||||||
|
fprintf(stderr, "%s: invalid MAC address `%s'\n",
|
||||||
|
CMDLINE_PARSER_PACKAGE, args.gateway_mac_arg);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
zconf.gw_mac_set = 1;
|
||||||
|
}
|
||||||
|
if (args.seed_given) {
|
||||||
|
zconf.seed = args.seed_arg;
|
||||||
|
zconf.use_seed = 1;
|
||||||
|
}
|
||||||
|
if (args.bandwidth_given) {
|
||||||
|
// Supported: G,g=*1000000000; M,m=*1000000 K,k=*1000 bits per second
|
||||||
|
zconf.bandwidth = atoi(args.bandwidth_arg);
|
||||||
|
char *suffix = args.bandwidth_arg;
|
||||||
|
while (*suffix >= '0' && *suffix <= '9') {
|
||||||
|
suffix++;
|
||||||
|
}
|
||||||
|
if (*suffix) {
|
||||||
|
switch (*suffix) {
|
||||||
|
case 'G': case 'g':
|
||||||
|
zconf.bandwidth *= 1000000000;
|
||||||
|
break;
|
||||||
|
case 'M': case 'm':
|
||||||
|
zconf.bandwidth *= 1000000;
|
||||||
|
break;
|
||||||
|
case 'K': case 'k':
|
||||||
|
zconf.bandwidth *= 1000;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "%s: unknown bandwidth suffix '%s' "
|
||||||
|
"(supported suffixes are G, M and K)\n",
|
||||||
|
CMDLINE_PARSER_PACKAGE, suffix);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (args.max_targets_given) {
|
||||||
|
errno = 0;
|
||||||
|
char *end;
|
||||||
|
double v = strtod(args.max_targets_arg, &end);
|
||||||
|
if (end == args.max_targets_arg || errno != 0) {
|
||||||
|
fprintf(stderr, "%s: can't convert max-targets to a number\n",
|
||||||
|
CMDLINE_PARSER_PACKAGE);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (end[0] == '%' && end[1] == '\0') {
|
||||||
|
// treat as percentage
|
||||||
|
v = v * (1L << 32) / 100.;
|
||||||
|
} else if (end[0] != '\0') {
|
||||||
|
fprintf(stderr, "%s: extra characters after max-targets\n",
|
||||||
|
CMDLINE_PARSER_PACKAGE);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (v <= 0) {
|
||||||
|
zconf.max_targets = 0;
|
||||||
|
}
|
||||||
|
else if (v >= (1L << 32)) {
|
||||||
|
zconf.max_targets = 0xFFFFFFFF;
|
||||||
|
} else {
|
||||||
|
zconf.max_targets = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start_zmap();
|
||||||
|
|
||||||
|
cmdline_parser_free(&args);
|
||||||
|
free(params);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
1339
src/zopt.c
Normal file
1339
src/zopt.c
Normal file
File diff suppressed because it is too large
Load Diff
119
src/zopt.ggo
Normal file
119
src/zopt.ggo
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
# ZMap Copyright 2013 Regents of the University of Michigan
|
||||||
|
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
# zmap option description to be processed by gengetopt
|
||||||
|
|
||||||
|
package "zmap"
|
||||||
|
version "1.0.0"
|
||||||
|
purpose "A fast Internet-wide scanner."
|
||||||
|
|
||||||
|
section "Basic arguments"
|
||||||
|
|
||||||
|
option "target-port" p "TCP port number to scan (for SYN scans)"
|
||||||
|
typestr="port"
|
||||||
|
optional int
|
||||||
|
option "output-file" o "Output file"
|
||||||
|
typestr="name"
|
||||||
|
optional string
|
||||||
|
option "blacklist-file" b "File of subnets to exclude, in CIDR notation, e.g. 192.168.0.0/16"
|
||||||
|
typestr="path"
|
||||||
|
optional string
|
||||||
|
option "whitelist-file" w "File of subnets to constrain scan to, in CIDR notation, e.g. 192.168.0.0/16"
|
||||||
|
typestr="path"
|
||||||
|
optional string
|
||||||
|
|
||||||
|
section "Scan options"
|
||||||
|
|
||||||
|
option "max-targets" n "Cap number of targets to probe (as a number or a percentage of the address space)"
|
||||||
|
typestr="n"
|
||||||
|
optional string
|
||||||
|
option "max-results" N "Cap number of results to return"
|
||||||
|
typestr="n"
|
||||||
|
optional int
|
||||||
|
option "max-runtime" t "Cap length of time for sending packets"
|
||||||
|
typestr="ses"
|
||||||
|
optional int
|
||||||
|
option "rate" r "Set send rate in packets/sec"
|
||||||
|
typestr="pps"
|
||||||
|
optional int
|
||||||
|
option "bandwidth" B "Set send rate in bits/second (supports suffixes G, M and K)"
|
||||||
|
typestr="bps"
|
||||||
|
optional string
|
||||||
|
option "cooldown-time" c "How long to continue receiving after sending last probe"
|
||||||
|
typestr="secs"
|
||||||
|
default="8"
|
||||||
|
optional int
|
||||||
|
option "seed" e "Seed used to select address permutation"
|
||||||
|
typestr="n"
|
||||||
|
optional int
|
||||||
|
option "sender-threads" T "Threads used to send packets"
|
||||||
|
typestr="n"
|
||||||
|
default="1"
|
||||||
|
optional int
|
||||||
|
option "probes" P "Number of probes to send to each IP"
|
||||||
|
typestr="n"
|
||||||
|
default="1"
|
||||||
|
optional int
|
||||||
|
option "dryrun" d "Don't actually send packets"
|
||||||
|
optional
|
||||||
|
|
||||||
|
section "Network options"
|
||||||
|
|
||||||
|
option "source-port" s "Source port(s) for scan packets"
|
||||||
|
typestr="port|range"
|
||||||
|
optional string
|
||||||
|
option "source-ip" S "Source address(es) for scan packets"
|
||||||
|
typestr="ip|range"
|
||||||
|
optional string
|
||||||
|
option "gateway-mac" G "Specify gateway MAC address"
|
||||||
|
typestr="addr"
|
||||||
|
optional string
|
||||||
|
option "interface" i "Specify network interface to use"
|
||||||
|
typestr="name"
|
||||||
|
optional string
|
||||||
|
|
||||||
|
section "Advanced options"
|
||||||
|
|
||||||
|
option "probe-module" M "Select probe module"
|
||||||
|
typestr="name"
|
||||||
|
default="tcp_synscan"
|
||||||
|
optional string
|
||||||
|
option "output-module" O "Select output module"
|
||||||
|
typestr="name"
|
||||||
|
default="simple_file"
|
||||||
|
optional string
|
||||||
|
option "probe-args" - "Arguments to pass to probe module"
|
||||||
|
typestr="args"
|
||||||
|
optional string
|
||||||
|
option "output-args" - "Arguments to pass to output module"
|
||||||
|
typestr="args"
|
||||||
|
optional string
|
||||||
|
option "list-output-modules" - "List available output modules"
|
||||||
|
optional
|
||||||
|
option "list-probe-modules" - "List available probe modules"
|
||||||
|
optional
|
||||||
|
|
||||||
|
section "Additional options"
|
||||||
|
|
||||||
|
option "config" C "Read a configuration file, which can specify any of these options"
|
||||||
|
typestr="filename"
|
||||||
|
default="/etc/zmap/zmap.conf"
|
||||||
|
optional string
|
||||||
|
option "quiet" q "Do not print status updates"
|
||||||
|
optional
|
||||||
|
option "summary" g "Print configuration and summary at end of scan"
|
||||||
|
optional
|
||||||
|
option "verbosity" v "Level of log detail (0-5)"
|
||||||
|
typestr="n"
|
||||||
|
default="3"
|
||||||
|
optional int
|
||||||
|
option "help" h "Print help and exit"
|
||||||
|
optional
|
||||||
|
option "version" V "Print version and exit"
|
||||||
|
optional
|
||||||
|
|
||||||
|
text "\nExamples:\n\
|
||||||
|
zmap -p 443 (scans the whole Internet for hosts with port 443 open)\n\
|
||||||
|
zmap -N 5 -B 10M -p 80 -o - (find 5 HTTP servers, scanning at 10 Mb/s)"
|
||||||
|
|
298
src/zopt.h
Normal file
298
src/zopt.h
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
/** @file zopt.h
|
||||||
|
* @brief The header file for the command line option parser
|
||||||
|
* generated by GNU Gengetopt version 2.22.5
|
||||||
|
* http://www.gnu.org/software/gengetopt.
|
||||||
|
* DO NOT modify this file, since it can be overwritten
|
||||||
|
* @author GNU Gengetopt by Lorenzo Bettini */
|
||||||
|
|
||||||
|
#ifndef ZOPT_H
|
||||||
|
#define ZOPT_H
|
||||||
|
|
||||||
|
/* If we use autoconf. */
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h> /* for FILE */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#ifndef CMDLINE_PARSER_PACKAGE
|
||||||
|
/** @brief the program name (used for printing errors) */
|
||||||
|
#define CMDLINE_PARSER_PACKAGE "zmap"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CMDLINE_PARSER_PACKAGE_NAME
|
||||||
|
/** @brief the complete program name (used for help and version) */
|
||||||
|
#define CMDLINE_PARSER_PACKAGE_NAME "zmap"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CMDLINE_PARSER_VERSION
|
||||||
|
/** @brief the program version */
|
||||||
|
#define CMDLINE_PARSER_VERSION "1.0.0"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @brief Where the command line options are stored */
|
||||||
|
struct gengetopt_args_info
|
||||||
|
{
|
||||||
|
int target_port_arg; /**< @brief TCP port number to scan (for SYN scans). */
|
||||||
|
char * target_port_orig; /**< @brief TCP port number to scan (for SYN scans) original value given at command line. */
|
||||||
|
const char *target_port_help; /**< @brief TCP port number to scan (for SYN scans) help description. */
|
||||||
|
char * output_file_arg; /**< @brief Output file. */
|
||||||
|
char * output_file_orig; /**< @brief Output file original value given at command line. */
|
||||||
|
const char *output_file_help; /**< @brief Output file help description. */
|
||||||
|
char * blacklist_file_arg; /**< @brief File of subnets to exclude, in CIDR notation, e.g. 192.168.0.0/16. */
|
||||||
|
char * blacklist_file_orig; /**< @brief File of subnets to exclude, in CIDR notation, e.g. 192.168.0.0/16 original value given at command line. */
|
||||||
|
const char *blacklist_file_help; /**< @brief File of subnets to exclude, in CIDR notation, e.g. 192.168.0.0/16 help description. */
|
||||||
|
char * whitelist_file_arg; /**< @brief File of subnets to constrain scan to, in CIDR notation, e.g. 192.168.0.0/16. */
|
||||||
|
char * whitelist_file_orig; /**< @brief File of subnets to constrain scan to, in CIDR notation, e.g. 192.168.0.0/16 original value given at command line. */
|
||||||
|
const char *whitelist_file_help; /**< @brief File of subnets to constrain scan to, in CIDR notation, e.g. 192.168.0.0/16 help description. */
|
||||||
|
char * max_targets_arg; /**< @brief Cap number of targets to probe (as a number or a percentage of the address space). */
|
||||||
|
char * max_targets_orig; /**< @brief Cap number of targets to probe (as a number or a percentage of the address space) original value given at command line. */
|
||||||
|
const char *max_targets_help; /**< @brief Cap number of targets to probe (as a number or a percentage of the address space) help description. */
|
||||||
|
int max_results_arg; /**< @brief Cap number of results to return. */
|
||||||
|
char * max_results_orig; /**< @brief Cap number of results to return original value given at command line. */
|
||||||
|
const char *max_results_help; /**< @brief Cap number of results to return help description. */
|
||||||
|
int max_runtime_arg; /**< @brief Cap length of time for sending packets. */
|
||||||
|
char * max_runtime_orig; /**< @brief Cap length of time for sending packets original value given at command line. */
|
||||||
|
const char *max_runtime_help; /**< @brief Cap length of time for sending packets help description. */
|
||||||
|
int rate_arg; /**< @brief Set send rate in packets/sec. */
|
||||||
|
char * rate_orig; /**< @brief Set send rate in packets/sec original value given at command line. */
|
||||||
|
const char *rate_help; /**< @brief Set send rate in packets/sec help description. */
|
||||||
|
char * bandwidth_arg; /**< @brief Set send rate in bits/second (supports suffixes G, M and K). */
|
||||||
|
char * bandwidth_orig; /**< @brief Set send rate in bits/second (supports suffixes G, M and K) original value given at command line. */
|
||||||
|
const char *bandwidth_help; /**< @brief Set send rate in bits/second (supports suffixes G, M and K) help description. */
|
||||||
|
int cooldown_time_arg; /**< @brief How long to continue receiving after sending last probe (default='8'). */
|
||||||
|
char * cooldown_time_orig; /**< @brief How long to continue receiving after sending last probe original value given at command line. */
|
||||||
|
const char *cooldown_time_help; /**< @brief How long to continue receiving after sending last probe help description. */
|
||||||
|
int seed_arg; /**< @brief Seed used to select address permutation. */
|
||||||
|
char * seed_orig; /**< @brief Seed used to select address permutation original value given at command line. */
|
||||||
|
const char *seed_help; /**< @brief Seed used to select address permutation help description. */
|
||||||
|
int sender_threads_arg; /**< @brief Threads used to send packets (default='1'). */
|
||||||
|
char * sender_threads_orig; /**< @brief Threads used to send packets original value given at command line. */
|
||||||
|
const char *sender_threads_help; /**< @brief Threads used to send packets help description. */
|
||||||
|
int probes_arg; /**< @brief Number of probes to send to each IP (default='1'). */
|
||||||
|
char * probes_orig; /**< @brief Number of probes to send to each IP original value given at command line. */
|
||||||
|
const char *probes_help; /**< @brief Number of probes to send to each IP help description. */
|
||||||
|
const char *dryrun_help; /**< @brief Don't actually send packets help description. */
|
||||||
|
char * source_port_arg; /**< @brief Source port(s) for scan packets. */
|
||||||
|
char * source_port_orig; /**< @brief Source port(s) for scan packets original value given at command line. */
|
||||||
|
const char *source_port_help; /**< @brief Source port(s) for scan packets help description. */
|
||||||
|
char * source_ip_arg; /**< @brief Source address(es) for scan packets. */
|
||||||
|
char * source_ip_orig; /**< @brief Source address(es) for scan packets original value given at command line. */
|
||||||
|
const char *source_ip_help; /**< @brief Source address(es) for scan packets help description. */
|
||||||
|
char * gateway_mac_arg; /**< @brief Specify gateway MAC address. */
|
||||||
|
char * gateway_mac_orig; /**< @brief Specify gateway MAC address original value given at command line. */
|
||||||
|
const char *gateway_mac_help; /**< @brief Specify gateway MAC address help description. */
|
||||||
|
char * interface_arg; /**< @brief Specify network interface to use. */
|
||||||
|
char * interface_orig; /**< @brief Specify network interface to use original value given at command line. */
|
||||||
|
const char *interface_help; /**< @brief Specify network interface to use help description. */
|
||||||
|
char * probe_module_arg; /**< @brief Select probe module (default='tcp_synscan'). */
|
||||||
|
char * probe_module_orig; /**< @brief Select probe module original value given at command line. */
|
||||||
|
const char *probe_module_help; /**< @brief Select probe module help description. */
|
||||||
|
char * output_module_arg; /**< @brief Select output module (default='simple_file'). */
|
||||||
|
char * output_module_orig; /**< @brief Select output module original value given at command line. */
|
||||||
|
const char *output_module_help; /**< @brief Select output module help description. */
|
||||||
|
char * probe_args_arg; /**< @brief Arguments to pass to probe module. */
|
||||||
|
char * probe_args_orig; /**< @brief Arguments to pass to probe module original value given at command line. */
|
||||||
|
const char *probe_args_help; /**< @brief Arguments to pass to probe module help description. */
|
||||||
|
char * output_args_arg; /**< @brief Arguments to pass to output module. */
|
||||||
|
char * output_args_orig; /**< @brief Arguments to pass to output module original value given at command line. */
|
||||||
|
const char *output_args_help; /**< @brief Arguments to pass to output module help description. */
|
||||||
|
const char *list_output_modules_help; /**< @brief List available output modules help description. */
|
||||||
|
const char *list_probe_modules_help; /**< @brief List available probe modules help description. */
|
||||||
|
char * config_arg; /**< @brief Read a configuration file, which can specify any of these options (default='/etc/zmap/zmap.conf'). */
|
||||||
|
char * config_orig; /**< @brief Read a configuration file, which can specify any of these options original value given at command line. */
|
||||||
|
const char *config_help; /**< @brief Read a configuration file, which can specify any of these options help description. */
|
||||||
|
const char *quiet_help; /**< @brief Do not print status updates help description. */
|
||||||
|
const char *summary_help; /**< @brief Print configuration and summary at end of scan help description. */
|
||||||
|
int verbosity_arg; /**< @brief Level of log detail (0-5) (default='3'). */
|
||||||
|
char * verbosity_orig; /**< @brief Level of log detail (0-5) original value given at command line. */
|
||||||
|
const char *verbosity_help; /**< @brief Level of log detail (0-5) help description. */
|
||||||
|
const char *help_help; /**< @brief Print help and exit help description. */
|
||||||
|
const char *version_help; /**< @brief Print version and exit help description. */
|
||||||
|
|
||||||
|
unsigned int target_port_given ; /**< @brief Whether target-port was given. */
|
||||||
|
unsigned int output_file_given ; /**< @brief Whether output-file was given. */
|
||||||
|
unsigned int blacklist_file_given ; /**< @brief Whether blacklist-file was given. */
|
||||||
|
unsigned int whitelist_file_given ; /**< @brief Whether whitelist-file was given. */
|
||||||
|
unsigned int max_targets_given ; /**< @brief Whether max-targets was given. */
|
||||||
|
unsigned int max_results_given ; /**< @brief Whether max-results was given. */
|
||||||
|
unsigned int max_runtime_given ; /**< @brief Whether max-runtime was given. */
|
||||||
|
unsigned int rate_given ; /**< @brief Whether rate was given. */
|
||||||
|
unsigned int bandwidth_given ; /**< @brief Whether bandwidth was given. */
|
||||||
|
unsigned int cooldown_time_given ; /**< @brief Whether cooldown-time was given. */
|
||||||
|
unsigned int seed_given ; /**< @brief Whether seed was given. */
|
||||||
|
unsigned int sender_threads_given ; /**< @brief Whether sender-threads was given. */
|
||||||
|
unsigned int probes_given ; /**< @brief Whether probes was given. */
|
||||||
|
unsigned int dryrun_given ; /**< @brief Whether dryrun was given. */
|
||||||
|
unsigned int source_port_given ; /**< @brief Whether source-port was given. */
|
||||||
|
unsigned int source_ip_given ; /**< @brief Whether source-ip was given. */
|
||||||
|
unsigned int gateway_mac_given ; /**< @brief Whether gateway-mac was given. */
|
||||||
|
unsigned int interface_given ; /**< @brief Whether interface was given. */
|
||||||
|
unsigned int probe_module_given ; /**< @brief Whether probe-module was given. */
|
||||||
|
unsigned int output_module_given ; /**< @brief Whether output-module was given. */
|
||||||
|
unsigned int probe_args_given ; /**< @brief Whether probe-args was given. */
|
||||||
|
unsigned int output_args_given ; /**< @brief Whether output-args was given. */
|
||||||
|
unsigned int list_output_modules_given ; /**< @brief Whether list-output-modules was given. */
|
||||||
|
unsigned int list_probe_modules_given ; /**< @brief Whether list-probe-modules was given. */
|
||||||
|
unsigned int config_given ; /**< @brief Whether config was given. */
|
||||||
|
unsigned int quiet_given ; /**< @brief Whether quiet was given. */
|
||||||
|
unsigned int summary_given ; /**< @brief Whether summary was given. */
|
||||||
|
unsigned int verbosity_given ; /**< @brief Whether verbosity was given. */
|
||||||
|
unsigned int help_given ; /**< @brief Whether help was given. */
|
||||||
|
unsigned int version_given ; /**< @brief Whether version was given. */
|
||||||
|
|
||||||
|
} ;
|
||||||
|
|
||||||
|
/** @brief The additional parameters to pass to parser functions */
|
||||||
|
struct cmdline_parser_params
|
||||||
|
{
|
||||||
|
int override; /**< @brief whether to override possibly already present options (default 0) */
|
||||||
|
int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */
|
||||||
|
int check_required; /**< @brief whether to check that all required options were provided (default 1) */
|
||||||
|
int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */
|
||||||
|
int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */
|
||||||
|
} ;
|
||||||
|
|
||||||
|
/** @brief the purpose string of the program */
|
||||||
|
extern const char *gengetopt_args_info_purpose;
|
||||||
|
/** @brief the usage string of the program */
|
||||||
|
extern const char *gengetopt_args_info_usage;
|
||||||
|
/** @brief all the lines making the help output */
|
||||||
|
extern const char *gengetopt_args_info_help[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The command line parser
|
||||||
|
* @param argc the number of command line options
|
||||||
|
* @param argv the command line options
|
||||||
|
* @param args_info the structure where option information will be stored
|
||||||
|
* @return 0 if everything went fine, NON 0 if an error took place
|
||||||
|
*/
|
||||||
|
int cmdline_parser (int argc, char **argv,
|
||||||
|
struct gengetopt_args_info *args_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The command line parser (version with additional parameters - deprecated)
|
||||||
|
* @param argc the number of command line options
|
||||||
|
* @param argv the command line options
|
||||||
|
* @param args_info the structure where option information will be stored
|
||||||
|
* @param override whether to override possibly already present options
|
||||||
|
* @param initialize whether to initialize the option structure my_args_info
|
||||||
|
* @param check_required whether to check that all required options were provided
|
||||||
|
* @return 0 if everything went fine, NON 0 if an error took place
|
||||||
|
* @deprecated use cmdline_parser_ext() instead
|
||||||
|
*/
|
||||||
|
int cmdline_parser2 (int argc, char **argv,
|
||||||
|
struct gengetopt_args_info *args_info,
|
||||||
|
int override, int initialize, int check_required);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The command line parser (version with additional parameters)
|
||||||
|
* @param argc the number of command line options
|
||||||
|
* @param argv the command line options
|
||||||
|
* @param args_info the structure where option information will be stored
|
||||||
|
* @param params additional parameters for the parser
|
||||||
|
* @return 0 if everything went fine, NON 0 if an error took place
|
||||||
|
*/
|
||||||
|
int cmdline_parser_ext (int argc, char **argv,
|
||||||
|
struct gengetopt_args_info *args_info,
|
||||||
|
struct cmdline_parser_params *params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the contents of the option struct into an already open FILE stream.
|
||||||
|
* @param outfile the stream where to dump options
|
||||||
|
* @param args_info the option struct to dump
|
||||||
|
* @return 0 if everything went fine, NON 0 if an error took place
|
||||||
|
*/
|
||||||
|
int cmdline_parser_dump(FILE *outfile,
|
||||||
|
struct gengetopt_args_info *args_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the contents of the option struct into a (text) file.
|
||||||
|
* This file can be read by the config file parser (if generated by gengetopt)
|
||||||
|
* @param filename the file where to save
|
||||||
|
* @param args_info the option struct to save
|
||||||
|
* @return 0 if everything went fine, NON 0 if an error took place
|
||||||
|
*/
|
||||||
|
int cmdline_parser_file_save(const char *filename,
|
||||||
|
struct gengetopt_args_info *args_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print the help
|
||||||
|
*/
|
||||||
|
void cmdline_parser_print_help(void);
|
||||||
|
/**
|
||||||
|
* Print the version
|
||||||
|
*/
|
||||||
|
void cmdline_parser_print_version(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes all the fields a cmdline_parser_params structure
|
||||||
|
* to their default values
|
||||||
|
* @param params the structure to initialize
|
||||||
|
*/
|
||||||
|
void cmdline_parser_params_init(struct cmdline_parser_params *params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocates dynamically a cmdline_parser_params structure and initializes
|
||||||
|
* all its fields to their default values
|
||||||
|
* @return the created and initialized cmdline_parser_params structure
|
||||||
|
*/
|
||||||
|
struct cmdline_parser_params *cmdline_parser_params_create(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the passed gengetopt_args_info structure's fields
|
||||||
|
* (also set default values for options that have a default)
|
||||||
|
* @param args_info the structure to initialize
|
||||||
|
*/
|
||||||
|
void cmdline_parser_init (struct gengetopt_args_info *args_info);
|
||||||
|
/**
|
||||||
|
* Deallocates the string fields of the gengetopt_args_info structure
|
||||||
|
* (but does not deallocate the structure itself)
|
||||||
|
* @param args_info the structure to deallocate
|
||||||
|
*/
|
||||||
|
void cmdline_parser_free (struct gengetopt_args_info *args_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The config file parser (deprecated version)
|
||||||
|
* @param filename the name of the config file
|
||||||
|
* @param args_info the structure where option information will be stored
|
||||||
|
* @param override whether to override possibly already present options
|
||||||
|
* @param initialize whether to initialize the option structure my_args_info
|
||||||
|
* @param check_required whether to check that all required options were provided
|
||||||
|
* @return 0 if everything went fine, NON 0 if an error took place
|
||||||
|
* @deprecated use cmdline_parser_config_file() instead
|
||||||
|
*/
|
||||||
|
int cmdline_parser_configfile (const char *filename,
|
||||||
|
struct gengetopt_args_info *args_info,
|
||||||
|
int override, int initialize, int check_required);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The config file parser
|
||||||
|
* @param filename the name of the config file
|
||||||
|
* @param args_info the structure where option information will be stored
|
||||||
|
* @param params additional parameters for the parser
|
||||||
|
* @return 0 if everything went fine, NON 0 if an error took place
|
||||||
|
*/
|
||||||
|
int cmdline_parser_config_file (const char *filename,
|
||||||
|
struct gengetopt_args_info *args_info,
|
||||||
|
struct cmdline_parser_params *params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that all the required options were specified
|
||||||
|
* @param args_info the structure to check
|
||||||
|
* @param prog_name the name of the program that will be used to print
|
||||||
|
* possible errors
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int cmdline_parser_required (struct gengetopt_args_info *args_info,
|
||||||
|
const char *prog_name);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
#endif /* ZOPT_H */
|
Loading…
Reference in New Issue
Block a user