history purge

This commit is contained in:
bol-van
2021-03-04 14:30:38 +03:00
commit 3703918a4b
179 changed files with 22082 additions and 0 deletions

12
nfq/BSDmakefile Normal file
View File

@@ -0,0 +1,12 @@
CC ?= cc
CFLAGS += -std=gnu99 -s -O3 -Wno-address-of-packed-member -Wno-logical-op-parentheses -Wno-switch
LIBS = -lz
SRC_FILES = *.c
all: dvtws
dvtws: $(SRC_FILES)
$(CC) $(CFLAGS) -o $@ $(SRC_FILES) $(LDFLAGS) $(LIBS)
clean:
rm -f dvtws *.o

22
nfq/Makefile Normal file
View File

@@ -0,0 +1,22 @@
CC ?= gcc
CFLAGS += -std=gnu99 -O3
CFLAGS_BSD = -Wno-address-of-packed-member -Wno-logical-op-parentheses -Wno-switch
CFLAGS_MAC = -mmacosx-version-min=10.8
STRIP = -s
LIBS = -lnetfilter_queue -lnfnetlink -lz
LIBS_BSD = -lz
SRC_FILES = *.c
all: nfqws
nfqws: $(SRC_FILES)
$(CC) $(STRIP) $(CFLAGS) -o $@ $(SRC_FILES) $(LDFLAGS) $(LIBS)
bsd: $(SRC_FILES)
$(CC) $(STRIP) $(CFLAGS) $(CFLAGS_BSD) -o dvtws $(SRC_FILES) $(LDFLAGS) $(LIBS_BSD)
mac: $(SRC_FILES)
$(CC) $(CFLAGS) $(CFLAGS_BSD) $(CFLAGS_MAC) -o dvtws $(SRC_FILES) $(LDFLAGS) $(LIBS_BSD)
clean:
rm -f nfqws dvtws *.o

138
nfq/checksum.c Normal file
View File

@@ -0,0 +1,138 @@
#define _GNU_SOURCE
#include "checksum.h"
#include <netinet/in.h>
//#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
//#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
static uint16_t from64to16(uint64_t x)
{
uint32_t u = (uint32_t)(uint16_t)x + (uint16_t)(x>>16) + (uint16_t)(x>>32) + (uint16_t)(x>>48);
return (uint16_t)u + (uint16_t)(u>>16);
}
static uint16_t do_csum(const uint8_t * buff, size_t len)
{
uint8_t odd;
size_t count;
uint64_t result,w,carry=0;
uint16_t u16;
if (len <= 0) return 0;
odd = (uint8_t)(1 & (size_t)buff);
if (odd)
{
// any endian compatible
u16 = 0;
*((uint8_t*)&u16+1) = *buff;
result = u16;
len--;
buff++;
}
else
result = 0;
count = len >> 1; /* nr of 16-bit words.. */
if (count)
{
if (2 & (size_t) buff)
{
result += *(uint16_t *) buff;
count--;
len -= 2;
buff += 2;
}
count >>= 1; /* nr of 32-bit words.. */
if (count)
{
if (4 & (size_t) buff)
{
result += *(uint32_t *) buff;
count--;
len -= 4;
buff += 4;
}
count >>= 1; /* nr of 64-bit words.. */
if (count)
{
do
{
w = *(uint64_t *) buff;
count--;
buff += 8;
result += carry;
result += w;
carry = (w > result);
} while (count);
result += carry;
result = (result & 0xffffffff) + (result >> 32);
}
if (len & 4)
{
result += *(uint32_t *) buff;
buff += 4;
}
}
if (len & 2)
{
result += *(uint16_t *) buff;
buff += 2;
}
}
if (len & 1)
{
// any endian compatible
u16 = 0;
*(uint8_t*)&u16 = *buff;
result += u16;
}
u16 = from64to16(result);
if (odd) u16 = ((u16 >> 8) & 0xff) | ((u16 & 0xff) << 8);
return u16;
}
uint16_t csum_partial(const void *buff, size_t len)
{
return do_csum(buff,len);
}
uint16_t csum_tcpudp_magic(uint32_t saddr, uint32_t daddr, size_t len, uint8_t proto, uint16_t sum)
{
return ~from64to16((uint64_t)saddr + daddr + sum + htonl(len+proto));
}
uint16_t ip4_compute_csum(const void *buff, size_t len)
{
return ~from64to16(do_csum(buff,len));
}
void ip4_fix_checksum(struct ip *ip)
{
ip->ip_sum = 0;
ip->ip_sum = ip4_compute_csum(ip, ip->ip_hl<<2);
}
uint16_t csum_ipv6_magic(const void *saddr, const void *daddr, size_t len, uint8_t proto, uint16_t sum)
{
uint64_t a = (uint64_t)sum + htonl(len+proto) +
*(uint32_t*)saddr + *((uint32_t*)saddr+1) + *((uint32_t*)saddr+2) + *((uint32_t*)saddr+3) +
*(uint32_t*)daddr + *((uint32_t*)daddr+1) + *((uint32_t*)daddr+2) + *((uint32_t*)daddr+3);
return ~from64to16(a);
}
void tcp4_fix_checksum(struct tcphdr *tcp,size_t len, const struct in_addr *src_addr, const struct in_addr *dest_addr)
{
tcp->th_sum = 0;
tcp->th_sum = csum_tcpudp_magic(src_addr->s_addr,dest_addr->s_addr,len,IPPROTO_TCP,csum_partial(tcp, len));
}
void tcp6_fix_checksum(struct tcphdr *tcp,size_t len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr)
{
tcp->th_sum = 0;
tcp->th_sum = csum_ipv6_magic(src_addr,dest_addr,len,IPPROTO_TCP,csum_partial(tcp, len));
}
void tcp_fix_checksum(struct tcphdr *tcp,size_t len,const struct ip *ip,const struct ip6_hdr *ip6hdr)
{
if (ip)
tcp4_fix_checksum(tcp, len, &ip->ip_src, &ip->ip_dst);
else if (ip6hdr)
tcp6_fix_checksum(tcp, len, &ip6hdr->ip6_src, &ip6hdr->ip6_dst);
}

19
nfq/checksum.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
uint16_t csum_partial(const void *buff, size_t len);
uint16_t csum_tcpudp_magic(uint32_t saddr, uint32_t daddr, size_t len, uint8_t proto, uint16_t sum);
uint16_t csum_ipv6_magic(const void *saddr, const void *daddr, size_t len, uint8_t proto, uint16_t sum);
uint16_t ip4_compute_csum(const void *buff, size_t len);
void ip4_fix_checksum(struct ip *ip);
void tcp4_fix_checksum(struct tcphdr *tcp,size_t len, const struct in_addr *src_addr, const struct in_addr *dest_addr);
void tcp6_fix_checksum(struct tcphdr *tcp,size_t len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr);
void tcp_fix_checksum(struct tcphdr *tcp,size_t len,const struct ip *ip,const struct ip6_hdr *ip6hdr);

650
nfq/darkmagic.c Normal file
View File

