#include #include #include #include "cJSON.h" #include "osfp_common.h" #include "osfp.h" #include "osfp_fingerprint.h" #include "osfp_log.h" #define OSFP_FINGERPRINT_FIELD_NAME_IP_ID "ip_id" #define OSFP_FINGERPRINT_FIELD_NAME_IP_TOS "ip_tos" #define OSFP_FINGERPRINT_FIELD_NAME_IP_TOTAL_LENGHT "ip_total_length" #define OSFP_FINGERPRINT_FIELD_NAME_IP_TTL "ip_ttl" #define OSFP_FINGERPRINT_FIELD_NAME_TCP_OFF "tcp_off" #define OSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP "tcp_timestamp" #define OSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP_ECHO_REPLY "tcp_timestamp_echo_reply" #define OSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_WCALING "tcp_window_scaling" #define OSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_SIZE "tcp_window_size" #define OSFP_FINGERPRINT_FIELD_NAME_TCP_FLAGS "tcp_flags" #define OSFP_FINGERPRINT_FIELD_NAME_TCP_MSS "tcp_mss" #define OSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS "tcp_options" #define OSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS_ORDERED "tcp_options_ordered" #define OSFP_FINGERPRINT_FIELD_NAME_OS "os" #define OSFP_FINGERPRINT_DEFAULT_OS_CLASS_NAME "OSFP_UNKNOWN" #define OSFP_FP_SET_FIELD(fp, field_id, v, l) do { \ (fp)->fields[(field_id)].name = osfp_fingerprint_get_field_name(field_id); \ (fp)->fields[(field_id)].enabled = 1; \ if ((fp)->value_buffer_used + (l) <= sizeof((fp)->value_buffer)) { \ memcpy(fp->value_buffer + (fp)->value_buffer_used, (v), (l)); \ (fp)->fields[(field_id)].value = (fp)->value_buffer + (fp)->value_buffer_used; \ (fp)->fields[(field_id)].value_len = (l); \ (fp)->value_buffer_used += (l); \ } else { \ (fp)->fields[(field_id)].value = NULL; \ (fp)->fields[(field_id)].value_len = 0; \ } \ } while (0) #define OSFP_FP_INIT_FIELD(fp, field_id) do { \ (fp)->fields[(field_id)].name = osfp_fingerprint_get_field_name((field_id)); \ (fp)->fields[(field_id)].enabled = 0; \ (fp)->fields[(field_id)].value = NULL; \ (fp)->fields[(field_id)].value_len = 0; \ } while (0) struct osfp_tcp_opt { unsigned char type; unsigned char len; const unsigned char *data; } osfp_tcp_opt; struct osfp_fingerprint_field fp_fields[OSFP_FIELD_MAX] = { {OSFP_FINGERPRINT_FIELD_NAME_IP_ID, 1, OSFP_FIELD_TYPE_UINT, 150, NULL, 0}, {OSFP_FINGERPRINT_FIELD_NAME_IP_TOS, 1, OSFP_FIELD_TYPE_UINT, 25, NULL, 0}, {OSFP_FINGERPRINT_FIELD_NAME_IP_TOTAL_LENGHT, 1, OSFP_FIELD_TYPE_UINT, 250, NULL, 0}, {OSFP_FINGERPRINT_FIELD_NAME_IP_TTL, 1, OSFP_FIELD_TYPE_UINT, 200, NULL, 0}, {OSFP_FINGERPRINT_FIELD_NAME_TCP_OFF, 1, OSFP_FIELD_TYPE_UINT, 250, NULL, 0}, {OSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP, 0, OSFP_FIELD_TYPE_UINT, 0, NULL, 0}, {OSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP_ECHO_REPLY,1, OSFP_FIELD_TYPE_UINT, 200, NULL, 0}, {OSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_WCALING, 1, OSFP_FIELD_TYPE_UINT, 200, NULL, 0}, {OSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_SIZE, 1, OSFP_FIELD_TYPE_UINT, 200, NULL, 0}, {OSFP_FINGERPRINT_FIELD_NAME_TCP_FLAGS, 1, OSFP_FIELD_TYPE_UINT, 25, NULL, 0}, {OSFP_FINGERPRINT_FIELD_NAME_TCP_MSS, 1, OSFP_FIELD_TYPE_UINT, 150, NULL, 0}, {OSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS, 1, OSFP_FIELD_TYPE_STRING, 400, NULL, 0}, {OSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS_ORDERED, 1, OSFP_FIELD_TYPE_STRING, 250, NULL, 0}, {OSFP_FINGERPRINT_FIELD_NAME_OS, 0, OSFP_FIELD_TYPE_STRING, 0, NULL, 0}, }; static char option_to_ascii(unsigned char type) { switch (type) { case OSFP_TCP_OPT_EOL: return 'E'; case OSFP_TCP_OPT_NOP: return 'N'; case OSFP_TCP_OPT_MSS: return 'M'; case OSFP_TCP_OPT_WSCALE: return 'W'; case OSFP_TCP_OPT_SACKOK: return 'S'; case OSFP_TCP_OPT_SACK: return 'K'; case OSFP_TCP_OPT_ECHO: return 'J'; case OSFP_TCP_OPT_ECHOREPLY: return 'F'; case OSFP_TCP_OPT_TIMESTAMP: return 'T'; case OSFP_TCP_OPT_POCONN: return 'P'; case OSFP_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 { ip_ttl = 255; } return ip_ttl; } static unsigned int decode_tcp_options(struct osfp_tcp_opt *tcp_opts, unsigned int max_opt_cnt, unsigned char *data, unsigned int len) { unsigned int offset = 0; unsigned int tcp_opt_cnt = 0; unsigned char type; unsigned char olen; unsigned char *odata; while (offset < len && tcp_opt_cnt < max_opt_cnt) { type = *(data + offset); if (type == OSFP_TCP_OPT_EOL || type == OSFP_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; } static char *osfp_fingerprint_tcp_options_to_ordered(char *tcp_options, unsigned int len) { int i; char *tcp_options_ordered; unsigned tcp_options_ordered_offset; unsigned tcp_options_offset; if (tcp_options == NULL && len == 0) { goto exit; } tcp_options_ordered = malloc(len + 1); if (tcp_options_ordered == NULL) { goto exit; } tcp_options_offset = 0; tcp_options_ordered_offset = 0; while(tcp_options_offset < len) { if (isalpha(tcp_options[tcp_options_offset])) { tcp_options_ordered[tcp_options_ordered_offset] = tcp_options[tcp_options_offset]; tcp_options_ordered_offset++; } tcp_options_offset++; } tcp_options_ordered[tcp_options_ordered_offset] = 0; return tcp_options_ordered; exit: return NULL; } int osfp_fingerprint_to_json_buf(struct osfp_fingerprint *fp, char *strbuf, unsigned int buf_len, unsigned int format) { int rlen = 0, ret, i; cJSON *root; if (fp == NULL || strbuf == NULL || buf_len == 0) { return 0; } strbuf[0] = 0; root = cJSON_CreateObject(); if (root == NULL) { return 0; } for (i = 0; i < OSFP_FIELD_MAX; i++) { if (fp->fields[i].enabled) { switch (fp_fields[i].type) { case OSFP_FIELD_TYPE_UINT: cJSON_AddNumberToObject(root, fp_fields[i].name, *(unsigned int *)fp->fields[i].value); break; case OSFP_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, format)) { return 0; } cJSON_Delete(root); return strlen(strbuf) + 1; } struct osfp_fingerprint *osfp_fingerprint_from_cjson(void *cjson_obj) { int i; cJSON *root; cJSON *field; void *value_ptr; unsigned int value_len; struct osfp_fingerprint *fp; root = (cJSON *)cjson_obj; if (root == NULL) { goto exit; } fp = calloc(1, sizeof(struct osfp_fingerprint)); if (fp == NULL) { goto exit; } for (i = 0; i < OSFP_FIELD_OS; i++) { if (0 == osfp_fingerprint_get_field_enabled(i)) { OSFP_FP_INIT_FIELD(fp, i); continue; } field = cJSON_GetObjectItem(root, osfp_fingerprint_get_field_name(i)); if (field == NULL) { OSFP_FP_INIT_FIELD(fp, i); continue; } switch (field->type) { case cJSON_Number: value_ptr = (void *)&field->valueint; value_len = sizeof(field->valueint); OSFP_FP_SET_FIELD(fp, i, value_ptr, value_len); if (i == OSFP_FIELD_IP_ID || i == OSFP_FIELD_TCP_TIMESTAMP || i == OSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY) { *(unsigned int*)(fp->fields[i].value) = !!field->valueint; } if (i == OSFP_FIELD_IP_TTL) { *(unsigned int*)(fp->fields[i].value) = compute_ip_ttl((unsigned int)field->valueint); } break; case cJSON_String: value_ptr = (void *)field->valuestring; value_len = strlen(field->valuestring) + 1; OSFP_FP_SET_FIELD(fp, i, value_ptr, value_len); break; case cJSON_NULL: //osfp_log_debug("fingerprint parse error: %s\n%s\n", field->string, cJSON_Print(root)); break; default: goto exit; } } OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_OS); return fp; exit: if (fp) { free(fp); } return NULL; } int osfp_fingerprint_from_json(struct osfp_fingerprint *fp, char *json_str) { int ret, i; cJSON *root; cJSON *field; void *value_ptr; unsigned int value_len; if (fp == NULL || json_str == NULL) { goto exit; } memset(fp, 0, sizeof(struct osfp_fingerprint)); root = cJSON_Parse(json_str); if (root == NULL) { osfp_log_error("parse json: '%s'\n", json_str); goto exit; } field = cJSON_GetObjectItem(root, osfp_fingerprint_get_field_name(OSFP_FIELD_TCP_OPTIONS_ORDERED)); if (field == NULL) { field = cJSON_GetObjectItem(root, osfp_fingerprint_get_field_name(OSFP_FIELD_TCP_OPTIONS)); if (field) { char *tcp_options_ordered_str = osfp_fingerprint_tcp_options_to_ordered(field->valuestring, strlen(field->valuestring)); if (tcp_options_ordered_str) { cJSON_AddItemToObject(root, osfp_fingerprint_get_field_name(OSFP_FIELD_TCP_OPTIONS_ORDERED), cJSON_CreateString(tcp_options_ordered_str)); free(tcp_options_ordered_str); } } } for (i = 0; i < OSFP_FIELD_OS; i++) { if (0 == osfp_fingerprint_get_field_enabled(i)) { OSFP_FP_INIT_FIELD(fp, i); continue; } field = cJSON_GetObjectItem(root, osfp_fingerprint_get_field_name(i)); if (field == NULL) { goto exit; } switch (field->type) { case cJSON_Number: value_ptr = (void *)&field->valueint; value_len = sizeof(field->valueint); OSFP_FP_SET_FIELD(fp, i, value_ptr, value_len); if (i == OSFP_FIELD_IP_ID || i == OSFP_FIELD_TCP_TIMESTAMP || i == OSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY) { *(unsigned int*)(fp->fields[i].value) = !!field->valueint; } if (i == OSFP_FIELD_IP_TTL) { *(unsigned int*)(fp->fields[i].value) = compute_ip_ttl((unsigned int)field->valueint); } break; case cJSON_String: value_ptr = (void *)field->valuestring; value_len = strlen(field->valuestring) + 1; OSFP_FP_SET_FIELD(fp, i, value_ptr, value_len); break; case cJSON_NULL: //osfp_log_debug("fingerprint parse error: %s\n%s\n", field->string, cJSON_Print(root)); break; default: goto exit; } } ret = 0; exit: if (root) { cJSON_Delete(root); } return ret; } static int osfp_fingerprinting_tcp_option(unsigned char *opt_data, unsigned int opt_len, struct osfp_fingerprint *fp) { int ret,i; unsigned int tcp_mss; unsigned int tcp_ws; unsigned int tcp_ts; unsigned int tcp_ter; unsigned int tcp_opt_cnt; struct osfp_tcp_opt tcp_opts[OSFP_TCP_OPTMAX]; char options[OSFP_TCP_OPTLENMAX]; char options_ordered[OSFP_TCP_OPTLENMAX]; unsigned int offset = 0; unsigned int maxoffset = sizeof(options) - 3; //for shortest "E," unsigned int ordered_offset = 0; unsigned int ordered_maxoffset = sizeof(options_ordered) - 1; if (opt_data == NULL || opt_len == 0 || fp == NULL) { goto exit; } tcp_opt_cnt = decode_tcp_options(tcp_opts, OSFP_TCP_OPTMAX, opt_data, opt_len); for (i = 0; i < tcp_opt_cnt && offset < maxoffset && ordered_offset < ordered_maxoffset; i++) { struct osfp_tcp_opt *opt = &tcp_opts[i]; char letter = option_to_ascii(opt->type); options[offset++] = letter; options_ordered[ordered_offset++] = letter; switch (opt->type) { case OSFP_TCP_OPT_EOL: case OSFP_TCP_OPT_NOP: break; case OSFP_TCP_OPT_MSS: if (opt->len != OSFP_TCP_OPT_MSS_LEN) { break; } tcp_mss = (unsigned int)ntohs(*(unsigned short *)opt->data); OSFP_FP_SET_FIELD(fp, OSFP_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 OSFP_TCP_OPT_WSCALE: if (opt->len != OSFP_TCP_OPT_WS_LEN) { break; } tcp_ws = (unsigned int)*(unsigned char *)opt->data; OSFP_FP_SET_FIELD(fp, OSFP_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 OSFP_TCP_OPT_TIMESTAMP: if (opt->len != OSFP_TCP_OPT_TS_LEN) { break; } tcp_ts = !!ntohl(*(unsigned int *)(opt->data)); OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_TIMESTAMP, &tcp_ts, sizeof(tcp_ts)); tcp_ter = !!ntohl(*(unsigned int *)(opt->data + 4)); OSFP_FP_SET_FIELD(fp, OSFP_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 OSFP_TCP_OPT_SACKOK: case OSFP_TCP_OPT_SACK: case OSFP_TCP_OPT_ECHO: case OSFP_TCP_OPT_ECHOREPLY: case OSFP_TCP_OPT_POCONN: case OSFP_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; } OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_OPTIONS, options, strlen(options) + 1); OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_OPTIONS_ORDERED, options_ordered, strlen(options_ordered) + 1); return 0; exit: return -1; } static int osfp_fingerprinting_tcp(struct tcphdr *tcph, unsigned int tcph_len, struct osfp_fingerprint *fp) { unsigned int tcp_off; unsigned int tcp_window_size; unsigned int tcp_flags; if (tcph == NULL || tcph_len > OSFP_TCP_DATA_OFF_MAX || fp == NULL) { goto exit; } tcp_off = tcph->doff << 2; tcp_window_size = ntohs(tcph->window); tcp_flags = *((unsigned char *)&tcph->ack_seq + 5); if (tcp_off != tcph_len) { goto exit; } OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_OFF, &tcp_off, sizeof(tcp_off)); OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_WINDOW_SIZE, &tcp_window_size, sizeof(tcp_window_size)); OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_FLAGS, &tcp_flags, sizeof(tcp_flags)); OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_TIMESTAMP); OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY); OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_WINDOW_SCALING); OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_MSS); OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_OPTIONS); OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_OPTIONS_ORDERED); // tcp options if (tcp_off > OSFP_TCP_HEADER_LEN) { osfp_fingerprinting_tcp_option((unsigned char *)tcph + OSFP_TCP_HEADER_LEN, tcp_off - OSFP_TCP_HEADER_LEN, fp); } return 0; exit: return -1; } static int osfp_fingerprinting_ipv4(struct iphdr *iph, struct osfp_fingerprint *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); OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_ID, &ip_id, sizeof(ip_id)); OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_TOS, &ip_tos, sizeof(ip_tos)); OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_TOTAL_LENGTH, &ip_total_length, sizeof(ip_total_length)); OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_TTL, &ip_ttl, sizeof(ip_ttl)); return 0; exit: return -1; } static int osfp_fingerprinting_ipv6(struct ip6_hdr *iph, struct osfp_fingerprint *fp) { if (iph == NULL || fp == NULL) { goto exit; } //unsigned int ip_id = 0; //unsigned int ip_tos = 0; unsigned int ip_total_length = OSFP_IPV6_HEADER_LEN + ntohs(iph->ip6_ctlun.ip6_un1.ip6_un1_plen); unsigned int ip_ttl = compute_ip_ttl(iph->ip6_ctlun.ip6_un1.ip6_un1_hlim); OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_TOTAL_LENGTH, &ip_total_length, sizeof(ip_total_length)); OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_TTL, &ip_ttl, sizeof(ip_ttl)); OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_IP_ID); OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_IP_TOS); return 0; exit: return -1; } int osfp_fingerprinting(unsigned char *iph, unsigned char *tcph, unsigned int tcph_len, struct osfp_fingerprint *fp, unsigned int ip_version) { int ret = OSFP_EINVAL; if (iph == NULL || tcph == NULL || tcph_len == 0 || fp == NULL) { goto exit; } fp->value_buffer_used = 0; switch (ip_version) { case 4: ret = osfp_fingerprinting_ipv4((struct iphdr *)iph, fp); break; case 6: ret = osfp_fingerprinting_ipv6((struct ip6_hdr *)iph, fp); break; default: ret = -1; goto exit; } if (ret != 0) { goto exit; } ret = osfp_fingerprinting_tcp((struct tcphdr *)tcph, tcph_len, fp); if (ret != 0) { goto exit; } OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_OS); return 0; exit: return -1; } int test_osfp_fingerprinting_ipv4(void) { int ret; char iph[] = { 0x45, 0x00, 0x00, 0x34, 0x51, 0xc4, 0x40, 0x00, 0x80, 0x06, 0xe7, 0x27, 0xc0, 0xa8, 0x73, 0x08, 0x6a, 0xb9, 0x23, 0x6e }; char tcph[] = { 0xc1, 0xbd, 0x00, 0x50, 0x3d, 0x58, 0x51, 0x60, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20, 0x00, 0x3d, 0x3a, 0x00, 0x00, 0x02, 0x04, 0x04, 0xec, 0x01, 0x03, 0x03, 0x08, 0x01, 0x01, 0x04, 0x02 }; char str_buf[2048] = ""; const char *target = "{\"ip_id\":1,\"ip_tos\":0,\"ip_total_length\":52,\"ip_ttl\":128,\"tcp_off\":32,\"tcp_timestamp\":null,\"tcp_timestamp_echo_reply\":null,\"tcp_window_scaling\":8,\"tcp_window_size\":8192,\"tcp_flags\":2,\"tcp_mss\":1260,\"tcp_options\":\"M1260,N,W8,N,N,S,\",\"tcp_options_ordered\":\"MNWNNS\",\"os\":null}"; struct osfp_fingerprint fp = {0}; ret = osfp_fingerprinting(iph, tcph, 32, &fp, 4); if (ret != 0) { goto exit; } ret = osfp_fingerprint_to_json_buf(&fp, str_buf, 2048, 0); if (ret <= 0) { goto exit; } ret = -1; if (0 != memcmp(str_buf, target, strlen(target))) { goto exit; } return 0; exit: return ret; } int test_osfp_fingerprinting_ipv6(void) { int ret; char iph[] = { 0x45, 0x00, 0x00, 0x34, 0x51, 0xc4, 0x40, 0x00, 0x80, 0x06, 0xe7, 0x27, 0xc0, 0xa8, 0x73, 0x08, 0x6a, 0xb9, 0x23, 0x6e }; char tcph[] = { 0xc1, 0xbd, 0x00, 0x50, 0x3d, 0x58, 0x51, 0x60, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20, 0x00, 0x3d, 0x3a, 0x00, 0x00, 0x02, 0x04, 0x04, 0xec, 0x01, 0x03, 0x03, 0x08, 0x01, 0x01, 0x04, 0x02 }; char str_buf[2048] = ""; const char *target = "{\"ip_id\":1,\"ip_tos\":0,\"ip_total_length\":52,\"ip_ttl\":128,\"tcp_off\":32,\"tcp_timestamp\":null,\"tcp_timestamp_echo_reply\":null,\"tcp_window_scaling\":8,\"tcp_window_size\":8192,\"tcp_flags\":2,\"tcp_mss\":1260,\"tcp_options\":\"M1260,N,W8,N,N,S,\",\"tcp_options_ordered\":\"MNWNNS\",\"os\":null}"; struct osfp_fingerprint fp = {0}; ret = osfp_fingerprinting(iph, tcph, 32, &fp, 4); if (ret != 0) { goto exit; } ret = osfp_fingerprint_to_json_buf(&fp, str_buf, 2048, 0); if (ret <= 0) { goto exit; } ret = -1; if (0 != memcmp(str_buf, target, strlen(target))) { goto exit; } return 0; exit: return ret; } int test_osfp_fingerprinting_tcp_option(void) { int ret; char tcp_opt[] = { 0x02, 0x04, 0x04, 0xec,0x01, 0x03, 0x03, 0x08, 0x01, 0x01, 0x04, 0x02 }; char str_buf[2048] = ""; const char *target_options = "M1260,N,W8,N,N,S,"; const char *target_options_ordered = "MNWNNS"; struct osfp_fingerprint fp = {0}; ret = osfp_fingerprinting_tcp_option((unsigned char *)tcp_opt, 12, &fp); if (ret != 0) { goto exit; } ret = -1; if (fp.fields[OSFP_FIELD_TCP_OPTIONS].value_len != strlen(target_options) + 1) { goto exit; } if (0 != memcmp(fp.fields[OSFP_FIELD_TCP_OPTIONS].value, target_options, strlen(target_options) + 1)) { goto exit; } if (fp.fields[OSFP_FIELD_TCP_OPTIONS_ORDERED].value_len != strlen(target_options_ordered) + 1) { goto exit; } if (0 != memcmp(fp.fields[OSFP_FIELD_TCP_OPTIONS_ORDERED].value, target_options_ordered, strlen(target_options_ordered) + 1)) { goto exit; } return 0; exit: return ret; }