vrl/stdlib/
ip_ntop.rs

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}