This commit is contained in:
delorean 2024-02-21 18:23:18 -06:00
commit 3749a2612a
Signed by: delorean
GPG Key ID: 08CFF8565BE941CD
202 changed files with 20512 additions and 0 deletions

11
.clang-format Normal file
View File

@ -0,0 +1,11 @@
BasedOnStyle: LLVM
IndentWidth: 8
UseTab: Always
BreakBeforeBraces: Linux
AllowShortIfStatementsOnASingleLine: false
IndentCaseLabels: false
DerivePointerAlignment: false
PointerAlignment: Right
BreakStringLiterals: false
SortIncludes: false
ReflowComments: false

18
.editorconfig Normal file
View File

@ -0,0 +1,18 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
[*.{c,h}]
indent_style = tab
indent_size = 8
[CMakeLists.txt]
indent_style = spaces
indent_size = 4
[*.py]
indent_style = spaces
indent_size = 4

38
.gitignore vendored Normal file
View File

@ -0,0 +1,38 @@
!.clang-format
*.o
*.a
*.pyc
*.pyo
*~
\#*
src/zmap
src/zopt.h
src/zopt.c
src/zbopt.h
src/zbopt.c
src/zitopt.c
src/zitopt.h
src/topt.c
src/topt.h
src/ztopt.c
src/ztopt.h
src/*.ggo
src/zblocklist
src/ztee
src/ziterate
lexer.c
lexer.h
parser.c
parser.h
*.deb
!.gitignore
!.travis.yml
cmake_installer.sh
CMakeFiles
*.cmake
Makefile
CMakeCache.txt
install_manifest.txt
_CPack_Packages/*
src/ztests
*.DS_Store

57
10gigE.md Normal file
View File

@ -0,0 +1,57 @@
10GigE (Zippier) ZMap
===========
It is possible to build ZMap to run at 95% of 10 GigE linespeed, sending over 14
million packets per second. This requires a compatible Intel 10 Gbps Ethernet
NIC and Linux.
### Prerequisites
0. A working ZMap development environment (see [INSTALL.md](install.md))
1. A [PF_RING ZC](http://www.ntop.org/products/pf_ring/pf_ring-zc-zero-copy/)
license from ntop.
2. PF_RING ZC headers and kernel module
3. A 10 Gbps NIC with compatible "PF_RING-aware" drivers
4. A Linux (not BSD or Mac) installation
5. For best results, a computer with at least 8 *physical* cores on the same
NUMA node.
6. libnuma (`sudo apt-get install libnuma-dev`)
### Building
Most build errors are due to incorrectly building or installing PF_RING. Make
sure you have build the drivers, the kernel module, and the userland library, as
well as install the headers and kernel module to the correct locations.
The PF_RING `make install` command might not copy `pfring_zc.h` to
`/usr/include`, in which case manually install the file and set permissions
correctly.
To build navigate to the root of the repository and run:
```
$ cmake -DWITH_PFRING=ON -DENABLE_DEVELOPMENT=OFF .
$ make
```
### Running
You'll have to carefully select the number of threads to use, as well as specify
as zero-copy interface, e.g. `zc:eth1`. Use the `--cores` option to pick which
cores to pin to. Make sure to pin to different physical cores, and note that
some machines interleave physical and "virtual" cores.
```
$ sudo ./src/zmap -p 80 -i zc:eth7 -o output.csv -T 5
```
### Considerations
DO NOT TAKE THIS LIGHTLY!
Running ZMap at 10Gbps hits every /16 on the Internet over 200 times a second.
Even if you have a large source IP range to scan from, it's very obvious that
you're scanning. As always, follow scanning best practices, honor blocklist
requests, and signal benign/research intent via domain names and websites on
your scan IPs.
Remember, you're sending a lot of traffic.

168
CMakeLists.txt Normal file
View File

@ -0,0 +1,168 @@
cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
project(ZMAP C)
set(ZMAP_VERSION DEVELOPMENT) # Change DEVELOPMENT to version number for release
option(ENABLE_DEVELOPMENT "Enable development specific compiler and linker flags" OFF)
option(ENABLE_LOG_TRACE "Enable log trace messages" OFF)
option(RESPECT_INSTALL_PREFIX_CONFIG "Respect CMAKE_INSTALL_PREFIX for /etc" OFF)
option(WITH_WERROR "Build with -Werror" OFF)
option(WITH_PFRING "Build with PF_RING ZC for send (10 GigE)" OFF)
option(FORCE_CONF_INSTALL "Overwrites existing configuration files at install" OFF)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(USING_CLANG "YES")
else()
set(USING_GCC "YES")
endif()
if("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD" OR "${CMAKE_SYSTEM_NAME}" MATCHES "NetBSD" OR "${CMAKE_SYSTEM_NAME}" MATCHES "DragonFly")
set(BSD "YES")
endif()
if("${CMAKE_SYSTEM_NAME}" MATCHES "NetBSD")
set(NetBSD "YES")
endif()
# Hardening and warnings for building with gcc
# Maybe add -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations
set(GCCWARNINGS
"-Wall -Wformat=2 -Wno-format-nonliteral"
"-pedantic -fno-strict-aliasing"
"-Wextra"
"-Wfloat-equal -Wundef -Wwrite-strings -Wredundant-decls"
"-Wnested-externs -Wbad-function-cast -Winit-self"
"-Wmissing-noreturn"
"-Wstack-protector"
)
# Fix line breaks
string(REPLACE ";" " " GCCWARNINGS "${GCCWARNINGS}")
if(WITH_WERROR)
set(GCCWARNINGS "${GCCWARNINGS} -Werror")
endif()
if(ENABLE_DEVELOPMENT)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -g")
else()
# Hardening and optimizations for building with gcc
set(GCCHARDENING "-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fstack-protector-all -fwrapv -fPIC --param ssp-buffer-size=1")
if(NOT APPLE AND NOT BSD)
set(LDHARDENING "-z relro -z now")
else()
set(LDHARDENING "")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GCCHARDENING} -O2")
set(CMAKE_EXE_LINKER_FLAGS "${LDHARDENING} ${CMAKE_EXE_LINKER_FLAGS}")
endif()
if(ENABLE_LOG_TRACE)
add_definitions("-DDEBUG")
endif()
set(CMAKE_C_FLAGS "${GCCWARNINGS} ${CMAKE_C_FLAGS}")
include(FindPkgConfig)
pkg_check_modules(JSON json-c)
if(JSON_FOUND)
include_directories(${JSON_INCLUDE_DIRS})
else()
message(FATAL_ERROR "Did not find libjson")
endif()
string(REPLACE ";" " " JSON_CFLAGS "${JSON_CFLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${JSON_CFLAGS}")
if(WITH_PFRING)
add_definitions("-DPFRING")
set(PFRING_LIBRARIES pfring rt numa)
endif()
set(JUDY_LIBRARIES "Judy")
# Standard FLAGS
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")
if(NOT APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread")
endif()
# Set up OS-specific include directories
if(APPLE)
if(EXISTS /opt/local/include)
include_directories(/opt/local/include)
endif()
if(EXISTS /opt/local/lib)
link_directories(/opt/local/lib)
endif()
if(EXISTS /usr/local/include)
include_directories(/usr/local/include)
endif()
if(EXISTS /usr/local/lib)
link_directories(/usr/local/lib)
endif()
if(EXISTS /opt/homebrew)
include_directories(/opt/homebrew/include)
link_directories(/opt/homebrew/lib)
endif()
endif()
if(BSD)
include_directories(/usr/local/include)
link_directories(/usr/local/lib)
endif()
if(NetBSD)
include_directories(/usr/pkg/include)
link_directories(/usr/pkg/lib)
endif()
add_subdirectory(lib)
add_subdirectory(src)
# Install conf files
if(RESPECT_INSTALL_PREFIX_CONFIG)
set(CONFIG_DESTINATION "etc/zmap")
else()
set(CONFIG_DESTINATION "/etc/zmap")
endif()
FILE(GLOB CONF_FILES "${PROJECT_SOURCE_DIR}/conf/*")
message(STATUS "Default ZMap configuration file location is /etc/zmap")
foreach(EACH_CONF ${CONF_FILES})
get_filename_component(CONF_BASENAME ${EACH_CONF} NAME)
message(STATUS "Checking if ${CONF_BASENAME} exists there...")
if(NOT EXISTS "${CONFIG_DESTINATION}/${CONF_BASENAME}")
install(FILES ${EACH_CONF} DESTINATION ${CONFIG_DESTINATION})
elseif(FORCE_CONF_INSTALL)
message(WARNING "FORCE_CONF_INSTALL will overwrite any existing configuration files")
install(FILES ${EACH_CONF} DESTINATION ${CONFIG_DESTINATION})
else()
message(WARNING "Existing configuration file detected at /etc/zmap/${CONF_BASENAME}, ${CONF_BASENAME} from sources will NOT be installed. Please check and install manually!")
endif()
endforeach()
# Allow Debian Packaging
include(InstallRequiredSystemLibraries)
set(CPACK_SET_DESTDIR "on")
set(CPACK_PACKAGING_INSTALL_PREFIX "/tmp")
set(CPACK_GENERATOR "DEB")
set(CPACK_DEBIAN_PACKAGE_VERSION ${ZMAP_VERSION})
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set(CPACK_DEBIAN_PACKAGE_SECTION "network")
set(CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.1.3), libgmp10, libpcap0.8, libjson-c-dev")
set(CPACK_PACKAGE_DESCRIPTION "Internet-scale network scanner")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "ZMap is an open source network scanner that enables researchers to easily perform Internet-wide network studies. With a single machine and a well provisioned network uplink, ZMap is capable of performing a complete scan of the IPv4 address space in under five minutes, approaching the theoretical limit of gigabit Ethernet. ZMap can be used to study protocol adoption over time, monitor service availability, and help us better understand large systems distributed across the Internet.")
set(CPACK_PACKAGE_CONTACT "Zakir Durumeric <zakird@gmail.com>")
set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${VERSION}_${CPACK_DEBIAN_ARCHITECTURE}")
set(CPACK_COMPONENTS_ALL Libraries ApplicationData)
include(CPack)

36
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,36 @@
Contributing to ZMap
====================
ZMap accepts contributions in the form of issues and pull requests. In either
case, before posting please [search](https://github.com/zmap/zmap/issues) to see
if your change or bug report has been addressed previously.
[INSTALL](INSTALL.md#building-from-source) provides guidance on building ZMap
from source.
Developing
----------
- ZMap code follows the [Linux kernel style guide][kernelguide]. We maintain [a
configuration file](/.clang-format) for `clang-format` that applies this
style. You can use the [format.sh](/format.sh) script to apply this style.
- Before submitting a PR, please rebase/squash your commits down to a single
commit. Follow these [commit message guidelines][guidelines], especially with
regard to formatting.
Reviewing
---------
- All commits must be reviewed in the form of a pull request by a ZMap
maintainer. This usually means @zakird or @dadrian (or both).
- All pull-requests should be squash-merged into master.
- When squash-merging, put the PR number in the commit title. GitHub does this
automatically in the web interface. Condense the commit messages down to a
single message; often this can just be the commit message from the first
commit in a PR. Follow the commit formatting guidelines [here][guidelines].
[kernelguide]: https://www.kernel.org/doc/Documentation/process/coding-style.rst
[guidelines]: https://github.com/torvalds/subsurface-for-dirk/blob/master/README#L92

20
checkFormat.sh Executable file
View File

@ -0,0 +1,20 @@
#!/bin/bash
CLANG_FORMAT=clang-format-6.0
files_to_lint=$(find ./src ./lib -type f -name '*.c' -or -name '*.h')
fail=0
for f in ${files_to_lint}; do
d="$(diff -u "$f" <($CLANG_FORMAT -style=file "$f") || true)"
if ! [ -z "$d" ]; then
printf "The file %s is not compliant with the coding style:\n%s\n" "$f" "$d"
fail=1
fi
done
if [ "$fail" -eq "1" ]; then
if [ ! -z $ZMAP_ENFORCE_FORMAT ]; then
exit 1
fi
fi

25
conf/blocklist.conf Normal file
View 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
198.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
View 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
### Blocklist 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.
blocklist-file "/etc/zmap/blocklist.conf"
### Optionally print a summary at the end
#summary

View File

@ -0,0 +1,4 @@
Forge Socket
============
Forge Socket is now maintained at https://github.com/ewust/forge_socket.

View File

@ -0,0 +1,203 @@
/*
* 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 <assert.h>
#include "../../lib/includes.h"
#include "../fieldset.h"
#include "probe_modules.h"
#include "packet.h"
probe_module_t module_tcp_synscan;
static uint32_t num_ports;
static int synscan_global_initialize(struct state_conf *state)
{
num_ports = state->source_port_last - state->source_port_first + 1;
return EXIT_SUCCESS;
}
static int synscan_init_perthread(void *buf, macaddr_t *src, macaddr_t *gw,
port_h_t dst_port,
__attribute__((unused)) void **arg_ptr)
{
memset(buf, 0, MAX_PACKET_SIZE);
struct ether_header *eth_header = (struct ether_header *)buf;
make_eth_header(eth_header, src, gw);
struct ip *ip_header = (struct ip *)(&eth_header[1]);
uint16_t len = htons(sizeof(struct ip) + 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, TH_SYN);
return EXIT_SUCCESS;
}
// instead of settings sequence number to be random for validation
// let's instead set to something static so that we can easily
// set acknowledgement number. I don't know how integer overflow
// is going to act in this.
// uint32_t tcp_seq = validation[0];
// From Mandiant
// 1. To initiate the process, a uniquely crafted TCP SYN packet is sent
// to port 80 of the “implanted” router. It is important to note that
// the difference between the sequence and acknowledgment numbers must
// be set to 0xC123D. Also the ACK number doesnt need to be zero.
#define BACKDOOR_SEQ 0x3D120C00
//#define BACKDOOR_SEQ 0x000C123D // wrong byte order
#define BACKDOOR_ACK 0x0
#define EXPECTED_RESPONSE_SEQ 0
//#define EXPECTED_RESPONSE_ACK 0x000C123E // wrong byte order
#define EXPECTED_RESPONSE_ACK 0x3E120C00
static int synscan_make_packet(void *buf, UNUSED size_t *buf_len,
ipaddr_n_t src_ip, ipaddr_n_t dst_ip, uint8_t ttl,
uint32_t *validation, int probe_num,
UNUSED void *arg)
{
struct ether_header *eth_header = (struct ether_header *)buf;
struct ip *ip_header = (struct ip *)(&eth_header[1]);
struct tcphdr *tcp_header = (struct tcphdr *)(&ip_header[1]);
ip_header->ip_src.s_addr = src_ip;
ip_header->ip_dst.s_addr = dst_ip;
ip_header->ip_ttl = ttl;
tcp_header->th_sport =
htons(get_src_port(num_ports, probe_num, validation));
tcp_header->th_seq = BACKDOOR_SEQ;
tcp_header->th_ack = BACKDOOR_ACK;
tcp_header->th_sum = 0;
tcp_header->th_sum =
tcp_checksum(sizeof(struct tcphdr), ip_header->ip_src.s_addr,
ip_header->ip_dst.s_addr, tcp_header);
ip_header->ip_sum = 0;
ip_header->ip_sum = zmap_ip_checksum((unsigned short *)ip_header);
return EXIT_SUCCESS;
}
static void synscan_print_packet(FILE *fp, void *packet)
{
struct ether_header *ethh = (struct ether_header *)packet;
struct ip *iph = (struct ip *)&ethh[1];
struct tcphdr *tcph = (struct tcphdr *)&iph[1];
fprintf(fp,
"tcp { source: %u | dest: %u | seq: %u | checksum: %#04X }\n",
ntohs(tcph->th_sport), ntohs(tcph->th_dport),
ntohl(tcph->th_seq), ntohs(tcph->th_sum));
fprintf_ip_header(fp, iph);
fprintf_eth_header(fp, ethh);
fprintf(fp, "------------------------------------------------------\n");
}
static int synscan_validate_packet(const struct ip *ip_hdr, uint32_t len,
__attribute__((unused)) uint32_t *src_ip,
uint32_t *validation)
{
if (ip_hdr->ip_p != IPPROTO_TCP) {
return 0;
}
if ((4 * ip_hdr->ip_hl + 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->ip_hl);
uint16_t sport = tcp->th_sport;
uint16_t dport = tcp->th_dport;
// validate source port
if (ntohs(sport) != zconf.target_port) {
return 0;
}
// validate destination port
if (!check_dst_port(ntohs(dport), num_ports, validation)) {
return 0;
}
// DO NOT validate ack number as this is currently statically set
// validate tcp acknowledgement number
// if (htonl(tcp->th_ack) != htonl(validation[0])+1) {
// return 0;
//}
return 1;
}
static void synscan_process_packet(const u_char *packet, uint32_t len,
fieldset_t *fs,
__attribute__((unused)) uint32_t *validation,
__attribute__((unused)) struct timespec ts)
{
struct ip *ip_hdr = (struct ip *)&packet[sizeof(struct ether_header)];
struct tcphdr *tcp =
(struct tcphdr *)((char *)ip_hdr + 4 * ip_hdr->ip_hl);
fs_add_uint64(fs, "sport", (uint64_t)ntohs(tcp->th_sport));
fs_add_uint64(fs, "dport", (uint64_t)ntohs(tcp->th_dport));
fs_add_uint64(fs, "seqnum", (uint64_t)ntohl(tcp->th_seq));
fs_add_uint64(fs, "acknum", (uint64_t)ntohl(tcp->th_ack));
fs_add_uint64(fs, "window", (uint64_t)ntohs(tcp->th_win));
fs_add_uint64(fs, "urgentptr", (uint64_t)ntohs(tcp->th_urp));
fs_add_uint64(fs, "flags", (uint64_t)ntohs(tcp->th_flags));
fs_add_binary(fs, "raw", len, (void *)packet, 0);
if (tcp->th_flags & TH_RST) { // RST packet
fs_add_string(fs, "classification", (char *)"rst", 0);
fs_add_bool(fs, "success", 0);
} else if (tcp->th_seq == EXPECTED_RESPONSE_SEQ && tcp->th_urp) {
fs_add_string(fs, "classification", (char *)"backdoor", 0);
fs_add_bool(fs, "success", 1);
} else { // SYNACK packet
fs_add_string(fs, "classification", (char *)"synack", 0);
fs_add_bool(fs, "success", 1);
}
}
static fielddef_t fields[] = {
{.name = "sport", .type = "int", .desc = "TCP source port"},
{.name = "dport", .type = "int", .desc = "TCP destination port"},
{.name = "seqnum", .type = "int", .desc = "TCP sequence number"},
{.name = "acknum", .type = "int", .desc = "TCP acknowledgement number"},
{.name = "window", .type = "int", .desc = "TCP window"},
{.name = "urgentptr", .type = "int", .desc = "Urgent POinter"},
{.name = "flags", .type = "int", .desc = "tcp flags"},
{.name = "raw", .type = "binary", .desc = "raw packet"},
{.name = "classification",
.type = "string",
.desc = "packet classification"},
{.name = "success",
.type = "bool",
.desc = "is response considered success"}};
probe_module_t module_tcp_cisco_backdoor = {
.name = "tcp_cisco_backdoor",
.packet_length = 54,
.pcap_filter = "tcp && tcp[13] & 4 != 0 || tcp[13] == 18",
.pcap_snaplen = 256,
.port_args = 1,
.global_initialize = &synscan_global_initialize,
.thread_initialize = &synscan_init_perthread,
.make_packet = &synscan_make_packet,
.print_packet = &synscan_print_packet,
.process_packet = &synscan_process_packet,
.validate_packet = &synscan_validate_packet,
.close = NULL,
.helptext = "Probe module that sends a TCP SYN packet to a specific "
"port. Possible classifications are: synack and rst. A "
"SYN-ACK packet is considered a success and a reset packet "
"is considered a failed response.",
.output_type = OUTPUT_TYPE_STATIC,
.fields = fields,
.numfields = 10};

View File

@ -0,0 +1,51 @@
UDP Data Probes
======
This directory contains a set of data files that can be used with the UDP probe module.
USING:
-----
$ zmap -M udp -p 137 --probe-args=file:examples/udp-probes/netbios_137.pkt
PROBES:
-----
citrix_1604.pkt This probe triggers a response from Citrix application discovery services on UDP port 1604
db2disco_523.pkt This probe triggers a response from IBM DB2 discovery services on UDP port 523
digi1_2362.pkt This probe triggers a response from Digi ADDP discovery services on UDP port 2362 (default magic)
digi2_2362.pkt This probe triggers a response from Digi ADDP discovery services on UDP port 2362 (devkit magic)
digi3_2362.pkt This probe triggers a response from Digi ADDP discovery services on UDP port 2362 (oem magic)
dns_53.pkt This probe queries for the DNS vendor and version using the BIND version TXT record over UDP port 53
dns_53_queryAwww.google.it.pkt This probe queries for the domain www.google.it A record over UDP port 53
dns_53_queryAwww.google.com.pkt This probe queries for the domain www.google.com A record over UDP port 53
ipmi_623.pkt This probe triggers a Get Channel Authentication reply from IPMI endpoints on UDP port 623
mdns_5353.pkt This probe triggers a response from mDNS/Avahi/Bonjour discovery services on UDP port 5353
memcache_11211.pkt This probe triggers a response from memcached on UDP port 11211 (stats items).
mssql_1434.pkt This probe triggers a response from Microsoft SQL Server discovery services on UDP port 1434
natpmp_5351.pkt This probe triggers a response from NATPMP-enabled devices on UDP port 5351
netbios_137.pkt This probe triggers a status reply from NetBIOS services on UDP port 137
ntp_123.pkt This probe triggers a response from NTP services on UDP port 123
ntp_123_monlist.pkt This probe triggers a response for command "monlist" from NTP services on UDP port 123
pca_nq_5632.pkt This probe triggers a response from PC Anywhere services on UDP port 5632 (network query)
pca_st_5632.pkt This probe triggers a response from PC Anywhere services on UDP port 5632 (status)
portmap_111.pkt This probe triggers a response from SunRPC portmapper services on UDP port 111
ripv1_520.pkt This probe triggers a response from the RIPv1 enabled routers/devices on UDP port 520
sentinel_5093.pkt This probe triggers a response from the Sentinel license manager service on UDP port 5093
snmp1_161.pkt This probe queries for the system description field of SNMP v1 services using community string public over UDP port 161
snmp2_161.pkt This probe queries for the system description field of SNMP v2 services using community string public over UDP port 161
snmp3_161.pkt This probe triggers a response from SNMP v3 services on UDP port 161
upnp_1900.pkt This probe triggers a response from UPnP SSDP services on UDP port 1900
wdbrpc_17185.pkt This probe triggers a response from VxWorks WDBRPC services on UDP port 17185
wsd_3702.pkt This probe triggers a response from WSD/DPWS services on UDP port 3702
coap_5683.pkt This probe triggers a response from COAP services on UDP port 5683
NOTES:
-----
Most of these probes return useful data in the response. Parsing this data requires capturing the raw output
and decoding this using a protocol-specific dissector. In most cases, Wireshark is capable of decoding these
replies.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@


Binary file not shown.

View File

@ -0,0 +1 @@
@}p».well-knowncore

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@


Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
NQ

View File

@ -0,0 +1 @@
ST

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
OPTIONS

View File

@ -0,0 +1,12 @@
OPTIONS sip:${RAND_ALPHA=8}@${DADDR} SIP/2.0
Via: SIP/2.0/UDP ${SADDR}:${SPORT};branch=${RAND_ALPHA=6}.${RAND_DIGIT=10};rport;alias
From: sip:${RAND_ALPHA=8}@${SADDR}:${SPORT};tag=${RAND_DIGIT=8}
To: sip:${RAND_ALPHA=8}@${DADDR}
Call-ID: ${RAND_DIGIT=10}@${SADDR}
CSeq: 1 OPTIONS
Contact: sip:${RAND_ALPHA=8}@${SADDR}:${SPORT}
Content-Length: 0
Max-Forwards: 20
User-Agent: ${RAND_ALPHA=8}
Accept: text/plain

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,5 @@
M-SEARCH * HTTP/1.1
HOST:239.255.255.250:1900
ST:ssdp:all
MAN:"ssdp:discover"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,7 @@
M-SEARCH * HTTP/1.1
Host:239.255.255.250:1900
ST:upnp:rootdevice
Man:"ssdp:discover"
MX:3

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsd="http://schemas.xmlsoap.org/ws/2005/04/discovery" xmlns:wsdp="http://schemas.xmlsoap.org/ws/2006/02/devprof">
<soap:Header><wsa:To>urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To><wsa:Action>http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action><wsa:MessageID>urn:uuid:ce04dad0-5d2c-4026-9146-1aabfc1e4111</wsa:MessageID></soap:Header><soap:Body><wsd:Probe><wsd:Types>wsdp:Device</wsd:Types></wsd:Probe></soap:Body></soap:Envelope>

View File

@ -0,0 +1 @@
<:/>

Binary file not shown.

24
format.sh Executable file
View File

@ -0,0 +1,24 @@
#!/bin/bash
set -e
set -o pipefail
MAJOR_REV=$((clang-format --version | awk '{print $3}' | cut -d '.' -f 1) || echo 0)
if [ $MAJOR_REV -lt 5 ]; then
echo "error: need at least clang-format version 5.x"
exit 1
fi
FORMAT_CMD="clang-format -i -style=file"
# No files passed, format everything
if [ $# -eq 0 ]; then
echo "formatting all C code in src/ and lib/"
find ./src -type f -name '*.c' -exec $FORMAT_CMD {} \;
find ./src -type f -name '*.h' -exec $FORMAT_CMD {} \;
find ./lib -type f -name '*.c' -exec $FORMAT_CMD {} \;
find ./lib -type f -name '*.h' -exec $FORMAT_CMD {} \;
exit 0
fi
# File names passed, format only those files
$FORMAT_CMD $@

23
lib/CMakeLists.txt Normal file
View File

@ -0,0 +1,23 @@
SET(LIB_SOURCES
blocklist.c
cachehash.c
constraint.c
logger.c
pbm.c
random.c
rijndael-alg-fst.c
xalloc.c
lockfd.c
util.c
queue.c
csv.c
)
add_library(zmaplib STATIC ${LIB_SOURCES})
target_link_libraries(
zmaplib
${JUDY_LIBRARIES}
)
target_include_directories (zmaplib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

308
lib/blocklist.c Normal file
View File

@ -0,0 +1,308 @@
/*
* Blocklist 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 <errno.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "blocklist.h"
#include "constraint.h"
#include "logger.h"
#include "xalloc.h"
#define ADDR_DISALLOWED 0
#define ADDR_ALLOWED 1
typedef struct bl_linked_list {
bl_cidr_node_t *first;
bl_cidr_node_t *last;
uint32_t len;
} bl_ll_t;
static constraint_t *constraint = NULL;
// keep track of the prefixes we've tried to BL/WL
// for logging purposes
static bl_ll_t *blocklisted_cidrs = NULL;
static bl_ll_t *allowlisted_cidrs = NULL;
void bl_ll_add(bl_ll_t *l, struct in_addr addr, uint16_t p)
{
assert(l);
bl_cidr_node_t *new = xmalloc(sizeof(bl_cidr_node_t));
new->next = NULL;
new->ip_address = addr.s_addr;
new->prefix_len = p;
if (!l->first) {
l->first = new;
} else {
l->last->next = new;
}
l->last = new;
l->len++;
}
bl_cidr_node_t *get_blocklisted_cidrs(void) { return blocklisted_cidrs->first; }
bl_cidr_node_t *get_allowlisted_cidrs(void) { return allowlisted_cidrs->first; }
uint32_t blocklist_lookup_index(uint64_t index)
{
return ntohl(constraint_lookup_index(constraint, index, ADDR_ALLOWED));
}
// check whether a single IP address is allowed to be scanned.
// 1 => is allowed
// 0 => is not allowed
int blocklist_is_allowed(uint32_t s_addr)
{
return constraint_lookup_ip(constraint, ntohl(s_addr)) == ADDR_ALLOWED;
}
static void _add_constraint(struct in_addr addr, int prefix_len, int value)
{
constraint_set(constraint, ntohl(addr.s_addr), prefix_len, value);
if (value == ADDR_ALLOWED) {
bl_ll_add(allowlisted_cidrs, addr, prefix_len);
} else if (value == ADDR_DISALLOWED) {
bl_ll_add(blocklisted_cidrs, addr, prefix_len);
} else {
log_fatal("blocklist",
"unknown type of blocklist operation specified");
}
}
// blocklist a CIDR network allocation
// e.g. blocklist_add("128.255.134.0", 24)
void blocklist_prefix(char *ip, int prefix_len)
{
struct in_addr addr;
addr.s_addr = inet_addr(ip);
_add_constraint(addr, prefix_len, ADDR_DISALLOWED);
}
// allowlist a CIDR network allocation
void allowlist_prefix(char *ip, int prefix_len)
{
struct in_addr addr;
addr.s_addr = inet_addr(ip);
_add_constraint(addr, prefix_len, ADDR_ALLOWED);
}
static int is_ip_ipv6(char *ip) {
// don't modify the input string
char *new_str = strdup(ip);
// check if there's a subnet mask_char
char *mask_char = strchr(new_str, '/');
if (mask_char != NULL) {
// set mask_char char to NULL char, so we can check if subnet is valid IPv6
*mask_char = '\0';
}
// attempt conversion of IP into IPv6 struct to check if IP is an IPv6 address
struct in6_addr ipv6_addr;
if (inet_pton(AF_INET6, new_str, &ipv6_addr) == 1) {
free(new_str);
return true;
}
free(new_str);
return false;
}
static int init_from_string(char *ip, int value)
{
if (is_ip_ipv6(ip)) {
log_debug("constraint", "ignoring IPv6 IP/subnet: %s", ip);
return 0;
}
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("constraint",
"'%s' is not a valid IPv4 prefix length", len);
return -1;
}
}
struct in_addr addr;
int ret = -1;
if (inet_aton(ip, &addr) == 0) {
// Not an IP and not a CIDR block, try dns resolution
struct addrinfo hint, *res;
memset(&hint, 0, sizeof(hint));
hint.ai_family = PF_INET;
int r = getaddrinfo(ip, NULL, &hint, &res);
if (r) {
log_error("constraint",
"'%s' is not a valid IP "
"address or hostname",
ip);
return -1;
}
// Got some addrinfo, let's see what happens
for (struct addrinfo *aip = res; aip; aip = aip->ai_next) {
if (aip->ai_family != AF_INET) {
continue;
}
struct sockaddr_in *sa =
(struct sockaddr_in *)aip->ai_addr;
memcpy(&addr, &sa->sin_addr, sizeof(addr));
log_debug("constraint", "%s retrieved by hostname",
inet_ntoa(addr));
ret = 0;
_add_constraint(addr, prefix_len, value);
}
} else {
_add_constraint(addr, prefix_len, value);
return 0;
}
return ret;
}
static int init_from_file(char *file, const char *name, int value,
int ignore_invalid_hosts)
{
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';
}
// hostnames can be up to 255 bytes
char ip[256];
if ((sscanf(line, "%255s", ip)) == EOF) {
continue;
}
if (init_from_string(ip, value)) {
if (!ignore_invalid_hosts) {
log_fatal(name, "unable to parse %s file: %s",
name, file);
}
}
}
fclose(fp);
return 0;
}
static void init_from_array(char **cidrs, size_t len, int value,
int ignore_invalid_hosts)
{
for (int i = 0; i < (int)len; i++) {
int ret = init_from_string(cidrs[i], value);
if (ret && !ignore_invalid_hosts) {
log_fatal("constraint",
"Unable to init from CIDR list");
}
}
}
uint64_t blocklist_count_allowed(void)
{
assert(constraint);
return constraint_count_ips(constraint, ADDR_ALLOWED);
}
uint64_t blocklist_count_not_allowed(void)
{
assert(constraint);
return constraint_count_ips(constraint, ADDR_DISALLOWED);
}
// network order
uint32_t blocklist_ip_to_index(uint32_t ip)
{
assert(constraint);
uint32_t ip_hostorder = ntohl(ip);
return constraint_lookup_ip(constraint, ip_hostorder);
}
// Initialize address constraints from allowlist and blocklist files.
// Either can be set to NULL to omit.
int blocklist_init(char *allowlist_filename, char *blocklist_filename,
char **allowlist_entries, size_t allowlist_entries_len,
char **blocklist_entries, size_t blocklist_entries_len,
int ignore_invalid_hosts)
{
assert(!constraint);
blocklisted_cidrs = xcalloc(1, sizeof(bl_ll_t));
allowlisted_cidrs = xcalloc(1, sizeof(bl_ll_t));
if (allowlist_filename && allowlist_entries) {
log_warn("allowlist",
"both a allowlist file and destination addresses "
"were specified. The union of these two sources "
"will be utilized.");
}
if (allowlist_filename || allowlist_entries_len > 0) {
// using a allowlist, so default to allowing nothing
constraint = constraint_init(ADDR_DISALLOWED);
log_debug("constraint", "blocklisting 0.0.0.0/0");
if (allowlist_filename) {
init_from_file(allowlist_filename, "allowlist",
ADDR_ALLOWED, ignore_invalid_hosts);
}
if (allowlist_entries) {
init_from_array(allowlist_entries,
allowlist_entries_len, ADDR_ALLOWED,
ignore_invalid_hosts);
}
} else {
// no allowlist, so default to allowing everything
log_debug("blocklist",
"no allowlist file or allowlist entries provided");
constraint = constraint_init(ADDR_ALLOWED);
}
if (blocklist_filename) {
init_from_file(blocklist_filename, "blocklist", ADDR_DISALLOWED,
ignore_invalid_hosts);
}
if (blocklist_entries) {
init_from_array(blocklist_entries, blocklist_entries_len,
ADDR_DISALLOWED, ignore_invalid_hosts);
}
init_from_string(strdup("0.0.0.0"), ADDR_DISALLOWED);
constraint_paint_value(constraint, ADDR_ALLOWED);
uint64_t allowed = blocklist_count_allowed();
log_debug("constraint",
"%lu addresses (%0.0f%% of address "
"space) can be scanned",
allowed, allowed * 100. / ((long long int)1 << 32));
if (!allowed) {
log_error("blocklist",
"no addresses are eligible to be scanned in the "
"current configuration. This may be because the "
"blocklist being used by ZMap (%s) prevents "
"any addresses from receiving probe packets.",
blocklist_filename);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

50
lib/blocklist.h Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include <stdint.h>
#ifndef BLACKLIST_H
#define BLACKLIST_H
typedef struct bl_cidr_node {
uint32_t ip_address;
int prefix_len;
struct bl_cidr_node *next;
} bl_cidr_node_t;
uint32_t blocklist_lookup_index(uint64_t index);
int blocklist_is_allowed(uint32_t s_addr);
void blocklist_prefix(char *ip, int prefix_len);
void allowlist_prefix(char *ip, int prefix_len);
int blocklist_init(char *allowlist, char *blocklist, char **allowlist_entries,
size_t allowlist_entries_len, char **blocklist_entries,
size_t blocklist_entries_len, int ignore_invalid_hosts);
uint64_t blocklist_count_allowed(void);
uint64_t blocklist_count_not_allowed(void);
uint32_t blocklist_ip_to_index(uint32_t ip);
bl_cidr_node_t *get_blocklisted_cidrs(void);
bl_cidr_node_t *get_allowlisted_cidrs(void);
#endif

275
lib/cachehash.c Normal file
View File

@ -0,0 +1,275 @@
/*
* CacheHash Copyright 2014 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 "cachehash.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <Judy.h>
#define EVICTION_NEEDED 1
#define EVICTION_UNNEC 0
// doubly-linked-list node
typedef struct node {
struct node *next;
struct node *prev;
void *key;
size_t keylen;
void *data;
} node_t;
// data structure that contains the enterity of a linked list
// we typedef this as cachehash in cachehash.h s.t. external interface is clean
struct cachehash_s {
Pvoid_t judy;
void *malloced;
node_t *start;
node_t *end;
node_t *curr_end;
size_t maxsize;
size_t currsize;
cachehash_process_cb *evict_cb;
};
cachehash *cachehash_init(size_t maxitems, cachehash_process_cb *cb)
{
assert(maxitems > 0);
cachehash *retv = malloc(sizeof(cachehash));
assert(retv);
memset(retv, 0, sizeof(cachehash));
// allocate nodes all at once to avoid fragmented memory
node_t *nodes = calloc(maxitems, sizeof(node_t));
retv->malloced = nodes;
assert(nodes);
retv->start = nodes;
retv->curr_end = nodes;
retv->end = &nodes[maxitems - 1];
retv->maxsize = maxitems;
retv->currsize = 0;
// initial node
nodes[0].next = &nodes[1];
// middle nodes
for (unsigned int i = 1; i < maxitems - 1; i++) {
nodes[i].prev = &nodes[i - 1];
nodes[i].next = &nodes[i + 1];
}
// last node
nodes[maxitems - 1].prev = &nodes[maxitems - 2];
retv->evict_cb = cb;
return retv;
}
void cachehash_set_evict_cb(cachehash *ch, cachehash_process_cb *cb)
{
ch->evict_cb = cb;
}
// is the hashcache full?
static inline int eviction_needed(cachehash *ch)
{
assert(ch);
return ch->currsize == ch->maxsize;
}
// completely evict the LRU object
// does not cb to user w/ object
static inline void *evict(cachehash *ch)
{
assert(ch);
node_t *last = ch->end;
// remove item from judy array
int rc;
JHSD(rc, ch->judy, last->key, last->keylen);
// we should never end up with something in the linked list
// that's not in the judy array.
assert(rc);
// reset linked list node
void *retv = last->data;
last->data = NULL;
free(last->key);
last->key = NULL;
last->keylen = 0;
ch->currsize--;
ch->curr_end = ch->end;
return retv;
}
static inline void use(cachehash *ch, node_t *n)
{
assert(ch);
assert(n);
//if first node, nothing to do and return
if (n == ch->start) {
return;
}
// remove from current spot in linked list
node_t *prev = n->prev;
n->prev->next = n->next;
// if last node then no next, but must update LL
if (n->next) {
n->next->prev = prev;
} else {
ch->end = prev;
}
// front of list
n->next = ch->start;
ch->start->prev = n;
ch->start = n;
n->prev = NULL;
}
static inline node_t *judy_get(cachehash *ch, void *key, size_t keylen)
{
assert(ch);
assert(key);
assert(keylen);
Word_t *v_;
JHSG(v_, ch->judy, key, keylen);
if (!v_) {
return NULL;
}
return (node_t *)*v_;
}
void *cachehash_has(cachehash *ch, const void *key, size_t keylen)
{
assert(ch);
assert(key);
assert(keylen);
node_t *n = judy_get(ch, (void *)key, keylen);
if (n) {
return n->data;
} else {
return NULL;
}
}
void *cachehash_get(cachehash *ch, const void *key, size_t keylen)
{
assert(ch);
assert(key);
assert(keylen);
node_t *n = judy_get(ch, (void *)key, keylen);
if (n) {
use(ch, n);
return n->data;
} else {
return NULL;
}
}
void *cachehash_evict_if_full(cachehash *ch)
{
assert(ch);
if (eviction_needed(ch) == EVICTION_UNNEC) {
return NULL;
}
return evict(ch);
}
void cachehash_put(cachehash *ch, const void *key, size_t keylen, void *value)
{
assert(ch);
assert(key);
assert(keylen);
void *evicted = cachehash_evict_if_full(ch);
if (evicted && ch->evict_cb) {
ch->evict_cb(evicted);
ch->curr_end = ch->end;
}
// create new node
node_t *n;
void *newkey = malloc(keylen);
n = ch->curr_end;
memcpy(newkey, key, keylen);
n->key = newkey;
n->keylen = keylen;
n->data = value;
//n->prev = ch->curr_end->prev;
//n->next = ch->curr_end->next;
if (ch->curr_end != ch->end) {
ch->curr_end = ch->curr_end->next;
ch->curr_end->prev = n;
}
use(ch, n);
ch->currsize++;
// add to judy array
Word_t *v_;
JHSI(v_, ch->judy, (void *)key, keylen);
// key should not already be in hash table
assert(!*v_);
*v_ = (Word_t)n;
}
// print out entire state.
void cachehash_debug_dump(cachehash *ch)
{
printf("Statistics:\n");
printf("\tcurrent size: %lu\n", ch->currsize);
printf("\tmaximum size: %lu\n", ch->maxsize);
printf("\n");
printf("Linked List:\n");
size_t i = 0;
node_t *n = ch->start;
do {
if (n->key) {
printf("\t%lu: %s -> %s\n", i++, (char *)n->key,
(char *)n->data);
} else {
printf("\t%lu: EMPTY\n", i++);
}
n = n->next;
} while (n);
}
void cachehash_free(cachehash *ch, cachehash_process_cb *cb)
{
assert(ch);
int rc;
JHSFA(rc, ch->judy);
node_t *n = ch->start;
do {
if (n->key) {
free(n->key);
if (cb) {
cb(n->data);
}
}
n = n->next;
} while (n);
free(ch->malloced);
free(ch);
}
void cachehash_iter(cachehash *ch, cachehash_process_cb *cb)
{
node_t *n = ch->start;
do {
if (n->key) {
cb(n->data);
} else {
break;
}
n = n->next;
} while (n);
}

46
lib/cachehash.h Normal file
View File

@ -0,0 +1,46 @@
/*
* CacheHash Copyright 2014 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 CACHEHASH_H
#define CACHEHASH_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct cachehash_s cachehash;
// function defintion for cachehash callbacks
typedef void(cachehash_process_cb)(void *data);
// initialize new cache hash
cachehash *cachehash_init(size_t maxitems, cachehash_process_cb *cb);
// return item from cachehash without changing its location in LL
void *cachehash_has(cachehash *ch, const void *key, size_t keylen);
// return item from cachehash and move to front
void *cachehash_get(cachehash *ch, const void *key, size_t keylen);
// add item to the cachehash
void cachehash_put(cachehash *ch, const void *key, size_t keylen, void *value);
// free memory used by a cachehash. unusable until new initialization
void cachehash_free(cachehash *ch, cachehash_process_cb *cb);
// evict the LRU if the cachehash is full
void *cachehash_evict_if_full(cachehash *ch);
// iterate over all values in the cache hash in MRU -> LRU order
void cachehash_iter(cachehash *ch, cachehash_process_cb *cb);
// print out hash cache to stdout assuming that all keys and values
// are null-terminated ASCII strings
void cachehash_debug_dump(cachehash *ch);
// change the callback function for the cachehash
void cachehash_set_evict_cb(cachehash *ch, cachehash_process_cb *cb);
#ifdef __cplusplus
}
#endif
#endif /* CACHEHASH_H */

