Skip to main content

retina_filtergen/
session_filter.rs

1use heck::CamelCase;
2use proc_macro2::{Ident, Span};
3use quote::quote;
4
5use retina_core::filter::ast::*;
6use retina_core::filter::ptree::{PNode, PTree, Terminate};
7
8use crate::util::binary_to_tokens;
9
10pub(crate) fn gen_session_filter(
11    ptree: &PTree,
12    statics: &mut Vec<proc_macro2::TokenStream>,
13    ct_nodes: Vec<usize>,
14) -> proc_macro2::TokenStream {
15    if ptree.root.is_terminal {
16        // only ethernet - no filter specified
17        return quote! {
18            true
19        };
20    }
21
22    let mut body: Vec<proc_macro2::TokenStream> = vec![];
23    let session_subtrees = ct_nodes
24        .into_iter()
25        .map(|id| ptree.get_subtree(id).unwrap())
26        .collect::<Vec<_>>();
27
28    for node in session_subtrees.iter() {
29        add_node_match_arm(&mut body, statics, node);
30    }
31
32    quote! {
33        match idx {
34            #( #body )*
35            _ => return false,
36        }
37    }
38}
39
40fn add_node_match_arm(
41    code: &mut Vec<proc_macro2::TokenStream>,
42    statics: &mut Vec<proc_macro2::TokenStream>,
43    node: &PNode,
44) {
45    let idx_lit = syn::LitInt::new(&node.id.to_string(), Span::call_site());
46    if node.is_terminal {
47        code.push(quote! {
48            #idx_lit => return true,
49        })
50    } else {
51        assert!(matches!(node.terminates, Terminate::Connection));
52        let mut body: Vec<proc_macro2::TokenStream> = vec![];
53        gen_session_filter_util(&mut body, statics, node);
54
55        let service = node.pred.get_protocol().name();
56        let proto_name = Ident::new(service, Span::call_site());
57        let proto_variant = Ident::new(&service.to_camel_case(), Span::call_site());
58
59        code.push(quote! {
60            #idx_lit => {
61                if let retina_core::protocols::stream::SessionData::#proto_variant(#proto_name) = &session.data {
62                    #( #body )*
63                }
64                return false;
65
66            }
67        })
68    }
69}
70
71// fn add_parser_match_arm(
72//     code: &mut Vec<proc_macro2::TokenStream>,
73//     statics: &mut Vec<proc_macro2::TokenStream>,
74//     node: &PNode,
75//     protocol: &ProtocolName,
76// ) {
77//     assert!(node.pred.is_unary());
78//     assert!(node.pred.on_connection() || node.pred.on_session());
79
80//     let ident = Ident::new(
81//         &(protocol.name().to_owned() + "sessions"),
82//         Span::call_site(),
83//     );
84//     let ident_type = Ident::new(&protocol.name().to_camel_case(), Span::call_site());
85
86//     let mut body: Vec<proc_macro2::TokenStream> = vec![];
87//     if node.is_terminal {
88//         // node terminates a stream filter pattern
89//         body.push(quote! {
90//             return true;
91//         });
92//     } else {
93//         add_conditional_filter(&mut body, statics, node);
94//     }
95
96//     code.push(quote! {
97//         retina_core::protocols::stream::Parser::#ident_type(#ident) => {
98//             #( #body )*
99//         }
100//     })
101// }
102
103// fn add_conditional_filter(
104//     code: &mut Vec<proc_macro2::TokenStream>,
105//     statics: &mut Vec<proc_macro2::TokenStream>,
106//     node: &PNode,
107// ) {
108//     let mut body_filter_all: Vec<proc_macro2::TokenStream> = vec![];
109//     let mut body_filter_back: Vec<proc_macro2::TokenStream> = vec![];
110
111//     add_filter_match_arm(&mut body_filter_all, &mut body_filter_back, statics, node);
112//     code.push(quote! {
113//         if check_all {
114//             #( #body_filter_all )*
115//         } else {
116//             #( #body_filter_back )*
117//         }
118//     })
119// }
120
121// fn add_filter_match_arm(
122//     body_filter_all: &mut Vec<proc_macro2::TokenStream>,
123//     body_filter_back: &mut Vec<proc_macro2::TokenStream>,
124//     statics: &mut Vec<proc_macro2::TokenStream>,
125//     node: &PNode,
126// ) {
127//     let ident = Ident::new(node.pred.get_protocol().name(), Span::call_site());
128//     let ident_mixed = Ident::new(
129//         &(node.pred.get_protocol().name().to_owned() + "sessions"),
130//         Span::call_site(),
131//     );
132//     let mut body_all: Vec<proc_macro2::TokenStream> = vec![];
133//     let mut body_back: Vec<proc_macro2::TokenStream> = vec![];
134//     gen_session_filter_util(&mut body_all, &mut body_back, statics, node);
135
136//     body_filter_all.push(quote! {
137//         #ident_mixed.parser.retain(|#ident|{
138//             #( #body_all )*
139//         });
140//         return !(#ident_mixed.parser.is_empty());
141//     });
142//     body_filter_back.push(quote! {
143//         let #ident = #ident_mixed.parser.back().unwrap();
144//         #( #body_back )*
145//     });
146// }
147
148fn gen_session_filter_util(
149    code: &mut Vec<proc_macro2::TokenStream>,
150    statics: &mut Vec<proc_macro2::TokenStream>,
151    node: &PNode,
152) {
153    for child in node.children.iter() {
154        assert!(node.pred.on_connection() || node.pred.on_session());
155        match &child.pred {
156            Predicate::Unary { .. } => {
157                panic!("Found unary predicate in session filter pattern");
158            }
159            Predicate::Binary {
160                protocol,
161                field,
162                op,
163                value,
164            } => {
165                assert!(protocol.name() == node.pred.get_protocol().name());
166                add_binary_pred(code, statics, child, protocol, field, op, value);
167            }
168        }
169    }
170}
171
172#[allow(clippy::too_many_arguments)]
173fn add_binary_pred(
174    code: &mut Vec<proc_macro2::TokenStream>,
175    statics: &mut Vec<proc_macro2::TokenStream>,
176    node: &PNode,
177    protocol: &ProtocolName,
178    field: &FieldName,
179    op: &BinOp,
180    value: &Value,
181) {
182    let mut body: Vec<proc_macro2::TokenStream> = vec![];
183    gen_session_filter_util(&mut body, statics, node);
184    if node.is_terminal {
185        // node terminates a session filter pattern
186        body.push(quote! {
187            return true;
188        });
189    }
190
191    let pred_tokenstream = binary_to_tokens(protocol, field, op, value, statics);
192    code.push(quote! {
193        if #pred_tokenstream {
194            #( #body )*
195        }
196    });
197}