// See the file "COPYING" in the main distribution directory for copyright. #include "config.h" #include #include #include "NetVar.h" #include "HTTP.h" #include "Gnutella.h" #include "Event.h" #include "PIA.h" GnutellaMsgState::GnutellaMsgState() { buffer = ""; current_offset = 0; headers = ""; msg_hops = 0; msg_len = 0; msg_pos = 0; msg_type = 0; msg_sent = 1; msg_ttl = 0; payload_left = 0; got_CR = 0; payload_len = 0; } Gnutella_Analyzer::Gnutella_Analyzer(Connection* conn) : TCP_ApplicationAnalyzer(AnalyzerTag::Gnutella, conn) { state = 0; new_state = 0; sent_establish = 0; ms = 0; orig_msg_state = new GnutellaMsgState(); resp_msg_state = new GnutellaMsgState(); } Gnutella_Analyzer::~Gnutella_Analyzer() { delete orig_msg_state; delete resp_msg_state; } void Gnutella_Analyzer::Done() { TCP_ApplicationAnalyzer::Done(); if ( ! sent_establish && (gnutella_establish || gnutella_not_establish) ) { val_list* vl = new val_list; vl->append(BuildConnVal()); if ( Established() && gnutella_establish ) ConnectionEvent(gnutella_establish, vl); else if ( ! Established () && gnutella_not_establish ) ConnectionEvent(gnutella_not_establish, vl); } if ( gnutella_partial_binary_msg ) { GnutellaMsgState* p = orig_msg_state; for ( int i = 0; i < 2; ++i, p = resp_msg_state ) { if ( ! p->msg_sent && p->msg_pos ) { val_list* vl = new val_list; vl->append(BuildConnVal()); vl->append(new StringVal(p->msg)); vl->append(new Val((i == 0), TYPE_BOOL)); vl->append(new Val(p->msg_pos, TYPE_COUNT)); ConnectionEvent(gnutella_partial_binary_msg, vl); } else if ( ! p->msg_sent && p->payload_left ) SendEvents(p, (i == 0)); } } } int Gnutella_Analyzer::NextLine(const u_char* data, int len) { if ( ! ms ) return 0; if ( Established() || ms->current_offset >= len ) return 0; for ( ; ms->current_offset < len; ++ms->current_offset ) { if ( data[ms->current_offset] == '\r' ) ms->got_CR = 1; else if ( data[ms->current_offset] == '\n' && ms->got_CR ) { ms->got_CR = 0; ++ms->current_offset; return 1; } else ms->buffer += data[ms->current_offset]; } return 0; } int Gnutella_Analyzer::IsHTTP(string header) { if ( header.find(" HTTP/1.") == string::npos ) return 0; if ( gnutella_http_notify ) { val_list* vl = new val_list; vl->append(BuildConnVal()); ConnectionEvent(gnutella_http_notify, vl); } if ( HTTP_Analyzer::Available() ) { Analyzer* a = new HTTP_Analyzer(Conn()); Parent()->AddChildAnalyzer(a); if ( Parent()->GetTag() == AnalyzerTag::TCP ) { // Replay buffered data. PIA* pia = static_cast(Parent())->GetPIA(); if ( pia ) static_cast(pia)->ReplayStreamBuffer(a); } Parent()->RemoveChildAnalyzer(this); } return 1; } int Gnutella_Analyzer::GnutellaOK(string header) { if ( strncmp("GNUTELLA", header.data(), 8) ) return 0; int codepos = header.find(' ') + 1; if ( ! strncmp("200", header.data() + codepos, 3) ) return 1; return 0; } void Gnutella_Analyzer::DeliverLines(int len, const u_char* data, bool orig) { if ( ! ms ) return; while ( NextLine(data, len) ) { if ( ms->buffer.length() ) { if ( ms->headers.length() == 0 ) { if ( IsHTTP(ms->buffer) ) return; if ( GnutellaOK(ms->buffer) ) new_state |= orig ? ORIG_OK : RESP_OK; } ms->headers = ms->headers + "\r\n" + ms->buffer; ms->buffer = ""; } else { if ( gnutella_text_msg ) { val_list* vl = new val_list; vl->append(BuildConnVal()); vl->append(new Val(orig, TYPE_BOOL)); vl->append(new StringVal(ms->headers.data())); ConnectionEvent(gnutella_text_msg, vl); } ms->headers = ""; state |= new_state; if ( Established () && gnutella_establish ) { val_list* vl = new val_list; sent_establish = 1; vl->append(BuildConnVal()); ConnectionEvent(gnutella_establish, vl); } } } } void Gnutella_Analyzer::DissectMessage(char* msg) { if ( ! ms ) return; ms->msg_type = msg[16]; ms->msg_ttl = msg[17]; ms->msg_hops = msg[18]; memcpy(&ms->msg_len, &msg[19], 4); } void Gnutella_Analyzer::SendEvents(GnutellaMsgState* p, bool is_orig) { if ( p->msg_sent ) return; if ( gnutella_binary_msg ) { val_list* vl = new val_list; vl->append(BuildConnVal()); vl->append(new Val(is_orig, TYPE_BOOL)); vl->append(new Val(p->msg_type, TYPE_COUNT)); vl->append(new Val(p->msg_ttl, TYPE_COUNT)); vl->append(new Val(p->msg_hops, TYPE_COUNT)); vl->append(new Val(p->msg_len, TYPE_COUNT)); vl->append(new StringVal(p->payload)); vl->append(new Val(p->payload_len, TYPE_COUNT)); vl->append(new Val((p->payload_len < min(p->msg_len, (unsigned int)GNUTELLA_MAX_PAYLOAD)), TYPE_BOOL)); vl->append(new Val((p->payload_left == 0), TYPE_BOOL)); ConnectionEvent(gnutella_binary_msg, vl); } } void Gnutella_Analyzer::DeliverMessages(int len, const u_char* data, bool orig) { if ( ! ms ) return; while ( ms->current_offset < len ) { ms->msg_sent = 0; unsigned int bytes_left = len - ms->current_offset; unsigned int needed = 0; if ( ms->msg_pos ) needed = GNUTELLA_MSG_SIZE - ms->msg_pos; if ( (! ms->msg_pos && ! ms->payload_left && (bytes_left >= GNUTELLA_MSG_SIZE)) || (ms->msg_pos && (bytes_left >= needed)) ) { int sz = ms->msg_pos ? needed : GNUTELLA_MSG_SIZE; memcpy(&ms->msg[ms->msg_pos], &data[ms->current_offset], sz); ms->current_offset += sz; DissectMessage(ms->msg); ms->payload_left = ms->msg_len; ms->msg_pos = 0; if ( ms->msg_len == 0 ) SendEvents(ms, orig); } else if ( (! ms->msg_pos && ! ms->payload_left && (bytes_left < GNUTELLA_MSG_SIZE)) || (ms->msg_pos && (bytes_left < needed)) ) { memcpy(&ms->msg[ms->msg_pos], &data[ms->current_offset], bytes_left); ms->current_offset += bytes_left; ms->msg_pos += bytes_left; } else if ( ms->payload_left ) { unsigned int space = ms->payload_len >= GNUTELLA_MAX_PAYLOAD ? 0 : GNUTELLA_MAX_PAYLOAD - ms->payload_len; unsigned int sz = (bytes_left < space) ? bytes_left : space; if ( space ) { memcpy(&ms->payload[ms->payload_len], &data[ms->current_offset], sz); ms->payload_len += sz; } if ( ms->payload_left > bytes_left ) { ms->current_offset += bytes_left; ms->payload_left -= bytes_left; } else { ms->current_offset += ms->payload_left; ms->payload_left = 0; SendEvents(ms, orig); } } } } void Gnutella_Analyzer::DeliverStream(int len, const u_char* data, bool orig) { TCP_ApplicationAnalyzer::DeliverStream(len, data, orig); ms = orig ? orig_msg_state : resp_msg_state; ms->current_offset = 0; if ( ! Established() ) { DeliverLines(len, data, orig); if ( Established() && ms->current_offset < len && gnutella_binary_msg ) DeliverMessages(len, data, orig); } else if ( gnutella_binary_msg ) DeliverMessages(len, data, orig); }