--- a/epan/dissectors/packet-stun2.c	2008-10-20 21:19:32.000000000 +0200
+++ b/epan/dissectors/packet-stun2.c	2008-12-10 12:07:50.000000000 +0100
@@ -3,6 +3,7 @@
  * Copyright 2003, Shiang-Ming Huang <smhuang@pcs.csie.nctu.edu.tw>
  * Copyright 2006, Marc Petit-Huguenin <marc@petit-huguenin.org>
  * Copyright 2007, 8x8 Inc. <petithug@8x8.com>
+ * Copyright 2008-2009, Sebastien Vincent <sebastien.vincent@turnserver.org>
  *
  * $Id: packet-stun2.c 23791 2007-12-07 13:26:15Z martinm $
  *
@@ -24,7 +25,9 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
- * Please refer to draft-ietf-behave-rfc3489bis-07 for protocol detail.
+ * Please refer to rfc5389 for STUN protocol detail.
+ * Please refer to draft-ietf-behave-turn-13 and draft-ietf-behave-turn-ipv6-06 
+ * for TURN protocol detail.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -69,7 +72,19 @@
 static int stun2_att_xor_ipv4 = -1;
 static int stun2_att_xor_ipv6 = -1;
 static int stun2_att_xor_port = -1;
-static int stun2_att_server = -1;
+static int stun2_att_software = -1;
+
+/* TURN */
+static int stun2_att_channel_number = -1;
+static int stun2_att_channel_number_rffu = -1;
+static int stun2_att_lifetime = -1;
+static int stun2_att_data = -1;
+static int stun2_att_even_port_r_flag = -1;
+static int stun2_att_requested_transport_protocol = -1;
+static int stun2_att_requested_transport_reserved = -1;
+static int stun2_att_requested_address_family = -1;
+static int stun2_att_requested_address_family_reserved = -1;
+static int stun2_att_reservation_token = -1;
 static int stun2_att_value = -1;
 
 /* Message classes */
@@ -81,20 +96,44 @@
 
 /* Request/Response Transactions */
 #define METHOD_MASK		0xCEEF
-#define BINDING			0x0001	/* draft-ietf-behave-rfc3489bis-07 */
+#define BINDING     0x0001  /* rfc5389 */
+
+/* TURN Request/Response Transactions */
+#define ALLOCATE    0x0003 /* draft-ietf-behave-turn-13 */
+#define REFRESH     0x0004 /* draft-ietf-behave-turn-13 */
+#define CREATEPERMISSION 0x0008 /* draft-ietf-behave-turn-13 */
+#define CHANNELBIND 0x0009 /* draft-ietf-behave-turn-13 */
+
+/* TURN Indications */
+#define SEND        0x0006 /* draft-ietf-behave-turn-13 */
+#define DATA_INDICATION        0x0007 /* draft-ietf-behave-turn-13 */
+
+ /* Attribute Types */
+#define MAPPED_ADDRESS    0x0001  /* rfc5389 */
+#define USERNAME    0x0006  /* rfc5389 */
+#define MESSAGE_INTEGRITY 0x0008  /* rfc5389 */
+#define ERROR_CODE    0x0009  /* rfc5389 */
+#define UNKNOWN_ATTRIBUTES  0x000a  /* rfc5389 */
+#define REALM     0x0014  /* rfc5389 */
+#define NONCE     0x0015  /* rfc5389 */
+#define XOR_MAPPED_ADDRESS  0x0020  /* rfc5389 */
+#define SOFTWARE      0x8022  /* rfc5389 */
+#define ALTERNATE_SERVER  0x8023  /* rfc5389 */
+#define FINGERPRINT   0x8028  /* rfc5389 */
+
+/* TURN attribute types */
+#define CHANNEL_NUMBER        0x000c /* draft-ietf-behave-turn-13 */
+#define LIFETIME              0x000d /* draft-ietf-behave-turn-13 */
+#define DONT_FRAGMENT         0x001A /* draft-ietf-behave-turn-13 */
+#define PEER_ADDRESS          0x0012 /* draft-ietf-behave-turn-13 */
+#define DATA                  0x0013 /* draft-ietf-behave-turn-13 */
+#define RELAYED_ADDRESS       0x0016 /* draft-ietf-behave-turn-13 */
+#define EVEN_PORT             0x0018 /* draft-ietf-behave-turn-13 */
+#define REQUESTED_TRANSPORT   0x0019 /* draft-ietf-behave-turn-13 */
+#define RESERVATION_TOKEN     0x0022 /* draft-ietf-behave-turn-13 */
+#define REQUESTED_ADDRESS_FAMILY 0x0017 /* draft-ietf-behave-turn-ipv6-06 */
 
