From: Ralph Ronnquist Date: Tue, 7 Sep 2021 13:24:09 +0000 (+1000) Subject: added X-Git-Url: https://git.rrq.au/?a=commitdiff_plain;h=5df54493e9eeb0f934f7f606cb1997cb546ca739;p=rrq%2Fnfblocker.git added --- 5df54493e9eeb0f934f7f606cb1997cb546ca739 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..534f9f1 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +# Distribution Makefile for nfbuilder +# + +ifeq ($(shell dpkg -l libnetfilter-queue-dev),) +$(info "nfblocker requires the libnetfilter-queue-dev package") +$(info "please install it before making") +$(error "TERMINATED") +endif + +INSTALLDIR=/usr/local/sbin +USERCOMMAND = nfblocker.sh + +.PHONY: install clean + +default: install + +bin/nfblocker: $(wildcard src/*.c) + gcc -g -Wall -o $@ $^ -lnetfilter_queue + +# Installing the control script in $(INSTALLDIR) + +$(INSTALLDIR)/$(USERCOMMAND): bin/nfblocker.sh $(INSTALLDIR) + ln -sTf $$(readlink -f $<) $@ + +install: bin/nfblocker $(INSTALLDIR)/$(USERCOMMAND) + +clean: + rm -f bin/nfblocker diff --git a/README.html b/README.html new file mode 100644 index 0000000..fb7af87 --- /dev/null +++ b/README.html @@ -0,0 +1,84 @@ +

Blacklist based domain name filtering

+ +

The nfblocker utility is a blacklist based network traffic filter +for iptables via libnetfilter-queue. It applies to HTTP and SSL +traffic for recognizing and dropping packets that are directed to +blacklisted domain names.

+ +

Dendencies

+ +

Operationally nfblocker depends on the libnetfilter-queue-dev and +iptables packages, and for building, you'll also need a C build +environment including make.

+ +

The blacklist format is that of squidblacklist.org, which you'll need +to acquire separately.

+ +

Build and Install

+ +

nfblocker is distributed in a tar file, which should be unpacked at +its future residence; e.g., as /usr/local/src/nfblocker-1.0.0. Then +cd into that directory and type:

+ +
+

# make

+
+ +

This will build the binary filter, and install the control script as +/usr/local/sbin/nfblocker.sh. Edit the Makefile to install +elsewhere.

+ +

Setup and Confguration

+ +

The residence has a directory acl that is intended to hold all +available access control lists, and a directory blocked that should +be set up with links to the access control list files to use. For +example:

+ +
+

# ( cd blocked && ln -s ../acl/youtube-google-videos.acl )

+
+ +

That command will set up youtube-google-videos.acl to be an included +blacklist. Do the opposite to remove; for example:

+ +
+

# rm blocked/youtube-google-videos.acl

+
+ +

Running

+ +

The nfblocker is started with the following command:

+ +
+

# nfblocker.sh start

+
+ +

With the start argument, the script adds appropriate iptables +rules to use direct certain traffic to net-filter queue 99, and it +starts a background process fot that filtering.

+ +
+

# nfblocker.sh reload

+
+ +

With the reload argument, the control script stops and restarts the +filter without changing iptables rules.

+ +
+

# nfblocker.sh stop

+
+ +

With the stop argument, the control script removes the iptables +rules and terminates the filtering process.

+ +

Technical Detail

+ +

The filtering uses the given lists of domain names for rejecting +packets. It recognizes HTTP message headers and SSL certificate +requests, from where it picks out the targeted domain name. If that +name is blacklised or in a blacklisted domain, then the packet is +rejected.

+ +

The filtering also uses a fixed size decision cache, so that +subsequent decisions for the same target can be made quickly.

