Skip to content

Commit

Permalink
apply suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
jjnicola committed Dec 2, 2024
1 parent e1319d9 commit 66a379f
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 209 deletions.
280 changes: 85 additions & 195 deletions rust/src/nasl/builtin/host/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, FunctionErrorKind> {
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<NaslValue, FunctionErrorKind> {
if let Some(hns) = context.target_vhosts() {
#[nasl_function]
fn get_host_names(context: &Context) -> Result<NaslValue, FunctionErrorKind> {
let hns = context.target_vhosts();
if !hns.is_empty() {
let hns = hns
.into_iter()
.map(|(h, _s)| NaslValue::String(h))
.collect::<Vec<_>>();
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.
Expand All @@ -50,22 +67,14 @@ fn get_host_ip(context: &Context) -> Result<IpAddr, FunctionErrorKind> {
///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<NaslValue, FunctionErrorKind> {
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)
}

Expand All @@ -74,54 +83,45 @@ pub fn get_host_name(
_register: &Register,
context: &Context,
) -> Result<NaslValue, FunctionErrorKind> {
let mut v = Vec::new();
if let Some(vh) = context.target_vhosts() {
v = vh
.into_iter()
.map(|(v, _s)| NaslValue::String(v))
.collect::<Vec<_>>();
}
let vh = context.target_vhosts();
let v = if !vh.is_empty() {
vh.iter()
.map(|(v, _s)| NaslValue::String(v.to_string()))
.collect::<Vec<_>>()
} 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
if !v.is_empty() {
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<NaslValue, FunctionErrorKind> {
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.
Expand All @@ -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<NaslValue, FunctionErrorKind> {
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<NaslValue, FunctionErrorKind> {
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<NaslValue, FunctionErrorKind> {
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<NaslValue, FunctionErrorKind> {
let target_ori = match context.target().is_empty() {
#[nasl_function]
fn target_is_ipv6(context: &Context) -> Result<bool, FunctionErrorKind> {
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::<Vec<_>>();
Ok(NaslValue::Boolean(!v.is_empty()))
}
Err(_) => Err(FunctionErrorKind::diagnostic_ret_null("address is Null")),
}
Ok(target.parse::<Ipv6Addr>().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<NaslValue, FunctionErrorKind> {
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<IpAddr> = h1.into_iter().map(|x| x.ip()).collect::<Vec<_>>();
let addr2: Vec<IpAddr> = h2.into_iter().map(|x| x.ip()).collect::<Vec<_>>();

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<bool>) -> Result<bool, FunctionErrorKind> {
let h1 = resolve(h1.to_string())?;
let h2 = resolve(h2.to_string())?;

let hostnames1 = h1
.iter()
.filter_map(|x| lookup_addr(x).ok())
.collect::<Vec<_>>();
let hostnames2 = addr2
.clone()
.into_iter()
.map(|x| lookup_addr(&x))
let hostnames2 = h2
.iter()
.filter_map(|x| lookup_addr(x).ok())
.collect::<Vec<_>>();

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;
Expand Down
15 changes: 6 additions & 9 deletions rust/src/nasl/utils/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -462,8 +459,8 @@ impl<'a> Context<'a> {
}

/// Get the target VHost list
pub fn target_vhosts(&self) -> Option<Vec<(String, String)>> {
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) {
Expand Down
Loading

0 comments on commit 66a379f

Please sign in to comment.