From Greg Morris:
[metze/wireshark/wip.git] / packet-stun.c
1 /* packet-stun.c
2  * Routines for Simple Traversal of UDP Through NAT dissection
3  * Copyright 2003, Shiang-Ming Huang <smhuang@pcs.csie.nctu.edu.tw>
4  *
5  * $Id: packet-stun.c,v 1.2 2003/08/18 01:40:16 gerald Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  * 
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.
15  * 
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.
20  * 
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.
24  *
25  * Please refer to RFC 3489 for protocol detail.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include <glib.h>
37
38 #ifdef NEED_SNPRINTF_H
39 # include "snprintf.h"
40 #endif
41
42 #include <epan/packet.h>
43
44 /* Initialize the protocol and registered fields */
45 static int proto_stun = -1;
46
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;
51
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;
64
65
66
67 /* Message Types */
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
74
75 /* Attribute Types */
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
87
88
89
90 /* Initialize the subtree pointers */
91 static gint ett_stun = -1;
92 static gint ett_stun_att = -1;
93
94
95 #define UDP_PORT_STUN   3478
96 #define TCP_PORT_STUN   3478
97
98
99 #define STUN_HDR_LEN    20      /* STUN message header length */
100 #define ATTR_HDR_LEN    4       /* STUN attribute header length */
101
102
103 static const true_false_string set_flag = {
104         "SET",
105         "NOT SET"
106 };
107
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"},
115         {0x00, NULL}
116 };
117
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"},
129         {0x00, NULL}
130 };
131
132 static const value_string attributes_family[] = {
133         {0x0001, "IPv4"},
134         {0x00, NULL}
135 };
136
137 static void
138 dissect_stun(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
139 {
140
141         proto_item *ti;
142         proto_item *ta;
143         proto_tree *stun_tree;
144         proto_tree *att_tree;
145
146         
147         guint16 message_type;
148
149         
150         guint16 att_type;
151         guint16 att_length;
152         guint16 offset;
153
154
155         if (check_col(pinfo->cinfo, COL_PROTOCOL)) 
156                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "STUN");
157     
158         if (check_col(pinfo->cinfo, COL_INFO)) {
159                 col_clear(pinfo->cinfo, COL_INFO);
160                 
161                 message_type = tvb_get_ntohs(tvb, 0);
162
163                     
164                 col_add_fstr(pinfo->cinfo, COL_INFO, "Message : %s",
165                                 (message_type==BINDING_REQUEST)?"Binding Request":
166                                 (message_type==BINDING_RESPONSE)?"Binding Response":
167                                 (message_type==BINDING_ERROR_RESPONSE)?"Binding Error Response":
168                                 (message_type==SHARED_SECRET_REQUEST)?"Shared Secret Request":
169                                 (message_type==SHARED_SECRET_RESPONSE)?"Shared Secret Response":
170                                 (message_type==SHARED_SECRET_ERROR_RESPONSE)?"Shared Secret Error Response":"UNKNOWN"
171                         );
172                             
173         }
174
175
176         if (tree) {
177
178
179                 ti = proto_tree_add_item(tree, proto_stun, tvb, 0, -1, FALSE);
180                             
181                 stun_tree = proto_item_add_subtree(ti, ett_stun);
182
183
184
185
186                 proto_tree_add_item(stun_tree, hf_stun_type, tvb, 0, 2, FALSE);
187                 proto_tree_add_item(stun_tree, hf_stun_length, tvb, 2, 2, FALSE);
188                 proto_tree_add_item(stun_tree, hf_stun_id, tvb, 4, 16, FALSE);
189
190                 ta = proto_tree_add_item(stun_tree, hf_stun_att, tvb, STUN_HDR_LEN, -1, FALSE);
191                 att_tree = proto_item_add_subtree(ta, ett_stun_att);
192
193                 offset = STUN_HDR_LEN;
194
195                 while(1){
196                         if( !tvb_bytes_exist(tvb, offset, ATTR_HDR_LEN) ) /* no data anymore */
197                             break;
198                             
199                         att_type = tvb_get_ntohs(tvb, offset); /* Type field in attribute header */
200                         att_length = tvb_get_ntohs(tvb, offset+2); /* Length field in attribute header */
201                         
202                         
203                         switch( att_type ){
204                                 case MAPPED_ADDRESS:
205                                 case RESPONSE_ADDRESS:
206                                 case SOURCE_ADDRESS:
207                                 case CHANGED_ADDRESS:
208                                 case REFLECTED_FROM:
209                                         proto_tree_add_item(att_tree, stun_att_type, tvb, offset, 2, FALSE);
210                                         proto_tree_add_item(att_tree, stun_att_length, tvb, offset+2, 2, FALSE);
211                                         proto_tree_add_item(att_tree, stun_att_family, tvb, offset+5, 1, FALSE);
212                                         proto_tree_add_item(att_tree, stun_att_port, tvb, offset+6, 2, FALSE);
213                                         proto_tree_add_item(att_tree, stun_att_ip, tvb, offset+8, 4, FALSE);
214
215                                         offset = offset+(ATTR_HDR_LEN+att_length);
216                                         
217                                         break;
218                                         
219                                 case CHANGE_REQUEST:
220                                         proto_tree_add_item(att_tree, stun_att_type, tvb, offset, 2, FALSE);
221                                         proto_tree_add_item(att_tree, stun_att_length, tvb, offset+2, 2, FALSE);
222                                         proto_tree_add_item(att_tree, stun_att_change_ip, tvb, offset+4, 4, FALSE);
223                                         proto_tree_add_item(att_tree, stun_att_change_port, tvb, offset+4, 4, FALSE);
224
225                                         offset = offset+(ATTR_HDR_LEN+att_length);
226                                         
227                                         break;                                  
228                                         
229                                 case USERNAME:
230                                 case PASSWORD:
231                                 case MESSAGE_INTEGRITY:
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_length, tvb, offset+2, att_length, FALSE);
235                                         
236                                         offset = offset+(ATTR_HDR_LEN+att_length);
237                                         
238                                         break;
239                                         
240                                 case ERROR_CODE:
241                                         proto_tree_add_item(att_tree, stun_att_type, tvb, offset, 2, FALSE);
242                                         proto_tree_add_item(att_tree, stun_att_length, tvb, offset+2, 2, FALSE);
243                                         
244                                         proto_tree_add_item(att_tree, stun_att_error_class, tvb, offset+6, 1, FALSE);
245                                         proto_tree_add_item(att_tree, stun_att_error_number, tvb, offset+7, 1, FALSE);
246                                         proto_tree_add_item(att_tree, stun_att_error_reason, tvb, offset+8, (att_length-4), FALSE);
247                                         
248                                         offset = offset+(ATTR_HDR_LEN+att_length);
249                                         
250                                         break;                          
251                                 
252                                 
253                                 case UNKNOWN_ATTRIBUTES:
254                                         proto_tree_add_item(att_tree, stun_att_type, tvb, offset, 2, FALSE);
255                                         proto_tree_add_item(att_tree, stun_att_length, tvb, offset+2, 2, FALSE);
256
257                                         offset = offset + ATTR_HDR_LEN;
258                                         while(tvb_bytes_exist(tvb, offset, 4)){ /* UNKNOWN-ATTRIBUTES is 4 bytes aligned */
259                                                 proto_tree_add_item(att_tree, stun_att_unknown, tvb, offset, 2, FALSE);
260                                                 proto_tree_add_item(att_tree, stun_att_unknown, tvb, offset+2, 2, FALSE);
261                                                 offset = offset + 4;
262                                         }
263                                                         
264                                         break;
265                                         
266                                 default:
267                                         return;
268                                 
269                         }
270                         
271                 }
272         }
273 }
274
275
276 static gboolean
277 dissect_stun_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
278 {
279         guint16 type, length;
280
281         /*
282          * This is a heuristic dissector, which means we get all the
283          * UDP and TCP traffic not sent to a known dissector and not
284          * claimed by a heuristic dissector called before us!
285          * So we first check if the frame is really meant for us.
286          */
287
288         /* First, make sure we have enough data to do the check. */
289         if (!tvb_bytes_exist(tvb, 0, STUN_HDR_LEN))
290                 return FALSE;
291         
292         type = tvb_get_ntohs(tvb, 0);
293         
294         /* check if message type is correct */
295         if(     (type != BINDING_REQUEST) &&
296                 (type != BINDING_RESPONSE) &&
297                 (type != BINDING_ERROR_RESPONSE) &&
298                 (type != SHARED_SECRET_REQUEST) &&
299                 (type != SHARED_SECRET_RESPONSE) &&
300                 (type != SHARED_SECRET_ERROR_RESPONSE)
301           )
302                 return FALSE;
303         
304         
305         length = tvb_get_ntohs(tvb, 2);
306         
307         /* check if payload enough */
308         if (!tvb_bytes_exist(tvb, 0, STUN_HDR_LEN+length))
309                 return FALSE;
310
311         if(tvb_bytes_exist(tvb, 0, STUN_HDR_LEN+length+1))
312                 return FALSE;
313
314         
315         /* The message seems to be a valid STUN message! */
316         dissect_stun(tvb, pinfo, tree);
317
318         return TRUE;
319 }
320
321
322
323
324 void
325 proto_register_stun(void)
326 {                 
327
328         static hf_register_info hf[] = {
329                 { &hf_stun_type,
330                         { "Message Type",       "stun.type",    FT_UINT16, 
331                         BASE_HEX,       VALS(messages), 0x0,    "",     HFILL }
332                 },
333                 { &hf_stun_length,
334                         { "Message Length",     "stun.length",  FT_UINT16, 
335                         BASE_HEX,       NULL,   0x0,    "",     HFILL }
336                 },
337                 { &hf_stun_id,
338                         { "Message Transaction ID",     "stun.id",      FT_BYTES,
339                         BASE_HEX,       NULL,   0x0,    "",     HFILL }
340                 },
341                 { &hf_stun_att,
342                         { "Attributes",         "stun.att",     FT_NONE,
343                         0,              NULL,   0x0,    "",     HFILL }
344                 },
345                 /* ////////////////////////////////////// */
346                 { &stun_att_type,
347                         { "Attribute Type",     "stun.att.type",        FT_UINT16,
348                         BASE_HEX,       VALS(attributes),       0x0,    "",     HFILL }
349                 },
350                 { &stun_att_length,
351                         { "Attribute Length",   "stun.att.length",      FT_UINT16,
352                         BASE_DEC,       NULL,   0x0,    "",     HFILL }
353                 },
354                 { &stun_att_value,
355                         { "Value",      "stun.att.value",       FT_BYTES,
356                         BASE_HEX,       NULL,   0x0,    "",     HFILL }
357                 },
358                 { &stun_att_family,
359                         { "Protocol Family",    "stun.att.family",      FT_UINT16,
360                         BASE_HEX,       VALS(attributes_family),        0x0,    "",     HFILL }
361                 },
362                 { &stun_att_ip,
363                         { "IP",         "stun.att.ip",  FT_IPv4,
364                         BASE_NONE,      NULL,   0x0,    "",     HFILL }
365                 },
366                 { &stun_att_port,
367                         { "Port",       "stun.att.port",        FT_UINT16,
368                         BASE_DEC,       NULL,   0x0,    "",     HFILL }
369                 },
370                 { &stun_att_change_ip,
371                         { "Change IP","stun.att.change.ip",     FT_BOOLEAN,
372                         16,     TFS(&set_flag), 0x0004, "",     HFILL}
373                 },
374                 { &stun_att_change_port,
375                         { "Change Port","stun.att.change.port", FT_BOOLEAN,
376                         16,     TFS(&set_flag), 0x0002, "",     HFILL}
377                 },              
378                 { &stun_att_unknown,
379                         { "Unknown Attribute","stun.att.unknown",       FT_UINT16,
380                         BASE_HEX,       NULL,   0x0,    "",     HFILL}
381                 },
382                 { &stun_att_error_class,
383                         { "Error Class","stun.att.error.class", FT_UINT8,
384                         BASE_DEC,       NULL,   0x07,   "",     HFILL}
385                 },
386                 { &stun_att_error_number,
387                         { "Error Code","stun.att.error",        FT_UINT8,
388                         BASE_DEC,       NULL,   0x0,    "",     HFILL}
389                 },
390                 { &stun_att_error_reason,
391                         { "Error Reason Phase","stun.att.error.reason", FT_STRING,
392                         BASE_NONE,      NULL,   0x0,    "",     HFILL}
393                 },
394         };
395
396 /* Setup protocol subtree array */
397         static gint *ett[] = {
398                 &ett_stun,
399                 &ett_stun_att,
400         };
401
402 /* Register the protocol name and description */
403         proto_stun = proto_register_protocol("Simple Traversal of UDP Through NAT",
404             "STUN", "stun");
405
406 /* Required function calls to register the header fields and subtrees used */
407         proto_register_field_array(proto_stun, hf, array_length(hf));
408         proto_register_subtree_array(ett, array_length(ett));
409 }
410
411
412 void
413 proto_reg_handoff_stun(void)
414 {
415         dissector_handle_t stun_handle;
416
417         stun_handle = create_dissector_handle(dissect_stun, proto_stun);
418         dissector_add("tcp.port", TCP_PORT_STUN, stun_handle);
419         dissector_add("udp.port", UDP_PORT_STUN, stun_handle);
420
421         heur_dissector_add( "udp", dissect_stun_heur, proto_stun );
422         heur_dissector_add( "tcp", dissect_stun_heur, proto_stun );
423 }