initial
This commit is contained in:
commit
8559dcdfaf
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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*/
|
||||
}
|
|
@ -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
|
|
@ -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*/
|
||||
}
|
|
@ -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*/
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef MAIN_READRANGE_H
|
||||
#define MAIN_READRANGE_H
|
||||
struct Masscan;
|
||||
|
||||
void
|
||||
main_readrange(struct Masscan *masscan);
|
||||
|
||||
#endif
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef MASSCAN_VERSION
|
||||
|
||||
#define MASSCAN_VERSION "1.3.9-integration"
|
||||
|
||||
#endif
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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
|
||||
};
|
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
};
|
|
@ -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
|
||||
};
|
|
@ -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
|
||||
};
|
|
@ -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
|
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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,
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef PROTO_FTP_H
|
||||
#define PROTO_FTP_H
|
||||
#include "proto-banner1.h"
|
||||
|
||||
extern const struct ProtocolParserStream banner_ftp;
|
||||
|
||||
#endif
|
|
@ -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,
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
@ -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:
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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,
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef PROTO_IMAP4_H
|
||||
#define PROTO_IMAP4_H
|
||||
#include "proto-banner1.h"
|
||||
|
||||
extern const struct ProtocolParserStream banner_imap4;
|
||||
|
||||
#endif
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
Loading…
Reference in New Issue