@@ -0,0 +1,650 @@
#define _GNU_SOURCE
#include "darkmagic.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/param.h>
#include <errno.h>
#include "helpers.h"
uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment)
{
return htonl(ntohl(netorder_value)+cpuorder_increment);
}
uint8_t *tcp_find_option(struct tcphdr *tcp, uint8_t kind)
{
uint8_t *t = (uint8_t*)(tcp+1);
uint8_t *end = (uint8_t*)tcp + (tcp->th_off<<2);
while(t<end)
{
switch(*t)
{
case 0: // end
return NULL;
case 1: // noop
t++;
break;
default: // kind,len,data
if ((t+1)>=end || t[1]<2 || (t+t[1])>end)
return NULL;
if (*t==kind)
return t;
t+=t[1];
break;
}
}
return NULL;
}
uint32_t *tcp_find_timestamps(struct tcphdr *tcp)
{
uint8_t *t = tcp_find_option(tcp,8);
return (t && t[1]==10) ? (uint32_t*)(t+2) : NULL;
}
static void fill_tcphdr(struct tcphdr *tcp, uint8_t tcp_flags, uint32_t seq, uint32_t ack_seq, uint8_t fooling, uint16_t nsport, uint16_t ndport, uint16_t nwsize, uint32_t *timestamps)
{
char *tcpopt = (char*)(tcp+1);
uint8_t t=0;
memset(tcp,0,sizeof(*tcp));
tcp->th_sport = nsport;
tcp->th_dport = ndport;
if (fooling & TCP_FOOL_BADSEQ)
{
tcp->th_seq = net32_add(seq,0x80000000);
tcp->th_ack = net32_add(ack_seq,0x80000000);
}
else
{
tcp->th_seq = seq;
tcp->th_ack = ack_seq;
}
tcp->th_off = 5;
*((uint8_t*)tcp+13)= tcp_flags;
tcp->th_win = nwsize;
if (fooling & TCP_FOOL_MD5SIG)
{
tcpopt[0] = 19; // kind
tcpopt[1] = 18; // len
*(uint32_t*)(tcpopt+2)=random();
*(uint32_t*)(tcpopt+6)=random();
*(uint32_t*)(tcpopt+10)=random();
*(uint32_t*)(tcpopt+14)=random();
t=18;
}
if (timestamps || (fooling & TCP_FOOL_TS))
{
tcpopt[t] = 8; // kind
tcpopt[t+1] = 10; // len
// forge only TSecr if orig timestamp is present
*(uint32_t*)(tcpopt+t+2) = timestamps ? timestamps[0] : -1;
*(uint32_t*)(tcpopt+t+6) = (timestamps && !(fooling & TCP_FOOL_TS)) ? timestamps[1] : -1;
t+=10;
}
while (t&3) tcpopt[t++]=1; // noop
tcp->th_off += t>>2;
}
static uint16_t tcpopt_len(uint8_t fooling, uint32_t *timestamps)
{
uint16_t t=0;
if (fooling & TCP_FOOL_MD5SIG) t=18;
if ((fooling & TCP_FOOL_TS) || timestamps) t+=10;
return (t+3)&~3;
}
static int rawsend_sock4=-1, rawsend_sock6=-1;
static void rawsend_clean_sock(int *sock)
{
if (sock && *sock!=-1)
{
close(*sock);
*sock=-1;
}
}
void rawsend_cleanup()
{
rawsend_clean_sock(&rawsend_sock4);
rawsend_clean_sock(&rawsend_sock6);
}
static int *rawsend_family_sock(sa_family_t family)
{
switch(family)
{
case AF_INET: return &rawsend_sock4;
case AF_INET6: return &rawsend_sock6;
default: return NULL;
}
}
#ifdef BSD
static int rawsend_socket_divert(sa_family_t family)
{
// HACK HACK HACK HACK HACK HACK HACK HACK
// FreeBSD doesnt allow IP_HDRINCL for IPV6
// OpenBSD doesnt allow rawsending tcp frames
// we either have to go to the link layer (its hard, possible problems arise, compat testing, ...) or use some HACKING
// from my point of view disabling direct ability to send ip frames is not security. its SHIT
int fd = socket(family, SOCK_RAW, IPPROTO_DIVERT);
if (!set_socket_buffers(fd,4096,RAW_SNDBUF))
{
close(fd);
return -1;
}
return fd;
}
static int rawsend_sendto_divert(sa_family_t family, int sock, const void *buf, size_t len)
{
struct sockaddr_storage sa;
socklen_t slen;
memset(&sa,0,sizeof(sa));
sa.ss_family = family;
switch(family)
{
case AF_INET:
slen = sizeof(struct sockaddr_in);
break;
case AF_INET6:
slen = sizeof(struct sockaddr_in6);
break;
default:
return -1;
}
return sendto(sock, buf, len, 0, (struct sockaddr*)&sa, slen);
}
#endif
static int rawsend_socket_raw(int domain, int proto)
{
int fd = socket(domain, SOCK_RAW, proto);
if (fd!=-1)
{
#ifdef __linux__
int s=RAW_SNDBUF/2;
int r=2048;
#else
int s=RAW_SNDBUF;
int r=4096;
#endif
if (!set_socket_buffers(fd,r,s))
{
close(fd);
return -1;
}
}
return fd;
}
static int rawsend_socket(sa_family_t family,uint32_t fwmark)
{
int yes=1;
int *sock = rawsend_family_sock(family);
if (!sock) return -1;
if (*sock==-1)
{
int yes=1,pri=6;
//printf("rawsend_socket: family %d",family);
#ifdef __FreeBSD__
// IPPROTO_RAW with ipv6 in FreeBSD always returns EACCES on sendto.
// must use IPPROTO_TCP for ipv6. IPPROTO_RAW works for ipv4
// divert sockets are always v4 but accept both v4 and v6
*sock = (family==AF_INET) ? rawsend_socket_raw(family, IPPROTO_TCP) : rawsend_socket_divert(AF_INET);
#elif defined(__OpenBSD__) || defined (__APPLE__)
// OpenBSD does not allow sending TCP frames through raw sockets
// I dont know about macos. They have dropped ipfw in recent versions and their PF does not support divert-packet
*sock = rawsend_socket_divert(family);
#else
*sock = rawsend_socket_raw(family, IPPROTO_RAW);
#endif
if (*sock==-1)
{
perror("rawsend: socket()");
return -1;
}
#ifdef BSD
#if !(defined(__OpenBSD__) || defined (__APPLE__))
// HDRINCL not supported for ipv6 in any BSD
if (family==AF_INET && setsockopt(*sock,IPPROTO_IP,IP_HDRINCL,&yes,sizeof(yes)) == -1)
{
perror("rawsend: setsockopt(IP_HDRINCL)");
goto exiterr;
}
#endif
#ifdef SO_USER_COOKIE
if (setsockopt(*sock, SOL_SOCKET, SO_USER_COOKIE, &fwmark, sizeof(fwmark)) == -1)
{
perror("rawsend: setsockopt(SO_MARK)");
goto exiterr;
}
#endif
#endif
#ifdef __linux__
if (setsockopt(*sock, SOL_SOCKET, SO_MARK, &fwmark, sizeof(fwmark)) == -1)
{
perror("rawsend: setsockopt(SO_MARK)");
goto exiterr;
}
if (setsockopt(*sock, SOL_SOCKET, SO_PRIORITY, &pri, sizeof(pri)) == -1)
{
perror("rawsend: setsockopt(SO_PRIORITY)");
goto exiterr;
}
#endif
}
return *sock;
exiterr:
rawsend_clean_sock(sock);
return -1;
}
bool rawsend_preinit(uint32_t fwmark)
{
return rawsend_socket(AF_INET,fwmark)!=-1 && rawsend_socket(AF_INET6,fwmark)!=-1;
}
bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const void *data,size_t len)
{
int sock=rawsend_socket(dst->sa_family,fwmark);
if (sock==-1) return false;
int salen = dst->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
struct sockaddr_storage dst2;
memcpy(&dst2,dst,salen);
if (dst->sa_family==AF_INET6)
((struct sockaddr_in6 *)&dst2)->sin6_port = 0; // or will be EINVAL in linux
#ifdef BSD
/*
// this works only for local connections and not working for transit : cant spoof source addr
if (len>=sizeof(struct ip6_hdr))
{
// BSD ipv6 raw socks are limited. cannot pass the whole packet with ip6 header.
struct sockaddr_storage sa_src;
int v;
extract_endpoints(NULL,(struct ip6_hdr *)data,NULL, &sa_src, NULL);
v = ((struct ip6_hdr *)data)->ip6_ctlun.ip6_un1.ip6_un1_hlim;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &v, sizeof(v)) == -1)
perror("rawsend: setsockopt(IPV6_HOPLIMIT)");
// the only way to control source address is bind. make it equal to ip6_hdr
if (bind(sock, (struct sockaddr*)&sa_src, salen) < 0)
perror("rawsend bind: ");
//printf("BSD v6 RAWSEND "); print_sockaddr((struct sockaddr*)&sa_src); printf(" -> "); print_sockaddr((struct sockaddr*)&dst2); printf("\n");
proto_skip_ipv6((uint8_t**)&data, &len, NULL);
}
*/
#if !(defined(__OpenBSD__) || defined (__APPLE__))
// OpenBSD doesnt allow rawsending tcp frames. always use divert socket
if (dst->sa_family==AF_INET6)
#endif
{
ssize_t bytes = rawsend_sendto_divert(dst->sa_family,sock,data,len);
if (bytes==-1)
{
perror("rawsend: sendto_divert");
return false;
}
return true;
}
#endif
#if defined(__FreeBSD__) && __FreeBSD__<=10
// old FreeBSD requires some fields in the host byte order
if (dst->sa_family==AF_INET && len>=sizeof(struct ip))
{
((struct ip*)data)->ip_len = htons(((struct ip*)data)->ip_len);
((struct ip*)data)->ip_off = htons(((struct ip*)data)->ip_off);
}
#endif
// normal raw socket sendto
ssize_t bytes = sendto(sock, data, len, 0, (struct sockaddr*)&dst2, salen);
#if defined(__FreeBSD) && __FreeBSD__<=10
// restore byte order
if (dst->sa_family==AF_INET && len>=sizeof(struct ip))
{
((struct ip*)data)->ip_len = htons(((struct ip*)data)->ip_len);
((struct ip*)data)->ip_off = htons(((struct ip*)data)->ip_off);
}
#endif
if (bytes==-1)
{
perror("rawsend: sendto");
return false;
}
return true;
}
bool prepare_tcp_segment4(
const struct sockaddr_in *src, const struct sockaddr_in *dst,
uint8_t tcp_flags,
uint32_t seq, uint32_t ack_seq,
uint16_t wsize,
uint32_t *timestamps,
uint8_t ttl,
uint8_t fooling,
const void *data, uint16_t len,
uint8_t *buf, size_t *buflen)
{
uint16_t tcpoptlen = tcpopt_len(fooling,timestamps);
uint16_t pktlen = sizeof(struct ip) + sizeof(struct tcphdr) + tcpoptlen + len;
if (pktlen>*buflen)
{
fprintf(stderr,"prepare_tcp_segment : packet len cannot exceed %zu\n",*buflen);
return false;
}
struct ip *ip = (struct ip*) buf;
struct tcphdr *tcp = (struct tcphdr*) (ip+1);
ip->ip_off = 0;
ip->ip_v = 4;
ip->ip_hl = 5;
ip->ip_len = htons(pktlen);
ip->ip_id = 0;
ip->ip_ttl = ttl;
ip->ip_p = IPPROTO_TCP;
ip->ip_src = src->sin_addr;
ip->ip_dst = dst->sin_addr;
fill_tcphdr(tcp,tcp_flags,seq,ack_seq,fooling,src->sin_port,dst->sin_port,wsize,timestamps);
memcpy((char*)tcp+sizeof(struct tcphdr)+tcpoptlen,data,len);
tcp4_fix_checksum(tcp,sizeof(struct tcphdr)+tcpoptlen+len,&ip->ip_src,&ip->ip_dst);
if (fooling & TCP_FOOL_BADSUM) tcp->th_sum^=0xBEAF;
*buflen = pktlen;
return true;
}
bool prepare_tcp_segment6(
const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst,
uint8_t tcp_flags,
uint32_t seq, uint32_t ack_seq,
uint16_t wsize,
uint32_t *timestamps,
uint8_t ttl,
uint8_t fooling,
const void *data, uint16_t len,
uint8_t *buf, size_t *buflen)
{
uint16_t tcpoptlen = tcpopt_len(fooling,timestamps);
uint16_t payloadlen = sizeof(struct tcphdr) + tcpoptlen + len;
uint16_t pktlen = sizeof(struct ip6_hdr) + payloadlen;
if (pktlen>*buflen)
{
fprintf(stderr,"prepare_tcp_segment : packet len cannot exceed %zu\n",*buflen);
return false;
}
struct ip6_hdr *ip6 = (struct ip6_hdr*) buf;
struct tcphdr *tcp = (struct tcphdr*) (ip6+1);
ip6->ip6_ctlun.ip6_un1.ip6_un1_flow = htonl(0x60000000);
ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(payloadlen);
ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_TCP;
ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = ttl;
ip6->ip6_src = src->sin6_addr;
ip6->ip6_dst = dst->sin6_addr;
fill_tcphdr(tcp,tcp_flags,seq,ack_seq,fooling,src->sin6_port,dst->sin6_port,wsize,timestamps);
memcpy((char*)tcp+sizeof(struct tcphdr)+tcpoptlen,data,len);
tcp6_fix_checksum(tcp,sizeof(struct tcphdr)+tcpoptlen+len,&ip6->ip6_src,&ip6->ip6_dst);
if (fooling & TCP_FOOL_BADSUM) tcp->th_sum^=0xBEAF;
*buflen = pktlen;
return true;
}
bool prepare_tcp_segment(
const struct sockaddr *src, const struct sockaddr *dst,
uint8_t tcp_flags,
uint32_t seq, uint32_t ack_seq,
uint16_t wsize,
uint32_t *timestamps,
uint8_t ttl,
uint8_t fooling,
const void *data, uint16_t len,
uint8_t *buf, size_t *buflen)
{
return (src->sa_family==AF_INET && dst->sa_family==AF_INET) ?
prepare_tcp_segment4((struct sockaddr_in *)src,(struct sockaddr_in *)dst,tcp_flags,seq,ack_seq,wsize,timestamps,ttl,fooling,data,len,buf,buflen) :
(src->sa_family==AF_INET6 && dst->sa_family==AF_INET6) ?
prepare_tcp_segment6((struct sockaddr_in6 *)src,(struct sockaddr_in6 *)dst,tcp_flags,seq,ack_seq,wsize,timestamps,ttl,fooling,data,len,buf,buflen) :
false;
}
void extract_endpoints(const struct ip *ip,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst)
{
if (ip)
{
struct sockaddr_in *si;
if (dst)
{
si = (struct sockaddr_in*)dst;
si->sin_family = AF_INET;
si->sin_port = tcphdr ? tcphdr->th_dport : 0;
si->sin_addr = ip->ip_dst;
}
if (src)
{
si = (struct sockaddr_in*)src;
si->sin_family = AF_INET;
si->sin_port = tcphdr ? tcphdr->th_sport : 0;
si->sin_addr = ip->ip_src;
}
}
else if (ip6hdr)
{
struct sockaddr_in6 *si;
if (dst)
{
si = (struct sockaddr_in6*)dst;
si->sin6_family = AF_INET6;
si->sin6_port = tcphdr ? tcphdr->th_dport : 0;
si->sin6_addr = ip6hdr->ip6_dst;
si->sin6_flowinfo = 0;
si->sin6_scope_id = 0;
}
if (src)
{
si = (struct sockaddr_in6*)src;
si->sin6_family = AF_INET6;
si->sin6_port = tcphdr ? tcphdr->th_sport : 0;
si->sin6_addr = ip6hdr->ip6_src;
si->sin6_flowinfo = 0;
si->sin6_scope_id = 0;
}
}
}
static const char *proto_name(uint8_t proto)
{
switch(proto)
{
case IPPROTO_TCP:
return "tcp";
case IPPROTO_UDP:
return "udp";
case IPPROTO_ICMP:
return "icmp";
case IPPROTO_IGMP:
return "igmp";
case IPPROTO_ESP:
return "esp";
case IPPROTO_AH:
return "ah";
case IPPROTO_IPV6:
return "6in4";
#ifdef IPPROTO_SCTP
case IPPROTO_SCTP:
return "sctp";
#endif
default:
return NULL;
}
}
static void str_proto_name(char *s, size_t s_len, uint8_t proto)
{
const char *name = proto_name(proto);
if (name)
snprintf(s,s_len,"%s",name);
else
snprintf(s,s_len,"%u",proto);
}
static void str_srcdst_ip(char *s, size_t s_len, const void *saddr,const void *daddr)
{
char s_ip[16],d_ip[16];
*s_ip=*d_ip=0;
inet_ntop(AF_INET, saddr, s_ip, sizeof(s_ip));
inet_ntop(AF_INET, daddr, d_ip, sizeof(d_ip));
snprintf(s,s_len,"%s => %s",s_ip,d_ip);
}
static void str_ip(char *s, size_t s_len, const struct ip *ip)
{
char ss[64],s_proto[16];
str_srcdst_ip(ss,sizeof(ss),&ip->ip_src,&ip->ip_dst);
str_proto_name(s_proto,sizeof(s_proto),ip->ip_p);
snprintf(s,s_len,"%s proto=%s",ss,s_proto);
}
void print_ip(const struct ip *ip)
{
char s[64];
str_ip(s,sizeof(s),ip);
printf("%s",s);
}
static void str_srcdst_ip6(char *s, size_t s_len, const void *saddr,const void *daddr)
{
char s_ip[40],d_ip[40];
*s_ip=*d_ip=0;
inet_ntop(AF_INET6, saddr, s_ip, sizeof(s_ip));
inet_ntop(AF_INET6, daddr, d_ip, sizeof(d_ip));
snprintf(s,s_len,"%s => %s",s_ip,d_ip);
}
static void str_ip6hdr(char *s, size_t s_len, const struct ip6_hdr *ip6hdr, uint8_t proto)
{
char ss[128],s_proto[16];
str_srcdst_ip6(ss,sizeof(ss),&ip6hdr->ip6_src,&ip6hdr->ip6_dst);
str_proto_name(s_proto,sizeof(s_proto),proto);
snprintf(s,s_len,"%s proto=%s",ss,s_proto);
}
void print_ip6hdr(const struct ip6_hdr *ip6hdr, uint8_t proto)
{
char s[128];
str_ip6hdr(s,sizeof(s),ip6hdr,proto);
printf("%s",s);
}
static void str_tcphdr(char *s, size_t s_len, const struct tcphdr *tcphdr)
{
char flags[7],*f=flags;
if (tcphdr->th_flags & TH_SYN) *f++='S';
if (tcphdr->th_flags & TH_ACK) *f++='A';
if (tcphdr->th_flags & TH_RST) *f++='R';
if (tcphdr->th_flags & TH_FIN) *f++='F';
if (tcphdr->th_flags & TH_PUSH) *f++='P';
if (tcphdr->th_flags & TH_URG) *f++='U';
*f=0;
snprintf(s,s_len,"sport=%u dport=%u flags=%s seq=%u ack_seq=%u",htons(tcphdr->th_sport),htons(tcphdr->th_dport),flags,htonl(tcphdr->th_seq),htonl(tcphdr->th_ack));
}
void print_tcphdr(const struct tcphdr *tcphdr)
{
char s[80];
str_tcphdr(s,sizeof(s),tcphdr);
printf("%s",s);
}
bool proto_check_ipv4(uint8_t *data, size_t len)
{
return len >= 20 && (data[0] & 0xF0) == 0x40 &&
len >= ((data[0] & 0x0F) << 2);
}
// move to transport protocol
void proto_skip_ipv4(uint8_t **data, size_t *len)
{
size_t l;
l = (**data & 0x0F) << 2;
*data += l;
*len -= l;
}
bool proto_check_tcp(uint8_t *data, size_t len)
{
return len >= 20 && len >= ((data[12] & 0xF0) >> 2);
}
void proto_skip_tcp(uint8_t **data, size_t *len)
{
size_t l;
l = ((*data)[12] & 0xF0) >> 2;
*data += l;
*len -= l;
}
bool proto_check_ipv6(uint8_t *data, size_t len)
{
return len >= 40 && (data[0] & 0xF0) == 0x60 &&
(len - 40) >= htons(*(uint16_t*)(data + 4)); // payload length
}
// move to transport protocol
// proto_type = 0 => error
void proto_skip_ipv6(uint8_t **data, size_t *len, uint8_t *proto_type)
{
size_t hdrlen;
uint8_t HeaderType;
if (proto_type) *proto_type = 0; // put error in advance
HeaderType = (*data)[6]; // NextHeader field
*data += 40; *len -= 40; // skip ipv6 base header
while (*len > 0) // need at least one byte for NextHeader field
{
switch (HeaderType)
{
case 0: // Hop-by-Hop Options
case 43: // routing
case 51: // authentication
case 60: // Destination Options
case 135: // mobility
case 139: // Host Identity Protocol Version v2
case 140: // Shim6
if (*len < 2) return; // error
hdrlen = 8 + ((*data)[1] << 3);
break;
case 44: // fragment. length fixed to 8, hdrlen field defined as reserved
hdrlen = 8;
break;
case 59: // no next header
return; // error
default:
// we found some meaningful payload. it can be tcp, udp, icmp or some another exotic shit
if (proto_type) *proto_type = HeaderType;
return;
}
if (*len < hdrlen) return; // error
HeaderType = **data;
// advance to the next header location
*len -= hdrlen;
*data += hdrlen;
}
// we have garbage
}
bool tcp_synack_segment(const struct tcphdr *tcphdr)
{
/* check for set bits in TCP hdr */
return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == (TH_ACK|TH_SYN));
}