416
lib/constraint.c Normal file
View File

@ -0,0 +1,416 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#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"
#include "../lib/xalloc.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
// allowlisting and blocklisting.
//
// 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;
uint64_t count;
} node_t;
// As an optimization, we precompute lookups for every prefix of this
// length:
#define RADIX_LENGTH 20
struct _constraint {
node_t *root; // root node of the tree
uint32_t *radix; // array of prefixes (/RADIX_LENGTH) that are painted
// paint_value
size_t radix_len; // number of prefixes in radix array
int painted; // have we precomputed counts for each node?
value_t paint_value; // value for which we precomputed counts
};
// 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 = xmalloc(sizeof(node_t));
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 <= 256);
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->painted = 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.)
value_t constraint_lookup_ip(constraint_t *con, uint32_t address)
{
assert(con);
return _lookup_ip(con->root, address);
}
// Return the nth painted IP address.
static int _lookup_index(node_t *root, uint64_t n)
{
assert(root);
node_t *node = root;
uint32_t ip = 0;
uint32_t mask = 0x80000000;
for (;;) {
if (IS_LEAF(node)) {
return ip | n;
}
if (n < node->l->count) {
node = node->l;
} else {
n -= node->l->count;
node = node->r;
ip |= mask;
}
mask >>= 1;
}
}
// For a given value, return the IP address with zero-based index n.
// (i.e., if there are three addresses with value 0xFF, looking up index 1
// will return the second one).
// Note that the tree must have been previously painted with this value.
uint32_t constraint_lookup_index(constraint_t *con, uint64_t index,
value_t value)
{
assert(con);
if (!con->painted || con->paint_value != value) {
constraint_paint_value(con, value);
}
uint64_t radix_idx = index / (1 << (32 - RADIX_LENGTH));
if (radix_idx < con->radix_len) {
// Radix lookup
uint32_t radix_offset =
index % (1 << (32 - RADIX_LENGTH)); // TODO: bitwise maths
return con->radix[radix_idx] | radix_offset;
}
// Otherwise, do the "slow" lookup in tree.
// Note that tree counts do NOT include things in the radix,
// so we subtract these off here.
index -= con->radix_len * (1 << (32 - RADIX_LENGTH));
assert(index < con->root->count);
return _lookup_index(con->root, index);
}
// 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.
// If paint is specified, each node will have its count set to the number of
// leaves under it set to value.
// If exclude_radix is specified, the number of addresses will exclude prefixes
// that are a /RADIX_LENGTH or larger
static uint64_t _count_ips_recurse(node_t *node, value_t value, uint64_t size,
int paint, int exclude_radix)
{
assert(node);
uint64_t n;
if (IS_LEAF(node)) {
if (node->value == value) {
n = size;
// Exclude prefixes already included in the radix
if (exclude_radix &&
size >= (1 << (32 - RADIX_LENGTH))) {
n = 0;
}
} else {
n = 0;
}
} else {
n = _count_ips_recurse(node->l, value, size >> 1, paint,
exclude_radix) +
_count_ips_recurse(node->r, value, size >> 1, paint,
exclude_radix);
}
if (paint) {
node->count = n;
}
return n;
}
// 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;
int i;
for (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;
}
// For each node, precompute the count of leaves beneath it set to value.
// Note that the tree can be painted for only one value at a time.
void constraint_paint_value(constraint_t *con, value_t value)
{
assert(con);
log_debug("constraint", "Painting value %lu", value);
// Paint everything except what we will put in radix
_count_ips_recurse(con->root, value, (uint64_t)1 << 32, 1, 1);
// Fill in the radix array with a list of addresses
uint32_t i;
con->radix_len = 0;
for (i = 0; i < (1 << RADIX_LENGTH); i++) {
uint32_t prefix = i << (32 - RADIX_LENGTH);
node_t *node = _lookup_node(con->root, prefix, RADIX_LENGTH);
if (IS_LEAF(node) && node->value == value) {
// Add this prefix to the radix
con->radix[con->radix_len++] = prefix;
}
}
log_debug("constraint", "%lu IPs in radix array, %lu IPs in tree",
con->radix_len * (1 << (32 - RADIX_LENGTH)),
con->root->count);
con->painted = 1;
con->paint_value = value;
}
// Return the number of addresses that have a given value.
uint64_t constraint_count_ips(constraint_t *con, value_t value)
{
assert(con);
if (con->painted && con->paint_value == value) {
return con->root->count +
con->radix_len * (1 << (32 - RADIX_LENGTH));
} else {
return _count_ips_recurse(con->root, value, (uint64_t)1 << 32,
0, 0);
}
}
// Initialize the tree.
// All addresses will initially have the given value.
constraint_t *constraint_init(value_t value)
{
constraint_t *con = xmalloc(sizeof(constraint_t));
con->root = _create_leaf(value);
con->radix = xcalloc(sizeof(uint32_t), 1 << RADIX_LENGTH);
con->painted = 0;
return con;
}
// Deinitialize and free the tree.
void constraint_free(constraint_t *con)
{
assert(con);
log_debug("constraint", "Cleaning up");
_destroy_subtree(con->root);
free(con->radix);
free(con);
}
/*
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);
}
*/

