This commit is contained in:
zhuzhenjun
2023-09-16 10:43:06 +08:00
parent e9b190b069
commit 91e6b79afc
21 changed files with 8589 additions and 142 deletions

View File

@@ -7,8 +7,8 @@ AC_CONFIG_MACRO_DIR([m4])
AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [enable debug info])], [enable_debug=$enableval], [enable_debug=no])
AS_IF([test "x$enable_debug" = xyes],
[CFLAGS="-ggdb3"],
[CFLAGS="-O2"])
[CFLAGS="-ggdb3 -O0"],
[CFLAGS="-g -O2"])
AC_PROG_CC

View File

@@ -1,11 +1,13 @@
bin_PROGRAMS = osfp_match
bin_PROGRAMS = osfp_example
osfp_match_SOURCES = \
osfp_match.c
osfp_example_SOURCES = \
osfp_example.c
osfp_match_LDADD = \
osfp_example_LDADD = \
../src/.libs/libosfp.la
osfp_match_LDFLAGS = \
osfp_example_LDFLAGS = \
-lpcap
osfp_example_CFLAGS = \
-I../src

655
example/osfp_example.c Normal file
View File

@@ -0,0 +1,655 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include <sys/socket.h>
#include <pcap.h>
#include "libosfp.h"
#include "libosfp_fingerprint.h"
#define DEFAULT_FP_FILE "./fp.json"
#define ETHERNET_HEADER_LEN 14
#define VLAN_HEADER_LEN 4
#define IPV4_HEADER_LEN 20
#define TCP_HEADER_LEN 20
#define IPV6_HEADER_LEN 40
#define VLAN_MAX_LAYER 2
/* Port is just a uint16_t */
typedef uint16_t Port;
#define SET_PORT(v, p) ((p) = (v))
#define COPY_PORT(a,b) ((b) = (a))
/* Address */
typedef struct Address_ {
char family;
union {
uint32_t address_un_data32[4]; /* type-specific field */
uint16_t address_un_data16[8]; /* type-specific field */
uint8_t address_un_data8[16]; /* type-specific field */
struct in6_addr address_un_in6;
} address;
} Address;
#define addr_data32 address.address_un_data32
#define addr_data16 address.address_un_data16
#define addr_data8 address.address_un_data8
#define addr_in6addr address.address_un_in6
#define COPY_ADDRESS(a, b) do { \
(b)->family = (a)->family; \
(b)->addr_data32[0] = (a)->addr_data32[0]; \
(b)->addr_data32[1] = (a)->addr_data32[1]; \
(b)->addr_data32[2] = (a)->addr_data32[2]; \
(b)->addr_data32[3] = (a)->addr_data32[3]; \
} while (0)
/* Set the IPv4 addresses into the Addrs of the Packet.
* Make sure p->ip4h is initialized and validated.
*
* We set the rest of the struct to 0 so we can
* prevent using memset. */
#define SET_IPV4_SRC_ADDR(p, a) do { \
(a)->family = AF_INET; \
(a)->addr_data32[0] = (uint32_t)(p)->iph->saddr; \
(a)->addr_data32[1] = 0; \
(a)->addr_data32[2] = 0; \
(a)->addr_data32[3] = 0; \
} while (0)
#define SET_IPV4_DST_ADDR(p, a) do { \
(a)->family = AF_INET; \
(a)->addr_data32[0] = (uint32_t)(p)->iph->daddr; \
(a)->addr_data32[1] = 0; \
(a)->addr_data32[2] = 0; \
(a)->addr_data32[3] = 0; \
} while (0)
/* Set the IPv6 addresses into the Addrs of the Packet.
* Make sure p->ip6h is initialized and validated. */
#define SET_IPV6_SRC_ADDR(p, a) do { \
(a)->family = AF_INET6; \
(a)->addr_data32[0] = (p)->ip6h->saddr.s6_addr32[0]; \
(a)->addr_data32[1] = (p)->ip6h->saddr.s6_addr32[1]; \
(a)->addr_data32[2] = (p)->ip6h->saddr.s6_addr32[2]; \
(a)->addr_data32[3] = (p)->ip6h->saddr.s6_addr32[3]; \
} while (0)
#define SET_IPV6_DST_ADDR(p, a) do { \
(a)->family = AF_INET6; \
(a)->addr_data32[0] = (p)->ip6h->daddr.s6_addr32[0]; \
(a)->addr_data32[1] = (p)->ip6h->daddr.s6_addr32[1]; \
(a)->addr_data32[2] = (p)->ip6h->daddr.s6_addr32[2]; \
(a)->addr_data32[3] = (p)->ip6h->daddr.s6_addr32[3]; \
} while (0)
#define TCP_GET_RAW_SRC_PORT(tcph) ntohs((tcph)->source)
#define TCP_GET_RAW_DST_PORT(tcph) ntohs((tcph)->dest)
#define TCP_GET_SRC_PORT(p) TCP_GET_RAW_SRC_PORT((p)->tcph)
#define TCP_GET_DST_PORT(p) TCP_GET_RAW_DST_PORT((p)->tcph)
/* Set the TCP ports into the Ports of the Packet.
* Make sure p->tcph is initialized and validated. */
#define SET_TCP_SRC_PORT(pkt, prt) do { \
SET_PORT(TCP_GET_SRC_PORT((pkt)), *(prt)); \
} while (0)
#define SET_TCP_DST_PORT(pkt, prt) do { \
SET_PORT(TCP_GET_DST_PORT((pkt)), *(prt)); \
} while (0)
#define GET_IPV4_SRC_ADDR_U32(p) ((p)->src.addr_data32[0])
#define GET_IPV4_DST_ADDR_U32(p) ((p)->dst.addr_data32[0])
#define GET_IPV4_SRC_ADDR_PTR(p) ((p)->src.addr_data32)
#define GET_IPV4_DST_ADDR_PTR(p) ((p)->dst.addr_data32)
#define GET_IPV6_SRC_IN6ADDR(p) ((p)->src.addr_in6addr)
#define GET_IPV6_DST_IN6ADDR(p) ((p)->dst.addr_in6addr)
#define GET_IPV6_SRC_ADDR(p) ((p)->src.addr_data32)
#define GET_IPV6_DST_ADDR(p) ((p)->dst.addr_data32)
#define GET_TCP_SRC_PORT(p) ((p)->sp)
#define GET_TCP_DST_PORT(p) ((p)->dp)
typedef struct Packet_ {
struct ethhdr *ethh;
struct iphdr *iph;
struct ipv6hdr *ip6h;
struct tcphdr *tcph;
Address src;
Address dst;
union {
Port sp;
// icmp type and code of this packet
struct {
uint8_t type;
uint8_t code;
} icmp_s;
};
union {
Port dp;
// icmp type and code of the expected counterpart (for flows)
struct {
uint8_t type;
uint8_t code;
} icmp_d;
};
int vlan_layer;
} Packet;
unsigned char *fp_file;
unsigned char *if_name;
unsigned char *pcap_file_name;
unsigned char *bpf_string;
pcap_t *pcap_handle;
int processed_packet;
int link_type;
void usage(void) {
fprintf(stderr,
"Usage: osfp_match [ ...options... ] [ 'filter rule' ]\n"
"\n"
"Network interface options:\n"
"\n"
" -i iface - listen on the specified network interface\n"
" -r file - read offline pcap data from a given file\n"
" -f file - read fingerprint database from 'file' (%s)\n",
DEFAULT_FP_FILE);
exit(1);
}
typedef struct EthernetHdr_ {
uint8_t eth_dst[6];
uint8_t eth_src[6];
uint16_t eth_type;
} __attribute__((__packed__)) EthernetHdr;
int packet_decode_tcp(Packet *p, const unsigned char *data, unsigned int len)
{
int ret = -1;
int tcp_hdr_len;
struct tcphdr *tcph;
if (len < TCP_HEADER_LEN) {
goto exit;
}
tcph = (struct tcphdr *)data;
tcp_hdr_len = tcph->doff << 2;
if (len < tcp_hdr_len) {
goto exit;
}
p->tcph = tcph;
SET_TCP_SRC_PORT(p,&p->sp);
SET_TCP_DST_PORT(p,&p->dp);
ret = 0;
exit:
return ret;
}
int packet_decode_ipv4(Packet *p, const unsigned char *data, unsigned int len)
{
int ret = -1;
int ip_total_len, ip_hdr_len;
struct iphdr *iph;
if (len < IPV4_HEADER_LEN) {
goto exit;
}
iph = (struct iphdr *)data;
ip_total_len = ntohs(iph->tot_len);
ip_hdr_len = iph->ihl << 2;
if (ip_hdr_len < IPV4_HEADER_LEN) {
goto exit;
}
if (ip_total_len < ip_hdr_len) {
goto exit;
}
if (len < ip_total_len) {
goto exit;
}
p->iph = iph;
/* set the address struct */
SET_IPV4_SRC_ADDR(p,&p->src);
SET_IPV4_DST_ADDR(p,&p->dst);
switch (p->iph->protocol) {
case IPPROTO_TCP:
packet_decode_tcp(p, data + ip_hdr_len, ip_total_len - ip_hdr_len);
break;
default:
goto exit;
}
ret = 0;
exit:
return ret;
}
int packet_decode_ipv6(Packet *p, const unsigned char *data, unsigned int len)
{
int ret = -1;
unsigned short ip6_payload_len;
unsigned char ip6_nexthdr;
struct ipv6hdr *ip6h;
if (len < IPV6_HEADER_LEN) {
goto exit;
}
ip6h = (struct ipv6hdr *)data;
ip6_payload_len = ntohs(ip6h->payload_len);
ip6_nexthdr = ip6h->nexthdr;
if (len < IPV6_HEADER_LEN + ip6_payload_len) {
goto exit;
}
p->ip6h = ip6h;
SET_IPV6_SRC_ADDR(p,&p->src);
SET_IPV6_DST_ADDR(p,&p->dst);
switch (ip6_nexthdr) {
case IPPROTO_TCP:
packet_decode_tcp(p, data + IPV6_HEADER_LEN, ip6_payload_len);
break;
default:
goto exit;
}
ret = 0;
exit:
return ret;
}
int pakcet_decode_network_layer(Packet *p, const unsigned char *data, unsigned int len, unsigned short proto)
{
switch (proto) {
case ETH_P_IP: {
packet_decode_ipv4(p, data, len);
break;
}
case ETH_P_IPV6: {
packet_decode_ipv6(p, data, len);
break;
}
case ETH_P_8021Q:
if (p->vlan_layer > VLAN_MAX_LAYER || len < VLAN_HEADER_LEN) {
return -1;
}
unsigned short vlan_proto = ntohs(*(unsigned short*)(data + 2));
pakcet_decode_network_layer(p, data + VLAN_HEADER_LEN, len - VLAN_HEADER_LEN, vlan_proto);
break;
default:
printf("L3 proto type: %02x not yet supported\n", proto);
break;
}
return 0;
}
int packet_decode_ethernet(Packet *p, const unsigned char *data, unsigned int len)
{
int ret = -1;
unsigned short proto;
struct ethhdr *ethh;
if (len < ETHERNET_HEADER_LEN) {
goto exit;
}
ethh = (struct ethhdr *)data;
proto = ntohs(ethh->h_proto);
p->ethh = ethh;
ret = pakcet_decode_network_layer(p, data + ETHERNET_HEADER_LEN, len - ETHERNET_HEADER_LEN, proto);
if (ret != 0) {
goto exit;
}
return 0;
exit:
return ret;
}
void packet_decode_link_layer(Packet *p, const unsigned char *data, unsigned int len, int datalink)
{
switch (datalink) {
case DLT_EN10MB:
packet_decode_ethernet(p, data, len);
break;
default:
printf("Datalink type: %02x not yet supported\n", link_type);
pcap_breakloop(pcap_handle);
break;
}
}
void packet_decode(Packet *p, const unsigned char *data, unsigned int len, int datalink)
{
packet_decode_link_layer(p, data, len, datalink);
}
size_t strlcat(char *dst, const char *src, size_t siz)
{
register char *d = dst;
register const char *s = src;
register size_t n = siz;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return(dlen + strlen(s));
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return(dlen + (s - src)); /* count does not include NUL */
}
static const char *PrintInetIPv6(const void *src, char *dst, socklen_t size)
{
int i;
char s_part[6];
uint16_t x[8];
memcpy(&x, src, 16);
/* current IPv6 format is fixed size */
if (size < 8 * 5) {
printf("Too small buffer to write IPv6 address");
return NULL;
}
memset(dst, 0, size);
for(i = 0; i < 8; i++) {
snprintf(s_part, sizeof(s_part), "%04x:", htons(x[i]));
strlcat(dst, s_part, size);
}
/* suppress last ':' */
dst[strlen(dst) - 1] = 0;
return dst;
}
const char *PrintInet(int af, const void *src, char *dst, socklen_t size)
{
switch (af) {
case AF_INET:
snprintf(dst, size, "%u.%u.%u.%u",
((unsigned char *)src)[0],
((unsigned char *)src)[1],
((unsigned char *)src)[2],
((unsigned char *)src)[3]);
return dst;
case AF_INET6:
/* Format IPv6 without deleting zeroes */
return PrintInetIPv6(src, dst, size);
default:
printf("Unsupported protocol: %d", af);
}
return NULL;
}
void example_header_match(libosfp_context_t *libosfp_context, Packet *p)
{
// tcp/ip header match
int ret;
char str_buf[1024];
unsigned char *iph = (unsigned char *)(p->iph != NULL ? (void *)p->iph : (void *)p->ip6h);
unsigned char *tcph = (unsigned char *)p->tcph;
libosfp_result_t result;
printf("Example header match: --------------------------\n");
ret = libosfp_header_match(libosfp_context, iph, tcph, &result);
if (ret != 0) {
printf("libosfp header match failed, erro: %s\n", "?");
goto exit;
}
char srcip[46] = {0}, dstip[46] = {0};
Port sp, dp;
if (p->iph) {
PrintInet(AF_INET, (const void *)&(p->src.addr_data32[0]), srcip, sizeof(srcip));
PrintInet(AF_INET, (const void *)&(p->dst.addr_data32[0]), dstip, sizeof(dstip));
} else if (p->ip6h) {
PrintInet(AF_INET6, (const void *)&(p->src.address), srcip, sizeof(srcip));
PrintInet(AF_INET6, (const void *)&(p->dst.address), dstip, sizeof(dstip));
}
sp = p->sp;
dp = p->dp;
printf("Connection info: %s:%d -> %s:%d\n", srcip, sp, dstip, dp);
printf("Most likely os class: %s\n", libosfp_result_likely_os_class_name_get(&result));
printf("Likely score: %u/100\n", libosfp_result_likely_os_class_score_get(&result));
libosfp_result_to_buf(&result, str_buf, sizeof(str_buf));
fprintf(stdout, "%s\n", str_buf);
exit:
return;
}
void example_fingerprint_match(libosfp_context_t *libosfp_context, Packet *p)
{
// fingerprint match
int ret;
char str_buf[1024];
unsigned char *iph = (unsigned char *)(p->iph != NULL ? (void *)p->iph : (void *)p->ip6h);
unsigned char *tcph = (unsigned char *)p->tcph;
libosfp_result_t result;
libosfp_fingerprint_t fp;
printf("Example fingerprint match: --------------------------\n");
ret = libosfp_fingerprinting(iph, tcph, &fp);
if (ret != 0) {
printf("libosfp fingerprinting failed\n");
goto exit;
}
libosfp_fingerprint_to_json_buf(&fp, str_buf, sizeof(str_buf));
fprintf(stdout, "%s\n", str_buf);
ret = libosfp_score_db_score(libosfp_context, &fp, &result);
if (ret != 0) {
printf("libosfp fingerprint score failed, error: %d\n", ret);
goto exit;
}
printf("Connection info: %s\n", "");
printf("Most likely os class: %s\n", libosfp_result_likely_os_class_name_get(&result));
printf("Likely score: %u/100\n", libosfp_result_likely_os_class_score_get(&result));
libosfp_result_to_buf(&result, str_buf, sizeof(str_buf));
fprintf(stdout, "%s\n", str_buf);
exit:
return;
}
void process_packet(char *user, struct pcap_pkthdr *h, u_char *pkt)
{
int ret;
libosfp_context_t *libosfp_context = (libosfp_context_t *)user;
Packet packet = {0}, *p = &packet;
// decode packet
packet_decode(p, pkt, h->len, link_type);
if (p->tcph == NULL || (p->iph == NULL && p->ip6h == NULL)) {
goto exit;
}
// only for tcp syn request packet
if (!p->tcph->syn || p->tcph->ack) {
goto exit;
}
example_header_match(libosfp_context, p);
example_fingerprint_match(libosfp_context, p);
printf("--------------------------- processed packet count %d\n", ++processed_packet);
exit:
return;
}
int main(int argc, char *argv[])
{
int r;
while ((r = getopt(argc, argv, "+f:i:r:")) != -1) {
switch(r) {
case 'f':
if (fp_file) {
printf("Multiple -f options not supported.\n");
exit(1);
}
fp_file = (unsigned char*)optarg;
break;
case 'i':
if (if_name) {
printf("Multiple -i options not supported.\n");
exit(1);
}
if_name = (unsigned char*)optarg;
break;
case 'r':
if (pcap_file_name) {
printf("Multiple -r options not supported.\n");
exit(1);
}
pcap_file_name = (unsigned char*)optarg;
break;
default:
usage();
break;
}
}
if (optind < argc) {
if (optind + 1 == argc) {
bpf_string = argv[optind];
} else {
printf("Filter rule must be a single parameter (use quotes).\n");
exit(1);
}
}
// prepare pcap handle
char pcap_err[PCAP_ERRBUF_SIZE];
if (pcap_file_name) {
if (access((char*)pcap_file_name, R_OK)) {
printf("No such file: %s\n", pcap_file_name);
exit(1);
}
pcap_handle = pcap_open_offline((char*)pcap_file_name, pcap_err);
if (pcap_handle == NULL ) {
printf("Pcap file open failed. File name: %s, Err: %s\n", pcap_file_name, pcap_err);
exit(1);
}
} else if (if_name) {
pcap_handle = pcap_open_live((char*)if_name, 65535, 1, 5, pcap_err);
if (pcap_handle == NULL) {
printf("Pcap live open failed. Interface name: %s, Err: %s\n", if_name, pcap_err);
exit(1);
}
} else {
usage();
}
// setup bpf filter
if (bpf_string) {
struct bpf_program bpf_filter;
if (pcap_compile(pcap_handle, &bpf_filter, bpf_string, 1, 0) < 0) {
printf("bpf compilation error %s", pcap_geterr(pcap_handle));
exit(1);
}
if (pcap_setfilter(pcap_handle, &bpf_filter) < 0) {
printf("could not set bpf filter %s", pcap_geterr(pcap_handle));
pcap_freecode(&bpf_filter);
exit(1);
}
pcap_freecode(&bpf_filter);
}
// get link type
link_type = pcap_datalink(pcap_handle);
// create libosfp context
if (fp_file == NULL) {
fp_file = DEFAULT_FP_FILE;
}
libosfp_context_t *libosfp_context = libosfp_context_create(fp_file);
if (libosfp_context == NULL) {
printf("could not create libosfp context. fingerprints file: %s\n", fp_file);
exit(1);
}
// setup libosfp context
r = libosfp_context_setup(libosfp_context);
if (r != LIBOSFP_NOERR) {
printf("could not setup libosfp context. error: %d\n", LIBOSFP_NOERR);
exit(1);
}
// loop
while (1) {
int r = pcap_dispatch(pcap_handle, 0, (pcap_handler)process_packet, (void*)libosfp_context);
if (r < 0) {
printf("error code: %d, error: %s\n", r, pcap_geterr(pcap_handle));
break;
}
}
// create libosfp context
libosfp_context_destroy(libosfp_context);
return 0;
}

