1 /* packet-lsd.c
2  * Local Service Discovery packet dissector
3  *
4  * From http://bittorrent.org/beps/bep_0014.html
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 #include "config.h"
14 
15 #include <epan/packet.h>
16 #include <epan/expert.h>
17 #include <wsutil/pint.h>
18 #include <wsutil/strtoi.h>
19 
20 void proto_register_lsd(void);
21 void proto_reg_handoff_lsd(void);
22 
23 #define LSD_MULTICAST_ADDRESS 0xEFC0988F /* 239.192.152.143 */
24 #define LSD_PORT 6771
25 
26 static int proto_lsd = -1;
27 static int hf_lsd_header = -1;
28 static int hf_lsd_host = -1;
29 static int hf_lsd_port = -1;
30 static int hf_lsd_infohash = -1;
31 static int hf_lsd_cookie = -1;
32 
33 static gint ett_lsd = -1;
34 
35 static expert_field ei_lsd_field = EI_INIT;
36 
37 static gboolean
parse_string_field(proto_tree * tree,int hf,packet_info * pinfo,tvbuff_t * tvb,int offset,int * next_offset,int * linelen)38 parse_string_field(proto_tree *tree, int hf, packet_info *pinfo, tvbuff_t *tvb, int offset, int* next_offset, int* linelen)
39 {
40   guint8 *str;
41   header_field_info* hf_info = proto_registrar_get_nth(hf);
42   gchar **field_and_value;
43   proto_item* ti;
44   gchar *p;
45 
46   *linelen = tvb_find_line_end(tvb, offset, -1, next_offset, FALSE);
47   if (*linelen < 0)
48     return FALSE;
49 
50   str = tvb_get_string_enc(pinfo->pool, tvb, offset, *linelen, ENC_ASCII);
51   if (g_ascii_strncasecmp(str, hf_info->name, strlen(hf_info->name)) == 0)
52   {
53       field_and_value = wmem_strsplit(pinfo->pool, str, ":", 2);
54       p = field_and_value[1];
55       if (p) {
56         while(g_ascii_isspace(*p))
57           p++;
58         proto_tree_add_string(tree, hf, tvb, offset, *linelen, p);
59         return TRUE;
60       }
61   }
62   ti = proto_tree_add_string_format(tree, hf, tvb, offset, *linelen, str, "%s", str);
63   expert_add_info_format(pinfo, ti, &ei_lsd_field, "%s field malformed", hf_info->name);
64 
65   return TRUE;
66 }
67 
68 static int
dissect_lsd(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)69 dissect_lsd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
70 {
71   proto_item *ti = NULL;
72   proto_tree *lsd_tree;
73   int offset = 0, next_offset = 0, linelen;
74   guint8 *str;
75   gchar **field_and_value;
76   guint16 port;
77   gboolean valid;
78 
79   linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
80   if (linelen < 0)
81       return 0;
82 
83   col_set_str(pinfo->cinfo, COL_PROTOCOL, "LSD");
84   col_clear(pinfo->cinfo, COL_INFO);
85 
86   /* Create display subtree for the protocol */
87   ti = proto_tree_add_item(tree, proto_lsd, tvb, 0, -1, ENC_NA);
88   lsd_tree = proto_item_add_subtree(ti, ett_lsd);
89 
90   proto_tree_add_item(lsd_tree, hf_lsd_header, tvb, offset, linelen, ENC_ASCII|ENC_NA);
91 
92   offset = next_offset;
93   if (!parse_string_field(lsd_tree, hf_lsd_host, pinfo, tvb, offset, &next_offset, &linelen))
94       return offset+linelen;
95 
96   offset = next_offset;
97   linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
98   if (linelen < 0)
99       return offset+linelen;
100   str = tvb_get_string_enc(pinfo->pool, tvb, offset, linelen, ENC_ASCII);
101   if (g_ascii_strncasecmp(str, "Port", strlen("Port")) == 0)
102   {
103     field_and_value = wmem_strsplit(pinfo->pool, str, ":", 2);
104     valid = ws_strtou16(field_and_value[1], NULL, &port);
105     ti = proto_tree_add_uint(lsd_tree, hf_lsd_port, tvb, offset, linelen, port);
106     if (!valid)
107     {
108       expert_add_info_format(pinfo, ti, &ei_lsd_field, "Port value malformed");
109     }
110   }
111   else
112   {
113     ti = proto_tree_add_uint(lsd_tree, hf_lsd_port, tvb, offset, 0, 0xFFFF);
114     expert_add_info_format(pinfo, ti, &ei_lsd_field, "Port field malformed");
115   }
116   proto_item_set_len(ti, linelen);
117 
118   offset = next_offset;
119   if (!parse_string_field(lsd_tree, hf_lsd_infohash, pinfo, tvb, offset, &next_offset, &linelen))
120       return offset+linelen;
121 
122   offset = next_offset;
123   linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
124   if (linelen < 0)
125       return offset+linelen;
126   /* Cookie is optionnal */
127   if (tvb_strncaseeql(tvb, offset, "cookie", strlen("cookie")) == 0)
128   {
129     if (!parse_string_field(lsd_tree, hf_lsd_cookie, pinfo, tvb, offset, &next_offset, &linelen))
130       return offset+linelen;
131   }
132 
133   return tvb_captured_length(tvb);
134 }
135 
136 static gboolean
dissect_lsd_heur(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)137 dissect_lsd_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
138 {
139   if (pinfo->dst.type == AT_IPv4 && pntoh32(pinfo->dst.data) == LSD_MULTICAST_ADDRESS && pinfo->destport == LSD_PORT)
140       return (dissect_lsd(tvb, pinfo, tree, data) != 0);
141 
142   if (pinfo->dst.type == AT_IPv6 && pinfo->destport == LSD_PORT)
143       return (dissect_lsd(tvb, pinfo, tree, data) != 0);
144 
145   return FALSE;
146 }
147 
148 void
proto_register_lsd(void)149 proto_register_lsd(void)
150 {
151   static hf_register_info hf[] = {
152     { &hf_lsd_header,
153       { "Header", "lsd.header",
154         FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL }
155     },
156     { &hf_lsd_host,
157       { "Host", "lsd.host",
158         FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL }
159     },
160     { &hf_lsd_port,
161       { "Port", "lsd.port",
162         FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }
163     },
164     { &hf_lsd_infohash,
165       { "Infohash", "lsd.infohash",
166         FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL }
167     },
168     { &hf_lsd_cookie,
169       { "cookie", "lsd.cookie",
170         FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL }
171     },
172   };
173 
174   static gint *ett[] = {
175     &ett_lsd,
176   };
177 
178   static ei_register_info ei[] = {
179     { &ei_lsd_field, { "lsd.malformed_field", PI_MALFORMED, PI_ERROR, "Malformed LDS field", EXPFILL }},
180   };
181 
182   expert_module_t* expert_lsd;
183 
184   proto_lsd = proto_register_protocol("Local Service Discovery", "LSD", "lsd");
185 
186   proto_register_field_array(proto_lsd, hf, array_length(hf));
187   proto_register_subtree_array(ett, array_length(ett));
188   expert_lsd = expert_register_protocol(proto_lsd);
189   expert_register_field_array(expert_lsd, ei, array_length(ei));
190 }
191 
192 void
proto_reg_handoff_lsd(void)193 proto_reg_handoff_lsd(void)
194 {
195     heur_dissector_add( "udp", dissect_lsd_heur, "LSD over UDP", "lsd_udp", proto_lsd, HEURISTIC_ENABLE);
196 }
197 
198 /*
199  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
200  *
201  * Local Variables:
202  * c-basic-offset: 2
203  * tab-width: 8
204  * indent-tabs-mode: nil
205  * End:
206  *
207  * ex: set shiftwidth=2 tabstop=8 expandtab:
208  * :indentSize=2:tabSize=8:noTabs=true:
209  */
210