34
lib/constraint.h Normal file
View File

@ -0,0 +1,34 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CONSTRAINT_H
#define CONSTRAINT_H
#include <stdint.h>
typedef struct _constraint constraint_t;
typedef uint32_t 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);
value_t constraint_lookup_ip(constraint_t *con, uint32_t address);
uint64_t constraint_count_ips(constraint_t *con, value_t value);
uint32_t constraint_lookup_index(constraint_t *con, uint64_t index,
value_t value);
void constraint_paint_value(constraint_t *con, value_t value);
#endif //_CONSTRAINT_H

52
lib/csv.c Normal file
View File

@ -0,0 +1,52 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#include "csv.h"
int csv_find_index(char *header, const char **names, size_t names_len)
{
char *split = header;
for (int idx = 0; split != NULL; ++idx) {
char *front = (idx == 0) ? split : split + 1;
for (size_t i = 0; i < names_len; ++i) {
if (strncmp(front, names[i], strlen(names[i])) == 0) {
return idx;
}
}
split = strchr(front, ',');
}
return -1;
}
char *csv_get_index(char *row, size_t idx)
{
char *split = row;
for (size_t i = 0; i < idx; ++i) {
split = strchr(split + 1, ',');
if (split == NULL) {
return NULL;
}
}
char *entry;
char *start = (idx == 0) ? split : split + 1;
char *end = strchr(start, ',');
if (end != NULL) {
entry = strndup(start, end - start);
} else {
entry = strdup(start);
}
return entry;
}

