mirror of
https://github.com/zeek/zeek.git
synced 2025-10-06 16:48:19 +00:00

In many cases, classes derived from TCP_ApplicationAnalyzer were *overloading* instead of overriding EndpointEOF causing the parent class version to become hidden in the child and also for the child's version to never to called polymorphically from TCP_Analyzer::EndpointEOF. Clang gave a warning in each case.
800 lines
17 KiB
C++
800 lines
17 KiB
C++
// This code contributed by Nadi Sarrar.
|
|
|
|
#include "BitTorrentTracker.h"
|
|
#include "TCP_Reassembler.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <regex.h>
|
|
|
|
#include <algorithm>
|
|
|
|
# define FMT_INT "%" PRId64
|
|
# define FMT_UINT "%" PRIu64
|
|
|
|
static TableType* bt_tracker_headers = 0;
|
|
static RecordType* bittorrent_peer;
|
|
static TableType* bittorrent_peer_set;
|
|
static RecordType* bittorrent_benc_value;
|
|
static TableType* bittorrent_benc_dir;
|
|
|
|
BitTorrentTracker_Analyzer::BitTorrentTracker_Analyzer(Connection* c)
|
|
: TCP_ApplicationAnalyzer(AnalyzerTag::BitTorrentTracker, c)
|
|
{
|
|
if ( ! bt_tracker_headers )
|
|
{
|
|
bt_tracker_headers =
|
|
internal_type("bt_tracker_headers")->AsTableType();
|
|
bittorrent_peer =
|
|
internal_type("bittorrent_peer")->AsRecordType();
|
|
bittorrent_peer_set =
|
|
internal_type("bittorrent_peer_set")->AsTableType();
|
|
bittorrent_benc_value =
|
|
internal_type("bittorrent_benc_value")->AsRecordType();
|
|
bittorrent_benc_dir =
|
|
internal_type("bittorrent_benc_dir")->AsTableType();
|
|
}
|
|
|
|
keep_alive = false;
|
|
|
|
req_state = BTT_REQ_GET;
|
|
req_buf[sizeof(req_buf) - 1] = 0;
|
|
req_buf_pos = req_buf;
|
|
req_buf_len = 0;
|
|
req_val_uri = 0;
|
|
req_val_headers = new TableVal(bt_tracker_headers);
|
|
|
|
res_state = BTT_RES_STATUS;
|
|
res_allow_blank_line = false;
|
|
res_buf[sizeof(res_buf) - 1] = 0;
|
|
res_buf_pos = res_buf;
|
|
res_buf_len = 0;
|
|
res_status = 0;
|
|
res_val_headers = new TableVal(bt_tracker_headers);
|
|
res_val_peers = new TableVal(bittorrent_peer_set);
|
|
res_val_benc = new TableVal(bittorrent_benc_dir);
|
|
|
|
InitBencParser();
|
|
|
|
stop_orig = false;
|
|
stop_resp = false;
|
|
}
|
|
|
|
BitTorrentTracker_Analyzer::~BitTorrentTracker_Analyzer()
|
|
{
|
|
Unref(req_val_uri);
|
|
Unref(req_val_headers);
|
|
|
|
Unref(res_val_headers);
|
|
Unref(res_val_peers);
|
|
Unref(res_val_benc);
|
|
|
|
benc_stack.clear();
|
|
benc_count.clear();
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::Done()
|
|
{
|
|
TCP_ApplicationAnalyzer::Done();
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::DeliverStream(int len, const u_char* data,
|
|
bool orig)
|
|
{
|
|
TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
|
|
|
|
assert(TCP());
|
|
|
|
if ( TCP()->IsPartial() )
|
|
// punt on partial.
|
|
return;
|
|
|
|
if ( orig )
|
|
ClientRequest(len, data);
|
|
else
|
|
ServerReply(len, data);
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::ClientRequest(int len, const u_char* data)
|
|
{
|
|
if ( stop_orig )
|
|
return;
|
|
|
|
if ( req_buf_len + len > sizeof(req_buf) - 1 )
|
|
{
|
|
ProtocolViolation("BitTorrentTracker: request message too long");
|
|
stop_orig = true;
|
|
return;
|
|
}
|
|
|
|
memcpy(&req_buf[req_buf_len], data, len);
|
|
req_buf_len += len;
|
|
req_buf[req_buf_len] = 0;
|
|
|
|
while ( req_buf_pos < req_buf + req_buf_len )
|
|
{
|
|
char* lf = strchr(req_buf_pos, '\n');
|
|
if ( ! lf )
|
|
break;
|
|
*lf = 0;
|
|
|
|
char* cr = strrchr(req_buf_pos, '\r');
|
|
if ( cr )
|
|
*cr = 0;
|
|
|
|
if ( ! ParseRequest(req_buf_pos) )
|
|
return;
|
|
|
|
req_buf_pos = lf + 1;
|
|
|
|
if ( req_state == BTT_REQ_DONE && keep_alive )
|
|
{
|
|
req_state = BTT_REQ_GET;
|
|
req_buf_len -= (req_buf_pos - req_buf);
|
|
memmove(req_buf, req_buf_pos, req_buf_len);
|
|
req_buf_pos = req_buf;
|
|
req_val_headers =
|
|
new TableVal(bt_tracker_headers);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::ServerReply(int len, const u_char* data)
|
|
{
|
|
if ( stop_resp )
|
|
return;
|
|
|
|
if ( res_state == BTT_RES_DONE )
|
|
// We are done already, i.e. state != 200.
|
|
return;
|
|
|
|
if ( res_buf_len + len > sizeof(res_buf) - 1 )
|
|
{
|
|
ProtocolViolation("BitTorrentTracker: response message too long");
|
|
stop_resp = true;
|
|
return;
|
|
}
|
|
|
|
memcpy(&res_buf[res_buf_len], data, len);
|
|
res_buf_len += len;
|
|
res_buf[res_buf_len] = 0;
|
|
|
|
while ( true )
|
|
{
|
|
while ( res_state != BTT_RES_BODY &&
|
|
res_buf_pos < res_buf + res_buf_len )
|
|
{
|
|
char* lf = strchr(res_buf_pos, '\n');
|
|
if ( ! lf )
|
|
break;
|
|
*lf = 0;
|
|
|
|
char* cr = strrchr(res_buf_pos, '\r');
|
|
if ( cr )
|
|
*cr = 0;
|
|
|
|
if ( ! ParseResponse(res_buf_pos) )
|
|
return;
|
|
|
|
res_buf_pos = lf + 1;
|
|
}
|
|
|
|
if ( res_state != BTT_RES_BODY ||
|
|
res_buf_pos >= res_buf + res_buf_len )
|
|
break;
|
|
|
|
ResponseBody();
|
|
|
|
if ( res_state != BTT_RES_DONE ||
|
|
res_status != 200 || ! keep_alive )
|
|
break;
|
|
|
|
res_state = BTT_RES_STATUS;
|
|
res_allow_blank_line = true;
|
|
res_buf_len -= res_buf_pos - res_buf;
|
|
memmove(res_buf, res_buf_pos, res_buf_len);
|
|
res_buf_pos = res_buf;
|
|
res_status = 0;
|
|
|
|
res_val_headers = new TableVal(bt_tracker_headers);
|
|
res_val_peers = new TableVal(bittorrent_peer_set);
|
|
res_val_benc = new TableVal(bittorrent_benc_dir);
|
|
|
|
InitBencParser();
|
|
}
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::Undelivered(int seq, int len, bool orig)
|
|
{
|
|
TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
|
|
|
|
ProtocolViolation("BitTorrentTracker: cannot recover from content gap");
|
|
|
|
if ( orig )
|
|
stop_orig = true;
|
|
else
|
|
stop_resp = true;
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::EndpointEOF(bool is_orig)
|
|
{
|
|
TCP_ApplicationAnalyzer::EndpointEOF(is_orig);
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::InitBencParser(void)
|
|
{
|
|
benc_stack.clear();
|
|
benc_count.clear();
|
|
|
|
benc_state = BENC_STATE_EMPTY;
|
|
benc_raw = 0;
|
|
benc_raw_type = BENC_TYPE_NONE;
|
|
benc_raw_len = 0;
|
|
benc_key = 0;
|
|
benc_key_len = 0;
|
|
benc_strlen = 0;
|
|
benc_str = 0;
|
|
benc_str_len = 0;
|
|
benc_str_have = 0;
|
|
benc_int = 0;
|
|
benc_int_val = 0;
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::DeliverWeird(const char* msg, bool orig)
|
|
{
|
|
if ( bt_tracker_weird )
|
|
{
|
|
val_list* vl = new val_list;
|
|
vl->append(BuildConnVal());
|
|
vl->append(new Val(orig, TYPE_BOOL));
|
|
vl->append(new StringVal(msg));
|
|
ConnectionEvent(bt_tracker_weird, vl);
|
|
}
|
|
else
|
|
Weird(msg);
|
|
}
|
|
|
|
bool BitTorrentTracker_Analyzer::ParseRequest(char* line)
|
|
{
|
|
static bool initialized = false;
|
|
static regex_t r_get, r_get_end, r_hdr;
|
|
|
|
if ( ! initialized )
|
|
{
|
|
regcomp(&r_get, "^GET[ \t]+", REG_EXTENDED | REG_ICASE);
|
|
regcomp(&r_get_end, "[ \t]+HTTP/[0123456789.]+$",
|
|
REG_EXTENDED | REG_ICASE);
|
|
regcomp(&r_hdr, "^[^: \t]+:[ ]*", REG_EXTENDED | REG_ICASE);
|
|
initialized = true;
|
|
}
|
|
|
|
switch ( req_state ) {
|
|
case BTT_REQ_GET:
|
|
{
|
|
regmatch_t match[1];
|
|
if ( regexec(&r_get, line, 1, match, 0) )
|
|
{
|
|
ProtocolViolation("BitTorrentTracker: invalid HTTP GET");
|
|
stop_orig = true;
|
|
return false;
|
|
}
|
|
|
|
regmatch_t match_end[1];
|
|
if ( ! regexec(&r_get_end, line, 1, match_end, 0) )
|
|
{
|
|
if ( match_end[0].rm_so <= match[0].rm_eo )
|
|
{
|
|
ProtocolViolation("BitTorrentTracker: invalid HTTP GET");
|
|
stop_orig = true;
|
|
return false;
|
|
}
|
|
|
|
keep_alive = (line[match_end[0].rm_eo - 1] == '1');
|
|
line[match_end[0].rm_so] = 0;
|
|
}
|
|
|
|
RequestGet(&line[match[0].rm_eo]);
|
|
|
|
req_state = BTT_REQ_HEADER;
|
|
}
|
|
break;
|
|
|
|
case BTT_REQ_HEADER:
|
|
{
|
|
if ( ! *line )
|
|
{
|
|
EmitRequest();
|
|
req_state = BTT_REQ_DONE;
|
|
break;
|
|
}
|
|
|
|
regmatch_t match[1];
|
|
if ( regexec(&r_hdr, line, 1, match, 0) )
|
|
{
|
|
ProtocolViolation("BitTorrentTracker: invalid HTTP request header");
|
|
stop_orig = true;
|
|
return false;
|
|
}
|
|
|
|
*strchr(line, ':') = 0; // this cannot fail - see regex_hdr
|
|
RequestHeader(line, &line[match[0].rm_eo]);
|
|
}
|
|
break;
|
|
|
|
case BTT_REQ_DONE:
|
|
if ( *line )
|
|
DeliverWeird(fmt("Got post request data: %s\n", line),
|
|
true);
|
|
break;
|
|
|
|
default:
|
|
// Make the compiler happy.
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::RequestGet(char* uri)
|
|
{
|
|
req_val_uri = new StringVal(uri);
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::EmitRequest(void)
|
|
{
|
|
val_list* vl;
|
|
|
|
ProtocolConfirmation();
|
|
|
|
vl = new val_list;
|
|
vl->append(BuildConnVal());
|
|
vl->append(req_val_uri);
|
|
vl->append(req_val_headers);
|
|
|
|
req_val_uri = 0;
|
|
req_val_headers = 0;
|
|
|
|
ConnectionEvent(bt_tracker_request, vl);
|
|
}
|
|
|
|
bool BitTorrentTracker_Analyzer::ParseResponse(char* line)
|
|
{
|
|
static bool initialized = false;
|
|
static regex_t r_stat, r_hdr;
|
|
|
|
if ( ! initialized )
|
|
{
|
|
regcomp(&r_stat, "^HTTP/[0123456789.]* ",
|
|
REG_EXTENDED | REG_ICASE);
|
|
regcomp(&r_hdr, "^[^: \t]+:[ ]*", REG_EXTENDED | REG_ICASE);
|
|
initialized = true;
|
|
}
|
|
|
|
switch ( res_state ) {
|
|
case BTT_RES_STATUS:
|
|
{
|
|
if ( res_allow_blank_line && ! *line )
|
|
{
|
|
// There may be an empty line after the bencoded
|
|
// directory, if this is a keep-alive connection.
|
|
// Ignore it.
|
|
res_allow_blank_line = false;
|
|
break;
|
|
}
|
|
|
|
regmatch_t match[1];
|
|
if ( regexec(&r_stat, line, 1, match, 0) )
|
|
{
|
|
ProtocolViolation("BitTorrentTracker: invalid HTTP status");
|
|
stop_resp = true;
|
|
return false;
|
|
}
|
|
|
|
ResponseStatus(&line[match[0].rm_eo]);
|
|
res_state = BTT_RES_HEADER;
|
|
}
|
|
break;
|
|
|
|
case BTT_RES_HEADER:
|
|
if ( ! *line )
|
|
{
|
|
if ( res_status != 200 )
|
|
{
|
|
val_list* vl = new val_list;
|
|
vl->append(BuildConnVal());
|
|
vl->append(new Val(res_status, TYPE_COUNT));
|
|
vl->append(res_val_headers);
|
|
ConnectionEvent(bt_tracker_response_not_ok, vl);
|
|
res_val_headers = 0;
|
|
res_buf_pos = res_buf + res_buf_len;
|
|
res_state = BTT_RES_DONE;
|
|
}
|
|
else
|
|
res_state = BTT_RES_BODY;
|
|
|
|
break;
|
|
}
|
|
|
|
{
|
|
regmatch_t match[1];
|
|
if ( regexec(&r_hdr, line, 1, match, 0) )
|
|
{
|
|
ProtocolViolation("BitTorrentTracker: invalid HTTP response header");
|
|
stop_resp = true;
|
|
return false;
|
|
}
|
|
|
|
*strchr(line, ':') = 0; // this cannot fail - see regex_hdr
|
|
ResponseHeader(line, &line[match[0].rm_eo]);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Make the compiler happy.
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::ResponseStatus(char* status)
|
|
{
|
|
if ( sscanf(status, FMT_UINT, &res_status) != 1 )
|
|
res_status = 0;
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::ParseHeader(char* name, char* value,
|
|
bool is_request)
|
|
{
|
|
if ( ! strcasecmp(name, "connection") )
|
|
{
|
|
if ( ! strcasecmp(value, "close") )
|
|
keep_alive = false;
|
|
else
|
|
keep_alive = true;
|
|
}
|
|
|
|
#ifdef BTTRACKER_STORE_HEADERS
|
|
StringVal* name_ = new StringVal(name);
|
|
StringVal* value_ = new StringVal(value);
|
|
|
|
(is_request ? req_val_headers : res_val_headers)->Assign(name_, value_);
|
|
Unref(name_);
|
|
#endif
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::ResponseBenc(int name_len, char* name,
|
|
enum btt_benc_types type, int value_len, char* value)
|
|
{
|
|
if ( name_len == 5 && ! strncmp(name, "peers", 5) )
|
|
{
|
|
for ( char* end = value + value_len; value < end; value += 6 )
|
|
{
|
|
// Note, weirdly/unfortunately AddrVal's take
|
|
// addresses in network order but PortVal's
|
|
// take ports in host order. BitTorrent specifies
|
|
// that both are in network order here.
|
|
uint32 ad = extract_uint32((u_char*) value);
|
|
uint16 pt = ntohs((value[4] << 8) | value[5]);
|
|
|
|
RecordVal* peer = new RecordVal(bittorrent_peer);
|
|
peer->Assign(0, new AddrVal(ad));
|
|
peer->Assign(1, new PortVal(pt, TRANSPORT_TCP));
|
|
res_val_peers->Assign(peer, 0);
|
|
|
|
Unref(peer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
StringVal* name_ = new StringVal(name_len, name);
|
|
RecordVal* benc_value = new RecordVal(bittorrent_benc_value);
|
|
benc_value->Assign(type, new StringVal(value_len, value));
|
|
res_val_benc->Assign(name_, benc_value);
|
|
|
|
Unref(name_);
|
|
}
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::ResponseBenc(int name_len, char* name,
|
|
enum btt_benc_types type, bro_int_t value)
|
|
{
|
|
RecordVal* benc_value = new RecordVal(bittorrent_benc_value);
|
|
StringVal* name_ = new StringVal(name_len, name);
|
|
|
|
benc_value->Assign(type, new Val(value, TYPE_INT));
|
|
res_val_benc->Assign(name_, benc_value);
|
|
|
|
Unref(name_);
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::ResponseBody(void)
|
|
{
|
|
switch ( ResponseParseBenc() ) {
|
|
case 0:
|
|
EmitResponse();
|
|
res_state = BTT_RES_DONE;
|
|
break;
|
|
|
|
case -1: // parsing failed
|
|
case -2: // need more data
|
|
break;
|
|
}
|
|
}
|
|
|
|
int BitTorrentTracker_Analyzer::ResponseParseBenc(void)
|
|
{
|
|
#define VIOLATION_IF(expr, msg) \
|
|
{ \
|
|
if ( expr ) \
|
|
{ \
|
|
ProtocolViolation(msg); \
|
|
stop_resp = true; \
|
|
return -1; \
|
|
} \
|
|
}
|
|
|
|
#define INC_COUNT \
|
|
{ \
|
|
unsigned int count = benc_count.back(); \
|
|
benc_count.pop_back(); \
|
|
benc_count.push_back(count + 1); \
|
|
}
|
|
|
|
for ( unsigned int len = res_buf_len - (res_buf_pos - res_buf); len;
|
|
--len, ++res_buf_pos )
|
|
{
|
|
switch ( benc_state ) {
|
|
case BENC_STATE_EMPTY:
|
|
{
|
|
switch ( res_buf_pos[0] ) {
|
|
case 'd':
|
|
switch ( benc_stack.size() ) {
|
|
case 0: break;
|
|
case 1:
|
|
benc_raw = res_buf_pos;
|
|
benc_raw_type = BENC_TYPE_DIR;
|
|
/* fall through */
|
|
default:
|
|
VIOLATION_IF(benc_stack.back() == 'd' &&
|
|
! (benc_count.back() % 2),
|
|
"BitTorrentTracker: directory key is not a string but a directory")
|
|
++benc_raw_len;
|
|
}
|
|
|
|
benc_stack.push_back('d');
|
|
benc_count.push_back(0);
|
|
break;
|
|
|
|
case 'l':
|
|
switch ( benc_stack.size() ) {
|
|
case 0:
|
|
VIOLATION_IF(1, "BitTorrentTracker: not a bencoded directory (first char: l)")
|
|
/* fall through */
|
|
|
|
case 1:
|
|
benc_raw = res_buf_pos;
|
|
benc_raw_type = BENC_TYPE_LIST;
|
|
/* fall through */
|
|
|
|
default:
|
|
VIOLATION_IF(benc_stack.back() == 'd' &&
|
|
! (benc_count.back() % 2),
|
|
"BitTorrentTracker: directory key is not a string but a list")
|
|
++benc_raw_len;
|
|
}
|
|
|
|
benc_stack.push_back('l');
|
|
benc_count.push_back(0);
|
|
break;
|
|
|
|
case 'i':
|
|
VIOLATION_IF(! benc_stack.size(),
|
|
"BitTorrentTracker: not a bencoded directory (first char: i)")
|
|
VIOLATION_IF(benc_stack.back() == 'd' &&
|
|
! (benc_count.back() % 2),
|
|
"BitTorrentTracker: directory key is not a string but an int")
|
|
|
|
if ( benc_raw_type != BENC_TYPE_NONE )
|
|
++benc_raw_len;
|
|
|
|
benc_state = BENC_STATE_INT1;
|
|
break;
|
|
|
|
case 'e':
|
|
VIOLATION_IF(! benc_stack.size(),
|
|
"BitTorrentTracker: not a bencoded directory (first char: e)")
|
|
VIOLATION_IF(benc_stack.back() == 'd' &&
|
|
benc_count.back() % 2,
|
|
"BitTorrentTracker: directory has an odd count of members")
|
|
|
|
if ( benc_raw_type != BENC_TYPE_NONE )
|
|
++benc_raw_len;
|
|
|
|
if ( benc_stack.size() == 2 )
|
|
{ // coming back to level 1
|
|
ResponseBenc(benc_key_len, benc_key,
|
|
benc_raw_type,
|
|
benc_raw_len, benc_raw);
|
|
benc_key = 0;
|
|
benc_key_len = 0;
|
|
benc_raw = 0;
|
|
benc_raw_len = 0;
|
|
benc_raw_type = BENC_TYPE_NONE;
|
|
}
|
|
|
|
benc_stack.pop_back();
|
|
benc_count.pop_back();
|
|
|
|
if ( benc_stack.size() )
|
|
INC_COUNT
|
|
else
|
|
{ // benc parsing successful
|
|
++res_buf_pos;
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
VIOLATION_IF(! benc_stack.size(),
|
|
"BitTorrentTracker: not a bencoded directory (first char: [0-9])")
|
|
|
|
if ( benc_raw_type != BENC_TYPE_NONE )
|
|
++benc_raw_len;
|
|
|
|
benc_strlen = res_buf_pos;
|
|
benc_state = BENC_STATE_STR1;
|
|
break;
|
|
|
|
default:
|
|
VIOLATION_IF(1, "BitTorrentTracker: no valid bencoding")
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BENC_STATE_INT1:
|
|
benc_int = res_buf_pos;
|
|
if ( res_buf_pos[0] == '-' )
|
|
{
|
|
if ( benc_raw_type != BENC_TYPE_NONE )
|
|
++benc_raw_len;
|
|
benc_state = BENC_STATE_INT2;
|
|
break;
|
|
}
|
|
|
|
case BENC_STATE_INT2:
|
|
VIOLATION_IF(res_buf_pos[0] < '0' ||
|
|
res_buf_pos[0] > '9',
|
|
"BitTorrentTracker: no valid bencoding")
|
|
|
|
if ( benc_raw_type != BENC_TYPE_NONE )
|
|
++benc_raw_len;
|
|
|
|
benc_state = BENC_STATE_INT3;
|
|
break;
|
|
|
|
case BENC_STATE_INT3:
|
|
if ( res_buf_pos[0] == 'e' )
|
|
{
|
|
if ( sscanf(benc_int, FMT_INT,
|
|
&benc_int_val) == 1 )
|
|
{
|
|
if ( benc_stack.size() == 1 )
|
|
{
|
|
ResponseBenc(benc_key_len,
|
|
benc_key, BENC_TYPE_INT,
|
|
benc_int_val);
|
|
benc_key = 0;
|
|
benc_key_len = 0;
|
|
}
|
|
}
|
|
else
|
|
VIOLATION_IF(1, "BitTorrentTracker: no valid bencoding")
|
|
|
|
INC_COUNT
|
|
benc_state = BENC_STATE_EMPTY;
|
|
}
|
|
|
|
else
|
|
VIOLATION_IF(res_buf_pos[0] < '0' ||
|
|
res_buf_pos[0] > '9',
|
|
"BitTorrentTracker: no valid bencoding");
|
|
|
|
if ( benc_raw_type != BENC_TYPE_NONE )
|
|
++benc_raw_len;
|
|
|
|
break;
|
|
|
|
case BENC_STATE_STR1:
|
|
switch ( res_buf_pos[0] ) {
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
if ( benc_raw_type != BENC_TYPE_NONE )
|
|
++benc_raw_len;
|
|
break;
|
|
|
|
case ':':
|
|
VIOLATION_IF(sscanf(benc_strlen, "%u",
|
|
&benc_str_len) != 1,
|
|
"BitTorrentTracker: no valid bencoding")
|
|
|
|
benc_str_have = 0;
|
|
benc_str = res_buf_pos + 1;
|
|
|
|
if ( benc_stack.size() == 1 &&
|
|
! (benc_count.front() % 2) )
|
|
{
|
|
benc_key = benc_str;
|
|
benc_key_len = benc_str_len;
|
|
}
|
|
|
|
if ( benc_raw_type != BENC_TYPE_NONE )
|
|
++benc_raw_len;
|
|
|
|
benc_state = BENC_STATE_STR2;
|
|
break;
|
|
|
|
default:
|
|
VIOLATION_IF(1, "BitTorrentTracker: no valid bencoding")
|
|
}
|
|
break;
|
|
|
|
case BENC_STATE_STR2:
|
|
if ( benc_str_have < benc_str_len )
|
|
{
|
|
unsigned int seek =
|
|
min(len, benc_str_len - benc_str_have);
|
|
benc_str_have += seek;
|
|
|
|
if ( benc_raw_type != BENC_TYPE_NONE )
|
|
benc_raw_len += seek;
|
|
|
|
res_buf_pos += seek - 1;
|
|
len -= seek - 1;
|
|
}
|
|
|
|
if ( benc_str_have == benc_str_len )
|
|
{
|
|
if ( benc_stack.size() == 1 && benc_key &&
|
|
benc_key != benc_str )
|
|
{
|
|
ResponseBenc(benc_key_len, benc_key,
|
|
BENC_TYPE_STR,
|
|
benc_str_len, benc_str);
|
|
benc_key_len = 0;
|
|
benc_key = 0;
|
|
}
|
|
|
|
if ( ! benc_str_len )
|
|
{
|
|
--res_buf_pos;
|
|
++len;
|
|
}
|
|
|
|
INC_COUNT
|
|
benc_state = BENC_STATE_EMPTY;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return -2; // need more data
|
|
}
|
|
|
|
void BitTorrentTracker_Analyzer::EmitResponse(void)
|
|
{
|
|
ProtocolConfirmation();
|
|
|
|
val_list* vl = new val_list;
|
|
vl->append(BuildConnVal());
|
|
vl->append(new Val(res_status, TYPE_COUNT));
|
|
vl->append(res_val_headers);
|
|
vl->append(res_val_peers);
|
|
vl->append(res_val_benc);
|
|
|
|
res_val_headers = 0;
|
|
res_val_peers = 0;
|
|
res_val_benc = 0;
|
|
|
|
ConnectionEvent(bt_tracker_response, vl);
|
|
}
|