vrl/stdlib/
reverse_dns.rs1use crate::compiler::prelude::*;
2
3#[cfg(not(target_arch = "wasm32"))]
4mod non_wasm {
5 use crate::compiler::prelude::*;
6 use crate::value::Value;
7 use dns_lookup::lookup_addr;
8 use std::net::IpAddr;
9
10 fn reverse_dns(value: &Value) -> Resolved {
11 let ip: IpAddr = value
12 .try_bytes_utf8_lossy()?
13 .parse()
14 .map_err(|err| format!("unable to parse IP address: {err}"))?;
15 let host = lookup_addr(&ip).map_err(|err| format!("unable to perform a lookup : {err}"))?;
16
17 Ok(host.into())
18 }
19
20 #[derive(Debug, Clone)]
21 pub(super) struct ReverseDnsFn {
22 pub(super) value: Box<dyn Expression>,
23 }
24
25 impl FunctionExpression for ReverseDnsFn {
26 fn resolve(&self, ctx: &mut Context) -> Resolved {
27 let value = self.value.resolve(ctx)?;
28 reverse_dns(&value)
29 }
30
31 fn type_def(&self, _: &state::TypeState) -> TypeDef {
32 TypeDef::bytes().fallible()
33 }
34 }
35}
36
37#[allow(clippy::wildcard_imports)]
38#[cfg(not(target_arch = "wasm32"))]
39use non_wasm::*;
40
41#[derive(Clone, Copy, Debug)]
42pub struct ReverseDns;
43
44impl Function for ReverseDns {
45 fn identifier(&self) -> &'static str {
46 "reverse_dns"
47 }
48
49 fn usage(&self) -> &'static str {
50 "Performs a reverse DNS lookup on the provided IP address to retrieve the associated hostname."
51 }
52
53 fn category(&self) -> &'static str {
54 Category::System.as_ref()
55 }
56
57 fn return_kind(&self) -> u16 {
58 kind::BYTES
59 }
60
61 fn parameters(&self) -> &'static [Parameter] {
62 const PARAMETERS: &[Parameter] = &[Parameter::required(
63 "value",
64 kind::BYTES,
65 "The IP address (IPv4 or IPv6) to perform the reverse DNS lookup on.",
66 )];
67 PARAMETERS
68 }
69
70 fn examples(&self) -> &'static [Example] {
71 &[example! {
72 title: "Example",
73 source: r#"reverse_dns!("127.0.0.1")"#,
74 result: Ok("localhost"),
75 }]
76 }
77
78 #[cfg(not(target_arch = "wasm32"))]
79 fn compile(
80 &self,
81 _state: &state::TypeState,
82 _ctx: &mut FunctionCompileContext,
83 arguments: ArgumentList,
84 ) -> Compiled {
85 let value = arguments.required("value");
86
87 Ok(ReverseDnsFn { value }.as_expr())
88 }
89
90 #[cfg(target_arch = "wasm32")]
91 fn compile(
92 &self,
93 _state: &state::TypeState,
94 ctx: &mut FunctionCompileContext,
95 _arguments: ArgumentList,
96 ) -> Compiled {
97 Ok(super::WasmUnsupportedFunction::new(ctx.span(), TypeDef::bytes().fallible()).as_expr())
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::value;
105
106 test_function![
107 reverse_dns => ReverseDns;
108
109 invalid_ip {
110 args: func_args![value: value!("999.999.999.999")],
111 want: Err("unable to parse IP address: invalid IP address syntax"),
112 tdef: TypeDef::bytes().fallible(),
113 }
114
115 google_ipv4 {
116 args: func_args![value: value!("8.8.8.8")],
117 want: Ok(value!("dns.google")),
118 tdef: TypeDef::bytes().fallible(),
119 }
120
121 google_ipv6 {
122 args: func_args![value: value!("2001:4860:4860::8844")],
123 want: Ok(value!("dns.google")),
124 tdef: TypeDef::bytes().fallible(),
125 }
126
127 invalid_type {
128 args: func_args![value: value!(1)],
129 want: Err("expected string, got integer"),
130 tdef: TypeDef::bytes().fallible(),
131 }
132 ];
133}