View File

@@ -1,129 +0,0 @@
#include <stdio.h>
#include <time.h>
#include <pcap.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <unistd.h>
#include <stdlib.h>
unsigned char *fp_file;
unsigned char *if_name;
unsigned char *pcap_file_name;
unsigned char *bpf_string;
int processed_packet;
void usage(void) {
fprintf(stderr,
"Usage: osfp_match [ ...options... ] [ 'filter rule' ]\n"
"\n"
"Network interface options:\n"
"\n"
" -i iface - listen on the specified network interface\n"
" -r file - read offline pcap data from a given file\n"
" -f file - read fingerprint database from 'file' (%s)\n"
);
exit(1);
}
void process_packet(char *user, struct pcap_pkthdr *h, u_char *pkt)
{
printf("packet count %d\n", ++processed_packet);
}
int main(int argc, char *argv[])
{
int r;
while ((r = getopt(argc, argv, "+f:i:r")) != -1) {
switch(r) {
case 'f':
if (fp_file) {
printf("Multiple -f options not supported.\n");
exit(1);
}
fp_file = (unsigned char*)optarg;
break;
case 'i':
if (if_name) {
printf("Multiple -i options not supported.\n");
exit(1);
}
if_name = (unsigned char*)optarg;
break;
case 'r':
if (pcap_file_name) {
printf("Multiple -r options not supported.\n");
exit(1);
}
pcap_file_name = (unsigned char*)optarg;
break;
default:
usage();
break;
}
}
if (optind < argc) {
if (optind + 1 == argc) {
bpf_string = argv[optind];
} else {
printf("Filter rule must be a single parameter (use quotes).\n");
exit(1);
}
}
// prepare pcap handle
char pcap_err[PCAP_ERRBUF_SIZE];
pcap_t *pcap_handle;
if (pcap_file_name) {
if (access((char*)pcap_file_name, R_OK)) {
printf("No such file: %s\n", pcap_file_name);
exit(1);
}
pcap_handle = pcap_open_offline((char*)pcap_file_name, pcap_err);
if (pcap_handle == NULL ) {
printf("Pcap file open failed. File name: %s, Err: %s\n", pcap_file_name, pcap_err);
exit(1);
}
} else if (if_name) {
pcap_handle = pcap_open_live((char*)if_name, 65535, 1, 5, pcap_err);
if (pcap_handle == NULL) {
printf("Pcap live open failed. Interface name: %s, Err: %s\n", if_name, pcap_err);
exit(1);
}
} else {
usage();
}
// setup bpf filter
if (bpf_string) {
struct bpf_program bpf_filter;
if (pcap_compile(pcap_handle, &bpf_filter, bpf_string, 1, 0) < 0) {
printf("bpf compilation error %s", pcap_geterr(pcap_handle));
exit(1);
}
if (pcap_setfilter(pcap_handle, &bpf_filter) < 0) {
printf("could not set bpf filter %s", pcap_geterr(pcap_handle));
pcap_freecode(&bpf_filter);
exit(1);
}
pcap_freecode(&bpf_filter);
}
// loop
while (1) {
int r = pcap_dispatch(pcap_handle, 0, (pcap_handler)process_packet, NULL);
if (r < 0) {
printf("error code: %d, error: %s\n", r, pcap_geterr(pcap_handle));
break;
}
}
return 0;
}

View File

@@ -1,7 +1,19 @@
lib_LTLIBRARIES = libosfp.la
libosfp_la_SOURCES = \
utarray.h \
uthash.h \
utlist.h \
utringbuffer.h \
utstack.h \
utstring.h \
cJSON.h \
cJSON.c \
libosfp.h \
libosfp.c
libosfp.c \
libosfp_fingerprint.h \
libosfp_fingerprint.c \
libosfp_score_db.h \
libosfp_score_db.c
pkginclude_HEADERS = libosfp.h
pkginclude_HEADERS = libosfp.h libosfp_fingerprint.h

3119
src/cJSON.c Normal file

File diff suppressed because it is too large Load Diff

300
src/cJSON.h Normal file
View File

