Revert "Fixup: tvb_* -> tvb_captured"
[metze/wireshark/wip.git] / epan / dissectors / packet-radius.c
1 /* packet-radius.c
2  *
3  * Routines for RADIUS packet disassembly
4  * Copyright 1999 Johan Feyaerts
5  * Changed 03/12/2003 Rui Carmo (http://the.taoofmac.com - added all 3GPP VSAs, some parsing)
6  * Changed 07/2005 Luis Ontanon <luis@ontanon.org> - use FreeRADIUS' dictionary
7  * Changed 10/2006 Alejandro Vaquero <alejandrovaquero@yahoo.com> - add Conversations support
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26  *
27  * References:
28  *
29  * RFC 2865 - Remote Authentication Dial In User Service (RADIUS)
30  * RFC 2866 - RADIUS Accounting
31  * RFC 2867 - RADIUS Accounting Modifications for Tunnel Protocol Support
32  * RFC 2868 - RADIUS Attributes for Tunnel Protocol Support
33  * RFC 2869 - RADIUS Extensions
34  * RFC 3162 - RADIUS and IPv6
35  * RFC 3576 - Dynamic Authorization Extensions to RADIUS
36  *
37  * See also
38  *
39  *      http://www.iana.org/assignments/radius-types
40  *
41  * and see
42  *
43  *      http://freeradius.org/radiusd/man/dictionary.html
44  *
45  * for the dictionary file syntax.
46  */
47
48
49 /*
50   TO (re)DO: (see svn rev 14786)
51     - dissect_3gpp_ipv6_dns_servers()
52  */
53
54 #include "config.h"
55
56 #include <stdlib.h>
57 #include <string.h>
58 #include <ctype.h>
59 #include <errno.h>
60
61 #include <glib.h>
62
63 #include <wsutil/report_err.h>
64 #include <wsutil/md5.h>
65
66 #include <epan/packet.h>
67 #include <epan/exceptions.h>
68 #include <epan/prefs.h>
69 #include <epan/sminmpec.h>
70 #include <wsutil/filesystem.h>
71 #include <epan/conversation.h>
72 #include <epan/tap.h>
73 #include <epan/addr_resolv.h>
74 #include <epan/garrayfix.h>
75 #include <epan/wmem/wmem.h>
76
77 #include "packet-radius.h"
78
79 void proto_register_radius(void);
80 void proto_reg_handoff_radius(void);
81
82 typedef struct _e_radiushdr {
83         guint8 rh_code;
84         guint8 rh_ident;
85         guint16 rh_pktlength;
86 } e_radiushdr;
87
88 typedef struct {
89         wmem_array_t* hf;
90         wmem_array_t* ett;
91         wmem_array_t* vend_vs;
92 } hfett_t;
93
94 #define AUTHENTICATOR_LENGTH    16
95 #define RD_HDR_LENGTH           4
96 #define HDR_LENGTH              (RD_HDR_LENGTH + AUTHENTICATOR_LENGTH)
97
98 #define UDP_PORT_RADIUS         1645
99 #define UDP_PORT_RADIUS_NEW     1812
100 #define UDP_PORT_RADACCT        1646
101 #define UDP_PORT_RADACCT_NEW    1813
102 #define UDP_PORT_DAE_OLD        1700 /* DAE: pre RFC */
103 #define UDP_PORT_DAE            3799 /* DAE: rfc3576 */
104
105 static radius_dictionary_t* dict = NULL;
106
107 static int proto_radius = -1;
108
109 static int hf_radius_req = -1;
110 static int hf_radius_rsp = -1;
111 static int hf_radius_req_frame = -1;
112 static int hf_radius_rsp_frame = -1;
113 static int hf_radius_time = -1;
114
115 static int hf_radius_dup = -1;
116 static int hf_radius_req_dup = -1;
117 static int hf_radius_rsp_dup = -1;
118
119 static int hf_radius_id = -1;
120 static int hf_radius_code = -1;
121 static int hf_radius_length = -1;
122 static int hf_radius_authenticator = -1;
123
124 static int hf_radius_chap_password = -1;
125 static int hf_radius_chap_ident = -1;
126 static int hf_radius_chap_string = -1;
127 static int hf_radius_framed_ip_address = -1;
128
129 static int hf_radius_login_ip_host = -1;
130 static int hf_radius_framed_ipx_network = -1;
131
132 static int hf_radius_cosine_vpi = -1;
133 static int hf_radius_cosine_vci = -1;
134
135 static int hf_radius_ascend_data_filter = -1;
136
137 static gint ett_radius = -1;
138 static gint ett_radius_avp = -1;
139 static gint ett_eap = -1;
140 static gint ett_chap = -1;
141 /*
142  * Define the tap for radius
143  */
144 static int radius_tap = -1;
145
146 static radius_vendor_info_t no_vendor = {"Unknown Vendor",0,NULL,-1,1,1,FALSE};
147
148 static radius_attr_info_t no_dictionary_entry = {"Unknown-Attribute",0,FALSE,FALSE,radius_octets, NULL, NULL, -1, -1, -1, -1, -1, NULL };
149
150 static dissector_handle_t eap_handle;
151
152 static const gchar* shared_secret = "";
153 static gboolean show_length = FALSE;
154 static guint alt_port_pref = 0;
155 static guint request_ttl = 5;
156
157 static guint8 authenticator[AUTHENTICATOR_LENGTH];
158
159 /* http://www.iana.org/assignments/radius-types */
160 static const value_string radius_pkt_type_codes[] =
161 {
162         {RADIUS_PKT_TYPE_ACCESS_REQUEST,                        "Access-Request"},                      /*  1 RFC2865 */
163         {RADIUS_PKT_TYPE_ACCESS_ACCEPT,                         "Access-Accept"},                       /*  2 RFC2865 */
164         {RADIUS_PKT_TYPE_ACCESS_REJECT,                         "Access-Reject"},                       /*  3 RFC2865 */
165         {RADIUS_PKT_TYPE_ACCOUNTING_REQUEST,                    "Accounting-Request"},                  /*  4 RFC2865 */
166         {RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE,                   "Accounting-Response"},                 /*  5 RFC2865 */
167         {RADIUS_PKT_TYPE_ACCOUNTING_STATUS,                     "Accounting-Status"},                   /*  6 RFC3575 */
168         {RADIUS_PKT_TYPE_PASSWORD_REQUEST,                      "Password-Request"},                    /*  7 RFC3575 */
169         {RADIUS_PKT_TYPE_PASSWORD_ACK,                          "Password-Ack"},                        /*  8 RFC3575 */
170         {RADIUS_PKT_TYPE_PASSWORD_REJECT,                       "Password-Reject"},                     /*  9 RFC3575 */
171         {RADIUS_PKT_TYPE_ACCOUNTING_MESSAGE,                    "Accounting-Message"},                  /* 10 RFC3575 */
172         {RADIUS_PKT_TYPE_ACCESS_CHALLENGE,                      "Access-Challenge"},                    /* 11 RFC2865 */
173         {RADIUS_PKT_TYPE_STATUS_SERVER,                         "Status-Server"},                       /* 12 RFC2865 */
174         {RADIUS_PKT_TYPE_STATUS_CLIENT,                         "Status-Client"},                       /* 13 RFC2865 */
175
176         {RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST,                 "Resource-Free-Request"},               /* 21 RFC3575 */
177         {RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE,                "Resource-Free-Response"},              /* 22 RFC3575 */
178         {RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST,                "Resource-Query-Request"},              /* 23 RFC3575 */
179         {RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE,               "Query_Response"},                      /* 24 RFC3575 */
180         {RADIUS_PKT_TYPE_ALTERNATE_RESOURCE_RECLAIM_REQUEST,    "Alternate-Resource-Reclaim-Request"},  /* 25 RFC3575 */
181         {RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST,                    "NAS-Reboot-Request"},                  /* 26 RFC3575 */
182         {RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE,                   "NAS-Reboot-Response"},                 /* 27 RFC3575 */
183
184         {RADIUS_PKT_TYPE_NEXT_PASSCODE,                         "Next-Passcode"},                       /* 29 RFC3575 */
185         {RADIUS_PKT_TYPE_NEW_PIN,                               "New-Pin"},                             /* 30 RFC3575 */
186         {RADIUS_PKT_TYPE_TERMINATE_SESSION,                     "Terminate-Session"},                   /* 31 RFC3575 */
187         {RADIUS_PKT_TYPE_PASSWORD_EXPIRED,                      "Password-Expired"},                    /* 32 RFC3575 */
188         {RADIUS_PKT_TYPE_EVENT_REQUEST,                         "Event-Request"},                       /* 33 RFC3575 */
189         {RADIUS_PKT_TYPE_EVENT_RESPONSE,                        "Event-Response"},                      /* 34 RFC3575|RFC5176 */
190
191         {RADIUS_PKT_TYPE_DISCONNECT_REQUEST,                    "Disconnect-Request"},                  /* 40 RFC3575|RFC5176 */
192         {RADIUS_PKT_TYPE_DISCONNECT_ACK,                        "Disconnect-ACK"},                      /* 41 RFC3575|RFC5176 */
193         {RADIUS_PKT_TYPE_DISCONNECT_NAK,                        "Disconnect-NAK"},                      /* 42 RFC3575|RFC5176 */
194         {RADIUS_PKT_TYPE_COA_REQUEST,                           "CoA-Request"},                         /* 43 RFC3575|RFC5176 */
195         {RADIUS_PKT_TYPE_COA_ACK,                               "CoA-ACK"},                             /* 44 RFC3575|RFC5176 */
196         {RADIUS_PKT_TYPE_COA_NAK,                               "CoA-NAK"},                             /* 45 RFC3575|RFC5176 */
197
198         {RADIUS_PKT_TYPE_IP_ADDRESS_ALLOCATE,                   "IP-Address-Allocate"},                 /* 50 RFC3575 */
199         {RADIUS_PKT_TYPE_IP_ADDRESS_RELEASE,                    "IP-Address-Release"},                  /* 51 RFC3575 */
200
201         {RADIUS_PKT_TYPE_ALU_STATE_REQUEST,                     "ALU-State-Request"},                   /* 129 ALU AAA */
202         {RADIUS_PKT_TYPE_ALU_STATE_ACCEPT,                      "ALU-State-Accept"},                    /* 130 ALU AAA */
203         {RADIUS_PKT_TYPE_ALU_STATE_REJECT,                      "ALU-State-Reject"},                    /* 131 ALU AAA */
204         {RADIUS_PKT_TYPE_ALU_STATE_ERROR,                       "ALU-State-Error"},                             /* 132 ALU AAA */
205 /*
206 250-253  Experimental Use             [RFC3575]
207 254-255  Reserved                     [RFC3575]
208 */
209         {0, NULL}
210 };
211 static value_string_ext radius_pkt_type_codes_ext = VALUE_STRING_EXT_INIT(radius_pkt_type_codes);
212
213 /*
214  * Init Hash table stuff for conversation
215  */
216
217 typedef struct _radius_call_info_key
218 {
219         guint code;
220         guint ident;
221         conversation_t *conversation;
222         nstime_t req_time;
223 } radius_call_info_key;
224
225 static GHashTable *radius_calls;
226
227 typedef struct _radius_vsa_buffer_key
228 {
229         guint32 vendor_id;
230         guint32 vsa_type;
231 } radius_vsa_buffer_key;
232
233 typedef struct _radius_vsa_buffer
234 {
235         radius_vsa_buffer_key key;
236         guint8* data;
237         guint seg_num;
238         guint len;
239 } radius_vsa_buffer;
240
241 static gint radius_vsa_equal(gconstpointer k1, gconstpointer k2)
242 {
243         const radius_vsa_buffer_key* key1 = (const radius_vsa_buffer_key*) k1;
244         const radius_vsa_buffer_key* key2 = (const radius_vsa_buffer_key*) k2;
245
246         return (((key1->vendor_id == key2->vendor_id) &&
247                 (key1->vsa_type == key2->vsa_type)
248                 ) ? TRUE : FALSE);
249 }
250
251 static guint radius_vsa_hash(gconstpointer k)
252 {
253         const radius_vsa_buffer_key* key = (const radius_vsa_buffer_key*) k;
254
255         return key->vendor_id + key->vsa_type;
256 }
257
258 /* Compare 2 keys */
259 static gboolean radius_call_equal(gconstpointer k1, gconstpointer k2)
260 {
261         const radius_call_info_key* key1 = (const radius_call_info_key*) k1;
262         const radius_call_info_key* key2 = (const radius_call_info_key*) k2;
263
264         if (key1->ident == key2->ident && key1->conversation == key2->conversation) {
265                 nstime_t delta;
266
267                 nstime_delta(&delta, &key1->req_time, &key2->req_time);
268                 if (abs( (int) nstime_to_sec(&delta)) > (double) request_ttl) return 0;
269
270                 if (key1->code == key2->code)
271                         return TRUE;
272                 /* check the request and response are of the same code type */
273                 if ((key1->code == RADIUS_PKT_TYPE_ACCESS_REQUEST) &&
274                     ((key2->code == RADIUS_PKT_TYPE_ACCESS_ACCEPT) ||
275                      (key2->code == RADIUS_PKT_TYPE_ACCESS_REJECT) ||
276                      (key2->code == RADIUS_PKT_TYPE_ACCESS_CHALLENGE)))
277                         return TRUE;
278
279                 if ((key1->code == RADIUS_PKT_TYPE_ACCOUNTING_REQUEST) &&
280                     (key2->code == RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE))
281                         return TRUE;
282
283                 if ((key1->code == RADIUS_PKT_TYPE_PASSWORD_REQUEST) &&
284                     ((key2->code == RADIUS_PKT_TYPE_PASSWORD_ACK) || (key2->code == RADIUS_PKT_TYPE_PASSWORD_REJECT)))
285                         return TRUE;
286
287                 if ((key1->code == RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST) &&
288                     (key2->code == RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE))
289                         return TRUE;
290
291                 if ((key1->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST) &&
292                     (key2->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE))
293                         return TRUE;
294
295                 if ((key1->code == RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST) &&
296                     (key2->code == RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE))
297                         return TRUE;
298
299                 if ((key1->code == RADIUS_PKT_TYPE_EVENT_REQUEST) &&
300                     (key2->code == RADIUS_PKT_TYPE_EVENT_RESPONSE))
301                         return TRUE;
302
303                 if ((key1->code == RADIUS_PKT_TYPE_DISCONNECT_REQUEST) &&
304                     ((key2->code == RADIUS_PKT_TYPE_DISCONNECT_ACK) || (key2->code == RADIUS_PKT_TYPE_DISCONNECT_NAK)))
305                         return TRUE;
306
307                 if ((key1->code == RADIUS_PKT_TYPE_COA_REQUEST) &&
308                     ((key2->code == RADIUS_PKT_TYPE_COA_ACK) || (key2->code == RADIUS_PKT_TYPE_COA_NAK)))
309                         return TRUE;
310
311                 if ((key1->code == RADIUS_PKT_TYPE_ALU_STATE_REQUEST) &&
312                     ((key2->code == RADIUS_PKT_TYPE_ALU_STATE_ACCEPT) ||
313                      (key2->code == RADIUS_PKT_TYPE_ALU_STATE_REJECT) ||
314                      (key2->code == RADIUS_PKT_TYPE_ALU_STATE_ERROR)))
315                         return TRUE;
316         }
317         return FALSE;
318 }
319
320 /* Calculate a hash key */
321 static guint radius_call_hash(gconstpointer k)
322 {
323         const radius_call_info_key* key = (const radius_call_info_key*) k;
324
325         return key->ident + /*key->code + */ key->conversation->index;
326 }
327
328
329 static const gchar *dissect_chap_password(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
330         int len;
331         proto_item *ti;
332         proto_tree *chap_tree;
333
334         len = tvb_length(tvb);
335         if (len != 17)
336                 return "[wrong length for CHAP-Password]";
337
338         ti = proto_tree_add_item(tree, hf_radius_chap_password, tvb, 0, len, ENC_NA);
339                 chap_tree = proto_item_add_subtree(ti, ett_chap);
340                 proto_tree_add_item(chap_tree, hf_radius_chap_ident, tvb, 0, 1, ENC_NA);
341                 proto_tree_add_item(chap_tree, hf_radius_chap_string, tvb, 1, 16, ENC_NA);
342         return (tvb_bytes_to_ep_str(tvb, 0, len));
343 }
344
345 static const gchar *dissect_framed_ip_address(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
346         int len;
347         guint32 ip;
348         guint32 ip_h;
349         const gchar *str;
350
351         len = tvb_length(tvb);
352         if (len != 4)
353                 return "[wrong length for IP address]";
354
355         ip=tvb_get_ipv4(tvb,0);
356         ip_h=g_ntohl(ip);
357
358         if (ip_h == 0xFFFFFFFF) {
359                 str = "Negotiated";
360                 proto_tree_add_ipv4_format_value(tree, hf_radius_framed_ip_address,
361                                            tvb, 0, len, ip, "%s", str);
362         } else if (ip_h == 0xFFFFFFFE) {
363                 str = "Assigned";
364                 proto_tree_add_ipv4_format_value(tree, hf_radius_framed_ip_address,
365                                            tvb, 0, len, ip, "%s", str);
366         } else {
367                 str = ip_to_str((guint8 *)&ip);
368                 proto_tree_add_ipv4_format_value(tree, hf_radius_framed_ip_address,
369                                            tvb, 0, len, ip, "%s (%s)",
370                                            get_hostname(ip), str);
371         }
372
373         return str;
374 }
375
376 static const gchar *dissect_login_ip_host(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
377         int len;
378         guint32 ip;
379         guint32 ip_h;
380         const gchar *str;
381
382         len = tvb_length(tvb);
383         if (len != 4)
384                 return "[wrong length for IP address]";
385
386         ip=tvb_get_ipv4(tvb,0);
387         ip_h=g_ntohl(ip);
388
389         if (ip_h == 0xFFFFFFFF) {
390                 str = "User-selected";
391                 proto_tree_add_ipv4_format_value(tree, hf_radius_login_ip_host,
392                                            tvb, 0, len, ip, "%s", str);
393         } else if (ip_h == 0) {
394                 str = "NAS-selected";
395                 proto_tree_add_ipv4_format_value(tree, hf_radius_login_ip_host,
396                                            tvb, 0, len, ip, "%s", str);
397         } else {
398                 str = ip_to_str((guint8 *)&ip);
399                 proto_tree_add_ipv4_format_value(tree, hf_radius_login_ip_host,
400                                            tvb, 0, len, ip, "%s (%s)",
401                                            get_hostname(ip), str);
402         }
403
404         return str;
405 }
406
407 static const value_string ascenddf_filtertype[] = { {0, "generic"}, {1, "ip"}, {0, NULL} };
408 static const value_string ascenddf_filteror[]   = { {0, "drop"}, {1, "forward"}, {0, NULL} };
409 static const value_string ascenddf_inout[]      = { {0, "out"}, {1, "in"}, {0, NULL} };
410 static const value_string ascenddf_proto[]      = { {1, "icmp"}, {6, "tcp"}, {17, "udp"}, {0, NULL} };
411 static const value_string ascenddf_portq[]      = { {1, "lt"}, {2, "eq"}, {3, "gt"}, {4, "ne"}, {0, NULL} };
412
413 static const gchar *dissect_ascend_data_filter(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
414         wmem_strbuf_t *filterstr;
415         int len;
416         guint8 proto, srclen, dstlen;
417         guint32 srcip, dstip;
418         guint16 srcport, dstport;
419         guint8 srcportq, dstportq;
420
421         len=tvb_length(tvb);
422
423         if (len != 24) {
424                 return wmem_strdup_printf(wmem_packet_scope(), "Wrong attribute length %d", len);
425         }
426
427         filterstr=wmem_strbuf_sized_new(wmem_packet_scope(), 64, 64);
428
429         proto_tree_add_item(tree, hf_radius_ascend_data_filter, tvb, 0, -1, ENC_NA);
430
431         wmem_strbuf_append_printf(filterstr, "%s %s %s",
432                 val_to_str(tvb_get_guint8(tvb, 0), ascenddf_filtertype, "%u"),
433                 val_to_str(tvb_get_guint8(tvb, 2), ascenddf_inout, "%u"),
434                 val_to_str(tvb_get_guint8(tvb, 1), ascenddf_filteror, "%u"));
435
436         proto=tvb_get_guint8(tvb, 14);
437         if (proto) {
438                 wmem_strbuf_append_printf(filterstr, " %s",
439                                 val_to_str(proto, ascenddf_proto, "%u"));
440         }
441
442         srcip=tvb_get_ipv4(tvb, 4);
443         srclen=tvb_get_guint8(tvb, 12);
444         srcport=tvb_get_ntohs(tvb, 16);
445         srcportq=tvb_get_guint8(tvb, 20);
446
447         if (srcip || srclen || srcportq) {
448                 wmem_strbuf_append_printf(filterstr, " srcip %s/%d", ip_to_str((guint8 *) &srcip), srclen);
449                 if (srcportq)
450                         wmem_strbuf_append_printf(filterstr, " srcport %s %d",
451                                 val_to_str(srcportq, ascenddf_portq, "%u"), srcport);
452         }
453
454         dstip=tvb_get_ipv4(tvb, 8);
455         dstlen=tvb_get_guint8(tvb, 13);
456         dstport=tvb_get_ntohs(tvb, 18);
457         dstportq=tvb_get_guint8(tvb, 21);
458
459         if (dstip || dstlen || dstportq) {
460                 wmem_strbuf_append_printf(filterstr, " dstip %s/%d", ip_to_str((guint8 *) &dstip), dstlen);
461                 if (dstportq)
462                         wmem_strbuf_append_printf(filterstr, " dstport %s %d",
463                                 val_to_str(dstportq, ascenddf_portq, "%u"), dstport);
464         }
465
466         return wmem_strbuf_get_str(filterstr);
467 }
468
469 static const gchar *dissect_framed_ipx_network(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
470         int len;
471         guint32 net;
472         const gchar *str;
473
474         len = tvb_length(tvb);
475         if (len != 4)
476                 return "[wrong length for IPX network]";
477
478         net=tvb_get_ntohl(tvb,0);
479
480         if (net == 0xFFFFFFFE)
481                 str = "NAS-selected";
482         else
483                 str = wmem_strdup_printf(wmem_packet_scope(), "0x%08X", net);
484         proto_tree_add_ipxnet_format_value(tree, hf_radius_framed_ipx_network, tvb, 0,
485                                      len, net, "Framed-IPX-Network: %s", str);
486
487         return str;
488 }
489
490 static const gchar* dissect_cosine_vpvc(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
491         guint vpi, vci;
492
493         if ( tvb_length(tvb) != 4 )
494                 return "[Wrong Length for VP/VC AVP]";
495
496         vpi = tvb_get_ntohs(tvb,0);
497         vci = tvb_get_ntohs(tvb,2);
498
499         proto_tree_add_uint(tree,hf_radius_cosine_vpi,tvb,0,2,vpi);
500         proto_tree_add_uint(tree,hf_radius_cosine_vci,tvb,2,2,vci);
501
502         return wmem_strdup_printf(wmem_packet_scope(), "%u/%u",vpi,vci);
503 }
504
505 static const value_string daylight_saving_time_vals[] = {
506     {0, "No adjustment"},
507     {1, "+1 hour adjustment for Daylight Saving Time"},
508     {2, "+2 hours adjustment for Daylight Saving Time"},
509     {3, "Reserved"},
510     {0, NULL}
511 };
512
513 static const gchar*
514 dissect_radius_3gpp_ms_tmime_zone(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
515
516     int offset = 0;
517     guint8      oct, daylight_saving_time;
518     char        sign;
519
520     /* 3GPP TS 23.040 version 6.6.0 Release 6
521      * 9.2.3.11 TP-Service-Centre-Time-Stamp (TP-SCTS)
522      * :
523      * The Time Zone indicates the difference, expressed in quarters of an hour,
524      * between the local time and GMT. In the first of the two semi-octets,
525      * the first bit (bit 3 of the seventh octet of the TP-Service-Centre-Time-Stamp field)
526      * represents the algebraic sign of this difference (0: positive, 1: negative).
527      */
528
529     oct = tvb_get_guint8(tvb, offset);
530     sign = (oct & 0x08) ? '-' : '+';
531     oct = (oct >> 4) + (oct & 0x07) * 10;
532
533     proto_tree_add_text(tree, tvb, offset, 1, "Timezone: GMT %c%d hours %d minutes", sign, oct / 4, oct % 4 * 15);
534     offset++;
535
536     daylight_saving_time = tvb_get_guint8(tvb, offset) & 0x3;
537     proto_tree_add_text(tree, tvb, offset, 1, "%s", val_to_str_const(daylight_saving_time, daylight_saving_time_vals, "Unknown"));
538
539         return wmem_strdup_printf(wmem_packet_scope(), "Timezone: GMT %c%d hours %d minutes %s ",
540                 sign, oct / 4, oct % 4 * 15, val_to_str_const(daylight_saving_time, daylight_saving_time_vals, "Unknown"));
541
542 }
543
544 static void
545 radius_decrypt_avp(gchar *dest,int dest_len,tvbuff_t *tvb,int offset,int length)
546 {
547         md5_state_t md_ctx, old_md_ctx;
548         md5_byte_t digest[AUTHENTICATOR_LENGTH];
549         int i, j;
550         gint totlen = 0, returned_length, padded_length;
551         guint8 *pd;
552         guchar c;
553
554         DISSECTOR_ASSERT(dest_len > 0);
555         dest[0] = '\0';
556         if ( length <= 0 )
557                 return;
558
559         /* The max avp length is 253 (255 - 2 for type & length), but only the
560          * User-Password is marked with encrypt=1 in dictionary.rfc2865, and the
561          * User-Password max length is only 128 (130 - 2 for type & length) per
562          * tools.ietf.org/html/rfc2865#section-5.2, so enforce that limit here.
563          */
564         if ( length > 128 )
565                 length = 128;
566
567         md5_init(&md_ctx);
568         md5_append(&md_ctx, (const guint8*)shared_secret, (int)strlen(shared_secret));
569         old_md_ctx = md_ctx;
570         md5_append(&md_ctx, authenticator, AUTHENTICATOR_LENGTH);
571         md5_finish(&md_ctx, digest);
572
573         padded_length = length + ((length % AUTHENTICATOR_LENGTH) ?
574                 (AUTHENTICATOR_LENGTH - (length % AUTHENTICATOR_LENGTH)) : 0);
575         pd = (guint8 *)wmem_alloc0(wmem_packet_scope(), padded_length);
576         tvb_memcpy(tvb, pd, offset, length);
577
578         for ( i = 0; i < padded_length; i += AUTHENTICATOR_LENGTH ) {
579                 for ( j = 0; j < AUTHENTICATOR_LENGTH; j++ ) {
580                         c = pd[i + j] ^ digest[j];
581                         if ( g_ascii_isprint(c) ) {
582                                 returned_length = g_snprintf(&dest[totlen], dest_len - totlen,
583                                         "%c", c);
584                                 totlen += MIN(returned_length, dest_len - totlen - 1);
585                         }
586                         else if ( c ) {
587                                 returned_length = g_snprintf(&dest[totlen], dest_len - totlen,
588                                         "\\%03o", c);
589                                 totlen += MIN(returned_length, dest_len - totlen - 1);
590                         }
591                 }
592
593                 md_ctx = old_md_ctx;
594                 md5_append(&md_ctx, &pd[i], AUTHENTICATOR_LENGTH);
595                 md5_finish(&md_ctx, digest);
596         }
597 }
598
599
600 void radius_integer(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
601         guint32 uintv;
602
603         switch (len) {
604                 case 1:
605                         uintv = tvb_get_guint8(tvb,offset);
606                         break;
607                 case 2:
608                         uintv = tvb_get_ntohs(tvb,offset);
609                         break;
610                 case 3:
611                         uintv = tvb_get_ntoh24(tvb,offset);
612                         break;
613                 case 4:
614                         uintv = tvb_get_ntohl(tvb,offset);
615                         break;
616                 case 8: {
617                         guint64 uintv64 = tvb_get_ntoh64(tvb,offset);
618                         proto_tree_add_uint64(tree,a->hf_alt,tvb,offset,len,uintv64);
619                         proto_item_append_text(avp_item, "%" G_GINT64_MODIFIER "u", uintv64);
620                         return;
621                 }
622                 default:
623                         proto_item_append_text(avp_item, "[unhandled integer length(%u)]", len);
624                         return;
625         }
626         proto_tree_add_item(tree,a->hf,tvb, offset, len, ENC_BIG_ENDIAN);
627
628         if (a->vs) {
629                 proto_item_append_text(avp_item, "%s(%u)", val_to_str_const(uintv, a->vs, "Unknown"),uintv);
630         } else {
631                 proto_item_append_text(avp_item, "%u", uintv);
632         }
633 }
634
635 void radius_signed(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
636         guint32 uintv;
637
638         switch (len) {
639                 case 1:
640                         uintv = tvb_get_guint8(tvb,offset);
641                         break;
642                 case 2:
643                         uintv = tvb_get_ntohs(tvb,offset);
644                         break;
645                 case 3:
646                         uintv = tvb_get_ntoh24(tvb,offset);
647                         break;
648                 case 4:
649                         uintv = tvb_get_ntohl(tvb,offset);
650                         break;
651                 case 8: {
652                         guint64 uintv64 = tvb_get_ntoh64(tvb,offset);
653                         proto_tree_add_int64(tree,a->hf_alt,tvb,offset,len,uintv64);
654                         proto_item_append_text(avp_item, "%" G_GINT64_MODIFIER "u", uintv64);
655                         return;
656                 }
657                 default:
658                         proto_item_append_text(avp_item, "[unhandled signed integer length(%u)]", len);
659                         return;
660         }
661
662         proto_tree_add_int(tree,a->hf,tvb,offset,len,uintv);
663
664         if (a->vs) {
665                 proto_item_append_text(avp_item, "%s(%d)", val_to_str_const(uintv, a->vs, "Unknown"),uintv);
666         } else {
667                 proto_item_append_text(avp_item, "%d", uintv);
668         }
669 }
670
671 void radius_string(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
672         switch (a->encrypt) {
673
674         case 0: /* not encrypted */
675                 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_UTF_8|ENC_NA);
676                 proto_item_append_text(avp_item, "%s", tvb_format_text(tvb, offset, len));
677                 break;
678
679         case 1: /* encrypted like User-Password as defined in RFC 2865 */
680                 if (*shared_secret == '\0') {
681                         proto_item_append_text(avp_item, "Encrypted");
682                         proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA);
683                 } else {
684                         gchar *buffer;
685                         buffer=(gchar *)wmem_alloc(wmem_packet_scope(), 1024); /* an AVP value can be at most 253 bytes */
686                         radius_decrypt_avp(buffer,1024,tvb,offset,len);
687                         proto_item_append_text(avp_item, "Decrypted: %s", buffer);
688                         proto_tree_add_string(tree, a->hf, tvb, offset, len, buffer);
689                 }
690                 break;
691
692         case 2: /* encrypted like Tunnel-Password as defined in RFC 2868 */
693                 proto_item_append_text(avp_item, "Encrypted");
694                 proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA);
695                 break;
696
697         case 3: /* encrypted like Ascend-Send-Secret as defined by Ascend^WLucent^WAlcatel-Lucent */
698                 proto_item_append_text(avp_item, "Encrypted");
699                 proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA);
700                 break;
701         }
702 }
703
704 void radius_octets(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
705         proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
706         proto_item_append_text(avp_item, "%s", tvb_bytes_to_ep_str(tvb, offset, len));
707 }
708
709 void radius_ipaddr(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
710         guint32 ip;
711         gchar buf[MAX_IP_STR_LEN];
712
713         if (len != 4) {
714                 proto_item_append_text(avp_item, "[wrong length for IP address]");
715                 return;
716         }
717
718         ip=tvb_get_ipv4(tvb,offset);
719
720         proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_BIG_ENDIAN);
721
722         ip_to_str_buf((guint8 *)&ip, buf, MAX_IP_STR_LEN);
723         proto_item_append_text(avp_item, "%s", buf);
724 }
725
726 void radius_ipv6addr(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
727         struct e_in6_addr ipv6_buff;
728         gchar txtbuf[256];
729
730         if (len != 16) {
731                 proto_item_append_text(avp_item, "[wrong length for IPv6 address]");
732                 return;
733         }
734
735         proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
736
737         tvb_get_ipv6(tvb, offset, &ipv6_buff);
738         ip6_to_str_buf(&ipv6_buff, txtbuf);
739         proto_item_append_text(avp_item, "%s", txtbuf);
740 }
741
742 void radius_ipv6prefix(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
743         struct e_in6_addr ipv6_buff;
744         gchar txtbuf[256];
745         guint8 n;
746
747         if ((len < 2) || (len > 18) ) {
748                 proto_item_append_text(avp_item, "[wrong length for IPv6 prefix]");
749                 return;
750         }
751
752         /* first byte is reserved == 0x00 */
753         if (tvb_get_guint8(tvb, offset)) {
754                 proto_item_append_text(avp_item, "[invalid reserved byte for IPv6 prefix]");
755                 return;
756         }
757
758         /* this is the prefix length */
759         n = tvb_get_guint8(tvb, offset + 1);
760         if (n > 128) {
761                 proto_item_append_text(avp_item, "[invalid IPv6 prefix length]");
762                 return;
763         }
764
765         proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
766
767         /* cannot use tvb_get_ipv6() here, since the prefix most likely is truncated */
768         memset(&ipv6_buff, 0, sizeof ipv6_buff);
769         tvb_memcpy(tvb, &ipv6_buff, offset + 2,  len - 2);
770         ip6_to_str_buf(&ipv6_buff, txtbuf);
771         proto_item_append_text(avp_item, "%s/%u", txtbuf, n);
772 }
773
774
775 void radius_combo_ip(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
776         guint32 ip;
777         struct e_in6_addr ipv6_buff;
778         gchar buf[256];
779
780         if (len == 4){
781                 ip=tvb_get_ipv4(tvb,offset);
782
783                 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_BIG_ENDIAN);
784
785                 ip_to_str_buf((guint8 *)&ip, buf, MAX_IP_STR_LEN);
786                 proto_item_append_text(avp_item, "%s", buf);
787         } else if (len == 16) {
788                 proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA);
789
790                 tvb_get_ipv6(tvb, offset, &ipv6_buff);
791                 ip6_to_str_buf(&ipv6_buff, buf);
792                 proto_item_append_text(avp_item, "%s", buf);
793         } else {
794                 proto_item_append_text(avp_item, "[wrong length for both of IPv4 and IPv6 address]");
795                 return;
796         }
797 }
798
799 void radius_ipxnet(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
800         guint32 net;
801
802         if (len != 4) {
803                 proto_item_append_text(avp_item, "[wrong length for IPX network]");
804                 return;
805         }
806
807         net=tvb_get_ntohl(tvb,offset);
808
809         proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
810
811         proto_item_append_text(avp_item, "0x%08X", net);
812 }
813
814 void radius_date(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
815         nstime_t time_ptr;
816
817         if (len != 4) {
818                 proto_item_append_text(avp_item, "[wrong length for timestamp]");
819                 return;
820         }
821         time_ptr.secs = tvb_get_ntohl(tvb,offset);
822         time_ptr.nsecs = 0;
823
824         proto_tree_add_time(tree, a->hf, tvb, offset, len, &time_ptr);
825         proto_item_append_text(avp_item, "%s", abs_time_to_str(wmem_packet_scope(), &time_ptr, ABSOLUTE_TIME_LOCAL, TRUE));
826 }
827
828 /*
829  * "abinary" is Ascend's binary format for filters.  See dissect_ascend_data_filter().
830  */
831 void radius_abinary(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
832         proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
833         proto_item_append_text(avp_item, "%s", tvb_bytes_to_ep_str(tvb, offset, len));
834 }
835
836 void radius_ether(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
837         if (len != 6) {
838                 proto_item_append_text(avp_item, "[wrong length for ethernet address]");
839                 return;
840         }
841
842         proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
843         proto_item_append_text(avp_item, "%s", tvb_ether_to_str(tvb, offset));
844 }
845
846 void radius_ifid(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
847         proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
848         proto_item_append_text(avp_item, "%s", tvb_bytes_to_ep_str(tvb, offset, len));
849 }
850
851 static void add_tlv_to_tree(proto_tree* tlv_tree, proto_item* tlv_item, packet_info* pinfo, tvbuff_t* tvb, radius_attr_info_t* dictionary_entry, guint32 tlv_length, guint32 offset) {
852         proto_item_append_text(tlv_item, ": ");
853         dictionary_entry->type(dictionary_entry,tlv_tree,pinfo,tvb,offset,tlv_length,tlv_item);
854 }
855
856 void radius_tlv(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
857         proto_item* item;
858         gint tlv_num = 0;
859
860         while (len > 0) {
861                 radius_attr_info_t* dictionary_entry = NULL;
862                 guint32 tlv_type;
863                 guint32 tlv_length;
864
865                 proto_item* tlv_item;
866                 proto_item* tlv_len_item;
867                 proto_tree* tlv_tree;
868
869                 if (len < 2) {
870                         item = proto_tree_add_text(tree, tvb, offset, 0,
871                                                    "Not enough room in packet for TLV header");
872                         PROTO_ITEM_SET_GENERATED(item);
873                         return;
874                 }
875                 tlv_type = tvb_get_guint8(tvb,offset);
876                 tlv_length = tvb_get_guint8(tvb,offset+1);
877
878                 if (tlv_length < 2) {
879                         item = proto_tree_add_text(tree, tvb, offset, 0,
880                                                    "TLV too short: length %u < 2", tlv_length);
881                         PROTO_ITEM_SET_GENERATED(item);
882                         return;
883                 }
884
885                 if (len < (gint)tlv_length) {
886                         item = proto_tree_add_text(tree, tvb, offset, 0,
887                                                    "Not enough room in packet for TLV");
888                         PROTO_ITEM_SET_GENERATED(item);
889                         return;
890                 }
891
892                 len -= tlv_length;
893
894                 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(a->tlvs_by_id,GUINT_TO_POINTER(tlv_type));
895
896                 if (! dictionary_entry ) {
897                         dictionary_entry = &no_dictionary_entry;
898                 }
899
900                 tlv_item = proto_tree_add_text(tree, tvb, offset, tlv_length,
901                                                "TLV: l=%u t=%s(%u)", tlv_length,
902                                                dictionary_entry->name, tlv_type);
903
904                 tlv_length -= 2;
905                 offset += 2;
906
907                 tlv_tree = proto_item_add_subtree(tlv_item,dictionary_entry->ett);
908
909                 if (show_length) {
910                         tlv_len_item = proto_tree_add_uint(tlv_tree,
911                                                            dictionary_entry->hf_len,
912                                                            tvb,0,0,tlv_length);
913                         PROTO_ITEM_SET_GENERATED(tlv_len_item);
914                 }
915
916                 add_tlv_to_tree(tlv_tree, tlv_item, pinfo, tvb, dictionary_entry,
917                                 tlv_length, offset);
918                 offset += tlv_length;
919                 tlv_num++;
920         }
921
922         proto_item_append_text(avp_item, "%d TLV(s) inside", tlv_num);
923 }
924
925 static void add_avp_to_tree(proto_tree* avp_tree, proto_item* avp_item, packet_info* pinfo, tvbuff_t* tvb, radius_attr_info_t* dictionary_entry, guint32 avp_length, guint32 offset) {
926         proto_item* pi;
927
928         if (dictionary_entry->tagged) {
929                 guint tag;
930
931                 if (avp_length == 0) {
932                         pi = proto_tree_add_text(avp_tree, tvb, offset,
933                                                  0, "AVP too short for tag");
934                         PROTO_ITEM_SET_GENERATED(pi);
935                         return;
936                 }
937
938                 tag = tvb_get_guint8(tvb, offset);
939
940                 if (tag <=  0x1f) {
941                         proto_tree_add_uint(avp_tree,
942                                             dictionary_entry->hf_tag,
943                                             tvb, offset, 1, tag);
944
945                         proto_item_append_text(avp_item,
946                                                " Tag=0x%.2x", tag);
947
948                         offset++;
949                         avp_length--;
950                 }
951         }
952
953         if ( dictionary_entry->dissector ) {
954                 tvbuff_t* tvb_value;
955                 const gchar* str;
956
957                 tvb_value = tvb_new_subset_length(tvb, offset, avp_length);
958
959                 str = dictionary_entry->dissector(avp_tree,tvb_value,pinfo);
960
961                 proto_item_append_text(avp_item, ": %s",str);
962         } else {
963                 proto_item_append_text(avp_item, ": ");
964
965                 dictionary_entry->type(dictionary_entry,avp_tree,pinfo,tvb,offset,avp_length,avp_item);
966         }
967 }
968
969 static gboolean vsa_buffer_destroy(gpointer k _U_, gpointer v, gpointer p _U_) {
970         radius_vsa_buffer* vsa_buffer = (radius_vsa_buffer*)v;
971         g_free((gpointer)vsa_buffer->data);
972         g_free(v);
973         return TRUE;
974 }
975
976 static void vsa_buffer_table_destroy(void *table) {
977         if (table) {
978                 g_hash_table_foreach_remove((GHashTable *)table, vsa_buffer_destroy, NULL);
979                 g_hash_table_destroy((GHashTable *)table);
980         }
981 }
982
983
984 void dissect_attribute_value_pairs(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset, guint length) {
985         proto_item* item;
986         gboolean last_eap = FALSE;
987         guint8* eap_buffer = NULL;
988         guint eap_seg_num = 0;
989         guint eap_tot_len_captured = 0;
990         guint eap_tot_len = 0;
991         proto_tree* eap_tree = NULL;
992         tvbuff_t* eap_tvb = NULL;
993
994         GHashTable* vsa_buffer_table = NULL;
995
996         /* Forces load of header fields, if not already done so */
997         DISSECTOR_ASSERT(proto_registrar_get_byname("radius.code"));
998
999         /*
1000          * In case we throw an exception, clean up whatever stuff we've
1001          * allocated (if any).
1002          */
1003         CLEANUP_PUSH_PFX(la, g_free, eap_buffer);
1004         CLEANUP_PUSH_PFX(lb, vsa_buffer_table_destroy, (void *)vsa_buffer_table);
1005
1006         while (length > 0) {
1007                 radius_attr_info_t* dictionary_entry = NULL;
1008                 gint tvb_len;
1009                 guint32 avp_type;
1010                 guint32 avp_length;
1011                 guint32 vendor_id;
1012
1013                 proto_item* avp_item;
1014                 proto_item* avp_len_item;
1015                 proto_tree* avp_tree;
1016
1017                 if (length < 2) {
1018                         item = proto_tree_add_text(tree, tvb, offset, 0,
1019                                                    "Not enough room in packet for AVP header");
1020                         PROTO_ITEM_SET_GENERATED(item);
1021                         break;  /* exit outer loop, then cleanup & return */
1022                 }
1023                 avp_type = tvb_get_guint8(tvb,offset);
1024                 avp_length = tvb_get_guint8(tvb,offset+1);
1025
1026                 if (avp_length < 2) {
1027                         item = proto_tree_add_text(tree, tvb, offset, 0,
1028                                                    "AVP too short: length %u < 2", avp_length);
1029                         PROTO_ITEM_SET_GENERATED(item);
1030                         break;  /* exit outer loop, then cleanup & return */
1031                 }
1032
1033                 if (length < avp_length) {
1034                         item = proto_tree_add_text(tree, tvb, offset, 0,
1035                                                    "Not enough room in packet for AVP");
1036                         PROTO_ITEM_SET_GENERATED(item);
1037                         break;  /* exit outer loop, then cleanup & return */
1038                 }
1039
1040                 length -= avp_length;
1041
1042                 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(dict->attrs_by_id, GUINT_TO_POINTER(avp_type));
1043
1044                 if (! dictionary_entry ) {
1045                         dictionary_entry = &no_dictionary_entry;
1046                 }
1047
1048                 avp_item = proto_tree_add_text(tree, tvb, offset, avp_length,
1049                                                "AVP: l=%u t=%s(%u)", avp_length,
1050                                                dictionary_entry->name, avp_type);
1051
1052                 avp_length -= 2;
1053                 offset += 2;
1054
1055                 if (avp_type == RADIUS_ATTR_TYPE_VENDOR_SPECIFIC) {
1056                         radius_vendor_info_t* vendor;
1057                         proto_tree* vendor_tree;
1058                         gint max_offset = offset + avp_length;
1059                         const gchar* vendor_str;
1060
1061                         /* XXX TODO: handle 2 byte codes for USR */
1062
1063                         if (avp_length < 4) {
1064                                 proto_item_append_text(avp_item, " [AVP too short; no room for vendor ID]");
1065                                 offset += avp_length;
1066                                 continue; /* while (length > 0) */
1067                         }
1068                         vendor_id = tvb_get_ntohl(tvb,offset);
1069
1070                         avp_length -= 4;
1071                         offset += 4;
1072
1073                         vendor = (radius_vendor_info_t *)g_hash_table_lookup(dict->vendors_by_id,GUINT_TO_POINTER(vendor_id));
1074                         vendor_str = val_to_str_ext_const(vendor_id, &sminmpec_values_ext, "Unknown");
1075                         if (!vendor) {
1076                                 vendor = &no_vendor;
1077                         }
1078                         proto_item_append_text(avp_item, " v=%s(%u)", vendor_str,
1079                                                vendor_id);
1080
1081                         vendor_tree = proto_item_add_subtree(avp_item,vendor->ett);
1082
1083                         while (offset < max_offset) {
1084                                 guint32 avp_vsa_type;
1085                                 guint32 avp_vsa_len;
1086                                 guint8 avp_vsa_flags = 0;
1087                                 guint32 avp_vsa_header_len = vendor->type_octets + vendor->length_octets + (vendor->has_flags ? 1 : 0);
1088
1089                                 switch (vendor->type_octets) {
1090                                         case 1:
1091                                                 avp_vsa_type = tvb_get_guint8(tvb,offset++);
1092                                                 break;
1093                                         case 2:
1094                                                 avp_vsa_type = tvb_get_ntohs(tvb,offset);
1095                                                 offset += 2;
1096                                                 break;
1097                                         case 4:
1098                                                 avp_vsa_type = tvb_get_ntohl(tvb,offset);
1099                                                 offset += 4;
1100                                                 break;
1101                                         default:
1102                                                 avp_vsa_type = tvb_get_guint8(tvb,offset++);
1103                                 }
1104
1105                                 switch (vendor->length_octets) {
1106                                         case 1:
1107                                                 avp_vsa_len = tvb_get_guint8(tvb,offset++);
1108                                                 break;
1109                                         case 0:
1110                                                 avp_vsa_len = avp_length;
1111                                                 break;
1112                                         case 2:
1113                                                 avp_vsa_len = tvb_get_ntohs(tvb,offset);
1114                                                 offset += 2;
1115                                                 break;
1116                                         default:
1117                                                 avp_vsa_len = tvb_get_guint8(tvb,offset++);
1118                                 }
1119
1120                                 if (vendor->has_flags) {
1121                                         avp_vsa_flags = tvb_get_guint8(tvb,offset++);
1122                                 }
1123
1124                                 if (avp_vsa_len < avp_vsa_header_len) {
1125                                         proto_tree_add_text(tree, tvb, offset+1, 1,
1126                                                             "[VSA too short]");
1127                                         break; /* exit while (offset < max_offset) loop */
1128                                 }
1129
1130                                 avp_vsa_len -= avp_vsa_header_len;
1131
1132                                 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(vendor->attrs_by_id,GUINT_TO_POINTER(avp_vsa_type));
1133
1134                                 if ( !dictionary_entry ) {
1135                                         dictionary_entry = &no_dictionary_entry;
1136                                 }
1137
1138                                 if (vendor->has_flags){
1139                                         avp_item = proto_tree_add_text(vendor_tree,tvb,offset-avp_vsa_header_len,avp_vsa_len+avp_vsa_header_len,
1140                                                                        "VSA: l=%u t=%s(%u) C=0x%02x",
1141                                                                        avp_vsa_len+avp_vsa_header_len, dictionary_entry->name, avp_vsa_type, avp_vsa_flags);
1142                                 } else {
1143                                         avp_item = proto_tree_add_text(vendor_tree,tvb,offset-avp_vsa_header_len,avp_vsa_len+avp_vsa_header_len,
1144                                                                        "VSA: l=%u t=%s(%u)",
1145                                                                        avp_vsa_len+avp_vsa_header_len, dictionary_entry->name, avp_vsa_type);
1146                                 }
1147
1148                                 avp_tree = proto_item_add_subtree(avp_item,dictionary_entry->ett);
1149
1150                                 if (show_length) {
1151                                         avp_len_item = proto_tree_add_uint(avp_tree,
1152                                                                            dictionary_entry->hf_len,
1153                                                                            tvb,0,0,avp_length);
1154                                         PROTO_ITEM_SET_GENERATED(avp_len_item);
1155                                 }
1156
1157                                 if (vendor->has_flags) {
1158                                         radius_vsa_buffer_key key;
1159                                         radius_vsa_buffer* vsa_buffer = NULL;
1160                                         key.vendor_id = vendor_id;
1161                                         key.vsa_type = avp_vsa_type;
1162
1163                                         if (!vsa_buffer_table) {
1164                                                 vsa_buffer_table = g_hash_table_new(radius_vsa_hash, radius_vsa_equal);
1165                                         }
1166
1167                                         vsa_buffer = (radius_vsa_buffer *)g_hash_table_lookup(vsa_buffer_table, &key);
1168                                         if (vsa_buffer) {
1169                                                 vsa_buffer->data = (guint8 *)g_realloc(vsa_buffer->data, vsa_buffer->len + avp_vsa_len);
1170                                                 tvb_memcpy(tvb, vsa_buffer->data + vsa_buffer->len, offset, avp_vsa_len);
1171                                                 vsa_buffer->len += avp_vsa_len;
1172                                                 vsa_buffer->seg_num++;
1173                                         }
1174
1175                                         if (avp_vsa_flags & 0x80) {
1176                                                 if (!vsa_buffer) {
1177                                                         vsa_buffer = (radius_vsa_buffer *)g_malloc(sizeof(radius_vsa_buffer));
1178                                                         vsa_buffer->key.vendor_id = vendor_id;
1179                                                         vsa_buffer->key.vsa_type = avp_vsa_type;
1180                                                         vsa_buffer->len = avp_vsa_len;
1181                                                         vsa_buffer->seg_num = 1;
1182                                                         vsa_buffer->data = (guint8 *)g_malloc(avp_vsa_len);
1183                                                         tvb_memcpy(tvb, vsa_buffer->data, offset, avp_vsa_len);
1184                                                         g_hash_table_insert(vsa_buffer_table, &(vsa_buffer->key), vsa_buffer);
1185                                                 }
1186                                                 proto_tree_add_text(avp_tree, tvb, offset, avp_vsa_len, "VSA fragment");
1187                                                 proto_item_append_text(avp_item, ": VSA fragment[%u]", vsa_buffer->seg_num);
1188                                         } else {
1189                                                 if (vsa_buffer) {
1190                                                         tvbuff_t* vsa_tvb = NULL;
1191                                                         proto_tree_add_text(avp_tree, tvb, offset, avp_vsa_len, "VSA fragment");
1192                                                         proto_item_append_text(avp_item, ": Last VSA fragment[%u]", vsa_buffer->seg_num);
1193                                                         vsa_tvb = tvb_new_child_real_data(tvb, vsa_buffer->data, vsa_buffer->len, vsa_buffer->len);
1194                                                         tvb_set_free_cb(vsa_tvb, g_free);
1195                                                         add_new_data_source(pinfo, vsa_tvb, "Reassembled VSA");
1196                                                         add_avp_to_tree(avp_tree, avp_item, pinfo, vsa_tvb, dictionary_entry, vsa_buffer->len, 0);
1197                                                         g_hash_table_remove(vsa_buffer_table, &(vsa_buffer->key));
1198                                                         g_free(vsa_buffer);
1199
1200                                                 } else {
1201                                                         add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry, avp_vsa_len, offset);
1202                                                 }
1203                                         }
1204                                 } else {
1205                                         add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry, avp_vsa_len, offset);
1206                                 }
1207
1208                                 offset += avp_vsa_len;
1209                         } /* while (offset < max_offset) */
1210                         continue;  /* while (length > 0) */
1211                 }
1212
1213                 avp_tree = proto_item_add_subtree(avp_item,dictionary_entry->ett);
1214
1215                 if (show_length) {
1216                         avp_len_item = proto_tree_add_uint(avp_tree,
1217                                                            dictionary_entry->hf_len,
1218                                                            tvb,0,0,avp_length);
1219                         PROTO_ITEM_SET_GENERATED(avp_len_item);
1220                 }
1221
1222                 tvb_len = tvb_length_remaining(tvb, offset);
1223
1224                 if ((gint)avp_length < tvb_len)
1225                         tvb_len = avp_length;
1226
1227                 if (avp_type == RADIUS_ATTR_TYPE_EAP_MESSAGE) {
1228                         eap_seg_num++;
1229
1230                         /* Show this as an EAP fragment. */
1231                         if (tree)
1232                                 proto_tree_add_text(avp_tree, tvb, offset, tvb_len,
1233                                                     "EAP fragment");
1234
1235                         if (eap_tvb != NULL) {
1236                                 /*
1237                                  * Oops, a non-consecutive EAP-Message
1238                                  * attribute.
1239                                  */
1240                                 proto_item_append_text(avp_item, " (non-consecutive)");
1241                         } else {
1242                                 /*
1243                                  * RFC 2869 says, in section 5.13, describing
1244                                  * the EAP-Message attribute:
1245                                  *
1246                                  *    The NAS places EAP messages received
1247                                  *    from the authenticating peer into one
1248                                  *    or more EAP-Message attributes and
1249                                  *    forwards them to the RADIUS Server
1250                                  *    within an Access-Request message.
1251                                  *    If multiple EAP-Messages are
1252                                  *    contained within an Access-Request or
1253                                  *    Access-Challenge packet, they MUST be
1254                                  *    in order and they MUST be consecutive
1255                                  *    attributes in the Access-Request or
1256                                  *    Access-Challenge packet.
1257                                  *
1258                                  *        ...
1259                                  *
1260                                  *    The String field contains EAP packets,
1261                                  *    as defined in [3].  If multiple
1262                                  *    EAP-Message attributes are present
1263                                  *    in a packet their values should be
1264                                  *    concatenated; this allows EAP packets
1265                                  *    longer than 253 octets to be passed
1266                                  *    by RADIUS.
1267                                  *
1268                                  * Do reassembly of EAP-Message attributes.
1269                                  * We just concatenate all the attributes,
1270                                  * and when we see either the end of the
1271                                  * attribute list or a non-EAP-Message
1272                                  * attribute, we know we're done.
1273                                  */
1274
1275                                 if (eap_buffer == NULL)
1276                                         eap_buffer = (guint8 *)g_malloc(eap_tot_len_captured + tvb_len);
1277                                 else
1278                                         eap_buffer = (guint8 *)g_realloc(eap_buffer,
1279                                                                eap_tot_len_captured + tvb_len);
1280                                 tvb_memcpy(tvb, eap_buffer + eap_tot_len_captured, offset,
1281                                            tvb_len);
1282                                 eap_tot_len_captured += tvb_len;
1283                                 eap_tot_len += avp_length;
1284
1285                                 if ( tvb_bytes_exist(tvb, offset + avp_length + 1, 1) ) {
1286                                         guint8 next_type = tvb_get_guint8(tvb, offset + avp_length);
1287
1288                                         if ( next_type != RADIUS_ATTR_TYPE_EAP_MESSAGE ) {
1289                                                 /* Non-EAP-Message attribute */
1290                                                 last_eap = TRUE;
1291                                         }
1292                                 } else {
1293                                         /*
1294                                          * No more attributes, either because
1295                                          * we're at the end of the packet or
1296                                          * because we're at the end of the
1297                                          * captured packet data.
1298                                          */
1299                                         last_eap = TRUE;
1300                                 }
1301
1302                                 if (last_eap && eap_buffer) {
1303                                         gboolean save_writable;
1304
1305                                         proto_item_append_text(avp_item, " Last Segment[%u]",
1306                                                                eap_seg_num);
1307
1308                                         eap_tree = proto_item_add_subtree(avp_item,ett_eap);
1309
1310                                         eap_tvb = tvb_new_child_real_data(tvb, eap_buffer,
1311                                                                           eap_tot_len_captured,
1312                                                                           eap_tot_len);
1313                                         tvb_set_free_cb(eap_tvb, g_free);
1314                                         add_new_data_source(pinfo, eap_tvb, "Reassembled EAP");
1315
1316                                         /*
1317                                          * Don't free this when we're done -
1318                                          * it's associated with a tvbuff.
1319                                          */
1320                                         eap_buffer = NULL;
1321
1322                                         /*
1323                                          * Set the columns non-writable,
1324                                          * so that the packet list shows
1325                                          * this as an RADIUS packet, not
1326                                          * as an EAP packet.
1327                                          */
1328                                         save_writable = col_get_writable(pinfo->cinfo);
1329                                         col_set_writable(pinfo->cinfo, FALSE);
1330
1331                                         call_dissector(eap_handle, eap_tvb, pinfo, eap_tree);
1332
1333                                         col_set_writable(pinfo->cinfo, save_writable);
1334                                 } else {
1335                                         proto_item_append_text(avp_item, " Segment[%u]",
1336                                                                eap_seg_num);
1337                                 }
1338                         }
1339
1340                         offset += avp_length;
1341                 } else {
1342                         add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry,
1343                                         avp_length, offset);
1344                         offset += avp_length;
1345                 }
1346
1347         }  /* while (length > 0) */
1348
1349         CLEANUP_CALL_AND_POP_PFX(lb); /* vsa_buffer_table_destroy(vsa_buffer_table) */
1350
1351         /*
1352          * Call the cleanup handler to free any reassembled data we haven't
1353          * attached to a tvbuff, and pop the handler.
1354          */
1355         CLEANUP_CALL_AND_POP_PFX(la);
1356 }
1357
1358 /* This function tries to determine whether a packet is radius or not */
1359 static gboolean
1360 is_radius(tvbuff_t *tvb)
1361 {
1362         guint8 code;
1363         guint16 length;
1364
1365         code=tvb_get_guint8(tvb, 0);
1366         if (try_val_to_str_ext(code, &radius_pkt_type_codes_ext) == NULL) {
1367                 return FALSE;
1368         }
1369
1370         /* Check for valid length value:
1371          * Length
1372          *
1373          *  The Length field is two octets.  It indicates the length of the
1374          *  packet including the Code, Identifier, Length, Authenticator and
1375          *  Attribute fields.  Octets outside the range of the Length field
1376          *  MUST be treated as padding and ignored on reception.  If the
1377          *  packet is shorter than the Length field indicates, it MUST be
1378          *  silently discarded.  The minimum length is 20 and maximum length
1379          *  is 4096.
1380          */
1381         length=tvb_get_ntohs(tvb, 2);
1382         if ( (length<20) || (length>4096) ) {
1383                 return FALSE;
1384         }
1385
1386         return TRUE;
1387 }
1388
1389 static void register_radius_fields(const char*);
1390
1391 static int
1392 dissect_radius(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
1393 {
1394         proto_tree *radius_tree = NULL;
1395         proto_tree *avptree = NULL;
1396         proto_item *ti, *hidden_item;
1397         proto_item *avptf;
1398         guint avplength;
1399         e_radiushdr rh;
1400         radius_info_t *rad_info;
1401
1402         conversation_t* conversation;
1403         radius_call_info_key radius_call_key;
1404         radius_call_info_key *new_radius_call_key;
1405         radius_call_t *radius_call = NULL;
1406         static address null_address = { AT_NONE, -1, 0, NULL };
1407
1408         /* does this look like radius ? */
1409         if(!is_radius(tvb)){
1410                 return 0;
1411         }
1412
1413         col_set_str(pinfo->cinfo, COL_PROTOCOL, "RADIUS");
1414         col_clear(pinfo->cinfo, COL_INFO);
1415
1416         rh.rh_code=tvb_get_guint8(tvb,0);
1417         rh.rh_ident=tvb_get_guint8(tvb,1);
1418         rh.rh_pktlength=tvb_get_ntohs(tvb,2);
1419
1420
1421         /* Initialise stat info for passing to tap */
1422         rad_info = wmem_new(wmem_packet_scope(), radius_info_t);
1423         rad_info->code = 0;
1424         rad_info->ident = 0;
1425         rad_info->req_time.secs = 0;
1426         rad_info->req_time.nsecs = 0;
1427         rad_info->is_duplicate = FALSE;
1428         rad_info->request_available = FALSE;
1429         rad_info->req_num = 0; /* frame number request seen */
1430         rad_info->rspcode = 0;
1431         /* tap stat info */
1432         rad_info->code = rh.rh_code;
1433         rad_info->ident = rh.rh_ident;
1434         tap_queue_packet(radius_tap, pinfo, rad_info);
1435
1436         col_add_fstr(pinfo->cinfo,COL_INFO,"%s(%d) (id=%d, l=%d)",
1437                         val_to_str_ext_const(rh.rh_code, &radius_pkt_type_codes_ext, "Unknown Packet"),
1438                         rh.rh_code, rh.rh_ident, rh.rh_pktlength);
1439
1440         if (tree)
1441         {
1442                 /* Forces load of header fields, if not already done so */
1443                 DISSECTOR_ASSERT(proto_registrar_get_byname("radius.code"));
1444
1445                 ti = proto_tree_add_item(tree,proto_radius, tvb, 0, rh.rh_pktlength, ENC_NA);
1446                 radius_tree = proto_item_add_subtree(ti, ett_radius);
1447                 proto_tree_add_uint(radius_tree,hf_radius_code, tvb, 0, 1, rh.rh_code);
1448                 proto_tree_add_uint_format(radius_tree,hf_radius_id, tvb, 1, 1, rh.rh_ident,
1449                         "Packet identifier: 0x%01x (%d)", rh.rh_ident, rh.rh_ident);
1450         }
1451
1452         /*
1453          * Make sure the length is sane.
1454          */
1455         if (rh.rh_pktlength < HDR_LENGTH)
1456         {
1457                 if (tree)
1458                 {
1459                         proto_tree_add_uint_format_value(radius_tree, hf_radius_length,
1460                                 tvb, 2, 2, rh.rh_pktlength, "%u (bogus, < %u)",
1461                                 rh.rh_pktlength, HDR_LENGTH);
1462                 }
1463                 return tvb_length(tvb);
1464         }
1465
1466         avplength = rh.rh_pktlength - HDR_LENGTH;
1467         if (tree)
1468         {
1469                 proto_tree_add_uint(radius_tree, hf_radius_length, tvb, 2, 2, rh.rh_pktlength);
1470                 proto_tree_add_item(radius_tree, hf_radius_authenticator, tvb, 4, AUTHENTICATOR_LENGTH,ENC_NA);
1471         }
1472         tvb_memcpy(tvb, authenticator, 4, AUTHENTICATOR_LENGTH);
1473
1474         /* Conversation support REQUEST/RESPONSES */
1475         switch (rh.rh_code)
1476         {
1477                 case RADIUS_PKT_TYPE_ACCESS_REQUEST:
1478                 case RADIUS_PKT_TYPE_ACCOUNTING_REQUEST:
1479                 case RADIUS_PKT_TYPE_PASSWORD_REQUEST:
1480                 case RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST:
1481                 case RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST:
1482                 case RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST:
1483                 case RADIUS_PKT_TYPE_EVENT_REQUEST:
1484                 case RADIUS_PKT_TYPE_DISCONNECT_REQUEST:
1485                 case RADIUS_PKT_TYPE_COA_REQUEST:
1486                 case RADIUS_PKT_TYPE_ALU_STATE_REQUEST:
1487                         /* Don't bother creating conversations if we're encapsulated within
1488                          * an error packet, such as an ICMP destination unreachable */
1489                         if (pinfo->flags.in_error_pkt)
1490                                 break;
1491
1492                         if (tree)
1493                         {
1494                                 hidden_item = proto_tree_add_boolean(radius_tree, hf_radius_req, tvb, 0, 0, TRUE);
1495                                 PROTO_ITEM_SET_HIDDEN(hidden_item);
1496                         }
1497
1498                         /* Keep track of the address and port whence the call came
1499                          *  so that we can match up requests with replies.
1500                          *
1501                          * Because it is UDP and the reply can come from any IP
1502                          * and port (not necessarily the request dest), we only
1503                          * track the source IP and port of the request to match
1504                          * the reply.
1505                          */
1506
1507                         /*
1508                          * XXX - can we just use NO_ADDR_B?  Unfortunately,
1509                          * you currently still have to pass a non-null
1510                          * pointer for the second address argument even
1511                          * if you do that.
1512                          */
1513                         conversation = find_conversation(pinfo->fd->num, &pinfo->src,
1514                                 &null_address, pinfo->ptype, pinfo->srcport,
1515                                 pinfo->destport, 0);
1516                         if (conversation == NULL)
1517                         {
1518                                 /* It's not part of any conversation - create a new one. */
1519                                 conversation = conversation_new(pinfo->fd->num, &pinfo->src,
1520                                         &null_address, pinfo->ptype, pinfo->srcport,
1521                                         pinfo->destport, 0);
1522                         }
1523
1524                         /* Prepare the key data */
1525                         radius_call_key.code = rh.rh_code;
1526                         radius_call_key.ident = rh.rh_ident;
1527                         radius_call_key.conversation = conversation;
1528                         radius_call_key.req_time = pinfo->fd->abs_ts;
1529
1530                         /* Look up the request */
1531                         radius_call = (radius_call_t *)g_hash_table_lookup(radius_calls, &radius_call_key);
1532                         if (radius_call != NULL)
1533                         {
1534                                 /* We've seen a request with this ID, with the same
1535                                    destination, before - but was it *this* request? */
1536                                 if (pinfo->fd->num != radius_call->req_num)
1537                                 {
1538                                         /* No, so it's a duplicate request. Mark it as such.
1539                                           FIXME: This is way too simple, as the request number
1540                                                  is only an 8-bit value. See bug#4096 */
1541                                         rad_info->is_duplicate = TRUE;
1542                                         rad_info->req_num = radius_call->req_num;
1543                                         col_append_fstr(pinfo->cinfo, COL_INFO,
1544                                                         ", Duplicate Request ID:%u", rh.rh_ident);
1545
1546                                         if (tree)
1547                                         {
1548                                                 proto_item* item;
1549                                                 hidden_item = proto_tree_add_uint(radius_tree, hf_radius_dup, tvb, 0,0, rh.rh_ident);
1550                                                 PROTO_ITEM_SET_HIDDEN(hidden_item);
1551                                                 item = proto_tree_add_uint(radius_tree, hf_radius_req_dup, tvb, 0,0, rh.rh_ident);
1552                                                 PROTO_ITEM_SET_GENERATED(item);
1553                                         }
1554                                 }
1555                         }
1556                         else
1557                         {
1558                                 /* Prepare the value data.
1559                                    "req_num" and "rsp_num" are frame numbers;
1560                                    frame numbers are 1-origin, so we use 0
1561                                    to mean "we don't yet know in which frame
1562                                    the reply for this call appears". */
1563                                 new_radius_call_key = wmem_new(wmem_file_scope(), radius_call_info_key);
1564                                 *new_radius_call_key = radius_call_key;
1565                                 radius_call = wmem_new(wmem_file_scope(), radius_call_t);
1566                                 radius_call->req_num = pinfo->fd->num;
1567                                 radius_call->rsp_num = 0;
1568                                 radius_call->ident = rh.rh_ident;
1569                                 radius_call->code = rh.rh_code;
1570                                 radius_call->responded = FALSE;
1571                                 radius_call->req_time = pinfo->fd->abs_ts;
1572                                 radius_call->rspcode = 0;
1573
1574                                 /* Store it */
1575                                 g_hash_table_insert(radius_calls, new_radius_call_key, radius_call);
1576                         }
1577                         if (tree && radius_call->rsp_num)
1578                         {
1579                                 proto_item* item;
1580                                 item = proto_tree_add_uint_format(radius_tree,
1581                                         hf_radius_rsp_frame, tvb, 0, 0, radius_call->rsp_num,
1582                                         "The response to this request is in frame %u",
1583                                         radius_call->rsp_num);
1584                                 PROTO_ITEM_SET_GENERATED(item);
1585                         }
1586                         break;
1587                 case RADIUS_PKT_TYPE_ACCESS_ACCEPT:
1588                 case RADIUS_PKT_TYPE_ACCESS_REJECT:
1589                 case RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE:
1590                 case RADIUS_PKT_TYPE_PASSWORD_ACK:
1591                 case RADIUS_PKT_TYPE_PASSWORD_REJECT:
1592                 case RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE:
1593                 case RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE:
1594                 case RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE:
1595                 case RADIUS_PKT_TYPE_EVENT_RESPONSE:
1596                 case RADIUS_PKT_TYPE_DISCONNECT_ACK:
1597                 case RADIUS_PKT_TYPE_DISCONNECT_NAK:
1598                 case RADIUS_PKT_TYPE_COA_ACK:
1599                 case RADIUS_PKT_TYPE_COA_NAK:
1600                 case RADIUS_PKT_TYPE_ACCESS_CHALLENGE:
1601                 case RADIUS_PKT_TYPE_ALU_STATE_ACCEPT:
1602                 case RADIUS_PKT_TYPE_ALU_STATE_REJECT:
1603                 case RADIUS_PKT_TYPE_ALU_STATE_ERROR:
1604                         /* Don't bother finding conversations if we're encapsulated within
1605                          * an error packet, such as an ICMP destination unreachable */
1606                         if (pinfo->flags.in_error_pkt)
1607                                 break;
1608
1609                         if (tree)
1610                         {
1611                                 hidden_item = proto_tree_add_boolean(radius_tree, hf_radius_rsp, tvb, 0, 0, TRUE);
1612                                 PROTO_ITEM_SET_HIDDEN(hidden_item);
1613                         }
1614
1615                         /* Check for RADIUS response.  A response must match a call that
1616                          * we've seen, and the response must be sent to the same
1617                          * port and address that the call came from.
1618                          *
1619                          * Because it is UDP and the reply can come from any IP
1620                          * and port (not necessarily the request dest), we only
1621                          * track the source IP and port of the request to match
1622                          * the reply.
1623                          */
1624
1625                         /* XXX - can we just use NO_ADDR_B?  Unfortunately,
1626                          * you currently still have to pass a non-null
1627                          * pointer for the second address argument even
1628                          * if you do that.
1629                          */
1630                         conversation = find_conversation(pinfo->fd->num, &null_address,
1631                                 &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
1632                         if (conversation != NULL)
1633                         {
1634                                 /* Look only for matching request, if
1635                                    matching conversation is available. */
1636                                 /* Prepare the key data */
1637                                 radius_call_key.code = rh.rh_code;
1638                                 radius_call_key.ident = rh.rh_ident;
1639                                 radius_call_key.conversation = conversation;
1640                                 radius_call_key.req_time = pinfo->fd->abs_ts;
1641
1642                                 radius_call = (radius_call_t *)g_hash_table_lookup(radius_calls, &radius_call_key);
1643                                 if (radius_call)
1644                                 {
1645                                         /* Indicate the frame to which this is a reply. */
1646                                         if (radius_call->req_num)
1647                                         {
1648                                                 rad_info->request_available = TRUE;
1649                                                 rad_info->req_num = radius_call->req_num;
1650                                                 radius_call->responded = TRUE;
1651
1652                                                 if (tree)
1653                                                 {
1654                                                         nstime_t delta;
1655                                                         proto_item* item;
1656                                                         item = proto_tree_add_uint_format(radius_tree,
1657                                                                 hf_radius_req_frame, tvb, 0, 0,
1658                                                                 radius_call->req_num,
1659                                                                 "This is a response to a request in frame %u",
1660                                                                 radius_call->req_num);
1661                                                         PROTO_ITEM_SET_GENERATED(item);
1662                                                         nstime_delta(&delta, &pinfo->fd->abs_ts, &radius_call->req_time);
1663                                                         item = proto_tree_add_time(radius_tree, hf_radius_time, tvb, 0, 0, &delta);
1664                                                         PROTO_ITEM_SET_GENERATED(item);
1665                                                 }
1666                                         }
1667
1668                                         if (radius_call->rsp_num == 0)
1669                                         {
1670                                                 /* We have not yet seen a response to that call, so
1671                                                    this must be the first response; remember its
1672                                                    frame number. */
1673                                                 radius_call->rsp_num = pinfo->fd->num;
1674                                         }
1675                                         else
1676                                         {
1677                                                 /* We have seen a response to this call - but was it
1678                                                    *this* response? (disregard provisional responses) */
1679                                                 if ( (radius_call->rsp_num != pinfo->fd->num) && (radius_call->rspcode == rh.rh_code) )
1680                                                 {
1681                                                         /* No, so it's a duplicate response. Mark it as such. */
1682                                                         rad_info->is_duplicate = TRUE;
1683                                                         col_append_fstr(pinfo->cinfo, COL_INFO,
1684                                                                         ", Duplicate Response ID:%u", rh.rh_ident);
1685
1686                                                         if (tree)
1687                                                         {
1688                                                                 proto_item* item;
1689                                                                 hidden_item = proto_tree_add_uint(radius_tree,
1690                                                                         hf_radius_dup, tvb, 0,0, rh.rh_ident);
1691                                                                 PROTO_ITEM_SET_HIDDEN(hidden_item);
1692                                                                 item = proto_tree_add_uint(radius_tree,
1693                                                                         hf_radius_rsp_dup, tvb, 0, 0, rh.rh_ident);
1694                                                                 PROTO_ITEM_SET_GENERATED(item);
1695                                                         }
1696                                                 }
1697                                         }
1698                                         /* Now store the response code (after comparison above) */
1699                                         radius_call->rspcode = rh.rh_code;
1700                                         rad_info->rspcode = rh.rh_code;
1701                                 }
1702                         }
1703                         break;
1704                 default:
1705                         break;
1706         }
1707
1708         if (radius_call)
1709         {
1710                 rad_info->req_time.secs = radius_call->req_time.secs;
1711                 rad_info->req_time.nsecs = radius_call->req_time.nsecs;
1712         }
1713
1714         if (avplength > 0)
1715         {
1716                 /* list the attribute value pairs */
1717                 avptf = proto_tree_add_text(radius_tree, tvb, HDR_LENGTH,
1718                         avplength, "Attribute Value Pairs");
1719                 avptree = proto_item_add_subtree(avptf, ett_radius_avp);
1720                 dissect_attribute_value_pairs(avptree, pinfo, tvb, HDR_LENGTH,
1721                         avplength);
1722         }
1723
1724         return tvb_length(tvb);
1725 }
1726
1727
1728 static void register_attrs(gpointer k _U_, gpointer v, gpointer p) {
1729         radius_attr_info_t* a = (radius_attr_info_t *)v;
1730         int i;
1731         gint* ett = &(a->ett);
1732         gchar* abbrev = wmem_strdup_printf(wmem_epan_scope(), "radius.%s", a->name);
1733         hf_register_info hfri[] = {
1734                 { NULL, { NULL,NULL, FT_NONE,  BASE_NONE, NULL, 0x0, NULL, HFILL }},
1735                 { NULL, { NULL,NULL, FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
1736                 { NULL, { NULL,NULL, FT_NONE,  BASE_NONE, NULL, 0x0, NULL, HFILL }},
1737                 { NULL, { NULL,NULL, FT_NONE,  BASE_NONE, NULL, 0x0, NULL, HFILL }}
1738         };
1739         guint len_hf = 2;
1740         hfett_t* ri = (hfett_t *)p;
1741
1742         for(i=0; abbrev[i]; i++) {
1743                 if(abbrev[i] == '-') abbrev[i] = '_';
1744                 if(abbrev[i] == '/') abbrev[i] = '_';
1745         }
1746
1747         hfri[0].p_id = &(a->hf);
1748         hfri[1].p_id = &(a->hf_len);
1749
1750         hfri[0].hfinfo.name = a->name;
1751         hfri[0].hfinfo.abbrev = abbrev;
1752
1753         hfri[1].hfinfo.name = "Length";
1754         hfri[1].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "%s.len", abbrev);
1755         hfri[1].hfinfo.blurb = wmem_strdup_printf(wmem_epan_scope(), "%s Length", a->name);
1756
1757         if (a->type == radius_integer) {
1758                 hfri[0].hfinfo.type = FT_UINT32;
1759                 hfri[0].hfinfo.display = BASE_DEC;
1760
1761                 hfri[2].p_id = &(a->hf_alt);
1762                 hfri[2].hfinfo.name = wmem_strdup(wmem_epan_scope(), a->name);
1763                 hfri[2].hfinfo.abbrev = abbrev;
1764                 hfri[2].hfinfo.type = FT_UINT64;
1765                 hfri[2].hfinfo.display = BASE_DEC;
1766
1767                 if (a->vs) {
1768                         hfri[0].hfinfo.strings = VALS(a->vs);
1769                 }
1770
1771                 len_hf++;
1772         }else if (a->type == radius_signed) {
1773                 hfri[0].hfinfo.type = FT_INT32;
1774                 hfri[0].hfinfo.display = BASE_DEC;
1775
1776                 hfri[2].p_id = &(a->hf_alt);
1777                 hfri[2].hfinfo.name = wmem_strdup(wmem_epan_scope(), a->name);
1778                 hfri[2].hfinfo.abbrev = abbrev;
1779                 hfri[2].hfinfo.type = FT_INT64;
1780                 hfri[2].hfinfo.display = BASE_DEC;
1781
1782                 if (a->vs) {
1783                         hfri[0].hfinfo.strings = VALS(a->vs);
1784                 }
1785
1786                 len_hf++;
1787         } else if (a->type == radius_string) {
1788                 hfri[0].hfinfo.type = FT_STRING;
1789                 hfri[0].hfinfo.display = BASE_NONE;
1790
1791                 if (a->encrypt != 0) {
1792                         /*
1793                          * This attribute is encrypted, so create an
1794                          * alternative field for the encrypted value.
1795                          */
1796                         hfri[2].p_id = &(a->hf_alt);
1797                         hfri[2].hfinfo.name = wmem_strdup_printf(wmem_epan_scope(), "%s (encrypted)", a->name);
1798                         hfri[2].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "%s_encrypted", abbrev);
1799                         hfri[2].hfinfo.type = FT_BYTES;
1800                         hfri[2].hfinfo.display = BASE_NONE;
1801
1802                         len_hf++;
1803                 }
1804         } else if (a->type == radius_octets) {
1805                 hfri[0].hfinfo.type = FT_BYTES;
1806                 hfri[0].hfinfo.display = BASE_NONE;
1807         } else if (a->type == radius_ipaddr) {
1808                 hfri[0].hfinfo.type = FT_IPv4;
1809                 hfri[0].hfinfo.display = BASE_NONE;
1810         } else if (a->type == radius_ipv6addr) {
1811                 hfri[0].hfinfo.type = FT_IPv6;
1812                 hfri[0].hfinfo.display = BASE_NONE;
1813         } else if (a->type == radius_ipv6prefix) {
1814                 hfri[0].hfinfo.type = FT_BYTES;
1815                 hfri[0].hfinfo.display = BASE_NONE;
1816         } else if (a->type == radius_ipxnet) {
1817                 hfri[0].hfinfo.type = FT_IPXNET;
1818                 hfri[0].hfinfo.display = BASE_NONE;
1819         } else if (a->type == radius_date) {
1820                 hfri[0].hfinfo.type = FT_ABSOLUTE_TIME;
1821                 hfri[0].hfinfo.display = ABSOLUTE_TIME_LOCAL;
1822         } else if (a->type == radius_abinary) {
1823                 hfri[0].hfinfo.type = FT_BYTES;
1824                 hfri[0].hfinfo.display = BASE_NONE;
1825         } else if (a->type == radius_ifid) {
1826                 hfri[0].hfinfo.type = FT_BYTES;
1827                 hfri[0].hfinfo.display = BASE_NONE;
1828         } else if (a->type == radius_combo_ip) {
1829                 hfri[0].hfinfo.type = FT_IPv4;
1830                 hfri[0].hfinfo.display = BASE_NONE;
1831
1832                 hfri[2].p_id = &(a->hf_alt);
1833                 hfri[2].hfinfo.name = wmem_strdup(wmem_epan_scope(), a->name);
1834                 hfri[2].hfinfo.abbrev = wmem_strdup(wmem_epan_scope(), abbrev);
1835                 hfri[2].hfinfo.type = FT_IPv6;
1836                 hfri[2].hfinfo.display = BASE_NONE;
1837
1838                 len_hf++;
1839         } else if (a->type == radius_tlv) {
1840                 hfri[0].hfinfo.type = FT_BYTES;
1841                 hfri[0].hfinfo.display = BASE_NONE;
1842         } else {
1843                 hfri[0].hfinfo.type = FT_BYTES;
1844                 hfri[0].hfinfo.display = BASE_NONE;
1845         }
1846
1847         if (a->tagged) {
1848                 hfri[len_hf].p_id = &(a->hf_tag);
1849                 hfri[len_hf].hfinfo.name = "Tag";
1850                 hfri[len_hf].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "%s.tag", abbrev);
1851                 hfri[len_hf].hfinfo.blurb = wmem_strdup_printf(wmem_epan_scope(), "%s Tag", a->name);
1852                 hfri[len_hf].hfinfo.type = FT_UINT8;
1853                 hfri[len_hf].hfinfo.display = BASE_HEX;
1854                 len_hf++;
1855         }
1856
1857         wmem_array_append(ri->hf,hfri,len_hf);
1858         wmem_array_append_one(ri->ett,ett);
1859
1860         if (a->tlvs_by_id) {
1861                 g_hash_table_foreach(a->tlvs_by_id,register_attrs,ri);
1862         }
1863 }
1864
1865 static void register_vendors(gpointer k _U_, gpointer v, gpointer p) {
1866         radius_vendor_info_t* vnd = (radius_vendor_info_t *)v;
1867         hfett_t* ri = (hfett_t *)p;
1868         value_string vnd_vs;
1869         gint* ett_p = &(vnd->ett);
1870
1871         vnd_vs.value = vnd->code;
1872         vnd_vs.strptr = vnd->name;
1873
1874         wmem_array_append_one(ri->vend_vs,vnd_vs);
1875         wmem_array_append_one(ri->ett,ett_p);
1876
1877         g_hash_table_foreach(vnd->attrs_by_id,register_attrs,ri);
1878
1879 }
1880
1881 extern void radius_register_avp_dissector(guint32 vendor_id, guint32 attribute_id, radius_avp_dissector_t radius_avp_dissector) {
1882         radius_vendor_info_t* vendor;
1883         radius_attr_info_t* dictionary_entry;
1884         GHashTable* by_id;
1885
1886         DISSECTOR_ASSERT(radius_avp_dissector != NULL);
1887
1888         if (vendor_id) {
1889                 vendor = (radius_vendor_info_t *)g_hash_table_lookup(dict->vendors_by_id,GUINT_TO_POINTER(vendor_id));
1890
1891                 if ( ! vendor ) {
1892                         vendor = (radius_vendor_info_t *)g_malloc(sizeof(radius_vendor_info_t));
1893
1894                         vendor->name = g_strdup_printf("%s-%u",
1895                                                        val_to_str_ext_const(vendor_id, &sminmpec_values_ext, "Unknown"),
1896                                                        vendor_id);
1897                         vendor->code = vendor_id;
1898                         vendor->attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
1899                         vendor->ett = no_vendor.ett;
1900
1901                         /* XXX: Default "standard" values: Should be parameters ?  */
1902                         vendor->type_octets   = 1;
1903                         vendor->length_octets = 1;
1904                         vendor->has_flags     = FALSE;
1905
1906                         g_hash_table_insert(dict->vendors_by_id,GUINT_TO_POINTER(vendor->code),vendor);
1907                         g_hash_table_insert(dict->vendors_by_name,(gpointer)(vendor->name),vendor);
1908                 }
1909
1910                 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(vendor->attrs_by_id,GUINT_TO_POINTER(attribute_id));
1911                 by_id = vendor->attrs_by_id;
1912         } else {
1913                 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(dict->attrs_by_id,GUINT_TO_POINTER(attribute_id));
1914                 by_id = dict->attrs_by_id;
1915         }
1916
1917         if (!dictionary_entry) {
1918                 dictionary_entry = (radius_attr_info_t *)g_malloc(sizeof(radius_attr_info_t));
1919
1920                 dictionary_entry->name = g_strdup_printf("Unknown-Attribute-%u",attribute_id);
1921                 dictionary_entry->code = attribute_id;
1922                 dictionary_entry->encrypt = 0;
1923                 dictionary_entry->type = NULL;
1924                 dictionary_entry->vs = NULL;
1925                 dictionary_entry->hf = no_dictionary_entry.hf;
1926                 dictionary_entry->tagged = 0;
1927                 dictionary_entry->hf_tag = -1;
1928                 dictionary_entry->hf_len = no_dictionary_entry.hf_len;
1929                 dictionary_entry->ett = no_dictionary_entry.ett;
1930                 dictionary_entry->tlvs_by_id = NULL;
1931
1932                 g_hash_table_insert(by_id,GUINT_TO_POINTER(dictionary_entry->code),dictionary_entry);
1933         }
1934
1935         dictionary_entry->dissector = radius_avp_dissector;
1936
1937 }
1938
1939 /* Discard and init any state we've saved */
1940 static void
1941 radius_init_protocol(void)
1942 {
1943         if (radius_calls != NULL)
1944         {
1945                 g_hash_table_destroy(radius_calls);
1946                 radius_calls = NULL;
1947         }
1948
1949         radius_calls = g_hash_table_new(radius_call_hash, radius_call_equal);
1950 }
1951
1952 static void register_radius_fields(const char* unused _U_) {
1953          hf_register_info base_hf[] = {
1954                  { &hf_radius_req,
1955                  { "Request", "radius.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1956                          "TRUE if RADIUS request", HFILL }},
1957                  { &hf_radius_rsp,
1958                  { "Response", "radius.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1959                          "TRUE if RADIUS response", HFILL }},
1960                  { &hf_radius_req_frame,
1961                  { "Request Frame", "radius.reqframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
1962                          NULL, HFILL }},
1963                  { &hf_radius_rsp_frame,
1964                  { "Response Frame", "radius.rspframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
1965                          NULL, HFILL }},
1966                  { &hf_radius_time,
1967                  { "Time from request", "radius.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
1968                          "Timedelta between Request and Response", HFILL }},
1969                  { &hf_radius_code,
1970                  { "Code","radius.code", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &radius_pkt_type_codes_ext, 0x0,
1971                          NULL, HFILL }},
1972                  { &hf_radius_id,
1973                  { "Identifier",        "radius.id", FT_UINT8, BASE_DEC, NULL, 0x0,
1974                          NULL, HFILL }},
1975                  { &hf_radius_authenticator,
1976                  { "Authenticator",     "radius.authenticator", FT_BYTES, BASE_NONE, NULL, 0x0,
1977                          NULL, HFILL }},
1978                  { &hf_radius_length,
1979                  { "Length","radius.length", FT_UINT16, BASE_DEC, NULL, 0x0,
1980                          NULL, HFILL }},
1981                  { &(no_dictionary_entry.hf),
1982                  { "Unknown-Attribute","radius.Unknown_Attribute", FT_BYTES, BASE_NONE, NULL, 0x0,
1983                          NULL, HFILL }},
1984                  { &(no_dictionary_entry.hf_len),
1985                  { "Unknown-Attribute Length","radius.Unknown_Attribute.length", FT_UINT8, BASE_DEC, NULL, 0x0,
1986                          NULL, HFILL }},
1987                  { &hf_radius_chap_password,
1988                  { "CHAP-Password","radius.CHAP_Password", FT_BYTES, BASE_NONE, NULL, 0x0,
1989                          NULL, HFILL }},
1990                  { &hf_radius_chap_ident,
1991                  { "CHAP Ident","radius.CHAP_Ident", FT_UINT8, BASE_HEX, NULL, 0x0,
1992                          NULL, HFILL }},
1993                  { &hf_radius_chap_string,
1994                  { "CHAP String","radius.CHAP_String", FT_BYTES, BASE_NONE, NULL, 0x0,
1995                          NULL, HFILL }},
1996                  { &hf_radius_framed_ip_address,
1997                  { "Framed-IP-Address","radius.Framed-IP-Address", FT_IPv4, BASE_NONE, NULL, 0x0,
1998                          NULL, HFILL }},
1999                  { &hf_radius_login_ip_host,
2000                  { "Login-IP-Host","radius.Login-IP-Host", FT_IPv4, BASE_NONE, NULL, 0x0,
2001                          NULL, HFILL }},
2002                  { &hf_radius_framed_ipx_network,
2003                  { "Framed-IPX-Network","radius.Framed-IPX-Network", FT_IPXNET, BASE_NONE, NULL, 0x0,
2004                          NULL, HFILL }},
2005                  { &hf_radius_cosine_vpi,
2006                  { "Cosine-VPI","radius.Cosine-Vpi", FT_UINT16, BASE_DEC, NULL, 0x0,
2007                          NULL, HFILL }},
2008                  { &hf_radius_cosine_vci,
2009                  { "Cosine-VCI","radius.Cosine-Vci", FT_UINT16, BASE_DEC, NULL, 0x0,
2010                          NULL, HFILL }},
2011                  { &hf_radius_dup,
2012                  { "Duplicate Message", "radius.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
2013                          NULL, HFILL }},
2014                  { &hf_radius_req_dup,
2015                  { "Duplicate Request", "radius.req.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
2016                          NULL, HFILL }},
2017                  { &hf_radius_rsp_dup,
2018                  { "Duplicate Response", "radius.rsp.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
2019                          NULL, HFILL }},
2020                  { &hf_radius_ascend_data_filter,
2021                  { "Ascend Data Filter", "radius.ascenddatafilter", FT_BYTES, BASE_NONE, NULL, 0x0,
2022                          NULL, HFILL }}
2023          };
2024
2025          gint *base_ett[] = {
2026                  &ett_radius,
2027                  &ett_radius_avp,
2028                  &ett_eap,
2029                  &ett_chap,
2030                  &(no_dictionary_entry.ett),
2031                  &(no_vendor.ett),
2032          };
2033
2034          hfett_t ri;
2035          char* dir = NULL;
2036          gchar* dict_err_str = NULL;
2037
2038          ri.hf = wmem_array_new(wmem_epan_scope(), sizeof(hf_register_info));
2039          ri.ett = wmem_array_new(wmem_epan_scope(), sizeof(gint *));
2040          ri.vend_vs = wmem_array_new(wmem_epan_scope(), sizeof(value_string));
2041
2042          wmem_array_append(ri.hf, base_hf, array_length(base_hf));
2043          wmem_array_append(ri.ett, base_ett, array_length(base_ett));
2044
2045          dir = get_persconffile_path("radius", FALSE);
2046
2047          if (test_for_directory(dir) != EISDIR) {
2048                  /* Although dir isn't a directory it may still use memory */
2049                  g_free(dir);
2050
2051                  dir = get_datafile_path("radius");
2052
2053                  if (test_for_directory(dir) != EISDIR) {
2054                          g_free(dir);
2055                          dir = NULL;
2056                  }
2057          }
2058
2059         if (dir) {
2060                 radius_load_dictionary(dict,dir,"dictionary",&dict_err_str);
2061
2062                 if (dict_err_str) {
2063                         report_failure("radius: %s",dict_err_str);
2064                         g_free(dict_err_str);
2065                 }
2066
2067                 g_hash_table_foreach(dict->attrs_by_id,register_attrs,&ri);
2068                 g_hash_table_foreach(dict->vendors_by_id,register_vendors,&ri);
2069         }
2070
2071         g_free(dir);
2072
2073         proto_register_field_array(proto_radius,(hf_register_info*)wmem_array_get_raw(ri.hf),wmem_array_get_count(ri.hf));
2074         proto_register_subtree_array((gint**)wmem_array_get_raw(ri.ett), wmem_array_get_count(ri.ett));
2075
2076         no_vendor.attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
2077
2078         /*
2079          * Handle attributes that have a special format.
2080          */
2081         radius_register_avp_dissector(0,3,dissect_chap_password);
2082         radius_register_avp_dissector(0,8,dissect_framed_ip_address);
2083         radius_register_avp_dissector(0,14,dissect_login_ip_host);
2084         radius_register_avp_dissector(0,23,dissect_framed_ipx_network);
2085         radius_register_avp_dissector(VENDOR_COSINE,5,dissect_cosine_vpvc);
2086         /*
2087          * XXX - should we just call dissect_ascend_data_filter()
2088          * in radius_abinary()?
2089          *
2090          * Note that there is no attribute 242 in dictionary.redback.
2091          */
2092         radius_register_avp_dissector(VENDOR_ASCEND,242,dissect_ascend_data_filter);
2093         radius_register_avp_dissector(VENDOR_REDBACK,242,dissect_ascend_data_filter);
2094         radius_register_avp_dissector(0,242,dissect_ascend_data_filter);
2095
2096         /*
2097          * XXX - we should special-case Cisco attribute 252; see the comment in
2098          * dictionary.cisco.
2099          */
2100         radius_register_avp_dissector(VENDOR_THE3GPP,23,dissect_radius_3gpp_ms_tmime_zone);
2101 }
2102
2103
2104 void
2105 proto_register_radius(void)
2106 {
2107         module_t *radius_module;
2108
2109         proto_radius = proto_register_protocol("Radius Protocol", "RADIUS", "radius");
2110         new_register_dissector("radius", dissect_radius, proto_radius);
2111         register_init_routine(&radius_init_protocol);
2112         radius_module = prefs_register_protocol(proto_radius, proto_reg_handoff_radius);
2113         prefs_register_string_preference(radius_module,"shared_secret","Shared Secret",
2114                                          "Shared secret used to decode User Passwords",
2115                                          &shared_secret);
2116         prefs_register_bool_preference(radius_module,"show_length","Show AVP Lengths",
2117                                        "Whether to add or not to the tree the AVP's payload length",
2118                                        &show_length);
2119         prefs_register_uint_preference(radius_module, "alternate_port","Alternate Port",
2120                                        "An alternate UDP port to decode as RADIUS", 10, &alt_port_pref);
2121         prefs_register_uint_preference(radius_module, "request_ttl", "Request TimeToLive",
2122                                        "Time to live for a radius request used for matching it with a response", 10, &request_ttl);
2123         radius_tap = register_tap("radius");
2124         proto_register_prefix("radius",register_radius_fields);
2125
2126         dict = (radius_dictionary_t *)g_malloc(sizeof(radius_dictionary_t));
2127         dict->attrs_by_id     = g_hash_table_new(g_direct_hash,g_direct_equal);
2128         dict->attrs_by_name   = g_hash_table_new(g_str_hash,g_str_equal);
2129         dict->vendors_by_id   = g_hash_table_new(g_direct_hash,g_direct_equal);
2130         dict->vendors_by_name = g_hash_table_new(g_str_hash,g_str_equal);
2131         dict->tlvs_by_name    = g_hash_table_new(g_str_hash,g_str_equal);
2132 }
2133
2134 void
2135 proto_reg_handoff_radius(void)
2136 {
2137         static gboolean initialized = FALSE;
2138         static dissector_handle_t radius_handle;
2139         static guint alt_port;
2140
2141         if (!initialized) {
2142                 radius_handle = find_dissector("radius");
2143                 dissector_add_uint("udp.port", UDP_PORT_RADIUS, radius_handle);
2144                 dissector_add_uint("udp.port", UDP_PORT_RADIUS_NEW, radius_handle);
2145                 dissector_add_uint("udp.port", UDP_PORT_RADACCT, radius_handle);
2146                 dissector_add_uint("udp.port", UDP_PORT_RADACCT_NEW, radius_handle);
2147                 dissector_add_uint("udp.port", UDP_PORT_DAE_OLD, radius_handle);
2148                 dissector_add_uint("udp.port", UDP_PORT_DAE, radius_handle);
2149
2150                 eap_handle = find_dissector("eap");
2151
2152                 initialized = TRUE;
2153         } else {
2154                 if (alt_port != 0)
2155                         dissector_delete_uint("udp.port", alt_port, radius_handle);
2156         }
2157
2158         if (alt_port_pref != 0)
2159                 dissector_add_uint("udp.port", alt_port_pref, radius_handle);
2160
2161         alt_port = alt_port_pref;
2162 }
2163
2164 /*
2165  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
2166  *
2167  * Local variables:
2168  * c-basic-offset: 8
2169  * tab-width: 8
2170  * indent-tabs-mode: t
2171  * End:
2172  *
2173  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
2174  * :indentSize=8:tabSize=8:noTabs=false:
2175  */