-/* Attribute Types */
-#define MAPPED_ADDRESS		0x0001	/* draft-ietf-behave-rfc3489bis-07 */
-#define USERNAME		0x0006	/* draft-ietf-behave-rfc3489bis-07 */
-#define MESSAGE_INTEGRITY	0x0008	/* draft-ietf-behave-rfc3489bis-07 */
-#define ERROR_CODE		0x0009	/* draft-ietf-behave-rfc3489bis-07 */
-#define UNKNOWN_ATTRIBUTES	0x000a	/* draft-ietf-behave-rfc3489bis-07 */
-#define REALM			0x0014	/* draft-ietf-behave-rfc3489bis-07 */
-#define NONCE			0x0015	/* draft-ietf-behave-rfc3489bis-07 */
-#define XOR_MAPPED_ADDRESS	0x0020	/* draft-ietf-behave-rfc3489bis-07 */
-#define SERVER			0x8022	/* draft-ietf-behave-rfc3489bis-07 */
-#define ALTERNATE_SERVER	0x8023	/* draft-ietf-behave-rfc3489bis-07 */
-#define FINGERPRINT		0x8028	/* draft-ietf-behave-rfc3489bis-07 */
+#define MAGIC_COOKIE 0x2112A442
 
 /* Initialize the subtree pointers */
 static gint ett_stun2 = -1;
@@ -118,6 +157,12 @@
 
 static const value_string methods[] = {
 	{BINDING, "Binding"},
+  {ALLOCATE, "Allocate"},
+  {REFRESH, "Refresh"},
+  {CHANNELBIND, "ChannelBind"},
+  {CREATEPERMISSION, "CreatePermission"},
+  {SEND, "Send"},
+  {DATA_INDICATION, "Data"}, 
 	{0x00, NULL}
 };
 
@@ -130,9 +175,19 @@
 	{REALM, "REALM"},
 	{NONCE, "NONCE"},
 	{XOR_MAPPED_ADDRESS, "XOR-MAPPED-ADDRESS"},
-	{SERVER, "SERVER"},
+	{SOFTWARE, "SOFTWARE"},
 	{ALTERNATE_SERVER, "ALTERNATE-SERVER"},
 	{FINGERPRINT, "FINGERPRINT"},
+  {CHANNEL_NUMBER, "CHANNEL-NUMBER"},
+  {LIFETIME, "LIFETIME"},
+  {PEER_ADDRESS, "PEER-ADDRESS"},
+  {DATA, "DATA"},
+  {RELAYED_ADDRESS, "RELAYED-ADDRESS"},
+  {EVEN_PORT, "EVEN-PORT"},
+  {REQUESTED_TRANSPORT, "REQUESTED-TRANSPORT"},
+  {DONT_FRAGMENT, "DONT-FRAGMENT"},
+  {RESERVATION_TOKEN, "RESERVATION-TOKEN"},
+  {REQUESTED_ADDRESS_FAMILY, "REQUESTED-ADDRESS-FAMILY"},
 	{0x00, NULL}
 };
 
@@ -142,6 +197,12 @@
 	{0x00, NULL}
 };
 
