v0.0.0
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
655
example/osfp_example.c
Normal 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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
3119
src/cJSON.c
Normal file
File diff suppressed because it is too large
Load Diff
300
src/cJSON.h
Normal file
300
src/cJSON.h
Normal 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
|
||||
207
src/libosfp.c
207
src/libosfp.c
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
415
src/libosfp_fingerprint.c
Normal 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
106
src/libosfp_fingerprint.h
Normal 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
58
src/libosfp_log.c
Normal 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
20
src/libosfp_log.h
Normal 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
539
src/libosfp_score_db.c
Normal 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
34
src/libosfp_score_db.h
Normal 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
252
src/utarray.h
Normal 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
1140
src/uthash.h
Normal file
File diff suppressed because it is too large
Load Diff
1076
src/utlist.h
Normal file
1076
src/utlist.h
Normal file
File diff suppressed because it is too large
Load Diff
108
src/utringbuffer.h
Normal file
108
src/utringbuffer.h
Normal 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
88
src/utstack.h
Normal 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
407
src/utstring.h
Normal 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 */
|
||||
Reference in New Issue
Block a user