Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-epmd.c
blob8f9e380e991166cc083c8c1acec37cf66ad14324
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/
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
20 #include "config.h"
22 #include <epan/packet.h>
23 #include <epan/conversation.h>
25 #define PNAME "Erlang Port Mapper Daemon"
26 #define PSNAME "EPMD"
27 #define PFNAME "epmd"
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;
48 static int ett_epmd;
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" },
83 { 0, NULL }
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" },
91 { 110 , "R6 nodes" },
92 { 0, NULL }
95 static const value_string protocol_vals[] = {
96 { 0 , "tcp/ip-v4" },
97 { 0, NULL }
100 const value_string epmd_version_vals[] = {
101 { 0 , "R3" },
102 { 1 , "R4" },
103 { 2 , "R5" },
104 { 3 , "R5C" },
105 { 4 , "R6 dev" },
106 { 5 , "R6" },
107 { 0, NULL }
110 static void
111 dissect_epmd_request(packet_info *pinfo, tvbuff_t *tvb, int offset, proto_tree *tree) {
112 uint8_t type;
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);
117 offset += 2;
118 type = tvb_get_uint8(tvb, offset);
119 proto_tree_add_item(tree, hf_epmd_type, tvb, offset, 1, ENC_BIG_ENDIAN);
120 offset++;
121 col_add_str(pinfo->cinfo, COL_INFO, val_to_str(type, VALS(message_types), "unknown (0x%02X)"));
123 switch (type) {
124 case EPMD_ALIVE2_REQ:
125 proto_tree_add_item(tree, hf_epmd_port_no, tvb, offset, 2, ENC_BIG_ENDIAN);
126 offset += 2;
127 proto_tree_add_item(tree, hf_epmd_node_type, tvb, offset, 1, ENC_BIG_ENDIAN);
128 offset++;
129 proto_tree_add_item(tree, hf_epmd_protocol, tvb, offset, 1, ENC_BIG_ENDIAN);
130 offset++;
131 proto_tree_add_item(tree, hf_epmd_dist_high, tvb, offset, 2, ENC_BIG_ENDIAN);
132 offset += 2;
133 proto_tree_add_item(tree, hf_epmd_dist_low, tvb, offset, 2, ENC_BIG_ENDIAN);
134 offset += 2;
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) {
140 uint16_t elen=0;
141 elen = tvb_get_ntohs(tvb, offset);
142 proto_tree_add_item(tree, hf_epmd_elen, tvb, offset, 2, ENC_BIG_ENDIAN);
143 if (elen > 0)
144 proto_tree_add_item(tree, hf_epmd_edata, tvb, offset + 2, elen, ENC_NA);
145 /*offset += 2 + elen;*/
147 break;
149 case EPMD_PORT_REQ:
150 case EPMD_PORT2_REQ:
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);
153 break;
155 case EPMD_ALIVE_REQ:
156 proto_tree_add_item(tree, hf_epmd_port_no, tvb, offset, 2, ENC_BIG_ENDIAN);
157 offset += 2;
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);
160 break;
162 case EPMD_NAMES_REQ:
163 break;
167 if (name) {
168 col_append_fstr(pinfo->cinfo, COL_INFO, " %s", name);
173 static void
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);
176 offset += 2;
177 proto_tree_add_item(tree, hf_epmd_names, tvb, offset, -1, ENC_NA);
180 static int
181 dissect_epmd_response(packet_info *pinfo, tvbuff_t *tvb, int offset, proto_tree *tree) {
182 uint8_t type, result;
183 uint32_t port;
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);
191 return 0;
194 type = tvb_get_uint8(tvb, offset);
195 proto_tree_add_item(tree, hf_epmd_type, tvb, offset, 1, ENC_BIG_ENDIAN);
196 offset++;
197 col_add_str(pinfo->cinfo, COL_INFO, val_to_str(type, VALS(message_types), "unknown (0x%02X)"));
199 switch (type) {
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);
204 offset++;
205 proto_tree_add_item(tree, hf_epmd_creation, tvb, offset, 2, ENC_BIG_ENDIAN);
206 offset += 2;
207 if (!result) {
208 col_append_str(pinfo->cinfo, COL_INFO, " OK");
209 } else {
210 col_append_fstr(pinfo->cinfo, COL_INFO, " ERROR 0x%02X", result);
212 break;
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);
217 offset++;
218 if (!result) {
219 col_append_str(pinfo->cinfo, COL_INFO, " OK");
220 } else {
221 col_append_fstr(pinfo->cinfo, COL_INFO, " ERROR 0x%02X", result);
222 break;
224 port = tvb_get_ntohs(tvb, offset);
225 proto_tree_add_item(tree, hf_epmd_port_no, tvb, offset, 2, ENC_BIG_ENDIAN);
226 offset += 2;
227 proto_tree_add_item(tree, hf_epmd_node_type, tvb, offset, 1, ENC_BIG_ENDIAN);
228 offset++;
229 proto_tree_add_item(tree, hf_epmd_protocol, tvb, offset, 1, ENC_BIG_ENDIAN);
230 offset++;
231 proto_tree_add_item(tree, hf_epmd_dist_high, tvb, offset, 2, ENC_BIG_ENDIAN);
232 offset += 2;
233 proto_tree_add_item(tree, hf_epmd_dist_low, tvb, offset, 2, ENC_BIG_ENDIAN);
234 offset += 2;
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) {
240 uint16_t elen=0;
241 elen = tvb_get_ntohs(tvb, offset);
242 proto_tree_add_item(tree, hf_epmd_elen, tvb, offset, 2, ENC_BIG_ENDIAN);
243 if (elen > 0)
244 proto_tree_add_item(tree, hf_epmd_edata, tvb, offset + 2, elen, ENC_NA);
245 offset += 2 + elen;
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);
252 break;
254 return offset;
257 static bool
258 check_epmd(tvbuff_t *tvb) {
259 uint8_t type;
261 /* simple heuristic:
263 * just check if the type is one of the EPMD
264 * command types
266 * It's possible to start checking lengths but imho that
267 * doesn't bring very much.
269 if (tvb_captured_length(tvb) < 3)
270 return false;
272 type = tvb_get_uint8(tvb, 0);
273 switch (type) {
274 case EPMD_ALIVE_OK_RESP:
275 case EPMD_ALIVE2_RESP:
276 case EPMD_PORT2_RESP:
277 return true;
278 default:
279 break;
282 type = tvb_get_uint8(tvb, 2);
283 switch (type) {
284 case EPMD_ALIVE_REQ:
285 case EPMD_ALIVE2_REQ:
286 case EPMD_PORT_REQ:
287 case EPMD_PORT2_REQ:
288 case EPMD_NAMES_REQ:
289 return true;
290 default:
291 break;
294 return false;
297 static int
298 dissect_epmd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
299 proto_tree *epmd_tree;
300 proto_item *ti;
302 if (!check_epmd(tvb))
303 return 0;
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);
312 } else {
313 dissect_epmd_response(pinfo, tvb, 0, epmd_tree);
316 return (tvb_captured_length(tvb));
319 void
320 proto_register_epmd(void)
322 static hf_register_info hf[] = {
323 { &hf_epmd_len,
324 { "Length", "epmd.len",
325 FT_UINT16, BASE_DEC, NULL, 0x0,
326 "Message Length", HFILL }},
328 { &hf_epmd_type,
329 { "Type", "epmd.type",
330 FT_UINT8, BASE_DEC, VALS(message_types), 0x0,
331 "Message Type", HFILL }},
333 { &hf_epmd_result,
334 { "Result", "epmd.result",
335 FT_UINT8, BASE_DEC, NULL, 0x0,
336 NULL, HFILL }},
338 { &hf_epmd_port_no,
339 { "Port No", "epmd.port_no",
340 FT_UINT16, BASE_DEC, NULL, 0x0,
341 NULL, HFILL }},
343 { &hf_epmd_node_type,
344 { "Node Type", "epmd.node_type",
345 FT_UINT8, BASE_DEC, VALS(node_type_vals), 0x0,
346 NULL, HFILL }},
348 { &hf_epmd_protocol,
349 { "Protocol", "epmd.protocol",
350 FT_UINT8, BASE_DEC, VALS(protocol_vals), 0x0,
351 NULL, HFILL }},
353 { &hf_epmd_creation,
354 { "Creation", "epmd.creation",
355 FT_UINT16, BASE_DEC, NULL, 0x0,
356 NULL, HFILL }},
358 { &hf_epmd_dist_high,
359 { "Highest Version", "epmd.dist_high",
360 FT_UINT16, BASE_DEC, VALS(epmd_version_vals), 0x0,
361 NULL, HFILL }},
363 { &hf_epmd_dist_low,
364 { "Lowest Version", "epmd.dist_low",
365 FT_UINT16, BASE_DEC, VALS(epmd_version_vals), 0x0,
366 NULL, HFILL }},
368 { &hf_epmd_name_len,
369 { "Name Length", "epmd.name_len",
370 FT_UINT16, BASE_DEC, NULL, 0x0,
371 NULL, HFILL }},
373 { &hf_epmd_name,
374 { "Node Name", "epmd.name",
375 FT_STRING, BASE_NONE, NULL, 0x0,
376 NULL, HFILL }},
378 { &hf_epmd_elen,
379 { "Elen", "epmd.elen",
380 FT_UINT16, BASE_DEC, NULL, 0x0,
381 "Extra Length", HFILL }},
383 { &hf_epmd_edata,
384 { "Edata", "epmd.edata",
385 FT_BYTES, BASE_NONE, NULL, 0x0,
386 "Extra Data", HFILL }},
388 { &hf_epmd_names,
389 { "Names", "epmd.names",
390 FT_BYTES, BASE_NONE, NULL, 0x0,
391 "List of names", HFILL }}
394 static int *ett[] = {
395 &ett_epmd,
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);
404 void
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
414 * Local variables:
415 * c-basic-offset: 4
416 * tab-width: 8
417 * indent-tabs-mode: nil
418 * End:
420 * vi: set shiftwidth=4 tabstop=8 expandtab:
421 * :indentSize=4:tabSize=8:noTabs=true: