mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
172 lines
5.4 KiB
C++
172 lines
5.4 KiB
C++
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
#include "zeek/packet_analysis/protocol/tcp/TCP.h"
|
|
|
|
#include "zeek/RunState.h"
|
|
#include "zeek/analyzer/protocol/pia/PIA.h"
|
|
#include "zeek/analyzer/protocol/tcp/events.bif.h"
|
|
#include "zeek/analyzer/protocol/tcp/types.bif.h"
|
|
#include "zeek/packet_analysis/protocol/tcp/TCPSessionAdapter.h"
|
|
|
|
using namespace zeek;
|
|
using namespace zeek::packet_analysis::TCP;
|
|
using namespace zeek::packet_analysis::IP;
|
|
|
|
TCPAnalyzer::TCPAnalyzer() : IPBasedAnalyzer("TCP", TRANSPORT_TCP, TCP_PORT_MASK, false) { }
|
|
|
|
void TCPAnalyzer::Initialize() { }
|
|
|
|
SessionAdapter* TCPAnalyzer::MakeSessionAdapter(Connection* conn)
|
|
{
|
|
auto* root = new TCPSessionAdapter(conn);
|
|
root->SetParent(this);
|
|
|
|
conn->EnableStatusUpdateTimer();
|
|
conn->SetInactivityTimeout(zeek::detail::udp_inactivity_timeout);
|
|
|
|
return root;
|
|
}
|
|
|
|
zeek::analyzer::pia::PIA* TCPAnalyzer::MakePIA(Connection* conn)
|
|
{
|
|
return new analyzer::pia::PIA_TCP(conn);
|
|
}
|
|
|
|
bool TCPAnalyzer::BuildConnTuple(size_t len, const uint8_t* data, Packet* packet, ConnTuple& tuple)
|
|
{
|
|
uint32_t min_hdr_len = sizeof(struct tcphdr);
|
|
if ( ! CheckHeaderTrunc(min_hdr_len, len, packet) )
|
|
return false;
|
|
|
|
tuple.src_addr = packet->ip_hdr->SrcAddr();
|
|
tuple.dst_addr = packet->ip_hdr->DstAddr();
|
|
|
|
data = packet->ip_hdr->Payload();
|
|
|
|
const struct tcphdr* tp = (const struct tcphdr*)data;
|
|
tuple.src_port = tp->th_sport;
|
|
tuple.dst_port = tp->th_dport;
|
|
tuple.is_one_way = false;
|
|
tuple.proto = TRANSPORT_TCP;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TCPAnalyzer::WantConnection(uint16_t src_port, uint16_t dst_port, const u_char* data,
|
|
bool& flip_roles) const
|
|
{
|
|
flip_roles = false;
|
|
const struct tcphdr* tp = (const struct tcphdr*)data;
|
|
uint8_t tcp_flags = tp->th_flags;
|
|
|
|
if ( ! (tcp_flags & TH_SYN) || (tcp_flags & TH_ACK) )
|
|
{
|
|
// The new connection is starting either without a SYN,
|
|
// or with a SYN ack. This means it's a partial connection.
|
|
if ( ! zeek::detail::partial_connection_ok )
|
|
return false;
|
|
|
|
if ( tcp_flags & TH_SYN && ! zeek::detail::tcp_SYN_ack_ok )
|
|
return false;
|
|
|
|
// Try to guess true responder by the port numbers.
|
|
// (We might also think that for SYN acks we could
|
|
// safely flip the roles, but that doesn't work
|
|
// for stealth scans.)
|
|
if ( IsLikelyServerPort(src_port) )
|
|
{ // connection is a candidate for flipping
|
|
if ( IsLikelyServerPort(dst_port) )
|
|
// Hmmm, both source and destination
|
|
// are plausible. Heuristic: flip only
|
|
// if (1) this isn't a SYN ACK (to avoid
|
|
// confusing stealth scans) and
|
|
// (2) dest port > src port (to favor
|
|
// more plausible servers).
|
|
flip_roles = ! (tcp_flags & TH_SYN) && src_port < dst_port;
|
|
else
|
|
// Source is plausible, destination isn't.
|
|
flip_roles = true;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void TCPAnalyzer::DeliverPacket(Connection* c, double t, bool is_orig, int remaining, Packet* pkt)
|
|
{
|
|
const u_char* data = pkt->ip_hdr->Payload();
|
|
int len = pkt->ip_hdr->PayloadLen();
|
|
// If the header length is zero, tcp checksum offloading is probably enabled
|
|
// In this case, let's fix up the length.
|
|
if ( pkt->ip_hdr->TotalLen() == 0 )
|
|
len = remaining;
|
|
auto* adapter = static_cast<TCPSessionAdapter*>(c->GetSessionAdapter());
|
|
|
|
const struct tcphdr* tp = ExtractTCP_Header(data, len, remaining, adapter);
|
|
if ( ! tp )
|
|
return;
|
|
|
|
// We need the min() here because Ethernet frame padding can lead to
|
|
// remaining > len.
|
|
if ( packet_contents )
|
|
adapter->PacketContents(data, std::min(len, remaining));
|
|
|
|
analyzer::tcp::TCP_Endpoint* endpoint = is_orig ? adapter->orig : adapter->resp;
|
|
analyzer::tcp::TCP_Endpoint* peer = endpoint->peer;
|
|
const std::shared_ptr<IP_Hdr>& ip = pkt->ip_hdr;
|
|
|
|
if ( ! ValidateChecksum(ip.get(), tp, endpoint, len, remaining, adapter) )
|
|
return;
|
|
|
|
adapter->Process(is_orig, tp, len, ip, data, remaining);
|
|
|
|
// Send the packet back into the packet analysis framework.
|
|
ForwardPacket(len, data, pkt);
|
|
|
|
// Call DeliverPacket on the adapter directly here. Normally we'd call ForwardPacket
|
|
// but this adapter does some other things in its DeliverPacket with the packet children
|
|
// analyzers.
|
|
adapter->DeliverPacket(len, data, is_orig, adapter->LastRelDataSeq(), ip.get(), remaining);
|
|
}
|
|
|
|
const struct tcphdr* TCPAnalyzer::ExtractTCP_Header(const u_char*& data, int& len, int& remaining,
|
|
TCPSessionAdapter* adapter)
|
|
{
|
|
const struct tcphdr* tp = (const struct tcphdr*)data;
|
|
uint32_t tcp_hdr_len = tp->th_off * 4;
|
|
|
|
if ( tcp_hdr_len < sizeof(struct tcphdr) )
|
|
{
|
|
adapter->Weird("bad_TCP_header_len");
|
|
return nullptr;
|
|
}
|
|
|
|
if ( tcp_hdr_len > uint32_t(len) || tcp_hdr_len > uint32_t(remaining) )
|
|
{
|
|
// This can happen even with the above test, due to TCP options.
|
|
adapter->Weird("truncated_header");
|
|
return nullptr;
|
|
}
|
|
|
|
len -= tcp_hdr_len; // remove TCP header
|
|
remaining -= tcp_hdr_len;
|
|
data += tcp_hdr_len;
|
|
|
|
return tp;
|
|
}
|
|
|
|
bool TCPAnalyzer::ValidateChecksum(const IP_Hdr* ip, const struct tcphdr* tp,
|
|
analyzer::tcp::TCP_Endpoint* endpoint, int len, int caplen,
|
|
TCPSessionAdapter* adapter)
|
|
{
|
|
if ( ! run_state::current_pkt->l3_checksummed && ! detail::ignore_checksums &&
|
|
! GetIgnoreChecksumsNets()->Contains(ip->IPHeaderSrcAddr()) && caplen >= len &&
|
|
! endpoint->ValidChecksum(tp, len, ip->IP4_Hdr()) )
|
|
{
|
|
adapter->Weird("bad_TCP_checksum");
|
|
endpoint->ChecksumError();
|
|
return false;
|
|
}
|
|
else
|
|
return true;
|
|
}
|