#include <string.h>
#include <unistd.h>
#include <linux/netfilter.h> /* for NF_ACCEPT */
-
#include <libnetfilter_queue/libnetfilter_queue.h>
-// Caching of verdicts
+/**
+ * This file implements the overall program logic by setting up a
+ * netfilter queue callback for analysing packets and make an ACCEPT
+ * or REJECT verdict for them.
+ */
+
+// API for 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
+// API for BAD domains database
unsigned int check_domain(unsigned char *domain);
void load_domains(char *file);
void start_domain_database_loading(void);
return ( ph )? ntohl( ph->packet_id ) : 0;
}
+/**
+ * Packet header if ipv4 packet.
+ */
struct ipv4_pkt {
struct ip first; // .ip_dst[4 bytes]
struct tcphdr second;
};
+/**
+ * Packet header if ipv6 packet.
+ */
struct ipv6_pkt {
struct ip6_hdr first; // .ip6_dst[16 bytes]
struct tcphdr second;
};
-// Payload packet
+/**
+ * Convenience type for network packets of undetermined family.
+ */
struct packet {
union {
struct ipv4_pkt pkt4;
struct ipv6_pkt pkt6;
} p;
- //unsigned char pad[12]; // ??
};
+/**
+ * Locates the header of a packet byte blob.
+ */
static struct packet *get_headerP(unsigned char *data) {
return (struct packet *) data;
}
-///////// Debugging
-//const char *inet_ntop(int af, const void *restrict src,
-// char dst[restrict .size], socklen_t size);
-
static const char *tell_ip(struct packet *ip) {
static char THEIP[200];
switch ( ip->p.pkt4.first.ip_v ) {
int length = nfq_get_payload( nfa, &data);
int verdict = NF_ACCEPT;
struct packet *header = get_headerP( data );
-#if 0
- fprintf( stderr, "PKT %s %d\n", tell_ip( header ), length );
-#endif
if ( length >= 100 ) {
unsigned char *host = http_host( data, length );
-#if 1
- if ( host ) {
- fprintf( stderr, "HTTP HOST %s %s\n", tell_ip( header ), host );
- }
-#endif
if ( host == 0 ) {
host = ssl_host( data, length );
-#if 1
- if ( host ) {
- fprintf( stderr, "SSL HOST %s %s\n", tell_ip( header ), 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( header ), hash_code( host ), host, ix );
-#endif
if ( ix > 0 ) {
+ // Notify "new" domain blocking
+ fprintf( stderr, "%d: block %s at %s by %d\n",
+ hash_code( host ), host, tell_ip( header ), ix );
verdict = NF_DROP;
}
} else if ( i > 0 ) {
}
/**
- * Program main function.
+ * Program main function. Load block lists, register netfilter
+ * calllback and go for it.
*/
int main(int argc, char **argv) {
// Load the database
start_domain_database_loading();
int n = 1;
for ( ; n < argc; n++ ) {
- fprintf( stderr, "Loading block list %s\n", argv[ n ] );
+ fprintf( stderr, "blockdomains loads block list %s\n", argv[ n ] );
load_domains( argv[ n ] );
}
end_domain_database_loading();
int rv;
char buf[4096] __attribute__ ((aligned));
- fprintf( stderr, "opening library handle\n");
+ fprintf( stderr, "blockdomains opens library handle\n");
h = nfq_open();
if ( !h ) {
- fprintf(stderr, "error during nfq_open()\n");
+ fprintf(stderr, "blockdomains error during nfq_open()\n");
exit(1);
}
- fprintf( stderr, "unbinding any existing nf_queue handler\n" );
+ fprintf( stderr, "blockdomains unbinds 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" );
+ fprintf( stderr, "blockdomains binds 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 );
+ fprintf( stderr, "blockdomains registers to queue '%d'\n", THEQUEUE );
qh = nfq_create_queue( h, THEQUEUE, &cb, NULL );
if ( !qh ) {
- fprintf(stderr, "error during nfq_create_queue()\n");
+ fprintf(stderr, "blockdomains error during nfq_create_queue()\n");
exit(1);
}
- fprintf( stderr, "setting copy_packet mode\n" );
+ fprintf( stderr, "blockdomains setting copy_packet mode\n" );
if ( nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff ) < 0) {
- fprintf(stderr, "can't set packet_copy mode\n");
+ fprintf(stderr, "blockdomains can't set packet_copy mode\n");
exit(1);
}
nfq_handle_packet(h, buf, rv);
}
- fprintf( stderr, "unbinding from queue %d\n", THEQUEUE);
+ fprintf( stderr, "blockdomains 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");
+ fprintf( stderr, "blockdomains unbinding from AF_INET\n");
nfq_unbind_pf(h, AF_INET);
#endif
- fprintf( stderr, "closing library handle\n");
+ fprintf( stderr, "blockdomains closing library handle\n");
nfq_close( h );
exit( 0 );
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
-
+/**
+ * Add an Entry for a given domain string and length.
+ */
static void fast_add_domain(unsigned char *domain,int length) {
int fill = database.fill;
if ( fill >= database.size ) {
database.fill++;
}
+/**
+ * Return a marker of the sort order of two given Entry records, with
+ * a<b marked by a negative integer, a=b by zero, and a>b by
+ * (non-zero) positive integer.
+ */
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,
}
/**
- * External call to check a given domain.
+ * External call to check whether a given domain is in the database,
+ * returning 0 if not and non-zero if the domain is present.
*/
unsigned int check_domain(unsigned char *domain) {
int i = index_domain( domain, wordlen( domain ), 1 );
return ( i < 0 )? 0 : ( i + 1 );
}
+/**
+ * This function doesn't do anything at all but is present for the
+ * sake of the ABI.
+ */
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
-
+/**
+ * This function sorts the database to support the lookup by binary
+ * search.
+ */
void end_domain_database_loading(void) {
qsort( database.table, database.fill, sizeof( Entry ),
(__compar_fn_t) table_order );
- //dump_table();
}
/**
}
//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 );
+ while( p < end ) { // Consume a line
+ if ( *p <= ' ' ) {
+ p++;
+ continue;
}
-#endif
if ( *p == '.' ) {
unsigned char *domain = ++p;
- while ( *p > ' ' ) {
+ while ( p < end && *p > ' ' ) {
p++;
}
fast_add_domain( domain, p - domain );