Index: /pjproject/trunk/pjnath/build/pjnath.dsp
===================================================================
--- /pjproject/trunk/pjnath/build/pjnath.dsp	(revision 1092)
+++ /pjproject/trunk/pjnath/build/pjnath.dsp	(revision 1093)
@@ -96,4 +96,8 @@
 # Begin Source File
 
+SOURCE=..\src\pjnath\ice_mt.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\src\pjnath\stun_auth.c
 # End Source File
@@ -129,4 +133,8 @@
 
 SOURCE=..\include\pjnath\ice.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\pjnath\ice_mt.h
 # End Source File
 # Begin Source File
Index: /pjproject/trunk/pjnath/build/pjnath.dsw
===================================================================
--- /pjproject/trunk/pjnath/build/pjnath.dsw	(revision 1092)
+++ /pjproject/trunk/pjnath/build/pjnath.dsw	(revision 1093)
@@ -36,4 +36,25 @@
 Package=<4>
 {{{
+}}}
+
+###############################################################################
+
+Project: "pjnath_test"=.\pjnath_test.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name pjlib
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjlib_util
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjnath
+    End Project Dependency
 }}}
 
Index: /pjproject/trunk/pjnath/build/pjnath_test.dsp
===================================================================
--- /pjproject/trunk/pjnath/build/pjnath_test.dsp	(revision 1093)
+++ /pjproject/trunk/pjnath/build/pjnath_test.dsp	(revision 1093)
@@ -0,0 +1,118 @@
+# Microsoft Developer Studio Project File - Name="pjnath_test" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=pjnath_test - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "pjnath_test.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pjnath_test.mak" CFG="pjnath_test - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pjnath_test - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "pjnath_test - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pjnath_test - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "./output/pjnath-test-i386-win32-vc6-release"
+# PROP BASE Intermediate_Dir "./output/pjnath-test-i386-win32-vc6-release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "./output/pjnath-test-i386-win32-vc6-release"
+# PROP Intermediate_Dir "./output/pjnath-test-i386-win32-vc6-release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W4 /GX /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../bin/pjnath-test-i386-win32-vc6-release.exe"
+
+!ELSEIF  "$(CFG)" == "pjnath_test - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "./output/pjnath-test-i386-win32-vc6-debug"
+# PROP BASE Intermediate_Dir "./output/pjnath-test-i386-win32-vc6-debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "./output/pjnath-test-i386-win32-vc6-debug"
+# PROP Intermediate_Dir "./output/pjnath-test-i386-win32-vc6-debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 netapi32.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjnath-test-i386-win32-vc6-debug.exe" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pjnath_test - Win32 Release"
+# Name "pjnath_test - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE="..\src\pjnath-test\ice.c"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pjnath-test\main.c"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pjnath-test\stun.c"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pjnath-test\test.c"
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE="..\src\pjnath-test\test.h"
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
Index: /pjproject/trunk/pjnath/include/pjnath.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath.h	(revision 1092)
+++ /pjproject/trunk/pjnath/include/pjnath.h	(revision 1093)
@@ -20,4 +20,6 @@
 #include <pjnath/config.h>
 #include <pjnath/errno.h>
+#include <pjnath/ice.h>
+#include <pjnath/ice_mt.h>
 #include <pjnath/stun_auth.h>
 #include <pjnath/stun_config.h>
Index: /pjproject/trunk/pjnath/include/pjnath/ice.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/ice.h	(revision 1092)
+++ /pjproject/trunk/pjnath/include/pjnath/ice.h	(revision 1093)
@@ -21,6 +21,6 @@
 
 /**
- * @file ice_sock.h
- * @brief ICE socket.
+ * @file ice.h
+ * @brief ICE.
  */
 #include <pjnath/types.h>
@@ -29,13 +29,18 @@
 #include <pj/timer.h>
 
+/**
+ * @defgroup PJNATH_ICE Interactive Connectivity Establishment (ICE)
+ * @brief Interactive Connectivity Establishment (ICE)
+ * @ingroup PJNATH
+ */
+
 
 PJ_BEGIN_DECL
 
 
-/* **************************************************************************/
-/**
- * @defgroup PJNATH_ICE_SOCK ICE Socket
- * @brief High level ICE socket abstraction.
- * @ingroup PJNATH
+/**
+ * @defgroup PJNATH_ICE_STREAM Transport Independent ICE Media Stream
+ * @brief Transport Independent ICE Media Stream
+ * @ingroup PJNATH_ICE
  * @{
  */
