Support map-based definition of ports in from_json()

The from_json() BiF and its underlying code in Val.cc currently expect ports
expressed as a string ('80/tcp' etc). Zeek's own serialization via ToJSON()
renders them as an object ('{"port":80, "proto":"tcp"}'). This adds support
for the latter format to from_json(), so serialized values can be read back.
This commit is contained in:
Christian Kreibich 2024-06-24 21:59:59 -07:00
parent a29f862f95
commit df645e9bb2
4 changed files with 51 additions and 22 deletions

View file

@ -943,28 +943,50 @@ static std::variant<ValPtr, std::string> BuildVal(const rapidjson::Value& j, con
}
case TYPE_PORT: {
if ( ! j.IsString() )
return mismatch_err();
int port = 0;
if ( j.GetStringLength() > 0 && j.GetStringLength() < 10 ) {
char* slash;
errno = 0;
port = strtol(j.GetString(), &slash, 10);
if ( ! errno ) {
++slash;
if ( util::streq(slash, "tcp") )
return val_mgr->Port(port, TRANSPORT_TCP);
else if ( util::streq(slash, "udp") )
return val_mgr->Port(port, TRANSPORT_UDP);
else if ( util::streq(slash, "icmp") )
return val_mgr->Port(port, TRANSPORT_ICMP);
else if ( util::streq(slash, "unknown") )
return val_mgr->Port(port, TRANSPORT_UNKNOWN);
if ( j.IsString() ) {
int port = 0;
if ( j.GetStringLength() > 0 && j.GetStringLength() < 10 ) {
char* slash;
errno = 0;
port = strtol(j.GetString(), &slash, 10);
if ( ! errno ) {
++slash;
if ( util::streq(slash, "tcp") )
return val_mgr->Port(port, TRANSPORT_TCP);
else if ( util::streq(slash, "udp") )
return val_mgr->Port(port, TRANSPORT_UDP);
else if ( util::streq(slash, "icmp") )
return val_mgr->Port(port, TRANSPORT_ICMP);
else if ( util::streq(slash, "unknown") )
return val_mgr->Port(port, TRANSPORT_UNKNOWN);
}
}
}
return "wrong port format, must be /[0-9]{1,5}\\/(tcp|udp|icmp|unknown)/";
return "wrong port format, string must be /[0-9]{1,5}\\/(tcp|udp|icmp|unknown)/";
}
else if ( j.IsObject() ) {
if ( ! j.HasMember("port") || ! j.HasMember("proto") )
return "wrong port format, object must have 'port' and 'proto' members";
if ( ! j["port"].IsNumber() )
return "wrong port format, port must be a number";
if ( ! j["proto"].IsString() )
return "wrong port format, protocol must be a string";
std::string proto{j["proto"].GetString()};
if ( proto == "tcp" )
return val_mgr->Port(j["port"].GetInt(), TRANSPORT_TCP);
if ( proto == "udp" )
return val_mgr->Port(j["port"].GetInt(), TRANSPORT_UDP);
if ( proto == "icmp" )
return val_mgr->Port(j["port"].GetInt(), TRANSPORT_ICMP);
if ( proto == "unknown" )
return val_mgr->Port(j["port"].GetInt(), TRANSPORT_UNKNOWN);
return "wrong port format, invalid protocol string";
}
else
return "wrong port format, must be string or object";
}
case TYPE_PATTERN: {

View file

@ -1,2 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
error in <...>/from_json.zeek, line 5: wrong port format, must be <...>/(tcp|udp|icmp|unknown)/ (from_json("80", to_any_coerce port_t, from_json_default_key_mapper))
error in <...>/from_json.zeek, line 8: wrong port format, string must be <...>/(tcp|udp|icmp|unknown)/ (from_json("80", to_any_coerce port_t, from_json_default_key_mapper))
error in <...>/from_json.zeek, line 9: wrong port format, object must have 'port' and 'proto' members (from_json({}, to_any_coerce port_t, from_json_default_key_mapper))

View file

@ -1,2 +1,4 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
[v=80/tcp, valid=T]
[v=<uninitialized>, valid=F]
[v=<uninitialized>, valid=F]

View file

@ -73,10 +73,14 @@ event zeek_init()
@TEST-START-NEXT
type port_t: port;
# wrong port format
# additional & incorrect port formats
event zeek_init()
{
# Ports can also be given as objects:
print from_json("{\"port\":80,\"proto\":\"tcp\"}", port_t);
# These are violations:
print from_json("\"80\"", port_t);
print from_json("{}", port_t);
}
@TEST-START-NEXT