1use crate::compiler::prelude::*;
2use std::net::IpAddr;
3
4fn ip_ntop(value: Value) -> Resolved {
5 let value = value.try_bytes()?;
6
7 match value.len() {
8 4 => {
9 let bytes: [u8; 4] = value[..].try_into().expect("invalid length");
10 Ok(IpAddr::from(bytes).to_string().into())
11 }
12 16 => {
13 let bytes: [u8; 16] = value[..].try_into().expect("invalid length");
14 Ok(IpAddr::from(bytes).to_string().into())
15 }
16 _ => Err(r#""value" must be of length 4 or 16 bytes"#.into()),
17 }
18}
19
20#[derive(Clone, Copy, Debug)]
21pub struct IpNtop;
22
23impl Function for IpNtop {
24 fn identifier(&self) -> &'static str {
25 "ip_ntop"
26 }
27
28 fn usage(&self) -> &'static str {
29 indoc! {"
30 Converts IPv4 and IPv6 addresses from binary to text form.
31
32 This behavior mimics [inet_ntop](https://linux.die.net/man/3/inet_ntop).
33 "}
34 }
35
36 fn category(&self) -> &'static str {
37 Category::Ip.as_ref()
38 }
39
40 fn internal_failure_reasons(&self) -> &'static [&'static str] {
41 &["`value` must be of length 4 or 16 bytes."]
42 }
43
44 fn return_kind(&self) -> u16 {
45 kind::BYTES
46 }
47
48 fn notices(&self) -> &'static [&'static str] {
49 &[indoc! {"
50 The binary data for this function is not easily printable. However, the results from
51 functions such as `decode_base64` or `decode_percent` can still be used correctly.
52 "}]
53 }
54
55 fn parameters(&self) -> &'static [Parameter] {
56 const PARAMETERS: &[Parameter] = &[Parameter::required(
57 "value",
58 kind::BYTES,
59 "The binary data to convert from.
60For IPv4 addresses, it must be 4 bytes (32 bits) long.
61For IPv6 addresses, it must be 16 bytes (128 bits) long.",
62 )];
63 PARAMETERS
64 }
65
66 fn examples(&self) -> &'static [Example] {
67 &[
68 example! {
69 title: "Convert IPv4 address from bytes after decoding from Base64",
70 source: r#"ip_ntop!(decode_base64!("wKgAAQ=="))"#,
71 result: Ok("192.168.0.1"),
72 },
73 example! {
74 title: "Convert IPv6 address from bytes after decoding from Base64",
75 source: r#"ip_ntop!(decode_base64!("IAENuIWjAAAAAIouA3BzNA=="))"#,
76 result: Ok("2001:db8:85a3::8a2e:370:7334"),
77 },
78 ]
79 }
80
81 fn compile(
82 &self,
83 _state: &state::TypeState,
84 _ctx: &mut FunctionCompileContext,
85 arguments: ArgumentList,
86 ) -> Compiled {
87 let value = arguments.required("value");
88
89 Ok(IpNtopFn { value }.as_expr())
90 }
91}
92
93#[derive(Debug, Clone)]
94struct IpNtopFn {
95 value: Box<dyn Expression>,
96}
97
98impl FunctionExpression for IpNtopFn {
99 fn resolve(&self, ctx: &mut Context) -> Resolved {
100 let value = self.value.resolve(ctx)?;
101 ip_ntop(value)
102 }
103
104 fn type_def(&self, _: &state::TypeState) -> TypeDef {
105 TypeDef::bytes().fallible()
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use crate::value;
113
114 test_function![
115 ip_ntop => IpNtop;
116
117 invalid {
118 args: func_args![value: "\x01\x02"],
119 want: Err(r#""value" must be of length 4 or 16 bytes"#),
120 tdef: TypeDef::bytes().fallible(),
121 }
122
123 valid_ipv4 {
124 args: func_args![value: "\x01\x02\x03\x04"],
125 want: Ok(value!("1.2.3.4")),
126 tdef: TypeDef::bytes().fallible(),
127 }
128
129 valid_ipv6 {
130 args: func_args![value: "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"],
131 want: Ok(value!("102:304:506:708:90a:b0c:d0e:f10")),
132 tdef: TypeDef::bytes().fallible(),
133 }
134 ];
135}