76
nfq/darkmagic.h Normal file
View File

@@ -0,0 +1,76 @@
#pragma once
#include "checksum.h"
#include <stdint.h>
#include <stdbool.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <sys/socket.h>
// returns netorder value
uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment);
#define TCP_FOOL_NONE 0
#define TCP_FOOL_MD5SIG 1
#define TCP_FOOL_BADSUM 2
#define TCP_FOOL_TS 4
#define TCP_FOOL_BADSEQ 8
// seq and wsize have network byte order
bool prepare_tcp_segment4(
const struct sockaddr_in *src, const struct sockaddr_in *dst,
uint8_t tcp_flags,
uint32_t seq, uint32_t ack_seq,
uint16_t wsize,
uint32_t *timestamps,
uint8_t ttl,
uint8_t fooling,
const void *data, uint16_t len,
uint8_t *buf, size_t *buflen);
bool prepare_tcp_segment6(
const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst,
uint8_t tcp_flags,
uint32_t seq, uint32_t ack_seq,
uint16_t wsize,
uint32_t *timestamps,
uint8_t ttl,
uint8_t fooling,
const void *data, uint16_t len,
uint8_t *buf, size_t *buflen);
bool prepare_tcp_segment(
const struct sockaddr *src, const struct sockaddr *dst,
uint8_t tcp_flags,
uint32_t seq, uint32_t ack_seq,
uint16_t wsize,
uint32_t *timestamps,
uint8_t ttl,
uint8_t fooling,
const void *data, uint16_t len,
uint8_t *buf, size_t *buflen);
void extract_endpoints(const struct ip *ip,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst);
uint8_t *tcp_find_option(struct tcphdr *tcp, uint8_t kind);
uint32_t *tcp_find_timestamps(struct tcphdr *tcp);
// auto creates internal socket and uses it for subsequent calls
bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const void *data,size_t len);
// should pre-do it if dropping privileges. otherwise its not necessary
bool rawsend_preinit(uint32_t fwmark);
// cleans up socket autocreated by rawsend
void rawsend_cleanup();
void print_ip(const struct ip *ip);
void print_ip6hdr(const struct ip6_hdr *ip6hdr, uint8_t proto);
void print_tcphdr(const struct tcphdr *tcphdr);
bool proto_check_ipv4(uint8_t *data, size_t len);
void proto_skip_ipv4(uint8_t **data, size_t *len);
bool proto_check_tcp(uint8_t *data, size_t len);
void proto_skip_tcp(uint8_t **data, size_t *len);
bool proto_check_ipv6(uint8_t *data, size_t len);
void proto_skip_ipv6(uint8_t **data, size_t *len, uint8_t *proto_type);
bool tcp_synack_segment(const struct tcphdr *tcphdr);

396
nfq/desync.c Normal file
View File