25
lib/csv.h Normal file
View File

@ -0,0 +1,25 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ZMAP_CSV_H
#define ZMAP_CSV_H
#include <string.h>
int csv_find_index(char *header, const char **names, size_t names_len);
char *csv_get_index(char *row, size_t idx);
#endif

45
lib/includes.h Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __FAVOR_BSD
#define __FAVOR_BSD 2
#endif
#ifndef __USE_BSD
#define __USE_BSD
#endif
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <netinet/ip_icmp.h>
#if defined(__NetBSD__)
#define ICMP_UNREACH_PRECEDENCE_CUTOFF ICMP_UNREACH_PREC_CUTOFF
#include <net/if_ether.h>
#else
#include <net/ethernet.h>
#endif
#include <netdb.h>
#include <net/if.h>
#include <ifaddrs.h> // NOTE: net/if.h MUST be included BEFORE ifaddrs.h
#include <arpa/inet.h>
#define MAC_ADDR_LEN ETHER_ADDR_LEN
#define UNUSED __attribute__((unused))

57
lib/lockfd.c Normal file
View File

@ -0,0 +1,57 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#include <pthread.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "xalloc.h"
static pthread_mutex_t **mutexes = NULL;
static pthread_mutex_t *get_mutex(int fd)
{
assert(fd < 3 && "todo: implement generically");
if (!mutexes) {
mutexes = xmalloc(3 * sizeof(char *));
assert(mutexes);
}
if (!mutexes[fd]) {
mutexes[fd] = xmalloc(sizeof(pthread_mutex_t));
assert(mutexes[fd]);
pthread_mutex_init(mutexes[fd], NULL);
assert(mutexes[fd]);
}
return mutexes[fd];
}
int lock_fd(int fd) { return pthread_mutex_lock(get_mutex(fd)); }
int unlock_fd(int fd) { return pthread_mutex_unlock(get_mutex(fd)); }
int lock_file(FILE *f)
{
assert(f);
return lock_fd(fileno(f));
}
int unlock_file(FILE *f)
{
assert(f);
return unlock_fd(fileno(f));
}

