use super::funejson::Value; use super::net_port_data::SERVICES; use super::net_port_is_port::is_port; /// The RFC 6335 range a port is in, and the IANA service registered on it for /// a protocol. A port with nothing in this registry's table has a null service: /// unassigned, or assigned but not common enough to ship here. /// /// # Panics /// Panics on a port outside 1..=65535 or a protocol other than tcp or udp. pub fn port_service(port: i64, protocol: &str) -> PortService { if !is_port(port) { panic!("port must be 1 to 65535, received {}", port); } if protocol != "tcp" && protocol != "udp" { panic!("protocol must be tcp or udp, received \"{}\"", protocol); } // RFC 6335 section 6: system 0-1023, user 1024-49151, dynamic 49152-65535. let range = if port <= 1023 { "system" } else if port <= 49151 { "user" } else { "dynamic" }; let row = SERVICES.iter().find(|s| s.port == port && s.protocol == protocol); PortService { port, protocol: protocol.to_string(), range: range.to_string(), service: row.map(|r| r.service.to_string()), description: row.map(|r| r.description.to_string()), } } /// Object keys are camelCase to match the shared vectors. pub fn port_service_to_value(result: &PortService) -> Value { let text = |field: &Option| match field { Some(s) => Value::str(s), None => Value::Null, }; Value::obj(vec![ ("port", Value::Int(result.port)), ("protocol", Value::str(&result.protocol)), ("range", Value::str(&result.range)), ("service", text(&result.service)), ("description", text(&result.description)), ]) } pub fn fune_vector(args: &[Value]) -> Value { // Refuse what an i64 cannot hold, with the wording TypeScript and Python use. let port = match &args[0] { Value::Int(i) => *i, other => panic!("port must be an integer, received {}", other), }; port_service_to_value(&port_service(port, args[1].as_str())) }