initial
This commit is contained in:
commit
3749a2612a
11
.clang-format
Normal file
11
.clang-format
Normal 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
18
.editorconfig
Normal 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
38
.gitignore
vendored
Normal 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
57
10gigE.md
Normal 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
168
CMakeLists.txt
Normal 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
36
CONTRIBUTING.md
Normal 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
20
checkFormat.sh
Executable 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
25
conf/blocklist.conf
Normal file
@ -0,0 +1,25 @@
|
||||
# From IANA IPv4 Special-Purpose Address Registry
|
||||
# http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
|
||||
# Updated 2013-05-22
|
||||
|
||||
0.0.0.0/8 # RFC1122: "This host on this network"
|
||||
10.0.0.0/8 # RFC1918: Private-Use
|
||||
100.64.0.0/10 # RFC6598: Shared Address Space
|
||||
127.0.0.0/8 # RFC1122: Loopback
|
||||
169.254.0.0/16 # RFC3927: Link Local
|
||||
172.16.0.0/12 # RFC1918: Private-Use
|
||||
192.0.0.0/24 # RFC6890: IETF Protocol Assignments
|
||||
192.0.2.0/24 # RFC5737: Documentation (TEST-NET-1)
|
||||
192.88.99.0/24 # RFC3068: 6to4 Relay Anycast
|
||||
192.168.0.0/16 # RFC1918: Private-Use
|
||||
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
22
conf/zmap.conf
Normal file
@ -0,0 +1,22 @@
|
||||
### Probe Module to use
|
||||
#probe-module tcp_synscan
|
||||
|
||||
|
||||
### Destination port to scan
|
||||
#target-port 443
|
||||
|
||||
### Scan rate in packets/sec
|
||||
#rate 10000
|
||||
|
||||
### Scan rate in bandwidth (bits/sec); overrides `rate`
|
||||
#bandwidth 1M # 1mbps
|
||||
|
||||
|
||||
### 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
|
4
examples/forge-socket/README
Normal file
4
examples/forge-socket/README
Normal file
@ -0,0 +1,4 @@
|
||||
Forge Socket
|
||||
============
|
||||
|
||||
Forge Socket is now maintained at https://github.com/ewust/forge_socket.
|
203
examples/probe-modules/module_tcp_cisco_backdoor.c
Normal file
203
examples/probe-modules/module_tcp_cisco_backdoor.c
Normal 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 *)(ð_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 doesn’t 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 *)(ð_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 *)ðh[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};
|
51
examples/udp-probes/README
Normal file
51
examples/udp-probes/README
Normal 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.
|
BIN
examples/udp-probes/ard_3283.pkt
Normal file
BIN
examples/udp-probes/ard_3283.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/bacnet_47808.pkt
Normal file
BIN
examples/udp-probes/bacnet_47808.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/bacnet_rpm_47808.pkt
Normal file
BIN
examples/udp-probes/bacnet_rpm_47808.pkt
Normal file
Binary file not shown.
1
examples/udp-probes/chargen_19.pkt
Normal file
1
examples/udp-probes/chargen_19.pkt
Normal file
@ -0,0 +1 @@
|
||||
|
BIN
examples/udp-probes/citrix_1604.pkt
Normal file
BIN
examples/udp-probes/citrix_1604.pkt
Normal file
Binary file not shown.
1
examples/udp-probes/coap_5683.pkt
Normal file
1
examples/udp-probes/coap_5683.pkt
Normal file
@ -0,0 +1 @@
|
||||
@}p».well-knowncore
|
BIN
examples/udp-probes/db2_523.pkt
Normal file
BIN
examples/udp-probes/db2_523.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/db2disco_523.pkt
Normal file
BIN
examples/udp-probes/db2disco_523.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/digi1_2362.pkt
Normal file
BIN
examples/udp-probes/digi1_2362.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/digi2_2362.pkt
Normal file
BIN
examples/udp-probes/digi2_2362.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/digi3_2362.pkt
Normal file
BIN
examples/udp-probes/digi3_2362.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/dns_53.pkt
Normal file
BIN
examples/udp-probes/dns_53.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/dns_53_queryAwww.google.com.pkt
Normal file
BIN
examples/udp-probes/dns_53_queryAwww.google.com.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/dns_53_queryAwww.google.it.pkt
Normal file
BIN
examples/udp-probes/dns_53_queryAwww.google.it.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/ipmi_623.pkt
Normal file
BIN
examples/udp-probes/ipmi_623.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/ldap_389.pkt
Normal file
BIN
examples/udp-probes/ldap_389.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/mdns_5353.pkt
Normal file
BIN
examples/udp-probes/mdns_5353.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/memcache_11211.pkt
Normal file
BIN
examples/udp-probes/memcache_11211.pkt
Normal file
Binary file not shown.
1
examples/udp-probes/mssql_1434.pkt
Normal file
1
examples/udp-probes/mssql_1434.pkt
Normal file
@ -0,0 +1 @@
|
||||
|
BIN
examples/udp-probes/nat_port_mapping_5351.pkt
Normal file
BIN
examples/udp-probes/nat_port_mapping_5351.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/natpmp_5351.pkt
Normal file
BIN
examples/udp-probes/natpmp_5351.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/netbios_137.pkt
Normal file
BIN
examples/udp-probes/netbios_137.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/netis_53413.pkt
Normal file
BIN
examples/udp-probes/netis_53413.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/ntp_123.pkt
Normal file
BIN
examples/udp-probes/ntp_123.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/ntp_123_monlist.pkt
Normal file
BIN
examples/udp-probes/ntp_123_monlist.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/openvpn_1194.pkt
Normal file
BIN
examples/udp-probes/openvpn_1194.pkt
Normal file
Binary file not shown.
1
examples/udp-probes/pca_nq_5632.pkt
Normal file
1
examples/udp-probes/pca_nq_5632.pkt
Normal file
@ -0,0 +1 @@
|
||||
NQ
|
1
examples/udp-probes/pca_st_5632.pkt
Normal file
1
examples/udp-probes/pca_st_5632.pkt
Normal file
@ -0,0 +1 @@
|
||||
ST
|
BIN
examples/udp-probes/pcanywhere_5632.pkt
Normal file
BIN
examples/udp-probes/pcanywhere_5632.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/portmap_111.pkt
Normal file
BIN
examples/udp-probes/portmap_111.pkt
Normal file
Binary file not shown.
1
examples/udp-probes/qotd_17.pkt
Normal file
1
examples/udp-probes/qotd_17.pkt
Normal file
@ -0,0 +1 @@
|
||||
|
BIN
examples/udp-probes/rdp_3389.pkt
Normal file
BIN
examples/udp-probes/rdp_3389.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/ripv1_520.pkt
Normal file
BIN
examples/udp-probes/ripv1_520.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/sentinel_5093.pkt
Normal file
BIN
examples/udp-probes/sentinel_5093.pkt
Normal file
Binary file not shown.
1
examples/udp-probes/sip_5060.pkt
Normal file
1
examples/udp-probes/sip_5060.pkt
Normal file
@ -0,0 +1 @@
|
||||
OPTIONS
|
12
examples/udp-probes/sip_options.tpl
Normal file
12
examples/udp-probes/sip_options.tpl
Normal 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
|
||||
|
BIN
examples/udp-probes/snmp1_161.pkt
Normal file
BIN
examples/udp-probes/snmp1_161.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/snmp2_161.pkt
Normal file
BIN
examples/udp-probes/snmp2_161.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/snmp3_161.pkt
Normal file
BIN
examples/udp-probes/snmp3_161.pkt
Normal file
Binary file not shown.
5
examples/udp-probes/ssdp_1900.pkt
Normal file
5
examples/udp-probes/ssdp_1900.pkt
Normal file
@ -0,0 +1,5 @@
|
||||
M-SEARCH * HTTP/1.1
|
||||
HOST:239.255.255.250:1900
|
||||
ST:ssdp:all
|
||||
MAN:"ssdp:discover"
|
||||
|
BIN
examples/udp-probes/tftp_69.pkt
Normal file
BIN
examples/udp-probes/tftp_69.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/ubiquiti_10001.pkt
Normal file
BIN
examples/udp-probes/ubiquiti_10001.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/ubiquiti_discovery_v1_10001.pkt
Normal file
BIN
examples/udp-probes/ubiquiti_discovery_v1_10001.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/ubiquiti_discovery_v2_10001.pkt
Normal file
BIN
examples/udp-probes/ubiquiti_discovery_v2_10001.pkt
Normal file
Binary file not shown.
7
examples/udp-probes/upnp_1900.pkt
Normal file
7
examples/udp-probes/upnp_1900.pkt
Normal file
@ -0,0 +1,7 @@
|
||||
M-SEARCH * HTTP/1.1
|
||||
Host:239.255.255.250:1900
|
||||
ST:upnp:rootdevice
|
||||
Man:"ssdp:discover"
|
||||
MX:3
|
||||
|
||||
|
BIN
examples/udp-probes/valve_27015.pkt
Normal file
BIN
examples/udp-probes/valve_27015.pkt
Normal file
Binary file not shown.
BIN
examples/udp-probes/wdbrpc_17185.pkt
Normal file
BIN
examples/udp-probes/wdbrpc_17185.pkt
Normal file
Binary file not shown.
3
examples/udp-probes/wsd_3702.pkt
Normal file
3
examples/udp-probes/wsd_3702.pkt
Normal 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>
|
1
examples/udp-probes/wsd_malformed_3702.pkt
Normal file
1
examples/udp-probes/wsd_malformed_3702.pkt
Normal file
@ -0,0 +1 @@
|
||||
<:/>
|
BIN
examples/udp-probes/xdmcp_177.pkt
Normal file
BIN
examples/udp-probes/xdmcp_177.pkt
Normal file
Binary file not shown.
24
format.sh
Executable file
24
format.sh
Executable 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
23
lib/CMakeLists.txt
Normal 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
308
lib/blocklist.c
Normal 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
50
lib/blocklist.h
Normal 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
275
lib/cachehash.c
Normal 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
46
lib/cachehash.h
Normal 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
416
lib/constraint.c
Normal 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
34
lib/constraint.h
Normal 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
52
lib/csv.c
Normal 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
25
lib/csv.h
Normal 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
45
lib/includes.h
Normal 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
57
lib/lockfd.c
Normal 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
22
lib/lockfd.h
Normal 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
271
lib/logger.c
Normal 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
67
lib/logger.h
Normal 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
111
lib/pbm.c
Normal 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
31
lib/pbm.h
Normal 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
109
lib/queue.c
Normal 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
49
lib/queue.h
Normal 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
32
lib/random.c
Normal 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
25
lib/random.h
Normal 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
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
53
lib/rijndael-alg-fst.h
Normal 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
30
lib/types.h
Normal 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
371
lib/util.c
Normal 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
74
lib/util.h
Normal 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
55
lib/xalloc.c
Normal 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
30
lib/xalloc.h
Normal 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
7
release-build.sh
Normal 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
38
scripts/check_manfile.py
Executable 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
4
src/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
lexer.c
|
||||
lexer.h
|
||||
parser.c
|
||||
parser.h
|
273
src/CMakeLists.txt
Normal file
273
src/CMakeLists.txt
Normal 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
15
src/CMakeVersion.txt
Normal 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
71
src/aesrand.c
Normal 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
24
src/aesrand.h
Normal 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
16
src/constants.h
Normal 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
191
src/cyclic.c
Normal 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
45
src/cyclic.h
Normal 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
Loading…
Reference in New Issue
Block a user