Skip to content

Commit

Permalink
Add: NASL builtin function
Browse files Browse the repository at this point in the history
  • Loading branch information
Kraemii committed Sep 23, 2024
1 parent 07bc95b commit 097b3f5
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 57 deletions.
2 changes: 1 addition & 1 deletion rust/src/nasl/builtin/network/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- islocalnet
- get_host_ip
- scanner_add_port
- recv_line

## Missing

Expand All @@ -28,7 +29,6 @@
- leave_multicast_group
- open_priv_sock_tcp
- open_priv_sock_udp
- recv_line
- scanner_get_port
- start_denial
- telnet_init
200 changes: 144 additions & 56 deletions rust/src/nasl/builtin/network/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ struct TCPConnection {
socket: TcpStream,
// Those values are currently unused, but needed for functions currently not implemented
tls_connection: Option<ClientConnection>,
_buffer: Option<Vec<u8>>,
// Buffer data for recv_line, to be able to read received messages line by line
buffer: Vec<u8>,
buffer_size: usize,
buffer_pos: usize,
}

impl TCPConnection {
Expand Down Expand Up @@ -171,22 +174,11 @@ impl NaslSockets {
fn open_tcp(
addr: IpAddr,
port: u16,
bufsz: Option<i64>,
timeout: Duration,
tls_config: Option<&TLSConfig>,
) -> Result<NaslSocket, FunctionErrorKind> {
// Resolve Address and Port to SocketAddr
let sock_addr = Self::resolve_socket_addr(addr, port)?;
// Create Vec depending of buffer size
let buffer = if let Some(bufsz) = bufsz {
if bufsz > 0 {
Some(Vec::with_capacity(bufsz as usize))
} else {
None
}
} else {
None
};

let socket = TcpStream::connect_timeout(&sock_addr, timeout)?;

Expand Down Expand Up @@ -215,7 +207,9 @@ impl NaslSockets {
Ok(NaslSocket::Tcp(Box::new(TCPConnection {
socket,
tls_connection,
_buffer: buffer,
buffer: vec![],
buffer_size: 0,
buffer_pos: 0,
})))
}

Expand Down Expand Up @@ -327,14 +321,13 @@ impl NaslSockets {
}
}

fn socket_recv<S: Read>(
fn socket_read<S: Read>(
socket: &mut S,
data: &mut [u8],
len: usize,
min: usize,
) -> Result<(), FunctionErrorKind> {
) -> Result<usize, FunctionErrorKind> {
let mut ret = 0;
while ret < len && ret < min {
while ret < data.len() && ret < min {
let n = socket.read(&mut data[ret..]).or_else(|e| match e.kind() {
io::ErrorKind::TimedOut => Ok(0),
_ => Err(e),
Expand All @@ -344,7 +337,65 @@ impl NaslSockets {
}
ret += n;
}
Ok(())
Ok(ret)
}

fn socket_recv(
socket: &mut TCPConnection,
data: &mut [u8],
min: usize,
) -> Result<usize, FunctionErrorKind> {
// Read from buffer
let read_length = std::cmp::min(data.len(), socket.buffer.len() - socket.buffer_pos);
if read_length > 0 {
data[..read_length].copy_from_slice(
&socket.buffer[socket.buffer_pos..socket.buffer_pos + read_length],
);
socket.buffer_pos += read_length;
}

if min > socket.buffer_size {
let read = Self::socket_read(&mut socket.socket, &mut data[read_length..], min)?;
return Ok(read_length + read);
}

let mut buffer = vec![0; socket.buffer_size];
let read = Self::socket_read(&mut socket.socket, &mut buffer, min)?;
socket.buffer = buffer[..read].to_vec();
socket.buffer_pos = 0;

Ok(read_length)
}

fn socket_recv_line<S: Read>(
socket: &mut S,
data: &mut [u8],
timeout: Option<i64>,
) -> Result<usize, FunctionErrorKind> {
let timeout = timeout.map(|timeout| Duration::from_secs(timeout as u64));
let start = if timeout.is_some() {
Some(std::time::Instant::now())
} else {
None
};
let mut bytes_read = 0;
loop {
let ret = Self::socket_read(socket, &mut data[bytes_read..], 1)?;

if ret == 0 {
if let (Some(start), Some(timeout)) = (start, timeout) {
if start.elapsed() > timeout {
break;
}
}
continue;
}
if data[bytes_read] == b'\n' {
break;
}
bytes_read += 1;
}
Ok(bytes_read)
}

/// Receives data from a TCP or UDP socket. For a UDP socket, if it cannot read data, NASL will
Expand Down Expand Up @@ -385,18 +436,13 @@ impl NaslSockets {
conn.socket
.set_read_timeout(Some(Duration::from_secs(timeout as u64)))?;
}
if let Some(tls) = conn.tls_connection.as_mut() {
let mut socket = Stream::new(tls, &mut conn.socket);
Self::socket_recv(&mut socket, &mut data, length, min)?;
} else {
Self::socket_recv(&mut conn.socket, &mut data, length, min)?;
}
let pos = Self::socket_recv(conn, &mut data, min)?;

if let Some(timeout) = old {
conn.socket.set_read_timeout(Some(timeout))?;
}

Ok(NaslValue::Data(data))
Ok(NaslValue::Data(data[..pos].to_vec()))
}
NaslSocket::Udp(conn) => {
let mut old = None;
Expand Down Expand Up @@ -441,6 +487,58 @@ impl NaslSockets {
}
}

#[nasl_function(named(socket, length, timeout))]
fn recv_line(
&self,
socket: usize,
length: usize,
timeout: Option<i64>,
) -> Result<NaslValue, FunctionErrorKind> {
let mut data = vec![0; length];
match self
.handles
.write()
.unwrap()
.handles
.get_mut(socket)
.ok_or(FunctionErrorKind::WrongArgument(format!(
"the given socket FD {socket} does not exist"
)))? {
NaslSocket::Tcp(conn) => {
if conn.buffer_size == 0 {
conn.buffer = vec![0; length];
conn.buffer_size = length;
}
let mut pos = 0;
loop {
let read = Self::socket_recv(conn, &mut data[pos..], 1)?;
if read == 0 {
break;
}
if data[pos] == b'\n' {
return Ok(NaslValue::Data(data[..pos].to_vec()));
}
pos += 1;
}
if let Some(tls) = conn.tls_connection.as_mut() {
let mut socket = Stream::new(tls, &mut conn.socket);
Self::socket_recv_line(&mut socket, &mut data, timeout)?;
} else {
Self::socket_recv_line(&mut conn.socket, &mut data, timeout)?;
}

Ok(NaslValue::Data(data))
}
NaslSocket::Udp(_) => Err(FunctionErrorKind::Diagnostic(
"This function is only available for TCP connections".to_string(),
None,
)),
NaslSocket::Close => Err(FunctionErrorKind::WrongArgument(
"the given socket FD is already closed".to_string(),
)),
}
}

/// Open a KDC socket. This function takes no arguments, but it is mandatory that keys are set. The following keys are required:
/// - Secret/kdc_hostname
/// - Secret/kdc_port
Expand Down Expand Up @@ -494,7 +592,7 @@ impl NaslSockets {
.unwrap_or(false);

let socket = match use_tcp {
true => Self::open_tcp(ip, port, None, Duration::from_secs(30), None),
true => Self::open_tcp(ip, port, Duration::from_secs(30), None),
false => Self::open_udp(ip, port),
}?;

Expand Down Expand Up @@ -530,16 +628,13 @@ impl NaslSockets {
// priority: Option<&str>,
context: &Context,
) -> Result<NaslValue, FunctionErrorKind> {
// TODO: Remove or implement bufsz
let _ = bufsz;
// Get port
let port = verify_port(port)?;
let transport = transport.unwrap_or(-1);

let addr = context.target();
if addr.is_empty() {
return Err(FunctionErrorKind::Dirty(
"A target must be specified to open a socket".to_string(),
));
}
let addr = ipstr2ipaddr(context.target())?;

self.wait_before_next_probe();

Expand All @@ -566,28 +661,26 @@ impl NaslSockets {
// Auto Detection
Some(OpenvasEncaps::Auto) => {
// Try SSL/TLS first
if let Ok(fd) =
self.open_sock_tcp_tls(addr, port, bufsz, timeout, vhost, context)
if let Ok(fd) = self.open_sock_tcp_tls(addr, port, timeout, vhost, context) {
fds.push(self.add(fd))
// TODO: Set port transport
} else if let Ok(fd) = self.open_sock_tcp_ip(addr, port, timeout, None, context)
{
// Then try IP
fds.push(self.add(fd))
// TODO: Set port transport
} else {
// Then try IP
if let Ok(fd) =
self.open_sock_tcp_ip(addr, port, bufsz, timeout, None, context)
{
fds.push(self.add(fd))
// TODO: Set port transport
}
return Err(FunctionErrorKind::Diagnostic(
"Unable to create TCP socket via auto encaps".to_string(),
None,
));
}
}
// IP
Some(OpenvasEncaps::Ip) => {
if let Ok(fd) = self.open_sock_tcp_ip(addr, port, bufsz, timeout, None, context)
{
fds.push(self.add(fd))
// TODO: Set port transport
}
let fd = self.open_sock_tcp_ip(addr, port, timeout, None, context)?;
fds.push(self.add(fd))
// TODO: Set port transport
}
// Unsupported transport layer
None | Some(OpenvasEncaps::Max) => {
Expand All @@ -598,8 +691,7 @@ impl NaslSockets {
// TLS/SSL
Some(tls_version) => match tls_version {
OpenvasEncaps::Tls12 | OpenvasEncaps::Tls13 => {
let fd =
self.open_sock_tcp_tls(addr, port, bufsz, timeout, vhost, context)?;
let fd = self.open_sock_tcp_tls(addr, port, timeout, vhost, context)?;
fds.push(self.add(fd))
}
_ => {
Expand All @@ -620,14 +712,12 @@ impl NaslSockets {

fn open_sock_tcp_ip(
&self,
addr: &str,
addr: IpAddr,
port: u16,
bufsz: Option<i64>,
timeout: Duration,
tls_config: Option<TLSConfig>,
context: &Context,
) -> Result<NaslSocket, FunctionErrorKind> {
let addr = ipstr2ipaddr(addr)?;
let mut retry = super::get_kb_item(context, "timeout_retry")?
.map(|val| match val {
NaslValue::String(val) => val.parse::<i64>().unwrap_or_default(),
Expand All @@ -637,7 +727,7 @@ impl NaslSockets {
.unwrap_or(2);

while retry >= 0 {
match Self::open_tcp(addr, port, bufsz, timeout, tls_config.as_ref()) {
match Self::open_tcp(addr, port, timeout, tls_config.as_ref()) {
Ok(socket) => return Ok(socket),
Err(err) => {
if !matches!(err, FunctionErrorKind::IOError(io::ErrorKind::TimedOut)) {
Expand Down Expand Up @@ -680,9 +770,8 @@ impl NaslSockets {

fn open_sock_tcp_tls(
&self,
addr: &str,
addr: IpAddr,
port: u16,
bufsz: Option<i64>,
timeout: Duration,
hostname: &str,
context: &Context,
Expand Down Expand Up @@ -756,7 +845,6 @@ impl NaslSockets {
self.open_sock_tcp_ip(
addr,
port,
bufsz,
timeout,
Some(TLSConfig { config, server }),
context,
Expand All @@ -769,7 +857,6 @@ impl NaslSockets {
let port = verify_port(port)?;
dbg!(context.target());
let addr = ipstr2ipaddr(context.target())?;
// let addr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));

let socket = Self::open_udp(addr, port)?;
let fd = self.add(socket);
Expand All @@ -788,5 +875,6 @@ function_set! {
(NaslSockets::close, "close"),
(NaslSockets::send, "send"),
(NaslSockets::recv, "recv"),
(NaslSockets::recv_line, "recv_line"),
)
}

0 comments on commit 097b3f5

Please sign in to comment.