Skip to main content

mock_igd/matcher/
mod.rs

1//! Request matching logic.
2
3use crate::action::{
4    Action, AddPortMappingParams, DeletePortMappingParams, GetGenericPortMappingEntryParams,
5    GetSpecificPortMappingEntryParams,
6};
7
8/// A parsed SOAP request that can be matched against.
9#[derive(Debug, Clone)]
10pub struct SoapRequest {
11    pub action_name: String,
12    pub service_type: String,
13    pub body: SoapRequestBody,
14}
15
16/// The body of a SOAP request, parsed into a known action type.
17#[derive(Debug, Clone)]
18pub enum SoapRequestBody {
19    GetExternalIPAddress,
20    GetStatusInfo,
21    AddPortMapping(AddPortMappingRequest),
22    DeletePortMapping(DeletePortMappingRequest),
23    GetGenericPortMappingEntry(GetGenericPortMappingEntryRequest),
24    GetSpecificPortMappingEntry(GetSpecificPortMappingEntryRequest),
25    GetCommonLinkProperties,
26    GetTotalBytesReceived,
27    GetTotalBytesSent,
28    Unknown(String),
29}
30
31/// Parsed AddPortMapping request.
32#[derive(Debug, Clone)]
33pub struct AddPortMappingRequest {
34    pub remote_host: String,
35    pub external_port: u16,
36    pub protocol: String,
37    pub internal_port: u16,
38    pub internal_client: String,
39    pub enabled: bool,
40    pub description: String,
41    pub lease_duration: u32,
42}
43
44/// Parsed DeletePortMapping request.
45#[derive(Debug, Clone)]
46pub struct DeletePortMappingRequest {
47    pub remote_host: String,
48    pub external_port: u16,
49    pub protocol: String,
50}
51
52/// Parsed GetGenericPortMappingEntry request.
53#[derive(Debug, Clone)]
54pub struct GetGenericPortMappingEntryRequest {
55    pub index: u32,
56}
57
58/// Parsed GetSpecificPortMappingEntry request.
59#[derive(Debug, Clone)]
60pub struct GetSpecificPortMappingEntryRequest {
61    pub remote_host: String,
62    pub external_port: u16,
63    pub protocol: String,
64}
65
66/// Trait for matching requests.
67pub trait Matcher: Send + Sync {
68    /// Check if this matcher matches the given request.
69    fn matches(&self, request: &SoapRequest) -> bool;
70}
71
72impl Matcher for Action {
73    fn matches(&self, request: &SoapRequest) -> bool {
74        match self {
75            Action::Any => true,
76
77            Action::GetExternalIPAddress => {
78                matches!(request.body, SoapRequestBody::GetExternalIPAddress)
79            }
80
81            Action::GetStatusInfo => {
82                matches!(request.body, SoapRequestBody::GetStatusInfo)
83            }
84
85            Action::AddPortMapping(params) => match &request.body {
86                SoapRequestBody::AddPortMapping(req) => matches_add_port_mapping(params, req),
87                _ => false,
88            },
89
90            Action::DeletePortMapping(params) => match &request.body {
91                SoapRequestBody::DeletePortMapping(req) => matches_delete_port_mapping(params, req),
92                _ => false,
93            },
94
95            Action::GetGenericPortMappingEntry(params) => match &request.body {
96                SoapRequestBody::GetGenericPortMappingEntry(req) => {
97                    matches_get_generic_port_mapping_entry(params, req)
98                }
99                _ => false,
100            },
101
102            Action::GetSpecificPortMappingEntry(params) => match &request.body {
103                SoapRequestBody::GetSpecificPortMappingEntry(req) => {
104                    matches_get_specific_port_mapping_entry(params, req)
105                }
106                _ => false,
107            },
108
109            Action::GetCommonLinkProperties => {
110                matches!(request.body, SoapRequestBody::GetCommonLinkProperties)
111            }
112
113            Action::GetTotalBytesReceived => {
114                matches!(request.body, SoapRequestBody::GetTotalBytesReceived)
115            }
116
117            Action::GetTotalBytesSent => {
118                matches!(request.body, SoapRequestBody::GetTotalBytesSent)
119            }
120        }
121    }
122}
123
124fn matches_add_port_mapping(params: &AddPortMappingParams, req: &AddPortMappingRequest) -> bool {
125    if let Some(port) = params.external_port {
126        if req.external_port != port {
127            return false;
128        }
129    }
130    if let Some(protocol) = &params.protocol {
131        if req.protocol.to_uppercase() != protocol.as_str() {
132            return false;
133        }
134    }
135    if let Some(port) = params.internal_port {
136        if req.internal_port != port {
137            return false;
138        }
139    }
140    if let Some(client) = &params.internal_client {
141        if req.internal_client != client.to_string() {
142            return false;
143        }
144    }
145    if let Some(desc) = &params.description {
146        if !req.description.contains(desc.as_str()) {
147            return false;
148        }
149    }
150    true
151}
152
153fn matches_delete_port_mapping(
154    params: &DeletePortMappingParams,
155    req: &DeletePortMappingRequest,
156) -> bool {
157    if let Some(port) = params.external_port {
158        if req.external_port != port {
159            return false;
160        }
161    }
162    if let Some(protocol) = &params.protocol {
163        if req.protocol.to_uppercase() != protocol.as_str() {
164            return false;
165        }
166    }
167    true
168}
169
170fn matches_get_generic_port_mapping_entry(
171    params: &GetGenericPortMappingEntryParams,
172    req: &GetGenericPortMappingEntryRequest,
173) -> bool {
174    if let Some(index) = params.index {
175        if req.index != index {
176            return false;
177        }
178    }
179    true
180}
181
182fn matches_get_specific_port_mapping_entry(
183    params: &GetSpecificPortMappingEntryParams,
184    req: &GetSpecificPortMappingEntryRequest,
185) -> bool {
186    if let Some(port) = params.external_port {
187        if req.external_port != port {
188            return false;
189        }
190    }
191    if let Some(protocol) = &params.protocol {
192        if req.protocol.to_uppercase() != protocol.as_str() {
193            return false;
194        }
195    }
196    true
197}