+static const value_string transport_protocol[] = {
+  {0x11, "UDP"},
+  {0x06, "TCP"},
+  {0x00, NULL}
+};
+
 static guint
 get_stun2_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
 {
@@ -164,7 +225,8 @@
 	guint16 att_length;
 	guint16 offset;
 	guint i;
-	guint transaction_id_first_word;
+	char transaction_id[12];
+  uint32_t magic_cookie = g_htonl(MAGIC_COOKIE);
 	guint   len;
 
 	/*
@@ -180,7 +242,7 @@
 	msg_length = tvb_get_ntohs(tvb, 2);
 
 	/* Check if it is really a STUN2 message */
-	if (msg_type & 0xC000 || tvb_get_ntohl(tvb, 4) != 0x2112a442)
+	if (msg_type & 0xC000 || tvb_get_ntohl(tvb, 4) != MAGIC_COOKIE)
 		return FALSE;
 
 	/* check if payload enough */
@@ -214,9 +276,8 @@
 	proto_tree_add_item(stun2_tree, hf_stun2_cookie, tvb, 4, 4, FALSE);
 	proto_tree_add_item(stun2_tree, hf_stun2_id, tvb, 8, 12, FALSE);
 
-	/* Remember this (in host order) so we can show clear xor'd addresses */
-	/* TODO IPv6 support */
-	transaction_id_first_word = tvb_get_ntohl(tvb, 4);
+	/* Remember this (in network byte order) so we can show clear xor'd addresses */
+	tvb_memcpy(tvb, transaction_id, 8, 12);
 
 	if (msg_length > 0) {
 	    ta = proto_tree_add_item(stun2_tree, hf_stun2_att, tvb, STUN2_HDR_LEN, msg_length, FALSE);
@@ -317,6 +378,8 @@
 					proto_tree_add_uint(att_tree, stun2_att_padding, tvb, offset+att_length, 4-(att_length % 4), 4-(att_length % 4));
 				break;
 
+      case PEER_ADDRESS:
+      case RELAYED_ADDRESS:
 			case XOR_MAPPED_ADDRESS:
 				if (att_length < 2)
 					break;
@@ -326,11 +389,12 @@
 				proto_tree_add_item(att_tree, stun2_att_xor_port, tvb, offset+2, 2, FALSE);
 
 				/* Show the port 'in the clear'
-				   XOR (host order) transid with (host order) xor-port.
+				   XOR most significant 16 bit of the cookie with (host order) xor-port.
 				   Add host-order port into tree. */
 				ti = proto_tree_add_uint(att_tree, stun2_att_port, tvb, offset+2, 2,
 										 tvb_get_ntohs(tvb, offset+2) ^
-										 (transaction_id_first_word >> 16));
+										 ((((guint8*)&magic_cookie)[0] << 8) | ((guint8*)&magic_cookie)[1]));
+                      /* (transaction_id_first_word >> 16)); */
 				PROTO_ITEM_SET_GENERATED(ti);
 
 				if (att_length < 8)
@@ -345,22 +409,43 @@
 						   XOR (host order) transid with (host order) xor-address.
 						   Add in network order tree. */
 						ti = proto_tree_add_ipv4(att_tree, stun2_att_ipv4, tvb, offset+4, 4,
-												 g_htonl(tvb_get_ntohl(tvb, offset+4) ^
-												 transaction_id_first_word));
+												 g_htonl(tvb_get_ntohl(tvb, offset+4) ^ 
+                         MAGIC_COOKIE));
+												 /* transaction_id_first_word)); */
 						PROTO_ITEM_SET_GENERATED(ti);
 						break;
 
 					case 2:
-						if (att_length < 20)
-							break;
-						/* TODO add IPv6 */
-						proto_tree_add_item(att_tree, stun2_att_xor_ipv6, tvb, offset+4, 16, FALSE);
+            {
+              char ipv6[16];
+              guint32 cookie = g_htonl(MAGIC_COOKIE);
+              guint8* ptr = (guint8*)&cookie;
+              unsigned int i = 0;
+          
+              tvb_memcpy(tvb, ipv6, offset+4, 16);
+
+              /* XOR with cookie */
+              for(i = 0 ; i < 4 ; i++)
+              {
+                ipv6[i] ^= ptr[i];
+              }
+
+              /* XOR with the transaction ID */
+              for(i = 4 ; i < 16 ; i++)
+              {
+                ipv6[i] ^= transaction_id[i - 4];
+              }
+
+  						if (att_length < 20)
+  							break;
+  						proto_tree_add_ipv6(att_tree, stun2_att_xor_ipv6, tvb, offset+4, 16, ipv6);
+            }
 						break;
 					}
 				break;
 
