/src/ndpi/src/lib/protocols/smpp.c
Line | Count | Source |
1 | | /* |
2 | | * smpp.c |
3 | | * |
4 | | * Copyright (C) 2016 - Damir Franusic <df@release14.org> |
5 | | * Copyright (C) 2016-22 - ntop.org |
6 | | * |
7 | | * nDPI is free software: you can redistribute it and/or modify |
8 | | * it under the terms of the GNU Lesser General Public License as published by |
9 | | * the Free Software Foundation, either version 3 of the License, or |
10 | | * (at your option) any later version. |
11 | | * |
12 | | * nDPI is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public License |
18 | | * along with nDPI. If not, see <http://www.gnu.org/licenses/>. |
19 | | * |
20 | | */ |
21 | | |
22 | | |
23 | | #include "ndpi_protocol_ids.h" |
24 | | |
25 | | #define NDPI_CURRENT_PROTO NDPI_PROTOCOL_SMPP |
26 | | |
27 | | #include "ndpi_api.h" |
28 | | #include "ndpi_private.h" |
29 | | |
30 | | |
31 | | static void ndpi_int_smpp_add_connection(struct ndpi_detection_module_struct* ndpi_struct, |
32 | | struct ndpi_flow_struct* flow) |
33 | 0 | { |
34 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_SMPP, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI); |
35 | 0 | } |
36 | | |
37 | | static u_int8_t ndpi_check_overflow(u_int32_t current_length, u_int32_t total_lenth) |
38 | 0 | { |
39 | 0 | return (current_length > 0 && current_length > INT_MAX - total_lenth); |
40 | 0 | } |
41 | | |
42 | | static void ndpi_search_smpp_tcp(struct ndpi_detection_module_struct* ndpi_struct, |
43 | | struct ndpi_flow_struct* flow) |
44 | 0 | { |
45 | 0 | struct ndpi_packet_struct* packet = &ndpi_struct->packet; |
46 | |
|
47 | 0 | NDPI_LOG_DBG(ndpi_struct, "search SMPP\n"); |
48 | 0 | if (flow->detected_protocol_stack[0] != NDPI_PROTOCOL_SMPP){ |
49 | | // min SMPP packet length = 16 bytes |
50 | 0 | if (packet->payload_packet_len < 16) { |
51 | 0 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
52 | 0 | return; |
53 | 0 | } |
54 | | // get PDU length |
55 | 0 | u_int32_t pdu_l = ntohl(get_u_int32_t(packet->payload, 0)); |
56 | |
|
57 | 0 | NDPI_LOG_DBG2(ndpi_struct, |
58 | 0 | "calculated PDU Length: %d, received PDU Length: %d\n", |
59 | 0 | pdu_l, packet->payload_packet_len); |
60 | | |
61 | | // if PDU size was invalid, try the following TCP segments, 3 attempts max |
62 | 0 | if(flow->packet_counter > 3) { |
63 | 0 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
64 | 0 | return; |
65 | 0 | } |
66 | | // verify PDU length |
67 | 0 | if(pdu_l != packet->payload_packet_len) { |
68 | | // check if multiple PDUs included |
69 | 0 | u_int32_t total_pdu_l = pdu_l; |
70 | 0 | u_int32_t tmp_pdu_l = 0; |
71 | | #ifdef NDPI_ENABLE_DEBUG_MESSAGES |
72 | | u_int16_t pdu_c = 1; |
73 | | #endif |
74 | | // loop PDUs (check if lengths are valid) |
75 | 0 | while(total_pdu_l < ((uint32_t)packet->payload_packet_len-4)) { |
76 | | // get next PDU length |
77 | 0 | tmp_pdu_l = ntohl(get_u_int32_t(packet->payload, total_pdu_l)); |
78 | | // if zero or overflowing , return, will try the next TCP segment |
79 | 0 | if(tmp_pdu_l == 0 || ndpi_check_overflow(tmp_pdu_l, total_pdu_l) ) return; |
80 | | // inc total PDU length |
81 | 0 | total_pdu_l += ntohl(get_u_int32_t(packet->payload, total_pdu_l)); |
82 | | #ifdef NDPI_ENABLE_DEBUG_MESSAGES |
83 | | // inc total PDU count |
84 | | ++pdu_c; |
85 | | #endif |
86 | 0 | } |
87 | | |
88 | 0 | NDPI_LOG_DBG2(ndpi_struct, |
89 | 0 | "multiple PDUs included, calculated total PDU Length: %d, PDU count: %d, TCP payload length: %d\n", |
90 | 0 | total_pdu_l, pdu_c, packet->payload_packet_len); |
91 | | |
92 | | // verify multi PDU total length |
93 | 0 | if(total_pdu_l != packet->payload_packet_len){ |
94 | | // return, will try the next TCP segment |
95 | 0 | return; |
96 | 0 | } |
97 | 0 | } |
98 | | |
99 | | // *** check PDU type *** |
100 | 0 | u_int32_t pdu_type = ntohl(get_u_int32_t(packet->payload, 4)); |
101 | | // first byte of PDU type is either 0x00 of 0x80 |
102 | 0 | if(!(packet->payload[4] == 0x00 || packet->payload[4] == 0x80)) { |
103 | 0 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
104 | 0 | return; |
105 | 0 | } |
106 | | // remove 0x80, get request type pdu |
107 | 0 | u_int32_t pdu_req = pdu_type & 0x00FFFFFF; |
108 | | // list of known PDU types |
109 | 0 | if((pdu_req <= 0x00000009) || /* [0-9] */ |
110 | 0 | (pdu_req == 0x0000000B || pdu_req == 0x00000015 || |
111 | 0 | pdu_req == 0x00000021 || pdu_req == 0x00000102 || |
112 | 0 | pdu_req == 0x00000103)){ |
113 | |
|
114 | 0 | NDPI_LOG_DBG2(ndpi_struct, |
115 | 0 | "PDU type: %x, Request PDU type = %x\n", |
116 | 0 | pdu_type, pdu_req); |
117 | | |
118 | | // fresult flag |
119 | 0 | char extra_passed = 1; |
120 | | // check PDU type specifics |
121 | 0 | switch(pdu_type){ |
122 | | // GENERIC_NACK |
123 | 0 | case 0x80000000: |
124 | | // body length must be zero |
125 | 0 | if(pdu_l > 16) extra_passed = 0; |
126 | 0 | break; |
127 | | |
128 | | // BIND_RECEIVER |
129 | | // BIND_TRANSMITTER |
130 | | // BIND_TRANSCEIVER |
131 | 0 | case 0x00000001: |
132 | 0 | case 0x00000002: |
133 | 0 | case 0x00000009: |
134 | | // status field must be NULL |
135 | 0 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
136 | | // min body length = 10 bytes (+16 in header) |
137 | 0 | if(pdu_l < 26) extra_passed = 0; |
138 | 0 | break; |
139 | | |
140 | | // BIND_RECEIVER_RESP |
141 | | // BIND_TRANSMITTER_RESP |
142 | | // BIND_TRANSCEIVER_RESP |
143 | 0 | case 0x80000001: |
144 | 0 | case 0x80000002: |
145 | 0 | case 0x80000009: |
146 | | // min body length = 2 bytes (+16 in header) |
147 | 0 | if(pdu_l < 18) extra_passed = 0; |
148 | 0 | break; |
149 | | |
150 | | // OUTBIND |
151 | 0 | case 0x0000000B: |
152 | | // status field must be NULL |
153 | 0 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
154 | | // min body length = 4 bytes (+16 in header) |
155 | 0 | if(pdu_l < 20) extra_passed = 0; |
156 | 0 | break; |
157 | | |
158 | | // UNBIND |
159 | 0 | case 0x00000006: |
160 | | // status field must be NULL |
161 | 0 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
162 | | // body length must be zero |
163 | 0 | if(pdu_l > 16) extra_passed = 0; |
164 | 0 | break; |
165 | | |
166 | | // UNBIND_RESP |
167 | 0 | case 0x80000006: |
168 | | // body length must be zero |
169 | 0 | if(pdu_l > 16) extra_passed = 0; |
170 | 0 | break; |
171 | | |
172 | | |
173 | | // SUBMIT_SM |
174 | 0 | case 0x00000004: |
175 | | // status field must be NULL |
176 | 0 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
177 | | // min body length = 17 bytes (+16 in header) |
178 | 0 | if(pdu_l < 33) extra_passed = 0; |
179 | 0 | break; |
180 | | |
181 | | // SUBMIT_SM_RESP |
182 | 0 | case 0x80000004: |
183 | | // - if status != 0, body length is 2 bytes min |
184 | | // - if status > 0, body lenth must be zero |
185 | 0 | if(get_u_int32_t(packet->payload, 8) != 0){ |
186 | 0 | if(pdu_l > 16) extra_passed = 0; |
187 | |
|
188 | 0 | }else if(pdu_l < 18) extra_passed = 0; |
189 | 0 | break; |
190 | | |
191 | | // SUBMIT_MULTI |
192 | 0 | case 0x00000021: |
193 | | // status field must be NULL |
194 | 0 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
195 | | // min body length = 17 bytes (+16 in header) |
196 | 0 | if(pdu_l < 33) extra_passed = 0; |
197 | 0 | break; |
198 | | |
199 | | // SUBMIT_MULTI_RESP |
200 | 0 | case 0x80000021: |
201 | | // min body length = 10 bytes (+16 in header) |
202 | 0 | if(pdu_l < 26) extra_passed = 0; |
203 | 0 | break; |
204 | | |
205 | | // DELIVER_SM |
206 | 0 | case 0x00000005: |
207 | | // status field must be NULL |
208 | 0 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
209 | | // min body length = 17 bytes (+16 in header) |
210 | 0 | if(pdu_l < 33) extra_passed = 0; |
211 | 0 | break; |
212 | | |
213 | | // DELIVER_SM_RESP |
214 | 0 | case 0x80000005: |
215 | | // min body length = 1 byte (+16 in header) |
216 | 0 | if(pdu_l < 17) extra_passed = 0; |
217 | 0 | break; |
218 | | |
219 | | // DATA_SM |
220 | 0 | case 0x00000103: |
221 | | // status field must be NULL |
222 | 0 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
223 | | // min body length = 10 bytes (+16 in header) |
224 | 0 | if(pdu_l < 26) extra_passed = 0; |
225 | 0 | break; |
226 | | |
227 | | // DATA_SM_RESP |
228 | 0 | case 0x80000103: |
229 | | // min body length = 2 bytes (+16 in header) |
230 | 0 | if(pdu_l < 18) extra_passed = 0; |
231 | 0 | break; |
232 | | |
233 | | // QUERY_SM |
234 | 0 | case 0x00000003: |
235 | | // status field must be NULL |
236 | 0 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
237 | | // min body length = 4 bytes (+16 in header) |
238 | 0 | if(pdu_l < 20) extra_passed = 0; |
239 | 0 | break; |
240 | | |
241 | | // QUERY_SM_RESP |
242 | 0 | case 0x80000003: |
243 | | // min body length = 5 bytes (+16 in header) |
244 | 0 | if(pdu_l < 21) extra_passed = 0; |
245 | 0 | break; |
246 | | |
247 | | // CANCEL_SM |
248 | 0 | case 0x00000008: |
249 | | // status field must be NULL |
250 | 0 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
251 | | // min body length = 8 bytes (+16 in header) |
252 | 0 | if(pdu_l < 24) extra_passed = 0; |
253 | 0 | break; |
254 | | |
255 | | // CANCEL_SM_RESP |
256 | 0 | case 0x80000008: |
257 | | // body lenth must be zero |
258 | 0 | if(pdu_l > 16) extra_passed = 0; |
259 | 0 | break; |
260 | | |
261 | | // REPLACE_SM |
262 | 0 | case 0x00000007: |
263 | | // status field must be NULL |
264 | 0 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
265 | | // min body length = 9 bytes (+16 in header) |
266 | 0 | if(pdu_l < 25) extra_passed = 0; |
267 | 0 | break; |
268 | | |
269 | | // REPLACE_SM_RESP |
270 | 0 | case 0x80000007: |
271 | | // body lenth must be zero |
272 | 0 | if(pdu_l > 16) extra_passed = 0; |
273 | 0 | break; |
274 | | |
275 | | // ENQUIRE_LINK |
276 | 0 | case 0x00000015: |
277 | | // status field must be NULL |
278 | 0 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
279 | | // body length must be zero |
280 | 0 | if(pdu_l > 16) extra_passed = 0; |
281 | 0 | break; |
282 | | |
283 | | // ENQUIRE_LINK_RESP |
284 | 0 | case 0x80000015: |
285 | | // body length must be zero |
286 | 0 | if(pdu_l > 16) extra_passed = 0; |
287 | 0 | break; |
288 | | |
289 | | // ALERT_NOTIFICATION |
290 | 0 | case 0x00000102: |
291 | | // status field must be NULL |
292 | 0 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
293 | | // min body length = 6 bytes (+16 in header) |
294 | 0 | if(pdu_l < 22) extra_passed = 0; |
295 | 0 | break; |
296 | | |
297 | 0 | default: break; |
298 | 0 | } |
299 | | |
300 | | // if extra checks passed, set as identified |
301 | 0 | if(extra_passed) { |
302 | 0 | NDPI_LOG_INFO(ndpi_struct, "found SMPP\n"); |
303 | 0 | ndpi_int_smpp_add_connection(ndpi_struct, flow); |
304 | 0 | return; |
305 | 0 | } |
306 | 0 | } |
307 | | |
308 | 0 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
309 | 0 | } |
310 | 0 | } |
311 | | |
312 | | |
313 | | void init_smpp_dissector(struct ndpi_detection_module_struct* ndpi_struct) |
314 | 1 | { |
315 | 1 | register_dissector("SMPP", ndpi_struct, |
316 | 1 | ndpi_search_smpp_tcp, |
317 | 1 | NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION, |
318 | 1 | 1, NDPI_PROTOCOL_SMPP); |
319 | 1 | } |