1use 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#[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); 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#[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 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, ) -> 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 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
233fn 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#[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 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#[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}