wsdf_derive/
lib.rs

1//! This crate provides the derive macros for [wsdf](http://docs.rs/wsdf), along with some helpers.
2
3use proc_macro::TokenStream;
4
5use quote::{format_ident, quote};
6use syn::parse::{Parse, ParseStream};
7
8mod attributes;
9mod model;
10mod types;
11mod util;
12
13use crate::attributes::*;
14use crate::model::DataRoot;
15use crate::util::*;
16
17#[derive(Debug)]
18struct VersionMacroInput {
19    plugin_ver: syn::LitStr,
20    ws_major_ver: syn::LitInt,
21    ws_minor_ver: syn::LitInt,
22}
23
24impl Parse for VersionMacroInput {
25    fn parse(input: ParseStream) -> syn::Result<Self> {
26        let plugin_ver = Parse::parse(input)?;
27        <syn::Token![,]>::parse(input)?;
28        let ws_major_ver = Parse::parse(input)?;
29        <syn::Token![,]>::parse(input)?;
30        let ws_minor_ver = Parse::parse(input)?;
31        Ok(VersionMacroInput {
32            plugin_ver,
33            ws_major_ver,
34            ws_minor_ver,
35        })
36    }
37}
38
39/// Declares the plugin version and supported Wireshark version.
40///
41/// # Example
42///
43/// The following usage declares a plugin version of 0.0.1, built for wireshark version 4.0.x.
44///
45/// ```
46/// use wsdf_derive::version;
47/// version!("0.0.1", 4, 0);
48/// ```
49#[proc_macro]
50pub fn version(input: TokenStream) -> TokenStream {
51    let input = syn::parse_macro_input!(input as VersionMacroInput);
52
53    let nr_chars = input.plugin_ver.value().len() + 1;
54    let mut ver_str = Vec::with_capacity(nr_chars);
55    for ch in input.plugin_ver.value().as_bytes() {
56        ver_str.push(*ch as i8);
57    }
58    ver_str.push(0); // pad a null byte
59
60    let ws_major_ver = input.ws_major_ver;
61    let ws_minor_ver = input.ws_minor_ver;
62
63    let version_info = quote! {
64        #[no_mangle]
65        #[used]
66        static plugin_version: [std::ffi::c_char; #nr_chars] = [#(#ver_str),*];
67        #[no_mangle]
68        #[used]
69        static plugin_want_major: std::ffi::c_int = #ws_major_ver;
70        #[no_mangle]
71        #[used]
72        static plugin_want_minor: std::ffi::c_int = #ws_minor_ver;
73    };
74
75    version_info.into()
76}
77
78/// Marks a struct as the protocol root.
79#[proc_macro_derive(Protocol, attributes(wsdf))]
80pub fn derive_protocol(input: TokenStream) -> TokenStream {
81    let input = syn::parse_macro_input!(input as syn::DeriveInput);
82    let ret = derive_protocol_impl(&input).unwrap_or_else(|e| e.to_compile_error());
83    ret.into()
84}
85
86fn derive_protocol_impl(input: &syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
87    match &input.data {
88        syn::Data::Enum(_) | syn::Data::Union(_) => {
89            return make_err(input, "only structs can derive Protocol");
90        }
91        syn::Data::Struct(_) => (),
92    }
93
94    let root = DataRoot::from_input(input, true)?;
95    let proto_opts = init_options::<ProtocolOptions>(&input.attrs)?;
96
97    if proto_opts.decode_from.is_empty() {
98        return make_err(
99            &input.ident,
100            "expected some way of registering with dissector table",
101        );
102    }
103
104    let add_dissector = proto_opts.decode_from.iter().map(DecodeFrom::to_tokens);
105
106    let upper_cased = input.ident.to_wsdf_upper_case();
107    let snake_cased = input.ident.to_wsdf_snake_case();
108
109    let proto_desc = proto_opts.proto_desc.as_ref().unwrap_or(&upper_cased);
110    let proto_name = proto_opts.proto_name.as_ref().unwrap_or(&upper_cased);
111    let proto_filter = proto_opts.proto_filter.as_ref().unwrap_or(&snake_cased);
112
113    let proto_desc_cstr: syn::Expr = cstr!(proto_desc);
114    let proto_name_cstr: syn::Expr = cstr!(proto_name);
115    let proto_filter_cstr: syn::Expr = cstr!(proto_filter);
116
117    let dissect_fn = root.dissection_fn();
118    let register_fn = root.registration_fn();
119
120    let input_ident = &input.ident;
121
122    let plugin_register = quote! {
123        #[no_mangle]
124        extern "C" fn plugin_register() {
125            static mut plug: wsdf::epan_sys::proto_plugin = wsdf::epan_sys::proto_plugin {
126                register_protoinfo: None,
127                register_handoff: None,
128            };
129            // SAFETY: this code is only called once in a single thread when wireshark starts
130            unsafe {
131                plug.register_protoinfo =
132                    std::option::Option::Some(<#input_ident as wsdf::Protocol>::proto_register);
133                plug.register_handoff =
134                    std::option::Option::Some(<#input_ident as wsdf::Protocol>::proto_reg_handoff);
135                wsdf::epan_sys::proto_register_plugin(&plug);
136            }
137        }
138    };
139
140    let init_rust_owned_tvb_buf = init_tvb_buf();
141
142    let main_dissect_fn = quote! {
143        unsafe extern "C" fn dissect_main(
144            #WSDF_TVB: *mut wsdf::epan_sys::tvbuff,
145            #WSDF_PINFO: *mut wsdf::epan_sys::_packet_info,
146            #WSDF_PROTO_TREE_ROOT: *mut wsdf::epan_sys::_proto_node,
147            __wsdf_data: *mut std::ffi::c_void, // unused
148        ) -> std::ffi::c_int {
149            wsdf::epan_sys::col_set_str(
150                (*#WSDF_PINFO).cinfo,
151                wsdf::epan_sys::COL_PROTOCOL as std::ffi::c_int,
152                #proto_desc_cstr,
153            );
154            wsdf::epan_sys::col_clear(
155                (*#WSDF_PINFO).cinfo,
156                wsdf::epan_sys::COL_INFO as std::ffi::c_int,
157            );
158
159            #init_rust_owned_tvb_buf
160
161            // Initialize a context to keep fields.
162            let mut #WSDF_FIELDS_STORE = wsdf::FieldsStore::default();
163
164            <#input_ident as wsdf::ProtocolField>::dissect(
165                0,
166                #WSDF_TVB,
167                #WSDF_PROTO_TREE_ROOT,
168                #proto_filter,
169                wsdf::VariantDispatch::None,
170                wsdf::SubtreeLabel::new(#proto_name_cstr),
171                &#WSDF_TVB_BUF,
172                #WSDF_PINFO,
173                #WSDF_PROTO_TREE_ROOT,
174                &mut #WSDF_FIELDS_STORE,
175            )
176        }
177    };
178
179    let protoinfo_fn = quote! {
180        extern "C" fn proto_register() {
181            let proto_id = unsafe {
182                wsdf::epan_sys::proto_register_protocol(
183                    #proto_desc_cstr,
184                    #proto_name_cstr,
185                    #proto_filter_cstr,
186                )
187            };
188            <#input_ident as wsdf::ProtocolField>::register(
189                #proto_filter,
190                proto_id,
191                wsdf::FieldIdent::null(),
192                wsdf::FieldBlurb::null(),
193            );
194        }
195    };
196
197    let handoff_fn = quote! {
198        extern "C" fn proto_reg_handoff() {
199            unsafe {
200                let handle = wsdf::epan_sys::create_dissector_handle(
201                    std::option::Option::Some(<#input_ident as wsdf::Protocol>::dissect_main),
202                    *<#input_ident as wsdf::ProtocolField>::proto_id(),
203                );
204                #(#add_dissector)*
205            }
206        }
207    };
208
209    let static_int_getters = static_int_getters();
210    let static_maps = static_map_fns();
211
212    let ret = quote! {
213        #plugin_register
214
215        impl wsdf::Protocol for #input_ident {
216            #main_dissect_fn
217            #protoinfo_fn
218            #handoff_fn
219        }
220
221        impl wsdf::ProtocolField for #input_ident {
222            #dissect_fn
223            #register_fn
224
225            #static_int_getters
226            #static_maps
227        }
228    };
229
230    Ok(ret)
231}
232
233/// Creates the code to initialize a Rust owned TVB slice.
234fn init_tvb_buf() -> proc_macro2::TokenStream {
235    const WSDF_TVB_BUF_SIZE: IdentHelper = IdentHelper("__wsdf_tvb_buf_size");
236    quote! {
237        let #WSDF_TVB_BUF_SIZE = unsafe {
238            wsdf::epan_sys::tvb_reported_length(#WSDF_TVB) as usize
239        };
240        let mut #WSDF_TVB_BUF = Vec::new();
241        #WSDF_TVB_BUF.resize(#WSDF_TVB_BUF_SIZE, 0);
242        unsafe {
243            wsdf::epan_sys::tvb_memcpy(
244                #WSDF_TVB,
245                #WSDF_TVB_BUF.as_mut_ptr() as *mut std::ffi::c_void,
246                0,
247                #WSDF_TVB_BUF_SIZE,
248            );
249        }
250
251    }
252}
253
254/// Registers a type to be used as a field within the main `#[derive(Protocol)]` type.
255#[proc_macro_derive(ProtocolField, attributes(wsdf))]
256pub fn derive_protocol_field(input: TokenStream) -> TokenStream {
257    let input = syn::parse_macro_input!(input as syn::DeriveInput);
258    let ret = derive_protocol_field_impl(&input).unwrap_or_else(|e| e.to_compile_error());
259    ret.into()
260}
261
262fn derive_protocol_field_impl(input: &syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
263    let root = DataRoot::from_input(input, false)?;
264
265    let input_ident = &input.ident;
266    let dissect_fn = root.dissection_fn();
267    let register_fn = root.registration_fn();
268
269    let static_int_getters = static_int_getters();
270    let static_maps = static_map_fns();
271
272    let ret = quote! {
273        impl wsdf::ProtocolField for #input_ident {
274            #dissect_fn
275            #register_fn
276
277            #static_int_getters
278            #static_maps
279        }
280    };
281
282    Ok(ret)
283}
284
285fn static_int_getters() -> proc_macro2::TokenStream {
286    quote! {
287        fn ett() -> std::ffi::c_int {
288            static mut ETT: std::ffi::c_int = -1;
289
290            // The ETT will be initialized once the first time we all it.
291            static INIT_ETT: std::sync::Once = std::sync::Once::new();
292            INIT_ETT.call_once(|| unsafe {
293                debug_assert_eq!(ETT, -1);
294                wsdf::epan_sys::proto_register_subtree_array(
295                    [unsafe { &mut ETT as *mut _ }].as_mut_ptr(),
296                    1
297                );
298            });
299
300            unsafe { ETT }
301        }
302
303        fn proto_id() -> &'static mut std::ffi::c_int {
304            static mut PROTO_ID: std::ffi::c_int = -1;
305            unsafe { &mut PROTO_ID }
306        }
307    }
308}
309
310fn static_map_fns() -> proc_macro2::TokenStream {
311    quote! {
312        fn subdissector_map(op: wsdf::SubdissectorMapOp) -> std::option::Option<wsdf::epan_sys::dissector_table_t> {
313            thread_local! {
314                static SUBDISSECTORS: wsdf::SubdissectorMap = wsdf::SubdissectorMap::default();
315            }
316            SUBDISSECTORS.with(|subdissectors| subdissectors.accept(op))
317        }
318
319        fn hf_map(op: wsdf::HfMapOp) -> std::option::Option<std::ffi::c_int> {
320            thread_local! {
321                static HFS: wsdf::HfMap = wsdf::HfMap::default();
322            }
323            HFS.with(|hfs| hfs.accept(op))
324        }
325    }
326}
327
328/// A helper macro to generate an "index" for an enum.
329#[proc_macro_derive(Dispatch)]
330pub fn derive_dispatch(input: TokenStream) -> TokenStream {
331    let input = syn::parse_macro_input!(input as syn::DeriveInput);
332    let data_enum = match input.data {
333        syn::Data::Enum(data_enum) => data_enum,
334        _ => {
335            return syn::Error::new(input.ident.span(), "expected enum")
336                .to_compile_error()
337                .into()
338        }
339    };
340
341    let new_type_ident = format_ident!("{}Dispatch", input.ident);
342    let new_variants = data_enum.variants.iter().map(|variant| &variant.ident);
343    let dispatch_variant_usize = new_variants
344        .clone()
345        .enumerate()
346        .map(|(idx, variant_ident)| {
347            quote! {
348                #new_type_ident::#variant_ident => #idx,
349            }
350        });
351
352    quote! {
353        enum #new_type_ident {
354            #(#new_variants),*
355        }
356        impl std::convert::From<#new_type_ident> for usize {
357            fn from(value: #new_type_ident) -> Self {
358                match value {
359                    #(#dispatch_variant_usize)*
360                }
361            }
362        }
363    }
364    .into()
365}