mirror of
https://github.com/bol-van/zapret.git
synced 2025-05-24 22:32:58 +03:00
history purge
This commit is contained in:
12
nfq/BSDmakefile
Normal file
12
nfq/BSDmakefile
Normal 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
22
nfq/Makefile
Normal 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
138
nfq/checksum.c
Normal 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
19
nfq/checksum.h
Normal 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
650
nfq/darkmagic.c
Normal 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
76
nfq/darkmagic.h
Normal 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
396
nfq/desync.c
Normal 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
41
nfq/desync.h
Normal 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
82
nfq/gzip.c
Normal 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
8
nfq/gzip.h
Normal 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
120
nfq/helpers.c
Normal 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
18
nfq/helpers.h
Normal 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
112
nfq/hostlist.c
Normal 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
7
nfq/hostlist.h
Normal 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);
|
809
nfq/nfqws.c
Normal file
809
nfq/nfqws.c
Normal 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(¶ms.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, ¶ms);
|
||||
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(¶ms.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(¶ms, 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", ¶ms.uid, ¶ms.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", ¶ms.desync_fwmark)) sscanf(optarg, "%u", ¶ms.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,¶ms.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,¶ms.fake_tls_size))
|
||||
{
|
||||
fprintf(stderr, "could not read %s\n",optarg);
|
||||
exit_clean(1);
|
||||
}
|
||||
break;
|
||||
case 22: /* hostlist */
|
||||
if (!LoadHostList(¶ms.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
6
nfq/nfqws.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum
|
||||
{
|
||||
pass = 0, modify, drop
|
||||
} packet_process_result;
|
54
nfq/params.h
Normal file
54
nfq/params.h
Normal 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
132
nfq/protocol.c
Normal 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
11
nfq/protocol.h
Normal 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
171
nfq/sec.c
Normal 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
20
nfq/sec.h
Normal 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
76
nfq/strpool.c
Normal 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
19
nfq/strpool.h
Normal 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
1217
nfq/uthash.h
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user