/src/wireshark/epan/dissectors/packet-ldss.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* packet-ldss.c |
2 | | * Routines for Local Download Sharing Service dissection |
3 | | * Copyright 2009, Vasantha Crabb <vcrabb@managesoft.com.au> |
4 | | * and Chris Adams <cadams@managesoft.com.au> |
5 | | * |
6 | | * Wireshark - Network traffic analyzer |
7 | | * By Gerald Combs <gerald@wireshark.org> |
8 | | * Copyright 1998 Gerald Combs |
9 | | * |
10 | | * SPDX-License-Identifier: GPL-2.0-or-later |
11 | | */ |
12 | | |
13 | | /* LDSS is a protocol for peers on a LAN to cooperatively download |
14 | | * files from a WAN. The peers ask each other about files and can |
15 | | * send files to each other, thus WAN use is minimized. However |
16 | | * if no peer possesses a file, a peer can download it via the WAN. |
17 | | * Usually the download uses HTTP, but WAN downloads are beyond |
18 | | * the scope of this dissector. To avoid saturating the WAN link, |
19 | | * peers also tell each other what they are fetching and how fast |
20 | | * they're downloading. Files are identified only by digests. |
21 | | * Broadcasts are sent via UDP and files transferred via TCP. Both |
22 | | * UDP and TCP portions of the protocol are handled in this dissector. |
23 | | */ |
24 | | |
25 | | #include "config.h" |
26 | | |
27 | | #include <stdlib.h> |
28 | | #include <math.h> |
29 | | |
30 | | #include <epan/packet.h> |
31 | | #include <epan/expert.h> |
32 | | #include <epan/strutil.h> |
33 | | #include "packet-tcp.h" |
34 | | |
35 | | /* The digest is up to 32 bytes long */ |
36 | 0 | #define DIGEST_LEN 32 |
37 | | |
38 | 0 | #define MESSAGE_ID_NEEDFILE 0 |
39 | 0 | #define MESSAGE_ID_WILLSEND 1 |
40 | | |
41 | | |
42 | | static const value_string ldss_message_id_value[] = { |
43 | | { MESSAGE_ID_NEEDFILE, "Need file" }, |
44 | | { MESSAGE_ID_WILLSEND, "Will send" }, |
45 | | { 0, NULL } |
46 | | }; |
47 | | |
48 | | /* Message detail is inferred from various contents in the packet */ |
49 | 0 | #define INFERRED_PEERSHUTDOWN 0 |
50 | 0 | #define INFERRED_SEARCH 1 |
51 | 0 | #define INFERRED_OFFER 2 |
52 | 0 | #define INFERRED_PROMISE 3 |
53 | 0 | #define INFERRED_WANDOWNLOAD 4 |
54 | 0 | #define INFERRED_NONE 5 |
55 | | |
56 | | /* Displayed in the info column */ |
57 | | static const value_string ldss_inferred_info[] = { |
58 | | { INFERRED_PEERSHUTDOWN, " - peer shutting down" }, |
59 | | { INFERRED_SEARCH, " - search" }, |
60 | | { INFERRED_OFFER, " - offer" }, |
61 | | { INFERRED_PROMISE, " - promise" }, |
62 | | { INFERRED_WANDOWNLOAD, " - WAN download start" }, |
63 | | { INFERRED_NONE, "" }, |
64 | | { 0, NULL } |
65 | | }; |
66 | | |
67 | | /* Displayed in the tree as a generated item */ |
68 | | static const value_string ldss_inferred_value[] = { |
69 | | { INFERRED_PEERSHUTDOWN, "Peer shutdown" }, |
70 | | { INFERRED_SEARCH, "File search" }, |
71 | | { INFERRED_OFFER, "File offer" }, |
72 | | { INFERRED_PROMISE, "Promise (download in progress)" }, |
73 | | { INFERRED_WANDOWNLOAD, "WAN download start" }, |
74 | | { INFERRED_NONE, "" }, |
75 | | { 0, NULL } |
76 | | }; |
77 | | |
78 | | |
79 | 0 | #define DIGEST_TYPE_UNKNOWN 0 |
80 | 0 | #define DIGEST_TYPE_MD5 1 |
81 | 0 | #define DIGEST_TYPE_SHA1 2 |
82 | 0 | #define DIGEST_TYPE_SHA256 3 |
83 | | |
84 | | |
85 | | static const value_string ldss_digest_type_value[] = { |
86 | | { DIGEST_TYPE_UNKNOWN, "Unknown" }, |
87 | | { DIGEST_TYPE_MD5, "MD5" }, |
88 | | { DIGEST_TYPE_SHA1, "SHA1" }, |
89 | | { DIGEST_TYPE_SHA256, "SHA256" }, |
90 | | { 0, NULL } |
91 | | }; |
92 | | |
93 | | |
94 | | #define COMPRESSION_NONE 0 |
95 | 0 | #define COMPRESSION_GZIP 1 |
96 | | |
97 | | |
98 | | static const value_string ldss_compression_value[] = { |
99 | | { COMPRESSION_NONE, "None" }, |
100 | | { COMPRESSION_GZIP, "gzip" }, |
101 | | { 0, NULL } |
102 | | }; |
103 | | |
104 | | /* Info about a broadcaster */ |
105 | | typedef struct _ldss_broadcaster_t { |
106 | | address addr; |
107 | | uint16_t port; |
108 | | } ldss_broadcaster_t; |
109 | | |
110 | | /* Info about a file */ |
111 | | typedef struct _ldss_file_t { |
112 | | uint8_t *digest; |
113 | | uint8_t digest_type; |
114 | | } ldss_file_t; |
115 | | |
116 | | /* Info about a broadcast packet */ |
117 | | typedef struct _ldss_broadcast_t { |
118 | | uint32_t num; |
119 | | nstime_t ts; |
120 | | uint16_t message_id; |
121 | | uint16_t message_detail; |
122 | | uint16_t port; |
123 | | uint64_t size; |
124 | | uint64_t offset; |
125 | | uint8_t compression; |
126 | | ldss_file_t *file; |
127 | | ldss_broadcaster_t *broadcaster; |
128 | | } ldss_broadcast_t; |
129 | | |
130 | | /* Info about a file as seen in a file request */ |
131 | | typedef struct _ldss_file_req_t { |
132 | | uint32_t num; |
133 | | nstime_t ts; |
134 | | uint64_t size; |
135 | | uint64_t offset; |
136 | | uint8_t compression; |
137 | | ldss_file_t *file; |
138 | | } ldss_file_request_t; |
139 | | |
140 | | /* Info attached to a file transfer conversation */ |
141 | | typedef struct _ldss_transfer_info_t { |
142 | | uint32_t resp_num; |
143 | | nstime_t resp_ts; |
144 | | /* Refers either to the file in the request (for pull) |
145 | | * or the file in the broadcast (for push) */ |
146 | | ldss_file_t *file; |
147 | | ldss_file_request_t *req; |
148 | | ldss_broadcast_t *broadcast; |
149 | | } ldss_transfer_info_t; |
150 | | |
151 | | /* Define udp_port for LDSS (IANA assigned) */ |
152 | 14 | #define UDP_PORT_LDSS 6087 |
153 | | |
154 | | void proto_register_ldss(void); |
155 | | void proto_reg_handoff_ldss(void); |
156 | | |
157 | | /* Define the ldss proto */ |
158 | | static int proto_ldss; |
159 | | |
160 | | /* Define headers for ldss */ |
161 | | static int hf_ldss_message_id; |
162 | | static int hf_ldss_message_detail; |
163 | | static int hf_ldss_digest_type; |
164 | | static int hf_ldss_compression; |
165 | | static int hf_ldss_cookie; |
166 | | static int hf_ldss_digest; |
167 | | static int hf_ldss_size; |
168 | | static int hf_ldss_offset; |
169 | | static int hf_ldss_target_time; |
170 | | static int hf_ldss_reserved_1; |
171 | | static int hf_ldss_port; |
172 | | static int hf_ldss_rate; |
173 | | static int hf_ldss_priority; |
174 | | static int hf_ldss_property_count; |
175 | | static int hf_ldss_properties; |
176 | | static int hf_ldss_file_data; |
177 | | static int hf_ldss_response_in; |
178 | | static int hf_ldss_response_to; |
179 | | static int hf_ldss_initiated_by; |
180 | | static int hf_ldss_transfer_response_time; |
181 | | static int hf_ldss_transfer_completed_in; |
182 | | |
183 | | /* Define the tree for ldss */ |
184 | | static int ett_ldss_broadcast; |
185 | | static int ett_ldss_transfer; |
186 | | static int ett_ldss_transfer_req; |
187 | | |
188 | | static expert_field ei_ldss_unrecognized_line; |
189 | | |
190 | | |
191 | | static dissector_handle_t ldss_udp_handle; |
192 | | static dissector_handle_t ldss_tcp_handle; |
193 | | |
194 | | /* When seeing a broadcast talking about an open TCP port on a host, create |
195 | | * a conversation to dissect anything sent/received at that address. Setup |
196 | | * protocol data so the TCP dissection knows what broadcast triggered it. */ |
197 | | static void |
198 | | prepare_ldss_transfer_conv(ldss_broadcast_t *broadcast) |
199 | 0 | { |
200 | 0 | if (!find_conversation(broadcast->num, &broadcast->broadcaster->addr, &broadcast->broadcaster->addr, |
201 | 0 | CONVERSATION_TCP, broadcast->broadcaster->port, broadcast->broadcaster->port, NO_ADDR_B|NO_PORT_B)) { |
202 | 0 | conversation_t *transfer_conv; |
203 | 0 | ldss_transfer_info_t *transfer_info; |
204 | |
|
205 | 0 | transfer_info = wmem_new0(wmem_file_scope(), ldss_transfer_info_t); |
206 | 0 | transfer_info->broadcast = broadcast; |
207 | | |
208 | | /* Preparation for later push/pull dissection */ |
209 | 0 | transfer_conv = conversation_new (broadcast->num, &broadcast->broadcaster->addr, &broadcast->broadcaster->addr, |
210 | 0 | CONVERSATION_TCP, broadcast->broadcaster->port, broadcast->broadcaster->port, NO_ADDR2|NO_PORT2); |
211 | 0 | conversation_add_proto_data(transfer_conv, proto_ldss, transfer_info); |
212 | 0 | conversation_set_dissector(transfer_conv, ldss_tcp_handle); |
213 | 0 | } |
214 | 0 | } |
215 | | |
216 | | /* Broadcasts are searches, offers or promises. |
217 | | * |
218 | | * Searches are sent by |
219 | | * a peer when it needs a file (ie. while applying its policy, when it needs |
220 | | * files such as installers to install software.) |
221 | | * |
222 | | * Each broadcast relates to one file and each file is identified only by its |
223 | | * checksum - no file names are ever used. A search times out after 10 seconds |
224 | | * (configurable) and the peer will then attempt to act on any offers by |
225 | | * downloading (via push or pull - see dissect_ldss_transfer) from those peers. |
226 | | * |
227 | | * If no offers are received, the search fails and the peer fetches the file |
228 | | * from a remote server, generally a HTTP server on the other side of a WAN. |
229 | | * The protocol exists to minimize the number of WAN downloads needed. |
230 | | * |
231 | | * While downloading from WAN the peer sends promises to inform other peers |
232 | | * when it will be available for them to download. This prevents multiple peers |
233 | | * simultaneously downloading the same file. Promises also inform other peers |
234 | | * how much download bandwidth is being used by their download. Other peers use |
235 | | * this information and the configured knowledge of the WAN bandwidth to avoid |
236 | | * saturating the WAN link, as file downloads are a non-time-critical and |
237 | | * non-business-critical network function. LDSS is intended for networks of |
238 | | * 5-20 machines connected by slow WAN link. The current implementation of the |
239 | | * protocol allows administrator to configure "time windows" when WAN usage is |
240 | | * throttled/unthrottled, though this isn't visible in LDSS. |
241 | | * |
242 | | * Once a WAN download or a LAN transfer (see below above dissect_ldss_transfer) |
243 | | * has complete the peer will offer the file to other peers on the LAN so they |
244 | | * don't need to download it themselves. |
245 | | * |
246 | | * Peers also notify when they shut down in case any other peer is waiting for |
247 | | * a file. */ |
248 | | static int |
249 | | dissect_ldss_broadcast(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) |
250 | 0 | { |
251 | 0 | uint16_t messageID; |
252 | 0 | uint8_t digest_type; |
253 | 0 | uint8_t compression; |
254 | 0 | uint32_t cookie; |
255 | 0 | uint8_t *digest; |
256 | 0 | uint64_t size; |
257 | 0 | uint64_t offset; |
258 | 0 | uint32_t targetTime; |
259 | 0 | uint16_t port; |
260 | 0 | uint16_t rate; |
261 | 0 | uint16_t messageDetail = INFERRED_NONE; |
262 | |
|
263 | 0 | proto_tree *ti, *ldss_tree; |
264 | |
|
265 | 0 | const char *packet_type, *packet_detail; |
266 | |
|
267 | 0 | messageID = tvb_get_ntohs (tvb, 0); |
268 | 0 | digest_type = tvb_get_uint8 (tvb, 2); |
269 | 0 | compression = tvb_get_uint8 (tvb, 3); |
270 | 0 | cookie = tvb_get_ntohl (tvb, 4); |
271 | 0 | digest = (uint8_t *)tvb_memdup (wmem_file_scope(), tvb, 8, DIGEST_LEN); |
272 | 0 | size = tvb_get_ntoh64 (tvb, 40); |
273 | 0 | offset = tvb_get_ntoh64 (tvb, 48); |
274 | 0 | targetTime = tvb_get_ntohl (tvb, 56); |
275 | 0 | port = tvb_get_ntohs (tvb, 64); |
276 | 0 | rate = tvb_get_ntohs (tvb, 66); |
277 | |
|
278 | 0 | packet_type = val_to_str_const(messageID, ldss_message_id_value, "unknown"); |
279 | |
|
280 | 0 | if (messageID == MESSAGE_ID_WILLSEND) { |
281 | 0 | if (cookie == 0) { |
282 | | /* Shutdown: Dishonor promises from this peer. Current |
283 | | * implementation abuses WillSend for this. */ |
284 | 0 | messageDetail = INFERRED_PEERSHUTDOWN; |
285 | 0 | } |
286 | 0 | else if (size == 0 && offset == 0) { |
287 | | /* NeedFile search failed - going to WAN */ |
288 | 0 | messageDetail = INFERRED_WANDOWNLOAD; |
289 | 0 | } |
290 | 0 | else if (size > 0) { |
291 | | /* Size is known (not always the case) */ |
292 | 0 | if (size == offset) { |
293 | | /* File is available for pull on this peer's TCP port */ |
294 | 0 | messageDetail = INFERRED_OFFER; |
295 | 0 | } |
296 | 0 | else { |
297 | | /* WAN download progress announcement from this peer */ |
298 | 0 | messageDetail = INFERRED_PROMISE; |
299 | 0 | } |
300 | 0 | } |
301 | 0 | } |
302 | 0 | else if (messageID == MESSAGE_ID_NEEDFILE) { |
303 | 0 | messageDetail = INFERRED_SEARCH; |
304 | 0 | } |
305 | 0 | packet_detail = val_to_str_const(messageDetail, ldss_inferred_info, "unknown"); |
306 | | |
307 | | /* Set the info column */ |
308 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, "LDSS Broadcast (%s%s)", |
309 | 0 | packet_type, |
310 | 0 | packet_detail); |
311 | | |
312 | | /* If we have a non-null tree (ie we are building the proto_tree |
313 | | * instead of just filling out the columns), then give more detail. */ |
314 | 0 | ti = proto_tree_add_item(tree, proto_ldss, |
315 | 0 | tvb, 0, (tvb_captured_length(tvb) > 72) ? tvb_captured_length(tvb) : 72, ENC_NA); |
316 | 0 | ldss_tree = proto_item_add_subtree(ti, ett_ldss_broadcast); |
317 | |
|
318 | 0 | proto_tree_add_item(ldss_tree, hf_ldss_message_id, |
319 | 0 | tvb, 0, 2, ENC_BIG_ENDIAN); |
320 | 0 | ti = proto_tree_add_uint(ldss_tree, hf_ldss_message_detail, |
321 | 0 | tvb, 0, 0, messageDetail); |
322 | 0 | proto_item_set_generated(ti); |
323 | 0 | proto_tree_add_item(ldss_tree, hf_ldss_digest_type, |
324 | 0 | tvb, 2, 1, ENC_BIG_ENDIAN); |
325 | 0 | proto_tree_add_item(ldss_tree, hf_ldss_compression, |
326 | 0 | tvb, 3, 1, ENC_BIG_ENDIAN); |
327 | 0 | proto_tree_add_uint_format_value(ldss_tree, hf_ldss_cookie, |
328 | 0 | tvb, 4, 4, false, |
329 | 0 | "0x%x%s", |
330 | 0 | cookie, |
331 | 0 | (cookie == 0) |
332 | 0 | ? " - shutdown (promises from this peer are no longer valid)" |
333 | 0 | : ""); |
334 | 0 | proto_tree_add_item(ldss_tree, hf_ldss_digest, |
335 | 0 | tvb, 8, DIGEST_LEN, ENC_NA); |
336 | 0 | proto_tree_add_item(ldss_tree, hf_ldss_size, |
337 | 0 | tvb, 40, 8, ENC_BIG_ENDIAN); |
338 | 0 | proto_tree_add_item(ldss_tree, hf_ldss_offset, |
339 | 0 | tvb, 48, 8, ENC_BIG_ENDIAN); |
340 | 0 | proto_tree_add_uint_format_value(ldss_tree, hf_ldss_target_time, |
341 | 0 | tvb, 56, 4, false, |
342 | 0 | "%d:%02d:%02d", |
343 | 0 | (int)(targetTime / 3600), |
344 | 0 | (int)((targetTime / 60) % 60), |
345 | 0 | (int)(targetTime % 60)); |
346 | 0 | proto_tree_add_item(ldss_tree, hf_ldss_reserved_1, |
347 | 0 | tvb, 60, 4, ENC_BIG_ENDIAN); |
348 | 0 | proto_tree_add_uint_format_value(ldss_tree, hf_ldss_port, |
349 | 0 | tvb, 64, 2, false, |
350 | 0 | "%d%s", |
351 | 0 | port, |
352 | 0 | (messageID == MESSAGE_ID_WILLSEND && |
353 | 0 | size > 0 && |
354 | 0 | size == offset) |
355 | 0 | ? " - file can be pulled at this TCP port" |
356 | 0 | : (messageID == MESSAGE_ID_NEEDFILE |
357 | 0 | ? " - file can be pushed to this TCP port" |
358 | 0 | : "")); |
359 | 0 | proto_tree_add_uint_format_value(ldss_tree, hf_ldss_rate, |
360 | 0 | tvb, 66, 2, false, |
361 | 0 | "%ld", |
362 | 0 | (rate > 0) |
363 | 0 | ? (long)floor(exp(rate * G_LN2 / 2048)) |
364 | 0 | : 0); |
365 | 0 | proto_tree_add_item(ldss_tree, hf_ldss_priority, |
366 | 0 | tvb, 68, 2, ENC_BIG_ENDIAN); |
367 | 0 | proto_tree_add_item(ldss_tree, hf_ldss_property_count, |
368 | 0 | tvb, 70, 2, ENC_BIG_ENDIAN); |
369 | 0 | if (tvb_reported_length(tvb) > 72) { |
370 | 0 | proto_tree_add_item(ldss_tree, hf_ldss_properties, |
371 | 0 | tvb, 72, tvb_captured_length(tvb) - 72, ENC_NA); |
372 | 0 | } |
373 | | |
374 | | /* Finally, store the broadcast and register ourselves to dissect |
375 | | * any pushes or pulls that result from this broadcast. All data |
376 | | * is pushed/pulled over TCP using the port from the broadcast |
377 | | * packet's port field. |
378 | | * Track each by a TCP conversation with the remote end wildcarded. |
379 | | * The TCP conv tracks back to a broadcast conv to determine what it |
380 | | * is in response to. |
381 | | * |
382 | | * These steps only need to be done once per packet, so a variable |
383 | | * tracks the highest frame number seen. Handles the case of first frame |
384 | | * being frame zero. */ |
385 | 0 | if ((messageDetail != INFERRED_PEERSHUTDOWN) && |
386 | 0 | !PINFO_FD_VISITED(pinfo)) { |
387 | |
|
388 | 0 | ldss_broadcast_t *data; |
389 | | |
390 | | /* Populate data from the broadcast */ |
391 | 0 | data = wmem_new0(wmem_file_scope(), ldss_broadcast_t); |
392 | 0 | data->num = pinfo->num; |
393 | 0 | data->ts = pinfo->abs_ts; |
394 | 0 | data->message_id = messageID; |
395 | 0 | data->message_detail = messageDetail; |
396 | 0 | data->port = port; |
397 | 0 | data->size = size; |
398 | 0 | data->offset = offset; |
399 | 0 | data->compression = compression; |
400 | |
|
401 | 0 | data->file = wmem_new0(wmem_file_scope(), ldss_file_t); |
402 | 0 | data->file->digest = digest; |
403 | 0 | data->file->digest_type = digest_type; |
404 | |
|
405 | 0 | data->broadcaster = wmem_new0(wmem_file_scope(), ldss_broadcaster_t); |
406 | 0 | copy_address_wmem(wmem_file_scope(), &data->broadcaster->addr, &pinfo->src); |
407 | 0 | data->broadcaster->port = port; |
408 | | |
409 | | /* Dissect any future pushes/pulls */ |
410 | 0 | if (port > 0) { |
411 | 0 | prepare_ldss_transfer_conv(data); |
412 | 0 | } |
413 | 0 | } |
414 | |
|
415 | 0 | return tvb_captured_length(tvb); |
416 | 0 | } |
417 | | |
418 | | /* Transfers happen in response to broadcasts, they are always TCP and are |
419 | | * used to send the file to the port mentioned in the broadcast. There are |
420 | | * 2 types of transfers: Pushes, which are direct responses to searches, |
421 | | * in which the peer that has the file connects to the peer that doesn't and |
422 | | * sends it, then disconnects. The other type of transfer is a pull, where |
423 | | * the peer that doesn't have the file connects to the peer that does and |
424 | | * requests it be sent. |
425 | | * |
426 | | * Pulls have a file request which identifies the desired file, |
427 | | * while pushes simply send the file. In practice this works because every |
428 | | * file the implementation sends searches for is on a different TCP port |
429 | | * on the searcher's machine. */ |
430 | | static int |
431 | | dissect_ldss_transfer (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) |
432 | 0 | { |
433 | 0 | conversation_t *transfer_conv; |
434 | 0 | ldss_transfer_info_t *transfer_info; |
435 | 0 | struct tcpinfo *transfer_tcpinfo; |
436 | 0 | proto_tree *ti, *line_tree = NULL, *ldss_tree = NULL; |
437 | 0 | nstime_t broadcast_response_time; |
438 | | |
439 | | /* Reject the packet if data is NULL */ |
440 | 0 | if (data == NULL) |
441 | 0 | return 0; |
442 | 0 | transfer_tcpinfo = (struct tcpinfo *)data; |
443 | |
|
444 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "LDSS"); |
445 | | |
446 | | /* Look for the transfer conversation; this was created during |
447 | | * earlier broadcast dissection (see prepare_ldss_transfer_conv) */ |
448 | 0 | transfer_conv = find_conversation (pinfo->num, &pinfo->src, &pinfo->dst, |
449 | 0 | CONVERSATION_TCP, pinfo->srcport, pinfo->destport, 0); |
450 | 0 | DISSECTOR_ASSERT(transfer_conv); |
451 | 0 | transfer_info = (ldss_transfer_info_t *)conversation_get_proto_data(transfer_conv, proto_ldss); |
452 | 0 | DISSECTOR_ASSERT(transfer_info); |
453 | | |
454 | | /* For a pull, the first packet in the TCP connection is the file request. |
455 | | * First packet is identified by relative seq/ack numbers of 1. |
456 | | * File request only appears on a pull (triggered by an offer - see above |
457 | | * about broadcasts) */ |
458 | 0 | if (transfer_tcpinfo->seq == 1 && |
459 | 0 | transfer_tcpinfo->lastackseq == 1 && |
460 | 0 | transfer_info->broadcast->message_id == MESSAGE_ID_WILLSEND) { |
461 | | /* LDSS pull transfers look a lot like HTTP. |
462 | | * Sample request: |
463 | | * md5:01234567890123... |
464 | | * Size: 2550 |
465 | | * Start: 0 |
466 | | * Compression: 0 |
467 | | * (remote end sends the file identified by the digest) */ |
468 | 0 | unsigned offset = 0; |
469 | |
|
470 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "LDSS File Transfer (Requesting file - pull)"); |
471 | |
|
472 | 0 | if (transfer_info->req == NULL) { |
473 | 0 | transfer_info->req = wmem_new0(wmem_file_scope(), ldss_file_request_t); |
474 | 0 | transfer_info->req->file = wmem_new0(wmem_file_scope(), ldss_file_t); |
475 | 0 | } |
476 | |
|
477 | 0 | ti = proto_tree_add_item(tree, proto_ldss, |
478 | 0 | tvb, 0, tvb_reported_length(tvb), ENC_NA); |
479 | 0 | ldss_tree = proto_item_add_subtree(ti, ett_ldss_transfer); |
480 | | |
481 | | /* Populate digest data into the file struct in the request */ |
482 | 0 | transfer_info->file = transfer_info->req->file; |
483 | | |
484 | | /* Grab each line from the packet, there should be 4 but lets |
485 | | * not walk off the end looking for more. */ |
486 | 0 | while (tvb_offset_exists(tvb, offset)) { |
487 | 0 | int next_offset; |
488 | 0 | const uint8_t *line; |
489 | 0 | int linelen; |
490 | 0 | unsigned digest_type_len = 0; |
491 | |
|
492 | 0 | linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, false); |
493 | | |
494 | | /* Include new-line in line */ |
495 | 0 | line = tvb_get_string_enc(pinfo->pool, tvb, offset, linelen, ENC_ASCII); |
496 | |
|
497 | 0 | line_tree = proto_tree_add_subtree(ldss_tree, tvb, offset, linelen, |
498 | 0 | ett_ldss_transfer_req, NULL, |
499 | 0 | tvb_format_text(pinfo->pool, tvb, offset, next_offset-offset)); |
500 | |
|
501 | 0 | if (strncmp(line,"md5:",4)==0) { |
502 | 0 | digest_type_len = 4; |
503 | 0 | transfer_info->file->digest_type = DIGEST_TYPE_MD5; |
504 | 0 | } |
505 | 0 | else if (strncmp(line, "sha1:", 5)==0) { |
506 | 0 | digest_type_len = 5; |
507 | 0 | transfer_info->file->digest_type = DIGEST_TYPE_SHA1; |
508 | 0 | } |
509 | 0 | else if (strncmp(line, "sha256:", 7)==0) { |
510 | 0 | digest_type_len = 7; |
511 | 0 | transfer_info->file->digest_type = DIGEST_TYPE_SHA256; |
512 | 0 | } |
513 | 0 | else if (strncmp(line, "unknown:", 8)==0) { |
514 | 0 | digest_type_len = 8; |
515 | 0 | transfer_info->file->digest_type = DIGEST_TYPE_UNKNOWN; |
516 | 0 | } |
517 | 0 | else if (strncmp(line, "Size: ", 6)==0) { |
518 | | /* Sample size line: |
519 | | * Size: 2550\n */ |
520 | 0 | transfer_info->req->size = g_ascii_strtoull(line+6, NULL, 10); |
521 | 0 | ti = proto_tree_add_uint64(line_tree, hf_ldss_size, |
522 | 0 | tvb, offset+6, linelen-6, transfer_info->req->size); |
523 | 0 | proto_item_set_generated(ti); |
524 | 0 | } |
525 | 0 | else if (strncmp(line, "Start: ", 7)==0) { |
526 | | /* Sample offset line: |
527 | | * Start: 0\n */ |
528 | 0 | transfer_info->req->offset = g_ascii_strtoull(line+7, NULL, 10); |
529 | 0 | ti = proto_tree_add_uint64(line_tree, hf_ldss_offset, |
530 | 0 | tvb, offset+7, linelen-7, transfer_info->req->offset); |
531 | 0 | proto_item_set_generated(ti); |
532 | 0 | } |
533 | 0 | else if (strncmp(line, "Compression: ", 13)==0) { |
534 | | /* Sample compression line: |
535 | | * Compression: 0\n */ |
536 | 0 | transfer_info->req->compression = (int8_t)strtol(line+13, NULL, 10); /* XXX - bad cast */ |
537 | 0 | ti = proto_tree_add_uint(line_tree, hf_ldss_compression, |
538 | 0 | tvb, offset+13, linelen-13, transfer_info->req->compression); |
539 | 0 | proto_item_set_generated(ti); |
540 | 0 | } |
541 | 0 | else { |
542 | 0 | proto_tree_add_expert(line_tree, pinfo, &ei_ldss_unrecognized_line, tvb, offset, linelen); |
543 | 0 | } |
544 | |
|
545 | 0 | if (digest_type_len > 0) { |
546 | 0 | proto_item *tii = NULL; |
547 | | |
548 | | /* Sample digest-type/digest line: |
549 | | * md5:0123456789ABCDEF\n */ |
550 | 0 | if (!transfer_info->file->digest) { |
551 | 0 | GByteArray *digest_bytes; |
552 | |
|
553 | 0 | digest_bytes = g_byte_array_new(); |
554 | 0 | hex_str_to_bytes( |
555 | 0 | tvb_get_ptr(tvb, offset+digest_type_len, linelen-digest_type_len), |
556 | 0 | digest_bytes, false); |
557 | |
|
558 | 0 | if(digest_bytes->len >= DIGEST_LEN) |
559 | 0 | digest_bytes->len = (DIGEST_LEN-1); |
560 | | /* Ensure the digest is zero-padded */ |
561 | 0 | transfer_info->file->digest = (uint8_t *)wmem_alloc0(wmem_file_scope(), DIGEST_LEN); |
562 | 0 | memcpy(transfer_info->file->digest, digest_bytes->data, digest_bytes->len); |
563 | |
|
564 | 0 | g_byte_array_free(digest_bytes, true); |
565 | 0 | } |
566 | |
|
567 | 0 | tii = proto_tree_add_uint(line_tree, hf_ldss_digest_type, |
568 | 0 | tvb, offset, digest_type_len, transfer_info->file->digest_type); |
569 | 0 | proto_item_set_generated(tii); |
570 | 0 | tii = proto_tree_add_bytes(line_tree, hf_ldss_digest, |
571 | 0 | tvb, offset+digest_type_len, MIN(linelen-digest_type_len, DIGEST_LEN), |
572 | 0 | transfer_info->file->digest); |
573 | 0 | proto_item_set_generated(tii); |
574 | 0 | } |
575 | |
|
576 | 0 | offset = next_offset; |
577 | 0 | } |
578 | | |
579 | | /* Link forwards to the response for this pull. */ |
580 | 0 | if (transfer_info->resp_num != 0) { |
581 | 0 | ti = proto_tree_add_uint(ldss_tree, hf_ldss_response_in, |
582 | 0 | tvb, 0, 0, transfer_info->resp_num); |
583 | 0 | proto_item_set_generated(ti); |
584 | 0 | } |
585 | |
|
586 | 0 | transfer_info->req->num = pinfo->num; |
587 | 0 | transfer_info->req->ts = pinfo->abs_ts; |
588 | 0 | } |
589 | | /* Remaining packets are the file response */ |
590 | 0 | else { |
591 | 0 | uint64_t size; |
592 | 0 | uint64_t offset; |
593 | 0 | uint8_t compression; |
594 | | |
595 | | /* size, digest, compression come from the file request for a pull but |
596 | | * they come from the broadcast for a push. Pushes don't bother |
597 | | * with a file request - they just send the data. We have to get file |
598 | | * info from the offer broadcast which triggered this transfer. |
599 | | * If we cannot find the file request, default to the broadcast. */ |
600 | 0 | if (transfer_info->broadcast->message_id == MESSAGE_ID_WILLSEND && |
601 | 0 | transfer_info->req != NULL) { |
602 | 0 | transfer_info->file = transfer_info->req->file; |
603 | 0 | size = transfer_info->req->size; |
604 | 0 | offset = transfer_info->req->offset; |
605 | 0 | compression = transfer_info->req->compression; |
606 | 0 | } |
607 | 0 | else { |
608 | 0 | transfer_info->file = transfer_info->broadcast->file; |
609 | 0 | size = transfer_info->broadcast->size; |
610 | 0 | offset = transfer_info->broadcast->offset; |
611 | 0 | compression = transfer_info->broadcast->compression; |
612 | 0 | } |
613 | | |
614 | | /* Remaining data in this TCP connection is all file data. |
615 | | * Always desegment if the size is 0 (ie. unknown) |
616 | | */ |
617 | 0 | if (pinfo->can_desegment) { |
618 | 0 | if (size == 0 || tvb_captured_length(tvb) < size) { |
619 | 0 | pinfo->desegment_offset = 0; |
620 | 0 | pinfo->desegment_len = DESEGMENT_UNTIL_FIN; |
621 | 0 | return -1; |
622 | 0 | } |
623 | 0 | } |
624 | | |
625 | | /* OK. Now we have the whole file that was transferred. */ |
626 | 0 | transfer_info->resp_num = pinfo->num; |
627 | 0 | transfer_info->resp_ts = pinfo->abs_ts; |
628 | |
|
629 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, "LDSS File Transfer (Sending file - %s)", |
630 | 0 | transfer_info->broadcast->message_id == MESSAGE_ID_WILLSEND |
631 | 0 | ? "pull" |
632 | 0 | : "push"); |
633 | |
|
634 | 0 | ti = proto_tree_add_item(tree, proto_ldss, |
635 | 0 | tvb, 0, tvb_reported_length(tvb), ENC_NA); |
636 | 0 | ldss_tree = proto_item_add_subtree(ti, ett_ldss_transfer); |
637 | 0 | proto_tree_add_bytes_format(ldss_tree, hf_ldss_file_data, |
638 | 0 | tvb, 0, tvb_captured_length(tvb), NULL, |
639 | 0 | compression == COMPRESSION_GZIP |
640 | 0 | ? "Gzip compressed data: %d bytes" |
641 | 0 | : "File data: %d bytes", |
642 | 0 | tvb_captured_length(tvb)); |
643 | 0 | #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) |
644 | | /* Be nice and uncompress the file data. */ |
645 | 0 | if (compression == COMPRESSION_GZIP) { |
646 | 0 | tvbuff_t *uncomp_tvb; |
647 | 0 | uncomp_tvb = tvb_child_uncompress_zlib(tvb, tvb, 0, tvb_captured_length(tvb)); |
648 | 0 | if (uncomp_tvb != NULL) { |
649 | | /* XXX: Maybe not a good idea to add a data_source for |
650 | | what may very well be a large buffer since then |
651 | | the full uncompressed buffer will be shown in a tab |
652 | | in the hex bytes pane ? |
653 | | However, if we don't, bytes in an unrelated tab will |
654 | | be highlighted. |
655 | | */ |
656 | 0 | add_new_data_source(pinfo, uncomp_tvb, "Uncompressed Data"); |
657 | 0 | proto_tree_add_bytes_format_value(ldss_tree, hf_ldss_file_data, |
658 | 0 | uncomp_tvb, 0, tvb_captured_length(uncomp_tvb), |
659 | 0 | NULL, "Uncompressed data: %d bytes", |
660 | 0 | tvb_captured_length(uncomp_tvb)); |
661 | 0 | } |
662 | 0 | } |
663 | 0 | #endif |
664 | 0 | ti = proto_tree_add_uint(ldss_tree, hf_ldss_digest_type, |
665 | 0 | tvb, 0, 0, transfer_info->file->digest_type); |
666 | 0 | proto_item_set_generated(ti); |
667 | 0 | if (transfer_info->file->digest != NULL) { |
668 | | /* This is ugly. You can't add bytes of nonzero length and have |
669 | | * filtering work correctly unless you give a valid location in |
670 | | * the packet. This hack pretends the first 32 bytes of the packet |
671 | | * are the digest, which they aren't: they're actually the first 32 |
672 | | * bytes of the file that was sent. */ |
673 | 0 | ti = proto_tree_add_bytes(ldss_tree, hf_ldss_digest, |
674 | 0 | tvb, 0, DIGEST_LEN, transfer_info->file->digest); |
675 | 0 | } |
676 | 0 | proto_item_set_generated(ti); |
677 | 0 | ti = proto_tree_add_uint64(ldss_tree, hf_ldss_size, |
678 | 0 | tvb, 0, 0, size); |
679 | 0 | proto_item_set_generated(ti); |
680 | 0 | ti = proto_tree_add_uint64(ldss_tree, hf_ldss_offset, |
681 | 0 | tvb, 0, 0, offset); |
682 | 0 | proto_item_set_generated(ti); |
683 | 0 | ti = proto_tree_add_uint(ldss_tree, hf_ldss_compression, |
684 | 0 | tvb, 0, 0, compression); |
685 | 0 | proto_item_set_generated(ti); |
686 | | /* Link to the request for a pull. */ |
687 | 0 | if (transfer_info->broadcast->message_id == MESSAGE_ID_WILLSEND && |
688 | 0 | transfer_info->req != NULL && |
689 | 0 | transfer_info->req->num != 0) { |
690 | 0 | ti = proto_tree_add_uint(ldss_tree, hf_ldss_response_to, |
691 | 0 | tvb, 0, 0, transfer_info->req->num); |
692 | 0 | proto_item_set_generated(ti); |
693 | 0 | } |
694 | 0 | } |
695 | | |
696 | | /* Print the pull response time */ |
697 | 0 | if (transfer_info->broadcast->message_id == MESSAGE_ID_WILLSEND && |
698 | 0 | transfer_info->req != NULL && |
699 | 0 | transfer_info->resp_num != 0) { |
700 | 0 | nstime_t pull_response_time; |
701 | 0 | nstime_delta(&pull_response_time, &transfer_info->resp_ts, |
702 | 0 | &transfer_info->req->ts); |
703 | 0 | ti = proto_tree_add_time(ldss_tree, hf_ldss_transfer_response_time, |
704 | 0 | tvb, 0, 0, &pull_response_time); |
705 | 0 | proto_item_set_generated(ti); |
706 | 0 | } |
707 | | |
708 | | /* Link the transfer back to the initiating broadcast. Response time is |
709 | | * calculated as the time from broadcast to completed transfer. */ |
710 | 0 | ti = proto_tree_add_uint(ldss_tree, hf_ldss_initiated_by, |
711 | 0 | tvb, 0, 0, transfer_info->broadcast->num); |
712 | 0 | proto_item_set_generated(ti); |
713 | |
|
714 | 0 | if (transfer_info->resp_num != 0) { |
715 | 0 | nstime_delta(&broadcast_response_time, &transfer_info->resp_ts, |
716 | 0 | &transfer_info->broadcast->ts); |
717 | 0 | ti = proto_tree_add_time(ldss_tree, hf_ldss_transfer_completed_in, |
718 | 0 | tvb, 0, 0, &broadcast_response_time); |
719 | 0 | proto_item_set_generated(ti); |
720 | 0 | } |
721 | | |
722 | | /* This conv got its addr2/port2 set by the TCP dissector because a TCP |
723 | | * connection was established. Make a new one to handle future connections |
724 | | * to the addr/port mentioned in the broadcast, because that socket is |
725 | | * still open. */ |
726 | 0 | if (transfer_tcpinfo->seq == 1 && |
727 | 0 | transfer_tcpinfo->lastackseq == 1) { |
728 | |
|
729 | 0 | prepare_ldss_transfer_conv(transfer_info->broadcast); |
730 | 0 | } |
731 | |
|
732 | 0 | return tvb_captured_length(tvb); |
733 | 0 | } |
734 | | |
735 | | static bool |
736 | | is_broadcast(address* addr) |
737 | 19 | { |
738 | 19 | static const uint8_t broadcast_addr_bytes[6] = { |
739 | 19 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF |
740 | 19 | }; |
741 | 19 | static const address broadcast_addr = ADDRESS_INIT(AT_ETHER, 6, broadcast_addr_bytes); |
742 | | |
743 | 19 | return addresses_equal(addr, &broadcast_addr); |
744 | 19 | } |
745 | | |
746 | | static int |
747 | | dissect_ldss (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
748 | 19 | { |
749 | 19 | if (is_broadcast(&pinfo->dl_dst)) { |
750 | |
|
751 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "LDSS"); |
752 | 0 | return dissect_ldss_broadcast(tvb, pinfo, tree); |
753 | 0 | } |
754 | | |
755 | | /* Definitely not LDSS */ |
756 | 19 | return 0; |
757 | 19 | } |
758 | | |
759 | | void |
760 | 14 | proto_register_ldss (void) { |
761 | 14 | static hf_register_info hf[] = { |
762 | 14 | { &hf_ldss_message_id, |
763 | 14 | { "LDSS Message ID", |
764 | 14 | "ldss.message_id", |
765 | 14 | FT_UINT16, BASE_DEC, VALS(ldss_message_id_value), 0x0, |
766 | 14 | NULL, HFILL |
767 | 14 | } |
768 | 14 | }, |
769 | 14 | { &hf_ldss_message_detail, |
770 | 14 | { "Inferred meaning", |
771 | 14 | "ldss.inferred_meaning", |
772 | 14 | FT_UINT16, BASE_DEC, VALS(ldss_inferred_value), 0x0, |
773 | 14 | "Inferred meaning of the packet", HFILL |
774 | 14 | } |
775 | 14 | }, |
776 | 14 | { &hf_ldss_digest_type, |
777 | 14 | { "Digest Type", |
778 | 14 | "ldss.digest_type", |
779 | 14 | FT_UINT8, BASE_DEC, VALS(ldss_digest_type_value), 0x0, |
780 | 14 | NULL, HFILL |
781 | 14 | } |
782 | 14 | }, |
783 | 14 | { &hf_ldss_compression, |
784 | 14 | { "Compressed Format", |
785 | 14 | "ldss.compression", |
786 | 14 | FT_UINT8, BASE_DEC, VALS(ldss_compression_value), 0x0, |
787 | 14 | NULL, HFILL |
788 | 14 | } |
789 | 14 | }, |
790 | 14 | { &hf_ldss_cookie, |
791 | 14 | { "Cookie", |
792 | 14 | "ldss.cookie", |
793 | 14 | FT_UINT32, BASE_HEX, NULL, 0x0, |
794 | 14 | "Random value used for duplicate rejection", HFILL |
795 | 14 | } |
796 | 14 | }, |
797 | 14 | { &hf_ldss_digest, |
798 | 14 | { "Digest", |
799 | 14 | "ldss.digest", |
800 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
801 | 14 | "Digest of file padded with 0x00", HFILL |
802 | 14 | } |
803 | 14 | }, |
804 | 14 | { &hf_ldss_size, |
805 | 14 | { "Size", |
806 | 14 | "ldss.size", |
807 | 14 | FT_UINT64, BASE_DEC, NULL, 0x0, |
808 | 14 | "Size of complete file", HFILL |
809 | 14 | } |
810 | 14 | }, |
811 | 14 | { &hf_ldss_offset, |
812 | 14 | { "Offset", |
813 | 14 | "ldss.offset", |
814 | 14 | FT_UINT64, BASE_DEC, NULL, 0x0, |
815 | 14 | "Size of currently available portion of file", HFILL |
816 | 14 | } |
817 | 14 | }, |
818 | 14 | { &hf_ldss_target_time, |
819 | 14 | { "Target time (relative)", |
820 | 14 | "ldss.target_time", |
821 | 14 | FT_UINT32, BASE_DEC, NULL, 0x0, |
822 | 14 | "Time until file will be needed/available", HFILL |
823 | 14 | } |
824 | 14 | }, |
825 | 14 | { &hf_ldss_reserved_1, |
826 | 14 | { "Reserved", |
827 | 14 | "ldss.reserved_1", |
828 | 14 | FT_UINT32, BASE_HEX, NULL, 0x0, |
829 | 14 | "Unused field - should be 0x00000000", HFILL |
830 | 14 | } |
831 | 14 | }, |
832 | 14 | { &hf_ldss_port, |
833 | 14 | { "Port", |
834 | 14 | "ldss.port", |
835 | 14 | FT_UINT16, BASE_DEC, NULL, 0x0, |
836 | 14 | "TCP port for push (Need file) or pull (Will send)", HFILL |
837 | 14 | } |
838 | 14 | }, |
839 | 14 | { &hf_ldss_rate, |
840 | 14 | { "Rate (B/s)", |
841 | 14 | "ldss.rate", |
842 | 14 | FT_UINT16, BASE_DEC, NULL, 0x0, |
843 | 14 | "Estimated current download rate", HFILL |
844 | 14 | } |
845 | 14 | }, |
846 | 14 | { &hf_ldss_priority, |
847 | 14 | { "Priority", |
848 | 14 | "ldss.priority", |
849 | 14 | FT_UINT16, BASE_DEC, NULL, 0x0, |
850 | 14 | NULL, HFILL |
851 | 14 | } |
852 | 14 | }, |
853 | 14 | { &hf_ldss_property_count, |
854 | 14 | { "Property Count", |
855 | 14 | "ldss.property_count", |
856 | 14 | FT_UINT16, BASE_DEC, NULL, 0x0, |
857 | 14 | NULL, HFILL |
858 | 14 | } |
859 | 14 | }, |
860 | 14 | { &hf_ldss_properties, |
861 | 14 | { "Properties", |
862 | 14 | "ldss.properties", |
863 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
864 | 14 | NULL, HFILL |
865 | 14 | } |
866 | 14 | }, |
867 | 14 | { &hf_ldss_file_data, |
868 | 14 | { "File data", |
869 | 14 | "ldss.file_data", |
870 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
871 | 14 | NULL, HFILL |
872 | 14 | } |
873 | 14 | }, |
874 | 14 | { &hf_ldss_response_in, |
875 | 14 | { "Response In", |
876 | 14 | "ldss.response_in", |
877 | 14 | FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0, |
878 | 14 | "The response to this file pull request is in this frame", HFILL } |
879 | 14 | }, |
880 | 14 | { &hf_ldss_response_to, |
881 | 14 | { "Request In", |
882 | 14 | "ldss.response_to", |
883 | 14 | FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0, |
884 | 14 | "This is a response to the file pull request in this frame", HFILL } |
885 | 14 | }, |
886 | 14 | { &hf_ldss_initiated_by, |
887 | 14 | { "Initiated by", |
888 | 14 | "ldss.initiated_by", |
889 | 14 | FT_FRAMENUM, BASE_NONE, NULL, 0x0, |
890 | 14 | "The broadcast that initiated this file transfer", HFILL } |
891 | 14 | }, |
892 | 14 | { &hf_ldss_transfer_response_time, |
893 | 14 | { "Transfer response time", |
894 | 14 | "ldss.transfer_response_time", |
895 | 14 | FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, |
896 | 14 | "The time between the request and the response for a pull transfer", HFILL } |
897 | 14 | }, |
898 | 14 | { &hf_ldss_transfer_completed_in, |
899 | 14 | { "Transfer completed in", |
900 | 14 | "ldss.transfer_completed_in", |
901 | 14 | FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, |
902 | 14 | "The time between requesting the file and completion of the file transfer", HFILL } |
903 | 14 | } |
904 | 14 | }; |
905 | | |
906 | 14 | static int *ett[] = { &ett_ldss_broadcast, &ett_ldss_transfer, &ett_ldss_transfer_req }; |
907 | | |
908 | 14 | static ei_register_info ei[] = { |
909 | 14 | { &ei_ldss_unrecognized_line, { "ldss.unrecognized_line", PI_PROTOCOL, PI_WARN, "Unrecognized line ignored", EXPFILL }}, |
910 | 14 | }; |
911 | | |
912 | 14 | expert_module_t* expert_ldss; |
913 | | |
914 | 14 | proto_ldss = proto_register_protocol("Local Download Sharing Service", "LDSS", "ldss"); |
915 | 14 | proto_register_field_array(proto_ldss, hf, array_length(hf)); |
916 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
917 | 14 | expert_ldss = expert_register_protocol(proto_ldss); |
918 | 14 | expert_register_field_array(expert_ldss, ei, array_length(ei)); |
919 | | |
920 | 14 | ldss_udp_handle = register_dissector("ldss", dissect_ldss, proto_ldss); |
921 | 14 | ldss_tcp_handle = register_dissector("ldss_transfer", dissect_ldss_transfer, proto_ldss); |
922 | 14 | } |
923 | | |
924 | | |
925 | | /* The registration hand-off routine */ |
926 | | void |
927 | | proto_reg_handoff_ldss (void) |
928 | 14 | { |
929 | 14 | dissector_add_uint_with_preference("udp.port", UDP_PORT_LDSS, ldss_udp_handle); |
930 | 14 | } |
931 | | |
932 | | /* |
933 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
934 | | * |
935 | | * Local variables: |
936 | | * c-basic-offset: 8 |
937 | | * tab-width: 8 |
938 | | * indent-tabs-mode: t |
939 | | * End: |
940 | | * |
941 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
942 | | * :indentSize=8:tabSize=8:noTabs=false: |
943 | | */ |