This commit is contained in:
delorean 2024-02-26 10:35:54 -06:00
commit 8559dcdfaf
Signed by: delorean
GPG Key ID: 08CFF8565BE941CD
218 changed files with 60874 additions and 0 deletions

138
Makefile Normal file
View File

@ -0,0 +1,138 @@
# If Windows, then assume the compiler is `gcc` for the
# MinGW environment. I can't figure out how to tell if it's
# actually MingGW. FIXME TODO
ifeq ($(OS),Windows_NT)
CC = gcc
endif
# Try to figure out the default compiler. I dont know the best
# way to do this with `gmake`. If you have better ideas, please
# submit a pull request on github.
ifeq ($(CC),)
ifneq (, $(shell which clang))
CC = clang
else ifneq (, $(shell which gcc))
CC = gcc
else
CC = cc
endif
endif
PREFIX ?= /usr
BINDIR ?= $(PREFIX)/bin
SYS := $(shell $(CC) -dumpmachine)
GITVER := $(shell git describe --tags)
INSTALL_DATA := -pDm755
ifeq ($(GITVER),)
GITVER = "unknown"
endif
# LINUX
# The automated regression tests run on Linux, so this is the one
# environment where things likely will work -- as well as anything
# works on the bajillion of different Linux environments
ifneq (, $(findstring linux, $(SYS)))
ifneq (, $(findstring musl, $(SYS)))
LIBS =
else
LIBS = -lm -lrt -ldl -lpthread
endif
INCLUDES =
FLAGS2 =
endif
# MAC OS X
# I occassionally develope code on Mac OS X, but it's not part of
# my regularly regression-test environment. That means at any point
# in time, something might be minorly broken in Mac OS X.
ifneq (, $(findstring darwin, $(SYS)))
LIBS = -lm
INCLUDES = -I.
FLAGS2 =
INSTALL_DATA = -pm755
endif
# MinGW on Windows
# I develope on Visual Studio 2010, so that's the Windows environment
# that'll work. However, 'git' on Windows runs under MingGW, so one
# day I acccidentally typed 'make' instead of 'git, and felt compelled
# to then fix all the errors, so this kinda works now. It's not the
# intended environment, so it make break in the future.
ifneq (, $(findstring mingw, $(SYS)))
INCLUDES = -Ivs10/include
LIBS = -L vs10/lib -lIPHLPAPI -lWs2_32
#FLAGS2 = -march=i686
endif
# Cygwin
# I hate Cygwin, use Visual Studio or MingGW instead. I just put this
# second here for completeness, or in case I gate tired of hitting my
# head with a hammer and want to feel a different sort of pain.
ifneq (, $(findstring cygwin, $(SYS)))
INCLUDES = -I.
LIBS =
FLAGS2 =
endif
# OpenBSD
ifneq (, $(findstring openbsd, $(SYS)))
LIBS = -lm -lpthread
INCLUDES = -I.
FLAGS2 =
endif
# FreeBSD
ifneq (, $(findstring freebsd, $(SYS)))
LIBS = -lm -lpthread
INCLUDES = -I.
FLAGS2 =
endif
# NetBSD
ifneq (, $(findstring netbsd, $(SYS)))
LIBS = -lm -lpthread
INCLUDES = -I.
FLAGS2 =
endif
DEFINES =
CFLAGS = -g -ggdb $(FLAGS2) $(INCLUDES) $(DEFINES) -Wall -O2
.SUFFIXES: .c .cpp
all: bin/masscan
tmp/main-conf.o: src/main-conf.c src/*.h
$(CC) $(CFLAGS) -c $< -o $@ -DGIT=\"$(GITVER)\"
# just compile everything in the 'src' directory. Using this technique
# means that include file dependencies are broken, so sometimes when
# the program crashes unexpectedly, 'make clean' then 'make' fixes the
# problem that a .h file was out of date
tmp/%.o: src/%.c src/*.h
$(CC) $(CFLAGS) -c $< -o $@
SRC = $(sort $(wildcard src/*.c))
OBJ = $(addprefix tmp/, $(notdir $(addsuffix .o, $(basename $(SRC)))))
bin/masscan: $(OBJ)
$(CC) $(CFLAGS) -o $@ $(OBJ) $(LDFLAGS) $(LIBS)
clean:
rm -f tmp/*.o
rm -f bin/masscan
regress: bin/masscan
bin/masscan --selftest
test: regress
install: bin/masscan
install $(INSTALL_DATA) bin/masscan $(DESTDIR)$(BINDIR)/masscan
default: bin/masscan

225
src/crypto-base64.c Normal file
View File

@ -0,0 +1,225 @@
#include "crypto-base64.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
/*****************************************************************************
*****************************************************************************/
size_t
base64_encode(void *vdst, size_t sizeof_dst,
const void *vsrc, size_t sizeof_src)
{
static const char *b64 =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"+/";
size_t i = 0;
size_t d = 0;
unsigned char *dst = (unsigned char *)vdst;
const unsigned char *src = (const unsigned char *)vsrc;
/* encode every 3 bytes of source into 4 bytes of destination text */
while (i + 3 <= sizeof_src) {
unsigned n;
/* make sure there is enough space */
if (d + 4 > sizeof_dst)
return d;
/* convert the chars */
n = src[i]<<16 | src[i+1]<<8 | src[i+2];
dst[d+0] = b64[ (n>>18) & 0x3F ];
dst[d+1] = b64[ (n>>12) & 0x3F ];
dst[d+2] = b64[ (n>> 6) & 0x3F ];
dst[d+3] = b64[ (n>> 0) & 0x3F ];
i += 3;
d += 4;
}
/* If the source text isn't an even multiple of 3 characters, then we'll
* have to append a '=' or '==' to the output to compensate */
if (i + 2 <= sizeof_src && d + 4 <= sizeof_dst) {
unsigned n = src[i]<<16 | src[i+1]<<8;
dst[d+0] = b64[ (n>>18) & 0x3F ];
dst[d+1] = b64[ (n>>12) & 0x3F ];
dst[d+2] = b64[ (n>> 6) & 0x3F ];
dst[d+3] = '=';
d += 4;
} else if (i + 1 <= sizeof_src && d + 4 <= sizeof_dst) {
unsigned n = src[i]<<16;
dst[d+0] = b64[ (n>>18) & 0x3F ];
dst[d+1] = b64[ (n>>12) & 0x3F ];
dst[d+2] = '=';
dst[d+3] = '=';
d += 4;
}
return d;
}
/*****************************************************************************
*****************************************************************************/
size_t
base64_decode(void *vdst, size_t sizeof_dst,
const void *vsrc, size_t sizeof_src)
{
static const unsigned char rstr[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 62, 0xFF, 0xFF, 0xFF, 63,
52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};
size_t i = 0;
size_t d = 0;
unsigned char *dst = (unsigned char *)vdst;
const unsigned char *src = (const unsigned char *)vsrc;
while (i < sizeof_src) {
unsigned b;
unsigned c=0;
/* byte#1 */
while (i<sizeof_src && (c = rstr[src[i]]) > 64)
i++;
if (src[i] == '=' || i++ >= sizeof_src)
break;
b = (c << 2) & 0xfc;
while (i<sizeof_src && (c = rstr[src[i]]) > 64)
i++;
if (src[i] == '=' || i++ >= sizeof_src)
break;
b |= (c>>4) & 0x03;
if (d<sizeof_dst)
dst[d++] = (unsigned char)b;
if (i>=sizeof_src)
break;
/* byte#2 */
b = (c<<4) & 0xF0;
while (i<sizeof_src && src[i] != '=' && (c = rstr[src[i]]) > 64)
;
if (src[i] == '=' || i++ >= sizeof_src)
break;
b |= (c>>2) & 0x0F;
if (d<sizeof_dst)
dst[d++] = (unsigned char)b;
if (i>=sizeof_src)
break;
/* byte#3*/
b = (c<<6) & 0xC0;
while (i<sizeof_src && src[i] != '=' && (c = rstr[src[i]]) > 64)
;
if (src[i] == '=' || i++ >= sizeof_src)
break;
b |= c;
if (d<sizeof_dst)
dst[d++] = (unsigned char)b;
if (i>=sizeof_src)
break;
}
if (d<sizeof_dst)
dst[d] = '\0';
return d;
}
/*****************************************************************************
* Provide my own rand() simply to avoid static-analysis warning me that
* 'rand()' is unrandom, when in fact we want the non-random properties of
* rand() for regression testing.
*****************************************************************************/
static unsigned
r_rand(unsigned *seed)
{
static const unsigned a = 214013;
static const unsigned c = 2531011;
*seed = (*seed) * a + c;
return (*seed)>>16 & 0x7fff;
}
/*****************************************************************************
*****************************************************************************/
int
base64_selftest(void)
{
char buf[100];
char buf2[100];
char buf3[100];
size_t buf_len;
size_t buf2_len;
unsigned i;
unsigned seed = (unsigned)time(0);
buf_len = base64_encode(buf, sizeof(buf), "hello", 5);
buf2_len = base64_decode(buf2, sizeof(buf2), buf, buf_len);
if (buf2_len != 5 && memcmp(buf2, "hello", 5) != 0) {
fprintf(stderr, "base64: selftest failed\n");
return 1;
}
/*
* Generate a bunch of random strings, encode them, then decode them,
* making sure the final result matches the original string
*/
for (i=0; i<100; i++) {
unsigned j;
size_t buf3_len;
/* create a string of random bytes */
buf_len = r_rand(&seed) % 50;
for (j=0; j<buf_len; j++) {
buf[j] = (char)r_rand(&seed);
}
/* encode it */
buf2_len = base64_encode(buf2, sizeof(buf2), buf, buf_len);
/* decode it back again */
buf3_len = base64_decode(buf3, sizeof(buf3), buf2, buf2_len);
/* now make sure result equals original */
if (buf3_len != buf_len && memcmp(buf3, buf, buf_len) != 0) {
fprintf(stderr, "base64: selftest failed\n");
return 1;
}
}
return 0;
}

10
src/crypto-base64.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef CRYPTO_BASE64_H
#define CRYPTO_BASE64_H
#include <stdio.h>
size_t base64_decode(void *dst, size_t sizeof_dst, const void *src, size_t sizeof_src);
size_t base64_encode(void *dst, size_t sizeof_dst, const void *src, size_t sizeof_src);
int base64_selftest(void);
#endif

420
src/crypto-blackrock.c Normal file
View File

@ -0,0 +1,420 @@
/*
BlackRock cipher
(h/t Marsh Ray @marshray for this idea)
This is a randomization/reshuffling function based on a crypto
"Feistel network" as described in the paper:
'Ciphers with Arbitrary Finite Domains'
by John Black and Phillip Rogaway
http://www.cs.ucdavis.edu/~rogaway/papers/subset.pdf
This is a crypto-like construction that encrypts an arbitrary sized
range. Given a number in the range [0..9999], it'll produce a mapping
to a distinct different number in the same range (and back again).
In other words, it randomizes the order of numbers in a sequence.
For example, it can be used to randomize the sequence [0..9]:
0 -> 6
1 -> 4
2 -> 8
3 -> 1
4 -> 9
5 -> 3
6 -> 0
7 -> 5
8 -> 2
9 -> 7
As you can see on the right hand side, the numbers are in random
order, and they don't repeat.
This is create for port scanning. We can take an index variable
and increment it during a scan, then use this function to
randomize it, yet be assured that we've probed every IP and port
within the range.
The cryptographic strength of this construction depends upon the
number of rounds, and the exact nature of the inner "READ()" function.
Because it's a Feistel network, that "READ()" function can be almost
anything.
We don't care about cryptographic strength, just speed, so we are
using a trivial READ() function.
This is a class of "format-preserving encryption". There are
probably better constructions than what I'm using.
*/
#include "crypto-blackrock.h"
#include "pixie-timer.h"
#include "util-malloc.h"
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <time.h>
#if defined(_MSC_VER)
#define inline _inline
#endif
/***************************************************************************
* It's an s-box. You gotta have an s-box
***************************************************************************/
const unsigned char sbox[256] = {
0x91, 0x58, 0xb3, 0x31, 0x6c, 0x33, 0xda, 0x88,
0x57, 0xdd, 0x8c, 0xf2, 0x29, 0x5a, 0x08, 0x9f,
0x49, 0x34, 0xce, 0x99, 0x9e, 0xbf, 0x0f, 0x81,
0xd4, 0x2f, 0x92, 0x3f, 0x95, 0xf5, 0x23, 0x00,
0x0d, 0x3e, 0xa8, 0x90, 0x98, 0xdd, 0x20, 0x00,
0x03, 0x69, 0x0a, 0xca, 0xba, 0x12, 0x08, 0x41,
0x6e, 0xb9, 0x86, 0xe4, 0x50, 0xf0, 0x84, 0xe2,
0xb3, 0xb3, 0xc8, 0xb5, 0xb2, 0x2d, 0x18, 0x70,
0x0a, 0xd7, 0x92, 0x90, 0x9e, 0x1e, 0x0c, 0x1f,
0x08, 0xe8, 0x06, 0xfd, 0x85, 0x2f, 0xaa, 0x5d,
0xcf, 0xf9, 0xe3, 0x55, 0xb9, 0xfe, 0xa6, 0x7f,
0x44, 0x3b, 0x4a, 0x4f, 0xc9, 0x2f, 0xd2, 0xd3,
0x8e, 0xdc, 0xae, 0xba, 0x4f, 0x02, 0xb4, 0x76,
0xba, 0x64, 0x2d, 0x07, 0x9e, 0x08, 0xec, 0xbd,
0x52, 0x29, 0x07, 0xbb, 0x9f, 0xb5, 0x58, 0x6f,
0x07, 0x55, 0xb0, 0x34, 0x74, 0x9f, 0x05, 0xb2,
0xdf, 0xa9, 0xc6, 0x2a, 0xa3, 0x5d, 0xff, 0x10,
0x40, 0xb3, 0xb7, 0xb4, 0x63, 0x6e, 0xf4, 0x3e,
0xee, 0xf6, 0x49, 0x52, 0xe3, 0x11, 0xb3, 0xf1,
0xfb, 0x60, 0x48, 0xa1, 0xa4, 0x19, 0x7a, 0x2e,
0x90, 0x28, 0x90, 0x8d, 0x5e, 0x8c, 0x8c, 0xc4,
0xf2, 0x4a, 0xf6, 0xb2, 0x19, 0x83, 0xea, 0xed,
0x6d, 0xba, 0xfe, 0xd8, 0xb6, 0xa3, 0x5a, 0xb4,
0x48, 0xfa, 0xbe, 0x5c, 0x69, 0xac, 0x3c, 0x8f,
0x63, 0xaf, 0xa4, 0x42, 0x25, 0x50, 0xab, 0x65,
0x80, 0x65, 0xb9, 0xfb, 0xc7, 0xf2, 0x2d, 0x5c,
0xe3, 0x4c, 0xa4, 0xa6, 0x8e, 0x07, 0x9c, 0xeb,
0x41, 0x93, 0x65, 0x44, 0x4a, 0x86, 0xc1, 0xf6,
0x2c, 0x97, 0xfd, 0xf4, 0x6c, 0xdc, 0xe1, 0xe0,
0x28, 0xd9, 0x89, 0x7b, 0x09, 0xe2, 0xa0, 0x38,
0x74, 0x4a, 0xa6, 0x5e, 0xd2, 0xe2, 0x4d, 0xf3,
0xf4, 0xc6, 0xbc, 0xa2, 0x51, 0x58, 0xe8, 0xae,
};
/***************************************************************************
***************************************************************************/
void
blackrock_init(struct BlackRock *br, uint64_t range, uint64_t seed, unsigned rounds)
{
double foo = sqrt(range * 1.0);
/* This algorithm gets very non-random at small numbers, so I'm going
* to try to fix some constants here to make it work. It doesn't have
* to be good, since it's kinda pointless having ranges this small */
switch (range) {
case 0:
br->a = 0;
br->b = 0;
break;
case 1:
br->a = 1;
br->b = 1;
break;
case 2:
br->a = 1;
br->b = 2;
break;
case 3:
br->a = 2;
br->b = 2;
break;
case 4:
case 5:
case 6:
br->a = 2;
br->b = 3;
break;
case 7:
case 8:
br->a = 3;
br->b = 3;
break;
default:
br->range = range;
br->a = (uint64_t)(foo - 2);
br->b = (uint64_t)(foo + 3);
break;
}
while (br->a * br->b <= range)
br->b++;
br->rounds = rounds;
br->seed = seed;
br->range = range;
}
/***************************************************************************
* The inner round/mixer function. In DES, it's a series of S-box lookups,
* which
***************************************************************************/
static inline uint64_t
READ(uint64_t r, uint64_t R, uint64_t seed)
{
uint64_t r0, r1, r2, r3;
#define GETBYTE(R,n) ((((R)>>(n*8))^seed^r)&0xFF)
R ^= (seed << r) ^ (seed >> (64 - r));
r0 = sbox[GETBYTE(R,0)]<< 0 | sbox[GETBYTE(R,1)]<< 8;
r1 = (sbox[GETBYTE(R,2)]<<16UL | sbox[GETBYTE(R,3)]<<24UL)&0x0ffffFFFFUL;
r2 = sbox[GETBYTE(R,4)]<< 0 | sbox[GETBYTE(R,5)]<< 8;
r3 = (sbox[GETBYTE(R,6)]<<16UL | sbox[GETBYTE(R,7)]<<24UL)&0x0ffffFFFFUL;
R = r0 ^ r1 ^ r2<<23UL ^ r3<<33UL;
return R;
}
/***************************************************************************
*
* NOTE:
* the names in this function are cryptic in order to match as closely
* as possible the pseudocode in the following paper:
* http://www.cs.ucdavis.edu/~rogaway/papers/subset.pdf
* Read that paper in order to understand this code.
***************************************************************************/
static inline uint64_t
ENCRYPT(unsigned r, uint64_t a, uint64_t b, uint64_t m, uint64_t seed)
{
uint64_t L, R;
unsigned j;
uint64_t tmp;
L = m % a;
R = m / a;
for (j=1; j<=r; j++) {
if (j & 1) {
tmp = (L + READ(j, R, seed)) % a;
} else {
tmp = (L + READ(j, R, seed)) % b;
}
L = R;
R = tmp;
}
if (r & 1) {
return a * L + R;
} else {
return a * R + L;
}
}
/***************************************************************************
***************************************************************************/
static inline uint64_t
UNENCRYPT(unsigned r, uint64_t a, uint64_t b, uint64_t m, uint64_t seed)
{
uint64_t L, R;
unsigned j;
uint64_t tmp;
if (r & 1) {
R = m % a;
L = m / a;
} else {
L = m % a;
R = m / a;
}
for (j=r; j>=1; j--) {
if (j & 1) {
tmp = READ(j, L, seed);
if (tmp > R) {
tmp = (tmp - R);
tmp = a - (tmp%a);
if (tmp == a)
tmp = 0;
} else {
tmp = (R - tmp);
tmp %= a;
}
} else {
tmp = READ(j, L, seed);
if (tmp > R) {
tmp = (tmp - R);
tmp = b - (tmp%b);
if (tmp == b)
tmp = 0;
} else {
tmp = (R - tmp);
tmp %= b;
}
}
R = L;
L = tmp;
}
return a * R + L;
}
/***************************************************************************
***************************************************************************/
uint64_t
blackrock_shuffle(const struct BlackRock *br, uint64_t m)
{
uint64_t c;
c = ENCRYPT(br->rounds, br->a, br->b, m, br->seed);
while (c >= br->range)
c = ENCRYPT(br->rounds, br->a, br->b, c, br->seed);
return c;
}
/***************************************************************************
***************************************************************************/
uint64_t
blackrock_unshuffle(const struct BlackRock *br, uint64_t m)
{
uint64_t c;
c = UNENCRYPT(br->rounds, br->a, br->b, m, br->seed);
while (c >= br->range)
c = UNENCRYPT(br->rounds, br->a, br->b, c, br->seed);
return c;
}
/***************************************************************************
* This function called only during selftest/regression-test.
***************************************************************************/
static unsigned
blackrock_verify(struct BlackRock *br, uint64_t max)
{
unsigned char *list;
uint64_t i;
unsigned is_success = 1;
uint64_t range = br->range;
/* Allocate a list of 1-byte counters */
list = CALLOC(1, (size_t)((range<max)?range:max));
/* For all numbers in the range, verify increment the counter for
* the output. */
for (i=0; i<range; i++) {
uint64_t x = blackrock_shuffle(br, i);
if (x < max)
list[x]++;
}
/* Now check the output to make sure that every counter is set exactly
* to the value of '1'. */
for (i=0; i<max && i<range; i++) {
if (list[i] != 1)
is_success = 0;
}
free(list);
return is_success;
}
/***************************************************************************
***************************************************************************/
void
blackrock_benchmark(unsigned rounds)
{
struct BlackRock br;
uint64_t range = 0x012356789123ULL;
uint64_t i;
uint64_t result = 0;
uint64_t start, stop;
static const uint64_t ITERATIONS = 5000000ULL;
printf("-- blackrock-1 -- \n");
printf("rounds = %u\n", rounds);
blackrock_init(&br, range, 1, rounds);
/*
* Time the algorithm
*/
start = pixie_nanotime();
for (i=0; i<ITERATIONS; i++) {
result += blackrock_shuffle(&br, i);
}
stop = pixie_nanotime();
/*
* Print the results
*/
if (result) {
double elapsed = ((double)(stop - start))/(1000000000.0);
double rate = ITERATIONS/elapsed;
rate /= 1000000.0;
printf("iterations/second = %5.3f-million\n", rate);
}
printf("\n");
}
/***************************************************************************
***************************************************************************/
int
blackrock_selftest(void)
{
uint64_t i;
uint64_t range;
/* @marshray
* Basic test of decryption. I take the index, encrypt it, then decrypt it,
* which means I should get the original index back again. Only, it's not
* working. The decryption fails. The reason it's failing is obvious -- I'm
* just not seeing it though. The error is probably in the 'UNENCRYPT()'
* function above.
*/
{
struct BlackRock br;
blackrock_init(&br, 1000, 0, 4);
for (i=0; i<10; i++) {
uint64_t result, result2;
result = blackrock_shuffle(&br, i);
result2 = blackrock_unshuffle(&br, result);
if (i != result2)
return 1; /*fail*/
}
}
range = 3015 * 3;
for (i=0; i<5; i++) {
struct BlackRock br;
int is_success;
range += 10 + i;
range *= 2;
blackrock_init(&br, range, time(0), 4);
is_success = blackrock_verify(&br, range);
if (!is_success) {
fprintf(stderr, "BLACKROCK: randomization failed\n");
return 1; /*fail*/
}
}
return 0; /*success*/
}

79
src/crypto-blackrock.h Normal file
View File

@ -0,0 +1,79 @@
#ifndef RAND_BLACKROCK_H
#define RAND_BLACKROCK_H
#include <stdint.h>
struct BlackRock {
uint64_t range;
uint64_t a;
uint64_t b;
uint64_t seed;
unsigned rounds;
uint64_t a_bits;
uint64_t a_mask;
uint64_t b_bits;
uint64_t b_mask;
};
/**
* Initializes a structure for shuffling numbers within
* a range.
*
* @param range
* The size of the range of numbers needing to be
* shuffled/randomized.
*/
void
blackrock_init(struct BlackRock *br, uint64_t range, uint64_t seed, unsigned rounds);
void
blackrock2_init(struct BlackRock *br, uint64_t range, uint64_t seed, unsigned rounds);
/**
* Given a number within a range, produce a different number with
* the same range. There is a 1-to-1 mapping between the two,
* so when linearly incrementing through the range, the output
* of this function won't repeat. In other words, encrypt the index variable.
* @param br
* The randomization parameters created with 'blackrock_init()'
* @param index
* An input within the specified range. We call it an 'index' variable
* because that's how we intend to use this function, shuffling a
* monotonically increasing index variable, but in truth, any sort
* of integer can be used. This must be within the 'range' specified
* during the call to blackrock_init(), or the results are undefined.
* @return
* A one-to-one matching index that's in the same range.
*/
uint64_t
blackrock_shuffle(const struct BlackRock *br, uint64_t index);
uint64_t
blackrock2_shuffle(const struct BlackRock *br, uint64_t index);
/**
* The reverse of the shuffle function above: given the shuffled/encrypted
* integer, return the original index value before the shuffling/encryption.
*/
uint64_t
blackrock_unshuffle(const struct BlackRock *br, uint64_t m);
uint64_t
blackrock2_unshuffle(const struct BlackRock *br, uint64_t m);
/**
* Do a regression test.
* @return
* 0 of the regression test succeeds or non-zero if it fails
*/
int
blackrock_selftest(void);
int
blackrock2_selftest(void);
/**
* Do a benchmark of this module regression test.
*/
void
blackrock_benchmark(unsigned rounds);
void
blackrock2_benchmark(unsigned rounds);
#endif

573
src/crypto-blackrock2.c Normal file
View File

@ -0,0 +1,573 @@
#include "crypto-blackrock.h"
#include "pixie-timer.h"
#include "unusedparm.h"
#include "util-malloc.h"
#include "util-safefunc.h"
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <time.h>
#if defined(_MSC_VER)
#define inline _inline
#endif
/*
* Expanded DES S-boxes
*/
static const uint32_t SB1[64] =
{
0x01010400, 0x00000000, 0x00010000, 0x01010404,
0x01010004, 0x00010404, 0x00000004, 0x00010000,
0x00000400, 0x01010400, 0x01010404, 0x00000400,
0x01000404, 0x01010004, 0x01000000, 0x00000004,
0x00000404, 0x01000400, 0x01000400, 0x00010400,
0x00010400, 0x01010000, 0x01010000, 0x01000404,
0x00010004, 0x01000004, 0x01000004, 0x00010004,
0x00000000, 0x00000404, 0x00010404, 0x01000000,
0x00010000, 0x01010404, 0x00000004, 0x01010000,
0x01010400, 0x01000000, 0x01000000, 0x00000400,
0x01010004, 0x00010000, 0x00010400, 0x01000004,
0x00000400, 0x00000004, 0x01000404, 0x00010404,
0x01010404, 0x00010004, 0x01010000, 0x01000404,
0x01000004, 0x00000404, 0x00010404, 0x01010400,
0x00000404, 0x01000400, 0x01000400, 0x00000000,
0x00010004, 0x00010400, 0x00000000, 0x01010004
};
static const uint32_t SB2[64] =
{
0x80108020, 0x80008000, 0x00008000, 0x00108020,
0x00100000, 0x00000020, 0x80100020, 0x80008020,
0x80000020, 0x80108020, 0x80108000, 0x80000000,
0x80008000, 0x00100000, 0x00000020, 0x80100020,
0x00108000, 0x00100020, 0x80008020, 0x00000000,
0x80000000, 0x00008000, 0x00108020, 0x80100000,
0x00100020, 0x80000020, 0x00000000, 0x00108000,
0x00008020, 0x80108000, 0x80100000, 0x00008020,
0x00000000, 0x00108020, 0x80100020, 0x00100000,
0x80008020, 0x80100000, 0x80108000, 0x00008000,
0x80100000, 0x80008000, 0x00000020, 0x80108020,
0x00108020, 0x00000020, 0x00008000, 0x80000000,
0x00008020, 0x80108000, 0x00100000, 0x80000020,
0x00100020, 0x80008020, 0x80000020, 0x00100020,
0x00108000, 0x00000000, 0x80008000, 0x00008020,
0x80000000, 0x80100020, 0x80108020, 0x00108000
};
static const uint32_t SB3[64] =
{
0x00000208, 0x08020200, 0x00000000, 0x08020008,
0x08000200, 0x00000000, 0x00020208, 0x08000200,
0x00020008, 0x08000008, 0x08000008, 0x00020000,
0x08020208, 0x00020008, 0x08020000, 0x00000208,
0x08000000, 0x00000008, 0x08020200, 0x00000200,
0x00020200, 0x08020000, 0x08020008, 0x00020208,
0x08000208, 0x00020200, 0x00020000, 0x08000208,
0x00000008, 0x08020208, 0x00000200, 0x08000000,
0x08020200, 0x08000000, 0x00020008, 0x00000208,
0x00020000, 0x08020200, 0x08000200, 0x00000000,
0x00000200, 0x00020008, 0x08020208, 0x08000200,
0x08000008, 0x00000200, 0x00000000, 0x08020008,
0x08000208, 0x00020000, 0x08000000, 0x08020208,
0x00000008, 0x00020208, 0x00020200, 0x08000008,
0x08020000, 0x08000208, 0x00000208, 0x08020000,
0x00020208, 0x00000008, 0x08020008, 0x00020200
};
static const uint32_t SB4[64] =
{
0x00802001, 0x00002081, 0x00002081, 0x00000080,
0x00802080, 0x00800081, 0x00800001, 0x00002001,
0x00000000, 0x00802000, 0x00802000, 0x00802081,
0x00000081, 0x00000000, 0x00800080, 0x00800001,
0x00000001, 0x00002000, 0x00800000, 0x00802001,
0x00000080, 0x00800000, 0x00002001, 0x00002080,
0x00800081, 0x00000001, 0x00002080, 0x00800080,
0x00002000, 0x00802080, 0x00802081, 0x00000081,
0x00800080, 0x00800001, 0x00802000, 0x00802081,
0x00000081, 0x00000000, 0x00000000, 0x00802000,
0x00002080, 0x00800080, 0x00800081, 0x00000001,
0x00802001, 0x00002081, 0x00002081, 0x00000080,
0x00802081, 0x00000081, 0x00000001, 0x00002000,
0x00800001, 0x00002001, 0x00802080, 0x00800081,
0x00002001, 0x00002080, 0x00800000, 0x00802001,
0x00000080, 0x00800000, 0x00002000, 0x00802080
};
static const uint32_t SB5[64] =
{
0x00000100, 0x02080100, 0x02080000, 0x42000100,
0x00080000, 0x00000100, 0x40000000, 0x02080000,
0x40080100, 0x00080000, 0x02000100, 0x40080100,
0x42000100, 0x42080000, 0x00080100, 0x40000000,
0x02000000, 0x40080000, 0x40080000, 0x00000000,
0x40000100, 0x42080100, 0x42080100, 0x02000100,
0x42080000, 0x40000100, 0x00000000, 0x42000000,
0x02080100, 0x02000000, 0x42000000, 0x00080100,
0x00080000, 0x42000100, 0x00000100, 0x02000000,
0x40000000, 0x02080000, 0x42000100, 0x40080100,
0x02000100, 0x40000000, 0x42080000, 0x02080100,
0x40080100, 0x00000100, 0x02000000, 0x42080000,
0x42080100, 0x00080100, 0x42000000, 0x42080100,
0x02080000, 0x00000000, 0x40080000, 0x42000000,
0x00080100, 0x02000100, 0x40000100, 0x00080000,
0x00000000, 0x40080000, 0x02080100, 0x40000100
};
static const uint32_t SB6[64] =
{
0x20000010, 0x20400000, 0x00004000, 0x20404010,
0x20400000, 0x00000010, 0x20404010, 0x00400000,
0x20004000, 0x00404010, 0x00400000, 0x20000010,
0x00400010, 0x20004000, 0x20000000, 0x00004010,
0x00000000, 0x00400010, 0x20004010, 0x00004000,
0x00404000, 0x20004010, 0x00000010, 0x20400010,
0x20400010, 0x00000000, 0x00404010, 0x20404000,
0x00004010, 0x00404000, 0x20404000, 0x20000000,
0x20004000, 0x00000010, 0x20400010, 0x00404000,
0x20404010, 0x00400000, 0x00004010, 0x20000010,
0x00400000, 0x20004000, 0x20000000, 0x00004010,
0x20000010, 0x20404010, 0x00404000, 0x20400000,
0x00404010, 0x20404000, 0x00000000, 0x20400010,
0x00000010, 0x00004000, 0x20400000, 0x00404010,
0x00004000, 0x00400010, 0x20004010, 0x00000000,
0x20404000, 0x20000000, 0x00400010, 0x20004010
};
static const uint32_t SB7[64] =
{
0x00200000, 0x04200002, 0x04000802, 0x00000000,
0x00000800, 0x04000802, 0x00200802, 0x04200800,
0x04200802, 0x00200000, 0x00000000, 0x04000002,
0x00000002, 0x04000000, 0x04200002, 0x00000802,
0x04000800, 0x00200802, 0x00200002, 0x04000800,
0x04000002, 0x04200000, 0x04200800, 0x00200002,
0x04200000, 0x00000800, 0x00000802, 0x04200802,
0x00200800, 0x00000002, 0x04000000, 0x00200800,
0x04000000, 0x00200800, 0x00200000, 0x04000802,
0x04000802, 0x04200002, 0x04200002, 0x00000002,
0x00200002, 0x04000000, 0x04000800, 0x00200000,
0x04200800, 0x00000802, 0x00200802, 0x04200800,
0x00000802, 0x04000002, 0x04200802, 0x04200000,
0x00200800, 0x00000000, 0x00000002, 0x04200802,
0x00000000, 0x00200802, 0x04200000, 0x00000800,
0x04000002, 0x04000800, 0x00000800, 0x00200002
};
static const uint32_t SB8[64] =
{
0x10001040, 0x00001000, 0x00040000, 0x10041040,
0x10000000, 0x10001040, 0x00000040, 0x10000000,
0x00040040, 0x10040000, 0x10041040, 0x00041000,
0x10041000, 0x00041040, 0x00001000, 0x00000040,
0x10040000, 0x10000040, 0x10001000, 0x00001040,
0x00041000, 0x00040040, 0x10040040, 0x10041000,
0x00001040, 0x00000000, 0x00000000, 0x10040040,
0x10000040, 0x10001000, 0x00041040, 0x00040000,
0x00041040, 0x00040000, 0x10041000, 0x00001000,
0x00000040, 0x10040040, 0x00001000, 0x00041040,
0x10001000, 0x00000040, 0x10000040, 0x10040000,
0x10040040, 0x10000000, 0x00040000, 0x10001040,
0x00000000, 0x10041040, 0x00040040, 0x10000040,
0x10040000, 0x10001000, 0x10001040, 0x00000000,
0x10041040, 0x00041000, 0x00041000, 0x00001040,
0x00001040, 0x00040040, 0x10000000, 0x10041000
};
/***************************************************************************
* It's an s-box. You gotta have an s-box
***************************************************************************/
const unsigned char sbox2[] = {
0x91, 0x58, 0xb3, 0x31, 0x6c, 0x33, 0xda, 0x88,
0x57, 0xdd, 0x8c, 0xf2, 0x29, 0x5a, 0x08, 0x9f,
0x49, 0x34, 0xce, 0x99, 0x9e, 0xbf, 0x0f, 0x81,
0xd4, 0x2f, 0x92, 0x3f, 0x95, 0xf5, 0x23, 0x00,
0x0d, 0x3e, 0xa8, 0x90, 0x98, 0xdd, 0x20, 0x00,
0x03, 0x69, 0x0a, 0xca, 0xba, 0x12, 0x08, 0x41,
0x6e, 0xb9, 0x86, 0xe4, 0x50, 0xf0, 0x84, 0xe2,
0xb3, 0xb3, 0xc8, 0xb5, 0xb2, 0x2d, 0x18, 0x70,
0x0a, 0xd7, 0x92, 0x90, 0x9e, 0x1e, 0x0c, 0x1f,
0x08, 0xe8, 0x06, 0xfd, 0x85, 0x2f, 0xaa, 0x5d,
0xcf, 0xf9, 0xe3, 0x55, 0xb9, 0xfe, 0xa6, 0x7f,
0x44, 0x3b, 0x4a, 0x4f, 0xc9, 0x2f, 0xd2, 0xd3,
0x8e, 0xdc, 0xae, 0xba, 0x4f, 0x02, 0xb4, 0x76,
0xba, 0x64, 0x2d, 0x07, 0x9e, 0x08, 0xec, 0xbd,
0x52, 0x29, 0x07, 0xbb, 0x9f, 0xb5, 0x58, 0x6f,
0x07, 0x55, 0xb0, 0x34, 0x74, 0x9f, 0x05, 0xb2,
0xdf, 0xa9, 0xc6, 0x2a, 0xa3, 0x5d, 0xff, 0x10,
0x40, 0xb3, 0xb7, 0xb4, 0x63, 0x6e, 0xf4, 0x3e,
0xee, 0xf6, 0x49, 0x52, 0xe3, 0x11, 0xb3, 0xf1,
0xfb, 0x60, 0x48, 0xa1, 0xa4, 0x19, 0x7a, 0x2e,
0x90, 0x28, 0x90, 0x8d, 0x5e, 0x8c, 0x8c, 0xc4,
0xf2, 0x4a, 0xf6, 0xb2, 0x19, 0x83, 0xea, 0xed,
0x6d, 0xba, 0xfe, 0xd8, 0xb6, 0xa3, 0x5a, 0xb4,
0x48, 0xfa, 0xbe, 0x5c, 0x69, 0xac, 0x3c, 0x8f,
0x63, 0xaf, 0xa4, 0x42, 0x25, 0x50, 0xab, 0x65,
0x80, 0x65, 0xb9, 0xfb, 0xc7, 0xf2, 0x2d, 0x5c,
0xe3, 0x4c, 0xa4, 0xa6, 0x8e, 0x07, 0x9c, 0xeb,
0x41, 0x93, 0x65, 0x44, 0x4a, 0x86, 0xc1, 0xf6,
0x2c, 0x97, 0xfd, 0xf4, 0x6c, 0xdc, 0xe1, 0xe0,
0x28, 0xd9, 0x89, 0x7b, 0x09, 0xe2, 0xa0, 0x38,
0x74, 0x4a, 0xa6, 0x5e, 0xd2, 0xe2, 0x4d, 0xf3,
0xf4, 0xc6, 0xbc, 0xa2, 0x51, 0x58, 0xe8, 0xae,
0x91, 0x58, 0xb3, 0x31, 0x6c, 0x33, 0xda, 0x88,
};
/****************************************************************************
* Given a number, figure out the nearest power-of-two (16,32,64,128,etc.)
* that can hold that number. We do this so that we can convert multiplies
* into shifts.
****************************************************************************/
static uint64_t
next_power_of_two(uint64_t num)
{
uint64_t power_of_two = 1;
num++;
while ((uint64_t)(1ULL << power_of_two) < num)
power_of_two++;
return (1ULL << power_of_two);
}
static uint64_t
bit_count(uint64_t num)
{
uint64_t bits = 0;
while ((num >> bits) > 1)
bits++;
return bits;
}
/***************************************************************************
***************************************************************************/
void
blackrock2_init(struct BlackRock *br, uint64_t range, uint64_t seed, unsigned rounds)
{
uint64_t a;
uint64_t b;
a = next_power_of_two(
(uint64_t)sqrt(range * 1.0)
);
b = next_power_of_two(range/a);
//printf("a=%llu b=%llu seed = 0x%llu\n", a, b, seed);
br->range = range;
br->a = a;
br->a_bits = bit_count(br->a);
br->a_mask = br->a - 1ULL;
br->b = b;
br->b_bits = bit_count(br->b);
br->b_mask = br->b - 1ULL;
//printf("a: 0x%llx / %llu\n", br->a_mask, br->a_bits);
//printf("b: 0x%llx / %llu\n", br->b_mask, br->b_bits);
br->rounds = rounds;
br->seed = seed;
br->range = range;
}
/***************************************************************************
* The inner round/mixer function. In DES, it's a series of S-box lookups,
* which
***************************************************************************/
static inline uint64_t
ROUND(uint64_t r, uint64_t R, uint64_t seed)
{
#define GETBYTE(R,n) ((uint64_t)(((((R)>>(n*8ULL)))&0xFFULL)))
#if 0
uint64_t r0, r1, r2, r3;
#endif
uint64_t T, Y;
T = R ^ ((seed>>r) | (seed<<(64-r)));
if (r & 1) {
Y = SB8[ (T ) & 0x3F ] ^ \
SB6[ (T >> 8) & 0x3F ] ^ \
SB4[ (T >> 16) & 0x3F ] ^ \
SB2[ (T >> 24) & 0x3F ]; \
} else {
Y = SB7[ (T ) & 0x3F ] ^ \
SB5[ (T >> 8) & 0x3F ] ^ \
SB3[ (T >> 16) & 0x3F ] ^ \
SB1[ (T >> 24) & 0x3F ];
}
return Y;
#if 0
r0 = sbox2[GETBYTE(R,0)]<< 6 | sbox2[GETBYTE(R,1)]<< 0;
r1 = sbox2[GETBYTE(R,2)]<< 6 | sbox2[GETBYTE(R,5)]<< 0;
r2 = sbox2[GETBYTE(R,4)]<< 6 | sbox2[GETBYTE(R,5)]<< 0;
r3 = sbox2[GETBYTE(R,6)]<< 6 | sbox2[GETBYTE(R,7)]<< 0;
R = r0 ^ (r1<<12) * (r2 << 24) ^ (r3 << 36) * r;
return R;
/*return((uint64_t)sbox2[GETBYTE(R,7ULL)]<< 0ULL)
| ((uint64_t)sbox2[GETBYTE(R,6ULL)]<< 8ULL)
| ((uint64_t)sbox2[GETBYTE(R,5ULL)]<<16ULL)
| ((uint64_t)sbox2[GETBYTE(R,4ULL)]<<24ULL)
| ((uint64_t)sbox2[GETBYTE(R,3ULL)]<<32ULL)
| ((uint64_t)sbox2[GETBYTE(R,2ULL)]<<40ULL)
| ((uint64_t)sbox2[GETBYTE(R,1ULL)]<<48ULL)
| ((uint64_t)sbox2[GETBYTE(R,0ULL)]<<56ULL)
;*/
return R;
#endif
}
/***************************************************************************
***************************************************************************/
static inline uint64_t
ENCRYPT(unsigned r, uint64_t a_bits, uint64_t a_mask, uint64_t b_bits, uint64_t b_mask, uint64_t m, uint64_t seed)
{
uint64_t L, R;
unsigned j = 1;
uint64_t tmp;
UNUSEDPARM(b_bits);
L = m & a_mask;
R = m >> a_bits;
for (j=1; j<=r; j++) {
tmp = (L + ROUND(j, R, seed)) & a_mask;
L = R;
R = tmp;
j++;
tmp = (L + ROUND(j, R, seed)) & b_mask;
L = R;
R = tmp;
}
if ((j-1) & 1) {
return (L << (a_bits)) + R;
} else {
return (R << (a_bits)) + L;
}
}
static inline uint64_t
DECRYPT(unsigned r, uint64_t a, uint64_t b, uint64_t m, uint64_t seed)
{
uint64_t L, R;
unsigned j;
uint64_t tmp;
if (r & 1) {
R = m % a;
L = m / a;
} else {
L = m % a;
R = m / a;
}
for (j=r; j>=1; j--) {
if (j & 1) {
tmp = ROUND(j, L, seed);
if (tmp > R) {
tmp = (tmp - R);
tmp = a - (tmp%a);
if (tmp == a)
tmp = 0;
} else {
tmp = (R - tmp);
tmp %= a;
}
} else {
tmp = ROUND(j, L, seed);
if (tmp > R) {
tmp = (tmp - R);
tmp = b - (tmp%b);
if (tmp == b)
tmp = 0;
} else {
tmp = (R - tmp);
tmp %= b;
}
}
R = L;
L = tmp;
}
return a * R + L;
}
/***************************************************************************
***************************************************************************/
uint64_t
blackrock2_shuffle(const struct BlackRock *br, uint64_t m)
{
uint64_t c;
c = ENCRYPT(br->rounds, br->a_bits, br->a_mask, br->b_bits, br->b_mask, m, br->seed);
while (c >= br->range)
c = ENCRYPT(br->rounds, br->a_bits, br->a_mask, br->b_bits, br->b_mask, c, br->seed);
return c;
}
/***************************************************************************
***************************************************************************/
uint64_t
blackrock2_unshuffle(const struct BlackRock *br, uint64_t m)
{
uint64_t c;
c = DECRYPT(br->rounds, br->a, br->b, m, br->seed);
while (c >= br->range)
c = DECRYPT(br->rounds, br->a, br->b, c, br->seed);
return c;
}
/***************************************************************************
* This function called only during selftest/regression-test.
***************************************************************************/
static unsigned
verify(struct BlackRock *br, uint64_t max)
{
unsigned char *list;
uint64_t i;
unsigned is_success = 1;
uint64_t range = br->range;
/* Allocate a list of 1-byte counters */
list = CALLOC(1, (size_t)((range<max)?range:max));
/* For all numbers in the range, verify increment the counter for
* the output. */
for (i=0; i<range; i++) {
uint64_t x = blackrock2_shuffle(br, i);
if (x < max)
list[x]++;
}
/* Now check the output to make sure that every counter is set exactly
* to the value of '1'. */
for (i=0; i<max && i<range; i++) {
if (list[i] != 1)
is_success = 0;
}
free(list);
return is_success;
}
/***************************************************************************
* Benchmarks the crypto function.
***************************************************************************/
void
blackrock2_benchmark(unsigned rounds)
{
struct BlackRock br;
uint64_t range = 0x010356789123ULL;
uint64_t i;
uint64_t result = 0;
uint64_t start, stop;
static const uint64_t ITERATIONS = 5000000ULL;
printf("-- blackrock-2 -- \n");
printf("rounds = %u\n", rounds);
blackrock2_init(&br, range, 1, rounds);
/*printf("range = 0x%10" PRIx64 "\n", range);
printf("rangex= 0x%10" PRIx64 "\n", br.a*br.b);
printf(" a = 0x%10" PRIx64 "\n", br.a);
printf(" b = 0x%10" PRIx64 "\n", br.b);*/
/*
* Time the algorithm
*/
start = pixie_nanotime();
for (i=0; i<ITERATIONS; i++) {
result += blackrock2_shuffle(&br, i);
}
stop = pixie_nanotime();
/*
* Print the results
*/
if (result) {
double elapsed = ((double)(stop - start))/(1000000000.0);
double rate = ITERATIONS/elapsed;
rate /= 1000000.0;
printf("iterations/second = %5.3f-million\n", rate);
}
printf("\n");
}
/***************************************************************************
***************************************************************************/
int
blackrock2_selftest(void)
{
uint64_t i;
int is_success = 0;
uint64_t range;
/* @marshray
* Basic test of decryption. I take the index, encrypt it, then decrypt it,
* which means I should get the original index back again. Only, it's not
* working. The decryption fails. The reason it's failing is obvious -- I'm
* just not seeing it though. The error is probably in the 'unfe()'
* function above.
*/
{
struct BlackRock br;
uint64_t result, result2;
blackrock2_init(&br, 1000, 0, 6);
for (i=0; i<10; i++) {
result = blackrock2_shuffle(&br, i);
result2 = blackrock2_unshuffle(&br, result);
if (i != result2)
return 1; /*fail*/
}
}
range = 3015 * 3;
for (i=0; i<5; i++) {
struct BlackRock br;
range += 11 + i;
range *= 1 + i;
blackrock2_init(&br, range, time(0), 6);
is_success = verify(&br, range);
if (!is_success) {
fprintf(stderr, "BLACKROCK: randomization failed\n");
return 1; /*fail*/
}
}
return 0; /*success*/
}

390
src/crypto-lcg.c Normal file
View File

@ -0,0 +1,390 @@
/*
This is a "linear-congruent-generator", a type of random number
generator.
*/
#include "crypto-lcg.h"
#include "crypto-primegen.h" /* DJB's prime factoring code */
#include "util-safefunc.h"
#include "util-malloc.h"
#include <math.h> /* for 'sqrt()', may need -lm for gcc */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/**
* A 64 bit number can't have more than 16 prime factors. The first factors
* are:
* 2*3*5*7*11*13*17*19*23*29*31*37*41*43*47*53 = 0xC443F2F861D29C3A
* 0123456789abcdef
* We zero termiante this list, so we are going to reserve 20 slots.
*/
typedef uint64_t PRIMEFACTORS[20];
/****************************************************************************
* Break down the number into prime factors using DJB's sieve code, which
* is about 5 to 10 times faster than the Sieve of Eratosthenes.
*
* @param number
* The integer that we are factoring. It can be any value up to 64 bits
* in size.
* @param factors
* The list of all the prime factors, zero terminated.
* @param non_factors
* A list of smallest numbers that aren't prime factors. We return
* this because we are going to use prime non-factors for finding
* interesting numbers.
****************************************************************************/
static unsigned
sieve_prime_factors(uint64_t number, PRIMEFACTORS factors,
PRIMEFACTORS non_factors, double *elapsed)
{
primegen pg;
clock_t start;
clock_t stop;
uint64_t prime;
uint64_t max;
unsigned factor_count = 0;
unsigned non_factor_count = 0;
/*
* We only need to sieve up to the square-root of the target number. Only
* one prime factor can be bigger than the square root, so once we find
* all the other primes, the square root is the only one left.
* Note: you have to link to the 'm' math library for some gcc platforms.
*/
max = (uint64_t)sqrt(number + 1.0);
/*
* Init the DJB primegen library.
*/
primegen_init(&pg);
/*
* Enumerate all the primes starting with 2
*/
start = clock();
for (;;) {
/* Sieve the next prime */
prime = primegen_next(&pg);
/* If we've reached the square root, then that's as far as we need
* to go */
if (prime > max)
break;
/* If this prime is not a factor (evenly divisible with no remainder)
* then loop back and get the next prime */
if ((number % prime) != 0) {
if (non_factor_count < 12)
non_factors[non_factor_count++] = prime;
continue;
}
/* Else we've found a prime factor, so add this to the list of primes */
factors[factor_count++] = prime;
/* At the end, we may have one prime factor left that's bigger than the
* sqrt. Therefore, as we go along, divide the original number
* (possibly several times) by the prime factor so that this large
* remaining factor will be the only one left */
while ((number % prime) == 0)
number /= prime;
/* exit early if we've found all prime factors. comment out this
* code if you want to benchmark it */
if (number == 1 && non_factor_count > 10)
break;
}
/*
* See if there is one last prime that's bigger than the square root.
* Note: This is the only number that can be larger than 32-bits in the
* way this code is written.
*/
if (number != 1)
factors[factor_count++] = number;
/*
* Zero terminate the results.
*/
factors[factor_count] = 0;
non_factors[non_factor_count] = 0;
/*
* Since prime factorization takes a long time, especially on slow
* CPUs, we benchmark it to keep track of performance.
*/
stop = clock();
if (elapsed)
*elapsed = ((double)stop - (double)start)/(double)CLOCKS_PER_SEC;
/* should always be at least 1, because if the number itself is prime,
* then that's it's only prime factor */
return factor_count;
}
/****************************************************************************
* Do a pseudo-random 1-to-1 translation of a number within a range to
* another number in that range.
*
* The constants 'a' and 'c' must be chosen to match the LCG algorithm
* to fit 'm' (range).
*
* This the same as the function 'rand()', except all the constants and
* seeds are specified as parameters.
*
* @param index
* The index within the range that we are randomizing.
* @param a
* The 'multiplier' of the LCG algorithm.
* @param c
* The 'increment' of the LCG algorithm.
* @param range
* The 'modulus' of the LCG algorithm.
****************************************************************************/
uint64_t
lcg_rand(uint64_t index, uint64_t a, uint64_t c, uint64_t range)
{
return (index * a + c) % range;
}
/****************************************************************************
* Verify the LCG algorithm. You shouldn't do this for large ranges,
* because we'll run out of memory. Therefore, this algorithm allocates
* a buffer only up to a smaller range. We still have to traverse the
* entire range of numbers, but we only need store values for a smaller
* range. If 10% of the range checks out, then there's a good chance
* it applies to the other 90% as well.
*
* This works by counting the results of rand(), which should be produced
* exactly once.
****************************************************************************/
static unsigned
lcg_verify(uint64_t a, uint64_t c, uint64_t range, uint64_t max)
{
unsigned char *list;
uint64_t i;
unsigned is_success = 1;
/* Allocate a list of 1-byte counters */
list = CALLOC(1, (size_t)((range<max)?range:max));
/* For all numbers in the range, verify increment the counter for the
* the output. */
for (i=0; i<range; i++) {
uint64_t x = lcg_rand(i, a, c, range);
if (x < max)
list[x]++;
}
/* Now check the output to make sure that every counter is set exactly
* to the value of '1'. */
for (i=0; i<max && i<range; i++) {
if (list[i] != 1)
is_success = 0;
}
free(list);
return is_success;
}
/****************************************************************************
* Count the number of digits in a number so that we can pretty-print a
* bunch of numbers in nice columns.
****************************************************************************/
static unsigned
count_digits(uint64_t num)
{
unsigned result = 0;
while (num) {
result++;
num /= 10;
}
return result;
}
/****************************************************************************
* Tell whether the number has any prime factors in common with the list
* of factors. In other words, if it's not coprime with the other number.
* @param c
* The number we want to see has common factors with the other number.
* @param factors
* The factors from the other number
* @return
* !is_coprime(c, factors)
****************************************************************************/
static uint64_t
has_factors_in_common(uint64_t c, PRIMEFACTORS factors)
{
unsigned i;
for (i=0; factors[i]; i++) {
if ((c % factors[i]) == 0)
return factors[i]; /* found a common factor */
}
return 0; /* no factors in common */
}
/****************************************************************************
* Given a range, calculate some possible constants for the LCG algorithm
* for randomizing the order of the array.
* @parm m
* The range for which we'll be finding random numbers. If we are
* looking for random numbers between [0..100), this number will
* be 100.
* @parm a
* The LCG 'a' constant that will be the result of this function.
* @param c
* The LCG 'c' constant that will be the result of this function. This
* should be set to 0 on the input to this function, or a suggested
* value.
****************************************************************************/
void
lcg_calculate_constants(uint64_t m, uint64_t *out_a, uint64_t *inout_c, int is_debug)
{
uint64_t a;
uint64_t c = *inout_c;
double elapsed = 0.0; /* Benchmark of 'sieve' algorithm */
PRIMEFACTORS factors; /* List of prime factors of 'm' */
PRIMEFACTORS non_factors;
unsigned i;
/*
* Find all the prime factors of the number. This step can take several
* seconds for 48 bit numbers, which is why we benchmark how long it
* takes.
*/
sieve_prime_factors(m, factors, non_factors, &elapsed);
/*
* Calculate the 'a-1' constant. It must share all the prime factors
* with the range, and if the range is a multiple of 4, must also
* be a multiple of 4
*/
if (factors[0] == m) {
/* this number has no prime factors, so we can choose anything.
* Therefore, we are going to pick something at random */
unsigned j;
a = 1;
for (j=0; non_factors[j] && j < 5; j++)
a *= non_factors[j];
} else {
//unsigned j;
a = 1;
for (i=0; factors[i]; i++)
a = a * factors[i];
if ((m % 4) == 0)
a *= 2;
/*for (j=0; j<0 && non_factors[j]; j++)
a *= non_factors[j];*/
}
a += 1;
/*
* Calculate the 'c' constant. It must have no prime factors in
* common with the range.
*/
if (c == 0)
c = 2531011 ; /* something random */
while (has_factors_in_common(c, factors))
c++;
if (is_debug) {
/*
* print the results
*/
//printf("sizeof(int) = %" PRIu64 "-bits\n", (uint64_t)(sizeof(size_t)*8));
printf("elapsed = %5.3f-seconds\n", elapsed);
printf("factors = ");
for (i=0; factors[i]; i++)
printf("%" PRIu64 " ", factors[i]);
printf("%s\n", factors[0]?"":"(none)");
printf("m = %-24" PRIu64 " (0x%" PRIx64 ")\n", m, m);
printf("a = %-24" PRIu64 " (0x%" PRIx64 ")\n", a, a);
printf("c = %-24" PRIu64 " (0x%" PRIx64 ")\n", c, c);
printf("c%%m = %-24" PRIu64 " (0x%" PRIx64 ")\n", c%m, c%m);
printf("a%%m = %-24" PRIu64 " (0x%" PRIx64 ")\n", a%m, a%m);
if (m < 1000000000) {
if (lcg_verify(a, c+1, m, 280))
printf("verify = success\n");
else
printf("verify = failure\n");
} else {
printf("verify = too big to check\n");
}
/*
* Print some first numbers. We use these to visually inspect whether
* the results are random or not.
*/
{
unsigned count = 0;
uint64_t x = 0;
unsigned digits = count_digits(m);
for (i=0; i<100 && i < m; i++) {
x = lcg_rand(x, a, c, m);
count += printf("%*" PRIu64 " ", digits, x);
if (count >= 70) {
count = 0;
printf("\n");
}
}
printf("\n");
}
}
*out_a = a;
*inout_c = c;
}
/***************************************************************************
***************************************************************************/
int
lcg_selftest(void)
{
unsigned i;
int is_success = 0;
uint64_t m, a, c;
m = 3015 * 3;
for (i=0; i<5; i++) {
a = 0;
c = 0;
m += 10 + i;
lcg_calculate_constants(m, &a, &c, 0);
is_success = lcg_verify(a, c, m, m);
if (!is_success) {
fprintf(stderr, "LCG: randomization failed\n");
return 1; /*fail*/
}
}
return 0; /*success*/
}

20
src/crypto-lcg.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef RAND_LCG_H
#define RAND_LCG_H
#include <stdint.h>
void
lcg_calculate_constants(uint64_t m, uint64_t *out_a, uint64_t *inout_c, int is_debug);
uint64_t
lcg_rand(uint64_t index, uint64_t a, uint64_t c, uint64_t range);
/**
* Performs a regression test on this module.
* @return
* 0 on success, or a positive integer on failure
*/
int
lcg_selftest(void);
#endif

716
src/crypto-primegen.c Normal file
View File

@ -0,0 +1,716 @@
/*
This is DJB's code for calculating primes, with a few modifications,
such as making it work with Microsoft's compiler on Windows, and
getting rid of warnings.
*/
#include "crypto-primegen.h"
/*
B is 32 times X.
Total memory use for one generator is 2B bytes = 64X bytes.
Covers primes in an interval of length 1920X.
Working set size for one generator is B bits = 4X bytes.
Speedup by a factor of 2 or 3 for L1 cache instead of L2 cache.
Slowdown by a factor of roughly n for primes past (nB)^2.
Possible choices of X:
2002 to fit inside an 8K L1 cache (e.g., Pentium).
4004 to fit inside a 16K L1 cache (e.g., Pentium II).
64064 to fit inside a 256K L2 cache.
There are various word-size limits on X; 1000000 should still be okay.
*/
#define B32 PRIMEGEN_WORDS
#define B (PRIMEGEN_WORDS * 32)
#ifdef _MSC_VER
#pragma warning(disable:4244)
#endif
static const uint32_t two[32] = {
0x00000001 , 0x00000002 , 0x00000004 , 0x00000008
, 0x00000010 , 0x00000020 , 0x00000040 , 0x00000080
, 0x00000100 , 0x00000200 , 0x00000400 , 0x00000800
, 0x00001000 , 0x00002000 , 0x00004000 , 0x00008000
, 0x00010000 , 0x00020000 , 0x00040000 , 0x00080000
, 0x00100000 , 0x00200000 , 0x00400000 , 0x00800000
, 0x01000000 , 0x02000000 , 0x04000000 , 0x08000000
, 0x10000000 , 0x20000000 , 0x40000000 , 0x80000000
} ;
static void clear(register uint32_t (*buf)[B32])
{
register int i;
register int j;
for (j = 0;j < 16;++j) {
for (i = 0;i < B32;++i)
(*buf)[i] = (uint32_t)~0;
++buf;
}
}
static void doit4(register uint32_t *a,register long x,register long y,int64_t start)
{
long i0;
long y0;
register long i;
register uint32_t data;
register uint32_t pos;
register uint32_t bits;
x += x; x += 15;
y += 15;
start += 1000000000;
while (start < 0) { start += x; x += 30; }
start -= 1000000000;
i = start;
while (i < B) { i += x; x += 30; }
for (;;) {
x -= 30;
if (x <= 15) return;
i -= x;
while (i < 0) { i += y; y += 30; }
i0 = i; y0 = y;
while (i < B) {
pos = (uint32_t)i; data = (uint32_t)i;
pos >>= 5; data &= 31;
i += y; y += 30;
bits = a[pos]; data = two[data];
bits ^= data;
a[pos] = bits;
}
i = i0; y = y0;
}
}
static void doit6(register uint32_t *a,register long x,register long y,int64_t start)
{
long i0;
long y0;
register long i;
register uint32_t data;
register uint32_t pos;
register uint32_t bits;
x += 5;
y += 15;
start += 1000000000;
while (start < 0) { start += x; x += 10; }
start -= 1000000000;
i = start;
while (i < B) { i += x; x += 10; }
for (;;) {
x -= 10;
if (x <= 5) return;
i -= x;
while (i < 0) { i += y; y += 30; }
i0 = i; y0 = y;
while (i < B) {
pos = (uint32_t)i; data = (uint32_t)i;
pos >>= 5; data &= 31;
i += y; y += 30;
bits = a[pos]; data = two[data];
bits ^= data;
a[pos] = bits;
}
i = i0; y = y0;
}
}
static void doit12(register uint32_t *a,register long x,register long y,int64_t start)
{
register long i;
register uint32_t data;
register uint32_t bits;
x += 5;
start += 1000000000;
while (start < 0) { start += x; x += 10; }
start -= 1000000000;
i = start;
while (i < 0) { i += x; x += 10; }
y += 15;
x += 10;
for (;;) {
long i0;
long y0;
while (i >= B) {
if (x <= y) return;
i -= y;
y += 30;
}
i0 = i;
y0 = y;
while ((i >= 0) && (y < x)) {
register uint32_t pos;
pos = (uint32_t)i; data = (uint32_t)i;
pos >>= 5; data &= 31;
i -= y;
y += 30;
bits = a[pos]; data = two[data];
bits ^= data;
a[pos] = bits;
}
i = i0;
y = y0;
i += x - 10;
x += 10;
}
}
static const int deltainverse[60] = {
-1,B32 * 0,-1,-1,-1,-1,-1,B32 * 1,-1,-1,-1,B32 * 2,-1,B32 * 3,-1
,-1,-1,B32 * 4,-1,B32 * 5,-1,-1,-1,B32 * 6,-1,-1,-1,-1,-1,B32 * 7
,-1,B32 * 8,-1,-1,-1,-1,-1,B32 * 9,-1,-1,-1,B32 * 10,-1,B32 * 11,-1
,-1,-1,B32 * 12,-1,B32 * 13,-1,-1,-1,B32 * 14,-1,-1,-1,-1,-1,B32 * 15
} ;
static void squarefree1big(uint32_t (*buf)[B32],uint64_t base,uint32_t q,uint64_t qq)
{
uint64_t i;
uint32_t pos;
int n;
uint64_t bound = base + 60 * B;
while (qq < bound) {
if (bound < 2000000000)
i = qq - (((uint32_t) base) % ((uint32_t) qq));
else
i = qq - (base % qq);
if (!(i & 1)) i += qq;
if (i < B * 60) {
pos = (uint32_t)i;
n = deltainverse[pos % 60];
if (n >= 0) {
pos /= 60;
(*buf)[n + (pos >> 5)] |= two[pos & 31];
}
}
qq += q; q += 1800;
}
}
static void squarefree1(register uint32_t (*buf)[B32],uint64_t L,uint32_t q)
{
uint32_t qq;
register uint32_t qqhigh;
uint32_t i;
register uint32_t ilow;
register uint32_t ihigh;
register int n;
uint64_t base;
base = 60 * L;
qq = q * q;
q = 60 * q + 900;
while (qq < B * 60) {
if (base < 2000000000)
i = qq - (((uint32_t) base) % qq);
else
i = qq - (base % qq);
if (!(i & 1)) i += qq;
if (i < B * 60) {
qqhigh = qq / 60;
ilow = i % 60; ihigh = i / 60;
qqhigh += qqhigh;
while (ihigh < B) {
n = deltainverse[ilow];
if (n >= 0)
(*buf)[n + (ihigh >> 5)] |= two[ihigh & 31];
ilow += 2; ihigh += qqhigh;
if (ilow >= 60) { ilow -= 60; ihigh += 1; }
}
}
qq += q; q += 1800;
}
squarefree1big(buf,base,q,qq);
}
static void squarefree49big(uint32_t (*buf)[B32],uint64_t base,uint32_t q,uint64_t qq)
{
uint64_t i;
uint32_t pos;
int n;
uint64_t bound = base + 60 * B;
while (qq < bound) {
if (bound < 2000000000)
i = qq - (((uint32_t) base) % ((uint32_t) qq));
else
i = qq - (base % qq);
if (!(i & 1)) i += qq;
if (i < B * 60) {
pos = (uint32_t)i;
n = deltainverse[pos % 60];
if (n >= 0) {
pos /= 60;
(*buf)[n + (pos >> 5)] |= two[pos & 31];
}
}
qq += q; q += 1800;
}
}
static void squarefree49(register uint32_t (*buf)[B32],uint64_t L,uint32_t q)
{
uint32_t qq;
register uint32_t qqhigh;
uint32_t i;
register uint32_t ilow;
register uint32_t ihigh;
register int n;
uint64_t base = 60 * L;
qq = q * q;
q = 60 * q + 900;
while (qq < B * 60) {
if (base < 2000000000)
i = qq - (((uint32_t) base) % qq);
else
i = qq - (base % qq);
if (!(i & 1)) i += qq;
if (i < B * 60) {
qqhigh = qq / 60;
ilow = i % 60; ihigh = i / 60;
qqhigh += qqhigh;
qqhigh += 1;
while (ihigh < B) {
n = deltainverse[ilow];
if (n >= 0)
(*buf)[n + (ihigh >> 5)] |= two[ihigh & 31];
ilow += 38; ihigh += qqhigh;
if (ilow >= 60) { ilow -= 60; ihigh += 1; }
}
}
qq += q; q += 1800;
}
squarefree49big(buf,base,q,qq);
}
/* squares of primes >= 7, < 240 */
uint32_t qqtab[49] = {
49,121,169,289,361,529,841,961,1369,1681
,1849,2209,2809,3481,3721,4489,5041,5329,6241,6889
,7921,9409,10201,10609,11449,11881,12769,16129,17161,18769
,19321,22201,22801,24649,26569,27889,29929,32041,32761,36481
,37249,38809,39601,44521,49729,51529,52441,54289,57121
} ;
/* (qq * 11 + 1) / 60 or (qq * 59 + 1) / 60 */
uint32_t qq60tab[49] = {
9,119,31,53,355,97,827,945,251,1653
,339,405,515,3423,3659,823,4957,977,6137
,1263,7789,1725,10031,1945,2099,11683,2341,2957
,16875,3441,18999,21831,22421,4519,4871,5113,5487
,31507,32215,35873,6829,7115,38941,43779,9117,9447,51567,9953,56169
} ;
static void squarefreetiny(register uint32_t *a,uint32_t *Lmodqq,int d)
{
int j;
for (j = 0;j < 49;++j) {
register uint32_t k;
register uint32_t qq;
qq = qqtab[j];
k = qq - 1 - ((Lmodqq[j] + qq60tab[j] * d - 1) % qq);
while (k < B) {
register uint32_t pos;
register uint32_t data;
register uint32_t bits;
pos = k;
data = k;
pos >>= 5;
data &= 31;
k += qq;
bits = a[pos];
data = two[data];
bits |= data;
a[pos] = bits;
}
}
}
typedef struct { char index; char f; char g; char k; } todo;
static const todo for4[] = {
{0,2,15,4} , {0,3,5,1} , {0,3,25,11} , {0,5,9,3}
, {0,5,21,9} , {0,7,15,7} , {0,8,15,8} , {0,10,9,8}
, {0,10,21,14} , {0,12,5,10} , {0,12,25,20} , {0,13,15,15}
, {0,15,1,15} , {0,15,11,17} , {0,15,19,21} , {0,15,29,29}
, {3,1,3,0} , {3,1,27,12} , {3,4,3,1} , {3,4,27,13}
, {3,6,7,3} , {3,6,13,5} , {3,6,17,7} , {3,6,23,11}
, {3,9,7,6} , {3,9,13,8} , {3,9,17,10} , {3,9,23,14}
, {3,11,3,8} , {3,11,27,20} , {3,14,3,13} , {3,14,27,25}
, {4,2,1,0} , {4,2,11,2} , {4,2,19,6} , {4,2,29,14}
, {4,7,1,3} , {4,7,11,5} , {4,7,19,9} , {4,7,29,17}
, {4,8,1,4} , {4,8,11,6} , {4,8,19,10} , {4,8,29,18}
, {4,13,1,11} , {4,13,11,13} , {4,13,19,17} , {4,13,29,25}
, {7,1,5,0} , {7,1,25,10} , {7,4,5,1} , {7,4,25,11}
, {7,5,7,2} , {7,5,13,4} , {7,5,17,6} , {7,5,23,10}
, {7,10,7,7} , {7,10,13,9} , {7,10,17,11} , {7,10,23,15}
, {7,11,5,8} , {7,11,25,18} , {7,14,5,13} , {7,14,25,23}
, {9,2,9,1} , {9,2,21,7} , {9,3,1,0} , {9,3,11,2}
, {9,3,19,6} , {9,3,29,14} , {9,7,9,4} , {9,7,21,10}
, {9,8,9,5} , {9,8,21,11} , {9,12,1,9} , {9,12,11,11}
, {9,12,19,15} , {9,12,29,23} , {9,13,9,12} , {9,13,21,18}
, {10,2,5,0} , {10,2,25,10} , {10,5,1,1} , {10,5,11,3}
, {10,5,19,7} , {10,5,29,15} , {10,7,5,3} , {10,7,25,13}
, {10,8,5,4} , {10,8,25,14} , {10,10,1,6} , {10,10,11,8}
, {10,10,19,12} , {10,10,29,20} , {10,13,5,11} , {10,13,25,21}
, {13,1,15,3} , {13,4,15,4} , {13,5,3,1} , {13,5,27,13}
, {13,6,5,2} , {13,6,25,12} , {13,9,5,5} , {13,9,25,15}
, {13,10,3,6} , {13,10,27,18} , {13,11,15,11} , {13,14,15,16}
, {13,15,7,15} , {13,15,13,17} , {13,15,17,19} , {13,15,23,23}
, {14,1,7,0} , {14,1,13,2} , {14,1,17,4} , {14,1,23,8}
, {14,4,7,1} , {14,4,13,3} , {14,4,17,5} , {14,4,23,9}
, {14,11,7,8} , {14,11,13,10} , {14,11,17,12} , {14,11,23,16}
, {14,14,7,13} , {14,14,13,15} , {14,14,17,17} , {14,14,23,21}
} ;
static const todo for6[] = {
{1,1,2,0} , {1,1,8,1} , {1,1,22,8} , {1,1,28,13}
, {1,3,10,2} , {1,3,20,7} , {1,7,10,4} , {1,7,20,9}
, {1,9,2,4} , {1,9,8,5} , {1,9,22,12} , {1,9,28,17}
, {5,1,4,0} , {5,1,14,3} , {5,1,16,4} , {5,1,26,11}
, {5,5,2,1} , {5,5,8,2} , {5,5,22,9} , {5,5,28,14}
, {5,9,4,4} , {5,9,14,7} , {5,9,16,8} , {5,9,26,15}
, {8,3,2,0} , {8,3,8,1} , {8,3,22,8} , {8,3,28,13}
, {8,5,4,1} , {8,5,14,4} , {8,5,16,5} , {8,5,26,12}
, {8,7,2,2} , {8,7,8,3} , {8,7,22,10} , {8,7,28,15}
, {11,1,10,1} , {11,1,20,6} , {11,3,4,0} , {11,3,14,3}
, {11,3,16,4} , {11,3,26,11} , {11,7,4,2} , {11,7,14,5}
, {11,7,16,6} , {11,7,26,13} , {11,9,10,5} , {11,9,20,10}
} ;
static const todo for12[] = {
{2,2,1,0} , {2,2,11,-2} , {2,2,19,-6} , {2,2,29,-14}
, {2,3,4,0} , {2,3,14,-3} , {2,3,16,-4} , {2,3,26,-11}
, {2,5,2,1} , {2,5,8,0} , {2,5,22,-7} , {2,5,28,-12}
, {2,7,4,2} , {2,7,14,-1} , {2,7,16,-2} , {2,7,26,-9}
, {2,8,1,3} , {2,8,11,1} , {2,8,19,-3} , {2,8,29,-11}
, {2,10,7,4} , {2,10,13,2} , {2,10,17,0} , {2,10,23,-4}
, {6,1,10,-2} , {6,1,20,-7} , {6,2,7,-1} , {6,2,13,-3}
, {6,2,17,-5} , {6,2,23,-9} , {6,3,2,0} , {6,3,8,-1}
, {6,3,22,-8} , {6,3,28,-13} , {6,4,5,0} , {6,4,25,-10}
, {6,6,5,1} , {6,6,25,-9} , {6,7,2,2} , {6,7,8,1}
, {6,7,22,-6} , {6,7,28,-11} , {6,8,7,2} , {6,8,13,0}
, {6,8,17,-2} , {6,8,23,-6} , {6,9,10,2} , {6,9,20,-3}
, {12,1,4,-1} , {12,1,14,-4} , {12,1,16,-5} , {12,1,26,-12}
, {12,2,5,-1} , {12,2,25,-11} , {12,3,10,-2} , {12,3,20,-7}
, {12,4,1,0} , {12,4,11,-2} , {12,4,19,-6} , {12,4,29,-14}
, {12,6,1,1} , {12,6,11,-1} , {12,6,19,-5} , {12,6,29,-13}
, {12,7,10,0} , {12,7,20,-5} , {12,8,5,2} , {12,8,25,-8}
, {12,9,4,3} , {12,9,14,0} , {12,9,16,-1} , {12,9,26,-8}
, {15,1,2,-1} , {15,1,8,-2} , {15,1,22,-9} , {15,1,28,-14}
, {15,4,7,-1} , {15,4,13,-3} , {15,4,17,-5} , {15,4,23,-9}
, {15,5,4,0} , {15,5,14,-3} , {15,5,16,-4} , {15,5,26,-11}
, {15,6,7,0} , {15,6,13,-2} , {15,6,17,-4} , {15,6,23,-8}
, {15,9,2,3} , {15,9,8,2} , {15,9,22,-5} , {15,9,28,-10}
, {15,10,1,4} , {15,10,11,2} , {15,10,19,-2} , {15,10,29,-10}
} ;
void primegen_sieve(primegen *pg)
{
uint32_t (*buf)[B32];
uint64_t L;
int i;
uint32_t Lmodqq[49];
buf = pg->buf;
L = pg->L;
if (L > 2000000000)
for (i = 0;i < 49;++i)
Lmodqq[i] = L % qqtab[i];
else
for (i = 0;i < 49;++i)
Lmodqq[i] = ((uint32_t) L) % qqtab[i];
clear(buf);
for (i = 0;i < 16;++i)
doit4(buf[0],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[0],Lmodqq,1);
for (;i < 32;++i)
doit4(buf[3],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[3],Lmodqq,13);
for (;i < 48;++i)
doit4(buf[4],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[4],Lmodqq,17);
for (;i < 64;++i)
doit4(buf[7],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[7],Lmodqq,29);
for (;i < 80;++i)
doit4(buf[9],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[9],Lmodqq,37);
for (;i < 96;++i)
doit4(buf[10],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[10],Lmodqq,41);
for (;i < 112;++i)
doit4(buf[13],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[13],Lmodqq,49);
for (;i < 128;++i)
doit4(buf[14],for4[i].f,for4[i].g,(int64_t) for4[i].k - L);
squarefreetiny(buf[14],Lmodqq,53);
for (i = 0;i < 12;++i)
doit6(buf[1],for6[i].f,for6[i].g,(int64_t) for6[i].k - L);
squarefreetiny(buf[1],Lmodqq,7);
for (;i < 24;++i)
doit6(buf[5],for6[i].f,for6[i].g,(int64_t) for6[i].k - L);
squarefreetiny(buf[5],Lmodqq,19);
for (;i < 36;++i)
doit6(buf[8],for6[i].f,for6[i].g,(int64_t) for6[i].k - L);
squarefreetiny(buf[8],Lmodqq,31);
for (;i < 48;++i)
doit6(buf[11],for6[i].f,for6[i].g,(int64_t) for6[i].k - L);
squarefreetiny(buf[11],Lmodqq,43);
for (i = 0;i < 24;++i)
doit12(buf[2],for12[i].f,for12[i].g,(int64_t) for12[i].k - L);
squarefreetiny(buf[2],Lmodqq,11);
for (;i < 48;++i)
doit12(buf[6],for12[i].f,for12[i].g,(int64_t) for12[i].k - L);
squarefreetiny(buf[6],Lmodqq,23);
for (;i < 72;++i)
doit12(buf[12],for12[i].f,for12[i].g,(int64_t) for12[i].k - L);
squarefreetiny(buf[12],Lmodqq,47);
for (;i < 96;++i)
doit12(buf[15],for12[i].f,for12[i].g,(int64_t) for12[i].k - L);
squarefreetiny(buf[15],Lmodqq,59);
squarefree49(buf,L,247);
squarefree49(buf,L,253);
squarefree49(buf,L,257);
squarefree49(buf,L,263);
squarefree1(buf,L,241);
squarefree1(buf,L,251);
squarefree1(buf,L,259);
squarefree1(buf,L,269);
}
void primegen_fill(primegen *pg)
{
int i;
uint32_t mask;
uint32_t bits0, bits1, bits2, bits3, bits4, bits5, bits6, bits7;
uint32_t bits8, bits9, bits10, bits11, bits12, bits13, bits14, bits15;
uint64_t base;
i = pg->pos;
if (i == B32) {
primegen_sieve(pg);
pg->L += B;
i = 0;
}
pg->pos = i + 1;
bits0 = ~pg->buf[0][i];
bits1 = ~pg->buf[1][i];
bits2 = ~pg->buf[2][i];
bits3 = ~pg->buf[3][i];
bits4 = ~pg->buf[4][i];
bits5 = ~pg->buf[5][i];
bits6 = ~pg->buf[6][i];
bits7 = ~pg->buf[7][i];
bits8 = ~pg->buf[8][i];
bits9 = ~pg->buf[9][i];
bits10 = ~pg->buf[10][i];
bits11 = ~pg->buf[11][i];
bits12 = ~pg->buf[12][i];
bits13 = ~pg->buf[13][i];
bits14 = ~pg->buf[14][i];
bits15 = ~pg->buf[15][i];
base = pg->base + 1920;
pg->base = base;
pg->num = 0;
for (mask = 0x80000000;mask;mask >>= 1) {
base -= 60;
if (bits15 & mask) pg->p[pg->num++] = base + 59;
if (bits14 & mask) pg->p[pg->num++] = base + 53;
if (bits13 & mask) pg->p[pg->num++] = base + 49;
if (bits12 & mask) pg->p[pg->num++] = base + 47;
if (bits11 & mask) pg->p[pg->num++] = base + 43;
if (bits10 & mask) pg->p[pg->num++] = base + 41;
if (bits9 & mask) pg->p[pg->num++] = base + 37;
if (bits8 & mask) pg->p[pg->num++] = base + 31;
if (bits7 & mask) pg->p[pg->num++] = base + 29;
if (bits6 & mask) pg->p[pg->num++] = base + 23;
if (bits5 & mask) pg->p[pg->num++] = base + 19;
if (bits4 & mask) pg->p[pg->num++] = base + 17;
if (bits3 & mask) pg->p[pg->num++] = base + 13;
if (bits2 & mask) pg->p[pg->num++] = base + 11;
if (bits1 & mask) pg->p[pg->num++] = base + 7;
if (bits0 & mask) pg->p[pg->num++] = base + 1;
}
}
uint64_t primegen_next(primegen *pg)
{
while (!pg->num)
primegen_fill(pg);
return pg->p[--pg->num];
}
uint64_t primegen_peek(primegen *pg)
{
while (!pg->num)
primegen_fill(pg);
return pg->p[pg->num - 1];
}
void primegen_init(primegen *pg)
{
pg->L = 1;
pg->base = 60;
pg->pos = PRIMEGEN_WORDS;
pg->p[0] = 59;
pg->p[1] = 53;
pg->p[2] = 47;
pg->p[3] = 43;
pg->p[4] = 41;
pg->p[5] = 37;
pg->p[6] = 31;
pg->p[7] = 29;
pg->p[8] = 23;
pg->p[9] = 19;
pg->p[10] = 17;
pg->p[11] = 13;
pg->p[12] = 11;
pg->p[13] = 7;
pg->p[14] = 5;
pg->p[15] = 3;
pg->p[16] = 2;
pg->num = 17;
}
static const unsigned long pop[256] = {
0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5
,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6
,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6
,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7
,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6
,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7
,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7
,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
};
uint64_t primegen_count(primegen *pg,uint64_t to)
{
uint64_t count = 0;
for (;;) {
register int pos;
register int j;
register uint32_t bits;
register uint32_t smallcount;
while (pg->num) {
if (pg->p[pg->num - 1] >= to) return count;
++count;
--pg->num;
}
smallcount = 0;
pos = pg->pos;
while ((pos < B32) && (pg->base + 1920 < to)) {
for (j = 0;j < 16;++j) {
bits = ~pg->buf[j][pos];
smallcount += pop[bits & 255]; bits >>= 8;
smallcount += pop[bits & 255]; bits >>= 8;
smallcount += pop[bits & 255]; bits >>= 8;
smallcount += pop[bits & 255];
}
pg->base += 1920;
++pos;
}
pg->pos = pos;
count += smallcount;
if (pos == B32)
while (pg->base + B * 60 < to) {
primegen_sieve(pg);
pg->L += B;
smallcount = 0;
for (j = 0;j < 16;++j)
for (pos = 0;pos < B32;++pos) {
bits = ~pg->buf[j][pos];
smallcount += pop[bits & 255]; bits >>= 8;
smallcount += pop[bits & 255]; bits >>= 8;
smallcount += pop[bits & 255]; bits >>= 8;
smallcount += pop[bits & 255];
}
count += smallcount;
pg->base += B * 60;
}
primegen_fill(pg);
}
}
void primegen_skipto(primegen *pg,uint64_t to)
{
for (;;) {
int pos;
while (pg->num) {
if (pg->p[pg->num - 1] >= to) return;
--pg->num;
}
pos = pg->pos;
while ((pos < B32) && (pg->base + 1920 < to)) {
pg->base += 1920;
++pos;
}
pg->pos = pos;
if (pos == B32)
while (pg->base + B * 60 < to) {
pg->L += B;
pg->base += B * 60;
}
primegen_fill(pg);
}
}

44
src/crypto-primegen.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef PRIMEGEN_H
#define PRIMEGEN_H
#include <stdint.h>
/**
* This is B/32: the number of 32-bit words of space used in the primegen
* inner loop. This should fit into the CPU's level-1 cache.
*
* 2048 works well on a Pentium-100.
* 3600 works well on a Pentium II-350
* 4004 works well on an UltraSPARC-I/167
*
* 2012-nov (Rob): This code was written 15 years ago. Processor caches
* haven't really gotten any larger. A number like 8008 works slightly
* better on an Ivy Bridge CPU, but works noticeably worse on an Atom
* or ARM processor. The value 4004 seems to be a good compromise for
* all these processors. In any case, modern CPUs will automatically
* prefetch the buffers anyway, significantly lessoning the impact of
* having a poor number defined here. I tried 16016, but it crashed, and
* I don't know why, but I don't care because I'm not going to use such a
* large size.
*/
#define PRIMEGEN_WORDS 4004
typedef struct {
uint32_t buf[16][PRIMEGEN_WORDS];
uint64_t p[512]; /* p[num-1] ... p[0], in that order */
int num;
int pos; /* next entry to use in buf; WORDS to restart */
uint64_t base;
uint64_t L;
} primegen;
extern void primegen_sieve(primegen *);
extern void primegen_fill(primegen *);
extern void primegen_init(primegen *);
extern uint64_t primegen_next(primegen *);
extern uint64_t primegen_peek(primegen *);
extern uint64_t primegen_count(primegen *,uint64_t to);
extern void primegen_skipto(primegen *,uint64_t to);
#endif

252
src/crypto-siphash24.c Normal file
View File

@ -0,0 +1,252 @@
/*
SipHash reference C implementation
Written in 2012 by
Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com>
Daniel J. Bernstein <djb@cr.yp.to>
To the extent possible under law, the author(s) have dedicated all copyright
and related and neighboring rights to this software to the public domain
worldwide. This software is distributed without any warranty.
You should have received a copy of the CC0 Public Domain Dedication along with
this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "crypto-siphash24.h"
typedef uint64_t u64;
typedef uint32_t u32;
typedef uint8_t u8;
#define ROTL(x,b) (u64)( ((x) << (b)) | ( (x) >> (64 - (b))) )
#define U32TO8_LE(p, v) \
(p)[0] = (u8)((v) ); (p)[1] = (u8)((v) >> 8); \
(p)[2] = (u8)((v) >> 16); (p)[3] = (u8)((v) >> 24);
#define U64TO8_LE(p, v) \
U32TO8_LE((p), (u32)((v) )); \
U32TO8_LE((p) + 4, (u32)((v) >> 32));
#define U8TO64_LE(p) \
(((u64)((p)[0]) ) | \
((u64)((p)[1]) << 8) | \
((u64)((p)[2]) << 16) | \
((u64)((p)[3]) << 24) | \
((u64)((p)[4]) << 32) | \
((u64)((p)[5]) << 40) | \
((u64)((p)[6]) << 48) | \
((u64)((p)[7]) << 56))
#define SIPROUND \
{ \
v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \
v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \
v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
}
/* SipHash-2-4 */
static int crypto_auth( unsigned char *out, const unsigned char *in, unsigned long long inlen, const unsigned char *k )
{
/* "somepseudorandomlygeneratedbytes" */
u64 v0 = 0x736f6d6570736575ULL;
u64 v1 = 0x646f72616e646f6dULL;
u64 v2 = 0x6c7967656e657261ULL;
u64 v3 = 0x7465646279746573ULL;
u64 b;
u64 k0 = U8TO64_LE( k );
u64 k1 = U8TO64_LE( k + 8 );
const u8 *end = in + inlen - ( inlen % sizeof( u64 ) );
const int left = inlen & 7;
b = ( ( u64 )inlen ) << 56;
v3 ^= k1;
v2 ^= k0;
v1 ^= k1;
v0 ^= k0;
for ( ; in != end; in += 8 )
{
u64 m;
m = U8TO64_LE( in );
#ifdef xxxDEBUG
printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
printf( "(%3d) compress %08x %08x\n", ( int )inlen, ( u32 )( m >> 32 ), ( u32 )m );
#endif
v3 ^= m;
SIPROUND;
SIPROUND;
v0 ^= m;
}
switch( left )
{
case 7: b |= ( ( u64 )in[ 6] ) << 48;
case 6: b |= ( ( u64 )in[ 5] ) << 40;
case 5: b |= ( ( u64 )in[ 4] ) << 32;
case 4: b |= ( ( u64 )in[ 3] ) << 24;
case 3: b |= ( ( u64 )in[ 2] ) << 16;
case 2: b |= ( ( u64 )in[ 1] ) << 8;
case 1: b |= ( ( u64 )in[ 0] ); break;
case 0: break;
}
#ifdef xxxDEBUG
printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
printf( "(%3d) padding %08x %08x\n", ( int )inlen, ( u32 )( b >> 32 ), ( u32 )b );
#endif
v3 ^= b;
SIPROUND;
SIPROUND;
v0 ^= b;
#ifdef xxxDEBUG
printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
#endif
v2 ^= 0xff;
SIPROUND;
SIPROUND;
SIPROUND;
SIPROUND;
b = v0 ^ v1 ^ v2 ^ v3;
U64TO8_LE( out, b );
return 0;
}
uint64_t
siphash24(const void *in, size_t inlen, const uint64_t key[2])
{
uint64_t result;
crypto_auth((unsigned char*)&result,
(const unsigned char *)in,
inlen,
(const unsigned char *)&key[0]);
return result;
}
/*
SipHash-2-4 output with
k = 00 01 02 ...
and
in = (empty string)
in = 00 (1 byte)
in = 00 01 (2 bytes)
in = 00 01 02 (3 bytes)
...
in = 00 01 02 ... 3e (63 bytes)
*/
static u8 vectors[64][8] =
{
{ 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, },
{ 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, },
{ 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, },
{ 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, },
{ 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, },
{ 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, },
{ 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, },
{ 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, },
{ 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, },
{ 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, },
{ 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, },
{ 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, },
{ 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, },
{ 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, },
{ 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, },
{ 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, },
{ 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, },
{ 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, },
{ 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, },
{ 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, },
{ 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, },
{ 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, },
{ 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, },
{ 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, },
{ 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, },
{ 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, },
{ 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, },
{ 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, },
{ 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, },
{ 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, },
{ 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, },
{ 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, },
{ 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, },
{ 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, },
{ 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, },
{ 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, },
{ 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, },
{ 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, },
{ 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, },
{ 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, },
{ 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, },
{ 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, },
{ 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, },
{ 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, },
{ 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, },
{ 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, },
{ 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, },
{ 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, },
{ 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, },
{ 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, },
{ 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, },
{ 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, },
{ 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, },
{ 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, },
{ 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, },
{ 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, },
{ 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, },
{ 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, },
{ 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, },
{ 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, },
{ 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, },
{ 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, },
{ 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, },
{ 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, }
};
static int
test_vectors()
{
#define MAXLEN 64
u8 in[MAXLEN], out[8], k[16];
int i;
int ok = 1;
for( i = 0; i < 16; ++i ) k[i] = (u8)i;
for( i = 0; i < MAXLEN; ++i )
{
in[i] = (u8)i;
crypto_auth( out, in, i, k );
if ( memcmp( out, vectors[i], 8 ) )
{
printf( "test vector failed for %d bytes\n", i );
ok = 0;
}
}
return ok;
}
int
siphash24_selftest(void)
{
if (test_vectors())
return 0;
else
return 1;
}

17
src/crypto-siphash24.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef CRYPTO_SIPHASH24_H
#define CRYPTO_SIPHASH24_H
#include <stdint.h>
uint64_t
siphash24(const void *in, size_t inlen, const uint64_t key[2]);
/**
* Regression-test this module.
* @return
* 0 on success, a positive integer otherwise.
*/
int
siphash24_selftest(void);
#endif

176
src/event-timeout.c Normal file
View File

@ -0,0 +1,176 @@
/*
Event timeout
This is for the user-mode TCP stack. We need to mark timeouts in the
future when we'll re-visit a connection/tcb. For example, when we
send a packet, we need to resend it in the future in case we don't
get a response.
This design creates a large "ring" of timeouts, and then cycles
again and again through the ring. This is a fairly high granularity,
just has hundreds, thousands, or 10 thousand entries per second.
(I keep adjusting the granularity up and down). Not that at any
slot in the ring, there may be entries from the far future.
NOTE: a big feature of this system is that the structure that tracks
the timeout is actually held within the TCB structure. In other
words, each TCB can have one-and-only-one timeout.
NOTE: a recurring bug is that the TCP code removes a TCB from the
timeout ring and forgets to put it back somewhere else. Since the
TCB is cleaned up on a timeout, such TCBs never get cleaned up,
leading to a memory leak. I keep fixing this bug, then changing the
code and causing the bug to come back again.
*/
#include "event-timeout.h"
#include "util-logger.h"
#include "util-malloc.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/***************************************************************************
* The timeout system is a circular ring. We move an index around the
* ring. At each slot in the ring is a linked-list of all entries at
* that time index. Because the ring can wrap, not everything at a given
* entry will be the same timestamp. Therefore, when doing the timeout
* logic at a slot, we have to doublecheck the actual timestamp, and skip
* those things that are further in the future.
***************************************************************************/
struct Timeouts {
/**
* This index is a monotonically increasing number, modulus the mask.
* Every time we check timeouts, we simply move it forward in time.
*/
uint64_t current_index;
/**
* Counts the number of outstanding timeouts. Adding a timeout increments
* this number, and removing a timeout decrements this number. The
* program shouldn't exit until this number is zero.
*/
uint64_t outstanding_count;
/**
* The number of slots is a power-of-2, so the mask is just this
* number minus 1
*/
unsigned mask;
/**
* The ring of entries.
*/
struct TimeoutEntry *slots[1024*1024];
};
/***************************************************************************
***************************************************************************/
struct Timeouts *
timeouts_create(uint64_t timestamp)
{
struct Timeouts *timeouts;
/*
* Allocate memory and initialize it to zero
*/
timeouts = CALLOC(1, sizeof(*timeouts));
/*
* We just mask off the low order bits to determine wrap. I'm using
* a variable here because one of these days I'm going to make
* the size of the ring dynamically adjustable depending upon
* the speed of the scan.
*/
timeouts->mask = sizeof(timeouts->slots)/sizeof(timeouts->slots[0]) - 1;
/*
* Set the index to the current time. Note that this timestamp is
* the 'time_t' value multiplied by the number of ticks-per-second,
* where 'ticks' is something I've defined for scanning. Right now
* I hard-code in the size of the ticks, but eventually they'll be
* dynamically resized depending upon the speed of the scan.
*/
timeouts->current_index = timestamp;
return timeouts;
}
/***************************************************************************
* This inserts the timeout entry into the appropriate place in the
* timeout ring.
***************************************************************************/
void
timeouts_add(struct Timeouts *timeouts, struct TimeoutEntry *entry,
size_t offset, uint64_t timestamp)
{
unsigned index;
/* Unlink from wherever the entry came from */
if (entry->timestamp)
timeouts->outstanding_count--;
timeout_unlink(entry);
if (entry->prev) {
LOG(1, "***CHANGE %d-seconds\n",
(int)((timestamp-entry->timestamp)/TICKS_PER_SECOND));
}
/* Initialize the new entry */
entry->timestamp = timestamp;
entry->offset = (unsigned)offset;
/* Link it into it's new location */
index = timestamp & timeouts->mask;
entry->next = timeouts->slots[index];
timeouts->slots[index] = entry;
entry->prev = &timeouts->slots[index];
if (entry->next)
entry->next->prev = &entry->next;
timeouts->outstanding_count++;
}
/***************************************************************************
* Remove the next event that it older than the specified timestamp
***************************************************************************/
void *
timeouts_remove(struct Timeouts *timeouts, uint64_t timestamp)
{
struct TimeoutEntry *entry = NULL;
/* Search until we find one */
while (timeouts->current_index <= timestamp) {
/* Start at the current slot */
entry = timeouts->slots[timeouts->current_index & timeouts->mask];
/* enumerate through the linked list until we find a used slot */
while (entry && entry->timestamp > timestamp)
entry = entry->next;
if (entry)
break;
/* found nothing at this slot, so move to next slot */
timeouts->current_index++;
}
if (entry == NULL) {
/* we've caught up to the current time, and there's nothing
* left to timeout, so return NULL */
return NULL;
}
/* unlink this entry from the timeout system */
timeouts--;
timeout_unlink(entry);
/* return a pointer to the structure holding this entry */
return ((char*)entry) - entry->offset;
}

132
src/event-timeout.h Normal file
View File

@ -0,0 +1,132 @@
#ifndef EVENT_TIMEOUT_H
#define EVENT_TIMEOUT_H
#include <stdint.h>
#include <stdio.h>
#include <stddef.h> /* offsetof*/
#include "util-bool.h" /* <stdbool.h> */
#if defined(_MSC_VER)
#undef inline
#define inline _inline
#endif
struct Timeouts;
/***************************************************************************
***************************************************************************/
struct TimeoutEntry {
/**
* In units of 1/16384 of a second. We use power-of-two units here
* to make the "modulus" operation a simple binary "and".
* See the TICKS_FROM_TV() macro for getting the timestamp from
* the current time.
*/
uint64_t timestamp;
/** we build a doubly-linked list */
struct TimeoutEntry *next;
struct TimeoutEntry **prev;
/** The timeout entry is never allocated by itself, but instead
* lives inside another data structure. This stores the value of
* 'offsetof()', so given a pointer to this structure, we can find
* the original structure that contains it */
unsigned offset;
};
/***************************************************************************
***************************************************************************/
static inline bool
timeout_is_unlinked(const struct TimeoutEntry *entry) {
if (entry->prev == 0 || entry->next == 0)
return true;
else
return false;
}
/***************************************************************************
***************************************************************************/
static inline void
timeout_unlink(struct TimeoutEntry *entry)
{
if (entry->prev == 0 && entry->next == 0)
return;
*(entry->prev) = entry->next;
if (entry->next)
entry->next->prev = entry->prev;
entry->next = 0;
entry->prev = 0;
entry->timestamp = 0;
}
/***************************************************************************
***************************************************************************/
static inline void
timeout_init(struct TimeoutEntry *entry)
{
entry->next = 0;
entry->prev = 0;
}
/**
* Create a timeout subsystem.
* @param timestamp_now
* The current timestamp indicating "now" when the thing starts.
* This should be 'time(0) * TICKS_PER_SECOND'.
*/
struct Timeouts *
timeouts_create(uint64_t timestamp_now);
/**
* Insert the timeout 'entry' into the future location in the timeout
* ring, as determined by the timestamp.
* @param timeouts
* A ring of timeouts, with each slot corresponding to a specific
* time in the future.
* @param entry
* The entry that we are going to insert into the ring. If it's
* already in the ring, it'll be removed from the old location
* first before inserting into the new location.
* @param offset
* The 'entry' field above is part of an existing structure. This
* tells the offset_of() from the beginning of that structure.
* In other words, this tells us the pointer to the object that
* that is the subject of the timeout.
* @param timestamp_expires
* When this timeout will expire. This is in terms of internal
* ticks, which in units of TICKS_PER_SECOND.
*/
void
timeouts_add(struct Timeouts *timeouts, struct TimeoutEntry *entry,
size_t offset, uint64_t timestamp_expires);
/**
* Remove an object from the timestamp system that is older than than
* the specified timestamp. This function must be called repeatedly
* until it returns NULL to remove all the objects that are older
* than the given timestamp.
* @param timeouts
* A ring of timeouts. We'll walk the ring until we've caught
* up with the current time.
* @param timestamp_now
* Usually, this timestmap will be "now", the current time,
* and anything older than this will be aged out.
* @return
* an object older than the specified timestamp, or NULL
* if there are no more objects to be found
*/
void *
timeouts_remove(struct Timeouts *timeouts, uint64_t timestamp_now);
/*
* This macros convert a normal "timeval" structure into the timestamp
* that we use for timeouts. The timeval structure probably will come
* from the packets that we are capturing.
*/
#define TICKS_PER_SECOND (16384ULL)
#define TICKS_FROM_SECS(secs) ((secs)*16384ULL)
#define TICKS_FROM_USECS(usecs) ((usecs)/16384ULL)
#define TICKS_FROM_TV(secs,usecs) (TICKS_FROM_SECS(secs)+TICKS_FROM_USECS(usecs))
#endif

691
src/in-binary.c Normal file
View File

@ -0,0 +1,691 @@
/*
Read in the binary file produced by "out-binary.c". This allows you to
translate the "binary" format into any of the other output formats.
*/
#include "massip-addr.h"
#include "in-binary.h"
#include "masscan.h"
#include "masscan-app.h"
#include "masscan-status.h"
#include "main-globals.h"
#include "output.h"
#include "util-safefunc.h"
#include "in-filter.h"
#include "in-report.h"
#include "util-malloc.h"
#include "util-logger.h"
#include <stdlib.h>
#include <assert.h>
#ifdef _MSC_VER
#pragma warning(disable:4996)
#endif
static const size_t BUF_MAX = 1024*1024;
struct MasscanRecord {
unsigned timestamp;
ipaddress ip;
unsigned char ip_proto;
unsigned short port;
unsigned char reason;
unsigned char ttl;
unsigned char mac[6];
enum ApplicationProtocol app_proto;
};
/***************************************************************************
***************************************************************************/
static void
parse_status(struct Output *out,
enum PortStatus status, /* open/closed */
const unsigned char *buf, size_t buf_length)
{
struct MasscanRecord record;
if (buf_length < 12)
return;
/* parse record */
record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3];
record.ip.ipv4 = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7];
record.ip.version = 4;
record.port = buf[8]<<8 | buf[9];
record.reason = buf[10];
record.ttl = buf[11];
/* if ARP, then there will be a MAC address */
if (record.ip.ipv4 == 0 && buf_length >= 12+6)
memcpy(record.mac, buf+12, 6);
else
memset(record.mac, 0, 6);
if (out->when_scan_started == 0)
out->when_scan_started = record.timestamp;
switch (record.port) {
case 53:
case 123:
case 137:
case 161:
record.ip_proto = 17;
break;
case 36422:
case 36412:
case 2905:
record.ip_proto = 132;
break;
default:
record.ip_proto = 6;
break;
}
/*
* Now report the result
*/
output_report_status(out,
record.timestamp,
status,
record.ip,
record.ip_proto,
record.port,
record.reason,
record.ttl,
record.mac);
}
/***************************************************************************
***************************************************************************/
static void
parse_status2(struct Output *out,
enum PortStatus status, /* open/closed */
const unsigned char *buf, size_t buf_length,
struct MassIP *filter)
{
struct MasscanRecord record;
if (buf_length < 13)
return;
/* parse record */
record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3];
record.ip.ipv4 = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7];
record.ip.version = 4;
record.ip_proto = buf[8];
record.port = buf[9]<<8 | buf[10];
record.reason = buf[11];
record.ttl = buf[12];
/* if ARP, then there will be a MAC address */
if (record.ip.ipv4 == 0 && buf_length >= 13+6)
memcpy(record.mac, buf+13, 6);
else
memset(record.mac, 0, 6);
if (out->when_scan_started == 0)
out->when_scan_started = record.timestamp;
/* Filter for known IP/ports, if specified on command-line */
if (filter && filter->count_ipv4s) {
if (!massip_has_ip(filter, record.ip))
return;
}
if (filter && filter->count_ports) {
if (!massip_has_port(filter, record.port))
return;
}
/*
* Now report the result
*/
output_report_status(out,
record.timestamp,
status,
record.ip,
record.ip_proto,
record.port,
record.reason,
record.ttl,
record.mac);
}
static unsigned char
_get_byte(const unsigned char *buf, size_t length, size_t *offset)
{
unsigned char result;
if (*offset < length) {
result = buf[*offset];
} else {
result = 0xFF;
}
(*offset)++;
return result;
}
static unsigned
_get_integer(const unsigned char *buf, size_t length, size_t *r_offset)
{
unsigned result;
size_t offset = *r_offset;
(*r_offset) += 4;
if (offset + 4 <= length) {
result = buf[offset+0]<<24
| buf[offset+1]<<16
| buf[offset+2]<<8
| buf[offset+3]<<0;
} else {
result = 0xFFFFFFFF;
}
return result;
}
static unsigned short
_get_short(const unsigned char *buf, size_t length, size_t *r_offset)
{
unsigned short result;
size_t offset = *r_offset;
(*r_offset) += 2;
if (offset + 2 <= length) {
result = buf[offset+0]<<8
| buf[offset+1]<<0;
} else {
result = 0xFFFF;
}
return result;
}
static unsigned long long
_get_long(const unsigned char *buf, size_t length, size_t *r_offset)
{
unsigned long long result;
size_t offset = *r_offset;
(*r_offset) += 8;
if (offset + 8 <= length) {
result =
(unsigned long long)buf[offset+0]<<56ULL
| (unsigned long long)buf[offset+1]<<48ULL
| (unsigned long long)buf[offset+2]<<40ULL
| (unsigned long long)buf[offset+3]<<32ULL
| (unsigned long long)buf[offset+4]<<24ULL
| (unsigned long long)buf[offset+5]<<16ULL
| (unsigned long long)buf[offset+6]<<8ULL
| (unsigned long long)buf[offset+7]<<0ULL;
} else {
result = 0xFFFFFFFFffffffffULL;
}
return result;
}
/***************************************************************************
***************************************************************************/
static void
parse_status6(struct Output *out,
enum PortStatus status, /* open/closed */
const unsigned char *buf, size_t length,
struct MassIP *filter)
{
struct MasscanRecord record;
size_t offset = 0;
/* parse record */
record.timestamp = _get_integer(buf, length, &offset);
record.ip_proto = _get_byte(buf, length, &offset);
record.port = _get_short(buf, length, &offset);
record.reason = _get_byte(buf, length, &offset);
record.ttl = _get_byte(buf, length, &offset);
record.ip.version= _get_byte(buf, length, &offset);
if (record.ip.version != 6) {
fprintf(stderr, "[-] corrupt record\n");
return;
}
record.ip.ipv6.hi = _get_long(buf, length, &offset);
record.ip.ipv6.lo = _get_long(buf, length, &offset);
if (out->when_scan_started == 0)
out->when_scan_started = record.timestamp;
/* Filter for known IP/ports, if specified on command-line */
if (filter && filter->count_ipv4s) {
if (!massip_has_ip(filter, record.ip))
return;
}
if (filter && filter->count_ports) {
if (!massip_has_port(filter, record.port))
return;
}
/*
* Now report the result
*/
output_report_status(out,
record.timestamp,
status,
record.ip,
record.ip_proto,
record.port,
record.reason,
record.ttl,
record.mac);
}
/***************************************************************************
***************************************************************************/
static void
parse_banner6(struct Output *out, unsigned char *buf, size_t length,
const struct MassIP *filter,
const struct RangeList *btypes)
{
struct MasscanRecord record;
size_t offset = 0;
/*
* Parse the parts that are common to most records
*/
record.timestamp = _get_integer(buf, length, &offset);
record.ip_proto = _get_byte(buf, length, &offset);
record.port = _get_short(buf, length, &offset);
record.app_proto = _get_short(buf, length, &offset);
record.ttl = _get_byte(buf, length, &offset);
record.ip.version= _get_byte(buf, length, &offset);
if (record.ip.version != 6) {
fprintf(stderr, "[-] corrupt record\n");
return;
}
record.ip.ipv6.hi = _get_long(buf, length, &offset);
record.ip.ipv6.lo = _get_long(buf, length, &offset);
if (out->when_scan_started == 0)
out->when_scan_started = record.timestamp;
/*
* Filter out records if requested
*/
if (!readscan_filter_pass(record.ip, record.port, record.app_proto,
filter, btypes))
return;
/*
* Now print the output
*/
if (offset > length)
return;
output_report_banner(
out,
record.timestamp,
record.ip,
record.ip_proto, /* TCP=6, UDP=17 */
record.port,
record.app_proto, /* HTTP, SSL, SNMP, etc. */
record.ttl, /* ttl */
buf+offset, (unsigned)(length-offset)
);
}
/***************************************************************************
* [OBSOLETE]
* This parses an old version of the banner record. I've still got files
* hanging around with this version, so I'm keeping it in the code for
* now, but eventually I'll get rid of it.
***************************************************************************/
static void
parse_banner3(struct Output *out, unsigned char *buf, size_t buf_length)
{
struct MasscanRecord record;
/*
* Parse the parts that are common to most records
*/
record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3];
record.ip.ipv4 = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7];
record.ip.version = 4;
record.port = buf[8]<<8 | buf[9];
record.app_proto = buf[10]<<8 | buf[11];
if (out->when_scan_started == 0)
out->when_scan_started = record.timestamp;
/*
* Now print the output
*/
output_report_banner(
out,
record.timestamp,
record.ip,
6, /* this is always TCP */
record.port,
record.app_proto,
0, /* ttl */
buf+12, (unsigned)buf_length-12
);
}
/***************************************************************************
* Parse the BANNER record, extracting the timestamp, IP address, and port
* number. We also convert the banner string into a safer form.
***************************************************************************/
static void
parse_banner4(struct Output *out, unsigned char *buf, size_t buf_length)
{
struct MasscanRecord record;
if (buf_length < 13)
return;
/*
* Parse the parts that are common to most records
*/
record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3];
record.ip.ipv4 = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7];
record.ip.version = 4;
record.ip_proto = buf[8];
record.port = buf[9]<<8 | buf[10];
record.app_proto = buf[11]<<8 | buf[12];
if (out->when_scan_started == 0)
out->when_scan_started = record.timestamp;
/*
* Now print the output
*/
output_report_banner(
out,
record.timestamp,
record.ip,
record.ip_proto, /* TCP=6, UDP=17 */
record.port,
record.app_proto, /* HTTP, SSL, SNMP, etc. */
0, /* ttl */
buf+13, (unsigned)buf_length-13
);
}
/***************************************************************************
***************************************************************************/
static void
parse_banner9(struct Output *out, unsigned char *buf, size_t buf_length,
const struct MassIP *filter,
const struct RangeList *btypes)
{
struct MasscanRecord record;
unsigned char *data = buf+14;
size_t data_length = buf_length-14;
if (buf_length < 14)
return;
/*
* Parse the parts that are common to most records
*/
record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3];
record.ip.ipv4 = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7];
record.ip.version = 4;
record.ip_proto = buf[8];
record.port = buf[9]<<8 | buf[10];
record.app_proto = buf[11]<<8 | buf[12];
record.ttl = buf[13];
if (out->when_scan_started == 0)
out->when_scan_started = record.timestamp;
/*
* KLUDGE: when doing SSL stuff, add a IP:name pair to a database
* so we can annotate [VULN] strings with this information
*/
//readscan_report(record.ip, record.app_proto, &data, &data_length);
/*
* Filter out records if requested
*/
if (!readscan_filter_pass(record.ip, record.port, record.app_proto,
filter, btypes))
return;
/*
* Now print the output
*/
output_report_banner(
out,
record.timestamp,
record.ip,
record.ip_proto, /* TCP=6, UDP=17 */
record.port,
record.app_proto, /* HTTP, SSL, SNMP, etc. */
record.ttl, /* ttl */
data, (unsigned)data_length
);
}
/***************************************************************************
* Read in the file, one record at a time.
***************************************************************************/
static uint64_t
_binaryfile_parse(struct Output *out, const char *filename,
struct MassIP *filter,
const struct RangeList *btypes)
{
FILE *fp = 0;
unsigned char *buf = 0;
size_t bytes_read;
uint64_t total_records = 0;
/* Allocate a buffer of up to one megabyte per record */
buf = MALLOC(BUF_MAX);
/* Open the file */
fp = fopen(filename, "rb");
if (fp == NULL) {
fprintf(stderr, "[-] FAIL: --readscan\n");
fprintf(stderr, "[-] %s: %s\n", filename, strerror(errno));
goto end;
}
LOG(0, "[+] --readscan %s\n", filename);
if (feof(fp)) {
LOG(0, "[-] %s: file is empty\n", filename);
goto end;
}
/* first record is pseudo-record */
bytes_read = fread(buf, 1, 'a'+2, fp);
if (bytes_read < 'a'+2) {
LOG(0, "[-] %s: %s\n", filename, strerror(errno));
goto end;
}
/* Make sure it's got the format string */
if (memcmp(buf, "masscan/1.1", 11) != 0) {
LOG(0,
"[-] %s: unknown file format (expected \"masscan/1.1\")\n",
filename);
goto end;
}
/*
* Look for start time
*/
if (buf[11] == '.' && strtoul((char*)buf+12,0,0) >= 2) {
unsigned i;
/* move to next field */
for (i=0; i<'a' && buf[i] && buf[i] != '\n'; i++)
;
i++;
if (buf[i] == 's')
i++;
if (buf[i] == ':')
i++;
/* extract timestamp */
if (i < 'a')
out->when_scan_started = strtoul((char*)buf+i,0,0);
}
/* Now read all records */
for (;;) {
unsigned type;
unsigned length;
/* [TYPE]
* This is one or more bytes indicating the type of type of the
* record
*/
bytes_read = fread(buf, 1, 1, fp);
if (bytes_read != 1)
break;
type = buf[0] & 0x7F;
while (buf[0] & 0x80) {
bytes_read = fread(buf, 1, 1, fp);
if (bytes_read != 1)
break;
type = (type << 7) | (buf[0] & 0x7F);
}
/* [LENGTH]
* Is one byte for lengths smaller than 127 bytes, or two
* bytes for lengths up to 16384.
*/
bytes_read = fread(buf, 1, 1, fp);
if (bytes_read != 1)
break;
length = buf[0] & 0x7F;
while (buf[0] & 0x80) {
bytes_read = fread(buf, 1, 1, fp);
if (bytes_read != 1)
break;
length = (length << 7) | (buf[0] & 0x7F);
}
if (length > BUF_MAX) {
LOG(0, "[-] file corrupt\n");
goto end;
}
/* get the remainder of the record */
bytes_read = fread(buf, 1, length, fp);
if (bytes_read < length)
break; /* eof */
/* Depending on record type, do something different */
switch (type) {
case 1: /* STATUS: open */
if (!btypes->count)
parse_status(out, PortStatus_Open, buf, bytes_read);
break;
case 2: /* STATUS: closed */
if (!btypes->count)
parse_status(out, PortStatus_Closed, buf, bytes_read);
break;
case 3: /* BANNER */
parse_banner3(out, buf, bytes_read);
break;
case 4:
if (fread(buf+bytes_read,1,1,fp) != 1) {
LOG(0, "[-] read() error\n");
exit(1);
}
bytes_read++;
parse_banner4(out, buf, bytes_read);
break;
case 5:
parse_banner4(out, buf, bytes_read);
break;
case 6: /* STATUS: open */
if (!btypes->count)
parse_status2(out, PortStatus_Open, buf, bytes_read, filter);
break;
case 7: /* STATUS: closed */
if (!btypes->count)
parse_status2(out, PortStatus_Closed, buf, bytes_read, filter);
break;
case 9:
parse_banner9(out, buf, bytes_read, filter, btypes);
break;
case 10: /* Open6 */
if (!btypes->count)
parse_status6(out, PortStatus_Open, buf, bytes_read, filter);
break;
case 11: /* Closed6 */
if (!btypes->count)
parse_status6(out, PortStatus_Closed, buf, bytes_read, filter);
break;
case 13: /* Banner6 */
parse_banner6(out, buf, bytes_read, filter, btypes);
break;
case 'm': /* FILEHEADER */
//goto end;
break;
default:
LOG(0, "[-] file corrupt: unknown type %u\n", type);
goto end;
}
total_records++;
if ((total_records & 0xFFFF) == 0)
LOG(0, "[+] %s: %8" PRIu64 "\r", filename, total_records);
}
end:
if (buf)
free(buf);
if (fp)
fclose(fp);
return total_records;
}
/*****************************************************************************
* When masscan is called with the "--readscan" parameter, it doesn't
* do a scan of the live network, but instead reads scan results from
* a file. Those scan results can then be written out in any of the
* other formats. This preserves the original timestamps.
*****************************************************************************/
void
readscan_binary_scanfile(struct Masscan *masscan,
int arg_first, int arg_max, char *argv[])
{
struct Output *out;
int i;
/*
* Create the output system, such as XML or JSON output
*/
out = output_create(masscan, 0);
/*
* Set the start time to zero. We'll read it from the first file
* that we parse
*/
out->when_scan_started = 0;
/*
* We don't parse the entire argument list, just a subrange
* containing the list of files. The 'arg_first' parameter
* points to the first filename after the '--readscan'
* parameter, and 'arg_max' is the parameter after
* the last filename. For example, consider an argument list that
* looks like:
* masscan --foo --readscan file1.scan file2.scan --bar
* Then arg_first=3 and arg_max=5.
*/
for (i=arg_first; i<arg_max; i++) {
_binaryfile_parse(out, argv[i], &masscan->targets, &masscan->banner_types);
}
/* Done! */
output_destroy(out);
}

16
src/in-binary.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef IN_BINARY_H
#define IN_BINARY_H
struct Masscan;
/**
* Read that output of previous scans that were saved in the binary format
* (i.e. using the -oB parameter or the '--output-format binary' parameter).
* The intent is that the user can then re-output in another format like
* JSON or XML.
*/
void
readscan_binary_scanfile(struct Masscan *masscan,
int arg_first, int arg_max, char *argv[]);
#endif

24
src/in-filter.c Normal file
View File

@ -0,0 +1,24 @@
#include "in-filter.h"
#include "massip.h"
int
readscan_filter_pass(ipaddress ip, unsigned port, unsigned type,
const struct MassIP *filter,
const struct RangeList *btypes)
{
if (filter && filter->count_ipv4s) {
if (!massip_has_ip(filter, ip))
return 0;
}
if (filter && filter->count_ports) {
if (!massip_has_port(filter, port))
return 0;
}
if (btypes && btypes->count) {
if (!rangelist_is_contains(btypes, type))
return 0;
}
return 1;
}

22
src/in-filter.h Normal file
View File

@ -0,0 +1,22 @@
/*
This is for filtering input in the "--readscan" feature
*/
#ifndef IN_FILTER_H
#define IN_FILTER_H
#include "massip-addr.h"
struct RangeList;
struct Range6List;
struct MassIP;
/**
* Filters readscan record by IP address, port number,
* or banner-type.
*/
int
readscan_filter_pass(ipaddress ip, unsigned port, unsigned type,
const struct MassIP *massip,
const struct RangeList *btypes);
#endif

397
src/in-report.c Normal file
View File

@ -0,0 +1,397 @@
#include "in-report.h"
#include "masscan-app.h"
#include "crypto-base64.h"
#include "proto-x509.h"
#include "proto-banout.h"
#include "smack.h"
#include "util-malloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
struct CNDB_Entry {
unsigned ip;
char *name;
struct CNDB_Entry *next;
};
struct CNDB_Database {
struct CNDB_Entry *entries[65536];
};
/***************************************************************************
***************************************************************************/
static struct CNDB_Database *db = NULL;
/***************************************************************************
***************************************************************************/
static const char *
cndb_lookup(unsigned ip)
{
const struct CNDB_Entry *entry;
if (db == NULL)
return 0;
entry = db->entries[ip&0xFFFF];
while (entry && entry->ip != ip)
entry = entry->next;
if (entry)
return entry->name;
else {
return 0;
}
}
/***************************************************************************
***************************************************************************/
static void
cndb_add(unsigned ip, const unsigned char *name, size_t name_length)
{
struct CNDB_Entry *entry;
if (name_length == 0)
return;
if (db == NULL) {
db = CALLOC(1, sizeof(*db));
}
entry = MALLOC(sizeof(*entry));
entry->ip =ip;
entry->name = MALLOC(name_length+1);
memcpy(entry->name, name, name_length+1);
entry->name[name_length] = '\0';
entry->next = db->entries[ip&0xFFFF];
db->entries[ip&0xFFFF] = entry;
}
/***************************************************************************
***************************************************************************/
#if 0
static void
cndb_add_cn(unsigned ip, const unsigned char *data, size_t length)
{
size_t offset = 0;
size_t name_offset;
size_t name_length;
if (length < 7)
return;
/*cipher:0x39 , safe-we1.dyndns.org*/
if (memcmp(data+offset, "cipher:", 7) != 0)
return;
offset += 7;
/* skip to name */
while (offset < length && data[offset] != ',')
offset++;
if (offset >= length)
return;
else
offset++; /* skip ',' */
while (offset < length && data[offset] == ' ')
offset++;
if (offset >= length)
return;
/* we should have a good name */
name_offset = offset;
while (offset < length && data[offset] != ',')
offset++;
name_length = offset - name_offset;
/* now insert into database */
cndb_add(ip, data+name_offset, name_length);
}
#endif
/***************************************************************************
***************************************************************************/
#if 0
static unsigned
found(const char *str, size_t str_len, const unsigned char *p, size_t length)
{
size_t i;
if (str_len > length)
return 0;
for (i=0; i<length; i++) {
if (str[0] == p[i] && memcmp(str, p+i, str_len) == 0)
return 1;
}
return 0;
}
#endif
enum {
XUnknown,
XNas,
XWiFi,
XFW,
X509,
XCom,
XVM,
XCam,
XVPN,
XPBX,
Xprint,
Xdefault,
XMail,
Xadmin,
Xav,
Xpot,
Xbox,
Xend
};
unsigned counts[32];
static void
print_counts()
{
unsigned i;
const char *count_names[] = {
"Unknown", "NAS", "WiFi", "FW", "X509",
"Conf", "VM", "Cam", "VPN", "PBX", "Printer",
"default", "mail", "admin", "AV", "honeypot", "box",
0, "", "", ""};
printf("----counts----\n");
for (i=0; i<Xend; i++) {
printf("%10u %s\n", counts[i], count_names[i]);
}
printf("---------------\n");
assert(count_names[i] == NULL);
}
struct Names {
unsigned code;
unsigned length;
const char *name;
} mynames[] = {
/* raspberry pi */
/* issuer[Debian */
{XNas, 9, "nasend~~]"},
{XPBX, 13, "issuer[iPECS]"},
{Xav, 13, "issuer[McAfee"},
{Xadmin,14, "issuer[webmin]"},
{Xadmin,14, "issuer[Webmin "},
{Xprint,15, "subject[HP-IPG]"},
{XNas, 16, "issuer[LaCie SA]"},
{XWiFi, 16, "subject[OpenWrt]"},
{Xadmin,16, "issuer[Puppet CA"},
{Xav, 16, "issuer[Kaspersky"},
{XFW, 17, "subject[Fortinet]"},
{XFW, 17, "issuer[ICC-FW CA]"},
{XCam, 17, "issuer[HIKVISION]"},
{Xprint,17, "subject[SHARP MX-"},
{X509, 18, "issuer[GANDI SAS]"},
{XFW, 18, "subject[FortiGate]"},
{XFW, 18, "issuer[watchguard]"},
{XVM, 18, "issuer[VMware Inc]"},
{Xbox, 19, "issuer[eBox Server]"},
{XFW, 19, "subject[WatchGuard]"},
{X509, 19, "issuer[RapidSSL CA]"},
{X509, 19, "issuer[AddTrust AB]"},
{XCom, 19, "issuer[Cisco SSCA2]"},
{XCom, 19, "subject[Cisco SSCA2]"},
{Xdefault,19,"issuer[v] issuer[v]"},
{X509, 20, "issuer[Register.com]"},
{X509, 20, "issuer[Thawte, Inc.]"},
{X509, 20, "issuer[thawte, Inc.]"},
{XMail, 20, "issuer[EQ-MT-RAPTOR]"},
{X509, 20, "issuer[DigiCert Inc]"},
{X509, 21, "issuer[TERENA SSL CA]"},
{XFW, 21, "issuer[WatchGuard CA]"},
{XVPN, 21, "issuer[OpenVPN Web CA"},
{X509, 21, "issuer[GeoTrust Inc.]"},
{XNas, 21, "issuer[TS Series NAS]"},
{XCom, 21, "subject[Polycom Inc.]"},
{XFW, 21, "issuer[Fortinet Ltd.]"},
{XNas, 21, "issuer[Synology Inc.]"},
{Xdefault,21,"issuer[XX] issuer[XX]"},
{XWiFi, 21, "2Wire]Gateway Device]"},
{X509, 21, "subject[DigiCert Inc]"},
{XCam, 22, "issuer[SamsungTechwin]"},
{X509, 22, "issuer[TAIWAN-CA INC.]"},
{X509, 22, "issuer[GeoTrust, Inc.]"},
{X509, 22, "issuer[ValiCert, Inc.]"},
{0, 22, "issuer[Apache Friends]"},
{X509, 22, "issuer[VeriSign, Inc.]"},
{X509, 22, "issuer[Cybertrust Inc]"},
{XCam, 23, "subject[HiTRON SYSTEMS]"},
{XFW, 23, "issuer[SonicWALL, Inc.]"},
{XFW, 23, "issuer[Future Systems.]"},
{XCom, 23, "issuer[Polycom Root CA]"},
{X509, 24, "issuer[AlphaSSL CA - G2]"},
{X509, 24, "issuer[GlobalSign nv-sa]"},
{XVPN, 24, "SonicWALL, Inc.]SSL-VPN]"},
{X509, 25, "issuer[Comodo CA Limited]"},
{X509, 25, "issuer[COMODO CA Limited]"},
{X509, 25, "issuer[GoDaddy.com, Inc.]"},
{Xbox, 26, "subject[Barracuda Networks]"},
{X509, 26, "issuer[Equifax Secure Inc.]"},
{X509, 28, "issuer[Gandi Standard SSL CA]"},
{X509, 28, "issuer[The USERTRUST Network]"},
{XCom, 28, "subject[Polycom] subject[VSG]"},
{X509, 28, "issuer[EuropeanSSL Server CA]"},
{0, 28, "issuer[SuSE Linux Web Server]"},
{XWiFi, 29, "issuer[CradlePoint Technology]"},
{XVPN, 29, "SonicWALL]Secure Remote Access]"},
{Xdefault,29,"subject[SomeOrganizationalUnit]"},
{Xdefault,29,"issuer[Internet Widgits Pty Ltd]"},
{X509, 30, "issuer[Network Solutions L.L.C.]"},
{X509, 30, "issuer[The Go Daddy Group, Inc.]"},
{Xpot, 30, "issuer[Nepenthes Development Team]"},
{X509, 30, "issuer[WoSign Class 1 DV Server CA]"},
{XCom, 30, "issuer[Polycom Equipment Policy CA]"},
{X509, 30, "issuer[Starfield Technologies, Inc.]"},
{X509, 30, "issuer[Certum Certification Authority]"},
{XNas, 30, "subject[Fujitsu CELVIN(R) NAS Server]"},
{XVPN, 35, "SonicWALL, Inc.]Secure Remote Access]"},
{X509, 40, "issuer[Secure Digital Certificate Signing]"},
{X509, 40, "issuer[Equifax Secure Certificate Authority]"},
{XVM, 40, "subject[VMware ESX Server Default Certificate]"},
{XCam, 40, "issuer[Cisco Systems] issuer[Cisco Manufacturing CA]"},
{0,0, 0}
};
static struct SMACK *global_xnames;
static void
xname_init(void)
{
unsigned i;
global_xnames = smack_create("readscan-x509-names", 0);
for (i=0; mynames[i].name; i++) {
const char *pattern = mynames[i].name;
unsigned len = mynames[i].length;
unsigned id = mynames[i].code;
smack_add_pattern( global_xnames,
pattern,
len,
id,
0
);
}
smack_compile(global_xnames);
}
/***************************************************************************
***************************************************************************/
static unsigned
found_type(const unsigned char *banner, size_t banner_length)
{
size_t id;
unsigned state = 0;
unsigned offset = 0;
/*for (i=0; mynames[i].name; i++) {
if (found(mynames[i].name, mynames[i].length, banner, banner_length))
return 1;
}*/
id = smack_search_next( global_xnames,
&state,
banner,
&offset,
(unsigned)banner_length);
if (id == SMACK_NOT_FOUND)
return 0;
counts[id]++;
return 1;
}
void
readscan_report( unsigned ip,
unsigned app_proto,
unsigned char **r_data,
size_t *r_data_length)
{
size_t data_length = *r_data_length;
unsigned char *data = *r_data;
if (app_proto == PROTO_X509_CERT) {
unsigned char *der = MALLOC(data_length);
struct CertDecode x;
size_t der_length;
struct BannerOutput banout[1];
const unsigned char *banner;
size_t banner_length;
banout_init(banout);
der_length = base64_decode(der, data_length, data, data_length);
x509_decode_init(&x, data_length);
x.is_capture_issuer = 1;
x.is_capture_subject = 1;
x509_decode(&x, der, der_length, banout);
banner = banout_string(banout, PROTO_SSL3);
banner_length = banout_string_length(banout, PROTO_SSL3);
if (banner_length) {
if (!found_type(banner, banner_length))
cndb_add(ip, banner, banner_length);
}
banout_release(banout);
/*} else if (0 && app_proto == PROTO_SSL3) {
cndb_add(ip, data, data_length);*/
} else if (app_proto == PROTO_VULN) {
const char *name = cndb_lookup(ip);
if (data_length == 15 && memcmp(data, "SSL[heartbeat] ", 15) == 0)
return;
if (name && strlen(name) < 300) {
//printf("vuln=%s\n", name);
((char*)data)[data_length] = ' ';
memcpy((char*)data+data_length+1, name, strlen(name)+1);
data_length += strlen(name)+1;
}
/* kludge */
if (data_length == 31 && memcmp(data, "SSL[heartbeat] SSL[HEARTBLEED] ", 31) == 0)
return;
}
}
void
readscan_report_init(void)
{
if (global_xnames == NULL)
xname_init();
}
void
readscan_report_print(void)
{
print_counts();
}

20
src/in-report.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef IN_REPORT_H
#define IN_REPORT_H
#include <stdio.h>
void
readscan_report( unsigned ip,
unsigned app_proto,
unsigned char **data,
size_t *data_length);
void
readscan_report_init(void);
void
readscan_report_print(void);
#endif

3959
src/main-conf.c Normal file

File diff suppressed because it is too large Load Diff

466
src/main-dedup.c Normal file
View File

@ -0,0 +1,466 @@
/*
Filters duplicate responses
This is an asynchronous and "stateless" scanner that spews out probes
without having holding "state" for the probes. This means that when
a response comes back, we have no "state" to associate with it.
This means when two responses come back, we still don't have any
"state" to remember that the first one came back. This will cause
us to report two results instead of one.
We could create a large table holding a record for EVERY response
that we've seen. But this would require a lot of memory for large
scans.
Instead, we remember a small hashtable of recent responses. This
takes advantage of the fact that multiple responses are likely
to be recent and eventually age out.
We call this "deduplication" as it's simply removing duplicate
responses.
*/
#include "main-dedup.h"
#include "util-malloc.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "syn-cookie.h"
/**
* This is the number of entries in our table. More entries does a better job at the
* cost of using more memory.
*/
#define DEDUP_ENTRIES 65536
struct DedupEntry_IPv4
{
unsigned ip_them;
unsigned port_them;
unsigned ip_me;
unsigned port_me;
};
struct DedupEntry_IPv6
{
ipv6address ip_them;
ipv6address ip_me;
unsigned short port_them;
unsigned short port_me;
};
/**
* This is simply the array of entries. We have two arrays, one for IPv4
* and another for IPv6.
*/
struct DedupTable
{
struct DedupEntry_IPv4 entries[DEDUP_ENTRIES][4];
struct DedupEntry_IPv6 entries6[DEDUP_ENTRIES][4];
};
/**
* We use the FNv1a hash algorithm, which starts with this seed value.
*/
const unsigned fnv1a_seed = 0x811C9DC5; /* 2166136261 */
/**
* Hash one byte, the other hash functions of multiple bytes call this function.
* @param hash
* The current hash value that we keep updating as we repeatedly
* call this function, or the `fnv1a_seed value on the first call to
* this function.
*/
static inline unsigned fnv1a(unsigned char c, unsigned hash)
{
const unsigned prime = 0x01000193; /* 16777619 */
return (c ^ hash) * prime;
}
static unsigned fnv1a_string(const void *v_buf, size_t length, unsigned hash)
{
const unsigned char *buf = (const unsigned char *)v_buf;
size_t i;
for (i=0; i<length; i++)
hash = fnv1a(buf[i], hash);
return hash;
}
static inline unsigned fnv1a_short(unsigned data, unsigned hash)
{
hash = fnv1a((data>>0)&0xFF, hash);
hash = fnv1a((data>>8)&0xFF, hash);
return hash;
}
static inline unsigned fnv1a_longlong(unsigned long long data, unsigned hash)
{
return fnv1a_string(&data, 8, hash);
}
/**
* Create a new table, which means simply allocating the object
* and setting it to zero.
*/
struct DedupTable *
dedup_create(void)
{
struct DedupTable *dedup;
dedup = CALLOC(1, sizeof(*dedup));
return dedup;
}
/**
* There's nothing special we need to do to free the structure
* since it's all contained in the single allocation.
*/
void
dedup_destroy(struct DedupTable *dedup)
{
free(dedup);
}
/**
* Create a hash of the IPv6 socket. This doesn't have to be
* cryptographically secure, so we are going to use the FNv1a algorithm.
*/
static inline unsigned
dedup_hash_ipv6(ipaddress ip_them, unsigned port_them, ipaddress ip_me, unsigned port_me)
{
unsigned hash = fnv1a_seed;
hash = fnv1a_longlong(ip_them.ipv6.hi, hash);
hash = fnv1a_longlong(ip_them.ipv6.lo, hash);
hash = fnv1a_short(port_them, hash);
hash = fnv1a_longlong(ip_me.ipv6.hi, hash);
hash = fnv1a_longlong(ip_me.ipv6.lo, hash);
hash = fnv1a_short(port_me, hash);
return hash;
}
/**
* If two IPv6 addresses are equal.
*/
static inline int
is_equal6(ipv6address lhs, ipv6address rhs)
{
return lhs.hi == rhs.hi && lhs.lo == rhs.lo;
}
/**
* Swap two addresses in the table. This uses the classic XOR trick
* rather than using a swap variable.
*/
static inline void
swap6(struct DedupEntry_IPv6 *lhs, struct DedupEntry_IPv6 *rhs)
{
lhs->ip_them.hi ^= rhs->ip_them.hi;
lhs->ip_them.lo ^= rhs->ip_them.lo;
lhs->port_them ^= rhs->port_them;
lhs->ip_me.hi ^= rhs->ip_me.hi;
lhs->ip_me.lo ^= rhs->ip_me.lo;
lhs->port_me ^= rhs->port_me;
rhs->ip_them.hi ^= lhs->ip_them.hi;
rhs->ip_them.lo ^= lhs->ip_them.lo;
rhs->port_them ^= lhs->port_them;
rhs->ip_me.hi ^= lhs->ip_me.hi;
rhs->ip_me.lo ^= lhs->ip_me.lo;
rhs->port_me ^= lhs->port_me;
lhs->ip_them.hi ^= rhs->ip_them.hi;
lhs->ip_them.lo ^= rhs->ip_them.lo;
lhs->port_them ^= rhs->port_them;
lhs->ip_me.hi ^= rhs->ip_me.hi;
lhs->ip_me.lo ^= rhs->ip_me.lo;
lhs->port_me ^= rhs->port_me;
}
/**
* This implements the same algorithm as for IPv4 addresses, but for
* IPv6 addresses instead.
*/
static unsigned
dedup_is_duplicate_ipv6(struct DedupTable *dedup,
ipaddress ip_them, unsigned port_them,
ipaddress ip_me, unsigned port_me)
{
unsigned hash;
struct DedupEntry_IPv6 *bucket;
unsigned i;
/* THREAT: probably need to secure this hash, though the syn-cookies
* provides some protection */
hash = dedup_hash_ipv6(ip_them, port_them, ip_me, port_me);
hash &= DEDUP_ENTRIES-1;
/* Search in this bucket */
bucket = dedup->entries6[hash];
/* If we find the entry in our table, move it to the front, so
* that it won't be aged out as quickly. We keep prepending new
* addresses to front, aging older addresses that haven't been
* seen in a while. */
for (i = 0; i < 4; i++) {
if (is_equal6(bucket[i].ip_them, ip_them.ipv6) && bucket[i].port_them == port_them
&& is_equal6(bucket[i].ip_me, ip_me.ipv6) && bucket[i].port_me == port_me) {
/* move to end of list so constant repeats get ignored */
if (i > 0) {
swap6(&bucket[0], &bucket[i]);
}
return 1;
}
}
/* We didn't find it, so add it to our list. This will push
* older entries at this bucket off the list */
memmove(bucket, bucket+1, 3*sizeof(*bucket));
bucket[0].ip_them.hi = ip_them.ipv6.hi;
bucket[0].ip_them.lo = ip_them.ipv6.lo;
bucket[0].port_them = (unsigned short)port_them;
bucket[0].ip_me.hi = ip_me.ipv6.hi;
bucket[0].ip_me.lo = ip_me.ipv6.lo;
bucket[0].port_me = (unsigned short)port_me;
return 0;
}
/***************************************************************************
***************************************************************************/
static unsigned
dedup_is_duplicate_ipv4(struct DedupTable *dedup,
ipaddress ip_them, unsigned port_them,
ipaddress ip_me, unsigned port_me)
{
unsigned hash;
struct DedupEntry_IPv4 *bucket;
unsigned i;
/* THREAT: probably need to secure this hash, though the syn-cookies
* provides some protection */
hash = (ip_them.ipv4 + port_them) ^ ((ip_me.ipv4) + (ip_them.ipv4>>16)) ^ (ip_them.ipv4>>24) ^ port_me;
hash &= DEDUP_ENTRIES-1;
/* Search in this bucket */
bucket = dedup->entries[hash];
/* If we find the entry in our table, move it to the front, so
* that it won't be aged out as quickly. We keep prepending new
* addresses to front, aging older addresses that haven't been
* seen in a while. */
for (i = 0; i < 4; i++) {
if (bucket[i].ip_them == ip_them.ipv4 && bucket[i].port_them == port_them
&& bucket[i].ip_me == ip_me.ipv4 && bucket[i].port_me == port_me) {
/* move to end of list so constant repeats get ignored */
if (i > 0) {
bucket[i].ip_them ^= bucket[0].ip_them;
bucket[i].port_them ^= bucket[0].port_them;
bucket[i].ip_me ^= bucket[0].ip_me;
bucket[i].port_me ^= bucket[0].port_me;
bucket[0].ip_them ^= bucket[i].ip_them;
bucket[0].port_them ^= bucket[i].port_them;
bucket[0].ip_me ^= bucket[i].ip_me;
bucket[0].port_me ^= bucket[i].port_me;
bucket[i].ip_them ^= bucket[0].ip_them;
bucket[i].port_them ^= bucket[0].port_them;
bucket[i].ip_me ^= bucket[0].ip_me;
bucket[i].port_me ^= bucket[0].port_me;
}
return 1;
}
}
/* We didn't find it, so add it to our list. This will push
* older entries at this bucket off the list */
memmove(bucket, bucket+1, 3*sizeof(*bucket));
bucket[0].ip_them = ip_them.ipv4;
bucket[0].port_them = port_them;
bucket[0].ip_me = ip_me.ipv4;
bucket[0].port_me = port_me;
return 0;
}
/***************************************************************************
***************************************************************************/
unsigned
dedup_is_duplicate(struct DedupTable *dedup,
ipaddress ip_them, unsigned port_them,
ipaddress ip_me, unsigned port_me)
{
if (ip_them.version == 6)
return dedup_is_duplicate_ipv6(dedup, ip_them, port_them, ip_me, port_me);
else
return dedup_is_duplicate_ipv4(dedup, ip_them, port_them, ip_me, port_me);
}
/**
* My own deterministic rand() function for testing this module
*/
static unsigned
_rand(unsigned *seed)
{
static const unsigned a = 214013;
static const unsigned c = 2531011;
*seed = (*seed) * a + c;
return (*seed)>>16 & 0x7fff;
}
/*
* Provide a simple unit test for this module.
*
* This is a pretty lame test. I'm going to generate
* a set of random addresses, tweaked so that they aren't
* too random, so that I get around 30 to 50 expected
* duplicates. If I get zero duplicates, or if I get too
* many duplicates in the test, then I know it's failed.
*
* This is in no way a reliable test that deterministically
* tests the functionality. It's a crappy non-deterministic
* test.
*
* We also do a simple deterministic test, but this still
* is insufficient testing how duplicates age out and such.
*/
int
dedup_selftest(void)
{
struct DedupTable *dedup;
unsigned seed = 0;
size_t i;
unsigned found_match = 0;
unsigned line = 0;
dedup = dedup_create();
/* Deterministic test.
*
* The first time we check on a socket combo, there should
* be no duplicate. The second time we check, however, there should
* be a duplicate.
*/
{
ipaddress ip_me;
ipaddress ip_them;
unsigned port_me;
unsigned port_them;
ip_me.version = 4;
ip_them.version = 4;
ip_me.ipv4 = 0x12345678;
ip_them.ipv4 = 0xabcdef0;
port_me = 0x1234;
port_them = 0xfedc;
if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) {
line = __LINE__;
goto fail;
}
if (!dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) {
line = __LINE__;
goto fail;
}
ip_me.version = 6;
ip_them.version = 6;
ip_me.ipv6.hi = 0x12345678;
ip_me.ipv6.lo = 0x12345678;
ip_them.ipv6.hi = 0xabcdef0;
ip_them.ipv6.lo = 0xabcdef0;
if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) {
line = __LINE__;
goto fail;
}
if (!dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) {
ipaddress_formatted_t fmt1 = ipaddress_fmt(ip_them);
ipaddress_formatted_t fmt2 = ipaddress_fmt(ip_me);
fprintf(stderr, "[-] [%s]:%u -> [%s]:%u\n",
fmt1.string, port_them,
fmt2.string, port_me);
line = __LINE__;
goto fail;
}
}
/* Test IPv4 addresses */
for (i=0; i<100000; i++) {
ipaddress ip_me;
ipaddress ip_them;
unsigned port_me;
unsigned port_them;
ip_me.version = 4;
ip_them.version = 4;
/* Instead of completely random numbers over the entire
* range, each port/IP is restricted to just 512
* random combinations. This should statistically
* give us around 10 matches*/
ip_me.ipv4 = _rand(&seed) & 0xFF800000;
ip_them.ipv4 = _rand(&seed) & 0x1FF;
port_me = _rand(&seed) & 0xFF80;
port_them = _rand(&seed) & 0x1FF;
if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) {
found_match++;
}
}
/* Approximately 30 matches should be found. If we couldn't
* find any, or if we've found too many, then the test has
* failed. */
if (found_match == 0 || found_match > 200) {
line = __LINE__;
goto fail;
}
/* Now do IPv6 */
found_match = 0;
seed = 0;
/* Test IPv4 addresses */
for (i=0; i<100000; i++) {
ipaddress ip_me;
ipaddress ip_them;
unsigned port_me;
unsigned port_them;
ip_me.version = 6;
ip_them.version = 6;
/* Instead of completely random numbers over the entire
* range, each port/IP is restricted to just 512
* random combinations. This should statistically
* give us around 10 matches*/
ip_me.ipv6.hi = _rand(&seed) & 0xFF800000;
ip_them.ipv6.lo = _rand(&seed) & 0x1FF;
port_me = _rand(&seed) & 0xFF80;
port_them = _rand(&seed) & 0x1FF;
if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) {
found_match++;
}
}
/* The result should be same as for IPv4, around 30 matches found. */
if (found_match == 0 || found_match > 200) {
line = __LINE__;
goto fail;
}
/* All tests have passed */
return 0; /* success :) */
fail:
fprintf(stderr, "[-] selftest: 'dedup' failed, file=%s, line=%u\n", __FILE__, line);
return 1;
}

23
src/main-dedup.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef MAIN_DEDUP_H
#define MAIN_DEDUP_H
#include "massip-addr.h"
struct DedupTable *
dedup_create(void);
void
dedup_destroy(struct DedupTable *table);
unsigned
dedup_is_duplicate( struct DedupTable *dedup,
ipaddress ip_them, unsigned port_them,
ipaddress ip_me, unsigned port_me);
/**
* Simple unit test
* @return 0 on success, 1 on failure.
*/
int dedup_selftest(void);
#endif

10
src/main-globals.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef MAIN_GLOBALS_H
#define MAIN_GLOBALS_H
#include <time.h>
extern unsigned volatile is_tx_done;
extern unsigned volatile is_rx_done;
extern time_t global_now;
#endif

269
src/main-initadapter.c Executable file
View File

@ -0,0 +1,269 @@
#include "masscan.h"
#include "util-logger.h"
#include "rawsock.h"
#include "rawsock-adapter.h"
#include "stack-arpv4.h"
#include "stack-ndpv6.h"
#include "stub-pcap-dlt.h"
/***************************************************************************
* Initialize the network adapter.
*
* This requires finding things like our IP address, MAC address, and router
* MAC address. The user could configure these things manually instead.
*
* Note that we don't update the "static" configuration with the discovered
* values, but instead return them as the "running" configuration. That's
* so if we pause and resume a scan, auto discovered values don't get saved
* in the configuration file.
***************************************************************************/
int
masscan_initialize_adapter(
struct Masscan *masscan,
unsigned index,
macaddress_t *source_mac,
macaddress_t *router_mac_ipv4,
macaddress_t *router_mac_ipv6
)
{
char *ifname;
char ifname2[256];
unsigned adapter_ip = 0;
unsigned is_usable_ipv4 = !massip_has_ipv4_targets(&masscan->targets); /* I don't understand this line, seems opposite */
unsigned is_usable_ipv6 = !massip_has_ipv6_targets(&masscan->targets); /* I don't understand this line, seems opposite */
ipaddress_formatted_t fmt;
/*
* ADAPTER/NETWORK-INTERFACE
*
* If no network interface was configured, we need to go hunt down
* the best Interface to use. We do this by choosing the first
* interface with a "default route" (aka. "gateway") defined
*/
if (masscan->nic[index].ifname[0])
ifname = masscan->nic[index].ifname;
else {
/* no adapter specified, so find a default one */
int err;
ifname2[0] = '\0';
err = rawsock_get_default_interface(ifname2, sizeof(ifname2));
if (err || ifname2[0] == '\0') {
LOG(0, "[-] FAIL: could not determine default interface\n");
LOG(0, " [hint] try \"--interface ethX\"\n");
return -1;
}
ifname = ifname2;
}
LOG(1, "[+] interface = %s\n", ifname);
/*
* START ADAPTER
*
* Once we've figured out which adapter to use, we now need to
* turn it on.
*/
masscan->nic[index].adapter = rawsock_init_adapter(
ifname,
masscan->is_pfring,
masscan->is_sendq,
masscan->nmap.packet_trace,
masscan->is_offline,
(void*)masscan->bpf_filter,
masscan->nic[index].is_vlan,
masscan->nic[index].vlan_id);
if (masscan->nic[index].adapter == 0) {
LOG(0, "[-] if:%s:init: failed\n", ifname);
return -1;
}
masscan->nic[index].link_type = masscan->nic[index].adapter->link_type;
LOG(1, "[+] interface-type = %u\n", masscan->nic[index].link_type);
rawsock_ignore_transmits(masscan->nic[index].adapter, ifname);
/*
* MAC ADDRESS
*
* This is the address we send packets from. It actually doesn't really
* matter what this address is, but to be a "responsible" citizen we
* try to use the hardware address in the network card.
*/
if (masscan->nic[index].link_type == PCAP_DLT_NULL) {
LOG(1, "[+] source-mac = %s\n", "none");
} else if (masscan->nic[index].link_type == PCAP_DLT_RAW) {
LOG(1, "[+] source-mac = %s\n", "none");
} else {
*source_mac = masscan->nic[index].source_mac;
if (masscan->nic[index].my_mac_count == 0) {
if (macaddress_is_zero(*source_mac)) {
rawsock_get_adapter_mac(ifname, source_mac->addr);
}
/* If still zero, then print error message */
if (macaddress_is_zero(*source_mac)) {
fprintf(stderr, "[-] FAIL: failed to detect MAC address of interface:"
" \"%s\"\n", ifname);
fprintf(stderr, " [hint] try something like "
"\"--source-mac 00-11-22-33-44-55\"\n");
return -1;
}
}
fmt = macaddress_fmt(*source_mac);
LOG(1, "[+] source-mac = %s\n", fmt.string);
}
/*
* IPv4 ADDRESS
*
* We need to figure out that IP address to send packets from. This
* is done by querying the adapter (or configured by user). If the
* adapter doesn't have one, then the user must configure one.
*/
if (massip_has_ipv4_targets(&masscan->targets)) {
adapter_ip = masscan->nic[index].src.ipv4.first;
if (adapter_ip == 0) {
adapter_ip = rawsock_get_adapter_ip(ifname);
masscan->nic[index].src.ipv4.first = adapter_ip;
masscan->nic[index].src.ipv4.last = adapter_ip;
masscan->nic[index].src.ipv4.range = 1;
}
if (adapter_ip == 0) {
/* We appear to have IPv4 targets, yet we cannot find an adapter
* to use for those targets. We are having trouble querying the
* operating system stack. */
LOG(0, "[-] FAIL: failed to detect IP of interface \"%s\"\n", ifname);
LOG(0, " [hint] did you spell the name correctly?\n");
LOG(0, " [hint] if it has no IP address, manually set with something like "
"\"--source-ip 198.51.100.17\"\n");
if (massip_has_ipv4_targets(&masscan->targets)) {
return -1;
}
}
fmt = ipv4address_fmt(adapter_ip);
LOG(1, "[+] source-ip = %s\n", fmt.string);
if (adapter_ip != 0)
is_usable_ipv4 = 1;
/*
* ROUTER MAC ADDRESS
*
* NOTE: this is one of the least understood aspects of the code. We must
* send packets to the local router, which means the MAC address (not
* IP address) of the router.
*
* Note: in order to ARP the router, we need to first enable the libpcap
* code above.
*/
*router_mac_ipv4 = masscan->nic[index].router_mac_ipv4;
if (masscan->is_offline) {
/* If we are doing offline benchmarking/testing, then create
* a fake MAC address fro the router */
memcpy(router_mac_ipv4->addr, "\x66\x55\x44\x33\x22\x11", 6);
} else if (masscan->nic[index].link_type == PCAP_DLT_NULL) {
/* If it's a VPN tunnel, then there is no Ethernet MAC address */
LOG(1, "[+] router-mac-ipv4 = %s\n", "implicit");
} else if (masscan->nic[index].link_type == PCAP_DLT_RAW) {
/* If it's a VPN tunnel, then there is no Ethernet MAC address */
LOG(1, "[+] router-mac-ipv4 = %s\n", "implicit");
} else if (macaddress_is_zero(*router_mac_ipv4)) {
ipv4address_t router_ipv4 = masscan->nic[index].router_ip;
int err = 0;
LOG(2, "[+] if(%s): looking for default gateway\n", ifname);
if (router_ipv4 == 0)
err = rawsock_get_default_gateway(ifname, &router_ipv4);
if (err == 0) {
fmt = ipv4address_fmt(router_ipv4);
LOG(1, "[+] router-ip = %s\n", fmt.string);
LOG(2, "[+] if(%s):arp: resolving IPv4 address\n", ifname);
stack_arp_resolve(
masscan->nic[index].adapter,
adapter_ip,
*source_mac,
router_ipv4,
router_mac_ipv4);
}
fmt = macaddress_fmt(*router_mac_ipv4);
LOG(1, "[+] router-mac-ipv4 = %s\n", fmt.string);
if (macaddress_is_zero(*router_mac_ipv4)) {
fmt = ipv4address_fmt(masscan->nic[index].router_ip);
LOG(0, "[-] FAIL: ARP timed-out resolving MAC address for router %s: \"%s\"\n", ifname, fmt.string);
LOG(0, " [hint] try \"--router ip 192.0.2.1\" to specify different router\n");
LOG(0, " [hint] try \"--router-mac 66-55-44-33-22-11\" instead to bypass ARP\n");
LOG(0, " [hint] try \"--interface eth0\" to change interface\n");
return -1;
}
}
}
/*
* IPv6 ADDRESS
*
* We need to figure out that IPv6 address to send packets from. This
* is done by querying the adapter (or configured by user). If the
* adapter doesn't have one, then the user must configure one.
*/
if (massip_has_ipv6_targets(&masscan->targets)) {
ipv6address adapter_ipv6 = masscan->nic[index].src.ipv6.first;
if (ipv6address_is_zero(adapter_ipv6)) {
adapter_ipv6 = rawsock_get_adapter_ipv6(ifname);
masscan->nic[index].src.ipv6.first = adapter_ipv6;
masscan->nic[index].src.ipv6.last = adapter_ipv6;
masscan->nic[index].src.ipv6.range = 1;
}
if (ipv6address_is_zero(adapter_ipv6)) {
fprintf(stderr, "[-] FAIL: failed to detect IPv6 address of interface \"%s\"\n",
ifname);
fprintf(stderr, " [hint] did you spell the name correctly?\n");
fprintf(stderr, " [hint] if it has no IP address, manually set with something like "
"\"--source-ip 2001:3b8::1234\"\n");
return -1;
}
fmt = ipv6address_fmt(adapter_ipv6);
LOG(1, "[+] source-ip = [%s]\n", fmt.string);
is_usable_ipv6 = 1;
/*
* ROUTER MAC ADDRESS
*/
*router_mac_ipv6 = masscan->nic[index].router_mac_ipv6;
if (masscan->is_offline) {
memcpy(router_mac_ipv6->addr, "\x66\x55\x44\x33\x22\x11", 6);
}
if (macaddress_is_zero(*router_mac_ipv6)) {
/* [synchronous]
* Wait for router neighbor notification. This may take
* some time */
stack_ndpv6_resolve(
masscan->nic[index].adapter,
adapter_ipv6,
*source_mac,
router_mac_ipv6);
}
fmt = macaddress_fmt(*router_mac_ipv6);
LOG(1, "[+] router-mac-ipv6 = %s\n", fmt.string);
if (macaddress_is_zero(*router_mac_ipv6)) {
fmt = ipv4address_fmt(masscan->nic[index].router_ip);
LOG(0, "[-] FAIL: NDP timed-out resolving MAC address for router %s: \"%s\"\n", ifname, fmt.string);
LOG(0, " [hint] try \"--router-mac-ipv6 66-55-44-33-22-11\" instead to bypass ARP\n");
LOG(0, " [hint] try \"--interface eth0\" to change interface\n");
return -1;
}
}
masscan->nic[index].is_usable = (is_usable_ipv4 & is_usable_ipv6);
LOG(2, "[+] if(%s): initialization done.\n", ifname);
return 0;
}

72
src/main-listscan.c Normal file
View File

@ -0,0 +1,72 @@
#include "masscan.h"
#include "util-logger.h"
#include "crypto-blackrock.h"
void
main_listscan(struct Masscan *masscan)
{
uint64_t i;
uint64_t range;
uint64_t start;
uint64_t end;
struct BlackRock blackrock;
unsigned increment = masscan->shard.of;
uint64_t seed = masscan->seed;
/* If called with no ports, then create a pseudo-port needed
* for the internal algorithm. */
if (!massip_has_target_ports(&masscan->targets))
rangelist_add_range(&masscan->targets.ports, 80, 80);
massip_optimize(&masscan->targets);
/* The "range" is the total number of IP/port combinations that
* the scan can produce */
range = massip_range(&masscan->targets).lo;
infinite:
blackrock_init(&blackrock, range, seed, masscan->blackrock_rounds);
start = masscan->resume.index + (masscan->shard.one-1);
end = range;
if (masscan->resume.count && end > start + masscan->resume.count)
end = start + masscan->resume.count;
end += (uint64_t)(masscan->retries * masscan->max_rate);
for (i=start; i<end; ) {
uint64_t xXx;
unsigned port;
ipaddress addr;
xXx = blackrock_shuffle(&blackrock, i);
massip_pick(&masscan->targets, xXx, &addr, &port);
if (masscan->is_test_csv) {
/* [KLUDGE] [TEST]
* For testing randomness output, prints last two bytes of
* IP address as CSV format for import into spreadsheet
*/
printf("%u,%u\n",(addr.ipv4>>8)&0xFF, (addr.ipv4>>0)&0xFF);
} else if (masscan->targets.count_ports == 1) {
ipaddress_formatted_t fmt = ipaddress_fmt(addr);
/* This is the normal case */
printf("%s\n", fmt.string);
} else {
ipaddress_formatted_t fmt = ipaddress_fmt(addr);
if (addr.version == 6)
printf("[%s]:%u\n", fmt.string, port);
else
printf("%s:%u\n", fmt.string, port);
}
i += increment; /* <------ increment by 1 normally, more with shards/NICs */
}
if (masscan->is_infinite) {
seed++;
goto infinite;
}
}

109
src/main-ptrace.c Normal file
View File

@ -0,0 +1,109 @@
#include "main-ptrace.h"
#include "proto-preprocess.h"
#include "pixie-timer.h"
#include "util-safefunc.h"
/***************************************************************************
* Print packet info, when using nmap-style --packet-trace option
***************************************************************************/
void
packet_trace(FILE *fp, double pt_start, const unsigned char *px, size_t length, unsigned is_sent)
{
unsigned x;
struct PreprocessedInfo parsed;
char from[64];
char to[64];
char sz_type[32];
unsigned type;
double timestamp = 1.0 * pixie_gettime() / 1000000.0;
unsigned offset;
const char *direction;
ipaddress_formatted_t fmt;
if (is_sent)
direction = "SENT";
else
direction = "RCVD";
/* parse the packet */
x = preprocess_frame(px, (unsigned)length, 1, &parsed);
if (!x)
return;
offset = parsed.found_offset;
/* format the IP addresses into fixed-width fields */
fmt = ipaddress_fmt(parsed.src_ip);
snprintf(from, sizeof(from), "[%s]:%u", fmt.string, parsed.port_src);
fmt = ipaddress_fmt(parsed.dst_ip);
snprintf(to, sizeof(to), "[%s]:%u", fmt.string, parsed.port_dst);
switch (parsed.found) {
case FOUND_ARP:
type = px[offset+6]<<8 | px[offset+7];
*strchr(to, ':') = '\0';
*strchr(from, ':') = '\0';
switch (type) {
case 1:safe_strcpy(sz_type, sizeof(sz_type), "request"); break;
case 2:safe_strcpy(sz_type, sizeof(sz_type), "response"); break;
default: snprintf(sz_type, sizeof(sz_type), "unknown(%u)", type); break;
}
fprintf(fp, "%s (%5.4f) ARP %-21s > %-21s %s\n", direction,
timestamp - pt_start, from, to, sz_type);
break;
case FOUND_DNS:
case FOUND_UDP:
fprintf(fp, "%s (%5.4f) UDP %-21s > %-21s \n", direction,
timestamp - pt_start, from, to);
break;
case FOUND_ICMP:
fprintf(fp, "%s (%5.4f) ICMP %-21s > %-21s \n", direction,
timestamp - pt_start, from, to);
break;
case FOUND_TCP:
type = px[offset+13];
switch (type) {
case 0x00: safe_strcpy(sz_type, sizeof(sz_type), "NULL"); break;
case 0x01: safe_strcpy(sz_type, sizeof(sz_type), "FIN"); break;
case 0x11: safe_strcpy(sz_type, sizeof(sz_type), "FIN-ACK"); break;
case 0x19: safe_strcpy(sz_type, sizeof(sz_type), "FIN-ACK-PSH"); break;
case 0x02: safe_strcpy(sz_type, sizeof(sz_type), "SYN"); break;
case 0x12: safe_strcpy(sz_type, sizeof(sz_type), "SYN-ACK"); break;
case 0x04: safe_strcpy(sz_type, sizeof(sz_type), "RST"); break;
case 0x14: safe_strcpy(sz_type, sizeof(sz_type), "RST-ACK"); break;
case 0x15: safe_strcpy(sz_type, sizeof(sz_type), "RST-FIN-ACK"); break;
case 0x10: safe_strcpy(sz_type, sizeof(sz_type), "ACK"); break;
case 0x18: safe_strcpy(sz_type, sizeof(sz_type), "ACK-PSH"); break;
default:
snprintf(sz_type, sizeof(sz_type),
"%s%s%s%s%s%s%s%s",
(type&0x01)?"FIN":"",
(type&0x02)?"SYN":"",
(type&0x04)?"RST":"",
(type&0x08)?"PSH":"",
(type&0x10)?"ACK":"",
(type&0x20)?"URG":"",
(type&0x40)?"ECE":"",
(type&0x80)?"CWR":""
);
break;
}
if (parsed.app_length)
fprintf(fp, "%s (%5.4f) TCP %-21s > %-21s %s %u-bytes\n", direction,
timestamp - pt_start, from, to, sz_type, parsed.app_length);
else
fprintf(fp, "%s (%5.4f) TCP %-21s > %-21s %s\n", direction,
timestamp - pt_start, from, to, sz_type);
break;
case FOUND_IPV6:
break;
default:
fprintf(fp, "%s (%5.4f) UNK %-21s > %-21s [%u]\n", direction,
timestamp - pt_start, from, to, parsed.found);
break;
}
}

10
src/main-ptrace.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef masscan_main_ptrace_h
#define masscan_main_ptrace_h
#include <stdio.h>
#include <stdint.h>
void packet_trace(FILE *fp, double pt_trace, const unsigned char *px, size_t length, unsigned is_sent);
#endif

106
src/main-readrange.c Normal file
View File

@ -0,0 +1,106 @@
#include "main-readrange.h"
#include "masscan.h"
#include <assert.h>
/***************************************************************************
***************************************************************************/
/*static unsigned
count_cidr_bits(struct Range range)
{
unsigned i;
for (i=0; i<32; i++) {
unsigned mask = 0xFFFFFFFF >> i;
if ((range.begin & ~mask) == (range.end & ~mask)) {
if ((range.begin & mask) == 0 && (range.end & mask) == mask)
return i;
}
}
return 0;
}*/
/***************************************************************************
***************************************************************************/
static unsigned
count_cidr6_bits(struct Range6 range)
{
uint64_t i;
/* Kludge: can't handle more than 64-bits of CIDR ranges */
if (range.begin.hi != range.begin.lo)
return 0;
for (i=0; i<64; i++) {
uint64_t mask = 0xFFFFFFFFffffffffull >> i;
if ((range.begin.lo & ~mask) == (range.end.lo & ~mask)) {
if ((range.begin.lo & mask) == 0 && (range.end.lo & mask) == mask)
return (unsigned)i;
}
}
return 0;
}
/***************************************************************************
***************************************************************************/
void
main_readrange(struct Masscan *masscan)
{
struct RangeList *list4 = &masscan->targets.ipv4;
struct Range6List *list6 = &masscan->targets.ipv6;
unsigned i;
FILE *fp = stdout;
for (i=0; i<list4->count; i++) {
unsigned prefix_length;
struct Range range = list4->list[i];
if (range.begin == range.end) {
fprintf(fp, "%u.%u.%u.%u\n",
(range.begin>>24)&0xFF,
(range.begin>>16)&0xFF,
(range.begin>> 8)&0xFF,
(range.begin>> 0)&0xFF
);
} else if (range_is_cidr(range, &prefix_length)) {
fprintf(fp, "%u.%u.%u.%u/%u\n",
(range.begin>>24)&0xFF,
(range.begin>>16)&0xFF,
(range.begin>> 8)&0xFF,
(range.begin>> 0)&0xFF,
prefix_length
);
} else {
fprintf(fp, "%u.%u.%u.%u-%u.%u.%u.%u\n",
(range.begin>>24)&0xFF,
(range.begin>>16)&0xFF,
(range.begin>> 8)&0xFF,
(range.begin>> 0)&0xFF,
(range.end>>24)&0xFF,
(range.end>>16)&0xFF,
(range.end>> 8)&0xFF,
(range.end>> 0)&0xFF
);
}
}
for (i=0; i<list6->count; i++) {
struct Range6 range = list6->list[i];
ipaddress_formatted_t fmt = ipv6address_fmt(range.begin);
fprintf(fp, "%s", fmt.string);
if (!ipv6address_is_equal(range.begin, range.end)) {
unsigned cidr_bits = count_cidr6_bits(range);
if (cidr_bits) {
fprintf(fp, "/%u", cidr_bits);
} else {
fmt = ipv6address_fmt(range.end);
fprintf(fp, "-%s", fmt.string);
}
}
fprintf(fp, "\n");
}
}

8
src/main-readrange.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef MAIN_READRANGE_H
#define MAIN_READRANGE_H
struct Masscan;
void
main_readrange(struct Masscan *masscan);
#endif

292
src/main-status.c Normal file
View File

@ -0,0 +1,292 @@
/*
prints "status" message once per second to the commandline
The status message indicates:
- the rate in packets-per-second
- %done
- estimated time remaining of the scan
- number of 'tcbs' (TCP control blocks) of active TCP connections
*/
#include "main-status.h"
#include "pixie-timer.h"
#include "unusedparm.h"
#include "main-globals.h"
#include "util-safefunc.h"
#include "util-bool.h"
#include <stdio.h>
/***************************************************************************
* Print a status message about once-per-second to the command-line. This
* algorithm is a little funky because checking the timestamp on EVERY
* packet is slow.
***************************************************************************/
void
status_print(
struct Status *status,
uint64_t count,
uint64_t max_count,
double pps,
uint64_t total_tcbs,
uint64_t total_synacks,
uint64_t total_syns,
uint64_t exiting,
bool json_status)
{
double elapsed_time;
double rate;
double now;
double percent_done;
double time_remaining;
uint64_t current_tcbs = 0;
uint64_t current_synacks = 0;
uint64_t current_syns = 0;
double tcb_rate = 0.0;
double synack_rate = 0.0;
double syn_rate = 0.0;
double kpps = pps / 1000;
const char *fmt;
/* Support for --json-status; does not impact legacy/default output */
/**
* {"state":"*","rate":{"kpps":24.99,"pps":24985.49,"synps": 27763,"ackps":4,"tcbps":4},"tcb": 33,"syn":246648}
*/
const char* json_fmt_infinite =
"{"
"\"state\":\"*\","
"\"rate\":"
"{"
"\"kpps\":%.2f,"
"\"pps\":%6$.2f,"
"\"synps\":%.0f,"
"\"ackps\":%.0f,"
"\"tcbps\":%.0f"
"},"
"\"tcb\":%5$" PRIu64 ","
"\"syn\":%7$" PRIu64
"}\n";
/**
* {"state":"waiting","rate":{"kpps":0.00,"pps":0.00},"progress":{"percent":21.87,"seconds":4,"found":56,"syn":{"sent": 341436,"total":1561528,"remaining":1220092}}}
*/
const char *json_fmt_waiting =
"{"
"\"state\":\"waiting\","
"\"rate\":"
"{"
"\"kpps\":%.2f,"
"\"pps\":%5$.2f"
"},"
"\"progress\":"
"{"
"\"percent\":%.2f,"
"\"seconds\":%d,"
"\"found\":%" PRIu64 ","
"\"syn\":"
"{"
"\"sent\":%6$" PRIu64 ","
"\"total\":%7$" PRIu64 ","
"\"remaining\":%8$" PRIu64
"}"
"}"
"}\n";
/**
* {"state":"running","rate":{"kpps":24.92,"pps":24923.07},"progress":{"percent":9.77,"eta":{
* "hours":0,"mins":0,"seconds":55},"syn":{"sent": 152510,"total": 1561528,"remaining": 1409018},"found": 27}}
*/
const char *json_fmt_running =
"{"
"\"state\":\"running\","
"\"rate\":"
"{"
"\"kpps\":%.2f,"
"\"pps\":%7$.2f"
"},"
"\"progress\":"
"{"
"\"percent\":%.2f,"
"\"eta\":"
"{"
"\"hours\":%u,"
"\"mins\":%u,"
"\"seconds\":%u"
"},"
"\"syn\":"
"{"
"\"sent\":%8$" PRIu64 ","
"\"total\":%9$" PRIu64 ","
"\"remaining\":%10$" PRIu64
"},"
"\"found\":%6$" PRIu64
"}"
"}\n";
/*
* #### FUGGLY TIME HACK ####
*
* PF_RING doesn't timestamp packets well, so we can't base time from
* incoming packets. Checking the time ourself is too ugly on per-packet
* basis. Therefore, we are going to create a global variable that keeps
* the time, and update that variable whenever it's convenient. This
* is one of those convenient places.
*/
global_now = time(0);
/* Get the time. NOTE: this is CLOCK_MONOTONIC_RAW on Linux, not
* wall-clock time. */
now = (double)pixie_gettime();
/* Figure how many SECONDS have elapsed, in a floating point value.
* Since the above timestamp is in microseconds, we need to
* shift it by 1-million
*/
elapsed_time = (now - status->last.clock)/1000000.0;
if (elapsed_time <= 0)
return;
/* Figure out the "packets-per-second" number, which is just:
*
* rate = packets_sent / elapsed_time;
*/
rate = (count - status->last.count)*1.0/elapsed_time;
/*
* Smooth the number by averaging over the last 8 seconds
*/
status->last_rates[status->last_count++ & 0x7] = rate;
rate = status->last_rates[0]
+ status->last_rates[1]
+ status->last_rates[2]
+ status->last_rates[3]
+ status->last_rates[4]
+ status->last_rates[5]
+ status->last_rates[6]
+ status->last_rates[7]
;
rate /= 8;
/*if (rate == 0)
return;*/
/*
* Calculate "percent-done", which is just the total number of
* packets sent divided by the number we need to send.
*/
percent_done = (double)(count*100.0/max_count);
/*
* Calculate the time remaining in the scan
*/
time_remaining = (1.0 - percent_done/100.0) * (max_count / rate);
/*
* some other stats
*/
if (total_tcbs) {
current_tcbs = total_tcbs - status->total_tcbs;
status->total_tcbs = total_tcbs;
tcb_rate = (1.0*current_tcbs)/elapsed_time;
}
if (total_synacks) {
current_synacks = total_synacks - status->total_synacks;
status->total_synacks = total_synacks;
synack_rate = (1.0*current_synacks)/elapsed_time;
}
if (total_syns) {
current_syns = total_syns - status->total_syns;
status->total_syns = total_syns;
syn_rate = (1.0*current_syns)/elapsed_time;
}
/*
* Print the message to <stderr> so that <stdout> can be redirected
* to a file (<stdout> reports what systems were found).
*/
if (status->is_infinite) {
if (json_status == 1)
fmt = json_fmt_infinite;
else
fmt = "rate:%6.2f-kpps, syn/s=%.0f ack/s=%.0f tcb-rate=%.0f, %" PRIu64 "-tcbs, \r";
fprintf(stderr,
fmt,
kpps,
syn_rate,
synack_rate,
tcb_rate,
total_tcbs,
pps,
count);
} else {
if (is_tx_done) {
if (json_status == 1)
fmt = json_fmt_waiting;
else
fmt = "rate:%6.2f-kpps, %5.2f%% done, waiting %d-secs, found=%" PRIu64 " \r";
fprintf(stderr,
fmt,
pps/1000.0,
percent_done,
(int)exiting,
total_synacks,
pps,
count,
max_count,
max_count-count);
} else {
if (json_status == 1)
fmt = json_fmt_running;
else
fmt = "rate:%6.2f-kpps, %5.2f%% done,%4u:%02u:%02u remaining, found=%" PRIu64 " \r";
fprintf(stderr,
fmt,
pps/1000.0,
percent_done,
(unsigned)(time_remaining/60/60),
(unsigned)(time_remaining/60)%60,
(unsigned)(time_remaining)%60,
total_synacks,
pps,
count,
max_count,
max_count-count);
}
}
fflush(stderr);
/*
* Remember the values to be diffed against the next time around
*/
status->last.clock = now;
status->last.count = count;
}
/***************************************************************************
***************************************************************************/
void
status_finish(struct Status *status)
{
UNUSEDPARM(status);
fprintf(stderr,
" \r");
}
/***************************************************************************
***************************************************************************/
void
status_start(struct Status *status)
{
memset(status, 0, sizeof(*status));
status->last.clock = clock();
status->last.time = time(0);
status->last.count = 0;
status->timer = 0x1;
}

33
src/main-status.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef MAIN_STATUS_H
#define MAIN_STATUS_H
#include <stdint.h>
#include <time.h>
#include "util-bool.h"
struct Status
{
struct {
double clock;
time_t time;
uint64_t count;
} last;
uint64_t timer;
unsigned charcount;
double last_rates[8];
unsigned last_count;
unsigned is_infinite:1;
uint64_t total_tcbs;
uint64_t total_synacks;
uint64_t total_syns;
};
void status_print(struct Status *status, uint64_t count, uint64_t max_count, double x, uint64_t total_tcbs, uint64_t total_synacks, uint64_t total_syns, uint64_t exiting, bool json_status);
void status_finish(struct Status *status);
void status_start(struct Status *status);
#endif

163
src/main-throttle.c Normal file
View File

@ -0,0 +1,163 @@
/*
Rate-limit/throttler: stops us from transmitting too fast.
We can send packets at millions of packets/second. This will
melt most networks. Therefore, we need to throttle or rate-limit
how fast we go.
Since we are sending packet at a rate of 10-million-per-second, we
the calculations need to be done in a light-weight manner. For one
thing, we can't do a system-call per packet.
NOTE: one complication to watch for is the difference between clock
time and elapsed time, and that they change. We have to avoid a problem
where somebody suspends the computer for a few days, then wake it up,
at which point the system tries sending a million packets/second instead
of the desired thousand packets/second.
*/
#include "main-throttle.h"
#include "pixie-timer.h"
#include "util-logger.h"
#include <string.h>
#include <stdio.h>
/***************************************************************************
***************************************************************************/
void
throttler_start(struct Throttler *throttler, double max_rate)
{
unsigned i;
memset(throttler, 0, sizeof(*throttler));
throttler->max_rate = max_rate;
for (i=0; i<sizeof(throttler->buckets)/sizeof(throttler->buckets[0]); i++) {
throttler->buckets[i].timestamp = pixie_gettime();
throttler->buckets[i].packet_count = 0;
}
throttler->batch_size = 1;
LOG(1, "[+] starting throttler: rate = %0.2f-pps\n", throttler->max_rate);
}
/***************************************************************************
* We return the number of packets that can be sent in a batch. Thus,
* instead of trying to throttle each packet individually, which has a
* high per-packet cost, we try to throttle a bunch at a time. Normally,
* this function will return 1, only at high rates does it return larger
* numbers.
*
* NOTE: The minimum value this returns is 1. When it's less than that,
* it'll pause and wait until it's ready to send a packet.
***************************************************************************/
uint64_t
throttler_next_batch(struct Throttler *throttler, uint64_t packet_count)
{
uint64_t timestamp;
uint64_t index;
uint64_t old_timestamp;
uint64_t old_packet_count;
double current_rate;
double max_rate = throttler->max_rate;
again:
/* NOTE: this uses CLOCK_MONOTONIC_RAW on Linux, so the timstamp doesn't
* move forward when the machine is suspended */
timestamp = pixie_gettime();
/*
* We record that last 256 buckets, and average the rate over all of
* them.
*/
index = (throttler->index) & 0xFF;
throttler->buckets[index].timestamp = timestamp;
throttler->buckets[index].packet_count = packet_count;
index = (++throttler->index) & 0xFF;
old_timestamp = throttler->buckets[index].timestamp;
old_packet_count = throttler->buckets[index].packet_count;
/*
* If the delay is more than 1-second, then we should reset the system
* in order to avoid transmitting too fast.
*/
if (timestamp - old_timestamp > 1000000) {
//throttler_start(throttler, throttler->max_rate);
throttler->batch_size = 1;
goto again;
}
/*
* Calculate the recent rate.
* NOTE: this isn't the rate "since start", but only the "recent" rate.
* That's so that if the system pauses for a while, we don't flood the
* network trying to catch up.
*/
current_rate = 1.0*(packet_count - old_packet_count)/((timestamp - old_timestamp)/1000000.0);
/*
* If we've been going too fast, then <pause> for a moment, then
* try again.
*/
if (current_rate > max_rate) {
double waittime;
/* calculate waittime, in seconds */
waittime = (current_rate - max_rate) / throttler->max_rate;
/* At higher rates of speed, we don't actually need to wait the full
* interval. It's better to have a much smaller interval, so that
* we converge back on the true rate faster */
waittime *= 0.1;
/* This is in case of gross failure of the system. This should never
* actually happen, unless there is a bug. Really, I ought to make
* this an 'assert()' instead to fail and fix the bug rather than
* silently continuing, but I'm too lazy */
if (waittime > 0.1)
waittime = 0.1;
/* Since we've exceeded the speed limit, we should reduce the
* batch size slightly. We don't do it only by a little bit to
* avoid over-correcting. We want to converge on the correct
* speed gradually. Note that since this happens hundreds or
* thousands of times a second, the convergence is very fast
* even with 0.1% adjustment */
throttler->batch_size *= 0.999;
/* Now we wait for a bit */
pixie_usleep((uint64_t)(waittime * 1000000.0));
/* There are two choices here. We could either return immediately,
* or we can loop around again. Right now, the code loops around
* again in order to support very slow rates, such as 0.5 packets
* per second. Nobody would want to run a scanner that slowly of
* course, but it's great for testing */
//return (uint64_t)throttler->batch_size;
goto again;
}
/*
* Calculate how many packets are needed to catch up again to the current
* rate, and return that.
*
* NOTE: this is almost always going to have the value of 1 (one). Only at
* very high speeds (above 100,000 packets/second) will this value get
* larger.
*/
throttler->batch_size *= 1.005;
if (throttler->batch_size > 10000)
throttler->batch_size = 10000;
throttler->current_rate = current_rate;
throttler->test_timestamp = timestamp;
throttler->test_packet_count = packet_count;
return (uint64_t)throttler->batch_size;
}

27
src/main-throttle.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef MAIN_THROTTLE_H
#define MAIN_THROTTLE_H
#include <stdint.h>
struct Throttler
{
double max_rate;
double current_rate;
double batch_size;
unsigned index;
struct {
uint64_t timestamp;
uint64_t packet_count;
} buckets[256];
uint64_t test_timestamp;
uint64_t test_packet_count;
};
uint64_t throttler_next_batch(struct Throttler *throttler, uint64_t count);
void throttler_start(struct Throttler *status, double max_rate);
#endif

1868
src/main.c Normal file

File diff suppressed because it is too large Load Diff

145
src/masscan-app.c Normal file
View File

@ -0,0 +1,145 @@
#include "masscan-app.h"
#include "util-safefunc.h"
/******************************************************************************
* When outputting results, we call this function to print out the type of
* banner that we've collected
******************************************************************************/
const char *
masscan_app_to_string(enum ApplicationProtocol proto)
{
static char tmp[64];
switch (proto) {
case PROTO_NONE: return "unknown";
case PROTO_HEUR: return "unknown";
case PROTO_SSH1: return "ssh";
case PROTO_SSH2: return "ssh";
case PROTO_HTTP: return "http";
case PROTO_FTP: return "ftp";
case PROTO_DNS_VERSIONBIND: return "dns-ver";
case PROTO_SNMP: return "snmp";
case PROTO_NBTSTAT: return "nbtstat";
case PROTO_SSL3: return "ssl";
case PROTO_SMB: return "smb";
case PROTO_SMTP: return "smtp";
case PROTO_POP3: return "pop";
case PROTO_IMAP4: return "imap";
case PROTO_UDP_ZEROACCESS: return "zeroaccess";
case PROTO_X509_CERT: return "X509";
case PROTO_X509_CACERT: return "X509CA";
case PROTO_HTML_TITLE: return "title";
case PROTO_HTML_FULL: return "html";
case PROTO_NTP: return "ntp";
case PROTO_VULN: return "vuln";
case PROTO_HEARTBLEED: return "heartbleed";
case PROTO_TICKETBLEED: return "ticketbleed";
case PROTO_VNC_OLD: return "vnc";
case PROTO_SAFE: return "safe";
case PROTO_MEMCACHED: return "memcached";
case PROTO_SCRIPTING: return "scripting";
case PROTO_VERSIONING: return "versioning";
case PROTO_COAP: return "coap";
case PROTO_TELNET: return "telnet";
case PROTO_RDP: return "rdp";
case PROTO_HTTP_SERVER: return "http.server";
case PROTO_MC: return "minecraft";
case PROTO_VNC_RFB: return "vnc";
case PROTO_VNC_INFO: return "vnc-info";
case PROTO_ISAKMP: return "isakmp";
case PROTO_ERROR: return "error";
default:
snprintf(tmp, sizeof(tmp), "(%u)", proto);
return tmp;
}
}
/******************************************************************************
******************************************************************************/
enum ApplicationProtocol
masscan_string_to_app(const char *str)
{
const static struct {
const char *name;
enum ApplicationProtocol value;
} list[] = {
{"ssh1", PROTO_SSH1},
{"ssh2", PROTO_SSH2},
{"ssh", PROTO_SSH2},
{"http", PROTO_HTTP},
{"ftp", PROTO_FTP},
{"dns-ver", PROTO_DNS_VERSIONBIND},
{"snmp", PROTO_SNMP},
{"nbtstat", PROTO_NBTSTAT},
{"ssl", PROTO_SSL3},
{"smtp", PROTO_SMTP},
{"smb", PROTO_SMB},
{"pop", PROTO_POP3},
{"imap", PROTO_IMAP4},
{"x509", PROTO_X509_CERT},
{"x509ca", PROTO_X509_CACERT},
{"zeroaccess", PROTO_UDP_ZEROACCESS},
{"title", PROTO_HTML_TITLE},
{"html", PROTO_HTML_FULL},
{"ntp", PROTO_NTP},
{"vuln", PROTO_VULN},
{"heartbleed", PROTO_HEARTBLEED},
{"ticketbleed", PROTO_TICKETBLEED},
{"vnc-old", PROTO_VNC_OLD},
{"safe", PROTO_SAFE},
{"memcached", PROTO_MEMCACHED},
{"scripting", PROTO_SCRIPTING},
{"versioning", PROTO_VERSIONING},
{"coap", PROTO_COAP},
{"telnet", PROTO_TELNET},
{"rdp", PROTO_RDP},
{"http.server", PROTO_HTTP_SERVER},
{"minecraft", PROTO_MC},
{"vnc", PROTO_VNC_RFB},
{"vnc-info", PROTO_VNC_INFO},
{"isakmp", PROTO_ISAKMP},
{0,0}
};
size_t i;
for (i=0; list[i].name; i++) {
if (strcmp(str, list[i].name) == 0)
return list[i].value;
}
return 0;
}
int
masscan_app_selftest(void) {
static const struct {
unsigned enumid;
unsigned expected;
} tests[] = {
{PROTO_SNMP, 7},
{PROTO_X509_CERT, 15},
{PROTO_HTTP_SERVER, 31},
{0,0}
};
size_t i;
/* The ENUM contains fixed values in external files,
* so programmers should only add onto its end, not
* the middle. This self-test will verify that
* a programmer hasn't made this mistake.
*/
for (i=0; tests[i].enumid != 0; i++) {
unsigned enumid = tests[i].enumid;
unsigned expected = tests[i].expected;
/* YOU ADDED AN ENUM IN THE MIDDLE INSTEAD ON THE END OF THE LIST */
if (enumid != expected) {
fprintf(stderr, "[-] %s:%u fail\n", __FILE__, (unsigned)__LINE__);
fprintf(stderr, "[-] enum expected=%u, found=%u\n", 30, PROTO_HTTP_SERVER);
return 1;
}
}
return 0;
}

60
src/masscan-app.h Normal file
View File

@ -0,0 +1,60 @@
#ifndef MASSCAN_APP_H
#define MASSCAN_APP_H
/*
* WARNING: these constants are used in files, so don't change the values.
* Add new ones onto the end
*/
enum ApplicationProtocol {
PROTO_NONE,
PROTO_HEUR,
PROTO_SSH1,
PROTO_SSH2,
PROTO_HTTP,
PROTO_FTP,
PROTO_DNS_VERSIONBIND,
PROTO_SNMP, /* 7 - simple network management protocol, udp/161 */
PROTO_NBTSTAT, /* 8 - netbios, udp/137 */
PROTO_SSL3,
PROTO_SMB, /* 10 - SMB tcp/139 and tcp/445 */
PROTO_SMTP, /* 11 - transfering email */
PROTO_POP3, /* 12 - fetching email */
PROTO_IMAP4, /* 13 - fetching email */
PROTO_UDP_ZEROACCESS,
PROTO_X509_CERT, /* 15 - just the cert */
PROTO_X509_CACERT,
PROTO_HTML_TITLE,
PROTO_HTML_FULL,
PROTO_NTP, /* 19 - network time protocol, udp/123 */
PROTO_VULN,
PROTO_HEARTBLEED,
PROTO_TICKETBLEED,
PROTO_VNC_OLD,
PROTO_SAFE,
PROTO_MEMCACHED, /* 25 - memcached */
PROTO_SCRIPTING,
PROTO_VERSIONING,
PROTO_COAP, /* 28 - constrained app proto, udp/5683, RFC7252 */
PROTO_TELNET, /* 29 - ye old remote terminal */
PROTO_RDP, /* 30 - Microsoft Remote Desktop Protocol tcp/3389 */
PROTO_HTTP_SERVER, /* 31 - HTTP "Server:" field */
PROTO_MC, /* 32 - Minecraft server */
PROTO_VNC_RFB,
PROTO_VNC_INFO,
PROTO_ISAKMP, /* 35 - IPsec key exchange */
PROTO_ERROR,
PROTO_end_of_list /* must be last one */
};
const char *
masscan_app_to_string(enum ApplicationProtocol proto);
enum ApplicationProtocol
masscan_string_to_app(const char *str);
int
masscan_app_selftest(void);
#endif

29
src/masscan-status.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef MASSCAN_STATUS_H
#define MASSCAN_STATUS_H
#if 0
enum PortStatus {
Port_Unknown,
Port_Open,
Port_Closed,
Port_IcmpEchoResponse,
Port_UdpOpen,
Port_UdpClosed,
Port_SctpOpen,
Port_SctpClosed,
Port_ArpOpen,
};
#endif
enum PortStatus {
PortStatus_Unknown,
PortStatus_Open,
PortStatus_Closed,
PortStatus_Arp,
PortStatus_Count
};
#endif

6
src/masscan-version.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef MASSCAN_VERSION
#define MASSCAN_VERSION "1.3.9-integration"
#endif

554
src/masscan.h Normal file
View File

@ -0,0 +1,554 @@
#ifndef MASSCAN_H
#define MASSCAN_H
#include "massip-addr.h"
#include "util-safefunc.h"
#include "stack-src.h"
#include "massip.h"
#include "util-bool.h"
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include "massip.h"
#include "stack-queue.h"
struct Adapter;
struct TemplateSet;
struct Banner1;
struct TemplateOptions;
/**
* This is the "operation" to be performed by masscan, which is almost always
* to "scan" the network. However, there are some lesser operations to do
* instead, like run a "regression self test", or "debug", or something else
* instead of scanning. We parse the command-line in order to figure out the
* proper operation
*/
enum Operation {
Operation_Default = 0, /* nothing specified, so print usage */
Operation_List_Adapters = 1, /* --listif */
Operation_Selftest = 2, /* --selftest or --regress */
Operation_Scan = 3, /* this is what you expect */
Operation_DebugIF = 4, /* --debug if */
Operation_ListScan = 5, /* -sL */
Operation_ReadScan = 6, /* --readscan <binary-output> */
Operation_ReadRange = 7, /* --readrange */
Operation_Benchmark = 8, /* --benchmark */
Operation_Echo = 9, /* --echo */
Operation_EchoAll = 10, /* --echo-all */
Operation_EchoCidr = 11, /* --echo-cidr */
};
/**
* The format of the output. If nothing is specified, then the default will
* be "--interactive", meaning that we'll print to the command-line live as
* results come in. Only one output format can be specified, except that
* "--interactive" can be specified alongside any of the other ones.
*/
enum OutputFormat {
Output_Default = 0x0000,
Output_Interactive = 0x0001, /* --interactive, print to cmdline */
Output_List = 0x0002,
Output_Binary = 0x0004, /* -oB, "binary", the primary format */
Output_XML = 0x0008, /* -oX, "xml" */
Output_JSON = 0x0010, /* -oJ, "json" */
Output_NDJSON = 0x0011, /* -oD, "ndjson" */
Output_Nmap = 0x0020,
Output_ScriptKiddie = 0x0040,
Output_Grepable = 0x0080, /* -oG, "grepable" */
Output_Redis = 0x0100,
Output_Unicornscan = 0x0200, /* -oU, "unicornscan" */
Output_None = 0x0400,
Output_Certs = 0x0800,
Output_Hostonly = 0x1000, /* -oH, "hostonly" */
Output_All = 0xFFBF, /* not supported */
};
/**
* Holds the list of TCP "hello" payloads, specified with the "--hello-file"
* or "--hello-string" options
*/
struct TcpCfgPayloads
{
/** The "hello" data in base64 format. This is either the base64 string
* specified in the cmdline/cfgfile with "--hello-string", or the
* contents of a file specified with "--hello-file" that we've converted
* into base64 */
char *payload_base64;
/** The TCP port that this hello belongs to */
unsigned port;
/** These configuration options are stored as a linked-list */
struct TcpCfgPayloads *next;
};
/**
* This is the master MASSCAN configuration structure. It is created on startup
* by reading the command-line and parsing configuration files.
*
* Once read in at the start, this structure doesn't change. The transmit
* and receive threads have only a "const" pointer to this structure.
*/
struct Masscan
{
/**
* What this program is doing, which is normally "Operation_Scan", but
* which can be other things, like "Operation_SelfTest"
*/
enum Operation op;
struct {
unsigned tcp:1;
unsigned udp:1; /* -sU */
unsigned sctp:1;
unsigned ping:1; /* --ping, ICMP echo */
unsigned arp:1; /* --arp, local ARP scan */
unsigned oproto:1; /* -sO */
} scan_type;
/**
* After scan type has been configured, add these ports. In other words,
* the user may specify `-sU` or `-sT` after the `--top-ports` parameter,
* so we have to wait until after parsing arguments to fill in the ports.
*/
unsigned top_ports;
/**
* Temporary file to echo parameters to, used for saving configuration
* to a file
*/
FILE *echo;
unsigned echo_all;
/**
* One or more network adapters that we'll use for scanning. Each adapter
* should have a separate set of IP source addresses, except in the case
* of PF_RING dnaX:Y adapters.
*/
struct {
char ifname[256];
struct Adapter *adapter;
struct stack_src_t src;
macaddress_t source_mac;
macaddress_t router_mac_ipv4;
macaddress_t router_mac_ipv6;
ipv4address_t router_ip;
int link_type; /* libpcap definitions */
unsigned char my_mac_count; /*is there a MAC address? */
unsigned vlan_id;
unsigned is_vlan:1;
unsigned is_usable:1;
} nic[8];
unsigned nic_count;
/**
* The target ranges of IPv4 addresses that are included in the scan.
* The user can specify anything here, and we'll resolve all overlaps
* and such, and sort the target ranges.
*/
struct MassIP targets;
/**
* IPv4 addresses/ranges that are to be excluded from the scan. This takes
* precedence over any 'include' statement. What happens is this: after
* all the configuration has been read, we then apply the exclude/blacklist
* on top of the target/whitelist, leaving only a target/whitelist left.
* Thus, during the scan, we only choose from the target/whitelist and
* don't consult the exclude/blacklist.
*/
struct MassIP exclude;
/**
* Only output these types of banners
*/
struct RangeList banner_types;
/**
* Maximum rate, in packets-per-second (--rate parameter). This can be
* a fraction of a packet-per-second, or be as high as 30000000.0 (or
* more actually, but I've only tested to 30megapps).
*/
double max_rate;
/**
* Number of retries (--retries or --max-retries parameter). Retries
* happen a few seconds apart.
*/
unsigned retries;
unsigned is_pfring:1; /* --pfring */
unsigned is_sendq:1; /* --sendq */
unsigned is_banners:1; /* --banners */
unsigned is_banners_rawudp:1; /* --rawudp */
unsigned is_offline:1; /* --offline */
unsigned is_noreset:1; /* --noreset, don't transmit RST */
unsigned is_gmt:1; /* --gmt, all times in GMT */
unsigned is_capture_cert:1; /* --capture cert */
unsigned is_capture_html:1; /* --capture html */
unsigned is_capture_heartbleed:1; /* --capture heartbleed */
unsigned is_capture_ticketbleed:1; /* --capture ticket */
unsigned is_test_csv:1; /* (temporary testing feature) */
unsigned is_infinite:1; /* -infinite */
unsigned is_readscan:1; /* --readscan, Operation_Readscan */
unsigned is_heartbleed:1; /* --heartbleed, scan for this vuln */
unsigned is_ticketbleed:1; /* --ticketbleed, scan for this vuln */
unsigned is_poodle_sslv3:1; /* --vuln poodle, scan for this vuln */
unsigned is_hello_ssl:1; /* --ssl, use SSL HELLO on all ports */
unsigned is_hello_smbv1:1; /* --smbv1, use SMBv1 hello, instead of v1/v2 hello */
unsigned is_hello_http:1; /* --hello=http, use HTTP on all ports */
unsigned is_scripting:1; /* whether scripting is needed */
unsigned is_capture_servername:1; /* --capture servername */
/** Packet template options, such as whether we should add a TCP MSS
* value, or remove it from the packet */
struct TemplateOptions *templ_opts; /* e.g. --tcpmss */
/**
* Wait forever for responses, instead of the default 10 seconds
*/
unsigned wait;
/**
* --resume
* This structure contains options for pausing the scan (by exiting the
* program) and restarting it later.
*/
struct {
/** --resume-index */
uint64_t index;
/** --resume-count */
uint64_t count;
/** Derives the --resume-index from the target ip:port */
struct {
unsigned ip;
unsigned port;
} target;
} resume;
/**
* --shard n/m
* This is used for distributing a scan across multiple "shards". Every
* shard in the scan must know the total number of shards, and must also
* know which of those shards is it's identity. Thus, shard 1/5 scans
* a different range than 2/5. These numbers start at 1, so it's
* 1/3 (#1 out of three), 2/3, and 3/3 (but not 0/3).
*/
struct {
unsigned one;
unsigned of;
} shard;
/**
* The packet template set we are current using. We store a binary template
* for TCP, UDP, SCTP, ICMP, and so on. All the scans using that protocol
* are then scanned using that basic template. IP and TCP options can be
* added to the basic template without affecting any other component
* of the system.
*/
struct TemplateSet *pkt_template;
/**
* A random seed for randomization if zero, otherwise we'll use
* the configured seed for repeatable tests.
*/
uint64_t seed;
/**
* This block configures what we do for the output files
*/
struct OutputStuff {
/**
* --output-format
* Examples are "xml", "binary", "json", "ndjson", "grepable", and so on.
*/
enum OutputFormat format;
/**
* --output-filename
* The name of the file where we are storing scan results.
* Note: the filename "-" means that we should send the file to
* <stdout> rather than to a file.
*/
char filename[256];
/**
* A feature of the XML output where we can insert an optional
* stylesheet into the file for better rendering on web browsers
*/
char stylesheet[256];
/**
* --append
* We should append to the output file rather than overwriting it.
*/
unsigned is_append:1;
/**
* --json-status
* Print each status update line to stderr as JSON ending with a newline
*
* This only applies to the three types of status lines that are printed
* in status_print(); it does *not* apply to things like startup messages,
* error messages or discovery of individual ports
*
*/
bool is_status_ndjson;
/**
* --open
* --open-only
* --show open
* Whether to show open ports
*/
unsigned is_show_open:1;
/**
* --show closed
* Whether to show closed ports (i.e. RSTs)
*/
unsigned is_show_closed:1;
/**
* --show host
* Whether to show host messages other than closed ports
*/
unsigned is_show_host:1;
/**
* print reason port is open, which is redundant for us
*/
unsigned is_reason:1;
/**
* --interactive
* Print to command-line while also writing to output file. This isn't
* needed if the output format is already 'interactive' (the default),
* but only if the default output format is anything else, and the
* user also wants interactivity.
*/
unsigned is_interactive:1;
/**
* Print state updates
*/
unsigned is_status_updates:1;
struct {
/**
* When we should rotate output into the target directory
*/
unsigned timeout;
/**
* When doing "--rotate daily", the rotation is done at GMT. In
* order to fix this, add an offset.
*/
unsigned offset;
/**
* Instead of rotating by timeout, we can rotate by filesize
*/
uint64_t filesize;
/**
* The directory to which we store rotated files
*/
char directory[256];
} rotate;
} output;
struct {
unsigned data_length; /* number of bytes to randomly append */
unsigned ttl; /* starting IP TTL field */
unsigned badsum; /* bad TCP/UDP/SCTP checksum */
unsigned packet_trace:1; /* print transmit messages */
char datadir[256];
} nmap;
char pcap_filename[256];
struct {
unsigned timeout;
} tcb;
struct {
char *pcap_payloads_filename;
char *nmap_payloads_filename;
char *nmap_service_probes_filename;
struct PayloadsUDP *udp;
struct PayloadsUDP *oproto;
struct TcpCfgPayloads *tcp;
struct NmapServiceProbeList *probes;
} payloads;
/** Reconfigure the HTTP header */
struct {
/* Method */
unsigned char *method;
size_t method_length;
/* URL */
unsigned char *url;
size_t url_length;
/* Version */
unsigned char *version;
size_t version_length;
/* Host */
unsigned char *host;
size_t host_length;
/* User-Agent */
unsigned char *user_agent;
size_t user_agent_length;
/* Payload after the header*/
unsigned char *payload;
size_t payload_length;
/* Headers */
struct {
const char *name;
unsigned char *value;
size_t value_length;
} headers[16];
size_t headers_count;
/* Cookies */
struct {
unsigned char *value;
size_t value_length;
} cookies[16];
size_t cookies_count;
/* Remove */
struct {
unsigned char *name;
} remove[16];
size_t remove_count;
} http;
unsigned tcp_connection_timeout;
/** Number of seconds to wait for a 'hello' from the server before
* giving up and sending a 'hello' from the client. Should be a small
* value when doing scans that expect client-side hellos, like HTTP or
* SSL, but should be a longer value when doing scans that expect server
* hellos, such as FTP or VNC */
unsigned tcp_hello_timeout;
char *bpf_filter;
struct {
ipaddress ip;
char *password;
unsigned port;
} redis;
/**
* --min-packet
*/
unsigned min_packet_size;
/**
* Number of rounds for randomization
* --blackrock-rounds
*/
unsigned blackrock_rounds;
/**
* --script <name>
*/
struct {
/* The name (filename) of the script to run */
char *name;
/* The script VM */
struct lua_State *L;
} scripting;
/**
* --vuln <name>
* The name of a vuln to check, like "poodle"
*/
const char *vuln_name;
};
int mainconf_selftest(void);
void masscan_read_config_file(struct Masscan *masscan, const char *filename);
void masscan_command_line(struct Masscan *masscan, int argc, char *argv[]);
void masscan_usage(void);
void masscan_save_state(struct Masscan *masscan);
void main_listscan(struct Masscan *masscan);
/**
* Load databases, such as:
* - nmap-payloads
* - nmap-service-probes
* - pcap-payloads
*/
void masscan_load_database_files(struct Masscan *masscan);
/**
* Pre-scan the command-line looking for options that may affect how
* previous options are handled. This is a bit of a kludge, really.
*/
int masscan_conf_contains(const char *x, int argc, char **argv);
/**
* Called to set a <name=value> pair.
*/
void
masscan_set_parameter(struct Masscan *masscan,
const char *name, const char *value);
/**
* Discover the local network adapter parameters, such as which
* MAC address we are using and the MAC addresses of the
* local routers.
*/
int
masscan_initialize_adapter(
struct Masscan *masscan,
unsigned index,
macaddress_t *source_mac,
macaddress_t *router_mac_ipv4,
macaddress_t *router_mac_ipv6);
/**
* Echoes the settings to the command-line. By default, echoes only
* non-default values. With "echo-all", everything is echoed.
*/
void
masscan_echo(struct Masscan *masscan, FILE *fp, unsigned is_echo_all);
/**
* Echoes the list of CIDR ranges to scan.
*/
void
masscan_echo_cidr(struct Masscan *masscan, FILE *fp, unsigned is_echo_all);
#endif

322
src/massip-addr.c Normal file
View File

@ -0,0 +1,322 @@
#include "massip-addr.h"
#include <string.h>
/**
* Holds the output string, so that we can append to it without
* overflowing buffers. The _append_xxx() functions below append
* to this string.
*/
typedef struct stream_t {
char *buf;
size_t offset;
size_t length;
} stream_t;
/**
* Append a character to the output string. All the other _append_xxx()
* functions call this one, so this is the only one where a
* buffer-overflow can occur.
*/
static void
_append_char(stream_t *out, char c)
{
if (out->offset < out->length)
out->buf[out->offset++] = c;
/* keep the string nul terminated as we build it */
if (out->offset < out->length)
out->buf[out->offset] = '\0';
}
static void
_append_ipv6(stream_t *out, const unsigned char *ipv6)
{
static const char hex[] = "0123456789abcdef";
size_t i;
int is_ellision = 0;
/* An IPv6 address is printed as a series of 2-byte hex words
* separated by colons :, for a total of 16-bytes */
for (i = 0; i < 16; i += 2) {
unsigned n = ipv6[i] << 8 | ipv6[i + 1];
/* Handle the ellision case. A series of words with a value
* of 0 can be removed completely, replaced by an extra colon */
if (n == 0 && !is_ellision) {
is_ellision = 1;
while (i < 13 && ipv6[i + 2] == 0 && ipv6[i + 3] == 0)
i += 2;
_append_char(out, ':');
/* test for all-zero address, in which case the output
* will be "::". */
while (i == 14 && ipv6[i] == 0 && ipv6[i + 1] == 0){
i=16;
_append_char(out, ':');
}
continue;
}
/* Print the colon between numbers. Fence-post alert: only colons
* between numbers are printed, not at the beginning or end of the
* string */
if (i)
_append_char(out, ':');
/* Print the digits. Leading zeroes are not printed */
if (n >> 12)
_append_char(out, hex[(n >> 12) & 0xF]);
if (n >> 8)
_append_char(out, hex[(n >> 8) & 0xF]);
if (n >> 4)
_append_char(out, hex[(n >> 4) & 0xF]);
_append_char(out, hex[(n >> 0) & 0xF]);
}
}
struct ipaddress_formatted ipv6address_fmt(ipv6address a)
{
struct ipaddress_formatted out;
unsigned char tmp[16];
size_t i;
stream_t s;
/*
* Convert address into a sequence of bytes. Our code
* here represents an IPv6 address as two 64-bit numbers, but
* the formatting code above that we copied from a different
* project represents it as an array of bytes.
*/
for (i=0; i<16; i++) {
uint64_t x;
if (i<8)
x = a.hi;
else
x = a.lo;
x >>= (7 - (i%8)) * 8;
tmp[i] = (unsigned char)(x & 0xFF);
}
/* Call the formatting function */
s.buf = out.string;
s.offset = 0;
s.length = sizeof(out.string);
_append_ipv6(&s, tmp);
return out;
}
/**
* Append a decimal integer.
*/
static void
_append_decimal(stream_t *out, unsigned long long n)
{
char tmp[64];
size_t tmp_offset = 0;
/* Create temporary string */
while (n >= 10) {
unsigned digit = n % 10;
n /= 10;
tmp[tmp_offset++] = (char)('0' + digit);
}
/* the final digit, may be zero */
tmp[tmp_offset++] = (char)('0' + n);
/* Copy the result backwards */
while (tmp_offset)
_append_char(out, tmp[--tmp_offset]);
}
static void
_append_hex2(stream_t *out, unsigned long long n)
{
static const char hex[17] = "0123456789abcdef";
_append_char(out, hex[(n>>4)&0xF]);
_append_char(out, hex[(n>>0)&0xF]);
}
struct ipaddress_formatted ipv4address_fmt(ipv4address ip)
{
struct ipaddress_formatted out;
stream_t s;
/* Call the formatting function */
s.buf = out.string;
s.offset = 0;
s.length = sizeof(out.string);
_append_decimal(&s, (ip >> 24) & 0xFF);
_append_char(&s, '.');
_append_decimal(&s, (ip >> 16) & 0xFF);
_append_char(&s, '.');
_append_decimal(&s, (ip >> 8) & 0xFF);
_append_char(&s, '.');
_append_decimal(&s, (ip >> 0) & 0xFF);
return out;
}
struct ipaddress_formatted macaddress_fmt(macaddress_t mac)
{
struct ipaddress_formatted out;
stream_t s;
/* Call the formatting function */
s.buf = out.string;
s.offset = 0;
s.length = sizeof(out.string);
_append_hex2(&s, mac.addr[0]);
_append_char(&s, '-');
_append_hex2(&s, mac.addr[1]);
_append_char(&s, '-');
_append_hex2(&s, mac.addr[2]);
_append_char(&s, '-');
_append_hex2(&s, mac.addr[3]);
_append_char(&s, '-');
_append_hex2(&s, mac.addr[4]);
_append_char(&s, '-');
_append_hex2(&s, mac.addr[5]);
return out;
}
struct ipaddress_formatted ipaddress_fmt(ipaddress a)
{
struct ipaddress_formatted out;
stream_t s;
ipv4address ip = a.ipv4;
if (a.version == 6) {
return ipv6address_fmt(a.ipv6);
}
/* Call the formatting function */
s.buf = out.string;
s.offset = 0;
s.length = sizeof(out.string);
_append_decimal(&s, (ip >> 24) & 0xFF);
_append_char(&s, '.');
_append_decimal(&s, (ip >> 16) & 0xFF);
_append_char(&s, '.');
_append_decimal(&s, (ip >> 8) & 0xFF);
_append_char(&s, '.');
_append_decimal(&s, (ip >> 0) & 0xFF);
return out;
}
static unsigned _count_long(uint64_t number)
{
unsigned i;
unsigned count = 0;
for (i=0; i<64; i++) {
if ((number >> i) & 1)
count = i + 1;
}
return count;
}
/**
* Find the number of bits needed to hold the integer. In other words,
* the number 0x64 would need 7 bits to store it.
*
* We use this to count the size of scans. We currently only support
* scan sizes up to 63 bits.
*/
unsigned massint128_bitcount(massint128_t number)
{
if (number.hi)
return _count_long(number.hi) + 64;
else
return _count_long(number.lo);
}
ipv6address_t ipv6address_add_uint64(ipv6address_t lhs, uint64_t rhs) {
lhs.lo += rhs;
if (lhs.lo < rhs) {
lhs.hi += 1;
}
return lhs;
}
ipv6address_t ipv6address_subtract(ipv6address_t lhs, ipv6address_t rhs) {
ipv6address_t difference;
difference.hi = lhs.hi - rhs.hi;
difference.lo = lhs.lo - rhs.lo;
/* check for underflow */
if (difference.lo > lhs.lo)
difference.hi -= 1;
return difference;
}
ipv6address_t ipv6address_add(ipv6address_t lhs, ipv6address_t rhs) {
ipv6address_t sum;
sum.hi = lhs.hi + rhs.hi;
sum.lo = lhs.lo - rhs.lo;
/* check for underflow */
if (sum.lo > lhs.lo)
sum.hi += 1;
return sum;
}
int ipv6address_selftest(void)
{
int x = 0;
ipaddress ip;
struct ipaddress_formatted fmt;
ip.version = 4;
ip.ipv4 = 0x01FF00A3;
fmt = ipaddress_fmt(ip);
if (strcmp(fmt.string, "1.255.0.163") != 0)
x++;
return x;
}
int ipv6address_is_equal_prefixed(ipv6address_t lhs, ipv6address_t rhs, unsigned prefix)
{
ipv6address mask;
/* If the prefix is bad, then the answer is 'no'. */
if (prefix > 128) {
return 0;
}
/* Create the mask from the prefix */
if (prefix > 64)
mask.hi = ~0ULL;
else if (prefix == 0)
mask.hi = 0;
else
mask.hi = ~0ULL << (64 - prefix);
if (prefix > 64)
mask.lo = ~0ULL << (128 - prefix);
else
mask.lo = 0;
/* Mask off any non-zero bits from both addresses */
lhs.hi &= mask.hi;
lhs.lo &= mask.lo;
rhs.hi &= mask.hi;
rhs.lo &= mask.lo;
/* Now do a normal compare */
return ipv6address_is_equal(lhs, rhs);
}

198
src/massip-addr.h Normal file
View File

@ -0,0 +1,198 @@
/*
Simple module for handling addresses (IPv6, IPv4, MAC).
Also implements a 128-bit type for dealing with addresses.
This is the module that almost all the other code depends
upon, because everything else deals with the IP address
types defined here.
*/
#ifndef MASSIP_ADDR_H
#define MASSIP_ADDR_H
#include <stdint.h>
#include <stddef.h>
#if defined(_MSC_VER) && !defined(inline)
#define inline __inline
#endif
#if defined(_MSC_VER)
#pragma warning(disable: 4201)
#endif
/**
* An IPv6 address is represented as two 64-bit integers instead of a single
* 128-bit integer. This is because currently (year 2020) most compilers
* do not support the `uint128_t` type, but all relevant ones do support
* the `uint64_t` type.
*/
struct ipv6address {uint64_t hi; uint64_t lo;};
typedef struct ipv6address ipv6address;
typedef struct ipv6address ipv6address_t;
/**
* IPv4 addresses are represented simply with an integer.
*/
typedef unsigned ipv4address;
typedef ipv4address ipv4address_t;
/**
* MAC address (layer 2). Since we have canonical types for IPv4/IPv6
* addresses, we may as well have a canonical type for MAC addresses,
* too.
*/
struct macaddress_t {unsigned char addr[6];};
typedef struct macaddress_t macaddress_t;
/**
* In many cases we need to do arithmetic on IPv6 addresses, treating
* them as a large 128-bit integer. Thus, we declare our own 128-bit
* integer type (and some accompanying math functions). But it's
* still just the same as a 128-bit integer.
*/
typedef ipv6address massint128_t;
/**
* Most of the code in this project is agnostic to the version of IP
* addresses (IPv4 or IPv6). Therefore, we represent them as a union
* distinguished by a version number. The `version` is an integer
* with a value of either 4 or 6.
*/
struct ipaddress {
union {
unsigned ipv4;
ipv6address ipv6;
};
unsigned char version;
};
typedef struct ipaddress ipaddress;
/** @return true if the IPv6 address is zero [::] */
static inline int ipv6address_is_zero(ipv6address_t a) {
return a.hi == 0 && a.lo == 0;
}
#define massint128_is_zero ipv6address_is_zero
/** The IPv6 address [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]
* is invalid */
static inline int ipv6address_is_invalid(ipv6address_t a) {
return a.hi == ~0ULL && a.lo == ~0ULL;
}
/** Compare two IPv6 addresses */
static inline int ipv6address_is_equal(ipv6address_t a, ipv6address_t b) {
return a.hi == b.hi && a.lo == b.lo;
}
static inline int ipaddress_is_equal(ipaddress a, ipaddress b) {
if (a.version != b.version)
return 0;
if (a.version == 4) {
return a.ipv4 == b.ipv4;
} else if (a.version == 6) {
return ipv6address_is_equal(a.ipv6, b.ipv6);
} else
return 0;
}
/** Compare two IPv6 addresses, to see which one comes frist. This is used
* in sorting the addresses
* @return true if a < b, false otherwise */
static inline int ipv6address_is_lessthan(ipv6address_t a, ipv6address_t b) {
return (a.hi == b.hi)?(a.lo < b.lo):(a.hi < b.hi);
}
/**
* Mask the lower bits of each address and test if the upper bits are equal
*/
int ipv6address_is_equal_prefixed(ipv6address_t lhs, ipv6address_t rhs, unsigned prefix);
ipv6address_t ipv6address_add_uint64(ipv6address_t lhs, uint64_t rhs);
ipv6address_t ipv6address_subtract(ipv6address_t lhs, ipv6address_t rhs);
ipv6address_t ipv6address_add(ipv6address_t lhs, ipv6address_t rhs);
/**
* Given a typical EXTERNAL representation of an IPv6 address, which is
* an array of 16 bytes, convert to the canonical INTERNAL address.
*/
static inline ipv6address ipv6address_from_bytes(const unsigned char *buf) {
ipv6address addr;
addr.hi = (uint64_t)buf[ 0] << 56
| (uint64_t)buf[ 1] << 48
| (uint64_t)buf[ 2] << 40
| (uint64_t)buf[ 3] << 32
| (uint64_t)buf[ 4] << 24
| (uint64_t)buf[ 5] << 16
| (uint64_t)buf[ 6] << 8
| (uint64_t)buf[ 7] << 0;
addr.lo = (uint64_t)buf[ 8] << 56
| (uint64_t)buf[ 9] << 48
| (uint64_t)buf[10] << 40
| (uint64_t)buf[11] << 32
| (uint64_t)buf[12] << 24
| (uint64_t)buf[13] << 16
| (uint64_t)buf[14] << 8
| (uint64_t)buf[15] << 0;
return addr;
}
/**
* Given a typical EXTERNAL representation of an Ethernet MAC address,
* which is an array of 6 bytes, convert to the canonical INTERNAL address.
*/
static inline macaddress_t macaddress_from_bytes(const void *vbuf)
{
const unsigned char *buf = (const unsigned char *)vbuf;
macaddress_t result;
result.addr[0] = buf[0];
result.addr[1] = buf[1];
result.addr[2] = buf[2];
result.addr[3] = buf[3];
result.addr[4] = buf[4];
result.addr[5] = buf[5];
return result;
}
/** Test if the Ethernet MAC address is all zeroes */
static inline int macaddress_is_zero(macaddress_t mac)
{
return mac.addr[0] == 0
&& mac.addr[1] == 0
&& mac.addr[2] == 0
&& mac.addr[3] == 0
&& mac.addr[4] == 0
&& mac.addr[5] == 0;
}
/** Compare two Ethernet MAC addresses to see if they are equal */
static inline int macaddress_is_equal(macaddress_t lhs, macaddress_t rhs)
{
return lhs.addr[0] == rhs.addr[0]
&& lhs.addr[1] == rhs.addr[1]
&& lhs.addr[2] == rhs.addr[2]
&& lhs.addr[3] == rhs.addr[3]
&& lhs.addr[4] == rhs.addr[4]
&& lhs.addr[5] == rhs.addr[5];
}
/**
* Return a buffer with the formatted address
*/
typedef struct ipaddress_formatted {
char string[48];
} ipaddress_formatted_t;
struct ipaddress_formatted ipv6address_fmt(ipv6address a);
struct ipaddress_formatted ipv4address_fmt(ipv4address a);
struct ipaddress_formatted ipaddress_fmt(ipaddress a);
struct ipaddress_formatted macaddress_fmt(macaddress_t a);
unsigned massint128_bitcount(massint128_t num);
/**
* @return 0 on success, 1 on failure
*/
int ipv6address_selftest(void);
#endif

1313
src/massip-parse.c Normal file

File diff suppressed because it is too large Load Diff

72
src/massip-parse.h Normal file
View File

@ -0,0 +1,72 @@
/*
massip-parse
This module parses IPv4 and IPv6 addresses.
It's not a typical parser. It's optimized around parsing large
files containing millions of addresses and ranges using a
"state-machine parser".
*/
#ifndef MASSIP_PARSE_H
#define MASSIP_PARSE_H
#include "massip-addr.h"
struct MassIP;
struct Range;
struct Range6;
/**
* Parse a file, extracting all the IPv4 and IPv6 addresses and ranges.
* This is optimized for speed, handling millions of entries in under
* a second. This is especially tuned for IPv6 addresses, as while IPv4
* scanning is mostly done with target rnages, IPv6 scanning is mostly
* done with huge lists of target addresses.
* @param filename
* The name of the file that we'll open, parse, and close.
* @param targets_ipv4
* The list of IPv4 targets that we append any IPv4 addresses to.
* @param targets_ipv6
* The list of IPv6 targets that we append any IPv6 addresses/ranges to.
* @return
0 on success, any other number on failure.
*/
int
massip_parse_file(struct MassIP *massip, const char *filename);
enum RangeParseResult {
Bad_Address,
Ipv4_Address=4,
Ipv6_Address=6,
};
/**
* Parse the next IPv4/IPv6 range from a string. This is called
* when parsing strings from the command-line.
*/
enum RangeParseResult
massip_parse_range(const char *line, size_t *inout_offset, size_t max, struct Range *ipv4, struct Range6 *ipv6);
/**
* Parse a single IPv6 address. This is called when working with
* the operating system stack, when querying addresses from
* the local network adapters.
*/
ipv6address_t
massip_parse_ipv6(const char *buf);
ipv4address_t
massip_parse_ipv4(const char *buf);
/**
* Do a simplistic unit test of the parser.
* @return 0 on success, 1 on failure
*/
int
massip_parse_selftest(void);
#endif

27
src/massip-port.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef MASSIP_PORT_H
#define MASSIP_PORT_H
/*
* Ports are 16-bit numbers ([0..65535], but different
* transports (TCP, UDP, SCTP) are distinct port ranges. Thus, we
* instead of three 64k ranges we could instead treat this internally
* as a 192k port range. We can expand this range to include other
* things we scan for, such as ICMP pings or ARP requests.
*/
enum {
Templ_TCP = 0,
Templ_TCP_last = 65535,
Templ_UDP = 65536,
Templ_UDP_last = 65536 + 65535,
Templ_SCTP = 65536*2,
Templ_SCTP_last = 65536*2 + 65535,
Templ_ICMP_echo = 65536*3+0,
Templ_ICMP_timestamp = 65536*3+1,
Templ_ARP = 65536*3+2,
Templ_Oproto_first = 65536*3 + 256,
Templ_Oproto_last = 65536*3 + 256 + 255,
Templ_VulnCheck = 65536*4,
};
#endif

1389
src/massip-rangesv4.c Normal file

File diff suppressed because it is too large Load Diff

252
src/massip-rangesv4.h Normal file
View File

@ -0,0 +1,252 @@
#ifndef RANGES_H
#define RANGES_H
#include <stdint.h>
#include <stdio.h>
#include "util-bool.h" /*<stdbool.h>*/
/**
* A range of either IP addresses or ports
*/
struct Range
{
unsigned begin;
unsigned end; /* inclusive, so [n..m] includes both 'n' and 'm' */
};
/**
* Find the first CIDR range (one that can be specified with a /prefix)
* inside the current range. If the current range can already be
* specified with a CIDR /prefix, then the entire range is returned.
* Examples:
* [10.0.0.0->10.0.0.255] returns [10.0.0.0->10.0.0.255] (no change)
* [10.0.0.1->10.0.0.255] returns [10.0.0.1->10.0.0.1]
* [10.0.0.2->10.0.0.255] returns [10.0.0.2->10.0.0.3]
* [10.0.0.4->10.0.0.255] returns [10.0.0.4->10.0.0.7]
* [10.0.0.248->10.0.0.254] returns [10.0.0.248->10.0.0.251]
* [10.0.0.252->10.0.0.254] returns [10.0.0.252->10.0.0.253]
* [10.0.0.254->10.0.0.254] returns [10.0.0.254->10.0.0.254]
* @param range
* A range specified by a starting IPv4 address and an ending
* IPv4 address, like [10.0.0.4->10.0.0.255].
* @param prefix_length
* An out-only parameter that receives the CIDR prefix length
* (number of bits) of the resulting range. This parameter is
* optional (may be NULL).
* @return the smaller range, and the number of prefix bits in the range.
*/
struct Range
range_first_cidr(const struct Range range, unsigned *prefix_length /*out*/);
/**
* Test if the range can instead be expressed using a CIDR /prefix.
* In other words, [10.0.0.0-10.0.0.255] can be expressed as [10.0.0.0/24].
* @param range to be tested
* @param prefix_length receivesoutput of the number of prefix bits if
* successful, otherwise set to 0xFFFFFFFF. This is optional, may
* be NULL and receive no output.
* @return True if the range can be expressed in CIDR notation, in
* which case `prefix_length` is set to the number of bits in the prefix
* for printing in that notation. False otherwise, in which case
* `prefix_length` is set to 0xFFFFFFFF (an invalid value).
*/
bool range_is_cidr(const struct Range range, unsigned *prefix_length /*out*/);
/**
* An array of ranges in sorted order
*/
struct RangeList
{
struct Range *list;
unsigned count;
unsigned max;
unsigned *picker;
unsigned is_sorted:1;
};
/**
* Adds the given range to the task list. The given range can be a duplicate
* or overlap with an existing range, which will get combined with existing
* ranges.
* @param task
* A list of ranges of either IPv4 addresses or port numbers.
* @param begin
* The first address of the range that'll be added.
* @param end
* The last address (inclusive) of the range that'll be added.
*/
void
rangelist_add_range(struct RangeList *task, unsigned begin, unsigned end);
void
rangelist_add_range_tcp(struct RangeList *targets, unsigned begin, unsigned end);
void
rangelist_add_range_udp(struct RangeList *targets, unsigned begin, unsigned end);
/**
* Returns 'true' is the indicated port or IP address is in one of the task
* ranges.
* @param task
* A list of ranges of either IPv4 addresses or port numbers.
* @param number
* Either an IPv4 address or a TCP/UDP port number.
* @return
* 'true' if the ranges contain the item, or 'false' otherwise
*/
int
rangelist_is_contains(const struct RangeList *task, unsigned number);
/**
* Returns 'true' if the indicate range is valid, which is simple the
* fact that 'begin' comes before 'end'. We mark invalid ranges
* by putting 'begin' after the 'end'
*/
int
range_is_valid(struct Range range);
/**
* Parses IPv4 addresses out of a string. A number of formats are allowed,
* either an individual IPv4 address, a CIDR spec, or a start/stop address.
* @param line
* A line of text, probably read from a configuration file, or a string
* probably input from the command line. It doesn't need to be nul
* terminated.
* @param inout_offset
* The offset into the line were we are parsing. This integer will be
* be incremented by the number of bytes we've parsed from the string.
* @param max
* The length of the line, in other words, the max value of inout_offset.
*/
struct Range
range_parse_ipv4(const char *line, unsigned *inout_offset, unsigned max);
/**
* Remove things from the target list. The primary use of this is the
* "exclude-file" containing a list of IP addresses that we should
* not scan
* @param targets
* Our array of target IP address (or port) ranges that we'll be
* scanning.
* @param excludes
* A list, probably read in from --excludefile, of things that we
* should not be scanning, that will override anything we otherwise
* try to scan.
*/
void
rangelist_exclude( struct RangeList *targets,
struct RangeList *excludes);
/**
* Counts the total number of IP addresses or ports in the target list. This
* iterates over all the ranges in the table, summing up the count within
* each range.
* @param targets
* A list of IP address or port ranges.
* @return
* The total number of address or ports.
*/
uint64_t
rangelist_count(const struct RangeList *targets);
/**
* Given an index in a continuous range of [0...count], pick a corresponding
* number (IP address or port) from a list of non-continuous ranges (not
* necessarily starting from 0). In other words, given the two ranges
* 10-19 50-69
* we'll have a total of 30 possible numbers. Thus, the index goes from
* [0..29], with the values 0..9 picking the corresponding values from the
* first range, and the values 10..29 picking the corresponding values
* from the second range.
*
* NOTE: This is a fundamental part of this program's design, that the user
* can specify non-contiguous IP and port ranges, but yet we iterate over
* them using a monotonically increasing index variable.
*
* @param targets
* A list of IP address ranges, or a list of port ranges (one or the
* other, but not both).
* @param index
* An integer starting at 0 up to (but not including) the value returned
* by 'rangelist_count()' for this target list.
* @return
* an IP address or port corresponding to this index.
*/
unsigned
rangelist_pick(const struct RangeList *targets, uint64_t i);
/**
* Given a string like "80,8080,20-25,U:161", parse it into a structure
* containing a list of port ranges.
*
* @param ports
* The array of port ranges that's produced by this parsing function.
* This structure will be used by the transmit thread when sending
* probes to a target IP address.
* @param string
* A string from either the command-line or configuration file
* in the nmap "ports" format.
* @param is_error
* Set to zero is no error occurred while parsing the string, or
* set to a non-zero value if an error was found.
* @return
* the pointer in the string where the parsing ended, so that additional
* things can be contained in the string, such as comments
*/
const char *
rangelist_parse_ports( struct RangeList *ports,
const char *string,
unsigned *is_error,
unsigned proto_offset
);
/**
* Remove all the ranges in the range list.
*/
void
rangelist_remove_all(struct RangeList *list);
/**
* Merge two range lists
*/
void
rangelist_merge(struct RangeList *list1, const struct RangeList *list2);
/**
* Optimizes the target list, so that when we call "rangelist_pick()"
* from an index, it runs faster. It currently configures this for
* a binary-search, though in the future some more efficient
* algorithm may be chosen.
*/
void
rangelist_optimize(struct RangeList *targets);
/**
* Sorts the list of target. We maintain the list of targets in sorted
* order internally even though we scan the targets in random order
* externally.
*/
void
rangelist_sort(struct RangeList *targets);
/**
* Does a regression test of this module
* @return
* 0 if the regression test succeeds, or a positive value on failure
*/
int
ranges_selftest(void);
#endif

721
src/massip-rangesv6.c Normal file
View File

@ -0,0 +1,721 @@
/*
for tracking IP/port ranges
*/
#include "massip-rangesv6.h"
#include "massip-rangesv4.h"
#include "util-malloc.h"
#include "util-logger.h"
#include "massip.h"
#include "massip-parse.h"
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define BUCKET_COUNT 16
#define REGRESS(i,x) if (!(x)) return (fprintf(stderr, "[-] %u: regression failed %s:%d\n", (unsigned)i, __FILE__, __LINE__)|1)
#ifndef false
#define false 0
#endif
#ifndef true
#define true 1
#endif
#define EQUAL(x,y) ipv6address_is_equal(x,y)
static inline ipv6address
_int128_add(ipv6address x, ipv6address y)
{
ipv6address result;
result.lo = x.lo + y.lo;
result.hi = x.hi + y.hi + (result.lo < x.lo);
return result;
}
static inline ipv6address
_int128_subtract(ipv6address x, ipv6address y)
{
ipv6address result;
result.lo = x.lo - y.lo;
result.hi = x.hi - y.hi - (result.lo > x.lo);
return result;
}
static ipv6address
_int128_add64(const ipv6address lhs, uint64_t rhs)
{
ipv6address result = lhs;
result.lo += rhs;
if (result.lo < lhs.lo)
result.hi++;
return result;
}
static inline massint128_t
_int128_mult64(massint128_t lhs, uint64_t rhs)
{
massint128_t result = {0,0};
uint64_t x;
uint64_t b;
uint64_t a;
/* low-order 32 */
a = (rhs>>0) & 0xFFFFFFFFULL;
b = (lhs.lo>>0) & 0xFFFFFFFFULL;
x = (a * b);
result.lo += x;
b = (lhs.lo>>32ULL) & 0xFFFFFFFFULL;
x = (a * b);
result.lo += x<<32ULL;
result.hi += x>>32ULL;
b = lhs.hi;
x = (a * b);
result.hi += x;
/* next 32 */
a = (rhs>>32ULL) & 0xFFFFFFFFULL;
b = (lhs.lo>>0ULL) & 0xFFFFFFFFULL;
x = (a * b);
result.lo += x<<32ULL;
result.hi += (x>>32ULL) + (result.lo < (x<<32ULL));
b = (lhs.lo>>32ULL) & 0xFFFFFFFFULL;
x = (a * b);
result.hi += x;
b = lhs.hi;
x = (a * b);
result.hi += x<<32ULL;
return result;
}
static int
LESS(const ipv6address lhs, const ipv6address rhs)
{
if (lhs.hi < rhs.hi)
return 1;
else if (lhs.hi == rhs.hi && lhs.lo < rhs.lo)
return 1;
else
return 0;
}
#define GREATEREQ(x,y) (!LESS(x,y))
static int
LESSEQ(const ipv6address lhs, const ipv6address rhs)
{
if (lhs.hi < rhs.hi)
return 1;
if (lhs.hi > rhs.hi)
return 0;
if (lhs.lo <= rhs.lo)
return 1;
else
return 0;
}
int range6_is_bad_address(const struct Range6 *range)
{
return LESS(range->end, range->begin);
}
static int
_int128_is_equals(const ipv6address lhs, const ipv6address rhs)
{
return lhs.hi == rhs.hi && lhs.lo == rhs.lo;
}
static ipv6address
MINUS_ONE(const ipv6address ip)
{
ipv6address result;
if (ip.lo == 0) {
result.hi = ip.hi - 1;
result.lo = ~0ULL;
} else {
result.hi = ip.hi;
result.lo = ip.lo - 1;
}
return result;
}
static ipv6address PLUS_ONE(const ipv6address ip)
{
ipv6address result;
if (ip.lo == ~0) {
result.hi = ip.hi + 1;
result.lo = 0;
} else {
result.hi = ip.hi;
result.lo = ip.lo + 1;
}
return result;
}
/***************************************************************************
***************************************************************************/
massint128_t
massip_range(struct MassIP *massip)
{
massint128_t result;
result = range6list_count(&massip->ipv6);
result = _int128_add64(result, rangelist_count(&massip->ipv4));
result = _int128_mult64(result, rangelist_count(&massip->ports));
return result;
}
/***************************************************************************
***************************************************************************/
int
range6list_is_contains(const struct Range6List *targets, const ipv6address ip)
{
unsigned i;
for (i=0; i<targets->count; i++) {
struct Range6 *range = &targets->list[i];
if (LESSEQ(range->begin, ip) && LESSEQ(ip, range->end))
return 1;
}
return 0;
}
/***************************************************************************
* ???
***************************************************************************/
static void
todo_remove_at(struct Range6List *targets, unsigned index)
{
memmove(&targets->list[index],
&targets->list[index+1],
(targets->count - index) * sizeof(targets->list[index])
);
targets->count--;
}
/***************************************************************************
* Test if two ranges overlap.
* This is easiest done by testing that they don't overlap, and inverting
* the result.
* Note that adjacent addresses overlap.
***************************************************************************/
static int
range6_is_overlap(const struct Range6 lhs, const struct Range6 rhs)
{
static const ipv6address FFFF = {~0ULL, ~0ULL};
if (LESS(lhs.begin, rhs.begin)) {
if (EQUAL(lhs.end, FFFF) || GREATEREQ(PLUS_ONE(lhs.end), rhs.begin))
return 1;
}
if (GREATEREQ(lhs.begin, rhs.begin)) {
if (LESSEQ(lhs.end, rhs.end))
return 1;
}
if (LESS(rhs.begin, lhs.begin)) {
if (EQUAL(rhs.end, FFFF) || GREATEREQ(PLUS_ONE(rhs.end), lhs.begin))
return 1;
}
if (GREATEREQ(rhs.begin, lhs.begin)) {
if (LESSEQ(rhs.end, lhs.end))
return 1;
}
return 0;
#if 0
static const ipv6address zero = {0, 0};
ipv6address lhs_endm = MINUS_ONE(lhs.end);
ipv6address rhs_endm = MINUS_ONE(rhs.end);
/* llll rrrr */
if (LESS(zero, lhs.end) && LESS(lhs_endm, rhs.begin))
return 0;
/* rrrr llll */
if (LESS(zero, rhs.end) && LESS(rhs_endm, lhs.begin))
return 0;
return 1;
#endif
}
/***************************************************************************
* Combine two ranges, such as when they overlap.
***************************************************************************/
static void
range6_combine(struct Range6 *lhs, const struct Range6 rhs)
{
if (LESSEQ(rhs.begin, lhs->begin))
lhs->begin = rhs.begin;
if (LESSEQ(lhs->end, rhs.end))
lhs->end = rhs.end;
}
/***************************************************************************
* Callback for qsort() for comparing two ranges
***************************************************************************/
static int
range6_compare(const void *lhs, const void *rhs)
{
struct Range6 *left = (struct Range6 *)lhs;
struct Range6 *right = (struct Range6 *)rhs;
if (ipv6address_is_equal(left->begin, right->begin))
return 0;
else if (LESS(left->begin, right->begin))
return -1;
else
return 1;
}
/***************************************************************************
***************************************************************************/
void
range6list_sort(struct Range6List *targets)
{
size_t i;
struct Range6List newlist = {0};
size_t original_count = targets->count;
/* Empty lists are, of course, sorted. We need to set this
* to avoid an error later on in the code which asserts that
* the lists are sorted */
if (targets->count == 0) {
targets->is_sorted = 1;
return;
}
/* If it's already sorted, then skip this */
if (targets->is_sorted) {
return;
}
/* First, sort the list */
LOG(3, "[+] range6:sort: sorting...\n");
qsort( targets->list, /* the array to sort */
targets->count, /* number of elements to sort */
sizeof(targets->list[0]), /* size of element */
range6_compare);
/* Second, combine all overlapping ranges. We do this by simply creating
* a new list from a sorted list, so we don't have to remove things in the
* middle when collapsing overlapping entries together, which is painfully
* slow. */
LOG(3, "[+] range:sort: combining...\n");
for (i=0; i<targets->count; i++) {
range6list_add_range(&newlist, targets->list[i].begin, targets->list[i].end);
}
LOG(3, "[+] range:sort: combined from %u elements to %u elements\n", original_count, newlist.count);
free(targets->list);
targets->list = newlist.list;
targets->count = newlist.count;
newlist.list = 0;
LOG(2, "[+] range:sort: done...\n");
targets->is_sorted = 1;
}
void
range6list_add_range(struct Range6List *targets, ipv6address begin, ipv6address end)
{
struct Range6 range;
range.begin = begin;
range.end = end;
/* auto-expand the list if necessary */
if (targets->count + 1 >= targets->max) {
targets->max = targets->max * 2 + 1;
targets->list = REALLOCARRAY(targets->list, targets->max, sizeof(targets->list[0]));
}
/* If empty list, then add this one */
if (targets->count == 0) {
targets->list[0] = range;
targets->count++;
targets->is_sorted = 1;
return;
}
/* If new range overlaps the last range in the list, then combine it
* rather than appending it. This is an optimization for the fact that
* we often read in sequential addresses */
if (range6_is_overlap(targets->list[targets->count - 1], range)) {
range6_combine(&targets->list[targets->count - 1], range);
targets->is_sorted = 0;
return;
}
/* append to the end of our list */
targets->list[targets->count] = range;
targets->count++;
targets->is_sorted = 0;
}
/***************************************************************************
***************************************************************************/
void
range6list_remove_all(struct Range6List *targets)
{
if (targets->list)
free(targets->list);
if (targets->picker)
free(targets->picker);
memset(targets, 0, sizeof(*targets));
}
/***************************************************************************
***************************************************************************/
void
range6list_merge(struct Range6List *list1, const struct Range6List *list2)
{
unsigned i;
for (i=0; i<list2->count; i++) {
range6list_add_range(list1, list2->list[i].begin, list2->list[i].end);
}
}
/***************************************************************************
***************************************************************************/
void
range6list_remove_range(struct Range6List *targets, const ipv6address begin, const ipv6address end)
{
unsigned i;
struct Range6 x;
x.begin = begin;
x.end = end;
/* See if the range overlaps any exist range already in the
* list */
for (i = 0; i < targets->count; i++) {
if (!range6_is_overlap(targets->list[i], x))
continue;
/* If the removal-range wholly covers the range, delete
* it completely */
if (LESSEQ(begin, targets->list[i].begin) && LESSEQ(targets->list[i].end, end)) {
todo_remove_at(targets, i);
i--;
continue;
}
/* If the removal-range bisects the target-rage, truncate
* the lower end and add a new high-end */
if (LESSEQ(targets->list[i].begin, begin) && LESSEQ(end, targets->list[i].end)) {
struct Range6 newrange;
newrange.begin = PLUS_ONE(end);
newrange.end = targets->list[i].end;
targets->list[i].end = MINUS_ONE(begin);
range6list_add_range(targets, newrange.begin, newrange.end);
i--;
continue;
}
/* If overlap on the lower side */
if (LESSEQ(targets->list[i].begin, end) && LESSEQ(end, targets->list[i].end)) {
targets->list[i].begin = PLUS_ONE(end);
}
/* If overlap on the upper side */
if (LESSEQ(targets->list[i].begin, begin) && LESSEQ(begin, targets->list[i].end)) {
targets->list[i].end = MINUS_ONE(begin);
}
}
}
void
range6list_remove_range2(struct Range6List *targets, struct Range6 range)
{
range6list_remove_range(targets, range.begin, range.end);
}
/***************************************************************************
***************************************************************************/
ipv6address
range6list_exclude( struct Range6List *targets,
const struct Range6List *excludes)
{
ipv6address count = {0,0};
unsigned i;
for (i=0; i<excludes->count; i++) {
struct Range6 range = excludes->list[i];
ipv6address x;
x = _int128_subtract(range.end, range.begin);
x = _int128_add64(x, 1);
count = _int128_add(count, x);
range6list_remove_range(targets, range.begin, range.end);
}
return count;
}
/***************************************************************************
***************************************************************************/
massint128_t
range6list_count(const struct Range6List *targets)
{
unsigned i;
ipv6address result = {0,0};
for (i=0; i<targets->count; i++) {
ipv6address x;
x = _int128_subtract(targets->list[i].end, targets->list[i].begin);
if (x.hi == ~0ULL && x.lo == ~0ULL)
return x; /* overflow */
x = _int128_add64(x, 1);
result = _int128_add(result, x);
}
return result;
}
/***************************************************************************
***************************************************************************/
ipv6address
range6list_pick(const struct Range6List *targets, uint64_t index)
{
size_t maxmax = targets->count;
size_t min = 0;
size_t max = targets->count;
size_t mid;
const size_t *picker = targets->picker;
if (picker == NULL) {
fprintf(stderr, "[-] ipv6 picker is null\n");
exit(1);
}
for (;;) {
mid = min + (max-min)/2;
if (index < picker[mid]) {
max = mid;
continue;
} if (index >= picker[mid]) {
if (mid + 1 == maxmax)
break;
else if (index < picker[mid+1])
break;
else
min = mid+1;
}
}
return _int128_add64(targets->list[mid].begin, (index - picker[mid]));
}
/***************************************************************************
* The normal "pick" function is a linear search, which is slow when there
* are a lot of ranges. Therefore, the "pick2" creates sort of binary
* search that'll be a lot faster. We choose "binary search" because
* it's the most cache-efficient, having the least overhead to fit within
* the cache.
***************************************************************************/
void
range6list_optimize(struct Range6List *targets)
{
size_t *picker;
size_t i;
ipv6address total = {0,0};
if (targets->count == 0)
return;
/* This technique only works when the targets are in
* ascending order */
if (!targets->is_sorted)
range6list_sort(targets);
if (targets->picker)
free(targets->picker);
picker = REALLOCARRAY(NULL, targets->count, sizeof(*picker));
for (i=0; i<targets->count; i++) {
ipv6address x;
picker[i] = (size_t)total.lo;
x = _int128_subtract(targets->list[i].end, targets->list[i].begin);
x = _int128_add64(x, 1);
total = _int128_add(total, x);
}
targets->picker = picker;
}
/***************************************************************************
* Provide my own rand() simply to avoid static-analysis warning me that
* 'rand()' is unrandom, when in fact we want the non-random properties of
* rand() for regression testing.
***************************************************************************/
static unsigned
r_rand(unsigned *seed)
{
static const unsigned a = 214013;
static const unsigned c = 2531011;
*seed = (*seed) * a + c;
return (*seed)>>16 & 0x7fff;
}
/***************************************************************************
***************************************************************************/
static int
regress_pick2()
{
unsigned i;
unsigned seed = 0;
/*
*/
for (i=0; i<65536; i++)
{
ipv6address a;
ipv6address b;
ipv6address c;
ipv6address d;
a.hi = r_rand(&seed);
a.lo = (unsigned long long)r_rand(&seed)<<49ULL;
b.hi = r_rand(&seed);
b.lo = 0x8765432100000000ULL;
c = _int128_add(a, b);
d = _int128_subtract(c, b);
if (!_int128_is_equals(a, d)) {
fprintf(stderr, "[-] %s:%d: test failed (%u)\n", __FILE__, __LINE__, (unsigned)i);
return 1;
}
}
/*
* Run 100 randomized regression tests
*/
for (i=3; i<100; i++) {
unsigned j;
unsigned num_targets;
ipv6address begin = {0};
ipv6address end = {0};
struct Range6List targets[1];
struct Range6List duplicate[1];
uint64_t range;
ipv6address x;
seed = i;
/* Create a new target list */
memset(targets, 0, sizeof(targets[0]));
/* fill the target list with random ranges */
num_targets = r_rand(&seed)%5 + 1;
for (j=0; j<num_targets; j++) {
begin.lo += r_rand(&seed)%10;
end.lo = begin.lo + r_rand(&seed)%10;
range6list_add_range(targets, begin, end);
}
/* Optimize for faster 'picking' addresses from an index */
range6list_optimize(targets);
/* Duplicate the targetlist using the picker */
memset(duplicate, 0, sizeof(duplicate[0]));
x = range6list_count(targets);
if (x.hi) {
fprintf(stderr, "[-] range6: range too big\n");
return 1;
}
range = x.lo;
for (j=0; j<range; j++) {
ipv6address addr;
addr = range6list_pick(targets, j);
range6list_add_range(duplicate, addr, addr);
}
/* at this point, the two range lists should be identical */
REGRESS(i, targets->count == duplicate->count);
REGRESS(i, memcmp(targets->list, duplicate->list, targets->count*sizeof(targets->list[0])) == 0);
range6list_remove_all(targets);
range6list_remove_all(duplicate);
}
return 0;
}
/***************************************************************************
* Called during "make regress" to run a regression test over this module.
***************************************************************************/
int
ranges6_selftest(void)
{
struct Range6 r;
struct Range6List targets[1];
int err;
REGRESS(0, regress_pick2() == 0);
memset(targets, 0, sizeof(targets[0]));
#define ERROR() fprintf(stderr, "selftest: failed %s:%u\n", __FILE__, __LINE__);
err = massip_parse_range("2001:0db8:85a3:0000:0000:8a2e:0370:7334", 0, 0, 0, &r);
if (err != Ipv6_Address)
ERROR();
/* test for the /0 CIDR block, since we'll be using that a lot to scan the entire
* Internet */
if (r.begin.hi != 0x20010db885a30000ULL)
return 1;
if (r.begin.lo != 0x00008a2e03707334ULL)
return 1;
return 0;
}

188
src/massip-rangesv6.h Normal file
View File

@ -0,0 +1,188 @@
/*
List of IPv6 ranges.
Sames as the "ranges.h" module, but for IPv6 instead of IPv4.
*/
#ifndef RANGES6_H
#define RANGES6_H
#include "massip-addr.h"
#include <stdio.h>
#include <stdint.h>
struct Range;
/**
* A range of IPv6 ranges.
* Inclusive, so [n..m] includes both 'n' and 'm'.
*/
struct Range6
{
ipv6address begin;
ipv6address end;
};
/**
* An array of ranges in sorted order
*/
struct Range6List
{
struct Range6 *list;
size_t count;
size_t max;
size_t *picker;
unsigned is_sorted:1;
};
/**
* Adds the given range to the targets list. The given range can be a duplicate
* or overlap with an existing range, which will get combined with existing
* ranges.
* @param targets
* A list of IPv6 ranges.
* @param begin
* The first address of the range that'll be added.
* @param end
* The last address (inclusive) of the range that'll be added.
*/
void
range6list_add_range(struct Range6List *targets, ipv6address begin, ipv6address end);
/**
* Removes the given range from the target list. The input range doesn't
* have to exist, or can partial overlap with existing ranges.
* @param targets
* A list of IPv6 ranges.
* @param begin
* The first address of the range that'll be removed.
* @param end
* The last address of the range that'll be removed (inclusive).
*/
void
range6list_remove_range(struct Range6List *targets, const ipv6address begin, const ipv6address end);
/**
* Same as 'rangelist_remove_range()', except the input is a range
* structure instead of a start/stop numbers.
*/
void
range6list_remove_range2(struct Range6List *targets, struct Range6 range);
/**
* Returns 'true' if the indicated IPv6 address is in one of the target
* ranges.
* @param targets
* A list of IPv6 ranges
* @param ip
* An IPv6 address that might in be in the list of ranges
* @return
* 'true' if the ranges contain the item, or 'false' otherwise
*/
int
range6list_is_contains(const struct Range6List *targets, const ipv6address ip);
/**
* Tests if the range is bad/invalid.
* @return 1 is invalid, 0 if good.
*/
int range6_is_bad_address(const struct Range6 *range);
/**
* Remove things from the target list. The primary use of this is the
* "exclude-file" containing a list of IP addresses that we should
* not scan
* @param targets
* Our array of target IP address (or port) ranges that we'll be
* scanning.
* @param excludes
* A list, probably read in from --excludefile, of things that we
* should not be scanning, that will override anything we otherwise
* try to scan.
* @return
* the total number of IP addresses or ports removed.
*/
ipv6address
range6list_exclude( struct Range6List *targets,
const struct Range6List *excludes);
/**
* Counts the total number of IPv6 addresses in the target list. This
* iterates over all the ranges in the table, summing up the count within
* each range.
* @param targets
* A list of IP address or port ranges.
* @return
* The total number of address or ports.
*/
massint128_t
range6list_count(const struct Range6List *targets);
/**
* Given an index in a continuous range of [0...count], pick a corresponding
* number (IP address or port) from a list of non-continuous ranges (not
* necessarily starting from 0). In other words, given the two ranges
* 10-19 50-69
* we'll have a total of 30 possible numbers. Thus, the index goes from
* [0..29], with the values 0..9 picking the corresponding values from the
* first range, and the values 10..29 picking the corresponding values
* from the second range.
*
* NOTE: This is a fundamental part of this program's design, that the user
* can specify non-contiguous IP and port ranges, but yet we iterate over
* them using a monotonically increasing index variable.
*
* @param targets
* A list of IP address ranges, or a list of port ranges (one or the
* other, but not both).
* @param index
* An integer starting at 0 up to (but not including) the value returned
* by 'rangelist_count()' for this target list.
* @return
* an IP address or port corresponding to this index.
*/
ipv6address
range6list_pick(const struct Range6List *targets, uint64_t index);
/**
* Remove all the ranges in the range list.
*/
void
range6list_remove_all(struct Range6List *list);
/**
* Merge two range lists
*/
void
range6list_merge(struct Range6List *list1, const struct Range6List *list2);
/**
* Optimizes the target list, so that when we call "rangelist_pick()"
* from an index, it runs faster. It currently configures this for
* a binary-search, though in the future some more efficient
* algorithm may be chosen.
*/
void
range6list_optimize(struct Range6List *targets);
/**
* Sorts the list of target. We maintain the list of targets in sorted
* order internally even though we scan the targets in random order
* externally.
*/
void
range6list_sort(struct Range6List *targets);
/**
* Does a regression test of this module
* @return
* 0 if the regression test succeeds, or a positive value on failure
*/
int
ranges6_selftest(void);
#endif

152
src/massip.c Normal file
View File

@ -0,0 +1,152 @@
#include "massip.h"
#include "massip-parse.h"
#include "massip-rangesv4.h"
#include "massip-rangesv6.h"
#include <string.h>
#include <ctype.h>
void massip_apply_excludes(struct MassIP *targets, struct MassIP *exclude)
{
rangelist_exclude(&targets->ipv4, &exclude->ipv4);
range6list_exclude(&targets->ipv6, &exclude->ipv6);
rangelist_exclude(&targets->ports, &exclude->ports);
}
void massip_optimize(struct MassIP *targets)
{
rangelist_optimize(&targets->ipv4);
range6list_optimize(&targets->ipv6);
rangelist_optimize(&targets->ports);
targets->count_ports = rangelist_count(&targets->ports);
targets->count_ipv4s = rangelist_count(&targets->ipv4);
targets->count_ipv6s = range6list_count(&targets->ipv6).lo;
targets->ipv4_index_threshold = targets->count_ipv4s * rangelist_count(&targets->ports);
}
int massip_pick(const struct MassIP *massip, uint64_t index, ipaddress *addr, unsigned *port)
{
/*
* We can return either IPv4 or IPv6 addresses
*/
if (index < massip->ipv4_index_threshold) {
addr->version = 4;
addr->ipv4 = rangelist_pick(&massip->ipv4, index % massip->count_ipv4s);
*port = rangelist_pick(&massip->ports, index / massip->count_ipv4s);
} else {
addr->version = 6;
index -= massip->ipv4_index_threshold;
addr->ipv6 = range6list_pick(&massip->ipv6, index % massip->count_ipv6s);
*port = rangelist_pick(&massip->ports, index / massip->count_ipv6s);
}
return 0;
}
int massip_has_ip(const struct MassIP *massip, ipaddress ip)
{
if (ip.version == 6)
return range6list_is_contains(&massip->ipv6, ip.ipv6);
else
return rangelist_is_contains(&massip->ipv4, ip.ipv4);
}
int massip_has_port(const struct MassIP *massip, unsigned port)
{
return rangelist_is_contains(&massip->ports, port);
}
int massip_has_ipv4_targets(const struct MassIP *massip)
{
return massip->ipv4.count != 0;
}
int massip_has_target_ports(const struct MassIP *massip)
{
return massip->ports.count != 0;
}
int massip_has_ipv6_targets(const struct MassIP *massip)
{
return massip->ipv6.count != 0;
}
int massip_add_target_string(struct MassIP *massip, const char *string)
{
const char *ranges = string;
size_t offset = 0;
size_t max_offset = strlen(ranges);
while (offset < max_offset) {
struct Range range;
struct Range6 range6;
int err;
/* Grab the next IPv4 or IPv6 range */
err = massip_parse_range(ranges, &offset, max_offset, &range, &range6);
switch (err) {
case Ipv4_Address:
rangelist_add_range(&massip->ipv4, range.begin, range.end);
break;
case Ipv6_Address:
range6list_add_range(&massip->ipv6, range6.begin, range6.end);
break;
default:
offset = max_offset; /* An error means skipping the rest of the string */
return 1;
}
while (offset < max_offset && (isspace(ranges[offset]&0xFF) || ranges[offset] == ','))
offset++;
}
return 0;
}
int massip_add_port_string(struct MassIP *targets, const char *string, unsigned defaultrange)
{
unsigned is_error = 0;
rangelist_parse_ports(&targets->ports, string, &is_error, defaultrange);
if (is_error)
return 1;
else
return 0;
}
int massip_selftest(void)
{
struct MassIP targets;
struct MassIP excludes;
int err;
int line;
massint128_t count;
memset(&targets, 0, sizeof(targets));
memset(&excludes, 0, sizeof(targets));
rangelist_parse_ports(&targets.ports, "80", 0, 0);
/* First, create a list of targets */
line = __LINE__;
err = massip_add_target_string(&targets, "2607:f8b0:4002:801::2004/124,1111::1");
if (err)
goto fail;
/* Second, create an exclude list */
line = __LINE__;
err = massip_add_target_string(&excludes, "2607:f8b0:4002:801::2004/126,1111::/16");
if (err)
goto fail;
/* Third, apply the excludes, causing ranges to be removed
* from the target list */
massip_apply_excludes(&targets, &excludes);
/* Now make sure the count equals the expected count */
line = __LINE__;
count = massip_range(&targets);
if (count.hi != 0 || count.lo != 12)
goto fail;
return 0;
fail:
fprintf(stderr, "[-] massip: test fail, line=%d\n", line);
return 1;
}

101
src/massip.h Normal file
View File

@ -0,0 +1,101 @@
#ifndef MASSIP_H
#define MASSIP_H
#include <stddef.h>
#include "massip-rangesv4.h"
#include "massip-rangesv6.h"
struct MassIP {
struct RangeList ipv4;
struct Range6List ipv6;
/**
* The ports we are scanning for. The user can specify repeated ports
* and overlapping ranges, but we'll deduplicate them, scanning ports
* only once.
* NOTE: TCP ports are stored 0-64k, but UDP ports are stored in the
* range 64k-128k, thus, allowing us to scan both at the same time.
*/
struct RangeList ports;
/**
* Used internally to differentiate between indexes selecting an
* IPv4 address and higher ones selecting an IPv6 address.
*/
uint64_t ipv4_index_threshold;
uint64_t count_ports;
uint64_t count_ipv4s;
uint64_t count_ipv6s;
};
/**
* Count the total number of targets in a scan. This is calculated
* the (IPv6 addresses * IPv4 addresses * ports). This can produce
* a 128-bit number (larger, actually).
*/
massint128_t massip_range(struct MassIP *massip);
/**
* Remove everything in "targets" that's listed in the "exclude"
* list. The reason for this is that we'll have a single policy
* file of those address ranges which we are forbidden to scan.
* Then, each time we run a scan with different targets, we
* apply this policy file.
*/
void massip_apply_excludes(struct MassIP *targets, struct MassIP *exclude);
/**
* The last step after processing the configuration, setting up the
* state to be used for scanning. This sorts the address, removes
* duplicates, and creates an optimized 'picker' system to easily
* find an address given an index, or find an index given an address.
*/
void massip_optimize(struct MassIP *targets);
/**
* This selects an IP+port combination given an index whose value
* is [0..range], where 'range' is the value returned by the function
* `massip_range()`. Since the optimization step (`massip_optimized()`)
* sorted all addresses/ports, a monotonically increasing index will
* list everything in sorted order. The intent, however, is to use the
* "blackrock" algorithm to randomize the index before calling this function.
*
* It is this function, plus the 'blackrock' randomization algorithm, that
* is at the heart of Masscan.
*/
int massip_pick(const struct MassIP *massip, uint64_t index, ipaddress *addr, unsigned *port);
int massip_has_ip(const struct MassIP *massip, ipaddress ip);
int massip_has_port(const struct MassIP *massip, unsigned port);
int massip_add_target_string(struct MassIP *massip, const char *string);
/**
* Parse the string contain port specifier.
*/
int massip_add_port_string(struct MassIP *massip, const char *string, unsigned proto);
/**
* Indicates whether there are IPv4 targets. If so, we'll have to
* initialize the IPv4 portion of the stack.
* @return true if there are IPv4 targets to be scanned, false
* otherwise
*/
int massip_has_ipv4_targets(const struct MassIP *massip);
int massip_has_target_ports(const struct MassIP *massip);
/**
* Indicates whether there are IPv6 targets. If so, we'll have to
* initialize the IPv6 portion of the stack.
* @return true if there are IPv6 targets to be scanned, false
* otherwise
*/
int massip_has_ipv6_targets(const struct MassIP *massip);
int massip_selftest(void);
#endif

194
src/misc-rstfilter.c Normal file
View File

@ -0,0 +1,194 @@
#include "misc-rstfilter.h"
#include "util-malloc.h"
#include "crypto-siphash24.h"
#include <time.h>
struct ResetFilter
{
unsigned long long seed;
size_t bucket_count;
size_t bucket_mask;
unsigned counter;
unsigned char *buckets;
};
static size_t
next_pow2(size_t n)
{
size_t bit_count = 0;
/* Always have at least one bit */
if (n == 0)
return 1;
/* If already a power-of-two, then return that */
if ((n & (n - 1)) == 0)
return n;
/* Count the number of bits */
while (n != 0) {
n >>= 1;
bit_count += 1;
}
return (size_t)1 << (size_t)bit_count;
}
struct ResetFilter *
rstfilter_create(unsigned long long seed, size_t bucket_count)
{
struct ResetFilter *rf;
rf = CALLOC(1, sizeof(*rf));
rf->seed = seed;
rf->bucket_count = next_pow2(bucket_count);
rf->bucket_mask = rf->bucket_count - 1;
rf->buckets = CALLOC(rf->bucket_count/2, sizeof(*rf->buckets));
return rf;
}
void
rstfilter_destroy(struct ResetFilter *rf)
{
if (rf == NULL)
return;
free(rf->buckets);
free(rf);
}
int
rstfilter_is_filter(struct ResetFilter *rf,
ipaddress src_ip, unsigned src_port,
ipaddress dst_ip, unsigned dst_port)
{
uint64_t hash;
uint64_t input[5];
uint64_t key[2];
size_t index;
unsigned char *p;
int result = 0;
/*
* Setup the input
*/
switch (src_ip.version) {
case 4:
input[0] = src_ip.ipv4;
input[1] = src_port;
input[2] = dst_ip.ipv4;
input[3] = dst_port;
break;
case 6:
input[0] = src_ip.ipv6.hi;
input[1] = src_ip.ipv6.lo;
input[2] = dst_ip.ipv6.hi;
input[3] = dst_ip.ipv6.lo;
input[4] = src_port<<16 | dst_port;
break;
}
key[0] = rf->seed;
key[1] = rf->seed;
/*
* Grab the bucket
*/
hash = siphash24(input, sizeof(input), key);
index = hash & rf->bucket_mask;
/*
* Find the result (1=filterout, 0=sendrst)
*/
p = &rf->buckets[index/2];
if (index & 1) {
if ((*p & 0x0F) == 0x0F)
result = 1; /* filter out */
else
*p = (*p) + 0x01;
} else {
if ((*p & 0xF0) == 0xF0)
result = 1; /* filter out */
else
*p = (*p) + 0x10;
}
/*
* Empty a random bucket
*/
input[0] = (unsigned)hash;
input[1] = rf->counter++;
hash = siphash24(input, sizeof(input), key);
index = hash & rf->bucket_mask;
p = &rf->buckets[index/2];
if (index & 1) {
if ((*p & 0x0F))
*p = (*p) - 0x01;
} else {
if ((*p & 0xF0))
*p = (*p) - 0x10;
}
return result;
}
int
rstfilter_selftest(void)
{
struct ResetFilter *rf;
size_t i;
unsigned count_filtered = 0;
unsigned count_passed = 0;
ipaddress src;
ipaddress dst;
src.version = 4;
src.ipv4 = 1;
dst.version = 4;
dst.ipv4 = 3;
rf = rstfilter_create(time(0), 64);
/* Verify the first 15 packets pass the filter */
for (i=0; i<15; i++) {
int x;
x = rstfilter_is_filter(rf, src, 2, dst, 4);
if (x) {
fprintf(stderr, "[-] rstfilter failed, line=%u\n", __LINE__);
return 1;
}
}
/* Now run 10000 more times */
for (i=0; i<1000; i++) {
int x;
x = rstfilter_is_filter(rf, src, 2, dst, 4);
count_filtered += x;
count_passed += !x;
}
/* SOME must have passed, due to us emptying random buckets */
if (count_passed == 0) {
fprintf(stderr, "[-] rstfilter failed, line=%u\n", __LINE__);
return 1;
}
/* However, while some pass, the vast majority should be filtered */
if (count_passed > count_filtered/10) {
fprintf(stderr, "[-] rstfilter failed, line=%u\n", __LINE__);
return 1;
}
//printf("filtered=%u passed=%u\n", count_filtered, count_passed);
return 0;
}

60
src/misc-rstfilter.h Normal file
View File

@ -0,0 +1,60 @@
/*
RST filter
In theory, we should transmit a RST packet every time we receive an invalid
TCP packet. In practice, this can lead to endless transmits when the other
size continues to transmit bad packets. This may happen accidentally, or this
may happen on purpose from the other side trying to attack the scanner
intentionally. In May 2019 I see this from somebody who I suspect is trying
to do that, replying back as fast as the scanner transmits (when running
at 10,000 packets per-second). This halts the scan, as it's throttle limit
is filled sending RSTs and not doing something useful.
The design is a simple non-deterministic algorithm. It hashes the
IP/prot combo, then updates a counter at that bucket. When it reaches
its limit, it stops transmitting resets. However, it'll also slowly
empty buckets, so can occasionally transmit a RST now and then.
*/
#ifndef MISC_RSTFILTER_H
#define MISC_RSTFILTER_H
#include <stdio.h>
#include "massip-addr.h"
struct ResetFilter;
/**
* Create a structure for this.
* @param seed
* A random seed chosen via entropy at startup, so that adversaries
* can't predict where the buckets will be.
* @param bucket_count
* The number of buckets. This'll be rounded up to the nearest
* power-of-two. 16384 is probably a good number.
* @return an instance of this object that should be eventually
* cleaned up with 'rstfilter_destroy()'.
*/
struct ResetFilter *
rstfilter_create(unsigned long long seed, size_t bucket_count);
/**
* Cleans up the object that was created with 'rstfilter_create()'.
*/
void
rstfilter_destroy(struct ResetFilter *rf);
/**
* Tests to see if we should ignore the given RST packet. This will
* also slowly empty a random bucket
* @return 1 if we should filter out the offending packet and ignore it,
* or else 0 if we shouldn't ignore it.
*/
int
rstfilter_is_filter(struct ResetFilter *rf, ipaddress src_ip, unsigned src_port, ipaddress dst_ip, unsigned dst_port);
int
rstfilter_selftest(void);
#endif

369
src/out-binary.c Normal file
View File

@ -0,0 +1,369 @@
#include "output.h"
#include "masscan-app.h"
#include "masscan-status.h"
#include "out-record.h"
#include "util-safefunc.h"
#include <assert.h>
/****************************************************************************
****************************************************************************/
static void
binary_out_open(struct Output *out, FILE *fp)
{
char firstrecord[2+'a'];
size_t bytes_written;
UNUSEDPARM(out);
memset(firstrecord, 0, 2+'a');
snprintf(firstrecord, 2+'a', "masscan/1.1\ns:%u\n",
(unsigned)out->when_scan_started);
bytes_written = fwrite(firstrecord, 1, 2+'a', fp);
if (bytes_written != 2+'a') {
perror("output");
exit(1);
}
out->rotate.bytes_written += bytes_written;
}
/****************************************************************************
****************************************************************************/
static void
binary_out_close(struct Output *out, FILE *fp)
{
char firstrecord[2+'a'];
size_t bytes_written;
UNUSEDPARM(out);
memset(firstrecord, 0, 2+'a');
snprintf(firstrecord, 2+'a', "masscan/1.1");
bytes_written = fwrite(firstrecord, 1, 2+'a', fp);
if (bytes_written != 2+'a') {
perror("output");
exit(1);
}
out->rotate.bytes_written += bytes_written;
}
static void
_put_byte(unsigned char *buf, size_t length, size_t *r_offset, unsigned long long num)
{
size_t offset = *r_offset;
(*r_offset) += 1;
if (*r_offset <= length) {
buf[offset++] = (unsigned char)(num>>0);
}
}
static void
_put_short(unsigned char *buf, size_t length, size_t *r_offset, unsigned long long num)
{
size_t offset = *r_offset;
(*r_offset) += 2;
if (*r_offset <= length) {
buf[offset++] = (unsigned char)(num>>8);
buf[offset++] = (unsigned char)(num>>0);
}
}
static void
_put_integer(unsigned char *buf, size_t length, size_t *r_offset, unsigned long long num)
{
size_t offset = *r_offset;
(*r_offset) += 4;
if (*r_offset <= length) {
buf[offset++] = (unsigned char)(num>>24);
buf[offset++] = (unsigned char)(num>>16);
buf[offset++] = (unsigned char)(num>>8);
buf[offset++] = (unsigned char)(num>>0);
}
}
static void
_put_long(unsigned char *buf, size_t length, size_t *r_offset, unsigned long long num)
{
size_t offset = *r_offset;
(*r_offset) += 8;
if (*r_offset <= length) {
buf[offset++] = (unsigned char)(num>>56);
buf[offset++] = (unsigned char)(num>>48);
buf[offset++] = (unsigned char)(num>>40);
buf[offset++] = (unsigned char)(num>>32);
buf[offset++] = (unsigned char)(num>>24);
buf[offset++] = (unsigned char)(num>>16);
buf[offset++] = (unsigned char)(num>>8);
buf[offset++] = (unsigned char)(num>>0);
}
}
/****************************************************************************
****************************************************************************/
static void
binary_out_status_ipv6(struct Output *out, FILE *fp, time_t timestamp,
int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl)
{
unsigned char buf[256+1];
size_t max = sizeof(buf)-1;
size_t offset = 0;
size_t bytes_written;
/* [TYPE] field */
switch (status) {
case PortStatus_Open:
_put_byte(buf, max, &offset, Out_Open6);
break;
case PortStatus_Closed:
_put_byte(buf, max, &offset, Out_Closed6);
break;
case PortStatus_Arp:
_put_byte(buf, max, &offset, Out_Arp6);
break;
default:
return;
}
/* [LENGTH] field
* see assert() below */
_put_byte(buf, max, &offset, 26);
_put_integer(buf, max, &offset, timestamp);
_put_byte(buf, max, &offset, ip_proto);
_put_short(buf, max, &offset, port);
_put_byte(buf, max, &offset, reason);
_put_byte(buf, max, &offset, ttl);
_put_byte(buf, max, &offset, ip.version);
_put_long(buf, max, &offset, ip.ipv6.hi);
_put_long(buf, max, &offset, ip.ipv6.lo);
assert(offset == 2 + 26);
bytes_written = fwrite(buf, 1, offset, fp);
if (bytes_written != offset) {
perror("output");
exit(1);
}
out->rotate.bytes_written += bytes_written;
}
/****************************************************************************
****************************************************************************/
static void
binary_out_status(struct Output *out, FILE *fp, time_t timestamp,
int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl)
{
unsigned char foo[256];
size_t bytes_written;
/* This function is for IPv6, call a different function for IPv6 */
if (ip.version == 6) {
binary_out_status_ipv6(out, fp, timestamp, status, ip, ip_proto, port, reason, ttl);
return;
}
/* [TYPE] field */
switch (status) {
case PortStatus_Open:
foo[0] = Out_Open2;
break;
case PortStatus_Closed:
foo[0] = Out_Closed2;
break;
case PortStatus_Arp:
foo[0] = Out_Arp2;
break;
default:
return;
}
/* [LENGTH] field */
foo[1] = 13;
/* [TIMESTAMP] field */
foo[2] = (unsigned char)(timestamp>>24);
foo[3] = (unsigned char)(timestamp>>16);
foo[4] = (unsigned char)(timestamp>> 8);
foo[5] = (unsigned char)(timestamp>> 0);
foo[6] = (unsigned char)(ip.ipv4 >>24);
foo[7] = (unsigned char)(ip.ipv4 >>16);
foo[8] = (unsigned char)(ip.ipv4 >> 8);
foo[9] = (unsigned char)(ip.ipv4 >> 0);
foo[10] = (unsigned char)(ip_proto);
foo[11] = (unsigned char)(port>>8);
foo[12] = (unsigned char)(port>>0);
foo[13] = (unsigned char)reason;
foo[14] = (unsigned char)ttl;
bytes_written = fwrite(&foo, 1, 15, fp);
if (bytes_written != 15) {
perror("output");
exit(1);
}
out->rotate.bytes_written += bytes_written;
}
/****************************************************************************
****************************************************************************/
static void
binary_out_banner_ipv6(struct Output *out, FILE *fp, time_t timestamp,
ipaddress ip, unsigned ip_proto, unsigned port,
enum ApplicationProtocol proto, unsigned ttl,
const unsigned char *px, unsigned length)
{
unsigned char foo[32768];
unsigned i;
size_t bytes_written;
static const unsigned HeaderLength = 14 + 13;
/* [TYPE] field */
foo[0] = Out_Banner6; /*banner*/
/* [LENGTH] field*/
if (length >= 128 * 128 - HeaderLength)
return;
if (length < 128 - HeaderLength) {
foo[1] = (unsigned char)(length + HeaderLength);
i = 2;
} else {
foo[1] = (unsigned char)((length + HeaderLength)>>7) | 0x80;
foo[2] = (unsigned char)((length + HeaderLength) & 0x7F);
i = 3;
}
/* [TIMESTAMP] field */
foo[i+0] = (unsigned char)(timestamp>>24);
foo[i+1] = (unsigned char)(timestamp>>16);
foo[i+2] = (unsigned char)(timestamp>> 8);
foo[i+3] = (unsigned char)(timestamp>> 0);
foo[i+ 4] = (unsigned char)(ip_proto);
foo[i+ 5] = (unsigned char)(port>>8);
foo[i+ 6] = (unsigned char)(port>>0);
foo[i+ 7] = (unsigned char)(proto>>8);
foo[i+ 8] = (unsigned char)(proto>>0);
foo[i+ 9] = (unsigned char)(ttl);
foo[i+10] = (unsigned char)(ip.version);
foo[i+11] = (unsigned char)(ip.ipv6.hi >> 56ULL);
foo[i+12] = (unsigned char)(ip.ipv6.hi >> 48ULL);
foo[i+13] = (unsigned char)(ip.ipv6.hi >> 40ULL);
foo[i+14] = (unsigned char)(ip.ipv6.hi >> 32ULL);
foo[i+15] = (unsigned char)(ip.ipv6.hi >> 24ULL);
foo[i+16] = (unsigned char)(ip.ipv6.hi >> 16ULL);
foo[i+17] = (unsigned char)(ip.ipv6.hi >> 8ULL);
foo[i+18] = (unsigned char)(ip.ipv6.hi >> 0ULL);
foo[i+19] = (unsigned char)(ip.ipv6.lo >> 56ULL);
foo[i+20] = (unsigned char)(ip.ipv6.lo >> 48ULL);
foo[i+21] = (unsigned char)(ip.ipv6.lo >> 40ULL);
foo[i+22] = (unsigned char)(ip.ipv6.lo >> 32ULL);
foo[i+23] = (unsigned char)(ip.ipv6.lo >> 24ULL);
foo[i+24] = (unsigned char)(ip.ipv6.lo >> 16ULL);
foo[i+25] = (unsigned char)(ip.ipv6.lo >> 8ULL);
foo[i+26] = (unsigned char)(ip.ipv6.lo >> 0ULL);
/* Banner */
memcpy(foo+i+14+13, px, length);
bytes_written = fwrite(&foo, 1, length+i+HeaderLength, fp);
if (bytes_written != length+i+HeaderLength) {
perror("output");
exit(1);
}
out->rotate.bytes_written += bytes_written;
}
/****************************************************************************
****************************************************************************/
static void
binary_out_banner(struct Output *out, FILE *fp, time_t timestamp,
ipaddress ip, unsigned ip_proto, unsigned port,
enum ApplicationProtocol proto, unsigned ttl,
const unsigned char *px, unsigned length)
{
unsigned char foo[32768];
unsigned i;
size_t bytes_written;
static const unsigned HeaderLength = 14;
if (ip.version == 6) {
binary_out_banner_ipv6(out, fp, timestamp, ip, ip_proto, port, proto, ttl, px, length);
return;
}
/* [TYPE] field */
foo[0] = Out_Banner9; /*banner*/
/* [LENGTH] field*/
if (length >= 128 * 128 - HeaderLength)
return;
if (length < 128 - HeaderLength) {
foo[1] = (unsigned char)(length + HeaderLength);
i = 2;
} else {
foo[1] = (unsigned char)((length + HeaderLength)>>7) | 0x80;
foo[2] = (unsigned char)((length + HeaderLength) & 0x7F);
i = 3;
}
/* [TIMESTAMP] field */
foo[i+0] = (unsigned char)(timestamp>>24);
foo[i+1] = (unsigned char)(timestamp>>16);
foo[i+2] = (unsigned char)(timestamp>> 8);
foo[i+3] = (unsigned char)(timestamp>> 0);
foo[i+4] = (unsigned char)(ip.ipv4 >> 24);
foo[i+5] = (unsigned char)(ip.ipv4 >> 16);
foo[i+6] = (unsigned char)(ip.ipv4 >> 8);
foo[i+7] = (unsigned char)(ip.ipv4 >> 0);
foo[i+8] = (unsigned char)(ip_proto);
foo[i+ 9] = (unsigned char)(port>>8);
foo[i+10] = (unsigned char)(port>>0);
foo[i+11] = (unsigned char)(proto>>8);
foo[i+12] = (unsigned char)(proto>>0);
foo[i+13] = (unsigned char)(ttl);
/* Banner */
memcpy(foo+i+14, px, length);
bytes_written = fwrite(&foo, 1, length+i+HeaderLength, fp);
if (bytes_written != length+i+HeaderLength) {
perror("output");
exit(1);
}
out->rotate.bytes_written += bytes_written;
}
/****************************************************************************
****************************************************************************/
const struct OutputType binary_output = {
"scan",
0,
binary_out_open,
binary_out_close,
binary_out_status,
binary_out_banner,
};

92
src/out-certs.c Normal file
View File

@ -0,0 +1,92 @@
#include "output.h"
#include "masscan-app.h"
#include "masscan-status.h"
#include "util-safefunc.h"
#include <ctype.h>
/****************************************************************************
****************************************************************************/
static void
cert_out_open(struct Output *out, FILE *fp)
{
UNUSEDPARM(out);
UNUSEDPARM(fp);
}
/****************************************************************************
****************************************************************************/
static void
cert_out_close(struct Output *out, FILE *fp)
{
UNUSEDPARM(out);
fprintf(fp, "{finished: 1}\n");
}
/******************************************************************************
******************************************************************************/
static void
cert_out_status(struct Output *out, FILE *fp, time_t timestamp, int status,
ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl)
{
/* certificates only come with banner info, so there is no port info
* to report */
UNUSEDPARM(out);
UNUSEDPARM(fp);
UNUSEDPARM(timestamp);
UNUSEDPARM(status);
UNUSEDPARM(ip);
UNUSEDPARM(ip_proto);
UNUSEDPARM(port);
UNUSEDPARM(reason);
UNUSEDPARM(ttl);
}
/******************************************************************************
******************************************************************************/
static void
cert_out_banner(struct Output *out, FILE *fp, time_t timestamp,
ipaddress ip, unsigned ip_proto, unsigned port,
enum ApplicationProtocol proto,
unsigned ttl,
const unsigned char *px, unsigned length)
{
unsigned i;
UNUSEDPARM(ip_proto);
UNUSEDPARM(ip);
UNUSEDPARM(timestamp);
UNUSEDPARM(fp);
UNUSEDPARM(out);
UNUSEDPARM(ttl);
UNUSEDPARM(proto);
UNUSEDPARM(port);
if (length > 5 && memcmp(px, "cert:", 5) == 0) {
px += 5;
length -= 5;
}
printf("-----BEGIN CERTIFICATE-----\n");
for (i=0; i<length; i += 72) {
unsigned len = length - i;
if (len > 72)
len = 72;
printf("%.*s\n", len, px+i);
}
printf("-----END CERTIFICATE-----\n");
}
/****************************************************************************
****************************************************************************/
const struct OutputType certs_output = {
"cert",
0,
cert_out_open,
cert_out_close,
cert_out_status,
cert_out_banner
};

217
src/out-grepable.c Normal file
View File

@ -0,0 +1,217 @@
#include "output.h"
#include "masscan.h"
#include "masscan-version.h"
#include "masscan-status.h"
#include "out-tcp-services.h"
#include "massip-port.h"
#include "util-safefunc.h"
/****************************************************************************
****************************************************************************/
static unsigned
count_type(const struct RangeList *ports, int start_type, int end_type)
{
unsigned min_port = start_type;
unsigned max_port = end_type;
unsigned i;
unsigned result = 0;
for (i=0; i<ports->count; i++) {
struct Range r = ports->list[i];
if (r.begin > max_port)
continue;
if (r.end < min_port)
continue;
if (r.begin < min_port)
r.begin = min_port;
if (r.end > max_port)
r.end = max_port;
result += r.end - r.begin + 1;
}
return result;
}
/****************************************************************************
****************************************************************************/
static void
print_port_list(const struct RangeList *ports, int type, FILE *fp)
{
unsigned min_port = type;
unsigned max_port = type + 65535;
unsigned i;
for (i=0; i<ports->count; i++) {
struct Range r = ports->list[i];
if (r.begin > max_port)
continue;
if (r.end < min_port)
continue;
if (r.begin < min_port)
r.begin = min_port;
if (r.end > max_port)
r.end = max_port;
fprintf(fp, "%u-%u%s", r.begin, r.end, (i+1<ports->count)?",":"");
}
}
extern const char *debug_recv_status;
/****************************************************************************
* This function doesn't really "open" the file. Instead, the purpose of
* this function is to initialize the file by printing header information.
****************************************************************************/
static void
grepable_out_open(struct Output *out, FILE *fp)
{
char timestamp[64];
struct tm tm;
unsigned count;
safe_gmtime(&tm, &out->when_scan_started);
//Tue Jan 21 20:23:22 2014
//%a %b %d %H:%M:%S %Y
strftime(timestamp, sizeof(timestamp), "%c", &tm);
fprintf(fp, "# Masscan " MASSCAN_VERSION " scan initiated %s\n",
timestamp);
count = count_type(&out->masscan->targets.ports, Templ_TCP, Templ_TCP_last);
fprintf(fp, "# Ports scanned: TCP(%u;", count);
if (count)
print_port_list(&out->masscan->targets.ports, Templ_TCP, fp);
count = count_type(&out->masscan->targets.ports, Templ_UDP, Templ_UDP_last);
fprintf(fp, ") UDP(%u;", count);
if (count)
print_port_list(&out->masscan->targets.ports, Templ_UDP, fp);
count = count_type(&out->masscan->targets.ports, Templ_SCTP, Templ_SCTP_last);
fprintf(fp, ") SCTP(%u;", count);
if (count)
print_port_list(&out->masscan->targets.ports, Templ_SCTP, fp);
count = count_type(&out->masscan->targets.ports, Templ_Oproto_first, Templ_Oproto_last);
fprintf(fp, ") PROTOCOLS(%u;", count);
if (count)
print_port_list(&out->masscan->targets.ports, Templ_Oproto_first, fp);
fprintf(fp, ")\n");
}
/****************************************************************************
* This function doesn't really "close" the file. Instead, it's purpose
* is to print trailing information to the file. This is pretty much only
* a concern for XML files that need stuff appended to the end.
****************************************************************************/
static void
grepable_out_close(struct Output *out, FILE *fp)
{
time_t now = time(0);
char timestamp[64];
struct tm tm;
UNUSEDPARM(out);
safe_gmtime(&tm, &now);
//Tue Jan 21 20:23:22 2014
//%a %b %d %H:%M:%S %Y
strftime(timestamp, sizeof(timestamp), "%c", &tm);
fprintf(fp, "# Masscan done at %s\n",
timestamp);
}
/****************************************************************************
* Prints out the status of a port, which is almost always just "open"
* or "closed".
****************************************************************************/
static void
grepable_out_status(struct Output *out, FILE *fp, time_t timestamp,
int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl)
{
const char *service;
ipaddress_formatted_t fmt;
UNUSEDPARM(timestamp);
UNUSEDPARM(out);
UNUSEDPARM(reason);
UNUSEDPARM(ttl);
if (ip_proto == 6)
service = tcp_service_name(port);
else if (ip_proto == 17)
service = udp_service_name(port);
else
service = oproto_service_name(ip_proto);
fprintf(fp, "Timestamp: %llu", (unsigned long long)timestamp);
fmt = ipaddress_fmt(ip);
fprintf(fp, "\tHost: %s ()", fmt.string);
fprintf(fp, "\tPorts: %u/%s/%s/%s/%s/%s/%s\n",
port,
status_string(status), //"open", "closed"
name_from_ip_proto(ip_proto), //"tcp", "udp", "sctp"
"", //owner
service, //service
"", //SunRPC info
"" //Version info
);
}
/****************************************************************************
* Prints out "banner" information for a port. This is done when there is
* a protocol defined for a port, and we do some interaction to find out
* more information about which protocol is running on a port, it's version,
* and other useful information.
****************************************************************************/
static void
grepable_out_banner(struct Output *out, FILE *fp, time_t timestamp,
ipaddress ip, unsigned ip_proto, unsigned port,
enum ApplicationProtocol proto, unsigned ttl,
const unsigned char *px, unsigned length)
{
char banner_buffer[MAX_BANNER_LENGTH];
ipaddress_formatted_t fmt;
UNUSEDPARM(ttl);
UNUSEDPARM(timestamp);
UNUSEDPARM(out);
UNUSEDPARM(ip_proto);
fmt = ipaddress_fmt(ip);
fprintf(fp, "Host: %s ()", fmt.string);
fprintf(fp, "\tPort: %u", port);
fprintf(fp, "\tService: %s", masscan_app_to_string(proto));
normalize_string(px, length, banner_buffer, sizeof(banner_buffer));
fprintf(fp, "\tBanner: %s\n", banner_buffer);
}
/****************************************************************************
* This is the only structure exposed to the rest of the system. Everything
* else in the file is defined 'static' or 'private'.
****************************************************************************/
const struct OutputType grepable_output = {
"grepable",
0,
grepable_out_open,
grepable_out_close,
grepable_out_status,
grepable_out_banner
};

81
src/out-hostonly.c Normal file
View File

@ -0,0 +1,81 @@
#include "output.h"
#include "masscan.h"
#include "masscan-app.h"
#include "masscan-status.h"
#include "unusedparm.h"
#include "out-tcp-services.h"
static void
hostonly_out_open(struct Output *out, FILE *fp)
{
UNUSEDPARM(fp);
UNUSEDPARM(out);
}
static void
hostonly_out_close(struct Output *out, FILE *fp)
{
UNUSEDPARM(fp);
UNUSEDPARM(out);
}
static void
hostonly_out_status(struct Output *out, FILE *fp, time_t timestamp,
int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl)
{
ipaddress_formatted_t fmt = ipaddress_fmt(ip);
UNUSEDPARM(reason);
UNUSEDPARM(out);
UNUSEDPARM(timestamp);
UNUSEDPARM(ttl);
UNUSEDPARM(port);
UNUSEDPARM(ip_proto);
UNUSEDPARM(status);
fprintf(fp, "%s\n", fmt.string);
}
/*************************************** *************************************
****************************************************************************/
static void
hostonly_out_banner(struct Output *out, FILE *fp, time_t timestamp,
ipaddress ip, unsigned ip_proto, unsigned port,
enum ApplicationProtocol proto, unsigned ttl,
const unsigned char *px, unsigned length)
{ /* SYN only - no banner */
ipaddress_formatted_t fmt = ipaddress_fmt(ip);
UNUSEDPARM(out);
UNUSEDPARM(ttl);
UNUSEDPARM(port);
UNUSEDPARM(fp);
UNUSEDPARM(timestamp);
UNUSEDPARM(ip);
UNUSEDPARM(ip_proto);
UNUSEDPARM(proto);
UNUSEDPARM(px);
UNUSEDPARM(length);
fprintf(fp, "%s\n", fmt.string);
return;
}
/****************************************************************************
****************************************************************************/
const struct OutputType hostonly_output = {
"hostonly",
0,
hostonly_out_open,
hostonly_out_close,
hostonly_out_status,
hostonly_out_banner
};

145
src/out-json.c Normal file
View File

@ -0,0 +1,145 @@
#include "output.h"
#include "masscan-app.h"
#include "masscan-status.h"
#include "util-safefunc.h"
#include <ctype.h>
/****************************************************************************
****************************************************************************/
static void
json_out_open(struct Output *out, FILE *fp)
{
UNUSEDPARM(out);
fprintf(fp, "[\n"); // enclose the atomic {}'s into an []
}
/****************************************************************************
****************************************************************************/
static void
json_out_close(struct Output *out, FILE *fp)
{
UNUSEDPARM(out);
fprintf(fp, "]\n"); // enclose the atomic {}'s into an []
}
//{ ip: "124.53.139.201", ports: [ {port: 443, proto: "tcp", status: "open", reason: "syn-ack", ttl: 48} ] }
/****************************************************************************
****************************************************************************/
static void
json_out_status(struct Output *out, FILE *fp, time_t timestamp, int status,
ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl)
{
char reason_buffer[128];
ipaddress_formatted_t fmt;
UNUSEDPARM(out);
//UNUSEDPARM(timestamp);
/* Trailing comma breaks some JSON parsers. We don't know precisely when
* we'll end, but we do know when we begin, so instead of appending
* a command to the record, we prepend it -- but not before first record */
if (out->is_first_record_seen)
fprintf(fp, ",\n");
else
out->is_first_record_seen = 1;
fprintf(fp, "{ ");
fmt = ipaddress_fmt(ip);
fprintf(fp, " \"ip\": \"%s\", ", fmt.string);
fprintf(fp, " \"timestamp\": \"%d\", \"ports\": [ {\"port\": %u, \"proto\": \"%s\", \"status\": \"%s\","
" \"reason\": \"%s\", \"ttl\": %u} ] ",
(int) timestamp,
port,
name_from_ip_proto(ip_proto),
status_string(status),
reason_string(reason, reason_buffer, sizeof(reason_buffer)),
ttl
);
fprintf(fp, "}\n");
}
/*****************************************************************************
* Remove bad characters from the banner, especially new lines and HTML
* control codes.
*****************************************************************************/
static const char *
normalize_json_string(const unsigned char *px, size_t length,
char *buf, size_t buf_len)
{
size_t i=0;
size_t offset = 0;
for (i=0; i<length; i++) {
unsigned char c = px[i];
if (isprint(c) && c != '<' && c != '>' && c != '&' && c != '\\' && c != '\"' && c != '\'') {
if (offset + 2 < buf_len)
buf[offset++] = px[i];
} else {
if (offset + 7 < buf_len) {
buf[offset++] = '\\';
buf[offset++] = 'u';
buf[offset++] = '0';
buf[offset++] = '0';
buf[offset++] = "0123456789abcdef"[px[i]>>4];
buf[offset++] = "0123456789abcdef"[px[i]&0xF];
}
}
}
buf[offset] = '\0';
return buf;
}
/******************************************************************************
******************************************************************************/
static void
json_out_banner(struct Output *out, FILE *fp, time_t timestamp,
ipaddress ip, unsigned ip_proto, unsigned port,
enum ApplicationProtocol proto,
unsigned ttl,
const unsigned char *px, unsigned length)
{
char banner_buffer[65536];
ipaddress_formatted_t fmt;
UNUSEDPARM(ttl);
/* Trailing comma breaks some JSON parsers. We don't know precisely when
* we'll end, but we do know when we begin, so instead of appending
* a command to the record, we prepend it -- but not before first record */
if (out->is_first_record_seen)
fprintf(fp, ",\n");
else
out->is_first_record_seen = 1;
fprintf(fp, "{ ");
fmt = ipaddress_fmt(ip);
fprintf(fp, " \"ip\": \"%s\", ", fmt.string);
fprintf(fp, " \"timestamp\": \"%d\", \"ports\": [ {\"port\": %u, \"proto\": \"%s\", \"service\": {\"name\": \"%s\", \"banner\": \"%s\"} } ] ",
(int) timestamp,
port,
name_from_ip_proto(ip_proto),
masscan_app_to_string(proto),
normalize_json_string(px, length, banner_buffer, sizeof(banner_buffer))
);
fprintf(fp, "}\n");
UNUSEDPARM(out);
}
/****************************************************************************
****************************************************************************/
const struct OutputType json_output = {
"json",
0,
json_out_open,
json_out_close,
json_out_status,
json_out_banner
};

163
src/out-ndjson.c Normal file
View File

@ -0,0 +1,163 @@
#include "output.h"
#include "masscan-app.h"
#include "masscan-status.h"
#include "util-safefunc.h"
#include <ctype.h>
/****************************************************************************
****************************************************************************/
static void
ndjson_out_open(struct Output *out, FILE *fp)
{
UNUSEDPARM(out);
UNUSEDPARM(fp);
}
/****************************************************************************
****************************************************************************/
static void
ndjson_out_close(struct Output *out, FILE *fp)
{
UNUSEDPARM(out);
UNUSEDPARM(fp);
}
//{ ip: "124.53.139.201", ports: [ {port: 443, proto: "tcp", status: "open", reason: "syn-ack", ttl: 48} ] }
/****************************************************************************
****************************************************************************/
static void
ndjson_out_status(struct Output *out, FILE *fp, time_t timestamp, int status,
ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl)
{
char reason_buffer[128];
ipaddress_formatted_t fmt;
UNUSEDPARM(out);
fprintf(fp, "{");
fmt = ipaddress_fmt(ip);
fprintf(fp, "\"ip\":\"%s\",", fmt.string);
fprintf(fp, "\"timestamp\":\"%d\",\"port\":%u,\"proto\":\"%s\",\"rec_type\":\"status\",\"data\":{\"status\":\"%s\","
"\"reason\":\"%s\",\"ttl\":%u}",
(int) timestamp,
port,
name_from_ip_proto(ip_proto),
status_string(status),
reason_string(reason, reason_buffer, sizeof(reason_buffer)),
ttl
);
fprintf(fp, "}\n");
}
/*****************************************************************************
* Remove bad characters from the banner, especially new lines and HTML
* control codes.
*
* Keeping this here since we may need to change the behavior from what
* is done in the sister `normalize_json_string` function. It's unlikely
* but it's a small function and will save time later if needed. Could also
* set it up to base64 encode the banner payload.
*****************************************************************************/
static const char *
normalize_ndjson_string(const unsigned char *px, size_t length,
char *buf, size_t buf_len)
{
size_t i=0;
size_t offset = 0;
for (i=0; i<length; i++) {
unsigned char c = px[i];
if (isprint(c) && c != '<' && c != '>' && c != '&' && c != '\\' && c != '\"' && c != '\'') {
if (offset + 2 < buf_len)
buf[offset++] = px[i];
} else {
if (offset + 7 < buf_len) {
buf[offset++] = '\\';
buf[offset++] = 'u';
buf[offset++] = '0';
buf[offset++] = '0';
buf[offset++] = "0123456789abcdef"[px[i]>>4];
buf[offset++] = "0123456789abcdef"[px[i]&0xF];
}
}
}
buf[offset] = '\0';
return buf;
}
/******************************************************************************
******************************************************************************/
static void
ndjson_out_banner(struct Output *out, FILE *fp, time_t timestamp,
ipaddress ip, unsigned ip_proto, unsigned port,
enum ApplicationProtocol proto,
unsigned ttl,
const unsigned char *px, unsigned length)
{
char banner_buffer[65536];
ipaddress_formatted_t fmt;
UNUSEDPARM(ttl);
//UNUSEDPARM(timestamp);
fprintf(fp, "{");
fmt = ipaddress_fmt(ip);
fprintf(fp, "\"ip\":\"%s\",", fmt.string);
fprintf(fp, "\"timestamp\":\"%d\",\"port\":%u,\"proto\":\"%s\",\"rec_type\":\"banner\",\"data\":{\"service_name\":\"%s\",\"banner\":\"%s\"}",
(int) timestamp,
port,
name_from_ip_proto(ip_proto),
masscan_app_to_string(proto),
normalize_ndjson_string(px, length, banner_buffer, sizeof(banner_buffer))
);
// fprintf(fp, "\"timestamp\":\"%d\",\"ports\":[{\"port\":%u,\"proto\":\"%s\",\"service\":{\"name\":\"%s\",\"banner\":\"%s\"}}]",
// (int) timestamp,
// port,
// name_from_ip_proto(ip_proto),
// masscan_app_to_string(proto),
// normalize_ndjson_string(px, length, banner_buffer, sizeof(banner_buffer))
// );
fprintf(fp, "}\n");
UNUSEDPARM(out);
/* fprintf(fp, "<host endtime=\"%u\">"
"<address addr=\"%u.%u.%u.%u\" addrtype=\"ipv4\"/>"
"<ports>"
"<port protocol=\"%s\" portid=\"%u\">"
"<state state=\"open\" reason=\"%s\" reason_ttl=\"%u\" />"
"<service name=\"%s\" banner=\"%s\"></service>"
"</port>"
"</ports>"
"</host>"
"\r\n",
(unsigned)timestamp,
(ip>>24)&0xFF,
(ip>>16)&0xFF,
(ip>> 8)&0xFF,
(ip>> 0)&0xFF,
name_from_ip_proto(ip_proto),
port,
reason, ttl,
masscan_app_to_string(proto),
normalize_string(px, length, banner_buffer, sizeof(banner_buffer))
);*/
}
/****************************************************************************
****************************************************************************/
const struct OutputType ndjson_output = {
"ndjson",
0,
ndjson_out_open,
ndjson_out_close,
ndjson_out_status,
ndjson_out_banner
};

85
src/out-null.c Normal file
View File

@ -0,0 +1,85 @@
#include "output.h"
#include "masscan.h"
/****************************************************************************
* This function doesn't really "open" the file. Instead, the purpose of
* this function is to initialize the file by printing header information.
****************************************************************************/
static void
null_out_open(struct Output *out, FILE *fp)
{
UNUSEDPARM(out);
UNUSEDPARM(fp);
}
/****************************************************************************
* This function doesn't really "close" the file. Instead, it's purpose
* is to print trailing information to the file. This is pretty much only
* a concern for XML files that need stuff appended to the end.
****************************************************************************/
static void
null_out_close(struct Output *out, FILE *fp)
{
UNUSEDPARM(out);
UNUSEDPARM(fp);
}
/****************************************************************************
* Prints out the status of a port, which is almost always just "open"
* or "closed".
****************************************************************************/
static void
null_out_status(struct Output *out, FILE *fp, time_t timestamp,
int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl)
{
UNUSEDPARM(timestamp);
UNUSEDPARM(out);
UNUSEDPARM(fp);
UNUSEDPARM(status);
UNUSEDPARM(ip_proto);
UNUSEDPARM(ip);
UNUSEDPARM(port);
UNUSEDPARM(reason);
UNUSEDPARM(ttl);
}
/****************************************************************************
* Prints out "banner" information for a port. This is done when there is
* a protocol defined for a port, and we do some interaction to find out
* more information about which protocol is running on a port, it's version,
* and other useful information.
****************************************************************************/
static void
null_out_banner(struct Output *out, FILE *fp, time_t timestamp,
ipaddress ip, unsigned ip_proto, unsigned port,
enum ApplicationProtocol proto, unsigned ttl,
const unsigned char *px, unsigned length)
{
UNUSEDPARM(ttl);
UNUSEDPARM(timestamp);
UNUSEDPARM(out);
UNUSEDPARM(fp);
UNUSEDPARM(ip);
UNUSEDPARM(ip_proto);
UNUSEDPARM(port);
UNUSEDPARM(proto);
UNUSEDPARM(px);
UNUSEDPARM(length);
}
/****************************************************************************
* This is the only structure exposed to the rest of the system. Everything
* else in the file is defined 'static' or 'private'.
****************************************************************************/
const struct OutputType null_output = {
"null",
0,
null_out_open,
null_out_close,
null_out_status,
null_out_banner
};

18
src/out-record.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef OUT_RECORD_H
#define OUT_RECORD_H
enum OutputRecordType {
Out_Open = 1,
Out_Closed = 2,
Out_Banner1 = 5,
Out_Open2 = 6,
Out_Closed2 = 7,
Out_Arp2 = 8,
Out_Banner9 = 9,
Out_Open6 = 10,
Out_Closed6 = 11,
Out_Arp6 = 12,
Out_Banner6 = 13,
};
#endif

352
src/out-redis.c Normal file
View File

@ -0,0 +1,352 @@
#include "output.h"
#include "masscan.h"
#include "pixie-sockets.h"
#include "util-logger.h"
#include <ctype.h>
/****************************************************************************
* Receive a full line from the socket
****************************************************************************/
static size_t
recv_line(SOCKET fd, void *buf, size_t buf_size)
{
size_t count = 0;
while (count < buf_size) {
size_t bytes_received;
bytes_received = recv(fd, (char*)buf+count, 1, 0);
if (bytes_received == 0) {
LOG(0, "redis: recv_line() failed\n");
exit(1);
}
count++;
if (((unsigned char*)buf)[count-1] == '\n')
break;
}
return count;
}
/****************************************************************************
****************************************************************************/
static int
parse_state_machine(struct Output *out, const unsigned char *px, size_t length)
{
unsigned state = out->redis.state;
unsigned i;
enum {
START,
NUMBER,
P,
PO,
PON,
PONG,
PONG_CR,
PONG_CR_LF
};
for (i=0; i<length; i++)
switch (state) {
case START:
switch (px[i]) {
case '+':
state = P;
break;
case ':':
state = NUMBER;
break;
default:
LOG(0, "redis: unexpected data: %.*s\n", (int)(length-i), px+i);
exit(1);
break;
}
break;
case NUMBER:
if (isdigit(px[i]) || px[i] == '\r')
;
else if (px[i] == '\n') {
state = 0;
if (out->redis.outstanding == 0) {
LOG(0, "redis: out of sync\n");
exit(1);
}
out->redis.outstanding--;
} else {
LOG(0, "redis: unexpected data: %.*s\n", (int)(length-i), px+i);
exit(1);
}
break;
case P:
case PO:
case PON:
case PONG_CR:
case PONG_CR_LF:
if ("PONG+\r\n"[i-P] == px[i]) {
state++;
if (px[i] == '\n') {
out->redis.state = 0;
return 1;
}
} else {
LOG(0, "redis: unexpected data: %.*s\n", (int)(length-i), px+i);
exit(1);
}
default:
LOG(0, "redis: unexpected state: %u\n", state);
exit(1);
}
out->redis.state = state;
return 0;
}
/****************************************************************************
****************************************************************************/
static int
clean_response_queue(struct Output *out, SOCKET fd)
{
fd_set readfds;
struct timeval tv = {0,0};
int x;
int nfds;
unsigned char buf[1024];
size_t bytes_read;
FD_ZERO(&readfds);
#ifdef _MSC_VER
#pragma warning(disable:4127)
#endif
FD_SET(fd, &readfds);
nfds = (int)fd;
x = select(nfds, &readfds, 0, 0, &tv);
if (x == 0)
return 1;
if (x < 0) {
LOG(0, "redis:select() failed\n");
exit(1);
}
if (x != 1) {
LOG(0, "redis:select() failed\n");
exit(1);
}
/*
* Data exists, so parse it
*/
bytes_read = recv(fd, (char*)buf, sizeof(buf), 0);
if (bytes_read == 0) {
LOG(0, "redis:recv() failed\n");
exit(1);
}
return parse_state_machine(out, buf, bytes_read);
}
/****************************************************************************
****************************************************************************/
static void
redis_out_open(struct Output *out, FILE *fp)
{
/*FIXME: why did I write this code using ptrdiff_t? */
ptrdiff_t fd = (ptrdiff_t)fp;
size_t count;
char line[1024];
UNUSEDPARM(out);
if (out->redis.password != NULL)
{
snprintf(line, sizeof(line),
"*2\r\n"
"$4\r\nAUTH\r\n"
"$%u\r\n%s\r\n",
(unsigned)strlen(out->redis.password), out->redis.password);
count = send((SOCKET)fd, line, (int)strlen(line), 0);
if (count != strlen(line))
{
LOG(0, "redis: error auth\n");
exit(1);
}
count = recv_line((SOCKET)fd, line, sizeof(line));
if (count != 5 && memcmp(line, "+OK\r\n", 5) != 0)
{
LOG(0, "redis: unexpected response from redis server: %s\n", line);
exit(1);
}
}
count = send((SOCKET)fd, "PING\r\n", 6, 0);
if (count != 6) {
LOG(0, "redis: send(ping) failed\n");
exit(1);
}
count = recv_line((SOCKET)fd, line, sizeof(line));
if (count != 7 && memcmp(line, "+PONG\r\n", 7) != 0) {
LOG(0, "redis: unexpected response from redis server: %s\n", line);
exit(1);
}
}
/****************************************************************************
****************************************************************************/
static void
redis_out_close(struct Output *out, FILE *fp)
{
ptrdiff_t fd = (ptrdiff_t)fp;
size_t count;
unsigned char line[1024];
UNUSEDPARM(out);
count = send((SOCKET)fd, "QUIT\r\n", 6, 0);
if (count != 6) {
LOG(0, "redis: send(quit) failed\n");
exit(1);
}
count = recv_line((SOCKET)fd, line, sizeof(line));
if ((count != 5 && memcmp(line, "+OK\r\n", 5) != 0) && (count != 4 && memcmp(line, ":0\r\n", 4) != 0)){
LOG(0, "redis: unexpected response from redis server: %s\n", line);
exit(1);
}
}
/****************************************************************************
****************************************************************************/
static void
redis_out_status(struct Output *out, FILE *fp, time_t timestamp,
int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl)
{
ptrdiff_t fd = (ptrdiff_t)fp;
char line[1024];
int line_length;
char ip_string[64];
char port_string[10];
int ip_string_length;
int port_string_length;
size_t count;
char values[64];
int values_length;
ipaddress_formatted_t fmt = ipaddress_fmt(ip);
ip_string_length = snprintf(ip_string, sizeof(ip_string), "%s", fmt.string);
port_string_length = snprintf(port_string, sizeof(port_string), "%u/%s", port, name_from_ip_proto(ip_proto));
/**3
$3
SET
$5
mykey
$7
myvalue
*/
/*
* KEY: "host"
* VALUE: ip
*/
snprintf(line, sizeof(line),
"*3\r\n"
"$4\r\nSADD\r\n"
"$%d\r\n%s\r\n"
"$%d\r\n%s\r\n"
,
4, "host",
ip_string_length, ip_string
);
count = send((SOCKET)fd, line, (int)strlen(line), 0);
if (count != strlen(line)) {
LOG(0, "redis: error sending data\n");
exit(1);
}
out->redis.outstanding++;
/*
* KEY: ip
* VALUE: port
*/
snprintf(line, sizeof(line),
"*3\r\n"
"$4\r\nSADD\r\n"
"$%d\r\n%s\r\n"
"$%d\r\n%s\r\n"
,
ip_string_length, ip_string,
port_string_length, port_string);
count = send((SOCKET)fd, line, (int)strlen(line), 0);
if (count != strlen(line)) {
LOG(0, "redis: error sending data\n");
exit(1);
}
out->redis.outstanding++;
/*
* KEY: ip:port
* VALUE: timestamp:status:reason:ttl
*/
values_length = snprintf(values, sizeof(values), "%u:%u:%u:%u",
(unsigned)timestamp, status, reason, ttl);
line_length = snprintf(line, sizeof(line),
"*3\r\n"
"$4\r\nSADD\r\n"
"$%d\r\n%s:%s\r\n"
"$%d\r\n%s\r\n"
,
ip_string_length + 1 + port_string_length,
ip_string, port_string,
values_length, values
);
count = send((SOCKET)fd, line, (int)line_length, 0);
if (count != (size_t)line_length) {
LOG(0, "redis: error sending data\n");
exit(1);
}
out->redis.outstanding++;
clean_response_queue(out, (SOCKET)fd);
}
/****************************************************************************
****************************************************************************/
static void
redis_out_banner(struct Output *out, FILE *fp, time_t timestamp,
ipaddress ip, unsigned ip_proto, unsigned port,
enum ApplicationProtocol proto, unsigned ttl,
const unsigned char *px, unsigned length)
{
UNUSEDPARM(ttl);
UNUSEDPARM(timestamp);
UNUSEDPARM(out);
UNUSEDPARM(fp);
UNUSEDPARM(ip);
UNUSEDPARM(ip_proto);
UNUSEDPARM(port);
UNUSEDPARM(proto);
UNUSEDPARM(px);
UNUSEDPARM(length);
}
/****************************************************************************
****************************************************************************/
const struct OutputType redis_output = {
"redis",
0,
redis_out_open,
redis_out_close,
redis_out_status,
redis_out_banner
};

117
src/out-tcp-services.c Normal file
View File

@ -0,0 +1,117 @@
#include "out-tcp-services.h"
#include <string.h>
#include <stdlib.h>
#ifndef WIN32
#include <netdb.h>
#else
#include <WinSock2.h>
#endif
#include <ctype.h>
/**
* This is a stupid hack to avoid dependencies. I want to minimize the dependence
* on network libraries. For example, I get a warning message on FreeBSD about
* a missing `htons()`. I could just add a system header, but then this increases
* dependencies on other things. Alternatively, I could just implement the
* function myself. So I chose that route.
*/
static unsigned short my_htons(unsigned port)
{
static const char test[3] = "\x11\x22";
if (*(unsigned short*)test == 0x1122)
return (unsigned short)(0xFFFF & port);
else
return (unsigned short)((port>>8)&0xFF) | ((port&0xFF)<<8);
}
#if _MSC_VER
#define strdup _strdup
#endif
static char *tcp_services[65536];
static char *udp_services[65536];
static char *oproto_services[256];
const char *
tcp_service_name(int port)
{
if (tcp_services[port])
return tcp_services[port];
#if defined(__linux__) && !defined(__TERMUX__)
int r;
struct servent result_buf;
struct servent *result;
char buf[2048];
r = getservbyport_r(my_htons(port), "tcp", &result_buf,buf, sizeof(buf), &result);
/* ignore ERANGE - if the result can't fit in 2k, just return unknown */
if (r != 0 || result == NULL)
return "unknown";
return tcp_services[port] = strdup(result_buf.s_name);
#else
{
struct servent *result;
result = getservbyport(my_htons((unsigned short)port), "tcp");
if (result == 0)
return "unknown";
return tcp_services[port] = strdup(result->s_name);
}
#endif
}
const char *
udp_service_name(int port)
{
if (udp_services[port])
return udp_services[port];
#if defined(__linux__) && !defined(__TERMUX__)
int r;
struct servent result_buf;
struct servent *result;
char buf[2048];
r = getservbyport_r(my_htons(port), "udp", &result_buf,buf, sizeof(buf), &result);
/* ignore ERANGE - if the result can't fit in 2k, just return unknown */
if (r != 0 || result == NULL)
return "unknown";
return udp_services[port] = strdup(result_buf.s_name);
#else
{
struct servent *result;
result = getservbyport(my_htons((unsigned short)port), "udp");
if (result == 0)
return "unknown";
return udp_services[port] = strdup(result->s_name);
}
#endif
}
const char *
oproto_service_name(int port)
{
if (oproto_services[port])
return oproto_services[port];
{
struct protoent *result;
result = getprotobynumber(port);
if (result == 0)
return "unknown";
return oproto_services[port] = strdup(result->p_name);
}
}

9
src/out-tcp-services.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef OUT_TCP_SERVICES_H
#define OUT_TCP_SERVICES_H
const char *tcp_service_name(int port);
const char *udp_service_name(int port);
const char *oproto_service_name(int protocol_number);
#endif

88
src/out-text.c Normal file
View File

@ -0,0 +1,88 @@
#include "output.h"
#include "masscan.h"
#include "masscan-app.h"
#include "masscan-status.h"
#include "unusedparm.h"
#include <ctype.h>
/****************************************************************************
****************************************************************************/
static void
text_out_open(struct Output *out, FILE *fp)
{
UNUSEDPARM(out);
fprintf(fp, "#masscan\n");
}
/****************************************************************************
****************************************************************************/
static void
text_out_close(struct Output *out, FILE *fp)
{
UNUSEDPARM(out);
fprintf(fp, "# end\n");
}
/****************************************************************************
****************************************************************************/
static void
text_out_status(struct Output *out, FILE *fp, time_t timestamp,
int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl)
{
ipaddress_formatted_t fmt = ipaddress_fmt(ip);
UNUSEDPARM(ttl);
UNUSEDPARM(reason);
UNUSEDPARM(out);
fprintf(fp, "%s %s %u %s %u\n",
status_string(status),
name_from_ip_proto(ip_proto),
port,
fmt.string,
(unsigned)timestamp
);
}
/*************************************** *************************************
****************************************************************************/
static void
text_out_banner(struct Output *out, FILE *fp, time_t timestamp,
ipaddress ip, unsigned ip_proto, unsigned port,
enum ApplicationProtocol proto, unsigned ttl,
const unsigned char *px, unsigned length)
{
char banner_buffer[MAX_BANNER_LENGTH];
ipaddress_formatted_t fmt = ipaddress_fmt(ip);
UNUSEDPARM(out);
UNUSEDPARM(ttl);
fprintf(fp, "%s %s %u %s %u %s %s\n",
"banner",
name_from_ip_proto(ip_proto),
port,
fmt.string,
(unsigned)timestamp,
masscan_app_to_string(proto),
normalize_string(px, length, banner_buffer, sizeof(banner_buffer))
);
}
/****************************************************************************
****************************************************************************/
const struct OutputType text_output = {
"txt",
0,
text_out_open,
text_out_close,
text_out_status,
text_out_banner
};

95
src/out-unicornscan.c Normal file
View File

@ -0,0 +1,95 @@
#include "output.h"
#include "masscan.h"
#include "masscan-app.h"
#include "masscan-status.h"
#include "unusedparm.h"
#include "out-tcp-services.h"
static void
unicornscan_out_open(struct Output *out, FILE *fp)
{
UNUSEDPARM(out);
fprintf(fp, "#masscan\n");
}
static void
unicornscan_out_close(struct Output *out, FILE *fp)
{
UNUSEDPARM(out);
fprintf(fp, "# end\n");
}
static void
unicornscan_out_status(struct Output *out, FILE *fp, time_t timestamp,
int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl)
{
ipaddress_formatted_t fmt = ipaddress_fmt(ip);
UNUSEDPARM(reason);
UNUSEDPARM(out);
UNUSEDPARM(timestamp);
if (ip_proto == 6) {
fprintf(fp,"TCP %s\t%16s[%5d]\t\tfrom %s ttl %-3d\n",
status_string(status),
tcp_service_name(port),
port,
fmt.string,
ttl);
} else {
/* unicornscan is TCP only, so just use grepable format for other protocols */
fprintf(fp, "Host: %s ()", fmt.string);
fprintf(fp, "\tPorts: %u/%s/%s/%s/%s/%s/%s\n",
port,
status_string(status), //"open", "closed"
name_from_ip_proto(ip_proto), //"tcp", "udp", "sctp"
"", //owner
"", //service
"", //SunRPC info
"" //Version info
);
}
}
/*************************************** *************************************
****************************************************************************/
static void
unicornscan_out_banner(struct Output *out, FILE *fp, time_t timestamp,
ipaddress ip, unsigned ip_proto, unsigned port,
enum ApplicationProtocol proto, unsigned ttl,
const unsigned char *px, unsigned length)
{ /* SYN only - no banner */
UNUSEDPARM(out);
UNUSEDPARM(ttl);
UNUSEDPARM(port);
UNUSEDPARM(fp);
UNUSEDPARM(timestamp);
UNUSEDPARM(ip);
UNUSEDPARM(ip_proto);
UNUSEDPARM(proto);
UNUSEDPARM(px);
UNUSEDPARM(length);
return;
}
/****************************************************************************
****************************************************************************/
const struct OutputType unicornscan_output = {
"uni",
0,
unicornscan_out_open,
unicornscan_out_close,
unicornscan_out_status,
unicornscan_out_banner
};

142
src/out-xml.c Normal file
View File

@ -0,0 +1,142 @@
#include "output.h"
#include "masscan-app.h"
#include "masscan-status.h"
#include "util-safefunc.h"
/****************************************************************************
****************************************************************************/
static void
xml_out_open(struct Output *out, FILE *fp)
{
//const struct Masscan *masscan = out->masscan;
fprintf(fp, "<?xml version=\"1.0\"?>\r\n");
fprintf(fp, "<!-- masscan v1.0 scan -->\r\n");
if (out->xml.stylesheet && out->xml.stylesheet[0]) {
fprintf(fp, "<?xml-stylesheet href=\"%s\" type=\"text/xsl\"?>\r\n",
out->xml.stylesheet);
}
fprintf(fp, "<nmaprun scanner=\"%s\" start=\"%u\" version=\"%s\" xmloutputversion=\"%s\">\r\n",
"masscan",
(unsigned)time(0),
"1.0-BETA",
"1.03" /* xml output version I copied from their site */
);
fprintf(fp, "<scaninfo type=\"%s\" protocol=\"%s\" />\r\n",
"syn", "tcp" );
}
/****************************************************************************
****************************************************************************/
static void
xml_out_close(struct Output *out, FILE *fp)
{
char buffer[256];
time_t now = time(0);
struct tm tm;
if (out->is_gmt)
safe_gmtime(&tm, &now);
else
safe_localtime(&tm, &now);
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm);
fprintf(fp,
"<runstats>\r\n"
"<finished time=\"%u\" timestr=\"%s\" elapsed=\"%u\" />\r\n"
"<hosts up=\"%" PRIu64 "\" down=\"%" PRIu64 "\" total=\"%" PRIu64 "\" />\r\n"
"</runstats>\r\n"
"</nmaprun>\r\n",
(unsigned)now, /* time */
buffer, /* timestr */
(unsigned)(now - out->rotate.last), /* elapsed */
out->counts.tcp.open,
out->counts.tcp.closed,
out->counts.tcp.open + out->counts.tcp.closed
);
}
/****************************************************************************
****************************************************************************/
static void
xml_out_status(struct Output *out, FILE *fp, time_t timestamp, int status,
ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl)
{
char reason_buffer[128];
ipaddress_formatted_t fmt = ipaddress_fmt(ip);
UNUSEDPARM(out);
fprintf(fp, "<host endtime=\"%u\">"
"<address addr=\"%s\" addrtype=\"ipv4\"/>"
"<ports>"
"<port protocol=\"%s\" portid=\"%u\">"
"<state state=\"%s\" reason=\"%s\" reason_ttl=\"%u\"/>"
"</port>"
"</ports>"
"</host>"
"\r\n",
(unsigned)timestamp,
fmt.string,
name_from_ip_proto(ip_proto),
port,
status_string(status),
reason_string(reason, reason_buffer, sizeof(reason_buffer)),
ttl
);
}
/****************************************************************************
****************************************************************************/
static void
xml_out_banner(struct Output *out, FILE *fp, time_t timestamp,
ipaddress ip, unsigned ip_proto, unsigned port,
enum ApplicationProtocol proto,
unsigned ttl,
const unsigned char *px, unsigned length)
{
char banner_buffer[MAX_BANNER_LENGTH];
const char *reason;
ipaddress_formatted_t fmt = ipaddress_fmt(ip);
switch (proto) {
case 6: reason = "syn-ack"; break;
default: reason = "response"; break;
}
UNUSEDPARM(out);
fprintf(fp, "<host endtime=\"%u\">"
"<address addr=\"%s\" addrtype=\"ipv4\"/>"
"<ports>"
"<port protocol=\"%s\" portid=\"%u\">"
"<state state=\"open\" reason=\"%s\" reason_ttl=\"%u\" />"
"<service name=\"%s\" banner=\"%s\"></service>"
"</port>"
"</ports>"
"</host>"
"\r\n",
(unsigned)timestamp,
fmt.string,
name_from_ip_proto(ip_proto),
port,
reason, ttl,
masscan_app_to_string(proto),
normalize_string(px, length, banner_buffer, sizeof(banner_buffer))
);
}
/****************************************************************************
****************************************************************************/
const struct OutputType xml_output = {
"xml",
0,
xml_out_open,
xml_out_close,
xml_out_status,
xml_out_banner
};

1003
src/output.c Normal file

File diff suppressed because it is too large Load Diff

198
src/output.h Normal file
View File

@ -0,0 +1,198 @@
#ifndef OUTPUT_H
#define OUTPUT_H
#include <stddef.h>
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include "massip-addr.h"
#include "stack-src.h"
#include "unusedparm.h"
#include "masscan-app.h"
#define MAX_BANNER_LENGTH 8192
struct Masscan;
struct Output;
enum ApplicationProtocol;
enum PortStatus;
/**
* Output plugins
*
* The various means for writing output are essentially plugins. As new methods
* are created, we just fill in a structure of function pointers.
* TODO: this needs to be a loadable DLL, but in the meantime, it's just
* internal structures.
*/
struct OutputType {
const char *file_extension;
void *(*create)(struct Output *out);
void (*open)(struct Output *out, FILE *fp);
void (*close)(struct Output *out, FILE *fp);
void (*status)(struct Output *out, FILE *fp,
time_t timestamp, int status,
ipaddress ip, unsigned ip_proto, unsigned port,
unsigned reason, unsigned ttl);
void (*banner)(struct Output *out, FILE *fp,
time_t timestamp, ipaddress ip, unsigned ip_proto,
unsigned port, enum ApplicationProtocol proto,
unsigned ttl,
const unsigned char *px, unsigned length);
};
/**
* Masscan creates one "output" structure per thread.
*/
struct Output
{
const struct Masscan *masscan;
char *filename;
struct stack_src_t src[8];
FILE *fp;
const struct OutputType *funcs;
unsigned format;
/**
* The timestamp when this scan started. This is preserved in output files
* because that's what nmap does, and a lot of tools parse this.
*/
time_t when_scan_started;
/**
* Whether we've started writing to a file yet. We are lazy writing
* the file header until we've actually go something to write
*/
unsigned is_virgin_file:1;
/**
* used by json output to test if the first record has been seen, in order
* to determine if it needs a , comma before the record
*/
unsigned is_first_record_seen:1;
struct {
time_t next;
time_t last;
unsigned period;
unsigned offset;
uint64_t filesize;
uint64_t bytes_written;
unsigned filecount; /* filesize rotates */
char *directory;
} rotate;
unsigned is_banner:1; /* --banners */
unsigned is_banner_rawudp:1; /* --rawudp */
unsigned is_gmt:1; /* --gmt */
unsigned is_interactive:1; /* echo to command line */
unsigned is_show_open:1; /* show open ports (default) */
unsigned is_show_closed:1; /* show closed ports */
unsigned is_show_host:1; /* show host status info, like up/down */
unsigned is_append:1; /* append to file */
struct {
struct {
uint64_t open;
uint64_t closed;
uint64_t banner;
} tcp;
struct {
uint64_t open;
uint64_t closed;
} udp;
struct {
uint64_t open;
uint64_t closed;
} sctp;
struct {
uint64_t echo;
uint64_t timestamp;
} icmp;
struct {
uint64_t open;
} arp;
struct {
uint64_t open;
uint64_t closed;
} oproto;
} counts;
struct {
ipaddress ip;
unsigned port;
char *password;
ptrdiff_t fd;
uint64_t outstanding;
unsigned state;
} redis;
struct {
char *stylesheet;
} xml;
};
const char *name_from_ip_proto(unsigned ip_proto);
const char *status_string(enum PortStatus x);
const char *reason_string(int x, char *buffer, size_t sizeof_buffer);
const char *normalize_string(const unsigned char *px, size_t length,
char *buf, size_t buf_len);
extern const struct OutputType text_output;
extern const struct OutputType unicornscan_output;
extern const struct OutputType xml_output;
extern const struct OutputType json_output;
extern const struct OutputType ndjson_output;
extern const struct OutputType certs_output;
extern const struct OutputType binary_output;
extern const struct OutputType null_output;
extern const struct OutputType redis_output;
extern const struct OutputType hostonly_output;
extern const struct OutputType grepable_output;
/**
* Creates an "output" object. This is called by the receive thread in order
* to send "status" information (open/closed ports) and "banners" to either
* the command-line or to files in specific formats, such as XML or Redis
* @param masscan
* The master configuration.
* @param thread_index
* When there are more than one receive threads, they are differentiated
* by this index number.
* @return
* an output object that must eventually be destroyed by output_destroy().
*/
struct Output *
output_create(const struct Masscan *masscan, unsigned thread_index);
void output_destroy(struct Output *output);
void output_report_status(struct Output *output, time_t timestamp,
int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl,
const unsigned char mac[6]);
typedef void (*OUTPUT_REPORT_BANNER)(
struct Output *output, time_t timestamp,
ipaddress ip, unsigned ip_proto, unsigned port,
unsigned proto, unsigned ttl,
const unsigned char *px, unsigned length);
void output_report_banner(
struct Output *output,
time_t timestamp,
ipaddress ip, unsigned ip_proto, unsigned port,
unsigned proto,
unsigned ttl,
const unsigned char *px, unsigned length);
/**
* Regression tests this unit.
* @return
* 0 on success, or positive integer on failure
*/
int
output_selftest(void);
#endif

216
src/pixie-backtrace.c Normal file
View File

@ -0,0 +1,216 @@
/*
When program crashes, print backtrace with line numbers
*/
#include "pixie-backtrace.h"
#include "unusedparm.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
char global_self[512] = "";
#if defined(__GLIBC__) && !defined(WIN32)
#include <unistd.h>
#include <execinfo.h>
#include <dlfcn.h>
#define BACKTRACE_SIZE 256
static void
handle_segfault(int sig)
{
void *func[BACKTRACE_SIZE];
char **symb = NULL;
int size;
printf("======================================================================\n");
printf(" Segmentation fault: please post this backtrace to:\n");
printf(" https://github.com/robertdavidgraham/masscan/issues\n");
printf("======================================================================\n");
size = backtrace(func, BACKTRACE_SIZE);
symb = backtrace_symbols(func, size);
while (size > 0) {
const char *symbol = symb[size - 1];
char foo[1024];
printf("%d: [%s]\n", size, symbol);
if (strstr(symbol, "[0x")) {
char *p = strstr(symbol, "[0x") + 1;
char *pp = strchr(p, ']');
snprintf(foo, sizeof(foo), "addr2line -p -i -f -e %s %.*s",
global_self,
(unsigned)(pp-p),
p);
if (system(foo) == -1)
printf("(addr2line missing)\n");
}
size --;
}
exit(1);
return;
}
/***************************************************************************
***************************************************************************/
void
pixie_backtrace_finish(void)
{
}
/***************************************************************************
***************************************************************************/
void
pixie_backtrace_init(const char *self)
{
ssize_t x;
/* Need to get a handle to the currently executing program. On Linux,
* we'll get this with /proc/self/exe, but on other platforms, we may
* need to do other things */
/* TODO: should we use readlink() to get the actual filename? */
#if defined(__linux__)
x = readlink("/proc/self/exe", global_self, sizeof(global_self));
#elif defined(__FreeBSD__)
x = readlink("/proc/curproc/file", global_self, sizeof(global_self));
#elif defined(__Solaris__)
x = readlink("/proc/self/path/a.out", global_self, sizeof(global_self));
#else
x = -1;
#endif
if (x == -1)
snprintf(global_self, sizeof(global_self), "%s", self);
signal(SIGSEGV, handle_segfault);
}
#elif defined(__MINGW32__)
void
pixie_backtrace_init(const char *self)
{
}
#elif defined(WIN32)
#include <Windows.h>
typedef struct _SYMBOL_INFO {
ULONG SizeOfStruct;
ULONG TypeIndex; // Type Index of symbol
ULONG64 Reserved[2];
ULONG Index;
ULONG Size;
ULONG64 ModBase; // Base Address of module containing this symbol
ULONG Flags;
ULONG64 Value; // Value of symbol, ValuePresent should be 1
ULONG64 Address; // Address of symbol including base address of module
ULONG Register; // register holding value or pointer to value
ULONG Scope; // scope of the symbol
ULONG Tag; // pdb classification
ULONG NameLen; // Actual length of name
ULONG MaxNameLen;
CHAR Name[1]; // Name of symbol
} SYMBOL_INFO, *PSYMBOL_INFO;
typedef BOOL (__stdcall *FUNC_SymInitialize)(
HANDLE hProcess,
PCSTR UserSearchPath,
BOOL fInvadeProcess
);
typedef BOOL (__stdcall * FUNC_SymFromAddr)(
HANDLE hProcess,
DWORD64 Address,
PDWORD64 Displacement,
PSYMBOL_INFO Symbol
);
struct _Dbg {
FUNC_SymInitialize SymInitialize;
FUNC_SymFromAddr SymFromAddr;
} Dbg;
void printStack( void );
void printStack( void )
{
unsigned int i;
void * stack[ 100 ];
unsigned short frames;
SYMBOL_INFO * symbol;
HANDLE process;
process = GetCurrentProcess();
if (Dbg.SymInitialize == NULL)
return;
if (Dbg.SymFromAddr == NULL)
return;
if (RtlCaptureStackBackTrace == NULL)
return;
Dbg.SymInitialize( process, NULL, TRUE );
frames = CaptureStackBackTrace( 0, 100, stack, NULL );
symbol = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 );
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof( SYMBOL_INFO );
for( i = 0; i < frames; i++ ) {
Dbg.SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol );
printf( "%u: %s - 0x%0X\n", frames - i - 1, symbol->Name, symbol->Address );
}
free( symbol );
}
static void
handle_segfault(int sig)
{
UNUSEDPARM(sig);
printf("======================================================================");
printf(" Segmentation fault: please post this backtrace to:\n");
printf(" https://github.com/robertdavidgraham/masscan/issues\n");
printf("======================================================================");
exit(1);
}
void
pixie_backtrace_init(const char *self)
{
self;
GetModuleFileNameA(NULL, global_self, sizeof(global_self));
{
HMODULE h;
h = LoadLibraryA("DbgHelp.dll");
if (h != NULL) {
//printf("found DbgHelp.dll\n");
Dbg.SymFromAddr = (FUNC_SymFromAddr)GetProcAddress(h, "SymFromAddr");
//if (Dbg.SymFromAddr) printf("found Dbg.SymFromAddr\n");
Dbg.SymInitialize = (FUNC_SymInitialize)GetProcAddress(h, "SymInitialize");
//if (Dbg.SymInitialize) printf("found Dbg.SymInitialize\n");
h = LoadLibraryA("Kernel32.dll");
if (GetProcAddress(NULL, "RtlCaptureStackBackTrace") != NULL)
; //printf("found Dbg.SymInitialize\n");
}
}
//signal(SIGSEGV, handle_segfault);
}
#else
void
pixie_backtrace_init(const char *self)
{
}
#endif

14
src/pixie-backtrace.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef PIXIE_BACKTRACE_H
#define PIXIE_BACKTRACE_H
/**
* Call this function at program startup in order to insert a signal handler
* that will be caught when the program crashes. This signal handler will
* print debug information to the console, such as the line numbers where
* the program crashes.
*/
void
pixie_backtrace_init(const char *self);
#endif

67
src/pixie-file.c Normal file
View File

@ -0,0 +1,67 @@
#include "pixie-file.h"
#if defined(WIN32)
#include <Windows.h>
#include <io.h>
#include <fcntl.h>
#define access _access
#else
#include <unistd.h>
#include <errno.h>
#endif
int
pixie_fopen_shareable(FILE **in_fp, const char *filename, unsigned is_append)
{
FILE *fp;
*in_fp = NULL;
#if defined(WIN32)
/* PORTABILITY: WINDOWS
* This bit of code deals with the fact that on Windows, fopen() opens
* a file so that it can't be moved. This code opens it a different
* way so that we can move it.
*
* NOTE: this is probably overkill, it appears that there is a better
* API _fsopen() that does what I want without all this nonsense.
*/
{
HANDLE hFile;
int fd;
/* The normal POSIX C functions lock the file */
/* int fd = open(filename, O_RDWR | O_CREAT, _S_IREAD | _S_IWRITE); */ /* Fails */
/* int fd = _sopen(filename, O_RDWR | O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE); */ /* Also fails */
/* We need to use WINAPI + _open_osfhandle to be able to use
file descriptors (instead of WINAPI handles) */
hFile = CreateFileA( filename,
GENERIC_WRITE | (is_append?FILE_APPEND_DATA:0),
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return -1;
}
fd = _open_osfhandle((intptr_t)hFile, _O_CREAT | _O_RDONLY | _O_TEMPORARY);
if (fd == -1) {
perror("_open_osfhandle");
return -1;
}
fp = _fdopen(fd, "w");
}
#else
fp = fopen(filename, is_append?"a":"w");
if (fp == NULL)
return errno;
#endif
*in_fp = fp;
return 0;
}

19
src/pixie-file.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef PIXIE_FILE_H
#define PIXIE_FILE_H
#include <stdio.h>
#if defined(WIN32)
#include <io.h>
#define access _access
#else
#include <unistd.h>
#endif
/**
* On Windows, files aren't shareable, so we need to have a portable function
* to open files that can be shared and renamed while they are still open.
*/
int
pixie_fopen_shareable(FILE **in_fp, const char *filename, unsigned is_append);
#endif

14
src/pixie-sockets.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef PIXIE_SOCKETS_H
#define PIXIE_SOCKETS_H
#include <stddef.h>
#if defined(WIN32)
#include <WinSock2.h>
#else
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
typedef int SOCKET;
#endif
#endif

218
src/pixie-threads.c Normal file
View File

@ -0,0 +1,218 @@
#define _GNU_SOURCE
#include "pixie-threads.h"
#if defined(WIN32)
#include <Windows.h>
#include <process.h>
#endif
#if defined(__GNUC__) && !defined(WIN32)
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
#include <errno.h>
#endif
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
#ifndef UNUSEDPARM
#ifdef _MSC_VER
#define UNUSEDPARM(x) x
#else
#define UNUSEDPARM(x)
#endif
#endif
/****************************************************************************
****************************************************************************/
void
pixie_cpu_raise_priority(void)
{
#if defined WIN32
DWORD_PTR result;
result = SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
if (result == 0) {
fprintf(stderr, "set_priority: returned error win32:%u\n", (unsigned)GetLastError());
}
#elif defined(__linux__) && defined(__GNUC__)
pthread_t thread = pthread_self();
pthread_attr_t thAttr;
int policy = 0;
int max_prio_for_policy = 0;
pthread_attr_init(&thAttr);
pthread_attr_getschedpolicy(&thAttr, &policy);
max_prio_for_policy = sched_get_priority_max(policy);
pthread_setschedprio(thread, max_prio_for_policy);
pthread_attr_destroy(&thAttr);
return;
#endif
}
/****************************************************************************
* Set the current thread (implicit) to run exclusively on the explicit
* process.
* http://en.wikipedia.org/wiki/Processor_affinity
****************************************************************************/
void
pixie_cpu_set_affinity(unsigned processor)
{
#if defined WIN32
DWORD_PTR mask;
DWORD_PTR result;
if (processor > 0)
processor--;
mask = ((size_t)1)<<processor;
//printf("mask(%u) = 0x%08x\n", processor, mask);
result = SetThreadAffinityMask(GetCurrentThread(), mask);
if (result == 0) {
fprintf(stderr, "set_affinity: returned error win32:%u\n", (unsigned)GetLastError());
}
#elif defined(__linux__) && defined(__GNUC__) && !defined(__TERMUX__)
int x;
pthread_t thread = pthread_self();
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(processor+1, &cpuset);
x = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
if (x != 0) {
fprintf(stderr, "set_affinity: returned error linux:%d\n", errno);
}
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
/* FIXME: add code here */
UNUSEDPARM(x);
#endif
}
/****************************************************************************
****************************************************************************/
unsigned
pixie_cpu_get_count(void)
{
#if defined WIN32
/* WINDOWS - use GetProcessAffinityMask() function */
size_t x;
#if defined _M_X64
DWORD_PTR process_mask = 0;
DWORD_PTR system_mask = 0;
#else
unsigned long process_mask = 0;
unsigned long system_mask = 0;
#endif
unsigned count = 0;
unsigned i;
x = GetProcessAffinityMask(GetCurrentProcess(), &process_mask, &system_mask);
if (x == 0) {
printf("GetProcessAffinityMask() returned error %u\n", (unsigned)GetLastError());
return 1;
}
for (i=0; i<32; i++) {
if (system_mask & 1)
count++;
system_mask >>= 1;
}
if (count == 0)
return 1;
else
return count;
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
/* BSD - use sysctl() function */
int x;
int mib[2];
size_t ncpu_length;
int ncpu = 1;
mib[0] = CTL_HW;
mib[1] = HW_NCPU;
ncpu_length = sizeof(ncpu);
x = sysctl(mib, 2, &ncpu, &ncpu_length, NULL, 0);
if (x == -1) {
perror("sysctl(HW_NCPU) failed");
return 1;
} else
return (unsigned)ncpu;
#elif defined linux
/* http://linux.die.net/man/2/sched_getaffinity */
{
pid_t pid;
cpu_set_t mask;
int err;
/* Gegret our process ID */
pid = getpid();
/* Get list of available CPUs for our system */
err = sched_getaffinity(pid, sizeof(mask), &mask);
if (err) {
perror("sched_getaffinity");
return 1;
} else {
#ifndef CPU_COUNT
return 1;
#else
return CPU_COUNT(&mask);
#endif
}
}
#elif defined(_SC_NPROCESSORS_ONLN)
/* Linux, Solaris, Mac OS>=10.4 */
return sysconf(_SC_NPROCESSORS_ONLN);
#elif defined(_SC_NPROC_ONLN)
/* Irix */
return sysconf(_SC_NPROC_ONLN);
#elif defined(MPC_GETNUMSPUS)
return mpctl(MPC_GETNUMSPUS, 0, 0);
#else
#error need to find CPU count
/* UNKNOWN - Well, we don't know the type of system which means we won't
* be able to start multiple threads anyway, so just return '1' */
return 1;
#endif
}
/****************************************************************************
****************************************************************************/
size_t
pixie_begin_thread(
void (*worker_thread)(void*),
unsigned flags,
void *worker_data)
{
#if defined(WIN32)
UNUSEDPARM(flags);
return _beginthread(worker_thread, 0, worker_data);
#else
typedef void *(*PTHREADFUNC)(void*);
pthread_t thread_id = 0;
pthread_create(
&thread_id,
NULL,
(PTHREADFUNC)worker_thread,
worker_data);
return (size_t)thread_id;
#endif
}
/****************************************************************************
****************************************************************************/
void pixie_thread_join(size_t thread_handle)
{
#if defined(WIN32)
WaitForSingleObject((HANDLE)thread_handle, INFINITE);
#else
void *p;
pthread_join((pthread_t)thread_handle, &p);
#endif
}

60
src/pixie-threads.h Normal file
View File

@ -0,0 +1,60 @@
#ifndef PORT_THREADS_H
#define PORT_THREADS_H
#include <stdio.h>
#include <stdint.h>
#if defined(_MSC_VER)
#include <intrin.h>
#endif
/**
* Returns the number of CPUs in the system, including virtual CPUs.
* On a single processor system, the number returned will be '1'.
* On a dual socket, dual-core per socket, hyperthreaded system, the
* count will be '8'.
*/
unsigned pixie_cpu_get_count(void);
/**
* Launch a thread
*/
size_t pixie_begin_thread(void (*worker_thread)(void*),
unsigned flags,
void *worker_data);
void pixie_thread_join(size_t thread_handle);
void pixie_cpu_set_affinity(unsigned processor);
void pixie_cpu_raise_priority(void);
void pixie_locked_subtract_u32(unsigned *lhs, unsigned rhs);
#if defined(_MSC_VER)
#define pixie_locked_add_u32(dst, src) _InterlockedExchangeAdd((volatile long*)(dst), (src))
#define pixie_locked_CAS32(dst, src, expected) (_InterlockedCompareExchange((volatile long*)dst, src, expected) == (expected))
#define pixie_locked_CAS64(dst, src, expected) (_InterlockedCompareExchange64((volatile long long*)dst, src, expected) == (expected))
#define rte_atomic32_cmpset(dst, exp, src) (_InterlockedCompareExchange((volatile long *)dst, (long)src, (long)exp)==(long)(exp))
#elif defined(__GNUC__)
#define pixie_locked_add_u32(dst, src) __sync_add_and_fetch((volatile int*)(dst), (int)(src));
#define rte_atomic32_cmpset(dst, expected, src) __sync_bool_compare_and_swap((volatile int*)(dst),(int)expected,(int)src)
#define pixie_locked_CAS32(dst, src, expected) __sync_bool_compare_and_swap((volatile int*)(dst),(int)expected,(int)src);
#define pixie_locked_CAS64(dst, src, expected) __sync_bool_compare_and_swap((volatile long long int*)(dst),(long long int)expected,(long long int)src);
#if !defined(__x86_64__) && !defined(__i386__)
#define rte_wmb() __sync_synchronize()
#define rte_rmb() __sync_synchronize()
#define rte_pause()
#else
#define rte_wmb() asm volatile("sfence;" : : : "memory")
#define rte_rmb() asm volatile("lfence;" : : : "memory")
#define rte_pause() asm volatile ("pause")
#endif
#else
unsigned pixie_locked_add_u32(volatile unsigned *lhs, unsigned rhs);
int pixie_locked_CAS32(volatile unsigned *dst, unsigned src, unsigned expected);
int pixie_locked_CAS64(volatile uint64_t *dst, uint64_t src, uint64_t expected);
#endif
#endif

291
src/pixie-timer.c Normal file
View File

@ -0,0 +1,291 @@
/*
portability: time
Since this program runs on both Linux and Windows, I need a portable
way to get a high-resolution timer.
NOTE: The time I'm looking for is "elapsed time" not "wall clock"
time. In other words, if you put the system to sleep and wake it
up a day later, this function should see no change, since time
wasn't elapsing while the system was asleep.
Reference:
http://www.python.org/dev/peps/pep-0418/#monotonic-clocks
http://www.brain-dump.org/blog/entry/107
*/
#include "pixie-timer.h"
#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifndef UNUSEDPARM
#ifdef __GNUC__
#define UNUSEDPARM(x)
#else
#define UNUSEDPARM(x) x=(x)
#endif
#endif
#if defined(WIN32)
#include <Windows.h>
LARGE_INTEGER
getFILETIMEoffset(void)
{
SYSTEMTIME s;
FILETIME f;
LARGE_INTEGER t;
s.wYear = 1970;
s.wMonth = 1;
s.wDay = 1;
s.wHour = 0;
s.wMinute = 0;
s.wSecond = 0;
s.wMilliseconds = 0;
SystemTimeToFileTime(&s, &f);
t.QuadPart = f.dwHighDateTime;
t.QuadPart <<= 32;
t.QuadPart |= f.dwLowDateTime;
return (t);
}
int
win_clock_gettime(int X, struct timeval *tv)
{
LARGE_INTEGER t;
FILETIME f;
double microseconds;
static LARGE_INTEGER offset;
static double frequencyToMicroseconds;
static int initialized = 0;
static BOOL usePerformanceCounter = 0;
UNUSEDPARM(X);
if (!initialized) {
LARGE_INTEGER performanceFrequency;
initialized = 1;
usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency);
if (usePerformanceCounter) {
QueryPerformanceCounter(&offset);
frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.;
} else {
offset = getFILETIMEoffset();
frequencyToMicroseconds = 10.;
}
}
if (usePerformanceCounter) QueryPerformanceCounter(&t);
else {
GetSystemTimeAsFileTime(&f);
t.QuadPart = f.dwHighDateTime;
t.QuadPart <<= 32;
t.QuadPart |= f.dwLowDateTime;
}
t.QuadPart -= offset.QuadPart;
microseconds = (double)t.QuadPart / frequencyToMicroseconds;
t.QuadPart = (LONGLONG)microseconds;
tv->tv_sec = (long)(t.QuadPart / 1000000);
tv->tv_usec = t.QuadPart % 1000000;
return (0);
}
uint64_t
pixie_gettime(void)
{
//struct timeval tv;
//clock_gettime(0, &tv);
uint64_t time1 = 0, freq = 0;
double seconds;
QueryPerformanceCounter((LARGE_INTEGER *) &time1);
QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
seconds = (double)time1/(double)freq;
return (uint64_t)(seconds * 1000000.0);
//return (uint64_t)tv.tv_sec * 1000000UL + tv.tv_usec;
}
uint64_t
pixie_nanotime(void)
{
uint64_t time1 = 0, freq = 0;
double seconds;
QueryPerformanceCounter((LARGE_INTEGER *) &time1);
QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
seconds = (double)time1/(double)freq;
return (uint64_t)(seconds * 1000000000.0);
}
void
pixie_mssleep(unsigned waitTime)
{
Sleep(waitTime);
}
void
pixie_usleep(uint64_t waitTime)
{
/*
uint64_t time1 = 0, time2 = 0, freq = 0;
QueryPerformanceCounter((LARGE_INTEGER *) &time1);
QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
do {
QueryPerformanceCounter((LARGE_INTEGER *) &time2);
} while((time2-time1) < waitTime);
*/
uint64_t start;
start = pixie_gettime();
if (waitTime > 1000)
Sleep((DWORD)(waitTime/1000));
while (pixie_gettime() - start < waitTime)
;
}
#elif defined(CLOCK_MONOTONIC)
#include <unistd.h>
void
pixie_mssleep(unsigned milliseconds)
{
pixie_usleep(milliseconds * 1000ULL);
}
void
pixie_usleep(uint64_t microseconds)
{
struct timespec ts;
struct timespec remaining;
int err;
ts.tv_sec = microseconds/1000000;
ts.tv_nsec = (microseconds%1000000) * 1000;
again:
err = nanosleep(&ts, &remaining);
if (err == -1 && errno == EINTR) {
memcpy(&ts, &remaining, sizeof(ts));
goto again;
}
//usleep(microseconds);
}
uint64_t
pixie_gettime(void)
{
int x;
struct timespec tv;
#if defined(CLOCK_UPTIME_RAW)
/* macOS: ignores time when suspended/sleep */
x = clock_gettime(CLOCK_UPTIME_RAW, &tv);
//#elif defined(CLOCK_MONOTONIC_RAW)
// x = clock_gettime(CLOCK_MONOTONIC_RAW, &tv);
#else
x = clock_gettime(CLOCK_MONOTONIC, &tv);
#endif
if (x != 0) {
printf("clock_gettime() err %d\n", errno);
}
return tv.tv_sec * 1000000 + tv.tv_nsec/1000;
}
uint64_t
pixie_nanotime(void)
{
int x;
struct timespec tv;
#ifdef CLOCK_MONOTONIC_RAW
x = clock_gettime(CLOCK_MONOTONIC_RAW, &tv);
#else
x = clock_gettime(CLOCK_MONOTONIC, &tv);
#endif
if (x != 0) {
printf("clock_gettime() err %d\n", errno);
}
return tv.tv_sec * 1000000000 + tv.tv_nsec;
}
#elif defined(__MACH__) || defined(__FreeBSD__) /* works for Apple */
#include <unistd.h>
#include <mach/mach_time.h>
void pixie_usleep(uint64_t microseconds)
{
struct timespec t;
t.tv_nsec = microseconds * 1000;
if (microseconds > 1000000)
t.tv_sec = microseconds/1000000;
else {
t.tv_sec = 0;
}
nanosleep(&t, 0);
//usleep(microseconds);
}
void
pixie_mssleep(unsigned milliseconds)
{
pixie_usleep(milliseconds * 1000ULL);
}
uint64_t
pixie_gettime(void)
{
return mach_absolute_time()/1000;
}
uint64_t
pixie_nanotime(void)
{
return mach_absolute_time();
}
#endif
/*
* Timing is incredibly importatn to masscan because we need to throttle
* how fast we spew packets. Every platofrm has slightly different timing
* even given standard APIs. We need to make sure we have an accurate
* timing function.
*
* This function tests betwe [0.9, 1.9] the expected results. I want something
* tight, like [0.99,1.01] (plus/minus 1%), but unfortunately automated
* testing platforms, like GitHub Actions, are overloaded, so when I wait
* for half a second, they might actually wait for 0.7 seconds, causing
* this test to fail. Thus, I have to greatly expand the range that passes
* this test.
*/
int pixie_time_selftest(void)
{
static const uint64_t duration = 456789;
uint64_t start, stop, elapsed;
start = pixie_gettime();
pixie_usleep(duration);
stop = pixie_gettime();
elapsed = stop - start;
if (elapsed < 0.9 * duration) {
fprintf(stderr, "timing error, long delay\n");
return 1;
}
if (1.9 * duration < elapsed) {
fprintf(stderr, "timing error, long delay %5.0f%%\n", elapsed*100.0/duration);
return 1;
}
return 0;
}

35
src/pixie-timer.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef PIXIE_TIMER_H
#define PIXIE_TIMER_H
#include <stdint.h>
/**
* The current time, in microseconds
*/
uint64_t pixie_gettime(void);
/**
* The current time, in nanoseconds
*/
uint64_t pixie_nanotime(void);
/**
* Wait the specified number of microseconds
*/
void pixie_usleep(uint64_t usec);
/**
* Wait the specified number of milliseconds
*/
void pixie_mssleep(unsigned milliseconds);
/**
* Do a self-test. Note that in some cases, this may
* actually fail when there is no problem. So far it hasn't, but I should
* probably add some code to fix this.
*/
int pixie_time_selftest(void);
#endif

41
src/proto-arp.c Normal file
View File

@ -0,0 +1,41 @@
#include "proto-arp.h"
#include "proto-preprocess.h"
#include "util-logger.h"
#include "output.h"
#include "masscan-status.h"
#include "unusedparm.h"
/***************************************************************************
* Process an ARP packet received in response to an ARP-scan.
***************************************************************************/
void
arp_recv_response(struct Output *out, time_t timestamp, const unsigned char *px,
unsigned length, struct PreprocessedInfo *parsed)
{
ipaddress ip_them = parsed->src_ip;
ipaddress_formatted_t fmt = ipaddress_fmt(ip_them);
UNUSEDPARM(length);
UNUSEDPARM(px);
LOG(3, "ARP %s = [%02X:%02X:%02X:%02X:%02X:%02X]\n",
fmt.string,
parsed->mac_src[0], parsed->mac_src[1], parsed->mac_src[2],
parsed->mac_src[3], parsed->mac_src[4], parsed->mac_src[5]);
output_report_status(
out,
timestamp,
PortStatus_Arp,
ip_them,
0, /* ip proto */
0,
0,
0,
parsed->mac_src);
}

11
src/proto-arp.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef PROTO_ARP_H
#define PROTO_ARP_H
#include <time.h>
struct Output;
struct PreprocessedInfo;
void
arp_recv_response(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed);
#endif

843
src/proto-banner1.c Normal file
View File

@ -0,0 +1,843 @@
/*
state machine for receiving banners
*/
#include "smack.h"
#include "rawsock-pcapfile.h"
#include "proto-preprocess.h"
#include "stack-tcp-api.h"
#include "proto-banner1.h"
#include "proto-http.h"
#include "proto-ssl.h"
#include "proto-smb.h"
#include "proto-ssh.h"
#include "proto-ftp.h"
#include "proto-smtp.h"
#include "proto-tcp-telnet.h"
#include "proto-tcp-rdp.h"
#include "proto-imap4.h"
#include "proto-pop3.h"
#include "proto-vnc.h"
#include "proto-memcached.h"
#include "proto-mc.h"
#include "proto-versioning.h"
#include "masscan-app.h"
#include "scripting.h"
#include "util-malloc.h"
#include "util-logger.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
struct Patterns patterns[] = {
{"\x00\x00" "**" "\xff" "SMB", 8, PROTO_SMB, SMACK_ANCHOR_BEGIN | SMACK_WILDCARDS, 0},
{"\x00\x00" "**" "\xfe" "SMB", 8, PROTO_SMB, SMACK_ANCHOR_BEGIN | SMACK_WILDCARDS, 0},
{"\x82\x00\x00\x00", 4, PROTO_SMB, SMACK_ANCHOR_BEGIN, 0}, /* Positive Session Response */
{"\x83\x00\x00\x01\x80", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN, 0}, /* Not listening on called name */
{"\x83\x00\x00\x01\x81", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN, 0}, /* Not listening for calling name */
{"\x83\x00\x00\x01\x82", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN, 0}, /* Called name not present */
{"\x83\x00\x00\x01\x83", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN, 0}, /* Called name present, but insufficient resources */
{"\x83\x00\x00\x01\x8f", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN, 0}, /* Unspecified error */
/* ...the remainder can be in any order */
{"{\x22", 2, PROTO_MC, 0, 0},
{"SSH-1.", 6, PROTO_SSH1, SMACK_ANCHOR_BEGIN, 0},
{"SSH-2.", 6, PROTO_SSH2, SMACK_ANCHOR_BEGIN, 0},
{"HTTP/1.", 7, PROTO_HTTP, SMACK_ANCHOR_BEGIN, 0},
{"220-", 4, PROTO_FTP, SMACK_ANCHOR_BEGIN, 0},
{"220 ", 4, PROTO_FTP, SMACK_ANCHOR_BEGIN, 1},
{"+OK ", 4, PROTO_POP3, SMACK_ANCHOR_BEGIN, 0},
{"* OK ", 5, PROTO_IMAP4, SMACK_ANCHOR_BEGIN, 0},
{"521 ", 4, PROTO_SMTP, SMACK_ANCHOR_BEGIN, 0},
{"\x16\x03\x00",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0},
{"\x16\x03\x01",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0},
{"\x16\x03\x02",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0},
{"\x16\x03\x03",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0},
{"\x15\x03\x00",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0},
{"\x15\x03\x01",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0},
{"\x15\x03\x02",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0},
{"\x15\x03\x03",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0},
{"RFB 000.000\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 1}, /* UltraVNC repeater mode */
{"RFB 003.003\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 3}, /* default version for everything */
{"RFB 003.005\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 3}, /* broken, same as 003.003 */
{"RFB 003.006\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 3}, /* broken, same as 003.003 */
{"RFB 003.007\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 7},
{"RFB 003.008\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 8},
{"RFB 003.889\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 8}, /* Apple's remote desktop, 003.007 */
{"RFB 003.009\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 8},
{"RFB 004.000\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 8}, /* Intel AMT KVM */
{"RFB 004.001\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 8}, /* RealVNC 4.6 */
{"RFB 004.002\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 8},
{"STAT pid ", 9, PROTO_MEMCACHED,SMACK_ANCHOR_BEGIN, 0}, /* memcached stat response */
{"\xff\xfb\x01\xff\xf0", 5, PROTO_TELNET, 0, 0},
{"\xff\xfb\x01\xff\xfb", 5, PROTO_TELNET, 0, 0},
{"\xff\xfb\x01\xff\xfc", 5, PROTO_TELNET, 0, 0},
{"\xff\xfb\x01\xff\xfd", 5, PROTO_TELNET, 0, 0},
{"\xff\xfb\x01\xff\xfe", 5, PROTO_TELNET, 0, 0},
{"\xff\xfb\x01\x0a\x0d", 5, PROTO_TELNET, 0, 0},
{"\xff\xfb\x01\x0d\x0a", 5, PROTO_TELNET, 0, 0},
{"\xff\xfb\x01\x0d\x0d", 5, PROTO_TELNET, 0, 0},
{"\xff\xfb\x01\x0a\x0a", 5, PROTO_TELNET, 0, 0},
{"\xff\xfb%\x25xff\xfb", 5, PROTO_TELNET, 0, 0},
{"\xff\xfb\x26\xff\xfd", 5, PROTO_TELNET, 0, 0},
{"\xff\xfd\x18\xff\xfd", 5, PROTO_TELNET, 0, 0},
{"\xff\xfd\x20\xff\xfd", 5, PROTO_TELNET, 0, 0},
{"\xff\xfd\x23\xff\xfd", 5, PROTO_TELNET, 0, 0},
{"\xff\xfd\x27\xff\xfd", 5, PROTO_TELNET, 0, 0},
{"\xff\xfb\x01\x1b[", 5, PROTO_TELNET, SMACK_ANCHOR_BEGIN, 0},
{"\xff\xfb\x01Input", 8, PROTO_TELNET, SMACK_ANCHOR_BEGIN, 0},
{"\xff\xfb\x01 ", 6, PROTO_TELNET, SMACK_ANCHOR_BEGIN, 0},
{"\xff\xfb\x01login", 8, PROTO_TELNET, SMACK_ANCHOR_BEGIN, 0},
{"login:", 6, PROTO_TELNET, SMACK_ANCHOR_BEGIN, 0},
{"password:", 9, PROTO_TELNET, SMACK_ANCHOR_BEGIN, 0},
{"\x03\x00\x00\x13\x0e\xd0\xbe\xef\x12\x34\x00\x02\x0f\x08\x00\x00\x00\x00\x00",
12, PROTO_RDP, SMACK_ANCHOR_BEGIN, 0},
{"\x03\x00\x00\x13\x0e\xd0\x00\x00\x12\x34\x00\x02\x0f\x08\x00\x00\x00\x00\x00",
12, PROTO_RDP, SMACK_ANCHOR_BEGIN, 0},
{0,0,0,0,0}
};
/***************************************************************************
***************************************************************************/
unsigned
banner1_parse(
const struct Banner1 *banner1,
struct StreamState *tcb_state,
const unsigned char *px, size_t length,
struct BannerOutput *banout,
struct stack_handle_t *socket)
{
size_t x;
unsigned offset = 0;
unsigned proto;
switch (tcb_state->app_proto) {
case PROTO_NONE:
case PROTO_HEUR:
x = smack_search_next(
banner1->smack,
&tcb_state->state,
px, &offset, (unsigned)length);
if (x == SMACK_NOT_FOUND)
proto = 0xFFFFFFFF;
else
proto = patterns[x].id;
if (proto != 0xFFFFFFFF
&& !(proto == PROTO_SSL3 && !tcb_state->is_sent_sslhello)) {
unsigned i;
/* re-read the stuff that we missed */
for (i=0; patterns[i].id && patterns[i].id != tcb_state->app_proto; i++)
;
/* Kludge: patterns look confusing, so add port info to the
* pattern */
switch (proto) {
case PROTO_FTP:
if (patterns[x].extra == 1) {
if (tcb_state->port == 25 || tcb_state->port == 587)
proto = PROTO_SMTP;
}
break;
case PROTO_VNC_RFB:
tcb_state->sub.vnc.version = (unsigned char)patterns[x].extra;
break;
}
tcb_state->app_proto = (unsigned short)proto;
/* reset the state back again */
tcb_state->state = 0;
/* If there is any data from a previous packet, re-parse that */
{
const unsigned char *s = banout_string(banout, PROTO_HEUR);
unsigned s_len = banout_string_length(banout, PROTO_HEUR);
if (s && s_len)
banner1_parse(
banner1,
tcb_state,
s, s_len,
banout,
socket);
}
banner1_parse(
banner1,
tcb_state,
px, length,
banout,
socket);
} else {
banout_append(banout, PROTO_HEUR, px, length);
}
break;
case PROTO_FTP:
banner_ftp.parse( banner1,
banner1->http_fields,
tcb_state,
px, length,
banout,
socket);
break;
case PROTO_SMTP:
banner_smtp.parse( banner1,
banner1->http_fields,
tcb_state,
px, length,
banout,
socket);
break;
case PROTO_TELNET:
banner_telnet.parse( banner1,
banner1->http_fields,
tcb_state,
px, length,
banout,
socket);
break;
case PROTO_RDP:
banner_rdp.parse( banner1,
banner1->http_fields,
tcb_state,
px, length,
banout,
socket);
break;
case PROTO_POP3:
banner_pop3.parse( banner1,
banner1->http_fields,
tcb_state,
px, length,
banout,
socket);
break;
case PROTO_IMAP4:
banner_imap4.parse(banner1,
banner1->http_fields,
tcb_state,
px, length,
banout,
socket);
break;
case PROTO_SSH1:
case PROTO_SSH2:
/* generic text-based parser
* TODO: in future, need to split these into separate protocols,
* especially when binary parsing is added to SSH */
banner_ssh.parse( banner1,
banner1->http_fields,
tcb_state,
px, length,
banout,
socket);
break;
case PROTO_HTTP:
banner_http.parse(
banner1,
banner1->http_fields,
tcb_state,
px, length,
banout,
socket);
break;
case PROTO_SSL3:
banner_ssl.parse(
banner1,
banner1->http_fields,
tcb_state,
px, length,
banout,
socket);
break;
case PROTO_SMB:
banner_smb1.parse(
banner1,
banner1->http_fields,
tcb_state,
px, length,
banout,
socket);
break;
case PROTO_VNC_RFB:
banner_vnc.parse( banner1,
banner1->http_fields,
tcb_state,
px, length,
banout,
socket);
break;
case PROTO_MEMCACHED:
banner_memcached.parse( banner1,
banner1->http_fields,
tcb_state,
px, length,
banout,
socket);
break;
case PROTO_SCRIPTING:
banner_scripting.parse( banner1,
banner1->http_fields,
tcb_state,
px, length,
banout,
socket);
break;
case PROTO_VERSIONING:
banner_versioning.parse( banner1,
banner1->http_fields,
tcb_state,
px, length,
banout,
socket);
break;
case PROTO_MC:
banner_mc.parse(
banner1,
banner1->http_fields,
tcb_state,
px, length,
banout,
socket);
break;
default:
fprintf(stderr, "banner1: internal error\n");
break;
}
return tcb_state->app_proto;
}
/*
* Simple banners with hello probes from nmap-service-probes
*/
static const char
genericlines_hello[] = "\r\n\r\n";
struct ProtocolParserStream banner_genericlines = {
"banner-GenericLines", 1098, genericlines_hello, sizeof(genericlines_hello) - 1, 0,
NULL,
NULL,
NULL,
};
static const char
x11_hello[] = "\x6C\0\x0B\0\0\0\0\0\0\0\0\0";
struct ProtocolParserStream banner_x11 = {
"banner-X11Probe", 6000, x11_hello, sizeof(x11_hello) - 1, 0,
NULL,
NULL,
NULL,
};
static const char
javarmi_hello[] = "\x4a\x52\x4d\x49\0\x02\x4b";
struct ProtocolParserStream banner_javarmi = {
"banner-JavaRMI", 1098, javarmi_hello, sizeof(javarmi_hello) - 1, 0,
NULL,
NULL,
NULL,
};
static const char
mongodb_hello[] = "\x41\0\0\0\x3a\x30\0\0\xff\xff\xff\xff\xd4\x07\0\0\0\0\0\0test.$cmd\0\0\0\0\0\xff\xff\xff\xff\x1b\0\0\0\x01serverStatus\0\0\0\0\0\0\0\xf0\x3f\0";
struct ProtocolParserStream banner_mongodb = {
"banner-mongodb", 27017, mongodb_hello, sizeof(mongodb_hello) - 1, 0,
NULL,
NULL,
NULL,
};
static const char
kerberos_hello[] = "\0\0\0\x71\x6a\x81\x6e\x30\x81\x6b\xa1\x03\x02\x01\x05\xa2\x03\x02\x01\x0a\xa4\x81\x5e\x30\x5c\xa0\x07\x03\x05\0\x50\x80\0\x10\xa2\x04\x1b\x02NM\xa3\x17\x30\x15\xa0\x03\x02\x01\0\xa1\x0e\x30\x0c\x1b\x06krbtgt\x1b\x02NM\xa5\x11\x18\x0f""19700101000000Z\xa7\x06\x02\x04\x1f\x1e\xb9\xd9\xa8\x17\x30\x15\x02\x01\x12\x02\x01\x11\x02\x01\x10\x02\x01\x17\x02\x01\x01\x02\x01\x03\x02\x01\x02";
struct ProtocolParserStream banner_kerberos = {
"banner-Kerberos", 88, kerberos_hello, sizeof(kerberos_hello) - 1, 0,
NULL,
NULL,
NULL,
};
static const char
dicom_hello[] = "\x01\x00\x00\x00\x00\xcd\x00\x01\x00\x00""ANY-SCP ECHOSCU 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x15""1.2.840.10008.3.1.1.1 \x00\x00.\x01\x00\x00\x00""0\x00\x00\x11""1.2.840.10008.1.1@\x00\x00\x11""1.2.840.10008.1.2P\x00\x00:Q\x00\x00\x04\x00\x00@\x00R\x00\x00\x1b""1.2.276.0.7230010.3.0.3.6.2U\x00\x00\x0fOFFIS_DCMTK_362";
struct ProtocolParserStream banner_dicom = {
"banner-dicom", 104, dicom_hello, sizeof(dicom_hello) - 1, 0,
NULL,
NULL,
NULL,
};
static const char
ldap_hello[] = "\x30\x84\x00\x00\x00\x2d\x02\x01\x07\x63\x84\x00\x00\x00\x24\x04\x00\x0a\x01\x00\x0a\x01\x00\x02\x01\x00\x02\x01\x64\x01\x01\x00\x87\x0b\x6f\x62\x6a\x65\x63\x74\x43\x6c\x61\x73\x73\x30\x84\x00\x00\x00\x00";
struct ProtocolParserStream banner_ldap = {
"banner-LDAPSearchReq", 389, ldap_hello, sizeof(ldap_hello) - 1, 0,
NULL,
NULL,
NULL,
};
static const char
sip_hello[] = "OPTIONS sip:nm SIP/2.0\r\nVia: SIP/2.0/TCP nm;branch=foo\r\nFrom: <sip:nm@nm>;tag=root\r\nTo: <sip:nm2@nm2>\r\nCall-ID: 50000\r\nCSeq: 42 OPTIONS\r\nMax-Forwards: 70\r\nContent-Length: 0\r\nContact: <sip:nm@nm>\r\nAccept: application/sdp\r\n\r\n";
struct ProtocolParserStream banner_sip = {
"banner-SIPOptions", 5060, sip_hello, sizeof(sip_hello) - 1, 0,
NULL,
NULL,
NULL,
};
static const char
rtsp_hello[] = "OPTIONS / RTSP/1.0\r\n\r\n";
struct ProtocolParserStream banner_rtsp = {
"banner-RTSPRequest", 554, rtsp_hello, sizeof(rtsp_hello) - 1, 0,
NULL,
NULL,
NULL,
};
static const char
rpc_hello[] = "\x80\0\0\x28\x72\xFE\x1D\x13\0\0\0\0\0\0\0\x02\0\x01\x86\xA0\0\x01\x97\x7C\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
struct ProtocolParserStream banner_rpc = {
"banner-RPCCheck", 111, rpc_hello, sizeof(rpc_hello) - 1, 0,
NULL,
NULL,
NULL,
};
static const char
dns_hello[] = "\0\x1E\0\x06\x01\0\0\x01\0\0\0\0\0\0\x07version\x04""bind\0\0\x10\0\x03";
struct ProtocolParserStream banner_dns = {
"banner-DNSVersionBindReqTCP", 53, dns_hello, sizeof(dns_hello) - 1, 0,
NULL,
NULL,
NULL,
};
static const char
docker_hello[] = "GET /version HTTP/1.1\r\n\r\n";
struct ProtocolParserStream banner_docker = {
"banner-docker", 2375, docker_hello, sizeof(docker_hello) - 1, 0,
NULL,
NULL,
NULL,
};
static const char
redis_hello[] = "*1\r\n$4\r\ninfo\r\n";
struct ProtocolParserStream banner_redis = {
"banner-redis-server", 6379, redis_hello, sizeof(redis_hello) - 1, 0,
NULL,
NULL,
NULL,
};
static const char
notes_rpc_hello[] = "\x3A\x00\x00\x00\x2F\x00\x00\x00\x02\x00\x00\x40\x02\x0F\x00\x01\x00\x3D\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x2F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x1F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
struct ProtocolParserStream banner_notes_rpc = {
"banner-NotesRPC", 6379, notes_rpc_hello, sizeof(notes_rpc_hello) - 1, 0,
NULL,
NULL,
NULL,
};
static const char
ms_sql_s_hello[] = "\x12\x01\x00\x34\x00\x00\x00\x00\x00\x00\x15\x00\x06\x01\x00\x1b\x00\x01\x02\x00\x1c\x00\x0c\x03\x00\x28\x00\x04\xff\x08\x00\x01\x55\x00\x00\x00\x4d\x53\x53\x51\x4c\x53\x65\x72\x76\x65\x72\x00\x48\x0f\x00\x00";
struct ProtocolParserStream banner_ms_sql_s = {
"banner-ms-sql-s", 6379, ms_sql_s_hello, sizeof(ms_sql_s_hello) - 1, 0,
NULL,
NULL,
NULL,
};
static const char
afp_hello[] = "\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x0f\x00";
struct ProtocolParserStream banner_afp = {
"banner-afp", 548, afp_hello, sizeof(afp_hello) - 1, 0,
NULL,
NULL,
NULL,
};
/***************************************************************************
* Create the --banners systems
***************************************************************************/
struct Banner1 *
banner1_create(void)
{
struct Banner1 *b;
unsigned i;
b = CALLOC(1, sizeof(*b));
/*
* This creates a pattern-matching blob for heuristically determining
* a protocol that runs on wrong ports, such as how FTP servers
* often respond with "220 " or VNC servers respond with "RFB".
*/
b->smack = smack_create("banner1", SMACK_CASE_INSENSITIVE);
for (i=0; patterns[i].pattern; i++)
smack_add_pattern(
b->smack,
patterns[i].pattern,
patterns[i].pattern_length,
i,
patterns[i].is_anchored);
smack_compile(b->smack);
/*
* [TODO] These need to be moved into the 'init' functions
*/
b->payloads.tcp[80] = &banner_http;
b->payloads.tcp[8080] = &banner_http;
b->payloads.tcp[139] = (void*)&banner_smb0;
b->payloads.tcp[445] = (void*)&banner_smb1;
b->payloads.tcp[8530] = (void*)&banner_http; /* WSUS */
b->payloads.tcp[8531] = (void*)&banner_ssl; /* WSUS/s */
/* https://www.nomotion.net/blog/sharknatto/ */
b->payloads.tcp[49955] = (void*)&banner_ssl; /* AT&T box */
b->payloads.tcp[443] = (void*)&banner_ssl; /* HTTP/s */
b->payloads.tcp[465] = (void*)&banner_ssl; /* SMTP/s */
b->payloads.tcp[990] = (void*)&banner_ssl; /* FTP/s */
b->payloads.tcp[991] = (void*)&banner_ssl;
b->payloads.tcp[992] = (void*)&banner_ssl; /* Telnet/s */
b->payloads.tcp[993] = (void*)&banner_ssl; /* IMAP4/s */
b->payloads.tcp[994] = (void*)&banner_ssl;
b->payloads.tcp[995] = (void*)&banner_ssl; /* POP3/s */
b->payloads.tcp[2083] = (void*)&banner_ssl; /* cPanel - SSL */
b->payloads.tcp[2087] = (void*)&banner_ssl; /* WHM - SSL */
b->payloads.tcp[2096] = (void*)&banner_ssl; /* cPanel webmail - SSL */
b->payloads.tcp[8443] = (void*)&banner_ssl; /* Plesk Control Panel - SSL */
b->payloads.tcp[9050] = (void*)&banner_ssl; /* Tor */
b->payloads.tcp[8140] = (void*)&banner_ssl; /* puppet */
b->payloads.tcp[11211] = (void*)&banner_memcached;
b->payloads.tcp[23] = (void*)&banner_telnet;
b->payloads.tcp[3389] = (void*)&banner_rdp;
b->payloads.tcp[1098] = (void*)&banner_javarmi;
b->payloads.tcp[1099] = (void*)&banner_javarmi;
for (i=0; i < 20; i++) {
b->payloads.tcp[6000 + i] = (void*)&banner_x11;
}
b->payloads.tcp[88] = (void*)&banner_kerberos;
b->payloads.tcp[9001] = (void*)&banner_mongodb;
b->payloads.tcp[27017] = (void*)&banner_mongodb;
b->payloads.tcp[49153] = (void*)&banner_mongodb;
b->payloads.tcp[104] = (void*)&banner_dicom;
b->payloads.tcp[2345] = (void*)&banner_dicom;
b->payloads.tcp[2761] = (void*)&banner_dicom;
b->payloads.tcp[2762] = (void*)&banner_dicom;
b->payloads.tcp[4242] = (void*)&banner_dicom;
b->payloads.tcp[11112] = (void*)&banner_dicom;
b->payloads.tcp[256] = (void*)&banner_ldap;
b->payloads.tcp[257] = (void*)&banner_ldap;
b->payloads.tcp[389] = (void*)&banner_ldap;
b->payloads.tcp[390] = (void*)&banner_ldap;
b->payloads.tcp[1702] = (void*)&banner_ldap;
b->payloads.tcp[3268] = (void*)&banner_ldap;
b->payloads.tcp[3892] = (void*)&banner_ldap;
b->payloads.tcp[11711] = (void*)&banner_ldap;
/* LDAP/s */
b->payloads.tcp[636] = (void*)&banner_ssl;
b->payloads.tcp[637] = (void*)&banner_ssl;
b->payloads.tcp[3269] = (void*)&banner_ssl;
b->payloads.tcp[11712] = (void*)&banner_ssl;
b->payloads.tcp[406] = (void*)&banner_sip;
b->payloads.tcp[5060] = (void*)&banner_sip;
b->payloads.tcp[8081] = (void*)&banner_sip;
b->payloads.tcp[31337] = (void*)&banner_sip;
/* SIP/s */
b->payloads.tcp[5061] = (void*)&banner_ssl;
b->payloads.tcp[554] = (void*)&banner_rtsp;
b->payloads.tcp[8554] = (void*)&banner_rtsp;
/* RTSP/s */
b->payloads.tcp[322] = (void*)&banner_ssl;
b->payloads.tcp[111] = (void*)&banner_rpc;
b->payloads.tcp[2049] = (void*)&banner_rpc;
b->payloads.tcp[53] = (void*)&banner_dns;
b->payloads.tcp[135] = (void*)&banner_dns;
b->payloads.tcp[50000] = (void*)&banner_dns;
b->payloads.tcp[50001] = (void*)&banner_dns;
b->payloads.tcp[50002] = (void*)&banner_dns;
b->payloads.tcp[2375] = (void*)&banner_docker;
/* Docker/s */
b->payloads.tcp[2376] = (void*)&banner_ssl;
b->payloads.tcp[2379] = (void*)&banner_docker;
b->payloads.tcp[2380] = (void*)&banner_docker;
b->payloads.tcp[6379] = (void*)&banner_redis;
b->payloads.tcp[130] = (void*)&banner_notes_rpc;
b->payloads.tcp[427] = (void*)&banner_notes_rpc;
b->payloads.tcp[1352] = (void*)&banner_notes_rpc;
b->payloads.tcp[1972] = (void*)&banner_notes_rpc;
b->payloads.tcp[7171] = (void*)&banner_notes_rpc;
b->payloads.tcp[8728] = (void*)&banner_notes_rpc;
b->payloads.tcp[22001] = (void*)&banner_notes_rpc;
b->payloads.tcp[1433] = (void*)&banner_ms_sql_s;
/* AFP */
b->payloads.tcp[548] = (void*)&banner_afp;
/*
* This goes down the list of all the TCP protocol handlers and initializes
* them.
*/
banner_ftp.init(b);
banner_http.init(b);
banner_imap4.init(b);
banner_memcached.init(b);
banner_pop3.init(b);
banner_smtp.init(b);
banner_ssh.init(b);
banner_ssl.init(b);
banner_ssl_12.init(b);
banner_smb0.init(b);
banner_smb1.init(b);
banner_telnet.init(b);
banner_rdp.init(b);
banner_vnc.init(b);
banner_mc.init(b);
/* scripting/versioning come after the rest */
//banner_scripting.init(b);
//banner_versioning.init(b);
return b;
}
/***************************************************************************
***************************************************************************/
void
banner1_destroy(struct Banner1 *b)
{
if (b == NULL)
return;
if (b->smack)
smack_destroy(b->smack);
if (b->http_fields)
smack_destroy(b->http_fields);
free(b);
}
/***************************************************************************
* Test the banner1 detection system by throwing random frames at it
***************************************************************************/
void
banner1_test(const char *filename)
{
struct PcapFile *cap;
unsigned link_type;
cap = pcapfile_openread(filename);
if (cap == NULL) {
fprintf(stderr, "%s: can't open capture file\n", filename);
return;
}
link_type = pcapfile_datalink(cap);
for (;;) {
int packets_read;
unsigned secs;
unsigned usecs;
unsigned origlength;
unsigned length;
unsigned char px[65536];
struct PreprocessedInfo parsed;
unsigned x;
packets_read = pcapfile_readframe(
cap, /* capture dump file */
&secs, &usecs,
&origlength, &length,
px, sizeof(px));
if (packets_read == 0)
break;
x = preprocess_frame(px, length, link_type, &parsed);
if (x == 0)
continue;
}
pcapfile_close(cap);
}
/***************************************************************************
***************************************************************************/
int
banner1_selftest()
{
unsigned i;
struct Banner1 *b;
struct StreamState tcb_state[1];
const unsigned char *px;
unsigned length;
struct BannerOutput banout[1];
static const char *http_header =
"HTTP/1.0 302 Redirect\r\n"
"Date: Tue, 03 Sep 2013 06:50:01 GMT\r\n"
"Connection: close\r\n"
"Via: HTTP/1.1 ir14.fp.bf1.yahoo.com (YahooTrafficServer/1.2.0.13 [c s f ])\r\n"
"Server: YTS/1.20.13\r\n"
"Cache-Control: no-store\r\n"
"Content-Type: text/html\r\n"
"Content-Language: en\r\n"
"Location: http://failsafe.fp.yahoo.com/404.html\r\n"
"Content-Length: 227\r\n"
"\r\n<title>hello</title>\n";
px = (const unsigned char *)http_header;
length = (unsigned)strlen(http_header);
LOG(1, "[ ] banners: selftesting\n");
/*
* First, test the "banout" subsystem
*/
if (banout_selftest() != 0) {
fprintf(stderr, "banout: failed\n");
return 1;
}
/*
* Test one character at a time
*/
b = banner1_create();
banout_init(banout);
memset(tcb_state, 0, sizeof(tcb_state[0]));
for (i=0; i<length; i++) {
struct stack_handle_t more = {0,0};
banner1_parse(
b,
tcb_state,
px+i, 1,
banout,
&more);
}
{
const unsigned char *s = banout_string(banout, PROTO_HTTP);
if (memcmp(s, "HTTP/1.0 302", 11) != 0) {
printf("banner1: test failed\n");
return 1;
}
}
banout_release(banout);
banner1_destroy(b);
/*
* Test whole buffer
*/
b = banner1_create();
memset(tcb_state, 0, sizeof(tcb_state[0]));
banner1_parse(
b,
tcb_state,
px, length,
banout,
0);
banner1_destroy(b);
/*if (memcmp(banner, "Via:HTTP/1.1", 11) != 0) {
printf("banner1: test failed\n");
return 1;
}*/
{
int x = 0;
x = banner_ssl.selftest();
if (x) {
fprintf(stderr, "SSL banner: selftest failed\n");
return 1;
}
x = banner_ssl_12.selftest();
if (x) {
fprintf(stderr, "SSL banner: selftest failed\n");
return 1;
}
x = banner_smb1.selftest();
if (x) {
fprintf(stderr, "SMB banner: selftest failed\n");
return 1;
}
x = banner_http.selftest();
if (x) {
fprintf(stderr, "HTTP banner: selftest failed\n");
return 1;
}
x = banner_telnet.selftest();
if (x) {
fprintf(stderr, "Telnet banner: selftest failed\n");
return 1;
}
x = banner_rdp.selftest();
if (x) {
fprintf(stderr, "RDP banner: selftest failed\n");
return 1;
}
if (x)
goto failure;
else
goto success;
}
success:
LOG(1, "[+] banners: success\n");
return 0;
failure:
LOG(1, "[-] banners: failure\n");
return 1;
}

360
src/proto-banner1.h Normal file
View File

@ -0,0 +1,360 @@
#ifndef PROTO_BANNER1_H
#define PROTO_BANNER1_H
#include <stdint.h>
#include <stdio.h>
#include "masscan-app.h"
#include "proto-banout.h"
#include "proto-x509.h"
#include "proto-spnego.h"
struct stack_handle_t;
struct Banner1;
struct StreamState;
typedef void (*BannerParser)(
const struct Banner1 *banner1,
void *banner1_private,
struct StreamState *stream_state,
const unsigned char *px, size_t length,
struct BannerOutput *banout,
struct stack_handle_t *socket);
struct Banner1
{
struct lua_State *L;
struct SMACK *smack;
struct SMACK *http_fields;
struct SMACK *html_fields;
struct SMACK *memcached_responses;
struct SMACK *memcached_stats;
unsigned is_capture_html:1;
unsigned is_capture_cert:1;
unsigned is_capture_servername:1;
unsigned is_capture_heartbleed:1;
unsigned is_capture_ticketbleed:1;
unsigned is_heartbleed:1;
unsigned is_ticketbleed:1;
unsigned is_poodle_sslv3:1;
struct {
const struct ProtocolParserStream *tcp[65536];
} payloads;
BannerParser parser[PROTO_end_of_list];
};
struct BannerBase64
{
unsigned state:2;
unsigned temp:24;
};
struct SSL_SERVER_HELLO {
unsigned state;
unsigned remaining;
unsigned timestamp;
unsigned short cipher_suite;
unsigned short ext_tag;
unsigned short ext_remaining;
unsigned char compression_method;
unsigned char version_major;
unsigned char version_minor;
};
struct SSL_SERVER_CERT {
unsigned state;
unsigned remaining;
struct {
unsigned remaining;
} sub;
struct CertDecode x509;
};
struct SSL_SERVER_ALERT {
unsigned char level;
unsigned char description;
};
struct SSLRECORD {
unsigned char type;
unsigned char version_major;
unsigned char version_minor;
struct {
unsigned state;
unsigned char type;
unsigned remaining;
} handshake;
union {
struct {
/* all these structs should start with state */
unsigned state;
} all;
struct SSL_SERVER_HELLO server_hello;
struct SSL_SERVER_CERT server_cert;
struct SSL_SERVER_ALERT server_alert;
} x;
};
struct PIXEL_FORMAT {
unsigned short red_max;
unsigned short green_max;
unsigned short blue_max;
unsigned char red_shift;
unsigned char green_shift;
unsigned char blue_shift;
unsigned char bits_per_pixel;
unsigned char depth;
unsigned big_endian_flag:1;
unsigned true_colour_flag:1;
};
struct VNCSTUFF {
unsigned sectype;
unsigned char version;
unsigned char len;
unsigned short width;
unsigned short height;
struct PIXEL_FORMAT pixel;
};
struct FTPSTUFF {
unsigned code;
unsigned is_last:1;
};
struct MCSTUFF {
char * banmem;
size_t totalLen;
size_t imgstart;
size_t imgend;
int brackcount;
};
struct SMTPSTUFF {
unsigned code;
unsigned is_last:1;
};
struct POP3STUFF {
unsigned code;
unsigned is_last:1;
};
struct MEMCACHEDSTUFF {
unsigned match;
};
struct Smb72_Negotiate {
uint16_t DialectIndex;
uint16_t SecurityMode;
uint64_t SystemTime;
uint32_t SessionKey;
uint32_t Capabilities;
uint16_t ServerTimeZone;
uint8_t ChallengeLength;
uint8_t ChallengeOffset;
};
struct Smb73_Setup {
uint16_t BlobLength;
uint16_t BlobOffset;
};
struct SMBSTUFF {
unsigned nbt_state;
unsigned char nbt_type;
unsigned char nbt_flags;
unsigned is_printed_ver:1;
unsigned is_printed_guid:1;
unsigned is_printed_time:1;
unsigned is_printed_boottime:1;
unsigned nbt_length;
unsigned nbt_err;
union {
struct {
unsigned char command;
unsigned status;
unsigned char flags1;
unsigned short flags2;
unsigned pid;
unsigned char signature[8];
unsigned short tid;
unsigned short uid;
unsigned short mid;
unsigned short param_length;
unsigned short param_offset;
unsigned short byte_count;
unsigned short byte_offset;
unsigned short byte_state;
unsigned short unicode_char;
} smb1;
struct {
unsigned seqno;
unsigned short header_length;
unsigned short offset;
unsigned short state;
unsigned short opcode;
unsigned short struct_length;
unsigned is_dynamic:1;
unsigned char flags;
unsigned ntstatus;
unsigned number;
unsigned short blob_offset;
unsigned short blob_length;
} smb2;
} hdr;
union {
struct Smb72_Negotiate negotiate;
struct Smb73_Setup setup;
struct {
uint64_t current_time;
uint64_t boot_time;
} negotiate2;
} parms;
struct SpnegoDecode spnego;
};
struct RDPSTUFF {
unsigned short tpkt_length;
struct {
unsigned state;
unsigned short dstref;
unsigned short srcref;
unsigned char len;
unsigned char type;
unsigned char flags;
} cotp;
struct {
unsigned state;
unsigned result;
unsigned char type;
unsigned char flags;
unsigned char len;
} cc;
};
struct SSHSTUFF{
size_t packet_length;
};
struct StreamState {
unsigned state;
unsigned remaining;
unsigned short port;
unsigned short app_proto;
unsigned is_sent_sslhello:1;
struct BannerBase64 base64;
union {
struct SSLRECORD ssl;
struct VNCSTUFF vnc;
struct FTPSTUFF ftp;
struct SMTPSTUFF smtp;
struct POP3STUFF pop3;
struct MEMCACHEDSTUFF memcached;
struct SMBSTUFF smb;
struct RDPSTUFF rdp;
struct MCSTUFF mc;
struct SSHSTUFF ssh;
} sub;
};
enum StreamFlags {
SF__none = 0,
SF__close = 0x01, /* send FIN after the static Hello is sent*/
SF__nowait_hello = 0x02, /* send our hello immediately, don't wait for their hello */
};
/**
* A registration structure for various TCP stream protocols
* like HTTP, SSL, and SSH
*/
struct ProtocolParserStream {
const char *name;
unsigned port;
const void *hello;
size_t hello_length;
enum StreamFlags flags;
int (*selftest)(void);
void *(*init)(struct Banner1 *b);
void (*parse)(
const struct Banner1 *banner1,
void *banner1_private,
struct StreamState *stream_state,
const unsigned char *px, size_t length,
struct BannerOutput *banout,
struct stack_handle_t *socket);
void (*cleanup)(struct StreamState *stream_state);
void (*transmit_hello)(const struct Banner1 *banner1, struct stack_handle_t *socket);
/* When multiple items are registered for a port. When one
* connection is closed, the next will be opened.*/
struct ProtocolParserStream *next;
/*NOTE: the 'next' parameter should be the last one in this structure,
* because we statically initialize the rest of the members at compile
* time, and then use this last parameter to link up structures
* at runtime */
};
/**
* Patterns that match the data from the start of a TCP connection.
* This will hint at what protocol that connection might be.
*/
struct Patterns {
/** A string like "SSH-" or "220 " that matches a banner */
const char *pattern;
/** The length of that string, since it may be binary containing
* nul characters */
unsigned pattern_length;
/** An integer arbitrarily assigned to this pattern, which should
* probably match the protocol ID that we are looking for */
unsigned id;
/**
* Whether this string matches only at the beginning ('anchored')
* or anywhere in the input. Virtually all the patterns are anchored.
*/
unsigned is_anchored;
/**
* Some extra flags for the pattern matcher for a few of the patterns.
*/
unsigned extra;
};
struct Banner1 *
banner1_create(void);
void
banner1_destroy(struct Banner1 *b);
unsigned
banner1_parse(
const struct Banner1 *banner1,
struct StreamState *pstate,
const unsigned char *px, size_t length,
struct BannerOutput *banout,
struct stack_handle_t *socket);
/**
* Test the banner protocol-parsing system by reading
* in a capture file
*/
void banner1_test(const char *filename);
int banner1_selftest(void);
#endif

538
src/proto-banout.c Normal file
View File

@ -0,0 +1,538 @@
/*
Banner Output
This module remembers "banners" from a connection. These are often
simple strings, like the FTP hello string. The can also be more
complex strings, parsed from binary protocols. They also may
contain bulk data, such as BASE64 encoded X.509 certificates from
SSL.
One complication is that since we can extract multiple types of
information from the same connection, we can have more than one
banner for the same connection.
*/
#include "proto-banner1.h"
#include "util-malloc.h"
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
/***************************************************************************
***************************************************************************/
void
banout_init(struct BannerOutput *banout)
{
banout->length = 0;
banout->protocol = 0;
banout->next = 0;
banout->max_length = sizeof(banout->banner);
}
/***************************************************************************
***************************************************************************/
void
banout_release(struct BannerOutput *banout)
{
while (banout->next) {
struct BannerOutput *next = banout->next->next;
free(banout->next);
banout->next = next;
}
banout_init(banout);
}
/***************************************************************************
***************************************************************************/
static struct BannerOutput *
banout_find_proto(struct BannerOutput *banout, unsigned proto)
{
while (banout && banout->protocol != proto)
banout = banout->next;
return banout;
}
/***************************************************************************
***************************************************************************/
const unsigned char *
banout_string(const struct BannerOutput *banout, unsigned proto)
{
while (banout && (banout->protocol&0xFFFF) != proto)
banout = banout->next;
if (banout)
return banout->banner;
else
return NULL;
}
/***************************************************************************
***************************************************************************/
unsigned
banout_is_equal(const struct BannerOutput *banout, unsigned proto,
const char *string)
{
const unsigned char *string2;
size_t string_length;
size_t string2_length;
/*
* Grab the string
*/
string2 = banout_string(banout, proto);
if (string2 == NULL)
return string == NULL;
if (string == NULL)
return 0;
string_length = strlen(string);
string2_length = banout_string_length(banout, proto);
if (string_length != string2_length)
return 0;
return memcmp(string, string2, string2_length) == 0;
}
/***************************************************************************
***************************************************************************/
unsigned
banout_is_contains(const struct BannerOutput *banout, unsigned proto,
const char *string)
{
const unsigned char *string2;
size_t string_length;
size_t string2_length;
size_t i;
/*
* Grab the string
*/
string2 = banout_string(banout, proto);
if (string2 == NULL)
return string == NULL;
if (string == NULL)
return 0;
string_length = strlen(string);
string2_length = banout_string_length(banout, proto);
if (string_length > string2_length)
return 0;
for (i=0; i<string2_length-string_length+1; i++) {
if (memcmp(string, string2+i, string_length) == 0)
return 1;
}
return 0;
}
/***************************************************************************
***************************************************************************/
unsigned
banout_string_length(const struct BannerOutput *banout, unsigned proto)
{
while (banout && banout->protocol != proto)
banout = banout->next;
if (banout)
return banout->length;
else
return 0;
}
/***************************************************************************
***************************************************************************/
void
banout_newline(struct BannerOutput *banout, unsigned proto)
{
struct BannerOutput *p;
p = banout_find_proto(banout, proto);
if (p && p->length) {
banout_append_char(banout, proto, '\n');
}
}
/***************************************************************************
***************************************************************************/
void
banout_end(struct BannerOutput *banout, unsigned proto)
{
struct BannerOutput *p;
p = banout_find_proto(banout, proto);
if (p && p->length) {
p->protocol |= 0x80000000;
}
}
/***************************************************************************
***************************************************************************/
void
banout_append_char(struct BannerOutput *banout, unsigned proto, int c)
{
char cc = (char)c;
banout_append(banout, proto, &cc, 1);
}
/***************************************************************************
***************************************************************************/
void
banout_append_hexint(struct BannerOutput *banout, unsigned proto, unsigned long long number, int digits)
{
if (digits == 0) {
for (digits=16; digits>0; digits--)
if (number>>((digits-1)*4) & 0xF)
break;
}
for (;digits>0; digits--) {
char c = "0123456789abcdef"[(number>>(unsigned long long)((digits-1)*4)) & 0xF];
banout_append_char(banout, proto, c);
}
}
/***************************************************************************
* Output either a normal character, or the hex form of a UTF-8 string
***************************************************************************/
void
banout_append_unicode(struct BannerOutput *banout, unsigned proto, unsigned c)
{
if (c & ~0xFFFF) {
unsigned c2;
c2 = 0xF0 | ((c>>18)&0x03);
banout_append_char(banout, proto, c2);
c2 = 0x80 | ((c>>12)&0x3F);
banout_append_char(banout, proto, c2);
c2 = 0x80 | ((c>> 6)&0x3F);
banout_append_char(banout, proto, c2);
c2 = 0x80 | ((c>> 0)&0x3F);
banout_append_char(banout, proto, c2);
} else if (c & ~0x7FF) {
unsigned c2;
c2 = 0xE0 | ((c>>12)&0x0F);
banout_append_char(banout, proto, c2);
c2 = 0x80 | ((c>> 6)&0x3F);
banout_append_char(banout, proto, c2);
c2 = 0x80 | ((c>> 0)&0x3F);
banout_append_char(banout, proto, c2);
} else if (c & ~0x7f) {
unsigned c2;
c2 = 0xc0 | ((c>> 6)&0x1F);
banout_append_char(banout, proto, c2);
c2 = 0x80 | ((c>> 0)&0x3F);
banout_append_char(banout, proto, c2);
} else
banout_append_char(banout, proto, c);
}
/***************************************************************************
***************************************************************************/
static struct BannerOutput *
banout_new_proto(struct BannerOutput *banout, unsigned proto)
{
struct BannerOutput *p;
if (banout->protocol == 0 && banout->length == 0) {
banout->protocol = proto;
return banout;
}
p = CALLOC(1, sizeof(*p));
p->protocol = proto;
p->max_length = sizeof(p->banner);
p->next = banout->next;
banout->next = p;
return p;
}
/***************************************************************************
***************************************************************************/
static struct BannerOutput *
banout_expand(struct BannerOutput *banout, struct BannerOutput *p)
{
struct BannerOutput *n;
/* Double the space */
n = MALLOC( offsetof(struct BannerOutput, banner)
+ 2 * p->max_length);
/* Copy the old structure */
memcpy(n, p, offsetof(struct BannerOutput, banner) + p->max_length);
n->max_length *= 2;
if (p == banout) {
/* 'p' is the head of the linked list, so we can't free it */
banout->next = n;
p->protocol = 0;
p->length = 0;
} else {
/* 'p' is not the head, so replace it in the list with 'n',
* then free it. */
while (banout->next != p)
banout = banout->next;
banout->next = n;
free(p);
}
return n;
}
/***************************************************************************
***************************************************************************/
static void
banout_vprintf(struct BannerOutput *banout, unsigned proto,
const char *fmt, va_list marker) {
char str[10];
int len;
len = vsnprintf(str, sizeof(str), fmt, marker);
if (len > sizeof(str)-1) {
char *tmp = malloc(len+1);
vsnprintf(tmp, len+1, fmt, marker);
banout_append(banout, proto, tmp, len);
free(tmp);
} else {
banout_append(banout, proto, str, len);
}
}
/***************************************************************************
***************************************************************************/
void
banout_printf(struct BannerOutput *banout, unsigned proto, const char *fmt, ...) {
va_list marker;
va_start(marker, fmt);
banout_vprintf(banout, proto, fmt, marker);
va_end(marker);
}
/***************************************************************************
***************************************************************************/
void
banout_append(struct BannerOutput *banout, unsigned proto,
const void *px, size_t length)
{
struct BannerOutput *p;
if (length == AUTO_LEN)
length = strlen((const char*)px);
/*
* Get the matching record for the protocol (e.g. HTML, SSL, etc.).
* If it doesn't already exist, add the protocol object to the linked
* list.
*/
p = banout_find_proto(banout, proto);
if (p == NULL) {
p = banout_new_proto(banout, proto);
}
/*
* If the current object isn't big enough, expand it
*/
while (p->length + length >= p->max_length) {
p = banout_expand(banout, p);
}
/*
* Now that we are assured there is enough space, do the copy
*/
memcpy(p->banner + p->length, px, length);
p->length = (unsigned)(p->length + length);
}
/*****************************************************************************
*****************************************************************************/
static const char *b64 =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"+/";
/*****************************************************************************
*****************************************************************************/
void
banout_init_base64(struct BannerBase64 *base64)
{
base64->state = 0;
base64->temp = 0;
}
/*****************************************************************************
*****************************************************************************/
void
banout_append_base64(struct BannerOutput *banout, unsigned proto,
const void *vpx, size_t length,
struct BannerBase64 *base64)
{
const unsigned char *px = (const unsigned char *)vpx;
size_t i;
unsigned x = base64->temp;
unsigned state = base64->state;
for (i=0; i<length; i++) {
switch (state) {
case 0:
x = px[i]<<16;
state++;
break;
case 1:
x |= px[i]<<8;
state++;
break;
case 2:
x |= px[i];
state = 0;
banout_append_char(banout, proto, b64[(x>>18)&0x3F]);
banout_append_char(banout, proto, b64[(x>>12)&0x3F]);
banout_append_char(banout, proto, b64[(x>> 6)&0x3F]);
banout_append_char(banout, proto, b64[(x>> 0)&0x3F]);
}
}
base64->temp = x;
base64->state = state;
}
/*****************************************************************************
*****************************************************************************/
void
banout_finalize_base64(struct BannerOutput *banout, unsigned proto,
struct BannerBase64 *base64)
{
unsigned x = base64->temp;
switch (base64->state) {
case 0:
break;
case 1:
banout_append_char(banout, proto, b64[(x>>18)&0x3F]);
banout_append_char(banout, proto, b64[(x>>12)&0x3F]);
banout_append_char(banout, proto, '=');
banout_append_char(banout, proto, '=');
break;
case 2:
banout_append_char(banout, proto, b64[(x>>18)&0x3F]);
banout_append_char(banout, proto, b64[(x>>12)&0x3F]);
banout_append_char(banout, proto, b64[(x>>6)&0x3F]);
banout_append_char(banout, proto, '=');
break;
}
}
/*****************************************************************************
*****************************************************************************/
static int
banout_string_equals(struct BannerOutput *banout, unsigned proto,
const char *rhs)
{
const unsigned char *lhs = banout_string(banout, proto);
size_t lhs_length = banout_string_length(banout, proto);
size_t rhs_length = strlen(rhs);
if (lhs_length != rhs_length)
return 0;
return memcmp(lhs, rhs, rhs_length) == 0;
}
/*****************************************************************************
*****************************************************************************/
int
banout_selftest(void)
{
/*
* Basic test
*/
{
struct BannerOutput banout[1];
unsigned i;
banout_init(banout);
for (i=0; i<10; i++) {
banout_append(banout, 1, "xxxx", 4);
banout_append(banout, 2, "yyyyy", 5);
}
if (banout->next == 0)
return 1;
if (banout_string_length(banout, 1) != 40)
return 1;
if (banout_string_length(banout, 2) != 50)
return 1;
banout_release(banout);
if (banout->next != 0)
return 1;
}
/*
* Test BASE64 encoding. We are going to do strings of various lengths
* in order to test the boundary condition of finalizing various strings
* properly
*/
{
struct BannerOutput banout[1];
struct BannerBase64 base64[1];
banout_init(banout);
banout_init_base64(base64);
banout_append_base64(banout, 1, "x", 1, base64);
banout_finalize_base64(banout, 1, base64);
banout_init_base64(base64);
banout_append_base64(banout, 2, "bc", 2, base64);
banout_finalize_base64(banout, 2, base64);
banout_init_base64(base64);
banout_append_base64(banout, 3, "mno", 3, base64);
banout_finalize_base64(banout, 3, base64);
banout_init_base64(base64);
banout_append_base64(banout, 4, "stuv", 4, base64);
banout_finalize_base64(banout, 4, base64);
banout_init_base64(base64);
banout_append_base64(banout, 5, "fghij", 5, base64);
banout_finalize_base64(banout, 5, base64);
if (!banout_string_equals(banout, 1, "eA=="))
return 1;
if (!banout_string_equals(banout, 2, "YmM="))
return 1;
if (!banout_string_equals(banout, 3, "bW5v"))
return 1;
if (!banout_string_equals(banout, 4, "c3R1dg=="))
return 1;
if (!banout_string_equals(banout, 5, "ZmdoaWo="))
return 1;
banout_release(banout);
}
return 0;
}

144
src/proto-banout.h Normal file
View File

@ -0,0 +1,144 @@
#ifndef PROTO_BANOUT_H
#define PROTO_BANOUT_H
struct BannerBase64;
/**
* A structure for tracking one or more banners from a target.
* There can be multiple banner information from a target, such
* as SSL certificates, or HTTP headers separate from HTML
* content, and so on. This will be exploited more in the future
* for extracting multiple bits of information from the same
* port, but giving them different labels. This will also be
* used for doing optional stuff, such as grabbing the entire
* default webpage when connecting to port 80.
*/
struct BannerOutput {
struct BannerOutput *next;
unsigned protocol;
unsigned length;
unsigned max_length;
unsigned char banner[200];
};
/**
* Initialize the list of banners. This doesn't allocate any
* memory, such sets it to zero.
*/
void
banout_init(struct BannerOutput *banout);
/**
* Release any memory. If the list contains only one short
* banner, then no memory was allocated, so nothing gets
* freed.
*/
void
banout_release(struct BannerOutput *banout);
/**
* Just appends a newline '\n' character. In the future, this may do something
* more interesting, which is why it's a separate function.
*/
void
banout_newline(struct BannerOutput *banout, unsigned proto);
/**
* End the banner of the current. This is called when the protocol parser
* knows it's at the end. The major reason for this is processing the
* SSL certificates, so that each certificate comes back as a separate
* banner.
*/
void
banout_end(struct BannerOutput *banout, unsigned proto);
/**
* Append text onto the banner. If this exceeds the buffer, then the
* buffer will be expanded.
*/
void
banout_append(struct BannerOutput *banout, unsigned proto, const void *px, size_t length);
#define AUTO_LEN ((size_t)~0)
void
banout_printf(struct BannerOutput *banout, unsigned proto, const char *fmt, ...);
/**
* Append a single character to the banner.
*/
void
banout_append_char(struct BannerOutput *banout, unsigned proto, int c);
/**
* Append an integer, with hex digits, with the specified number of
* digits
*/
void
banout_append_hexint(struct BannerOutput *banout, unsigned proto, unsigned long long number, int digits);
void
banout_append_unicode(struct BannerOutput *banout, unsigned proto, unsigned c);
/**
* Select a specific string (of the specified protocol).
* The "banner output" can have multiple protocol objects associated
* with it, such as an SSL protocol object and an X.509 certificate.
* Thus, instead of just grabbing the string, we need to grab the
* specific protocol instead.
*/
const unsigned char *
banout_string(const struct BannerOutput *banout, unsigned proto);
/**
* Get the length of a specific string of the specified protocol.
* This is the matching function to banout_string.
*/
unsigned
banout_string_length(const struct BannerOutput *banout, unsigned proto);
/**
* Prepare to start calling banout_append_base64()
*/
void
banout_init_base64(struct BannerBase64 *base64);
/**
* Converts the string to BASE64 and appends it to the banner.
* Since this can be called iteratively as new input arrives,
* a call to banout_init_base64() must be called before the first fragment,
* and a call to banout_finalize_base64() must be called after the last
* fragment
*/
void
banout_append_base64(struct BannerOutput *banout, unsigned proto,
const void *px, size_t length,
struct BannerBase64 *base64);
/**
* Finish encoding the BASE64 string, appending the '==' things on the
* end if necessary
*/
void
banout_finalize_base64(struct BannerOutput *banout, unsigned proto,
struct BannerBase64 *base64);
/**
* Compares a banner string to a fixed string. This is primarily used
* in the "self-test" feature in order to compare parsed banners from
* expected banners.
*/
unsigned
banout_is_equal(const struct BannerOutput *banout, unsigned proto,
const char *string);
unsigned
banout_is_contains(const struct BannerOutput *banout, unsigned proto,
const char *string);
/**
* Do the typical unit/regression test, for this module.
*/
int
banout_selftest(void);
#endif

733
src/proto-coap.c Normal file
View File

@ -0,0 +1,733 @@
/*
CoAP - Constrained Application Protocol
https://en.wikipedia.org/wiki/Constrained_Application_Protocol
This is a very simple protocol for interacting with IoT devices
that have a minimal amount of resources, such as less than a
megabyte of RAM.
From a scanner point of view, we want to execute the equivalent
of:
GET /.well-known/core
This will return the list of additional items that we can access
on the target device.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Ver| T | TKL | Code | Message ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Token (if any, TKL bytes) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options (if any) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|1 1 1 1 1 1 1 1| Payload (if any) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
#include "proto-coap.h"
#include "proto-banner1.h"
#include "smack.h"
#include "unusedparm.h"
#include "util-logger.h"
#include "masscan-app.h"
#include "output.h"
#include "stack-tcp-api.h"
#include "proto-preprocess.h"
#include "proto-ssl.h"
#include "proto-udp.h"
#include "syn-cookie.h"
#include "massip-port.h"
#include "util-malloc.h"
#include "util-safefunc.h"
#include "util-bool.h"
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
struct CoapLink
{
unsigned link_offset;
unsigned link_length;
unsigned parms_offset;
unsigned parms_length;
};
/****************************************************************************
****************************************************************************/
static const char *
response_code(unsigned code)
{
#define CODE(x,y) (((x)<<5) | (y))
switch (code) {
case CODE(2,0): return "Okay";
case CODE(2,1): return "Created";
case CODE(2,2): return "Deleted";
case CODE(2,3): return "Valid";
case CODE(2,4): return "Changed";
case CODE(2,5): return "Content";
case CODE(4,0): return "Bad Request";
case CODE(4,1): return "Unauthorized";
case CODE(4,2): return "Bad Option";
case CODE(4,3): return "Forbidden";
case CODE(4,4): return "Not Found";
case CODE(4,5): return "Method Not Allowed";
case CODE(4,6): return "Not Acceptable";
case CODE(4,12): return "Precondition Failed";
case CODE(4,13): return "Request Too Large";
case CODE(4,15): return "Unsupported Content-Format";
case CODE(5,0): return "Internal Server Error";
case CODE(5,1): return "Not Implemented";
case CODE(5,2): return "Bad Gateway";
case CODE(5,3): return "Service Unavailable";
case CODE(5,4): return "Gateway Timeout";
case CODE(5,5): return "Proxying Not Supported";
}
switch (code>>5) {
case 2: return "Okay";
case 4: return "Error";
default: return "PARSE_ERR";
}
}
/****************************************************************************
* RFC5987
* attr-char = ALPHA / DIGIT
* / "!" / "#" / "$" / "&" / "+" / "-" / "."
* / "^" / "_" / "`" / "|" / "~"
* ; token except ( "*" / "'" / "%" )
* We need this in parsing the links, which may have parameters afterwards
* whose names are in this format.
****************************************************************************/
static bool
is_attr_char(unsigned c)
{
switch (c) {
case '!': case '#': case '$': case '&': case '+': case '-': case '.':
case '^': case '_': case '`': case '|': case '~':
return true;
default:
return isalnum(c) != 0;
}
}
/****************************************************************************
****************************************************************************/
static struct CoapLink *
parse_links(const unsigned char *px, unsigned offset, unsigned length, size_t *r_count)
{
struct CoapLink *l;
struct CoapLink *links;
unsigned count = 0;
enum {
LINK_BEGIN=0,
LINK_VALUE,
LINK_END,
PARM_BEGIN,
PARM_NAME_BEGIN,
PARM_VALUE_BEGIN,
PARM_QUOTED,
PARM_QUOTED_ESCAPE,
PARM_NAME,
PARM_VALUE,
INVALID
} state = LINK_BEGIN;
/* For selftesting purposes, we pass in nul-terminated strings,
* indicated by a length of (~0) */
if (length == ~0)
length = (unsigned)strlen((const char *)px);
/* Allocate space for at least one result */
links = CALLOC(1, sizeof(*links));
l = &links[0];
l->parms_offset = offset;
l->link_offset = offset;
for (; offset < length; offset++)
switch (state) {
case INVALID:
offset = length;
break;
case LINK_BEGIN:
/* Ignore leading whitespace */
if (isspace(px[offset]))
continue;
/* Links must start with "<" character */
if (px[offset] != '<') {
state = INVALID;
break;
}
/* Reserve space for next link */
links = REALLOCARRAY(links, ++count+1, sizeof(*links));
links[count].link_offset = length; /* indicate end-of-list by pointing to end-of-input */
links[count].link_length = 0;
links[count].parms_offset = length;
links[count].parms_length = 0;
/* Grab a pointer to this <link> */
l = &links[count-1];
l->link_offset = offset+1;
l->parms_offset = l->link_offset;
state = LINK_VALUE;
break;
case LINK_VALUE:
if (px[offset] == '>') {
/* End of the link, it may be followed by parameters */
state = LINK_END;
} else {
l->link_length++;
}
break;
case LINK_END:
l->parms_offset = offset+1;
l->parms_length = 0;
if (isspace(px[offset])) {
continue;
} else if (px[offset] == ',') {
/* next link */
state = LINK_BEGIN;
} else if (px[offset] == ';') {
state = PARM_NAME_BEGIN;
} else {
state = INVALID;
}
break;
case PARM_BEGIN:
if (isspace(px[offset])) {
continue;
} else if (px[offset] == ',') {
/* next link */
l->parms_length = offset - l->parms_offset;
state = LINK_BEGIN;
} else if (px[offset] == ';') {
state = PARM_NAME_BEGIN;
} else {
state = INVALID;
}
break;
case PARM_NAME_BEGIN:
if (isspace(px[offset]))
continue;
if (!is_attr_char(px[offset]))
state = INVALID;
else
state = PARM_NAME;
break;
case PARM_NAME:
if (isspace(px[offset])) {
continue;
} else if (px[offset] == '=') {
state = PARM_VALUE_BEGIN;
} else if (!is_attr_char(px[offset])) {
state = INVALID;
}
break;
case PARM_VALUE_BEGIN:
if (isspace(px[offset]))
continue;
else if (px[offset] == '\"') {
state = PARM_QUOTED;
} else if (offset == ';') {
state = PARM_NAME_BEGIN;
} else if (px[offset] == ',') {
l->parms_length = offset - l->parms_offset;
state = LINK_BEGIN;
} else
state = PARM_VALUE;
break;
case PARM_VALUE:
if (isspace(px[offset]))
continue;
else if (px[offset] == ';')
state = PARM_NAME_BEGIN;
else if (px[offset] == ',') {
l->parms_length = offset - l->parms_offset;
state = LINK_BEGIN;
} else {
; /* do nothing */
}
break;
case PARM_QUOTED:
/* RFC2616:
quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
qdtext = <any TEXT except <">>
quoted-pair = "\" CHAR
*/
if (px[offset] == '\\') {
state = PARM_QUOTED_ESCAPE;
} else if (px[offset] == '\"') {
state = PARM_VALUE;
}
break;
case PARM_QUOTED_ESCAPE:
state = PARM_QUOTED;
break;
default:
fprintf(stderr, "invalid state\n");
state = INVALID;
break;
}
/* Return an array of links and a count of the number of links */
*r_count = count;
return links;
}
/****************************************************************************
****************************************************************************/
static bool
coap_parse(const unsigned char *px, size_t length, struct BannerOutput *banout,
unsigned *request_id)
{
unsigned version;
unsigned type;
unsigned code = 0;
unsigned token_length = 0;
unsigned long long token = 0;
unsigned offset;
unsigned optnum;
unsigned content_format;
size_t i;
/* All coap responses will be at least 8 bytes */
if (length < 4) {
LOG(3, "[-] CoAP: short length\n");
goto not_this_protocol;
}
/*
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Ver| T | TKL | Code | Message ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Token (if any, TKL bytes) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options (if any) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|1 1 1 1 1 1 1 1| Payload (if any) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
version = (px[0]>>6) & 3;
type = (px[0]>>4) & 3;
token_length = px[0] & 0x0F;
code = px[1];
*request_id = px[2]<<8 | px[3];
/* Only version supported is v1 */
if (version != 1) {
LOG(3, "[-] CoAP: version=%u\n", version);
goto not_this_protocol;
}
/* Only ACKs suported */
if (type != 2) {
LOG(3, "[-] CoAP: type=%u\n", type);
goto not_this_protocol;
}
/* Only token lengths up to 8 bytes are supported.
* Token length must fit within the packet */
if (token_length > 8 || 4 + token_length > length) {
LOG(3, "[-] CoAP: token-length=%u\n", token_length);
goto not_this_protocol;
}
token = 0;
for (i=0; i<token_length; i++) {
token = token << 8ULL;
token = token | (unsigned long long)px[i];
}
/* Response code */
{
char buf[64];
snprintf(buf, sizeof(buf), "rsp=%u.%u(%s)", code>>5, code&0x1F, response_code(code));
banout_append(banout, PROTO_COAP, buf, AUTO_LEN);
//code >>= 5;
}
/* If there was a token, the print it. */
if (token) {
char buf[64];
snprintf(buf, sizeof(buf), " token=0x%llu", token);
banout_append(banout, PROTO_COAP, buf, AUTO_LEN);
}
/*
* Now process the options fields
0 1 2 3 4 5 6 7
+---------------+---------------+
| | |
| Option Delta | Option Length | 1 byte
| | |
+---------------+---------------+
\ \
/ Option Delta / 0-2 bytes
\ (extended) \
+-------------------------------+
\ \
/ Option Length / 0-2 bytes
\ (extended) \
+-------------------------------+
\ \
/ /
\ \
/ Option Value / 0 or more bytes
\ \
/ /
\ \
+-------------------------------+
*/
offset = 4 + token_length;
optnum = 0;
content_format = 0;
while (offset < length) {
unsigned delta;
unsigned opt;
unsigned optlen;
/* Get the 'opt' byte */
opt = px[offset++];
if (opt == 0xFF)
break;
optlen = (opt>>0) & 0x0F;
delta = (opt>>4) & 0x0F;
/* Decode the delta field */
switch (delta) {
default:
optnum += delta;
break;
case 13:
if (offset >= length) {
banout_append(banout, PROTO_COAP, " PARSE_ERR", AUTO_LEN);
optnum = 0xFFFFFFFF;
} else {
delta = px[offset++] + 13;
optnum += delta;
}
break;
case 14:
if (offset + 1 >= length) {
banout_append(banout, PROTO_COAP, " PARSE_ERR", AUTO_LEN);
optnum = 0xFFFFFFFF;
} else {
delta = px[offset+0]<<8 | px[offset+1];
delta += 269;
offset += 2;
optnum += delta;
}
break;
case 15:
if (optlen != 15)
banout_append(banout, PROTO_COAP, " PARSE_ERR", AUTO_LEN);
optnum = 0xFFFFFFFF;
}
/* Decode the optlen field */
switch (optlen) {
default:
break;
case 13:
if (offset >= length) {
banout_append(banout, PROTO_COAP, " PARSE_ERR", AUTO_LEN);
optnum = 0xFFFFFFFF;
} else {
optlen = px[offset++] + 13;
}
break;
case 14:
if (offset + 1 >= length) {
banout_append(banout, PROTO_COAP, " PARSE_ERR", AUTO_LEN);
optnum = 0xFFFFFFFF;
} else {
optlen = px[offset+0]<<8 | px[offset+1];
optlen += 269;
offset += 2;
}
break;
}
if (offset + optlen > length) {
banout_append(banout, PROTO_COAP, " PARSE_ERR", AUTO_LEN);
optnum = 0xFFFFFFFF;
}
/* Process the option contents */
switch (optnum) {
case 0xFFFFFFFF:
break;
case 1: banout_append(banout, PROTO_COAP, " /If-Match/", AUTO_LEN); break;
case 3: banout_append(banout, PROTO_COAP, " /Uri-Host/", AUTO_LEN); break;
case 4: banout_append(banout, PROTO_COAP, " /Etag", AUTO_LEN); break;
case 5: banout_append(banout, PROTO_COAP, " /If-None-Match/", AUTO_LEN); break;
case 7: banout_append(banout, PROTO_COAP, " /Uri-Port/", AUTO_LEN); break;
case 8: banout_append(banout, PROTO_COAP, " /Location-Path/", AUTO_LEN); break;
case 11: banout_append(banout, PROTO_COAP, " /Uri-Path/", AUTO_LEN); break;
case 12:
banout_append(banout, PROTO_COAP, " /Content-Format/", AUTO_LEN);
content_format = 0;
for (i=0; i<optlen; i++) {
content_format = content_format<<8 | px[offset+i];
}
break;
case 14: banout_append(banout, PROTO_COAP, " /Max-Age/", AUTO_LEN); break;
case 15: banout_append(banout, PROTO_COAP, " /Uri-Query/", AUTO_LEN); break;
case 17: banout_append(banout, PROTO_COAP, " /Accept/", AUTO_LEN); break;
case 20: banout_append(banout, PROTO_COAP, " /Location-Query/", AUTO_LEN); break;
case 35: banout_append(banout, PROTO_COAP, " /Proxy-Uri/", AUTO_LEN); break;
case 39: banout_append(banout, PROTO_COAP, " /Proxy-Scheme/", AUTO_LEN); break;
case 60: banout_append(banout, PROTO_COAP, " /Size1/", AUTO_LEN); break;
default: banout_append(banout, PROTO_COAP, " /(Unknown)/", AUTO_LEN); break;
}
if (optnum == 0xFFFFFFFF)
break;
offset += optlen;
}
switch (content_format) {
case 0: banout_append(banout, PROTO_COAP, " text-plain", AUTO_LEN); break;
case 40:
banout_append(banout, PROTO_COAP, " application/link-format", AUTO_LEN);
{
struct CoapLink *links;
size_t count = 0;
links = parse_links(px, offset, (unsigned)length, &count);
for (i=0; i<count; i++) {
banout_append(banout, PROTO_COAP, " ", AUTO_LEN);
banout_append(banout, PROTO_COAP, px+links[i].link_offset, links[i].link_length);
}
free(links);
}
break;
case 41: banout_append(banout, PROTO_COAP, " application/xml", AUTO_LEN); break;
case 42: banout_append(banout, PROTO_COAP, " application/octet-stream", AUTO_LEN); break;
case 47: banout_append(banout, PROTO_COAP, " application/exi", AUTO_LEN); break;
case 50: banout_append(banout, PROTO_COAP, " application/json", AUTO_LEN); break;
default: banout_append(banout, PROTO_COAP, " (unknown-content-type)", AUTO_LEN); break;
}
LOG(3, "[+] CoAP: valid\n");
return true;
not_this_protocol:
return false;
}
/***************************************************************************
***************************************************************************/
unsigned
coap_handle_response(struct Output *out, time_t timestamp,
const unsigned char *px, unsigned length,
struct PreprocessedInfo *parsed,
uint64_t entropy
)
{
ipaddress ip_them = parsed->src_ip;
ipaddress ip_me = parsed->dst_ip;
unsigned port_them = parsed->port_src;
unsigned port_me = parsed->port_dst;
unsigned message_id = 0;
unsigned cookie;
struct BannerOutput banout[1];
bool is_valid;
LOG(1, "[+] COAP\n");
/* Initialize the "banner output" module that we'll use to print
* pretty text in place of the raw packet */
banout_init(banout);
/*
* Do the protocol parsing
*/
is_valid = coap_parse(px, length, banout, &message_id);
/* Validate the "syn-cookie" style information, which should match the "Message ID field*/
cookie = (unsigned)syn_cookie(ip_them, port_them | Templ_UDP, ip_me, port_me, entropy);
/*if ((seqno&0xffff) != message_id)
goto not_this_protocol;*/
/* See if cookies match. So far, we are allowing responses with the
* wrong cookie */
if ((cookie&0xffff) != message_id)
banout_append(banout, PROTO_COAP, " IP-MISMATCH", AUTO_LEN);
/* Print the banner information, or save to a file, depending */
if (is_valid) {
output_report_banner(
out, timestamp,
ip_them, 17 /*udp*/, parsed->port_src,
PROTO_COAP,
parsed->ip_ttl,
banout_string(banout, PROTO_COAP),
banout_string_length(banout, PROTO_COAP));
banout_release(banout);
return 0;
} else {
banout_release(banout);
return default_udp_parse(out, timestamp, px, length, parsed, entropy);
}
}
/****************************************************************************
****************************************************************************/
unsigned
coap_udp_set_cookie(unsigned char *px, size_t length, uint64_t seqno)
{
/*
The frame header is 4 bytes long, with bytes 2 and 3 being
the Message ID.
We can also put up to 8 bytes of a "token" here instead of
just using the message ID.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Ver| T | TKL | Code | Message ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Token (if any, TKL bytes) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
if (length < 4)
return 0;
px[2] = (unsigned char)(seqno >> 8);
px[3] = (unsigned char)(seqno >> 0);
return 0;
}
/****************************************************************************
* For the selftest code, tests whether the indicated link is within the
* given list.
****************************************************************************/
static int
test_is_link(const char *name, const unsigned char *vinput, struct CoapLink *links, size_t count, int line_number)
{
size_t i;
size_t name_length = strlen(name);
const char *input = (const char *)vinput;
for (i=0; i<count; i++) {
const char *name2;
if (name_length != links[i].link_length)
continue;
name2 = input + links[i].link_offset;
if (memcmp(name2, name, name_length) != 0)
continue;
return 1; /* found */
}
fprintf(stderr, "[-] proto-coap failed at line number %d\n", line_number);
return 0; /* not found */
}
/****************************************************************************
****************************************************************************/
int
proto_coap_selftest(void)
{
struct CoapLink *links;
size_t count=0;
/* test quoted */
{
static const unsigned char *input = (const unsigned char *)
"</sensors/temp>;if=\"se\\\"\\;\\,\\<\\>\\\\nsor\",</success>";
links = parse_links(input, 0, (unsigned)(~0), &count);
if (!test_is_link("/success", input, links, count, __LINE__))
return 1;
}
/* test a simple link */
{
static const unsigned char *input = (const unsigned char *)
"</sensors/temp>;if=\"sensor\"";
links = parse_links(input, 0, (unsigned)(~0), &count);
if (!test_is_link("/sensors/temp", input, links, count, __LINE__))
return 1;
}
/* Test a complex dump */
{
static const unsigned char *input = (const unsigned char *)
"</sensors/temp>;if=\"sensor\","
"</sensors/light>;if=\"sensor\","
"</sensors>;ct=40,"
"</sensors/temp>;rt=\"temperature-c\";if=\"sensor\","
"</sensors/light>;rt=\"light-lux\";if=\"sensor\","
"</sensors/light>;rt=\"light-lux\";if=\"sensor\","
"</sensors/light>;rt=\"light-lux core.sen-light\";if=\"sensor\","
"</sensors>;ct=40;title=\"Sensor Index\","
"</sensors/temp>;rt=\"temperature-c\";if=\"sensor\","
"</sensors/light>;rt=\"light-lux\";if=\"sensor\","
"<http://www.example.com/sensors/t123>;anchor=\"/sensors/temp\";rel=\"describedby\","
"</t>;anchor=\"/sensors/temp\";rel=\"alternate\","
"</firmware/v2.1>;rt=\"firmware\";sz=262144"
;
links = parse_links(input, 0, (unsigned)(~0), &count);
if (!test_is_link("/firmware/v2.1", input, links, count, __LINE__))
return 1;
}
/* Now test an entire packet */
{
const char input[] =
"\x60\x45\x01\xce\xc1\x28\xff\x3c\x2f\x72\x65\x67\x69\x73\x74\x65"
"\x72\x3e\x2c\x3c\x2f\x6e\x64\x6d\x2f\x64\x69\x73\x3e\x2c\x3c\x2f"
"\x6e\x64\x6d\x2f\x63\x69\x3e\x2c\x3c\x2f\x6d\x69\x72\x72\x6f\x72"
"\x3e\x2c\x3c\x2f\x75\x68\x70\x3e\x2c\x3c\x2f\x6e\x64\x6d\x2f\x6c"
"\x6f\x67\x6f\x75\x74\x3e\x2c\x3c\x2f\x6e\x64\x6d\x2f\x6c\x6f\x67"
"\x69\x6e\x3e\x2c\x3c\x2f\x69\x6e\x66\x6f\x3e";
unsigned request_id = 0;
struct BannerOutput banout[1];
bool is_valid;
banout_init(banout);
/* parse a test packet */
is_valid = coap_parse( (const unsigned char*)input,
sizeof(input)-1,
banout,
&request_id
);
//fprintf(stderr, "[+] %.*s\n", (int)banout_string_length(banout, PROTO_COAP), banout_string(banout, PROTO_COAP));
if (!is_valid)
return 1;
if (request_id != 462)
return 1;
{
const unsigned char *str = banout_string(banout, PROTO_COAP);
size_t str_length = banout_string_length(banout, PROTO_COAP);
if (str_length <= 16 && memcmp(str, "rsp=2.5(Content)", 16) != 0)
return 1;
}
banout_release(banout);
}
return 0;
}

31
src/proto-coap.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef PROTO_COAP_H
#define PROTO_COAP_H
#include "proto-banner1.h"
struct Output;
struct PreprocessedInfo;
/*
* For sending TCP requests and parsing TCP responses.
*/
extern const struct ProtocolParserStream banner_coap;
/*
* For parsing UDP responses
*/
unsigned
coap_handle_response(struct Output *out, time_t timestamp,
const unsigned char *px, unsigned length,
struct PreprocessedInfo *parsed,
uint64_t entropy
);
/*
* For creating UDP request
*/
unsigned
coap_udp_set_cookie(unsigned char *px, size_t length, uint64_t seqno);
int
proto_coap_selftest(void);
#endif

51
src/proto-dns-parse.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef PROTO_DNS_PARSE_H
#define PROTO_DNS_PARSE_H
struct DomainPointer
{
const unsigned char *name;
unsigned length;
};
struct DNS_Incoming
{
unsigned id; /* transaction id */
unsigned is_valid:1;
unsigned is_formerr:1;
unsigned is_edns0:1;/* edns0 features found */
unsigned qr:1; /* 'query' or 'response' */
unsigned aa:1; /* 'authoritative answer' */
unsigned tc:1; /* 'truncation' */
unsigned rd:1; /* 'recursion desired' */
unsigned ra:1; /* 'recursion available' */
unsigned z:3; /* reserved */
unsigned opcode;
unsigned rcode; /* response error code */
unsigned qdcount; /* query count */
unsigned ancount; /* answer count */
unsigned nscount; /* name-server/authority count */
unsigned arcount; /* additional record count */
struct {
unsigned payload_size;
unsigned version;
unsigned z;
} edns0;
const unsigned char *req;
unsigned req_length;
/* the query name */
struct DomainPointer query_name;
unsigned query_type;
unsigned char query_name_buffer[256];
unsigned rr_count;
unsigned short rr_offset[1024];
unsigned edns0_offset;
};
void
proto_dns_parse(struct DNS_Incoming *dns, const unsigned char px[], unsigned offset, unsigned max);
unsigned
dns_name_skip(const unsigned char px[], unsigned offset, unsigned max);
#endif

449
src/proto-dns.c Normal file
View File

@ -0,0 +1,449 @@
/*
Parses DNS response information
The scanner sends a CHAOS TXT query for "version.bind". This module parses
DNS in order to find the response string.
*/
#include "proto-udp.h"
#include "proto-dns.h"
#include "proto-dns-parse.h"
#include "proto-preprocess.h"
#include "syn-cookie.h"
#include "util-logger.h"
#include "output.h"
#include "masscan-app.h"
#include "proto-banner1.h"
#include "massip-port.h"
#include "masscan.h"
#include "unusedparm.h"
#define VERIFY_REMAINING(n) if (offset+(n) > length) return;
/****************************************************************************
* This skips over a name field while parsing the packet. If the name
* is just a two-byte compression field like 0xc0 0x1a, then it'll skip
* those two bytes. However, when it does the skip, it does validate
* the name. Thus, if it's a compressed name, it'll follow the compression
* links to validate things like long names and infinite recursion.
****************************************************************************/
static unsigned
dns_name_skip_validate(const unsigned char *px, unsigned offset, unsigned length, unsigned name_length)
{
unsigned ERROR = length + 1;
unsigned result = offset + 2;
unsigned recursion = 0;
/* 'for all labels' */
for (;;) {
unsigned len;
/* validate: the eventual uncompressed name will be less than 255 */
if (name_length >= 255)
return ERROR;
/* validate: haven't gone off end of packet */
if (offset >= length)
return ERROR;
/* grab length of next label */
len = px[offset];
/* Do two types of processing, either a compression code or a
* original label. Note that we can alternate back and forth
* between these two states. */
if (len & 0xC0) {
/* validate: top 2 bits are 11*/
if ((len & 0xC0) != 0xC0)
return ERROR;
/* validate: enough bytes left for 2 byte compression field */
if (offset + 1 >= length)
return ERROR;
/* follow the compression pointer to the next location */
offset = (px[offset]&0x3F)<<8 | px[offset+1];
/* validate: follow a max of 4 links */
if (++recursion > 4)
return ERROR;
} else {
/* we have a normal label */
recursion = 0;
/* If the label-length is zero, then that means we've reached
* the end of the name */
if (len == 0) {
return result; /* end of domain name */
}
/* There are more labels to come, therefore skip this and go
* to the next one */
name_length += len + 1;
offset += len + 1;
}
}
}
/****************************************************************************
* Just skip the name, without validating whether it's valid or not. This
* is for re-parsing the packet usually, after we've validated that all
* the names are OK.
****************************************************************************/
unsigned
dns_name_skip(const unsigned char px[], unsigned offset, unsigned max)
{
unsigned name_length = 0;
/* Loop through all labels
* NOTE: the only way this loops around is in the case of a normal
* label. All other conditions cause a 'return' from this function */
for (;;) {
if (name_length >= 255)
return max + 1;
if (offset >= max)
return max + 1;
switch (px[offset]>>6) {
case 0:
/* uncompressed label */
if (px[offset] == 0) {
return offset+1; /* end of domain name */
} else {
name_length += px[offset] + 1;
offset += px[offset] + 1;
continue;
}
break;
case 3:
/* 0xc0 = compressed name */
return dns_name_skip_validate(px, offset, max, name_length);
case 2:
/* 0x40 - ENDS0 extended label type
* rfc2671 section 3.1
* I have no idea how to parse this */
return max + 1; /* totally clueless how to parse it */
case 1:
return max + 1;
}
}
}
/****************************************************************************
****************************************************************************/
static void
dns_extract_name(const unsigned char px[], unsigned offset, unsigned max,
struct DomainPointer *name)
{
name->length = 0;
for (;;) {
unsigned len;
if (offset >= max)
return;
len = px[offset];
if (len & 0xC0) {
if ((len & 0xC0) != 0xC0)
return;
else if (offset + 1 >= max)
return;
else {
offset = (px[offset]&0x3F)<<8 | px[offset+1];
}
} else {
if (len == 0) {
return; /* end of domain name */
} else {
memcpy((unsigned char*)name->name+name->length, px+offset, len+1);
name->length = (unsigned char)(name->length + len + 1);
offset += len + 1;
}
}
}
}
/****************************************************************************
****************************************************************************/
void
proto_dns_parse(struct DNS_Incoming *dns, const unsigned char px[], unsigned offset, unsigned max)
{
static const unsigned MAX_RRs = sizeof(dns->rr_offset)/sizeof(dns->rr_offset[0]);
unsigned i;
dns->is_valid = 0; /* not valid yet until we've successfully parsed*/
dns->req = px;
dns->req_length = max-offset;
dns->edns0.payload_size = 512; /* RFC 1035 4.2.1 */
/*
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ID |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR| Opcode |AA|TC|RD|RA| Z | RCODE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QDCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ANCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| NSCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ARCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
if (max - offset < 12)
return;
dns->id = px[offset+0]<<8 | px[offset+1];
dns->qr = (px[offset+2]>>7)&1;
dns->aa = (px[offset+2]>>2)&1;
dns->tc = (px[offset+2]>>1)&1;
dns->rd = (px[offset+2]>>0)&1;
dns->ra = (px[offset+3]>>7)&1;
dns->z = (px[offset+3]>>4)&7;
dns->opcode = (px[offset+2]>>3)&0xf;
dns->rcode = (px[offset+3]>>0)&0xf;
dns->qdcount = px[offset+4]<<8 | px[offset+5];
dns->ancount = px[offset+6]<<8 | px[offset+7];
dns->nscount = px[offset+8]<<8 | px[offset+9];
dns->arcount = px[offset+10]<<8 | px[offset+11];
dns->rr_count = 0; /* so far */
offset += 12;
dns->is_valid = 1;
dns->is_formerr = 1; /* is "formate-error" until we've finished parsing */
/*
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| |
/ QNAME /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QTYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QCLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
for (i=0; i<dns->qdcount; i++) {
unsigned xclass;
unsigned xtype;
if (dns->rr_count >= MAX_RRs)
return;
dns->rr_offset[dns->rr_count++] = (unsigned short)offset;
offset = dns_name_skip(px, offset, max);
offset += 4; /* length of type and class */
if (offset > max)
return;
xclass = px[offset-2]<<8 | px[offset-1];
if (xclass != 1 && xclass != 255 && xclass != 3)
return;
xtype = px[offset-4]<<8 | px[offset-3];
dns->query_type = xtype;
}
/*
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| |
/ /
/ NAME /
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| CLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TTL |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| RDLENGTH |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
/ RDATA /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
for (i=0; i<dns->ancount + dns->nscount; i++) {
unsigned rdlength;
if (dns->rr_count >= sizeof(dns->rr_offset)/sizeof(dns->rr_offset[0]))
return;
dns->rr_offset[dns->rr_count++] = (unsigned short)offset;
offset = dns_name_skip(px, offset, max);
offset += 10;
if (offset > max)
return;
rdlength = px[offset-2]<<8 | px[offset-1];
offset += rdlength;
if (offset > max)
return;
}
for (i=0; i<dns->arcount; i++) {
unsigned rdlength;
if (dns->rr_count >= sizeof(dns->rr_offset)/sizeof(dns->rr_offset[0]))
return;
dns->rr_offset[dns->rr_count++] = (unsigned short)offset;
/* ENDS0 OPT parsing */
if (offset + 11 <= max && px[offset] == 0 && px[offset+1] == 0 && px[offset+2] == 0x29) {
dns->edns0.payload_size = px[offset+3]<<8 | px[offset+4];
if (dns->edns0.payload_size < 512)
return;
dns->rcode |= px[offset+5]<<4;
dns->edns0.version = px[offset+6];
dns->is_edns0 = 1;
}
offset = dns_name_skip(px, offset, max);
offset += 10;
if (offset > max)
return;
rdlength = px[offset-2]<<8 | px[offset-1];
offset += rdlength;
if (offset > max)
return;
}
dns->query_name.name = dns->query_name_buffer;
dns_extract_name(px, dns->rr_offset[0], max, &dns->query_name);
dns->is_formerr = 0;
return;
}
/***************************************************************************
* Set the "syn-cookie" style information so that we can validate replies
* match a valid request. We don't hold "state" on the requests, so this
* becomes a hash of the port/IP information.
* DNS has a two-byte "transaction id" field, so we can't use the full
* cookie, just the lower two bytes of it.
* Below in "handle_dns", we validate that the cookie is correct.
***************************************************************************/
unsigned
dns_set_cookie(unsigned char *px, size_t length, uint64_t cookie)
{
if (length > 2) {
px[0] = (unsigned char)(cookie >> 8);
px[1] = (unsigned char)(cookie >> 0);
return cookie & 0xFFFF;
} else
return 0;
}
/***************************************************************************
* Process a DNS packet received in response to UDP probes to port 53.
* This function has three main tasks:
* - parse the DNS protocol, and make sure it's valid DNS.
* - make sure that the DNS response matches a valid request using
* the "syn-cookie" approach.
* - parse the "version.bind" response and report it as the version
* string for the banner.
***************************************************************************/
unsigned
handle_dns(struct Output *out, time_t timestamp,
const unsigned char *px, unsigned length,
struct PreprocessedInfo *parsed,
uint64_t entropy)
{
ipaddress ip_them = parsed->src_ip;
ipaddress ip_me = parsed->dst_ip;
unsigned port_them = parsed->port_src;
unsigned port_me = parsed->port_dst;
struct DNS_Incoming dns[1];
unsigned offset;
uint64_t seqno;
const char *reason = 0;
seqno = (unsigned)syn_cookie(ip_them, port_them | Templ_UDP, ip_me, port_me, entropy);
proto_dns_parse(dns, px, parsed->app_offset, parsed->app_offset + parsed->app_length);
if ((seqno & 0xFFFF) != dns->id)
return 1;
/*
* In practice, DNS queries always have the query count set to 1,
* though in theory servers could support multiple queries in a
* single request, almost none of them do
*/
if (dns->qr != 1)
return 0;
/*
* If we get back NOERROR, we drop through and extract the strings in
* the packet. Otherwise, we report the error here.
*/
switch (dns->rcode) {
case 0: reason = 0; break; /* NOERROR */
case 1: reason = "1:FORMERR"; break;
case 2: reason = "2:SERVFAIL"; break;
case 3: reason = "3:NXDOMAIN"; break;
case 4: reason = "4:NOTIMP"; break;
case 5: reason = "5:REFUSED"; break;
case 6: reason = "6:YXDOMAIN"; break;
case 7: reason = "7:XRRSET"; break;
case 8: reason = "8:NOTAUTH"; break;
case 9: reason = "9:NOTZONE"; break;
}
if (reason != 0) {
output_report_banner(
out, timestamp,
ip_them, 17, port_them,
PROTO_DNS_VERSIONBIND,
parsed->ip_ttl,
(const unsigned char*)reason,
(unsigned)strlen(reason));
return 0;
}
/*if (dns->qdcount != 1)
return 0;
if (dns->ancount < 1)
return 0;
if (dns->rr_count < 2)
return 0;*/
offset = dns->rr_offset[1];
offset = dns_name_skip(px, offset, length);
if (offset + 10 >= length)
return 0;
{
unsigned type = px[offset+0]<<8 | px[offset+1];
unsigned xclass = px[offset+2]<<8 | px[offset+3];
unsigned rrlen = px[offset+8]<<8 | px[offset+9];
unsigned txtlen = px[offset+10];
offset += 11;
/* Make sure can't exceed bounds of RR */
if (txtlen > length - offset)
txtlen = length - offset;
if (rrlen == 0 || txtlen > rrlen-1)
return 0;
if (type != 0x10 || xclass != 3)
return 0;
output_report_banner(
out, timestamp,
ip_them, 17, port_them,
PROTO_DNS_VERSIONBIND,
parsed->ip_ttl,
px + offset, txtlen);
return 1;
}
}

13
src/proto-dns.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef PROTO_DNS_H
#define PROTO_DNS_H
#include <time.h>
#include <stdint.h>
struct PreprocessedInfo;
struct Output;
unsigned handle_dns(struct Output *out, time_t timestamp,
const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy);
unsigned dns_set_cookie(unsigned char *px, size_t length, uint64_t seqno);
#endif

151
src/proto-ftp.c Normal file
View File

@ -0,0 +1,151 @@
#include "proto-ftp.h"
#include "proto-banner1.h"
#include "unusedparm.h"
#include "masscan-app.h"
#include "stack-tcp-api.h"
#include "proto-ssl.h"
#include <ctype.h>
#include <string.h>
/***************************************************************************
***************************************************************************/
static void
ftp_parse( const struct Banner1 *banner1,
void *banner1_private,
struct StreamState *pstate,
const unsigned char *px, size_t length,
struct BannerOutput *banout,
struct stack_handle_t *socket)
{
unsigned state = pstate->state;
unsigned i;
struct FTPSTUFF *ftp = &pstate->sub.ftp;
UNUSEDPARM(banner1_private);
UNUSEDPARM(banner1);
for (i=0; i<length; i++) {
switch (state) {
case 0:
case 100:
ftp->code = 0;
state++;
/* fall through */
case 1:
case 2:
case 3:
case 101:
case 102:
case 103:
if (!isdigit(px[i]&0xFF)) {
state = 0xffffffff;
tcpapi_close(socket);
} else {
ftp->code *= 10;
ftp->code += (px[i] - '0');
state++;
banout_append_char(banout, PROTO_FTP, px[i]);
}
break;
case 4:
case 104:
if (px[i] == ' ') {
ftp->is_last = 1;
state++;
banout_append_char(banout, PROTO_FTP, px[i]);
} else if (px[i] == '-') {
ftp->is_last = 0;
state++;
banout_append_char(banout, PROTO_FTP, px[i]);
} else {
state = 0xffffffff;
tcpapi_close(socket);
}
break;
case 5:
if (px[i] == '\r')
continue;
else if (px[i] == '\n') {
if (ftp->is_last) {
tcpapi_send(socket, "AUTH TLS\r\n", 10, 0);
state = 100;
banout_append_char(banout, PROTO_FTP, px[i]);
} else {
banout_append_char(banout, PROTO_FTP, px[i]);
state = 0;
}
} else if (px[i] == '\0' || !isprint(px[i])) {
state = 0xffffffff;
tcpapi_close(socket);
continue;
} else {
banout_append_char(banout, PROTO_FTP, px[i]);
}
break;
case 105:
if (px[i] == '\r')
continue;
else if (px[i] == '\n') {
if (ftp->code == 234) {
/* change the state here to SSL */
unsigned port = pstate->port;
memset(pstate, 0, sizeof(*pstate));
pstate->app_proto = PROTO_SSL3;
pstate->is_sent_sslhello = 1;
pstate->port = (unsigned short)port;
state = 0;
tcpapi_send(socket, banner_ssl.hello, banner_ssl.hello_length, 0);
} else {
state = 0xffffffff;
tcpapi_close(socket);
}
} else if (px[i] == '\0' || !isprint(px[i])) {
state = 0xffffffff;
tcpapi_close(socket);
continue;
} else {
banout_append_char(banout, PROTO_FTP, px[i]);
}
break;
default:
i = (unsigned)length;
break;
}
}
pstate->state = state;
}
/***************************************************************************
***************************************************************************/
static void *
ftp_init(struct Banner1 *banner1)
{
UNUSEDPARM(banner1);
//banner1->payloads.tcp[21] = &banner_ftp;
return 0;
}
/***************************************************************************
***************************************************************************/
static int
ftp_selftest(void)
{
return 0;
}
/***************************************************************************
***************************************************************************/
const struct ProtocolParserStream banner_ftp = {
"ftp", 21, 0, 0, 0,
ftp_selftest,
ftp_init,
ftp_parse,
};

7
src/proto-ftp.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef PROTO_FTP_H
#define PROTO_FTP_H
#include "proto-banner1.h"
extern const struct ProtocolParserStream banner_ftp;
#endif

952
src/proto-http.c Normal file
View File

@ -0,0 +1,952 @@
#include "proto-http.h"
#include "proto-banner1.h"
#include "stack-tcp-api.h"
#include "smack.h"
#include "unusedparm.h"
#include "util-safefunc.h"
#include "masscan-app.h"
#include "util-malloc.h"
#include "util-bool.h"
#include "stack-tcp-core.h"
#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
enum {
HTTPFIELD_INCOMPLETE,
HTTPFIELD_SERVER,
HTTPFIELD_CONTENT_LENGTH,
HTTPFIELD_CONTENT_TYPE,
HTTPFIELD_VIA,
HTTPFIELD_LOCATION,
HTTPFIELD_UNKNOWN,
HTTPFIELD_NEWLINE,
};
static struct Patterns http_fields[] = {
{"Server:", 7, HTTPFIELD_SERVER, SMACK_ANCHOR_BEGIN},
//{"Content-Length:", 15, HTTPFIELD_CONTENT_LENGTH, SMACK_ANCHOR_BEGIN},
//{"Content-Type:", 13, HTTPFIELD_CONTENT_TYPE, SMACK_ANCHOR_BEGIN},
{"Via:", 4, HTTPFIELD_VIA, SMACK_ANCHOR_BEGIN},
{"Location:", 9, HTTPFIELD_LOCATION, SMACK_ANCHOR_BEGIN},
{":", 1, HTTPFIELD_UNKNOWN, 0},
{"\n", 1, HTTPFIELD_NEWLINE, 0},
{0,0,0,0}
};
enum {
HTML_INCOMPLETE,
HTML_TITLE,
HTML_UNKNOWN,
};
static struct Patterns html_fields[] = {
{"<TiTle", 6, HTML_TITLE, 0},
{0,0,0,0}
};
extern struct ProtocolParserStream banner_http;
/**
* We might have an incomplete HTTP request header. Thus, as we insert
* fields into it, we'll add missing components onto the end.
*/
static size_t
_http_append(unsigned char **inout_header, size_t length1, size_t length2, const char *str)
{
size_t str_length = strlen(str);
*inout_header = REALLOC(*inout_header, length1 + length2 + str_length + 1);
memcpy(*inout_header + length1, str, str_length + 1);
return str_length;
}
enum What {spaces, notspaces, end_of_line, end_of_field};
static size_t
_skip(enum What what, const unsigned char *hdr, size_t offset, size_t header_length)
{
switch (what) {
case notspaces:
while (offset < header_length && !isspace(hdr[offset]&0xFF))
offset++;
break;
case spaces:
while (offset < header_length && hdr[offset] != '\n' && isspace(hdr[offset]&0xFF))
offset++;
if (offset < header_length && hdr[offset] == '\n') {
while (offset > 0 && hdr[offset-1] == '\r')
offset--;
}
break;
case end_of_field:
while (offset < header_length && hdr[offset] != '\n')
offset++;
if (offset < header_length && hdr[offset] == '\n') {
while (offset > 0 && hdr[offset-1] == '\r')
offset--;
}
break;
case end_of_line:
while (offset < header_length && hdr[offset] != '\n')
offset++;
if (offset < header_length && hdr[offset] == '\n')
offset++;
break;
}
return offset;
}
/**
* Used when editing our HTTP prototype request, it replaces the existing
* field (start..end) with the new field. The header is resized and data moved
* to accommodate this insertion.
*/
static size_t
_http_insert(unsigned char **r_hdr, size_t start, size_t end, size_t header_length, size_t field_length, const void *field)
{
size_t old_field_length = (end-start);
size_t new_header_length = header_length + field_length - old_field_length;
unsigned char *hdr;
*r_hdr = REALLOC(*r_hdr, new_header_length + 1);
hdr = *r_hdr;
/* Shrink/expand the field */
memmove(&hdr[start + field_length], &hdr[end], header_length - end + 1);
/* Insert the new header at this location */
memcpy(&hdr[start], field, field_length);
return new_header_length;
}
/***************************************************************************
***************************************************************************/
size_t
http_change_requestline(unsigned char **hdr, size_t header_length,
const void *field, size_t field_length, int item)
{
size_t offset;
size_t start;
/* If no length given, calculate length */
if (field_length == ~(size_t)0)
field_length = strlen((const char *)field);
/* GET /example.html HTTP/1.0
* 0111233333333333334
* #0 skip leading whitespace
* #1 skip past method
* #2 skip past space after method
* #3 skip past URL field
* #4 skip past space after URL
* #5 skip past version
*/
/* #0 Skip leading whitespace */
offset = 0;
offset = _skip(spaces, *hdr, offset, header_length);
/* #1 Method */
start = offset;
if (offset == header_length)
header_length += _http_append(hdr, header_length, field_length, "GET");
offset = _skip(notspaces, *hdr, offset, header_length);
if (item == 0) {
return _http_insert(hdr, start, offset, header_length, field_length, field);
}
/* #2 Method space */
if (offset == header_length)
header_length += _http_append(hdr, header_length, field_length, " ");
offset = _skip(spaces, *hdr, offset, header_length);
/* #3 URL */
start = offset;
if (offset == header_length)
header_length += _http_append(hdr, header_length, field_length, "/");
offset = _skip(notspaces, *hdr, offset, header_length);
if (item == 1) {
return _http_insert(hdr, start, offset, header_length, field_length, field);
}
/* #4 Space after URL */
if (offset == header_length)
header_length += _http_append(hdr, header_length, field_length, " ");
offset = _skip(spaces, *hdr, offset, header_length);
/* #5 version */
start = offset;
if (offset == header_length)
header_length += _http_append(hdr, header_length, field_length, "HTTP/1.0");
offset = _skip(notspaces, *hdr, offset, header_length);
if (item == 2) {
return _http_insert(hdr, start, offset, header_length, field_length, field);
}
/* ending line */
if (offset == header_length)
header_length += _http_append(hdr, header_length, field_length, "\r\n");
offset = _skip(spaces, *hdr, offset, header_length);
offset = _skip(end_of_line, *hdr, offset, header_length);
/* now find a blank line */
for (;;) {
/* make sure there's at least one line left */
if (offset == header_length)
header_length += _http_append(hdr, header_length, field_length, "\r\n");
if (offset + 1 == header_length && (*hdr)[offset] == '\r')
header_length += _http_append(hdr, header_length, field_length, "\n");
start = offset;
offset = _skip(end_of_field, *hdr, offset, header_length);
if (start == offset) {
/* We've reached the end of the header*/
offset = _skip(end_of_line, *hdr, offset, header_length);
break;
}
if (offset == header_length)
header_length += _http_append(hdr, header_length, field_length, "\r\n");
if (offset + 1 == header_length && (*hdr)[offset] == '\r')
header_length += _http_append(hdr, header_length, field_length, "\n");
offset = _skip(end_of_line, *hdr, offset, header_length);
}
start = offset;
offset = header_length;
if (item == 3) {
return _http_insert(hdr, start, offset, header_length, field_length, field);
}
return header_length;
}
static size_t
_field_length(const unsigned char *hdr, size_t offset, size_t hdr_length)
{
size_t original_offset = offset;
/* Find newline */
while (offset < hdr_length && hdr[offset] != '\n')
offset++;
/* Trim trailing whitespace */
while (offset > original_offset && isspace(hdr[offset-1]&0xFF))
offset--;
return offset - original_offset;
}
static size_t _next_field(const unsigned char *hdr, size_t offset, size_t hdr_length)
{
size_t original_offset = offset;
/* Find newline */
while (offset < hdr_length && hdr[offset] != '\n')
offset++;
/* Remove newline too*/
if (offset > original_offset && isspace(hdr[offset-1]&0xFF))
offset++;
return offset;
}
static bool
_has_field_name(const char *name, size_t name_length, const unsigned char *hdr, size_t offset, size_t hdr_length)
{
size_t x;
bool found_colon = false;
/* Trim leading whitespace */
while (offset < hdr_length && isspace(hdr[offset]&0xFF) && hdr[offset] != '\n')
offset++;
/* Make sure there's enough space left */
if (hdr_length - offset < name_length)
return false;
/* Make sure there's colon after */
for (x = offset + name_length; x<hdr_length; x++) {
unsigned char c = hdr[x] & 0xFF;
if (isspace(c))
continue;
else if (c == ':') {
found_colon = true;
break;
} else {
/* some unexpected character was found in the name */
return false;
}
}
if (!found_colon)
return false;
/* Compare the name (case insensitive) */
return memcasecmp(name, hdr + offset, name_length) == 0;
}
/***************************************************************************
***************************************************************************/
size_t
http_change_field(unsigned char **inout_header, size_t header_length,
const char *name,
const unsigned char *value, size_t value_length,
int what)
{
unsigned char *hdr = *inout_header;
size_t name_length = strlen(name);
size_t offset;
size_t next_offset;
/* If field 'name' ends in a colon, trim that. Also, trim whitespace */
while (name_length) {
unsigned char c = name[name_length-1];
if (c == ':' || isspace(c & 0xFF))
name_length--;
else
break;
}
/* If length of the fiend value not specified, then assume
* nul-terminated string */
if (value_length == ~(size_t)0)
value_length = strlen((const char *)value);
/* Find our field */
for (offset = _next_field(hdr, 0, header_length);
offset < header_length;
offset = _next_field(hdr, offset, header_length)) {
if (_has_field_name(name, name_length, hdr, offset, header_length)) {
break;
} else if (_field_length(hdr, offset, header_length) == 0) {
/* We reached end without finding field, so insert before end
* instead of replacing an existing header. */
if (what == http_field_remove)
return header_length;
what = http_field_add;
break;
}
}
/* Allocate a new header to replace the old one. We'll allocated
* more space than we actually need */
*inout_header = REALLOC(*inout_header, header_length + name_length + 2 + value_length + 2 + 1 + 2);
hdr = *inout_header;
/* If we reached the end without finding proper termination, then add
* it */
if (offset == header_length) {
if (offset == 0 || hdr[offset-1] != '\n') {
if (hdr[offset-1] == '\r')
header_length = _http_append(&hdr, header_length, value_length+2, "\n");
else
header_length = _http_append(&hdr, header_length, value_length+2, "\r\n");
}
}
/* Make room for the new header */
next_offset = _next_field(hdr, offset, header_length);
if (value == NULL || what == http_field_remove) {
memmove(&hdr[offset + 0],
&hdr[next_offset],
header_length - next_offset + 1);
header_length += 0 - (next_offset - offset);
return header_length;
} else if (what == http_field_replace) {
/* Replace existing field */
memmove(&hdr[offset + name_length + 2 + value_length + 2],
&hdr[next_offset],
header_length - offset + 1);
header_length += (name_length + 2 + value_length + 2) - (next_offset - offset);
} else {
/* Add a new field onto the end */
memmove(&hdr[offset + name_length + 2 + value_length + 2],
&hdr[offset],
header_length - offset + 1);
header_length += (name_length + 2 + value_length + 2);
}
hdr[header_length] = '\0';
/* Copy the new header */
memcpy(&hdr[offset], name, name_length);
memcpy(&hdr[offset + name_length], ": ", 2);
memcpy(&hdr[offset + name_length + 2], value, value_length);
memcpy(&hdr[offset + name_length + 2 + value_length], "\r\n", 2);
return header_length;
}
/***************************************************************************
***************************************************************************/
static const char
http_hello[] = "GET / HTTP/1.0\r\n"
#ifdef IVRE_BUILD
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.\r\n"
#else
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.\r\n"
#endif
"Accept: */*\r\n"
//"Connection: Keep-Alive\r\n"
//"Content-Length: 0\r\n"
"\r\n";
/*****************************************************************************
*****************************************************************************/
void
field_name(struct BannerOutput *banout, size_t id,
struct Patterns *xhttp_fields);
void
field_name(struct BannerOutput *banout, size_t id,
struct Patterns *xhttp_fields)
{
unsigned i;
if (id == HTTPFIELD_INCOMPLETE)
return;
if (id == HTTPFIELD_UNKNOWN)
return;
if (id == HTTPFIELD_NEWLINE)
return;
for (i=0; xhttp_fields[i].pattern; i++) {
if (xhttp_fields[i].id == id) {
banout_newline(banout, PROTO_HTTP);
banout_append( banout, PROTO_HTTP,
(const unsigned char*)xhttp_fields[i].pattern
+ ((xhttp_fields[i].pattern[0]=='<')?1:0), /* bah. hack. ugly. */
xhttp_fields[i].pattern_length
- ((xhttp_fields[i].pattern[0]=='<')?1:0) /* bah. hack. ugly. */
);
return;
}
}
}
/*****************************************************************************
* Initialize some stuff that's part of the HTTP state-machine-parser.
*****************************************************************************/
static void *
http_init(struct Banner1 *b)
{
unsigned i;
/*
* These match HTTP Header-Field: names
*/
b->http_fields = smack_create("http", SMACK_CASE_INSENSITIVE);
for (i=0; http_fields[i].pattern; i++)
smack_add_pattern(
b->http_fields,
http_fields[i].pattern,
http_fields[i].pattern_length,
http_fields[i].id,
http_fields[i].is_anchored);
smack_compile(b->http_fields);
/*
* These match HTML <tag names
*/
b->html_fields = smack_create("html", SMACK_CASE_INSENSITIVE);
for (i=0; html_fields[i].pattern; i++)
smack_add_pattern(
b->html_fields,
html_fields[i].pattern,
html_fields[i].pattern_length,
html_fields[i].id,
html_fields[i].is_anchored);
smack_compile(b->html_fields);
banner_http.hello = MALLOC(banner_http.hello_length);
memcpy((char*)banner_http.hello, http_hello, banner_http.hello_length);
return b->http_fields;
}
/***************************************************************************
* BIZARRE CODE ALERT!
*
* This uses a "byte-by-byte state-machine" to parse the response HTTP
* header. This is standard practice for high-performance network
* devices, but is probably unfamiliar to the average network engineer.
*
* The way this works is that each byte of input causes a transition to
* the next state. That means we can parse the response from a server
* without having to buffer packets. The server can send the response
* one byte at a time (one packet for each byte) or in one entire packet.
* Either way, we don't. We don't need to buffer the entire response
* header waiting for the final packet to arrive, but handle each packet
* individually.
*
* This is especially useful with our custom TCP stack, which simply
* rejects out-of-order packets.
***************************************************************************/
static void
http_parse(
const struct Banner1 *banner1,
void *banner1_private,
struct StreamState *pstate,
const unsigned char *px, size_t length,
struct BannerOutput *banout,
struct stack_handle_t *socket)
{
unsigned state = pstate->state;
unsigned i;
unsigned state2;
unsigned log_begin = 0;
unsigned log_end = 0;
size_t id;
enum {
FIELD_START = 9,
FIELD_NAME,
FIELD_COLON,
FIELD_VALUE,
CONTENT,
CONTENT_TAG,
CONTENT_FIELD,
DONE_PARSING
};
UNUSEDPARM(banner1_private);
UNUSEDPARM(socket);
state2 = (state>>16) & 0xFFFF;
id = (state>>8) & 0xFF;
state = (state>>0) & 0xFF;
for (i=0; i<length; i++)
switch (state) {
case 0: case 1: case 2: case 3: case 4:
if (toupper(px[i]) != "HTTP/"[state]) {
state = DONE_PARSING;
tcpapi_close(socket);
} else
state++;
break;
case 5:
if (px[i] == '.')
state++;
else if (!isdigit(px[i])) {
state = DONE_PARSING;
tcpapi_close(socket);
}
break;
case 6:
if (isspace(px[i]))
state++;
else if (!isdigit(px[i])) {
state = DONE_PARSING;
tcpapi_close(socket);
}
break;
case 7:
/* TODO: look for 1xx response code */
if (px[i] == '\n')
state = FIELD_START;
break;
case FIELD_START:
if (px[i] == '\r')
break;
else if (px[i] == '\n') {
state2 = 0;
state = CONTENT;
log_end = i;
banout_append(banout, PROTO_HTTP, px+log_begin, log_end-log_begin);
log_begin = log_end;
break;
} else {
state2 = 0;
state = FIELD_NAME;
/* drop down */
}
case FIELD_NAME:
if (px[i] == '\r')
break;
id = smack_search_next(
banner1->http_fields,
&state2,
px, &i, (unsigned)length);
i--;
if (id == HTTPFIELD_NEWLINE) {
state2 = 0;
state = FIELD_START;
} else if (id == SMACK_NOT_FOUND)
; /* continue here */
else if (id == HTTPFIELD_UNKNOWN) {
/* Oops, at this point, both ":" and "Server:" will match.
* Therefore, we need to make sure ":" was found, and not
* a known field like "Server:" */
size_t id2;
id2 = smack_next_match(banner1->http_fields, &state2);
if (id2 != SMACK_NOT_FOUND)
id = id2;
state = FIELD_COLON;
} else
state = FIELD_COLON;
break;
case FIELD_COLON:
if (px[i] == '\n') {
state = FIELD_START;
break;
} else if (isspace(px[i])) {
break;
} else {
//field_name(banout, id, http_fields);
state = FIELD_VALUE;
/* drop down */
}
case FIELD_VALUE:
if (px[i] == '\r')
break;
else if (px[i] == '\n') {
state = FIELD_START;
break;
}
switch (id) {
case HTTPFIELD_SERVER:
banout_append(banout, PROTO_HTTP_SERVER, &px[i], 1);
break;
case HTTPFIELD_LOCATION:
case HTTPFIELD_VIA:
//banner_append(&px[i], 1, banout);
break;
case HTTPFIELD_CONTENT_LENGTH:
if (isdigit(px[i]&0xFF)) {
; /* TODO: add content length parsing */
} else {
id = 0;
}
break;
}
break;
case CONTENT:
{
unsigned next = i;
id = smack_search_next(
banner1->html_fields,
&state2,
px, &next, (unsigned)length);
if (banner1->is_capture_html) {
banout_append(banout, PROTO_HTML_FULL, &px[i], next-i);
}
if (id != SMACK_NOT_FOUND) {
state = CONTENT_TAG;
}
i = next - 1;
}
break;
case CONTENT_TAG:
for (; i<length; i++) {
if (banner1->is_capture_html) {
banout_append_char(banout, PROTO_HTML_FULL, px[i]);
}
if (px[i] == '>') {
state = CONTENT_FIELD;
break;
}
}
break;
case CONTENT_FIELD:
if (banner1->is_capture_html) {
banout_append_char(banout, PROTO_HTML_FULL, px[i]);
}
if (px[i] == '<')
state = CONTENT;
else {
banout_append_char(banout, PROTO_HTML_TITLE, px[i]);
}
break;
case DONE_PARSING:
default:
i = (unsigned)length;
break;
}
if (log_end == 0 && state < CONTENT)
log_end = i;
if (log_begin < log_end)
banout_append(banout, PROTO_HTTP, px + log_begin, log_end-log_begin);
if (state == DONE_PARSING)
pstate->state = state;
else
pstate->state = (state2 & 0xFFFF) << 16
| ((unsigned)id & 0xFF) << 8
| (state & 0xFF);
}
static const char *test_response =
"HTTP/1.0 200 OK\r\n"
"Date: Wed, 13 Jan 2021 18:18:25 GMT\r\n"
"Expires: -1\r\n"
"Cache-Control: private, max-age=0\r\n"
"Content-Type: text/html; charset=ISO-8859-1\r\n"
"P3P: CP=\x22This is not a P3P policy! See g.co/p3phelp for more info.\x22\r\n"
"Server: gws\r\n"
"X-XSS-Protection: 0\r\n"
"X-Frame-Options: SAMEORIGIN\r\n"
"Set-Cookie: 1P_JAR=2021-01-13-18; expires=Fri, 12-Feb-2021 18:18:25 GMT; path=/; domain=.google.com; Secure\r\n"
"Set-Cookie: NID=207=QioO2ZqRsR6k1wtvXjuuhLrXYtl6ki8SQhf56doo_wcADvldNoHfnKvFk1YXdxSVTWnmqHQVPC6ZudGneMs7vDftJ6vB36B0OCDy_KetZ3sOT_ZAHcmi1pAGeO0VekZ0SYt_UXMjcDhuvNVW7hbuHEeXQFSgBywyzB6mF2EVN00; expires=Thu, 15-Jul-2021 18:18:25 GMT; path=/; domain=.google.com; HttpOnly\r\n"
"Accept-Ranges: none\r\n"
"Vary: Accept-Encoding\r\n"
"\r\n";
/***************************************************************************
***************************************************************************/
static int
http_selftest_parser(void)
{
struct Banner1 *banner1 = NULL;
struct StreamState pstate[1];
struct BannerOutput banout[1];
memset(pstate, 0, sizeof(pstate[0]));
memset(banout, 0, sizeof(banout[0]));
/*
* Test start
*/
banner1 = banner1_create();
banner1->is_capture_servername = 1;
memset(pstate, 0, sizeof(pstate[0]));
banout_init(banout);
/*
* Run Test
*/
http_parse(banner1, 0, pstate, (const unsigned char *)test_response, strlen(test_response), banout, 0);
/*
* Verify results
*/
{
const unsigned char *string;
size_t length;
string = banout_string(banout, PROTO_HTTP_SERVER);
length = banout_string_length(banout, PROTO_HTTP_SERVER);
if (length != 3 || memcmp(string, "gws", 3) != 0) {
fprintf(stderr, "[-] HTTP parser failed: %s %u\n", __FILE__, __LINE__);
return 1; /* failure */
}
}
/*
* Test end
*/
banner1_destroy(banner1);
banout_release(banout);
return 0; /* success */
}
/***************************************************************************
***************************************************************************/
static int
http_selftest_config(void)
{
size_t i;
static const struct {const char *from; const char *to;} urlsamples[] = {
{"", "GET /foo.html"},
{"GET / HTTP/1.0\r\n\r\n", "GET /foo.html HTTP/1.0\r\n\r\n"},
{"GET /longerthan HTTP/1.0\r\n\r\n", "GET /foo.html HTTP/1.0\r\n\r\n"},
{0,0}
};
static const struct {const char *from; const char *to;} methodsamples[] = {
{"", "POST"},
{"GET / HTTP/1.0\r\n\r\n", "POST / HTTP/1.0\r\n\r\n"},
{"O / HTTP/1.0\r\n\r\n", "POST / HTTP/1.0\r\n\r\n"},
{0,0}
};
static const struct {const char *from; const char *to;} versionsamples[] = {
{"", "GET / HTTP/1.1"},
{"GET / FOO\r\n\r\n", "GET / HTTP/1.1\r\n\r\n"},
{"GET / XXXXXXXXXXXX\r\n\r\n", "GET / HTTP/1.1\r\n\r\n"},
{0,0}
};
static const struct {const char *from; const char *to;} fieldsamples[] = {
{"GET / HTTP/1.0\r\nfoobar: a\r\nHost: xyz\r\n\r\n", "GET / HTTP/1.0\r\nfoobar: a\r\nHost: xyz\r\nfoo: bar\r\n\r\n"},
{"GET / HTTP/1.0\r\nfoo:abc\r\nHost: xyz\r\n\r\n", "GET / HTTP/1.0\r\nfoo: bar\r\nHost: xyz\r\n\r\n"},
{"GET / HTTP/1.0\r\nfoo: abcdef\r\nHost: xyz\r\n\r\n", "GET / HTTP/1.0\r\nfoo: bar\r\nHost: xyz\r\n\r\n"},
{"GET / HTTP/1.0\r\nfoo: a\r\nHost: xyz\r\n\r\n", "GET / HTTP/1.0\r\nfoo: bar\r\nHost: xyz\r\n\r\n"},
{"GET / HTTP/1.0\r\nHost: xyz\r\n\r\n", "GET / HTTP/1.0\r\nHost: xyz\r\nfoo: bar\r\n\r\n"},
{0,0}
};
static const struct {const char *from; const char *to;} removesamples[] = {
{"GET / HTTP/1.0\r\nfoo: a\r\nHost: xyz\r\n\r\n", "GET / HTTP/1.0\r\nHost: xyz\r\n\r\n"},
{"GET / HTTP/1.0\r\nfooa: a\r\nHost: xyz\r\n\r\n", "GET / HTTP/1.0\r\nfooa: a\r\nHost: xyz\r\n\r\n"},
{0,0}
};
static const struct {const char *from; const char *to;} payloadsamples[] = {
{"", "GET / HTTP/1.0\r\n\r\nfoo"},
{"GET / HTTP/1.0\r\nHost: xyz\r\n\r\nbar", "GET / HTTP/1.0\r\nHost: xyz\r\n\r\nfoo"},
{0,0}
};
/* Test replacing URL */
for (i=0; urlsamples[i].from; i++) {
unsigned char *x = (unsigned char*)STRDUP(urlsamples[i].from);
size_t len1 = strlen((const char *)x);
size_t len2;
size_t len3 = strlen(urlsamples[i].to);
/* Replace whatever URL is in the header with this new one */
len2 = http_change_requestline(&x, len1, "/foo.html", ~(size_t)0, 1);
if (len2 != len3 && memcmp(urlsamples[i].to, x, len3) != 0) {
fprintf(stderr, "[-] HTTP.selftest: config URL sample #%u\n", (unsigned)i);
return 1;
}
}
/* Test replacing method */
for (i=0; methodsamples[i].from; i++) {
unsigned char *x = (unsigned char*)STRDUP(methodsamples[i].from);
size_t len1 = strlen((const char *)x);
size_t len2;
size_t len3 = strlen(methodsamples[i].to);
len2 = http_change_requestline(&x, len1, "POST", ~(size_t)0, 0);
if (len2 != len3 && memcmp(methodsamples[i].to, x, len3) != 0) {
fprintf(stderr, "[-] HTTP.selftest: config method sample #%u\n", (unsigned)i);
return 1;
}
}
/* Test replacing version */
for (i=0; versionsamples[i].from; i++) {
unsigned char *x = (unsigned char*)STRDUP(versionsamples[i].from);
size_t len1 = strlen((const char *)x);
size_t len2;
size_t len3 = strlen(versionsamples[i].to);
len2 = http_change_requestline(&x, len1, "HTTP/1.1", ~(size_t)0, 2);
if (len2 != len3 && memcmp(versionsamples[i].to, x, len3) != 0) {
fprintf(stderr, "[-] HTTP.selftest: config version sample #%u\n", (unsigned)i);
return 1;
}
}
/* Test payload */
for (i=0; payloadsamples[i].from; i++) {
unsigned char *x = (unsigned char*)STRDUP(payloadsamples[i].from);
size_t len1 = strlen((const char *)x);
size_t len2;
size_t len3 = strlen(payloadsamples[i].to);
len2 = http_change_requestline(&x, len1, "foo", ~(size_t)0, 3);
if (len2 != len3 && memcmp(payloadsamples[i].to, x, len3) != 0) {
fprintf(stderr, "[-] HTTP.selftest: config payload sample #%u\n", (unsigned)i);
return 1;
}
}
/* Test adding fields */
for (i=0; fieldsamples[i].from; i++) {
unsigned char *x;
size_t len1 = strlen((const char *)fieldsamples[i].from);
size_t len2;
size_t len3 = strlen(fieldsamples[i].to);
/* Replace whatever URL is in the header with this new one */
x = (unsigned char*)STRDUP(fieldsamples[i].from);
len2 = http_change_field(&x, len1, "foo", (const unsigned char *)"bar", ~(size_t)0, http_field_replace);
if (len2 != len3 || memcmp(fieldsamples[i].to, x, len3) != 0) {
fprintf(stderr, "[-] HTTP.selftest: config header field sample #%u\n", (unsigned)i);
return 1;
}
free(x);
/* Same test as above, but when name specified with a colon */
x = (unsigned char*)STRDUP(fieldsamples[i].from);
len2 = http_change_field(&x, len1, "foo:", (const unsigned char *)"bar", ~(size_t)0, http_field_replace);
if (len2 != len3 || memcmp(fieldsamples[i].to, x, len3) != 0) {
fprintf(stderr, "[-] HTTP.selftest: config header field sample #%u\n", (unsigned)i);
return 1;
}
free(x);
/* Same test as above, but with name having additional space */
x = (unsigned char*)STRDUP(fieldsamples[i].from);
len2 = http_change_field(&x, len1, "foo : : ", (const unsigned char *)"bar", ~(size_t)0, http_field_replace);
if (len2 != len3 || memcmp(fieldsamples[i].to, x, len3) != 0) {
fprintf(stderr, "[-] HTTP.selftest: config header field sample #%u\n", (unsigned)i);
return 1;
}
free(x);
}
/* Removing fields */
for (i=0; removesamples[i].from; i++) {
unsigned char *x = (unsigned char*)STRDUP(removesamples[i].from);
size_t len1 = strlen((const char *)x);
size_t len2;
size_t len3 = strlen(removesamples[i].to);
/* Replace whatever URL is in the header with this new one */
len2 = http_change_field(&x, len1, "foo", (const unsigned char *)"bar", ~(size_t)0, http_field_remove);
if (len2 != len3 || memcmp(removesamples[i].to, x, len3) != 0) {
fprintf(stderr, "[-] HTTP.selftest: config remove field sample #%u\n", (unsigned)i);
return 1;
}
free(x);
}
return 0;
}
/***************************************************************************
* Called when `--selftest` command-line parameter in order to do some
* basic unit testing of this module.
***************************************************************************/
static int
http_selftest(void)
{
int err;
/* Test parsing HTTP responses */
err = http_selftest_parser();
if (err)
return 1; /* failure */
/* Test configuring HTTP requests */
err = http_selftest_config();
if (err)
return 1; /* failure */
return 0; /* success */
}
/***************************************************************************
***************************************************************************/
struct ProtocolParserStream banner_http = {
"http", 80, http_hello, sizeof(http_hello)-1, 0,
http_selftest,
http_init,
http_parse,
};

35
src/proto-http.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef PROTO_HTTP_H
#define PROTO_HTTP_H
#include "proto-banner1.h"
#include "util-bool.h"
extern struct ProtocolParserStream banner_http;
/**
* Called during configuration when processing a command-line option
* like "--http-field <name=value>" to add/change a field in the HTTP
* header.
*/
size_t
http_change_field(unsigned char **inout_header, size_t header_length,
const char *field_name,
const unsigned char *field_value, size_t field_value_len,
int what);
/**
* Called during configuration when processing a command-line option
* like "--http-url /foo.html". This replaces whatever the existing
* URL is into the new one.
* @param item
* 0=method, 1=url, 2=version
* @return
* the new length of the header (expanded or shrunk)
*/
size_t
http_change_requestline(unsigned char **inout_header, size_t header_length,
const void *url, size_t url_length, int item);
#endif

190
src/proto-icmp.c Normal file
View File

@ -0,0 +1,190 @@
#include "proto-icmp.h"
#include "proto-preprocess.h"
#include "syn-cookie.h"
#include "util-logger.h"
#include "output.h"
#include "masscan-status.h"
#include "massip-port.h"
#include "main-dedup.h"
/***************************************************************************
***************************************************************************/
static int
matches_me(struct Output *out, ipaddress ip, unsigned port)
{
unsigned i;
for (i=0; i<8; i++) {
if (is_myself(&out->src[i], ip, port))
return 1;
}
return 0;
}
/***************************************************************************
***************************************************************************/
static int
parse_port_unreachable(const unsigned char *px, unsigned length,
unsigned *r_ip_me, unsigned *r_ip_them,
unsigned *r_port_me, unsigned *r_port_them,
unsigned *r_ip_proto)
{
if (length < 24)
return -1;
*r_ip_me = px[12]<<24 | px[13]<<16 | px[14]<<8 | px[15];
*r_ip_them = px[16]<<24 | px[17]<<16 | px[18]<<8 | px[19];
*r_ip_proto = px[9]; /* TCP=6, UDP=17 */
px += (px[0]&0xF)<<2;
length -= (px[0]&0xF)<<2;
if (length < 4)
return -1;
*r_port_me = px[0]<<8 | px[1];
*r_port_them = px[2]<<8 | px[3];
return 0;
}
/***************************************************************************
* This is where we handle all incoming ICMP packets. Some of these packets
* will be due to scans we are doing, like pings (echoes). Some will
* be inadvertent, such as "destination unreachable" messages.
***************************************************************************/
void
handle_icmp(struct Output *out, time_t timestamp,
const unsigned char *px, unsigned length,
struct PreprocessedInfo *parsed,
uint64_t entropy)
{
unsigned type = parsed->port_src;
unsigned code = parsed->port_dst;
unsigned seqno_me;
ipaddress ip_me = parsed->dst_ip;
ipaddress ip_them = parsed->src_ip;
unsigned cookie;
/* dedup ICMP echo replies as well as SYN/ACK replies */
static struct DedupTable *echo_reply_dedup = NULL;
if (!echo_reply_dedup)
echo_reply_dedup = dedup_create();
seqno_me = px[parsed->transport_offset+4]<<24
| px[parsed->transport_offset+5]<<16
| px[parsed->transport_offset+6]<<8
| px[parsed->transport_offset+7]<<0;
switch (type) {
case 0: /* ICMP echo reply */
case 129:
cookie = (unsigned)syn_cookie(ip_them, Templ_ICMP_echo, ip_me, 0, entropy);
if ((cookie & 0xFFFFFFFF) != seqno_me)
return; /* not my response */
if (dedup_is_duplicate(echo_reply_dedup, ip_them, 0, ip_me, 0))
break;
//if (syn_hash(ip_them, Templ_ICMP_echo) != seqno_me)
// return; /* not my response */
/*
* Report "open" or "existence" of host
*/
output_report_status(
out,
timestamp,
PortStatus_Open,
ip_them,
1, /* ip proto */
0,
0,
parsed->ip_ttl,
parsed->mac_src);
break;
case 3: /* destination unreachable */
switch (code) {
case 0: /* net unreachable */
/* We get these a lot while port scanning, often a flood coming
* back from broken/misconfigured networks */
break;
case 1: /* host unreachable */
/* This means the router doesn't exist */
break;
case 2: /* protocol unreachable */
/* The host exists, but it doesn't support SCTP */
break;
case 3: /* port unreachable */
if (length - parsed->transport_offset > 8) {
ipaddress ip_me2;
ipaddress ip_them2;
unsigned port_me2, port_them2;
unsigned ip_proto;
int err;
ip_me2.version = 4;
ip_them2.version = 4;
err = parse_port_unreachable(
px + parsed->transport_offset + 8,
length - parsed->transport_offset + 8,
&ip_me2.ipv4, &ip_them2.ipv4, &port_me2, &port_them2,
&ip_proto);
if (err)
return;
if (!matches_me(out, ip_me2, port_me2))
return;
switch (ip_proto) {
case 6:
output_report_status(
out,
timestamp,
PortStatus_Closed,
ip_them2,
ip_proto,
port_them2,
0,
parsed->ip_ttl,
parsed->mac_src);
break;
case 17:
output_report_status(
out,
timestamp,
PortStatus_Closed,
ip_them2,
ip_proto,
port_them2,
0,
parsed->ip_ttl,
parsed->mac_src);
break;
case 132:
output_report_status(
out,
timestamp,
PortStatus_Closed,
ip_them2,
ip_proto,
port_them2,
0,
parsed->ip_ttl,
parsed->mac_src);
break;
}
}
}
break;
default:
;
}
}

13
src/proto-icmp.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef PROTO_ICMP_H
#define PROTO_ICMP_H
#include <time.h>
#include <stdint.h>
struct PreprocessedInfo;
struct Output;
void handle_icmp(struct Output *out, time_t timestamp,
const unsigned char *px, unsigned length,
struct PreprocessedInfo *parsed,
uint64_t entropy);
#endif

220
src/proto-imap4.c Normal file
View File

@ -0,0 +1,220 @@
/*
imap4 banner checker
*/
#include "proto-imap4.h"
#include "proto-banner1.h"
#include "unusedparm.h"
#include "masscan-app.h"
#include "stack-tcp-api.h"
#include "proto-ssl.h"
#include <ctype.h>
#include <string.h>
/***************************************************************************
***************************************************************************/
static void
imap4_parse( const struct Banner1 *banner1,
void *banner1_private,
struct StreamState *pstate,
const unsigned char *px, size_t length,
struct BannerOutput *banout,
struct stack_handle_t *socket)
{
unsigned state = pstate->state;
unsigned i;
UNUSEDPARM(banner1_private);
UNUSEDPARM(banner1);
for (i=0; i<length; i++) {
if (px[i] == '\r')
continue;
switch (state) {
case 0:
banout_append_char(banout, PROTO_IMAP4, px[i]);
if (px[i] == '*')
state++;
else {
state = 0xffffffff;
tcpapi_close(socket);
}
break;
case 1:
if (px[i] == ' ') {
banout_append_char(banout, PROTO_IMAP4, px[i]);
continue;
} else {
state = 0xffffffff;
tcpapi_close(socket);
}
/* fall through */
case 2:
banout_append_char(banout, PROTO_IMAP4, px[i]);
if (px[i] == 'O')
state++;
else {
state = 0xffffffff;
tcpapi_close(socket);
}
break;
case 3:
banout_append_char(banout, PROTO_IMAP4, px[i]);
if (px[i] == 'K')
state++;
else {
state = 0xffffffff;
tcpapi_close(socket);
}
break;
case 4:
if (px[i] == ' ') {
banout_append_char(banout, PROTO_IMAP4, px[i]);
state++;
break;
} else if (px[i] != '\n') {
banout_append_char(banout, PROTO_IMAP4, px[i]);
/* no transition */
break;
} else {
state++;
/* fall through */
}
case 5:
banout_append_char(banout, PROTO_IMAP4, px[i]);
if (px[i] == '\n') {
tcpapi_send(socket, "a001 CAPABILITY\r\n", 17, 0);
state = 100;
}
break;
case 100:
case 300:
banout_append_char(banout, PROTO_IMAP4, px[i]);
if (px[i] == '*')
state += 100;
else if (px[i] == 'a')
state++;
else {
state = 0xffffffff;
tcpapi_close(socket);
}
break;
case 101:
case 301:
banout_append_char(banout, PROTO_IMAP4, px[i]);
if (px[i] == '0')
state++;
else {
state = 0xffffffff;
tcpapi_close(socket);
}
break;
case 102:
case 302:
banout_append_char(banout, PROTO_IMAP4, px[i]);
if (px[i] == '0')
state++;
else {
state = 0xffffffff;
tcpapi_close(socket);
}
break;
case 103:
banout_append_char(banout, PROTO_IMAP4, px[i]);
if (px[i] == '1')
state++;
else {
state = 0xffffffff;
tcpapi_close(socket);
}
break;
case 303:
banout_append_char(banout, PROTO_IMAP4, px[i]);
if (px[i] == '2')
state++;
else {
state = 0xffffffff;
tcpapi_close(socket);
}
break;
case 104:
case 304:
banout_append_char(banout, PROTO_IMAP4, px[i]);
if (px[i] == ' ')
state++;
else {
state = 0xffffffff;
tcpapi_close(socket);
}
break;
case 105:
banout_append_char(banout, PROTO_IMAP4, px[i]);
if (px[i] == '\n') {
tcpapi_send(socket, "a002 STARTTLS\r\n", 15, 0);
state = 300;
}
break;
case 200:
case 400:
banout_append_char(banout, PROTO_IMAP4, px[i]);
if (px[i] == '\n')
state -= 100;
break;
case 305:
if (px[i] == '\n') {
/* change the state here to SSL */
unsigned port = pstate->port;
memset(pstate, 0, sizeof(*pstate));
pstate->app_proto = PROTO_SSL3;
pstate->is_sent_sslhello = 1;
pstate->port = (unsigned short)port;
state = 0;
tcpapi_send(socket, banner_ssl.hello, banner_ssl.hello_length, 0);
break;
}
break;
case 0xffffffff:
default:
i = (unsigned)length;
break;
}
}
pstate->state = state;
}
/***************************************************************************
***************************************************************************/
static void *
imap4_init(struct Banner1 *banner1)
{
UNUSEDPARM(banner1);
return 0;
}
/***************************************************************************
***************************************************************************/
static int
imap4_selftest(void)
{
return 0;
}
/***************************************************************************
***************************************************************************/
const struct ProtocolParserStream banner_imap4 = {
"imap4", 21, 0, 0, 0,
imap4_selftest,
imap4_init,
imap4_parse,
};

7
src/proto-imap4.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef PROTO_IMAP4_H
#define PROTO_IMAP4_H
#include "proto-banner1.h"
extern const struct ProtocolParserStream banner_imap4;
#endif

580
src/proto-isakmp.c Normal file
View File

@ -0,0 +1,580 @@
/* ISAKMP protocol support
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Initiator !
! Cookie !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Responder !
! Cookie !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Next Payload ! MjVer ! MnVer ! Exchange Type ! Flags !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Message ID !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Length !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
#include "proto-isakmp.h"
#include "proto-banout.h"
#include "proto-preprocess.h"
#include "syn-cookie.h"
#include "massip-port.h"
#include "output.h"
#include "util-extract.h"
#include "util-logger.h"
#include <stdarg.h>
#include <string.h>
typedef struct payload_t {
unsigned char next;
unsigned char reserved;
size_t length;
struct ebuf_t ebuf;
} payload_t;
static payload_t
_get_payload(const struct ebuf_t *ebuf) {
payload_t result = {0};
result.ebuf = *ebuf;
result.next = e_next_byte(&result.ebuf);
result.reserved = e_next_byte(&result.ebuf);
result.length = e_next_short16(&result.ebuf, EBUF_BE);
if (result.length >= 4) {
result.ebuf.max = result.ebuf.offset + result.length - 4;
}
return result;
}
static unsigned
_parse_transform(struct BannerOutput *banout,
unsigned proto,
struct ebuf_t in_ebuf) {
struct ebuf_t *ebuf = &in_ebuf;
unsigned transform_id;
e_next_byte(ebuf); /* transform number */
transform_id = e_next_byte(ebuf);
switch (transform_id) {
case 1: {
banout_printf(banout, proto, "trans=IKE ");
e_next_short16(ebuf, EBUF_BE); /* reserved */
while (ebuf->offset < ebuf->max) {
unsigned x = e_next_short16(ebuf, EBUF_BE);
unsigned val = e_next_short16(ebuf, EBUF_BE);
if ((x & 0x8000) == 0)
return 1;
switch (x&0x7fff) {
case 1: /* encryption algorithm */
switch (val) {
case 5:
banout_printf(banout, proto, "%s ", "3DES-CBC");
break;
case 7:
banout_printf(banout, proto, "%s ", "AES-CBC");
break;
default:
banout_printf(banout, proto, "encrypt=0x%x ", val);
break;
}
break;
case 2: /* hash algorithm */
switch (val) {
case 2:
banout_printf(banout, proto, "%s ", "SHA");
break;
default:
banout_printf(banout, proto, "hash=0x%x ", val);
break;
}
break;
case 3: /* auth */
switch (val) {
case 1:
banout_printf(banout, proto, "%s ", "PSK");
break;
case 5:
banout_printf(banout, proto, "%s ", "PSK");
break;
default:
banout_printf(banout, proto, "auth=0x%x ", val);
break;
}
break;
case 4: /* group */
break;
case 11: /* life type */
break;
case 12: /* life duration*/
break;
case 14: /* key length */
banout_printf(banout, proto, "key=%ubits ", val);
break;
default:
banout_printf(banout, proto, "val=0x%04x%04x ", x&0x7fff, val);
break;
}
}
}
break;
default:
banout_printf(banout, proto, "trans=%u ", transform_id);
break;
}
return 1;
}
static unsigned
_parse_transforms(struct BannerOutput *banout,
unsigned proto,
struct ebuf_t ebuf,
unsigned next_payload
) {
while (ebuf.offset + 4 <= ebuf.max) {
payload_t payload = _get_payload(&ebuf);
_parse_transform(banout, proto, payload.ebuf);
/* loop around */
ebuf.offset += payload.length;
next_payload = payload.next;
if (next_payload == 0)
break;
}
return 0;
}
static unsigned
_parse_proposal(struct BannerOutput *banout,
unsigned proto,
struct ebuf_t ebuf) {
unsigned proto_id;
banout_printf(banout, proto, "%u ", e_next_byte(&ebuf));
proto_id = e_next_byte(&ebuf);
switch (proto_id) {
case 1:
banout_printf(banout, proto, "id=ISAKMP ");
break;
default:
banout_printf(banout, proto, "id=%u ", proto_id);
break;
}
e_next_byte(&ebuf); /* spi size */
e_next_byte(&ebuf); /* proposal transforms */
_parse_transforms(banout, proto, ebuf, 0);
return 1;
}
static unsigned
_parse_proposals(struct BannerOutput *banout,
unsigned proto,
struct ebuf_t ebuf,
unsigned next_payload
) {
while (ebuf.offset + 4 <= ebuf.max) {
payload_t payload = _get_payload(&ebuf);
_parse_proposal(banout, proto, payload.ebuf);
/* loop around */
ebuf.offset += payload.length;
next_payload = payload.next;
if (next_payload == 0)
break;
}
return 0;
}
static unsigned
_payload_security_association(struct BannerOutput *banout, unsigned proto, struct ebuf_t ebuf) {
unsigned doi;
unsigned bitmap;
doi = e_next_int32(&ebuf, EBUF_BE);
bitmap = e_next_int32(&ebuf, EBUF_BE);
switch (doi) {
case 0: /* generic */
banout_printf(banout, proto, "DOI=generic ");
break;
case 1: /* IPsec */
banout_printf(banout, proto, "DOI=ipsec ");
if (bitmap & 0x00000001)
banout_printf(banout, proto, "IDENTITY ");
if (bitmap & 0x00000002)
banout_printf(banout, proto, "SECRECY ");
if (bitmap & 0x00000004)
banout_printf(banout, proto, "INTEGRITY ");
_parse_proposals(banout, proto, ebuf, 0);
break;
default:
banout_printf(banout, proto, "DOI=%u ", doi);
break;
}
return 1;
}
static unsigned
_payload_vendor_id(struct BannerOutput *banout, unsigned proto, struct ebuf_t ebuf) {
size_t i;
size_t length = ebuf.max - ebuf.offset;
struct {
unsigned length;
const char *vendor;
const char *name;
} vendors[] = {
{16, "\x4a\x13\x1c\x81\x07\x03\x58\x45\x5c\x57\x28\xf2\x0e\x95\x45\x2f", "RFC-39947-NAT"},
{16, "\x12\xf5\xf2\x8c\x45\x71\x68\xa9\x70\x2d\x9f\xe2\x74\xcc\x01\x00", "CISCO-UNITY"},
{16, "\xaf\xca\xd7\x13\x68\xa1\xf1\xc9\x6b\x86\x96\xfc\x77\x57\x01\x00", "RFC3706-DPD"},
{8, "\x09\x00" "&\x89\xdf\xd6\xb7\x12", "XAUTH"},
{0,0}
};
for (i=0; vendors[i].length; i++) {
if (length != vendors[i].length)
continue;
if (memcmp(vendors[i].vendor, ebuf.buf + ebuf.offset, length) == 0) {
banout_printf(banout, proto, "{%s} ", vendors[i].name);
break;
}
}
return 1;
}
static unsigned
_parse_response(struct BannerOutput *banout, unsigned proto,
const unsigned char *px, size_t length
) {
struct ebuf_t ebuf[1] = {{px, 0, length}};
unsigned next_payload;
unsigned version;
unsigned flags;
unsigned exchange_type;
unsigned my_length;
static const char *payload_names[] = {
"[0]", "[SEC-ASSOC]", "[2]", "[3]",
"[KEY-XCHG]", "[5]", "[6]", "[7]",
"[8]", "[9]", "[NONCE]", "[11]",
"[12]", ""/*vendor-id*/, "[14]", "[15]",
"[16]", "[17]", "[18]", "[19]",
"[NAT-D]", "[21]", "[22]", "[23]",
"[24]", "[25]", "[26]", "[27]",
"[28]", "[29]", "[30]", "[31]",
};
/*
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Initiator !
! Cookie !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Responder !
! Cookie !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Next Payload ! MjVer ! MnVer ! Exchange Type ! Flags !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Message ID !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Length !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
/* Skip the cookies */
e_next_long64(ebuf, EBUF_BE);
e_next_long64(ebuf, EBUF_BE);
/* Parse the header */
next_payload = e_next_byte(ebuf);
version = e_next_byte(ebuf);
exchange_type = e_next_byte(ebuf);
flags = e_next_byte(ebuf); /* flags */
e_next_int32(ebuf, 0);
my_length = e_next_int32(ebuf, EBUF_BE);
if (ebuf->max >= my_length)
ebuf->max = my_length;
banout_printf(banout, proto, "v%u.%u ", (version>>4)&0xF, version&0xF);
switch (exchange_type) {
case 2:
banout_printf(banout, proto, "xchg=id-prot ");
break;
default:
banout_printf(banout, proto, "xchg=%u ", exchange_type);
break;
}
if (flags & 1) {
banout_printf(banout, proto, "ENCRYPTED ", exchange_type);
return 1;
}
/*
* Enumerate all the payloads
*/
while (next_payload && ebuf->offset + 4 <= ebuf->max) {
/*
* Parse this payload-header
*/
payload_t payload = _get_payload(ebuf);
/*
* Print the payload name if it's in our list of known names,
* or print a number if it isn't
*/
if (next_payload < sizeof(payload_names)/sizeof(payload_names[0]))
banout_printf(banout, proto, "%s ", payload_names[next_payload]);
else
banout_printf(banout, proto, "[%u] ", next_payload);
/*
* Handle the individual payload if it's a known type
*/
switch (next_payload) {
case 1:
_payload_security_association(banout, proto, payload.ebuf);
break;
case 4: /* key exchange */
//banout_printf(banout, proto, "KEY-EXCH ");
break;
case 10: /* nonce */
//banout_printf(banout, proto, "NONCE ");
break;
case 13: /* vendir id */
_payload_vendor_id(banout, proto, payload.ebuf);
break;
case 20:
//banout_printf(banout, proto, "NAT-D ");
break;
default:
break;
}
/*
* Loop around to the next payload
*/
ebuf->offset += payload.length;
next_payload = payload.next;
}
return 1;
}
unsigned
isakmp_parse(struct Output *out, time_t timestamp,
const unsigned char *px, unsigned length,
struct PreprocessedInfo *parsed,
uint64_t entropy
)
{
ipaddress ip_them;
ipaddress ip_me;
unsigned port_them = parsed->port_src;
unsigned port_me = parsed->port_dst;
uint64_t cookie;
uint64_t resp_cookie;
/* All responses will be at least 8 bytes */
if (length < 16)
return 0;
/* Grab IP addresses */
ip_them = parsed->src_ip;
ip_me = parsed->dst_ip;
/* Calculate the expected SYN-cookie */
cookie = (unsigned)syn_cookie(ip_them, port_them | Templ_UDP, ip_me, port_me, entropy);
/* Extract the SYN-cookie from the response. We just do this byte-by-byte */
resp_cookie = (uint64_t)px[0] << 56ull;
resp_cookie |= (uint64_t)px[1] << 48ull;
resp_cookie |= (uint64_t)px[2] << 40ull;
resp_cookie |= (uint64_t)px[3] << 32ull;
resp_cookie |= (uint64_t)px[4] << 24ull;
resp_cookie |= (uint64_t)px[5] << 16ull;
resp_cookie |= (uint64_t)px[6] << 8ull;
resp_cookie |= (uint64_t)px[7] << 0ull;
if (resp_cookie != cookie) {
/* If they aren't equal, then this is some other protocol.
* TODO: we should use a heuristic on these bytes to
* discover what the protocol probably is */
/*output_report_banner(out, timestamp, ip_them, 17, port_them,
PROTO_ERROR, parsed->ip_ttl,
(unsigned char *) "IP-MISSMATCH", 12);*/
return 0;
} else {
/* We've found our protocol, so report the banner
* TODO: we should parse this better. */
struct BannerOutput banout[1];
banout_init(banout);
/* Parse the packet and generate strings */
_parse_response(banout, PROTO_ISAKMP, px, length);
/* Print the banner to the output */
output_report_banner(
out, timestamp,
ip_them, 17, port_them,
PROTO_ISAKMP,
parsed->ip_ttl,
banout_string(banout, PROTO_ISAKMP),
banout_string_length(banout, PROTO_ISAKMP));
banout_release(banout);
return 1;
}
}
unsigned
isakmp_set_cookie(unsigned char *px, size_t length, uint64_t seqno)
{
/*
The frame header starts with an 8 bytes init cookie, which is just
fine for us
*/
unsigned char i;
if (length < 8)
return 0;
for(i = 0; i < 8; i++)
px[i] = (unsigned char)(seqno >> (56 - 8 * i));
return 0;
}
static const unsigned char
sample1[] =
"\x00\x00\x00\x00\xc1\x18"
"\x84\xda\xbe\x3d\xc6\x8e\xea\xf2\xda\xac\x01\x10\x02\x00\x00\x00"
"\x00\x00\x00\x00\x00\x50\x00\x00\x00\x34\x00\x00\x00\x01\x00\x00"
"\x00\x01\x00\x00\x00\x28\x01\x01\x00\x01\x00\x00\x00\x20\x01\x01"
"\x00\x00\x80\x01\x00\x05\x80\x02\x00\x02\x80\x04\x00\x02\x80\x03"
"\x00\x01\x80\x0b\x00\x01\x80\x0c\x00\x01";
static const unsigned char
sample2[] = "\xe4\x7a\x59\x1f\xd0\x57"
"\x58\x7f\xa0\x0b\x8e\xf0\x90\x2b\xb8\xec\x01\x10\x02\x00\x00\x00"
"\x00\x00\x00\x00\x00\x6c\x0d\x00\x00\x3c\x00\x00\x00\x01\x00\x00"
"\x00\x01\x00\x00\x00\x30\x01\x01\x00\x01\x00\x00\x00\x28\x01\x01"
"\x00\x00\x80\x01\x00\x07\x80\x0e\x00\x80\x80\x02\x00\x02\x80\x04"
"\x00\x02\x80\x03\x00\x01\x80\x0b\x00\x01\x00\x0c\x00\x04\x00\x01"
"\x51\x80\x00\x00\x00\x14\x4a\x13\x1c\x81\x07\x03\x58\x45\x5c\x57"
"\x28\xf2\x0e\x95\x45\x2f";
static const unsigned char sample3[] =
"\xe4\x7a\x59\x1f\xd0\x57\x58\x7f\xa0\x0b\x8e\xf0\x90\x2b\xb8\xec"
"\x04\x10\x02\x00\x00\x00\x00\x00\x00\x00\x01\x30\x0a\x00\x00\x84"
"\x6d\x02\x6d\x56\x16\xc4\x5b\xe0\x5e\x5b\x89\x84\x11\xe9\xf9\x5d"
"\x19\x5c\xea\x00\x9a\xd2\x2c\x62\xbe\xf0\x6c\x57\x1b\x7c\xfb\xc4"
"\x79\x2f\x45\x56\x4e\xc7\x10\xac\x58\x4a\xa1\x8d\x20\xcb\xc8\xf5"
"\xf8\x91\x06\x66\xb8\x9e\x4e\xe2\xf9\x5a\xbc\x02\x30\xe2\xcb\xa1"
"\xb8\x8a\xc4\xbb\xa7\xfc\xc8\x18\xa9\x86\xc0\x1a\x4c\xa8\x65\xa5"
"\xeb\x82\x88\x4d\xbe\xc8\x5b\xfd\x7d\x1a\x30\x3b\x09\x89\x4d\xcf"
"\x2e\x37\x85\xfd\x79\xdb\xa2\x25\x37\x7c\xf8\xcc\xa0\x09\xce\xff"
"\xbb\x6a\xa3\x8b\x64\x8c\x4b\x05\x40\x4f\x1c\xfa\xac\x36\x1a\xff"
"\x0d\x00\x00\x18\x15\xb6\x88\x42\x1e\xd5\xc3\xdd\x92\xd3\xb8\x6e"
"\x47\xa7\x6f\x0d\x39\xcc\x09\xe0\x0d\x00\x00\x14\x12\xf5\xf2\x8c"
"\x45\x71\x68\xa9\x70\x2d\x9f\xe2\x74\xcc\x01\x00\x0d\x00\x00\x14"
"\xaf\xca\xd7\x13\x68\xa1\xf1\xc9\x6b\x86\x96\xfc\x77\x57\x01\x00"
"\x0d\x00\x00\x14\x55\xcc\x29\xed\x90\x2a\xb8\xec\x53\xb1\xdf\x86"
"\x7c\x61\x09\x29\x14\x00\x00\x0c\x09\x00\x26\x89\xdf\xd6\xb7\x12"
"\x14\x00\x00\x18\xfe\xbf\x46\x2f\x1c\xd7\x58\x05\xa7\xba\xa2\x87"
"\x47\xe7\x69\xd6\x74\xf8\x56\x00\x00\x00\x00\x18\x15\x74\xd6\x4c"
"\x01\x65\xba\xd1\x6a\x02\x3f\x03\x8d\x45\xa0\x74\x98\xd8\xd0\x51";
const char sample4[] =
"\xe4\x7a\x59\x1f\xd0\x57\x58\x7f\xa0\x0b\x8e\xf0\x90\x2b\xb8\xec"
"\x05\x10\x02\x01\x00\x00\x00\x00\x00\x00\x00\x4c\xb0\x32\xaa\xa6"
"\x2a\x70\x71\x8e\xf2\xf0\x99\xcd\xd8\xbf\x6e\xb9\x04\x42\xed\x9d"
"\x72\x6d\xaa\x6b\x6d\xad\x62\x40\x26\xf5\xfb\xb1\x73\xd9\xf7\x75"
"\x71\xc2\x32\xa5\x6a\xcf\xe1\x2c\x74\x03\xe9\x53";
static int
_test_sample(const void *sample, size_t sizeof_sample, const char *expected) {
int is_valid;
struct BannerOutput banout[1];
/* Initialize printing banners */
banout_init(banout);
/* Parse the sample */
is_valid = _parse_response(banout, PROTO_ISAKMP,
(const unsigned char*)sample,
sizeof_sample);
/* If there was a parse error, then*/
if (!is_valid)
goto fail;
{
const unsigned char *str = banout_string(banout, PROTO_ISAKMP);
size_t str_length = banout_string_length(banout, PROTO_ISAKMP);
//printf("%.*s\n", (unsigned)str_length, str);
if (str_length < strlen(expected) || memcmp(str, expected, strlen(expected)) != 0)
goto fail;
}
banout_release(banout);
return 0;
fail:
banout_release(banout);
return 0;
}
/****************************************************************************
****************************************************************************/
int
proto_isakmp_selftest(void)
{
unsigned fail_count = 0;
LOG(1, "[ ] ISAKMP: selftesting...\n");
fail_count += _test_sample(
sample1, sizeof(sample1)-1,
"v1.0 xchg=id-prot [SEC-ASSOC] DOI=ipsec IDENTITY 1 id=ISAKMP trans=IKE 3DES-CBC SHA PSK");
fail_count += _test_sample(
sample2, sizeof(sample2)-1,
"v1.0 xchg=id-prot [SEC-ASSOC] DOI=ipsec IDENTITY 1 id=ISAKMP trans=IKE AES-CBC key=128bits SHA PSK {RFC-39947-NAT}");
fail_count += _test_sample(
sample3, sizeof(sample3)-1,
"v1.0 xchg=id-prot [KEY-XCHG] [NONCE] {CISCO-UNITY} {RFC3706-DPD} {XAUTH} [NAT-D] [NAT-D]");
fail_count += _test_sample(
sample4, sizeof(sample4)-1,
"v1.0 xchg=id-prot ENCRYPTED");
if (fail_count)
goto fail;
LOG(1, "[-] ISAKMP: success\n");
return 0;
fail:
LOG(1, "[-] ISAKMP: fail\n");
return 1;
}

18
src/proto-isakmp.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef PROTO_ISAKMP_H
#define PROTO_ISAKMP_H
#include <time.h>
#include <stdint.h>
#include <stdlib.h>
struct Output;
struct PreprocessedInfo;
unsigned isakmp_parse(struct Output *out, time_t timestamp,
const unsigned char *px, unsigned length,
struct PreprocessedInfo *parsed, uint64_t entropy);
unsigned isakmp_set_cookie(unsigned char *px, size_t length, uint64_t seqno);
int
proto_isakmp_selftest(void);
#endif

121
src/proto-mc.c Normal file
View File

@ -0,0 +1,121 @@
#include "proto-mc.h"
#include "proto-banner1.h"
#include "unusedparm.h"
#include "masscan-app.h"
#include "stack-tcp-api.h"
#include "output.h"
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
static unsigned char hand_shake_ptr[128];
static unsigned char *
hand_shake(uint16_t port, const char* ip, size_t ip_len)
{
size_t tlen = 10+ip_len;
unsigned char * ret = (unsigned char *)calloc(1,tlen);
ret[0] = (unsigned char)(7+ip_len);
ret[2] = 0xf7;
ret[3] = 5;
ret[4] = (unsigned char)ip_len;
memcpy(ret+5,ip,ip_len);
ret[tlen-5] = (unsigned char)(port>>8);
ret[tlen-4] = (unsigned char)(port&0xff);
ret[tlen-3] = 1;
ret[tlen-2] = 1;
ret[tlen-1] = 0;
return ret;
}
static void *
memstr(void * mem, size_t len, char * str)
{
size_t i;
size_t stlen = strlen(str);
if(len < stlen)
return 0;
for(i = 0; i < len-stlen; i++) {
if(!memcmp((char*)mem+i,str,stlen))
return (char*)mem+i;
}
return 0;
}
/***************************************************************************
***************************************************************************/
static void
mc_parse( const struct Banner1 *banner1,
void *banner1_private,
struct StreamState *pstate,
const unsigned char *px, size_t length,
struct BannerOutput *banout,
struct stack_handle_t *socket)
{
size_t i;
struct MCSTUFF *mc = &pstate->sub.mc;
UNUSEDPARM(banner1_private);
UNUSEDPARM(banner1);
for(i = 0; i < length; i++) {
if(px[i] == '{')
mc->brackcount++;
if(px[i] == '}')
mc->brackcount--;
}
if(mc->brackcount <= 0)
tcpapi_close(socket);
if((mc->imgstart&&mc->imgend) || mc->brackcount <= 0) { // we already found and removed image data
banout_append(banout, PROTO_MC,px,length);
} else {
mc->banmem = realloc(mc->banmem,mc->totalLen+length+1); // expand to add new memory for added paket
memcpy(mc->banmem+mc->totalLen,px,length); // copy in new packet
mc->banmem[mc->totalLen] = 0; // add ending 0 for str
mc->totalLen+=length;
if(!mc->imgstart) { // dont search again if we found start
mc->imgstart = (size_t)memstr(mc->banmem,mc->totalLen,"data:image/png;base64");
if(mc->imgstart)
mc->imgstart-=(size_t)mc->banmem;
} else { // we found start but not the end
mc->imgend = (size_t)memchr(mc->banmem+mc->imgstart,'\"',mc->totalLen-mc->imgstart);
if(mc->imgend){ // we found the end
mc->imgend-=(size_t)mc->banmem;
memcpy(mc->banmem+mc->imgstart,mc->banmem+mc->imgend,(mc->totalLen-mc->imgend)+1); // copy data after B64
mc->totalLen=mc->imgstart+(mc->totalLen-mc->imgend); // shrink length to subtract B64 image
banout_append(banout, PROTO_MC,mc->banmem,mc->totalLen); // print out banner minus image data
free(mc->banmem); // we dont need to keep track of this any more.
}
}
}
}
/***************************************************************************
***************************************************************************/
static void *
mc_init(struct Banner1 *banner1)
{
unsigned char * tmp = hand_shake(25565,"localhost",9);
memcpy(hand_shake_ptr,tmp,tmp[0]+3);
free(tmp);
banner_mc.hello = hand_shake_ptr;
banner_mc.hello_length = hand_shake_ptr[0]+3;
banner1->payloads.tcp[25565] = (void*)&banner_mc;
return 0;
}
/***************************************************************************
***************************************************************************/
static int
mc_selftest(void)
{
return 0;
}
/***************************************************************************
***************************************************************************/
struct ProtocolParserStream banner_mc = {
"mc", 25565, 0, 0, 0,
mc_selftest,
mc_init,
mc_parse,
};

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