mirror of
https://github.com/vel21ripn/nDPI.git
synced 2026-05-06 03:45:32 +00:00
509 lines
12 KiB
C
509 lines
12 KiB
C
/*
|
|
* btlib.c
|
|
*
|
|
* Copyright (C) 2011-15 - ntop.org
|
|
* Contributed by Vitaly Lavrov <vel21ripn@gmail.com>
|
|
*
|
|
* This file is part of nDPI, an open source deep packet inspection
|
|
* library based on the OpenDPI and PACE technology by ipoque GmbH
|
|
*
|
|
* nDPI is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* nDPI is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with nDPI. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#ifndef NDPI_NO_STD_INC
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
|
|
typedef unsigned char u_int8_t;
|
|
typedef unsigned short int u_int16_t;
|
|
typedef unsigned long long int u_int64_t;
|
|
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
|
|
typedef signed long long int i_int64_t;
|
|
|
|
#include "btlib.h"
|
|
|
|
#ifndef __KERNEL__
|
|
|
|
int bt_parse_debug = 0;
|
|
|
|
static char *printXb(char *s,const u_int8_t *b,int l) {
|
|
int i;
|
|
for(i=0; i < l; i++)
|
|
snprintf(&s[i*2],41,"%02x",b[i]);
|
|
return s;
|
|
}
|
|
|
|
static char *print20b(char *s,const u_int8_t *b) {
|
|
snprintf(s,41,"%08x%08x%08x%08x%08x",
|
|
htonl(*(u_int32_t*)b),
|
|
htonl(*(u_int32_t*)(b+4)),
|
|
htonl(*(u_int32_t*)(b+8)),
|
|
htonl(*(u_int32_t*)(b+12)),
|
|
htonl(*(u_int32_t*)(b+16)));
|
|
return s;
|
|
}
|
|
|
|
static char *print_id_ip_p(char *s, const struct bt_nodes_data *b) {
|
|
u_int8_t *p = (void*)b;
|
|
print20b(s,b->id);
|
|
snprintf(s+40,39," %d.%d.%d.%d:%u",
|
|
p[20], p[21], p[22], p[23], htons(b->port));
|
|
return s;
|
|
}
|
|
|
|
static char *print_ip_p(char *s, const struct bt_ipv4p *b,int np) {
|
|
const u_int8_t *p = (const void*)b;
|
|
snprintf(s,39,!np ? "%d.%d.%d.%d:%u":"%d.%d.%d.%d",
|
|
p[0], p[1], p[2], p[3], htons(b->port));
|
|
return s;
|
|
}
|
|
|
|
static char *print_ip6_p(char *s, const struct bt_ipv6p *b,int np) {
|
|
u_int16_t *p = (void*)b;
|
|
snprintf(s,79,!np ? "%x:%x:%x:%x:%x:%x:%x:%x.%u":"%x:%x:%x:%x:%x:%x:%x:%x",
|
|
htons(p[0]), htons(p[1]), htons(p[2]), htons(p[3]),
|
|
htons(p[4]), htons(p[5]), htons(p[6]), htons(p[7]),
|
|
htons(b->port));
|
|
return s;
|
|
}
|
|
|
|
static char *print_id_ip6_p(char *s,const struct bt_nodes6_data *b) {
|
|
return print_ip6_p(s,(struct bt_ipv6p *)&b->ip,0);
|
|
}
|
|
|
|
|
|
void dump_bt_proto_struct(struct bt_parse_protocol *p) {
|
|
char b20h[128];
|
|
int i;
|
|
|
|
if(p->y_e && p->e_msg) {
|
|
printf("Error %s/%u\n", p->e_msg, p->e_len);
|
|
}
|
|
if(p->y_q) {
|
|
printf("Query ");
|
|
if(p->q_ping) printf("ping\n");
|
|
if(p->q_g_peers) printf("get_peers\n");
|
|
if(p->q_f_node) printf("find_node\n");
|
|
if(p->q_a_peer) printf("announce_peer\n");
|
|
}
|
|
if(p->y_r)
|
|
printf("Reply\n");
|
|
|
|
if(p->t) printf("\tt\t%llx\n",p->t);
|
|
if(p->v) printf("\tv\t%llx\n",p->v);
|
|
if(p->ip) printf("\tIP\t%s\n",print_ip_p(b20h,p->ip,0));
|
|
|
|
if(p->a.port) printf("\tport\t%d\n",htons(p->a.port));
|
|
if(p->a.id) printf("\tID\t%s\n",print20b(b20h,p->a.id));
|
|
if(p->a.target) printf("\ttarget\t%s\n",print20b(b20h,p->a.target));
|
|
if(p->a.token) printf("\ttoken\t%s\n",printXb(b20h,p->a.token,p->a.t_len));
|
|
if(p->a.info_hash) printf("\ti_hash\t%s\n",print20b(b20h,p->a.info_hash));
|
|
if(p->a.name && p->a.name_len) printf("\tname\t%.*s\n",p->a.name_len,p->a.name);
|
|
|
|
if(p->r.ip) printf("\tip\t%s\n",print_ip_p(b20h,p->r.ip,1));
|
|
if(p->r.port) printf("\tport\t%d\n",htons(p->r.port));
|
|
if(p->r.id) printf("\tID\t%s\n",print20b(b20h,p->r.id));
|
|
if(p->r.token) printf("\ttoken\t%s\n",printXb(b20h,p->r.token,p->r.t_len));
|
|
if(p->r.name && p->r.name_len) printf("\tname\t%.*s\n",p->r.name_len,p->r.name);
|
|
if(p->r.values && p->r.nv) {
|
|
struct bt_ipv4p2 *n = (struct bt_ipv4p2 *)p->r.values;
|
|
for(i=0;i < p->r.nv; i++,n++) {
|
|
printf("\tvalues\t%s\n", print_ip_p(b20h,&n->d,0));
|
|
}
|
|
}
|
|
if(p->r.values6 && p->r.nv6) {
|
|
struct bt_ipv6p2 *n = (struct bt_ipv6p2 *)p->r.values6;
|
|
for(i=0;i < p->r.nv6; i++,n++) {
|
|
printf("\tvalues6\t%s\n", print_ip6_p(b20h,&n->d,0));
|
|
}
|
|
}
|
|
if(p->r.nodes && p->r.nn) {
|
|
for(i=0;i < p->r.nn; i++) {
|
|
printf("\tnodes\t%s\n",print_id_ip_p(b20h,p->r.nodes+i));
|
|
}
|
|
}
|
|
if(p->r.nodes6 && p->r.nn6) {
|
|
for(i=0;i < p->r.nn6; i++) {
|
|
printf("\tnodes6\t%s\n",print_id_ip6_p(b20h,p->r.nodes6+i));
|
|
}
|
|
}
|
|
|
|
if(p->peers && p->n_peers) {
|
|
for(i=0;i < p->n_peers; i++) {
|
|
printf("\tpeers\t%s\n",print_ip_p(b20h,p->peers+i,0));
|
|
}
|
|
}
|
|
|
|
if(p->interval) printf("\tinterval\t%d\n",p->interval);
|
|
if(p->min_interval) printf("\tmin interval\t%d\n",p->min_interval);
|
|
}
|
|
|
|
static void _print_safe_str(char *msg,char *k,const u_int8_t *s,size_t l) {
|
|
static const char *th="0123456789abcdef?";
|
|
char *buf = (char*)ndpi_malloc((size_t)(l*3+2));
|
|
|
|
int sl = l;
|
|
if(buf) {
|
|
char *b = buf;
|
|
for(;l > 0; s++,l--) {
|
|
if(*s < ' ' || *s >= 127) {
|
|
*b++ = '%';
|
|
*b++ = th[(*s >> 4)&0xf];
|
|
*b++ = th[(*s)&0xf];
|
|
} else *b++ = *s;
|
|
}
|
|
*b = 0;
|
|
|
|
printf("%s %s %s len %d\n",msg,k,buf ? buf:"",sl);
|
|
|
|
ndpi_free(buf);
|
|
}
|
|
}
|
|
|
|
static void print_safe_str(char *msg,bt_parse_data_cb_t *cbd) {
|
|
_print_safe_str(msg,cbd->buf,cbd->v.s.s,cbd->v.s.l);
|
|
}
|
|
#define DEBUG_TRACE(cmd) { if(bt_parse_debug) cmd; }
|
|
#else
|
|
#define DEBUG_TRACE(cmd,args...)
|
|
#endif /* __KERNEL */
|
|
|
|
#define STREQ(a,b) !strcmp(a,b)
|
|
|
|
|
|
void cb_data(bt_parse_data_cb_t *cbd,int *ret) {
|
|
struct bt_parse_protocol *p = &(cbd->p);
|
|
const u_int8_t *s;
|
|
const char *ss;
|
|
|
|
if(cbd->t == 0) return;
|
|
|
|
if(cbd->t == 1) {
|
|
|
|
DEBUG_TRACE(printf("%s %lld\n",cbd->buf,cbd->v.i));
|
|
|
|
if(STREQ(cbd->buf,"a.port")) {
|
|
p->a.port = (u_int16_t)(cbd->v.i & 0xffff);
|
|
return;
|
|
}
|
|
if(
|
|
STREQ(cbd->buf,"a.implied_port") ||
|
|
STREQ(cbd->buf,"a.noseed") ||
|
|
STREQ(cbd->buf,"a.scrape") ||
|
|
STREQ(cbd->buf,"a.seed") ||
|
|
STREQ(cbd->buf,"a.vote")
|
|
) {
|
|
return;
|
|
}
|
|
if(STREQ(cbd->buf,"r.port") || STREQ(cbd->buf,"r.p")) {
|
|
p->r.port = (u_int16_t)(cbd->v.i & 0xffff);
|
|
return;
|
|
}
|
|
if(STREQ(cbd->buf,"interval")) {
|
|
p->interval = (u_int16_t)(cbd->v.i & 0x7fffffff);
|
|
p->h_int = 1;
|
|
return;
|
|
}
|
|
if(STREQ(cbd->buf,"min interval")) {
|
|
p->min_interval = (u_int16_t)(cbd->v.i & 0x7fffffff);
|
|
p->h_mint = 1;
|
|
return;
|
|
}
|
|
DEBUG_TRACE(printf("UNKNOWN %s %lld\n",cbd->buf,cbd->v.i));
|
|
return;
|
|
}
|
|
if(cbd->t != 2) {
|
|
DEBUG_TRACE(printf("BUG! t=%d %s\n",cbd->t,cbd->buf));
|
|
return;
|
|
}
|
|
DEBUG_TRACE(print_safe_str("",cbd));
|
|
|
|
s = cbd->v.s.s;
|
|
ss = (char *)s;
|
|
|
|
if(STREQ(cbd->buf,"a.id")) {
|
|
p->a.id = s;
|
|
return;
|
|
}
|
|
if(STREQ(cbd->buf,"a.info_hash")) {
|
|
p->a.info_hash = s;
|
|
return;
|
|
}
|
|
if(STREQ(cbd->buf,"a.target")) {
|
|
p->a.target = s;
|
|
return;
|
|
}
|
|
if(STREQ(cbd->buf,"a.token")) {
|
|
p->a.token = s;
|
|
p->a.t_len = cbd->v.s.l;
|
|
return;
|
|
}
|
|
if(STREQ(cbd->buf,"a.name")) {
|
|
p->a.name = s;
|
|
p->a.name_len = cbd->v.s.l;
|
|
return;
|
|
}
|
|
if(STREQ(cbd->buf,"a.want")) {
|
|
return;
|
|
}
|
|
|
|
if(STREQ(cbd->buf,"r.id")) {
|
|
p->r.id = s;
|
|
return;
|
|
}
|
|
if(STREQ(cbd->buf,"r.ip")) {
|
|
if(cbd->v.s.l != 4) {
|
|
DEBUG_TRACE(printf("BUG! r.ip with port\n"));
|
|
return;
|
|
}
|
|
p->r.ip = (struct bt_ipv4p *)s;
|
|
return;
|
|
}
|
|
if(STREQ(cbd->buf,"r.token")) {
|
|
p->r.token = s;
|
|
p->r.t_len = cbd->v.s.l;
|
|
return;
|
|
}
|
|
if(STREQ(cbd->buf,"r.values")) {
|
|
if(cbd->v.s.l == 18) {
|
|
if(!p->r.values6) {
|
|
p->r.values6 = s;
|
|
p->r.nv6 = 1;
|
|
} else {
|
|
if(s != p->r.values6+(p->r.nv6*21)) {
|
|
// DEBUG_TRACE(printf("BUG! r.values6 not in list! %08x %08x \n", p->r.values+(p->r.nv6*21),s));
|
|
return;
|
|
}
|
|
p->r.nv6++;
|
|
}
|
|
return;
|
|
}
|
|
if(cbd->v.s.l == 6) {
|
|
if(!p->r.values) {
|
|
p->r.values = s;
|
|
p->r.nv = 1;
|
|
} else {
|
|
if(s != p->r.values+(p->r.nv*8)) {
|
|
// DEBUG_TRACE(printf("BUG! r.values not in list! %u \n",s-p->r.values+(p->r.nv*8)));
|
|
return;
|
|
}
|
|
p->r.nv++;
|
|
}
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(STREQ(cbd->buf,"r.name") || STREQ(cbd->buf,"r.n")) {
|
|
p->r.name = s;
|
|
p->r.name_len = cbd->v.s.l;
|
|
return;
|
|
}
|
|
if(STREQ(cbd->buf,"r.nodes")) {
|
|
if(cbd->v.s.l % 26) {
|
|
// DEBUG_TRACE(printf("BUG! r.nodes length %d not %% 26\n",cbd->v.s.l));
|
|
return;
|
|
}
|
|
p->r.nodes = (struct bt_nodes_data *)s;
|
|
p->r.nn = cbd->v.s.l / 26;
|
|
return;
|
|
}
|
|
if(STREQ(cbd->buf,"r.nodes6")) {
|
|
if(cbd->v.s.l % 38) {
|
|
// DEBUG_TRACE(printf("BUG! r.nodes length %d not %% 38\n",cbd->v.s.l));
|
|
return;
|
|
}
|
|
p->r.nodes6 = (struct bt_nodes6_data *)s;
|
|
p->r.nn6 = cbd->v.s.l / 38;
|
|
return;
|
|
}
|
|
|
|
if(cbd->buf[0] == 'y' && !cbd->buf[1]) {
|
|
if(cbd->v.s.l != 1) return;
|
|
if(*ss == 'q') { p->y_q = 1; return; }
|
|
if(*ss == 'r') { p->y_r = 1; return; }
|
|
if(*ss == 'e') { p->y_e = 1; return; }
|
|
return;
|
|
}
|
|
if(cbd->buf[0] == 'q' && !cbd->buf[1]) {
|
|
if(!strncmp(ss,"announce_peer",13)) {
|
|
p->q_a_peer = 1;
|
|
return;
|
|
}
|
|
if(!strncmp(ss,"find_node",9)) {
|
|
p->q_f_node = 1;
|
|
return;
|
|
}
|
|
if(!strncmp(ss,"get_peers",9)) {
|
|
p->q_g_peers = 1;
|
|
return;
|
|
}
|
|
if(!strncmp(ss,"ping",4)) {
|
|
p->q_ping = 1;
|
|
return;
|
|
}
|
|
if(!strncmp(ss,"vote",4)) {
|
|
return;
|
|
}
|
|
}
|
|
if(STREQ(cbd->buf,"ip")) {
|
|
if(cbd->v.s.l != 6) {
|
|
// DEBUG_TRACE(printf("BUG! r.ip w/o port\n"));
|
|
}
|
|
p->ip = (struct bt_ipv4p *)s;
|
|
p->h_ip = 1;
|
|
return;
|
|
}
|
|
if(STREQ(cbd->buf,"peers")) {
|
|
if(cbd->v.s.l % 6) return;
|
|
p->peers = (struct bt_ipv4p *)s;
|
|
p->n_peers = cbd->v.s.l / 6;
|
|
return;
|
|
}
|
|
if((*cbd->buf == 't' || *cbd->buf == 'v') && !cbd->buf[1]) {
|
|
u_int64_t d = *(u_int64_t*)s;
|
|
switch(cbd->v.s.l) {
|
|
case 2:
|
|
d &= 0xffffllu; d = htons(d); break;
|
|
case 4:
|
|
d &= 0xffffffffllu; d = htonl(d); break;
|
|
case 6:
|
|
d &= 0xffffffffffffllu; d = (htonl(d & 0xffffffff) << 16) |
|
|
(htons(d >> 32) & 0xffff);
|
|
break;
|
|
case 8: d = ((u_int64_t)htonl(d & 0xffffffff) << 32) |
|
|
htonl(d >> 32);
|
|
break;
|
|
default: d = 0;
|
|
}
|
|
if(*cbd->buf == 'v') cbd->p.v = d;
|
|
else cbd->p.t = d;
|
|
return;
|
|
}
|
|
|
|
if(cbd->buf[0] == 'e' && !cbd->buf[0]) {
|
|
p->e_msg = s;
|
|
p->e_len = cbd->v.s.l;
|
|
return;
|
|
}
|
|
// DEBUG_TRACE(print_safe_str("UKNOWN",cbd));
|
|
}
|
|
|
|
|
|
const u_int8_t *bt_decode(const u_int8_t *b, size_t *l, int *ret, bt_parse_data_cb_t *cbd) {
|
|
|
|
unsigned int n=0,neg=0;
|
|
i_int64_t d = 0;
|
|
register u_int8_t c;
|
|
|
|
if(*l == 0) return NULL;
|
|
if(cbd->level > BDEC_MAXDEPT) goto bad_data;
|
|
c = *b++; (*l)--;
|
|
if(c == 'i') { // integer
|
|
while(*l) {
|
|
c = *b++; (*l)--;
|
|
n++;
|
|
if(c == '-') {
|
|
if(n != 1) goto bad_data;
|
|
n--;
|
|
neg=1;
|
|
continue;
|
|
}
|
|
if(c >= '0' && c <= '9') {
|
|
if(c == '0' && n > 1 && !d && *b != 'e') goto bad_data;
|
|
d *= 10;
|
|
d += c-'0';
|
|
continue;
|
|
}
|
|
if(c != 'e') goto bad_data;
|
|
break;
|
|
}
|
|
if(neg) d=-d;
|
|
cbd->t = 1;
|
|
cbd->v.i = neg ? -d:d;
|
|
return b;
|
|
}
|
|
if(c >= '1' && c <= '9') { //string
|
|
d=c-'0';
|
|
while(*l) {
|
|
c = *b++; (*l)--;
|
|
n++;
|
|
if(c >= '0' && c <= '9') {
|
|
if(c == '0' && n > 1 && d == 0) goto bad_data;
|
|
d *= 10;
|
|
d += c-'0';
|
|
continue;
|
|
}
|
|
if(c != ':') goto bad_data;
|
|
break;
|
|
}
|
|
if(d > *l) goto bad_data;
|
|
cbd->t = 2;
|
|
cbd->v.s.s = b;
|
|
cbd->v.s.l = d;
|
|
b += d;
|
|
*l -= d;
|
|
return b;
|
|
}
|
|
if(c == 'l') {
|
|
cbd->level++;
|
|
do {
|
|
b = bt_decode(b,l,ret,cbd);
|
|
if(*ret < 0 || *l == 0) goto bad_data;
|
|
cb_data(cbd,ret);
|
|
if(*ret < 0) goto bad_data;
|
|
cbd->t = 0;
|
|
} while (*b != 'e' && *l != 0);
|
|
b++; (*l)--;
|
|
cbd->level--;
|
|
return b;
|
|
}
|
|
if(c == 'd') {
|
|
cbd->level++;
|
|
do {
|
|
char *ls = cbd->buf + strlen(cbd->buf);
|
|
int l1 = ls != cbd->buf ? 1:0;
|
|
if(!(*b >= '1' && *b <= '9')) goto bad_data;
|
|
b = bt_decode(b,l,ret,cbd);
|
|
if(*ret < 0 || *l == 0) goto bad_data;
|
|
if(ls+cbd->v.s.l+l1 < &cbd->buf[sizeof(cbd->buf)-1]) {
|
|
if(l1) ls[0]='.';
|
|
strncpy(ls+l1,(char *)cbd->v.s.s,cbd->v.s.l);
|
|
ls[cbd->v.s.l+l1]=0;
|
|
}
|
|
b = bt_decode(b,l,ret,cbd);
|
|
if(*ret < 0 || *l == 0) goto bad_data;
|
|
cb_data(cbd,ret);
|
|
if(*ret < 0) goto bad_data;
|
|
cbd->t = 0;
|
|
*ls = 0;
|
|
} while (*b != 'e' && l != 0);
|
|
|
|
b++; (*l)--;
|
|
cbd->level--;
|
|
return b;
|
|
}
|
|
bad_data:
|
|
*ret=-1;
|
|
return b;
|
|
}
|