For proto_tree_add_item(..., proto_xxx, ...)use ENC_NA as the encoding arg.
[obnox/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  * $Id$
12  *
13  * Wireshark - Network traffic analyzer
14  * By Gerald Combs <gerald[AT]wireshark.org>
15  * Copyright 1998 Gerald Combs
16  *
17  * Copied from packet-time.c
18  *
19  * This program is free software; you can redistribute it and/or
20  * modify it under the terms of the GNU General Public License
21  * as published by the Free Software Foundation; either version 2
22  * of the License, or (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program; if not, write to the Free Software
31  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
32  */
33
34 #ifdef HAVE_CONFIG_H
35 # include "config.h"
36 #endif
37
38 #include <epan/packet.h>
39 #include <epan/conversation.h>
40
41 #define PNAME  "Erlang Port Mapper Daemon"
42 #define PSNAME "EPMD"
43 #define PFNAME "epmd"
44
45 static int proto_epmd = -1;
46 static int hf_epmd_len = -1;
47 static int hf_epmd_type = -1;
48 static int hf_epmd_port_no = -1;
49 static int hf_epmd_node_type = -1;
50 static int hf_epmd_protocol = -1;
51 static int hf_epmd_dist_high = -1;
52 static int hf_epmd_dist_low = -1;
53 static int hf_epmd_name_len = -1;
54 static int hf_epmd_name = -1;
55 static int hf_epmd_elen = -1;
56 static int hf_epmd_edata = -1;
57 static int hf_epmd_names = -1;
58 static int hf_epmd_result = -1;
59 static int hf_epmd_creation = -1;
60
61 static gint ett_epmd = -1;
62
63 /* Other dissectors */
64 static dissector_handle_t edp_handle = NULL;
65
66 #define EPMD_PORT 4369
67
68 /* Definitions of message codes */
69 #define EPMD_ALIVE_REQ    'a'
70 #define EPMD_ALIVE_OK_RESP 'Y'
71 #define EPMD_PORT_REQ     'p'
72 #define EPMD_NAMES_REQ    'n'
73 #define EPMD_DUMP_REQ     'd'
74 #define EPMD_KILL_REQ     'k'
75 #define EPMD_STOP_REQ     's'
76 /* New epmd messages */
77 #define EPMD_ALIVE2_REQ   'x' /* 120 */
78 #define EPMD_PORT2_REQ    'z' /* 122 */
79 #define EPMD_ALIVE2_RESP  'y' /* 121 */
80 #define EPMD_PORT2_RESP   'w' /* 119 */
81
82 static const value_string message_types[] = {
83   { EPMD_ALIVE_REQ    , "EPMD_ALIVE_REQ" },
84   { EPMD_ALIVE_OK_RESP, "EPMD_ALIVE_OK_RESP" },
85   { EPMD_PORT_REQ     , "EPMD_PORT_REQ" },
86   { EPMD_NAMES_REQ    , "EPMD_NAMES_REQ" },
87   { EPMD_DUMP_REQ     , "EPMD_DUMP_REQ" },
88   { EPMD_KILL_REQ     , "EPMD_KILL_REQ" },
89   { EPMD_STOP_REQ     , "EPMD_STOP_REQ" },
90   { EPMD_ALIVE2_REQ   , "EPMD_ALIVE2_REQ" },
91   { EPMD_PORT2_REQ    , "EPMD_PORT2_REQ" },
92   { EPMD_ALIVE2_RESP  , "EPMD_ALIVE2_RESP" },
93   { EPMD_PORT2_RESP   , "EPMD_PORT2_RESP" },
94   {  0, NULL }
95 };
96
97 static const value_string node_type_vals[] = {
98   {  72 , "R3 hidden node" },
99   {  77 , "R3 erlang node" },
100   { 104 , "R4 hidden node" },
101   { 109 , "R4 erlang node" },
102   { 110 , "R6 nodes" },
103   {  0, NULL }
104 };
105
106 static const value_string protocol_vals[] = {
107   {  0 , "tcp/ip-v4" },
108   {  0, NULL }
109 };
110
111 const value_string epmd_version_vals[] = {
112   {  0 , "R3" },
113   {  1 , "R4" },
114   {  2 , "R5" },
115   {  3 , "R5C" },
116   {  4 , "R6 dev" },
117   {  5 , "R6" },
118   {  0, NULL }
119 };
120
121 static void
122 dissect_epmd_request(packet_info *pinfo, tvbuff_t *tvb, gint offset, proto_tree *tree) {
123   guint8 type;
124   guint16 name_length = 0;
125   const gchar *name = NULL;
126
127   proto_tree_add_item(tree, hf_epmd_len, tvb, offset, 2, ENC_BIG_ENDIAN);
128   offset += 2;
129   type = tvb_get_guint8(tvb, offset);
130   proto_tree_add_item(tree, hf_epmd_type, tvb, offset, 1, ENC_BIG_ENDIAN);
131   offset++;
132   col_add_str(pinfo->cinfo, COL_INFO, val_to_str(type, VALS(message_types), "unknown (0x%02X)"));
133
134   switch (type) {
135     case EPMD_ALIVE2_REQ:
136       proto_tree_add_item(tree, hf_epmd_port_no, tvb, offset, 2, ENC_BIG_ENDIAN);
137       offset += 2;
138       proto_tree_add_item(tree, hf_epmd_node_type, tvb, offset, 1, ENC_BIG_ENDIAN);
139       offset++;
140       proto_tree_add_item(tree, hf_epmd_protocol, tvb, offset, 1, ENC_BIG_ENDIAN);
141       offset++;
142       proto_tree_add_item(tree, hf_epmd_dist_high, tvb, offset, 2, ENC_BIG_ENDIAN);
143       offset += 2;
144       proto_tree_add_item(tree, hf_epmd_dist_low, tvb, offset, 2, ENC_BIG_ENDIAN);
145       offset += 2;
146       name_length = tvb_get_ntohs(tvb, offset);
147       proto_tree_add_item(tree, hf_epmd_name_len, tvb, offset, 2, ENC_BIG_ENDIAN);
148       proto_tree_add_item(tree, hf_epmd_name, tvb, offset + 2, name_length, ENC_ASCII|ENC_NA);
149       name = tvb_get_ephemeral_string(tvb, offset + 2, name_length);
150       offset += 2 + name_length;
151       if (tvb_length_remaining(tvb, offset) >= 2) {
152         guint16 elen=0;
153         elen = tvb_get_ntohs(tvb, offset);
154         proto_tree_add_item(tree, hf_epmd_elen, tvb, offset, 2, ENC_BIG_ENDIAN);
155         if (elen > 0)
156           proto_tree_add_item(tree, hf_epmd_edata, tvb, offset + 2, elen, ENC_NA);
157         offset += 2 + elen;
158       }
159       break;
160
161     case EPMD_PORT_REQ:
162     case EPMD_PORT2_REQ:
163       name_length = tvb_length_remaining(tvb, offset);
164       proto_tree_add_item(tree, hf_epmd_name, tvb, offset, name_length, ENC_ASCII|ENC_NA);
165       name = tvb_get_ephemeral_string(tvb, offset, name_length);
166       break;
167
168     case EPMD_ALIVE_REQ:
169       proto_tree_add_item(tree, hf_epmd_port_no, tvb, offset, 2, ENC_BIG_ENDIAN);
170       offset += 2;
171       name_length = tvb_length_remaining(tvb, offset);
172       proto_tree_add_item(tree, hf_epmd_name, tvb, offset, name_length, ENC_ASCII|ENC_NA);
173       name = tvb_get_ephemeral_string(tvb, offset, name_length);
174       break;
175
176     case EPMD_NAMES_REQ:
177       break;
178
179   }
180
181   if (name) {
182     col_append_fstr(pinfo->cinfo, COL_INFO, " %s", name);
183   }
184
185 }
186
187 static void
188 dissect_epmd_response_names(packet_info *pinfo _U_, tvbuff_t *tvb, gint offset, proto_tree *tree) {
189   proto_tree_add_item(tree, hf_epmd_port_no, tvb, offset, 2, ENC_BIG_ENDIAN);
190   offset += 2;
191   proto_tree_add_item(tree, hf_epmd_names, tvb, offset, -1, ENC_NA);
192 }
193
194 static void
195 dissect_epmd_response(packet_info *pinfo, tvbuff_t *tvb, gint offset, proto_tree *tree) {
196   guint8 type, result;
197   guint32 port;
198   guint16 name_length = 0;
199   const gchar *name = NULL;
200   conversation_t *conv = NULL;
201
202   port = tvb_get_ntohl(tvb, offset);
203   if (port == EPMD_PORT) {
204     dissect_epmd_response_names(pinfo, tvb, offset, tree);
205     return;
206   }
207
208   type = tvb_get_guint8(tvb, offset);
209   proto_tree_add_item(tree, hf_epmd_type, tvb, offset, 1, ENC_BIG_ENDIAN);
210   offset++;
211   col_add_str(pinfo->cinfo, COL_INFO, val_to_str(type, VALS(message_types), "unknown (0x%02X)"));
212
213   switch (type) {
214     case EPMD_ALIVE_OK_RESP:
215     case EPMD_ALIVE2_RESP:
216       result = tvb_get_guint8(tvb, offset);
217       proto_tree_add_item(tree, hf_epmd_result, tvb, offset, 1, ENC_BIG_ENDIAN);
218       offset++;
219       proto_tree_add_item(tree, hf_epmd_creation, tvb, offset, 2, ENC_BIG_ENDIAN);
220       offset += 2;
221       if (!result) {
222         col_append_str(pinfo->cinfo, COL_INFO, " OK");
223       } else {
224         col_append_fstr(pinfo->cinfo, COL_INFO, " ERROR 0x%02X", result);
225       }
226       break;
227
228     case EPMD_PORT2_RESP:
229       result = tvb_get_guint8(tvb, offset);
230       proto_tree_add_item(tree, hf_epmd_result, tvb, offset, 1, ENC_BIG_ENDIAN);
231       offset++;
232       if (!result) {
233         col_append_str(pinfo->cinfo, COL_INFO, " OK");
234       } else {
235         col_append_fstr(pinfo->cinfo, COL_INFO, " ERROR 0x%02X", result);
236         break;
237       }
238       port = tvb_get_ntohs(tvb, offset);
239       proto_tree_add_item(tree, hf_epmd_port_no, tvb, offset, 2, ENC_BIG_ENDIAN);
240       offset += 2;
241       proto_tree_add_item(tree, hf_epmd_node_type, tvb, offset, 1, ENC_BIG_ENDIAN);
242       offset++;
243       proto_tree_add_item(tree, hf_epmd_protocol, tvb, offset, 1, ENC_BIG_ENDIAN);
244       offset++;
245       proto_tree_add_item(tree, hf_epmd_dist_high, tvb, offset, 2, ENC_BIG_ENDIAN);
246       offset += 2;
247       proto_tree_add_item(tree, hf_epmd_dist_low, tvb, offset, 2, ENC_BIG_ENDIAN);
248       offset += 2;
249       name_length = tvb_get_ntohs(tvb, offset);
250       proto_tree_add_item(tree, hf_epmd_name_len, tvb, offset, 2, ENC_BIG_ENDIAN);
251       proto_tree_add_item(tree, hf_epmd_name, tvb, offset + 2, name_length, ENC_ASCII|ENC_NA);
252       name = tvb_get_ephemeral_string(tvb, offset + 2, name_length);
253       offset += 2 + name_length;
254       if (tvb_length_remaining(tvb, offset) >= 2) {
255         guint16 elen=0;
256         elen = tvb_get_ntohs(tvb, offset);
257         proto_tree_add_item(tree, hf_epmd_elen, tvb, offset, 2, ENC_BIG_ENDIAN);
258         if (elen > 0)
259           proto_tree_add_item(tree, hf_epmd_edata, tvb, offset + 2, elen, ENC_NA);
260         offset += 2 + elen;
261       }
262       col_append_fstr(pinfo->cinfo, COL_INFO, " %s port=%d", name, port);
263       if (!pinfo->fd->flags.visited) {
264         conv = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_TCP, port, 0, NO_PORT2);
265         conversation_set_dissector(conv, edp_handle);
266       }
267       break;
268   }
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) {
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_port == 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         { &hf_epmd_type,
342             { "Type", "epmd.type",
343                 FT_UINT8, BASE_DEC, VALS(message_types), 0x0,
344                 "Message Type", HFILL }},
345         { &hf_epmd_result,
346             { "Result", "epmd.result",
347                 FT_UINT8, BASE_DEC, NULL, 0x0,
348                 NULL, HFILL }},
349         { &hf_epmd_port_no,
350             { "Port No", "epmd.port_no",
351                 FT_UINT16, BASE_DEC, NULL, 0x0,
352                 NULL, HFILL }},
353         { &hf_epmd_node_type,
354             { "Node Type", "epmd.node_type",
355                 FT_UINT8, BASE_DEC, VALS(node_type_vals), 0x0,
356                 NULL, HFILL }},
357         { &hf_epmd_protocol,
358             { "Protocol", "epmd.protocol",
359                 FT_UINT8, BASE_DEC, VALS(protocol_vals), 0x0,
360                 NULL, HFILL }},
361         { &hf_epmd_creation,
362             { "Creation", "epmd.creation",
363                 FT_UINT16, BASE_DEC, NULL, 0x0,
364                 NULL, HFILL }},
365         { &hf_epmd_dist_high,
366             { "Highest Version", "epmd.dist_high",
367                 FT_UINT16, BASE_DEC, VALS(epmd_version_vals), 0x0,
368                 NULL, HFILL }},
369         { &hf_epmd_dist_low,
370             { "Lowest Version", "epmd.dist_low",
371                 FT_UINT16, BASE_DEC, VALS(epmd_version_vals), 0x0,
372                 NULL, HFILL }},
373         { &hf_epmd_name_len,
374             { "Name Length", "epmd.name_len",
375                 FT_UINT16, BASE_DEC, NULL, 0x0,
376                 NULL, HFILL }},
377         { &hf_epmd_name,
378             { "Node Name", "epmd.name",
379                 FT_STRING, BASE_NONE, NULL, 0x0,
380                 NULL, HFILL }},
381         { &hf_epmd_elen,
382             { "Elen", "epmd.elen",
383                 FT_UINT16, BASE_DEC, NULL, 0x0,
384                 "Extra Length", HFILL }},
385         { &hf_epmd_edata,
386             { "Edata", "epmd.edata",
387                 FT_BYTES, BASE_NONE, NULL, 0x0,
388                 "Extra Data", HFILL }},
389         { &hf_epmd_names,
390             { "Names", "epmd.names",
391                 FT_BYTES, BASE_NONE, NULL, 0x0,
392                 "List of names", HFILL }}
393     };
394     static gint *ett[] = {
395         &ett_epmd,
396     };
397
398     proto_epmd = proto_register_protocol(PNAME, PSNAME, PFNAME);
399     proto_register_field_array(proto_epmd, hf, array_length(hf));
400     proto_register_subtree_array(ett, array_length(ett));
401     new_register_dissector(PFNAME, dissect_epmd, proto_epmd);
402 }
403
404 void
405 proto_reg_handoff_epmd(void) {
406   dissector_handle_t epmd_handle;
407
408   epmd_handle = find_dissector("epmd");
409   edp_handle = find_dissector("erldp");
410
411   dissector_add_uint("tcp.port", EPMD_PORT, epmd_handle);
412 }