@@ -0,0 +1,396 @@
#define _GNU_SOURCE
#include "desync.h"
#include "protocol.h"
#include "params.h"
#include "helpers.h"
#include "hostlist.h"
#include <string.h>
const char *fake_http_request_default = "GET / HTTP/1.1\r\nHost: www.w3.org\r\n"
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0\r\n"
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
"Accept-Encoding: gzip, deflate\r\n\r\n";
const uint8_t fake_tls_clienthello_default[517] = {
0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0xfc, 0x03, 0x03, 0x9a, 0x8f, 0xa7, 0x6a, 0x5d,
0x57, 0xf3, 0x62, 0x19, 0xbe, 0x46, 0x82, 0x45, 0xe2, 0x59, 0x5c, 0xb4, 0x48, 0x31, 0x12, 0x15,
0x14, 0x79, 0x2c, 0xaa, 0xcd, 0xea, 0xda, 0xf0, 0xe1, 0xfd, 0xbb, 0x20, 0xf4, 0x83, 0x2a, 0x94,
0xf1, 0x48, 0x3b, 0x9d, 0xb6, 0x74, 0xba, 0x3c, 0x81, 0x63, 0xbc, 0x18, 0xcc, 0x14, 0x45, 0x57,
0x6c, 0x80, 0xf9, 0x25, 0xcf, 0x9c, 0x86, 0x60, 0x50, 0x31, 0x2e, 0xe9, 0x00, 0x22, 0x13, 0x01,
0x13, 0x03, 0x13, 0x02, 0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x2c, 0xc0, 0x30,
0xc0, 0x0a, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x33, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x35,
0x01, 0x00, 0x01, 0x91, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0x00, 0x0a, 0x77, 0x77, 0x77,
0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00,
0x00, 0x0a, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x01, 0x00,
0x01, 0x01, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e,
0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x05,
0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x1d, 0x00,
0x20, 0xb0, 0xe4, 0xda, 0x34, 0xb4, 0x29, 0x8d, 0xd3, 0x5c, 0x70, 0xd3, 0xbe, 0xe8, 0xa7, 0x2a,
0x6b, 0xe4, 0x11, 0x19, 0x8b, 0x18, 0x9d, 0x83, 0x9a, 0x49, 0x7c, 0x83, 0x7f, 0xa9, 0x03, 0x8c,
0x3c, 0x00, 0x17, 0x00, 0x41, 0x04, 0x4c, 0x04, 0xa4, 0x71, 0x4c, 0x49, 0x75, 0x55, 0xd1, 0x18,
0x1e, 0x22, 0x62, 0x19, 0x53, 0x00, 0xde, 0x74, 0x2f, 0xb3, 0xde, 0x13, 0x54, 0xe6, 0x78, 0x07,
0x94, 0x55, 0x0e, 0xb2, 0x6c, 0xb0, 0x03, 0xee, 0x79, 0xa9, 0x96, 0x1e, 0x0e, 0x98, 0x17, 0x78,
0x24, 0x44, 0x0c, 0x88, 0x80, 0x06, 0x8b, 0xd4, 0x80, 0xbf, 0x67, 0x7c, 0x37, 0x6a, 0x5b, 0x46,
0x4c, 0xa7, 0x98, 0x6f, 0xb9, 0x22, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x03, 0x04, 0x03, 0x03, 0x03,
0x02, 0x03, 0x01, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x16, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08,
0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03, 0x02, 0x01, 0x00,
0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x1c, 0x00, 0x02, 0x40, 0x01, 0x00, 0x15, 0x00, 0x96, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00
};
#define PKT_MAXDUMP 32
static uint8_t zeropkt[DPI_DESYNC_MAX_FAKE_LEN];
void desync_init()
{
memset(zeropkt, 0, sizeof(zeropkt));
}
bool desync_valid_first_stage(enum dpi_desync_mode mode)
{
return mode==DESYNC_FAKE || mode==DESYNC_RST || mode==DESYNC_RSTACK;
}
bool desync_valid_second_stage(enum dpi_desync_mode mode)
{
return mode==DESYNC_NONE || mode==DESYNC_DISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_SPLIT || mode==DESYNC_SPLIT2;
}
enum dpi_desync_mode desync_mode_from_string(const char *s)
{
if (!s)
return DESYNC_NONE;
else if (!strcmp(s,"fake"))
return DESYNC_FAKE;
else if (!strcmp(s,"rst"))
return DESYNC_RST;
else if (!strcmp(s,"rstack"))
return DESYNC_RSTACK;
else if (!strcmp(s,"disorder"))
return DESYNC_DISORDER;
else if (!strcmp(s,"disorder2"))
return DESYNC_DISORDER2;
else if (!strcmp(s,"split"))
return DESYNC_SPLIT;
else if (!strcmp(s,"split2"))
return DESYNC_SPLIT2;
return DESYNC_INVALID;
}
// auto creates internal socket and uses it for subsequent calls
static bool rawsend_rep(const struct sockaddr* dst,uint32_t fwmark,const void *data,size_t len)
{
for (int i=0;i<params.desync_repeats;i++)
if (!rawsend(dst,fwmark,data,len))
return false;
return true;
}
// result : true - drop original packet, false = dont drop
packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, size_t len_tcp, uint8_t *data_payload, size_t len_payload)
{
packet_process_result res=pass;
if (!!ip == !!ip6hdr) return res; // one and only one must be present
if (params.desync_mode==DESYNC_NONE && !params.hostcase && !params.hostnospace && !params.domcase) return res; // nothing to do. do not waste cpu
if (!(tcphdr->th_flags & TH_SYN) && len_payload)
{
struct sockaddr_storage src, dst;
const uint8_t *fake;
size_t fake_size;
char host[256];
bool bHaveHost=false;
bool bIsHttp;
uint8_t *p, *phost;
if ((bIsHttp = IsHttp(data_payload,len_payload)))
{
DLOG("packet contains HTTP request\n")
fake = params.fake_http;
fake_size = params.fake_http_size;
if (params.hostlist || params.debug) bHaveHost=HttpExtractHost(data_payload,len_payload,host,sizeof(host));
if (params.hostlist && !bHaveHost)
{
DLOG("not applying tampering to HTTP without Host:\n")
return res;
}
}
else if (IsTLSClientHello(data_payload,len_payload))
{
DLOG("packet contains TLS ClientHello\n")
fake = params.fake_tls;
fake_size = params.fake_tls_size;
if (params.hostlist || params.desync_skip_nosni || params.debug)
{
bHaveHost=TLSHelloExtractHost(data_payload,len_payload,host,sizeof(host));
if (params.desync_skip_nosni && !bHaveHost)
{
DLOG("not applying tampering to TLS ClientHello without hostname in the SNI\n")
return res;
}
}
}
else
{
if (!params.desync_any_proto) return res;
DLOG("applying tampering to unknown protocol\n")
fake = zeropkt;
fake_size = 256;
}
if (bHaveHost)
{
DLOG("hostname: %s\n",host)
if (params.hostlist && !SearchHostList(params.hostlist,host,params.debug))
{
DLOG("not applying tampering to this request\n")
return res;
}
}
if (bIsHttp && (params.hostcase || params.hostnospace || params.domcase) && (phost = (uint8_t*)memmem(data_payload, len_payload, "\r\nHost: ", 8)))
{
if (params.hostcase)
{
DLOG("modifying Host: => %c%c%c%c:\n", params.hostspell[0], params.hostspell[1], params.hostspell[2], params.hostspell[3])
memcpy(phost + 2, params.hostspell, 4);
res=modify;
}
if (params.domcase)
{
DLOG("mixing domain case\n");
for (p = phost+7; p < (data_payload + len_payload) && *p != '\r' && *p != '\n'; p++)
*p = (((size_t)p) & 1) ? tolower(*p) : toupper(*p);
res=modify;
}
uint8_t *pua;
if (params.hostnospace &&
(pua = (uint8_t*)memmem(data_payload, len_payload, "\r\nUser-Agent: ", 14)) &&
(pua = (uint8_t*)memmem(pua + 1, len_payload - (pua - data_payload) - 1, "\r\n", 2)))
{
DLOG("removing space after Host: and adding it to User-Agent:\n")
if (pua > phost)
{
memmove(phost + 7, phost + 8, pua - phost - 8);
phost[pua - phost - 1] = ' ';
}
else
{
memmove(pua + 1, pua, phost - pua + 7);
*pua = ' ';
}
res=modify;
}
}
if (params.desync_mode==DESYNC_NONE) return res;
extract_endpoints(ip, ip6hdr, tcphdr, &src, &dst);
if (params.debug)
{
printf("dpi desync src=");
print_sockaddr((struct sockaddr *)&src);
printf(" dst=");
print_sockaddr((struct sockaddr *)&dst);
printf("\n");
}
uint8_t newdata[DPI_DESYNC_MAX_FAKE_LEN+100];
size_t newlen;
uint8_t ttl_orig = ip ? ip->ip_ttl : ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim;
uint8_t ttl_fake = params.desync_ttl ? params.desync_ttl : ttl_orig;
uint8_t flags_orig = *((uint8_t*)tcphdr+13);
uint32_t *timestamps = tcp_find_timestamps(tcphdr);
enum dpi_desync_mode desync_mode = params.desync_mode;
bool b;
newlen = sizeof(newdata);
b = false;
switch(desync_mode)
{
case DESYNC_FAKE:
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, timestamps,
ttl_fake,params.desync_tcp_fooling_mode,
fake, fake_size, newdata, &newlen))
{
return res;
}
DLOG("sending fake request : ");
hexdump_limited_dlog(fake,fake_size,PKT_MAXDUMP); DLOG("\n")
b = true;
break;
case DESYNC_RST:
case DESYNC_RSTACK:
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_RST | (desync_mode==DESYNC_RSTACK ? TH_ACK:0), tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, timestamps,
ttl_fake,params.desync_tcp_fooling_mode,
NULL, 0, newdata, &newlen))
{
return res;
}
DLOG("sending fake RST/RSTACK\n");
b = true;
break;
}
if (b)
{
if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen))
return res;
if (params.desync_mode2==DESYNC_NONE)
{
if (params.desync_retrans)
{
DLOG("dropping original packet to force retransmission. len=%zu len_payload=%zu\n", len_pkt, len_payload)
}
else
{
DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", len_pkt, len_payload)
#ifdef __FreeBSD__
// FreeBSD tend to pass ipv6 frames with wrong checksum
if (res==modify || ip6hdr)
#else
// if original packet was tampered earlier it needs checksum fixed
if (res==modify)
#endif
tcp_fix_checksum(tcphdr,len_tcp,ip,ip6hdr);
if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, data_pkt, len_pkt))
return res;
}
return drop;
}
desync_mode = params.desync_mode2;
}
newlen = sizeof(newdata);
switch(desync_mode)
{
case DESYNC_DISORDER:
case DESYNC_DISORDER2:
{
size_t split_pos=len_payload>params.desync_split_pos ? params.desync_split_pos : 1;
uint8_t fakeseg[DPI_DESYNC_MAX_FAKE_LEN+100];
size_t fakeseg_len;
if (split_pos<len_payload)
{
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(tcphdr->th_seq,split_pos), tcphdr->th_ack, tcphdr->th_win, timestamps,
ttl_orig,TCP_FOOL_NONE,
data_payload+split_pos, len_payload-split_pos, newdata, &newlen))
return res;
DLOG("sending 2nd out-of-order tcp segment %zu-%zu len=%zu : ",split_pos,len_payload-1, len_payload-split_pos)
hexdump_limited_dlog(data_payload+split_pos,len_payload-split_pos,PKT_MAXDUMP); DLOG("\n")
if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen))
return res;
}
if (desync_mode==DESYNC_DISORDER)
{
fakeseg_len = sizeof(fakeseg);
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, timestamps,
ttl_fake,params.desync_tcp_fooling_mode,
zeropkt, split_pos, fakeseg, &fakeseg_len))
return res;
DLOG("sending fake(1) 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos)
hexdump_limited_dlog(zeropkt,split_pos,PKT_MAXDUMP); DLOG("\n")
if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len))
return res;
}
newlen = sizeof(newdata);
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, timestamps,
ttl_orig,TCP_FOOL_NONE,
data_payload, split_pos, newdata, &newlen))
return res;
DLOG("sending 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos)
hexdump_limited_dlog(data_payload,split_pos,PKT_MAXDUMP); DLOG("\n")
if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen))
return res;
if (desync_mode==DESYNC_DISORDER)
{
DLOG("sending fake(2) 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos)
hexdump_limited_dlog(zeropkt,split_pos,PKT_MAXDUMP); DLOG("\n")
if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len))
return res;
}
return drop;
}
break;
case DESYNC_SPLIT:
case DESYNC_SPLIT2:
{
size_t split_pos=len_payload>params.desync_split_pos ? params.desync_split_pos : 1;
uint8_t fakeseg[DPI_DESYNC_MAX_FAKE_LEN+100];
size_t fakeseg_len;
if (desync_mode==DESYNC_SPLIT)
{
fakeseg_len = sizeof(fakeseg);
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, timestamps,
ttl_fake,params.desync_tcp_fooling_mode,
zeropkt, split_pos, fakeseg, &fakeseg_len))
return res;
DLOG("sending fake(1) 1st tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos)
hexdump_limited_dlog(zeropkt,split_pos,PKT_MAXDUMP); DLOG("\n")
if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len))
return res;
}
newlen = sizeof(newdata);
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, timestamps,
ttl_orig,TCP_FOOL_NONE,
data_payload, split_pos, newdata, &newlen))
return res;
DLOG("sending 1st tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos)
hexdump_limited_dlog(data_payload,split_pos,PKT_MAXDUMP); DLOG("\n")
if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen))
return res;
if (desync_mode==DESYNC_SPLIT)
{
DLOG("sending fake(2) 1st tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos)
hexdump_limited_dlog(zeropkt,split_pos,PKT_MAXDUMP); DLOG("\n")
if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len))
return res;
}
if (split_pos<len_payload)
{
newlen = sizeof(newdata);
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(tcphdr->th_seq,split_pos), tcphdr->th_ack, tcphdr->th_win, timestamps,
ttl_orig,TCP_FOOL_NONE,
data_payload+split_pos, len_payload-split_pos, newdata, &newlen))
return res;
DLOG("sending 2nd tcp segment %zu-%zu len=%zu : ",split_pos,len_payload-1, len_payload-split_pos)
hexdump_limited_dlog(data_payload+split_pos,len_payload-split_pos,PKT_MAXDUMP); DLOG("\n")
if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen))
return res;
}
return drop;
}
break;
}
return res;
}
return res;
}

41
nfq/desync.h Normal file
View File

