/src/wireshark/epan/dissectors/packet-tpncp.c
Line | Count | Source |
1 | | /* packet-tpncp.c |
2 | | * Routines for Audiocodes TrunkPack Network Control Protocol (TPNCP) dissection |
3 | | * |
4 | | * Copyright (c) 2007 by Valery Sigalov <valery.sigalov@audiocodes.com> |
5 | | * |
6 | | * Wireshark - Network traffic analyzer |
7 | | * By Gerald Combs <gerald@wireshark.com> |
8 | | * Copyright 1998 Gerald Combs |
9 | | * |
10 | | * SPDX-License-Identifier: GPL-2.0-or-later |
11 | | */ |
12 | | |
13 | | /*---------------------------------------------------------------------------*/ |
14 | | |
15 | | #define WS_LOG_DOMAIN "TPNCP" |
16 | | #include "config.h" |
17 | | |
18 | | #include <epan/packet.h> |
19 | | #include <epan/exceptions.h> |
20 | | #include <epan/expert.h> |
21 | | #include <epan/prefs.h> |
22 | | #include <wsutil/filesystem.h> |
23 | | #include <wsutil/file_util.h> |
24 | | #include <wsutil/report_message.h> |
25 | | #include <wsutil/strtoi.h> |
26 | | #include <epan/wmem_scopes.h> |
27 | | #include "packet-acdr.h" |
28 | | #include "packet-tcp.h" |
29 | | |
30 | | /*---------------------------------------------------------------------------*/ |
31 | | |
32 | 28 | #define BASE_TPNCP_PORT 2424 |
33 | 0 | #define HA_PORT_TPNCP_TRUNKPACK 2442 |
34 | 14 | #define TCP_PORT_TPNCP_TRUNKPACK BASE_TPNCP_PORT |
35 | 14 | #define UDP_PORT_TPNCP_TRUNKPACK BASE_TPNCP_PORT |
36 | | #define TCP_PORT_TPNCP_HOST BASE_TPNCP_PORT |
37 | | #define UDP_PORT_TPNCP_HOST BASE_TPNCP_PORT |
38 | | |
39 | 0 | #define MAX_TPNCP_DB_ENTRY_LEN 3000 |
40 | | |
41 | | /*---------------------------------------------------------------------------*/ |
42 | | |
43 | | void proto_register_tpncp(void); |
44 | | void proto_reg_handoff_tpncp(void); |
45 | | |
46 | | enum SpecialFieldType { |
47 | | TPNCP_NORMAL, |
48 | | TPNCP_ADDRESS_FAMILY, |
49 | | TPNCP_IP_ADDR, |
50 | | TPNCP_OPEN_CHANNEL_START, |
51 | | TPNCP_SECURITY_START, |
52 | | TPNCP_SECURITY_OFFSET, |
53 | | RTP_STATE_START, |
54 | | RTP_STATE_OFFSET, |
55 | | RTP_STATE_END, |
56 | | TPNCP_CHANNEL_CONFIGURATION |
57 | | }; |
58 | | |
59 | | /* The linked list for storing information about specific data fields. */ |
60 | | typedef struct tpncp_data_field_info |
61 | | { |
62 | | char *name; |
63 | | int descr; |
64 | | int ipv6_descr; |
65 | | int array_dim; |
66 | | enum SpecialFieldType special_type; |
67 | | unsigned char size; |
68 | | unsigned char sign; |
69 | | int since; |
70 | | struct tpncp_data_field_info *p_next; |
71 | | } tpncp_data_field_info; |
72 | | |
73 | | /*--------------------------------------------------------------------------- |
74 | | * Desegmentation of TPNCP over TCP */ |
75 | | static bool tpncp_desegment = true; |
76 | | |
77 | | /* Database for storing information about all TPNCP events. */ |
78 | | static tpncp_data_field_info **tpncp_events_info_db; |
79 | | unsigned tpncp_events_info_len; |
80 | | |
81 | | /* Database for storing information about all TPNCP commands. */ |
82 | | static tpncp_data_field_info **tpncp_commands_info_db; |
83 | | unsigned tpncp_commands_info_len; |
84 | | |
85 | | /* Global variables for bitfields representation. */ |
86 | | /* TPNCP packet header fields. */ |
87 | | static int proto_tpncp; |
88 | | static int hf_tpncp_version; |
89 | | static int hf_tpncp_length; |
90 | | static int hf_tpncp_seq_number; |
91 | | static int hf_tpncp_length_ext; |
92 | | static int hf_tpncp_reserved; |
93 | | static int hf_tpncp_command_id; |
94 | | static int hf_tpncp_event_id; |
95 | | static int hf_tpncp_cid; |
96 | | |
97 | | static expert_field ei_tpncp_unknown_data; |
98 | | |
99 | | /* TPNCP fields defining a subtree. */ |
100 | | static int ett_tpncp; |
101 | | static int ett_tpncp_body; |
102 | | |
103 | | static bool global_tpncp_load_db; |
104 | | static const char *tpncp_dat_path = NULL; |
105 | | |
106 | | static dissector_handle_t tpncp_handle; |
107 | | static dissector_handle_t tpncp_tcp_handle; |
108 | | |
109 | | /* TODO: Runtime value_string_ext arrays should be used*/ |
110 | | static value_string *tpncp_commands_id_vals; |
111 | | static value_string *tpncp_events_id_vals; |
112 | | static value_string **tpncp_enums_id_vals; |
113 | | static char **tpncp_enums_name_vals; |
114 | | |
115 | | static int hf_size; |
116 | | static int hf_allocated; |
117 | | static hf_register_info *hf; |
118 | | |
119 | | static bool db_initialized; |
120 | | |
121 | | /*---------------------------------------------------------------------------*/ |
122 | | |
123 | | enum AddressFamily { |
124 | | TPNCP_IPV4 = 2, |
125 | | TPNCP_IPV6 = 10, |
126 | | TPNCP_IPV6_PSOS = 28 |
127 | | }; |
128 | | |
129 | | static void |
130 | | dissect_tpncp_data(unsigned data_id, packet_info *pinfo, tvbuff_t *tvb, proto_tree *ltree, |
131 | | int *offset, tpncp_data_field_info **data_fields_info, int ver, unsigned encoding) |
132 | 0 | { |
133 | 0 | int g_str_len; |
134 | 0 | tpncp_data_field_info *field = NULL; |
135 | 0 | int bitindex = encoding == ENC_LITTLE_ENDIAN ? 7 : 0; |
136 | 0 | enum AddressFamily address_family = TPNCP_IPV4; |
137 | 0 | int open_channel_start = -1, security_offset = 0, rtp_state_offset = 0; |
138 | 0 | int channel_b_offset = 0, rtp_tx_state_offset = 0, rtp_state_size = 0; |
139 | 0 | const int initial_offset = *offset; |
140 | |
|
141 | 0 | for (field = data_fields_info[data_id]; field; field = field->p_next) { |
142 | 0 | if (field->since > 0 && field->since > ver) |
143 | 0 | continue; |
144 | 0 | switch (field->special_type) { |
145 | 0 | case TPNCP_OPEN_CHANNEL_START: |
146 | 0 | open_channel_start = *offset; |
147 | 0 | break; |
148 | 0 | case TPNCP_SECURITY_OFFSET: { |
149 | 0 | const uint32_t sec_offset = tvb_get_uint32(tvb, *offset, encoding); |
150 | 0 | if (sec_offset > 0 && open_channel_start >= 0) |
151 | 0 | security_offset = open_channel_start + sec_offset; |
152 | 0 | break; |
153 | 0 | } |
154 | 0 | case TPNCP_SECURITY_START: |
155 | 0 | *offset = security_offset; |
156 | 0 | open_channel_start = -1; |
157 | 0 | security_offset = 0; |
158 | 0 | break; |
159 | 0 | case RTP_STATE_OFFSET: |
160 | 0 | rtp_state_offset = tvb_get_int32(tvb, *offset, encoding); |
161 | 0 | if (rtp_state_offset > 0) |
162 | 0 | rtp_state_offset += initial_offset + 4; /* The offset starts after CID */ |
163 | 0 | break; |
164 | 0 | case RTP_STATE_START: |
165 | 0 | *offset = rtp_state_offset; |
166 | 0 | rtp_state_offset = 0; |
167 | 0 | if (rtp_tx_state_offset == 0) { |
168 | 0 | rtp_state_size = (tvb_reported_length_remaining(tvb, *offset) - 4) / 2; |
169 | 0 | rtp_tx_state_offset = *offset + rtp_state_size; |
170 | 0 | } else { |
171 | 0 | *offset = rtp_tx_state_offset; |
172 | 0 | rtp_tx_state_offset += rtp_state_size; |
173 | 0 | } |
174 | 0 | break; |
175 | 0 | case RTP_STATE_END: |
176 | 0 | rtp_tx_state_offset = 0; |
177 | 0 | break; |
178 | 0 | case TPNCP_CHANNEL_CONFIGURATION: |
179 | 0 | if (channel_b_offset == 0) { |
180 | 0 | int channel_configuration_size = tvb_reported_length_remaining(tvb, *offset) / 2; |
181 | 0 | channel_b_offset = *offset + channel_configuration_size; |
182 | 0 | } else { |
183 | 0 | *offset = channel_b_offset; |
184 | 0 | channel_b_offset = 0; |
185 | 0 | } |
186 | 0 | break; |
187 | 0 | case TPNCP_ADDRESS_FAMILY: |
188 | 0 | address_family = (enum AddressFamily)tvb_get_uint32(tvb, *offset, encoding); |
189 | | // fall-through |
190 | 0 | default: |
191 | 0 | if (open_channel_start != -1 && security_offset > 0 && *offset >= security_offset) |
192 | 0 | continue; |
193 | 0 | if (rtp_state_offset > 0 && *offset >= rtp_state_offset) |
194 | 0 | continue; |
195 | 0 | if (rtp_tx_state_offset > 0 && *offset >= rtp_tx_state_offset) |
196 | 0 | continue; |
197 | 0 | if (channel_b_offset > 0 && *offset >= channel_b_offset) |
198 | 0 | continue; |
199 | 0 | break; |
200 | 0 | } |
201 | 0 | switch (field->size) { |
202 | 0 | case 1: case 2: case 3: case 4: |
203 | 0 | case 5: case 6: case 7: case 8: |
204 | | /* add char array */ |
205 | 0 | if ((g_str_len = field->array_dim)) { |
206 | 0 | g_str_len = MIN(g_str_len, tvb_reported_length_remaining(tvb, *offset)); |
207 | 0 | proto_tree_add_item(ltree, field->descr, tvb, *offset, g_str_len, ENC_NA | ENC_ASCII); |
208 | 0 | (*offset) += g_str_len; |
209 | 0 | } else { /* add single char */ |
210 | | /* Output only the numeric value for 8-bit fields, considering sign, with no extra formatting. */ |
211 | 0 | if (field->size == 8) { |
212 | 0 | uint8_t g_uchar = tvb_get_uint8(tvb, *offset); |
213 | 0 | if (field->sign) |
214 | 0 | proto_tree_add_uint(ltree, field->descr, tvb, *offset, 1, g_uchar); |
215 | 0 | else |
216 | 0 | proto_tree_add_int(ltree, field->descr, tvb, *offset, 1, (int8_t)g_uchar); |
217 | 0 | (*offset)++; |
218 | 0 | } else { /* unsigned bitfield <8 */ |
219 | 0 | unsigned bit_offset; |
220 | | |
221 | | /* |
222 | | * tpncp.dat always lists bitfields in little-endian (LSB-first) order, regardless of encoding. |
223 | | * Wireshark's proto_tree_add_bits_item with ENC_LITTLE_ENDIAN matches this order. |
224 | | * |
225 | | * Therefore, we always use ENC_LITTLE_ENDIAN for bitfield extraction, regardless of the packet encoding. |
226 | | * |
227 | | * bit_offset is simply (*offset) * 8 + bitindex, and bitindex increments by field->size. |
228 | | */ |
229 | 0 | bit_offset = (*offset) * 8 + bitindex; |
230 | 0 | proto_tree_add_bits_item(ltree, field->descr, tvb, bit_offset, field->size, ENC_LITTLE_ENDIAN); |
231 | |
|
232 | 0 | bitindex += field->size; |
233 | 0 | (*offset) += bitindex / 8; |
234 | 0 | bitindex %= 8; |
235 | 0 | } |
236 | 0 | } |
237 | 0 | break; |
238 | 0 | case 16: |
239 | 0 | proto_tree_add_item(ltree, field->descr, tvb, *offset, 2, encoding); |
240 | 0 | (*offset) += 2; |
241 | 0 | break; |
242 | 0 | case 32: |
243 | 0 | proto_tree_add_item(ltree, field->descr, tvb, *offset, 4, encoding); |
244 | 0 | (*offset) += 4; |
245 | 0 | break; |
246 | 0 | case 64: |
247 | 0 | proto_tree_add_item(ltree, field->descr, tvb, *offset, 8, encoding); |
248 | 0 | (*offset) += 8; |
249 | 0 | break; |
250 | 0 | case 128: |
251 | 0 | if (field->special_type == TPNCP_IP_ADDR) { |
252 | 0 | if (address_family == TPNCP_IPV6 || address_family == TPNCP_IPV6_PSOS) |
253 | 0 | proto_tree_add_item(ltree, field->ipv6_descr, tvb, *offset, 16, encoding); |
254 | 0 | else |
255 | 0 | proto_tree_add_item(ltree, field->descr, tvb, *offset, 4, encoding); |
256 | 0 | address_family = TPNCP_IPV4; |
257 | 0 | } |
258 | 0 | (*offset) += 16; |
259 | 0 | break; |
260 | 0 | default: |
261 | 0 | break; |
262 | 0 | } |
263 | 0 | if (tvb_reported_length_remaining(tvb, *offset) <= 0) |
264 | 0 | break; |
265 | 0 | } |
266 | 0 | if ((g_str_len = tvb_reported_length_remaining(tvb, *offset)) > 0) { |
267 | 0 | expert_add_info_format(pinfo, ltree, &ei_tpncp_unknown_data, "TPNCP Unknown Data"); |
268 | 0 | (*offset) += g_str_len; |
269 | 0 | } |
270 | 0 | } |
271 | | |
272 | | /*---------------------------------------------------------------------------*/ |
273 | | static int |
274 | | dissect_tpncp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
275 | 1 | { |
276 | 1 | proto_item *item = NULL; |
277 | 1 | proto_tree *tpncp_tree = NULL, *event_tree, *command_tree; |
278 | 1 | int offset = 0, cid = -1; |
279 | 1 | unsigned id; |
280 | 1 | unsigned seq_number, len, ver; |
281 | 1 | unsigned len_ext, reserved, encoding; |
282 | 1 | uint32_t fullLength; |
283 | | |
284 | 1 | if (!db_initialized) |
285 | 1 | return 0; |
286 | | |
287 | 0 | encoding = tvb_get_ntohs(tvb, 8) == 0 ? ENC_BIG_ENDIAN : ENC_LITTLE_ENDIAN; |
288 | |
|
289 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "TPNCP"); |
290 | |
|
291 | 0 | item = proto_tree_add_item(tree, proto_tpncp, tvb, 0, -1, ENC_NA); |
292 | 0 | tpncp_tree = proto_item_add_subtree(item, ett_tpncp); |
293 | |
|
294 | 0 | proto_tree_add_item_ret_uint(tpncp_tree, hf_tpncp_version, tvb, 0, 2, encoding, &ver); |
295 | 0 | proto_tree_add_item_ret_uint(tpncp_tree, hf_tpncp_length, tvb, 2, 2, encoding, &len); |
296 | 0 | proto_tree_add_item_ret_uint(tpncp_tree, hf_tpncp_seq_number, tvb, 4, 2, encoding, &seq_number); |
297 | 0 | proto_tree_add_item_ret_uint(tpncp_tree, hf_tpncp_length_ext, tvb, 6, 1, encoding, &len_ext); |
298 | 0 | proto_tree_add_item_ret_uint(tpncp_tree, hf_tpncp_reserved, tvb, 7, 1, encoding, &reserved); |
299 | 0 | fullLength = 0xffff * len_ext + len; |
300 | |
|
301 | 0 | id = tvb_get_uint32(tvb, 8, encoding); |
302 | 0 | if (len > 8) |
303 | 0 | cid = tvb_get_int32(tvb, 12, encoding); |
304 | 0 | if (pinfo->srcport == UDP_PORT_TPNCP_TRUNKPACK || |
305 | 0 | pinfo->srcport == HA_PORT_TPNCP_TRUNKPACK) { |
306 | 0 | if (try_val_to_str(id, tpncp_events_id_vals)) { |
307 | 0 | proto_tree_add_uint(tpncp_tree, hf_tpncp_event_id, tvb, 8, 4, id); |
308 | 0 | if (len > 8) |
309 | 0 | proto_tree_add_int(tpncp_tree, hf_tpncp_cid, tvb, 12, 4, cid); |
310 | 0 | offset += 16; |
311 | 0 | if (id < tpncp_events_info_len && tpncp_events_info_db[id] != NULL && len > 12) { |
312 | 0 | event_tree = proto_tree_add_subtree_format( |
313 | 0 | tree, tvb, offset, -1, ett_tpncp_body, NULL, |
314 | 0 | "TPNCP Event: %s (%d)", |
315 | 0 | val_to_str_const(id, tpncp_events_id_vals, "Unknown"), id); |
316 | 0 | dissect_tpncp_data(id, pinfo, tvb, event_tree, &offset, tpncp_events_info_db, |
317 | 0 | ver, encoding); |
318 | 0 | } |
319 | 0 | } |
320 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, |
321 | 0 | "EvID=%s(%d), SeqNo=%d, CID=%d, Len=%d, Ver=%d", |
322 | 0 | val_to_str_const(id, tpncp_events_id_vals, "Unknown"), |
323 | 0 | id, seq_number, cid, fullLength, ver); |
324 | 0 | } else { |
325 | 0 | if (try_val_to_str(id, tpncp_commands_id_vals)) { |
326 | 0 | proto_tree_add_uint(tpncp_tree, hf_tpncp_command_id, tvb, 8, 4, id); |
327 | 0 | offset += 12; |
328 | 0 | if (id < tpncp_commands_info_len && tpncp_commands_info_db[id] != NULL && len > 8) { |
329 | 0 | command_tree = proto_tree_add_subtree_format( |
330 | 0 | tree, tvb, offset, -1, ett_tpncp_body, NULL, |
331 | 0 | "TPNCP Command: %s (%d)", |
332 | 0 | val_to_str_const(id, tpncp_commands_id_vals, "Unknown"), id); |
333 | 0 | dissect_tpncp_data(id, pinfo, tvb, command_tree, &offset, tpncp_commands_info_db, |
334 | 0 | ver, encoding); |
335 | 0 | } |
336 | 0 | } |
337 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, |
338 | 0 | "CmdID=%s(%d), SeqNo=%d, CID=%d, Len=%d, Ver=%d", |
339 | 0 | val_to_str_const(id, tpncp_commands_id_vals, "Unknown"), |
340 | 0 | id, seq_number, cid, fullLength, ver); |
341 | 0 | } |
342 | |
|
343 | 0 | return tvb_reported_length(tvb); |
344 | 1 | } |
345 | | |
346 | | /*---------------------------------------------------------------------------*/ |
347 | | |
348 | | static unsigned |
349 | | get_tpncp_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) |
350 | 0 | { |
351 | 0 | uint32_t plen; |
352 | | |
353 | | /* Get the length of the TPNCP packet. */ |
354 | 0 | plen = tvb_get_ntohs(tvb, offset + 2) + 0xffff * tvb_get_uint8(tvb, offset + 6); |
355 | | |
356 | | /* Length does not include the version+length field. */ |
357 | 0 | plen += 4; |
358 | |
|
359 | 0 | return plen; |
360 | 0 | } |
361 | | |
362 | | /*---------------------------------------------------------------------------*/ |
363 | | |
364 | | static int |
365 | | dissect_tpncp_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
366 | 1 | { |
367 | 1 | if (!db_initialized) |
368 | 1 | return 0; |
369 | | |
370 | 0 | if (pinfo->can_desegment) |
371 | | /* If desegmentation is enabled (TCP preferences) use the desegmentation API. */ |
372 | 0 | tcp_dissect_pdus(tvb, pinfo, tree, tpncp_desegment, 4, get_tpncp_pdu_len, |
373 | 0 | dissect_tpncp, data); |
374 | 0 | else |
375 | | /* Otherwise use the regular dissector (might not give correct dissection). */ |
376 | 0 | dissect_tpncp(tvb, pinfo, tree, data); |
377 | |
|
378 | 0 | return tvb_reported_length(tvb); |
379 | 1 | } |
380 | | |
381 | | static int |
382 | | dissect_acdr_event(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
383 | 0 | { |
384 | 0 | int res = 0; |
385 | 0 | acdr_dissector_data_t *acdr_data = (acdr_dissector_data_t *) data; |
386 | 0 | uint32_t orig_port = pinfo->srcport; |
387 | |
|
388 | 0 | if (acdr_data == NULL) |
389 | 0 | return 0; |
390 | | |
391 | | // only on version 2+ events are sent with TPNCP header that enables using tpncp parser |
392 | 0 | if (acdr_data->version <= 1) |
393 | 0 | return 0; |
394 | | |
395 | | // the TPNCP dissector uses the following statement to |
396 | | // differentiate command from event: |
397 | | // if (pinfo->srcport == UDP_PORT_TPNCP_TRUNKPACK) -> Event |
398 | | // so for proper dissection we want to imitate this behaviour |
399 | 0 | pinfo->srcport = UDP_PORT_TPNCP_TRUNKPACK; |
400 | 0 | res = dissect_tpncp(tvb, pinfo, tree, NULL); |
401 | 0 | pinfo->srcport = orig_port; |
402 | 0 | return res; |
403 | 0 | } |
404 | | |
405 | | static int |
406 | | dissect_acdr_tpncp_by_tracepoint(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
407 | 0 | { |
408 | 0 | acdr_dissector_data_t *acdr_data = (acdr_dissector_data_t *) data; |
409 | 0 | uint32_t orig_port = pinfo->srcport; |
410 | 0 | int res = 0; |
411 | |
|
412 | 0 | if (acdr_data == NULL) |
413 | 0 | return 0; |
414 | | |
415 | | // the TPNCP dissector uses the following statement to |
416 | | // differentiate command from event: |
417 | | // if (pinfo->srcport == UDP_PORT_TPNCP_TRUNKPACK) -> Event |
418 | | // so for proper dissection we want to imitate this behaviour |
419 | | |
420 | 0 | if (acdr_data->trace_point == Host2Net) // event |
421 | 0 | pinfo->srcport = UDP_PORT_TPNCP_TRUNKPACK; |
422 | 0 | else // Net2Host ->command |
423 | 0 | pinfo->srcport = UDP_PORT_TPNCP_TRUNKPACK + 1; |
424 | |
|
425 | 0 | res = dissect_tpncp(tvb, pinfo, tree, NULL); |
426 | 0 | pinfo->srcport = orig_port; |
427 | 0 | return res; |
428 | 0 | } |
429 | | |
430 | | /*---------------------------------------------------------------------------*/ |
431 | | |
432 | | static bool |
433 | | fgetline(char *buffer, int size, FILE *file) |
434 | 0 | { |
435 | 0 | if (!fgets(buffer, size, file)) |
436 | 0 | return 0; |
437 | 0 | size_t last = strlen(buffer); |
438 | 0 | if (buffer[last - 1] == '\n') |
439 | 0 | buffer[last - 1] = 0; |
440 | 0 | return 1; |
441 | 0 | } |
442 | | |
443 | | static int |
444 | | fill_tpncp_id_vals(value_string **strings, FILE *file) |
445 | 0 | { |
446 | 0 | wmem_array_t *vs_arr; |
447 | 0 | char *line_in_file; |
448 | |
|
449 | 0 | if (file == NULL) return -1; |
450 | | |
451 | 0 | line_in_file = (char *) g_malloc(MAX_TPNCP_DB_ENTRY_LEN); |
452 | 0 | vs_arr = wmem_array_new(NULL, sizeof **strings); |
453 | |
|
454 | 0 | while (fgetline(line_in_file, MAX_TPNCP_DB_ENTRY_LEN, file) && !feof(file)) { |
455 | 0 | int tpncp_id = 0; |
456 | 0 | char tpncp_name[256]; |
457 | |
|
458 | 0 | if (!strncmp(line_in_file, "#####", 5)) |
459 | 0 | break; |
460 | 0 | if (sscanf(line_in_file, "%255s %d", tpncp_name, &tpncp_id) == 2) { |
461 | 0 | value_string const string = { |
462 | 0 | .value = (uint32_t)tpncp_id, |
463 | 0 | .strptr = wmem_strdup(wmem_epan_scope(), tpncp_name) |
464 | 0 | }; |
465 | 0 | wmem_array_append_one(vs_arr, string); |
466 | 0 | } |
467 | 0 | } |
468 | |
|
469 | 0 | wmem_array_set_null_terminator(vs_arr); |
470 | 0 | *strings = wmem_array_finalize(vs_arr); |
471 | 0 | g_free(line_in_file); |
472 | |
|
473 | 0 | return 0; |
474 | 0 | } |
475 | | |
476 | | /*---------------------------------------------------------------------------*/ |
477 | | |
478 | | static int |
479 | | fill_enums_id_vals(char ***enum_names, value_string ***enum_value_strings, FILE *file) |
480 | 0 | { |
481 | 0 | wmem_array_t *enum_name_arr, *enum_vs_arr, *enum_vs = NULL; |
482 | 0 | char *line_in_file; |
483 | 0 | char enum_type[256]; |
484 | |
|
485 | 0 | line_in_file = (char *) g_malloc(MAX_TPNCP_DB_ENTRY_LEN); |
486 | 0 | enum_type[0] = '\0'; |
487 | |
|
488 | 0 | enum_name_arr = wmem_array_new(NULL, sizeof **enum_names); |
489 | 0 | enum_vs_arr = wmem_array_new(NULL, sizeof **enum_value_strings); |
490 | |
|
491 | 0 | while (fgetline(line_in_file, MAX_TPNCP_DB_ENTRY_LEN, file)) { |
492 | 0 | char enum_name[256], enum_str[256]; |
493 | 0 | int enum_id; |
494 | |
|
495 | 0 | if (!strncmp(line_in_file, "#####", 5)) |
496 | 0 | break; |
497 | 0 | if (sscanf(line_in_file, "%255s %255s %d", enum_name, enum_str, &enum_id) == 3) { |
498 | 0 | if (strcmp(enum_type, enum_name) != 0) { |
499 | | /* New record. */ |
500 | 0 | if (enum_vs != NULL) { |
501 | | /* The previous enum_vs is now complete. */ |
502 | 0 | wmem_array_set_null_terminator(enum_vs); |
503 | 0 | value_string *new_enum_vs = wmem_array_finalize(enum_vs); |
504 | 0 | wmem_array_append_one(enum_vs_arr, new_enum_vs); |
505 | 0 | } |
506 | 0 | enum_vs = wmem_array_sized_new(NULL, sizeof ***enum_value_strings, 10); |
507 | |
|
508 | 0 | char *enum_name_alloc = wmem_strdup(wmem_epan_scope(), enum_name); |
509 | 0 | wmem_array_append_one(enum_name_arr, enum_name_alloc); |
510 | 0 | g_strlcpy(enum_type, enum_name, sizeof enum_type); |
511 | 0 | } |
512 | 0 | value_string const vs = { |
513 | 0 | .value = enum_id, |
514 | 0 | .strptr = wmem_strdup(wmem_epan_scope(), enum_str) |
515 | 0 | }; |
516 | 0 | wmem_array_append_one(enum_vs, vs); |
517 | 0 | } |
518 | 0 | } |
519 | |
|
520 | 0 | if (enum_vs != NULL) { |
521 | | /* The final enum_vs is now complete. */ |
522 | 0 | wmem_array_set_null_terminator(enum_vs); |
523 | 0 | value_string *new_enum_vs = wmem_array_finalize(enum_vs); |
524 | 0 | wmem_array_append_one(enum_vs_arr, new_enum_vs); |
525 | 0 | } |
526 | |
|
527 | 0 | wmem_array_set_null_terminator(enum_name_arr); |
528 | 0 | *enum_names = (char **)wmem_array_finalize(enum_name_arr); |
529 | |
|
530 | 0 | wmem_array_set_null_terminator(enum_vs_arr); |
531 | 0 | *enum_value_strings = (value_string **)wmem_array_finalize(enum_vs_arr); |
532 | 0 | g_free(line_in_file); |
533 | |
|
534 | 0 | return 0; |
535 | 0 | } |
536 | | |
537 | | /*---------------------------------------------------------------------------*/ |
538 | | |
539 | | static int |
540 | | get_enum_name_val(const char *enum_name) |
541 | 0 | { |
542 | 0 | int enum_val = 0; |
543 | |
|
544 | 0 | while (tpncp_enums_name_vals[enum_val]) { |
545 | 0 | if (!strcmp(enum_name, tpncp_enums_name_vals[enum_val])) |
546 | 0 | return enum_val; |
547 | 0 | enum_val++; |
548 | 0 | } |
549 | | |
550 | 0 | return -1; |
551 | 0 | } |
552 | | |
553 | | /*---------------------------------------------------------------------------*/ |
554 | | |
555 | | static bool add_hf(hf_register_info *hf_entr) |
556 | 0 | { |
557 | 0 | if (hf_size >= hf_allocated) { |
558 | 0 | void *newbuf; |
559 | 0 | hf_allocated += 1024; |
560 | 0 | newbuf = wmem_realloc(wmem_epan_scope(), hf, hf_allocated * sizeof (hf_register_info)); |
561 | 0 | if (!newbuf) |
562 | 0 | return false; |
563 | 0 | hf = (hf_register_info *) newbuf; |
564 | 0 | } |
565 | 0 | memcpy(hf + hf_size, hf_entr, sizeof (hf_register_info)); |
566 | 0 | hf_size++; |
567 | 0 | return true; |
568 | 0 | } |
569 | | |
570 | | static int |
571 | | init_tpncp_data_fields_info(tpncp_data_field_info ***data_fields_info, unsigned *data_fields_len, FILE *file) |
572 | 0 | { |
573 | 0 | static bool was_registered = false; |
574 | 0 | char tpncp_db_entry[MAX_TPNCP_DB_ENTRY_LEN]; |
575 | 0 | char entry_copy[MAX_TPNCP_DB_ENTRY_LEN]; |
576 | 0 | const char *name = NULL, *tmp = NULL; |
577 | 0 | int enum_val, data_id, current_data_id = -1, array_dim; |
578 | 0 | unsigned char size; |
579 | 0 | enum SpecialFieldType special_type; |
580 | 0 | bool sign, is_address_family; |
581 | 0 | unsigned idx, since, ip_addr_field; |
582 | 0 | tpncp_data_field_info *field = NULL; |
583 | 0 | hf_register_info hf_entr; |
584 | 0 | wmem_array_t *data_fields_info_arr; |
585 | |
|
586 | 0 | hf_register_info hf_tpncp[] = { |
587 | 0 | { |
588 | 0 | &hf_tpncp_version, |
589 | 0 | { |
590 | 0 | "Version", |
591 | 0 | "tpncp.version", |
592 | 0 | FT_UINT16, |
593 | 0 | BASE_DEC, |
594 | 0 | NULL, |
595 | 0 | 0x0, |
596 | 0 | NULL, HFILL |
597 | 0 | } |
598 | 0 | }, |
599 | 0 | { |
600 | 0 | &hf_tpncp_length, |
601 | 0 | { |
602 | 0 | "Length", |
603 | 0 | "tpncp.length", |
604 | 0 | FT_UINT16, |
605 | 0 | BASE_DEC, |
606 | 0 | NULL, |
607 | 0 | 0x0, |
608 | 0 | NULL, HFILL |
609 | 0 | } |
610 | 0 | }, |
611 | 0 | { |
612 | 0 | &hf_tpncp_seq_number, |
613 | 0 | { |
614 | 0 | "Sequence number", |
615 | 0 | "tpncp.seq_number", |
616 | 0 | FT_UINT16, |
617 | 0 | BASE_DEC, |
618 | 0 | NULL, |
619 | 0 | 0x0, |
620 | 0 | NULL, HFILL |
621 | 0 | } |
622 | 0 | }, |
623 | 0 | { |
624 | 0 | &hf_tpncp_length_ext, |
625 | 0 | { |
626 | 0 | "Length Extension", |
627 | 0 | "tpncp.lengthextension", |
628 | 0 | FT_UINT8, |
629 | 0 | BASE_DEC, |
630 | 0 | NULL, |
631 | 0 | 0x0, |
632 | 0 | NULL, HFILL |
633 | 0 | } |
634 | 0 | }, |
635 | 0 | { |
636 | 0 | &hf_tpncp_reserved, |
637 | 0 | { |
638 | 0 | "Reserved", |
639 | 0 | "tpncp.reserved", |
640 | 0 | FT_UINT8, |
641 | 0 | BASE_DEC, |
642 | 0 | NULL, |
643 | 0 | 0x0, |
644 | 0 | NULL, HFILL |
645 | 0 | } |
646 | 0 | }, |
647 | 0 | { |
648 | 0 | &hf_tpncp_command_id, |
649 | 0 | { |
650 | 0 | "Command ID", |
651 | 0 | "tpncp.command_id", |
652 | 0 | FT_UINT32, |
653 | 0 | BASE_DEC, |
654 | 0 | VALS(tpncp_commands_id_vals), |
655 | 0 | 0x0, |
656 | 0 | NULL, HFILL |
657 | 0 | } |
658 | 0 | }, |
659 | 0 | { |
660 | 0 | &hf_tpncp_event_id, |
661 | 0 | { |
662 | 0 | "Event ID", |
663 | 0 | "tpncp.event_id", |
664 | 0 | FT_UINT32, |
665 | 0 | BASE_DEC, |
666 | 0 | VALS(tpncp_events_id_vals), |
667 | 0 | 0x0, |
668 | 0 | NULL, HFILL |
669 | 0 | } |
670 | 0 | }, |
671 | 0 | { |
672 | 0 | &hf_tpncp_cid, |
673 | 0 | { |
674 | 0 | "Channel ID", |
675 | 0 | "tpncp.channel_id", |
676 | 0 | FT_INT32, |
677 | 0 | BASE_DEC, |
678 | 0 | NULL, |
679 | 0 | 0x0, |
680 | 0 | NULL, HFILL |
681 | 0 | } |
682 | 0 | } |
683 | 0 | }; |
684 | | |
685 | | /* Register common fields of hf_register_info structure. */ |
686 | 0 | hf_entr.hfinfo.type = FT_NONE; |
687 | 0 | hf_entr.hfinfo.strings = NULL; |
688 | 0 | hf_entr.hfinfo.bitmask = 0x0; |
689 | 0 | hf_entr.hfinfo.blurb = NULL; |
690 | 0 | HFILL_INIT(hf_entr); |
691 | |
|
692 | 0 | if (!was_registered) { |
693 | 0 | void *newbuf; |
694 | | |
695 | | /* Register non-standard data should be done only once. */ |
696 | 0 | hf_allocated = hf_size + (int) array_length(hf_tpncp); |
697 | 0 | newbuf = wmem_realloc(wmem_epan_scope(), hf, hf_allocated * sizeof (hf_register_info)); |
698 | 0 | if (!newbuf) |
699 | 0 | return -1; |
700 | 0 | hf = (hf_register_info *) newbuf; |
701 | 0 | for (idx = 0; idx < array_length(hf_tpncp); idx++) { |
702 | 0 | memcpy(hf + hf_size, hf_tpncp + idx, sizeof (hf_register_info)); |
703 | 0 | hf_size++; |
704 | 0 | } |
705 | 0 | was_registered = true; |
706 | 0 | } |
707 | | |
708 | 0 | is_address_family = false; |
709 | 0 | ip_addr_field = 0; |
710 | |
|
711 | 0 | data_fields_info_arr = wmem_array_new(NULL, sizeof **data_fields_info); |
712 | | |
713 | | /* Register standard data. */ |
714 | 0 | while (fgetline(tpncp_db_entry, MAX_TPNCP_DB_ENTRY_LEN, file)) { |
715 | 0 | special_type = TPNCP_NORMAL; |
716 | 0 | since = 0; |
717 | 0 | snprintf(entry_copy, MAX_TPNCP_DB_ENTRY_LEN, "%s", tpncp_db_entry); |
718 | 0 | if (!strncmp(tpncp_db_entry, "#####", 5)) |
719 | 0 | break; |
720 | | |
721 | | /* Default to decimal display type */ |
722 | 0 | hf_entr.hfinfo.display = BASE_DEC; |
723 | 0 | if ((tmp = strtok(tpncp_db_entry, " ")) == NULL) { |
724 | 0 | report_failure( |
725 | 0 | "ERROR! Badly formed data base entry: %s - corresponding field's registration is skipped.", |
726 | 0 | entry_copy); |
727 | 0 | continue; |
728 | 0 | } |
729 | 0 | data_id = (int) g_ascii_strtoll(tmp, NULL, 10); |
730 | 0 | if ((name = strtok(NULL, " ")) == NULL) { |
731 | 0 | report_failure( |
732 | 0 | "ERROR! Badly formed data base entry: %s - corresponding field's registration is skipped.", |
733 | 0 | entry_copy); |
734 | 0 | continue; |
735 | 0 | } |
736 | | |
737 | | /* We happen to have a line without a name (57 0 32 0 0 primitive). Consider unnamed. */ |
738 | 0 | if (g_ascii_isdigit(*name)) { |
739 | 0 | tmp = name; |
740 | 0 | name = "unnamed"; |
741 | 0 | } else { |
742 | 0 | if ((tmp = strtok(NULL, " ")) == NULL) { |
743 | 0 | report_failure( |
744 | 0 | "ERROR! Badly formed data base entry: %s - corresponding field's registration is skipped.", |
745 | 0 | entry_copy); |
746 | 0 | continue; |
747 | 0 | } |
748 | 0 | } |
749 | 0 | if (name[0] == 'c' && !strcmp(name, "cmd_rev_lsb")) |
750 | 0 | special_type = TPNCP_OPEN_CHANNEL_START; |
751 | 0 | else if (name[0] == 'r' && !strcmp(name, "rtp_authentication_algorithm")) |
752 | 0 | special_type = TPNCP_SECURITY_START; |
753 | 0 | else if (name[0] == 's' && !strcmp(name, "security_cmd_offset")) |
754 | 0 | special_type = TPNCP_SECURITY_OFFSET; |
755 | 0 | else if (data_id != 1611 && name[0] == 's' && !strcmp(name, "ssrc")) |
756 | 0 | special_type = RTP_STATE_START; |
757 | 0 | else if (name[0] == 'r' && !strcmp(name, "rtp_tx_state_ssrc")) |
758 | 0 | special_type = RTP_STATE_START; |
759 | 0 | else if (name[0] == 'r' && !strcmp(name, "rtp_state_offset")) |
760 | 0 | special_type = RTP_STATE_OFFSET; |
761 | 0 | else if (name[0] == 's' && !strcmp(name, "state_update_time_stamp")) |
762 | 0 | special_type = RTP_STATE_END; |
763 | 0 | else if (data_id == 1611 && name[0] == 'c' && strstr(name, "configuration_type_updated")) |
764 | 0 | special_type = TPNCP_CHANNEL_CONFIGURATION; |
765 | 0 | else if ((data_id == 4 && strstr(name, "secondary_rtp_seq_num")) || |
766 | 0 | (data_id == 1611 && strstr(name, "dtls_remote_fingerprint_alg"))) { |
767 | 0 | since = 7401; |
768 | 0 | } |
769 | 0 | sign = !!((bool) g_ascii_strtoll(tmp, NULL, 10)); |
770 | 0 | if ((tmp = strtok(NULL, " ")) == NULL) { |
771 | 0 | report_failure( |
772 | 0 | "ERROR! Badly formed data base entry: %s - corresponding field's registration is skipped.", |
773 | 0 | entry_copy); |
774 | 0 | continue; |
775 | 0 | } |
776 | 0 | size = (unsigned char) g_ascii_strtoll(tmp, NULL, 10); |
777 | 0 | if ((tmp = strtok(NULL, " ")) == NULL) { |
778 | 0 | report_failure( |
779 | 0 | "ERROR! Badly formed data base entry: %s - corresponding field's registration is skipped.", |
780 | 0 | entry_copy); |
781 | 0 | continue; |
782 | 0 | } |
783 | 0 | array_dim = (int) g_ascii_strtoll(tmp, NULL, 10); |
784 | 0 | if ((tmp = strtok(NULL, " ")) == NULL) { |
785 | 0 | report_failure( |
786 | 0 | "ERROR! Badly formed data base entry: %s - corresponding field's registration is skipped.", |
787 | 0 | entry_copy); |
788 | 0 | continue; |
789 | 0 | } |
790 | 0 | if (sign && g_ascii_strtoll(tmp, NULL, 10)) |
791 | 0 | special_type = TPNCP_IP_ADDR; |
792 | 0 | if ((tmp = strtok(NULL, "\n")) == NULL) { |
793 | 0 | report_failure( |
794 | 0 | "ERROR! Badly formed data base entry: %s - corresponding field's registration is skipped.", |
795 | 0 | entry_copy); |
796 | 0 | continue; |
797 | 0 | } |
798 | | |
799 | 0 | if (ip_addr_field > 0) { |
800 | | // ip address that comes after address family has 4 fields: ip_addr_0, ip_addr_1, 2 and 3 |
801 | | // On these cases, ignore 1, 2 and 3 and enlarge the field size of 0 to 128 |
802 | 0 | char *seq = (char*)name + strlen(name) - 2; |
803 | 0 | --ip_addr_field; |
804 | 0 | if (seq > name && *seq == '_') { |
805 | 0 | if (seq[1] >= '1' && seq[1] <= '3') |
806 | 0 | continue; |
807 | | // relates to the *previous* field |
808 | 0 | if (is_address_family) { |
809 | 0 | *seq = 0; |
810 | 0 | size = 128; |
811 | 0 | special_type = TPNCP_IP_ADDR; |
812 | 0 | } else { |
813 | 0 | report_warning("Bad address form. Field name: %s", name); |
814 | 0 | ip_addr_field = 0; |
815 | 0 | } |
816 | 0 | } |
817 | 0 | } |
818 | | |
819 | 0 | is_address_family = false; |
820 | 0 | if (current_data_id != data_id) { /* new data */ |
821 | 0 | tpncp_data_field_info **fp; |
822 | |
|
823 | 0 | while (wmem_array_get_count(data_fields_info_arr) <= (unsigned)data_id) { |
824 | 0 | static const tpncp_data_field_info **empty = NULL; |
825 | 0 | wmem_array_append_one(data_fields_info_arr, empty); |
826 | 0 | } |
827 | 0 | fp = (tpncp_data_field_info **)wmem_array_index(data_fields_info_arr, data_id); |
828 | 0 | if (*fp != NULL) { |
829 | 0 | report_failure( |
830 | 0 | "ERROR! The data_id %d already registered. Cannot register two identical events/command", |
831 | 0 | data_id); |
832 | 0 | continue; |
833 | 0 | } |
834 | 0 | field = wmem_new0(wmem_epan_scope(), tpncp_data_field_info); |
835 | 0 | *fp = field; |
836 | 0 | current_data_id = data_id; |
837 | 0 | } else { |
838 | 0 | field->p_next = wmem_new(wmem_epan_scope(), tpncp_data_field_info); |
839 | 0 | if (!field->p_next) |
840 | 0 | return (-1); |
841 | 0 | field = field->p_next; |
842 | 0 | field->p_next = NULL; |
843 | 0 | } |
844 | | |
845 | | /* Register specific fields of hf_register_info structure. */ |
846 | 0 | if (strcmp(tmp, "primitive")) { |
847 | 0 | enum_val = get_enum_name_val(tmp); |
848 | 0 | if (enum_val == -1) { |
849 | 0 | hf_entr.hfinfo.strings = NULL; |
850 | 0 | } else { |
851 | 0 | if (special_type == TPNCP_IP_ADDR) |
852 | 0 | special_type = TPNCP_NORMAL; |
853 | 0 | hf_entr.hfinfo.strings = VALS(tpncp_enums_id_vals[enum_val]); |
854 | 0 | if (!strcmp(tmp, "AddressFamily")) { |
855 | 0 | is_address_family = true; |
856 | 0 | ip_addr_field = 4; |
857 | 0 | } |
858 | 0 | } |
859 | 0 | } else { |
860 | 0 | hf_entr.hfinfo.strings = NULL; |
861 | 0 | } |
862 | 0 | field->descr = -1; |
863 | 0 | field->ipv6_descr = -1; |
864 | 0 | hf_entr.p_id = &field->descr; |
865 | 0 | field->name = wmem_strdup_printf(wmem_epan_scope(), "tpncp.%s", name); |
866 | 0 | hf_entr.hfinfo.name = field->name; |
867 | 0 | hf_entr.hfinfo.abbrev = field->name; |
868 | 0 | switch (size) { |
869 | 0 | case 1: |
870 | 0 | hf_entr.hfinfo.type = FT_BOOLEAN; |
871 | 0 | break; |
872 | 0 | case 2: case 3: case 4: |
873 | 0 | case 5: case 6: case 7: case 8: |
874 | 0 | if (array_dim) { |
875 | 0 | hf_entr.hfinfo.type = FT_STRING; |
876 | 0 | hf_entr.hfinfo.display = BASE_NONE; |
877 | 0 | } else { |
878 | 0 | hf_entr.hfinfo.type = (sign) ? FT_UINT8 : FT_INT8; |
879 | 0 | } |
880 | 0 | break; |
881 | 0 | case 16: |
882 | 0 | hf_entr.hfinfo.type = (sign) ? FT_UINT16 : FT_INT16; |
883 | 0 | break; |
884 | 0 | case 32: |
885 | 0 | if (special_type == TPNCP_IP_ADDR) { |
886 | 0 | hf_entr.hfinfo.display = BASE_NONE; |
887 | 0 | hf_entr.hfinfo.type = FT_IPv4; |
888 | 0 | } else { |
889 | 0 | hf_entr.hfinfo.type = (sign) ? FT_UINT32 : FT_INT32; |
890 | 0 | } |
891 | 0 | break; |
892 | 0 | case 64: |
893 | 0 | hf_entr.hfinfo.type = (sign) ? FT_UINT64 : FT_INT64; |
894 | 0 | break; |
895 | 0 | case 128: |
896 | 0 | if (special_type == TPNCP_IP_ADDR) { |
897 | 0 | hf_entr.hfinfo.display = BASE_NONE; |
898 | 0 | hf_entr.hfinfo.type = FT_IPv4; |
899 | 0 | if (!add_hf(&hf_entr)) |
900 | 0 | return -1; |
901 | 0 | hf_entr.p_id = &field->ipv6_descr; |
902 | 0 | hf_entr.hfinfo.type = FT_IPv6; |
903 | 0 | } |
904 | 0 | break; |
905 | 0 | default: |
906 | 0 | break; |
907 | 0 | } |
908 | | |
909 | | /* Register initialized hf_register_info in global database. */ |
910 | 0 | if (!add_hf(&hf_entr)) |
911 | 0 | return -1; |
912 | 0 | field->sign = sign; |
913 | 0 | field->size = size; |
914 | 0 | field->array_dim = array_dim; |
915 | 0 | field->special_type = is_address_family ? TPNCP_ADDRESS_FAMILY : special_type; |
916 | 0 | field->since = since; |
917 | 0 | } |
918 | | |
919 | 0 | *data_fields_len = wmem_array_get_count(data_fields_info_arr); |
920 | 0 | *data_fields_info = (tpncp_data_field_info **)wmem_array_finalize(data_fields_info_arr); |
921 | |
|
922 | 0 | return 0; |
923 | 0 | } |
924 | | |
925 | | /*---------------------------------------------------------------------------*/ |
926 | | |
927 | | static FILE * |
928 | | open_tpncp_dat(void) |
929 | 0 | { |
930 | 0 | char *path_buf = NULL; |
931 | 0 | const char *tpncp_dat_file_path = tpncp_dat_path; |
932 | 0 | FILE *res; |
933 | 0 | if (!*tpncp_dat_file_path) { |
934 | 0 | tpncp_dat_file_path = path_buf = g_build_path( |
935 | 0 | G_DIR_SEPARATOR_S, get_datafile_dir(epan_get_environment_prefix()), "tpncp", "tpncp.dat", NULL); |
936 | 0 | } |
937 | 0 | res = ws_fopen(tpncp_dat_file_path, "r"); |
938 | 0 | g_free(path_buf); |
939 | 0 | return res; |
940 | 0 | } |
941 | | |
942 | | static int |
943 | | init_tpncp_db(void) |
944 | 0 | { |
945 | | /* Open file with TPNCP data. */ |
946 | 0 | FILE *file = open_tpncp_dat(); |
947 | 0 | if (!file) { |
948 | 0 | return (-1); |
949 | 0 | } |
950 | 0 | fill_tpncp_id_vals(&tpncp_events_id_vals, file); |
951 | 0 | fill_tpncp_id_vals(&tpncp_commands_id_vals, file); |
952 | 0 | fill_enums_id_vals(&tpncp_enums_name_vals, &tpncp_enums_id_vals, file); |
953 | 0 | init_tpncp_data_fields_info(&tpncp_events_info_db, &tpncp_events_info_len, file); |
954 | 0 | init_tpncp_data_fields_info(&tpncp_commands_info_db, &tpncp_commands_info_len, file); |
955 | |
|
956 | 0 | fclose(file); |
957 | 0 | return 0; |
958 | 0 | } |
959 | | |
960 | | /*---------------------------------------------------------------------------*/ |
961 | | |
962 | | void |
963 | | proto_reg_handoff_tpncp(void) |
964 | 14 | { |
965 | 14 | static bool initialized = false; |
966 | | |
967 | 14 | if (proto_tpncp <= 0) return; |
968 | | |
969 | 14 | if (!initialized) { |
970 | 14 | dissector_add_uint_with_preference("udp.port", UDP_PORT_TPNCP_TRUNKPACK, tpncp_handle); |
971 | 14 | dissector_add_uint_with_preference("tcp.port", TCP_PORT_TPNCP_TRUNKPACK, tpncp_tcp_handle); |
972 | 14 | dissector_add_uint("acdr.media_type", ACDR_PCIIF_COMMAND, tpncp_handle); |
973 | 14 | dissector_add_uint("acdr.media_type", ACDR_COMMAND, tpncp_handle); |
974 | 14 | dissector_add_uint("acdr.media_type", ACDR_Event, create_dissector_handle(dissect_acdr_event, proto_tpncp)); |
975 | 14 | dissector_add_uint("acdr.media_type", ACDR_TPNCP, |
976 | 14 | create_dissector_handle(dissect_acdr_tpncp_by_tracepoint, proto_tpncp)); |
977 | 14 | dissector_add_uint("acdr.tls_application", TLS_APP_TPNCP, tpncp_handle); |
978 | 14 | initialized = true; |
979 | 14 | } |
980 | | |
981 | 14 | if (!global_tpncp_load_db) |
982 | 14 | return; |
983 | | |
984 | | /* If we weren't able to load the database (and thus the hf_ entries) |
985 | | * do not attach to any ports (if we did then we'd get a "dissector bug" |
986 | | * assertions every time a packet is handed to us and we tried to use the |
987 | | * hf_ entry). |
988 | | |
989 | | */ |
990 | 0 | if (hf_allocated == 0 && init_tpncp_db() == -1) { |
991 | 0 | report_failure("tpncp: Could not load tpncp.dat file, tpncp dissector will not work"); |
992 | 0 | return; |
993 | 0 | } |
994 | | |
995 | 0 | if (db_initialized) |
996 | 0 | return; |
997 | | |
998 | | /* Rather than duplicating large quantities of code from |
999 | | * proto_register_field_array() and friends to sanitize the tpncp.dat file |
1000 | | * when we read it, just catch any exceptions we get while registering and |
1001 | | * take them as a hint that the file is corrupt. Then move on, so that at |
1002 | | * least the rest of the protocol dissectors will still work. |
1003 | | */ |
1004 | 0 | TRY { |
1005 | 0 | int idx; |
1006 | | /* The function proto_register_field_array does not work with dynamic |
1007 | | * arrays, so pass dynamic array elements one-by-one in the loop. |
1008 | | */ |
1009 | 0 | for (idx = 0; idx < hf_size; idx++) |
1010 | 0 | proto_register_field_array(proto_tpncp, &hf[idx], 1); |
1011 | 0 | } |
1012 | |
|
1013 | 0 | CATCH_ALL { |
1014 | 0 | report_failure("Corrupt tpncp.dat file, tpncp dissector will not work."); |
1015 | 0 | } |
1016 | |
|
1017 | 0 | ENDTRY; |
1018 | 0 | db_initialized = true; |
1019 | 0 | } |
1020 | | |
1021 | | /*---------------------------------------------------------------------------*/ |
1022 | | |
1023 | | void |
1024 | | proto_register_tpncp(void) |
1025 | 14 | { |
1026 | 14 | module_t *tpncp_module; |
1027 | 14 | expert_module_t* expert_tpncp; |
1028 | 14 | static int *ett[] = { |
1029 | 14 | &ett_tpncp, |
1030 | 14 | &ett_tpncp_body |
1031 | 14 | }; |
1032 | | |
1033 | 14 | static ei_register_info ei[] = { |
1034 | 14 | { &ei_tpncp_unknown_data, { "tpncp.unknown_data", PI_UNDECODED, PI_WARN, "Unknown data", EXPFILL } }, |
1035 | 14 | }; |
1036 | | |
1037 | | /* this dissector reads hf entries from a database |
1038 | | * a boolean preference defines whether the database is loaded or not |
1039 | | * we initialize the hf array in the handoff function when we have |
1040 | | * access to the preference's value */ |
1041 | | |
1042 | 14 | proto_tpncp = proto_register_protocol("TPNCP (TrunkPack Network Control Protocol)", |
1043 | 14 | "TPNCP", "tpncp"); |
1044 | | |
1045 | 14 | tpncp_handle = register_dissector("tpncp", dissect_tpncp, proto_tpncp); |
1046 | 14 | tpncp_tcp_handle = register_dissector("tpncp.tcp", dissect_tpncp_tcp, proto_tpncp); |
1047 | | |
1048 | 14 | tpncp_module = prefs_register_protocol(proto_tpncp, proto_reg_handoff_tpncp); |
1049 | | |
1050 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
1051 | | |
1052 | 14 | expert_tpncp = expert_register_protocol(proto_tpncp); |
1053 | 14 | expert_register_field_array(expert_tpncp, ei, array_length(ei)); |
1054 | | |
1055 | | /* See https://gitlab.com/wireshark/wireshark/-/issues/9569 for some discussion on this as well */ |
1056 | 14 | prefs_register_bool_preference(tpncp_module, "load_db", |
1057 | 14 | "Whether to load DB or not; if DB not loaded dissector is passive", |
1058 | 14 | "Whether to load the Database or not; not loading the DB" |
1059 | 14 | " disables the protocol; Wireshark has to be restarted for the" |
1060 | 14 | " setting to take effect.", |
1061 | 14 | &global_tpncp_load_db); |
1062 | 14 | prefs_register_filename_preference(tpncp_module, "dat_file", |
1063 | 14 | "TPNCP data file (tpncp.dat)", |
1064 | 14 | "Path to tpncp.dat. If empty, the file shipped with Wireshark will be used.\n" |
1065 | 14 | "Changing the path requires a Wireshark restart to take effect.", |
1066 | | &tpncp_dat_path, false); |
1067 | 14 | } |
1068 | | |
1069 | | /* |
1070 | | * Editor modelines |
1071 | | * |
1072 | | * Local Variables: |
1073 | | * c-basic-offset: 4 |
1074 | | * tab-width: 8 |
1075 | | * indent-tabs-mode: nil |
1076 | | * End: |
1077 | | * |
1078 | | * ex: set shiftwidth=4 tabstop=8 expandtab: |
1079 | | * :indentSize=4:tabSize=8:noTabs=true: |
1080 | | */ |