@@ -0,0 +1,300 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#ifdef __cplusplus
extern "C"
{
#endif
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
#define __WINDOWS__
#endif
#ifdef __WINDOWS__
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
For *nix builds that support visibility attribute, you can define similar behavior by
setting default visibility to hidden by adding
-fvisibility=hidden (for gcc)
or
-xldscope=hidden (for sun cc)
to CFLAGS
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
*/
#define CJSON_CDECL __cdecl
#define CJSON_STDCALL __stdcall
/* export symbols by default, this is necessary for copy pasting the C and header file */
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_EXPORT_SYMBOLS
#endif
#if defined(CJSON_HIDE_SYMBOLS)
#define CJSON_PUBLIC(type) type CJSON_STDCALL
#elif defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
#elif defined(CJSON_IMPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
#endif
#else /* !__WINDOWS__ */
#define CJSON_CDECL
#define CJSON_STDCALL
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
#else
#define CJSON_PUBLIC(type) type
#endif
#endif
/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 16
#include <stddef.h>
/* cJSON Types: */
#define cJSON_Invalid (0)
#define cJSON_False (1 << 0)
#define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
/* The cJSON structure: */
typedef struct cJSON
{
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *next;
struct cJSON *prev;
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
struct cJSON *child;
/* The type of the item, as above. */
int type;
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint;
/* The item's number, if type==cJSON_Number */
double valuedouble;
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
char *string;
} cJSON;
typedef struct cJSON_Hooks
{
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
void *(CJSON_CDECL *malloc_fn)(size_t sz);
void (CJSON_CDECL *free_fn)(void *ptr);
} cJSON_Hooks;
typedef int cJSON_bool;
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_NESTING_LIMIT
#define CJSON_NESTING_LIMIT 1000
#endif
/* returns the version of cJSON as a string */
CJSON_PUBLIC(const char*) cJSON_Version(void);
/* Supply malloc, realloc and free functions to cJSON */
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
/* Render a cJSON entity to text for transfer/storage. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. */
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
/* Delete a cJSON entity and all subentities. */
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
/* Returns the number of items in an array (or object). */
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
/* Get item "string" from object. Case insensitive. */
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
/* Check item type and return its value */
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
/* These functions check the type of an item */
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
/* These calls create a cJSON item of the appropriate type. */
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
/* raw json */
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
/* Create a string where valuestring references a string so
* it will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
/* Create an object/array that only references it's elements so
* they will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
/* These utilities create an Array of count items.
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
/* Append item to the specified array/object. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
* writing to `item->string` */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
/* Remove/Detach items from Arrays/Objects. */
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
/* Update array items. */
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
* need to be released. With recurse!=0, it will duplicate any children connected to the item.
* The item->next and ->prev pointers are always zero on return from Duplicate. */
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
* The input pointer json cannot point to a read-only address area, such as a string constant,
* but should point to a readable and writable address area. */
CJSON_PUBLIC(void) cJSON_Minify(char *json);
/* Helper functions for creating and adding items to an object at the same time.
* They return the added item or NULL on failure. */
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
/* helper for the cJSON_SetNumberValue macro */
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
#define cJSON_SetBoolValue(object, boolValue) ( \
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
cJSON_Invalid\
)
/* Macro for iterating over an array or object */
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
CJSON_PUBLIC(void) cJSON_free(void *object);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,6 +1,207 @@
#include "libosfp.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <malloc.h>
int libosfp_init(void)
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include "libosfp.h"
#include "libosfp_fingerprint.h"
#include "libosfp_score_db.h"
#include "libosfp_log.h"
#define LIBOSFP_OS_CLASS_NAME_WINDOWS "Windows"
#define LIBOSFP_OS_CLASS_NAME_LINUX "Linux"
#define LIBOSFP_OS_CLASS_NAME_MAC_OS "Mac OS"
#define LIBOSFP_OS_CLASS_NAME_IOS "iOS"
#define LIBOSFP_OS_CLASS_NAME_ANDROID "Android"
#define LIBOSFP_WRITE_STRING_TO_BUF(ret, buf, size, off, ...) do { \
ret = snprintf((char *)buf + off, \
size - off, \
__VA_ARGS__); \
if (ret >= 0) { \
if ( (off + ret) >= size) { \
off = size - 1; \
} else { \
off += ret; \
} \
} \
} while (0)
const char *os_class_name[LIBOSFP_OS_CLASS_MAX] = {
LIBOSFP_OS_CLASS_NAME_WINDOWS,
LIBOSFP_OS_CLASS_NAME_LINUX,
LIBOSFP_OS_CLASS_NAME_MAC_OS,
LIBOSFP_OS_CLASS_NAME_IOS,
LIBOSFP_OS_CLASS_NAME_ANDROID
};
libosfp_os_class_id_t libosfp_os_class_name_to_id(char *name)
{
return 0;
libosfp_os_class_id_t os_class;
if (0 == strncmp(name, LIBOSFP_OS_CLASS_NAME_WINDOWS, strlen(LIBOSFP_OS_CLASS_NAME_WINDOWS))) {
os_class = LIBOSFP_OS_CLASS_WINDOWS;
} else if (0 == strncmp(name, LIBOSFP_OS_CLASS_NAME_LINUX, strlen(LIBOSFP_OS_CLASS_NAME_LINUX))) {
os_class = LIBOSFP_OS_CLASS_Linux;
} else if (0 == strncmp(name, LIBOSFP_OS_CLASS_NAME_MAC_OS, strlen(LIBOSFP_OS_CLASS_NAME_MAC_OS))) {
os_class = LIBOSFP_OS_CLASS_MAC_OS;
} else if (0 == strncmp(name, LIBOSFP_OS_CLASS_NAME_IOS, strlen(LIBOSFP_OS_CLASS_NAME_IOS))) {
os_class = LIBOSFP_OS_CLASS_IOS;
} else if (0 == strncmp(name, LIBOSFP_OS_CLASS_NAME_ANDROID, strlen(LIBOSFP_OS_CLASS_NAME_ANDROID))) {
os_class = LIBOSFP_OS_CLASS_ANDROID;
} else {
os_class = LIBOSFP_OS_CLASS_MAX;
}
return os_class;
}
const char *libosfp_os_class_id_to_name(libosfp_os_class_id_t os_class)
{
if (os_class < 0 || os_class >= LIBOSFP_OS_CLASS_MAX) {
return NULL;
}
return os_class_name[os_class];
}
const char *libosfp_result_likely_os_class_name_get(libosfp_result_t *result)
{
libosfp_os_class_id_t os_class;
if (result == NULL) {
return NULL;
}
os_class = result->likely_os_class;
if (os_class < 0 || os_class >= LIBOSFP_OS_CLASS_MAX) {
return NULL;
}
return os_class_name[os_class];
}
unsigned int libosfp_result_likely_os_class_score_get(libosfp_result_t *result)
{
if (result == NULL) {
return 0;
}
return result->likely_score;
}
int libosfp_result_to_buf(libosfp_result_t *result, char *strbuf, unsigned int buf_len)
{
int ret, offset = 0, i;
if (result == NULL || strbuf == NULL || buf_len == 0) {
return 0;
}
LIBOSFP_WRITE_STRING_TO_BUF(ret, strbuf, buf_len, offset,
"Most likely os class: %s\nLikely score: %u/100\n",
os_class_name[result->likely_os_class], result->likely_score);
LIBOSFP_WRITE_STRING_TO_BUF(ret, strbuf, buf_len, offset,"Details:\n");
for (i = 0; i < LIBOSFP_OS_CLASS_MAX; i++) {
LIBOSFP_WRITE_STRING_TO_BUF(ret, strbuf, buf_len, offset,"%s score: %u\n",
os_class_name[i], result->score.os_class_score[i]);
}
exit:
return offset;
}
libosfp_error_code_t libosfp_header_match(libosfp_context_t *libosfp_context, unsigned char *ip_hdr, unsigned char *tcp_hdr, libosfp_result_t *result)
{
int ret = LIBOSFP_EINVAL;
libosfp_fingerprint_t fp = {0};
if (libosfp_context == NULL || ip_hdr == NULL || tcp_hdr == NULL || result == NULL) {
goto exit;
}
ret = libosfp_fingerprinting(ip_hdr, tcp_hdr, &fp);
if (ret != 0) {
goto exit;
}
ret = libosfp_score_db_score(libosfp_context, &fp, result);
if (ret != 0) {
goto exit;
}
return LIBOSFP_NOERR;
exit:
return ret;
}
libosfp_error_code_t libosfp_context_setup(libosfp_context_t *libosfp_context)
{
int ret = LIBOSFP_EINVAL;
if (libosfp_context == NULL) {
goto exit;
}
ret = libosfp_score_db_load((libosfp_score_db_t *)libosfp_context->score_db, libosfp_context->fp_file);
if (ret != LIBOSFP_NOERR) {
goto exit;
}
return LIBOSFP_NOERR;
exit:
return ret;
}
libosfp_context_t *libosfp_context_create(char *fp_file)
{
libosfp_context_t *libosfp_context = NULL;
if (fp_file == NULL || 0 != access(fp_file, R_OK)) {
goto exit;
}
libosfp_context = malloc(sizeof(libosfp_context_t));
if (libosfp_context == NULL) {
goto exit;
}
libosfp_context->fp_file = strdup((const char*)fp_file);
if (libosfp_context->fp_file == NULL) {
goto exit;
}
libosfp_context->score_db = (void *)libosfp_score_db_create();
if (libosfp_context->score_db == NULL) {
goto exit;
}
return libosfp_context;
exit:
if (libosfp_context) {
libosfp_context_destroy(libosfp_context);
}
return NULL;
}
void libosfp_context_destroy(libosfp_context_t *libosfp_context)
{
if (libosfp_context) {
if (libosfp_context->fp_file) {
free(libosfp_context->fp_file);
}
if (libosfp_context->score_db) {
libosfp_score_db_destroy(libosfp_context->score_db);
}
free(libosfp_context);
}
}

View File

@@ -1,6 +1,50 @@
#ifndef __LIBOSFP_H__
#define __LIBOSFP_H__
int libosfp_init(void);
typedef enum libosfp_error_code {
LIBOSFP_NOERR,
LIBOSFP_EINVAL,
LIBOSFP_ERR_READ_FILE,
LIBOSFP_ERR_PARSE_FILE,
} libosfp_error_code_t;
typedef enum libosfp_os_class_id {
LIBOSFP_OS_CLASS_WINDOWS,
LIBOSFP_OS_CLASS_Linux,
LIBOSFP_OS_CLASS_MAC_OS,
LIBOSFP_OS_CLASS_IOS,
LIBOSFP_OS_CLASS_ANDROID,
LIBOSFP_OS_CLASS_MAX,
} libosfp_os_class_id_t;
typedef struct libosfp_score {
unsigned int os_class_score[LIBOSFP_OS_CLASS_MAX];
} libosfp_score_t;
typedef struct libosfp_result {
enum libosfp_error_code err;
enum libosfp_os_class_id likely_os_class; // top rated os class
unsigned int likely_score;
unsigned int perfect_score;
libosfp_score_t score;
} libosfp_result_t;
typedef struct libosfp_context {
char *fp_file;
void *score_db;
} libosfp_context_t;
libosfp_os_class_id_t libosfp_os_class_name_to_id(char *name);
const char *libosfp_os_class_id_to_name(libosfp_os_class_id_t os_class);
int libosfp_result_to_buf(libosfp_result_t *result, char *strbuf, unsigned int buf_len);
unsigned int libosfp_result_likely_os_class_score_get(libosfp_result_t *result);
const char *libosfp_result_likely_os_class_name_get(libosfp_result_t *result);
libosfp_error_code_t libosfp_header_match(libosfp_context_t *libosfp_context, unsigned char *ip_hdr, unsigned char *tcp_hdr, libosfp_result_t *result);
libosfp_error_code_t libosfp_context_setup(libosfp_context_t *libosfp_context);
libosfp_context_t *libosfp_context_create(char *fp_file);
void libosfp_context_destroy(libosfp_context_t *libosfp_context);
#endif

415
src/libosfp_fingerprint.c Normal file
View File