@@ -0,0 +1,41 @@
#pragma once
#include "darkmagic.h"
#include "nfqws.h"
#include <stdint.h>
#include <stdbool.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#ifdef __linux__
#define DPI_DESYNC_FWMARK_DEFAULT 0x40000000
#else
#define DPI_DESYNC_FWMARK_DEFAULT 512
#endif
#define DPI_DESYNC_MAX_FAKE_LEN 1500
enum dpi_desync_mode {
DESYNC_NONE=0,
DESYNC_INVALID,
DESYNC_FAKE,
DESYNC_RST,
DESYNC_RSTACK,
DESYNC_DISORDER,
DESYNC_DISORDER2,
DESYNC_SPLIT,
DESYNC_SPLIT2
};
extern const char *fake_http_request_default;
extern const uint8_t fake_tls_clienthello_default[517];
enum dpi_desync_mode desync_mode_from_string(const char *s);
bool desync_valid_first_stage(enum dpi_desync_mode mode);
bool desync_valid_second_stage(enum dpi_desync_mode mode);
void desync_init();
packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, size_t len_tcp, uint8_t *data_payload, size_t len_payload);

82
nfq/gzip.c Normal file
View File

@@ -0,0 +1,82 @@
#include "gzip.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ZCHUNK 16384
#define BUFMIN 128
#define BUFCHUNK (1024*128)
int z_readfile(FILE *F, char **buf, size_t *size)
{
z_stream zs;
int r;
unsigned char in[ZCHUNK];
size_t bufsize;
void *newbuf;
memset(&zs, 0, sizeof(zs));
*buf = NULL;
bufsize = *size = 0;
r = inflateInit2(&zs, 47);
if (r != Z_OK) return r;
do
{
zs.avail_in = fread(in, 1, sizeof(in), F);
if (ferror(F))
{
r = Z_ERRNO;
goto zerr;
}
if (!zs.avail_in) break;
zs.next_in = in;
do
{
if ((bufsize - *size) < BUFMIN)
{
bufsize += BUFCHUNK;
newbuf = *buf ? realloc(*buf, bufsize) : malloc(bufsize);
if (!newbuf)
{
r = Z_MEM_ERROR;
goto zerr;
}
*buf = newbuf;
}
zs.avail_out = bufsize - *size;
zs.next_out = (unsigned char*)(*buf + *size);
r = inflate(&zs, Z_NO_FLUSH);
if (r != Z_OK && r != Z_STREAM_END) goto zerr;
*size = bufsize - zs.avail_out;
} while (r == Z_OK && zs.avail_in);
} while (r == Z_OK);
if (*size < bufsize)
{
// free extra space
if ((newbuf = realloc(*buf, *size))) *buf = newbuf;
}
inflateEnd(&zs);
return Z_OK;
zerr:
inflateEnd(&zs);
if (*buf)
{
free(*buf);
*buf = NULL;
}
return r;
}
bool is_gzip(FILE* F)
{
unsigned char magic[2];
bool b = !fseek(F, 0, SEEK_SET) && fread(magic, 1, 2, F) == 2 && magic[0] == 0x1F && magic[1] == 0x8B;
fseek(F, 0, SEEK_SET);
return b;
}

8
nfq/gzip.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
#include <stdio.h>
#include <zlib.h>
#include <stdbool.h>
int z_readfile(FILE *F,char **buf,size_t *size);
bool is_gzip(FILE* F);

120
nfq/helpers.c Normal file
View File

@@ -0,0 +1,120 @@
#define _GNU_SOURCE
#include "helpers.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit)
{
size_t k;
bool bcut=false;
if (size>limit)
{
size=limit;
bcut = true;
}
if (!size) return;
for (k=0;k<size;k++) DLOG("%02X ",data[k]);
DLOG(bcut ? "... : " : ": ");
for (k=0;k<size;k++) DLOG("%c",data[k]>=0x20 && data[k]<=0x7F ? (char)data[k] : '.');
if (bcut) DLOG(" ...");
}
char *strncasestr(const char *s,const char *find, size_t slen)
{
char c, sc;
size_t len;
if ((c = *find++) != '\0')
{
len = strlen(find);
do
{
do
{
if (slen-- < 1 || (sc = *s++) == '\0') return NULL;
} while (toupper(c) != toupper(sc));
if (len > slen) return NULL;
} while (strncasecmp(s, find, len) != 0);
s--;
}
return (char *)s;
}
bool load_file(const char *filename,void *buffer,size_t *buffer_size)
{
FILE *F;
F = fopen(filename,"rb");
if (!F) return false;
*buffer_size = fread(buffer,1,*buffer_size,F);
if (ferror(F))
{
fclose(F);
return false;
}
fclose(F);
return true;
}
bool load_file_nonempty(const char *filename,void *buffer,size_t *buffer_size)
{
bool b = load_file(filename,buffer,buffer_size);
return b && *buffer_size;
}
void print_sockaddr(const struct sockaddr *sa)
{
char str[64];
switch (sa->sa_family)
{
case AF_INET:
if (inet_ntop(sa->sa_family, &((struct sockaddr_in*)sa)->sin_addr, str, sizeof(str)))
printf("%s:%d", str, ntohs(((struct sockaddr_in*)sa)->sin_port));
break;
case AF_INET6:
if (inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, str, sizeof(str)))
printf("[%s]:%d", str, ntohs(((struct sockaddr_in6*)sa)->sin6_port));
break;
default:
printf("UNKNOWN_FAMILY_%d", sa->sa_family);
}
}
void dbgprint_socket_buffers(int fd)
{
if (params.debug)
{
int v;
socklen_t sz;
sz=sizeof(int);
if (!getsockopt(fd,SOL_SOCKET,SO_RCVBUF,&v,&sz))
DLOG("fd=%d SO_RCVBUF=%d\n",fd,v)
sz=sizeof(int);
if (!getsockopt(fd,SOL_SOCKET,SO_SNDBUF,&v,&sz))
DLOG("fd=%d SO_SNDBUF=%d\n",fd,v)
}
}
bool set_socket_buffers(int fd, int rcvbuf, int sndbuf)
{
DLOG("set_socket_buffers fd=%d rcvbuf=%d sndbuf=%d\n",fd,rcvbuf,sndbuf)
if (rcvbuf && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) <0)
{
perror("setsockopt (SO_RCVBUF): ");
close(fd);
return false;
}
if (sndbuf && setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(int)) <0)
{
perror("setsockopt (SO_SNDBUF): ");
close(fd);
return false;
}
dbgprint_socket_buffers(fd);
return true;
}

18
nfq/helpers.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stddef.h>
#include <stdbool.h>
#include "params.h"
void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit);
char *strncasestr(const char *s,const char *find, size_t slen);
bool load_file(const char *filename,void *buffer,size_t *buffer_size);
bool load_file_nonempty(const char *filename,void *buffer,size_t *buffer_size);
void print_sockaddr(const struct sockaddr *sa);
void dbgprint_socket_buffers(int fd);
bool set_socket_buffers(int fd, int rcvbuf, int sndbuf);

112
nfq/hostlist.c Normal file
View File

@@ -0,0 +1,112 @@
#include <stdio.h>
#include "hostlist.h"
#include "gzip.h"
static bool addpool(strpool **hostlist, char **s, char *end)
{
char *p;
// advance until eol lowering all chars
for (p = *s; p<end && *p && *p!='\r' && *p != '\n'; p++) *p=tolower(*p);
if (!StrPoolAddStrLen(hostlist, *s, p-*s))
{
StrPoolDestroy(hostlist);
*hostlist = NULL;
return false;
}
// advance to the next line
for (; p<end && (!*p || *p=='\r' || *p=='\n') ; p++);
*s = p;
return true;
}
bool LoadHostList(strpool **hostlist, char *filename)
{
char *p, *e, s[256], *zbuf;
size_t zsize;
int ct = 0;
FILE *F;
int r;
if (*hostlist)
{
StrPoolDestroy(hostlist);
*hostlist = NULL;
}
if (!(F = fopen(filename, "rb")))
{
fprintf(stderr, "Could not open %s\n", filename);
return false;
}
if (is_gzip(F))
{
r = z_readfile(F,&zbuf,&zsize);
fclose(F);
if (r==Z_OK)
{
printf("zlib compression detected. uncompressed size : %zu\n", zsize);
p = zbuf;
e = zbuf + zsize;
while(p<e)
{
if (!addpool(hostlist,&p,e))
{
fprintf(stderr, "Not enough memory to store host list : %s\n", filename);
free(zbuf);
return false;
}
ct++;
}
free(zbuf);
}
else
{
fprintf(stderr, "zlib decompression failed : result %d\n",r);
return false;
}
}
else
{
printf("loading plain text list\n");
while (fgets(s, 256, F))
{
p = s;
if (!addpool(hostlist,&p,p+strlen(p)))
{
fprintf(stderr, "Not enough memory to store host list : %s\n", filename);
fclose(F);
return false;
}
ct++;
}
fclose(F);
}
printf("Loaded %d hosts from %s\n", ct, filename);
return true;
}
bool SearchHostList(strpool *hostlist, const char *host, bool debug)
{
if (hostlist)
{
const char *p = host;
bool bInHostList;
while (p)
{
bInHostList = StrPoolCheckStr(hostlist, p);
if (debug) printf("Hostlist check for %s : %s\n", p, bInHostList ? "positive" : "negative");
if (bInHostList) return true;
p = strchr(p, '.');
if (p) p++;
}
}
return false;
}

7
nfq/hostlist.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
#include <stdbool.h>
#include "strpool.h"
bool LoadHostList(strpool **hostlist, char *filename);
bool SearchHostList(strpool *hostlist, const char *host,bool debug);

1
nfq/nfqws Symbolic link
View File

@@ -0,0 +1 @@
../binaries/x86_64/nfqws

809
nfq/nfqws.c Normal file
View File

