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 20, 2024
1 parent 3f619e8 commit 9a2473e
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 57 deletions.
2 changes: 1 addition & 1 deletion rust/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/nasl-builtin-network/src/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,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 @@ -170,22 +173,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 @@ -214,7 +206,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 @@ -326,14 +320,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 @@ -343,7 +336,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 @@ -384,18 +435,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 @@ -440,6 +486,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 @@ -493,7 +591,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 @@ -529,16 +627,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 @@ -565,28 +660,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 @@ -597,8 +690,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 @@ -619,14 +711,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 @@ -636,7 +726,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 @@ -679,9 +769,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 @@ -755,7 +844,6 @@ impl NaslSockets {
self.open_sock_tcp_ip(
addr,
port,
bufsz,
timeout,
Some(TLSConfig { config, server }),
context,
Expand All @@ -768,7 +856,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 @@ -787,5 +874,6 @@ function_set! {
(NaslSockets::close, "close"),
(NaslSockets::send, "send"),
(NaslSockets::recv, "recv"),
(NaslSockets::recv_line, "recv_line"),
)
}

0 comments on commit 9a2473e

Please sign in to comment.