history purge

This commit is contained in:
bol-van
2020-01-02 13:10:28 +03:00
commit ae2b6d1e86
117 changed files with 14137 additions and 0 deletions

12
nfq/Makefile Normal file
View File

@@ -0,0 +1,12 @@
CC ?= gcc
CFLAGS += -std=c99 -s -O3
LIBS = -lnetfilter_queue -lnfnetlink -lcap -lz
SRC_FILES = *.c
all: nfqws
nfqws: $(SRC_FILES)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
clean:
rm -f nfqws *.o

312
nfq/darkmagic.c Normal file
View File

@@ -0,0 +1,312 @@
#define _GNU_SOURCE
#include "darkmagic.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
uint16_t tcp_checksum(const void *buff, int len, in_addr_t src_addr, in_addr_t dest_addr)
{
const uint16_t *buf=buff;
uint16_t *ip_src=(uint16_t *)&src_addr, *ip_dst=(uint16_t *)&dest_addr;
uint32_t sum;
int length=len;
// Calculate the sum
sum = 0;
while (len > 1)
{
sum += *buf++;
if (sum & 0x80000000)
sum = (sum & 0xFFFF) + (sum >> 16);
len -= 2;
}
if ( len & 1 )
{
// Add the padding if the packet lenght is odd
uint16_t v=0;
*(uint8_t *)&v = *((uint8_t *)buf);
sum += v;
}
// Add the pseudo-header
sum += *(ip_src++);
sum += *ip_src;
sum += *(ip_dst++);
sum += *ip_dst;
sum += htons(IPPROTO_TCP);
sum += htons(length);
// Add the carries
while (sum >> 16)
sum = (sum & 0xFFFF) + (sum >> 16);
// Return the one's complement of sum
return (uint16_t)(~sum);
}
void tcp_fix_checksum(struct tcphdr *tcp,int len, in_addr_t src_addr, in_addr_t dest_addr)
{
tcp->check = 0;
tcp->check = tcp_checksum(tcp,len,src_addr,dest_addr);
}
uint16_t tcp6_checksum(const void *buff, int len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr)
{
const uint16_t *buf=buff;
const uint16_t *ip_src=(uint16_t *)src_addr, *ip_dst=(uint16_t *)dest_addr;
uint32_t sum;
int length=len;
// Calculate the sum
sum = 0;
while (len > 1)
{
sum += *buf++;
if (sum & 0x80000000)
sum = (sum & 0xFFFF) + (sum >> 16);
len -= 2;
}
if ( len & 1 )
{
// Add the padding if the packet lenght is odd
uint16_t v=0;
*(uint8_t *)&v = *((uint8_t *)buf);
sum += v;
}
// Add the pseudo-header
sum += *(ip_src++);
sum += *(ip_src++);
sum += *(ip_src++);
sum += *(ip_src++);
sum += *(ip_src++);
sum += *(ip_src++);
sum += *(ip_src++);
sum += *ip_src;
sum += *(ip_dst++);
sum += *(ip_dst++);
sum += *(ip_dst++);
sum += *(ip_dst++);
sum += *(ip_dst++);
sum += *(ip_dst++);
sum += *(ip_dst++);
sum += *ip_dst;
sum += htons(IPPROTO_TCP);
sum += htons(length);
// Add the carries
while (sum >> 16)
sum = (sum & 0xFFFF) + (sum >> 16);
// Return the one's complement of sum
return (uint16_t)(~sum);
}
void tcp6_fix_checksum(struct tcphdr *tcp,int len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr)
{
tcp->check = 0;
tcp->check = tcp6_checksum(tcp,len,src_addr,dest_addr);
}
static void fill_tcphdr(struct tcphdr *tcp, uint8_t tcp_flags, uint32_t seq, uint32_t ack_seq, enum tcp_fooling_mode fooling, uint16_t nsport, uint16_t ndport)
{
char *tcpopt = (char*)(tcp+1);
memset(tcp,0,sizeof(*tcp));
tcp->source = nsport;
tcp->dest = ndport;
tcp->seq = seq;
tcp->ack_seq = ack_seq;
tcp->doff = 5;
*((uint8_t*)tcp+13)= tcp_flags;
tcp->window = htons(65535);
if (fooling==TCP_FOOL_MD5SIG)
{
tcp->doff += 5; // +20 bytes
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();
tcpopt[18] = 0; // end
tcpopt[19] = 0;
}
}
static int rawsend_sock=-1;
void rawsend_cleanup()
{
if (rawsend_sock!=-1)
{
close(rawsend_sock);
rawsend_sock=-1;
}
}
static void rawsend_socket(int family,uint32_t fwmark)
{
if (rawsend_sock==-1)
{
int yes=1,pri=6;
rawsend_sock = socket(family, SOCK_RAW, IPPROTO_RAW);
if (rawsend_sock==-1)
perror("rawsend: socket()");
else if (setsockopt(rawsend_sock, SOL_SOCKET, SO_MARK, &fwmark, sizeof(fwmark)) == -1)
{
perror("rawsend: setsockopt(SO_MARK)");
rawsend_cleanup();
}
else if (setsockopt(rawsend_sock, SOL_SOCKET, SO_PRIORITY, &pri, sizeof(pri)) == -1)
{
perror("rawsend: setsockopt(SO_PRIORITY)");
rawsend_cleanup();
}
}
}
bool rawsend(struct sockaddr* dst,uint32_t fwmark,const void *data,size_t len)
{
rawsend_socket(dst->sa_family,fwmark);
if (rawsend_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
int bytes = sendto(rawsend_sock, data, len, 0, (struct sockaddr*)&dst2, salen);
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,
uint8_t ttl,
enum tcp_fooling_mode fooling,
const void *data, uint16_t len,
char *buf, size_t *buflen)
{
uint16_t tcpoptlen = 0;
if (fooling==TCP_FOOL_MD5SIG) tcpoptlen=20;
uint16_t pktlen = sizeof(struct iphdr) + sizeof(struct tcphdr) + tcpoptlen + len;
if (pktlen>*buflen)
{
fprintf(stderr,"prepare_tcp_segment : packet len cannot exceed %zu\n",*buflen);
return false;
}
struct iphdr *ip = (struct iphdr*) buf;
struct tcphdr *tcp = (struct tcphdr*) (ip+1);
ip->frag_off = 0;
ip->version = 4;
ip->ihl = 5;
ip->tot_len = htons(pktlen);
ip->id = 0;
ip->ttl = ttl;
ip->protocol = IPPROTO_TCP;
ip->saddr = src->sin_addr.s_addr;
ip->daddr = dst->sin_addr.s_addr;
fill_tcphdr(tcp,tcp_flags,seq,ack_seq,fooling,src->sin_port,dst->sin_port);
memcpy((char*)tcp+sizeof(struct tcphdr)+tcpoptlen,data,len);
tcp_fix_checksum(tcp,sizeof(struct tcphdr)+tcpoptlen+len,ip->saddr,ip->daddr);
if (fooling==TCP_FOOL_BADSUM) tcp->check^=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,
uint8_t ttl,
enum tcp_fooling_mode fooling,
const void *data, uint16_t len,
char *buf, size_t *buflen)
{
uint16_t tcpoptlen = 0;
if (fooling==TCP_FOOL_MD5SIG) tcpoptlen=20;
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);
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->check^=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,
uint8_t ttl,
enum tcp_fooling_mode fooling,
const void *data, uint16_t len,
char *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,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,ttl,fooling,data,len,buf,buflen) :
false;
}
void extract_endpoints(const struct iphdr *iphdr,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst)
{
if (iphdr)
{
struct sockaddr_in *si = (struct sockaddr_in*)dst;
si->sin_family = AF_INET;
si->sin_port = tcphdr->dest;
si->sin_addr.s_addr = iphdr->daddr;
si = (struct sockaddr_in*)src;
si->sin_family = AF_INET;
si->sin_port = tcphdr->source;
si->sin_addr.s_addr = iphdr->saddr;
}
else if (ip6hdr)
{
struct sockaddr_in6 *si = (struct sockaddr_in6*)dst;
si->sin6_family = AF_INET6;
si->sin6_port = tcphdr->dest;
si->sin6_addr = ip6hdr->ip6_dst;
si->sin6_flowinfo = 0;
si->sin6_scope_id = 0;
si = (struct sockaddr_in6*)src;
si->sin6_family = AF_INET6;
si->sin6_port = tcphdr->source;
si->sin6_addr = ip6hdr->ip6_src;
si->sin6_flowinfo = 0;
si->sin6_scope_id = 0;
}
}

51
nfq/darkmagic.h Normal file
View File

@@ -0,0 +1,51 @@
#pragma once
#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>
uint16_t tcp_checksum(const void *buff, int len, in_addr_t src_addr, in_addr_t dest_addr);
void tcp_fix_checksum(struct tcphdr *tcp,int len, in_addr_t src_addr, in_addr_t dest_addr);
uint16_t tcp6_checksum(const void *buff, int len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr);
void tcp6_fix_checksum(struct tcphdr *tcp,int len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr);
enum tcp_fooling_mode {
TCP_FOOL_NONE=0,
TCP_FOOL_MD5SIG=1,
TCP_FOOL_BADSUM=2
};
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,
uint8_t ttl,
enum tcp_fooling_mode fooling,
const void *data, uint16_t len,
char *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,
uint8_t ttl,
enum tcp_fooling_mode fooling,
const void *data, uint16_t len,
char *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,
uint8_t ttl,
enum tcp_fooling_mode fooling,
const void *data, uint16_t len,
char *buf, size_t *buflen);
void extract_endpoints(const struct iphdr *iphdr,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst);
// auto creates internal socket and uses it for subsequent calls
bool rawsend(struct sockaddr* dst,uint32_t fwmark,const void *data,size_t len);
// cleans up socket autocreated by rawsend
void rawsend_cleanup();

82
nfq/gzip.c Normal file
View File

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

8
nfq/gzip.h Normal file
View File

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

112
nfq/hostlist.c Normal file
View File

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

7
nfq/hostlist.h Normal file
View File

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

1041
nfq/nfqws.c Normal file

File diff suppressed because it is too large Load Diff

128
nfq/sec.c Normal file
View File

@@ -0,0 +1,128 @@
#include <stdio.h>
#include <stdlib.h>
#include "sec.h"
#include <sys/prctl.h>
#include <unistd.h>
#include <fcntl.h>
bool setpcap(cap_value_t *caps, int ncaps)
{
cap_t capabilities;
if (!(capabilities = cap_init()))
return false;
if (ncaps && (cap_set_flag(capabilities, CAP_PERMITTED, ncaps, caps, CAP_SET) ||
cap_set_flag(capabilities, CAP_EFFECTIVE, ncaps, caps, CAP_SET)))
{
cap_free(capabilities);
return false;
}
if (cap_set_proc(capabilities))
{
cap_free(capabilities);
return false;
}
cap_free(capabilities);
return true;
}
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()
{
// must have CAP_SETPCAP at the end. its required to clear bounding set
cap_value_t cap_values[] = { CAP_NET_ADMIN,CAP_NET_RAW,CAP_SETPCAP };
int capct = sizeof(cap_values) / sizeof(*cap_values);
int maxcap = getmaxcap();
if (setpcap(cap_values, capct))
{
for (int cap = 0; cap <= maxcap; cap++)
{
if (cap_drop_bound(cap))
{
fprintf(stderr, "could not drop cap %d\n", cap);
perror("cap_drop_bound");
}
}
}
// now without CAP_SETPCAP
if (!setpcap(cap_values, capct - 1))
{
perror("setpcap");
return false;
}
return true;
}
bool droproot(uid_t uid, gid_t gid)
{
if (uid || gid)
{
if (prctl(PR_SET_KEEPCAPS, 1L))
{
perror("prctl(PR_SET_KEEPCAPS): ");
return false;
}
if (setgid(gid))
{
perror("setgid: ");
return false;
}
if (setuid(uid))
{
perror("setuid: ");
return false;
}
}
return dropcaps();
}
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;
}

12
nfq/sec.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#include <sys/capability.h>
#include <sys/types.h>
#include <stdbool.h>
bool setpcap(cap_value_t *caps, int ncaps);
int getmaxcap();
bool dropcaps();
bool droproot(uid_t uid, gid_t gid);
void daemonize();
bool writepid(const char *filename);

76
nfq/strpool.c Normal file
View File

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

19
nfq/strpool.h Normal file
View File

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

1217
nfq/uthash.h Normal file

File diff suppressed because it is too large Load Diff