Revert "Fixup: tvb_* -> tvb_captured"
[metze/wireshark/wip.git] / epan / dissectors / packet-epmd.c
1 /* packet-epmd.c
2  * dissector for EPMD (Erlang Port Mapper Daemon) messages;
3  * this are the messages sent between Erlang nodes and
4  * the empd process.
5  * The message formats are derived from the
6  * lib/kernel/src/erl_epmd.* files as part of the Erlang
7  * distribution available from http://www.erlang.org/
8  *
9  * (c) 2007 Joost Yervante Damad <joost[AT]teluna.org>
10  *
11  * Wireshark - Network traffic analyzer
12  * By Gerald Combs <gerald[AT]wireshark.org>
13  * Copyright 1998 Gerald Combs
14  *
15  * Copied from packet-time.c
16  *
17  * This program is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU General Public License
19  * as published by the Free Software Foundation; either version 2
20  * of the License, or (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, write to the Free Software
29  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30  */
31
32 #include "config.h"
33
34 #include <epan/packet.h>
35 #include <epan/conversation.h>
36
37 #define PNAME  "Erlang Port Mapper Daemon"
38 #define PSNAME "EPMD"
39 #define PFNAME "epmd"
40
41 void proto_register_epmd(void);
42 void proto_reg_handoff_epmd(void);
43
44 static int proto_epmd = -1;
45 static int hf_epmd_len = -1;
46 static int hf_epmd_type = -1;
47 static int hf_epmd_port_no = -1;
48 static int hf_epmd_node_type = -1;
49 static int hf_epmd_protocol = -1;
50 static int hf_epmd_dist_high = -1;
51 static int hf_epmd_dist_low = -1;
52 static int hf_epmd_name_len = -1;
53 static int hf_epmd_name = -1;
54 static int hf_epmd_elen = -1;
55 static int hf_epmd_edata = -1;
56 static int hf_epmd_names = -1;
57 static int hf_epmd_result = -1;
58 static int hf_epmd_creation = -1;
59
60 static gint ett_epmd = -1;
61
62 /* Other dissectors */
63 static dissector_handle_t edp_handle = NULL;
64
65 #define EPMD_PORT 4369
66
67 /* Definitions of message codes */
68 #define EPMD_ALIVE_REQ     'a'
69 #define EPMD_ALIVE_OK_RESP 'Y'
70 #define EPMD_PORT_REQ      'p'
71 #define EPMD_NAMES_REQ     'n'
72 #define EPMD_DUMP_REQ      'd'
73 #define EPMD_KILL_REQ      'k'
74 #define EPMD_STOP_REQ      's'
75 /* New epmd messages */
76 #define EPMD_ALIVE2_REQ    'x' /* 120 */
77 #define EPMD_PORT2_REQ     'z' /* 122 */
78 #define EPMD_ALIVE2_RESP   'y' /* 121 */
79 #define EPMD_PORT2_RESP    'w' /* 119 */
80
81 static const value_string message_types[] = {
82     { EPMD_ALIVE_REQ    , "EPMD_ALIVE_REQ"     },
83     { EPMD_ALIVE_OK_RESP, "EPMD_ALIVE_OK_RESP" },
84     { EPMD_PORT_REQ     , "EPMD_PORT_REQ"      },
85     { EPMD_NAMES_REQ    , "EPMD_NAMES_REQ"     },
86     { EPMD_DUMP_REQ     , "EPMD_DUMP_REQ"      },
87     { EPMD_KILL_REQ     , "EPMD_KILL_REQ"      },
88     { EPMD_STOP_REQ     , "EPMD_STOP_REQ"      },
89     { EPMD_ALIVE2_REQ   , "EPMD_ALIVE2_REQ"    },
90     { EPMD_PORT2_REQ    , "EPMD_PORT2_REQ"     },
91     { EPMD_ALIVE2_RESP  , "EPMD_ALIVE2_RESP"   },
92     { EPMD_PORT2_RESP   , "EPMD_PORT2_RESP"    },
93     {  0, NULL }
94 };
95
96 static const value_string node_type_vals[] = {
97     {  72 , "R3 hidden node" },
98     {  77 , "R3 erlang node" },
99     { 104 , "R4 hidden node" },
100     { 109 , "R4 erlang node" },
101     { 110 , "R6 nodes" },
102     {  0, NULL }
103 };
104
105 static const value_string protocol_vals[] = {
106     {  0 , "tcp/ip-v4" },
107     {  0, NULL }
108 };
109
110 const value_string epmd_version_vals[] = {
111     {  0 , "R3"     },
112     {  1 , "R4"     },
113     {  2 , "R5"     },
114     {  3 , "R5C"    },
115     {  4 , "R6 dev" },
116     {  5 , "R6"     },
117     {  0, NULL }
118 };
119
120 static void
121 dissect_epmd_request(packet_info *pinfo, tvbuff_t *tvb, gint offset, proto_tree *tree) {
122     guint8       type;
123     guint16      name_length = 0;
124     const gchar *name        = NULL;
125
126     proto_tree_add_item(tree, hf_epmd_len, tvb, offset, 2, ENC_BIG_ENDIAN);
127     offset += 2;
128     type = tvb_get_guint8(tvb, offset);
129     proto_tree_add_item(tree, hf_epmd_type, tvb, offset, 1, ENC_BIG_ENDIAN);
130     offset++;
131     col_add_str(pinfo->cinfo, COL_INFO, val_to_str(type, VALS(message_types), "unknown (0x%02X)"));
132
133     switch (type) {
134         case EPMD_ALIVE2_REQ:
135             proto_tree_add_item(tree, hf_epmd_port_no, tvb, offset, 2, ENC_BIG_ENDIAN);
136             offset += 2;
137             proto_tree_add_item(tree, hf_epmd_node_type, tvb, offset, 1, ENC_BIG_ENDIAN);
138             offset++;
139             proto_tree_add_item(tree, hf_epmd_protocol, tvb, offset, 1, ENC_BIG_ENDIAN);
140             offset++;
141             proto_tree_add_item(tree, hf_epmd_dist_high, tvb, offset, 2, ENC_BIG_ENDIAN);
142             offset += 2;
143             proto_tree_add_item(tree, hf_epmd_dist_low, tvb, offset, 2, ENC_BIG_ENDIAN);
144             offset += 2;
145             name_length = tvb_get_ntohs(tvb, offset);
146             proto_tree_add_item(tree, hf_epmd_name_len, tvb, offset, 2, ENC_BIG_ENDIAN);
147             proto_tree_add_item(tree, hf_epmd_name, tvb, offset + 2, name_length, ENC_ASCII|ENC_NA);
148             name = tvb_get_string_enc(wmem_packet_scope(), tvb, offset + 2, name_length, ENC_ASCII);
149             offset += 2 + name_length;
150             if (tvb_length_remaining(tvb, offset) >= 2) {
151                 guint16 elen=0;
152                 elen = tvb_get_ntohs(tvb, offset);
153                 proto_tree_add_item(tree, hf_epmd_elen, tvb, offset, 2, ENC_BIG_ENDIAN);
154                 if (elen > 0)
155                     proto_tree_add_item(tree, hf_epmd_edata, tvb, offset + 2, elen, ENC_NA);
156                 /*offset += 2 + elen;*/
157             }
158             break;
159
160         case EPMD_PORT_REQ:
161         case EPMD_PORT2_REQ:
162             name_length = tvb_length_remaining(tvb, offset);
163             proto_tree_add_item(tree, hf_epmd_name, tvb, offset, name_length, ENC_ASCII|ENC_NA);
164             name = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, name_length, ENC_ASCII);
165             break;
166
167         case EPMD_ALIVE_REQ:
168             proto_tree_add_item(tree, hf_epmd_port_no, tvb, offset, 2, ENC_BIG_ENDIAN);
169             offset += 2;
170             name_length = tvb_length_remaining(tvb, offset);
171             proto_tree_add_item(tree, hf_epmd_name, tvb, offset, name_length, ENC_ASCII|ENC_NA);
172             name = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, name_length, ENC_ASCII);
173             break;
174
175         case EPMD_NAMES_REQ:
176             break;
177
178     }
179
180     if (name) {
181         col_append_fstr(pinfo->cinfo, COL_INFO, " %s", name);
182     }
183
184 }
185
186 static void
187 dissect_epmd_response_names(packet_info *pinfo _U_, tvbuff_t *tvb, gint offset, proto_tree *tree) {
188     proto_tree_add_item(tree, hf_epmd_port_no, tvb, offset, 2, ENC_BIG_ENDIAN);
189     offset += 2;
190     proto_tree_add_item(tree, hf_epmd_names, tvb, offset, -1, ENC_NA);
191 }
192
193 static int
194 dissect_epmd_response(packet_info *pinfo, tvbuff_t *tvb, gint offset, proto_tree *tree) {
195     guint8          type, result;
196     guint32         port;
197     guint16         name_length = 0;
198     const gchar    *name        = NULL;
199     conversation_t *conv        = NULL;
200
201     port = tvb_get_ntohl(tvb, offset);
202     if (port == EPMD_PORT) {
203         dissect_epmd_response_names(pinfo, tvb, offset, tree);
204         return 0;
205     }
206
207     type = tvb_get_guint8(tvb, offset);
208     proto_tree_add_item(tree, hf_epmd_type, tvb, offset, 1, ENC_BIG_ENDIAN);
209     offset++;
210     col_add_str(pinfo->cinfo, COL_INFO, val_to_str(type, VALS(message_types), "unknown (0x%02X)"));
211
212     switch (type) {
213         case EPMD_ALIVE_OK_RESP:
214         case EPMD_ALIVE2_RESP:
215             result = tvb_get_guint8(tvb, offset);
216             proto_tree_add_item(tree, hf_epmd_result, tvb, offset, 1, ENC_BIG_ENDIAN);
217             offset++;
218             proto_tree_add_item(tree, hf_epmd_creation, tvb, offset, 2, ENC_BIG_ENDIAN);
219             offset += 2;
220             if (!result) {
221                 col_append_str(pinfo->cinfo, COL_INFO, " OK");
222             } else {
223                 col_append_fstr(pinfo->cinfo, COL_INFO, " ERROR 0x%02X", result);
224             }
225             break;
226
227         case EPMD_PORT2_RESP:
228             result = tvb_get_guint8(tvb, offset);
229             proto_tree_add_item(tree, hf_epmd_result, tvb, offset, 1, ENC_BIG_ENDIAN);
230             offset++;
231             if (!result) {
232                 col_append_str(pinfo->cinfo, COL_INFO, " OK");
233             } else {
234                 col_append_fstr(pinfo->cinfo, COL_INFO, " ERROR 0x%02X", result);
235                 break;
236             }
237             port = tvb_get_ntohs(tvb, offset);
238             proto_tree_add_item(tree, hf_epmd_port_no, tvb, offset, 2, ENC_BIG_ENDIAN);
239             offset += 2;
240             proto_tree_add_item(tree, hf_epmd_node_type, tvb, offset, 1, ENC_BIG_ENDIAN);
241             offset++;
242             proto_tree_add_item(tree, hf_epmd_protocol, tvb, offset, 1, ENC_BIG_ENDIAN);
243             offset++;
244             proto_tree_add_item(tree, hf_epmd_dist_high, tvb, offset, 2, ENC_BIG_ENDIAN);
245             offset += 2;
246             proto_tree_add_item(tree, hf_epmd_dist_low, tvb, offset, 2, ENC_BIG_ENDIAN);
247             offset += 2;
248             name_length = tvb_get_ntohs(tvb, offset);
249             proto_tree_add_item(tree, hf_epmd_name_len, tvb, offset, 2, ENC_BIG_ENDIAN);
250             proto_tree_add_item(tree, hf_epmd_name, tvb, offset + 2, name_length, ENC_ASCII|ENC_NA);
251             name = tvb_get_string_enc(wmem_packet_scope(), tvb, offset + 2, name_length, ENC_ASCII);
252             offset += 2 + name_length;
253             if (tvb_length_remaining(tvb, offset) >= 2) {
254                 guint16 elen=0;
255                 elen = tvb_get_ntohs(tvb, offset);
256                 proto_tree_add_item(tree, hf_epmd_elen, tvb, offset, 2, ENC_BIG_ENDIAN);
257                 if (elen > 0)
258                     proto_tree_add_item(tree, hf_epmd_edata, tvb, offset + 2, elen, ENC_NA);
259                 offset += 2 + elen;
260             }
261             col_append_fstr(pinfo->cinfo, COL_INFO, " %s port=%d", name, port);
262             if (!pinfo->fd->flags.visited) {
263                 conv = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_TCP, port, 0, NO_PORT2);
264                 conversation_set_dissector(conv, edp_handle);
265             }
266             break;
267     }
268     return offset;
269 }
270
271 static gboolean
272 check_epmd(tvbuff_t *tvb) {
273     guint8 type;
274
275     /* simple heuristic:
276      *
277      * just check if the type is one of the EPMD
278      * command types
279      *
280      * It's possible to start checking lengths but imho that
281      * doesn't bring very much.
282      */
283     if (tvb_length(tvb) < 3)
284         return (FALSE);
285
286     type = tvb_get_guint8(tvb, 0);
287     switch (type) {
288         case EPMD_ALIVE_OK_RESP:
289         case EPMD_ALIVE2_RESP:
290         case EPMD_PORT2_RESP:
291             return (TRUE);
292         default:
293             break;
294     }
295
296     type = tvb_get_guint8(tvb, 2);
297     switch (type) {
298         case EPMD_ALIVE_REQ:
299         case EPMD_ALIVE2_REQ:
300         case EPMD_PORT_REQ:
301         case EPMD_PORT2_REQ:
302         case EPMD_NAMES_REQ:
303             return (TRUE);
304         default:
305             break;
306     }
307
308     return (FALSE);
309 }
310
311 static int
312 dissect_epmd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
313     proto_tree *epmd_tree;
314     proto_item *ti;
315
316     if (!check_epmd(tvb))
317         return (0);
318
319     col_set_str(pinfo->cinfo, COL_PROTOCOL, PSNAME);
320
321     ti = proto_tree_add_item(tree, proto_epmd, tvb, 0, -1, ENC_NA);
322     epmd_tree = proto_item_add_subtree(ti, ett_epmd);
323
324     if (pinfo->match_uint == pinfo->destport) {
325         dissect_epmd_request(pinfo, tvb, 0, epmd_tree);
326     } else {
327         dissect_epmd_response(pinfo, tvb, 0, epmd_tree);
328     }
329
330     return (tvb_length(tvb));
331 }
332
333 void
334 proto_register_epmd(void)
335 {
336     static hf_register_info hf[] = {
337         { &hf_epmd_len,
338           { "Length", "epmd.len",
339             FT_UINT16, BASE_DEC, NULL, 0x0,
340             "Message Length", HFILL }},
341
342         { &hf_epmd_type,
343           { "Type", "epmd.type",
344             FT_UINT8, BASE_DEC, VALS(message_types), 0x0,
345             "Message Type", HFILL }},
346
347         { &hf_epmd_result,
348           { "Result", "epmd.result",
349             FT_UINT8, BASE_DEC, NULL, 0x0,
350             NULL, HFILL }},
351
352         { &hf_epmd_port_no,
353           { "Port No", "epmd.port_no",
354             FT_UINT16, BASE_DEC, NULL, 0x0,
355             NULL, HFILL }},
356
357         { &hf_epmd_node_type,
358           { "Node Type", "epmd.node_type",
359             FT_UINT8, BASE_DEC, VALS(node_type_vals), 0x0,
360             NULL, HFILL }},
361
362         { &hf_epmd_protocol,
363           { "Protocol", "epmd.protocol",
364             FT_UINT8, BASE_DEC, VALS(protocol_vals), 0x0,
365             NULL, HFILL }},
366
367         { &hf_epmd_creation,
368           { "Creation", "epmd.creation",
369             FT_UINT16, BASE_DEC, NULL, 0x0,
370             NULL, HFILL }},
371
372         { &hf_epmd_dist_high,
373           { "Highest Version", "epmd.dist_high",
374             FT_UINT16, BASE_DEC, VALS(epmd_version_vals), 0x0,
375             NULL, HFILL }},
376
377         { &hf_epmd_dist_low,
378           { "Lowest Version", "epmd.dist_low",
379             FT_UINT16, BASE_DEC, VALS(epmd_version_vals), 0x0,
380             NULL, HFILL }},
381
382         { &hf_epmd_name_len,
383           { "Name Length", "epmd.name_len",
384             FT_UINT16, BASE_DEC, NULL, 0x0,
385             NULL, HFILL }},
386
387         { &hf_epmd_name,
388           { "Node Name", "epmd.name",
389             FT_STRING, BASE_NONE, NULL, 0x0,
390             NULL, HFILL }},
391
392         { &hf_epmd_elen,
393           { "Elen", "epmd.elen",
394             FT_UINT16, BASE_DEC, NULL, 0x0,
395             "Extra Length", HFILL }},
396
397         { &hf_epmd_edata,
398           { "Edata", "epmd.edata",
399             FT_BYTES, BASE_NONE, NULL, 0x0,
400             "Extra Data", HFILL }},
401
402         { &hf_epmd_names,
403           { "Names", "epmd.names",
404             FT_BYTES, BASE_NONE, NULL, 0x0,
405             "List of names", HFILL }}
406     };
407
408     static gint *ett[] = {
409         &ett_epmd,
410     };
411
412     proto_epmd = proto_register_protocol(PNAME, PSNAME, PFNAME);
413     proto_register_field_array(proto_epmd, hf, array_length(hf));
414     proto_register_subtree_array(ett, array_length(ett));
415     new_register_dissector(PFNAME, dissect_epmd, proto_epmd);
416 }
417
418 void
419 proto_reg_handoff_epmd(void) {
420     dissector_handle_t epmd_handle;
421
422     epmd_handle = find_dissector("epmd");
423     edp_handle = find_dissector("erldp");
424
425     dissector_add_uint("tcp.port", EPMD_PORT, epmd_handle);
426 }