zeek/scripts/base/protocols/socks/main.zeek
Benjamin Bannier d5fd29edcd Prefer explicit construction to coercion in record initialization
While we support initializing records via coercion from an expression
list, e.g.,

    local x: X = [$x1=1, $x2=2];

this can sometimes obscure the code to readers, e.g., when assigning to
value declared and typed elsewhere. The language runtime has a similar
overhead since instead of just constructing a known type it needs to
check at runtime that the coercion from the expression list is valid;
this can be slower than just writing the readible code in the first
place, see #4559.

With this patch we use explicit construction, e.g.,

    local x = X($x1=1, $x2=2);
2025-07-11 16:28:37 -07:00

129 lines
4 KiB
Text

@load base/frameworks/tunnels
@load ./consts
@load base/protocols/conn/removal-hooks
module SOCKS;
export {
redef enum Log::ID += { LOG };
global log_policy: Log::PolicyHook;
## Whether passwords are captured or not.
option default_capture_password = F;
## The record type which contains the fields of the SOCKS log.
type Info: record {
## Time when the proxy connection was first detected.
ts: time &log;
## Unique ID for the tunnel - may correspond to connection uid
## or be nonexistent.
uid: string &log;
## The connection's 4-tuple of endpoint addresses/ports.
id: conn_id &log;
## Protocol version of SOCKS.
version: count &log;
## Username used to request a login to the proxy.
user: string &log &optional;
## Password used to request a login to the proxy.
password: string &log &optional;
## Server status for the attempt at using the proxy.
status: string &log &optional;
## Client requested SOCKS address. Could be an address, a name
## or both.
request: SOCKS::Address &log &optional;
## Client requested port.
request_p: port &log &optional;
## Server bound address. Could be an address, a name or both.
bound: SOCKS::Address &log &optional;
## Server bound port.
bound_p: port &log &optional;
## Determines if the password will be captured for this request.
capture_password: bool &default=default_capture_password;
};
## Event that can be handled to access the SOCKS
## record as it is sent on to the logging framework.
global log_socks: event(rec: Info);
## SOCKS finalization hook. Remaining SOCKS info may get logged when it's called.
global finalize_socks: Conn::RemovalHook;
}
const ports = { 1080/tcp };
redef likely_server_ports += { ports };
event zeek_init() &priority=5
{
Log::create_stream(SOCKS::LOG, Log::Stream($columns=Info, $ev=log_socks, $path="socks", $policy=log_policy));
Analyzer::register_for_ports(Analyzer::ANALYZER_SOCKS, ports);
}
redef record connection += {
socks: SOCKS::Info &optional;
};
function set_session(c: connection, version: count)
{
if ( ! c?$socks )
{
c$socks = Info($ts=network_time(), $id=c$id, $uid=c$uid, $version=version);
Conn::register_removal_hook(c, finalize_socks);
}
}
event socks_request(c: connection, version: count, request_type: count,
sa: SOCKS::Address, p: port, user: string) &priority=5
{
set_session(c, version);
c$socks$request = sa;
c$socks$request_p = p;
# Copy this conn_id and set the orig_p to zero because in the case of SOCKS proxies there will
# be potentially many source ports since a new proxy connection is established for each
# proxied connection. We treat this as a singular "tunnel".
local cid = copy(c$id);
cid$orig_p = 0/tcp;
Tunnel::register(Tunnel::EncapsulatingConn($cid=cid, $tunnel_type=Tunnel::SOCKS));
}
event socks_reply(c: connection, version: count, reply: count, sa: SOCKS::Address, p: port) &priority=5
{
set_session(c, version);
if ( version == 5 )
c$socks$status = v5_status[reply];
else if ( version == 4 )
c$socks$status = v4_status[reply];
c$socks$bound = sa;
c$socks$bound_p = p;
}
event socks_login_userpass_request(c: connection, user: string, password: string) &priority=5
{
# Authentication only possible with the version 5.
set_session(c, 5);
c$socks$user = user;
if ( c$socks$capture_password )
c$socks$password = password;
}
event socks_login_userpass_reply(c: connection, code: count) &priority=5
{
# Authentication only possible with the version 5.
set_session(c, 5);
c$socks$status = v5_status[code];
}
hook finalize_socks(c: connection)
{
# This will handle the case where the analyzer failed in some way and was
# removed. We probably don't want to log these connections.
if ( "SOCKS" in c$service )
Log::write(SOCKS::LOG, c$socks);
}