-			case SERVER:
-				proto_tree_add_item(att_tree, stun2_att_server, tvb, offset, att_length, FALSE);
+			case SOFTWARE:
+				proto_tree_add_item(att_tree, stun2_att_software, tvb, offset, att_length, FALSE);
 				if (att_length % 4 != 0)
 					proto_tree_add_uint(att_tree, stun2_att_padding, tvb, offset+att_length, 4-(att_length % 4), 4-(att_length % 4));
 				break;
@@ -371,6 +456,74 @@
 				proto_tree_add_item(att_tree, stun2_att_crc32, tvb, offset, att_length, FALSE);
 				break;
 
+      case CHANNEL_NUMBER:
+        if (att_length < 4)
+          break;
+        {
+          guint32 number = 0;
+          /* number */
+          tvb_memcpy(tvb, (guint8*)&number, offset, sizeof(guint16));
+          proto_tree_add_uint(att_tree, stun2_att_channel_number, tvb, offset, 2, g_ntohs(number));
+
+          /* RFFU */
+          tvb_memcpy(tvb, (guint8*)&number, offset+2, sizeof(guint16));
+          proto_tree_add_uint(att_tree, stun2_att_channel_number_rffu, tvb, offset+2, 2, number);
+        }
+        break;
+
+      case LIFETIME:
+        if (att_length < 4)
+          break;
+        {
+          guint32 lifetime = 0;
+          tvb_memcpy(tvb, (guint8*)&lifetime, offset, sizeof(guint32));
+          proto_tree_add_uint(att_tree, stun2_att_lifetime, tvb, offset, 4, g_ntohl(lifetime));
+        }
+        break;
+
+      case DATA:
+        if (att_length > 0)
+          proto_tree_add_item(att_tree, stun2_att_value, tvb, offset, att_length, FALSE);
+        if (att_length % 4 != 0)
+          proto_tree_add_uint(att_tree, stun2_att_padding, tvb, offset+att_length, 4-(att_length % 4), 4-(att_length % 4));
+        break;
+
+     case EVEN_PORT:
+        if (att_length < 4)
+          break;
+        {
+          guint8 flags = 0;
+          tvb_memcpy(tvb, (guint8*)&flags, offset, sizeof(guint8));
+          proto_tree_add_uint(att_tree, stun2_att_even_port_r_flag, tvb, offset, 1, flags);
+        }
+
+        break;
+     case REQUESTED_TRANSPORT:
+        if (att_length < 4)
+          break;
+        /* protocol */
+        proto_tree_add_item(att_tree, stun2_att_requested_transport_protocol, tvb, offset, 1, FALSE);
+
+        /* reserved */
+        proto_tree_add_item(att_tree, stun2_att_requested_transport_reserved, tvb, offset+1, 3, FALSE);
+        break;
+
+     case REQUESTED_ADDRESS_FAMILY:
+        if(att_length < 4)
+          break;
+        /* address type */
+        proto_tree_add_item(att_tree, stun2_att_requested_address_family, tvb, offset, 1, FALSE);
+
+        /* reserved */
+        proto_tree_add_item(att_tree, stun2_att_requested_address_family_reserved, tvb, offset+1, 3, FALSE);
+        break;
+
+     case RESERVATION_TOKEN:
+        if (att_length < 8)
+          break;
+        proto_tree_add_item(att_tree, stun2_att_reservation_token, tvb, offset, att_length, FALSE);
+        break;
+
 			default:
 				if (att_length > 0)
 					proto_tree_add_item(att_tree, stun2_att_value, tvb, offset, att_length, FALSE);
@@ -421,7 +574,7 @@
 	msg_length = tvb_get_ntohs(tvb, 2);
 
 	/* Check if it is really a STUN2 message */