@@ -0,0 +1,415 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <malloc.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include "cJSON.h"
#include "libosfp.h"
#include "libosfp_fingerprint.h"
#include "libosfp_log.h"
#define LIBOSFP_ETHERNET_HEADER_LEN 14
#define LIBOSFP_VLAN_HEADER_LEN 4
#define LIBOSFP_IPV4_HEADER_LEN 20
#define LIBOSFP_IPV6_HEADER_LEN 40
#define LIBOSFP_TCP_HEADER_LEN 20
#define LIBOSFP_FINGERPRINT_FIELD_NAME_IP_ID "ip_id"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_IP_TOS "ip_tos"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_IP_TOTAL_LENGHT "ip_total_length"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_IP_TTL "ip_ttl"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_OFF "tcp_off"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP "tcp_timestamp"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP_ECHO_REPLY "tcp_timestamp_echo_reply"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_WCALING "tcp_window_scaling"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_SIZE "tcp_window_size"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_FLAGS "tcp_flags"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_MSS "tcp_mss"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS "tcp_options"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS_ORDERED "tcp_options_ordered"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_OS "os"
typedef struct libosfp_tcp_opt {
unsigned char type;
unsigned char len;
const unsigned char *data;
} libosfp_tcp_opt_t;
libosfp_fingerprint_field_t fp_fields[LIBOSFP_FIELD_MAX] = {
{LIBOSFP_FINGERPRINT_FIELD_NAME_IP_ID, 1, LIBOSFP_FIELD_TYPE_UINT, 150, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_IP_TOS, 1, LIBOSFP_FIELD_TYPE_UINT, 25, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_IP_TOTAL_LENGHT, 1, LIBOSFP_FIELD_TYPE_UINT, 250, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_IP_TTL, 1, LIBOSFP_FIELD_TYPE_UINT, 200, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_OFF, 1, LIBOSFP_FIELD_TYPE_UINT, 250, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP, 0, LIBOSFP_FIELD_TYPE_UINT, 0, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP_ECHO_REPLY,1, LIBOSFP_FIELD_TYPE_UINT, 200, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_WCALING, 1, LIBOSFP_FIELD_TYPE_UINT, 200, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_SIZE, 1, LIBOSFP_FIELD_TYPE_UINT, 200, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_FLAGS, 1, LIBOSFP_FIELD_TYPE_UINT, 25, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_MSS, 1, LIBOSFP_FIELD_TYPE_UINT, 150, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS, 1, LIBOSFP_FIELD_TYPE_STRING, 400, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS_ORDERED, 1, LIBOSFP_FIELD_TYPE_STRING, 250, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_OS, 0, LIBOSFP_FIELD_TYPE_STRING, 0, NULL, 0},
};
static char option_to_ascii(unsigned char type)
{
switch (type) {
case LIBOSFP_TCP_OPT_EOL:
return 'E';
case LIBOSFP_TCP_OPT_NOP:
return 'N';
case LIBOSFP_TCP_OPT_MSS:
return 'M';
case LIBOSFP_TCP_OPT_WSCALE:
return 'W';
case LIBOSFP_TCP_OPT_SACKOK:
return 'S';
case LIBOSFP_TCP_OPT_SACK:
return 'K';
case LIBOSFP_TCP_OPT_ECHO:
return 'J';
case LIBOSFP_TCP_OPT_ECHOREPLY:
return 'F';
case LIBOSFP_TCP_OPT_TIMESTAMP:
return 'T';
case LIBOSFP_TCP_OPT_POCONN:
return 'P';
case LIBOSFP_TCP_OPT_POSVC:
return 'R';
default:
return 'U';
}
}
static unsigned int compute_ip_ttl(unsigned int ip_ttl)
{
if (ip_ttl >= 0 && ip_ttl <= 32) {
ip_ttl = 32;
} else if (ip_ttl > 32 && ip_ttl <= 64) {
ip_ttl = 64;
} else if (ip_ttl > 64 && ip_ttl <= 128) {
ip_ttl = 128;
} else if (ip_ttl > 128) {
ip_ttl = 255;
}
return ip_ttl;
}
static unsigned int decode_tcp_options(libosfp_tcp_opt_t *tcp_opts, unsigned int max_opt_cnt, unsigned char *data, unsigned int len)
{
unsigned int offset = 0;
unsigned int tcp_opt_cnt = 0;
while (offset < len && tcp_opt_cnt < max_opt_cnt) {
unsigned char type;
unsigned char olen;
unsigned char *odata;
type = *(data + offset);
if (type == LIBOSFP_TCP_OPT_EOL || type == LIBOSFP_TCP_OPT_NOP) {
olen = 1;
} else {
olen = *(data + offset + 1);
if (olen < 2) {
break;
}
}
if (offset + olen > len) {
break;
}
odata = (olen > 2) ? (data + offset + 2) : NULL;
tcp_opts[tcp_opt_cnt].type = type;
tcp_opts[tcp_opt_cnt].len = olen;
tcp_opts[tcp_opt_cnt].data = odata;
offset += olen;
tcp_opt_cnt++;
}
return tcp_opt_cnt;
}
int libosfp_fingerprint_to_json_buf(libosfp_fingerprint_t *fp, char *strbuf, unsigned int buf_len)
{
int rlen = 0, ret, i;
cJSON *root;
if (fp == NULL || strbuf == NULL || buf_len == 0) {
return 0;
}
root = cJSON_CreateObject();
if (root == NULL) {
return 0;
}
for (i = 0; i < LIBOSFP_FIELD_MAX; i++) {
if (fp->fields[i].enabled) {
switch (fp_fields[i].type) {
case LIBOSFP_FIELD_TYPE_UINT:
cJSON_AddNumberToObject(root, fp_fields[i].name, *(unsigned int *)fp->fields[i].value);
break;
case LIBOSFP_FIELD_TYPE_STRING:
cJSON_AddStringToObject(root, fp_fields[i].name, (char *)fp->fields[i].value);
break;
default:
break;
}
} else {
cJSON_AddNullToObject(root, fp_fields[i].name);
}
}
if (!cJSON_PrintPreallocated(root, strbuf, buf_len, 1)) {
return 0;
}
cJSON_Delete(root);
return strlen(strbuf) + 1;
}
unsigned int libosfp_fingerprint_get_field_enabled(libosfp_field_id_t field_id)
{
return fp_fields[field_id].enabled;
}
unsigned int libosfp_fingerprint_get_field_importance(libosfp_field_id_t field_id)
{
return fp_fields[field_id].importance;
}
char *libosfp_fingerprint_get_field_name(libosfp_field_id_t field_id)
{
return fp_fields[field_id].name;
}
unsigned int libosfp_fingerprint_get_field_type(libosfp_field_id_t field_id)
{
return fp_fields[field_id].type;
}
void libosfp_fingerprint_setup_field(libosfp_fingerprint_t *fp, libosfp_field_id_t field_id, void *value, unsigned int len)
{
fp->fields[field_id].name = libosfp_fingerprint_get_field_name(field_id);
fp->fields[field_id].enabled = 1;
if (fp->value_buffer_used + len <= sizeof(fp->value_buffer)) {
memcpy(fp->value_buffer + fp->value_buffer_used, value, len);
fp->fields[field_id].value = fp->value_buffer + fp->value_buffer_used;
fp->fields[field_id].value_len = len;
fp->value_buffer_used += len;
} else {
fp->fields[field_id].value = NULL;
fp->fields[field_id].value_len = 0;
}
}
void libosfp_fingerprinting_tcp_option(unsigned char *pkt, unsigned int pktlen, libosfp_fingerprint_t *fp)
{
int ret,i;
char options[LIBOSFP_TCP_OPTLENMAX];
char options_ordered[LIBOSFP_TCP_OPTLENMAX];
unsigned int maxoffset = sizeof(options) - 3;
unsigned int ordered_maxoffset = sizeof(options_ordered) - 1;
unsigned int offset = 0;
unsigned int ordered_offset = 0;
unsigned int tcp_mss;
unsigned int tcp_ws;
unsigned int tcp_ter;
unsigned int tcp_opt_cnt;
libosfp_tcp_opt_t tcp_opts[LIBOSFP_TCP_OPTMAX];
tcp_opt_cnt = decode_tcp_options(tcp_opts, LIBOSFP_TCP_OPTMAX, pkt, pktlen);
for (i = 0; i < tcp_opt_cnt && offset < maxoffset && ordered_offset < ordered_maxoffset; i++) {
libosfp_tcp_opt_t *opt = &tcp_opts[i];
char letter = option_to_ascii(opt->type);
options[offset++] = letter;
options_ordered[ordered_offset++] = letter;
switch (opt->type) {
case LIBOSFP_TCP_OPT_EOL:
case LIBOSFP_TCP_OPT_NOP:
break;
case LIBOSFP_TCP_OPT_MSS:
if (opt->len != LIBOSFP_TCP_OPT_MSS_LEN) {
break;
}
tcp_mss = (unsigned int)ntohs(*(unsigned short *)opt->data);
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_MSS, &tcp_mss, sizeof(tcp_mss));
ret = snprintf(options + offset, sizeof(options), "%u", tcp_mss);
if (ret < 0 || offset + ret > maxoffset) {
break;
}
offset += ret;
break;
case LIBOSFP_TCP_OPT_WSCALE:
if (opt->len != LIBOSFP_TCP_OPT_WS_LEN) {
break;
}
tcp_ws = (unsigned int)*(unsigned char *)opt->data;
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_WINDOW_SCALING, &tcp_ws, sizeof(tcp_ws));
ret = snprintf(options + offset, sizeof(options), "%u", tcp_ws);
if (ret < 0 || offset + ret > maxoffset) {
break;
}
offset += ret;
break;
case LIBOSFP_TCP_OPT_TIMESTAMP:
if (opt->len != LIBOSFP_TCP_OPT_TS_LEN) {
break;
}
tcp_ter = ntohl(*(unsigned int *)(opt->data + 4));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY, &tcp_ter, sizeof(tcp_ter));
ret = snprintf(options + offset, sizeof(options), "%u", tcp_ter);
if (ret < 0 || offset + ret > maxoffset) {
break;
}
offset += ret;
break;
case LIBOSFP_TCP_OPT_SACKOK:
case LIBOSFP_TCP_OPT_SACK:
case LIBOSFP_TCP_OPT_ECHO:
case LIBOSFP_TCP_OPT_ECHOREPLY:
case LIBOSFP_TCP_OPT_POCONN:
case LIBOSFP_TCP_OPT_POSVC:
break;
default:
ret = snprintf(options + offset, sizeof(options), "%u", opt->type);
if (ret < 0 || offset + ret > maxoffset) {
break;
}
offset += ret;
}
options[offset++] = ',';
options[offset] = 0;
options_ordered[ordered_offset] = 0;
}
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_OPTIONS, options, strlen(options) + 1);
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_OPTIONS_ORDERED, options_ordered, strlen(options_ordered) + 1);
return;
}
int libosfp_fingerprinting_tcp(struct tcphdr *tcph, libosfp_fingerprint_t *fp)
{
if (tcph == NULL || fp == NULL) {
goto exit;
}
unsigned int tcp_off = tcph->doff << 2;
unsigned int tcp_window_size = ntohs(tcph->window);
unsigned int tcp_flags = *((unsigned char *)&tcph->window - 1);
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_OFF, &tcp_off, sizeof(tcp_off));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_WINDOW_SIZE, &tcp_window_size, sizeof(tcp_window_size));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_FLAGS, &tcp_flags, sizeof(tcp_flags));
// tcp options
if (tcp_off > LIBOSFP_TCP_HEADER_LEN) {
libosfp_fingerprinting_tcp_option((unsigned char *)tcph + LIBOSFP_TCP_HEADER_LEN, tcp_off - LIBOSFP_TCP_HEADER_LEN, fp);
}
return 0;
exit:
return -1;
}
int libosfp_fingerprinting_ipv4(struct iphdr *iph, libosfp_fingerprint_t *fp)
{
if (iph == NULL || fp == NULL) {
goto exit;
}
unsigned int ip_id = !!iph->id;
unsigned int ip_tos = iph->tos;
unsigned int ip_total_length = ntohs(iph->tot_len);
unsigned int ip_ttl = compute_ip_ttl(iph->ttl);
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_ID, &ip_id, sizeof(ip_id));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_TOS, &ip_tos, sizeof(ip_tos));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_TOTAL_LENGTH, &ip_total_length, sizeof(ip_total_length));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_TTL, &ip_ttl, sizeof(ip_ttl));
return 0;
exit:
return -1;
}
int libosfp_fingerprinting_ipv6(struct ipv6hdr *iph, libosfp_fingerprint_t *fp)
{
if (iph == NULL || fp == NULL) {
goto exit;
}
//unsigned int ip_id = 0;
//unsigned int ip_tos = 0;
unsigned int ip_total_length = LIBOSFP_IPV6_HEADER_LEN + ntohs(iph->payload_len);
unsigned int ip_ttl = compute_ip_ttl(iph->hop_limit);
//libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_ID, &ip_id, sizeof(ip_id));
//libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_TOS, &ip_tos, sizeof(ip_tos));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_TOTAL_LENGTH, &ip_total_length, sizeof(ip_total_length));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_TTL, &ip_ttl, sizeof(ip_ttl));
return 0;
exit:
return -1;
}
int libosfp_fingerprinting(unsigned char *iph, unsigned char *tcph, libosfp_fingerprint_t *fp)
{
int ret, ip_version;
if (iph == NULL || tcph == NULL || fp == NULL) {
goto exit;
}
memset(fp, 0, sizeof(libosfp_fingerprint_t));
ip_version = ((iph)[0] & 0xf0) >> 4;
switch (ip_version) {
case 4:
ret = libosfp_fingerprinting_ipv4((struct iphdr *)iph, fp);
break;
case 6:
ret = libosfp_fingerprinting_ipv6((struct ipv6hdr *)iph, fp);
break;
default:
ret = -1;
goto exit;
}
if (ret != 0) {
goto exit;
}
ret = libosfp_fingerprinting_tcp((struct tcphdr *)tcph, fp);
if (ret != 0) {
goto exit;
}
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_OS, "", strlen("") + 1);
return 0;
exit:
return -1;
}