@@ -0,0 +1,809 @@
#define _GNU_SOURCE
#include "nfqws.h"
#include "sec.h"
#include "desync.h"
#include "helpers.h"
#include "checksum.h"
#include "params.h"
#include "protocol.h"
#include "hostlist.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <getopt.h>
#include <fcntl.h>
#include <pwd.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#ifdef __linux__
#include <libnetfilter_queue/libnetfilter_queue.h>
#define NF_DROP 0
#define NF_ACCEPT 1
#endif
#ifndef IPPROTO_DIVERT
#define IPPROTO_DIVERT 258
#endif
struct params_s params;
static bool bHup = false;
static void onhup(int sig)
{
printf("HUP received !\n");
if (params.hostlist)
printf("Will reload hostlist on next request\n");
bHup = true;
}
// should be called in normal execution
static void dohup()
{
if (bHup)
{
if (params.hostlist)
{
if (!LoadHostList(&params.hostlist, params.hostfile))
{
// what will we do without hostlist ?? sure, gonna die
exit(1);
}
}
bHup = false;
}
}
static void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize)
{
uint16_t winsize_old;
winsize_old = htons(tcp->th_win); // << scale_factor;
tcp->th_win = htons(winsize);
DLOG("Window size change %u => %u\n", winsize_old, winsize)
}
// data/len points to data payload
static bool modify_tcp_packet(uint8_t *data, size_t len, struct tcphdr *tcphdr)
{
if (tcp_synack_segment(tcphdr) && params.wsize)
{
tcp_rewrite_winsize(tcphdr, (uint16_t)params.wsize);
return true;
}
return false;
}
#ifdef __linux__
static packet_process_result processPacketData(uint8_t *data_pkt, size_t len_pkt, uint32_t *mark)
#else
static packet_process_result processPacketData(uint8_t *data_pkt, size_t len_pkt)
#endif
{
struct ip *ip = NULL;
struct ip6_hdr *ip6hdr = NULL;
struct tcphdr *tcphdr = NULL;
size_t len = len_pkt, len_tcp;
uint8_t *data = data_pkt;
packet_process_result res = pass, res2;
uint8_t proto;
#ifdef __linux__
if (*mark & params.desync_fwmark)
{
DLOG("ignoring generated packet\n")
return res;
}
#endif
if (proto_check_ipv4(data, len))
{
ip = (struct ip *) data;
proto = ip->ip_p;
proto_skip_ipv4(&data, &len);
if (params.debug)
{
printf("IP4: ");
print_ip(ip);
}
}
else if (proto_check_ipv6(data, len))
{
ip6hdr = (struct ip6_hdr *) data;
proto_skip_ipv6(&data, &len, &proto);
if (params.debug)
{
printf("IP6: ");
print_ip6hdr(ip6hdr, proto);
}
}
else
{
// not ipv6 and not ipv4
return res;
}
if (proto==IPPROTO_TCP && proto_check_tcp(data, len))
{
tcphdr = (struct tcphdr *) data;
len_tcp = len;
proto_skip_tcp(&data, &len);
if (params.debug)
{
printf(" ");
print_tcphdr(tcphdr);
printf("\n");
}
if (len) { DLOG("TCP: ") hexdump_limited_dlog(data, len, 32); DLOG("\n") }
if (modify_tcp_packet(data, len, tcphdr))
res = modify;
res2 = dpi_desync_packet(data_pkt, len_pkt, ip, ip6hdr, tcphdr, len_tcp, data, len);
res = (res2==pass && res==modify) ? modify : res2;
// in my FreeBSD divert tests only ipv4 packets were reinjected with correct checksum
// ipv6 packets were with incorrect checksum
#ifdef __FreeBSD__
// FreeBSD tend to pass ipv6 frames with wrong checksum
if (res==modify || ip6hdr)
#else
if (res==modify)
#endif
tcp_fix_checksum(tcphdr,len_tcp,ip,ip6hdr);
}
else
{
if (params.debug) printf("\n");
}
return res;
}
#ifdef __linux__
static int nfq_cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
struct nfq_data *nfa, void *cookie)
{
int id;
size_t len;
struct nfqnl_msg_packet_hdr *ph;
uint8_t *data;
ph = nfq_get_msg_packet_hdr(nfa);
id = ph ? ntohl(ph->packet_id) : 0;
uint32_t mark = nfq_get_nfmark(nfa);
len = nfq_get_payload(nfa, &data);
DLOG("packet: id=%d len=%zu\n", id, len)
if (len >= 0)
{
switch (processPacketData(data, len, &mark))
{
case modify:
DLOG("packet: id=%d pass modified\n", id);
return nfq_set_verdict2(qh, id, NF_ACCEPT, mark, len, data);
case drop:
DLOG("packet: id=%d drop\n", id);
return nfq_set_verdict2(qh, id, NF_DROP, mark, 0, NULL);
}
}
DLOG("packet: id=%d pass unmodified\n", id);
return nfq_set_verdict2(qh, id, NF_ACCEPT, mark, 0, NULL);
}
static int nfq_main()
{
struct nfq_handle *h = NULL;
struct nfq_q_handle *qh = NULL;
int fd,rv;
uint8_t buf[16384] __attribute__((aligned));
printf("opening library handle\n");
h = nfq_open();
if (!h) {
perror("nfq_open() :");
goto exiterr;
}
printf("unbinding existing nf_queue handler for AF_INET (if any)\n");
if (nfq_unbind_pf(h, AF_INET) < 0) {
perror("nfq_unbind_pf() :");
goto exiterr;
}
printf("binding nfnetlink_queue as nf_queue handler for AF_INET\n");
if (nfq_bind_pf(h, AF_INET) < 0) {
perror("nfq_bind_pf() :");
goto exiterr;
}
printf("binding this socket to queue '%u'\n", params.qnum);
qh = nfq_create_queue(h, params.qnum, &nfq_cb, &params);
if (!qh) {
perror("nfq_create_queue() :");
goto exiterr;
}
printf("setting copy_packet mode\n");
if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) {
perror("can't set packet_copy mode :");
goto exiterr;
}
if (nfq_set_queue_maxlen(qh, Q_MAXLEN) < 0) {
perror("can't set queue maxlen : ");
goto exiterr;
}
// accept packets if they cant be handled
if (nfq_set_queue_flags(qh, NFQA_CFG_F_FAIL_OPEN , NFQA_CFG_F_FAIL_OPEN))
{
fprintf(stderr, "can't set queue flags. its OK on linux <3.6\n");
// dot not fail. not supported on old linuxes <3.6
}
if (params.droproot && !droproot(params.uid, params.gid))
goto exiterr;
print_id();
signal(SIGHUP, onhup);
desync_init();
fd = nfq_fd(h);
// increase socket buffer size. on slow systems reloading hostlist can take a while.
// if too many unhandled packets are received its possible to get "no buffer space available" error
if (!set_socket_buffers(fd,Q_RCVBUF/2,Q_SNDBUF/2))
goto exiterr;
do
{
while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0)
{
dohup();
int r = nfq_handle_packet(h, buf, rv);
if (r) fprintf(stderr, "nfq_handle_packet error %d\n", r);
}
fprintf(stderr, "recv: errno %d\n",errno);
perror("recv");
// do not fail on ENOBUFS
} while(errno==ENOBUFS);
printf("unbinding from queue 0\n");
nfq_destroy_queue(qh);
#ifdef INSANE
/* normally, applications SHOULD NOT issue this command, since
* it detaches other programs/sockets from AF_INET, too ! */
printf("unbinding from AF_INET\n");
nfq_unbind_pf(h, AF_INET);
#endif
printf("closing library handle\n");
nfq_close(h);
return 0;
exiterr:
if (qh) nfq_destroy_queue(qh);
if (h) nfq_close(h);
return 1;
}
#elif defined(BSD)
static int dvt_main()
{
uint8_t buf[16384] __attribute__((aligned));
struct sockaddr_storage sa_from;
int fd[2] = {-1,-1}; // 4,6
int i,r,res=1,fdct=1,fdmax;
unsigned int id=0;
socklen_t socklen;
ssize_t rd,wr;
packet_process_result ppr;
fd_set fdset;
{
struct sockaddr_in bp4;
bp4.sin_family = AF_INET;
bp4.sin_port = htons(params.port);
bp4.sin_addr.s_addr = INADDR_ANY;
printf("creating divert4 socket\n");
fd[0] = socket(AF_INET, SOCK_RAW, IPPROTO_DIVERT);
if (fd[0] == -1) {
perror("socket (DIVERT4): ");
goto exiterr;
}
printf("binding divert4 socket\n");
if (bind(fd[0], (struct sockaddr*)&bp4, sizeof(bp4)) < 0)
{
perror("bind (DIVERT4): ");
goto exiterr;
}
if (!set_socket_buffers(fd[0],Q_RCVBUF,Q_SNDBUF))
goto exiterr;
}
#ifdef __OpenBSD__
{
// in OpenBSD must use separate divert sockets for ipv4 and ipv6
struct sockaddr_in6 bp6;
memset(&bp6,0,sizeof(bp6));
bp6.sin6_family = AF_INET6;
bp6.sin6_port = htons(params.port);
printf("creating divert6 socket\n");
fd[1] = socket(AF_INET6, SOCK_RAW, IPPROTO_DIVERT);
if (fd[1] == -1) {
perror("socket (DIVERT6): ");
goto exiterr;
}
printf("binding divert6 socket\n");
if (bind(fd[1], (struct sockaddr*)&bp6, sizeof(bp6)) < 0)
{
perror("bind (DIVERT6): ");
goto exiterr;
}
fdct++;
if (!set_socket_buffers(fd[1],Q_RCVBUF,Q_SNDBUF))
goto exiterr;
}
#endif
fdmax = (fd[0]>fd[1] ? fd[0] : fd[1]) + 1;
printf("initializing raw sockets with sockarg 0x%08X (%u)\n", params.desync_fwmark, params.desync_fwmark);
if (!rawsend_preinit(params.desync_fwmark))
goto exiterr;
if (params.droproot && !droproot(params.uid, params.gid))
goto exiterr;
print_id();
signal(SIGHUP, onhup);
desync_init();
for(;;)
{
FD_ZERO(&fdset);
for(i=0;i<fdct;i++) FD_SET(fd[i], &fdset);
r = select(fdmax,&fdset,NULL,NULL,NULL);
if (r==-1)
{
if (errno==EINTR)
{
// a signal received
dohup();
continue;
}
perror("select: ");
goto exiterr;
}
for(i=0;i<fdct;i++)
{
if (FD_ISSET(fd[i], &fdset))
{
socklen = sizeof(sa_from);
rd = recvfrom(fd[i], buf, sizeof(buf), 0, (struct sockaddr*)&sa_from, &socklen);
if (rd<0)
{
perror("recvfrom: ");
goto exiterr;
}
else if (rd>0)
{
DLOG("packet: id=%u len=%zd\n", id, rd)
ppr = processPacketData(buf, rd);
switch (ppr)
{
case pass:
case modify:
DLOG(ppr==pass ? "packet: id=%u reinject unmodified\n" : "packet: id=%u reinject modified\n", id);
wr = sendto(fd[i], buf, rd, 0, (struct sockaddr*)&sa_from, socklen);
if (wr<0)
perror("reinject sendto: ");
else if (wr!=rd)
fprintf(stderr,"reinject sendto: not all data was reinjected. received %zd, sent %zd\n", rd, wr);
break;
default:
DLOG("packet: id=%u drop\n", id);
}
id++;
}
else
{
DLOG("unexpected zero size recvfrom\n")
}
}
}
}
res=0;
exiterr:
if (fd[0]!=-1) close(fd[0]);
if (fd[1]!=-1) close(fd[1]);
return res;
}
#endif
static void exithelp()
{
printf(
" --debug=0|1\n"
#ifdef __linux__
" --qnum=<nfqueue_number>\n"
#elif defined(BSD)
" --port=<port>\t\t\t\t; divert port\n"
#endif
" --daemon\t\t\t\t; daemonize\n"
" --pidfile=<filename>\t\t\t; write pid to file\n"
" --user=<username>\t\t\t; drop root privs\n"
" --uid=uid[:gid]\t\t\t; drop root privs\n"
" --wsize=<window_size>\t\t\t; set window size. 0 = do not modify. OBSOLETE !\n"
" --hostcase\t\t\t\t; change Host: => host:\n"
" --hostspell\t\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n"
" --hostnospace\t\t\t\t; remove space after Host: and add it to User-Agent: to preserve packet size\n"
" --domcase\t\t\t\t; mix domain case : Host: TeSt.cOm\n"
" --dpi-desync=<mode>[,<mode2>]\t\t; try to desync dpi state. modes : fake rst rstack disorder disorder2 split split2\n"
#ifdef __linux__
" --dpi-desync-fwmark=<int|0xHEX>\t; override fwmark for desync packet. default = 0x%08X (%u)\n"
#elif defined(SO_USER_COOKIE)
" --dpi-desync-sockarg=<int|0xHEX>\t; override sockarg (SO_USER_COOKIE) for desync packet. default = 0x%08X (%u)\n"
#endif
" --dpi-desync-ttl=<int>\t\t\t; set ttl for desync packet\n"
" --dpi-desync-fooling=<mode>[,<mode>]\t; can use multiple comma separated values. modes : none md5sig ts badseq badsum\n"
#ifdef __linux__
" --dpi-desync-retrans=0|1\t\t; 0(default)=reinject original data packet after fake 1=drop original data packet to force its retransmission\n"
#endif
" --dpi-desync-repeats=<N>\t\t; send every desync packet N times\n"
" --dpi-desync-skip-nosni=0|1\t\t; 1(default)=do not act on ClientHello without SNI (ESNI ?)\n"
" --dpi-desync-split-pos=<1..%u>\t; (for disorder only) split TCP packet at specified position\n"
" --dpi-desync-any-protocol=0|1\t\t; 0(default)=desync only http and tls 1=desync any nonempty data packet\n"
" --dpi-desync-fake-http=<filename>\t; file containing fake http request\n"
" --dpi-desync-fake-tls=<filename>\t; file containing fake TLS ClientHello (for https)\n"
" --hostlist=<filename>\t\t\t; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply)\n",
#if defined(__linux__) || defined(SO_USER_COOKIE)
DPI_DESYNC_FWMARK_DEFAULT,DPI_DESYNC_FWMARK_DEFAULT,
#endif
DPI_DESYNC_MAX_FAKE_LEN
);
exit(1);
}
static void cleanup_params()
{
if (params.hostlist)
{
StrPoolDestroy(&params.hostlist);
params.hostlist = NULL;
}
}
static void exithelp_clean()
{
cleanup_params();
exithelp();
}
static void exit_clean(int code)
{
cleanup_params();
exit(code);
}
int main(int argc, char **argv)
{
int result, v;
int option_index = 0;
bool daemon = false;
char pidfile[256];
srandom(time(NULL));
memset(&params, 0, sizeof(params));
memcpy(params.hostspell, "host", 4); // default hostspell
*pidfile = 0;
params.desync_fwmark = DPI_DESYNC_FWMARK_DEFAULT;
params.desync_skip_nosni = true;
params.desync_split_pos = 3;
params.desync_repeats = 1;
params.fake_tls_size = sizeof(fake_tls_clienthello_default);
memcpy(params.fake_tls,fake_tls_clienthello_default,params.fake_tls_size);
params.fake_http_size = strlen(fake_http_request_default);
memcpy(params.fake_http,fake_http_request_default,params.fake_http_size);
if (can_drop_root()) // are we root ?
{
params.uid = params.gid = 0x7FFFFFFF; // default uid:gid
params.droproot = true;
}
const struct option long_options[] = {
{"debug",optional_argument,0,0}, // optidx=0
#ifdef __linux__
{"qnum",required_argument,0,0}, // optidx=1
#elif defined(BSD)
{"port",required_argument,0,0}, // optidx=1
#else
{"disabled_argument_1",no_argument,0,0},// optidx=1
#endif
{"daemon",no_argument,0,0}, // optidx=2
{"pidfile",required_argument,0,0}, // optidx=3
{"user",required_argument,0,0 }, // optidx=4
{"uid",required_argument,0,0 }, // optidx=5
{"wsize",required_argument,0,0}, // optidx=6
{"hostcase",no_argument,0,0}, // optidx=7
{"hostspell",required_argument,0,0}, // optidx=8
{"hostnospace",no_argument,0,0}, // optidx=9
{"domcase",no_argument,0,0 }, // optidx=10
{"dpi-desync",required_argument,0,0}, // optidx=11
#ifdef __linux__
{"dpi-desync-fwmark",required_argument,0,0}, // optidx=12
#elif defined(SO_USER_COOKIE)
{"dpi-desync-sockarg",required_argument,0,0}, // optidx=12
#else
{"disabled_argument_2",no_argument,0,0}, // optidx=12
#endif
{"dpi-desync-ttl",required_argument,0,0}, // optidx=13
{"dpi-desync-fooling",required_argument,0,0}, // optidx=14
{"dpi-desync-retrans",optional_argument,0,0}, // optidx=15
{"dpi-desync-repeats",required_argument,0,0}, // optidx=16
{"dpi-desync-skip-nosni",optional_argument,0,0},// optidx=17
{"dpi-desync-split-pos",required_argument,0,0},// optidx=18
{"dpi-desync-any-protocol",optional_argument,0,0},// optidx=19
{"dpi-desync-fake-http",required_argument,0,0},// optidx=20
{"dpi-desync-fake-tls",required_argument,0,0},// optidx=21
{"hostlist",required_argument,0,0}, // optidx=22
{NULL,0,NULL,0}
};
if (argc < 2) exithelp();
while ((v = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1)
{
if (v) exithelp();
switch (option_index)
{
case 0: /* debug */
params.debug = !optarg || atoi(optarg);
break;
case 1: /* qnum or port */
#ifdef __linux__
params.qnum = atoi(optarg);
if (params.qnum < 0 || params.qnum>65535)
{
fprintf(stderr, "bad qnum\n");
exit_clean(1);
}
#elif defined(BSD)
{
int i = atoi(optarg);
if (i <= 0 || i > 65535)
{
fprintf(stderr, "bad port number\n");
exit_clean(1);
}
params.port = (uint16_t)i;
}
#endif
break;
case 2: /* daemon */
daemon = true;
break;
case 3: /* pidfile */
strncpy(pidfile, optarg, sizeof(pidfile));
pidfile[sizeof(pidfile) - 1] = '\0';
break;
case 4: /* user */
{
struct passwd *pwd = getpwnam(optarg);
if (!pwd)
{
fprintf(stderr, "non-existent username supplied\n");
exit_clean(1);
}
params.uid = pwd->pw_uid;
params.gid = pwd->pw_gid;
params.droproot = true;
break;
}
case 5: /* uid */
params.gid = 0x7FFFFFFF; // default gid. drop gid=0
params.droproot = true;
if (!sscanf(optarg, "%u:%u", &params.uid, &params.gid))
{
fprintf(stderr, "--uid should be : uid[:gid]\n");
exit_clean(1);
}
break;
case 6: /* wsize */
params.wsize = atoi(optarg);
if (params.wsize < 0 || params.wsize>65535)
{
fprintf(stderr, "bad wsize\n");
exit_clean(1);
}
break;
case 7: /* hostcase */
params.hostcase = true;
break;
case 8: /* hostspell */
if (strlen(optarg) != 4)
{
fprintf(stderr, "hostspell must be exactly 4 chars long\n");
exit_clean(1);
}
params.hostcase = true;
memcpy(params.hostspell, optarg, 4);
break;
case 9: /* hostnospace */
params.hostnospace = true;
break;
case 10: /* domcase */
params.domcase = true;
break;
case 11: /* dpi-desync */
{
char *mode2;
mode2 = optarg ? strchr(optarg,',') : NULL;
if (mode2) *mode2++=0;
params.desync_mode = desync_mode_from_string(optarg);
params.desync_mode2 = desync_mode_from_string(mode2);
if (params.desync_mode==DESYNC_NONE || params.desync_mode==DESYNC_INVALID || params.desync_mode2==DESYNC_INVALID)
{
fprintf(stderr, "invalid dpi-desync mode\n");
exit_clean(1);
}
if (params.desync_mode2 && !(desync_valid_first_stage(params.desync_mode) && desync_valid_second_stage(params.desync_mode2)))
{
fprintf(stderr, "invalid desync combo : %s+%s\n", optarg,mode2);
exit_clean(1);
}
}
break;
case 12: /* dpi-desync-fwmark/dpi-desync-sockarg */
#if defined(__linux__) || defined(SO_USER_COOKIE)
params.desync_fwmark = 0;
if (!sscanf(optarg, "0x%X", &params.desync_fwmark)) sscanf(optarg, "%u", &params.desync_fwmark);
if (!params.desync_fwmark)
{
fprintf(stderr, "fwmark/sockarg should be decimal or 0xHEX and should not be zero\n");
exit_clean(1);
}
#else
fprintf(stderr, "fmwark/sockarg not supported in this OS\n");
exit_clean(1);
#endif
break;
case 13: /* dpi-desync-ttl */
params.desync_ttl = (uint8_t)atoi(optarg);
break;
case 14: /* dpi-desync-fooling */
{
char *e,*p = optarg;
while (p)
{
e = strchr(p,',');
if (e) *e++=0;
if (!strcmp(p,"md5sig"))
params.desync_tcp_fooling_mode |= TCP_FOOL_MD5SIG;
else if (!strcmp(p,"ts"))
params.desync_tcp_fooling_mode |= TCP_FOOL_TS;
else if (!strcmp(p,"badsum"))
{
#ifdef __OpenBSD__
printf("\nWARNING !!! OpenBSD may forcibly recompute tcp checksums !!! In this case badsum fooling will not work.\nYou should check tcp checksum correctness in tcpdump manually before using badsum.\n\n");
#endif
params.desync_tcp_fooling_mode |= TCP_FOOL_BADSUM;
}
else if (!strcmp(p,"badseq"))
params.desync_tcp_fooling_mode |= TCP_FOOL_BADSEQ;
else if (strcmp(p,"none"))
{
fprintf(stderr, "dpi-desync-fooling allowed values : none,md5sig,ts,badseq,badsum\n");
exit_clean(1);
}
p = e;
}
}
break;
case 15: /* dpi-desync-retrans */
#ifdef __linux__
params.desync_retrans = !optarg || atoi(optarg);
#else
fprintf(stderr, "dpi-desync-retrans is only supported in linux\n");
exit_clean(1);
#endif
break;
case 16: /* dpi-desync-repeats */
params.desync_repeats = atoi(optarg);
if (params.desync_repeats<=0 || params.desync_repeats>20)
{
fprintf(stderr, "dpi-desync-repeats must be within 1..20\n");
exit_clean(1);
}
break;
case 17: /* dpi-desync-skip-nosni */
params.desync_skip_nosni = !optarg || atoi(optarg);
break;
case 18: /* dpi-desync-split-pos */
params.desync_split_pos = atoi(optarg);
if (params.desync_split_pos<1 || params.desync_split_pos>DPI_DESYNC_MAX_FAKE_LEN)
{
fprintf(stderr, "dpi-desync-split-pos must be within 1..%u range\n",DPI_DESYNC_MAX_FAKE_LEN);
exit_clean(1);
}
break;
case 19: /* dpi-desync-any-protocol */
params.desync_any_proto = !optarg || atoi(optarg);
break;
case 20: /* dpi-desync-fake-http */
params.fake_http_size = sizeof(params.fake_http);
if (!load_file_nonempty(optarg,params.fake_http,&params.fake_http_size))
{
fprintf(stderr, "could not read %s\n",optarg);
exit_clean(1);
}
break;
case 21: /* dpi-desync-fake-tls */
params.fake_tls_size = sizeof(params.fake_tls);
if (!load_file_nonempty(optarg,params.fake_tls,&params.fake_tls_size))
{
fprintf(stderr, "could not read %s\n",optarg);
exit_clean(1);
}
break;
case 22: /* hostlist */
if (!LoadHostList(&params.hostlist, optarg))
exit_clean(1);
strncpy(params.hostfile,optarg,sizeof(params.hostfile));
params.hostfile[sizeof(params.hostfile)-1]='\0';
break;
}
}
#ifdef BSD
if (!params.port)
{
fprintf(stderr, "Need port number\n");
exit_clean(1);
}
#endif
if (daemon) daemonize();
if (*pidfile && !writepid(pidfile))
{
fprintf(stderr, "could not write pidfile\n");
goto exiterr;
}
#ifdef __linux__
result = nfq_main();
#elif defined(BSD)
result = dvt_main();
#else
#error unsupported OS
#endif
ex:
rawsend_cleanup();
cleanup_params();
return result;
exiterr:
result = 1;
goto ex;
}

6
nfq/nfqws.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
typedef enum
{
pass = 0, modify, drop
} packet_process_result;

54
nfq/params.h Normal file
View File

@@ -0,0 +1,54 @@
#pragma once
#include "params.h"
#include "strpool.h"
#include "desync.h"
#include <sys/param.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#if defined(__OpenBSD__) || defined (__APPLE__)
// divert-packet also diverts return traffic. sockets will experience high load
#define Q_RCVBUF (256*1024) // in bytes
#define Q_SNDBUF (256*1024) // in bytes
#define RAW_SNDBUF (64*1024) // in bytes
#else
#define Q_RCVBUF (128*1024) // in bytes
#define Q_SNDBUF (64*1024) // in bytes
#define RAW_SNDBUF (64*1024) // in bytes
#endif
#define Q_MAXLEN 1024 // in packets
struct params_s
{
bool debug;
int wsize;
#ifdef __linux__
int qnum;
#elif defined(BSD)
uint16_t port; // divert port
#endif
bool hostcase, hostnospace, domcase;
char hostspell[4];
enum dpi_desync_mode desync_mode,desync_mode2;
bool desync_retrans,desync_skip_nosni,desync_any_proto;
int desync_repeats,desync_split_pos;
uint8_t desync_ttl;
uint8_t desync_tcp_fooling_mode;
uint32_t desync_fwmark; // unused in BSD
char hostfile[256];
strpool *hostlist;
uint8_t fake_http[1460],fake_tls[1460];
size_t fake_http_size,fake_tls_size;
bool droproot;
uid_t uid;
gid_t gid;
};
extern struct params_s params;
#define DLOG(format, ...) {if (params.debug) printf(format, ##__VA_ARGS__);}

132
nfq/protocol.c Normal file
View File

@@ -0,0 +1,132 @@
#define _GNU_SOURCE
#include "protocol.h"
#include "helpers.h"
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <string.h>
const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL };
bool IsHttp(const uint8_t *data, size_t len)
{
const char **method;
size_t method_len;
for (method = http_methods; *method; method++)
{
method_len = strlen(*method);
if (method_len <= len && !memcmp(data, *method, method_len))
return true;
}
return false;
}
bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host)
{
const uint8_t *p, *s, *e=data+len;
p = (uint8_t*)strncasestr((char*)data, "\nHost:", len);
if (!p) return false;
p+=6;
while(p<e && (*p==' ' || *p=='\t')) p++;
s=p;
while(s<e && (*s!='\r' && *s!='\n' && *s!=' ' && *s!='\t')) s++;
if (s>p)
{
size_t slen = s-p;
if (host && len_host)
{
if (slen>=len_host) slen=len_host-1;
for(size_t i=0;i<slen;i++) host[i]=tolower(p[i]);
host[slen]=0;
}
return true;
}
return false;
}
bool IsTLSClientHello(const uint8_t *data, size_t len)
{
return len>=6 && data[0]==0x16 && data[1]==0x03 && data[2]==0x01 && data[5]==0x01 && (ntohs(*(uint16_t*)(data+3))+5)<=len;
}
bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext)
{
// +0
// u8 ContentType: Handshake
// u16 Version: TLS1.0
// u16 Length
// +5
// u8 HandshakeType: ClientHello
// u24 Length
// u16 Version
// c[32] random
// u8 SessionIDLength
// <SessionID>
// u16 CipherSuitesLength
// <CipherSuites>
// u8 CompressionMethodsLength
// <CompressionMethods>
// u16 ExtensionsLength
size_t l,ll;
l = 1+2+2+1+3+2+32;
// SessionIDLength
if (len<(l+1)) return false;
ll = data[6]<<16 | data[7]<<8 | data[8]; // HandshakeProtocol length
if (len<(ll+9)) return false;
l += data[l]+1;
// CipherSuitesLength
if (len<(l+2)) return false;
l += ntohs(*(uint16_t*)(data+l))+2;
// CompressionMethodsLength
if (len<(l+1)) return false;
l += data[l]+1;
// ExtensionsLength
if (len<(l+2)) return false;
data+=l; len-=l;
l=ntohs(*(uint16_t*)data);
data+=2; len-=2;
if (l<len) return false;
uint16_t ntype=htons(type);
while(l>=4)
{
uint16_t etype=*(uint16_t*)data;
size_t elen=ntohs(*(uint16_t*)(data+2));
data+=4; l-=4;
if (l<elen) break;
if (etype==ntype)
{
if (ext && len_ext)
{
*ext = data;
*len_ext = elen;
}
return true;
}
data+=elen; l-=elen;
}
return false;
}
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host)
{
const uint8_t *ext;
size_t elen;
if (!TLSFindExt(data,len,0,&ext,&elen)) return false;
// u16 data+0 - name list length
// u8 data+2 - server name type. 0=host_name
// u16 data+3 - server name length
if (elen<5 || ext[2]!=0) return false;
size_t slen = ntohs(*(uint16_t*)(ext+3));
ext+=5; elen-=5;
if (slen<elen) return false;
if (ext && len_host)
{
if (slen>=len_host) slen=len_host-1;
for(size_t i=0;i<slen;i++) host[i]=tolower(ext[i]);
host[slen]=0;
}
return true;
}

