2 * Routines for gnutella dissection
3 * Copyright 2001, B. Johannessen <bob@havoq.com>
5 * $Id: packet-gnutella.c,v 1.7 2001/10/26 18:28:16 gram 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.
34 #ifdef HAVE_SYS_TYPES_H
35 #include <sys/types.h>
38 #ifdef HAVE_NETINET_IN_H
39 #include <netinet/in.h>
44 #ifdef NEED_SNPRINTF_H
50 #include "packet-gnutella.h"
52 static int proto_gnutella = -1;
54 static int hf_gnutella_stream = -1;
56 static int hf_gnutella_truncated = -1;
58 static int hf_gnutella_header = -1;
59 static int hf_gnutella_header_id = -1;
60 static int hf_gnutella_header_payload = -1;
61 static int hf_gnutella_header_ttl = -1;
62 static int hf_gnutella_header_hops = -1;
63 static int hf_gnutella_header_size = -1;
65 static int hf_gnutella_pong_payload = -1;
66 static int hf_gnutella_pong_port = -1;
67 static int hf_gnutella_pong_ip = -1;
68 static int hf_gnutella_pong_files = -1;
69 static int hf_gnutella_pong_kbytes = -1;
71 static int hf_gnutella_query_payload = -1;
72 static int hf_gnutella_query_min_speed = -1;
73 static int hf_gnutella_query_search = -1;
75 static int hf_gnutella_queryhit_payload = -1;
76 static int hf_gnutella_queryhit_count = -1;
77 static int hf_gnutella_queryhit_port = -1;
78 static int hf_gnutella_queryhit_ip = -1;
79 static int hf_gnutella_queryhit_speed = -1;
80 static int hf_gnutella_queryhit_extra = -1;
81 static int hf_gnutella_queryhit_servent_id = -1;
83 static int hf_gnutella_queryhit_hit = -1;
84 static int hf_gnutella_queryhit_hit_index = -1;
85 static int hf_gnutella_queryhit_hit_size = -1;
86 static int hf_gnutella_queryhit_hit_name = -1;
87 static int hf_gnutella_queryhit_hit_extra = -1;
89 static int hf_gnutella_push_payload = -1;
90 static int hf_gnutella_push_servent_id = -1;
91 static int hf_gnutella_push_index = -1;
92 static int hf_gnutella_push_ip = -1;
93 static int hf_gnutella_push_port = -1;
95 static gint ett_gnutella = -1;
97 static void dissect_gnutella_pong(tvbuff_t *tvb, guint offset, proto_tree *tree, guint size) {
99 if(offset + size > tvb_length(tvb)) {
100 proto_tree_add_item(tree,
101 hf_gnutella_truncated,
109 proto_tree_add_item(tree,
110 hf_gnutella_pong_port,
112 offset + GNUTELLA_PONG_PORT_OFFSET,
113 GNUTELLA_PORT_LENGTH,
116 proto_tree_add_item(tree,
119 offset + GNUTELLA_PONG_IP_OFFSET,
123 proto_tree_add_item(tree,
124 hf_gnutella_pong_files,
126 offset + GNUTELLA_PONG_FILES_OFFSET,
127 GNUTELLA_LONG_LENGTH,
130 proto_tree_add_item(tree,
131 hf_gnutella_pong_kbytes,
133 offset + GNUTELLA_PONG_KBYTES_OFFSET,
134 GNUTELLA_LONG_LENGTH,
139 static void dissect_gnutella_query(tvbuff_t *tvb, guint offset, proto_tree *tree, guint size) {
141 if(offset + size > tvb_length(tvb)) {
142 proto_tree_add_item(tree,
143 hf_gnutella_truncated,
151 proto_tree_add_item(tree,
152 hf_gnutella_query_min_speed,
154 offset + GNUTELLA_QUERY_SPEED_OFFSET,
155 GNUTELLA_SHORT_LENGTH,
158 if (size > GNUTELLA_SHORT_LENGTH) {
159 proto_tree_add_item(tree,
160 hf_gnutella_query_search,
162 offset + GNUTELLA_QUERY_SEARCH_OFFSET,
163 size - GNUTELLA_SHORT_LENGTH,
167 proto_tree_add_text(tree,
169 offset + GNUTELLA_QUERY_SEARCH_OFFSET,
171 "Missing data for Query Search.");
175 static void dissect_gnutella_queryhit(tvbuff_t *tvb, guint offset, proto_tree *tree, guint size) {
177 proto_tree *qhi, *hit_tree;
179 int hit_offset, idx, hit_size;
180 int name_length, extra_length;
181 int idx_at_offset, size_at_offset;
182 int servent_id_at_offset;
183 int name_at_offset, extra_at_offset;
184 int cur_char, remaining, used;
187 if(offset + size > tvb_length(tvb)) {
188 proto_tree_add_item(tree,
189 hf_gnutella_truncated,
197 hit_count = tvb_get_guint8(tvb, offset + GNUTELLA_QUERYHIT_COUNT_OFFSET);
199 proto_tree_add_uint(tree,
200 hf_gnutella_queryhit_count,
202 offset + GNUTELLA_QUERYHIT_COUNT_OFFSET,
203 GNUTELLA_BYTE_LENGTH,
206 proto_tree_add_item(tree,
207 hf_gnutella_queryhit_port,
209 offset + GNUTELLA_QUERYHIT_PORT_OFFSET,
210 GNUTELLA_PORT_LENGTH,
213 proto_tree_add_item(tree,
214 hf_gnutella_queryhit_ip,
216 offset + GNUTELLA_QUERYHIT_IP_OFFSET,
220 proto_tree_add_item(tree,
221 hf_gnutella_queryhit_speed,
223 offset + GNUTELLA_QUERYHIT_SPEED_OFFSET,
224 GNUTELLA_LONG_LENGTH,
227 hit_offset = offset + GNUTELLA_QUERYHIT_FIRST_HIT_OFFSET;
229 for(i = 0; i < hit_count; i++) {
230 idx_at_offset = hit_offset;
231 size_at_offset = hit_offset + GNUTELLA_QUERYHIT_HIT_SIZE_OFFSET;
233 hit_offset += (GNUTELLA_LONG_LENGTH * 2);
238 name_at_offset = hit_offset;
240 while(hit_offset - offset < size) {
241 cur_char = tvb_get_guint8(tvb, hit_offset);
251 extra_at_offset = hit_offset;
253 while(hit_offset - offset < size) {
254 cur_char = tvb_get_guint8(tvb, hit_offset);
264 qhi = proto_tree_add_item(tree,
265 hf_gnutella_queryhit_hit,
268 (GNUTELLA_LONG_LENGTH * 2) +
269 name_length + extra_length +
270 GNUTELLA_QUERYHIT_END_OF_STRING_LENGTH,
273 hit_tree = proto_item_add_subtree(qhi, ett_gnutella);
275 proto_tree_add_item(hit_tree,
276 hf_gnutella_queryhit_hit_index,
279 GNUTELLA_LONG_LENGTH,
282 proto_tree_add_item(hit_tree,
283 hf_gnutella_queryhit_hit_size,
286 GNUTELLA_LONG_LENGTH,
289 proto_tree_add_item(hit_tree,
290 hf_gnutella_queryhit_hit_name,
297 proto_tree_add_item(hit_tree,
298 hf_gnutella_queryhit_hit_extra,
306 used = hit_offset - offset;
307 remaining = size - used;
309 if(remaining > GNUTELLA_SERVENT_ID_LENGTH) {
310 servent_id_at_offset = hit_offset + remaining - GNUTELLA_SERVENT_ID_LENGTH;
312 proto_tree_add_item(tree,
313 hf_gnutella_queryhit_extra,
316 servent_id_at_offset - hit_offset,
320 servent_id_at_offset = hit_offset;
323 proto_tree_add_item(tree,
324 hf_gnutella_queryhit_servent_id,
326 servent_id_at_offset,
327 GNUTELLA_SERVENT_ID_LENGTH,
332 static void dissect_gnutella_push(tvbuff_t *tvb, guint offset, proto_tree *tree, guint size) {
334 if(offset + size > tvb_length(tvb)) {
335 proto_tree_add_item(tree,
336 hf_gnutella_truncated,
344 proto_tree_add_item(tree,
345 hf_gnutella_push_servent_id,
347 offset + GNUTELLA_PUSH_SERVENT_ID_OFFSET,
348 GNUTELLA_SERVENT_ID_LENGTH,
351 proto_tree_add_item(tree,
352 hf_gnutella_push_index,
354 offset + GNUTELLA_PUSH_INDEX_OFFSET,
355 GNUTELLA_LONG_LENGTH,
358 proto_tree_add_item(tree,
361 offset + GNUTELLA_PUSH_IP_OFFSET,
365 proto_tree_add_item(tree,
366 hf_gnutella_push_port,
368 offset + GNUTELLA_PUSH_PORT_OFFSET,
369 GNUTELLA_PORT_LENGTH,
374 static void dissect_gnutella(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
376 proto_item *ti, *hi, *pi;
377 proto_tree *gnutella_tree, *gnutella_header_tree, *gnutella_pong_tree;
378 proto_tree *gnutella_queryhit_tree, *gnutella_push_tree;
379 proto_tree *gnutella_query_tree;
380 int snap_len, payload_descriptor, offset;
382 char *payload_descriptor_text;
384 if (check_col(pinfo->fd, COL_PROTOCOL))
385 col_set_str(pinfo->fd, COL_PROTOCOL, "Gnutella");
387 if (check_col(pinfo->fd, COL_INFO))
388 col_set_str(pinfo->fd, COL_INFO, "Gnutella");
390 snap_len = tvb_length(tvb);
392 if(snap_len < GNUTELLA_HEADER_LENGTH) {
393 if (check_col(pinfo->fd, COL_INFO))
394 col_append_fstr(pinfo->fd, COL_INFO,
395 ", %i bytes [INCOMPLETE]", snap_len);
399 if (check_col(pinfo->fd, COL_INFO))
400 col_append_fstr(pinfo->fd, COL_INFO,
401 ", %i bytes", snap_len);
405 ti = proto_tree_add_item(tree,
411 gnutella_tree = proto_item_add_subtree(ti, ett_gnutella);
415 size = tvb_get_letohl(
417 offset + GNUTELLA_HEADER_SIZE_OFFSET);
418 if(size > GNUTELLA_MAX_SNAP_SIZE) {
419 proto_tree_add_item(gnutella_tree,
428 while(snap_len - offset >= GNUTELLA_HEADER_LENGTH) {
429 payload_descriptor = tvb_get_guint8(
432 GNUTELLA_HEADER_PAYLOAD_OFFSET);
433 size = tvb_get_letohl(
435 offset + GNUTELLA_HEADER_SIZE_OFFSET);
437 switch(payload_descriptor) {
439 payload_descriptor_text = GNUTELLA_PING_NAME;
442 payload_descriptor_text = GNUTELLA_PONG_NAME;
445 payload_descriptor_text = GNUTELLA_PUSH_NAME;
448 payload_descriptor_text = GNUTELLA_QUERY_NAME;
450 case GNUTELLA_QUERYHIT:
451 payload_descriptor_text = GNUTELLA_QUERYHIT_NAME;
454 payload_descriptor_text = GNUTELLA_UNKNOWN_NAME;
458 hi = proto_tree_add_item(gnutella_tree,
462 GNUTELLA_HEADER_LENGTH,
464 gnutella_header_tree = proto_item_add_subtree(hi, ett_gnutella);
466 proto_tree_add_item(gnutella_header_tree,
467 hf_gnutella_header_id,
469 offset + GNUTELLA_HEADER_ID_OFFSET,
470 GNUTELLA_SERVENT_ID_LENGTH,
473 proto_tree_add_uint_format(gnutella_header_tree,
474 hf_gnutella_header_payload,
476 offset + GNUTELLA_HEADER_PAYLOAD_OFFSET,
477 GNUTELLA_BYTE_LENGTH,
481 payload_descriptor_text);
483 proto_tree_add_item(gnutella_header_tree,
484 hf_gnutella_header_ttl,
486 offset + GNUTELLA_HEADER_TTL_OFFSET,
487 GNUTELLA_BYTE_LENGTH,
490 proto_tree_add_item(gnutella_header_tree,
491 hf_gnutella_header_hops,
493 offset + GNUTELLA_HEADER_HOPS_OFFSET,
494 GNUTELLA_BYTE_LENGTH,
497 proto_tree_add_uint(gnutella_header_tree,
498 hf_gnutella_header_size,
500 offset + GNUTELLA_HEADER_SIZE_OFFSET,
501 GNUTELLA_LONG_LENGTH,
506 switch(payload_descriptor) {
508 pi = proto_tree_add_item(
509 gnutella_header_tree,
510 hf_gnutella_pong_payload,
512 offset + GNUTELLA_HEADER_LENGTH,
515 gnutella_pong_tree = proto_item_add_subtree(
518 dissect_gnutella_pong(
520 offset + GNUTELLA_HEADER_LENGTH,
525 pi = proto_tree_add_item(
526 gnutella_header_tree,
527 hf_gnutella_push_payload,
529 offset + GNUTELLA_HEADER_LENGTH,
532 gnutella_push_tree = proto_item_add_subtree(
535 dissect_gnutella_push(
537 offset + GNUTELLA_HEADER_LENGTH,
542 pi = proto_tree_add_item(
543 gnutella_header_tree,
544 hf_gnutella_query_payload,
546 offset + GNUTELLA_HEADER_LENGTH,
549 gnutella_query_tree = proto_item_add_subtree(
552 dissect_gnutella_query(
554 offset + GNUTELLA_HEADER_LENGTH,
558 case GNUTELLA_QUERYHIT:
559 pi = proto_tree_add_item(
560 gnutella_header_tree,
561 hf_gnutella_queryhit_payload,
563 offset + GNUTELLA_HEADER_LENGTH,
566 gnutella_queryhit_tree = proto_item_add_subtree(
569 dissect_gnutella_queryhit(
571 offset + GNUTELLA_HEADER_LENGTH,
572 gnutella_queryhit_tree,
578 offset = offset + GNUTELLA_HEADER_LENGTH + size;
585 void proto_register_gnutella(void) {
587 static hf_register_info hf[] = {
588 { &hf_gnutella_header,
589 { "Descriptor Header", "gnutella.header",
590 FT_NONE, BASE_NONE, NULL, 0,
591 "Gnutella Descriptor Header", HFILL }
593 { &hf_gnutella_pong_payload,
594 { "Pong", "gnutella.pong.payload",
595 FT_NONE, BASE_NONE, NULL, 0,
596 "Gnutella Pong Payload", HFILL }
598 { &hf_gnutella_push_payload,
599 { "Push", "gnutella.push.payload",
600 FT_NONE, BASE_NONE, NULL, 0,
601 "Gnutella Push Payload", HFILL }
603 { &hf_gnutella_query_payload,
604 { "Query", "gnutella.query.payload",
605 FT_NONE, BASE_NONE, NULL, 0,
606 "Gnutella Query Payload", HFILL }
608 { &hf_gnutella_queryhit_payload,
609 { "QueryHit", "gnutella.queryhit.payload",
610 FT_NONE, BASE_NONE, NULL, 0,
611 "Gnutella QueryHit Payload", HFILL }
613 { &hf_gnutella_truncated,
614 { "Truncated Frame", "gnutella.truncated",
615 FT_NONE, BASE_NONE, NULL, 0,
616 "The Gnutella Frame Was Truncated", HFILL }
618 { &hf_gnutella_stream,
619 { "Gnutella Upload / Download Stream", "gnutella.stream",
620 FT_NONE, BASE_NONE, NULL, 0,
621 "Gnutella Upload / Download Stream", HFILL }
623 { &hf_gnutella_header_id,
624 { "ID", "gnutella.header.id",
625 FT_BYTES, BASE_HEX, NULL, 0,
626 "Gnutella Descriptor ID", HFILL }
628 { &hf_gnutella_header_payload,
629 { "Payload", "gnutella.header.payload",
630 FT_UINT8, BASE_DEC, NULL, 0,
631 "Gnutella Descriptor Payload", HFILL }
633 { &hf_gnutella_header_ttl,
634 { "TTL", "gnutella.header.ttl",
635 FT_UINT8, BASE_DEC, NULL, 0,
636 "Gnutella Descriptor Time To Live", HFILL }
638 { &hf_gnutella_header_hops,
639 { "Hops", "gnutella.header.hops",
640 FT_UINT8, BASE_DEC, NULL, 0,
641 "Gnutella Descriptor Hop Count", HFILL }
643 { &hf_gnutella_header_size,
644 { "Length", "gnutella.header.size",
645 FT_UINT8, BASE_DEC, NULL, 0,
646 "Gnutella Descriptor Payload Length", HFILL }
648 { &hf_gnutella_pong_port,
649 { "Port", "gnutella.pong.port",
650 FT_UINT16, BASE_DEC, NULL, 0,
651 "Gnutella Pong TCP Port", HFILL }
653 { &hf_gnutella_pong_ip,
654 { "IP", "gnutella.pong.ip",
655 FT_IPv4, BASE_DEC, NULL, 0,
656 "Gnutella Pong IP Address", HFILL }
658 { &hf_gnutella_pong_files,
659 { "Files Shared", "gnutella.pong.files",
660 FT_UINT32, BASE_DEC, NULL, 0,
661 "Gnutella Pong Files Shared", HFILL }
663 { &hf_gnutella_pong_kbytes,
664 { "KBytes Shared", "gnutella.pong.kbytes",
665 FT_UINT32, BASE_DEC, NULL, 0,
666 "Gnutella Pong KBytes Shared", HFILL }
668 { &hf_gnutella_query_min_speed,
669 { "Min Speed", "gnutella.query.min_speed",
670 FT_UINT32, BASE_DEC, NULL, 0,
671 "Gnutella Query Minimum Speed", HFILL }
673 { &hf_gnutella_query_search,
674 { "Search", "gnutella.query.search",
675 FT_STRINGZ, BASE_NONE, NULL, 0,
676 "Gnutella Query Search", HFILL }
678 { &hf_gnutella_queryhit_hit,
679 { "Hit", "gnutella.queryhit.hit",
680 FT_NONE, BASE_NONE, NULL, 0,
681 "Gnutella QueryHit", HFILL }
683 { &hf_gnutella_queryhit_hit_index,
684 { "Index", "gnutella.queryhit.hit.index",
685 FT_UINT32, BASE_DEC, NULL, 0,
686 "Gnutella QueryHit Index", HFILL }
688 { &hf_gnutella_queryhit_hit_size,
689 { "Size", "gnutella.queryhit.hit.size",
690 FT_UINT32, BASE_DEC, NULL, 0,
691 "Gnutella QueryHit Size", HFILL }
693 { &hf_gnutella_queryhit_hit_name,
694 { "Name", "gnutella.queryhit.hit.name",
695 FT_STRING, BASE_NONE, NULL, 0,
696 "Gnutella Query Name", HFILL }
698 { &hf_gnutella_queryhit_hit_extra,
699 { "Extra", "gnutella.queryhit.hit.extra",
700 FT_BYTES, BASE_HEX, NULL, 0,
701 "Gnutella Query Extra", HFILL }
703 { &hf_gnutella_queryhit_count,
704 { "Count", "gnutella.queryhit.count",
705 FT_UINT8, BASE_DEC, NULL, 0,
706 "Gnutella QueryHit Count", HFILL }
708 { &hf_gnutella_queryhit_port,
709 { "Port", "gnutella.queryhit.port",
710 FT_UINT16, BASE_DEC, NULL, 0,
711 "Gnutella QueryHit Port", HFILL }
713 { &hf_gnutella_queryhit_ip,
714 { "IP", "gnutella.queryhit.ip",
715 FT_IPv4, BASE_DEC, NULL, 0,
716 "Gnutella QueryHit IP Address", HFILL }
718 { &hf_gnutella_queryhit_speed,
719 { "Speed", "gnutella.queryhit.speed",
720 FT_UINT32, BASE_DEC, NULL, 0,
721 "Gnutella QueryHit Speed", HFILL }
723 { &hf_gnutella_queryhit_extra,
724 { "Extra", "gnutella.queryhit.extra",
725 FT_BYTES, BASE_HEX, NULL, 0,
726 "Gnutella QueryHit Extra", HFILL }
728 { &hf_gnutella_queryhit_servent_id,
729 { "Servent ID", "gnutella.queryhit.servent_id",
730 FT_BYTES, BASE_HEX, NULL, 0,
731 "Gnutella QueryHit Servent ID", HFILL }
733 { &hf_gnutella_push_servent_id,
734 { "Servent ID", "gnutella.push.servent_id",
735 FT_BYTES, BASE_HEX, NULL, 0,
736 "Gnutella Push Servent ID", HFILL }
738 { &hf_gnutella_push_ip,
739 { "IP", "gnutella.push.ip",
740 FT_IPv4, BASE_DEC, NULL, 0,
741 "Gnutella Push IP Address", HFILL }
743 { &hf_gnutella_push_index,
744 { "Index", "gnutella.push.index",
745 FT_UINT32, BASE_DEC, NULL, 0,
746 "Gnutella Push Index", HFILL }
748 { &hf_gnutella_push_port,
749 { "Port", "gnutella.push.port",
750 FT_UINT16, BASE_DEC, NULL, 0,
751 "Gnutella Push Port", HFILL }
755 static gint *ett[] = {
759 proto_gnutella = proto_register_protocol("Gnutella Protocol",
763 proto_register_field_array(proto_gnutella, hf, array_length(hf));
765 proto_register_subtree_array(ett, array_length(ett));
768 void proto_reg_handoff_gnutella(void) {
769 dissector_add("tcp.port",