mirror of
https://github.com/zeek/zeek.git
synced 2025-10-04 07:38:19 +00:00

This is based on commit 2731def9159247e6da8a3191783c89683363689c from the zeek-docs repo.
162 lines
3.8 KiB
Text
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);
|
|
}
|