11
nfq/protocol.h Normal file
View File

@@ -0,0 +1,11 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
bool IsHttp(const uint8_t *data, size_t len);
bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host);
bool IsTLSClientHello(const uint8_t *data, size_t len);
bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext);
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host);

171
nfq/sec.c Normal file
View File

@@ -0,0 +1,171 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include "sec.h"
#include <unistd.h>
#include <fcntl.h>
#include <grp.h>
#ifdef __linux__
#include <sys/prctl.h>
bool checkpcap(uint64_t caps)
{
if (!caps) return true; // no special caps reqd
struct __user_cap_header_struct ch = {_LINUX_CAPABILITY_VERSION_3, getpid()};
struct __user_cap_data_struct cd[2];
uint32_t c0 = (uint32_t)caps;
uint32_t c1 = (uint32_t)(caps>>32);
return !capget(&ch,cd) && (cd[0].effective & c0)==c0 && (cd[1].effective & c1)==c1;
}
bool setpcap(uint64_t caps)
{
struct __user_cap_header_struct ch = {_LINUX_CAPABILITY_VERSION_3, getpid()};
struct __user_cap_data_struct cd[2];
cd[0].effective = cd[0].permitted = (uint32_t)caps;
cd[0].inheritable = 0;
cd[1].effective = cd[1].permitted = (uint32_t)(caps>>32);
cd[1].inheritable = 0;
return !capset(&ch,cd);
}
int getmaxcap()
{
int maxcap = CAP_LAST_CAP;
FILE *F = fopen("/proc/sys/kernel/cap_last_cap", "r");
if (F)
{
int n = fscanf(F, "%d", &maxcap);
fclose(F);
}
return maxcap;
}
bool dropcaps()
{
uint64_t caps = (1<<CAP_NET_ADMIN)|(1<<CAP_NET_RAW);
int maxcap = getmaxcap();
if (setpcap(caps|(1<<CAP_SETPCAP)))
{
for (int cap = 0; cap <= maxcap; cap++)
{
if (prctl(PR_CAPBSET_DROP, cap)<0)
{
fprintf(stderr, "could not drop bound cap %d\n", cap);
perror("cap_drop_bound");
}
}
}
// now without CAP_SETPCAP
if (!setpcap(caps))
{
perror("setpcap");
return checkpcap(caps);
}
return true;
}
#endif
bool can_drop_root()
{
#ifdef __linux__
// has some caps
return checkpcap((1<<CAP_SETUID)|(1<<CAP_SETGID)|(1<<CAP_SETPCAP));
#else
// effective root
return !geteuid();
#endif
}
bool droproot(uid_t uid, gid_t gid)
{
#ifdef __linux__
if (prctl(PR_SET_KEEPCAPS, 1L))
{
perror("prctl(PR_SET_KEEPCAPS): ");
return false;
}
#endif
// drop all SGIDs
if (setgroups(0,NULL))
{
perror("setgroups: ");
return false;
}
if (setgid(gid))
{
perror("setgid: ");
return false;
}
if (setuid(uid))
{
perror("setuid: ");
return false;
}
#ifdef __linux__
return dropcaps();
#else
return true;
#endif
}
void print_id()
{
int i,N;
gid_t g[128];
printf("Running as UID=%u GID=",getuid());
N=getgroups(sizeof(g)/sizeof(*g),g);
if (N>0)
{
for(i=0;i<N;i++)
printf(i==(N-1) ? "%u" : "%u,", g[i]);
printf("\n");
}
else
printf("%u\n",getgid());
}
void daemonize()
{
int pid;
pid = fork();
if (pid == -1)
{
perror("fork: ");
exit(2);
}
else if (pid != 0)
exit(0);
if (setsid() == -1)
exit(2);
if (chdir("/") == -1)
exit(2);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
/* redirect fd's 0,1,2 to /dev/null */
open("/dev/null", O_RDWR);
int fd;
/* stdin */
fd = dup(0);
/* stdout */
fd = dup(0);
/* stderror */
}
bool writepid(const char *filename)
{
FILE *F;
if (!(F = fopen(filename, "w")))
return false;
fprintf(F, "%d", getpid());
fclose(F);
return true;
}

