mirror of
https://github.com/zeek/zeek.git
synced 2025-10-09 18:18:19 +00:00
346 lines
7 KiB
C++
346 lines
7 KiB
C++
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
#include "config.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <algorithm>
|
|
|
|
#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<TransportLayerAnalyzer *>(Parent())->GetPIA();
|
|
if ( pia )
|
|
static_cast<PIA_TCP *>(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);
|
|
}
|
|
|