diff --git a/.gitignore b/.gitignore index 362b9c890..db310602b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -pjlib/include/pj/config_site.h lib/ bin/ output/ diff --git a/aconfigure b/aconfigure index b5d344631..6707ea11b 100755 --- a/aconfigure +++ b/aconfigure @@ -6974,9 +6974,6 @@ case $target in # UUID case $target in - *android*) - ac_os_objs="$ac_os_objs guid_android.o" - ;; *darwin*) ac_os_objs="$ac_os_objs guid_darwin.o" ;; diff --git a/aconfigure.ac b/aconfigure.ac index 279870e9d..43d69dc09 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -675,9 +675,6 @@ case $target in # UUID case $target in - *android*) - ac_os_objs="$ac_os_objs guid_android.o" - ;; *darwin*) ac_os_objs="$ac_os_objs guid_darwin.o" ;; diff --git a/build/vs/pjproject-vs14-common-config.props b/build/vs/pjproject-vs14-common-config.props index 456e4f02e..400439a78 100644 --- a/build/vs/pjproject-vs14-common-config.props +++ b/build/vs/pjproject-vs14-common-config.props @@ -18,12 +18,12 @@ WinDesktop - v140 + v141 - v140 + v141 WIN32;PJ_WIN32=1;PJ_M_I386=1; WIN64;PJ_WIN64=1;PJ_M_X86_64=1; PJ_M_ARM64=1; @@ -31,10 +31,10 @@ - v140 + v141 PJ_WIN32_UWP;UNICODE;_UNICODE; $(PreprocessorDef);PJ_M_ARMV7=1; - 10.0.10586.0 + 10.0.16299.0 10.0.10240.0 10.0 diff --git a/build/vs/pjproject-vs14-common-defaults.props b/build/vs/pjproject-vs14-common-defaults.props index 526f6c925..974447f43 100644 --- a/build/vs/pjproject-vs14-common-defaults.props +++ b/build/vs/pjproject-vs14-common-defaults.props @@ -3,7 +3,7 @@ - 14 + 15 <_ProjectFileVersion>14.0.22823.1 diff --git a/pjlib/include/pj/config_site.h b/pjlib/include/pj/config_site.h new file mode 100644 index 000000000..ba81c7f4d --- /dev/null +++ b/pjlib/include/pj/config_site.h @@ -0,0 +1,26 @@ +#include "config_site_sample.h" + +/* +* PJLIB settings. +*/ +#define PJ_HAS_IPV6 1 +#define PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION 1 + +/* +* PJSIP settings. +*/ +#define PJSIP_MAX_PKT_LEN 8000 +#define PJSIP_TRANSPORT_SERVER_IDLE_TIME 3 + +/* +* PJNAT settings. +*/ +#define PJ_ICE_MAX_CAND 256 +#define PJ_ICE_ST_MAX_CAND 32 +#define PJ_ICE_MAX_STUN 6 +#define PJ_ICE_MAX_TURN 4 +#define PJ_ICE_COMP_BITS 5 +#define PJ_ICE_MAX_CHECKS 1024 +/* Set permanent permissions on the TURN + server for all peer candidates */ +#define PJ_ICE_ST_USE_TURN_PERMANENT_PERM PJ_TRUE diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h index 88d679c5e..9af42ba3a 100644 --- a/pjlib/include/pj/sock.h +++ b/pjlib/include/pj/sock.h @@ -320,6 +320,12 @@ extern const pj_uint16_t PJ_SO_REUSEADDR; /** Do not generate SIGPIPE. @see pj_SO_NOSIGPIPE */ extern const pj_uint16_t PJ_SO_NOSIGPIPE; +extern const pj_uint16_t PJ_SO_KEEPALIVE; +extern const pj_uint16_t PJ_TCP_KEEPIDLE; +extern const pj_uint16_t PJ_TCP_KEEPINTVL; +extern const pj_uint16_t PJ_TCP_KEEPCNT; +extern const pj_uint16_t PJ_TCP_USER_TIMEOUT; + /** Set the protocol-defined priority for all packets to be sent on socket. */ extern const pj_uint16_t PJ_SO_PRIORITY; @@ -350,9 +356,24 @@ extern const pj_uint16_t PJ_IP_DROP_MEMBERSHIP; /** Get #PJ_SO_SNDBUF constant */ PJ_DECL(pj_uint16_t) pj_SO_SNDBUF(void); + /** Get #PJ_SO_KEEPALIVE constant */ +# define pj_SO_KEEPALIVE() PJ_SO_KEEPALIVE(void); + /** Get #PJ_TCP_NODELAY constant */ PJ_DECL(pj_uint16_t) pj_TCP_NODELAY(void); + /** Get #PJ_TCP_KEEPIDLE constant */ +# define pj_TCP_KEEPIDLE() PJ_TCP_KEEPIDLE(void); + + /** Get #PJ_TCP_KEEPINTVL constant */ +# define pj_TCP_KEEPINTVL() PJ_TCP_KEEPINTVL(void); + + /** Get #PJ_TCP_USER_TIMEOUT constant */ + PJ_DECL(pj_uint16_t) PJ_TCP_USER_TIMEOUT(void); + + /** Get #PJ_TCP_KEEPCNT constant */ +# define pj_TCP_KEEPCNT() PJ_TCP_KEEPCNT(void); + /** Get #PJ_SO_REUSEADDR constant */ PJ_DECL(pj_uint16_t) pj_SO_REUSEADDR(void); @@ -386,9 +407,24 @@ extern const pj_uint16_t PJ_IP_DROP_MEMBERSHIP; /** Get #PJ_SO_SNDBUF constant */ # define pj_SO_SNDBUF() PJ_SO_SNDBUF + /** Get #PJ_SO_KEEPALIVE constant */ +# define pj_SO_KEEPALIVE() PJ_SO_KEEPALIVE + /** Get #PJ_TCP_NODELAY constant */ # define pj_TCP_NODELAY() PJ_TCP_NODELAY + /** Get #PJ_TCP_KEEPIDLE constant */ +# define pj_TCP_KEEPIDLE() PJ_TCP_KEEPIDLE + + /** Get #PJ_TCP_USER_TIMEOUT constant */ +# define pj_TCP_USER_TIMEOUT() PJ_TCP_USER_TIMEOUT + + /** Get #PJ_TCP_KEEPINTVL constant */ +# define pj_TCP_KEEPINTVL() PJ_TCP_KEEPINTVL + + /** Get #PJ_TCP_KEEPCNT constant */ +# define pj_TCP_KEEPCNT() PJ_TCP_KEEPCNT + /** Get #PJ_SO_REUSEADDR constant */ # define pj_SO_REUSEADDR() PJ_SO_REUSEADDR diff --git a/pjlib/src/pj/ioqueue_common_abs.c b/pjlib/src/pj/ioqueue_common_abs.c index a9a6a9cfd..a0d17e72e 100644 --- a/pjlib/src/pj/ioqueue_common_abs.c +++ b/pjlib/src/pj/ioqueue_common_abs.c @@ -1056,7 +1056,10 @@ retry_on_restart: /* * Check that address storage can hold the address parameter. */ - PJ_ASSERT_RETURN(addrlen <= (int)sizeof(pj_sockaddr), PJ_EBUG); + PJ_ASSERT_RETURN((((pj_sockaddr*)addr)->addr.sa_family == pj_AF_INET() && + addrlen <= (int)sizeof(pj_sockaddr_in)) || + (((pj_sockaddr*)addr)->addr.sa_family == pj_AF_INET6() && + addrlen <= (int)sizeof(pj_sockaddr_in6)), PJ_EBUG); /* * Schedule asynchronous send. diff --git a/pjlib/src/pj/os_core_unix.c b/pjlib/src/pj/os_core_unix.c index c90c5ef69..233bbbb00 100644 --- a/pjlib/src/pj/os_core_unix.c +++ b/pjlib/src/pj/os_core_unix.c @@ -71,7 +71,7 @@ JavaVM *pj_jni_jvm = NULL; JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { pj_jni_jvm = vm; - + return JNI_VERSION_1_4; } @@ -845,6 +845,18 @@ PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) return rc; } +#if PJ_DARWINOS +static pthread_key_t key; +static pthread_once_t key_once = PTHREAD_ONCE_INIT; + +static void +make_key() +{ + (void) pthread_key_create(&key, free); +} +#endif + + /* * pj_thread_this() */ @@ -854,9 +866,26 @@ PJ_DEF(pj_thread_t*) pj_thread_this(void) pj_thread_t *rec = (pj_thread_t*)pj_thread_local_get(thread_tls_id); if (rec == NULL) { - pj_assert(!"Calling pjlib from unknown/external thread. You must " - "register external threads with pj_thread_register() " - "before calling any pjlib functions."); + + static pj_thread_t* dummy; + +#if PJ_DARWINOS + (void) pthread_once(&key_once, make_key); + + pj_thread_t* desc; + + if ((desc = pthread_getspecific(key)) == NULL) { + desc = malloc(sizeof(pj_thread_desc)); + pj_bzero(desc, sizeof(pj_thread_desc)); + (void) pthread_setspecific(key, desc); + } +#else + static __thread pj_thread_desc desc; +#endif + + pj_thread_register(NULL, (long*)desc, &dummy); + + rec = (pj_thread_t*)pj_thread_local_get(thread_tls_id); } /* @@ -1049,7 +1078,7 @@ PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var ) pj_status_t status; PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL); - + #if PJ_HAS_THREADS status = pj_mutex_destroy( atomic_var->mutex ); if (status == PJ_SUCCESS) { diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c index 68a538dcb..d2ee5b180 100644 --- a/pjlib/src/pj/os_core_win32.c +++ b/pjlib/src/pj/os_core_win32.c @@ -655,9 +655,10 @@ PJ_DEF(pj_thread_t*) pj_thread_this(void) pj_thread_t *rec = pj_thread_local_get(thread_tls_id); if (rec == NULL) { - pj_assert(!"Calling pjlib from unknown/external thread. You must " - "register external threads with pj_thread_register() " - "before calling any pjlib functions."); + static __declspec(thread) pj_thread_desc desc; + static __declspec(thread) pj_thread_t* this_thread; + pj_thread_register(NULL, desc, &this_thread); + rec = (pj_thread_t*)pj_thread_local_get(thread_tls_id); } /* diff --git a/pjlib/src/pj/os_timestamp_posix.c b/pjlib/src/pj/os_timestamp_posix.c index 07ef682a9..0371aad43 100644 --- a/pjlib/src/pj/os_timestamp_posix.c +++ b/pjlib/src/pj/os_timestamp_posix.c @@ -202,7 +202,7 @@ PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) return PJ_SUCCESS; } -#elif defined(__ANDROID__) +#elif defined(PJ_ANDROID) && PJ_ANDROID #include #include diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c index 5f594efa7..ddc8cd9cc 100644 --- a/pjlib/src/pj/sock_bsd.c +++ b/pjlib/src/pj/sock_bsd.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include @@ -35,6 +35,15 @@ #define THIS_FILE "sock_bsd.c" +#if !defined(PJ_WIN32) && !defined(PJ_WIN64) +# if !defined(SOL_TCP) && defined(IPPROTO_TCP) +# define SOL_TCP IPPROTO_TCP +# endif +# if !defined(TCP_KEEPIDLE) && defined(TCP_KEEPALIVE) +# define TCP_KEEPIDLE TCP_KEEPALIVE +# endif +#endif + /* * Address families conversion. * The values here are indexed based on pj_addr_family. @@ -172,7 +181,24 @@ const pj_uint16_t PJ_IPV6_TCLASS = 0xFFFF; const pj_uint16_t PJ_SO_TYPE = SO_TYPE; const pj_uint16_t PJ_SO_RCVBUF = SO_RCVBUF; const pj_uint16_t PJ_SO_SNDBUF = SO_SNDBUF; +const pj_uint16_t PJ_SO_KEEPALIVE = SO_KEEPALIVE; const pj_uint16_t PJ_TCP_NODELAY= TCP_NODELAY; +#if !defined(PJ_WIN32) && !defined(PJ_WIN64) +# ifdef TCP_KEEPIDLE +const pj_uint16_t PJ_TCP_KEEPIDLE = TCP_KEEPIDLE; +# endif +# ifdef TCP_KEEPINTVL +const pj_uint16_t PJ_TCP_KEEPINTVL = TCP_KEEPINTVL; +# endif +# ifdef TCP_USER_TIMEOUT +const pj_uint16_t PJ_TCP_USER_TIMEOUT = TCP_USER_TIMEOUT; +#else +const pj_uint16_t PJ_TCP_USER_TIMEOUT = 18; +# endif +# ifdef TCP_KEEPCNT +const pj_uint16_t PJ_TCP_KEEPCNT = TCP_KEEPCNT; +# endif +#endif const pj_uint16_t PJ_SO_REUSEADDR= SO_REUSEADDR; #ifdef SO_NOSIGPIPE const pj_uint16_t PJ_SO_NOSIGPIPE = SO_NOSIGPIPE; @@ -270,7 +296,7 @@ PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr) /* * This function converts the Internet host address cp from the standard * numbers-and-dots notation into binary data and stores it in the structure - * that inp points to. + * that inp points to. */ PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, pj_in_addr *inp) { @@ -312,7 +338,7 @@ PJ_DEF(pj_status_t) pj_inet_pton(int af, const pj_str_t *src, void *dst) PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EAFNOTSUP); PJ_ASSERT_RETURN(src && src->slen && dst, PJ_EINVAL); - /* Initialize output with PJ_IN_ADDR_NONE for IPv4 (to be + /* Initialize output with PJ_IN_ADDR_NONE for IPv4 (to be * compatible with pj_inet_aton() */ if (af==PJ_AF_INET) { @@ -357,7 +383,7 @@ PJ_DEF(pj_status_t) pj_inet_pton(int af, const pj_str_t *src, void *dst) sock_addr.addr.sa_family = (pj_uint16_t)af; rc = WSAStringToAddress( - PJ_STRING_TO_NATIVE(tempaddr,wtempaddr,sizeof(wtempaddr)), + PJ_STRING_TO_NATIVE(tempaddr,wtempaddr,sizeof(wtempaddr)), af, NULL, (LPSOCKADDR)&sock_addr, &addr_len); if (rc != 0) { /* If you get rc 130022 Invalid argument (WSAEINVAL) with IPv6, @@ -505,8 +531,8 @@ PJ_DEF(const pj_str_t*) pj_gethostname(void) /* * Create new socket/endpoint for communication and returns a descriptor. */ -PJ_DEF(pj_status_t) pj_sock_socket(int af, - int type, +PJ_DEF(pj_status_t) pj_sock_socket(int af, + int type, int proto, pj_sock_t *sock) { @@ -514,14 +540,14 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, /* Sanity checks. */ PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL); - PJ_ASSERT_RETURN((SOCKET)PJ_INVALID_SOCKET==INVALID_SOCKET, + PJ_ASSERT_RETURN((SOCKET)PJ_INVALID_SOCKET==INVALID_SOCKET, (*sock=PJ_INVALID_SOCKET, PJ_EINVAL)); *sock = WSASocket(af, type, proto, NULL, 0, WSA_FLAG_OVERLAPPED); - if (*sock == PJ_INVALID_SOCKET) + if (*sock == PJ_INVALID_SOCKET) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - + #if PJ_SOCK_DISABLE_WSAECONNRESET && \ (!defined(PJ_WIN32_WINCE) || PJ_WIN32_WINCE==0) @@ -555,9 +581,9 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, /* * Create new socket/endpoint for communication and returns a descriptor. */ -PJ_DEF(pj_status_t) pj_sock_socket(int af, - int type, - int proto, +PJ_DEF(pj_status_t) pj_sock_socket(int af, + int type, + int proto, pj_sock_t *sock) { int type0 = type; @@ -566,7 +592,7 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, /* Sanity checks. */ PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL); - PJ_ASSERT_RETURN(PJ_INVALID_SOCKET==-1, + PJ_ASSERT_RETURN(PJ_INVALID_SOCKET==-1, (*sock=PJ_INVALID_SOCKET, PJ_EINVAL)); #if !defined(SOCK_CLOEXEC) @@ -584,7 +610,22 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, pj_int32_t val = 1; if ((type & 0xF) == pj_SOCK_STREAM()) { pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), pj_SO_NOSIGPIPE(), - &val, sizeof(val)); + &val, sizeof(val)); + pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), pj_SO_KEEPALIVE(), + &val, sizeof(val)); + pj_sock_setsockopt(*sock, pj_SOL_TCP(), pj_TCP_KEEPCNT(), + &val, sizeof(val)); + val = 30; + pj_sock_setsockopt(*sock, pj_SOL_TCP(), pj_TCP_KEEPIDLE(), + &val, sizeof(val)); + pj_sock_setsockopt(*sock, pj_SOL_TCP(), pj_TCP_KEEPINTVL(), + &val, sizeof(val)); + val = 30000; + pj_sock_setsockopt(*sock, pj_SOL_TCP(), pj_TCP_USER_TIMEOUT(), + &val, sizeof(val)); + val = 1; + pj_sock_setsockopt(*sock, pj_SOL_TCP(), pj_TCP_NODELAY(), + &val, sizeof(val)); } #if defined(PJ_SOCK_HAS_IPV6_V6ONLY) && PJ_SOCK_HAS_IPV6_V6ONLY != 0 if (af == PJ_AF_INET6) { @@ -595,7 +636,7 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 if ((type & 0xF) == pj_SOCK_DGRAM()) { - pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), SO_NOSIGPIPE, + pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), SO_NOSIGPIPE, &val, sizeof(val)); } #endif @@ -612,7 +653,7 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, /* * Bind socket. */ -PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock, +PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock, const pj_sockaddr_t *addr, int len) { @@ -632,7 +673,7 @@ PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock, /* * Bind socket. */ -PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock, +PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock, pj_uint32_t addr32, pj_uint16_t port) { @@ -741,7 +782,7 @@ PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock, { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(len, PJ_EINVAL); - + CHECK_ADDR_LEN(to, tolen); #ifdef MSG_NOSIGNAL @@ -749,12 +790,12 @@ PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock, flags |= MSG_NOSIGNAL; #endif - *len = sendto(sock, (const char*)buf, (int)(*len), flags, + *len = sendto(sock, (const char*)buf, (int)(*len), flags, (const struct sockaddr*)to, tolen); - if (*len < 0) + if (*len < 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else + else return PJ_SUCCESS; } @@ -771,7 +812,7 @@ PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock, *len = recv(sock, (char*)buf, (int)(*len), flags); - if (*len < 0) + if (*len < 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else return PJ_SUCCESS; @@ -790,10 +831,10 @@ PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock, PJ_CHECK_STACK(); PJ_ASSERT_RETURN(buf && len, PJ_EINVAL); - *len = recvfrom(sock, (char*)buf, (int)(*len), flags, + *len = recvfrom(sock, (char*)buf, (int)(*len), flags, (struct sockaddr*)from, (socklen_t*)fromlen); - if (*len < 0) + if (*len < 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else { if (from) { @@ -832,12 +873,12 @@ PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock, { int status; PJ_CHECK_STACK(); - + #if (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_SUNOS) && PJ_SUNOS) /* Some opt may still need int value (e.g:SO_EXCLUSIVEADDRUSE in win32). */ - status = setsockopt(sock, - level, - ((optname&0xff00)==0xff00)?(int)optname|0xffff0000:optname, + status = setsockopt(sock, + level, + ((optname&0xff00)==0xff00)?(int)optname|0xffff0000:optname, (const char*)optval, optlen); #else status = setsockopt(sock, level, optname, (const char*)optval, optlen); @@ -861,12 +902,12 @@ PJ_DEF(pj_status_t) pj_sock_setsockopt_params( pj_sock_t sockfd, pj_status_t retval = PJ_SUCCESS; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(params, PJ_EINVAL); - + for (;icnt && ioptions[i].level, (pj_uint16_t)params->options[i].optname, - params->options[i].optval, + params->options[i].optval, params->options[i].optlen); if (status != PJ_SUCCESS) { retval = status; @@ -937,18 +978,18 @@ PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd, PJ_SOCKADDR_SET_LEN(addr, *addrlen); } #endif - + *newsock = accept(serverfd, (struct sockaddr*)addr, (socklen_t*)addrlen); if (*newsock==PJ_INVALID_SOCKET) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else { - + #if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0 if (addr) { PJ_SOCKADDR_RESET_LEN(addr); } #endif - + return PJ_SUCCESS; } } diff --git a/pjlib/src/pj/sock_common.c b/pjlib/src/pj/sock_common.c index 62b08bdea..e9f78ff4f 100644 --- a/pjlib/src/pj/sock_common.c +++ b/pjlib/src/pj/sock_common.c @@ -1649,11 +1649,36 @@ PJ_DEF(pj_uint16_t) pj_SO_SNDBUF(void) return PJ_SO_SNDBUF; } +PJ_DEF(pj_uint16_t) pj_SO_KEEPALIVE(void) +{ + return PJ_SO_KEEPALIVE; +} + +PJ_DEF(pj_uint16_t) pj_TCP_USER_TIMEOUT(void) +{ + return PJ_TCP_USER_TIMEOUT; +} + PJ_DEF(pj_uint16_t) pj_TCP_NODELAY(void) { return PJ_TCP_NODELAY; } +PJ_DEF(pj_uint16_t) pj_TCP_KEEPIDLE(void) +{ + return PJ_TCP_KEEPIDLE +} + +PJ_DEF(pj_uint16_t) pj_TCP_KEEPINTVL(void) +{ + return PJ_TCP_KEEPINTVL +} + +PJ_DEF(pj_uint16_t) pj_TCP_KEEPCNT(void) +{ + return PJ_TCP_KEEPCNT +} + PJ_DEF(pj_uint16_t) pj_SO_REUSEADDR(void) { return PJ_SO_REUSEADDR; diff --git a/pjlib/src/pj/sock_uwp.cpp b/pjlib/src/pj/sock_uwp.cpp index 14ce05875..2230af9d1 100644 --- a/pjlib/src/pj/sock_uwp.cpp +++ b/pjlib/src/pj/sock_uwp.cpp @@ -69,6 +69,24 @@ const pj_uint16_t PJ_SOL_IP = IPPROTO_IP; const pj_uint16_t PJ_SOL_IP = 0; #endif /* SOL_IP */ +#if defined(TCP_KEEPIDLE) +const pj_uint16_t PJ_TCP_KEEPIDLE = TCP_KEEPIDLE; +#else +const pj_uint16_t PJ_TCP_KEEPIDLE = 4; +#endif + +#if defined(TCP_KEEPINTVL) +const pj_uint16_t PJ_TCP_KEEPINTVL = TCP_KEEPINTVL; +#else +const pj_uint16_t PJ_TCP_KEEPINTVL = 5; +#endif + +#if defined(TCP_KEEPCNT) +const pj_uint16_t PJ_TCP_KEEPCNT = TCP_KEEPCNT; +#else +const pj_uint16_t PJ_TCP_KEEPCNT = 6; +#endif + #if defined(SOL_TCP) const pj_uint16_t PJ_SOL_TCP = SOL_TCP; #elif defined(IPPROTO_TCP) @@ -79,6 +97,18 @@ const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP; const pj_uint16_t PJ_SOL_TCP = 6; #endif /* SOL_TCP */ +#if defined(TCP_USER_TIMEOUT) +const pj_uint16_t PJ_TCP_USER_TIMEOUT = TCP_USER_TIMEOUT; +#else +const pj_uint16_t PJ_TCP_USER_TIMEOUT = 18; +#endif + +#if defined(SOL_KEEPALIVE) +const pj_uint16_t PJ_SOL_KEEPALIVE = SOL_KEEPALIVE; +#else +const pj_uint16_t PJ_SOL_KEEPALIVE = 9; +#endif + #ifdef SOL_UDP const pj_uint16_t PJ_SOL_UDP = SOL_UDP; #elif defined(IPPROTO_UDP) diff --git a/pjlib/src/pj/symbols.c b/pjlib/src/pj/symbols.c index ad56c4f98..f224300c9 100644 --- a/pjlib/src/pj/symbols.c +++ b/pjlib/src/pj/symbols.c @@ -258,6 +258,10 @@ PJ_EXPORT_SYMBOL(PJ_SOCK_RAW) PJ_EXPORT_SYMBOL(PJ_SOCK_RDM) PJ_EXPORT_SYMBOL(PJ_SOL_SOCKET) PJ_EXPORT_SYMBOL(PJ_SOL_IP) +PJ_EXPORT_SYMBOL(PJ_TCP_KEEPIDLE) +PJ_EXPORT_SYMBOL(PJ_TCP_KEEPINTVL) +PJ_EXPORT_SYMBOL(PJ_TCP_KEEPCNT) +PJ_EXPORT_SYMBOL(PJ_TCP_USER_TIMEOUT) PJ_EXPORT_SYMBOL(PJ_SOL_TCP) PJ_EXPORT_SYMBOL(PJ_SOL_UDP) PJ_EXPORT_SYMBOL(PJ_SOL_IPV6) diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h index e904c3ac4..bd988d3d5 100644 --- a/pjnath/include/pjnath/config.h +++ b/pjnath/include/pjnath/config.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_CONFIG_H__ #define __PJNATH_CONFIG_H__ @@ -65,9 +65,9 @@ /** * The default initial STUN round-trip time estimation (the RTO value - * in RFC 3489-bis), in miliseconds. - * This value is used to control the STUN request - * retransmit time. The initial value of retransmission interval + * in RFC 3489-bis), in miliseconds. + * This value is used to control the STUN request + * retransmit time. The initial value of retransmission interval * would be set to this value, and will be doubled after each * retransmission. */ @@ -78,7 +78,7 @@ /** * The STUN transaction timeout value, in miliseconds. - * After the last retransmission is sent and if no response is received + * After the last retransmission is sent and if no response is received * after this time, the STUN transaction will be considered to have failed. * * The default value is 16x RTO (as per RFC 3489-bis). @@ -201,8 +201,8 @@ /** - * Number of seconds to refresh the permission/channel binding before the - * permission/channel binding expires. This value should be greater than + * Number of seconds to refresh the permission/channel binding before the + * permission/channel binding expires. This value should be greater than * PJ_TURN_PERM_TIMEOUT setting. */ #ifndef PJ_TURN_REFRESH_SEC_BEFORE @@ -211,7 +211,7 @@ /** - * The TURN session timer heart beat interval. When this timer occurs, the + * The TURN session timer heart beat interval. When this timer occurs, the * TURN session will scan all the permissions/channel bindings to see which * need to be refreshed. */ @@ -278,7 +278,7 @@ * the maximum number of components (PJ_ICE_MAX_COMP) value. */ #ifndef PJ_ICE_COMP_BITS -# define PJ_ICE_COMP_BITS 1 +# define PJ_ICE_COMP_BITS 2 #endif @@ -310,10 +310,10 @@ /** * The number of bits to represent ICE candidate's local preference. The * local preference is used to specify preference among candidates with - * the same type, and ICE draft suggests 65535 as the default local - * preference, which means we need 16 bits to represent the value. But + * the same type, and ICE draft suggests 65535 as the default local + * preference, which means we need 16 bits to represent the value. But * since we don't have the facility to specify local preference, we'll - * just disable this feature and let the preference sorted by the + * just disable this feature and let the preference sorted by the * type only. * * Default: 0 @@ -339,20 +339,20 @@ * Default: 20 */ #ifndef PJ_ICE_TA_VAL -# define PJ_ICE_TA_VAL 20 +# define PJ_ICE_TA_VAL 50 #endif /** - * According to ICE Section 8.2. Updating States, if an In-Progress pair in - * the check list is for the same component as a nominated pair, the agent + * According to ICE Section 8.2. Updating States, if an In-Progress pair in + * the check list is for the same component as a nominated pair, the agent * SHOULD cease retransmissions for its check if its pair priority is lower * than the lowest priority nominated pair for that component. * * If a higher priority check is In Progress, this rule would cause that * check to be performed even when it most likely will fail. * - * The macro here controls if ICE session should cancel all In Progress + * The macro here controls if ICE session should cancel all In Progress * checks for the same component regardless of its priority. * * Default: 1 (yes, cancel all) @@ -382,6 +382,42 @@ # define ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT 10000 #endif +/** + * For TCP transport, this timer is time that a controlling agent must wait for + * incoming checks if the local candidate is of type "passive" or "s-o". + * + * Default: 10000 (milliseconds) + */ +#ifndef ICE_CONTROLLING_PASSIVE_TIMEOUT +# define ICE_CONTROLLING_PASSIVE_TIMEOUT 10000 +#endif + +/** + * Allowed timeout for pending connections. TCP only. + * + * Default: 15000 (milliseconds) + */ +#ifndef PJ_ICE_TCP_CONNECTION_TIMEOUT +# define PJ_ICE_TCP_CONNECTION_TIMEOUT 15000 +#endif + +/** + * Delay between two reconnection attempts. TCP only. + * + * Default: 500 (milliseconds) + */ +#ifndef PJ_ICE_TCP_RECONNECTION_DELAY +# define PJ_ICE_TCP_RECONNECTION_DELAY 500 +#endif + +/** + * Maximum number of reconnection attempts. TCP only. + * + * Default: 24 + */ +#ifndef PJ_ICE_TCP_MAX_RECONNECTION_COUNT +# define PJ_ICE_TCP_MAX_RECONNECTION_COUNT 24 +#endif /** * For controlling agent if it uses regular nomination, specify the delay to @@ -583,7 +619,7 @@ /** Default duration for searching UPnP Internet Gateway Devices (in seconds). * Default: 5 seconds */ -#ifndef PJ_UPNP_DEFAULT_SEARCH_TIME +#ifndef PJ_UPNP_DEFAULT_SEARCH_TIME # define PJ_UPNP_DEFAULT_SEARCH_TIME 5 #endif diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h index e796b2539..60e0564db 100644 --- a/pjnath/include/pjnath/ice_session.h +++ b/pjnath/include/pjnath/ice_session.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_ICE_SESSION_H__ #define __PJNATH_ICE_SESSION_H__ @@ -41,7 +41,7 @@ PJ_BEGIN_DECL * * \section pj_ice_sess_sec ICE Session * - * An ICE session, represented by #pj_ice_sess structure, is the lowest + * An ICE session, represented by #pj_ice_sess structure, is the lowest * abstraction of ICE in PJNATH, and it is used to perform and manage * connectivity checks of transport address candidates within a * single media stream (note: this differs from what is described @@ -50,12 +50,12 @@ PJ_BEGIN_DECL * * The ICE session described here is independent from any transports, * meaning that the actual network I/O for this session would have to - * be performed by the application, or higher layer abstraction. + * be performed by the application, or higher layer abstraction. * Using this framework, application would give any incoming packets to * the ICE session, and it would provide the ICE session with a callback * to send outgoing message. * - * For higher abstraction of ICE where transport is included, please + * For higher abstraction of ICE where transport is included, please * see \ref PJNATH_ICE_STREAM_TRANSPORT. * * \subsection pj_ice_sess_using_sec Using The ICE Session @@ -162,6 +162,52 @@ typedef enum pj_ice_cand_type } pj_ice_cand_type; +/** + * ICE candidates types like described by RFC 6544. + */ +typedef enum pj_ice_cand_transport { + /** + * Candidates UDP compatible + */ + PJ_CAND_UDP, + /** + * Candidates sending outgoing TCP connections + */ + PJ_CAND_TCP_ACTIVE, + /** + * Candidates accepting incoming TCP connections + */ + PJ_CAND_TCP_PASSIVE, + /** + * Candidates capable of receiving incoming connections and sending + * connections + */ + PJ_CAND_TCP_SO +} pj_ice_cand_transport; + +/** + * ICE transport types, which will be used both to specify the connection + * type for reaching candidates and other client + */ +typedef enum pj_ice_tp_type { + /** + * UDP transport, which value corresponds to IANA protocol number. + */ + PJ_ICE_TP_UDP = 17, + + /** + * TCP transport, which value corresponds to IANA protocol number. + */ + PJ_ICE_TP_TCP = 6, + + /** + * TLS transport. The TLS transport will only be used as the connection + * type to reach the server and never as the allocation transport type. + */ + PJ_ICE_TP_TLS = 255 + +} pj_ice_tp_type; + /** Forward declaration for pj_ice_sess */ typedef struct pj_ice_sess pj_ice_sess; @@ -169,12 +215,9 @@ typedef struct pj_ice_sess pj_ice_sess; /** Forward declaration for pj_ice_sess_check */ typedef struct pj_ice_sess_check pj_ice_sess_check; -/** Forward declaration for pj_ice_sess_cand */ -typedef struct pj_ice_sess_cand pj_ice_sess_cand; - /** - * This structure describes ICE component. - * A media stream may require multiple components, each of which has + * This structure describes ICE component. + * A media stream may require multiple components, each of which has * to work for the media stream as a whole to work. For media streams * based on RTP, there are two components per media stream - one for RTP, * and one for RTCP. @@ -204,32 +247,6 @@ typedef struct pj_ice_sess_comp } pj_ice_sess_comp; -/** - * Data structure to be attached to internal message processing. - */ -typedef struct pj_ice_msg_data -{ - /** Transport ID for this message */ - unsigned transport_id; - - /** Flag to indicate whether data.req contains data */ - pj_bool_t has_req_data; - - /** The data */ - union data { - /** Request data */ - struct request_data { - pj_ice_sess *ice; /**< ICE session */ - pj_ice_sess_checklist *clist; /**< Checklist */ - unsigned ckid; /**< Check ID */ - pj_ice_sess_cand *lcand; /**< Local cand */ - pj_ice_sess_cand *rcand; /**< Remote cand */ - } req; /**< Request data */ - } data; /**< The data */ - -} pj_ice_msg_data; - - /** * This structure describes an ICE candidate. * ICE candidate is a transport address that is to be tested by ICE @@ -238,7 +255,7 @@ typedef struct pj_ice_msg_data * (server reflexive, relayed or host), priority, foundation, and * base. */ -struct pj_ice_sess_cand +typedef struct pj_ice_sess_cand { /** * The candidate ID. @@ -250,10 +267,10 @@ struct pj_ice_sess_cand */ pj_ice_cand_type type; - /** + /** * Status of this candidate. The value will be PJ_SUCCESS if candidate * address has been resolved successfully, PJ_EPENDING when the address - * resolution process is in progress, or other value when the address + * resolution process is in progress, or other value when the address * resolution has completed with failure. */ pj_status_t status; @@ -277,8 +294,8 @@ struct pj_ice_sess_cand /** * The foundation string, which is an identifier which value will be - * equivalent for two candidates that are of the same type, share the - * same base, and come from the same STUN server. The foundation is + * equivalent for two candidates that are of the same type, share the + * same base, and come from the same STUN server. The foundation is * used to optimize ICE performance in the Frozen algorithm. */ pj_str_t foundation; @@ -295,16 +312,16 @@ struct pj_ice_sess_cand * the local address of the socket. For reflexive candidates, the value * will be the public address allocated in NAT router for the host * candidate and as reported in MAPPED-ADDRESS or XOR-MAPPED-ADDRESS - * attribute of STUN Binding request. For relayed candidate, the value + * attribute of STUN Binding request. For relayed candidate, the value * will be the address allocated in the TURN server by STUN Allocate * request. */ pj_sockaddr addr; /** - * Base address of this candidate. "Base" refers to the address an agent + * Base address of this candidate. "Base" refers to the address an agent * sends from for a particular candidate. For host candidates, the base - * is the same as the host candidate itself. For reflexive candidates, + * is the same as the host candidate itself. For reflexive candidates, * the base is the local IP address of the socket. For relayed candidates, * the base address is the transport address allocated in the TURN server * for this candidate. @@ -317,7 +334,38 @@ struct pj_ice_sess_cand */ pj_sockaddr rel_addr; -}; + /** + * Transport used (TCP or UDP) + */ + pj_ice_cand_transport transport; + +} pj_ice_sess_cand; + + +/** + * Data structure to be attached to internal message processing. + */ +typedef struct pj_ice_msg_data +{ + /** Transport ID for this message */ + unsigned transport_id; + + /** Flag to indicate whether data.req contains data */ + pj_bool_t has_req_data; + + /** The data */ + union data { + /** Request data */ + struct request_data { + pj_ice_sess *ice; /**< ICE session */ + pj_ice_sess_checklist *clist; /**< Checklist */ + unsigned ckid; /**< Check ID */ + pj_ice_sess_cand *lcand; /**< Local cand */ + pj_ice_sess_cand *rcand; /**< Remote cand */ + } req; /**< Request data */ + } data; /**< The data */ + +} pj_ice_msg_data; /** @@ -332,6 +380,22 @@ typedef enum pj_ice_sess_check_state */ PJ_ICE_SESS_CHECK_STATE_FROZEN, + /** + * The following status is used when a packet sent via TURN got a + * "Connection reset by peer". This mean that the peer didn't allow + * us to connect yet. The socket will be reconnected during the next + * loop. + */ + PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, + + /** + * TODO (sblin): REMOVE THIS! - https://github.com/coturn/coturn/issues/408 + * For now, this status is only used because sometimes, the first packet + * doesn't receive any response. So, we retry to send the packet every + * 50 loops. + */ + PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, + /** * A check has not been performed for this pair, and can be * performed as soon as it is the highest priority Waiting pair on @@ -339,6 +403,12 @@ typedef enum pj_ice_sess_check_state */ PJ_ICE_SESS_CHECK_STATE_WAITING, + /** + * A check has not been performed for this pair, but TCP socket + * is currently connecting to the pair. Wait to finish the connection. + */ + PJ_ICE_SESS_CHECK_STATE_PENDING, + /** * A check has not been performed for this pair, and can be * performed as soon as it is the highest priority Waiting pair on @@ -365,9 +435,9 @@ typedef enum pj_ice_sess_check_state /** * This structure describes an ICE connectivity check. An ICE check - * contains a candidate pair, and will involve sending STUN Binding - * Request transaction for the purposes of verifying connectivity. - * A check is sent from the local candidate to the remote candidate + * contains a candidate pair, and will involve sending STUN Binding + * Request transaction for the purposes of verifying connectivity. + * A check is sent from the local candidate to the remote candidate * of a candidate pair. */ struct pj_ice_sess_check @@ -398,8 +468,8 @@ struct pj_ice_sess_check pj_ice_sess_check_state state; /** - * STUN transmit data containing STUN Binding request that was sent - * as part of this check. The value will only be set when this check + * STUN transmit data containing STUN Binding request that was sent + * as part of this check. The value will only be set when this check * has a pending transaction, and is used to cancel the transaction * when other check has succeeded. */ @@ -416,6 +486,13 @@ struct pj_ice_sess_check * STUN transaction. */ pj_status_t err_code; + +#if PJ_HAS_TCP + /** + * TCP reconnection attemps counter. + */ + int reconnect_count; +#endif }; @@ -445,7 +522,7 @@ typedef enum pj_ice_sess_checklist_state /** - * This structure represents ICE check list, that is an ordered set of + * This structure represents ICE check list, that is an ordered set of * candidate pairs that an agent will use to generate checks. */ struct pj_ice_sess_checklist @@ -509,7 +586,7 @@ typedef struct pj_ice_sess_cb /** * A mandatory callback which will be called by the ICE session when - * it needs to send outgoing STUN packet. + * it needs to send outgoing STUN packet. * * @param ice The ICE session. * @param comp_id ICE component ID. @@ -519,7 +596,7 @@ typedef struct pj_ice_sess_cb * @param dst_addr Packet destination address. * @param dst_addr_len Length of destination address. */ - pj_status_t (*on_tx_pkt)(pj_ice_sess *ice, unsigned comp_id, + pj_status_t (*on_tx_pkt)(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, @@ -534,15 +611,49 @@ typedef struct pj_ice_sess_cb * @param transport_id Transport ID. * @param pkt The whole packet. * @param size Size of the packet. - * @param src_addr Source address where this packet was received + * @param src_addr Source address where this packet was received * from. * @param src_addr_len The length of source address. */ - void (*on_rx_data)(pj_ice_sess *ice, unsigned comp_id, - unsigned transport_id, - void *pkt, pj_size_t size, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len); + void (*on_rx_data)(pj_ice_sess *ice, unsigned comp_id, + unsigned transport_id, + void *pkt, pj_size_t size, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); + + /** + * Wait for TCP and send connectivity check + * + * @param ice The ICE session. + * @param check_id The wanted check. + */ + pj_status_t (*wait_tcp_connection)(pj_ice_sess *ice, + unsigned check_id); + + /** + * Reconnect a resetted TCP connection and send connectivity check + * cf. PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY + * + * @param ice The ICE session. + * @param check_id The wanted check. + */ + pj_status_t (*reconnect_tcp_connection)(pj_ice_sess *ice, + unsigned check_id); + + /** + * Close TCP socket + * + * @param ice The ICE session. + * @param check_id The wanted check. + */ + pj_status_t (*close_tcp_connection)(pj_ice_sess *ice, + unsigned check_id); + + /** + * If an internal TCP keep alive, this mount the error to the application + */ + void (*on_ice_destroy)(pj_ice_sess *ice); + } pj_ice_sess_cb; @@ -630,7 +741,7 @@ typedef enum pj_ice_sess_trickle /** * This structure describes various ICE session options. Application - * configure the ICE session with these options by calling + * configure the ICE session with these options by calling * #pj_ice_sess_set_options(). */ typedef struct pj_ice_sess_options @@ -643,7 +754,7 @@ typedef struct pj_ice_sess_options /** * For controlling agent if it uses regular nomination, specify the delay - * to perform nominated check (connectivity check with USE-CANDIDATE + * to perform nominated check (connectivity check with USE-CANDIDATE * attribute) after all components have a valid pair. * * Default value is PJ_ICE_NOMINATED_CHECK_DELAY. @@ -651,14 +762,14 @@ typedef struct pj_ice_sess_options unsigned nominated_check_delay; /** - * For a controlled agent, specify how long it wants to wait (in - * milliseconds) for the controlling agent to complete sending + * For a controlled agent, specify how long it wants to wait (in + * milliseconds) for the controlling agent to complete sending * connectivity check with nominated flag set to true for all components * after the controlled agent has found that all connectivity checks in * its checklist have been completed and there is at least one successful * (but not nominated) check for every component. * - * Default value for this option is + * Default value for this option is * ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT. Specify -1 to disable * this timer. */ @@ -672,6 +783,13 @@ typedef struct pj_ice_sess_options */ pj_ice_sess_trickle trickle; + /** + * For a controlling agent, specify how long it wants to wait + * in milliseconds for passive candidates and wait for connection + * attempts + */ + int agent_passive_timeout; + } pj_ice_sess_options; @@ -704,6 +822,7 @@ struct pj_ice_sess pj_status_t ice_status; /**< Error status. */ pj_timer_entry timer; /**< ICE timer. */ pj_timer_entry timer_end_of_cand; /**< End-of-cand timer. */ + pj_timer_entry timer_connect; /**< ICE timer tcp timeout*/ pj_ice_sess_cb cb; /**< Callback. */ pj_stun_config stun_cfg; /**< STUN settings. */ @@ -741,10 +860,10 @@ struct pj_ice_sess /* Checklist */ pj_ice_sess_checklist clist; /**< Active checklist */ - + /* Valid list */ pj_ice_sess_checklist valid_list; /**< Valid list. */ - + /** Temporary buffer for misc stuffs to avoid using stack too much */ union { char txt[128]; @@ -813,7 +932,7 @@ PJ_DECL(void) pj_ice_sess_options_default(pj_ice_sess_options *opt); * @param cb ICE callback. * @param local_ufrag Optional string to be used as local username to * authenticate incoming STUN binding request. If - * the value is NULL, a random string will be + * the value is NULL, a random string will be * generated. * @param local_passwd Optional string to be used as local password. * @param grp_lock Optional group lock to be used by this session. @@ -911,8 +1030,8 @@ PJ_DECL(pj_status_t) pj_ice_sess_change_role(pj_ice_sess *ice, /** * Assign a custom preference values for ICE candidate types. By assigning * custom preference value, application can control the order of candidates - * to be checked first. The default preference settings is to use 126 for - * host candidates, 100 for server reflexive candidates, 110 for peer + * to be checked first. The default preference settings is to use 126 for + * host candidates, 100 for server reflexive candidates, 110 for peer * reflexive candidates, an 0 for relayed candidates. * * Note that this function must be called before any candidates are added @@ -932,7 +1051,7 @@ PJ_DECL(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice, /** * Add a candidate to this ICE session. Application must add candidates for - * each components ID before it can start pairing the candidates and + * each components ID before it can start pairing the candidates and * performing connectivity checks. * * @param ice ICE session instance. @@ -948,6 +1067,7 @@ PJ_DECL(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice, * @param rel_addr Optional related address. * @param addr_len Length of addresses. * @param p_cand_id Optional pointer to receive the candidate ID. + * @param transport Candidate's type * * @return PJ_SUCCESS if candidate is successfully added. */ @@ -961,14 +1081,15 @@ PJ_DECL(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, const pj_sockaddr_t *base_addr, const pj_sockaddr_t *rel_addr, int addr_len, - unsigned *p_cand_id); + unsigned *p_cand_id, + pj_ice_cand_transport transport); /** * Find default candidate for the specified component ID, using this * rule: * - if the component has a successful candidate pair, then the * local candidate of this pair will be returned. - * - otherwise a relay, reflexive, or host candidate will be selected + * - otherwise a relay, reflexive, or host candidate will be selected * on that specified order. * * @param ice The ICE session instance. @@ -991,18 +1112,18 @@ PJ_DECL(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, * #pj_ice_sess_start_check(). * * @param ice ICE session instance. - * @param rem_ufrag Remote ufrag, as seen in the SDP received from + * @param rem_ufrag Remote ufrag, as seen in the SDP received from * the remote agent. * @param rem_passwd Remote password, as seen in the SDP received from * the remote agent. * @param rem_cand_cnt Number of remote candidates. * @param rem_cand Remote candidate array. Remote candidates are - * gathered from the SDP received from the remote + * gathered from the SDP received from the remote * agent. * * @return PJ_SUCCESS or the appropriate error code. */ -PJ_DECL(pj_status_t) +PJ_DECL(pj_status_t) pj_ice_sess_create_check_list(pj_ice_sess *ice, const pj_str_t *rem_ufrag, const pj_str_t *rem_passwd, @@ -1020,13 +1141,13 @@ pj_ice_sess_create_check_list(pj_ice_sess *ice, * This function is only applicable when trickle ICE is not disabled. * * @param ice ICE session instance. - * @param rem_ufrag Remote ufrag, as seen in the SDP received from + * @param rem_ufrag Remote ufrag, as seen in the SDP received from * the remote agent. * @param rem_passwd Remote password, as seen in the SDP received from * the remote agent. * @param rem_cand_cnt Number of remote candidates. * @param rem_cand Remote candidate array. Remote candidates are - * gathered from the SDP received from the remote + * gathered from the SDP received from the remote * agent. * @param trickle_done Flag to indicate end of trickling, set to PJ_TRUE * after all local candidates have been gathered AND @@ -1035,7 +1156,7 @@ pj_ice_sess_create_check_list(pj_ice_sess *ice, * * @return PJ_SUCCESS or the appropriate error code. */ -PJ_DECL(pj_status_t) +PJ_DECL(pj_status_t) pj_ice_sess_update_check_list(pj_ice_sess *ice, const pj_str_t *rem_ufrag, const pj_str_t *rem_passwd, @@ -1108,6 +1229,44 @@ PJ_DECL(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, const pj_sockaddr_t *src_addr, int src_addr_len); +/** + * Notification when ICE session get a new incoming connection + * + * @param ice The ICE session. + * @param transport_id Related transport + * @param status PJ_SUCCESS when connection is made, or any errors + * if the connection has failed (or if the peer has + * disconnected after an established connection). + * @param remote_addr Connected remove address + */ +PJ_DECL(void) ice_sess_on_peer_connection(pj_ice_sess *ice, + pj_uint8_t transport_id, + pj_status_t status, + pj_sockaddr_t* remote_addr); + +/** + * Notification when ICE session get a new resetted connection + * cf PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY + * + * @param ice The ICE session. + * @param transport_id Related transport + * @param remote_addr Connected remove address + */ +PJ_DECL(void) ice_sess_on_peer_reset_connection(pj_ice_sess *ice, + pj_uint8_t transport_id, + pj_sockaddr_t* remote_addr); + +/** + * Notification when ICE session get a new packet + * Used to remove the PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET status + * + * @param ice The ICE session. + * @param transport_id Related transport + * @param remote_addr Connected remove address + */ +PJ_DECL(void) ice_sess_on_peer_packet(pj_ice_sess *ice, + pj_uint8_t transport_id, + pj_sockaddr_t* remote_addr); /** diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h index 0f2510aa4..f8aa41eae 100644 --- a/pjnath/include/pjnath/ice_strans.h +++ b/pjnath/include/pjnath/ice_strans.h @@ -218,6 +218,13 @@ typedef struct pj_ice_strans_cb const pj_ice_sess_cand *cand, pj_bool_t end_of_cand); + /** + * This callback is called if an internal operation fails + * + * @param ice_st The ICE stream transport. + */ + void (*on_destroy)(pj_ice_strans *ice_st); + } pj_ice_strans_cb; @@ -300,6 +307,13 @@ typedef struct pj_ice_strans_stun_cfg */ pj_bool_t ignore_stun_error; + /** + * Type of connection to the STUN server. + * + * Default is PJ_STUN_TP_UDP. + */ + pj_stun_tp_type conn_type; + } pj_ice_strans_stun_cfg; @@ -315,6 +329,13 @@ typedef struct pj_ice_strans_turn_cfg */ int af; + /** + * If we want to use UDP or TCP as described by RFC 6544. + * This will discover candidates via TCP sockets. Then it will + * transfer messages on the transport via TCP. + */ + pj_ice_tp_type protocol; + /** * Optional TURN socket settings. The default values will be * initialized by #pj_turn_sock_cfg_default(). This contains @@ -394,6 +415,13 @@ typedef struct pj_ice_strans_cfg */ int af; + /** + * If we want to use UDP or TCP as described by RFC 6544. + * This will discover candidates via TCP sockets. Then it will + * transfer messages on the transport via TCP. + */ + pj_ice_tp_type protocol; + /** * STUN configuration which contains the timer heap and * ioqueue instance to be used, and STUN retransmission diff --git a/pjnath/include/pjnath/stun_session.h b/pjnath/include/pjnath/stun_session.h index 4a5076bb1..56cc7dc3a 100644 --- a/pjnath/include/pjnath/stun_session.h +++ b/pjnath/include/pjnath/stun_session.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_STUN_SESSION_H__ #define __PJNATH_STUN_SESSION_H__ @@ -40,26 +40,26 @@ PJ_BEGIN_DECL * @addtogroup PJNATH_STUN_SESSION * @{ * - * This is is a transport-independent object to manage a client or server + * This is is a transport-independent object to manage a client or server * STUN session. It has the following features: - * + * * - transport independent:\n * the object does not have it's own socket, but rather it provides * functions and callbacks to send and receive packets. This way the - * object can be used by different transport types (e.g. UDP, TCP, + * object can be used by different transport types (e.g. UDP, TCP, * TLS, etc.) as well as better integration to application which * already has its own means to send and receive packets. - * + * * - authentication management:\n * the object manages STUN authentication throughout the lifetime of * the session. For client sessions, once it's given a credential to * authenticate itself with the server, the object will automatically * add authentication info (the MESSAGE-INTEGRITY) to the request as - * well as authenticate the response. It will also handle long-term + * well as authenticate the response. It will also handle long-term * authentication challenges, including handling of nonce expiration, - * and retry the request automatically. For server sessions, it can + * and retry the request automatically. For server sessions, it can * be configured to authenticate incoming requests automatically. - * + * * - static or dynamic credential:\n * application may specify static or dynamic credential to be used by * the STUN session. Static credential means a static combination of @@ -67,16 +67,16 @@ PJ_BEGIN_DECL * duration), while dynamic credential provides callback to ask the * application about which username/password to use everytime * authentication is about to be performed. - * + * * - client transaction management:\n * outgoing requests may be sent with a STUN transaction for reliability, * and the object will manage the transaction internally (including * performing retransmissions). Application will be notified about the * result of the request when the response arrives (or the transaction * times out). When the request is challenged with authentication, the - * object will retry the request with new authentication info, and + * object will retry the request with new authentication info, and * application will be notified about the final result of this request. - * + * * - server transaction management:\n * application may ask response to incoming requests to be cached by * the object, and in this case the object will check for cached @@ -95,7 +95,7 @@ PJ_BEGIN_DECL * * - create the STUN session:\n * by calling #pj_stun_session_create(). Among other things, this - * function requires the instance of #pj_stun_config and also + * function requires the instance of #pj_stun_config and also * #pj_stun_session_cb structure which stores callbacks to send * outgoing packets as well as to notify application about incoming * STUN requests, responses, and indicates and other events. @@ -124,8 +124,8 @@ PJ_BEGIN_DECL * use #pj_stun_session_send_msg() to send outgoing STUN messages (this * includes STUN requests, indications, and responses). The function has * options whether to retransmit the request (for non reliable transports) - * or to cache the response if we're sending response. This function in - * turn will call the \a on_send_msg() callback of #pj_stun_session_cb + * or to cache the response if we're sending response. This function in + * turn will call the \a on_send_msg() callback of #pj_stun_session_cb * to request the application to send the packet. * * - handling incoming packet:\n @@ -146,7 +146,7 @@ PJ_BEGIN_DECL * * - creating and sending response:\n * create the STUN response with #pj_stun_session_create_res(). This will - * create a transmit data buffer containing a blank STUN response. You + * create a transmit data buffer containing a blank STUN response. You * will then typically need to add STUN attributes that are relevant to * the response, but note that some default attributes will * be added by the session later when the message is sent (such as @@ -157,7 +157,7 @@ PJ_BEGIN_DECL * - convenient way to send response:\n * the #pj_stun_session_respond() is provided as a convenient way to * create and send simple STUN responses, such as error responses. - * + * * - destroying the session:\n * once the session is done, use #pj_stun_session_destroy() to destroy * the session. @@ -173,6 +173,29 @@ typedef struct pj_stun_rx_data pj_stun_rx_data; /** Forward declaration for pj_stun_session */ typedef struct pj_stun_session pj_stun_session; +/** + * STUN transport types, which will be used both to specify the connection + * type for reaching STUN server and the type of allocation transport to be + * requested to server (the REQUESTED-TRANSPORT attribute). + */ +typedef enum pj_stun_tp_type { + /** + * UDP transport, which value corresponds to IANA protocol number. + */ + PJ_STUN_TP_UDP = 17, + + /** + * TCP transport, which value corresponds to IANA protocol number. + */ + PJ_STUN_TP_TCP = 6, + + /** + * TLS transport. The TLS transport will only be used as the connection + * type to reach the server and never as the allocation transport type. + */ + PJ_STUN_TP_TLS = 255 + +} pj_stun_tp_type; /** * This is the callback to be registered to pj_stun_session, to send @@ -186,7 +209,7 @@ typedef struct pj_stun_session_cb * * @param sess The STUN session. * @param token The token associated with this outgoing message - * and was set by the application. This token was + * and was set by the application. This token was * set by application in pj_stun_session_send_msg() * for outgoing messages that are initiated by the * application, or in pj_stun_session_on_rx_pkt() @@ -209,11 +232,11 @@ typedef struct pj_stun_session_cb const pj_sockaddr_t *dst_addr, unsigned addr_len); - /** + /** * Callback to be called on incoming STUN request message. This function * is called when application calls pj_stun_session_on_rx_pkt() and when * the STUN session has detected that the incoming STUN message is a - * STUN request message. In the + * STUN request message. In the * callback processing, application MUST create a response by calling * pj_stun_session_create_response() function and send the response * with pj_stun_session_send_msg() function, before returning from @@ -241,7 +264,7 @@ typedef struct pj_stun_session_cb unsigned src_addr_len); /** - * Callback to be called when response is received or the transaction + * Callback to be called when response is received or the transaction * has timed out. This callback is called either when application calls * pj_stun_session_on_rx_pkt() with the packet containing a STUN * response for the client transaction, or when the internal timer of @@ -254,7 +277,7 @@ typedef struct pj_stun_session_cb * or other error has occurred, and the response * argument may be NULL. * Note that when the status is not success, the - * response may contain non-NULL value if the + * response may contain non-NULL value if the * response contains STUN ERROR-CODE attribute. * @param token The token that was set by the application when * calling pj_stun_session_send_msg() function. @@ -264,9 +287,9 @@ typedef struct pj_stun_session_cb * @param response The response message, on successful transaction, * or otherwise MAY BE NULL if status is not success. * Note that when the status is not success, this - * argument may contain non-NULL value if the + * argument may contain non-NULL value if the * response contains STUN ERROR-CODE attribute. - * @param src_addr The source address where the response was + * @param src_addr The source address where the response was * received, or NULL if the response is NULL. * @param src_addr_len The length of the source address. */ @@ -306,6 +329,38 @@ typedef struct pj_stun_session_cb const pj_sockaddr_t *src_addr, unsigned src_addr_len); + /** + * Notification when STUN session get a ConnectionAttempt indication. + * + * @param stun_session The STUN session. + * @param status PJ_SUCCESS when connection is made, or any errors + * if the connection has failed (or if the peer has + * disconnected after an established connection). + * @param remote_addr The remote connected + */ + void (*on_peer_connection)(pj_stun_session *sess, + pj_status_t status, + pj_sockaddr_t* remote_addr); + + /** + * Notification when STUN connection is resetted (TCP only). + * + * @param stun_session The STUN session. + * @param remote_addr The remote resetted + */ + void (*on_peer_reset_connection)(pj_stun_session *sess, + pj_sockaddr_t* + remote_addr); + + /** + * Notification when STUN connection is resetted (TCP only). + * + * @param stun_session The STUN session. + * @param remote_addr The remote resetted + */ + void (*on_peer_packet)(pj_stun_session *sess, + pj_sockaddr_t* remote_addr); + } pj_stun_session_cb; @@ -320,8 +375,8 @@ struct pj_stun_rx_data pj_stun_msg *msg; /** - * Credential information that is found and used to authenticate - * incoming request. Application may use this information when + * Credential information that is found and used to authenticate + * incoming request. Application may use this information when * generating authentication for the outgoing response. */ pj_stun_req_cred_info info; @@ -348,7 +403,7 @@ struct pj_stun_tx_data pj_bool_t retransmit; /**< Retransmit request? */ pj_uint32_t msg_magic; /**< Message magic. */ pj_uint8_t msg_key[12]; /**< Message/transaction key. */ - + pj_grp_lock_t *grp_lock; /**< Group lock (for resp cache). */ pj_stun_req_cred_info auth_info; /**< Credential info */ @@ -390,6 +445,7 @@ typedef enum pj_stun_sess_msg_log_flag * @param grp_lock Optional group lock to be used by this session. * If NULL, the session will create one itself. * @param p_sess Pointer to receive STUN session instance. + * @param conn_type If the session use UDP or TCP * * @return PJ_SUCCESS on success, or the appropriate error code. */ @@ -398,7 +454,8 @@ PJ_DECL(pj_status_t) pj_stun_session_create(pj_stun_config *cfg, const pj_stun_session_cb *cb, pj_bool_t fingerprint, pj_grp_lock_t *grp_lock, - pj_stun_session **p_sess); + pj_stun_session **p_sess, + pj_stun_tp_type conn_type); /** * Destroy the STUN session and all objects created in the context of @@ -499,7 +556,7 @@ PJ_DECL(pj_bool_t) pj_stun_session_use_fingerprint(pj_stun_session *sess, /** * Create a STUN request message. After the message has been successfully - * created, application can send the message by calling + * created, application can send the message by calling * pj_stun_session_send_msg(). * * @param sess The STUN session instance. @@ -520,7 +577,7 @@ PJ_DECL(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, /** * Create a STUN Indication message. After the message has been successfully - * created, application can send the message by calling + * created, application can send the message by calling * pj_stun_session_send_msg(). * * @param sess The STUN session instance. @@ -537,8 +594,8 @@ PJ_DECL(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess, pj_stun_tx_data **p_tdata); /** - * Create a STUN response message. After the message has been - * successfully created, application can send the message by calling + * Create a STUN response message. After the message has been + * successfully created, application can send the message by calling * pj_stun_session_send_msg(). Alternatively application may use * pj_stun_session_respond() to create and send response in one function * call. @@ -576,8 +633,8 @@ PJ_DECL(pj_status_t) pj_stun_session_create_res(pj_stun_session *sess, * @param sess The STUN session instance. * @param token Optional token which will be given back to application in * \a on_send_msg() callback and \a on_request_complete() - * callback, if the message is a STUN request message. - * Internally this function will put the token in the + * callback, if the message is a STUN request message. + * Internally this function will put the token in the * \a token field of pj_stun_tx_data, hence it will * overwrite any value that the application puts there. * @param cache_res If the message is a response message for an incoming @@ -595,8 +652,8 @@ PJ_DECL(pj_status_t) pj_stun_session_create_res(pj_stun_session *sess, * be sent. * * @return PJ_SUCCESS on success, or the appropriate error code. - * This function will return PJNATH_ESTUNDESTROYED if - * application has destroyed the session in + * This function will return PJNATH_ESTUNDESTROYED if + * application has destroyed the session in * \a on_send_msg() callback. */ PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess, @@ -625,30 +682,30 @@ PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess, * be used. * @param token Optional token which will be given back to application in * \a on_send_msg() callback and \a on_request_complete() - * callback, if the message is a STUN request message. - * Internally this function will put the token in the + * callback, if the message is a STUN request message. + * Internally this function will put the token in the * \a token field of pj_stun_tx_data, hence it will * overwrite any value that the application puts there. * @param cache Specify whether session should cache this response for * future request retransmission. If TRUE, subsequent request - * retransmission will be handled by the session and it + * retransmission will be handled by the session and it * will not call request callback. * @param dst_addr Destination address of the response (or equal to the * source address of the original request). * @param addr_len Address length. * * @return PJ_SUCCESS on success, or the appropriate error code. - * This function will return PJNATH_ESTUNDESTROYED if - * application has destroyed the session in + * This function will return PJNATH_ESTUNDESTROYED if + * application has destroyed the session in * \a on_send_msg() callback. */ -PJ_DECL(pj_status_t) pj_stun_session_respond(pj_stun_session *sess, +PJ_DECL(pj_status_t) pj_stun_session_respond(pj_stun_session *sess, const pj_stun_rx_data *rdata, - unsigned code, + unsigned code, const char *err_msg, void *token, - pj_bool_t cache, - const pj_sockaddr_t *dst_addr, + pj_bool_t cache, + const pj_sockaddr_t *dst_addr, unsigned addr_len); /** @@ -665,8 +722,8 @@ PJ_DECL(pj_status_t) pj_stun_session_respond(pj_stun_session *sess, * callback. This error status MUST NOT be PJ_SUCCESS. * * @return PJ_SUCCESS if transaction is successfully cancelled. - * This function will return PJNATH_ESTUNDESTROYED if - * application has destroyed the session in + * This function will return PJNATH_ESTUNDESTROYED if + * application has destroyed the session in * \a on_request_complete() callback. */ PJ_DECL(pj_status_t) pj_stun_session_cancel_req(pj_stun_session *sess, @@ -685,7 +742,7 @@ PJ_DECL(pj_status_t) pj_stun_session_cancel_req(pj_stun_session *sess, * needs to be incremented. * * @return PJ_SUCCESS on success, or the appropriate error. - * This function will return PJNATH_ESTUNDESTROYED if + * This function will return PJNATH_ESTUNDESTROYED if * application has destroyed the session in \a on_send_msg() * callback. */ @@ -716,8 +773,8 @@ PJ_DECL(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess, * STUN message (useful if packet is received via a * stream oriented protocol). * @param token Optional token which will be given back to application - * in the \a on_rx_request(), \a on_rx_indication() and - * \a on_send_msg() callbacks. The token can be used to + * in the \a on_rx_request(), \a on_rx_indication() and + * \a on_send_msg() callbacks. The token can be used to * associate processing or incoming request or indication * with some context. * @param src_addr The source address of the packet, which will also @@ -726,7 +783,7 @@ PJ_DECL(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess, * @param src_addr_len Length of the source address. * * @return PJ_SUCCESS on success, or the appropriate error code. - * This function will return PJNATH_ESTUNDESTROYED if + * This function will return PJNATH_ESTUNDESTROYED if * application has destroyed the session in one of the * callback. */ @@ -751,6 +808,22 @@ PJ_DECL(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, PJ_DECL(void) pj_stun_msg_destroy_tdata(pj_stun_session *sess, pj_stun_tx_data *tdata); +/** + * + * @param sess The STUN session. + * + * @return The callback linked to the STUN session + */ +PJ_DECL(pj_stun_session_cb *) pj_stun_session_callback(pj_stun_session *sess); + +/** + * + * @param sess The STUN session. + * + * @return The connection type linked to the STUN session + */ +PJ_DECL(pj_stun_tp_type) pj_stun_session_tp_type(pj_stun_session *sess); + /** * @} diff --git a/pjnath/include/pjnath/stun_sock.h b/pjnath/include/pjnath/stun_sock.h index a6610335e..b1601f65f 100644 --- a/pjnath/include/pjnath/stun_sock.h +++ b/pjnath/include/pjnath/stun_sock.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_STUN_SOCK_H__ #define __PJNATH_STUN_SOCK_H__ @@ -23,10 +23,14 @@ * @file stun_sock.h * @brief STUN aware socket transport */ +#include #include +#include #include +#include #include #include +#include #include #include @@ -86,7 +90,17 @@ typedef enum pj_stun_sock_op /** * IP address change notification from the keep-alive operation. */ - PJ_STUN_SOCK_MAPPED_ADDR_CHANGE + PJ_STUN_SOCK_MAPPED_ADDR_CHANGE, + + /** + * STUN session was destroyed. + */ + PJ_STUN_SESS_DESTROYED, + + /** + * TCP fails to connect + */ + PJ_STUN_TCP_CONNECT_ERROR } pj_stun_sock_op; @@ -143,7 +157,7 @@ typedef struct pj_stun_sock_cb * callback may be called for the following conditions: * - the first time the publicly mapped address has been resolved from * the STUN server, this callback will be called with \a op argument - * set to PJ_STUN_SOCK_BINDING_OP \a status argument set to + * set to PJ_STUN_SOCK_BINDING_OP \a status argument set to * PJ_SUCCESS. * - anytime when the transport has detected that the publicly mapped * address has changed, this callback will be called with \a op @@ -152,7 +166,7 @@ typedef struct pj_stun_sock_cb * application will get the resolved public address in the * #pj_stun_sock_info structure. * - for any terminal error (such as STUN time-out, DNS resolution - * failure, or keep-alive failure), this callback will be called + * failure, or keep-alive failure), this callback will be called * with the \a status argument set to non-PJ_SUCCESS. * * @param stun_sock The STUN transport. @@ -166,7 +180,7 @@ typedef struct pj_stun_sock_cb * should return PJ_TRUE to let the STUN socket operation * continues. */ - pj_bool_t (*on_status)(pj_stun_sock *stun_sock, + pj_bool_t (*on_status)(pj_stun_sock *stun_sock, pj_stun_sock_op op, pj_status_t status); @@ -196,6 +210,11 @@ typedef struct pj_stun_sock_info */ pj_sockaddr mapped_addr; + /** + * If connected, the remote address will be stored here. + */ + pj_sockaddr outgoing_addr; + /** * Number of interface address aliases. The interface address aliases * are list of all interface addresses in this host. @@ -207,6 +226,11 @@ typedef struct pj_stun_sock_info */ pj_sockaddr aliases[PJ_ICE_ST_MAX_CAND]; + /** + * The tranport type of the socket + */ + pj_stun_tp_type conn_type; + } pj_stun_sock_info; @@ -251,6 +275,28 @@ typedef struct pj_stun_sock_cfg */ pj_sockaddr bound_addr; + /** + * This member holds a list of address mappings (internal/external) that + * the user (application) provides. These mappings are meant to be used + * to add server reflexive candidates that are not typically discovered + * by regular ICE operations. This is the case for mappings obtained + * through UPNP-IGD/NAT-PMP/PCP requests, or manually configured (port + * forward). + */ + struct { + pj_sockaddr local_addr; + pj_sockaddr mapped_addr; + int tp_type; + } user_mapping[PJ_ICE_MAX_COMP]; + + /** + * Holds the actual number of allocated ports. If the feature is used, + * this value should match the number of components of the ICE session. + * The feature is disabled if this variable is set to 0. + * Default value is 0. + */ + unsigned user_mapping_cnt; + /** * Specify the port range for STUN socket binding, relative to the start * port number specified in \a bound_addr. Note that this setting is only @@ -262,7 +308,7 @@ typedef struct pj_stun_sock_cfg /** * Specify the STUN keep-alive duration, in seconds. The STUN transport - * does keep-alive by sending STUN Binding request to the STUN server. + * does keep-alive by sending STUN Binding request to the STUN server. * If this value is zero, the PJ_STUN_KEEP_ALIVE_SEC value will be used. * If the value is negative, it will disable STUN keep-alive. */ @@ -341,9 +387,11 @@ PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg); * things the ioqueue and timer heap instance for * the operation of this transport. * @param af Address family of socket. Currently pj_AF_INET() - * and pj_AF_INET6() are supported. + * and pj_AF_INET6() are supported. * @param name Optional name to be given to this transport to * assist debugging. + * @param conn_type Connection type to the STUN server. Both TCP and UDP are + * supported. * @param cb Callback to receive events/data from the transport. * @param cfg Optional transport settings. * @param user_data Arbitrary application data to be associated with @@ -356,6 +404,7 @@ PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg); PJ_DECL(pj_status_t) pj_stun_sock_create(pj_stun_config *stun_cfg, const char *name, int af, + pj_stun_tp_type conn_type, const pj_stun_sock_cb *cb, const pj_stun_sock_cfg *cfg, void *user_data, @@ -475,7 +524,7 @@ PJ_DECL(pj_status_t) pj_stun_sock_get_info(pj_stun_sock *stun_sock, * this case the \a on_data_sent() callback will be * called when data is actually sent. Any other return * value indicates error condition. - */ + */ PJ_DECL(pj_status_t) pj_stun_sock_sendto(pj_stun_sock *stun_sock, pj_ioqueue_op_key_t *send_key, const void *pkt, @@ -484,6 +533,51 @@ PJ_DECL(pj_status_t) pj_stun_sock_sendto(pj_stun_sock *stun_sock, const pj_sockaddr_t *dst_addr, unsigned addr_len); + +#if PJ_HAS_TCP +/** + * Connect active socket to remote address + * @param stun_sock + * @param remote_addr the destination + * @param af address family + */ +PJ_DECL(pj_status_t) pj_stun_sock_connect_active(pj_stun_sock *stun_sock, + const pj_sockaddr_t *remote_addr, + int af); + +/** + * Connect active socket to remote address + * @param stun_sock + * @param remote_addr the destination + * @param af address family + */ +PJ_DECL(pj_status_t) pj_stun_sock_reconnect_active(pj_stun_sock *stun_sock, + const pj_sockaddr_t *remote_addr, + int af); + +/** + * Close active socket + * @param stun_sock + * @param remote_addr The remote address linked + */ +PJ_DECL(pj_status_t) pj_stun_sock_close(pj_stun_sock *stun_sock, + const pj_sockaddr_t *remote_addr); + +/** + * Close all active sockets except the one with remote_addr + * @param stun_sock + * @param remote_addr The remote address linked + */ +PJ_DECL(pj_status_t) pj_stun_sock_close_all_except(pj_stun_sock *stun_sock, + const pj_sockaddr_t *remote_addr); + +#endif + +/** + * Retrieve the linked session + * @param stun_sock + */ +PJ_DECL(pj_stun_session *) pj_stun_sock_get_session(pj_stun_sock *stun_sock); /** * @} */ diff --git a/pjnath/include/pjnath/turn_session.h b/pjnath/include/pjnath/turn_session.h index 66acd9956..66a4afd1d 100644 --- a/pjnath/include/pjnath/turn_session.h +++ b/pjnath/include/pjnath/turn_session.h @@ -253,6 +253,35 @@ typedef struct pj_turn_session_cb const pj_sockaddr_t *dst_addr, unsigned addr_len); + /** + * This callback will be called by the TURN session whenever it + * needs to send outgoing message. Since the TURN session doesn't + * have a socket on its own, this callback must be implemented. + * + * The difference with on_send_pkt is that this function returns + * the size of the packet actually sent to predict when a busy will + * occurs. Indeed, activesock send the data asynchronously. When the + * data are actually sent, on_data_sent will be triggered. + * + * @param sess The TURN session. + * @param pkt The packet/data to be sent. + * @param pkt_len Length of the packet/data. + * @param dst_addr Destination address of the packet. + * @param addr_len Length of the destination address. + * @param send_size Length sent. + * @param original_size The length of the packet without the HEADER + * + * @return The callback should return the status of the + * send operation. + */ + pj_status_t (*on_send_pkt2)(pj_turn_session *sess, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_sockaddr_t *dst_addr, + unsigned addr_len, + unsigned* sent_size, + unsigned original_size); + /** * This callback will be called by the TURN session whenever it * needs to send outgoing STUN requests/messages for TURN signalling @@ -833,6 +862,42 @@ PJ_DECL(pj_status_t) pj_turn_session_sendto(pj_turn_session *sess, const pj_sockaddr_t *peer_addr, unsigned addr_len); +/** + * Send a data to the specified peer address via the TURN relay. This + * function will encapsulate the data as STUN Send Indication or TURN + * ChannelData packet and send the message to the TURN server. The TURN + * server then will send the data to the peer. + * + * The allocation (pj_turn_session_alloc()) must have been successfully + * created before application can relay any data. + * + * Since TURN session is transport independent, this function will + * ultimately call \a on_send_pkt() callback to request the application + * to actually send the packet containing the data to the TURN server. + * + * The difference with pj_turn_session_sendto is that this function returns + * the size of the packet actually sent to predict when a busy will + * occurs. Indeed, activesock send the data asynchronously. When the + * data are actually sent, on_data_sent will be triggered. + * + * @param sess The TURN client session. + * @param pkt The data/packet to be sent to peer. + * @param pkt_len Length of the data. + * @param peer_addr The remote peer address (the ultimate destination + * of the data, and not the TURN server address). + * @param addr_len Length of the address. + * @param sent The size of the packet actually sent + * + * @return PJ_SUCCESS if the operation has been successful, + * or the appropriate error code on failure. + */ +PJ_DECL(pj_status_t) pj_turn_session_sendto2(pj_turn_session *sess, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_sockaddr_t *peer_addr, + unsigned addr_len, + unsigned *sent); + /** * Optionally establish channel binding for the specified a peer address. * This function will assign a unique channel number for the peer address diff --git a/pjnath/include/pjnath/turn_sock.h b/pjnath/include/pjnath/turn_sock.h index 2de6b4267..681ac32ba 100644 --- a/pjnath/include/pjnath/turn_sock.h +++ b/pjnath/include/pjnath/turn_sock.h @@ -668,6 +668,44 @@ PJ_DECL(pj_status_t) pj_turn_sock_disconnect(pj_turn_sock *turn_sock, const pj_sockaddr_t *peer, unsigned addr_len); +/** + * Send a data to the specified peer address via the TURN relay. This + * function will encapsulate the data as STUN Send Indication or TURN + * ChannelData packet and send the message to the TURN server. The TURN + * server then will send the data to the peer. + * + * The allocation (pj_turn_sock_alloc()) must have been successfully + * created before application can relay any data. + * + * @param turn_sock The TURN transport instance. + * @param pkt The data/packet to be sent to peer. + * @param pkt_len Length of the data. + * @param peer_addr The remote peer address (the ultimate destination + * of the data, and not the TURN server address). + * @param addr_len Length of the address. + * @param sent Size actually sent. + * + * @return PJ_SUCCESS if the operation has been successful, + * or the appropriate error code on failure. + */ +PJ_DECL(pj_status_t) pj_turn_sock_sendto2(pj_turn_sock *turn_sock, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_sockaddr_t *peer_addr, + unsigned addr_len, + unsigned* sent); + +/** + * Check if peer is a dataconn + * + * @param turn_sock The turn sock + * @param peer The peer addr to check + * + * @return true if dataconn else false + */ +PJ_DECL(pj_bool_t) pj_turn_sock_has_dataconn(pj_turn_sock *turn_sock, + const pj_sockaddr_t *peer); + /** * @} diff --git a/pjnath/src/pjnath-test/concur_test.c b/pjnath/src/pjnath-test/concur_test.c index 54ddb7b6c..f684097a6 100644 --- a/pjnath/src/pjnath-test/concur_test.c +++ b/pjnath/src/pjnath-test/concur_test.c @@ -183,6 +183,7 @@ static int stun_destroy_test_session(struct stun_test_session *test_sess) char name[10]; pj_ansi_snprintf(name, sizeof(name), "stun%02d", i); status = pj_stun_sock_create(&test_sess->stun_cfg, name, pj_AF_INET(), + PJ_STUN_TP_UDP, &stun_cb, NULL, test_sess, &stun_sock[i]); if (status != PJ_SUCCESS) { diff --git a/pjnath/src/pjnath-test/sess_auth.c b/pjnath/src/pjnath-test/sess_auth.c index f0e308bba..b67049a49 100644 --- a/pjnath/src/pjnath-test/sess_auth.c +++ b/pjnath/src/pjnath-test/sess_auth.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "test.h" @@ -81,7 +81,7 @@ static pj_status_t server_on_rx_request(pj_stun_session *sess, PJ_UNUSED_ARG(pkt_len); PJ_UNUSED_ARG(token); - return pj_stun_session_respond(sess, rdata, 0, NULL, NULL, PJ_TRUE, + return pj_stun_session_respond(sess, rdata, 0, NULL, NULL, PJ_TRUE, src_addr, src_addr_len); } @@ -106,7 +106,7 @@ static pj_status_t server_get_auth(void *user_data, static pj_status_t server_get_password( const pj_stun_msg *msg, - void *user_data, + void *user_data, const pj_str_t *realm, const pj_str_t *username, pj_pool_t *pool, @@ -172,8 +172,8 @@ static int server_thread(void *unused) PJ_FD_ZERO(&readset); PJ_FD_SET(server->sock, &readset); - if (pj_sock_select((int)server->sock+1, &readset, NULL, NULL, &delay)==1 - && PJ_FD_ISSET(server->sock, &readset)) + if (pj_sock_select((int)server->sock+1, &readset, NULL, NULL, &delay)==1 + && PJ_FD_ISSET(server->sock, &readset)) { char pkt[1000]; pj_ssize_t len; @@ -195,7 +195,7 @@ static int server_thread(void *unused) if (!server->responding) continue; - pj_stun_session_on_rx_pkt(server->sess, pkt, len, + pj_stun_session_on_rx_pkt(server->sess, pkt, len, PJ_STUN_CHECK_PACKET | PJ_STUN_IS_DATAGRAM, NULL, NULL, &src_addr, src_addr_len); } @@ -235,7 +235,7 @@ static int create_std_server(pj_stun_auth_type auth_type, pj_stun_session_cb sess_cb; pj_stun_auth_cred cred; pj_status_t status; - + /* Create server */ pool = pj_pool_create(mem, "server", 1000, 1000, NULL); server = PJ_POOL_ZALLOC_T(pool, struct server); @@ -247,7 +247,8 @@ static int create_std_server(pj_stun_auth_type auth_type, pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_rx_request = &server_on_rx_request; sess_cb.on_send_msg = &server_send_msg; - status = pj_stun_session_create(&stun_cfg, "server", &sess_cb, PJ_FALSE, NULL, &server->sess); + status = pj_stun_session_create(&stun_cfg, "server", &sess_cb, PJ_FALSE, + NULL, &server->sess, PJ_STUN_TP_UDP); if (status != PJ_SUCCESS) { destroy_server(); return -10; @@ -294,7 +295,7 @@ static int create_std_server(pj_stun_auth_type auth_type, * 'no route to host' error, so let's just hardcode to [::1] */ pj_sockaddr_init(pj_AF_INET6(), &addr, NULL, 0); - addr.ipv6.sin6_addr.s6_addr[15] = 1; + addr.ipv6.sin6_addr.s6_addr[15] = 1; } else { status = pj_gethostip(GET_AF(use_ipv6), &addr); if (status != PJ_SUCCESS) { @@ -394,8 +395,8 @@ static int client_thread(void *unused) PJ_FD_ZERO(&readset); PJ_FD_SET(client->sock, &readset); - if (pj_sock_select((int)client->sock+1, &readset, NULL, NULL, &delay)==1 - && PJ_FD_ISSET(client->sock, &readset)) + if (pj_sock_select((int)client->sock+1, &readset, NULL, NULL, &delay)==1 + && PJ_FD_ISSET(client->sock, &readset)) { char pkt[1000]; pj_ssize_t len; @@ -417,11 +418,11 @@ static int client_thread(void *unused) if (!client->responding) continue; - pj_stun_session_on_rx_pkt(client->sess, pkt, len, + pj_stun_session_on_rx_pkt(client->sess, pkt, len, PJ_STUN_CHECK_PACKET | PJ_STUN_IS_DATAGRAM, NULL, NULL, &src_addr, src_addr_len); } - + } return 0; @@ -465,7 +466,7 @@ static int run_client_test(const char *title, pj_status_t expected_code, const char *expected_realm, const char *expected_nonce, - + int (*more_check)(void)) { pj_pool_t *pool; @@ -475,7 +476,7 @@ static int run_client_test(const char *title, pj_status_t status; pj_sockaddr addr; int rc = 0; - + PJ_LOG(3,(THIS_FILE, " %s test (%s)", title, use_ipv6?"IPv6":"IPv4")); /* Create client */ @@ -488,7 +489,8 @@ static int run_client_test(const char *title, pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_request_complete = &client_on_request_complete; sess_cb.on_send_msg = &client_send_msg; - status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, NULL, &client->sess); + status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, + NULL, &client->sess, PJ_STUN_TP_UDP); if (status != PJ_SUCCESS) { destroy_client_server(); return -200; @@ -545,7 +547,7 @@ static int run_client_test(const char *title, } /* Create request */ - status = pj_stun_session_create_req(client->sess, PJ_STUN_BINDING_REQUEST, + status = pj_stun_session_create_req(client->sess, PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC, NULL, &tdata); if (status != PJ_SUCCESS) { destroy_client_server(); @@ -570,11 +572,11 @@ static int run_client_test(const char *title, pj_stun_msgint_attr_create(tdata->pool, &mi); pj_stun_msg_add_attr(tdata->msg, &mi->hdr); } - + } /* Send the request */ - status = pj_stun_session_send_msg(client->sess, NULL, PJ_FALSE, PJ_TRUE, &server->addr, + status = pj_stun_session_send_msg(client->sess, NULL, PJ_FALSE, (pj_stun_session_tp_type(client->sess) == PJ_STUN_TP_UDP), &server->addr, pj_sockaddr_get_len(&server->addr), tdata); if (status != PJ_SUCCESS) { destroy_client_server(); @@ -596,7 +598,7 @@ static int run_client_test(const char *title, PJ_LOG(3,(THIS_FILE, " err: expecting %d (%s) but got %d (%s) response", expected_code, e1, client->response_status, e2)); rc = -500; - } + } } else { int res_code = 0; @@ -604,17 +606,17 @@ static int run_client_test(const char *title, pj_stun_nonce_attr *anonce; if (client->response_status != 0) { - PJ_LOG(3,(THIS_FILE, " err: expecting successful operation but got error %d", + PJ_LOG(3,(THIS_FILE, " err: expecting successful operation but got error %d", client->response_status)); rc = -600; goto done; - } + } if (PJ_STUN_IS_ERROR_RESPONSE(client->response->hdr.type)) { pj_stun_errcode_attr *aerr = NULL; aerr = (pj_stun_errcode_attr*) - pj_stun_msg_find_attr(client->response, + pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_ERROR_CODE, 0); if (aerr == NULL) { PJ_LOG(3,(THIS_FILE, " err: received error response without ERROR-CODE")); @@ -747,8 +749,8 @@ static int long_term_check1(void) static int long_term_check2(void) { - /* response SHOULD NOT include a USERNAME, NONCE, REALM or - * MESSAGE-INTEGRITY attribute. + /* response SHOULD NOT include a USERNAME, NONCE, REALM or + * MESSAGE-INTEGRITY attribute. */ if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_USERNAME, 0)) return -900; @@ -851,7 +853,7 @@ int sess_auth_test(void) } /* If the USERNAME does not contain a username value currently valid - * within the server: If the message is a request, the server MUST + * within the server: If the message is a request, the server MUST * reject the request with an error response. This response MUST use * an error code of 401 (Unauthorized). */ @@ -1083,7 +1085,7 @@ int sess_auth_test(void) * MUST include a NONCE and REALM attribute and SHOULD NOT incude the * USERNAME or MESSAGE-INTEGRITY attribute. Servers can invalidate * nonces in order to provide additional security. See Section 4.3 - * of [RFC2617] for guidelines. + * of [RFC2617] for guidelines. */ // how?? diff --git a/pjnath/src/pjnath-test/stun_sock_test.c b/pjnath/src/pjnath-test/stun_sock_test.c index f44988aee..76bcb241e 100644 --- a/pjnath/src/pjnath-test/stun_sock_test.c +++ b/pjnath/src/pjnath-test/stun_sock_test.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "test.h" @@ -65,9 +65,9 @@ static pj_bool_t srv_on_data_recvfrom(pj_activesock_t *asock, pj_stun_msg *req_msg, *res_msg; pool = pj_pool_create(mem, "stunsrv", 512, 512, NULL); - + /* Parse request */ - status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size, + status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size, PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, &req_msg, NULL, NULL); if (status != PJ_SUCCESS) { @@ -89,8 +89,8 @@ static pj_bool_t srv_on_data_recvfrom(pj_activesock_t *asock, if (srv->flag & WITH_MAPPED) { pj_sockaddr addr; pj_bool_t use_ipv6 = (srv->addr.addr.sa_family == pj_AF_INET6()); - - pj_sockaddr_init(GET_AF(use_ipv6), &addr, &srv->ip_to_send, + + pj_sockaddr_init(GET_AF(use_ipv6), &addr, &srv->ip_to_send, srv->port_to_send); pj_stun_msg_add_sockaddr_attr(pool, res_msg, PJ_STUN_ATTR_MAPPED_ADDR, @@ -98,17 +98,17 @@ static pj_bool_t srv_on_data_recvfrom(pj_activesock_t *asock, } else if (srv->flag & WITH_XOR_MAPPED) { pj_sockaddr addr; pj_bool_t use_ipv6 = (srv->addr.addr.sa_family == pj_AF_INET6()); - - pj_sockaddr_init(GET_AF(use_ipv6), &addr, &srv->ip_to_send, + + pj_sockaddr_init(GET_AF(use_ipv6), &addr, &srv->ip_to_send, srv->port_to_send); - pj_stun_msg_add_sockaddr_attr(pool, res_msg, + pj_stun_msg_add_sockaddr_attr(pool, res_msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &addr, sizeof(addr)); } /* Encode */ - status = pj_stun_msg_encode(res_msg, (pj_uint8_t*)data, 100, 0, + status = pj_stun_msg_encode(res_msg, (pj_uint8_t*)data, 100, 0, NULL, &size); if (status != PJ_SUCCESS) { app_perror(" pj_stun_msg_encode()", status); @@ -118,7 +118,7 @@ static pj_bool_t srv_on_data_recvfrom(pj_activesock_t *asock, /* Send back */ sent = size; - pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0, + pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0, src_addr, addr_len); pj_pool_release(pool); @@ -126,7 +126,7 @@ static pj_bool_t srv_on_data_recvfrom(pj_activesock_t *asock, } else if (srv->flag & ECHO) { /* Send back */ sent = size; - pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0, + pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0, src_addr, addr_len); } @@ -156,7 +156,7 @@ static pj_status_t create_server(pj_pool_t *pool, pj_bzero(&activesock_cb, sizeof(activesock_cb)); activesock_cb.on_data_recvfrom = &srv_on_data_recvfrom; status = pj_activesock_create_udp(pool, &srv->addr, NULL, ioqueue, - &activesock_cb, srv, &srv->asock, + &activesock_cb, srv, &srv->asock, &srv->addr); if (status != PJ_SUCCESS) return status; @@ -194,7 +194,7 @@ struct stun_client unsigned on_rx_data_cnt; }; -static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, +static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, pj_stun_sock_op op, pj_status_t status) { @@ -253,7 +253,7 @@ static pj_status_t create_client(pj_stun_config *cfg, pj_bzero(&cb, sizeof(cb)); cb.on_status = &stun_sock_on_status; cb.on_rx_data = &stun_sock_on_rx_data; - status = pj_stun_sock_create(cfg, NULL, GET_AF(use_ipv6), &cb, &sock_cfg, + status = pj_stun_sock_create(cfg, NULL, GET_AF(use_ipv6), PJ_STUN_TP_UDP, &cb, &sock_cfg, client, &client->sock); if (status != PJ_SUCCESS) { app_perror(" pj_stun_sock_create()", status); @@ -298,7 +298,7 @@ static void handle_events(pj_stun_config *cfg, unsigned msec_delay) /* * Timeout test: scenario when no response is received from server */ -static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, +static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, pj_bool_t use_ipv6) { struct stun_srv *srv; @@ -308,7 +308,7 @@ static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, int i, ret = 0; pj_status_t status; - PJ_LOG(3,(THIS_FILE, " timeout test [%d] - (%s)", destroy_on_err, + PJ_LOG(3,(THIS_FILE, " timeout test [%d] - (%s)", destroy_on_err, (use_ipv6)?"IPv6":"IPv4")); status = create_client(cfg, &client, destroy_on_err, use_ipv6); @@ -323,7 +323,7 @@ static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, srv_addr = (use_ipv6)?pj_str("::1"):pj_str("127.0.0.1"); - status = pj_stun_sock_start(client->sock, &srv_addr, + status = pj_stun_sock_start(client->sock, &srv_addr, pj_sockaddr_get_port(&srv->addr), NULL); if (status != PJ_SUCCESS) { destroy_server(srv); @@ -382,7 +382,7 @@ on_return: * Invalid response scenario: when server returns no MAPPED-ADDRESS or * XOR-MAPPED-ADDRESS attribute. */ -static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, +static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, pj_bool_t use_ipv6) { struct stun_srv *srv; @@ -392,14 +392,14 @@ static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, int i, ret = 0; pj_status_t status; - PJ_LOG(3,(THIS_FILE, " missing attribute test [%d] - (%s)", + PJ_LOG(3,(THIS_FILE, " missing attribute test [%d] - (%s)", destroy_on_err, (use_ipv6)?"IPv6":"IPv4")); status = create_client(cfg, &client, destroy_on_err, use_ipv6); if (status != PJ_SUCCESS) return -110; - status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN, use_ipv6, + status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN, use_ipv6, &srv); if (status != PJ_SUCCESS) { destroy_client(client); @@ -407,8 +407,8 @@ static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, } srv_addr = (use_ipv6)?pj_str("::1"):pj_str("127.0.0.1"); - status = pj_stun_sock_start(client->sock, &srv_addr, - pj_sockaddr_get_port(&srv->addr), NULL); + status = pj_stun_sock_start(client->sock, &srv_addr, + pj_sockaddr_get_port(&srv->addr), NULL); if (status != PJ_SUCCESS) { destroy_server(srv); destroy_client(client); @@ -467,14 +467,14 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) int i, ret = 0; pj_status_t status; - PJ_LOG(3,(THIS_FILE, " normal operation - (%s)", + PJ_LOG(3,(THIS_FILE, " normal operation - (%s)", (use_ipv6)?"IPv6":"IPv4")); status = create_client(cfg, &client, PJ_TRUE, use_ipv6); if (status != PJ_SUCCESS) return -310; - status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN|WITH_XOR_MAPPED, + status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN|WITH_XOR_MAPPED, use_ipv6, &srv); if (status != PJ_SUCCESS) { destroy_client(client); @@ -488,7 +488,7 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) srv_addr = (use_ipv6)?pj_str("::1"):pj_str("127.0.0.1"); - status = pj_stun_sock_start(client->sock, &srv_addr, + status = pj_stun_sock_start(client->sock, &srv_addr, pj_sockaddr_get_port(&srv->addr), NULL); if (status != PJ_SUCCESS) { destroy_server(srv); @@ -545,7 +545,7 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) goto on_return; } /* verify the mapped address */ - pj_sockaddr_init(GET_AF(use_ipv6), &mapped_addr, + pj_sockaddr_init(GET_AF(use_ipv6), &mapped_addr, &srv->ip_to_send, srv->port_to_send); if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) { PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched")); @@ -583,7 +583,7 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) PJ_LOG(3,(THIS_FILE, " sending to %s", pj_sockaddr_print(&info.srv_addr, txt, sizeof(txt), 3))); } status = pj_stun_sock_sendto(client->sock, NULL, &ret, sizeof(ret), - 0, &info.srv_addr, + 0, &info.srv_addr, pj_sockaddr_get_len(&info.srv_addr)); if (status != PJ_SUCCESS && status != PJ_EPENDING) { app_perror(" error: server sending data", status); @@ -683,7 +683,7 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) srv->flag = RESPOND_STUN | WITH_XOR_MAPPED; /* Change mapped address in the response */ - srv->ip_to_send = (use_ipv6)?pj_str("2002:202:202::"):pj_str("2.2.2.2"); + srv->ip_to_send = (use_ipv6)?pj_str("2002:202:202::"):pj_str("2.2.2.2"); srv->port_to_send++; /* Reset server */ @@ -754,7 +754,7 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) goto on_return; } /* verify the mapped address */ - pj_sockaddr_init(GET_AF(use_ipv6), &mapped_addr, + pj_sockaddr_init(GET_AF(use_ipv6), &mapped_addr, &srv->ip_to_send, srv->port_to_send); if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) { PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched")); @@ -779,7 +779,7 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) * Part 5: Failed keep-alive */ PJ_LOG(3,(THIS_FILE, " failed keep-alive scenario")); - + /* Change server operation mode to respond without attribute */ srv->flag = RESPOND_STUN; @@ -864,7 +864,7 @@ int stun_sock_test(void) ret = -8; goto on_return; } - + pj_stun_config_init(&stun_cfg, mem, 0, ioqueue, timer_heap); DO_TEST(timeout_test(&stun_cfg, PJ_FALSE, USE_IPV6)); diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c index 8afe4d181..d0cbb0ce5 100644 --- a/pjnath/src/pjnath/ice_session.c +++ b/pjnath/src/pjnath/ice_session.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,9 +14,10 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #include #include #include @@ -28,6 +29,22 @@ #include #include +#if defined(_WIN32) || defined(__APPLE__) +/* TODO(sblin): find an alternative for these paltforms */ +#else +/* The following headers are used to get DEPRECATED addresses + * as specified in RFC 2462 Section 5.5.4 + * https://tools.ietf.org/html/rfc2462#section-5.5.4 + */ +#include +#include +#include +#include +#include +#include +#include +#endif + /* String names for candidate types */ static const char *cand_type_names[] = { @@ -40,10 +57,13 @@ static const char *cand_type_names[] = /* String names for pj_ice_sess_check_state */ #if PJ_LOG_MAX_LEVEL >= 4 -static const char *check_state_name[] = +static const char *check_state_name[] = { "Frozen", + "Needs Retry", + "Needs First Packet", "Waiting", + "Pending", "In Progress", "Succeeded", "Failed" @@ -57,7 +77,7 @@ static const char *clist_state_name[] = }; #endif /* PJ_LOG_MAX_LEVEL >= 4 */ -static const char *role_names[] = +static const char *role_names[] = { "Unknown", "Controlled", @@ -68,13 +88,15 @@ enum timer_type { TIMER_NONE, /**< Timer not active */ TIMER_COMPLETION_CALLBACK, /**< Call on_ice_complete() callback */ - TIMER_CONTROLLED_WAIT_NOM, /**< Controlled agent is waiting for + TIMER_CONTROLLING_TCP_PASSIVE_TIMEOUT, /** < Controlling agent is waiting for passive TCP connection timeout **/ + TIMER_CONTROLLED_WAIT_NOM, /**< Controlled agent is waiting for controlling agent to send connectivity check with nominated flag after it has valid check for every components. */ TIMER_START_NOMINATED_CHECK,/**< Controlling agent start connectivity checks with USE-CANDIDATE flag. */ - TIMER_KEEP_ALIVE /**< ICE keep-alive timer. */ + TIMER_KEEP_ALIVE, /**< ICE keep-alive timer. */ + TIMER_CONNECTION_TIMEOUT }; @@ -122,6 +144,8 @@ typedef struct timer_data { pj_ice_sess *ice; pj_ice_sess_checklist *clist; + /* TODO (remove), for now, needed for the NEEDS_FIRST_PACKET state */ + unsigned first_packet_counter; } timer_data; @@ -132,15 +156,16 @@ typedef struct timer_data /* Forward declarations */ static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te); +static void on_tcp_connect_timeout(pj_ice_sess *ice); static void on_ice_complete(pj_ice_sess *ice, pj_status_t status); static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now); static void ice_on_destroy(void *obj); static void destroy_ice(pj_ice_sess *ice, pj_status_t reason); -static pj_status_t start_periodic_check(pj_timer_heap_t *th, +static pj_status_t start_periodic_check(pj_timer_heap_t *th, pj_timer_entry *te); static void start_nominated_check(pj_ice_sess *ice); -static void periodic_timer(pj_timer_heap_t *th, +static void periodic_timer(pj_timer_heap_t *th, pj_timer_entry *te); static void handle_incoming_check(pj_ice_sess *ice, const pj_ice_rx_check *rcheck); @@ -190,7 +215,7 @@ static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg, pj_stun_passwd_type *data_type, pj_str_t *data); static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, - void *user_data, + void *user_data, const pj_str_t *realm, const pj_str_t *username, pj_pool_t *pool, @@ -289,10 +314,11 @@ static pj_status_t init_comp(pj_ice_sess *ice, sess_cb.on_send_msg = &on_stun_send_msg; /* Create STUN session for this candidate */ - status = pj_stun_session_create(&ice->stun_cfg, NULL, + status = pj_stun_session_create(&ice->stun_cfg, NULL, &sess_cb, PJ_TRUE, ice->grp_lock, - &comp->stun_sess); + &comp->stun_sess, + PJ_STUN_TP_UDP); if (status != PJ_SUCCESS) return status; @@ -322,9 +348,10 @@ PJ_DEF(void) pj_ice_sess_options_default(pj_ice_sess_options *opt) { opt->aggressive = PJ_TRUE; opt->nominated_check_delay = PJ_ICE_NOMINATED_CHECK_DELAY; - opt->controlled_agent_want_nom_timeout = + opt->controlled_agent_want_nom_timeout = ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT; opt->trickle = PJ_ICE_SESS_TRICKLE_DISABLED; + opt->agent_passive_timeout = ICE_CONTROLLING_PASSIVE_TIMEOUT; } /* @@ -350,7 +377,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, if (name == NULL) name = "icess%p"; - pool = pj_pool_create(stun_cfg->pf, name, PJNATH_POOL_LEN_ICE_SESS, + pool = pj_pool_create(stun_cfg->pf, name, PJNATH_POOL_LEN_ICE_SESS, PJNATH_POOL_INC_ICE_SESS, NULL); ice = PJ_POOL_ZALLOC_T(pool, pj_ice_sess); ice->pool = pool; @@ -361,6 +388,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, pj_ice_sess_options_default(&ice->opt); pj_timer_entry_init(&ice->timer, TIMER_NONE, (void*)ice, &on_timer); + pj_timer_entry_init(&ice->timer_connect, TIMER_NONE, (void*)ice, &on_timer); pj_ansi_snprintf(ice->obj_name, sizeof(ice->obj_name), name, ice); @@ -425,7 +453,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, /* Done */ *p_ice = ice; - LOG4((ice->obj_name, + LOG4((ice->obj_name, "ICE session created, comp_cnt=%d, role is %s agent", comp_cnt, role_names[ice->role])); @@ -507,6 +535,9 @@ static void destroy_ice(pj_ice_sess *ice, pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer, PJ_FALSE); + pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, + &ice->timer_connect, TIMER_NONE); + for (i=0; icomp_cnt; ++i) { if (ice->comp[i].stun_sess) { pj_stun_session_destroy(ice->comp[i].stun_sess); @@ -551,7 +582,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_detach_grp_lock(pj_ice_sess *ice, /* - * Change session role. + * Change session role. */ PJ_DEF(pj_status_t) pj_ice_sess_change_role(pj_ice_sess *ice, pj_ice_sess_role new_role) @@ -651,7 +682,7 @@ static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg, /* Get password to be used to authenticate incoming message */ static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, - void *user_data, + void *user_data, const pj_str_t *realm, const pj_str_t *username, pj_pool_t *pool, @@ -680,8 +711,8 @@ static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, /* The agent MUST accept a credential if the username consists * of two values separated by a colon, where the first value is * equal to the username fragment generated by the agent in an offer - * or answer for a session in-progress, and the MESSAGE-INTEGRITY - * is the output of a hash of the password and the STUN packet's + * or answer for a session in-progress, and the MESSAGE-INTEGRITY + * is the output of a hash of the password and the STUN packet's * contents. */ const char *pos; @@ -712,7 +743,7 @@ static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice, pj_uint32_t comp_id) { #if PJNATH_ICE_PRIO_STD - return ((ice->prefs[type] & 0xFF) << 24) + + return ((ice->prefs[type] & 0xFF) << 24) + ((local_pref & 0xFFFF) << 8) + (((256 - comp_id) & 0xFF) << 0); #else @@ -728,12 +759,151 @@ static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice, max_comp = (2<prefs[type] & type_mask) << type_shift) + + return ((ice->prefs[type] & type_mask) << type_shift) + ((local_pref & local_mask) << local_shift) + (((max_comp - comp_id) & comp_mask) << comp_shift); #endif } +/* retrieve invalid addresses and store it in a string */ +static PJ_DEF(void) get_invalid_addresses(char** addresses, size_t* size) +{ +#if defined(_WIN32) || defined(__APPLE__) + // PJ_TODO("sblin: find alternative for WIN32 and APPLE"); +#else + struct { + struct nlmsghdr nlmsg_info; + struct ifaddrmsg ifaddrmsg_info; + } netlink_req; + + int fd; + + long pagesize = sysconf(_SC_PAGESIZE); + + if (!pagesize) + pagesize = 4096; /* Assume pagesize is 4096 if sysconf() failed */ + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if(fd < 0) { + perror("socket initialization error: abort"); + return; + } + + int rtn; + + bzero(&netlink_req, sizeof(netlink_req)); + + netlink_req.nlmsg_info.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + netlink_req.nlmsg_info.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + netlink_req.nlmsg_info.nlmsg_type = RTM_GETADDR; + netlink_req.nlmsg_info.nlmsg_pid = getpid(); + + netlink_req.ifaddrmsg_info.ifa_family = AF_INET6; + + rtn = send(fd, &netlink_req, netlink_req.nlmsg_info.nlmsg_len, 0); + if(rtn < 0) { + perror("send error: abort"); + return; + } + + char read_buffer[pagesize]; + struct nlmsghdr *nlmsg_ptr; + int nlmsg_len; + + size_t idx = 0; + /* Will store all deprecated addresses into a string */ + char* deprecatedAddrs = malloc(256*sizeof(char)*PJ_INET6_ADDRSTRLEN); + if (!deprecatedAddrs) { + perror("malloc error: abort"); + return; + } + + while(1) { + int rtn; + + bzero(read_buffer, pagesize); + rtn = recv(fd, read_buffer, pagesize, 0); + if(rtn < 0) { + perror ("recv(): "); + free(deprecatedAddrs); + return; + } + + nlmsg_ptr = (struct nlmsghdr *) read_buffer; + nlmsg_len = rtn; + + if (nlmsg_len < sizeof (struct nlmsghdr)) { + perror ("Received an incomplete netlink packet"); + free(deprecatedAddrs); + return; + } + + for(; NLMSG_OK(nlmsg_ptr, nlmsg_len); + nlmsg_ptr = NLMSG_NEXT(nlmsg_ptr, nlmsg_len)) + { + if (nlmsg_ptr->nlmsg_type == NLMSG_DONE) + goto nlmsg_done; + + struct ifaddrmsg *ifaddrmsg_ptr; + struct rtattr *rtattr_ptr; + int ifaddrmsg_len; + + ifaddrmsg_ptr = (struct ifaddrmsg *) NLMSG_DATA(nlmsg_ptr); + + if (ifaddrmsg_ptr->ifa_flags & IFA_F_DEPRECATED || + ifaddrmsg_ptr->ifa_flags & IFA_F_TENTATIVE) + { + rtattr_ptr = (struct rtattr *) IFA_RTA(ifaddrmsg_ptr); + ifaddrmsg_len = IFA_PAYLOAD(nlmsg_ptr); + + for(;RTA_OK(rtattr_ptr, ifaddrmsg_len); + rtattr_ptr = RTA_NEXT(rtattr_ptr, ifaddrmsg_len)) + { + switch(rtattr_ptr->rta_type) { + case IFA_ADDRESS: + /* Any 256 obsolete ips (should not happen), resize the array. */ + if (idx > 0 && idx % 256 == 0) { + char* newDeprecated = realloc(deprecatedAddrs, + (idx + 256)*sizeof(char)*PJ_INET6_ADDRSTRLEN); + if (newDeprecated == NULL) { + perror("realloc error: abort"); + free(deprecatedAddrs); + return; + } + deprecatedAddrs = newDeprecated; + } + /* Store deprecated IP */ + inet_ntop(ifaddrmsg_ptr->ifa_family, + RTA_DATA(rtattr_ptr), + &deprecatedAddrs[idx*PJ_INET6_ADDRSTRLEN], + sizeof(char)*PJ_INET6_ADDRSTRLEN); + ++idx; + break; + default: + break; + } + } + } + } + } + +nlmsg_done: + close(fd); + *size = idx; + if (idx > 0) { + char *final = realloc(deprecatedAddrs, + idx*sizeof(char)*PJ_INET6_ADDRSTRLEN); + if (final) { + *addresses = final; + } else { + perror("realloc error: abort"); + free(deprecatedAddrs); + } + } else { + free(deprecatedAddrs); + } +#endif +} /* * Add ICE candidate @@ -748,14 +918,38 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, const pj_sockaddr_t *base_addr, const pj_sockaddr_t *rel_addr, int addr_len, - unsigned *p_cand_id) + unsigned *p_cand_id, + pj_ice_cand_transport transport) { + /** + * RFC 2466: an ip address can have the status DEPRECATED and SHOULD NOT + * be used by new by applications unless they already use it. + * So, we should ignore these addresses. + * Also, ips with the TENTATIVE state are not ready and SHOULD NOT be + * used for now. Ignore these addresses too. + */ + char* deprecatedAddrs = NULL; + size_t size = 0; + get_invalid_addresses(&deprecatedAddrs, &size); + if (deprecatedAddrs != NULL) { + char tmpAddrStr[PJ_INET6_ADDRSTRLEN]; + pj_sockaddr_print(addr, tmpAddrStr, sizeof(tmpAddrStr), 0); + for (int i = 0; icomp_cnt, PJ_EINVAL); @@ -794,6 +988,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, lcand->comp_id = (pj_uint8_t)comp_id; lcand->transport_id = (pj_uint8_t)transport_id; lcand->type = type; + lcand->transport = transport; pj_strdup(ice->pool, &lcand->foundation, foundation); lcand->local_pref = local_pref; lcand->prio = CALC_CAND_PRIO(ice, type, local_pref, lcand->comp_id); @@ -821,15 +1016,15 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, pj_ansi_strxcpy(ice->tmp.txt, pj_sockaddr_print(&lcand->addr, address, sizeof(address), 2), sizeof(ice->tmp.txt)); - LOG4((ice->obj_name, + LOG4((ice->obj_name, "Candidate %d added: comp_id=%d, type=%s, foundation=%.*s, " "addr=%s:%d, base=%s:%d, prio=0x%x (%u)", lcand->id, - lcand->comp_id, + lcand->comp_id, cand_type_names[lcand->type], (int)lcand->foundation.slen, lcand->foundation.ptr, - ice->tmp.txt, + ice->tmp.txt, pj_sockaddr_get_port(&lcand->addr), pj_sockaddr_print(&lcand->base_addr, address, sizeof(address), 2), pj_sockaddr_get_port(&lcand->base_addr), @@ -863,7 +1058,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, /* First find in valid list if we have nominated pair */ for (i=0; ivalid_list.count; ++i) { pj_ice_sess_check *check = &ice->valid_list.checks[i]; - + if (check->lcand->comp_id == comp_id) { *cand_id = GET_LCAND_ID(check->lcand); pj_grp_lock_release(ice->grp_lock); @@ -875,7 +1070,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, for (i=0; ilcand_cnt; ++i) { pj_ice_sess_cand *lcand = &ice->lcand[i]; if (lcand->comp_id==comp_id && - lcand->type == PJ_ICE_CAND_TYPE_RELAYED) + lcand->type == PJ_ICE_CAND_TYPE_RELAYED) { *cand_id = GET_LCAND_ID(lcand); pj_grp_lock_release(ice->grp_lock); @@ -888,7 +1083,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, pj_ice_sess_cand *lcand = &ice->lcand[i]; if (lcand->comp_id==comp_id && (lcand->type == PJ_ICE_CAND_TYPE_SRFLX || - lcand->type == PJ_ICE_CAND_TYPE_PRFLX)) + lcand->type == PJ_ICE_CAND_TYPE_PRFLX)) { *cand_id = GET_LCAND_ID(lcand); pj_grp_lock_release(ice->grp_lock); @@ -900,7 +1095,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, for (i=0; ilcand_cnt; ++i) { pj_ice_sess_cand *lcand = &ice->lcand[i]; if (lcand->comp_id==comp_id && - lcand->type == PJ_ICE_CAND_TYPE_HOST) + lcand->type == PJ_ICE_CAND_TYPE_HOST) { *cand_id = GET_LCAND_ID(lcand); pj_grp_lock_release(ice->grp_lock); @@ -924,7 +1119,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, # define MAX(a,b) (a > b ? a : b) #endif -static pj_timestamp CALC_CHECK_PRIO(const pj_ice_sess *ice, +static pj_timestamp CALC_CHECK_PRIO(const pj_ice_sess *ice, const pj_ice_sess_cand *lcand, const pj_ice_sess_cand *rcand) { @@ -936,7 +1131,7 @@ static pj_timestamp CALC_CHECK_PRIO(const pj_ice_sess *ice, */ if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) { - O = lcand->prio; + O = lcand->prio; A = rcand->prio; } else { O = rcand->prio; @@ -1013,7 +1208,7 @@ static const char *dump_check(char *buffer, unsigned bufsize, return buffer; } -static void dump_checklist(const char *title, pj_ice_sess *ice, +static void dump_checklist(const char *title, pj_ice_sess *ice, const pj_ice_sess_checklist *clist) { unsigned i; @@ -1023,7 +1218,7 @@ static void dump_checklist(const char *title, pj_ice_sess *ice, const pj_ice_sess_check *c = &clist->checks[i]; LOG4((ice->obj_name, " %s (%s, state=%s)", dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, c), - (c->nominated ? "nominated" : "not nominated"), + (c->nominated ? "nominated" : "not nominated"), check_state_name[c->state])); } } @@ -1033,9 +1228,12 @@ static void dump_checklist(const char *title, pj_ice_sess *ice, #endif static void check_set_state(pj_ice_sess *ice, pj_ice_sess_check *check, - pj_ice_sess_check_state st, + pj_ice_sess_check_state st, pj_status_t err_code) { + if (check->state >= PJ_ICE_SESS_CHECK_STATE_SUCCEEDED) + return; + LOG5((ice->obj_name, "Check %s: state changed from %s to %s", dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), check_state_name[check->state], @@ -1102,9 +1300,9 @@ static void sort_checklist(pj_ice_sess *ice, pj_ice_sess_checklist *clist) unsigned k; pj_memcpy(&tmp, &clist->checks[i], sizeof(pj_ice_sess_check)); - pj_memcpy(&clist->checks[i], &clist->checks[highest], + pj_memcpy(&clist->checks[i], &clist->checks[highest], sizeof(pj_ice_sess_check)); - pj_memcpy(&clist->checks[highest], &tmp, + pj_memcpy(&clist->checks[highest], &tmp, sizeof(pj_ice_sess_check)); /* Update valid and nominated check pointers, since we're moving @@ -1138,7 +1336,7 @@ static void remove_check(pj_ice_sess *ice, pj_ice_sess_checklist *clist, /* Prune checklist, this must have been done after the checklist * is sorted. */ -static pj_status_t prune_checklist(pj_ice_sess *ice, +static pj_status_t prune_checklist(pj_ice_sess *ice, pj_ice_sess_checklist *clist) { unsigned i; @@ -1151,7 +1349,7 @@ static pj_status_t prune_checklist(pj_ice_sess *ice, * the list. This is done by removing a pair if its local and remote * candidates are identical to the local and remote candidates of a pair * higher up on the priority list. The result is a sequence of ordered - * candidate pairs, called the check list for that media stream. + * candidate pairs, called the check list for that media stream. */ /* First replace SRFLX candidates with their base */ for (i=0; icount; ++i) { @@ -1178,7 +1376,7 @@ static pj_status_t prune_checklist(pj_ice_sess *ice, if (j==ice->lcand_cnt) { char baddr[PJ_INET6_ADDRSTRLEN]; /* Host candidate not found this this srflx! */ - LOG4((ice->obj_name, + LOG4((ice->obj_name, "Base candidate %s:%d not found for srflx candidate %d", pj_sockaddr_print(&srflx->base_addr, baddr, sizeof(baddr), 2), @@ -1187,6 +1385,15 @@ static pj_status_t prune_checklist(pj_ice_sess *ice, return PJNATH_EICENOHOSTCAND; } } + + /* Section 6.2, RFC 6544 (https://tools.ietf.org/html/rfc6544) + * When the agent prunes the check list, it MUST also remove any pair + * for which the local candidate is a passive TCP candidate + */ + if (clist->checks[i].lcand->transport == PJ_CAND_TCP_PASSIVE) { + remove_check(ice, clist, i, "local passive TCP"); + i--; + } } /* Next remove a pair if its local and remote candidates are identical @@ -1218,8 +1425,8 @@ static pj_status_t prune_checklist(pj_ice_sess *ice, if ((licand == ljcand) && (ricand == rjcand)) { reason = "duplicate found"; } else if ((rjcand == ricand) && - (pj_sockaddr_cmp(&ljcand->base_addr, - &licand->base_addr)==0)) + (pj_sockaddr_cmp(&ljcand->base_addr, + &licand->base_addr)==0)) { reason = "equal base"; } @@ -1256,8 +1463,13 @@ static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te) } switch (type) { + case TIMER_CONTROLLING_TCP_PASSIVE_TIMEOUT: + LOG4((ice->obj_name, + "Controlling agent timed-out while waiting for incoming TCP checks. Set state to failed!")); + on_ice_complete(ice, PJNATH_EICEFAILED); + break; case TIMER_CONTROLLED_WAIT_NOM: - LOG4((ice->obj_name, + LOG4((ice->obj_name, "Controlled agent timed-out in waiting for the controlling " "agent to send nominated check. Setting state to fail now..")); on_ice_complete(ice, PJNATH_EICENOMTIMEOUT); @@ -1289,6 +1501,9 @@ static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te) case TIMER_KEEP_ALIVE: ice_keep_alive(ice, PJ_TRUE); break; + case TIMER_CONNECTION_TIMEOUT: + on_tcp_connect_timeout(ice); + break; case TIMER_NONE: /* Nothing to do, just to get rid of gcc warning */ break; @@ -1315,7 +1530,7 @@ static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now) the_check = comp->nominated_check; /* Create the Binding Indication */ - status = pj_stun_session_create_ind(comp->stun_sess, + status = pj_stun_session_create_ind(comp->stun_sess, PJ_STUN_BINDING_INDICATION, &tdata); if (status != PJ_SUCCESS) @@ -1335,9 +1550,15 @@ static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now) /* Send to session */ addr_len = pj_sockaddr_get_len(&the_check->rcand->addr); status = pj_stun_session_send_msg(comp->stun_sess, msg_data, - PJ_FALSE, PJ_FALSE, - &the_check->rcand->addr, + PJ_FALSE, PJ_FALSE, + &the_check->rcand->addr, addr_len, tdata); + if (status != PJ_SUCCESS && status != PJ_EPENDING && status != PJ_EBUSY) { + if (ice->cb.on_ice_destroy) { + ice->cb.on_ice_destroy(ice); + } + return; + } /* Restore FINGERPRINT usage */ pj_stun_session_use_fingerprint(comp->stun_sess, saved); @@ -1349,8 +1570,8 @@ done: if (ice->timer.id == TIMER_NONE) { pj_time_val delay = { 0, 0 }; - delay.msec = (PJ_ICE_SESS_KEEP_ALIVE_MIN + - (pj_rand() % PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND)) * 1000 / + delay.msec = (PJ_ICE_SESS_KEEP_ALIVE_MIN + + (pj_rand() % PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND)) * 1000 / ice->comp_cnt; pj_time_val_normalize(&delay); @@ -1370,13 +1591,13 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) if (!ice->is_complete) { ice->is_complete = PJ_TRUE; ice->ice_status = status; - + pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer, TIMER_NONE); /* Log message */ - LOG4((ice->obj_name, "ICE process complete, status=%s", - pj_strerror(status, ice->tmp.errmsg, + LOG4((ice->obj_name, "ICE process complete, status=%s", + pj_strerror(status, ice->tmp.errmsg, sizeof(ice->tmp.errmsg)).ptr)); dump_checklist("Valid list", ice, &ice->valid_list); @@ -1394,7 +1615,7 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) } /* Update valid check and nominated check for the candidate */ -static void update_comp_check(pj_ice_sess *ice, unsigned comp_id, +static void update_comp_check(pj_ice_sess *ice, unsigned comp_id, pj_ice_sess_check *check) { pj_ice_sess_comp *comp; @@ -1443,18 +1664,18 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) pj_bool_t no_pending_check = PJ_FALSE; /* Still in 8.2. Updating States - * + * * o Once there is at least one nominated pair in the valid list for * every component of at least one media stream and the state of the * check list is Running: - * + * * * The agent MUST change the state of processing for its check * list for that media stream to Completed. - * + * * * The agent MUST continue to respond to any checks it may still * receive for that media stream, and MUST perform triggered * checks if required by the processing of Section 7.2. - * + * * * The agent MAY begin transmitting media for this media stream as * described in Section 11.1 */ @@ -1474,28 +1695,28 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) /* Note: this is the stuffs that we don't do in 7.1.2.2.2, since our * ICE session only supports one media stream for now: - * + * * 7.1.2.2.2. Updating Pair States * * 2. If there is a pair in the valid list for every component of this * media stream (where this is the actual number of components being * used, in cases where the number of components signaled in the SDP * differs from offerer to answerer), the success of this check may - * unfreeze checks for other media streams. + * unfreeze checks for other media streams. */ /* 7.1.2.3. Check List and Timer State Updates * Regardless of whether the check was successful or failed, the * completion of the transaction may require updating of check list and * timer states. - * + * * If all of the pairs in the check list are now either in the Failed or * Succeeded state, and there is not a pair in the valid list for each * component of the media stream, the state of the check list is set to - * Failed. + * Failed. */ - /* + /* * See if all checks in the checklist have completed. If we do, * then mark ICE processing as failed. */ @@ -1508,14 +1729,64 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) } no_pending_check = (i == ice->clist.count); } +#if PJ_HAS_TCP + pj_bool_t hasTCP = PJ_FALSE; +#endif + for (i=0; iclist.count; ++i) { + pj_ice_sess_check *c = &ice->clist.checks[i]; + +#if PJ_HAS_TCP + if (c && c->lcand && + ( + c->lcand->transport == PJ_CAND_TCP_ACTIVE + )) { + hasTCP = PJ_TRUE; + } +#endif + } if (no_pending_check) { +#if PJ_HAS_TCP + if (hasTCP) { + // STUN server procedure https://tools.ietf.org/html/rfc6544#section-7.2 + // An ICE TCP agent, full or lite, MUST be prepared to receive incoming + // TCP connection requests on the base of any TCP candidate that is + // simultaneous-open or passive. When the connection request is + // received, the agent MUST accept it. + // https://tools.ietf.org/html/rfc5245#section-2.6 + // In that case, allowing ICE to run a little longer might produce + // better results. + if (ice->timer.id == TIMER_NONE && + ice->opt.agent_passive_timeout >= 0) + { + pj_time_val delay; + + delay.sec = 0; + delay.msec = ice->opt.agent_passive_timeout; + pj_time_val_normalize(&delay); + + pj_timer_heap_schedule_w_grp_lock( + ice->stun_cfg.timer_heap, + &ice->timer, &delay, + TIMER_CONTROLLING_TCP_PASSIVE_TIMEOUT, + ice->grp_lock); + + LOG5((ice->obj_name, + "All checks have completed but failed. Just " + "wait for passive connections to timeout " + "(timeout=%d msec)", + ice->opt.agent_passive_timeout)); + return PJ_FALSE; + } + } +#endif + /* All checks have completed, but we don't have nominated pair. - * If agent's role is controlled, check if all components have - * valid pair. If it does, this means the controlled agent has - * finished the check list and it's waiting for controlling - * agent to send checks with USE-CANDIDATE flag set. - */ + * If agent's role is controlled, check if all components have + * valid pair. If it does, this means the controlled agent has + * finished the check list and it's waiting for controlling + * agent to send checks with USE-CANDIDATE flag set. + */ if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLED) { for (i=0; i < ice->comp_cnt; ++i) { if (ice->comp[i].valid_check == NULL) @@ -1524,16 +1795,16 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) if (i < ice->comp_cnt) { /* This component ID doesn't have valid pair. - * Mark ICE as failed. - */ + * Mark ICE as failed. + */ on_ice_complete(ice, PJNATH_EICEFAILED); return PJ_TRUE; } else { /* All components have a valid pair. - * We should wait until we receive nominated checks. - */ + * We should wait until we receive nominated checks. + */ if (ice->timer.id == TIMER_NONE && - ice->opt.controlled_agent_want_nom_timeout >= 0) + ice->opt.controlled_agent_want_nom_timeout >= 0) { pj_time_val delay; @@ -1547,11 +1818,11 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) TIMER_CONTROLLED_WAIT_NOM, ice->grp_lock); - LOG5((ice->obj_name, - "All checks have completed. Controlled agent now " - "waits for nomination from controlling agent " - "(timeout=%d msec)", - ice->opt.controlled_agent_want_nom_timeout)); + LOG5((ice->obj_name, + "All checks have completed. Controlled agent now " + "waits for nomination from controlling agent " + "(timeout=%d msec)", + ice->opt.controlled_agent_want_nom_timeout)); } return PJ_FALSE; } @@ -1560,17 +1831,16 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) } else if (ice->is_nominating) { /* We are controlling agent and all checks have completed but - * there's at least one component without nominated pair (or - * more likely we don't have any nominated pairs at all). - */ + * there's at least one component without nominated pair (or + * more likely we don't have any nominated pairs at all). + */ on_ice_complete(ice, PJNATH_EICEFAILED); return PJ_TRUE; - } else { /* We are controlling agent and all checks have completed. If - * we have valid list for every component, then move on to - * sending nominated check, otherwise we have failed. - */ + * we have valid list for every component, then move on to + * sending nominated check, otherwise we have failed. + */ for (i=0; icomp_cnt; ++i) { if (ice->comp[i].valid_check == NULL) break; @@ -1578,17 +1848,17 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) if (i < ice->comp_cnt) { /* At least one component doesn't have a valid check. Mark - * ICE as failed. - */ + * ICE as failed. + */ on_ice_complete(ice, PJNATH_EICEFAILED); return PJ_TRUE; } - /* Now it's time to send connectivity check with nomination - * flag set. - */ - LOG4((ice->obj_name, - "All checks have completed, starting nominated checks now")); + /* Now it's time to send connectivity check with nomination + * flag set. + */ + LOG4((ice->obj_name, + "All checks have completed, starting nominated checks now")); start_nominated_check(ice); return PJ_FALSE; } @@ -1602,7 +1872,7 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) if (/*check->err_code == PJ_SUCCESS && */ ice->role==PJ_ICE_SESS_ROLE_CONTROLLING && !ice->is_nominating && - ice->timer.id == TIMER_NONE) + ice->timer.id == TIMER_NONE) { pj_time_val delay; @@ -1618,7 +1888,7 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) return PJ_FALSE; } - LOG4((ice->obj_name, + LOG4((ice->obj_name, "Scheduling nominated check in %d ms", ice->opt.nominated_check_delay)); @@ -1655,12 +1925,12 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, comp = find_comp(ice, check->lcand->comp_id); /* 7.1.2.2.2. Updating Pair States - * + * * The agent sets the state of the pair that generated the check to * Succeeded. The success of this check might also cause the state of * other checks to change as well. The agent MUST perform the following * two steps: - * + * * 1. The agent changes the states for all other Frozen pairs for the * same media stream and same foundation to Waiting. Typically * these other pairs will have different component IDs but not @@ -1692,7 +1962,7 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, } /* 8.2. Updating States - * + * * For both controlling and controlled agents, the state of ICE * processing depends on the presence of nominated candidate pairs in * the valid list and on the state of the check list: @@ -1723,10 +1993,10 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, if (c->state < PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) { /* Just fail Frozen/Waiting check */ - LOG5((ice->obj_name, + LOG5((ice->obj_name, "Check %s to be failed because state is %s", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), - &ice->clist, c), + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + &ice->clist, c), check_state_name[c->state])); check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_FAILED, PJ_ECANCELLED); @@ -1737,11 +2007,11 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, /* State is IN_PROGRESS, cancel transaction */ if (c->tdata) { - LOG5((ice->obj_name, + LOG5((ice->obj_name, "Cancelling check %s (In Progress)", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, c))); - pj_stun_session_cancel_req(comp->stun_sess, + pj_stun_session_cancel_req(comp->stun_sess, c->tdata, PJ_FALSE, 0); c->tdata = NULL; check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_FAILED, @@ -1755,6 +2025,44 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, return check_ice_complete(ice); } +static void on_tcp_connect_timeout(pj_ice_sess* ice) +{ + pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap,&ice->timer_connect, + TIMER_NONE); + + pj_bool_t first_found = PJ_FALSE, set_timer = PJ_FALSE; + + for (int i = 0; iclist.count && !set_timer; ++i) { + pj_ice_sess_check *check = &ice->clist.checks[i]; + if (check->state == PJ_ICE_SESS_CHECK_STATE_PENDING) { + if (first_found) { + set_timer = PJ_TRUE; + } else { + first_found = PJ_TRUE; + if (*ice->cb.close_tcp_connection) + (*ice->cb.close_tcp_connection)(ice, i); + + check_set_state(ice, check, + PJ_ICE_SESS_CHECK_STATE_FAILED, PJ_ECANCELLED); + on_check_complete(ice, check); + } + } + } + + if (set_timer && ice->timer_connect.id == TIMER_NONE) { + /* Reschedule */ + pj_time_val delay = { + .sec = 15, + .msec = 0 + }; + pj_time_val_normalize(&delay); + pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap, + &ice->timer_connect, &delay, + TIMER_CONNECTION_TIMEOUT, + ice->grp_lock); + } +} + /* Get foundation index of a check pair. This function can also be used for * adding a new foundation (combination of local & remote cands foundations) @@ -1876,7 +2184,7 @@ static pj_status_t add_rcand_and_update_checklist( if (j < ice->rcand_cnt) continue; } - + /* Available cand slot? */ if (ice->rcand_cnt >= PJ_ICE_MAX_CAND) { char tmp[PJ_INET6_ADDRSTRLEN + 10]; @@ -1912,10 +2220,10 @@ static pj_status_t add_rcand_and_update_checklist( if (discard_check(ice, clist, &max_prio) == 0) continue; } - + /* A local candidate is paired with a remote candidate if - * and only if the two candidates have the same component ID - * and have the same IP address version. + * and only if the two candidates have the same component ID + * and have the same IP address version. */ if ((lcand->comp_id != rcand->comp_id) || (lcand->addr.addr.sa_family != rcand->addr.addr.sa_family)) @@ -1923,6 +2231,29 @@ static pj_status_t add_rcand_and_update_checklist( continue; } + /* Section 6.2, RFC 6544 (https://tools.ietf.org/html/rfc6544) + * As with UDP, check lists are formed only by full ICE implementations. + * When forming candidate pairs, the following types of TCP candidates + * can be paired with each other: + * + * Local Remote + * Candidate Candidate + * --------------------------- + * tcp-so tcp-so + * tcp-active tcp-passive + * tcp-passive tcp-active + */ + if ((lcand->transport == PJ_CAND_UDP && + rcand->transport != PJ_CAND_UDP) || + (lcand->transport == PJ_CAND_TCP_PASSIVE && + rcand->transport != PJ_CAND_TCP_ACTIVE) || + (lcand->transport == PJ_CAND_TCP_ACTIVE && + rcand->transport != PJ_CAND_TCP_PASSIVE) || + (lcand->transport == PJ_CAND_TCP_SO && + rcand->transport != PJ_CAND_TCP_SO)) + { + continue; + } #if 0 /* Trickle ICE: * Make sure that pair has not been added to checklist @@ -1952,6 +2283,9 @@ static pj_status_t add_rcand_and_update_checklist( chk->state = PJ_ICE_SESS_CHECK_STATE_FROZEN; chk->foundation_idx = get_check_foundation_idx(ice, lcand, rcand, PJ_TRUE); +#if PJ_HAS_TCP + chk->reconnect_count = 0; +#endif /* Check if foundation cannot be added (e.g: list is full) */ if (chk->foundation_idx < 0) @@ -2139,6 +2473,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list( td = PJ_POOL_ZALLOC_T(ice->pool, timer_data); td->ice = ice; td->clist = clist; + td->first_packet_counter = 1; clist->timer.user_data = (void*)td; clist->timer.cb = &periodic_timer; @@ -2196,7 +2531,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_update_check_list( pj_grp_lock_release(ice->grp_lock); return PJ_SUCCESS; } - + /* Verify remote ufrag & passwd, if remote candidate specified */ if (rem_cand_cnt && (pj_strcmp(&ice->tx_ufrag, rem_ufrag) || pj_strcmp(&ice->tx_pass, rem_passwd))) @@ -2227,8 +2562,38 @@ PJ_DEF(pj_status_t) pj_ice_sess_update_check_list( return status; } +static pj_status_t send_connectivity_check(pj_ice_sess *ice, + pj_ice_sess_checklist *clist, + unsigned check_id, + pj_bool_t nominate, + pj_ice_msg_data *msg_data) +{ + pj_ice_sess_check *check; + const pj_ice_sess_cand *lcand; + const pj_ice_sess_cand *rcand; + pj_ice_sess_comp *comp; + + check = &clist->checks[check_id]; + lcand = check->lcand; + rcand = check->rcand; + comp = find_comp(ice, lcand->comp_id); + + /* Note that USERNAME and MESSAGE-INTEGRITY will be added by the + * STUN session. + */ + + /* Initiate STUN transaction to send the request */ + + return pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE, + pj_stun_session_tp_type(comp->stun_sess)== + PJ_STUN_TP_UDP, + &rcand->addr, + pj_sockaddr_get_len(&rcand->addr), + check->tdata); +} + /* Perform check on the specified candidate pair. */ -static pj_status_t perform_check(pj_ice_sess *ice, +static pj_status_t perform_check(pj_ice_sess *ice, pj_ice_sess_checklist *clist, unsigned check_id, pj_bool_t nominate) @@ -2237,22 +2602,20 @@ static pj_status_t perform_check(pj_ice_sess *ice, pj_ice_msg_data *msg_data; pj_ice_sess_check *check; const pj_ice_sess_cand *lcand; - const pj_ice_sess_cand *rcand; pj_uint32_t prio; pj_status_t status; check = &clist->checks[check_id]; lcand = check->lcand; - rcand = check->rcand; comp = find_comp(ice, lcand->comp_id); - LOG5((ice->obj_name, - "Sending connectivity check for check %s", + LOG5((ice->obj_name, + "Sending connectivity check for check %s", dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, check))); pj_log_push_indent(); /* Create request */ - status = pj_stun_session_create_req(comp->stun_sess, + status = pj_stun_session_create_req(comp->stun_sess, PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC, NULL, &check->tdata); if (status != PJ_SUCCESS) { @@ -2282,7 +2645,7 @@ static pj_status_t perform_check(pj_ice_sess *ice, ((1 << PJ_ICE_LOCAL_PREF_BITS) - 1) - lcand->id, lcand->comp_id); #endif - pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg, + pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg, PJ_STUN_ATTR_PRIORITY, prio); /* Add USE-CANDIDATE and set this check to nominated. @@ -2295,44 +2658,84 @@ static pj_status_t perform_check(pj_ice_sess *ice, check->nominated = PJ_TRUE; } - pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, + pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, PJ_STUN_ATTR_ICE_CONTROLLING, &ice->tie_breaker); } else { - pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, - PJ_STUN_ATTR_ICE_CONTROLLED, - &ice->tie_breaker); - } - - /* Note that USERNAME and MESSAGE-INTEGRITY will be added by the - * STUN session. - */ + pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, + PJ_STUN_ATTR_ICE_CONTROLLED, + &ice->tie_breaker); + } - /* Initiate STUN transaction to send the request */ - status = pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE, - PJ_TRUE, &rcand->addr, - pj_sockaddr_get_len(&rcand->addr), - check->tdata); - if (status != PJ_SUCCESS) { - check->tdata = NULL; - pjnath_perror(ice->obj_name, "Error sending STUN request", status); - pj_log_pop_indent(); - return status; +#if PJ_HAS_TCP + switch (lcand->transport) { + case PJ_CAND_TCP_ACTIVE: + switch (check->state) { + case PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY: + status = (*ice->cb.reconnect_tcp_connection)(ice,check_id); + break; + case PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET: + status = send_connectivity_check(ice, clist, check_id, + nominate, msg_data); + break; + default: + pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, + &ice->timer_connect, TIMER_NONE); + status = (*ice->cb.wait_tcp_connection)(ice, check_id); + if (ice->timer_connect.id != TIMER_NONE) { + pj_assert(!"Not expected any timer active"); + } else { + LOG5((ice->obj_name, + "Scheduling connection time-out for check %s", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, check))); + + pj_time_val delay = { + .sec = 0, + .msec = PJ_ICE_TCP_CONNECTION_TIMEOUT, + }; + pj_time_val_normalize(&delay); + pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap, + &ice->timer_connect, &delay, + TIMER_CONNECTION_TIMEOUT, + ice->grp_lock); + } + break; + } + break; + case PJ_CAND_TCP_PASSIVE: + case PJ_CAND_TCP_SO: + case PJ_CAND_UDP: + default: + status = send_connectivity_check(ice, clist, check_id, nominate, msg_data); + break; } +#else + status = send_connectivity_check(ice, clist, check_id, nominate, msg_data); +#endif - check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, - PJ_SUCCESS); + if (status == PJ_SUCCESS) { + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, + status); + } else if (status == PJ_EPENDING) { + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_PENDING, status); + } else if (check->rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { + /* TODO (sblin) remove this - https://github.com/coturn/coturn/issues/408 */ + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, + status); + } else { + check->tdata = NULL; + pjnath_perror(ice->obj_name, "Error sending STUN request (perform check)", status); + } pj_log_pop_indent(); - return PJ_SUCCESS; + return status; } - /* Start periodic check for the specified checklist. - * This callback is called by timer on every Ta (20msec by default) + * This callback is called by timer on every Ta */ -static pj_status_t start_periodic_check(pj_timer_heap_t *th, +static pj_status_t start_periodic_check(pj_timer_heap_t *th, pj_timer_entry *te) { timer_data *td; @@ -2345,6 +2748,8 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th, td = (struct timer_data*) te->user_data; ice = td->ice; clist = td->clist; + pj_time_val timeout = {0, PJ_ICE_TA_VAL}; + pj_bool_t check_pending = PJ_FALSE; pj_grp_lock_acquire(ice->grp_lock); @@ -2401,10 +2806,53 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th, } } +#if PJ_HAS_TCP /* If we don't have anything in Waiting state, find any pair with - * highest priority in Frozen state. + * highest priority in Retry state. */ + if (!check) { + for (i = 0; i < clist->count; ++i) { + pj_ice_sess_check *c = &clist->checks[i]; + // Reconnect closed TURN sockets + if (c->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY) { + LOG5((ice->obj_name, "re-Starting periodic check for check %i (needs retry)", i)); + check = c; + check_idx = i; + + timeout.msec = PJ_ICE_TCP_RECONNECTION_DELAY; + timeout.sec = 0; + break; + } + } + } + + if (!check) { + // TODO (sblin) remove - https://github.com/coturn/coturn/issues/408 + pj_bool_t inc_counter = PJ_TRUE; + for (i = 0; i < clist->count; ++i) { + pj_ice_sess_check *c = &clist->checks[i]; + if (c->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET) { + if (inc_counter) { + td->first_packet_counter += 1; + inc_counter = PJ_FALSE; + } + if (td->first_packet_counter % 50 == 0) { + LOG5((ice->obj_name, "re-Starting periodic check for check %i (needs 1st packet)", i)); + check = c; + check_idx = i; + } + check_pending = PJ_TRUE; + break; + } + } + } +#endif + + /* If we don't have anything in Waiting or Retry state, find any pair with + * highest priority in Frozen state. + */ + if (!check && !check_pending) { for (i=0; icount; ++i) { pj_ice_sess_check *c = &clist->checks[i]; if (c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) { @@ -2414,6 +2862,19 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th, } } } + +#if PJ_HAS_TCP + if (!check && !check_pending) { + // If all sockets are pending, do nothing + for (i = 0; i < clist->count; ++i) { + pj_ice_sess_check *c = &clist->checks[i]; + if (c->state == PJ_ICE_SESS_CHECK_STATE_PENDING) { + check_pending = PJ_TRUE; + break; + } + } + } +#endif } /* Perform check & schedule next check for next candidate pair, @@ -2421,15 +2882,14 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th, * or empty checklist). */ if (check) { - pj_time_val timeout = {0, PJ_ICE_TA_VAL}; - status = perform_check(ice, clist, check_idx, ice->is_nominating); - if (status != PJ_SUCCESS) { + if (status != PJ_SUCCESS && status != PJ_EPENDING) { check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); on_check_complete(ice, check); } - + } + if (check || check_pending) { /* Schedule next check */ pj_time_val_normalize(&timeout); pj_timer_heap_schedule_w_grp_lock(th, te, &timeout, PJ_TRUE, @@ -2441,7 +2901,6 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th, return PJ_SUCCESS; } - /* Start sending connectivity check with USE-CANDIDATE */ static void start_nominated_check(pj_ice_sess *ice) { @@ -2483,7 +2942,7 @@ static void start_nominated_check(pj_ice_sess *ice) { pj_assert(c->err_code == PJ_SUCCESS); c->state = PJ_ICE_SESS_CHECK_STATE_FROZEN; - check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, + check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, PJ_SUCCESS); break; } @@ -2511,7 +2970,7 @@ static void start_nominated_check(pj_ice_sess *ice) } /* Timer callback to perform periodic check */ -static void periodic_timer(pj_timer_heap_t *th, +static void periodic_timer(pj_timer_heap_t *th, pj_timer_entry *te) { start_periodic_check(th, te); @@ -2550,9 +3009,9 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice) * media stream is the first media stream when it is described by * the first m-line in the SDP offer and answer). For that media * stream, it: - * + * * - Groups together all of the pairs with the same foundation, - * + * * - For each group, sets the state of the pair with the lowest * component ID to Waiting. If there is more than one such pair, * the one with the highest priority is used. @@ -2600,7 +3059,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice) /* First, perform all pending triggered checks, simultaneously. */ rcheck = ice->early_check.next; while (rcheck != &ice->early_check) { - LOG4((ice->obj_name, + LOG4((ice->obj_name, "Performing delayed triggerred check for component %d", rcheck->comp_id)); pj_log_push_indent(); @@ -2611,7 +3070,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice) pj_list_init(&ice->early_check); /* Start periodic check */ - /* We could start it immediately like below, but lets schedule timer + /* We could start it immediately like below, but lets schedule timer * instead to reduce stack usage: * return start_periodic_check(ice->stun_cfg.timer_heap, &clist->timer); */ @@ -2663,7 +3122,7 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess, pj_ice_sess *ice = sd->ice; pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token; pj_status_t status; - + pj_grp_lock_acquire(ice->grp_lock); if (ice->is_destroying) { @@ -2680,6 +3139,252 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess, return status; } +static pj_ice_sess_check* get_current_check_at_state(pj_ice_sess *ice, + pj_sockaddr_t *remote_addr, + pj_ice_sess_check_state state, + int *current_check) +{ + if (!ice || !remote_addr) + return NULL; + // NOTE: Multiple checks can have the same remote, we only take care of the first + // First, check if the TCP is really connected. If not, abort + pj_ice_sess_check *check = NULL; + for (int i = 0; i < ice->clist.count; ++i) { + // Find related check + pj_ice_sess_check *c = &ice->clist.checks[i]; + /* Host candidate not found this this srflx! */ + if (pj_sockaddr_cmp(remote_addr, &c->rcand->addr) == 0) { + if (c->tdata == NULL || c->state != state) + continue; + /* Match */ + check = c; + if (current_check) *current_check = i; + break; + } + } + return check; +} + +void ice_sess_on_peer_connection(pj_ice_sess *ice, + pj_uint8_t transport_id, + pj_status_t status, + pj_sockaddr_t* remote_addr) +{ + // The TCP link is now ready. We can now send the first STUN message (send + // connectivity check) This should trigger on_stun_request_complete when + // finished + if (!remote_addr) + return; + + pj_grp_lock_acquire(ice->grp_lock); + + int current_check = -1; + pj_ice_sess_check *check = get_current_check_at_state(ice,remote_addr, + PJ_ICE_SESS_CHECK_STATE_PENDING, + ¤t_check); + if (!check) { + // Handle peer reflexive candidates (incoming are still waiting here) + check = get_current_check_at_state(ice, remote_addr, + PJ_ICE_SESS_CHECK_STATE_WAITING, + ¤t_check); + if (!check) { + pj_grp_lock_release(ice->grp_lock); + return; + } + } + + const pj_ice_sess_cand *rcand = check->rcand; + if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED && ( + status == PJ_ERRNO_START_SYS + 104 || status == 130054 /* CONNECTION RESET BY PEER */ || + status == PJ_ERRNO_START_SYS + 111 /* Connection refused */ + )) { + /** + * This part of the code is triggered when using ICE over TCP via TURN + * In fact, the other peer has to authorize this peer to connect to + * the relayed candidate. This is done by set_perm from the other case. + * But from this side, we can't know if the peer has authorized us. If it's + * not the case, the connection will got a CONNECTION RESET BY PEER status. + * In this case, we try to reconnect few times with a delay between two + * attempts. + */ + if (check->reconnect_count < PJ_ICE_TCP_MAX_RECONNECTION_COUNT) { + check->state = PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY; + check_set_state(ice, check,PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, + status); + check->reconnect_count++; + } else { + // Max attempts reached. Fail this check. + LOG4((ice->obj_name, "Check %s: connection failed after %d attempts", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), + PJ_ICE_TCP_MAX_RECONNECTION_COUNT)); + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); + on_check_complete(ice, check); + } + pj_grp_lock_release(ice->grp_lock); + return; + } else if (status != PJ_SUCCESS) { + if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { + char raddr[PJ_INET6_ADDRSTRLEN + 10]; + PJ_LOG(4, (ice->obj_name, + "Connection to TURN (%s) failed with status %u", + pj_sockaddr_print(&rcand->addr, raddr, sizeof(raddr), 3), status)); + } + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); + on_check_complete(ice, check); + pj_grp_lock_release(ice->grp_lock); + return; + } + + // TCP is correctly connected. Craft the message to send + const pj_ice_sess_cand *lcand = check->lcand; + if (check->tdata == NULL) { + LOG5((ice->obj_name, "Error sending STUN request, empty data")); + pj_grp_lock_release(ice->grp_lock); + return; + } + pj_ice_msg_data *msg_data = + PJ_POOL_ZALLOC_T(check->tdata->pool, pj_ice_msg_data); + + msg_data->transport_id = transport_id; + msg_data->has_req_data = PJ_TRUE; + msg_data->data.req.ice = ice; + msg_data->data.req.clist = &ice->clist; + msg_data->data.req.ckid = current_check; + msg_data->data.req.lcand = check->lcand; + msg_data->data.req.rcand = check->rcand; + + pj_ice_sess_comp *comp = find_comp(ice, lcand->comp_id); + // Note that USERNAME and MESSAGE-INTEGRITY will be added by the + // STUN session. + + // Initiate STUN transaction to send the request + status = pj_stun_session_send_msg(comp->stun_sess, msg_data, + PJ_FALSE, PJ_FALSE, &rcand->addr, + pj_sockaddr_get_len(&rcand->addr), + check->tdata); + + if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED && ( + status == PJ_ERRNO_START_SYS + 104 || status == 130054 || /* CONNECTION RESET BY PEER */ + status == PJ_ERRNO_START_SYS + 32 /* EPIPE */ || + status == PJ_ERRNO_START_SYS + 111 /* Connection refused */ + )) { + /** + * This part of the code is triggered when using ICE over TCP via TURN + * In fact, the other peer has to authorize this peer to connect to + * the relayed candidate. This is done by set_perm from the other case. + * But from this side, we can't know if the peer has authorized us. If it's + * not the case, the connection will got a CONNECTION RESET BY PEER status. + * In this case, we can try to reconnect a bit after and this until the check + * reached its timeout. + */ + + if (check->reconnect_count < PJ_ICE_TCP_MAX_RECONNECTION_COUNT) { + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, + status); + check->reconnect_count++; + } else { + // Max attempts reached. Fail this check. + LOG4((ice->obj_name, "Check %s: connection failed after %d attempts", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), + PJ_ICE_TCP_MAX_RECONNECTION_COUNT)); + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); + on_check_complete(ice, check); + pj_grp_lock_release(ice->grp_lock); + return; + } + } else if (status == PJ_EBUSY /* EBUSY */) { + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, + status); + } else if (status != PJ_SUCCESS) { + + if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { + char raddr[PJ_INET6_ADDRSTRLEN + 10]; + PJ_LOG(5, (ice->obj_name, + "STUN send message to TURN (%s) failed with status %u", + pj_sockaddr_print(&rcand->addr, raddr, sizeof(raddr), 3), status)); + } + check->tdata = NULL; + pjnath_perror(ice->obj_name, "Error sending STUN request (on peer connection)", status); + pj_log_pop_indent(); + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); + on_check_complete(ice, check); + } else { + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, status); + } + pj_grp_lock_release(ice->grp_lock); +} + +void ice_sess_on_peer_reset_connection(pj_ice_sess *ice, + pj_uint8_t transport_id, + pj_sockaddr_t* remote_addr) +{ + // The TCP link is reset + if (!remote_addr) + return; + + pj_grp_lock_acquire(ice->grp_lock); + pj_ice_sess_check *check = get_current_check_at_state(ice, remote_addr, + PJ_ICE_SESS_CHECK_STATE_PENDING, + NULL); + if (!check) { + // Handle peer reflexive candidates (incoming are still waiting here) + check = get_current_check_at_state(ice, remote_addr, + PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, + NULL); + + if (!check) { + // Just check if it's not the first packet failing + check = get_current_check_at_state(ice, remote_addr, + PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, + NULL); + if (!check) { + pj_grp_lock_release(ice->grp_lock); + return; + } + } + } + + const pj_ice_sess_cand *rcand = check->rcand; + if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { + char raddr[PJ_INET6_ADDRSTRLEN + 10]; + PJ_LOG(5, (ice->obj_name, + "Connection to TURN (%s) is reset", + pj_sockaddr_print(&rcand->addr, raddr, sizeof(raddr), 3))); + + check->state = PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY; + check_set_state(ice, check, + PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, 120104); + } + + pj_grp_lock_release(ice->grp_lock); +} + +void ice_sess_on_peer_packet(pj_ice_sess *ice, + pj_uint8_t transport_id, + pj_sockaddr_t* remote_addr) +{ + // The TCP link received its bind request response + if (!ice || !remote_addr) { + return; + } + + pj_grp_lock_acquire(ice->grp_lock); + pj_ice_sess_check *check = + get_current_check_at_state(ice, remote_addr, + PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, + NULL); + if (!check) { + pj_grp_lock_release(ice->grp_lock); + return; + } + + const pj_ice_sess_cand *rcand = check->rcand; + if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { + check_set_state(ice, check, + PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, PJ_SUCCESS); + } + pj_grp_lock_release(ice->grp_lock); +} /* This callback is called when outgoing STUN request completed */ static void on_stun_request_complete(pj_stun_session *stun_sess, @@ -2765,13 +3470,13 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, * * 7.1.2.1. Failure Cases: * - * If the request had contained the ICE-CONTROLLED attribute, + * If the request had contained the ICE-CONTROLLED attribute, * the agent MUST switch to the controlling role if it has not - * already done so. If the request had contained the - * ICE-CONTROLLING attribute, the agent MUST switch to the + * already done so. If the request had contained the + * ICE-CONTROLLING attribute, the agent MUST switch to the * controlled role if it has not already done so. Once it has * switched, the agent MUST immediately retry the request with - * the ICE-CONTROLLING or ICE-CONTROLLED attribute reflecting + * the ICE-CONTROLLING or ICE-CONTROLLED attribute reflecting * its new role. */ pj_ice_sess_role new_role = PJ_ICE_SESS_ROLE_UNKNOWN; @@ -2779,7 +3484,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_ICE_CONTROLLING, 0)) { new_role = PJ_ICE_SESS_ROLE_CONTROLLED; - } else if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_ICE_CONTROLLED, + } else if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_ICE_CONTROLLED, 0)) { new_role = PJ_ICE_SESS_ROLE_CONTROLLING; } else { @@ -2788,7 +3493,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, } if (new_role != ice->role) { - LOG4((ice->obj_name, + LOG4((ice->obj_name, "Changing role because of role conflict response")); pj_ice_sess_change_role(ice, new_role); } @@ -2805,9 +3510,9 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, } pj_strerror(status, errmsg, sizeof(errmsg)); - LOG4((ice->obj_name, + LOG4((ice->obj_name, "Check %s%s: connectivity check FAILED: %s", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), (check->nominated ? " (nominated)" : " (not nominated)"), errmsg)); @@ -2836,7 +3541,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, * is synthesized from IPv4). */ pj_sockaddr synth_addr; - + status = pj_sockaddr_synthesize(pj_AF_INET6(), &synth_addr, &check->rcand->addr); if (status == PJ_SUCCESS && @@ -2848,9 +3553,9 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, if (pj_sockaddr_cmp(&check->rcand->addr, source_addr) != 0) { status = PJNATH_EICEINSRCADDR; - LOG4((ice->obj_name, + LOG4((ice->obj_name, "Check %s%s: connectivity check FAILED: source address mismatch", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), (check->nominated ? " (nominated)" : " (not nominated)"))); pj_log_push_indent(); @@ -2862,24 +3567,24 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, } /* 7.1.2.2. Success Cases - * + * * A check is considered to be a success if all of the following are * true: - * + * * o the STUN transaction generated a success response - * + * * o the source IP address and port of the response equals the * destination IP address and port that the Binding Request was sent * to - * + * * o the destination IP address and port of the response match the * source IP address and port that the Binding Request was sent from */ - LOG4((ice->obj_name, + LOG4((ice->obj_name, "Check %s%s: connectivity check SUCCESS", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), (check->nominated ? " (nominated)" : " (not nominated)"))); @@ -2887,7 +3592,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, xaddr = (pj_stun_xor_mapped_addr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR,0); if (!xaddr) { - check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, PJNATH_ESTUNNOMAPPEDADDR); on_check_complete(ice, check); pj_grp_lock_release(ice->grp_lock); @@ -2960,7 +3665,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, /* 7.1.2.2.1. Discovering Peer Reflexive Candidates * If the transport address returned in XOR-MAPPED-ADDRESS does not match - * any of the local candidates that the agent knows about, the mapped + * any of the local candidates that the agent knows about, the mapped * address represents a new candidate - a peer reflexive candidate. */ if (lcand == NULL) { @@ -2994,7 +3699,9 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, &check->lcand->base_addr, &check->lcand->base_addr, pj_sockaddr_get_len(&xaddr->sockaddr), - &cand_id); + &cand_id, + check->rcand->transport == PJ_CAND_UDP ? + PJ_CAND_UDP : PJ_CAND_TCP_PASSIVE); // Note: for IPv6, pj_ice_sess_add_cand can return SUCCESS // without adding any candidates if the candidate is // deprecated (because the ICE MUST NOT fail) @@ -3018,7 +3725,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, /* 7.1.2.2.3. Constructing a Valid Pair * Next, the agent constructs a candidate pair whose local candidate * equals the mapped address of the response, and whose remote candidate - * equals the destination address to which the request was sent. + * equals the destination address to which the request was sent. */ /* Add pair to valid list, if it's not there, otherwise just update @@ -3039,6 +3746,9 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, new_check->state = PJ_ICE_SESS_CHECK_STATE_SUCCEEDED; new_check->nominated = check->nominated; new_check->err_code = PJ_SUCCESS; +#if PJ_HAS_TCP + new_check->reconnect_count = 0; +#endif } else { new_check = &ice->valid_list.checks[i]; ice->valid_list.checks[i].nominated = check->nominated; @@ -3053,12 +3763,12 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, sort_checklist(ice, &ice->valid_list); /* 7.1.2.2.2. Updating Pair States - * + * * The agent sets the state of the pair that generated the check to * Succeeded. The success of this check might also cause the state of * other checks to change as well. */ - check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_SUCCEEDED, + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_SUCCEEDED, PJ_SUCCESS); /* Perform 7.1.2.2.2. Updating Pair States. @@ -3100,11 +3810,11 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, PJ_UNUSED_ARG(pkt); PJ_UNUSED_ARG(pkt_len); - + /* Reject any requests except Binding request */ if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) { - pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, - NULL, token, PJ_TRUE, + pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, + NULL, token, PJ_TRUE, src_addr, src_addr_len); return PJ_SUCCESS; } @@ -3170,13 +3880,13 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, { if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) { /* Switch role to controlled */ - LOG4((ice->obj_name, + LOG4((ice->obj_name, "Changing role because of ICE-CONTROLLING attribute")); pj_ice_sess_change_role(ice, PJ_ICE_SESS_ROLE_CONTROLLED); } else { /* Generate 487 response */ - pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, - NULL, token, PJ_TRUE, + pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, + NULL, token, PJ_TRUE, src_addr, src_addr_len); pj_grp_lock_release(ice->grp_lock); return PJ_SUCCESS; @@ -3187,21 +3897,21 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, { if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) { /* Generate 487 response */ - pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, - NULL, token, PJ_TRUE, + pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, + NULL, token, PJ_TRUE, src_addr, src_addr_len); pj_grp_lock_release(ice->grp_lock); return PJ_SUCCESS; } else { /* Switch role to controlled */ - LOG4((ice->obj_name, + LOG4((ice->obj_name, "Changing role because of ICE-CONTROLLED attribute")); pj_ice_sess_change_role(ice, PJ_ICE_SESS_ROLE_CONTROLLING); } } - /* - * First send response to this request + /* + * First send response to this request */ status = pj_stun_session_create_res(sess, rdata, 0, NULL, &tdata); if (status != PJ_SUCCESS) { @@ -3217,7 +3927,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, for (i = 0; i < ice->clist.count; ++i) { pj_ice_sess_check *c = &ice->clist.checks[i]; if (c->lcand->comp_id == sd->comp_id && - c->lcand->transport_id == transport_id) + c->lcand->transport_id == transport_id) { lcand = c->lcand; break; @@ -3232,7 +3942,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, */ for (i = 0; i < ice->rcand_cnt; ++i) { pj_sockaddr synth_addr; - + if (ice->rcand[i].addr.addr.sa_family != pj_AF_INET()) continue; @@ -3252,7 +3962,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, /* Add XOR-MAPPED-ADDRESS attribute */ - status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, + status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, source_addr, source_addr_len); @@ -3263,11 +3973,14 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, msg_data->has_req_data = PJ_FALSE; /* Send the response */ - status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, PJ_TRUE, + status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, pj_stun_session_tp_type(sess) == PJ_STUN_TP_UDP, src_addr, src_addr_len, tdata); + if (status == PJ_EBUSY) { + PJ_LOG(5, (ice->obj_name, "on_stun_rx_request, PJ_EBUSY")); + } - /* + /* * Handling early check. * * It's possible that we receive this request before we receive SDP @@ -3326,7 +4039,7 @@ static void handle_incoming_check(pj_ice_sess *ice, comp = find_comp(ice, rcheck->comp_id); - /* Find remote candidate based on the source transport address of + /* Find remote candidate based on the source transport address of * the request. */ for (i=0; ircand_cnt; ++i) { @@ -3344,7 +4057,7 @@ static void handle_incoming_check(pj_ice_sess *ice, void *p; if (ice->rcand_cnt >= PJ_ICE_MAX_CAND) { - LOG4((ice->obj_name, + LOG4((ice->obj_name, "Unable to add new peer reflexive candidate: too many " "candidates already (%d)", PJ_ICE_MAX_CAND)); return; @@ -3361,7 +4074,7 @@ static void handle_incoming_check(pj_ice_sess *ice, rcand->foundation.slen = pj_ansi_snprintf(rcand->foundation.ptr, 36, "f%p", p); - LOG4((ice->obj_name, + LOG4((ice->obj_name, "Added new remote candidate from the request: %s:%d", pj_sockaddr_print(&rcand->addr, raddr, sizeof(raddr), 2), pj_sockaddr_get_port(&rcand->addr))); @@ -3391,12 +4104,12 @@ static void handle_incoming_check(pj_ice_sess *ice, /* Just get candidate with the highest priority and same transport ID * for the specified component ID in the checklist. */ - for (i=0; iclist.count; ++i) { - pj_ice_sess_check *c = &ice->clist.checks[i]; - if (c->lcand->comp_id == rcheck->comp_id && - c->lcand->transport_id == rcheck->transport_id) + for (i=0; ilcand_cnt; ++i) { + pj_ice_sess_cand *lcand_tmp = &ice->lcand[i]; + if (lcand_tmp->comp_id == rcheck->comp_id && + lcand_tmp->transport_id == rcheck->transport_id) { - lcand = c->lcand; + lcand = lcand_tmp; break; } } @@ -3404,17 +4117,17 @@ static void handle_incoming_check(pj_ice_sess *ice, /* Should not happen, but just in case remote is sending a * Binding request for a component which it doesn't have. */ - LOG4((ice->obj_name, + LOG4((ice->obj_name, "Received Binding request but no local candidate is found!")); return; } #endif - /* - * Create candidate pair for this request. + /* + * Create candidate pair for this request. */ - /* + /* * 7.2.1.4. Triggered Checks * * Now that we have local and remote candidate, check if we already @@ -3435,14 +4148,14 @@ static void handle_incoming_check(pj_ice_sess *ice, * generate an immediate retransmit of the Binding Request for the * check in progress. This is to facilitate rapid completion of * ICE when both agents are behind NAT. - * + * * - If the state of that pair is Failed or Succeeded, no triggered * check is sent. */ if (i != ice->clist.count) { pj_ice_sess_check *c = &ice->clist.checks[i]; - /* If USE-CANDIDATE is present, set nominated flag + /* If USE-CANDIDATE is present, set nominated flag * Note: DO NOT overwrite nominated flag if one is already set. */ c->nominated = ((rcheck->use_candidate) || c->nominated); @@ -3483,14 +4196,14 @@ static void handle_incoming_check(pj_ice_sess *ice, unsigned j; /* If this check is nominated, scan the valid_list for the - * same check and update the nominated flag. A controlled + * same check and update the nominated flag. A controlled * agent might have finished the check earlier. */ if (rcheck->use_candidate) { for (j=0; jvalid_list.count; ++j) { pj_ice_sess_check *vc = &ice->valid_list.checks[j]; - if (vc->lcand->transport_id == c->lcand->transport_id && - vc->rcand == c->rcand) + if (vc->lcand->transport_id == c->lcand->transport_id && + vc->rcand == c->rcand) { /* Set nominated flag */ vc->nominated = PJ_TRUE; @@ -3498,8 +4211,8 @@ static void handle_incoming_check(pj_ice_sess *ice, /* Update valid check and nominated check for the component */ update_comp_check(ice, vc->lcand->comp_id, vc); - LOG5((ice->obj_name, "Valid check %s is nominated", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + LOG5((ice->obj_name, "Valid check %s is nominated", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->valid_list, vc))); } } @@ -3599,7 +4312,7 @@ static pj_status_t on_stun_rx_indication(pj_stun_session *sess, "for component %d", sd->comp_id)); } else { LOG4((sd->ice->obj_name, "Received unexpected %s indication " - "for component %d", pj_stun_get_method_name(msg->hdr.type), + "for component %d", pj_stun_get_method_name(msg->hdr.type), sd->comp_id)); } @@ -3621,7 +4334,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice, pj_sockaddr addr; PJ_ASSERT_RETURN(ice && comp_id, PJ_EINVAL); - + /* It is possible that comp_cnt is less than comp_id, when remote * doesn't support all the components that we have. */ @@ -3658,9 +4371,9 @@ PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice, PJ_RACE_ME(5); - status = (*ice->cb.on_tx_pkt)(ice, comp_id, transport_id, - data, data_len, - &addr, + status = (*ice->cb.on_tx_pkt)(ice, comp_id, transport_id, + data, data_len, + &addr, pj_sockaddr_get_len(&addr)); on_return: @@ -3713,7 +4426,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, * packets. We don't need to verify the STUN packet too rigorously, that * will be done by the user. */ - status = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_size, + status = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_size, PJ_STUN_IS_DATAGRAM | PJ_STUN_NO_FINGERPRINT_CHECK); if (status == PJ_SUCCESS) { @@ -3734,12 +4447,10 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, PJ_RACE_ME(5); - (*ice->cb.on_rx_data)(ice, comp_id, transport_id, pkt, pkt_size, + (*ice->cb.on_rx_data)(ice, comp_id, transport_id, pkt, pkt_size, src_addr, src_addr_len); status = PJ_SUCCESS; } return status; } - - diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c index 370ca6f14..b666696ac 100644 --- a/pjnath/src/pjnath/ice_strans.c +++ b/pjnath/src/pjnath/ice_strans.c @@ -68,6 +68,7 @@ enum tp_type # define RELAY_PREF ((1 << PJ_ICE_LOCAL_PREF_BITS) - 1) #endif +#define MAX_RTP_SIZE 65536 /* The candidate type preference when STUN candidate is used */ static pj_uint8_t srflx_pref_table[PJ_ICE_CAND_TYPE_MAX] = @@ -86,9 +87,18 @@ static pj_uint8_t srflx_pref_table[PJ_ICE_CAND_TYPE_MAX] = #endif }; +////////////////////////////////////////////////////////////////////////////// + +static pj_uint16_t GETVAL16H(const pj_uint8_t *buf1, const pj_uint8_t *buf2) +{ + return (pj_uint16_t) ((buf1[0] << 8) | (buf2[0] << 0)); +} + +////////////////////////////////////////////////////////////////////////////// /* ICE callbacks */ static void on_valid_pair(pj_ice_sess *ice); +static void on_ice_destroy(pj_ice_sess *ice); static void on_ice_complete(pj_ice_sess *ice, pj_status_t status); static pj_status_t ice_tx_pkt(pj_ice_sess *ice, unsigned comp_id, @@ -103,6 +113,18 @@ static void ice_rx_data(pj_ice_sess *ice, const pj_sockaddr_t *src_addr, unsigned src_addr_len); +#if PJ_HAS_TCP +static pj_status_t ice_wait_tcp_connection(pj_ice_sess *ice, + unsigned check_id); + +static pj_status_t ice_reconnect_tcp_connection(pj_ice_sess *ice, + unsigned check_id); + +static pj_status_t ice_close_tcp_connection(pj_ice_sess *ice, + unsigned check_id); +static pj_status_t ice_close_remaining_tcp(pj_ice_sess *ice); +#endif + /* STUN socket callbacks */ /* Notification when incoming packet has been received. */ @@ -182,6 +204,16 @@ typedef struct pj_ice_strans_comp } pj_ice_strans_comp; +static pj_bool_t add_local_candidate(pj_ice_sess_cand *cand, + unsigned idx, + unsigned i, + unsigned *cand_cnt, + unsigned *max_cand_cnt, + pj_stun_sock_info stun_sock_info, + pj_ice_strans *ice_st, + pj_ice_strans_comp *comp, + pj_ice_cand_transport transport); + /* Pending send buffer */ typedef struct pending_send { @@ -232,6 +264,12 @@ struct pj_ice_strans signalled end of candidate? */ pj_bool_t loc_cand_end;/**< Trickle ICE: local has signalled end of candidate? */ + pj_uint8_t rtp_pkt[MAX_RTP_SIZE]; + pj_uint8_t rx_buffer[MAX_RTP_SIZE]; + pj_uint16_t rx_buffer_size; + pj_uint16_t rx_wanted_size; + + pj_ssize_t last_data_len; /**< What the application is waiting. */ }; @@ -268,6 +306,7 @@ PJ_DEF(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg) pj_bzero(cfg, sizeof(*cfg)); cfg->af = pj_AF_INET(); + cfg->protocol = PJ_ICE_TP_UDP; pj_stun_config_init(&cfg->stun_cfg, NULL, 0, NULL, NULL); pj_ice_strans_stun_cfg_default(&cfg->stun); pj_ice_strans_turn_cfg_default(&cfg->turn); @@ -285,6 +324,7 @@ PJ_DEF(void) pj_ice_strans_stun_cfg_default(pj_ice_strans_stun_cfg *cfg) pj_bzero(cfg, sizeof(*cfg)); cfg->af = pj_AF_INET(); + cfg->conn_type = PJ_STUN_TP_UDP; cfg->port = PJ_STUN_PORT; cfg->max_host_cands = 64; cfg->ignore_stun_error = PJ_FALSE; @@ -428,6 +468,9 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st, cand->transport_id = tp_id; cand->comp_id = (pj_uint8_t) comp->comp_id; new_cand = PJ_TRUE; + cand->transport = turn_cfg->conn_type == PJ_TURN_TP_UDP ? + PJ_CAND_UDP : + PJ_CAND_TCP_PASSIVE; } /* Allocate and initialize TURN socket data */ @@ -435,6 +478,10 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st, data->comp = comp; data->transport_id = cand->transport_id; + if (turn_cfg->conn_type == PJ_TURN_TP_TCP) { + turn_cfg->alloc_param.peer_conn_type = PJ_TURN_TP_TCP; + } + /* Create the TURN transport */ status = pj_turn_sock_create(&ice_st->cfg.stun_cfg, turn_cfg->af, turn_cfg->conn_type, @@ -476,7 +523,7 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st, return PJ_SUCCESS; } -static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand, +static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand, pj_ice_sess_cand *rcand) { if (lcand == NULL && rcand == NULL){ @@ -485,7 +532,7 @@ static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand, if (lcand == NULL || rcand == NULL){ return PJ_FALSE; } - + if (lcand->type != rcand->type || lcand->status != rcand->status || lcand->comp_id != rcand->comp_id @@ -493,15 +540,145 @@ static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand, // local pref is no longer a constant, so it may be different //|| lcand->local_pref != rcand->local_pref || lcand->prio != rcand->prio + || lcand->transport != rcand->transport || pj_sockaddr_cmp(&lcand->addr, &rcand->addr) != 0 || pj_sockaddr_cmp(&lcand->base_addr, &rcand->base_addr) != 0) { return PJ_FALSE; } - + return PJ_TRUE; } +static pj_status_t add_nat_assisted_cand(pj_ice_strans *ice_st, + pj_ice_strans_comp *comp, + unsigned idx, + unsigned max_cand_cnt) +{ + /* PJNATH library handles host and srflx connections through STUN + * sockets, even if there is no actual STUN server configured (for host + * only candidates). Since NAT-assisted candidates are srflx candidates, + * they will be handled through STUN sockets as well. + * NAT-assisted candidates are provided as a STUN configuration (as an + * entry in the stun_tp list). The position (index) of the config in the + * list is used to calculate the "local preference" of the priority, thus + * it will determine the priority of the NAT-assisted candidates relative + * to other srflx candidates. + */ + + pj_ice_sess_cand *cand; + pj_ice_strans_stun_cfg *nat_cfg = &ice_st->cfg.stun_tp[idx]; + pj_stun_sock_cfg *sock_cfg = &nat_cfg->cfg; + unsigned comp_idx = comp->comp_id - 1; + pj_stun_sock_cb sock_cb; + sock_user_data *data; + pj_status_t status; + + PJ_ASSERT_RETURN(max_cand_cnt > 0, PJ_ETOOSMALL); + PJ_ASSERT_RETURN(nat_cfg->cfg.user_mapping_cnt > comp_idx, PJ_ETOOSMALL); + + pj_sockaddr *laddr = &nat_cfg->cfg.user_mapping[comp_idx].local_addr; + pj_sockaddr *maddr = &nat_cfg->cfg.user_mapping[comp_idx].mapped_addr; + + pj_bzero(&sock_cb, sizeof(sock_cb)); + sock_cb.on_rx_data = &stun_on_rx_data; + sock_cb.on_status = &stun_on_status; + sock_cb.on_data_sent = &stun_on_data_sent; + + /* Override component specific QoS settings, if any */ + if (ice_st->cfg.comp[comp_idx].qos_type) { + sock_cfg->qos_type = ice_st->cfg.comp[comp_idx].qos_type; + } + if (ice_st->cfg.comp[comp_idx].qos_params.flags) { + pj_memcpy(&sock_cfg->qos_params, + &ice_st->cfg.comp[comp_idx].qos_params, + sizeof(sock_cfg->qos_params)); + } + + /* Override component specific socket buffer size settings, if any */ + if (ice_st->cfg.comp[comp_idx].so_rcvbuf_size > 0) { + sock_cfg->so_rcvbuf_size = ice_st->cfg.comp[comp_idx].so_rcvbuf_size; + } + if (ice_st->cfg.comp[comp_idx].so_sndbuf_size > 0) { + sock_cfg->so_sndbuf_size = ice_st->cfg.comp[comp_idx].so_sndbuf_size; + } + + /* Setup srflx candidate*/ + cand = &comp->cand_list[comp->cand_cnt]; + cand->type = PJ_ICE_CAND_TYPE_SRFLX; + /* User candidates are assumed ready */ + cand->status = PJ_SUCCESS; + cand->local_pref = (pj_uint16_t)(SRFLX_PREF - idx); + cand->transport_id = CREATE_TP_ID(TP_STUN, idx); + cand->comp_id = (pj_uint8_t) comp->comp_id; + cand->transport = nat_cfg->cfg.user_mapping[comp_idx].tp_type; + + /* Set the user mappings if availlabe. */ + pj_sockaddr_cp(&sock_cfg->bound_addr, laddr); + + { + char localStr[PJ_INET6_ADDRSTRLEN+8]; + char mappedStr[PJ_INET6_ADDRSTRLEN+8]; + PJ_LOG(5,(ice_st->obj_name, "Setting user mapping %s -> %s [%s (%i)] for comp %u at config index %i", + pj_sockaddr_print(laddr, localStr, sizeof(localStr), 3), + pj_sockaddr_print(maddr, mappedStr, sizeof(mappedStr), 3), + nat_cfg->conn_type == PJ_STUN_TP_UDP?"UDP":"TCP", + nat_cfg->conn_type, + comp->comp_id, idx)); + } + + /* Allocate and initialize STUN socket data */ + data = PJ_POOL_ZALLOC_T(ice_st->pool, sock_user_data); + data->comp = comp; + data->transport_id = cand->transport_id; + + /* Create the STUN transport */ + status = pj_stun_sock_create(&ice_st->cfg.stun_cfg, NULL, + nat_cfg->af, nat_cfg->conn_type, + &sock_cb, sock_cfg, data, + &comp->stun[idx].sock); + if (status != PJ_SUCCESS) + return status; + + /* Update and commit NAT-assisted candidate. */ + pj_sockaddr_cp(&cand->addr, maddr); + pj_sockaddr_cp(&cand->base_addr, laddr); + pj_sockaddr_cp(&cand->rel_addr, &cand->base_addr); + pj_ice_calc_foundation(ice_st->pool, &cand->foundation, + cand->type, &cand->base_addr); + comp->cand_cnt++; + max_cand_cnt--; + + // Check if we already have a matching host candidate for + // this srflx candidate + + /* Find the base for this candidate */ + unsigned j=0; + for (; jcand_cnt; j++) { + pj_ice_sess_cand *host = &comp->cand_list[j]; + + if (host->type != PJ_ICE_CAND_TYPE_HOST) + continue; + + if (pj_sockaddr_cmp(&cand->base_addr, &host->addr) == 0) { + /* Found a matching host cadidate */ + break; + } + } + + /* Add local address as a host candidate if not already present. */ + if (j == comp->cand_cnt && nat_cfg->max_host_cands) { + pj_stun_sock_info stun_sock_info; + pj_memset(&stun_sock_info, 0, sizeof(stun_sock_info)); + stun_sock_info.alias_cnt = 1; + pj_sockaddr_cp(&stun_sock_info.aliases[0], laddr); + unsigned cand_cnt = 0; + status = add_local_candidate(cand, idx, 0, &cand_cnt, &max_cand_cnt, + stun_sock_info, ice_st, comp, cand->transport); + } + + return status; +} static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, pj_ice_strans_comp *comp, @@ -553,6 +730,9 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, cand->local_pref = (pj_uint16_t)(SRFLX_PREF - idx); cand->transport_id = CREATE_TP_ID(TP_STUN, idx); cand->comp_id = (pj_uint8_t) comp->comp_id; + cand->transport = stun_cfg->conn_type == PJ_STUN_TP_UDP ? + PJ_CAND_UDP : + PJ_CAND_TCP_PASSIVE; /* Allocate and initialize STUN socket data */ data = PJ_POOL_ZALLOC_T(ice_st->pool, sock_user_data); @@ -561,7 +741,7 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, /* Create the STUN transport */ status = pj_stun_sock_create(&ice_st->cfg.stun_cfg, NULL, - stun_cfg->af, &stun_sock_cb, + stun_cfg->af, stun_cfg->conn_type, &stun_sock_cb, sock_cfg, data, &comp->stun[idx].sock); if (status != PJ_SUCCESS) return status; @@ -646,106 +826,154 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, return status; } - for (i = 0; i < stun_sock_info.alias_cnt && - cand_cnt < stun_cfg->max_host_cands; ++i) - { - unsigned j; - pj_bool_t cand_duplicate = PJ_FALSE; - char addrinfo[PJ_INET6_ADDRSTRLEN+10]; - const pj_sockaddr *addr = &stun_sock_info.aliases[i]; - - if (max_cand_cnt==0) { - PJ_LOG(4,(ice_st->obj_name, "Too many host candidates")); - break; - } - - /* Ignore loopback addresses if cfg->stun.loop_addr is unset */ - if (stun_cfg->loop_addr==PJ_FALSE) { - if (stun_cfg->af == pj_AF_INET() && - (pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==127) - { - continue; - } - else if (stun_cfg->af == pj_AF_INET6()) { - pj_in6_addr in6addr = {{{0}}}; - in6addr.s6_addr[15] = 1; - if (pj_memcmp(&in6addr, &addr->ipv6.sin6_addr, - sizeof(in6addr))==0) - { - continue; - } - } + for (i = 0; i < stun_sock_info.alias_cnt && + cand_cnt < stun_cfg->max_host_cands && + status == PJ_SUCCESS; ++i) + { + status = !PJ_SUCCESS; + if (stun_sock_info.conn_type == PJ_STUN_TP_UDP) { + status = add_local_candidate(cand, idx, i, + &cand_cnt, &max_cand_cnt, + stun_sock_info, ice_st, comp, + PJ_CAND_UDP); + } else { + status = add_local_candidate(cand, idx, i, + &cand_cnt, &max_cand_cnt, + stun_sock_info, ice_st, comp, + PJ_CAND_TCP_PASSIVE); + /** RFC 6544, Section 4.1: + * First, agents SHOULD obtain host candidates as described in + * Section 5.1. Then, each agent SHOULD "obtain" (allocate a + * placeholder for) an active host candidate for each component of + * each TCP-capable media stream on each interface that the host + * has. The agent does not yet have to actually allocate a port for + * these candidates, but they are used for the creation of the check + * lists. + */ + status = add_local_candidate(cand, idx, i, + &cand_cnt, &max_cand_cnt, + stun_sock_info, ice_st, comp, + PJ_CAND_TCP_ACTIVE); } + } + } - /* Ignore IPv6 link-local address, unless it is the default - * address (first alias). - */ - if (stun_cfg->af == pj_AF_INET6() && i != 0) { - const pj_in6_addr *a = &addr->ipv6.sin6_addr; - if (a->s6_addr[0] == 0xFE && (a->s6_addr[1] & 0xC0) == 0x80) - continue; - } + return status; +} - cand = &comp->cand_list[comp->cand_cnt]; - - cand->type = PJ_ICE_CAND_TYPE_HOST; - cand->status = PJ_SUCCESS; - cand->local_pref = (pj_uint16_t)(HOST_PREF - cand_cnt); - cand->transport_id = CREATE_TP_ID(TP_STUN, idx); - cand->comp_id = (pj_uint8_t) comp->comp_id; - pj_sockaddr_cp(&cand->addr, addr); - pj_sockaddr_cp(&cand->base_addr, addr); - pj_bzero(&cand->rel_addr, sizeof(cand->rel_addr)); - - /* Check if not already in list */ - for (j=0; jcand_cnt; j++) { - if (ice_cand_equals(cand, &comp->cand_list[j])) { - cand_duplicate = PJ_TRUE; - break; - } - } +static pj_bool_t add_local_candidate(pj_ice_sess_cand *cand, + unsigned idx, + unsigned i, + unsigned *cand_cnt, + unsigned *max_cand_cnt, + pj_stun_sock_info stun_sock_info, + pj_ice_strans *ice_st, + pj_ice_strans_comp *comp, + pj_ice_cand_transport transport) +{ + unsigned j; + pj_bool_t cand_duplicate = PJ_FALSE; + char addrinfo[PJ_INET6_ADDRSTRLEN+10]; + const pj_sockaddr *addr = &stun_sock_info.aliases[i]; + pj_ice_strans_stun_cfg *stun_cfg = &ice_st->cfg.stun_tp[idx]; - if (cand_duplicate) { - PJ_LOG(4, (ice_st->obj_name, - "Comp %d: host candidate %s (tpid=%d) is a duplicate", - comp->comp_id, pj_sockaddr_print(&cand->addr, addrinfo, - sizeof(addrinfo), 3), cand->transport_id)); - pj_bzero(&cand->addr, sizeof(cand->addr)); - pj_bzero(&cand->base_addr, sizeof(cand->base_addr)); - continue; - } else { - comp->cand_cnt+=1; - cand_cnt++; - max_cand_cnt--; - } - - pj_ice_calc_foundation(ice_st->pool, &cand->foundation, - cand->type, &cand->base_addr); + if (*max_cand_cnt==0) { + PJ_LOG(4,(ice_st->obj_name, "Too many host candidates")); + return !PJ_SUCCESS; + } - /* Set default candidate with the preferred default - * address family - */ - if (comp->ice_st->cfg.af != pj_AF_UNSPEC() && - addr->addr.sa_family == comp->ice_st->cfg.af && - comp->cand_list[comp->default_cand].base_addr.addr.sa_family != - ice_st->cfg.af) + /* Ignore loopback addresses if cfg->stun.loop_addr is unset */ + if (stun_cfg->loop_addr==PJ_FALSE) { + if (stun_cfg->af == pj_AF_INET() && + (pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==127) + { + return PJ_SUCCESS; + } + else if (stun_cfg->af == pj_AF_INET6()) { + pj_in6_addr in6addr = {0}; + in6addr.s6_addr[15] = 1; + if (pj_memcmp(&in6addr, &addr->ipv6.sin6_addr, + sizeof(in6addr))==0) { - comp->default_cand = (unsigned)(cand - comp->cand_list); + return PJ_SUCCESS; } + } + } - PJ_LOG(4,(ice_st->obj_name, - "Comp %d/%d: host candidate %s (tpid=%d) added", - comp->comp_id, comp->cand_cnt-1, - pj_sockaddr_print(&cand->addr, addrinfo, - sizeof(addrinfo), 3), - cand->transport_id)); + /* Ignore IPv6 link-local address, unless it is the default + * address (first alias). + */ + if (stun_cfg->af == pj_AF_INET6() && i != 0) { + const pj_in6_addr *a = &addr->ipv6.sin6_addr; + if (a->s6_addr[0] == 0xFE && (a->s6_addr[1] & 0xC0) == 0x80) + return PJ_SUCCESS; + } + + cand = &comp->cand_list[comp->cand_cnt]; + + cand->type = PJ_ICE_CAND_TYPE_HOST; + cand->status = PJ_SUCCESS; + cand->local_pref = (pj_uint16_t)(HOST_PREF - *cand_cnt); + cand->transport_id = CREATE_TP_ID(TP_STUN, idx); + cand->comp_id = (pj_uint8_t) comp->comp_id; + cand->transport = transport; + + pj_sockaddr_cp(&cand->addr, addr); + pj_sockaddr_cp(&cand->base_addr, addr); + pj_bzero(&cand->rel_addr, sizeof(cand->rel_addr)); + + /* Check if not already in list */ + for (j=0; jcand_cnt; j++) { + if (ice_cand_equals(cand, &comp->cand_list[j])) { + cand_duplicate = PJ_TRUE; + return !PJ_SUCCESS; } } - return status; -} + if (cand_duplicate) { + PJ_LOG(4, (ice_st->obj_name, + "Comp %d: host candidate %s (tpid=%d) is a duplicate", + comp->comp_id, + pj_sockaddr_print(&cand->addr, + addrinfo, sizeof(addrinfo), 3), + cand->transport_id)); + + pj_bzero(&cand->addr, sizeof(cand->addr)); + pj_bzero(&cand->base_addr, sizeof(cand->base_addr)); + return PJ_SUCCESS; + } else { + comp->cand_cnt+=1; + (*cand_cnt)++; + (*max_cand_cnt)--; + } + pj_ice_calc_foundation(ice_st->pool, &cand->foundation, + cand->type, &cand->base_addr); + + /* Set default candidate with the preferred default + * address family + */ + if (comp->ice_st->cfg.af != pj_AF_UNSPEC() && + addr->addr.sa_family == comp->ice_st->cfg.af && + comp->cand_list[comp->default_cand].base_addr.addr.sa_family != + ice_st->cfg.af) + { + comp->default_cand = (unsigned)(cand - comp->cand_list); + } + if (transport == PJ_CAND_TCP_ACTIVE) { + // Use the port 9 (DISCARD Protocol) for TCP active candidates. + pj_sockaddr_set_port(&cand->addr, 9); + } + + PJ_LOG(4,(ice_st->obj_name, + "Comp %d/%d: host candidate %s (tpid=%d) added", + comp->comp_id, comp->cand_cnt-1, + pj_sockaddr_print(&cand->addr, addrinfo, + sizeof(addrinfo), 3), + cand->transport_id)); + return PJ_SUCCESS; +} /* * Create the component. @@ -776,18 +1004,31 @@ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id) /* Create STUN transport if configured */ for (i=0; icfg.stun_tp_cnt; ++i) { unsigned max_cand_cnt = PJ_ICE_ST_MAX_CAND - comp->cand_cnt - - ice_st->cfg.turn_tp_cnt; + ice_st->cfg.turn_tp_cnt; status = PJ_ETOOSMALL; - if ((max_cand_cnt > 0) && (max_cand_cnt <= PJ_ICE_ST_MAX_CAND)) - status = add_stun_and_host(ice_st, comp, i, max_cand_cnt); - - if (status != PJ_SUCCESS) { - PJ_PERROR(3,(ice_st->obj_name, status, - "Failed creating STUN transport #%d for comp %d", - i, comp->comp_id)); - //return status; + if ((max_cand_cnt > 0) && (max_cand_cnt <= PJ_ICE_ST_MAX_CAND)) { + // Set custom mapping (nat) if provided by the user. + if (ice_st->cfg.stun_tp[i].cfg.user_mapping_cnt > 0) { + status = add_nat_assisted_cand(ice_st, comp, i, max_cand_cnt); + if (status != PJ_SUCCESS) + PJ_PERROR(3,(ice_st->obj_name, status, + "Failed to add NAT-assisted candidate at config #%d for comp %d", + i, comp->comp_id)); + } else { + status = add_stun_and_host(ice_st, comp, i, max_cand_cnt); + if (status != PJ_SUCCESS) + PJ_PERROR(3,(ice_st->obj_name, status, + "Failed creating STUN transport #%d for comp %d", + i, comp->comp_id)); + } + } else { + // All STUN config slots have been used. + if (status != PJ_SUCCESS) + PJ_PERROR(3,(ice_st->obj_name, status, + "Max STUN config (%d) has been reached for comp %d", + PJ_ICE_ST_MAX_CAND, comp->comp_id)); } } @@ -828,10 +1069,10 @@ static pj_status_t alloc_send_buf(pj_ice_strans *ice_st, unsigned buf_size) { if (buf_size > ice_st->buf_size) { unsigned i; - + if (ice_st->is_pending) { /* The current buffer is insufficient, but still currently used.*/ - return PJ_EBUSY; + return PJ_EPENDING; } pj_pool_safe_release(&ice_st->buf_pool); @@ -851,7 +1092,7 @@ static pj_status_t alloc_send_buf(pj_ice_strans *ice_st, unsigned buf_size) } ice_st->buf_idx = ice_st->empty_idx = 0; } - + return PJ_SUCCESS; } @@ -919,7 +1160,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_create( const char *name, /* To maintain backward compatibility, check if old/deprecated setting is set * and the new setting is not, copy the value to the new setting. */ - if (cfg->stun_tp_cnt == 0 && + if (cfg->stun_tp_cnt == 0 && (cfg->stun.server.slen || cfg->stun.max_host_cands)) { ice_st->cfg.stun_tp_cnt = 1; @@ -1135,7 +1376,7 @@ static void sess_init_update(pj_ice_strans *ice_st) pj_ice_get_cand_type_name(cand->type))); return; } - + if (status == PJ_EUNKNOWN) { status = cand->status; } else { @@ -1144,7 +1385,7 @@ static void sess_init_update(pj_ice_strans *ice_st) status = PJ_SUCCESS; } } - + if (status != PJ_SUCCESS) break; } @@ -1288,6 +1529,12 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, ice_cb.on_ice_complete = &on_ice_complete; ice_cb.on_rx_data = &ice_rx_data; ice_cb.on_tx_pkt = &ice_tx_pkt; +#if PJ_HAS_TCP + ice_cb.wait_tcp_connection = &ice_wait_tcp_connection; + ice_cb.reconnect_tcp_connection = &ice_reconnect_tcp_connection; + ice_cb.close_tcp_connection = &ice_close_tcp_connection; + ice_cb.on_ice_destroy = &on_ice_destroy; +#endif /* Release the pool of previous ICE session to avoid memory bloat, * as otherwise it will only be released after ICE strans is destroyed @@ -1372,7 +1619,8 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, &cand->foundation, &cand->addr, &cand->base_addr, &cand->rel_addr, pj_sockaddr_get_len(&cand->addr), - (unsigned*)&ice_cand_id); + (unsigned*)&ice_cand_id, + cand->transport); if (status != PJ_SUCCESS) goto on_error; } @@ -1735,7 +1983,7 @@ pj_ice_strans_get_valid_pair(const pj_ice_strans *ice_st, PJ_DEF(pj_status_t) pj_ice_strans_stop_ice(pj_ice_strans *ice_st) { PJ_ASSERT_RETURN(ice_st, PJ_EINVAL); - + /* Protect with group lock, since this may cause race condition with * pj_ice_strans_sendto2(). * See ticket #1877. @@ -1771,7 +2019,7 @@ static pj_status_t use_buffer( pj_ice_strans *ice_st, status = alloc_send_buf(ice_st, (unsigned)data_len); if (status != PJ_SUCCESS) return status; - + if (ice_st->is_pending && ice_st->empty_idx == ice_st->buf_idx) { /* We don't use buffer or there's no more empty buffer. */ return PJ_EBUSY; @@ -1786,12 +2034,12 @@ static pj_status_t use_buffer( pj_ice_strans *ice_st, pj_sockaddr_cp(&ice_st->send_buf[idx].dst_addr, dst_addr); ice_st->send_buf[idx].dst_addr_len = dst_addr_len; *buffer = ice_st->send_buf[idx].buffer; - + if (ice_st->is_pending) { /* We'll continue later since there's still a pending send. */ return PJ_EPENDING; } - + ice_st->is_pending = PJ_TRUE; ice_st->buf_idx = idx; @@ -1844,6 +2092,8 @@ static pj_status_t send_data(pj_ice_strans *ice_st, } } + def_cand = &comp->cand_list[comp->default_cand]; + pj_bool_t add_header = def_cand->transport != PJ_CAND_UDP; /* If ICE is available, send data with ICE. If ICE nego is not completed * yet, ICE will try to send using any valid candidate pair. For any * failure, it will fallback to sending with the default candidate @@ -1854,16 +2104,35 @@ static pj_status_t send_data(pj_ice_strans *ice_st, */ if (ice_st->ice && ice_st->state <= PJ_ICE_STRANS_STATE_RUNNING) { status = pj_ice_sess_send_data(ice_st->ice, comp_id, buf, data_len); - if (status == PJ_SUCCESS || status == PJ_EPENDING) { - pj_grp_lock_release(ice_st->grp_lock); - goto on_return; - } - } + pj_grp_lock_release(ice_st->grp_lock); + goto on_return; + } pj_grp_lock_release(ice_st->grp_lock); - def_cand = &comp->cand_list[comp->default_cand]; - + /* TCP, add header */ + if (add_header) { + /* + * RFC6544 ICE requires an agent to demultiplex STUN and + * application-layer traffic, since they appear on the same port. This + * demultiplexing is described in [RFC5245] and is done using the magic + * cookie and other fields of the message. Stream-oriented transports + * introduce another wrinkle, since they require a way to frame the + * connection so that the application and STUN packets can be extracted + * in order to differentiate STUN packets from application-layer + * traffic. For this reason, TCP media streams utilizing ICE use the + * basic framing provided in RFC 4571 [RFC4571], even if the application + * layer protocol is not RTP. + */ + pj_uint8_t header_1 = data_len % 256; + pj_uint8_t header_0 = data_len >> 8; + pj_memcpy(&ice_st->rtp_pkt, &(header_0), sizeof(pj_uint8_t)); + pj_memcpy(&ice_st->rtp_pkt[1], &(header_1), sizeof(pj_uint8_t)); + pj_memcpy(&ice_st->rtp_pkt[2], (unsigned char *)data, data_len); + buf = &ice_st->rtp_pkt; + data_len += 2; + } + if (def_cand->status == PJ_SUCCESS) { unsigned tp_idx = GET_TP_IDX(def_cand->transport_id); @@ -1925,6 +2194,10 @@ static pj_status_t send_data(pj_ice_strans *ice_st, status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, buf, (unsigned)data_len, 0, dest_addr, dest_addr_len); + /* Do not count the header */ + if (add_header) { + data_len -= sizeof(pj_uint16_t); + } goto on_return; } @@ -1933,8 +2206,14 @@ static pj_status_t send_data(pj_ice_strans *ice_st, on_return: /* We continue later in on_data_sent() callback. */ - if (status == PJ_EPENDING) - return status; + if (status == PJ_EPENDING) { + ice_st->last_data_len = data_len; + if (add_header) { + // Don't forget the header + ice_st->last_data_len += sizeof(pj_uint16_t); + } + return status; + } if (call_cb) { on_data_sent(ice_st, (status == PJ_SUCCESS? (pj_ssize_t)data_len: -status)); @@ -1966,7 +2245,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st, dst_addr_len, PJ_TRUE, PJ_FALSE); if (status == PJ_EPENDING) status = PJ_SUCCESS; - + return status; } #endif @@ -2026,7 +2305,7 @@ static void on_valid_pair(pj_ice_sess *ice) pj_sockaddr_print(&check->lcand->addr, lip, sizeof(lip), 3); pj_sockaddr_print(&check->rcand->addr, rip, sizeof(rip), 3); - if (tp_typ == TP_TURN) { + if (tp_typ == TP_TURN && check->lcand->transport == PJ_CAND_UDP) { /* Activate channel binding for the remote address * for more efficient data transfer using TURN. */ @@ -2068,6 +2347,15 @@ static void on_valid_pair(pj_ice_sess *ice) pj_grp_lock_dec_ref(ice_st->grp_lock); } +static void on_ice_destroy(pj_ice_sess *ice) +{ + pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data; + + if (ice_st->cb.on_destroy) { + (*ice_st->cb.on_destroy)(ice_st); + } +} + /* * Callback called by ICE session when ICE processing is complete, either * successfully or with failure. @@ -2107,25 +2395,43 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) pj_ice_strans_comp *comp = ice_st->comp[i]; check = pj_ice_strans_get_valid_pair(ice_st, i+1); + + // We nominated a connection, we can close the other ones. + ice_close_remaining_tcp(ice_st->ice); if (check) { char lip[PJ_INET6_ADDRSTRLEN+10]; char rip[PJ_INET6_ADDRSTRLEN+10]; unsigned tp_idx = GET_TP_IDX(check->lcand->transport_id); unsigned tp_typ = GET_TP_TYPE(check->lcand->transport_id); - pj_sockaddr_print(&check->lcand->addr, lip, - sizeof(lip), 3); + pj_sockaddr_print(&check->lcand->addr, lip, + sizeof(lip), 3); pj_sockaddr_print(&check->rcand->addr, rip, - sizeof(rip), 3); - - if (tp_typ == TP_TURN) { + sizeof(rip), 3); +#if PJ_HAS_TCP + int idx = -1; + for (int i=0; icfg.stun_tp_cnt; ++i) { + if (ice_st->cfg.stun_tp[i].af == + check->rcand->addr.addr.sa_family) + { + idx = i; + break; + } + } + if (idx == -1) { + PJ_LOG(4, (ice_st->obj_name, + "Comp %d: No STUN sock found.", + comp->comp_id)); + } +#endif + if (tp_typ == TP_TURN && check->lcand->transport == PJ_CAND_UDP) { /* Activate channel binding for the remote address - * for more efficient data transfer using TURN. - */ + * for more efficient data transfer using TURN. + */ status = pj_turn_sock_bind_channel( - comp->turn[tp_idx].sock, - &check->rcand->addr, - sizeof(check->rcand->addr)); + comp->turn[tp_idx].sock, + &check->rcand->addr, + sizeof(check->rcand->addr)); /* Disable logging for Send/Data indications */ PJ_LOG(5,(ice_st->obj_name, @@ -2211,6 +2517,29 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, pj_sockaddr_get_port(dst_addr), tp_typ)); + /* TCP, add header */ + if (comp->ice_st->cfg.stun_tp->conn_type == PJ_STUN_TP_TCP) { + /* + * RFC6544 ICE requires an agent to demultiplex STUN and + * application-layer traffic, since they appear on the same port. This + * demultiplexing is described in [RFC5245] and is done using the magic + * cookie and other fields of the message. Stream-oriented transports + * introduce another wrinkle, since they require a way to frame the + * connection so that the application and STUN packets can be extracted + * in order to differentiate STUN packets from application-layer + * traffic. For this reason, TCP media streams utilizing ICE use the + * basic framing provided in RFC 4571 [RFC4571], even if the application + * layer protocol is not RTP. + */ + pj_uint8_t header_1 = size % 256; + pj_uint8_t header_0 = size >> 8; + pj_memcpy(&ice_st->rtp_pkt, &(header_0), sizeof(pj_uint8_t)); + pj_memcpy(&ice_st->rtp_pkt[1], &(header_1), sizeof(pj_uint8_t)); + pj_memcpy(&ice_st->rtp_pkt[2], (unsigned char *)pkt, size); + buf = &ice_st->rtp_pkt; + size += 2; + } + if (tp_typ == TP_TURN) { if (comp->turn[tp_idx].sock) { status = pj_turn_sock_sendto(comp->turn[tp_idx].sock, @@ -2233,7 +2562,7 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, if (status != PJ_SUCCESS) { goto on_return; } - + pj_sockaddr_cp(&comp->dst_addr, dst_addr); comp->synth_addr_len = pj_sockaddr_get_len(&comp->synth_addr); } @@ -2244,9 +2573,13 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, dest_addr_len = dst_addr_len; } - status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, - buf, (unsigned)size, 0, - dest_addr, dest_addr_len); + if (comp->stun[tp_idx].sock) { + status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, + buf, (unsigned)size, 0, + dest_addr, dest_addr_len); + } else { + status = PJ_EINVALIDOP; + } } else { pj_assert(!"Invalid transport ID"); status = PJ_EINVALIDOP; @@ -2292,7 +2625,7 @@ static void check_pending_send(pj_ice_strans *ice_st) if (ice_st->num_buf > 0) ice_st->buf_idx = (ice_st->buf_idx + 1) % ice_st->num_buf; - + if (ice_st->num_buf > 0 && ice_st->buf_idx != ice_st->empty_idx) { /* There's some pending send. Send it one by one. */ pending_send *ps = &ice_st->send_buf[ice_st->buf_idx]; @@ -2306,6 +2639,253 @@ static void check_pending_send(pj_ice_strans *ice_st) } } +static void on_peer_connection(pj_stun_session* sess, + pj_status_t status, + pj_sockaddr_t* remote_addr) +{ + + pj_stun_sock *stun_sock; + sock_user_data *data; + pj_ice_strans_comp *comp; + pj_ice_strans *ice_st; + + stun_sock = (pj_stun_sock *)pj_stun_session_get_user_data(sess); + /* We have disassociated ourselves from the STUN session */ + if (!stun_sock) + return; + + data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock); + /* We have disassociated ourselves from the STUN socket */ + if (!data) + return; + + comp = data->comp; + ice_st = comp->ice_st; + + /* Incorrect ICE */ + if (!ice_st || !ice_st->ice) + return; + + pj_grp_lock_add_ref(ice_st->grp_lock); + ice_sess_on_peer_connection(ice_st->ice, + data->transport_id, status, remote_addr); + pj_grp_lock_dec_ref(ice_st->grp_lock); +} + +static void on_peer_reset_connection(pj_stun_session* sess, + pj_sockaddr_t* remote_addr) +{ + pj_stun_sock *stun_sock; + sock_user_data *data; + pj_ice_strans_comp *comp; + pj_ice_strans *ice_st; + + stun_sock = (pj_stun_sock *)pj_stun_session_get_user_data(sess); + /* We have disassociated ourselves from the STUN session */ + if (!stun_sock) + return; + + data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock); + /* We have disassociated ourselves from the STUN socket */ + if (!data) + return; + + comp = data->comp; + ice_st = comp->ice_st; + + /* Incorrect ICE */ + if (!ice_st || !ice_st->ice) + return; + + pj_grp_lock_add_ref(ice_st->grp_lock); + + ice_sess_on_peer_reset_connection(ice_st->ice, + data->transport_id, remote_addr); + pj_grp_lock_dec_ref(ice_st->grp_lock); +} + +static void on_peer_packet(pj_stun_session* sess, pj_sockaddr_t* remote_addr) +{ + + if (!sess || !remote_addr) + return; + + pj_stun_sock *stun_sock; + sock_user_data *data; + pj_ice_strans_comp *comp; + pj_ice_strans *ice_st; + + stun_sock = (pj_stun_sock *)pj_stun_session_get_user_data(sess); + /* We have disassociated ourselves from the STUN session */ + if (!stun_sock) + return; + + data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock); + /* We have disassociated ourselves from the STUN socket */ + if (!data) + return; + + comp = data->comp; + if (!comp) + return; + + ice_st = comp->ice_st; + /* Incorrect ICE */ + if (!ice_st || !ice_st->ice) + return; + + pj_grp_lock_add_ref(ice_st->grp_lock); + ice_sess_on_peer_packet(ice_st->ice, data->transport_id, remote_addr); + pj_grp_lock_dec_ref(ice_st->grp_lock); +} + +#if PJ_HAS_TCP +static pj_status_t ice_wait_tcp_connection(pj_ice_sess *ice, + unsigned check_id) +{ + pj_ice_sess_check *check = &ice->clist.checks[check_id]; + const pj_ice_sess_cand *lcand = check->lcand; + const pj_ice_sess_cand *rcand = check->rcand; + pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; + pj_ice_strans_comp *st_comp = ice_st->comp[lcand->comp_id - 1]; + + int idx = -1; + for (int i=0; icfg.stun_tp_cnt; ++i) + if (ice_st->cfg.stun_tp[i].af == rcand->addr.addr.sa_family) { + idx = i; + break; + } + + if (idx == -1) { + PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN sock found.", + st_comp->comp_id)); + return PJ_EINVAL; + } + if (st_comp->stun[idx].sock) { + pj_stun_session *sess = pj_stun_sock_get_session(st_comp->stun[idx].sock); + if (!sess) { + PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN session.", + st_comp->comp_id)); + return PJ_EINVAL; + } + pj_stun_session_callback(sess)->on_peer_connection = + &on_peer_connection; + pj_stun_session_callback(sess)->on_peer_reset_connection = + &on_peer_reset_connection; + pj_stun_session_callback(sess)->on_peer_packet = &on_peer_packet; + + return pj_stun_sock_connect_active(st_comp->stun[idx].sock, + &rcand->addr, + rcand->addr.addr.sa_family); + } + + return PJ_EINVAL; +} + +static pj_status_t ice_reconnect_tcp_connection(pj_ice_sess *ice, + unsigned check_id) +{ + pj_ice_sess_check *check = &ice->clist.checks[check_id]; + const pj_ice_sess_cand *lcand = check->lcand; + const pj_ice_sess_cand *rcand = check->rcand; + pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; + pj_ice_strans_comp *st_comp = ice_st->comp[lcand->comp_id - 1]; + + int idx = -1; + for (int i=0; icfg.stun_tp_cnt; ++i) + if (ice_st->cfg.stun_tp[i].af == rcand->addr.addr.sa_family) { + idx = i; + break; + } + + if (idx == -1) { + PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN sock found.", + st_comp->comp_id)); + return PJ_EINVAL; + } + + if (st_comp->stun[idx].sock) { + pj_stun_session *sess = pj_stun_sock_get_session(st_comp->stun[idx].sock); + if (!sess) { + PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN session.", + st_comp->comp_id)); + return PJ_EINVAL; + } + pj_stun_session_callback(sess)->on_peer_connection = + &on_peer_connection; + pj_stun_session_callback(sess)->on_peer_reset_connection = + &on_peer_reset_connection; + pj_stun_session_callback(sess)->on_peer_packet = &on_peer_packet; + return pj_stun_sock_reconnect_active(st_comp->stun[idx].sock, + &rcand->addr, + rcand->addr.addr.sa_family); + } + + return PJ_EINVAL; +} + +static pj_status_t ice_close_tcp_connection(pj_ice_sess *ice, + unsigned check_id) +{ + pj_ice_sess_check *check = &ice->clist.checks[check_id]; + const pj_ice_sess_cand *lcand = check->lcand; + const pj_ice_sess_cand *rcand = check->rcand; + pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; + pj_ice_strans_comp *st_comp = ice_st->comp[lcand->comp_id - 1]; + + int idx = -1; + for (int i=0; icfg.stun_tp_cnt; ++i) + if (ice_st->cfg.stun_tp[i].af == rcand->addr.addr.sa_family) { + idx = i; + break; + } + + if (idx != -1 && st_comp->stun[idx].sock) { + const pj_ice_sess_cand *rcand = check->rcand; + return pj_stun_sock_close(st_comp->stun[idx].sock, &rcand->addr); + } + + return PJ_EINVAL; +} + +static pj_status_t ice_close_remaining_tcp(pj_ice_sess *ice) +{ + for (int i = 0; i < ice->comp_cnt; i++) { + pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; + pj_ice_strans_comp *st_comp = ice_st->comp[i]; + + const pj_ice_sess_check *valid_check = pj_ice_strans_get_valid_pair(ice_st, i + 1); + + if (!valid_check) { + continue; + } + + if (valid_check->lcand->type != PJ_ICE_CAND_TYPE_RELAYED + && valid_check->rcand->type != PJ_ICE_CAND_TYPE_RELAYED) { + // If we're not a turn session we can close it. + for (int j = 0; j < ice_st->cfg.turn_tp_cnt; ++j) { + if (st_comp->turn[j].sock) { + pj_turn_sock_destroy(st_comp->turn[j].sock); + st_comp->turn[j].sock = NULL; + } + } + } + for (int j=0; j< ice_st->cfg.stun_tp_cnt; ++j) { + if (st_comp->stun[j].sock) { + pj_stun_sock_close_all_except(st_comp->stun[j].sock, &valid_check->rcand->addr); + } + if (ice_st->cfg.stun_tp[j].af != valid_check->rcand->addr.addr.sa_family) { + // If the valid candidate got the other address family we can close. + pj_stun_sock_destroy(st_comp->stun[j].sock); + } + } + } + + return PJ_SUCCESS; +} + +#endif + /* Notifification when asynchronous send operation via STUN/TURN * has completed. */ @@ -2314,7 +2894,8 @@ static pj_bool_t on_data_sent(pj_ice_strans *ice_st, pj_ssize_t sent) if (ice_st->destroy_req || !ice_st->is_pending) return PJ_TRUE; - if (ice_st->call_send_cb && ice_st->cb.on_data_sent) { + if (ice_st->call_send_cb && ice_st->cb.on_data_sent + && sent == ice_st->last_data_len /* Only app data should be announced */) { (*ice_st->cb.on_data_sent)(ice_st, sent); } @@ -2472,7 +3053,7 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, { /* We get an IPv4 mapped address for our IPv6 * host address. - */ + */ comp->ipv4_mapped = PJ_TRUE; /* Find other host candidates with the same (IPv6) @@ -2484,7 +3065,7 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, if (comp->cand_list[i].type != PJ_ICE_CAND_TYPE_HOST) continue; - + a1 = &comp->cand_list[i].addr; a2 = &cand->base_addr; if (pj_memcmp(pj_sockaddr_get_addr(a1), @@ -2501,7 +3082,7 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, pj_sockaddr_cp(&cand->base_addr, &info.mapped_addr); pj_sockaddr_cp(&cand->rel_addr, &info.mapped_addr); } - + /* Eliminate the srflx candidate if the address is * equal to other (host) candidates. */ @@ -2551,7 +3132,8 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, &cand->base_addr, &cand->rel_addr, pj_sockaddr_get_len(&cand->addr), - NULL); + NULL, + cand->transport); } } @@ -2576,7 +3158,7 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, if (op == PJ_STUN_SOCK_MAPPED_ADDR_CHANGE && ice_st->cb.on_ice_complete) { - (*ice_st->cb.on_ice_complete)(ice_st, + (*ice_st->cb.on_ice_complete)(ice_st, PJ_ICE_STRANS_OP_ADDR_CHANGE, status); } @@ -2632,6 +3214,10 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, } } break; + case PJ_STUN_SESS_DESTROYED: + case PJ_STUN_TCP_CONNECT_ERROR: + default: + break; } return pj_grp_lock_dec_ref(ice_st->grp_lock)? PJ_FALSE : PJ_TRUE; @@ -2671,16 +3257,105 @@ static void turn_on_rx_data(pj_turn_sock *turn_sock, } else { - /* Hand over the packet to ICE */ - status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, - data->transport_id, pkt, pkt_len, - peer_addr, addr_len); - - if (status != PJ_SUCCESS) { - ice_st_perror(comp->ice_st, - "Error processing packet from TURN relay", - status); - } + /* Hand over the packet to ICE */ + if (comp->ice_st->cfg.turn_tp->conn_type == PJ_TURN_TP_TCP && pkt_len > 0) { + unsigned parsed = 0; + pj_status_t status; + + do { + pj_uint16_t leftover = pkt_len - parsed; + pj_uint8_t *current_packet = ((pj_uint8_t *)(pkt)) + parsed; + + /** + * RFC6544, the packet is wrapped into a packet following the + * RFC4571 + */ + pj_bool_t store_remaining = PJ_TRUE; + if (comp->ice_st->rx_buffer_size || + comp->ice_st->rx_wanted_size) + { + /* a single packet left to process */ + if (comp->ice_st->rx_buffer_size == 1 && comp->ice_st->rx_wanted_size == 0) { + /* get last frame's lenght from its header */ + leftover = GETVAL16H(comp->ice_st->rx_buffer, + current_packet); + /* adjust counters accordingly */ + comp->ice_st->rx_buffer_size = 0; + current_packet++; + parsed++; + + if (leftover + parsed <= pkt_len) { + /* we didn't get what we were promissed in the + * header. furthermore, this was the last frame and + * therefore we're done. + */ + store_remaining = PJ_FALSE; + parsed += leftover; + } else { + comp->ice_st->rx_wanted_size = leftover; + } + } else if (leftover + comp->ice_st->rx_buffer_size >= + comp->ice_st->rx_wanted_size) + { + /* We have enough leftover bytes in buffer to build a new + * packet and parse it + */ + store_remaining = PJ_FALSE; + + pj_uint16_t eaten_bytes = comp->ice_st->rx_wanted_size - + comp->ice_st->rx_buffer_size; + pj_memcpy(comp->ice_st->rx_buffer + + comp->ice_st->rx_buffer_size, + current_packet, eaten_bytes); + + leftover = comp->ice_st->rx_wanted_size; + current_packet = comp->ice_st->rx_buffer; + parsed += eaten_bytes; + + comp->ice_st->rx_buffer_size = 0; + comp->ice_st->rx_wanted_size = 0; + } + } else if (leftover > 1) { + leftover = GETVAL16H(current_packet, current_packet+1); + current_packet += 2; + parsed += 2; + if (leftover + parsed <= pkt_len) { + store_remaining = PJ_FALSE; + parsed += leftover; + } else { + comp->ice_st->rx_wanted_size = leftover; + } + } + + if (store_remaining) { + leftover = pkt_len - parsed; + pj_memcpy(comp->ice_st->rx_buffer + + comp->ice_st->rx_buffer_size, + current_packet, leftover); + comp->ice_st->rx_buffer_size += leftover; + status = PJ_SUCCESS; + break; + } + + status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, + data->transport_id, + current_packet, leftover, + peer_addr, addr_len); + if (status != PJ_SUCCESS) { + ice_st_perror(comp->ice_st, + "Error processing packet from TURN relay", + status); + } + } while (parsed < pkt_len); + } else { + status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, + data->transport_id, pkt, pkt_len, + peer_addr, addr_len); + if (status != PJ_SUCCESS) + ice_st_perror(comp->ice_st, + "Error processing packet from TURN relay", + status); + } } pj_grp_lock_dec_ref(comp->ice_st->grp_lock); @@ -2816,10 +3491,11 @@ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state, cand->local_pref, &cand->foundation, &cand->addr, - &cand->base_addr, + &cand->base_addr, &cand->rel_addr, pj_sockaddr_get_len(&cand->addr), - NULL); + NULL, + cand->transport); if (status != PJ_SUCCESS) { PJ_PERROR(4,(comp->ice_st->obj_name, status, "Comp %d/%d: failed to add TURN (tpid=%d) to ICE", @@ -2953,4 +3629,3 @@ on_return: pj_log_pop_indent(); } - diff --git a/pjnath/src/pjnath/nat_detect.c b/pjnath/src/pjnath/nat_detect.c index cf94c4e44..cb35770cd 100644 --- a/pjnath/src/pjnath/nat_detect.c +++ b/pjnath/src/pjnath/nat_detect.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include @@ -109,8 +109,8 @@ typedef struct nat_detect_session } nat_detect_session; -static void on_read_complete(pj_ioqueue_key_t *key, - pj_ioqueue_op_key_t *op_key, +static void on_read_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read); static void on_request_complete(pj_stun_session *sess, pj_status_t status, @@ -201,7 +201,7 @@ static pj_status_t get_local_interface(const pj_sockaddr *server, } pj_sockaddr_cp(local_addr, &tmp); - + pj_sock_close(sock); return PJ_SUCCESS; } @@ -241,7 +241,7 @@ PJ_DEF(pj_status_t) pj_stun_detect_nat_type2(const pj_sockaddr *server, /* * Init NAT detection session. */ - pool = pj_pool_create(stun_cfg->pf, "natck%p", PJNATH_POOL_LEN_NATCK, + pool = pj_pool_create(stun_cfg->pf, "natck%p", PJNATH_POOL_LEN_NATCK, PJNATH_POOL_INC_NATCK, NULL); if (!pool) return PJ_ENOMEM; @@ -317,7 +317,7 @@ PJ_DEF(pj_status_t) pj_stun_detect_nat_type2(const pj_sockaddr *server, pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb)); ioqueue_cb.on_read_complete = &on_read_complete; - status = pj_ioqueue_register_sock2(sess->pool, stun_cfg->ioqueue, + status = pj_ioqueue_register_sock2(sess->pool, stun_cfg->ioqueue, sess->sock, sess->grp_lock, sess, &ioqueue_cb, &sess->key); if (status != PJ_SUCCESS) @@ -330,7 +330,7 @@ PJ_DEF(pj_status_t) pj_stun_detect_nat_type2(const pj_sockaddr *server, sess_cb.on_request_complete = &on_request_complete; sess_cb.on_send_msg = &on_send_msg; status = pj_stun_session_create(stun_cfg, pool->obj_name, &sess_cb, - PJ_FALSE, sess->grp_lock, &sess->stun_sess); + PJ_FALSE, sess->grp_lock, &sess->stun_sess, PJ_STUN_TP_UDP); if (status != PJ_SUCCESS) goto on_error; @@ -359,7 +359,7 @@ on_error: static void sess_destroy(nat_detect_session *sess) { - if (sess->stun_sess) { + if (sess->stun_sess) { pj_stun_session_destroy(sess->stun_sess); sess->stun_sess = NULL; } @@ -422,8 +422,8 @@ static void end_session(nat_detect_session *sess, /* * Callback upon receiving packet from network. */ -static void on_read_complete(pj_ioqueue_key_t *key, - pj_ioqueue_op_key_t *op_key, +static void on_read_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { nat_detect_session *sess; @@ -440,19 +440,19 @@ static void on_read_complete(pj_ioqueue_key_t *key, if (bytes_read < 0) { if (-bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) && - -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && - -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) + -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && + -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) { /* Permanent error */ - end_session(sess, (pj_status_t)-bytes_read, + end_session(sess, (pj_status_t)-bytes_read, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); goto on_return; } } else if (bytes_read > 0) { pj_stun_session_on_rx_pkt(sess->stun_sess, sess->rx_pkt, bytes_read, - PJ_STUN_IS_DATAGRAM|PJ_STUN_CHECK_PACKET, - NULL, NULL, + PJ_STUN_IS_DATAGRAM|PJ_STUN_CHECK_PACKET, + NULL, NULL, &sess->src_addr, sess->src_addr_len); } @@ -460,7 +460,7 @@ static void on_read_complete(pj_ioqueue_key_t *key, sess->rx_pkt_len = sizeof(sess->rx_pkt); sess->src_addr_len = sizeof(sess->src_addr); status = pj_ioqueue_recvfrom(key, op_key, sess->rx_pkt, &sess->rx_pkt_len, - PJ_IOQUEUE_ALWAYS_ASYNC, + PJ_IOQUEUE_ALWAYS_ASYNC, &sess->src_addr, &sess->src_addr_len); if (status != PJ_EPENDING) { @@ -595,11 +595,11 @@ static void on_request_complete(pj_stun_session *stun_sess, /* Send Test 1B only when Test 2 completes. Must not send Test 1B * before Test 2 completes to avoid creating mapping on the NAT. */ - if (!sess->result[ST_TEST_1B].executed && + if (!sess->result[ST_TEST_1B].executed && sess->result[ST_TEST_2].complete && sess->result[ST_TEST_2].status != PJ_SUCCESS && sess->result[ST_TEST_1].complete && - sess->result[ST_TEST_1].status == PJ_SUCCESS) + sess->result[ST_TEST_1].status == PJ_SUCCESS) { cmp = pj_sockaddr_cmp(&sess->local_addr, &sess->result[ST_TEST_1].ma); if (cmp != 0) @@ -661,7 +661,7 @@ static void on_request_complete(pj_stun_session *stun_sess, switch (sess->result[ST_TEST_1].status) { case PJNATH_ESTUNTIMEDOUT: /* - * Test 1 has timed-out. Conclude with NAT_TYPE_BLOCKED. + * Test 1 has timed-out. Conclude with NAT_TYPE_BLOCKED. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_BLOCKED); break; @@ -694,7 +694,7 @@ static void on_request_complete(pj_stun_session *stun_sess, /* * We've got other error with Test 2. */ - end_session(sess, sess->result[ST_TEST_2].status, + end_session(sess, sess->result[ST_TEST_2].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } @@ -774,14 +774,14 @@ static void on_request_complete(pj_stun_session *stun_sess, * It could be that port 3489 is blocked, while the * NAT itself looks to be a Restricted one. */ - end_session(sess, PJ_SUCCESS, + end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_RESTRICTED); break; default: /* Can't distinguish between Symmetric and Port * Restricted, so set the type to Unknown */ - end_session(sess, PJ_SUCCESS, + end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } @@ -799,7 +799,7 @@ static void on_request_complete(pj_stun_session *stun_sess, /* * We've got other error with Test 2. */ - end_session(sess, sess->result[ST_TEST_2].status, + end_session(sess, sess->result[ST_TEST_2].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } @@ -809,7 +809,7 @@ static void on_request_complete(pj_stun_session *stun_sess, /* * We've got other error with Test 1. */ - end_session(sess, sess->result[ST_TEST_1].status, + end_session(sess, sess->result[ST_TEST_1].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } @@ -841,15 +841,15 @@ static pj_status_t send_test(nat_detect_session *sess, tsx_id[2] = test_id; /* Create BIND request */ - status = pj_stun_session_create_req(sess->stun_sess, + status = pj_stun_session_create_req(sess->stun_sess, PJ_STUN_BINDING_REQUEST, magic, - (pj_uint8_t*)tsx_id, + (pj_uint8_t*)tsx_id, &sess->result[test_id].tdata); if (status != PJ_SUCCESS) goto on_error; /* Add CHANGE-REQUEST attribute */ - status = pj_stun_msg_add_uint_attr(sess->pool, + status = pj_stun_msg_add_uint_attr(sess->pool, sess->result[test_id].tdata->msg, PJ_STUN_ATTR_CHANGE_REQUEST, change_flag); @@ -868,15 +868,16 @@ static pj_status_t send_test(nat_detect_session *sess, sess->cur_server = &sess->server; } - PJ_LOG(5,(sess->pool->obj_name, - "Performing %s to %s:%d", + PJ_LOG(5,(sess->pool->obj_name, + "Performing %s to %s:%d", test_names[test_id], pj_sockaddr_print(sess->cur_server, addr, sizeof(addr), 2), pj_sockaddr_get_port(sess->cur_server))); /* Send the request */ status = pj_stun_session_send_msg(sess->stun_sess, NULL, PJ_TRUE, - PJ_TRUE, sess->cur_server, + (pj_stun_session_tp_type(sess->stun_sess) == PJ_STUN_TP_UDP), + sess->cur_server, pj_sockaddr_get_len(sess->cur_server), sess->result[test_id].tdata); if (status != PJ_SUCCESS) diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c index 4a3e165f5..e117fef39 100644 --- a/pjnath/src/pjnath/stun_session.c +++ b/pjnath/src/pjnath/stun_session.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include @@ -48,6 +48,8 @@ struct pj_stun_session pj_stun_tx_data pending_request_list; pj_stun_tx_data cached_response_list; + + pj_stun_tp_type conn_type; }; #define SNAME(s_) ((s_)->pool->obj_name) @@ -66,7 +68,7 @@ struct pj_stun_session static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, - pj_status_t status, + pj_status_t status, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len); @@ -77,7 +79,7 @@ static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx); static void stun_sess_on_destroy(void *comp); static void destroy_tdata(pj_stun_tx_data *tdata, pj_bool_t force); -static pj_stun_tsx_cb tsx_cb = +static pj_stun_tsx_cb tsx_cb = { &stun_tsx_on_complete, &stun_tsx_on_send_msg, @@ -109,7 +111,7 @@ static pj_stun_tx_data* tsx_lookup(pj_stun_session *sess, while (tdata != &sess->pending_request_list) { pj_assert(sizeof(tdata->msg_key)==sizeof(msg->hdr.tsx_id)); if (tdata->msg_magic == msg->hdr.magic && - pj_memcmp(tdata->msg_key, msg->hdr.tsx_id, + pj_memcmp(tdata->msg_key, msg->hdr.tsx_id, sizeof(msg->hdr.tsx_id))==0) { return tdata; @@ -127,7 +129,7 @@ static pj_status_t create_tdata(pj_stun_session *sess, pj_stun_tx_data *tdata; /* Create pool and initialize basic tdata attributes */ - pool = pj_pool_create(sess->cfg->pf, "tdata%p", + pool = pj_pool_create(sess->cfg->pf, "tdata%p", TDATA_POOL_SIZE, TDATA_POOL_INC, NULL); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); @@ -150,7 +152,7 @@ static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx) pj_stun_client_tsx_stop(tsx); if (tdata) { pj_stun_session *sess = tdata->sess; - + pj_grp_lock_acquire(sess->grp_lock); tsx_erase(sess, tdata); destroy_tdata(tdata, PJ_TRUE); @@ -268,16 +270,16 @@ static pj_status_t apply_msg_options(pj_stun_session *sess, pj_str_t realm, username, nonce, auth_key; /* If the agent is sending a request, it SHOULD add a SOFTWARE attribute - * to the request. The server SHOULD include a SOFTWARE attribute in all + * to the request. The server SHOULD include a SOFTWARE attribute in all * responses. * * If magic value is not PJ_STUN_MAGIC, only apply the attribute for * responses. */ - if (sess->srv_name.slen && + if (sess->srv_name.slen && pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_SOFTWARE, 0)==NULL && (PJ_STUN_IS_RESPONSE(msg->hdr.type) || - (PJ_STUN_IS_REQUEST(msg->hdr.type) && msg->hdr.magic==PJ_STUN_MAGIC))) + (PJ_STUN_IS_REQUEST(msg->hdr.type) && msg->hdr.magic==PJ_STUN_MAGIC))) { pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE, &sess->srv_name); @@ -309,9 +311,9 @@ static pj_status_t apply_msg_options(pj_stun_session *sess, } /* Add NONCE when desired */ - if (nonce.slen && + if (nonce.slen && (PJ_STUN_IS_REQUEST(msg->hdr.type) || - PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))) + PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))) { status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_NONCE, @@ -328,7 +330,7 @@ static pj_status_t apply_msg_options(pj_stun_session *sess, /* Add FINGERPRINT attribute if necessary */ if (sess->use_fingerprint) { - status = pj_stun_msg_add_uint_attr(pool, msg, + status = pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } @@ -352,7 +354,7 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, if (sess->auth_type != PJ_STUN_AUTH_LONG_TERM) return PJ_SUCCESS; - + if (!PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type)) { sess->auth_retry = 0; return PJ_SUCCESS; @@ -367,7 +369,7 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, return PJNATH_EINSTUNMSG; } - if (ea->err_code == PJ_STUN_SC_UNAUTHORIZED || + if (ea->err_code == PJ_STUN_SC_UNAUTHORIZED || ea->err_code == PJ_STUN_SC_STALE_NONCE) { const pj_stun_nonce_attr *anonce; @@ -433,7 +435,7 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, continue; } - tdata->msg->attr[tdata->msg->attr_count++] = + tdata->msg->attr[tdata->msg->attr_count++] = pj_stun_attr_clone(tdata->pool, asrc); } @@ -445,8 +447,8 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, PJ_LOG(4,(SNAME(sess), "Retrying request with new authentication")); /* Retry the request */ - status = pj_stun_session_send_msg(sess, request->token, PJ_TRUE, - request->retransmit, src_addr, + status = pj_stun_session_send_msg(sess, request->token, PJ_TRUE, + request->retransmit, src_addr, src_addr_len, tdata); } else { @@ -457,7 +459,7 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, } static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, - pj_status_t status, + pj_status_t status, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) @@ -482,12 +484,12 @@ static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, src_addr_len, ¬ify_user); if (notify_user && sess->cb.on_request_complete) { - (*sess->cb.on_request_complete)(sess, status, tdata->token, tdata, + (*sess->cb.on_request_complete)(sess, status, tdata->token, tdata, response, src_addr, src_addr_len); } /* Destroy the transmit data. This will remove the transaction - * from the pending list too. + * from the pending list too. */ if (status == PJNATH_ESTUNTIMEDOUT) destroy_tdata(tdata, PJ_TRUE); @@ -514,15 +516,15 @@ static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx, /* Lock the session and prevent user from destroying us in the callback */ pj_grp_lock_acquire(sess->grp_lock); - + if (sess->is_destroying) { /* Stray timer */ pj_grp_lock_release(sess->grp_lock); return PJ_EINVALIDOP; } - status = sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt, - pkt_size, tdata->dst_addr, + status = sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt, + pkt_size, tdata->dst_addr, tdata->addr_len); if (pj_grp_lock_release(sess->grp_lock)) return PJ_EGONE; @@ -537,7 +539,8 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg, const pj_stun_session_cb *cb, pj_bool_t fingerprint, pj_grp_lock_t *grp_lock, - pj_stun_session **p_sess) + pj_stun_session **p_sess, + pj_stun_tp_type conn_type) { pj_pool_t *pool; pj_stun_session *sess; @@ -548,7 +551,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg, if (name==NULL) name = "stuse%p"; - pool = pj_pool_create(cfg->pf, name, PJNATH_POOL_LEN_STUN_SESS, + pool = pj_pool_create(cfg->pf, name, PJNATH_POOL_LEN_STUN_SESS, PJNATH_POOL_INC_STUN_SESS, NULL); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); @@ -558,6 +561,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg, pj_memcpy(&sess->cb, cb, sizeof(*cb)); sess->use_fingerprint = fingerprint; sess->log_flag = 0xFFFF; + sess->conn_type = conn_type; if (grp_lock) { sess->grp_lock = grp_lock; @@ -727,7 +731,7 @@ static pj_status_t get_auth(pj_stun_session *sess, tdata->auth_info.username = sess->cred.data.static_cred.username; tdata->auth_info.nonce = sess->cred.data.static_cred.nonce; - pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key, + pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key, &tdata->auth_info.realm, &tdata->auth_info.username, sess->cred.data.static_cred.data_type, @@ -739,16 +743,16 @@ static pj_status_t get_auth(pj_stun_session *sess, pj_stun_passwd_type data_type = PJ_STUN_PASSWD_PLAIN; pj_status_t rc; - rc = (*sess->cred.data.dyn_cred.get_cred)(tdata->msg, user_data, + rc = (*sess->cred.data.dyn_cred.get_cred)(tdata->msg, user_data, tdata->pool, - &tdata->auth_info.realm, + &tdata->auth_info.realm, &tdata->auth_info.username, - &tdata->auth_info.nonce, + &tdata->auth_info.nonce, &data_type, &password); if (rc != PJ_SUCCESS) return rc; - pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key, + pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key, &tdata->auth_info.realm, &tdata->auth_info.username, data_type, &password); @@ -782,7 +786,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, goto on_error; /* Create STUN message */ - status = pj_stun_msg_create(tdata->pool, method, magic, + status = pj_stun_msg_create(tdata->pool, method, magic, tsx_id, &tdata->msg); if (status != PJ_SUCCESS) goto on_error; @@ -793,7 +797,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, pj_memcpy(tdata->msg_key, tdata->msg->hdr.tsx_id, sizeof(tdata->msg->hdr.tsx_id)); - + /* Get authentication information for the request */ if (sess->auth_type == PJ_STUN_AUTH_NONE) { /* No authentication */ @@ -856,7 +860,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess, /* Create STUN message */ msg_type |= PJ_STUN_INDICATION_BIT; - status = pj_stun_msg_create(tdata->pool, msg_type, PJ_STUN_MAGIC, + status = pj_stun_msg_create(tdata->pool, msg_type, PJ_STUN_MAGIC, NULL, &tdata->msg); if (status != PJ_SUCCESS) { pj_pool_safe_release(&tdata->pool); @@ -895,7 +899,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess, } /* Create STUN response message */ - status = pj_stun_msg_create_response(tdata->pool, rdata->msg, + status = pj_stun_msg_create_response(tdata->pool, rdata->msg, err_code, err_msg, &tdata->msg); if (status != PJ_SUCCESS) { pj_pool_safe_release(&tdata->pool); @@ -906,7 +910,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess, /* copy the request's transaction ID as the transaction key. */ pj_assert(sizeof(tdata->msg_key)==sizeof(rdata->msg->hdr.tsx_id)); tdata->msg_magic = rdata->msg->hdr.magic; - pj_memcpy(tdata->msg_key, rdata->msg->hdr.tsx_id, + pj_memcpy(tdata->msg_key, rdata->msg->hdr.tsx_id, sizeof(rdata->msg->hdr.tsx_id)); /* copy the credential found in the request */ @@ -925,8 +929,8 @@ static void dump_tx_msg(pj_stun_session *sess, const pj_stun_msg *msg, unsigned pkt_size, const pj_sockaddr_t *addr) { char dst_name[PJ_INET6_ADDRSTRLEN+10]; - - if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && + + if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && (sess->log_flag & PJ_STUN_SESS_LOG_TX_REQ)==0) || (PJ_STUN_IS_RESPONSE(msg->hdr.type) && (sess->log_flag & PJ_STUN_SESS_LOG_TX_RES)==0) || @@ -938,13 +942,13 @@ static void dump_tx_msg(pj_stun_session *sess, const pj_stun_msg *msg, pj_sockaddr_print(addr, dst_name, sizeof(dst_name), 3); - PJ_LOG(5,(SNAME(sess), + PJ_LOG(5,(SNAME(sess), "TX %d bytes STUN message to %s:\n" "--- begin STUN message ---\n" "%s" "--- end of STUN message ---\n", - pkt_size, dst_name, - pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf), + pkt_size, dst_name, + pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf), NULL))); } @@ -979,7 +983,7 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, tdata->retransmit = retransmit; /* Apply options */ - status = apply_msg_options(sess, tdata->pool, &tdata->auth_info, + status = apply_msg_options(sess, tdata->pool, &tdata->auth_info, tdata->msg); if (status != PJ_SUCCESS) { pj_stun_msg_destroy_tdata(sess, tdata); @@ -988,8 +992,8 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, } /* Encode message */ - status = pj_stun_msg_encode(tdata->msg, (pj_uint8_t*)tdata->pkt, - tdata->max_len, 0, + status = pj_stun_msg_encode(tdata->msg, (pj_uint8_t*)tdata->pkt, + tdata->max_len, 0, &tdata->auth_info.auth_key, &tdata->pkt_size); if (status != PJ_SUCCESS) { @@ -1019,11 +1023,11 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, /* Send the request! */ status = pj_stun_client_tsx_send_msg(tdata->client_tsx, retransmit, - tdata->pkt, + tdata->pkt, (unsigned)tdata->pkt_size); if (status != PJ_SUCCESS && status != PJ_EPENDING) { pj_stun_msg_destroy_tdata(sess, tdata); - LOG_ERR_(sess, "Error sending STUN request", status); + LOG_ERR_(sess, "Error sending STUN request (pj_stun_client_tsx_send_msg", status); goto on_return; } @@ -1032,9 +1036,9 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, } else { /* Otherwise for non-request message, send directly to transport. */ - if (cache_res && + if (cache_res && (PJ_STUN_IS_SUCCESS_RESPONSE(tdata->msg->hdr.type) || - PJ_STUN_IS_ERROR_RESPONSE(tdata->msg->hdr.type))) + PJ_STUN_IS_ERROR_RESPONSE(tdata->msg->hdr.type))) { /* Requested to keep the response in the cache */ pj_time_val timeout; @@ -1053,7 +1057,7 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, * is still valid when cache timeout callback is called. */ pj_grp_lock_add_ref(sess->grp_lock); - + pj_memset(&tdata->res_timer, 0, sizeof(tdata->res_timer)); pj_timer_entry_init(&tdata->res_timer, PJ_FALSE, tdata, &on_cache_timeout); @@ -1075,12 +1079,12 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, } /* Send to transport directly. */ - status = sess->cb.on_send_msg(sess, token, tdata->pkt, + status = sess->cb.on_send_msg(sess, token, tdata->pkt, tdata->pkt_size, server, addr_len); if (status != PJ_SUCCESS && status != PJ_EPENDING) { pj_stun_msg_destroy_tdata(sess, tdata); - LOG_ERR_(sess, "Error sending STUN request", status); + LOG_ERR_(sess, "Error sending STUN request (pj_stun_session_send_msg)", status); goto on_return; } @@ -1103,13 +1107,13 @@ on_return: /* * Create and send STUN response message. */ -PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess, +PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess, const pj_stun_rx_data *rdata, - unsigned code, + unsigned code, const char *errmsg, void *token, - pj_bool_t cache, - const pj_sockaddr_t *dst_addr, + pj_bool_t cache, + const pj_sockaddr_t *dst_addr, unsigned addr_len) { pj_status_t status; @@ -1122,8 +1126,8 @@ PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess, return PJ_EINVALIDOP; } - status = pj_stun_session_create_res(sess, rdata, code, - (errmsg?pj_cstr(&reason,errmsg):NULL), + status = pj_stun_session_create_res(sess, rdata, code, + (errmsg?pj_cstr(&reason,errmsg):NULL), &tdata); if (status != PJ_SUCCESS) { pj_grp_lock_release(sess->grp_lock); @@ -1139,7 +1143,7 @@ PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess, /* - * Cancel outgoing STUN transaction. + * Cancel outgoing STUN transaction. */ PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess, pj_stun_tx_data *tdata, @@ -1158,7 +1162,7 @@ PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess, } if (notify) { - (sess->cb.on_request_complete)(sess, notify_status, tdata->token, + (sess->cb.on_request_complete)(sess, notify_status, tdata->token, tdata, NULL, NULL, 0); } @@ -1220,7 +1224,7 @@ static pj_status_t send_response(pj_stun_session *sess, void *token, out_pkt = (pj_uint8_t*) pj_pool_alloc(pool, out_max_len); /* Encode */ - status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0, + status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0, &auth_info->auth_key, &out_len); if (status != PJ_SUCCESS) { LOG_ERR_(sess, "Error encoding message", status); @@ -1231,7 +1235,7 @@ static pj_status_t send_response(pj_stun_session *sess, void *token, dump_tx_msg(sess, response, (unsigned)out_len, addr); /* Send packet */ - status = sess->cb.on_send_msg(sess, token, out_pkt, (unsigned)out_len, + status = sess->cb.on_send_msg(sess, token, out_pkt, (unsigned)out_len, addr, addr_len); return status; @@ -1250,18 +1254,18 @@ static pj_status_t authenticate_req(pj_stun_session *sess, pj_stun_msg *response; pj_status_t status; - if (PJ_STUN_IS_ERROR_RESPONSE(rdata->msg->hdr.type) || + if (PJ_STUN_IS_ERROR_RESPONSE(rdata->msg->hdr.type) || sess->auth_type == PJ_STUN_AUTH_NONE) { return PJ_SUCCESS; } - status = pj_stun_authenticate_request(pkt, pkt_len, rdata->msg, + status = pj_stun_authenticate_request(pkt, pkt_len, rdata->msg, &sess->cred, tmp_pool, &rdata->info, &response); if (status != PJ_SUCCESS && response != NULL) { PJ_PERROR(5,(SNAME(sess), status, "Message authentication failed")); - send_response(sess, token, tmp_pool, response, &rdata->info, + send_response(sess, token, tmp_pool, response, &rdata->info, PJ_FALSE, src_addr, src_addr_len); } @@ -1284,7 +1288,7 @@ static pj_status_t on_incoming_response(pj_stun_session *sess, /* Lookup pending client transaction */ tdata = tsx_lookup(sess, msg); if (tdata == NULL) { - PJ_LOG(5,(SNAME(sess), + PJ_LOG(5,(SNAME(sess), "Transaction not found, response silently discarded")); return PJ_SUCCESS; } @@ -1295,11 +1299,11 @@ static pj_status_t on_incoming_response(pj_stun_session *sess, /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE * is specified in the option. */ - if ((options & PJ_STUN_NO_AUTHENTICATE) == 0 && - tdata->auth_info.auth_key.slen != 0 && + if ((options & PJ_STUN_NO_AUTHENTICATE) == 0 && + tdata->auth_info.auth_key.slen != 0 && pj_stun_auth_valid_for_msg(msg)) { - status = pj_stun_authenticate_response(pkt, pkt_len, msg, + status = pj_stun_authenticate_response(pkt, pkt_len, msg, &tdata->auth_info.auth_key); if (status != PJ_SUCCESS) { PJ_PERROR(5,(SNAME(sess), status, @@ -1308,11 +1312,11 @@ static pj_status_t on_incoming_response(pj_stun_session *sess, } } - /* Pass the response to the transaction. + /* Pass the response to the transaction. * If the message is accepted, transaction callback will be called, * and this will call the session callback too. */ - status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg, + status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg, src_addr, src_addr_len); if (status != PJ_SUCCESS) { return status; @@ -1336,7 +1340,7 @@ static pj_status_t check_cached_response(pj_stun_session *sess, while (t != &sess->cached_response_list) { if (t->msg_magic == msg->hdr.magic && t->msg->hdr.type == msg->hdr.type && - pj_memcmp(t->msg_key, msg->hdr.tsx_id, + pj_memcmp(t->msg_key, msg->hdr.tsx_id, sizeof(msg->hdr.tsx_id))==0) { break; @@ -1347,10 +1351,10 @@ static pj_status_t check_cached_response(pj_stun_session *sess, if (t != &sess->cached_response_list) { /* Found response in the cache */ - PJ_LOG(5,(SNAME(sess), + PJ_LOG(5,(SNAME(sess), "Request retransmission, sending cached response")); - send_response(sess, t->token, tmp_pool, t->msg, &t->auth_info, + send_response(sess, t->token, tmp_pool, t->msg, &t->auth_info, PJ_TRUE, src_addr, src_addr_len); return PJ_SUCCESS; } @@ -1383,8 +1387,8 @@ static pj_status_t on_incoming_request(pj_stun_session *sess, * is specified in the option. */ if ((options & PJ_STUN_NO_AUTHENTICATE) == 0) { - status = authenticate_req(sess, token, (const pj_uint8_t*) in_pkt, - in_pkt_len,&rdata, tmp_pool, src_addr, + status = authenticate_req(sess, token, (const pj_uint8_t*) in_pkt, + in_pkt_len,&rdata, tmp_pool, src_addr, src_addr_len); if (status != PJ_SUCCESS) { return status; @@ -1400,11 +1404,11 @@ static pj_status_t on_incoming_request(pj_stun_session *sess, pj_stun_msg *response; err_text = pj_str("Callback is not set to handle request"); - status = pj_stun_msg_create_response(tmp_pool, msg, - PJ_STUN_SC_BAD_REQUEST, + status = pj_stun_msg_create_response(tmp_pool, msg, + PJ_STUN_SC_BAD_REQUEST, &err_text, &response); if (status == PJ_SUCCESS && response) { - status = send_response(sess, token, tmp_pool, response, + status = send_response(sess, token, tmp_pool, response, NULL, PJ_FALSE, src_addr, src_addr_len); } } @@ -1440,8 +1444,8 @@ static void dump_rx_msg(pj_stun_session *sess, const pj_stun_msg *msg, unsigned pkt_size, const pj_sockaddr_t *addr) { char src_info[PJ_INET6_ADDRSTRLEN+10]; - - if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && + + if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && (sess->log_flag & PJ_STUN_SESS_LOG_RX_REQ)==0) || (PJ_STUN_IS_RESPONSE(msg->hdr.type) && (sess->log_flag & PJ_STUN_SESS_LOG_RX_RES)==0) || @@ -1459,7 +1463,7 @@ static void dump_rx_msg(pj_stun_session *sess, const pj_stun_msg *msg, "%s" "--- end of STUN message ---\n", pkt_size, src_info, - pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf), + pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf), NULL))); } @@ -1494,7 +1498,7 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, /* Try to parse the message */ status = pj_stun_msg_decode(sess->rx_pool, (const pj_uint8_t*)packet, - pkt_size, options, + pkt_size, options, &msg, parsed_len, &response); if (status != PJ_SUCCESS) { LOG_ERR_(sess, "STUN msg_decode() error", status); @@ -1508,7 +1512,7 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, dump_rx_msg(sess, msg, (unsigned)pkt_size, src_addr); /* For requests, check if we have cached response */ - status = check_cached_response(sess, sess->rx_pool, msg, + status = check_cached_response(sess, sess->rx_pool, msg, src_addr, src_addr_len); if (status == PJ_SUCCESS) { goto on_return; @@ -1518,23 +1522,23 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, if (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) || PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) { - status = on_incoming_response(sess, options, - (const pj_uint8_t*) packet, - (unsigned)pkt_size, msg, + status = on_incoming_response(sess, options, + (const pj_uint8_t*) packet, + (unsigned)pkt_size, msg, src_addr, src_addr_len); } else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) { - status = on_incoming_request(sess, options, token, sess->rx_pool, - (const pj_uint8_t*) packet, - (unsigned)pkt_size, + status = on_incoming_request(sess, options, token, sess->rx_pool, + (const pj_uint8_t*) packet, + (unsigned)pkt_size, msg, src_addr, src_addr_len); } else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) { - status = on_incoming_indication(sess, token, sess->rx_pool, - (const pj_uint8_t*) packet, - (unsigned)pkt_size, msg, src_addr, + status = on_incoming_indication(sess, token, sess->rx_pool, + (const pj_uint8_t*) packet, + (unsigned)pkt_size, msg, src_addr, src_addr_len); } else { @@ -1551,3 +1555,12 @@ on_return: return status; } +PJ_DECL(pj_stun_session_cb *) pj_stun_session_callback(pj_stun_session *sess) +{ + return sess ? &sess->cb : NULL; +} + +PJ_DECL(pj_stun_tp_type) pj_stun_session_tp_type(pj_stun_session *sess) +{ + return sess ? sess->conn_type : PJ_STUN_TP_UDP; +} diff --git a/pjnath/src/pjnath/stun_sock.c b/pjnath/src/pjnath/stun_sock.c index 28f760384..93b368777 100644 --- a/pjnath/src/pjnath/stun_sock.c +++ b/pjnath/src/pjnath/stun_sock.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include @@ -39,6 +39,35 @@ enum { MAX_BIND_RETRY = 100 }; +#if PJ_HAS_TCP +// The head of a RTP packet is stored in a 16 bits header, so the max size of a +// packet is 65536 +#define MAX_RTP_SIZE 65536 +#endif + +typedef struct outgoing_sock { + pj_sock_t fd; + pj_activesock_t *sock; + pj_sockaddr addr; + int addr_len; +} outgoing_sock; + +typedef struct incoming_sock { + pj_sock_t fd; + pj_activesock_t *sock; + pj_sockaddr addr; + int addr_len; +} incoming_sock; + +typedef struct rx_buf { + pj_activesock_t *asock; + pj_uint8_t rx_buffer[MAX_RTP_SIZE]; + pj_uint16_t rx_buffer_size; + pj_uint16_t rx_wanted_size; + struct rx_buf *next; + struct rx_buf *prev; +} rx_buf; + struct pj_stun_sock { char *obj_name; /* Log identification */ @@ -46,6 +75,8 @@ struct pj_stun_sock void *user_data; /* Application user data */ pj_bool_t is_destroying; /* Destroy already called */ int af; /* Address family */ + pj_stun_tp_type conn_type; + pj_stun_sock_cfg cfg; pj_stun_config stun_cfg; /* STUN config (ioqueue etc)*/ pj_stun_sock_cb cb; /* Application callbacks */ @@ -58,6 +89,14 @@ struct pj_stun_sock pj_dns_srv_async_query *q; /* Pending DNS query */ pj_sock_t sock_fd; /* Socket descriptor */ pj_activesock_t *active_sock; /* Active socket object */ +#if PJ_HAS_TCP + pj_bool_t no_new_socket; + int outgoing_nb; + outgoing_sock outgoing_socks[PJ_ICE_MAX_CHECKS]; + int incoming_nb; + incoming_sock incoming_socks[PJ_ICE_MAX_CHECKS]; + rx_buf *rx_buffers; +#endif pj_ioqueue_op_key_t send_key; /* Default send key for app */ pj_ioqueue_op_key_t int_send_key; /* Send key for internal */ pj_status_t last_err; /* Last error status */ @@ -67,8 +106,17 @@ struct pj_stun_sock pj_grp_lock_t *grp_lock; /* Session group lock */ }; -/* - * Prototypes for static functions +////////////////////////////////////////////////////////////////////////////// + +static pj_uint16_t GETVAL16H(const pj_uint8_t *buf1, const pj_uint8_t *buf2) +{ + return (pj_uint16_t) ((buf1[0] << 8) | (buf2[0] << 0)); +} + +////////////////////////////////////////////////////////////////////////////// + +/* + * Prototypes for static functions */ /* Destructor for group lock */ @@ -82,7 +130,7 @@ static pj_status_t sess_on_send_msg(pj_stun_session *sess, const pj_sockaddr_t *dst_addr, unsigned addr_len); -/* This callback is called by the STUN session when outgoing transaction +/* This callback is called by the STUN session when outgoing transaction * is complete */ static void sess_on_request_complete(pj_stun_session *sess, @@ -119,6 +167,24 @@ static void start_ka_timer(pj_stun_sock *stun_sock); /* Keep-alive timer callback */ static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te); + +static pj_bool_t on_stun_sock_ready(pj_activesock_t *asock, + pj_status_t status); + +static pj_bool_t on_stun_sock_accept(pj_activesock_t *asock, + pj_sock_t newsock, + const pj_sockaddr_t *src_addr, + int src_addr_len); + +static pj_bool_t on_connect_complete(pj_activesock_t *asock, + pj_status_t status); + +/* Notify application that session has failed */ +static pj_bool_t sess_fail(pj_stun_sock *stun_sock, + pj_stun_sock_op op, + pj_status_t status); + + #define INTERNAL_MSG_TOKEN (void*)(pj_ssize_t)1 @@ -150,6 +216,7 @@ PJ_DEF(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg) cfg->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; cfg->qos_type = PJ_QOS_TYPE_BEST_EFFORT; cfg->qos_ignore_error = PJ_TRUE; + cfg->user_mapping_cnt = 0; } @@ -160,116 +227,67 @@ static pj_bool_t pj_stun_sock_cfg_is_valid(const pj_stun_sock_cfg *cfg) } /* - * Create the STUN transport using the specified configuration. + * Initialize. */ -PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, - const char *name, - int af, - const pj_stun_sock_cb *cb, - const pj_stun_sock_cfg *cfg, - void *user_data, - pj_stun_sock **p_stun_sock) +PJ_DEF(pj_status_t) pj_stun_sock_alloc(pj_stun_sock *stun_sock) { - pj_pool_t *pool; - pj_stun_sock *stun_sock; - pj_stun_sock_cfg default_cfg; + pj_status_t status; pj_sockaddr bound_addr; - unsigned i; pj_uint16_t max_bind_retry; - pj_status_t status; - - PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL); - PJ_ASSERT_RETURN(af==pj_AF_INET()||af==pj_AF_INET6(), PJ_EAFNOTSUP); - PJ_ASSERT_RETURN(!cfg || pj_stun_sock_cfg_is_valid(cfg), PJ_EINVAL); - PJ_ASSERT_RETURN(cb->on_status, PJ_EINVAL); - - status = pj_stun_config_check_valid(stun_cfg); - if (status != PJ_SUCCESS) - return status; + int sock_type; - if (name == NULL) - name = "stuntp%p"; - - if (cfg == NULL) { - pj_stun_sock_cfg_default(&default_cfg); - cfg = &default_cfg; - } - - - /* Create structure */ - pool = pj_pool_create(stun_cfg->pf, name, 256, 512, NULL); - stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock); - stun_sock->pool = pool; - stun_sock->obj_name = pool->obj_name; - stun_sock->user_data = user_data; - stun_sock->af = af; - stun_sock->sock_fd = PJ_INVALID_SOCKET; - pj_memcpy(&stun_sock->stun_cfg, stun_cfg, sizeof(*stun_cfg)); - pj_memcpy(&stun_sock->cb, cb, sizeof(*cb)); - - stun_sock->ka_interval = cfg->ka_interval; - if (stun_sock->ka_interval == 0) - stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; - - if (cfg->grp_lock) { - stun_sock->grp_lock = cfg->grp_lock; - } else { - status = pj_grp_lock_create(pool, NULL, &stun_sock->grp_lock); - if (status != PJ_SUCCESS) { - pj_pool_release(pool); - return status; - } - } + pj_grp_lock_acquire(stun_sock->grp_lock); - pj_grp_lock_add_ref(stun_sock->grp_lock); - pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock, - &stun_sock_destructor); + if (stun_sock->conn_type == PJ_STUN_TP_UDP) + sock_type = pj_SOCK_DGRAM(); + else + sock_type = pj_SOCK_STREAM(); /* Create socket and bind socket */ - status = pj_sock_socket(af, pj_SOCK_DGRAM() | pj_SOCK_CLOEXEC(), 0, &stun_sock->sock_fd); + status = pj_sock_socket(stun_sock->af, sock_type, 0, &stun_sock->sock_fd); if (status != PJ_SUCCESS) goto on_error; /* Apply QoS, if specified */ - status = pj_sock_apply_qos2(stun_sock->sock_fd, cfg->qos_type, - &cfg->qos_params, 2, stun_sock->obj_name, + status = pj_sock_apply_qos2(stun_sock->sock_fd, stun_sock->cfg.qos_type, + &stun_sock->cfg.qos_params, 2, stun_sock->obj_name, NULL); - if (status != PJ_SUCCESS && !cfg->qos_ignore_error) + if (status != PJ_SUCCESS && !stun_sock->cfg.qos_ignore_error) goto on_error; /* Apply socket buffer size */ - if (cfg->so_rcvbuf_size > 0) { - unsigned sobuf_size = cfg->so_rcvbuf_size; + if (stun_sock->cfg.so_rcvbuf_size > 0) { + unsigned sobuf_size = stun_sock->cfg.so_rcvbuf_size; status = pj_sock_setsockopt_sobuf(stun_sock->sock_fd, pj_SO_RCVBUF(), PJ_TRUE, &sobuf_size); if (status != PJ_SUCCESS) { PJ_PERROR(3, (stun_sock->obj_name, status, "Failed setting SO_RCVBUF")); } else { - if (sobuf_size < cfg->so_rcvbuf_size) { - PJ_LOG(4, (stun_sock->obj_name, + if (sobuf_size < stun_sock->cfg.so_rcvbuf_size) { + PJ_LOG(4, (stun_sock->obj_name, "Warning! Cannot set SO_RCVBUF as configured, " "now=%d, configured=%d", - sobuf_size, cfg->so_rcvbuf_size)); + sobuf_size, stun_sock->cfg.so_rcvbuf_size)); } else { PJ_LOG(5, (stun_sock->obj_name, "SO_RCVBUF set to %d", sobuf_size)); } } } - if (cfg->so_sndbuf_size > 0) { - unsigned sobuf_size = cfg->so_sndbuf_size; + if (stun_sock->cfg.so_sndbuf_size > 0) { + unsigned sobuf_size = stun_sock->cfg.so_sndbuf_size; status = pj_sock_setsockopt_sobuf(stun_sock->sock_fd, pj_SO_SNDBUF(), PJ_TRUE, &sobuf_size); if (status != PJ_SUCCESS) { PJ_PERROR(3, (stun_sock->obj_name, status, "Failed setting SO_SNDBUF")); } else { - if (sobuf_size < cfg->so_sndbuf_size) { - PJ_LOG(4, (stun_sock->obj_name, + if (sobuf_size < stun_sock->cfg.so_sndbuf_size) { + PJ_LOG(4, (stun_sock->obj_name, "Warning! Cannot set SO_SNDBUF as configured, " "now=%d, configured=%d", - sobuf_size, cfg->so_sndbuf_size)); + sobuf_size, stun_sock->cfg.so_sndbuf_size)); } else { PJ_LOG(5, (stun_sock->obj_name, "SO_SNDBUF set to %d", sobuf_size)); @@ -279,16 +297,16 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, /* Bind socket */ max_bind_retry = MAX_BIND_RETRY; - if (cfg->port_range && cfg->port_range < max_bind_retry) - max_bind_retry = cfg->port_range; - pj_sockaddr_init(af, &bound_addr, NULL, 0); - if (cfg->bound_addr.addr.sa_family == pj_AF_INET() || - cfg->bound_addr.addr.sa_family == pj_AF_INET6()) + if (stun_sock->cfg.port_range && stun_sock->cfg.port_range < max_bind_retry) + max_bind_retry = stun_sock->cfg.port_range; + pj_sockaddr_init(stun_sock->af, &bound_addr, NULL, 0); + if (stun_sock->cfg.bound_addr.addr.sa_family == pj_AF_INET() || + stun_sock->cfg.bound_addr.addr.sa_family == pj_AF_INET6()) { - pj_sockaddr_cp(&bound_addr, &cfg->bound_addr); + pj_sockaddr_cp(&bound_addr, &stun_sock->cfg.bound_addr); } status = pj_sock_bind_random(stun_sock->sock_fd, &bound_addr, - cfg->port_range, max_bind_retry); + stun_sock->cfg.port_range, max_bind_retry); if (status != PJ_SUCCESS) goto on_error; @@ -298,13 +316,13 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, pj_sockaddr bound_addr; int addr_len = sizeof(bound_addr); - status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, + status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; stun_sock->info = pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+10); - pj_sockaddr_print(&bound_addr, stun_sock->info, + pj_sockaddr_print(&bound_addr, stun_sock->info, PJ_INET6_ADDRSTRLEN, 3); } #endif @@ -315,35 +333,153 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, pj_activesock_cb activesock_cb; pj_activesock_cfg_default(&activesock_cfg); - activesock_cfg.grp_lock = stun_sock->grp_lock; - activesock_cfg.async_cnt = cfg->async_cnt; + activesock_cfg.grp_lock = stun_sock->grp_lock; + activesock_cfg.async_cnt = stun_sock->cfg.async_cnt; activesock_cfg.concurrency = 0; /* Create the active socket */ pj_bzero(&activesock_cb, sizeof(activesock_cb)); + activesock_cb.on_data_sent = &on_data_sent; activesock_cb.on_data_recvfrom = &on_data_recvfrom; - activesock_cb.on_data_sent = &on_data_sent; - status = pj_activesock_create(pool, stun_sock->sock_fd, - pj_SOCK_DGRAM(), - &activesock_cfg, stun_cfg->ioqueue, - &activesock_cb, stun_sock, - &stun_sock->active_sock); - if (status != PJ_SUCCESS) + +#if PJ_HAS_TCP + if (stun_sock->conn_type != PJ_STUN_TP_UDP) { + activesock_cb.on_accept_complete = &on_stun_sock_accept; + // Will be ready to accept incoming connections from the external world + status = pj_sock_listen(stun_sock->sock_fd, PJ_SOMAXCONN); + if (status != PJ_SUCCESS) { + goto on_error; + } + } else { + activesock_cb.on_connect_complete = &on_stun_sock_ready; + } +#else + activesock_cb.on_connect_complete = &on_stun_sock_ready; +#endif + + status = pj_activesock_create(stun_sock->pool, stun_sock->sock_fd, + sock_type, &activesock_cfg, + stun_sock->stun_cfg.ioqueue, + &activesock_cb, stun_sock, + &stun_sock->active_sock); + if (status != PJ_SUCCESS) { goto on_error; + } - /* Start asynchronous read operations */ - status = pj_activesock_start_recvfrom(stun_sock->active_sock, pool, - cfg->max_pkt_size, 0); - if (status != PJ_SUCCESS) +#if PJ_HAS_TCP + if (stun_sock->conn_type != PJ_STUN_TP_UDP) { + status = pj_activesock_start_accept(stun_sock->active_sock, + stun_sock->pool); + } else { + status = PJ_SUCCESS; + } + if (status == PJ_SUCCESS) { + on_stun_sock_ready(stun_sock->active_sock, PJ_SUCCESS); + } else if (status != PJ_EPENDING) { + char addrinfo[PJ_INET6_ADDRSTRLEN + 10]; + pj_perror(3, stun_sock->pool->obj_name, status, + "Failed to connect to %s", + pj_sockaddr_print(&bound_addr, addrinfo, + sizeof(addrinfo), 3)); goto on_error; + } +#else + on_stun_sock_ready(stun_sock->active_sock, PJ_SUCCESS); +#endif + } - /* Init send keys */ - pj_ioqueue_op_key_init(&stun_sock->send_key, - sizeof(stun_sock->send_key)); - pj_ioqueue_op_key_init(&stun_sock->int_send_key, - sizeof(stun_sock->int_send_key)); + pj_grp_lock_release(stun_sock->grp_lock); + return status; +on_error: + pj_stun_sock_destroy(stun_sock); + pj_grp_lock_release(stun_sock->grp_lock); + return status; +} + +/* + * Create the STUN transport using the specified configuration. + */ +PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, + const char *name, + int af, + pj_stun_tp_type conn_type, + const pj_stun_sock_cb *cb, + const pj_stun_sock_cfg *cfg, + void *user_data, + pj_stun_sock **p_stun_sock) +{ + pj_pool_t *pool; + pj_stun_sock *stun_sock; + pj_stun_sock_cfg default_cfg; + pj_status_t status; + + PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL); + PJ_ASSERT_RETURN(af==pj_AF_INET()||af==pj_AF_INET6(), PJ_EAFNOTSUP); + PJ_ASSERT_RETURN(!cfg || pj_stun_sock_cfg_is_valid(cfg), PJ_EINVAL); + PJ_ASSERT_RETURN(cb->on_status, PJ_EINVAL); + PJ_ASSERT_RETURN(conn_type != PJ_STUN_TP_TCP || PJ_HAS_TCP, PJ_EINVAL); + + status = pj_stun_config_check_valid(stun_cfg); + if (status != PJ_SUCCESS) + return status; + + if (name == NULL) { + switch (conn_type) { + case PJ_STUN_TP_UDP: + name = "udpstun%p"; + break; + case PJ_STUN_TP_TCP: + name = "tcpstun%p"; + break; + default: + PJ_ASSERT_RETURN(!"Invalid STUN conn_type", PJ_EINVAL); + name = "tcpstun%p"; + break; + } + } + + if (cfg == NULL) { + pj_stun_sock_cfg_default(&default_cfg); + cfg = &default_cfg; + } + + /* Create structure */ + pool = pj_pool_create(stun_cfg->pf, name, 256, 512, NULL); + stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock); + stun_sock->pool = pool; + stun_sock->obj_name = pool->obj_name; + stun_sock->user_data = user_data; + stun_sock->af = af; + stun_sock->conn_type = conn_type; + stun_sock->sock_fd = PJ_INVALID_SOCKET; +#if PJ_HAS_TCP + stun_sock->no_new_socket = PJ_FALSE; + stun_sock->outgoing_nb = -1; + stun_sock->incoming_nb = -1; +#endif + pj_memcpy(&stun_sock->stun_cfg, stun_cfg, sizeof(*stun_cfg)); + pj_memcpy(&stun_sock->cb, cb, sizeof(*cb)); + /* Copy socket settings; QoS parameters etc */ + pj_memcpy(&stun_sock->cfg, cfg, sizeof(*cfg)); + + stun_sock->ka_interval = cfg->ka_interval; + if (stun_sock->ka_interval == 0) + stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; + + if (cfg->grp_lock) { + stun_sock->grp_lock = cfg->grp_lock; + } else { + status = pj_grp_lock_create(pool, NULL, &stun_sock->grp_lock); + if (status != PJ_SUCCESS) { + pj_pool_release(pool); + return status; + } } + pj_grp_lock_add_ref(stun_sock->grp_lock); + pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock, + &stun_sock_destructor); + /* Create STUN session */ { pj_stun_session_cb sess_cb; @@ -351,13 +487,16 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_request_complete = &sess_on_request_complete; sess_cb.on_send_msg = &sess_on_send_msg; - status = pj_stun_session_create(&stun_sock->stun_cfg, + status = pj_stun_session_create(&stun_sock->stun_cfg, stun_sock->obj_name, - &sess_cb, PJ_FALSE, + &sess_cb, PJ_FALSE, stun_sock->grp_lock, - &stun_sock->stun_sess); - if (status != PJ_SUCCESS) - goto on_error; + &stun_sock->stun_sess, + conn_type); + if (status != PJ_SUCCESS) { + pj_stun_sock_destroy(stun_sock); + return status; + } } /* Associate us with the STUN session */ @@ -368,25 +507,370 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, * STUN messages we sent with STUN messages that the application sends. * The last 16bit value in the array is a counter. */ + unsigned i; for (i=0; itsx_id); ++i) { stun_sock->tsx_id[i] = (pj_uint16_t) pj_rand(); } stun_sock->tsx_id[5] = 0; - /* Init timer entry */ stun_sock->ka_timer.cb = &ka_timer_cb; stun_sock->ka_timer.user_data = stun_sock; + pj_stun_sock_alloc(stun_sock); + /* Done */ *p_stun_sock = stun_sock; return PJ_SUCCESS; +} -on_error: - pj_stun_sock_destroy(stun_sock); - return status; +/* + * Notification when outgoing TCP socket has been connected. + */ +static pj_bool_t on_stun_sock_ready(pj_activesock_t *asock, pj_status_t status) +{ + pj_stun_sock *stun_sock; + stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(asock); + if (!stun_sock) + return PJ_FALSE; + + pj_grp_lock_acquire(stun_sock->grp_lock); + + /* TURN session may have already been destroyed here. + * See ticket #1557 (http://trac.pjsip.org/repos/ticket/1557). + */ + if (!stun_sock->stun_sess) { + sess_fail(stun_sock, PJ_STUN_SESS_DESTROYED, status); + pj_grp_lock_release(stun_sock->grp_lock); + return PJ_FALSE; + } + + if (status != PJ_SUCCESS) { + sess_fail(stun_sock, PJ_STUN_TCP_CONNECT_ERROR, status); + pj_grp_lock_release(stun_sock->grp_lock); + return PJ_FALSE; + } + + if (stun_sock->conn_type != PJ_STUN_TP_UDP) + PJ_LOG(5,(stun_sock->obj_name, "TCP connected")); + + /* Start asynchronous read operations */ + pj_status_t result; + result = pj_activesock_start_recvfrom(asock, stun_sock->pool, + stun_sock->cfg.max_pkt_size, 0); + if (result != PJ_SUCCESS) + return PJ_FALSE; + + /* Associate us with the STUN session */ + pj_stun_session_set_user_data(stun_sock->stun_sess, stun_sock); + + /* Initialize random numbers to be used as STUN transaction ID for + * outgoing Binding request. We use the 80bit number to distinguish + * STUN messages we sent with STUN messages that the application sends. + * The last 16bit value in the array is a counter. + */ + unsigned i; + for (i=0; itsx_id); ++i) { + stun_sock->tsx_id[i] = (pj_uint16_t) pj_rand(); + } + stun_sock->tsx_id[5] = 0; + + /* Init timer entry */ + stun_sock->ka_timer.cb = &ka_timer_cb; + stun_sock->ka_timer.user_data = stun_sock; + + if (status != PJ_SUCCESS) { + pj_stun_sock_destroy(stun_sock); + pj_grp_lock_release(stun_sock->grp_lock); + return status; + } + + /* Init send keys */ + pj_ioqueue_op_key_init(&stun_sock->send_key, sizeof(stun_sock->send_key)); + pj_ioqueue_op_key_init(&stun_sock->int_send_key, + sizeof(stun_sock->int_send_key)); + + pj_grp_lock_release(stun_sock->grp_lock); + return PJ_TRUE; } +static pj_bool_t parse_rx_packet(pj_activesock_t *asock, + void *data, + pj_size_t size, + const pj_sockaddr_t *rx_addr, + unsigned sock_addr_len) +{ + + pj_stun_sock *stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock); + if (!stun_sock) + return PJ_FALSE; + + pj_grp_lock_acquire(stun_sock->grp_lock); + pj_uint16_t parsed = 0; + pj_status_t result = PJ_TRUE; + pj_status_t status; + +#if PJ_HAS_TCP + // Search current rx_buf + rx_buf* buf = NULL; + rx_buf* stun_sock_buf = stun_sock->rx_buffers; + while (stun_sock_buf) { + if (stun_sock_buf->asock == asock) { + buf = stun_sock_buf; + break; + } + stun_sock_buf = stun_sock_buf->next; + } + if (!buf) { + // Create rx_buf, this buf will be released when the pool is released + buf = (rx_buf*)pj_pool_calloc(stun_sock->pool, 1, sizeof(rx_buf)); + if (!buf) { + PJ_LOG(5, (stun_sock->obj_name, "Cannot allocate memory for rx_buf")); + status = pj_grp_lock_release(stun_sock->grp_lock); + return PJ_FALSE; + } + buf->asock = asock; + buf->next = stun_sock->rx_buffers; + if (stun_sock->rx_buffers) + stun_sock->rx_buffers->prev = buf; + stun_sock->rx_buffers = buf; + } +#endif + + do { + pj_uint16_t leftover = size - parsed; + pj_uint8_t *current_packet = ((pj_uint8_t *)(data)) + parsed; + +#if PJ_HAS_TCP + if (stun_sock->conn_type != PJ_STUN_TP_UDP) { + /* RFC6544, the packet is wrapped into a packet following the RFC4571 */ + pj_bool_t store_remaining = PJ_TRUE; + if (buf->rx_buffer_size != 0 || buf->rx_wanted_size != 0) { + if (buf->rx_buffer_size == 1 && buf->rx_wanted_size == 0) { + // In this case, we want to know the header size + leftover = GETVAL16H(buf->rx_buffer, current_packet); + + buf->rx_buffer_size = 0; + current_packet++; + parsed++; + + if (leftover + parsed <= size) { + store_remaining = PJ_FALSE; + parsed += leftover; + } else { + buf->rx_wanted_size = leftover; + } + + } else if (leftover + buf->rx_buffer_size >= buf->rx_wanted_size) { + // We have enough data Build new packet to parse + store_remaining = PJ_FALSE; + pj_uint16_t eaten_bytes = buf->rx_wanted_size - buf->rx_buffer_size; + pj_memcpy(buf->rx_buffer + buf->rx_buffer_size, + current_packet, eaten_bytes); + + leftover = buf->rx_wanted_size; + current_packet = buf->rx_buffer; + parsed += eaten_bytes; + + buf->rx_buffer_size = 0; + buf->rx_wanted_size = 0; + } + } else if (leftover > 1) { + leftover = GETVAL16H(current_packet, current_packet+1); + current_packet += 2; + parsed += 2; + if (leftover + parsed <= size) { + store_remaining = PJ_FALSE; + parsed += leftover; + } else { + buf->rx_wanted_size = leftover; + } + } + if (store_remaining) { + leftover = size - parsed; + pj_memcpy(buf->rx_buffer + buf->rx_buffer_size, + current_packet, leftover); + buf->rx_buffer_size += leftover; + break; + } + } else { +#endif + parsed = size; +#if PJ_HAS_TCP + } +#endif + /* Check that this is STUN message */ + status = pj_stun_msg_check((const pj_uint8_t *)current_packet, leftover, + PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET); + if (status != PJ_SUCCESS) { + /* Not STUN -- give it to application */ + goto process_app_data; + } + + /* Treat packet as STUN header and copy the STUN message type. + * We don't want to access the type directly from the header + * since it may not be properly aligned. + */ + pj_stun_msg_hdr *hdr = (pj_stun_msg_hdr *)current_packet; + pj_uint16_t type; + pj_memcpy(&type, &hdr->type, 2); + type = pj_ntohs(type); + + /* If the packet is a STUN Binding response and part of the + * transaction ID matches our internal ID, then this is + * our internal STUN message (Binding request or keep alive). + * Give it to our STUN session. + */ + if (!PJ_STUN_IS_RESPONSE(type) || + PJ_STUN_GET_METHOD(type) != PJ_STUN_BINDING_METHOD || + pj_memcmp(hdr->tsx_id, stun_sock->tsx_id, 10) != 0) + { + /* Not STUN Binding response, or STUN transaction ID mismatch. + * This is not our message too -- give it to application. + */ + goto process_app_data; + } + + /* This is our STUN Binding response. Give it to the STUN session */ + status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, current_packet, + leftover, PJ_STUN_IS_DATAGRAM, NULL, + NULL, rx_addr, sock_addr_len); + + result &= status != PJ_EGONE ? PJ_TRUE : PJ_FALSE; + continue; + +process_app_data: + if (stun_sock->cb.on_rx_data) + (*stun_sock->cb.on_rx_data)(stun_sock, current_packet, + (unsigned)leftover, rx_addr, sock_addr_len); + + result &= status != PJ_EGONE ? PJ_TRUE : PJ_FALSE; + } while (parsed < size && result); + + status = pj_grp_lock_release(stun_sock->grp_lock); + return result; +} + +static pj_bool_t on_data_read(pj_activesock_t *asock, + void *data, + pj_size_t size, + pj_status_t status, + pj_size_t *remainder) +{ + + pj_stun_sock *stun_sock; + + if (!(stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(asock))) + return PJ_FALSE; + + pj_stun_session_cb *cb = pj_stun_session_callback(stun_sock->stun_sess); + /* Log socket error or disconnection */ + if (status != PJ_SUCCESS) { + if (stun_sock->conn_type == PJ_STUN_TP_UDP + || (status != PJ_EEOF && status != 120104 && status != 130054)) + { + PJ_PERROR(2, (stun_sock->obj_name, status, "read() error")); + } else if (status == 120104 + || status == 130054 /* RESET BY PEER */) + { + for (int i = 0; i <= stun_sock->outgoing_nb; ++i) + if (stun_sock->outgoing_socks[i].sock == asock + && cb + && (cb->on_peer_reset_connection)) + { + (cb->on_peer_reset_connection)(stun_sock->stun_sess, + &stun_sock->outgoing_socks[i].addr); + } + } + return PJ_FALSE; + } +#if PJ_HAS_TCP + pj_sockaddr_t *rx_addr = NULL; + unsigned sock_addr_len = 0; + for (int i = 0; i <= stun_sock->outgoing_nb; ++i) + if (stun_sock->outgoing_socks[i].sock == asock) { + rx_addr = &stun_sock->outgoing_socks[i].addr; + sock_addr_len = pj_sockaddr_get_len(rx_addr); + if (cb && (cb->on_peer_packet)) + (cb->on_peer_packet)(stun_sock->stun_sess, + &stun_sock->outgoing_socks[i].addr); + } + + if (rx_addr == NULL && stun_sock->incoming_nb != -1) { + // It's an incoming message + for (int i = 0; i <= stun_sock->incoming_nb; ++i) + if (stun_sock->incoming_socks[i].sock == asock) { + rx_addr = &stun_sock->incoming_socks[i].addr; + sock_addr_len = stun_sock->incoming_socks[i].addr_len; + } + } + return parse_rx_packet(asock, data, size, rx_addr, sock_addr_len); +#else + pj_grp_lock_release(stun_sock->grp_lock); + return PJ_FALSE; +#endif +} + +#if PJ_HAS_TCP +/* + * Notification when incoming TCP socket has been connected. + * NOTE: cf https://www.pjsip.org/docs/latest/pjlib/docs/html//structpj__activesock__cb.htm if status needed + */ +static pj_bool_t on_stun_sock_accept(pj_activesock_t *active_sock, + pj_sock_t sock, + const pj_sockaddr_t *src_addr, + int src_addr_len) +{ + pj_status_t status; + pj_stun_sock *stun_sock; + int sock_type = pj_SOCK_STREAM(); + stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(active_sock); + + if (stun_sock->no_new_socket) + return PJ_FALSE; + + stun_sock->incoming_nb += 1; + int nb_check = stun_sock->incoming_nb; + pj_sock_t *fd = &stun_sock->incoming_socks[nb_check].fd; + pj_activesock_t **asock = &stun_sock->incoming_socks[nb_check].sock; + + pj_sockaddr_cp(&stun_sock->incoming_socks[nb_check].addr, src_addr); + stun_sock->incoming_socks[nb_check].addr_len = src_addr_len; + *fd = sock; + + pj_activesock_cfg activesock_cfg; + pj_activesock_cb activesock_cb; + + pj_activesock_cfg_default(&activesock_cfg); + activesock_cfg.grp_lock = stun_sock->grp_lock; + activesock_cfg.async_cnt = stun_sock->cfg.async_cnt; + activesock_cfg.concurrency = 0; + + /* Create the active socket */ + pj_bzero(&activesock_cb, sizeof(activesock_cb)); + activesock_cb.on_data_read = &on_data_read; + activesock_cb.on_data_sent = &on_data_sent; + + status = pj_activesock_create(stun_sock->pool, *fd, sock_type, + &activesock_cfg, stun_sock->stun_cfg.ioqueue, + &activesock_cb, stun_sock, asock); + if (status != PJ_SUCCESS) { + pj_stun_sock_destroy(stun_sock); + pj_grp_lock_release(stun_sock->grp_lock); + return status; + } + + /* Start asynchronous read operations */ + pj_status_t result; + result = pj_activesock_start_read(*asock, stun_sock->pool, + stun_sock->cfg.max_pkt_size, 0); + if (result != PJ_SUCCESS) + return PJ_FALSE; + + return PJ_TRUE; +} +#endif + /* Start socket. */ PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock, const pj_str_t *domain, @@ -401,7 +885,7 @@ PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock, /* Check whether the domain contains IP address */ stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)stun_sock->af; - status = pj_inet_pton(stun_sock->af, domain, + status = pj_inet_pton(stun_sock->af, domain, pj_sockaddr_get_addr(&stun_sock->srv_addr)); if (status != PJ_SUCCESS) { stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)0; @@ -423,9 +907,9 @@ PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock, opt = PJ_DNS_SRV_FALLBACK_A; stun_sock->last_err = PJ_SUCCESS; - status = pj_dns_srv_resolve(domain, &res_name, default_port, + status = pj_dns_srv_resolve(domain, &res_name, default_port, stun_sock->pool, resolver, opt, - stun_sock, &dns_srv_resolver_cb, + stun_sock, &dns_srv_resolver_cb, &stun_sock->q); if (status != PJ_SUCCESS) { PJ_PERROR(4,(stun_sock->obj_name, status, @@ -525,6 +1009,26 @@ PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock) stun_sock->sock_fd = PJ_INVALID_SOCKET; } + for (int i = 0; i <= stun_sock->incoming_nb ; ++i) { + if (stun_sock->incoming_socks[i].sock != NULL) { + stun_sock->incoming_socks[i].fd = PJ_INVALID_SOCKET; + pj_activesock_close(stun_sock->incoming_socks[i].sock); + } else if (stun_sock->incoming_socks[i].fd != PJ_INVALID_SOCKET) { + pj_sock_close(stun_sock->incoming_socks[i].fd); + stun_sock->incoming_socks[i].fd = PJ_INVALID_SOCKET; + } + } + + for (int i = 0; i <= stun_sock->outgoing_nb ; ++i) { + if (stun_sock->outgoing_socks[i].sock != NULL) { + stun_sock->outgoing_socks[i].fd = PJ_INVALID_SOCKET; + pj_activesock_close(stun_sock->outgoing_socks[i].sock); + } else if (stun_sock->outgoing_socks[i].fd != PJ_INVALID_SOCKET) { + pj_sock_close(stun_sock->outgoing_socks[i].fd); + stun_sock->outgoing_socks[i].fd = PJ_INVALID_SOCKET; + } + } + if (stun_sock->stun_sess) { pj_stun_session_destroy(stun_sock->stun_sess); } @@ -558,13 +1062,13 @@ PJ_DEF(pj_grp_lock_t *) pj_stun_sock_get_grp_lock(pj_stun_sock *stun_sock) } /* Notify application that session has failed */ -static pj_bool_t sess_fail(pj_stun_sock *stun_sock, +static pj_bool_t sess_fail(pj_stun_sock *stun_sock, pj_stun_sock_op op, pj_status_t status) { pj_bool_t ret; - PJ_PERROR(4,(stun_sock->obj_name, status, + PJ_PERROR(4,(stun_sock->obj_name, status, "Session failed because %s failed", pj_stun_sock_op_name(op))); @@ -601,10 +1105,10 @@ static void dns_srv_resolver_cb(void *user_data, pj_sockaddr_init(stun_sock->af, &stun_sock->srv_addr, NULL, rec->entry[0].port); if (stun_sock->af == pj_AF_INET6()) { - stun_sock->srv_addr.ipv6.sin6_addr = + stun_sock->srv_addr.ipv6.sin6_addr = rec->entry[0].server.addr[0].ip.v6; } else { - stun_sock->srv_addr.ipv4.sin_addr = + stun_sock->srv_addr.ipv4.sin_addr = rec->entry[0].server.addr[0].ip.v4; } @@ -625,18 +1129,18 @@ static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock) ++stun_sock->tsx_id[5]; status = pj_stun_session_create_req(stun_sock->stun_sess, PJ_STUN_BINDING_REQUEST, - PJ_STUN_MAGIC, - (const pj_uint8_t*)stun_sock->tsx_id, + PJ_STUN_MAGIC, + (const pj_uint8_t*)stun_sock->tsx_id, &tdata); if (status != PJ_SUCCESS) goto on_error; - + /* Send request */ status=pj_stun_session_send_msg(stun_sock->stun_sess, INTERNAL_MSG_TOKEN, - PJ_FALSE, PJ_TRUE, &stun_sock->srv_addr, + PJ_FALSE, (stun_sock->conn_type == PJ_STUN_TP_UDP), &stun_sock->srv_addr, pj_sockaddr_get_len(&stun_sock->srv_addr), tdata); - if (status != PJ_SUCCESS) + if (status != PJ_SUCCESS && status != PJ_EPENDING) goto on_error; return PJ_SUCCESS; @@ -657,10 +1161,12 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock, pj_grp_lock_acquire(stun_sock->grp_lock); + info->conn_type = stun_sock->conn_type; + /* Copy STUN server address and mapped address */ pj_memcpy(&info->srv_addr, &stun_sock->srv_addr, sizeof(pj_sockaddr)); - pj_memcpy(&info->mapped_addr, &stun_sock->mapped_addr, + pj_memcpy(&info->mapped_addr, &stun_sock->mapped_addr, sizeof(pj_sockaddr)); /* Retrieve bound address */ @@ -673,7 +1179,7 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock, } /* If socket is bound to a specific interface, then only put that - * interface in the alias list. Otherwise query all the interfaces + * interface in the alias list. Otherwise query all the interfaces * in the host. */ if (pj_sockaddr_has_addr(&info->bound_addr)) { @@ -693,20 +1199,20 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock, pj_grp_lock_release(stun_sock->grp_lock); return status; } - + pj_sockaddr_set_port(&def_addr, port); - + /* Enum all IP interfaces in the host */ pj_enum_ip_option_default(&enum_opt); enum_opt.af = stun_sock->af; enum_opt.omit_deprecated_ipv6 = PJ_TRUE; info->alias_cnt = PJ_ARRAY_SIZE(info->aliases); - status = pj_enum_ip_interface2(&enum_opt, &info->alias_cnt, + status = pj_enum_ip_interface2(&enum_opt, &info->alias_cnt, info->aliases); if (status == PJ_ENOTSUP) { /* Try again without omitting deprecated IPv6 addresses */ enum_opt.omit_deprecated_ipv6 = PJ_FALSE; - status = pj_enum_ip_interface2(&enum_opt, &info->alias_cnt, + status = pj_enum_ip_interface2(&enum_opt, &info->alias_cnt, info->aliases); } @@ -754,7 +1260,7 @@ PJ_DEF(pj_status_t) pj_stun_sock_sendto( pj_stun_sock *stun_sock, pj_status_t status; PJ_ASSERT_RETURN(stun_sock && pkt && dst_addr && addr_len, PJ_EINVAL); - + pj_grp_lock_acquire(stun_sock->grp_lock); if (!stun_sock->active_sock) { @@ -769,13 +1275,276 @@ PJ_DEF(pj_status_t) pj_stun_sock_sendto( pj_stun_sock *stun_sock, send_key = &stun_sock->send_key; size = pkt_len; - status = pj_activesock_sendto(stun_sock->active_sock, send_key, - pkt, &size, flag, dst_addr, addr_len); + if (stun_sock->conn_type == PJ_STUN_TP_UDP) { + status = pj_activesock_sendto(stun_sock->active_sock, send_key, + pkt, &size, flag, dst_addr, addr_len); + } else { +#if PJ_HAS_TCP + pj_bool_t is_outgoing = PJ_FALSE; + pj_bool_t is_incoming = PJ_FALSE; + for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { + if (stun_sock->outgoing_socks[i].sock != NULL + && pj_sockaddr_cmp(&stun_sock->outgoing_socks[i].addr, dst_addr) == 0) { + is_outgoing = PJ_TRUE; + status = pj_activesock_send(stun_sock->outgoing_socks[i].sock, + send_key, pkt, &size, flag); + break; + } + } + if (is_outgoing == PJ_FALSE) { + for (int i = 0 ; i <= stun_sock->incoming_nb; ++i) { + if (stun_sock->incoming_socks[i].sock != NULL + && pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, + dst_addr) == 0) { + status = pj_activesock_send(stun_sock->incoming_socks[i].sock, + send_key, pkt, &size, flag); + is_incoming = PJ_TRUE; + break; + } + } + } + if (is_outgoing == PJ_FALSE && is_incoming == PJ_FALSE) { + status = pj_activesock_send(stun_sock->active_sock, send_key, pkt, + &size, flag); + } + +#endif + } pj_grp_lock_release(stun_sock->grp_lock); return status; } +#if PJ_HAS_TCP + +PJ_DECL(pj_status_t) pj_stun_sock_connect(pj_stun_sock *stun_sock, + const pj_sockaddr_t *remote_addr, + int af, + int nb_check) +{ + + pj_grp_lock_acquire(stun_sock->grp_lock); + int sock_type = pj_SOCK_STREAM(); + + outgoing_sock* os = &stun_sock->outgoing_socks[nb_check]; + pj_sock_t *fd = &os->fd; + pj_activesock_t **asock = &os->sock; + + pj_sockaddr_t *addr = &os->addr; + os->addr_len = pj_sockaddr_get_len(remote_addr); + + + pj_status_t status = pj_sock_socket(af, sock_type, 0, fd); + if (status != PJ_SUCCESS) { + pj_stun_sock_destroy(stun_sock); + pj_grp_lock_release(stun_sock->grp_lock); + return status; + } + + /* Apply QoS, if specified */ + status = pj_sock_apply_qos2(*fd, stun_sock->cfg.qos_type, + &stun_sock->cfg.qos_params, 2, stun_sock->obj_name, NULL); + if (status != PJ_SUCCESS && !stun_sock->cfg.qos_ignore_error) { + pj_stun_sock_destroy(stun_sock); + pj_grp_lock_release(stun_sock->grp_lock); + return status; + } + + /* Apply socket buffer size */ + if (stun_sock->cfg.so_rcvbuf_size > 0) { + unsigned sobuf_size = stun_sock->cfg.so_rcvbuf_size; + status = pj_sock_setsockopt_sobuf(*fd, pj_SO_RCVBUF(), PJ_TRUE, &sobuf_size); + if (status != PJ_SUCCESS) { + pj_perror(3, stun_sock->obj_name, status, "Failed setting SO_RCVBUF"); + } else { + if (sobuf_size < stun_sock->cfg.so_rcvbuf_size) { + PJ_LOG(4, (stun_sock->obj_name, + "Warning! Cannot set SO_RCVBUF as configured, " + "now=%d, configured=%d", + sobuf_size, stun_sock->cfg.so_rcvbuf_size)); + } else { + PJ_LOG(5, (stun_sock->obj_name, "SO_RCVBUF set to %d", sobuf_size)); + } + } + } + + if (stun_sock->cfg.so_sndbuf_size > 0) { + unsigned sobuf_size = stun_sock->cfg.so_sndbuf_size; + status = pj_sock_setsockopt_sobuf(*fd, pj_SO_SNDBUF(), PJ_TRUE, &sobuf_size); + if (status != PJ_SUCCESS) { + pj_perror(3, stun_sock->obj_name, status, "Failed setting SO_SNDBUF"); + } else { + if (sobuf_size < stun_sock->cfg.so_sndbuf_size) { + PJ_LOG(4, (stun_sock->obj_name, + "Warning! Cannot set SO_SNDBUF as configured, " + "now=%d, configured=%d", + sobuf_size, stun_sock->cfg.so_sndbuf_size)); + } else { + PJ_LOG(5, (stun_sock->obj_name, "SO_SNDBUF set to %d", sobuf_size)); + } + } + } + + /* Init active socket configuration */ + { + pj_activesock_cfg activesock_cfg; + pj_activesock_cb activesock_cb; + + pj_activesock_cfg_default(&activesock_cfg); + activesock_cfg.grp_lock = stun_sock->grp_lock; + activesock_cfg.async_cnt = stun_sock->cfg.async_cnt; + activesock_cfg.concurrency = 0; + + /* Create the active socket */ + pj_bzero(&activesock_cb, sizeof(activesock_cb)); + activesock_cb.on_data_read = &on_data_read; + activesock_cb.on_data_sent = &on_data_sent; + activesock_cb.on_connect_complete = &on_connect_complete; + + status = pj_activesock_create(stun_sock->pool, *fd, + sock_type, &activesock_cfg, + stun_sock->stun_cfg.ioqueue, &activesock_cb, + stun_sock, asock); + if (status != PJ_SUCCESS) { + pj_grp_lock_release(stun_sock->grp_lock); + return status; + } + + pj_sockaddr_init(stun_sock->af, addr, NULL, 0); + pj_sockaddr_cp(addr, remote_addr); + + status = pj_activesock_start_connect( + *asock, stun_sock->pool, addr, + os->addr_len); + if (status == PJ_SUCCESS) { + on_connect_complete(*asock, status); + } else if (status != PJ_EPENDING) { + char addrinfo[PJ_INET6_ADDRSTRLEN+8]; + pj_perror(3, stun_sock->pool->obj_name, status, "Failed to connect to %s", + pj_sockaddr_print(addr, addrinfo, sizeof(addrinfo), 3)); + pj_grp_lock_release(stun_sock->grp_lock); + return status; + } + } + + pj_grp_lock_release(stun_sock->grp_lock); + return status; +} + +PJ_DECL(pj_status_t) pj_stun_sock_connect_active(pj_stun_sock *stun_sock, + const pj_sockaddr_t *remote_addr, + int af) +{ + + if (stun_sock->incoming_nb != -1) { + // Check if not incoming, if so, already connected (mainly for PRFLX candidates) + for (int i = 0 ; i <= stun_sock->incoming_nb; ++i) { + if (stun_sock->incoming_socks[i].sock != NULL + && pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, remote_addr)==0) { + pj_stun_session_cb *cb = + pj_stun_session_callback(stun_sock->stun_sess); + (cb->on_peer_connection)(stun_sock->stun_sess, PJ_SUCCESS, + (pj_sockaddr_t *)remote_addr); + return PJ_SUCCESS; + } + } + } + + /* Create socket and bind socket */ + stun_sock->outgoing_nb += 1; + int nb_check = stun_sock->outgoing_nb; + return pj_stun_sock_connect(stun_sock, remote_addr, af, nb_check); + +} + +PJ_DECL(pj_status_t) pj_stun_sock_reconnect_active(pj_stun_sock *stun_sock, + const pj_sockaddr_t *remote_addr, + int af) +{ + for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { + if (stun_sock->outgoing_socks[i].sock != NULL + && pj_sockaddr_cmp(&stun_sock->outgoing_socks[i].addr, remote_addr) == 0) { + pj_activesock_close(stun_sock->outgoing_socks[i].sock); + return pj_stun_sock_connect(stun_sock, remote_addr, af, i); + } + } + return PJ_EINVAL; +} + + +PJ_DECL(pj_status_t) pj_stun_sock_close(pj_stun_sock *stun_sock, + const pj_sockaddr_t *remote_addr) +{ + for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { + if (stun_sock->outgoing_socks[i].sock != NULL + && pj_sockaddr_cmp(&stun_sock->outgoing_socks[i].addr, remote_addr) == 0) { + return pj_activesock_close(stun_sock->outgoing_socks[i].sock); + } + } + + for (int i = 0; i <= stun_sock->incoming_nb; ++i) { + if (stun_sock->incoming_socks[i].sock != NULL + && pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, remote_addr) == 0) { + return pj_activesock_close(stun_sock->incoming_socks[i].sock); + } + } + return PJ_EINVAL; +} + + +PJ_DECL(pj_status_t) pj_stun_sock_close_all_except(pj_stun_sock *stun_sock, const pj_sockaddr_t *remote_addr) +{ + stun_sock->no_new_socket = PJ_TRUE; + for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { + if (stun_sock->outgoing_socks[i].sock != NULL + && pj_sockaddr_cmp(&stun_sock->outgoing_socks[i].addr, remote_addr) != 0) { + pj_activesock_close(stun_sock->outgoing_socks[i].sock); + } + } + + for (int i = 0; i <= stun_sock->incoming_nb; ++i) { + if (stun_sock->incoming_socks[i].sock != NULL + && pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, remote_addr) != 0) { + pj_activesock_close(stun_sock->incoming_socks[i].sock); + } + } + return PJ_SUCCESS; +} + +static pj_bool_t on_connect_complete(pj_activesock_t *asock, pj_status_t status) +{ + pj_stun_sock *stun_sock; + stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(asock); + + pj_sockaddr remote_addr; + pj_bool_t addr_found = PJ_FALSE; + + // Get remote connected address + for (int i = 0 ; i <= stun_sock->outgoing_nb ; ++i) { + if (stun_sock->outgoing_socks[i].sock == asock) { + pj_sockaddr_cp(&remote_addr, &stun_sock->outgoing_socks[i].addr); + addr_found = PJ_TRUE; + break; + } + } + if (!addr_found) + return PJ_FALSE; + + pj_stun_session_cb *cb = pj_stun_session_callback(stun_sock->stun_sess); + if (!cb->on_peer_connection) + return PJ_FALSE; + + + if (status == PJ_SUCCESS) { + status = pj_activesock_start_read(asock, stun_sock->pool, + stun_sock->cfg.max_pkt_size, 0); + } + (cb->on_peer_connection)(stun_sock->stun_sess, status, &remote_addr); + return status != PJ_SUCCESS; +} + +#endif + /* This callback is called by the STUN session to send packet */ static pj_status_t sess_on_send_msg(pj_stun_session *sess, void *token, @@ -786,6 +1555,7 @@ static pj_status_t sess_on_send_msg(pj_stun_session *sess, { pj_stun_sock *stun_sock; pj_ssize_t size; + pj_status_t status; stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); if (!stun_sock || !stun_sock->active_sock) { @@ -799,12 +1569,33 @@ static pj_status_t sess_on_send_msg(pj_stun_session *sess, PJ_UNUSED_ARG(token); size = pkt_size; - return pj_activesock_sendto(stun_sock->active_sock, - &stun_sock->int_send_key, - pkt, &size, 0, dst_addr, addr_len); + if (stun_sock->conn_type == PJ_STUN_TP_UDP) { + status = pj_activesock_sendto(stun_sock->active_sock, + &stun_sock->int_send_key,pkt, &size, 0, + dst_addr, addr_len); + } +#if PJ_HAS_TCP + else { + for (int i = 0 ; i <= stun_sock->incoming_nb; ++i) { + if (stun_sock->incoming_socks[i].sock != NULL + && !pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, dst_addr)) { + status = pj_activesock_send(stun_sock->incoming_socks[i].sock, + &stun_sock->int_send_key, + pkt, &size, 0); + if (status != PJ_SUCCESS && status != PJ_EPENDING) + PJ_PERROR(4,(stun_sock->obj_name, status, + "Error sending answer on incoming_sock(s)")); + } + } + /* last attempt */ + status = pj_activesock_send(stun_sock->active_sock, + &stun_sock->int_send_key, pkt, &size, 0); + } +#endif + return status; } -/* This callback is called by the STUN session when outgoing transaction +/* This callback is called by the STUN session when outgoing transaction * is complete */ static void sess_on_request_complete(pj_stun_session *sess, @@ -860,16 +1651,16 @@ static void sess_on_request_complete(pj_stun_session *sess, } /* Determine if mapped address has changed, and save the new mapped - * address and call callback if so + * address and call callback if so */ mapped_changed = !pj_sockaddr_has_addr(&stun_sock->mapped_addr) || - pj_sockaddr_cmp(&stun_sock->mapped_addr, + pj_sockaddr_cmp(&stun_sock->mapped_addr, &mapped_attr->sockaddr) != 0; if (mapped_changed) { /* Print mapped adress */ { char addrinfo[PJ_INET6_ADDRSTRLEN+10]; - PJ_LOG(4,(stun_sock->obj_name, + PJ_LOG(4,(stun_sock->obj_name, "STUN mapped address found/changed: %s", pj_sockaddr_print(&mapped_attr->sockaddr, addrinfo, sizeof(addrinfo), 3))); @@ -941,8 +1732,6 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, pj_status_t status) { pj_stun_sock *stun_sock; - pj_stun_msg_hdr *hdr; - pj_uint16_t type; stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock); if (!stun_sock) @@ -954,58 +1743,7 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, return PJ_TRUE; } - pj_grp_lock_acquire(stun_sock->grp_lock); - - /* Check that this is STUN message */ - status = pj_stun_msg_check((const pj_uint8_t*)data, size, - PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET); - if (status != PJ_SUCCESS) { - /* Not STUN -- give it to application */ - goto process_app_data; - } - - /* Treat packet as STUN header and copy the STUN message type. - * We don't want to access the type directly from the header - * since it may not be properly aligned. - */ - hdr = (pj_stun_msg_hdr*) data; - pj_memcpy(&type, &hdr->type, 2); - type = pj_ntohs(type); - - /* If the packet is a STUN Binding response and part of the - * transaction ID matches our internal ID, then this is - * our internal STUN message (Binding request or keep alive). - * Give it to our STUN session. - */ - if (!PJ_STUN_IS_RESPONSE(type) || - PJ_STUN_GET_METHOD(type) != PJ_STUN_BINDING_METHOD || - pj_memcmp(hdr->tsx_id, stun_sock->tsx_id, 10) != 0) - { - /* Not STUN Binding response, or STUN transaction ID mismatch. - * This is not our message too -- give it to application. - */ - goto process_app_data; - } - - /* This is our STUN Binding response. Give it to the STUN session */ - status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, data, size, - PJ_STUN_IS_DATAGRAM, NULL, NULL, - src_addr, addr_len); - - status = pj_grp_lock_release(stun_sock->grp_lock); - - return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; - -process_app_data: - if (stun_sock->cb.on_rx_data) { - (*stun_sock->cb.on_rx_data)(stun_sock, data, (unsigned)size, - src_addr, addr_len); - status = pj_grp_lock_release(stun_sock->grp_lock); - return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; - } - - status = pj_grp_lock_release(stun_sock->grp_lock); - return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; + return parse_rx_packet(asock, data, size, src_addr, addr_len); } /* Callback from active socket about send status */ @@ -1031,7 +1769,7 @@ static pj_bool_t on_data_sent(pj_activesock_t *asock, pj_grp_lock_acquire(stun_sock->grp_lock); /* If app gives NULL send_key in sendto() function, then give - * NULL in the callback too + * NULL in the callback too */ if (send_key == &stun_sock->send_key) send_key = NULL; @@ -1046,3 +1784,7 @@ static pj_bool_t on_data_sent(pj_activesock_t *asock, return PJ_TRUE; } +pj_stun_session* pj_stun_sock_get_session(pj_stun_sock *stun_sock) +{ + return stun_sock ? stun_sock->stun_sess : NULL; +} diff --git a/pjnath/src/pjnath/stun_transaction.c b/pjnath/src/pjnath/stun_transaction.c index 71c407b06..a367e6704 100644 --- a/pjnath/src/pjnath/stun_transaction.c +++ b/pjnath/src/pjnath/stun_transaction.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include @@ -61,9 +61,9 @@ struct pj_stun_client_tsx #endif -static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, +static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, pj_timer_entry *timer); -static void destroy_timer_callback(pj_timer_heap_t *timer_heap, +static void destroy_timer_callback(pj_timer_heap_t *timer_heap, pj_timer_entry *timer); /* @@ -355,7 +355,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx, /* Retransmit timer callback */ -static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, +static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, pj_timer_entry *timer) { pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data; @@ -411,6 +411,9 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx, pj_bool_t mod_count) { + if (!tsx) + return PJ_EINVAL; + if (tsx->destroy_timer.id != 0 || tsx->is_destroying) return PJ_SUCCESS; @@ -423,7 +426,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx, } /* Timer callback to destroy transaction */ -static void destroy_timer_callback(pj_timer_heap_t *timer_heap, +static void destroy_timer_callback(pj_timer_heap_t *timer_heap, pj_timer_entry *timer) { pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data; @@ -449,23 +452,23 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, pj_status_t status; /* Must be STUN response message */ - if (!PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) && + if (!PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) && !PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) { - PJ_LOG(4,(tsx->obj_name, + PJ_LOG(4,(tsx->obj_name, "STUN rx_msg() error: not response message")); return PJNATH_EINSTUNMSGTYPE; } - /* We have a response with matching transaction ID. + /* We have a response with matching transaction ID. * We can cancel retransmit timer now. */ pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer, TIMER_INACTIVE); /* Find STUN error code attribute */ - err_attr = (pj_stun_errcode_attr*) + err_attr = (pj_stun_errcode_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ERROR_CODE, 0); if (err_attr && err_attr->err_code <= 200) { @@ -473,7 +476,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, * Any response between 100 and 299 MUST result in the cessation * of request retransmissions, but otherwise is discarded. */ - PJ_LOG(4,(tsx->obj_name, + PJ_LOG(4,(tsx->obj_name, "STUN rx_msg() error: received provisional %d code (%.*s)", err_attr->err_code, (int)err_attr->reason.slen, diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c index e85e971f8..279e1bb82 100644 --- a/pjnath/src/pjnath/turn_session.c +++ b/pjnath/src/pjnath/turn_session.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include @@ -35,7 +35,7 @@ #define PJ_TURN_CHANNEL_HTABLE_SIZE 8 #define PJ_TURN_PERM_HTABLE_SIZE 8 -static const char *state_names[] = +static const char *state_names[] = { "Null", "Resolving", @@ -95,8 +95,8 @@ struct perm_t /* The permission expiration */ pj_time_val expiry; - /* Arbitrary/random pointer value (token) to map this perm with the - * request to create it. It is used to invalidate this perm when the + /* Arbitrary/random pointer value (token) to map this perm with the + * request to create it. It is used to invalidate this perm when the * request fails. */ void *req_token; @@ -219,7 +219,7 @@ PJ_DEF(void) pj_turn_alloc_param_default(pj_turn_alloc_param *prm) /* * Duplicate pj_turn_alloc_param. */ -PJ_DEF(void) pj_turn_alloc_param_copy( pj_pool_t *pool, +PJ_DEF(void) pj_turn_alloc_param_copy( pj_pool_t *pool, pj_turn_alloc_param *dst, const pj_turn_alloc_param *src) { @@ -311,7 +311,7 @@ PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg, stun_cb.on_request_complete = &stun_on_request_complete; stun_cb.on_rx_indication = &stun_on_rx_indication; status = pj_stun_session_create(&sess->stun_cfg, sess->obj_name, &stun_cb, - PJ_FALSE, sess->grp_lock, &sess->stun); + PJ_FALSE, sess->grp_lock, &sess->stun, conn_type); if (status != PJ_SUCCESS) { do_destroy(sess); return status; @@ -509,9 +509,9 @@ PJ_DEF(pj_status_t) pj_turn_session_get_info( pj_turn_session *sess, else pj_bzero(&info->server, sizeof(info->server)); - pj_memcpy(&info->mapped_addr, &sess->mapped_addr, + pj_memcpy(&info->mapped_addr, &sess->mapped_addr, sizeof(sess->mapped_addr)); - pj_memcpy(&info->relay_addr, &sess->relay_addr, + pj_memcpy(&info->relay_addr, &sess->relay_addr, sizeof(sess->relay_addr)); return PJ_SUCCESS; @@ -594,7 +594,7 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess, /* See if "domain" contains just IP address */ tmp_addr.addr.sa_family = sess->af; - status = pj_inet_pton(sess->af, domain, + status = pj_inet_pton(sess->af, domain, pj_sockaddr_get_addr(&tmp_addr)); is_ip_addr = (status == PJ_SUCCESS); @@ -647,8 +647,8 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess, /* Add reference before async DNS resolution */ pj_grp_lock_add_ref(sess->grp_lock); - status = pj_dns_srv_resolve(domain, &res_name, default_port, - sess->pool, resolver, opt, sess, + status = pj_dns_srv_resolve(domain, &res_name, default_port, + sess->pool, resolver, opt, sess, &dns_srv_resolver_cb, NULL); if (status != PJ_SUCCESS) { set_state(sess, PJ_TURN_STATE_NULL); @@ -664,9 +664,9 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess, unsigned i, cnt; /* Default port must be specified */ - PJ_ASSERT_ON_FAIL(default_port>0 && default_port<65536, + PJ_ASSERT_ON_FAIL(default_port>0 && default_port<65536, {status=PJ_EINVAL; goto on_return;}); - + sess->default_port = (pj_uint16_t)default_port; cnt = PJ_TURN_MAX_DNS_SRV_CNT; @@ -689,7 +689,7 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess, sess->srv_addr_cnt = (pj_uint16_t)cnt; sess->srv_addr_list = (pj_sockaddr*) - pj_pool_calloc(sess->pool, cnt, + pj_pool_calloc(sess->pool, cnt, sizeof(pj_sockaddr)); for (i=0; isrv_addr_list[i]; @@ -738,8 +738,8 @@ PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess, pj_status_t status; PJ_ASSERT_RETURN(sess, PJ_EINVAL); - PJ_ASSERT_RETURN(sess->state>PJ_TURN_STATE_NULL && - sess->state<=PJ_TURN_STATE_RESOLVED, + PJ_ASSERT_RETURN(sess->state>PJ_TURN_STATE_NULL && + sess->state<=PJ_TURN_STATE_RESOLVED, PJ_EINVALIDOP); PJ_ASSERT_RETURN(!param || param->peer_conn_type == PJ_TURN_TP_UDP || param->peer_conn_type == PJ_TURN_TP_TCP, @@ -753,7 +753,7 @@ PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess, pj_grp_lock_acquire(sess->grp_lock); - if (param && param != &sess->alloc_param) + if (param && param != &sess->alloc_param) pj_turn_alloc_param_copy(sess->pool, &sess->alloc_param, param); if (sess->state < PJ_TURN_STATE_RESOLVED) { @@ -769,7 +769,7 @@ PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess, /* Ready to allocate */ pj_assert(sess->state == PJ_TURN_STATE_RESOLVED); - + /* Create a bare request */ status = pj_stun_session_create_req(sess->stun, PJ_STUN_ALLOCATE_REQUEST, PJ_STUN_MAGIC, NULL, &tdata); @@ -820,9 +820,9 @@ PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess, /* Send request */ set_state(sess, PJ_TURN_STATE_ALLOCATING); retransmit = (sess->conn_type == PJ_TURN_TP_UDP); - status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, + status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, retransmit, sess->srv_addr, - pj_sockaddr_get_len(sess->srv_addr), + pj_sockaddr_get_len(sess->srv_addr), tdata); if (status != PJ_SUCCESS) { /* Set state back to RESOLVED. We don't want to destroy session now, @@ -856,7 +856,7 @@ PJ_DEF(pj_status_t) pj_turn_session_set_perm( pj_turn_session *sess, pj_grp_lock_acquire(sess->grp_lock); /* Create a bare CreatePermission request */ - status = pj_stun_session_create_req(sess->stun, + status = pj_stun_session_create_req(sess->stun, PJ_STUN_CREATE_PERM_REQUEST, PJ_STUN_MAGIC, NULL, &tdata); if (status != PJ_SUCCESS) { @@ -905,10 +905,10 @@ PJ_DEF(pj_status_t) pj_turn_session_set_perm( pj_turn_session *sess, } /* Send the request */ - status = pj_stun_session_send_msg(sess->stun, req_token, PJ_FALSE, + status = pj_stun_session_send_msg(sess->stun, req_token, PJ_FALSE, (sess->conn_type==PJ_TURN_TP_UDP), sess->srv_addr, - pj_sockaddr_get_len(sess->srv_addr), + pj_sockaddr_get_len(sess->srv_addr), tdata); if (status != PJ_SUCCESS) { /* tdata is already destroyed */ @@ -964,10 +964,10 @@ static void send_refresh(pj_turn_session *sess, int lifetime) set_state(sess, PJ_TURN_STATE_DEALLOCATING); } - status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, + status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, (sess->conn_type==PJ_TURN_TP_UDP), sess->srv_addr, - pj_sockaddr_get_len(sess->srv_addr), + pj_sockaddr_get_len(sess->srv_addr), tdata); if (status != PJ_SUCCESS) goto on_error; @@ -985,17 +985,29 @@ on_error: /** * Relay data to the specified peer through the session. */ + PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_sockaddr_t *addr, unsigned addr_len) +{ + unsigned sent; + return pj_turn_session_sendto2(sess, pkt, pkt_len, addr, addr_len, &sent); +} + +PJ_DEF(pj_status_t) pj_turn_session_sendto2(pj_turn_session *sess, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_sockaddr_t *addr, + unsigned addr_len, + unsigned *sent) { struct ch_t *ch; struct perm_t *perm; pj_status_t status; - PJ_ASSERT_RETURN(sess && pkt && pkt_len && addr && addr_len, + PJ_ASSERT_RETURN(sess && pkt && pkt_len && addr && addr_len, PJ_EINVAL); /* Return error if we're not ready */ @@ -1012,11 +1024,11 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, /* Permission doesn't exist, install it first */ char ipstr[PJ_INET6_ADDRSTRLEN+2]; - PJ_LOG(4,(sess->obj_name, + PJ_LOG(4,(sess->obj_name, "sendto(): IP %s has no permission, requesting it first..", pj_sockaddr_print(addr, ipstr, sizeof(ipstr), 2))); - status = pj_turn_session_set_perm(sess, 1, (const pj_sockaddr*)addr, + status = pj_turn_session_set_perm(sess, 1, (const pj_sockaddr*)addr, 0); if (status != PJ_SUCCESS) { pj_grp_lock_release(sess->grp_lock); @@ -1026,19 +1038,19 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, /* If peer connection is TCP (RFC 6062), send it directly */ if (sess->alloc_param.peer_conn_type == PJ_TURN_TP_TCP) { - status = sess->cb.on_send_pkt(sess, pkt, pkt_len, addr, addr_len); + status = sess->cb.on_send_pkt2(sess, pkt, pkt_len, addr, addr_len, sent, pkt_len); goto on_return; } /* See if the peer is bound to a channel number */ - ch = lookup_ch_by_addr(sess, addr, pj_sockaddr_get_len(addr), + ch = lookup_ch_by_addr(sess, addr, pj_sockaddr_get_len(addr), PJ_FALSE, PJ_FALSE); if (ch && ch->num != PJ_TURN_INVALID_CHANNEL && ch->bound) { unsigned total_len; /* Peer is assigned a channel number, we can use ChannelData */ pj_turn_channel_data *cd = (pj_turn_channel_data*)sess->tx_pkt; - + pj_assert(sizeof(*cd)==4); /* Calculate total length, including paddings */ @@ -1052,11 +1064,10 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, cd->length = pj_htons((pj_uint16_t)pkt_len); pj_memcpy(cd+1, pkt, pkt_len); - pj_assert(sess->srv_addr != NULL); - - status = sess->cb.on_send_pkt(sess, sess->tx_pkt, total_len, - sess->srv_addr, - pj_sockaddr_get_len(sess->srv_addr)); + status = sess->cb.on_send_pkt2(sess, sess->tx_pkt, total_len, + sess->srv_addr, + pj_sockaddr_get_len(sess->srv_addr), + sent, pkt_len); } else { /* Use Send Indication. */ @@ -1070,7 +1081,7 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, /* Create blank SEND-INDICATION */ status = pj_stun_msg_init(&send_ind, PJ_STUN_SEND_INDICATION, - PJ_STUN_MAGIC, + PJ_STUN_MAGIC, (const pj_uint8_t*)sess->send_ind_tsx_id); if (status != PJ_SUCCESS) goto on_return; @@ -1087,17 +1098,18 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, pj_stun_msg_add_attr(&send_ind, (pj_stun_attr_hdr*)&data_attr); /* Encode the message */ - status = pj_stun_msg_encode(&send_ind, sess->tx_pkt, + status = pj_stun_msg_encode(&send_ind, sess->tx_pkt, sizeof(sess->tx_pkt), 0, NULL, &send_ind_len); if (status != PJ_SUCCESS) goto on_return; /* Send the Send Indication */ - status = sess->cb.on_send_pkt(sess, sess->tx_pkt, - (unsigned)send_ind_len, - sess->srv_addr, - pj_sockaddr_get_len(sess->srv_addr)); + status = sess->cb.on_send_pkt2(sess, sess->tx_pkt, + (unsigned)send_ind_len, + sess->srv_addr, + pj_sockaddr_get_len(sess->srv_addr), + sent, pkt_len); } on_return: @@ -1124,7 +1136,7 @@ PJ_DEF(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess, pj_grp_lock_acquire(sess->grp_lock); /* Create blank ChannelBind request */ - status = pj_stun_session_create_req(sess->stun, + status = pj_stun_session_create_req(sess->stun, PJ_STUN_CHANNEL_BIND_REQUEST, PJ_STUN_MAGIC, NULL, &tdata); if (status != PJ_SUCCESS) @@ -1139,7 +1151,7 @@ PJ_DEF(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess, /* Channel is already bound. This is a refresh request. */ ch_num = ch->num; } else { - PJ_ASSERT_ON_FAIL(sess->next_ch <= PJ_TURN_CHANNEL_MAX, + PJ_ASSERT_ON_FAIL(sess->next_ch <= PJ_TURN_CHANNEL_MAX, {status=PJ_ETOOMANY; goto on_return;}); ch->num = ch_num = sess->next_ch++; } @@ -1154,10 +1166,10 @@ PJ_DEF(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess, PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE, peer_adr, addr_len); - /* Send the request, associate peer data structure with tdata + /* Send the request, associate peer data structure with tdata * for future reference when we receive the ChannelBind response. */ - status = pj_stun_session_send_msg(sess->stun, ch, PJ_FALSE, + status = pj_stun_session_send_msg(sess->stun, ch, PJ_FALSE, (sess->conn_type==PJ_TURN_TP_UDP), sess->srv_addr, pj_sockaddr_get_len(sess->srv_addr), @@ -1190,7 +1202,7 @@ PJ_DEF(pj_status_t) pj_turn_session_connection_bind( pj_grp_lock_acquire(sess->grp_lock); /* Create blank ConnectionBind request */ - status = pj_stun_session_create_req(sess->stun, + status = pj_stun_session_create_req(sess->stun, PJ_STUN_CONNECTION_BIND_REQUEST, PJ_STUN_MAGIC, NULL, &tdata); if (status != PJ_SUCCESS) @@ -1206,7 +1218,7 @@ PJ_DEF(pj_status_t) pj_turn_session_connection_bind( pj_sockaddr_cp(&conn_bind->peer_addr, peer_addr); conn_bind->peer_addr_len = addr_len; - /* Send the request, associate connection data structure with tdata + /* Send the request, associate connection data structure with tdata * for future reference when we receive the ConnectionBind response. */ status = pj_stun_session_send_msg(sess->stun, conn_bind, PJ_FALSE, @@ -1259,7 +1271,7 @@ PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess, { pj_turn_session_on_rx_pkt_param prm; pj_status_t status; - + pj_bzero(&prm, sizeof(prm)); prm.pkt = pkt; prm.pkt_len = pkt_len; @@ -1349,7 +1361,7 @@ PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt2( /* Notify application */ if (sess->cb.on_rx_data) { - (*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)prm->pkt)+sizeof(cd), + (*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)prm->pkt)+sizeof(cd), cd.length, &ch->addr, pj_sockaddr_get_len(&ch->addr)); } @@ -1394,7 +1406,7 @@ static pj_status_t stun_on_send_msg(pj_stun_session *stun, * Handle failed ALLOCATE or REFRESH request. This may switch to alternate * server if we have one. */ -static void on_session_fail( pj_turn_session *sess, +static void on_session_fail( pj_turn_session *sess, enum pj_stun_method_e method, pj_status_t status, const pj_str_t *reason) @@ -1415,12 +1427,12 @@ static void on_session_fail( pj_turn_session *sess, pj_stun_get_method_name(method), (int)reason->slen, reason->ptr)); - /* If this is ALLOCATE response and we don't have more server + /* If this is ALLOCATE response and we don't have more server * addresses to try, notify application and destroy the TURN * session. */ if (method==PJ_STUN_ALLOCATE_METHOD && - sess->srv_addr == &sess->srv_addr_list[sess->srv_addr_cnt-1]) + sess->srv_addr == &sess->srv_addr_list[sess->srv_addr_cnt-1]) { set_state(sess, PJ_TURN_STATE_DEALLOCATED); @@ -1451,7 +1463,7 @@ static void on_session_fail( pj_turn_session *sess, /* * Handle successful response to ALLOCATE or REFRESH request. */ -static void on_allocate_success(pj_turn_session *sess, +static void on_allocate_success(pj_turn_session *sess, enum pj_stun_method_e method, const pj_stun_msg *msg) { @@ -1528,10 +1540,10 @@ static void on_allocate_success(pj_turn_session *sess, "RELAY-ADDRESS attribute")); return; } - + /* Save relayed address */ if (raddr_attr) { - /* If we already have relay address, check if the relay address + /* If we already have relay address, check if the relay address * in the response matches our relay address. */ if (pj_sockaddr_has_addr(&sess->relay_addr)) { @@ -1543,7 +1555,7 @@ static void on_allocate_success(pj_turn_session *sess, } } else { /* Otherwise save the relayed address */ - pj_memcpy(&sess->relay_addr, &raddr_attr->sockaddr, + pj_memcpy(&sess->relay_addr, &raddr_attr->sockaddr, sizeof(pj_sockaddr)); } } @@ -1591,7 +1603,7 @@ static void stun_on_request_complete(pj_stun_session *stun, unsigned src_addr_len) { pj_turn_session *sess; - enum pj_stun_method_e method = (enum pj_stun_method_e) + enum pj_stun_method_e method = (enum pj_stun_method_e) PJ_STUN_GET_METHOD(tdata->msg->hdr.type); PJ_UNUSED_ARG(src_addr); @@ -1612,8 +1624,8 @@ static void stun_on_request_complete(pj_stun_session *stun, } /* Handle ALLOCATE response */ - if (status==PJ_SUCCESS && - PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) + if (status==PJ_SUCCESS && + PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) { /* Successful Allocate response */ @@ -1641,8 +1653,8 @@ static void stun_on_request_complete(pj_stun_session *stun, } else if (method == PJ_STUN_REFRESH_METHOD) { /* Handle Refresh response */ - if (status==PJ_SUCCESS && - PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) + if (status==PJ_SUCCESS && + PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) { /* Success, schedule next refresh. */ on_allocate_success(sess, method, response); @@ -1670,8 +1682,8 @@ static void stun_on_request_complete(pj_stun_session *stun, } else if (method == PJ_STUN_CHANNEL_BIND_METHOD) { /* Handle ChannelBind response */ - if (status==PJ_SUCCESS && - PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) + if (status==PJ_SUCCESS && + PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) { /* Successful ChannelBind response */ struct ch_t *ch = (struct ch_t*)token; @@ -1720,8 +1732,8 @@ static void stun_on_request_complete(pj_stun_session *stun, } else if (method == PJ_STUN_CREATE_PERM_METHOD) { /* Handle CreatePermission response */ - if (status==PJ_SUCCESS && - PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) + if (status==PJ_SUCCESS && + PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) { /* No special handling when the request is successful. */ } else { @@ -1740,7 +1752,7 @@ static void stun_on_request_complete(pj_stun_session *stun, const pj_stun_errcode_attr *eattr; eattr = (const pj_stun_errcode_attr*) - pj_stun_msg_find_attr(response, + pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0); if (eattr) { err_code = eattr->err_code; @@ -1761,9 +1773,9 @@ static void stun_on_request_complete(pj_stun_session *stun, it = pj_hash_next(sess->perm_table, it); if (perm->req_token == token) { - PJ_LOG(1,(sess->obj_name, + PJ_LOG(1,(sess->obj_name, "CreatePermission failed for IP %s: %d/%.*s", - pj_sockaddr_print(&perm->addr, ipstr, + pj_sockaddr_print(&perm->addr, ipstr, sizeof(ipstr), 2), err_code, (int)reason.slen, reason.ptr)); @@ -1784,7 +1796,7 @@ static void stun_on_request_complete(pj_stun_session *stun, struct conn_bind_t *conn_bind = (struct conn_bind_t*)token; if (status != PJ_SUCCESS || - !PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) + !PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) { pj_str_t reason = {0}; if (status == PJ_SUCCESS) { @@ -1898,7 +1910,7 @@ static pj_status_t stun_on_rx_indication(pj_stun_session *stun, /* Must have both XOR-PEER-ADDRESS and CONNECTION-ID attributes */ if (!peer_attr || !connection_id_attr) { - PJ_LOG(4,(sess->obj_name, + PJ_LOG(4,(sess->obj_name, "Received ConnectionAttempt indication with missing " "attributes")); return PJ_EINVALIDOP; @@ -1940,14 +1952,14 @@ static pj_status_t stun_on_rx_indication(pj_stun_session *stun, /* Must have both XOR-PEER-ADDRESS and DATA attributes */ if (!peer_attr || !data_attr) { - PJ_LOG(4,(sess->obj_name, + PJ_LOG(4,(sess->obj_name, "Received Data indication with missing attributes")); return PJ_EINVALIDOP; } /* Notify application */ if (sess->cb.on_rx_data) { - (*sess->cb.on_rx_data)(sess, data_attr->data, data_attr->length, + (*sess->cb.on_rx_data)(sess, data_attr->data, data_attr->length, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); } @@ -1985,15 +1997,15 @@ static void dns_srv_resolver_cb(void *user_data, /* Allocate server entries */ sess->srv_addr_list = (pj_sockaddr*) - pj_pool_calloc(sess->pool, tot_cnt, + pj_pool_calloc(sess->pool, tot_cnt, sizeof(pj_sockaddr)); /* Copy results to server entries */ for (i=0, cnt=0; icount && cntentry[i].server.addr_count && - cntentry[i].server.addr_count && + cntentry[i].server.addr[j].af == sess->af) { pj_sockaddr *addr = &sess->srv_addr_list[cnt]; @@ -2041,7 +2053,7 @@ static struct ch_t *lookup_ch_by_addr(pj_turn_session *sess, pj_uint32_t hval = 0; struct ch_t *ch; - ch = (struct ch_t*) + ch = (struct ch_t*) pj_hash_get(sess->ch_table, addr, addr_len, &hval); if (ch == NULL && update) { ch = PJ_POOL_ZALLOC_T(sess->pool, struct ch_t); @@ -2062,7 +2074,7 @@ static struct ch_t *lookup_ch_by_addr(pj_turn_session *sess, /* Register by channel number */ pj_assert(ch->num != PJ_TURN_INVALID_CHANNEL && ch->bound); - if (pj_hash_get(sess->ch_table, &ch->num, + if (pj_hash_get(sess->ch_table, &ch->num, sizeof(ch->num), &hval2)==0) { pj_hash_set(sess->pool, sess->ch_table, &ch->num, sizeof(ch->num), hval2, ch); @@ -2089,7 +2101,7 @@ static struct ch_t *lookup_ch_by_addr(pj_turn_session *sess, static struct ch_t *lookup_ch_by_chnum(pj_turn_session *sess, pj_uint16_t chnum) { - return (struct ch_t*) pj_hash_get(sess->ch_table, &chnum, + return (struct ch_t*) pj_hash_get(sess->ch_table, &chnum, sizeof(chnum), NULL); } @@ -2114,7 +2126,7 @@ static struct perm_t *lookup_perm(pj_turn_session *sess, } /* lookup and create if it doesn't exist and wanted */ - perm = (struct perm_t*) + perm = (struct perm_t*) pj_hash_get(sess->perm_table, addr, addr_len, &hval); if (perm == NULL && update) { perm = PJ_POOL_ZALLOC_T(sess->pool, struct perm_t); @@ -2147,7 +2159,7 @@ static void invalidate_perm(pj_turn_session *sess, /* * Scan permission's hash table to refresh the permission. */ -static unsigned refresh_permissions(pj_turn_session *sess, +static unsigned refresh_permissions(pj_turn_session *sess, const pj_time_val *now) { pj_stun_tx_data *tdata = NULL; @@ -2169,7 +2181,7 @@ static unsigned refresh_permissions(pj_turn_session *sess, if (tdata == NULL) { /* Create a bare CreatePermission request */ status = pj_stun_session_create_req( - sess->stun, + sess->stun, PJ_STUN_CREATE_PERM_REQUEST, PJ_STUN_MAGIC, NULL, &tdata); if (status != PJ_SUCCESS) { @@ -2185,7 +2197,7 @@ static unsigned refresh_permissions(pj_turn_session *sess, } status = pj_stun_msg_add_sockaddr_attr( - tdata->pool, + tdata->pool, tdata->msg, PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE, @@ -2211,10 +2223,10 @@ static unsigned refresh_permissions(pj_turn_session *sess, } if (tdata) { - status = pj_stun_session_send_msg(sess->stun, req_token, PJ_FALSE, + status = pj_stun_session_send_msg(sess->stun, req_token, PJ_FALSE, (sess->conn_type==PJ_TURN_TP_UDP), sess->srv_addr, - pj_sockaddr_get_len(sess->srv_addr), + pj_sockaddr_get_len(sess->srv_addr), tdata); if (status != PJ_SUCCESS) { PJ_PERROR(1,(sess->obj_name, status, @@ -2241,7 +2253,7 @@ static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e) eid = (enum timer_id_t) e->id; e->id = TIMER_NONE; - + if (eid == TIMER_KEEP_ALIVE) { pj_time_val now; pj_hash_iterator_t itbuf, *it; @@ -2270,11 +2282,11 @@ static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e) /* Scan hash table to refresh bound channels */ it = pj_hash_first(sess->ch_table, &itbuf); while (it) { - struct ch_t *ch = (struct ch_t*) + struct ch_t *ch = (struct ch_t*) pj_hash_this(sess->ch_table, it); if (ch->bound && PJ_TIME_VAL_LTE(ch->expiry, now)) { - /* Send ChannelBind to refresh channel binding and + /* Send ChannelBind to refresh channel binding and * permission. */ pj_turn_session_bind_channel(sess, &ch->addr, @@ -2297,7 +2309,7 @@ static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e) pj_status_t rc; /* Create blank SEND-INDICATION */ - rc = pj_stun_session_create_ind(sess->stun, + rc = pj_stun_session_create_ind(sess->stun, PJ_STUN_SEND_INDICATION, &tdata); if (rc == PJ_SUCCESS) { /* Add DATA attribute with zero length */ @@ -2305,7 +2317,7 @@ static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e) PJ_STUN_ATTR_DATA, NULL, 0); /* Send the indication */ - pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, + pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, PJ_FALSE, sess->srv_addr, pj_sockaddr_get_len(sess->srv_addr), tdata); diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c index e273e6e28..ada864c85 100644 --- a/pjnath/src/pjnath/turn_sock.c +++ b/pjnath/src/pjnath/turn_sock.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include @@ -99,6 +99,10 @@ struct pj_turn_sock /* Data connection, when peer_conn_type==PJ_TURN_TP_TCP (RFC 6062) */ unsigned data_conn_cnt; tcp_data_conn_t data_conn[PJ_TURN_MAX_TCP_CONN_CNT]; + + /* The following variables are used by the on_data_sent callback */ + unsigned current_pkt_len; + unsigned current_body_len; }; @@ -115,6 +119,13 @@ static pj_status_t turn_on_stun_send_pkt(pj_turn_session *sess, unsigned pkt_len, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len); +static pj_status_t turn_on_send_pkt2(pj_turn_session *sess, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_sockaddr_t *dst_addr, + unsigned dst_addr_len, + unsigned *sent, + unsigned body_len); static void turn_on_channel_bound(pj_turn_session *sess, const pj_sockaddr_t *peer_addr, unsigned addr_len, @@ -124,7 +135,7 @@ static void turn_on_rx_data(pj_turn_session *sess, unsigned pkt_len, const pj_sockaddr_t *peer_addr, unsigned addr_len); -static void turn_on_state(pj_turn_session *sess, +static void turn_on_state(pj_turn_session *sess, pj_turn_state_t old_state, pj_turn_state_t new_state); static void turn_on_connection_attempt(pj_turn_session *sess, @@ -252,7 +263,7 @@ PJ_DEF(void) pj_turn_sock_tls_cfg_wipe_keys(pj_turn_sock_tls_cfg *tls_cfg) wipe_buf(&tls_cfg->password); wipe_buf(&tls_cfg->ca_buf); wipe_buf(&tls_cfg->cert_buf); - wipe_buf(&tls_cfg->privkey_buf); + wipe_buf(&tls_cfg->privkey_buf); } #endif @@ -349,6 +360,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg, pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_send_pkt = &turn_on_send_pkt; sess_cb.on_stun_send_pkt = &turn_on_stun_send_pkt; + sess_cb.on_send_pkt2 = &turn_on_send_pkt2; sess_cb.on_channel_bound = &turn_on_channel_bound; sess_cb.on_rx_data = &turn_on_rx_data; sess_cb.on_state = &turn_on_state; @@ -545,7 +557,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_unlock(pj_turn_sock *turn_sock) } /* - * Set STUN message logging for this TURN session. + * Set STUN message logging for this TURN session. */ PJ_DEF(void) pj_turn_sock_set_log( pj_turn_sock *turn_sock, unsigned flags) @@ -579,7 +591,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock, pj_grp_lock_acquire(turn_sock->grp_lock); - /* Copy alloc param. We will call session_alloc() only after the + /* Copy alloc param. We will call session_alloc() only after the * server address has been resolved. */ if (param) { @@ -643,7 +655,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_set_perm( pj_turn_sock *turn_sock, /* * Send packet. - */ + */ PJ_DEF(pj_status_t) pj_turn_sock_sendto( pj_turn_sock *turn_sock, const pj_uint8_t *pkt, unsigned pkt_len, @@ -659,10 +671,26 @@ PJ_DEF(pj_status_t) pj_turn_sock_sendto( pj_turn_sock *turn_sock, * to store our actual data length to be sent here. */ turn_sock->body_len = pkt_len; - return pj_turn_session_sendto(turn_sock->sess, pkt, pkt_len, + return pj_turn_session_sendto(turn_sock->sess, pkt, pkt_len, addr, addr_len); } +PJ_DEF(pj_status_t) pj_turn_sock_sendto2( pj_turn_sock *turn_sock, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_sockaddr_t *addr, + unsigned addr_len, + unsigned *sent) +{ + PJ_ASSERT_RETURN(turn_sock && addr && addr_len, PJ_EINVAL); + + if (turn_sock->sess == NULL) + return PJ_EINVALIDOP; + + return pj_turn_session_sendto2(turn_sock->sess, pkt, pkt_len, + addr, addr_len, sent); +} + /* * Bind a peer address to a channel number. */ @@ -759,10 +787,10 @@ static pj_bool_t on_connect_complete(pj_turn_sock *turn_sock, } /* Kick start pending read operation */ - if (turn_sock->conn_type != PJ_TURN_TP_TLS) - status = pj_activesock_start_read(turn_sock->active_sock, + if (turn_sock->conn_type != PJ_TURN_TP_TLS) + status = pj_activesock_start_read(turn_sock->active_sock, turn_sock->pool, - turn_sock->setting.max_pkt_size, + turn_sock->setting.max_pkt_size, 0); #if PJ_HAS_SSL_SOCK else @@ -854,7 +882,7 @@ static unsigned has_packet(pj_turn_sock *turn_sock, const void *buf, pj_size_t b pj_memcpy(&cd, buf, sizeof(pj_turn_channel_data)); cd.length = pj_ntohs(cd.length); - if (bufsize >= cd.length+sizeof(cd)) + if (bufsize >= cd.length+sizeof(cd)) return (cd.length+sizeof(cd)+3) & (~3); else return 0; @@ -880,18 +908,18 @@ static pj_bool_t on_data_read(pj_turn_sock *turn_sock, */ unsigned pkt_len; - //PJ_LOG(5,(turn_sock->pool->obj_name, + //PJ_LOG(5,(turn_sock->pool->obj_name, // "Incoming data, %lu bytes total buffer", size)); while ((pkt_len=has_packet(turn_sock, data, size)) != 0) { pj_size_t parsed_len; //const pj_uint8_t *pkt = (const pj_uint8_t*)data; - //PJ_LOG(5,(turn_sock->pool->obj_name, - // "Packet start: %02X %02X %02X %02X", + //PJ_LOG(5,(turn_sock->pool->obj_name, + // "Packet start: %02X %02X %02X %02X", // pkt[0], pkt[1], pkt[2], pkt[3])); - //PJ_LOG(5,(turn_sock->pool->obj_name, + //PJ_LOG(5,(turn_sock->pool->obj_name, // "Processing %lu bytes packet of %lu bytes total buffer", // pkt_len, size)); @@ -912,7 +940,7 @@ static pj_bool_t on_data_read(pj_turn_sock *turn_sock, } size = *remainder; - //PJ_LOG(5,(turn_sock->pool->obj_name, + //PJ_LOG(5,(turn_sock->pool->obj_name, // "Buffer size now %lu bytes", size)); } } else if (status != PJ_SUCCESS) { @@ -956,12 +984,7 @@ static pj_bool_t on_data_sent(pj_turn_sock *turn_sock, } if (turn_sock->cb.on_data_sent) { - pj_ssize_t header_len, sent_size; - - /* Remove the length of packet header from sent size. */ - header_len = turn_sock->pkt_len - turn_sock->body_len; - sent_size = (sent > header_len)? (sent - header_len) : 0; - (*turn_sock->cb.on_data_sent)(turn_sock, sent_size); + (*turn_sock->cb.on_data_sent)(turn_sock, sent); } return PJ_TRUE; @@ -1028,7 +1051,7 @@ static pj_status_t send_pkt(pj_turn_session *sess, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len) { - pj_turn_sock *turn_sock = (pj_turn_sock*) + pj_turn_sock *turn_sock = (pj_turn_sock*) pj_turn_session_get_user_data(sess); pj_ssize_t len = pkt_len; pj_status_t status = PJ_SUCCESS; @@ -1108,6 +1131,74 @@ static pj_status_t turn_on_send_pkt(pj_turn_session *sess, dst_addr, dst_addr_len); } +static pj_status_t turn_on_send_pkt2(pj_turn_session *sess, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_sockaddr_t *dst_addr, + unsigned dst_addr_len, + unsigned *sent, + unsigned body_len) +{ + *sent = pkt_len; + pj_turn_sock *turn_sock = (pj_turn_sock*)pj_turn_session_get_user_data(sess); + pj_status_t status = PJ_SUCCESS; + + pj_ssize_t len = pkt_len; + turn_sock->current_body_len = body_len; + turn_sock->current_pkt_len = pkt_len; + + if (turn_sock == NULL || turn_sock->is_destroying) { + /* We've been destroyed */ + // https://trac.pjsip.org/repos/ticket/1316 + //pj_assert(!"We should shutdown gracefully"); + return PJ_EINVALIDOP; + } + + if (turn_sock->conn_type == PJ_TURN_TP_UDP) { + status = pj_activesock_sendto(turn_sock->active_sock, + &turn_sock->send_key, pkt, &len, 0, + dst_addr, dst_addr_len); + } else if (turn_sock->alloc_param.peer_conn_type == PJ_TURN_TP_TCP) { + pj_turn_session_info info; + pj_turn_session_get_info(turn_sock->sess, &info); + if (pj_sockaddr_cmp(&info.server, dst_addr) == 0) { + /* Destination address is TURN server */ + status = pj_activesock_send(turn_sock->active_sock, + &turn_sock->send_key, pkt, &len, 0); + } else { + /* Destination address is peer, lookup data connection */ + unsigned i; + + status = PJ_ENOTFOUND; + for (i=0; i < PJ_TURN_MAX_TCP_CONN_CNT; ++i) { + tcp_data_conn_t *conn = &turn_sock->data_conn[i]; + if (conn->state < DATACONN_STATE_CONN_BINDING) + continue; + if (pj_sockaddr_cmp(&conn->peer_addr, dst_addr) == 0) { + status = pj_activesock_send(conn->asock, + &conn->send_key, + pkt, &len, 0); + break; + } + } + } + } else { + status = pj_activesock_send(turn_sock->active_sock, + &turn_sock->send_key, pkt, &len, 0); + } + + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + show_err(turn_sock, "socket send()", status); + } + + // Remove header from sent size. + // The application only wants to know if the packet is actually sent. + unsigned header_len = pkt_len - body_len; + *sent = (len > header_len)? (len - header_len) : 0; + + return status; + } + static pj_status_t turn_on_stun_send_pkt(pj_turn_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, @@ -1143,7 +1234,7 @@ static void turn_on_rx_data(pj_turn_session *sess, const pj_sockaddr_t *peer_addr, unsigned addr_len) { - pj_turn_sock *turn_sock = (pj_turn_sock*) + pj_turn_sock *turn_sock = (pj_turn_sock*) pj_turn_session_get_user_data(sess); if (turn_sock == NULL || turn_sock->is_destroying) { /* We've been destroyed */ @@ -1156,7 +1247,7 @@ static void turn_on_rx_data(pj_turn_session *sess, } if (turn_sock->cb.on_rx_data) { - (*turn_sock->cb.on_rx_data)(turn_sock, pkt, pkt_len, + (*turn_sock->cb.on_rx_data)(turn_sock, pkt, pkt_len, peer_addr, addr_len); } } @@ -1165,11 +1256,11 @@ static void turn_on_rx_data(pj_turn_session *sess, /* * Callback from TURN session when state has changed */ -static void turn_on_state(pj_turn_session *sess, +static void turn_on_state(pj_turn_session *sess, pj_turn_state_t old_state, pj_turn_state_t new_state) { - pj_turn_sock *turn_sock = (pj_turn_sock*) + pj_turn_sock *turn_sock = (pj_turn_sock*) pj_turn_session_get_user_data(sess); pj_status_t status = PJ_SUCCESS; @@ -1210,8 +1301,8 @@ static void turn_on_state(pj_turn_session *sess, * we're switching to alternate TURN server when either TCP * connection or ALLOCATE request failed. */ - if ((turn_sock->conn_type != PJ_TURN_TP_TLS) && - (turn_sock->active_sock)) + if ((turn_sock->conn_type != PJ_TURN_TP_TLS) && + (turn_sock->active_sock)) { pj_activesock_close(turn_sock->active_sock); turn_sock->active_sock = NULL; @@ -1241,7 +1332,7 @@ static void turn_on_state(pj_turn_session *sess, max_bind_retry = turn_sock->setting.port_range; } pj_sockaddr_init(turn_sock->af, &bound_addr, NULL, 0); - if (cfg_bind_addr->addr.sa_family == pj_AF_INET() || + if (cfg_bind_addr->addr.sa_family == pj_AF_INET() || cfg_bind_addr->addr.sa_family == pj_AF_INET6()) { pj_sockaddr_cp(&bound_addr, cfg_bind_addr); @@ -1271,7 +1362,7 @@ static void turn_on_state(pj_turn_session *sess, &turn_sock->setting.qos_params, (turn_sock->setting.qos_ignore_error?2:1), turn_sock->pool->obj_name, NULL); - if (status != PJ_SUCCESS && !turn_sock->setting.qos_ignore_error) + if (status != PJ_SUCCESS && !turn_sock->setting.qos_ignore_error) { pj_sock_close(sock); turn_sock_destroy(turn_sock, status); @@ -1331,7 +1422,7 @@ static void turn_on_state(pj_turn_session *sess, sock_type, &asock_cfg, turn_sock->cfg.ioqueue, &asock_cb, turn_sock, - &turn_sock->active_sock); + &turn_sock->active_sock); if (status != PJ_SUCCESS) pj_sock_close(sock); } @@ -1436,8 +1527,8 @@ static void turn_on_state(pj_turn_session *sess, } PJ_LOG(5,(turn_sock->pool->obj_name, - "Connecting to %s", - pj_sockaddr_print(&info.server, addrtxt, + "Connecting to %s", + pj_sockaddr_print(&info.server, addrtxt, sizeof(addrtxt), 3))); /* Initiate non-blocking connect */ @@ -1447,12 +1538,12 @@ static void turn_on_state(pj_turn_session *sess, #if PJ_HAS_TCP else if (turn_sock->conn_type == PJ_TURN_TP_TCP) { status=pj_activesock_start_connect( - turn_sock->active_sock, + turn_sock->active_sock, turn_sock->pool, - &info.server, + &info.server, pj_sockaddr_get_len(&info.server)); - } -#endif + } +#endif #if PJ_HAS_SSL_SOCK else if (turn_sock->conn_type == PJ_TURN_TP_TLS) { pj_ssl_start_connect_param connect_param; @@ -1478,7 +1569,7 @@ static void turn_on_state(pj_turn_session *sess, return; } - /* Done for now. Subsequent work will be done in + /* Done for now. Subsequent work will be done in * on_connect_complete() callback. */ } @@ -1486,9 +1577,6 @@ static void turn_on_state(pj_turn_session *sess, if (new_state >= PJ_TURN_STATE_DESTROYING && turn_sock->sess) { pj_time_val delay = {0, 0}; - turn_sock->sess = NULL; - pj_turn_session_set_user_data(sess, NULL); - pj_timer_heap_cancel_if_active(turn_sock->cfg.timer_heap, &turn_sock->timer, 0); pj_timer_heap_schedule_w_grp_lock(turn_sock->cfg.timer_heap, @@ -1525,8 +1613,12 @@ static pj_bool_t dataconn_on_data_read(pj_activesock_t *asock, if (size == 0 && status != PJ_SUCCESS) { /* Connection gone, release data connection */ - dataconn_cleanup(conn); - --turn_sock->data_conn_cnt; + if (conn->state == DATACONN_STATE_CONN_BINDING) { + // TODO cancel request (and do not cleanup there) + } else if (conn->state == DATACONN_STATE_READY) { + dataconn_cleanup(conn); + --turn_sock->data_conn_cnt; + } pj_grp_lock_release(turn_sock->grp_lock); return PJ_FALSE; } @@ -1592,6 +1684,14 @@ static pj_bool_t dataconn_on_connect_complete(pj_activesock_t *asock, pj_grp_lock_acquire(turn_sock->grp_lock); + if (pj_turn_sock_get_user_data(turn_sock) == NULL) { + // It's possible for a TURN socket to be destroyed by ice_close_remaining_tcp + // after the on_connect_complete event has been put into an ioqueue, but + // before the callback is actually called, so we need to check for this. + PJ_LOG(4,(turn_sock->obj_name, "Socket is being destroyed, can't be used to establish a data connection")); + status = PJ_ECANCELLED; + } + if (status == PJ_SUCCESS) { status = pj_activesock_start_read(asock, turn_sock->pool, turn_sock->setting.max_pkt_size, 0); @@ -1621,7 +1721,7 @@ static void turn_on_connection_attempt(pj_turn_session *sess, const pj_sockaddr_t *peer_addr, unsigned addr_len) { - pj_turn_sock *turn_sock = (pj_turn_sock*) + pj_turn_sock *turn_sock = (pj_turn_sock*) pj_turn_session_get_user_data(sess); pj_pool_t *pool; tcp_data_conn_t *new_conn; @@ -1796,7 +1896,7 @@ on_return: pj_sockaddr_print(peer_addr, addrtxt, sizeof(addrtxt), 3)); if (!new_conn->asock && sock != PJ_INVALID_SOCKET) - pj_sock_close(sock); + pj_sock_close(sock); dataconn_cleanup(new_conn); --turn_sock->data_conn_cnt; @@ -1816,7 +1916,7 @@ static void turn_on_connection_bind_status(pj_turn_session *sess, const pj_sockaddr_t *peer_addr, unsigned addr_len) { - pj_turn_sock *turn_sock = (pj_turn_sock*) + pj_turn_sock *turn_sock = (pj_turn_sock*) pj_turn_session_get_user_data(sess); tcp_data_conn_t *conn = NULL; unsigned i; @@ -1860,7 +1960,7 @@ static void turn_on_connect_complete(pj_turn_session *sess, const pj_sockaddr_t *peer_addr, unsigned addr_len) { - pj_turn_sock *turn_sock = (pj_turn_sock*) + pj_turn_sock *turn_sock = (pj_turn_sock*) pj_turn_session_get_user_data(sess); pj_pool_t *pool; tcp_data_conn_t *new_conn; @@ -2030,7 +2130,7 @@ on_return: pj_sockaddr_print(peer_addr, addrtxt, sizeof(addrtxt), 3)); if (!new_conn->asock && sock != PJ_INVALID_SOCKET) - pj_sock_close(sock); + pj_sock_close(sock); dataconn_cleanup(new_conn); --turn_sock->data_conn_cnt; @@ -2043,3 +2143,20 @@ on_return: } pj_grp_lock_release(turn_sock->grp_lock); } + +pj_bool_t pj_turn_sock_has_dataconn(pj_turn_sock *turn_sock, + const pj_sockaddr_t *peer) +{ + if (!turn_sock) return PJ_FALSE; + + for (int i = 0; i < turn_sock->data_conn_cnt; ++i) { + tcp_data_conn_t* dataconn = &turn_sock->data_conn[i]; + if (dataconn) { + pj_sockaddr_t* conn_peer = &dataconn->peer_addr; + if (pj_sockaddr_cmp(conn_peer, peer) == 0) + return PJ_TRUE; + } + } + + return PJ_FALSE; +} diff --git a/pjnath/src/pjturn-client/client_main.c b/pjnath/src/pjturn-client/client_main.c index 686a81e70..b18a7be32 100644 --- a/pjnath/src/pjturn-client/client_main.c +++ b/pjnath/src/pjturn-client/client_main.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include @@ -78,7 +78,7 @@ static void turn_on_rx_data(pj_turn_sock *relay, unsigned addr_len); static void turn_on_state(pj_turn_sock *relay, pj_turn_state_t old_state, pj_turn_state_t new_state); -static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, +static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, pj_stun_sock_op op, pj_status_t status); static pj_bool_t stun_sock_on_rx_data(pj_stun_sock *stun_sock, @@ -130,7 +130,7 @@ static int init() /* Create global ioqueue */ CHECK( pj_ioqueue_create(g.pool, 16, &g.stun_config.ioqueue) ); - /* + /* * Create peers */ for (i=0; i<(int)PJ_ARRAY_SIZE(g.peer); ++i) { @@ -153,8 +153,8 @@ static int init() #endif name[strlen(name)-1] = '0'+i; - status = pj_stun_sock_create(&g.stun_config, name, pj_AF_INET(), - &stun_sock_cb, &ss_cfg, + status = pj_stun_sock_create(&g.stun_config, name, pj_AF_INET(), + PJ_STUN_TP_UDP, &stun_sock_cb, &ss_cfg, &g.peer[i], &g.peer[i].stun_sock); if (status != PJ_SUCCESS) { my_perror("pj_stun_sock_create()", status); @@ -168,7 +168,7 @@ static int init() server = pj_str(o.srv_addr); port = (pj_uint16_t)(o.srv_port?atoi(o.srv_port):PJ_STUN_PORT); } - status = pj_stun_sock_start(g.peer[i].stun_sock, &server, + status = pj_stun_sock_start(g.peer[i].stun_sock, &server, port, NULL); if (status != PJ_SUCCESS) { my_perror("pj_stun_sock_start()", status); @@ -257,8 +257,8 @@ static pj_status_t create_relay(void) if (o.nameserver) { pj_str_t ns = pj_str(o.nameserver); - status = pj_dns_resolver_create(&g.cp.factory, "resolver", 0, - g.stun_config.timer_heap, + status = pj_dns_resolver_create(&g.cp.factory, "resolver", 0, + g.stun_config.timer_heap, g.stun_config.ioqueue, &g.resolver); if (status != PJ_SUCCESS) { PJ_LOG(1,(THIS_FILE, "Error creating resolver (err=%d)", status)); @@ -275,7 +275,7 @@ static pj_status_t create_relay(void) pj_bzero(&rel_cb, sizeof(rel_cb)); rel_cb.on_rx_data = &turn_on_rx_data; rel_cb.on_state = &turn_on_state; - CHECK( pj_turn_sock_create(&g.stun_config, pj_AF_INET(), + CHECK( pj_turn_sock_create(&g.stun_config, pj_AF_INET(), (o.use_tcp? PJ_TURN_TP_TCP : PJ_TURN_TP_UDP), &rel_cb, 0, NULL, &g.relay) ); @@ -332,7 +332,7 @@ static void turn_on_rx_data(pj_turn_sock *relay, static void turn_on_state(pj_turn_sock *relay, pj_turn_state_t old_state, pj_turn_state_t new_state) { - PJ_LOG(3,(THIS_FILE, "State %s --> %s", pj_turn_state_name(old_state), + PJ_LOG(3,(THIS_FILE, "State %s --> %s", pj_turn_state_name(old_state), pj_turn_state_name(new_state))); if (new_state == PJ_TURN_STATE_READY) { @@ -345,7 +345,7 @@ static void turn_on_state(pj_turn_sock *relay, pj_turn_state_t old_state, } } -static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, +static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, pj_stun_sock_op op, pj_status_t status) { @@ -458,7 +458,7 @@ static void console_main(void) if (fgets(input, sizeof(input), stdin) == NULL) break; - + switch (input[0]) { case 'a': create_relay(); @@ -477,9 +477,9 @@ static void console_main(void) peer = &g.peer[1]; pj_ansi_strxcpy(input, "Hello from client", sizeof(input)); - status = pj_turn_sock_sendto(g.relay, (const pj_uint8_t*)input, - strlen(input)+1, - &peer->mapped_addr, + status = pj_turn_sock_sendto(g.relay, (const pj_uint8_t*)input, + strlen(input)+1, + &peer->mapped_addr, pj_sockaddr_get_len(&peer->mapped_addr)); if (status != PJ_SUCCESS && status != PJ_EPENDING) my_perror("turn_udp_sendto() failed", status); @@ -622,10 +622,10 @@ int main(int argc, char *argv[]) if ((status=init()) != 0) goto on_return; - + //if ((status=create_relay()) != 0) // goto on_return; - + console_main(); on_return: diff --git a/pjnath/src/pjturn-srv/allocation.c b/pjnath/src/pjturn-srv/allocation.c index 640c87a3d..dc6b3ee0c 100644 --- a/pjnath/src/pjturn-srv/allocation.c +++ b/pjnath/src/pjturn-srv/allocation.c @@ -338,7 +338,7 @@ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_transport *transport, sess_cb.on_rx_request = &stun_on_rx_request; sess_cb.on_rx_indication = &stun_on_rx_indication; status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name, - &sess_cb, PJ_FALSE, NULL, &alloc->sess); + &sess_cb, PJ_FALSE, NULL, &alloc->sess, PJ_STUN_TP_UDP); if (status != PJ_SUCCESS) { goto on_error; } diff --git a/pjnath/src/pjturn-srv/server.c b/pjnath/src/pjturn-srv/server.c index 48b62ce13..997c84a9f 100644 --- a/pjnath/src/pjturn-srv/server.c +++ b/pjnath/src/pjturn-srv/server.c @@ -155,7 +155,7 @@ PJ_DEF(pj_status_t) pj_turn_srv_create(pj_pool_factory *pf, status = pj_stun_session_create(&srv->core.stun_cfg, srv->obj_name, &sess_cb, PJ_FALSE, NULL, - &srv->core.stun_sess); + &srv->core.stun_sess, PJ_STUN_TP_UDP); if (status != PJ_SUCCESS) { goto on_error; } diff --git a/pjsip-apps/src/samples/icedemo.c b/pjsip-apps/src/samples/icedemo.c index ed8f010a6..c10f86f87 100644 --- a/pjsip-apps/src/samples/icedemo.c +++ b/pjsip-apps/src/samples/icedemo.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include @@ -43,6 +43,7 @@ static struct app_t pj_str_t stun_srv; pj_str_t turn_srv; pj_bool_t turn_tcp; + pj_bool_t ice_tcp; pj_str_t turn_username; pj_str_t turn_password; pj_bool_t turn_fingerprint; @@ -92,7 +93,7 @@ static void err_exit(const char *title, pj_status_t status) if (icedemo.icest) pj_ice_strans_destroy(icedemo.icest); - + pj_thread_sleep(500); icedemo.thread_quit_flag = PJ_TRUE; @@ -150,13 +151,13 @@ static pj_status_t handle_events(unsigned max_msec, unsigned *p_count) pj_assert(timeout.sec >= 0 && timeout.msec >= 0); if (timeout.msec >= 1000) timeout.msec = 999; - /* compare the value with the timeout to wait from timer, and use the - * minimum value. + /* compare the value with the timeout to wait from timer, and use the + * minimum value. */ if (PJ_TIME_VAL_GT(timeout, max_timeout)) timeout = max_timeout; - /* Poll ioqueue. + /* Poll ioqueue. * Repeat polling the ioqueue while we have immediate events, because * timer heap may process more than one events, so if we only process * one network events at a time (such as when IOCP backend is used), @@ -212,7 +213,7 @@ static int icedemo_worker_thread(void *unused) * as STUN connectivity checks or TURN signaling). */ static void cb_on_rx_data(pj_ice_strans *ice_st, - unsigned comp_id, + unsigned comp_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len) @@ -237,11 +238,11 @@ static void cb_on_rx_data(pj_ice_strans *ice_st, * This is the callback that is registered to the ICE stream transport to * receive notification about ICE state progression. */ -static void cb_on_ice_complete(pj_ice_strans *ice_st, +static void cb_on_ice_complete(pj_ice_strans *ice_st, pj_ice_strans_op op, pj_status_t status) { - const char *opname = + const char *opname = (op==PJ_ICE_STRANS_OP_INIT? "initialization" : (op==PJ_ICE_STRANS_OP_NEGOTIATION ? "negotiation" : "unknown_op")); @@ -269,7 +270,7 @@ static void log_func(int level, const char *data, int len) /* * This is the main application initialization function. It is called - * once (and only once) during application initialization sequence by + * once (and only once) during application initialization sequence by * main(). */ static pj_status_t icedemo_init(void) @@ -295,18 +296,18 @@ static pj_status_t icedemo_init(void) icedemo.ice_cfg.stun_cfg.pf = &icedemo.cp.factory; /* Create application memory pool */ - icedemo.pool = pj_pool_create(&icedemo.cp.factory, "icedemo", + icedemo.pool = pj_pool_create(&icedemo.cp.factory, "icedemo", 512, 512, NULL); /* Create timer heap for timer stuff */ - CHECK( pj_timer_heap_create(icedemo.pool, 100, + CHECK( pj_timer_heap_create(icedemo.pool, 100, &icedemo.ice_cfg.stun_cfg.timer_heap) ); /* and create ioqueue for network I/O stuff */ - CHECK( pj_ioqueue_create(icedemo.pool, 16, + CHECK( pj_ioqueue_create(icedemo.pool, 16, &icedemo.ice_cfg.stun_cfg.ioqueue) ); - /* something must poll the timer heap and ioqueue, + /* something must poll the timer heap and ioqueue, * unless we're on Symbian where the timer heap and ioqueue run * on themselves. */ @@ -317,14 +318,14 @@ static pj_status_t icedemo_init(void) /* Create DNS resolver if nameserver is set */ if (icedemo.opt.ns.slen) { - CHECK( pj_dns_resolver_create(&icedemo.cp.factory, - "resolver", - 0, + CHECK( pj_dns_resolver_create(&icedemo.cp.factory, + "resolver", + 0, icedemo.ice_cfg.stun_cfg.timer_heap, - icedemo.ice_cfg.stun_cfg.ioqueue, + icedemo.ice_cfg.stun_cfg.ioqueue, &icedemo.ice_cfg.resolver) ); - CHECK( pj_dns_resolver_set_ns(icedemo.ice_cfg.resolver, 1, + CHECK( pj_dns_resolver_set_ns(icedemo.ice_cfg.resolver, 1, &icedemo.opt.ns, NULL) ); } @@ -340,6 +341,12 @@ static pj_status_t icedemo_init(void) else icedemo.ice_cfg.opt.aggressive = PJ_TRUE; + /* Connection type to STUN server */ + if (icedemo.opt.ice_tcp) + icedemo.ice_cfg.stun.conn_type = PJ_STUN_TP_TCP; + else + icedemo.ice_cfg.stun.conn_type = PJ_STUN_TP_UDP; + /* Configure STUN/srflx candidate resolution */ if (icedemo.opt.stun_srv.slen) { char *pos; @@ -394,6 +401,10 @@ static pj_status_t icedemo_init(void) icedemo.ice_cfg.turn.alloc_param.ka_interval = KA_INTERVAL; } + if (icedemo.opt.ice_tcp) { + icedemo.ice_cfg.protocol = PJ_ICE_TP_TCP; + } + /* -= That's it for now, initialization is complete =- */ return PJ_SUCCESS; } @@ -462,8 +473,8 @@ static void icedemo_destroy_instance(void) */ static void icedemo_init_session(unsigned rolechar) { - pj_ice_sess_role role = (pj_tolower((pj_uint8_t)rolechar)=='o' ? - PJ_ICE_SESS_ROLE_CONTROLLING : + pj_ice_sess_role role = (pj_tolower((pj_uint8_t)rolechar)=='o' ? + PJ_ICE_SESS_ROLE_CONTROLLING : PJ_ICE_SESS_ROLE_CONTROLLED); pj_status_t status; @@ -529,18 +540,36 @@ static int print_cand(char buffer[], unsigned maxlen, char *p = buffer; int printed; - PRINT("a=candidate:%.*s %u UDP %u %s %u typ ", + PRINT("a=candidate:%.*s %u %s %u %s %u typ ", (int)cand->foundation.slen, cand->foundation.ptr, (unsigned)cand->comp_id, + cand->transport == PJ_CAND_UDP? "UDP" : "TCP", cand->prio, - pj_sockaddr_print(&cand->addr, ipaddr, + pj_sockaddr_print(&cand->addr, ipaddr, sizeof(ipaddr), 0), (unsigned)pj_sockaddr_get_port(&cand->addr)); PRINT("%s\n", pj_ice_get_cand_type_name(cand->type)); + if (cand->transport != PJ_CAND_UDP) { + PRINT(" tcptype"); + switch (cand->transport) { + case PJ_CAND_TCP_ACTIVE: + PRINT(" active"); + break; + case PJ_CAND_TCP_PASSIVE: + PRINT(" passive"); + break; + case PJ_CAND_TCP_SO: + default: + PRINT(" so"); + break; + } + } + PRINT("\n"); + if (p == buffer+maxlen) return -PJ_ETOOSMALL; @@ -549,7 +578,7 @@ static int print_cand(char buffer[], unsigned maxlen, return (int)(p-buffer); } -/* +/* * Encode ICE information in SDP. */ static int encode_session(char buffer[], unsigned maxlen) @@ -607,6 +636,26 @@ static int encode_session(char buffer[], unsigned maxlen) sizeof(ipaddr), 0)); } + if (cand[0].transport != PJ_CAND_UDP) { + /** RFC 6544, Section 4.5: + * If the default candidate is TCP-based, the agent MUST include the + * a=setup and a=connection attributes from RFC 4145 [RFC4145], + * following the procedures defined there as if ICE were not in use. + */ + PRINT("a=setup:"); + switch (cand[0].transport) { + case PJ_CAND_TCP_ACTIVE: + PRINT("active\n"); + break; + case PJ_CAND_TCP_PASSIVE: + PRINT("passive\n"); + break; + default: + return PJ_EINVALIDOP; + } + PRINT("a=connection:new\n"); + } + /* Enumerate all candidates for this component */ cand_cnt = PJ_ARRAY_SIZE(cand); status = pj_ice_strans_enum_cands(icedemo.icest, comp+1, @@ -663,7 +712,7 @@ static void icedemo_show_ice(void) return; } - printf("Negotiated comp_cnt: %d\n", + printf("Negotiated comp_cnt: %d\n", pj_ice_strans_get_running_comp_cnt(icedemo.icest)); printf("Role : %s\n", pj_ice_strans_get_role(icedemo.icest)==PJ_ICE_SESS_ROLE_CONTROLLED ? @@ -703,12 +752,12 @@ static void icedemo_show_ice(void) /* - * Input and parse SDP from the remote (containing remote's ICE information) + * Input and parse SDP from the remote (containing remote's ICE information) * and save it to global variables. */ static void icedemo_input_remote(void) { - char linebuf[80]; + char linebuf[120]; unsigned media_cnt = 0; unsigned comp0_port = 0; char comp0_addr[80]; @@ -764,14 +813,14 @@ static void icedemo_input_remote(void) } comp0_port = atoi(portstr); - + } break; case 'c': { int cnt; char c[32], net[32], ip[80]; - + cnt = sscanf(line+2, "%s %s %s", c, net, ip); if (cnt != 3) { PJ_LOG(1,(THIS_FILE, "Error parsing connection line")); @@ -822,28 +871,34 @@ static void icedemo_input_remote(void) } else if (strcmp(attr, "candidate")==0) { char *sdpcand = attr+strlen(attr)+1; int af, cnt; - char foundation[32], transport[12], ipaddr[80], type[32]; + char foundation[32], transport[12], ipaddr[80], type[32], tcp_type[32]; pj_str_t tmpaddr; int comp_id, prio, port; pj_ice_sess_cand *cand; pj_status_t status; + pj_bool_t is_tcp = PJ_FALSE; - cnt = sscanf(sdpcand, "%s %d %s %d %s %d typ %s", + cnt = sscanf(sdpcand, "%s %d %s %d %s %d typ %s tcptype %s\n", foundation, &comp_id, transport, &prio, ipaddr, &port, - type); - if (cnt != 7) { + type, + tcp_type); + if (cnt != 7 && cnt != 8) { PJ_LOG(1, (THIS_FILE, "error: Invalid ICE candidate line")); goto on_error; } + if (strcmp(transport, "TCP") == 0) { + is_tcp = PJ_TRUE; + } + cand = &icedemo.rem.cand[icedemo.rem.cand_cnt]; pj_bzero(cand, sizeof(*cand)); - + if (strcmp(type, "host")==0) cand->type = PJ_ICE_CAND_TYPE_HOST; else if (strcmp(type, "srflx")==0) @@ -851,15 +906,32 @@ static void icedemo_input_remote(void) else if (strcmp(type, "relay")==0) cand->type = PJ_ICE_CAND_TYPE_RELAYED; else { - PJ_LOG(1, (THIS_FILE, "Error: invalid candidate type '%s'", + PJ_LOG(1, (THIS_FILE, "Error: invalid candidate type '%s'", type)); goto on_error; } + if (is_tcp) { + if (strcmp(tcp_type, "active") == 0) + cand->transport = PJ_CAND_TCP_ACTIVE; + else if (strcmp(tcp_type, "passive") == 0) + cand->transport = PJ_CAND_TCP_PASSIVE; + else if (strcmp(tcp_type, "so") == 0) + cand->transport = PJ_CAND_TCP_SO; + else { + PJ_LOG(1, (THIS_FILE, + "Error: invalid transport type '%s'", + tcp_type)); + goto on_error; + } + } else { + cand->transport = PJ_CAND_UDP; + } + cand->comp_id = (pj_uint8_t)comp_id; pj_strdup2(icedemo.pool, &cand->foundation, foundation); cand->prio = prio; - + if (strchr(ipaddr, ':')) af = pj_AF_INET6(); else @@ -919,7 +991,7 @@ static void icedemo_input_remote(void) pj_sockaddr_set_port(&icedemo.rem.def_addr[0], (pj_uint16_t)comp0_port); } - PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added", + PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added", icedemo.rem.cand_cnt)); return; @@ -953,7 +1025,7 @@ static void icedemo_start_nego(void) PJ_LOG(3,(THIS_FILE, "Starting ICE negotiation..")); - status = pj_ice_strans_start_ice(icedemo.icest, + status = pj_ice_strans_start_ice(icedemo.icest, pj_cstr(&rufrag, icedemo.rem.ufrag), pj_cstr(&rpwd, icedemo.rem.pwd), icedemo.rem.cand_cnt, diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index c5885f305..e1e7c6f2f 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include @@ -44,7 +44,7 @@ static const char *addr_string(const pj_sockaddr_t *addr) { static char str[PJ_INET6_ADDRSTRLEN]; - pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family, + pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family, pj_sockaddr_get_addr(addr), str, sizeof(str)); return str; @@ -110,9 +110,9 @@ enum timer_id { static pj_status_t mod_on_tx_msg(pjsip_tx_data *tdata); /* This module has sole purpose to print transmit data to contigous buffer - * before actually transmitted to the wire. + * before actually transmitted to the wire. */ -static pjsip_module mod_msg_print = +static pjsip_module mod_msg_print = { NULL, NULL, /* prev and next */ { "mod-msg-print", 13}, /* Name. */ @@ -140,7 +140,7 @@ typedef struct transport /* * Transport manager. */ -struct pjsip_tpmgr +struct pjsip_tpmgr { pj_hash_table_t *table; pj_lock_t *lock; @@ -204,76 +204,76 @@ static struct transport_names_t const char *description; /* Longer description */ unsigned flag; /* Flags */ char name_buf[16]; /* For user's transport */ -} transport_names[16] = +} transport_names[16] = { - { - PJSIP_TRANSPORT_UNSPECIFIED, - 0, - {"Unspecified", 11}, - "Unspecified", + { + PJSIP_TRANSPORT_UNSPECIFIED, + 0, + {"Unspecified", 11}, + "Unspecified", 0 }, - { - PJSIP_TRANSPORT_UDP, - 5060, - {"UDP", 3}, - "UDP transport", + { + PJSIP_TRANSPORT_UDP, + 5060, + {"UDP", 3}, + "UDP transport", PJSIP_TRANSPORT_DATAGRAM }, - { - PJSIP_TRANSPORT_TCP, - 5060, - {"TCP", 3}, - "TCP transport", + { + PJSIP_TRANSPORT_TCP, + 5060, + {"TCP", 3}, + "TCP transport", PJSIP_TRANSPORT_RELIABLE }, - { - PJSIP_TRANSPORT_TLS, - 5061, - {"TLS", 3}, - "TLS transport", + { + PJSIP_TRANSPORT_TLS, + 5061, + {"TLS", 3}, + "TLS transport", PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE }, - { + { PJSIP_TRANSPORT_DTLS, - 5061, - {"DTLS", 4}, - "DTLS transport", + 5061, + {"DTLS", 4}, + "DTLS transport", PJSIP_TRANSPORT_SECURE }, - { - PJSIP_TRANSPORT_SCTP, - 5060, - {"SCTP", 4}, - "SCTP transport", + { + PJSIP_TRANSPORT_SCTP, + 5060, + {"SCTP", 4}, + "SCTP transport", PJSIP_TRANSPORT_RELIABLE }, - { - PJSIP_TRANSPORT_LOOP, - 15060, - {"LOOP", 4}, - "Loopback transport", + { + PJSIP_TRANSPORT_LOOP, + 15060, + {"LOOP", 4}, + "Loopback transport", PJSIP_TRANSPORT_RELIABLE - }, - { - PJSIP_TRANSPORT_LOOP_DGRAM, - 15060, - {"LOOP-DGRAM", 10}, - "Loopback datagram transport", + }, + { + PJSIP_TRANSPORT_LOOP_DGRAM, + 15060, + {"LOOP-DGRAM", 10}, + "Loopback datagram transport", PJSIP_TRANSPORT_DATAGRAM }, - { - PJSIP_TRANSPORT_UDP6, - 5060, - {"UDP", 3}, - "UDP IPv6 transport", + { + PJSIP_TRANSPORT_UDP6, + 5060, + {"UDP", 3}, + "UDP IPv6 transport", PJSIP_TRANSPORT_DATAGRAM }, - { - PJSIP_TRANSPORT_TCP6, - 5060, - {"TCP", 3}, - "TCP IPv6 transport", + { + PJSIP_TRANSPORT_TCP6, + 5060, + {"TCP", 3}, + "TCP IPv6 transport", PJSIP_TRANSPORT_RELIABLE }, { @@ -322,12 +322,12 @@ PJ_DEF(pj_status_t) pjsip_transport_register_type( unsigned tp_flag, pjsip_transport_type_e parent = 0; PJ_ASSERT_RETURN(tp_flag && tp_name && def_port, PJ_EINVAL); - PJ_ASSERT_RETURN(pj_ansi_strlen(tp_name) < - PJ_ARRAY_SIZE(transport_names[0].name_buf), + PJ_ASSERT_RETURN(pj_ansi_strlen(tp_name) < + PJ_ARRAY_SIZE(transport_names[0].name_buf), PJ_ENAMETOOLONG); for (i=1; iendpt, tdata->pool ); return status; } - + //status = pj_lock_create_simple_mutex(pool, "tdta%p", &tdata->lock); status = pj_lock_create_null_mutex(pool, "tdta%p", &tdata->lock); if (status != PJ_SUCCESS) { @@ -574,7 +574,7 @@ static void tx_data_destroy(pjsip_tx_data *tdata) PJ_DEF(pj_status_t) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata ) { pj_atomic_value_t ref_cnt; - + PJ_ASSERT_RETURN(tdata && tdata->ref_cnt, PJ_EINVAL); ref_cnt = pj_atomic_dec_and_get(tdata->ref_cnt); @@ -607,7 +607,7 @@ PJ_DEF(pj_status_t) pjsip_tx_data_encode(pjsip_tx_data *tdata) PJ_USE_EXCEPTION; PJ_TRY { - tdata->buf.start = (char*) + tdata->buf.start = (char*) pj_pool_alloc(tdata->pool, PJSIP_MAX_PKT_LEN); } PJ_CATCH_ANY { @@ -623,7 +623,7 @@ PJ_DEF(pj_status_t) pjsip_tx_data_encode(pjsip_tx_data *tdata) if (!pjsip_tx_data_is_valid(tdata)) { pj_ssize_t size; - size = pjsip_msg_print( tdata->msg, tdata->buf.start, + size = pjsip_msg_print( tdata->msg, tdata->buf.start, tdata->buf.end - tdata->buf.start); if (size < 0) { return PJSIP_EMSGTOOLONG; @@ -652,7 +652,7 @@ static char *get_msg_info(pj_pool_t *pool, const char *obj_name, PJ_ASSERT_RETURN(cseq != NULL, "INVALID MSG"); if (msg->type == PJSIP_REQUEST_MSG) { - len = pj_ansi_snprintf(info_buf, sizeof(info_buf), + len = pj_ansi_snprintf(info_buf, sizeof(info_buf), "Request msg %.*s/cseq=%d (%s)", (int)msg->line.req.method.name.slen, msg->line.req.method.name.ptr, @@ -918,7 +918,7 @@ static pj_status_t mod_on_tx_msg(pjsip_tx_data *tdata) /* * Send a SIP message using the specified transport. */ -PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr, +PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr, pjsip_tx_data *tdata, const pj_sockaddr_t *addr, int addr_len, @@ -932,7 +932,7 @@ PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr, /* Is it currently being sent? */ if (tdata->is_pending) { pj_assert(!"Invalid operation step!"); - PJ_LOG(2,(THIS_FILE, "Unable to send %s: message is pending", + PJ_LOG(2,(THIS_FILE, "Unable to send %s: message is pending", pjsip_tx_data_get_info(tdata))); return PJSIP_EPENDINGTX; } @@ -953,7 +953,7 @@ PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr, sizeof(tdata->tp_info.dst_name)); tdata->tp_info.dst_port = pj_sockaddr_get_port(addr); - /* Distribute to modules. + /* Distribute to modules. * When the message reach mod_msg_print, the contents of the message will * be "printed" to contiguous buffer. */ @@ -976,7 +976,7 @@ PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr, tdata->is_pending = 1; /* Send to transport. */ - status = (*tr->send_msg)(tr, tdata, addr, addr_len, (void*)tdata, + status = (*tr->send_msg)(tr, tdata, addr, addr_len, (void*)tdata, &transport_send_callback); if (status != PJ_EPENDING) { @@ -1035,7 +1035,7 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_send_raw(pjsip_tpmgr *mgr, { pjsip_transport *tr; pj_status_t status; - + /* Acquire the transport */ status = pjsip_tpmgr_acquire_transport(mgr, tp_type, addr, addr_len, sel, &tr); @@ -1064,13 +1064,13 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_send_raw(pjsip_tpmgr *mgr, tdata->buf.start = (char*) pj_pool_alloc(tdata->pool, data_len+1); tdata->buf.end = tdata->buf.start + data_len + 1; } - + /* Copy data, if any! (application may send zero len packet) */ if (data_len) { pj_memcpy(tdata->buf.start, raw_data, data_len); } tdata->buf.cur = tdata->buf.start + data_len; - + /* Save callback data. */ tdata->token = token; tdata->cb = cb; @@ -1081,7 +1081,7 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_send_raw(pjsip_tpmgr *mgr, /* Send to transport */ status = tr->send_msg(tr, tdata, addr, addr_len, tdata, &send_raw_callback); - + if (status != PJ_EPENDING) { /* callback will not be called, so destroy tdata now. */ pjsip_tx_data_dec_ref(tdata); @@ -1115,7 +1115,7 @@ static void transport_idle_callback(pj_timer_heap_t *timer_heap, if (pj_atomic_get(tp->ref_cnt) == 0) { tp->is_destroying = PJ_TRUE; PJ_LOG(4, (THIS_FILE, "Transport %s is being destroyed " - "due to timeout in %s timer", tp->obj_name, + "due to timeout in %s timer", tp->obj_name, (entry_id == IDLE_TIMER_ID)?"idle":"initial")); if (entry_id == INITIAL_IDLE_TIMER_ID) { if (tp->last_recv_len > 0 && tp->tpmgr->tp_drop_data_cb) { @@ -1225,7 +1225,7 @@ PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp ) !tp->is_destroying && pj_atomic_get(tp->ref_cnt) == 0) { pj_time_val delay; - + int timer_id = IDLE_TIMER_ID; /* If transport is in graceful shutdown, then this is the @@ -1240,7 +1240,7 @@ PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp ) } else { delay.sec = PJSIP_TRANSPORT_SERVER_IDLE_TIME; if (tp->last_recv_ts.u64 == 0 && tp->initial_timeout) { - PJ_LOG(4, (THIS_FILE, + PJ_LOG(4, (THIS_FILE, "Starting transport %s initial timer", tp->obj_name)); timer_id = INITIAL_IDLE_TIMER_ID; @@ -1309,7 +1309,7 @@ PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr, if (!tp_add){ pj_lock_release(mgr->lock); return PJ_ENOMEM; - } + } pj_list_init(tp_add); pj_list_push_back(&mgr->tp_entry_freelist, tp_add); } @@ -1319,7 +1319,7 @@ PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr, pj_list_erase(tp_add); if (tp_ref) { - /* There'a already a transport list from the hash table. Add the + /* There'a already a transport list from the hash table. Add the * new transport to the list. */ pj_list_push_back(tp_ref, tp_add); @@ -1442,7 +1442,7 @@ static pj_status_t destroy_transport( pjsip_tpmgr *mgr, /* - * Start graceful shutdown procedure for this transport. + * Start graceful shutdown procedure for this transport. */ PJ_DEF(pj_status_t) pjsip_transport_shutdown(pjsip_transport *tp) { @@ -1451,7 +1451,7 @@ PJ_DEF(pj_status_t) pjsip_transport_shutdown(pjsip_transport *tp) /* - * Start shutdown procedure for this transport. + * Start shutdown procedure for this transport. */ PJ_DEF(pj_status_t) pjsip_transport_shutdown2(pjsip_transport *tp, pj_bool_t force) @@ -1551,6 +1551,12 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr, /* Check that no same factory has been registered. */ status = PJ_SUCCESS; for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) { + if (p->type == tpf->type && + !pj_sockaddr_cmp(&tpf->local_addr, &p->local_addr)) + { + status = PJSIP_ETYPEEXISTS; + break; + } if (p == tpf) { status = PJ_EEXISTS; break; @@ -1702,7 +1708,7 @@ static pj_status_t get_net_interface(pjsip_transport_type_e tp_type, status = pj_getipinterface(af, dst, &itf_addr, PJ_TRUE, NULL); } - if (status != PJ_SUCCESS) { + if (status != PJ_SUCCESS) { status = pj_getipinterface(af, dst, &itf_addr, PJ_FALSE, NULL); if (status != PJ_SUCCESS) { /* If it fails, e.g: on WM6(http://support.microsoft.com/kb/129065), @@ -1727,7 +1733,7 @@ static pj_status_t get_net_interface(pjsip_transport_type_e tp_type, /* * Find out the appropriate local address info (IP address and port) to - * advertise in Contact header based on the remote address to be + * advertise in Contact header based on the remote address to be * contacted. The local address info would be the address name of the * transport or listener which will be used to send the request. * @@ -2089,9 +2095,9 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, tr->last_recv_len = rdata->pkt_info.len; pj_get_timestamp(&tr->last_recv_ts); - - /* Must NULL terminate buffer. This is the requirement of the - * parser etc. + + /* Must NULL terminate buffer. This is the requirement of the + * parser etc. */ current_pkt[remaining_len] = '\0'; @@ -2134,7 +2140,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, /* Initialize default fragment size. */ msg_fragment_size = remaining_len; - /* Clear and init msg_info in rdata. + /* Clear and init msg_info in rdata. * Endpoint might inspect the values there when we call the callback * to report some errors. */ @@ -2164,7 +2170,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, } } - msg_status = pjsip_find_msg(current_pkt, remaining_len, PJ_FALSE, + msg_status = pjsip_find_msg(current_pkt, remaining_len, PJ_FALSE, &msg_fragment_size); if (msg_status != PJ_SUCCESS) { pj_status_t dd_status = msg_status; @@ -2207,10 +2213,10 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, } if (msg_status == PJSIP_EPARTIALMSG) { - if (rdata->tp_info.transport->idle_timer.id == + if (rdata->tp_info.transport->idle_timer.id == INITIAL_IDLE_TIMER_ID) { - /* We are not getting the first valid SIP message + /* We are not getting the first valid SIP message * as expected, close the transport. */ PJ_LOG(4, (THIS_FILE, "Unexpected data was received "\ @@ -2238,7 +2244,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, current_pkt[msg_fragment_size] = '\0'; /* Parse the message. */ - rdata->msg_info.msg = msg = + rdata->msg_info.msg = msg = pjsip_parse_rdata( current_pkt, msg_fragment_size, rdata); /* Restore null termination */ @@ -2299,7 +2305,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, dd.len = msg_fragment_size; dd.status = PJSIP_EINVALIDMSG; (*mgr->tp_drop_data_cb)(&dd); - + if (dd.len > 0 && dd.len < msg_fragment_size) msg_fragment_size = dd.len; } @@ -2309,11 +2315,11 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, /* Perform basic header checking. */ if (rdata->msg_info.cid == NULL || - rdata->msg_info.cid->id.slen == 0 || - rdata->msg_info.from == NULL || - rdata->msg_info.to == NULL || - rdata->msg_info.via == NULL || - rdata->msg_info.cseq == NULL) + rdata->msg_info.cid->id.slen == 0 || + rdata->msg_info.from == NULL || + rdata->msg_info.to == NULL || + rdata->msg_info.via == NULL || + rdata->msg_info.cseq == NULL) { mgr->on_rx_msg(mgr->endpt, PJSIP_EMISSINGHDR, rdata); @@ -2325,7 +2331,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, dd.data = current_pkt; dd.len = msg_fragment_size; dd.status = PJSIP_EMISSINGHDR; - (*mgr->tp_drop_data_cb)(&dd); + (*mgr->tp_drop_data_cb)(&dd); } goto finish_process_fragment; } @@ -2333,8 +2339,8 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, /* For request: */ if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) { /* always add received parameter to the via. */ - pj_strdup2(rdata->tp_info.pool, - &rdata->msg_info.via->recvd_param, + pj_strdup2(rdata->tp_info.pool, + &rdata->msg_info.via->recvd_param, rdata->pkt_info.src_name); /* RFC 3581: @@ -2358,7 +2364,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, dd.data = current_pkt; dd.len = msg_fragment_size; dd.status = PJSIP_EINVALIDSTATUS; - (*mgr->tp_drop_data_cb)(&dd); + (*mgr->tp_drop_data_cb)(&dd); } goto finish_process_fragment; } @@ -2463,10 +2469,13 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, * for the destination. */ if (sel && sel->type == PJSIP_TPSELECTOR_TRANSPORT && - sel->u.transport) + sel->u.transport) { pjsip_transport *seltp = sel->u.transport; + pjsip_transport_type_e type_no_ipv6 = type % PJSIP_TRANSPORT_IPV6; + pjsip_transport_type_e key_type_no_ipv6 = seltp->key.type % + PJSIP_TRANSPORT_IPV6; /* See if the transport is (not) suitable */ if (seltp->key.type != type) { pj_lock_release(mgr->lock); @@ -2765,7 +2774,7 @@ PJ_DEF(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr) PJ_LOG(3, (THIS_FILE, " Dumping listeners:")); factory = mgr->factory_list.next; while (factory != &mgr->factory_list) { - PJ_LOG(3, (THIS_FILE, " %s %s:%.*s:%d", + PJ_LOG(3, (THIS_FILE, " %s %s:%.*s:%d", factory->obj_name, factory->type_name, (int)factory->addr_name.host.slen, @@ -2933,7 +2942,7 @@ PJ_DEF(pj_status_t) pjsip_transport_add_state_listener ( } /** - * Remove a listener from the specified transport for transport state + * Remove a listener from the specified transport for transport state * notification. */ PJ_DEF(pj_status_t) pjsip_transport_remove_state_listener ( diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index a29b5cdd6..74f6f527c 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * @@ -14,7 +14,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include @@ -40,7 +40,7 @@ PJ_DEF(struct pjsua_data*) pjsua_get_var(void) /* Display error */ -PJ_DEF(void) pjsua_perror( const char *sender, const char *title, +PJ_DEF(void) pjsua_perror( const char *sender, const char *title, pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; @@ -58,7 +58,7 @@ static void init_data() for (i=0; imsg_logging = PJ_TRUE; cfg->level = 5; cfg->console_level = 4; - cfg->decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME | + cfg->decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_SPACE | PJ_LOG_HAS_THREAD_SWC | PJ_LOG_HAS_INDENT; @@ -285,13 +285,13 @@ PJ_DEF(void) pjsua_srtp_opt_dup( pj_pool_t *pool, pjsua_srtp_opt *dst, pj_bool_t check_str) { pjsua_srtp_opt backup_dst; - + if (check_str) pj_memcpy(&backup_dst, dst, sizeof(*dst)); pj_memcpy(dst, src, sizeof(*src)); if (pool) { unsigned i; - + for (i = 0; i < src->crypto_count; i++) { if (!check_str || pj_stricmp(&backup_dst.crypto[i].key, &src->crypto[i].key)) @@ -396,7 +396,7 @@ PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg) { const pj_sys_info *si = pj_get_sys_info(); pj_str_t dev_model = {"iPhone5", 7}; - + pj_bzero(cfg, sizeof(*cfg)); cfg->clock_rate = PJSUA_DEFAULT_CLOCK_RATE; @@ -462,15 +462,15 @@ static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata) "--end msg--", rdata->msg_info.len, pjsip_rx_data_get_info(rdata), - rdata->tp_info.transport->type_name, - pj_addr_str_print(&input_str, - rdata->pkt_info.src_port, + rdata->tp_info.transport->type_name, + pj_addr_str_print(&input_str, + rdata->pkt_info.src_port, addr, - sizeof(addr), + sizeof(addr), 1), (int)rdata->msg_info.len, rdata->msg_info.msg_buf)); - + /* Always return false, otherwise messages will not get processed! */ return PJ_FALSE; } @@ -480,7 +480,7 @@ static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) { char addr[PJ_INET6_ADDRSTRLEN+10]; pj_str_t input_str = pj_str(tdata->tp_info.dst_name); - + /* Important note: * tp_info field is only valid after outgoing messages has passed * transport layer. So don't try to access tp_info when the module @@ -492,10 +492,10 @@ static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) (int)(tdata->buf.cur - tdata->buf.start), pjsip_tx_data_get_info(tdata), tdata->tp_info.transport->type_name, - pj_addr_str_print(&input_str, - tdata->tp_info.dst_port, + pj_addr_str_print(&input_str, + tdata->tp_info.dst_port, addr, - sizeof(addr), + sizeof(addr), 1), (int)(tdata->buf.cur - tdata->buf.start), tdata->buf.start)); @@ -506,7 +506,7 @@ static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) } /* The module instance. */ -static pjsip_module pjsua_msg_logger = +static pjsip_module pjsua_msg_logger = { NULL, NULL, /* prev, next. */ { "mod-pjsua-log", 13 }, /* Name. */ @@ -546,14 +546,14 @@ static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata) /* Don't want to handle if shutdown is in progress */ if (pjsua_var.thread_quit_flag) { - pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, + pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL, NULL, NULL); return PJ_TRUE; } /* Create basic response. */ - status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL, + status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create OPTIONS response", status); @@ -563,28 +563,28 @@ static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata) /* Add Allow header */ cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ALLOW, NULL); if (cap_hdr) { - pjsip_msg_add_hdr(tdata->msg, + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr)); } /* Add Accept header */ cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL); if (cap_hdr) { - pjsip_msg_add_hdr(tdata->msg, + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr)); } /* Add Supported header */ cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_SUPPORTED, NULL); if (cap_hdr) { - pjsip_msg_add_hdr(tdata->msg, + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr)); } /* Add Allow-Events header from the evsub module */ cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL); if (cap_hdr) { - pjsip_msg_add_hdr(tdata->msg, + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr)); } @@ -628,7 +628,7 @@ static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata) /* The module instance. */ -static pjsip_module pjsua_options_handler = +static pjsip_module pjsua_options_handler = { NULL, NULL, /* prev, next. */ { "mod-pjsua-options", 17 }, /* Name. */ @@ -768,9 +768,9 @@ PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg) if (pjsua_var.log_cfg.log_filename.slen) { unsigned flags = PJ_O_WRONLY | PJ_O_CLOEXEC; flags |= pjsua_var.log_cfg.log_file_flags; - status = pj_file_open(pjsua_var.pool, + status = pj_file_open(pjsua_var.pool, pjsua_var.log_cfg.log_filename.ptr, - flags, + flags, &pjsua_var.log_file); if (status != PJ_SUCCESS) { @@ -974,9 +974,9 @@ PJ_DEF(pj_status_t) pjsua_create(void) pj_shutdown(); return status; } - + /* Create mutex */ - status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua", + status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua", &pjsua_var.mutex); if (status != PJ_SUCCESS) { pj_log_pop_indent(); @@ -988,8 +988,8 @@ PJ_DEF(pj_status_t) pjsua_create(void) /* Must create SIP endpoint to initialize SIP parser. The parser * is needed for example when application needs to call pjsua_verify_url(). */ - status = pjsip_endpt_create(&pjsua_var.cp.factory, - pj_gethostname()->ptr, + status = pjsip_endpt_create(&pjsua_var.cp.factory, + pj_gethostname()->ptr, &pjsua_var.endpt); if (status != PJ_SUCCESS) { pj_log_pop_indent(); @@ -1004,7 +1004,7 @@ PJ_DEF(pj_status_t) pjsua_create(void) pj_list_init(&pjsua_var.event_list); /* Create timer mutex */ - status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua_timer", + status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua_timer", &pjsua_var.timer_mutex); if (status != PJ_SUCCESS) { pj_log_pop_indent(); @@ -1028,7 +1028,7 @@ static void upnp_cb(pj_status_t status) #endif /* - * Initialize pjsua with the specified settings. All the settings are + * Initialize pjsua with the specified settings. All the settings are * optional, and the default values will be used when the config is not * specified. */ @@ -1081,7 +1081,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, unsigned ii; /* Create DNS resolver */ - status = pjsip_endpt_create_resolver(pjsua_var.endpt, + status = pjsip_endpt_create_resolver(pjsua_var.endpt, &pjsua_var.resolver); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating resolver", status); @@ -1089,7 +1089,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, } /* Configure nameserver for the DNS resolver */ - status = pj_dns_resolver_set_ns(pjsua_var.resolver, + status = pj_dns_resolver_set_ns(pjsua_var.resolver, ua_cfg->nameserver_count, ua_cfg->nameserver, NULL); if (status != PJ_SUCCESS) { @@ -1111,7 +1111,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, ua_cfg->nameserver[ii].ptr)); } #else - PJ_LOG(2,(THIS_FILE, + PJ_LOG(2,(THIS_FILE, "DNS resolver is disabled (PJSIP_HAS_RESOLVER==0)")); #endif } @@ -1146,7 +1146,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, /* Initialize and register PJSUA application module. */ { - const pjsip_module mod_initializer = + const pjsip_module mod_initializer = { NULL, NULL, /* prev, next. */ { "mod-pjsua", 9 }, /* Name. */ @@ -1201,7 +1201,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, pj_list_push_back(&pjsua_var.outbound_proxy, r); } - + /* Initialize PJSUA call subsystem: */ status = pjsua_call_subsys_init(ua_cfg); @@ -1211,11 +1211,11 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, /* Convert deprecated STUN settings */ if (pjsua_var.ua_cfg.stun_srv_cnt==0) { if (pjsua_var.ua_cfg.stun_domain.slen) { - pjsua_var.ua_cfg.stun_srv[pjsua_var.ua_cfg.stun_srv_cnt++] = + pjsua_var.ua_cfg.stun_srv[pjsua_var.ua_cfg.stun_srv_cnt++] = pjsua_var.ua_cfg.stun_domain; } if (pjsua_var.ua_cfg.stun_host.slen) { - pjsua_var.ua_cfg.stun_srv[pjsua_var.ua_cfg.stun_srv_cnt++] = + pjsua_var.ua_cfg.stun_srv[pjsua_var.ua_cfg.stun_srv_cnt++] = pjsua_var.ua_cfg.stun_host; } } @@ -1311,7 +1311,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, for (ii=0; iiinfo.ptr)); pjsua_set_state(PJSUA_STATE_INIT); @@ -1435,7 +1435,7 @@ static void stun_resolve_dec_ref(pjsua_stun_resolve *sess) * is allowed to destroy the session, otherwise it may cause deadlock. */ if ((ref_cnt > 0) || - (sess->blocking && (sess->waiter != pj_thread_this()))) + (sess->blocking && (sess->waiter != pj_thread_this()))) { return; } @@ -1465,7 +1465,7 @@ static void stun_resolve_complete(pjsua_stun_resolve *sess) if (result.status == PJ_SUCCESS) { char addr[PJ_INET6_ADDRSTRLEN+10]; pj_sockaddr_print(&result.addr, addr, sizeof(addr), 3); - PJ_LOG(4,(THIS_FILE, + PJ_LOG(4,(THIS_FILE, "STUN resolution success, using %.*s, address is %s", (int)sess->srv[sess->idx].slen, sess->srv[sess->idx].ptr, @@ -1488,7 +1488,7 @@ on_return: * to report it's state. We use this as part of testing the * STUN server. */ -static pj_bool_t test_stun_on_status(pj_stun_sock *stun_sock, +static pj_bool_t test_stun_on_status(pj_stun_sock *stun_sock, pj_stun_sock_op op, pj_status_t status) { @@ -1553,7 +1553,7 @@ static pj_bool_t test_stun_on_status(pj_stun_sock *stun_sock, } else return PJ_TRUE; - + } /* This is an internal function to resolve and test current @@ -1574,7 +1574,7 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) pj_str_t hostpart; pj_uint16_t port; pj_stun_sock_cb stun_sock_cb; - + pj_assert(sess->idx < sess->count); if (pjsua_var.ua_cfg.stun_try_ipv6 && @@ -1584,7 +1584,7 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) /* Skip IPv4 STUN resolution if NAT64 is not disabled. */ PJ_LOG(4,(THIS_FILE, "Skipping IPv4 resolution of STUN server " "%s (%d of %d)", target, - sess->idx+1, sess->count)); + sess->idx+1, sess->count)); continue; } @@ -1599,7 +1599,7 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) PJ_LOG(2,(THIS_FILE, "Invalid STUN server entry %s", target)); continue; } - + /* Use default port if not specified */ if (port == 0) port = PJ_STUN_PORT; @@ -1615,12 +1615,12 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) stun_sock_cb.on_status = &test_stun_on_status; sess->async_wait = PJ_FALSE; status = pj_stun_sock_create(&pjsua_var.stun_cfg, "stunresolve", - sess->af, &stun_sock_cb, + sess->af, PJ_STUN_TP_UDP, &stun_sock_cb, NULL, sess, &sess->stun_sock); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); - PJ_LOG(4,(THIS_FILE, + PJ_LOG(4,(THIS_FILE, "Error creating STUN socket for %s: %s", target, errmsg)); @@ -1632,7 +1632,7 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); - PJ_LOG(4,(THIS_FILE, + PJ_LOG(4,(THIS_FILE, "Error starting STUN socket for %s: %s", target, errmsg)); @@ -1672,7 +1672,7 @@ PJ_DEF(pj_status_t) pjsua_update_stun_servers(unsigned count, pj_str_t srv[], pj_status_t status; PJ_ASSERT_RETURN(count && srv, PJ_EINVAL); - + PJSUA_LOCK(); pjsua_var.ua_cfg.stun_srv_cnt = count; @@ -1683,7 +1683,7 @@ PJ_DEF(pj_status_t) pjsua_update_stun_servers(unsigned count, pj_str_t srv[], pjsua_var.stun_status = PJ_EUNKNOWN; PJSUA_UNLOCK(); - + status = resolve_stun_server(wait, PJ_FALSE, 0); if (wait == PJ_FALSE && status == PJ_EPENDING) status = PJ_SUCCESS; @@ -1743,7 +1743,7 @@ PJ_DEF(pj_status_t) pjsua_resolve_stun_servers( unsigned count, */ max_wait_ms = count * pjsua_var.stun_cfg.rto_msec * (1 << 7); pj_get_timestamp(&start); - + while ((sess->status == PJ_EPENDING) && (!sess->destroy_flag)) { /* If there is no worker thread or * the function is called from the only worker thread, @@ -1794,7 +1794,7 @@ PJ_DEF(pj_status_t) pjsua_cancel_stun_resolution( void *token, result.status = PJ_ECANCELLED; sess->cb(&result); - } + } ++cancelled_count; } @@ -1820,7 +1820,7 @@ static void internal_stun_resolve_cb(const pj_stun_resolve_result *result) pjsua_detect_nat_type(); } } - + if (pjsua_var.ua_cfg.cb.on_stun_resolution_complete) (*pjsua_var.ua_cfg.cb.on_stun_resolution_complete)(result); } @@ -1879,7 +1879,7 @@ pj_status_t resolve_stun_server(pj_bool_t wait, pj_bool_t retry_if_cur_error, pjsua_var.stun_cfg.rto_msec * (1 << 7); pj_get_timestamp(&start); - while (pjsua_var.stun_status == PJ_EPENDING) { + while (pjsua_var.stun_status == PJ_EPENDING) { /* If there is no worker thread or * the function is called from the only worker thread, * we have to handle the events here. @@ -1904,7 +1904,7 @@ pj_status_t resolve_stun_server(pj_bool_t wait, pj_bool_t retry_if_cur_error, pjsua_var.stun_status != PJ_SUCCESS && pjsua_var.ua_cfg.stun_ignore_failure) { - PJ_LOG(2,(THIS_FILE, + PJ_LOG(2,(THIS_FILE, "Ignoring STUN resolution failure (by setting)")); //pjsua_var.stun_status = PJ_SUCCESS; return PJ_SUCCESS; @@ -1932,7 +1932,7 @@ PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags) /* Signal threads to quit: */ pjsua_stop_worker_threads(); - + if (pjsua_var.endpt) { unsigned max_wait; @@ -1973,7 +1973,7 @@ PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags) if (pjsua_var.acc[i].cfg.unpublish_max_wait_time_msec > max_wait) max_wait = pjsua_var.acc[i].cfg.unpublish_max_wait_time_msec; } - + /* No waiting if RX is disabled */ if (flags & PJSUA_DESTROY_NO_RX_MSG) { max_wait = 0; @@ -2027,7 +2027,7 @@ PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags) if (pjsua_var.acc[i].cfg.unreg_timeout > max_wait) max_wait = pjsua_var.acc[i].cfg.unreg_timeout; } - + /* No waiting if RX is disabled */ if (flags & PJSUA_DESTROY_NO_RX_MSG) { max_wait = 0; @@ -2051,14 +2051,14 @@ PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags) /* Note variable 'i' is used below */ /* Wait for some time to allow unregistration and ICE/TURN - * transports shutdown to complete: + * transports shutdown to complete: */ if (i < 20 && (flags & PJSUA_DESTROY_NO_RX_MSG) == 0) { busy_sleep(1000 - i*50); } PJ_LOG(4,(THIS_FILE, "Destroying...")); - + /* Terminate any pending STUN resolution */ if (!pj_list_empty(&pjsua_var.stun_res)) { pjsua_stun_resolve *sess = pjsua_var.stun_res.next; @@ -2073,7 +2073,7 @@ PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags) for (i = 0; i < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata); i++) { if (pjsua_var.tpdata[i].data.ptr) { pjsip_transport_type_e tp_type; - + tp_type = pjsua_var.tpdata[i].type & ~PJSIP_TRANSPORT_IPV6; if ((flags & PJSUA_DESTROY_NO_TX_MSG) && tp_type == PJSIP_TRANSPORT_UDP && @@ -2131,7 +2131,7 @@ PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags) pj_mutex_destroy(pjsua_var.mutex); pjsua_var.mutex = NULL; } - + if (pjsua_var.timer_mutex) { pj_mutex_destroy(pjsua_var.timer_mutex); pjsua_var.timer_mutex = NULL; @@ -2202,7 +2202,7 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) /** * Application is recommended to call this function after all initialization * is done, so that the library can do additional checking set up - * additional + * additional * * @return PJ_SUCCESS on success, or the appropriate error code. */ @@ -2259,7 +2259,7 @@ PJ_DEF(int) pjsua_handle_events(unsigned msec_timeout) return -status; return count; - + #endif } @@ -2271,7 +2271,7 @@ PJ_DEF(pj_pool_t*) pjsua_pool_create( const char *name, pj_size_t init_size, pj_size_t increment) { /* Pool factory is thread safe, no need to lock */ - return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment, + return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment, NULL); } @@ -2314,7 +2314,7 @@ static const char *addr_string(const pj_sockaddr_t *addr) { static char str[128]; str[0] = '\0'; - pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family, + pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family, pj_sockaddr_get_addr(addr), str, sizeof(str)); return str; @@ -2378,11 +2378,11 @@ static pj_status_t create_sip_udp_sock(int af, /* Initialize bound address */ if (cfg->bound_addr.slen) { - status = pj_sockaddr_init(af, &bind_addr, &cfg->bound_addr, + status = pj_sockaddr_init(af, &bind_addr, &cfg->bound_addr, (pj_uint16_t)port); if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Unable to resolve transport bound address", + pjsua_perror(THIS_FILE, + "Unable to resolve transport bound address", status); return status; } @@ -2398,8 +2398,8 @@ static pj_status_t create_sip_udp_sock(int af, } /* Apply QoS, if specified */ - status = pj_sock_apply_qos2(sock, cfg->qos_type, - &cfg->qos_params, + status = pj_sock_apply_qos2(sock, cfg->qos_type, + &cfg->qos_params, 2, THIS_FILE, "SIP UDP socket"); /* Apply sockopt, if specified */ @@ -2446,7 +2446,7 @@ static pj_status_t create_sip_udp_sock(int af, status = PJ_SUCCESS; if (pj_sockaddr_has_addr(p_pub_addr)) { /* - * Public address is already specified, no need to resolve the + * Public address is already specified, no need to resolve the * address, only set the port. */ if (pj_sockaddr_get_port(p_pub_addr) == 0) @@ -2466,14 +2466,14 @@ static pj_status_t create_sip_udp_sock(int af, stun_opt.use_stun2 = pjsua_var.ua_cfg.stun_map_use_stun2; stun_opt.af = pjsua_var.stun_srv.addr.sa_family; stun_opt.srv1 = stun_opt.srv2 = stun_srv; - stun_opt.port1 = stun_opt.port2 = + stun_opt.port1 = stun_opt.port2 = pj_sockaddr_get_port(&pjsua_var.stun_srv); status = pjstun_get_mapped_addr2(&pjsua_var.cp.factory, &stun_opt, 1, &sock, &p_pub_addr->ipv4); if (status != PJ_SUCCESS) { /* Failed getting mapped address via STUN */ pjsua_perror(THIS_FILE, "Error contacting STUN server", status); - + /* Return error if configured to not ignore STUN failure */ if (!pjsua_var.ua_cfg.stun_ignore_failure) { pj_sock_close(sock); @@ -2573,20 +2573,20 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, } /* Initialize the public address from the config, if any */ - pj_sockaddr_init(pjsip_transport_type_get_af(type), &pub_addr, + pj_sockaddr_init(pjsip_transport_type_get_af(type), &pub_addr, NULL, (pj_uint16_t)cfg->port); if (cfg->public_addr.slen) { status = pj_sockaddr_set_str_addr(pjsip_transport_type_get_af(type), &pub_addr, &cfg->public_addr); if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Unable to resolve transport public address", + pjsua_perror(THIS_FILE, + "Unable to resolve transport public address", status); goto on_return; } } - /* Create the socket and possibly resolve the address with STUN + /* Create the socket and possibly resolve the address with STUN * (only when public address is not specified). */ status = create_sip_udp_sock(pjsip_transport_type_get_af(type), @@ -2602,7 +2602,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, status = pjsip_udp_transport_attach2(pjsua_var.endpt, type, sock, &addr_name, 1, &tp); if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Error creating SIP UDP transport", + pjsua_perror(THIS_FILE, "Error creating SIP UDP transport", status); pj_sock_close(sock); goto on_return; @@ -2642,12 +2642,12 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, pj_sockaddr_set_port(&tcp_cfg.bind_addr, (pj_uint16_t)cfg->port); if (cfg->bound_addr.slen) { - status = pj_sockaddr_set_str_addr(tcp_cfg.af, + status = pj_sockaddr_set_str_addr(tcp_cfg.af, &tcp_cfg.bind_addr, &cfg->bound_addr); if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Unable to resolve transport bound address", + pjsua_perror(THIS_FILE, + "Unable to resolve transport bound address", status); goto on_return; } @@ -2659,7 +2659,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, /* Copy the QoS settings */ tcp_cfg.qos_type = cfg->qos_type; - pj_memcpy(&tcp_cfg.qos_params, &cfg->qos_params, + pj_memcpy(&tcp_cfg.qos_params, &cfg->qos_params, sizeof(cfg->qos_params)); /* Copy the sockopt */ @@ -2670,7 +2670,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, status = pjsip_tcp_transport_start3(pjsua_var.endpt, &tcp_cfg, &tcp); if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Error creating SIP TCP listener", + pjsua_perror(THIS_FILE, "Error creating SIP TCP listener", status); goto on_return; } @@ -2711,8 +2711,8 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, status = pj_sockaddr_set_str_addr(af, &local_addr, &cfg->bound_addr); if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Unable to resolve transport bound address", + pjsua_perror(THIS_FILE, + "Unable to resolve transport bound address", status); goto on_return; } @@ -2726,7 +2726,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, status = pjsip_tls_transport_start2(pjsua_var.endpt, &cfg->tls_setting, &local_addr, &a_name, 1, &tls); if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Error creating SIP TLS listener", + pjsua_perror(THIS_FILE, "Error creating SIP TLS listener", status); goto on_return; } @@ -2847,8 +2847,8 @@ PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[], PJSUA_LOCK(); - for (i=0, count=0; i=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), + PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), PJ_EINVAL); /* Make sure that transport exists */ @@ -2892,7 +2892,7 @@ PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id, PJSUA_UNLOCK(); return PJ_EINVALIDOP; } - + info->id = id; info->type = (pjsip_transport_type_e) tp->key.type; info->type_name = pj_str(tp->type_name); @@ -2915,7 +2915,7 @@ PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id, PJSUA_UNLOCK(); return PJ_EINVALIDOP; } - + info->id = id; info->type = t->type; info->type_name = pj_str(factory->type_name); @@ -2948,7 +2948,7 @@ PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id, pj_bool_t enabled) { /* Make sure id is in range. */ - PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), + PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), PJ_EINVAL); /* Make sure that transport exists */ @@ -2974,7 +2974,7 @@ PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id, pjsip_transport_type_e tp_type; /* Make sure id is in range. */ - PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), + PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), PJ_EINVAL); /* Make sure that transport exists */ @@ -2989,7 +2989,7 @@ PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id, */ PJ_LOG(1, (THIS_FILE, "pjsua_transport_close(force=PJ_TRUE) is " "deprecated.")); - + /* To minimize the effect to users, we shouldn't hard-deprecate this * and let it continue as if force is false. */ @@ -3019,7 +3019,7 @@ PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id, case PJSIP_TRANSPORT_TLS: case PJSIP_TRANSPORT_TCP: /* This will close the TCP listener, but existing TCP/TLS - * connections (if any) will still linger + * connections (if any) will still linger */ status = (*pjsua_var.tpdata[id].data.factory->destroy) (pjsua_var.tpdata[id].data.factory); @@ -3048,31 +3048,31 @@ PJ_DEF(pj_status_t) pjsua_transport_lis_start(pjsua_transport_id id, pjsip_transport_type_e tp_type; /* Make sure id is in range. */ - PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), + PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), PJ_EINVAL); /* Make sure that transport exists */ PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL); tp_type = pjsua_var.tpdata[id].type & ~PJSIP_TRANSPORT_IPV6; - + if ((tp_type == PJSIP_TRANSPORT_TLS) || (tp_type == PJSIP_TRANSPORT_TCP)) { pj_sockaddr bind_addr; pjsip_host_port addr_name; pjsip_tpfactory *factory = pjsua_var.tpdata[id].data.factory; - + int af = pjsip_transport_type_get_af(factory->type); if (cfg->port) pj_sockaddr_init(af, &bind_addr, NULL, (pj_uint16_t)cfg->port); if (cfg->bound_addr.slen) { - status = pj_sockaddr_set_str_addr(af, + status = pj_sockaddr_set_str_addr(af, &bind_addr, &cfg->bound_addr); if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Unable to resolve transport bound address", + pjsua_perror(THIS_FILE, + "Unable to resolve transport bound address", status); return status; } @@ -3090,9 +3090,9 @@ PJ_DEF(pj_status_t) pjsua_transport_lis_start(pjsua_transport_id id, #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0 else { status = pjsip_tls_transport_lis_start(factory, &bind_addr, - &addr_name); + &addr_name); } -#endif +#endif } else if (tp_type == PJSIP_TRANSPORT_UDP) { status = PJ_SUCCESS; } else { @@ -3113,13 +3113,13 @@ void pjsua_process_msg_data(pjsip_tx_data *tdata, const pjsip_hdr *hdr; /* Always add User-Agent */ - if (pjsua_var.ua_cfg.user_agent.slen && - tdata->msg->type == PJSIP_REQUEST_MSG) + if (pjsua_var.ua_cfg.user_agent.slen && + tdata->msg->type == PJSIP_REQUEST_MSG) { const pj_str_t STR_USER_AGENT = { "User-Agent", 10 }; pjsip_hdr *h; - h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool, - &STR_USER_AGENT, + h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool, + &STR_USER_AGENT, &pjsua_var.ua_cfg.user_agent); pjsip_msg_add_hdr(tdata->msg, h); } @@ -3141,7 +3141,7 @@ void pjsua_process_msg_data(pjsip_tx_data *tdata, if (allow_body && msg_data->content_type.slen && msg_data->msg_body.slen) { pjsip_media_type ctype; - pjsip_msg_body *body; + pjsip_msg_body *body; pjsua_parse_media_type(tdata->pool, &msg_data->content_type, &ctype); body = pjsip_msg_body_create(tdata->pool, &ctype.type, &ctype.subtype, @@ -3218,9 +3218,9 @@ void pjsua_parse_media_type( pj_pool_t *pool, pos = pj_strchr(&tmp, '/'); if (pos) { - media_type->type.ptr = tmp.ptr; + media_type->type.ptr = tmp.ptr; media_type->type.slen = (pos-tmp.ptr); - media_type->subtype.ptr = pos+1; + media_type->subtype.ptr = pos+1; media_type->subtype.slen = tmp.ptr+tmp.slen-pos-1; } else { media_type->type = tmp; @@ -3242,7 +3242,7 @@ void pjsua_init_tpselector(pjsua_acc_id acc_id, pjsua_transport_data *tpdata; unsigned flag; - PJ_ASSERT_RETURN(acc->cfg.transport_id >= 0 && + PJ_ASSERT_RETURN(acc->cfg.transport_id >= 0 && acc->cfg.transport_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata), ); tpdata = &pjsua_var.tpdata[acc->cfg.transport_id]; @@ -3273,7 +3273,7 @@ PJ_DEF(void) pjsua_ip_change_param_default(pjsua_ip_change_param *param) /* Callback upon NAT detection completion */ -static void nat_detect_cb(void *user_data, +static void nat_detect_cb(void *user_data, const pj_stun_nat_detect_result *res) { PJ_UNUSED_ARG(user_data); @@ -3312,8 +3312,8 @@ PJ_DEF(pj_status_t) pjsua_detect_nat_type() return PJNATH_ESTUNINSERVER; } - status = pj_stun_detect_nat_type2(&pjsua_var.stun_srv, - &pjsua_var.stun_cfg, + status = pj_stun_detect_nat_type2(&pjsua_var.stun_srv, + &pjsua_var.stun_cfg, NULL, &nat_detect_cb); if (status != PJ_SUCCESS) { @@ -3391,7 +3391,7 @@ PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url) } /* - * Schedule a timer entry. + * Schedule a timer entry. */ #if PJ_TIMER_DEBUG PJ_DEF(pj_status_t) pjsua_schedule_timer_dbg( pj_timer_entry *entry, @@ -3430,7 +3430,7 @@ static void timer_cb( pj_timer_heap_t *th, } /* - * Schedule a timer callback. + * Schedule a timer callback. */ #if PJ_TIMER_DEBUG PJ_DEF(pj_status_t) pjsua_schedule_timer2_dbg( void (*cb)(void *user_data), @@ -3488,7 +3488,7 @@ PJ_DEF(void) pjsua_cancel_timer(pj_timer_entry *entry) pjsip_endpt_cancel_timer(pjsua_var.endpt, entry); } -/** +/** * Normalize route URI (check for ";lr" and append one if it doesn't * exist and pjsua_config.force_lr is set. */ @@ -3507,16 +3507,16 @@ pj_status_t normalize_route_uri(pj_pool_t *pool, pj_str_t *uri) uri_obj = pjsip_parse_uri(tmp_pool, tmp_uri.ptr, tmp_uri.slen, 0); if (!uri_obj) { - PJ_LOG(1,(THIS_FILE, "Invalid route URI: %.*s", + PJ_LOG(1,(THIS_FILE, "Invalid route URI: %.*s", (int)uri->slen, uri->ptr)); pj_pool_release(tmp_pool); return PJSIP_EINVALIDURI; } - if (!PJSIP_URI_SCHEME_IS_SIP(uri_obj) && + if (!PJSIP_URI_SCHEME_IS_SIP(uri_obj) && !PJSIP_URI_SCHEME_IS_SIPS(uri_obj)) { - PJ_LOG(1,(THIS_FILE, "Route URI must be SIP URI: %.*s", + PJ_LOG(1,(THIS_FILE, "Route URI must be SIP URI: %.*s", (int)uri->slen, uri->ptr)); pj_pool_release(tmp_pool); return PJSIP_EINVALIDSCHEME; @@ -3535,10 +3535,10 @@ pj_status_t normalize_route_uri(pj_pool_t *pool, pj_str_t *uri) /* Print the URI */ tmp_uri.ptr = (char*) pj_pool_alloc(tmp_pool, PJSIP_MAX_URL_SIZE); - tmp_uri.slen = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, uri_obj, + tmp_uri.slen = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, uri_obj, tmp_uri.ptr, PJSIP_MAX_URL_SIZE); if (tmp_uri.slen < 1) { - PJ_LOG(1,(THIS_FILE, "Route URI is too long: %.*s", + PJ_LOG(1,(THIS_FILE, "Route URI is too long: %.*s", (int)uri->slen, uri->ptr)); pj_pool_release(tmp_pool); return PJSIP_EURITOOLONG; @@ -3623,7 +3623,7 @@ PJ_DEF(void) pjsua_dump(pj_bool_t detail) pjsip_tsx_layer_dump(detail); pjsip_ua_dump(detail); -// Dumping complete call states may require a 'large' buffer +// Dumping complete call states may require a 'large' buffer // (about 3KB per call session, including RTCP XR). #if 0 /* Dump all invite sessions: */ @@ -3638,7 +3638,7 @@ PJ_DEF(void) pjsua_dump(pj_bool_t detail) for (i=0; i