2 * Routines for Simple Traversal of UDP Through NAT dissection
3 * Copyright 2003, Shiang-Ming Huang <smhuang@pcs.csie.nctu.edu.tw>
5 * $Id: packet-stun.c,v 1.3 2003/09/05 04:39:19 guy Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@ethereal.com>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 * Please refer to RFC 3489 for protocol detail.
38 #ifdef NEED_SNPRINTF_H
39 # include "snprintf.h"
42 #include <epan/packet.h>
44 /* Initialize the protocol and registered fields */
45 static int proto_stun = -1;
47 static int hf_stun_type = -1; /* STUN message header */
48 static int hf_stun_length = -1;
49 static int hf_stun_id = -1;
50 static int hf_stun_att = -1;
52 static int stun_att_type = -1; /* STUN attribute fields */
53 static int stun_att_length = -1;
54 static int stun_att_value = -1;
55 static int stun_att_family = -1;
56 static int stun_att_ip = -1;
57 static int stun_att_port = -1;
58 static int stun_att_change_ip = -1;
59 static int stun_att_change_port = -1;
60 static int stun_att_unknown = -1;
61 static int stun_att_error_class = -1;
62 static int stun_att_error_number = -1;
63 static int stun_att_error_reason = -1;
68 #define BINDING_REQUEST 0x0001
69 #define BINDING_RESPONSE 0x0101
70 #define BINDING_ERROR_RESPONSE 0x0111
71 #define SHARED_SECRET_REQUEST 0x0002
72 #define SHARED_SECRET_RESPONSE 0x0102
73 #define SHARED_SECRET_ERROR_RESPONSE 0x1112
76 #define MAPPED_ADDRESS 0x0001
77 #define RESPONSE_ADDRESS 0x0002
78 #define CHANGE_REQUEST 0x0003
79 #define SOURCE_ADDRESS 0x0004
80 #define CHANGED_ADDRESS 0x0005
81 #define USERNAME 0x0006
82 #define PASSWORD 0x0007
83 #define MESSAGE_INTEGRITY 0x0008
84 #define ERROR_CODE 0x0009
85 #define UNKNOWN_ATTRIBUTES 0x000a
86 #define REFLECTED_FROM 0x000b
90 /* Initialize the subtree pointers */
91 static gint ett_stun = -1;
92 static gint ett_stun_att = -1;
95 #define UDP_PORT_STUN 3478
96 #define TCP_PORT_STUN 3478
99 #define STUN_HDR_LEN 20 /* STUN message header length */
100 #define ATTR_HDR_LEN 4 /* STUN attribute header length */
103 static const true_false_string set_flag = {
108 static const value_string messages[] = {
109 {BINDING_REQUEST, "Binding Request"},
110 {BINDING_RESPONSE, "Binding Response"},
111 {BINDING_ERROR_RESPONSE, "Binding Error Response"},
112 {SHARED_SECRET_REQUEST, "Shared Secret Request"},
113 {SHARED_SECRET_RESPONSE, "Shared Secret Response"},
114 {SHARED_SECRET_ERROR_RESPONSE, "Shared Secret Error Response"},
118 static const value_string attributes[] = {
119 {MAPPED_ADDRESS, "MAPPED-ADDRESS"},
120 {RESPONSE_ADDRESS, "RESPONSE-ADDRESS"},
121 {CHANGE_REQUEST, "CHANGE-REQUEST"},
122 {SOURCE_ADDRESS, "SOURCE-ADDRESS"},
123 {CHANGED_ADDRESS, "CHANGED-ADDRESS"},
124 {USERNAME, "USERNAME"},
125 {PASSWORD, "PASSWORD"},
126 {MESSAGE_INTEGRITY, "MESSAGE-INTEGRITY"},
127 {ERROR_CODE, "ERROR-CODE"},
128 {REFLECTED_FROM, "REFLECTED-FROM"},
132 static const value_string attributes_family[] = {
138 dissect_stun(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
143 proto_tree *stun_tree;
144 proto_tree *att_tree;
150 * First check if the frame is really meant for us.
153 /* First, make sure we have enough data to do the check. */
154 if (!tvb_bytes_exist(tvb, 0, STUN_HDR_LEN))
157 att_type = tvb_get_ntohs(tvb, 0);
159 /* check if message type is correct */
160 if( (att_type != BINDING_REQUEST) &&
161 (att_type != BINDING_RESPONSE) &&
162 (att_type != BINDING_ERROR_RESPONSE) &&
163 (att_type != SHARED_SECRET_REQUEST) &&
164 (att_type != SHARED_SECRET_RESPONSE) &&
165 (att_type != SHARED_SECRET_ERROR_RESPONSE)
170 att_length = tvb_get_ntohs(tvb, 2);
172 /* check if payload enough */
173 if (!tvb_bytes_exist(tvb, 0, STUN_HDR_LEN+att_length))
176 if(tvb_bytes_exist(tvb, 0, STUN_HDR_LEN+att_length+1))
179 /* The message seems to be a valid STUN message! */
181 if (check_col(pinfo->cinfo, COL_PROTOCOL))
182 col_set_str(pinfo->cinfo, COL_PROTOCOL, "STUN");
184 if (check_col(pinfo->cinfo, COL_INFO)) {
185 col_clear(pinfo->cinfo, COL_INFO);
187 col_add_fstr(pinfo->cinfo, COL_INFO, "Message : %s",
188 (att_type==BINDING_REQUEST)?"Binding Request":
189 (att_type==BINDING_RESPONSE)?"Binding Response":
190 (att_type==BINDING_ERROR_RESPONSE)?"Binding Error Response":
191 (att_type==SHARED_SECRET_REQUEST)?"Shared Secret Request":
192 (att_type==SHARED_SECRET_RESPONSE)?"Shared Secret Response":
193 (att_type==SHARED_SECRET_ERROR_RESPONSE)?"Shared Secret Error Response":"UNKNOWN"
202 ti = proto_tree_add_item(tree, proto_stun, tvb, 0, -1, FALSE);
204 stun_tree = proto_item_add_subtree(ti, ett_stun);
209 proto_tree_add_item(stun_tree, hf_stun_type, tvb, 0, 2, FALSE);
210 proto_tree_add_item(stun_tree, hf_stun_length, tvb, 2, 2, FALSE);
211 proto_tree_add_item(stun_tree, hf_stun_id, tvb, 4, 16, FALSE);
213 ta = proto_tree_add_item(stun_tree, hf_stun_att, tvb, STUN_HDR_LEN, -1, FALSE);
214 att_tree = proto_item_add_subtree(ta, ett_stun_att);
216 offset = STUN_HDR_LEN;
219 if( !tvb_bytes_exist(tvb, offset, ATTR_HDR_LEN) ) /* no data anymore */
222 att_type = tvb_get_ntohs(tvb, offset); /* Type field in attribute header */
223 att_length = tvb_get_ntohs(tvb, offset+2); /* Length field in attribute header */
228 case RESPONSE_ADDRESS:
230 case CHANGED_ADDRESS:
232 proto_tree_add_item(att_tree, stun_att_type, tvb, offset, 2, FALSE);
233 proto_tree_add_item(att_tree, stun_att_length, tvb, offset+2, 2, FALSE);
234 proto_tree_add_item(att_tree, stun_att_family, tvb, offset+5, 1, FALSE);
235 proto_tree_add_item(att_tree, stun_att_port, tvb, offset+6, 2, FALSE);
236 proto_tree_add_item(att_tree, stun_att_ip, tvb, offset+8, 4, FALSE);
238 offset = offset+(ATTR_HDR_LEN+att_length);
243 proto_tree_add_item(att_tree, stun_att_type, tvb, offset, 2, FALSE);
244 proto_tree_add_item(att_tree, stun_att_length, tvb, offset+2, 2, FALSE);
245 proto_tree_add_item(att_tree, stun_att_change_ip, tvb, offset+4, 4, FALSE);
246 proto_tree_add_item(att_tree, stun_att_change_port, tvb, offset+4, 4, FALSE);
248 offset = offset+(ATTR_HDR_LEN+att_length);
254 case MESSAGE_INTEGRITY:
255 proto_tree_add_item(att_tree, stun_att_type, tvb, offset, 2, FALSE);
256 proto_tree_add_item(att_tree, stun_att_length, tvb, offset+2, 2, FALSE);
257 proto_tree_add_item(att_tree, stun_att_length, tvb, offset+2, att_length, FALSE);
259 offset = offset+(ATTR_HDR_LEN+att_length);
264 proto_tree_add_item(att_tree, stun_att_type, tvb, offset, 2, FALSE);
265 proto_tree_add_item(att_tree, stun_att_length, tvb, offset+2, 2, FALSE);
267 proto_tree_add_item(att_tree, stun_att_error_class, tvb, offset+6, 1, FALSE);
268 proto_tree_add_item(att_tree, stun_att_error_number, tvb, offset+7, 1, FALSE);
269 proto_tree_add_item(att_tree, stun_att_error_reason, tvb, offset+8, (att_length-4), FALSE);
271 offset = offset+(ATTR_HDR_LEN+att_length);
276 case UNKNOWN_ATTRIBUTES:
277 proto_tree_add_item(att_tree, stun_att_type, tvb, offset, 2, FALSE);
278 proto_tree_add_item(att_tree, stun_att_length, tvb, offset+2, 2, FALSE);
280 offset = offset + ATTR_HDR_LEN;
281 while(tvb_bytes_exist(tvb, offset, 4)){ /* UNKNOWN-ATTRIBUTES is 4 bytes aligned */
282 proto_tree_add_item(att_tree, stun_att_unknown, tvb, offset, 2, FALSE);
283 proto_tree_add_item(att_tree, stun_att_unknown, tvb, offset+2, 2, FALSE);
290 return tvb_length(tvb);
296 return tvb_length(tvb);
301 dissect_stun_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
303 if (dissect_stun(tvb, pinfo, tree) == 0)
313 proto_register_stun(void)
315 static hf_register_info hf[] = {
317 { "Message Type", "stun.type", FT_UINT16,
318 BASE_HEX, VALS(messages), 0x0, "", HFILL }
321 { "Message Length", "stun.length", FT_UINT16,
322 BASE_HEX, NULL, 0x0, "", HFILL }
325 { "Message Transaction ID", "stun.id", FT_BYTES,
326 BASE_HEX, NULL, 0x0, "", HFILL }
329 { "Attributes", "stun.att", FT_NONE,
330 0, NULL, 0x0, "", HFILL }
332 /* ////////////////////////////////////// */
334 { "Attribute Type", "stun.att.type", FT_UINT16,
335 BASE_HEX, VALS(attributes), 0x0, "", HFILL }
338 { "Attribute Length", "stun.att.length", FT_UINT16,
339 BASE_DEC, NULL, 0x0, "", HFILL }
342 { "Value", "stun.att.value", FT_BYTES,
343 BASE_HEX, NULL, 0x0, "", HFILL }
346 { "Protocol Family", "stun.att.family", FT_UINT16,
347 BASE_HEX, VALS(attributes_family), 0x0, "", HFILL }
350 { "IP", "stun.att.ip", FT_IPv4,
351 BASE_NONE, NULL, 0x0, "", HFILL }
354 { "Port", "stun.att.port", FT_UINT16,
355 BASE_DEC, NULL, 0x0, "", HFILL }
357 { &stun_att_change_ip,
358 { "Change IP","stun.att.change.ip", FT_BOOLEAN,
359 16, TFS(&set_flag), 0x0004, "", HFILL}
361 { &stun_att_change_port,
362 { "Change Port","stun.att.change.port", FT_BOOLEAN,
363 16, TFS(&set_flag), 0x0002, "", HFILL}
366 { "Unknown Attribute","stun.att.unknown", FT_UINT16,
367 BASE_HEX, NULL, 0x0, "", HFILL}
369 { &stun_att_error_class,
370 { "Error Class","stun.att.error.class", FT_UINT8,
371 BASE_DEC, NULL, 0x07, "", HFILL}
373 { &stun_att_error_number,
374 { "Error Code","stun.att.error", FT_UINT8,
375 BASE_DEC, NULL, 0x0, "", HFILL}
377 { &stun_att_error_reason,
378 { "Error Reason Phase","stun.att.error.reason", FT_STRING,
379 BASE_NONE, NULL, 0x0, "", HFILL}
383 /* Setup protocol subtree array */
384 static gint *ett[] = {
389 /* Register the protocol name and description */
390 proto_stun = proto_register_protocol("Simple Traversal of UDP Through NAT",
393 /* Required function calls to register the header fields and subtrees used */
394 proto_register_field_array(proto_stun, hf, array_length(hf));
395 proto_register_subtree_array(ett, array_length(ett));
400 proto_reg_handoff_stun(void)
402 dissector_handle_t stun_handle;
404 stun_handle = new_create_dissector_handle(dissect_stun, proto_stun);
405 dissector_add("tcp.port", TCP_PORT_STUN, stun_handle);
406 dissector_add("udp.port", UDP_PORT_STUN, stun_handle);
408 heur_dissector_add("udp", dissect_stun_heur, proto_stun);
409 heur_dissector_add("tcp", dissect_stun_heur, proto_stun);