106
src/libosfp_fingerprint.h Normal file
View File

@@ -0,0 +1,106 @@
#ifndef __LIBOSFP_FINGERPRINT_H__
#define __LIBOSFP_FINGERPRINT_H__
#include "libosfp.h"
#define LIBOSFP_FINGERPRINT_VALUE_BUFFER_MAX 128
#define LIBOSFP_TCP_OPTLENMAX 64
#define LIBOSFP_TCP_OPTMAX 20
//# TCP Options (opt_type) - http://www.iana.org/assignments/tcp-parameters
#define LIBOSFP_TCP_OPT_EOL 0 //# end of option list
#define LIBOSFP_TCP_OPT_NOP 1 //# no operation
#define LIBOSFP_TCP_OPT_MSS 2 //# maximum segment size
#define LIBOSFP_TCP_OPT_WSCALE 3 //# window scale factor, RFC 1072
#define LIBOSFP_TCP_OPT_SACKOK 4 //# SACK permitted, RFC 2018
#define LIBOSFP_TCP_OPT_SACK 5 //# SACK, RFC 2018
#define LIBOSFP_TCP_OPT_ECHO 6 //# echo (obsolete), RFC 1072
#define LIBOSFP_TCP_OPT_ECHOREPLY 7 //# echo reply (obsolete), RFC 1072
#define LIBOSFP_TCP_OPT_TIMESTAMP 8 //# timestamps, RFC 1323
#define LIBOSFP_TCP_OPT_POCONN 9 //# partial order conn, RFC 1693
#define LIBOSFP_TCP_OPT_POSVC 10 //# partial order service, RFC 1693
#define LIBOSFP_TCP_OPT_CC 11 //# connection count, RFC 1644
#define LIBOSFP_TCP_OPT_CCNEW 12 //# CC.NEW, RFC 1644
#define LIBOSFP_TCP_OPT_CCECHO 13 //# CC.ECHO, RFC 1644
#define LIBOSFP_TCP_OPT_ALTSUM 14 //# alt checksum request, RFC 1146
#define LIBOSFP_TCP_OPT_ALTSUMDATA 15 //# alt checksum data, RFC 1146
#define LIBOSFP_TCP_OPT_SKEETER 16 //# Skeeter
#define LIBOSFP_TCP_OPT_BUBBA 17 //# Bubba
#define LIBOSFP_TCP_OPT_TRAILSUM 18 //# trailer checksum
#define LIBOSFP_TCP_OPT_MD5 19 //# MD5 signature, RFC 2385
#define LIBOSFP_TCP_OPT_SCPS 20 //# SCPS capabilities
#define LIBOSFP_TCP_OPT_SNACK 21 //# selective negative acks
#define LIBOSFP_TCP_OPT_REC 22 //# record boundaries
#define LIBOSFP_TCP_OPT_CORRUPT 23 //# corruption experienced
#define LIBOSFP_TCP_OPT_SNAP 24 //# SNAP
#define LIBOSFP_TCP_OPT_TCPCOMP 26 //# TCP compression filter
#define LIBOSFP_TCP_OPT_MAX 27 //# Quick-Start Response
#define LIBOSFP_TCP_OPT_USRTO 28 //# User Timeout Option (also, other known unauthorized use) [***][1] [RFC5482]
#define LIBOSFP_TCP_OPT_AUTH 29 //# TCP Authentication Option (TCP-AO) [RFC5925]
#define LIBOSFP_TCP_OPT_MULTIPATH 30 //# Multipath TCP (MPTCP)
#define LIBOSFP_TCP_OPT_FASTOPEN 34 //# TCP Fast Open Cookie [RFC7413]
#define LIBOSFP_TCP_OPY_ENCNEG 69 //# Encryption Negotiation (TCP-ENO) [RFC8547]
#define LIBOSFP_TCP_OPT_EXP1 253 //# RFC3692-style Experiment 1 (also improperly used for shipping products)
#define LIBOSFP_TCP_OPT_EXP2 254 //# RFC3692-style Experiment 2 (also improperly used for shipping products)
#define LIBOSFP_TCP_OPT_SACKOK_LEN 2
#define LIBOSFP_TCP_OPT_WS_LEN 3
#define LIBOSFP_TCP_OPT_TS_LEN 10
#define LIBOSFP_TCP_OPT_MSS_LEN 4
#define LIBOSFP_TCP_OPT_SACK_MIN_LEN 10 /* hdr 2, 1 pair 8 = 10 */
#define LIBOSFP_TCP_OPT_SACK_MAX_LEN 34 /* hdr 2, 4 pair 32= 34 */
#define LIBOSFP_TCP_OPT_TFO_MIN_LEN 4 /* kind, len, 2 bytes cookie: 4 */
#define LIBOSFP_TCP_OPT_TFO_MAX_LEN 18 /* kind, len, 18 */
typedef enum libosfp_field_id {
LIBOSFP_FIELD_IP_ID,
LIBOSFP_FIELD_IP_TOS,
LIBOSFP_FIELD_IP_TOTAL_LENGTH,
LIBOSFP_FIELD_IP_TTL,
LIBOSFP_FIELD_TCP_OFF,
LIBOSFP_FIELD_TCP_TIMESTAMP,
LIBOSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY,
LIBOSFP_FIELD_TCP_WINDOW_SCALING,
LIBOSFP_FIELD_TCP_WINDOW_SIZE,
LIBOSFP_FIELD_TCP_FLAGS,
LIBOSFP_FIELD_TCP_MSS,
LIBOSFP_FIELD_TCP_OPTIONS,
LIBOSFP_FIELD_TCP_OPTIONS_ORDERED,
LIBOSFP_FIELD_OS,
LIBOSFP_FIELD_MAX,
} libosfp_field_id_t;
typedef enum libosfp_field_type {
LIBOSFP_FIELD_TYPE_UNKNOWN,
LIBOSFP_FIELD_TYPE_UINT,
LIBOSFP_FIELD_TYPE_STRING,
LIBOSFP_FIELD_TYPE_MAX
} libosfp_field_type_t;
typedef struct libosfp_fingerprint_field {
char *name;
unsigned int enabled;
unsigned int type;
unsigned int importance;
void *value;
unsigned int value_len;
} libosfp_fingerprint_field_t;
typedef struct libosfp_fingerprint {
libosfp_fingerprint_field_t fields[LIBOSFP_FIELD_MAX];
char value_buffer[LIBOSFP_FINGERPRINT_VALUE_BUFFER_MAX];
unsigned value_buffer_used;
} libosfp_fingerprint_t;
char *libosfp_fingerprint_get_field_name(libosfp_field_id_t field_id);
unsigned int libosfp_fingerprint_get_field_enabled(libosfp_field_id_t field_id);
unsigned int libosfp_fingerprint_get_field_importance(libosfp_field_id_t field_id);
unsigned int libosfp_fingerprint_get_field_type(libosfp_field_id_t field_id);
int libosfp_fingerprint_to_json_buf(libosfp_fingerprint_t *fp, char *strbuf, unsigned int buf_len);
int libosfp_fingerprinting(unsigned char *iphdr, unsigned char *tcphdr, libosfp_fingerprint_t *fp);
#endif

58
src/libosfp_log.c Normal file
View File

@@ -0,0 +1,58 @@
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include "libosfp.h"
#include "libosfp_log.h"
/* The maximum length of the log message */
#define LIBOSFP_MAX_LOG_MSG_LEN 2048
unsigned int libosfp_log_level = LIBOSFP_LOG_LEVEL_INFO;
void libosfp_log_message(unsigned int x, const char *file, const int line, const char *func, const char *msg)
{
char buffer[LIBOSFP_MAX_LOG_MSG_LEN] = "";
char log_time_buf[128];
time_t now;
struct tm tm_now;
time(&now);
localtime_r(&now, &tm_now);
strftime(log_time_buf, sizeof(log_time_buf), "%Y-%m-%d %T", &tm_now);
switch (x) {
case LIBOSFP_LOG_LEVEL_DEBUG:
snprintf(buffer, sizeof(buffer), "[%s][DEBUG][%s:%d %s] %s", log_time_buf, file, line, func, msg);
break;
case LIBOSFP_LOG_LEVEL_INFO:
snprintf(buffer, sizeof(buffer), "[%s][INFO][%s:%d %s] %s", log_time_buf, file, line, func, msg);
break;
case LIBOSFP_LOG_LEVEL_WARNING:
snprintf(buffer, sizeof(buffer), "[%s][WARN][%s:%d %s] %s", log_time_buf, file, line, func, msg);
break;
case LIBOSFP_LOG_LEVEL_ERROR:
snprintf(buffer, sizeof(buffer), "[%s][ERROR][%s:%d %s] %s", log_time_buf, file, line, func, msg);
break;
}
}
void libosfp_log(unsigned int x, const char *file, const char *func, const int line, const char *fmt, ...)
{
if (libosfp_log_level >= x ) {
char msg[LIBOSFP_MAX_LOG_MSG_LEN];
va_list ap;
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
libosfp_log_message(x, file, line, func, msg);
}
}
void libosfp_log_level_set(libosfp_log_level_t level)
{
libosfp_log_level = level;
}

20
src/libosfp_log.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef __LIBOSFP_LOG_H__
#define __LIBOSFP_LOG_H__
typedef enum libosfp_log_level {
LIBOSFP_LOG_LEVEL_DEBUG,
LIBOSFP_LOG_LEVEL_INFO,
LIBOSFP_LOG_LEVEL_WARNING,
LIBOSFP_LOG_LEVEL_ERROR
} libosfp_log_level_t;
#define libosfp_log_debug(...) libosfp_log(LIBOSFP_LOG_LEVEL_DEBUG, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#define libosfp_log_info(...) libosfp_log(LIBOSFP_LOG_LEVEL_INFO, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#define libosfp_log_warning(...) libosfp_log(LIBOSFP_LOG_LEVEL_WARNING, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#define libosfp_log_error(...) libosfp_log(LIBOSFP_LOG_LEVEL_ERROR, __FILE__, __LINE__, __FUNCTION__,__VA_ARGS__)
void libosfp_log_level_set(libosfp_log_level_t level);
void libosfp_log(unsigned int x, const char *file, const char *func, const int line, const char *fmt, ...);
#endif

539
src/libosfp_score_db.c Normal file
View File

@@ -0,0 +1,539 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <malloc.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "uthash.h"
#include "cJSON.h"
#include "libosfp.h"
#include "libosfp_fingerprint.h"
#include "libosfp_score_db.h"
#include "libosfp_log.h"
#define LIBOSFP_SCORE_DB_FIELD_UINT_VALUE_MAX 65536
typedef struct libosfp_score_db_array_data {
libosfp_score_t *array_head;
unsigned int array_len;
} libosfp_score_db_array_data_t;
typedef struct libosfp_score_db_hash_element {
char *key;
unsigned int keylen;
libosfp_score_t *score;
UT_hash_handle hh;
} libosfp_score_db_hash_element_t;
typedef struct libosfp_score_db_hash_data {
libosfp_score_db_hash_element_t *hash_head;
} libosfp_score_db_hash_data_t;
int libosfp_score_db_array_add(void *data, libosfp_score_t *score, void *value, unsigned int len)
{
int ret = -1, i;
unsigned int index;
libosfp_score_db_array_data_t *array_data = (libosfp_score_db_array_data_t *)data;
if (array_data == NULL || score == NULL || value == NULL || len != sizeof(unsigned int)) {
goto exit;
}
if (array_data->array_head == NULL || array_data->array_len == 0) {
goto exit;
}
index = *(unsigned int *)value;
if (index >= array_data->array_len) {
goto exit;
}
for (i = 0; i < LIBOSFP_OS_CLASS_MAX; i++) {
array_data->array_head[index].os_class_score[i] += score->os_class_score[i];
}
return 0;
exit:
return ret;
}
libosfp_score_t *libosfp_score_db_array_match(void *data, void *value, unsigned int len)
{
unsigned int index;
libosfp_score_db_array_data_t *array_data = (libosfp_score_db_array_data_t *)data;
if (array_data == NULL || value == NULL || len != sizeof(unsigned int)) {
return NULL;
}
if (array_data->array_head == NULL || array_data->array_len == 0) {
return NULL;
}
index = *(unsigned int *)value;
if (index >= array_data->array_len) {
return NULL;
}
return &((array_data->array_head)[index]);
}
void *libosfp_score_db_array_create(void)
{
libosfp_score_db_array_data_t *array_data = calloc(1, sizeof(libosfp_score_db_array_data_t));
if (array_data == NULL) {
return NULL;
}
array_data->array_head = calloc(LIBOSFP_SCORE_DB_FIELD_UINT_VALUE_MAX, sizeof(libosfp_score_t));
if (array_data->array_head == NULL) {
free(array_data);
return NULL;
}
array_data->array_len = LIBOSFP_SCORE_DB_FIELD_UINT_VALUE_MAX;
return (void *)array_data;
}
void libosfp_score_db_array_destroy(void *data) {
libosfp_score_db_array_data_t *array_data = (libosfp_score_db_array_data_t *)data;
if (array_data) {
if (array_data->array_head) {
free(array_data->array_head);
}
free(array_data);
}
}
int libosfp_score_db_hash_add(void *data, libosfp_score_t *score, void *value, unsigned int len)
{
int ret = -1, i;
libosfp_score_db_hash_data_t *hash_data = (libosfp_score_db_hash_data_t *)data;
libosfp_score_db_hash_element_t *element = NULL;
if (hash_data == NULL || score == NULL || value == NULL || len == 0) {
goto exit;
}
HASH_FIND(hh, hash_data->hash_head, value, len, element);
if (element == NULL) {
element = (libosfp_score_db_hash_element_t *)calloc(1, sizeof(libosfp_score_db_hash_element_t));
if (element == NULL) {
goto exit;
}
element->key = strdup(value);
element->keylen = len;
element->score = (libosfp_score_t *)calloc(1, sizeof(libosfp_score_t));
if (element->score == NULL) {
free(element);
element = NULL;
goto exit;
}
HASH_ADD_KEYPTR(hh, hash_data->hash_head, element->key, element->keylen, element);
}
for (i = 0; i < LIBOSFP_OS_CLASS_MAX; i++) {
element->score->os_class_score[i] += score->os_class_score[i];
}
return 0;
exit:
return ret;
}
libosfp_score_t *libosfp_score_db_hash_match(void *data, void *value, unsigned int len)
{
libosfp_score_db_hash_data_t *hash_data = (libosfp_score_db_hash_data_t *)data;
libosfp_score_db_hash_element_t *element = NULL;
if (data == NULL || value == NULL || len == 0) {
return NULL;
}
if (hash_data->hash_head == NULL) {
return NULL;
}
HASH_FIND(hh, hash_data->hash_head, value, len, element);
if (element == NULL) {
return NULL;
}
return element->score;
}
void *libosfp_score_db_hash_create(void)
{
return (void*)calloc(1, sizeof(libosfp_score_db_hash_data_t));
}
void libosfp_score_db_hash_destroy(void *data) {
libosfp_score_db_hash_data_t *hash_data = (libosfp_score_db_hash_data_t *)data;
libosfp_score_db_hash_element_t *element = NULL;
libosfp_score_db_hash_element_t *tmp = NULL;
if (hash_data) {
if (hash_data->hash_head) {
HASH_ITER(hh, hash_data->hash_head, element, tmp) {
HASH_DELETE(hh, hash_data->hash_head, element);
if (element) {
if (element->key) {
free(element->key);
}
if (element->score) {
free(element->score);
}
free(element);
}
}
}
free(hash_data);
}
}
libosfp_score_t *libosfp_score_db_filed_match(libosfp_field_score_db_t *db, void *value, unsigned int len)
{
if (db == NULL || value == NULL || len == 0) {
return NULL;
}
return db->match(db->data, value, len);
}
char *libosfp_score_db_read_file(char *fp_file)
{
int ret = -1;
char *file_buffer = NULL;
unsigned int file_len = 0;
FILE *fp = NULL;
struct stat st;
if (0 > stat(fp_file, &st)) {
printf("stat() on '%s' failed.\n", fp_file);
goto exit;
}
if (st.st_size == 0) {
printf("Empty file: %s.\n", fp_file);
goto exit;
}
file_len = (unsigned int)st.st_size;
file_buffer = malloc(file_len);
if (file_buffer == NULL) {
printf("Not enough memory for file buffer. file name: %s\n",fp_file);
goto exit;
}
fp = fopen(fp_file, "r");
if (fp == NULL) {
printf("Cannot open '%s' for reading.\n", fp_file);
goto exit;
}
ret = fread(file_buffer, 1, file_len, fp);
if (ret < 0) {
free(file_buffer);
fclose(fp);
goto exit;
}
fclose(fp);
return file_buffer;
exit:
return NULL;
}
int libosfp_score_db_load_field(libosfp_field_score_db_t *db, cJSON *field, libosfp_os_class_id_t os_class)
{
int ret = -1;
libosfp_score_t score = {0};
void *value_ptr;
unsigned int value_len;
switch (field->type) {
case cJSON_Number:
value_ptr = (void *)&field->valueint;
value_len = sizeof(field->valueint);
break;
case cJSON_String:
value_ptr = (void *)field->valuestring;
value_len = strlen(field->valuestring) + 1;
break;
case cJSON_NULL:
ret = 0;
goto exit;
default:
goto exit;
}
score.os_class_score[os_class] = 1;
ret = db->add(db->data, &score, value_ptr, value_len);
if (ret != 0) {
goto exit;
}
db->entry_count++;
return 0;
exit:
return ret;
}
int libosfp_score_db_load_entry(libosfp_score_db_t *score_db, cJSON *entry)
{
int ret = -1, i;
cJSON *field = NULL;
if (score_db == NULL || entry == NULL) {
goto exit;
}
field = cJSON_GetObjectItem(entry, libosfp_fingerprint_get_field_name(LIBOSFP_FIELD_OS));
if (field == NULL || field->valuestring == NULL) {
goto exit;
}
libosfp_os_class_id_t os_class = libosfp_os_class_name_to_id(field->valuestring);
if (os_class >= LIBOSFP_OS_CLASS_MAX) {
goto exit;
}
for (i = 0; i < LIBOSFP_FIELD_OS; i++) {
libosfp_field_score_db_t *db = &score_db->field_score_dbs[i];
if (db == NULL) {
goto exit;
}
if (!db->enabled) {
continue;
}
field = cJSON_GetObjectItem(entry, libosfp_fingerprint_get_field_name(i));
if (field == NULL) {
printf("json entry missing field: %s\n%s\n",
libosfp_fingerprint_get_field_name(i), cJSON_Print(entry));
continue;
}
ret = libosfp_score_db_load_field(db, field, os_class);
if (ret != 0) {
printf("json entry field load failed. field: %s\n%s\n",
libosfp_fingerprint_get_field_name(i), cJSON_Print(entry));
continue;
}
}
score_db->entry_count++;
score_db->os_class_entry_count[os_class]++;
return 0;
exit:
return ret;
}
int libosfp_score_db_load(libosfp_score_db_t *score_db, char *fp_file)
{
int ret = LIBOSFP_EINVAL, i;
char *file_buffer;
libosfp_field_score_db_t *field_score_db;
cJSON *root = NULL;
cJSON *entry = NULL;
if (score_db == NULL || fp_file == NULL) {
goto exit;
}
file_buffer = libosfp_score_db_read_file(fp_file);
if (file_buffer == NULL) {
ret = LIBOSFP_ERR_READ_FILE;
goto exit;
}
root = cJSON_Parse(file_buffer);
if (root == NULL) {
ret = LIBOSFP_ERR_PARSE_FILE;
goto exit;
}
score_db->entry_count = cJSON_GetArraySize(root);
for (i = 0; i < LIBOSFP_FIELD_MAX; i++) {
field_score_db = &score_db->field_score_dbs[i];
if (field_score_db->enabled) {
score_db->perfect_score += libosfp_fingerprint_get_field_importance(i);
}
}
for (i = 0; i < score_db->entry_count; i++) {
entry = cJSON_GetArrayItem(root, i);
if (entry) {
ret = libosfp_score_db_load_entry(score_db, entry);
if (ret != 0) {
printf("json entry load failed.\n%s\n", cJSON_Print(entry));
continue;
}
}
}
cJSON_Delete(root);
return 0;
exit:
return ret;
}
int libosfp_score_db_score(libosfp_context_t *libosfp_context, libosfp_fingerprint_t *fp, libosfp_result_t *result)
{
int ret = -1, i, j;
void *field_value;
unsigned int os_class_score;
unsigned int perfect_score;
unsigned int entry_count;
unsigned int importance;
unsigned int field_len;
libosfp_score_t *score;
libosfp_score_db_t *score_db;
libosfp_field_score_db_t *field_score_db;
if (libosfp_context == NULL || fp == NULL || result == NULL) {
goto exit;
}
memset(result, 0, sizeof(libosfp_result_t));
score_db = (libosfp_score_db_t*)libosfp_context->score_db;
for (i = 0; i < LIBOSFP_FIELD_MAX; i++) {
if (!fp->fields[i].enabled) {
continue;
}
field_score_db = &score_db->field_score_dbs[i];
if (!field_score_db->enabled) {
continue;
}
field_value = fp->fields[i].value;
field_len = fp->fields[i].value_len;
score = libosfp_score_db_filed_match(field_score_db, field_value, field_len);
if (score == NULL) {
continue;
}
importance = libosfp_fingerprint_get_field_importance(i);
for (j = 0; j < LIBOSFP_OS_CLASS_MAX; j++) {
result->score.os_class_score[j] += score->os_class_score[j] * importance;
}
if (i == LIBOSFP_FIELD_TCP_OPTIONS) {
i++;
}
}
perfect_score = score_db->perfect_score;
result->perfect_score = perfect_score;
for (j = 0; j < LIBOSFP_OS_CLASS_MAX; j++) {
entry_count = score_db->os_class_entry_count[j];
os_class_score = result->score.os_class_score[j];
if (entry_count == 0 || perfect_score == 0) {
continue;
}
os_class_score = 100 * os_class_score / entry_count;
os_class_score = (unsigned int)((float)os_class_score / (float)perfect_score);
if (result->likely_score < os_class_score) {
result->likely_score = os_class_score;
result->likely_os_class = j;
}
result->score.os_class_score[j] = os_class_score;
}
return 0;
exit:
return ret;
}
libosfp_score_db_t *libosfp_score_db_create(void)
{
int ret = -1, i;
libosfp_score_db_t *score_db;
libosfp_field_score_db_t *db;
score_db = calloc(1, sizeof(libosfp_score_db_t));
for (i = 0; i < LIBOSFP_FIELD_MAX; i++) {
db = &score_db->field_score_dbs[i];
db->enabled = libosfp_fingerprint_get_field_enabled(i);
db->type = libosfp_fingerprint_get_field_type(i);
switch (score_db->field_score_dbs[i].type) {
case LIBOSFP_FIELD_TYPE_UINT:
score_db->field_score_dbs[i].create = libosfp_score_db_array_create;
score_db->field_score_dbs[i].destroy = libosfp_score_db_array_destroy;
score_db->field_score_dbs[i].add = libosfp_score_db_array_add;
score_db->field_score_dbs[i].match = libosfp_score_db_array_match;
break;
case LIBOSFP_FIELD_TYPE_STRING:
score_db->field_score_dbs[i].create = libosfp_score_db_hash_create;
score_db->field_score_dbs[i].destroy = libosfp_score_db_hash_destroy;
score_db->field_score_dbs[i].add = libosfp_score_db_hash_add;
score_db->field_score_dbs[i].match = libosfp_score_db_hash_match;
break;
default:
goto exit;
}
db->data = score_db->field_score_dbs[i].create();
if (db->data == NULL) {
continue;
}
}
return score_db;
exit:
if (score_db) {
libosfp_score_db_destroy(score_db);
}
return NULL;
}
void libosfp_score_db_destroy(libosfp_score_db_t *score_db)
{
int i;
libosfp_field_score_db_t *db;
if (score_db) {
for (i = 0; i < LIBOSFP_FIELD_MAX; i++) {
db = &score_db->field_score_dbs[i];
db->destroy(db->data);
db->data = NULL;
}
free(score_db);
}
}