-	if (msg_type & 0xC000 || tvb_get_ntohl(tvb, 4) != 0x2112a442)
+	if (msg_type & 0xC000 || tvb_get_ntohl(tvb, 4) != MAGIC_COOKIE)
 		return FALSE;
 
 	/* check if payload enough */
@@ -526,21 +679,62 @@
 			BASE_HEX, 	NULL,	0x0,	"",	HFILL}
 		},
 		{ &stun2_att_xor_ipv4,
-			{ "IP (XOR-d)",		"stun2.att.ipv4-xord",	FT_IPv4,
+			{ "IPv4 (XOR-d)",		"stun2.att.ipv4-xord",	FT_IPv4,
 			BASE_NONE,	NULL,	0x0, 	"",	HFILL }
 		},
 		{ &stun2_att_xor_ipv6,
-			{ "IP (XOR-d)",		"stun2.att.ipv6-xord",	FT_IPv6,
+			{ "IPv6 (XOR-d)",		"stun2.att.ipv6-xord",	FT_IPv6,
 			BASE_NONE,	NULL,	0x0, 	"",	HFILL }
 		},
 		{ &stun2_att_xor_port,
 			{ "Port (XOR-d)",	"stun2.att.port-xord",	FT_UINT16,
 			BASE_DEC,	NULL,	0x0, 	"",	HFILL }
 		},
-		{ &stun2_att_server,
-			{ "Server software","stun2.att.server",	FT_STRING,
+		{ &stun2_att_software,
+			{ "Software","stun2.att.software",	FT_STRING,
 			BASE_NONE, 	NULL,	0x0,	"",	HFILL}
  		},
+    /* TURN */
+    { &stun2_att_channel_number, 
+      {"Channel", "stun2.att.channel_number", FT_UINT16, 
+      BASE_DEC, NULL, 0x00, "", HFILL }
+    },
+    { &stun2_att_channel_number_rffu, 
+      {"RFFU", "stun2.att.channel_number_rffu", FT_UINT16, 
+      BASE_DEC, NULL, 0x00, "", HFILL }
+    },
+    { &stun2_att_lifetime, 
+      {"Lifetime", "stun2.att.lifetime", FT_UINT32, 
+      BASE_DEC, NULL, 0x00, "", HFILL }
+    },
+    { &stun2_att_data, 
+      {"Data", "stun2.att.data", FT_BYTES, 
+      BASE_HEX, NULL, 0x00, "", HFILL }
+    },
+    { &stun2_att_even_port_r_flag, 
+      { "R flag", "stun2.att.even_port.r_flag", FT_UINT32,
+      BASE_HEX, NULL, 0x80, "", HFILL}
+    },
+    { &stun2_att_requested_transport_protocol,
+      { "Requested transport protocol", "stun2.att.requested_transport.protocol", FT_UINT8,
+      BASE_HEX, VALS(transport_protocol), 0x00, "", HFILL}
+    },
+    { &stun2_att_requested_transport_reserved,
+      { "Reserved", "stun2.att.requested_transport.reserved", FT_BYTES,
+      BASE_HEX, NULL, 0x00, "", HFILL}
+    },
+    { &stun2_att_requested_address_family,
+      { "Address type", "stun2.att.requested_address_family.family", FT_UINT8, BASE_HEX, 
+        VALS(attributes_family), 0x00, "", HFILL}
+    },
+    { &stun2_att_requested_address_family_reserved,
+      { "Reserved", "stun2.att.requested_address_family.reserved", FT_BYTES,
+      BASE_HEX, NULL, 0x00, "", HFILL}
+    },
+    { &stun2_att_reservation_token,
+      { "Reservation token", "stun2.att.reservation_token", FT_BYTES,
+        BASE_HEX, NULL, 0x0, "", HFILL}
+    },
 		{ &stun2_att_value,
 			{ "Value",	"stun2.value",	FT_BYTES,
 			BASE_HEX,	NULL,	0x0, 	"",	HFILL }