@@ -166,7 +171,6 @@
 
     pj_pool_t		*pool;
+    void		*user_data;
     pj_mutex_t		*mutex;
-    int			 af;
-    int			 sock_type;
     pj_ice_role		 role;
     pj_bool_t		 is_complete;
@@ -177,6 +181,8 @@
 
     /* STUN credentials */
+    pj_str_t		 tx_ufrag;
     pj_str_t		 tx_uname;
     pj_str_t		 tx_pass;
+    pj_str_t		 rx_ufrag;
     pj_str_t		 rx_uname;
     pj_str_t		 rx_pass;
@@ -207,6 +213,4 @@
 				   pj_ice_role role,
 				   const pj_ice_cb *cb,
-				   int af,
-				   int sock_type,
 				   pj_ice **p_ice);
 PJ_DECL(pj_status_t) pj_ice_destroy(pj_ice *ice);
@@ -215,7 +219,4 @@
 				     const pj_sockaddr_t *local_addr,
 				     unsigned addr_len);
-PJ_DECL(pj_status_t) pj_ice_add_sock_comp(pj_ice *ice,
-					  unsigned comp_id,
-					  pj_sock_t sock);
 PJ_DECL(pj_status_t) pj_ice_set_credentials(pj_ice *ice,
 					    const pj_str_t *local_ufrag,
Index: /pjproject/trunk/pjnath/include/pjnath/ice_mt.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/ice_mt.h	(revision 1093)
+++ /pjproject/trunk/pjnath/include/pjnath/ice_mt.h	(revision 1093)
@@ -0,0 +1,114 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#ifndef __PJNATH_ICE_MT_H__
+#define __PJNATH_ICE_MT_H__
+
+
+/**
+ * @file ice_mt.h
+ * @brief ICE Media Transport.
+ */
+#include <pjnath/ice.h>
+#include <pj/ioqueue.h>
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJNATH_ICE_MEDIA_TRANSPORT ICE Media Transport
+ * @brief ICE Media Transport
+ * @ingroup PJNATH_ICE
+ * @{
+ */
+
+typedef struct pj_icemt pj_icemt;
+
+typedef struct pj_icemt_cb
+{
+    void	(*on_ice_complete)(pj_icemt *icemt, 
+				   pj_status_t status);
+    void	(*on_rx_rtp)(pj_icemt *icemt,
+			     void *pkt, pj_size_t size,
+			     const pj_sockaddr_t *src_addr,
+			     unsigned src_addr_len);
+    void	(*on_rx_rtcp)(pj_icemt *icemt,
+			      void *pkt, pj_size_t size,
+			      const pj_sockaddr_t *src_addr,
+			      unsigned src_addr_len);
+
+} pj_icemt_cb;
+
+
+typedef struct pj_icemt_sock
+{
+    pj_icemt		*icemt;
+    unsigned		 comp_id;
+    pj_sock_t		 sock;
+    pj_sockaddr		 addr;
+    pj_sockaddr		 base_addr;
+    pj_ioqueue_key_t	*key;
+    pj_uint8_t		 pkt[1500];
+    pj_ioqueue_op_key_t	 read_op;
+    pj_ioqueue_op_key_t	 write_op;
+    pj_sockaddr		 src_addr;
+    int			 src_addr_len;
+} pj_icemt_sock;
+
+
+struct pj_icemt
+{
+    pj_pool_t	    *pool;
+    pj_ice	    *ice;
+    void	    *user_data;
+
+    pj_icemt_cb	     cb;
+
+    pj_icemt_sock    rtp;
+    pj_icemt_sock    rtcp;
+
+    pj_bool_t	     has_turn;
+    pj_sockaddr	     stun_srv;
+};
+
+
+PJ_DECL(pj_status_t) pj_icemt_create(pj_stun_config *stun_cfg,
+				     const char *name,
+				     pj_ice_role role,
+				     const pj_icemt_cb *cb,
+				     unsigned rtp_port,
+				     pj_bool_t has_rtcp,
+				     pj_bool_t has_turn,
+				     const pj_sockaddr *srv,
+				     pj_icemt **p_icemt);
+PJ_DECL(pj_status_t) pj_icemt_destroy(pj_icemt *icemt);
+
+
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
+
+
+
+#endif	/* __PJNATH_ICE_MT_H__ */
+
Index: /pjproject/trunk/pjnath/src/pjnath-test/ice.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath-test/ice.c	(revision 1093)
+++ /pjproject/trunk/pjnath/src/pjnath-test/ice.c	(revision 1093)
@@ -0,0 +1,233 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+#define THIS_FILE   "ice.c"
+
+
+struct ice_data
+{
+    pj_bool_t	    complete;
+    pj_status_t	    err_code;
+    unsigned	    rx_rtp_cnt;
+    unsigned	    rx_rtcp_cnt;
+};
+
+static pj_stun_config stun_cfg;
+
+static void on_ice_complete(pj_icemt *icemt, 
+			    pj_status_t status)
+{
+    struct ice_data *id = (struct ice_data*) icemt->user_data;
+    id->complete = PJ_TRUE;
+    id->err_code = status;
+}
+
+
+static void on_rx_rtp(pj_icemt *icemt,
+		      void *pkt, pj_size_t size,
+		      const pj_sockaddr_t *src_addr,
+		      unsigned src_addr_len)
+{
+    struct ice_data *id = (struct ice_data*) icemt->user_data;
+    id->rx_rtp_cnt++;
+}
+
+
+static void on_rx_rtcp(pj_icemt *icemt,
+		       void *pkt, pj_size_t size,
+		       const pj_sockaddr_t *src_addr,
+		       unsigned src_addr_len)
+{
+    struct ice_data *id = (struct ice_data*) icemt->user_data;
+    id->rx_rtcp_cnt++;
+}
+
+
+static void handle_events(unsigned msec_timeout)
+{
+    pj_time_val delay;
+
+    pj_timer_heap_poll(stun_cfg.timer_heap, NULL);
+
+    delay.sec = 0;
+    delay.msec = msec_timeout;
+    pj_time_val_normalize(&delay);
+
+    pj_ioqueue_poll(stun_cfg.ioqueue, &delay);
+}
+
+
+/* Basic create and destroy test */
+static int ice_basic_create_destroy_test()
+{
+    pj_icemt *im;
+    pj_ice *ice;
+    pj_icemt_cb icemt_cb;
+    pj_status_t status;
+
+    PJ_LOG(3,(THIS_FILE, "...basic create/destroy"));
+
+    pj_bzero(&icemt_cb, sizeof(icemt_cb));
+    icemt_cb.on_ice_complete = &on_ice_complete;
+    icemt_cb.on_rx_rtcp = &on_rx_rtp;
+    icemt_cb.on_rx_rtcp = &on_rx_rtcp;
+
+    status = pj_icemt_create(&stun_cfg, NULL, PJ_ICE_ROLE_CONTROLLING,
+			     &icemt_cb, 0, PJ_FALSE, PJ_FALSE, NULL, &im);
+    if (status != PJ_SUCCESS)
+	return -10;
+
+    ice = im->ice;
+
+    pj_icemt_destroy(im);
+
+    return 0;
+}
+
+
+static pj_status_t set_remote_list(pj_icemt *src, pj_icemt *dst)
+{
+    unsigned i, count;
+    unsigned cand_id[PJ_ICE_MAX_CAND];
+    pj_ice_cand cand[PJ_ICE_MAX_CAND];
+    pj_status_t status;
+
+    count = PJ_ARRAY_SIZE(cand_id);
+    status = pj_ice_enum_cands(src->ice, &count, cand_id);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    for (i=0; i<count; ++i) {
+	pj_ice_cand *p_cand;
+	status = pj_ice_get_cand(src->ice, cand_id[i], &p_cand);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	pj_memcpy(&cand[i], p_cand, sizeof(pj_ice_cand));
+    }
+
+    status = pj_ice_create_check_list(dst->ice, count, cand);
+    return status;
+}
+
+
+/* Direct agent to agent communication */
+static int ice_direct_test()
+{
+    pj_icemt *im1, *im2;
+    pj_icemt_cb icemt_cb;
+    struct ice_data *id1, *id2;
+    pj_status_t status;
+
+    PJ_LOG(3,(THIS_FILE, "...direct communication"));
+
+    pj_bzero(&icemt_cb, sizeof(icemt_cb));
+    icemt_cb.on_ice_complete = &on_ice_complete;
+    icemt_cb.on_rx_rtcp = &on_rx_rtp;
+    icemt_cb.on_rx_rtcp = &on_rx_rtcp;
+
+    /* Create first ICE */
+    status = pj_icemt_create(&stun_cfg, NULL, PJ_ICE_ROLE_CONTROLLING,
+			     &icemt_cb, 0, PJ_FALSE, PJ_FALSE, NULL, &im1);
+    if (status != PJ_SUCCESS)
+	return -20;
+
+    id1 = PJ_POOL_ZALLOC_T(im1->pool, struct ice_data);
+    im1->user_data = id1;
+
+    /* Create second ICE */
+    status = pj_icemt_create(&stun_cfg, NULL, PJ_ICE_ROLE_CONTROLLED,
+			     &icemt_cb, 0, PJ_FALSE, PJ_FALSE, NULL, &im2);
+    if (status != PJ_SUCCESS)
+	return -25;
+
+    id2 = PJ_POOL_ZALLOC_T(im2->pool, struct ice_data);
+    im2->user_data = id2;
+
+    {
+	pj_str_t u1 = pj_str("uname1");
+	pj_str_t p1 = pj_str("pass1");
+	pj_str_t u2 = pj_str("uname2");
+	pj_str_t p2 = pj_str("pass2");
+
+	pj_ice_set_credentials(im1->ice, &u1, &p1, &u2, &p2);
+	pj_ice_set_credentials(im2->ice, &u2, &p2, &u1, &p1);
+    }
+
+    /* Send offer to im2 */
+    status = set_remote_list(im1, im2);
+    if (status != PJ_SUCCESS)
+	return -30;
+
+    /* Send answer to im1 */
+    status = set_remote_list(im2, im1);
+    if (status != PJ_SUCCESS)
+	return -35;
+
+    /* Both can start now */
+    status = pj_ice_start_check(im1->ice);
+    if (status != PJ_SUCCESS)
+	return -40;
+
+#if 0
+    status = pj_ice_start_check(im2->ice);
+    if (status != PJ_SUCCESS)
+	return -40;
+#endif
+
+    /* Just wait until both completes, or timed out */
+    while (!id1->complete || !id2->complete)
+	handle_events(1);
+
+    return 0;
+
+}
+
+
+int ice_test(void)
+{
+    int rc = 0;
+    pj_pool_t *pool;
+    pj_ioqueue_t *ioqueue;
+    pj_timer_heap_t *timer_heap;
+    
+    pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
+    pj_ioqueue_create(pool, 12, &ioqueue);
+    pj_timer_heap_create(pool, 100, &timer_heap);
+    
+    pj_stun_config_init(&stun_cfg, mem, 0, ioqueue, timer_heap);
+
+    pj_log_set_level(5);
+
+    rc = ice_basic_create_destroy_test();
+    if (rc != 0)
+	goto on_return;
+
+    rc = ice_direct_test();
+    if (rc != 0)
+	goto on_return;
+
+on_return:
+    pj_log_set_level(3);
+    pj_ioqueue_destroy(stun_cfg.ioqueue);
+    pj_pool_release(pool);
+    return rc;
+}
+
Index: /pjproject/trunk/pjnath/src/pjnath-test/main.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath-test/main.c	(revision 1093)
+++ /pjproject/trunk/pjnath/src/pjnath-test/main.c	(revision 1093)
@@ -0,0 +1,53 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+#if defined(PJ_SUNOS) && PJ_SUNOS!=0
+#include <signal.h>
+static void init_signals()
+{
+    struct sigaction act;
+
+    memset(&act, 0, sizeof(act));
+    act.sa_handler = SIG_IGN;
+
+    sigaction(SIGALRM, &act, NULL);
+}
+
+#else
+#define init_signals()
+#endif
+
+#define boost()
+
+int main(int argc, char *argv[])
+{
+    int rc;
+
+    PJ_UNUSED_ARG(argc);
+    PJ_UNUSED_ARG(argv);
+
+    boost();
+    init_signals();
+
+    rc = test_main();
+
+    return rc;
+}
+
Index: /pjproject/trunk/pjnath/src/pjnath-test/stun.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath-test/stun.c	(revision 1093)
+++ /pjproject/trunk/pjnath/src/pjnath-test/stun.c	(revision 1093)
@@ -0,0 +1,118 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+static int decode_test(void)
+{
+    /* Invalid message type */
+
+    /* Short message */
+
+    /* Long, random message */
+
+    /* Message length in header is shorter */
+
+    /* Message length in header is longer */
+
+    /* Invalid magic */
+
+    /* Attribute length is not valid */
+
+    /* Unknown mandatory attribute type should generate error */
+
+    /* Unknown but non-mandatory should be okay */
+
+    /* String/binary attribute length is larger than the message */
+
+    /* Valid message with MESSAGE-INTEGRITY */
+
+    /* Valid message with FINGERPRINT */
+
+    /* Valid message with MESSAGE-INTEGRITY and FINGERPRINT */
+
+    /* Another attribute not FINGERPRINT exists after MESSAGE-INTEGRITY */
+
+    /* Another attribute exists after FINGERPRINT */
+
+    return 0;
+}
+
+static int decode_verify(void)
+{
+    /* Decode all attribute types */
+    return 0;
+}
+
+static int auth_test(void)
+{
+    /* REALM and USERNAME is present, but MESSAGE-INTEGRITY is not present.
+     * For short term, must with reply 401 without REALM.
+     * For long term, must reply with 401 with REALM.
+     */
+
+    /* USERNAME is not present, server must respond with 432 (Missing
+     * Username).
+     */
+
+    /* If long term credential is wanted and REALM is not present, server 
+     * must respond with 434 (Missing Realm) 
+     */
+
+    /* If REALM doesn't match, server must respond with 434 (Missing Realm)
+     * too, containing REALM and NONCE attribute.
+     */
+
+    /* When long term authentication is wanted and NONCE is NOT present,
+     * server must respond with 435 (Missing Nonce), containing REALM and
+     * NONCE attribute.
+     */
+
+    /* Simulate 438 (Stale Nonce) */
+    
+    /* Simulate 436 (Unknown Username) */
+
+    /* When server wants to use short term credential, but request has
+     * REALM, reject with .... ???
+     */
+
+    /* Invalid HMAC */
+
+    /* Valid static short term, without NONCE */
+
+    /* Valid static short term, WITH NONCE */
+
+    /* Valid static long term (with NONCE */
+
+    /* Valid dynamic short term (without NONCE) */
+
+    /* Valid dynamic short term (with NONCE) */
+
+    /* Valid dynamic long term (with NONCE) */
+
+    return 0;
+}
+
+
+int stun_test(void)
+{
+    decode_verify();
+    decode_test();
+    auth_test();
+    return 0;
+}
+
Index: /pjproject/trunk/pjnath/src/pjnath-test/test.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath-test/test.c	(revision 1093)
+++ /pjproject/trunk/pjnath/src/pjnath-test/test.c	(revision 1093)
@@ -0,0 +1,89 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pjlib.h>
+
+void app_perror(const char *msg, pj_status_t rc)
+{
+    char errbuf[256];
+
+    PJ_CHECK_STACK();
+
+    pj_strerror(rc, errbuf, sizeof(errbuf));
+    PJ_LOG(1,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf));
+}
+
+#define DO_TEST(test)	do { \
+			    PJ_LOG(3, ("test", "Running %s...", #test));  \
+			    rc = test; \
+			    PJ_LOG(3, ("test",  \
+				       "%s(%d)",  \
+				       (char*)(rc ? "..ERROR" : "..success"), rc)); \
+			    if (rc!=0) goto on_return; \
+			} while (0)
+
+
+pj_pool_factory *mem;
+
+
+static int test_inner(void)
+{
+    pj_caching_pool caching_pool;
+    int rc = 0;
+
+    mem = &caching_pool.factory;
+
+    pj_log_set_level(3);
+    pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | 
+                     PJ_LOG_HAS_MICRO_SEC);
+
+    rc = pj_init();
+    if (rc != 0) {
+	app_perror("pj_init() error!!", rc);
+	return rc;
+    }
+    
+    pj_dump_config();
+    pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 );
+
+#if INCLUDE_ICE_TEST
+    DO_TEST(ice_test());
+#endif
+
+on_return:
+    return rc;
+}
+
+int test_main(void)
+{
+    PJ_USE_EXCEPTION;
+
+    PJ_TRY {
+        return test_inner();
+    }
+    PJ_CATCH_ANY {
+        int id = PJ_GET_EXCEPTION();
+        PJ_LOG(3,("test", "FATAL: unhandled exception id %d (%s)", 
+                  id, pj_exception_id_name(id)));
+    }
+    PJ_END;
+
+    return -1;
+}
+
Index: /pjproject/trunk/pjnath/src/pjnath-test/test.h
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath-test/test.h	(revision 1093)
+++ /pjproject/trunk/pjnath/src/pjnath-test/test.h	(revision 1093)
@@ -0,0 +1,30 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjlib.h>
+#include <pjlib-util.h>
+#include <pjnath.h>
+
+#define INCLUDE_ICE_TEST	    1
+
+extern int ice_test(void);
+extern int test_main(void);
+
+extern void app_perror(const char *title, pj_status_t rc);
+extern pj_pool_factory *mem;
+
Index: /pjproject/trunk/pjnath/src/pjnath/ice.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/ice.c	(revision 1092)
+++ /pjproject/trunk/pjnath/src/pjnath/ice.c	(revision 1093)
@@ -137,6 +137,4 @@
 				  pj_ice_role role,
 				  const pj_ice_cb *cb,