34
src/libosfp_score_db.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef __LIBOSFP_SCORE_DB_H__
#define __LIBOSFP_SCORE_DB_H__
#include "libosfp.h"
#include "libosfp_fingerprint.h"
typedef struct libosfp_field_score_db {
unsigned int enabled;
unsigned int type;
unsigned int entry_count;
void *data;
void *(*create)(void);
void (*destroy)(void *);
int (*add)(void *data, libosfp_score_t *, void *, unsigned int);
libosfp_score_t *(*match)(void *, void *, unsigned int);
} libosfp_field_score_db_t;
typedef struct libosfp_score_db {
unsigned int entry_count;
unsigned int perfect_score;
unsigned int os_class_entry_count[LIBOSFP_OS_CLASS_MAX];
libosfp_field_score_db_t field_score_dbs[LIBOSFP_FIELD_MAX];
} libosfp_score_db_t;
int libosfp_score_db_load(libosfp_score_db_t *score_db, char *fp_file);
int libosfp_score_db_score(libosfp_context_t *libosfp_context, libosfp_fingerprint_t *fp, libosfp_result_t *result);
libosfp_score_db_t *libosfp_score_db_create(void);
void libosfp_score_db_destroy(libosfp_score_db_t *score_db);
#endif

252
src/utarray.h Normal file
View File

@@ -0,0 +1,252 @@
/*
Copyright (c) 2008-2022, Troy D. Hanson https://troydhanson.github.io/uthash/
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* a dynamic array implementation using macros
*/
#ifndef UTARRAY_H
#define UTARRAY_H
#define UTARRAY_VERSION 2.3.0
#include <stddef.h> /* size_t */
#include <string.h> /* memset, etc */
#include <stdlib.h> /* exit */
#ifdef __GNUC__
#define UTARRAY_UNUSED __attribute__((__unused__))
#else
#define UTARRAY_UNUSED
#endif
#ifndef utarray_oom
#define utarray_oom() exit(-1)
#endif
typedef void (ctor_f)(void *dst, const void *src);
typedef void (dtor_f)(void *elt);
typedef void (init_f)(void *elt);
typedef struct {
size_t sz;
init_f *init;
ctor_f *copy;
dtor_f *dtor;
} UT_icd;
typedef struct {
unsigned i,n;/* i: index of next available slot, n: num slots */
UT_icd icd; /* initializer, copy and destructor functions */
char *d; /* n slots of size icd->sz*/
} UT_array;
#define utarray_init(a,_icd) do { \
memset(a,0,sizeof(UT_array)); \
(a)->icd = *(_icd); \
} while(0)
#define utarray_done(a) do { \
if ((a)->n) { \
if ((a)->icd.dtor) { \
unsigned _ut_i; \
for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \
(a)->icd.dtor(utarray_eltptr(a,_ut_i)); \
} \
} \
free((a)->d); \
} \
(a)->n=0; \
} while(0)
#define utarray_new(a,_icd) do { \
(a) = (UT_array*)malloc(sizeof(UT_array)); \
if ((a) == NULL) { \
utarray_oom(); \
} \
utarray_init(a,_icd); \
} while(0)
#define utarray_free(a) do { \
utarray_done(a); \
free(a); \
} while(0)
#define utarray_reserve(a,by) do { \
if (((a)->i+(by)) > (a)->n) { \
char *utarray_tmp; \
while (((a)->i+(by)) > (a)->n) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \
utarray_tmp=(char*)realloc((a)->d, (a)->n*(a)->icd.sz); \
if (utarray_tmp == NULL) { \
utarray_oom(); \
} \
(a)->d=utarray_tmp; \
} \
} while(0)
#define utarray_push_back(a,p) do { \
utarray_reserve(a,1); \
if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \
else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \
} while(0)
#define utarray_pop_back(a) do { \
if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \
else { (a)->i--; } \
} while(0)
#define utarray_extend_back(a) do { \
utarray_reserve(a,1); \
if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \
else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \
(a)->i++; \
} while(0)
#define utarray_len(a) ((a)->i)
#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL)
#define _utarray_eltptr(a,j) ((void*)((a)->d + ((a)->icd.sz * (j))))
#define utarray_insert(a,p,j) do { \
if ((j) > (a)->i) utarray_resize(a,j); \
utarray_reserve(a,1); \
if ((j) < (a)->i) { \
memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \
((a)->i - (j))*((a)->icd.sz)); \
} \
if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \
else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \
(a)->i++; \
} while(0)
#define utarray_inserta(a,w,j) do { \
if (utarray_len(w) == 0) break; \
if ((j) > (a)->i) utarray_resize(a,j); \
utarray_reserve(a,utarray_len(w)); \
if ((j) < (a)->i) { \
memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \
_utarray_eltptr(a,j), \
((a)->i - (j))*((a)->icd.sz)); \
} \
if ((a)->icd.copy) { \
unsigned _ut_i; \
for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \
(a)->icd.copy(_utarray_eltptr(a, (j) + _ut_i), _utarray_eltptr(w, _ut_i)); \
} \
} else { \
memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \
utarray_len(w)*((a)->icd.sz)); \
} \
(a)->i += utarray_len(w); \
} while(0)
#define utarray_resize(dst,num) do { \
unsigned _ut_i; \
if ((dst)->i > (unsigned)(num)) { \
if ((dst)->icd.dtor) { \
for (_ut_i = (num); _ut_i < (dst)->i; ++_ut_i) { \
(dst)->icd.dtor(_utarray_eltptr(dst, _ut_i)); \
} \
} \
} else if ((dst)->i < (unsigned)(num)) { \
utarray_reserve(dst, (num) - (dst)->i); \
if ((dst)->icd.init) { \
for (_ut_i = (dst)->i; _ut_i < (unsigned)(num); ++_ut_i) { \
(dst)->icd.init(_utarray_eltptr(dst, _ut_i)); \
} \
} else { \
memset(_utarray_eltptr(dst, (dst)->i), 0, (dst)->icd.sz*((num) - (dst)->i)); \
} \
} \
(dst)->i = (num); \
} while(0)
#define utarray_concat(dst,src) do { \
utarray_inserta(dst, src, utarray_len(dst)); \
} while(0)
#define utarray_erase(a,pos,len) do { \
if ((a)->icd.dtor) { \
unsigned _ut_i; \
for (_ut_i = 0; _ut_i < (len); _ut_i++) { \
(a)->icd.dtor(utarray_eltptr(a, (pos) + _ut_i)); \
} \
} \
if ((a)->i > ((pos) + (len))) { \
memmove(_utarray_eltptr(a, pos), _utarray_eltptr(a, (pos) + (len)), \
((a)->i - ((pos) + (len))) * (a)->icd.sz); \
} \
(a)->i -= (len); \
} while(0)
#define utarray_renew(a,u) do { \
if (a) utarray_clear(a); \
else utarray_new(a, u); \
} while(0)
#define utarray_clear(a) do { \
if ((a)->i > 0) { \
if ((a)->icd.dtor) { \
unsigned _ut_i; \
for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \
(a)->icd.dtor(_utarray_eltptr(a, _ut_i)); \
} \
} \
(a)->i = 0; \
} \
} while(0)
#define utarray_sort(a,cmp) do { \
qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \
} while(0)
#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp)
#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL)
#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : (((a)->i != utarray_eltidx(a,e)+1) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL))
#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) != 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL))
#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL)
#define utarray_eltidx(a,e) (((char*)(e) - (a)->d) / (a)->icd.sz)
/* last we pre-define a few icd for common utarrays of ints and strings */
static void utarray_str_cpy(void *dst, const void *src) {
char *const *srcc = (char *const *)src;
char **dstc = (char**)dst;
if (*srcc == NULL) {
*dstc = NULL;
} else {
*dstc = (char*)malloc(strlen(*srcc) + 1);
if (*dstc == NULL) {
utarray_oom();
} else {
strcpy(*dstc, *srcc);
}
}
}
static void utarray_str_dtor(void *elt) {
char **eltc = (char**)elt;
if (*eltc != NULL) free(*eltc);
}
static const UT_icd ut_str_icd UTARRAY_UNUSED = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor};
static const UT_icd ut_int_icd UTARRAY_UNUSED = {sizeof(int),NULL,NULL,NULL};
static const UT_icd ut_ptr_icd UTARRAY_UNUSED = {sizeof(void*),NULL,NULL,NULL};
#endif /* UTARRAY_H */

1140
src/uthash.h Normal file

File diff suppressed because it is too large Load Diff

1076
src/utlist.h Normal file

File diff suppressed because it is too large Load Diff

108
src/utringbuffer.h Normal file
View File

@@ -0,0 +1,108 @@
/*
Copyright (c) 2015-2022, Troy D. Hanson https://troydhanson.github.io/uthash/
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* a ring-buffer implementation using macros
*/
#ifndef UTRINGBUFFER_H
#define UTRINGBUFFER_H
#define UTRINGBUFFER_VERSION 2.3.0
#include <stdlib.h>
#include <string.h>
#include "utarray.h" // for "UT_icd"
typedef struct {
unsigned i; /* index of next available slot; wraps at n */
unsigned n; /* capacity */
unsigned char f; /* full */
UT_icd icd; /* initializer, copy and destructor functions */
char *d; /* n slots of size icd->sz */
} UT_ringbuffer;
#define utringbuffer_init(a, _n, _icd) do { \
memset(a, 0, sizeof(UT_ringbuffer)); \
(a)->icd = *(_icd); \
(a)->n = (_n); \
if ((a)->n) { (a)->d = (char*)malloc((a)->n * (_icd)->sz); } \
} while(0)
#define utringbuffer_clear(a) do { \
if ((a)->icd.dtor) { \
if ((a)->f) { \
unsigned _ut_i; \
for (_ut_i = 0; _ut_i < (a)->n; ++_ut_i) { \
(a)->icd.dtor(utringbuffer_eltptr(a, _ut_i)); \
} \
} else { \
unsigned _ut_i; \
for (_ut_i = 0; _ut_i < (a)->i; ++_ut_i) { \
(a)->icd.dtor(utringbuffer_eltptr(a, _ut_i)); \
} \
} \
} \
(a)->i = 0; \
(a)->f = 0; \
} while(0)
#define utringbuffer_done(a) do { \
utringbuffer_clear(a); \
free((a)->d); (a)->d = NULL; \
(a)->n = 0; \
} while(0)
#define utringbuffer_new(a,n,_icd) do { \
a = (UT_ringbuffer*)malloc(sizeof(UT_ringbuffer)); \
utringbuffer_init(a, n, _icd); \
} while(0)
#define utringbuffer_free(a) do { \
utringbuffer_done(a); \
free(a); \
} while(0)
#define utringbuffer_push_back(a,p) do { \
if ((a)->icd.dtor && (a)->f) { (a)->icd.dtor(_utringbuffer_internalptr(a,(a)->i)); } \
if ((a)->icd.copy) { (a)->icd.copy( _utringbuffer_internalptr(a,(a)->i), p); } \
else { memcpy(_utringbuffer_internalptr(a,(a)->i), p, (a)->icd.sz); }; \
if (++(a)->i == (a)->n) { (a)->i = 0; (a)->f = 1; } \
} while(0)
#define utringbuffer_len(a) ((a)->f ? (a)->n : (a)->i)
#define utringbuffer_empty(a) ((a)->i == 0 && !(a)->f)
#define utringbuffer_full(a) ((a)->f != 0)
#define _utringbuffer_real_idx(a,j) ((a)->f ? ((j) + (a)->i) % (a)->n : (j))
#define _utringbuffer_internalptr(a,j) ((void*)((a)->d + ((a)->icd.sz * (j))))
#define utringbuffer_eltptr(a,j) ((0 <= (j) && (j) < utringbuffer_len(a)) ? _utringbuffer_internalptr(a,_utringbuffer_real_idx(a,j)) : NULL)
#define _utringbuffer_fake_idx(a,j) ((a)->f ? ((j) + (a)->n - (a)->i) % (a)->n : (j))
#define _utringbuffer_internalidx(a,e) (((char*)(e) >= (a)->d) ? (((char*)(e) - (a)->d)/(a)->icd.sz) : -1)
#define utringbuffer_eltidx(a,e) _utringbuffer_fake_idx(a, _utringbuffer_internalidx(a,e))
#define utringbuffer_front(a) utringbuffer_eltptr(a,0)
#define utringbuffer_next(a,e) ((e)==NULL ? utringbuffer_front(a) : utringbuffer_eltptr(a, utringbuffer_eltidx(a,e)+1))
#define utringbuffer_prev(a,e) ((e)==NULL ? utringbuffer_back(a) : utringbuffer_eltptr(a, utringbuffer_eltidx(a,e)-1))
#define utringbuffer_back(a) (utringbuffer_empty(a) ? NULL : utringbuffer_eltptr(a, utringbuffer_len(a) - 1))
#endif /* UTRINGBUFFER_H */

