diff --git a/rust/src/feed/transpile/error.rs b/rust/src/feed/transpile/error.rs index 2b70916fb..26c006eb7 100644 --- a/rust/src/feed/transpile/error.rs +++ b/rust/src/feed/transpile/error.rs @@ -5,7 +5,7 @@ use crate::nasl::syntax::{LoadError, Statement}; use super::verify; use super::Replace; -#[derive(Debug, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Clone, PartialEq, Error)] /// Error during transpiling pub enum TranspileError { /// Loader is unable to handle operation @@ -19,7 +19,7 @@ pub enum TranspileError { Replace(#[from] ReplaceError), } -#[derive(Debug, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Clone, PartialEq, Error)] /// Error cases on a replace operation pub enum ReplaceError { /// The replace operation is invalid on statement diff --git a/rust/src/feed/update/error.rs b/rust/src/feed/update/error.rs index 9a59437ac..04a77f49a 100644 --- a/rust/src/feed/update/error.rs +++ b/rust/src/feed/update/error.rs @@ -9,7 +9,7 @@ use thiserror::Error; use crate::feed::{verify, VerifyError}; -#[derive(Debug, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Error)] /// Errors within feed handling pub enum ErrorKind { /// An InterpretError occurred while interpreting @@ -32,7 +32,7 @@ pub enum ErrorKind { VerifyError(#[from] verify::Error), } -#[derive(Debug, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Error)] #[error("Error with key '{key}': {kind}")] /// ErrorKind and key of error pub struct Error { diff --git a/rust/src/feed/verify/mod.rs b/rust/src/feed/verify/mod.rs index 2cfe57dfa..2b26c092a 100644 --- a/rust/src/feed/verify/mod.rs +++ b/rust/src/feed/verify/mod.rs @@ -33,7 +33,7 @@ use sequoia_ipc::keybox::{Keybox, KeyboxRecord}; use sequoia_openpgp as openpgp; use thiserror::Error; -#[derive(Debug, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Clone, PartialEq, Error)] /// Defines error cases that can happen while verifying pub enum Error { #[error("Incorrect feed.")] diff --git a/rust/src/nasl/builtin/cert/mod.rs b/rust/src/nasl/builtin/cert/mod.rs index f34d1e7d0..733a220c4 100644 --- a/rust/src/nasl/builtin/cert/mod.rs +++ b/rust/src/nasl/builtin/cert/mod.rs @@ -6,16 +6,24 @@ use std::collections::HashMap; use std::sync::RwLock; use nasl_function_proc_macro::nasl_function; +use thiserror::Error; use x509_certificate::X509Certificate; use x509_parser::prelude::GeneralName; -use crate::{ - function_set, - nasl::{FunctionErrorKind, NaslValue}, -}; +use crate::nasl::prelude::*; use super::string::encode_hex; +#[derive(Debug, Error)] +pub enum CertError { + #[error("Unable to calculate SHA256 fingerprint")] + UnableToCalculateSHA256Fingerprint, + #[error("Unable to calculate SHA1 fingerprint")] + UnableToCalculateSHA1Fingerprint, + #[error("Query parameter 'all' not implemented yet.")] + QueryParamAllNotImplemented, +} + fn sign_alg_oid_to_name(oid: &str) -> &str { match oid { "1.2.840.10040.4.1" => "id-dsa", @@ -118,7 +126,7 @@ pub enum CertCommands { } impl TryFrom<&str> for CertCommands { - type Error = FunctionErrorKind; + type Error = FnError; fn try_from(value: &str) -> Result { match value { @@ -138,9 +146,9 @@ impl TryFrom<&str> for CertCommands { "modulus" => Ok(Self::Modulus), "exponent" => Ok(Self::Exponent), "key-size" => Ok(Self::KeySize), - _ => Err(FunctionErrorKind::WrongArgument( - "The given query is not valid.".to_string(), - )), + _ => Err( + ArgumentError::WrongArgument("The given query is not valid.".to_string()).into(), + ), } } } @@ -176,7 +184,7 @@ impl NaslCerts { /// On success the function returns a cert identifier that can be used /// for further operations. #[nasl_function] - fn cert_open(&self, cert: &[u8]) -> Result { + fn cert_open(&self, cert: &[u8]) -> Result { if let Ok(cert) = X509Certificate::from_der(cert) { return Ok(self.insert(cert)); } @@ -187,10 +195,11 @@ impl NaslCerts { return Ok(self.insert(cert)); } - Err(FunctionErrorKind::WrongArgument( + Err(ArgumentError::WrongArgument( "The given string is not a valid DER, BER or PEM encoded X.509 certificate." .to_string(), - )) + ) + .into()) } /// Release a certificate object. @@ -358,12 +367,12 @@ impl NaslCerts { cert_handle: usize, query: &str, idx: Option, - ) -> Result { + ) -> Result { let idx = idx.unwrap_or(0); let handle = self.0.read().unwrap(); let cert = handle.certs.get(&cert_handle).ok_or_else(|| { - FunctionErrorKind::WrongArgument("The given file descriptor is not valid.".to_string()) + ArgumentError::WrongArgument("The given file descriptor is not valid.".to_string()) })?; let result = match CertCommands::try_from(query)? { CertCommands::Serial => { @@ -387,27 +396,12 @@ impl NaslCerts { CertCommands::FprSha256 => cert .sha256_fingerprint() .map(|fpr| NaslValue::String(encode_hex(fpr.as_ref()))) - .map_err(|_| { - FunctionErrorKind::Diagnostic( - "Unable to calculate SHA256 fingerprint".to_string(), - None, - ) - })?, + .map_err(|_| CertError::UnableToCalculateSHA256Fingerprint)?, CertCommands::FprSha1 => cert .sha1_fingerprint() .map(|fpr| NaslValue::String(encode_hex(fpr.as_ref()))) - .map_err(|_| { - FunctionErrorKind::Diagnostic( - "Unable to calculate SHA1 fingerprint".to_string(), - None, - ) - })?, - CertCommands::All => { - return Err(FunctionErrorKind::Diagnostic( - "Query parameter 'all' is not implemented yet".to_string(), - None, - )) - } + .map_err(|_| CertError::UnableToCalculateSHA1Fingerprint)?, + CertCommands::All => return Err(CertError::QueryParamAllNotImplemented.into()), CertCommands::Hostnames => NaslValue::Array( Self::hostnames(cert) .into_iter() diff --git a/rust/src/nasl/builtin/cryptographic/aes_cbc.rs b/rust/src/nasl/builtin/cryptographic/aes_cbc.rs index db69748ac..17b71ce49 100644 --- a/rust/src/nasl/builtin/cryptographic/aes_cbc.rs +++ b/rust/src/nasl/builtin/cryptographic/aes_cbc.rs @@ -12,15 +12,12 @@ use aes::{ }; use cbc::{Decryptor, Encryptor}; -use crate::function_set; -use crate::nasl::syntax::NaslValue; -use crate::nasl::utils::error::FunctionErrorKind; -use crate::nasl::utils::{Context, Register}; +use crate::nasl::prelude::*; use super::{get_data, get_iv, get_key, get_len, Crypt}; /// Base function for en- and decrypting Cipher Block Chaining (CBC) mode -fn cbc(register: &Register, crypt: Crypt) -> Result +fn cbc(register: &Register, crypt: Crypt) -> Result where D: BlockCipher + BlockEncrypt + BlockDecrypt + KeyInit, { @@ -35,7 +32,7 @@ where let res = Encryptor::::new_from_slices(key, iv); match res { Ok(encryptor) => Ok(encryptor.encrypt_padded_vec_mut::(data).into()), - Err(e) => Err(FunctionErrorKind::WrongArgument(e.to_string())), + Err(e) => Err(ArgumentError::WrongArgument(e.to_string()).into()), } } Crypt::Decrypt => { @@ -47,20 +44,21 @@ where // len should not be more than the length of the data if len > data.len() { - return Err(FunctionErrorKind::wrong_argument( + return Err(ArgumentError::wrong_argument( "len", format!("<={:?}", data.len()).as_str(), len.to_string().as_str(), - )); + ) + .into()); } let res = Decryptor::::new_from_slices(key, iv); match res { Ok(decryptor) => Ok(decryptor .decrypt_padded_vec_mut::(data) - .map_err(|e| FunctionErrorKind::WrongArgument(e.to_string()))?[..len] + .map_err(|e| ArgumentError::WrongArgument(e.to_string()))?[..len] .to_vec() .into()), - Err(e) => Err(FunctionErrorKind::WrongArgument(e.to_string())), + Err(e) => Err(ArgumentError::WrongArgument(e.to_string()).into()), } } } @@ -73,7 +71,7 @@ where /// Currently the data is filled with zeroes. Therefore the length of the encrypted data must be /// known for decryption. If no length is given, the last block is decrypted as a whole. /// - The iv must have a length of 16 bytes -fn aes128_cbc_encrypt(register: &Register, _: &Context) -> Result { +fn aes128_cbc_encrypt(register: &Register, _: &Context) -> Result { cbc::(register, Crypt::Encrypt) } @@ -85,7 +83,7 @@ fn aes128_cbc_encrypt(register: &Register, _: &Context) -> Result Result { +fn aes128_cbc_decrypt(register: &Register, _: &Context) -> Result { cbc::(register, Crypt::Decrypt) } @@ -96,7 +94,7 @@ fn aes128_cbc_decrypt(register: &Register, _: &Context) -> Result Result { +fn aes192_cbc_encrypt(register: &Register, _: &Context) -> Result { cbc::(register, Crypt::Encrypt) } @@ -108,7 +106,7 @@ fn aes192_cbc_encrypt(register: &Register, _: &Context) -> Result Result { +fn aes192_cbc_decrypt(register: &Register, _: &Context) -> Result { cbc::(register, Crypt::Decrypt) } @@ -119,7 +117,7 @@ fn aes192_cbc_decrypt(register: &Register, _: &Context) -> Result Result { +fn aes256_cbc_encrypt(register: &Register, _: &Context) -> Result { cbc::(register, Crypt::Encrypt) } @@ -131,7 +129,7 @@ fn aes256_cbc_encrypt(register: &Register, _: &Context) -> Result Result { +fn aes256_cbc_decrypt(register: &Register, _: &Context) -> Result { cbc::(register, Crypt::Decrypt) } diff --git a/rust/src/nasl/builtin/cryptographic/aes_ccm.rs b/rust/src/nasl/builtin/cryptographic/aes_ccm.rs index 9eaa248d3..b52e6d0f5 100644 --- a/rust/src/nasl/builtin/cryptographic/aes_ccm.rs +++ b/rust/src/nasl/builtin/cryptographic/aes_ccm.rs @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception -use crate::nasl::utils::error::{FunctionErrorKind, GeneralErrorType}; +use crate::nasl::utils::error::FnError; use aes::cipher::{BlockCipher, BlockDecrypt, BlockEncrypt, BlockSizeUser}; use aes::{Aes128, Aes192, Aes256}; use ccm::{ @@ -17,7 +17,7 @@ use crate::nasl::utils::{Context, Register}; use crate::function_set; -use super::{get_aad, get_data, get_iv, get_key, get_len, Crypt}; +use super::{get_aad, get_data, get_iv, get_key, get_len, Crypt, CryptographicError}; /// Core function to en- and decrypt data. Throws error in case of failure. fn ccm_crypt( @@ -41,7 +41,7 @@ where } /// Base function for ccm en- and decryption. Sets the tag length to 16. -fn ccm(register: &Register, crypt: Crypt, auth: bool) -> Result +fn ccm(register: &Register, crypt: Crypt, auth: bool) -> Result where D: BlockCipher + BlockSizeUser + BlockEncrypt + BlockDecrypt + KeyInit, { @@ -60,9 +60,7 @@ where // Error handling match res { Ok(x) => Ok(NaslValue::Data(x)), - Err(_) => Err(FunctionErrorKind::GeneralError( - GeneralErrorType::UnexpectedData("unable to en-/decrypt data".to_string()), - )), + Err(_) => Err(CryptographicError::AesCcmUnableToEncrypt.into()), } } @@ -73,7 +71,7 @@ where /// - The length of the key should be 16 bytes long /// - The iv must have a length of 7-13 bytes /// - The tag_size default is 16, it can be set to either 4, 6, 8, 10, 12, 14 or 16 -fn aes128_ccm_encrypt(register: &Register, _: &Context) -> Result { +fn aes128_ccm_encrypt(register: &Register, _: &Context) -> Result { ccm::(register, Crypt::Encrypt, false) } @@ -84,10 +82,7 @@ fn aes128_ccm_encrypt(register: &Register, _: &Context) -> Result Result { +fn aes128_ccm_encrypt_auth(register: &Register, _: &Context) -> Result { ccm::(register, Crypt::Encrypt, true) } @@ -98,7 +93,7 @@ fn aes128_ccm_encrypt_auth( /// - The length of the key should be 16 bytes long /// - The iv must have a length of 7-13 bytes /// - The tag_size default is 16, it can be set to either 4, 6, 8, 10, 12, 14 or 16 -fn aes128_ccm_decrypt(register: &Register, _: &Context) -> Result { +fn aes128_ccm_decrypt(register: &Register, _: &Context) -> Result { ccm::(register, Crypt::Decrypt, false) } @@ -109,10 +104,7 @@ fn aes128_ccm_decrypt(register: &Register, _: &Context) -> Result Result { +fn aes128_ccm_decrypt_auth(register: &Register, _: &Context) -> Result { ccm::(register, Crypt::Decrypt, true) } @@ -123,7 +115,7 @@ fn aes128_ccm_decrypt_auth( /// - The length of the key should be 16 bytes long /// - The iv must have a length of 7-13 bytes /// - The tag_size default is 16, it can be set to either 4, 6, 8, 10, 12, 14 or 16 -fn aes192_ccm_encrypt(register: &Register, _: &Context) -> Result { +fn aes192_ccm_encrypt(register: &Register, _: &Context) -> Result { ccm::(register, Crypt::Encrypt, false) } @@ -134,10 +126,7 @@ fn aes192_ccm_encrypt(register: &Register, _: &Context) -> Result Result { +fn aes192_ccm_encrypt_auth(register: &Register, _: &Context) -> Result { ccm::(register, Crypt::Encrypt, true) } @@ -148,7 +137,7 @@ fn aes192_ccm_encrypt_auth( /// - The length of the key should be 16 bytes long /// - The iv must have a length of 7-13 bytes /// - The tag_size default is 16, it can be set to either 4, 6, 8, 10, 12, 14 or 16 -fn aes192_ccm_decrypt(register: &Register, _: &Context) -> Result { +fn aes192_ccm_decrypt(register: &Register, _: &Context) -> Result { ccm::(register, Crypt::Decrypt, false) } @@ -159,10 +148,7 @@ fn aes192_ccm_decrypt(register: &Register, _: &Context) -> Result Result { +fn aes192_ccm_decrypt_auth(register: &Register, _: &Context) -> Result { ccm::(register, Crypt::Decrypt, true) } @@ -173,7 +159,7 @@ fn aes192_ccm_decrypt_auth( /// - The length of the key should be 16 bytes long /// - The iv must have a length of 7-13 bytes /// - The tag_size default is 16, it can be set to either 4, 6, 8, 10, 12, 14 or 16 -fn aes256_ccm_encrypt(register: &Register, _: &Context) -> Result { +fn aes256_ccm_encrypt(register: &Register, _: &Context) -> Result { ccm::(register, Crypt::Encrypt, false) } @@ -184,10 +170,7 @@ fn aes256_ccm_encrypt(register: &Register, _: &Context) -> Result Result { +fn aes256_ccm_encrypt_auth(register: &Register, _: &Context) -> Result { ccm::(register, Crypt::Encrypt, true) } @@ -198,7 +181,7 @@ fn aes256_ccm_encrypt_auth( /// - The length of the key should be 16 bytes long /// - The iv must have a length of 7-13 bytes /// - The tag_size default is 16, it can be set to either 4, 6, 8, 10, 12, 14 or 16 -fn aes256_ccm_decrypt(register: &Register, _: &Context) -> Result { +fn aes256_ccm_decrypt(register: &Register, _: &Context) -> Result { ccm::(register, Crypt::Decrypt, false) } @@ -209,16 +192,13 @@ fn aes256_ccm_decrypt(register: &Register, _: &Context) -> Result Result { +fn aes256_ccm_decrypt_auth(register: &Register, _: &Context) -> Result { ccm::(register, Crypt::Decrypt, true) } macro_rules! ccm_call_typed { ($(($t1s: expr, $t1: ty) => $(($t2s: expr, $t2: ty)),*);*) => { - fn ccm_typed(tag_size: usize, iv_size: usize, crypt: Crypt, key: &[u8], nonce: &[u8], data: &[u8], aad: &[u8]) -> Result, aError>, FunctionErrorKind> + fn ccm_typed(tag_size: usize, iv_size: usize, crypt: Crypt, key: &[u8], nonce: &[u8], data: &[u8], aad: &[u8]) -> Result, aError>, FnError> where D: BlockCipher + BlockSizeUser + BlockEncrypt + BlockDecrypt + KeyInit { match tag_size { @@ -230,11 +210,11 @@ macro_rules! ccm_call_typed { Ok(ccm_crypt::(crypt, key, nonce, data, aad)) } ),* - other => Err(FunctionErrorKind::wrong_unnamed_argument("iv must be between 7 and 13", other.to_string().as_str())) + other => Err(FnError::wrong_unnamed_argument("iv must be between 7 and 13", other.to_string().as_str())) } } ),* - other => Err(FunctionErrorKind::wrong_unnamed_argument("tag_size must be 4, 6, 8, 10, 12, 14 or 16", other.to_string().as_str())) + other => Err(FnError::wrong_unnamed_argument("tag_size must be 4, 6, 8, 10, 12, 14 or 16", other.to_string().as_str())) } } } diff --git a/rust/src/nasl/builtin/cryptographic/aes_cmac.rs b/rust/src/nasl/builtin/cryptographic/aes_cmac.rs index 927719248..4e80eb718 100644 --- a/rust/src/nasl/builtin/cryptographic/aes_cmac.rs +++ b/rust/src/nasl/builtin/cryptographic/aes_cmac.rs @@ -3,26 +3,25 @@ // SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception use crate::nasl::syntax::NaslValue; -use crate::nasl::utils::error::GeneralErrorType; -use crate::nasl::utils::{Context, FunctionErrorKind, Register}; +use crate::nasl::utils::{Context, FnError, Register}; use aes::Aes128; use cmac::{Cmac, Mac}; use crate::function_set; -use super::{get_data, get_key}; +use super::{get_data, get_key, CryptographicError}; /// NASL function to calculate CMAC wit AES128. /// /// This function expects 2 named arguments key and data either in a string or data type. /// It is important to notice, that internally the CMAC algorithm is used and not, as the name /// suggests, CBC-MAC. -fn aes_cmac(register: &Register, _: &Context) -> Result { +fn aes_cmac(register: &Register, _: &Context) -> Result { let key = get_key(register)?; let data = get_data(register)?; - let mut mac = Cmac::::new_from_slice(key) - .map_err(|e| GeneralErrorType::UnexpectedData(format!("CMAC: {}", e)))?; + let mut mac = + Cmac::::new_from_slice(key).map_err(CryptographicError::AesCmacInvalidLength)?; mac.update(data); Ok(mac.finalize().into_bytes().to_vec().into()) diff --git a/rust/src/nasl/builtin/cryptographic/aes_ctr.rs b/rust/src/nasl/builtin/cryptographic/aes_ctr.rs index 7d46c7495..7e6d9ec08 100644 --- a/rust/src/nasl/builtin/cryptographic/aes_ctr.rs +++ b/rust/src/nasl/builtin/cryptographic/aes_ctr.rs @@ -15,7 +15,7 @@ use crate::nasl::prelude::*; use super::{get_data, get_iv, get_key, get_len, Crypt}; -fn ctr(register: &Register, crypt: Crypt) -> Result +fn ctr(register: &Register, crypt: Crypt) -> Result where D: BlockSizeUser + aes::cipher::KeyInit @@ -56,7 +56,7 @@ where /// Currently the data is filled with zeroes. Therefore the length of the encrypted data must be /// known for decryption. If no length is given, the last block is decrypted as a whole. /// - The iv must have a length of 16 bytes. It is used as the initial counter. -fn aes128_ctr_encrypt(register: &Register, _: &Context) -> Result { +fn aes128_ctr_encrypt(register: &Register, _: &Context) -> Result { ctr::(register, Crypt::Encrypt) } @@ -68,7 +68,7 @@ fn aes128_ctr_encrypt(register: &Register, _: &Context) -> Result Result { +fn aes128_ctr_decrypt(register: &Register, _: &Context) -> Result { ctr::(register, Crypt::Decrypt) } @@ -79,7 +79,7 @@ fn aes128_ctr_decrypt(register: &Register, _: &Context) -> Result Result { +fn aes192_ctr_encrypt(register: &Register, _: &Context) -> Result { ctr::(register, Crypt::Encrypt) } @@ -91,7 +91,7 @@ fn aes192_ctr_encrypt(register: &Register, _: &Context) -> Result Result { +fn aes192_ctr_decrypt(register: &Register, _: &Context) -> Result { ctr::(register, Crypt::Decrypt) } @@ -102,7 +102,7 @@ fn aes192_ctr_decrypt(register: &Register, _: &Context) -> Result Result { +fn aes256_ctr_encrypt(register: &Register, _: &Context) -> Result { ctr::(register, Crypt::Encrypt) } @@ -114,7 +114,7 @@ fn aes256_ctr_encrypt(register: &Register, _: &Context) -> Result Result { +fn aes256_ctr_decrypt(register: &Register, _: &Context) -> Result { ctr::(register, Crypt::Decrypt) } diff --git a/rust/src/nasl/builtin/cryptographic/aes_gcm.rs b/rust/src/nasl/builtin/cryptographic/aes_gcm.rs index 71868f4a5..55bab0129 100644 --- a/rust/src/nasl/builtin/cryptographic/aes_gcm.rs +++ b/rust/src/nasl/builtin/cryptographic/aes_gcm.rs @@ -2,12 +2,8 @@ // // SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception -// FunctionErrorKind::GeneralError -use crate::nasl::syntax::NaslValue; -use crate::{ - function_set, - nasl::utils::{Context, FunctionErrorKind, Register}, -}; +// FnError::GeneralError +use crate::nasl::prelude::*; use aes::{ cipher::{BlockCipher, BlockDecrypt, BlockEncrypt, BlockSizeUser, KeyInit}, Aes128, Aes192, Aes256, @@ -18,9 +14,9 @@ use aes_gcm::{ }; use digest::typenum::{U12, U16}; -use super::{get_aad, get_data, get_iv, get_key, get_len, Crypt}; +use super::{get_aad, get_data, get_iv, get_key, get_len, Crypt, CryptographicError}; -fn gcm(register: &Register, crypt: Crypt, auth: bool) -> Result +fn gcm(register: &Register, crypt: Crypt, auth: bool) -> Result where D: BlockSizeUser + aes::cipher::KeyInit @@ -67,7 +63,7 @@ where }, Crypt::Encrypt => Ok(x.into()), }, - Err(_) => Err(FunctionErrorKind::Authentication), + Err(_) => Err(CryptographicError::InsufficientBufferSize.into()), } } @@ -80,7 +76,7 @@ where /// - The iv must have a length of 16 bytes. It is used as the initial counter. /// - The result contains the ciphertext and the calculated tag in a single data type. /// - The tag has a size of 16 Bytes. -fn aes128_gcm_encrypt(register: &Register, _: &Context) -> Result { +fn aes128_gcm_encrypt(register: &Register, _: &Context) -> Result { gcm::(register, Crypt::Encrypt, false) } @@ -93,10 +89,7 @@ fn aes128_gcm_encrypt(register: &Register, _: &Context) -> Result Result { +fn aes128_gcm_encrypt_auth(register: &Register, _: &Context) -> Result { gcm::(register, Crypt::Encrypt, true) } @@ -109,7 +102,7 @@ fn aes128_gcm_encrypt_auth( /// known for decryption. If no length is given, the last block is decrypted as a whole. /// - The iv must have a length of 16 bytes. It is used as the initial counter. /// - The tag is needed as a postfix in the given data in order to decrypt successfully. -fn aes128_gcm_decrypt(register: &Register, _: &Context) -> Result { +fn aes128_gcm_decrypt(register: &Register, _: &Context) -> Result { gcm::(register, Crypt::Decrypt, false) } @@ -122,10 +115,7 @@ fn aes128_gcm_decrypt(register: &Register, _: &Context) -> Result Result { +fn aes128_gcm_decrypt_auth(register: &Register, _: &Context) -> Result { gcm::(register, Crypt::Decrypt, true) } @@ -138,7 +128,7 @@ fn aes128_gcm_decrypt_auth( /// - The iv must have a length of 16 bytes. It is used as the initial counter. /// - The result contains the ciphertext and the calculated tag in a single data type. /// - The tag has a size of 16 Bytes. -fn aes192_gcm_encrypt(register: &Register, _: &Context) -> Result { +fn aes192_gcm_encrypt(register: &Register, _: &Context) -> Result { gcm::(register, Crypt::Encrypt, false) } @@ -151,10 +141,7 @@ fn aes192_gcm_encrypt(register: &Register, _: &Context) -> Result Result { +fn aes192_gcm_encrypt_auth(register: &Register, _: &Context) -> Result { gcm::(register, Crypt::Encrypt, true) } @@ -167,7 +154,7 @@ fn aes192_gcm_encrypt_auth( /// known for decryption. If no length is given, the last block is decrypted as a whole. /// - The iv must have a length of 16 bytes. It is used as the initial counter. /// - The tag is needed as a postfix in the given data in order to decrypt successfully. -fn aes192_gcm_decrypt(register: &Register, _: &Context) -> Result { +fn aes192_gcm_decrypt(register: &Register, _: &Context) -> Result { gcm::(register, Crypt::Decrypt, false) } @@ -180,10 +167,7 @@ fn aes192_gcm_decrypt(register: &Register, _: &Context) -> Result Result { +fn aes192_gcm_decrypt_auth(register: &Register, _: &Context) -> Result { gcm::(register, Crypt::Decrypt, true) } @@ -196,7 +180,7 @@ fn aes192_gcm_decrypt_auth( /// - The iv must have a length of 16 bytes. It is used as the initial counter. /// - The result contains the ciphertext and the calculated tag in a single data type. /// - The tag has a size of 16 Bytes. -fn aes256_gcm_encrypt(register: &Register, _: &Context) -> Result { +fn aes256_gcm_encrypt(register: &Register, _: &Context) -> Result { gcm::(register, Crypt::Encrypt, false) } @@ -209,10 +193,7 @@ fn aes256_gcm_encrypt(register: &Register, _: &Context) -> Result Result { +fn aes256_gcm_encrypt_auth(register: &Register, _: &Context) -> Result { gcm::(register, Crypt::Encrypt, true) } @@ -225,7 +206,7 @@ fn aes256_gcm_encrypt_auth( /// known for decryption. If no length is given, the last block is decrypted as a whole. /// - The iv must have a length of 16 bytes. It is used as the initial counter. /// - The tag is needed as a postfix in the given data in order to decrypt successfully. -fn aes256_gcm_decrypt(register: &Register, _: &Context) -> Result { +fn aes256_gcm_decrypt(register: &Register, _: &Context) -> Result { gcm::(register, Crypt::Decrypt, false) } @@ -238,10 +219,7 @@ fn aes256_gcm_decrypt(register: &Register, _: &Context) -> Result Result { +fn aes256_gcm_decrypt_auth(register: &Register, _: &Context) -> Result { gcm::(register, Crypt::Decrypt, true) } diff --git a/rust/src/nasl/builtin/cryptographic/aes_gmac.rs b/rust/src/nasl/builtin/cryptographic/aes_gmac.rs index 15cb71d3c..195738c9e 100644 --- a/rust/src/nasl/builtin/cryptographic/aes_gmac.rs +++ b/rust/src/nasl/builtin/cryptographic/aes_gmac.rs @@ -3,16 +3,15 @@ // SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception use crate::function_set; +#[cfg(feature = "nasl-c-lib")] +use crate::nasl::prelude::*; /// NASL function to calculate GMAC with AES128. /// /// This function expects 3 named arguments key, data and iv either in a string or data type. #[cfg(feature = "nasl-c-lib")] -fn aes_gmac( - register: &crate::nasl::utils::Register, - _: &crate::nasl::utils::Context, -) -> Result { - use super::{get_data, get_iv, get_key}; +fn aes_gmac(register: &Register, _: &Context) -> Result { + use super::{get_data, get_iv, get_key, CryptographicError}; use nasl_c_lib::cryptographic::mac::aes_gmac; let key = get_key(register)?; @@ -21,12 +20,7 @@ fn aes_gmac( match aes_gmac(data, key, iv) { Ok(val) => Ok(val.into()), - Err(code) => Err(crate::nasl::utils::FunctionErrorKind::GeneralError( - crate::nasl::utils::error::GeneralErrorType::UnexpectedData(format!( - "Error code {}", - code - )), - )), + Err(msg) => Err(CryptographicError::AesGmacError(msg.into()).into()), } } diff --git a/rust/src/nasl/builtin/cryptographic/bf_cbc.rs b/rust/src/nasl/builtin/cryptographic/bf_cbc.rs index 8aeeffd38..1f24a09a7 100644 --- a/rust/src/nasl/builtin/cryptographic/bf_cbc.rs +++ b/rust/src/nasl/builtin/cryptographic/bf_cbc.rs @@ -12,15 +12,12 @@ use blowfish::{ }; use cbc::{Decryptor, Encryptor}; -use crate::function_set; -use crate::nasl::syntax::NaslValue; -use crate::nasl::utils::error::FunctionErrorKind; -use crate::nasl::utils::{Context, Register}; +use crate::nasl::prelude::*; use super::{get_data, get_iv, get_key, get_len, Crypt}; /// Base function for en- and decrypting Cipher Block Chaining (CBC) mode -fn cbc(register: &Register, crypt: Crypt) -> Result +fn cbc(register: &Register, crypt: Crypt) -> Result where D: BlockCipher + BlockEncrypt + BlockDecrypt + KeyInit, { @@ -35,7 +32,7 @@ where let res = Encryptor::::new_from_slices(key, iv); match res { Ok(encryptor) => Ok(encryptor.encrypt_padded_vec_mut::(data).into()), - Err(e) => Err(FunctionErrorKind::WrongArgument(e.to_string())), + Err(e) => Err(ArgumentError::WrongArgument(e.to_string()).into()), } } Crypt::Decrypt => { @@ -47,20 +44,21 @@ where // len should not be more than the length of the data if len > data.len() { - return Err(FunctionErrorKind::wrong_argument( + return Err(ArgumentError::wrong_argument( "len", format!("<={:?}", data.len()).as_str(), len.to_string().as_str(), - )); + ) + .into()); } let res = Decryptor::::new_from_slices(key, iv); match res { Ok(decryptor) => Ok(decryptor .decrypt_padded_vec_mut::(data) - .map_err(|e| FunctionErrorKind::WrongArgument(e.to_string()))?[..len] + .map_err(|e| ArgumentError::WrongArgument(e.to_string()))?[..len] .to_vec() .into()), - Err(e) => Err(FunctionErrorKind::WrongArgument(e.to_string())), + Err(e) => Err(ArgumentError::WrongArgument(e.to_string()).into()), } } } @@ -76,7 +74,7 @@ where /// The return value is an array a with a[0] being the encrypted data and /// a[1] the new initialization vector to use for the next part of the /// data. -fn bf_cbc_encrypt(register: &Register, _: &Context) -> Result { +fn bf_cbc_encrypt(register: &Register, _: &Context) -> Result { cbc::(register, Crypt::Encrypt) } @@ -90,7 +88,7 @@ fn bf_cbc_encrypt(register: &Register, _: &Context) -> Result Result { +fn bf_cbc_decrypt(register: &Register, _: &Context) -> Result { cbc::(register, Crypt::Decrypt) } diff --git a/rust/src/nasl/builtin/cryptographic/des.rs b/rust/src/nasl/builtin/cryptographic/des.rs index 23ce75016..ddd8b2c37 100644 --- a/rust/src/nasl/builtin/cryptographic/des.rs +++ b/rust/src/nasl/builtin/cryptographic/des.rs @@ -2,45 +2,32 @@ // // SPDX-License-Identifier: GPL-2.0-or-later -use crate::{ - function_set, - nasl::utils::{Context, FunctionErrorKind, Register}, -}; +use crate::nasl::prelude::*; use aes::cipher::BlockEncrypt; use ccm::KeyInit; use des::cipher::generic_array::GenericArray; -fn encrypt_des( - register: &Register, - _: &Context, -) -> Result { +fn encrypt_des(register: &Register, _: &Context) -> Result { let positional = register.positional(); if positional.len() != 2 { - return Err(FunctionErrorKind::MissingPositionalArguments { + return Err(ArgumentError::MissingPositionals { expected: 2, got: positional.len(), - }); + } + .into()); } let key = match &positional[1] { - crate::nasl::syntax::NaslValue::Data(x) => x, - _ => { - return Err(FunctionErrorKind::WrongArgument( - "expected Data.".to_string(), - )) - } + NaslValue::Data(x) => x, + _ => return Err(ArgumentError::WrongArgument("expected Data.".to_string()).into()), }; if key.len() != 8 { - return Err(FunctionErrorKind::WrongArgument( - "16, 32 or 48 bytes length key".to_string(), - )); + return Err( + ArgumentError::WrongArgument("16, 32 or 48 bytes length key".to_string()).into(), + ); } let mut data = GenericArray::clone_from_slice(match &positional[0] { - crate::nasl::syntax::NaslValue::Data(x) => x, - _ => { - return Err(FunctionErrorKind::WrongArgument( - "expected Data.".to_string(), - )) - } + NaslValue::Data(x) => x, + _ => return Err(ArgumentError::WrongArgument("expected Data.".to_string()).into()), }); let des_cipher = des::Des::new(&GenericArray::clone_from_slice(key)); des_cipher.encrypt_block(&mut data); diff --git a/rust/src/nasl/builtin/cryptographic/hash.rs b/rust/src/nasl/builtin/cryptographic/hash.rs index 5289e0033..be4ef0aa5 100644 --- a/rust/src/nasl/builtin/cryptographic/hash.rs +++ b/rust/src/nasl/builtin/cryptographic/hash.rs @@ -2,8 +2,6 @@ // // SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception -use crate::function_set; -use crate::nasl::utils::error::FunctionErrorKind; use digest::Digest; use md2::Md2; use md4::Md4; @@ -12,63 +10,63 @@ use ripemd::Ripemd160; use sha1::Sha1; use sha2::{Sha256, Sha512}; -use crate::nasl::syntax::NaslValue; -use crate::nasl::utils::{Context, Register}; +use crate::nasl::prelude::*; +use crate::nasl::utils::function::StringOrData; -fn nasl_hash(register: &Register) -> Result +fn nasl_hash(data: Option) -> Result where D::OutputSize: std::ops::Add, ::Output: digest::generic_array::ArrayLength, { - let positional = register.positional(); - if positional.is_empty() { - return Ok(NaslValue::Null); - }; - let data = match &positional[0] { - NaslValue::String(x) => x.as_bytes(), - NaslValue::Data(x) => x, - NaslValue::Null => return Ok(NaslValue::Null), - x => return Err(("data", "string", x).into()), - }; - - let mut hash = D::new(); - hash.update(data); - Ok(NaslValue::Data(hash.finalize().as_slice().to_vec())) + if let Some(data) = data { + let mut hash = D::new(); + hash.update(data.0.as_bytes()); + Ok(NaslValue::Data(hash.finalize().as_slice().to_vec())) + } else { + Ok(NaslValue::Null) + } } /// NASL function to get MD2 hash -pub fn hash_md2(register: &Register, _: &Context) -> Result { - nasl_hash::(register) +#[nasl_function] +pub fn hash_md2(data: Option) -> Result { + nasl_hash::(data) } /// NASL function to get MD4 hash -pub fn hash_md4(register: &Register, _: &Context) -> Result { - nasl_hash::(register) +#[nasl_function] +pub fn hash_md4(data: Option) -> Result { + nasl_hash::(data) } /// NASL function to get MD5 hash -pub fn hash_md5(register: &Register, _: &Context) -> Result { - nasl_hash::(register) +#[nasl_function] +pub fn hash_md5(data: Option) -> Result { + nasl_hash::(data) } /// NASL function to get SHA1 hash -pub fn hash_sha1(register: &Register, _: &Context) -> Result { - nasl_hash::(register) +#[nasl_function] +pub fn hash_sha1(data: Option) -> Result { + nasl_hash::(data) } /// NASL function to get SHA256 hash -pub fn hash_sha256(register: &Register, _: &Context) -> Result { - nasl_hash::(register) +#[nasl_function] +pub fn hash_sha256(data: Option) -> Result { + nasl_hash::(data) } /// NASL function to get SHA512 hash -pub fn hash_sha512(register: &Register, _: &Context) -> Result { - nasl_hash::(register) +#[nasl_function] +pub fn hash_sha512(data: Option) -> Result { + nasl_hash::(data) } /// NASL function to get RIPemd160 hash -pub fn hash_ripemd160(register: &Register, _: &Context) -> Result { - nasl_hash::(register) +#[nasl_function] +pub fn hash_ripemd160(data: Option) -> Result { + nasl_hash::(data) } pub struct Hash; diff --git a/rust/src/nasl/builtin/cryptographic/hmac.rs b/rust/src/nasl/builtin/cryptographic/hmac.rs index 4aa78f744..556cc5899 100644 --- a/rust/src/nasl/builtin/cryptographic/hmac.rs +++ b/rust/src/nasl/builtin/cryptographic/hmac.rs @@ -19,7 +19,7 @@ use sha2::{Sha256, Sha384, Sha512}; use crate::nasl::prelude::*; -fn hmac(register: &Register) -> Result +fn hmac(key: &str, data: &str) -> Result where D: CoreProxy, D::Core: HashMarker @@ -31,20 +31,10 @@ where ::BlockSize: IsLess, Le<::BlockSize, U256>: NonZero, { - let key = match register.named("key") { - Some(ContextType::Value(NaslValue::String(x))) => x, - Some(ContextType::Value(NaslValue::Null)) => return Ok(NaslValue::Null), - x => return Err(("key", "string", x).into()), - }; - let data = match register.named("data") { - Some(ContextType::Value(NaslValue::String(x))) => x, - Some(ContextType::Value(NaslValue::Null)) => return Ok(NaslValue::Null), - x => return Err(("data", "string", x).into()), - }; let mut hmac = match Hmac::::new_from_slice(key.as_bytes()) { Ok(x) => x, Err(InvalidLength) => { - return Err(FunctionErrorKind::wrong_unnamed_argument( + return Err(FnError::wrong_unnamed_argument( "valid size key", "invalid size key", )) @@ -57,38 +47,45 @@ where } /// NASL function to get HMAC MD2 string -pub fn hmac_md2(register: &Register, _: &Context) -> Result { - hmac::(register) +#[nasl_function(named(key, data))] +pub fn hmac_md2(key: &str, data: &str) -> Result { + hmac::(key, data) } /// NASL function to get HMAC MD5 string -pub fn hmac_md5(register: &Register, _: &Context) -> Result { - hmac::(register) +#[nasl_function(named(key, data))] +pub fn hmac_md5(key: &str, data: &str) -> Result { + hmac::(key, data) } /// NASL function to get HMAC RIPEMD160 string -pub fn hmac_ripemd160(register: &Register, _: &Context) -> Result { - hmac::(register) +#[nasl_function(named(key, data))] +pub fn hmac_ripemd160(key: &str, data: &str) -> Result { + hmac::(key, data) } /// NASL function to get HMAC SHA1 string -pub fn hmac_sha1(register: &Register, _: &Context) -> Result { - hmac::(register) +#[nasl_function(named(key, data))] +pub fn hmac_sha1(key: &str, data: &str) -> Result { + hmac::(key, data) } /// NASL function to get HMAC SHA256 string -pub fn hmac_sha256(register: &Register, _: &Context) -> Result { - hmac::(register) +#[nasl_function(named(key, data))] +pub fn hmac_sha256(key: &str, data: &str) -> Result { + hmac::(key, data) } /// NASL function to get HMAC SHA384 string -pub fn hmac_sha384(register: &Register, _: &Context) -> Result { - hmac::(register) +#[nasl_function(named(key, data))] +pub fn hmac_sha384(key: &str, data: &str) -> Result { + hmac::(key, data) } /// NASL function to get HMAC SHA512 string -pub fn hmac_sha512(register: &Register, _: &Context) -> Result { - hmac::(register) +#[nasl_function(named(key, data))] +pub fn hmac_sha512(key: &str, data: &str) -> Result { + hmac::(key, data) } pub struct HmacFns; diff --git a/rust/src/nasl/builtin/cryptographic/mod.rs b/rust/src/nasl/builtin/cryptographic/mod.rs index 06cfad2aa..1702331aa 100644 --- a/rust/src/nasl/builtin/cryptographic/mod.rs +++ b/rust/src/nasl/builtin/cryptographic/mod.rs @@ -2,10 +2,11 @@ // // SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception +use thiserror::Error; + // use crate::nasl::utils::combine_function_sets; -use crate::nasl::utils::error::FunctionErrorKind; +use crate::nasl::prelude::*; -use crate::nasl::syntax::NaslValue; use crate::nasl::utils::{ContextType, IntoFunctionSet, Register, StoredFunctionSet}; pub mod aes_cbc; @@ -24,6 +25,22 @@ pub mod rsa; #[cfg(test)] mod tests; +#[derive(Debug, Error)] +pub enum CryptographicError { + #[error("Error in AesGcm: insufficient buffer size.")] + InsufficientBufferSize, + #[error("Error in AesCcm: unable to encrypt.")] + AesCcmUnableToEncrypt, + #[error("Error in AesGmac: {0}.")] + AesGmacError(String), + #[error("Invalid length of key in AesCmac {0}.")] + AesCmacInvalidLength(digest::InvalidLength), + #[error("Error in RSA: {0}.")] + Rsa(String), + #[error("Error in RC4: {0}.")] + Rc4(String), +} + enum Crypt { Encrypt, Decrypt, @@ -36,16 +53,16 @@ enum Crypt { fn get_required_named_data<'a>( register: &'a Register, key: &'a str, -) -> Result<&'a [u8], FunctionErrorKind> { +) -> Result<&'a [u8], ArgumentError> { match register.named(key) { Some(ContextType::Value(NaslValue::Data(x))) => Ok(x.as_slice()), Some(ContextType::Value(NaslValue::String(x))) => Ok(x.as_bytes()), - Some(x) => Err(FunctionErrorKind::wrong_argument( + Some(x) => Err(ArgumentError::wrong_argument( key, "a String or Data Value", format!("{:?}", x).as_str(), )), - _ => Err(FunctionErrorKind::missing_argument(key)), + _ => Err(ArgumentError::MissingNamed(vec![key.into()])), } } @@ -53,13 +70,10 @@ fn get_required_named_data<'a>( /// In case the argument is required, the returned value is either an Error or the Option is always /// set to Some value. If it is false, no error will be returned but the Option can be either Some /// or None. -fn get_optional_named_number( - register: &Register, - key: &str, -) -> Result, FunctionErrorKind> { +fn get_optional_named_number(register: &Register, key: &str) -> Result, ArgumentError> { match register.named(key) { Some(ContextType::Value(NaslValue::Number(x))) => Ok(Some(*x)), - Some(x) => Err(FunctionErrorKind::wrong_argument( + Some(x) => Err(ArgumentError::wrong_argument( key, "a Number Value", format!("{:?}", x).as_str(), @@ -69,33 +83,33 @@ fn get_optional_named_number( } /// Get the required key argument or error. -fn get_key(register: &Register) -> Result<&[u8], FunctionErrorKind> { +fn get_key(register: &Register) -> Result<&[u8], ArgumentError> { get_required_named_data(register, "key") } /// Get the required data argument or error. -fn get_data(register: &Register) -> Result<&[u8], FunctionErrorKind> { +fn get_data(register: &Register) -> Result<&[u8], ArgumentError> { get_required_named_data(register, "data") } /// Get the required iv argument or error. -fn get_iv(register: &Register) -> Result<&[u8], FunctionErrorKind> { +fn get_iv(register: &Register) -> Result<&[u8], ArgumentError> { get_required_named_data(register, "iv") } /// Get the required iv argument or error. -fn get_aad(register: &Register) -> Result<&[u8], FunctionErrorKind> { +fn get_aad(register: &Register) -> Result<&[u8], ArgumentError> { get_required_named_data(register, "aad") } /// Get the optional len argument with proper error handling. -fn get_len(register: &Register) -> Result, FunctionErrorKind> { +fn get_len(register: &Register) -> Result, ArgumentError> { let buf = get_optional_named_number(register, "len")?; match buf { None => Ok(None), Some(x) => match x.try_into() { Ok(y) => Ok(Some(y)), - Err(_) => Err(FunctionErrorKind::WrongArgument(format!( + Err(_) => Err(ArgumentError::WrongArgument(format!( "System only supports numbers between {:?} and {:?} but was {:?}", usize::MIN, usize::MAX, diff --git a/rust/src/nasl/builtin/cryptographic/rc4.rs b/rust/src/nasl/builtin/cryptographic/rc4.rs index 03dcce2b8..224e41027 100644 --- a/rust/src/nasl/builtin/cryptographic/rc4.rs +++ b/rust/src/nasl/builtin/cryptographic/rc4.rs @@ -8,10 +8,9 @@ use std::sync::{Arc, Mutex, MutexGuard}; use crate::nasl::prelude::*; use crate::nasl::syntax::NaslValue; -use crate::nasl::utils::error::FunctionErrorKind; use crate::nasl::utils::{Context, Register}; -use super::{get_data, get_key}; +use super::{get_data, get_key, CryptographicError}; /// Structure to hold a Cipher Handler pub struct CipherHandler { @@ -23,9 +22,9 @@ pub struct CipherHandler { fn lock_handlers( handlers: &Arc>>, -) -> Result>, FunctionErrorKind> { +) -> Result>, FnError> { // we actually need to panic as a lock error is fatal - // alternatively we need to add a poison error on FunctionErrorKind + // alternatively we need to add a poison error on FnError Ok(Arc::as_ref(handlers).lock().unwrap()) } @@ -63,15 +62,10 @@ impl CipherHandlers { &self, register: &Register, _: &Context, - ) -> Result { + ) -> Result { let hd = match register.named("hd") { Some(ContextType::Value(NaslValue::Number(x))) => *x as i32, - _ => { - return Err(FunctionErrorKind::Diagnostic( - "Handler ID not found".to_string(), - Some(NaslValue::Null), - )) - } + _ => return Err(CryptographicError::Rc4("Handler ID not found".to_string()).into()), }; let mut handlers = lock_handlers(&self.cipher_handlers)?; @@ -80,10 +74,7 @@ impl CipherHandlers { handlers.remove(i); Ok(NaslValue::Number(0)) } - _ => Err(FunctionErrorKind::Diagnostic( - format!("Handler ID {} not found", hd), - Some(NaslValue::Null), - )), + _ => Err(CryptographicError::Rc4(format!("Handler ID {} not found", hd)).into()), } } @@ -93,21 +84,12 @@ impl CipherHandlers { /// -key: the key used for encryption /// /// Returns the id of the encrypted data cipher handler on success. - pub fn open_rc4_cipher( - &self, - register: &Register, - _: &Context, - ) -> Result { + pub fn open_rc4_cipher(&self, register: &Register, _: &Context) -> Result { // Get Arguments let key = match get_key(register) { Ok(k) if !k.is_empty() => k.to_vec(), - _ => { - return Err(FunctionErrorKind::Diagnostic( - "Missing Key argument".to_string(), - Some(NaslValue::Null), - )) - } + _ => return Err(CryptographicError::Rc4("Missing Key argument".to_string()).into()), }; let rc_handler = Rc4Key::build_handler_from_key(key.to_vec())?; @@ -131,19 +113,10 @@ impl CipherHandlers { /// -hd: the handler index. (mandatory if not key and iv is given) /// -iv: string Initialization vector (mandatory if no handler is given). /// -key: string key (mandatory if no handler is given). - pub fn rc4_encrypt( - &self, - register: &Register, - _: &Context, - ) -> Result { + pub fn rc4_encrypt(&self, register: &Register, _: &Context) -> Result { let data = match get_data(register) { Ok(d) if !d.is_empty() => d.to_vec(), - _ => { - return Err(FunctionErrorKind::Diagnostic( - "Missing data argument".to_string(), - Some(NaslValue::Null), - )) - } + _ => return Err(CryptographicError::Rc4("Missing data argument".to_string()).into()), }; let hd = match register.named("hd") { @@ -162,12 +135,7 @@ impl CipherHandlers { let key = match get_key(register) { Ok(k) if !k.is_empty() => k.to_vec(), - _ => { - return Err(FunctionErrorKind::Diagnostic( - "Missing Key argument".to_string(), - Some(NaslValue::Null), - )) - } + _ => return Err(CryptographicError::Rc4("Missing Key argument".to_string()).into()), }; let mut rc_handler = Rc4Key::build_handler_from_key(key.to_vec())?; @@ -185,10 +153,10 @@ macro_rules! build_rc4key_enum { } impl Rc4Key { - fn build_handler_from_key(bl: Vec) -> Result { + fn build_handler_from_key(bl: Vec) -> Result { match bl.len() { $($l => Ok(Self::$i(Rc4::new_from_slice(bl.as_slice()).unwrap())),)* - _ => {return Err(FunctionErrorKind::Diagnostic("RC4 Key size not supported".into(), Some(NaslValue::Null)))} + _ => {return Err(CryptographicError::Rc4("RC4 Key size not supported".into()).into())} } } diff --git a/rust/src/nasl/builtin/cryptographic/rsa.rs b/rust/src/nasl/builtin/cryptographic/rsa.rs index 102a17961..2ce383d8a 100644 --- a/rust/src/nasl/builtin/cryptographic/rsa.rs +++ b/rust/src/nasl/builtin/cryptographic/rsa.rs @@ -3,8 +3,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later use crate::function_set; -use crate::nasl::FunctionErrorKind; -use crate::nasl::NaslValue; +use crate::nasl::prelude::*; use ccm::aead::OsRng; use nasl_function_proc_macro::nasl_function; use rsa::pkcs8::DecodePrivateKey; @@ -12,28 +11,30 @@ use rsa::signature::digest::Digest; use rsa::{BigUint, Pkcs1v15Encrypt, Pkcs1v15Sign, RsaPrivateKey, RsaPublicKey}; use sha1::Sha1; +use super::CryptographicError; + #[nasl_function(named(data, n, e, pad))] fn rsa_public_encrypt( data: &[u8], n: &[u8], e: &[u8], pad: Option, -) -> Result { +) -> Result { let pad = pad.unwrap_or_default(); let mut rng = rand::thread_rng(); let pub_key = RsaPublicKey::new( rsa::BigUint::from_bytes_be(n), rsa::BigUint::from_bytes_be(e), ) - .map_err(|e| FunctionErrorKind::Diagnostic(e.to_string(), None))?; + .map_err(|e| CryptographicError::Rsa(e.to_string()))?; let biguint_data = BigUint::from_bytes_be(data); let enc_data = if pad { pub_key .encrypt(&mut rng, Pkcs1v15Encrypt, data) - .map_err(|e| FunctionErrorKind::Diagnostic(e.to_string(), None))? + .map_err(|e| CryptographicError::Rsa(e.to_string()))? } else { rsa::hazmat::rsa_encrypt(&pub_key, &biguint_data) - .map_err(|e| FunctionErrorKind::Diagnostic(e.to_string(), None))? + .map_err(|e| CryptographicError::Rsa(e.to_string()))? .to_bytes_be() }; Ok(enc_data.to_vec().into()) @@ -46,7 +47,7 @@ fn rsa_private_decrypt( e: &[u8], d: &[u8], pad: Option, -) -> Result { +) -> Result { let pad = pad.unwrap_or_default(); let priv_key = match RsaPrivateKey::from_components( rsa::BigUint::from_bytes_be(n), @@ -55,30 +56,32 @@ fn rsa_private_decrypt( vec![], ) { Ok(val) => Ok(val), - Err(code) => Err(crate::nasl::FunctionErrorKind::Diagnostic( - format!("Error code {}", code), - Some(NaslValue::Array(vec![ - NaslValue::Data(n.to_vec()), - NaslValue::Data(e.to_vec()), - NaslValue::Data(d.to_vec()), - ])), - )), + Err(code) => Err( + FnError::from(CryptographicError::Rsa(format!("Error code {}", code))).with( + ReturnValue(NaslValue::Array(vec![ + NaslValue::Data(n.to_vec()), + NaslValue::Data(e.to_vec()), + NaslValue::Data(d.to_vec()), + ])), + ), + ), } - .map_err(|e| FunctionErrorKind::Diagnostic(e.to_string(), None))?; + .map_err(|e| CryptographicError::Rsa(e.to_string()))?; let mut rng = OsRng; let biguint_data = BigUint::from_bytes_be(data); let dec_data = if pad { match priv_key.decrypt(Pkcs1v15Encrypt, data) { Ok(val) => Ok(val), - Err(code) => Err(crate::nasl::FunctionErrorKind::Diagnostic( - format!("Error code {}", code), - Some(NaslValue::Data(data.to_vec())), - )), + Err(code) => Err(FnError::from(CryptographicError::Rsa(format!( + "Error code {}", + code + ))) + .with(ReturnValue(NaslValue::Data(data.to_vec())))), } - .map_err(|e| FunctionErrorKind::Diagnostic(e.to_string(), None))? + .map_err(|e| CryptographicError::Rsa(e.to_string()))? } else { rsa::hazmat::rsa_decrypt_and_check(&priv_key, Some(&mut rng), &biguint_data) - .map_err(|e| FunctionErrorKind::Diagnostic(e.to_string(), None))? + .map_err(|e| CryptographicError::Rsa(e.to_string()))? .to_bytes_be() }; @@ -86,39 +89,34 @@ fn rsa_private_decrypt( } #[nasl_function(named(data, pem, passphrase))] -fn rsa_sign( - data: &[u8], - pem: &[u8], - passphrase: Option<&str>, -) -> Result { - let pem_str = - std::str::from_utf8(pem).map_err(|e| FunctionErrorKind::Diagnostic(e.to_string(), None))?; +fn rsa_sign(data: &[u8], pem: &[u8], passphrase: Option<&str>) -> Result { + let pem_str = std::str::from_utf8(pem).map_err(|e| CryptographicError::Rsa(e.to_string()))?; let rsa: RsaPrivateKey = if passphrase.unwrap_or_default() != "" { pkcs8::DecodePrivateKey::from_pkcs8_encrypted_pem(pem_str, passphrase.unwrap_or_default()) - .map_err(|e| FunctionErrorKind::Diagnostic(e.to_string(), None))? + .map_err(|e| CryptographicError::Rsa(e.to_string()))? } else { RsaPrivateKey::from_pkcs8_pem(pem_str) - .map_err(|e| FunctionErrorKind::Diagnostic(e.to_string(), None))? + .map_err(|e| CryptographicError::Rsa(e.to_string()))? }; let mut hasher = Sha1::new_with_prefix(data); hasher.update(data); let hashed_data = hasher.finalize(); let signature = rsa .sign(Pkcs1v15Sign::new_unprefixed(), &hashed_data) - .map_err(|e| FunctionErrorKind::Diagnostic(e.to_string(), None))?; + .map_err(|e| CryptographicError::Rsa(e.to_string()))?; Ok(signature.into()) } #[nasl_function(named(sign, n, e))] -fn rsa_public_decrypt(sign: &[u8], n: &[u8], e: &[u8]) -> Result { +fn rsa_public_decrypt(sign: &[u8], n: &[u8], e: &[u8]) -> Result { let e_b = rsa::BigUint::from_bytes_be(e); let n_b = rsa::BigUint::from_bytes_be(n); - let public_key = RsaPublicKey::new(n_b, e_b) - .map_err(|e| FunctionErrorKind::Diagnostic(e.to_string(), None))?; + let public_key = + RsaPublicKey::new(n_b, e_b).map_err(|e| CryptographicError::Rsa(e.to_string()))?; let mut rng = rand::thread_rng(); let enc_data = public_key .encrypt(&mut rng, Pkcs1v15Encrypt, sign) - .map_err(|e| FunctionErrorKind::Diagnostic(e.to_string(), None))?; + .map_err(|e| CryptographicError::Rsa(e.to_string()))?; Ok(enc_data.to_vec().into()) } diff --git a/rust/src/nasl/builtin/cryptographic/tests/aes_cbc.rs b/rust/src/nasl/builtin/cryptographic/tests/aes_cbc.rs index bd0829f8b..560bb0b28 100644 --- a/rust/src/nasl/builtin/cryptographic/tests/aes_cbc.rs +++ b/rust/src/nasl/builtin/cryptographic/tests/aes_cbc.rs @@ -68,5 +68,8 @@ fn padding() { "#, ); let results = t.results(); - assert_eq!(results[results.len() - 2], results[results.len() - 1]); + assert_eq!( + results[results.len() - 2].as_ref().unwrap(), + results[results.len() - 1].as_ref().unwrap() + ); } diff --git a/rust/src/nasl/builtin/cryptographic/tests/aes_gcm.rs b/rust/src/nasl/builtin/cryptographic/tests/aes_gcm.rs index 1708da468..5363ce454 100644 --- a/rust/src/nasl/builtin/cryptographic/tests/aes_gcm.rs +++ b/rust/src/nasl/builtin/cryptographic/tests/aes_gcm.rs @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception -// FunctionErrorKind::GeneralError +// FnError::GeneralError use super::helper::decode_hex; use crate::nasl::test_prelude::*; @@ -119,5 +119,8 @@ fn padding() { "#, ); let results = t.results(); - assert_eq!(results[results.len() - 2], results[results.len() - 1]); + assert_eq!( + results[results.len() - 2].as_ref().unwrap(), + results[results.len() - 1].as_ref().unwrap() + ); } diff --git a/rust/src/nasl/builtin/description/mod.rs b/rust/src/nasl/builtin/description/mod.rs index 91b4734bc..8aae68866 100644 --- a/rust/src/nasl/builtin/description/mod.rs +++ b/rust/src/nasl/builtin/description/mod.rs @@ -25,7 +25,7 @@ use crate::nasl::utils::get_named_parameter; ///} /// ```` /// The first parameter is the name of the function as well as the &str lookup key. -/// Afterwards a method that transform `&[&NaslValue]` to `Result` must be defined. +/// Afterwards a method that transform `&[&NaslValue]` to `Result` must be defined. /// /// Parameter are separated from the definition by a `=>`. /// @@ -59,13 +59,13 @@ macro_rules! make_storage_function { pub fn $name( registrat: &Register, ctxconfigs: &Context, - ) -> Result { + ) -> Result { let mut variables = vec![]; $( let positional = registrat.positional(); if $len > 0 && positional.len() != $len{ return Err( - FunctionErrorKind::MissingPositionalArguments { expected: $len, got: positional.len() } + ArgumentError::MissingPositionals { expected: $len, got: positional.len() }.into() ); } for p in positional { @@ -106,7 +106,7 @@ macro_rules! make_storage_function { }; } -type Transform = Result, FunctionErrorKind>; +type Transform = Result, FnError>; fn as_timeout_field(_: &ContextKey, arguments: &[&NaslValue]) -> Transform { Ok(vec![NVTField::Preference(NvtPreference { @@ -120,7 +120,10 @@ fn as_timeout_field(_: &ContextKey, arguments: &[&NaslValue]) -> Transform { fn as_category_field(_: &ContextKey, arguments: &[&NaslValue]) -> Transform { match arguments[0] { NaslValue::AttackCategory(cat) => Ok(vec![NVTField::Category(*cat)]), - a => Err(("AttackCategory", a).into()), + a => Err(FnError::wrong_unnamed_argument( + "AttackCategory", + &a.to_string(), + )), } } @@ -200,10 +203,7 @@ fn as_tag_field(_: &ContextKey, arguments: &[&NaslValue]) -> Transform { fn as_xref_field(_: &ContextKey, arguments: &[&NaslValue]) -> Transform { if arguments.len() != 2 { - return Err(FunctionErrorKind::MissingArguments(vec![ - "name".to_owned(), - "csv".to_owned(), - ])); + return Err(ArgumentError::MissingNamed(vec!["name".to_owned(), "csv".to_owned()]).into()); } Ok(vec![NVTField::Reference(vec![NvtRef { class: arguments[1].to_string(), @@ -213,10 +213,9 @@ fn as_xref_field(_: &ContextKey, arguments: &[&NaslValue]) -> Transform { fn as_preference(_: &ContextKey, arguments: &[&NaslValue]) -> Transform { if arguments.len() < 3 { - return Err(FunctionErrorKind::MissingArguments(vec![ - "type".to_owned(), - "value".to_owned(), - ])); + return Err( + ArgumentError::MissingNamed(vec!["type".to_owned(), "value".to_owned()]).into(), + ); } let name = arguments[0].to_string(); let class = arguments[1].to_string(); diff --git a/rust/src/nasl/builtin/error.rs b/rust/src/nasl/builtin/error.rs new file mode 100644 index 000000000..244540ebd --- /dev/null +++ b/rust/src/nasl/builtin/error.rs @@ -0,0 +1,86 @@ +use thiserror::Error; + +use crate::nasl::prelude::*; +use crate::nasl::utils::error::FnErrorKind; + +use super::cert::CertError; +use super::cryptographic::CryptographicError; +use super::host::HostError; +use super::http::HttpError; +use super::isotime::IsotimeError; +use super::knowledge_base::KBError; +use super::regex::RegexError; +use super::{misc::MiscError, network::socket::SocketError, ssh::SshError, string::StringError}; + +#[derive(Debug, Error)] +pub enum BuiltinError { + #[error("{0}")] + Ssh(SshError), + #[error("{0}")] + Http(HttpError), + #[error("{0}")] + String(StringError), + #[error("{0}")] + Misc(MiscError), + #[error("{0}")] + Socket(SocketError), + #[error("{0}")] + Cryptographic(CryptographicError), + #[error("{0}")] + Regex(RegexError), + #[error("{0}")] + Isotime(IsotimeError), + #[error("{0}")] + KB(KBError), + #[error("{0}")] + Host(HostError), + #[error("{0}")] + Cert(CertError), + #[cfg(feature = "nasl-builtin-raw-ip")] + #[error("{0}")] + RawIp(super::raw_ip::RawIpError), +} + +macro_rules! builtin_error_variant ( + ($err: path, $variant: ident) => { + impl From<$err> for BuiltinError { + fn from(value: $err) -> Self { + BuiltinError::$variant(value).into() + } + } + + impl From<$err> for FnError { + fn from(value: $err) -> Self { + FnErrorKind::Builtin(BuiltinError::$variant(value).into()).into() + } + } + + impl<'a> TryFrom<&'a FnError> for &'a $err { + type Error = (); + + fn try_from(value: &'a FnError) -> Result { + match &value.kind { + FnErrorKind::Builtin( + BuiltinError::$variant(e) + ) => Ok(e), + _ => Err(()), + } + } + } + } +); + +builtin_error_variant!(StringError, String); +builtin_error_variant!(MiscError, Misc); +builtin_error_variant!(SocketError, Socket); +builtin_error_variant!(CryptographicError, Cryptographic); +builtin_error_variant!(SshError, Ssh); +builtin_error_variant!(HttpError, Http); +builtin_error_variant!(IsotimeError, Isotime); +builtin_error_variant!(RegexError, Regex); +builtin_error_variant!(KBError, KB); +builtin_error_variant!(HostError, Host); +builtin_error_variant!(CertError, Cert); + +#[cfg(feature = "nasl-builtin-raw-ip")] +builtin_error_variant!(super::raw_ip::RawIpError, RawIp); diff --git a/rust/src/nasl/builtin/host/mod.rs b/rust/src/nasl/builtin/host/mod.rs index 60d73bcc5..a5d7fcce0 100644 --- a/rust/src/nasl/builtin/host/mod.rs +++ b/rust/src/nasl/builtin/host/mod.rs @@ -11,20 +11,27 @@ use std::{ }; use dns_lookup::lookup_addr; -use nasl_function_proc_macro::nasl_function; - -use crate::nasl::utils::{error::FunctionErrorKind, hosts::resolve}; -use crate::{function_set, nasl::FromNaslValue}; - -use crate::nasl::syntax::NaslValue; -use crate::nasl::utils::{Context, Register}; +use thiserror::Error; + +use crate::nasl::prelude::*; +use crate::nasl::utils::hosts::resolve; + +#[derive(Debug, Error)] +pub enum HostError { + #[error("Empty hostname.")] + EmptyHostname, + #[error("Empty address.")] + EmptyAddress, + #[error("Target is not a hostname.")] + TargetIsNotAHostname, +} struct Hostname(String); impl<'a> FromNaslValue<'a> for Hostname { - fn from_nasl_value(value: &'a NaslValue) -> Result { + 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.")) + Err(HostError::EmptyHostname.into()) } else { Ok(Self(str)) } @@ -33,7 +40,7 @@ impl<'a> FromNaslValue<'a> for Hostname { /// Get a list of found hostnames or a IP of the current target in case no hostnames were found yet. #[nasl_function] -fn get_host_names(context: &Context) -> Result { +fn get_host_names(context: &Context) -> Result { let hns = context.target_vhosts(); if !hns.is_empty() { let hns = hns @@ -48,7 +55,7 @@ fn get_host_names(context: &Context) -> Result { } /// Return the target's IP address as IpAddr. -pub fn get_host_ip(context: &Context) -> Result { +pub fn get_host_ip(context: &Context) -> Result { let default_ip = "127.0.0.1"; let r_sock_addr = match context.target() { x if !x.is_empty() => IpAddr::from_str(x), @@ -57,7 +64,7 @@ pub fn get_host_ip(context: &Context) -> Result { match r_sock_addr { Ok(x) => Ok(x), - Err(e) => Err(FunctionErrorKind::wrong_unnamed_argument( + Err(e) => Err(FnError::wrong_unnamed_argument( "IP address", e.to_string().as_str(), )), @@ -72,17 +79,14 @@ pub fn add_host_name( context: &Context, hostname: Hostname, source: Option<&str>, -) -> Result { +) -> Result { let source = source.filter(|x| !x.is_empty()).unwrap_or("NASL"); context.add_hostname(hostname.0, source.into()); Ok(NaslValue::Null) } /// Get the host name of the currently scanned target. If there is no host name available, the IP of the target is returned instead. -pub fn get_host_name( - _register: &Register, - context: &Context, -) -> Result { +pub fn get_host_name(_register: &Register, context: &Context) -> Result { let vh = context.target_vhosts(); let v = if !vh.is_empty() { vh.iter() @@ -125,10 +129,7 @@ pub fn get_host_name_source(context: &Context, hostname: Hostname) -> String { } /// Return the target's IP address or 127.0.0.1 if not set. -fn nasl_get_host_ip( - _register: &Register, - context: &Context, -) -> Result { +fn nasl_get_host_ip(_register: &Register, context: &Context) -> Result { let ip = get_host_ip(context)?; Ok(NaslValue::String(ip.to_string())) } @@ -144,7 +145,7 @@ fn resolve_host_name(hostname: Hostname) -> String { /// 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 { +fn resolve_hostname_to_multiple_ips(hostname: Hostname) -> Result { let ips = resolve(hostname.0)? .into_iter() .map(|x| NaslValue::String(x.to_string())) @@ -155,10 +156,10 @@ fn resolve_hostname_to_multiple_ips(hostname: Hostname) -> Result Result { +fn target_is_ipv6(context: &Context) -> Result { let target = match context.target().is_empty() { true => { - return Err(FunctionErrorKind::diagnostic_ret_null("Address is NULL!")); + return Err(HostError::EmptyAddress.into()); } false => context.target(), }; @@ -169,7 +170,7 @@ fn target_is_ipv6(context: &Context) -> Result { /// 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 #[nasl_function(named(cmp_hostname))] -fn same_host(h1: &str, h2: &str, cmp_hostname: Option) -> Result { +fn same_host(h1: &str, h2: &str, cmp_hostname: Option) -> Result { let h1 = resolve(h1.to_string())?; let h2 = resolve(h2.to_string())?; diff --git a/rust/src/nasl/builtin/http/error.rs b/rust/src/nasl/builtin/http/error.rs new file mode 100644 index 000000000..7e28c6d1d --- /dev/null +++ b/rust/src/nasl/builtin/http/error.rs @@ -0,0 +1,25 @@ +use std::io; + +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum HttpError { + #[error("IO error during HTTP: {0}")] + IO(io::ErrorKind), + #[error("HTTP error: {0}")] + H2(String), + #[error("Handle ID {0} not found.")] + HandleIdNotFound(i32), +} + +impl From for HttpError { + fn from(value: io::Error) -> Self { + Self::IO(value.kind()) + } +} + +impl From for HttpError { + fn from(value: h2::Error) -> Self { + Self::H2(format!("{}", value)) + } +} diff --git a/rust/src/nasl/builtin/http/mod.rs b/rust/src/nasl/builtin/http/mod.rs index af134ffda..9fd9274c3 100644 --- a/rust/src/nasl/builtin/http/mod.rs +++ b/rust/src/nasl/builtin/http/mod.rs @@ -5,9 +5,12 @@ //! Defines NASL functions to perform HTTP/2 request. // TODO: implement http functions once socket handling is available +mod error; + use crate::nasl::prelude::*; use crate::nasl::utils::ContextType; +pub use error::HttpError; use h2::client; use core::convert::AsRef; @@ -35,9 +38,9 @@ pub struct NaslHttp { async fn lock_handles( handles: &Arc>>, -) -> Result>, FunctionErrorKind> { +) -> Result>, FnError> { // we actually need to panic as a lock error is fatal - // alternatively we need to add a poison error on FunctionErrorKind + // alternatively we need to add a poison error on FnError Ok(Arc::as_ref(handles).lock().await) } @@ -131,7 +134,7 @@ impl NaslHttp { data: String, method: Method, handle: &mut Handle, - ) -> Result<(Parts, String), FunctionErrorKind> { + ) -> Result<(Parts, String), HttpError> { // Establish TCP connection to the server. let mut config = ClientConfig::builder() @@ -145,49 +148,20 @@ impl NaslHttp { let server_name = ip_str.clone().to_owned().try_into().unwrap(); let connector = TlsConnector::from(Arc::new(config)); - let stream = match TcpStream::connect(format!("{}:{}", ip_str, port)).await { - Ok(a) => a, - Err(e) => { - return Err(FunctionErrorKind::Diagnostic( - e.to_string(), - Some(NaslValue::Null), - )); - } - }; - - let stream = match connector.connect(server_name, stream).await { - Ok(a) => a, - Err(e) => { - return Err(FunctionErrorKind::Diagnostic( - e.to_string(), - Some(NaslValue::Null), - )); - } - }; - - let (h2, connection) = match client::handshake(stream).await { - Ok((x, y)) => (x, y), - Err(e) => { - return Err(FunctionErrorKind::Diagnostic( - e.to_string(), - Some(NaslValue::Null), - )) - } - }; + let stream = TcpStream::connect(format!("{}:{}", ip_str, port)) + .await + .map_err(HttpError::from)?; + let stream = connector + .connect(server_name, stream) + .await + .map_err(HttpError::from)?; + let (h2, connection) = client::handshake(stream).await.map_err(HttpError::from)?; tokio::spawn(async move { connection.await.unwrap(); }); - let mut h2 = match h2.ready().await { - Ok(x) => x, - Err(e) => { - return Err(FunctionErrorKind::Diagnostic( - e.to_string(), - Some(NaslValue::Null), - )) - } - }; + let mut h2 = h2.ready().await.map_err(HttpError::from)?; // Prepare the HTTP request to send to the server. let mut request = Request::builder(); @@ -214,15 +188,7 @@ impl NaslHttp { let mut resp = String::new(); while let Some(chunk) = body.data().await { - let chunk = match chunk { - Ok(byte_chunk) => byte_chunk, - Err(e) => { - return Err(FunctionErrorKind::Diagnostic( - e.to_string(), - Some(NaslValue::Null), - )) - } - }; + let chunk = chunk.map_err(HttpError::from)?; resp.push_str(&String::from_utf8_lossy(&chunk)); // Let the server send more data. @@ -238,40 +204,23 @@ impl NaslHttp { register: &Register, ctx: &Context<'a>, method: Method, - ) -> Result { + ) -> Result { let handle_id = match register.named("handle") { Some(ContextType::Value(NaslValue::Number(x))) => *x as i32, - _ => { - return Err(FunctionErrorKind::WrongArgument( - ("Invalid handle ID").to_string(), - )) - } + _ => return Err(ArgumentError::WrongArgument("Invalid handle ID".to_string()).into()), }; let mut handles = lock_handles(&self.handles).await?; - let handle = match handles + let (_, handle) = handles .iter_mut() .enumerate() .find(|(_i, h)| h.handle_id == handle_id) - { - Some((_i, handle)) => handle, - _ => { - return Err(FunctionErrorKind::Diagnostic( - format!("Handle ID {} not found", handle_id), - Some(NaslValue::Null), - )) - } - }; + .ok_or(HttpError::HandleIdNotFound(handle_id))?; - let item: String = match register.named("item") { - Some(x) => x.to_string(), - _ => { - return Err(FunctionErrorKind::Diagnostic( - "Missing item".to_string(), - Some(NaslValue::Null), - )) - } - }; + let item: String = register + .named("item") + .map(|x| x.to_string()) + .ok_or(FnError::missing_argument("item"))?; let schema: String = match register.named("schema") { Some(x) => { @@ -308,60 +257,42 @@ impl NaslHttp { uri = format!("{}{}", uri, item); - match self.request(&ip_str, port, uri, data, method, handle).await { - Ok((head, body)) => { - handle.http_code = head.status.as_u16(); - let mut header_str = String::new(); - header_str.push_str(format!("{:?} ", head.version).as_str()); - header_str.push_str(format!("{:?}\n", head.status).as_str()); - for (k, v) in head.headers.iter() { - header_str.push_str(&format!( - "{}: {}\n", - k.as_str(), - String::from_utf8_lossy(v.as_bytes()) - )) - } - //let _ = head.headers.iter().map(|(k,v)| header_str.push_str(&format!("{}: {}\n", k.as_str(), String::from_utf8_lossy(v.as_bytes())))); - header_str.push_str(&body); - Ok(NaslValue::String(header_str)) - } - Err(e) => Err(e), + let (head, body) = self + .request(&ip_str, port, uri, data, method, handle) + .await?; + handle.http_code = head.status.as_u16(); + let mut header_str = String::new(); + header_str.push_str(format!("{:?} ", head.version).as_str()); + header_str.push_str(format!("{:?}\n", head.status).as_str()); + for (k, v) in head.headers.iter() { + header_str.push_str(&format!( + "{}: {}\n", + k.as_str(), + String::from_utf8_lossy(v.as_bytes()) + )) } + //let _ = head.headers.iter().map(|(k,v)| header_str.push_str(&format!("{}: {}\n", k.as_str(), String::from_utf8_lossy(v.as_bytes())))); + header_str.push_str(&body); + Ok(NaslValue::String(header_str)) } /// Wrapper function for GET request. See http2_req - async fn get<'a>( - &self, - register: &Register, - ctx: &Context<'a>, - ) -> Result { + async fn get<'a>(&self, register: &Register, ctx: &Context<'a>) -> Result { self.http2_req(register, ctx, Method::GET).await } /// Wrapper function for POST request. See http2_req - async fn post<'a>( - &self, - register: &Register, - ctx: &Context<'a>, - ) -> Result { + async fn post<'a>(&self, register: &Register, ctx: &Context<'a>) -> Result { self.http2_req(register, ctx, Method::POST).await } /// Wrapper function for PUT request. See http2_req - async fn put<'a>( - &self, - register: &Register, - ctx: &Context<'a>, - ) -> Result { + async fn put<'a>(&self, register: &Register, ctx: &Context<'a>) -> Result { self.http2_req(register, ctx, Method::PUT).await } /// Wrapper function for HEAD request. See http2_req - async fn head<'a>( - &self, - register: &Register, - ctx: &Context<'a>, - ) -> Result { + async fn head<'a>(&self, register: &Register, ctx: &Context<'a>) -> Result { self.http2_req(register, ctx, Method::HEAD).await } @@ -370,7 +301,7 @@ impl NaslHttp { &self, register: &Register, ctx: &Context<'a>, - ) -> Result { + ) -> Result { self.http2_req(register, ctx, Method::DELETE).await } @@ -381,7 +312,7 @@ impl NaslHttp { /// On success the function returns a and integer with the handle /// identifier. Null on error. #[nasl_function] - async fn handle(&self) -> Result { + async fn handle(&self) -> Result { let mut handles = lock_handles(&self.handles).await?; let handle_id = next_handle_id(&handles); let h = Handle { @@ -401,7 +332,7 @@ impl NaslHttp { /// The function returns an integer. /// O on success, -1 on error. #[nasl_function(named(handle))] - async fn close_handle(&self, handle: i32) -> Result { + async fn close_handle(&self, handle: i32) -> Result { let mut handles = lock_handles(&self.handles).await?; match handles .iter_mut() @@ -412,10 +343,7 @@ impl NaslHttp { handles.remove(i); Ok(NaslValue::Number(0)) } - _ => Err(FunctionErrorKind::Diagnostic( - format!("Handle ID {} not found", handle), - Some(NaslValue::Number(-1)), - )), + _ => Err(HttpError::HandleIdNotFound(handle).with(ReturnValue(-1))), } } @@ -429,13 +357,11 @@ impl NaslHttp { &self, register: &Register, _: &Context<'_>, - ) -> Result { + ) -> Result { let handle_id = match register.named("handle") { Some(ContextType::Value(NaslValue::Number(x))) => *x as i32, _ => { - return Err(FunctionErrorKind::WrongArgument( - ("Invalid handle ID").to_string(), - )) + return Err(ArgumentError::WrongArgument(("Invalid handle ID").to_string()).into()) } }; @@ -446,10 +372,7 @@ impl NaslHttp { .find(|(_i, h)| h.handle_id == handle_id) { Some((_i, handle)) => Ok(NaslValue::Number(handle.http_code as i64)), - _ => Err(FunctionErrorKind::Diagnostic( - format!("Handle ID {} not found", handle_id), - Some(NaslValue::Null), - )), + _ => Err(HttpError::HandleIdNotFound(handle_id).into()), } } @@ -463,10 +386,10 @@ impl NaslHttp { &self, register: &Register, _: &Context<'_>, - ) -> Result { + ) -> Result { let header_item = match register.named("header_item") { Some(ContextType::Value(NaslValue::String(x))) => x, - _ => return Err(FunctionErrorKind::missing_argument("No command passed")), + _ => return Err(FnError::missing_argument("No command passed")), }; let (key, val) = header_item.split_once(": ").expect("Missing header_item"); @@ -474,9 +397,7 @@ impl NaslHttp { let handle_id = match register.named("handle") { Some(ContextType::Value(NaslValue::Number(x))) => *x as i32, _ => { - return Err(FunctionErrorKind::WrongArgument( - ("Invalid handle ID").to_string(), - )) + return Err(ArgumentError::WrongArgument(("Invalid handle ID").to_string()).into()) } }; @@ -490,10 +411,7 @@ impl NaslHttp { h.header_items.push((key.to_string(), val.to_string())); Ok(NaslValue::Number(0)) } - _ => Err(FunctionErrorKind::Diagnostic( - format!("Handle ID {} not found", handle_id), - Some(NaslValue::Null), - )), + _ => Err(HttpError::HandleIdNotFound(handle_id).into()), } } } diff --git a/rust/src/nasl/builtin/isotime/mod.rs b/rust/src/nasl/builtin/isotime/mod.rs index 5f9543fd0..565e36149 100644 --- a/rust/src/nasl/builtin/isotime/mod.rs +++ b/rust/src/nasl/builtin/isotime/mod.rs @@ -9,6 +9,11 @@ mod tests; use crate::nasl::prelude::*; use chrono::{Datelike, Months, NaiveDate, NaiveDateTime, TimeDelta}; +use thiserror::Error; + +#[derive(Debug, Error)] +#[error("{0}")] +pub struct IsotimeError(String); const ISOFORMAT: &str = "yyyymmddThhmmss"; const READABLEFORMAT: &str = "yyyy-mm-dd hh:mm:ss"; @@ -41,20 +46,17 @@ fn parse_readable_time(time: &str) -> Option { None } -fn parse_time(time: &str) -> Result { +fn parse_time(time: &str) -> Result { if let Some(time) = parse_isotime(time) { return Ok(time); } if let Some(time) = parse_readable_time(time) { return Ok(time); } - Err(FunctionErrorKind::Diagnostic( - format!( - "The given time is not in the correct isotime ({}) or readable time format ({}): {}", - ISOFORMAT, READABLEFORMAT, time - ), - None, - )) + Err(IsotimeError(format!( + "The given time is not in the correct isotime ({}) or readable time format ({}): {}", + ISOFORMAT, READABLEFORMAT, time + ))) } #[nasl_function(named(years, days, seconds))] @@ -63,7 +65,7 @@ fn isotime_add( years: Option, days: Option, seconds: Option, -) -> Result { +) -> Result { let mut time = parse_time(time)?; if let Some(years) = years { @@ -83,13 +85,10 @@ fn isotime_add( } if time.year() < 0 || time.year() > 9999 { - return Err(FunctionErrorKind::Diagnostic( - format!( - "The resulting year is out of range (0000-9999): {}.", - time.year() - ), - None, - )); + return Err(IsotimeError(format!( + "The resulting year is out of range (0000-9999): {}.", + time.year() + ))); } Ok(time.format("%Y%m%dT%H%M%S").to_string()) @@ -106,12 +105,12 @@ fn isotime_now() -> String { } #[nasl_function] -fn isotime_print(time: &str) -> Result { +fn isotime_print(time: &str) -> Result { Ok(parse_time(time)?.format("%Y-%m-%d %H:%M:%S").to_string()) } #[nasl_function] -fn isotime_scan(time: &str) -> Result { +fn isotime_scan(time: &str) -> Result { let time = parse_time(time)?; Ok(time.format("%Y%m%dT%H%M%S").to_string()) diff --git a/rust/src/nasl/builtin/isotime/tests.rs b/rust/src/nasl/builtin/isotime/tests.rs index eaf6d86a3..202bdca44 100644 --- a/rust/src/nasl/builtin/isotime/tests.rs +++ b/rust/src/nasl/builtin/isotime/tests.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception #[cfg(test)] mod tests { - use crate::nasl::test_prelude::*; + use crate::nasl::{builtin::isotime::IsotimeError, test_prelude::*}; #[test] fn isotime_is_valid() { @@ -21,22 +21,13 @@ mod tests { #[test] fn isotime_scan() { - check_err_matches!("isotime_scan(\"\");", FunctionErrorKind::Diagnostic { .. }); - check_err_matches!( - "isotime_scan(\"a8691002T123456\");", - FunctionErrorKind::Diagnostic { .. } - ); - check_err_matches!( - "isotime_scan(\"18691002T1234\");", - FunctionErrorKind::Diagnostic { .. } - ); - check_err_matches!( - "isotime_scan(\"18691002T1234512\");", - FunctionErrorKind::Diagnostic { .. } - ); + check_err_matches!("isotime_scan(\"\");", IsotimeError { .. }); + check_err_matches!("isotime_scan(\"a8691002T123456\");", IsotimeError { .. }); + check_err_matches!("isotime_scan(\"18691002T1234\");", IsotimeError { .. }); + check_err_matches!("isotime_scan(\"18691002T1234512\");", IsotimeError { .. }); check_err_matches!( "isotime_scan(\"1869-10-02T12:34:56\");", - FunctionErrorKind::Diagnostic { .. } + IsotimeError { .. } ); check_code_result("isotime_scan(\"18691002T123456\");", "18691002T123456"); @@ -47,18 +38,12 @@ mod tests { #[test] fn isotime_print() { - check_err_matches!("isotime_print(\"\");", FunctionErrorKind::Diagnostic { .. }); - check_err_matches!( - "isotime_print(\"a8691002T123456\");", - FunctionErrorKind::Diagnostic { .. } - ); - check_err_matches!( - "isotime_print(\"18691002T1234\");", - FunctionErrorKind::Diagnostic { .. } - ); + check_err_matches!("isotime_print(\"\");", IsotimeError { .. }); + check_err_matches!("isotime_print(\"a8691002T123456\");", IsotimeError { .. }); + check_err_matches!("isotime_print(\"18691002T1234\");", IsotimeError { .. }); check_err_matches!( "isotime_print(\"1869-10-02T12:34:56\");", - FunctionErrorKind::Diagnostic { .. } + IsotimeError { .. } ); check_code_result("isotime_print(\"18691002T123456\");", "1869-10-02 12:34:56"); @@ -76,17 +61,14 @@ mod tests { #[test] fn isotime_add() { - check_err_matches!( - "isotime_add(\"\", years: 0);", - FunctionErrorKind::Diagnostic { .. } - ); + check_err_matches!("isotime_add(\"\", years: 0);", IsotimeError { .. }); check_err_matches!( "isotime_add(\"50001002T120000\", years: 5000);", - FunctionErrorKind::Diagnostic { .. } + IsotimeError { .. } ); check_err_matches!( "isotime_add(\"50001002T120000\", years: -5001);", - FunctionErrorKind::Diagnostic { .. } + IsotimeError { .. } ); check_code_result( diff --git a/rust/src/nasl/builtin/knowledge_base/mod.rs b/rust/src/nasl/builtin/knowledge_base/mod.rs index 296fd6a83..cb798160c 100644 --- a/rust/src/nasl/builtin/knowledge_base/mod.rs +++ b/rust/src/nasl/builtin/knowledge_base/mod.rs @@ -9,10 +9,15 @@ use std::time::{SystemTime, UNIX_EPOCH}; use crate::function_set; use crate::nasl::syntax::NaslValue; -use crate::nasl::utils::error::FunctionErrorKind; +use crate::nasl::utils::error::FnError; use crate::nasl::utils::Context; use crate::storage::{Field, Kb, Retrieve}; use nasl_function_proc_macro::nasl_function; +use thiserror::Error; + +#[derive(Debug, Error)] +#[error("{0}")] +pub struct KBError(pub String); /// NASL function to set a value under name in a knowledge base /// Only pushes unique values for the given name. @@ -22,16 +27,13 @@ fn set_kb_item( name: NaslValue, value: NaslValue, expires: Option, -) -> Result { +) -> Result { let expires = match expires { Some(NaslValue::Number(x)) => Some(x), Some(NaslValue::Exit(0)) => None, None => None, Some(x) => { - return Err(FunctionErrorKind::Diagnostic( - format!("expected expires to be a number but is {x}."), - None, - )) + return Err(KBError(format!("expected expires to be a number but is {x}.")).into()) } } .map(|seconds| { @@ -56,7 +58,7 @@ fn set_kb_item( /// NASL function to get a knowledge base #[nasl_function] -fn get_kb_item(c: &Context, key: &str) -> Result { +fn get_kb_item(c: &Context, key: &str) -> Result { c.retriever() .retrieve(c.key(), Retrieve::KB(key.to_string())) .map(|r| { @@ -73,11 +75,7 @@ fn get_kb_item(c: &Context, key: &str) -> Result { /// NASL function to replace a kb list #[nasl_function(named(name, value))] -fn replace_kb_item( - c: &Context, - name: NaslValue, - value: NaslValue, -) -> Result { +fn replace_kb_item(c: &Context, name: NaslValue, value: NaslValue) -> Result { c.dispatcher() .dispatch_replace( c.key(), @@ -93,7 +91,7 @@ fn replace_kb_item( /// NASL function to retrieve an item in a KB. #[nasl_function] -fn get_kb_list(c: &Context, key: NaslValue) -> Result { +fn get_kb_list(c: &Context, key: NaslValue) -> Result { c.retriever() .retrieve(c.key(), Retrieve::KB(key.to_string())) .map(|r| { diff --git a/rust/src/nasl/builtin/knowledge_base/tests.rs b/rust/src/nasl/builtin/knowledge_base/tests.rs index c27e0a817..9cccfacb3 100644 --- a/rust/src/nasl/builtin/knowledge_base/tests.rs +++ b/rust/src/nasl/builtin/knowledge_base/tests.rs @@ -5,13 +5,13 @@ #[cfg(test)] mod tests { use crate::nasl::test_prelude::*; - use FunctionErrorKind::*; + use ArgumentError::*; #[test] fn set_kb_item() { check_code_result(r#"set_kb_item(name: "test", value: 1);"#, NaslValue::Null); - check_err_matches!(r#"set_kb_item(name: "test");"#, MissingArguments { .. }); - check_err_matches!(r#"set_kb_item(value: 1);"#, MissingArguments { .. }); + check_err_matches!(r#"set_kb_item(name: "test");"#, MissingNamed { .. }); + check_err_matches!(r#"set_kb_item(value: 1);"#, MissingNamed { .. }); } #[test] @@ -19,16 +19,8 @@ mod tests { let mut t = TestBuilder::default(); t.ok(r#"set_kb_item(name: "test", value: 1);"#, NaslValue::Null); t.ok(r#"get_kb_item("test");"#, 1); - check_err_matches!( - t, - r#"get_kb_item("test", 1);"#, - FunctionErrorKind::TrailingPositionalArguments { .. } - ); - check_err_matches!( - t, - r#"get_kb_item();"#, - FunctionErrorKind::MissingPositionalArguments { .. } - ); + check_err_matches!(t, r#"get_kb_item("test", 1);"#, TrailingPositionals { .. },); + check_err_matches!(t, r#"get_kb_item();"#, MissingPositionals { .. }); } #[test] diff --git a/rust/src/nasl/builtin/misc/mod.rs b/rust/src/nasl/builtin/misc/mod.rs index b392176e8..92cc6a92e 100644 --- a/rust/src/nasl/builtin/misc/mod.rs +++ b/rust/src/nasl/builtin/misc/mod.rs @@ -10,7 +10,7 @@ mod tests; use std::{ collections::HashMap, fs::File, - io::{Read, Write}, + io::{self, Read, Write}, thread, time::{self, Duration, UNIX_EPOCH}, }; @@ -19,16 +19,25 @@ use chrono::{ self, DateTime, Datelike, FixedOffset, Local, LocalResult, Offset, TimeZone, Timelike, Utc, }; use nasl_function_proc_macro::nasl_function; +use thiserror::Error; use crate::nasl::{prelude::*, utils::function::Maybe}; use flate2::{ read::GzDecoder, read::ZlibDecoder, write::GzEncoder, write::ZlibEncoder, Compression, }; +#[derive(Debug, Error)] +pub enum MiscError { + #[error("IO Error: {0}")] + IO(#[from] io::Error), + #[error("Encountered time before 1970. {0}")] + TimeBefore1970(String), +} + #[inline] #[cfg(unix)] /// Reads 8 bytes from /dev/urandom and parses it to an i64 -pub fn random_impl() -> Result { +pub fn random_impl() -> Result { let mut rng = File::open("/dev/urandom")?; let mut buffer = [0u8; 8]; rng.read_exact(&mut buffer) @@ -38,7 +47,7 @@ pub fn random_impl() -> Result { /// NASL function to get random number #[nasl_function] -fn rand() -> Result { +fn rand() -> Result { random_impl() } @@ -91,13 +100,11 @@ fn isnull(val: NaslValue) -> bool { /// Returns the seconds counted from 1st January 1970 as an integer. #[nasl_function] -fn unixtime() -> Result { +fn unixtime() -> Result { std::time::SystemTime::now() .duration_since(UNIX_EPOCH) .map(|t| t.as_secs()) - .map_err(|_| { - FunctionErrorKind::Dirty("System time set to time before 1st January 1960".into()) - }) + .map_err(|e| MiscError::TimeBefore1970(e.to_string())) } /// Compress given data with gzip, when headformat is set to 'gzip' it uses gzipheader. @@ -226,13 +233,13 @@ fn defined_func(ctx: &Context, register: &Register, fn_name: Option> /// /// For example: “1067352015.030757” means 1067352015 seconds and 30757 microseconds. #[nasl_function] -fn gettimeofday() -> Result { +fn gettimeofday() -> Result { match time::SystemTime::now().duration_since(time::SystemTime::UNIX_EPOCH) { Ok(time) => { let time = time.as_micros(); Ok(format!("{}.{:06}", time / 1000000, time % 1000000)) } - Err(e) => Err(FunctionErrorKind::Dirty(format!("{e}"))), + Err(e) => Err(MiscError::TimeBefore1970(e.to_string())), } } diff --git a/rust/src/nasl/builtin/misc/tests.rs b/rust/src/nasl/builtin/misc/tests.rs index 99936771d..cb9977419 100644 --- a/rust/src/nasl/builtin/misc/tests.rs +++ b/rust/src/nasl/builtin/misc/tests.rs @@ -38,7 +38,7 @@ mod tests { check_err_matches!( t, r#"typeof(23,76);"#, - FunctionErrorKind::TrailingPositionalArguments { .. } + ArgumentError::TrailingPositionals { .. } ); t.ok("d['test'] = 2;", 2); t.ok("typeof(d);", "array"); @@ -72,22 +72,12 @@ mod tests { #[test] fn gunzip() { let mut t = TestBuilder::default(); - t.run_all( - r#" - z = raw_string (0x78, 0x9c, 0xab, 0x02, 0x00, 0x00, 0x7b, 0x00, 0x7b); - gunzip(data: z); - # With Header Format and data is data - gz = gzip(data: 'gz', headformat: "gzip"); - gunzip(data: gz); - # Without Header format and data is a string - ngz = gzip(data: "ngz"); - gunzip(data: ngz); - "#, - ); - let results = t.results(); - assert_eq!(results[1], Ok(NaslValue::String("z".into()))); - assert_eq!(results[3], Ok(NaslValue::String("gz".into()))); - assert_eq!(results[5], Ok(NaslValue::String("ngz".into()))); + t.run(r#"z = raw_string (0x78, 0x9c, 0xab, 0x02, 0x00, 0x00, 0x7b, 0x00, 0x7b);"#); + t.ok(r#"gunzip(data: z);"#, "z"); + t.run(r#"gz = gzip(data: 'gz', headformat: "gzip");"#); + t.ok(r#"gunzip(data: gz);"#, "gz"); + t.run(r#"ngz = gzip(data: "ngz");"#); + t.ok(r#"gunzip(data: ngz);"#, "ngz"); } #[test] diff --git a/rust/src/nasl/builtin/mod.rs b/rust/src/nasl/builtin/mod.rs index 21fb57575..2cdc9b05d 100644 --- a/rust/src/nasl/builtin/mod.rs +++ b/rust/src/nasl/builtin/mod.rs @@ -8,6 +8,7 @@ mod array; mod cert; mod cryptographic; mod description; +mod error; mod host; mod http; mod isotime; @@ -24,7 +25,8 @@ mod string; #[cfg(test)] mod tests; -pub use ssh::SshError; +pub use error::BuiltinError; +pub use host::HostError; use crate::nasl::syntax::{Loader, NoOpLoader}; use crate::nasl::utils::{Context, Executor, NaslVarRegister, NaslVarRegisterBuilder, Register}; diff --git a/rust/src/nasl/builtin/network/mod.rs b/rust/src/nasl/builtin/network/mod.rs index ab36c9a59..33ae8fe6c 100644 --- a/rust/src/nasl/builtin/network/mod.rs +++ b/rust/src/nasl/builtin/network/mod.rs @@ -4,10 +4,11 @@ use std::{fmt::Display, net::IpAddr}; -use crate::nasl::syntax::NaslValue; -use crate::nasl::utils::{Context, FunctionErrorKind}; +use crate::nasl::prelude::*; use crate::storage::{Field, Retrieve}; +use super::knowledge_base::KBError; + #[allow(clippy::module_inception)] pub mod network; pub mod network_utils; @@ -96,7 +97,7 @@ pub fn get_retry(context: &Context) -> u8 { } } -pub fn get_kb_item(context: &Context, name: &str) -> Result, FunctionErrorKind> { +pub fn get_kb_item(context: &Context, name: &str) -> Result, FnError> { context .retriever() .retrieve(context.key(), Retrieve::KB(name.to_string())) @@ -110,15 +111,15 @@ pub fn get_kb_item(context: &Context, name: &str) -> Result, F .map_err(|e| e.into()) } -pub fn get_kb_item_str(context: &Context, name: &str) -> Result { +pub fn get_kb_item_str(context: &Context, name: &str) -> Result { get_kb_item(context, name)? .map(|x| x.to_string()) - .ok_or_else(|| FunctionErrorKind::Diagnostic(format!("KB key {} is not set", name), None)) + .ok_or_else(|| KBError(format!("KB key {} is not set", name)).into()) } -pub fn verify_port(port: i64) -> Result { +pub fn verify_port(port: i64) -> Result { if !(0..=65535).contains(&port) { - return Err(FunctionErrorKind::WrongArgument(format!( + return Err(ArgumentError::WrongArgument(format!( "{} is not a valid port number", port ))); diff --git a/rust/src/nasl/builtin/network/network.rs b/rust/src/nasl/builtin/network/network.rs index a5e6f3391..078b2440b 100644 --- a/rust/src/nasl/builtin/network/network.rs +++ b/rust/src/nasl/builtin/network/network.rs @@ -5,12 +5,13 @@ use std::{net::IpAddr, process::Command}; use super::mtu; +use super::socket::SocketError; use super::{ network_utils::{get_netmask_by_local_ip, get_source_ip, ipstr2ipaddr, islocalhost}, verify_port, DEFAULT_PORT, }; use crate::function_set; -use crate::nasl::utils::{Context, FunctionErrorKind}; +use crate::nasl::utils::{Context, FnError}; use crate::storage::{types::Primitive, Field, Kb}; use nasl_function_proc_macro::nasl_function; @@ -22,14 +23,14 @@ fn get_host_ip(context: &Context) -> String { /// Get the IP address of the current (attacking) machine depending on which network device is used #[nasl_function] -fn this_host(context: &Context) -> Result { +fn this_host(context: &Context) -> Result { let dst = ipstr2ipaddr(context.target())?; let port: u16 = DEFAULT_PORT; get_source_ip(dst, port) .map(|ip| ip.to_string()) - .map_err(|_| FunctionErrorKind::Diagnostic("No route to destination".to_string(), None)) + .map_err(|_| SocketError::Diagnostic("No route to destination".to_string())) } /// Get the host name of the current (attacking) machine @@ -44,21 +45,21 @@ fn this_host_name() -> String { /// get the maximum transition unit for the scanned host #[nasl_function] -fn get_mtu(context: &Context) -> Result { +fn get_mtu(context: &Context) -> Result { let target = ipstr2ipaddr(context.target())?; Ok(mtu(target) as i64) } /// check if the currently scanned host is the localhost #[nasl_function] -fn nasl_islocalhost(context: &Context) -> Result { +fn nasl_islocalhost(context: &Context) -> Result { let host_ip = ipstr2ipaddr(context.target())?; Ok(islocalhost(host_ip)) } /// Check if the target host is on the same network as the attacking host #[nasl_function] -fn islocalnet(context: &Context) -> Result { +fn islocalnet(context: &Context) -> Result { let dst = ipstr2ipaddr(context.target())?; let src = get_source_ip(dst, DEFAULT_PORT)?; let netmask = match get_netmask_by_local_ip(src)? { @@ -133,11 +134,7 @@ fn islocalnet(context: &Context) -> Result { /// Declares an open port on the target host #[nasl_function(named(port, proto))] -fn scanner_add_port( - context: &Context, - port: i64, - proto: Option<&str>, -) -> Result<(), FunctionErrorKind> { +fn scanner_add_port(context: &Context, port: i64, proto: Option<&str>) -> Result<(), FnError> { let port = verify_port(port)?; let protocol = proto.unwrap_or("tcp"); diff --git a/rust/src/nasl/builtin/network/network_utils.rs b/rust/src/nasl/builtin/network/network_utils.rs index 7884e22c8..6bc75847d 100644 --- a/rust/src/nasl/builtin/network/network_utils.rs +++ b/rust/src/nasl/builtin/network/network_utils.rs @@ -4,23 +4,22 @@ //! This module provides utility functions for IP handling. use std::{ - io, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, UdpSocket}, ptr, str::FromStr, time::Duration, }; -use crate::nasl::prelude::*; +use super::socket::SocketError; /// Convert a string in a IpAddr -pub fn ipstr2ipaddr(ip_addr: &str) -> Result { +pub fn ipstr2ipaddr(ip_addr: &str) -> Result { match IpAddr::from_str(ip_addr) { Ok(ip) => Ok(ip), - Err(_) => Err(FunctionErrorKind::Diagnostic( - format!("Invalid IP address ({})", ip_addr), - Some(NaslValue::Null), - )), + Err(_) => Err(SocketError::Diagnostic(format!( + "Invalid IP address ({})", + ip_addr + ))), } } @@ -32,20 +31,25 @@ pub fn convert_timeout(timeout: Option) -> Option { } /// Bind a local UDP socket to a V4 or V6 address depending on the given destination address -pub fn bind_local_socket(dst: &SocketAddr) -> io::Result { +pub fn bind_local_socket(dst: &SocketAddr) -> Result { + let fe = Err(SocketError::Diagnostic("Error binding".to_string())); match dst { - SocketAddr::V4(_) => UdpSocket::bind("0.0.0.0:0"), - SocketAddr::V6(_) => UdpSocket::bind("[::]:0"), + SocketAddr::V4(_) => UdpSocket::bind("0.0.0.0:0").or(fe), + SocketAddr::V6(_) => UdpSocket::bind("[::]:0").or(fe), } } /// Return the source IP address given the destination IP address -pub fn get_source_ip(dst: IpAddr, port: u16) -> io::Result { +pub fn get_source_ip(dst: IpAddr, port: u16) -> Result { let socket = SocketAddr::new(dst, port); let sd = format!("{}:{}", dst, port); let local_socket = bind_local_socket(&socket)?; - local_socket.connect(sd)?; - Ok(local_socket.local_addr()?.ip()) + local_socket + .connect(sd) + .ok() + .and_then(|_| local_socket.local_addr().ok()) + .and_then(|l_addr| IpAddr::from_str(&l_addr.ip().to_string()).ok()) + .ok_or_else(|| SocketError::Diagnostic("No route to destination".to_string())) } /// Tests whether a packet sent to IP is LIKELY to route through the @@ -61,15 +65,14 @@ pub fn islocalhost(addr: IpAddr) -> bool { } /// Get the interface from the local ip -pub fn get_netmask_by_local_ip(local_address: IpAddr) -> Result, FunctionErrorKind> { +pub fn get_netmask_by_local_ip(local_address: IpAddr) -> Result, SocketError> { let mut interfaces: *mut libc::ifaddrs = ptr::null_mut(); let ret = unsafe { libc::getifaddrs(&mut interfaces) }; if ret < 0 { - return Err(FunctionErrorKind::Diagnostic( + return Err(SocketError::Diagnostic( "Error getting interfaces".to_string(), - None, )); } @@ -127,8 +130,7 @@ pub fn get_netmask_by_local_ip(local_address: IpAddr) -> Result, unsafe { libc::freeifaddrs(interfaces); } - Err(FunctionErrorKind::Diagnostic( + Err(SocketError::Diagnostic( "No route to destination".to_string(), - None, )) } diff --git a/rust/src/nasl/builtin/network/socket.rs b/rust/src/nasl/builtin/network/socket.rs index 8bf170b51..8f337b087 100644 --- a/rust/src/nasl/builtin/network/socket.rs +++ b/rust/src/nasl/builtin/network/socket.rs @@ -10,12 +10,10 @@ use std::{ time::{Duration, SystemTime}, }; -use crate::function_set; -use crate::nasl::syntax::NaslValue; -use crate::nasl::utils::{error::FunctionErrorKind, Context}; +use crate::nasl::prelude::*; use dns_lookup::lookup_host; -use nasl_function_proc_macro::nasl_function; use rustls::ClientConnection; +use thiserror::Error; use super::{ get_kb_item, get_kb_item_str, get_retry, @@ -26,6 +24,14 @@ use super::{ verify_port, OpenvasEncaps, }; +#[derive(Debug, Error)] +#[error("{0}")] +pub enum SocketError { + IO(#[from] std::io::Error), + Diagnostic(String), + WrongArgument(String), +} + /// Interval used for timing tcp requests. Any tcp request has to wait at least /// the time defined by this interval after the last request has been sent. struct Interval { @@ -98,24 +104,24 @@ impl NaslSockets { /// Close a given file descriptor taken as an unnamed argument. #[nasl_function] - fn close(&self, socket_fd: usize) -> Result { + fn close(&self, socket_fd: usize) -> Result { let mut handles = self.handles.write().unwrap(); match handles.handles.get_mut(socket_fd) { Some(NaslSocket::Closed) => { - return Err(FunctionErrorKind::Diagnostic( + return Err(SocketError::Diagnostic( "the given socket FD is already closed".to_string(), - None, - )) + ) + .into()) } Some(socket) => { *socket = NaslSocket::Closed; handles.closed_fd.push(socket_fd); } None => { - return Err(FunctionErrorKind::Diagnostic( + return Err(SocketError::Diagnostic( "the given socket FD does not exist".to_string(), - None, - )) + ) + .into()) } } Ok(NaslValue::Null) @@ -144,7 +150,7 @@ impl NaslSockets { data: &[u8], option: Option, len: Option, - ) -> Result { + ) -> Result { let len = if let Some(len) = len { if len < 1 || len > data.len() { data.len() @@ -163,7 +169,7 @@ impl NaslSockets { .unwrap() .handles .get_mut(socket) - .ok_or(FunctionErrorKind::WrongArgument(format!( + .ok_or(SocketError::WrongArgument(format!( "the given socket FD {socket} does not exist" )))? { NaslSocket::Tcp(conn) => { @@ -171,7 +177,7 @@ impl NaslSockets { if let Some(flags) = option { if flags < 0 || flags > i32::MAX as i64 { - return Err(FunctionErrorKind::WrongArgument( + return Err(SocketError::WrongArgument( "the given flags value is out of range".to_string(), )); } @@ -186,7 +192,7 @@ impl NaslSockets { } Ok(conn.write(data)?) } - NaslSocket::Closed => Err(FunctionErrorKind::WrongArgument( + NaslSocket::Closed => Err(SocketError::WrongArgument( "the given socket FD is already closed".to_string(), )), } @@ -213,7 +219,7 @@ impl NaslSockets { length: usize, min: Option, timeout: Option, - ) -> Result { + ) -> Result { let min = min .map(|min| if min < 0 { length } else { min as usize }) .unwrap_or(length); @@ -225,7 +231,7 @@ impl NaslSockets { .unwrap() .handles .get_mut(socket) - .ok_or(FunctionErrorKind::WrongArgument(format!( + .ok_or(SocketError::WrongArgument(format!( "the given socket FD {socket} does not exist" )))? { NaslSocket::Tcp(conn) => { @@ -238,7 +244,7 @@ impl NaslSockets { match conn.read_with_timeout(&mut data[pos..], timeout) { Ok(n) => pos += n, Err(e) if e.kind() == io::ErrorKind::TimedOut => break, - Err(e) => return Err(e.into()), + Err(e) => return Err(SocketError::from(e)), } } Ok(NaslValue::Data(data[..pos].to_vec())) @@ -251,7 +257,7 @@ impl NaslSockets { Ok(NaslValue::Data(data[..pos].to_vec())) } - NaslSocket::Closed => Err(FunctionErrorKind::WrongArgument( + NaslSocket::Closed => Err(SocketError::WrongArgument( "the given socket FD is already closed".to_string(), )), } @@ -268,7 +274,7 @@ impl NaslSockets { socket: usize, #[allow(unused_variables)] length: usize, timeout: Option, - ) -> Result { + ) -> Result { let mut data = String::new(); match self .handles @@ -276,7 +282,7 @@ impl NaslSockets { .unwrap() .handles .get_mut(socket) - .ok_or(FunctionErrorKind::WrongArgument(format!( + .ok_or(SocketError::WrongArgument(format!( "the given socket FD {socket} does not exist" )))? { NaslSocket::Tcp(conn) => { @@ -286,11 +292,10 @@ impl NaslSockets { }?; Ok(NaslValue::Data(data.as_bytes()[..pos].to_vec())) } - NaslSocket::Udp(_) => Err(FunctionErrorKind::Diagnostic( + NaslSocket::Udp(_) => Err(SocketError::Diagnostic( "This function is only available for TCP connections".to_string(), - None, )), - NaslSocket::Closed => Err(FunctionErrorKind::WrongArgument( + NaslSocket::Closed => Err(SocketError::WrongArgument( "the given socket FD is already closed".to_string(), )), } @@ -301,40 +306,34 @@ impl NaslSockets { /// - Secret/kdc_port /// - Secret/kdc_use_tcp #[nasl_function] - fn open_sock_kdc(&self, context: &Context) -> Result { + fn open_sock_kdc(&self, context: &Context) -> Result { let hostname = get_kb_item_str(context, "Secret/kdc_hostname")?; let ip = lookup_host(&hostname) - .map_err(|_| { - FunctionErrorKind::Diagnostic(format!("unable to lookup hostname {hostname}"), None) - })? + .map_err(|_| SocketError::Diagnostic(format!("unable to lookup hostname {hostname}")))? .into_iter() .next() - .ok_or(FunctionErrorKind::Diagnostic( - format!("No IP found for hostname {hostname}"), - None, - ))?; + .ok_or(SocketError::Diagnostic(format!( + "No IP found for hostname {hostname}" + )))?; let port = get_kb_item(context, "Secret/kdc_port")?; let port = match port { Some(NaslValue::Number(x)) => { if x <= 0 || x > 65535 { - Err(FunctionErrorKind::Diagnostic( + Err(SocketError::Diagnostic( "KB key 'Secret/kdc_port' out of range".to_string(), - port, )) } else { Ok(x as u16) } } - Some(_) => Err(FunctionErrorKind::Diagnostic( + Some(_) => Err(SocketError::Diagnostic( "KB key 'Secret/kdc_port' has wrong type".to_string(), - port, )), - None => Err(FunctionErrorKind::Diagnostic( + None => Err(SocketError::Diagnostic( "KB key 'Secret/kdc_port' is not set".to_string(), - None, )), }?; @@ -350,7 +349,8 @@ impl NaslSockets { Duration::from_secs(30), None, get_retry(context), - )?; + ) + .map_err(SocketError::from)?; NaslSocket::Tcp(Box::new(tcp)) } else { let udp = UdpConnection::new(ip, port)?; @@ -383,7 +383,7 @@ impl NaslSockets { port: u16, vhost: &str, transport: i64, - ) -> Result, FunctionErrorKind> { + ) -> Result, SocketError> { if transport < 0 { // TODO: Get port transport and open connection depending on it todo!() @@ -398,10 +398,9 @@ impl NaslSockets { Some(OpenvasEncaps::Ip) => None, // Unsupported transport layer None | Some(OpenvasEncaps::Max) => { - return Err(FunctionErrorKind::Diagnostic( - format!("unsupported transport layer: {transport} (unknown)"), - None, - )) + return Err(SocketError::Diagnostic(format!( + "unsupported transport layer: {transport} (unknown)" + ))) } // TLS/SSL Some(tls_version) => match tls_version { @@ -409,10 +408,9 @@ impl NaslSockets { Self::make_tls_client_connection(context, vhost) } _ => { - return Err(FunctionErrorKind::Diagnostic( - format!("unsupported transport layer: {transport} {tls_version}"), - None, - )) + return Err(SocketError::Diagnostic(format!( + "unsupported transport layer: {transport} {tls_version}" + ))) } }, }; @@ -449,7 +447,7 @@ impl NaslSockets { bufsz: Option, // TODO: Extract information from custom priority string // priority: Option<&str>, - ) -> Result { + ) -> Result { // Get port let port = verify_port(port)?; let transport = transport.unwrap_or(-1); @@ -487,7 +485,7 @@ impl NaslSockets { /// Reads the information necessary for a TLS connection from the KB and /// return a TlsConfig on success. - fn get_tls_conf(context: &Context) -> Result { + fn get_tls_conf(context: &Context) -> Result { let cert_path = get_kb_item_str(context, "SSL/cert")?; let key_path = get_kb_item_str(context, "SSL/key")?; let password = get_kb_item_str(context, "SSL/password")?; @@ -503,7 +501,7 @@ impl NaslSockets { /// Open a UDP socket to the target host #[nasl_function] - fn open_sock_udp(&self, context: &Context, port: i64) -> Result { + fn open_sock_udp(&self, context: &Context, port: i64) -> Result { let port = verify_port(port)?; let addr = ipstr2ipaddr(context.target())?; @@ -519,7 +517,7 @@ impl NaslSockets { sport: u16, dport: u16, tcp: bool, - ) -> Result { + ) -> Result { if tcp { // TODO: set timeout to global recv timeout when available let timeout = Duration::from_secs(10); @@ -540,12 +538,12 @@ impl NaslSockets { dport: i64, sport: Option, tcp: bool, - ) -> Result { + ) -> Result { let dport = verify_port(dport)?; if let Some(sport) = sport { let sport = verify_port(sport)?; - return self.connect_priv_sock(addr, sport, dport as u16, tcp); + return Ok(self.connect_priv_sock(addr, sport, dport as u16, tcp)?); } for sport in (1..=1023).rev() { @@ -565,13 +563,11 @@ impl NaslSockets { }; return Ok(NaslValue::Number(fd as i64)); } - Err(FunctionErrorKind::Diagnostic( - format!( - "Unable to open priv socket to {} on any socket from 1-1023", - addr - ), - None, + Err(SocketError::Diagnostic(format!( + "Unable to open priv socket to {} on any socket from 1-1023", + addr )) + .into()) } /// Open a privileged socket to the target host. @@ -586,7 +582,7 @@ impl NaslSockets { context: &Context, dport: i64, sport: Option, - ) -> Result { + ) -> Result { let addr = ipstr2ipaddr(context.target())?; self.open_priv_sock(addr, dport, sport, true) } @@ -602,26 +598,26 @@ impl NaslSockets { context: &Context, dport: i64, sport: Option, - ) -> Result { + ) -> Result { let addr = ipstr2ipaddr(context.target())?; self.open_priv_sock(addr, dport, sport, false) } /// Get the source port of a open socket #[nasl_function] - fn get_source_port(&self, socket: usize) -> Result { + fn get_source_port(&self, socket: usize) -> Result { let handles = self.handles.read().unwrap(); let socket = handles .handles .get(socket) - .ok_or(FunctionErrorKind::WrongArgument( + .ok_or(SocketError::WrongArgument( "the given socket FD does not exist".to_string(), ))?; let port = match socket { NaslSocket::Tcp(conn) => conn.local_addr()?.port(), NaslSocket::Udp(conn) => conn.local_addr()?.port(), NaslSocket::Closed => { - return Err(FunctionErrorKind::WrongArgument( + return Err(SocketError::WrongArgument( "the given socket FD is already closed".to_string(), )) } @@ -635,19 +631,18 @@ impl NaslSockets { pub fn check_ftp_response( mut conn: impl BufRead, expected_code: &[usize], - ) -> Result { + ) -> Result { let mut line = String::with_capacity(5); conn.read_line(&mut line)?; if line.len() < 5 { - return Err(FunctionErrorKind::Diagnostic( + return Err(SocketError::Diagnostic( "could not read reply code".to_owned(), - None, )); } let code: usize = line[0..3].parse().map_err(|err| { - FunctionErrorKind::Diagnostic(format!("could not parse reply code: {}", err), None) + SocketError::Diagnostic(format!("could not parse reply code: {}", err)) })?; // multiple line reply @@ -663,10 +658,10 @@ impl NaslSockets { if expected_code.iter().any(|ec| code == *ec) { Ok(code) } else { - Err(FunctionErrorKind::Diagnostic( - format!("Expected code {:?}, got response: {}", expected_code, line), - None, - )) + Err(SocketError::Diagnostic(format!( + "Expected code {:?}, got response: {}", + expected_code, line + ))) } } @@ -675,14 +670,14 @@ impl NaslSockets { /// - pass: is the password (again, no default value like the user e-mail address) /// - socket: an open socket. #[nasl_function(named(user, pass, socket))] - fn ftp_log_in(&self, user: &str, pass: &str, socket: usize) -> Result { + fn ftp_log_in(&self, user: &str, pass: &str, socket: usize) -> Result { match self .handles .write() .unwrap() .handles .get_mut(socket) - .ok_or(FunctionErrorKind::WrongArgument(format!( + .ok_or(SocketError::WrongArgument(format!( "the given socket FD {socket} does not exist" )))? { NaslSocket::Tcp(conn) => { @@ -698,11 +693,10 @@ impl NaslSockets { } Ok(true) } - NaslSocket::Udp(_) => Err(FunctionErrorKind::Diagnostic( + NaslSocket::Udp(_) => Err(SocketError::Diagnostic( "This function is only available for TCP connections".to_string(), - None, )), - NaslSocket::Closed => Err(FunctionErrorKind::WrongArgument( + NaslSocket::Closed => Err(SocketError::WrongArgument( "the given socket FD is already closed".to_string(), )), } diff --git a/rust/src/nasl/builtin/network/tcp.rs b/rust/src/nasl/builtin/network/tcp.rs index 5b1a54b7e..953e4078f 100644 --- a/rust/src/nasl/builtin/network/tcp.rs +++ b/rust/src/nasl/builtin/network/tcp.rs @@ -8,7 +8,7 @@ use rustls::{ClientConnection, Stream}; use socket2::{self, Socket}; -use super::network_utils::get_source_ip; +use super::{network_utils::get_source_ip, socket::SocketError}; struct TcpDataStream { sock: Socket, @@ -127,7 +127,7 @@ impl TcpConnection { sport: u16, dport: u16, timeout: Duration, - ) -> io::Result { + ) -> Result { let sock = match addr { IpAddr::V4(_) => socket2::Socket::new_raw( socket2::Domain::IPV4, diff --git a/rust/src/nasl/builtin/network/udp.rs b/rust/src/nasl/builtin/network/udp.rs index 3403cd969..be911ebca 100644 --- a/rust/src/nasl/builtin/network/udp.rs +++ b/rust/src/nasl/builtin/network/udp.rs @@ -5,7 +5,7 @@ use std::{ time::Duration, }; -use super::{mtu, network_utils::bind_local_socket}; +use super::{mtu, network_utils::bind_local_socket, socket::SocketError}; pub struct UdpConnection { socket: UdpSocket, @@ -69,7 +69,7 @@ impl Write for UdpConnection { } impl UdpConnection { - pub fn new(addr: IpAddr, port: u16) -> io::Result { + pub fn new(addr: IpAddr, port: u16) -> Result { let sock_addr = SocketAddr::new(addr, port); let socket = bind_local_socket(&sock_addr)?; socket.connect(sock_addr)?; diff --git a/rust/src/nasl/builtin/raw_ip/frame_forgery.rs b/rust/src/nasl/builtin/raw_ip/frame_forgery.rs index 44f2aa9ba..e45c73eb7 100644 --- a/rust/src/nasl/builtin/raw_ip/frame_forgery.rs +++ b/rust/src/nasl/builtin/raw_ip/frame_forgery.rs @@ -14,6 +14,7 @@ use pcap::{Capture, Device}; use super::super::host::get_host_ip; use super::raw_ip_utils::{get_interface_by_local_ip, get_source_ip, ipstr2ipaddr}; +use super::RawIpError; use tracing::info; @@ -105,11 +106,11 @@ impl From<&Frame> for Vec { } impl TryFrom<&[u8]> for Frame { - type Error = FunctionErrorKind; + type Error = FnError; fn try_from(f: &[u8]) -> Result { if f.len() < 14 { - Err(FunctionErrorKind::missing_argument("valid ip address")) + Err(FnError::missing_argument("valid ip address")) } else { let mut frame = Frame::new(); frame.set_dsthaddr(MacAddr(f[0], f[1], f[2], f[3], f[4], f[5])); @@ -272,28 +273,27 @@ fn convert_vec_into_mac_address(v: &[u8]) -> Option { } } -fn validate_mac_address(v: Option<&ContextType>) -> Result { +fn validate_mac_address(v: Option<&ContextType>) -> Result { let mac_addr = match v { Some(ContextType::Value(NaslValue::String(x))) => MacAddr::from_str(x).ok(), Some(ContextType::Value(NaslValue::Data(x))) => convert_vec_into_mac_address(x), _ => None, }; - mac_addr.ok_or_else(|| { - FunctionErrorKind::wrong_unnamed_argument("mac address", "invalid mac address") - }) + mac_addr.ok_or_else(|| FnError::wrong_unnamed_argument("mac address", "invalid mac address")) } /// Return the MAC address, given the interface name -fn get_local_mac_address(name: &str) -> Option { - match interfaces().into_iter().find(|x| x.name == *name) { - Some(dev) => dev.mac, - _ => None, - } +fn get_local_mac_address(name: &str) -> Result { + interfaces() + .into_iter() + .find(|x| x.name == *name) + .and_then(|dev| dev.mac) + .ok_or_else(|| RawIpError::FailedToGetLocalMacAddress.into()) } /// Return a frame given a capture device and a filter. It returns an empty frame in case /// there was no response or anything was filtered. -fn recv_frame(cap: &mut Capture, filter: &str) -> Result { +fn recv_frame(cap: &mut Capture, filter: &str) -> Result { let f = Frame::new(); let p = match cap.filter(filter, true) { @@ -313,7 +313,7 @@ fn send_frame( pcap_active: &bool, filter: Option<&String>, timeout: i32, -) -> Result, FunctionErrorKind> { +) -> Result, FnError> { let mut capture_dev = match Capture::from_device(iface.clone()) { Ok(c) => match c.promisc(true).timeout(timeout).open() { Ok(mut capture) => match capture.sendpacket(frame) { @@ -346,15 +346,12 @@ fn send_frame( /// /// It takes the following argument: /// - cap_timeout: time to wait for answer in seconds, 5 by default -fn nasl_send_arp_request( - register: &Register, - context: &Context, -) -> Result { +fn nasl_send_arp_request(register: &Register, context: &Context) -> Result { let timeout = match register.named("pcap_timeout") { Some(ContextType::Value(NaslValue::Number(x))) => *x as i32 * 1000i32, // to milliseconds None => DEFAULT_TIMEOUT, _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( + return Err(FnError::wrong_unnamed_argument( "Integer", "Invalid timeout value", )) @@ -364,26 +361,19 @@ fn nasl_send_arp_request( let target_ip = get_host_ip(context)?; if target_ip.is_ipv6() { - return Err(FunctionErrorKind::wrong_unnamed_argument( + return Err(FnError::wrong_unnamed_argument( "IPv4", "IPv6 does not support ARP protocol.", )); } let local_ip = get_source_ip(target_ip, 50000u16)?; let iface = get_interface_by_local_ip(local_ip)?; - let local_mac_address = match get_local_mac_address(&iface.name) { - Some(x) => x, - _ => { - return Err(FunctionErrorKind::missing_argument( - "Not possible to get a src mac address.", - )) - } - }; + let local_mac_address = get_local_mac_address(&iface.name)?; let src_ip = match Ipv4Addr::from_str(&local_ip.to_string()) { Ok(x) => x, Err(_) => { - return Err(FunctionErrorKind::missing_argument( + return Err(FnError::missing_argument( "Not possible to parse the src IP address.", )) } @@ -392,7 +382,7 @@ fn nasl_send_arp_request( let dst_ip = match Ipv4Addr::from_str(&target_ip.to_string()) { Ok(x) => x, Err(_) => { - return Err(FunctionErrorKind::missing_argument( + return Err(FnError::missing_argument( "Not possible to parse the dst IP address.", )) } @@ -412,30 +402,26 @@ fn nasl_send_arp_request( fn nasl_get_local_mac_address_from_ip( register: &Register, _: &Context, -) -> Result { +) -> Result { let positional = register.positional(); if positional.is_empty() { - return Err(FunctionErrorKind::MissingPositionalArguments { + return Err(ArgumentError::MissingPositionals { expected: 1, got: 0, - }); + } + .into()); } match &positional[0] { NaslValue::String(x) => { let ip = ipstr2ipaddr(x)?; let iface = get_interface_by_local_ip(ip)?; - match get_local_mac_address(&iface.name) { - Some(mac) => Ok(NaslValue::String(mac.to_string())), - _ => Err(FunctionErrorKind::Diagnostic( - "Not possible to get the local mac address".to_string(), - Some(NaslValue::Null), - )), - } + get_local_mac_address(&iface.name).map(|mac| mac.to_string().into()) } - _ => Err(FunctionErrorKind::WrongArgument( + _ => Err(ArgumentError::WrongArgument( "Expected String containing a valid IP address.".to_string(), - )), + ) + .into()), } } @@ -445,7 +431,7 @@ fn nasl_get_local_mac_address_from_ip( /// - ether_proto: is an int containing the ethernet type (normally given as hexadecimal). /// It is optional and its default value is 0x0800. A list of Types can be e.g. looked up here. /// - payload: is any data, which is then attached as payload to the frame. -fn nasl_forge_frame(register: &Register, _: &Context) -> Result { +fn nasl_forge_frame(register: &Register, _: &Context) -> Result { let src_haddr = validate_mac_address(register.named("src_haddr"))?; let dst_haddr = validate_mac_address(register.named("dst_haddr"))?; let ether_proto = match register.named("ether_proto") { @@ -473,22 +459,17 @@ fn nasl_forge_frame(register: &Register, _: &Context) -> Result Result { +fn nasl_send_frame(register: &Register, context: &Context) -> Result { let frame = match register.named("frame") { Some(ContextType::Value(NaslValue::Data(x))) => x, - _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( - "Data", - "Invalid data type", - )) - } + _ => return Err(FnError::wrong_unnamed_argument("Data", "Invalid data type")), }; let pcap_active = match register.named("pcap_active") { Some(ContextType::Value(NaslValue::Boolean(x))) => x, None => &true, _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( + return Err(FnError::wrong_unnamed_argument( "Boolean", "Invalid pcap_active value", )) @@ -499,7 +480,7 @@ fn nasl_send_frame(register: &Register, context: &Context) -> Result Some(x), None => None, _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( + return Err(FnError::wrong_unnamed_argument( "String", "Invalid pcap_filter value", )) @@ -510,7 +491,7 @@ fn nasl_send_frame(register: &Register, context: &Context) -> Result *x as i32 * 1000i32, // to milliseconds None => DEFAULT_TIMEOUT, _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( + return Err(FnError::wrong_unnamed_argument( "Integer", "Invalid timeout value", )) @@ -532,15 +513,10 @@ fn nasl_send_frame(register: &Register, context: &Context) -> Result Result { +fn nasl_dump_frame(register: &Register, _: &Context) -> Result { let frame: Frame = match register.named("frame") { Some(ContextType::Value(NaslValue::Data(x))) => (x as &[u8]).try_into()?, - _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( - "Data", - "Invalid data type", - )) - } + _ => return Err(FnError::wrong_unnamed_argument("Data", "Invalid data type")), }; info!(frame=%frame); @@ -613,9 +589,9 @@ mod tests { #[test] fn get_local_mac() { if cfg!(target_os = "macos") { - assert_eq!(get_local_mac_address("lo"), None); + assert!(matches!(get_local_mac_address("lo"), Err(_))); } else { - assert_eq!(get_local_mac_address("lo"), Some(MacAddr::zero())); + assert_eq!(get_local_mac_address("lo").unwrap(), MacAddr::zero()); } } } diff --git a/rust/src/nasl/builtin/raw_ip/mod.rs b/rust/src/nasl/builtin/raw_ip/mod.rs index 2d87d2a91..25924f4d4 100644 --- a/rust/src/nasl/builtin/raw_ip/mod.rs +++ b/rust/src/nasl/builtin/raw_ip/mod.rs @@ -5,9 +5,29 @@ mod frame_forgery; mod packet_forgery; mod raw_ip_utils; +use std::io; + use crate::nasl::utils::{IntoFunctionSet, NaslVars, StoredFunctionSet}; use frame_forgery::FrameForgery; use packet_forgery::PacketForgery; +pub use packet_forgery::PacketForgeryError; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum RawIpError { + #[error("Failed to get local MAC address.")] + FailedToGetLocalMacAddress, + #[error("Failed to get device list.")] + FailedToGetDeviceList, + #[error("Invalid IP address.")] + InvalidIpAddress, + #[error("Failed to bind.")] + FailedToBind(io::Error), + #[error("No route to destination.")] + NoRouteToDestination, + #[error("{0}")] + PacketForgery(PacketForgeryError), +} pub struct RawIp; diff --git a/rust/src/nasl/builtin/raw_ip/packet_forgery.rs b/rust/src/nasl/builtin/raw_ip/packet_forgery.rs index 71decc73a..f1bcf043c 100644 --- a/rust/src/nasl/builtin/raw_ip/packet_forgery.rs +++ b/rust/src/nasl/builtin/raw_ip/packet_forgery.rs @@ -9,7 +9,10 @@ use std::{ str::FromStr, }; -use super::raw_ip_utils::{get_interface_by_local_ip, get_source_ip, islocalhost}; +use super::{ + raw_ip_utils::{get_interface_by_local_ip, get_source_ip, islocalhost}, + RawIpError, +}; use super::super::host::get_host_ip; use crate::nasl::builtin::misc::random_impl; @@ -32,14 +35,34 @@ use pnet::packet::{ use pnet_macros_support::types::u9be; use socket2::{Domain, Protocol, Socket}; +use thiserror::Error; use tracing::debug; +#[derive(Debug, Error)] +pub enum PacketForgeryError { + #[error("{0}")] + Custom(String), + #[error("Failed to parse socket address. {0}")] + ParseSocketAddr(std::net::AddrParseError), + #[error("Failed to send packet. {0}")] + SendPacket(std::io::Error), + #[error("Failed to create packet from buffer.")] + CreatePacket, +} + +impl From for FnError { + fn from(e: PacketForgeryError) -> Self { + RawIpError::PacketForgery(e).into() + } +} + +fn error(s: String) -> FnError { + PacketForgeryError::Custom(s).into() +} + macro_rules! custom_error { ($a:expr, $b:expr) => { - Err(FunctionErrorKind::Diagnostic( - format!($a, $b), - Some(NaslValue::Null), - )) + Err(RawIpError::PacketForgery(PacketForgeryError::Custom(format!($a, $b))).into()) }; } @@ -88,7 +111,7 @@ fn safe_copy_from_slice( o_buf: &[u8], o_init: usize, o_fin: usize, -) -> Result<(), FunctionErrorKind> { +) -> Result<(), FnError> { let o_range = o_fin - o_init; let d_range = d_fin - d_init; if d_buf.len() < d_range @@ -97,7 +120,7 @@ fn safe_copy_from_slice( || d_buf.len() < d_fin || o_buf.len() < o_fin { - return Err(FunctionErrorKind::Dirty( + return Err(error( "Error copying from slice. Index out of range".to_string(), )); } @@ -121,14 +144,15 @@ fn safe_copy_from_slice( /// - ip_v is: the IP version. 4 by default. /// /// Returns the IP datagram or NULL on error. -fn forge_ip_packet(register: &Register, configs: &Context) -> Result { +fn forge_ip_packet(register: &Register, configs: &Context) -> Result { let dst_addr = get_host_ip(configs)?; if dst_addr.is_ipv6() { - return Err(FunctionErrorKind::WrongArgument( + return Err(ArgumentError::WrongArgument( "forge_ip_packet: No valid dst_addr could be determined via call to get_host_ip()" .to_string(), - )); + ) + .into()); } let data = match register.named("data") { @@ -140,12 +164,8 @@ fn forge_ip_packet(register: &Register, configs: &Context) -> Result Result { - return Err(FunctionErrorKind::WrongArgument(format!( - "Invalid ip_src: {}", - e - ))); + return Err( + ArgumentError::WrongArgument(format!("Invalid ip_src: {}", e)).into(), + ); } }; x.to_string() @@ -220,10 +239,9 @@ fn forge_ip_packet(register: &Register, configs: &Context) -> Result { - return Err(FunctionErrorKind::WrongArgument(format!( - "Invalid ip_dst: {}", - e - ))); + return Err( + ArgumentError::WrongArgument(format!("Invalid ip_dst: {}", e)).into(), + ); } }; x.to_string() @@ -234,10 +252,7 @@ fn forge_ip_packet(register: &Register, configs: &Context) -> Result { - return Err(FunctionErrorKind::WrongArgument(format!( - "Invalid ip: {}", - e - ))); + return Err(ArgumentError::WrongArgument(format!("Invalid ip: {}", e)).into()); } }; dst_addr.to_string() @@ -268,23 +283,16 @@ fn forge_ip_packet(register: &Register, configs: &Context) -> Result Result { +fn set_ip_elements(register: &Register, _configs: &Context) -> Result { let mut buf = match register.named("ip") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(FunctionErrorKind::missing_argument("ip")); + return Err(FnError::missing_argument("ip")); } }; - let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut buf).ok_or_else(|| { - FunctionErrorKind::Diagnostic( - "No possible to create a packet from buffer".to_string(), - None, - ) - })?; + let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut buf) + .ok_or_else(|| PacketForgeryError::CreatePacket)?; let ip_hl = match register.named("ip_hl") { Some(ContextType::Value(NaslValue::Number(x))) => *x as u8, @@ -334,10 +342,7 @@ fn set_ip_elements( pkt.set_source(ip); } Err(e) => { - return Err(FunctionErrorKind::WrongArgument(format!( - "Invalid ip_src: {}", - e - ))); + return Err(ArgumentError::WrongArgument(format!("Invalid ip_src: {}", e)).into()); } }; }; @@ -367,17 +372,16 @@ fn set_ip_elements( /// - ip_sum /// - ip_src /// - ip_dst -fn get_ip_element(register: &Register, _configs: &Context) -> Result { +fn get_ip_element(register: &Register, _configs: &Context) -> Result { let buf = match register.named("ip") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(FunctionErrorKind::missing_argument("ip")); + return Err(FnError::missing_argument("ip")); } }; - let pkt = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let pkt = packet::ipv4::Ipv4Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; match register.named("element") { Some(ContextType::Value(NaslValue::String(e))) => match e.as_str() { @@ -392,32 +396,28 @@ fn get_ip_element(register: &Register, _configs: &Context) -> Result Ok(NaslValue::Number(pkt.get_checksum() as i64)), "ip_src" => Ok(NaslValue::String(pkt.get_source().to_string())), "ip_dst" => Ok(NaslValue::String(pkt.get_destination().to_string())), - _ => Err(FunctionErrorKind::WrongArgument( - "Invalid element".to_string(), - )), + _ => Err(ArgumentError::WrongArgument("Invalid element".to_string()).into()), }, - _ => Err(FunctionErrorKind::missing_argument("element")), + _ => Err(FnError::missing_argument("element")), } } /// Receive a list of IP packets and print them in a readable format in the screen. -fn dump_ip_packet(register: &Register, _: &Context) -> Result { +fn dump_ip_packet(register: &Register, _: &Context) -> Result { let positional = register.positional(); if positional.is_empty() { - return Err(FunctionErrorKind::MissingPositionalArguments { + return Err(ArgumentError::MissingPositionals { expected: 1, got: 0, - }); + } + .into()); } for ip in positional.iter() { match ip { NaslValue::Data(data) => { - let pkt = packet::ipv4::Ipv4Packet::new(data).ok_or_else(|| { - FunctionErrorKind::Dirty( - "No possible to create a packet from buffer".to_string(), - ) - })?; + let pkt = packet::ipv4::Ipv4Packet::new(data) + .ok_or_else(|| PacketForgeryError::CreatePacket)?; println!("\tip_hl={}", pkt.get_header_length()); println!("\tip_v={}", pkt.get_version()); @@ -433,9 +433,7 @@ fn dump_ip_packet(register: &Register, _: &Context) -> Result { - return Err(FunctionErrorKind::WrongArgument( - "Invalid ip packet".to_string(), - )); + return Err(ArgumentError::WrongArgument("Invalid ip packet".to_string()).into()); } } } @@ -460,34 +458,31 @@ fn dump_protocol(pkt: &Ipv4Packet) -> String { /// - code: is the identifier of the option to add /// - length: is the length of the option data /// - value: is the option data -fn insert_ip_options( - register: &Register, - _configs: &Context, -) -> Result { +fn insert_ip_options(register: &Register, _configs: &Context) -> Result { let buf = match register.named("ip") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(FunctionErrorKind::missing_argument("ip")); + return Err(FnError::missing_argument("ip")); } }; let code = match register.named("code") { Some(ContextType::Value(NaslValue::Number(x))) => *x, _ => { - return Err(FunctionErrorKind::missing_argument("code")); + return Err(FnError::missing_argument("code")); } }; let length = match register.named("length") { Some(ContextType::Value(NaslValue::Number(x))) => *x as usize, _ => { - return Err(FunctionErrorKind::missing_argument("length")); + return Err(FnError::missing_argument("length")); } }; let value = match register.named("value") { Some(ContextType::Value(NaslValue::String(x))) => x.as_bytes(), Some(ContextType::Value(NaslValue::Data(x))) => x, _ => { - return Err(FunctionErrorKind::missing_argument("value")); + return Err(FnError::missing_argument("value")); } }; @@ -531,9 +526,8 @@ fn insert_ip_options( opt_buf.len(), )?; - let mut new_pkt = MutableIpv4Packet::new(&mut new_buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let mut new_pkt = MutableIpv4Packet::new(&mut new_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; let checksum = checksum(&new_pkt.to_immutable()); new_pkt.set_checksum(checksum); new_pkt.set_header_length((hl / 4) as u8); @@ -557,15 +551,12 @@ fn insert_ip_options( /// - update_ip_len: is a flag (TRUE by default). If set, NASL will recompute the size field of the IP datagram. /// /// The modified IP datagram or NULL on error. -fn forge_tcp_packet( - register: &Register, - _configs: &Context, -) -> Result { +fn forge_tcp_packet(register: &Register, _configs: &Context) -> Result { let mut ip_buf = match register.named("ip") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { // Missingarguments - return Err(FunctionErrorKind::missing_argument("ip")); + return Err(FnError::missing_argument("ip")); } }; let original_ip_len = ip_buf.len(); @@ -580,9 +571,8 @@ fn forge_tcp_packet( //tcp length + data length let total_length = 20 + data.len(); let mut buf = vec![0; total_length]; - let mut tcp_seg = packet::tcp::MutableTcpPacket::new(&mut buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let mut tcp_seg = packet::tcp::MutableTcpPacket::new(&mut buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; if !data.is_empty() { tcp_seg.set_payload(&data); @@ -629,26 +619,22 @@ fn forge_tcp_packet( let chksum = match register.named("th_sum") { Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), _ => { - let pkt = packet::ipv4::Ipv4Packet::new(&ip_buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; - let tcp_aux = TcpPacket::new(tcp_seg.packet()).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let pkt = packet::ipv4::Ipv4Packet::new(&ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let tcp_aux = TcpPacket::new(tcp_seg.packet()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pnet::packet::tcp::ipv4_checksum(&tcp_aux, &pkt.get_source(), &pkt.get_destination()) } }; - let mut tcp_seg = packet::tcp::MutableTcpPacket::new(&mut buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let mut tcp_seg = packet::tcp::MutableTcpPacket::new(&mut buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; tcp_seg.set_checksum(chksum); ip_buf.append(&mut buf); let l = ip_buf.len(); - let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut ip_buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pkt.set_total_length(l as u16); match register.named("update_ip_len") { Some(ContextType::Value(NaslValue::Boolean(l))) if !(*l) => { @@ -680,23 +666,18 @@ fn forge_tcp_packet( /// - data /// /// Returns an TCP element from a IP datagram. -fn get_tcp_element( - register: &Register, - _configs: &Context, -) -> Result { +fn get_tcp_element(register: &Register, _configs: &Context) -> Result { let buf = match register.named("tcp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(FunctionErrorKind::missing_argument("tcp")); + return Err(FnError::missing_argument("tcp")); } }; - let ip = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; - let tcp = packet::tcp::TcpPacket::new(ip.payload()).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let ip = packet::ipv4::Ipv4Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let tcp = packet::tcp::TcpPacket::new(ip.payload()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; match register.named("element") { Some(ContextType::Value(NaslValue::String(el))) => match el.as_str() { @@ -711,9 +692,9 @@ fn get_tcp_element( "th_sum" => Ok(NaslValue::Number(tcp.get_checksum() as i64)), "th_urp" => Ok(NaslValue::Number(tcp.get_urgent_ptr() as i64)), "th_data" => Ok(NaslValue::Data(tcp.payload().to_vec())), - _ => Err(FunctionErrorKind::WrongArgument("element".to_string())), + _ => Err(ArgumentError::WrongArgument("element".to_string()).into()), }, - _ => Err(FunctionErrorKind::missing_argument("element")), + _ => Err(FnError::missing_argument("element")), } } @@ -728,20 +709,18 @@ fn get_tcp_element( /// - 8: TCPOPT_TIMESTAMP, 8 bytes value for timestamp and echo timestamp, 4 bytes each one. /// /// The returned option depends on the given *option* parameter. It is either an int for option 2, 3 and 4 or an array containing the two values for option 8. -fn get_tcp_option(register: &Register, _configs: &Context) -> Result { +fn get_tcp_option(register: &Register, _configs: &Context) -> Result { let buf = match register.named("tcp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(FunctionErrorKind::missing_argument("tcp")); + return Err(FnError::missing_argument("tcp")); } }; - let ip = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; - let tcp = packet::tcp::TcpPacket::new(ip.payload()).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let ip = packet::ipv4::Ipv4Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let tcp = packet::tcp::TcpPacket::new(ip.payload()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; let mut max_seg: i64 = 0; let mut window: i64 = 0; @@ -783,11 +762,9 @@ fn get_tcp_option(register: &Register, _configs: &Context) -> Result Ok(NaslValue::Number(window)), 4 => Ok(NaslValue::Number(sack_permitted)), 8 => Ok(NaslValue::Array(timestamps)), - _ => Err(FunctionErrorKind::WrongArgument( - "Invalid option".to_string(), - )), + _ => Err(ArgumentError::WrongArgument("Invalid option".to_string()).into()), }, - _ => Err(FunctionErrorKind::missing_argument("option")), + _ => Err(FnError::missing_argument("option")), } } @@ -806,20 +783,16 @@ fn get_tcp_option(register: &Register, _configs: &Context) -> Result Result { +fn set_tcp_elements(register: &Register, _configs: &Context) -> Result { let buf = match register.named("tcp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(FunctionErrorKind::missing_argument("tcp")); + return Err(FnError::missing_argument("tcp")); } }; - let ip = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let ip = packet::ipv4::Ipv4Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; let iph_len = ip.get_header_length() as usize * 4; // the header length is given in 32-bits words let data = match register.named("data") { @@ -840,9 +813,8 @@ fn set_tcp_elements( new_buf = vec![0u8; tcp_total_length]; //new_buf[..20].copy_from_slice(&ori_tcp_buf[..20]); safe_copy_from_slice(&mut new_buf[..], 0, 20, &ori_tcp_buf, 0, 20)?; - ori_tcp = packet::tcp::MutableTcpPacket::new(&mut new_buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + ori_tcp = packet::tcp::MutableTcpPacket::new(&mut new_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; ori_tcp.set_payload(&data); } else { // Copy the original tcp buffer into the new buffer @@ -857,9 +829,8 @@ fn set_tcp_elements( 0, ori_tcp_buf.len(), )?; - ori_tcp = packet::tcp::MutableTcpPacket::new(&mut new_buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + ori_tcp = packet::tcp::MutableTcpPacket::new(&mut new_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; } if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("th_sport") { @@ -895,12 +866,10 @@ fn set_tcp_elements( let chksum = match register.named("th_sum") { Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), _ => { - let pkt = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; - let tcp_aux = TcpPacket::new(ori_tcp.packet()).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let pkt = packet::ipv4::Ipv4Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let tcp_aux = TcpPacket::new(ori_tcp.packet()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pnet::packet::tcp::ipv4_checksum(&tcp_aux, &pkt.get_source(), &pkt.get_destination()) } }; @@ -919,9 +888,8 @@ fn set_tcp_elements( new_ip_buf.append(&mut fin_tcp_buf.to_vec()); let l = new_ip_buf.len(); - let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut new_ip_buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut new_ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; // pnet will panic if the total length set in the ip datagram field does not much with the total length. // Therefore, the total length is set to the right one before setting the payload. @@ -951,22 +919,16 @@ fn set_tcp_elements( /// - 3: TCPOPT_WINDOW, with values between 0 and 14 /// - 4: TCPOPT_SACK_PERMITTED, no value required. /// - 8: TCPOPT_TIMESTAMP, 8 bytes value for timestamp and echo timestamp, 4 bytes each one. -fn insert_tcp_options( - register: &Register, - _configs: &Context, -) -> Result { +fn insert_tcp_options(register: &Register, _configs: &Context) -> Result { let buf = match register.named("tcp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(FunctionErrorKind::Dirty( - "insert_tcp_options: missing field".to_string(), - )); + return Err(error("insert_tcp_options: missing field".to_string())); } }; - let ip = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let ip = packet::ipv4::Ipv4Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; let iph_len = ip.get_header_length() as usize * 4; // the header length is given in 32-bits words let ori_tcp_buf = <&[u8]>::clone(&ip.payload()).to_owned(); let mut ori_tcp: packet::tcp::MutableTcpPacket; @@ -977,9 +939,8 @@ fn insert_tcp_options( Some(ContextType::Value(NaslValue::String(d))) => d.as_bytes().to_vec(), Some(ContextType::Value(NaslValue::Number(d))) => d.to_be_bytes().to_vec(), _ => { - let tcp = TcpPacket::new(&ori_tcp_buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let tcp = TcpPacket::new(&ori_tcp_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; tcp.payload().to_vec() } }; @@ -987,7 +948,7 @@ fn insert_tcp_options( // Forge the options field let positional = register.positional(); if positional.is_empty() { - return Err(FunctionErrorKind::Dirty( + return Err(error( "Missing optional arguments. At least one optional porsitional argument followed by its value must be given".to_string() )); } @@ -1003,9 +964,10 @@ fn insert_tcp_options( opts.push(TcpOption::mss(v)); opts_len += 4; } else { - return Err(FunctionErrorKind::WrongArgument( + return Err(ArgumentError::WrongArgument( "Invalid value for tcp option TCPOPT_MAXSEG".to_string(), - )); + ) + .into()); } } Some(NaslValue::Number(o)) if *o == 3 => { @@ -1014,9 +976,10 @@ fn insert_tcp_options( opts.push(TcpOption::wscale(v)); opts_len += 3; } else { - return Err(FunctionErrorKind::WrongArgument( + return Err(ArgumentError::WrongArgument( "Invalid value for tcp option TCPOPT_WINDOW".to_string(), - )); + ) + .into()); } } @@ -1032,21 +995,24 @@ fn insert_tcp_options( opts.push(TcpOption::timestamp(v1, v2)); opts_len += 10; } else { - return Err(FunctionErrorKind::WrongArgument( + return Err(ArgumentError::WrongArgument( "Invalid value for tcp option TCPOPT_TIMESTAMP".to_string(), - )); + ) + .into()); } } else { - return Err(FunctionErrorKind::WrongArgument( + return Err(ArgumentError::WrongArgument( "Invalid value for tcp option TCPOPT_TIMESTAMP".to_string(), - )); + ) + .into()); } } None => break, _ => { - return Err(FunctionErrorKind::WrongArgument( + return Err(ArgumentError::WrongArgument( "insert_tcp_options: invalid tcp option".to_string(), - )); + ) + .into()); } } } @@ -1071,9 +1037,8 @@ fn insert_tcp_options( //new_buf[..20].copy_from_slice(&ori_tcp_buf[..20]); safe_copy_from_slice(&mut new_buf[..], 0, 20, &ori_tcp_buf, 0, 20)?; - ori_tcp = packet::tcp::MutableTcpPacket::new(&mut new_buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + ori_tcp = packet::tcp::MutableTcpPacket::new(&mut new_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; // At this point, opts len is a 4bytes multiple and the offset is expressed in 32bits words ori_tcp.set_data_offset(5 + opts_len as u8 / 4); @@ -1088,12 +1053,10 @@ fn insert_tcp_options( let chksum = match register.named("th_sum") { Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), _ => { - let pkt = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; - let tcp_aux = TcpPacket::new(ori_tcp.packet()).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let pkt = packet::ipv4::Ipv4Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let tcp_aux = TcpPacket::new(ori_tcp.packet()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pnet::packet::tcp::ipv4_checksum(&tcp_aux, &pkt.get_source(), &pkt.get_destination()) } }; @@ -1111,9 +1074,8 @@ fn insert_tcp_options( new_ip_buf.append(&mut fin_tcp_buf.to_vec()); let l = new_ip_buf.len(); - let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut new_ip_buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut new_ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; // pnet will panic if the total length set in the ip datagram field does not much with the total length. // Therefore, the total length is set to the right one before setting the payload. @@ -1136,10 +1098,10 @@ fn insert_tcp_options( } /// Receive a list of IPv4 datagrams and print their TCP part in a readable format in the screen. -fn dump_tcp_packet(register: &Register, _: &Context) -> Result { +fn dump_tcp_packet(register: &Register, _: &Context) -> Result { let positional = register.positional(); if positional.is_empty() { - return Err(FunctionErrorKind::Dirty( + return Err(error( "Missing arguments. It needs at least one tcp packet".to_string(), )); } @@ -1150,9 +1112,9 @@ fn dump_tcp_packet(register: &Register, _: &Context) -> Result ip, None => { - return Err(FunctionErrorKind::WrongArgument( - "Invalid TCP packet".to_string(), - )); + return Err( + ArgumentError::WrongArgument("Invalid TCP packet".to_string()).into(), + ); } }; @@ -1174,16 +1136,14 @@ fn dump_tcp_packet(register: &Register, _: &Context) -> Result { - return Err(FunctionErrorKind::WrongArgument( - "Invalid TCP packet".to_string(), - )); + return Err( + ArgumentError::WrongArgument("Invalid TCP packet".to_string()).into(), + ); } } } _ => { - return Err(FunctionErrorKind::WrongArgument( - "Invalid ip packet".to_string(), - )); + return Err(ArgumentError::WrongArgument("Invalid ip packet".to_string()).into()); } } } @@ -1240,13 +1200,10 @@ fn format_flags(pkt: &TcpPacket) -> String { /// - update_ip_len: is a flag (TRUE by default). If set, NASL will recompute the size field of the IP datagram. /// /// Returns the modified IP datagram or NULL on error. -fn forge_udp_packet( - register: &Register, - _configs: &Context, -) -> Result { +fn forge_udp_packet(register: &Register, _configs: &Context) -> Result { let mut ip_buf = match register.named("ip") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), - _ => return Err(FunctionErrorKind::missing_argument("ip")), + _ => return Err(FnError::missing_argument("ip")), }; let original_ip_len = ip_buf.len(); @@ -1260,9 +1217,8 @@ fn forge_udp_packet( //udp length + data length let total_length = 8 + data.len(); let mut buf = vec![0; total_length]; - let mut udp_datagram = packet::udp::MutableUdpPacket::new(&mut buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let mut udp_datagram = packet::udp::MutableUdpPacket::new(&mut buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; if !data.is_empty() { udp_datagram.set_payload(&data); @@ -1285,26 +1241,22 @@ fn forge_udp_packet( let chksum = match register.named("th_sum") { Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), _ => { - let pkt = packet::ipv4::Ipv4Packet::new(&ip_buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; - let udp_aux = UdpPacket::new(udp_datagram.packet()).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let pkt = packet::ipv4::Ipv4Packet::new(&ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let udp_aux = UdpPacket::new(udp_datagram.packet()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pnet::packet::udp::ipv4_checksum(&udp_aux, &pkt.get_source(), &pkt.get_destination()) } }; - let mut udp_datagram = packet::udp::MutableUdpPacket::new(&mut buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let mut udp_datagram = packet::udp::MutableUdpPacket::new(&mut buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; udp_datagram.set_checksum(chksum); ip_buf.append(&mut buf); let l = ip_buf.len(); - let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut ip_buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pkt.set_total_length(l as u16); match register.named("update_ip_len") { Some(ContextType::Value(NaslValue::Boolean(l))) if !(*l) => { @@ -1326,20 +1278,16 @@ fn forge_udp_packet( /// - uh_sport: is the source port. NASL will convert it into network order if necessary. 0 by default. /// - uh_sum: is the UDP checksum. Although it is not compulsory, the right value is computed by default. /// - uh_ulen: is the data length. By default it is set to the length the data argument plus the size of the UDP header. -fn set_udp_elements( - register: &Register, - _configs: &Context, -) -> Result { +fn set_udp_elements(register: &Register, _configs: &Context) -> Result { let buf = match register.named("udp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(FunctionErrorKind::missing_argument("udp")); + return Err(FnError::missing_argument("udp")); } }; - let ip = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let ip = packet::ipv4::Ipv4Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; let iph_len = ip.get_header_length() as usize * 4; // the header length is given in 32-bits words let data = match register.named("data") { @@ -1361,9 +1309,8 @@ fn set_udp_elements( //new_buf[..8].copy_from_slice(&ori_udp_buf[..8]); safe_copy_from_slice(&mut new_buf[..], 0, 8, &ori_udp_buf, 0, 8)?; - ori_udp = packet::udp::MutableUdpPacket::new(&mut new_buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + ori_udp = packet::udp::MutableUdpPacket::new(&mut new_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; ori_udp.set_payload(&data); } else { // Copy the original udp buffer into the new buffer @@ -1378,9 +1325,8 @@ fn set_udp_elements( 0, ori_udp_buf.len(), )?; - ori_udp = packet::udp::MutableUdpPacket::new(&mut new_buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + ori_udp = packet::udp::MutableUdpPacket::new(&mut new_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; } if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("uh_sport") { @@ -1398,12 +1344,10 @@ fn set_udp_elements( let chksum = match register.named("uh_sum") { Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), _ => { - let pkt = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; - let udp_aux = UdpPacket::new(ori_udp.packet()).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let pkt = packet::ipv4::Ipv4Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let udp_aux = UdpPacket::new(ori_udp.packet()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pnet::packet::udp::ipv4_checksum(&udp_aux, &pkt.get_source(), &pkt.get_destination()) } }; @@ -1421,9 +1365,8 @@ fn set_udp_elements( new_ip_buf.append(&mut fin_udp_buf.to_vec()); let l = new_ip_buf.len(); - let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut new_ip_buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut new_ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pkt.set_total_length(l as u16); pkt.set_payload(&fin_udp_buf); @@ -1436,18 +1379,15 @@ fn set_udp_elements( } /// Receive a list of IPv4 datagrams and print their UDP part in a readable format in the screen. -fn dump_udp_packet(register: &Register, _: &Context) -> Result { +fn dump_udp_packet(register: &Register, _: &Context) -> Result { let positional = register.positional(); if positional.is_empty() { - return Err(FunctionErrorKind::Dirty( + return Err(error( "Missing arguments. It needs at least one UDP packet".to_string(), )); } - let invalid_udp_packet_error = || { - Err(FunctionErrorKind::WrongArgument( - "Invalid UDP packet".to_string(), - )) - }; + let invalid_udp_packet_error = + || Err(ArgumentError::WrongArgument("Invalid UDP packet".to_string()).into()); for udp_datagram in positional.iter() { match udp_datagram { @@ -1491,23 +1431,18 @@ fn dump_udp_packet(register: &Register, _: &Context) -> Result Result { +fn get_udp_element(register: &Register, _configs: &Context) -> Result { let buf = match register.named("udp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(FunctionErrorKind::missing_argument("udp")); + return Err(FnError::missing_argument("udp")); } }; - let ip = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; - let udp = packet::udp::UdpPacket::new(ip.payload()).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let ip = packet::ipv4::Ipv4Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let udp = packet::udp::UdpPacket::new(ip.payload()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; match register.named("element") { Some(ContextType::Value(NaslValue::String(el))) => match el.as_str() { @@ -1516,9 +1451,9 @@ fn get_udp_element( "uh_len" => Ok(NaslValue::Number(udp.get_length() as i64)), "uh_sum" => Ok(NaslValue::Number(udp.get_checksum() as i64)), "data" => Ok(NaslValue::Data(udp.payload().to_vec())), - _ => Err(FunctionErrorKind::WrongArgument("element".to_string())), + _ => Err(ArgumentError::WrongArgument("element".to_string()).into()), }, - _ => Err(FunctionErrorKind::WrongArgument("element".to_string())), + _ => Err(ArgumentError::WrongArgument("element".to_string()).into()), } } @@ -1531,14 +1466,11 @@ fn get_udp_element( /// - *icmp_seq*: ICMP sequence number. /// - *icmp_type*: ICMP type. 0 by default. /// - *update_ip_len*: If this flag is set, NASL will recompute the size field of the IP datagram. Default: True. -fn forge_icmp_packet( - register: &Register, - _configs: &Context, -) -> Result { +fn forge_icmp_packet(register: &Register, _configs: &Context) -> Result { let mut ip_buf = match register.named("ip") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(FunctionErrorKind::missing_argument("icmp")); + return Err(FnError::missing_argument("icmp")); } }; let original_ip_len = ip_buf.len(); @@ -1552,9 +1484,8 @@ fn forge_icmp_packet( let total_length = 8 + data.len(); let mut buf = vec![0; total_length]; - let mut icmp_pkt = packet::icmp::MutableIcmpPacket::new(&mut buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let mut icmp_pkt = packet::icmp::MutableIcmpPacket::new(&mut buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; match register.named("icmp_type") { Some(ContextType::Value(NaslValue::Number(x))) => { @@ -1588,23 +1519,20 @@ fn forge_icmp_packet( let chksum = match register.named("icmp_cksum") { Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), _ => { - let icmp_aux = IcmpPacket::new(&buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let icmp_aux = IcmpPacket::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pnet::packet::icmp::checksum(&icmp_aux) } }; - let mut icmp_pkt = packet::icmp::MutableIcmpPacket::new(&mut buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let mut icmp_pkt = packet::icmp::MutableIcmpPacket::new(&mut buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; icmp_pkt.set_checksum(chksum); ip_buf.append(&mut buf); let l = ip_buf.len(); - let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut ip_buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pkt.set_total_length(l as u16); match register.named("update_ip_len") { Some(ContextType::Value(NaslValue::Boolean(l))) if !(*l) => { @@ -1629,23 +1557,18 @@ fn forge_icmp_packet( /// - icmp_seq /// - icmp_chsum /// - icmp_data -fn get_icmp_element( - register: &Register, - _configs: &Context, -) -> Result { +fn get_icmp_element(register: &Register, _configs: &Context) -> Result { let buf = match register.named("icmp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(FunctionErrorKind::missing_argument("icmp")); + return Err(FnError::missing_argument("icmp")); } }; - let ip = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; - let icmp = packet::icmp::IcmpPacket::new(ip.payload()).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let ip = packet::ipv4::Ipv4Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let icmp = packet::icmp::IcmpPacket::new(ip.payload()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; match register.named("element") { Some(ContextType::Value(NaslValue::String(el))) => match el.as_str() { @@ -1685,15 +1608,15 @@ fn get_icmp_element( } _ => Ok(NaslValue::Null), }, - _ => Err(FunctionErrorKind::missing_argument("element")), + _ => Err(FnError::missing_argument("element")), } } /// Receive a list of IPv4 ICMP packets and print them in a readable format in the screen. -fn dump_icmp_packet(register: &Register, _: &Context) -> Result { +fn dump_icmp_packet(register: &Register, _: &Context) -> Result { let positional = register.positional(); if positional.is_empty() { - return Err(FunctionErrorKind::missing_argument("icmp")); + return Err(FnError::missing_argument("icmp")); } for icmp_pkt in positional.iter() { @@ -1704,12 +1627,10 @@ fn dump_icmp_packet(register: &Register, _: &Context) -> Result= 4 { @@ -1818,14 +1739,11 @@ pub mod igmp { /// - group: IGMP group /// - type: IGMP type. 0 by default. /// - update_ip_len: If this flag is set, NASL will recompute the size field of the IP datagram. Default: True. -fn forge_igmp_packet( - register: &Register, - _configs: &Context, -) -> Result { +fn forge_igmp_packet(register: &Register, _configs: &Context) -> Result { let mut ip_buf = match register.named("ip") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(FunctionErrorKind::missing_argument("igmp")); + return Err(FnError::missing_argument("igmp")); } }; let original_ip_len = ip_buf.len(); @@ -1839,9 +1757,8 @@ fn forge_igmp_packet( let total_length = 8 + data.len(); let mut buf = vec![0; total_length]; - let mut igmp_pkt = igmp::MutableIgmpPacket::new(&mut buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let mut igmp_pkt = igmp::MutableIgmpPacket::new(&mut buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; match register.named("type") { Some(ContextType::Value(NaslValue::Number(x))) => { @@ -1863,10 +1780,7 @@ fn forge_igmp_packet( igmp_pkt.set_group_address(ip); } Err(e) => { - return Err(FunctionErrorKind::Dirty(format!( - "Invalid address group: {}", - e - ))); + return Err(error(format!("Invalid address group: {}", e))); } }; } @@ -1877,24 +1791,18 @@ fn forge_igmp_packet( igmp_pkt.set_payload(&data); } - let igmp_aux = igmp::IgmpPacket::new(&buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let igmp_aux = igmp::IgmpPacket::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; let cksum = igmp::checksum(&igmp_aux); - let mut icmp_pkt = packet::icmp::MutableIcmpPacket::new(&mut buf).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let mut icmp_pkt = packet::icmp::MutableIcmpPacket::new(&mut buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; icmp_pkt.set_checksum(cksum); ip_buf.append(&mut buf); let l = ip_buf.len(); - let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut ip_buf).ok_or_else(|| { - FunctionErrorKind::Diagnostic( - "No possible to create a packet from buffer".to_string(), - Some(NaslValue::Null), - ) - })?; + let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut ip_buf) + .ok_or_else(|| PacketForgeryError::CreatePacket)?; pkt.set_total_length(l as u16); match register.named("update_ip_len") { Some(ContextType::Value(NaslValue::Boolean(l))) if !(*l) => { @@ -1908,17 +1816,14 @@ fn forge_igmp_packet( Ok(NaslValue::Data(ip_buf)) } -fn new_raw_socket() -> Result { +fn new_raw_socket() -> Result { match Socket::new_raw( Domain::IPV4, socket2::Type::RAW, Some(Protocol::from(IPPROTO_RAW)), ) { Ok(s) => Ok(s), - Err(e) => Err(FunctionErrorKind::Dirty(format!( - "Not possible to create a raw socket: {}", - e - ))), + Err(e) => Err(error(format!("Not possible to create a raw socket: {}", e))), } } @@ -1926,7 +1831,7 @@ fn new_raw_socket() -> Result { /// /// Its argument is: /// - port: port for the ping -fn nasl_tcp_ping(register: &Register, configs: &Context) -> Result { +fn nasl_tcp_ping(register: &Register, configs: &Context) -> Result { let rnd_tcp_port = || -> u16 { (random_impl().unwrap_or(0) % 65535 + 1024) as u16 }; let sports_ori: Vec = vec![ @@ -1948,10 +1853,7 @@ fn nasl_tcp_ping(register: &Register, configs: &Context) -> Result Result *x, None => 0, //TODO: implement plug_get_host_open_port() _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( + return Err(FnError::wrong_unnamed_argument( "Number", "Invalid length value", )) @@ -1984,9 +1886,8 @@ fn nasl_tcp_ping(register: &Register, configs: &Context) -> Result Result Result { - return Err(FunctionErrorKind::Dirty(format!("send_packet: {}", e))); + return Err(error(format!("send_packet: {}", e))); } } @@ -2067,15 +1967,12 @@ fn nasl_tcp_ping(register: &Register, configs: &Context) -> Result Result { +fn nasl_send_packet(register: &Register, configs: &Context) -> Result { let use_pcap = match register.named("pcap_active") { Some(ContextType::Value(NaslValue::Boolean(x))) => *x, None => true, _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( + return Err(FnError::wrong_unnamed_argument( "Boolean", "Invalid pcap_active value", )) @@ -2086,7 +1983,7 @@ fn nasl_send_packet( Some(ContextType::Value(NaslValue::String(x))) => x.to_string(), None => String::new(), _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( + return Err(FnError::wrong_unnamed_argument( "String", "Invalid pcap_filter value", )) @@ -2097,7 +1994,7 @@ fn nasl_send_packet( Some(ContextType::Value(NaslValue::Number(x))) => *x as i32 * 1000i32, // to milliseconds None => DEFAULT_TIMEOUT, _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( + return Err(FnError::wrong_unnamed_argument( "Integer", "Invalid timeout value", )) @@ -2108,7 +2005,7 @@ fn nasl_send_packet( Some(ContextType::Value(NaslValue::Boolean(x))) => *x, None => false, _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( + return Err(FnError::wrong_unnamed_argument( "Boolean", "Invalid allow_broadcast value", )) @@ -2123,17 +2020,14 @@ fn nasl_send_packet( let soc = new_raw_socket()?; if let Err(e) = soc.set_header_included(true) { - return Err(FunctionErrorKind::Dirty(format!( - "Not possible to create a raw socket: {}", - e - ))); + return Err(error(format!("Not possible to create a raw socket: {}", e))); }; let _dflt_packet_sz = match register.named("length") { Some(ContextType::Value(NaslValue::Number(x))) => *x, None => 0, _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( + return Err(FnError::wrong_unnamed_argument( "Number", "Invalid length value", )) @@ -2156,16 +2050,10 @@ fn nasl_send_packet( for pkt in positional.iter() { let packet_raw = match pkt { NaslValue::Data(data) => data as &[u8], - _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( - "Data", - "Invalid packet", - )) - } + _ => return Err(FnError::wrong_unnamed_argument("Data", "Invalid packet")), }; - let packet = packet::ipv4::Ipv4Packet::new(packet_raw).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let packet = packet::ipv4::Ipv4Packet::new(packet_raw) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; if allow_broadcast { if let Err(err) = soc.set_broadcast(true) { @@ -2178,32 +2066,23 @@ fn nasl_send_packet( // No broadcast destination and dst ip address inside the IP packet // differs from target IP, is consider a malicious or buggy script. if packet.get_destination() != target_ip && !allow_broadcast { - return Err(FunctionErrorKind::Dirty( + return Err(error( format!("send_packet: malicious or buggy script is trying to send packet to {} instead of designated target {}", packet.get_destination(), target_ip) )); } let sock_str = format!("{}:{}", &packet.get_destination().to_string().as_str(), 0); - let sockaddr = match SocketAddr::from_str(&sock_str) { - Ok(addr) => socket2::SockAddr::from(addr), - Err(e) => { - return Err(FunctionErrorKind::Diagnostic( - format!("send_packet: {}", e), - Some(NaslValue::Null), - )); - } - }; + let sockaddr = + SocketAddr::from_str(&sock_str).map_err(PacketForgeryError::ParseSocketAddr)?; + let sockaddr = socket2::SockAddr::from(sockaddr); match soc.send_to(packet_raw, &sockaddr) { Ok(b) => { debug!("Sent {} bytes", b); } Err(e) => { - return Err(FunctionErrorKind::Diagnostic( - format!("send_packet: {}", e), - Some(NaslValue::Null), - )); + return Err(PacketForgeryError::SendPacket(e).into()); } } @@ -2227,7 +2106,7 @@ fn nasl_send_packet( /// - interface: network interface name, by default NASL will try to find the best one /// - pcap_filter: BPF filter, by default it listens to everything /// - timeout: timeout in seconds, 5 by default -fn nasl_pcap_next(register: &Register, configs: &Context) -> Result { +fn nasl_pcap_next(register: &Register, configs: &Context) -> Result { nasl_send_capture(register, configs) } @@ -2236,15 +2115,12 @@ fn nasl_pcap_next(register: &Register, configs: &Context) -> Result Result { +fn nasl_send_capture(register: &Register, configs: &Context) -> Result { let interface = match register.named("interface") { Some(ContextType::Value(NaslValue::String(x))) => x.to_string(), None => String::new(), _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( + return Err(FnError::wrong_unnamed_argument( "String", "Invalid interface value", )) @@ -2255,7 +2131,7 @@ fn nasl_send_capture( Some(ContextType::Value(NaslValue::String(x))) => x.to_string(), None => String::new(), _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( + return Err(FnError::wrong_unnamed_argument( "String", "Invalid pcap_filter value", )) @@ -2266,7 +2142,7 @@ fn nasl_send_capture( Some(ContextType::Value(NaslValue::Number(x))) => *x as i32 * 1000i32, // to milliseconds None => DEFAULT_TIMEOUT, _ => { - return Err(FunctionErrorKind::wrong_unnamed_argument( + return Err(FnError::wrong_unnamed_argument( "Integer", "Invalid timeout value", )) @@ -2297,9 +2173,8 @@ fn nasl_send_capture( match p { Ok(packet) => { // Remove all from lower layer - let frame = EthernetPacket::new(packet.data).ok_or_else(|| { - FunctionErrorKind::Dirty("No possible to create a packet from buffer".to_string()) - })?; + let frame = EthernetPacket::new(packet.data) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; Ok(NaslValue::Data(frame.payload().to_vec())) } Err(_) => Ok(NaslValue::Null), diff --git a/rust/src/nasl/builtin/raw_ip/raw_ip_utils.rs b/rust/src/nasl/builtin/raw_ip/raw_ip_utils.rs index ae1794023..94e67ae83 100644 --- a/rust/src/nasl/builtin/raw_ip/raw_ip_utils.rs +++ b/rust/src/nasl/builtin/raw_ip/raw_ip_utils.rs @@ -7,18 +7,19 @@ use std::{ str::FromStr, }; -use crate::nasl::syntax::NaslValue; -use crate::nasl::utils::FunctionErrorKind; +use crate::nasl::prelude::*; use pcap::{Address, Device}; +use super::RawIpError; + /// Convert a string in a IpAddr -pub fn ipstr2ipaddr(ip_addr: &str) -> Result { +pub fn ipstr2ipaddr(ip_addr: &str) -> Result { match IpAddr::from_str(ip_addr) { Ok(ip) => Ok(ip), - Err(_) => Err(FunctionErrorKind::Diagnostic( + Err(_) => Err(FnError::from(ArgumentError::WrongArgument( "Invalid IP address".to_string(), - Some(NaslValue::Null), - )), + )) + .with(ReturnValue(NaslValue::Null))), } } @@ -39,7 +40,7 @@ pub fn islocalhost(addr: IpAddr) -> bool { } /// Get the interface from the local ip -pub fn get_interface_by_local_ip(local_address: IpAddr) -> Result { +pub fn get_interface_by_local_ip(local_address: IpAddr) -> Result { // This fake IP is used for matching (and return false) // during the search of the interface in case an interface // doesn't have an associated address. @@ -58,58 +59,35 @@ pub fn get_interface_by_local_ip(local_address: IpAddr) -> Result devices.into_iter().find(|x| { + let devices = Device::list().map_err(|_| RawIpError::FailedToGetDeviceList)?; + devices + .into_iter() + .find(|x| { local_address == (x.addresses.clone().into_iter().find(ip_match)) .unwrap_or_else(|| fake_addr.to_owned()) .addr - }), - Err(_) => None, - }; - - match dev { - Some(dev) => Ok(dev), - _ => Err(FunctionErrorKind::Diagnostic( - "Invalid ip address".to_string(), - None, - )), - } + }) + .ok_or(RawIpError::InvalidIpAddress.into()) } -pub fn bind_local_socket(dst: &SocketAddr) -> Result { - let fe = Err(FunctionErrorKind::Diagnostic( - "Error binding".to_string(), - None, - )); +pub fn bind_local_socket(dst: &SocketAddr) -> Result { match dst { - SocketAddr::V4(_) => UdpSocket::bind("0.0.0.0:0").or(fe), - SocketAddr::V6(_) => UdpSocket::bind(" 0:0:0:0:0:0:0:0:0").or(fe), + SocketAddr::V4(_) => UdpSocket::bind("0.0.0.0:0"), + SocketAddr::V6(_) => UdpSocket::bind(" 0:0:0:0:0:0:0:0:0"), } + .map_err(RawIpError::FailedToBind) } /// Return the source IP address given the destination IP address -pub fn get_source_ip(dst: IpAddr, port: u16) -> Result { +pub fn get_source_ip(dst: IpAddr, port: u16) -> Result { let socket = SocketAddr::new(dst, port); let sd = format!("{}:{}", dst, port); let local_socket = bind_local_socket(&socket)?; - match local_socket.connect(sd) { - Ok(_) => match local_socket.local_addr() { - Ok(l_addr) => match IpAddr::from_str(&l_addr.ip().to_string()) { - Ok(x) => Ok(x), - Err(_) => Err(FunctionErrorKind::Diagnostic( - "No route to destination".to_string(), - None, - )), - }, - Err(_) => Err(FunctionErrorKind::Diagnostic( - "No route to destination".to_string(), - None, - )), - }, - Err(_) => Err(FunctionErrorKind::Diagnostic( - "No route to destination".to_string(), - None, - )), - } + local_socket + .connect(sd) + .ok() + .and_then(|_| local_socket.local_addr().ok()) + .and_then(|l_addr| IpAddr::from_str(&l_addr.ip().to_string()).ok()) + .ok_or(RawIpError::NoRouteToDestination.into()) } diff --git a/rust/src/nasl/builtin/raw_ip/tests/packet_forgery.rs b/rust/src/nasl/builtin/raw_ip/tests/packet_forgery.rs index fb446c261..cf3a20b53 100644 --- a/rust/src/nasl/builtin/raw_ip/tests/packet_forgery.rs +++ b/rust/src/nasl/builtin/raw_ip/tests/packet_forgery.rs @@ -8,7 +8,7 @@ mod tests { use nasl_builtin_raw_ip::RawIp; use nasl_builtin_std::ContextFactory; - use crate::nasl::utils::{error::FunctionErrorKind, Executor}; + use crate::nasl::utils::{error::FnError, Executor}; use crate::nasl::interpreter::test_utils::TestBuilder; use crate::nasl::syntax::NaslValue; @@ -20,7 +20,7 @@ mod tests { o_buf: &[u8], o_init: usize, o_fin: usize, - ) -> Result<(), FunctionErrorKind> { + ) -> Result<(), FnError> { let o_range = o_fin - o_init; let d_range = d_fin - d_init; if d_buf.len() < d_range @@ -29,7 +29,7 @@ mod tests { || d_buf.len() < d_fin || o_buf.len() < o_fin { - return Err(FunctionErrorKind::Diagnostic( + return Err(FnError::Diagnostic( "Error copying from slice. Index out of range".to_string(), Some(NaslValue::Null), )); @@ -270,7 +270,7 @@ mod tests { // different range size between origin and destination assert_eq!( safe_copy_from_slice(&mut a, 0, 2, &b, 0, b.len()), - Err(FunctionErrorKind::Diagnostic( + Err(FnError::Diagnostic( "Error copying from slice. Index out of range".to_string(), Some(NaslValue::Null) )) @@ -279,7 +279,7 @@ mod tests { // different range size between origin and destination assert_eq!( safe_copy_from_slice(&mut a, 0, alen, &b, 0, 2), - Err(FunctionErrorKind::Diagnostic( + Err(FnError::Diagnostic( "Error copying from slice. Index out of range".to_string(), Some(NaslValue::Null) )) @@ -288,7 +288,7 @@ mod tests { // out of index in the destination range assert_eq!( safe_copy_from_slice(&mut a, 1, alen + 1, &b, 0, b.len()), - Err(FunctionErrorKind::Diagnostic( + Err(FnError::Diagnostic( "Error copying from slice. Index out of range".to_string(), Some(NaslValue::Null) )) diff --git a/rust/src/nasl/builtin/regex/mod.rs b/rust/src/nasl/builtin/regex/mod.rs index c615d3d72..eda1fdfba 100644 --- a/rust/src/nasl/builtin/regex/mod.rs +++ b/rust/src/nasl/builtin/regex/mod.rs @@ -7,6 +7,13 @@ mod tests; use crate::nasl::prelude::*; use regex::{Regex, RegexBuilder}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum RegexError { + #[error("Error building regular expression pattern: {0}")] + BuildingError(regex::Error), +} fn parse_search_string(mut s: &str, rnul: bool, multiline: bool) -> &str { if !rnul { @@ -19,17 +26,14 @@ fn parse_search_string(mut s: &str, rnul: bool, multiline: bool) -> &str { s } -fn make_regex(pattern: &str, icase: bool, multiline: bool) -> Result { +fn make_regex(pattern: &str, icase: bool, multiline: bool) -> Result { match RegexBuilder::new(pattern.to_string().as_str()) .case_insensitive(icase) .multi_line(multiline) .build() { Ok(re) => Ok(re), - Err(e) => Err(FunctionErrorKind::Dirty(format!( - " Error building regular expression pattern: {}", - e - ))), + Err(e) => Err(RegexError::BuildingError(e)), } } @@ -48,7 +52,7 @@ fn ereg( icase: Option, rnul: Option, multiline: Option, -) -> Result { +) -> Result { let icase = icase.unwrap_or(false); let rnul = rnul.unwrap_or(true); let multiline = multiline.unwrap_or(false); @@ -75,7 +79,7 @@ fn ereg_replace( replace: NaslValue, icase: Option, rnul: Option, -) -> Result { +) -> Result { let icase = icase.unwrap_or(false); let rnul = rnul.unwrap_or(true); @@ -103,7 +107,7 @@ fn egrep( pattern: NaslValue, icase: Option, rnul: Option, -) -> Result { +) -> Result { let icase = icase.unwrap_or(false); let rnul = rnul.unwrap_or(true); @@ -137,7 +141,7 @@ fn eregmatch( find_all: Option, icase: Option, rnul: Option, -) -> Result { +) -> Result { let icase = icase.unwrap_or(false); let rnul = rnul.unwrap_or(true); let find_all = find_all.unwrap_or(false); diff --git a/rust/src/nasl/builtin/regex/tests.rs b/rust/src/nasl/builtin/regex/tests.rs index 03402f615..87eaa0772 100644 --- a/rust/src/nasl/builtin/regex/tests.rs +++ b/rust/src/nasl/builtin/regex/tests.rs @@ -107,8 +107,8 @@ mod tests { "#, ); assert_eq!( - t.results()[1], - Ok(NaslValue::String("Pair 0\n Pair 2\n".to_string())) + t.results()[1].as_ref().unwrap(), + &NaslValue::String("Pair 0\n Pair 2\n".to_string()) ); } @@ -124,8 +124,8 @@ mod tests { "#, ); assert_eq!( - t.results()[1], - Ok(NaslValue::String("Pair 0\n Pair 2\n".to_string())) + t.results()[1].as_ref().unwrap(), + &NaslValue::String("Pair 0\n Pair 2\n".to_string()) ); } diff --git a/rust/src/nasl/builtin/report_functions/mod.rs b/rust/src/nasl/builtin/report_functions/mod.rs index 652d1fcd9..689e9ae26 100644 --- a/rust/src/nasl/builtin/report_functions/mod.rs +++ b/rust/src/nasl/builtin/report_functions/mod.rs @@ -26,7 +26,7 @@ impl Reporting { typus: ResultType, register: &Register, context: &Context, - ) -> Result { + ) -> Result { let data = register.named("data").map(|x| x.to_string()); let port = register .named("port") @@ -70,11 +70,7 @@ impl Reporting { /// - port, optional TCP or UDP port number of the service /// - proto is the protocol ("tcp" by default; "udp" is the other value). /// - uri specifies the location of a found product - fn log_message( - &self, - register: &Register, - context: &Context, - ) -> Result { + fn log_message(&self, register: &Register, context: &Context) -> Result { self.store_result(ResultType::Log, register, context) } @@ -89,7 +85,7 @@ impl Reporting { &self, register: &Register, context: &Context, - ) -> Result { + ) -> Result { self.store_result(ResultType::Alarm, register, context) } @@ -100,11 +96,7 @@ impl Reporting { /// - port, optional TCP or UDP port number of the service /// - proto is the protocol ("tcp" by default; "udp" is the other value). /// - uri specifies the location of a found product - fn error_message( - &self, - register: &Register, - context: &Context, - ) -> Result { + fn error_message(&self, register: &Register, context: &Context) -> Result { self.store_result(ResultType::Error, register, context) } } diff --git a/rust/src/nasl/builtin/ssh/error.rs b/rust/src/nasl/builtin/ssh/error.rs index 115e83b13..396acd60d 100644 --- a/rust/src/nasl/builtin/ssh/error.rs +++ b/rust/src/nasl/builtin/ssh/error.rs @@ -2,12 +2,12 @@ use std::fmt; use thiserror::Error; -use crate::nasl::FunctionErrorKind; +use crate::nasl::utils::error::WithErrorInfo; use super::SessionId; /// A cloneable representation of the Error type of the underlying SSH lib -#[derive(Clone, Debug, PartialEq, Eq, Error)] +#[derive(Clone, Debug, Error)] #[error("{0}")] pub struct LibError(String); @@ -25,7 +25,7 @@ impl From for LibError { } } -#[derive(Clone, Debug, PartialEq, Eq, Error)] +#[derive(Debug, Error)] pub struct SshError { pub kind: SshErrorKind, id: Option, @@ -48,7 +48,7 @@ impl fmt::Display for SshError { } } -#[derive(Clone, Debug, PartialEq, Eq, Error)] +#[derive(Clone, Debug, Error)] pub enum SshErrorKind { #[error("Failed to open new SSH session.")] NewSession, @@ -112,30 +112,34 @@ pub enum SshErrorKind { ConvertPrivateKey, #[error("Not yet implemented.")] Unimplemented, + #[error("Unexpected authentication status")] + UnexpectedAuthenticationStatus(String), } -pub trait AttachErrorInfo { - fn attach_error_info(self, e: Info) -> Self; -} +impl WithErrorInfo for SshError { + type Error = SshError; -impl AttachErrorInfo for SshError { - fn attach_error_info(mut self, id: SessionId) -> SshError { + fn with(mut self, id: SessionId) -> SshError { self.id = Some(id); self } } #[cfg(feature = "nasl-builtin-libssh")] -impl AttachErrorInfo for SshError { - fn attach_error_info(mut self, source: libssh_rs::Error) -> SshError { +impl WithErrorInfo for SshError { + type Error = SshError; + + fn with(mut self, source: libssh_rs::Error) -> SshError { self.source = Some(source.into()); self } } #[cfg(not(feature = "nasl-builtin-libssh"))] -impl AttachErrorInfo for SshError { - fn attach_error_info(mut self, source: russh::Error) -> SshError { +impl WithErrorInfo for SshError { + type Error = SshError; + + fn with(mut self, source: russh::Error) -> SshError { self.source = Some(source.into()); self } @@ -152,26 +156,11 @@ impl From for SshError { } impl SshErrorKind { - pub fn with(self, m: Info) -> SshError + pub fn with(self, m: Info) -> >::Error where - SshError: AttachErrorInfo, + SshError: WithErrorInfo, { let e: SshError = self.into(); - e.attach_error_info(m) - } -} - -impl SshError { - pub fn with(self, m: Info) -> SshError - where - SshError: AttachErrorInfo, - { - self.attach_error_info(m) - } -} - -impl From for FunctionErrorKind { - fn from(e: SshError) -> Self { - FunctionErrorKind::Ssh(e) + e.with(m) } } diff --git a/rust/src/nasl/builtin/ssh/libssh/channel.rs b/rust/src/nasl/builtin/ssh/libssh/channel.rs index 36a9a9412..ed53a8771 100644 --- a/rust/src/nasl/builtin/ssh/libssh/channel.rs +++ b/rust/src/nasl/builtin/ssh/libssh/channel.rs @@ -1,6 +1,7 @@ use std::time::Duration; use crate::nasl::builtin::ssh::error::SshErrorKind; +use crate::nasl::utils::error::WithErrorInfo; use super::{super::error::Result, SessionId}; diff --git a/rust/src/nasl/builtin/ssh/libssh/session.rs b/rust/src/nasl/builtin/ssh/libssh/session.rs index d095e61d4..95999ec69 100644 --- a/rust/src/nasl/builtin/ssh/libssh/session.rs +++ b/rust/src/nasl/builtin/ssh/libssh/session.rs @@ -7,6 +7,7 @@ use super::super::error::{Result, SshErrorKind}; use super::super::Output; use super::SessionId; use super::{channel::Channel, Socket}; +use crate::nasl::utils::error::WithErrorInfo; /// Structure to hold an SSH Session pub struct SshSession { diff --git a/rust/src/nasl/builtin/ssh/mod.rs b/rust/src/nasl/builtin/ssh/mod.rs index 10323ecd0..d5bdfe79c 100644 --- a/rust/src/nasl/builtin/ssh/mod.rs +++ b/rust/src/nasl/builtin/ssh/mod.rs @@ -42,7 +42,7 @@ mod libssh_uses { #[cfg(feature = "nasl-builtin-libssh")] pub use libssh_uses::*; -type Result = std::result::Result; +type Result = std::result::Result; const DEFAULT_SSH_PORT: u16 = 22; @@ -522,13 +522,12 @@ impl Ssh { } AuthStatus::Success => break, status => { - return Err(FunctionErrorKind::Diagnostic( - format!( - "Unexpected authentication status for session_id {}: {:?}", - session_id, status - ), - Some(NaslValue::Number(-1)), - )); + return Err(SshErrorKind::UnexpectedAuthenticationStatus(format!( + "{:?}", + status + )) + .with(session_id) + .with(ReturnValue(-1))); } } } diff --git a/rust/src/nasl/builtin/ssh/russh/session.rs b/rust/src/nasl/builtin/ssh/russh/session.rs index 1d603aec3..d3ac115cc 100644 --- a/rust/src/nasl/builtin/ssh/russh/session.rs +++ b/rust/src/nasl/builtin/ssh/russh/session.rs @@ -10,6 +10,7 @@ use tracing::{error, warn}; use crate::nasl::builtin::ssh::error::SshErrorKind; use crate::nasl::builtin::ssh::Output; +use crate::nasl::utils::error::WithErrorInfo; use crate::nasl::utils::function::bytes_to_str; use super::super::error::SshError; diff --git a/rust/src/nasl/builtin/ssh/tests/mod.rs b/rust/src/nasl/builtin/ssh/tests/mod.rs index 1ed70972d..84a9183e0 100644 --- a/rust/src/nasl/builtin/ssh/tests/mod.rs +++ b/rust/src/nasl/builtin/ssh/tests/mod.rs @@ -13,7 +13,7 @@ use server::TestServer; use crate::check_err_matches; use crate::nasl::builtin::ssh::error::SshErrorKind; use crate::nasl::builtin::ssh::sessions::MIN_SESSION_ID; -use crate::nasl::builtin::SshError; +use crate::nasl::builtin::ssh::SshError; use crate::nasl::test_prelude::*; use crate::nasl::NoOpLoader; use crate::storage::DefaultDispatcher; @@ -83,16 +83,16 @@ async fn ssh_connect() { check_err_matches!( t, format!(r#"id = ssh_connect(port:{}, keytype: "foo");"#, PORT), - FunctionErrorKind::WrongArgument(_) + ArgumentError::WrongArgument(_) ); // Without a matching key algorithm, we should not be able to connect check_err_matches!( t, format!(r#"id = ssh_connect(port:{}, keytype: "ssh-rsa");"#, PORT), - FunctionErrorKind::Ssh(SshError { + SshError { kind: SshErrorKind::Connect, .. - }) + } ); }, default_config(), @@ -121,10 +121,10 @@ async fn ssh_userauth() { check_err_matches!( t, r#"ssh_userauth(session_id);"#, - FunctionErrorKind::Ssh(SshError { + SshError { kind: SshErrorKind::NoAuthenticationGiven, .. - }), + }, ); userauth(&mut t); }, diff --git a/rust/src/nasl/builtin/ssh/utils.rs b/rust/src/nasl/builtin/ssh/utils.rs index be283db84..446c2501c 100644 --- a/rust/src/nasl/builtin/ssh/utils.rs +++ b/rust/src/nasl/builtin/ssh/utils.rs @@ -12,7 +12,7 @@ impl<'a, T> FromNaslValue<'a> for CommaSeparated where T: for<'b> FromNaslValue<'b>, { - fn from_nasl_value(value: &'a NaslValue) -> Result { + fn from_nasl_value(value: &'a NaslValue) -> Result { let s = StringOrData::from_nasl_value(value)?; Ok(Self( s.0.split(",") @@ -21,31 +21,27 @@ where let nasl_val = NaslValue::String(substr.to_string()); T::from_nasl_value(&nasl_val) }) - .collect::, FunctionErrorKind>>()?, + .collect::, FnError>>()?, )) } } impl<'a> FromNaslValue<'a> for key::Name { - fn from_nasl_value(value: &'a NaslValue) -> Result { + fn from_nasl_value(value: &'a NaslValue) -> Result { let s = String::from_nasl_value(value)?; key::Name::try_from(&*s).map_err(|_| { - FunctionErrorKind::WrongArgument(format!( - "Expected a valid SSH key type, found '{}'", - s - )) + ArgumentError::WrongArgument(format!("Expected a valid SSH key type, found '{}'", s)) + .into() }) } } impl<'a> FromNaslValue<'a> for cipher::Name { - fn from_nasl_value(value: &'a NaslValue) -> Result { + fn from_nasl_value(value: &'a NaslValue) -> Result { let s = String::from_nasl_value(value)?; cipher::Name::try_from(&*s).map_err(|_| { - FunctionErrorKind::WrongArgument(format!( - "Expected a valid SSH cipher type, found '{}'", - s - )) + ArgumentError::WrongArgument(format!("Expected a valid SSH cipher type, found '{}'", s)) + .into() }) } } diff --git a/rust/src/nasl/builtin/string/mod.rs b/rust/src/nasl/builtin/string/mod.rs index 31ad2188b..957fc8e38 100644 --- a/rust/src/nasl/builtin/string/mod.rs +++ b/rust/src/nasl/builtin/string/mod.rs @@ -7,17 +7,25 @@ #[cfg(test)] mod tests; -use crate::nasl::utils::{ - function::{bytes_to_str, CheckedPositionals, Maybe, StringOrData}, - Context, FunctionErrorKind, Register, +use crate::nasl::{ + utils::{ + function::{bytes_to_str, CheckedPositionals, Maybe, StringOrData}, + FnError, + }, + ArgumentError, }; use core::fmt::Write; use glob::{MatchOptions, Pattern}; -use nasl_function_proc_macro::nasl_function; use std::num::ParseIntError; +use thiserror::Error; -use crate::function_set; -use crate::nasl::syntax::NaslValue; +use crate::nasl::prelude::*; + +use super::BuiltinError; + +#[derive(Debug, Error)] +#[error("{0}")] +pub struct StringError(#[from] std::fmt::Error); /// Decodes given string as hex and returns the result as a byte array pub fn decode_hex(s: &str) -> Result, ParseIntError> { @@ -74,7 +82,7 @@ fn raw_string(positional: CheckedPositionals<&NaslValue>) -> Vec { data } -fn write_nasl_string(s: &mut String, value: &NaslValue) -> Result<(), FunctionErrorKind> { +fn write_nasl_string(s: &mut String, value: &NaslValue) -> Result<(), StringError> { match value { NaslValue::String(x) => write!(s, "{x}"), NaslValue::Data(x) => { @@ -108,7 +116,7 @@ fn write_nasl_string(s: &mut String, value: &NaslValue) -> Result<(), FunctionEr /// NASL function to parse values into string representations #[nasl_function] -fn string(positional: CheckedPositionals<&NaslValue>) -> Result { +fn string(positional: CheckedPositionals<&NaslValue>) -> Result { let mut s = String::with_capacity(2 * positional.len()); for p in positional { write_nasl_string_value(&mut s, p)?; @@ -116,7 +124,7 @@ fn string(positional: CheckedPositionals<&NaslValue>) -> Result Result<(), FunctionErrorKind> { +fn write_nasl_string_value(s: &mut String, value: &NaslValue) -> Result<(), StringError> { match value { NaslValue::Array(x) => { for p in x { @@ -219,11 +227,11 @@ fn hex(s: i64) -> String { /// /// The first positional argument must be a string, all other arguments are ignored. If either the no argument was given or the first positional is not a string, a error is returned. #[nasl_function] -fn hexstr_to_data(s: NaslValue) -> Result, FunctionErrorKind> { +fn hexstr_to_data(s: NaslValue) -> Result, ArgumentError> { let s = s.to_string(); let s = s.as_str(); decode_hex(s).map_err(|_| { - FunctionErrorKind::WrongArgument(format!( + ArgumentError::WrongArgument(format!( "Expected an even-length string containing only 0-9a-fA-F, found '{}'", s )) @@ -282,7 +290,7 @@ fn stridx(haystack: NaslValue, needle: NaslValue, offset: Option) -> i64 /// NASL function to display any number of NASL values /// /// Internally the string function is used to concatenate the given parameters -fn display(register: &Register, configs: &Context) -> Result { +fn display(register: &Register, configs: &Context) -> Result { println!("{}", &string(register, configs)?); Ok(NaslValue::Null) } @@ -331,7 +339,7 @@ fn insstr( to_insert: NaslValue, start: usize, end: Option, -) -> Result { +) -> Result { let mut s = s.to_string(); let insb = to_insert.to_string(); @@ -339,7 +347,7 @@ fn insstr( let end = end.unwrap_or(s.len()).min(s.len()); if start > end { - return Err(FunctionErrorKind::WrongArgument(format!( + return Err(ArgumentError::WrongArgument(format!( "start index ({}) larger than end ({}).", start, end ))); @@ -360,11 +368,7 @@ fn insstr( /// `pattern` contains the pattern to search for. /// The optional argument `icase` toggles case sensitivity. Default: false (case sensitive). If true, search is case insensitive. #[nasl_function(named(string, pattern, icase))] -fn match_( - string: NaslValue, - pattern: NaslValue, - icase: Option, -) -> Result { +fn match_(string: NaslValue, pattern: NaslValue, icase: Option) -> Result { let options = MatchOptions { case_sensitive: !icase.unwrap_or(false), require_literal_separator: false, @@ -377,7 +381,7 @@ fn match_( Ok(Pattern::new(pattern) .map_err(|err| { - FunctionErrorKind::WrongArgument(format!( + ArgumentError::WrongArgument(format!( "Argument 'pattern' to 'match' is not a valid pattern: {}. {}", pattern, err )) diff --git a/rust/src/nasl/builtin/string/tests.rs b/rust/src/nasl/builtin/string/tests.rs index 1f6e95dc7..65a796617 100644 --- a/rust/src/nasl/builtin/string/tests.rs +++ b/rust/src/nasl/builtin/string/tests.rs @@ -3,8 +3,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception #[cfg(test)] mod tests { - use crate::nasl::test_prelude::*; - use FunctionErrorKind::*; + use crate::nasl::{test_prelude::*, utils::error::ArgumentError}; + use ArgumentError::*; use NaslValue::*; #[test] @@ -12,7 +12,7 @@ mod tests { check_code_result("hexstr('foo');", "666f6f"); check_err_matches!( "hexstr('foo', 'I will be ignored');", - TrailingPositionalArguments { .. }, + TrailingPositionals { .. }, ); check_code_result("hexstr(6);", Null); check_code_result("hexstr();", Null); @@ -82,7 +82,7 @@ mod tests { check_code_result("chomp('abc\n');", "abc"); check_code_result("chomp('abc ');", "abc"); check_code_result("chomp('abc\n\t\r ');", "abc"); - check_err_matches!("chomp();", MissingPositionalArguments { .. }); + check_err_matches!("chomp();", MissingPositionals { .. }); } #[test] @@ -123,7 +123,7 @@ mod tests { check_code_result(r#"ord("c");"#, 99); check_code_result(r#"ord("");"#, Null); check_code_result("ord(1);", 49); - check_err_matches!("ord();", MissingPositionalArguments { .. }); + check_err_matches!("ord();", MissingPositionals { .. }); } #[test] @@ -141,8 +141,8 @@ mod tests { // g_pattern_spec allows globs to match slashes, make sure we do too check_code_result(r#"match(string: "a///", pattern: "a*");"#, true); check_code_result(r#"match(string: "///a", pattern: "*a");"#, true); - check_err_matches!(r#"match(string: "abcd");"#, MissingArguments { .. }); - check_err_matches!(r#"match(pattern: "ab");"#, MissingArguments { .. }); + check_err_matches!(r#"match(string: "abcd");"#, MissingNamed { .. }); + check_err_matches!(r#"match(pattern: "ab");"#, MissingNamed { .. }); } #[test] @@ -153,7 +153,7 @@ mod tests { check_code_result(r#"hex(256);"#, "0x00"); check_code_result(r#"hex(257);"#, "0x01"); check_code_result(r#"hex(-2);"#, "0xfe"); - check_err_matches!(r#"hex();"#, MissingPositionalArguments { .. }); + check_err_matches!(r#"hex();"#, MissingPositionals { .. }); } #[test] @@ -210,7 +210,7 @@ mod tests { r#"split("a;b;c", sep: ";");"#, vec!["a;".to_string(), "b;".to_string(), "c".to_string()], ); - check_err_matches!(r#"split();"#, MissingPositionalArguments { .. }); + check_err_matches!(r#"split();"#, MissingPositionals { .. }); } #[test] @@ -219,8 +219,8 @@ mod tests { r#"str_replace(string: "abc", find: "b", replace: "foo");"#, "afooc", ); - check_err_matches!(r#"str_replace();"#, MissingArguments { .. }); - check_err_matches!(r#"str_replace(string: "abc");"#, MissingArguments { .. }); + check_err_matches!(r#"str_replace();"#, MissingNamed { .. }); + check_err_matches!(r#"str_replace(string: "abc");"#, MissingNamed { .. }); check_code_result(r#"str_replace(string: "abc", find: "b");"#, "ac"); check_code_result( r#"str_replace(string: "abcbd", find: "b", count: 1);"#, @@ -236,7 +236,7 @@ mod tests { check_code_result(r#"strstr("abc", "b");"#, "bc"); check_code_result(r#"strstr("abcbd", "b");"#, "bcbd"); check_code_result(r#"strstr('a\rbcbd', '\rb');"#, "\rbcbd"); - check_err_matches!(r#"strstr();"#, MissingPositionalArguments { .. }); - check_err_matches!(r#"strstr("a");"#, MissingPositionalArguments { .. }); + check_err_matches!(r#"strstr();"#, MissingPositionals { .. }); + check_err_matches!(r#"strstr("a");"#, MissingPositionals { .. }); } } diff --git a/rust/src/nasl/interpreter/call.rs b/rust/src/nasl/interpreter/call.rs index 6c6845bb8..959c5ed9e 100644 --- a/rust/src/nasl/interpreter/call.rs +++ b/rust/src/nasl/interpreter/call.rs @@ -2,11 +2,11 @@ // // SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception -use crate::nasl::syntax::{Statement, StatementKind::*, Token}; +use crate::nasl::syntax::{Statement, StatementKind::*}; use crate::nasl::utils::lookup_keys::FC_ANON_ARGS; use crate::nasl::interpreter::{ - error::{FunctionError, InterpretError}, + error::{FunctionCallError, InterpretError}, interpreter::{InterpretResult, RunSpecific}, Interpreter, }; @@ -15,8 +15,15 @@ use crate::nasl::syntax::NaslValue; use crate::nasl::utils::ContextType; use std::collections::HashMap; +use super::InterpretErrorKind; + impl<'a> Interpreter<'a> { - pub async fn call(&mut self, name: &Token, arguments: &[Statement]) -> InterpretResult { + pub async fn call( + &mut self, + statement: &Statement, + arguments: &[Statement], + ) -> InterpretResult { + let name = statement.as_token(); let name = &Self::identifier(name)?; // get the context let mut named = HashMap::new(); @@ -76,14 +83,17 @@ impl<'a> Interpreter<'a> { Ok(x[0].clone()) } - Some(Ok(NaslValue::Fork(x))) if self.index == 0 && x.is_empty() => { - Ok(NaslValue::Null) - } + Some(Ok(NaslValue::Fork(x))) if self.index == 0 && x.is_empty() => Ok(NaslValue::Null), Some(Ok(NaslValue::Fork(_))) => { unreachable!("NaslValue::Fork must only occur on root instance, all other cases should return a value within run_specific") } - Some(r) => r.map_err(|x| FunctionError::new(name, x).into()), + Some(r) => r.map_err(|e| { + InterpretError::new( + InterpretErrorKind::FunctionCallError(FunctionCallError::new(name, e)), + Some(statement.clone()), + ) + }), None => { let found = self .register() @@ -176,16 +186,9 @@ get_kb_item("port") + ":" + get_kb_item("host"); "#, ); - let results: Vec<_> = t - .results() - .into_iter() - .filter_map(|x| x.ok()) - .collect(); + let results: Vec<_> = t.results().into_iter().filter_map(|x| x.ok()).collect(); - assert_eq!( - results, - vec!["\0:\0".into()] - ); + assert_eq!(results, vec!["\0:\0".into()]); } #[test] diff --git a/rust/src/nasl/interpreter/error.rs b/rust/src/nasl/interpreter/error.rs index 60d04b56f..7602ca448 100644 --- a/rust/src/nasl/interpreter/error.rs +++ b/rust/src/nasl/interpreter/error.rs @@ -2,38 +2,39 @@ // // SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception -use std::io; - use crate::nasl::syntax::LoadError; use crate::nasl::syntax::{Statement, SyntaxError, TokenCategory}; -use crate::nasl::utils::error::FunctionErrorKind; -use crate::storage::StorageError; +use crate::nasl::utils::error::FnError; use thiserror::Error; -#[derive(Debug, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Error)] /// An error that occurred while calling a function #[error("Error while calling function '{function}': {kind}")] -pub struct FunctionError { +pub struct FunctionCallError { /// Name of the function pub function: String, /// Kind of error #[source] - pub kind: FunctionErrorKind, + pub kind: FnError, } -impl FunctionError { +impl FunctionCallError { /// Creates a new FunctionError - pub fn new(function: &str, kind: FunctionErrorKind) -> Self { + pub fn new(function: &str, kind: FnError) -> Self { Self { function: function.to_owned(), kind, } } + + fn retryable(&self) -> bool { + self.kind.retryable() + } } -#[derive(Debug, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Error)] /// Is used to represent an error while interpreting -#[error("{}{kind}", self.origin.clone().map(|e| format!("{e}: ")).unwrap_or_default())] +#[error("{} {kind}", self.format_origin())] pub struct InterpretError { /// Defined the type of error that occurred. #[source] @@ -42,7 +43,27 @@ pub struct InterpretError { pub origin: Option, } -#[derive(Debug, Clone, PartialEq, Eq, Error)] +impl InterpretError { + fn format_origin(&self) -> String { + if let Some(ref origin) = self.origin { + let line = self.line(); + let col = self.column(); + format!("Error in statement '{origin}' at {}:{}.", line, col) + } else { + "".into() + } + } + + pub fn retryable(&self) -> bool { + match &self.kind { + InterpretErrorKind::LoadError(LoadError::Retry(_)) => true, + InterpretErrorKind::FunctionCallError(e) => e.retryable(), + _ => false, + } + } +} + +#[derive(Debug, Error)] /// Is used to give hints to the user how to react on an error while interpreting pub enum InterpretErrorKind { /// When returned context is a function when a value is required. @@ -74,22 +95,12 @@ pub enum InterpretErrorKind { /// When the given key was not found in the context #[error("Key not found: {0}")] NotFound(String), - /// A StorageError occurred - // FIXME rename to general error - #[error("{0}")] - StorageError(StorageError), /// A LoadError occurred #[error("{0}")] LoadError(LoadError), - /// A Formatting error occurred - #[error("{0}")] - FMTError(std::fmt::Error), - /// An IOError occurred - #[error("{0}")] - IOError(io::ErrorKind), /// An error occurred while calling a built-in function. #[error("{0}")] - FunctionCallError(FunctionError), + FunctionCallError(FunctionCallError), } impl InterpretError { @@ -197,45 +208,14 @@ impl From for InterpretError { } } -impl From for InterpretError { - fn from(se: StorageError) -> Self { - Self::new(InterpretErrorKind::StorageError(se), None) - } -} - -impl From for InterpretError { - fn from(ie: io::ErrorKind) -> Self { - Self::new(InterpretErrorKind::IOError(ie), None) - } -} - -impl From for InterpretError { - fn from(e: io::Error) -> Self { - e.kind().into() - } -} - -impl From for InterpretError { - fn from(fe: std::fmt::Error) -> Self { - Self::new(InterpretErrorKind::FMTError(fe), None) - } -} - impl From for InterpretError { fn from(le: LoadError) -> Self { Self::new(InterpretErrorKind::LoadError(le), None) } } -impl From for InterpretError { - fn from(fe: FunctionError) -> Self { - match fe.kind { - FunctionErrorKind::FMTError(fe) => fe.into(), - FunctionErrorKind::IOError(ie) => ie.into(), - FunctionErrorKind::GeneralError(e) => { - Self::new(InterpretErrorKind::StorageError(e), None) - } - _ => Self::new(InterpretErrorKind::FunctionCallError(fe), None), - } +impl From for InterpretError { + fn from(fe: FunctionCallError) -> Self { + Self::new(InterpretErrorKind::FunctionCallError(fe), None) } } diff --git a/rust/src/nasl/interpreter/include.rs b/rust/src/nasl/interpreter/include.rs index 232c6136d..b94a39101 100644 --- a/rust/src/nasl/interpreter/include.rs +++ b/rust/src/nasl/interpreter/include.rs @@ -54,16 +54,19 @@ mod tests { let ctx = context.build(Default::default()); let mut interpreter = CodeInterpreter::new(code, register, &ctx); assert_eq!( - interpreter.next_statement().await, - Some(Ok(NaslValue::Null)) + interpreter.next_statement().await.unwrap().unwrap(), + NaslValue::Null ); - assert_eq!(interpreter.next_statement().await, Some(Ok(12.into()))); assert_eq!( - interpreter.next_statement().await, - Some(Ok(NaslValue::Dict(HashMap::from([( + interpreter.next_statement().await.unwrap().unwrap(), + 12.into() + ); + assert_eq!( + interpreter.next_statement().await.unwrap().unwrap(), + NaslValue::Dict(HashMap::from([( "hello".to_owned(), NaslValue::Data("world".as_bytes().into()) - )])))) + )])) ); } } diff --git a/rust/src/nasl/interpreter/interpreter.rs b/rust/src/nasl/interpreter/interpreter.rs index bcdcb0e7a..0700ed688 100644 --- a/rust/src/nasl/interpreter/interpreter.rs +++ b/rust/src/nasl/interpreter/interpreter.rs @@ -2,17 +2,14 @@ // // SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception -use std::{collections::HashMap, io}; - -use crate::nasl::syntax::{ - IdentifierType, LoadError, NaslValue, Statement, StatementKind::*, SyntaxError, Token, - TokenCategory, -}; -use crate::storage::StorageError; +use std::collections::HashMap; use crate::nasl::interpreter::{ declare::{DeclareFunctionExtension, DeclareVariableExtension}, - InterpretError, InterpretErrorKind, + InterpretError, +}; +use crate::nasl::syntax::{ + IdentifierType, NaslValue, Statement, StatementKind::*, SyntaxError, Token, TokenCategory, }; use crate::nasl::utils::{Context, ContextType, Register}; @@ -214,13 +211,10 @@ impl<'a> Interpreter<'a> { Ok(x) => Ok(x), Err(e) => { if max_attempts > 0 { - match e.kind { - InterpretErrorKind::LoadError(LoadError::Retry(_)) - | InterpretErrorKind::IOError(io::ErrorKind::Interrupted) - | InterpretErrorKind::StorageError(StorageError::Retry(_)) => { - Box::pin(self.retry_resolve_next(stmt, max_attempts - 1)).await - } - _ => Err(e), + if e.retryable() { + Box::pin(self.retry_resolve_next(stmt, max_attempts - 1)).await + } else { + Err(e) } } else { Err(e) @@ -248,10 +242,10 @@ impl<'a> Interpreter<'a> { pub(crate) async fn resolve(&mut self, statement: &Statement) -> InterpretResult { self.position_mut().up(); let span = tracing::span!(tracing::Level::WARN, "resolve", - statement=statement.to_string(), - index=self.index, - position=%self.position(), - run_specific_len=self.run_specific.len(), + statement=statement.to_string(), + index=self.index, + position=%self.position(), + run_specific_len=self.run_specific.len(), skipped_value_pos=?self.skip_until_return(), ); let _enter = span.enter(); if let Some(val) = self.may_return_value() { @@ -283,9 +277,7 @@ impl<'a> Interpreter<'a> { } Primitive => self.resolve_primitive(statement), Variable => self.resolve_variable(statement), - Call(arguments) => { - Box::pin(self.call(statement.as_token(), arguments.children())).await - } + Call(arguments) => Box::pin(self.call(statement, arguments.children())).await, Declare(stmts) => self.declare_variable(statement.as_token(), stmts), Parameter(x) => self.resolve_parameter(x).await, Assign(cat, order, left, right) => { @@ -466,5 +458,4 @@ impl<'a> Interpreter<'a> { ), } } - } diff --git a/rust/src/nasl/interpreter/mod.rs b/rust/src/nasl/interpreter/mod.rs index 13c01efa4..bb2e2a585 100644 --- a/rust/src/nasl/interpreter/mod.rs +++ b/rust/src/nasl/interpreter/mod.rs @@ -20,7 +20,7 @@ mod operator; mod tests; pub use code_interpreter::*; -pub use error::FunctionError; +pub use error::FunctionCallError; pub use error::InterpretError; pub use error::InterpretErrorKind; pub use interpreter::Interpreter; diff --git a/rust/src/nasl/interpreter/tests/description.rs b/rust/src/nasl/interpreter/tests/description.rs index acc601334..9d6db83e8 100644 --- a/rust/src/nasl/interpreter/tests/description.rs +++ b/rust/src/nasl/interpreter/tests/description.rs @@ -61,7 +61,10 @@ if(description) t.set_variable("description", NaslValue::Number(1)); t.run_all(code); let results = t.results(); - assert_eq!(*results.last().unwrap(), Ok(NaslValue::Exit(23))); + assert_eq!( + *results.last().unwrap().as_ref().unwrap(), + NaslValue::Exit(23) + ); let mut tag = BTreeMap::new(); tag.insert(TagKey::CreationDate, 1366091481.into()); diff --git a/rust/src/nasl/interpreter/tests/local_var.rs b/rust/src/nasl/interpreter/tests/local_var.rs index 8b39cdef4..65d61fee7 100644 --- a/rust/src/nasl/interpreter/tests/local_var.rs +++ b/rust/src/nasl/interpreter/tests/local_var.rs @@ -18,5 +18,8 @@ if (a) { a; "###, ); - assert_eq!(t.results().last().unwrap(), &Ok(NaslValue::Number(1))); + assert!(matches!( + t.results().last().unwrap(), + &Ok(NaslValue::Number(1)) + )); } diff --git a/rust/src/nasl/interpreter/tests/mod.rs b/rust/src/nasl/interpreter/tests/mod.rs index 46dc84ed4..6663b80cc 100644 --- a/rust/src/nasl/interpreter/tests/mod.rs +++ b/rust/src/nasl/interpreter/tests/mod.rs @@ -1,2 +1,3 @@ mod description; mod local_var; +mod retry; diff --git a/rust/src/nasl/interpreter/tests/retry.rs b/rust/src/nasl/interpreter/tests/retry.rs new file mode 100644 index 000000000..d850f03ff --- /dev/null +++ b/rust/src/nasl/interpreter/tests/retry.rs @@ -0,0 +1,46 @@ +//! Checks that errors that specify that they are solvable by +//! retrying are actually retried within the interpreter. + +use crate::nasl::{test_prelude::*, utils::Executor}; + +struct Counter { + count: usize, +} + +impl Counter { + fn check_and_increment(&mut self) -> Result { + self.count += 1; + if self.count < 5 { + // Return a dummy error, it doesnt matter what it is. + Err(FnError::from(ArgumentError::WrongArgument("test".into()))) + } else { + Ok(self.count) + } + } + + #[nasl_function] + fn check_counter_retry(&mut self) -> Result { + self.check_and_increment().map_err(|e| e.with(Retryable)) + } + + #[nasl_function] + fn check_counter(&mut self) -> Result { + self.check_and_increment() + } +} + +function_set! { + Counter, + sync_stateful_mut, + ( + (Counter::check_counter_retry, "check_counter_retry"), + (Counter::check_counter, "check_counter"), + ) +} + +#[test] +fn retryable_error() { + let mut t = TestBuilder::default().with_executor(Executor::single(Counter { count: 0 })); + check_err_matches!(t, "check_counter();", ArgumentError::WrongArgument(_)); + t.ok("check_counter_retry();", 5); +} diff --git a/rust/src/nasl/mod.rs b/rust/src/nasl/mod.rs index a399cbcb9..4436e50bb 100644 --- a/rust/src/nasl/mod.rs +++ b/rust/src/nasl/mod.rs @@ -7,18 +7,25 @@ pub mod utils; pub mod test_utils; pub mod prelude { + pub use super::builtin::BuiltinError; pub use super::builtin::ContextFactory; pub use super::builtin::RegisterBuilder; pub use super::syntax::FSPluginLoader; pub use super::syntax::Loader; pub use super::syntax::NaslValue; + pub use super::utils::error::FnErrorKind; + pub use super::utils::error::Retryable; + pub use super::utils::error::ReturnValue; + pub use super::utils::error::WithErrorInfo; pub use super::utils::function::CheckedPositionals; pub use super::utils::function::FromNaslValue; pub use super::utils::function::Positionals; pub use super::utils::function::ToNaslResult; + pub use super::utils::ArgumentError; pub use super::utils::Context; pub use super::utils::ContextType; - pub use super::utils::FunctionErrorKind; + pub use super::utils::FnError; + pub use super::utils::InternalError; pub use super::utils::NaslResult; pub use super::utils::Register; pub use crate::function_set; diff --git a/rust/src/nasl/test_utils.rs b/rust/src/nasl/test_utils.rs index 92b1f8c63..e132c14d7 100644 --- a/rust/src/nasl/test_utils.rs +++ b/rust/src/nasl/test_utils.rs @@ -24,7 +24,7 @@ use super::{ // The following exists to trick the trait solver into // believing me that everything is fine. Doing this naively // runs into some compiler errors. -trait CloneableFn: Fn(NaslResult) -> bool + Sync + Send { +trait CloneableFn: Fn(&NaslResult) -> bool + Sync + Send { fn clone_box<'a>(&self) -> Box where Self: 'a; @@ -32,7 +32,7 @@ trait CloneableFn: Fn(NaslResult) -> bool + Sync + Send { impl CloneableFn for F where - F: Fn(NaslResult) -> bool + Clone + Sync + Send, + F: Fn(&NaslResult) -> bool + Clone + Sync + Send, { fn clone_box<'a>(&self) -> Box where @@ -184,7 +184,7 @@ where pub fn check( &mut self, line: impl Into, - f: impl Fn(NaslResult) -> bool + 'static + Clone + Sync + Send, + f: impl Fn(&NaslResult) -> bool + 'static + Clone + Sync + Send, expected: Option>, ) -> &mut Self { self.add_line( @@ -294,7 +294,7 @@ where fn check_result( &self, - result: &Result, + result: &Result, reference: &TracedTestResult, line_count: usize, ) { @@ -305,7 +305,7 @@ where "Mismatch at {}.\nIn code \"{}\":\nExpected: {:?}\nFound: {:?}", reference.location, self.lines[line_count], - Ok::<_, FunctionErrorKind>(reference_result), + Ok::<_, FnError>(reference_result), result, ); } @@ -324,14 +324,10 @@ where } } - fn compare_result( - &self, - result: &Result, - reference: &TestResult, - ) -> bool { + fn compare_result(&self, result: &Result, reference: &TestResult) -> bool { match reference { - TestResult::Ok(val) => result.as_ref() == Ok(val), - TestResult::GenericCheck(f, _) => f(result.clone()), + TestResult::Ok(val) => result.as_ref().unwrap() == val, + TestResult::GenericCheck(f, _) => f(result), TestResult::None => true, } } @@ -398,11 +394,32 @@ pub fn check_code_result(code: &str, expected: impl ToNaslResult) { #[macro_export] macro_rules! check_err_matches { ($t: ident, $code: expr, $pat: pat $(,)?) => { - $t.check($code, |e| matches!(e, Err($pat)), Some(stringify!($pat))); + $t.check( + $code, + |e| { + if let Err(e) = e { + // Convert with try_into to allow using + // the variants of `FnErrorKind` directly without + // having to wrap them in the outer enum. + let converted = e.try_into(); + // This is only irrefutable for the + // FnError -> FnError conversion but not for others. + #[allow(irrefutable_let_patterns)] + if let Ok(e) = converted { + matches!(e, &$pat) + } else { + false + } + } else { + false + } + }, + Some(stringify!($pat)), + ); }; ($code: expr, $pat: pat $(,)?) => { let mut t = $crate::nasl::test_utils::TestBuilder::default(); - t.check($code, |e| matches!(e, Err($pat)), Some(stringify!($pat))); + check_err_matches!(t, $code, $pat); }; } diff --git a/rust/src/nasl/utils/README.md b/rust/src/nasl/utils/README.md index 71c0b9e50..772ba6d17 100644 --- a/rust/src/nasl/utils/README.md +++ b/rust/src/nasl/utils/README.md @@ -2,5 +2,117 @@ Contains the necessary traits and helper functions to create builtin functions. -To register your function you have to add it into the context of an interpreter. -Usually that is done by adding it to [nasl-builtin-std::nasl_std_functions] so that it is registered on an default interpreter run. +# Error handling +This section briefly describes how to handle errors that occur during builtin functions. Builtin functions return a result with an error type `FnError`. This is a type that contains metadata about the error as well as its kind, described by the `FnErrorKind` enum, which is structured as follows + + +```rust +pub enum FnErrorKind { + Argument(ArgumentError), + Internal(InternalError), + Builtin(BuiltinError), +} +``` + +The variants represent three different kinds of errors: `ArgumentError` refers to errors caused by calling a function with the wrong arguments which typically reflect an error in the usage of the function by the script authors. + +`InternalError` refers to any error caused by failure of an internal process of the scanner (such as the storage). This reflects a deeper problem and may mean we cannot simply proceed with execution of the scan. + +Finally, `BuiltinError` represents any (more or less expected) error that occurred within a builtin function. This may include timeouts, authentication failures, etc.. + +## Metadata +As stated above, the metadata (return value, retryable) on how to handle a specific error are stored in a `FnError`. This means that authors of builtin functions always have the option of explicitly overriding any defaults for any specific case. However, in practice, the defaults should reflect the behavior what we want most of the time. + +### Return behavior +The return behavior of a given error specifies how an error should be handled during execution. This is specified by the following type: +```rust +enum ReturnBehavior { + ExitScript, + ReturnValue(NaslValue), +} +``` +When the interpreter encounters an error with `ReturnBehavior::ExitScript` behavior, it will unsurprisingly exit the script. If it encounters an error with `ReturnBehavior::ReturnValue(val)`, it will return `val` and continue execution. + +In the corresponding `From` impls, the `Argument` and `Internal` variants of `FnError` are automatically constructed with `ReturnBehavior::ExitScript`, meaning that they abort execution of the script. The `Builtin` variant is constructed with `ReturnBehavior::ReturnValue(NaslValue::Null)` by default, but this value can easily be overwritten when the error is created, for example: +```rust +HttpError::HandleIdNotFound(handle).with(ReturnValue(-1)) +``` + +### Retry behavior +Certain errors can be flagged as being solvable by retrying the operation that caused them. This is represented by a `retryable` boolean on `FnError`, which is `false` by default for all variants except for a specific internal error in the storage. However, this default behavior can be overwritten at error creation if needed, for example +```rust +HttpError::ConnectionFailed(...).with(Retryable) +``` +I also added a small test to make sure that the interpreter does actually retry retryable errors. + +## How to add a new error type for a builtin module +1. Add a custom error type for the builtin module. These can be of arbitrary form but a typical error type might look like +```rust +#[derive(Debug, Error)] +enum FooError { + #[error("Bar occurred.")] + Bar, + #[error("Baz occurred. Here is some more data: {0}")] + Baz(SomeData), +} +``` +This helps with keeping the error messages all in one place to ensure a common form. + +2. Add this builtin error as a variant of the `BuiltinError` type described above. +```rust +enum BuiltinError { + ... + Foo(FooError), + ... +} +``` + +3. For convenience, some `From` impls and `TryFrom` impls can make the error type easier to use by enabling use of the question mark operator. I added a tiny macro that implements these traits (because the implementations are usually trivial), so this comes down to one line too: +``` +builtin_error_variant!(Foo, FooError); +``` + +This is all that is needed to make this error usable in NASL functions: +```rust +fn check(a: usize) -> Result<(), FooError> { + if a == 0 { + Err(FooError::Bar) + } + else { + Ok(()) + } +} + +#[nasl_function] +fn foo(a: usize) -> Result { + check(a)?; + Ok(a) +} +``` + +As a side note, NASL functions can also return any concrete `impl Into` directly, so for this case we can also write + +```rust +#[nasl_function] +fn foo(a: usize) -> Result { + if a == 0 { + Err(FooError::Bar) + } + else { + Ok(a) + } +} +``` + +Note that the above `From` impls that are automatically written by the `builtin_error_variant!` macro can also be manually implemented if one wants to specify defaults for a specific error variant. For example + +```rust +impl From for FnError { + fn from(e: FooError) -> FnError { + match e { + FooError::Foo => BuiltinError::Foo(e).with(ReturnValue(-1)), + _ => BuiltinError::Foo(e).into(), + } + } +} +``` diff --git a/rust/src/nasl/utils/error.rs b/rust/src/nasl/utils/error.rs index 797d9fba9..409d0cf6d 100644 --- a/rust/src/nasl/utils/error.rs +++ b/rust/src/nasl/utils/error.rs @@ -2,143 +2,213 @@ // // SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception -//! Defines function error kinds -use std::io; use thiserror::Error; +use crate::nasl::builtin::BuiltinError; use crate::nasl::prelude::NaslValue; use crate::storage::StorageError; -use super::super::builtin::SshError; -use super::ContextType; - -/// Reuses the StorageError definitions as they should fit most cases. -pub type GeneralErrorType = StorageError; - -#[derive(Debug, Clone, PartialEq, Eq, Error)] -/// Descriptive kind of error that can occur while calling a function -pub enum FunctionErrorKind { - /// Function called with insufficient arguments - #[error("Expected {expected} but got {got}")] - MissingPositionalArguments { - /// Expected amount of arguments - expected: usize, - /// Actual amount of arguments - got: usize, - }, - /// Function called with trailing positional arguments - #[error("Expected {expected} but got {got}")] - TrailingPositionalArguments { - /// Expected amount of arguments - expected: usize, - /// Actual amount of arguments - got: usize, - }, - /// Function called without required named arguments - #[error("Missing arguments: {}", .0.join(", "))] - MissingArguments(Vec), - /// Function called with additional, unexpected named arguments - #[error("Unknown named argument given to function: {}", .0)] +#[derive(Debug, Error)] +#[error("{kind}")] +pub struct FnError { + #[source] + pub kind: FnErrorKind, + return_behavior: ReturnBehavior, + retryable: bool, +} + +#[derive(Debug)] +pub enum ReturnBehavior { + ExitScript, + ReturnValue(NaslValue), +} + +impl FnError { + pub fn return_behavior(&self) -> &ReturnBehavior { + &self.return_behavior + } + + pub fn retryable(&self) -> bool { + self.retryable + } + + fn from_kind(kind: FnErrorKind) -> FnError { + Self { + kind, + return_behavior: ReturnBehavior::ExitScript, + retryable: false, + } + } +} + +impl From for FnError { + fn from(kind: FnErrorKind) -> Self { + FnError::from_kind(kind) + } +} + +impl From for FnError { + fn from(kind: ArgumentError) -> Self { + FnError::from_kind(FnErrorKind::Argument(kind)) + } +} + +impl From for FnError { + fn from(kind: BuiltinError) -> Self { + FnError { + kind: FnErrorKind::Builtin(kind), + retryable: false, + return_behavior: ReturnBehavior::ReturnValue(NaslValue::Null), + } + } +} + +impl From for FnError { + fn from(kind: InternalError) -> Self { + let retryable = kind.retryable(); + Self { + kind: FnErrorKind::Internal(kind), + retryable, + return_behavior: ReturnBehavior::ExitScript, + } + } +} + +#[derive(Debug, Error)] +pub enum FnErrorKind { + #[error("{0}")] + Argument(ArgumentError), + #[error("{0}")] + Builtin(BuiltinError), + #[error("{0}")] + Internal(InternalError), +} + +#[derive(Debug, Clone, PartialEq, Error)] +pub enum ArgumentError { + #[error("Missing positional arguments. Expected {expected} but got {got}.")] + MissingPositionals { expected: usize, got: usize }, + #[error("Trailing positional arguments. Expected {expected} but got {got}.")] + TrailingPositionals { expected: usize, got: usize }, + #[error("Missing named arguments: {}", .0.join(", "))] + MissingNamed(Vec), + #[error("Unknown named argument given: {}", .0)] UnexpectedArgument(String), - /// Wraps formatting error - #[error("Formatting error: {0}")] - FMTError(#[from] std::fmt::Error), - /// Wraps io::Error - #[error("IOError: {0}")] - IOError(io::ErrorKind), - /// Function was called with wrong arguments - #[error("Function was called with wrong arguments: {0}")] + #[error("Wrong arguments given: {0}")] WrongArgument(String), - /// Authentication failed - #[error("Authentication failed.")] - Authentication, - /// Diagnostic string is informational and the second arg is the return value for the user - #[error("{0}")] - Diagnostic(String, Option), - /// Generic error - #[error("Generic error: {0}")] - GeneralError(#[from] GeneralErrorType), - /// There is a deeper problem - /// An example would be that there is no free memory left in the system +} + +#[derive(Debug, Clone, PartialEq, Error)] +pub enum InternalError { #[error("{0}")] - Dirty(String), - /// An Error originating from an SSH-specific NASL function - #[error("SSH error: {0}")] - Ssh(SshError), + Storage(#[from] StorageError), } -// It would be nicer to derive this using #[from] from -// thiserror, but io::Error does not impl `PartialEq`, -// `Eq` or `Clone`, so we wrap `io::ErrorKind` instead, which -// does not impl `Error` which is why this `From` impl exists. -impl From for FunctionErrorKind { - fn from(e: io::Error) -> Self { - Self::IOError(e.kind()) +impl InternalError { + fn retryable(&self) -> bool { + // Keep this match exhaustive without a catchall + // to make sure we implement future internal errors + // properly. + match self { + InternalError::Storage(StorageError::Retry(_)) => true, + InternalError::Storage(_) => false, + } } } -impl FunctionErrorKind { - /// Helper function to quickly construct a `WrongArgument` variant - /// containing the name of the argument, the expected value and - /// the actual value. - pub fn wrong_argument(key: &str, expected: &str, got: &str) -> Self { - Self::WrongArgument(format!("Expected {key} to be {expected} but it is {got}")) +impl From for FnError { + fn from(value: StorageError) -> Self { + FnErrorKind::Internal(InternalError::Storage(value)).into() } +} - /// Helper function to quickly construct a `WrongArgument` variant - /// containing the name of the argument, the expected value and - /// the actual value. - pub fn wrong_unnamed_argument(expected: &str, got: &str) -> Self { - Self::WrongArgument(format!("Expected {expected} but {got}")) +impl<'a> TryFrom<&'a FnError> for &'a ArgumentError { + type Error = (); + + fn try_from(value: &'a FnError) -> Result { + match &value.kind { + FnErrorKind::Argument(e) => Ok(e), + _ => Err(()), + } } +} - /// Helper function to quickly construct a `MissingArguments` variant - /// for a single missing argument. - pub fn missing_argument(val: &str) -> Self { - Self::MissingArguments(vec![val.to_string()]) +impl<'a> TryFrom<&'a FnError> for &'a InternalError { + type Error = (); + + fn try_from(value: &'a FnError) -> Result { + match &value.kind { + FnErrorKind::Internal(e) => Ok(e), + _ => Err(()), + } } +} - /// Helper function to quickly construct a `MissingArguments` variant - /// for a single missing argument and returning a NaslValue::Null - pub fn diagnostic_ret_null(val: &str) -> Self { - Self::Diagnostic(val.to_string(), Some(NaslValue::Null)) +impl<'a> TryFrom<&'a FnError> for &'a BuiltinError { + type Error = (); + + fn try_from(value: &'a FnError) -> Result { + match &value.kind { + FnErrorKind::Builtin(e) => Ok(e), + _ => Err(()), + } } } -impl From<(&str, &str, &NaslValue)> for FunctionErrorKind { - fn from(value: (&str, &str, &NaslValue)) -> Self { - let (key, expected, got) = value; - let got: &str = &got.to_string(); - FunctionErrorKind::wrong_argument(key, expected, got) +pub trait WithErrorInfo { + type Error; + fn with(self, e: Info) -> Self::Error; +} + +pub struct Retryable; + +impl> WithErrorInfo for E { + type Error = FnError; + + fn with(self, _: Retryable) -> Self::Error { + let mut e = self.into(); + e.retryable = true; + e } } -impl From<(&str, &str, Option<&NaslValue>)> for FunctionErrorKind { - fn from(value: (&str, &str, Option<&NaslValue>)) -> Self { - match value { - (key, expected, Some(x)) => (key, expected, x).into(), - (key, expected, None) => FunctionErrorKind::wrong_argument(key, expected, "NULL"), - } +pub struct ReturnValue(pub T); + +impl, E: Into> WithErrorInfo> for E { + type Error = FnError; + + fn with(self, val: ReturnValue) -> Self::Error { + let mut e = self.into(); + let val = val.0.into(); + e.return_behavior = ReturnBehavior::ReturnValue(val); + e } } -impl From<(&str, &str, Option<&ContextType>)> for FunctionErrorKind { - fn from(value: (&str, &str, Option<&ContextType>)) -> Self { - match value { - (key, expected, Some(ContextType::Value(x))) => (key, expected, x).into(), - (key, expected, Some(ContextType::Function(_, _))) => { - FunctionErrorKind::wrong_argument(key, expected, "function") - } - (key, expected, None) => FunctionErrorKind::wrong_argument(key, expected, "NULL"), - } +impl ArgumentError { + /// Helper function to quickly construct a `WrongArgument` variant + /// containing the name of the argument, the expected value and + /// the actual value. + pub fn wrong_argument(key: &str, expected: &str, got: &str) -> Self { + ArgumentError::WrongArgument(format!("Expected {key} to be {expected} but it is {got}")) } } -impl From<(&str, &NaslValue)> for FunctionErrorKind { - fn from(value: (&str, &NaslValue)) -> Self { - let (expected, got) = value; - let got: &str = &got.to_string(); - FunctionErrorKind::wrong_unnamed_argument(expected, got) +impl FnError { + /// Helper function to quickly construct a `WrongArgument` variant + /// containing the name of the argument, the expected value and + /// the actual value. + pub fn wrong_unnamed_argument(expected: &str, got: &str) -> Self { + FnErrorKind::Argument(ArgumentError::WrongArgument(format!( + "Expected {expected} but {got}" + ))) + .into() + } + + /// Helper function to quickly construct a `MissingArguments` variant + /// for a single missing argument. + pub fn missing_argument(val: &str) -> Self { + FnErrorKind::Argument(ArgumentError::MissingNamed(vec![val.to_string()])).into() } } diff --git a/rust/src/nasl/utils/function/from_nasl_value.rs b/rust/src/nasl/utils/function/from_nasl_value.rs index e461b8684..d8c1cdb9b 100644 --- a/rust/src/nasl/utils/function/from_nasl_value.rs +++ b/rust/src/nasl/utils/function/from_nasl_value.rs @@ -6,90 +6,78 @@ use crate::nasl::prelude::*; /// The conversion may fail. pub trait FromNaslValue<'a>: Sized { /// Perform the conversion - fn from_nasl_value(value: &'a NaslValue) -> Result; + fn from_nasl_value(value: &'a NaslValue) -> Result; } impl<'a> FromNaslValue<'a> for NaslValue { - fn from_nasl_value(value: &'a NaslValue) -> Result { + fn from_nasl_value(value: &'a NaslValue) -> Result { Ok(value.clone()) } } impl<'a> FromNaslValue<'a> for &'a NaslValue { - fn from_nasl_value(value: &'a NaslValue) -> Result { + fn from_nasl_value(value: &'a NaslValue) -> Result { Ok(value) } } impl FromNaslValue<'_> for String { - fn from_nasl_value(value: &NaslValue) -> Result { + fn from_nasl_value(value: &NaslValue) -> Result { match value { NaslValue::String(string) => Ok(string.to_string()), - _ => Err(FunctionErrorKind::WrongArgument( - "Expected string.".to_string(), - )), + _ => Err(ArgumentError::WrongArgument("Expected string.".to_string()).into()), } } } impl<'a> FromNaslValue<'a> for &'a str { - fn from_nasl_value(value: &'a NaslValue) -> Result { + fn from_nasl_value(value: &'a NaslValue) -> Result { match value { NaslValue::String(string) => Ok(string), - _ => Err(FunctionErrorKind::WrongArgument( - "Expected string.".to_string(), - )), + _ => Err(ArgumentError::WrongArgument("Expected string.".to_string()).into()), } } } impl<'a> FromNaslValue<'a> for &'a [u8] { - fn from_nasl_value(value: &'a NaslValue) -> Result { + fn from_nasl_value(value: &'a NaslValue) -> Result { match value { NaslValue::Data(bytes) => Ok(bytes), - _ => Err(FunctionErrorKind::WrongArgument( - "Expected byte data.".to_string(), - )), + _ => Err(ArgumentError::WrongArgument("Expected byte data.".to_string()).into()), } } } impl<'a, T: FromNaslValue<'a>> FromNaslValue<'a> for Vec { - fn from_nasl_value(value: &'a NaslValue) -> Result { + fn from_nasl_value(value: &'a NaslValue) -> Result { match value { NaslValue::Array(vals) => Ok(vals .iter() .map(T::from_nasl_value) - .collect::, FunctionErrorKind>>()?), - _ => Err(FunctionErrorKind::WrongArgument( - "Expected an array..".to_string(), - )), + .collect::, FnError>>()?), + _ => Err(ArgumentError::WrongArgument("Expected an array..".to_string()).into()), } } } impl<'a, T: FromNaslValue<'a>> FromNaslValue<'a> for HashMap { - fn from_nasl_value(value: &'a NaslValue) -> Result { + fn from_nasl_value(value: &'a NaslValue) -> Result { match value { NaslValue::Dict(map) => Ok(map .iter() .map(|(k, v)| T::from_nasl_value(v).map(|v| (k.clone(), v))) .collect::, _>>()?), - _ => Err(FunctionErrorKind::WrongArgument( - "Expected a dictionary.".to_string(), - )), + _ => Err(ArgumentError::WrongArgument("Expected a dictionary.".to_string()).into()), } } } impl<'a> FromNaslValue<'a> for bool { - fn from_nasl_value(value: &'a NaslValue) -> Result { + fn from_nasl_value(value: &'a NaslValue) -> Result { match value { NaslValue::Boolean(b) => Ok(*b), NaslValue::Number(n) => Ok(*n != 0), - _ => Err(FunctionErrorKind::WrongArgument( - "Expected bool.".to_string(), - )), + _ => Err(ArgumentError::WrongArgument("Expected bool.".to_string()).into()), } } } @@ -97,15 +85,16 @@ impl<'a> FromNaslValue<'a> for bool { macro_rules! impl_from_nasl_value_for_numeric_type { ($ty: ty) => { impl<'a> FromNaslValue<'a> for $ty { - fn from_nasl_value(value: &NaslValue) -> Result { + fn from_nasl_value(value: &NaslValue) -> Result { match value { NaslValue::Number(num) => Ok(<$ty>::try_from(*num).map_err(|_| { - FunctionErrorKind::WrongArgument("Expected positive number.".into()) + ArgumentError::WrongArgument("Expected positive number.".into()) })?), - e => Err(FunctionErrorKind::WrongArgument(format!( + e => Err(ArgumentError::WrongArgument(format!( "Expected a number, found '{}'.", e - ))), + )) + .into()), } } } diff --git a/rust/src/nasl/utils/function/maybe.rs b/rust/src/nasl/utils/function/maybe.rs index 28085e718..f7e868892 100644 --- a/rust/src/nasl/utils/function/maybe.rs +++ b/rust/src/nasl/utils/function/maybe.rs @@ -1,6 +1,6 @@ use crate::nasl::syntax::NaslValue; -use crate::nasl::FunctionErrorKind; +use crate::nasl::FnError; use super::FromNaslValue; @@ -12,7 +12,7 @@ use super::FromNaslValue; pub struct Maybe(Option); impl<'a, T: FromNaslValue<'a>> FromNaslValue<'a> for Maybe { - fn from_nasl_value(value: &'a NaslValue) -> Result { + fn from_nasl_value(value: &'a NaslValue) -> Result { Ok(Self(T::from_nasl_value(value).ok())) } } diff --git a/rust/src/nasl/utils/function/positionals.rs b/rust/src/nasl/utils/function/positionals.rs index bd29d26f5..58cb1bef6 100644 --- a/rust/src/nasl/utils/function/positionals.rs +++ b/rust/src/nasl/utils/function/positionals.rs @@ -1,6 +1,6 @@ use std::{marker::PhantomData, ops::Index}; -use crate::nasl::{FunctionErrorKind, Register}; +use crate::nasl::{FnError, Register}; use super::FromNaslValue; @@ -22,9 +22,9 @@ impl<'a, T: FromNaslValue<'a>> Positionals<'a, T> { } /// Returns an iterator over the positional arguments. - /// The item type is Result, since + /// The item type is Result, since /// the conversion to T can still fail. - pub fn iter(&self) -> impl Iterator> + 'a { + pub fn iter(&self) -> impl Iterator> + 'a { self.register .positional() .iter() @@ -45,12 +45,12 @@ pub struct CheckedPositionals { impl<'a, T: FromNaslValue<'a>> CheckedPositionals { /// Create a new `CheckedPositionals` from the register. - pub fn new(register: &'a Register) -> Result { + pub fn new(register: &'a Register) -> Result { let data = register .positional() .iter() .map(T::from_nasl_value) - .collect::, FunctionErrorKind>>()?; + .collect::, FnError>>()?; Ok(Self { data, _marker: PhantomData, diff --git a/rust/src/nasl/utils/function/to_nasl_result.rs b/rust/src/nasl/utils/function/to_nasl_result.rs index ab48aa7eb..c96d004d9 100644 --- a/rust/src/nasl/utils/function/to_nasl_result.rs +++ b/rust/src/nasl/utils/function/to_nasl_result.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crate::nasl::syntax::NaslValue; -use crate::nasl::{FunctionErrorKind, NaslResult}; +use crate::nasl::{FnError, NaslResult}; /// A type that can be converted to a NaslResult. /// The conversion is fallible to make it possible to convert from other Result @@ -26,7 +26,7 @@ impl ToNaslResult for Option { } } -impl> ToNaslResult for Result { +impl> ToNaslResult for Result { fn to_nasl_result(self) -> NaslResult { self.map_err(|e| e.into()).and_then(|x| x.to_nasl_result()) } @@ -67,7 +67,7 @@ impl ToNaslResult for Vec<&str> { Ok(NaslValue::Array( self.into_iter() .map(|s| s.to_nasl_result()) - .collect::, FunctionErrorKind>>()?, + .collect::, FnError>>()?, )) } } @@ -77,7 +77,7 @@ impl ToNaslResult for Vec { Ok(NaslValue::Array( self.into_iter() .map(|s| s.to_nasl_result()) - .collect::, FunctionErrorKind>>()?, + .collect::, FnError>>()?, )) } } @@ -93,7 +93,7 @@ impl ToNaslResult for HashMap { Ok(NaslValue::Dict( self.into_iter() .map(|(key, s)| s.to_nasl_result().map(|res| (key, res))) - .collect::, FunctionErrorKind>>()?, + .collect::, FnError>>()?, )) } } @@ -116,7 +116,7 @@ macro_rules! impl_to_nasl_result_for_numeric_type { impl_to_nasl_result_for_numeric_type!($ty, skip_vec_impl); impl ToNaslResult for Vec<$ty> { fn to_nasl_result(self) -> NaslResult { - let collected: Result, FunctionErrorKind> = + let collected: Result, FnError> = self.into_iter().map(|x| x.to_nasl_result()).collect(); Ok(NaslValue::Array(collected?)) } diff --git a/rust/src/nasl/utils/function/types.rs b/rust/src/nasl/utils/function/types.rs index 5da814b36..b4d911775 100644 --- a/rust/src/nasl/utils/function/types.rs +++ b/rust/src/nasl/utils/function/types.rs @@ -5,13 +5,13 @@ use crate::nasl::prelude::*; pub struct StringOrData(pub String); impl<'a> FromNaslValue<'a> for StringOrData { - fn from_nasl_value(value: &'a NaslValue) -> Result { + fn from_nasl_value(value: &'a NaslValue) -> Result { match value { NaslValue::String(string) => Ok(Self(string.clone())), NaslValue::Data(buffer) => Ok(Self(bytes_to_str(buffer))), - _ => Err(FunctionErrorKind::WrongArgument( - "Expected string or byte buffer.".to_string(), - )), + _ => Err( + ArgumentError::WrongArgument("Expected string or byte buffer.".to_string()).into(), + ), } } } diff --git a/rust/src/nasl/utils/function/utils.rs b/rust/src/nasl/utils/function/utils.rs index 0fbbc7990..94802d668 100644 --- a/rust/src/nasl/utils/function/utils.rs +++ b/rust/src/nasl/utils/function/utils.rs @@ -9,7 +9,7 @@ use crate::nasl::prelude::*; pub fn get_optional_positional_arg<'a, T: FromNaslValue<'a>>( register: &'a Register, position: usize, -) -> Result, FunctionErrorKind> { +) -> Result, FnError> { register .positional() .get(position) @@ -23,11 +23,11 @@ pub fn get_positional_arg<'a, T: FromNaslValue<'a>>( register: &'a Register, position: usize, num_required_positional_args: usize, -) -> Result { +) -> Result { let positional = register.positional(); let arg = positional.get(position).ok_or_else(|| { let num_given = positional.len(); - FunctionErrorKind::MissingPositionalArguments { + ArgumentError::MissingPositionals { expected: num_required_positional_args, got: num_given, } @@ -38,9 +38,9 @@ pub fn get_positional_arg<'a, T: FromNaslValue<'a>>( fn context_type_as_nasl_value<'a>( context_type: &'a ContextType, arg_name: &str, -) -> Result<&'a NaslValue, FunctionErrorKind> { +) -> Result<&'a NaslValue, ArgumentError> { match context_type { - ContextType::Function(_, _) => Err(FunctionErrorKind::WrongArgument(format!( + ContextType::Function(_, _) => Err(ArgumentError::WrongArgument(format!( "Wrong argument for {}, expected a value, found a function.", arg_name ))), @@ -53,7 +53,7 @@ fn context_type_as_nasl_value<'a>( pub fn get_optional_named_arg<'a, T: FromNaslValue<'a>>( register: &'a Register, name: &'a str, -) -> Result, FunctionErrorKind> { +) -> Result, FnError> { register .named(name) .map(|arg| context_type_as_nasl_value(arg, name)) @@ -67,10 +67,10 @@ pub fn get_optional_named_arg<'a, T: FromNaslValue<'a>>( pub fn get_named_arg<'a, T: FromNaslValue<'a>>( register: &'a Register, name: &'a str, -) -> Result { +) -> Result { let arg = register .named(name) - .ok_or_else(|| FunctionErrorKind::MissingArguments(vec![name.to_string()]))?; + .ok_or_else(|| ArgumentError::MissingNamed(vec![name.to_string()]))?; ::from_nasl_value(context_type_as_nasl_value(arg, name)?) } @@ -80,7 +80,7 @@ pub fn get_optional_maybe_named_arg<'a, T: FromNaslValue<'a>>( register: &'a Register, name: &'a str, position: usize, -) -> Result, FunctionErrorKind> { +) -> Result, FnError> { let via_position = get_optional_positional_arg::(register, position)?; if let Some(via_position) = via_position { Ok(Some(via_position)) @@ -95,7 +95,7 @@ pub fn get_maybe_named_arg<'a, T: FromNaslValue<'a>>( register: &'a Register, name: &'a str, position: usize, -) -> Result { +) -> Result { let via_position = get_optional_positional_arg(register, position)?; if let Some(via_position) = via_position { Ok(via_position) @@ -114,7 +114,7 @@ fn check_named_args( _nasl_fn_name: &str, named: &[&str], maybe_named: &[&str], -) -> Result { +) -> Result { let mut num_maybe_named = 0; for arg_name in register.iter_named_args().unwrap() { if arg_name == FC_ANON_ARGS || named.contains(&arg_name) { @@ -123,7 +123,7 @@ fn check_named_args( num_maybe_named += 1; } else { #[cfg(feature = "enforce-no-trailing-arguments")] - return Err(FunctionErrorKind::UnexpectedArgument(arg_name.into())); + return Err(ArgumentError::UnexpectedArgument(arg_name.into()).into()); #[cfg(not(feature = "enforce-no-trailing-arguments"))] tracing::debug!( "Unexpected named argument '{arg_name}' in NASL function {_nasl_fn_name}." @@ -142,17 +142,18 @@ pub fn check_args( named: &[&str], maybe_named: &[&str], max_num_expected_positional: Option, -) -> Result<(), FunctionErrorKind> { +) -> Result<(), FnError> { let num_maybe_named_given = check_named_args(register, _nasl_fn_name, named, maybe_named)?; let num_positional_given = register.positional().len(); if let Some(max_num_expected_positional) = max_num_expected_positional { let num_positional_expected = max_num_expected_positional - num_maybe_named_given; if num_positional_given > num_positional_expected { #[cfg(feature = "enforce-no-trailing-arguments")] - return Err(FunctionErrorKind::TrailingPositionalArguments { + return Err(ArgumentError::TrailingPositionals { expected: num_positional_expected, got: num_positional_given, - }); + } + .into()); #[cfg(not(feature = "enforce-no-trailing-arguments"))] tracing::debug!( "Trailing positional arguments in NASL function {_nasl_fn_name}. Expected {num_positional_expected}, found {num_positional_given}" diff --git a/rust/src/nasl/utils/hosts.rs b/rust/src/nasl/utils/hosts.rs index f3d105128..94102dd78 100644 --- a/rust/src/nasl/utils/hosts.rs +++ b/rust/src/nasl/utils/hosts.rs @@ -4,9 +4,11 @@ use std::net::{IpAddr, ToSocketAddrs}; -use crate::nasl::utils::error::FunctionErrorKind; +use crate::nasl::builtin::HostError; -pub fn resolve(mut hostname: String) -> Result, FunctionErrorKind> { +use super::FnError; + +pub fn resolve(mut hostname: String) -> Result, FnError> { //std::net to_socket_addrs() requires a port. Therefore, using a dummy port hostname.push_str(":5000"); @@ -16,6 +18,6 @@ pub fn resolve(mut hostname: String) -> Result, FunctionErrorKind> { Ok(ips) } // assumes that target is already a hostname - Err(_) => Err(FunctionErrorKind::diagnostic_ret_null("Missing Hostname")), + Err(_) => Err(HostError::TargetIsNotAHostname.into()), } } diff --git a/rust/src/nasl/utils/mod.rs b/rust/src/nasl/utils/mod.rs index afb7fa0ab..544cc4e77 100644 --- a/rust/src/nasl/utils/mod.rs +++ b/rust/src/nasl/utils/mod.rs @@ -12,13 +12,15 @@ pub mod lookup_keys; use std::collections::HashMap; -pub use context::{Context, ContextType, Register, Target}; -pub use error::FunctionErrorKind; +pub use context::{Context, ContextType, Register}; +pub use error::ArgumentError; +pub use error::FnError; +pub use error::InternalError; pub use executor::{Executor, IntoFunctionSet, StoredFunctionSet}; /// The result of a function call. -pub type NaslResult = Result; +pub type NaslResult = Result; /// Resolves positional arguments from the register. pub fn resolve_positional_arguments(register: &Register) -> Vec { @@ -47,11 +49,11 @@ pub fn get_named_parameter<'a>( registrat: &'a Register, key: &'a str, required: bool, -) -> Result<&'a crate::nasl::syntax::NaslValue, FunctionErrorKind> { +) -> Result<&'a crate::nasl::syntax::NaslValue, ArgumentError> { match registrat.named(key) { None => { if required { - Err(FunctionErrorKind::MissingArguments(vec![key.to_owned()])) + Err(ArgumentError::MissingNamed(vec![key.to_owned()])) } else { // we use exit because a named value can be intentionally set to null and may be // treated differently when it is not set compared to set but null. @@ -60,7 +62,7 @@ pub fn get_named_parameter<'a>( } Some(ct) => match ct { ContextType::Value(value) => Ok(value), - _ => Err(FunctionErrorKind::wrong_argument(key, "value", "function")), + _ => Err(ArgumentError::wrong_argument(key, "value", "function")), }, } } diff --git a/rust/src/scanner/error.rs b/rust/src/scanner/error.rs index 0054c59d3..545eedeec 100644 --- a/rust/src/scanner/error.rs +++ b/rust/src/scanner/error.rs @@ -24,7 +24,7 @@ pub enum ExecuteError { Parameter(crate::models::Parameter), } -#[derive(Debug, Clone)] +#[derive(Debug)] /// Contains the result of a executed script pub enum ScriptResultKind { /// Contains the code provided by exit call or 0 when script finished successful without exit @@ -46,7 +46,7 @@ pub enum ScriptResultKind { Error(InterpretError), } -#[derive(Debug, Clone)] +#[derive(Debug)] /// Contains meta data of the script and its result pub struct ScriptResult { /// Object identifier of the script @@ -66,10 +66,6 @@ impl ScriptResult { pub fn has_succeeded(&self) -> bool { matches!(&self.kind, ScriptResultKind::ReturnCode(0)) } - /// Returns true when the return code of the script not 0 - pub fn has_failed(&self) -> bool { - !self.has_succeeded() - } /// Returns true when the script didn't run pub fn has_not_run(&self) -> bool { diff --git a/rust/src/scanner/running_scan.rs b/rust/src/scanner/running_scan.rs index c357e4217..e2884fe3f 100644 --- a/rust/src/scanner/running_scan.rs +++ b/rust/src/scanner/running_scan.rs @@ -122,7 +122,7 @@ impl RunningScan { } debug!(result=?result, "script finished"); - if result.has_failed() { + if !result.has_succeeded() { end_phase = Phase::Failed; } } diff --git a/rust/src/scanner/scan_runner.rs b/rust/src/scanner/scan_runner.rs index 8cc68a796..221b93ced 100644 --- a/rust/src/scanner/scan_runner.rs +++ b/rust/src/scanner/scan_runner.rs @@ -118,10 +118,10 @@ pub(super) mod tests { use crate::models::Target; use crate::models::VT; use crate::nasl::syntax::NaslValue; + use crate::nasl::utils::context::Target as ContextTarget; use crate::nasl::utils::Context; use crate::nasl::utils::Executor; use crate::nasl::utils::Register; - use crate::nasl::utils::Target as ContextTarget; use crate::nasl::{interpreter::CodeInterpreter, nasl_std_functions}; use crate::scanner::{ error::{ExecuteError, ScriptResult}, @@ -395,18 +395,14 @@ exit({rc}); dispatcher: DefaultDispatcher, ) -> (Vec, Vec) { let result = run(vts.to_vec(), dispatcher).await.expect("success run"); - let success = result - .clone() + let (success, rest): (Vec<_>, Vec<_>) = result .into_iter() .filter_map(|x| x.ok()) - .filter(|x| x.has_succeeded()) - .collect::>(); - let failure = result + .partition(|x| x.has_succeeded()); + let failure = rest .into_iter() - .filter_map(|x| x.ok()) - .filter(|x| x.has_failed()) - .filter(|x| x.has_not_run()) - .collect::>(); + .filter(|x| !x.has_succeeded() && x.has_not_run()) + .collect(); (success, failure) } diff --git a/rust/src/scanner/vt_runner.rs b/rust/src/scanner/vt_runner.rs index 6a9c78831..0ed9b997c 100644 --- a/rust/src/scanner/vt_runner.rs +++ b/rust/src/scanner/vt_runner.rs @@ -209,7 +209,7 @@ impl<'a, Stack: ScannerStack> VTRunner<'a, Stack> { while let Some(r) = results.next().await { match r { Ok(NaslValue::Exit(x)) => return ScriptResultKind::ReturnCode(x), - Err(e) => return ScriptResultKind::Error(e.clone()), + Err(e) => return ScriptResultKind::Error(e), Ok(x) => { trace!(statement_result=?x); } diff --git a/rust/src/scannerctl/error.rs b/rust/src/scannerctl/error.rs index 7a4a40a88..18f3e1771 100644 --- a/rust/src/scannerctl/error.rs +++ b/rust/src/scannerctl/error.rs @@ -16,7 +16,7 @@ use scannerlib::{ scanner::ExecuteError, }; -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum CliErrorKind { WrongAction, @@ -52,7 +52,7 @@ impl CliErrorKind { } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct CliError { pub filename: String, pub kind: CliErrorKind, diff --git a/rust/src/scannerctl/interpret/mod.rs b/rust/src/scannerctl/interpret/mod.rs index 42ffa81ea..fcff5c871 100644 --- a/rust/src/scannerctl/interpret/mod.rs +++ b/rust/src/scannerctl/interpret/mod.rs @@ -8,9 +8,9 @@ use std::{ }; use futures::StreamExt; -use scannerlib::nasl::interpreter::CodeInterpreter; +use scannerlib::nasl::{interpreter::CodeInterpreter, utils::error::ReturnBehavior}; use scannerlib::nasl::{ - interpreter::{FunctionError, InterpretErrorKind}, + interpreter::InterpretErrorKind, prelude::*, syntax::{load_non_utf8_path, LoadError}, Loader, NoOpLoader, @@ -138,16 +138,19 @@ where while let Some(result) = results.next().await { let r = match result { Ok(x) => x, - Err(e) => match &e.kind { - InterpretErrorKind::FunctionCallError(FunctionError { - function: _, - kind: FunctionErrorKind::Diagnostic(_, x), - }) => { - tracing::warn!(error=?e, "function call error"); - x.clone().unwrap_or_default() + Err(e) => { + if let InterpretErrorKind::FunctionCallError(ref fe) = e.kind { + match fe.kind.return_behavior() { + ReturnBehavior::ExitScript => return Err(e.into()), + ReturnBehavior::ReturnValue(val) => { + tracing::warn!("{}", e.to_string()); + val.clone() + } + } + } else { + return Err(e.into()); } - _ => return Err(e.into()), - }, + } }; match r { NaslValue::Exit(rc) => std::process::exit(rc as i32), diff --git a/rust/src/storage/mod.rs b/rust/src/storage/mod.rs index 674901c1d..d9f80bbee 100644 --- a/rust/src/storage/mod.rs +++ b/rust/src/storage/mod.rs @@ -167,7 +167,7 @@ impl From for Field { } /// Defines abstract error cases -#[derive(Clone, Debug, PartialEq, Eq, Error)] +#[derive(Clone, Debug, PartialEq, Error)] pub enum StorageError { /// Informs the caller to retry the call #[error("There was a temporary issue while reading: {0}")]