--- /dev/null
+# 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
--- /dev/null
+<h1>Blacklist based domain name filtering</h1>
+
+<p>The <code>nfblocker</code> utility is a blacklist based network traffic filter
+for <code>iptables</code> via <code>libnetfilter-queue</code>. It applies to HTTP and SSL
+traffic for recognizing and dropping packets that are directed to
+blacklisted domain names.</p>
+
+<h2>Dendencies</h2>
+
+<p>Operationally <code>nfblocker</code> depends on the <code>libnetfilter-queue-dev</code> and
+<code>iptables</code> packages, and for building, you'll also need a C build
+environment including <code>make</code>.</p>
+
+<p>The blacklist format is that of squidblacklist.org, which you'll need
+to acquire separately.</p>
+
+<h2>Build and Install</h2>
+
+<p><code>nfblocker</code> 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
+<code>cd</code> into that directory and type:</p>
+
+<blockquote>
+ <p><code># make</code></p>
+</blockquote>
+
+<p>This will build the binary filter, and install the control script as
+<code>/usr/local/sbin/nfblocker.sh</code>. Edit the Makefile to install
+elsewhere.</p>
+
+<h2>Setup and Confguration</h2>
+
+<p>The residence has a directory <code>acl</code> that is intended to hold all
+available access control lists, and a directory <code>blocked</code> that should
+be set up with links to the access control list files to use. For
+example:</p>
+
+<blockquote>
+ <p><code># ( cd blocked && ln -s ../acl/youtube-google-videos.acl )</code></p>
+</blockquote>
+
+<p>That command will set up <code>youtube-google-videos.acl</code> to be an included
+blacklist. Do the opposite to remove; for example:</p>
+
+<blockquote>
+ <p><code># rm blocked/youtube-google-videos.acl</code></p>
+</blockquote>
+
+<h2>Running</h2>
+
+<p>The <code>nfblocker</code> is started with the following command:</p>
+
+<blockquote>
+ <p><code># nfblocker.sh start</code></p>
+</blockquote>
+
+<p>With the <code>start</code> argument, the script adds appropriate <code>iptables</code>
+rules to use direct certain traffic to net-filter queue 99, and it
+starts a background process fot that filtering.</p>
+
+<blockquote>
+ <p><code># nfblocker.sh reload</code></p>
+</blockquote>
+
+<p>With the <code>reload</code> argument, the control script stops and restarts the
+filter without changing <code>iptables</code> rules.</p>
+
+<blockquote>
+ <p><code># nfblocker.sh stop</code></p>
+</blockquote>
+
+<p>With the <code>stop</code> argument, the control script removes the <code>iptables</code>
+rules and terminates the filtering process.</p>
+
+<h2>Technical Detail</h2>
+
+<p>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.</p>
+
+<p>The filtering also uses a fixed size decision cache, so that
+subsequent decisions for the same target can be made quickly.</p>
--- /dev/null
+# 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.
--- /dev/null
+#!/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
--- /dev/null
+../acl/squid-ads.acl
\ No newline at end of file
--- /dev/null
+../acl/squid-dating.acl
\ No newline at end of file
--- /dev/null
+../acl/squid-gambling.acl
\ No newline at end of file
--- /dev/null
+../acl/squid-malicious.acl
\ No newline at end of file
--- /dev/null
+../acl/squid-porn.acl
\ No newline at end of file
--- /dev/null
+../acl/squid-video.acl
\ No newline at end of file
--- /dev/null
+../acl/youtube-google-videos.acl
\ No newline at end of file
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+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;
+}
--- /dev/null
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/**
+ * 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 );
+}
--- /dev/null
+#include <linux/types.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/netfilter.h> /* for NF_ACCEPT */
+
+#include <libnetfilter_queue/libnetfilter_queue.h>
+
+// 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 );
+}
--- /dev/null
+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
+};