zeek/doc/devel/spicy/autogen/tftp.zeek
Tim Wojtulewicz ded98cd373 Copy docs into Zeek repo directly
This is based on commit 2731def9159247e6da8a3191783c89683363689c from the
zeek-docs repo.
2025-09-26 02:58:29 +00:00

162 lines
3.8 KiB
Text

# Copyright (c) 2021 by the Zeek Project. See LICENSE for details.
module TFTP;
export {
redef enum Log::ID += { LOG };
type Info: record {
## Timestamp for when the request happened.
ts: time &log;
## Unique ID for the connection.
uid: string &log;
## The connection's 4-tuple of endpoint addresses/ports.
id: conn_id &log;
## True for write requests, False for read request.
wrq: bool &log;
## File name of request.
fname: string &log;
## Mode of request.
mode: string &log;
## UID of data connection
uid_data: string &optional &log;
## Number of bytes sent.
size: count &default=0 &log;
## Highest block number sent.
block_sent: count &default=0 &log;
## Highest block number ackknowledged.
block_acked: count &default=0 &log;
## Any error code encountered.
error_code: count &optional &log;
## Any error message encountered.
error_msg: string &optional &log;
# Set to block number of final piece of data once received.
final_block: count &optional;
# Set to true once logged.
done: bool &default=F;
};
## Event that can be handled to access the TFTP logging record.
global log_tftp: event(rec: Info);
}
# Maps a partial data connection ID to the request's Info record.
global expected_data_conns: table[addr, port, addr] of Info;
redef record connection += {
tftp: Info &optional;
};
event zeek_init() &priority=5
{
Log::create_stream(TFTP::LOG, [$columns = Info, $ev = log_tftp, $path="tftp"]);
}
function log_pending(c: connection)
{
if ( ! c?$tftp || c$tftp$done )
return;
Log::write(TFTP::LOG, c$tftp);
c$tftp$done = T;
}
function init_request(c: connection, is_orig: bool, fname: string, mode: string, is_read: bool)
{
log_pending(c);
local info: Info;
info$ts = network_time();
info$uid = c$uid;
info$id = c$id;
info$fname = fname;
info$mode = mode;
info$wrq = (! is_read);
c$tftp = info;
# The data will come in from a different source port.
Analyzer::schedule_analyzer(c$id$resp_h, c$id$orig_h, c$id$orig_p, Analyzer::ANALYZER_SPICY_TFTP, 1min);
expected_data_conns[c$id$resp_h, c$id$orig_p, c$id$orig_h] = info;
}
event scheduled_analyzer_applied(c: connection, a: Analyzer::Tag) &priority=10
{
local id = c$id;
if ( [c$id$orig_h, c$id$resp_p, c$id$resp_h] in expected_data_conns )
{
c$tftp = expected_data_conns[c$id$orig_h, c$id$resp_p, c$id$resp_h];
c$tftp$uid_data = c$uid;
add c$service["spicy_tftp_data"];
}
}
event tftp::read_request(c: connection, is_orig: bool, fname: string, mode: string)
{
init_request(c, is_orig, fname, mode, T);
}
event tftp::write_request(c: connection, is_orig: bool, fname: string, mode: string)
{
init_request(c, is_orig, fname, mode, F);
}
event tftp::data(c: connection, is_orig: bool, block_num: count, data: string)
{
if ( ! c?$tftp || c$tftp$done )
return;
local info = c$tftp;
if ( block_num <= info$block_sent )
# Duplicate (or previous gap; we don't track that)
return;
info$size += |data|;
info$block_sent = block_num;
if ( |data| < 512 )
# Last block, per spec.
info$final_block = block_num;
}
event tftp::ack(c: connection, is_orig: bool, block_num: count)
{
if ( ! c?$tftp || c$tftp$done )
return;
local info = c$tftp;
info$block_acked = block_num;
if ( block_num <= info$block_acked )
# Duplicate (or previous gap, we don't track that)
return;
info$block_acked = block_num;
# If it's an ack for the last block, we're done.
if ( info?$final_block && info$final_block == block_num )
log_pending(c);
}
event tftp::error(c: connection, is_orig: bool, code: count, msg: string)
{
if ( ! c?$tftp || c$tftp$done )
return;
local info = c$tftp;
info$error_code = code;
info$error_msg = msg;
log_pending(c);
}
event connection_state_remove(c: connection)
{
if ( ! c?$tftp || c$tftp$done )
return;
log_pending(c);
}