Skip to content

Commit 42f3dba

Browse files
authored
Merge pull request #1772 from greenbone/function_set_improvements
`function_set!` improvements
2 parents 0608824 + d714276 commit 42f3dba

File tree

36 files changed

+393
-199
lines changed

36 files changed

+393
-199
lines changed

rust/crates/nasl-function-proc-macro/src/codegen.rs

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ impl<'a> ArgsStruct<'a> {
3333
})
3434
}
3535

36+
fn has_register_arg(&self) -> bool {
37+
self.args
38+
.iter()
39+
.any(|arg| matches!(arg.kind, ArgKind::Register))
40+
}
41+
3642
fn get_args(&self) -> TokenStream {
3743
self
3844
.args.iter().map(|arg| {
@@ -146,6 +152,9 @@ impl<'a> ArgsStruct<'a> {
146152
}
147153

148154
fn gen_checks(&self) -> TokenStream {
155+
if self.has_register_arg() {
156+
return quote! {};
157+
}
149158
let named_array = self.make_array_of_names(ArgKind::get_named_arg_name);
150159
let maybe_named_array = self.make_array_of_names(ArgKind::get_maybe_named_arg_name);
151160
let num_allowed_positional_args = if self.has_positional_iterator_arg() {
@@ -160,6 +169,43 @@ impl<'a> ArgsStruct<'a> {
160169
}
161170
}
162171

172+
fn impl_add_to_set(
173+
&self,
174+
ident: &Ident,
175+
fn_name: &Ident,
176+
asyncness: Option<Async>,
177+
) -> TokenStream {
178+
let nasl_function_expr = match (asyncness, &self.receiver_type) {
179+
(Some(_), ReceiverType::None) => {
180+
quote! { AsyncStateless(Box::new(#fn_name)) }
181+
}
182+
(Some(_), ReceiverType::RefSelf) => {
183+
quote! { AsyncStateful(Box::new(Self::#fn_name)) }
184+
}
185+
(Some(_), ReceiverType::RefMutSelf) => {
186+
quote! { AsyncStatefulMut(Box::new(Self::#fn_name)) }
187+
}
188+
(None, ReceiverType::None) => quote! { SyncStateless(#fn_name) },
189+
(None, ReceiverType::RefSelf) => {
190+
quote! { SyncStateful(Self::#fn_name) }
191+
}
192+
(None, ReceiverType::RefMutSelf) => {
193+
quote! { SyncStatefulMut(Self::#fn_name) }
194+
}
195+
};
196+
197+
let (generics, state_type) = match &self.receiver_type {
198+
ReceiverType::None => (quote! { < S > }, quote! { S }),
199+
ReceiverType::RefSelf | ReceiverType::RefMutSelf => (quote! {}, quote! { Self }),
200+
};
201+
202+
quote! {
203+
fn #ident #generics (set: &mut crate::nasl::utils::StoredFunctionSet<#state_type>, name: &str) {
204+
set.add_nasl_function(name, crate::nasl::utils::NaslFunction::#nasl_function_expr);
205+
}
206+
}
207+
}
208+
163209
pub fn impl_nasl_function_args(&self) -> TokenStream {
164210
let ItemFn {
165211
attrs,
@@ -193,21 +239,26 @@ impl<'a> ArgsStruct<'a> {
193239
};
194240
let asyncness = sig.asyncness;
195241
let checks = self.gen_checks();
196-
let mangled_name = format!("_internal_{}", ident);
197-
let mangled_ident = Ident::new(&mangled_name, ident.span());
198-
let inner_call = self.get_inner_call_expr(&mangled_ident, asyncness);
242+
let mangled_ident_original_fn = Ident::new(&format!("_internal_{}", ident), ident.span());
243+
let mangled_ident_transformed_fn =
244+
Ident::new(&(format!("_internal_convert_{}", ident)), ident.span());
245+
let inner_call = self.get_inner_call_expr(&mangled_ident_original_fn, asyncness);
246+
let add_to_set = self.impl_add_to_set(ident, &mangled_ident_transformed_fn, asyncness);
247+
199248
quote! {
200249
#[allow(clippy::too_many_arguments)]
201-
#asyncness fn #mangled_ident #generics ( #fn_args ) -> #output_ty {
250+
#asyncness fn #mangled_ident_original_fn #generics ( #fn_args ) -> #output_ty {
202251
#(#stmts)*
203252
}
204253

205-
#(#attrs)* #vis #asyncness #fn_token #ident #generics ( #inputs ) -> crate::nasl::NaslResult {
254+
#(#attrs)* #vis #asyncness #fn_token #mangled_ident_transformed_fn #generics ( #inputs ) -> crate::nasl::NaslResult {
206255
#checks
207256
#get_args
208257
let _result = #inner_call;
209258
<#output_ty as crate::nasl::ToNaslResult>::to_nasl_result(_result)
210259
}
260+
261+
#add_to_set
211262
}
212263
}
213264
}

rust/crates/nasl-function-proc-macro/src/lib.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,72 @@
1+
//! This crate provides the `nasl_function` proc macro, which is
2+
//! designed to make implementing new NASL builtin functions as
3+
//! convenient as possible.
4+
//!
5+
//! Design: There are two main purposes that the `nasl_function` macro
6+
//! serves.
7+
//!
8+
//! Purpose 1: Unify argument handling.
9+
//!
10+
//! The `nasl_function!` macro provides a structured approach to argument handling
11+
//! within NASL builtin functions. The macro takes as input a function
12+
//! taking any number of arguments, along with instructions on whether
13+
//! those arguments are named, positional, optional, etc. It then
14+
//! produces a function that automatically handles conversion of the
15+
//! arguments into the correct types and produces consistent error
16+
//! messages if the function has been called with an invalid set of
17+
//! arguments.
18+
//!
19+
//! To do so, the macro transforms the annotated function into a function
20+
//! taking `&Context` and `&Register` as arguments (plus self arguments
21+
//! if needed) and then calls the original function from within the transformed
22+
//! function, deriving each argument from the `FromNaslValue` implementation
23+
//! of its type and handling optional and named arguments appropriately.
24+
//!
25+
//! The macro renames the inner function into a proper, first class
26+
//! function instead of a closure in order to provide support for
27+
//! async functions (without relying on the unstable async
28+
//! closures).
29+
//!
30+
//! Purpose 2: Provide a uniform way to add builtin functions to function sets.
31+
//!
32+
//! NASL builtin functions come in one of several types, depending on
33+
//! their asyncness and whether they are stateless or stateful (and
34+
//! whether they require mutable access to their state, if they
35+
//! are). The `NaslFunction` type defined in the executor code is a
36+
//! singular type which can represent all the various variants of
37+
//! builtin functions. The executor also provides the
38+
//! `StoredFunctionSet`, which represents a set of `NaslFunction`s
39+
//! together with their state. This state struct is used both as the
40+
//! actual state that these functions require, as well as an
41+
//! identifying name. Together, the `NaslFunction` and
42+
//! `StoredFunctionSet` types provide the ability to store NASL
43+
//! functions in a type-erased way, so that the interpreter can run
44+
//! them independently of their properties.
45+
//!
46+
//! In order to provide a unified interface for adding NASL functions
47+
//! to `StoredFunctionSet`s, there needs to be a way to convert any of
48+
//! the 6 variants which builtin functions come in (sync_stateless,
49+
//! async_stateless, sync_stateful, ... ) into their corresponding
50+
//! variant of `NaslFunction`. On the surface, this problem sounds
51+
//! simple: Simply implement `Into<NaslFunction>` for `Fn(&Context,
52+
//! &Register) -> NaslResult` as well as for `Fn(&Context, &Register)
53+
//! -> Future<NaslResult>`, as well as for the other 4 variants. Then
54+
//! provide a `add_function` method on `StoredFunctionSet` that takes
55+
//! any `impl Into<NaslFunction>` as argument. The problem with this
56+
//! approach is that the Rust compiler cannot determine that these 6
57+
//! implementations are coherent, i.e. it believes that there might be
58+
//! a type `T` that implements multiple of these `Fn` traits
59+
//! simultaneously, which would result in overlapping trait impls.
60+
//!
61+
//! In order to solve this problem, the `nasl_function!` macro
62+
//! transforms the annotated function into a special function that
63+
//! takes a `StoredFunctionSet` and adds the correct variant of
64+
//! `NaslFunction` to the set. This is a very indirect approach, but
65+
//! it works because the `nasl_function!` macro knows exactly what the
66+
//! signature of the annotated function is and can therefore derive
67+
//! which of the 6 variants of `NaslFunction` it should become,
68+
//! without requiring type-erasure via an intermediate trait.
69+
170
mod codegen;
271
mod error;
372
mod parse;

rust/src/nasl/builtin/array/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ pub struct Array;
8484

8585
function_set! {
8686
Array,
87-
sync_stateless,
8887
(
8988
make_array,
9089
make_list,

rust/src/nasl/builtin/cert/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,6 @@ impl NaslCerts {
437437

438438
function_set! {
439439
NaslCerts,
440-
sync_stateful,
441440
(
442441
(NaslCerts::cert_open, "cert_open"),
443442
(NaslCerts::cert_close, "cert_close"),

rust/src/nasl/builtin/cryptographic/aes_cbc.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ where
7171
/// Currently the data is filled with zeroes. Therefore the length of the encrypted data must be
7272
/// known for decryption. If no length is given, the last block is decrypted as a whole.
7373
/// - The iv must have a length of 16 bytes
74-
fn aes128_cbc_encrypt(register: &Register, _: &Context) -> Result<NaslValue, FnError> {
74+
#[nasl_function]
75+
fn aes128_cbc_encrypt(register: &Register) -> Result<NaslValue, FnError> {
7576
cbc::<Aes128>(register, Crypt::Encrypt)
7677
}
7778

@@ -83,7 +84,8 @@ fn aes128_cbc_encrypt(register: &Register, _: &Context) -> Result<NaslValue, FnE
8384
/// Currently the data is filled with zeroes. Therefore the length of the encrypted data must be
8485
/// known for decryption. If no length is given, the last block is decrypted as a whole.
8586
/// - The iv must have a length of 16 bytes
86-
fn aes128_cbc_decrypt(register: &Register, _: &Context) -> Result<NaslValue, FnError> {
87+
#[nasl_function]
88+
fn aes128_cbc_decrypt(register: &Register) -> Result<NaslValue, FnError> {
8789
cbc::<Aes128>(register, Crypt::Decrypt)
8890
}
8991

@@ -94,7 +96,8 @@ fn aes128_cbc_decrypt(register: &Register, _: &Context) -> Result<NaslValue, FnE
9496
/// Currently the data is filled with zeroes. Therefore the length of the encrypted data must be
9597
/// known for decryption. If no length is given, the last block is decrypted as a whole.
9698
/// - The iv must have a length of 16 bytes
97-
fn aes192_cbc_encrypt(register: &Register, _: &Context) -> Result<NaslValue, FnError> {
99+
#[nasl_function]
100+
fn aes192_cbc_encrypt(register: &Register) -> Result<NaslValue, FnError> {
98101
cbc::<Aes192>(register, Crypt::Encrypt)
99102
}
100103

@@ -106,7 +109,8 @@ fn aes192_cbc_encrypt(register: &Register, _: &Context) -> Result<NaslValue, FnE
106109
/// Currently the data is filled with zeroes. Therefore the length of the encrypted data must be
107110
/// known for decryption. If no length is given, the last block is decrypted as a whole.
108111
/// - The iv must have a length of 16 bytes
109-
fn aes192_cbc_decrypt(register: &Register, _: &Context) -> Result<NaslValue, FnError> {
112+
#[nasl_function]
113+
fn aes192_cbc_decrypt(register: &Register) -> Result<NaslValue, FnError> {
110114
cbc::<Aes192>(register, Crypt::Decrypt)
111115
}
112116

@@ -117,7 +121,8 @@ fn aes192_cbc_decrypt(register: &Register, _: &Context) -> Result<NaslValue, FnE
117121
/// Currently the data is filled with zeroes. Therefore the length of the encrypted data must be
118122
/// known for decryption. If no length is given, the last block is decrypted as a whole.
119123
/// - The iv must have a length of 16 bytes
120-
fn aes256_cbc_encrypt(register: &Register, _: &Context) -> Result<NaslValue, FnError> {
124+
#[nasl_function]
125+
fn aes256_cbc_encrypt(register: &Register) -> Result<NaslValue, FnError> {
121126
cbc::<Aes256>(register, Crypt::Encrypt)
122127
}
123128

@@ -129,15 +134,15 @@ fn aes256_cbc_encrypt(register: &Register, _: &Context) -> Result<NaslValue, FnE
129134
/// Currently the data is filled with zeroes. Therefore the length of the encrypted data must be
130135
/// known for decryption. If no length is given, the last block is decrypted as a whole.
131136
/// - The iv must have a length of 16 bytes
132-
fn aes256_cbc_decrypt(register: &Register, _: &Context) -> Result<NaslValue, FnError> {
137+
#[nasl_function]
138+
fn aes256_cbc_decrypt(register: &Register) -> Result<NaslValue, FnError> {
133139
cbc::<Aes256>(register, Crypt::Decrypt)
134140
}
135141

136142
pub struct AesCbc;
137143

138144
function_set! {
139145
AesCbc,
140-
sync_stateless,
141146
(
142147
aes128_cbc_encrypt,
143148
aes128_cbc_decrypt,

0 commit comments

Comments
 (0)