22
lib/lockfd.h Normal file
View File

@ -0,0 +1,22 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
int lock_fd(int fd);
int unlock_fd(int fd);
int lock_file(FILE *f);
int unlock_file(FILE *f);

271
lib/logger.c Normal file
View File

@ -0,0 +1,271 @@
/*
* 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 <unistd.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <sys/time.h>
#include <time.h>
#include <syslog.h>
#include <math.h>
#include <pthread.h>
#include "logger.h"
#include "xalloc.h"
#include "lockfd.h"
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static enum LogLevel log_output_level = ZLOG_INFO;
static FILE *log_output_stream = NULL;
static int color = 0;
static int log_to_syslog = 0;
static const char *log_level_name[] = {"FATAL", "ERROR", "WARN",
"INFO", "DEBUG", "TRACE"};
#define RED "\x1b[31m"
#define GREEN "\x1b[32m"
#define YELLOW "\x1b[33m"
#define BLUE "\x1b[34m"
#define MAGENTA "\x1b[35m"
#define CYAN "\x1b[36m"
#define RESET "\033[0m"
#define COLOR(x) \
do { \
if (color) \
fprintf(log_output_stream, "%s", x); \
} while (0)
static const char *color_for_level(enum LogLevel level)
{
switch (level) {
case ZLOG_FATAL:
return RED;
case ZLOG_ERROR:
return MAGENTA;
case ZLOG_WARN:
return YELLOW;
case ZLOG_INFO:
return GREEN;
case ZLOG_DEBUG:
return BLUE;
case ZLOG_TRACE:
return RESET;
default:
return RESET;
}
}
static int LogLogVA(enum LogLevel level, const char *loggerName,
const char *logMessage, va_list args)
{
if (level <= log_output_level) {
if (!log_output_stream) {
log_output_stream = stderr;
}
// if logging to a shared output channel, then use a global
// lock across ZMap. Otherwise, if we're logging to a file,
// only lockin with the module, in order to avoid having
// corrupt log entries.
if (log_output_stream == stdout ||
log_output_stream == stderr) {
lock_file(log_output_stream);
} else {
pthread_mutex_lock(&mutex);
}
if (color) {
COLOR(color_for_level(level));
}
const char *levelName = log_level_name[level];
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,
(long)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);
}
if (color) {
COLOR(RESET);
}
fflush(log_output_stream);
if (log_output_stream == stdout ||
log_output_stream == stderr) {
unlock_file(log_output_stream);
} else {
pthread_mutex_unlock(&mutex);
}
}
return EXIT_SUCCESS;
}
int log_fatal(const char *name, const char *message, ...)
{
va_list va;
va_start(va, message);
LogLogVA(ZLOG_FATAL, name, message, va);
va_end(va);
if (log_to_syslog) {
va_start(va, message);
vsyslog(LOG_MAKEPRI(LOG_USER, LOG_CRIT), 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(ZLOG_ERROR, name, message, va);
va_end(va);
if (log_to_syslog) {
va_start(va, message);
vsyslog(LOG_MAKEPRI(LOG_USER, LOG_ERR), 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(ZLOG_WARN, name, message, va);
va_end(va);
if (log_to_syslog) {
va_start(va, message);
vsyslog(LOG_MAKEPRI(LOG_USER, LOG_WARNING), 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(ZLOG_INFO, name, message, va);
va_end(va);
char *prefixed = xmalloc(strlen(name) + strlen(message) + 3);
strcpy(prefixed, name);
strcat(prefixed, ": ");
strcat(prefixed, message);
if (log_to_syslog) {
va_start(va, message);
vsyslog(LOG_MAKEPRI(LOG_USER, LOG_INFO), prefixed, va);
va_end(va);
}
free(prefixed);
return ret;
}
int log_debug(const char *name, const char *message, ...)
{
va_list va;
va_start(va, message);
int ret = LogLogVA(ZLOG_DEBUG, name, message, va);
va_end(va);
char *prefixed = xmalloc(strlen(name) + strlen(message) + 3);
strcpy(prefixed, name);
strcat(prefixed, ": ");
strcat(prefixed, message);
if (log_to_syslog) {
va_start(va, message);
vsyslog(LOG_MAKEPRI(LOG_USER, LOG_DEBUG), prefixed, va);
va_end(va);
}
free(prefixed);
return ret;
}
#ifdef DEBUG
extern int log_trace(const char *name, const char *message, ...)
{
va_list va;
va_start(va, message);
int ret = LogLogVA(ZLOG_TRACE, name, message, va);
va_end(va);
char *prefixed = xmalloc(strlen(name) + strlen(message) + 3);
strcpy(prefixed, name);
strcat(prefixed, ": ");
strcat(prefixed, message);
if (log_to_syslog) {
va_start(va, message);
vsyslog(LOG_MAKEPRI(LOG_USER, LOG_DEBUG), prefixed, va);
va_end(va);
}
free(prefixed);
return ret;
}
#endif
int log_init(FILE *stream, enum LogLevel level, int syslog_enabled,
const char *appname)
{
log_output_stream = stream;
log_output_level = level;
if (syslog_enabled) {
log_to_syslog = 1;
openlog(appname, 0, LOG_USER); // no options
}
if (isatty(fileno(log_output_stream))) {
color = 1;
}
return 0;
}
void check_and_log_file_error(FILE *file, const char *name)
{
if (ferror(file)) {
log_fatal(name, "unable to write to file");
}
}
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));
}

67
lib/logger.h Normal file
View File

@ -0,0 +1,67 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdarg.h>
#include <syslog.h>
#ifndef LOGGER_H
#define LOGGER_H
#ifdef __cplusplus
extern "C" {
#endif
// do not collide with constants defined in syslog.h
enum LogLevel {
ZLOG_FATAL,
ZLOG_ERROR,
ZLOG_WARN,
ZLOG_INFO,
ZLOG_DEBUG,
ZLOG_TRACE,
ZNUM_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, ...);
#ifdef DEBUG
int log_trace(const char *loggerName, const char *logMessage, ...);
#else
#define log_trace(...) ;
#endif
int log_init(FILE *stream, enum LogLevel level, int syslog_enabled,
const char *syslog_app);
void check_and_log_file_error(FILE *file, const char *name);
size_t dstrftime(char *, size_t, const char *, double);
#ifdef __cplusplus
}
#endif
#endif // _LOGGER_H

111
lib/pbm.c Normal file
View File

@ -0,0 +1,111 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "logger.h"
#include "xalloc.h"
#define NUM_VALUES 0xFFFFFFFF
#define PAGE_SIZE_IN_BITS 0x10000
#define PAGE_SIZE_IN_BYTES (PAGE_SIZE_IN_BITS / 8)
#define NUM_PAGES 0x10000
#define PAGE_MASK 0xFFFF
uint8_t **pbm_init(void)
{
uint8_t **retv = xcalloc(NUM_PAGES, sizeof(void *));
return retv;
}
uint8_t *bm_init(void)
{
uint8_t *bm = xmalloc(PAGE_SIZE_IN_BYTES);
memset(bm, 0, PAGE_SIZE_IN_BYTES);
return bm;
}
int bm_check(uint8_t *bm, uint16_t v)
{
uint16_t page_idx = (v >> 3);
uint8_t bit_idx = (uint8_t)(v & 0x07);
return bm[page_idx] & (1 << bit_idx);
}
void bm_set(uint8_t *bm, uint16_t v)
{
uint16_t page_idx = (v >> 3);
uint8_t bit_idx = (uint8_t)(v & 0x07);
bm[page_idx] |= (1 << bit_idx);
}
int pbm_check(uint8_t **b, uint32_t v)
{
uint32_t top = v >> 16;
uint32_t bottom = v & PAGE_MASK;
return b[top] && bm_check(b[top], bottom);
}
void pbm_set(uint8_t **b, uint32_t v)
{
uint16_t top = (uint16_t)(v >> 16);
uint16_t bottom = (uint16_t)(v & PAGE_MASK);
if (!b[top]) {
b[top] = bm_init();
}
bm_set(b[top], bottom);
}
uint32_t pbm_load_from_file(uint8_t **b, char *file)
{
if (!b) {
log_fatal("pbm", "load_from_file called with NULL PBM");
}
if (!file) {
log_fatal("pbm", "load_from_file called with NULL filename");
}
FILE *fp = fopen(file, "r");
if (fp == NULL) {
log_fatal("pbm", "unable to open file: %s: %s", file,
strerror(errno));
}
char line[1000];
uint32_t count = 0;
while (fgets(line, sizeof(line), fp)) {
char *comment = strchr(line, '#');
if (comment) {
*comment = '\0';
}
struct in_addr addr;
if (inet_aton(line, &addr) != 1) {
log_fatal("pbm", "unable to parse IP address: %s",
line);
}
pbm_set(b, addr.s_addr);
++count;
}
fclose(fp);
return count;
}

31
lib/pbm.h Normal file
View File

@ -0,0 +1,31 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ZMAP_PBM_H
#define ZMAP_PBM_H
#include <stdint.h>
uint8_t *bm_init(void);
int bm_check(uint8_t *bm, uint16_t v);
void bm_set(uint8_t *bm, uint16_t v);
uint8_t **pbm_init(void);
int pbm_check(uint8_t **b, uint32_t v);
void pbm_set(uint8_t **b, uint32_t v);
uint32_t pbm_load_from_file(uint8_t **b, char *file);
#endif /* ZMAP_PBM_H */