diff --git a/README.md b/README.md new file mode 100644 index 0000000..f950a79 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# Blacklist based domain name filtering + +The `nfblocker` utility is a blacklist based network traffic filter +for `iptables` via `libnetfilter-queue`. It applies to HTTP and SSL +traffic for recognizing and dropping packets that are directed to +blacklisted domain names. + +## Dendencies + +Operationally `nfblocker` depends on the `libnetfilter-queue-dev` and +`iptables` packages, and for building, you'll also need a C build +environment including `make`. + +The blacklist format is that of squidblacklist.org, which you'll need +to acquire separately. + +## Build and Install + +`nfblocker` is distributed in a tar file, which should be unpacked at +its future residence; e.g., as /usr/local/src/nfblocker-1.0.0. Then +`cd` into that directory and type: + +> `# make` + +This will build the binary filter, and install the control script as +`/usr/local/sbin/nfblocker.sh`. Edit the Makefile to install +elsewhere. + +## Setup and Confguration + +The residence has a directory `acl` that is intended to hold all +available access control lists, and a directory `blocked` that should +be set up with links to the access control list files to use. For +example: + +> `# ( cd blocked && ln -s ../acl/youtube-google-videos.acl )` + +That command will set up `youtube-google-videos.acl` to be an included +blacklist. Do the opposite to remove; for example: + +> `# rm blocked/youtube-google-videos.acl` + +## Running + +The `nfblocker` is started with the following command: + +> `# nfblocker.sh start` + +With the `start` argument, the script adds appropriate `iptables` +rules to use direct certain traffic to net-filter queue 99, and it +starts a background process fot that filtering. + +> `# nfblocker.sh reload` + +With the `reload` argument, the control script stops and restarts the +filter without changing `iptables` rules. + +> `# nfblocker.sh stop` + +With the `stop` argument, the control script removes the `iptables` +rules and terminates the filtering process. + +## Technical Detail + +The filtering uses the given lists of domain names for rejecting +packets. It recognizes HTTP message headers and SSL certificate +requests, from where it picks out the targeted domain name. If that +name is blacklised or in a blacklisted domain, then the packet is +rejected. + +The filtering also uses a fixed size decision cache, so that +subsequent decisions for the same target can be made quickly. diff --git a/bin/nfblocker b/bin/nfblocker new file mode 100755 index 0000000..a9a9c8a Binary files /dev/null and b/bin/nfblocker differ diff --git a/bin/nfblocker.sh b/bin/nfblocker.sh new file mode 100755 index 0000000..661d8d0 --- /dev/null +++ b/bin/nfblocker.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# +# This is a control script for nfblocker. +# + +if [ -z "$1" ] ; then + echo "start or stop?" + exit 1 +fi + +cd $(dirname $(readlink $0)) + +function start_nfblocker() { + LOG=/var/log/nfblocker.$(date +%Y%m%d) + BLOCKED=( ../acl/*.acl ) + echo "BLOCKING ${BLOCKED[@]}" >> $LOG + ./nfblocker ../blocked/*.acl >> $LOG 2>&1 & +} + +TABLE=OUTPUT +case $1 in + start) + iptables -I $TABLE -p tcp -j NFQUEUE --queue-num 99 + pkill -x nfblocker + start_nfblocker + ;; + reload) + pkill -x nfblocker + start_nfblocker + ;; + stop) + iptables -D $TABLE -p tcp -j NFQUEUE --queue-num 99 + pkill -x nfblocker + ;; + *) + echo "Use start, stop or reload" >&2 + ;; +esac diff --git a/blocked/squid-ads.acl b/blocked/squid-ads.acl new file mode 120000 index 0000000..905bd71 --- /dev/null +++ b/blocked/squid-ads.acl @@ -0,0 +1 @@ +../acl/squid-ads.acl \ No newline at end of file diff --git a/blocked/squid-dating.acl b/blocked/squid-dating.acl new file mode 120000 index 0000000..6a7fd51 --- /dev/null +++ b/blocked/squid-dating.acl @@ -0,0 +1 @@ +../acl/squid-dating.acl \ No newline at end of file diff --git a/blocked/squid-gambling.acl b/blocked/squid-gambling.acl new file mode 120000 index 0000000..fd170a7 --- /dev/null +++ b/blocked/squid-gambling.acl @@ -0,0 +1 @@ +../acl/squid-gambling.acl \ No newline at end of file diff --git a/blocked/squid-malicious.acl b/blocked/squid-malicious.acl new file mode 120000 index 0000000..b0b7f81 --- /dev/null +++ b/blocked/squid-malicious.acl @@ -0,0 +1 @@ +../acl/squid-malicious.acl \ No newline at end of file diff --git a/blocked/squid-porn.acl b/blocked/squid-porn.acl new file mode 120000 index 0000000..0710db6 --- /dev/null +++ b/blocked/squid-porn.acl @@ -0,0 +1 @@ +../acl/squid-porn.acl \ No newline at end of file diff --git a/blocked/squid-video.acl b/blocked/squid-video.acl new file mode 120000 index 0000000..d0b480f --- /dev/null +++ b/blocked/squid-video.acl @@ -0,0 +1 @@ +../acl/squid-video.acl \ No newline at end of file diff --git a/blocked/youtube-google-videos.acl b/blocked/youtube-google-videos.acl new file mode 120000 index 0000000..06f57c8 --- /dev/null +++ b/blocked/youtube-google-videos.acl @@ -0,0 +1 @@ +../acl/youtube-google-videos.acl \ No newline at end of file diff --git a/src/cache.c b/src/cache.c new file mode 100644 index 0000000..2afb37a --- /dev/null +++ b/src/cache.c @@ -0,0 +1,45 @@ +#include +#include +#include + +typedef struct _CacheEntry { + unsigned char *domain; + unsigned int ix; +} CacheEntry; + +struct { + CacheEntry *table; + int size; +} cache; + +int hash_code(unsigned char *domain) { + int i = 0; + for ( ; *domain; domain++ ) { + i += *domain; + } + return i % cache.size; +} + +int lookup_cache(unsigned char *domain) { + if ( cache.table ) { + int i = hash_code( domain ); + if ( cache.table[i].domain && + strcmp( (char*) domain, (char*) cache.table[i].domain ) == 0 ) { + return cache.table[i].ix; + } + } + return -1; +} + +void add_cache(unsigned char *domain,unsigned int ix) { + if ( cache.table == 0 ) { + cache.size = 1024; + cache.table = (CacheEntry*) calloc( cache.size, sizeof( CacheEntry ) ); + } + int i = hash_code( domain ); + if ( cache.table[i].domain ) { + free( cache.table[i].domain ); + } + cache.table[i].domain = (unsigned char*) strdup( (char*) domain ); + cache.table[i].ix = ix; +} diff --git a/src/database.c b/src/database.c new file mode 100644 index 0000000..02bb8dc --- /dev/null +++ b/src/database.c @@ -0,0 +1,280 @@ +#include +#include +#include +#include +#include +#include +#include + +/** + * This file implements a "database" of "bad" domains, loaded from + * ".acl" files of a fairly strict format; each domain to block is + * written on a line starting with a period, immediately followed by + * the domain to block, then an optional comment. + * + * The database is populated by using the call sequence: + * 1. start_domain_database_loading(); + * 2. load_domains( filename ); // repeated + * N. end_domain_database_loading(); + * + * The final call triggers a reordering of domains so as to support + * binary search in reverse text order, for matching domain suffixes. + * See the function `tail_compare` for details. + */ + +/** + * This is the Entry type for the "database", which basically is an + * array of these. The domain pointer will point at a domain name in + * the loaded ".acl" file, and length is the domain name length. + */ +typedef struct _Entry { + int length; + unsigned char *domain; +} Entry; + +/** + * This is the domain name database root structure. It holds a pointer + * to the array of Entry records, the fill of that array, and the + * allocated size for that array (no lesser than the fill, of course). + */ +static struct { + Entry *table; + int fill; + int size; +} database = { 0, 0, 0 }; + +/** + * This function compares strings backwars; the last k bytes of string + * (a,na) versus string (b,nb). It also holds '.' as the least of + * characters, so as to ensure that refined/extended domain names are + * comparatively greater that their base domain names. + */ +static int tail_compare(unsigned char *a,unsigned char *b,int k) { + while ( k-- > 0 ) { + int c = *(--a) - *(--b); + if ( c != 0) { + if ( *a == '.' ) { + return -1; + } + if ( *b == '.' ) { + return 1; + } + return c; + } + } + return 0; +} + +/** + * Extend the domain name table to allow additions. + */ +#define STARTSIZE 100000 +static void grow() { + if ( database.table ) { + Entry *old = database.table; + int s = database.size; + database.size += 100000; + database.table = (Entry*) calloc( database.size, sizeof( Entry ) ); + memcpy( database.table, old, s * sizeof( Entry ) ); + free( old ); + } else { + database.table = (Entry*) calloc( STARTSIZE, sizeof( Entry ) ); + database.size = STARTSIZE; + } +} + +/** + * Determine the index for given domain. This matches computes a tail + * match between the given domain and the databse domains, returning + * the index for the matching database entry, or (-index-1) to + * indicate insertion point. In lookup mode, a database entry being a + * tail domain part of the given domain is also considered a match. + */ +static int index_domain(unsigned char *domain,int n,int lookup) { + int lo = 0; + int hi = database.fill; + while ( lo < hi ) { + int m = ( lo + hi ) / 2; + Entry *p = &database.table[ m ]; + int k = p->length; + if ( n < k ) { + k = n; + } + int q = tail_compare( p->domain + p->length, domain + n, k ); +#if 0 + fprintf( stderr, "%s %d %d %d\n", domain, k, m, q ); +#endif + if ( q == 0 ) { + if ( p->length < n ) { + // table entry shorter => new entry after, or match on lookup + if ( lookup && *(domain+n-k-1) == '.' ) { + return m; + } + lo = m + 1; + } else if ( p->length > n ) { + // table entry longer => new entry before + hi = m; + } else { + // equal + return m; + } + } else if ( q < 0 ) { + // new entry after + lo = m + 1; + } else { + // new entry before + hi = m; + } + } + return -lo - 1; +} + +/** + * Determine the length of a "word" + */ +static int wordlen(unsigned char *p) { + unsigned char *q = p; + while ( *q > ' ' ) { + q++; + } + return q - p; +} + +#if 0 +static void add_domain(char *domain) { + if ( database.fill >= database.size ) { + grow(); + } + int length = wordlen( domain ); + int i = index_domain( domain, length, 0 ); + if ( i < 0 ) { + i = -i-1; + int tail = database.fill - i; + if ( tail ) { + memmove( &database.table[ i+1 ], + &database.table[i], + tail * sizeof( Entry ) ); + } + database.table[ i ].domain = domain; + database.table[ i ].length = length; + database.fill++; + } else { + char *p1 = strndup( domain, length ); + char *p2 = strndup( database.table[i].domain, + database.table[i].length ); + fprintf( stderr, "fill = %d %d %s == %s\n", + i, database.fill, p1, p2 ); + free( p1 ); + free( p2 ); + } +} +#endif + +static void fast_add_domain(unsigned char *domain,int length) { + int fill = database.fill; + if ( fill >= database.size ) { + grow(); + } + database.table[ fill ].length = length; + database.table[ fill ].domain = domain; + database.fill++; +} + +static int table_order(Entry *a,Entry *b) { + int k = ( a->length < b->length )? a->length : b->length; + int c = tail_compare( a->domain + a->length, + b->domain + b->length, k ); + if ( c != 0 ) { + return c; + } + return a->length - b->length; +} + +/** + * External call to check a given domain. + */ +unsigned int check_domain(unsigned char *domain) { + int i = index_domain( domain, wordlen( domain ), 1 ); + return ( i < 0 )? 0 : ( i + 1 ); +} + +void start_domain_database_loading(void) { +} + +#if 0 +static void dump_table() { + fprintf( stderr, "Table fill=%d size=%d\n", database.fill, database.size ); + int i = 0; + for ( ; i < database.fill; i++ ) { + char *p = strndup( database.table[i].domain, + database.table[i].length ); + fprintf( stderr, "[%d] %d %p %s\n", + i, database.table[i].length, database.table[i].domain, p ); + free( p ); + } +} +#endif + +void end_domain_database_loading(void) { + qsort( database.table, database.fill, sizeof( Entry ), + (__compar_fn_t) table_order ); + //dump_table(); +} + +/** + * Load BAD domain names from file. The file is line based where data + * lines consist of domain name starting with period and ending with + * space or newline, and other lines ignored. + */ +void load_domains(char *file) { + struct stat info; + unsigned char *data; + //fprintf( stderr, "state(\"%s\",&info)\n", file ); + if ( stat( file, &info ) ) { + perror( file ); + exit( 1 ); + } + int n = info.st_size; + data = (unsigned char *) malloc( n ); + //fprintf( stderr, "open(\"%s\",)\n", file ); + int fd = open( file, O_RDONLY ); + if ( fd < 0 ) { + perror( file ); + exit( 1 ); + } + //fprintf( stderr, "Loading %s\n", file ); + unsigned char *end = data; + while ( n > 0 ) { + int k = read( fd, end, n ); + if ( k == 0 ) { + fprintf( stderr, "Premature EOF for %s\n", file ); + exit( 1 ); + } + end += k; + n -= k; + } + //fprintf( stderr, "processing %s %p %p\n", file, data, end ); + unsigned char *p = data; +#if 0 + int count = 0; +#endif + while( p < end ) { +#if 0 + if ( ( ++count % 10000 ) == 0 ) { + fprintf( stderr, "%d rules\n", count ); + } +#endif + if ( *p == '.' ) { + unsigned char *domain = ++p; + while ( *p > ' ' ) { + p++; + } + fast_add_domain( domain, p - domain ); + } + while ( p < end && *p != '\n' ) { + p++; + } + p++; + } + close( fd ); +} diff --git a/src/nfblocker.c b/src/nfblocker.c new file mode 100644 index 0000000..b8b979c --- /dev/null +++ b/src/nfblocker.c @@ -0,0 +1,286 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for NF_ACCEPT */ + +#include + +// Caching of verdicts +unsigned int lookup_cache(unsigned char *domain); +void add_cache(unsigned char *domain,unsigned int ix); +int hash_code(unsigned char *domain); + +// BAD domains database +unsigned int check_domain(unsigned char *domain); +void load_domains(char *file); +void start_domain_database_loading(void); +void end_domain_database_loading(void); + +/** + * Return packet id, or 0 on error. + */ +static u_int32_t get_packet_id(struct nfq_data *tb) { + struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr( tb ); + return ( ph )? ntohl( ph->packet_id ) : 0; +} + +// Payload headers +struct headers { + struct ip first; + struct tcphdr second; + //unsigned char pad[12]; // ?? +}; + +///////// Debugging + +static unsigned char *tell_ip(u_int32_t ip) { + static unsigned char THEIP[20]; + unsigned char *b = (unsigned char *)&ip; + sprintf( (char*) THEIP, "%d.%d.%d.%d%c", b[0], b[1], b[2], b[3], 0 ); + return THEIP; +} + +static u_int32_t get_dest_ip4(unsigned char *data) { + struct headers *p = (struct headers *) data; + return p->first.ip_dst.s_addr; +} + +/** + * Review payload packet payload + */ +static void view_payload(unsigned char *data,int length) { + u_int32_t ip4 = get_dest_ip4( data ); + u_int16_t port = ntohs( ((struct headers *) data )->second.th_dport ); + u_int8_t syn = sizeof( struct headers ); + unsigned char *body = data ;//+ sizeof( struct headers ); +#define END 400 + unsigned char * end = body + ( ( length > END )? END : length ); + fprintf( stderr, "%s %d %d %d ", tell_ip( ip4 ), syn, port, length ); + while ( body < end ) { + unsigned char c = *body++; + if ( c < ' ' || c >= 127 || 1 ) { + fprintf( stderr, "%02x ", c ); + } else { + fprintf( stderr, "%c", c ); + } + } + fprintf( stderr, "\n" ); +} + +////////////////// +static unsigned char buffer[1000]; + +/** + * SSL traffic includes a data packet with a clear text host name. + * This is knwon as the SNI extension. + */ +static unsigned char *ssl_host(unsigned char *data,int length) { + // Check that it's a "Client Hello" message + unsigned char *p = data + sizeof( struct headers ) + 12; + if ( p[0] != 0x16 || p[1] != 0x03 || p[5] != 0x01 || p[6] != 0x00 ) { + return 0; + } + //fprintf( stderr, "Client Hello\n" ); + // Note minor version p[2] is not checked + // record_length = 256 * p[3] + p[4] + // handshake_message_length = 256 * p[7] + p[8] + if ( p[9] != 0x03 || p[10] != 0x03 ) { // TLS 1.2 (?ralph?) + return 0; + } + //fprintf( stderr, "TLS 1.2\n" ); + unsigned int i = 46 + ( 256 * p[44] ) + p[45]; + i += p[i] + 1; + unsigned int extensions_length = ( 256 * p[i] ) + p[i+1]; + i += 2; + int k = 0; + //fprintf( stderr, "TLS 1.2 %d %d\n", i, extensions_length ); + while ( k < extensions_length ) { + unsigned int type = ( 256 * p[i+k] ) + p[i+k+1]; + k += 2; + unsigned int length = ( 256 * p[i+k] ) + p[i+k+1]; + k += 2; + //fprintf( stderr, "Extension %d %d\n", k-4, type ); + if ( type == 0 ) { // Server Name + if ( p[i+k+2] ) { + break; // Name badness + } + unsigned int name_length = ( 256 * p[i+k+3] ) + p[i+k+4]; + unsigned char *path = &p[i+k+5]; + memcpy( buffer, path, name_length ); + buffer[ name_length ] = '\0'; + return buffer; + } + k += length; + } + // This point is only reached on "missing or bad SNI". + view_payload( data, length ); + return 0; +} + +/** + * HTTP traffic includes a data packet with the host name as a + * "Host:" attribute. + */ +static unsigned char *http_host(unsigned char *data,int length) { + unsigned char *body = data + sizeof( struct headers ); + if ( ( strncmp( (char*) body, "GET ", 4 ) != 0 ) && + ( strncmp( (char*) body, "POST ", 5 ) != 0 ) ) { + return 0; + } + unsigned char *end = data + length - 6; + int check = 0; + for ( ; body < end; body++ ) { + if ( check ) { + if ( strncmp( (char*) body, "Host:", 5 ) == 0 ) { + body += 5; + for( ; body < end; body++ ) if ( *body != ' ' ) break; + unsigned char *start = body; + int n = 0; + for( ; body < end; n++, body++ ) if ( *body <= ' ' ) break; + if ( n < 5 ) { + return 0; + } + memcpy( buffer, start, n ); + buffer[ n ] = '\0'; + return buffer; + } + if ( strncmp( (char*) body, "\r\n", 2 ) == 0 ) { + return 0; + } + for( ; body < end; body++ ) if ( *body == '\n' ) break; + if ( body >= end ) { + return 0; + } + } + check = ( *body == '\n' ); + } + return 0; +} + +/** + * Callback function to handle a packet. + */ +static int cb( + struct nfq_q_handle *qh, + struct nfgenmsg *nfmsg, + struct nfq_data *nfa, void *code ) +{ + u_int32_t id = get_packet_id( nfa ); + unsigned char *data; + int length = nfq_get_payload( nfa, &data); + int verdict = NF_ACCEPT; + u_int32_t ip4 = get_dest_ip4( data ); +#if 0 + fprintf( stderr, "PKT %s %d\n", tell_ip( ip4 ), length ); +#endif + if ( length >= 100 ) { + unsigned char *host = http_host( data, length ); +#if 1 + fprintf( stderr, "HTTP HOST %s %s\n", tell_ip( ip4 ), host ); +#endif + if ( host == 0 ) { + host = ssl_host( data, length ); +#if 1 + fprintf( stderr, "SSL HOST %s %s\n", tell_ip( ip4 ), host ); +#endif + } + if ( host ) { + int i = lookup_cache( host ); + if ( i < 0 ) { + unsigned int ix = check_domain( host ); + add_cache( host, ix ); +#if 1 + fprintf( stderr, "%s %d %s ** %d\n", + tell_ip( ip4 ), hash_code( host ), host, ix ); +#endif + if ( ix > 0 ) { + verdict = NF_DROP; + } + } else if ( i > 0 ) { + verdict = NF_DROP; + } + } + } + return nfq_set_verdict(qh, id, verdict, 0, NULL); +} + +/** + * Program main function. + */ +int main(int argc, char **argv) { + // Load the database + start_domain_database_loading(); + int n = 1; + for ( ; n < argc; n++ ) { + fprintf( stderr, "Loading blacklist %s\n", argv[ n ] ); + load_domains( argv[ n ] ); + } + end_domain_database_loading(); + + struct nfq_handle *h; + struct nfq_q_handle *qh; + //struct nfnl_handle *nh; + int fd; + int rv; + char buf[4096] __attribute__ ((aligned)); + + fprintf( stderr, "opening library handle\n"); + h = nfq_open(); + if ( !h ) { + fprintf(stderr, "error during nfq_open()\n"); + exit(1); + } + + fprintf( stderr, "unbinding any existing nf_queue handler\n" ); + if ( nfq_unbind_pf(h, AF_INET) < 0 ) { + fprintf(stderr, "error during nfq_unbind_pf()\n"); + exit(1); + } + + fprintf( stderr, "binding nfnetlink_queue as nf_queue handler\n" ); + if ( nfq_bind_pf(h, AF_INET) < 0 ) { + fprintf(stderr, "error during nfq_bind_pf()\n"); + exit(1); + } + +#define THEQUEUE 99 + fprintf( stderr, "binding this socket to queue '%d'\n", THEQUEUE ); + qh = nfq_create_queue( h, THEQUEUE, &cb, NULL ); + if ( !qh ) { + fprintf(stderr, "error during nfq_create_queue()\n"); + exit(1); + } + + fprintf( stderr, "setting copy_packet mode\n" ); + if ( nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff ) < 0) { + fprintf(stderr, "can't set packet_copy mode\n"); + exit(1); + } + + fd = nfq_fd( h ); + + while ( ( rv = recv(fd, buf, sizeof(buf), 0) ) && rv >= 0 ) { + //printf( "pkt received\n" ); + nfq_handle_packet(h, buf, rv); + } + + fprintf( stderr, "unbinding from queue %d\n", THEQUEUE); + nfq_destroy_queue(qh); + +#ifdef INSANE + /* normally, applications SHOULD NOT issue this command, since it + detaches other programs/sockets from AF_INET, too ! */ + fprintf( stderr, "unbinding from AF_INET\n"); + nfq_unbind_pf(h, AF_INET); +#endif + + fprintf( stderr, "closing library handle\n"); + nfq_close( h ); + + exit( 0 ); +} diff --git a/src/ssl.txt b/src/ssl.txt new file mode 100644 index 0000000..9dbe988 --- /dev/null +++ b/src/ssl.txt @@ -0,0 +1,42 @@ +const unsigned char good_data_2[] = { + // TLS record + 0x16, // [0] Content Type: Handshake + 0x03, 0x01, // [1,2] Version: TLS 1.0 + 0x00, 0x6c, // [3,4] Length (use for bounds checking) + // Handshake + 0x01, // [5] Handshake Type: Client Hello + 0x00, 0x00, 0x68, // [6,7,8] Length (use for bounds checking) + 0x03, 0x03, // [9,10] Version: TLS 1.2 + // [11,,42] Random (32 bytes fixed length) + 0xb6, 0xb2, 0x6a, 0xfb, 0x55, 0x5e, 0x03, 0xd5, + 0x65, 0xa3, 0x6a, 0xf0, 0x5e, 0xa5, 0x43, 0x02, + 0x93, 0xb9, 0x59, 0xa7, 0x54, 0xc3, 0xdd, 0x78, + 0x57, 0x58, 0x34, 0xc5, 0x82, 0xfd, 0x53, 0xd1, + 0x00, // [43] Session ID Length (skip past this much) + 0x00, 0x04, // [44,45] Cipher Suites Length (skip past this much) + 0x00, 0x01, // NULL-MD5 + 0x00, 0xff, // RENEGOTIATION INFO SCSV + 0x01, // Compression Methods Length (skip past this much) + 0x00, // NULL + 0x00, 0x3b, // Extensions Length (use for bounds checking) + // Extension + 0x00, 0x00, // Extension Type: Server Name (check extension type) + 0x00, 0x0e, // Length (use for bounds checking) + 0x00, 0x0c, // Server Name Indication Length + 0x00, // Server Name Type: host_name (check server name type) + 0x00, 0x09, // Length (length of your data) + // "localhost" (data your after) + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, + // Extension + 0x00, 0x0d, // Extension Type: Signature Algorithms (check extension type) + 0x00, 0x20, // Length (skip past since this is the wrong extension) + // Data + 0x00, 0x1e, 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, + 0x05, 0x01, 0x05, 0x02, 0x05, 0x03, 0x04, 0x01, + 0x04, 0x02, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, + 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x03, + // Extension + 0x00, 0x0f, // Extension Type: Heart Beat (check extension type) + 0x00, 0x01, // Length (skip past since this is the wrong extension) + 0x01 // Mode: Peer allows to send requests +};