88
src/utstack.h Normal file
View File

@@ -0,0 +1,88 @@
/*
Copyright (c) 2018-2022, Troy D. Hanson https://troydhanson.github.io/uthash/
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef UTSTACK_H
#define UTSTACK_H
#define UTSTACK_VERSION 2.3.0
/*
* This file contains macros to manipulate a singly-linked list as a stack.
*
* To use utstack, your structure must have a "next" pointer.
*
* ----------------.EXAMPLE -------------------------
* struct item {
* int id;
* struct item *next;
* };
*
* struct item *stack = NULL;
*
* int main() {
* int count;
* struct item *tmp;
* struct item *item = malloc(sizeof *item);
* item->id = 42;
* STACK_COUNT(stack, tmp, count); assert(count == 0);
* STACK_PUSH(stack, item);
* STACK_COUNT(stack, tmp, count); assert(count == 1);
* STACK_POP(stack, item);
* free(item);
* STACK_COUNT(stack, tmp, count); assert(count == 0);
* }
* --------------------------------------------------
*/
#define STACK_TOP(head) (head)
#define STACK_EMPTY(head) (!(head))
#define STACK_PUSH(head,add) \
STACK_PUSH2(head,add,next)
#define STACK_PUSH2(head,add,next) \
do { \
(add)->next = (head); \
(head) = (add); \
} while (0)
#define STACK_POP(head,result) \
STACK_POP2(head,result,next)
#define STACK_POP2(head,result,next) \
do { \
(result) = (head); \
(head) = (head)->next; \
} while (0)
#define STACK_COUNT(head,el,counter) \
STACK_COUNT2(head,el,counter,next) \
#define STACK_COUNT2(head,el,counter,next) \
do { \
(counter) = 0; \
for ((el) = (head); el; (el) = (el)->next) { ++(counter); } \
} while (0)
#endif /* UTSTACK_H */

407
src/utstring.h Normal file
View File

@@ -0,0 +1,407 @@
/*
Copyright (c) 2008-2022, Troy D. Hanson https://troydhanson.github.io/uthash/
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* a dynamic string implementation using macros
*/
#ifndef UTSTRING_H
#define UTSTRING_H
#define UTSTRING_VERSION 2.3.0
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#ifdef __GNUC__
#define UTSTRING_UNUSED __attribute__((__unused__))
#else
#define UTSTRING_UNUSED
#endif
#ifdef oom
#error "The name of macro 'oom' has been changed to 'utstring_oom'. Please update your code."
#define utstring_oom() oom()
#endif
#ifndef utstring_oom
#define utstring_oom() exit(-1)
#endif
typedef struct {
char *d; /* pointer to allocated buffer */
size_t n; /* allocated capacity */
size_t i; /* index of first unused byte */
} UT_string;
#define utstring_reserve(s,amt) \
do { \
if (((s)->n - (s)->i) < (size_t)(amt)) { \
char *utstring_tmp = (char*)realloc( \
(s)->d, (s)->n + (amt)); \
if (!utstring_tmp) { \
utstring_oom(); \
} \
(s)->d = utstring_tmp; \
(s)->n += (amt); \
} \
} while(0)
#define utstring_init(s) \
do { \
(s)->n = 0; (s)->i = 0; (s)->d = NULL; \
utstring_reserve(s,100); \
(s)->d[0] = '\0'; \
} while(0)
#define utstring_done(s) \
do { \
if ((s)->d != NULL) free((s)->d); \
(s)->n = 0; \
} while(0)
#define utstring_free(s) \
do { \
utstring_done(s); \
free(s); \
} while(0)
#define utstring_new(s) \
do { \
(s) = (UT_string*)malloc(sizeof(UT_string)); \
if (!(s)) { \
utstring_oom(); \
} \
utstring_init(s); \
} while(0)
#define utstring_renew(s) \
do { \
if (s) { \
utstring_clear(s); \
} else { \
utstring_new(s); \
} \
} while(0)
#define utstring_clear(s) \
do { \
(s)->i = 0; \
(s)->d[0] = '\0'; \
} while(0)
#define utstring_bincpy(s,b,l) \
do { \
utstring_reserve((s),(l)+1); \
if (l) memcpy(&(s)->d[(s)->i], b, l); \
(s)->i += (l); \
(s)->d[(s)->i]='\0'; \
} while(0)
#define utstring_concat(dst,src) \
do { \
utstring_reserve((dst),((src)->i)+1); \
if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \
(dst)->i += (src)->i; \
(dst)->d[(dst)->i]='\0'; \
} while(0)
#define utstring_len(s) ((s)->i)
#define utstring_body(s) ((s)->d)
UTSTRING_UNUSED static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) {
int n;
va_list cp;
for (;;) {
#ifdef _WIN32
cp = ap;
#else
va_copy(cp, ap);
#endif
n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp);
va_end(cp);
if ((n > -1) && ((size_t) n < (s->n-s->i))) {
s->i += n;
return;
}
/* Else try again with more space. */
if (n > -1) utstring_reserve(s,n+1); /* exact */
else utstring_reserve(s,(s->n)*2); /* 2x */
}
}
#ifdef __GNUC__
/* support printf format checking (2=the format string, 3=start of varargs) */
static void utstring_printf(UT_string *s, const char *fmt, ...)
__attribute__ (( format( printf, 2, 3) ));
#endif
UTSTRING_UNUSED static void utstring_printf(UT_string *s, const char *fmt, ...) {
va_list ap;
va_start(ap,fmt);
utstring_printf_va(s,fmt,ap);
va_end(ap);
}
/*******************************************************************************
* begin substring search functions *
******************************************************************************/
/* Build KMP table from left to right. */
UTSTRING_UNUSED static void _utstring_BuildTable(
const char *P_Needle,
size_t P_NeedleLen,
long *P_KMP_Table)
{
long i, j;
i = 0;
j = i - 1;
P_KMP_Table[i] = j;
while (i < (long) P_NeedleLen)
{
while ( (j > -1) && (P_Needle[i] != P_Needle[j]) )
{
j = P_KMP_Table[j];
}
i++;
j++;
if (i < (long) P_NeedleLen)
{
if (P_Needle[i] == P_Needle[j])
{
P_KMP_Table[i] = P_KMP_Table[j];
}
else
{
P_KMP_Table[i] = j;
}
}
else
{
P_KMP_Table[i] = j;
}
}
return;
}
/* Build KMP table from right to left. */
UTSTRING_UNUSED static void _utstring_BuildTableR(
const char *P_Needle,
size_t P_NeedleLen,
long *P_KMP_Table)
{
long i, j;
i = P_NeedleLen - 1;
j = i + 1;
P_KMP_Table[i + 1] = j;
while (i >= 0)
{
while ( (j < (long) P_NeedleLen) && (P_Needle[i] != P_Needle[j]) )
{
j = P_KMP_Table[j + 1];
}
i--;
j--;
if (i >= 0)
{
if (P_Needle[i] == P_Needle[j])
{
P_KMP_Table[i + 1] = P_KMP_Table[j + 1];
}
else
{
P_KMP_Table[i + 1] = j;
}
}
else
{
P_KMP_Table[i + 1] = j;
}
}
return;
}
/* Search data from left to right. ( Multiple search mode. ) */
UTSTRING_UNUSED static long _utstring_find(
const char *P_Haystack,
size_t P_HaystackLen,
const char *P_Needle,
size_t P_NeedleLen,
long *P_KMP_Table)
{
long i, j;
long V_FindPosition = -1;
/* Search from left to right. */
i = j = 0;
while ( (j < (int)P_HaystackLen) && (((P_HaystackLen - j) + i) >= P_NeedleLen) )
{
while ( (i > -1) && (P_Needle[i] != P_Haystack[j]) )
{
i = P_KMP_Table[i];
}
i++;
j++;
if (i >= (int)P_NeedleLen)
{
/* Found. */
V_FindPosition = j - i;
break;
}
}
return V_FindPosition;
}
/* Search data from right to left. ( Multiple search mode. ) */
UTSTRING_UNUSED static long _utstring_findR(
const char *P_Haystack,
size_t P_HaystackLen,
const char *P_Needle,
size_t P_NeedleLen,
long *P_KMP_Table)
{
long i, j;
long V_FindPosition = -1;
/* Search from right to left. */
j = (P_HaystackLen - 1);
i = (P_NeedleLen - 1);
while ( (j >= 0) && (j >= i) )
{
while ( (i < (int)P_NeedleLen) && (P_Needle[i] != P_Haystack[j]) )
{
i = P_KMP_Table[i + 1];
}
i--;
j--;
if (i < 0)
{
/* Found. */
V_FindPosition = j + 1;
break;
}
}
return V_FindPosition;
}
/* Search data from left to right. ( One time search mode. ) */
UTSTRING_UNUSED static long utstring_find(
UT_string *s,
long P_StartPosition, /* Start from 0. -1 means last position. */
const char *P_Needle,
size_t P_NeedleLen)
{
long V_StartPosition;
long V_HaystackLen;
long *V_KMP_Table;
long V_FindPosition = -1;
if (P_StartPosition < 0)
{
V_StartPosition = s->i + P_StartPosition;
}
else
{
V_StartPosition = P_StartPosition;
}
V_HaystackLen = s->i - V_StartPosition;
if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) )
{
V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1));
if (V_KMP_Table != NULL)
{
_utstring_BuildTable(P_Needle, P_NeedleLen, V_KMP_Table);
V_FindPosition = _utstring_find(s->d + V_StartPosition,
V_HaystackLen,
P_Needle,
P_NeedleLen,
V_KMP_Table);
if (V_FindPosition >= 0)
{
V_FindPosition += V_StartPosition;
}
free(V_KMP_Table);
}
}
return V_FindPosition;
}
/* Search data from right to left. ( One time search mode. ) */
UTSTRING_UNUSED static long utstring_findR(
UT_string *s,
long P_StartPosition, /* Start from 0. -1 means last position. */
const char *P_Needle,
size_t P_NeedleLen)
{
long V_StartPosition;
long V_HaystackLen;
long *V_KMP_Table;
long V_FindPosition = -1;
if (P_StartPosition < 0)
{
V_StartPosition = s->i + P_StartPosition;
}
else
{
V_StartPosition = P_StartPosition;
}
V_HaystackLen = V_StartPosition + 1;
if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) )
{
V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1));
if (V_KMP_Table != NULL)
{
_utstring_BuildTableR(P_Needle, P_NeedleLen, V_KMP_Table);
V_FindPosition = _utstring_findR(s->d,
V_HaystackLen,
P_Needle,
P_NeedleLen,
V_KMP_Table);
free(V_KMP_Table);
}
}
return V_FindPosition;
}
/*******************************************************************************
* end substring search functions *
******************************************************************************/
#endif /* UTSTRING_H */