-				  int af,
-				  int sock_type,
 				  pj_ice **p_ice)
 {
@@ -147,6 +145,4 @@
 
     PJ_ASSERT_RETURN(stun_cfg && cb && p_ice, PJ_EINVAL);
-    PJ_ASSERT_RETURN(sock_type==PJ_SOCK_DGRAM || sock_type==PJ_SOCK_STREAM,
-		     PJ_EINVAL);
 
     if (!name)
@@ -156,6 +152,4 @@
     ice = PJ_POOL_ZALLOC_T(pool, pj_ice);
     ice->pool = pool;
-    ice->af = af;
-    ice->sock_type = sock_type;
     ice->role = role;
 
@@ -436,4 +430,5 @@
 
     pj_strdup(ice->pool, &ice->tx_uname, &username);
+    pj_strdup(ice->pool, &ice->tx_ufrag, remote_ufrag);
     pj_strdup(ice->pool, &ice->tx_pass, remote_pass);
 
@@ -443,4 +438,5 @@
 
     pj_strdup(ice->pool, &ice->rx_uname, &username);
+    pj_strdup(ice->pool, &ice->rx_ufrag, local_ufrag);
     pj_strdup(ice->pool, &ice->rx_pass, local_pass);
 
@@ -482,5 +478,5 @@
     char tmp[128];
 
-    PJ_ASSERT_RETURN(ice && comp_id && type && local_pref &&
+    PJ_ASSERT_RETURN(ice && comp_id && local_pref &&
 		     foundation && addr && base_addr && addr_len,
 		     PJ_EINVAL);
@@ -704,5 +700,5 @@
     for (i=0; i<clist->count; ++i) {
 	const pj_ice_check *c = &clist->checks[i];
-	LOG((ice->obj_name, " %d: %s (prio=%u, state=%s)",
+	LOG((ice->obj_name, " %d: %s (prio=0x%"PJ_INT64_FMT"x, state=%s)",
 	     i, dump_check(buffer, sizeof(buffer), c),
 	     c->prio, check_state_name[c->state]));
@@ -1102,5 +1098,5 @@
     lcand = check->lcand;
     rcand = check->rcand;
-    comp = &ice->comp[lcand->comp_id];
+    comp = find_comp(ice, lcand->comp_id);
 
     LOG((ice->obj_name, 
@@ -1734,5 +1730,5 @@
     pj_status_t status = PJ_SUCCESS;
     pj_ice_comp *comp;
-    pj_bool_t is_stun;
+    pj_status_t stun_status;
 
     PJ_ASSERT_RETURN(ice, PJ_EINVAL);
@@ -1746,6 +1742,6 @@
     }
 
-    is_stun = pj_stun_msg_check(pkt, pkt_size, PJ_STUN_IS_DATAGRAM);
-    if (is_stun) {
+    stun_status = pj_stun_msg_check(pkt, pkt_size, PJ_STUN_IS_DATAGRAM);
+    if (stun_status == PJ_SUCCESS) {
 	status = pj_stun_session_on_rx_pkt(comp->stun_sess, pkt, pkt_size,
 					   PJ_STUN_IS_DATAGRAM,
Index: /pjproject/trunk/pjnath/src/pjnath/ice_mt.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/ice_mt.c	(revision 1093)
+++ /pjproject/trunk/pjnath/src/pjnath/ice_mt.c	(revision 1093)
@@ -0,0 +1,285 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjnath/ice_mt.h>
+#include <pjnath/errno.h>
+#include <pj/addr_resolv.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+#define RTP_COMP_ID	1
+#define RTCP_COMP_ID	2
+
+
+
+/* ICE callbacks */
+static void	   on_ice_complete(pj_ice *ice, pj_status_t status);
+static pj_status_t on_tx_pkt(pj_ice *ice, unsigned comp_id,
+			     const void *pkt, pj_size_t size,
+			     const pj_sockaddr_t *dst_addr,
+			     unsigned dst_addr_len);
+static pj_status_t on_rx_data(pj_ice *ice, unsigned comp_id,
+			      void *pkt, pj_size_t size,
+			      const pj_sockaddr_t *src_addr,
+			      unsigned src_addr_len);
+
+/* Ioqueue callback */
+static void on_read_complete(pj_ioqueue_key_t *key, 
+                             pj_ioqueue_op_key_t *op_key, 
+                             pj_ssize_t bytes_read);
+
+static void destroy_ice_sock(pj_icemt_sock *is);
+
+static pj_status_t create_ice_sock(pj_icemt *icemt,
+				   pj_ioqueue_t *ioqueue,
+				   unsigned comp_id,
+				   unsigned port,
+				   pj_icemt_sock *is)
+{
+    pj_ioqueue_callback ioqueue_cb;
+    const pj_str_t H1 = { "H1", 2 };
+    int addr_len;
+    pj_status_t status;
+
+    pj_bzero(is, sizeof(*is));
+    is->sock = PJ_INVALID_SOCKET;
+    is->comp_id = comp_id;
+    is->icemt = icemt;
+
+    status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &is->sock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Bind and get the local IP address */
+    pj_sockaddr_in_init(&is->base_addr.ipv4, NULL, (pj_uint16_t)port);
+    status = pj_sock_bind(is->sock, &is->base_addr, sizeof(pj_sockaddr_in));
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    addr_len = sizeof(is->base_addr);
+    status = pj_sock_getsockname(is->sock, &is->base_addr, &addr_len);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    if (is->base_addr.ipv4.sin_addr.s_addr == 0) {
+	status = pj_gethostip(&is->base_addr.ipv4.sin_addr);
+	if (status != PJ_SUCCESS) 
+	    goto on_error;
+    }
+
+    /* Register to ioqueue */
+    pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb));
+    ioqueue_cb.on_read_complete = &on_read_complete;
+    status = pj_ioqueue_register_sock(icemt->pool, ioqueue, is->sock, is,
+				      &ioqueue_cb, &is->key);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    pj_ioqueue_op_key_init(&is->read_op, sizeof(is->read_op));
+    pj_ioqueue_op_key_init(&is->write_op, sizeof(is->write_op));
+
+    on_read_complete(is->key, &is->read_op, 0);
+
+    /* Add new ICE component */
+    status = pj_ice_add_comp(icemt->ice, comp_id, &is->base_addr,
+			     sizeof(pj_sockaddr_in));
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Add host candidate */
+    status = pj_ice_add_cand(icemt->ice, comp_id, PJ_ICE_CAND_TYPE_HOST,
+			     65535, &H1, &is->base_addr, &is->base_addr,
+			     NULL, sizeof(pj_sockaddr_in), NULL);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+    
+    return PJ_SUCCESS;
+
+on_error:
+    destroy_ice_sock(is);
+    return status;
+}
+
+
+static void on_read_complete(pj_ioqueue_key_t *key, 
+                             pj_ioqueue_op_key_t *op_key, 
+                             pj_ssize_t bytes_read)
+{
+    pj_icemt_sock *is = (pj_icemt_sock*) pj_ioqueue_get_user_data(key);
+    pj_ssize_t pkt_size;
+    pj_status_t status;
+
+    if (bytes_read > 0) {
+	status = pj_ice_on_rx_pkt(is->icemt->ice, is->comp_id, 
+				  is->pkt, bytes_read,
+				  &is->src_addr, is->src_addr_len);
+    }
+
+    pkt_size = sizeof(is->pkt);
+    is->src_addr_len = sizeof(is->src_addr);
+    status = pj_ioqueue_recvfrom(key, op_key, is->pkt, &pkt_size, 
+				 PJ_IOQUEUE_ALWAYS_ASYNC,
+				 &is->src_addr, &is->src_addr_len);
+    pj_assert(status == PJ_SUCCESS || status == PJ_EPENDING);
+}
+
+
+static void destroy_ice_sock(pj_icemt_sock *is)
+{
+    if (is->key) {
+	pj_ioqueue_unregister(is->key);
+	is->key = NULL;
+	is->sock = PJ_INVALID_SOCKET;
+    } else if (is->sock != PJ_INVALID_SOCKET && is->sock != 0) {
+	pj_sock_close(is->sock);
+	is->sock = PJ_INVALID_SOCKET;
+    }
+}
+
+
+PJ_DEF(pj_status_t) pj_icemt_create( pj_stun_config *stun_cfg,
+				     const char *name,
+				     pj_ice_role role,
+				     const pj_icemt_cb *cb,
+				     unsigned rtp_port,
+				     pj_bool_t has_rtcp,
+				     pj_bool_t has_turn,
+				     const pj_sockaddr *srv,
+				     pj_icemt **p_icemt)
+{
+    pj_pool_t *pool;
+    pj_icemt *icemt;
+    pj_ice_cb ice_cb;
+    pj_status_t status;
+
+    pool = pj_pool_create(stun_cfg->pf, name, 512, 512, NULL);
+    icemt = PJ_POOL_ZALLOC_T(pool, struct pj_icemt);
+    icemt->pool = pool;
+
+
+    pj_bzero(&ice_cb, sizeof(ice_cb));
+    ice_cb.on_ice_complete = &on_ice_complete;
+    ice_cb.on_tx_pkt = &on_tx_pkt;
+    ice_cb.on_rx_data = &on_rx_data;
+
+    pj_memcpy(&icemt->cb, cb, sizeof(*cb));
+
+    status = pj_ice_create(stun_cfg, name, role, &ice_cb, &icemt->ice);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    icemt->ice->user_data = (void*)icemt;
+
+    icemt->has_turn = has_turn;
+    if (srv)
+	pj_memcpy(&icemt->stun_srv, srv, sizeof(pj_sockaddr));
+
+    status = create_ice_sock(icemt, stun_cfg->ioqueue, RTP_COMP_ID,
+			     rtp_port, &icemt->rtp);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    if (has_rtcp) {
+	if (rtp_port) ++rtp_port;
+
+	status = create_ice_sock(icemt, stun_cfg->ioqueue, RTCP_COMP_ID,
+				 rtp_port, &icemt->rtcp);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+    }
+
+    *p_icemt = icemt;
+    return PJ_SUCCESS;
+
+on_error:
+    if (icemt->ice)
+	pj_ice_destroy(icemt->ice);
+    pj_pool_release(pool);
+    return status;
+}
+
+
+PJ_DEF(pj_status_t) pj_icemt_destroy(pj_icemt *icemt)
+{
+    destroy_ice_sock(&icemt->rtp);
+    destroy_ice_sock(&icemt->rtcp);
+
+    pj_ice_destroy(icemt->ice);
+    pj_pool_release(icemt->pool);
+
+    return PJ_SUCCESS;
+}
+
+
+static void on_ice_complete(pj_ice *ice, pj_status_t status)
+{
+    pj_icemt *icemt = (pj_icemt*)ice->user_data;
+    (*icemt->cb.on_ice_complete)(icemt, status);
+}
+
+
+static pj_status_t on_tx_pkt(pj_ice *ice, unsigned comp_id,
+			     const void *pkt, pj_size_t size,
+			     const pj_sockaddr_t *dst_addr,
+			     unsigned dst_addr_len)
+{
+    pj_icemt *icemt = (pj_icemt*)ice->user_data;
+    pj_icemt_sock *is;
+    pj_ssize_t pkt_size;
+    pj_status_t status;
+
+    if (comp_id == RTP_COMP_ID)
+	is = &icemt->rtp;
+    else if (comp_id == RTCP_COMP_ID)
+	is = &icemt->rtcp;
+    else {
+	pj_assert(!"Invalid comp_id");
+	return -1;
+    }
+
+    pkt_size = size;
+    status = pj_ioqueue_sendto(is->key, &is->write_op, 
+			       pkt, &pkt_size, 0,
+			       dst_addr, dst_addr_len);
+    
+    return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status;
+}
+
+
+static pj_status_t on_rx_data(pj_ice *ice, unsigned comp_id,
+			      void *pkt, pj_size_t size,
+			      const pj_sockaddr_t *src_addr,
+			      unsigned src_addr_len)
+{
+    pj_icemt *icemt = (pj_icemt*)ice->user_data;
+
+    if (comp_id == RTP_COMP_ID) {
+	(*icemt->cb.on_rx_rtp)(icemt, pkt, size, src_addr, src_addr_len);
+    } else if (comp_id == RTCP_COMP_ID) {
+	(*icemt->cb.on_rx_rtcp)(icemt, pkt, size, src_addr, src_addr_len);
+    } else {
+	pj_assert(!"Invalid comp_id");
+	return -1;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
Index: /pjproject/trunk/pjnath/src/pjnath/stun_msg.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 1092)
+++ /pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 1093)
@@ -482,5 +482,5 @@
 	      PJ_STUN_ATTR_END_EXTENDED_ATTR-PJ_STUN_ATTR_START_EXTENDED_ATTR);
 
-    if (attr_type < PJ_STUN_ATTR_START_EXTENDED_ATTR)
+    if (attr_type < PJ_STUN_ATTR_END_MANDATORY_ATTR)
 	desc = &mandatory_attr_desc[attr_type];
     else if (attr_type >= PJ_STUN_ATTR_START_EXTENDED_ATTR &&