109
lib/queue.c Normal file
View File

@ -0,0 +1,109 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#include "queue.h"
#include "xalloc.h"
#include <pthread.h>
zqueue_t *queue_init(void)
{
zqueue_t *p = xmalloc(sizeof(zqueue_t));
p->front = NULL;
p->back = NULL;
p->size = 0;
pthread_mutex_init(&p->lock, NULL);
pthread_cond_init(&p->empty, NULL);
return p;
}
int is_empty(zqueue_t *queue) { return queue->size == 0; }
void push_back(char *data, zqueue_t *queue)
{
znode_t *new_node = xmalloc(sizeof(znode_t));
new_node->prev = NULL;
new_node->next = NULL;
new_node->data = strdup(data);
pthread_mutex_lock(&queue->lock);
if (is_empty(queue)) {
queue->front = new_node;
queue->back = new_node;
} else {
queue->back->next = new_node;
new_node->prev = queue->back;
queue->back = new_node;
}
queue->size++;
pthread_cond_signal(&queue->empty);
pthread_mutex_unlock(&queue->lock);
}
znode_t *pop_front(zqueue_t *queue)
{
pthread_mutex_lock(&queue->lock);
while (is_empty(queue)) {
pthread_cond_wait(&queue->empty, &queue->lock);
}
znode_t *temp = pop_front_unsafe(queue);
pthread_mutex_unlock(&queue->lock);
return temp;
}
znode_t *pop_front_unsafe(zqueue_t *queue)
{
znode_t *temp = queue->front;
queue->front = temp->next;
if (queue->front != NULL) {
queue->front->prev = NULL;
}
queue->size--;
return temp;
}
znode_t *get_front(zqueue_t *queue)
{
pthread_mutex_lock(&queue->lock);
while (is_empty(queue)) {
pthread_cond_wait(&queue->empty, &queue->lock);
}
znode_t *temp = xmalloc(sizeof(znode_t));
temp = queue->front;
pthread_mutex_unlock(&queue->lock);
return temp;
}
znode_t *get_back(zqueue_t *queue)
{
pthread_mutex_lock(&queue->lock);
while (is_empty(queue)) {
pthread_cond_wait(&queue->empty, &queue->lock);
}
znode_t *temp = xmalloc(sizeof(znode_t));
temp = queue->back;
pthread_mutex_unlock(&queue->lock);
return temp;
}
size_t get_size(zqueue_t *queue) { return queue->size; }

49
lib/queue.h Normal file
View File

@ -0,0 +1,49 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ZMAP_QUEUE_H
#define ZMAP_QUEUE_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
typedef struct zqueue_node {
char *data;
struct zqueue_node *prev;
struct zqueue_node *next;
} znode_t;
typedef struct zqueue {
struct zqueue_node *front;
struct zqueue_node *back;
size_t size;
// Threading utilities
pthread_mutex_t lock;
pthread_cond_t empty;
} zqueue_t;
zqueue_t *queue_init(void);
int is_empty(zqueue_t *queue);
void push_back(char *data, zqueue_t *queue);
znode_t *pop_front(zqueue_t *queue);
znode_t *pop_front_unsafe(zqueue_t *queue);
znode_t *get_front(zqueue_t *queue);
znode_t *get_back(zqueue_t *queue);
size_t get_size(zqueue_t *queue);
#endif /* ZMAP_QUEUE_H */

32
lib/random.c Normal file
View File

@ -0,0 +1,32 @@
/*
* 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 <errno.h>
#include "logger.h"
#define RANDSRC "/dev/urandom"
int random_bytes(void *dst, size_t n)
{
FILE *f = fopen(RANDSRC, "rb");
if (!f) {
log_fatal("random", "unable to read /dev/urandom: %s",
strerror(errno));
}
size_t r = fread(dst, n, 1, f);
fclose(f);
if (r < 1) {
return 0;
}
return 1;
}

25
lib/random.h Normal file
View File

@ -0,0 +1,25 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include <stdint.h>
#ifndef RANDOM_H
#define RANDOM_H
int random_bytes(void *dst, size_t n);
#endif

1284
lib/rijndael-alg-fst.c Normal file

File diff suppressed because it is too large Load Diff

53
lib/rijndael-alg-fst.h Normal file
View File

@ -0,0 +1,53 @@
/**
* 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 */

30
lib/types.h Normal file
View File

@ -0,0 +1,30 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ZMAP_TYPES_H
#define ZMAP_TYPES_H
#include <stdint.h>
#include <stdint.h>
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;
#endif /* ZMAP_TYPES_H */

371
lib/util.c Normal file
View File

