Coverage Report

Created: 2025-02-15 06:25

/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
 */