2 * Routines for gnutella dissection
3 * Copyright 2001, B. Johannessen <bob@havoq.com>
5 * $Id: packet-gnutella.c,v 1.15 2002/08/28 21:00:13 jmayer Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@ethereal.com>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
37 #include <epan/packet.h>
38 #include "packet-gnutella.h"
40 static int proto_gnutella = -1;
42 static int hf_gnutella_stream = -1;
44 static int hf_gnutella_truncated = -1;
46 static int hf_gnutella_header = -1;
47 static int hf_gnutella_header_id = -1;
48 static int hf_gnutella_header_payload = -1;
49 static int hf_gnutella_header_ttl = -1;
50 static int hf_gnutella_header_hops = -1;
51 static int hf_gnutella_header_size = -1;
53 static int hf_gnutella_pong_payload = -1;
54 static int hf_gnutella_pong_port = -1;
55 static int hf_gnutella_pong_ip = -1;
56 static int hf_gnutella_pong_files = -1;
57 static int hf_gnutella_pong_kbytes = -1;
59 static int hf_gnutella_query_payload = -1;
60 static int hf_gnutella_query_min_speed = -1;
61 static int hf_gnutella_query_search = -1;
63 static int hf_gnutella_queryhit_payload = -1;
64 static int hf_gnutella_queryhit_count = -1;
65 static int hf_gnutella_queryhit_port = -1;
66 static int hf_gnutella_queryhit_ip = -1;
67 static int hf_gnutella_queryhit_speed = -1;
68 static int hf_gnutella_queryhit_extra = -1;
69 static int hf_gnutella_queryhit_servent_id = -1;
71 static int hf_gnutella_queryhit_hit = -1;
72 static int hf_gnutella_queryhit_hit_index = -1;
73 static int hf_gnutella_queryhit_hit_size = -1;
74 static int hf_gnutella_queryhit_hit_name = -1;
75 static int hf_gnutella_queryhit_hit_extra = -1;
77 static int hf_gnutella_push_payload = -1;
78 static int hf_gnutella_push_servent_id = -1;
79 static int hf_gnutella_push_index = -1;
80 static int hf_gnutella_push_ip = -1;
81 static int hf_gnutella_push_port = -1;
83 static gint ett_gnutella = -1;
85 static void dissect_gnutella_pong(tvbuff_t *tvb, guint offset, proto_tree *tree, guint size) {
87 if(offset + size > tvb_length(tvb)) {
88 proto_tree_add_item(tree,
89 hf_gnutella_truncated,
97 proto_tree_add_item(tree,
98 hf_gnutella_pong_port,
100 offset + GNUTELLA_PONG_PORT_OFFSET,
101 GNUTELLA_PORT_LENGTH,
104 proto_tree_add_item(tree,
107 offset + GNUTELLA_PONG_IP_OFFSET,
111 proto_tree_add_item(tree,
112 hf_gnutella_pong_files,
114 offset + GNUTELLA_PONG_FILES_OFFSET,
115 GNUTELLA_LONG_LENGTH,
118 proto_tree_add_item(tree,
119 hf_gnutella_pong_kbytes,
121 offset + GNUTELLA_PONG_KBYTES_OFFSET,
122 GNUTELLA_LONG_LENGTH,
127 static void dissect_gnutella_query(tvbuff_t *tvb, guint offset, proto_tree *tree, guint size) {
129 if(offset + size > tvb_length(tvb)) {
130 proto_tree_add_item(tree,
131 hf_gnutella_truncated,
139 proto_tree_add_item(tree,
140 hf_gnutella_query_min_speed,
142 offset + GNUTELLA_QUERY_SPEED_OFFSET,
143 GNUTELLA_SHORT_LENGTH,
146 if (size > GNUTELLA_SHORT_LENGTH) {
147 proto_tree_add_item(tree,
148 hf_gnutella_query_search,
150 offset + GNUTELLA_QUERY_SEARCH_OFFSET,
151 size - GNUTELLA_SHORT_LENGTH,
155 proto_tree_add_text(tree,
157 offset + GNUTELLA_QUERY_SEARCH_OFFSET,
159 "Missing data for Query Search.");
163 static void dissect_gnutella_queryhit(tvbuff_t *tvb, guint offset, proto_tree *tree, guint size) {
165 proto_tree *qhi, *hit_tree;
168 int name_length, extra_length;
169 int idx_at_offset, size_at_offset;
170 int servent_id_at_offset;
171 int name_at_offset, extra_at_offset;
172 int cur_char, remaining, used;
174 if(offset + size > tvb_length(tvb)) {
175 proto_tree_add_item(tree,
176 hf_gnutella_truncated,
184 hit_count = tvb_get_guint8(tvb, offset + GNUTELLA_QUERYHIT_COUNT_OFFSET);
186 proto_tree_add_uint(tree,
187 hf_gnutella_queryhit_count,
189 offset + GNUTELLA_QUERYHIT_COUNT_OFFSET,
190 GNUTELLA_BYTE_LENGTH,
193 proto_tree_add_item(tree,
194 hf_gnutella_queryhit_port,
196 offset + GNUTELLA_QUERYHIT_PORT_OFFSET,
197 GNUTELLA_PORT_LENGTH,
200 proto_tree_add_item(tree,
201 hf_gnutella_queryhit_ip,
203 offset + GNUTELLA_QUERYHIT_IP_OFFSET,
207 proto_tree_add_item(tree,
208 hf_gnutella_queryhit_speed,
210 offset + GNUTELLA_QUERYHIT_SPEED_OFFSET,
211 GNUTELLA_LONG_LENGTH,
214 hit_offset = offset + GNUTELLA_QUERYHIT_FIRST_HIT_OFFSET;
216 for(i = 0; i < hit_count; i++) {
217 idx_at_offset = hit_offset;
218 size_at_offset = hit_offset + GNUTELLA_QUERYHIT_HIT_SIZE_OFFSET;
220 hit_offset += (GNUTELLA_LONG_LENGTH * 2);
225 name_at_offset = hit_offset;
227 while(hit_offset - offset < size) {
228 cur_char = tvb_get_guint8(tvb, hit_offset);
238 extra_at_offset = hit_offset;
240 while(hit_offset - offset < size) {
241 cur_char = tvb_get_guint8(tvb, hit_offset);
251 qhi = proto_tree_add_item(tree,
252 hf_gnutella_queryhit_hit,
255 (GNUTELLA_LONG_LENGTH * 2) +
256 name_length + extra_length +
257 GNUTELLA_QUERYHIT_END_OF_STRING_LENGTH,
260 hit_tree = proto_item_add_subtree(qhi, ett_gnutella);
262 proto_tree_add_item(hit_tree,
263 hf_gnutella_queryhit_hit_index,
266 GNUTELLA_LONG_LENGTH,
269 proto_tree_add_item(hit_tree,
270 hf_gnutella_queryhit_hit_size,
273 GNUTELLA_LONG_LENGTH,
276 proto_tree_add_item(hit_tree,
277 hf_gnutella_queryhit_hit_name,
284 proto_tree_add_item(hit_tree,
285 hf_gnutella_queryhit_hit_extra,
293 used = hit_offset - offset;
294 remaining = size - used;
296 if(remaining > GNUTELLA_SERVENT_ID_LENGTH) {
297 servent_id_at_offset = hit_offset + remaining - GNUTELLA_SERVENT_ID_LENGTH;
299 proto_tree_add_item(tree,
300 hf_gnutella_queryhit_extra,
303 servent_id_at_offset - hit_offset,
307 servent_id_at_offset = hit_offset;
310 proto_tree_add_item(tree,
311 hf_gnutella_queryhit_servent_id,
313 servent_id_at_offset,
314 GNUTELLA_SERVENT_ID_LENGTH,
319 static void dissect_gnutella_push(tvbuff_t *tvb, guint offset, proto_tree *tree, guint size) {
321 if(offset + size > tvb_length(tvb)) {
322 proto_tree_add_item(tree,
323 hf_gnutella_truncated,
331 proto_tree_add_item(tree,
332 hf_gnutella_push_servent_id,
334 offset + GNUTELLA_PUSH_SERVENT_ID_OFFSET,
335 GNUTELLA_SERVENT_ID_LENGTH,
338 proto_tree_add_item(tree,
339 hf_gnutella_push_index,
341 offset + GNUTELLA_PUSH_INDEX_OFFSET,
342 GNUTELLA_LONG_LENGTH,
345 proto_tree_add_item(tree,
348 offset + GNUTELLA_PUSH_IP_OFFSET,
352 proto_tree_add_item(tree,
353 hf_gnutella_push_port,
355 offset + GNUTELLA_PUSH_PORT_OFFSET,
356 GNUTELLA_PORT_LENGTH,
361 static void dissect_gnutella(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
363 proto_item *ti, *hi, *pi;
364 proto_tree *gnutella_tree, *gnutella_header_tree, *gnutella_pong_tree;
365 proto_tree *gnutella_queryhit_tree, *gnutella_push_tree;
366 proto_tree *gnutella_query_tree;
367 int snap_len, payload_descriptor, offset;
369 char *payload_descriptor_text;
371 if (check_col(pinfo->cinfo, COL_PROTOCOL))
372 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Gnutella");
374 if (check_col(pinfo->cinfo, COL_INFO))
375 col_set_str(pinfo->cinfo, COL_INFO, "Gnutella");
377 snap_len = tvb_length(tvb);
379 if(snap_len < GNUTELLA_HEADER_LENGTH) {
380 if (check_col(pinfo->cinfo, COL_INFO))
381 col_append_fstr(pinfo->cinfo, COL_INFO,
382 ", %i bytes [INCOMPLETE]", snap_len);
386 if (check_col(pinfo->cinfo, COL_INFO))
387 col_append_fstr(pinfo->cinfo, COL_INFO,
388 ", %i bytes", snap_len);
392 ti = proto_tree_add_item(tree,
398 gnutella_tree = proto_item_add_subtree(ti, ett_gnutella);
402 size = tvb_get_letohl(
404 offset + GNUTELLA_HEADER_SIZE_OFFSET);
405 if(size > GNUTELLA_MAX_SNAP_SIZE) {
406 proto_tree_add_item(gnutella_tree,
415 while(snap_len - offset >= GNUTELLA_HEADER_LENGTH) {
416 payload_descriptor = tvb_get_guint8(
419 GNUTELLA_HEADER_PAYLOAD_OFFSET);
420 size = tvb_get_letohl(
422 offset + GNUTELLA_HEADER_SIZE_OFFSET);
424 switch(payload_descriptor) {
426 payload_descriptor_text = GNUTELLA_PING_NAME;
429 payload_descriptor_text = GNUTELLA_PONG_NAME;
432 payload_descriptor_text = GNUTELLA_PUSH_NAME;
435 payload_descriptor_text = GNUTELLA_QUERY_NAME;
437 case GNUTELLA_QUERYHIT:
438 payload_descriptor_text = GNUTELLA_QUERYHIT_NAME;
441 payload_descriptor_text = GNUTELLA_UNKNOWN_NAME;
445 hi = proto_tree_add_item(gnutella_tree,
449 GNUTELLA_HEADER_LENGTH,
451 gnutella_header_tree = proto_item_add_subtree(hi, ett_gnutella);
453 proto_tree_add_item(gnutella_header_tree,
454 hf_gnutella_header_id,
456 offset + GNUTELLA_HEADER_ID_OFFSET,
457 GNUTELLA_SERVENT_ID_LENGTH,
460 proto_tree_add_uint_format(gnutella_header_tree,
461 hf_gnutella_header_payload,
463 offset + GNUTELLA_HEADER_PAYLOAD_OFFSET,
464 GNUTELLA_BYTE_LENGTH,
468 payload_descriptor_text);
470 proto_tree_add_item(gnutella_header_tree,
471 hf_gnutella_header_ttl,
473 offset + GNUTELLA_HEADER_TTL_OFFSET,
474 GNUTELLA_BYTE_LENGTH,
477 proto_tree_add_item(gnutella_header_tree,
478 hf_gnutella_header_hops,
480 offset + GNUTELLA_HEADER_HOPS_OFFSET,
481 GNUTELLA_BYTE_LENGTH,
484 proto_tree_add_uint(gnutella_header_tree,
485 hf_gnutella_header_size,
487 offset + GNUTELLA_HEADER_SIZE_OFFSET,
488 GNUTELLA_LONG_LENGTH,
493 switch(payload_descriptor) {
495 pi = proto_tree_add_item(
496 gnutella_header_tree,
497 hf_gnutella_pong_payload,
499 offset + GNUTELLA_HEADER_LENGTH,
502 gnutella_pong_tree = proto_item_add_subtree(
505 dissect_gnutella_pong(
507 offset + GNUTELLA_HEADER_LENGTH,
512 pi = proto_tree_add_item(
513 gnutella_header_tree,
514 hf_gnutella_push_payload,
516 offset + GNUTELLA_HEADER_LENGTH,
519 gnutella_push_tree = proto_item_add_subtree(
522 dissect_gnutella_push(
524 offset + GNUTELLA_HEADER_LENGTH,
529 pi = proto_tree_add_item(
530 gnutella_header_tree,
531 hf_gnutella_query_payload,
533 offset + GNUTELLA_HEADER_LENGTH,
536 gnutella_query_tree = proto_item_add_subtree(
539 dissect_gnutella_query(
541 offset + GNUTELLA_HEADER_LENGTH,
545 case GNUTELLA_QUERYHIT:
546 pi = proto_tree_add_item(
547 gnutella_header_tree,
548 hf_gnutella_queryhit_payload,
550 offset + GNUTELLA_HEADER_LENGTH,
553 gnutella_queryhit_tree = proto_item_add_subtree(
556 dissect_gnutella_queryhit(
558 offset + GNUTELLA_HEADER_LENGTH,
559 gnutella_queryhit_tree,
565 offset = offset + GNUTELLA_HEADER_LENGTH + size;
572 void proto_register_gnutella(void) {
574 static hf_register_info hf[] = {
575 { &hf_gnutella_header,
576 { "Descriptor Header", "gnutella.header",
577 FT_NONE, BASE_NONE, NULL, 0,
578 "Gnutella Descriptor Header", HFILL }
580 { &hf_gnutella_pong_payload,
581 { "Pong", "gnutella.pong.payload",
582 FT_NONE, BASE_NONE, NULL, 0,
583 "Gnutella Pong Payload", HFILL }
585 { &hf_gnutella_push_payload,
586 { "Push", "gnutella.push.payload",
587 FT_NONE, BASE_NONE, NULL, 0,
588 "Gnutella Push Payload", HFILL }
590 { &hf_gnutella_query_payload,
591 { "Query", "gnutella.query.payload",
592 FT_NONE, BASE_NONE, NULL, 0,
593 "Gnutella Query Payload", HFILL }
595 { &hf_gnutella_queryhit_payload,
596 { "QueryHit", "gnutella.queryhit.payload",
597 FT_NONE, BASE_NONE, NULL, 0,
598 "Gnutella QueryHit Payload", HFILL }
600 { &hf_gnutella_truncated,
601 { "Truncated Frame", "gnutella.truncated",
602 FT_NONE, BASE_NONE, NULL, 0,
603 "The Gnutella Frame Was Truncated", HFILL }
605 { &hf_gnutella_stream,
606 { "Gnutella Upload / Download Stream", "gnutella.stream",
607 FT_NONE, BASE_NONE, NULL, 0,
608 "Gnutella Upload / Download Stream", HFILL }
610 { &hf_gnutella_header_id,
611 { "ID", "gnutella.header.id",
612 FT_BYTES, BASE_HEX, NULL, 0,
613 "Gnutella Descriptor ID", HFILL }
615 { &hf_gnutella_header_payload,
616 { "Payload", "gnutella.header.payload",
617 FT_UINT8, BASE_DEC, NULL, 0,
618 "Gnutella Descriptor Payload", HFILL }
620 { &hf_gnutella_header_ttl,
621 { "TTL", "gnutella.header.ttl",
622 FT_UINT8, BASE_DEC, NULL, 0,
623 "Gnutella Descriptor Time To Live", HFILL }
625 { &hf_gnutella_header_hops,
626 { "Hops", "gnutella.header.hops",
627 FT_UINT8, BASE_DEC, NULL, 0,
628 "Gnutella Descriptor Hop Count", HFILL }
630 { &hf_gnutella_header_size,
631 { "Length", "gnutella.header.size",
632 FT_UINT8, BASE_DEC, NULL, 0,
633 "Gnutella Descriptor Payload Length", HFILL }
635 { &hf_gnutella_pong_port,
636 { "Port", "gnutella.pong.port",
637 FT_UINT16, BASE_DEC, NULL, 0,
638 "Gnutella Pong TCP Port", HFILL }
640 { &hf_gnutella_pong_ip,
641 { "IP", "gnutella.pong.ip",
642 FT_IPv4, BASE_DEC, NULL, 0,
643 "Gnutella Pong IP Address", HFILL }
645 { &hf_gnutella_pong_files,
646 { "Files Shared", "gnutella.pong.files",
647 FT_UINT32, BASE_DEC, NULL, 0,
648 "Gnutella Pong Files Shared", HFILL }
650 { &hf_gnutella_pong_kbytes,
651 { "KBytes Shared", "gnutella.pong.kbytes",
652 FT_UINT32, BASE_DEC, NULL, 0,
653 "Gnutella Pong KBytes Shared", HFILL }
655 { &hf_gnutella_query_min_speed,
656 { "Min Speed", "gnutella.query.min_speed",
657 FT_UINT32, BASE_DEC, NULL, 0,
658 "Gnutella Query Minimum Speed", HFILL }
660 { &hf_gnutella_query_search,
661 { "Search", "gnutella.query.search",
662 FT_STRINGZ, BASE_NONE, NULL, 0,
663 "Gnutella Query Search", HFILL }
665 { &hf_gnutella_queryhit_hit,
666 { "Hit", "gnutella.queryhit.hit",
667 FT_NONE, BASE_NONE, NULL, 0,
668 "Gnutella QueryHit", HFILL }
670 { &hf_gnutella_queryhit_hit_index,
671 { "Index", "gnutella.queryhit.hit.index",
672 FT_UINT32, BASE_DEC, NULL, 0,
673 "Gnutella QueryHit Index", HFILL }
675 { &hf_gnutella_queryhit_hit_size,
676 { "Size", "gnutella.queryhit.hit.size",
677 FT_UINT32, BASE_DEC, NULL, 0,
678 "Gnutella QueryHit Size", HFILL }
680 { &hf_gnutella_queryhit_hit_name,
681 { "Name", "gnutella.queryhit.hit.name",
682 FT_STRING, BASE_NONE, NULL, 0,
683 "Gnutella Query Name", HFILL }
685 { &hf_gnutella_queryhit_hit_extra,
686 { "Extra", "gnutella.queryhit.hit.extra",
687 FT_BYTES, BASE_HEX, NULL, 0,
688 "Gnutella Query Extra", HFILL }
690 { &hf_gnutella_queryhit_count,
691 { "Count", "gnutella.queryhit.count",
692 FT_UINT8, BASE_DEC, NULL, 0,
693 "Gnutella QueryHit Count", HFILL }
695 { &hf_gnutella_queryhit_port,
696 { "Port", "gnutella.queryhit.port",
697 FT_UINT16, BASE_DEC, NULL, 0,
698 "Gnutella QueryHit Port", HFILL }
700 { &hf_gnutella_queryhit_ip,
701 { "IP", "gnutella.queryhit.ip",
702 FT_IPv4, BASE_DEC, NULL, 0,
703 "Gnutella QueryHit IP Address", HFILL }
705 { &hf_gnutella_queryhit_speed,
706 { "Speed", "gnutella.queryhit.speed",
707 FT_UINT32, BASE_DEC, NULL, 0,
708 "Gnutella QueryHit Speed", HFILL }
710 { &hf_gnutella_queryhit_extra,
711 { "Extra", "gnutella.queryhit.extra",
712 FT_BYTES, BASE_HEX, NULL, 0,
713 "Gnutella QueryHit Extra", HFILL }
715 { &hf_gnutella_queryhit_servent_id,
716 { "Servent ID", "gnutella.queryhit.servent_id",
717 FT_BYTES, BASE_HEX, NULL, 0,
718 "Gnutella QueryHit Servent ID", HFILL }
720 { &hf_gnutella_push_servent_id,
721 { "Servent ID", "gnutella.push.servent_id",
722 FT_BYTES, BASE_HEX, NULL, 0,
723 "Gnutella Push Servent ID", HFILL }
725 { &hf_gnutella_push_ip,
726 { "IP", "gnutella.push.ip",
727 FT_IPv4, BASE_DEC, NULL, 0,
728 "Gnutella Push IP Address", HFILL }
730 { &hf_gnutella_push_index,
731 { "Index", "gnutella.push.index",
732 FT_UINT32, BASE_DEC, NULL, 0,
733 "Gnutella Push Index", HFILL }
735 { &hf_gnutella_push_port,
736 { "Port", "gnutella.push.port",
737 FT_UINT16, BASE_DEC, NULL, 0,
738 "Gnutella Push Port", HFILL }
742 static gint *ett[] = {
746 proto_gnutella = proto_register_protocol("Gnutella Protocol",
750 proto_register_field_array(proto_gnutella, hf, array_length(hf));
752 proto_register_subtree_array(ett, array_length(ett));
755 void proto_reg_handoff_gnutella(void) {
756 dissector_handle_t gnutella_handle;
758 gnutella_handle = create_dissector_handle(dissect_gnutella,
760 dissector_add("tcp.port", GNUTELLA_TCP_PORT, gnutella_handle);