@ -0,0 +1,371 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#define _GNU_SOURCE
#include "util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "includes.h"
#include "xalloc.h"
#include <errno.h>
#include <unistd.h>
#include <sched.h>
#include <pthread.h>
#include <sys/types.h>
#include <pwd.h>
#include "../lib/logger.h"
#define MAX_SPLITS 128
int max_int(int a, int b)
{
if (a >= b) {
return a;
}
return b;
}
int min_int(int a, int b)
{
if (a >= b) {
return b;
}
return a;
}
void enforce_range(const char *name, int v, int min, int max)
{
if (check_range(v, min, max) == EXIT_FAILURE) {
log_fatal("zmap", "argument `%s' must be between %d and %d\n",
name, min, max);
}
}
void split_string(const char *in, int *len, const char ***results)
{
const char **fields = xcalloc(MAX_SPLITS, sizeof(const char *));
int retvlen = 0;
const char *currloc = in;
// parse csv into a set of strings
while (1) {
assert(retvlen < MAX_SPLITS);
size_t len = strcspn(currloc, ", ");
if (len == 0) {
currloc++;
} else {
char *new = xmalloc(len + 1);
strncpy(new, currloc, len);
new[len] = '\0';
fields[retvlen++] = new;
assert(fields[retvlen - 1]);
}
if (len == strlen(currloc)) {
break;
}
currloc += len;
}
*results = fields;
*len = retvlen;
}
void fprintw(FILE *f, const char *s, size_t w)
{
if (strlen(s) <= w) {
fprintf(f, "%s", s);
return;
}
// process each line individually in order to
// respect existing line breaks in string.
char *news = strdup(s);
char *pch = strtok(news, "\n");
while (pch) {
if (strlen(pch) <= w) {
printf("%s\n", pch);
pch = strtok(NULL, "\n");
continue;
}
char *t = pch;
while (strlen(t)) {
size_t numchars = 0; // number of chars to print
char *tmp = t;
while (1) {
size_t new = strcspn(tmp, " ") + 1;
if (new == strlen(tmp) || new > w) {
// there are no spaces in the string,
// so, just print the entire thing on
// one line;
numchars += new;
break;
} else if (numchars + new > w) {
// if we added any more, we'd be over w
// chars so time to print the line and
// move on to the next.
break;
} else {
tmp += (size_t) new;
numchars += new;
}
}
fprintf(f, "%.*s\n", (int)numchars, t);
t += (size_t)numchars;
if (t > pch + (size_t)strlen(pch)) {
break;
}
}
pch = strtok(NULL, "\n");
}
free(news);
}
uint32_t parse_max_hosts(char *max_targets)
{
char *end;
errno = 0;
double v = strtod(max_targets, &end);
if (end == max_targets || errno != 0) {
log_fatal("argparse", "can't convert max-targets to a number");
}
if (end[0] == '%' && end[1] == '\0') {
// treat as percentage
v = v * ((unsigned long long int)1 << 32) / 100.;
} else if (end[0] != '\0') {
log_fatal("eargparse", "extra characters after max-targets");
}
if (v <= 0) {
return 0;
} else if (v >= ((unsigned long long int)1 << 32)) {
return 0xFFFFFFFF;
} else {
return v;
}
}
// pretty print elapsed (or estimated) number of seconds
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
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.);
}
}
int parse_mac(macaddr_t *out, char *in)
{
if (strlen(in) < MAC_ADDR_LEN * 3 - 1)
return 0;
char octet[4];
octet[2] = '\0';
for (int i = 0; i < MAC_ADDR_LEN; i++) {
if (i < MAC_ADDR_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;
}
int check_range(int v, int min, int max)
{
if (v < min || v > max) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int file_exists(char *name)
{
FILE *file = fopen(name, "r");
if (!file)
return 0;
fclose(file);
return 1;
}
#if defined(__APPLE__)
#include <uuid/uuid.h>
#endif
int drop_privs(void)
{
struct passwd *pw;
if (geteuid() != 0) {
/* Not root */
return EXIT_SUCCESS;
}
if ((pw = getpwnam("nobody")) != NULL) {
if (setuid(pw->pw_uid) == 0) {
return EXIT_SUCCESS; // success
}
}
return EXIT_FAILURE;
}
#if defined(__APPLE__)
#include <mach/thread_act.h>
int set_cpu(uint32_t core)
{
mach_port_t tid = pthread_mach_thread_np(pthread_self());
struct thread_affinity_policy policy;
policy.affinity_tag = core;
kern_return_t ret = thread_policy_set(tid, THREAD_AFFINITY_POLICY,
(thread_policy_t)&policy,
THREAD_AFFINITY_POLICY_COUNT);
if (ret != KERN_SUCCESS) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
#elif defined(__DragonFly__)
#include <sys/usched.h>
int set_cpu(uint32_t core)
{
if (usched_set(getpid(), USCHED_SET_CPU, &core, sizeof(core)) != 0) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
#elif defined(__NetBSD__)
int set_cpu(uint32_t core)
{
cpuset_t *cpuset = cpuset_create();
if (cpuset == NULL) {
return EXIT_FAILURE;
}
cpuset_zero(cpuset);
cpuset_set(core, cpuset);
cpuset_destroy(cpuset);
if (pthread_setaffinity_np(pthread_self(), cpuset_size(cpuset),
cpuset) != 0) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
#else
#if defined(__FreeBSD__)
#include <sys/param.h>
#include <sys/cpuset.h>
#include <pthread_np.h>
#define cpu_set_t cpuset_t
#endif
int set_cpu(uint32_t core)
{
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(core, &cpuset);
if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t),
&cpuset) != 0) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
#endif
double now(void)
{
struct timeval now;
gettimeofday(&now, NULL);
return (double)now.tv_sec + (double)now.tv_usec / 1000000.;
}
double steady_now(void)
{
#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK)
struct timespec tp;
clock_gettime(CLOCK_MONOTONIC, &tp);
return (double)tp.tv_sec + (double)tp.tv_nsec / 1000000000.;
#else
struct timeval now;
gettimeofday(&now, NULL);
return (double)now.tv_sec + (double)now.tv_usec / 1000000.;
#endif
}

74
lib/util.h Normal file
View File

@ -0,0 +1,74 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ZMAP_UTIL_H
#define ZMAP_UTIL_H
#include <stdio.h>
#include <stdint.h>
#include <sys/time.h>
#include "types.h"
int max_int(int a, int b);
int min_int(int a, int b);
uint32_t parse_max_hosts(char *max_targets);
void enforce_range(const char *name, int v, int min, int max);
// Splits comma delimited string into char*[]. Does not handle
// escaping or complicated setups - designed to process a set
// of fields that the user wants output
void split_string(const char *in, int *len, const char ***results);
// Print a string using w length long lines, attempting to break on
// spaces
void fprintw(FILE *f, const char *s, size_t w);
// pretty print elapsed (or estimated) number of seconds
void time_string(uint32_t time, int est, char *buf, size_t len);
// pretty print quantities
void number_string(uint32_t n, char *buf, size_t len);
// Convert a string representation of a MAC address to a byte array
int parse_mac(macaddr_t *out, char *in);
int check_range(int v, int min, int max);
int file_exists(char *name);
// If running as root, drops privileges to that of user "nobody".
// Otherwise, does nothing.
int drop_privs(void);
// Set CPU affinity to a single core
int set_cpu(uint32_t core);
// The number of seconds and microseconds since the Epoch.
double now(void);
// The number of seconds and nanoseconds since an unspecified point in time.
// On supported hosts, this value is guaranteed to never decrease.
//
// According to the POSIX specification the `clock_gettime` function is part
// of the Timers option and may not be available on all implementations.
//
// On hosts where a monotonic clock is not available, this falls back
// to `gettimeofday` which was ZMap's original implementation.
double steady_now(void);
#endif /* ZMAP_UTIL_H */

55
lib/xalloc.c Normal file
View File

@ -0,0 +1,55 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#include "xalloc.h"
#include "logger.h"
#include <stdlib.h>
#include <string.h>
static void die(void) __attribute__((noreturn));
void *xcalloc(size_t count, size_t size)
{
void *res = calloc(count, size);
if (res == NULL) {
die();
}
return res;
}
void xfree(void *ptr) { free(ptr); }
void *xmalloc(size_t size)
{
void *res = malloc(size);
if (res == NULL) {
die();
}
memset(res, 0, size);
return res;
}
void *xrealloc(void *ptr, size_t size)
{
void *res = realloc(ptr, size);
if (res == NULL) {
die();
}
return res;
}
void die(void) { log_fatal("zmap", "Out of memory"); }

30
lib/xalloc.h Normal file
View File

@ -0,0 +1,30 @@
/*
* Copyright 2021 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 the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ZMAP_ALLOC_H
#define ZMAP_ALLOC_H
#include <stddef.h>
void *xcalloc(size_t count, size_t size);
void xfree(void *ptr);
void *xmalloc(size_t size);
void *xrealloc(void *ptr, size_t size);
#endif /* ZMAP_ALLOC_H */

7
release-build.sh Normal file
View File

@ -0,0 +1,7 @@
#!/bin/bash
cmake \
-DENABLE_DEVELOPMENT=off \
-DZMAP_VERSION=$1 \
-DENABLE_LOG_TRACE=off \
.
make

38
scripts/check_manfile.py Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env python3
#
# CI runs this script to verify that options appearing in ZTools' ggo.in files
# also appear in their .ronn files. It does not check that `make manpages` has
# actually been run.
#
# This script assumes it's being run from the root of the zmap repository.
#
import sys
checks = [
("zopt.ggo.in", "zmap.1.ronn"),
("zbopt.ggo.in", "zblocklist.1.ronn"),
("zitopt.ggo.in", "ziterate.1.ronn"),
("ztopt.ggo.in", "ztee.1.ronn")
]
failures = False
for ggo, ronn in checks:
options = []
with open("src/" + ggo) as fd:
for l in fd:
if l.startswith("option "):
option = l.split()[1].lstrip('"').rstrip('"')
options.append(option)
man = open("src/" + ronn).read()
for option in options:
if option not in man:
failures = True
sys.stderr.write(f"option \"{option}\" is present in \"{ggo}\" but missing from man file \"{ronn}\"\n")
sys.stderr.flush()
if failures:
sys.exit(1)

4
src/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
lexer.c
lexer.h
parser.c
parser.h

273
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,273 @@
include_directories(
"${CMAKE_CURRENT_BINARY_DIR}"
${PROJECT_SOURCE_DIR}/lib
${PROJECT_SOURCE_DIR}/src
${PROJECT_SOURCE_DIR}/src/output_modules
${PROJECT_SOURCE_DIR}/src/tests
)
# ADD YOUR PROBE MODULE HERE
set(EXTRA_PROBE_MODULES
)
# ADD YOUR OUTPUT MODULE HERE
set(EXTRA_OUTPUT_MODULES
)
set(OUTPUT_MODULE_SOURCES
output_modules/module_csv.c
output_modules/module_json.c
output_modules/output_modules.c
)
set(PROBE_MODULE_SOURCES
probe_modules/module_icmp_echo.c
probe_modules/module_icmp_echo_time.c
probe_modules/module_tcp_synscan.c
probe_modules/module_tcp_synackscan.c
#probe_modules/module_tcp_cisco_backdoor.c
probe_modules/module_udp.c
probe_modules/packet.c
probe_modules/probe_modules.c
probe_modules/module_ntp.c
probe_modules/module_upnp.c
probe_modules/module_dns.c
probe_modules/module_bacnet.c
)
set(SOURCES
aesrand.c
cyclic.c
expression.c
fieldset.c
filter.c
get_gateway.c
iterator.c
monitor.c
ports.c
recv.c
send.c
shard.c
socket.c
state.c
summary.c
utility.c
validate.c
zmap.c
zopt_compat.c
"${CMAKE_CURRENT_BINARY_DIR}/zopt.h"
"${CMAKE_CURRENT_BINARY_DIR}/lexer.c"
"${CMAKE_CURRENT_BINARY_DIR}/parser.c"
${EXTRA_PROBE_MODULES}
${EXTRA_OUTPUT_MODULES}
${PROBE_MODULE_SOURCES}
${OUTPUT_MODULE_SOURCES}
)
set(ZTESTSOURCES
aesrand.c
cyclic.c
expression.c
fieldset.c
filter.c
get_gateway.c
iterator.c
monitor.c
ports.c
recv.c
send.c
shard.c
socket.c
state.c
summary.c
validate.c
ztopt_compat.c
${PROBE_MODULE_SOURCES}
${OUTPUT_MODULE_SOURCES}
tests/test_harness.c
"${CMAKE_CURRENT_BINARY_DIR}/ztopt.h"
"${CMAKE_CURRENT_BINARY_DIR}/lexer.c"
"${CMAKE_CURRENT_BINARY_DIR}/parser.c"
${EXTRA_PROBE_MODULES}
${EXTRA_OUTPUT_MODULES}
)
set(ZBLSOURCES
zblocklist.c
zbopt_compat.c
"${CMAKE_CURRENT_BINARY_DIR}/zbopt.h"
)
set(ZITSOURCES
aesrand.c
cyclic.c
iterator.c
ports.c
shard.c
state.c
validate.c
zitopt_compat.c
ziterate.c
"${CMAKE_CURRENT_BINARY_DIR}/zitopt.h"
)
set(ZTEESOURCES
ztee.c
topt_compat.c
"${CMAKE_CURRENT_BINARY_DIR}/topt.h"
)
# Handle various versions of socket
if(WITH_PFRING)
set(SOURCES ${SOURCES} socket-pfring.c)
set(ZTESTSOURCES ${ZTESTSOURCES} socket-pfring.c)
elseif (APPLE OR BSD)
set(SOURCES ${SOURCES} socket-bsd.c)
set(ZTESTSOURCES ${ZTESTSOURCES} socket-bsd.c)
else()
set(SOURCES ${SOURCES} socket-linux.c send-linux.c)
set(ZTESTSOURCES ${ZTESTSOURCES} socket-linux.c send-linux.c)
endif()
# Handle various versions of recv
if(WITH_PFRING)
set(SOURCES ${SOURCES} recv-pfring.c)
set(ZTESTSOURCES ${ZTESTSOURCES} recv-pfring.c)
else()
set(SOURCES ${SOURCES} recv-pcap.c)
set(ZTESTSOURCES ${ZTESTSOURCES} recv-pcap.c)
endif()
# Set configure time zmap version
configure_file(topt.ggo.in ${CMAKE_BINARY_DIR}/src/topt.ggo @ONLY)
configure_file(zbopt.ggo.in ${CMAKE_BINARY_DIR}/src/zbopt.ggo @ONLY)
configure_file(zitopt.ggo.in ${CMAKE_BINARY_DIR}/src/zitopt.ggo @ONLY)
configure_file(zopt.ggo.in ${CMAKE_BINARY_DIR}/src/zopt.ggo @ONLY)
configure_file(ztopt.ggo.in ${CMAKE_BINARY_DIR}/src/ztopt.ggo @ONLY)
# Additional ggo.in's should be added here and CMakeVersion.txt
# This sets a *build* time dependency that checks git
if("${ZMAP_VERSION}" STREQUAL "DEVELOPMENT")
add_custom_target(git_versioning ALL
COMMAND ${CMAKE_COMMAND} -D ORIG_SRC_DIR:STRING="${CMAKE_SOURCE_DIR}" -P "${CMAKE_SOURCE_DIR}/src/CMakeVersion.txt"
)
endif()
add_custom_command(OUTPUT zopt.h
COMMAND gengetopt -C --no-help --no-version --unamed-opts=SUBNETS -i "${CMAKE_CURRENT_BINARY_DIR}/zopt.ggo" -F "${CMAKE_CURRENT_BINARY_DIR}/zopt"
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/zopt.ggo"
)
add_custom_command(OUTPUT topt.h
COMMAND gengetopt -S --no-help --no-version --unamed-opts=FILE -i "${CMAKE_CURRENT_BINARY_DIR}/topt.ggo" -F "${CMAKE_CURRENT_BINARY_DIR}/topt"
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/topt.ggo"
)
add_custom_command(OUTPUT zbopt.h
COMMAND gengetopt -C --no-help --no-version -i "${CMAKE_CURRENT_BINARY_DIR}/zbopt.ggo" -F "${CMAKE_CURRENT_BINARY_DIR}/zbopt"
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/zbopt.ggo"
)
add_custom_command(OUTPUT zitopt.h
COMMAND gengetopt -C --no-help --no-version --unamed-opts=SUBNETS -i "${CMAKE_CURRENT_BINARY_DIR}/zitopt.ggo" -F "${CMAKE_CURRENT_BINARY_DIR}/zitopt"
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/zitopt.ggo"
)
add_custom_command(OUTPUT ztopt.h
COMMAND gengetopt -C --no-help --no-version -i "${CMAKE_CURRENT_BINARY_DIR}/ztopt.ggo" -F "${CMAKE_CURRENT_BINARY_DIR}/ztopt"
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/ztopt.ggo"
)
add_custom_command(OUTPUT lexer.c
COMMAND flex -o "${CMAKE_CURRENT_BINARY_DIR}/lexer.c" --header-file="${CMAKE_CURRENT_BINARY_DIR}/lexer.h" "${CMAKE_CURRENT_SOURCE_DIR}/lexer.l"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/lexer.l"
)
if(NetBSD)
add_custom_command(OUTPUT parser.c
COMMAND yacc -d -o "${CMAKE_CURRENT_BINARY_DIR}/parser.c" "${CMAKE_CURRENT_SOURCE_DIR}/parser.y"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/parser.y"
)
else()
add_custom_command(OUTPUT parser.c
COMMAND byacc -d -o "${CMAKE_CURRENT_BINARY_DIR}/parser.c" "${CMAKE_CURRENT_SOURCE_DIR}/parser.y"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/parser.y"
)
endif()
add_custom_target(manpages ronn "${CMAKE_CURRENT_SOURCE_DIR}/zmap.1.ronn" --organization="ZMap" --manual="zmap"
COMMAND ronn "${CMAKE_CURRENT_SOURCE_DIR}/zblocklist.1.ronn" --organization="ZMap" --manual="zblocklist"
COMMAND ronn "${CMAKE_CURRENT_SOURCE_DIR}/ziterate.1.ronn" --organization="ZMap" --manual="ziterate"
COMMAND ronn "${CMAKE_CURRENT_SOURCE_DIR}/ztee.1.ronn" --organization="ZMap" --manual="ztee"
SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/zblocklist.1.ronn" "${CMAKE_CURRENT_SOURCE_DIR}/ziterate.1.ronn" "${CMAKE_CURRENT_SOURCE_DIR}/zmap.1.ronn" "${CMAKE_CURRENT_SOURCE_DIR}/ztee.1.ronn"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
)
add_executable(zmap ${SOURCES})
add_executable(zblocklist ${ZBLSOURCES})
add_executable(ziterate ${ZITSOURCES})
add_executable(ztee ${ZTEESOURCES})
add_executable(ztests ${ZTESTSOURCES})
if(APPLE OR BSD)
else()
set(ZTESTSOURCES ${ZTESTSOURCES} send-linux.c)
set(SOURCES ${SOURCES} send-linux.c)
endif()
target_link_libraries(
zmap
zmaplib
${PFRING_LIBRARIES}
pcap gmp m unistring
${JSON_LIBRARIES}
${JUDY_LIBRARIES}
)
target_link_libraries(
zblocklist
zmaplib
m
)
target_link_libraries(
ziterate
zmaplib
gmp
m
)
target_link_libraries(
ztee
zmaplib
m
)
target_link_libraries(
ztests
zmaplib
${PFRING_LIBRARIES}
pcap gmp m unistring
${JSON_LIBRARIES}
${JUDY_LIBRARIES}
)
# Install binary
install(
TARGETS
zmap
zblocklist
ziterate
ztee
RUNTIME DESTINATION sbin
)
# Install Manpages
install(
FILES
zmap.1
zblocklist.1
ziterate.1
ztee.1
DESTINATION share/man/man1
)

15
src/CMakeVersion.txt Normal file
View File

@ -0,0 +1,15 @@
set(GIT_CMD "git")
set(GIT_ARGS "log" "-n" "1" "--pretty=format:%h - %ad")
execute_process(COMMAND ${GIT_CMD} ${GIT_ARGS}
RESULT_VARIABLE GIT_RESULT
OUTPUT_VARIABLE GIT_COMMIT)
if (GIT_RESULT)
set (GIT_COMMIT "UNKNOWN")
endif()
set(ZMAP_VERSION "Development Build. Commit ${GIT_COMMIT}")
configure_file("${ORIG_SRC_DIR}/src/topt.ggo.in" "${CMAKE_BINARY_DIR}/topt.ggo" @ONLY)
configure_file("${ORIG_SRC_DIR}/src/zbopt.ggo.in" "${CMAKE_BINARY_DIR}/zbopt.ggo" @ONLY)
configure_file("${ORIG_SRC_DIR}/src/zitopt.ggo.in" "${CMAKE_BINARY_DIR}/zitopt.ggo" @ONLY)
configure_file("${ORIG_SRC_DIR}/src/zopt.ggo.in" "${CMAKE_BINARY_DIR}/zopt.ggo" @ONLY)
configure_file("${ORIG_SRC_DIR}/src/ztopt.ggo.in" "${CMAKE_BINARY_DIR}/ztopt.ggo" @ONLY)

71
src/aesrand.c Normal file
View File

@ -0,0 +1,71 @@
/*
* 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/logger.h"
#include "../lib/rijndael-alg-fst.h"
#include "../lib/random.h"
#include "../lib/xalloc.h"
#include "aesrand.h"
#define AES_ROUNDS 10
#define AES_BLOCK_WORDS 4
#define AES_KEY_BYTES 16
#define AES_KEY_BITS (AES_KEY_BYTES * 8)
#define OUTPUT_BYTES 16
struct aesrand {
uint32_t input[AES_BLOCK_WORDS];
uint32_t sched[(AES_ROUNDS + 1) * 4];
uint8_t output[OUTPUT_BYTES];
};
static aesrand_t *_aesrand_init(uint8_t *key)
{
aesrand_t *aes = xmalloc(sizeof(aesrand_t));
memset(&aes->input, 0, AES_BLOCK_WORDS * 4);
if (rijndaelKeySetupEnc(aes->sched, key, AES_KEY_BITS) != AES_ROUNDS) {
log_fatal("aesrand", "could not initialize AES key");
}
memset(aes->output, 0, OUTPUT_BYTES);
return aes;
}
aesrand_t *aesrand_init_from_seed(uint64_t seed)
{
uint8_t key[AES_KEY_BYTES];
memset(key, 0, AES_KEY_BYTES);
for (uint8_t i = 0; i < sizeof(seed); ++i) {
key[i] = (uint8_t)((seed >> 8 * i) & 0xFF);
}
return _aesrand_init(key);
}
aesrand_t *aesrand_init_from_random(void)
{
uint8_t key[AES_KEY_BYTES];
if (!random_bytes(key, AES_KEY_BYTES)) {
log_fatal("aesrand", "Couldn't get random bytes");
}
return _aesrand_init(key);
}
uint64_t aesrand_getword(aesrand_t *aes)
{
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;
}

24
src/aesrand.h Normal file
View File

@ -0,0 +1,24 @@
/*
* 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
typedef struct aesrand aesrand_t;
aesrand_t *aesrand_init_from_random(void);
aesrand_t *aesrand_init_from_seed(uint64_t);
uint64_t aesrand_getword(aesrand_t *aes);
aesrand_t *aesrand_free(aesrand_t *aes);
#endif

16
src/constants.h Normal file
View File

@ -0,0 +1,16 @@
/*
* ZMap Copyright 2023 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 ZMAP_CONSTANTS_H
#define ZMAP_CONSTANTS_H
#ifndef ZMAP_DEFAULT_BLOCKLIST
#define ZMAP_DEFAULT_BLOCKLIST "/etc/zmap/blocklist.conf"
#endif
#endif /* ZMAP_CONSTANTS_H */

191
src/cyclic.c Normal file
View File

@ -0,0 +1,191 @@
/*
* 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 <gmp.h>
#include "../lib/includes.h"
#include "../lib/logger.h"
// We will pick the first cyclic group from this list that is
// larger than the number of IPs in our allowlist. E.g. for an
// entire Internet scan, this would be cyclic32
// Note: this list should remain ordered by size (primes) ascending.
static cyclic_group_t groups[] = {
{// 2^8 + 1
.prime = 257,
.known_primroot = 3,
.prime_factors = {2},
.num_prime_factors = 1},
{// 2^16 + 1
.prime = 65537,
.known_primroot = 3,
.prime_factors = {2},
.num_prime_factors = 1},
{// 2^24 + 43
.prime = 16777259,
.known_primroot = 2,
.prime_factors = {2, 23, 103, 3541},
.num_prime_factors = 4},
{// 2^28 + 3
.prime = 268435459,
.known_primroot = 2,
.prime_factors = {2, 3, 19, 87211},
.num_prime_factors = 4},
{// 2^32 + 15
.prime = 4294967311,
.known_primroot = 3,
.prime_factors = {2, 3, 5, 131, 364289},
.num_prime_factors = 5},
{// 2^33 + 17
.prime = 8589934609,
.known_primroot = 19,
.prime_factors = {2, 3, 59, 3033169},
.num_prime_factors = 4},
{// 2^34 + 25
.prime = 17179869209,
.known_primroot = 3,
.prime_factors = {2, 83, 1277, 20261},
.num_prime_factors = 4},
{// 2^36 + 31
.prime = 68719476767,
.known_primroot = 5,
.prime_factors = {2, 163, 883, 238727},
.num_prime_factors = 4},
{// 2^40 + 15
.prime = 1099511627791,
.known_primroot = 3,
.prime_factors = {2, 3, 5, 36650387593},
.num_prime_factors = 4},
{// 2^44 + 7
.prime = 17592186044423,
.known_primroot = 5,
.prime_factors = {2, 11, 53, 97, 155542661},
.num_prime_factors = 5},
{// 2^48 + 23
.prime = 281474976710677,
.known_primroot = 6,
.prime_factors = {2, 3, 7, 1361, 2462081249},
.num_prime_factors = 5},
};
// Return a (random) number coprime with (p - 1) of the group,
// which is a generator of the additive group mod (p - 1)
static uint32_t find_primroot(const cyclic_group_t *group, aesrand_t *aes)
{
uint32_t candidate =
(uint32_t)((aesrand_getword(aes) & 0xFFFFFFFF) % group->prime);
uint64_t retv = 0;
// The maximum primitive root we can return needs to be small enough such
// that there is no overflow when multiplied by any element in the largest
// group in ZMap, which currently has p = 2^{32} + 15.
const uint64_t max_root = (UINT64_C(1) << 22);
// Repeatedly find a generator until we hit one that is small enough. For
// the largest group, we have a very low probability of ever executing this
// loop more than once, and for small groups it will only execute once.
do {
candidate += 1;
// Only one of these mods will ever have an effect.
candidate %= group->prime;
candidate %= max_root;
if (candidate == 0) {
continue;
}
mpz_t prime;
mpz_init_set_ui(prime, group->prime);
int ok = 1;
for (size_t i = 0; i < group->num_prime_factors && ok; ++i) {
const uint64_t q = group->prime_factors[i];
const uint64_t k = (group->prime - 1) / q;
mpz_t base, power, res;
mpz_init_set_ui(base, candidate);
mpz_init_set_ui(power, k);
mpz_init(res);
mpz_powm(res, base, power, prime);
uint64_t res_ui = mpz_get_ui(res);
if (res_ui == 1) {
ok = 0;
}
mpz_clear(base);
mpz_clear(power);
mpz_clear(res);
}
if (ok) {
retv = candidate;
break;
}
} while (1);
log_debug("zmap", "Isomorphism: %llu", retv);
return retv;
}
const cyclic_group_t *get_group(uint64_t min_size)
{
for (unsigned i = 0; i < sizeof(groups); ++i) {
if (groups[i].prime > min_size) {
return &groups[i];
}
}
// Should not reach, final group should always be larger than 2^48
// which is max based on 2**32 IPs and 2**16 ports
assert(0);
}
cycle_t make_cycle(const cyclic_group_t *group, aesrand_t *aes)
{
cycle_t cycle;
cycle.group = group;
cycle.generator = find_primroot(group, aes);
cycle.offset = (uint32_t)(aesrand_getword(aes) & 0xFFFFFFFF);
cycle.offset %= group->prime;
cycle.order = group->prime - 1;
return cycle;
}

45
src/cyclic.h Normal file
View File

@ -0,0 +1,45 @@
/*
* 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 CYCLIC_H
#define CYCLIC_H
#include <stdint.h>
#include <stddef.h>
#include "aesrand.h"
// Represents a multiplicative cyclic group (Z/pZ)*
typedef struct cyclic_group {
uint64_t prime; // p
uint64_t known_primroot; // Known primitive root of (Z/pZ)*
size_t num_prime_factors; // Length of num_prime_factors
uint64_t prime_factors[10]; // Unique prime factors of (p-1)
} cyclic_group_t;
// Represents a cycle in a group
typedef struct cycle {
const cyclic_group_t *group;
uint64_t generator;
uint64_t order;
uint32_t offset;
} cycle_t;
// Get a cyclic_group_t of at least min_size.
// Pointer into static data, do not free().
const cyclic_group_t *get_group(uint64_t min_size);
// Generate cycle (find generator and inverse)
cycle_t make_cycle(const cyclic_group_t *group, aesrand_t *aes);
// Perform the isomorphism from (Z/pZ)+ to (Z/pZ)*
// Given known primitive root of (Z/pZ)* n, with x in (Z/pZ)+, do:
// f(x) = n^x mod p
//
#endif

Some files were not shown because too many files have changed in this diff Show More