diff options
author | Mario Preksavec <mario@slackware.hr> | 2018-10-28 01:29:00 +0200 |
---|---|---|
committer | Willy Sudiarto Raharjo <willysr@slackbuilds.org> | 2018-11-03 07:46:59 +0700 |
commit | a68fd92ff0bc4473d07eeffea093acf4bf6a8f02 (patch) | |
tree | 7bd46461a5095ed7c0f9d7eacf0fff5335da2606 /libraries/pjproject-ring | |
parent | 6f07b04688b6a311d651dc1d32f2d8d97845e0aa (diff) | |
download | slackbuilds-a68fd92ff0bc4473d07eeffea093acf4bf6a8f02.tar.gz |
libraries/pjproject-ring: Removed.
Signed-off-by: Mario Preksavec <mario@slackware.hr>
Diffstat (limited to 'libraries/pjproject-ring')
24 files changed, 0 insertions, 7290 deletions
diff --git a/libraries/pjproject-ring/README b/libraries/pjproject-ring/README deleted file mode 100644 index 9507d7350d..0000000000 --- a/libraries/pjproject-ring/README +++ /dev/null @@ -1,8 +0,0 @@ -PJSIP is a free and open source multimedia communication library written -in C language implementing standard based protocols such as SIP, SDP, -RTP, STUN, TURN, and ICE. It combines signaling protocol (SIP) with rich -multimedia framework and NAT traversal functionality into high level API -that is portable and suitable for almost any type of systems ranging from -desktops, embedded systems, to mobile handsets. - -This package includes patches from Ring project. diff --git a/libraries/pjproject-ring/patches/add_dtls_transport.patch b/libraries/pjproject-ring/patches/add_dtls_transport.patch deleted file mode 100644 index b7f9f296e7..0000000000 --- a/libraries/pjproject-ring/patches/add_dtls_transport.patch +++ /dev/null @@ -1,63 +0,0 @@ ---- a/pjsip/src/pjsip/sip_transport.c -+++ b/pjsip/src/pjsip/sip_transport.c -@@ -183,6 +183,13 @@ - PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE - }, - { -+ PJSIP_TRANSPORT_DTLS, -+ 5061, -+ {"DTLS", 4}, -+ "DTLS transport", -+ PJSIP_TRANSPORT_SECURE -+ }, -+ { - PJSIP_TRANSPORT_SCTP, - 5060, - {"SCTP", 4}, -@@ -224,6 +231,13 @@ - "TLS IPv6 transport", - PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE - }, -+ { -+ PJSIP_TRANSPORT_DTLS6, -+ 5061, -+ {"DTLS", 4}, -+ "DTLS IPv6 transport", -+ PJSIP_TRANSPORT_SECURE -+ }, - }; - - static void tp_state_callback(pjsip_transport *tp, -@@ -249,7 +263,7 @@ - */ - PJ_DEF(pj_status_t) pjsip_transport_register_type( unsigned tp_flag, - const char *tp_name, -- int def_port, -+ int def_port, - int *p_tp_type) - { - unsigned i; ---- a/pjsip/include/pjsip/sip_types.h -+++ b/pjsip/include/pjsip/sip_types.h -@@ -73,6 +73,9 @@ - /** TLS. */ - PJSIP_TRANSPORT_TLS, - -+ /** DTLS. */ -+ PJSIP_TRANSPORT_DTLS, -+ - /** SCTP. */ - PJSIP_TRANSPORT_SCTP, - -@@ -95,7 +98,10 @@ - PJSIP_TRANSPORT_TCP6 = PJSIP_TRANSPORT_TCP + PJSIP_TRANSPORT_IPV6, - - /** TLS over IPv6 */ -- PJSIP_TRANSPORT_TLS6 = PJSIP_TRANSPORT_TLS + PJSIP_TRANSPORT_IPV6 -+ PJSIP_TRANSPORT_TLS6 = PJSIP_TRANSPORT_TLS + PJSIP_TRANSPORT_IPV6, -+ -+ /** DTLS over IPv6 */ -+ PJSIP_TRANSPORT_DTLS6 = PJSIP_TRANSPORT_DTLS + PJSIP_TRANSPORT_IPV6 - - } pjsip_transport_type_e; - diff --git a/libraries/pjproject-ring/patches/android.patch b/libraries/pjproject-ring/patches/android.patch deleted file mode 100644 index 01621b9886..0000000000 --- a/libraries/pjproject-ring/patches/android.patch +++ /dev/null @@ -1,49 +0,0 @@ ---- a/pjlib/include/pj/config.h -+++ b/pjlib/include/pj/config.h -@@ -293,6 +293,21 @@ - */ - #include <pj/config_site.h> - -+#undef PJ_ANDROID -+#define PJ_ANDROID 0 -+#define PJ_JNI_HAS_JNI_ONLOAD 0 -+#undef PJ_HAS_FLOATING_POINT -+#define PJ_HAS_FLOATING_POINT 0 -+#define PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO 0 -+#define PJMEDIA_AUDIO_DEV_HAS_WMME 0 -+#define PJMEDIA_AUDIO_DEV_HAS_OPENSL 0 -+#define PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI 0 -+#define PJMEDIA_HAS_L16_CODEC 0 -+#define PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY 5 -+#define PJMEDIA_VID_DEV_INFO_FMT_CNT 128 -+#define PJSIP_MAX_TSX_COUNT 31 -+#define PJSIP_MAX_DIALOG_COUNT 31 -+ - /******************************************************************** - * PJLIB Features. - */ ---- a/aconfigure -+++ b/aconfigure -@@ -5809,6 +5809,3 @@ - case $target in -- *android*) -- ac_os_objs="$ac_os_objs guid_android.o" -- ;; - *) - if test "$ac_has_uuid_lib" = "1" -a "$ac_has_uuid_h" = "1"; then ---- a/aconfigure.ac -+++ b/aconfigure.ac -@@ -457,6 +457,3 @@ - case $target in -- *android*) -- ac_os_objs="$ac_os_objs guid_android.o" -- ;; - *) - if test "$ac_has_uuid_lib" = "1" -a "$ac_has_uuid_h" = "1"; then ---- a/pjlib/src/pj/os_timestamp_posix.c -+++ b/pjlib/src/pj/os_timestamp_posix.c -@@ -163,3 +163,3 @@ - --#elif defined(__ANDROID__) -+#elif defined(PJ_ANDROID) && PJ_ANDROID -
\ No newline at end of file diff --git a/libraries/pjproject-ring/patches/endianness.patch b/libraries/pjproject-ring/patches/endianness.patch deleted file mode 100644 index 84b9499448..0000000000 --- a/libraries/pjproject-ring/patches/endianness.patch +++ /dev/null @@ -1,19 +0,0 @@ -diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h -index 10f86fd..4ace1bc 100644 ---- a/pjlib/include/pj/config.h -+++ b/pjlib/include/pj/config.h -@@ -245,7 +245,13 @@ - # define PJ_M_NAME "armv4" - # define PJ_HAS_PENTIUM 0 - # if !PJ_IS_LITTLE_ENDIAN && !PJ_IS_BIG_ENDIAN --# error Endianness must be declared for this processor -+# if defined(__GNUC__) -+# include <endian.h> -+# define PJ_IS_LITTLE_ENDIAN __BYTE_ORDER__ == __LITTLE_ENDIAN__ -+# define PJ_IS_BIG_ENDIAN __BYTE_ORDER__ == __BIG_ENDIAN__ -+# else -+# error Endianness must be declared for this processor -+# endif - # endif - - #elif defined (PJ_M_POWERPC) || defined(__powerpc) || defined(__powerpc__) || \ diff --git a/libraries/pjproject-ring/patches/fix_base64.patch b/libraries/pjproject-ring/patches/fix_base64.patch deleted file mode 100644 index 70bdd5054d..0000000000 --- a/libraries/pjproject-ring/patches/fix_base64.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- a/pjlib-util/src/pjlib-util/base64.c 2017-05-03 10:29:07.200417026 -0400 -+++ b/pjlib-util/src/pjlib-util/base64.c 2017-05-03 10:28:30.344335390 -0400 -@@ -131,7 +131,7 @@ - - PJ_ASSERT_RETURN(input && out && out_len, PJ_EINVAL); - -- while (buf[len-1] == '=' && len) -+ while (len && buf[len-1] == '=') - --len; - - PJ_ASSERT_RETURN(*out_len >= PJ_BASE64_TO_BASE256_LEN(len), -@@ -161,7 +161,7 @@ - out[j++] = (pj_uint8_t)(((c[2] & 0x03)<<6) | (c[3] & 0x3F)); - } - -- pj_assert(j < *out_len); -+ pj_assert(j <= *out_len); - *out_len = j; - - return PJ_SUCCESS; diff --git a/libraries/pjproject-ring/patches/fix_ioqueue_ipv6_sendto.patch b/libraries/pjproject-ring/patches/fix_ioqueue_ipv6_sendto.patch deleted file mode 100644 index bc53fd2cb4..0000000000 --- a/libraries/pjproject-ring/patches/fix_ioqueue_ipv6_sendto.patch +++ /dev/null @@ -1,19 +0,0 @@ ---- a/pjlib/src/pj/ioqueue_common_abs.c 2015-11-05 23:18:46.000000000 -0500 -+++ b/pjlib/src/pj/ioqueue_common_abs.c 2016-10-21 13:49:09.183662433 -0400 -@@ -1048,5 +1048,6 @@ - * Check that address storage can hold the address parameter. - */ -- PJ_ASSERT_RETURN(addrlen <= (int)sizeof(pj_sockaddr_in), 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); - - /* ---- a/pjlib/src/pj/ioqueue_common_abs.h 2013-02-21 06:18:36.000000000 -0500 -+++ b/pjlib/src/pj/ioqueue_common_abs.h 2016-10-21 14:04:04.148928591 -0400 -@@ -64,5 +64,5 @@ - pj_ssize_t written; - unsigned flags; -- pj_sockaddr_in rmt_addr; -+ pj_sockaddr rmt_addr; - int rmt_addrlen; - }; diff --git a/libraries/pjproject-ring/patches/fix_turn_fallback.patch b/libraries/pjproject-ring/patches/fix_turn_fallback.patch deleted file mode 100644 index 50229dd01c..0000000000 --- a/libraries/pjproject-ring/patches/fix_turn_fallback.patch +++ /dev/null @@ -1,53 +0,0 @@ ---- a/pjnath/src/pjnath/turn_session.c 2016-09-19 18:21:09.073614574 -0400 -+++ b/pjnath/src/pjnath/turn_session.c 2016-09-19 18:21:30.648631620 -0400 -@@ -653,3 +653,3 @@ - -- cnt = PJ_TURN_MAX_DNS_SRV_CNT; -+ cnt = 1; - ai = (pj_addrinfo*) ---- a/pjnath/src/pjnath/ice_strans.c 2016-09-19 18:36:04.180104330 -0400 -+++ b/pjnath/src/pjnath/ice_strans.c 2016-09-19 18:37:10.614136809 -0400 -@@ -1304,2 +1304,5 @@ - -+ if (!comp->turn[n].sock) -+ continue; -+ - /* Gather remote addresses for this component */ -@@ -1995,4 +1998,37 @@ - sess_init_update(comp->ice_st); - -+ } else if ((old_state == PJ_TURN_STATE_RESOLVING || old_state == PJ_TURN_STATE_ALLOCATING) && -+ new_state >= PJ_TURN_STATE_DEALLOCATING) -+ { -+ pj_ice_sess_cand *cand = NULL; -+ unsigned i; -+ -+ /* DNS resolution has failed! */ -+ ++comp->turn[tp_idx].err_cnt; -+ -+ /* Unregister ourself from the TURN relay */ -+ pj_turn_sock_set_user_data(turn_sock, NULL); -+ comp->turn[tp_idx].sock = NULL; -+ -+ /* Wait until initialization completes */ -+ pj_grp_lock_acquire(comp->ice_st->grp_lock); -+ -+ /* Find relayed candidate in the component */ -+ for (i=0; i<comp->cand_cnt; ++i) { -+ if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_RELAYED && -+ comp->cand_list[i].transport_id == data->transport_id) -+ { -+ cand = &comp->cand_list[i]; -+ break; -+ } -+ } -+ pj_assert(cand != NULL); -+ -+ pj_grp_lock_release(comp->ice_st->grp_lock); -+ -+ cand->status = old_state == PJ_TURN_STATE_RESOLVING ? PJ_ERESOLVE : PJ_EINVALIDOP; -+ -+ sess_init_update(comp->ice_st); -+ - } else if (new_state >= PJ_TURN_STATE_DEALLOCATING) { - pj_turn_session_info info; diff --git a/libraries/pjproject-ring/patches/gnutls.patch b/libraries/pjproject-ring/patches/gnutls.patch deleted file mode 100644 index e8b26657ea..0000000000 --- a/libraries/pjproject-ring/patches/gnutls.patch +++ /dev/null @@ -1,3295 +0,0 @@ -Copyright (c) 2014-2017 Savoir-faire Linux Inc. - -ssl_sock: add gnutls backend - -This backend is mutually exclusive with the OpenSSL one, but completely -compatible, and conformant to the PJSIP API. Also avoids any license issues -when linking statically. - -The configure script is updated to select either OpenSSL or GnuTLS -with --enable-ssl[='...'] and a new symbol (PJ_HAS_TLS_SOCK) is introduced -to identify which backend is in use. - -Written by -Vittorio Giovara <vittorio.giovara@savoirfairelinux.com> -Philippe Proulx <philippe.proulx@savoirfairelinux.com> and -Adrien BĂ©raud <adrien.beraud@savoirfairelinux.com> -on behalf of Savoir-faire Linux. - ---- -diff -ru a/aconfigure b/aconfigure ---- a/aconfigure 2017-01-25 06:23:08.000000000 -0500 -+++ b/aconfigure 2017-06-08 13:51:11.146810527 -0400 -@@ -644,6 +644,8 @@ - libcrypto_present - libssl_present - openssl_h_present -+libgnutls_present -+gnutls_h_present - ac_ssl_has_aes_gcm - ac_no_ssl - ac_openh264_ldflags -@@ -1482,8 +1484,8 @@ - package and samples location using IPPROOT and - IPPSAMPLES env var or with --with-ipp and - --with-ipp-samples options -- --disable-ssl Exclude SSL support the build (default: autodetect) -- -+ --enable-ssl=backend Select 'gnutls' or 'openssl' (default) to provide -+ SSL support (autodetect) - --disable-opencore-amr Exclude OpenCORE AMR support from the build - (default: autodetect) - -@@ -7787,17 +7789,149 @@ - - # Check whether --enable-ssl was given. - if test "${enable_ssl+set}" = set; then : -- enableval=$enable_ssl; -- if test "$enable_ssl" = "no"; then -- ac_no_ssl=1 -- { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if SSL support is disabled... yes" >&5 -+ enableval=$enable_ssl; if test "x$enableval" = "xgnutls"; then -+ ssl_backend="gnutls" -+ else -+ ssl_backend="openssl" -+ fi -+fi -+ -+ -+if test "x$enable_ssl" = "xno"; then -+ ac_no_ssl=1 -+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if SSL support is disabled... yes" >&5 - $as_echo "Checking if SSL support is disabled... yes" >&6; } -- fi -+else -+ if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then -+ CFLAGS="$CFLAGS -I$with_ssl/include" -+ LDFLAGS="$LDFLAGS -L$with_ssl/lib" -+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using SSL prefix... $with_ssl" >&5 -+$as_echo "Using SSL prefix... $with_ssl" >&6; } -+ fi -+ if test "x$ssl_backend" = "xgnutls"; then -+ for ac_prog in $host-pkg-config pkg-config "python pkgconfig.py" -+do -+ # Extract the first word of "$ac_prog", so it can be a program name with args. -+set dummy $ac_prog; ac_word=$2 -+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -+$as_echo_n "checking for $ac_word... " >&6; } -+if ${ac_cv_prog_PKG_CONFIG+:} false; then : -+ $as_echo_n "(cached) " >&6 -+else -+ if test -n "$PKG_CONFIG"; then -+ ac_cv_prog_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test. -+else -+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -+for as_dir in $PATH -+do -+ IFS=$as_save_IFS -+ test -z "$as_dir" && as_dir=. -+ for ac_exec_ext in '' $ac_executable_extensions; do -+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then -+ ac_cv_prog_PKG_CONFIG="$ac_prog" -+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 -+ break 2 -+ fi -+done -+ done -+IFS=$as_save_IFS -+ -+fi -+fi -+PKG_CONFIG=$ac_cv_prog_PKG_CONFIG -+if test -n "$PKG_CONFIG"; then -+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 -+$as_echo "$PKG_CONFIG" >&6; } -+else -+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -+$as_echo "no" >&6; } -+fi -+ -+ -+ test -n "$PKG_CONFIG" && break -+done -+test -n "$PKG_CONFIG" || PKG_CONFIG="none" -+ -+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: checking for GnuTLS installations.." >&5 -+$as_echo "checking for GnuTLS installations.." >&6; } -+ -+ -+ ac_fn_c_check_header_mongrel "$LINENO" "gnutls/gnutls.h" "ac_cv_header_gnutls_gnutls_h" "$ac_includes_default" -+if test "x$ac_cv_header_gnutls_gnutls_h" = xyes; then : -+ gnutls_h_present=1 -+fi -+ - -+ -+ if test "$PKG_CONFIG" != "none"; then -+ if $PKG_CONFIG --exists gnutls; then -+ LIBS="$LIBS `$PKG_CONFIG --libs gnutls`" -+ libgnutls_present=1 -+ else -+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gnutls_certificate_set_x509_system_trust in -lgnutls" >&5 -+$as_echo_n "checking for gnutls_certificate_set_x509_system_trust in -lgnutls... " >&6; } -+if ${ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust+:} false; then : -+ $as_echo_n "(cached) " >&6 -+else -+ ac_check_lib_save_LIBS=$LIBS -+LIBS="-lgnutls $LIBS" -+cat confdefs.h - <<_ACEOF >conftest.$ac_ext -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char gnutls_certificate_set_x509_system_trust (); -+int -+main () -+{ -+return gnutls_certificate_set_x509_system_trust (); -+ ; -+ return 0; -+} -+_ACEOF -+if ac_fn_c_try_link "$LINENO"; then : -+ ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust=yes - else -+ ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust=no -+fi -+rm -f core conftest.err conftest.$ac_objext \ -+ conftest$ac_exeext conftest.$ac_ext -+LIBS=$ac_check_lib_save_LIBS -+fi -+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust" >&5 -+$as_echo "$ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust" >&6; } -+if test "x$ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust" = xyes; then : -+ libgnutls_present=1 && -+ LIBS="$LIBS -lgnutls" -+fi - -+ fi -+ else -+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: *** Warning: neither pkg-config nor python is available, disabling gnutls. ***" >&5 -+$as_echo "*** Warning: neither pkg-config nor python is available, disabling gnutls. ***" >&6; } -+ fi -+ -+ if test "x$gnutls_h_present" = "x1" -a "x$libgnutls_present" = "x1"; then -+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: GnuTLS library found, SSL support enabled" >&5 -+$as_echo "GnuTLS library found, SSL support enabled" >&6; } -+ # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK -+ #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1) -+ $as_echo "#define PJ_HAS_SSL_SOCK 1" >>confdefs.h -+ -+ $as_echo "#define PJ_HAS_TLS_SOCK 1" >>confdefs.h -+ -+ else -+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ** No GnuTLS libraries found, disabling SSL support **" >&5 -+$as_echo "** No GnuTLS libraries found, disabling SSL support **" >&6; } -+ fi -+ else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: checking for OpenSSL installations.." >&5 - $as_echo "checking for OpenSSL installations.." >&6; } -+ - if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then - CFLAGS="$CFLAGS -I$with_ssl/include" - LDFLAGS="$LDFLAGS -L$with_ssl/lib" -@@ -7971,11 +8105,10 @@ - { $as_echo "$as_me:${as_lineno-$LINENO}: result: ** OpenSSL libraries not found, disabling SSL support **" >&5 - $as_echo "** OpenSSL libraries not found, disabling SSL support **" >&6; } - fi -- -+ fi - fi - - -- - # Check whether --with-opencore-amrnb was given. - if test "${with_opencore_amrnb+set}" = set; then : - withval=$with_opencore_amrnb; as_fn_error $? "This option is obsolete and replaced by --with-opencore-amr=DIR" "$LINENO" 5 -diff -ru a/aconfigure.ac b/aconfigure.ac ---- a/aconfigure.ac 2017-01-25 06:23:08.000000000 -0500 -+++ b/aconfigure.ac 2017-06-08 13:28:17.138135490 -0400 -@@ -1533,18 +1533,59 @@ - dnl # Include SSL support - AC_SUBST(ac_no_ssl) - AC_SUBST(ac_ssl_has_aes_gcm,0) --AC_ARG_ENABLE(ssl, -- AS_HELP_STRING([--disable-ssl], -- [Exclude SSL support the build (default: autodetect)]) -- , -- [ -- if test "$enable_ssl" = "no"; then -- [ac_no_ssl=1] -- AC_MSG_RESULT([Checking if SSL support is disabled... yes]) -- fi -- ], -- [ -+AC_ARG_ENABLE([ssl], -+ AS_HELP_STRING([--enable-ssl[=backend]], -+ [Select 'gnutls' or 'openssl' (default) to provide SSL support (autodetect)]), -+ [ if test "x$enableval" = "xgnutls"; then -+ [ssl_backend="gnutls"] -+ else -+ [ssl_backend="openssl"] -+ fi ]) -+ -+if test "x$enable_ssl" = "xno"; then -+ [ac_no_ssl=1] -+ AC_MSG_RESULT([Checking if SSL support is disabled... yes]) -+else -+ if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then -+ CFLAGS="$CFLAGS -I$with_ssl/include" -+ LDFLAGS="$LDFLAGS -L$with_ssl/lib" -+ AC_MSG_RESULT([Using SSL prefix... $with_ssl]) -+ fi -+ if test "x$ssl_backend" = "xgnutls"; then -+ AC_CHECK_PROGS(PKG_CONFIG, -+ $host-pkg-config pkg-config "python pkgconfig.py", -+ none) -+ AC_MSG_RESULT([checking for GnuTLS installations..]) -+ AC_SUBST(gnutls_h_present) -+ AC_SUBST(libgnutls_present) -+ AC_CHECK_HEADER(gnutls/gnutls.h, [gnutls_h_present=1]) -+ -+ if test "$PKG_CONFIG" != "none"; then -+ if $PKG_CONFIG --exists gnutls; then -+ LIBS="$LIBS `$PKG_CONFIG --libs gnutls`" -+ libgnutls_present=1 -+ else -+ AC_CHECK_LIB(gnutls, -+ gnutls_certificate_set_x509_system_trust, -+ [libgnutls_present=1 && -+ LIBS="$LIBS -lgnutls"]) -+ fi -+ else -+ AC_MSG_RESULT([*** Warning: neither pkg-config nor python is available, disabling gnutls. ***]) -+ fi -+ -+ if test "x$gnutls_h_present" = "x1" -a "x$libgnutls_present" = "x1"; then -+ AC_MSG_RESULT([GnuTLS library found, SSL support enabled]) -+ # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK -+ #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1) -+ AC_DEFINE(PJ_HAS_SSL_SOCK, 1) -+ AC_DEFINE(PJ_HAS_TLS_SOCK, 1) -+ else -+ AC_MSG_RESULT([** No GnuTLS libraries found, disabling SSL support **]) -+ fi -+ else - AC_MSG_RESULT([checking for OpenSSL installations..]) -+ - if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then - CFLAGS="$CFLAGS -I$with_ssl/include" - LDFLAGS="$LDFLAGS -L$with_ssl/lib" -@@ -1578,7 +1619,8 @@ - else - AC_MSG_RESULT([** OpenSSL libraries not found, disabling SSL support **]) - fi -- ]) -+ fi -+fi - - dnl # Obsolete option --with-opencore-amrnb - AC_ARG_WITH(opencore-amrnb, -diff -ru a/pjlib/build/Makefile b/pjlib/build/Makefile ---- a/pjlib/build/Makefile 2016-10-05 05:52:39.000000000 -0400 -+++ b/pjlib/build/Makefile 2017-06-08 13:30:20.702138591 -0400 -@@ -35,7 +35,7 @@ - guid.o hash.o ip_helper_generic.o list.o lock.o log.o os_time_common.o \ - os_info.o pool.o pool_buf.o pool_caching.o pool_dbg.o rand.o \ - rbtree.o sock_common.o sock_qos_common.o \ -- ssl_sock_common.o ssl_sock_ossl.o ssl_sock_dump.o \ -+ ssl_sock_common.o ssl_sock_ossl.o ssl_sock_gtls.o ssl_sock_dump.o \ - string.o timer.o types.o - export PJLIB_CFLAGS += $(_CFLAGS) - export PJLIB_CXXFLAGS += $(_CXXFLAGS) -diff -ru a/pjlib/include/pj/compat/os_auto.h.in b/pjlib/include/pj/compat/os_auto.h.in ---- a/pjlib/include/pj/compat/os_auto.h.in 2017-01-24 00:36:50.000000000 -0500 -+++ b/pjlib/include/pj/compat/os_auto.h.in 2017-06-08 13:31:04.976064779 -0400 -@@ -219,6 +219,9 @@ - #ifndef PJ_HAS_SSL_SOCK - #undef PJ_HAS_SSL_SOCK - #endif -+#ifndef PJ_HAS_TLS_SOCK -+#undef PJ_HAS_TLS_SOCK -+#endif - - - #endif /* __PJ_COMPAT_OS_AUTO_H__ */ -diff -ru a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h ---- a/pjlib/include/pj/config.h 2017-01-25 21:29:59.000000000 -0500 -+++ b/pjlib/include/pj/config.h 2017-06-08 13:34:27.642149351 -0400 -@@ -888,7 +888,7 @@ - - /** - * Enable secure socket. For most platforms, this is implemented using -- * OpenSSL, so this will require OpenSSL to be installed. For Symbian -+ * OpenSSL or GnuTLS, so this will require OpenSSL or GnuTLS to be installed. For Symbian - * platform, this is implemented natively using CSecureSocket. - * - * Default: 0 (for now) -@@ -896,6 +896,10 @@ - #ifndef PJ_HAS_SSL_SOCK - # define PJ_HAS_SSL_SOCK 0 - #endif -+// When set to 1 secure sockets will use the GnuTLS backend than OpenSSL -+#ifndef PJ_HAS_TLS_SOCK -+# define PJ_HAS_TLS_SOCK 0 -+#endif - - - /** -diff -ru a/pjlib/include/pj/ssl_sock.h b/pjlib/include/pj/ssl_sock.h ---- a/pjlib/include/pj/ssl_sock.h 2016-10-27 03:58:01.000000000 -0400 -+++ b/pjlib/include/pj/ssl_sock.h 2017-06-08 13:36:16.448510381 -0400 -@@ -184,6 +184,10 @@ - pj_str_t raw; /**< Raw certificate in PEM format, only - available for remote certificate. */ - -+ struct { -+ unsigned cnt; /**< # of entry */ -+ pj_str_t* cert_raw; -+ } raw_chain; - } pj_ssl_cert_info; - - -diff -ru a/pjlib/src/pj/ssl_sock_common.c b/pjlib/src/pj/ssl_sock_common.c ---- a/pjlib/src/pj/ssl_sock_common.c 2016-10-27 03:58:01.000000000 -0400 -+++ b/pjlib/src/pj/ssl_sock_common.c 2017-06-08 13:37:17.171037628 -0400 -@@ -35,7 +35,12 @@ - param->async_cnt = 1; - param->concurrency = -1; - param->whole_data = PJ_TRUE; -+#if defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK == 1 -+ // GnuTLS is allowed to send bigger chunks -+ param->send_buffer_size = 65536; -+#else - param->send_buffer_size = 8192; -+#endif - #if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0 - param->read_buffer_size = 1500; - #endif -diff --git a/pjlib/src/pj/ssl_sock_gtls.c b/pjlib/src/pj/ssl_sock_gtls.c -new file mode 100644 -index 0000000..37bcaba ---- /dev/null -+++ b/pjlib/src/pj/ssl_sock_gtls.c -@@ -0,0 +1,2877 @@ -+/* $Id$ */ -+/* -+ * Copyright (C) 2014-2016 Savoir-faire Linux. (https://www.savoirfairelinux.com) -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#include <pj/ssl_sock.h> -+#include <pj/activesock.h> -+#include <pj/compat/socket.h> -+#include <pj/assert.h> -+#include <pj/errno.h> -+#include <pj/list.h> -+#include <pj/lock.h> -+#include <pj/log.h> -+#include <pj/math.h> -+#include <pj/os.h> -+#include <pj/pool.h> -+#include <pj/string.h> -+#include <pj/timer.h> -+#include <pj/file_io.h> -+ -+#if GNUTLS_VERSION_NUMBER < 0x030306 && !defined(_MSC_VER) -+#include <dirent.h> -+#endif -+ -+#include <errno.h> -+ -+/* Only build when PJ_HAS_SSL_SOCK and PJ_HAS_TLS_SOCK are enabled */ -+#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \ -+ defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK != 0 -+ -+#define THIS_FILE "ssl_sock_gtls.c" -+ -+/* Workaround for ticket #985 */ -+#define DELAYED_CLOSE_TIMEOUT 200 -+ -+/* Maximum ciphers */ -+#define MAX_CIPHERS 100 -+ -+/* Standard trust locations */ -+#define TRUST_STORE_FILE1 "/etc/ssl/certs/ca-certificates.crt" -+#define TRUST_STORE_FILE2 "/etc/ssl/certs/ca-bundle.crt" -+ -+/* Debugging output level for GnuTLS only */ -+#define GNUTLS_LOG_LEVEL 0 -+ -+/* GnuTLS includes */ -+#include <gnutls/gnutls.h> -+#include <gnutls/x509.h> -+#include <gnutls/abstract.h> -+ -+#ifdef _MSC_VER -+# pragma comment( lib, "libgnutls") -+#endif -+ -+ -+/* TLS state enumeration. */ -+enum tls_connection_state { -+ TLS_STATE_NULL, -+ TLS_STATE_HANDSHAKING, -+ TLS_STATE_ESTABLISHED -+}; -+ -+/* Internal timer types. */ -+enum timer_id { -+ TIMER_NONE, -+ TIMER_HANDSHAKE_TIMEOUT, -+ TIMER_CLOSE -+}; -+ -+/* Structure of SSL socket read buffer. */ -+typedef struct read_data_t { -+ void *data; -+ pj_size_t len; -+} read_data_t; -+ -+/* -+ * Get the offset of pointer to read-buffer of SSL socket from read-buffer -+ * of active socket. Note that both SSL socket and active socket employ -+ * different but correlated read-buffers (as much as async_cnt for each), -+ * and to make it easier/faster to find corresponding SSL socket's read-buffer -+ * from known active socket's read-buffer, the pointer of corresponding -+ * SSL socket's read-buffer is stored right after the end of active socket's -+ * read-buffer. -+ */ -+#define OFFSET_OF_READ_DATA_PTR(ssock, asock_rbuf) \ -+ (read_data_t**) \ -+ ((pj_int8_t *)(asock_rbuf) + \ -+ ssock->param.read_buffer_size) -+ -+/* Structure of SSL socket write data. */ -+typedef struct write_data_t { -+ PJ_DECL_LIST_MEMBER(struct write_data_t); -+ pj_ioqueue_op_key_t key; -+ pj_size_t record_len; -+ pj_ioqueue_op_key_t *app_key; -+ pj_size_t plain_data_len; -+ pj_size_t data_len; -+ unsigned flags; -+ union { -+ char content[1]; -+ const char *ptr; -+ } data; -+} write_data_t; -+ -+ -+/* Structure of SSL socket write buffer (circular buffer). */ -+typedef struct send_buf_t { -+ char *buf; -+ pj_size_t max_len; -+ char *start; -+ pj_size_t len; -+} send_buf_t; -+ -+ -+/* Circular buffer object */ -+typedef struct circ_buf_t { -+ pj_size_t cap; /* maximum number of elements (must be power of 2) */ -+ pj_size_t readp; /* index of oldest element */ -+ pj_size_t writep; /* index at which to write new element */ -+ pj_size_t size; /* number of elements */ -+ pj_uint8_t *buf; /* data buffer */ -+ pj_pool_t *pool; /* where new allocations will take place */ -+} circ_buf_t; -+ -+ -+/* Secure socket structure definition. */ -+struct pj_ssl_sock_t { -+ pj_pool_t *pool; -+ pj_ssl_sock_t *parent; -+ pj_ssl_sock_param param; -+ pj_ssl_sock_param newsock_param; -+ pj_ssl_cert_t *cert; -+ -+ pj_ssl_cert_info local_cert_info; -+ pj_ssl_cert_info remote_cert_info; -+ -+ pj_bool_t is_server; -+ enum tls_connection_state connection_state; -+ pj_ioqueue_op_key_t handshake_op_key; -+ pj_timer_entry timer; -+ pj_status_t verify_status; -+ -+ int last_err; -+ -+ pj_sock_t sock; -+ pj_activesock_t *asock; -+ -+ pj_sockaddr local_addr; -+ pj_sockaddr rem_addr; -+ int addr_len; -+ -+ pj_bool_t read_started; -+ pj_size_t read_size; -+ pj_uint32_t read_flags; -+ void **asock_rbuf; -+ read_data_t *ssock_rbuf; -+ -+ write_data_t write_pending; /* list of pending writes */ -+ write_data_t write_pending_empty; /* cache for write_pending */ -+ pj_bool_t flushing_write_pend; /* flag of flushing is ongoing */ -+ send_buf_t send_buf; -+ write_data_t send_pending; /* list of pending write to network */ -+ -+ gnutls_session_t session; -+ gnutls_certificate_credentials_t xcred; -+ -+ circ_buf_t circ_buf_input; -+ pj_lock_t *circ_buf_input_mutex; -+ -+ circ_buf_t circ_buf_output; -+ pj_lock_t *circ_buf_output_mutex; -+ -+ int tls_init_count; /* library initialization counter */ -+}; -+ -+ -+/* Certificate/credential structure definition. */ -+struct pj_ssl_cert_t { -+ pj_str_t CA_file; -+ pj_str_t CA_path; -+ pj_str_t cert_file; -+ pj_str_t privkey_file; -+ pj_str_t privkey_pass; -+}; -+ -+/* GnuTLS available ciphers */ -+static unsigned tls_available_ciphers; -+ -+/* Array of id/names for available ciphers */ -+static struct tls_ciphers_t { -+ pj_ssl_cipher id; -+ const char *name; -+} tls_ciphers[MAX_CIPHERS]; -+ -+/* Last error reported somehow */ -+static int tls_last_error; -+ -+ -+/* -+ ******************************************************************* -+ * Circular buffer functions. -+ ******************************************************************* -+ */ -+ -+static pj_status_t circ_init(pj_pool_factory *factory, -+ circ_buf_t *cb, pj_size_t cap) -+{ -+ cb->cap = cap; -+ cb->readp = 0; -+ cb->writep = 0; -+ cb->size = 0; -+ -+ /* Initial pool holding the buffer elements */ -+ cb->pool = pj_pool_create(factory, "tls-circ%p", cap, cap, NULL); -+ if (!cb->pool) -+ return PJ_ENOMEM; -+ -+ /* Allocate circular buffer */ -+ cb->buf = pj_pool_alloc(cb->pool, cap); -+ if (!cb->buf) { -+ pj_pool_release(cb->pool); -+ return PJ_ENOMEM; -+ } -+ -+ return PJ_SUCCESS; -+} -+ -+static void circ_deinit(circ_buf_t *cb) -+{ -+ if (cb->pool) { -+ pj_pool_release(cb->pool); -+ cb->pool = NULL; -+ } -+} -+ -+static pj_bool_t circ_empty(const circ_buf_t *cb) -+{ -+ return cb->size == 0; -+} -+ -+static pj_size_t circ_size(const circ_buf_t *cb) -+{ -+ return cb->size; -+} -+ -+static pj_size_t circ_avail(const circ_buf_t *cb) -+{ -+ return cb->cap - cb->size; -+} -+ -+static void circ_read(circ_buf_t *cb, pj_uint8_t *dst, pj_size_t len) -+{ -+ pj_size_t size_after = cb->cap - cb->readp; -+ pj_size_t tbc = PJ_MIN(size_after, len); -+ pj_size_t rem = len - tbc; -+ -+ pj_memcpy(dst, cb->buf + cb->readp, tbc); -+ pj_memcpy(dst + tbc, cb->buf, rem); -+ -+ cb->readp += len; -+ cb->readp &= (cb->cap - 1); -+ -+ cb->size -= len; -+} -+ -+static pj_status_t circ_write(circ_buf_t *cb, -+ const pj_uint8_t *src, pj_size_t len) -+{ -+ /* Overflow condition: resize */ -+ if (len > circ_avail(cb)) { -+ /* Minimum required capacity */ -+ pj_size_t min_cap = len + cb->size; -+ -+ /* Next 32-bit power of two */ -+ min_cap--; -+ min_cap |= min_cap >> 1; -+ min_cap |= min_cap >> 2; -+ min_cap |= min_cap >> 4; -+ min_cap |= min_cap >> 8; -+ min_cap |= min_cap >> 16; -+ min_cap++; -+ -+ /* Create a new pool to hold a bigger buffer, using the same factory */ -+ pj_pool_t *pool = pj_pool_create(cb->pool->factory, "tls-circ%p", -+ min_cap, min_cap, NULL); -+ if (!pool) -+ return PJ_ENOMEM; -+ -+ /* Allocate our new buffer */ -+ pj_uint8_t *buf = pj_pool_alloc(pool, min_cap); -+ if (!buf) { -+ pj_pool_release(pool); -+ return PJ_ENOMEM; -+ } -+ -+ /* Save old size, which we shall restore after the next read */ -+ pj_size_t old_size = cb->size; -+ -+ /* Copy old data into beginning of new buffer */ -+ circ_read(cb, buf, cb->size); -+ -+ /* Restore old size now */ -+ cb->size = old_size; -+ -+ /* Release the previous pool */ -+ pj_pool_release(cb->pool); -+ -+ /* Update circular buffer members */ -+ cb->pool = pool; -+ cb->buf = buf; -+ cb->readp = 0; -+ cb->writep = cb->size; -+ cb->cap = min_cap; -+ } -+ -+ pj_size_t size_after = cb->cap - cb->writep; -+ pj_size_t tbc = PJ_MIN(size_after, len); -+ pj_size_t rem = len - tbc; -+ -+ pj_memcpy(cb->buf + cb->writep, src, tbc); -+ pj_memcpy(cb->buf, src + tbc, rem); -+ -+ cb->writep += len; -+ cb->writep &= (cb->cap - 1); -+ -+ cb->size += len; -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* -+ ******************************************************************* -+ * Static/internal functions. -+ ******************************************************************* -+ */ -+ -+/* Convert from GnuTLS error to pj_status_t. */ -+static pj_status_t tls_status_from_err(pj_ssl_sock_t *ssock, int err) -+{ -+ pj_status_t status; -+ -+ switch (err) { -+ case GNUTLS_E_SUCCESS: -+ status = PJ_SUCCESS; -+ break; -+ case GNUTLS_E_MEMORY_ERROR: -+ status = PJ_ENOMEM; -+ break; -+ case GNUTLS_E_LARGE_PACKET: -+ status = PJ_ETOOBIG; -+ break; -+ case GNUTLS_E_NO_CERTIFICATE_FOUND: -+ status = PJ_ENOTFOUND; -+ break; -+ case GNUTLS_E_SESSION_EOF: -+ status = PJ_EEOF; -+ break; -+ case GNUTLS_E_HANDSHAKE_TOO_LARGE: -+ status = PJ_ETOOBIG; -+ break; -+ case GNUTLS_E_EXPIRED: -+ status = PJ_EGONE; -+ break; -+ case GNUTLS_E_TIMEDOUT: -+ status = PJ_ETIMEDOUT; -+ break; -+ case GNUTLS_E_PREMATURE_TERMINATION: -+ status = PJ_ECANCELLED; -+ break; -+ case GNUTLS_E_INTERNAL_ERROR: -+ case GNUTLS_E_UNIMPLEMENTED_FEATURE: -+ status = PJ_EBUG; -+ break; -+ case GNUTLS_E_AGAIN: -+ case GNUTLS_E_INTERRUPTED: -+ case GNUTLS_E_REHANDSHAKE: -+ status = PJ_EPENDING; -+ break; -+ case GNUTLS_E_TOO_MANY_EMPTY_PACKETS: -+ case GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS: -+ case GNUTLS_E_RECORD_LIMIT_REACHED: -+ status = PJ_ETOOMANY; -+ break; -+ case GNUTLS_E_UNSUPPORTED_VERSION_PACKET: -+ case GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM: -+ case GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE: -+ case GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE: -+ case GNUTLS_E_X509_UNSUPPORTED_EXTENSION: -+ case GNUTLS_E_X509_UNSUPPORTED_CRITICAL_EXTENSION: -+ status = PJ_ENOTSUP; -+ break; -+ case GNUTLS_E_INVALID_SESSION: -+ case GNUTLS_E_INVALID_REQUEST: -+ case GNUTLS_E_INVALID_PASSWORD: -+ case GNUTLS_E_ILLEGAL_PARAMETER: -+ case GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION: -+ case GNUTLS_E_UNEXPECTED_PACKET: -+ case GNUTLS_E_UNEXPECTED_PACKET_LENGTH: -+ case GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET: -+ case GNUTLS_E_UNWANTED_ALGORITHM: -+ case GNUTLS_E_USER_ERROR: -+ status = PJ_EINVAL; -+ break; -+ default: -+ status = PJ_EUNKNOWN; -+ break; -+ } -+ -+ /* Not thread safe */ -+ tls_last_error = err; -+ if (ssock) -+ ssock->last_err = err; -+ return status; -+} -+ -+ -+/* Get error string from GnuTLS using tls_last_error */ -+static pj_str_t tls_strerror(pj_status_t status, -+ char *buf, pj_size_t bufsize) -+{ -+ pj_str_t errstr; -+ const char *tmp = gnutls_strerror(tls_last_error); -+ -+#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) -+ if (tmp) { -+ pj_ansi_strncpy(buf, tmp, bufsize); -+ errstr = pj_str(buf); -+ return errstr; -+ } -+#endif /* PJ_HAS_ERROR_STRING */ -+ -+ errstr.ptr = buf; -+ errstr.slen = pj_ansi_snprintf(buf, bufsize, "GnuTLS error %d: %s", -+ tls_last_error, tmp); -+ if (errstr.slen < 1 || errstr.slen >= (int) bufsize) -+ errstr.slen = bufsize - 1; -+ -+ return errstr; -+} -+ -+ -+/* Initialize GnuTLS. */ -+static pj_status_t tls_init(void) -+{ -+ /* Register error subsystem */ -+ pj_status_t status = pj_register_strerror(PJ_ERRNO_START_USER + -+ PJ_ERRNO_SPACE_SIZE * 6, -+ PJ_ERRNO_SPACE_SIZE, -+ &tls_strerror); -+ pj_assert(status == PJ_SUCCESS); -+ -+ /* Init GnuTLS library */ -+ int ret = gnutls_global_init(); -+ if (ret < 0) -+ return tls_status_from_err(NULL, ret); -+ -+ /* Init available ciphers */ -+ if (!tls_available_ciphers) { -+ unsigned int i; -+ -+ for (i = 0; ; i++) { -+ unsigned char id[2]; -+ const char *suite = gnutls_cipher_suite_info(i, (unsigned char *)id, -+ NULL, NULL, NULL, NULL); -+ tls_ciphers[i].id = 0; -+ /* usually the array size is bigger than the number of available -+ * ciphers anyway, so by checking here we can exit the loop as soon -+ * as either all ciphers have been added or the array is full */ -+ if (suite && i < PJ_ARRAY_SIZE(tls_ciphers)) { -+ tls_ciphers[i].id = (pj_ssl_cipher) -+ (pj_uint32_t) ((id[0] << 8) | id[1]); -+ tls_ciphers[i].name = suite; -+ } else -+ break; -+ } -+ -+ tls_available_ciphers = i; -+ } -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* Shutdown GnuTLS */ -+static void tls_deinit(void) -+{ -+ gnutls_global_deinit(); -+} -+ -+ -+/* Callback invoked every time a certificate has to be validated. */ -+static int tls_cert_verify_cb(gnutls_session_t session) -+{ -+ pj_ssl_sock_t *ssock; -+ unsigned int status; -+ int ret; -+ -+ /* Get SSL socket instance */ -+ ssock = (pj_ssl_sock_t *)gnutls_session_get_ptr(session); -+ pj_assert(ssock); -+ -+ /* Support only x509 format */ -+ ret = gnutls_certificate_type_get(session) != GNUTLS_CRT_X509; -+ if (ret < 0) { -+ ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT; -+ return GNUTLS_E_CERTIFICATE_ERROR; -+ } -+ -+ /* Store verification status */ -+ ret = gnutls_certificate_verify_peers2(session, &status); -+ if (ret < 0) { -+ ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN; -+ return GNUTLS_E_CERTIFICATE_ERROR; -+ } -+ if (ssock->param.verify_peer) { -+ if (status & GNUTLS_CERT_INVALID) { -+ if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) -+ ssock->verify_status |= PJ_SSL_CERT_EISSUER_NOT_FOUND; -+ else if (status & GNUTLS_CERT_EXPIRED || -+ status & GNUTLS_CERT_NOT_ACTIVATED) -+ ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD; -+ else if (status & GNUTLS_CERT_SIGNER_NOT_CA || -+ status & GNUTLS_CERT_INSECURE_ALGORITHM) -+ ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED; -+ else if (status & GNUTLS_CERT_UNEXPECTED_OWNER || -+ status & GNUTLS_CERT_MISMATCH) -+ ssock->verify_status |= PJ_SSL_CERT_EISSUER_MISMATCH; -+ else if (status & GNUTLS_CERT_REVOKED) -+ ssock->verify_status |= PJ_SSL_CERT_EREVOKED; -+ else -+ ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN; -+ -+ return GNUTLS_E_CERTIFICATE_ERROR; -+ } -+ -+ /* When verification is not requested just return ok here, however -+ * applications can still get the verification status. */ -+ gnutls_x509_crt_t cert; -+ unsigned int cert_list_size; -+ const gnutls_datum_t *cert_list; -+ int ret; -+ -+ ret = gnutls_x509_crt_init(&cert); -+ if (ret < 0) -+ goto out; -+ -+ cert_list = gnutls_certificate_get_peers(session, &cert_list_size); -+ if (cert_list == NULL) { -+ ret = GNUTLS_E_NO_CERTIFICATE_FOUND; -+ goto out; -+ } -+ -+ /* TODO: verify whole chain perhaps? */ -+ ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER); -+ if (ret < 0) -+ ret = gnutls_x509_crt_import(cert, &cert_list[0], -+ GNUTLS_X509_FMT_PEM); -+ if (ret < 0) { -+ ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT; -+ goto out; -+ } -+ ret = gnutls_x509_crt_check_hostname(cert, ssock->param.server_name.ptr); -+ if (ret < 0) -+ goto out; -+ -+ gnutls_x509_crt_deinit(cert); -+ -+ /* notify GnuTLS to continue handshake normally */ -+ return GNUTLS_E_SUCCESS; -+ -+out: -+ tls_last_error = ret; -+ ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN; -+ return GNUTLS_E_CERTIFICATE_ERROR; -+ } -+ -+ return GNUTLS_E_SUCCESS; -+} -+ -+ -+/* gnutls_handshake() and gnutls_record_send() will call this function to -+ * send/write (encrypted) data */ -+static ssize_t tls_data_push(gnutls_transport_ptr_t ptr, -+ const void *data, size_t len) -+{ -+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)ptr; -+ -+ pj_lock_acquire(ssock->circ_buf_output_mutex); -+ if (circ_write(&ssock->circ_buf_output, data, len) != PJ_SUCCESS) { -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ -+ gnutls_transport_set_errno(ssock->session, ENOMEM); -+ return -1; -+ } -+ -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ -+ return len; -+} -+ -+ -+/* gnutls_handshake() and gnutls_record_recv() will call this function to -+ * receive/read (encrypted) data */ -+static ssize_t tls_data_pull(gnutls_transport_ptr_t ptr, -+ void *data, pj_size_t len) -+{ -+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)ptr; -+ -+ pj_lock_acquire(ssock->circ_buf_input_mutex); -+ -+ if (circ_empty(&ssock->circ_buf_input)) { -+ pj_lock_release(ssock->circ_buf_input_mutex); -+ -+ /* Data buffers not yet filled */ -+ gnutls_transport_set_errno(ssock->session, EAGAIN); -+ return -1; -+ } -+ -+ pj_size_t circ_buf_size = circ_size(&ssock->circ_buf_input); -+ pj_size_t read_size = PJ_MIN(circ_buf_size, len); -+ -+ circ_read(&ssock->circ_buf_input, data, read_size); -+ -+ pj_lock_release(ssock->circ_buf_input_mutex); -+ -+ return read_size; -+} -+ -+ -+/* Append a string to the priority string, only once. */ -+static pj_status_t tls_str_append_once(pj_str_t *dst, pj_str_t *src) -+{ -+ if (pj_strstr(dst, src) == NULL) { -+ /* Check buffer size */ -+ if (dst->slen + src->slen + 3 > 1024) -+ return PJ_ETOOMANY; -+ -+ pj_strcat2(dst, ":+"); -+ pj_strcat(dst, src); -+ } -+ return PJ_SUCCESS; -+} -+ -+ -+/* Generate priority string with user preference order. */ -+static pj_status_t tls_priorities_set(pj_ssl_sock_t *ssock) -+{ -+ char buf[1024]; -+ char priority_buf[256]; -+ pj_str_t cipher_list; -+ pj_str_t compression = pj_str("COMP-NULL"); -+ pj_str_t server = pj_str(":%SERVER_PRECEDENCE"); -+ int i, j, ret; -+ pj_str_t priority; -+ const char *err; -+ -+ pj_strset(&cipher_list, buf, 0); -+ pj_strset(&priority, priority_buf, 0); -+ -+ /* For each level, enable only the requested protocol */ -+ pj_strcat2(&priority, "NORMAL:"); -+ if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2) { -+ pj_strcat2(&priority, "+VERS-TLS1.2:"); -+ } -+ if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_1) { -+ pj_strcat2(&priority, "+VERS-TLS1.1:"); -+ } -+ if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1) { -+ pj_strcat2(&priority, "+VERS-TLS1.0:"); -+ } -+ pj_strcat2(&priority, "-VERS-SSL3.0:"); -+ pj_strcat2(&priority, "%LATEST_RECORD_VERSION"); -+ -+ pj_strcat(&cipher_list, &priority); -+ for (i = 0; i < ssock->param.ciphers_num; i++) { -+ for (j = 0; ; j++) { -+ pj_ssl_cipher c; -+ const char *suite; -+ unsigned char id[2]; -+ gnutls_protocol_t proto; -+ gnutls_kx_algorithm_t kx; -+ gnutls_mac_algorithm_t mac; -+ gnutls_cipher_algorithm_t algo; -+ -+ suite = gnutls_cipher_suite_info(j, (unsigned char *)id, -+ &kx, &algo, &mac, &proto); -+ if (!suite) -+ break; -+ -+ c = (pj_ssl_cipher) (pj_uint32_t) ((id[0] << 8) | id[1]); -+ if (ssock->param.ciphers[i] == c) { -+ char temp[256]; -+ pj_str_t cipher_entry; -+ -+ /* Protocol version */ -+ pj_strset(&cipher_entry, temp, 0); -+ pj_strcat2(&cipher_entry, "VERS-"); -+ pj_strcat2(&cipher_entry, gnutls_protocol_get_name(proto)); -+ ret = tls_str_append_once(&cipher_list, &cipher_entry); -+ if (ret != PJ_SUCCESS) -+ return ret; -+ -+ /* Cipher */ -+ pj_strset(&cipher_entry, temp, 0); -+ pj_strcat2(&cipher_entry, gnutls_cipher_get_name(algo)); -+ ret = tls_str_append_once(&cipher_list, &cipher_entry); -+ if (ret != PJ_SUCCESS) -+ return ret; -+ -+ /* Mac */ -+ pj_strset(&cipher_entry, temp, 0); -+ pj_strcat2(&cipher_entry, gnutls_mac_get_name(mac)); -+ ret = tls_str_append_once(&cipher_list, &cipher_entry); -+ if (ret != PJ_SUCCESS) -+ return ret; -+ -+ /* Key exchange */ -+ pj_strset(&cipher_entry, temp, 0); -+ pj_strcat2(&cipher_entry, gnutls_kx_get_name(kx)); -+ ret = tls_str_append_once(&cipher_list, &cipher_entry); -+ if (ret != PJ_SUCCESS) -+ return ret; -+ -+ /* Compression is always disabled */ -+ /* Signature is level-default */ -+ break; -+ } -+ } -+ } -+ -+ /* Disable compression, it's a TLS-only extension after all */ -+ tls_str_append_once(&cipher_list, &compression); -+ -+ /* Server will be the one deciding which crypto to use */ -+ if (ssock->is_server) { -+ if (cipher_list.slen + server.slen + 1 > sizeof(buf)) -+ return PJ_ETOOMANY; -+ else -+ pj_strcat(&cipher_list, &server); -+ } -+ -+ /* End the string and print it */ -+ cipher_list.ptr[cipher_list.slen] = '\0'; -+ PJ_LOG(5, (ssock->pool->obj_name, "Priority string: %s", cipher_list.ptr)); -+ -+ /* Set our priority string */ -+ ret = gnutls_priority_set_direct(ssock->session, -+ cipher_list.ptr, &err); -+ if (ret < 0) { -+ tls_last_error = GNUTLS_E_INVALID_REQUEST; -+ return PJ_EINVAL; -+ } -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* Load root CA file or load the installed ones. */ -+static pj_status_t tls_trust_set(pj_ssl_sock_t *ssock) -+{ -+ int ntrusts = 0; -+ int err; -+ -+ err = gnutls_certificate_set_x509_system_trust(ssock->xcred); -+ if (err > 0) -+ ntrusts += err; -+ err = gnutls_certificate_set_x509_trust_file(ssock->xcred, -+ TRUST_STORE_FILE1, -+ GNUTLS_X509_FMT_PEM); -+ if (err > 0) -+ ntrusts += err; -+ -+ err = gnutls_certificate_set_x509_trust_file(ssock->xcred, -+ TRUST_STORE_FILE2, -+ GNUTLS_X509_FMT_PEM); -+ if (err > 0) -+ ntrusts += err; -+ -+ if (ntrusts > 0) -+ return PJ_SUCCESS; -+ else if (!ntrusts) -+ return PJ_ENOTFOUND; -+ else -+ return PJ_EINVAL; -+} -+ -+#if GNUTLS_VERSION_NUMBER < 0x030306 -+ -+#ifdef _POSIX_PATH_MAX -+# define GNUTLS_PATH_MAX _POSIX_PATH_MAX -+#else -+# define GNUTLS_PATH_MAX 256 -+#endif -+ -+static -+int gnutls_certificate_set_x509_trust_dir(gnutls_certificate_credentials_t cred, const char *dirname, unsigned type) -+{ -+ DIR *dirp; -+ struct dirent *d; -+ int ret; -+ int r = 0; -+ char path[GNUTLS_PATH_MAX]; -+#ifndef _WIN32 -+ struct dirent e; -+#endif -+ -+ dirp = opendir(dirname); -+ if (dirp != NULL) { -+ do { -+#ifdef _WIN32 -+ d = readdir(dirp); -+ if (d != NULL) { -+#else -+ ret = readdir_r(dirp, &e, &d); -+ if (ret == 0 && d != NULL -+#ifdef _DIRENT_HAVE_D_TYPE -+ && (d->d_type == DT_REG || d->d_type == DT_LNK || d->d_type == DT_UNKNOWN) -+#endif -+ ) { -+#endif -+ snprintf(path, sizeof(path), "%s/%s", -+ dirname, d->d_name); -+ -+ ret = gnutls_certificate_set_x509_trust_file(cred, path, type); -+ if (ret >= 0) -+ r += ret; -+ } -+ } -+ while (d != NULL); -+ closedir(dirp); -+ } -+ -+ return r; -+} -+ -+#endif -+ -+/* Create and initialize new GnuTLS context and instance */ -+static pj_status_t tls_open(pj_ssl_sock_t *ssock) -+{ -+ pj_ssl_cert_t *cert; -+ pj_status_t status; -+ int ret; -+ -+ pj_assert(ssock); -+ -+ cert = ssock->cert; -+ -+ /* Even if reopening is harmless, having one instance only simplifies -+ * deallocating it later on */ -+ if (!ssock->tls_init_count) { -+ ssock->tls_init_count++; -+ ret = tls_init(); -+ if (ret < 0) -+ return ret; -+ } else -+ return PJ_SUCCESS; -+ -+ /* Start this socket session */ -+ ret = gnutls_init(&ssock->session, ssock->is_server ? GNUTLS_SERVER -+ : GNUTLS_CLIENT); -+ if (ret < 0) -+ goto out; -+ -+ /* Set the ssock object to be retrieved by transport (send/recv) and by -+ * user data from this session */ -+ gnutls_transport_set_ptr(ssock->session, -+ (gnutls_transport_ptr_t) (uintptr_t) ssock); -+ gnutls_session_set_ptr(ssock->session, -+ (gnutls_transport_ptr_t) (uintptr_t) ssock); -+ -+ /* Initialize input circular buffer */ -+ status = circ_init(ssock->pool->factory, &ssock->circ_buf_input, 512); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ /* Initialize output circular buffer */ -+ status = circ_init(ssock->pool->factory, &ssock->circ_buf_output, 512); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ /* Set the callback that allows GnuTLS to PUSH and PULL data -+ * TO and FROM the transport layer */ -+ gnutls_transport_set_push_function(ssock->session, tls_data_push); -+ gnutls_transport_set_pull_function(ssock->session, tls_data_pull); -+ -+ /* Determine which cipher suite to support */ -+ status = tls_priorities_set(ssock); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ /* Allocate credentials for handshaking and transmission */ -+ ret = gnutls_certificate_allocate_credentials(&ssock->xcred); -+ if (ret < 0) -+ goto out; -+ gnutls_certificate_set_verify_function(ssock->xcred, tls_cert_verify_cb); -+ -+ /* Load system trust file(s) */ -+ status = tls_trust_set(ssock); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ /* Load user-provided CA, certificate and key if available */ -+ if (cert) { -+ /* Load CA if one is specified. */ -+ if (cert->CA_file.slen) { -+ ret = gnutls_certificate_set_x509_trust_file(ssock->xcred, -+ cert->CA_file.ptr, -+ GNUTLS_X509_FMT_PEM); -+ if (ret < 0) -+ ret = gnutls_certificate_set_x509_trust_file(ssock->xcred, -+ cert->CA_file.ptr, -+ GNUTLS_X509_FMT_DER); -+ if (ret < 0) -+ goto out; -+ } -+ if (cert->CA_path.slen) { -+ ret = gnutls_certificate_set_x509_trust_dir(ssock->xcred, -+ cert->CA_path.ptr, -+ GNUTLS_X509_FMT_PEM); -+ if (ret < 0) -+ ret = gnutls_certificate_set_x509_trust_dir(ssock->xcred, -+ cert->CA_path.ptr, -+ GNUTLS_X509_FMT_DER); -+ if (ret < 0) -+ goto out; -+ } -+ -+ /* Load certificate, key and pass if one is specified */ -+ if (cert->cert_file.slen && cert->privkey_file.slen) { -+ const char *prikey_file = cert->privkey_file.ptr; -+ const char *prikey_pass = cert->privkey_pass.slen -+ ? cert->privkey_pass.ptr -+ : NULL; -+ ret = gnutls_certificate_set_x509_key_file2(ssock->xcred, -+ cert->cert_file.ptr, -+ prikey_file, -+ GNUTLS_X509_FMT_PEM, -+ prikey_pass, -+ 0); -+ if (ret != GNUTLS_E_SUCCESS) -+ ret = gnutls_certificate_set_x509_key_file2(ssock->xcred, -+ cert->cert_file.ptr, -+ prikey_file, -+ GNUTLS_X509_FMT_DER, -+ prikey_pass, -+ 0); -+ if (ret < 0) -+ goto out; -+ } -+ } -+ -+ /* Require client certificate if asked */ -+ if (ssock->is_server && ssock->param.require_client_cert) -+ gnutls_certificate_server_set_request(ssock->session, -+ GNUTLS_CERT_REQUIRE); -+ -+ /* Finally set credentials for this session */ -+ ret = gnutls_credentials_set(ssock->session, -+ GNUTLS_CRD_CERTIFICATE, ssock->xcred); -+ if (ret < 0) -+ goto out; -+ -+ ret = GNUTLS_E_SUCCESS; -+out: -+ return tls_status_from_err(ssock, ret); -+} -+ -+ -+/* Destroy GnuTLS credentials and session. */ -+static void tls_close(pj_ssl_sock_t *ssock) -+{ -+ if (ssock->session) { -+ gnutls_bye(ssock->session, GNUTLS_SHUT_RDWR); -+ gnutls_deinit(ssock->session); -+ ssock->session = NULL; -+ } -+ -+ if (ssock->xcred) { -+ gnutls_certificate_free_credentials(ssock->xcred); -+ ssock->xcred = NULL; -+ } -+ -+ /* Free GnuTLS library */ -+ if (ssock->tls_init_count) { -+ ssock->tls_init_count--; -+ tls_deinit(); -+ } -+ -+ /* Destroy circular buffers */ -+ circ_deinit(&ssock->circ_buf_input); -+ circ_deinit(&ssock->circ_buf_output); -+} -+ -+ -+/* Reset socket state. */ -+static void tls_sock_reset(pj_ssl_sock_t *ssock) -+{ -+ ssock->connection_state = TLS_STATE_NULL; -+ -+ tls_close(ssock); -+ -+ if (ssock->asock) { -+ pj_activesock_close(ssock->asock); -+ ssock->asock = NULL; -+ ssock->sock = PJ_INVALID_SOCKET; -+ } -+ if (ssock->sock != PJ_INVALID_SOCKET) { -+ pj_sock_close(ssock->sock); -+ ssock->sock = PJ_INVALID_SOCKET; -+ } -+ -+ ssock->last_err = tls_last_error = GNUTLS_E_SUCCESS; -+} -+ -+ -+/* Get Common Name field string from a general name string */ -+static void tls_cert_get_cn(const pj_str_t *gen_name, pj_str_t *cn) -+{ -+ pj_str_t CN_sign = {"CN=", 3}; -+ char *p, *q; -+ -+ pj_bzero(cn, sizeof(cn)); -+ -+ p = pj_strstr(gen_name, &CN_sign); -+ if (!p) -+ return; -+ -+ p += 3; /* shift pointer to value part */ -+ pj_strset(cn, p, gen_name->slen - (p - gen_name->ptr)); -+ q = pj_strchr(cn, ','); -+ if (q) -+ cn->slen = q - p; -+} -+ -+ -+/* Get certificate info; in case the certificate info is already populated, -+ * this function will check if the contents need updating by inspecting the -+ * issuer and the serial number. */ -+static void tls_cert_get_info(pj_pool_t *pool, pj_ssl_cert_info *ci, gnutls_x509_crt_t cert) -+{ -+ pj_bool_t update_needed; -+ char buf[512] = { 0 }; -+ size_t bufsize = sizeof(buf); -+ pj_uint8_t serial_no[64] = { 0 }; /* should be >= sizeof(ci->serial_no) */ -+ size_t serialsize = sizeof(serial_no); -+ size_t len = sizeof(buf); -+ int i, ret, seq = 0; -+ pj_ssl_cert_name_type type; -+ -+ pj_assert(pool && ci && cert); -+ -+ /* Get issuer */ -+ gnutls_x509_crt_get_issuer_dn(cert, buf, &bufsize); -+ -+ /* Get serial no */ -+ gnutls_x509_crt_get_serial(cert, serial_no, &serialsize); -+ -+ /* Check if the contents need to be updated */ -+ update_needed = pj_strcmp2(&ci->issuer.info, buf) || -+ pj_memcmp(ci->serial_no, serial_no, serialsize); -+ if (!update_needed) -+ return; -+ -+ /* Update cert info */ -+ -+ pj_bzero(ci, sizeof(pj_ssl_cert_info)); -+ -+ /* Version */ -+ ci->version = gnutls_x509_crt_get_version(cert); -+ -+ /* Issuer */ -+ pj_strdup2(pool, &ci->issuer.info, buf); -+ tls_cert_get_cn(&ci->issuer.info, &ci->issuer.cn); -+ -+ /* Serial number */ -+ pj_memcpy(ci->serial_no, serial_no, sizeof(ci->serial_no)); -+ -+ /* Subject */ -+ bufsize = sizeof(buf); -+ gnutls_x509_crt_get_dn(cert, buf, &bufsize); -+ pj_strdup2(pool, &ci->subject.info, buf); -+ tls_cert_get_cn(&ci->subject.info, &ci->subject.cn); -+ -+ /* Validity */ -+ ci->validity.end.sec = gnutls_x509_crt_get_expiration_time(cert); -+ ci->validity.start.sec = gnutls_x509_crt_get_activation_time(cert); -+ ci->validity.gmt = 0; -+ -+ /* Subject Alternative Name extension */ -+ if (ci->version >= 3) { -+ char out[256] = { 0 }; -+ /* Get the number of all alternate names so that we can allocate -+ * the correct number of bytes in subj_alt_name */ -+ while (gnutls_x509_crt_get_subject_alt_name(cert, seq, out, &len, -+ NULL) != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) -+ seq++; -+ -+ ci->subj_alt_name.entry = pj_pool_calloc(pool, seq, -+ sizeof(*ci->subj_alt_name.entry)); -+ if (!ci->subj_alt_name.entry) { -+ tls_last_error = GNUTLS_E_MEMORY_ERROR; -+ return; -+ } -+ -+ /* Now populate the alternative names */ -+ for (i = 0; i < seq; i++) { -+ len = sizeof(out) - 1; -+ ret = gnutls_x509_crt_get_subject_alt_name(cert, i, out, &len, NULL); -+ switch (ret) { -+ case GNUTLS_SAN_IPADDRESS: -+ type = PJ_SSL_CERT_NAME_IP; -+ pj_inet_ntop2(len == sizeof(pj_in6_addr) ? pj_AF_INET6() -+ : pj_AF_INET(), -+ out, buf, sizeof(buf)); -+ break; -+ case GNUTLS_SAN_URI: -+ type = PJ_SSL_CERT_NAME_URI; -+ break; -+ case GNUTLS_SAN_RFC822NAME: -+ type = PJ_SSL_CERT_NAME_RFC822; -+ break; -+ case GNUTLS_SAN_DNSNAME: -+ type = PJ_SSL_CERT_NAME_DNS; -+ break; -+ default: -+ type = PJ_SSL_CERT_NAME_UNKNOWN; -+ break; -+ } -+ -+ if (len && type != PJ_SSL_CERT_NAME_UNKNOWN) { -+ ci->subj_alt_name.entry[ci->subj_alt_name.cnt].type = type; -+ pj_strdup2(pool, -+ &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name, -+ type == PJ_SSL_CERT_NAME_IP ? buf : out); -+ ci->subj_alt_name.cnt++; -+ } -+ } -+ /* TODO: if no DNS alt. names were found, we could check against -+ * the commonName as per RFC3280. */ -+ } -+} -+ -+static void tls_cert_get_chain_raw(pj_pool_t *pool, pj_ssl_cert_info *ci, const gnutls_datum_t *certs, size_t certs_num) -+{ -+ size_t i=0; -+ ci->raw_chain.cert_raw = pj_pool_calloc(pool, certs_num, sizeof(*ci->raw_chain.cert_raw)); -+ ci->raw_chain.cnt = certs_num; -+ for (i=0; i < certs_num; ++i) { -+ const pj_str_t crt_raw = {(const char*)certs[i].data, (pj_ssize_t)certs[i].size}; -+ pj_strdup(pool, ci->raw_chain.cert_raw+i, &crt_raw); -+ } -+} -+ -+/* Update local & remote certificates info. This function should be -+ * called after handshake or renegotiation successfully completed. */ -+static void tls_cert_update(pj_ssl_sock_t *ssock) -+{ -+ gnutls_x509_crt_t cert = NULL; -+ const gnutls_datum_t *us; -+ const gnutls_datum_t *certs; -+ unsigned int certslen = 0; -+ int ret = GNUTLS_CERT_INVALID; -+ -+ pj_assert(ssock->connection_state == TLS_STATE_ESTABLISHED); -+ -+ /* Get active local certificate */ -+ us = gnutls_certificate_get_ours(ssock->session); -+ if (!us) -+ goto us_out; -+ -+ ret = gnutls_x509_crt_init(&cert); -+ if (ret < 0) -+ goto us_out; -+ ret = gnutls_x509_crt_import(cert, us, GNUTLS_X509_FMT_DER); -+ if (ret < 0) -+ ret = gnutls_x509_crt_import(cert, us, GNUTLS_X509_FMT_PEM); -+ if (ret < 0) -+ goto us_out; -+ -+ tls_cert_get_info(ssock->pool, &ssock->local_cert_info, cert); -+ tls_cert_get_chain_raw(ssock->pool, &ssock->local_cert_info, us, 1); -+ -+us_out: -+ tls_last_error = ret; -+ if (cert) -+ gnutls_x509_crt_deinit(cert); -+ else -+ pj_bzero(&ssock->local_cert_info, sizeof(pj_ssl_cert_info)); -+ -+ cert = NULL; -+ -+ /* Get active remote certificate */ -+ certs = gnutls_certificate_get_peers(ssock->session, &certslen); -+ if (certs == NULL || certslen == 0) -+ goto peer_out; -+ -+ ret = gnutls_x509_crt_init(&cert); -+ if (ret < 0) -+ goto peer_out; -+ -+ ret = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_PEM); -+ if (ret < 0) -+ ret = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_DER); -+ if (ret < 0) -+ goto peer_out; -+ -+ tls_cert_get_info(ssock->pool, &ssock->remote_cert_info, cert); -+ tls_cert_get_chain_raw(ssock->pool, &ssock->remote_cert_info, certs, certslen); -+ -+peer_out: -+ tls_last_error = ret; -+ if (cert) -+ gnutls_x509_crt_deinit(cert); -+ else -+ pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info)); -+} -+ -+ -+/* When handshake completed: -+ * - notify application -+ * - if handshake failed, reset SSL state -+ * - return PJ_FALSE when SSL socket instance is destroyed by application. */ -+static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock, -+ pj_status_t status) -+{ -+ pj_bool_t ret = PJ_TRUE; -+ -+ /* Cancel handshake timer */ -+ if (ssock->timer.id == TIMER_HANDSHAKE_TIMEOUT) { -+ pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer); -+ ssock->timer.id = TIMER_NONE; -+ } -+ -+ /* Update certificates info on successful handshake */ -+ if (status == PJ_SUCCESS) -+ tls_cert_update(ssock); -+ -+ /* Accepting */ -+ if (ssock->is_server) { -+ if (status != PJ_SUCCESS) { -+ /* Handshake failed in accepting, destroy our self silently. */ -+ -+ char errmsg[PJ_ERR_MSG_SIZE]; -+ char buf[PJ_INET6_ADDRSTRLEN + 10]; -+ -+ pj_strerror(status, errmsg, sizeof(errmsg)); -+ PJ_LOG(3, (ssock->pool->obj_name, -+ "Handshake failed in accepting %s: %s", -+ pj_sockaddr_print(&ssock->rem_addr, buf, sizeof(buf), 3), -+ errmsg)); -+ -+ /* Workaround for ticket #985 */ -+#if (defined(PJ_WIN32) && PJ_WIN32 != 0) || (defined(PJ_WIN64) && PJ_WIN64 != 0) -+ if (ssock->param.timer_heap) { -+ pj_time_val interval = {0, DELAYED_CLOSE_TIMEOUT}; -+ -+ tls_sock_reset(ssock); -+ -+ ssock->timer.id = TIMER_CLOSE; -+ pj_time_val_normalize(&interval); -+ if (pj_timer_heap_schedule(ssock->param.timer_heap, -+ &ssock->timer, &interval) != 0) -+ { -+ ssock->timer.id = TIMER_NONE; -+ pj_ssl_sock_close(ssock); -+ } -+ } else -+#endif /* PJ_WIN32 */ -+ { -+ pj_ssl_sock_close(ssock); -+ } -+ -+ return PJ_FALSE; -+ } -+ /* Notify application the newly accepted SSL socket */ -+ if (ssock->param.cb.on_accept_complete) -+ ret = (*ssock->param.cb.on_accept_complete) -+ (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr, -+ pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr)); -+ -+ } else { /* Connecting */ -+ /* On failure, reset SSL socket state first, as app may try to -+ * reconnect in the callback. */ -+ if (status != PJ_SUCCESS) { -+ /* Server disconnected us, possibly due to negotiation failure */ -+ tls_sock_reset(ssock); -+ } -+ if (ssock->param.cb.on_connect_complete) { -+ -+ ret = (*ssock->param.cb.on_connect_complete)(ssock, status); -+ } -+ } -+ -+ return ret; -+} -+ -+static write_data_t *alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len) -+{ -+ send_buf_t *send_buf = &ssock->send_buf; -+ pj_size_t avail_len, skipped_len = 0; -+ char *reg1, *reg2; -+ pj_size_t reg1_len, reg2_len; -+ write_data_t *p; -+ -+ /* Check buffer availability */ -+ avail_len = send_buf->max_len - send_buf->len; -+ if (avail_len < len) -+ return NULL; -+ -+ /* If buffer empty, reset start pointer and return it */ -+ if (send_buf->len == 0) { -+ send_buf->start = send_buf->buf; -+ send_buf->len = len; -+ p = (write_data_t*)send_buf->start; -+ goto init_send_data; -+ } -+ -+ /* Free space may be wrapped/splitted into two regions, so let's -+ * analyze them if any region can hold the write data. */ -+ reg1 = send_buf->start + send_buf->len; -+ if (reg1 >= send_buf->buf + send_buf->max_len) -+ reg1 -= send_buf->max_len; -+ reg1_len = send_buf->max_len - send_buf->len; -+ if (reg1 + reg1_len > send_buf->buf + send_buf->max_len) { -+ reg1_len = send_buf->buf + send_buf->max_len - reg1; -+ reg2 = send_buf->buf; -+ reg2_len = send_buf->start - send_buf->buf; -+ } else { -+ reg2 = NULL; -+ reg2_len = 0; -+ } -+ -+ /* More buffer availability check, note that the write data must be in -+ * a contigue buffer. */ -+ avail_len = PJ_MAX(reg1_len, reg2_len); -+ if (avail_len < len) -+ return NULL; -+ -+ /* Get the data slot */ -+ if (reg1_len >= len) { -+ p = (write_data_t*)reg1; -+ } else { -+ p = (write_data_t*)reg2; -+ skipped_len = reg1_len; -+ } -+ -+ /* Update buffer length */ -+ send_buf->len += len + skipped_len; -+ -+init_send_data: -+ /* Init the new send data */ -+ pj_bzero(p, sizeof(*p)); -+ pj_list_init(p); -+ pj_list_push_back(&ssock->send_pending, p); -+ -+ return p; -+} -+ -+static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata) -+{ -+ send_buf_t *buf = &ssock->send_buf; -+ write_data_t *spl = &ssock->send_pending; -+ -+ pj_assert(!pj_list_empty(&ssock->send_pending)); -+ -+ /* Free slot from the buffer */ -+ if (spl->next == wdata && spl->prev == wdata) { -+ /* This is the only data, reset the buffer */ -+ buf->start = buf->buf; -+ buf->len = 0; -+ } else if (spl->next == wdata) { -+ /* This is the first data, shift start pointer of the buffer and -+ * adjust the buffer length. -+ */ -+ buf->start = (char*)wdata->next; -+ if (wdata->next > wdata) { -+ buf->len -= ((char*)wdata->next - buf->start); -+ } else { -+ /* Overlapped */ -+ pj_size_t right_len, left_len; -+ right_len = buf->buf + buf->max_len - (char*)wdata; -+ left_len = (char*)wdata->next - buf->buf; -+ buf->len -= (right_len + left_len); -+ } -+ } else if (spl->prev == wdata) { -+ /* This is the last data, just adjust the buffer length */ -+ if (wdata->prev < wdata) { -+ pj_size_t jump_len; -+ jump_len = (char*)wdata - -+ ((char*)wdata->prev + wdata->prev->record_len); -+ buf->len -= (wdata->record_len + jump_len); -+ } else { -+ /* Overlapped */ -+ pj_size_t right_len, left_len; -+ right_len = buf->buf + buf->max_len - -+ ((char*)wdata->prev + wdata->prev->record_len); -+ left_len = (char*)wdata + wdata->record_len - buf->buf; -+ buf->len -= (right_len + left_len); -+ } -+ } -+ /* For data in the middle buffer, just do nothing on the buffer. The slot -+ * will be freed later when freeing the first/last data. */ -+ -+ /* Remove the data from send pending list */ -+ pj_list_erase(wdata); -+} -+ -+#if 0 -+/* Just for testing send buffer alloc/free */ -+#include <pj/rand.h> -+pj_status_t pj_ssl_sock_ossl_test_send_buf(pj_pool_t *pool) -+{ -+ enum { MAX_CHUNK_NUM = 20 }; -+ unsigned chunk_size, chunk_cnt, i; -+ write_data_t *wdata[MAX_CHUNK_NUM] = {0}; -+ pj_time_val now; -+ pj_ssl_sock_t *ssock = NULL; -+ pj_ssl_sock_param param; -+ pj_status_t status; -+ -+ pj_gettimeofday(&now); -+ pj_srand((unsigned)now.sec); -+ -+ pj_ssl_sock_param_default(¶m); -+ status = pj_ssl_sock_create(pool, ¶m, &ssock); -+ if (status != PJ_SUCCESS) { -+ return status; -+ } -+ -+ if (ssock->send_buf.max_len == 0) { -+ ssock->send_buf.buf = (char *) -+ pj_pool_alloc(ssock->pool, -+ ssock->param.send_buffer_size); -+ ssock->send_buf.max_len = ssock->param.send_buffer_size; -+ ssock->send_buf.start = ssock->send_buf.buf; -+ ssock->send_buf.len = 0; -+ } -+ -+ chunk_size = ssock->param.send_buffer_size / MAX_CHUNK_NUM / 2; -+ chunk_cnt = 0; -+ for (i = 0; i < MAX_CHUNK_NUM; i++) { -+ wdata[i] = alloc_send_data(ssock, pj_rand() % chunk_size + 321); -+ if (wdata[i]) -+ chunk_cnt++; -+ else -+ break; -+ } -+ -+ while (chunk_cnt) { -+ i = pj_rand() % MAX_CHUNK_NUM; -+ if (wdata[i]) { -+ free_send_data(ssock, wdata[i]); -+ wdata[i] = NULL; -+ chunk_cnt--; -+ } -+ } -+ -+ if (ssock->send_buf.len != 0) -+ status = PJ_EBUG; -+ -+ pj_ssl_sock_close(ssock); -+ return status; -+} -+#endif -+ -+/* Flush write circular buffer to network socket. */ -+static pj_status_t flush_circ_buf_output(pj_ssl_sock_t *ssock, -+ pj_ioqueue_op_key_t *send_key, -+ pj_size_t orig_len, unsigned flags) -+{ -+ pj_ssize_t len; -+ write_data_t *wdata; -+ pj_size_t needed_len; -+ pj_status_t status; -+ -+ pj_lock_acquire(ssock->circ_buf_output_mutex); -+ -+ /* Check if there is data in the circular buffer, flush it if any */ -+ if (circ_empty(&ssock->circ_buf_output)) { -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ -+ return PJ_SUCCESS; -+ } -+ -+ len = circ_size(&ssock->circ_buf_output); -+ -+ /* Calculate buffer size needed, and align it to 8 */ -+ needed_len = len + sizeof(write_data_t); -+ needed_len = ((needed_len + 7) >> 3) << 3; -+ -+ /* Allocate buffer for send data */ -+ wdata = alloc_send_data(ssock, needed_len); -+ if (wdata == NULL) { -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ return PJ_ENOMEM; -+ } -+ -+ /* Copy the data and set its properties into the send data */ -+ pj_ioqueue_op_key_init(&wdata->key, sizeof(pj_ioqueue_op_key_t)); -+ wdata->key.user_data = wdata; -+ wdata->app_key = send_key; -+ wdata->record_len = needed_len; -+ wdata->data_len = len; -+ wdata->plain_data_len = orig_len; -+ wdata->flags = flags; -+ circ_read(&ssock->circ_buf_output, (pj_uint8_t *)&wdata->data, len); -+ -+ /* Ticket #1573: Don't hold mutex while calling PJLIB socket send(). */ -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ -+ /* Send it */ -+ if (ssock->param.sock_type == pj_SOCK_STREAM()) { -+ status = pj_activesock_send(ssock->asock, &wdata->key, -+ wdata->data.content, &len, -+ flags); -+ } else { -+ status = pj_activesock_sendto(ssock->asock, &wdata->key, -+ wdata->data.content, &len, -+ flags, -+ (pj_sockaddr_t*)&ssock->rem_addr, -+ ssock->addr_len); -+ } -+ -+ if (status != PJ_EPENDING) { -+ /* When the sending is not pending, remove the wdata from send -+ * pending list. */ -+ pj_lock_acquire(ssock->circ_buf_output_mutex); -+ free_send_data(ssock, wdata); -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ } -+ -+ return status; -+} -+ -+static void on_timer(pj_timer_heap_t *th, struct pj_timer_entry *te) -+{ -+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)te->user_data; -+ int timer_id = te->id; -+ -+ te->id = TIMER_NONE; -+ -+ PJ_UNUSED_ARG(th); -+ -+ switch (timer_id) { -+ case TIMER_HANDSHAKE_TIMEOUT: -+ PJ_LOG(1, (ssock->pool->obj_name, "TLS timeout after %d.%ds", -+ ssock->param.timeout.sec, ssock->param.timeout.msec)); -+ -+ on_handshake_complete(ssock, PJ_ETIMEDOUT); -+ break; -+ case TIMER_CLOSE: -+ pj_ssl_sock_close(ssock); -+ break; -+ default: -+ pj_assert(!"Unknown timer"); -+ break; -+ } -+} -+ -+ -+/* Try to perform an asynchronous handshake */ -+static pj_status_t tls_try_handshake(pj_ssl_sock_t *ssock) -+{ -+ int ret; -+ pj_status_t status; -+ -+ /* Perform SSL handshake */ -+ ret = gnutls_handshake(ssock->session); -+ -+ status = flush_circ_buf_output(ssock, &ssock->handshake_op_key, 0, 0); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ if (ret == GNUTLS_E_SUCCESS) { -+ /* System are GO */ -+ ssock->connection_state = TLS_STATE_ESTABLISHED; -+ status = PJ_SUCCESS; -+ } else if (!gnutls_error_is_fatal(ret)) { -+ /* Non fatal error, retry later (busy or again) */ -+ status = PJ_EPENDING; -+ } else { -+ /* Fatal error invalidates session, no fallback */ -+ status = PJ_EINVAL; -+ } -+ -+ tls_last_error = ret; -+ -+ return status; -+} -+ -+ -+/* -+ ******************************************************************* -+ * Active socket callbacks. -+ ******************************************************************* -+ */ -+ -+/* PJ_TRUE asks the socket to read more data, PJ_FALSE takes it off the queue */ -+static pj_bool_t asock_on_data_read(pj_activesock_t *asock, void *data, -+ pj_size_t size, pj_status_t status, -+ pj_size_t *remainder) -+{ -+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t *) -+ pj_activesock_get_user_data(asock); -+ -+ pj_size_t app_remainder = 0; -+ -+ if (data && size > 0) { -+ /* Push data into input circular buffer (for GnuTLS) */ -+ pj_lock_acquire(ssock->circ_buf_input_mutex); -+ circ_write(&ssock->circ_buf_input, data, size); -+ pj_lock_release(ssock->circ_buf_input_mutex); -+ } -+ -+ /* Check if SSL handshake hasn't finished yet */ -+ if (ssock->connection_state == TLS_STATE_HANDSHAKING) { -+ pj_bool_t ret = PJ_TRUE; -+ -+ if (status == PJ_SUCCESS) -+ status = tls_try_handshake(ssock); -+ -+ /* Not pending is either success or failed */ -+ if (status != PJ_EPENDING) -+ ret = on_handshake_complete(ssock, status); -+ -+ return ret; -+ } -+ -+ /* See if there is any decrypted data for the application */ -+ if (ssock->read_started) { -+ do { -+ /* Get read data structure at the end of the data */ -+ read_data_t *app_read_data = *(OFFSET_OF_READ_DATA_PTR(ssock, data)); -+ int app_data_size = (int)(ssock->read_size - app_read_data->len); -+ -+ /* Decrypt received data using GnuTLS (will read our input -+ * circular buffer) */ -+ int decrypted_size = gnutls_record_recv(ssock->session, -+ ((read_data_t *)app_read_data->data) + -+ app_read_data->len, -+ app_data_size); -+ -+ if (decrypted_size > 0 || status != PJ_SUCCESS) { -+ if (ssock->param.cb.on_data_read) { -+ pj_bool_t ret; -+ app_remainder = 0; -+ -+ if (decrypted_size > 0) -+ app_read_data->len += decrypted_size; -+ -+ ret = (*ssock->param.cb.on_data_read)(ssock, -+ app_read_data->data, -+ app_read_data->len, -+ status, -+ &app_remainder); -+ -+ if (!ret) { -+ /* We've been destroyed */ -+ return PJ_FALSE; -+ } -+ -+ /* Application may have left some data to be consumed -+ * later as remainder */ -+ app_read_data->len = app_remainder; -+ } -+ -+ /* Active socket signalled connection closed/error, this has -+ * been signalled to the application along with any remaining -+ * buffer. So, let's just reset SSL socket now. */ -+ if (status != PJ_SUCCESS) { -+ tls_sock_reset(ssock); -+ return PJ_FALSE; -+ } -+ } else if (decrypted_size == 0) { -+ /* Nothing more to read */ -+ -+ return PJ_TRUE; -+ } else if (decrypted_size == GNUTLS_E_AGAIN || -+ decrypted_size == GNUTLS_E_INTERRUPTED) { -+ return PJ_TRUE; -+ } else if (decrypted_size == GNUTLS_E_REHANDSHAKE) { -+ /* Seems like we are renegotiating */ -+ pj_status_t try_handshake_status = tls_try_handshake(ssock); -+ -+ /* Not pending is either success or failed */ -+ if (try_handshake_status != PJ_EPENDING) { -+ if (!on_handshake_complete(ssock, try_handshake_status)) { -+ return PJ_FALSE; -+ } -+ } -+ -+ if (try_handshake_status != PJ_SUCCESS && -+ try_handshake_status != PJ_EPENDING) { -+ return PJ_FALSE; -+ } -+ } else if (!gnutls_error_is_fatal(decrypted_size)) { -+ /* non-fatal error, let's just continue */ -+ } else { -+ return PJ_FALSE; -+ } -+ } while (PJ_TRUE); -+ } -+ -+ return PJ_TRUE; -+} -+ -+ -+/* Callback every time new data is available from the active socket */ -+static pj_bool_t asock_on_data_sent(pj_activesock_t *asock, -+ pj_ioqueue_op_key_t *send_key, -+ pj_ssize_t sent) -+{ -+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)pj_activesock_get_user_data(asock); -+ -+ PJ_UNUSED_ARG(send_key); -+ PJ_UNUSED_ARG(sent); -+ -+ if (ssock->connection_state == TLS_STATE_HANDSHAKING) { -+ /* Initial handshaking */ -+ pj_status_t status = tls_try_handshake(ssock); -+ -+ /* Not pending is either success or failed */ -+ if (status != PJ_EPENDING) -+ return on_handshake_complete(ssock, status); -+ -+ } else if (send_key != &ssock->handshake_op_key) { -+ /* Some data has been sent, notify application */ -+ write_data_t *wdata = (write_data_t*)send_key->user_data; -+ if (ssock->param.cb.on_data_sent) { -+ pj_bool_t ret; -+ pj_ssize_t sent_len; -+ -+ sent_len = sent > 0 ? wdata->plain_data_len : sent; -+ -+ ret = (*ssock->param.cb.on_data_sent)(ssock, wdata->app_key, -+ sent_len); -+ if (!ret) { -+ /* We've been destroyed */ -+ return PJ_FALSE; -+ } -+ } -+ -+ /* Update write buffer state */ -+ pj_lock_acquire(ssock->circ_buf_output_mutex); -+ free_send_data(ssock, wdata); -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ } else { -+ /* SSL re-negotiation is on-progress, just do nothing */ -+ /* FIXME: check if this is valid for GnuTLS too */ -+ } -+ -+ return PJ_TRUE; -+} -+ -+ -+/* Callback every time a new connection has been accepted (server) */ -+static pj_bool_t asock_on_accept_complete(pj_activesock_t *asock, -+ pj_sock_t newsock, -+ const pj_sockaddr_t *src_addr, -+ int src_addr_len) -+{ -+ pj_ssl_sock_t *ssock_parent = (pj_ssl_sock_t *) -+ pj_activesock_get_user_data(asock); -+ -+ pj_ssl_sock_t *ssock; -+ pj_activesock_cb asock_cb; -+ pj_activesock_cfg asock_cfg; -+ unsigned int i; -+ pj_status_t status; -+ -+ PJ_UNUSED_ARG(src_addr_len); -+ -+ /* Create new SSL socket instance */ -+ status = pj_ssl_sock_create(ssock_parent->pool, &ssock_parent->newsock_param, -+ &ssock); -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Update new SSL socket attributes */ -+ ssock->sock = newsock; -+ ssock->parent = ssock_parent; -+ ssock->is_server = PJ_TRUE; -+ if (ssock_parent->cert) { -+ status = pj_ssl_sock_set_certificate(ssock, ssock->pool, -+ ssock_parent->cert); -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ } -+ -+ /* Apply QoS, if specified */ -+ status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type, -+ &ssock->param.qos_params, 1, -+ ssock->pool->obj_name, NULL); -+ if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error) -+ goto on_return; -+ -+ /* Update local address */ -+ ssock->addr_len = src_addr_len; -+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, -+ &ssock->addr_len); -+ if (status != PJ_SUCCESS) { -+ /* This fails on few envs, e.g: win IOCP, just tolerate this and -+ * use parent local address instead. -+ */ -+ pj_sockaddr_cp(&ssock->local_addr, &ssock_parent->local_addr); -+ } -+ -+ /* Set remote address */ -+ pj_sockaddr_cp(&ssock->rem_addr, src_addr); -+ -+ /* Create SSL context */ -+ status = tls_open(ssock); -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Prepare read buffer */ -+ ssock->asock_rbuf = (void **)pj_pool_calloc(ssock->pool, -+ ssock->param.async_cnt, -+ sizeof(void*)); -+ if (!ssock->asock_rbuf) -+ return PJ_ENOMEM; -+ -+ for (i = 0; i < ssock->param.async_cnt; ++i) { -+ ssock->asock_rbuf[i] = (void *)pj_pool_alloc( -+ ssock->pool, -+ ssock->param.read_buffer_size + -+ sizeof(read_data_t*)); -+ if (!ssock->asock_rbuf[i]) -+ return PJ_ENOMEM; -+ } -+ -+ /* Create active socket */ -+ pj_activesock_cfg_default(&asock_cfg); -+ asock_cfg.async_cnt = ssock->param.async_cnt; -+ asock_cfg.concurrency = ssock->param.concurrency; -+ asock_cfg.whole_data = PJ_TRUE; -+ -+ pj_bzero(&asock_cb, sizeof(asock_cb)); -+ asock_cb.on_data_read = asock_on_data_read; -+ asock_cb.on_data_sent = asock_on_data_sent; -+ -+ status = pj_activesock_create(ssock->pool, -+ ssock->sock, -+ ssock->param.sock_type, -+ &asock_cfg, -+ ssock->param.ioqueue, -+ &asock_cb, -+ ssock, -+ &ssock->asock); -+ -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Start reading */ -+ status = pj_activesock_start_read2(ssock->asock, ssock->pool, -+ (unsigned)ssock->param.read_buffer_size, -+ ssock->asock_rbuf, -+ PJ_IOQUEUE_ALWAYS_ASYNC); -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Prepare write/send state */ -+ pj_assert(ssock->send_buf.max_len == 0); -+ ssock->send_buf.buf = (char *)pj_pool_alloc(ssock->pool, -+ ssock->param.send_buffer_size); -+ if (!ssock->send_buf.buf) -+ return PJ_ENOMEM; -+ -+ ssock->send_buf.max_len = ssock->param.send_buffer_size; -+ ssock->send_buf.start = ssock->send_buf.buf; -+ ssock->send_buf.len = 0; -+ -+ /* Start handshake timer */ -+ if (ssock->param.timer_heap && -+ (ssock->param.timeout.sec != 0 || ssock->param.timeout.msec != 0)) { -+ pj_assert(ssock->timer.id == TIMER_NONE); -+ ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT; -+ status = pj_timer_heap_schedule(ssock->param.timer_heap, -+ &ssock->timer, -+ &ssock->param.timeout); -+ if (status != PJ_SUCCESS) -+ ssock->timer.id = TIMER_NONE; -+ } -+ -+ /* Start SSL handshake */ -+ ssock->connection_state = TLS_STATE_HANDSHAKING; -+ -+ status = tls_try_handshake(ssock); -+ -+on_return: -+ if (ssock && status != PJ_EPENDING) -+ on_handshake_complete(ssock, status); -+ -+ /* Must return PJ_TRUE whatever happened, as active socket must -+ * continue listening. -+ */ -+ return PJ_TRUE; -+} -+ -+ -+/* Callback every time a new connection has been completed (client) */ -+static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock, -+ pj_status_t status) -+{ -+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t*) -+ pj_activesock_get_user_data(asock); -+ -+ unsigned int i; -+ int ret; -+ -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Update local address */ -+ ssock->addr_len = sizeof(pj_sockaddr); -+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, -+ &ssock->addr_len); -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Create SSL context */ -+ status = tls_open(ssock); -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Prepare read buffer */ -+ ssock->asock_rbuf = (void **)pj_pool_calloc(ssock->pool, -+ ssock->param.async_cnt, -+ sizeof(void *)); -+ if (!ssock->asock_rbuf) -+ return PJ_ENOMEM; -+ -+ for (i = 0; i < ssock->param.async_cnt; ++i) { -+ ssock->asock_rbuf[i] = (void *)pj_pool_alloc( -+ ssock->pool, -+ ssock->param.read_buffer_size + -+ sizeof(read_data_t *)); -+ if (!ssock->asock_rbuf[i]) -+ return PJ_ENOMEM; -+ } -+ -+ /* Start read */ -+ status = pj_activesock_start_read2(ssock->asock, ssock->pool, -+ (unsigned) ssock->param.read_buffer_size, -+ ssock->asock_rbuf, -+ PJ_IOQUEUE_ALWAYS_ASYNC); -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Prepare write/send state */ -+ pj_assert(ssock->send_buf.max_len == 0); -+ ssock->send_buf.buf = (char *)pj_pool_alloc(ssock->pool, -+ ssock->param.send_buffer_size); -+ if (!ssock->send_buf.buf) -+ return PJ_ENOMEM; -+ -+ ssock->send_buf.max_len = ssock->param.send_buffer_size; -+ ssock->send_buf.start = ssock->send_buf.buf; -+ ssock->send_buf.len = 0; -+ -+ /* Set server name to connect */ -+ if (ssock->param.server_name.slen) { -+ /* Server name is null terminated already */ -+ ret = gnutls_server_name_set(ssock->session, GNUTLS_NAME_DNS, -+ ssock->param.server_name.ptr, -+ ssock->param.server_name.slen); -+ if (ret < 0) { -+ PJ_LOG(3, (ssock->pool->obj_name, -+ "gnutls_server_name_set() failed: %s", -+ gnutls_strerror(ret))); -+ } -+ } -+ -+ /* Start handshake */ -+ ssock->connection_state = TLS_STATE_HANDSHAKING; -+ -+ status = tls_try_handshake(ssock); -+ if (status != PJ_EPENDING) -+ goto on_return; -+ -+ return PJ_TRUE; -+ -+on_return: -+ return on_handshake_complete(ssock, status); -+} -+ -+static void tls_ciphers_fill(void) -+{ -+ if (!tls_available_ciphers) { -+ tls_init(); -+ tls_deinit(); -+ } -+} -+ -+/* -+ ******************************************************************* -+ * API -+ ******************************************************************* -+ */ -+ -+/* Load credentials from files. */ -+PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool, -+ const pj_str_t *CA_file, -+ const pj_str_t *cert_file, -+ const pj_str_t *privkey_file, -+ const pj_str_t *privkey_pass, -+ pj_ssl_cert_t **p_cert) -+{ -+ return pj_ssl_cert_load_from_files2(pool, CA_file, NULL, cert_file, -+ privkey_file, privkey_pass, p_cert); -+} -+ -+/* Load credentials from files. */ -+PJ_DECL(pj_status_t) pj_ssl_cert_load_from_files2( -+ pj_pool_t *pool, -+ const pj_str_t *CA_file, -+ const pj_str_t *CA_path, -+ const pj_str_t *cert_file, -+ const pj_str_t *privkey_file, -+ const pj_str_t *privkey_pass, -+ pj_ssl_cert_t **p_cert) -+{ -+ pj_ssl_cert_t *cert; -+ -+ PJ_ASSERT_RETURN(pool && (CA_file || CA_path) && cert_file && -+ privkey_file, -+ PJ_EINVAL); -+ -+ cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t); -+ if (CA_file) { -+ pj_strdup_with_null(pool, &cert->CA_file, CA_file); -+ } -+ if (CA_path) { -+ pj_strdup_with_null(pool, &cert->CA_path, CA_path); -+ } -+ pj_strdup_with_null(pool, &cert->cert_file, cert_file); -+ pj_strdup_with_null(pool, &cert->privkey_file, privkey_file); -+ pj_strdup_with_null(pool, &cert->privkey_pass, privkey_pass); -+ -+ *p_cert = cert; -+ -+ return PJ_SUCCESS; -+} -+ -+/* Store credentials. */ -+PJ_DECL(pj_status_t) pj_ssl_sock_set_certificate(pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ const pj_ssl_cert_t *cert) -+{ -+ pj_ssl_cert_t *cert_; -+ -+ PJ_ASSERT_RETURN(ssock && pool && cert, PJ_EINVAL); -+ -+ cert_ = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t); -+ pj_memcpy(cert_, cert, sizeof(cert)); -+ pj_strdup_with_null(pool, &cert_->CA_file, &cert->CA_file); -+ pj_strdup_with_null(pool, &cert_->CA_path, &cert->CA_path); -+ pj_strdup_with_null(pool, &cert_->cert_file, &cert->cert_file); -+ pj_strdup_with_null(pool, &cert_->privkey_file, &cert->privkey_file); -+ pj_strdup_with_null(pool, &cert_->privkey_pass, &cert->privkey_pass); -+ -+ ssock->cert = cert_; -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* Get available ciphers. */ -+PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[], -+ unsigned *cipher_num) -+{ -+ unsigned int i; -+ -+ PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL); -+ -+ tls_ciphers_fill(); -+ -+ if (!tls_available_ciphers) { -+ *cipher_num = 0; -+ return PJ_ENOTFOUND; -+ } -+ -+ *cipher_num = PJ_MIN(*cipher_num, tls_available_ciphers); -+ -+ for (i = 0; i < *cipher_num; ++i) -+ ciphers[i] = tls_ciphers[i].id; -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* Get cipher name string. */ -+PJ_DEF(const char *)pj_ssl_cipher_name(pj_ssl_cipher cipher) -+{ -+ unsigned int i; -+ -+ tls_ciphers_fill(); -+ -+ for (i = 0; i < tls_available_ciphers; ++i) { -+ if (cipher == tls_ciphers[i].id) -+ return tls_ciphers[i].name; -+ } -+ -+ return NULL; -+} -+ -+ -+/* Get cipher identifier. */ -+PJ_DEF(pj_ssl_cipher) pj_ssl_cipher_id(const char *cipher_name) -+{ -+ unsigned int i; -+ -+ tls_ciphers_fill(); -+ -+ for (i = 0; i < tls_available_ciphers; ++i) { -+ if (!pj_ansi_stricmp(tls_ciphers[i].name, cipher_name)) -+ return tls_ciphers[i].id; -+ } -+ -+ return PJ_TLS_UNKNOWN_CIPHER; -+} -+ -+ -+/* Check if the specified cipher is supported by the TLS backend. */ -+PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher) -+{ -+ unsigned int i; -+ -+ tls_ciphers_fill(); -+ -+ for (i = 0; i < tls_available_ciphers; ++i) { -+ if (cipher == tls_ciphers[i].id) -+ return PJ_TRUE; -+ } -+ -+ return PJ_FALSE; -+} -+ -+/* Create SSL socket instance. */ -+PJ_DEF(pj_status_t) pj_ssl_sock_create(pj_pool_t *pool, -+ const pj_ssl_sock_param *param, -+ pj_ssl_sock_t **p_ssock) -+{ -+ pj_ssl_sock_t *ssock; -+ pj_status_t status; -+ -+ PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL); -+ PJ_ASSERT_RETURN(param->sock_type == pj_SOCK_STREAM(), PJ_ENOTSUP); -+ -+ pool = pj_pool_create(pool->factory, "tls%p", 512, 512, NULL); -+ -+ /* Create secure socket */ -+ ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t); -+ ssock->pool = pool; -+ ssock->sock = PJ_INVALID_SOCKET; -+ ssock->connection_state = TLS_STATE_NULL; -+ pj_list_init(&ssock->write_pending); -+ pj_list_init(&ssock->write_pending_empty); -+ pj_list_init(&ssock->send_pending); -+ pj_timer_entry_init(&ssock->timer, 0, ssock, &on_timer); -+ pj_ioqueue_op_key_init(&ssock->handshake_op_key, -+ sizeof(pj_ioqueue_op_key_t)); -+ -+ /* Create secure socket mutex */ -+ status = pj_lock_create_recursive_mutex(pool, pool->obj_name, -+ &ssock->circ_buf_output_mutex); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ /* Create input circular buffer mutex */ -+ status = pj_lock_create_simple_mutex(pool, pool->obj_name, -+ &ssock->circ_buf_input_mutex); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ /* Create output circular buffer mutex */ -+ status = pj_lock_create_simple_mutex(pool, pool->obj_name, -+ &ssock->circ_buf_output_mutex); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ /* Init secure socket param */ -+ ssock->param = *param; -+ ssock->param.read_buffer_size = ((ssock->param.read_buffer_size + 7) >> 3) << 3; -+ -+ if (param->ciphers_num > 0) { -+ unsigned int i; -+ ssock->param.ciphers = (pj_ssl_cipher *) -+ pj_pool_calloc(pool, param->ciphers_num, -+ sizeof(pj_ssl_cipher)); -+ if (!ssock->param.ciphers) -+ return PJ_ENOMEM; -+ -+ for (i = 0; i < param->ciphers_num; ++i) -+ ssock->param.ciphers[i] = param->ciphers[i]; -+ } -+ -+ /* Server name must be null-terminated */ -+ pj_strdup_with_null(pool, &ssock->param.server_name, ¶m->server_name); -+ -+ /* Finally */ -+ *p_ssock = ssock; -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* -+ * Close the secure socket. This will unregister the socket from the -+ * ioqueue and ultimately close the socket. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock) -+{ -+ pj_pool_t *pool; -+ -+ PJ_ASSERT_RETURN(ssock, PJ_EINVAL); -+ -+ if (!ssock->pool) -+ return PJ_SUCCESS; -+ -+ if (ssock->timer.id != TIMER_NONE) { -+ pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer); -+ ssock->timer.id = TIMER_NONE; -+ } -+ -+ tls_sock_reset(ssock); -+ -+ pj_lock_destroy(ssock->circ_buf_output_mutex); -+ pj_lock_destroy(ssock->circ_buf_input_mutex); -+ -+ pool = ssock->pool; -+ ssock->pool = NULL; -+ if (pool) -+ pj_pool_release(pool); -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* Associate arbitrary data with the secure socket. */ -+PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data(pj_ssl_sock_t *ssock, -+ void *user_data) -+{ -+ PJ_ASSERT_RETURN(ssock, PJ_EINVAL); -+ -+ ssock->param.user_data = user_data; -+ return PJ_SUCCESS; -+} -+ -+ -+/* Retrieve the user data previously associated with this secure socket. */ -+PJ_DEF(void *)pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock) -+{ -+ PJ_ASSERT_RETURN(ssock, NULL); -+ -+ return ssock->param.user_data; -+} -+ -+ -+/* Retrieve the local address and port used by specified SSL socket. */ -+PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock, -+ pj_ssl_sock_info *info) -+{ -+ pj_bzero(info, sizeof(*info)); -+ -+ /* Established flag */ -+ info->established = (ssock->connection_state == TLS_STATE_ESTABLISHED); -+ -+ /* Protocol */ -+ info->proto = ssock->param.proto; -+ -+ /* Local address */ -+ pj_sockaddr_cp(&info->local_addr, &ssock->local_addr); -+ -+ if (info->established) { -+ int i; -+ gnutls_cipher_algorithm_t lookup; -+ gnutls_cipher_algorithm_t cipher; -+ -+ /* Current cipher */ -+ cipher = gnutls_cipher_get(ssock->session); -+ for (i = 0; ; i++) { -+ unsigned char id[2]; -+ const char *suite = gnutls_cipher_suite_info(i, (unsigned char *)id, -+ NULL, &lookup, NULL, -+ NULL); -+ if (suite) { -+ if (lookup == cipher) { -+ info->cipher = (pj_uint32_t) ((id[0] << 8) | id[1]); -+ break; -+ } -+ } else -+ break; -+ } -+ -+ /* Remote address */ -+ pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr); -+ -+ /* Certificates info */ -+ info->local_cert_info = &ssock->local_cert_info; -+ info->remote_cert_info = &ssock->remote_cert_info; -+ -+ /* Verification status */ -+ info->verify_status = ssock->verify_status; -+ } -+ -+ /* Last known GnuTLS error code */ -+ info->last_native_err = ssock->last_err; -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* Starts read operation on this secure socket. */ -+PJ_DEF(pj_status_t) pj_ssl_sock_start_read(pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ unsigned buff_size, -+ pj_uint32_t flags) -+{ -+ void **readbuf; -+ unsigned int i; -+ -+ PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL); -+ PJ_ASSERT_RETURN(ssock->connection_state == TLS_STATE_ESTABLISHED, -+ PJ_EINVALIDOP); -+ -+ readbuf = (void**) pj_pool_calloc(pool, ssock->param.async_cnt, -+ sizeof(void *)); -+ if (!readbuf) -+ return PJ_ENOMEM; -+ -+ for (i = 0; i < ssock->param.async_cnt; ++i) { -+ readbuf[i] = pj_pool_alloc(pool, buff_size); -+ if (!readbuf[i]) -+ return PJ_ENOMEM; -+ } -+ -+ return pj_ssl_sock_start_read2(ssock, pool, buff_size, readbuf, flags); -+} -+ -+ -+/* -+ * Same as #pj_ssl_sock_start_read(), except that the application -+ * supplies the buffers for the read operation so that the acive socket -+ * does not have to allocate the buffers. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ unsigned buff_size, -+ void *readbuf[], -+ pj_uint32_t flags) -+{ -+ unsigned int i; -+ -+ PJ_ASSERT_RETURN(ssock && pool && buff_size && readbuf, PJ_EINVAL); -+ PJ_ASSERT_RETURN(ssock->connection_state == TLS_STATE_ESTABLISHED, -+ PJ_EINVALIDOP); -+ -+ /* Create SSL socket read buffer */ -+ ssock->ssock_rbuf = (read_data_t*)pj_pool_calloc(pool, -+ ssock->param.async_cnt, -+ sizeof(read_data_t)); -+ if (!ssock->ssock_rbuf) -+ return PJ_ENOMEM; -+ -+ /* Store SSL socket read buffer pointer in the activesock read buffer */ -+ for (i = 0; i < ssock->param.async_cnt; ++i) { -+ read_data_t **p_ssock_rbuf = -+ OFFSET_OF_READ_DATA_PTR(ssock, ssock->asock_rbuf[i]); -+ -+ ssock->ssock_rbuf[i].data = readbuf[i]; -+ ssock->ssock_rbuf[i].len = 0; -+ -+ *p_ssock_rbuf = &ssock->ssock_rbuf[i]; -+ } -+ -+ ssock->read_size = buff_size; -+ ssock->read_started = PJ_TRUE; -+ ssock->read_flags = flags; -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* -+ * Same as pj_ssl_sock_start_read(), except that this function is used -+ * only for datagram sockets, and it will trigger \a on_data_recvfrom() -+ * callback instead. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ unsigned buff_size, -+ pj_uint32_t flags) -+{ -+ PJ_UNUSED_ARG(ssock); -+ PJ_UNUSED_ARG(pool); -+ PJ_UNUSED_ARG(buff_size); -+ PJ_UNUSED_ARG(flags); -+ -+ return PJ_ENOTSUP; -+} -+ -+ -+/* -+ * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() -+ * operation takes the buffer from the argument rather than creating -+ * new ones. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ unsigned buff_size, -+ void *readbuf[], -+ pj_uint32_t flags) -+{ -+ PJ_UNUSED_ARG(ssock); -+ PJ_UNUSED_ARG(pool); -+ PJ_UNUSED_ARG(buff_size); -+ PJ_UNUSED_ARG(readbuf); -+ PJ_UNUSED_ARG(flags); -+ -+ return PJ_ENOTSUP; -+} -+ -+ -+/* -+ * Write the plain data to GnuTLS, it will be encrypted by gnutls_record_send() -+ * and sent via tls_data_push. Note that re-negotitation may be on progress, so -+ * sending data should be delayed until re-negotiation is completed. -+ */ -+static pj_status_t tls_write(pj_ssl_sock_t *ssock, -+ pj_ioqueue_op_key_t *send_key, -+ const void *data, pj_ssize_t size, unsigned flags) -+{ -+ pj_status_t status; -+ int nwritten; -+ pj_ssize_t total_written = 0; -+ -+ /* Ask GnuTLS to encrypt our plaintext now. GnuTLS will use the push -+ * callback to actually write the encrypted bytes into our output circular -+ * buffer. GnuTLS may refuse to "send" everything at once, but since we are -+ * not really sending now, we will just call it again now until it succeeds -+ * (or fails in a fatal way). */ -+ while (total_written < size) { -+ /* Try encrypting using GnuTLS */ -+ nwritten = gnutls_record_send(ssock->session, ((read_data_t *)data) + total_written, -+ size); -+ -+ if (nwritten > 0) { -+ /* Good, some data was encrypted and written */ -+ total_written += nwritten; -+ } else { -+ /* Normally we would have to retry record_send but our internal -+ * state has not changed, so we have to ask for more data first. -+ * We will just try again later, although this should never happen. -+ */ -+ return tls_status_from_err(ssock, nwritten); -+ } -+ } -+ -+ /* All encrypted data is written to the output circular buffer; -+ * now send it on the socket (or notify problem). */ -+ if (total_written == size) -+ status = flush_circ_buf_output(ssock, send_key, size, flags); -+ else -+ status = PJ_ENOMEM; -+ -+ return status; -+} -+ -+ -+/* Flush delayed data sending in the write pending list. */ -+static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock) -+{ -+ /* Check for another ongoing flush */ -+ if (ssock->flushing_write_pend) { -+ return PJ_EBUSY; -+ } -+ -+ pj_lock_acquire(ssock->circ_buf_output_mutex); -+ -+ /* Again, check for another ongoing flush */ -+ if (ssock->flushing_write_pend) { -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ return PJ_EBUSY; -+ } -+ -+ /* Set ongoing flush flag */ -+ ssock->flushing_write_pend = PJ_TRUE; -+ -+ while (!pj_list_empty(&ssock->write_pending)) { -+ write_data_t *wp; -+ pj_status_t status; -+ -+ wp = ssock->write_pending.next; -+ -+ /* Ticket #1573: Don't hold mutex while calling socket send. */ -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ -+ status = tls_write(ssock, &wp->key, wp->data.ptr, -+ wp->plain_data_len, wp->flags); -+ if (status != PJ_SUCCESS) { -+ /* Reset ongoing flush flag first. */ -+ ssock->flushing_write_pend = PJ_FALSE; -+ return status; -+ } -+ -+ pj_lock_acquire(ssock->circ_buf_output_mutex); -+ pj_list_erase(wp); -+ pj_list_push_back(&ssock->write_pending_empty, wp); -+ } -+ -+ /* Reset ongoing flush flag */ -+ ssock->flushing_write_pend = PJ_FALSE; -+ -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* Sending is delayed, push back the sending data into pending list. */ -+static pj_status_t delay_send(pj_ssl_sock_t *ssock, -+ pj_ioqueue_op_key_t *send_key, -+ const void *data, pj_ssize_t size, -+ unsigned flags) -+{ -+ write_data_t *wp; -+ -+ pj_lock_acquire(ssock->circ_buf_output_mutex); -+ -+ /* Init write pending instance */ -+ if (!pj_list_empty(&ssock->write_pending_empty)) { -+ wp = ssock->write_pending_empty.next; -+ pj_list_erase(wp); -+ } else { -+ wp = PJ_POOL_ZALLOC_T(ssock->pool, write_data_t); -+ } -+ -+ wp->app_key = send_key; -+ wp->plain_data_len = size; -+ wp->data.ptr = data; -+ wp->flags = flags; -+ -+ pj_list_push_back(&ssock->write_pending, wp); -+ -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ -+ /* Must return PJ_EPENDING */ -+ return PJ_EPENDING; -+} -+ -+ -+/** -+ * Send data using the socket. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_send(pj_ssl_sock_t *ssock, -+ pj_ioqueue_op_key_t *send_key, -+ const void *data, pj_ssize_t *size, -+ unsigned flags) -+{ -+ pj_status_t status; -+ -+ PJ_ASSERT_RETURN(ssock && data && size && (*size > 0), PJ_EINVAL); -+ PJ_ASSERT_RETURN(ssock->connection_state==TLS_STATE_ESTABLISHED, -+ PJ_EINVALIDOP); -+ -+ /* Flush delayed send first. Sending data might be delayed when -+ * re-negotiation is on-progress. */ -+ status = flush_delayed_send(ssock); -+ if (status == PJ_EBUSY) { -+ /* Re-negotiation or flushing is on progress, delay sending */ -+ status = delay_send(ssock, send_key, data, *size, flags); -+ goto on_return; -+ } else if (status != PJ_SUCCESS) { -+ goto on_return; -+ } -+ -+ /* Write data to SSL */ -+ status = tls_write(ssock, send_key, data, *size, flags); -+ if (status == PJ_EBUSY) { -+ /* Re-negotiation is on progress, delay sending */ -+ status = delay_send(ssock, send_key, data, *size, flags); -+ } -+ -+on_return: -+ return status; -+} -+ -+ -+/** -+ * Send datagram using the socket. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock, -+ pj_ioqueue_op_key_t *send_key, -+ const void *data, pj_ssize_t *size, -+ unsigned flags, -+ const pj_sockaddr_t *addr, int addr_len) -+{ -+ PJ_UNUSED_ARG(ssock); -+ PJ_UNUSED_ARG(send_key); -+ PJ_UNUSED_ARG(data); -+ PJ_UNUSED_ARG(size); -+ PJ_UNUSED_ARG(flags); -+ PJ_UNUSED_ARG(addr); -+ PJ_UNUSED_ARG(addr_len); -+ -+ return PJ_ENOTSUP; -+} -+ -+/** -+ * Starts asynchronous socket accept() operations on this secure socket. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ const pj_sockaddr_t *localaddr, -+ int addr_len) -+{ -+ return pj_ssl_sock_start_accept2(ssock, pool, localaddr, addr_len, -+ &ssock->param); -+} -+ -+/** -+ * Starts asynchronous socket accept() operations on this secure socket. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_start_accept2 (pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ const pj_sockaddr_t *localaddr, -+ int addr_len, -+ const pj_ssl_sock_param *newsock_param) -+{ -+ pj_activesock_cb asock_cb; -+ pj_activesock_cfg asock_cfg; -+ pj_status_t status; -+ -+ PJ_ASSERT_RETURN(ssock && pool && localaddr && addr_len, PJ_EINVAL); -+ -+ /* Verify new socket parameters */ -+ if (newsock_param->grp_lock != ssock->param.grp_lock || -+ newsock_param->sock_af != ssock->param.sock_af || -+ newsock_param->sock_type != ssock->param.sock_type) -+ { -+ return PJ_EINVAL; -+ } -+ -+ /* Create socket */ -+ status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, -+ &ssock->sock); -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Apply SO_REUSEADDR */ -+ if (ssock->param.reuse_addr) { -+ int enabled = 1; -+ status = pj_sock_setsockopt(ssock->sock, pj_SOL_SOCKET(), -+ pj_SO_REUSEADDR(), -+ &enabled, sizeof(enabled)); -+ if (status != PJ_SUCCESS) { -+ PJ_PERROR(4,(ssock->pool->obj_name, status, -+ "Warning: error applying SO_REUSEADDR")); -+ } -+ } -+ -+ /* Apply QoS, if specified */ -+ status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type, -+ &ssock->param.qos_params, 2, -+ ssock->pool->obj_name, NULL); -+ if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error) -+ goto on_error; -+ -+ /* Bind socket */ -+ status = pj_sock_bind(ssock->sock, localaddr, addr_len); -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Start listening to the address */ -+ status = pj_sock_listen(ssock->sock, PJ_SOMAXCONN); -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Create active socket */ -+ pj_activesock_cfg_default(&asock_cfg); -+ asock_cfg.async_cnt = ssock->param.async_cnt; -+ asock_cfg.concurrency = ssock->param.concurrency; -+ asock_cfg.whole_data = PJ_TRUE; -+ -+ pj_bzero(&asock_cb, sizeof(asock_cb)); -+ asock_cb.on_accept_complete = asock_on_accept_complete; -+ -+ status = pj_activesock_create(pool, -+ ssock->sock, -+ ssock->param.sock_type, -+ &asock_cfg, -+ ssock->param.ioqueue, -+ &asock_cb, -+ ssock, -+ &ssock->asock); -+ -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Start accepting */ -+ pj_ssl_sock_param_copy(pool, &ssock->newsock_param, newsock_param); -+ status = pj_activesock_start_accept(ssock->asock, pool); -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Update local address */ -+ ssock->addr_len = addr_len; -+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, -+ &ssock->addr_len); -+ if (status != PJ_SUCCESS) -+ pj_sockaddr_cp(&ssock->local_addr, localaddr); -+ -+ ssock->is_server = PJ_TRUE; -+ -+ return PJ_SUCCESS; -+ -+on_error: -+ tls_sock_reset(ssock); -+ return status; -+} -+ -+ -+/** -+ * Starts asynchronous socket connect() operation. -+ */ -+PJ_DECL(pj_status_t) pj_ssl_sock_start_connect(pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ const pj_sockaddr_t *localaddr, -+ const pj_sockaddr_t *remaddr, -+ int addr_len) -+{ -+ pj_activesock_cb asock_cb; -+ pj_activesock_cfg asock_cfg; -+ pj_status_t status; -+ -+ PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len, -+ PJ_EINVAL); -+ -+ /* Create socket */ -+ status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, -+ &ssock->sock); -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Apply QoS, if specified */ -+ status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type, -+ &ssock->param.qos_params, 2, -+ ssock->pool->obj_name, NULL); -+ if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error) -+ goto on_error; -+ -+ /* Bind socket */ -+ status = pj_sock_bind(ssock->sock, localaddr, addr_len); -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Create active socket */ -+ pj_activesock_cfg_default(&asock_cfg); -+ asock_cfg.async_cnt = ssock->param.async_cnt; -+ asock_cfg.concurrency = ssock->param.concurrency; -+ asock_cfg.whole_data = PJ_TRUE; -+ -+ pj_bzero(&asock_cb, sizeof(asock_cb)); -+ asock_cb.on_connect_complete = asock_on_connect_complete; -+ asock_cb.on_data_read = asock_on_data_read; -+ asock_cb.on_data_sent = asock_on_data_sent; -+ -+ status = pj_activesock_create(pool, -+ ssock->sock, -+ ssock->param.sock_type, -+ &asock_cfg, -+ ssock->param.ioqueue, -+ &asock_cb, -+ ssock, -+ &ssock->asock); -+ -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Save remote address */ -+ pj_sockaddr_cp(&ssock->rem_addr, remaddr); -+ -+ /* Start timer */ -+ if (ssock->param.timer_heap && -+ (ssock->param.timeout.sec != 0 || ssock->param.timeout.msec != 0)) -+ { -+ pj_assert(ssock->timer.id == TIMER_NONE); -+ ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT; -+ status = pj_timer_heap_schedule(ssock->param.timer_heap, -+ &ssock->timer, -+ &ssock->param.timeout); -+ if (status != PJ_SUCCESS) -+ ssock->timer.id = TIMER_NONE; -+ } -+ -+ status = pj_activesock_start_connect(ssock->asock, pool, remaddr, -+ addr_len); -+ -+ if (status == PJ_SUCCESS) -+ asock_on_connect_complete(ssock->asock, PJ_SUCCESS); -+ else if (status != PJ_EPENDING) -+ goto on_error; -+ -+ /* Update local address */ -+ ssock->addr_len = addr_len; -+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, -+ &ssock->addr_len); -+ /* Note that we may not get an IP address here. This can -+ * happen for example on Windows, where getsockname() -+ * would return 0.0.0.0 if socket has just started the -+ * async connect. In this case, just leave the local -+ * address with 0.0.0.0 for now; it will be updated -+ * once the socket is established. -+ */ -+ -+ /* Update socket state */ -+ ssock->is_server = PJ_FALSE; -+ -+ return PJ_EPENDING; -+ -+on_error: -+ tls_sock_reset(ssock); -+ return status; -+} -+ -+ -+PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock) -+{ -+ int status; -+ -+ /* Nothing established yet */ -+ PJ_ASSERT_RETURN(ssock->connection_state == TLS_STATE_ESTABLISHED, -+ PJ_EINVALIDOP); -+ -+ /* Cannot renegotiate; we're a client */ -+ /* FIXME: in fact maybe that's not true */ -+ PJ_ASSERT_RETURN(!ssock->is_server, PJ_EINVALIDOP); -+ -+ /* First call gnutls_rehandshake() to see if this is even possible */ -+ status = gnutls_rehandshake(ssock->session); -+ -+ if (status == GNUTLS_E_SUCCESS) { -+ /* Rehandshake is possible, so try a GnuTLS handshake now. The eventual -+ * gnutls_record_recv() calls could return a few specific values during -+ * this state: -+ * -+ * - GNUTLS_E_REHANDSHAKE: rehandshake message processing -+ * - GNUTLS_E_WARNING_ALERT_RECEIVED: client does not wish to -+ * renegotiate -+ */ -+ ssock->connection_state = TLS_STATE_HANDSHAKING; -+ status = tls_try_handshake(ssock); -+ -+ return status; -+ } else { -+ return tls_status_from_err(ssock, status); -+ } -+} -+ -+#endif /* PJ_HAS_SSL_SOCK */ -diff -ru a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c ---- a/pjlib/src/pj/ssl_sock_ossl.c 2017-01-24 00:41:05.000000000 -0500 -+++ b/pjlib/src/pj/ssl_sock_ossl.c 2017-06-08 13:42:15.188809557 -0400 -@@ -32,8 +32,10 @@ - #include <pj/timer.h> - - --/* Only build when PJ_HAS_SSL_SOCK is enabled */ --#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK!=0 -+/* Only build when PJ_HAS_SSL_SOCK is enabled and when PJ_HAS_TLS_SOCK is -+ * disabled (meaning GnuTLS is off) */ -+#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \ -+ defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK == 0 - - #define THIS_FILE "ssl_sock_ossl.c" - -diff -ru a/pjmedia/src/pjmedia/transport_srtp.c b/pjmedia/src/pjmedia/transport_srtp.c ---- a/pjmedia/src/pjmedia/transport_srtp.c 2017-01-10 23:38:29.000000000 -0500 -+++ b/pjmedia/src/pjmedia/transport_srtp.c 2017-06-08 13:43:29.727001721 -0400 -@@ -30,7 +30,8 @@ - - #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) - --#if defined(PJ_HAS_SSL_SOCK) && (PJ_HAS_SSL_SOCK != 0) -+#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \ -+ defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK == 0 - # include <openssl/rand.h> - - /* Suppress compile warning of OpenSSL deprecation (OpenSSL is deprecated -@@ -1147,7 +1148,8 @@ - key_ok = PJ_TRUE; - - --#if defined(PJ_HAS_SSL_SOCK) && (PJ_HAS_SSL_SOCK != 0) -+#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \ -+ defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK == 0 - - /* Include OpenSSL libraries for MSVC */ - # ifdef _MSC_VER - diff --git a/libraries/pjproject-ring/patches/ice_config.patch b/libraries/pjproject-ring/patches/ice_config.patch deleted file mode 100644 index 0bc2e43292..0000000000 --- a/libraries/pjproject-ring/patches/ice_config.patch +++ /dev/null @@ -1,27 +0,0 @@ ---- a/pjnath/include/pjnath/config.h -+++ b/pjnath/include/pjnath/config.h -@@ -233,3 +233,3 @@ - #ifndef PJ_ICE_MAX_CAND --# define PJ_ICE_MAX_CAND 16 -+# define PJ_ICE_MAX_CAND 256 - #endif -@@ -243,3 +243,3 @@ - #ifndef PJ_ICE_ST_MAX_CAND --# define PJ_ICE_ST_MAX_CAND 8 -+# define PJ_ICE_ST_MAX_CAND 32 - #endif -@@ -254,3 +254,3 @@ - #ifndef PJ_ICE_MAX_STUN --# define PJ_ICE_MAX_STUN 2 -+# define PJ_ICE_MAX_STUN 3 - #endif -@@ -274,3 +274,3 @@ - #ifndef PJ_ICE_COMP_BITS --# define PJ_ICE_COMP_BITS 1 -+# define PJ_ICE_COMP_BITS 2 - #endif -@@ -325,3 +325,3 @@ - #ifndef PJ_ICE_MAX_CHECKS --# define PJ_ICE_MAX_CHECKS 32 -+# define PJ_ICE_MAX_CHECKS 150 - #endif diff --git a/libraries/pjproject-ring/patches/intptr_t.patch b/libraries/pjproject-ring/patches/intptr_t.patch deleted file mode 100644 index 8994a991c0..0000000000 --- a/libraries/pjproject-ring/patches/intptr_t.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- pjproject/pjsip/src/pjsua2/endpoint.cpp.orig 2014-09-05 16:39:29.708512865 -0400 -+++ pjproject/pjsip/src/pjsua2/endpoint.cpp 2014-09-05 16:39:00.084513427 -0400 -@@ -489,7 +489,7 @@ - LogEntry entry; - entry.level = level; - entry.msg = string(data, len); -- entry.threadId = (long)pj_thread_this(); -+ entry.threadId = (intptr_t)pj_thread_this(); - entry.threadName = string(pj_thread_get_name(pj_thread_this())); - - ep.utilLogWrite(entry); diff --git a/libraries/pjproject-ring/patches/ipv6.patch b/libraries/pjproject-ring/patches/ipv6.patch deleted file mode 100644 index 8b42fbbda1..0000000000 --- a/libraries/pjproject-ring/patches/ipv6.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/pjlib/include/pj/config.h -+++ b/pjlib/include/pj/config.h -@@ -549,7 +549,7 @@ - * Default: 0 (disabled, for now) - */ - #ifndef PJ_HAS_IPV6 --# define PJ_HAS_IPV6 0 -+# define PJ_HAS_IPV6 1 - #endif - - /** diff --git a/libraries/pjproject-ring/patches/isblank.patch b/libraries/pjproject-ring/patches/isblank.patch deleted file mode 100644 index 98f6f1e827..0000000000 --- a/libraries/pjproject-ring/patches/isblank.patch +++ /dev/null @@ -1,8 +0,0 @@ ---- pjproject/pjlib/include/pj/compat/ctype.h 2016-10-06 16:39:29.708512865 -0400 -+++ pjproject/pjlib/include/pj/compat/ctype.h 2016-10-06 17:39:00.084513427 -0400 -@@ -43,5 +43,2 @@ - --#ifndef isblank --# define isblank(c) (c==' ' || c=='\t') --#endif - diff --git a/libraries/pjproject-ring/patches/multiple_listeners.patch b/libraries/pjproject-ring/patches/multiple_listeners.patch deleted file mode 100644 index 89590107c8..0000000000 --- a/libraries/pjproject-ring/patches/multiple_listeners.patch +++ /dev/null @@ -1,66 +0,0 @@ -diff --git a/pjproject/pjsip/src/pjsip/sip_transport.c b/pjproject_new/pjsip/src/pjsip/sip_transport.c -index 4b2cc1a..22e3603 100644 ---- a/pjsip/src/pjsip/sip_transport.c -+++ b/pjsip/src/pjsip/sip_transport.c -@@ -1248,22 +1248,22 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr, - - pj_lock_acquire(mgr->lock); - -- /* Check that no factory with the same type has been registered. */ -+ /* Check that no factory with the same type and bound address has been registered. */ - status = PJ_SUCCESS; - for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) { -- if (p->type == tpf->type) { -- status = PJSIP_ETYPEEXISTS; -- break; -- } -- if (p == tpf) { -- status = PJ_EEXISTS; -- break; -- } -+ 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; -+ } - } - - if (status != PJ_SUCCESS) { -- pj_lock_release(mgr->lock); -- return status; -+ pj_lock_release(mgr->lock); -+ return status; - } - - pj_list_insert_before(&mgr->factory_list, tpf); -@@ -2047,13 +2047,11 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, - pj_memcpy(&key.rem_addr, remote, addr_len); - - transport = (pjsip_transport*) -- pj_hash_get(mgr->table, &key, key_len, NULL); -- -+ pj_hash_get(mgr->table, &key, key_len, NULL); -+ unsigned flag = pjsip_transport_get_flag_from_type(type); - if (transport == NULL) { -- unsigned flag = pjsip_transport_get_flag_from_type(type); - const pj_sockaddr *remote_addr = (const pj_sockaddr*)remote; - -- - /* Ignore address for loop transports. */ - if (type == PJSIP_TRANSPORT_LOOP || - type == PJSIP_TRANSPORT_LOOP_DGRAM) -@@ -2135,6 +2135,11 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, - } - - } else { -+ /* Make sure we don't use another factory than the one given if secure flag is set */ -+ if (flag & PJSIP_TRANSPORT_SECURE) { -+ TRACE_((THIS_FILE, "Can't create new TLS transport with no provided suitable TLS listener.")); -+ return PJSIP_ETPNOTSUITABLE; -+ } - - /* Find factory with type matches the destination type */ - factory = mgr->factory_list.next; diff --git a/libraries/pjproject-ring/patches/notestsapps.patch b/libraries/pjproject-ring/patches/notestsapps.patch deleted file mode 100644 index 8bf957b368..0000000000 --- a/libraries/pjproject-ring/patches/notestsapps.patch +++ /dev/null @@ -1,106 +0,0 @@ -diff --git a/Makefile b/Makefile -index 33a4e6b..a486eb7 100644 ---- a/Makefile -+++ b/Makefile -@@ -4,7 +4,7 @@ include build/host-$(HOST_NAME).mak - include version.mak - - LIB_DIRS = pjlib/build pjlib-util/build pjnath/build third_party/build pjmedia/build pjsip/build --DIRS = $(LIB_DIRS) pjsip-apps/build $(EXTRA_DIRS) -+DIRS = $(LIB_DIRS) $(EXTRA_DIRS) - - ifdef MINSIZE - MAKE_FLAGS := MINSIZE=1 -diff --git a/pjlib-util/build/Makefile b/pjlib-util/build/Makefile -index cb601cb..862a78a 100644 ---- a/pjlib-util/build/Makefile -+++ b/pjlib-util/build/Makefile -@@ -54,7 +54,6 @@ export UTIL_TEST_OBJS += xml.o encryption.o stun.o resolver_test.o test.o \ - export UTIL_TEST_CFLAGS += $(_CFLAGS) - export UTIL_TEST_CXXFLAGS += $(_CXXFLAGS) - export UTIL_TEST_LDFLAGS += $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS) --export UTIL_TEST_EXE:=pjlib-util-test-$(TARGET_NAME)$(HOST_EXE) - - - export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT -diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile -index 1e64950..a75fa65 100644 ---- a/pjlib/build/Makefile -+++ b/pjlib/build/Makefile -@@ -56,7 +56,6 @@ export TEST_OBJS += activesock.o atomic.o echo_clt.o errno.o exception.o \ - export TEST_CFLAGS += $(_CFLAGS) - export TEST_CXXFLAGS += $(_CXXFLAGS) - export TEST_LDFLAGS += $(PJLIB_LDLIB) $(_LDFLAGS) --export TEST_EXE := pjlib-test-$(TARGET_NAME)$(HOST_EXE) - - - export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT -diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile -index 8012cb7..2ca283a 100644 ---- a/pjmedia/build/Makefile -+++ b/pjmedia/build/Makefile -@@ -165,7 +165,6 @@ export PJMEDIA_TEST_LDFLAGS += $(PJMEDIA_CODEC_LDLIB) \ - $(PJLIB_UTIL_LDLIB) \ - $(PJNATH_LDLIB) \ - $(_LDFLAGS) --export PJMEDIA_TEST_EXE:=pjmedia-test-$(TARGET_NAME)$(HOST_EXE) - - - export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT -diff --git a/pjnath/build/Makefile b/pjnath/build/Makefile -index 1bc08b5..109f79b 100644 ---- a/pjnath/build/Makefile -+++ b/pjnath/build/Makefile -@@ -54,7 +54,6 @@ export PJNATH_TEST_OBJS += ice_test.o stun.o sess_auth.o server.o concur_test.o - export PJNATH_TEST_CFLAGS += $(_CFLAGS) - export PJNATH_TEST_CXXFLAGS += $(_CXXFLAGS) - export PJNATH_TEST_LDFLAGS += $(PJNATH_LDLIB) $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS) --export PJNATH_TEST_EXE:=pjnath-test-$(TARGET_NAME)$(HOST_EXE) - - - ############################################################################### -@@ -65,7 +64,6 @@ export PJTURN_CLIENT_OBJS += client_main.o - export PJTURN_CLIENT_CFLAGS += $(_CFLAGS) - export PJTURN_CLIENT_CXXFLAGS += $(_CXXFLAGS) - export PJTURN_CLIENT_LDFLAGS += $(PJNATH_LDLIB) $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS) --export PJTURN_CLIENT_EXE:=pjturn-client-$(TARGET_NAME)$(HOST_EXE) - - ############################################################################### - # Defines for building TURN server application -@@ -76,7 +74,6 @@ export PJTURN_SRV_OBJS += allocation.o auth.o listener_udp.o \ - export PJTURN_SRV_CFLAGS += $(_CFLAGS) - export PJTURN_SRV_CXXFLAGS += $(_CXXFLAGS) - export PJTURN_SRV_LDFLAGS += $(PJNATH_LDLIB) $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS) --export PJTURN_SRV_EXE:=pjturn-srv-$(TARGET_NAME)$(HOST_EXE) - - - -diff --git a/pjsip/build/Makefile b/pjsip/build/Makefile -index d2a5c2a..7e2ec60 100644 ---- a/pjsip/build/Makefile -+++ b/pjsip/build/Makefile -@@ -140,7 +140,7 @@ export PJSUA2_LIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ - account.o endpoint.o json.o persistent.o types.o \ - siptypes.o call.o presence.o media.o - export PJSUA2_LIB_CFLAGS += $(_CFLAGS) $(PJ_VIDEO_CFLAGS) --export PJSUA2_LIB_CXXFLAGS = $(PJSUA2_LIB_CFLAGS) -+export PJSUA2_LIB_CXXFLAGS = $(_CXXFLAGS) $(PJ_VIDEO_CFLAGS) - export PJSUA2_LIB_LDFLAGS += $(PJSUA_LIB_LDLIB) \ - $(PJSIP_UA_LDLIB) \ - $(PJSIP_SIMPLE_LDLIB) \ -@@ -165,7 +165,6 @@ export PJSUA2_TEST_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ - export PJSUA2_TEST_CFLAGS += $(_CFLAGS) $(PJ_VIDEO_CFLAGS) - export PJSUA2_TEST_CXXFLAGS = $(PJSUA2_LIB_CFLAGS) - export PJSUA2_TEST_LDFLAGS += $(PJ_LDXXFLAGS) $(PJ_LDXXLIBS) $(LDFLAGS) --export PJSUA2_TEST_EXE := pjsua2-test-$(TARGET_NAME)$(HOST_EXE) - - export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT - -@@ -195,7 +194,6 @@ export TEST_LDFLAGS += $(PJSIP_LDLIB) \ - $(PJLIB_UTIL_LDLIB) \ - $(PJNATH_LDLIB) \ - $(_LDFLAGS) --export TEST_EXE := pjsip-test-$(TARGET_NAME)$(HOST_EXE) - - - export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT diff --git a/libraries/pjproject-ring/patches/pj_ice_sess.patch b/libraries/pjproject-ring/patches/pj_ice_sess.patch deleted file mode 100644 index bd040ffe3e..0000000000 --- a/libraries/pjproject-ring/patches/pj_ice_sess.patch +++ /dev/null @@ -1,22 +0,0 @@ ---- a/pjnath/include/pjnath/ice_strans.h -+++ b/pjnath/include/pjnath/ice_strans.h -@@ -845,6 +845,8 @@ PJ_DECL(pj_status_t) pj_ice_strans_sendt - int dst_addr_len); - - -+PJ_DECL(pj_ice_sess *) pj_ice_strans_get_ice_sess(pj_ice_strans *ice_st); -+ - /** - * @} - */ ---- a/pjnath/src/pjnath/ice_strans.c -+++ b/pjnath/src/pjnath/ice_strans.c -@@ -1243,6 +1243,11 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto - return PJ_EINVALIDOP; - } - -+PJ_DECL(pj_ice_sess *) pj_ice_strans_get_ice_sess( pj_ice_strans *ice_st ) -+{ -+ return ice_st->ice; -+} -+ diff --git a/libraries/pjproject-ring/patches/pj_uwp.patch b/libraries/pjproject-ring/patches/pj_uwp.patch deleted file mode 100644 index 2e7f038266..0000000000 --- a/libraries/pjproject-ring/patches/pj_uwp.patch +++ /dev/null @@ -1,203 +0,0 @@ ---- a/pjlib-util/build/pjlib_util.vcxproj
-+++ b/pjlib-util/build/pjlib_util.vcxproj
-@@ -327,7 +327,7 @@
- </Midl>
- <ClCompile>
- <AdditionalIncludeDirectories>../include;../../pjlib/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-- <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-+ <PreprocessorDefinitions>PJ_OS_HAS_CHECK_STACK=0;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <PrecompiledHeaderOutputFile />
- </ClCompile>
- <Lib>
---- a/pjlib/build/pjlib.vcxproj
-+++ b/pjlib/build/pjlib.vcxproj
-@@ -327,8 +327,8 @@
- <TargetEnvironment>X64</TargetEnvironment>
- </Midl>
- <ClCompile>
-- <AdditionalIncludeDirectories>../include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-- <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-+ <AdditionalIncludeDirectories>..\..\..\include;../include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-+ <PreprocessorDefinitions>_WIN32_WINNT=0x0A00;PJ_TERM_HAS_COLOR=0;PJ_OS_HAS_CHECK_STACK=0;WIN32_NATIVE;PJ_HAS_SSL_SOCK;PJ_HAS_TLS_SOCK;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <PrecompiledHeaderOutputFile />
- </ClCompile>
- </ItemDefinitionGroup>
-@@ -620,6 +620,7 @@
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
- </ClCompile>
- <ClCompile Include="..\src\pj\ip_helper_win32.c">
-+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
- <ExcludedFromBuild Condition="'$(API_Family)'!='WinDesktop'">true</ExcludedFromBuild>
- </ClCompile>
- <ClCompile Include="..\src\pj\ip_helper_generic.c">
-@@ -851,6 +852,7 @@
- <ClCompile Include="..\src\pj\sock_select.c" />
- <ClCompile Include="..\src\pj\ssl_sock_common.c" />
- <ClCompile Include="..\src\pj\ssl_sock_dump.c" />
-+ <ClCompile Include="..\src\pj\ssl_sock_gtls.c" />
- <ClCompile Include="..\src\pj\ssl_sock_ossl.c" />
- <ClCompile Include="..\src\pj\string.c" />
- <ClCompile Include="..\src\pj\symbols.c">
---- a/pjlib/include/pj/compat/string.h
-+++ b/pjlib/include/pj/compat/string.h
-@@ -43,7 +43,7 @@
- # include <stdlib.h>
- #endif
-
--#if defined(_MSC_VER)
-+#if defined(PJ_WIN32)
- # define strcasecmp _stricmp
- # define strncasecmp _strnicmp
- # define snprintf _snprintf
---- /dev/null
-+++ b/pjlib/include/pj/config_site.h
-@@ -0,0 +1,8 @@
-+#include "config_site_sample.h"
-+
-+#undef PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO
-+#undef PJMEDIA_AUDIO_DEV_HAS_WMME
-+
-+#define PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO 0
-+#define PJMEDIA_AUDIO_DEV_HAS_WMME 0
-+#define PJMEDIA_RESAMPLE_IMP PJMEDIA_RESAMPLE_SPEEX
-\ No newline at end of file
---- a/pjlib/src/pj/file_access_win32.c
-+++ b/pjlib/src/pj/file_access_win32.c
-@@ -79,7 +79,7 @@ static HANDLE WINAPI create_file(LPCTSTR filename, DWORD desired_access,
- return CreateFile2(filename, desired_access, share_mode,
- creation_disposition, NULL);
- #else
-- return CreateFile(filename, desired_access, share_mode,
-+ return CreateFile2(filename, desired_access, share_mode,
- security_attributes, creation_disposition,
- flags_and_attributes, template_file);
- #endif
---- a/pjlib/src/pj/file_io_win32.c
-+++ b/pjlib/src/pj/file_io_win32.c
-@@ -129,7 +129,7 @@ PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool,
- dwDesiredAccess, dwShareMode, dwCreationDisposition,
- NULL);
- #else
-- hFile = CreateFile(PJ_STRING_TO_NATIVE(pathname,
-+ hFile = CreateFile2(PJ_STRING_TO_NATIVE(pathname,
- wpathname, sizeof(wpathname)),
- dwDesiredAccess, dwShareMode, NULL,
- dwCreationDisposition, dwFlagsAndAttributes, NULL);
---- a/pjmedia/build/pjmedia.vcxproj
-+++ b/pjmedia/build/pjmedia.vcxproj
-@@ -325,7 +325,7 @@
- </Midl>
- <ClCompile>
- <AdditionalIncludeDirectories>../include;../../pjlib/include;../../pjlib-util/include;../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include;../../third_party/webrtc/src;../..;$(DXSDK_DIR)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-- <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-+ <PreprocessorDefinitions>PJ_OS_HAS_CHECK_STACK=0;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <PrecompiledHeaderOutputFile />
- </ClCompile>
- </ItemDefinitionGroup>
---- a/pjnath/build/pjnath.vcxproj
-+++ b/pjnath/build/pjnath.vcxproj
-@@ -410,7 +410,7 @@
- </Midl>
- <ClCompile>
- <AdditionalIncludeDirectories>../include;../../pjlib/include;../../pjlib-util/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-- <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-+ <PreprocessorDefinitions>PJ_OS_HAS_CHECK_STACK=0;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <PrecompiledHeaderOutputFile />
- </ClCompile>
- </ItemDefinitionGroup>
---- a/pjsip/build/pjsip_core.vcxproj
-+++ b/pjsip/build/pjsip_core.vcxproj
-@@ -327,7 +327,7 @@
- </Midl>
- <ClCompile>
- <AdditionalIncludeDirectories>../include;../../pjlib/include;../../pjlib-util/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-- <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-+ <PreprocessorDefinitions>PJ_OS_HAS_CHECK_STACK=0;PJ_HAS_SSL_SOCK;_LIB;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <PrecompiledHeaderOutputFile />
- </ClCompile>
- <Lib>
---- a/pjsip/build/pjsip_simple.vcxproj
-+++ b/pjsip/build/pjsip_simple.vcxproj
-@@ -362,7 +362,7 @@
- </Midl>
- <ClCompile>
- <AdditionalIncludeDirectories>../include;../../pjlib-util/include;../../pjlib/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-- <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-+ <PreprocessorDefinitions>PJ_OS_HAS_CHECK_STACK=0;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <PrecompiledHeaderOutputFile />
- </ClCompile>
- <Lib>
---- a/pjsip/build/pjsip_ua.vcxproj
-+++ b/pjsip/build/pjsip_ua.vcxproj
-@@ -327,7 +327,7 @@
- </Midl>
- <ClCompile>
- <AdditionalIncludeDirectories>../include;../../pjlib/include;../../pjlib-util/include;../../pjmedia/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-- <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-+ <PreprocessorDefinitions>PJ_OS_HAS_CHECK_STACK=0;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <PrecompiledHeaderOutputFile />
- </ClCompile>
- <Lib>
---- a/pjsip/build/pjsua2_lib.vcxproj
-+++ b/pjsip/build/pjsua2_lib.vcxproj
-@@ -364,7 +364,7 @@
- </Midl>
- <ClCompile>
- <AdditionalIncludeDirectories>../include;../../pjmedia/include;../../pjlib-util/include;../../pjlib/include;../../pjnath/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-- <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-+ <PreprocessorDefinitions>PJ_OS_HAS_CHECK_STACK=0;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <PrecompiledHeaderOutputFile />
- </ClCompile>
- <Lib>
---- a/pjsip/build/pjsua_lib.vcxproj
-+++ b/pjsip/build/pjsua_lib.vcxproj
-@@ -362,7 +362,7 @@
- </Midl>
- <ClCompile>
- <AdditionalIncludeDirectories>../include;../../pjmedia/include;../../pjlib-util/include;../../pjlib/include;../../pjnath/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-- <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-+ <PreprocessorDefinitions>PJ_OS_HAS_CHECK_STACK=0;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <PrecompiledHeaderOutputFile />
- </ClCompile>
- <Lib>
---- a/pjsip/src/pjsip/sip_dialog.c
-+++ b/pjsip/src/pjsip/sip_dialog.c
-@@ -882,7 +882,7 @@ PJ_DEF(void) pjsip_dlg_inc_lock(pjsip_dialog *dlg)
- PJ_LOG(6,(dlg->obj_name, "Entering pjsip_dlg_inc_lock(), sess_count=%d",
- dlg->sess_count));
-
-- pj_mutex_lock(dlg->mutex_);
-+ pj_mutex_trylock(dlg->mutex_);
- dlg->sess_count++;
-
- PJ_LOG(6,(dlg->obj_name, "Leaving pjsip_dlg_inc_lock(), sess_count=%d",
---- a/pjlib/build/pjlib.vcxproj
-+++ b/pjlib/build/pjlib.vcxproj
-@@ -617,7 +617,7 @@
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release-Static|x64'">true</ExcludedFromBuild>
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
-- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
-+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
- </ClCompile>
- <ClCompile Include="..\src\pj\ip_helper_win32.c">
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
---- a/pjlib/src/pj/os_core_win32.c
-+++ b/pjlib/src/pj/os_core_win32.c
-@@ -1424,10 +1424,10 @@ PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
-
- PJ_LOG(6, (event->obj_name, "Pulsing event"));
-
-- if (PulseEvent(event->hEvent))
-+ //if (PulseEvent(event->hEvent))
- return PJ_SUCCESS;
-- else
-- return PJ_RETURN_OS_ERROR(GetLastError());
-+ //else
-+ //return PJ_RETURN_OS_ERROR(GetLastError());
- #endif
- }
-
---
-2.10.2.windows.1
-
diff --git a/libraries/pjproject-ring/patches/pj_uwp_fix_turn_fallback.patch b/libraries/pjproject-ring/patches/pj_uwp_fix_turn_fallback.patch deleted file mode 100644 index 5d7b44b9f5..0000000000 --- a/libraries/pjproject-ring/patches/pj_uwp_fix_turn_fallback.patch +++ /dev/null @@ -1,51 +0,0 @@ ---- a/pjnath/src/pjnath/turn_session.c -+++ b/pjnath/src/pjnath/turn_session.c -@@ -653,3 +653,3 @@ - -- cnt = PJ_TURN_MAX_DNS_SRV_CNT; -+ cnt = 1; - ai = (pj_addrinfo*) ---- a/pjnath/src/pjnath/ice_strans.c -+++ b/pjnath/src/pjnath/ice_strans.c -@@ -2078,6 +2078,38 @@ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state, - } - - sess_init_update(comp->ice_st); -+ } else if ((old_state == PJ_TURN_STATE_RESOLVING || old_state == PJ_TURN_STATE_ALLOCATING) && -+ new_state >= PJ_TURN_STATE_DEALLOCATING) -+ { -+ pj_ice_sess_cand *cand = NULL; -+ unsigned i; -+ -+ /* DNS resolution has failed! */ -+ ++comp->turn[tp_idx].err_cnt; -+ -+ /* Unregister ourself from the TURN relay */ -+ pj_turn_sock_set_user_data(turn_sock, NULL); -+ comp->turn[tp_idx].sock = NULL; -+ -+ /* Wait until initialization completes */ -+ pj_grp_lock_acquire(comp->ice_st->grp_lock); -+ -+ /* Find relayed candidate in the component */ -+ for (i=0; i<comp->cand_cnt; ++i) { -+ if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_RELAYED && -+ comp->cand_list[i].transport_id == data->transport_id) -+ { -+ cand = &comp->cand_list[i]; -+ break; -+ } -+ } -+ pj_assert(cand != NULL); -+ -+ pj_grp_lock_release(comp->ice_st->grp_lock); -+ -+ cand->status = old_state == PJ_TURN_STATE_RESOLVING ? PJ_ERESOLVE : PJ_EINVALIDOP; -+ -+ sess_init_update(comp->ice_st); - - } else if (new_state >= PJ_TURN_STATE_DEALLOCATING) { - pj_turn_session_info info; --- -2.8.1.windows.1 - diff --git a/libraries/pjproject-ring/patches/pj_uwp_gnutls.patch b/libraries/pjproject-ring/patches/pj_uwp_gnutls.patch deleted file mode 100644 index 9f20025be4..0000000000 --- a/libraries/pjproject-ring/patches/pj_uwp_gnutls.patch +++ /dev/null @@ -1,3004 +0,0 @@ -Copyright (c) 2014-2017 Savoir-faire Linux Inc. - -ssl_sock: add gnutls backend - -This backend is mutually exclusive with the OpenSSL one, but completely -compatible, and conformant to the PJSIP API. Also avoids any license issues -when linking statically. - -The configure script is updated to select either OpenSSL or GnuTLS -with --enable-ssl[='...'] and a new symbol (PJ_HAS_TLS_SOCK) is introduced -to identify which backend is in use. - -Written by -Vittorio Giovara <vittorio.giovara@savoirfairelinux.com> -Philippe Proulx <philippe.proulx@savoirfairelinux.com> and -Adrien BĂ©raud <adrien.beraud@savoirfairelinux.com> -on behalf of Savoir-faire Linux. ---- a/pjlib/include/pj/compat/os_auto.h.in -+++ b/pjlib/include/pj/compat/os_auto.h.in -@@ -209,6 +209,9 @@ - #ifndef PJ_HAS_SSL_SOCK - #undef PJ_HAS_SSL_SOCK - #endif -+#ifndef PJ_HAS_TLS_SOCK -+#undef PJ_HAS_TLS_SOCK -+#endif - - - #endif /* __PJ_COMPAT_OS_AUTO_H__ */ ---- a/pjlib/include/pj/config.h -+++ b/pjlib/include/pj/config.h -@@ -855,13 +861,15 @@ - - /** - * Enable secure socket. For most platforms, this is implemented using -- * OpenSSL, so this will require OpenSSL to be installed. For Symbian -+ * OpenSSL, so this will require OpenSSL or GnuTLS to be installed. For Symbian - * platform, this is implemented natively using CSecureSocket. - * - * Default: 0 (for now) - */ - #ifndef PJ_HAS_SSL_SOCK - # define PJ_HAS_SSL_SOCK 0 -+ // When set to 1 secure sockets will use the GnuTLS backend -+# define PJ_HAS_TLS_SOCK 0 - #endif - - ---- a/pjlib/include/pj/ssl_sock.h -+++ b/pjlib/include/pj/ssl_sock.h -@@ -184,6 +184,11 @@ typedef struct pj_ssl_cert_info { - pj_str_t raw; /**< Raw certificate in PEM format, only - available for remote certificate. */ - -+ struct { -+ unsigned cnt; /**< # of entry */ -+ pj_str_t* cert_raw; -+ } raw_chain; -+ - } pj_ssl_cert_info; - - ---- a/pjlib/src/pj/ssl_sock_common.c -+++ b/pjlib/src/pj/ssl_sock_common.c -@@ -35,7 +35,12 @@ PJ_DEF(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param) - param->async_cnt = 1; - param->concurrency = -1; - param->whole_data = PJ_TRUE; -+#if defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK == 1 -+ // GnuTLS is allowed to send bigger chunks -+ param->send_buffer_size = 65536; -+#else - param->send_buffer_size = 8192; -+#endif - #if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0 - param->read_buffer_size = 1500; - #endif ---- /dev/null -+++ b/pjlib/src/pj/ssl_sock_gtls.c -@@ -0,0 +1,2887 @@ -+/* $Id$ */ -+/* -+ * Copyright (C) 2014-2016 Savoir-faire Linux. (https://www.savoirfairelinux.com) -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#include <pj/ssl_sock.h> -+#include <pj/activesock.h> -+#include <pj/compat/socket.h> -+#include <pj/assert.h> -+#include <pj/errno.h> -+#include <pj/list.h> -+#include <pj/lock.h> -+#include <pj/log.h> -+#include <pj/math.h> -+#include <pj/os.h> -+#include <pj/pool.h> -+#include <pj/string.h> -+#include <pj/timer.h> -+#include <pj/file_io.h> -+ -+#if GNUTLS_VERSION_NUMBER < 0x030306 && !defined(_MSC_VER) -+#include <dirent.h> -+#endif -+ -+#include <errno.h> -+ -+/* Only build when PJ_HAS_SSL_SOCK and PJ_HAS_TLS_SOCK are enabled */ -+#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \ -+ defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK != 0 -+ -+#define THIS_FILE "ssl_sock_gtls.c" -+ -+/* Workaround for ticket #985 */ -+#define DELAYED_CLOSE_TIMEOUT 200 -+ -+/* Maximum ciphers */ -+#define MAX_CIPHERS 100 -+ -+/* Standard trust locations */ -+#define TRUST_STORE_FILE1 "/etc/ssl/certs/ca-certificates.crt" -+#define TRUST_STORE_FILE2 "/etc/ssl/certs/ca-bundle.crt" -+ -+/* Debugging output level for GnuTLS only */ -+#define GNUTLS_LOG_LEVEL 0 -+ -+/* GnuTLS includes */ -+#include <gnutls/gnutls.h> -+#include <gnutls/x509.h> -+#include <gnutls/abstract.h> -+ -+#ifdef _MSC_VER -+# pragma comment( lib, "libgnutls") -+#endif -+ -+ -+/* TLS state enumeration. */ -+enum tls_connection_state { -+ TLS_STATE_NULL, -+ TLS_STATE_HANDSHAKING, -+ TLS_STATE_ESTABLISHED -+}; -+ -+/* Internal timer types. */ -+enum timer_id { -+ TIMER_NONE, -+ TIMER_HANDSHAKE_TIMEOUT, -+ TIMER_CLOSE -+}; -+ -+/* Structure of SSL socket read buffer. */ -+typedef struct read_data_t { -+ void *data; -+ pj_size_t len; -+} read_data_t; -+ -+/* -+ * Get the offset of pointer to read-buffer of SSL socket from read-buffer -+ * of active socket. Note that both SSL socket and active socket employ -+ * different but correlated read-buffers (as much as async_cnt for each), -+ * and to make it easier/faster to find corresponding SSL socket's read-buffer -+ * from known active socket's read-buffer, the pointer of corresponding -+ * SSL socket's read-buffer is stored right after the end of active socket's -+ * read-buffer. -+ */ -+#define OFFSET_OF_READ_DATA_PTR(ssock, asock_rbuf) \ -+ (read_data_t**) \ -+ ((pj_int8_t *)(asock_rbuf) + \ -+ ssock->param.read_buffer_size) -+ -+/* Structure of SSL socket write data. */ -+typedef struct write_data_t { -+ PJ_DECL_LIST_MEMBER(struct write_data_t); -+ pj_ioqueue_op_key_t key; -+ pj_size_t record_len; -+ pj_ioqueue_op_key_t *app_key; -+ pj_size_t plain_data_len; -+ pj_size_t data_len; -+ unsigned flags; -+ union { -+ char content[1]; -+ const char *ptr; -+ } data; -+} write_data_t; -+ -+ -+/* Structure of SSL socket write buffer (circular buffer). */ -+typedef struct send_buf_t { -+ char *buf; -+ pj_size_t max_len; -+ char *start; -+ pj_size_t len; -+} send_buf_t; -+ -+ -+/* Circular buffer object */ -+typedef struct circ_buf_t { -+ pj_size_t cap; /* maximum number of elements (must be power of 2) */ -+ pj_size_t readp; /* index of oldest element */ -+ pj_size_t writep; /* index at which to write new element */ -+ pj_size_t size; /* number of elements */ -+ pj_uint8_t *buf; /* data buffer */ -+ pj_pool_t *pool; /* where new allocations will take place */ -+} circ_buf_t; -+ -+ -+/* Secure socket structure definition. */ -+struct pj_ssl_sock_t { -+ pj_pool_t *pool; -+ pj_ssl_sock_t *parent; -+ pj_ssl_sock_param param; -+ pj_ssl_sock_param newsock_param; -+ pj_ssl_cert_t *cert; -+ -+ pj_ssl_cert_info local_cert_info; -+ pj_ssl_cert_info remote_cert_info; -+ -+ pj_bool_t is_server; -+ enum tls_connection_state connection_state; -+ pj_ioqueue_op_key_t handshake_op_key; -+ pj_timer_entry timer; -+ pj_status_t verify_status; -+ -+ int last_err; -+ -+ pj_sock_t sock; -+ pj_activesock_t *asock; -+ -+ pj_sockaddr local_addr; -+ pj_sockaddr rem_addr; -+ int addr_len; -+ -+ pj_bool_t read_started; -+ pj_size_t read_size; -+ pj_uint32_t read_flags; -+ void **asock_rbuf; -+ read_data_t *ssock_rbuf; -+ -+ write_data_t write_pending; /* list of pending writes */ -+ write_data_t write_pending_empty; /* cache for write_pending */ -+ pj_bool_t flushing_write_pend; /* flag of flushing is ongoing */ -+ send_buf_t send_buf; -+ write_data_t send_pending; /* list of pending write to network */ -+ -+ gnutls_session_t session; -+ gnutls_certificate_credentials_t xcred; -+ -+ circ_buf_t circ_buf_input; -+ pj_lock_t *circ_buf_input_mutex; -+ -+ circ_buf_t circ_buf_output; -+ pj_lock_t *circ_buf_output_mutex; -+ -+ int tls_init_count; /* library initialization counter */ -+}; -+ -+ -+/* Certificate/credential structure definition. */ -+struct pj_ssl_cert_t { -+ pj_str_t CA_file; -+ pj_str_t CA_path; -+ pj_str_t cert_file; -+ pj_str_t privkey_file; -+ pj_str_t privkey_pass; -+}; -+ -+/* GnuTLS available ciphers */ -+static unsigned tls_available_ciphers; -+ -+/* Array of id/names for available ciphers */ -+static struct tls_ciphers_t { -+ pj_ssl_cipher id; -+ const char *name; -+} tls_ciphers[MAX_CIPHERS]; -+ -+/* Last error reported somehow */ -+static int tls_last_error; -+ -+ -+/* -+ ******************************************************************* -+ * Circular buffer functions. -+ ******************************************************************* -+ */ -+ -+static pj_status_t circ_init(pj_pool_factory *factory, -+ circ_buf_t *cb, pj_size_t cap) -+{ -+ cb->cap = cap; -+ cb->readp = 0; -+ cb->writep = 0; -+ cb->size = 0; -+ -+ /* Initial pool holding the buffer elements */ -+ cb->pool = pj_pool_create(factory, "tls-circ%p", cap, cap, NULL); -+ if (!cb->pool) -+ return PJ_ENOMEM; -+ -+ /* Allocate circular buffer */ -+ cb->buf = pj_pool_alloc(cb->pool, cap); -+ if (!cb->buf) { -+ pj_pool_release(cb->pool); -+ return PJ_ENOMEM; -+ } -+ -+ return PJ_SUCCESS; -+} -+ -+static void circ_deinit(circ_buf_t *cb) -+{ -+ if (cb->pool) { -+ pj_pool_release(cb->pool); -+ cb->pool = NULL; -+ } -+} -+ -+static pj_bool_t circ_empty(const circ_buf_t *cb) -+{ -+ return cb->size == 0; -+} -+ -+static pj_size_t circ_size(const circ_buf_t *cb) -+{ -+ return cb->size; -+} -+ -+static pj_size_t circ_avail(const circ_buf_t *cb) -+{ -+ return cb->cap - cb->size; -+} -+ -+static void circ_read(circ_buf_t *cb, pj_uint8_t *dst, pj_size_t len) -+{ -+ pj_size_t size_after = cb->cap - cb->readp; -+ pj_size_t tbc = PJ_MIN(size_after, len); -+ pj_size_t rem = len - tbc; -+ -+ pj_memcpy(dst, cb->buf + cb->readp, tbc); -+ pj_memcpy(dst + tbc, cb->buf, rem); -+ -+ cb->readp += len; -+ cb->readp &= (cb->cap - 1); -+ -+ cb->size -= len; -+} -+ -+static pj_status_t circ_write(circ_buf_t *cb, -+ const pj_uint8_t *src, pj_size_t len) -+{ -+ /* Overflow condition: resize */ -+ if (len > circ_avail(cb)) { -+ /* Minimum required capacity */ -+ pj_size_t min_cap = len + cb->size; -+ -+ /* Next 32-bit power of two */ -+ min_cap--; -+ min_cap |= min_cap >> 1; -+ min_cap |= min_cap >> 2; -+ min_cap |= min_cap >> 4; -+ min_cap |= min_cap >> 8; -+ min_cap |= min_cap >> 16; -+ min_cap++; -+ -+ /* Create a new pool to hold a bigger buffer, using the same factory */ -+ pj_pool_t *pool = pj_pool_create(cb->pool->factory, "tls-circ%p", -+ min_cap, min_cap, NULL); -+ if (!pool) -+ return PJ_ENOMEM; -+ -+ /* Allocate our new buffer */ -+ pj_uint8_t *buf = pj_pool_alloc(pool, min_cap); -+ if (!buf) { -+ pj_pool_release(pool); -+ return PJ_ENOMEM; -+ } -+ -+ /* Save old size, which we shall restore after the next read */ -+ pj_size_t old_size = cb->size; -+ -+ /* Copy old data into beginning of new buffer */ -+ circ_read(cb, buf, cb->size); -+ -+ /* Restore old size now */ -+ cb->size = old_size; -+ -+ /* Release the previous pool */ -+ pj_pool_release(cb->pool); -+ -+ /* Update circular buffer members */ -+ cb->pool = pool; -+ cb->buf = buf; -+ cb->readp = 0; -+ cb->writep = cb->size; -+ cb->cap = min_cap; -+ } -+ -+ pj_size_t size_after = cb->cap - cb->writep; -+ pj_size_t tbc = PJ_MIN(size_after, len); -+ pj_size_t rem = len - tbc; -+ -+ pj_memcpy(cb->buf + cb->writep, src, tbc); -+ pj_memcpy(cb->buf, src + tbc, rem); -+ -+ cb->writep += len; -+ cb->writep &= (cb->cap - 1); -+ -+ cb->size += len; -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* -+ ******************************************************************* -+ * Static/internal functions. -+ ******************************************************************* -+ */ -+ -+/* Convert from GnuTLS error to pj_status_t. */ -+static pj_status_t tls_status_from_err(pj_ssl_sock_t *ssock, int err) -+{ -+ pj_status_t status; -+ -+ switch (err) { -+ case GNUTLS_E_SUCCESS: -+ status = PJ_SUCCESS; -+ break; -+ case GNUTLS_E_MEMORY_ERROR: -+ status = PJ_ENOMEM; -+ break; -+ case GNUTLS_E_LARGE_PACKET: -+ status = PJ_ETOOBIG; -+ break; -+ case GNUTLS_E_NO_CERTIFICATE_FOUND: -+ status = PJ_ENOTFOUND; -+ break; -+ case GNUTLS_E_SESSION_EOF: -+ status = PJ_EEOF; -+ break; -+ case GNUTLS_E_HANDSHAKE_TOO_LARGE: -+ status = PJ_ETOOBIG; -+ break; -+ case GNUTLS_E_EXPIRED: -+ status = PJ_EGONE; -+ break; -+ case GNUTLS_E_TIMEDOUT: -+ status = PJ_ETIMEDOUT; -+ break; -+ case GNUTLS_E_PREMATURE_TERMINATION: -+ status = PJ_ECANCELLED; -+ break; -+ case GNUTLS_E_INTERNAL_ERROR: -+ case GNUTLS_E_UNIMPLEMENTED_FEATURE: -+ status = PJ_EBUG; -+ break; -+ case GNUTLS_E_AGAIN: -+ case GNUTLS_E_INTERRUPTED: -+ case GNUTLS_E_REHANDSHAKE: -+ status = PJ_EPENDING; -+ break; -+ case GNUTLS_E_TOO_MANY_EMPTY_PACKETS: -+ case GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS: -+ case GNUTLS_E_RECORD_LIMIT_REACHED: -+ status = PJ_ETOOMANY; -+ break; -+ case GNUTLS_E_UNSUPPORTED_VERSION_PACKET: -+ case GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM: -+ case GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE: -+ case GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE: -+ case GNUTLS_E_X509_UNSUPPORTED_EXTENSION: -+ case GNUTLS_E_X509_UNSUPPORTED_CRITICAL_EXTENSION: -+ status = PJ_ENOTSUP; -+ break; -+ case GNUTLS_E_INVALID_SESSION: -+ case GNUTLS_E_INVALID_REQUEST: -+ case GNUTLS_E_INVALID_PASSWORD: -+ case GNUTLS_E_ILLEGAL_PARAMETER: -+ case GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION: -+ case GNUTLS_E_UNEXPECTED_PACKET: -+ case GNUTLS_E_UNEXPECTED_PACKET_LENGTH: -+ case GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET: -+ case GNUTLS_E_UNWANTED_ALGORITHM: -+ case GNUTLS_E_USER_ERROR: -+ status = PJ_EINVAL; -+ break; -+ default: -+ status = PJ_EUNKNOWN; -+ break; -+ } -+ -+ /* Not thread safe */ -+ tls_last_error = err; -+ if (ssock) -+ ssock->last_err = err; -+ return status; -+} -+ -+ -+/* Get error string from GnuTLS using tls_last_error */ -+static pj_str_t tls_strerror(pj_status_t status, -+ char *buf, pj_size_t bufsize) -+{ -+ pj_str_t errstr; -+ const char *tmp = gnutls_strerror(tls_last_error); -+ -+#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) -+ if (tmp) { -+ pj_ansi_strncpy(buf, tmp, bufsize); -+ errstr = pj_str(buf); -+ return errstr; -+ } -+#endif /* PJ_HAS_ERROR_STRING */ -+ -+ errstr.ptr = buf; -+ errstr.slen = pj_ansi_snprintf(buf, bufsize, "GnuTLS error %d: %s", -+ tls_last_error, tmp); -+ if (errstr.slen < 1 || errstr.slen >= (int) bufsize) -+ errstr.slen = bufsize - 1; -+ -+ return errstr; -+} -+ -+ -+/* GnuTLS way of reporting internal operations. */ -+static void tls_print_logs(int level, const char* msg) -+{ -+ PJ_LOG(3, (THIS_FILE, "GnuTLS [%d]: %s", level, msg)); -+} -+ -+ -+/* Initialize GnuTLS. */ -+static pj_status_t tls_init(void) -+{ -+ /* Register error subsystem */ -+ pj_status_t status = pj_register_strerror(PJ_ERRNO_START_USER + -+ PJ_ERRNO_SPACE_SIZE * 6, -+ PJ_ERRNO_SPACE_SIZE, -+ &tls_strerror); -+ pj_assert(status == PJ_SUCCESS); -+ -+ /* Init GnuTLS library */ -+ int ret = gnutls_global_init(); -+ if (ret < 0) -+ return tls_status_from_err(NULL, ret); -+ -+ gnutls_global_set_log_level(GNUTLS_LOG_LEVEL); -+ gnutls_global_set_log_function(tls_print_logs); -+ -+ /* Init available ciphers */ -+ if (!tls_available_ciphers) { -+ unsigned int i; -+ -+ for (i = 0; ; i++) { -+ unsigned char id[2]; -+ const char *suite = gnutls_cipher_suite_info(i, (unsigned char *)id, -+ NULL, NULL, NULL, NULL); -+ tls_ciphers[i].id = 0; -+ /* usually the array size is bigger than the number of available -+ * ciphers anyway, so by checking here we can exit the loop as soon -+ * as either all ciphers have been added or the array is full */ -+ if (suite && i < PJ_ARRAY_SIZE(tls_ciphers)) { -+ tls_ciphers[i].id = (pj_ssl_cipher) -+ (pj_uint32_t) ((id[0] << 8) | id[1]); -+ tls_ciphers[i].name = suite; -+ } else -+ break; -+ } -+ -+ tls_available_ciphers = i; -+ } -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* Shutdown GnuTLS */ -+static void tls_deinit(void) -+{ -+ gnutls_global_deinit(); -+} -+ -+ -+/* Callback invoked every time a certificate has to be validated. */ -+static int tls_cert_verify_cb(gnutls_session_t session) -+{ -+ pj_ssl_sock_t *ssock; -+ unsigned int status; -+ int ret; -+ -+ /* Get SSL socket instance */ -+ ssock = (pj_ssl_sock_t *)gnutls_session_get_ptr(session); -+ pj_assert(ssock); -+ -+ /* Support only x509 format */ -+ ret = gnutls_certificate_type_get(session) != GNUTLS_CRT_X509; -+ if (ret < 0) { -+ ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT; -+ return GNUTLS_E_CERTIFICATE_ERROR; -+ } -+ -+ /* Store verification status */ -+ ret = gnutls_certificate_verify_peers2(session, &status); -+ if (ret < 0) { -+ ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN; -+ return GNUTLS_E_CERTIFICATE_ERROR; -+ } -+ if (ssock->param.verify_peer) { -+ if (status & GNUTLS_CERT_INVALID) { -+ if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) -+ ssock->verify_status |= PJ_SSL_CERT_EISSUER_NOT_FOUND; -+ else if (status & GNUTLS_CERT_EXPIRED || -+ status & GNUTLS_CERT_NOT_ACTIVATED) -+ ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD; -+ else if (status & GNUTLS_CERT_SIGNER_NOT_CA || -+ status & GNUTLS_CERT_INSECURE_ALGORITHM) -+ ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED; -+ else if (status & GNUTLS_CERT_UNEXPECTED_OWNER || -+ status & GNUTLS_CERT_MISMATCH) -+ ssock->verify_status |= PJ_SSL_CERT_EISSUER_MISMATCH; -+ else if (status & GNUTLS_CERT_REVOKED) -+ ssock->verify_status |= PJ_SSL_CERT_EREVOKED; -+ else -+ ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN; -+ -+ return GNUTLS_E_CERTIFICATE_ERROR; -+ } -+ -+ /* When verification is not requested just return ok here, however -+ * applications can still get the verification status. */ -+ gnutls_x509_crt_t cert; -+ unsigned int cert_list_size; -+ const gnutls_datum_t *cert_list; -+ int ret; -+ -+ ret = gnutls_x509_crt_init(&cert); -+ if (ret < 0) -+ goto out; -+ -+ cert_list = gnutls_certificate_get_peers(session, &cert_list_size); -+ if (cert_list == NULL) { -+ ret = GNUTLS_E_NO_CERTIFICATE_FOUND; -+ goto out; -+ } -+ -+ /* TODO: verify whole chain perhaps? */ -+ ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER); -+ if (ret < 0) -+ ret = gnutls_x509_crt_import(cert, &cert_list[0], -+ GNUTLS_X509_FMT_PEM); -+ if (ret < 0) { -+ ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT; -+ goto out; -+ } -+ ret = gnutls_x509_crt_check_hostname(cert, ssock->param.server_name.ptr); -+ if (ret < 0) -+ goto out; -+ -+ gnutls_x509_crt_deinit(cert); -+ -+ /* notify GnuTLS to continue handshake normally */ -+ return GNUTLS_E_SUCCESS; -+ -+out: -+ tls_last_error = ret; -+ ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN; -+ return GNUTLS_E_CERTIFICATE_ERROR; -+ } -+ -+ return GNUTLS_E_SUCCESS; -+} -+ -+ -+/* gnutls_handshake() and gnutls_record_send() will call this function to -+ * send/write (encrypted) data */ -+static ssize_t tls_data_push(gnutls_transport_ptr_t ptr, -+ const void *data, size_t len) -+{ -+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)ptr; -+ -+ pj_lock_acquire(ssock->circ_buf_output_mutex); -+ if (circ_write(&ssock->circ_buf_output, data, len) != PJ_SUCCESS) { -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ -+ gnutls_transport_set_errno(ssock->session, ENOMEM); -+ return -1; -+ } -+ -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ -+ return len; -+} -+ -+ -+/* gnutls_handshake() and gnutls_record_recv() will call this function to -+ * receive/read (encrypted) data */ -+static ssize_t tls_data_pull(gnutls_transport_ptr_t ptr, -+ void *data, pj_size_t len) -+{ -+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)ptr; -+ -+ pj_lock_acquire(ssock->circ_buf_input_mutex); -+ -+ if (circ_empty(&ssock->circ_buf_input)) { -+ pj_lock_release(ssock->circ_buf_input_mutex); -+ -+ /* Data buffers not yet filled */ -+ gnutls_transport_set_errno(ssock->session, EAGAIN); -+ return -1; -+ } -+ -+ pj_size_t circ_buf_size = circ_size(&ssock->circ_buf_input); -+ pj_size_t read_size = PJ_MIN(circ_buf_size, len); -+ -+ circ_read(&ssock->circ_buf_input, data, read_size); -+ -+ pj_lock_release(ssock->circ_buf_input_mutex); -+ -+ return read_size; -+} -+ -+ -+/* Append a string to the priority string, only once. */ -+static pj_status_t tls_str_append_once(pj_str_t *dst, pj_str_t *src) -+{ -+ if (pj_strstr(dst, src) == NULL) { -+ /* Check buffer size */ -+ if (dst->slen + src->slen + 3 > 1024) -+ return PJ_ETOOMANY; -+ -+ pj_strcat2(dst, ":+"); -+ pj_strcat(dst, src); -+ } -+ return PJ_SUCCESS; -+} -+ -+ -+/* Generate priority string with user preference order. */ -+static pj_status_t tls_priorities_set(pj_ssl_sock_t *ssock) -+{ -+ char buf[1024]; -+ char priority_buf[256]; -+ pj_str_t cipher_list; -+ pj_str_t compression = pj_str("COMP-NULL"); -+ pj_str_t server = pj_str(":%SERVER_PRECEDENCE"); -+ int i, j, ret; -+ pj_str_t priority; -+ const char *err; -+ -+ pj_strset(&cipher_list, buf, 0); -+ pj_strset(&priority, priority_buf, 0); -+ -+ /* For each level, enable only the requested protocol */ -+ pj_strcat2(&priority, "NORMAL:"); -+ if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2) { -+ pj_strcat2(&priority, "+VERS-TLS1.2:"); -+ } -+ if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_1) { -+ pj_strcat2(&priority, "+VERS-TLS1.1:"); -+ } -+ if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1) { -+ pj_strcat2(&priority, "+VERS-TLS1.0:"); -+ } -+ pj_strcat2(&priority, "-VERS-SSL3.0:"); -+ pj_strcat2(&priority, "%LATEST_RECORD_VERSION"); -+ -+ pj_strcat(&cipher_list, &priority); -+ for (i = 0; i < ssock->param.ciphers_num; i++) { -+ for (j = 0; ; j++) { -+ pj_ssl_cipher c; -+ const char *suite; -+ unsigned char id[2]; -+ gnutls_protocol_t proto; -+ gnutls_kx_algorithm_t kx; -+ gnutls_mac_algorithm_t mac; -+ gnutls_cipher_algorithm_t algo; -+ -+ suite = gnutls_cipher_suite_info(j, (unsigned char *)id, -+ &kx, &algo, &mac, &proto); -+ if (!suite) -+ break; -+ -+ c = (pj_ssl_cipher) (pj_uint32_t) ((id[0] << 8) | id[1]); -+ if (ssock->param.ciphers[i] == c) { -+ char temp[256]; -+ pj_str_t cipher_entry; -+ -+ /* Protocol version */ -+ pj_strset(&cipher_entry, temp, 0); -+ pj_strcat2(&cipher_entry, "VERS-"); -+ pj_strcat2(&cipher_entry, gnutls_protocol_get_name(proto)); -+ ret = tls_str_append_once(&cipher_list, &cipher_entry); -+ if (ret != PJ_SUCCESS) -+ return ret; -+ -+ /* Cipher */ -+ pj_strset(&cipher_entry, temp, 0); -+ pj_strcat2(&cipher_entry, gnutls_cipher_get_name(algo)); -+ ret = tls_str_append_once(&cipher_list, &cipher_entry); -+ if (ret != PJ_SUCCESS) -+ return ret; -+ -+ /* Mac */ -+ pj_strset(&cipher_entry, temp, 0); -+ pj_strcat2(&cipher_entry, gnutls_mac_get_name(mac)); -+ ret = tls_str_append_once(&cipher_list, &cipher_entry); -+ if (ret != PJ_SUCCESS) -+ return ret; -+ -+ /* Key exchange */ -+ pj_strset(&cipher_entry, temp, 0); -+ pj_strcat2(&cipher_entry, gnutls_kx_get_name(kx)); -+ ret = tls_str_append_once(&cipher_list, &cipher_entry); -+ if (ret != PJ_SUCCESS) -+ return ret; -+ -+ /* Compression is always disabled */ -+ /* Signature is level-default */ -+ break; -+ } -+ } -+ } -+ -+ /* Disable compression, it's a TLS-only extension after all */ -+ tls_str_append_once(&cipher_list, &compression); -+ -+ /* Server will be the one deciding which crypto to use */ -+ if (ssock->is_server) { -+ if (cipher_list.slen + server.slen + 1 > sizeof(buf)) -+ return PJ_ETOOMANY; -+ else -+ pj_strcat(&cipher_list, &server); -+ } -+ -+ /* End the string and print it */ -+ cipher_list.ptr[cipher_list.slen] = '\0'; -+ PJ_LOG(5, (ssock->pool->obj_name, "Priority string: %s", cipher_list.ptr)); -+ -+ /* Set our priority string */ -+ ret = gnutls_priority_set_direct(ssock->session, -+ cipher_list.ptr, &err); -+ if (ret < 0) { -+ tls_last_error = GNUTLS_E_INVALID_REQUEST; -+ return PJ_EINVAL; -+ } -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* Load root CA file or load the installed ones. */ -+static pj_status_t tls_trust_set(pj_ssl_sock_t *ssock) -+{ -+ int ntrusts = 0; -+ int err; -+ -+ err = gnutls_certificate_set_x509_system_trust(ssock->xcred); -+ if (err > 0) -+ ntrusts += err; -+ err = gnutls_certificate_set_x509_trust_file(ssock->xcred, -+ TRUST_STORE_FILE1, -+ GNUTLS_X509_FMT_PEM); -+ if (err > 0) -+ ntrusts += err; -+ -+ err = gnutls_certificate_set_x509_trust_file(ssock->xcred, -+ TRUST_STORE_FILE2, -+ GNUTLS_X509_FMT_PEM); -+ if (err > 0) -+ ntrusts += err; -+ -+ if (ntrusts > 0) -+ return PJ_SUCCESS; -+ else if (!ntrusts) -+ return PJ_ENOTFOUND; -+ else -+ return PJ_EINVAL; -+} -+ -+#if GNUTLS_VERSION_NUMBER < 0x030306 -+ -+#ifdef _POSIX_PATH_MAX -+# define GNUTLS_PATH_MAX _POSIX_PATH_MAX -+#else -+# define GNUTLS_PATH_MAX 256 -+#endif -+ -+static -+int gnutls_certificate_set_x509_trust_dir(gnutls_certificate_credentials_t cred, const char *dirname, unsigned type) -+{ -+ DIR *dirp; -+ struct dirent *d; -+ int ret; -+ int r = 0; -+ char path[GNUTLS_PATH_MAX]; -+#ifndef _WIN32 -+ struct dirent e; -+#endif -+ -+ dirp = opendir(dirname); -+ if (dirp != NULL) { -+ do { -+#ifdef _WIN32 -+ d = readdir(dirp); -+ if (d != NULL) { -+#else -+ ret = readdir_r(dirp, &e, &d); -+ if (ret == 0 && d != NULL -+#ifdef _DIRENT_HAVE_D_TYPE -+ && (d->d_type == DT_REG || d->d_type == DT_LNK || d->d_type == DT_UNKNOWN) -+#endif -+ ) { -+#endif -+ snprintf(path, sizeof(path), "%s/%s", -+ dirname, d->d_name); -+ -+ ret = gnutls_certificate_set_x509_trust_file(cred, path, type); -+ if (ret >= 0) -+ r += ret; -+ } -+ } -+ while (d != NULL); -+ closedir(dirp); -+ } -+ -+ return r; -+} -+ -+#endif -+ -+/* Create and initialize new GnuTLS context and instance */ -+static pj_status_t tls_open(pj_ssl_sock_t *ssock) -+{ -+ pj_ssl_cert_t *cert; -+ pj_status_t status; -+ int ret; -+ -+ pj_assert(ssock); -+ -+ cert = ssock->cert; -+ -+ /* Even if reopening is harmless, having one instance only simplifies -+ * deallocating it later on */ -+ if (!ssock->tls_init_count) { -+ ssock->tls_init_count++; -+ ret = tls_init(); -+ if (ret < 0) -+ return ret; -+ } else -+ return PJ_SUCCESS; -+ -+ /* Start this socket session */ -+ ret = gnutls_init(&ssock->session, ssock->is_server ? GNUTLS_SERVER -+ : GNUTLS_CLIENT); -+ if (ret < 0) -+ goto out; -+ -+ /* Set the ssock object to be retrieved by transport (send/recv) and by -+ * user data from this session */ -+ gnutls_transport_set_ptr(ssock->session, -+ (gnutls_transport_ptr_t) (uintptr_t) ssock); -+ gnutls_session_set_ptr(ssock->session, -+ (gnutls_transport_ptr_t) (uintptr_t) ssock); -+ -+ /* Initialize input circular buffer */ -+ status = circ_init(ssock->pool->factory, &ssock->circ_buf_input, 512); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ /* Initialize output circular buffer */ -+ status = circ_init(ssock->pool->factory, &ssock->circ_buf_output, 512); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ /* Set the callback that allows GnuTLS to PUSH and PULL data -+ * TO and FROM the transport layer */ -+ gnutls_transport_set_push_function(ssock->session, tls_data_push); -+ gnutls_transport_set_pull_function(ssock->session, tls_data_pull); -+ -+ /* Determine which cipher suite to support */ -+ status = tls_priorities_set(ssock); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ /* Allocate credentials for handshaking and transmission */ -+ ret = gnutls_certificate_allocate_credentials(&ssock->xcred); -+ if (ret < 0) -+ goto out; -+ gnutls_certificate_set_verify_function(ssock->xcred, tls_cert_verify_cb); -+ -+ /* Load system trust file(s) */ -+ status = tls_trust_set(ssock); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ /* Load user-provided CA, certificate and key if available */ -+ if (cert) { -+ /* Load CA if one is specified. */ -+ if (cert->CA_file.slen) { -+ ret = gnutls_certificate_set_x509_trust_file(ssock->xcred, -+ cert->CA_file.ptr, -+ GNUTLS_X509_FMT_PEM); -+ if (ret < 0) -+ ret = gnutls_certificate_set_x509_trust_file(ssock->xcred, -+ cert->CA_file.ptr, -+ GNUTLS_X509_FMT_DER); -+ if (ret < 0) -+ goto out; -+ } -+ if (cert->CA_path.slen) { -+ ret = gnutls_certificate_set_x509_trust_dir(ssock->xcred, -+ cert->CA_path.ptr, -+ GNUTLS_X509_FMT_PEM); -+ if (ret < 0) -+ ret = gnutls_certificate_set_x509_trust_dir(ssock->xcred, -+ cert->CA_path.ptr, -+ GNUTLS_X509_FMT_DER); -+ if (ret < 0) -+ goto out; -+ } -+ -+ /* Load certificate, key and pass if one is specified */ -+ if (cert->cert_file.slen && cert->privkey_file.slen) { -+ const char *prikey_file = cert->privkey_file.ptr; -+ const char *prikey_pass = cert->privkey_pass.slen -+ ? cert->privkey_pass.ptr -+ : NULL; -+ ret = gnutls_certificate_set_x509_key_file2(ssock->xcred, -+ cert->cert_file.ptr, -+ prikey_file, -+ GNUTLS_X509_FMT_PEM, -+ prikey_pass, -+ 0); -+ if (ret != GNUTLS_E_SUCCESS) -+ ret = gnutls_certificate_set_x509_key_file2(ssock->xcred, -+ cert->cert_file.ptr, -+ prikey_file, -+ GNUTLS_X509_FMT_DER, -+ prikey_pass, -+ 0); -+ if (ret < 0) -+ goto out; -+ } -+ } -+ -+ /* Require client certificate if asked */ -+ if (ssock->is_server && ssock->param.require_client_cert) -+ gnutls_certificate_server_set_request(ssock->session, -+ GNUTLS_CERT_REQUIRE); -+ -+ /* Finally set credentials for this session */ -+ ret = gnutls_credentials_set(ssock->session, -+ GNUTLS_CRD_CERTIFICATE, ssock->xcred); -+ if (ret < 0) -+ goto out; -+ -+ ret = GNUTLS_E_SUCCESS; -+out: -+ return tls_status_from_err(ssock, ret); -+} -+ -+ -+/* Destroy GnuTLS credentials and session. */ -+static void tls_close(pj_ssl_sock_t *ssock) -+{ -+ if (ssock->session) { -+ gnutls_bye(ssock->session, GNUTLS_SHUT_RDWR); -+ gnutls_deinit(ssock->session); -+ ssock->session = NULL; -+ } -+ -+ if (ssock->xcred) { -+ gnutls_certificate_free_credentials(ssock->xcred); -+ ssock->xcred = NULL; -+ } -+ -+ /* Free GnuTLS library */ -+ if (ssock->tls_init_count) { -+ ssock->tls_init_count--; -+ tls_deinit(); -+ } -+ -+ /* Destroy circular buffers */ -+ circ_deinit(&ssock->circ_buf_input); -+ circ_deinit(&ssock->circ_buf_output); -+} -+ -+ -+/* Reset socket state. */ -+static void tls_sock_reset(pj_ssl_sock_t *ssock) -+{ -+ ssock->connection_state = TLS_STATE_NULL; -+ -+ tls_close(ssock); -+ -+ if (ssock->asock) { -+ pj_activesock_close(ssock->asock); -+ ssock->asock = NULL; -+ ssock->sock = PJ_INVALID_SOCKET; -+ } -+ if (ssock->sock != PJ_INVALID_SOCKET) { -+ pj_sock_close(ssock->sock); -+ ssock->sock = PJ_INVALID_SOCKET; -+ } -+ -+ ssock->last_err = tls_last_error = GNUTLS_E_SUCCESS; -+} -+ -+ -+/* Get Common Name field string from a general name string */ -+static void tls_cert_get_cn(const pj_str_t *gen_name, pj_str_t *cn) -+{ -+ pj_str_t CN_sign = {"CN=", 3}; -+ char *p, *q; -+ -+ pj_bzero(cn, sizeof(cn)); -+ -+ p = pj_strstr(gen_name, &CN_sign); -+ if (!p) -+ return; -+ -+ p += 3; /* shift pointer to value part */ -+ pj_strset(cn, p, gen_name->slen - (p - gen_name->ptr)); -+ q = pj_strchr(cn, ','); -+ if (q) -+ cn->slen = q - p; -+} -+ -+ -+/* Get certificate info; in case the certificate info is already populated, -+ * this function will check if the contents need updating by inspecting the -+ * issuer and the serial number. */ -+static void tls_cert_get_info(pj_pool_t *pool, pj_ssl_cert_info *ci, gnutls_x509_crt_t cert) -+{ -+ pj_bool_t update_needed; -+ char buf[512] = { 0 }; -+ size_t bufsize = sizeof(buf); -+ pj_uint8_t serial_no[64] = { 0 }; /* should be >= sizeof(ci->serial_no) */ -+ size_t serialsize = sizeof(serial_no); -+ size_t len = sizeof(buf); -+ int i, ret, seq = 0; -+ pj_ssl_cert_name_type type; -+ -+ pj_assert(pool && ci && cert); -+ -+ /* Get issuer */ -+ gnutls_x509_crt_get_issuer_dn(cert, buf, &bufsize); -+ -+ /* Get serial no */ -+ gnutls_x509_crt_get_serial(cert, serial_no, &serialsize); -+ -+ /* Check if the contents need to be updated */ -+ update_needed = pj_strcmp2(&ci->issuer.info, buf) || -+ pj_memcmp(ci->serial_no, serial_no, serialsize); -+ if (!update_needed) -+ return; -+ -+ /* Update cert info */ -+ -+ pj_bzero(ci, sizeof(pj_ssl_cert_info)); -+ -+ /* Version */ -+ ci->version = gnutls_x509_crt_get_version(cert); -+ -+ /* Issuer */ -+ pj_strdup2(pool, &ci->issuer.info, buf); -+ tls_cert_get_cn(&ci->issuer.info, &ci->issuer.cn); -+ -+ /* Serial number */ -+ pj_memcpy(ci->serial_no, serial_no, sizeof(ci->serial_no)); -+ -+ /* Subject */ -+ bufsize = sizeof(buf); -+ gnutls_x509_crt_get_dn(cert, buf, &bufsize); -+ pj_strdup2(pool, &ci->subject.info, buf); -+ tls_cert_get_cn(&ci->subject.info, &ci->subject.cn); -+ -+ /* Validity */ -+ ci->validity.end.sec = gnutls_x509_crt_get_expiration_time(cert); -+ ci->validity.start.sec = gnutls_x509_crt_get_activation_time(cert); -+ ci->validity.gmt = 0; -+ -+ /* Subject Alternative Name extension */ -+ if (ci->version >= 3) { -+ char out[256] = { 0 }; -+ /* Get the number of all alternate names so that we can allocate -+ * the correct number of bytes in subj_alt_name */ -+ while (gnutls_x509_crt_get_subject_alt_name(cert, seq, out, &len, -+ NULL) != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) -+ seq++; -+ -+ ci->subj_alt_name.entry = pj_pool_calloc(pool, seq, -+ sizeof(*ci->subj_alt_name.entry)); -+ if (!ci->subj_alt_name.entry) { -+ tls_last_error = GNUTLS_E_MEMORY_ERROR; -+ return; -+ } -+ -+ /* Now populate the alternative names */ -+ for (i = 0; i < seq; i++) { -+ len = sizeof(out) - 1; -+ ret = gnutls_x509_crt_get_subject_alt_name(cert, i, out, &len, NULL); -+ switch (ret) { -+ case GNUTLS_SAN_IPADDRESS: -+ type = PJ_SSL_CERT_NAME_IP; -+ pj_inet_ntop2(len == sizeof(pj_in6_addr) ? pj_AF_INET6() -+ : pj_AF_INET(), -+ out, buf, sizeof(buf)); -+ break; -+ case GNUTLS_SAN_URI: -+ type = PJ_SSL_CERT_NAME_URI; -+ break; -+ case GNUTLS_SAN_RFC822NAME: -+ type = PJ_SSL_CERT_NAME_RFC822; -+ break; -+ case GNUTLS_SAN_DNSNAME: -+ type = PJ_SSL_CERT_NAME_DNS; -+ break; -+ default: -+ type = PJ_SSL_CERT_NAME_UNKNOWN; -+ break; -+ } -+ -+ if (len && type != PJ_SSL_CERT_NAME_UNKNOWN) { -+ ci->subj_alt_name.entry[ci->subj_alt_name.cnt].type = type; -+ pj_strdup2(pool, -+ &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name, -+ type == PJ_SSL_CERT_NAME_IP ? buf : out); -+ ci->subj_alt_name.cnt++; -+ } -+ } -+ /* TODO: if no DNS alt. names were found, we could check against -+ * the commonName as per RFC3280. */ -+ } -+} -+ -+static void tls_cert_get_chain_raw(pj_pool_t *pool, pj_ssl_cert_info *ci, const gnutls_datum_t *certs, size_t certs_num) -+{ -+ size_t i=0; -+ ci->raw_chain.cert_raw = pj_pool_calloc(pool, certs_num, sizeof(*ci->raw_chain.cert_raw)); -+ ci->raw_chain.cnt = certs_num; -+ for (i=0; i < certs_num; ++i) { -+ const pj_str_t crt_raw = {(const char*)certs[i].data, (pj_ssize_t)certs[i].size}; -+ pj_strdup(pool, ci->raw_chain.cert_raw+i, &crt_raw); -+ } -+} -+ -+/* Update local & remote certificates info. This function should be -+ * called after handshake or renegotiation successfully completed. */ -+static void tls_cert_update(pj_ssl_sock_t *ssock) -+{ -+ gnutls_x509_crt_t cert = NULL; -+ const gnutls_datum_t *us; -+ const gnutls_datum_t *certs; -+ unsigned int certslen = 0; -+ int ret = GNUTLS_CERT_INVALID; -+ -+ pj_assert(ssock->connection_state == TLS_STATE_ESTABLISHED); -+ -+ /* Get active local certificate */ -+ us = gnutls_certificate_get_ours(ssock->session); -+ if (!us) -+ goto us_out; -+ -+ ret = gnutls_x509_crt_init(&cert); -+ if (ret < 0) -+ goto us_out; -+ ret = gnutls_x509_crt_import(cert, us, GNUTLS_X509_FMT_DER); -+ if (ret < 0) -+ ret = gnutls_x509_crt_import(cert, us, GNUTLS_X509_FMT_PEM); -+ if (ret < 0) -+ goto us_out; -+ -+ tls_cert_get_info(ssock->pool, &ssock->local_cert_info, cert); -+ tls_cert_get_chain_raw(ssock->pool, &ssock->local_cert_info, us, 1); -+ -+us_out: -+ tls_last_error = ret; -+ if (cert) -+ gnutls_x509_crt_deinit(cert); -+ else -+ pj_bzero(&ssock->local_cert_info, sizeof(pj_ssl_cert_info)); -+ -+ cert = NULL; -+ -+ /* Get active remote certificate */ -+ certs = gnutls_certificate_get_peers(ssock->session, &certslen); -+ if (certs == NULL || certslen == 0) -+ goto peer_out; -+ -+ ret = gnutls_x509_crt_init(&cert); -+ if (ret < 0) -+ goto peer_out; -+ -+ ret = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_PEM); -+ if (ret < 0) -+ ret = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_DER); -+ if (ret < 0) -+ goto peer_out; -+ -+ tls_cert_get_info(ssock->pool, &ssock->remote_cert_info, cert); -+ tls_cert_get_chain_raw(ssock->pool, &ssock->remote_cert_info, certs, certslen); -+ -+peer_out: -+ tls_last_error = ret; -+ if (cert) -+ gnutls_x509_crt_deinit(cert); -+ else -+ pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info)); -+} -+ -+ -+/* When handshake completed: -+ * - notify application -+ * - if handshake failed, reset SSL state -+ * - return PJ_FALSE when SSL socket instance is destroyed by application. */ -+static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock, -+ pj_status_t status) -+{ -+ pj_bool_t ret = PJ_TRUE; -+ -+ /* Cancel handshake timer */ -+ if (ssock->timer.id == TIMER_HANDSHAKE_TIMEOUT) { -+ pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer); -+ ssock->timer.id = TIMER_NONE; -+ } -+ -+ /* Update certificates info on successful handshake */ -+ if (status == PJ_SUCCESS) -+ tls_cert_update(ssock); -+ -+ /* Accepting */ -+ if (ssock->is_server) { -+ if (status != PJ_SUCCESS) { -+ /* Handshake failed in accepting, destroy our self silently. */ -+ -+ char errmsg[PJ_ERR_MSG_SIZE]; -+ char buf[PJ_INET6_ADDRSTRLEN + 10]; -+ -+ pj_strerror(status, errmsg, sizeof(errmsg)); -+ PJ_LOG(3, (ssock->pool->obj_name, -+ "Handshake failed in accepting %s: %s", -+ pj_sockaddr_print(&ssock->rem_addr, buf, sizeof(buf), 3), -+ errmsg)); -+ -+ /* Workaround for ticket #985 */ -+#if (defined(PJ_WIN32) && PJ_WIN32 != 0) || (defined(PJ_WIN64) && PJ_WIN64 != 0) -+ if (ssock->param.timer_heap) { -+ pj_time_val interval = {0, DELAYED_CLOSE_TIMEOUT}; -+ -+ tls_sock_reset(ssock); -+ -+ ssock->timer.id = TIMER_CLOSE; -+ pj_time_val_normalize(&interval); -+ if (pj_timer_heap_schedule(ssock->param.timer_heap, -+ &ssock->timer, &interval) != 0) -+ { -+ ssock->timer.id = TIMER_NONE; -+ pj_ssl_sock_close(ssock); -+ } -+ } else -+#endif /* PJ_WIN32 */ -+ { -+ pj_ssl_sock_close(ssock); -+ } -+ -+ return PJ_FALSE; -+ } -+ /* Notify application the newly accepted SSL socket */ -+ if (ssock->param.cb.on_accept_complete) -+ ret = (*ssock->param.cb.on_accept_complete) -+ (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr, -+ pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr)); -+ -+ } else { /* Connecting */ -+ /* On failure, reset SSL socket state first, as app may try to -+ * reconnect in the callback. */ -+ if (status != PJ_SUCCESS) { -+ /* Server disconnected us, possibly due to negotiation failure */ -+ tls_sock_reset(ssock); -+ } -+ if (ssock->param.cb.on_connect_complete) { -+ -+ ret = (*ssock->param.cb.on_connect_complete)(ssock, status); -+ } -+ } -+ -+ return ret; -+} -+ -+static write_data_t *alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len) -+{ -+ send_buf_t *send_buf = &ssock->send_buf; -+ pj_size_t avail_len, skipped_len = 0; -+ char *reg1, *reg2; -+ pj_size_t reg1_len, reg2_len; -+ write_data_t *p; -+ -+ /* Check buffer availability */ -+ avail_len = send_buf->max_len - send_buf->len; -+ if (avail_len < len) -+ return NULL; -+ -+ /* If buffer empty, reset start pointer and return it */ -+ if (send_buf->len == 0) { -+ send_buf->start = send_buf->buf; -+ send_buf->len = len; -+ p = (write_data_t*)send_buf->start; -+ goto init_send_data; -+ } -+ -+ /* Free space may be wrapped/splitted into two regions, so let's -+ * analyze them if any region can hold the write data. */ -+ reg1 = send_buf->start + send_buf->len; -+ if (reg1 >= send_buf->buf + send_buf->max_len) -+ reg1 -= send_buf->max_len; -+ reg1_len = send_buf->max_len - send_buf->len; -+ if (reg1 + reg1_len > send_buf->buf + send_buf->max_len) { -+ reg1_len = send_buf->buf + send_buf->max_len - reg1; -+ reg2 = send_buf->buf; -+ reg2_len = send_buf->start - send_buf->buf; -+ } else { -+ reg2 = NULL; -+ reg2_len = 0; -+ } -+ -+ /* More buffer availability check, note that the write data must be in -+ * a contigue buffer. */ -+ avail_len = PJ_MAX(reg1_len, reg2_len); -+ if (avail_len < len) -+ return NULL; -+ -+ /* Get the data slot */ -+ if (reg1_len >= len) { -+ p = (write_data_t*)reg1; -+ } else { -+ p = (write_data_t*)reg2; -+ skipped_len = reg1_len; -+ } -+ -+ /* Update buffer length */ -+ send_buf->len += len + skipped_len; -+ -+init_send_data: -+ /* Init the new send data */ -+ pj_bzero(p, sizeof(*p)); -+ pj_list_init(p); -+ pj_list_push_back(&ssock->send_pending, p); -+ -+ return p; -+} -+ -+static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata) -+{ -+ send_buf_t *buf = &ssock->send_buf; -+ write_data_t *spl = &ssock->send_pending; -+ -+ pj_assert(!pj_list_empty(&ssock->send_pending)); -+ -+ /* Free slot from the buffer */ -+ if (spl->next == wdata && spl->prev == wdata) { -+ /* This is the only data, reset the buffer */ -+ buf->start = buf->buf; -+ buf->len = 0; -+ } else if (spl->next == wdata) { -+ /* This is the first data, shift start pointer of the buffer and -+ * adjust the buffer length. -+ */ -+ buf->start = (char*)wdata->next; -+ if (wdata->next > wdata) { -+ buf->len -= ((char*)wdata->next - buf->start); -+ } else { -+ /* Overlapped */ -+ pj_size_t right_len, left_len; -+ right_len = buf->buf + buf->max_len - (char*)wdata; -+ left_len = (char*)wdata->next - buf->buf; -+ buf->len -= (right_len + left_len); -+ } -+ } else if (spl->prev == wdata) { -+ /* This is the last data, just adjust the buffer length */ -+ if (wdata->prev < wdata) { -+ pj_size_t jump_len; -+ jump_len = (char*)wdata - -+ ((char*)wdata->prev + wdata->prev->record_len); -+ buf->len -= (wdata->record_len + jump_len); -+ } else { -+ /* Overlapped */ -+ pj_size_t right_len, left_len; -+ right_len = buf->buf + buf->max_len - -+ ((char*)wdata->prev + wdata->prev->record_len); -+ left_len = (char*)wdata + wdata->record_len - buf->buf; -+ buf->len -= (right_len + left_len); -+ } -+ } -+ /* For data in the middle buffer, just do nothing on the buffer. The slot -+ * will be freed later when freeing the first/last data. */ -+ -+ /* Remove the data from send pending list */ -+ pj_list_erase(wdata); -+} -+ -+#if 0 -+/* Just for testing send buffer alloc/free */ -+#include <pj/rand.h> -+pj_status_t pj_ssl_sock_ossl_test_send_buf(pj_pool_t *pool) -+{ -+ enum { MAX_CHUNK_NUM = 20 }; -+ unsigned chunk_size, chunk_cnt, i; -+ write_data_t *wdata[MAX_CHUNK_NUM] = {0}; -+ pj_time_val now; -+ pj_ssl_sock_t *ssock = NULL; -+ pj_ssl_sock_param param; -+ pj_status_t status; -+ -+ pj_gettimeofday(&now); -+ pj_srand((unsigned)now.sec); -+ -+ pj_ssl_sock_param_default(¶m); -+ status = pj_ssl_sock_create(pool, ¶m, &ssock); -+ if (status != PJ_SUCCESS) { -+ return status; -+ } -+ -+ if (ssock->send_buf.max_len == 0) { -+ ssock->send_buf.buf = (char *) -+ pj_pool_alloc(ssock->pool, -+ ssock->param.send_buffer_size); -+ ssock->send_buf.max_len = ssock->param.send_buffer_size; -+ ssock->send_buf.start = ssock->send_buf.buf; -+ ssock->send_buf.len = 0; -+ } -+ -+ chunk_size = ssock->param.send_buffer_size / MAX_CHUNK_NUM / 2; -+ chunk_cnt = 0; -+ for (i = 0; i < MAX_CHUNK_NUM; i++) { -+ wdata[i] = alloc_send_data(ssock, pj_rand() % chunk_size + 321); -+ if (wdata[i]) -+ chunk_cnt++; -+ else -+ break; -+ } -+ -+ while (chunk_cnt) { -+ i = pj_rand() % MAX_CHUNK_NUM; -+ if (wdata[i]) { -+ free_send_data(ssock, wdata[i]); -+ wdata[i] = NULL; -+ chunk_cnt--; -+ } -+ } -+ -+ if (ssock->send_buf.len != 0) -+ status = PJ_EBUG; -+ -+ pj_ssl_sock_close(ssock); -+ return status; -+} -+#endif -+ -+/* Flush write circular buffer to network socket. */ -+static pj_status_t flush_circ_buf_output(pj_ssl_sock_t *ssock, -+ pj_ioqueue_op_key_t *send_key, -+ pj_size_t orig_len, unsigned flags) -+{ -+ pj_ssize_t len; -+ write_data_t *wdata; -+ pj_size_t needed_len; -+ pj_status_t status; -+ -+ pj_lock_acquire(ssock->circ_buf_output_mutex); -+ -+ /* Check if there is data in the circular buffer, flush it if any */ -+ if (circ_empty(&ssock->circ_buf_output)) { -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ -+ return PJ_SUCCESS; -+ } -+ -+ len = circ_size(&ssock->circ_buf_output); -+ -+ /* Calculate buffer size needed, and align it to 8 */ -+ needed_len = len + sizeof(write_data_t); -+ needed_len = ((needed_len + 7) >> 3) << 3; -+ -+ /* Allocate buffer for send data */ -+ wdata = alloc_send_data(ssock, needed_len); -+ if (wdata == NULL) { -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ return PJ_ENOMEM; -+ } -+ -+ /* Copy the data and set its properties into the send data */ -+ pj_ioqueue_op_key_init(&wdata->key, sizeof(pj_ioqueue_op_key_t)); -+ wdata->key.user_data = wdata; -+ wdata->app_key = send_key; -+ wdata->record_len = needed_len; -+ wdata->data_len = len; -+ wdata->plain_data_len = orig_len; -+ wdata->flags = flags; -+ circ_read(&ssock->circ_buf_output, (pj_uint8_t *)&wdata->data, len); -+ -+ /* Ticket #1573: Don't hold mutex while calling PJLIB socket send(). */ -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ -+ /* Send it */ -+ if (ssock->param.sock_type == pj_SOCK_STREAM()) { -+ status = pj_activesock_send(ssock->asock, &wdata->key, -+ wdata->data.content, &len, -+ flags); -+ } else { -+ status = pj_activesock_sendto(ssock->asock, &wdata->key, -+ wdata->data.content, &len, -+ flags, -+ (pj_sockaddr_t*)&ssock->rem_addr, -+ ssock->addr_len); -+ } -+ -+ if (status != PJ_EPENDING) { -+ /* When the sending is not pending, remove the wdata from send -+ * pending list. */ -+ pj_lock_acquire(ssock->circ_buf_output_mutex); -+ free_send_data(ssock, wdata); -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ } -+ -+ return status; -+} -+ -+static void on_timer(pj_timer_heap_t *th, struct pj_timer_entry *te) -+{ -+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)te->user_data; -+ int timer_id = te->id; -+ -+ te->id = TIMER_NONE; -+ -+ PJ_UNUSED_ARG(th); -+ -+ switch (timer_id) { -+ case TIMER_HANDSHAKE_TIMEOUT: -+ PJ_LOG(1, (ssock->pool->obj_name, "TLS timeout after %d.%ds", -+ ssock->param.timeout.sec, ssock->param.timeout.msec)); -+ -+ on_handshake_complete(ssock, PJ_ETIMEDOUT); -+ break; -+ case TIMER_CLOSE: -+ pj_ssl_sock_close(ssock); -+ break; -+ default: -+ pj_assert(!"Unknown timer"); -+ break; -+ } -+} -+ -+ -+/* Try to perform an asynchronous handshake */ -+static pj_status_t tls_try_handshake(pj_ssl_sock_t *ssock) -+{ -+ int ret; -+ pj_status_t status; -+ -+ /* Perform SSL handshake */ -+ ret = gnutls_handshake(ssock->session); -+ -+ status = flush_circ_buf_output(ssock, &ssock->handshake_op_key, 0, 0); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ if (ret == GNUTLS_E_SUCCESS) { -+ /* System are GO */ -+ ssock->connection_state = TLS_STATE_ESTABLISHED; -+ status = PJ_SUCCESS; -+ } else if (!gnutls_error_is_fatal(ret)) { -+ /* Non fatal error, retry later (busy or again) */ -+ status = PJ_EPENDING; -+ } else { -+ /* Fatal error invalidates session, no fallback */ -+ status = PJ_EINVAL; -+ } -+ -+ tls_last_error = ret; -+ -+ return status; -+} -+ -+ -+/* -+ ******************************************************************* -+ * Active socket callbacks. -+ ******************************************************************* -+ */ -+ -+/* PJ_TRUE asks the socket to read more data, PJ_FALSE takes it off the queue */ -+static pj_bool_t asock_on_data_read(pj_activesock_t *asock, void *data, -+ pj_size_t size, pj_status_t status, -+ pj_size_t *remainder) -+{ -+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t *) -+ pj_activesock_get_user_data(asock); -+ -+ pj_size_t app_remainder = 0; -+ -+ if (data && size > 0) { -+ /* Push data into input circular buffer (for GnuTLS) */ -+ pj_lock_acquire(ssock->circ_buf_input_mutex); -+ circ_write(&ssock->circ_buf_input, data, size); -+ pj_lock_release(ssock->circ_buf_input_mutex); -+ } -+ -+ /* Check if SSL handshake hasn't finished yet */ -+ if (ssock->connection_state == TLS_STATE_HANDSHAKING) { -+ pj_bool_t ret = PJ_TRUE; -+ -+ if (status == PJ_SUCCESS) -+ status = tls_try_handshake(ssock); -+ -+ /* Not pending is either success or failed */ -+ if (status != PJ_EPENDING) -+ ret = on_handshake_complete(ssock, status); -+ -+ return ret; -+ } -+ -+ /* See if there is any decrypted data for the application */ -+ if (ssock->read_started) { -+ do { -+ /* Get read data structure at the end of the data */ -+ read_data_t *app_read_data = *(OFFSET_OF_READ_DATA_PTR(ssock, data)); -+ int app_data_size = (int)(ssock->read_size - app_read_data->len); -+ -+ /* Decrypt received data using GnuTLS (will read our input -+ * circular buffer) */ -+ int decrypted_size = gnutls_record_recv(ssock->session, -+ ((read_data_t *)app_read_data->data) + -+ app_read_data->len, -+ app_data_size); -+ -+ if (decrypted_size > 0 || status != PJ_SUCCESS) { -+ if (ssock->param.cb.on_data_read) { -+ pj_bool_t ret; -+ app_remainder = 0; -+ -+ if (decrypted_size > 0) -+ app_read_data->len += decrypted_size; -+ -+ ret = (*ssock->param.cb.on_data_read)(ssock, -+ app_read_data->data, -+ app_read_data->len, -+ status, -+ &app_remainder); -+ -+ if (!ret) { -+ /* We've been destroyed */ -+ return PJ_FALSE; -+ } -+ -+ /* Application may have left some data to be consumed -+ * later as remainder */ -+ app_read_data->len = app_remainder; -+ } -+ -+ /* Active socket signalled connection closed/error, this has -+ * been signalled to the application along with any remaining -+ * buffer. So, let's just reset SSL socket now. */ -+ if (status != PJ_SUCCESS) { -+ tls_sock_reset(ssock); -+ return PJ_FALSE; -+ } -+ } else if (decrypted_size == 0) { -+ /* Nothing more to read */ -+ -+ return PJ_TRUE; -+ } else if (decrypted_size == GNUTLS_E_AGAIN || -+ decrypted_size == GNUTLS_E_INTERRUPTED) { -+ return PJ_TRUE; -+ } else if (decrypted_size == GNUTLS_E_REHANDSHAKE) { -+ /* Seems like we are renegotiating */ -+ pj_status_t try_handshake_status = tls_try_handshake(ssock); -+ -+ /* Not pending is either success or failed */ -+ if (try_handshake_status != PJ_EPENDING) { -+ if (!on_handshake_complete(ssock, try_handshake_status)) { -+ return PJ_FALSE; -+ } -+ } -+ -+ if (try_handshake_status != PJ_SUCCESS && -+ try_handshake_status != PJ_EPENDING) { -+ return PJ_FALSE; -+ } -+ } else if (!gnutls_error_is_fatal(decrypted_size)) { -+ /* non-fatal error, let's just continue */ -+ } else { -+ return PJ_FALSE; -+ } -+ } while (PJ_TRUE); -+ } -+ -+ return PJ_TRUE; -+} -+ -+ -+/* Callback every time new data is available from the active socket */ -+static pj_bool_t asock_on_data_sent(pj_activesock_t *asock, -+ pj_ioqueue_op_key_t *send_key, -+ pj_ssize_t sent) -+{ -+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)pj_activesock_get_user_data(asock); -+ -+ PJ_UNUSED_ARG(send_key); -+ PJ_UNUSED_ARG(sent); -+ -+ if (ssock->connection_state == TLS_STATE_HANDSHAKING) { -+ /* Initial handshaking */ -+ pj_status_t status = tls_try_handshake(ssock); -+ -+ /* Not pending is either success or failed */ -+ if (status != PJ_EPENDING) -+ return on_handshake_complete(ssock, status); -+ -+ } else if (send_key != &ssock->handshake_op_key) { -+ /* Some data has been sent, notify application */ -+ write_data_t *wdata = (write_data_t*)send_key->user_data; -+ if (ssock->param.cb.on_data_sent) { -+ pj_bool_t ret; -+ pj_ssize_t sent_len; -+ -+ sent_len = sent > 0 ? wdata->plain_data_len : sent; -+ -+ ret = (*ssock->param.cb.on_data_sent)(ssock, wdata->app_key, -+ sent_len); -+ if (!ret) { -+ /* We've been destroyed */ -+ return PJ_FALSE; -+ } -+ } -+ -+ /* Update write buffer state */ -+ pj_lock_acquire(ssock->circ_buf_output_mutex); -+ free_send_data(ssock, wdata); -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ } else { -+ /* SSL re-negotiation is on-progress, just do nothing */ -+ /* FIXME: check if this is valid for GnuTLS too */ -+ } -+ -+ return PJ_TRUE; -+} -+ -+ -+/* Callback every time a new connection has been accepted (server) */ -+static pj_bool_t asock_on_accept_complete(pj_activesock_t *asock, -+ pj_sock_t newsock, -+ const pj_sockaddr_t *src_addr, -+ int src_addr_len) -+{ -+ pj_ssl_sock_t *ssock_parent = (pj_ssl_sock_t *) -+ pj_activesock_get_user_data(asock); -+ -+ pj_ssl_sock_t *ssock; -+ pj_activesock_cb asock_cb; -+ pj_activesock_cfg asock_cfg; -+ unsigned int i; -+ pj_status_t status; -+ -+ PJ_UNUSED_ARG(src_addr_len); -+ -+ /* Create new SSL socket instance */ -+ status = pj_ssl_sock_create(ssock_parent->pool, &ssock_parent->newsock_param, -+ &ssock); -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Update new SSL socket attributes */ -+ ssock->sock = newsock; -+ ssock->parent = ssock_parent; -+ ssock->is_server = PJ_TRUE; -+ if (ssock_parent->cert) { -+ status = pj_ssl_sock_set_certificate(ssock, ssock->pool, -+ ssock_parent->cert); -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ } -+ -+ /* Apply QoS, if specified */ -+ status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type, -+ &ssock->param.qos_params, 1, -+ ssock->pool->obj_name, NULL); -+ if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error) -+ goto on_return; -+ -+ /* Update local address */ -+ ssock->addr_len = src_addr_len; -+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, -+ &ssock->addr_len); -+ if (status != PJ_SUCCESS) { -+ /* This fails on few envs, e.g: win IOCP, just tolerate this and -+ * use parent local address instead. -+ */ -+ pj_sockaddr_cp(&ssock->local_addr, &ssock_parent->local_addr); -+ } -+ -+ /* Set remote address */ -+ pj_sockaddr_cp(&ssock->rem_addr, src_addr); -+ -+ /* Create SSL context */ -+ status = tls_open(ssock); -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Prepare read buffer */ -+ ssock->asock_rbuf = (void **)pj_pool_calloc(ssock->pool, -+ ssock->param.async_cnt, -+ sizeof(void*)); -+ if (!ssock->asock_rbuf) -+ return PJ_ENOMEM; -+ -+ for (i = 0; i < ssock->param.async_cnt; ++i) { -+ ssock->asock_rbuf[i] = (void *)pj_pool_alloc( -+ ssock->pool, -+ ssock->param.read_buffer_size + -+ sizeof(read_data_t*)); -+ if (!ssock->asock_rbuf[i]) -+ return PJ_ENOMEM; -+ } -+ -+ /* Create active socket */ -+ pj_activesock_cfg_default(&asock_cfg); -+ asock_cfg.async_cnt = ssock->param.async_cnt; -+ asock_cfg.concurrency = ssock->param.concurrency; -+ asock_cfg.whole_data = PJ_TRUE; -+ -+ pj_bzero(&asock_cb, sizeof(asock_cb)); -+ asock_cb.on_data_read = asock_on_data_read; -+ asock_cb.on_data_sent = asock_on_data_sent; -+ -+ status = pj_activesock_create(ssock->pool, -+ ssock->sock, -+ ssock->param.sock_type, -+ &asock_cfg, -+ ssock->param.ioqueue, -+ &asock_cb, -+ ssock, -+ &ssock->asock); -+ -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Start reading */ -+ status = pj_activesock_start_read2(ssock->asock, ssock->pool, -+ (unsigned)ssock->param.read_buffer_size, -+ ssock->asock_rbuf, -+ PJ_IOQUEUE_ALWAYS_ASYNC); -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Prepare write/send state */ -+ pj_assert(ssock->send_buf.max_len == 0); -+ ssock->send_buf.buf = (char *)pj_pool_alloc(ssock->pool, -+ ssock->param.send_buffer_size); -+ if (!ssock->send_buf.buf) -+ return PJ_ENOMEM; -+ -+ ssock->send_buf.max_len = ssock->param.send_buffer_size; -+ ssock->send_buf.start = ssock->send_buf.buf; -+ ssock->send_buf.len = 0; -+ -+ /* Start handshake timer */ -+ if (ssock->param.timer_heap && -+ (ssock->param.timeout.sec != 0 || ssock->param.timeout.msec != 0)) { -+ pj_assert(ssock->timer.id == TIMER_NONE); -+ ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT; -+ status = pj_timer_heap_schedule(ssock->param.timer_heap, -+ &ssock->timer, -+ &ssock->param.timeout); -+ if (status != PJ_SUCCESS) -+ ssock->timer.id = TIMER_NONE; -+ } -+ -+ /* Start SSL handshake */ -+ ssock->connection_state = TLS_STATE_HANDSHAKING; -+ -+ status = tls_try_handshake(ssock); -+ -+on_return: -+ if (ssock && status != PJ_EPENDING) -+ on_handshake_complete(ssock, status); -+ -+ /* Must return PJ_TRUE whatever happened, as active socket must -+ * continue listening. -+ */ -+ return PJ_TRUE; -+} -+ -+ -+/* Callback every time a new connection has been completed (client) */ -+static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock, -+ pj_status_t status) -+{ -+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t*) -+ pj_activesock_get_user_data(asock); -+ -+ unsigned int i; -+ int ret; -+ -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Update local address */ -+ ssock->addr_len = sizeof(pj_sockaddr); -+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, -+ &ssock->addr_len); -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Create SSL context */ -+ status = tls_open(ssock); -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Prepare read buffer */ -+ ssock->asock_rbuf = (void **)pj_pool_calloc(ssock->pool, -+ ssock->param.async_cnt, -+ sizeof(void *)); -+ if (!ssock->asock_rbuf) -+ return PJ_ENOMEM; -+ -+ for (i = 0; i < ssock->param.async_cnt; ++i) { -+ ssock->asock_rbuf[i] = (void *)pj_pool_alloc( -+ ssock->pool, -+ ssock->param.read_buffer_size + -+ sizeof(read_data_t *)); -+ if (!ssock->asock_rbuf[i]) -+ return PJ_ENOMEM; -+ } -+ -+ /* Start read */ -+ status = pj_activesock_start_read2(ssock->asock, ssock->pool, -+ (unsigned) ssock->param.read_buffer_size, -+ ssock->asock_rbuf, -+ PJ_IOQUEUE_ALWAYS_ASYNC); -+ if (status != PJ_SUCCESS) -+ goto on_return; -+ -+ /* Prepare write/send state */ -+ pj_assert(ssock->send_buf.max_len == 0); -+ ssock->send_buf.buf = (char *)pj_pool_alloc(ssock->pool, -+ ssock->param.send_buffer_size); -+ if (!ssock->send_buf.buf) -+ return PJ_ENOMEM; -+ -+ ssock->send_buf.max_len = ssock->param.send_buffer_size; -+ ssock->send_buf.start = ssock->send_buf.buf; -+ ssock->send_buf.len = 0; -+ -+ /* Set server name to connect */ -+ if (ssock->param.server_name.slen) { -+ /* Server name is null terminated already */ -+ ret = gnutls_server_name_set(ssock->session, GNUTLS_NAME_DNS, -+ ssock->param.server_name.ptr, -+ ssock->param.server_name.slen); -+ if (ret < 0) { -+ PJ_LOG(3, (ssock->pool->obj_name, -+ "gnutls_server_name_set() failed: %s", -+ gnutls_strerror(ret))); -+ } -+ } -+ -+ /* Start handshake */ -+ ssock->connection_state = TLS_STATE_HANDSHAKING; -+ -+ status = tls_try_handshake(ssock); -+ if (status != PJ_EPENDING) -+ goto on_return; -+ -+ return PJ_TRUE; -+ -+on_return: -+ return on_handshake_complete(ssock, status); -+} -+ -+static void tls_ciphers_fill(void) -+{ -+ if (!tls_available_ciphers) { -+ tls_init(); -+ tls_deinit(); -+ } -+} -+ -+/* -+ ******************************************************************* -+ * API -+ ******************************************************************* -+ */ -+ -+/* Load credentials from files. */ -+PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool, -+ const pj_str_t *CA_file, -+ const pj_str_t *cert_file, -+ const pj_str_t *privkey_file, -+ const pj_str_t *privkey_pass, -+ pj_ssl_cert_t **p_cert) -+{ -+ return pj_ssl_cert_load_from_files2(pool, CA_file, NULL, cert_file, -+ privkey_file, privkey_pass, p_cert); -+} -+ -+/* Load credentials from files. */ -+PJ_DECL(pj_status_t) pj_ssl_cert_load_from_files2( -+ pj_pool_t *pool, -+ const pj_str_t *CA_file, -+ const pj_str_t *CA_path, -+ const pj_str_t *cert_file, -+ const pj_str_t *privkey_file, -+ const pj_str_t *privkey_pass, -+ pj_ssl_cert_t **p_cert) -+{ -+ pj_ssl_cert_t *cert; -+ -+ PJ_ASSERT_RETURN(pool && (CA_file || CA_path) && cert_file && -+ privkey_file, -+ PJ_EINVAL); -+ -+ cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t); -+ if (CA_file) { -+ pj_strdup_with_null(pool, &cert->CA_file, CA_file); -+ } -+ if (CA_path) { -+ pj_strdup_with_null(pool, &cert->CA_path, CA_path); -+ } -+ pj_strdup_with_null(pool, &cert->cert_file, cert_file); -+ pj_strdup_with_null(pool, &cert->privkey_file, privkey_file); -+ pj_strdup_with_null(pool, &cert->privkey_pass, privkey_pass); -+ -+ *p_cert = cert; -+ -+ return PJ_SUCCESS; -+} -+ -+/* Store credentials. */ -+PJ_DECL(pj_status_t) pj_ssl_sock_set_certificate(pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ const pj_ssl_cert_t *cert) -+{ -+ pj_ssl_cert_t *cert_; -+ -+ PJ_ASSERT_RETURN(ssock && pool && cert, PJ_EINVAL); -+ -+ cert_ = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t); -+ pj_memcpy(cert_, cert, sizeof(cert)); -+ pj_strdup_with_null(pool, &cert_->CA_file, &cert->CA_file); -+ pj_strdup_with_null(pool, &cert_->CA_path, &cert->CA_path); -+ pj_strdup_with_null(pool, &cert_->cert_file, &cert->cert_file); -+ pj_strdup_with_null(pool, &cert_->privkey_file, &cert->privkey_file); -+ pj_strdup_with_null(pool, &cert_->privkey_pass, &cert->privkey_pass); -+ -+ ssock->cert = cert_; -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* Get available ciphers. */ -+PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[], -+ unsigned *cipher_num) -+{ -+ unsigned int i; -+ -+ PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL); -+ -+ tls_ciphers_fill(); -+ -+ if (!tls_available_ciphers) { -+ *cipher_num = 0; -+ return PJ_ENOTFOUND; -+ } -+ -+ *cipher_num = PJ_MIN(*cipher_num, tls_available_ciphers); -+ -+ for (i = 0; i < *cipher_num; ++i) -+ ciphers[i] = tls_ciphers[i].id; -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* Get cipher name string. */ -+PJ_DEF(const char *)pj_ssl_cipher_name(pj_ssl_cipher cipher) -+{ -+ unsigned int i; -+ -+ tls_ciphers_fill(); -+ -+ for (i = 0; i < tls_available_ciphers; ++i) { -+ if (cipher == tls_ciphers[i].id) -+ return tls_ciphers[i].name; -+ } -+ -+ return NULL; -+} -+ -+ -+/* Get cipher identifier. */ -+PJ_DEF(pj_ssl_cipher) pj_ssl_cipher_id(const char *cipher_name) -+{ -+ unsigned int i; -+ -+ tls_ciphers_fill(); -+ -+ for (i = 0; i < tls_available_ciphers; ++i) { -+ if (!pj_ansi_stricmp(tls_ciphers[i].name, cipher_name)) -+ return tls_ciphers[i].id; -+ } -+ -+ return PJ_TLS_UNKNOWN_CIPHER; -+} -+ -+ -+/* Check if the specified cipher is supported by the TLS backend. */ -+PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher) -+{ -+ unsigned int i; -+ -+ tls_ciphers_fill(); -+ -+ for (i = 0; i < tls_available_ciphers; ++i) { -+ if (cipher == tls_ciphers[i].id) -+ return PJ_TRUE; -+ } -+ -+ return PJ_FALSE; -+} -+ -+/* Create SSL socket instance. */ -+PJ_DEF(pj_status_t) pj_ssl_sock_create(pj_pool_t *pool, -+ const pj_ssl_sock_param *param, -+ pj_ssl_sock_t **p_ssock) -+{ -+ pj_ssl_sock_t *ssock; -+ pj_status_t status; -+ -+ PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL); -+ PJ_ASSERT_RETURN(param->sock_type == pj_SOCK_STREAM(), PJ_ENOTSUP); -+ -+ pool = pj_pool_create(pool->factory, "tls%p", 512, 512, NULL); -+ -+ /* Create secure socket */ -+ ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t); -+ ssock->pool = pool; -+ ssock->sock = PJ_INVALID_SOCKET; -+ ssock->connection_state = TLS_STATE_NULL; -+ pj_list_init(&ssock->write_pending); -+ pj_list_init(&ssock->write_pending_empty); -+ pj_list_init(&ssock->send_pending); -+ pj_timer_entry_init(&ssock->timer, 0, ssock, &on_timer); -+ pj_ioqueue_op_key_init(&ssock->handshake_op_key, -+ sizeof(pj_ioqueue_op_key_t)); -+ -+ /* Create secure socket mutex */ -+ status = pj_lock_create_recursive_mutex(pool, pool->obj_name, -+ &ssock->circ_buf_output_mutex); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ /* Create input circular buffer mutex */ -+ status = pj_lock_create_simple_mutex(pool, pool->obj_name, -+ &ssock->circ_buf_input_mutex); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ /* Create output circular buffer mutex */ -+ status = pj_lock_create_simple_mutex(pool, pool->obj_name, -+ &ssock->circ_buf_output_mutex); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ /* Init secure socket param */ -+ ssock->param = *param; -+ ssock->param.read_buffer_size = ((ssock->param.read_buffer_size + 7) >> 3) << 3; -+ -+ if (param->ciphers_num > 0) { -+ unsigned int i; -+ ssock->param.ciphers = (pj_ssl_cipher *) -+ pj_pool_calloc(pool, param->ciphers_num, -+ sizeof(pj_ssl_cipher)); -+ if (!ssock->param.ciphers) -+ return PJ_ENOMEM; -+ -+ for (i = 0; i < param->ciphers_num; ++i) -+ ssock->param.ciphers[i] = param->ciphers[i]; -+ } -+ -+ /* Server name must be null-terminated */ -+ pj_strdup_with_null(pool, &ssock->param.server_name, ¶m->server_name); -+ -+ /* Finally */ -+ *p_ssock = ssock; -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* -+ * Close the secure socket. This will unregister the socket from the -+ * ioqueue and ultimately close the socket. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock) -+{ -+ pj_pool_t *pool; -+ -+ PJ_ASSERT_RETURN(ssock, PJ_EINVAL); -+ -+ if (!ssock->pool) -+ return PJ_SUCCESS; -+ -+ if (ssock->timer.id != TIMER_NONE) { -+ pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer); -+ ssock->timer.id = TIMER_NONE; -+ } -+ -+ tls_sock_reset(ssock); -+ -+ pj_lock_destroy(ssock->circ_buf_output_mutex); -+ pj_lock_destroy(ssock->circ_buf_input_mutex); -+ -+ pool = ssock->pool; -+ ssock->pool = NULL; -+ if (pool) -+ pj_pool_release(pool); -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* Associate arbitrary data with the secure socket. */ -+PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data(pj_ssl_sock_t *ssock, -+ void *user_data) -+{ -+ PJ_ASSERT_RETURN(ssock, PJ_EINVAL); -+ -+ ssock->param.user_data = user_data; -+ return PJ_SUCCESS; -+} -+ -+ -+/* Retrieve the user data previously associated with this secure socket. */ -+PJ_DEF(void *)pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock) -+{ -+ PJ_ASSERT_RETURN(ssock, NULL); -+ -+ return ssock->param.user_data; -+} -+ -+ -+/* Retrieve the local address and port used by specified SSL socket. */ -+PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock, -+ pj_ssl_sock_info *info) -+{ -+ pj_bzero(info, sizeof(*info)); -+ -+ /* Established flag */ -+ info->established = (ssock->connection_state == TLS_STATE_ESTABLISHED); -+ -+ /* Protocol */ -+ info->proto = ssock->param.proto; -+ -+ /* Local address */ -+ pj_sockaddr_cp(&info->local_addr, &ssock->local_addr); -+ -+ if (info->established) { -+ int i; -+ gnutls_cipher_algorithm_t lookup; -+ gnutls_cipher_algorithm_t cipher; -+ -+ /* Current cipher */ -+ cipher = gnutls_cipher_get(ssock->session); -+ for (i = 0; ; i++) { -+ unsigned char id[2]; -+ const char *suite = gnutls_cipher_suite_info(i, (unsigned char *)id, -+ NULL, &lookup, NULL, -+ NULL); -+ if (suite) { -+ if (lookup == cipher) { -+ info->cipher = (pj_uint32_t) ((id[0] << 8) | id[1]); -+ break; -+ } -+ } else -+ break; -+ } -+ -+ /* Remote address */ -+ pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr); -+ -+ /* Certificates info */ -+ info->local_cert_info = &ssock->local_cert_info; -+ info->remote_cert_info = &ssock->remote_cert_info; -+ -+ /* Verification status */ -+ info->verify_status = ssock->verify_status; -+ } -+ -+ /* Last known GnuTLS error code */ -+ info->last_native_err = ssock->last_err; -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* Starts read operation on this secure socket. */ -+PJ_DEF(pj_status_t) pj_ssl_sock_start_read(pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ unsigned buff_size, -+ pj_uint32_t flags) -+{ -+ void **readbuf; -+ unsigned int i; -+ -+ PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL); -+ PJ_ASSERT_RETURN(ssock->connection_state == TLS_STATE_ESTABLISHED, -+ PJ_EINVALIDOP); -+ -+ readbuf = (void**) pj_pool_calloc(pool, ssock->param.async_cnt, -+ sizeof(void *)); -+ if (!readbuf) -+ return PJ_ENOMEM; -+ -+ for (i = 0; i < ssock->param.async_cnt; ++i) { -+ readbuf[i] = pj_pool_alloc(pool, buff_size); -+ if (!readbuf[i]) -+ return PJ_ENOMEM; -+ } -+ -+ return pj_ssl_sock_start_read2(ssock, pool, buff_size, readbuf, flags); -+} -+ -+ -+/* -+ * Same as #pj_ssl_sock_start_read(), except that the application -+ * supplies the buffers for the read operation so that the acive socket -+ * does not have to allocate the buffers. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ unsigned buff_size, -+ void *readbuf[], -+ pj_uint32_t flags) -+{ -+ unsigned int i; -+ -+ PJ_ASSERT_RETURN(ssock && pool && buff_size && readbuf, PJ_EINVAL); -+ PJ_ASSERT_RETURN(ssock->connection_state == TLS_STATE_ESTABLISHED, -+ PJ_EINVALIDOP); -+ -+ /* Create SSL socket read buffer */ -+ ssock->ssock_rbuf = (read_data_t*)pj_pool_calloc(pool, -+ ssock->param.async_cnt, -+ sizeof(read_data_t)); -+ if (!ssock->ssock_rbuf) -+ return PJ_ENOMEM; -+ -+ /* Store SSL socket read buffer pointer in the activesock read buffer */ -+ for (i = 0; i < ssock->param.async_cnt; ++i) { -+ read_data_t **p_ssock_rbuf = -+ OFFSET_OF_READ_DATA_PTR(ssock, ssock->asock_rbuf[i]); -+ -+ ssock->ssock_rbuf[i].data = readbuf[i]; -+ ssock->ssock_rbuf[i].len = 0; -+ -+ *p_ssock_rbuf = &ssock->ssock_rbuf[i]; -+ } -+ -+ ssock->read_size = buff_size; -+ ssock->read_started = PJ_TRUE; -+ ssock->read_flags = flags; -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* -+ * Same as pj_ssl_sock_start_read(), except that this function is used -+ * only for datagram sockets, and it will trigger \a on_data_recvfrom() -+ * callback instead. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ unsigned buff_size, -+ pj_uint32_t flags) -+{ -+ PJ_UNUSED_ARG(ssock); -+ PJ_UNUSED_ARG(pool); -+ PJ_UNUSED_ARG(buff_size); -+ PJ_UNUSED_ARG(flags); -+ -+ return PJ_ENOTSUP; -+} -+ -+ -+/* -+ * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() -+ * operation takes the buffer from the argument rather than creating -+ * new ones. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ unsigned buff_size, -+ void *readbuf[], -+ pj_uint32_t flags) -+{ -+ PJ_UNUSED_ARG(ssock); -+ PJ_UNUSED_ARG(pool); -+ PJ_UNUSED_ARG(buff_size); -+ PJ_UNUSED_ARG(readbuf); -+ PJ_UNUSED_ARG(flags); -+ -+ return PJ_ENOTSUP; -+} -+ -+ -+/* -+ * Write the plain data to GnuTLS, it will be encrypted by gnutls_record_send() -+ * and sent via tls_data_push. Note that re-negotitation may be on progress, so -+ * sending data should be delayed until re-negotiation is completed. -+ */ -+static pj_status_t tls_write(pj_ssl_sock_t *ssock, -+ pj_ioqueue_op_key_t *send_key, -+ const void *data, pj_ssize_t size, unsigned flags) -+{ -+ pj_status_t status; -+ int nwritten; -+ pj_ssize_t total_written = 0; -+ -+ /* Ask GnuTLS to encrypt our plaintext now. GnuTLS will use the push -+ * callback to actually write the encrypted bytes into our output circular -+ * buffer. GnuTLS may refuse to "send" everything at once, but since we are -+ * not really sending now, we will just call it again now until it succeeds -+ * (or fails in a fatal way). */ -+ while (total_written < size) { -+ /* Try encrypting using GnuTLS */ -+ nwritten = gnutls_record_send(ssock->session, ((read_data_t *)data) + total_written, -+ size); -+ -+ if (nwritten > 0) { -+ /* Good, some data was encrypted and written */ -+ total_written += nwritten; -+ } else { -+ /* Normally we would have to retry record_send but our internal -+ * state has not changed, so we have to ask for more data first. -+ * We will just try again later, although this should never happen. -+ */ -+ return tls_status_from_err(ssock, nwritten); -+ } -+ } -+ -+ /* All encrypted data is written to the output circular buffer; -+ * now send it on the socket (or notify problem). */ -+ if (total_written == size) -+ status = flush_circ_buf_output(ssock, send_key, size, flags); -+ else -+ status = PJ_ENOMEM; -+ -+ return status; -+} -+ -+ -+/* Flush delayed data sending in the write pending list. */ -+static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock) -+{ -+ /* Check for another ongoing flush */ -+ if (ssock->flushing_write_pend) { -+ return PJ_EBUSY; -+ } -+ -+ pj_lock_acquire(ssock->circ_buf_output_mutex); -+ -+ /* Again, check for another ongoing flush */ -+ if (ssock->flushing_write_pend) { -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ return PJ_EBUSY; -+ } -+ -+ /* Set ongoing flush flag */ -+ ssock->flushing_write_pend = PJ_TRUE; -+ -+ while (!pj_list_empty(&ssock->write_pending)) { -+ write_data_t *wp; -+ pj_status_t status; -+ -+ wp = ssock->write_pending.next; -+ -+ /* Ticket #1573: Don't hold mutex while calling socket send. */ -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ -+ status = tls_write(ssock, &wp->key, wp->data.ptr, -+ wp->plain_data_len, wp->flags); -+ if (status != PJ_SUCCESS) { -+ /* Reset ongoing flush flag first. */ -+ ssock->flushing_write_pend = PJ_FALSE; -+ return status; -+ } -+ -+ pj_lock_acquire(ssock->circ_buf_output_mutex); -+ pj_list_erase(wp); -+ pj_list_push_back(&ssock->write_pending_empty, wp); -+ } -+ -+ /* Reset ongoing flush flag */ -+ ssock->flushing_write_pend = PJ_FALSE; -+ -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ -+ return PJ_SUCCESS; -+} -+ -+ -+/* Sending is delayed, push back the sending data into pending list. */ -+static pj_status_t delay_send(pj_ssl_sock_t *ssock, -+ pj_ioqueue_op_key_t *send_key, -+ const void *data, pj_ssize_t size, -+ unsigned flags) -+{ -+ write_data_t *wp; -+ -+ pj_lock_acquire(ssock->circ_buf_output_mutex); -+ -+ /* Init write pending instance */ -+ if (!pj_list_empty(&ssock->write_pending_empty)) { -+ wp = ssock->write_pending_empty.next; -+ pj_list_erase(wp); -+ } else { -+ wp = PJ_POOL_ZALLOC_T(ssock->pool, write_data_t); -+ } -+ -+ wp->app_key = send_key; -+ wp->plain_data_len = size; -+ wp->data.ptr = data; -+ wp->flags = flags; -+ -+ pj_list_push_back(&ssock->write_pending, wp); -+ -+ pj_lock_release(ssock->circ_buf_output_mutex); -+ -+ /* Must return PJ_EPENDING */ -+ return PJ_EPENDING; -+} -+ -+ -+/** -+ * Send data using the socket. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_send(pj_ssl_sock_t *ssock, -+ pj_ioqueue_op_key_t *send_key, -+ const void *data, pj_ssize_t *size, -+ unsigned flags) -+{ -+ pj_status_t status; -+ -+ PJ_ASSERT_RETURN(ssock && data && size && (*size > 0), PJ_EINVAL); -+ PJ_ASSERT_RETURN(ssock->connection_state==TLS_STATE_ESTABLISHED, -+ PJ_EINVALIDOP); -+ -+ /* Flush delayed send first. Sending data might be delayed when -+ * re-negotiation is on-progress. */ -+ status = flush_delayed_send(ssock); -+ if (status == PJ_EBUSY) { -+ /* Re-negotiation or flushing is on progress, delay sending */ -+ status = delay_send(ssock, send_key, data, *size, flags); -+ goto on_return; -+ } else if (status != PJ_SUCCESS) { -+ goto on_return; -+ } -+ -+ /* Write data to SSL */ -+ status = tls_write(ssock, send_key, data, *size, flags); -+ if (status == PJ_EBUSY) { -+ /* Re-negotiation is on progress, delay sending */ -+ status = delay_send(ssock, send_key, data, *size, flags); -+ } -+ -+on_return: -+ return status; -+} -+ -+ -+/** -+ * Send datagram using the socket. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock, -+ pj_ioqueue_op_key_t *send_key, -+ const void *data, pj_ssize_t *size, -+ unsigned flags, -+ const pj_sockaddr_t *addr, int addr_len) -+{ -+ PJ_UNUSED_ARG(ssock); -+ PJ_UNUSED_ARG(send_key); -+ PJ_UNUSED_ARG(data); -+ PJ_UNUSED_ARG(size); -+ PJ_UNUSED_ARG(flags); -+ PJ_UNUSED_ARG(addr); -+ PJ_UNUSED_ARG(addr_len); -+ -+ return PJ_ENOTSUP; -+} -+ -+/** -+ * Starts asynchronous socket accept() operations on this secure socket. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ const pj_sockaddr_t *localaddr, -+ int addr_len) -+{ -+ return pj_ssl_sock_start_accept2(ssock, pool, localaddr, addr_len, -+ &ssock->param); -+} -+ -+/** -+ * Starts asynchronous socket accept() operations on this secure socket. -+ */ -+PJ_DEF(pj_status_t) pj_ssl_sock_start_accept2 (pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ const pj_sockaddr_t *localaddr, -+ int addr_len, -+ const pj_ssl_sock_param *newsock_param) -+{ -+ pj_activesock_cb asock_cb; -+ pj_activesock_cfg asock_cfg; -+ pj_status_t status; -+ -+ PJ_ASSERT_RETURN(ssock && pool && localaddr && addr_len, PJ_EINVAL); -+ -+ /* Verify new socket parameters */ -+ if (newsock_param->grp_lock != ssock->param.grp_lock || -+ newsock_param->sock_af != ssock->param.sock_af || -+ newsock_param->sock_type != ssock->param.sock_type) -+ { -+ return PJ_EINVAL; -+ } -+ -+ /* Create socket */ -+ status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, -+ &ssock->sock); -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Apply SO_REUSEADDR */ -+ if (ssock->param.reuse_addr) { -+ int enabled = 1; -+ status = pj_sock_setsockopt(ssock->sock, pj_SOL_SOCKET(), -+ pj_SO_REUSEADDR(), -+ &enabled, sizeof(enabled)); -+ if (status != PJ_SUCCESS) { -+ PJ_PERROR(4,(ssock->pool->obj_name, status, -+ "Warning: error applying SO_REUSEADDR")); -+ } -+ } -+ -+ /* Apply QoS, if specified */ -+ status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type, -+ &ssock->param.qos_params, 2, -+ ssock->pool->obj_name, NULL); -+ if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error) -+ goto on_error; -+ -+ /* Bind socket */ -+ status = pj_sock_bind(ssock->sock, localaddr, addr_len); -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Start listening to the address */ -+ status = pj_sock_listen(ssock->sock, PJ_SOMAXCONN); -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Create active socket */ -+ pj_activesock_cfg_default(&asock_cfg); -+ asock_cfg.async_cnt = ssock->param.async_cnt; -+ asock_cfg.concurrency = ssock->param.concurrency; -+ asock_cfg.whole_data = PJ_TRUE; -+ -+ pj_bzero(&asock_cb, sizeof(asock_cb)); -+ asock_cb.on_accept_complete = asock_on_accept_complete; -+ -+ status = pj_activesock_create(pool, -+ ssock->sock, -+ ssock->param.sock_type, -+ &asock_cfg, -+ ssock->param.ioqueue, -+ &asock_cb, -+ ssock, -+ &ssock->asock); -+ -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Start accepting */ -+ pj_ssl_sock_param_copy(pool, &ssock->newsock_param, newsock_param); -+ status = pj_activesock_start_accept(ssock->asock, pool); -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Update local address */ -+ ssock->addr_len = addr_len; -+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, -+ &ssock->addr_len); -+ if (status != PJ_SUCCESS) -+ pj_sockaddr_cp(&ssock->local_addr, localaddr); -+ -+ ssock->is_server = PJ_TRUE; -+ -+ return PJ_SUCCESS; -+ -+on_error: -+ tls_sock_reset(ssock); -+ return status; -+} -+ -+ -+/** -+ * Starts asynchronous socket connect() operation. -+ */ -+PJ_DECL(pj_status_t) pj_ssl_sock_start_connect(pj_ssl_sock_t *ssock, -+ pj_pool_t *pool, -+ const pj_sockaddr_t *localaddr, -+ const pj_sockaddr_t *remaddr, -+ int addr_len) -+{ -+ pj_activesock_cb asock_cb; -+ pj_activesock_cfg asock_cfg; -+ pj_status_t status; -+ -+ PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len, -+ PJ_EINVAL); -+ -+ /* Create socket */ -+ status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, -+ &ssock->sock); -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Apply QoS, if specified */ -+ status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type, -+ &ssock->param.qos_params, 2, -+ ssock->pool->obj_name, NULL); -+ if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error) -+ goto on_error; -+ -+ /* Bind socket */ -+ status = pj_sock_bind(ssock->sock, localaddr, addr_len); -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Create active socket */ -+ pj_activesock_cfg_default(&asock_cfg); -+ asock_cfg.async_cnt = ssock->param.async_cnt; -+ asock_cfg.concurrency = ssock->param.concurrency; -+ asock_cfg.whole_data = PJ_TRUE; -+ -+ pj_bzero(&asock_cb, sizeof(asock_cb)); -+ asock_cb.on_connect_complete = asock_on_connect_complete; -+ asock_cb.on_data_read = asock_on_data_read; -+ asock_cb.on_data_sent = asock_on_data_sent; -+ -+ status = pj_activesock_create(pool, -+ ssock->sock, -+ ssock->param.sock_type, -+ &asock_cfg, -+ ssock->param.ioqueue, -+ &asock_cb, -+ ssock, -+ &ssock->asock); -+ -+ if (status != PJ_SUCCESS) -+ goto on_error; -+ -+ /* Save remote address */ -+ pj_sockaddr_cp(&ssock->rem_addr, remaddr); -+ -+ /* Start timer */ -+ if (ssock->param.timer_heap && -+ (ssock->param.timeout.sec != 0 || ssock->param.timeout.msec != 0)) -+ { -+ pj_assert(ssock->timer.id == TIMER_NONE); -+ ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT; -+ status = pj_timer_heap_schedule(ssock->param.timer_heap, -+ &ssock->timer, -+ &ssock->param.timeout); -+ if (status != PJ_SUCCESS) -+ ssock->timer.id = TIMER_NONE; -+ } -+ -+ status = pj_activesock_start_connect(ssock->asock, pool, remaddr, -+ addr_len); -+ -+ if (status == PJ_SUCCESS) -+ asock_on_connect_complete(ssock->asock, PJ_SUCCESS); -+ else if (status != PJ_EPENDING) -+ goto on_error; -+ -+ /* Update local address */ -+ ssock->addr_len = addr_len; -+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, -+ &ssock->addr_len); -+ /* Note that we may not get an IP address here. This can -+ * happen for example on Windows, where getsockname() -+ * would return 0.0.0.0 if socket has just started the -+ * async connect. In this case, just leave the local -+ * address with 0.0.0.0 for now; it will be updated -+ * once the socket is established. -+ */ -+ -+ /* Update socket state */ -+ ssock->is_server = PJ_FALSE; -+ -+ return PJ_EPENDING; -+ -+on_error: -+ tls_sock_reset(ssock); -+ return status; -+} -+ -+ -+PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock) -+{ -+ int status; -+ -+ /* Nothing established yet */ -+ PJ_ASSERT_RETURN(ssock->connection_state == TLS_STATE_ESTABLISHED, -+ PJ_EINVALIDOP); -+ -+ /* Cannot renegotiate; we're a client */ -+ /* FIXME: in fact maybe that's not true */ -+ PJ_ASSERT_RETURN(!ssock->is_server, PJ_EINVALIDOP); -+ -+ /* First call gnutls_rehandshake() to see if this is even possible */ -+ status = gnutls_rehandshake(ssock->session); -+ -+ if (status == GNUTLS_E_SUCCESS) { -+ /* Rehandshake is possible, so try a GnuTLS handshake now. The eventual -+ * gnutls_record_recv() calls could return a few specific values during -+ * this state: -+ * -+ * - GNUTLS_E_REHANDSHAKE: rehandshake message processing -+ * - GNUTLS_E_WARNING_ALERT_RECEIVED: client does not wish to -+ * renegotiate -+ */ -+ ssock->connection_state = TLS_STATE_HANDSHAKING; -+ status = tls_try_handshake(ssock); -+ -+ return status; -+ } else { -+ return tls_status_from_err(ssock, status); -+ } -+} -+ -+#endif /* PJ_HAS_SSL_SOCK */ ---- a/pjlib/src/pj/ssl_sock_ossl.c -+++ b/pjlib/src/pj/ssl_sock_ossl.c -@@ -32,8 +32,10 @@ - #include <pj/timer.h> - - --/* Only build when PJ_HAS_SSL_SOCK is enabled */ --#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK!=0 -+/* Only build when PJ_HAS_SSL_SOCK is enabled and when PJ_HAS_TLS_SOCK is -+ * disabled (meaning GnuTLS is off) */ -+#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \ -+ defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK == 0 - - #define THIS_FILE "ssl_sock_ossl.c" - ---- a/pjmedia/src/pjmedia/transport_srtp.c -+++ b/pjmedia/src/pjmedia/transport_srtp.c -@@ -30,7 +30,8 @@ - - #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) - --#if defined(PJ_HAS_SSL_SOCK) && (PJ_HAS_SSL_SOCK != 0) -+#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \ -+ defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK == 0 - # include <openssl/rand.h> - - /* Suppress compile warning of OpenSSL deprecation (OpenSSL is deprecated -@@ -1113,7 +1114,8 @@ static pj_status_t generate_crypto_attr_value(pj_pool_t *pool, - key_ok = PJ_TRUE; - - --#if defined(PJ_HAS_SSL_SOCK) && (PJ_HAS_SSL_SOCK != 0) -+#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \ -+ defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK == 0 - - /* Include OpenSSL libraries for MSVC */ - # ifdef _MSC_VER diff --git a/libraries/pjproject-ring/patches/pj_uwp_ice_sess.patch b/libraries/pjproject-ring/patches/pj_uwp_ice_sess.patch deleted file mode 100644 index ce4e0b4898..0000000000 --- a/libraries/pjproject-ring/patches/pj_uwp_ice_sess.patch +++ /dev/null @@ -1,25 +0,0 @@ ---- a/pjnath/include/pjnath/ice_strans.h
-+++ b/pjnath/include/pjnath/ice_strans.h
-@@ -930,6 +930,7 @@
- int dst_addr_len);
-
-
-+PJ_DECL(pj_ice_sess *) pj_ice_strans_get_ice_sess(pj_ice_strans *ice_st);
- /**
- * @}
- */
---- a/pjnath/src/pjnath/ice_strans.c
-+++ b/pjnath/src/pjnath/ice_strans.c
-@@ -1465,6 +1468,11 @@
- return PJ_EINVALIDOP;
- }
-
-+PJ_DECL(pj_ice_sess *) pj_ice_strans_get_ice_sess( pj_ice_strans *ice_st )
-+{
-+ return ice_st->ice;
-+}
-+
- /*
- * Callback called by ICE session when ICE processing is complete, either
- * successfully or with failure.
-
\ No newline at end of file diff --git a/libraries/pjproject-ring/patches/pj_uwp_multiple_listeners.patch b/libraries/pjproject-ring/patches/pj_uwp_multiple_listeners.patch deleted file mode 100644 index 893bcfbdca..0000000000 --- a/libraries/pjproject-ring/patches/pj_uwp_multiple_listeners.patch +++ /dev/null @@ -1,64 +0,0 @@ ---- a/pjsip/src/pjsip/sip_transport.c -+++ b/pjsip/src/pjsip/sip_transport.c -@@ -1248,22 +1248,22 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr, - - pj_lock_acquire(mgr->lock); - -- /* Check that no factory with the same type has been registered. */ -+ /* Check that no factory with the same type and bound address has been registered. */ - status = PJ_SUCCESS; - for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) { -- if (p->type == tpf->type) { -- status = PJSIP_ETYPEEXISTS; -- break; -- } -- if (p == tpf) { -- status = PJ_EEXISTS; -- break; -- } -+ 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; -+ } - } - - if (status != PJ_SUCCESS) { -- pj_lock_release(mgr->lock); -- return status; -+ pj_lock_release(mgr->lock); -+ return status; - } - - pj_list_insert_before(&mgr->factory_list, tpf); -@@ -2047,13 +2047,11 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, - pj_memcpy(&key.rem_addr, remote, addr_len); - - transport = (pjsip_transport*) -- pj_hash_get(mgr->table, &key, key_len, NULL); -- -+ pj_hash_get(mgr->table, &key, key_len, NULL); -+ unsigned flag = pjsip_transport_get_flag_from_type(type); - if (transport == NULL) { -- unsigned flag = pjsip_transport_get_flag_from_type(type); - const pj_sockaddr *remote_addr = (const pj_sockaddr*)remote; - -- - /* Ignore address for loop transports. */ - if (type == PJSIP_TRANSPORT_LOOP || - type == PJSIP_TRANSPORT_LOOP_DGRAM) -@@ -2135,6 +2135,11 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, - } - - } else { -+ /* Make sure we don't use another factory than the one given if secure flag is set */ -+ if (flag & PJSIP_TRANSPORT_SECURE) { -+ TRACE_((THIS_FILE, "Can't create new TLS transport with no provided suitable TLS listener.")); -+ return PJSIP_ETPNOTSUITABLE; -+ } - - /* Find factory with type matches the destination type */ - factory = mgr->factory_list.next; diff --git a/libraries/pjproject-ring/patches/pj_win.patch b/libraries/pjproject-ring/patches/pj_win.patch deleted file mode 100644 index c1c4c78f19..0000000000 --- a/libraries/pjproject-ring/patches/pj_win.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- pjproject/pjlib/include/pj/compat/string.h.orig 2014-09-08 16:17:36.471214560 -0400 -+++ pjproject/pjlib/include/pj/compat/string.h 2014-09-08 16:09:22.095207141 -0400 -@@ -43,7 +43,7 @@ - # include <stdlib.h> - #endif - --#if defined(_MSC_VER) -+#if defined(PJ_WIN32) - # define strcasecmp _stricmp - # define strncasecmp _strnicmp - # define snprintf _snprintf diff --git a/libraries/pjproject-ring/pjproject-ring.SlackBuild b/libraries/pjproject-ring/pjproject-ring.SlackBuild deleted file mode 100644 index d8bf842e34..0000000000 --- a/libraries/pjproject-ring/pjproject-ring.SlackBuild +++ /dev/null @@ -1,126 +0,0 @@ -#!/bin/sh - -# Slackware build script for pjproject-ring - -# Copyright 2017 Mario Preksavec, Zagreb, Croatia -# All rights reserved. -# -# Redistribution and use of this script, with or without modification, is -# permitted provided that the following conditions are met: -# -# 1. Redistributions of this script must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED -# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -PRGNAM=pjproject-ring -VERSION=${VERSION:-2.6} -BUILD=${BUILD:-1} -TAG=${TAG:-_SBo} - -SRCNAM=pjproject - -if [ -z "$ARCH" ]; then - case "$( uname -m )" in - i?86) ARCH=i586 ;; - arm*) ARCH=arm ;; - *) ARCH=$( uname -m ) ;; - esac -fi - -CWD=$(pwd) -TMP=${TMP:-/tmp/SBo} -PKG=$TMP/package-$PRGNAM -OUTPUT=${OUTPUT:-/tmp} - -if [ "$ARCH" = "i586" ]; then - SLKCFLAGS="-O2 -march=i586 -mtune=i686" - LIBDIRSUFFIX="" -elif [ "$ARCH" = "i686" ]; then - SLKCFLAGS="-O2 -march=i686 -mtune=i686" - LIBDIRSUFFIX="" -elif [ "$ARCH" = "x86_64" ]; then - SLKCFLAGS="-O2 -fPIC" - LIBDIRSUFFIX="64" -else - SLKCFLAGS="-O2" - LIBDIRSUFFIX="" -fi - -set -e - -rm -rf $PKG -mkdir -p $TMP $PKG $OUTPUT -cd $TMP -rm -rf $SRCNAM-$VERSION -tar xvf $CWD/$SRCNAM-$VERSION.tar.bz2 -cd $SRCNAM-$VERSION -chown -R root:root . -find -L . \ - \( -perm 777 -o -perm 775 -o -perm 750 -o -perm 711 -o -perm 555 \ - -o -perm 511 \) -exec chmod 755 {} \; -o \ - \( -perm 666 -o -perm 664 -o -perm 640 -o -perm 600 -o -perm 444 \ - -o -perm 440 -o -perm 400 \) -exec chmod 644 {} \; - -for patch in gnutls notestsapps fix_base64 ipv6 ice_config \ - multiple_listeners pj_ice_sess fix_turn_fallback \ - fix_ioqueue_ipv6_sendto add_dtls_transport; do - patch -p1 -l <$CWD/patches/$patch.patch -done - -PJFLAGS="-DPJ_ICE_MAX_CAND=256 - -DPJ_ICE_MAX_CHECKS=150 - -DPJ_ICE_COMP_BITS=2 - -DPJ_ICE_MAX_STUN=3 - -DPJSIP_MAX_PKT_LEN=8000 - -DPJ_ICE_ST_MAX_CAND=32" - -CFLAGS="$SLKCFLAGS -g $PJFLAGS" \ -CXXFLAGS="$SLKCFLAGS -g $PJFLAGS -std=gnu++11" \ -./aconfigure \ - --prefix=/usr \ - --libdir=/usr/lib${LIBDIRSUFFIX} \ - --disable-oss \ - --disable-sound \ - --disable-video \ - --enable-ext-sound \ - --disable-speex-aec \ - --disable-g711-codec \ - --disable-l16-codec \ - --disable-gsm-codec \ - --disable-g722-codec \ - --disable-g7221-codec \ - --disable-speex-codec \ - --disable-ilbc-codec \ - --disable-opencore-amr \ - --disable-silk \ - --disable-sdl \ - --disable-ffmpeg \ - --disable-v4l2 \ - --disable-openh264 \ - --disable-resample \ - --disable-libwebrtc \ - --enable-ssl=gnutls \ - --build=$ARCH-slackware-linux - -make -make install DESTDIR=$PKG - -mkdir -p $PKG/usr/doc/$PRGNAM-$VERSION -cp -a COPYING README-RTEMS README.txt $PKG/usr/doc/$PRGNAM-$VERSION -cat $CWD/$PRGNAM.SlackBuild > $PKG/usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild - -mkdir -p $PKG/install -cat $CWD/slack-desc > $PKG/install/slack-desc - -cd $PKG -/sbin/makepkg -l y -c n $OUTPUT/$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.${PKGTYPE:-tgz} diff --git a/libraries/pjproject-ring/pjproject-ring.info b/libraries/pjproject-ring/pjproject-ring.info deleted file mode 100644 index 6c60d0c8bb..0000000000 --- a/libraries/pjproject-ring/pjproject-ring.info +++ /dev/null @@ -1,10 +0,0 @@ -PRGNAM="pjproject-ring" -VERSION="2.6" -HOMEPAGE="https://www.pjsip.org/" -DOWNLOAD="https://www.pjsip.org/release/2.6/pjproject-2.6.tar.bz2" -MD5SUM="c347a672679e7875ce572e18517884b2" -DOWNLOAD_x86_64="" -MD5SUM_x86_64="" -REQUIRES="" -MAINTAINER="Mario Preksavec" -EMAIL="mario at slackware dot hr" diff --git a/libraries/pjproject-ring/slack-desc b/libraries/pjproject-ring/slack-desc deleted file mode 100644 index b700136cf4..0000000000 --- a/libraries/pjproject-ring/slack-desc +++ /dev/null @@ -1,19 +0,0 @@ -# HOW TO EDIT THIS FILE: -# The "handy ruler" below makes it easier to edit a package description. -# Line up the first '|' above the ':' following the base package name, and -# the '|' on the right side marks the last column you can put a character in. -# You must make exactly 11 lines for the formatting to be correct. It's also -# customary to leave one space after the ':' except on otherwise blank lines. - - |-----handy-ruler------------------------------------------------------| -pjproject-ring: pjproject-ring (Multimedia Communication Library) -pjproject-ring: -pjproject-ring: PJSIP is a free and open source multimedia communication library -pjproject-ring: written in C language implementing standard based protocols such as -pjproject-ring: SIP, SDP, RTP, STUN, TURN, and ICE. -pjproject-ring: -pjproject-ring: This package includes patches from Ring project. -pjproject-ring: -pjproject-ring: Homepage: https://www.pjsip.org/ -pjproject-ring: -pjproject-ring: |