/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 | 1.82k | { |
34 | 1.82k | ndpi_set_detected_protocol(ndpi_struct, &flow->core, NDPI_PROTOCOL_SMPP, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI); |
35 | 1.82k | } |
36 | | |
37 | | static u_int8_t ndpi_check_overflow(u_int32_t current_length, u_int32_t total_lenth) |
38 | 15.0k | { |
39 | 15.0k | return (current_length > 0 && current_length > INT_MAX - total_lenth); |
40 | 15.0k | } |
41 | | |
42 | | static void ndpi_search_smpp_tcp(struct ndpi_detection_module_struct* ndpi_struct, |
43 | | struct ndpi_flow_struct* flow) |
44 | 4.08M | { |
45 | 4.08M | struct ndpi_packet_struct* packet = &ndpi_struct->packet; |
46 | | |
47 | 4.08M | NDPI_LOG_DBG(ndpi_struct, "search SMPP\n"); |
48 | 4.08M | if (flow->core.detected_protocol_stack[0] != NDPI_PROTOCOL_SMPP){ |
49 | | // min SMPP packet length = 16 bytes |
50 | 4.08M | if (packet->payload_packet_len < 16) { |
51 | 1.28M | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
52 | 1.28M | return; |
53 | 1.28M | } |
54 | | // get PDU length |
55 | 2.80M | u_int32_t pdu_l = ntohl(get_u_int32_t(packet->payload, 0)); |
56 | | |
57 | 2.80M | NDPI_LOG_DBG2(ndpi_struct, |
58 | 2.80M | "calculated PDU Length: %d, received PDU Length: %d\n", |
59 | 2.80M | pdu_l, packet->payload_packet_len); |
60 | | |
61 | | // if PDU size was invalid, try the following TCP segments, 3 attempts max |
62 | 2.80M | if(flow->core.packet_counter > 3) { |
63 | 113k | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
64 | 113k | return; |
65 | 113k | } |
66 | | // verify PDU length |
67 | 2.68M | if(pdu_l != packet->payload_packet_len) { |
68 | | // check if multiple PDUs included |
69 | 2.68M | u_int32_t total_pdu_l = pdu_l; |
70 | 2.68M | 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 | 2.69M | while(total_pdu_l < ((uint32_t)packet->payload_packet_len-4)) { |
76 | | // get next PDU length |
77 | 333k | 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 | 333k | if(tmp_pdu_l == 0 || ndpi_check_overflow(tmp_pdu_l, total_pdu_l) ) return; |
80 | | // inc total PDU length |
81 | 11.5k | 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 | 11.5k | } |
87 | | |
88 | 2.35M | NDPI_LOG_DBG2(ndpi_struct, |
89 | 2.35M | "multiple PDUs included, calculated total PDU Length: %d, PDU count: %d, TCP payload length: %d\n", |
90 | 2.35M | total_pdu_l, pdu_c, packet->payload_packet_len); |
91 | | |
92 | | // verify multi PDU total length |
93 | 2.35M | if(total_pdu_l != packet->payload_packet_len){ |
94 | | // return, will try the next TCP segment |
95 | 2.35M | return; |
96 | 2.35M | } |
97 | 2.35M | } |
98 | | |
99 | | // *** check PDU type *** |
100 | 6.99k | 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 | 6.99k | if(!(packet->payload[4] == 0x00 || packet->payload[4] == 0x80)) { |
103 | 391 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
104 | 391 | return; |
105 | 391 | } |
106 | | // remove 0x80, get request type pdu |
107 | 6.60k | u_int32_t pdu_req = pdu_type & 0x00FFFFFF; |
108 | | // list of known PDU types |
109 | 6.60k | if((pdu_req <= 0x00000009) || /* [0-9] */ |
110 | 2.50k | (pdu_req == 0x0000000B || pdu_req == 0x00000015 || |
111 | 1.80k | pdu_req == 0x00000021 || pdu_req == 0x00000102 || |
112 | 5.75k | pdu_req == 0x00000103)){ |
113 | | |
114 | 5.75k | NDPI_LOG_DBG2(ndpi_struct, |
115 | 5.75k | "PDU type: %x, Request PDU type = %x\n", |
116 | 5.75k | pdu_type, pdu_req); |
117 | | |
118 | | // fresult flag |
119 | 5.75k | char extra_passed = 1; |
120 | | // check PDU type specifics |
121 | 5.75k | switch(pdu_type){ |
122 | | // GENERIC_NACK |
123 | 161 | case 0x80000000: |
124 | | // body length must be zero |
125 | 161 | if(pdu_l > 16) extra_passed = 0; |
126 | 161 | break; |
127 | | |
128 | | // BIND_RECEIVER |
129 | | // BIND_TRANSMITTER |
130 | | // BIND_TRANSCEIVER |
131 | 144 | case 0x00000001: |
132 | 354 | case 0x00000002: |
133 | 457 | case 0x00000009: |
134 | | // status field must be NULL |
135 | 457 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
136 | | // min body length = 10 bytes (+16 in header) |
137 | 457 | if(pdu_l < 26) extra_passed = 0; |
138 | 457 | break; |
139 | | |
140 | | // BIND_RECEIVER_RESP |
141 | | // BIND_TRANSMITTER_RESP |
142 | | // BIND_TRANSCEIVER_RESP |
143 | 87 | case 0x80000001: |
144 | 193 | case 0x80000002: |
145 | 285 | case 0x80000009: |
146 | | // min body length = 2 bytes (+16 in header) |
147 | 285 | if(pdu_l < 18) extra_passed = 0; |
148 | 285 | break; |
149 | | |
150 | | // OUTBIND |
151 | 233 | case 0x0000000B: |
152 | | // status field must be NULL |
153 | 233 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
154 | | // min body length = 4 bytes (+16 in header) |
155 | 233 | if(pdu_l < 20) extra_passed = 0; |
156 | 233 | break; |
157 | | |
158 | | // UNBIND |
159 | 266 | case 0x00000006: |
160 | | // status field must be NULL |
161 | 266 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
162 | | // body length must be zero |
163 | 266 | if(pdu_l > 16) extra_passed = 0; |
164 | 266 | break; |
165 | | |
166 | | // UNBIND_RESP |
167 | 151 | case 0x80000006: |
168 | | // body length must be zero |
169 | 151 | if(pdu_l > 16) extra_passed = 0; |
170 | 151 | break; |
171 | | |
172 | | |
173 | | // SUBMIT_SM |
174 | 415 | case 0x00000004: |
175 | | // status field must be NULL |
176 | 415 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
177 | | // min body length = 17 bytes (+16 in header) |
178 | 415 | if(pdu_l < 33) extra_passed = 0; |
179 | 415 | break; |
180 | | |
181 | | // SUBMIT_SM_RESP |
182 | 334 | case 0x80000004: |
183 | | // - if status != 0, body length is 2 bytes min |
184 | | // - if status > 0, body lenth must be zero |
185 | 334 | if(get_u_int32_t(packet->payload, 8) != 0){ |
186 | 198 | if(pdu_l > 16) extra_passed = 0; |
187 | | |
188 | 198 | }else if(pdu_l < 18) extra_passed = 0; |
189 | 334 | break; |
190 | | |
191 | | // SUBMIT_MULTI |
192 | 268 | case 0x00000021: |
193 | | // status field must be NULL |
194 | 268 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
195 | | // min body length = 17 bytes (+16 in header) |
196 | 268 | if(pdu_l < 33) extra_passed = 0; |
197 | 268 | break; |
198 | | |
199 | | // SUBMIT_MULTI_RESP |
200 | 147 | case 0x80000021: |
201 | | // min body length = 10 bytes (+16 in header) |
202 | 147 | if(pdu_l < 26) extra_passed = 0; |
203 | 147 | break; |
204 | | |
205 | | // DELIVER_SM |
206 | 314 | case 0x00000005: |
207 | | // status field must be NULL |
208 | 314 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
209 | | // min body length = 17 bytes (+16 in header) |
210 | 314 | if(pdu_l < 33) extra_passed = 0; |
211 | 314 | break; |
212 | | |
213 | | // DELIVER_SM_RESP |
214 | 175 | case 0x80000005: |
215 | | // min body length = 1 byte (+16 in header) |
216 | 175 | if(pdu_l < 17) extra_passed = 0; |
217 | 175 | break; |
218 | | |
219 | | // DATA_SM |
220 | 186 | case 0x00000103: |
221 | | // status field must be NULL |
222 | 186 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
223 | | // min body length = 10 bytes (+16 in header) |
224 | 186 | if(pdu_l < 26) extra_passed = 0; |
225 | 186 | break; |
226 | | |
227 | | // DATA_SM_RESP |
228 | 112 | case 0x80000103: |
229 | | // min body length = 2 bytes (+16 in header) |
230 | 112 | if(pdu_l < 18) extra_passed = 0; |
231 | 112 | break; |
232 | | |
233 | | // QUERY_SM |
234 | 300 | case 0x00000003: |
235 | | // status field must be NULL |
236 | 300 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
237 | | // min body length = 4 bytes (+16 in header) |
238 | 300 | if(pdu_l < 20) extra_passed = 0; |
239 | 300 | break; |
240 | | |
241 | | // QUERY_SM_RESP |
242 | 142 | case 0x80000003: |
243 | | // min body length = 5 bytes (+16 in header) |
244 | 142 | if(pdu_l < 21) extra_passed = 0; |
245 | 142 | break; |
246 | | |
247 | | // CANCEL_SM |
248 | 340 | case 0x00000008: |
249 | | // status field must be NULL |
250 | 340 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
251 | | // min body length = 8 bytes (+16 in header) |
252 | 340 | if(pdu_l < 24) extra_passed = 0; |
253 | 340 | break; |
254 | | |
255 | | // CANCEL_SM_RESP |
256 | 141 | case 0x80000008: |
257 | | // body lenth must be zero |
258 | 141 | if(pdu_l > 16) extra_passed = 0; |
259 | 141 | break; |
260 | | |
261 | | // REPLACE_SM |
262 | 338 | case 0x00000007: |
263 | | // status field must be NULL |
264 | 338 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
265 | | // min body length = 9 bytes (+16 in header) |
266 | 338 | if(pdu_l < 25) extra_passed = 0; |
267 | 338 | break; |
268 | | |
269 | | // REPLACE_SM_RESP |
270 | 135 | case 0x80000007: |
271 | | // body lenth must be zero |
272 | 135 | if(pdu_l > 16) extra_passed = 0; |
273 | 135 | break; |
274 | | |
275 | | // ENQUIRE_LINK |
276 | 284 | case 0x00000015: |
277 | | // status field must be NULL |
278 | 284 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
279 | | // body length must be zero |
280 | 284 | if(pdu_l > 16) extra_passed = 0; |
281 | 284 | break; |
282 | | |
283 | | // ENQUIRE_LINK_RESP |
284 | 154 | case 0x80000015: |
285 | | // body length must be zero |
286 | 154 | if(pdu_l > 16) extra_passed = 0; |
287 | 154 | break; |
288 | | |
289 | | // ALERT_NOTIFICATION |
290 | 225 | case 0x00000102: |
291 | | // status field must be NULL |
292 | 225 | if(get_u_int32_t(packet->payload, 8) != 0) extra_passed = 0; |
293 | | // min body length = 6 bytes (+16 in header) |
294 | 225 | if(pdu_l < 22) extra_passed = 0; |
295 | 225 | break; |
296 | | |
297 | 190 | default: break; |
298 | 5.75k | } |
299 | | |
300 | | // if extra checks passed, set as identified |
301 | 5.75k | if(extra_passed) { |
302 | 1.82k | NDPI_LOG_INFO(ndpi_struct, "found SMPP\n"); |
303 | 1.82k | ndpi_int_smpp_add_connection(ndpi_struct, flow); |
304 | 1.82k | return; |
305 | 1.82k | } |
306 | 5.75k | } |
307 | | |
308 | 4.78k | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
309 | 4.78k | } |
310 | 4.08M | } |
311 | | |
312 | | |
313 | | void init_smpp_dissector(struct ndpi_detection_module_struct* ndpi_struct) |
314 | 15.4k | { |
315 | 15.4k | ndpi_register_dissector("SMPP", ndpi_struct, |
316 | 15.4k | ndpi_search_smpp_tcp, |
317 | 15.4k | NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION, |
318 | 15.4k | DISSECTOR_LICENSE_LGPL, |
319 | 15.4k | 1, NDPI_PROTOCOL_SMPP); |
320 | 15.4k | } |