huginn_net_db/
observable_http_signals_matching.rs1use crate::db::HttpIndexKey;
2use crate::db_matching_trait::{DatabaseSignature, MatchQuality};
3use crate::http::{self, Header, HttpMatchQuality, Version};
4use crate::observable_signals::{HttpRequestObservation, HttpResponseObservation};
5
6pub trait HttpDistance {
7 fn get_version(&self) -> Version;
8 fn get_horder(&self) -> &[Header];
9 fn get_habsent(&self) -> &[Header];
10 fn get_expsw(&self) -> &str;
11
12 fn distance_ip_version(&self, other: &http::Signature) -> Option<u32> {
13 if other.version == Version::Any || self.get_version() == other.version {
14 Some(HttpMatchQuality::High.as_score())
15 } else {
16 None
17 }
18 }
19
20 fn distance_header(observed: &[Header], signature: &[Header]) -> Option<u32> {
41 let mut obs_idx = 0usize; let mut sig_idx = 0usize; let mut errors: u32 = 0; while obs_idx < observed.len() && sig_idx < signature.len() {
46 let obs_header = &observed[obs_idx];
47 let sig_header = &signature[sig_idx];
48
49 if obs_header.name == sig_header.name && obs_header.value == sig_header.value {
50 obs_idx = obs_idx.saturating_add(1);
51 sig_idx = sig_idx.saturating_add(1);
52 } else if obs_header.name == sig_header.name {
53 if !sig_header.optional {
54 errors = errors.saturating_add(1);
55 }
56 obs_idx = obs_idx.saturating_add(1);
57 sig_idx = sig_idx.saturating_add(1);
58 } else if sig_header.optional {
59 sig_idx = sig_idx.saturating_add(1);
60 } else {
61 errors = errors.saturating_add(1);
62 sig_idx = sig_idx.saturating_add(1);
63 }
64 }
65
66 while obs_idx < observed.len() {
67 errors = errors.saturating_add(1);
68 obs_idx = obs_idx.saturating_add(1);
69 }
70
71 while sig_idx < signature.len() {
72 if !signature[sig_idx].optional {
73 errors = errors.saturating_add(1);
74 }
75 sig_idx = sig_idx.saturating_add(1);
76 }
77
78 match errors {
79 0..=2 => Some(HttpMatchQuality::High.as_score()), 3..=5 => Some(HttpMatchQuality::Medium.as_score()), 6..=8 => Some(HttpMatchQuality::Low.as_score()), 9..=11 => Some(HttpMatchQuality::Bad.as_score()), _ => None, }
85 }
86
87 fn distance_horder(&self, other: &http::Signature) -> Option<u32> {
88 Self::distance_header(self.get_horder(), &other.horder)
89 }
90
91 fn distance_habsent(&self, other: &http::Signature) -> Option<u32> {
92 Self::distance_header(self.get_habsent(), &other.habsent)
93 }
94
95 fn distance_expsw(&self, other: &http::Signature) -> Option<u32> {
96 if other.expsw.as_str().contains(self.get_expsw()) {
97 Some(HttpMatchQuality::High.as_score())
98 } else {
99 Some(HttpMatchQuality::Bad.as_score())
100 }
101 }
102}
103
104impl HttpDistance for HttpRequestObservation {
105 fn get_version(&self) -> Version {
106 self.version
107 }
108 fn get_horder(&self) -> &[Header] {
109 &self.horder
110 }
111 fn get_habsent(&self) -> &[Header] {
112 &self.habsent
113 }
114 fn get_expsw(&self) -> &str {
115 &self.expsw
116 }
117}
118
119impl HttpDistance for HttpResponseObservation {
120 fn get_version(&self) -> Version {
121 self.version
122 }
123 fn get_horder(&self) -> &[Header] {
124 &self.horder
125 }
126 fn get_habsent(&self) -> &[Header] {
127 &self.habsent
128 }
129 fn get_expsw(&self) -> &str {
130 &self.expsw
131 }
132}
133
134trait HttpSignatureHelper {
135 fn calculate_http_distance<T: HttpDistance>(&self, observed: &T) -> Option<u32>;
136
137 fn generate_http_index_keys(&self) -> Vec<HttpIndexKey>;
138
139 fn get_quality_score_by_distance(&self, distance: u32) -> f32 {
146 http::HttpMatchQuality::distance_to_score(distance)
147 }
148}
149
150impl HttpSignatureHelper for http::Signature {
151 fn calculate_http_distance<T: HttpDistance>(&self, observed: &T) -> Option<u32> {
152 let signature: &http::Signature = self;
153 let distance = observed
154 .distance_ip_version(signature)?
155 .saturating_add(observed.distance_horder(signature)?)
156 .saturating_add(observed.distance_habsent(signature)?)
157 .saturating_add(observed.distance_expsw(signature)?);
158 Some(distance)
159 }
160 fn generate_http_index_keys(&self) -> Vec<HttpIndexKey> {
161 let mut keys = Vec::new();
162 if self.version == Version::Any {
163 keys.push(HttpIndexKey {
164 http_version_key: Version::V10,
165 });
166 keys.push(HttpIndexKey {
167 http_version_key: Version::V11,
168 });
169 } else {
170 keys.push(HttpIndexKey {
171 http_version_key: self.version,
172 });
173 }
174 keys
175 }
176}
177
178impl DatabaseSignature<HttpRequestObservation> for http::Signature {
179 fn calculate_distance(&self, observed: &HttpRequestObservation) -> Option<u32> {
180 self.calculate_http_distance(observed)
181 }
182 fn get_quality_score(&self, distance: u32) -> f32 {
183 self.get_quality_score_by_distance(distance)
184 }
185 fn generate_index_keys_for_db_entry(&self) -> Vec<HttpIndexKey> {
186 self.generate_http_index_keys()
187 }
188}
189
190impl DatabaseSignature<HttpResponseObservation> for http::Signature {
191 fn calculate_distance(&self, observed: &HttpResponseObservation) -> Option<u32> {
192 self.calculate_http_distance(observed)
193 }
194 fn get_quality_score(&self, distance: u32) -> f32 {
195 self.get_quality_score_by_distance(distance)
196 }
197 fn generate_index_keys_for_db_entry(&self) -> Vec<HttpIndexKey> {
198 self.generate_http_index_keys()
199 }
200}