diff --git a/rust/src/nasl/builtin/host/mod.rs b/rust/src/nasl/builtin/host/mod.rs index 8a6d148a6..9d57a0bc8 100644 --- a/rust/src/nasl/builtin/host/mod.rs +++ b/rust/src/nasl/builtin/host/mod.rs @@ -6,28 +6,45 @@ mod tests; use std::{ - net::{IpAddr, ToSocketAddrs}, + net::{IpAddr, Ipv6Addr}, str::FromStr, }; use dns_lookup::lookup_addr; +use nasl_function_proc_macro::nasl_function; -use crate::function_set; use crate::nasl::utils::{error::FunctionErrorKind, hosts::resolve}; +use crate::{function_set, nasl::FromNaslValue}; use crate::nasl::syntax::NaslValue; -use crate::nasl::utils::{Context, ContextType, Register}; +use crate::nasl::utils::{Context, Register}; + +struct Hostname(String); +impl<'a> FromNaslValue<'a> for Hostname { + fn from_nasl_value(value: &'a NaslValue) -> Result { + let str = String::from_nasl_value(value)?; + if str.is_empty() { + Err(FunctionErrorKind::diagnostic_ret_null("Empty hostname.")) + } else { + Ok(Self(str)) + } + } +} /// Get a list of found hostnames or a IP of the current target in case no hostnames were found yet. -fn get_host_names(_register: &Register, context: &Context) -> Result { - if let Some(hns) = context.target_vhosts() { +#[nasl_function] +fn get_host_names(context: &Context) -> Result { + let hns = context.target_vhosts(); + if !hns.is_empty() { let hns = hns .into_iter() .map(|(h, _s)| NaslValue::String(h)) .collect::>(); return Ok(NaslValue::Array(hns)); }; - Ok(NaslValue::String(context.target().to_string())) + Ok(NaslValue::Array(vec![NaslValue::String( + context.target().to_string(), + )])) } /// Return the target's IP address as IpAddr. @@ -50,22 +67,14 @@ fn get_host_ip(context: &Context) -> Result { ///Expands the vHosts list with the given hostname. ///The mandatory parameter hostname is of type string. It contains the hostname which should be added to the list of vHosts ///Additionally a source, how the hostname was detected can be added with the named argument source as a string. If it is not given, the value NASL is set as default. +#[nasl_function(named(hostname, source))] pub fn add_host_name( - register: &Register, context: &Context, + hostname: Hostname, + source: Option<&str>, ) -> Result { - let hostname = match register.named("hostname") { - Some(ContextType::Value(NaslValue::String(x))) if !x.is_empty() => x.clone(), - _ => { - return Err(FunctionErrorKind::diagnostic_ret_null("Empty Hostname")); - } - }; - let source = match register.named("source") { - Some(ContextType::Value(NaslValue::String(x))) if !x.is_empty() => x.clone(), - _ => "NASL".to_string(), - }; - - context.add_hostname(hostname, source); + let source = source.filter(|x| !x.is_empty()).unwrap_or("NASL"); + context.add_hostname(hostname.0, source.into()); Ok(NaslValue::Null) } @@ -74,13 +83,15 @@ pub fn get_host_name( _register: &Register, context: &Context, ) -> Result { - let mut v = Vec::new(); - if let Some(vh) = context.target_vhosts() { - v = vh - .into_iter() - .map(|(v, _s)| NaslValue::String(v)) - .collect::>(); - } + let vh = context.target_vhosts(); + let v = if !vh.is_empty() { + vh.iter() + .map(|(v, _s)| NaslValue::String(v.to_string())) + .collect::>() + } else { + vec![] + }; + //TODO: store the current hostname being forked. //TODO: don't fork if expand_vhost is disabled. //TODO: don't fork if already in a vhost @@ -88,40 +99,29 @@ pub fn get_host_name( return Ok(NaslValue::Fork(v)); } - if let Ok(ip) = get_host_ip(context) { - match lookup_addr(&ip) { - Ok(host) => Ok(NaslValue::String(host)), - Err(_) => Ok(NaslValue::String(ip.to_string())), - } - } else { - Ok(NaslValue::String(context.target().to_string())) - } + let host = match get_host_ip(context) { + Ok(ip) => match lookup_addr(&ip) { + Ok(host) => host, + Err(_) => ip.to_string(), + }, + Err(_) => context.target().to_string(), + }; + Ok(NaslValue::String(host)) } /// This function returns the source of detection of a given hostname. /// The named parameter hostname is a string containing the hostname. /// When no hostname is given, the current scanned host is taken. /// If no virtual hosts are found yet this function always returns IP-address. -pub fn get_host_name_source( - register: &Register, - context: &Context, -) -> Result { - let hostname = match register.named("hostname") { - Some(ContextType::Value(NaslValue::String(x))) if !x.is_empty() => x.clone(), - _ => { - return Err(FunctionErrorKind::diagnostic_ret_null("Empty Hostname")); - } - }; - - if let Some(vh) = context.target_vhosts() { - if let Some(source) = vh - .into_iter() - .find_map(|(v, s)| if v == hostname { Some(s) } else { None }) - { - return Ok(NaslValue::String(source)); +#[nasl_function(named(hostname))] +pub fn get_host_name_source(context: &Context, hostname: Hostname) -> String { + let vh = context.target_vhosts(); + if !vh.is_empty() { + if let Some((_, source)) = vh.into_iter().find(|(v, _)| v == &hostname.0) { + return source; }; } - Ok(NaslValue::String(context.target().to_string())) + context.target().to_string() } /// Return the target's IP address or 127.0.0.1 if not set. @@ -134,169 +134,59 @@ fn nasl_get_host_ip( } /// Get an IP address corresponding to the host name -fn resolve_host_name( - register: &Register, - _context: &Context, -) -> Result { - let hostname = match register.named("hostname") { - Some(ContextType::Value(NaslValue::String(x))) if !x.is_empty() => x.clone(), - _ => { - return Err(FunctionErrorKind::diagnostic_ret_null("Missing Hostname")); - } - }; - - match resolve(hostname)? { - Some(mut a) => { - let address = a.next().map_or_else(String::new, |x| x.to_string()); - let address = &address[..(address.len() - 5)]; - Ok(NaslValue::String(address.to_string())) - } - None => Ok(NaslValue::Null), - } +#[nasl_function(named(hostname))] +fn resolve_host_name(hostname: Hostname) -> String { + resolve(hostname.0).map_or_else( + |_| "127.0.0.1".to_string(), + |x| x.first().map_or("127.0.0.1".to_string(), |v| v.to_string()), + ) } -/// Resolve a hostname to all found addresses and return them in an NaslValue::Array -fn resolve_hostname_to_multiple_ips( - register: &Register, - _context: &Context, -) -> Result { - let hostname = match register.named("hostname") { - Some(ContextType::Value(NaslValue::String(x))) if !x.is_empty() => x.clone(), - _ => { - return Err(FunctionErrorKind::diagnostic_ret_null("Missing Hostname")); - } - }; - match resolve(hostname)? { - Some(addr) => { - let ips = addr - .into_iter() - .map(|x| { - let address = x.to_string(); - let address = &address[..(address.len() - 5)]; - NaslValue::String(address.to_string()) - }) - .collect(); - Ok(NaslValue::Array(ips)) - } - // assumes that target is already a hostname - None => Ok(NaslValue::Null), - } +/// Resolve a hostname to all found addresses and return them in an NaslValue::Array +#[nasl_function(named(hostname))] +fn resolve_hostname_to_multiple_ips(hostname: Hostname) -> Result { + let ips = resolve(hostname.0)? + .into_iter() + .map(|x| NaslValue::String(x.to_string())) + .collect(); + Ok(NaslValue::Array(ips)) } /// Check if the currently scanned target is an IPv6 address. /// Return TRUE if the current target is an IPv6 address, else FALSE. In case of an error, NULL is returned. -fn target_is_ipv6(_register: &Register, context: &Context) -> Result { - let target_ori = match context.target().is_empty() { +#[nasl_function] +fn target_is_ipv6(context: &Context) -> Result { + let target = match context.target().is_empty() { true => { return Err(FunctionErrorKind::diagnostic_ret_null("Address is NULL!")); } false => context.target(), }; - - let mut target = target_ori.to_string(); - // IPV6 must be between [] - if target.contains(":") { - let mut t_aux = String::from("["); - t_aux.push_str(target_ori); - t_aux.push(']'); - target = t_aux; - } - - // SocketAddr requires a socket, not only the IP addr. - target.push_str(":5000"); - match target.to_socket_addrs() { - Ok(addr) => { - let v = addr.into_iter().filter(|x| x.is_ipv6()).collect::>(); - Ok(NaslValue::Boolean(!v.is_empty())) - } - Err(_) => Err(FunctionErrorKind::diagnostic_ret_null("address is Null")), - } + Ok(target.parse::().is_ok()) } /// Compare if two hosts are the same. /// The first two unnamed arguments are string containing the host to compare /// If the named argument cmp_hostname is set to TRUE, the given hosts are resolved into their hostnames -fn same_host(register: &Register, _: &Context) -> Result { - let positional = register.positional(); - if positional.len() != 2 { - return Err(FunctionErrorKind::diagnostic_ret_null( - "same_host needs two parameters!", - )); - } - - let h1 = match &positional[0] { - NaslValue::String(x) => { - if let Some(h) = resolve(x.to_string())? { - h - } else { - return Err(FunctionErrorKind::diagnostic_ret_null( - "Wrong parameter type", - )); - } - } - _ => { - return Err(FunctionErrorKind::diagnostic_ret_null( - "Wrong parameter type", - )); - } - }; - - let h2 = match &positional[1] { - NaslValue::String(x) => { - if let Some(h) = resolve(x.to_string())? { - h - } else { - return Err(FunctionErrorKind::diagnostic_ret_null( - "Wrong parameter type", - )); - } - } - _ => { - return Err(FunctionErrorKind::diagnostic_ret_null( - "Wrong parameter type", - )); - } - }; - - let cmp_hostname = match register.named("cmp_hostname") { - Some(ContextType::Value(NaslValue::Boolean(x))) => *x, - _ => false, - }; - - let addr1: Vec = h1.into_iter().map(|x| x.ip()).collect::>(); - let addr2: Vec = h2.into_iter().map(|x| x.ip()).collect::>(); - - let hostnames1 = addr1 - .clone() - .into_iter() - .map(|x| lookup_addr(&x)) +#[nasl_function(named(cmp_hostname))] +fn same_host(h1: &str, h2: &str, cmp_hostname: Option) -> Result { + let h1 = resolve(h1.to_string())?; + let h2 = resolve(h2.to_string())?; + + let hostnames1 = h1 + .iter() + .filter_map(|x| lookup_addr(x).ok()) .collect::>(); - let hostnames2 = addr2 - .clone() - .into_iter() - .map(|x| lookup_addr(&x)) + let hostnames2 = h2 + .iter() + .filter_map(|x| lookup_addr(x).ok()) .collect::>(); - let mut flag = false; - for a1 in addr1.iter() { - for a2 in addr2.iter() { - if a1.eq(a2) { - flag = true; - } - } - } - - if cmp_hostname { - for hn1 in hostnames1.iter() { - for hn2 in hostnames2.iter() { - if hn1.is_ok() && hn2.is_ok() && hn1.as_ref().unwrap() == hn2.as_ref().unwrap() { - flag = true; - } - } - } - } + let any_ip_address_matches = h1.iter().any(|a1| h2.contains(a1)); + let any_hostname_matches = hostnames1.iter().any(|h1| hostnames2.contains(h1)); + let cmp_hostname = cmp_hostname.filter(|x| *x).unwrap_or(false); - Ok(NaslValue::Boolean(flag)) + Ok(any_ip_address_matches || (cmp_hostname && any_hostname_matches)) } pub struct Host; diff --git a/rust/src/nasl/utils/context.rs b/rust/src/nasl/utils/context.rs index 60bc00cd2..cc8d39d0f 100644 --- a/rust/src/nasl/utils/context.rs +++ b/rust/src/nasl/utils/context.rs @@ -361,13 +361,10 @@ impl Target { self.target = target; // Store the IpAddr if possible, else default to localhost - if let Ok(host) = resolve(self.target.clone()) { - let t = match host { - Some(mut a) => a.next().map_or_else(String::new, |x| x.ip().to_string()), - None => "127.0.0.1".to_string(), - }; - self.ip_addr = IpAddr::from_str(t.as_str()).unwrap(); - } + self.ip_addr = match resolve(self.target.clone()) { + Ok(a) => *a.first().unwrap_or(&IpAddr::from_str("127.0.0.1").unwrap()), + Err(_) => IpAddr::from_str("127.0.0.1").unwrap(), + }; self } @@ -462,8 +459,8 @@ impl<'a> Context<'a> { } /// Get the target VHost list - pub fn target_vhosts(&self) -> Option> { - Some(self.target.vhosts.lock().unwrap().clone()) + pub fn target_vhosts(&self) -> Vec<(String, String)> { + self.target.vhosts.lock().unwrap().clone() } pub fn set_target(&mut self, target: String) { diff --git a/rust/src/nasl/utils/hosts.rs b/rust/src/nasl/utils/hosts.rs index c1baaca61..f3d105128 100644 --- a/rust/src/nasl/utils/hosts.rs +++ b/rust/src/nasl/utils/hosts.rs @@ -2,18 +2,19 @@ // // SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception -use std::net::{SocketAddr, ToSocketAddrs}; +use std::net::{IpAddr, ToSocketAddrs}; use crate::nasl::utils::error::FunctionErrorKind; -pub fn resolve( - mut hostname: String, -) -> Result>>, FunctionErrorKind> { +pub fn resolve(mut hostname: String) -> Result, FunctionErrorKind> { //std::net to_socket_addrs() requires a port. Therefore, using a dummy port hostname.push_str(":5000"); match hostname.to_socket_addrs() { - Ok(addr) => Ok(Some(Box::new(addr))), + Ok(addr) => { + let ips = addr.into_iter().map(|x| x.ip()).collect::>(); + Ok(ips) + } // assumes that target is already a hostname Err(_) => Err(FunctionErrorKind::diagnostic_ret_null("Missing Hostname")), }