20
nfq/sec.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include <sys/types.h>
#include <stdbool.h>
#ifdef __linux__
#include <sys/capability.h>
bool checkpcap(uint64_t caps);
bool setpcap(uint64_t caps);
int getmaxcap();
bool dropcaps();
#endif
bool can_drop_root();
bool droproot(uid_t uid, gid_t gid);
void print_id();
void daemonize();
bool writepid(const char *filename);

76
nfq/strpool.c Normal file
View File

@@ -0,0 +1,76 @@
#define _GNU_SOURCE
#include "strpool.h"
#include <string.h>
#include <stdlib.h>
#undef uthash_nonfatal_oom
#define uthash_nonfatal_oom(elt) ut_oom_recover(elt)
static bool oom = false;
static void ut_oom_recover(strpool *elem)
{
oom = true;
}
// for zero terminated strings
bool StrPoolAddStr(strpool **pp, const char *s)
{
strpool *elem;
if (!(elem = (strpool*)malloc(sizeof(strpool))))
return false;
if (!(elem->str = strdup(s)))
{
free(elem);
return false;
}
oom = false;
HASH_ADD_KEYPTR(hh, *pp, elem->str, strlen(elem->str), elem);
if (oom)
{
free(elem->str);
free(elem);
return false;
}
return true;
}
// for not zero terminated strings
bool StrPoolAddStrLen(strpool **pp, const char *s, size_t slen)
{
strpool *elem;
if (!(elem = (strpool*)malloc(sizeof(strpool))))
return false;
if (!(elem->str = malloc(slen + 1)))
{
free(elem);
return false;
}
memcpy(elem->str, s, slen);
elem->str[slen] = 0;
oom = false;
HASH_ADD_KEYPTR(hh, *pp, elem->str, strlen(elem->str), elem);
if (oom)
{
free(elem->str);
free(elem);
return false;
}
return true;
}
bool StrPoolCheckStr(strpool *p, const char *s)
{
strpool *elem;
HASH_FIND_STR(p, s, elem);
return elem != NULL;
}
void StrPoolDestroy(strpool **p)
{
strpool *elem, *tmp;
HASH_ITER(hh, *p, elem, tmp) {
free(elem->str);
HASH_DEL(*p, elem);
free(elem);
}
*p = NULL;
}

19
nfq/strpool.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include <stdbool.h>
#include <ctype.h>
//#define HASH_BLOOM 20
#define HASH_NONFATAL_OOM 1
#define HASH_FUNCTION HASH_BER
#include "uthash.h"
typedef struct strpool {
char *str; /* key */
UT_hash_handle hh; /* makes this structure hashable */
} strpool;
void StrPoolDestroy(strpool **p);
bool StrPoolAddStr(strpool **pp,const char *s);
bool StrPoolAddStrLen(strpool **pp,const char *s,size_t slen);
bool StrPoolCheckStr(strpool *p,const char *s);

1217
nfq/uthash.h Normal file

File diff suppressed because it is too large Load Diff