2 * dissector for EPMD (Erlang Port Mapper Daemon) messages;
3 * this are the messages sent between Erlang nodes and
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/
9 * (c) 2007 Joost Yervante Damad <joost[AT]teluna.org>
11 * Wireshark - Network traffic analyzer
12 * By Gerald Combs <gerald[AT]wireshark.org>
13 * Copyright 1998 Gerald Combs
15 * Copied from packet-time.c
17 * SPDX-License-Identifier: GPL-2.0-or-later
22 #include <epan/packet.h>
23 #include <epan/conversation.h>
25 #define PNAME "Erlang Port Mapper Daemon"
29 void proto_register_epmd(void);
30 void proto_reg_handoff_epmd(void);
32 static int proto_epmd
;
33 static int hf_epmd_len
;
34 static int hf_epmd_type
;
35 static int hf_epmd_port_no
;
36 static int hf_epmd_node_type
;
37 static int hf_epmd_protocol
;
38 static int hf_epmd_dist_high
;
39 static int hf_epmd_dist_low
;
40 static int hf_epmd_name_len
;
41 static int hf_epmd_name
;
42 static int hf_epmd_elen
;
43 static int hf_epmd_edata
;
44 static int hf_epmd_names
;
45 static int hf_epmd_result
;
46 static int hf_epmd_creation
;
50 static dissector_handle_t epmd_handle
;
52 /* Other dissectors */
53 static dissector_handle_t edp_handle
;
55 #define EPMD_PORT 4369
57 /* Definitions of message codes */
58 #define EPMD_ALIVE_REQ 'a'
59 #define EPMD_ALIVE_OK_RESP 'Y'
60 #define EPMD_PORT_REQ 'p'
61 #define EPMD_NAMES_REQ 'n'
62 #define EPMD_DUMP_REQ 'd'
63 #define EPMD_KILL_REQ 'k'
64 #define EPMD_STOP_REQ 's'
65 /* New epmd messages */
66 #define EPMD_ALIVE2_REQ 'x' /* 120 */
67 #define EPMD_PORT2_REQ 'z' /* 122 */
68 #define EPMD_ALIVE2_RESP 'y' /* 121 */
69 #define EPMD_PORT2_RESP 'w' /* 119 */
71 static const value_string message_types
[] = {
72 { EPMD_ALIVE_REQ
, "EPMD_ALIVE_REQ" },
73 { EPMD_ALIVE_OK_RESP
, "EPMD_ALIVE_OK_RESP" },
74 { EPMD_PORT_REQ
, "EPMD_PORT_REQ" },
75 { EPMD_NAMES_REQ
, "EPMD_NAMES_REQ" },
76 { EPMD_DUMP_REQ
, "EPMD_DUMP_REQ" },
77 { EPMD_KILL_REQ
, "EPMD_KILL_REQ" },
78 { EPMD_STOP_REQ
, "EPMD_STOP_REQ" },
79 { EPMD_ALIVE2_REQ
, "EPMD_ALIVE2_REQ" },
80 { EPMD_PORT2_REQ
, "EPMD_PORT2_REQ" },
81 { EPMD_ALIVE2_RESP
, "EPMD_ALIVE2_RESP" },
82 { EPMD_PORT2_RESP
, "EPMD_PORT2_RESP" },
86 static const value_string node_type_vals
[] = {
87 { 72 , "R3 hidden node" },
88 { 77 , "R3 erlang node" },
89 { 104 , "R4 hidden node" },
90 { 109 , "R4 erlang node" },
95 static const value_string protocol_vals
[] = {
100 const value_string epmd_version_vals
[] = {
111 dissect_epmd_request(packet_info
*pinfo
, tvbuff_t
*tvb
, int offset
, proto_tree
*tree
) {
113 uint16_t name_length
= 0;
114 const uint8_t *name
= NULL
;
116 proto_tree_add_item(tree
, hf_epmd_len
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
118 type
= tvb_get_uint8(tvb
, offset
);
119 proto_tree_add_item(tree
, hf_epmd_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
121 col_add_str(pinfo
->cinfo
, COL_INFO
, val_to_str(type
, VALS(message_types
), "unknown (0x%02X)"));
124 case EPMD_ALIVE2_REQ
:
125 proto_tree_add_item(tree
, hf_epmd_port_no
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
127 proto_tree_add_item(tree
, hf_epmd_node_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
129 proto_tree_add_item(tree
, hf_epmd_protocol
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
131 proto_tree_add_item(tree
, hf_epmd_dist_high
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
133 proto_tree_add_item(tree
, hf_epmd_dist_low
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
135 name_length
= tvb_get_ntohs(tvb
, offset
);
136 proto_tree_add_item(tree
, hf_epmd_name_len
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
137 proto_tree_add_item_ret_string(tree
, hf_epmd_name
, tvb
, offset
+ 2, name_length
, ENC_ASCII
|ENC_NA
, pinfo
->pool
, &name
);
138 offset
+= 2 + name_length
;
139 if (tvb_reported_length_remaining(tvb
, offset
) >= 2) {
141 elen
= tvb_get_ntohs(tvb
, offset
);
142 proto_tree_add_item(tree
, hf_epmd_elen
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
144 proto_tree_add_item(tree
, hf_epmd_edata
, tvb
, offset
+ 2, elen
, ENC_NA
);
145 /*offset += 2 + elen;*/
151 name_length
= tvb_captured_length_remaining(tvb
, offset
);
152 proto_tree_add_item_ret_string(tree
, hf_epmd_name
, tvb
, offset
, name_length
, ENC_ASCII
|ENC_NA
, pinfo
->pool
, &name
);
156 proto_tree_add_item(tree
, hf_epmd_port_no
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
158 name_length
= tvb_captured_length_remaining(tvb
, offset
);
159 proto_tree_add_item_ret_string(tree
, hf_epmd_name
, tvb
, offset
, name_length
, ENC_ASCII
|ENC_NA
, pinfo
->pool
, &name
);
168 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " %s", name
);
174 dissect_epmd_response_names(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset
, proto_tree
*tree
) {
175 proto_tree_add_item(tree
, hf_epmd_port_no
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
177 proto_tree_add_item(tree
, hf_epmd_names
, tvb
, offset
, -1, ENC_NA
);
181 dissect_epmd_response(packet_info
*pinfo
, tvbuff_t
*tvb
, int offset
, proto_tree
*tree
) {
182 uint8_t type
, result
;
184 uint16_t name_length
= 0;
185 const uint8_t *name
= NULL
;
186 conversation_t
*conv
= NULL
;
188 port
= tvb_get_ntohl(tvb
, offset
);
189 if (port
== EPMD_PORT
) {
190 dissect_epmd_response_names(pinfo
, tvb
, offset
, tree
);
194 type
= tvb_get_uint8(tvb
, offset
);
195 proto_tree_add_item(tree
, hf_epmd_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
197 col_add_str(pinfo
->cinfo
, COL_INFO
, val_to_str(type
, VALS(message_types
), "unknown (0x%02X)"));
200 case EPMD_ALIVE_OK_RESP
:
201 case EPMD_ALIVE2_RESP
:
202 result
= tvb_get_uint8(tvb
, offset
);
203 proto_tree_add_item(tree
, hf_epmd_result
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
205 proto_tree_add_item(tree
, hf_epmd_creation
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
208 col_append_str(pinfo
->cinfo
, COL_INFO
, " OK");
210 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " ERROR 0x%02X", result
);
214 case EPMD_PORT2_RESP
:
215 result
= tvb_get_uint8(tvb
, offset
);
216 proto_tree_add_item(tree
, hf_epmd_result
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
219 col_append_str(pinfo
->cinfo
, COL_INFO
, " OK");
221 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " ERROR 0x%02X", result
);
224 port
= tvb_get_ntohs(tvb
, offset
);
225 proto_tree_add_item(tree
, hf_epmd_port_no
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
227 proto_tree_add_item(tree
, hf_epmd_node_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
229 proto_tree_add_item(tree
, hf_epmd_protocol
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
231 proto_tree_add_item(tree
, hf_epmd_dist_high
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
233 proto_tree_add_item(tree
, hf_epmd_dist_low
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
235 name_length
= tvb_get_ntohs(tvb
, offset
);
236 proto_tree_add_item(tree
, hf_epmd_name_len
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
237 proto_tree_add_item_ret_string(tree
, hf_epmd_name
, tvb
, offset
+ 2, name_length
, ENC_ASCII
|ENC_NA
, pinfo
->pool
, &name
);
238 offset
+= 2 + name_length
;
239 if (tvb_reported_length_remaining(tvb
, offset
) >= 2) {
241 elen
= tvb_get_ntohs(tvb
, offset
);
242 proto_tree_add_item(tree
, hf_epmd_elen
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
244 proto_tree_add_item(tree
, hf_epmd_edata
, tvb
, offset
+ 2, elen
, ENC_NA
);
247 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " %s port=%d", name
, port
);
248 if (!pinfo
->fd
->visited
) {
249 conv
= conversation_new(pinfo
->num
, &pinfo
->src
, &pinfo
->dst
, CONVERSATION_TCP
, port
, 0, NO_PORT2
);
250 conversation_set_dissector(conv
, edp_handle
);
258 check_epmd(tvbuff_t
*tvb
) {
263 * just check if the type is one of the EPMD
266 * It's possible to start checking lengths but imho that
267 * doesn't bring very much.
269 if (tvb_captured_length(tvb
) < 3)
272 type
= tvb_get_uint8(tvb
, 0);
274 case EPMD_ALIVE_OK_RESP
:
275 case EPMD_ALIVE2_RESP
:
276 case EPMD_PORT2_RESP
:
282 type
= tvb_get_uint8(tvb
, 2);
285 case EPMD_ALIVE2_REQ
:
298 dissect_epmd(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
299 proto_tree
*epmd_tree
;
302 if (!check_epmd(tvb
))
305 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, PSNAME
);
307 ti
= proto_tree_add_item(tree
, proto_epmd
, tvb
, 0, -1, ENC_NA
);
308 epmd_tree
= proto_item_add_subtree(ti
, ett_epmd
);
310 if (pinfo
->match_uint
== pinfo
->destport
) {
311 dissect_epmd_request(pinfo
, tvb
, 0, epmd_tree
);
313 dissect_epmd_response(pinfo
, tvb
, 0, epmd_tree
);
316 return (tvb_captured_length(tvb
));
320 proto_register_epmd(void)
322 static hf_register_info hf
[] = {
324 { "Length", "epmd.len",
325 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
326 "Message Length", HFILL
}},
329 { "Type", "epmd.type",
330 FT_UINT8
, BASE_DEC
, VALS(message_types
), 0x0,
331 "Message Type", HFILL
}},
334 { "Result", "epmd.result",
335 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
339 { "Port No", "epmd.port_no",
340 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
343 { &hf_epmd_node_type
,
344 { "Node Type", "epmd.node_type",
345 FT_UINT8
, BASE_DEC
, VALS(node_type_vals
), 0x0,
349 { "Protocol", "epmd.protocol",
350 FT_UINT8
, BASE_DEC
, VALS(protocol_vals
), 0x0,
354 { "Creation", "epmd.creation",
355 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
358 { &hf_epmd_dist_high
,
359 { "Highest Version", "epmd.dist_high",
360 FT_UINT16
, BASE_DEC
, VALS(epmd_version_vals
), 0x0,
364 { "Lowest Version", "epmd.dist_low",
365 FT_UINT16
, BASE_DEC
, VALS(epmd_version_vals
), 0x0,
369 { "Name Length", "epmd.name_len",
370 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
374 { "Node Name", "epmd.name",
375 FT_STRING
, BASE_NONE
, NULL
, 0x0,
379 { "Elen", "epmd.elen",
380 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
381 "Extra Length", HFILL
}},
384 { "Edata", "epmd.edata",
385 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
386 "Extra Data", HFILL
}},
389 { "Names", "epmd.names",
390 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
391 "List of names", HFILL
}}
394 static int *ett
[] = {
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 epmd_handle
= register_dissector(PFNAME
, dissect_epmd
, proto_epmd
);
405 proto_reg_handoff_epmd(void) {
406 edp_handle
= find_dissector("erldp");
408 dissector_add_uint_with_preference("tcp.port", EPMD_PORT
, epmd_handle
);
412 * Editor modelines - https://www.wireshark.org/tools/modelines.html
417 * indent-tabs-mode: nil
420 * vi: set shiftwidth=4 tabstop=8 expandtab:
421 * :indentSize=4:tabSize=8:noTabs=true: