turnserver-3.2.3.1/000755 001751 001751 00000000000 12315706777 014034 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/turndb/000755 001751 001751 00000000000 12315706777 015332 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/LICENSE.OpenSSL000644 001751 001751 00000014207 12315706777 016327 0ustar00olegoleg000000 000000 LICENSE ISSUES ============== The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit. See below for the actual license texts. Actually both licenses are BSD-style Open Source licenses. In case of any license issues related to OpenSSL please contact openssl-core@openssl.org. OpenSSL License --------------- /* ==================================================================== * Copyright (c) 1998-2008 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * openssl-core@openssl.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.openssl.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED 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 OpenSSL PROJECT OR * ITS CONTRIBUTORS 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. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). * */ Original SSLeay License ----------------------- /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@cryptsoft.com). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@cryptsoft.com). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * "This product includes cryptographic software written by * Eric Young (eay@cryptsoft.com)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 OR CONTRIBUTORS 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. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] */ turnserver-3.2.3.1/LICENSE000644 001751 001751 00000003134 12315706777 015042 0ustar00olegoleg000000 000000 /* * TURN Server - RFC5766 TURN Server implementation * Copyright (C) 2011, 2012, 2013, 2014 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ turnserver-3.2.3.1/configure000755 001751 001751 00000065627 12315706777 015763 0ustar00olegoleg000000 000000 #!/bin/sh # Proprietary configure script of RFC5766-TURN-Server project cleanup() { rm -rf ${TMPCPROGC} rm -rf ${TMPCPROGB} rm -rf ${TH_TMPCPROGC} rm -rf ${TH_TMPCPROGB} rm -rf ${DTLS_TMPCPROGC} rm -rf ${DTLS_TMPCPROGB} rm -rf ${PQ_TMPCPROGC} rm -rf ${PQ_TMPCPROGB} rm -rf ${MYSQL_TMPCPROGC} rm -rf ${MYSQL_TMPCPROGB} rm -rf ${D_TMPCPROGC} rm -rf ${D_TMPCPROGB} rm -rf ${E_TMPCPROGC} rm -rf ${E_TMPCPROGO} rm -rf ${HR_TMPCPROGC} rm -rf ${HR_TMPCPROGB} rm -rf ${TMPCADDRPROGO} } testlibraw() { ${CC} ${TMPCPROGC} -o ${TMPCPROGB} ${OSCFLAGS} ${OSLIBS} -${1} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Do not use -${1}" return 0 else OSLIBS="${OSLIBS} -${1}" return 1 fi } testlibevent2_comp() { ${CC} -c ${E_TMPCPROGC} -o ${E_TMPCPROGO} ${OSCFLAGS} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Libevent2 development is not installed properly" return 0 else return 1 fi } testhiredis() { HIREDISCFLAGS= HIREDISLIBS=-lhiredis ${CC} ${HR_TMPCPROGC} -o ${HR_TMPCPROGB} ${OSCFLAGS} ${DBLIBS} ${HIREDISCFLAGS} ${HIREDISLIBS} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} ${ECHO_CMD} "HIREDIS DEVELOPMENT LIBRARY (libhiredis.*) AND/OR HEADERS (hiredis/*.h)" ${ECHO_CMD} " ARE NOT INSTALLED PROPERLY ON THIS SYSTEM." ${ECHO_CMD} " THAT'S OK BUT THE TURN SERVER IS BUILDING WITHOUT REDIS SUPPORT." ${ECHO_CMD} return 0 else DBCFLAGS="${DBCFLAGS} ${HIREDISCFLAGS}" DBLIBS="${DBLIBS} ${HIREDISLIBS}" return 1 fi } testlibpq() { POSTCFLAGS="-I${PREFIX}/pgsql/include -I${PREFIX}/include/pgsql/ -I${PREFIX}/include/postgres/ -I${PREFIX}/postgres/include/ -I${PREFIX}/include/postgresql/ -I${PREFIX}/postgresql/include/" POSTCFLAGS="${POSTCFLAGS} -I/usr/local/pgsql/include -I/usr/local/include/pgsql/ -I/usr/local/include/postgres/ -I/usr/local/postgres/include/ -I/usr/local/include/postgresql/ -I/usr/local/postgresql/include/" POSTCFLAGS="${POSTCFLAGS} -I/usr/pgsql/include -I/usr/include/pgsql/ -I/usr/include/postgres/ -I/usr/postgres/include/ -I/usr/include/postgresql/ -I/usr/postgresql/include/" for ilib in ${PREFIX}/pgsql/lib ${PREFIX}/lib/pgsql ${PREFIX}/lib64/pgsql /usr/local/pgsql/lib /usr/local/lib/pgsql /usr/local/lib64/pgsql /usr/pgsql/lib /usr/lib/pgsql /usr/lib64/pgsql ${PREFIX}/postgres/lib ${PREFIX}/lib/postgres ${PREFIX}/lib64/postgres /usr/local/postgres/lib /usr/local/lib/postgres /usr/local/lib64/postgres /usr/postgres/lib /usr/lib/postgres /usr/lib64/postgres ${PREFIX}/postgresql/lib ${PREFIX}/lib/postgresql ${PREFIX}/lib64/postgresql /usr/local/postgresql/lib /usr/local/lib/postgresql /usr/local/lib64/postgresql /usr/postgresql/lib /usr/lib/postgresql /usr/lib64/postgresql do if [ -d ${ilib} ] ; then POSTLIBS="${POSTLIBS} -L${ilib}" if ! [ -z "${TURN_ACCEPT_RPATH}" ] ; then TURN_RPATH="${TURN_RPATH} -Wl,-rpath,${ilib}" fi fi done POSTLIBS="${OSLIBS} ${POSTLIBS} -lpq" ${CC} ${PQ_TMPCPROGC} -o ${PQ_TMPCPROGB} ${OSCFLAGS} ${DBCFLAGS} ${POSTCFLAGS} ${DBLIBS} ${POSTLIBS} ${OSLIBS} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} ${ECHO_CMD} "POSTGRESQL DEVELOPMENT LIBRARY (libpq.a) AND/OR HEADER (libpq-fe.h)" ${ECHO_CMD} " ARE NOT INSTALLED PROPERLY ON THIS SYSTEM." ${ECHO_CMD} " THAT'S OK BUT THE TURN SERVER IS BUILDING WITHOUT POSTGRESQL DATABASE SUPPORT." ${ECHO_CMD} return 0 else DBCFLAGS="${DBCFLAGS} ${POSTCFLAGS}" DBLIBS="${DBLIBS} ${POSTLIBS}" return 1 fi } testlibmysql() { MYSQL_CFLAGS="-I${PREFIX}/mysql/include -I${PREFIX}/include/mysql/" MYSQL_CFLAGS="${MYSQL_CFLAGS} -I/usr/local/mysql/include -I/usr/local/include/mysql/" MYSQL_CFLAGS="${MYSQL_CFLAGS} -I/usr/mysql/include -I/usr/include/mysql/" for ilib in ${PREFIX}/mysql/lib ${PREFIX}/lib/mysql ${PREFIX}/lib64/mysql /usr/local/mysql/lib /usr/local/lib/mysql /usr/local/lib64/mysql /usr/mysql/lib /usr/lib/mysql /usr/lib64/mysql do if [ -d ${ilib} ] ; then MYSQL_LIBS="${MYSQL_LIBS} -L${ilib}" if ! [ -z "${TURN_ACCEPT_RPATH}" ] ; then TURN_RPATH="${TURN_RPATH} -Wl,-rpath,${ilib}" fi fi done MYSQL_LIBS="${OSLIBS} ${MYSQL_LIBS} -lmysqlclient" ${CC} ${MYSQL_TMPCPROGC} -o ${MYSQL_TMPCPROGB} ${OSCFLAGS} ${DBCFLAGS} ${DBLIBS} ${MYSQL_CFLAGS} ${MYSQL_LIBS} ${OSLIBS} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} ${ECHO_CMD} "MYSQL DEVELOPMENT LIBRARY (libmysqlclient) AND/OR HEADER (mysql.h)" ${ECHO_CMD} " ARE NOT INSTALLED PROPERLY ON THIS SYSTEM." ${ECHO_CMD} " THAT'S OK BUT THE TURN SERVER IS BUILDING WITHOUT MYSQL DATABASE SUPPORT." ${ECHO_CMD} return 0 else DBCFLAGS="${DBCFLAGS} ${MYSQL_CFLAGS}" DBLIBS="${DBLIBS} ${MYSQL_LIBS}" return 1 fi } testlib() { testlibraw l${1} } pthread_testlib() { SYSTEM=`uname` if [ "${SYSTEM}" = "DragonFly" ] ; then OSLIBS="${OSLIBS} -pthread" fi ISBSD=`uname | grep -i bsd` if ! [ -z "${ISBSD}" ] ; then OSLIBS="${OSLIBS} -pthread" fi if [ -z "${PTHREAD_LIBS}" ] ; then ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then return 1 else ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} -pthread 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then OSLIBS="${OSLIBS} -pthread" return 1 else ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} -lpthread 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then OSLIBS="${OSLIBS} -lpthread" return 1 fi fi fi else OSLIBS="${OSLIBS} ${PTHREAD_LIBS}" fi ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then return 1 else ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} -D_GNU_SOURCE 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Older GNU pthread library found" OSCFLAGS="${OSCFLAGS} -D_GNU_SOURCE" return 1 else ${ECHO_CMD} "Do not use pthreads" fi fi return 0 } pthread_testbarriers() { ${ECHO_CMD} "pthread_barrier_t barrier;" >> ${TH_TMPCPROGC} ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "pthread barriers not found" OSCFLAGS="${OSCFLAGS} -DTURN_NO_THREAD_BARRIERS" fi } dtls_testlib() { if [ -z "${TURN_NO_DTLS}" ] ; then ${CC} ${DTLS_TMPCPROGC} -o ${DTLS_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then return 1 else return 0 fi else return 0 fi } testdaemon() { ${CC} ${D_TMPCPROGC} -o ${D_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then OSCFLAGS="${OSCFLAGS} -DTURN_HAS_DAEMON" fi } test_sin_len() { TMPCADDRPROGC=src/client/ns_turn_ioaddr.c ${CC} -c ${OSCFLAGS} -DTURN_HAS_SIN_LEN -Isrc ${TMPCADDRPROGC} -o ${TMPCADDRPROGO} 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then OSCFLAGS="${OSCFLAGS} -DTURN_HAS_SIN_LEN" ${ECHO_CMD} "Sockets code is fine: sin_len field present" else ${CC} -c ${OSCFLAGS} -Isrc ${TMPCADDRPROGC} -o ${TMPCADDRPROGO} 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Sockets code is fine: no sin_len field present" else ${ECHO_CMD} "WARNING: trial compilation failed: src/client/ns_turn_ioaddr.c" fi fi } ######################### # Start ######################### cleanup ######################### # To be set: ######################### if [ -z "${ECHO_CMD}" ] ; then ECHO_CMD=echo fi if [ -z "${PORTNAME}" ] ; then PORTNAME=turnserver fi ######################### # Installation directory options ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=BINDIR ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) BINDIR=$ac_optarg ;; -datadir | --datadir | --datadi | --datad | -schemadir | --schemadir) ac_prev=SCHEMADIR ;; -datadir=* | --datadir=* | --datadi=* | --datad=* | -schemadir=* | --schemadir=*) SCHEMADIR=$ac_optarg ;; -docdir | --docdir | --docdi | --doc | --do | -docsdir | --docsdir | --docsdi | --docs) ac_prev=DOCDIR ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=* | -docsdir=* | --docsdir=* | --docsdi=* | --docs=*) DOCSDIR=$ac_optarg ;; -examplesdir | --examplesdir | -examples | --examples) ac_prev=EXAMPLESDIR ;; -examplesdir=* | --examplesdir=* | -examples=* | --examples=*) EXAMPLESDIR=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=INCLUDEDIR ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) INCLUDEDIR=$ac_optarg ;; -turnincludedir | --turnincludedir) ac_prev=TURNINCLUDEDIR ;; -turnincludedir=* | --turnincludedir=*) TURNINCLUDEDIR=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=LIBDIR ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) LIBDIR=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m | -manprefix | --manprefix) ac_prev=MAXPREFIX ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=* | -manprefix=* | --manprefix=*) MANPREFIX=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=PREFIX ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) PREFIX=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy | -confdir | --confdir) ac_prev=CONFDIR ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=* | -confdir=* | --confdir=*) CONFDIR=$ac_optarg ;; -disable-rpath | --disable-rpath) TURN_DISABLE_RPATH=1 ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi ############################################# if [ -z "${PREFIX}" ] ; then if [ -z "${prefix}" ] ; then SYSTEM=`uname` if [ "${SYSTEM}" = "NetBSD" ] ; then # A little tough guy PREFIX=/usr/pkg elif [ "${SYSTEM}" = "SunOS" ] ; then # A fat guy PREFIX=/usr else # An ordinary person PREFIX=/usr/local fi else PREFIX=${prefix} fi fi if [ -z "${BINDIR}" ] ; then if [ -z "${bindir}" ] ; then BINDIR=${PREFIX}/bin else BINDIR=${bindir} fi fi if [ -z "${CONFDIR}" ] ; then if [ -z "${confdir}" ] ; then CONFDIR=${PREFIX}/etc else CONFDIR=${confdir} fi fi if [ -z "${MANPREFIX}" ] ; then if [ -z "${manprefix}" ] ; then MANPREFIX=${PREFIX} else MANPREFIX=${manprefix} fi fi if [ -z "${EXAMPLESDIR}" ] ; then if [ -z "${examplesdir}" ] ; then EXAMPLESDIR=${PREFIX}/share/examples/${PORTNAME} else EXAMPLESDIR=${examplesdir} fi fi if [ -z "${DOCSDIR}" ] ; then if [ -z "${docsdir}" ] ; then DOCSDIR=${PREFIX}/share/doc/${PORTNAME} else DOCSDIR=${docsdir} fi fi if [ -z "${LIBDIR}" ] ; then if [ -z "${libdir}" ] ; then LIBDIR=${PREFIX}/lib else LIBDIR=${libdir} fi fi if [ -z "${SCHEMADIR}" ] ; then if [ -z "${schemadir}" ] ; then SCHEMADIR=${PREFIX}/share/${PORTNAME} else SCHEMADIR=${schemadir} fi fi if [ -z "${INCLUDEDIR}" ] ; then if [ -z "${includedir}" ] ; then INCLUDEDIR=${PREFIX}/include else INCLUDEDIR=${includedir} fi fi if [ -z "${TURNINCLUDEDIR}" ] ; then if [ -z "${turnincludedir}" ] ; then TURNINCLUDEDIR=${INCLUDEDIR}/turn else TURNINCLUDEDIR=${turnincludedir} fi fi ############################################### if [ -z "${ARCHIVERCMD}" ] ; then ARCHIVERCMD="ar -r" fi if [ -z "${MORECMD}" ]; then type more 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then MORECMD="more" else type less 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then MORECMD="less" else MORECMD="cat" fi fi fi OSCFLAGS="-I${INCLUDEDIR} -I${PREFIX}/include/ -I/usr/local/include ${CFLAGS}" OSLIBS="${LDFLAGS}" for ilib in ${PREFIX}/lib/event2/ ${PREFIX}/lib/ /usr/local/lib/event2/ /usr/local/lib/ ${PREFIX}/lib64/event2/ ${PREFIX}/lib64/ /usr/local/lib64/event2/ /usr/local/lib64/ do if [ -d ${ilib} ] ; then OSLIBS="${OSLIBS} -L${ilib}" TURN_RPATH="${TURN_RPATH} -Wl,-rpath,${ilib}" fi done SYSTEM=`uname` if [ "${SYSTEM}" = "NetBSD" ] ; then OSCFLAGS="${OSCFLAGS} -I/usr/pkg/include" OSLIBS="-L/usr/pkg/lib ${OSLIBS}" if ! [ -z "${TURN_ACCEPT_RPATH}" ] ; then TURN_RPATH="${TURN_RPATH} -Wl,-rpath,/usr/pkg/lib" fi fi ########################### # Install shell commands ########################### type ginstall 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then INSTALL_PROGRAM="ginstall" INSTALL_MAN="ginstall" INSTALL_SCRIPT="ginstall" INSTALL_SHARED_LIB="ginstall" INSTALL_STATIC_LIB="ginstall" INSTALL_DATA="ginstall" MKDIR="ginstall -d" else type install 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then INSTALL_PROGRAM="install" INSTALL_MAN="install" INSTALL_SCRIPT="install" INSTALL_SHARED_LIB="install" INSTALL_STATIC_LIB="install" INSTALL_DATA="install" MKDIR="install -d" else INSTALL_PROGRAM="cp -pf" INSTALL_MAN="cp -pf" INSTALL_SCRIPT="cp -pf" INSTALL_SHARED_LIB="cp -pf" INSTALL_STATIC_LIB="cp -pf" INSTALL_DATA="cp -pf" MKDIR="mkdir -p" fi fi type pkill 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then PKILL_PROGRAM="pkill" else PKILL_PROGRAM="${ECHO_CMD}" fi INSTALL_DIR="cp -rpf" MKBUILDDIR="mkdir -p" RMCMD="rm -rf" ############################# # Adjustments for Solaris ############################# SYSTEM=`uname` if [ "${SYSTEM}" = "SunOS" ] ; then # Solaris ? is this you ?! OSCFLAGS="${OSCFLAGS} -D__EXTENSIONS__ -D_XOPEN_SOURCE=500" fi ######################### # Temporary DIR location: ######################### TMPDIR="." if [ -d /var/tmp ] ; then TMPDIR="/var/tmp" elif [ -d /tmp ] ; then TMPDIR=/tmp fi ${ECHO_CMD} Use TMP dir ${TMPDIR} ######################### # Basic C test programs ######################### TMPCADDRPROGO=${TMPDIR}/__test__ccomp_addr_$$.o TMPCPROG=__test__ccomp__$$ TMPCPROGC=${TMPDIR}/${TMPCPROG}.c TMPCPROGB=${TMPDIR}/${TMPCPROG} cat > ${TMPCPROGC} < int main(int argc, char** argv) { return (int)(argv[argc][0]); } ! TH_TMPCPROG=__test__ccomp__pthread__$$ TH_TMPCPROGC=${TMPDIR}/${TH_TMPCPROG}.c TH_TMPCPROGB=${TMPDIR}/${TH_TMPCPROG} cat > ${TH_TMPCPROGC} < #include int main(int argc, char** argv) { pthread_mutexattr_settype(0,PTHREAD_MUTEX_RECURSIVE); return (int)pthread_create(0,0,0,0)+(int)(argv[argc][0]); } ! DTLS_TMPCPROG=__test__ccomp__dtls__$$ DTLS_TMPCPROGC=${TMPDIR}/${DTLS_TMPCPROG}.c DTLS_TMPCPROGB=${TMPDIR}/${DTLS_TMPCPROG} cat > ${DTLS_TMPCPROGC} < #include #include int main(int argc, char** argv) { return (((int)(BIO_CTRL_DGRAM_QUERY_MTU)) + argc + (int)(argv[argc][0]) + DTLSv1_listen(NULL,NULL)); } ! D_TMPCPROG=__test__ccomp__daemon__$$ D_TMPCPROGC=${TMPDIR}/${D_TMPCPROG}.c D_TMPCPROGB=${TMPDIR}/${D_TMPCPROG} cat > ${D_TMPCPROGC} < #include int main(int argc, char** argv) { return (int)daemon(0,0)+(int)(argv[argc][0]); } ! E_TMPCPROG=__test__ccomp__libevent2__$$ E_TMPCPROGC=${TMPDIR}/${E_TMPCPROG}.c E_TMPCPROGO=${TMPDIR}/${E_TMPCPROG}.o cat > ${E_TMPCPROGC} < #include int main(int argc, char** argv) { return (int)(argv[argc][0]); } ! HR_TMPCPROG=__test__ccomp__hiredis__$$ HR_TMPCPROGC=${TMPDIR}/${HR_TMPCPROG}.c HR_TMPCPROGB=${TMPDIR}/${HR_TMPCPROG} cat > ${HR_TMPCPROGC} < #include #include int main(int argc, char** argv) { redisAsyncHandleRead(NULL); return (int)(argv[argc][0]); } ! PQ_TMPCPROG=__test__ccomp__libpq__$$ PQ_TMPCPROGC=${TMPDIR}/${PQ_TMPCPROG}.c PQ_TMPCPROGB=${TMPDIR}/${PQ_TMPCPROG} cat > ${PQ_TMPCPROGC} < #include int main(int argc, char** argv) { return (argc+(PQprotocolVersion(NULL))+(int)(argv[0][0])); } ! MYSQL_TMPCPROG=__test__ccomp__libmysql__$$ MYSQL_TMPCPROGC=${TMPDIR}/${MYSQL_TMPCPROG}.c MYSQL_TMPCPROGB=${TMPDIR}/${MYSQL_TMPCPROG} cat > ${MYSQL_TMPCPROGC} < #include int main(int argc, char** argv) { return (argc+ (int)(mysql_real_connect(NULL, NULL, NULL, NULL, NULL, 0, NULL, 0)!=0)+ (int)(argv[0][0])); } ! ########################## # What is our compiler ? ########################## if [ -z "${CC}" ] ; then CC=cc ${CC} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then CC=gcc ${CC} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then CC=clang ${CC} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then CC=unknown fi fi fi fi ${ECHO_CMD} "Compiler: ${CC}" if [ -z "${TURN_ACCEPT_RPATH}" ] ; then ${CC} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} -Wl,-rpath,/usr/lib 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then TURN_ACCEPT_RPATH=1 fi fi ${CC} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "ERROR: cannot use compiler ${CC} properly" cleanup exit fi ########################### # Check if we can use GNU # or Clang compiler flags ########################### GNUOSCFLAGS="-g ${GNUOSCFLAGS}" GNUOSCFLAGS="${GNUOSCFLAGS} -Wall -Wno-deprecated-declarations -Wextra -Wformat-security -Wnested-externs -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wcast-qual" GNUOSCFLAGS="${GNUOSCFLAGS} -Wno-write-strings" ${CC} -Werror ${GNUOSCFLAGS} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Hm..." ${CC} -Wall ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Not an ordinary GNU or Clang compiler" else ${ECHO_CMD} "g++ or something..." GNUOSCFLAGS="-g -Wall -Wno-deprecated-declarations -Wextra -Wformat-security -Wpointer-arith -Wcast-qual -Wno-write-strings" ${CC} -Werror ${GNUOSCFLAGS} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Not an ordinary g++ compiler" GNUOSCFLAGS="-x c++ -g -Wall -Wno-deprecated-declarations -Wextra -Wformat-security -Wpointer-arith -Wcast-qual -Wno-write-strings" ${CC} -Werror ${GNUOSCFLAGS} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Not an ordinary c++ compiler" else ${ECHO_CMD} "Clang++ compiler ?" OSCFLAGS="${GNUOSCFLAGS} ${OSCFLAGS}" fi else OSCFLAGS="${GNUOSCFLAGS} ${OSCFLAGS}" fi fi else OSCFLAGS="${GNUOSCFLAGS} ${OSCFLAGS}" fi ########################### # Test some general-purpose # libraries ########################### testlib socket testlib nsl testlib dl testlib rt testlib wldap32 ER=$? if ! [ ${ER} -eq 0 ] ; then echo "CYGWIN ?" fi testlib wldap64 testlib intl ########################### # Test sockets compilation ########################### test_sin_len ########################### # Can we use multi-threading ? ########################### pthread_testlib ER=$? if [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "ERROR: Cannot find pthread library functions." exit fi if [ -z ${TURN_NO_THREAD_BARRIERS} ] ; then pthread_testbarriers else TURN_NO_THREAD_BARRIERS="-DTURN_NO_THREAD_BARRIERS" fi if [ -z ${TURN_IP_RECVERR} ] ; then ${ECHO_CMD} "Ignore IP_RECVERR" else ${ECHO_CMD} "Use IP_RECVERR" TURN_IP_RECVERR="-DTURN_IP_RECVERR" OSCFLAGS="${OSCFLAGS} ${TURN_IP_RECVERR}" fi ########################### # Can we use daemon ? ########################### testdaemon ########################### # Test OpenSSL installation ########################### testlib crypto ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Crypto SSL lib found." else ${ECHO_CMD} "ERROR: OpenSSL Crypto development libraries are not installed properly in required location." ${ECHO_CMD} "Abort." cleanup exit fi testlib ssl ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "SSL lib found." else ${ECHO_CMD} "ERROR: OpenSSL development libraries are not installed properly in required location." ${ECHO_CMD} "Abort." cleanup exit fi ########################### # Can we use DTLS ? ########################### if [ -z ${TURN_NO_DTLS} ] ; then dtls_testlib ER=$? if [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "WARNING: Cannot find DTLS support." ${ECHO_CMD} "Turning DTLS off." TURN_NO_DTLS="-DTURN_NO_DTLS" fi else TURN_NO_DTLS="-DTURN_NO_DTLS" fi ########################### # Test Libevent2 setup ########################### testlibevent2_comp ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Libevent2 development found." else ${ECHO_CMD} "ERROR: Libevent2 development libraries are not installed properly in required location." ${ECHO_CMD} "ERROR: may be you have just too old libevent tool - then you have to upgrade it." ${ECHO_CMD} "See the INSTALL file." ${ECHO_CMD} "Abort." cleanup exit fi testlib event_core ER=$? if ! [ ${ER} -eq 0 ] ; then testlib event_extra ${ECHO_CMD} "Libevent2 runtime found." else testlib event ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Libevent2 runtime found." else ${ECHO_CMD} "ERROR: Libevent2 runtime libraries are not installed properly in required location." ${ECHO_CMD} "See the INSTALL file." ${ECHO_CMD} "Abort." cleanup exit fi fi if [ -z "${TURN_NO_TLS}" ] ; then testlib event_openssl ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Libevent2 openssl found." else ${ECHO_CMD} "ERROR: Libevent2 development libraries are not compiled with OpenSSL support." ${ECHO_CMD} "TLS will be disabled." TURN_NO_TLS="-DTURN_NO_TLS" fi else TURN_NO_TLS="-DTURN_NO_TLS" fi testlib event_pthreads ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Libevent2 pthreads found." else ${ECHO_CMD} "ERROR: Libevent2 development libraries are not compiled with threads support." exit fi ########################### # Test PostgreSQL ########################### if [ -z "${TURN_NO_PQ}" ] ; then testlibpq ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "PostgreSQL found." else TURN_NO_PQ="-DTURN_NO_PQ" fi else TURN_NO_PQ="-DTURN_NO_PQ" fi ########################### # Test MySQL ########################### if [ -z "${TURN_NO_MYSQL}" ] ; then testlibmysql ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "MySQL found." else TURN_NO_MYSQL="-DTURN_NO_MYSQL" fi else TURN_NO_MYSQL="-DTURN_NO_MYSQL" fi ########################### # Test Redis ########################### if [ -z "${TURN_NO_HIREDIS}" ] ; then testhiredis ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Hiredis found." else TURN_NO_HIREDIS="-DTURN_NO_HIREDIS" fi else TURN_NO_HIREDIS="-DTURN_NO_HIREDIS" fi ############################### # LDCONFIG ############################### if [ -z "${LDCONFIG}" ] ; then ISBSD=`uname | grep -i bsd` if [ -z "${ISBSD}" ] ; then ISLINUX=`uname | grep -i linux` if [ -z "${ISLINUX}" ] ; then SYSTEM=`uname` if [ "${SYSTEM}" = "SunOS" ] ; then LDCONFIG="crle -u -l" else LDCONFIG=${ECHO_CMD} fi else LDCONFIG="ldconfig -n" fi else LDCONFIG="ldconfig -m" fi fi ############################### # So, what we have now: ############################### OSCFLAGS="${OSCFLAGS} ${TURN_NO_THREAD_BARRIERS} ${TURN_NO_DTLS} ${TURN_NO_TLS} -DINSTALL_PREFIX=${PREFIX}" if ! [ -z "${TURN_ACCEPT_RPATH}" ] ; then if [ -z "${TURN_DISABLE_RPATH}" ] ; then TURN_RPATH="${TURN_RPATH} -Wl,-rpath,/usr/local/lib" OSLIBS="${OSLIBS} ${TURN_RPATH}" fi fi ${ECHO_CMD} PREFIX="${PREFIX}" OSLIBS="${OSLIBS}" DBLIBS="${DBLIBS}" OSCFLAGS="${OSCFLAGS}" DBCFLAGS="${DBCFLAGS}" $@ ############################### # Make make: ############################### ${ECHO_CMD} "#################################" > Makefile ${ECHO_CMD} "# Generated by configure script #" >> Makefile ${ECHO_CMD} "#################################" >> Makefile ${ECHO_CMD} "ECHO_CMD = ${ECHO_CMD}" >> Makefile ${ECHO_CMD} "CC = ${CC}" >> Makefile ${ECHO_CMD} "LDFLAGS += ${OSLIBS}" >> Makefile ${ECHO_CMD} "DBLIBS += ${DBLIBS}" >> Makefile ${ECHO_CMD} "CFLAGS += ${OSCFLAGS}" >> Makefile ${ECHO_CMD} "CPPFLAGS = ${CPPFLAGS}" >> Makefile ${ECHO_CMD} "DBCFLAGS += ${DBCFLAGS} ${TURN_NO_PQ} ${TURN_NO_MYSQL} ${TURN_NO_HIREDIS}" >> Makefile ${ECHO_CMD} "#" >> Makefile ${ECHO_CMD} "PORTNAME = ${PORTNAME}" >> Makefile ${ECHO_CMD} "PREFIX = ${PREFIX}" >> Makefile ${ECHO_CMD} "prefix = ${PREFIX}" >> Makefile ${ECHO_CMD} "BINDIR = ${BINDIR}" >> Makefile ${ECHO_CMD} "bindir = ${BINDIR}" >> Makefile ${ECHO_CMD} "CONFDIR = ${CONFDIR}" >> Makefile ${ECHO_CMD} "confdir = ${CONFDIR}" >> Makefile ${ECHO_CMD} "MANPREFIX = ${MANPREFIX}" >> Makefile ${ECHO_CMD} "manprefix = ${MANPREFIX}" >> Makefile ${ECHO_CMD} "EXAMPLESDIR = ${EXAMPLESDIR}" >> Makefile ${ECHO_CMD} "examplesdir = ${EXAMPLESDIR}" >> Makefile ${ECHO_CMD} "DOCSDIR = ${DOCSDIR}" >> Makefile ${ECHO_CMD} "docsdir = ${DOCSDIR}" >> Makefile ${ECHO_CMD} "LIBDIR = ${LIBDIR}" >> Makefile ${ECHO_CMD} "libdir = ${LIBDIR}" >> Makefile ${ECHO_CMD} "SCHEMADIR = ${SCHEMADIR}" >> Makefile ${ECHO_CMD} "schemadir = ${SCHEMADIR}" >> Makefile ${ECHO_CMD} "INCLUDEDIR = ${INCLUDEDIR}" >> Makefile ${ECHO_CMD} "includedir = ${INCLUDEDIR}" >> Makefile ${ECHO_CMD} "TURNINCLUDEDIR = ${TURNINCLUDEDIR}" >> Makefile ${ECHO_CMD} "turnincludedir = ${TURNINCLUDEDIR}" >> Makefile ${ECHO_CMD} "#" >> Makefile ${ECHO_CMD} "ARCHIVERCMD = ${ARCHIVERCMD}" >> Makefile ${ECHO_CMD} "MKDIR = ${MKDIR}" >> Makefile ${ECHO_CMD} "INSTALL_PROGRAM = ${INSTALL_PROGRAM}" >> Makefile ${ECHO_CMD} "PKILL_PROGRAM = ${PKILL_PROGRAM}" >> Makefile ${ECHO_CMD} "INSTALL_MAN = ${INSTALL_MAN}" >> Makefile ${ECHO_CMD} "INSTALL_SCRIPT = ${INSTALL_SCRIPT}" >> Makefile ${ECHO_CMD} "INSTALL_SHARED_LIB = ${INSTALL_SHARED_LIB}" >> Makefile ${ECHO_CMD} "INSTALL_STATIC_LIB = ${INSTALL_STATIC_LIB}" >> Makefile ${ECHO_CMD} "INSTALL_DATA = ${INSTALL_DATA}" >> Makefile ${ECHO_CMD} "INSTALL_DIR = ${INSTALL_DIR}" >> Makefile ${ECHO_CMD} "MKBUILDDIR = ${MKBUILDDIR}" >> Makefile ${ECHO_CMD} "RMCMD = ${RMCMD}" >> Makefile ${ECHO_CMD} "MORECMD = ${MORECMD}" >> Makefile ${ECHO_CMD} "LDCONFIG=${LDCONFIG}" >> Makefile ${ECHO_CMD} "################################" >> Makefile ${ECHO_CMD} "" >> Makefile cat Makefile.in >> Makefile ############################### # End: ############################### cleanup turnserver-3.2.3.1/ChangeLog000644 001751 001751 00000067403 12315706777 015620 0ustar00olegoleg000000 000000 03/29/2014 Oleg Moskalenko Version 3.2.3.1 'Marshal West': - TCP congestion avoidance completed. - Read and write streams are treated separately in bandwidth control. - Test client fixed. - Experimental SHA256 key storage supported. 03/17/2014 Oleg Moskalenko Version 3.2.2.912 'Marshal West': - TCP-in-TCP congestion avoidance implemented. - UDP-in-TCP congestion avoidance improved. - Alternate-server code cleaned. 03/10/2014 Oleg Moskalenko Version 3.2.2.911 'Marshal West': - "Congestion control" for UDP-inside-TCP tunneling; - memory management improvements; - socket logging improvements; - debug info added to CentOS and Fedora RPMs; - TCP traffic buffering improved; - Thread barriers cleaned; - TCP memory leak fixed; - minor TCP test client improvement. 03/09/2014 Oleg Moskalenko Version 3.2.2.910 'Marshal West': - Log messages extended and cleaned. - Some memory cleaning. 03/02/2014 Oleg Moskalenko Version 3.2.2.9 'Marshal West': - Issue 113 fixed (TCP rate limit fixed); - Issue 114 fixed (TCP stability). 02/18/2014 Oleg Moskalenko Version 3.2.2.8 'Marshal West': - Issue 102: SO_BSDCOMPAT socket option removed; - Issue 104: check for the REALM attribute value; - Issue 105: no-cli segfault fixed; - Issue 106: MESSAGE-INTEGRITY removed from DATA indication; - Issue 108: Server should return 438 on unknown nonce; - Issue 109: make the random functions stronger (mostly for transaction ID and for nonce); - Issue 111: fix valgrind warning on memory initialization. - Issue 112: RTCP sockets logging. 02/12/2014 Oleg Moskalenko Version 3.2.2.7 'Marshal West': - Possible indefinite cycle fixed in TCP/TCP routing (Issue 99); - Address 0.0.0.0 can be used as a listener address (Issue 100); - DHCP-configured servers supported (Issue 101); 02/04/2014 Oleg Moskalenko Version 3.2.2.6 'Marshal West': - Channel traffic memory copy elimination. - Send indication memory copy elimination. - DTLS traffic processing memory copy eliminated. - Mobility forbidden error code number fixed - according to the new draft document. - getsockname() usage minimized. - port allocation improved. - default relay behavior fixed (when no relay addresses defined). - atomic create permission request handling (Issue 97). 01/25/2014 Oleg Moskalenko Version 3.2.2.5 'Marshal West': - code optimization. 01/24/2014 Oleg Moskalenko Version 3.2.2.4 'Marshal West': - HMAC key handling fixed (Issue 96). 01/23/2014 Oleg Moskalenko Version 3.2.2.3 'Marshal West': - Security fix (issue 95). - Default "implicit" relay IP allocation policy is more usable (issue 94 fixed). - SSLv2 fixed (for those who are still using it) (issue 93 fixed). - Cosmetic changes. 01/19/2014 Oleg Moskalenko Version 3.2.2.1 'Marshal West': - CPU/memory cache optimization (memory locality). - torture tests enhanced. - stability fixes. - minor possible memory leak fix. - new TLS options: --no-sslv2, --no-sslv3, --no-tlsv1, --no-tlsv1_1, --no-tlsv1_2 01/06/2014 Oleg Moskalenko Version 3.2.1.4 'Marshal West': - Linux epoll performance improvements. - DTLS minor fix. 01/06/2014 Oleg Moskalenko Version 3.2.1.3 'Marshal West': - Telnet client added to installation when necessary. 01/05/2014 Oleg Moskalenko Version 3.2.1.2 'Marshal West': - Config file adjusted for DragonFly. 01/03/2014 Oleg Moskalenko Version 3.2.1.1 'Marshal West': - Minor TLS fix. - Default cipher list is DEFAULT now. 12/26/2013 Oleg Moskalenko Version 3.2.1.0 'Marshal West': - Optimized TCP network engine for Linux 3.9+. - Security fix: DH and ECDH temporary keys are now regenerated for each TLS or DTLS session. - Fix for systems with multiple CPU cores (more than 128). - DH TLS key now can be configured as 566, 1066 (default) or 2066 bits. - DH TLS key can be taken from a PEM file. - Issue 91 (test client crash) fixed. - Configurable net engine type. 12/25/2013 Oleg Moskalenko Version 3.1.6.0 'Arch Lector': - Timers optimization: linked list timers structure for often-used intervals. 12/23/2013 Oleg Moskalenko Version 3.1.5.3 'Arch Lector': - HTTP "keep-alive" support improved. - TCP channel "fortification". 12/19/2013 Oleg Moskalenko Version 3.1.5.1 'Arch Lector': - Private key password allowed for encrypted keys. - HTTP "keep-alive" supported. - "psd" CLI command added (ps dump to file). 12/18/2013 Oleg Moskalenko Version 3.1.4.2 'Arch Lector': - Time functions optimization. - Online changes to the alternate servers list thru telnet CLI. - Certificate chain files allowed. 12/13/2013 Oleg Moskalenko Version 3.1.3.1 'Arch Lector': - "Start time" ps command info added. - Protocol option added to "pu" command. - "Delete allocation" debug message fixed. - "Allocation id" debug info message fixed. - RFC6062 usage statistics fixed. - Info/Debug messages cleaned. 12/11/2013 Oleg Moskalenko Version 3.1.2.3 'Arch Lector': - CentOS 6 package fixed. 12/10/2013 Oleg Moskalenko Version 3.1.2.2 'Arch Lector': - ps output typo fixed (TLS params). - configurable EC curve name. - CLI TLS-related information extended. - "print users" (pu) CLI command added. 12/09/2013 Oleg Moskalenko Version 3.1.2.1 'Arch Lector': - DH cipher suites basic implementation. - Elliptic Curve cipher suites basic implementation. - RFC 6062 crash fixed. - More CLI parameters added. - Redis allocation statistics fixed. - Number of cli max session lines configurable. - uclient cipher suite configurable. 12/08/2013 Oleg Moskalenko Version 3.1.1.0 'Arch Lector': - Telnet CLI. - RFC 6062 internal messaging fixed. - Server relay endpoints (a non-standard feature). - "atomic line" stdout log print. - printed help minor fix. - client program does not necessary require certificate for TLS. - docs fixes. - allocation quota bug fixed. 11/29/2013 Oleg Moskalenko Version 3.0.2.1 'Practical Frost': - TCP stability fixes. - RFC 6062 "first packet(s)" bug fixed. - RFC 6062 stability fixes. - Multithreaded Mobile ICE. 11/28/2013 Oleg Moskalenko Version 3.0.1.4 'Practical Frost': - CentOS/Fedora packaging fixed. - PID file fixed. 11/26/2013 Oleg Moskalenko Version 3.0.1.3 'Practical Frost': - Misc cosmetic changes. - CentOS/Fedora packaging fixed. 11/25/2013 Oleg Moskalenko Version 3.0.1.2 'Practical Frost': - Mobility draft implemented. - DTLS communications fixes. - UDP Linux optimization. - Log output time starts with 0. - A new "drop root privileges" options: --proc-user and --proc-group added. - SHA256 agility updated: 426 error code on too weak SHA function. - "-m 0" and "-m 1" options improved. - non-threading environment support dropped. - stability fixes. - OpenSUSE support added. 11/10/2013 Oleg Moskalenko Version 3.0.0.0 'Practical Frost': - New network engine for Linux kernel 3.9+. 11/08/2013 Oleg Moskalenko Version 2.6.7.2 'Harding Grim': - SHA256 agility updated: 441 error code on too weak SHA function. 11/07/2013 Oleg Moskalenko Version 2.6.7.1 'Harding Grim': - CentOS / Fedora uninstall script. - Debian compilation error fixed. - OpenSSL 0.9.7 and earlier build fixed. - NetBSD build fixed. 11/03/2013 Oleg Moskalenko , Peter Dunkley Version 2.6.7.0 'Harding Grim': - CentOS 6 pre-compiled distribution. - Fedora pre-compiled distribution. - TURN_NO_TLS case compilation cleaning. - Text files cleaning. - Issue 68 fixed (no-stun option added). 10/27/2013 Oleg Moskalenko Version 2.6.6.1 'Harding Grim': - SHA256 added as a non-standard message integrity option. - CentOS rpm specs added. - Peter Dunkley added to the authors list. 10/20/2013 Oleg Moskalenko Version 2.6.6.0 'Harding Grim': - Cygwin loopback relay interfaces fixed (Issue 62). - rpath added to the Makefile (Issue 63). - CONFLICTS added to FreeBSD port Makefile (Issue 64). - Certificate check options, for server and for the test client (Issue 65). - Some compilation cleaning. 10/09/2013 Oleg Moskalenko Version 2.6.5.2 'Harding Grim': - Documentation changes. - Redis-related memory leak fixed (Issue 61). 09/25/2013 Oleg Moskalenko Version 2.6.4.1 'Harding Grim': - Crash on uninitialized redis db name is fixed (Issue 59). - Optional authentication of STUN Binding request is implemented (Issue 60). 09/16/2013 Oleg Moskalenko Version 2.6.3.1 'Harding Grim': - Issue 58: support changing white/black IP lists while server is running. database tables (keys for redis) added for that new functionality. 09/03/2013 Oleg Moskalenko Version 2.6.2.2 'Harding Grim': - Issue 52: RFC 6062 relay endpoints connection process fixed for Linux pre-3.9 kernel. 09/03/2013 Oleg Moskalenko Version 2.6.2.1 'Harding Grim': - UDP performance improvements. - Issue 56: DTLS scaleability improvements. - Issue 55: DTLS support in Cygwin. - Issue 57: --pidfile option - Issue 52: RFC 6062 relay endpoints connection process fixed. - Issue 53: Fingerprints added to the indications. - Issue 54: Long-term credentials mechanism integrity and software attributes added to the indications. 08/11/2013 Oleg Moskalenko Version 2.6.1.4 'Harding Grim': - UDP memory leak fixed. 08/11/2013 Oleg Moskalenko Version 2.6.1.3 'Harding Grim': - DTLS crash fix. 08/10/2013 Oleg Moskalenko Version 2.6.1.2 'Harding Grim': - TLS buffer decreased to avoid memory problems. - TLS BIO object fix. - UDP socket open/reopen process fixed. 08/08/2013 Oleg Moskalenko Version 2.6.1.1 'Harding Grim': - Network optimization: * "pure" UDP setup optimized (when no DTLS configured); * Auxiliary listening endpoints (configured by --aux-server=). * --udp-self-balance option to balance the UDP traffic among the aux endpoints (for clients supporting 300 ALTERNATE-SERVER response). - Security improvements: * no authentication required on the load balancer server (Issue 50). * REST API improvement: = --secret-ts-exp-time option deprecated; = In REST API timestamp, we are now using the expiration time (Issue 31). * Configurable cipher suite in the TURN server. * SSL3 support. * TLS 1.1 and 1.2 support. * SSL2 "encapsulation" mode support. * NULL OpenSSL cipher is allowed to be negotiated between server and client. * -U option (NULL cipher) added to the test client. * DTLS crash fixed on overload. - STUN enhancements and fixes: * Classic STUN transaction ID fixed (Issue 48). * Classic STUN attribute ERROR fixed (Issue 49). * Unused RFC 5780 functionality removed from TCP, TLS and DTLS relays. * resources optimization for stun-only: short connection expiration time. 07/26/2013 Oleg Moskalenko , Vladimir Tsanev Version 2.5.2.1 'Shivers': - log file placement changes. - Base64 encode/decode memory initialization fix. 07/23/2013 Oleg Moskalenko , Po-sheng Lin Version 2.5.1.2 'Shivers': - getopt fix in client test programs. - cosmetic changes. - allow anonymous alternate-server functionality. 07/21/2013 Oleg Moskalenko Version 2.5.1.1 'Shivers': - Improved "split" network engine: two different threading models for TCP and UDP. - DTLS crash fixed. - Multithreading with Cygwin. 07/20/2013 Oleg Moskalenko Version 2.1.3.1 'Shivers': - DTLS improvements for DOS attacks - deeper optimization for DOS attack (mostly for Linux) 07/19/2013 Oleg Moskalenko Version 2.1.2.0 'Shivers': - deeper optimization for DOS attack (mostly for Linux) 07/18/2013 Oleg Moskalenko , Po-sheng Lin Version 2.1.1.1 'Shivers': - udp fixes. - Makefile cleaning. - Dependencies cleaning. - DOS attack client emulation. - DOS attack defense logic added to the server. 07/14/2013 Oleg Moskalenko Version 2.0.0.0 'Shivers': - new networking engine: - scalable UDP socket model. - multithreaded TCP relay implemented. - race condition fixed in authentication of TCP sessions. - Cygwin "port" fixed. 06/23/2013 Oleg Moskalenko , Vladimir Tsanev Version 1.8.7.0 'Black Dow': - Added support for obsolete "classic" STUN RFC 3489; - Full TURN support for Cygwin implemented: MS-Win UDP sockets fixed; - Relay threads number changed; - Fedora warnings fixed; - turndb/testdbsetup.sh example file added; - Multiple Makefile and ./configure script fixes implemented: * Changes taken from Arch Linux port; * Manpages installation and deinstallation; * rfc5769check utility removed from installation, it is used for the compilation result test only and makes no sense for the end user; * "--parameter" format support in ./configure script; it allows simpler native OS package definitions (like in Debian package); * Mac OS X linking warnings removed. * pthread test fixed. 06/08/2013 Oleg Moskalenko Version 1.8.6.3 'Black Dow': - DONT-FRAGMENT flag removed on UDP listening (clients-facing) sockets. - UDP fix for Cygwin only: UDP channels work fine now. - docs fixes. 06/06/2013 Oleg Moskalenko Version 1.8.6.2 'Black Dow': - Just cosmetic re-packaging for Debian, tarball warnings removed. 06/05/2013 Oleg Moskalenko Version 1.8.6.1 'Black Dow': - Peer permissions bug fixed. 06/03/2013 Oleg Moskalenko Version 1.8.6.0 'Black Dow': - Optimization. - Mac OS X compilation fixes. 06/01/2013 Oleg Moskalenko Version 1.8.5.4 'Black Dow': - Issues 29 and 30 fixed (channels padding). - minor fixes. - Mac OS X compilation fixes. - Cygwin-related compilation fixes and INSTALL additions. 05/31/2013 Oleg Moskalenko Version 1.8.5.3 'Black Dow': - REST API extra script example and docs extention. 05/26/2013 Oleg Moskalenko Version 1.8.5.1 'Black Dow': - Config file parsing fixed (Issue 28) 05/20/2013 Oleg Moskalenko , Erik Johnston Version 1.8.5.0 'Black Dow': - IP access control lists. - log file name fix. - alt-* ports default behavior changed. - "passive TCP" option in uclient. 05/18/2013 Oleg Moskalenko Version 1.8.4.5 'Black Dow': - socket conditions cleaned (SIGPIPE, etc) 05/17/2013 Oleg Moskalenko Version 1.8.4.4 'Black Dow': - configuration and installation adjusted for: - NetBSD; - Solaris; - OpenBSD; - Screen messages fixed; - code security fixes. 05/15/2013 Oleg Moskalenko Version 1.8.4.3 'Black Dow': - Compilation warning removed. - Log file fixed (Issue 26) 05/15/2013 Oleg Moskalenko Version 1.8.4.2 'Black Dow': - repackaging for Debian compliance. Docs separated. 05/14/2013 Oleg Moskalenko Version 1.8.4.1 'Black Dow': - Cosmetics (docs, warnings, etc). - More complex case of TURN-server-behind-NAT is implemented, when multiple public-ip/private-ip mappings are involved. 05/13/2013 Oleg Moskalenko Version 1.8.4.0 'Black Dow': - Redis DB support added. - Crash on help text fixed. - Max allocation time can be changed in the command-line or in the config file. 05/09/2013 Oleg Moskalenko Version 1.8.3.9 'Black Dow': - No changes - just the tarball is repackaged for Debian compatibility. 05/07/2013 Oleg Moskalenko Version 1.8.3.8 'Black Dow': - multicast and loopback addresses disallow options added. - option to direct all log messages to the system log (syslog). 05/02/2013 Oleg Moskalenko Version 1.8.3.7 'Black Dow': - Allocation status log. 05/01/2013 Oleg Moskalenko Version 1.8.3.6 'Black Dow': - Stuntman client interoperability fixed. - Manpages installation fixed. 04/30/2013 Oleg Moskalenko Version 1.8.3.5 'Black Dow': - Lintian fixes. 04/27/2013 Oleg Moskalenko Version 1.8.3.4 'Black Dow': - Installation fixes. 04/26/2013 Oleg Moskalenko Version 1.8.3.3 'Black Dow': - Log file midnight rollover implemented (Issue 15). 04/25/2013 Oleg Moskalenko Version 1.8.3.1 'Black Dow': - Configurable REST API separator symbol (Issue 16). - Stale Nonce bug fixed (Issue 17). - Minor client fix. 04/21/2013 Oleg Moskalenko Version 1.8.3.0 'Black Dow': - STUN stand-alone functionality improved according to RFC 5389. - ALTERNATE-SERVER implemented as "light" load balancing feature. - stun-only option implemented. - scripts directory reorganized. 04/19/2013 Oleg Moskalenko Version 1.8.2.1 'Black Dow': - Misc docs fixes. 04/13/2013 Oleg Moskalenko Version 1.8.2.0 'Black Dow': - Multiple database shared secrets supported for REST API. - Added support for some OpenSSL FIPS versions (like openssl 0.9.8e-fips-rhel5). 04/13/2013 Oleg Moskalenko Version 1.8.1.3 'Black Dow': - Maintenance (docs, etc). - Added partial support for Cygwin. Only TCP & TLS protocols are support for client-to-server communications (as in RFC 5766 and RFC 6062). UDP supported only for relay communications. DTLS is not supported at all. The problem is in Winsock UDP sockets implementation. 04/11/2013 Oleg Moskalenko Version 1.8.1.2 'Black Dow': - Work on configuration and build. 04/9/2013 Oleg Moskalenko Version 1.8.1.1 'Black Dow': - Docs improvements. - Load balancing use case added to TurnNetworks.pdf. - Verbose mode split into 'normal' and 'extra' modes. - Logging extended and fixed. 04/7/2013 Oleg Moskalenko Version 1.8.1.0 'Black Dow': - Compilation flags improved. - utility programs renamed and moved to bin/ directory. - README and turnserver man page separated into three sections - turnserver, turnadmin, turnutils. 04/6/2013 Oleg Moskalenko Version 1.8.0.6 'Black Dow': - Added option "--psql-userdb" for better visual separation between PostgreSQL and MySQL stuff. - turnadmin flat files handling fixed. - added set/show commands to turnadmin for secret key. 04/6/2013 Oleg Moskalenko Version 1.8.0.5 'Black Dow': - turnadmin MySQL connection fixed. - minor cosmetic changes. - Added "list" commands for long-term and short-term users, to turnadmin. 04/5/2013 Oleg Moskalenko Version 1.8.0.4 'Black Dow': - Minor compilation fixes. - Minor docs fixes. - "connect_timeout" option support for MySQL. 04/5/2013 Oleg Moskalenko Version 1.8.0.3 'Black Dow': - Issue 11 (secret timestamp check) fixed. 04/4/2013 Oleg Moskalenko Version 1.8.0.2 'Black Dow': - TCP sockets flush removed. - rfc5769check utility removed from the Makefile. 04/4/2013 Oleg Moskalenko Version 1.8.0.1 'Black Dow': - Some short-term auth problems fixed. - rfc5769check utility added to the Makefile and upgraded. 04/3/2013 Oleg Moskalenko Version 1.8.0.0 'Black Dow': - Short-term credentials mechanism implemented. 04/2/2013 Oleg Moskalenko Version 1.7.3.1 'Superior Glokta': - Listeners code cleaned. - The default number of extra relay threads changes from 0 to 1. 04/1/2013 Oleg Moskalenko Version 1.7.3.0 'Superior Glokta': - Issue 10 fixed: log file control options. Two options added: --no-stdout-log and --log-file. 03/29/2013 Oleg Moskalenko Version 1.7.2.0 'Superior Glokta': - Issue 9 fixed (uclient). - Secret-based authentication implemented (see TURNServerRESTAPI.pdf). - Uclient docs fixed. - database schema extended (table for the secret added). 03/27/2013 Oleg Moskalenko Version 1.7.1.2 'Superior Glokta': - CHANNEL BIND request handling fixed: now it produces an error when client is trying to tie the same peer address to different channels. - uclient and peer test apps upgraded so that RTP channels are talking to and RTCP channels are talking to in client-to-peer communication patterns. - compilation warning is fixed when MySQL is not used. 03/27/2013 Oleg Moskalenko Version 1.7.1.1 'Superior Glokta': - CONNECT response fixed in RFC 6062. - uclient checks server responses integrity. 03/26/2013 Oleg Moskalenko Version 1.7.1.0 'Superior Glokta': - MySQL support added for the user keys repository. - PostgreSQL support improved. - Docs fixed. - 64 bits platform fixes. 03/23/2013 Oleg Moskalenko Version 1.7.0.0 'Glokta': - Authentication fix. - PostgreSQL database can be used as the user keys repository. 03/21/2013 Oleg Moskalenko Version 1.6.1.3 'Whirrun': - UDP segmentation fault fixed 03/21/2013 Oleg Moskalenko Version 1.6.1.2 'Whirrun': - RFC 6062 fix 03/21/2013 Oleg Moskalenko Version 1.6.1.1 'Whirrun': - Authentication error fixed 03/19/2013 Oleg Moskalenko Version 1.6.1.0 'Whirrun': - --stale-nonce option - working on userdb - "hang on" option fixed in uclient 03/18/2013 Oleg Moskalenko Version 1.6.0.2 'Whirrun': - working on userdb - c++ compilation fix 03/17/2013 Oleg Moskalenko Version 1.6.0.1 'Whirrun': - uclient performance improved - TurnNetworks.pdf document added 03/15/2013 Oleg Moskalenko Version 1.6.0.0 'Whirrun': - "Pure" TCP relaying (RFC 6062) implemented. - Network interactions fixes. - RFC 6062 test scripts added. 03/03/2013 Oleg Moskalenko Version 1.5.2.8 'Iosiv Lestek': - authorization processing improvements. - peer application fixed. - some ICE attributes added. 02/27/2013 Oleg Moskalenko Version 1.5.2.7 'Iosiv Lestek': - authorization processing improvements - Issue 4 fixed. - secure client-to-client script added 02/22/2013 Oleg Moskalenko Version 1.5.2.6 'Iosiv Lestek': - strcpy/strncpy fixed - some screen messages fixed - uclient statistics fixed - software attribute fixed - example scripts fixed 02/16/2013 Oleg Moskalenko Version 1.5.2.5 'Lestek': - uclient application fixed - Docs fixes 02/14/2013 Oleg Moskalenko Version 1.5.2.4 'Lestek': - Crash fixed on unconfigured interfaces - Docs fixes 02/12/2013 Oleg Moskalenko Version 1.5.2.3 'Lestek': - Added feature: TURN Server always uses fingerprints in a session if the session client is using fingerprints. - Default unsecure alternative port changed to 3479, default secure alternative port changed to 5350. - TURN Server always trying to search for default certificate file turn_server_cert.pem and for default private key file turn_server_pkey.pem, if not certificate or private key is explicitly configured. - configurable packet rate in the uclient test program. - default peer port changed to 3480. - -z, --no-auth option added to turnserver. 02/11/2013 Oleg Moskalenko Version 1.5.2.2 'Lestek': - Some cleanup added to the network input handlers. 02/9/2013 Oleg Moskalenko Version 1.5.2.1 'Lestek': - Binding requests do not require authentication. - SOFTWARE in the end of the message. 02/8/2013 Oleg Moskalenko Version 1.5.2.0 'Lestek': - NAT discovery fixed (RFC5780). 02/8/2013 Oleg Moskalenko Version 1.5.1.6 'Calder': - Installation instructions fixed. 02/8/2013 Oleg Moskalenko Version 1.5.1.5 'Calder': - Mac compilation fixes. - Fixes for old Linuxes. 02/7/2013 Oleg Moskalenko Version 1.5.1.4 'Calder': - Configuration alert (warning) messages. - Relay addresses by default use listener addresses. - Realm/user sequence fixed in the config file reading. 01/27/2013 Oleg Moskalenko Version 1.5.1.3 'Calder': - 'External' IP implemented for TURN-server-behind-NAT situation. 01/26/2013 Oleg Moskalenko Version 1.5.1.2 'Calder': - Alternative ports moved to 20000-ish territory. - Docs fixes. 01/22/2013 Oleg Moskalenko Version 1.5.1.1 'Calder': - Docs fixes. 01/22/2013 Oleg Moskalenko Version 1.5.1.0 'Calder': - C++ compatible headers and build. - C++ library header. - HTML-formatted development reference. 01/14/2013 Oleg Moskalenko Version 1.5.0.0 'Calder': - RFC 5769 check utility implemented. - RFC 5780 STUN extension implemented. 01/13/2013 Oleg Moskalenko Version 1.4.2.5 'Scale': - Issue 2 fixed. 01/08/2013 Oleg Moskalenko Version 1.4.2.4 'Scale': - Bogus "Bind to device" error message removed (Linux). - Docs improvements. 01/08/2013 Oleg Moskalenko Version 1.4.2.3 'Scale': - Bandwidth limitation implemented (--max-bps option). - DTLS communications improved. 01/07/2013 Oleg Moskalenko Version 1.4.2.2 'Scale': - Output messages fixed. - Peer test application accepts multiple listening addresses. - config search directories improved. 01/06/2013 Oleg Moskalenko Version 1.4.2.1 'Scale': - Examples directory structure fixed - Installation fixes - Output messages fixed 01/05/2013 Oleg Moskalenko Version 1.4.2 'Scale': - Daemon execution improved - Installation fixes - Added comments to the scripts 01/04/2013 Oleg Moskalenko Version 1.4.1.2 'Scale': - Configure script introduced - Installation fixes - Run as daemon 01/01/2013 Oleg Moskalenko Version 1.4.1 'Scale': - Options fixes - Build fixes - Script fixes - Installation fixes 12/31/2012 Oleg Moskalenko Version 1.4 'Scale': - Separate file for the dynamic user database - Build fixes - Script fixes - Logging fixes 12/29/2012 Oleg Moskalenko Version 1.3.0.2 'Ferro': - Debian 'squeeze' compilation fix 12/26/2012 Oleg Moskalenko Version 1.3.0.1 'Ferro': - install procedure minor improvements 12/24/2012 Oleg Moskalenko Version 1.3 'Ferro': - default conf file renamed to turnserver.conf - build script improved - client library linking fixed - install procedure 12/23/2012 Oleg Moskalenko Version 1.2.3 'Luthar': - turnserver options fixed - man page renamed to turnserver 12/22/2012 Oleg Moskalenko Version 1.2.2: - Man page fix 12/21/2012 Oleg Moskalenko Version 1.2.1 'Juvens': - Man page 12/21/2012 Oleg Moskalenko Version 1.2 'Euz': - Project cleaning 12/20/2012 Oleg Moskalenko Version 1.1 'no name': - DTLS extension 12/17/2012 Oleg Moskalenko Version 1.0 'no name': - RFC 5766 - RFC 6156 turnserver-3.2.3.1/postinstall.txt000644 001751 001751 00000002667 12315706777 017164 0ustar00olegoleg000000 000000 ================================================================== 1) If you system supports automatic start-up system daemon services, the, to enable the turnserver as an automatically started system service, you have to: a) Create and edit /etc/turnserver.conf or /usr/local/etc/turnserver.conf . Use /usr/local/etc/turnserver.conf.default as an example. b) For user accounts settings, if using the turnserver with authentication: create and edit /etc/turnuserdb.conf file, or set up PostgreSQL or MySQL or Redis database for user accounts. Use /usr/local/etc/turnuserdb.conf.default as example for flat file DB, or use /usr/local/share/turnserver/schema.sql as SQL database schema, or use /usr/local/share/turnserver/schema.userdb.redis as Redis database schema description and/or /usr/local/share/turnserver/schema.stats.redis as Redis status & statistics database schema description. c) add whatever is necessary to enable start-up daemon for the /usr/local/bin/turnserver. 2) If you do not want the turnserver to be a system service, then you can start/stop it "manually", using the "turnserver" executable with appropriate options (see the documentation). 3) To create database schema, use schema in file /usr/local/share/turnserver/schema.sql. 4) For additional information, run: $ man turnserver $ man turnadmin $ man turnutils ================================================================== turnserver-3.2.3.1/src/000755 001751 001751 00000000000 12315706777 014623 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/README.turnutils000644 001751 001751 00000021210 12315706777 016760 0ustar00olegoleg000000 000000 GENERAL INFORMATION A set of turnutils_* programs provides some utility functionality to be used for testing and for setting up the TURN server. 1. turnutils_uclient: emulates multiple UDP,TCP,TLS or DTLS clients. (this program is provided for the testing purposes only !) The compiled binary image of this program is located in bin/ sub-directory. WARNING: the turnutils_uclient program is a primitive client application. It does not implement the re-transmission pattern that is necessary for a correct TURN client implementation. In TURN, the retransmission burden is lying almost entirely on the client application. We provide the messaging functionality in the client library, but the client must implement the correct Networking IO processing in the client program code. 2. turnutils_peer: a simple stateless UDP-only "echo" server, to be used as the final server in relay pattern ("peer"). For every incoming UDP packet, it simply echoes it back. (this program is provided for the testing purposes only !) When the test clients are communicating in the client-to-client manner (when the "turnutils_uclient" program is used with "-y" option) then the turnutils_peer is not needed. The compiled binary image of this program is located in bin/ subdirectory. 3. turnutils_stunclient: a simple STUN client example. The compiled binary image of this program is located in bin/ subdirectory. 4. turnutils_rfc5769check: a utility that checks the correctness of the STUN/TURN protocol implementation. This utility is used only for the compilation check procedure, it is not copied to the installation destination. In the "examples/scripts" subdirectory, you will find the examples of command lines to be used to run the programs. The scripts are meant to be run from examples/ subdirectory, for example: $ cd examples $ ./scripts/secure_relay.sh ===================================== NAME turnutils_uclient - this client emulation application is supplied for the test purposes only. SYNOPSIS $ turnutils_uclient [-tTSvsyhcxg] [options] DESCRIPTION It was designed to simulate multiple clients. It uses asynch IO API in libevent to handle multiple clients. A client connects to the relay, negotiates the session, and sends multiple (configured number) messages to the server (relay), expecting the same number of replies. The length of the messages is configurable. The message is an arbitrary octet stream, but it can be configured as a string. The number of the messages to send is configurable. Flags: -t Use TCP for communications between client and TURN server (default is UDP). -T Use TCP for the relay transport (default - UDP). Implies options -t, -y, -c, and ignores flags and options -s, -e, -r and -g. -P Passive TCP (RFC6062 with active peer). Implies -T. -S Secure SSL connection: SSL/TLS for TCP, DTLS for UDP. -U Secure unencrypted connection (suite eNULL): SSL/TLS for TCP, DTLS for UDP. -v Verbose. -s Use "Send" method in TURN; by default, it uses TURN Channels. -y Use client-to-client connections: RTP/RTCP pair of channels to another RTP/RTCP pair of channels. with this option the turnutils_peer application is not used, as the allocated relay endpoints are talking to each other. -h Hang on indefinitely after the last sent packet. -c Do not create rtcp connections. -x Request IPv6 relay address (RFC6156). -X IPv4 relay address explicitly requested. -g Set DONT_FRAGMENT parameter in TURN requests. -A use short-term credentials mechanism for authentication. By default, the program uses the long-term credentials mechanism if authentication is required. -D Do mandatory channel padding even for UDP (like pjnath). -N do negative tests (some limited cases only). -R do negative protocol tests. -O DOS attack mode. -H SHA256 digest function for message integrity calculation. Without this option, by default, SHA1 is used. -M Use TURN ICE Mobility. -I Do not set permissions on TURN relay endpoints (for testing the non-standard server relay functionality). -G Generate extra requests (create permissions, channel bind). Options with required values: -l Message length (Default: 100 Bytes). -i Certificate file (for secure connections only, optional). -k Private key file (for secure connections only). -E CA file for server certificate verification, if the server certificate to be verified. -p TURN Server port (Defaults: 3478 unsecure, 5349 secure). -n Number of messages to send (Default: 5). -d Local interface device (optional, Linux only). -L Local IP address (optional). -m Number of clients (Default: 1, 2 or 4, depending on options). -e Peer address. -r Peer port (Default: 3480). -z Per-session packet interval in milliseconds (Default: 20). -u STUN/TURN user name. -w STUN/TURN user password. -W TURN REST API authentication secret. Is not compatible with -A flag. -C This is the username/timestamp separator symbol (character) in TURN REST API. The default value is :. -F Cipher suite for TLS/DTLS. Default value is DEFAULT. See the examples in the "examples/scripts" directory. ====================================== NAME turnutils_peer - a simple UDP-only echo backend server. SYNOPSYS $ turnutils_peer [-v] [options] DESCRIPTION This application is used for the test purposes only, as a peer for the turnutils_uclient application. Options with required values: -p Listening UDP port (Default: 3480). -d Listening interface device (optional) -L Listening address of turnutils_peer server. Multiple listening addresses can be used, IPv4 and IPv6. If no listener address(es) defined, then it listens on all IPv4 and IPv6 addresses. -v Verbose ======================================== NAME turnutils_stunclient - a basic STUN client. SYNOPSIS $ turnutils_stunclient [options] DESCRIPTION It sends a "new" STUN RFC 5389 request (over UDP) and shows the reply information. Options with required values: -p STUN server port (Default: 3478). -L Local address to use (optional). -f Force RFC 5780 processing. The turnutils_stunclient program checks the results of the first request, and if it finds that the STUN server supports RFC 5780 (the binding response reveals that) then the turnutils_stunclient makes a couple more requests with different parameters, to demonstrate the NAT discovery capabilities. This utility does not support the "old" "classic" STUN protocol (RFC 3489). ===================================== NAME turnutils_rfc5769check - a utility that tests the correctness of STUN protocol implementation. SYNOPSIS $ turnutils_rfc5769check DESCRIPTION turnutils_rfc5769check tests the correctness of STUN protocol implementation against the test vectors predefined in RFC 5769 and prints the results of the tests on the screen. This utility is used only for the compilation check procedure, it is not copied to the installation destination. Usage: $ turnutils_rfc5769check =================================== DOCS After installation, run the command: $ man turnutils or in the project root directory: $ man -M man turnutils to see the man page. ===================================== FILES /etc/turnserver.conf /etc/turnuserdb.conf /usr/local/etc/turnserver.conf /usr/local/etc/turnuserdb.conf ================================= DIRECTORIES /usr/local/share/turnserver /usr/local/share/doc/turnserver /usr/local/share/examples/turnserver =================================== STANDARDS new STUN RFC 5389 TURN RFC 5766 TURN-TCP extension RFC 6062 TURN IPv6 extension RFC 6156 STUN/TURN test vectors RFC 5769 STUN NAT behavior discovery RFC 5780 ==================================== SEE ALSO turnserver, turnadmin ====================================== WEB RESOURCES project page: http://code.google.com/p/rfc5766-turn-server/ Wiki page: http://code.google.com/p/rfc5766-turn-server/wiki/Readme forum: https://groups.google.com/forum/?fromgroups=#!forum/turn-server-project-rfc5766-turn-server/ ====================================== AUTHORS Oleg Moskalenko Gabor Kovesdan http://kovesdan.org/ Daniel Pocock http://danielpocock.com/ John Selbie (jselbie@gmail.com) Lee Sylvester Erik Johnston Roman Lisagor Vladimir Tsanev Po-sheng Lin Peter Dunkley Mutsutoshi Yoshimoto turnserver-3.2.3.1/README.turnserver000644 001751 001751 00000112151 12315706777 017133 0ustar00olegoleg000000 000000 GENERAL INFORMATION The TURN Server project contains the source code of a TURN server and TURN client messaging library. Also, some extra programs provided, for testing-only purposes. See the INSTALL file for the building instructions. After the build, you will have the following binary images: 1. turnserver: TURN Server relay. The compiled binary image of the TURN Server program is located in bin/ sub-directory. 2. turnadmin: TURN administration tool. See README.turnadmin and turnadmin man page. 3. turnutils_uclient. See README.turnutils and turnutils man page. 4. turnutils_peer. See README.turnutils and turnutils man page. 5. turnutils_stunclient. See README.turnutils and turnutils man page. 6. turnutils_rfc5769check. See README.turnutils and turnutils man page. In the "examples/scripts" sub-directory, you will find the examples of command lines to be used to run the programs. The scripts are meant to be run from examples/ sub-directory, for example: $ cd examples $ ./scripts/secure_relay.sh RUNNING THE TURN SERVER Options note: turnserver has long and short option names, for most options. Some options have only long form, some options have only short form. Their syntax somewhat different, if an argument is required: The short form must be used as this (for example): $ turnserver -L 12.34.56.78 The long form equivalent must use the "=" character: $ turnserver --listening-ip=12.34.56.78 If this is a flag option (no argument required) then their usage are the same, for example: $ turnserver -a is equivalent to: $ turnserver --lt-cred-mech ===================================== NAME turnserver - a TURN relay server implementation. SYNOPSIS $ turnserver [-n | -c ] [flags] [ --userdb= | --psql-userdb= | --mysql-userdb= | --redis-userdb= ] [-z | --no-auth | -a | --lt-cred-mech ] [options] $ turnserver -h DESCRIPTION Config file settings: -n Do not use configuration file, use only command line parameters. -c Configuration file name (default - turnserver.conf). The format of config file can be seen in the supplied examples/etc/turnserver.conf example file. Long names of the options are used as the configuration items names in the file. If not an absolute path is supplied, then the file is searched in the following directories: * current directory * current directory etc/ sub-directory * upper directory level etc/ * /etc/ * /usr/local/etc/ * installation directory /etc User database settings: -b, --userdb User database file name (default - turnuserdb.conf), for long-term credentials mechanism only. This user file database is being dynamically checked while the turnserver is working, and the user accounts can be changed dynamically by editing the database. -e, --psql-userdb User database connection string for PostgreSQL. This database can be used for long-term and short-term credentials mechanisms, and it can store the secret value for secret-based timed authentication in TURN RESP API. The connection string format is like that: "host= dbname= user= password= connect_timeout=" (for 8.x or newer Postgres). Or: "postgresql://username:password@hostname:port/databasename" (for 9.x or newer Postgres). See the INSTALL file for more explanations and examples. Also, see http://www.PostgreSQL.org for full PostgreSQL documentation. -M, --mysql-userdb User database connection string for MySQL or MariaDB. This database can be used for long-term and short-term credentials mechanisms, and it can store the secret value for secret-based timed authentication in TURN RESP API. The connection string format is like that: "host= dbname= user= password= connect_timeout=" See the INSTALL file for more explanations and examples. Also, see http://www.mysql.org or http://mariadb.org for full MySQL documentation. -N, --redis-userdb User database connection string for Redis. This database can be used for long-term and short-term credentials mechanisms, and it can store the secret value for secret-based timed authentication in TURN RESP API. The connection string format is like that: "ip= dbname= password= connect_timeout=" See the INSTALL file for more explanations and examples. Also, see http://redis.io for full Redis documentation. Flags: -v, --verbose Moderate verbose mode. -V, --Verbose Extra verbose mode, very annoying and not recommended. -o, --daemon Run server as daemon. -f, --fingerprint Use fingerprints in the TURN messages. If an incoming request contains a fingerprint, then TURN server will always add fingerprints to the messages in this session, regardless of the per-server setting. -a, --lt-cred-mech Use long-term credentials mechanism (this one you need for WebRTC usage). This option can be used with either flat file user database or PostgreSQL DB or MySQL DB or Redis for user keys storage. -A, --st-cred-mech Use the short-term credentials mechanism. This option requires a PostgreSQL or MySQL or Redis DB for short term passwords storage. -z, --no-auth Do not use any credentials mechanism, allow anonymous access. Opposite to -a and -A options. This is default option when no authentication-related options are set. By default, no credential mechanism is used - any user is allowed. --use-auth-secret TURN REST API flag. Flag that sets a special WebRTC authorization option that is based upon authentication secret. The feature purpose is to support "TURN Server REST API" as described in the TURN REST API section below. This option uses timestamp as part of combined username: usercombo -> "user_id:timestamp", turn user -> usercombo, turn password -> base64(hmac(secret key, usercombo)). This allows TURN credentials to be accounted for a specific user id. If you don't have a suitable id, the timestamp alone can be used. This option is just turns on secret-based authentication. The actual value of the secret is defined either by option static-auth-secret, or can be found in the turn_secret table in the database. This option can be used with long-term credentials mechanisms only - it does not make much sense with the short-term mechanism. --dh566 Use 566 bits predefined DH TLS key. Default size of the key is 1066. --dh2066 Use 2066 bits predefined DH TLS key. Default size of the key is 1066. --no-sslv2 Do not allow SSLv2 protocol. --no-sslv3 Do not allow SSLv3 protocol. --no-tlsv1 Do not allow TLSv1 protocol. --no-tlsv1_1 Do not allow TLSv1.1 protocol. --no-tlsv1_2 Do not allow TLSv1.2 protocol. --no-udp Do not start UDP client listeners. --no-tcp Do not start TCP client listeners. --no-tls Do not start TLS client listeners. --no-dtls Do not start DTLS client listeners. --no-udp-relay Do not allow UDP relay endpoints defined in RFC 5766, use only TCP relay endpoints as defined in RFC 6062. --no-tcp-relay Do not allow TCP relay endpoints defined in RFC 6062, use only UDP relay endpoints as defined in RFC 5766. --stale-nonce Use extra security with nonce value having limited lifetime (600 secs). --no-stdout-log Flag to prevent stdout log messages. By default, all log messages are going to both stdout and to the configured log file. With this option everything will be going to the log file only (unless the log file itself is stdout). --syslog With this flag, all log will be redirected to the system log (syslog). --secure-stun Require authentication of the STUN Binding request. By default, the clients are allowed anonymous access to the STUN Binding functionality. -S, --stun-only Run as STUN server only, all TURN requests will be ignored. Option to suppress TURN functionality, only STUN requests will be processed. --no-stun Run as TURN server only, all STUN requests will be ignored. Option to suppress STUN functionality, only TURN requests will be processed. --no-loopback-peers Disallow peers on the loopback addresses (127.x.x.x and ::1). --no-multicast-peers Disallow peers on well-known broadcast addresses (224.0.0.0 and above, and FFXX:*). --sha256 Require SHA256 digest function to be used for the message integrity. By default, the server uses SHA1 hashes. With this option, the server requires the stronger SHA256 hashes. The client application must support SHA256 hash function if this option is used. If the server obtains a message from the client with a weaker (SHA1) hash function then the server returns error code 426. --mobility Mobility with ICE (MICE) specs support. --no-cli Turn OFF the CLI support. By default it is always ON. See also options --cli-ip and --cli-port. --server-relay Server relay. NON-STANDARD AND DANGEROUS OPTION. Only for those applications when we want to run server applications on the relay endpoints. This option eliminates the IP permissions check on the packets incoming to the relay endpoints. See http://tools.ietf.org/search/rfc5766#section-17.2.3 . --udp-self-balance (recommended for older Linuxes only) Automatically balance UDP traffic over auxiliary servers (if configured). The load balancing is using the ALTERNATE-SERVER mechanism. The TURN client must support 300 ALTERNATE-SERVER response for this functionality. -h Help. Options with required values: -d, --listening-device Listener interface device. (NOT RECOMMENDED. Optional functionality, Linux only). The turnserver process must have root privileges to bind the listening endpoint to a device. If turnserver must run as a process without root privileges, then just do not use this setting. -L, --listening-ip Listener IP address of relay server. Multiple listeners can be specified, for example: -L ip1 -L ip2 -L ip3 If no IP(s) specified, then all IPv4 and IPv6 system IPs will be used for listening. The same ip(s) can be used as both listening and relay ip(s). -p, --listening-port TURN listener port for UDP and TCP listeners (Default: 3478). Note: actually, TLS & DTLS sessions can connect to the "plain" TCP & UDP port(s), too - if allowed by configuration. --tls-listening-port TURN listener port for TLS and DTLS listeners (Default: 5349). Note: actually, "plain" TCP & UDP sessions can connect to the TLS & DTLS port(s), too - if allowed by configuration. The TURN server "automatically" recognizes the type of traffic. Actually, two listening endpoints (the "plain" one and the "tls" one) are equivalent in terms of functionality; but we keep both endpoints to satisfy the RFC 5766 specs. For secure TCP connections, we currently support SSL version 3 and TLS versions 1.0, 1.1, 1.2. SSL2 "encapsulation mode" is also supported. For secure UDP connections, we support DTLS version 1. --alt-listening-port Alternative listening port for UDP and TCP listeners; default (or zero) value means "listening port plus one". This is needed for STUN CHANGE_REQUEST - in RFC 5780 sense or in old RFC 3489 sense - for NAT behavior discovery). The TURN Server supports CHANGE_REQUEST only if it is started with more than one listening IP address of the same family (IPv4 or IPv6). The CHANGE_REQUEST is only supported by UDP protocol, other protocols are listening on that endpoint only for "symmetry". --alt-tls-listening-port Alternative listening port for TLS and DTLS protocols. Default (or zero) value means "TLS listening port plus one". --aux-server Auxiliary STUN/TURN server listening endpoint. Aux servers have almost full TURN and STUN functionality. The (minor) limitations are: 1) Auxiliary servers do not have alternative ports and they do not support STUN RFC 5780 functionality (CHANGE REQUEST). 2) Auxiliary servers also are never returning ALTERNATIVE-SERVER reply. Valid formats are 1.2.3.4:5555 for IPv4 and [1:2::3:4]:5555 for IPv6. There may be multiple aux-server options, each will be used for listening to client requests. -i, --relay-device Relay interface device for relay sockets (NOT RECOMMENDED. Optional, Linux only). -E, --relay-ip Relay address (the local IP address that will be used to relay the packets to the peer). Multiple relay addresses may be used: -E ip1 -E ip2 -E ip3 The same IP(s) can be used as both listening IP(s) and relay IP(s). If no relay IP(s) specified, then the turnserver will apply the default policy: it will decide itself which relay addresses to be used, and it will always be using the client socket IP address as the relay IP address of the TURN session (if the requested relay address family is the same as the family of the client socket). -X, --external-ip TURN Server public/private address mapping, if the server is behind NAT. In that situation, if a -X is used in form "-X " then that ip will be reported as relay IP address of all allocations. This scenario works only in a simple case when one single relay address is be used, and no CHANGE_REQUEST functionality is required. That single relay address must be mapped by NAT to the 'external' IP. The "external-ip" value, if not empty, is returned in XOR-RELAYED-ADDRESS field. For that 'external' IP, NAT must forward ports directly (relayed port 12345 must be always mapped to the same 'external' port 12345). In more complex case when more than one IP address is involved, that option must be used several times, each entry must have form "-X ", to map all involved addresses. CHANGE_REQUEST (RFC5780 or RFC3489) NAT discovery STUN functionality will work correctly, if the addresses are mapped properly, even when the TURN server itself is behind A NAT. By default, this value is empty, and no address mapping is used. -m, --relay-threads Number of relay threads to handle the established connections (in addition to authentication thread and the listener thread). If set to 0 then application runs relay process in a single thread, in the same thread with the listener process (the authentication thread will still be a separate thread). In older systems (before Linux kernel 3.9), the number of UDP threads is always one threads per network listening endpoint - unless "-m 0" or "-m 1" is set. --min-port Lower bound of the UDP port range for relay endpoints allocation. Default value is 49152, according to RFC 5766. --max-port Upper bound of the UDP port range for relay endpoints allocation. Default value is 65535, according to RFC 5766. -u, --user Long-term security mechanism credentials user account, in the column-separated form username:key. Multiple user accounts may used in the command line. The key is either the user password, or the key is generated by turnadmin command. In the second case, the key must be prepended with 0x symbols. The key is calculated over the user name, the realm, and the user password. This setting may not be used with TURN REST API or with short-term credentials mechanism. -r, --realm Realm to be used for all users. Must be used with long-term credentials mechanism or with TURN REST API. -C, --rest-api-separator This is the username/timestamp separator symbol (character) in TURN REST API. The default value is :. -q, --user-quota Per-user allocations quota: how many concurrent allocations a user can create. -Q, --total-quota Total allocations quota: global limit on concurrent allocations. --static-auth-secret Static authentication secret value (a string) for TURN REST API only. If not set, then the turn server will try to use the dynamic value in turn_secret table in user database (if present). The database-stored value can be changed on-the-fly by a separate program, so this is why that other mode is dynamic. Multiple shared secrets can be used (both in the database and in the "static" fashion). -s, --max-bps Max bytes-per-second bandwidth a TURN session is allowed to handle (input and output network streams are treated separately). Anything above that limit will be dropped or temporary suppressed (within the available buffer limits). --cert Certificate file, PEM format. Same file search rules applied as for the configuration file. If both --no-tls and --no-dtls options are specified, then this parameter is not needed. Default value is turn_server_cert.pem. --pkey Private key file, PEM format. Same file search rules applied as for the configuration file. If both --no-tls and --no-dtls options are specified, then this parameter is not needed. Default value is turn_server_pkey.pem. --pkey-pwd If the private key file is encrypted, then this password to be used. --cipher-list Allowed OpenSSL cipher list for TLS/DTLS connections. Default value is "DEFAULT". --CA-file CA file in OpenSSL format. Forces TURN server to verify the client SSL certificates. By default, no CA is set and no client certificate check is performed. --ec-curve-name Curve name for EC ciphers, if supported by OpenSSL library (TLS and DTLS). The default value is prime256v1. --dh-file Use custom DH TLS key, stored in PEM format in the file. Flags --dh566 and --dh2066 are ignored when the DH key is taken from a file. -l, --log-file Option to set the full path name of the log file. By default, the turnserver tries to open a log file in /var/log/turnserver, /var/log, /var/tmp, /tmp and . (current) directories (which file open operation succeeds first that file will be used). With this option you can set the definite log file name. The special names are "stdout" and "-" - they will force everything to the stdout. Also, "syslog" name will redirect everything into the system log (syslog), as if the option "--syslog" was set. --alternate-server Option to set the "redirection" mode. The value of this option will be the address of the alternate server for UDP & TCP service in form of [:]. The server will send this value in the attribute ALTERNATE-SERVER, with error 300, on ALLOCATE request, to the client. Client will receive only values with the same address family as the client network endpoint address family. See RFC 5389 and RFC 5766 for ALTERNATE-SERVER functionality description. The client must use the obtained value for subsequent TURN communications. If more than one --alternate-server options are provided, then the functionality can be more accurately described as "load-balancing" than a mere "redirection". If the port number is omitted, then the default port number 3478 for the UDP/TCP protocols will be used. Colon (:) characters in IPv6 addresses may conflict with the syntax of the option. To alleviate this conflict, literal IPv6 addresses are enclosed in square brackets in such resource identifiers, for example: [2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478 . Multiple alternate servers can be set. They will be used in the round-robin manner. All servers in the pool are considered of equal weight and the load will be distributed equally. For example, if we have 4 alternate servers, then each server will receive 25% of ALLOCATE requests. An alternate TURN server address can be used more than one time with the alternate-server option, so this can emulate "weighting" of the servers. --tls-alternate-server Option to set alternative server for TLS & DTLS services in form of :. If the port number is omitted, then the default port number 5349 for the TLS/DTLS protocols will be used. See the previous option for the functionality description. -O, --redis-statsdb Redis status and statistics database connection string, if used (default - empty, no Redis stats DB used). This database keeps allocations status information, and it can be also used for publishing and delivering traffic and allocation event notifications. This database option can be used independently of --redis-userdb option, and actually Redis can be used for status/statistics and MySQL or PostgreSQL can be used for the user database. The connection string has the same parameters as redis-userdb connection string. --max-allocate-timeout Max time, in seconds, allowed for full allocation establishment. Default is 60 seconds. --denied-peer-ip= --allowed-peer-ip= Options to ban or allow specific ip addresses or ranges of ip addresses. If an ip address is specified as both allowed and denied, then the ip address is considered to be allowed. This is useful when you wish to ban a range of ip addresses, except for a few specific ips within that range. This can be used when you do not want users of the turn server to be able to access machines reachable by the turn server, but would otherwise be unreachable from the internet (e.g. when the turn server is sitting behind a NAT). The 'white" and "black" peer IP ranges can also be dynamically changed in the database. The allowed/denied addresses (white/black lists) rules are very simple: 1) If there is no rule for an address, then it is allowed; 2) If there is an "allowed" rule that fits the address then it is allowed - no matter what; 3) If there is no "allowed" rule that fits the address, and if there is a "denied" rule that fits the address, then it is denied. --pidfile File name to store the pid of the process. Default is /var/run/turnserver.pid (if superuser account is used) or /var/tmp/turnserver.pid . --proc-user User name to run the process. After the initialization, the turnserver process will make an attempt to change the current user ID to that user. --proc-group Group name to run the process. After the initialization, the turnserver process will make an attempt to change the current group ID to that group. --cli-ip Local system IP address to be used for CLI management interface. The turnserver process can be accessed for management with telnet, at this IP address and on the CLI port (see the next parameter). Default value is 127.0.0.1. You can use telnet or putty (in telnet mode) to access the CLI management interface. --cli-port CLI management interface listening port. Default is 5766. --cli-password CLI access password. Default is empty (no password). --cli-max-output-sessions Maximum number of output sessions in ps CLI command. This value can be changed on-the-fly in CLI. The default value is 256. --ne=[1|2|3] Set network engine type for the process (for internal purposes). ================================== LOAD BALANCE AND PERFORMANCE TUNING This topic is covered in the wiki page: http://code.google.com/p/rfc5766-turn-server/wiki/turn_performance_and_load_balance =================================== WEBRTC USAGE This is a set of notes for the WebRTC users: 1) WebRTC uses long-term authentication mechanism, so you have to use -a option (or --lt-cred-mech). WebRTC relaying will not work with anonymous access or with short-term authentication. With -a option, do not forget to set the realm (-r option). You will also have to set up the user accounts, for that you have a number of options: a) command-line options (-u). b) userdb config file. c) a database table (PostgreSQL or MySQL). You will have to set keys with turnadmin utility (see docs and wiki for turnadmin). You cannot use open passwords in the database. d) Redis key/value pair(s), if Redis is used. You key use either keys or open passwords with Redis; see turndb/testredisdbsetup.sh file. e) You also can use the TURN REST API. You will need shared secret(s) set either through the command line option, or through the config file, or through the database table or Redis key/value pairs. 2) Usually WebRTC uses fingerprinting (-f). 3) -v option may be nice to see the connected clients. 4) -X is needed if you are running your TURN server behind a NAT. 5) --min-port and --max-port may be needed if you want to limit the relay endpoints ports number range. =================================== TURN REST API In WebRTC, the browser obtains the TURN connection information from the web server. This information is a secure information - because it contains the necessary TURN credentials. As these credentials are transmitted over the public networks, we have a potential security breach. If we have to transmit a valuable information over the public network, then this information has to have a limited lifetime. Then the guy who obtains this information without permission will be able to perform only limited damage. This is how the idea of TURN REST API - time-limited TURN credentials - appeared. This security mechanism is based upon the long-term credentials mechanism. The main idea of the REST API is that the web server provides the credentials to the client, but those credentials can be used only limited time by an application that has to create a TURN server connection. The "classic" long-term credentials mechanism (LTCM) is described here: http://tools.ietf.org/html/rfc5389#section-10.2 http://tools.ietf.org/html/rfc5389#section-15.4 For authentication, each user must know two things: the username and the password. The nonce and the realm values are supplied by the TURN server. But LTCM is not saying anything about the nature and about the persistence of the username and of the password; and this is used by the REST API. In the TURN REST API, there is no persistent passwords for users. A user has just the username. The password is always temporary, and it is generated by the web server on-demand, when the user accesses the WebRTC page. And, actually, a temporary one-time session only, username is provided to the user, too. The temporary user is generated as: temporary-username="username" + ":" + "timestamp" where username is the persistent user name, and the timestamp format is just seconds sinse 1970 - the same value as time(NULL) function returns. The temporary password is obtained as HMAC-SHA1 function over the temporary username, with shared secret as the HMAC key, and then the result is encoded: temporary-password = base64_encode(hmac-sha1(shared-secret, temporary-username)) Both the TURN server and the web server know the same shared secret. How the shared secret is distributed among the involved entities is left to the WebRTC deployment details - this is beyond the scope of the TURN REST API. So, a timestamp is used for the temporary password calculation, and this timestamp can be retrieved from the temporary username. This information is valuable, but only temporary, while the timestamp is not expired. Without knowledge of the shared secret, a new temporary password cannot be generated. This is all formally described in Justin's Uberti TURN REST API document that can be obtained following the link "TURN REST API" in the TURN Server project's page http://code.google.com/p/rfc5766-turn-server/. Once the temporary username and password are obtained by the client (browser) application, then the rest is just 'classic" long-term credentials mechanism. For developers, we are going to describe it step-by-step below: - a new TURN client sends a request command to the TURN server. - TURN server sees that this is a new client and the message is not authenticated. - the TURN server generates a random nonce string, and return the error 401 to the client, with nonce and realm included. - the client sees the 401 error and it extracts two values from the error response: the nonce and the realm. - the client uses username, realm and password to produce a key: key = MD5(username ":" realm ":" SASLprep(password)) (SASLprep is described here: http://tools.ietf.org/html/rfc4013) - the client forms a new request, adds username, realm and nonce to the request. Then, the client calculates and adds the integrity field to the request. This is the trickiest part of the process, and it is described in the end of section 15.4: http://tools.ietf.org/html/rfc5389#section-15.4 - the client, optionally, adds the fingerprint field. This may be also a tricky procedure, described in section 15.5 of the same document. WebRTC usually uses fingerprinted TURN messages. - the TURN server receives the request, reads the username. - then the TURN server checks that the nonce and the realm in the request are the valid ones. - then the TURN server calculates the key. - then the TURN server calculates the integrity field. - then the TURN server compares the calculated integrity field with the received one - they must be the same. If the integrity fields differ, then the request is rejected. In subsequent communications, the client may go with exactly the same sequence, but for optimization usually the client, having already information about realm and nonce, pre-calculates the integrity string for each request, so that the 401 error response becomes unnecessary. The TURN server may use "--stale-nonce" option for extra security: in some time, the nonce expires and the client will obtain 438 error response with the new nonce, and the client will have to start using the new nonce. In subsequent communications, the sever and the client will always assume the same password - the original password becomes the session parameter and is never expiring. So the password is not changing while the session is valid and unexpired. So, if the session is properly maintained, it may go forever, even if the user password has been already changed (in the database). The session simply is using the old password. Once the session got disconnected, the client will have to use the new password to re-connect (if the password has been changed). An example when a new shared secret is generated every hour by the TURN server box and then supplied to the web server, remotely, is provided in the script examples/scripts/restapi/shared_secret_maintainer.pl . A very important thing is that the nonce must be totally random and it must be different for different clients and different sessions. =================================== DATABASES For the user database, the turnserver has the following options: 1) Users can be set in the command line, with multiple -u or --user options. Obviously, only a few users can be set that way, and their credentials are fixed for the turnserver process lifetime. 2) Users can be set in turnusers.conf flat file DB. The turnserver process periodically re-reads this file, so the user accounts may be changed while the turnserver is running. But still a relatively small (up to a hundred ?) number of users can be handled that way. 3) Users can be stored in PostgreSQL database, if the turnserver was compiled with PostgreSQL support. Each time turnserver checks user credentials, it reads the database (asynchronously, of course, so that the current flow of packets is not delayed in any way), so any change in the database content is immediately visible by the turnserver. This is the way if you need the best scalability. The schema for the database can be found in schema.sql file. For long-term credentials, you have to set the "keys" for the users; the "keys" are generated by the turnadmin utility. For the key generation, you need username, password and the realm. All users in the database must use the same realm value; if down the road you will decide to change the realm name, then you will have to re-generate all user keys (that can be done in a batch script). If you are using short-term credentials, then you use open passwords in the database; you will have to make sure that nobody can access the database outside of the TURN server box. 4) The same is true for MySQL database. The same schema file is applicable. The same considerations are applicable. 5) The same is true for the Redis database, but the Redis database has aa different schema - it can be found (in the form of explanation) in schema.userdb.redis. Also, in Redis you can store both "keys" and open passwords (for long term credentials) - the "open password" option is less secure but more convenient for low-security environments. For short-term credentials, you will use open passwords only. See the file turndb/testredisdbsetup.sh as an example. 6) Of course, the turnserver can be used in non-secure mode, when users are allowed to establish sessions anonymously. But in most cases (like WebRTC) that will not work. For the status and statistics database, there are two choices: 1) The simplest choice is not to use it. Do not set --redis-statsdb option, and this functionality will be simply ignored. 2) If you choose to use it, then set the --redis-statsdb option. This may be the same database as in --redis-userdb option, or it may be a different database. You may want to use different database for security or convenience reasons. Also, you can use different database management systems for the user database and for the ststus and statistics database. For example, you can use MySQL as the user database, and you can use redis for the statistics. Or you can use Redis for both. So, we have 6 choices for the user management, and 2 choices for the statistics management. These two are totally independent. So, you have overall 6*2=12 ways to handle persistent information, choose any for your convenience. You do not have to handle the database information "manually" - the turnadmin program can handle everything for you. For PostgreSQL and MySQL you will just have to create an empty database with schema.sql SQL script. With Redis, you do not have to do even that - just run turnadmin and it will set the users for you (see the turnadmin manuals). ================================= LIBRARIES In the lib/ sub-directory the build process will create TURN client messaging library. In the include/ sub-directory, the necessary include files will be placed. The C++ wrapper for the messaging functionality is located in TurnMsgLib.h header. An example of C++ code can be found in stunclient.c file. ================================= DOCS After installation, run the command: $ man turnserver or in the project root directory: $ man -M man turnserver to see the man page. In the docs/html subdirectory of the original archive tree, you will find the client library reference. After the installation, it will be placed in PREFIX/share/doc/turnserver/html. ================================= LOGS When the TURN Server starts, it makes efforts to create a log file turn_.log in the following directories: * /var/log * /log/ * /var/tmp * /tmp * current directory If all efforts failed (due to the system permission settings) then all log messages are sent only to the standard output of the process. This behavior can be controlled by --log-file, --syslog and --no-stdout-log options. ================================= TELNET CLI The turnserver process provides a telnet CLI access as statistics and basic management interface. By default, the turnserver starts a telnet CLI listener on IP 127.0.0.1 and port 5766. That can be changed by the command-cline options of the turnserver process (see --cli-ip and --cli-port options). The full list of telnet CLI commands is provided in "help" command output in the telnet CLI. ================================= CLUSTERS TURN Server can be a part of the cluster installation. But, to support the "even port" functionality (RTP/RTCP streams pairs) the client requests from a particular IP must be delivered to the same TURN Server instance, so it requires some networking setup massaging for the cluster. The reason is that the RTP and RTCP relaying endpoints must be allocated on the same relay IP. It would be possible to design a scheme with the application-level requests forwarding (and we may do that later) but it would affect the performance. ================================= FILES /etc/turnserver.conf /etc/turnuserdb.conf /usr/local/etc/turnserver.conf /usr/local/etc/turnuserdb.conf ================================= DIRECTORIES /usr/local/share/turnserver /usr/local/share/doc/turnserver /usr/local/share/examples/turnserver ================================= STANDARDS obsolete STUN RFC 3489 new STUN RFC 5389 TURN RFC 5766 TURN-TCP extension RFC 6062 TURN IPv6 extension RFC 6156 STUN/TURN test vectors RFC 5769 STUN NAT behavior discovery RFC 5780 ================================= SEE ALSO turnadmin, turnutils ====================================== WEB RESOURCES project page: http://code.google.com/p/rfc5766-turn-server/ Wiki page: http://code.google.com/p/rfc5766-turn-server/wiki/Readme forum: https://groups.google.com/forum/?fromgroups=#!forum/turn-server-project-rfc5766-turn-server/ ====================================== AUTHORS Oleg Moskalenko Gabor Kovesdan http://kovesdan.org/ Daniel Pocock http://danielpocock.com/ John Selbie (jselbie@gmail.com) Lee Sylvester Erik Johnston Roman Lisagor Vladimir Tsanev Po-sheng Lin Peter Dunkley Mutsutoshi Yoshimoto turnserver-3.2.3.1/README.turnadmin000644 001751 001751 00000011746 12315706777 016725 0ustar00olegoleg000000 000000 GENERAL INFORMATION turnadmin is a TURN administration tool. This tool can be used to manage the user accounts (add/remove users, generate TURN keys for the users). For security reasons, we do not recommend storing passwords openly. The better option is to use pre-processed "keys" which are then used for authentication. These keys are generated by turnadmin. Turnadmin is a link to turnserver binary, but turnadmin performs different functions. Options note: turnadmin has long and short option names, for most options. Some options have only long form, some options have only short form. Their syntax somewhat different, if an argument is required: The short form must be used as this (for example): $ turnadmin -u ... The long form equivalent must use the "=" character: $ turnadmin --user= ... If this is a flag option (no argument required) then their usage are the same, for example: $ turnadmin -k ... is equivalent to: $ turnadmin --key ... ===================================== NAME turnadmin - a TURN relay administration tool. SYNOPSIS $ turnadmin [command] [options] $ turnadmin [ -h | --help] DESCRIPTION Commands: -k, --key Generate key for a long-term credentials mechanism user. -a, --add Add or update a long-term user. -A, --add-st Add or update a short-term credentials mechanism user. -d, --delete Delete a long-term user. -D, --delete-st Delete a short-term user. -l, --list List long-term users in the database. -L, --list-st List short-term users in the database. -s, --set-secret= Add shared secret for TURN RESP API -S, --show-secret Show stored shared secrets for TURN REST API -X, --delete-secret= Delete a shared secret. --delete-all_secrets Delete all shared secrets for REST API. NOTE: if you are using the flat file for the user database, then you will have to use a text editor to set or show the shared secrets. Options with required values: -b, --userdb File-based user database file name (default - turnuserdb.conf). See the --userdb option in the turnserver section. -e, --psql-userdb PostgreSQL user database connection string. See the --psql-userdb option in the turnserver section. -M, --mysql-userdb MySQL user database connection string. See the --mysql-userdb option in the turnserver section. -N, --redis-userdb Redis user database connection string. See the --redis-userdb option in the turnserver section. -u, --user User name. -r, --realm Realm, for long-term credentials mechanism only. -p, --password Password. -H, --sha256 Use SHA256 keys and message integrity. By default, HMAC-SHA1 is used for the message digest calculation, and MD5 is used for the key storage encryption. -h, --help Help. Generate a key: $ turnadmin -k -u -r -p Add/update a user (and realm) in the userdb file or in the database: $ turnadmin -a [-b | -e | -M | -N ] -u -r -p Delete a user from the userdb file or from the database: $ turnadmin -d [-b | -e | -M | -N ] -u List all long-term users in MySQL database: $ turnadmin -l --mysql-userdb="" List all short-term users in Redis database: $ turnadmin -L --redis-userdb="" Set secret in MySQL database: $ turnadmin -s --mysql-userdb="" Show secret stored in PostgreSQL database: $ turnadmin -S --psql-userdb="" Help: $ turnadmin -h ======================================= DOCS After installation, run the command: $ man turnadmin or in the project root directory: $ man -M man turnadmin to see the man page. ===================================== FILES /etc/turnserver.conf /etc/turnuserdb.conf /usr/local/etc/turnserver.conf /usr/local/etc/turnuserdb.conf ===================================== DIRECTORIES /usr/local/share/turnserver /usr/local/share/doc/turnserver /usr/local/share/examples/turnserver ====================================== SEE ALSO turnserver, turnutils ====================================== WEB RESOURCES project page: http://code.google.com/p/rfc5766-turn-server/ Wiki page: http://code.google.com/p/rfc5766-turn-server/wiki/Readme forum: https://groups.google.com/forum/?fromgroups=#!forum/turn-server-project-rfc5766-turn-server/ ====================================== AUTHORS Oleg Moskalenko Gabor Kovesdan http://kovesdan.org/ Daniel Pocock http://danielpocock.com/ John Selbie (jselbie@gmail.com) Lee Sylvester Erik Johnston Roman Lisagor Vladimir Tsanev Po-sheng Lin Peter Dunkley Mutsutoshi Yoshimoto turnserver-3.2.3.1/Makefile.in000755 001751 001751 00000020763 12315706777 016114 0ustar00olegoleg000000 000000 LIBEVENT_INCLUDE = -I${PREFIX}/include/ -I/usr/local/include/ INCFLAGS = -Isrc -Isrc/apps/common -Isrc/server -Isrc/client -Isrc/client++ ${LIBEVENT_INCLUDE} CFLAGS += ${INCFLAGS} MAKE_DEPS = Makefile LIBCLIENTTURN_HEADERS = src/ns_turn_defs.h src/client++/TurnMsgLib.h src/client/ns_turn_ioaddr.h src/client/ns_turn_msg.h src/client/ns_turn_msg_defs.h src/client/ns_turn_msg_addr.h LIBCLIENTTURN_MODS = src/client/ns_turn_ioaddr.c src/client/ns_turn_msg_addr.c src/client/ns_turn_msg.c LIBCLIENTTURN_DEPS = ${LIBCLIENTTURN_HEADERS} ${MAKE_DEPS} LIBCLIENTTURN_OBJS = build/obj/ns_turn_ioaddr.o build/obj/ns_turn_msg_addr.o build/obj/ns_turn_msg.o SERVERTURN_HEADERS = src/server/ns_turn_allocation.h src/server/ns_turn_ioalib.h src/server/ns_turn_khash.h src/server/ns_turn_maps_rtcp.h src/server/ns_turn_maps.h src/server/ns_turn_server.h src/server/ns_turn_session.h SERVERTURN_DEPS = ${LIBCLIENTTURN_HEADERS} ${SERVERTURN_HEADERS} ${MAKE_DEPS} SERVERTURN_MODS = ${LIBCLIENTTURN_MODS} src/server/ns_turn_allocation.c src/server/ns_turn_maps_rtcp.c src/server/ns_turn_maps.c src/server/ns_turn_server.c COMMON_HEADERS = src/apps/common/apputils.h src/apps/common/ns_turn_utils.h src/apps/common/stun_buffer.h COMMON_MODS = src/apps/common/apputils.c src/apps/common/ns_turn_utils.c src/apps/common/stun_buffer.c COMMON_DEPS = ${LIBCLIENTTURN_DEPS} ${COMMON_MODS} ${COMMON_HEADERS} IMPL_HEADERS = src/apps/relay/ns_ioalib_impl.h src/apps/relay/ns_sm.h src/apps/relay/turn_ports.h IMPL_MODS = src/apps/relay/ns_ioalib_engine_impl.c src/apps/relay/turn_ports.c IMPL_DEPS = ${COMMON_DEPS} ${IMPL_HEADERS} ${IMPL_MODS} HIREDIS_HEADERS = src/apps/common/hiredis_libevent2.h HIREDIS_MODS = src/apps/common/hiredis_libevent2.c SERVERAPP_HEADERS = src/apps/relay/userdb.h src/apps/relay/tls_listener.h src/apps/relay/mainrelay.h src/apps/relay/turncli.h src/apps/relay/dtls_listener.h src/apps/relay/libtelnet.h ${HIREDIS_HEADERS} SERVERAPP_MODS = src/apps/relay/mainrelay.c src/apps/relay/netengine.c src/apps/relay/libtelnet.c src/apps/relay/turncli.c src/apps/relay/userdb.c src/apps/relay/tls_listener.c src/apps/relay/dtls_listener.c ${HIREDIS_MODS} SERVERAPP_DEPS = ${SERVERTURN_MODS} ${SERVERTURN_DEPS} ${SERVERAPP_MODS} ${SERVERAPP_HEADERS} ${COMMON_DEPS} ${IMPL_DEPS} lib/libturnclient.a TURN_BUILD_RESULTS = bin/turnutils_stunclient bin/turnutils_rfc5769check bin/turnutils_uclient bin/turnserver bin/turnutils_peer lib/libturnclient.a include/turn/ns_turn_defs.h all: ${TURN_BUILD_RESULTS} test: check check: bin/turnutils_rfc5769check bin/turnutils_rfc5769check include/turn/ns_turn_defs.h: src/ns_turn_defs.h ${RMCMD} include ${MKBUILDDIR} include/turn/client cp -pf src/client/*.h include/turn/client/ cp -pf src/client++/*.h include/turn/client/ cp -pf src/ns_turn_defs.h include/turn/ bin/turnutils_uclient: ${COMMON_DEPS} src/apps/uclient/session.h lib/libturnclient.a src/apps/uclient/mainuclient.c src/apps/uclient/uclient.c src/apps/uclient/uclient.h src/apps/uclient/startuclient.c src/apps/uclient/startuclient.h ${MKBUILDDIR} bin ${CC} ${CPPFLAGS} ${CFLAGS} src/apps/uclient/uclient.c src/apps/uclient/startuclient.c src/apps/uclient/mainuclient.c ${COMMON_MODS} -o $@ -Llib -lturnclient -Llib ${LDFLAGS} bin/turnutils_stunclient: ${COMMON_DEPS} lib/libturnclient.a src/apps/stunclient/stunclient.c pwd ${MKBUILDDIR} bin ${CC} ${CPPFLAGS} ${CFLAGS} src/apps/stunclient/stunclient.c ${COMMON_MODS} -o $@ -Llib -lturnclient -Llib ${LDFLAGS} bin/turnutils_rfc5769check: ${COMMON_DEPS} lib/libturnclient.a src/apps/rfc5769/rfc5769check.c pwd ${MKBUILDDIR} bin ${CC} ${CPPFLAGS} ${CFLAGS} src/apps/rfc5769/rfc5769check.c ${COMMON_MODS} -o $@ -Llib -lturnclient -Llib ${LDFLAGS} bin/turnserver: ${SERVERAPP_DEPS} ${MKBUILDDIR} bin ${RMCMD} bin/turnadmin ${CC} ${CPPFLAGS} ${CFLAGS} ${DBCFLAGS} ${IMPL_MODS} -Ilib ${SERVERAPP_MODS} ${COMMON_MODS} ${SERVERTURN_MODS} -o $@ ${DBLIBS} ${LDFLAGS} cd bin; ln -s turnserver turnadmin bin/turnutils_peer: ${COMMON_DEPS} ${LIBCLIENTTURN_MODS} ${LIBCLIENTTURN_DEPS} lib/libturnclient.a src/apps/peer/mainudpserver.c src/apps/peer/udpserver.h src/apps/peer/udpserver.c ${MKBUILDDIR} bin ${CC} ${CPPFLAGS} ${CFLAGS} src/apps/peer/mainudpserver.c src/apps/peer/udpserver.c ${COMMON_MODS} -o $@ -Llib -lturnclient -Llib ${LDFLAGS} ### Client Library: lib/libturnclient.a: ${LIBCLIENTTURN_OBJS} ${LIBCLIENTTURN_DEPS} ${MKBUILDDIR} lib ${ARCHIVERCMD} $@ ${LIBCLIENTTURN_OBJS} build/obj/ns_turn_ioaddr.o: src/client/ns_turn_ioaddr.c ${LUBCLIENTTURN_DEPS} ${MKBUILDDIR} build/obj ${CC} ${CPPFLAGS} ${CFLAGS} -c src/client/ns_turn_ioaddr.c -o $@ build/obj/ns_turn_msg_addr.o: src/client/ns_turn_msg_addr.c ${LUBCLIENTTURN_DEPS} ${MKBUILDDIR} build/obj ${CC} ${CPPFLAGS} ${CFLAGS} -c src/client/ns_turn_msg_addr.c -o $@ build/obj/ns_turn_msg.o: src/client/ns_turn_msg.c ${LUBCLIENTTURN_DEPS} ${MKBUILDDIR} build/obj ${CC} ${CPPFLAGS} ${CFLAGS} -c src/client/ns_turn_msg.c -o $@ ### Clean all: clean: ${RMCMD} bin build lib obj *bak *~ */*~ */*/*~ */*/*/*~ *core include Makefile tmp distclean: clean ### Install all: install: all ${MAKE_DEPS} ${MKDIR} ${DESTDIR}${PREFIX} ${MKDIR} ${DESTDIR}${BINDIR} ${MKDIR} ${DESTDIR}${MANPREFIX}/man/man1 ${MKDIR} ${DESTDIR}${CONFDIR} ${MKDIR} ${DESTDIR}${LIBDIR} ${MKDIR} ${DESTDIR}${EXAMPLESDIR} ${MKDIR} ${DESTDIR}${DOCSDIR} ${MKDIR} ${DESTDIR}${SCHEMADIR} ${MKDIR} ${DESTDIR}${TURNINCLUDEDIR} ${INSTALL_PROGRAM} bin/turnserver ${DESTDIR}${BINDIR} ${INSTALL_PROGRAM} bin/turnadmin ${DESTDIR}${BINDIR} ${INSTALL_PROGRAM} bin/turnutils_uclient ${DESTDIR}${BINDIR} ${INSTALL_PROGRAM} bin/turnutils_peer ${DESTDIR}${BINDIR} ${INSTALL_PROGRAM} bin/turnutils_stunclient ${DESTDIR}${BINDIR} ${INSTALL_MAN} man/man1/turnserver.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_MAN} man/man1/turnadmin.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_MAN} man/man1/turnutils.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_MAN} man/man1/turnutils_uclient.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_MAN} man/man1/turnutils_stunclient.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_MAN} man/man1/turnutils_peer.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_MAN} man/man1/rfc5766-turn-server.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_STATIC_LIB} lib/libturnclient.a ${DESTDIR}${LIBDIR} ${INSTALL_DATA} LICENSE ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} README.turnserver ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} README.turnadmin ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} README.turnutils ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} INSTALL ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} postinstall.txt ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} turndb/schema.sql ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} turndb/schema.sql ${DESTDIR}${SCHEMADIR} ${INSTALL_DATA} turndb/testredisdbsetup.sh ${DESTDIR}${SCHEMADIR} ${INSTALL_DATA} turndb/schema.userdb.redis ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} turndb/schema.userdb.redis ${DESTDIR}${SCHEMADIR} ${INSTALL_DATA} turndb/schema.stats.redis ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} turndb/schema.stats.redis ${DESTDIR}${SCHEMADIR} ${INSTALL_DATA} examples/etc/turnserver.conf ${DESTDIR}${CONFDIR}/turnserver.conf.default ${INSTALL_DATA} examples/etc/turnuserdb.conf ${DESTDIR}${CONFDIR}/turnuserdb.conf.default ${INSTALL_DIR} examples/etc ${DESTDIR}${EXAMPLESDIR} ${INSTALL_DIR} examples/scripts ${DESTDIR}${EXAMPLESDIR} ${RMCMD} ${DESTDIR}${EXAMPLESDIR}/scripts/rfc5769.sh ${INSTALL_DIR} include/turn/client ${DESTDIR}${TURNINCLUDEDIR} ${INSTALL_DATA} include/turn/ns_turn_defs.h ${DESTDIR}${TURNINCLUDEDIR} ${MORECMD} ${DESTDIR}${DOCSDIR}/postinstall.txt deinstall: ${MAKE_DEPS} ${PKILL_PROGRAM} turnserver || ${ECHO_CMD} OK ${RMCMD} ${DESTDIR}${DOCSDIR} ${RMCMD} ${DESTDIR}${SCHEMADIR} ${RMCMD} ${DESTDIR}${BINDIR}/turnserver ${RMCMD} ${DESTDIR}${BINDIR}/turnadmin ${RMCMD} ${DESTDIR}${BINDIR}/turnutils_peer ${RMCMD} ${DESTDIR}${BINDIR}/turnutils_uclient ${RMCMD} ${DESTDIR}${BINDIR}/turnutils_stunclient ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/turnserver.1 ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/turnadmin.1 ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/turnutils.1 ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/turnutils_uclient.1 ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/turnutils_stunclient.1 ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/turnutils_peer.1 ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/rfc5766-turn-server.1 ${RMCMD} ${DESTDIR}${LIBDIR}/libturnclient.a ${RMCMD} ${DESTDIR}${EXAMPLESDIR}/ ${RMCMD} ${DESTDIR}${CONFDIR}/turnserver.conf.default ${RMCMD} ${DESTDIR}${CONFDIR}/turnuserdb.conf.default ${RMCMD} ${DESTDIR}${TURNINCLUDEDIR} uninstall: deinstall reinstall: deinstall install turnserver-3.2.3.1/TODO000644 001751 001751 00000007076 12315706777 014536 0ustar00olegoleg000000 000000 ================================================================== ### I. PLATFORMS SUPPORT ### ================================================================== 1) Fedora official package (turnserver or rfc5766-turn-server ? TBD). 2) MS Windows support. Cygwin is supported. A "real" MS-Windows port would involve a usable GUI. ================================================================== ### II. DOCS ### ================================================================== 1) User's manual. 2) Developer's manual. ================================================================== ### III. NETWORK ENGINE ### ================================================================== 1) Exclusive IP addresses for relay ================================================================== ### IV. PERFORMANCE OPTIMIZATION ### ================================================================== 1) A smarter load balancer has to be implemented. The load balancer has to have a heartbeat channels with the slave servers, currently it is only just a dumb round-robin load distributor. 2) For a large enterprise, a user-space stack to be integrated. An another socket abstraction to be implemented, the one that uses the user-space TCP/IP stack with zero memory copy. This is an ambitious goal that would increase the system scaleability, significantly. The stock TCP/IP stack in UNIX and in MS Windows do not scale gracefully. We are trying to suppress those issues in the TURN Server, by using an advanced synchronous I/O technique, but still the underlying stock TCP/IP stack is a limitation. 3) Multiple authentication threads. ================================================================== ### V. SECURITY ### ================================================================== 1) RADIUS integration ? 2) Watch new TURN security draft. oAuth integration. ================================================================== ### VI. STANDARDS SUPPORT ### ================================================================== 1) Follow the draft ICE endpoint mobility standard and add changes when necessary: https://ietf.org/doc/draft-wing-mmusic-ice-mobility/ 2) For extra difficult NAT/FWs, consider implementing Websockets. 3) MS TURN, MS STUN extensions. 4) Origin draft. ================================================================== ### VII. MISC FEATURES ### ================================================================== 1) Locale support (?). Currently we assume that all text data is 8-bits ASCII encoded, like C locale. It would be nice to support localized strings (both 8-bits and 2-bytes). But I am not sure whether this is really important, given the essentially backend nature of the TURN Server. The TURN server is so deeply "hidden" in the network infrastructure that the significant code complication may be unjustified. 2) HTTP or GUI status monitor and management. For enterprise users, a management (configuration, status and statistics) GUI has to be implemented. Currently, all these features are available through the shell command line, telnet client and through Redis command line. 3) Traffic recording (for selected allocations). That would be a helpful feature for a large enterprise (for testing and security purposes). 4) Ganglia monitoring. ================================================================== ### VIII. CODING STUFF ### ================================================================== Nope ================================================================== turnserver-3.2.3.1/INSTALL000644 001751 001751 00000102724 12315706777 015073 0ustar00olegoleg000000 000000 I. TURN Server as a standard OS package At the present time, several operation systems have this project pre-packaged: 1) FreeBSD (and PC-BSD) have this project as a "port", named "turnserver", in /usr/ports/net/turnserver directory. Installation is very simple: # optional commands, to update the ports tree: $ sudo portsnap fetch $ sudo portsnap update # Build and install the TURN Server: $ cd /usr/ports/net/turnserver $ sudo make install clean 2) Debian "jessie" (and the recent version of Ubuntu and Mint) have this project packaged as "rfc5766-turn-server", see the link: http://packages.qa.debian.org/r/rfc5766-turn-server.html In the new Debian "jessie", and in the related Ubuntu and Mint, you will be able to just select rfc5766-turn-server from the packages list and install it through Synaptic or through the package manager. 3) ArchLinux has a TURN server package: https://aur.archlinux.org/packages/rfc5766-turn-server/ 4) OpenSUSE has a package, too: https://build.opensuse.org/package/show/home:ehauenstein/rfc5766-turn-server If you are using a pre-packaged TURN server then you can skip to the section IX. II. DOWNLOAD You have to download the archive file turnserver-*.tar.gz and unpack it: $ tar xfz turnserver-*.tgz it will create the directory 'turnserver-*' with all sources, build files, examples and documentation. III. BUILD If you are sure that you system is ready for the build (see the section "Extra libraries and Utilities" below) then you can build the system. First, you have to run the configure script: $ cd turnserver-* $ ./configure It will create a Makefile customized for your system. By default, the generated Makefile will be set to install everything in: - /usr on Solaris. - /usr/pkg on NetBSD. - /usr/local everywhere else. The binaries will be copied in bin subdirectory of the installation destination, config files copied to etc subdirectory. There will be also documents, examples and some other files, in separate directories. You can change the root configured destination directory by setting PREFIX variable in the configure command line. For example: $ PREFIX=/opt ./configure Or: $ ./configure --prefix=/opt You can change the auxiliary configured destination sub-directories by setting BINDIR, CONFDIR, MANPREFIX, EXAMPLESDIR, DOCSDIR, LIBDIR, SCHEMADIR and TURNINCLUDEDIR variables in the configure command line. For example: $ PREFIX=/opt BINDIR=/opt/bin64 CONFDIR=/opt/conf ./configure Or: $ ./configure --prefix=/opt --bindir=/opt/bin64 --confdir=/opt/conf You also can change the compilation and link options by setting common build variables in the configure command line. For example: $ CC=clang CFLAGS=-D_CAURIB LDFLAGS=-lshanka ./configure --prefix=/opt/shy See below a separate INSTALL section for more details. The script configure is a proprietary script. It will create a Makefile that you can use to build the project: $ make The make command without options will do the following: - compile the code. - create bin/ sub-directory and put the TURN server, TURN admin and "utility" programs there. - create lib/ sub-directory and put the client library there. - create include/turn/ sub-directory and put include files there. The programs can be either called directly, or a shell scripts can be used. The script examples are located in examples/scripts directory. These scripts are just examples: you can run them successfully for the tests, but you will have to change the script parameters for your real environment. The command: $ sudo make install will install everything into the system file structure (see below). (NOTE: On NetBSD, use "su root -c"). The command: $ sudo make deinstall will remove all installed TURN Server files from your system. The command: $ make clean will clean all results of the build and configuration actions. Do not run "make clean" before "make deinstall". The "clean" command will remove the Makefile and you will not be able to "deinstall" then. If that has happened, then run ./configure and make again, then deinstall and then clean. NOTE: On most modern systems, the build will produce dynamically linked executables. If you want statically linked executables, you have to modify, accordingly, the Makefile.in template file. IV. INSTALL This step is optional. You can run the turnserver from the original build directory, successfully, without installing the TURN server into the system. You have to install the turnserver only if you want to integrate the turnserver in your system. Run the command: $ make install It will install turnserver in /usr/local/ directory (or to whatever directory was set in the PREFIX variable). You will have to copy /usr/local/etc/turnserver.conf.default to /usr/local/etc/turnserver.conf file and adjust your runtime configuration. This command will also: - copy the content of examples subdirectory into PREFIX/share/examples/turnserver/ directory; - copy the content of include/turn subdirectory into PREFIX/include/turn/ directory; - copy the database schema file turndb/schema.sql into PREFIX/share/turnserver/ directory; - copy all docs into PREFIX/share/doc/turnserver/ directory. The installation destination of "make install" can be changed by using DESTDIR variable, for example: $ ./configure --prefix=/usr $ make $ make DESTDIR=/opt install In this example, the root installation directory will be /opt/usr. The "configure" script by default generates a Makefile with "rpath" option set for the binaries linking (if your compiler allows that option). If that is not desirable (like in some OS packaging procedures), then run the "configure" script with --disable-rpath option. If you do not want to use the rpath linking option, or you OS or compiler do not allows that, then after the installation, you may have to adjust the system-wide shared library search path by using "ldconfig -n " (Linux), "ldconfig -m " (BSD) or "crle -u -l " (Solaris). Your system must be able to find the libevent2, openssl and (optionally) PostgreSQL and/or MySQL (MariaDB) and/or Redis shared libraries, either with the help of the system-wide library search configuration or by using LD_LIBRARY_PATH. "make install" will make a non-garantied effort to add automatically PREFIX/lib and /usr/local/lib to the libraries search path, but if you have some libraries in different non-default directories you will have to add them manually to the search path, or you will have to adjust LD_LIBRARY_PATH. V. PLATFORMS The TURN Server is using generic *NIX system APIs and is supposed to be usable on wide range of *NIX systems. The following platforms have been used in the development: - Linux Ubuntu 11.x and 12.x, i386 and x86_64 - FreeBSD 6.x, i386 - FreeBSD 8.x, i386 - PC-BSD 9.x, x86_64 - Solaris 11, x86_64 - Linux CentOS / Red Hat Enterprise Edition 6.3, x86_64 (amd64) - Linux CentOS / Red Hat Enterprise Edition 6.4, x86_32 (i386) - Linux Debian 'Squeeze', i386 - Linux Mint 14.1 'Nadia', i386 - Linux Debian 'Wheezy', x86_64 - Cygwin 1.7.20 - NetBSD 6.0.1 - OpenBSD 5.3 - Amazon Linux - Mac OS X Mountain Lion - ArchLinux - Fedora 19 - OpenSUSE 12.3 x86_64 It must work on many other *NIXes, as well. The configure script and/or Makefile may need adjustments for other *NIXes not mentioned above. The code of the client messaging library can be compiled and used on Windows, too, but it is not supported for now. VI. COMPILERS The TURN Server is written in C programming language, for portability and for the performance reasons. The tested C compilers are: - gcc 3.4.4 thru 4.8.1 - clang 3.0 or better - Solaris Studio 12.3 C compiler, version 5.12 It may be compiled with others compilers, too. The code is compatible with C++ compiler, and a C++ compiler (like g++) can be used for the compilation, too: $ CC=g++ ./configure $ make VII. WHICH EXTRA LIBRARIES AND UTILITIES YOU NEED In addition to common *NIX OS services and libraries, to compile this code, OpenSSL (version 1.0.0a or better recommended) and libevent2 (version 2.0.5 or better) are required, the PostgreSQL C client development setup is optional, the MySQL (MariaDB) C client development setup is optional, and the Hiredis development files for Redis database access are optional. For fully functional build, the extra set of libraries must be installed in full version (the development headers and the libraries to link with). For runtime, only runtime setup is required. If the build is modified for static linking, then even runtime installation is not needed. OpenSSL, libevent2, PostgreSQL, MySQL (or MariaDB) and Hiredis libraries can be downloaded from their web sites: - http://www.openssl.org (required); - http://www.libevent.org (required); - http://www.postgresql.org (optional); - http://www.mysql.org (or http://mariadb.org) (optional); - http://redis.io (optional). The installations are pretty straightforward - the usual "./configure" and "make install" commands. Install them into their default locations - the configure script and the Makefile are assuming that they are installed in their default locations. If not, then you will have to modify those. Most modern popular systems (FreeBSD / PC-BSD, Linux Ubuntu 11.10+, Debian Wheezy, Linux Mint 14+, Amazon Linux, Fedora) have a simpler way of the third party tools installation: *) PC-BSD or FreeBSD (the FRESH ports database is assumed to be installed, with the turnserver port included): $ cd /usr/ports/net/turnserver $ sudo make install clear That's it - that command will install the TURN server with all necesary thrid-party tools. If you system have no fresh ports repository: $ cd /usr/ports/security/openssl/ $ sudo make install clean $ cd /usr/ports/devel/libevent2/ $ sudo make install clean $ cd /usr/ports/databases/postgresql84-client/ (or any other version) $ sudo make install clean $ cd /usr/ports/databases/mysql51-client/ (or any other version) $ sudo make install clean $ cd /usr/ports/databases/hiredis/ $ sudo make install clean **) Linux Ubuntu 11.10+, Debian Wheezy, Mint 14+: $ sudo apt-get install libssl-dev $ sudo apt-get install libevent-dev $ sudo apt-get install libpq-dev $ sudo apt-get install mysql-client $ sudo apt-get install libmysqlclient-dev $ sudo apt-get install libhiredis-dev or you can use Synaptic or other software center. ***) Fedora: $ sudo yum install openssl-devel $ sudo yum install libevent $ sudo yum install libevent-devel $ sudo yum install postgresql-devel $ sudo yum install postgresql-server $ sudo yum install mysql-devel $ sudo yum install mysql-server $ sudo yum install hiredis $ sudo yum install hiredis-devel ****) Amazon Linux is similar to Fedora, but: - you have to install gcc first: $ sudo yum install gcc - hiredis packages are not available, so do not issue the hiredis installation commands. Redis support will not be compiled, unless you install it "manually" before the TURN server compilation. For Amazon EC2 AMIs, we install the redis manually in the system. But the TURN server can be perfectly installed without redis support - if you do not need it. *****) Some OSes in Debian family (Debian Squeeze and pre-11.10 Ubuntus) setups are similar to Debian Wheezy, although some packages have different names. ******) On some CentOS / RedHat 6.x systems you have to install libevent2 "manually", and optionally you have to download and install Hiredis, but everything else can be found in the software repository. Also, if you would like to make an RPM for CentOS, check the directory rpm/ with the instructions. NOTE: If your tools are installed in non-standard locations, you will have to adjust CFLAGS and LDFLAGS environment variables for TURN server ./configure script. For example, to configure the TURN server with Solaris 11 PostgreSQL 32-bits setup, you may use a command like this: $ CFLAGS="${CFLAGS} -I/usr/postgres/9.2-pgdg/include/" LDFLAGS="${LDFLAGS} -L/usr/postgres/9.2-pgdg/lib/" ./configure Dynamic library paths: You may also have to adjust the turn server start script, add PostgreSQL and/or MySQL and/or Redis runtime library path to LD_LIBRARY_PATH. Or you may find that it would be more convenient to adjust the system-wide shared library search path by using commands: on Linux: $ ldconfig -n or on BSD: $ ldconfig -m or on Solaris: $ crle -u -l On Mac OS X, you have three different choices for dynamic libraries handling: 1) Use DYLD_LIBRARY_PATH environment variable in runtime; OR 2) Before the compilation, check the dynamic libraries and adjust their identification names, if necessary, to the absolute library path or to @rpath/. For exmple, the MySQL dynamic library may need that adjustment. You will have to use "adjust_name_tool" with -id option for that; OR 3) After the compilation, you can use the same tool, "adjust_name_tool", with option -change, to adjust the library paths values in the binary, where necessary. All library paths must be absolute paths or @rpath/... . See also the next section. NOTE: See "PostgreSQL setup" and "MySQL setup" and "Redis setup" sections below for more database setup information. NOTE: If you do not install PostgreSQL or MySQL or Redis then you will be limited to flat files for user database. It will work great for smaller user databases (like 100 users) but for larger systems you will need PostgreSQL or MySQL or Redis. NOTE: To run PostgreSQL or MySQL or Redis server on the same system, you will also have to install a corresponding PostgreSQL or MySQL or Redis server package. The DB C development packages only provide development libraries, and client libraries only provide client access utilities and runtime libraries. The server packages may include everything - client, C development and server runtime. NOTE: OpenSSL to be installed before libevent2. When libevent2 is building, it is checking whether OpenSSL has been already installed, and which version of OpenSSL. If the OpenSSL is missed, or too old, then libevent_openssl library is not being created during the build, and you will not be able to compile the TURN Server with TLS support. NOTE: An older libevent version, version 1.x.x, is often included in some *NIX distributions. That version has its deficiencies and is inferior to the newer libevent2, especially in the performance department. This is why we are not providing backward compatibility with the older libevent 1.x version. If you have a system with older libevent, then you have to install the new libevent2 from their web site. It was tested with older *NIXes (like FreeBSD 6.x) and it works just fine. NOTE: For extra security features (DTLS and SHA256) support, OpenSSL version 1.0.0a or newer is recommended. Older versions do not support DTLS, reliably, in some cases. For example, the Debian 'Squeeze' Linux supplies 0.9.8 version of OpenSSL, that does not work correctly with DTLS over IPv6. If your system already has an older version of OpenSSL installed (usually in directory /usr) then you may want to install your newer OpenSSL "over" the old one (because it will most probably will not allow removal of the old one). When installing the newer OpenSSL, run the OpenSSL's configure command like this: $ ./config --prefix=/usr that will set the installation prefix to /usr (without "--prefix=/usr" by default it would be installed to /usr/local). This is necessary if you want to overwrite your existing older OpenSSL installation. VIII. BUILDING WITH NON-DEFAULT PREFIX DIRECTORY Say, you have an older system with old openssl and old libevent library and you do not want to change that, but you still want to build the turnserver. Do the following steps: 1) Download new openssl from openssl.org. 2) Configure and build new openssl and install it into /opt: $ ./config --prefix=/opt $ make $ make install 3) Download the latest libevent2 from libevent.org, configure and install it into /opt: $ ./configure --prefix=/opt $ make $ make install 4) Change directory to rfc5766-turn-server and build it: $ ./configure --prefix=/opt $ make After that, you can either use it locally, or install it into /opt. But remember that to run it, you have to adjust your LD_LIBRARY_PATH, like that: $ LD_LIBRARY_PATH=/opt/lib ./bin/turnserver An alternative would be adjusting the system-wide shared library search path by using $ ldconfig -n (Linux) $ ldconfig -m (BSD) $ crle -u -l (Solaris) IX. TEST SCRIPT SETS First of all, we can use test vectors from RFC 5769 to double-check that our STUN/TURN message encoding algorithms work properly. Run the utility: $ cd examples $ ./scripts/rfc5769.sh It will perform several protocol checks and print the results on the output. If anything has compiled wrongly (TURN Server, or OpenSSL libraries) then you will see some errors. Now, you can perform the TURN functionality test (bare minimum TURN example). If everything compiled properly, then the following programs must run together successfully, simulating TURN network routing in local loopback networking environment: Open two shell screens or consoles: In shell number 1, run TURN server application: $ cd examples $ ./scripts/basic/relay.sh In shell number 2, run test client application: $ cd examples $ ./scripts/basic/udp_c2c_client.sh If the client application produces output and in approximately 22 seconds prints the jitter, loss and round-trip-delay statistics, then everything is fine. There is another more complex test: In shell number 1, run TURN server application: $ cd examples $ ./scripts/basic/relay.sh In shell number 2, run "peer" application: $ cd examples $ ./scripts/peer.sh In shell number 3, run test client application: $ cd examples $ ./scripts/basic/udp_client.sh (or ./scripts/basic/tcp_client.sh) There is a similar set of examples/scripts/longtermsecure/* scripts for TURN environment with long-term authentication mechanism. This set of scripts is more complex, and checking the scripts options is useful for understanding how the TURN Server works: In shell number 1, run secure TURN server application: $ cd examples $ ./scripts/longtermsecure/secure_relay.sh In shell number 2, run "peer" application: $ cd examples $ ./scripts/peer.sh In shell number 3, run secure test client application: $ cd examples $ ./scripts/longtermsecure/secure_udp_client.sh (or ./scripts/longtermsecure/secure_tcp_client.sh) (or ./scripts/longtermsecure/secure_tls_client.sh) (or ./scripts/longtermsecure/secure_dtls_client.sh) (or ./scripts/longtermsecure/secure_udp_c2c.sh for "peerless" client-to-client communications) The provided scripts are set for the local loopback communications, as an example and as a test environment. Real networking IPs must be used in real work environments. Try wireshark to check the communications between client, turnserver and the peer. Check the README.* files and the comments in the scripts relay.sh and secure_relay.sh as a guidance how to run the TURN server. X. OS X compilation notes OS X usually has an older version of openssl supplied, with some Apple additions. The best option is to install a good fresh openssl development library, free of Apple tweaks, from http://www.openssl.org. But the "native" openssl will work, too. XI. MS Windows and Cygwin support Currently, this project cannot be compiled under MS Windows. As the project is using fairly straightforward *NIX API, it is supported under Cygwin environment in MS Windows. One note for Cygwin users: we recommended libevent2 installation from the cygwin "ports" site: http://sourceware.org/cygwinports/ . You will have to install libevent2 runtime and libevent-devel packages. "Manual" libevent2 compilation and installation in Cygwin is not recommended and does not garantee a good outcome. XII. CLIENT API LIBRARY. The compilation process will create lib/ sub-directory with libturnclient.a library. The header files for this library are located in include/turn/client/ sub-directory. The C++ wrapper for the messaging functionality is located in TurnMsgLib.h header. An example of C++ code can be found in stunclient.c file. This file is compiled as a C++ program if C++ compiler is used, and as a C program if C compiler is used. XIII. DOCS After installation, the man page turnserver(1) must be available. The man page is located in man/man1 subdirectory. If you want to see the man page without installation, run the command: $ man -M man turnserver HTML-formatted client library functions reference is located in docs/html subdirectory of the original archive tree. After the installation, it will be placed in PREFIX/share/doc/turnserver/html. XIV. PostgreSQL setup The site http://www.postgresql.org site has excellent extensive documentation. For a quick-start guide, you can take a look into this page: http://www.freebsddiary.org/postgresql.php. That page is written for FreeBSD users, but it has lots of generic information applicable to other *NIXes, too. For the psql-userdb TURN server parameter, you can either set a PostgreSQL connection string, or a PostgreSQL URI, see the link: For 8.4 PostgreSQL version: http://www.postgresql.org/docs/8.4/static/libpq-connect.html For newer 9.x versions: http://www.postgresql.org/docs/9.2/static/libpq-connect.html#LIBPQ-CONNSTRING. In the PostgreSQL connection string or URI, you can set the host, the access port, the database name, the user, and the user password (if the access is secured). Numerous other parameters can be set, see the links above. The TURN server will blindly use that connection string without any modifications. You are responsible for the right connection string format. Below are the steps to setup the PostgreSQL database server from scratch: 1) Install PostgreSQL server. 2) Find and edit Postgres' pg_hba.conf file to set the access options (see docs). On different systems, it may be located in different places. Set the lines for local access as "trust" for now (you can change it later), for TCP/IP access set the value as "md5". To set TCP/IP access from any host, use "0.0.0.0/0" for IPv4, and "::/0" for IPv6. 3) Edit postgresql.conf file to allow TCP/IP access - uncomment and edit the "listen_addresses" option (see docs). On different systems, this file may be located in different places. 4) Restart your system or restart the postgresql server, for example: $ sudo /etc/init.d/postgresql stop $ sudo /etc/init.d/postgresql start 5) Check /etc/passwd file to find out which user account is used for the PostgreSQL admin access on your system (it may be "pgsql", or "postgres", or "postgresql"). Let's assume that this is "postgres" account. 6) Create a database for the TURN purposes, with name, say, "turn": $ createdb -U postgres turn 7) Create a user for the TURN with name, say, "turn": $ psql -U postgres turn turn=# create user turn with password 'turn'; turn=# Ctrl-D 8) Create the TURN users database schema. The database schema for the TURN server is very minimalistic and is located in project's turndb/schema.sql file, or in the system's PREFIX/share/turnserver/schema.sql file after the turnserver installation: $ cat turndb/schema.sql | psql -U turn turn NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "turnusers_lt_pkey" for table "turnusers_lt" CREATE TABLE NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "turnusers_st_pkey" for table "turnusers_st" CREATE TABLE CREATE TABLE $ The schema description: # Table for long-term credentials mechanism authorization: # CREATE TABLE turnusers_lt ( name varchar(512) PRIMARY KEY, hmackey char(128) ); # Table for short-term credentials mechanism authorisation: # CREATE TABLE turnusers_st ( name varchar(512) PRIMARY KEY, password varchar(512) ); # Table holding shared secrets for secret-based authorization # (REST API). It can only be used together with the long-term # mechanism: # CREATE TABLE turn_secret ( value varchar(512) ); # Table holding "white" allowed peer IP ranges. # CREATE TABLE allowed_peer_ip ( ip_range varchar(256) ); # Table holding "black" denied peer IP ranges. # CREATE TABLE denied_peer_ip ( ip_range varchar(256) ); The field hmackey contains HEX string representation of the key. We do not store the user open passwords for long-term credentials, for security reasons. Storing only the HMAC key has its own implications - if you change the realm, you will have to update the HMAC keys of all users, because the realm is used for the HMAC key generation. The key must be 32 characters (HEX representation of 16 bytes) for SHA1, or 64 characters (HEX representation of 32 bytes) for SHA256. You can use turnadmin program to manage the database - you can either use turnadmin to add/modify/delete users, or you can use turnadmin to produce the hmac keys and modify the database with your favorite tools. More examples of database schema creation: psql -h -U -d < turndb/schema.sql (old style for 8.4) psql postgresql://username:password@/databasename < turndb/schema.sql (newer style for 9.x, UNIX domain local sockets) Or: psql postgresql://sql-userusername:password@hostname:port/databasename < turndb/schema.sql (newer style for 9.x, TCP/IP access) Here, the string "postgresql://turn:turn@/turn" is the connection URI. Of course, the administrators can play with the connection string as they want. When starting the turnserver, the psql-userdb parameter will be, for example: turnserver ... --psql-userdb="host=localhost dbname=turn user=turn password=turn connect_timeout=30" Or, for 9.x PostgreSQL versions: turnserver ... --psql-userdb=postgresql://username:password@/databasename ... 9) You are ready to use the TURN database. The database name is "turn", the user name is "turn", the user password is "turn". Of course, you can choose your own names. Now, you will have to use the program turnadmin to fill the database, or you can do that manually with psql. Fill in users, for example: Shared secret for the TURN REST API: $ bin/turnadmin -s logen -e "host=localhost dbname=turn user=turn password=turn" Long-term credentials mechanism: $ bin/turnadmin -a -e "host=localhost dbname=turn user=turn password=turn" -u gorst -r north.gov -p hero $ bin/turnadmin -a -e "host=localhost dbname=turn user=turn password=turn" -u ninefingers -r north.gov -p youhavetoberealistic Long-term credentials mechanism with SHA256 extention: $ bin/turnadmin -a -e "host=localhost dbname=turn user=turn password=turn" -u bethod -r north.gov -p king-of-north --sha256 Short-term credentials mechanism: $ bin/turnadmin -A -e "host=localhost dbname=turn user=turn password=turn" -u gorst -r north.gov -p hero $ bin/turnadmin -A -e "host=localhost dbname=turn user=turn password=turn" -u ninefingers -r north.gov -p youhavetoberealistic XV. MySQL (MariaDB) setup The MySQL setup is similar to PostgreSQL (same idea), and is well documented on their site http://www.mysql.org. The TURN Server database schema is the same as for PostgreSQL and you can find it in turndb/schema.sql file, or in the system's PREFIX/share/turnserver/schema.sql file after the turnserver installation. The general setup idea is the same as for PostgreSQL case: 1) Check that the mysql server access is OK. Immediately after the MySQL server installation, it must be accessible, at the very minimum, at the localhost with the root account. 2) Login into mysql console from root account: $ sudo bash # mysql -p mysql 3) Add 'turn' user with 'turn' password (for example): > create user 'turn'@'localhost' identified by 'turn'; 4) Create database 'turn' (for example) and grant privileges to user 'turn': > create database turn; > grant all on turn.* to 'turn'@'localhost'; > flush privileges; Ctrl-D 5) Create database schema: $ mysql -p -u turn turn < turndb/schema.sql Enter password: turn $ 6) Fill in users, for example: Shared secret for the TURN REST API: $ bin/turnadmin -s logen -M "host=localhost dbname=turn user=turn password=turn" Long-term credentials mechanism: $ bin/turnadmin -a -M "host=localhost dbname=turn user=turn password=turn" -u gorst -r north.gov -p hero $ bin/turnadmin -a -M "host=localhost dbname=turn user=turn password=turn" -u ninefingers -r north.gov -p youhavetoberealistic Long-term credentials mechanism with SHA256 extention: $ bin/turnadmin -a -M "host=localhost dbname=turn user=turn password=turn" -u bethod -r north.gov -p king-of-north --sha256 Short-term credentials mechanism: $ bin/turnadmin -A -M "host=localhost dbname=turn user=turn password=turn" -u gorst -r north.gov -p hero $ bin/turnadmin -A -M "host=localhost dbname=turn user=turn password=turn" -u ninefingers -r north.gov -p youhavetoberealistic 7) Now we can use mysql in the turnserver. If the TURN server was compiled with MySQL support, then we can use the TURN server database parameter --mysql-userdb. The value of this parameter is a connection string for the MySQL database. As "native" MySQL does not have such a feature as "connection string", the TURN server parses the connection string and converts it into MySQL database connection parameter. The format of the MySQL connection string is: "host= dbname= user= password= port= connect_timeout=" (all parameters are optional) So, an example of the MySQL database parameter in the TURN server command line would be: --mysql-userdb="host=localhost dbname=turn user=turn password=turn connect_timeout=30" Or in the turnserver.conf file: mysql-userdb="host=localhost dbname=turn user=turn password=turn connect_timeout=30" XVI. Redis setup The Redis setup is well documented on their site http://redis.io. The TURN Server Redis database schema description can be found in turndb/schema.*.redis files. If the TURN server was compiled with Hiredis support (Hiredis is the C client library for Redis), then we can use the TURN server database parameter --redis-userdb. The value of this parameter is a connection string for the Redis database. As "native" Redis does not have such a feature as "connection string", the TURN server parses the connection string and converts it into Redis database access parameter. The format of the Redis connection string is: "ip= dbname= password= port= connect_timeout=" (all parameters are optional) So, an example of the Redis database parameter in the TURN server command line would be: --redis-userdb="ip=127.0.0.1 dbname=0 password=turn connect_timeout=30" Or in the turnserver.conf file: redis-userdb="ip=127.0.0.1 dbname=0 password=turn connect_timeout=30" Redis can be also used for the TURN allocation status check and for status and traffic notifications. See the explanation in the turndb/schema.stats.redis file, and an example in turndb/testredisdbsetup.sh file. One special thing about TURN Redis security setup is that you can store open passwords for long-term credentials in Redis. You cannot set open passwords for long-term credentials in MySQL and PostgreSQL - with those DBs, you have to use the keys only. With Redis, you have a choice - keys or open passwords. You also have to take care about Redis connection parameters, the timeout and the keepalive. The following settings must be in your Redis config file (/etc/redis.conf or /usr/local/etc/redis.conf): .......... timeout 0 .......... tcp-keepalive 60 .......... Redis TURN admin commands: Shared secret for the TURN REST API: $ bin/turnadmin -s logen -N "host=localhost dbname=0 user=turn password=turn" Long-term credentials mechanism: $ bin/turnadmin -a -N "host=localhost dbname=0 user=turn password=turn" -u gorst -r north.gov -p hero $ bin/turnadmin -a -N "host=localhost dbname=0 user=turn password=turn" -u ninefingers -r north.gov -p youhavetoberealistic Long-term credentials mechanism with SHA256 extention: $ bin/turnadmin -a -N "host=localhost dbname=0 user=turn password=turn" -u bethod -r north.gov -p king-of-north --sha256 Short-term credentials mechanism: $ bin/turnadmin -A -N "host=localhost dbname=0 user=turn password=turn" -u gorst -r north.gov -p hero $ bin/turnadmin -A -N "host=localhost dbname=0 user=turn password=turn" -u ninefingers -r north.gov -p youhavetoberealistic XV. Performance tuning This topic is covered in the wiki page: http://code.google.com/p/rfc5766-turn-server/wiki/turn_performance_and_load_balance XVI. TURN Server setup Read the project wiki pages: http://code.google.com/p/rfc5766-turn-server/w/list Also, check the project from page links to the TURN/WebRTC configuration examples. It may give you an idea how it can be done. XVI. Management interface You have a telnet interface (enabled by default) to access the turnserver process, to view its state, to gather some statistical information, and to make some changes on-the-fly. You can access that CLI interface with telnet or putty program (in telnet mode). The process by default listens to port 5766 on IP address 127.0.0.1 for the telnet connections. WARNING: all telnet communications are going unencrypted over the network. For security reasons, we advise using the loopback IP addresses for CLI (127.0.0.1 or ::1). The CLI may have a password configured, but that password is transferred over the network unencrypted, too. So sticking to the local system CLI access, and accessing the turnserver system terminal with ssh only, would be a wise decision. turnserver-3.2.3.1/STATUS000644 001751 001751 00000005176 12315706777 015013 0ustar00olegoleg000000 000000 Currently implemented functionality: 1) RFC5389 (new STUN protocol) full server and client implementations. We do not maintain strict compatibility with the obsolete RFC 3489 "old STUN" protocol. 2) RFC5766 TURN protocol full server and client implementations. We support file-based long term user credentials, for now. We added experimental DTLS protocol, too. 3) RFC6156 TURN IPv6 extension. 4) We support the following client-to-server network transports for TURN messages: a) UDP b) TCP c) TLS d) DTLS 5) Performance tested. 6) Torture and stability tests. 7) Multiple *NIX platforms tested and supported. 8) TTL field handling implemented for all platforms, preferred behavior in RFC5766. 9) TOS (DiffServ and ECN) field handling (preferred behavior of RFC 5766) implemented, for Linux. Other platforms support the alternative behavior of RFC 5766. 10) DF field alternative behavior of RFC 5766 implemented. 11) Bandwidth limitation per session implemented. 12) RFC 5769 test vectors implemented (where applicable). 13) RFC 5780 STUN extension: NAT behavior discovery. 14) C++ mapping implemented. 15) RFC 6062 TCP relaying implemented. 16) Users can be stored in PostgreSQL database. 17) Users can be stored in MySQL database. 18) TURN Server REST API implemented. 19) Short-term credentials mechanism implemented. 20) Simple load-balancing with ALTERNATE-SERVER implemented. 21) Redis database support added. 22) RFC3489 backward compatibility. 23) Multithreaded TCP relay processing (UDP relay has been multithreaded from the beginning). 24) Networking engine 2.0 implemented, with more scalable approach to the UDP sockets handling. 25) DOS attack prevention logic added to the server; DOS attack client emulation implemented. 26) Linux UDP sockets workaround added to counter RFC 1122 behavior. 27) DTLS sockets re-implemented for better scalability and for Cygwin compatibility. 28) A number of TLS/DTLS improvements added: multiple protocols support, certificate check option. 29) SHA256 support added (experimental). 30) UDP network engine optimized for the new Linux kernels (3.9+). 31) ICE Mobility draft implemented (experimental). 32) CLI implemented. 33) DH and EC TLS ciphers added. 34) HTTP "keep alive" request supported. 35) Optimized (for thousands and more sessions) timers implementation. 36) TCP network engine optimized for the new Linux kernels (3.9+). 37) telnet-based monitor implemented. 38) Package memory copy eliminated in traffic routing. 39) Congestion avoidance implemented, for all protocols. Things to be implemented in future (the development roadmap) are described in the TODO file. turnserver-3.2.3.1/man/000755 001751 001751 00000000000 12315706777 014607 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/examples/000755 001751 001751 00000000000 12315706777 015652 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/rpm/000755 001751 001751 00000000000 12315706777 014632 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/make-man.sh000755 001751 001751 00000001702 12315706777 016061 0ustar00olegoleg000000 000000 #!/bin/sh rm -rf man/man1/* txt2man -s 1 -t TURN -I turnserver -I turnadmin -I turnutils -I turnutils_uclient -I turnutils_stunclient -I turnutils_rfc5769check -I turnutils_peer -B "TURN Server" README.turnserver | sed -e 's/-/\\-/g' > man/man1/turnserver.1 txt2man -s 1 -t TURN -I turnserver -I turnadmin -I turnutils -I turnutils_uclient -I turnutils_stunclient -I turnutils_rfc5769check -I turnutils_peer -B "TURN Server" README.turnadmin | sed -e 's/-/\\-/g'> man/man1/turnadmin.1 txt2man -s 1 -t TURN -I turnserver -I turnadmin -I turnutils -I turnutils_uclient -I turnutils_stunclient -I turnutils_rfc5769check -I turnutils_peer -B "TURN Server" README.turnutils | sed -e 's/-/\\-/g' > man/man1/turnutils.1 cd man/man1; ln -s turnutils.1 turnutils_uclient.1;cd ../.. cd man/man1; ln -s turnutils.1 turnutils_peer.1;cd ../.. cd man/man1; ln -s turnutils.1 turnutils_stunclient.1;cd ../.. cd man/man1; ln -s turnserver.1 rfc5766-turn-server.1;cd ../.. turnserver-3.2.3.1/NOTE000644 001751 001751 00000000126 12315706777 014523 0ustar00olegoleg000000 000000 This project is active in Google code: http://code.google.com/p/rfc5766-turn-server/ turnserver-3.2.3.1/AUTHORS000644 001751 001751 00000002050 12315706777 015101 0ustar00olegoleg000000 000000 Oleg Moskalenko : General design and implementation (2011-2013); Gabor Kovesdan, http://kovesdan.org : FreeBSD packaging (since v1.5.2.6); Daniel Pocock, http://danielpocock.com : Debian packaging (since v1.8.3.6); John Selbie (jselbie@gmail.com) : Stuntman interoperability, RFC5780 fixes MS Windows port work (since v1.8.3.6); Lee Sylvester : Status and statistics - ideas and pilot implementation (since v1.8.4.0); Erik Johnston : Access Control Lists, 2013 (since v1.8.5.0); Roman Lisagor : Testing, code optimization (since v1.8.6.0); Vladimir Tsanev : configure script and Makefile fixes, Arch Linux port (since v1.8.6.1); Po-sheng Lin : Libevent dependencies cleaning (since v2.0.1.1); Peter Dunkley : CentOS/Fedora port (since v2.6.6.1) Mutsutoshi Yoshimoto TCP routing: testing and bug fixes (since v3.2.2.7) turnserver-3.2.3.1/rpm/Fedora.pre.build.sh000755 001751 001751 00000000464 12315706777 020260 0ustar00olegoleg000000 000000 #!/bin/bash CPWD=`pwd` # Fedora preparation script. . ./common.pre.build.sh PACKS="libevent-devel mariadb-devel" sudo yum -y install ${PACKS} ER=$? if ! [ ${ER} -eq 0 ] ; then echo "Cannot install package(s) ${PACKS}" cd ${CPWD} exit -1 fi echo "Fedora20" > ${BUILDDIR}/platform cd ${CPWD} turnserver-3.2.3.1/rpm/common.pre.build.sh000755 001751 001751 00000000661 12315706777 020347 0ustar00olegoleg000000 000000 #!/bin/bash # Common preparation script. . ./build.settings.sh # DIRS rm -rf ${BUILDDIR} mkdir -p ${BUILDDIR} mkdir -p ${BUILDDIR}/SOURCES mkdir -p ${BUILDDIR}/SPECS mkdir -p ${BUILDDIR}/RPMS mkdir -p ${BUILDDIR}/tmp # Common packs PACKS="make gcc redhat-rpm-config rpm-build doxygen openssl-devel svn" sudo yum -y install ${PACKS} ER=$? if ! [ ${ER} -eq 0 ] ; then echo "Cannot install packages ${PACKS}" exit -1 fi turnserver-3.2.3.1/rpm/epel.install.sh000755 001751 001751 00000001174 12315706777 017566 0ustar00olegoleg000000 000000 #!/bin/bash CPWD=`pwd` # Epel installation script EPEL=epel-release-6-8.noarch EPELRPM=${EPEL}.rpm BUILDDIR=~/rpmbuild WGETOPTIONS="--no-check-certificate" RPMOPTIONS="-ivh --force" mkdir -p ${BUILDDIR} mkdir -p ${BUILDDIR}/RPMS sudo yum -y install wget cd ${BUILDDIR}/RPMS if ! [ -f ${EPELRPM} ] ; then wget ${WGETOPTIONS} http://download.fedoraproject.org/pub/epel/6/i386/${EPELRPM} ER=$? if ! [ ${ER} -eq 0 ] ; then cd ${CPWD} exit -1 fi fi PACK=${EPELRPM} sudo rpm ${RPMOPTIONS} ${PACK} ER=$? if ! [ ${ER} -eq 0 ] ; then echo "Cannot install package ${PACK}" cd ${CPWD} exit -1 fi cd ${CPWD} turnserver-3.2.3.1/rpm/build.settings.sh000755 001751 001751 00000000410 12315706777 020122 0ustar00olegoleg000000 000000 #!/bin/bash # Common settings script. TURNVERSION=3.2.3.1 BUILDDIR=~/rpmbuild ARCH=`uname -p` TURNSERVER_SVN_URL=http://rfc5766-turn-server.googlecode.com/svn TURNSERVER_SVN_URL_VER=branches/v3.2 WGETOPTIONS="--no-check-certificate" RPMOPTIONS="-ivh --force" turnserver-3.2.3.1/rpm/turnserver.init.el000644 001751 001751 00000002554 12315706777 020343 0ustar00olegoleg000000 000000 #!/bin/bash # # Startup script for TURN Server # # chkconfig: 345 85 15 # description: RFC 5766 TURN Server # # processname: turnserver # pidfile: /var/run/turnserver.pid # config: /etc/turnserver/turnserver.conf # ### BEGIN INIT INFO # Provides: turnserver # Required-Start: $local_fs $network # Short-Description: RFC 5766 TURN Server # Description: RFC 5766 TURN Server ### END INIT INFO # Source function library. . /etc/rc.d/init.d/functions TURN=/usr/bin/turnserver PROG=turnserver TURNCFG=/etc/turnserver/$PROG.conf PID_FILE=/var/run/$PROG.pid LOCK_FILE=/var/lock/subsys/$PROG DEFAULTS=/etc/sysconfig/$PROG RETVAL=0 USER=turnserver start() { echo -n $"Starting $PROG: " daemon --user=$USER $TURN $OPTIONS RETVAL=$? if [ $RETVAL = 0 ]; then pidofproc $TURN > $PID_FILE RETVAL=$? [ $RETVAL = 0 ] && touch $LOCK_FILE && success fi echo return $RETVAL } stop() { echo -n $"Stopping $PROG: " killproc $TURN RETVAL=$? echo [ $RETVAL = 0 ] && rm -f $LOCK_FILE $PID_FILE } [ -f $DEFAULTS ] && . $DEFAULTS OPTIONS="-o -c $TURNCFG $EXTRA_OPTIONS" # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status $TURN RETVAL=$? ;; restart) stop start ;; condrestart) if [ -f $PID_FILE ] ; then stop start fi ;; *) echo $"Usage: $PROG {start|stop|restart|condrestart|status|help}" exit 1 esac exit $RETVAL turnserver-3.2.3.1/rpm/build.sh000755 001751 001751 00000003614 12315706777 016274 0ustar00olegoleg000000 000000 #!/bin/bash CPWD=`pwd` . ./build.settings.sh # Required packages PACKS="postgresql-devel hiredis-devel" sudo yum -y install ${PACKS} ER=$? if ! [ ${ER} -eq 0 ] ; then echo "Cannot install packages ${PACKS}" cd ${CPWD} exit -1 fi # TURN cd ${BUILDDIR}/tmp rm -rf turnserver-${TURNVERSION} svn export ${TURNSERVER_SVN_URL}/${TURNSERVER_SVN_URL_VER}/ turnserver-${TURNVERSION} ER=$? if ! [ ${ER} -eq 0 ] ; then cd ${CPWD} exit -1 fi tar zcf ${BUILDDIR}/SOURCES/turnserver-${TURNVERSION}.tar.gz turnserver-${TURNVERSION} ER=$? if ! [ ${ER} -eq 0 ] ; then cd ${CPWD} exit -1 fi rpmbuild -ta ${BUILDDIR}/SOURCES/turnserver-${TURNVERSION}.tar.gz ER=$? if ! [ ${ER} -eq 0 ] ; then cd ${CPWD} exit -1 fi # Make binary tarball cd ${BUILDDIR}/RPMS/${ARCH} mkdir -p di mv *debuginfo* di mv *devel* di rm -rf turnserver-${TURNVERSION} mkdir turnserver-${TURNVERSION} mv *.rpm turnserver-${TURNVERSION}/ rm -rf turnserver-${TURNVERSION}/install.sh if [ -f ${BUILDDIR}/install.sh ] ; then cat ${BUILDDIR}/install.sh > turnserver-${TURNVERSION}/install.sh else echo "#!/bin/sh" > turnserver-${TURNVERSION}/install.sh fi cat <>turnserver-${TURNVERSION}/install.sh sudo yum -y install openssl sudo yum -y install telnet for i in *.rpm ; do sudo yum -y install \${i} ER=\$? if ! [ \${ER} -eq 0 ] ; then sudo rpm -Uvh \${i} ER=\$? if ! [ \${ER} -eq 0 ] ; then sudo rpm -ivh --force \${i} ER=\$? if ! [ \${ER} -eq 0 ] ; then echo "ERROR: cannot install package \${i}" exit -1 fi fi fi done echo SUCCESS ! EOF chmod a+x turnserver-${TURNVERSION}/install.sh cp ${CPWD}/uninstall.turnserver.sh turnserver-${TURNVERSION}/ chmod a+x turnserver-${TURNVERSION}/uninstall.turnserver.sh PLATFORM=`cat ${BUILDDIR}/platform` tar cvfz turnserver-${TURNVERSION}-${PLATFORM}-${ARCH}.tar.gz turnserver-${TURNVERSION} cd ${CPWD} turnserver-3.2.3.1/rpm/turnserver.sysconfig000644 001751 001751 00000000064 12315706777 020777 0ustar00olegoleg000000 000000 # # TURN Server startup options # EXTRA_OPTIONS="" turnserver-3.2.3.1/rpm/turnserver.service.fc000644 001751 001751 00000000666 12315706777 021032 0ustar00olegoleg000000 000000 [Unit] Description=rfc5766-turn-server Documentation=man:rfc5766-turn-server(1) man:turnadmin(1) man:turnserver(1) After=syslog.target network.target [Service] Type=forking EnvironmentFile=/etc/sysconfig/turnserver PIDFile=/var/run/turnserver.pid ExecStart=/usr/bin/turnserver -o -c /etc/turnserver/turnserver.conf $EXTRA_OPTIONS ExecStopPost=/usr/bin/rm -f /var/run/turnserver.pid Restart=on-abort [Install] WantedBy=multi-user.target turnserver-3.2.3.1/rpm/uninstall.turnserver.sh000755 001751 001751 00000000405 12315706777 021417 0ustar00olegoleg000000 000000 #!/bin/sh for i in `rpm -q -a | grep turnserver-utils-3` do echo $i sudo rpm -e $i done for i in `rpm -q -a | grep turnserver-client-libs-3` do echo $i sudo rpm -e $i done for i in `rpm -q -a | grep turnserver.*-3` do echo $i sudo rpm -e $i done turnserver-3.2.3.1/rpm/CentOS6.pre.build.sh000755 001751 001751 00000003707 12315706777 020304 0ustar00olegoleg000000 000000 #!/bin/bash # CentOS6 preparation script. CPWD=`pwd` . ./common.pre.build.sh cd ${CPWD} EPELRPM=epel-release-6-8.noarch.rpm LIBEVENT_MAJOR_VERSION=2 LIBEVENT_VERSION=${LIBEVENT_MAJOR_VERSION}.0.21 LIBEVENT_DISTRO=libevent-${LIBEVENT_VERSION}-stable.tar.gz LIBEVENT_SPEC_DIR=libevent.rpm LIBEVENTSPEC_SVN_URL=${TURNSERVER_SVN_URL}/${LIBEVENT_SPEC_DIR} LIBEVENT_SPEC_FILE=libevent.spec # Common packs PACKS="mysql-devel" sudo yum -y install ${PACKS} ER=$? if ! [ ${ER} -eq 0 ] ; then echo "Cannot install package(s) ${PACKS}" cd ${CPWD} exit -1 fi # Libevent2: cd ${BUILDDIR}/SOURCES if ! [ -f ${LIBEVENT_DISTRO} ] ; then wget ${WGETOPTIONS} https://github.com/downloads/libevent/libevent/${LIBEVENT_DISTRO} ER=$? if ! [ ${ER} -eq 0 ] ; then cd ${CPWD} exit -1 fi fi if ! [ -f ${BUILDDIR}/SPECS/${LIBEVENT_SPEC_FILE} ] ; then cd ${BUILDDIR}/tmp rm -rf ${LIBEVENT_SPEC_DIR} svn export ${LIBEVENTSPEC_SVN_URL} ${LIBEVENT_SPEC_DIR} ER=$? if ! [ ${ER} -eq 0 ] ; then cd ${CPWD} exit -1 fi if ! [ -f ${LIBEVENT_SPEC_DIR}/${LIBEVENT_SPEC_FILE} ] ; then echo "ERROR: cannot download ${LIBEVENT_SPEC_FILE} file" cd ${CPWD} exit -1 fi cp ${LIBEVENT_SPEC_DIR}/${LIBEVENT_SPEC_FILE} ${BUILDDIR}/SPECS fi cd ${BUILDDIR}/SPECS rpmbuild -ba ${BUILDDIR}/SPECS/${LIBEVENT_SPEC_FILE} ER=$? if ! [ ${ER} -eq 0 ] ; then cd ${CPWD} exit -1 fi PACK=${BUILDDIR}/RPMS/${ARCH}/libevent-${LIBEVENT_MAJOR_VERSION}*.rpm sudo rpm ${RPMOPTIONS} ${PACK} ER=$? if ! [ ${ER} -eq 0 ] ; then echo "Cannot install packages ${PACK}" cd ${CPWD} exit -1 fi PACK=${BUILDDIR}/RPMS/${ARCH}/libevent-devel*.rpm sudo rpm ${RPMOPTIONS} ${PACK} ER=$? if ! [ ${ER} -eq 0 ] ; then echo "Cannot install packages ${PACK}" cd ${CPWD} exit -1 fi # EPEL (for hiredis) cd ${CPWD} ./epel.install.sh # Platform file echo "CentOS6.5" > ${BUILDDIR}/platform cp ${CPWD}/epel.install.sh ${BUILDDIR}/install.sh cd ${CPWD} turnserver-3.2.3.1/rpm/build.instructions.txt000644 001751 001751 00000003513 12315706777 021237 0ustar00olegoleg000000 000000 MANUAL PROCESS FOR CENTOS 6: The first thing you need to build/use the TURN server with CentOS is to build and install libevent 2.x.x. CentOS 6 ships with libevent 1.x.x. You can find a .spec file to build libevent 2.x.x here: https://github.com/crocodilertc/libevent To build libevent: 1) Install the dependencies for building libevent: gcc, make, redhat-rpm-config, doxygen, openssl-devel, rpm-build 2) $ mkdir ~/rpmbuild 3) $ mkdir ~/rpmbuild/SOURCES 4) $ mkdir ~/rpmbuild/SPECS 5) Put the tarball for libevent (https://github.com/downloads/libevent/libevent/libevent-2.0.21-stable.tar.gz) and put it into ~/rpmbuild/SOURCES 6) Put the .spec for libevent (https://raw.github.com/crocodilertc/libevent/master/libevent.spec) into ~/rpmbuild/SPECS 7) Build the RPMs, "rpmbuild -ba ~/rpmbuild/SPECS/libevent.spec" To build the TURN server: 1) Install libevent and libevent-devel rpms 2) Install EPEL (http://fedoraproject.org/wiki/EPEL) - needed for hiredis 3) Install the dependencies for building the TURN server: gcc, make, redhat-rpm-config, openssl-devel, libevent-devel >= 2.0.0, mysql-devel, postgresql-devel, hiredis-devel 4) $ mkdir ~/rpmbuild 5) $ mkdir ~/rpmbuild/SOURCES 6) Export the TURN server from SVN, "svn export http://rfc5766-turn-server.googlecode.com/svn/trunk/ turnserver-2.6.7.0" 7) Create a tarball, "tar zcf ~/rpmbuild/SOURCES/turnserver-2.6.7.0.tar.gz turnserver-2.6.7.0" 8) Build the RPMs, "rpmbuild -ta ~/rpmbuild/SOURCES/turnserver-2.6.7.0.tar.gz" AUTOMATED PROCESS FOR CENTOS 6: $ cd <...>/rfc57666-turn-server/rpm $ ./CentOS6.pre.build.sh $ ./build.sh (then see the tarball in ~/rpmbuild/RPMS/) AUTOMATED PROCESS FOR Fedora: $ cd <...>/rfc57666-turn-server/rpm $ ./Fedora.pre.build.sh $ ./build.sh (then see the tarball in ~/rpmbuild/RPMS/) turnserver-3.2.3.1/rpm/turnserver.spec000644 001751 001751 00000032422 12315706777 017730 0ustar00olegoleg000000 000000 Name: turnserver Version: 3.2.3.1 Release: 0%{dist} Summary: RFC5766 TURN Server Group: System Environment/Libraries License: BSD URL: https://code.google.com/p/rfc5766-turn-server/ Source0: http://turnserver.open-sys.org/downloads/v%{version}/%{name}-%{version}.tar.gz BuildRequires: gcc, make, redhat-rpm-config BuildRequires: openssl-devel, libevent-devel >= 2.0.0, postgresql-devel BuildRequires: hiredis-devel Requires: openssl, libevent >= 2.0.0, mysql-libs, postgresql-libs Requires: hiredis, perl-DBI, perl-libwww-perl Requires: telnet %if 0%{?el6} BuildRequires: epel-release, mysql-devel Requires: epel-release, mysql-libs %else BuildRequires: mariadb-devel Requires: mariadb-libs %endif %description The TURN Server is a VoIP media traffic NAT traversal server and gateway. It can be used as a general-purpose network traffic TURN server/gateway, too. This implementation also includes some extra features. Supported RFCs: TURN specs: - RFC 5766 - base TURN specs - RFC 6062 - TCP relaying TURN extension - RFC 6156 - IPv6 extension for TURN - Experimental DTLS support as client protocol. STUN specs: - RFC 3489 - "classic" STUN - RFC 5389 - base "new" STUN specs - RFC 5769 - test vectors for STUN protocol testing - RFC 5780 - NAT behavior discovery support The implementation fully supports the following client-to-TURN-server protocols: - UDP (per RFC 5766) - TCP (per RFC 5766 and RFC 6062) - TLS (per RFC 5766 and RFC 6062); SSL3/TLS1.0/TLS1.1/TLS1.2; SSL2 wrapping supported - DTLS (experimental non-standard feature) Supported relay protocols: - UDP (per RFC 5766) - TCP (per RFC 6062) Supported user databases (for user repository, with passwords or keys, if authentication is required): - Flat files - MySQL - PostgreSQL - Redis Redis can also be used for status and statistics storage and notification. Supported TURN authentication mechanisms: - short-term - long-term - TURN REST API (a modification of the long-term mechanism, for time-limited secret-based authentication, for WebRTC applications) The load balancing can be implemented with the following tools (either one or a combination of them): - network load-balancer server - DNS-based load balancing - built-in ALTERNATE-SERVER mechanism. %package utils Summary: TURN client utils Group: System Environment/Libraries Requires: turnserver-client-libs = %{version}-%{release} %description utils This package contains the TURN client utils. %package client-libs Summary: TURN client library Group: System Environment/Libraries Requires: openssl, libevent >= 2.0.0 %description client-libs This package contains the TURN client library. %package client-devel Summary: TURN client development headers. Group: Development/Libraries Requires: turnserver-client-libs = %{version}-%{release} %description client-devel This package contains the TURN client development headers. %prep %setup -q -n %{name}-%{version} %build PREFIX=%{_prefix} CONFDIR=%{_sysconfdir}/%{name} EXAMPLESDIR=%{_datadir}/%{name} \ MANPREFIX=%{_datadir} LIBDIR=%{_libdir} MORECMD=cat ./configure make %install rm -rf $RPM_BUILD_ROOT DESTDIR=$RPM_BUILD_ROOT make install mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig install -m644 rpm/turnserver.sysconfig \ $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/turnserver mv $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/turnuserdb.conf.default \ $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/turnuserdb.conf %if 0%{?el6} cat $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/turnserver.conf.default | \ sed s/#syslog/syslog/g | \ sed s/#no-stdout-log/no-stdout-log/g > \ $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/turnserver.conf mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d install -m755 rpm/turnserver.init.el \ $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d/turnserver %else cat $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/turnserver.conf.default | \ sed s/#syslog/syslog/g | \ sed s/#no-stdout-log/no-stdout-log/g | \ sed s/#pidfile/pidfile/g > \ $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/turnserver.conf mkdir -p $RPM_BUILD_ROOT/%{_unitdir} install -m755 rpm/turnserver.service.fc \ $RPM_BUILD_ROOT/%{_unitdir}/turnserver.service %endif rm -rf $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/turnserver.conf.default %clean rm -rf "$RPM_BUILD_ROOT" %pre %{_sbindir}/groupadd -r turnserver 2> /dev/null || : %{_sbindir}/useradd -r -g turnserver -s /bin/false -c "TURN Server daemon" -d \ %{_datadir}/%{name} turnserver 2> /dev/null || : %post %if 0%{?el6} /sbin/chkconfig --add turnserver %else /bin/systemctl --system daemon-reload %endif %preun if [ $1 = 0 ]; then %if 0%{?el6} /sbin/service turnserver stop > /dev/null 2>&1 /sbin/chkconfig --del turnserver %else /bin/systemctl stop turnserver.service /bin/systemctl disable turnserver.service 2> /dev/null %endif fi %postun %if 0%{?fedora} /bin/systemctl --system daemon-reload %endif %files %defattr(-,root,root) %{_bindir}/turnserver %{_bindir}/turnadmin %{_mandir}/man1/rfc5766-turn-server.1.gz %{_mandir}/man1/turnserver.1.gz %{_mandir}/man1/turnadmin.1.gz %dir %attr(-,turnserver,turnserver) %{_sysconfdir}/%{name} %config(noreplace) %attr(0644,turnserver,turnserver) %{_sysconfdir}/%{name}/turnserver.conf %config(noreplace) %attr(0644,turnserver,turnserver) %{_sysconfdir}/%{name}/turnuserdb.conf %config(noreplace) %{_sysconfdir}/sysconfig/turnserver %if 0%{?el6} %config %{_sysconfdir}/rc.d/init.d/turnserver %else %config %{_unitdir}/turnserver.service %endif %dir %{_docdir}/%{name} %{_docdir}/%{name}/LICENSE %{_docdir}/%{name}/INSTALL %{_docdir}/%{name}/postinstall.txt %{_docdir}/%{name}/README.turnadmin %{_docdir}/%{name}/README.turnserver %{_docdir}/%{name}/schema.sql %{_docdir}/%{name}/schema.stats.redis %{_docdir}/%{name}/schema.userdb.redis %dir %{_datadir}/%{name} %{_datadir}/%{name}/schema.sql %{_datadir}/%{name}/schema.stats.redis %{_datadir}/%{name}/schema.userdb.redis %{_datadir}/%{name}/testredisdbsetup.sh %dir %{_datadir}/%{name}/etc %{_datadir}/%{name}/etc/turn_server_cert.pem %{_datadir}/%{name}/etc/turn_server_pkey.pem %{_datadir}/%{name}/etc/turnserver.conf %{_datadir}/%{name}/etc/turnuserdb.conf %dir %{_datadir}/%{name}/scripts %{_datadir}/%{name}/scripts/peer.sh %{_datadir}/%{name}/scripts/readme.txt %dir %{_datadir}/%{name}/scripts/basic %{_datadir}/%{name}/scripts/basic/dos_attack.sh %{_datadir}/%{name}/scripts/basic/relay.sh %{_datadir}/%{name}/scripts/basic/tcp_client.sh %{_datadir}/%{name}/scripts/basic/tcp_client_c2c_tcp_relay.sh %{_datadir}/%{name}/scripts/basic/udp_c2c_client.sh %{_datadir}/%{name}/scripts/basic/udp_client.sh %dir %{_datadir}/%{name}/scripts/loadbalance %{_datadir}/%{name}/scripts/loadbalance/master_relay.sh %{_datadir}/%{name}/scripts/loadbalance/slave_relay_1.sh %{_datadir}/%{name}/scripts/loadbalance/slave_relay_2.sh %{_datadir}/%{name}/scripts/loadbalance/tcp_c2c_tcp_relay.sh %{_datadir}/%{name}/scripts/loadbalance/udp_c2c.sh %dir %{_datadir}/%{name}/scripts/longtermsecure %{_datadir}/%{name}/scripts/longtermsecure/secure_dos_attack.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_dtls_client.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_dtls_client_cert.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_tls_client_cert.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_relay.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_relay_cert.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_tcp_client.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_tcp_client_c2c_tcp_relay.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_tls_client.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_tls_client_c2c_tcp_relay.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_udp_c2c.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_udp_client.sh %dir %{_datadir}/%{name}/scripts/longtermsecuredb %{_datadir}/%{name}/scripts/longtermsecuredb/secure_relay_with_db_mysql.sh %{_datadir}/%{name}/scripts/longtermsecuredb/secure_relay_with_db_psql.sh %{_datadir}/%{name}/scripts/longtermsecuredb/secure_relay_with_db_redis.sh %dir %{_datadir}/%{name}/scripts/restapi %{_datadir}/%{name}/scripts/restapi/secure_relay_secret.sh %{_datadir}/%{name}/scripts/restapi/secure_relay_secret_with_db_mysql.sh %{_datadir}/%{name}/scripts/restapi/secure_relay_secret_with_db_psql.sh %{_datadir}/%{name}/scripts/restapi/secure_relay_secret_with_db_redis.sh %{_datadir}/%{name}/scripts/restapi/secure_udp_client_with_secret.sh %{_datadir}/%{name}/scripts/restapi/shared_secret_maintainer.pl %dir %{_datadir}/%{name}/scripts/selfloadbalance %{_datadir}/%{name}/scripts/selfloadbalance/secure_dos_attack.sh %{_datadir}/%{name}/scripts/selfloadbalance/secure_relay.sh %dir %{_datadir}/%{name}/scripts/shorttermsecure %{_datadir}/%{name}/scripts/shorttermsecure/secure_relay_short_term_mech.sh %{_datadir}/%{name}/scripts/shorttermsecure/secure_tcp_client_c2c_tcp_relay_short_term.sh %{_datadir}/%{name}/scripts/shorttermsecure/secure_udp_client_short_term.sh %dir %{_datadir}/%{name}/scripts/mobile %{_datadir}/%{name}/scripts/mobile/mobile_relay.sh %{_datadir}/%{name}/scripts/mobile/mobile_dtls_client.sh %{_datadir}/%{name}/scripts/mobile/mobile_tcp_client.sh %{_datadir}/%{name}/scripts/mobile/mobile_tls_client_c2c_tcp_relay.sh %{_datadir}/%{name}/scripts/mobile/mobile_udp_client.sh %files utils %defattr(-,root,root) %{_bindir}/turnutils_peer %{_bindir}/turnutils_stunclient %{_bindir}/turnutils_uclient %{_mandir}/man1/turnutils.1.gz %{_mandir}/man1/turnutils_peer.1.gz %{_mandir}/man1/turnutils_stunclient.1.gz %{_mandir}/man1/turnutils_uclient.1.gz %dir %{_docdir}/%{name} %{_docdir}/%{name}/LICENSE %{_docdir}/%{name}/README.turnutils %dir %{_datadir}/%{name} %dir %{_datadir}/%{name}/etc %{_datadir}/%{name}/etc/turn_client_cert.pem %{_datadir}/%{name}/etc/turn_client_pkey.pem %files client-libs %{_docdir}/%{name}/LICENSE %{_libdir}/libturnclient.a %files client-devel %{_docdir}/%{name}/LICENSE %dir %{_includedir}/turn %{_includedir}/turn/ns_turn_defs.h %dir %{_includedir}/turn/client %{_includedir}/turn/client/ns_turn_ioaddr.h %{_includedir}/turn/client/ns_turn_msg_addr.h %{_includedir}/turn/client/ns_turn_msg_defs.h %{_includedir}/turn/client/ns_turn_msg.h %{_includedir}/turn/client/TurnMsgLib.h %changelog * Sat Mar 29 2014 Oleg Moskalenko - Sync to 3.2.3.1 * Mon Mar 17 2014 Oleg Moskalenko - Sync to 3.2.2.912 * Mon Mar 10 2014 Oleg Moskalenko - Sync to 3.2.2.911 * Sun Mar 09 2014 Oleg Moskalenko - Sync to 3.2.2.910 * Sun Mar 02 2014 Oleg Moskalenko - Sync to 3.2.2.9 * Tue Feb 18 2014 Oleg Moskalenko - Sync to 3.2.2.8 * Wed Feb 12 2014 Oleg Moskalenko - Sync to 3.2.2.7 * Tue Feb 04 2014 Oleg Moskalenko - Sync to 3.2.2.6 * Sat Jan 25 2014 Oleg Moskalenko - Sync to 3.2.2.5 * Fri Jan 24 2014 Oleg Moskalenko - Sync to 3.2.2.4 * Thu Jan 23 2014 Oleg Moskalenko - Sync to 3.2.2.3 * Tue Jan 21 2014 Oleg Moskalenko - Sync to 3.2.2.2 * Sat Jan 11 2014 Oleg Moskalenko - CPU optimization, added to 3.2.2.1 * Mon Jan 06 2014 Oleg Moskalenko - Linux epoll performance improvements, added to 3.2.1.4 * Mon Jan 06 2014 Oleg Moskalenko - Telnet client installation added to 3.2.1.3 * Sun Jan 05 2014 Oleg Moskalenko - Sync to 3.2.1.2 * Fri Jan 03 2014 Oleg Moskalenko - Sync to 3.2.1.1 * Thu Dec 26 2013 Oleg Moskalenko - Sync to 3.2.1.0 * Wed Dec 25 2013 Oleg Moskalenko - Sync to 3.1.6.0 * Mon Dec 23 2013 Oleg Moskalenko - Sync to 3.1.5.3 * Fri Dec 20 2013 Oleg Moskalenko - Sync to 3.1.5.1 * Thu Dec 19 2013 Oleg Moskalenko - Sync to 3.1.4.2 * Sat Dec 14 2013 Oleg Moskalenko - Sync to 3.1.3.1 * Wed Dec 11 2013 Oleg Moskalenko - OpenSSL installation fixed 3.1.2.3 * Tue Dec 10 2013 Oleg Moskalenko - Updated to version 3.1.2.2 * Mon Dec 09 2013 Oleg Moskalenko - Updated to version 3.1.2.1 * Sun Dec 01 2013 Oleg Moskalenko - Updated to version 3.1.1.0 * Sat Nov 30 2013 Oleg Moskalenko - Updated to version 3.0.2.1. * Thu Nov 28 2013 Oleg Moskalenko - Config file setting fixed: version 3.0.1.4. * Wed Nov 27 2013 Oleg Moskalenko - Config file setting fixed: version 3.0.1.3. * Mon Nov 25 2013 Oleg Moskalenko - Updated to version 3.0.1.2 * Sun Nov 10 2013 Oleg Moskalenko - Updated to version 3.0.0.0 * Fri Nov 8 2013 Oleg Moskalenko - Updated to version 2.6.7.2 * Thu Nov 7 2013 Oleg Moskalenko - Updated to version 2.6.7.1 * Sun Nov 3 2013 Oleg Moskalenko - Updated to version 2.6.7.0 * Sat Nov 2 2013 Peter Dunkley - Added Fedora support * Thu Oct 31 2013 Oleg Moskalenko - Updated to version 2.6.6.2 * Sun Oct 27 2013 Oleg Moskalenko - Updated to version 2.6.6.1 * Sun Oct 27 2013 Peter Dunkley - Updated to version 2.6.6.0 * Fri May 3 2013 Peter Dunkley - First version turnserver-3.2.3.1/examples/etc/000755 001751 001751 00000000000 12315706777 016425 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/examples/scripts/000755 001751 001751 00000000000 12315706777 017341 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/examples/scripts/longtermsecure/000755 001751 001751 00000000000 12315706777 022377 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/examples/scripts/loadbalance/000755 001751 001751 00000000000 12315706777 021566 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/examples/scripts/selfloadbalance/000755 001751 001751 00000000000 12315706777 022440 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/examples/scripts/shorttermsecure/000755 001751 001751 00000000000 12315706777 022577 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/examples/scripts/mobile/000755 001751 001751 00000000000 12315706777 020610 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/examples/scripts/peer.sh000755 001751 001751 00000000715 12315706777 020636 0ustar00olegoleg000000 000000 #!/bin/sh # # This is a script for the peer application, # for testing only purposes. It opens UDP echo-like sockets # on IPv4 address 127.0.0.1 and IPv6 address ::1. # The default port 3480 is used. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:bin/:../bin:${PATH} turnutils_peer -L 127.0.0.1 -L ::1 -L 0.0.0.0 $@ turnserver-3.2.3.1/examples/scripts/restapi/000755 001751 001751 00000000000 12315706777 021010 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/examples/scripts/basic/000755 001751 001751 00000000000 12315706777 020422 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/examples/scripts/rfc5769.sh000755 001751 001751 00000000551 12315706777 021006 0ustar00olegoleg000000 000000 #!/bin/sh # # This is a script for RFC 5769 STUN protocol check. # It checks whether the main code was compiled correctly. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:bin/:../bin:${PATH} turnutils_rfc5769check $@ turnserver-3.2.3.1/examples/scripts/readme.txt000644 001751 001751 00000002065 12315706777 021342 0ustar00olegoleg000000 000000 This directory contains various example scripts for the TURN server functionality illustration. 1) peer.sh starts the "peer" application that serves as a peer for all examples. 2) "basic" directory contains set of scripts which works together to demonstrate very basic anynymous functionality of the TURN server. The "peer.sh" must be used, too. 3) "longtermsecure" directory contains set of scripts demonstrating the long-term authentication mechanism (peer.sh to be used, too). 4) "longtermsecuredb" shows how to start TURN server with database. The clients from the directory "longtermsecure" can be used with the relay scripts in the "longtermsecuredb" directory. Of course, the database (PostgreSQL or MySQL) must be set for these scripts to work correctly. 5) "restapi" shows how to use TURN REST API. 6) "shorttermsecure" shows how to use the short-term authentication mechanism. The short term mechanism is always used with the database. 7) "loadbalance" shows how to use the simple load-balancing mechanism based upon the ALTERNATE-SERVER functionality. turnserver-3.2.3.1/examples/scripts/longtermsecuredb/000755 001751 001751 00000000000 12315706777 022705 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/examples/scripts/longtermsecuredb/secure_relay_with_db_redis.sh000755 001751 001751 00000004007 12315706777 030615 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example how to start a TURN Server in # secure mode with Redis database for users # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) --redis-userdb="ip=127.0.0.1 dbname=0 password=turn connect_timeout=30" # means that local Redis database 0 will be used, # database password is "turn", and connection timeout 30 seconds. # 7) --redis-statsdb="ip=127.0.0.1 dbname=0 password=turn connect_timeout=30" # means that we want to use Redis for status and statistics information, # and this will be the same database as the one used as user database. # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2 # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 -r north.gov --redis-userdb="ip=127.0.0.1 dbname=0 password=turn connect_timeout=30" --redis-statsdb="ip=127.0.0.1 dbname=0 password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL:SSLv2 $@ turnserver-3.2.3.1/examples/scripts/longtermsecuredb/secure_relay_with_db_psql.sh000755 001751 001751 00000004164 12315706777 030472 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example how to start a TURN Server in # secure mode with Postgres database for users # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) --psql-userdb="host=localhost dbname=turn user=turn password=turn connect_timeout=30" # means that local database "turn" will be used, with database user "turn" and database user # password "turn". # 7) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 8) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 9) "--log-file=stdout" means that all log output will go to the stdout. # 10) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2 # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 -r north.gov --psql-userdb="host=localhost dbname=turn user=turn password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL:SSLv2 $@ # Newer PostgreSQL style connection string example: # PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 -r north.gov --psql-userdb=postgresql://turn:turn@/turn --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL:SSLv2 $@ turnserver-3.2.3.1/examples/scripts/longtermsecuredb/secure_relay_with_db_mysql.sh000755 001751 001751 00000003441 12315706777 030655 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example how to start a TURN Server in # secure mode with MySQL database for users # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) --mysql-userdb="host=localhost dbname=turn user=turn password=turn connect_timeout=30" # means that local MySQL database "turn" will be used, with database user "turn" and # database user password "turn", and connection timeout 30 seconds. # 7) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 8) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 9) "--log-file=stdout" means that all log output will go to the stdout. # 10) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2 # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 -r north.gov --mysql-userdb="host=localhost dbname=turn user=turn password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL:SSLv2 $@ turnserver-3.2.3.1/examples/scripts/basic/tcp_client.sh000755 001751 001751 00000002137 12315706777 023110 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "unsecure" TURN TCP client. # Options: # 1) -t is present, it means that TCP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 11) -X means that IPv4 relay address is requested. # 12) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin:bin/:${PATH} turnutils_uclient -t -n 1000 -m 10 -l 3037 -e 127.0.0.1 -g -X $@ ::1 turnserver-3.2.3.1/examples/scripts/basic/relay.sh000755 001751 001751 00000002153 12315706777 022076 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example how to start a TURN Server in # non-secure mode (when authentication is not used). # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # Other options: # set bandwidth limit on client session 3000000 bytes per second (--max-bps) # use fingerprints (-f) # use 3 relay threads (-m 3) # use min UDP relay port 32355 and max UDP relay port 65535 # --no-tls and --no-dtls mean that we are not trying to # --no-auth means that no authentication to be used, # allow anonymous users. # start TLS and DTLS services. # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="bin:../bin:../../bin:${PATH}" turnserver -v --syslog -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --no-tls --no-dtls --no-auth $@ turnserver-3.2.3.1/examples/scripts/basic/udp_c2c_client.sh000755 001751 001751 00000002304 12315706777 023635 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "unsecure" TURN UDP client, # in client-to-client fashion (when client talks to another client # through their corresponding allocated relayed endpoints). # Options: # 1) -t is absent, it means that UDP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -y means "client to client" communication pattern. # the client calculates the peer address # (which is the allocated relayed endpoint of the next client in array of clients). # 8) -l 170 means that the payload size of the packets is 170 bytes # like average audio RTP packet). # 9) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 10) 127.0.0.1 (the last parameter) is the TURN Server IP address. # 11) -z 5 means that we want 5 ms interval between the packets (per each session). # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:bin/:${PATH} turnutils_uclient -n 1000 -m 10 -y -l 170 -z 15 $@ 127.0.0.1 turnserver-3.2.3.1/examples/scripts/basic/tcp_client_c2c_tcp_relay.sh000755 001751 001751 00000002012 12315706777 025671 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "unsecure" TURN TCP client # with TCP relay endpoints (RFC 6062). # Options: # 1) -T is present, it means that TCP networking is used with TCP relay endpoints. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -y means that the clients will connect to the 'neighbor' clients, no peer app will be used. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin:bin/:${PATH} turnutils_uclient -T -n 1000 -m 10 -l 170 -y -g $@ ::1 turnserver-3.2.3.1/examples/scripts/basic/dos_attack.sh000755 001751 001751 00000001556 12315706777 023104 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script for DOS attack emulation if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ while [ 0 ] ; do PATH=examples/bin/:../bin/:bin/:${PATH} turnutils_uclient -O -D -G -n 1 -m 12 -e 127.0.0.1 -X -g $@ ::1 & PATH=examples/bin/:../bin/:bin/:${PATH} turnutils_uclient -O -G -n 1 -m 12 -y -s $@ 127.0.0.1 & PATH=examples/bin/:../bin:bin/:${PATH} turnutils_uclient -O -G -t -n 1 -m 12 -e 127.0.0.1 -X -g $@ ::1 & PATH=examples/bin/:../bin:bin/:${PATH} turnutils_uclient -O -G -T -n 1 -m 12 -y -s $@ 127.0.0.1 & sleep 1 type killall >>/dev/null 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then killall turnutils_uclient >>/dev/null 2>>/dev/null else type pkill >>/dev/null 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then pkill turnutils_u >>/dev/null 2>>/dev/null fi fi done turnserver-3.2.3.1/examples/scripts/basic/udp_client.sh000755 001751 001751 00000002237 12315706777 023113 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "unsecure" TURN UDP client. # Options: # 0) -D means "mandatory padding", like pjnath does; # 1) -t is absent, it means that UDP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 171 means that the payload size of the packets is 171 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 11) -X means that IPv4 relay address is requested. # 12) 127.0.0.1 (the last parameter) is the TURN Server IP address. We use IPv4 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:bin/:${PATH} turnutils_uclient -D -n 1000 -m 10 -l 171 -e 127.0.0.1 -g -X $@ 127.0.0.1 turnserver-3.2.3.1/examples/scripts/restapi/secure_relay_secret_with_db_redis.sh000755 001751 001751 00000004101 12315706777 030260 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example how to start a TURN Server in # secure 'dynamic' 'secret' mode (see TURNServerRESTAPI.pdf) # with Redis database for users information # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) --use-auth-secret means that we are using 'secret' authentication mode. # Absense of --static-auth-secret value means that we will be taking the secret value # from the database ('dynamic' mode). # 6) --realm=north.gov sets realm value as "north.gov". # 7) --redis-userdb="ip=127.0.0.1 dbname=0 password=turn connect_timeout=30" # means that local Redis database 0 will be used, with database # password "turn", and connection timeout 30 seconds. # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2. # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --use-auth-secret --realm=north.gov --redis-userdb="ip=127.0.0.1 dbname=0 password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --redis-statsdb="ip=127.0.0.1 dbname=0 password=turn connect_timeout=30" --cipher-list=ALL:SSLv2 $@ turnserver-3.2.3.1/examples/scripts/restapi/secure_udp_client_with_secret.sh000755 001751 001751 00000002467 12315706777 027454 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN UDP client # with the long-term credentials mechanism and with # secret-based authorization (see TURNServerRESTAPI.pdf document). # # Options: # # 1) -t is absent, it means that UDP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u ninefingers means that if the server challenges the client with # authentication challenge, then we use account "ninefingers". # 11) -W logen sets the secret for the secret-based authentication as "logen". # 12) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 13) ::1 (the last parameter) is the TURN Server IPv6 address. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -z 5 -n 10000 -s -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -W logen $@ ::1 turnserver-3.2.3.1/examples/scripts/restapi/secure_relay_secret_with_db_psql.sh000755 001751 001751 00000004104 12315706777 030134 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example how to start a TURN Server in # secure 'dynamic' 'secret' mode (see TURNServerRESTAPI.pdf) # with PostgreSQL database for users information # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) --use-auth-secret means that we are using 'secret' authentication mode. # Absense of --static-auth-secret value means that we will be taking the secret value # from the database ('dynamic' mode). # 6)--realm=north.gov sets realm value as "north.gov". # 7) --psql-userdb="host=localhost dbname=turn user=turn password=turn connect_timeout=30" # means that local PostgreSQL database "turn" will be used, with database user "turn" and # with database user password "turn", and connection timeout 30 seconds. # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2. # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --use-auth-secret --realm=north.gov --psql-userdb="host=localhost dbname=turn user=turn password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL:SSLv2 $@ turnserver-3.2.3.1/examples/scripts/restapi/secure_relay_secret.sh000755 001751 001751 00000003522 12315706777 025400 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example how to start a TURN Server in # secure 'static' 'secret' mode (see TURNServerRESTAPI.pdf) # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) --use-auth-secret means that we are using 'secret' authentication mode. # 6) --static-auth-secret=logen means that we will be using 'static' secret value. # 7) --realm=north.gov sets realm value as "north.gov". # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) "-q 100" means that single user can create no more than 100 sessions # 12) "-Q 300" means that there may be no more than 300 sessions totally # 13) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2. # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --use-auth-secret --static-auth-secret=logen --realm=north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -q 100 -Q 300 --cipher-list=ALL:SSLv2 $@ turnserver-3.2.3.1/examples/scripts/restapi/shared_secret_maintainer.pl000755 001751 001751 00000004160 12315706777 026373 0ustar00olegoleg000000 000000 #!/usr/bin/perl # # This is an example of Perl script maintaining dynamic shared secret # database for the REST API # use strict; use warnings; use DBI; use HTTP::Request::Common; my $DBNAME="turn"; my $DBUSERNAME="turn"; my $DBPWD="turn"; my $DBHOST="localhost"; my $webserver = 'http://example.com/'; my $old_secret = ""; my $current_secret=""; my $INTERVAL=3600; my $dbh; $dbh = DBI->connect("DBI:mysql:$DBNAME;host=$DBHOST", $DBUSERNAME, $DBPWD) || die "Could not connect to database: $DBI::errstr"; $dbh->do('CREATE TABLE IF NOT EXISTS turn_secret (value varchar(512))'); my $c = $dbh->do("delete from turn_secret"); print "Deleted $c rows\n"; $dbh->disconnect(); do { $dbh = DBI->connect("DBI:mysql:$DBNAME;host=$DBHOST", $DBUSERNAME, $DBPWD) || die "Could not connect to database: $DBI::errstr"; $dbh->do('CREATE TABLE IF NOT EXISTS turn_secret (value varchar(512))'); if(length($current_secret)) { if(length($old_secret)) { remove_secret($dbh, $old_secret); } $old_secret=$current_secret; } print "CURRENT SECRET TO BE (RE)GENERATED\n"; $current_secret = generate_secret(); insert_secret($dbh, $current_secret); $dbh->disconnect(); # # Web server interaction example: # Here we can put code to submit this secret to the web server: # my $req = POST($webserver, Content => [param => $current_secret]); $req->method('PUT'); print $req->as_string,"\n"; # # Alternatively, you can use this link for compute-on-demand: # https://github.com/alfreddatakillen/computeengineondemand # # write your code here. # sleep($INTERVAL); } while(1); sub remove_secret { my $dbh = shift; my $secret=shift; my $c = $dbh->do("delete from turn_secret where value = '$secret'"); print "Deleted $c rows\n"; } sub insert_secret { my $dbh = shift; my $secret=shift; my $c = $dbh->do("insert into turn_secret values('$secret')"); print "Inserted $c rows\n"; } sub generate_secret { my @chars = ('0'..'9', 'A'..'F'); my $len = 8; my $string; while($len--){ $string .= $chars[rand @chars] }; return $string; } turnserver-3.2.3.1/examples/scripts/restapi/secure_relay_secret_with_db_mysql.sh000755 001751 001751 00000004074 12315706777 030330 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example how to start a TURN Server in # secure 'dynamic' 'secret' mode (see TURNServerRESTAPI.pdf) # with MySQL database for users information # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) --use-auth-secret means that we are using 'secret' authentication mode. # Absense of --static-auth-secret value means that we will be taking the secret value # from the database ('dynamic' mode). # 6) --realm=north.gov sets realm value as "north.gov". # 7) --mysql-userdb="host=localhost dbname=turn user=turn password=turn connect_timeout=30" # means that local MySQL database "turn" will be used, with database user "turn" and # with database user password "turn", and connection timeout 30 seconds. # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2 # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --use-auth-secret --realm=north.gov --mysql-userdb="host=localhost dbname=turn user=turn password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL:SSLv2 $@ turnserver-3.2.3.1/examples/scripts/mobile/mobile_tcp_client.sh000755 001751 001751 00000002543 12315706777 024626 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" "mobile" # TURN TCP client with the long-term credentials mechanism. # # Options: # # 1) -t is present, it means that TCP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u gorst means that if the server challenges the client with # authentication challenge, then we use account "gorst". # 11) -w hero sets the password for the account as "hero". # 12) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 13) -M turns on the Mobile ICE TURN functionality. # 14) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -t -n 3000 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -M $@ ::1 turnserver-3.2.3.1/examples/scripts/mobile/mobile_dtls_client.sh000755 001751 001751 00000003045 12315706777 025004 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN DTLS client # with "mobile" option and the long-term credentials mechanism. # # Options: # # 1) -t is absent, it means that UDP networking is used. # 2) -S means "SSL protocol with default encryption" # 3) -i absent. # 4) -k sets private key file for TLS. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer IPv4 address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u ninefingers means that if the server challenges the client with # authentication challenge, then we use account "ninefingers". # 11) -w youhavetoberealistic sets the password for the account. # 12) -s option means that the client will be using "send" mechanism for data. # 13) -M turns on the Mobile ICE TURN functionality. # 14) 127.0.0.1 (the last parameter) is the TURN Server IP address. # We use IPv6 - to - IPv4 here to illustrate how the TURN Server # converts the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -S -k turn_client_pkey.pem -n 1000 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic -s -M $@ 127.0.0.1 turnserver-3.2.3.1/examples/scripts/mobile/mobile_relay.sh000755 001751 001751 00000003641 12315706777 023616 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example how to start a "mobile" TURN Server in # secure mode (when authentication is used) - see option -a # that means "use long-term credential mechanism". # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 10 relay threads (-m 10) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) "--user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee" means # "allow user 'ninefinger' with generated key '0xbc807ee29df3c9ffa736523fb2c4e8ee' ". # 7) "--user=gorst:hero" means "allow user 'gorst' with password 'hero' ". # 8) "--cert=turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) "-v" means normal verbose mode (with some moderate logging). # 12) "--mobility" turns on the Mobile ICE TURN functionality. # 13) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2 # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee --user=gorst:hero -r north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -v --mobility --cipher-list=ALL:SSLv2 $@ turnserver-3.2.3.1/examples/scripts/mobile/mobile_tls_client_c2c_tcp_relay.sh000755 001751 001751 00000003120 12315706777 027423 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN TLS client # with "mobile" option and with the long-term credentials mechanism and with # TCP relay endpoints (RFC 6062). # # Options: # # 1) -T is present, it means that TCP networking is used, with TCP # relay endpoints (RFC 6062. # 2) -S means that "secure protocol", that is TLS in the case of TCP, # will be used between the client and the TURN Server. # 3) -i absent. # 4) -k sets private key file for TLS. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -y means that the clients will connect to the 'neighbor' clients, no peer app will be used. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u gorst means that if the server challenges the client with # authentication challenge, then we use account "gorst". # 11) -w hero sets the password for the account as "hero". # 12) -M turns on the Mobile ICE TURN functionality. # 13) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/postgres/9.2-pgdg/lib PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -T -S -k turn_client_pkey.pem -n 1000 -m 10 -l 170 -y -g -u gorst -w hero -M $@ ::1 turnserver-3.2.3.1/examples/scripts/mobile/mobile_udp_client.sh000755 001751 001751 00000002661 12315706777 024631 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN UDP client # with "mobile" option and with the long-term credentials mechanism. # # Options: # # 1) -t is absent, it means that UDP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u ninefingers means that if the server challenges the client with # authentication challenge, then we use account "ninefingers". # 11) -w youhavetoberealistic sets the password for the account as "youhavetoberealistic". # 12) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 13) -M turns on the Mobile ICE TURN functionality. # 14) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -n 1000 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic -M $@ ::1 turnserver-3.2.3.1/examples/scripts/shorttermsecure/secure_udp_client_short_term.sh000755 001751 001751 00000002477 12315706777 031112 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN UDP client # with short-term credential mechanism. # # Options: # # 1) -t is absent, it means that UDP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -A means that the short-term credentials mechanism is used. # 11) -u ninefingers sets the client user name. # 12) -w youhavetoberealistic sets the password for the user account as "youhavetoberealistic". # 13) -s option means that the client will be using "send" indication for data trasfer. # 14) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -n 1000 -m 10 -l 170 -e 127.0.0.1 -X -g -A -u ninefingers -w youhavetoberealistic -s $@ ::1 turnserver-3.2.3.1/examples/scripts/shorttermsecure/secure_relay_short_term_mech.sh000755 001751 001751 00000003613 12315706777 031065 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example how to start a TURN Server in # secure mode with short-term security mechanism - see option -A # that means "use short-term credential mechanism". # # The short-term credentials mechanism must be used with PostgreSQL or # MySQL database only, the flat file userdb cannot be used. # # We listen on available interfaces here, and we use the "external" IPs # for relay endpoints allocation. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) --mysql-userdb="host=localhost dbname=turn user=turn password=turn connect_timeout=30" # means that local MySQL database "turn" will be used, with database user "turn" and # database user password "turn", and connection timeout 30 seconds. # 6) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 7) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 8) "--log-file=stdout" means that all log output will go to the stdout. # 9) -E 127.0.0.1 and -E :;1 sets the relay addresses, in this case for loopback # communications only. # 10) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2. # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -A --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --mysql-userdb="host=localhost dbname=turn user=turn password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -E 127.0.0.1 -E ::1 --cipher-list=ALL:SSLv2 $@ turnserver-3.2.3.1/examples/scripts/shorttermsecure/secure_tcp_client_c2c_tcp_relay_short_term.sh000755 001751 001751 00000002370 12315706777 033671 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN TCP client # with the short-term credentials mechanism and with # TCP relay endpoints (RFC 6062). # # Options: # # 1) -T is present, it means that TCP networking is used, with TCP relay endpoints (RFC 6062). # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -y means that the clients will connect to the 'neighbor' clients, no peer app will be used. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -A sets the short-term credentials mechanism. # 11) -u gorst sets the client user name. # 12) -w hero sets the password for the account as "hero". # 13) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -T -n 1000 -m 10 -l 170 -y -g -A -u gorst -w hero $@ ::1 turnserver-3.2.3.1/examples/scripts/selfloadbalance/secure_relay.sh000755 001751 001751 00000004446 12315706777 025471 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example how to start a TURN Server # with self-udp-balancing, in secure mode # (when authentication is used) - see option -a # that means "use long-term credential mechanism". # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) --aux-server=... options start two auxiliary severs on IP address 127.0.0.1 # and ports 12345 and 12346, and two auxiliary servers on IP adress ::1 # with the same ports. # 2) --self-udp-balance option forces the server to distribute the load from the # main server points to the auxiliary servers through the ALTERNATE-SERVER # mechanism. # 3) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 4) use fingerprints (-f) # 5) use 10 relay threads (-m 10) # 6) use min UDP relay port 32355 and max UDP relay port 65535 # 7) "-r north.gov" means "use authentication realm north.gov" # 8) "--user=ninefingers:youhavetoberealistic" means # "allow user 'ninefinger' with password 'youhavetoberealistic' ". # 9) "--user=gorst:hero" means "allow user 'gorst' with password 'hero' ". # 10) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 11) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 12) "--log-file=stdout" means that all log output will go to the stdout. # 13) "-v" means normal verbose mode (with some moderate logging). # 14) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2. # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --aux-server=127.0.0.1:12345 --aux-server=[::1]:12345 --aux-server=127.0.0.1:12346 --aux-server=[::1]:12346 --udp-self-balance --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:youhavetoberealistic --user=gorst:hero -r north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL:SSLv2 $@ turnserver-3.2.3.1/examples/scripts/selfloadbalance/secure_dos_attack.sh000755 001751 001751 00000016254 12315706777 026471 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a DOS attack # in a "secure" environment on a server with # self-load-balancing option # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ export SLEEP_TIME=9 while [ 0 ] ; do ########################## PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -n 30 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -S -k turn_client_pkey.pem -n 10 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -t -n 50 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -T -n 30 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -T -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -t -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -n 30 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y -p 12345 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic -p 12345 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s -p 12345 $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -t -n 50 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -T -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -T -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -t -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -n 30 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y -p 12346 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic -p 12346 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s -p 12346 $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -t -n 50 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12346 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -T -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12346 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -T -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12346 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -t -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12346 $@ ::1 & ########################### PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -n 30 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -S -k turn_client_pkey.pem -n 10 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -t -n 50 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -T -n 30 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -T -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -t -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -n 30 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y -p 12345 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic -p 12345 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s -p 12345 $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -t -n 50 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -T -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -T -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -t -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -n 30 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y -p 12346 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic -p 12346 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s -p 12346 $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -t -n 50 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12346 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -T -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12346 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -T -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12346 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -t -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12346 $@ ::1 & ######################### sleep ${SLEEP_TIME} type killall >>/dev/null 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then killall turnutils_uclient >>/dev/null 2>>/dev/null fi type pkill >>/dev/null 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then pkill turnutils_u >>/dev/null 2>>/dev/null pkill turnutils_uclie >>/dev/null 2>>/dev/null pkill turnutils_uclient >>/dev/null 2>>/dev/null else sleep 10 fi done turnserver-3.2.3.1/examples/scripts/loadbalance/udp_c2c.sh000755 001751 001751 00000002472 12315706777 023451 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN UDP client # with the long-term credentials mechanism, # in client-to-client communication patter. # # Options: # # 1) -t is absent, it means that UDP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -y means that the clients will be connecting to each other and the peer will not be used. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u ninefingers means that if the server challenges the client with # authentication challenge, then we use account "ninefingers". # 11) -w youhavetoberealistic sets the password for the account as "youhavetoberealistic". # 12) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 13) 127.0.0.1 (the last parameter) is the TURN Server IP address. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -n 1000 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y $@ 127.0.0.1 turnserver-3.2.3.1/examples/scripts/loadbalance/master_relay.sh000755 001751 001751 00000003450 12315706777 024616 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a MASTER TURN server that distributes # the load among several "slave" TURN servers. # # The TURN Server is started in # secure mode (when authentication is used) - see option -a # that means "use long-term credential mechanism". # # We start here a TURN Server listening on IPv4 address # 127.0.0.1. We use 127.0.0.1 as the relay address, too. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) "--user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee" means # "allow user 'ninefinger' with generated key '0xbc807ee29df3c9ffa736523fb2c4e8ee' ". # 7) "--user=gorst:hero" means "allow user 'gorst' with password 'hero' ". # 8) "--log-file=stdout" means that all log output will go to the stdout. # 9) "-v" means normal verbose mode (with some moderate logging). # 10) --no-dtls and --no-tls measn that we are not using DTLS & TLS protocols here # (for the sake of simplicity). # 11) --alternate-server options set the "slave" servers. # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --syslog -a -L 127.0.0.1 -E 127.0.0.1 --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee --user=gorst:hero -r north.gov --log-file=stdout -v --no-dtls --no-tls --alternate-server=127.0.0.1:3333 --alternate-server=127.0.0.1:4444 $@ turnserver-3.2.3.1/examples/scripts/loadbalance/slave_relay_2.sh000755 001751 001751 00000003355 12315706777 024662 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a SLAVE TURN server that accepts # the redirected requests. # # The TURN Server is started in # secure mode (when authentication is used) - see option -a # that means "use long-term credential mechanism". # # We start here a TURN Server listening on IPv4 address # 127.0.0.1. We use 127.0.0.1 as the relay address, too. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 20000 and max UDP relay port 29999 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) "--user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee" means # "allow user 'ninefinger' with generated key '0xbc807ee29df3c9ffa736523fb2c4e8ee' ". # 7) "--user=gorst:hero" means "allow user 'gorst' with password 'hero' ". # 8) "--log-file=stdout" means that all log output will go to the stdout. # 9) "-v" means normal verbose mode (with some moderate logging). # 10) --no-dtls and --no-tls measn that we are not using DTLS & TLS protocols here # (for the sake of simplicity). # 11) -p 4444 means that we are using UDP & TCP listening port 4444. # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --syslog -a -L 127.0.0.1 -E 127.0.0.1 --max-bps=3000000 -f -m 3 --min-port=20000 --max-port=29999 --user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee --user=gorst:hero -r north.gov --log-file=stdout -v --no-dtls --no-tls -p 4444 --cli-port=5768 $@ turnserver-3.2.3.1/examples/scripts/loadbalance/slave_relay_1.sh000755 001751 001751 00000003355 12315706777 024661 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a SLAVE TURN server that accepts # the redirected requests. # # The TURN Server is started in # secure mode (when authentication is used) - see option -a # that means "use long-term credential mechanism". # # We start here a TURN Server listening on IPv4 address # 127.0.0.1. We use 127.0.0.1 as the relay address, too. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 10000 and max UDP relay port 19999 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) "--user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee" means # "allow user 'ninefinger' with generated key '0xbc807ee29df3c9ffa736523fb2c4e8ee' ". # 7) "--user=gorst:hero" means "allow user 'gorst' with password 'hero' ". # 8) "--log-file=stdout" means that all log output will go to the stdout. # 9) "-v" means normal verbose mode (with some moderate logging). # 10) --no-dtls and --no-tls measn that we are not using DTLS & TLS protocols here # (for the sake of simplicity). # 11) -p 3333 means that we are using UDP & TCP listening port 3333. # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --syslog -a -L 127.0.0.1 -E 127.0.0.1 --max-bps=3000000 -f -m 3 --min-port=10000 --max-port=19999 --user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee --user=gorst:hero -r north.gov --log-file=stdout -v --no-dtls --no-tls -p 3333 --cli-port=5767 $@ turnserver-3.2.3.1/examples/scripts/loadbalance/tcp_c2c_tcp_relay.sh000755 001751 001751 00000002271 12315706777 025506 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN TCP client # with the long-term credentials mechanism and with # TCP relay endpoints (RFC 6062). # # Options: # # 1) -T is present, it means that TCP networking is used, with TCP relay endpoints (RFC 6062). # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -y means that the clients will connect to the 'neighbor' clients, no peer app will be used. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u gorst means that if the server challenges the client with # authentication challenge, then we use account "gorst". # 11) -w hero sets the password for the account as "hero". # 12) 127.0.0.1 (the last parameter) is the TURN Server IP address. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -T -n 1000 -m 10 -l 170 -y -g -u gorst -w hero $@ 127.0.0.1 turnserver-3.2.3.1/examples/scripts/longtermsecure/secure_relay_cert.sh000755 001751 001751 00000003661 12315706777 026443 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example how to start a TURN Server in # secure mode (when authentication is used) - see option -a # that means "use long-term credential mechanism". # # This script shows how to use certificate check option. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 10 relay threads (-m 10) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r bolt.co" means "use authentication realm 'bolt.co'" # 6) "--user=ninefingers:youhavetoberealistic" means "allow user # 'ninefinger' with password 'youhavetoberealistic'.". # 7) "--user=bolt:kwyjibo" means "allow user 'bolt' with password 'kwyjibo' ". # 8) "--cert=..." sets the OpenSSL certificate file name. # 9) "--pkey=..." sets the OpenSSL private key name. # 10) --CA-file sets the CA file for client certificate check. # 11) "--log-file=stdout" means that all log output will go to the stdout. # 12) "-v" means normal verbose mode (with some moderate logging). # 13) --cipher-list="ALL:SSLv2:!eNULL:!aNULL:!NULL" measn "all ciphers, except anonymous". # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:youhavetoberealistic --user=bolt:kwyjibo -r bolt.co --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --CA-file=turn_server_cert.pem --log-file=stdout -v --cipher-list="ALL:SSLv2:!eNULL:!aNULL:!NULL" $@ turnserver-3.2.3.1/examples/scripts/longtermsecure/secure_tcp_client_c2c_tcp_relay.sh000755 001751 001751 00000002437 12315706777 031227 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN TCP client # with the long-term credentials mechanism and with # TCP relay endpoints (RFC 6062). # # Options: # # 1) -T is present, it means that TCP networking is used, with TCP relay endpoints (RFC 6062). # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -y means that the clients will connect to the 'neighbor' clients, no peer app will be used. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u gorst means that if the server challenges the client with # authentication challenge, then we use account "gorst". # 11) -w hero sets the password for the account as "hero". # 12) 127.0.0.1 (the last parameter) is the TURN Server IP address. We use IPv4 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -T -n 1000 -m 10 -l 170 -y -g -u gorst -w hero $@ 127.0.0.1 turnserver-3.2.3.1/examples/scripts/longtermsecure/secure_tls_client_cert.sh000755 001751 001751 00000003115 12315706777 027461 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN DTLS client # with the long-term credentials mechanism and with certificate check. # # Options: # # 1) -t means that TCP networking is used. # 2) -S means "SSL protocol with default encryption" # 3) -i sets certificate file for TLS. -R sets certificate check mode. # -E sets CA file for certificate check. # 4) -k sets private key file for TLS. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer IPv4 address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u bolt means that if the server challenges the client with # authentication challenge, then we use account "bolt". # 11) -w kwyjibo sets the password for the account. # 12) -s option means that the client will be using "send" mechanism for data. # 13) 127.0.0.1 (the last parameter) is the TURN Server IP address. # We use IPv6 - to - IPv4 here to illustrate how the TURN Server # converts the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -t -S -i turn_server_cert.pem -k turn_server_pkey.pem -E turn_server_cert.pem -n 1000 -m 10 -l 170 -e 127.0.0.1 -X -g -u bolt -w kwyjibo -s $@ 127.0.0.1 turnserver-3.2.3.1/examples/scripts/longtermsecure/secure_tls_client_c2c_tcp_relay.sh000755 001751 001751 00000002777 12315706777 031252 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN TLS client # with the long-term credentials mechanism and with # TCP relay endpoints (RFC 6062). # # Options: # # 1) -T is present, it means that TCP networking is used, with TCP # relay endpoints (RFC 6062. # 2) -S means that "secure protocol", that is TLS in the case of TCP, # will be used between the client and the TURN Server. # 3) -i absent. # 4) -k sets private key file for TLS. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -y means that the clients will connect to the 'neighbor' clients, no peer app will be used. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u gorst means that if the server challenges the client with # authentication challenge, then we use account "gorst". # 11) -w hero sets the password for the account as "hero". # 12) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/postgres/9.2-pgdg/lib PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -T -S -k turn_client_pkey.pem -n 1000 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 turnserver-3.2.3.1/examples/scripts/longtermsecure/secure_dtls_client_cert.sh000755 001751 001751 00000003130 12315706777 027622 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN DTLS client # with the long-term credentials mechanism and with certificate check. # # Options: # # 1) -t is absent, it means that UDP networking is used. # 2) -S means "SSL protocol with default encryption" # 3) -i sets certificate file for TLS. -R sets certificate check mode. # -E sets CA file for certificate check. # 4) -k sets private key file for TLS. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer IPv4 address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u bolt means that if the server challenges the client with # authentication challenge, then we use account "bolt". # 11) -w kwyjibo sets the password for the account. # 12) -s option means that the client will be using "send" mechanism for data. # 13) 127.0.0.1 (the last parameter) is the TURN Server IP address. # We use IPv6 - to - IPv4 here to illustrate how the TURN Server # converts the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -S -i turn_server_cert.pem -k turn_server_pkey.pem -E turn_server_cert.pem -n 1000 -m 10 -l 170 -e 127.0.0.1 -g -u bolt -w kwyjibo -s -X $@ 127.0.0.1 turnserver-3.2.3.1/examples/scripts/longtermsecure/secure_tcp_client.sh000755 001751 001751 00000002441 12315706777 026431 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN TCP client # with the long-term credentials mechanism. # # Options: # # 1) -t is present, it means that TCP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u gorst means that if the server challenges the client with # authentication challenge, then we use account "gorst". # 11) -w hero sets the password for the account as "hero". # 12) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 13) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -t -n 3000 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 turnserver-3.2.3.1/examples/scripts/longtermsecure/secure_udp_client.sh000755 001751 001751 00000002540 12315706777 026433 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN UDP client # with the long-term credentials mechanism. # # Options: # # 1) -t is absent, it means that UDP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u ninefingers means that if the server challenges the client with # authentication challenge, then we use account "ninefingers". # 11) -w youhavetoberealistic sets the password for the account as "youhavetoberealistic". # 12) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 13) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -n 1000 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic $@ ::1 turnserver-3.2.3.1/examples/scripts/longtermsecure/secure_dtls_client.sh000755 001751 001751 00000002760 12315706777 026615 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN DTLS client # with the long-term credentials mechanism. # # Options: # # 1) -t is absent, it means that UDP networking is used. # 2) -S means "SSL protocol with default encryption" # 3) -i absent. # 4) -k sets private key file for TLS. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer IPv4 address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u ninefingers means that if the server challenges the client with # authentication challenge, then we use account "ninefingers". # 11) -w youhavetoberealistic sets the password for the account. # 12) -s option absent - that means that the client will be using # the channel mechanism for data. # 13) 127.0.0.1 (the last parameter) is the TURN Server IP address. # We use IPv6 - to - IPv4 here to illustrate how the TURN Server # converts the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -S -k turn_client_pkey.pem -n 1000 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic $@ 127.0.0.1 turnserver-3.2.3.1/examples/scripts/longtermsecure/secure_relay.sh000755 001751 001751 00000003437 12315706777 025427 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example how to start a TURN Server in # secure mode (when authentication is used) - see option -a # that means "use long-term credential mechanism". # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 10 relay threads (-m 10) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) "--user=ninefingers:youhavetoberealistic" means # "allow user 'ninefinger' with password 'youhavetoberealistic' ". # 7) "--user=gorst:hero" means "allow user 'gorst' with password 'hero' ". # 8) "--cert=turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) "-v" means normal verbose mode (with some moderate logging). # 12) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2 # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:youhavetoberealistic --user=gorst:hero -r north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -v --cipher-list=ALL:SSLv2 $@ turnserver-3.2.3.1/examples/scripts/longtermsecure/secure_dos_attack.sh000755 001751 001751 00000003145 12315706777 026423 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a DOS attack in a # "secure" environment # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ while [ 0 ] ; do PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -O -n 10 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -O -n 10 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -O -S -k turn_client_pkey.pem -n 10 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -O -t -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -O -T -n 10 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -O -T -S -k turn_client_pkey.pem -n 10 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -O -t -S -k turn_client_pkey.pem -n 10 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 & sleep 2 type killall >>/dev/null 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then killall turnutils_uclient >>/dev/null 2>>/dev/null fi type pkill >>/dev/null 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then pkill turnutils_u >>/dev/null 2>>/dev/null pkill turnutils_uclie >>/dev/null 2>>/dev/null pkill turnutils_uclient >>/dev/null 2>>/dev/null else sleep 10 fi done turnserver-3.2.3.1/examples/scripts/longtermsecure/secure_udp_c2c.sh000755 001751 001751 00000002622 12315706777 025625 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN UDP client # with the long-term credentials mechanism, # in client-to-client communication patter. # # Options: # # 1) -t is absent, it means that UDP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -y means that the clients will be connecting to each other and the peer will not be used. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u ninefingers means that if the server challenges the client with # authentication challenge, then we use account "ninefingers". # 11) -w youhavetoberealistic sets the password for the account as "youhavetoberealistic". # 12) -s option is present - it means that the client will be using # the DATA mechanism for data. # 13) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -s -n 1000 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y $@ ::1 turnserver-3.2.3.1/examples/scripts/longtermsecure/secure_tls_client.sh000755 001751 001751 00000002620 12315706777 026444 0ustar00olegoleg000000 000000 #!/bin/sh # # This is an example of a script to run a "secure" TURN TLS client # with the long-term credentials mechanism. # # Options: # # 1) -t is present, it means that TCP networking is used. # 2) -S means "SSL/TLS protocol with default cipher". # 3) -i absent. # 4) -k sets private key file for TLS. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u gorst means that if the server challenges the client with # authentication challenge, then we use account "gorst". # 11) -w hero sets the password for the account as "hero". # 12) -s option means that the client will be using "send" mechanism for data. # 13) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -t -S -k turn_client_pkey.pem -n 1000 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 turnserver-3.2.3.1/examples/etc/turn_client_cert.pem000644 001751 001751 00000002543 12315706777 022477 0ustar00olegoleg000000 000000 -----BEGIN CERTIFICATE----- MIIDzjCCArYCCQD3YHhln4EqhDANBgkqhkiG9w0BAQUFADCBpzELMAkGA1UEBhMC VVMxCzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxXYWxudXQgQ3JlZWsxKzApBgNVBAoT IlJGQzU3NjYgVFVSTiBTZXJ2ZXIgcHVibGljIHByb2plY3QxFDASBgNVBAsTC2Rl dmVsb3BtZW50MQ0wCwYDVQQDEwRPbGVnMSIwIAYJKoZIhvcNAQkBFhNtb20wNDAy NjdAZ21haWwuY29tMCAXDTEyMTEyNzAwNDEwNVoYDzIxMTIxMTAzMDA0MTA1WjCB pzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxXYWxudXQgQ3Jl ZWsxKzApBgNVBAoTIlJGQzU3NjYgVFVSTiBTZXJ2ZXIgcHVibGljIHByb2plY3Qx FDASBgNVBAsTC2RldmVsb3BtZW50MQ0wCwYDVQQDEwRPbGVnMSIwIAYJKoZIhvcN AQkBFhNtb20wNDAyNjdAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEA3huHvPYyvNZBK91bP3O1dBdOj93YQ3812BTcRMjEYnvSyyEosxFd dEnILgDiFK//pFnDtwm7FxOCtVwRQ0+8qGTH4vH0EIpKTBsaafKH3L9CYe40pwcm BJHvclOa4vl2Ghi09+M0UEHdokkM77K9rpXx7aZILoICkqnoAuBe0TY8D5PBXinM gtk7HlrvANxSmPHAAaGQ5t/+jfTWVH1UYCpogTgCKYPbNi+joKu6oEz+qRKAqDYd FY6/Qpiv7reYiNiVhM7HGNY27FkKDJDBhsmZRmtTIEdYFfcWPZvv69L7Rf1skOXF Vm5/to3HArJJF+lz6YGj0C3pE6dZt6sUmQIDAQABMA0GCSqGSIb3DQEBBQUAA4IB AQAhXgGdXXf0dMPdkfl4jv4dqFNSmax6wmeNc+oJC9qIFVDLsdAaAWXZ+pZHYIMR UN8mQobsIZdfPQ0gs8CgUwrKziAjA92y2Q/I7vsg83qRLhysGC5etYMD/wlySDDS AJKraevDPTEdmfNstCblubNG2PIeqV1isWtPMqB2dMsCeyzJXVyfD0QcABzFv4Fs MMy7JI7MsctNh1tjV/0TsddDMeMLs22rix5fS8MZ6uunFzIuJ0MshFNehXFuvz0B uNmn0k7djUm3h+2Avs3YGCo/8GtqHapc/lva/9gT+iEW0e7i0Ru5Jhar66VMzJqv +wEhQafC77d3vWHtXQU8dYmM -----END CERTIFICATE----- turnserver-3.2.3.1/examples/etc/turnuserdb.conf000644 001751 001751 00000001340 12315706777 021467 0ustar00olegoleg000000 000000 #This file can be used as user accounts storage for long-term credentials mechanism. # #username1:key1 #username2:key2 # OR: #username1:password1 #username2:password2 # # Keys must be generated by turnadmin utility. The key value depends # on user name, realm, and password: # # Example: # $ turnadmin -k -u ninefingers -r north.gov -p youhavetoberealistic # Output: 0xbc807ee29df3c9ffa736523fb2c4e8ee # ('0x' in the beginning of the key is what differentiates the key from # password. If it has 0x then it is a key, otherwise it is a password). # # The corresponding user account entry in the userdb file will be: # #ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee # Or, equivalently (less secure): #ninefingers:youhavetoberealistic # turnserver-3.2.3.1/examples/etc/turn_client_pkey.pem000644 001751 001751 00000003213 12315706777 022505 0ustar00olegoleg000000 000000 -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEA3huHvPYyvNZBK91bP3O1dBdOj93YQ3812BTcRMjEYnvSyyEo sxFddEnILgDiFK//pFnDtwm7FxOCtVwRQ0+8qGTH4vH0EIpKTBsaafKH3L9CYe40 pwcmBJHvclOa4vl2Ghi09+M0UEHdokkM77K9rpXx7aZILoICkqnoAuBe0TY8D5PB XinMgtk7HlrvANxSmPHAAaGQ5t/+jfTWVH1UYCpogTgCKYPbNi+joKu6oEz+qRKA qDYdFY6/Qpiv7reYiNiVhM7HGNY27FkKDJDBhsmZRmtTIEdYFfcWPZvv69L7Rf1s kOXFVm5/to3HArJJF+lz6YGj0C3pE6dZt6sUmQIDAQABAoIBAH5ITN8FZEe10gws qUrkcRD2h3aI/gMyetzGz45UUERmfq17xvY5M1eA884kNmbowoMhfoO9hqBSOYkA Ndh9p5he5L+GLeyRlDi9WEFQ4iqCnC2uEEW/bMBAcVIhcvkGOT4ROiOPDRlsuaUh v7cxe2OeYZVra7L1vJzC+eVYyNBN5CgK8w08MPEkupQS9+Jvr0QWCikRz187cG45 EiDMrBKyJNE9lY6u4P8gJ+/NgaASWP/D3kbsjiQ2OwSGLrwDAvWC7Bx2GK3/0goA btp7YGaWvp+mE5V91cOW+PfweC5Do4MjOr4ToNkczW0AxKE5o94yo56h+II5bX6N z65VvtkCgYEA/Sq/3S2yup/Oodzj003KG4skWYFrj7KXeXgm7RZcpNwkd8JaFXJ/ Cwl7/3bkRv6RHLmXX/2hcNWlxq3u6Efs1EjtycdArU68kO01vLdExJYIzHKmHikV n+T4hukxGDzObxn3lH1KcOodh/x572Uufn79dewoZCPzH8t/jiMOWGcCgYEA4JfN 66Kq/oDookqenM9Ij5l6zeeNwzMjIlkU2eG0DAH0KdsBN/hTGGGRQVBk03YREQmK crEhGAZxzfrX5fK11UVG3C2pqAtrVe6FuD32vFUpP1MO0ftSA889NoEwGdNZV4pV Mk0+6xVCNOatj2inMXlQq5s68WfCzkiWD7uLCv8CgYBcwuYsF4tuYBGpMzNzAAS2 1OPLu+T6cPiZdFHm+xOVAGiITPkO9LXiCGabsydvb+UhvkrdzCP0IQQt6RsplvkK y3H9RfnHxprHC3NuI0SaN1Mf/j4pvOoEfTQm0pi/hcAp6zzQ9ptpBg8t/W98LPm9 NbCPHamrD5UMqFajcOrXrwKBgD8D2M8IcRm/aYY/kYlFz4Ia+g3Trj7alj0I6YTI gw/rbGph/FGL5ySsG2lL+T4rnlY9aw8LC9IF3OCCRRlLpCEWsu8MENIJgjA2IGa1 XAkzi8MstrfL4BMZjn9AeBKG7kZVldnrOoATEuRs5L2cC20iMLQ1dbBOAKaITzJS 2IxZAoGBAKqwr/uennxJrnMtpjLBgcphoU3aXJZvzzDqlOaqzJp6Xmbese4sDEe0 hvVHreigDzOnGnqL/vSjTDWaLqS/O1iE7p+UrGIkZj/Zl6Jk54OX6AHmWE2LhdlU FYgIQKX7fuocpF1Dpe7xEeVwvdp+UqbDzHQg1CWGe1cBPYDYIkSH -----END RSA PRIVATE KEY----- turnserver-3.2.3.1/examples/etc/turn_server_pkey.pem000644 001751 001751 00000003217 12315706777 022541 0ustar00olegoleg000000 000000 -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAv6bYkERhZ43RjW4EuqCaTq5g+D+lJI/GwlVzdzQ3+F4clMQD R1kp1nX+9AvwjCXz3AYwY1H9CqjmjGM4R9uNJJseK/aJd2DUFADkF+7I674XwX8U 2Fy5on9jqWq3jdbb8eg/awcTBdrNLWNPquwfS2KVdooj9yPkqnO0c3ko1/OzIQCc s09O3l/MPt+aOsHk3B9l79ZRs3zWkylI+we0Fnc+7tZEpsCztA+KCCoiJf7NenOv VhdKg7D1AXuzJ/P/Euvc3+CIiS9HI4pWLopY1k+HydLeIcopqSbg9CRIKe1HOL8Y TvCm2ZoTqgijwWUlGtwEDf2xxUQX/TLYiW8JFQIDAQABAoIBADUPHCXUyKLCwKFH NEf27sGZxX71H+NfaseioLT/3/8DDyagncfDB7I4OL2YEKC8YScpD3xv1n59BFcZ oRtDzW+1AkVpm+VRCWYAWSXHFhkuJ6WKaVr9UOeMHStqQCcktP/kLKqU6s9UJDnM pOHNPVzBjl+jHxHs/gGyxuKxSH2Anwkrzpiv5j0obKFnw3QtAqeZRs1NlvPtYt2S eihZWr8r8LqylPk9ga9MYmO79Yr+EPVaqd6bmz4MpZJ4/7LEjx03Q6azdMCPhFNY cYzPIDZFEj81Zj/tqA2MU/uTTUUrcXint4dHRJs34m5N68PV1Y1XhhH6FG0+X711 ZymudoECgYEA/ChS5zmmOoLoaq2441+PzQbDP45qR6+G4slHwC8RDZhsYw0hQnp9 n44Qagpt74J4FjxT20BdE714DZP32IqagUwatWRQ+z3UoGafkJSNc5JSEogwZ65C nC8RI1pPHLEvE8IzBJiqUA1kbMOMfTYW694wdN9JVZang05/AXaJzm8CgYEAwpJ8 nJRR9JFweHRrRgnrVk0Qi+ABbN9T/nhPXYab2vjBfeBOTA1Mob0M3zMJDCnL2i+D K1GzE6WaYHElr45j2Wfphd/rRTk74WR4BaPpTCGaAhBQNn0ufqUkKsCPEAlTU+nG iyXP4OvdMPjEBckjbKm/mlX7m0njSHAY6SWNorsCgYEAi8Yubk3efwChpMC3hBIs vBHLmSdwclwyAPRh+X4djdO4AQ/+J8OObytond86IVHJD0pRkW+UKKUWLzCeakIq cxGknHgHC72yZ1d7i8FMx4uMQwmLC23lLn5ImbgtslHlLqavcRTPE6DY0hFzhtS8 z/JSGfbLx83C/V49uKnkqbECgYA6h1oYt70XdpCAi3ShcuZp5XCuwslq+JsJlyM4 nP9RFTcPKGQlGHMOzBGNKor0L7Z0gYpRg5f8tvoDPMX7UzfR9CIY9UyOXDMZD+HS wIWzMwBi0olueqV7zy1b9uSSDFwWh+IDhXJM1GaLDqnYm7KeQ0mxoV+4TLej2KSF rZg3dQKBgQCVrVxFV8jHBsRsH5PzMx6pUSAollmuyte9mGU1MIE7EZf+LEQIAjGZ 9jvtAILYVJXwVZv1/zNxldUfBNuWc95ft+Gg7FEN0p0uLpdYNXQUcXuJaJ9tJ1td ZfvRcrUXdFNKYt9/yaGeHVaIQfp4W1faZD7OnII7EOVkUKyv/qNGAA== -----END RSA PRIVATE KEY----- turnserver-3.2.3.1/examples/etc/turn_server_cert.pem000644 001751 001751 00000002472 12315706777 022530 0ustar00olegoleg000000 000000 -----BEGIN CERTIFICATE----- MIIDsDCCApgCCQCmgrJCiQlGOTANBgkqhkiG9w0BAQUFADCBmDELMAkGA1UEBhMC VVMxCzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxXYWxudXQgQ3JlZWsxHDAaBgNVBAoT E1RVUk4gU2VydmVyIHByb2plY3QxFDASBgNVBAsTC0RldmVsb3BtZW50MQ0wCwYD VQQDEwRPbGVnMSIwIAYJKoZIhvcNAQkBFhNtb20wNDAyNjdAZ21haWwuY29tMCAX DTEyMTEyNTA4MjAxNloYDzIxMTIxMTAxMDgyMDE2WjCBmDELMAkGA1UEBhMCVVMx CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxXYWxudXQgQ3JlZWsxHDAaBgNVBAoTE1RV Uk4gU2VydmVyIHByb2plY3QxFDASBgNVBAsTC0RldmVsb3BtZW50MQ0wCwYDVQQD EwRPbGVnMSIwIAYJKoZIhvcNAQkBFhNtb20wNDAyNjdAZ21haWwuY29tMIIBIjAN BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv6bYkERhZ43RjW4EuqCaTq5g+D+l JI/GwlVzdzQ3+F4clMQDR1kp1nX+9AvwjCXz3AYwY1H9CqjmjGM4R9uNJJseK/aJ d2DUFADkF+7I674XwX8U2Fy5on9jqWq3jdbb8eg/awcTBdrNLWNPquwfS2KVdooj 9yPkqnO0c3ko1/OzIQCcs09O3l/MPt+aOsHk3B9l79ZRs3zWkylI+we0Fnc+7tZE psCztA+KCCoiJf7NenOvVhdKg7D1AXuzJ/P/Euvc3+CIiS9HI4pWLopY1k+HydLe IcopqSbg9CRIKe1HOL8YTvCm2ZoTqgijwWUlGtwEDf2xxUQX/TLYiW8JFQIDAQAB MA0GCSqGSIb3DQEBBQUAA4IBAQATbrBOLV4e8Qmsby9+srxXsdbNc60PmDZ4WiZ1 IElfWmzM7wGXm9sJg1PX/7T24R1tbwZGLIhZnkhecG372GChULZJ9Pdjh0Ab2nK5 LRKHXTpjp/xOJvx0JMCIIyRnGZT1nABPOk8uEjNW8PaU6yhQ4f5nKaSOgYGRCln6 dcy5vylCsyD9Q7GXs0KOC38XD+Ycv6VLX4zKJ2Yum50Wt643nLjG9RlGT3FXWJ1K HUbPC5TO6bcYLdiTjaYr+X8xC/x6h/Ngdo/16w7fRmQQ4uS+TVXrg8ITmI71KX/I m7C9jbsubwzrhW84oZXYf+o/0ATtEAhiVLnHifKCCYikqfVj -----END CERTIFICATE----- turnserver-3.2.3.1/examples/etc/turnserver.conf000644 001751 001751 00000051446 12315706777 021525 0ustar00olegoleg000000 000000 # RFC5766-TURN-SERVER configuration file # # Boolean values note: where boolean value is supposed to be used, # you can use '0', 'off', 'no', 'false', 'f' as 'false, # and you can use '1', 'on', 'yes', 'true', 't' as 'true' # If the value is missed, then it means 'true'. # # Listener interface device (optional, Linux only). # NOT RECOMMENDED. # #listening-device=eth0 # TURN listener port for UDP and TCP (Default: 3478). # Note: actually, TLS & DTLS sessions can connect to the # "plain" TCP & UDP port(s), too - if allowed by configuration. # #listening-port=3478 # TURN listener port for TLS (Default: 5349). # Note: actually, "plain" TCP & UDP sessions can connect to the TLS & DTLS # port(s), too - if allowed by configuration. The TURN server # "automatically" recognizes the type of traffic. Actually, two listening # endpoints (the "plain" one and the "tls" one) are equivalent in terms of # functionality; but we keep both endpoints to satisfy the RFC 5766 specs. # For secure TCP connections, we currently support SSL version 3 and # TLS version 1.0, 1.1 and 1.2. SSL2 "encapculation mode" is also supported. # For secure UDP connections, we support DTLS version 1. # #tls-listening-port=5349 # Alternative listening port for UDP and TCP listeners; # default (or zero) value means "listening port plus one". # This is needed for RFC 5780 support # (STUN extension specs, NAT behavior discovery). The TURN Server # supports RFC 5780 only if it is started with more than one # listening IP address of the same family (IPv4 or IPv6). # RFC 5780 is supported only by UDP protocol, other protocols # are listening to that endpoint only for "symmetry". # #alt-listening-port=0 # Alternative listening port for TLS and DTLS protocols. # Default (or zero) value means "TLS listening port plus one". # #alt-tls-listening-port=0 # Listener IP address of relay server. Multiple listeners can be specified. # If no IP(s) specified in the config file or in the command line options, # then all IPv4 and IPv6 system IPs will be used for listening. # #listening-ip=172.17.19.101 #listening-ip=10.207.21.238 #listening-ip=2607:f0d0:1002:51::4 # Auxiliary STUN/TURN server listening endpoint. # Aux servers have almost full TURN and STUN functionality. # The (minor) limitations are: # # 1) Auxiliary servers do not have alternative ports and # they do not support STUN RFC 5780 functionality (CHANGE REQUEST). # # 2) Auxiliary servers also are never returning ALTERNATIVE-SERVER reply. # # Valid formats are 1.2.3.4:5555 for IPv4 and [1:2::3:4]:5555 for IPv6. # # There may be multiple aux-server options, each will be used for listening # to client requests. # #aux-server=172.17.19.110:33478 #aux-server=[2607:f0d0:1002:51::4]:33478 # (recommended for older Linuxes only) # Automatically balance UDP traffic over auxiliary servers (if configured). # The load balancing is using the ALTERNATE-SERVER mechanism. # The TURN client must support 300 ALTERNATE-SERVER response for this # functionality. # #udp-self-balance # Relay interface device for relay sockets (optional, Linux only). # NOT RECOMMENDED. # #relay-device=eth1 # Relay address (the local IP address that will be used to relay the # packets to the peer). # Multiple relay addresses may be used. # The same IP(s) can be used as both listening IP(s) and relay IP(s). # # If no relay IP(s) specified, then the turnserver will apply the default # policy: it will decide itself which relay addresses to be used, and it # will always be using the client socket IP address as the relay IP address # of the TURN session (if the requested relay address family is the same # as the family of the client socket). # #relay-ip=172.17.19.105 #relay-ip=2607:f0d0:1002:51::5 # For Amazon EC2 users: # # TURN Server public/private address mapping, if the server is behind NAT. # In that situation, if a -X is used in form "-X " then that ip will be reported # as relay IP address of all allocations. This scenario works only in a simple case # when one single relay address is be used, and no RFC5780 functionality is required. # That single relay address must be mapped by NAT to the 'external' IP. # The "external-ip" value, if not empty, is returned in XOR-RELAYED-ADDRESS field. # For that 'external' IP, NAT must forward ports directly (relayed port 12345 # must be always mapped to the same 'external' port 12345). # # In more complex case when more than one IP address is involved, # that option must be used several times, each entry must # have form "-X ", to map all involved addresses. # RFC5780 NAT discovery STUN functionality will work correctly, # if the addresses are mapped properly, even when the TURN server itself # is behind A NAT. # # By default, this value is empty, and no address mapping is used. # #external-ip=60.70.80.91 # #OR: # #external-ip=60.70.80.91/172.17.19.101 #external-ip=60.70.80.92/172.17.19.102 # Number of relay threads to handle the established connections # (in addition to authentication thread and the listener thread). # If set to 0 then application runs relay process in a single thread, # in the same thread with the listener process (the authentication thread will # still be a separate thread). # # In the older systems (Linux kernel before 3.9), # the number of UDP threads is always one thread per network listening endpoint - # including the auxiliary endpoints - unless 0 (zero) or 1 (one) value is set. # #relay-threads=0 # Lower and upper bounds of the UDP relay endpoints: # (default values are 49152 and 65535) # #min-port=49152 #max-port=65535 # Uncomment to run TURN server in 'normal' 'moderate' verbose mode. # By default the verbose mode is off. #verbose # Uncomment to run TURN server in 'extra' verbose mode. # This mode is very annoying and produces lots of output. # Not recommended under any normal circumstances. # #Verbose # Uncomment to use fingerprints in the TURN messages. # By default the fingerprints are off. # #fingerprint # Uncomment to use long-term credential mechanism. # By default no credentials mechanism is used (any user allowed). # This option can be used with either flat file user database or # PostgreSQL DB or MySQL DB or Redis DB for user keys storage. # #lt-cred-mech # Uncomment to use short-term credential mechanism. # By default no credentials mechanism is used (any user allowed). # For short-term credential mechanism you have to use PostgreSQL or # MySQL or Redis database for user password storage. # #st-cred-mech # This option is opposite to lt-cred-mech or st-cred-mech. # (TURN Server with no-auth option allows anonymous access). # If neither option is defined, and no users are defined, # then no-auth is default. If at least one user is defined, # in this file or in command line or in usersdb file, then # lt-cred-mech is default. # #no-auth # TURN REST API flag. # Flag that sets a special authorization option that is based upon authentication secret. # This feature can be used with the long-term authentication mechanism, only. # This feature purpose is to support "TURN Server REST API", see # "TURN REST API" link in the project's page # http://code.google.com/p/rfc5766-turn-server/. # # This option is used with timestamp: # # usercombo -> "timestamp:userid" # turn user -> usercombo # turn password -> base64(hmac(secret key, usercombo)) # # This allows TURN credentials to be accounted for a specific user id. # If you don't have a suitable id, the timestamp alone can be used. # This option is just turning on secret-based authentication. # The actual value of the secret is defined either by option static-auth-secret, # or can be found in the turn_secret table in the database (see below). # #use-auth-secret # 'Static' authentication secret value (a string) for TURN REST API only. # If not set, then the turn server # will try to use the 'dynamic' value in turn_secret table # in user database (if present). The database-stored value can be changed on-the-fly # by a separate program, so this is why that other mode is 'dynamic'. # #static-auth-secret # 'Static' user accounts for long term credentials mechanism, only. # This option cannot be used with TURN REST API or with short-term credentials # mechanism. # 'Static' user accounts are NOT dynamically checked by the turnserver process, # so that they can NOT be changed while the turnserver is running. # #user=username1:key1 #user=username2:key2 # OR: #user=username1:password1 #user=username2:password2 # # Keys must be generated by turnadmin utility. The key value depends # on user name, realm, and password: # # Example: # $ turnadmin -k -u ninefingers -r north.gov -p youhavetoberealistic # Output: 0xbc807ee29df3c9ffa736523fb2c4e8ee # ('0x' in the beginning of the key is what differentiates the key from # password. If it has 0x then it is a key, otherwise it is a password). # # The corresponding user account entry in the config file will be: # #user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee # Or, equivalently, with open clear password (less secure): #user=ninefingers:youhavetoberealistic # # 'Dynamic' user accounts database file name. # Only users for long-term mechanism can be stored in a flat file, # short-term mechanism will not work with option, the short-term # mechanism required PostgreSQL or MySQL or Redis database. # 'Dynamic' long-term user accounts are dynamically checked by the turnserver process, # so that they can be changed while the turnserver is running. # # Default file name is turnuserdb.conf. # #userdb=/usr/local/etc/turnuserdb.conf # PostgreSQL database connection string in the case that we are using PostgreSQL # as the user database. # This database can be used for long-term and short-term credential mechanisms # and it can store the secret value for secret-based timed authentication in TURN RESP API. # See http://www.postgresql.org/docs/8.4/static/libpq-connect.html for 8.x PostgreSQL # versions connection string format, see # http://www.postgresql.org/docs/9.2/static/libpq-connect.html#LIBPQ-CONNSTRING # for 9.x and newer connection string formats. # #psql-userdb="host= dbname= user= password= connect_timeout=30" # MySQL database connection string in the case that we are using MySQL # as the user database. # This database can be used for long-term and short-term credential mechanisms # and it can store the secret value for secret-based timed authentication in TURN RESP API. # Use string format as below (space separated parameters, all optional): # #mysql-userdb="host= dbname= user= password= port= connect_timeout=" # Redis database connection string in the case that we are using Redis # as the user database. # This database can be used for long-term and short-term credential mechanisms # and it can store the secret value for secret-based timed authentication in TURN RESP API. # Use string format as below (space separated parameters, all optional): # #redis-userdb="ip= dbname= password= port= connect_timeout=" # Redis status and statistics database connection string, if used (default - empty, no Redis stats DB used). # This database keeps allocations status information, and it can be also used for publishing # and delivering traffic and allocation event notifications. # The connection string has the same parameters as redis-userdb connection string. # Use string format as below (space separated parameters, all optional): # #redis-statsdb="ip= dbname= password= port= connect_timeout=" # Realm for long-term credentials mechanism and for TURN REST API. # #realm=mycompany.org # Per-user allocation quota. # default value is 0 (no quota, unlimited number of sessions per user). # #user-quota=0 # Total allocation quota. # default value is 0 (no quota). # #total-quota=0 # Max bytes-per-second bandwidth a TURN session is allowed to handle # (input and output network streams are treated separately). Anything above # that limit will be dropped or temporary suppressed (within # the available buffer limits). # #max-bps=0 # Uncomment if no UDP client listener is desired. # By default UDP client listener is always started. # #no-udp # Uncomment if no TCP client listener is desired. # By default TCP client listener is always started. # #no-tcp # Uncomment if no TLS client listener is desired. # By default TLS client listener is always started. # #no-tls # Uncomment if no DTLS client listener is desired. # By default DTLS client listener is always started. # #no-dtls # Uncomment if no UDP relay endpoints are allowed. # By default UDP relay endpoints are enabled (like in RFC 5766). # #no-udp-relay # Uncomment if no TCP relay endpoints are allowed. # By default TCP relay endpoints are enabled (like in RFC 6062). # #no-tcp-relay # Uncomment if extra security is desired, # with nonce value having limited lifetime (600 secs). # By default, the nonce value is unique for a session, # but it has unlimited lifetime. With this option, # the nonce lifetime is limited to 600 seconds, after that # the client will get 438 error and will have to re-authenticate itself. # #stale-nonce # Certificate file. # Use an absolute path or path relative to the # configuration file. # #cert=/usr/local/etc/turn_server_cert.pem # Private key file. # Use an absolute path or path relative to the # configuration file. # Use PEM file format. # #pkey=/usr/local/etc/turn_server_pkey.pem # Private key file password, if it is in encoded format. # This option has no default value. # #pkey-pwd=... # Allowed OpenSSL cipher list for TLS/DTLS connections. # Default value is "DEFAULT". # #cipher-list="DEFAULT" # CA file in OpenSSL format. # Forces TURN server to verify the client SSL certificates. # By default it is not set: there is no default value and the client # certificate is not checked. # # Example: #CA-file=/etc/ssh/id_rsa.cert # Curve name for EC ciphers, if supported by OpenSSL library (TLS and DTLS). # The default value is prime256v1. # #ec-curve-name=prime256v1 # Use 566 bits predefined DH TLS key. Default size of the key is 1066. # #dh566 # Use 2066 bits predefined DH TLS key. Default size of the key is 1066. # #dh2066 # Use custom DH TLS key, stored in PEM format in the file. # Flags --dh566 and --dh2066 are ignored when the DH key is taken from a file. # #dh-file= # Flag to prevent stdout log messages. # By default, all log messages are going to both stdout and to # the configured log file. With this option everything will be # going to the configured log only (unless the log file itself is stdout). # #no-stdout-log # Option to set the log file name. # By default, the turnserver tries to open a log file in # /var/log, /var/tmp, /tmp and current directories directories # (which open operation succeeds first that file will be used). # With this option you can set the definite log file name. # The special names are "stdout" and "-" - they will force everything # to the stdout. Also, the "syslog" name will force everything to # the system log (syslog). # #log-file=/var/tmp/turn.log # Option to redirect all log output into system log (syslog). # #syslog # Option to set the "redirection" mode. The value of this option # will be the address of the alternate server for UDP & TCP service in form of # [:]. The server will send this value in the attribute # ALTERNATE-SERVER, with error 300, on ALLOCATE request, to the client. # Client will receive only values with the same address family # as the client network endpoint address family. # See RFC 5389 and RFC 5766 for ALTERNATE-SERVER functionality description. # The client must use the obtained value for subsequent TURN communications. # If more than one --alternate-server options are provided, then the functionality # can be more accurately described as "load-balancing" than a mere "redirection". # If the port number is omitted, then the default port # number 3478 for the UDP/TCP protocols will be used. # Colon (:) characters in IPv6 addresses may conflict with the syntax of # the option. To alleviate this conflict, literal IPv6 addresses are enclosed # in square brackets in such resource identifiers, for example: # [2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478 . # Multiple alternate servers can be set. They will be used in the # round-robin manner. All servers in the pool are considered of equal weight and # the load will be distributed equally. For example, if we have 4 alternate servers, # then each server will receive 25% of ALLOCATE requests. A alternate TURN server # address can be used more than one time with the alternate-server option, so this # can emulate "weighting" of the servers. # # Examples: #alternate-server=1.2.3.4:5678 #alternate-server=11.22.33.44:56789 #alternate-server=5.6.7.8 #alternate-server=[2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478 # Option to set alternative server for TLS & DTLS services in form of # :. If the port number is omitted, then the default port # number 5349 for the TLS/DTLS protocols will be used. See the previous # option for the functionality description. # # Examples: #tls-alternate-server=1.2.3.4:5678 #tls-alternate-server=11.22.33.44:56789 #tls-alternate-server=[2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478 # Option to suppress TURN functionality, only STUN requests will be processed. # Run as STUN server only, all TURN requests will be ignored. # By default, this option is NOT set. # #stun-only # Option to suppress STUN functionality, only TURN requests will be processed. # Run as TURN server only, all STUN requests will be ignored. # By default, this option is NOT set. # #no-stun # This is the username/timestamp separator symbol (character) in TURN REST API. # The default value is ':'. # rest-api-separator=: # Flag that can be used to disallow peers on the loopback addresses (127.x.x.x and ::1). # This is an extra security measure. # #no-loopback-peers # Flag that can be used to disallow peers on well-known broadcast addresses (224.0.0.0 and above, and FFXX:*). # This is an extra security measure. # #no-multicast-peers # Option to set the max time, in seconds, allowed for full allocation establishment. # Default is 60 seconds. # #max-allocate-timeout=60 # Option to allow or ban specific ip addresses or ranges of ip addresses. # If an ip address is specified as both allowed and denied, then the ip address is # considered to be allowed. This is useful when you wish to ban a range of ip # addresses, except for a few specific ips within that range. # # This can be used when you do not want users of the turn server to be able to access # machines reachable by the turn server, but would otherwise be unreachable from the # internet (e.g. when the turn server is sitting behind a NAT) # # Examples: # denied-peer-ip=83.166.64.0-83.166.95.255 # allowed-peer-ip=83.166.68.45 # File name to store the pid of the process. # Default is /var/run/turnserver.pid (if superuser account is used) or # /var/tmp/turnserver.pid . # #pidfile="/var/run/turnserver.pid" # Require authentication of the STUN Binding request. # By default, the clients are allowed anonymous access to the STUN Binding functionality. # #secure-stun # Require SHA256 digest function to be used for the message integrity. # By default, the server uses SHA1 (as per TURN standard specs). # With this option, the server # always requires the stronger SHA256 function. The client application # must support SHA256 hash function if this option is used. If the server obtains # a message from the client with a weaker (SHA1) hash function then the # server returns error code 426. # #sha256 # Mobility with ICE (MICE) specs support. # #mobility # User name to run the process. After the initialization, the turnserver process # will make an attempt to change the current user ID to that user. # #proc-user= # Group name to run the process. After the initialization, the turnserver process # will make an attempt to change the current group ID to that group. # #proc-group= # Turn OFF the CLI support. # By default it is always ON. # See also options cli-ip and cli-port. # #no-cli #Local system IP address to be used for CLI server endpoint. Default value # is 127.0.0.1. # #cli-ip=127.0.0.1 # CLI server port. Default is 5766. # #cli-port=5766 # CLI access password. Default is empty (no password). # #cli-password=logen # Server relay. NON-STANDARD AND DANGEROUS OPTION. # Only for those applications when we want to run # server applications on the relay endpoints. # This option eliminates the IP permissions check on # the packets incoming to the relay endpoints. # #server-relay # Maximum number of output sessions in ps CLI command. # This value can be changed on-the-fly in CLI. The default value is 256. # #cli-max-output-sessions # Set network engine type for the process (for internal purposes). # #ne=[1|2|3] # Do not allow an SSL/TLS version of protocol # #no-sslv2 #no-sslv3 #no-tlsv1 #no-tlsv1_1 #no-tlsv1_2 turnserver-3.2.3.1/man/man1/000755 001751 001751 00000000000 12315706777 015443 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/man/man1/turnutils_peer.1000755 001751 001751 00000000000 12315706777 022735 2turnutils.1ustar00olegoleg000000 000000 turnserver-3.2.3.1/man/man1/turnutils_stunclient.1000755 001751 001751 00000000000 12315706777 024172 2turnutils.1ustar00olegoleg000000 000000 turnserver-3.2.3.1/man/man1/turnutils.1000644 001751 001751 00000023276 12315706777 017610 0ustar00olegoleg000000 000000 .\" Text automatically generated by txt2man .TH TURN 1 "28 March 2014" "" "" .SH GENERAL INFORMATION A set of turnutils_* programs provides some utility functionality to be used for testing and for setting up the TURN server. .TP .B 1. \fIturnutils_uclient\fP: emulates multiple UDP,TCP,TLS or DTLS clients. (this program is provided for the testing purposes only !) The compiled binary image of this program is located in bin/ sub\-directory. .PP WARNING: the \fIturnutils_uclient\fP program is a primitive client application. It does not implement the re\-transmission pattern that is necessary for a correct TURN client implementation. In TURN, the retransmission burden is lying almost entirely on the client application. We provide the messaging functionality in the client library, but the client must implement the correct Networking IO processing in the client program code. .TP .B 2. \fIturnutils_peer\fP: a simple stateless UDP\-only "echo" server, to be used as the final server in relay pattern ("peer"). For every incoming UDP packet, it simply echoes it back. (this program is provided for the testing purposes only !) When the test clients are communicating in the client\-to\-client manner (when the "\fIturnutils_uclient\fP" program is used with "\fB\-y\fP" option) then the \fIturnutils_peer\fP is not needed. .PP The compiled binary image of this program is located in bin/ subdirectory. .TP .B 3. \fIturnutils_stunclient\fP: a simple STUN client example. The compiled binary image of this program is located in bin/ subdirectory. .TP .B 4. \fIturnutils_rfc5769check\fP: a utility that checks the correctness of the STUN/TURN protocol implementation. This utility is used only for the compilation check procedure, it is not copied to the installation destination. .RE .PP .RS In the "examples/scripts" subdirectory, you will find the examples of command lines to be used to run the programs. The scripts are meant to be run from examples/ subdirectory, for example: .PP $ cd examples .PP $ ./scripts/secure_relay.sh .PP ===================================== .SS NAME \fB \fBturnutils_uclient \fP\- this client emulation application is supplied for the test purposes only. \fB .SS SYNOPSIS $ \fIturnutils_uclient\fP [\fB\-tTSvsyhcxg\fP] [options] .SS DESCRIPTION It was designed to simulate multiple clients. It uses asynch IO API in libevent to handle multiple clients. A client connects to the relay, negotiates the session, and sends multiple (configured number) messages to the server (relay), expecting the same number of replies. The length of the messages is configurable. The message is an arbitrary octet stream, but it can be configured as a string. The number of the messages to send is configurable. .TP .B Flags: .TP .B \fB\-t\fP Use TCP for communications between client and TURN server (default is UDP). .TP .B \fB\-T\fP Use TCP for the relay transport (default \- UDP). Implies options \fB\-t\fP, \fB\-y\fP, \fB\-c\fP, and ignores flags and options \fB\-s\fP, \fB\-e\fP, \fB\-r\fP and \fB\-g\fP. .TP .B \fB\-P\fP Passive TCP (RFC6062 with active peer). Implies \fB\-T\fP. .TP .B \fB\-S\fP Secure SSL connection: SSL/TLS for TCP, DTLS for UDP. .TP .B \fB\-U\fP Secure unencrypted connection (suite eNULL): SSL/TLS for TCP, DTLS for UDP. .TP .B \fB\-v\fP Verbose. .TP .B \fB\-s\fP Use "Send" method in TURN; by default, it uses TURN Channels. .TP .B \fB\-y\fP Use client\-to\-client connections: RTP/RTCP pair of channels to another RTP/RTCP pair of channels. with this option the \fIturnutils_peer\fP application is not used, as the allocated relay endpoints are talking to each other. .TP .B \fB\-h\fP Hang on indefinitely after the last sent packet. .TP .B \fB\-c\fP Do not create rtcp connections. .TP .B \fB\-x\fP Request IPv6 relay address (RFC6156). .TP .B \fB\-X\fP IPv4 relay address explicitly requested. .TP .B \fB\-g\fP Set DONT_FRAGMENT parameter in TURN requests. .TP .B \fB\-A\fP use short\-term credentials mechanism for authentication. By default, the program uses the long\-term credentials mechanism if authentication is required. .TP .B \fB\-D\fP Do mandatory channel padding even for UDP (like pjnath). .TP .B \fB\-N\fP do negative tests (some limited cases only). .TP .B \fB\-R\fP do negative protocol tests. .TP .B \fB\-O\fP DOS attack mode. .TP .B \fB\-H\fP SHA256 digest function for message integrity calculation. Without this option, by default, SHA1 is used. .TP .B \fB\-M\fP Use TURN ICE Mobility. .TP .B \fB\-I\fP Do not set permissions on TURN relay endpoints (for testing the non\-standard server relay functionality). .TP .B \fB\-G\fP Generate extra requests (create permissions, channel bind). .TP .B Options with required values: .TP .B \fB\-l\fP Message length (Default: 100 Bytes). .TP .B \fB\-i\fP Certificate file (for secure connections only, optional). .TP .B \fB\-k\fP Private key file (for secure connections only). .TP .B \fB\-E\fP CA file for server certificate verification, if the server certificate to be verified. .TP .B \fB\-p\fP \fBTURN Server\fP port (Defaults: 3478 unsecure, 5349 secure). .TP .B \fB\-n\fP Number of messages to send (Default: 5). .TP .B \fB\-d\fP Local interface device (optional, Linux only). .TP .B \fB\-L\fP Local IP address (optional). .TP .B \fB\-m\fP Number of clients (Default: 1, 2 or 4, depending on options). .TP .B \fB\-e\fP Peer address. .TP .B \fB\-r\fP Peer port (Default: 3480). .TP .B \fB\-z\fP Per\-session packet interval in milliseconds (Default: 20). .TP .B \fB\-u\fP STUN/TURN user name. .TP .B \fB\-w\fP STUN/TURN user password. .TP .B \fB\-W\fP TURN REST API authentication secret. Is not compatible with \fB\-A\fP flag. .TP .B \fB\-C\fP This is the username/timestamp separator symbol (character) in TURN REST API. The default value is :. .TP .B \fB\-F\fP Cipher suite for TLS/DTLS. Default value is DEFAULT. .PP See the examples in the "examples/scripts" directory. .PP ====================================== .SS NAME \fB \fBturnutils_peer \fP\- a simple UDP\-only echo backend server. \fB .SS SYNOPSYS $ \fIturnutils_peer\fP [\fB\-v\fP] [options] .SS DESCRIPTION This application is used for the test purposes only, as a peer for the \fIturnutils_uclient\fP application. .TP .B Options with required values: .TP .B \fB\-p\fP Listening UDP port (Default: 3480). .TP .B \fB\-d\fP Listening interface device (optional) .TP .B \fB\-L\fP Listening address of \fIturnutils_peer\fP server. Multiple listening addresses can be used, IPv4 and IPv6. If no listener \fBaddress\fP(es) defined, then it listens on all IPv4 and IPv6 addresses. .TP .B \fB\-v\fP Verbose .PP ======================================== .SS NAME \fB \fBturnutils_stunclient \fP\- a basic STUN client. \fB .SS SYNOPSIS .nf .fam C $ \fIturnutils_stunclient\fP [\fIoptions\fP] .fam T .fi .fam T .fi .SS DESCRIPTION It sends a "new" STUN RFC 5389 request (over UDP) and shows the reply information. .TP .B Options with required values: .TP .B \fB\-p\fP STUN server port (Default: 3478). .TP .B \fB\-L\fP Local address to use (optional). .TP .B \fB\-f\fP Force RFC 5780 processing. .PP The \fIturnutils_stunclient\fP program checks the results of the first request, and if it finds that the STUN server supports RFC 5780 (the binding response reveals that) then the \fIturnutils_stunclient\fP makes a couple more requests with different parameters, to demonstrate the NAT discovery capabilities. .PP This utility does not support the "old" "classic" STUN protocol (RFC 3489). .PP ===================================== .SS NAME \fB \fBturnutils_rfc5769check \fP\- a utility that tests the correctness of STUN protocol implementation. \fB .SS SYNOPSIS .nf .fam C $ \fIturnutils_rfc5769check\fP .fam T .fi .fam T .fi .SS DESCRIPTION \fIturnutils_rfc5769check\fP tests the correctness of STUN protocol implementation against the test vectors predefined in RFC 5769 and prints the results of the tests on the screen. This utility is used only for the compilation check procedure, it is not copied to the installation destination. .TP .B Usage: .PP $ \fIturnutils_rfc5769check\fP .PP =================================== .SH DOCS After installation, run the command: .PP $ man \fIturnutils\fP .PP or in the project root directory: .PP $ man \fB\-M\fP man \fIturnutils\fP .PP to see the man page. .PP ===================================== .SH FILES /etc/turnserver.conf .PP /etc/turnuserdb.conf .PP /usr/local/etc/turnserver.conf .PP /usr/local/etc/turnuserdb.conf .PP ================================= .SH DIRECTORIES /usr/local/share/\fIturnserver\fP .PP /usr/local/share/doc/\fIturnserver\fP .PP /usr/local/share/examples/\fIturnserver\fP .PP =================================== .SH STANDARDS new STUN RFC 5389 .PP TURN RFC 5766 .PP TURN\-TCP extension RFC 6062 .PP TURN IPv6 extension RFC 6156 .PP STUN/TURN test vectors RFC 5769 .PP STUN NAT behavior discovery RFC 5780 .PP ==================================== .SH SEE ALSO \fIturnserver\fP, \fIturnadmin\fP .RE .PP ====================================== .SS WEB RESOURCES project page: .PP http://code.google.com/p/rfc5766\-turn\-server/ .PP Wiki page: .PP http://code.google.com/p/rfc5766\-turn\-server/wiki/Readme .PP forum: .PP https://groups.google.com/forum/?fromgroups=#!forum/turn\-server\-project\-rfc5766\-turn\-server/ .RE .PP ====================================== .SS AUTHORS Oleg Moskalenko .PP Gabor Kovesdan http://kovesdan.org/ .PP Daniel Pocock http://danielpocock.com/ .PP John Selbie (jselbie@gmail.com) .PP Lee Sylvester .PP Erik Johnston .PP Roman Lisagor .PP Vladimir Tsanev .PP Po\-sheng Lin .PP Peter Dunkley .PP Mutsutoshi Yoshimoto turnserver-3.2.3.1/man/man1/rfc5766-turn-server.1000755 001751 001751 00000000000 12315706777 023413 2turnserver.1ustar00olegoleg000000 000000 turnserver-3.2.3.1/man/man1/turnserver.1000644 001751 001751 00000117215 12315706777 017753 0ustar00olegoleg000000 000000 .\" Text automatically generated by txt2man .TH TURN 1 "28 March 2014" "" "" .SH GENERAL INFORMATION The \fBTURN Server\fP project contains the source code of a TURN server and TURN client messaging library. Also, some extra programs provided, for testing\-only purposes. .PP See the INSTALL file for the building instructions. .PP After the build, you will have the following binary images: .TP .B 1. \fIturnserver\fP: \fBTURN Server\fP relay. The compiled binary image of the \fBTURN Server\fP program is located in bin/ sub\-directory. .TP .B 2. \fIturnadmin\fP: TURN administration tool. See README.turnadmin and \fIturnadmin\fP man page. .TP .B 3. turnutils_uclient. See README.turnutils and \fIturnutils\fP man page. .TP .B 4. turnutils_peer. See README.turnutils and \fIturnutils\fP man page. .TP .B 5. turnutils_stunclient. See README.turnutils and \fIturnutils\fP man page. .TP .B 6. turnutils_rfc5769check. See README.turnutils and \fIturnutils\fP man page. .PP In the "examples/scripts" sub\-directory, you will find the examples of command lines to be used to run the programs. The scripts are meant to be run from examples/ sub\-directory, for example: .PP $ cd examples $ ./scripts/secure_relay.sh .SH RUNNING THE TURN SERVER Options note: \fIturnserver\fP has long and short option names, for most options. Some options have only long form, some options have only short form. Their syntax somewhat different, if an argument is required: .PP The short form must be used as this (for example): .PP .nf .fam C $ turnserver \-L 12.34.56.78 .fam T .fi The long form equivalent must use the "=" character: .PP .nf .fam C $ turnserver \-\-listening\-ip=12.34.56.78 .fam T .fi If this is a flag option (no argument required) then their usage are the same, for example: .PP .nf .fam C $ turnserver \-a .fam T .fi is equivalent to: .PP .nf .fam C $ turnserver \-\-lt\-cred\-mech .fam T .fi ===================================== .SS NAME \fB \fBturnserver \fP\- a TURN relay server implementation. \fB .SS SYNOPSIS .nf .fam C $ \fIturnserver\fP [\fB\-n\fP | \fB\-c\fP ] [\fIflags\fP] [ \fB\-\-userdb\fP= | \fB\-\-psql\-userdb\fP= | \fB\-\-mysql\-userdb\fP= | \fB\-\-redis\-userdb\fP= ] [\fB\-z\fP | \fB\-\-no\-auth\fP | \fB\-a\fP | \fB\-\-lt\-cred\-mech\fP ] [\fIoptions\fP] $ \fIturnserver\fP \fB\-h\fP .fam T .fi .fam T .fi .SS DESCRIPTION .TP .B Config file settings: .TP .B \fB\-n\fP Do not use configuration file, use only command line parameters. .TP .B \fB\-c\fP Configuration file name (default \- turnserver.conf). The format of config file can be seen in the supplied examples/etc/turnserver.conf example file. Long names of the \fIoptions\fP are used as the configuration items names in the file. If not an absolute path is supplied, then the file is searched in the following directories: .RS .IP \(bu 3 current directory .IP \(bu 3 current directory etc/ sub\-directory .IP \(bu 3 upper directory level etc/ .IP \(bu 3 /etc/ .IP \(bu 3 /usr/local/etc/ .IP \(bu 3 installation directory /etc .RE .TP .B User database settings: .TP .B \fB\-b\fP, \fB\-\-userdb\fP User database file name (default \- turnuserdb.conf), for long\-term credentials mechanism only. This user file database is being dynamically checked while the \fIturnserver\fP is working, and the user accounts can be changed dynamically by editing the database. .TP .B \fB\-e\fP, \fB\-\-psql\-userdb\fP User database connection string for PostgreSQL. This database can be used for long\-term and short\-term credentials mechanisms, and it can store the secret value for secret\-based timed authentication in TURN RESP API. The connection string format is like that: .RS .PP "host= dbname= user= password= connect_timeout=" (for 8.x or newer Postgres). .PP Or: .PP "postgresql://username:password@hostname:port/databasename" (for 9.x or newer Postgres). See the INSTALL file for more explanations and examples. .PP Also, see http://www.PostgreSQL.org for full PostgreSQL documentation. .RE .TP .B \fB\-M\fP, \fB\-\-mysql\-userdb\fP User database connection string for MySQL or MariaDB. This database can be used for long\-term and short\-term credentials mechanisms, and it can store the secret value for secret\-based timed authentication in TURN RESP API. The connection string format is like that: .RS .PP "host= dbname= user= password= connect_timeout=" See the INSTALL file for more explanations and examples. .PP Also, see http://www.mysql.org or http://mariadb.org for full MySQL documentation. .RE .TP .B \fB\-N\fP, \fB\-\-redis\-userdb\fP User database connection string for Redis. This database can be used for long\-term and short\-term credentials mechanisms, and it can store the secret value for secret\-based timed authentication in TURN RESP API. The connection string format is like that: .RS .PP "ip= dbname= password= connect_timeout=" See the INSTALL file for more explanations and examples. .PP Also, see http://redis.io for full Redis documentation. .RE .TP .B Flags: .TP .B \fB\-v\fP, \fB\-\-verbose\fP Moderate verbose mode. .TP .B \fB\-V\fP, \fB\-\-Verbose\fP Extra verbose mode, very annoying and not recommended. .TP .B \fB\-o\fP, \fB\-\-daemon\fP Run server as daemon. .TP .B \fB\-f\fP, \fB\-\-fingerprint\fP Use fingerprints in the TURN messages. If an incoming request contains a fingerprint, then TURN server will always add fingerprints to the messages in this session, regardless of the per\-server setting. .TP .B \fB\-a\fP, \fB\-\-lt\-cred\-mech\fP Use long\-term credentials mechanism (this one you need for WebRTC usage). This option can be used with either flat file user database or PostgreSQL DB or MySQL DB or Redis for user keys storage. .TP .B \fB\-A\fP, \fB\-\-st\-cred\-mech\fP Use the short\-term credentials mechanism. This option requires a PostgreSQL or MySQL or Redis DB for short term passwords storage. .TP .B \fB\-z\fP, \fB\-\-no\-auth\fP Do not use any credentials mechanism, allow anonymous access. Opposite to \fB\-a\fP and \fB\-A\fP \fIoptions\fP. This is default option when no authentication\-related \fIoptions\fP are set. By default, no credential mechanism is used \- any user is allowed. .TP .B \fB\-\-use\-auth\-secret\fP TURN REST API flag. Flag that sets a special WebRTC authorization option that is based upon authentication secret. The feature purpose is to support "\fBTURN Server\fP REST API" as described in the TURN REST API section below. This option uses timestamp as part of combined username: usercombo \-> "user_id:timestamp", turn user \-> usercombo, turn password \-> \fBbase64\fP(hmac(secret key, usercombo)). This allows TURN credentials to be accounted for a specific user id. If you don't have a suitable id, the timestamp alone can be used. This option is just turns on secret\-based authentication. The actual value of the secret is defined either by option static\-auth\-secret, or can be found in the turn_secret table in the database. This option can be used with long\-term credentials mechanisms only \- it does not make much sense with the short\-term mechanism. .TP .B \fB\-\-dh566\fP Use 566 bits predefined DH TLS key. Default size of the key is 1066. .TP .B \fB\-\-dh2066\fP Use 2066 bits predefined DH TLS key. Default size of the key is 1066. .TP .B \fB\-\-no\-sslv2\fP Do not allow SSLv2 protocol. .TP .B \fB\-\-no\-sslv3\fP Do not allow SSLv3 protocol. .TP .B \fB\-\-no\-tlsv1\fP Do not allow TLSv1 protocol. .TP .B \fB\-\-no\-tlsv1_1\fP Do not allow TLSv1.1 protocol. .TP .B \fB\-\-no\-tlsv1_2\fP Do not allow TLSv1.2 protocol. .TP .B \fB\-\-no\-udp\fP Do not start UDP client listeners. .TP .B \fB\-\-no\-tcp\fP Do not start TCP client listeners. .TP .B \fB\-\-no\-tls\fP Do not start TLS client listeners. .TP .B \fB\-\-no\-dtls\fP Do not start DTLS client listeners. .TP .B \fB\-\-no\-udp\-relay\fP Do not allow UDP relay endpoints defined in RFC 5766, use only TCP relay endpoints as defined in RFC 6062. .TP .B \fB\-\-no\-tcp\-relay\fP Do not allow TCP relay endpoints defined in RFC 6062, use only UDP relay endpoints as defined in RFC 5766. .TP .B \fB\-\-stale\-nonce\fP Use extra security with nonce value having limited lifetime (600 secs). .TP .B \fB\-\-no\-stdout\-log\fP Flag to prevent stdout log messages. By default, all log messages are going to both stdout and to the configured log file. With this option everything will be going to the log file only (unless the log file itself is stdout). .TP .B \fB\-\-syslog\fP With this flag, all log will be redirected to the system log (syslog). .TP .B \fB\-\-secure\-stun\fP Require authentication of the STUN Binding request. By default, the clients are allowed anonymous access to the STUN Binding functionality. .TP .B \fB\-S\fP, \fB\-\-stun\-only\fP Run as STUN server only, all TURN requests will be ignored. Option to suppress TURN functionality, only STUN requests will be processed. .TP .B \fB\-\-no\-stun\fP Run as TURN server only, all STUN requests will be ignored. Option to suppress STUN functionality, only TURN requests will be processed. .TP .B \fB\-\-no\-loopback\-peers\fP Disallow peers on the loopback addresses (127.x.x.x and ::1). .TP .B \fB\-\-no\-multicast\-peers\fP Disallow peers on well\-known broadcast addresses (224.0.0.0 and above, and FFXX:*). .TP .B \fB\-\-sha256\fP Require SHA256 digest function to be used for the message integrity. By default, the server uses SHA1 hashes. With this option, the server requires the stronger SHA256 hashes. The client application must support SHA256 hash function if this option is used. If the server obtains a message from the client with a weaker (SHA1) hash function then the server returns error code 426. .TP .B \fB\-\-mobility\fP Mobility with ICE (MICE) specs support. .TP .B \fB\-\-no\-cli\fP Turn OFF the CLI support. By default it is always ON. See also \fIoptions\fP \fB\-\-cli\-ip\fP and \fB\-\-cli\-port\fP. .TP .B \fB\-\-server\-relay\fP Server relay. NON\-STANDARD AND DANGEROUS OPTION. Only for those applications when we want to run server applications on the relay endpoints. This option eliminates the IP permissions check on the packets incoming to the relay endpoints. See http://tools.ietf.org/search/rfc5766#section\-17.2.3 . .TP .B \fB\-\-udp\-self\-balance\fP (recommended for older Linuxes only) Automatically balance UDP traffic over auxiliary servers (if configured). The load balancing is using the ALTERNATE\-SERVER mechanism. The TURN client must support 300 ALTERNATE\-SERVER response for this functionality. .TP .B \fB\-h\fP Help. .TP .B Options with required values: .TP .B \fB\-d\fP, \fB\-\-listening\-device\fP Listener interface device. (NOT RECOMMENDED. Optional functionality, Linux only). The \fIturnserver\fP process must have root privileges to bind the listening endpoint to a device. If \fIturnserver\fP must run as a process without root privileges, then just do not use this setting. .TP .B \fB\-L\fP, \fB\-\-listening\-ip\fP Listener IP address of relay server. Multiple listeners can be specified, for example: \fB\-L\fP ip1 \fB\-L\fP ip2 \fB\-L\fP ip3 If no \fBIP\fP(s) specified, then all IPv4 and IPv6 system IPs will be used for listening. The same \fBip\fP(s) can be used as both listening and relay \fBip\fP(s). .TP .B \fB\-p\fP, \fB\-\-listening\-port\fP TURN listener port for UDP and TCP listeners (Default: 3478). Note: actually, TLS & DTLS sessions can connect to the "plain" TCP & UDP \fBport\fP(s), too \- if allowed by configuration. .TP .B \fB\-\-tls\-listening\-port\fP TURN listener port for TLS and DTLS listeners (Default: 5349). Note: actually, "plain" TCP & UDP sessions can connect to the TLS & DTLS \fBport\fP(s), too \- if allowed by configuration. The TURN server "automatically" recognizes the type of traffic. Actually, two listening endpoints (the "plain" one and the "tls" one) are equivalent in terms of functionality; but we keep both endpoints to satisfy the RFC 5766 specs. For secure TCP connections, we currently support SSL version 3 and TLS versions 1.0, 1.1, 1.2. SSL2 "encapsulation mode" is also supported. For secure UDP connections, we support DTLS version 1. .TP .B \fB\-\-alt\-listening\-port\fP Alternative listening port for UDP and TCP listeners; default (or zero) value means "listening port plus one". This is needed for STUN CHANGE_REQUEST \- in RFC 5780 sense or in old RFC 3489 sense \- for NAT behavior discovery). The \fBTURN Server\fP supports CHANGE_REQUEST only if it is started with more than one listening IP address of the same family (IPv4 or IPv6). The CHANGE_REQUEST is only supported by UDP protocol, other protocols are listening on that endpoint only for "symmetry". .TP .B \fB\-\-alt\-tls\-listening\-port\fP Alternative listening port for TLS and DTLS protocols. Default (or zero) value means "TLS listening port plus one". .TP .B \fB\-\-aux\-server\fP Auxiliary STUN/TURN server listening endpoint. Aux servers have almost full TURN and STUN functionality. The (minor) limitations are: .RS .IP 1) 4 Auxiliary servers do not have alternative ports and they do not support STUN RFC 5780 functionality (CHANGE REQUEST). .IP 2) 4 Auxiliary servers also are never returning ALTERNATIVE\-SERVER reply. .RE .PP Valid formats are 1.2.3.4:5555 for IPv4 and [1:2::3:4]:5555 for IPv6. There may be multiple aux\-server \fIoptions\fP, each will be used for listening to client requests. .TP .B \fB\-i\fP, \fB\-\-relay\-device\fP Relay interface device for relay sockets (NOT RECOMMENDED. Optional, Linux only). .TP .B \fB\-E\fP, \fB\-\-relay\-ip\fP Relay address (the local IP address that will be used to relay the packets to the peer). Multiple relay addresses may be used: \fB\-E\fP ip1 \fB\-E\fP ip2 \fB\-E\fP ip3 The same \fBIP\fP(s) can be used as both listening \fBIP\fP(s) and relay \fBIP\fP(s). If no relay \fBIP\fP(s) specified, then the \fIturnserver\fP will apply the default policy: it will decide itself which relay addresses to be used, and it will always be using the client socket IP address as the relay IP address of the TURN session (if the requested relay address family is the same as the family of the client socket). .TP .B \fB\-X\fP, \fB\-\-external\-ip\fP \fBTURN Server\fP public/private address mapping, if the server is behind NAT. In that situation, if a \fB\-X\fP is used in form "\fB\-X\fP " then that ip will be reported as relay IP address of all allocations. This scenario works only in a simple case when one single relay address is be used, and no CHANGE_REQUEST functionality is required. That single relay address must be mapped by NAT to the 'external' IP. The "external\-ip" value, if not empty, is returned in XOR\-RELAYED\-ADDRESS field. For that 'external' IP, NAT must forward ports directly (relayed port 12345 must be always mapped to the same 'external' port 12345). In more complex case when more than one IP address is involved, that option must be used several times, each entry must have form "\fB\-X\fP ", to map all involved addresses. CHANGE_REQUEST (RFC5780 or RFC3489) NAT discovery STUN functionality will work correctly, if the addresses are mapped properly, even when the TURN server itself is behind A NAT. By default, this value is empty, and no address mapping is used. .TP .B \fB\-m\fP, \fB\-\-relay\-threads\fP Number of relay threads to handle the established connections (in addition to authentication thread and the listener thread). If set to 0 then application runs relay process in a single thread, in the same thread with the listener process (the authentication thread will still be a separate thread). In older systems (before Linux kernel 3.9), the number of UDP threads is always one threads per network listening endpoint \- unless "\fB\-m\fP 0" or "\fB\-m\fP 1" is set. .TP .B \fB\-\-min\-port\fP Lower bound of the UDP port range for relay endpoints allocation. Default value is 49152, according to RFC 5766. .TP .B \fB\-\-max\-port\fP Upper bound of the UDP port range for relay endpoints allocation. Default value is 65535, according to RFC 5766. .TP .B \fB\-u\fP, \fB\-\-user\fP Long\-term security mechanism credentials user account, in the column\-separated form username:key. Multiple user accounts may used in the command line. The key is either the user password, or the key is generated by \fIturnadmin\fP command. In the second case, the key must be prepended with 0x symbols. The key is calculated over the user name, the realm, and the user password. This setting may not be used with TURN REST API or with short\-term credentials mechanism. .TP .B \fB\-r\fP, \fB\-\-realm\fP Realm to be used for all users. Must be used with long\-term credentials mechanism or with TURN REST API. .TP .B \fB\-C\fP, \fB\-\-rest\-api\-separator\fP This is the username/timestamp separator symbol (character) in TURN REST API. The default value is :. .TP .B \fB\-q\fP, \fB\-\-user\-quota\fP Per\-user allocations quota: how many concurrent allocations a user can create. .TP .B \fB\-Q\fP, \fB\-\-total\-quota\fP Total allocations quota: global limit on concurrent allocations. .TP .B \fB\-\-static\-auth\-secret\fP Static authentication secret value (a string) for TURN REST API only. If not set, then the turn server will try to use the dynamic value in turn_secret table in user database (if present). The database\-stored value can be changed on\-the\-fly by a separate program, so this is why that other mode is dynamic. Multiple shared secrets can be used (both in the database and in the "static" fashion). .TP .B \fB\-s\fP, \fB\-\-max\-bps\fP Max bytes\-per\-second bandwidth a TURN session is allowed to handle (input and output network streams are treated separately). Anything above that limit will be dropped or temporary suppressed (within the available buffer limits). .TP .B \fB\-\-cert\fP Certificate file, PEM format. Same file search rules applied as for the configuration file. If both \fB\-\-no\-tls\fP and \fB\-\-no\-dtls\fP \fIoptions\fP are specified, then this parameter is not needed. Default value is turn_server_cert.pem. .TP .B \fB\-\-pkey\fP Private key file, PEM format. Same file search rules applied as for the configuration file. If both \fB\-\-no\-tls\fP and \fB\-\-no\-dtls\fP \fIoptions\fP are specified, then this parameter is not needed. Default value is turn_server_pkey.pem. .TP .B \fB\-\-pkey\-pwd\fP If the private key file is encrypted, then this password to be used. .TP .B \fB\-\-cipher\-list\fP Allowed OpenSSL cipher list for TLS/DTLS connections. Default value is "DEFAULT". .TP .B \fB\-\-CA\-file\fP CA file in OpenSSL format. Forces TURN server to verify the client SSL certificates. By default, no CA is set and no client certificate check is performed. .TP .B \fB\-\-ec\-curve\-name\fP Curve name for EC ciphers, if supported by OpenSSL library (TLS and DTLS). The default value is prime256v1. .TP .B \fB\-\-dh\-file\fP Use custom DH TLS key, stored in PEM format in the file. Flags \fB\-\-dh566\fP and \fB\-\-dh2066\fP are ignored when the DH key is taken from a file. .TP .B \fB\-l\fP, \fB\-\-log\-file\fP Option to set the full path name of the log file. By default, the \fIturnserver\fP tries to open a log file in /var/log/\fIturnserver\fP, /var/log, /var/tmp, /tmp and . (current) directories (which file open operation succeeds first that file will be used). With this option you can set the definite log file name. The special names are "stdout" and "\-" \- they will force everything to the stdout. Also, "syslog" name will redirect everything into the system log (syslog), as if the option "\fB\-\-syslog\fP" was set. .TP .B \fB\-\-alternate\-server\fP Option to set the "redirection" mode. The value of this option will be the address of the alternate server for UDP & TCP service in form of [:]. The server will send this value in the attribute ALTERNATE\-SERVER, with error 300, on ALLOCATE request, to the client. Client will receive only values with the same address family as the client network endpoint address family. See RFC 5389 and RFC 5766 for ALTERNATE\-SERVER functionality description. The client must use the obtained value for subsequent TURN communications. If more than one \fB\-\-alternate\-server\fP \fIoptions\fP are provided, then the functionality can be more accurately described as "load\-balancing" than a mere "redirection". If the port number is omitted, then the default port number 3478 for the UDP/TCP protocols will be used. Colon (:) characters in IPv6 addresses may conflict with the syntax of the option. To alleviate this conflict, literal IPv6 addresses are enclosed in square brackets in such resource identifiers, for example: [2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478 . Multiple alternate servers can be set. They will be used in the round\-robin manner. All servers in the pool are considered of equal weight and the load will be distributed equally. For example, if we have 4 alternate servers, then each server will receive 25% of ALLOCATE requests. An alternate TURN server address can be used more than one time with the alternate\-server option, so this can emulate "weighting" of the servers. .TP .B \fB\-\-tls\-alternate\-server\fP Option to set alternative server for TLS & DTLS services in form of :. If the port number is omitted, then the default port number 5349 for the TLS/DTLS protocols will be used. See the previous option for the functionality description. .TP .B \fB\-O\fP, \fB\-\-redis\-statsdb\fP Redis status and statistics database connection string, if used (default \- empty, no Redis stats DB used). This database keeps allocations status information, and it can be also used for publishing and delivering traffic and allocation event notifications. This database option can be used independently of \fB\-\-redis\-userdb\fP option, and actually Redis can be used for status/statistics and MySQL or PostgreSQL can be used for the user database. The connection string has the same parameters as redis\-userdb connection string. .TP .B \fB\-\-max\-allocate\-timeout\fP Max time, in seconds, allowed for full allocation establishment. Default is 60 seconds. .PP \fB\-\-denied\-peer\-ip\fP= .PP \fB\-\-allowed\-peer\-ip\fP= Options to ban or allow specific ip addresses or ranges of ip addresses. If an ip address is specified as both allowed and denied, then the ip address is considered to be allowed. This is useful when you wish to ban a range of ip addresses, except for a few specific ips within that range. This can be used when you do not want users of the turn server to be able to access machines reachable by the turn server, but would otherwise be unreachable from the internet (e.g. when the turn server is sitting behind a NAT). The 'white" and "black" peer IP ranges can also be dynamically changed in the database. The allowed/denied addresses (white/black lists) rules are very simple: .RS .IP 1) 4 If there is no rule for an address, then it is allowed; .IP 2) 4 If there is an "allowed" rule that fits the address then it is allowed \- no matter what; .IP 3) 4 If there is no "allowed" rule that fits the address, and if there is a "denied" rule that fits the address, then it is denied. .RE .TP .B \fB\-\-pidfile\fP File name to store the pid of the process. Default is /var/run/turnserver.pid (if superuser account is used) or /var/tmp/turnserver.pid . .TP .B \fB\-\-proc\-user\fP User name to run the process. After the initialization, the \fIturnserver\fP process will make an attempt to change the current user ID to that user. .TP .B \fB\-\-proc\-group\fP Group name to run the process. After the initialization, the \fIturnserver\fP process will make an attempt to change the current group ID to that group. .TP .B \fB\-\-cli\-ip\fP Local system IP address to be used for CLI management interface. The \fIturnserver\fP process can be accessed for management with telnet, at this IP address and on the CLI port (see the next parameter). Default value is 127.0.0.1. You can use telnet or putty (in telnet mode) to access the CLI management interface. .TP .B \fB\-\-cli\-port\fP CLI management interface listening port. Default is 5766. .TP .B \fB\-\-cli\-password\fP CLI access password. Default is empty (no password). .TP .B \fB\-\-cli\-max\-output\-sessions\fP Maximum number of output sessions in ps CLI command. This value can be changed on\-the\-fly in CLI. The default value is 256. .TP .B \fB\-\-ne\fP=[1|2|3] Set network engine type for the process (for internal purposes). .PP ================================== .SH LOAD BALANCE AND PERFORMANCE TUNING This topic is covered in the wiki page: .PP http://code.google.com/p/rfc5766\-turn\-server/wiki/turn_performance_and_load_balance .PP =================================== .SH WEBRTC USAGE This is a set of notes for the WebRTC users: .IP 1) 4 WebRTC uses long\-term authentication mechanism, so you have to use \fB\-a\fP option (or \fB\-\-lt\-cred\-mech\fP). WebRTC relaying will not work with anonymous access or with short\-term authentication. With \fB\-a\fP option, do not forget to set the realm (\fB\-r\fP option). You will also have to set up the user accounts, for that you have a number of \fIoptions\fP: .PP .nf .fam C a) command\-line options (\-u). b) userdb config file. c) a database table (PostgreSQL or MySQL). You will have to set keys with turnadmin utility (see docs and wiki for turnadmin). You cannot use open passwords in the database. d) Redis key/value pair(s), if Redis is used. You key use either keys or open passwords with Redis; see turndb/testredisdbsetup.sh file. e) You also can use the TURN REST API. You will need shared secret(s) set either through the command line option, or through the config file, or through the database table or Redis key/value pairs. .fam T .fi .IP 2) 4 Usually WebRTC uses fingerprinting (\fB\-f\fP). .IP 3) 4 \fB\-v\fP option may be nice to see the connected clients. .IP 4) 4 \fB\-X\fP is needed if you are running your TURN server behind a NAT. .IP 5) 4 \fB\-\-min\-port\fP and \fB\-\-max\-port\fP may be needed if you want to limit the relay endpoints ports number range. .PP =================================== .SH TURN REST API In WebRTC, the browser obtains the TURN connection information from the web server. This information is a secure information \- because it contains the necessary TURN credentials. As these credentials are transmitted over the public networks, we have a potential security breach. .PP If we have to transmit a valuable information over the public network, then this information has to have a limited lifetime. Then the guy who obtains this information without permission will be able to perform only limited damage. .PP This is how the idea of TURN REST API \- time\-limited TURN credentials \- appeared. This security mechanism is based upon the long\-term credentials mechanism. The main idea of the REST API is that the web server provides the credentials to the client, but those credentials can be used only limited time by an application that has to create a TURN server connection. .PP The "classic" long\-term credentials mechanism (LTCM) is described here: .PP http://tools.ietf.org/html/rfc5389#section\-10.2 http://tools.ietf.org/html/rfc5389#section\-15.4 .PP For authentication, each user must know two things: the username and the password. The nonce and the realm values are supplied by the TURN server. But LTCM is not saying anything about the nature and about the persistence of the username and of the password; and this is used by the REST API. .PP In the TURN REST API, there is no persistent passwords for users. A user has just the username. The password is always temporary, and it is generated by the web server on\-demand, when the user accesses the WebRTC page. And, actually, a temporary one\-time session only, username is provided to the user, too. .PP The temporary user is generated as: .PP temporary\-username="username" + ":" + "timestamp" .PP where username is the persistent user name, and the timestamp format is just seconds sinse 1970 \- the same value as \fBtime\fP(NULL) function returns. .PP The temporary password is obtained as HMAC\-SHA1 function over the temporary username, with shared secret as the HMAC key, and then the result is encoded: .PP temporary\-password = \fBbase64_encode\fP(hmac\-sha1(shared\-secret, temporary\-username)) .PP Both the TURN server and the web server know the same shared secret. How the shared secret is distributed among the involved entities is left to the WebRTC deployment details \- this is beyond the scope of the TURN REST API. .PP So, a timestamp is used for the temporary password calculation, and this timestamp can be retrieved from the temporary username. This information is valuable, but only temporary, while the timestamp is not expired. Without knowledge of the shared secret, a new temporary password cannot be generated. .PP This is all formally described in Justin's Uberti TURN REST API document that can be obtained following the link "TURN REST API" in the \fBTURN Server\fP project's page http://code.google.com/p/rfc5766\-turn\-server/. .PP Once the temporary username and password are obtained by the client (browser) application, then the rest is just 'classic" long\-term credentials mechanism. For developers, we are going to describe it step\-by\-step below: .RS .IP \(bu 3 a new TURN client sends a request command to the TURN server. .IP \(bu 3 TURN server sees that this is a new client and the message is not authenticated. .IP \(bu 3 the TURN server generates a random nonce string, and return the error 401 to the client, with nonce and realm included. .IP \(bu 3 the client sees the 401 error and it extracts two values from the error response: the nonce and the realm. .IP \(bu 3 the client uses username, realm and password to produce a key: .PP .nf .fam C key = MD5(username ":" realm ":" SASLprep(password)) .fam T .fi (SASLprep is described here: http://tools.ietf.org/html/rfc4013) .IP \(bu 3 the client forms a new request, adds username, realm and nonce to the request. Then, the client calculates and adds the integrity field to the request. This is the trickiest part of the process, and it is described in the end of section 15.4: http://tools.ietf.org/html/rfc5389#section\-15.4 .IP \(bu 3 the client, optionally, adds the fingerprint field. This may be also a tricky procedure, described in section 15.5 of the same document. WebRTC usually uses fingerprinted TURN messages. .IP \(bu 3 the TURN server receives the request, reads the username. .IP \(bu 3 then the TURN server checks that the nonce and the realm in the request are the valid ones. .IP \(bu 3 then the TURN server calculates the key. .IP \(bu 3 then the TURN server calculates the integrity field. .IP \(bu 3 then the TURN server compares the calculated integrity field with the received one \- they must be the same. If the integrity fields differ, then the request is rejected. .RE .PP In subsequent communications, the client may go with exactly the same sequence, but for optimization usually the client, having already information about realm and nonce, pre\-calculates the integrity string for each request, so that the 401 error response becomes unnecessary. The TURN server may use "\fB\-\-stale\-nonce\fP" option for extra security: in some time, the nonce expires and the client will obtain 438 error response with the new nonce, and the client will have to start using the new nonce. .PP In subsequent communications, the sever and the client will always assume the same password \- the original password becomes the session parameter and is never expiring. So the password is not changing while the session is valid and unexpired. So, if the session is properly maintained, it may go forever, even if the user password has been already changed (in the database). The session simply is using the old password. Once the session got disconnected, the client will have to use the new password to re\-connect (if the password has been changed). .PP An example when a new shared secret is generated every hour by the TURN server box and then supplied to the web server, remotely, is provided in the script examples/scripts/restapi/shared_secret_maintainer.pl . .PP A very important thing is that the nonce must be totally random and it must be different for different clients and different sessions. .PP =================================== .SH DATABASES For the user database, the \fIturnserver\fP has the following \fIoptions\fP: .IP 1) 4 Users can be set in the command line, with multiple \fB\-u\fP or \fB\-\-user\fP \fIoptions\fP. Obviously, only a few users can be set that way, and their credentials are fixed for the \fIturnserver\fP process lifetime. .IP 2) 4 Users can be set in turnusers.conf flat file DB. The \fIturnserver\fP process periodically re\-reads this file, so the user accounts may be changed while the \fIturnserver\fP is running. But still a relatively small (up to a hundred ?) number of users can be handled that way. .IP 3) 4 Users can be stored in PostgreSQL database, if the \fIturnserver\fP was compiled with PostgreSQL support. Each time \fIturnserver\fP checks user credentials, it reads the database (asynchronously, of course, so that the current flow of packets is not delayed in any way), so any change in the database content is immediately visible by the \fIturnserver\fP. This is the way if you need the best scalability. The schema for the database can be found in schema.sql file. For long\-term credentials, you have to set the "keys" for the users; the "keys" are generated by the \fIturnadmin\fP utility. For the key generation, you need username, password and the realm. All users in the database must use the same realm value; if down the road you will decide to change the realm name, then you will have to re\-generate all user keys (that can be done in a batch script). If you are using short\-term credentials, then you use open passwords in the database; you will have to make sure that nobody can access the database outside of the TURN server box. .IP 4) 4 The same is true for MySQL database. The same schema file is applicable. The same considerations are applicable. .IP 5) 4 The same is true for the Redis database, but the Redis database has aa different schema \- it can be found (in the form of explanation) in schema.userdb.redis. Also, in Redis you can store both "keys" and open passwords (for long term credentials) \- the "open password" option is less secure but more convenient for low\-security environments. For short\-term credentials, you will use open passwords only. See the file turndb/testredisdbsetup.sh as an example. .IP 6) 4 Of course, the \fIturnserver\fP can be used in non\-secure mode, when users are allowed to establish sessions anonymously. But in most cases (like WebRTC) that will not work. .PP For the status and statistics database, there are two choices: .IP 1) 4 The simplest choice is not to use it. Do not set \fB\-\-redis\-statsdb\fP option, and this functionality will be simply ignored. .IP 2) 4 If you choose to use it, then set the \fB\-\-redis\-statsdb\fP option. This may be the same database as in \fB\-\-redis\-userdb\fP option, or it may be a different database. You may want to use different database for security or convenience reasons. Also, you can use different database management systems for the user database and for the ststus and statistics database. For example, you can use MySQL as the user database, and you can use redis for the statistics. Or you can use Redis for both. .PP So, we have 6 choices for the user management, and 2 choices for the statistics management. These two are totally independent. So, you have overall 6*2=12 ways to handle persistent information, choose any for your convenience. .PP You do not have to handle the database information "manually" \- the \fIturnadmin\fP program can handle everything for you. For PostgreSQL and MySQL you will just have to create an empty database with schema.sql SQL script. With Redis, you do not have to do even that \- just run \fIturnadmin\fP and it will set the users for you (see the \fIturnadmin\fP manuals). .PP ================================= .SH LIBRARIES In the lib/ sub\-directory the build process will create TURN client messaging library. In the include/ sub\-directory, the necessary include files will be placed. The C++ wrapper for the messaging functionality is located in TurnMsgLib.h header. An example of C++ code can be found in stunclient.c file. .PP ================================= .SH DOCS After installation, run the command: .PP $ man \fIturnserver\fP .PP or in the project root directory: .PP $ man \fB\-M\fP man \fIturnserver\fP .PP to see the man page. .PP In the docs/html subdirectory of the original archive tree, you will find the client library reference. After the installation, it will be placed in PREFIX/share/doc/\fIturnserver\fP/html. .PP ================================= .SH LOGS When the \fBTURN Server\fP starts, it makes efforts to create a log file turn_.log in the following directories: .RS .IP \(bu 3 /var/log .IP \(bu 3 /log/ .IP \(bu 3 /var/tmp .IP \(bu 3 /tmp .IP \(bu 3 current directory .RE .PP If all efforts failed (due to the system permission settings) then all log messages are sent only to the standard output of the process. .PP This behavior can be controlled by \fB\-\-log\-file\fP, \fB\-\-syslog\fP and \fB\-\-no\-stdout\-log\fP \fIoptions\fP. .PP ================================= .SH TELNET CLI The \fIturnserver\fP process provides a telnet CLI access as statistics and basic management interface. By default, the \fIturnserver\fP starts a telnet CLI listener on IP 127.0.0.1 and port 5766. That can be changed by the command\-cline \fIoptions\fP of the \fIturnserver\fP process (see \fB\-\-cli\-ip\fP and \fB\-\-cli\-port\fP \fIoptions\fP). The full list of telnet CLI commands is provided in "help" command output in the telnet CLI. .PP ================================= .SH CLUSTERS \fBTURN Server\fP can be a part of the cluster installation. But, to support the "even port" functionality (RTP/RTCP streams pairs) the client requests from a particular IP must be delivered to the same \fBTURN Server\fP instance, so it requires some networking setup massaging for the cluster. The reason is that the RTP and RTCP relaying endpoints must be allocated on the same relay IP. It would be possible to design a scheme with the application\-level requests forwarding (and we may do that later) but it would affect the performance. .PP ================================= .SH FILES /etc/turnserver.conf .PP /etc/turnuserdb.conf .PP /usr/local/etc/turnserver.conf .PP /usr/local/etc/turnuserdb.conf .PP ================================= .SH DIRECTORIES /usr/local/share/\fIturnserver\fP .PP /usr/local/share/doc/\fIturnserver\fP .PP /usr/local/share/examples/\fIturnserver\fP .PP ================================= .SH STANDARDS obsolete STUN RFC 3489 .PP new STUN RFC 5389 .PP TURN RFC 5766 .PP TURN\-TCP extension RFC 6062 .PP TURN IPv6 extension RFC 6156 .PP STUN/TURN test vectors RFC 5769 .PP STUN NAT behavior discovery RFC 5780 .PP ================================= .SH SEE ALSO \fIturnadmin\fP, \fIturnutils\fP .RE .PP ====================================== .SS WEB RESOURCES project page: .PP http://code.google.com/p/rfc5766\-turn\-server/ .PP Wiki page: .PP http://code.google.com/p/rfc5766\-turn\-server/wiki/Readme .PP forum: .PP https://groups.google.com/forum/?fromgroups=#!forum/turn\-server\-project\-rfc5766\-turn\-server/ .RE .PP ====================================== .SS AUTHORS Oleg Moskalenko .PP Gabor Kovesdan http://kovesdan.org/ .PP Daniel Pocock http://danielpocock.com/ .PP John Selbie (jselbie@gmail.com) .PP Lee Sylvester .PP Erik Johnston .PP Roman Lisagor .PP Vladimir Tsanev .PP Po\-sheng Lin .PP Peter Dunkley .PP Mutsutoshi Yoshimoto turnserver-3.2.3.1/man/man1/turnutils_uclient.1000755 001751 001751 00000000000 12315706777 023445 2turnutils.1ustar00olegoleg000000 000000 turnserver-3.2.3.1/man/man1/turnadmin.1000644 001751 001751 00000014222 12315706777 017527 0ustar00olegoleg000000 000000 .\" Text automatically generated by txt2man .TH TURN 1 "28 March 2014" "" "" .SH GENERAL INFORMATION \fIturnadmin\fP is a TURN administration tool. This tool can be used to manage the user accounts (add/remove users, generate TURN keys for the users). For security reasons, we do not recommend storing passwords openly. The better option is to use pre\-processed "keys" which are then used for authentication. These keys are generated by \fIturnadmin\fP. Turnadmin is a link to \fIturnserver\fP binary, but \fIturnadmin\fP performs different functions. .PP Options note: \fIturnadmin\fP has long and short option names, for most options. Some options have only long form, some options have only short form. Their syntax somewhat different, if an argument is required: .PP The short form must be used as this (for example): .PP .nf .fam C $ turnadmin \-u \.\.\. .fam T .fi The long form equivalent must use the "=" character: .PP .nf .fam C $ turnadmin \-\-user= \.\.\. .fam T .fi If this is a flag option (no argument required) then their usage are the same, for example: .PP .nf .fam C $ turnadmin \-k \.\.\. .fam T .fi is equivalent to: .PP .nf .fam C $ turnadmin \-\-key \.\.\. .fam T .fi ===================================== .SS NAME \fB \fBturnadmin \fP\- a TURN relay administration tool. \fB .SS SYNOPSIS $ \fIturnadmin\fP [command] [options] .PP $ \fIturnadmin\fP [ \fB\-h\fP | \fB\-\-help\fP] .SS DESCRIPTION .TP .B Commands: .TP .B \fB\-k\fP, \fB\-\-key\fP Generate key for a long\-term credentials mechanism user. .TP .B \fB\-a\fP, \fB\-\-add\fP Add or update a long\-term user. .TP .B \fB\-A\fP, \fB\-\-add\-st\fP Add or update a short\-term credentials mechanism user. .TP .B \fB\-d\fP, \fB\-\-delete\fP Delete a long\-term user. .TP .B \fB\-D\fP, \fB\-\-delete\-st\fP Delete a short\-term user. .TP .B \fB\-l\fP, \fB\-\-list\fP List long\-term users in the database. .TP .B \fB\-L\fP, \fB\-\-list\-st\fP List short\-term users in the database. .PP \fB\-s\fP, \fB\-\-set\-secret\fP= Add shared secret for TURN RESP API .TP .B \fB\-S\fP, \fB\-\-show\-secret\fP Show stored shared secrets for TURN REST API .PP \fB\-X\fP, \fB\-\-delete\-secret\fP= Delete a shared secret. .TP .B \fB\-\-delete\-all_secrets\fP Delete all shared secrets for REST API. .PP NOTE: if you are using the flat file for the user database, then you will have to use a text editor to set or show the shared secrets. .TP .B Options with required values: .TP .B \fB\-b\fP, \fB\-\-userdb\fP File\-based user database file name (default \- turnuserdb.conf). See the \fB\-\-userdb\fP option in the \fIturnserver\fP section. .TP .B \fB\-e\fP, \fB\-\-psql\-userdb\fP PostgreSQL user database connection string. See the \fB\-\-psql\-userdb\fP option in the \fIturnserver\fP section. .TP .B \fB\-M\fP, \fB\-\-mysql\-userdb\fP MySQL user database connection string. See the \fB\-\-mysql\-userdb\fP option in the \fIturnserver\fP section. .TP .B \fB\-N\fP, \fB\-\-redis\-userdb\fP Redis user database connection string. See the \fB\-\-redis\-userdb\fP option in the \fIturnserver\fP section. .TP .B \fB\-u\fP, \fB\-\-user\fP User name. .TP .B \fB\-r\fP, \fB\-\-realm\fP Realm, for long\-term credentials mechanism only. .TP .B \fB\-p\fP, \fB\-\-password\fP Password. .TP .B \fB\-H\fP, \fB\-\-sha256\fP Use SHA256 keys and message integrity. By default, HMAC\-SHA1 is used for the message digest calculation, and MD5 is used for the key storage encryption. .TP .B \fB\-h\fP, \fB\-\-help\fP Help. .TP .B Generate a key: .PP $ \fIturnadmin\fP \fB\-k\fP \fB\-u\fP \fB\-r\fP \fB\-p\fP .PP Add/update a user (and realm) in the userdb file or in the database: .PP $ \fIturnadmin\fP \fB\-a\fP [\fB\-b\fP | \fB\-e\fP | \fB\-M\fP | \fB\-N\fP ] \fB\-u\fP \fB\-r\fP \fB\-p\fP .PP Delete a user from the userdb file or from the database: .PP $ \fIturnadmin\fP \fB\-d\fP [\fB\-b\fP | \fB\-e\fP | \fB\-M\fP | \fB\-N\fP ] \fB\-u\fP .PP List all long\-term users in MySQL database: .PP $ \fIturnadmin\fP \fB\-l\fP \fB\-\-mysql\-userdb\fP="" .PP List all short\-term users in Redis database: .PP $ \fIturnadmin\fP \fB\-L\fP \fB\-\-redis\-userdb\fP="" .PP Set secret in MySQL database: .PP $ \fIturnadmin\fP \fB\-s\fP \fB\-\-mysql\-userdb\fP="" .PP Show secret stored in PostgreSQL database: .PP $ \fIturnadmin\fP \fB\-S\fP \fB\-\-psql\-userdb\fP="" .TP .B Help: .PP $ \fIturnadmin\fP \fB\-h\fP .PP ======================================= .SS DOCS After installation, run the command: .PP $ man \fIturnadmin\fP .PP or in the project root directory: .PP $ man \fB\-M\fP man \fIturnadmin\fP .PP to see the man page. .PP ===================================== .SS FILES /etc/turnserver.conf .PP /etc/turnuserdb.conf .PP /usr/local/etc/turnserver.conf .PP /usr/local/etc/turnuserdb.conf .PP ===================================== .SS DIRECTORIES /usr/local/share/\fIturnserver\fP .PP /usr/local/share/doc/\fIturnserver\fP .PP /usr/local/share/examples/\fIturnserver\fP .PP ====================================== .SS SEE ALSO \fIturnserver\fP, \fIturnutils\fP .RE .PP ====================================== .SS WEB RESOURCES project page: .PP http://code.google.com/p/rfc5766\-turn\-server/ .PP Wiki page: .PP http://code.google.com/p/rfc5766\-turn\-server/wiki/Readme .PP forum: .PP https://groups.google.com/forum/?fromgroups=#!forum/turn\-server\-project\-rfc5766\-turn\-server/ .RE .PP ====================================== .SS AUTHORS Oleg Moskalenko .PP Gabor Kovesdan http://kovesdan.org/ .PP Daniel Pocock http://danielpocock.com/ .PP John Selbie (jselbie@gmail.com) .PP Lee Sylvester .PP Erik Johnston .PP Roman Lisagor .PP Vladimir Tsanev .PP Po\-sheng Lin .PP Peter Dunkley .PP Mutsutoshi Yoshimoto turnserver-3.2.3.1/src/client/000755 001751 001751 00000000000 12315706777 016101 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/src/ns_turn_defs.h000644 001751 001751 00000011256 12315706777 017472 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __IOADEFS__ #define __IOADEFS__ #define TURN_SERVER_VERSION "3.2.3.1" #define TURN_SERVER_VERSION_NAME "Marshal West" #define TURN_SOFTWARE "Citrix-"TURN_SERVER_VERSION" '"TURN_SERVER_VERSION_NAME"'" #if (defined(__unix__) || defined(unix)) && !defined(USG) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /////////////////////////////////////////// /* NS types: */ #define s08bits char #define s16bits int16_t #define s32bits int32_t #define s64bits int64_t #define u08bits unsigned char #define u16bits uint16_t #define u32bits uint32_t #define u64bits uint64_t #define ns_bcopy(src,dst,sz) bcopy((src),(dst),(sz)) #define ns_bzero(ptr,sz) bzero((ptr),(sz)) #define nswap16(s) ntohs(s) #define nswap32(ul) ntohl(ul) #define nswap64(ull) ioa_ntoh64(ull) static inline u64bits _ioa_ntoh64(u64bits v) { #if BYTE_ORDER == LITTLE_ENDIAN u08bits *src = (u08bits*) &v; u08bits* dst = src + 7; while (src < dst) { u08bits vdst = *dst; *(dst--) = *src; *(src++) = vdst; } #elif BYTE_ORDER == BIG_ENDIAN /* OK */ #else #error WRONG BYTE_ORDER SETTING #endif return v; } /* TTL */ #define TTL_IGNORE ((int)(-1)) #define TTL_DEFAULT (64) /* TOS */ #define TOS_IGNORE ((int)(-1)) #define TOS_DEFAULT (0) #define ioa_ntoh64 _ioa_ntoh64 #define ioa_hton64 _ioa_ntoh64 #define turn_malloc(sz) malloc(sz) #define turn_free(ptr,sz) free(ptr) #define turn_realloc(ptr, old_sz, new_sz) realloc((ptr),(new_sz)) #define turn_calloc(number, sz) calloc((number),(sz)) #define turn_time() ((turn_time_t)time(NULL)) typedef u32bits turn_time_t; #define turn_time_before(t1,t2) ((((s32bits)(t1))-((s32bits)(t2))) < 0) #if !defined(UNUSED_ARG) #define UNUSED_ARG(A) do { A=A; } while(0) #endif #define MAX_STUN_MESSAGE_SIZE (65507) #define STUN_BUFFER_SIZE (MAX_STUN_MESSAGE_SIZE) #define UDP_STUN_BUFFER_SIZE (1024<<4) #define NONCE_LENGTH_32BITS (4) #define DEFAULT_STUN_PORT (3478) #define DEFAULT_STUN_TLS_PORT (5349) #if BYTE_ORDER == LITTLE_ENDIAN #define DEFAULT_STUN_PORT_NBO (0x960D) #elif BYTE_ORDER == BIG_ENDIAN #define DEFAULT_STUN_PORT_NBO (0x0D96) #else #error WRONG BYTE_ORDER SETTING #endif #define STRCPY(dst,src) \ do { \ if(sizeof(dst)==sizeof(char*))\ strcpy(((char*)(dst)),(const char*)(src));\ else {\ strncpy((char*)(dst),(const char*)(src),sizeof((dst)));\ ((char*)(dst))[sizeof((dst))-1] = 0; \ }\ } while(0) ////////////////// Security //////////////////////////// #define SHA1SIZEBYTES (20) #define SHA256SIZEBYTES (32) #define MAXSHASIZE (128) enum _SHATYPE { SHATYPE_SHA1 = 0, SHATYPE_SHA256 }; typedef enum _SHATYPE SHATYPE; #define shatype_name(sht) ((sht == SHATYPE_SHA1) ? "SHA1" : ((sht == SHATYPE_SHA256) ? "SHA256" : "SHA UNKNOWN")) #define SHA_TOO_WEAK (426) //////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif /* __IODEFS__ */ turnserver-3.2.3.1/src/server/000755 001751 001751 00000000000 12315706777 016131 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/src/client++/000755 001751 001751 00000000000 12315706777 016227 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/src/apps/000755 001751 001751 00000000000 12315706777 015566 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/src/apps/rfc5769/000755 001751 001751 00000000000 12315706777 016673 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/src/apps/uclient/000755 001751 001751 00000000000 12315706777 017231 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/src/apps/peer/000755 001751 001751 00000000000 12315706777 016521 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/src/apps/common/000755 001751 001751 00000000000 12315706777 017056 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/src/apps/stunclient/000755 001751 001751 00000000000 12315706777 017756 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/src/apps/relay/000755 001751 001751 00000000000 12315706777 016702 5ustar00olegoleg000000 000000 turnserver-3.2.3.1/src/apps/relay/dtls_listener.h000644 001751 001751 00000005056 12315706777 021734 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __DTLS_LISTENER__ #define __DTLS_LISTENER__ #include "ns_turn_utils.h" #include "ns_ioalib_impl.h" #include "ns_turn_server.h" #include #ifdef __cplusplus extern "C" { #endif /////////////////////////////////////////// struct dtls_listener_relay_server_info; typedef struct dtls_listener_relay_server_info dtls_listener_relay_server_type; /////////////////////////////////////////// dtls_listener_relay_server_type* create_dtls_listener_server(const char* ifname, const char *local_address, int port, int verbose, ioa_engine_handle e, turn_turnserver *ts, int report_creation, ioa_engine_new_connection_event_handler send_socket); void udp_send_message(dtls_listener_relay_server_type *server, ioa_network_buffer_handle nbh, ioa_addr *dest); ioa_engine_handle get_engine(dtls_listener_relay_server_type* server); /////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__DTLS_LISTENER__ turnserver-3.2.3.1/src/apps/relay/tls_listener.h000644 001751 001751 00000004405 12315706777 021565 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __TLS_LISTENER__ #define __TLS_LISTENER__ #include #include "ns_turn_utils.h" #include "ns_ioalib_impl.h" #ifdef __cplusplus extern "C" { #endif /////////////////////////////////////////// struct tls_listener_relay_server_info; typedef struct tls_listener_relay_server_info tls_listener_relay_server_type; /////////////////////////////////////////// tls_listener_relay_server_type* create_tls_listener_server(const char* ifname, const char *local_address, int port, int verbose, ioa_engine_handle e, ioa_engine_new_connection_event_handler send_socket, struct relay_server *relay_server); /////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TLS_LISTENER__ turnserver-3.2.3.1/src/apps/relay/ns_sm.h000644 001751 001751 00000004470 12315706777 020177 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013, 2014 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ /* * IO Abstraction library */ #ifndef __IOA_SM__ #define __IOA_SM__ #include "ns_turn_ioalib.h" #include #ifdef __cplusplus extern "C" { #endif ////////////////////////////////////////////////////// struct _super_memory; typedef struct _super_memory super_memory_t; ////////////////////////////////////////////////////// void init_super_memory(void); super_memory_t* new_super_memory_region(void); #define allocate_super_memory_region(region,size) allocate_super_memory_region_func(region, size, __FILE__, __FUNCTION__, __LINE__) void* allocate_super_memory_region_func(super_memory_t *region, size_t size, const char* file, const char* func, int line); ///////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif /* __IOA_SM__ */ turnserver-3.2.3.1/src/apps/relay/mainrelay.h000644 001751 001751 00000017760 12315706777 021047 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #if !defined(__MAIN_RELAY__) #define __MAIN_RELAY__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ns_turn_utils.h" #include "ns_turn_khash.h" #include "userdb.h" #include "turncli.h" #include "tls_listener.h" #include "dtls_listener.h" #include "ns_turn_server.h" #include "ns_turn_maps.h" #include "apputils.h" #include "ns_ioalib_impl.h" #if !defined(TURN_NO_HIREDIS) #include "hiredis_libevent2.h" #endif #ifdef __cplusplus extern "C" { #endif ////////////// DEFINES //////////////////////////// #define DEFAULT_CONFIG_FILE "turnserver.conf" #define DEFAULT_CIPHER_LIST "DEFAULT" /* "ALL:eNULL:aNULL:NULL" */ #define DEFAULT_EC_CURVE_NAME "prime256v1" #define MAX_NUMBER_OF_GENERAL_RELAY_SERVERS ((u08bits)(0x80)) #define TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP MAX_NUMBER_OF_GENERAL_RELAY_SERVERS #define TURNSERVER_ID_BOUNDARY_BETWEEN_UDP_AND_TCP TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP /////////// TYPES /////////////////////////////////// enum _DH_KEY_SIZE { DH_566, DH_1066, DH_2066, DH_CUSTOM }; typedef enum _DH_KEY_SIZE DH_KEY_SIZE; ///////// LISTENER SERVER TYPES ///////////////////// struct message_to_listener_to_client { ioa_addr origin; ioa_addr destination; ioa_network_buffer_handle nbh; }; enum _MESSAGE_TO_LISTENER_TYPE { LMT_UNKNOWN, LMT_TO_CLIENT }; typedef enum _MESSAGE_TO_LISTENER_TYPE MESSAGE_TO_LISTENER_TYPE; struct message_to_listener { MESSAGE_TO_LISTENER_TYPE t; union { struct message_to_listener_to_client tc; } m; }; struct listener_server { rtcp_map* rtcpmap; turnipports* tp; struct event_base* event_base; ioa_engine_handle ioa_eng; struct bufferevent *in_buf; struct bufferevent *out_buf; char **addrs; ioa_addr **encaddrs; size_t addrs_number; size_t services_number; dtls_listener_relay_server_type ***udp_services; dtls_listener_relay_server_type ***dtls_services; dtls_listener_relay_server_type ***aux_udp_services; #if !defined(TURN_NO_HIREDIS) redis_context_handle rch; #endif }; enum _NET_ENG_VERSION { NEV_UNKNOWN=0, NEV_MIN, NEV_UDP_SOCKET_PER_SESSION=NEV_MIN, NEV_UDP_SOCKET_PER_ENDPOINT, NEV_UDP_SOCKET_PER_THREAD, NEV_MAX=NEV_UDP_SOCKET_PER_THREAD, NEV_TOTAL }; typedef enum _NET_ENG_VERSION NET_ENG_VERSION; ////////////// Auth Server Types //////////////// struct auth_server { struct event_base* event_base; struct bufferevent *in_buf; struct bufferevent *out_buf; pthread_t thr; }; /////////// PARAMS ////////////////////////////////// typedef struct _turn_params_ { //////////////// OpenSSL group ////////////////////// SSL_CTX *tls_ctx_ssl23; SSL_CTX *tls_ctx_v1_0; #if defined(SSL_TXT_TLSV1_1) SSL_CTX *tls_ctx_v1_1; #if defined(SSL_TXT_TLSV1_2) SSL_CTX *tls_ctx_v1_2; #endif #endif SSL_CTX *dtls_ctx; SHATYPE shatype; DH_KEY_SIZE dh_key_size; char cipher_list[1025]; char ec_curve_name[33]; char ca_cert_file[1025]; char cert_file[1025]; char pkey_file[1025]; char tls_password[513]; char dh_file[1025]; int no_sslv2; int no_sslv3; int no_tlsv1; int no_tlsv1_1; int no_tlsv1_2; int no_tls; int no_dtls; //////////////// Common params //////////////////// int verbose; int turn_daemon; vint stale_nonce; vint stun_only; vint no_stun; vint secure_stun; int server_relay; int do_not_use_config_file; char pidfile[1025]; //////////////// Listener server ///////////////// int listener_port; int tls_listener_port; int alt_listener_port; int alt_tls_listener_port; int rfc5780; int no_udp; int no_tcp; vint no_tcp_relay; vint no_udp_relay; char listener_ifname[1025]; #if !defined(TURN_NO_HIREDIS) char redis_statsdb[1025]; int use_redis_statsdb; #endif struct listener_server listener; ip_range_list_t ip_whitelist; ip_range_list_t ip_blacklist; NET_ENG_VERSION net_engine_version; const char* net_engine_version_txt[NEV_TOTAL]; //////////////// Relay servers ///////////// band_limit_t max_bps; u16bits min_port; u16bits max_port; vint no_multicast_peers; vint no_loopback_peers; char relay_ifname[1025]; size_t relays_number; char **relay_addrs; int default_relays; // Single global public IP. // If multiple public IPs are used // then ioa_addr mapping must be used. ioa_addr *external_ip; int fingerprint; turnserver_id general_relay_servers_number; turnserver_id udp_relay_servers_number; vint mobility; ////////////// Auth server //////////////// struct auth_server authserver; /////////////// AUX SERVERS //////////////// turn_server_addrs_list_t aux_servers_list; int udp_self_balance; /////////////// ALTERNATE SERVERS //////////////// turn_server_addrs_list_t alternate_servers_list; turn_server_addrs_list_t tls_alternate_servers_list; ////////////// USERS ///////////////////// users_params_t users_params; int stop_turn_server; } turn_params_t; extern turn_params_t turn_params; //////////////// Listener server ///////////////// static inline int get_alt_listener_port(void) { if(turn_params.alt_listener_port<1) return turn_params.listener_port + 1; return turn_params.alt_listener_port; } static inline int get_alt_tls_listener_port(void) { if(turn_params.alt_tls_listener_port<1) return turn_params.tls_listener_port + 1; return turn_params.alt_tls_listener_port; } void add_aux_server(const char *saddr); void add_alternate_server(const char *saddr); void del_alternate_server(const char *saddr); void add_tls_alternate_server(const char *saddr); void del_tls_alternate_server(const char *saddr); ////////// Addrs //////////////////// void add_listener_addr(const char* addr); int add_relay_addr(const char* addr); ///////// Auth //////////////// void send_auth_message_to_auth_server(struct auth_message *am); /////////// Setup server //////// void init_listener(void); void setup_server(void); void run_listener_server(struct event_base *eb); /////////////////////////////// #ifdef __cplusplus } #endif #endif //__MAIN_RELAY__ turnserver-3.2.3.1/src/apps/relay/netengine.c000644 001751 001751 00000147301 12315706777 021030 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "mainrelay.h" //////////// Barrier for the threads ////////////// #if !defined(TURN_NO_THREAD_BARRIERS) static unsigned int barrier_count = 0; static pthread_barrier_t barrier; #endif ////////////////////////////////////////////// #define get_real_general_relay_servers_number() (turn_params.general_relay_servers_number > 1 ? turn_params.general_relay_servers_number : 1) #define get_real_udp_relay_servers_number() (turn_params.udp_relay_servers_number > 1 ? turn_params.udp_relay_servers_number : 1) static struct relay_server **general_relay_servers = NULL; static struct relay_server **udp_relay_servers = NULL; ////////////////////////////////////////////// static void run_events(struct event_base *eb); static void setup_relay_server(struct relay_server *rs, ioa_engine_handle e, int to_set_rfc5780); /////////////// BARRIERS /////////////////// #if !defined(PTHREAD_BARRIER_SERIAL_THREAD) #define PTHREAD_BARRIER_SERIAL_THREAD (-1) #endif static void barrier_wait_func(const char* func, int line) { #if !defined(TURN_NO_THREAD_BARRIERS) int br = 0; do { br = pthread_barrier_wait(&barrier); if ((br < 0)&&(br != PTHREAD_BARRIER_SERIAL_THREAD)) { int err = errno; perror("barrier wait"); printf("%s:%s:%d: %d\n", __FUNCTION__, func,line,err); } } while (((br < 0)&&(br != PTHREAD_BARRIER_SERIAL_THREAD)) && (errno == EINTR)); #else UNUSED_ARG(func); UNUSED_ARG(line); sleep(5); #endif } #define barrier_wait() barrier_wait_func(__FUNCTION__,__LINE__) /////////////// AUX SERVERS //////////////// static void add_aux_server_list(const char *saddr, turn_server_addrs_list_t *list) { if(saddr && list) { ioa_addr addr; if(make_ioa_addr_from_full_string((const u08bits*)saddr, 0, &addr)!=0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong full address format: %s\n",saddr); } else { list->addrs = (ioa_addr*)realloc(list->addrs,sizeof(ioa_addr)*(list->size+1)); addr_cpy(&(list->addrs[(list->size)++]),&addr); { u08bits s[1025]; addr_to_string(&addr, s); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Aux server: %s\n",s); } } } } void add_aux_server(const char *saddr) { add_aux_server_list(saddr,&turn_params.aux_servers_list); } /////////////// ALTERNATE SERVERS //////////////// static void add_alt_server(const char *saddr, int default_port, turn_server_addrs_list_t *list) { if(saddr && list) { ioa_addr addr; turn_mutex_lock(&(list->m)); if(make_ioa_addr_from_full_string((const u08bits*)saddr, default_port, &addr)!=0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong IP address format: %s\n",saddr); } else { list->addrs = (ioa_addr*)realloc(list->addrs,sizeof(ioa_addr)*(list->size+1)); addr_cpy(&(list->addrs[(list->size)++]),&addr); { u08bits s[1025]; addr_to_string(&addr, s); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Alternate server added: %s\n",s); } } turn_mutex_unlock(&(list->m)); } } static void del_alt_server(const char *saddr, int default_port, turn_server_addrs_list_t *list) { if(saddr && list && list->size && list->addrs) { ioa_addr addr; turn_mutex_lock(&(list->m)); if(make_ioa_addr_from_full_string((const u08bits*)saddr, default_port, &addr)!=0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong IP address format: %s\n",saddr); } else { size_t i; int found = 0; for(i=0;isize;++i) { if(addr_eq(&(list->addrs[i]),&addr)) { found = 1; break; } } if(found) { size_t j; ioa_addr *new_addrs = (ioa_addr*)malloc(sizeof(ioa_addr)*(list->size-1)); for(j=0;jaddrs[j])); } for(j=i;jsize-1;++j) { addr_cpy(&(new_addrs[j]),&(list->addrs[j+1])); } free(list->addrs); list->addrs = new_addrs; list->size -= 1; { u08bits s[1025]; addr_to_string(&addr, s); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Alternate server removed: %s\n",s); } del_alt_server(saddr, default_port, list); } } turn_mutex_unlock(&(list->m)); } } void add_alternate_server(const char *saddr) { add_alt_server(saddr,DEFAULT_STUN_PORT,&turn_params.alternate_servers_list); } void del_alternate_server(const char *saddr) { del_alt_server(saddr,DEFAULT_STUN_PORT,&turn_params.alternate_servers_list); } void add_tls_alternate_server(const char *saddr) { add_alt_server(saddr,DEFAULT_STUN_TLS_PORT,&turn_params.tls_alternate_servers_list); } void del_tls_alternate_server(const char *saddr) { del_alt_server(saddr,DEFAULT_STUN_TLS_PORT,&turn_params.tls_alternate_servers_list); } ////////////////////////////////////////////////// void add_listener_addr(const char* addr) { ioa_addr baddr; if(make_ioa_addr((const u08bits*)addr,0,&baddr)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot add a listener address: %s\n",addr); } else { size_t i = 0; for(i=0;i=0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, " relay %s initialization...\n",turn_params.relay_addrs[i]); turnipports_add_ip(STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE, &baddr); turnipports_add_ip(STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE, &baddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, " relay %s initialization done\n",turn_params.relay_addrs[i]); } } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Relay ports initialization done\n"); } ////////////////////////////////////////////////// // communications between listener and relays ==>> static int handle_relay_message(relay_server_handle rs, struct message_to_relay *sm); void send_auth_message_to_auth_server(struct auth_message *am) { struct evbuffer *output = bufferevent_get_output(turn_params.authserver.out_buf); if(evbuffer_add(output,am,sizeof(struct auth_message))<0) { fprintf(stderr,"%s: Weird buffer error\n",__FUNCTION__); } } static void auth_server_receive_message(struct bufferevent *bev, void *ptr) { UNUSED_ARG(ptr); struct auth_message am; int n = 0; struct evbuffer *input = bufferevent_get_input(bev); while ((n = evbuffer_remove(input, &am, sizeof(struct auth_message))) > 0) { if (n != sizeof(struct auth_message)) { fprintf(stderr,"%s: Weird buffer error: size=%d\n",__FUNCTION__,n); continue; } if(turn_params.users_params.use_st_credentials) { st_password_t pwd; if(get_user_pwd(am.username,pwd)<0) { am.success = 0; } else { ns_bcopy(pwd,am.pwd,sizeof(st_password_t)); am.success = 1; } } else { hmackey_t key; if(get_user_key(am.username,key,am.in_buffer.nbh)<0) { am.success = 0; } else { ns_bcopy(key,am.key,sizeof(hmackey_t)); am.success = 1; } } size_t dest = am.id; struct evbuffer *output = NULL; if(dest>=TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP) { dest -= TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP; if(dest >= get_real_udp_relay_servers_number()) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Too large UDP relay number: %d\n", __FUNCTION__,(int)dest); } else { output = bufferevent_get_output(udp_relay_servers[dest]->auth_out_buf); } } else { if(dest >= get_real_general_relay_servers_number()) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Too large general relay number: %d\n", __FUNCTION__,(int)dest); } else { output = bufferevent_get_output(general_relay_servers[dest]->auth_out_buf); } } if(output) evbuffer_add(output,&am,sizeof(struct auth_message)); else { ioa_network_buffer_delete(NULL, am.in_buffer.nbh); am.in_buffer.nbh = NULL; } } } static int send_socket_to_general_relay(ioa_engine_handle e, struct message_to_relay *sm) { struct relay_server *rdest = sm->relay_server; if(!rdest) { size_t dest = (hash_int32(addr_get_port(&(sm->m.sm.nd.src_addr)))) % get_real_general_relay_servers_number(); rdest = general_relay_servers[dest]; } struct message_to_relay *smptr = sm; smptr->t = RMT_SOCKET; { struct evbuffer *output = NULL; int success = 0; output = bufferevent_get_output(rdest->out_buf); if(output) { if(evbuffer_add(output,smptr,sizeof(struct message_to_relay))<0) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Cannot add message to relay output buffer\n", __FUNCTION__); } else { success = 1; smptr->m.sm.nd.nbh=NULL; } } if(!success) { ioa_network_buffer_delete(e, smptr->m.sm.nd.nbh); smptr->m.sm.nd.nbh=NULL; IOA_CLOSE_SOCKET(smptr->m.sm.s); return -1; } } return 0; } static int send_socket_to_relay(turnserver_id id, u64bits cid, stun_tid *tid, ioa_socket_handle s, int message_integrity, MESSAGE_TO_RELAY_TYPE rmt, ioa_net_data *nd) { int ret = 0; struct message_to_relay sm; ns_bzero(&sm,sizeof(struct message_to_relay)); sm.t = rmt; struct relay_server *rs = NULL; if(id>=TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP) { size_t dest = id-TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP; if(dest >= get_real_udp_relay_servers_number()) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Too large UDP relay number: %d, rmt=%d\n", __FUNCTION__,(int)dest,(int)rmt); ret = -1; goto err; } rs = udp_relay_servers[dest]; } else { size_t dest = id; if(dest >= get_real_general_relay_servers_number()) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Too large general relay number: %d, rmt=%d\n", __FUNCTION__,(int)dest,(int)rmt); ret = -1; goto err; } rs = general_relay_servers[dest]; } switch (rmt) { case(RMT_CB_SOCKET): { sm.m.cb_sm.id = id; sm.m.cb_sm.connection_id = (tcp_connection_id)cid; stun_tid_cpy(&(sm.m.cb_sm.tid),tid); sm.m.cb_sm.s = s; sm.m.cb_sm.message_integrity = message_integrity; break; } case (RMT_MOBILE_SOCKET): { if(nd && nd->nbh) { sm.m.sm.s = s; addr_cpy(&(sm.m.sm.nd.src_addr),&(nd->src_addr)); sm.m.sm.nd.recv_tos = nd->recv_tos; sm.m.sm.nd.recv_ttl = nd->recv_ttl; sm.m.sm.nd.nbh = nd->nbh; nd->nbh = NULL; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Empty buffer with mobile socket\n",__FUNCTION__); ret = -1; } break; } default: { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: UNKNOWN RMT message: %d\n",__FUNCTION__,(int)rmt); ret = -1; } } if(ret == 0) { struct evbuffer *output = bufferevent_get_output(rs->out_buf); if(output) { evbuffer_add(output,&sm,sizeof(struct message_to_relay)); } else { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Empty output buffer\n", __FUNCTION__); ret = -1; } } err: if(ret < 0) { IOA_CLOSE_SOCKET(s); if(nd) { ioa_network_buffer_delete(NULL, nd->nbh); nd->nbh = NULL; } if(rmt == RMT_MOBILE_SOCKET) { ioa_network_buffer_delete(NULL, sm.m.sm.nd.nbh); sm.m.sm.nd.nbh = NULL; } } return ret; } static int handle_relay_message(relay_server_handle rs, struct message_to_relay *sm) { if(rs && sm) { switch (sm->t) { case RMT_SOCKET: { if (sm->m.sm.s->defer_nbh) { if (!sm->m.sm.nd.nbh) { sm->m.sm.nd.nbh = sm->m.sm.s->defer_nbh; sm->m.sm.s->defer_nbh = NULL; } else { ioa_network_buffer_delete(rs->ioa_eng, sm->m.sm.s->defer_nbh); sm->m.sm.s->defer_nbh = NULL; } } ioa_socket_handle s = sm->m.sm.s; if (!s) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: socket EMPTY\n",__FUNCTION__); } else if (s->read_event || s->bev) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: socket wrongly preset: 0x%lx : 0x%lx\n", __FUNCTION__, (long) s->read_event, (long) s->bev); IOA_CLOSE_SOCKET(s); } else { s->e = rs->ioa_eng; open_client_connection_session(&(rs->server), &(sm->m.sm)); } ioa_network_buffer_delete(rs->ioa_eng, sm->m.sm.nd.nbh); sm->m.sm.nd.nbh = NULL; } break; case RMT_CB_SOCKET: turnserver_accept_tcp_client_data_connection(&(rs->server), sm->m.cb_sm.connection_id, &(sm->m.cb_sm.tid), sm->m.cb_sm.s, sm->m.cb_sm.message_integrity); break; case RMT_MOBILE_SOCKET: { ioa_socket_handle s = sm->m.sm.s; if (!s) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: mobile socket EMPTY\n",__FUNCTION__); } else if (s->read_event || s->bev) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: mobile socket wrongly preset: 0x%lx : 0x%lx\n", __FUNCTION__, (long) s->read_event, (long) s->bev); IOA_CLOSE_SOCKET(s); } else { s->e = rs->ioa_eng; open_client_connection_session(&(rs->server), &(sm->m.sm)); } ioa_network_buffer_delete(rs->ioa_eng, sm->m.sm.nd.nbh); sm->m.sm.nd.nbh = NULL; break; } default: { perror("Weird buffer type\n"); } } } return 0; } static void handle_relay_auth_message(struct relay_server *rs, struct auth_message *am) { am->resume_func(am->success, am->key, am->pwd, &(rs->server), am->ctxkey, &(am->in_buffer)); if (am->in_buffer.nbh) { ioa_network_buffer_delete(rs->ioa_eng, am->in_buffer.nbh); am->in_buffer.nbh = NULL; } } static void relay_receive_message(struct bufferevent *bev, void *ptr) { struct message_to_relay sm; int n = 0; struct evbuffer *input = bufferevent_get_input(bev); struct relay_server *rs = (struct relay_server *)ptr; while ((n = evbuffer_remove(input, &sm, sizeof(struct message_to_relay))) > 0) { if (n != sizeof(struct message_to_relay)) { perror("Weird buffer error\n"); continue; } handle_relay_message(rs, &sm); } } static void relay_receive_auth_message(struct bufferevent *bev, void *ptr) { struct auth_message am; int n = 0; struct evbuffer *input = bufferevent_get_input(bev); struct relay_server *rs = (struct relay_server *)ptr; while ((n = evbuffer_remove(input, &am, sizeof(struct auth_message))) > 0) { if (n != sizeof(struct auth_message)) { perror("Weird auth_buffer error\n"); continue; } handle_relay_auth_message(rs, &am); } } static int send_message_from_listener_to_client(ioa_engine_handle e, ioa_network_buffer_handle nbh, ioa_addr *origin, ioa_addr *destination) { struct message_to_listener mm; mm.t = LMT_TO_CLIENT; addr_cpy(&(mm.m.tc.origin),origin); addr_cpy(&(mm.m.tc.destination),destination); mm.m.tc.nbh = ioa_network_buffer_allocate(e); ioa_network_buffer_header_init(mm.m.tc.nbh); ns_bcopy(ioa_network_buffer_data(nbh),ioa_network_buffer_data(mm.m.tc.nbh),ioa_network_buffer_get_size(nbh)); ioa_network_buffer_set_size(mm.m.tc.nbh,ioa_network_buffer_get_size(nbh)); struct evbuffer *output = bufferevent_get_output(turn_params.listener.out_buf); evbuffer_add(output,&mm,sizeof(struct message_to_listener)); return 0; } static void listener_receive_message(struct bufferevent *bev, void *ptr) { UNUSED_ARG(ptr); struct message_to_listener mm; int n = 0; struct evbuffer *input = bufferevent_get_input(bev); while ((n = evbuffer_remove(input, &mm, sizeof(struct message_to_listener))) > 0) { if (n != sizeof(struct message_to_listener)) { perror("Weird buffer error\n"); continue; } if (mm.t != LMT_TO_CLIENT) { perror("Weird buffer type\n"); continue; } size_t relay_thread_index = 0; if(turn_params.net_engine_version == NEV_UDP_SOCKET_PER_THREAD) { size_t ri; for(ri=0;rithr == pthread_self()) { relay_thread_index=ri; break; } } } size_t i; int found = 0; for(i=0;ievent_base); } return arg; } static void setup_listener(void) { super_memory_t* sm = new_super_memory_region(); turn_params.listener.tp = turnipports_create(sm, turn_params.min_port, turn_params.max_port); turn_params.listener.event_base = turn_event_base_new(); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"IO method (main listener thread): %s\n",event_base_get_method(turn_params.listener.event_base)); turn_params.listener.ioa_eng = create_ioa_engine(sm, turn_params.listener.event_base, turn_params.listener.tp, turn_params.relay_ifname, turn_params.relays_number, turn_params.relay_addrs, turn_params.default_relays, turn_params.verbose, turn_params.max_bps); if(!turn_params.listener.ioa_eng) exit(-1); set_ssl_ctx(turn_params.listener.ioa_eng, turn_params.tls_ctx_ssl23, turn_params.tls_ctx_v1_0, #if defined(SSL_TXT_TLSV1_1) turn_params.tls_ctx_v1_1, #if defined(SSL_TXT_TLSV1_2) turn_params.tls_ctx_v1_2, #endif #endif turn_params.dtls_ctx); turn_params.listener.rtcpmap = rtcp_map_create(turn_params.listener.ioa_eng); #if !defined(TURN_NO_HIREDIS) if(turn_params.use_redis_statsdb) { turn_params.listener.rch = get_redis_async_connection(turn_params.listener.event_base, turn_params.redis_statsdb); set_default_async_context(turn_params.listener.rch); turn_report_allocation_delete_all(); } #endif ioa_engine_set_rtcp_map(turn_params.listener.ioa_eng, turn_params.listener.rtcpmap); { struct bufferevent *pair[2]; int opts = BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS; opts |= BEV_OPT_THREADSAFE; bufferevent_pair_new(turn_params.listener.event_base, opts, pair); turn_params.listener.in_buf = pair[0]; turn_params.listener.out_buf = pair[1]; bufferevent_setcb(turn_params.listener.in_buf, listener_receive_message, NULL, NULL, &turn_params.listener); bufferevent_enable(turn_params.listener.in_buf, EV_READ); } if(turn_params.listener.addrs_number<2) { turn_params.rfc5780 = 0; TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: I cannot support STUN CHANGE_REQUEST functionality because only one IP address is provided\n"); } else { turn_params.listener.services_number = turn_params.listener.services_number * 2; } turn_params.listener.udp_services = (dtls_listener_relay_server_type***)allocate_super_memory_engine(turn_params.listener.ioa_eng, sizeof(dtls_listener_relay_server_type**)*turn_params.listener.services_number); turn_params.listener.dtls_services = (dtls_listener_relay_server_type***)allocate_super_memory_engine(turn_params.listener.ioa_eng, sizeof(dtls_listener_relay_server_type**)*turn_params.listener.services_number); turn_params.listener.aux_udp_services = (dtls_listener_relay_server_type***)allocate_super_memory_engine(turn_params.listener.ioa_eng, (sizeof(dtls_listener_relay_server_type**)*turn_params.aux_servers_list.size)+sizeof(void*)); } static void setup_barriers(void) { /* Adjust barriers: */ #if !defined(TURN_NO_THREAD_BARRIERS) if((turn_params.net_engine_version == NEV_UDP_SOCKET_PER_ENDPOINT) && turn_params.general_relay_servers_number>1) { /* UDP: */ if(!turn_params.no_udp) { barrier_count += turn_params.listener.addrs_number; if(turn_params.rfc5780) { barrier_count += turn_params.listener.addrs_number; } } if(!turn_params.no_dtls && (turn_params.no_udp || (turn_params.listener_port != turn_params.tls_listener_port))) { barrier_count += turn_params.listener.addrs_number; if(turn_params.rfc5780) { barrier_count += turn_params.listener.addrs_number; } } if(!turn_params.no_udp || !turn_params.no_dtls) { barrier_count += (unsigned int)turn_params.aux_servers_list.size; } } #endif #if !defined(TURN_NO_THREAD_BARRIERS) { if(pthread_barrier_init(&barrier,NULL,barrier_count)<0) perror("barrier init"); } #endif } static void setup_socket_per_endpoint_udp_listener_servers(void) { size_t i = 0; /* Adjust udp relay number */ if(turn_params.general_relay_servers_number>1) { if (!turn_params.no_udp) { turn_params.udp_relay_servers_number += turn_params.listener.addrs_number; if (turn_params.rfc5780) { turn_params.udp_relay_servers_number += turn_params.listener.addrs_number; } } if (!turn_params.no_dtls && (turn_params.no_udp || (turn_params.listener_port != turn_params.tls_listener_port))) { turn_params.udp_relay_servers_number += turn_params.listener.addrs_number; if (turn_params.rfc5780) { turn_params.udp_relay_servers_number += turn_params.listener.addrs_number; } } if (!turn_params.no_udp || !turn_params.no_dtls) { turn_params.udp_relay_servers_number += (unsigned int) turn_params.aux_servers_list.size; } } { if (!turn_params.no_udp || !turn_params.no_dtls) { udp_relay_servers = (struct relay_server**) allocate_super_memory_engine(turn_params.listener.ioa_eng, sizeof(struct relay_server *)*get_real_udp_relay_servers_number()); for (i = 0; i < get_real_udp_relay_servers_number(); i++) { ioa_engine_handle e = turn_params.listener.ioa_eng; int is_5780 = turn_params.rfc5780; if(turn_params.general_relay_servers_number<=1) { while(!(general_relay_servers[0]->ioa_eng)) sched_yield(); udp_relay_servers[i] = general_relay_servers[0]; continue; } else if(turn_params.general_relay_servers_number>1) { e = create_new_listener_engine(); is_5780 = is_5780 && (i >= (size_t) (turn_params.aux_servers_list.size)); } super_memory_t *sm = new_super_memory_region(); struct relay_server* udp_rs = (struct relay_server*) allocate_super_memory_region(sm, sizeof(struct relay_server)); udp_rs->id = (turnserver_id) i + TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP; udp_rs->sm = sm; setup_relay_server(udp_rs, e, is_5780); udp_relay_servers[i] = udp_rs; } } } int udp_relay_server_index = 0; /* Create listeners */ /* Aux UDP servers */ for(i=0; iioa_eng, sizeof(dtls_listener_relay_server_type*)); turn_params.listener.aux_udp_services[index][0] = create_dtls_listener_server(turn_params.listener_ifname, saddr, port, turn_params.verbose, udp_relay_servers[udp_relay_server_index]->ioa_eng, &(udp_relay_servers[udp_relay_server_index]->server), 1, NULL); if(turn_params.general_relay_servers_number>1) { ++udp_relay_server_index; pthread_t thr; if(pthread_create(&thr, NULL, run_udp_listener_thread, turn_params.listener.aux_udp_services[index][0])<0) { perror("Cannot create aux listener thread\n"); exit(-1); } pthread_detach(thr); } } } /* Main servers */ for(i=0; iioa_eng, sizeof(dtls_listener_relay_server_type*)); turn_params.listener.udp_services[index][0] = create_dtls_listener_server(turn_params.listener_ifname, turn_params.listener.addrs[i], turn_params.listener_port, turn_params.verbose, udp_relay_servers[udp_relay_server_index]->ioa_eng, &(udp_relay_servers[udp_relay_server_index]->server), 1, NULL); if(turn_params.general_relay_servers_number>1) { ++udp_relay_server_index; pthread_t thr; if(pthread_create(&thr, NULL, run_udp_listener_thread, turn_params.listener.udp_services[index][0])<0) { perror("Cannot create listener thread\n"); exit(-1); } pthread_detach(thr); } if(turn_params.rfc5780) { turn_params.listener.udp_services[index+1] = (dtls_listener_relay_server_type**)allocate_super_memory_engine(udp_relay_servers[udp_relay_server_index]->ioa_eng, sizeof(dtls_listener_relay_server_type*)); turn_params.listener.udp_services[index+1][0] = create_dtls_listener_server(turn_params.listener_ifname, turn_params.listener.addrs[i], get_alt_listener_port(), turn_params.verbose, udp_relay_servers[udp_relay_server_index]->ioa_eng, &(udp_relay_servers[udp_relay_server_index]->server), 1, NULL); if(turn_params.general_relay_servers_number>1) { ++udp_relay_server_index; pthread_t thr; if(pthread_create(&thr, NULL, run_udp_listener_thread, turn_params.listener.udp_services[index+1][0])<0) { perror("Cannot create listener thread\n"); exit(-1); } pthread_detach(thr); } } } else { turn_params.listener.udp_services[index] = NULL; if(turn_params.rfc5780) turn_params.listener.udp_services[index+1] = NULL; } if(!turn_params.no_dtls && (turn_params.no_udp || (turn_params.listener_port != turn_params.tls_listener_port))) { turn_params.listener.dtls_services[index] = (dtls_listener_relay_server_type**)allocate_super_memory_engine(udp_relay_servers[udp_relay_server_index]->ioa_eng, sizeof(dtls_listener_relay_server_type*)); turn_params.listener.dtls_services[index][0] = create_dtls_listener_server(turn_params.listener_ifname, turn_params.listener.addrs[i], turn_params.tls_listener_port, turn_params.verbose, udp_relay_servers[udp_relay_server_index]->ioa_eng, &(udp_relay_servers[udp_relay_server_index]->server), 1, NULL); if(turn_params.general_relay_servers_number>1) { ++udp_relay_server_index; pthread_t thr; if(pthread_create(&thr, NULL, run_udp_listener_thread, turn_params.listener.dtls_services[index][0])<0) { perror("Cannot create listener thread\n"); exit(-1); } pthread_detach(thr); } if(turn_params.rfc5780) { turn_params.listener.dtls_services[index+1] = (dtls_listener_relay_server_type**)allocate_super_memory_engine(udp_relay_servers[udp_relay_server_index]->ioa_eng, sizeof(dtls_listener_relay_server_type*)); turn_params.listener.dtls_services[index+1][0] = create_dtls_listener_server(turn_params.listener_ifname, turn_params.listener.addrs[i], get_alt_tls_listener_port(), turn_params.verbose, udp_relay_servers[udp_relay_server_index]->ioa_eng, &(udp_relay_servers[udp_relay_server_index]->server), 1, NULL); if(turn_params.general_relay_servers_number>1) { ++udp_relay_server_index; pthread_t thr; if(pthread_create(&thr, NULL, run_udp_listener_thread, turn_params.listener.dtls_services[index+1][0])<0) { perror("Cannot create listener thread\n"); exit(-1); } pthread_detach(thr); } } } else { turn_params.listener.dtls_services[index] = NULL; if(turn_params.rfc5780) turn_params.listener.dtls_services[index+1] = NULL; } } } static void setup_socket_per_thread_udp_listener_servers(void) { size_t i = 0; size_t relayindex = 0; /* Create listeners */ for(relayindex=0;relayindexioa_eng) || !(general_relay_servers[relayindex]->server.e)) sched_yield(); } /* Aux UDP servers */ for(i=0; iioa_eng, &(general_relay_servers[relayindex]->server), !relayindex, NULL); } } } /* Main servers */ for(i=0; iioa_eng, &(general_relay_servers[relayindex]->server), !relayindex, NULL); } if(turn_params.rfc5780) { turn_params.listener.udp_services[index+1] = (dtls_listener_relay_server_type**)allocate_super_memory_engine(turn_params.listener.ioa_eng, sizeof(dtls_listener_relay_server_type*) * get_real_general_relay_servers_number()); for(relayindex=0;relayindexioa_eng, &(general_relay_servers[relayindex]->server), !relayindex, NULL); } } } else { turn_params.listener.udp_services[index] = NULL; if(turn_params.rfc5780) turn_params.listener.udp_services[index+1] = NULL; } if(!turn_params.no_dtls && (turn_params.no_udp || (turn_params.listener_port != turn_params.tls_listener_port))) { turn_params.listener.dtls_services[index] = (dtls_listener_relay_server_type**)allocate_super_memory_engine(turn_params.listener.ioa_eng, sizeof(dtls_listener_relay_server_type*) * get_real_general_relay_servers_number()); for(relayindex=0;relayindexioa_eng, &(general_relay_servers[relayindex]->server), !relayindex, NULL); } if(turn_params.rfc5780) { turn_params.listener.dtls_services[index+1] = (dtls_listener_relay_server_type**)allocate_super_memory_engine(turn_params.listener.ioa_eng, sizeof(dtls_listener_relay_server_type*) * get_real_general_relay_servers_number()); for(relayindex=0;relayindexioa_eng, &(general_relay_servers[relayindex]->server), !relayindex, NULL); } } } else { turn_params.listener.dtls_services[index] = NULL; if(turn_params.rfc5780) turn_params.listener.dtls_services[index+1] = NULL; } } } static void setup_socket_per_session_udp_listener_servers(void) { size_t i = 0; /* Aux UDP servers */ for(i=0; iss.sa_family == turn_params.listener.encaddrs[i]->ss.sa_family) { index=i; break; } } } if(index!=0xffff) { for(i=0;iss.sa_family == addr->ss.sa_family) { addr_cpy(alt_addr,caddr); addr_set_port(alt_addr, alt_port); return 0; } } } } } return -1; } static void run_events(struct event_base *eb) { if (!eb) return; struct timeval timeout; timeout.tv_sec = 5; timeout.tv_usec = 0; event_base_loopexit(eb, &timeout); event_base_dispatch(eb); } void run_listener_server(struct event_base *eb) { unsigned int cycle = 0; while (!turn_params.stop_turn_server) { if (eve(turn_params.verbose)) { if ((cycle++ & 15) == 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cycle=%u\n", __FUNCTION__, cycle); } } run_events(eb); rollover_logfile(); } } static void setup_relay_server(struct relay_server *rs, ioa_engine_handle e, int to_set_rfc5780) { struct bufferevent *pair[2]; int opts = BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS; if(e) { rs->event_base = e->event_base; rs->ioa_eng = e; } else { rs->event_base = turn_event_base_new(); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"IO method (general relay thread): %s\n",event_base_get_method(rs->event_base)); rs->ioa_eng = create_ioa_engine(rs->sm, rs->event_base, turn_params.listener.tp, turn_params.relay_ifname, turn_params.relays_number, turn_params.relay_addrs, turn_params.default_relays, turn_params.verbose, turn_params.max_bps); set_ssl_ctx(rs->ioa_eng, turn_params.tls_ctx_ssl23, turn_params.tls_ctx_v1_0, #if defined(SSL_TXT_TLSV1_1) turn_params.tls_ctx_v1_1, #if defined(SSL_TXT_TLSV1_2) turn_params.tls_ctx_v1_2, #endif #endif turn_params.dtls_ctx); ioa_engine_set_rtcp_map(rs->ioa_eng, turn_params.listener.rtcpmap); } opts |= BEV_OPT_THREADSAFE; bufferevent_pair_new(rs->event_base, opts, pair); rs->in_buf = pair[0]; rs->out_buf = pair[1]; bufferevent_setcb(rs->in_buf, relay_receive_message, NULL, NULL, rs); bufferevent_enable(rs->in_buf, EV_READ); bufferevent_pair_new(rs->event_base, opts, pair); rs->auth_in_buf = pair[0]; rs->auth_out_buf = pair[1]; bufferevent_setcb(rs->auth_in_buf, relay_receive_auth_message, NULL, NULL, rs); bufferevent_enable(rs->auth_in_buf, EV_READ); init_turn_server(&(rs->server), rs->id, turn_params.verbose, rs->ioa_eng, 0, turn_params.fingerprint, DONT_FRAGMENT_SUPPORTED, turn_params.users_params.users.ct, (u08bits*)turn_params.users_params.global_realm, start_user_check, check_new_allocation_quota, release_allocation_quota, turn_params.external_ip, &turn_params.no_tcp_relay, &turn_params.no_udp_relay, &turn_params.stale_nonce, &turn_params.stun_only, &turn_params.no_stun, &turn_params.alternate_servers_list, &turn_params.tls_alternate_servers_list, &turn_params.aux_servers_list, turn_params.udp_self_balance, &turn_params.no_multicast_peers, &turn_params.no_loopback_peers, &turn_params.ip_whitelist, &turn_params.ip_blacklist, send_socket_to_relay, &turn_params.secure_stun, turn_params.shatype, &turn_params.mobility, turn_params.server_relay, send_turn_session_info); if(to_set_rfc5780) { set_rfc5780(&(rs->server), get_alt_addr, send_message_from_listener_to_client); } if(turn_params.net_engine_version == NEV_UDP_SOCKET_PER_THREAD) { setup_tcp_listener_servers(rs->ioa_eng, rs); } } static void *run_general_relay_thread(void *arg) { static int always_true = 1; struct relay_server *rs = (struct relay_server *)arg; int udp_reuses_the_same_relay_server = (turn_params.general_relay_servers_number<=1) || (turn_params.net_engine_version == NEV_UDP_SOCKET_PER_THREAD) || (turn_params.net_engine_version == NEV_UDP_SOCKET_PER_SESSION); int we_need_rfc5780 = udp_reuses_the_same_relay_server && turn_params.rfc5780; ignore_sigpipe(); setup_relay_server(rs, NULL, we_need_rfc5780); barrier_wait(); while(always_true) { run_events(rs->event_base); } return arg; } static void setup_general_relay_servers(void) { size_t i = 0; general_relay_servers = (struct relay_server**)allocate_super_memory_engine(turn_params.listener.ioa_eng, sizeof(struct relay_server *)*get_real_general_relay_servers_number()); for(i=0;iid = (turnserver_id)i; general_relay_servers[i]->sm = NULL; setup_relay_server(general_relay_servers[i], turn_params.listener.ioa_eng, ((turn_params.net_engine_version == NEV_UDP_SOCKET_PER_THREAD) || (turn_params.net_engine_version == NEV_UDP_SOCKET_PER_SESSION)) && turn_params.rfc5780); general_relay_servers[i]->thr = pthread_self(); } else { super_memory_t *sm = new_super_memory_region(); general_relay_servers[i] = (struct relay_server*)allocate_super_memory_region(sm,sizeof(struct relay_server)); general_relay_servers[i]->id = (turnserver_id)i; general_relay_servers[i]->sm = sm; if(pthread_create(&(general_relay_servers[i]->thr), NULL, run_general_relay_thread, general_relay_servers[i])<0) { perror("Cannot create relay thread\n"); exit(-1); } pthread_detach(general_relay_servers[i]->thr); } } } static int run_auth_server_flag = 1; static void* run_auth_server_thread(void *arg) { ignore_sigpipe(); ns_bzero(&turn_params.authserver,sizeof(struct auth_server)); turn_params.authserver.event_base = turn_event_base_new(); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"IO method (auth thread): %s\n",event_base_get_method(turn_params.authserver.event_base)); struct bufferevent *pair[2]; int opts = BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS; opts |= BEV_OPT_THREADSAFE; bufferevent_pair_new(turn_params.authserver.event_base, opts, pair); turn_params.authserver.in_buf = pair[0]; turn_params.authserver.out_buf = pair[1]; bufferevent_setcb(turn_params.authserver.in_buf, auth_server_receive_message, NULL, NULL, &turn_params.authserver); bufferevent_enable(turn_params.authserver.in_buf, EV_READ); struct event_base *eb = turn_params.authserver.event_base; barrier_wait(); while(run_auth_server_flag) { run_events(eb); read_userdb_file(0); update_white_and_black_lists(); auth_ping(); #if !defined(TURN_NO_HIREDIS) send_message_to_redis(NULL, "publish", "__XXX__", "__YYY__"); #endif } return arg; } static void setup_auth_server(void) { if(pthread_create(&(turn_params.authserver.thr), NULL, run_auth_server_thread, NULL)<0) { perror("Cannot create auth thread\n"); exit(-1); } pthread_detach(turn_params.authserver.thr); } static void* run_cli_server_thread(void *arg) { ignore_sigpipe(); setup_cli_thread(); barrier_wait(); while(cliserver.event_base) { run_events(cliserver.event_base); } return arg; } static void setup_cli_server(void) { ns_bzero(&cliserver,sizeof(struct cli_server)); cliserver.listen_fd = -1; cliserver.verbose = turn_params.verbose; if(pthread_create(&(cliserver.thr), NULL, run_cli_server_thread, &cliserver)<0) { perror("Cannot create cli thread\n"); exit(-1); } pthread_detach(cliserver.thr); } void setup_server(void) { evthread_use_pthreads(); #if !defined(TURN_NO_THREAD_BARRIERS) /* relay threads plus auth thread plus main listener thread */ /* udp address listener thread(s) will start later */ barrier_count = turn_params.general_relay_servers_number+2; if(use_cli) { barrier_count += 1; } #endif setup_listener(); allocate_relay_addrs_ports(); setup_barriers(); setup_general_relay_servers(); if(turn_params.net_engine_version == NEV_UDP_SOCKET_PER_THREAD) setup_socket_per_thread_udp_listener_servers(); else if(turn_params.net_engine_version == NEV_UDP_SOCKET_PER_ENDPOINT) setup_socket_per_endpoint_udp_listener_servers(); else if(turn_params.net_engine_version == NEV_UDP_SOCKET_PER_SESSION) setup_socket_per_session_udp_listener_servers(); if(turn_params.net_engine_version != NEV_UDP_SOCKET_PER_THREAD) { setup_tcp_listener_servers(turn_params.listener.ioa_eng, NULL); } setup_auth_server(); if(use_cli) setup_cli_server(); barrier_wait(); } void init_listener(void) { ns_bzero(&turn_params.listener,sizeof(struct listener_server)); } /////////////////////////////// turnserver-3.2.3.1/src/apps/relay/libtelnet.c000644 001751 001751 00000117404 12315706777 021037 0ustar00olegoleg000000 000000 /* * libtelnet - TELNET protocol handling library * * Sean Middleditch * sean@sourcemud.org * * The author or authors of this code dedicate any and all copyright interest * in this code to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and successors. We * intend this dedication to be an overt act of relinquishment in perpetuity of * all present and future rights to this code under copyright law. */ /** * Minor fixes by Oleg Moskalenko */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include /* Win32 compatibility */ #if defined(_WIN32) # define vsnprintf _vsnprintf # define __func__ __FUNCTION__ # define ZLIB_WINAPI 1 #endif #if defined(HAVE_ZLIB) # include #endif #include "libtelnet.h" /* inlinable functions */ #if defined(__GNUC__) || __STDC_VERSION__ >= 199901L # define INLINE __inline__ #else # define INLINE #endif /* helper for Q-method option tracking */ #define Q_US(q) ((q).state & 0x0F) #define Q_HIM(q) (((q).state & 0xF0) >> 4) #define Q_MAKE(us,him) ((us) | ((him) << 4)) /* helper for the negotiation routines */ #define NEGOTIATE_EVENT(telnet,cmd,opt) \ ev.type = (cmd); \ ev.neg.telopt = (opt); \ (telnet)->eh((telnet), &ev, (telnet)->ud); /* telnet state codes */ enum telnet_state_t { TELNET_STATE_DATA = 0, TELNET_STATE_IAC, TELNET_STATE_WILL, TELNET_STATE_WONT, TELNET_STATE_DO, TELNET_STATE_DONT, TELNET_STATE_SB, TELNET_STATE_SB_DATA, TELNET_STATE_SB_DATA_IAC }; typedef enum telnet_state_t telnet_state_t; /* telnet state tracker */ struct telnet_t { /* user data */ void *ud; /* telopt support table */ const telnet_telopt_t *telopts; /* event handler */ telnet_event_handler_t eh; #if defined(HAVE_ZLIB) /* zlib (mccp2) compression */ z_stream *z; #endif /* RFC1143 option negotiation states */ struct telnet_rfc1143_t *q; /* sub-request buffer */ char *buffer; /* current size of the buffer */ size_t buffer_size; /* current buffer write position (also length of buffer data) */ size_t buffer_pos; /* current state */ enum telnet_state_t state; /* option flags */ unsigned char flags; /* current subnegotiation telopt */ unsigned char sb_telopt; /* length of RFC1143 queue */ unsigned char q_size; }; /* RFC1143 option negotiation state */ typedef struct telnet_rfc1143_t { unsigned char telopt; unsigned char state; } telnet_rfc1143_t; /* RFC1143 state names */ #define Q_NO 0 #define Q_YES 1 #define Q_WANTNO 2 #define Q_WANTYES 3 #define Q_WANTNO_OP 4 #define Q_WANTYES_OP 5 /* buffer sizes */ static const size_t _buffer_sizes[] = { 0, 512, 2048, 8192, 16384, }; static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) / sizeof(_buffer_sizes[0]); /* error generation function */ static telnet_error_t _error(telnet_t *telnet, unsigned line, const char* func, telnet_error_t err, int fatal, const char *fmt, ...) { telnet_event_t ev; char buffer[512]; va_list va; /* format informational text */ va_start(va, fmt); vsnprintf(buffer, sizeof(buffer), fmt, va); va_end(va); /* send error event to the user */ ev.type = fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING; ev.error.file = __FILE__; ev.error.func = func; ev.error.line = line; ev.error.msg = buffer; telnet->eh(telnet, &ev, telnet->ud); return err; } #if defined(HAVE_ZLIB) /* initialize the zlib box for a telnet box; if deflate is non-zero, it * initializes zlib for delating (compression), otherwise for inflating * (decompression). returns TELNET_EOK on success, something else on * failure. */ telnet_error_t _init_zlib(telnet_t *telnet, int deflate, int err_fatal) { z_stream *z; int rs; /* if compression is already enabled, fail loudly */ if (telnet->z != 0) return _error(telnet, __LINE__, __func__, TELNET_EBADVAL, err_fatal, "cannot initialize compression twice"); /* allocate zstream box */ if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0) return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal, "malloc() failed: %s", strerror(errno)); /* initialize */ if (deflate) { if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) { free(z); return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, err_fatal, "deflateInit() failed: %s", zError(rs)); } telnet->flags |= TELNET_PFLAG_DEFLATE; } else { if ((rs = inflateInit(z)) != Z_OK) { free(z); return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, err_fatal, "inflateInit() failed: %s", zError(rs)); } telnet->flags &= ~TELNET_PFLAG_DEFLATE; } telnet->z = z; return TELNET_EOK; } #endif /* defined(HAVE_ZLIB) */ /* push bytes out, compressing them first if need be */ static void _send(telnet_t *telnet, const char *buffer, size_t size) { telnet_event_t ev; #if defined(HAVE_ZLIB) /* if we have a deflate (compression) zlib box, use it */ if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) { char deflate_buffer[1024]; int rs; /* initialize z state */ telnet->z->next_in = (unsigned char *)buffer; telnet->z->avail_in = size; telnet->z->next_out = (unsigned char *)deflate_buffer; telnet->z->avail_out = sizeof(deflate_buffer); /* deflate until buffer exhausted and all output is produced */ while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) { /* compress */ if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) { _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1, "deflate() failed: %s", zError(rs)); deflateEnd(telnet->z); free(telnet->z); telnet->z = 0; break; } /* send event */ ev.type = TELNET_EV_SEND; ev.data.buffer = deflate_buffer; ev.data.size = sizeof(deflate_buffer) - telnet->z->avail_out; telnet->eh(telnet, &ev, telnet->ud); /* prepare output buffer for next run */ telnet->z->next_out = (unsigned char *)deflate_buffer; telnet->z->avail_out = sizeof(deflate_buffer); } /* do not continue with remaining code */ return; } #endif /* defined(HAVE_ZLIB) */ ev.type = TELNET_EV_SEND; ev.data.buffer = buffer; ev.data.size = size; telnet->eh(telnet, &ev, telnet->ud); } /* to send bags of unsigned chars */ #define _sendu(t, d, s) _send((t), (const char*)(d), (s)) /* check if we support a particular telopt; if us is non-zero, we * check if we (local) supports it, otherwise we check if he (remote) * supports it. return non-zero if supported, zero if not supported. */ static INLINE int _check_telopt(telnet_t *telnet, unsigned char telopt, int us) { int i; /* if we have no telopts table, we obviously don't support it */ if (telnet->telopts == 0) return 0; /* loop unti found or end marker (us and him both 0) */ for (i = 0; telnet->telopts[i].telopt != -1; ++i) { if (telnet->telopts[i].telopt == telopt) { if (us && telnet->telopts[i].us == TELNET_WILL) return 1; else if (!us && telnet->telopts[i].him == TELNET_DO) return 1; else return 0; } } /* not found, so not supported */ return 0; } /* retrieve RFC1143 option state */ static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet, unsigned char telopt) { telnet_rfc1143_t empty; int i; /* search for entry */ for (i = 0; i != telnet->q_size; ++i) { if (telnet->q[i].telopt == telopt) { return telnet->q[i]; } } /* not found, return empty value */ empty.telopt = telopt; empty.state = 0; return empty; } /* save RFC1143 option state */ static INLINE void _set_rfc1143(telnet_t *telnet, unsigned char telopt, char us, char him) { telnet_rfc1143_t *qtmp; int i; /* search for entry */ for (i = 0; i != telnet->q_size; ++i) { if (telnet->q[i].telopt == telopt) { telnet->q[i].state = Q_MAKE(us,him); return; } } /* we're going to need to track state for it, so grow the queue * by 4 (four) elements and put the telopt into it; bail on allocation * error. we go by four because it seems like a reasonable guess as * to the number of enabled options for most simple code, and it * allows for an acceptable number of reallocations for complex code. */ if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q, sizeof(telnet_rfc1143_t) * (telnet->q_size + 4))) == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "realloc() failed: %s", strerror(errno)); return; } memset(&qtmp[telnet->q_size], 0, sizeof(telnet_rfc1143_t) * 4); telnet->q = qtmp; telnet->q[telnet->q_size].telopt = telopt; telnet->q[telnet->q_size].state = Q_MAKE(us, him); telnet->q_size += 4; } /* send negotiation bytes */ static INLINE void _send_negotiate(telnet_t *telnet, unsigned char cmd, unsigned char telopt) { unsigned char bytes[3]; bytes[0] = TELNET_IAC; bytes[1] = cmd; bytes[2] = telopt; _sendu(telnet, bytes, 3); } /* negotiation handling magic for RFC1143 */ static void _negotiate(telnet_t *telnet, unsigned char telopt) { telnet_event_t ev; telnet_rfc1143_t q; /* in PROXY mode, just pass it thru and do nothing */ if (telnet->flags & TELNET_FLAG_PROXY) { switch ((int)telnet->state) { case TELNET_STATE_WILL: NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); break; case TELNET_STATE_WONT: NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt); break; case TELNET_STATE_DO: NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); break; case TELNET_STATE_DONT: NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt); break; } return; } /* lookup the current state of the option */ q = _get_rfc1143(telnet, telopt); /* start processing... */ switch ((int)telnet->state) { /* request to enable option on remote end or confirm DO */ case TELNET_STATE_WILL: switch (Q_HIM(q)) { case Q_NO: if (_check_telopt(telnet, telopt, 0)) { _set_rfc1143(telnet, telopt, Q_US(q), Q_YES); _send_negotiate(telnet, TELNET_DO, telopt); NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); } else _send_negotiate(telnet, TELNET_DONT, telopt); break; case Q_WANTNO: _set_rfc1143(telnet, telopt, Q_US(q), Q_NO); NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt); _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "DONT answered by WILL"); break; case Q_WANTNO_OP: _set_rfc1143(telnet, telopt, Q_US(q), Q_YES); NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "DONT answered by WILL"); break; case Q_WANTYES: _set_rfc1143(telnet, telopt, Q_US(q), Q_YES); NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); break; case Q_WANTYES_OP: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO); _send_negotiate(telnet, TELNET_DONT, telopt); NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); break; } break; /* request to disable option on remote end, confirm DONT, reject DO */ case TELNET_STATE_WONT: switch (Q_HIM(q)) { case Q_YES: _set_rfc1143(telnet, telopt, Q_US(q), Q_NO); _send_negotiate(telnet, TELNET_DONT, telopt); NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt); break; case Q_WANTNO: _set_rfc1143(telnet, telopt, Q_US(q), Q_NO); NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt); break; case Q_WANTNO_OP: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES); NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); break; case Q_WANTYES: case Q_WANTYES_OP: _set_rfc1143(telnet, telopt, Q_US(q), Q_NO); break; } break; /* request to enable option on local end or confirm WILL */ case TELNET_STATE_DO: switch (Q_US(q)) { case Q_NO: if (_check_telopt(telnet, telopt, 1)) { _set_rfc1143(telnet, telopt, Q_YES, Q_HIM(q)); _send_negotiate(telnet, TELNET_WILL, telopt); NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); } else _send_negotiate(telnet, TELNET_WONT, telopt); break; case Q_WANTNO: _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q)); NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt); _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "WONT answered by DO"); break; case Q_WANTNO_OP: _set_rfc1143(telnet, telopt, Q_YES, Q_HIM(q)); NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "WONT answered by DO"); break; case Q_WANTYES: _set_rfc1143(telnet, telopt, Q_YES, Q_HIM(q)); NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); break; case Q_WANTYES_OP: _set_rfc1143(telnet, telopt, Q_WANTNO, Q_HIM(q)); _send_negotiate(telnet, TELNET_WONT, telopt); NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); break; } break; /* request to disable option on local end, confirm WONT, reject WILL */ case TELNET_STATE_DONT: switch (Q_US(q)) { case Q_YES: _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q)); _send_negotiate(telnet, TELNET_WONT, telopt); NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt); break; case Q_WANTNO: _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q)); NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt); break; case Q_WANTNO_OP: _set_rfc1143(telnet, telopt, Q_WANTYES, Q_HIM(q)); _send_negotiate(telnet, TELNET_WILL, telopt); NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); break; case Q_WANTYES: case Q_WANTYES_OP: _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q)); break; } break; } } /* process an ENVIRON/NEW-ENVIRON subnegotiation buffer * * the algorithm and approach used here is kind of a hack, * but it reduces the number of memory allocations we have * to make. * * we copy the bytes back into the buffer, starting at the very * beginning, which makes it easy to handle the ENVIRON ESC * escape mechanism as well as ensure the variable name and * value strings are NUL-terminated, all while fitting inside * of the original buffer. */ static int _environ_telnet(telnet_t *telnet, unsigned char type, char* buffer, size_t size) { telnet_event_t ev; struct telnet_environ_t *values = 0; char *c, *last, *out; size_t index, count; /* if we have no data, just pass it through */ if (size == 0) { return 0; } /* first byte must be a valid command */ if ((unsigned)buffer[0] != TELNET_ENVIRON_SEND && (unsigned)buffer[0] != TELNET_ENVIRON_IS && (unsigned)buffer[0] != TELNET_ENVIRON_INFO) { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "telopt %d subneg has invalid command", type); return 0; } /* store ENVIRON command */ ev.environ.cmd = buffer[0]; /* if we have no arguments, send an event with no data end return */ if (size == 1) { /* no list of variables given */ ev.environ.values = 0; ev.environ.size = 0; /* invoke event with our arguments */ ev.type = TELNET_EV_ENVIRON; telnet->eh(telnet, &ev, telnet->ud); return 1; } /* very second byte must be VAR or USERVAR, if present */ if ((unsigned)buffer[1] != TELNET_ENVIRON_VAR && (unsigned)buffer[1] != TELNET_ENVIRON_USERVAR) { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "telopt %d subneg missing variable type", type); return 0; } /* ensure last byte is not an escape byte (makes parsing later easier) */ if ((unsigned)buffer[size - 1] == TELNET_ENVIRON_ESC) { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "telopt %d subneg ends with ESC", type); return 0; } /* count arguments; each valid entry starts with VAR or USERVAR */ count = 0; for (c = buffer + 1; c < buffer + size; ++c) { if (*c == TELNET_ENVIRON_VAR || *c == TELNET_ENVIRON_USERVAR) { ++count; } else if (*c == TELNET_ENVIRON_ESC) { /* skip the next byte */ ++c; } } /* allocate argument array, bail on error */ if ((values = (struct telnet_environ_t *)calloc(count, sizeof(struct telnet_environ_t))) == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "calloc() failed: %s", strerror(errno)); return 0; } /* parse argument array strings */ out = buffer; c = buffer + 1; for (index = 0; index != count; ++index) { /* remember the variable type (will be VAR or USERVAR) */ values[index].type = *c++; /* scan until we find an end-marker, and buffer up unescaped * bytes into our buffer */ last = out; while (c < buffer + size) { /* stop at the next variable or at the value */ if ((unsigned)*c == TELNET_ENVIRON_VAR || (unsigned)*c == TELNET_ENVIRON_VALUE || (unsigned)*c == TELNET_ENVIRON_USERVAR) { break; } /* buffer next byte (taking into account ESC) */ if (*c == TELNET_ENVIRON_ESC) { ++c; } *out++ = *c++; } *out++ = '\0'; /* store the variable name we have just received */ values[index].var = last; values[index].value = ""; /* if we got a value, find the next end marker and * store the value; otherwise, store empty string */ if (c < buffer + size && *c == TELNET_ENVIRON_VALUE) { ++c; last = out; while (c < buffer + size) { /* stop when we find the start of the next variable */ if ((unsigned)*c == TELNET_ENVIRON_VAR || (unsigned)*c == TELNET_ENVIRON_USERVAR) { break; } /* buffer next byte (taking into account ESC) */ if (*c == TELNET_ENVIRON_ESC) { ++c; } *out++ = *c++; } *out++ = '\0'; /* store the variable value */ values[index].value = last; } } /* pass values array and count to event */ ev.environ.values = values; ev.environ.size = count; /* invoke event with our arguments */ ev.type = TELNET_EV_ENVIRON; telnet->eh(telnet, &ev, telnet->ud); /* clean up */ free(values); return 1; } /* process an MSSP subnegotiation buffer */ static int _mssp_telnet(telnet_t *telnet, char* buffer, size_t size) { telnet_event_t ev; struct telnet_environ_t *values; char *var = 0; char *c, *last, *out; size_t i, count; unsigned char next_type; /* if we have no data, just pass it through */ if (size == 0) { return 0; } /* first byte must be a VAR */ if ((unsigned)buffer[0] != TELNET_MSSP_VAR) { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "MSSP subnegotiation has invalid data"); return 0; } /* count the arguments, any part that starts with VALUE */ for (count = 0, i = 0; i != size; ++i) { if ((unsigned)buffer[i] == TELNET_MSSP_VAL) { ++count; } } /* allocate argument array, bail on error */ if ((values = (struct telnet_environ_t *)calloc(count, sizeof(struct telnet_environ_t))) == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "calloc() failed: %s", strerror(errno)); return 0; } ev.mssp.values = values; ev.mssp.size = count; /* allocate strings in argument array */ out = last = buffer; next_type = buffer[0]; for (i = 0, c = buffer + 1; c < buffer + size;) { /* search for end marker */ while (c < buffer + size && (unsigned)*c != TELNET_MSSP_VAR && (unsigned)*c != TELNET_MSSP_VAL) { *out++ = *c++; } *out++ = '\0'; /* if it's a variable name, just store the name for now */ if (next_type == TELNET_MSSP_VAR) { var = last; } else if (next_type == TELNET_MSSP_VAL && var != 0) { values[i].var = var; values[i].value = last; ++i; } else { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "invalid MSSP subnegotiation data"); free(values); return 0; } /* remember our next type and increment c for next loop run */ last = out; next_type = *c++; } /* invoke event with our arguments */ ev.type = TELNET_EV_MSSP; telnet->eh(telnet, &ev, telnet->ud); /* clean up */ free(values); return 0; } /* parse ZMP command subnegotiation buffers */ static int _zmp_telnet(telnet_t *telnet, const char* buffer, size_t size) { telnet_event_t ev; const char **argv; const char *c; size_t i, argc; /* make sure this is a valid ZMP buffer */ if (size == 0 || buffer[size - 1] != 0) { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "incomplete ZMP frame"); return 0; } /* count arguments */ for (argc = 0, c = buffer; c != buffer + size; ++argc) c += strlen(c) + 1; /* allocate argument array, bail on error */ if ((argv = (const char **)calloc(argc, sizeof(char *))) == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "calloc() failed: %s", strerror(errno)); return 0; } /* populate argument array */ for (i = 0, c = buffer; i != argc; ++i) { argv[i] = c; c += strlen(c) + 1; } /* invoke event with our arguments */ ev.type = TELNET_EV_ZMP; ev.zmp.argv = argv; ev.zmp.argc = argc; telnet->eh(telnet, &ev, telnet->ud); /* clean up */ free(argv); return 0; } /* parse TERMINAL-TYPE command subnegotiation buffers */ static int _ttype_telnet(telnet_t *telnet, const char* buffer, size_t size) { telnet_event_t ev; /* make sure request is not empty */ if (size == 0) { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "incomplete TERMINAL-TYPE request"); return 0; } /* make sure request has valid command type */ if (buffer[0] != TELNET_TTYPE_IS && buffer[0] != TELNET_TTYPE_SEND) { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "TERMINAL-TYPE request has invalid type"); return 0; } /* send proper event */ if (buffer[0] == TELNET_TTYPE_IS) { char *name; /* allocate space for name */ if ((name = (char *)malloc(size)) == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "malloc() failed: %s", strerror(errno)); return 0; } memcpy(name, buffer + 1, size - 1); name[size - 1] = '\0'; ev.type = TELNET_EV_TTYPE; ev.ttype.cmd = TELNET_TTYPE_IS; ev.ttype.name = name; telnet->eh(telnet, &ev, telnet->ud); /* clean up */ free(name); } else { ev.type = TELNET_EV_TTYPE; ev.ttype.cmd = TELNET_TTYPE_SEND; ev.ttype.name = 0; telnet->eh(telnet, &ev, telnet->ud); } return 0; } /* process a subnegotiation buffer; return non-zero if the current buffer * must be aborted and reprocessed due to COMPRESS2 being activated */ static int _subnegotiate(telnet_t *telnet) { telnet_event_t ev; /* standard subnegotiation event */ ev.type = TELNET_EV_SUBNEGOTIATION; ev.sub.telopt = telnet->sb_telopt; ev.sub.buffer = telnet->buffer; ev.sub.size = telnet->buffer_pos; telnet->eh(telnet, &ev, telnet->ud); switch (telnet->sb_telopt) { #if defined(HAVE_ZLIB) /* received COMPRESS2 begin marker, setup our zlib box and * start handling the compressed stream if it's not already. */ case TELNET_TELOPT_COMPRESS2: if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) { if (_init_zlib(telnet, 0, 1) != TELNET_EOK) return 0; /* notify app that compression was enabled */ ev.type = TELNET_EV_COMPRESS; ev.compress.state = 1; telnet->eh(telnet, &ev, telnet->ud); return 1; } return 0; #endif /* defined(HAVE_ZLIB) */ /* specially handled subnegotiation telopt types */ case TELNET_TELOPT_ZMP: return _zmp_telnet(telnet, telnet->buffer, telnet->buffer_pos); case TELNET_TELOPT_TTYPE: return _ttype_telnet(telnet, telnet->buffer, telnet->buffer_pos); case TELNET_TELOPT_ENVIRON: case TELNET_TELOPT_NEW_ENVIRON: return _environ_telnet(telnet, telnet->sb_telopt, telnet->buffer, telnet->buffer_pos); case TELNET_TELOPT_MSSP: return _mssp_telnet(telnet, telnet->buffer, telnet->buffer_pos); default: return 0; } } /* initialize a telnet state tracker */ telnet_t *telnet_init(const telnet_telopt_t *telopts, telnet_event_handler_t eh, unsigned char flags, void *user_data) { /* allocate structure */ struct telnet_t *telnet = (telnet_t*)calloc(1, sizeof(telnet_t)); if (telnet == 0) return 0; /* initialize data */ telnet->ud = user_data; telnet->telopts = telopts; telnet->eh = eh; telnet->flags = flags; return telnet; } /* free up any memory allocated by a state tracker */ void telnet_free(telnet_t *telnet) { /* free sub-request buffer */ if (telnet->buffer != 0) { free(telnet->buffer); telnet->buffer = 0; telnet->buffer_size = 0; telnet->buffer_pos = 0; } #if defined(HAVE_ZLIB) /* free zlib box */ if (telnet->z != 0) { if (telnet->flags & TELNET_PFLAG_DEFLATE) deflateEnd(telnet->z); else inflateEnd(telnet->z); free(telnet->z); telnet->z = 0; } #endif /* defined(HAVE_ZLIB) */ /* free RFC1143 queue */ if (telnet->q) { free(telnet->q); telnet->q = 0; telnet->q_size = 0; } /* free the telnet structure itself */ free(telnet); } /* push a byte into the telnet buffer */ static telnet_error_t _buffer_byte(telnet_t *telnet, unsigned char byte) { char *new_buffer; size_t i; /* check if we're out of room */ if (telnet->buffer_pos == telnet->buffer_size) { /* find the next buffer size */ for (i = 0; i != _buffer_sizes_count; ++i) { if (_buffer_sizes[i] == telnet->buffer_size) { break; } } /* overflow -- can't grow any more */ if (i >= _buffer_sizes_count - 1) { _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0, "subnegotiation buffer size limit reached"); return TELNET_EOVERFLOW; } /* (re)allocate buffer */ new_buffer = (char *)realloc(telnet->buffer, _buffer_sizes[i + 1]); if (new_buffer == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "realloc() failed"); return TELNET_ENOMEM; } telnet->buffer = new_buffer; telnet->buffer_size = _buffer_sizes[i + 1]; } /* push the byte, all set */ telnet->buffer[telnet->buffer_pos++] = byte; return TELNET_EOK; } static void _process(telnet_t *telnet, const char *buffer, size_t size) { telnet_event_t ev; unsigned char byte; size_t i, start; for (i = start = 0; i != size; ++i) { byte = buffer[i]; switch (telnet->state) { /* regular data */ case TELNET_STATE_DATA: /* on an IAC byte, pass through all pending bytes and * switch states */ if (byte == TELNET_IAC) { if (i != start) { ev.type = TELNET_EV_DATA; ev.data.buffer = buffer + start; ev.data.size = i - start; telnet->eh(telnet, &ev, telnet->ud); } telnet->state = TELNET_STATE_IAC; } break; /* IAC command */ case TELNET_STATE_IAC: switch (byte) { /* subnegotiation */ case TELNET_SB: telnet->state = TELNET_STATE_SB; break; /* negotiation commands */ case TELNET_WILL: telnet->state = TELNET_STATE_WILL; break; case TELNET_WONT: telnet->state = TELNET_STATE_WONT; break; case TELNET_DO: telnet->state = TELNET_STATE_DO; break; case TELNET_DONT: telnet->state = TELNET_STATE_DONT; break; /* IAC escaping */ case TELNET_IAC: /* event */ ev.type = TELNET_EV_DATA; ev.data.buffer = (char*)&byte; ev.data.size = 1; telnet->eh(telnet, &ev, telnet->ud); /* state update */ start = i + 1; telnet->state = TELNET_STATE_DATA; break; /* some other command */ default: /* event */ ev.type = TELNET_EV_IAC; ev.iac.cmd = byte; telnet->eh(telnet, &ev, telnet->ud); /* state update */ start = i + 1; telnet->state = TELNET_STATE_DATA; } break; /* negotiation commands */ case TELNET_STATE_WILL: case TELNET_STATE_WONT: case TELNET_STATE_DO: case TELNET_STATE_DONT: _negotiate(telnet, byte); start = i + 1; telnet->state = TELNET_STATE_DATA; break; /* subnegotiation -- determine subnegotiation telopt */ case TELNET_STATE_SB: telnet->sb_telopt = byte; telnet->buffer_pos = 0; telnet->state = TELNET_STATE_SB_DATA; break; /* subnegotiation -- buffer bytes until end request */ case TELNET_STATE_SB_DATA: /* IAC command in subnegotiation -- either IAC SE or IAC IAC */ if (byte == TELNET_IAC) { telnet->state = TELNET_STATE_SB_DATA_IAC; /* buffer the byte, or bail if we can't */ } else if (_buffer_byte(telnet, byte) != TELNET_EOK) { start = i + 1; telnet->state = TELNET_STATE_DATA; } break; /* IAC escaping inside a subnegotiation */ case TELNET_STATE_SB_DATA_IAC: switch (byte) { /* end subnegotiation */ case TELNET_SE: /* return to default state */ start = i + 1; telnet->state = TELNET_STATE_DATA; /* process subnegotiation */ if (_subnegotiate(telnet) != 0) { /* any remaining bytes in the buffer are compressed. * we have to re-invoke telnet_recv to get those * bytes inflated and abort trying to process the * remaining compressed bytes in the current _process * buffer argument */ telnet_recv(telnet, &buffer[start], size - start); return; } break; /* escaped IAC byte */ case TELNET_IAC: /* push IAC into buffer */ if (_buffer_byte(telnet, TELNET_IAC) != TELNET_EOK) { start = i + 1; telnet->state = TELNET_STATE_DATA; } else { telnet->state = TELNET_STATE_SB_DATA; } break; /* something else -- protocol error. attempt to process * content in subnegotiation buffer, then evaluate the * given command as an IAC code. */ default: _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "unexpected byte after IAC inside SB: %d", byte); /* enter IAC state */ start = i + 1; telnet->state = TELNET_STATE_IAC; /* process subnegotiation; see comment in * TELNET_STATE_SB_DATA_IAC about invoking telnet_recv() */ if (_subnegotiate(telnet) != 0) { telnet_recv(telnet, &buffer[start], size - start); return; } else { /* recursive call to get the current input byte processed * as a regular IAC command. we could use a goto, but * that would be gross. */ _process(telnet, (char *)&byte, 1); } break; } break; } } /* pass through any remaining bytes */ if (telnet->state == TELNET_STATE_DATA && i != start) { ev.type = TELNET_EV_DATA; ev.data.buffer = buffer + start; ev.data.size = i - start; telnet->eh(telnet, &ev, telnet->ud); } } /* push a bytes into the state tracker */ void telnet_recv(telnet_t *telnet, const char *buffer, size_t size) { #if defined(HAVE_ZLIB) /* if we have an inflate (decompression) zlib stream, use it */ if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) { char inflate_buffer[1024]; int rs; /* initialize zlib state */ telnet->z->next_in = (unsigned char*)buffer; telnet->z->avail_in = size; telnet->z->next_out = (unsigned char *)inflate_buffer; telnet->z->avail_out = sizeof(inflate_buffer); /* inflate until buffer exhausted and all output is produced */ while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) { /* reset output buffer */ /* decompress */ rs = inflate(telnet->z, Z_SYNC_FLUSH); /* process the decompressed bytes on success */ if (rs == Z_OK || rs == Z_STREAM_END) _process(telnet, inflate_buffer, sizeof(inflate_buffer) - telnet->z->avail_out); else _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1, "inflate() failed: %s", zError(rs)); /* prepare output buffer for next run */ telnet->z->next_out = (unsigned char *)inflate_buffer; telnet->z->avail_out = sizeof(inflate_buffer); /* on error (or on end of stream) disable further inflation */ if (rs != Z_OK) { telnet_event_t ev; /* disable compression */ inflateEnd(telnet->z); free(telnet->z); telnet->z = 0; /* send event */ ev.type = TELNET_EV_COMPRESS; ev.compress.state = 0; telnet->eh(telnet, &ev, telnet->ud); break; } } /* COMPRESS2 is not negotiated, just process */ } else #endif /* defined(HAVE_ZLIB) */ _process(telnet, buffer, size); } /* send an iac command */ void telnet_iac(telnet_t *telnet, unsigned char cmd) { unsigned char bytes[2]; bytes[0] = TELNET_IAC; bytes[1] = cmd; _sendu(telnet, bytes, 2); } /* send negotiation */ void telnet_negotiate(telnet_t *telnet, unsigned char cmd, unsigned char telopt) { telnet_rfc1143_t q; /* if we're in proxy mode, just send it now */ if (telnet->flags & TELNET_FLAG_PROXY) { unsigned char bytes[3]; bytes[0] = TELNET_IAC; bytes[1] = cmd; bytes[2] = telopt; _sendu(telnet, bytes, 3); return; } /* get current option states */ q = _get_rfc1143(telnet, telopt); switch (cmd) { /* advertise willingess to support an option */ case TELNET_WILL: switch (Q_US(q)) { case Q_NO: _set_rfc1143(telnet, telopt, Q_WANTYES, Q_HIM(q)); _send_negotiate(telnet, TELNET_WILL, telopt); break; case Q_WANTNO: _set_rfc1143(telnet, telopt, Q_WANTNO_OP, Q_HIM(q)); break; case Q_WANTYES_OP: _set_rfc1143(telnet, telopt, Q_WANTYES, Q_HIM(q)); break; } break; /* force turn-off of locally enabled option */ case TELNET_WONT: switch (Q_US(q)) { case Q_YES: _set_rfc1143(telnet, telopt, Q_WANTNO, Q_HIM(q)); _send_negotiate(telnet, TELNET_WONT, telopt); break; case Q_WANTYES: _set_rfc1143(telnet, telopt, Q_WANTYES_OP, Q_HIM(q)); break; case Q_WANTNO_OP: _set_rfc1143(telnet, telopt, Q_WANTNO, Q_HIM(q)); break; } break; /* ask remote end to enable an option */ case TELNET_DO: switch (Q_HIM(q)) { case Q_NO: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES); _send_negotiate(telnet, TELNET_DO, telopt); break; case Q_WANTNO: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO_OP); break; case Q_WANTYES_OP: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES); break; } break; /* demand remote end disable an option */ case TELNET_DONT: switch (Q_HIM(q)) { case Q_YES: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO); _send_negotiate(telnet, TELNET_DONT, telopt); break; case Q_WANTYES: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES_OP); break; case Q_WANTNO_OP: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO); break; } break; } } /* send non-command data (escapes IAC bytes) */ void telnet_send(telnet_t *telnet, const char *buffer, size_t size) { size_t i, l; for (l = i = 0; i != size; ++i) { /* dump prior portion of text, send escaped bytes */ if (buffer[i] == (char)TELNET_IAC) { /* dump prior text if any */ if (i != l) { _send(telnet, buffer + l, i - l); } l = i + 1; /* send escape */ telnet_iac(telnet, TELNET_IAC); } } /* send whatever portion of buffer is left */ if (i != l) { _send(telnet, buffer + l, i - l); } } /* send subnegotiation header */ void telnet_begin_sb(telnet_t *telnet, unsigned char telopt) { unsigned char sb[3]; sb[0] = TELNET_IAC; sb[1] = TELNET_SB; sb[2] = telopt; _sendu(telnet, sb, 3); } /* send complete subnegotiation */ void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt, const char *buffer, size_t size) { unsigned char bytes[5]; bytes[0] = TELNET_IAC; bytes[1] = TELNET_SB; bytes[2] = telopt; bytes[3] = TELNET_IAC; bytes[4] = TELNET_SE; _sendu(telnet, bytes, 3); telnet_send(telnet, buffer, size); _sendu(telnet, bytes + 3, 2); #if defined(HAVE_ZLIB) /* if we're a proxy and we just sent the COMPRESS2 marker, we must * make sure all further data is compressed if not already. */ if (telnet->flags & TELNET_FLAG_PROXY && telopt == TELNET_TELOPT_COMPRESS2) { telnet_event_t ev; if (_init_zlib(telnet, 1, 1) != TELNET_EOK) return; /* notify app that compression was enabled */ ev.type = TELNET_EV_COMPRESS; ev.compress.state = 1; telnet->eh(telnet, &ev, telnet->ud); } #endif /* defined(HAVE_ZLIB) */ } void telnet_begin_compress2(telnet_t *telnet) { UNUSED_ARG(telnet); #if defined(HAVE_ZLIB) static const unsigned char compress2[] = { TELNET_IAC, TELNET_SB, TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE }; telnet_event_t ev; /* attempt to create output stream first, bail if we can't */ if (_init_zlib(telnet, 1, 0) != TELNET_EOK) return; /* send compression marker. we send directly to the event handler * instead of passing through _send because _send would result in * the compress marker itself being compressed. */ ev.type = TELNET_EV_SEND; ev.data.buffer = (const char*)compress2; ev.data.size = sizeof(compress2); telnet->eh(telnet, &ev, telnet->ud); /* notify app that compression was successfully enabled */ ev.type = TELNET_EV_COMPRESS; ev.compress.state = 1; telnet->eh(telnet, &ev, telnet->ud); #endif /* defined(HAVE_ZLIB) */ } /* send formatted data with \r and \n translation in addition to IAC IAC */ int telnet_vprintf(telnet_t *telnet, const char *fmt, va_list va) { static const char CRLF[] = { '\r', '\n' }; static const char CRNUL[] = { '\r', '\0' }; char buffer[1024]; char *output = buffer; int rs, i, l; /* format */ rs = vsnprintf(buffer, sizeof(buffer), fmt, va); if ((size_t)rs >= sizeof(buffer)) { output = (char*)malloc(rs + 1); if (output == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "malloc() failed: %s", strerror(errno)); return -1; } rs = vsnprintf(output, rs + 1, fmt, va); } /* send */ for (l = i = 0; i != rs; ++i) { /* special characters */ if (output[i] == (char)TELNET_IAC || output[i] == '\r' || output[i] == '\n') { /* dump prior portion of text */ if (i != l) _send(telnet, output + l, i - l); l = i + 1; /* IAC -> IAC IAC */ if (output[i] == (char)TELNET_IAC) telnet_iac(telnet, TELNET_IAC); /* automatic translation of \r -> CRNUL */ else if (output[i] == '\r') _send(telnet, CRNUL, 2); /* automatic translation of \n -> CRLF */ else if (output[i] == '\n') _send(telnet, CRLF, 2); } } /* send whatever portion of output is left */ if (i != l) { _send(telnet, output + l, i - l); } /* free allocated memory, if any */ if (output != buffer) { free(output); } return rs; } /* see telnet_vprintf */ int telnet_printf(telnet_t *telnet, const char *fmt, ...) { va_list va; int rs; va_start(va, fmt); rs = telnet_vprintf(telnet, fmt, va); va_end(va); return rs; } /* send formatted data through telnet_send */ int telnet_raw_vprintf(telnet_t *telnet, const char *fmt, va_list va) { char buffer[1024]; char *output = buffer; int rs; /* format; allocate more space if necessary */ rs = vsnprintf(buffer, sizeof(buffer), fmt, va); if ((size_t)rs >= sizeof(buffer)) { output = (char*)malloc(rs + 1); if (output == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "malloc() failed: %s", strerror(errno)); return -1; } rs = vsnprintf(output, rs + 1, fmt, va); } /* send out the formatted data */ telnet_send(telnet, output, rs); /* release allocated memory, if any */ if (output != buffer) { free(output); } return rs; } /* see telnet_raw_vprintf */ int telnet_raw_printf(telnet_t *telnet, const char *fmt, ...) { va_list va; int rs; va_start(va, fmt); rs = telnet_raw_vprintf(telnet, fmt, va); va_end(va); return rs; } /* begin NEW-ENVIRON subnegotation */ void telnet_begin_newenviron(telnet_t *telnet, unsigned char cmd) { telnet_begin_sb(telnet, TELNET_TELOPT_NEW_ENVIRON); telnet_send(telnet, (char*)&cmd, 1); } /* send a NEW-ENVIRON value */ void telnet_newenviron_value(telnet_t *telnet, unsigned char type, const char *string) { telnet_send(telnet, (char*)&type, 1); if (string != 0) { telnet_send(telnet, string, strlen(string)); } } /* send TERMINAL-TYPE SEND command */ void telnet_ttype_send(telnet_t *telnet) { static const unsigned char SEND[] = { TELNET_IAC, TELNET_SB, TELNET_TELOPT_TTYPE, TELNET_TTYPE_SEND, TELNET_IAC, TELNET_SE }; _sendu(telnet, SEND, sizeof(SEND)); } /* send TERMINAL-TYPE IS command */ void telnet_ttype_is(telnet_t *telnet, const char* ttype) { static const unsigned char IS[] = { TELNET_IAC, TELNET_SB, TELNET_TELOPT_TTYPE, TELNET_TTYPE_IS }; _sendu(telnet, IS, sizeof(IS)); _send(telnet, ttype, strlen(ttype)); telnet_finish_sb(telnet); } /* send ZMP data */ void telnet_send_zmp(telnet_t *telnet, size_t argc, const char **argv) { size_t i; /* ZMP header */ telnet_begin_zmp(telnet, argv[0]); /* send out each argument, including trailing NUL byte */ for (i = 1; i != argc; ++i) telnet_zmp_arg(telnet, argv[i]); /* ZMP footer */ telnet_finish_zmp(telnet); } /* send ZMP data using varargs */ void telnet_send_vzmpv(telnet_t *telnet, va_list va) { const char* arg; /* ZMP header */ telnet_begin_sb(telnet, TELNET_TELOPT_ZMP); /* send out each argument, including trailing NUL byte */ while ((arg = va_arg(va, const char *)) != 0) telnet_zmp_arg(telnet, arg); /* ZMP footer */ telnet_finish_zmp(telnet); } /* see telnet_send_vzmpv */ void telnet_send_zmpv(telnet_t *telnet, ...) { va_list va; va_start(va, telnet); telnet_send_vzmpv(telnet, va); va_end(va); } /* begin a ZMP command */ void telnet_begin_zmp(telnet_t *telnet, const char *cmd) { telnet_begin_sb(telnet, TELNET_TELOPT_ZMP); telnet_zmp_arg(telnet, cmd); } /* send a ZMP argument */ void telnet_zmp_arg(telnet_t *telnet, const char* arg) { telnet_send(telnet, arg, strlen(arg) + 1); } turnserver-3.2.3.1/src/apps/relay/userdb.c000644 001751 001751 00000175047 12315706777 020350 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include #include #include #include #include #include #include #include #include #include #if !defined(TURN_NO_PQ) #include #endif #if !defined(TURN_NO_MYSQL) #include #endif #if !defined(TURN_NO_HIREDIS) #include "hiredis_libevent2.h" #include #endif #include #include #include #include #include #include #include #include #include "userdb.h" #include "mainrelay.h" #include "ns_turn_utils.h" #include "ns_turn_server.h" #include "ns_turn_maps.h" #include "apputils.h" //////////// USER DB ////////////////////////////// #define LONG_STRING_SIZE (TURN_LONG_STRING_SIZE) static int donot_print_connection_success=0; /////////// SHARED SECRETS ///////////////// void init_secrets_list(secrets_list_t *sl) { if(sl) { ns_bzero(sl,sizeof(secrets_list_t)); } } void clean_secrets_list(secrets_list_t *sl) { if(sl) { if(sl->secrets) { size_t i = 0; for(i = 0;isz;++i) { if(sl->secrets[i]) { turn_free(sl->secrets[i], strlen(sl->secrets[i])+1); } } turn_free(sl->secrets,(sl->sz)*sizeof(char*)); sl->secrets = NULL; sl->sz = 0; } } } size_t get_secrets_list_size(secrets_list_t *sl) { if(sl && sl->secrets) { return sl->sz; } return 0; } const char* get_secrets_list_elem(secrets_list_t *sl, size_t i) { if(get_secrets_list_size(sl)>i) { return sl->secrets[i]; } return NULL; } void add_to_secrets_list(secrets_list_t *sl, const char* elem) { if(sl && elem) { sl->secrets = (char**)realloc(sl->secrets,(sizeof(char*)*(sl->sz+1))); sl->secrets[sl->sz] = strdup(elem); sl->sz += 1; } } /////////// USER DB CHECK ////////////////// static int convert_string_key_to_binary(char* keysource, hmackey_t key, size_t sz) { { char is[3]; size_t i; unsigned int v; is[2]=0; for(i=0;i, connection string format error: %s\n",turn_params.users_params.userdb,errmsg); turn_free(errmsg,strlen(errmsg)+1); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection: <%s>, unknown connection string format error\n",turn_params.users_params.userdb); } } else { PQconninfoFree(co); if(errmsg) turn_free(errmsg,strlen(errmsg)+1); pqdbconnection = PQconnectdb(turn_params.users_params.userdb); if(!pqdbconnection) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection: <%s>, runtime error\n",turn_params.users_params.userdb); } else { ConnStatusType status = PQstatus(pqdbconnection); if(status != CONNECTION_OK) { PQfinish(pqdbconnection); pqdbconnection = NULL; TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection: <%s>, runtime error\n",turn_params.users_params.userdb); } else if(!donot_print_connection_success){ TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "PostgreSQL DB connection success: %s\n",turn_params.users_params.userdb); } } } } return pqdbconnection; } #endif #if !defined(TURN_NO_MYSQL) struct _Myconninfo { char *host; char *dbname; char *user; char *password; unsigned int port; unsigned int connect_timeout; }; typedef struct _Myconninfo Myconninfo; static void MyconninfoFree(Myconninfo *co) { if(co) { if(co->host) turn_free(co->host,strlen(co->host)+1); if(co->dbname) turn_free(co->dbname, strlen(co->dbname)+1); if(co->user) turn_free(co->user, strlen(co->user)+1); if(co->password) turn_free(co->password, strlen(co->password)+1); ns_bzero(co,sizeof(Myconninfo)); } } static Myconninfo *MyconninfoParse(char *userdb, char **errmsg) { Myconninfo *co = (Myconninfo*)turn_malloc(sizeof(Myconninfo)); ns_bzero(co,sizeof(Myconninfo)); if(userdb) { char *s0=strdup(userdb); char *s = s0; while(s && *s) { while(*s && (*s==' ')) ++s; char *snext = strstr(s," "); if(snext) { *snext = 0; ++snext; } char* seq = strstr(s,"="); if(!seq) { MyconninfoFree(co); co = NULL; if(errmsg) { *errmsg = strdup(s); } break; } *seq = 0; if(!strcmp(s,"host")) co->host = strdup(seq+1); else if(!strcmp(s,"ip")) co->host = strdup(seq+1); else if(!strcmp(s,"addr")) co->host = strdup(seq+1); else if(!strcmp(s,"ipaddr")) co->host = strdup(seq+1); else if(!strcmp(s,"hostaddr")) co->host = strdup(seq+1); else if(!strcmp(s,"dbname")) co->dbname = strdup(seq+1); else if(!strcmp(s,"db")) co->dbname = strdup(seq+1); else if(!strcmp(s,"database")) co->dbname = strdup(seq+1); else if(!strcmp(s,"user")) co->user = strdup(seq+1); else if(!strcmp(s,"uname")) co->user = strdup(seq+1); else if(!strcmp(s,"name")) co->user = strdup(seq+1); else if(!strcmp(s,"username")) co->user = strdup(seq+1); else if(!strcmp(s,"password")) co->password = strdup(seq+1); else if(!strcmp(s,"pwd")) co->password = strdup(seq+1); else if(!strcmp(s,"passwd")) co->password = strdup(seq+1); else if(!strcmp(s,"secret")) co->password = strdup(seq+1); else if(!strcmp(s,"port")) co->port = (unsigned int)atoi(seq+1); else if(!strcmp(s,"p")) co->port = (unsigned int)atoi(seq+1); else if(!strcmp(s,"connect_timeout")) co->connect_timeout = (unsigned int)atoi(seq+1); else if(!strcmp(s,"timeout")) co->connect_timeout = (unsigned int)atoi(seq+1); else { MyconninfoFree(co); co = NULL; if(errmsg) { *errmsg = strdup(s); } break; } s = snext; } turn_free(s0, strlen(s0)+1); } if(!(co->dbname)) co->dbname=strdup("0"); if(!(co->host)) co->host=strdup("127.0.0.1"); if(!(co->user)) co->user=strdup(""); if(!(co->password)) co->password=strdup(""); return co; } static MYSQL *get_mydb_connection(void) { static MYSQL *mydbconnection = NULL; if(mydbconnection) { if(mysql_ping(mydbconnection)) { mysql_close(mydbconnection); mydbconnection=NULL; } } if(!mydbconnection && is_mysql_userdb()) { char *errmsg=NULL; Myconninfo *co=MyconninfoParse(turn_params.users_params.userdb, &errmsg); if(!co) { if(errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open MySQL DB connection <%s>, connection string format error: %s\n",turn_params.users_params.userdb,errmsg); turn_free(errmsg,strlen(errmsg)+1); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open MySQL DB connection <%s>, connection string format error\n",turn_params.users_params.userdb); } } else if(errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open MySQL DB connection <%s>, connection string format error: %s\n",turn_params.users_params.userdb,errmsg); turn_free(errmsg,strlen(errmsg)+1); MyconninfoFree(co); } else if(!(co->dbname)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "MySQL Database name is not provided: <%s>\n",turn_params.users_params.userdb); MyconninfoFree(co); } else { mydbconnection = mysql_init(NULL); if(!mydbconnection) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot initialize MySQL DB connection\n"); } else { if(co->connect_timeout) mysql_options(mydbconnection,MYSQL_OPT_CONNECT_TIMEOUT,&(co->connect_timeout)); MYSQL *conn = mysql_real_connect(mydbconnection, co->host, co->user, co->password, co->dbname, co->port, NULL, CLIENT_IGNORE_SIGPIPE); if(!conn) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open MySQL DB connection: <%s>, runtime error\n",turn_params.users_params.userdb); mysql_close(mydbconnection); mydbconnection=NULL; } else if(mysql_select_db(mydbconnection, co->dbname)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot connect to MySQL DB: %s\n",co->dbname); mysql_close(mydbconnection); mydbconnection=NULL; } else if(!donot_print_connection_success) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MySQL DB connection success: %s\n",turn_params.users_params.userdb); } } MyconninfoFree(co); } } return mydbconnection; } #endif #if !defined(TURN_NO_HIREDIS) static void turnFreeRedisReply(void *reply) { if(reply) { freeReplyObject(reply); } } struct _Ryconninfo { char *host; char *dbname; char *password; unsigned int connect_timeout; unsigned int port; }; typedef struct _Ryconninfo Ryconninfo; static void RyconninfoFree(Ryconninfo *co) { if(co) { if(co->host) turn_free(co->host, strlen(co->host)+1); if(co->dbname) turn_free(co->dbname, strlen(co->username)+1); if(co->password) turn_free(co->password, strlen(co->password)+1); ns_bzero(co,sizeof(Ryconninfo)); } } static Ryconninfo *RyconninfoParse(char *userdb, char **errmsg) { Ryconninfo *co = (Ryconninfo*) turn_malloc(sizeof(Ryconninfo)); ns_bzero(co,sizeof(Ryconninfo)); if (userdb) { char *s0 = strdup(userdb); char *s = s0; while (s && *s) { while (*s && (*s == ' ')) ++s; char *snext = strstr(s, " "); if (snext) { *snext = 0; ++snext; } char* seq = strstr(s, "="); if (!seq) { RyconninfoFree(co); co = NULL; if (errmsg) { *errmsg = strdup(s); } break; } *seq = 0; if (!strcmp(s, "host")) co->host = strdup(seq + 1); else if (!strcmp(s, "ip")) co->host = strdup(seq + 1); else if (!strcmp(s, "addr")) co->host = strdup(seq + 1); else if (!strcmp(s, "ipaddr")) co->host = strdup(seq + 1); else if (!strcmp(s, "hostaddr")) co->host = strdup(seq + 1); else if (!strcmp(s, "dbname")) co->dbname = strdup(seq + 1); else if (!strcmp(s, "db")) co->dbname = strdup(seq + 1); else if (!strcmp(s, "database")) co->dbname = strdup(seq + 1); else if (!strcmp(s, "user")) ; else if (!strcmp(s, "uname")) ; else if (!strcmp(s, "name")) ; else if (!strcmp(s, "username")) ; else if (!strcmp(s, "password")) co->password = strdup(seq + 1); else if (!strcmp(s, "pwd")) co->password = strdup(seq + 1); else if (!strcmp(s, "passwd")) co->password = strdup(seq + 1); else if (!strcmp(s, "secret")) co->password = strdup(seq + 1); else if (!strcmp(s, "port")) co->port = (unsigned int) atoi(seq + 1); else if (!strcmp(s, "p")) co->port = (unsigned int) atoi(seq + 1); else if (!strcmp(s, "connect_timeout")) co->connect_timeout = (unsigned int) atoi(seq + 1); else if (!strcmp(s, "timeout")) co->connect_timeout = (unsigned int) atoi(seq + 1); else { RyconninfoFree(co); co = NULL; if (errmsg) { *errmsg = strdup(s); } break; } s = snext; } turn_free(s0, strlen(s0)+1); } if(!(co->dbname)) co->dbname=strdup("0"); if(!(co->host)) co->host=strdup("127.0.0.1"); if(!(co->password)) co->password=strdup(""); return co; } redis_context_handle get_redis_async_connection(struct event_base *base, char* connection_string) { redis_context_handle ret = NULL; char *errmsg = NULL; Ryconninfo *co = RyconninfoParse(connection_string, &errmsg); if (!co) { if (errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open Redis DB connection <%s>, connection string format error: %s\n", turn_params.users_params.userdb, errmsg); turn_free(errmsg,strlen(errmsg)+1); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open Redis DB connection <%s>, connection string format error\n", turn_params.users_params.userdb); } } else if (errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open Redis DB connection <%s>, connection string format error: %s\n", turn_params.users_params.userdb, errmsg); turn_free(errmsg,strlen(errmsg)+1); RyconninfoFree(co); } else { ret = redisLibeventAttach(base, co->host, co->port, co->password, atoi(co->dbname)); if (!ret) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot initialize Redis DB connection\n"); } else { if (!donot_print_connection_success) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Redis DB async connection success: %s\n", connection_string); } } RyconninfoFree(co); } return ret; } static redisContext *get_redis_connection(void) { static redisContext *redisconnection = NULL; if (!redisconnection && is_redis_userdb()) { char *errmsg = NULL; Ryconninfo *co = RyconninfoParse(turn_params.users_params.userdb, &errmsg); if (!co) { if (errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open Redis DB connection <%s>, connection string format error: %s\n", turn_params.users_params.userdb, errmsg); turn_free(errmsg,strlen(errmsg)+1); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open Redis DB connection <%s>, connection string format error\n", turn_params.users_params.userdb); } } else if (errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open Redis DB connection <%s>, connection string format error: %s\n", turn_params.users_params.userdb, errmsg); turn_free(errmsg,strlen(errmsg)+1); RyconninfoFree(co); } else { char ip[256] = "\0"; int port = DEFAULT_REDIS_PORT; if (co->host) STRCPY(ip,co->host); if (!ip[0]) STRCPY(ip,"127.0.0.1"); if (co->port) port = (int) (co->port); if (co->connect_timeout) { struct timeval tv; tv.tv_usec = 0; tv.tv_sec = (time_t) (co->connect_timeout); redisconnection = redisConnectWithTimeout(ip, port, tv); } else { redisconnection = redisConnect(ip, port); } if (!redisconnection) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot initialize Redis DB connection\n"); } else { if (co->password) { turnFreeRedisReply(redisCommand(redisconnection, "AUTH %s", co->password)); } if (co->dbname) { turnFreeRedisReply(redisCommand(redisconnection, "select %s", co->dbname)); } if (!donot_print_connection_success) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Redis DB sync connection success: %s\n", turn_params.users_params.userdb); } } RyconninfoFree(co); } } return redisconnection; } #endif static int get_auth_secrets(secrets_list_t *sl) { int ret = -1; clean_secrets_list(sl); if(get_secrets_list_size(&turn_params.users_params.static_auth_secrets)) { size_t i = 0; for(i=0;itype == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { size_t i; for (i = 0; i < reply->elements; ++i) { add_to_secrets_list(&keys,reply->element[i]->str); } } for(isz=0;isztype == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); else if (rget->type != REDIS_REPLY_STRING) { if (rget->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); } else { add_to_secrets_list(sl,rget->str); } turnFreeRedisReply(rget); } } clean_secrets_list(&keys); ret = 0; turnFreeRedisReply(reply); } } #endif return ret; } /* * Timestamp retrieval */ static turn_time_t get_rest_api_timestamp(char *usname) { turn_time_t ts = 0; int ts_set = 0; char *col = strchr(usname,turn_params.users_params.rest_api_separator); if(col) { if(col == usname) { usname +=1; } else { char *ptr = usname; int found_non_figure = 0; while(ptr < col) { if(!(ptr[0]>='0' && ptr[0]<='9')) { found_non_figure=1; break; } ++ptr; } if(found_non_figure) { ts = (turn_time_t)atol(col+1); ts_set = 1; } else { *col=0; ts = (turn_time_t)atol(usname); ts_set = 1; *col=turn_params.users_params.rest_api_separator; } } } if(!ts_set) { ts = (turn_time_t)atol(usname); } return ts; } static char *get_real_username(char *usname) { if(turn_params.users_params.use_auth_secret_with_timestamp) { char *col=strchr(usname,turn_params.users_params.rest_api_separator); if(col) { if(col == usname) { usname +=1; } else { char *ptr = usname; int found_non_figure = 0; while(ptr < col) { if(!(ptr[0]>='0' && ptr[0]<='9')) { found_non_figure=1; break; } ++ptr; } if(!found_non_figure) { usname = col+1; } else { *col=0; usname = strdup(usname); *col=turn_params.users_params.rest_api_separator; return usname; } } } } return strdup(usname); } /* * Long-term mechanism password retrieval */ int get_user_key(u08bits *usname, hmackey_t key, ioa_network_buffer_handle nbh) { int ret = -1; if(turn_params.users_params.use_auth_secret_with_timestamp) { turn_time_t ctime = (turn_time_t) time(NULL); turn_time_t ts = 0; secrets_list_t sl; size_t sll = 0; init_secrets_list(&sl); if(get_auth_secrets(&sl)<0) return ret; ts = get_rest_api_timestamp((char*)usname); if(!turn_time_before(ts, ctime)) { u08bits hmac[MAXSHASIZE]; unsigned int hmac_len; st_password_t pwdtmp; hmac[0] = 0; stun_attr_ref sar = stun_attr_get_first_by_type_str(ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh), STUN_ATTRIBUTE_MESSAGE_INTEGRITY); if (!sar) return -1; int sarlen = stun_attr_get_len(sar); switch(sarlen) { case SHA1SIZEBYTES: if(turn_params.shatype != SHATYPE_SHA1) return -1; hmac_len = SHA1SIZEBYTES; break; case SHA256SIZEBYTES: if(turn_params.shatype != SHATYPE_SHA256) return -1; hmac_len = SHA256SIZEBYTES; break; default: return -1; }; for(sll=0;sll=0) { size_t pwd_length = 0; char *pwd = base64_encode(hmac,hmac_len,&pwd_length); if(pwd) { if(pwd_length<1) { turn_free(pwd,strlen(pwd)+1); } else { if(stun_produce_integrity_key_str((u08bits*)usname, (u08bits*)turn_params.users_params.global_realm, (u08bits*)pwd, key, turn_params.shatype)>=0) { if(stun_check_message_integrity_by_key_str(TURN_CREDENTIALS_LONG_TERM, ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh), key, pwdtmp, turn_params.shatype,NULL)>0) { ret = 0; } } turn_free(pwd,pwd_length); if(ret==0) break; } } } } } } clean_secrets_list(&sl); return ret; } ur_string_map_value_type ukey = NULL; ur_string_map_lock(turn_params.users_params.users.static_accounts); if(ur_string_map_get(turn_params.users_params.users.static_accounts, (ur_string_map_key_type)usname, &ukey)) { ret = 0; } else { ur_string_map_lock(turn_params.users_params.users.dynamic_accounts); if(ur_string_map_get(turn_params.users_params.users.dynamic_accounts, (ur_string_map_key_type)usname, &ukey)) { ret = 0; } ur_string_map_unlock(turn_params.users_params.users.dynamic_accounts); } ur_string_map_unlock(turn_params.users_params.users.static_accounts); if(ret==0) { size_t sz = get_hmackey_size(turn_params.shatype); ns_bcopy(ukey,key,sz); return 0; } #if !defined(TURN_NO_PQ) { PGconn * pqc = get_pqdb_connection(); if(pqc) { char statement[LONG_STRING_SIZE]; snprintf(statement,sizeof(statement),"select hmackey from turnusers_lt where name='%s'",usname); PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res)!=1)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc)); } else { char *kval = PQgetvalue(res,0,0); int len = PQgetlength(res,0,0); if(kval) { size_t sz = get_hmackey_size(turn_params.shatype); if(((size_t)lentype == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); else if (rget->type != REDIS_REPLY_STRING) { if (rget->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); } else { size_t sz = get_hmackey_size(turn_params.shatype); if(strlen(rget->str)str,usname); } else if(convert_string_key_to_binary(rget->str, key, sz)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key: %s, user %s\n",rget->str,usname); } else { ret = 0; } } turnFreeRedisReply(rget); } if(ret != 0) { snprintf(s,sizeof(s),"get turn/user/%s/password", usname); rget = (redisReply *)redisCommand(rc, s); if(rget) { if (rget->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); else if (rget->type != REDIS_REPLY_STRING) { if (rget->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); } else { if(stun_produce_integrity_key_str((u08bits*)usname, (u08bits*)turn_params.users_params.global_realm, (u08bits*)rget->str, key, turn_params.shatype)>=0) { ret = 0; } } turnFreeRedisReply(rget); } } } } #endif return ret; } /* * Short-term mechanism password retrieval */ int get_user_pwd(u08bits *usname, st_password_t pwd) { int ret = -1; UNUSED_ARG(pwd); char statement[LONG_STRING_SIZE]; snprintf(statement,sizeof(statement),"select password from turnusers_st where name='%s'",usname); { #if !defined(TURN_NO_PQ) PGconn * pqc = get_pqdb_connection(); if(pqc) { PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res)!=1)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc)); } else { char *kval = PQgetvalue(res,0,0); if(kval) { strncpy((char*)pwd,kval,sizeof(st_password_t)); ret = 0; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong password data for user %s: NULL\n",usname); } } if(res) { PQclear(res); } } #endif #if !defined(TURN_NO_MYSQL) MYSQL * myc = get_mydb_connection(); if(myc) { int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { MYSQL_ROW row = mysql_fetch_row(mres); if(row && row[0]) { unsigned long *lengths = mysql_fetch_lengths(mres); if(lengths) { if(lengths[0]<1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong password data for user %s, size in MySQL DB is zero(0)\n",usname); } else { ns_bcopy(row[0],pwd,lengths[0]); pwd[lengths[0]]=0; ret = 0; } } } } if(mres) mysql_free_result(mres); } } #endif #if !defined(TURN_NO_HIREDIS) { redisContext * rc = get_redis_connection(); if(rc) { char s[LONG_STRING_SIZE]; snprintf(s,sizeof(s),"get turn/user/%s/password", usname); redisReply *rget = (redisReply *)redisCommand(rc, s); if(rget) { if (rget->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); else if (rget->type != REDIS_REPLY_STRING) { if (rget->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); } else { strncpy((char*)pwd,rget->str,SHORT_TERM_PASSWORD_SIZE); pwd[SHORT_TERM_PASSWORD_SIZE]=0; ret = 0; } turnFreeRedisReply(rget); } } } #endif } return ret; } u08bits *start_user_check(turnserver_id id, u08bits *usname, get_username_resume_cb resume, ioa_net_data *in_buffer, u64bits ctxkey, int *postpone_reply) { *postpone_reply = 1; struct auth_message am; ns_bzero(&am,sizeof(struct auth_message)); am.id = id; STRCPY(am.username,usname); am.resume_func = resume; memcpy(&(am.in_buffer),in_buffer,sizeof(ioa_net_data)); in_buffer->nbh = NULL; am.ctxkey = ctxkey; send_auth_message_to_auth_server(&am); return NULL; } int check_new_allocation_quota(u08bits *user) { int ret = 0; if (user) { u08bits *username = (u08bits*)get_real_username((char*)user); ur_string_map_lock(turn_params.users_params.users.alloc_counters); if (turn_params.users_params.users.total_quota && (turn_params.users_params.users.total_current_allocs >= turn_params.users_params.users.total_quota)) { ret = -1; } else { ur_string_map_value_type value = 0; if (!ur_string_map_get(turn_params.users_params.users.alloc_counters, (ur_string_map_key_type) username, &value)) { value = (ur_string_map_value_type) 1; ur_string_map_put(turn_params.users_params.users.alloc_counters, (ur_string_map_key_type) username, value); ++(turn_params.users_params.users.total_current_allocs); } else { if ((turn_params.users_params.users.user_quota) && ((size_t) value >= (size_t)(turn_params.users_params.users.user_quota))) { ret = -1; } else { value = (ur_string_map_value_type)(((size_t)value) + 1); ur_string_map_put(turn_params.users_params.users.alloc_counters, (ur_string_map_key_type) username, value); ++(turn_params.users_params.users.total_current_allocs); } } } turn_free(username,strlen(username)+1); ur_string_map_unlock(turn_params.users_params.users.alloc_counters); } return ret; } void release_allocation_quota(u08bits *user) { if (user) { u08bits *username = (u08bits*)get_real_username((char*)user); ur_string_map_lock(turn_params.users_params.users.alloc_counters); ur_string_map_value_type value = 0; ur_string_map_get(turn_params.users_params.users.alloc_counters, (ur_string_map_key_type) username, &value); if (value) { value = (ur_string_map_value_type)(((size_t)value) - 1); ur_string_map_put(turn_params.users_params.users.alloc_counters, (ur_string_map_key_type) username, value); } if (turn_params.users_params.users.total_current_allocs) --(turn_params.users_params.users.total_current_allocs); ur_string_map_unlock(turn_params.users_params.users.alloc_counters); turn_free(username, strlen(username)+1); } } ////////////////////////////////// void read_userdb_file(int to_print) { static char *full_path_to_userdb_file = NULL; static int first_read = 1; static turn_time_t mtime = 0; if(turn_params.users_params.userdb_type != TURN_USERDB_TYPE_FILE) return; if(turn_params.users_params.use_auth_secret_with_timestamp) return; FILE *f = NULL; if(full_path_to_userdb_file) { struct stat sb; if(stat(full_path_to_userdb_file,&sb)<0) { perror("File statistics"); } else { turn_time_t newmtime = (turn_time_t)(sb.st_mtime); if(mtime == newmtime) return; mtime = newmtime; } } if (!full_path_to_userdb_file) full_path_to_userdb_file = find_config_file(turn_params.users_params.userdb, first_read); if (full_path_to_userdb_file) f = fopen(full_path_to_userdb_file, "r"); if (f) { char sbuf[LONG_STRING_SIZE]; ur_string_map_lock(turn_params.users_params.users.dynamic_accounts); ur_string_map_clean(turn_params.users_params.users.dynamic_accounts); for (;;) { char *s = fgets(sbuf, sizeof(sbuf) - 1, f); if (!s) break; s = skip_blanks(s); if (s[0] == '#') continue; if (!s[0]) continue; size_t slen = strlen(s); while (slen && (s[slen - 1] == 10 || s[slen - 1] == 13)) s[--slen] = 0; if (slen) { if(to_print) { char* sc=strstr(s,":"); if(sc) sc[0]=0; printf("%s\n",s); } else { add_user_account(s,1); } } } ur_string_map_unlock(turn_params.users_params.users.dynamic_accounts); fclose(f); } else if (first_read) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: Cannot find userdb file: %s: going without flat file user database.\n", turn_params.users_params.userdb); } first_read = 0; } int add_user_account(char *user, int dynamic) { if(user && !turn_params.users_params.use_auth_secret_with_timestamp) { char *s = strstr(user, ":"); if(!s || (s==user) || (strlen(s)<2)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user account: %s\n",user); } else { size_t ulen = s-user; char *usname = (char*)turn_malloc(sizeof(char)*(ulen+1)); strncpy(usname,user,ulen); usname[ulen]=0; if(SASLprep((u08bits*)usname)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user name: %s\n",user); turn_free(usname,sizeof(char)*(ulen+1)); return -1; } s = skip_blanks(s+1); hmackey_t *key = (hmackey_t*)turn_malloc(sizeof(hmackey_t)); if(strstr(s,"0x")==s) { char *keysource = s + 2; size_t sz = get_hmackey_size(turn_params.shatype); if(strlen(keysource)type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { size_t i; for (i = 0; i < reply->elements; ++i) { add_to_secrets_list(&keys,reply->element[i]->str); } } turnFreeRedisReply(reply); } } reply = (redisReply*)redisCommand(rc, "keys turn/user/*/password"); if(reply) { if (reply->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { size_t i; for (i = 0; i < reply->elements; ++i) { add_to_secrets_list(&keys,reply->element[i]->str); } } turnFreeRedisReply(reply); } for(isz=0;isztype == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { size_t i; for (i = 0; i < reply->elements; ++i) { add_to_secrets_list(&keys,reply->element[i]->str); } } for(isz=0;isztype == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); else if (rget->type != REDIS_REPLY_STRING) { if (rget->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); } else { printf("%s\n",rget->str); } } turnFreeRedisReply(rget); } clean_secrets_list(&keys); turnFreeRedisReply(reply); } } #endif } return 0; } static int del_secret(u08bits *secret) { UNUSED_ARG(secret); donot_print_connection_success=1; if (is_pqsql_userdb()) { #if !defined(TURN_NO_PQ) char statement[LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if (pqc) { if(!secret || (secret[0]==0)) snprintf(statement,sizeof(statement),"delete from turn_secret"); else snprintf(statement,sizeof(statement),"delete from turn_secret where value='%s'",secret); PGresult *res = PQexec(pqc, statement); if (res) { PQclear(res); } } #endif } else if (is_mysql_userdb()) { #if !defined(TURN_NO_MYSQL) char statement[LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if (myc) { if(!secret || (secret[0]==0)) snprintf(statement,sizeof(statement),"delete from turn_secret"); else snprintf(statement,sizeof(statement),"delete from turn_secret where value='%s'",secret); mysql_query(myc, statement); } #endif } else if(is_redis_userdb()) { #if !defined(TURN_NO_HIREDIS) redisContext *rc = get_redis_connection(); if(rc) { redisReply *reply = (redisReply*)redisCommand(rc, "keys turn/secret/*"); if(reply) { secrets_list_t keys; size_t isz = 0; char s[LONG_STRING_SIZE]; init_secrets_list(&keys); if (reply->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { size_t i; for (i = 0; i < reply->elements; ++i) { add_to_secrets_list(&keys,reply->element[i]->str); } } for(isz=0;isztype == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); else if (rget->type != REDIS_REPLY_STRING) { if (rget->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); } else { if(!strcmp((char*)secret,rget->str)) { snprintf(s,sizeof(s),"del %s", keys.secrets[isz]); turnFreeRedisReply(redisCommand(rc, s)); } } turnFreeRedisReply(rget); } } } turnFreeRedisReply(redisCommand(rc, "save")); clean_secrets_list(&keys); turnFreeRedisReply(reply); } } #endif } return 0; } static int set_secret(u08bits *secret) { if(!secret || (secret[0]==0)) return 0; donot_print_connection_success = 1; del_secret(secret); if (is_pqsql_userdb()) { #if !defined(TURN_NO_PQ) char statement[LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if (pqc) { snprintf(statement,sizeof(statement),"insert into turn_secret values('%s')",secret); PGresult *res = PQexec(pqc, statement); if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "Error inserting/updating secret key information: %s\n", PQerrorMessage(pqc)); } if (res) { PQclear(res); } } #endif } else if (is_mysql_userdb()) { #if !defined(TURN_NO_MYSQL) char statement[LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if (myc) { snprintf(statement,sizeof(statement),"insert into turn_secret values('%s')",secret); int res = mysql_query(myc, statement); if (res) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "Error inserting/updating secret key information: %s\n", mysql_error(myc)); } } #endif } else if(is_redis_userdb()) { #if !defined(TURN_NO_HIREDIS) redisContext *rc = get_redis_connection(); if(rc) { char s[LONG_STRING_SIZE]; del_secret(secret); snprintf(s,sizeof(s),"set turn/secret/%lu %s", (unsigned long)turn_time(), secret); turnFreeRedisReply(redisCommand(rc, s)); turnFreeRedisReply(redisCommand(rc, "save")); } #endif } return 0; } int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, TURNADMIN_COMMAND_TYPE ct, int is_st) { hmackey_t key; char skey[sizeof(hmackey_t)*2+1]; donot_print_connection_success = 1; st_password_t passwd; if(ct == TA_LIST_USERS) { return list_users(is_st); } if(ct == TA_SHOW_SECRET) { return show_secret(); } if(ct == TA_SET_SECRET) { return set_secret(secret); } if(ct == TA_DEL_SECRET) { return del_secret(secret); } if(ct != TA_DELETE_USER) { if(is_st) { strncpy((char*)passwd,(char*)pwd,sizeof(st_password_t)); } else { if(!(realm[0])) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: with long-term mechanism, you must specify the realm !\n"); exit(-1); } stun_produce_integrity_key_str(user, realm, pwd, key, turn_params.shatype); size_t i = 0; size_t sz = get_hmackey_size(turn_params.shatype); int maxsz = (int)(sz*2)+1; char *s=skey; for(i=0;(i2);i++) { snprintf(s,(size_t)(sz*2),"%02x",(unsigned int)key[i]); maxsz-=2; s+=2; } skey[sz*2]=0; } } if(ct == TA_PRINT_KEY) { if(!is_st) { printf("0x%s\n",skey); } } else if(is_pqsql_userdb()){ #if !defined(TURN_NO_PQ) char statement[LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if(pqc) { if(ct == TA_DELETE_USER) { if(is_st) { snprintf(statement,sizeof(statement),"delete from turnusers_st where name='%s'",user); } else { snprintf(statement,sizeof(statement),"delete from turnusers_lt where name='%s'",user); } PGresult *res = PQexec(pqc, statement); if(res) { PQclear(res); } } if(ct == TA_UPDATE_USER) { if(is_st) { snprintf(statement,sizeof(statement),"insert into turnusers_st values('%s','%s')",user,passwd); } else { snprintf(statement,sizeof(statement),"insert into turnusers_lt values('%s','%s')",user,skey); } PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { if(res) { PQclear(res); } if(is_st) { snprintf(statement,sizeof(statement),"update turnusers_st set password='%s' where name='%s'",passwd,user); } else { snprintf(statement,sizeof(statement),"update turnusers_lt set hmackey='%s' where name='%s'",skey,user); } res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user information: %s\n",PQerrorMessage(pqc)); } } if(res) { PQclear(res); } } } #endif } else if(is_mysql_userdb()){ #if !defined(TURN_NO_MYSQL) char statement[LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if(myc) { if(ct == TA_DELETE_USER) { if(is_st) { snprintf(statement,sizeof(statement),"delete from turnusers_st where name='%s'",user); } else { snprintf(statement,sizeof(statement),"delete from turnusers_lt where name='%s'",user); } int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting user key information: %s\n",mysql_error(myc)); } } if(ct == TA_UPDATE_USER) { if(is_st) { snprintf(statement,sizeof(statement),"insert into turnusers_st values('%s','%s')",user,passwd); } else { snprintf(statement,sizeof(statement),"insert into turnusers_lt values('%s','%s')",user,skey); } int res = mysql_query(myc, statement); if(res) { if(is_st) { snprintf(statement,sizeof(statement),"update turnusers_st set password='%s' where name='%s'",passwd,user); } else { snprintf(statement,sizeof(statement),"update turnusers_lt set hmackey='%s' where name='%s'",skey,user); } res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user key information: %s\n",mysql_error(myc)); } } } } #endif } else if(is_redis_userdb()) { #if !defined(TURN_NO_HIREDIS) redisContext *rc = get_redis_connection(); if(rc) { char statement[LONG_STRING_SIZE]; if(ct == TA_DELETE_USER) { if(!is_st) { snprintf(statement,sizeof(statement),"del turn/user/%s/key",user); turnFreeRedisReply(redisCommand(rc, statement)); } snprintf(statement,sizeof(statement),"del turn/user/%s/password",user); turnFreeRedisReply(redisCommand(rc, statement)); } if(ct == TA_UPDATE_USER) { if(is_st) { snprintf(statement,sizeof(statement),"set turn/user/%s/password %s",user,passwd); } else { snprintf(statement,sizeof(statement),"set turn/user/%s/key %s",user,skey); } turnFreeRedisReply(redisCommand(rc, statement)); } turnFreeRedisReply(redisCommand(rc, "save")); } #endif } else if(!is_st) { char *full_path_to_userdb_file = find_config_file(turn_params.users_params.userdb, 1); FILE *f = full_path_to_userdb_file ? fopen(full_path_to_userdb_file,"r") : NULL; int found = 0; char us[LONG_STRING_SIZE]; size_t i = 0; char **content = NULL; size_t csz = 0; STRCPY(us, (char*) user); strncpy(us + strlen(us), ":", sizeof(us)-1-strlen(us)); us[sizeof(us)-1]=0; if (!f) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "File %s not found, will be created.\n",turn_params.users_params.userdb); } else { char sarg[LONG_STRING_SIZE]; char sbuf[LONG_STRING_SIZE]; for (;;) { char *s0 = fgets(sbuf, sizeof(sbuf) - 1, f); if (!s0) break; size_t slen = strlen(s0); while (slen && (s0[slen - 1] == 10 || s0[slen - 1] == 13)) s0[--slen] = 0; char *s = skip_blanks(s0); if (s[0] == '#') goto add_and_cont; if (!s[0]) goto add_and_cont; STRCPY(sarg, s); if (strstr(sarg, us) == sarg) { if (ct == TA_DELETE_USER) continue; if (found) continue; found = 1; STRCPY(us, (char*) user); strncpy(us + strlen(us), ":0x", sizeof(us)-1-strlen(us)); us[sizeof(us)-1]=0; size_t sz = get_hmackey_size(turn_params.shatype); for (i = 0; i < sz; i++) { snprintf( us + strlen(us), sizeof(us)-strlen(us), "%02x", (unsigned int) key[i]); } s0 = us; } add_and_cont: content = (char**)realloc(content, sizeof(char*) * (++csz)); content[csz - 1] = strdup(s0); } fclose(f); } if(!found && (ct == TA_UPDATE_USER)) { STRCPY(us,(char*)user); strncpy(us+strlen(us),":0x",sizeof(us)-1-strlen(us)); us[sizeof(us)-1]=0; size_t sz = get_hmackey_size(turn_params.shatype); for(i=0;itype == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { size_t i; for (i = 0; i < reply->elements; ++i) { add_to_secrets_list(&keys,reply->element[i]->str); } } for(isz=0;isztype == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); else if (rget->type != REDIS_REPLY_STRING) { if (rget->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); } else { add_ip_list_range(rget->str,ret); } turnFreeRedisReply(rget); } } clean_secrets_list(&keys); turnFreeRedisReply(reply); } } #endif return ret; } static void ip_list_free(ip_range_list_t *l) { if(l) { size_t i; for(i=0;iranges_number;++i) { if(l->ranges && l->ranges[i]) free(l->ranges[i]); if(l->encaddrsranges && l->encaddrsranges[i]) free(l->encaddrsranges[i]); } if(l->ranges) free(l->ranges); if(l->encaddrsranges) free(l->encaddrsranges); free(l); } } void update_white_and_black_lists(void) { { ip_range_list_t *wl = get_ip_list("allowed"); ip_range_list_t *owl = NULL; ioa_wrlock_whitelist(NULL); owl = ipwhitelist; ipwhitelist = wl; ioa_unlock_whitelist(NULL); ip_list_free(owl); } { ip_range_list_t *bl = get_ip_list("denied"); ip_range_list_t *obl = NULL; ioa_wrlock_blacklist(NULL); obl = ipblacklist; ipblacklist = bl; ioa_unlock_blacklist(NULL); ip_list_free(obl); } } /////////////// add ACL record /////////////////// int add_ip_list_range(char* range, ip_range_list_t * list) { char* separator = strchr(range, '-'); if (separator) { *separator = '\0'; } ioa_addr min, max; if (make_ioa_addr((const u08bits*) range, 0, &min) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address format: %s\n", range); return -1; } if (separator) { if (make_ioa_addr((const u08bits*) separator + 1, 0, &max) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address format: %s\n", separator + 1); return -1; } } else { // Doesn't have a '-' character in it, so assume that this is a single address addr_cpy(&max, &min); } if (separator) *separator = '-'; ++(list->ranges_number); list->ranges = (char**) realloc(list->ranges, sizeof(char*) * list->ranges_number); list->ranges[list->ranges_number - 1] = strdup(range); list->encaddrsranges = (ioa_addr_range**) realloc(list->encaddrsranges, sizeof(ioa_addr_range*) * list->ranges_number); list->encaddrsranges[list->ranges_number - 1] = (ioa_addr_range*) turn_malloc(sizeof(ioa_addr_range)); ioa_addr_range_set(list->encaddrsranges[list->ranges_number - 1], &min, &max); return 0; } /////////////////////////////// turnserver-3.2.3.1/src/apps/relay/turncli.h000644 001751 001751 00000005456 12315706777 020545 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __TURNCLI__ #define __TURNCLI__ #include #include #include #include #include #include "ns_turn_utils.h" #include "ns_turn_maps.h" #include "ns_turn_server.h" #include "apputils.h" #ifdef __cplusplus extern "C" { #endif //////////////////////////////////////////// struct cli_server { evutil_socket_t listen_fd; struct event_base* event_base; int verbose; struct evconnlistener *l; struct bufferevent *in_buf; struct bufferevent *out_buf; ur_map *sessions; pthread_t thr; }; /////////////////////////////////////////// extern struct cli_server cliserver; extern int use_cli; #define CLI_DEFAULT_IP ("127.0.0.1") extern ioa_addr cli_addr; extern int cli_addr_set; #define CLI_DEFAULT_PORT (5766) extern int cli_port; #define CLI_PASSWORD_LENGTH (129) extern char cli_password[CLI_PASSWORD_LENGTH]; #define DEFAULT_CLI_MAX_OUTPUT_SESSIONS (256) extern int cli_max_output_sessions; //////////////////////////////////////////// void setup_cli_thread(void); void cli_server_receive_message(struct bufferevent *bev, void *ptr); int send_turn_session_info(struct turn_session_info* tsi); //////////////////////////////////////////// #ifdef __cplusplus } #endif #endif /// __TURNCLI__/// turnserver-3.2.3.1/src/apps/relay/turn_ports.c000644 001751 001751 00000027200 12315706777 021266 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "ns_turn_maps.h" #include "ns_turn_msg_defs.h" #include "ns_turn_ioalib.h" #include "ns_ioalib_impl.h" #include "turn_ports.h" ////////// DATA //////////////////////////////////////////// #define PORTS_SIZE (0xFFFF+1) #define TPS_OUT_OF_RANGE ((u32bits)(-1)) #define TPS_TAKEN_SINGLE ((u32bits)(-2)) #define TPS_TAKEN_EVEN ((u32bits)(-3)) #define TPS_TAKEN_ODD ((u32bits)(-4)) struct _turnports { u32bits status[PORTS_SIZE]; u32bits low; u32bits high; u16bits range_start; u16bits range_stop; u16bits ports[PORTS_SIZE]; TURN_MUTEX_DECLARE(mutex) }; typedef struct _turnports turnports; /////////////// TURNPORTS statics ////////////////////////// static turnports* turnports_create(super_memory_t *sm, u16bits start, u16bits end); static u16bits turnports_size(turnports* tp); static int turnports_allocate(turnports* tp); static int turnports_allocate_even(turnports* tp, int allocate_rtcp, u64bits *reservation_token); static void turnports_release(turnports* tp, u16bits port); static int turnports_is_allocated(turnports* tp, u16bits port); static int turnports_is_available(turnports* tp, u16bits port); /////////////// UTILS ////////////////////////////////////// static int is_taken(u32bits status) { int ret = -1; switch (status) { case TPS_TAKEN_SINGLE : case TPS_TAKEN_EVEN : case TPS_TAKEN_ODD : ret = 1; break; default: ret = 0; }; return ret; } static void turnports_randomize(turnports* tp) { if(tp) { unsigned int size=(unsigned int)(tp->high-tp->low); unsigned int i=0; unsigned int cycles=size*10; for(i=0;ilow + (u16bits)(((unsigned long)random())%((unsigned long)size))); u16bits port2 = (u16bits)(tp->low + (u16bits)(((unsigned long)random())%((unsigned long)size))); if(port1!=port2) { int pos1=tp->status[port1]; int pos2=tp->status[port2]; int tmp=(int)tp->status[port1]; tp->status[port1]=tp->status[port2]; tp->status[port2]=(u32bits)tmp; tmp=(int)tp->ports[pos1]; tp->ports[pos1]=tp->ports[pos2]; tp->ports[pos2]=(u16bits)tmp; } } } } static void turnports_init(turnports* tp, u16bits start, u16bits end) { tp->low=start; tp->high=((u32bits)end)+1; tp->range_start=start; tp->range_stop=end; int i=0; for(i=0;istatus[i]=TPS_OUT_OF_RANGE; tp->ports[i]=(u16bits)i; } for(i=start;i<=end;i++) { tp->status[i]=(u32bits)i; tp->ports[i]=(u16bits)i; } for(i=((int)end)+1;istatus[i]=TPS_OUT_OF_RANGE; tp->ports[i]=(u16bits)i; } turnports_randomize(tp); TURN_MUTEX_INIT_RECURSIVE(&(tp->mutex)); } /////////////// FUNC /////////////////////////////////////// turnports* turnports_create(super_memory_t *sm, u16bits start, u16bits end) { if(start>end) return NULL; turnports* ret=(turnports*)allocate_super_memory_region(sm, sizeof(turnports)); turnports_init(ret,start,end); return ret; } u16bits turnports_size(turnports* tp) { if(!tp) return 0; else { TURN_MUTEX_LOCK(&tp->mutex); u16bits ret = (u16bits)((tp->high-tp->low)); TURN_MUTEX_UNLOCK(&tp->mutex); return ret; } } int turnports_allocate(turnports* tp) { int port=-1; TURN_MUTEX_LOCK(&tp->mutex); if(tp) { while(1) { if(tp->high <= tp->low) { TURN_MUTEX_UNLOCK(&tp->mutex); return -1; } int position=(u16bits)(tp->low & 0x0000FFFF); port=(int)tp->ports[position]; if(port<(int)(tp->range_start) || port>((int)(tp->range_stop))) { TURN_MUTEX_UNLOCK(&tp->mutex); return -1; } if(is_taken(tp->status[port])) { ++(tp->low); continue; } if(tp->status[port]!=tp->low) { ++(tp->low); continue; } tp->status[port]=TPS_TAKEN_SINGLE; ++(tp->low); break; } } TURN_MUTEX_UNLOCK(&tp->mutex); return port; } void turnports_release(turnports* tp, u16bits port) { TURN_MUTEX_LOCK(&tp->mutex); if(tp && port>=tp->range_start && port<=tp->range_stop) { u16bits position=(u16bits)(tp->high & 0x0000FFFF); if(is_taken(tp->status[port])) { tp->status[port]=tp->high; tp->ports[position]=port; ++(tp->high); } } TURN_MUTEX_UNLOCK(&tp->mutex); } int turnports_allocate_even(turnports* tp, int allocate_rtcp, u64bits *reservation_token) { if(tp) { TURN_MUTEX_LOCK(&tp->mutex); u16bits size = turnports_size(tp); if(size>1) { u16bits i=0; for(i=0;imutex); return port; } else { int rtcp_port=port+1; if(rtcp_port>tp->range_stop) { turnports_release(tp,port); } else if(!turnports_is_available(tp,rtcp_port)) { turnports_release(tp,port); } else { tp->status[port]=TPS_TAKEN_EVEN; tp->status[rtcp_port]=TPS_TAKEN_ODD; if(reservation_token) { u16bits *v16=(u16bits*)reservation_token; u32bits *v32=(u32bits*)reservation_token; v16[0]=(u16bits)(tp->ports[(u16bits)(tp->low & 0x0000FFFF)]); v16[1]=(u16bits)(tp->ports[(u16bits)(tp->high & 0x0000FFFF)]); v32[1]=(u32bits)turn_random(); } TURN_MUTEX_UNLOCK(&tp->mutex); return port; } } } } } TURN_MUTEX_UNLOCK(&tp->mutex); } return -1; } int turnports_is_allocated(turnports* tp, u16bits port) { if(!tp) return 0; else { TURN_MUTEX_LOCK(&tp->mutex); int ret = is_taken(tp->status[port]); TURN_MUTEX_UNLOCK(&tp->mutex); return ret; } } int turnports_is_available(turnports* tp, u16bits port) { if(tp) { TURN_MUTEX_LOCK(&tp->mutex); u32bits status = tp->status[port]; if((status!=TPS_OUT_OF_RANGE) && !is_taken(status)) { u16bits position=(u16bits)(status & 0x0000FFFF); if(tp->ports[position]==port) { TURN_MUTEX_UNLOCK(&tp->mutex); return 1; } } TURN_MUTEX_UNLOCK(&tp->mutex); } return 0; } /////////////////// IP-mapped PORTS ///////////////////////////////////// struct _turnipports { super_memory_t *sm; u16bits start; u16bits end; ur_addr_map ip_to_turnports_udp; ur_addr_map ip_to_turnports_tcp; TURN_MUTEX_DECLARE(mutex) }; ////////////////////////////////////////////////// static ur_addr_map *get_map(turnipports *tp, u08bits transport) { if(transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE) return &(tp->ip_to_turnports_tcp); return &(tp->ip_to_turnports_udp); } ////////////////////////////////////////////////// static turnipports* turnipports_singleton = NULL; turnipports* turnipports_create(super_memory_t *sm, u16bits start, u16bits end) { turnipports *ret = (turnipports*) allocate_super_memory_region(sm, sizeof(turnipports)); ret->sm = sm; ur_addr_map_init(&(ret->ip_to_turnports_udp)); ur_addr_map_init(&(ret->ip_to_turnports_tcp)); ret->start = start; ret->end = end; TURN_MUTEX_INIT_RECURSIVE(&(ret->mutex)); turnipports_singleton = ret; return ret; } static turnports* turnipports_add(turnipports* tp, u08bits transport, const ioa_addr *backend_addr) { ur_addr_map_value_type t = 0; if (tp && backend_addr) { ioa_addr ba; addr_cpy(&ba, backend_addr); addr_set_port(&ba, 0); TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex)); if (!ur_addr_map_get(get_map(tp, transport), &ba, &t)) { t = (ur_addr_map_value_type) turnports_create(tp->sm, tp->start, tp->end); ur_addr_map_put(get_map(tp, transport), &ba, t); } TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex)); } return (turnports*) t; } void turnipports_add_ip(u08bits transport, const ioa_addr *backend_addr) { turnipports_add(turnipports_singleton, transport, backend_addr); } int turnipports_allocate(turnipports* tp, u08bits transport, const ioa_addr *backend_addr) { int ret = -1; if (tp && backend_addr) { TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex)); turnports *t = turnipports_add(tp, transport, backend_addr); ret = turnports_allocate(t); TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex)); } return ret; } int turnipports_allocate_even(turnipports* tp, const ioa_addr *backend_addr, int allocate_rtcp, u64bits *reservation_token) { int ret = -1; if (tp && backend_addr) { TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex)); turnports *t = turnipports_add(tp, STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE, backend_addr); ret = turnports_allocate_even(t, allocate_rtcp, reservation_token); TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex)); } return ret; } void turnipports_release(turnipports* tp, u08bits transport, const ioa_addr *socket_addr) { if (tp && socket_addr) { ioa_addr ba; ur_addr_map_value_type t; addr_cpy(&ba, socket_addr); addr_set_port(&ba, 0); TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex)); if (ur_addr_map_get(get_map(tp, transport), &ba, &t)) { turnports_release((turnports*) t, addr_get_port(socket_addr)); } TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex)); } } int turnipports_is_allocated(turnipports* tp, u08bits transport, const ioa_addr *backend_addr, u16bits port) { int ret = 0; if (tp && backend_addr) { ioa_addr ba; ur_addr_map_value_type t; addr_cpy(&ba, backend_addr); addr_set_port(&ba, 0); TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex)); if (ur_addr_map_get(get_map(tp,transport), &ba, &t)) { ret = turnports_is_allocated((turnports*) t, port); } TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex)); } return ret; } int turnipports_is_available(turnipports* tp, u08bits transport, const ioa_addr *backend_addr, u16bits port) { int ret = 0; if (tp && backend_addr) { ioa_addr ba; ur_addr_map_value_type t; addr_cpy(&ba, backend_addr); addr_set_port(&ba, 0); TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex)); if (!ur_addr_map_get(get_map(tp,transport), &ba, &t)) { ret = 1; } else { ret = turnports_is_available((turnports*) t, port); } TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex)); } return ret; } ////////////////////////////////////////////////////////////////// turnserver-3.2.3.1/src/apps/relay/ns_ioalib_impl.h000644 001751 001751 00000017537 12315706777 022050 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ /* * IO Abstraction library */ #ifndef __IOA_LIBIMPL__ #define __IOA_LIBIMPL__ #include #include #include #include #include #include "ns_turn_ioalib.h" #include "turn_ports.h" #include "ns_turn_maps_rtcp.h" #include "ns_turn_maps.h" #include "ns_turn_server.h" #include "apputils.h" #include "stun_buffer.h" #include "ns_sm.h" #include #ifdef __cplusplus extern "C" { #endif ////////////////////////////////////////////////////// #define MAX_BUFFER_QUEUE_SIZE_PER_ENGINE (64) #define MAX_SOCKET_BUFFER_BACKLOG (16) #define BUFFEREVENT_HIGH_WATERMARK (128<<10) #define BUFFEREVENT_MAX_UDP_TO_TCP_WRITE (64<<9) #define BUFFEREVENT_MAX_TCP_TO_TCP_WRITE (192<<10) typedef struct _stun_buffer_list_elem { struct _stun_buffer_list_elem *next; stun_buffer buf; } stun_buffer_list_elem; typedef struct _stun_buffer_list { stun_buffer_list_elem *head; size_t tsz; } stun_buffer_list; typedef unsigned long band_limit_t; /* * New connection callback */ struct cb_socket_message { turnserver_id id; tcp_connection_id connection_id; stun_tid tid; ioa_socket_handle s; int message_integrity; }; struct relay_server { turnserver_id id; super_memory_t* sm; struct event_base* event_base; struct bufferevent *in_buf; struct bufferevent *out_buf; struct bufferevent *auth_in_buf; struct bufferevent *auth_out_buf; ioa_engine_handle ioa_eng; turn_turnserver server; pthread_t thr; }; struct message_to_relay { MESSAGE_TO_RELAY_TYPE t; struct relay_server *relay_server; union { struct socket_message sm; struct cb_socket_message cb_sm; } m; }; struct relay_server; typedef struct relay_server *relay_server_handle; typedef int (*ioa_engine_new_connection_event_handler)(ioa_engine_handle e, struct message_to_relay *sm); typedef int (*ioa_engine_udp_event_handler)(relay_server_handle rs, struct message_to_relay *sm); #define TURN_CMSG_SZ (65536) #define PREDEF_TIMERS_NUM (14) extern const int predef_timer_intervals[PREDEF_TIMERS_NUM]; struct _ioa_engine { super_memory_t *sm; struct event_base *event_base; int deallocate_eb; int verbose; turnipports* tp; rtcp_map *map_rtcp; stun_buffer_list bufs; SSL_CTX *tls_ctx_ssl23; SSL_CTX *tls_ctx_v1_0; #if defined(SSL_TXT_TLSV1_1) SSL_CTX *tls_ctx_v1_1; #if defined(SSL_TXT_TLSV1_2) SSL_CTX *tls_ctx_v1_2; #endif #endif SSL_CTX *dtls_ctx; turn_time_t jiffie; band_limit_t max_bpj; ioa_timer_handle timer_ev; s08bits cmsg[TURN_CMSG_SZ+1]; int predef_timer_intervals[PREDEF_TIMERS_NUM]; struct timeval predef_timers[PREDEF_TIMERS_NUM]; /* Relays */ s08bits relay_ifname[1025]; int default_relays; size_t relays_number; size_t relay_addr_counter; ioa_addr *relay_addrs; }; #define SOCKET_MAGIC (0xABACADEF) struct _ioa_socket { evutil_socket_t fd; struct _ioa_socket *parent_s; void *listener_server; u32bits magic; ur_addr_map *sockets_container; /* relay container for UDP sockets */ struct bufferevent *bev; ioa_network_buffer_handle defer_nbh; int family; SOCKET_TYPE st; SOCKET_APP_TYPE sat; SSL* ssl; char orig_ctx_type[16]; int bound; int local_addr_known; ioa_addr local_addr; int connected; ioa_addr remote_addr; ioa_engine_handle e; struct event *read_event; ioa_net_event_handler read_cb; void *read_ctx; int done; ts_ur_super_session* session; int current_df_relay_flag; /* RFC6156: if IPv6 is involved, do not use DF: */ int do_not_use_df; int tobeclosed; int broken; int default_ttl; int current_ttl; int default_tos; int current_tos; stun_buffer_list bufs; turn_time_t jiffie; band_limit_t jiffie_bytes_read; band_limit_t jiffie_bytes_write; /* RFC 6062 ==>> */ //Connection session: tcp_connection *sub_session; //Connect: struct bufferevent *conn_bev; connect_cb conn_cb; void *conn_arg; //Accept: struct evconnlistener *list_ev; accept_cb acb; void *acbarg; /* <<== RFC 6062 */ }; typedef struct _timer_event { struct event *ev; ioa_engine_handle e; ioa_timer_event_handler cb; void *ctx; s08bits* txt; } timer_event; /* engine handling */ ioa_engine_handle create_ioa_engine(super_memory_t *sm, struct event_base *eb, turnipports* tp, const s08bits* relay_if, size_t relays_number, s08bits **relay_addrs, int default_relays, int verbose, band_limit_t max_bps); void set_ssl_ctx(ioa_engine_handle e, SSL_CTX *tls_ctx_ssl23, SSL_CTX *tls_ctx_v1_0, #if defined(SSL_TXT_TLSV1_1) SSL_CTX *tls_ctx_v1_1, #if defined(SSL_TXT_TLSV1_2) SSL_CTX *tls_ctx_v1_2, #endif #endif SSL_CTX *dtls_ctx); void ioa_engine_set_rtcp_map(ioa_engine_handle e, rtcp_map *rtcpmap); ioa_socket_handle create_ioa_socket_from_fd(ioa_engine_handle e, ioa_socket_raw fd, ioa_socket_handle parent_s, SOCKET_TYPE st, SOCKET_APP_TYPE sat, const ioa_addr *remote_addr, const ioa_addr *local_addr); ioa_socket_handle create_ioa_socket_from_ssl(ioa_engine_handle e, ioa_socket_handle parent_s, SSL* ssl, SOCKET_TYPE st, SOCKET_APP_TYPE sat, const ioa_addr *remote_addr, const ioa_addr *local_addr); int get_a_local_relay(int family, ioa_addr *relay_addr); void add_socket_to_parent(ioa_socket_handle parent_s, ioa_socket_handle s); void delete_socket_from_parent(ioa_socket_handle s); void add_socket_to_map(ioa_socket_handle s, ur_addr_map *amap); void delete_socket_from_map(ioa_socket_handle s); int is_connreset(void); int would_block(void); int udp_send(ioa_socket_handle s, const ioa_addr* dest_addr, const s08bits* buffer, int len); int udp_recvfrom(evutil_socket_t fd, ioa_addr* orig_addr, const ioa_addr *like_addr, s08bits* buffer, int buf_size, int *ttl, int *tos, s08bits *ecmsg, int flags, u32bits *errcode); int ssl_read(evutil_socket_t fd, SSL* ssl, ioa_network_buffer_handle nbh, int verbose); int set_raw_socket_ttl_options(evutil_socket_t fd, int family); int set_raw_socket_tos_options(evutil_socket_t fd, int family); int set_socket_options_fd(evutil_socket_t fd, int tcp, int family); int set_socket_options(ioa_socket_handle s); ///////////////////////// SUPER MEMORY //////// #define allocate_super_memory_engine(e,size) allocate_super_memory_engine_func(e, size, __FILE__, __FUNCTION__, __LINE__) void* allocate_super_memory_engine_func(ioa_engine_handle e, size_t size, const char* file, const char* func, int line); ///////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif /* __IOA_LIBIMPL__ */ turnserver-3.2.3.1/src/apps/relay/mainrelay.c000644 001751 001751 00000231042 12315706777 021031 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "mainrelay.h" //////TURN PARAMS STRUCTURE DEFINITION ////// #define DEFAULT_GENERAL_RELAY_SERVERS_NUMBER (1) turn_params_t turn_params = { NULL, NULL, #if defined(SSL_TXT_TLSV1_1) NULL, #if defined(SSL_TXT_TLSV1_2) NULL, #endif #endif NULL, SHATYPE_SHA1, DH_1066, "", DEFAULT_EC_CURVE_NAME, "", "turn_server_cert.pem","turn_server_pkey.pem", "", "", 0,0,0,0,0, #if defined(TURN_NO_TLS) 1, #else 0, #endif #if defined(TURN_NO_DTLS) 1, #else 0, #endif TURN_VERBOSE_NONE,0,0,0,0,0,0,0, "/var/run/turnserver.pid", DEFAULT_STUN_PORT,DEFAULT_STUN_TLS_PORT,0,0,1, 0,0,0,0, "", #if !defined(TURN_NO_HIREDIS) "",0, #endif { NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0,0,NULL,NULL,NULL #if !defined(TURN_NO_HIREDIS) ,NULL #endif }, {NULL, NULL, 0},{NULL, NULL, 0}, NEV_UNKNOWN, { "Unknown", "UDP listening socket per session", "UDP thread per network endpoint", "UDP thread per CPU core" }, //////////////// Relay servers ////////////////////////////////// 0,LOW_DEFAULT_PORTS_BOUNDARY,HIGH_DEFAULT_PORTS_BOUNDARY,0,0,"", 0,NULL,0,NULL,0,DEFAULT_GENERAL_RELAY_SERVERS_NUMBER,0,0, ////////////// Auth server ///////////////////////////////////// {NULL,NULL,NULL,0}, /////////////// AUX SERVERS //////////////// {NULL,0,{0,NULL}},0, /////////////// ALTERNATE SERVERS //////////////// {NULL,0,{0,NULL}},{NULL,0,{0,NULL}}, /////////////// USERS //////////////////////////// { TURN_USERDB_TYPE_FILE,"\0",0,0,0,0, {TURN_CREDENTIALS_NONE,0,0,0,NULL,NULL,NULL}, "\0", 0,':', {NULL,0} }, /////////////// stop server //////////////// 0 }; //////////////// OpenSSL Init ////////////////////// static void openssl_setup(void); /* * openssl genrsa -out pkey 2048 * openssl req -new -key pkey -out cert.req * openssl x509 -req -days 365 -in cert.req -signkey pkey -out cert * */ //////////// Common static process params //////// static gid_t procgroupid = 0; static uid_t procuserid = 0; static gid_t procgroupid_set = 0; static uid_t procuserid_set = 0; static char procusername[1025]="\0"; static char procgroupname[1025]="\0"; ////////////// Configuration functionality //////////////////////////////// static void read_config_file(int argc, char **argv, int pass); ////////////////////////////////////////////////// static int make_local_listeners_list(void) { int ret = 0; struct ifaddrs * ifs = NULL; struct ifaddrs * ifa = NULL; char saddr[INET6_ADDRSTRLEN] = ""; if((getifaddrs(&ifs) == 0) && ifs) { for (ifa = ifs; ifa != NULL; ifa = ifa->ifa_next) { if(!(ifa->ifa_flags & IFF_UP)) continue; if(!(ifa->ifa_addr)) continue; if (ifa ->ifa_addr->sa_family == AF_INET) { if(!inet_ntop(AF_INET, &((struct sockaddr_in *) ifa->ifa_addr)->sin_addr, saddr, INET_ADDRSTRLEN)) continue; if(strstr(saddr,"169.254.") == saddr) continue; if(!strcmp(saddr,"0.0.0.0")) continue; } else if (ifa->ifa_addr->sa_family == AF_INET6) { if(!inet_ntop(AF_INET6, &((struct sockaddr_in6 *) ifa->ifa_addr)->sin6_addr, saddr, INET6_ADDRSTRLEN)) continue; if(strstr(saddr,"fe80") == saddr) continue; if(!strcmp(saddr,"::")) continue; } else { continue; } add_listener_addr(saddr); if(!(ifa->ifa_flags & IFF_LOOPBACK)) ret++; } freeifaddrs(ifs); } return ret; } static int make_local_relays_list(int allow_local, int family) { struct ifaddrs * ifs = NULL; struct ifaddrs * ifa = NULL; char saddr[INET6_ADDRSTRLEN] = ""; getifaddrs(&ifs); int counter = 0; if (ifs) { for (ifa = ifs; ifa != NULL; ifa = ifa->ifa_next) { if(!(ifa->ifa_flags & IFF_UP)) continue; if(!(ifa->ifa_name)) continue; if(!(ifa ->ifa_addr)) continue; if(!allow_local && (ifa->ifa_flags & IFF_LOOPBACK)) continue; if (ifa ->ifa_addr->sa_family == AF_INET) { if(family != AF_INET) continue; if(!inet_ntop(AF_INET, &((struct sockaddr_in *) ifa->ifa_addr)->sin_addr, saddr, INET_ADDRSTRLEN)) continue; if(strstr(saddr,"169.254.") == saddr) continue; if(!strcmp(saddr,"0.0.0.0")) continue; } else if (ifa->ifa_addr->sa_family == AF_INET6) { if(family != AF_INET6) continue; if(!inet_ntop(AF_INET6, &((struct sockaddr_in6 *) ifa->ifa_addr)->sin6_addr, saddr, INET6_ADDRSTRLEN)) continue; if(strstr(saddr,"fe80") == saddr) continue; if(!strcmp(saddr,"::")) continue; } else continue; if(add_relay_addr(saddr)>0) { counter += 1; } } freeifaddrs(ifs); } return counter; } int get_a_local_relay(int family, ioa_addr *relay_addr) { struct ifaddrs * ifs = NULL; int allow_local = 0; int ret = -1; char saddr[INET6_ADDRSTRLEN] = ""; getifaddrs(&ifs); if (ifs) { galr_start: { struct ifaddrs *ifa = NULL; for (ifa = ifs; ifa != NULL ; ifa = ifa->ifa_next) { if (!(ifa->ifa_flags & IFF_UP)) continue; if (!(ifa->ifa_name)) continue; if (!(ifa->ifa_addr)) continue; if (!allow_local && (ifa->ifa_flags & IFF_LOOPBACK)) continue; if (ifa->ifa_addr->sa_family == AF_INET) { if (family != AF_INET) continue; if (!inet_ntop(AF_INET, &((struct sockaddr_in *) ifa->ifa_addr)->sin_addr, saddr, INET_ADDRSTRLEN)) continue; if (strstr(saddr, "169.254.") == saddr) continue; if (!strcmp(saddr, "0.0.0.0")) continue; } else if (ifa->ifa_addr->sa_family == AF_INET6) { if (family != AF_INET6) continue; if (!inet_ntop(AF_INET6, &((struct sockaddr_in6 *) ifa->ifa_addr)->sin6_addr, saddr, INET6_ADDRSTRLEN)) continue; if (strstr(saddr, "fe80") == saddr) continue; if (!strcmp(saddr, "::")) continue; } else continue; if (make_ioa_addr((const u08bits*) saddr, 0, relay_addr) < 0) { continue; } else { ret = 0; break; } } } if(ret<0 && !allow_local) { allow_local = 1; goto galr_start; } freeifaddrs(ifs); } return -1; } ////////////////////////////////////////////////// static char Usage[] = "Usage: turnserver [options]\n" "Options:\n" " -d, --listening-device Listener interface device (NOT RECOMMENDED. Optional, Linux only).\n" " -p, --listening-port TURN listener port (Default: 3478).\n" " Note: actually, TLS & DTLS sessions can connect to the \"plain\" TCP & UDP port(s), too,\n" " if allowed by configuration.\n" " --tls-listening-port TURN listener port for TLS & DTLS listeners\n" " (Default: 5349).\n" " Note: actually, \"plain\" TCP & UDP sessions can connect to the TLS & DTLS port(s), too,\n" " if allowed by configuration. The TURN server\n" " \"automatically\" recognizes the type of traffic. Actually, two listening\n" " endpoints (the \"plain\" one and the \"tls\" one) are equivalent in terms of\n" " functionality; but we keep both endpoints to satisfy the RFC 5766 specs.\n" " For secure TCP connections, we currently support SSL version 3 and\n" " TLS versions 1.0, 1.1 and 1.2. For secure UDP connections, we support\n" " DTLS version 1.\n" " --alt-listening-port Alternative listening port for STUN CHANGE_REQUEST (in RFC 5780 sense, \n" " or in old RFC 3489 sense, default is \"listening port plus one\").\n" " --alt-tls-listening-port Alternative listening port for TLS and DTLS,\n" " the default is \"TLS/DTLS port plus one\".\n" " -L, --listening-ip Listener IP address of relay server. Multiple listeners can be specified.\n" " --aux-server Auxiliary STUN/TURN server listening endpoint.\n" " Auxiliary servers do not have alternative ports and\n" " they do not support RFC 5780 functionality (CHANGE REQUEST).\n" " Valid formats are 1.2.3.4:5555 for IPv4 and [1:2::3:4]:5555 for IPv6.\n" " --udp-self-balance (recommended for older Linuxes only) Automatically balance UDP traffic\n" " over auxiliary servers (if configured).\n" " The load balancing is using the ALTERNATE-SERVER mechanism.\n" " The TURN client must support 300 ALTERNATE-SERVER response for this functionality.\n" " -i, --relay-device Relay interface device for relay sockets (NOT RECOMMENDED. Optional, Linux only).\n" " -E, --relay-ip Relay address (the local IP address that will be used to relay the\n" " packets to the peer).\n" " Multiple relay addresses may be used.\n" " The same IP(s) can be used as both listening IP(s) and relay IP(s).\n" " If no relay IP(s) specified, then the turnserver will apply the default\n" " policy: it will decide itself which relay addresses to be used, and it\n" " will always be using the client socket IP address as the relay IP address\n" " of the TURN session (if the requested relay address family is the same\n" " as the family of the client socket).\n" " -X, --external-ip TURN Server public/private address mapping, if the server is behind NAT.\n" " In that situation, if a -X is used in form \"-X ip\" then that ip will be reported\n" " as relay IP address of all allocations. This scenario works only in a simple case\n" " when one single relay address is be used, and no STUN CHANGE_REQUEST functionality is required.\n" " That single relay address must be mapped by NAT to the 'external' IP.\n" " For that 'external' IP, NAT must forward ports directly (relayed port 12345\n" " must be always mapped to the same 'external' port 12345).\n" " In more complex case when more than one IP address is involved,\n" " that option must be used several times in the command line, each entry must\n" " have form \"-X public-ip/private-ip\", to map all involved addresses.\n" " --no-loopback-peers Disallow peers on the loopback addresses (127.x.x.x and ::1).\n" " --no-multicast-peers Disallow peers on well-known broadcast addresses (224.0.0.0 and above, and FFXX:*).\n" " -m, --relay-threads Number of relay threads to handle the established connections\n" " (in addition to authentication thread and the listener thread).\n" " If set to 0 then application runs in single-threaded mode.\n" " The default thread number is the number of CPUs.\n" " In older systems (pre-Linux 3.9) the number of UDP relay threads always equals\n" " the number of listening endpoints (unless -m 0 is set).\n" " --min-port Lower bound of the UDP port range for relay endpoints allocation.\n" " Default value is 49152, according to RFC 5766.\n" " --max-port Upper bound of the UDP port range for relay endpoints allocation.\n" " Default value is 65535, according to RFC 5766.\n" " -v, --verbose 'Moderate' verbose mode.\n" " -V, --Verbose Extra verbose mode, very annoying (for debug purposes only).\n" " -o, --daemon Start process as daemon (detach from current shell).\n" " -f, --fingerprint Use fingerprints in the TURN messages.\n" " -a, --lt-cred-mech Use the long-term credential mechanism. This option can be used with either\n" " flat file user database or PostgreSQL DB or MySQL DB for user keys storage.\n" " -A, --st-cred-mech Use the short-term credential mechanism. This option requires\n" " a PostgreSQL or MySQL DB for short term passwords storage.\n" " -z, --no-auth Do not use any credential mechanism, allow anonymous access.\n" " -u, --user User account, in form 'username:password', for long-term credentials.\n" " Cannot be used with TURN REST API or with short-term credentials.\n" " -r, --realm Realm, for long-term credentials and for TURN REST API.\n" " -q, --user-quota Per-user allocation quota: how many concurrent allocations a user can create.\n" " -Q, --total-quota Total allocations quota: global limit on concurrent allocations.\n" " -s, --max-bps Max bytes-per-second bandwidth a TURN session is allowed to handle\n" " (input and output network streams are treated separately). Anything above\n" " that limit will be dropped or temporary suppressed\n" " (within the available buffer limits).\n" " -c Configuration file name (default - turnserver.conf).\n" " -b, --userdb User database file name (default - turnuserdb.conf) for long-term credentials only.\n" #if !defined(TURN_NO_PQ) " -e, --psql-userdb, --sql-userdb PostgreSQL database connection string, if used (default - empty, no PostreSQL DB used).\n" " This database can be used for long-term and short-term credentials mechanisms,\n" " and it can store the secret value(s) for secret-based timed authentication in TURN RESP API.\n" " See http://www.postgresql.org/docs/8.4/static/libpq-connect.html for 8.x PostgreSQL\n" " versions format, see \n" " http://www.postgresql.org/docs/9.2/static/libpq-connect.html#LIBPQ-CONNSTRING\n" " for 9.x and newer connection string formats.\n" #endif #if !defined(TURN_NO_MYSQL) " -M, --mysql-userdb MySQL database connection string, if used (default - empty, no MySQL DB used).\n" " This database can be used for long-term and short-term credentials mechanisms,\n" " and it can store the secret value(s) for secret-based timed authentication in TURN RESP API.\n" " The connection string my be space-separated list of parameters:\n" " \"host= dbname= user= \\\n password= port= connect_timeout=\".\n" " All parameters are optional.\n" #endif #if !defined(TURN_NO_HIREDIS) " -N, --redis-userdb Redis user database connection string, if used (default - empty, no Redis DB used).\n" " This database can be used for long-term and short-term credentials mechanisms,\n" " and it can store the secret value(s) for secret-based timed authentication in TURN RESP API.\n" " The connection string my be space-separated list of parameters:\n" " \"host= dbname= \\\n password= port= connect_timeout=\".\n" " All parameters are optional.\n" " -O, --redis-statsdb Redis status and statistics database connection string, if used \n" " (default - empty, no Redis stats DB used).\n" " This database keeps allocations status information, and it can be also used for publishing\n" " and delivering traffic and allocation event notifications.\n" " The connection string has the same parameters as redis-userdb connection string.\n" #endif " --use-auth-secret TURN REST API flag.\n" " Flag that sets a special authorization option that is based upon authentication secret\n" " (TURN Server REST API, see TURNServerRESTAPI.pdf). This option is used with timestamp.\n" " --static-auth-secret 'Static' authentication secret value (a string) for TURN REST API only.\n" " If not set, then the turn server will try to use the 'dynamic' value\n" " in turn_secret table in user database (if present).\n" " That database value can be changed on-the-fly\n" " by a separate program, so this is why it is 'dynamic'.\n" " Multiple shared secrets can be used (both in the database and in the \"static\" fashion).\n" " -n Do not use configuration file, take all parameters from the command line only.\n" " --cert Certificate file, PEM format. Same file search rules\n" " applied as for the configuration file.\n" " If both --no-tls and --no_dtls options\n" " are specified, then this parameter is not needed.\n" " --pkey Private key file, PEM format. Same file search rules\n" " applied as for the configuration file.\n" " If both --no-tls and --no-dtls options\n" " --pkey-pwd If the private key file is encrypted, then this password to be used.\n" " --cipher-list <\"cipher-string\"> Allowed OpenSSL cipher list for TLS/DTLS connections.\n" " Default value is \"DEFAULT\".\n" " --CA-file CA file in OpenSSL format.\n" " Forces TURN server to verify the client SSL certificates.\n" " By default, no CA is set and no client certificate check is performed.\n" " --ec-curve-name Curve name for EC ciphers, if supported by OpenSSL library\n" " (TLS and DTLS). The default value is prime256v1.\n" " --dh566 Use 566 bits predefined DH TLS key. Default size of the predefined key is 1066.\n" " --dh2066 Use 2066 bits predefined DH TLS key. Default size of the predefined key is 1066.\n" " --dh-file Use custom DH TLS key, stored in PEM format in the file.\n" " Flags --dh566 and --dh2066 are ignored when the DH key is taken from a file.\n" " --no-sslv2 Do not allow SSLv2 protocol.\n" " --no-sslv3 Do not allow SSLv3 protocol.\n" " --no-tlsv1 Do not allow TLSv1 protocol.\n" " --no-tlsv1_1 Do not allow TLSv1.1 protocol.\n" " --no-tlsv1_2 Do not allow TLSv1.2 protocol.\n" " --no-udp Do not start UDP client listeners.\n" " --no-tcp Do not start TCP client listeners.\n" " --no-tls Do not start TLS client listeners.\n" " --no-dtls Do not start DTLS client listeners.\n" " --no-udp-relay Do not allow UDP relay endpoints, use only TCP relay option.\n" " --no-tcp-relay Do not allow TCP relay endpoints, use only UDP relay options.\n" " -l, --log-file Option to set the full path name of the log file.\n" " By default, the turnserver tries to open a log file in\n" " /var/log/turnserver/, /var/log, /var/tmp, /tmp and . (current) directories\n" " (which open operation succeeds first that file will be used).\n" " With this option you can set the definite log file name.\n" " The special names are \"stdout\" and \"-\" - they will force everything\n" " to the stdout; and \"syslog\" name will force all output to the syslog.\n" " --no-stdout-log Flag to prevent stdout log messages.\n" " By default, all log messages are going to both stdout and to\n" " a log file. With this option everything will be going to the log file only\n" " (unless the log file itself is stdout).\n" " --syslog Output all log information into the system log (syslog), do not use the file output.\n" " --stale-nonce Use extra security with nonce value having limited lifetime (600 secs).\n" " -S, --stun-only Option to set standalone STUN operation only, all TURN requests will be ignored.\n" " --no-stun Option to suppress STUN functionality, only TURN requests will be processed.\n" " --alternate-server Set the TURN server to redirect the allocate requests (UDP and TCP services).\n" " Multiple alternate-server options can be set for load balancing purposes.\n" " See the docs for more information.\n" " --tls-alternate-server Set the TURN server to redirect the allocate requests (DTLS and TLS services).\n" " Multiple alternate-server options can be set for load balancing purposes.\n" " See the docs for more information.\n" " -C, --rest-api-separator This is the username/timestamp separator symbol (character) in TURN REST API.\n" " The default value is ':'.\n" " --max-allocate-timeout= Max time, in seconds, allowed for full allocation establishment. Default is 60.\n" " --allowed-peer-ip= Specifies an ip or range of ips that are explicitly allowed to connect to the \n" " turn server. Multiple allowed-peer-ip can be set.\n" " --denied-peer-ip= Specifies an ip or range of ips that are not allowed to connect to the turn server.\n" " Multiple denied-peer-ip can be set.\n" " --pidfile <\"pid-file-name\"> File name to store the pid of the process.\n" " Default is /var/run/turnserver.pid (if superuser account is used) or\n" " /var/tmp/turnserver.pid .\n" " --secure-stun Require authentication of the STUN Binding request.\n" " By default, the clients are allowed anonymous access to the STUN Binding functionality.\n" " --sha256 Require SHA256 digest function to be used for the message integrity.\n" " By default, the server SHA1 (as per TURN standard specs).\n" " With this option, the server\n" " requires the stronger SHA256 function. The client application must\n" " support SHA256 hash function if this option is used. If the server obtains\n" " a message from the client with a weaker (SHA1) hash function then the server\n" " returns error code 426.\n" " --proc-user User name to run the turnserver process.\n" " After the initialization, the turnserver process\n" " will make an attempt to change the current user ID to that user.\n" " --proc-group Group name to run the turnserver process.\n" " After the initialization, the turnserver process\n" " will make an attempt to change the current group ID to that group.\n" " --mobility Mobility with ICE (MICE) specs support.\n" " --no-cli Turn OFF the CLI support. By default it is always ON.\n" " --cli-ip= Local system IP address to be used for CLI server endpoint. Default value\n" " is 127.0.0.1.\n" " --cli-port= CLI server port. Default is 5766.\n" " --cli-password= CLI access password. Default is empty (no password).\n" " --server-relay Server relay. NON-STANDARD AND DANGEROUS OPTION. Only for those applications\n" " when we want to run server applications on the relay endpoints.\n" " This option eliminates the IP permissions check on the packets\n" " incoming to the relay endpoints.\n" " --cli-max-output-sessions Maximum number of output sessions in ps CLI command.\n" " This value can be changed on-the-fly in CLI. The default value is 256.\n" " --ne=[1|2|3] Set network engine type for the process (for internal purposes).\n" " -h Help\n" "\n" " For more information, see the wiki pages:\n" "\n" " http://code.google.com/p/rfc5766-turn-server/w/list\n" "\n"; static char AdminUsage[] = "Usage: turnadmin [command] [options]\n" "Commands:\n" " -k, --key generate long-term credential mechanism key for a user\n" " -a, --add add/update a long-term mechanism user\n" " -A, --add-st add/update a short-term mechanism user\n" " -d, --delete delete a long-term mechanism user\n" " -D, --delete-st delete a short-term mechanism user\n" " -l, --list list all long-term mechanism users\n" " -L, --list-st list all short-term mechanism users\n" #if !defined(TURN_NO_PQ) || !defined(TURN_NO_MYSQL) " -s, --set-secret= Add shared secret for TURN RESP API\n" " -S, --show-secret Show stored shared secrets for TURN REST API\n" " -X, --delete-secret= Delete a shared secret\n" " --delete-all-secrets Delete all shared secrets for REST API\n" #endif "Options:\n" " -b, --userdb User database file, if flat DB file is used.\n" #if !defined(TURN_NO_PQ) " -e, --psql-userdb, --sql-userdb PostgreSQL user database connection string, if PostgreSQL DB is used.\n" #endif #if !defined(TURN_NO_MYSQL) " -M, --mysql-userdb MySQL user database connection string, if MySQL DB is used.\n" #endif #if !defined(TURN_NO_HIREDIS) " -N, --redis-userdb Redis user database connection string, if Redis DB is used.\n" #endif " -u, --user Username\n" " -r, --realm Realm for long-term mechanism only\n" " -p, --password Password\n" " -H, --sha256 Use SHA256 digest function to be used for the message integrity.\n" " By default, the server SHA1 (as per TURN standard specs).\n" " -h, --help Help\n"; #define OPTIONS "c:d:p:L:E:X:i:m:l:r:u:b:e:M:N:O:q:Q:s:C:vVofhznaAS" #define ADMIN_OPTIONS "HlLkaADSdb:e:M:N:u:r:p:s:X:h" enum EXTRA_OPTS { NO_UDP_OPT=256, NO_TCP_OPT, NO_TLS_OPT, NO_DTLS_OPT, NO_UDP_RELAY_OPT, NO_TCP_RELAY_OPT, TLS_PORT_OPT, ALT_PORT_OPT, ALT_TLS_PORT_OPT, CERT_FILE_OPT, PKEY_FILE_OPT, PKEY_PWD_OPT, MIN_PORT_OPT, MAX_PORT_OPT, STALE_NONCE_OPT, AUTH_SECRET_OPT, DEL_ALL_AUTH_SECRETS_OPT, STATIC_AUTH_SECRET_VAL_OPT, AUTH_SECRET_TS_EXP, /* deprecated */ NO_STDOUT_LOG_OPT, SYSLOG_OPT, AUX_SERVER_OPT, UDP_SELF_BALANCE_OPT, ALTERNATE_SERVER_OPT, TLS_ALTERNATE_SERVER_OPT, NO_MULTICAST_PEERS_OPT, NO_LOOPBACK_PEERS_OPT, MAX_ALLOCATE_TIMEOUT_OPT, ALLOWED_PEER_IPS, DENIED_PEER_IPS, CIPHER_LIST_OPT, PIDFILE_OPT, SECURE_STUN_OPT, CA_FILE_OPT, DH_FILE_OPT, SHA256_OPT, NO_STUN_OPT, PROC_USER_OPT, PROC_GROUP_OPT, MOBILITY_OPT, NO_CLI_OPT, CLI_IP_OPT, CLI_PORT_OPT, CLI_PASSWORD_OPT, SERVER_RELAY_OPT, CLI_MAX_SESSIONS_OPT, EC_CURVE_NAME_OPT, DH566_OPT, DH2066_OPT, NE_TYPE_OPT, NO_SSLV2_OPT, NO_SSLV3_OPT, NO_TLSV1_OPT, NO_TLSV1_1_OPT, NO_TLSV1_2_OPT }; static struct option long_options[] = { { "listening-device", required_argument, NULL, 'd' }, { "listening-port", required_argument, NULL, 'p' }, { "tls-listening-port", required_argument, NULL, TLS_PORT_OPT }, { "alt-listening-port", required_argument, NULL, ALT_PORT_OPT }, { "alt-tls-listening-port", required_argument, NULL, ALT_TLS_PORT_OPT }, { "listening-ip", required_argument, NULL, 'L' }, { "relay-device", required_argument, NULL, 'i' }, { "relay-ip", required_argument, NULL, 'E' }, { "external-ip", required_argument, NULL, 'X' }, { "relay-threads", required_argument, NULL, 'm' }, { "min-port", required_argument, NULL, MIN_PORT_OPT }, { "max-port", required_argument, NULL, MAX_PORT_OPT }, { "lt-cred-mech", optional_argument, NULL, 'a' }, { "st-cred-mech", optional_argument, NULL, 'A' }, { "no-auth", optional_argument, NULL, 'z' }, { "user", required_argument, NULL, 'u' }, { "userdb", required_argument, NULL, 'b' }, #if !defined(TURN_NO_PQ) { "psql-userdb", required_argument, NULL, 'e' }, { "sql-userdb", required_argument, NULL, 'e' }, #endif #if !defined(TURN_NO_MYSQL) { "mysql-userdb", required_argument, NULL, 'M' }, #endif #if !defined(TURN_NO_HIREDIS) { "redis-userdb", required_argument, NULL, 'N' }, { "redis-statsdb", required_argument, NULL, 'O' }, #endif { "use-auth-secret", optional_argument, NULL, AUTH_SECRET_OPT }, { "static-auth-secret", required_argument, NULL, STATIC_AUTH_SECRET_VAL_OPT }, /* deprecated: */ { "secret-ts-exp-time", optional_argument, NULL, AUTH_SECRET_TS_EXP }, { "realm", required_argument, NULL, 'r' }, { "user-quota", required_argument, NULL, 'q' }, { "total-quota", required_argument, NULL, 'Q' }, { "max-bps", required_argument, NULL, 's' }, { "verbose", optional_argument, NULL, 'v' }, { "Verbose", optional_argument, NULL, 'V' }, { "daemon", optional_argument, NULL, 'o' }, { "fingerprint", optional_argument, NULL, 'f' }, { "no-udp", optional_argument, NULL, NO_UDP_OPT }, { "no-tcp", optional_argument, NULL, NO_TCP_OPT }, { "no-tls", optional_argument, NULL, NO_TLS_OPT }, { "no-dtls", optional_argument, NULL, NO_DTLS_OPT }, { "no-udp-relay", optional_argument, NULL, NO_UDP_RELAY_OPT }, { "no-tcp-relay", optional_argument, NULL, NO_TCP_RELAY_OPT }, { "stale-nonce", optional_argument, NULL, STALE_NONCE_OPT }, { "stun-only", optional_argument, NULL, 'S' }, { "no-stun", optional_argument, NULL, NO_STUN_OPT }, { "cert", required_argument, NULL, CERT_FILE_OPT }, { "pkey", required_argument, NULL, PKEY_FILE_OPT }, { "pkey-pwd", required_argument, NULL, PKEY_PWD_OPT }, { "log-file", required_argument, NULL, 'l' }, { "no-stdout-log", optional_argument, NULL, NO_STDOUT_LOG_OPT }, { "syslog", optional_argument, NULL, SYSLOG_OPT }, { "aux-server", required_argument, NULL, AUX_SERVER_OPT }, { "udp-self-balance", optional_argument, NULL, UDP_SELF_BALANCE_OPT }, { "alternate-server", required_argument, NULL, ALTERNATE_SERVER_OPT }, { "tls-alternate-server", required_argument, NULL, TLS_ALTERNATE_SERVER_OPT }, { "rest-api-separator", required_argument, NULL, 'C' }, { "max-allocate-timeout", required_argument, NULL, MAX_ALLOCATE_TIMEOUT_OPT }, { "no-multicast-peers", optional_argument, NULL, NO_MULTICAST_PEERS_OPT }, { "no-loopback-peers", optional_argument, NULL, NO_LOOPBACK_PEERS_OPT }, { "allowed-peer-ip", required_argument, NULL, ALLOWED_PEER_IPS }, { "denied-peer-ip", required_argument, NULL, DENIED_PEER_IPS }, { "cipher-list", required_argument, NULL, CIPHER_LIST_OPT }, { "pidfile", required_argument, NULL, PIDFILE_OPT }, { "secure-stun", optional_argument, NULL, SECURE_STUN_OPT }, { "CA-file", required_argument, NULL, CA_FILE_OPT }, { "dh-file", required_argument, NULL, DH_FILE_OPT }, { "sha256", optional_argument, NULL, SHA256_OPT }, { "proc-user", required_argument, NULL, PROC_USER_OPT }, { "proc-group", required_argument, NULL, PROC_GROUP_OPT }, { "mobility", optional_argument, NULL, MOBILITY_OPT }, { "no-cli", optional_argument, NULL, NO_CLI_OPT }, { "cli-ip", required_argument, NULL, CLI_IP_OPT }, { "cli-port", required_argument, NULL, CLI_PORT_OPT }, { "cli-password", required_argument, NULL, CLI_PASSWORD_OPT }, { "server-relay", optional_argument, NULL, SERVER_RELAY_OPT }, { "cli-max-output-sessions", required_argument, NULL, CLI_MAX_SESSIONS_OPT }, { "ec-curve-name", required_argument, NULL, EC_CURVE_NAME_OPT }, { "dh566", optional_argument, NULL, DH566_OPT }, { "dh2066", optional_argument, NULL, DH2066_OPT }, { "ne", required_argument, NULL, NE_TYPE_OPT }, { "no-sslv2", optional_argument, NULL, NO_SSLV2_OPT }, { "no-sslv3", optional_argument, NULL, NO_SSLV3_OPT }, { "no-tlsv1", optional_argument, NULL, NO_TLSV1_OPT }, { "no-tlsv1_1", optional_argument, NULL, NO_TLSV1_1_OPT }, { "no-tlsv1_2", optional_argument, NULL, NO_TLSV1_2_OPT }, { NULL, no_argument, NULL, 0 } }; static struct option admin_long_options[] = { { "key", no_argument, NULL, 'k' }, { "add", no_argument, NULL, 'a' }, { "delete", no_argument, NULL, 'd' }, { "list", no_argument, NULL, 'l' }, { "list-st", no_argument, NULL, 'L' }, #if !defined(TURN_NO_PQ) || !defined(TURN_NO_MYSQL) { "set-secret", required_argument, NULL, 's' }, { "show-secret", no_argument, NULL, 'S' }, { "delete-secret", required_argument, NULL, 'X' }, { "delete-all-secrets", no_argument, NULL, DEL_ALL_AUTH_SECRETS_OPT }, #endif { "add-st", no_argument, NULL, 'A' }, { "delete-st", no_argument, NULL, 'D' }, { "userdb", required_argument, NULL, 'b' }, #if !defined(TURN_NO_PQ) { "psql-userdb", required_argument, NULL, 'e' }, { "sql-userdb", required_argument, NULL, 'e' }, #endif #if !defined(TURN_NO_MYSQL) { "mysql-userdb", required_argument, NULL, 'M' }, #endif #if !defined(TURN_NO_HIREDIS) { "redis-userdb", required_argument, NULL, 'N' }, #endif { "user", required_argument, NULL, 'u' }, { "realm", required_argument, NULL, 'r' }, { "password", required_argument, NULL, 'p' }, { "sha256", no_argument, NULL, 'H' }, { "help", no_argument, NULL, 'h' }, { NULL, no_argument, NULL, 0 } }; static int get_bool_value(const char* s) { if(!s || !(s[0])) return 1; if(s[0]=='0' || s[0]=='n' || s[0]=='N' || s[0]=='f' || s[0]=='F') return 0; if(s[0]=='y' || s[0]=='Y' || s[0]=='t' || s[0]=='T') return 1; if(s[0]>'0' && s[0]<='9') return 1; if(!strcmp(s,"off") || !strcmp(s,"OFF") || !strcmp(s,"Off")) return 0; if(!strcmp(s,"on") || !strcmp(s,"ON") || !strcmp(s,"On")) return 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown boolean value: %s. You can use on/off, yes/no, 1/0, true/false.\n",s); exit(-1); } static void set_option(int c, char *value) { if(value && value[0]=='=') { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: option -%c is possibly used incorrectly. The short form of the option must be used as this: -%c , no \'equals\' sign may be used, that sign is used only with long form options (like --user=).\n",(char)c,(char)c); } switch (c) { case NO_SSLV2_OPT: turn_params.no_sslv2 = get_bool_value(value); break; case NO_SSLV3_OPT: turn_params.no_sslv3 = get_bool_value(value); break; case NO_TLSV1_OPT: turn_params.no_tlsv1 = get_bool_value(value); break; case NO_TLSV1_1_OPT: turn_params.no_tlsv1_1 = get_bool_value(value); break; case NO_TLSV1_2_OPT: turn_params.no_tlsv1_2 = get_bool_value(value); break; case NE_TYPE_OPT: { int ne = atoi(value); if((ne<(int)NEV_MIN)||(ne>(int)NEV_MAX)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "ERROR: wrong version of the network engine: %d\n",ne); } turn_params.net_engine_version = (NET_ENG_VERSION)ne; } break; case DH566_OPT: if(get_bool_value(value)) turn_params.dh_key_size = DH_566; break; case DH2066_OPT: if(get_bool_value(value)) turn_params.dh_key_size = DH_2066; break; case EC_CURVE_NAME_OPT: STRCPY(turn_params.ec_curve_name,value); break; case CLI_MAX_SESSIONS_OPT: cli_max_output_sessions = atoi(value); break; case SERVER_RELAY_OPT: turn_params.server_relay = get_bool_value(value); break; case MOBILITY_OPT: turn_params.mobility = get_bool_value(value); break; case NO_CLI_OPT: use_cli = !get_bool_value(value); break; case CLI_IP_OPT: if(make_ioa_addr((const u08bits*)value,0,&cli_addr)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot set cli address: %s\n",value); } else{ cli_addr_set = 1; } break; case CLI_PORT_OPT: cli_port = atoi(value); break; case CLI_PASSWORD_OPT: STRCPY(cli_password,value); break; case PROC_USER_OPT: { struct passwd* pwd = getpwnam(value); if(!pwd) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown user name: %s\n",value); exit(-1); } else { procuserid = pwd->pw_uid; procuserid_set = 1; STRCPY(procusername,value); } } break; case PROC_GROUP_OPT: { struct group* gr = getgrnam(value); if(!gr) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown group name: %s\n",value); exit(-1); } else { procgroupid = gr->gr_gid; procgroupid_set = 1; STRCPY(procgroupname,value); } } break; case 'i': STRCPY(turn_params.relay_ifname, value); break; case 'm': #if defined(OPENSSL_THREADS) if(atoi(value)>MAX_NUMBER_OF_GENERAL_RELAY_SERVERS) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: max number of relay threads is 128.\n"); turn_params.general_relay_servers_number = MAX_NUMBER_OF_GENERAL_RELAY_SERVERS; } else if(atoi(value)<=0) { turn_params.general_relay_servers_number = 0; } else { turn_params.general_relay_servers_number = atoi(value); } #else TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: OpenSSL version is too old OR does not support threading,\n I am using single thread for relaying.\n"); #endif break; case 'd': STRCPY(turn_params.listener_ifname, value); break; case 'p': turn_params.listener_port = atoi(value); break; case TLS_PORT_OPT: turn_params.tls_listener_port = atoi(value); break; case ALT_PORT_OPT: turn_params.alt_listener_port = atoi(value); break; case ALT_TLS_PORT_OPT: turn_params.alt_tls_listener_port = atoi(value); break; case MIN_PORT_OPT: turn_params.min_port = atoi(value); break; case MAX_PORT_OPT: turn_params.max_port = atoi(value); break; case SECURE_STUN_OPT: turn_params.secure_stun = get_bool_value(value); break; case SHA256_OPT: if(get_bool_value(value)) turn_params.shatype = SHATYPE_SHA256; else turn_params.shatype = SHATYPE_SHA1; break; case NO_MULTICAST_PEERS_OPT: turn_params.no_multicast_peers = get_bool_value(value); break; case NO_LOOPBACK_PEERS_OPT: turn_params.no_loopback_peers = get_bool_value(value); break; case STALE_NONCE_OPT: turn_params.stale_nonce = get_bool_value(value); break; case MAX_ALLOCATE_TIMEOUT_OPT: TURN_MAX_ALLOCATE_TIMEOUT = atoi(value); TURN_MAX_ALLOCATE_TIMEOUT_STUN_ONLY = atoi(value); break; case 'S': turn_params.stun_only = get_bool_value(value); break; case NO_STUN_OPT: turn_params.no_stun = get_bool_value(value); break; case 'L': add_listener_addr(value); break; case 'E': add_relay_addr(value); break; case 'X': if(value) { char *div = strchr(value,'/'); if(div) { char *nval=strdup(value); div = strchr(nval,'/'); div[0]=0; ++div; ioa_addr apub,apriv; if(make_ioa_addr((const u08bits*)nval,0,&apub)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"-X : Wrong address format: %s\n",nval); } else { if(make_ioa_addr((const u08bits*)div,0,&apriv)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"-X : Wrong address format: %s\n",div); } else { ioa_addr_add_mapping(&apub,&apriv); } } turn_free(nval,strlen(nval)+1); } else { if(turn_params.external_ip) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "You cannot define external IP more than once in the configuration\n"); } else { turn_params.external_ip = (ioa_addr*)allocate_super_memory_engine(turn_params.listener.ioa_eng, sizeof(ioa_addr)); if(make_ioa_addr((const u08bits*)value,0,turn_params.external_ip)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"-X : Wrong address format: %s\n",value); turn_free(turn_params.external_ip,sizeof(ioa_addr)); turn_params.external_ip = NULL; } } } } break; case 'v': if(get_bool_value(value)) { turn_params.verbose = TURN_VERBOSE_NORMAL; } else { turn_params.verbose = TURN_VERBOSE_NONE; } break; case 'V': if(get_bool_value(value)) { turn_params.verbose = TURN_VERBOSE_EXTRA; } break; case 'o': turn_params.turn_daemon = get_bool_value(value); break; case 'a': if (get_bool_value(value)) { turn_params.users_params.users.ct = TURN_CREDENTIALS_LONG_TERM; turn_params.users_params.use_lt_credentials=1; } else { turn_params.users_params.users.ct = TURN_CREDENTIALS_UNDEFINED; turn_params.users_params.use_lt_credentials=0; } break; case 'A': if (get_bool_value(value)) { turn_params.users_params.users.ct = TURN_CREDENTIALS_SHORT_TERM; turn_params.users_params.use_st_credentials=1; } else { turn_params.users_params.users.ct = TURN_CREDENTIALS_UNDEFINED; turn_params.users_params.use_st_credentials=0; } break; case 'z': if (!get_bool_value(value)) { turn_params.users_params.users.ct = TURN_CREDENTIALS_UNDEFINED; turn_params.users_params.anon_credentials = 0; } else { turn_params.users_params.users.ct = TURN_CREDENTIALS_NONE; turn_params.users_params.anon_credentials = 1; } break; case 'f': turn_params.fingerprint = get_bool_value(value); break; case 'u': add_user_account(value,0); break; case 'b': STRCPY(turn_params.users_params.userdb, value); turn_params.users_params.userdb_type = TURN_USERDB_TYPE_FILE; break; #if !defined(TURN_NO_PQ) case 'e': STRCPY(turn_params.users_params.userdb, value); turn_params.users_params.userdb_type = TURN_USERDB_TYPE_PQ; break; #endif #if !defined(TURN_NO_MYSQL) case 'M': STRCPY(turn_params.users_params.userdb, value); turn_params.users_params.userdb_type = TURN_USERDB_TYPE_MYSQL; break; #endif #if !defined(TURN_NO_HIREDIS) case 'N': STRCPY(turn_params.users_params.userdb, value); turn_params.users_params.userdb_type = TURN_USERDB_TYPE_REDIS; break; case 'O': STRCPY(turn_params.redis_statsdb, value); turn_params.use_redis_statsdb = 1; break; #endif case AUTH_SECRET_OPT: turn_params.users_params.use_auth_secret_with_timestamp = 1; break; case STATIC_AUTH_SECRET_VAL_OPT: add_to_secrets_list(&turn_params.users_params.static_auth_secrets,value); turn_params.users_params.use_auth_secret_with_timestamp = 1; break; case AUTH_SECRET_TS_EXP: TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: Option --secret-ts-exp-time deprecated and has no effect.\n"); break; case 'r': STRCPY(turn_params.users_params.global_realm,value); break; case 'q': turn_params.users_params.users.user_quota = atoi(value); break; case 'Q': turn_params.users_params.users.total_quota = atoi(value); break; case 's': turn_params.max_bps = (band_limit_t)atol(value); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%lu bytes per second allowed per session\n",(unsigned long)turn_params.max_bps); break; case NO_UDP_OPT: turn_params.no_udp = get_bool_value(value); break; case NO_TCP_OPT: turn_params.no_tcp = get_bool_value(value); break; case NO_UDP_RELAY_OPT: turn_params.no_udp_relay = get_bool_value(value); break; case NO_TCP_RELAY_OPT: turn_params.no_tcp_relay = get_bool_value(value); break; case NO_TLS_OPT: #if defined(TURN_NO_TLS) turn_params.no_tls = 1; #else turn_params.no_tls = get_bool_value(value); #endif break; case NO_DTLS_OPT: #if !defined(TURN_NO_DTLS) turn_params.no_dtls = get_bool_value(value); #else turn_params.no_dtls = 1; #endif break; case CERT_FILE_OPT: STRCPY(turn_params.cert_file,value); break; case CA_FILE_OPT: STRCPY(turn_params.ca_cert_file,value); break; case DH_FILE_OPT: STRCPY(turn_params.dh_file,value); break; case PKEY_FILE_OPT: STRCPY(turn_params.pkey_file,value); break; case PKEY_PWD_OPT: STRCPY(turn_params.tls_password,value); break; case ALTERNATE_SERVER_OPT: add_alternate_server(value); break; case AUX_SERVER_OPT: add_aux_server(value); break; case UDP_SELF_BALANCE_OPT: turn_params.udp_self_balance = get_bool_value(value); break; case TLS_ALTERNATE_SERVER_OPT: add_tls_alternate_server(value); break; case ALLOWED_PEER_IPS: if (add_ip_list_range(value, &turn_params.ip_whitelist) == 0) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "White listing: %s\n", value); break; case DENIED_PEER_IPS: if (add_ip_list_range(value, &turn_params.ip_blacklist) == 0) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Black listing: %s\n", value); break; case CIPHER_LIST_OPT: STRCPY(turn_params.cipher_list,value); break; case PIDFILE_OPT: STRCPY(turn_params.pidfile,value); break; case 'C': if(value && *value) { turn_params.users_params.rest_api_separator=*value; } break; /* these options have been already taken care of before: */ case 'l': case NO_STDOUT_LOG_OPT: case SYSLOG_OPT: case 'c': case 'n': case 'h': break; default: fprintf(stderr,"\n%s\n", Usage); exit(-1); } } static int parse_arg_string(char *sarg, int *c, char **value) { int i = 0; char *name = sarg; while(*sarg) { if((*sarg==' ') || (*sarg=='=') || (*sarg=='\t')) { *sarg=0; do { ++sarg; } while((*sarg==' ') || (*sarg=='=') || (*sarg=='\t')); *value = sarg; break; } ++sarg; *value=sarg; } if(value && *value && **value=='\"') { *value += 1; size_t len = strlen(*value); while(len>0 && ( ((*value)[len-1]=='\n') || ((*value)[len-1]=='\r') || ((*value)[len-1]==' ') || ((*value)[len-1]=='\t') ) ) { (*value)[--len]=0; } if(len>0 && (*value)[len-1]=='\"') { (*value)[--len]=0; } } while(long_options[i].name) { if(strcmp(long_options[i].name,name)) { ++i; continue; } *c=long_options[i].val; return 0; } return -1; } static void read_config_file(int argc, char **argv, int pass) { static char config_file[1025] = DEFAULT_CONFIG_FILE; if(pass == 0) { if (argv) { int i = 0; for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "-c")) { if (i < argc - 1) { STRCPY(config_file, argv[i + 1]); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Wrong usage of -c option\n"); } } else if (!strcmp(argv[i], "-n")) { turn_params.do_not_use_config_file = 1; config_file[0]=0; return; } else if (!strcmp(argv[i], "-h")) { printf("\n%s\n",Usage); exit(0); } } } } if (!turn_params.do_not_use_config_file && config_file[0]) { FILE *f = NULL; char *full_path_to_config_file = NULL; full_path_to_config_file = find_config_file(config_file, 1); if (full_path_to_config_file) f = fopen(full_path_to_config_file, "r"); if (f && full_path_to_config_file) { char sbuf[1025]; char sarg[1035]; for (;;) { char *s = fgets(sbuf, sizeof(sbuf) - 1, f); if (!s) break; s = skip_blanks(s); if (s[0] == '#') continue; if (!s[0]) continue; size_t slen = strlen(s); while (slen && ((s[slen - 1] == 10) || (s[slen - 1] == 13))) s[--slen] = 0; if (slen) { int c = 0; char *value = NULL; STRCPY(sarg, s); if (parse_arg_string(sarg, &c, &value) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Bad configuration format: %s\n", sarg); } else if((pass == 0) && (c == 'l')) { set_logfile(value); } else if((pass==0) && (c==NO_STDOUT_LOG_OPT)) { set_no_stdout_log(get_bool_value(value)); } else if((pass==0) && (c==SYSLOG_OPT)) { set_log_to_syslog(get_bool_value(value)); } else if((pass == 0) && (c != 'u')) { set_option(c, value); } else if((pass > 0) && (c == 'u')) { set_option(c, value); } } } fclose(f); } else TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: Cannot find config file: %s. Default and command-line settings will be used.\n", config_file); } } static int adminmain(int argc, char **argv) { int c = 0; TURNADMIN_COMMAND_TYPE ct = TA_COMMAND_UNKNOWN; int is_st = 0; u08bits user[STUN_MAX_USERNAME_SIZE+1]=""; u08bits realm[STUN_MAX_REALM_SIZE+1]=""; u08bits pwd[STUN_MAX_PWD_SIZE+1]=""; u08bits secret[AUTH_SECRET_SIZE+1]=""; while (((c = getopt_long(argc, argv, ADMIN_OPTIONS, admin_long_options, NULL)) != -1)) { switch (c){ case 'k': ct = TA_PRINT_KEY; break; case 'a': ct = TA_UPDATE_USER; break; case 'd': ct = TA_DELETE_USER; break; case 'A': ct = TA_UPDATE_USER; is_st = 1; break; case 'D': ct = TA_DELETE_USER; is_st = 1; break; case 'l': ct = TA_LIST_USERS; break; case 'L': ct = TA_LIST_USERS; is_st = 1; break; #if !defined(TURN_NO_PQ) || !defined(TURN_NO_MYSQL) case 's': ct = TA_SET_SECRET; STRCPY(secret,optarg); break; case 'S': ct = TA_SHOW_SECRET; break; case 'X': ct = TA_DEL_SECRET; if(optarg) STRCPY(secret,optarg); break; case DEL_ALL_AUTH_SECRETS_OPT: ct = TA_DEL_SECRET; break; #endif case 'b': STRCPY(turn_params.users_params.userdb,optarg); turn_params.users_params.userdb_type = TURN_USERDB_TYPE_FILE; break; #if !defined(TURN_NO_PQ) case 'e': STRCPY(turn_params.users_params.userdb,optarg); turn_params.users_params.userdb_type = TURN_USERDB_TYPE_PQ; break; #endif #if !defined(TURN_NO_MYSQL) case 'M': STRCPY(turn_params.users_params.userdb,optarg); turn_params.users_params.userdb_type = TURN_USERDB_TYPE_MYSQL; break; #endif #if !defined(TURN_NO_HIREDIS) case 'N': STRCPY(turn_params.users_params.userdb,optarg); turn_params.users_params.userdb_type = TURN_USERDB_TYPE_REDIS; break; #endif case 'u': STRCPY(user,optarg); if(SASLprep((u08bits*)user)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user name: %s\n",user); exit(-1); } break; case 'r': STRCPY(realm,optarg); if(SASLprep((u08bits*)realm)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong realm: %s\n",realm); exit(-1); } break; case 'p': STRCPY(pwd,optarg); if(SASLprep((u08bits*)pwd)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong password: %s\n",pwd); exit(-1); } break; case 'H': if(get_bool_value(optarg)) turn_params.shatype = SHATYPE_SHA256; else turn_params.shatype = SHATYPE_SHA1; break; case 'h': printf("\n%s\n", AdminUsage); exit(0); break; default: fprintf(stderr,"\n%s\n", AdminUsage); exit(-1); } } if(is_st && (turn_params.users_params.userdb_type == TURN_USERDB_TYPE_FILE)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "ERROR: you have to use a PostgreSQL or MySQL database with short-term credentials\n"); exit(-1); } if(!strlen(turn_params.users_params.userdb) && (turn_params.users_params.userdb_type == TURN_USERDB_TYPE_FILE)) STRCPY(turn_params.users_params.userdb,DEFAULT_USERDB_FILE); if(ct == TA_COMMAND_UNKNOWN) { fprintf(stderr,"\n%s\n", AdminUsage); exit(-1); } argc -= optind; argv += optind; if(argc != 0) { fprintf(stderr,"\n%s\n", AdminUsage); exit(-1); } return adminuser(user, realm, pwd, secret, ct, is_st); } static void print_features(unsigned long mfn) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\nRFC 3489/5389/5766/5780/6062/6156 STUN/TURN Server\nVersion %s\n",TURN_SOFTWARE); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\nMax number of open files/sockets allowed for this process: %lu\n",mfn); if(turn_params.net_engine_version == 1) mfn = mfn/3; else mfn = mfn/2; mfn = ((unsigned long)(mfn/500))*500; if(mfn<500) mfn = 500; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\nDue to the open files/sockets limitation,\nmax supported number of TURN Sessions possible is: %lu (approximately)\n",mfn); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n\n==== Show him the instruments, Practical Frost: ====\n\n"); #if defined(TURN_NO_TLS) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TLS is not supported\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TLS supported\n"); #endif #if defined(TURN_NO_DTLS) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS is not supported\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS supported\n"); #endif #if !defined(TURN_NO_HIREDIS) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Redis supported\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Redis is not supported\n"); #endif #if !defined(TURN_NO_PQ) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "PostgreSQL supported\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "PostgreSQL is not supported\n"); #endif #if !defined(TURN_NO_MYSQL) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MySQL supported\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MySQL is not supported\n"); #endif #if defined(OPENSSL_THREADS) //TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "OpenSSL multithreading supported\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "OpenSSL multithreading is not supported (?!)\n"); #endif #if OPENSSL_VERSION_NUMBER >= 0x10000000L TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "OpenSSL version: fresh enough\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "OpenSSL version: antique\n"); #endif TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Default Net Engine version: %d (%s)\n\n=====================================================\n\n", (int)turn_params.net_engine_version, turn_params.net_engine_version_txt[(int)turn_params.net_engine_version]); } #if defined(__linux__) || defined(__LINUX__) || defined(__linux) || defined(linux__) || defined(LINUX) || defined(__LINUX) || defined(LINUX__) #include #endif static void set_network_engine(void) { if(turn_params.net_engine_version != NEV_UNKNOWN) return; turn_params.net_engine_version = NEV_UDP_SOCKET_PER_ENDPOINT; #if defined(SO_REUSEPORT) #if defined(__linux__) || defined(__LINUX__) || defined(__linux) || defined(linux__) || defined(LINUX) || defined(__LINUX) || defined(LINUX__) turn_params.net_engine_version = NEV_UDP_SOCKET_PER_THREAD; #else /* BSD ? */ turn_params.net_engine_version = NEV_UDP_SOCKET_PER_SESSION; #endif /* Linux */ #else /* defined(SO_REUSEPORT) */ #if defined(__linux__) || defined(__LINUX__) || defined(__linux) || defined(linux__) || defined(LINUX) || defined(__LINUX) || defined(LINUX__) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33) //net_engine_version = NEV_UDP_SOCKET_PER_SESSION; turn_params.net_engine_version = NEV_UDP_SOCKET_PER_ENDPOINT; #else turn_params.net_engine_version = NEV_UDP_SOCKET_PER_ENDPOINT; #endif /* Linux version */ #endif /* Linux */ #endif /* defined(SO_REUSEPORT) */ } static void drop_privileges(void) { if(procgroupid_set) { if(getgid() != procgroupid) { if (setgid(procgroupid) != 0) { perror("setgid: Unable to change group privileges"); exit(-1); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "New GID: %s(%lu)\n", procgroupname, (unsigned long)procgroupid); } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Keep GID: %s(%lu)\n", procgroupname, (unsigned long)procgroupid); } } if(procuserid_set) { if(procuserid != getuid()) { if (setuid(procuserid) != 0) { perror("setuid: Unable to change user privileges"); exit(-1); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "New UID: %s(%lu)\n", procusername, (unsigned long)procuserid); } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Keep UID: %s(%lu)\n", procusername, (unsigned long)procuserid); } } } int main(int argc, char **argv) { int c = 0; IS_TURN_SERVER = 1; set_execdir(); init_super_memory(); init_turn_server_addrs_list(&turn_params.alternate_servers_list); init_turn_server_addrs_list(&turn_params.tls_alternate_servers_list); init_turn_server_addrs_list(&turn_params.aux_servers_list); set_network_engine(); init_listener(); init_secrets_list(&turn_params.users_params.static_auth_secrets); init_dynamic_ip_lists(); if (!strstr(argv[0], "turnadmin")) { while (((c = getopt_long(argc, argv, OPTIONS, long_options, NULL)) != -1)) { switch (c){ case 'l': set_logfile(optarg); break; case NO_STDOUT_LOG_OPT: set_no_stdout_log(get_bool_value(optarg)); break; case SYSLOG_OPT: set_log_to_syslog(get_bool_value(optarg)); break; default: ; } } } optind = 0; #if defined(TURN_NO_TLS) turn_params.no_tls = 1; #endif #if defined(TURN_NO_DTLS) turn_params.no_dtls = 1; #endif #if defined(_SC_NPROCESSORS_ONLN) { long cpus = (long)sysconf(_SC_NPROCESSORS_CONF); if(cpus<1) cpus = 1; else if(cpus>MAX_NUMBER_OF_GENERAL_RELAY_SERVERS) cpus = MAX_NUMBER_OF_GENERAL_RELAY_SERVERS; turn_params.general_relay_servers_number = (turnserver_id)cpus; } #endif ns_bzero(&turn_params.users_params.users,sizeof(turn_user_db)); turn_params.users_params.users.ct = TURN_CREDENTIALS_NONE; turn_params.users_params.users.static_accounts = ur_string_map_create(free); turn_params.users_params.users.dynamic_accounts = ur_string_map_create(free); turn_params.users_params.users.alloc_counters = ur_string_map_create(NULL); if(strstr(argv[0],"turnadmin")) return adminmain(argc,argv); { unsigned long mfn = set_system_parameters(1); print_features(mfn); } read_config_file(argc,argv,0); while (((c = getopt_long(argc, argv, OPTIONS, long_options, NULL)) != -1)) { if(c != 'u') set_option(c,optarg); } read_config_file(argc,argv,1); optind = 0; while (((c = getopt_long(argc, argv, OPTIONS, long_options, NULL)) != -1)) { if(c == 'u') { set_option(c,optarg); } } if(turn_params.no_udp_relay && turn_params.no_tcp_relay) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIG ERROR: --no-udp-relay and --no-tcp-relay options cannot be used together.\n"); exit(-1); } if(turn_params.no_udp_relay) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\nCONFIG: --no-udp-relay: UDP relay endpoints are not allowed.\n"); } if(turn_params.no_tcp_relay) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\nCONFIG: --no-tcp-relay: TCP relay endpoints are not allowed.\n"); } if(turn_params.server_relay) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "\nCONFIG: WARNING: --server-relay: NON-STANDARD AND DANGEROUS OPTION.\n"); } if(!strlen(turn_params.users_params.userdb) && (turn_params.users_params.userdb_type == TURN_USERDB_TYPE_FILE)) STRCPY(turn_params.users_params.userdb,DEFAULT_USERDB_FILE); read_userdb_file(0); update_white_and_black_lists(); argc -= optind; argv += optind; if(argc>0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIGURATION ALERT: Unknown argument: %s\n",argv[argc-1]); } if(turn_params.users_params.use_lt_credentials && turn_params.users_params.anon_credentials) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIG ERROR: -a and -z options cannot be used together.\n"); exit(-1); } if(turn_params.users_params.use_st_credentials && turn_params.users_params.anon_credentials) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIG ERROR: -A and -z options cannot be used together.\n"); exit(-1); } if(turn_params.users_params.use_lt_credentials && turn_params.users_params.use_st_credentials) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIG ERROR: -a and -A options cannot be used together.\n"); exit(-1); } if(!turn_params.users_params.use_lt_credentials && !turn_params.users_params.anon_credentials && !turn_params.users_params.use_st_credentials) { if(turn_params.users_params.users_number) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "\nCONFIGURATION ALERT: you specified long-term user accounts, (-u option) \n but you did not specify the long-term credentials option\n (-a or --lt-cred-mech option).\n I am turning --lt-cred-mech ON for you, but double-check your configuration.\n"); turn_params.users_params.users.ct = TURN_CREDENTIALS_LONG_TERM; turn_params.users_params.use_lt_credentials=1; } else { turn_params.users_params.users.ct = TURN_CREDENTIALS_NONE; turn_params.users_params.use_lt_credentials=0; } } if(turn_params.users_params.use_lt_credentials) { if(!turn_params.users_params.users_number && (turn_params.users_params.userdb_type == TURN_USERDB_TYPE_FILE) && !turn_params.users_params.use_auth_secret_with_timestamp) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "\nCONFIGURATION ALERT: you did not specify any user account, (-u option) \n but you did specified a long-term credentials mechanism option (-a option).\n The TURN Server will be inaccessible.\n Check your configuration.\n"); } else if(!turn_params.users_params.global_realm[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "\nCONFIGURATION ALERT: you did specify the long-term credentials usage\n but you did not specify the realm option (-r option).\n The TURN Server will be inaccessible.\n Check your configuration.\n"); } } if(turn_params.users_params.anon_credentials) { if(turn_params.users_params.users_number) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "\nCONFIGURATION ALERT: you specified user accounts, (-u option) \n but you also specified the anonymous user access option (-z or --no-auth option).\n User accounts will be ignored.\n"); turn_params.users_params.users.ct = TURN_CREDENTIALS_NONE; turn_params.users_params.use_lt_credentials=0; turn_params.users_params.use_st_credentials=0; } } if(turn_params.users_params.use_auth_secret_with_timestamp && turn_params.users_params.use_st_credentials) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIGURATION ERROR: Authentication secret (REST API) cannot be used with short-term credentials mechanism.\n"); exit(-1); } openssl_setup(); int local_listeners = 0; if (!turn_params.listener.addrs_number) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "NO EXPLICIT LISTENER ADDRESS(ES) ARE CONFIGURED\n"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "===========Discovering listener addresses: =========\n"); int maddrs = make_local_listeners_list(); if((maddrs<1) || !turn_params.listener.addrs_number) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot configure any meaningful IP listener address\n", __FUNCTION__); fprintf(stderr,"\n%s\n", Usage); exit(-1); } local_listeners = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "=====================================================\n"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Total: %d 'real' addresses discovered\n",maddrs); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "=====================================================\n"); } if (!turn_params.relays_number) { if(!local_listeners && turn_params.listener.addrs_number && turn_params.listener.addrs) { size_t la = 0; for(la=0;la0) exit(0); if(pid<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "ERROR: Cannot start daemon process\n"); exit(-1); } #else if(daemon(1,0)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "ERROR: Cannot start daemon process\n"); exit(-1); } reset_rtpprintf(); #endif } if(turn_params.pidfile[0]) { char s[2049]; FILE *f = fopen(turn_params.pidfile,"w"); if(f) { STRCPY(s,turn_params.pidfile); } else { snprintf(s,sizeof(s),"Cannot create pid file: %s",turn_params.pidfile); perror(s); TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "%s\n", s); { const char *pfs[] = {"/var/run/turnserver.pid", "/var/spool/turnserver.pid", "/var/turnserver.pid", "/var/tmp/turnserver.pid", "/tmp/turnserver.pid", "turnserver.pid", NULL}; const char **ppfs = pfs; while(*ppfs) { f = fopen(*ppfs,"w"); if(f) { STRCPY(s,*ppfs); break; } else { ++ppfs; } } } } if(f) { fprintf(f,"%lu\n",(unsigned long)getpid()); fclose(f); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "pid file created: %s\n", s); } } setup_server(); drop_privileges(); run_listener_server(turn_params.listener.event_base); return 0; } ////////// OpenSSL locking //////////////////////////////////////// #if defined(OPENSSL_THREADS) static pthread_mutex_t* mutex_buf = NULL; static void locking_function(int mode, int n, const char *file, int line) { UNUSED_ARG(file); UNUSED_ARG(line); if (mode & CRYPTO_LOCK) pthread_mutex_lock(&mutex_buf[n]); else pthread_mutex_unlock(&mutex_buf[n]); } #if OPENSSL_VERSION_NUMBER >= 0x10000000L static void id_function(CRYPTO_THREADID *ctid) { CRYPTO_THREADID_set_numeric(ctid, (unsigned long)pthread_self()); } #else static unsigned long id_function(void) { return (unsigned long)pthread_self(); } #endif #endif static int THREAD_setup(void) { #if defined(OPENSSL_THREADS) int i; mutex_buf = (pthread_mutex_t*) turn_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t)); if (!mutex_buf) return 0; for (i = 0; i < CRYPTO_num_locks(); i++) pthread_mutex_init(&mutex_buf[i], NULL); #if OPENSSL_VERSION_NUMBER >= 0x10000000L CRYPTO_THREADID_set_callback(id_function); #else CRYPTO_set_id_callback(id_function); #endif CRYPTO_set_locking_callback(locking_function); #endif return 1; } int THREAD_cleanup(void); int THREAD_cleanup(void) { #if defined(OPENSSL_THREADS) int i; if (!mutex_buf) return 0; #if OPENSSL_VERSION_NUMBER >= 0x10000000L CRYPTO_THREADID_set_callback(NULL); #else CRYPTO_set_id_callback(NULL); #endif CRYPTO_set_locking_callback(NULL); for (i = 0; i < CRYPTO_num_locks(); i++) pthread_mutex_destroy(&mutex_buf[i]); turn_free(mutex_buf,sizeof(pthread_mutex_t)); mutex_buf = NULL; #endif return 1; } static void adjust_key_file_name(char *fn, const char* file_title, int critical) { char *full_path_to_file = NULL; if(!fn[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"\nERROR: you must set the %s file parameter\n",file_title); goto keyerr; } else { full_path_to_file = find_config_file(fn, 1); FILE *f = full_path_to_file ? fopen(full_path_to_file,"r") : NULL; if(!f) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING,"WARNING: cannot find %s file: %s (1)\n",file_title,fn); goto keyerr; } if(!full_path_to_file) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING,"WARNING: cannot find %s file: %s (2)\n",file_title,fn); goto keyerr; } strncpy(fn,full_path_to_file,sizeof(turn_params.cert_file)-1); fn[sizeof(turn_params.cert_file)-1]=0; if(full_path_to_file) turn_free(full_path_to_file,strlen(full_path_to_file)+1); return; } keyerr: { if(critical) { turn_params.no_tls = 1; turn_params.no_dtls = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING,"WARNING: cannot start TLS and DTLS listeners because %s file is not set properly\n",file_title); } if(full_path_to_file) turn_free(full_path_to_file,strlen(full_path_to_file)+1); return; } } static void adjust_key_file_names(void) { if(turn_params.ca_cert_file[0]) adjust_key_file_name(turn_params.ca_cert_file,"CA",1); adjust_key_file_name(turn_params.cert_file,"certificate",1); adjust_key_file_name(turn_params.pkey_file,"private key",1); if(turn_params.dh_file[0]) adjust_key_file_name(turn_params.dh_file,"DH key",0); } static DH *get_dh566(void) { unsigned char dh566_p[] = { 0x36,0x53,0xA8,0x9C,0x3C,0xF1,0xD1,0x1B,0x2D,0xA2,0x64,0xDE, 0x59,0x3B,0xE3,0x8C,0x27,0x74,0xC2,0xBE,0x9B,0x6D,0x56,0xE7, 0xDF,0xFF,0x67,0x6A,0xD2,0x0C,0xE8,0x9E,0x52,0x00,0x05,0xB3, 0x53,0xF7,0x1C,0x41,0xB2,0xAC,0x38,0x16,0x32,0x3A,0x8E,0x90, 0x6C,0x7E,0xD1,0x44,0xCB,0xF9,0x2D,0x1E,0x4A,0x9A,0x32,0x81, 0x58,0xE1,0xE1,0x17,0xC1,0x9C,0xF1,0x1E,0x96,0x2D,0x5F }; // -----BEGIN DH PARAMETERS----- //MEwCRzZTqJw88dEbLaJk3lk744wndMK+m21W59//Z2rSDOieUgAFs1P3HEGyrDgW //MjqOkGx+0UTL+S0eSpoygVjh4RfBnPEeli1fAgEF // -----END DH PARAMETERS----- unsigned char dh566_g[] = { 0x05 }; DH *dh; if ((dh = DH_new()) == NULL ) return (NULL ); dh->p = BN_bin2bn(dh566_p, sizeof(dh566_p), NULL ); dh->g = BN_bin2bn(dh566_g, sizeof(dh566_g), NULL ); if ((dh->p == NULL )|| (dh->g == NULL)){ DH_free(dh); return(NULL);} return (dh); } static DH *get_dh1066(void) { unsigned char dh1066_p[] = { 0x02,0x0E,0x26,0x6F,0xAA,0x9F,0xA8,0xE5,0x3F,0x70,0x88,0xF1, 0xA9,0x29,0xAE,0x1A,0x2B,0xA8,0x2F,0xE8,0xE5,0x0E,0x81,0x78, 0xD7,0x12,0x41,0xDC,0xE2,0xD5,0x10,0x6F,0x8A,0x35,0x23,0xCE, 0x66,0x93,0x67,0x14,0xEA,0x0A,0x61,0xD4,0x43,0x63,0x5C,0xDF, 0xDE,0xF5,0xB9,0xC6,0xB4,0x8C,0xBA,0x1A,0x25,0x9F,0x73,0x0F, 0x1E,0x1A,0x97,0x42,0x2E,0x60,0x9E,0x4C,0x3C,0x70,0x6A,0xFB, 0xDD,0xAA,0x7A,0x48,0xA5,0x1E,0x87,0xC8,0xA3,0x5E,0x26,0x40, 0x1B,0xDE,0x08,0x5E,0xA2,0xB8,0xE8,0x76,0x43,0xE8,0xF1,0x4B, 0x35,0x4C,0x38,0x92,0xB9,0xFF,0x61,0xE6,0x6C,0xBA,0xF9,0x16, 0x36,0x3C,0x69,0x2D,0x57,0x90,0x62,0x8A,0xD0,0xD4,0xFB,0xB2, 0x5A,0x61,0x99,0xA9,0xE8,0x93,0x80,0xA2,0xB7,0xDC,0xB1,0x6A, 0xAF,0xE3 }; // -----BEGIN DH PARAMETERS----- // MIGMAoGGAg4mb6qfqOU/cIjxqSmuGiuoL+jlDoF41xJB3OLVEG+KNSPOZpNnFOoK // YdRDY1zf3vW5xrSMuholn3MPHhqXQi5gnkw8cGr73ap6SKUeh8ijXiZAG94IXqK4 // 6HZD6PFLNUw4krn/YeZsuvkWNjxpLVeQYorQ1PuyWmGZqeiTgKK33LFqr+MCAQI= // -----END DH PARAMETERS----- unsigned char dh1066_g[] = { 0x02 }; DH *dh; if ((dh = DH_new()) == NULL ) return (NULL ); dh->p = BN_bin2bn(dh1066_p, sizeof(dh1066_p), NULL ); dh->g = BN_bin2bn(dh1066_g, sizeof(dh1066_g), NULL ); if ((dh->p == NULL )|| (dh->g == NULL)){ DH_free(dh); return(NULL);} return (dh); } static DH *get_dh2066(void) { unsigned char dh2066_p[] = { 0x03,0x31,0x77,0x20,0x58,0xA6,0x69,0xA3,0x9D,0x2D,0x5E,0xE0, 0x5C,0x46,0x82,0x0F,0x9E,0x80,0xF0,0x00,0x2A,0xF9,0x0F,0x62, 0x1F,0x89,0xCE,0x7D,0x2A,0xFD,0xC5,0x9A,0x7C,0x6A,0x60,0x2C, 0xF1,0xDD,0xD4,0x4D,0x6B,0xCD,0xE9,0x95,0xDB,0x42,0x97,0xBA, 0xE4,0xAF,0x41,0x38,0x8F,0x57,0x31,0xA4,0x39,0xDD,0x31,0xC3, 0x6F,0x98,0x0E,0xE3,0xB1,0x43,0xD1,0x36,0xB0,0x01,0x28,0x42, 0x71,0xD3,0xB0,0x36,0xA0,0x47,0x99,0x25,0x9B,0x32,0xF5,0x86, 0xB1,0x13,0x5C,0x24,0x8D,0x8D,0x7F,0xE2,0x7F,0x9A,0xC1,0x52, 0x58,0xC0,0x63,0xAA,0x00,0x7C,0x1F,0x11,0xBD,0xAC,0x4C,0x2D, 0xE0,0xA2,0x9D,0x4E,0x21,0xE4,0x0B,0xCD,0x24,0x92,0xD2,0x37, 0x27,0x84,0x59,0x90,0x46,0x2F,0xD5,0xB9,0x27,0x93,0x18,0x88, 0xBD,0x91,0x5B,0x87,0x55,0x56,0xD8,0x1B,0xE4,0xCF,0x1C,0xAA, 0xBC,0xCF,0x80,0x1E,0x35,0x2D,0xB1,0xBC,0x35,0x31,0x92,0x62, 0x3C,0x91,0x8D,0x62,0xDA,0xCF,0x83,0x63,0x12,0x4B,0x30,0x80, 0xEE,0x82,0x3C,0x2C,0xD2,0x17,0x13,0x1F,0xF9,0x62,0x33,0x5C, 0x63,0xD8,0x75,0x5B,0xAA,0x16,0x5A,0x36,0x49,0x17,0x77,0xB7, 0x74,0xBD,0x3E,0x3F,0x98,0x20,0x59,0x5E,0xC7,0x72,0xE8,0xA3, 0x89,0x21,0xB4,0x3C,0x25,0xF4,0xF4,0x21,0x96,0x5A,0xA6,0x77, 0xFF,0x2C,0x3A,0xFC,0x98,0x5F,0xC1,0xBF,0x2A,0xCF,0xB8,0x62, 0x67,0x23,0xE8,0x2F,0xCC,0x7B,0x32,0x1B,0x6B,0x33,0x67,0x0A, 0xCB,0xD0,0x1F,0x65,0xD7,0x84,0x54,0xF6,0xF1,0x88,0xB5,0xBB, 0x0C,0x63,0x65,0x34,0xE4,0x66,0x4B }; // -----BEGIN DH PARAMETERS----- //MIIBCgKCAQMDMXcgWKZpo50tXuBcRoIPnoDwACr5D2Ific59Kv3FmnxqYCzx3dRN //a83pldtCl7rkr0E4j1cxpDndMcNvmA7jsUPRNrABKEJx07A2oEeZJZsy9YaxE1wk //jY1/4n+awVJYwGOqAHwfEb2sTC3gop1OIeQLzSSS0jcnhFmQRi/VuSeTGIi9kVuH //VVbYG+TPHKq8z4AeNS2xvDUxkmI8kY1i2s+DYxJLMIDugjws0hcTH/liM1xj2HVb //qhZaNkkXd7d0vT4/mCBZXsdy6KOJIbQ8JfT0IZZapnf/LDr8mF/BvyrPuGJnI+gv //zHsyG2szZwrL0B9l14RU9vGItbsMY2U05GZLAgEF // -----END DH PARAMETERS----- unsigned char dh2066_g[] = { 0x05 }; DH *dh; if ((dh = DH_new()) == NULL ) return (NULL ); dh->p = BN_bin2bn(dh2066_p, sizeof(dh2066_p), NULL ); dh->g = BN_bin2bn(dh2066_g, sizeof(dh2066_g), NULL ); if ((dh->p == NULL )|| (dh->g == NULL)){ DH_free(dh); return(NULL);} return (dh); } static int pem_password_func(char *buf, int size, int rwflag, void *password) { UNUSED_ARG(rwflag); strncpy(buf, (char * )(password), size); buf[size - 1] = 0; return (strlen(buf)); } static void set_ctx(SSL_CTX* ctx, const char *protocol) { SSL_CTX_set_default_passwd_cb_userdata(ctx, turn_params.tls_password); SSL_CTX_set_default_passwd_cb(ctx, pem_password_func); if(!(turn_params.cipher_list[0])) STRCPY(turn_params.cipher_list,DEFAULT_CIPHER_LIST); SSL_CTX_set_cipher_list(ctx, turn_params.cipher_list); SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); if (!SSL_CTX_use_certificate_chain_file(ctx, turn_params.cert_file)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: no certificate found\n", protocol); } else { print_abs_file_name(protocol, ": Certificate", turn_params.cert_file); } if (!SSL_CTX_use_PrivateKey_file(ctx, turn_params.pkey_file, SSL_FILETYPE_PEM)) { if (!SSL_CTX_use_RSAPrivateKey_file(ctx, turn_params.pkey_file, SSL_FILETYPE_PEM)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: no valid private key found, or invalid private key password provided\n", protocol); } else { print_abs_file_name(protocol, ": Private RSA key", turn_params.pkey_file); } } else { print_abs_file_name(protocol, ": Private key", turn_params.pkey_file); } if (!SSL_CTX_check_private_key(ctx)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: invalid private key\n", protocol); } if(turn_params.ca_cert_file[0]) { if (!SSL_CTX_load_verify_locations(ctx, turn_params.ca_cert_file, NULL )) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot load CA from file: %s\n", turn_params.ca_cert_file); } SSL_CTX_set_client_CA_list(ctx,SSL_load_client_CA_file(turn_params.ca_cert_file)); /* Set to require peer (client) certificate verification */ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE, NULL); /* Set the verification depth to 9 */ SSL_CTX_set_verify_depth(ctx, 9); } else { SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); } #if !defined(OPENSSL_NO_EC) && defined(OPENSSL_EC_NAMED_CURVE) { //Elliptic curve algorithms: int nid = NID_X9_62_prime256v1; if (turn_params.ec_curve_name[0]) { nid = OBJ_sn2nid(turn_params.ec_curve_name); if (nid == 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"unknown curve name (%s), using NID_X9_62_prime256v1\n",turn_params.ec_curve_name); nid = NID_X9_62_prime256v1; } } EC_KEY *ecdh = EC_KEY_new_by_curve_name(nid); if (!ecdh) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: allocate EC suite\n"); } else { SSL_CTX_set_tmp_ecdh(ctx, ecdh); EC_KEY_free(ecdh); } } #endif {//DH algorithms: DH *dh = NULL; if(turn_params.dh_file[0]) { FILE *paramfile = fopen(turn_params.dh_file, "r"); if (!paramfile) { perror("Cannot open DH file"); } else { dh = PEM_read_DHparams(paramfile, NULL, NULL, NULL); fclose(paramfile); if(dh) { turn_params.dh_key_size = DH_CUSTOM; } } } if(!dh) { if(turn_params.dh_key_size == DH_566) dh = get_dh566(); else if(turn_params.dh_key_size == DH_2066) dh = get_dh2066(); else dh = get_dh1066(); } if(!dh) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: cannot allocate DH suite\n"); } else { if (1 != SSL_CTX_set_tmp_dh (ctx, dh)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: cannot set DH\n"); } DH_free (dh); } } { int op = 0; #if defined(SSL_OP_NO_SSLv2) if(turn_params.no_sslv2) op |= SSL_OP_NO_SSLv2; #endif if(turn_params.no_sslv3) op |= SSL_OP_NO_SSLv3; if(turn_params.no_tlsv1) op |= SSL_OP_NO_TLSv1; #if defined(SSL_OP_NO_TLSv1_1) if(turn_params.no_tlsv1_1) op |= SSL_OP_NO_TLSv1_1; #endif #if defined(SSL_OP_NO_TLSv1_2) if(turn_params.no_tlsv1_2) op |= SSL_OP_NO_TLSv1_2; #endif #if defined(SSL_OP_CIPHER_SERVER_PREFERENCE) op |= SSL_OP_CIPHER_SERVER_PREFERENCE; #endif #if defined(SSL_OP_SINGLE_DH_USE) op |= SSL_OP_SINGLE_DH_USE; #endif #if defined(SSL_OP_SINGLE_ECDH_USE) op |= SSL_OP_SINGLE_ECDH_USE; #endif SSL_CTX_set_options(ctx, op); } } static void openssl_setup(void) { THREAD_setup(); SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); #if defined(TURN_NO_TLS) if(!turn_params.no_tls) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "WARNING: TLS is not supported\n"); turn_params.no_tls = 1; } #endif if(!(turn_params.no_tls && turn_params.no_dtls) && !turn_params.cert_file[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING,"\nWARNING: certificate file is not specified, I cannot start TLS/DTLS services.\nOnly 'plain' UDP/TCP listeners can be started.\n"); turn_params.no_tls = 1; turn_params.no_dtls = 1; } if(!(turn_params.no_tls && turn_params.no_dtls) && !turn_params.pkey_file[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING,"\nWARNING: private key file is not specified, I cannot start TLS/DTLS services.\nOnly 'plain' UDP/TCP listeners can be started.\n"); turn_params.no_tls = 1; turn_params.no_dtls = 1; } if(!(turn_params.no_tls && turn_params.no_dtls)) { adjust_key_file_names(); } if(!turn_params.no_tls) { turn_params.tls_ctx_ssl23 = SSL_CTX_new(SSLv23_server_method()); /*compatibility mode */ set_ctx(turn_params.tls_ctx_ssl23,"SSL23"); if(!turn_params.no_tlsv1) { turn_params.tls_ctx_v1_0 = SSL_CTX_new(TLSv1_server_method()); set_ctx(turn_params.tls_ctx_v1_0,"TLS1.0"); } #if defined(SSL_TXT_TLSV1_1) if(!turn_params.no_tlsv1_1) { turn_params.tls_ctx_v1_1 = SSL_CTX_new(TLSv1_1_server_method()); set_ctx(turn_params.tls_ctx_v1_1,"TLS1.1"); } #if defined(SSL_TXT_TLSV1_2) if(!turn_params.no_tlsv1_2) { turn_params.tls_ctx_v1_2 = SSL_CTX_new(TLSv1_2_server_method()); set_ctx(turn_params.tls_ctx_v1_2,"TLS1.2"); } #endif #endif TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TLS cipher suite: %s\n",turn_params.cipher_list); } if(!turn_params.no_dtls) { #if defined(TURN_NO_DTLS) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "ERROR: DTLS is not supported.\n"); #else if(OPENSSL_VERSION_NUMBER < 0x10000000L) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: TURN Server was compiled with rather old OpenSSL version, DTLS may not be working correctly.\n"); } turn_params.dtls_ctx = SSL_CTX_new(DTLSv1_server_method()); set_ctx(turn_params.dtls_ctx,"DTLS"); SSL_CTX_set_read_ahead(turn_params.dtls_ctx, 1); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS cipher suite: %s\n",turn_params.cipher_list); #endif } } /////////////////////////////// turnserver-3.2.3.1/src/apps/relay/tls_listener.c000644 001751 001751 00000016017 12315706777 021562 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "apputils.h" #include "mainrelay.h" #include "ns_turn_utils.h" #include "tls_listener.h" #include "ns_ioalib_impl.h" #include /////////////////////////////////////////////////// #define FUNCSTART if(server && eve(server->verbose)) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s:%d:start\n",__FUNCTION__,__LINE__) #define FUNCEND if(server && eve(server->verbose)) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s:%d:end\n",__FUNCTION__,__LINE__) struct tls_listener_relay_server_info { char ifname[1025]; ioa_addr addr; ioa_engine_handle e; int verbose; struct evconnlistener *l; struct message_to_relay sm; ioa_engine_new_connection_event_handler connect_cb; struct relay_server *relay_server; }; /////////////// io handlers /////////////////// static void server_input_handler(struct evconnlistener *l, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *arg) { UNUSED_ARG(l); tls_listener_relay_server_type * server = (tls_listener_relay_server_type*) arg; if(!(server->connect_cb)) { socket_closesocket(fd); return; } FUNCSTART; if (!server) return; ns_bcopy(sa,&(server->sm.m.sm.nd.src_addr),socklen); addr_debug_print(server->verbose, &(server->sm.m.sm.nd.src_addr),"tcp or tls connected to"); SOCKET_TYPE st = TENTATIVE_TCP_SOCKET; if(turn_params.no_tls) st = TCP_SOCKET; else if(turn_params.no_tcp) st = TLS_SOCKET; ioa_socket_handle ioas = create_ioa_socket_from_fd( server->e, fd, NULL, st, CLIENT_SOCKET, &(server->sm.m.sm.nd.src_addr), &(server->addr)); if (ioas) { ioas->listener_server = server; server->sm.m.sm.nd.recv_ttl = TTL_IGNORE; server->sm.m.sm.nd.recv_tos = TOS_IGNORE; server->sm.m.sm.nd.nbh = NULL; server->sm.m.sm.s = ioas; server->sm.relay_server = server->relay_server; int rc = server->connect_cb(server->e, &(server->sm)); if (rc < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot create tcp or tls session\n"); } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot create ioa_socket from FD\n"); socket_closesocket(fd); } FUNCEND ; } ///////////////////// operations ////////////////////////// static int create_server_listener(tls_listener_relay_server_type* server) { FUNCSTART; if(!server) return -1; evutil_socket_t tls_listen_fd = -1; tls_listen_fd = socket(server->addr.ss.sa_family, SOCK_STREAM, 0); if (tls_listen_fd < 0) { perror("socket"); return -1; } if(sock_bind_to_device(tls_listen_fd, (unsigned char*)server->ifname)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Cannot bind listener socket to device %s\n",server->ifname); } { const int max_binding_time = 60; int addr_bind_cycle = 0; retry_addr_bind: if(addr_bind(tls_listen_fd,&server->addr,1)<0) { perror("Cannot bind local socket to addr"); char saddr[129]; addr_to_string(&server->addr,(u08bits*)saddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING,"Cannot bind TCP/TLS listener socket to addr %s\n",saddr); if(addr_bind_cyclel = evconnlistener_new(server->e->event_base, server_input_handler, server, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 1024, tls_listen_fd); if(!(server->l)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Cannot create TLS listener\n"); socket_closesocket(tls_listen_fd); return -1; } if(!turn_params.no_tcp && !turn_params.no_tls) addr_debug_print(server->verbose, &server->addr,"TCP/TLS listener opened on "); else if(!turn_params.no_tls) addr_debug_print(server->verbose, &server->addr,"TLS listener opened on "); else if(!turn_params.no_tcp) addr_debug_print(server->verbose, &server->addr,"TCP listener opened on "); FUNCEND; return 0; } static int init_server(tls_listener_relay_server_type* server, const char* ifname, const char *local_address, int port, int verbose, ioa_engine_handle e, ioa_engine_new_connection_event_handler send_socket, struct relay_server *relay_server) { if(!server) return -1; server->connect_cb = send_socket; server->relay_server = relay_server; if(ifname) STRCPY(server->ifname,ifname); if(make_ioa_addr((const u08bits*)local_address, port, &server->addr)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot create a TCP/TLS listener for address: %s\n",local_address); return -1; } server->verbose=verbose; server->e = e; return create_server_listener(server); } /////////////////////////////////////////////////////////// tls_listener_relay_server_type* create_tls_listener_server(const char* ifname, const char *local_address, int port, int verbose, ioa_engine_handle e, ioa_engine_new_connection_event_handler send_socket, struct relay_server *relay_server) { tls_listener_relay_server_type* server = (tls_listener_relay_server_type*) allocate_super_memory_engine(e,sizeof(tls_listener_relay_server_type)); if (init_server(server, ifname, local_address, port, verbose, e, send_socket, relay_server) < 0) { return NULL ; } else { return server; } } ////////////////////////////////////////////////////////////////// turnserver-3.2.3.1/src/apps/relay/dtls_listener.c000644 001751 001751 00000064536 12315706777 021737 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "apputils.h" #include "mainrelay.h" #include "dtls_listener.h" #include "ns_ioalib_impl.h" #include #include #include #include #include /* #define REQUEST_CLIENT_CERT */ /////////////////////////////////////////////////// #define FUNCSTART if(server && eve(server->verbose)) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s:%d:start\n",__FUNCTION__,__LINE__) #define FUNCEND if(server && eve(server->verbose)) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s:%d:end\n",__FUNCTION__,__LINE__) #define COOKIE_SECRET_LENGTH (32) #define MAX_SINGLE_UDP_BATCH (16) struct dtls_listener_relay_server_info { char ifname[1025]; ioa_addr addr; ioa_engine_handle e; turn_turnserver *ts; int verbose; SSL_CTX *dtls_ctx; struct event *udp_listen_ev; ioa_socket_handle udp_listen_s; ur_addr_map *children_ss; /* map of socket children on remote addr */ struct message_to_relay sm; int slen0; ioa_engine_new_connection_event_handler connect_cb; }; ///////////// forward declarations //////// static int create_server_socket(dtls_listener_relay_server_type* server, int report_creation); static int clean_server(dtls_listener_relay_server_type* server); static int reopen_server_socket(dtls_listener_relay_server_type* server, evutil_socket_t fd); ///////////// dtls message types ////////// int is_dtls_handshake_message(const unsigned char* buf, int len); int is_dtls_data_message(const unsigned char* buf, int len); int is_dtls_alert_message(const unsigned char* buf, int len); int is_dtls_cipher_change_message(const unsigned char* buf, int len); int is_dtls_message(const unsigned char* buf, int len); int is_dtls_handshake_message(const unsigned char* buf, int len) { return (buf && len>3 && buf[0]==0x16 && buf[1]==0xfe && buf[2]==0xff); } int is_dtls_data_message(const unsigned char* buf, int len) { return (buf && len>3 && buf[0]==0x17 && buf[1]==0xfe && buf[2]==0xff); } int is_dtls_alert_message(const unsigned char* buf, int len) { return (buf && len>3 && buf[0]==0x15 && buf[1]==0xfe && buf[2]==0xff); } int is_dtls_cipher_change_message(const unsigned char* buf, int len) { return (buf && len>3 && buf[0]==0x14 && buf[1]==0xfe && buf[2]==0xff); } int is_dtls_message(const unsigned char* buf, int len) { if(buf && (len>3) && (buf[1])==0xfe && (buf[2]==0xff)) { switch (buf[0]) { case 0x14: case 0x15: case 0x16: case 0x17: return 1; default: ; } } return 0; } ///////////// utils ///////////////////// #if !defined(TURN_NO_DTLS) static void calculate_cookie(SSL* ssl, unsigned char *cookie_secret, unsigned int cookie_length) { long rv=(long)ssl; long inum=(cookie_length-(((long)cookie_secret)%sizeof(long)))/sizeof(long); long i=0; long *ip=(long*)cookie_secret; for(i=0;ifd, ssl, nbh, server->verbose); if (rc < 0) return NULL; addr_debug_print(server->verbose, remote_addr, "Accepted connection from"); ioa_socket_handle ioas = create_ioa_socket_from_ssl(server->e, sock, ssl, DTLS_SOCKET, CLIENT_SOCKET, remote_addr, local_addr); if(ioas) { ioas->listener_server = server; addr_cpy(&(server->sm.m.sm.nd.src_addr),remote_addr); server->sm.m.sm.nd.recv_ttl = TTL_IGNORE; server->sm.m.sm.nd.recv_tos = TOS_IGNORE; server->sm.m.sm.s = ioas; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot create ioa_socket from SSL\n"); } FUNCEND ; return ioas; } static ioa_socket_handle dtls_server_input_handler(dtls_listener_relay_server_type* server, ioa_socket_handle s, ioa_network_buffer_handle nbh) { FUNCSTART; if (!server || !nbh) { return NULL; } SSL* connecting_ssl = NULL; BIO *wbio = NULL; struct timeval timeout; /* Create BIO */ wbio = BIO_new_dgram(s->fd, BIO_NOCLOSE); (void)BIO_dgram_set_peer(wbio, (struct sockaddr*) &(server->sm.m.sm.nd.src_addr)); /* Set and activate timeouts */ timeout.tv_sec = DTLS_MAX_RECV_TIMEOUT; timeout.tv_usec = 0; BIO_ctrl(wbio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout); connecting_ssl = SSL_new(server->dtls_ctx); SSL_set_accept_state(connecting_ssl); SSL_set_bio(connecting_ssl, NULL, wbio); SSL_set_options(connecting_ssl, SSL_OP_COOKIE_EXCHANGE); SSL_set_max_cert_list(connecting_ssl, 655350); ioa_socket_handle rc = dtls_accept_client_connection(server, s, connecting_ssl, &(server->sm.m.sm.nd.src_addr), &(server->addr), nbh); if (!rc) { if (!(SSL_get_shutdown(connecting_ssl) & SSL_SENT_SHUTDOWN)) { SSL_set_shutdown(connecting_ssl, SSL_RECEIVED_SHUTDOWN); SSL_shutdown(connecting_ssl); } SSL_free(connecting_ssl); } return rc; } #endif static int handle_udp_packet(dtls_listener_relay_server_type *server, struct message_to_relay *sm, ioa_engine_handle ioa_eng, turn_turnserver *ts) { int verbose = ioa_eng->verbose; ioa_socket_handle s = sm->m.sm.s; ur_addr_map_value_type mvt = 0; if(!(server->children_ss)) { server->children_ss = (ur_addr_map*)allocate_super_memory_engine(server->e, sizeof(ur_addr_map)); ur_addr_map_init(server->children_ss); } ur_addr_map *amap = server->children_ss; ioa_socket_handle chs = NULL; if ((ur_addr_map_get(amap, &(sm->m.sm.nd.src_addr), &mvt) > 0) && mvt) { chs = (ioa_socket_handle) mvt; } if (chs && !ioa_socket_tobeclosed(chs) && (chs->sockets_container == amap) && (chs->magic == SOCKET_MAGIC)) { s = chs; sm->m.sm.s = s; if(s->ssl) { int sslret = ssl_read(s->fd, s->ssl, sm->m.sm.nd.nbh, verbose); if(sslret < 0) { ioa_network_buffer_delete(ioa_eng, sm->m.sm.nd.nbh); sm->m.sm.nd.nbh = NULL; ts_ur_super_session *ss = (ts_ur_super_session *) s->session; if (ss) { turn_turnserver *server = (turn_turnserver *) ss->server; if (server) { shutdown_client_connection(server, ss, 0, "SSL read error"); } } else { close_ioa_socket(s); } ur_addr_map_del(amap, &(sm->m.sm.nd.src_addr), NULL); sm->m.sm.s = NULL; s = NULL; chs = NULL; } else if(ioa_network_buffer_get_size(sm->m.sm.nd.nbh)>0) { ; } else { ioa_network_buffer_delete(ioa_eng, sm->m.sm.nd.nbh); sm->m.sm.nd.nbh = NULL; } } if (s && s->read_cb && sm->m.sm.nd.nbh) { s->e = ioa_eng; s->read_cb(s, IOA_EV_READ, &(sm->m.sm.nd), s->read_ctx); ioa_network_buffer_delete(ioa_eng, sm->m.sm.nd.nbh); sm->m.sm.nd.nbh = NULL; if (ioa_socket_tobeclosed(s)) { ts_ur_super_session *ss = (ts_ur_super_session *) s->session; if (ss) { turn_turnserver *server = (turn_turnserver *) ss->server; if (server) { shutdown_client_connection(server, ss, 0, "UDP packet processing error"); } } } } } else { if (chs && ioa_socket_tobeclosed(chs)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: socket to be closed\n", __FUNCTION__); { u08bits saddr[129]; u08bits rsaddr[129]; long thrid = (long) pthread_self(); addr_to_string(get_local_addr_from_ioa_socket(chs),saddr); addr_to_string(get_remote_addr_from_ioa_socket(chs),rsaddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: 111.111: thrid=0x%lx: Amap = 0x%lx, socket container=0x%lx, local addr %s, remote addr %s, s=0x%lx, done=%d, tbc=%d\n", __FUNCTION__, thrid, (long) amap, (long) (chs->sockets_container), (char*) saddr, (char*) rsaddr, (long) s, (int) (chs->done), (int) (chs->tobeclosed)); } } if (chs && (chs->magic != SOCKET_MAGIC)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: wrong socket magic\n", __FUNCTION__); } if (chs && (chs->sockets_container != amap)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: wrong socket container\n", __FUNCTION__); { u08bits saddr[129]; u08bits rsaddr[129]; long thrid = (long) pthread_self(); addr_to_string(get_local_addr_from_ioa_socket(chs),saddr); addr_to_string(get_remote_addr_from_ioa_socket(chs),rsaddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: 111.222: thrid=0x%lx: Amap = 0x%lx, socket container=0x%lx, local addr %s, remote addr %s, s=0x%lx, done=%d, tbc=%d, st=%d, sat=%d\n", __FUNCTION__, thrid, (long) amap, (long) (chs->sockets_container), (char*) saddr, (char*) rsaddr, (long) chs, (int) (chs->done), (int) (chs->tobeclosed), (int) (chs->st), (int) (chs->sat)); } } chs = NULL; #if !defined(TURN_NO_DTLS) if (!turn_params.no_dtls && is_dtls_handshake_message(ioa_network_buffer_data(sm->m.sm.nd.nbh), (int)ioa_network_buffer_get_size(sm->m.sm.nd.nbh))) { chs = dtls_server_input_handler(server,s, sm->m.sm.nd.nbh); ioa_network_buffer_delete(server->e, sm->m.sm.nd.nbh); sm->m.sm.nd.nbh = NULL; } #endif if(!chs) { chs = create_ioa_socket_from_fd(ioa_eng, s->fd, s, UDP_SOCKET, CLIENT_SOCKET, &(sm->m.sm.nd.src_addr), get_local_addr_from_ioa_socket(s)); } s = chs; sm->m.sm.s = s; if (s) { if(verbose) { u08bits saddr[129]; u08bits rsaddr[129]; addr_to_string(get_local_addr_from_ioa_socket(s),saddr); addr_to_string(get_remote_addr_from_ioa_socket(s),rsaddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: New UDP endpoint: local addr %s, remote addr %s\n", __FUNCTION__, (char*) saddr,(char*) rsaddr); } s->e = ioa_eng; s->listener_server = server; add_socket_to_map(s, amap); open_client_connection_session(ts, &(sm->m.sm)); } } return 0; } static int create_new_connected_udp_socket( dtls_listener_relay_server_type* server, ioa_socket_handle s) { evutil_socket_t udp_fd = socket(s->local_addr.ss.sa_family, SOCK_DGRAM, 0); if (udp_fd < 0) { perror("socket"); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Cannot allocate new socket\n", __FUNCTION__); return -1; } if (sock_bind_to_device(udp_fd, (unsigned char*) (s->e->relay_ifname)) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot bind udp server socket to device %s\n", (char*) (s->e->relay_ifname)); } ioa_socket_handle ret = (ioa_socket*) turn_malloc(sizeof(ioa_socket)); if (!ret) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Cannot allocate new socket structure\n", __FUNCTION__); close(udp_fd); return -1; } ns_bzero(ret, sizeof(ioa_socket)); ret->magic = SOCKET_MAGIC; ret->fd = udp_fd; ret->family = s->family; ret->st = s->st; ret->sat = CLIENT_SOCKET; ret->local_addr_known = 1; addr_cpy(&(ret->local_addr), &(s->local_addr)); if (addr_bind(udp_fd,&(s->local_addr),1) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot bind new detached udp server socket to local addr\n"); IOA_CLOSE_SOCKET(ret); return -1; } ret->bound = 1; { int connect_err = 0; if (addr_connect(udp_fd, &(server->sm.m.sm.nd.src_addr), &connect_err) < 0) { char sl[129]; char sr[129]; addr_to_string(&(ret->local_addr),(u08bits*)sl); addr_to_string(&(server->sm.m.sm.nd.src_addr),(u08bits*)sr); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot connect new detached udp client socket from local addr %s to remote addr %s\n",sl,sr); IOA_CLOSE_SOCKET(ret); return -1; } } ret->connected = 1; addr_cpy(&(ret->remote_addr), &(server->sm.m.sm.nd.src_addr)); set_socket_options(ret); ret->current_ttl = s->current_ttl; ret->default_ttl = s->default_ttl; ret->current_tos = s->current_tos; ret->default_tos = s->default_tos; #if !defined(TURN_NO_DTLS) if (!turn_params.no_dtls && is_dtls_handshake_message( ioa_network_buffer_data(server->sm.m.sm.nd.nbh), (int) ioa_network_buffer_get_size( server->sm.m.sm.nd.nbh))) { SSL* connecting_ssl = NULL; BIO *wbio = NULL; struct timeval timeout; /* Create BIO */ wbio = BIO_new_dgram(ret->fd, BIO_NOCLOSE); (void) BIO_dgram_set_peer(wbio, (struct sockaddr*) &(server->sm.m.sm.nd.src_addr)); BIO_ctrl(wbio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &(server->sm.m.sm.nd.src_addr)); /* Set and activate timeouts */ timeout.tv_sec = DTLS_MAX_RECV_TIMEOUT; timeout.tv_usec = 0; BIO_ctrl(wbio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout); connecting_ssl = SSL_new(server->dtls_ctx); SSL_set_accept_state(connecting_ssl); SSL_set_bio(connecting_ssl, NULL, wbio); SSL_set_options(connecting_ssl, SSL_OP_COOKIE_EXCHANGE); SSL_set_max_cert_list(connecting_ssl, 655350); int rc = ssl_read(ret->fd, connecting_ssl, server->sm.m.sm.nd.nbh, server->verbose); if (rc < 0) { if (!(SSL_get_shutdown(connecting_ssl) & SSL_SENT_SHUTDOWN)) { SSL_set_shutdown(connecting_ssl, SSL_RECEIVED_SHUTDOWN); SSL_shutdown(connecting_ssl); } SSL_free(connecting_ssl); IOA_CLOSE_SOCKET(ret); return -1; } addr_debug_print(server->verbose, &(server->sm.m.sm.nd.src_addr), "Accepted DTLS connection from"); ret->ssl = connecting_ssl; ret->listener_server = server; ioa_network_buffer_delete(server->e, server->sm.m.sm.nd.nbh); server->sm.m.sm.nd.nbh = NULL; } #endif server->sm.m.sm.s = ret; return server->connect_cb(server->e, &(server->sm)); } static void udp_server_input_handler(evutil_socket_t fd, short what, void* arg) { int cycle = 0; ioa_socket_handle s = (ioa_socket_handle)arg; dtls_listener_relay_server_type* server = (dtls_listener_relay_server_type*)s->listener_server; FUNCSTART; if (!(what & EV_READ)) { return; } //printf_server_socket(server, fd); ioa_network_buffer_handle *elem = NULL; start_udp_cycle: elem = (ioa_network_buffer_handle *)ioa_network_buffer_allocate(server->e); server->sm.m.sm.nd.nbh = elem; server->sm.m.sm.nd.recv_ttl = TTL_IGNORE; server->sm.m.sm.nd.recv_tos = TOS_IGNORE; addr_set_any(&(server->sm.m.sm.nd.src_addr)); int slen = server->slen0; ssize_t bsize = 0; int flags = 0; do { bsize = recvfrom(fd, ioa_network_buffer_data(elem), ioa_network_buffer_get_capacity_udp(), flags, (struct sockaddr*) &(server->sm.m.sm.nd.src_addr), (socklen_t*) &slen); } while (bsize < 0 && (errno == EINTR)); int conn_reset = is_connreset(); int to_block = would_block(); if (bsize < 0) { if(to_block) { ioa_network_buffer_delete(server->e, server->sm.m.sm.nd.nbh); server->sm.m.sm.nd.nbh = NULL; FUNCEND; return; } #if defined(MSG_ERRQUEUE) //Linux int eflags = MSG_ERRQUEUE | MSG_DONTWAIT; static s08bits buffer[65535]; u32bits errcode = 0; ioa_addr orig_addr; int ttl = 0; int tos = 0; udp_recvfrom(fd, &orig_addr, &(server->addr), buffer, (int) sizeof(buffer), &ttl, &tos, server->e->cmsg, eflags, &errcode); //try again... do { bsize = recvfrom(fd, ioa_network_buffer_data(elem), ioa_network_buffer_get_capacity_udp(), flags, (struct sockaddr*) &(server->sm.m.sm.nd.src_addr), (socklen_t*) &slen); } while (bsize < 0 && (errno == EINTR)); conn_reset = is_connreset(); to_block = would_block(); #endif if(conn_reset) { ioa_network_buffer_delete(server->e, server->sm.m.sm.nd.nbh); server->sm.m.sm.nd.nbh = NULL; reopen_server_socket(server,fd); FUNCEND; return; } } if(bsize<0) { if(!to_block && !conn_reset) { int ern=errno; perror(__FUNCTION__); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: recvfrom error %d\n",__FUNCTION__,ern); } ioa_network_buffer_delete(server->e, server->sm.m.sm.nd.nbh); server->sm.m.sm.nd.nbh = NULL; FUNCEND; return; } if (bsize > 0) { int rc = 0; ioa_network_buffer_set_size(elem, (size_t)bsize); if(server->connect_cb) { rc = create_new_connected_udp_socket(server, s); if(rc<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot handle UDP packet, size %d\n",(int)bsize); } } else { server->sm.m.sm.s = s; rc = handle_udp_packet(server, &(server->sm), server->e, server->ts); } if(rc < 0) { if(eve(server->e->verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot handle UDP event\n"); } } } ioa_network_buffer_delete(server->e, server->sm.m.sm.nd.nbh); server->sm.m.sm.nd.nbh = NULL; if((bsize>0) && (cycle++addr.ss.sa_family, SOCK_DGRAM, 0); if (udp_listen_fd < 0) { perror("socket"); return -1; } server->udp_listen_s = create_ioa_socket_from_fd(server->e, udp_listen_fd, NULL, UDP_SOCKET, LISTENER_SOCKET, NULL, &(server->addr)); server->udp_listen_s->listener_server = server; set_sock_buf_size(udp_listen_fd,UR_SERVER_SOCK_BUF_SIZE); if(sock_bind_to_device(udp_listen_fd, (unsigned char*)server->ifname)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Cannot bind listener socket to device %s\n",server->ifname); } { const int max_binding_time = 60; int addr_bind_cycle = 0; retry_addr_bind: if(addr_bind(udp_listen_fd,&server->addr,1)<0) { perror("Cannot bind local socket to addr"); char saddr[129]; addr_to_string(&server->addr,(u08bits*)saddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING,"Cannot bind UDP/DTLS listener socket to addr %s\n",saddr); if(addr_bind_cycleudp_listen_ev = event_new(server->e->event_base,udp_listen_fd, EV_READ|EV_PERSIST,udp_server_input_handler, server->udp_listen_s); event_add(server->udp_listen_ev,NULL); } if(report_creation) { if(!turn_params.no_udp && !turn_params.no_dtls) addr_debug_print(server->verbose, &server->addr,"UDP/DTLS listener opened on"); else if(!turn_params.no_dtls) addr_debug_print(server->verbose, &server->addr,"DTLS listener opened on"); else if(!turn_params.no_udp) addr_debug_print(server->verbose, &server->addr,"UDP listener opened on"); } FUNCEND; return 0; } static int reopen_server_socket(dtls_listener_relay_server_type* server, evutil_socket_t fd) { UNUSED_ARG(fd); if(!server) return 0; FUNCSTART; { EVENT_DEL(server->udp_listen_ev); if(server->udp_listen_s->fd>=0) { socket_closesocket(server->udp_listen_s->fd); server->udp_listen_s->fd = -1; } if (!(server->udp_listen_s)) { return create_server_socket(server,1); } ioa_socket_raw udp_listen_fd = socket(server->addr.ss.sa_family, SOCK_DGRAM, 0); if (udp_listen_fd < 0) { perror("socket"); FUNCEND; return -1; } server->udp_listen_s->fd = udp_listen_fd; /* some UDP sessions may fail due to the race condition here */ set_socket_options(server->udp_listen_s); set_sock_buf_size(udp_listen_fd, UR_SERVER_SOCK_BUF_SIZE); if (sock_bind_to_device(udp_listen_fd, (unsigned char*) server->ifname) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Cannot bind listener socket to device %s\n", server->ifname); } if(addr_bind(udp_listen_fd,&server->addr,1)<0) { perror("Cannot bind local socket to addr"); char saddr[129]; addr_to_string(&server->addr,(u08bits*)saddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Cannot bind listener socket to addr %s\n",saddr); return -1; } server->udp_listen_ev = event_new(server->e->event_base, udp_listen_fd, EV_READ | EV_PERSIST, udp_server_input_handler, server->udp_listen_s); event_add(server->udp_listen_ev, NULL ); } if (!turn_params.no_udp && !turn_params.no_dtls) addr_debug_print(server->verbose, &server->addr, "UDP/DTLS listener opened on "); else if (!turn_params.no_dtls) addr_debug_print(server->verbose, &server->addr, "DTLS listener opened on "); else if (!turn_params.no_udp) addr_debug_print(server->verbose, &server->addr, "UDP listener opened on "); FUNCEND; return 0; } #if defined(REQUEST_CLIENT_CERT) static int dtls_verify_callback (int ok, X509_STORE_CTX *ctx) { /* This function should ask the user * if he trusts the received certificate. * Here we always trust. */ if(ok && ctx) return 1; return -1; } #endif static int init_server(dtls_listener_relay_server_type* server, const char* ifname, const char *local_address, int port, int verbose, ioa_engine_handle e, turn_turnserver *ts, int report_creation, ioa_engine_new_connection_event_handler send_socket) { if(!server) return -1; server->dtls_ctx = e->dtls_ctx; server->ts = ts; server->connect_cb = send_socket; if(ifname) STRCPY(server->ifname,ifname); if(make_ioa_addr((const u08bits*)local_address, port, &server->addr)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot create a UDP/DTLS listener for address: %s\n",local_address); return -1; } server->slen0 = get_ioa_addr_len(&(server->addr)); server->verbose=verbose; server->e = e; if(server->dtls_ctx) { #if defined(REQUEST_CLIENT_CERT) /* If client has to authenticate, then */ SSL_CTX_set_verify(server->dtls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, dtls_verify_callback); #endif SSL_CTX_set_read_ahead(server->dtls_ctx, 1); #if !defined(TURN_NO_DTLS) SSL_CTX_set_cookie_generate_cb(server->dtls_ctx, generate_cookie); SSL_CTX_set_cookie_verify_cb(server->dtls_ctx, verify_cookie); #endif } return create_server_socket(server, report_creation); } static int clean_server(dtls_listener_relay_server_type* server) { if(server) { EVENT_DEL(server->udp_listen_ev); close_ioa_socket(server->udp_listen_s); server->udp_listen_s = NULL; } return 0; } /////////////////////////////////////////////////////////// dtls_listener_relay_server_type* create_dtls_listener_server(const char* ifname, const char *local_address, int port, int verbose, ioa_engine_handle e, turn_turnserver *ts, int report_creation, ioa_engine_new_connection_event_handler send_socket) { dtls_listener_relay_server_type* server=(dtls_listener_relay_server_type*) allocate_super_memory_engine(e,sizeof(dtls_listener_relay_server_type)); if(init_server(server, ifname, local_address, port, verbose, e, ts, report_creation, send_socket)<0) { return NULL; } else { return server; } } ioa_engine_handle get_engine(dtls_listener_relay_server_type* server) { if(server) return server->e; return NULL; } //////////// UDP send //////////////// void udp_send_message(dtls_listener_relay_server_type *server, ioa_network_buffer_handle nbh, ioa_addr *dest) { if(server && dest && nbh && (server->udp_listen_s)) udp_send(server->udp_listen_s, dest, (s08bits*)ioa_network_buffer_data(nbh), (int)ioa_network_buffer_get_size(nbh)); } ////////////////////////////////////////////////////////////////// turnserver-3.2.3.1/src/apps/relay/libtelnet.h000644 001751 001751 00000050460 12315706777 021042 0ustar00olegoleg000000 000000 /*! * \brief libtelnet - TELNET protocol handling library * * SUMMARY: * * libtelnet is a library for handling the TELNET protocol. It includes * routines for parsing incoming data from a remote peer as well as formatting * data to send to the remote peer. * * libtelnet uses a callback-oriented API, allowing application-specific * handling of various events. The callback system is also used for buffering * outgoing protocol data, allowing the application to maintain control over * the actual socket connection. * * Features supported include the full TELNET protocol, Q-method option * negotiation, ZMP, MCCP2, MSSP, and NEW-ENVIRON. * * CONFORMS TO: * * RFC854 - http://www.faqs.org/rfcs/rfc854.html * RFC855 - http://www.faqs.org/rfcs/rfc855.html * RFC1091 - http://www.faqs.org/rfcs/rfc1091.html * RFC1143 - http://www.faqs.org/rfcs/rfc1143.html * RFC1408 - http://www.faqs.org/rfcs/rfc1408.html * RFC1572 - http://www.faqs.org/rfcs/rfc1572.html * * LICENSE: * * The author or authors of this code dedicate any and all copyright interest * in this code to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and successors. We * intend this dedication to be an overt act of relinquishment in perpetuity of * all present and future rights to this code under copyright law. * * \file libtelnet.h * * \version 0.21 * * \author Sean Middleditch */ /** * Minor fixes by Oleg Moskalenko */ #if !defined(LIBTELNET_INCLUDE) #define LIBTELNET_INCLUDE 1 /* standard C headers necessary for the libtelnet API */ #include /* C++ support */ #if defined(__cplusplus) extern "C" { #endif /* printf type checking feature in GCC and some other compilers */ #if __GNUC__ # define TELNET_GNU_PRINTF(f,a) __attribute__((format(printf, f, a))) /*!< internal helper */ #else # define TELNET_GNU_PRINTF(f,a) /*!< internal helper */ #endif /*! Telnet state tracker object type. */ typedef struct telnet_t telnet_t; /*! Telnet event object type. */ typedef union telnet_event_t telnet_event_t; /*! Telnet option table element type. */ typedef struct telnet_telopt_t telnet_telopt_t; /*! \name Telnet commands */ /*@{*/ /*! Telnet commands and special values. */ #define TELNET_IAC 255 #define TELNET_DONT 254 #define TELNET_DO 253 #define TELNET_WONT 252 #define TELNET_WILL 251 #define TELNET_SB 250 #define TELNET_GA 249 #define TELNET_EL 248 #define TELNET_EC 247 #define TELNET_AYT 246 #define TELNET_AO 245 #define TELNET_IP 244 #define TELNET_BREAK 243 #define TELNET_DM 242 #define TELNET_NOP 241 #define TELNET_SE 240 #define TELNET_EOR 239 #define TELNET_ABORT 238 #define TELNET_SUSP 237 #define TELNET_EOF 236 /*@}*/ /*! \name Telnet option values. */ /*@{*/ /*! Telnet options. */ #define TELNET_TELOPT_BINARY 0 #define TELNET_TELOPT_ECHO 1 #define TELNET_TELOPT_RCP 2 #define TELNET_TELOPT_SGA 3 #define TELNET_TELOPT_NAMS 4 #define TELNET_TELOPT_STATUS 5 #define TELNET_TELOPT_TM 6 #define TELNET_TELOPT_RCTE 7 #define TELNET_TELOPT_NAOL 8 #define TELNET_TELOPT_NAOP 9 #define TELNET_TELOPT_NAOCRD 10 #define TELNET_TELOPT_NAOHTS 11 #define TELNET_TELOPT_NAOHTD 12 #define TELNET_TELOPT_NAOFFD 13 #define TELNET_TELOPT_NAOVTS 14 #define TELNET_TELOPT_NAOVTD 15 #define TELNET_TELOPT_NAOLFD 16 #define TELNET_TELOPT_XASCII 17 #define TELNET_TELOPT_LOGOUT 18 #define TELNET_TELOPT_BM 19 #define TELNET_TELOPT_DET 20 #define TELNET_TELOPT_SUPDUP 21 #define TELNET_TELOPT_SUPDUPOUTPUT 22 #define TELNET_TELOPT_SNDLOC 23 #define TELNET_TELOPT_TTYPE 24 #define TELNET_TELOPT_EOR 25 #define TELNET_TELOPT_TUID 26 #define TELNET_TELOPT_OUTMRK 27 #define TELNET_TELOPT_TTYLOC 28 #define TELNET_TELOPT_3270REGIME 29 #define TELNET_TELOPT_X3PAD 30 #define TELNET_TELOPT_NAWS 31 #define TELNET_TELOPT_TSPEED 32 #define TELNET_TELOPT_LFLOW 33 #define TELNET_TELOPT_LINEMODE 34 #define TELNET_TELOPT_XDISPLOC 35 #define TELNET_TELOPT_ENVIRON 36 #define TELNET_TELOPT_AUTHENTICATION 37 #define TELNET_TELOPT_ENCRYPT 38 #define TELNET_TELOPT_NEW_ENVIRON 39 #define TELNET_TELOPT_MSSP 70 #define TELNET_TELOPT_COMPRESS2 86 #define TELNET_TELOPT_ZMP 93 #define TELNET_TELOPT_EXOPL 255 #define TELNET_TELOPT_MCCP2 86 /*@}*/ /*! \name Protocol codes for TERMINAL-TYPE commands. */ /*@{*/ /*! TERMINAL-TYPE codes. */ #define TELNET_TTYPE_IS 0 #define TELNET_TTYPE_SEND 1 /*@}*/ /*! \name Protocol codes for NEW-ENVIRON/ENVIRON commands. */ /*@{*/ /*! NEW-ENVIRON/ENVIRON codes. */ #define TELNET_ENVIRON_IS 0 #define TELNET_ENVIRON_SEND 1 #define TELNET_ENVIRON_INFO 2 #define TELNET_ENVIRON_VAR 0 #define TELNET_ENVIRON_VALUE 1 #define TELNET_ENVIRON_ESC 2 #define TELNET_ENVIRON_USERVAR 3 /*@}*/ /*! \name Protocol codes for MSSP commands. */ /*@{*/ /*! MSSP codes. */ #define TELNET_MSSP_VAR 1 #define TELNET_MSSP_VAL 2 /*@}*/ /*! \name Telnet state tracker flags. */ /*@{*/ /*! Control behavior of telnet state tracker. */ #define TELNET_FLAG_PROXY (1<<0) #define TELNET_PFLAG_DEFLATE (1<<7) /*@}*/ #if !defined(UNUSED_ARG) #define UNUSED_ARG(A) do { A=A; } while(0) #endif /*! * error codes */ enum telnet_error_t { TELNET_EOK = 0, /*!< no error */ TELNET_EBADVAL, /*!< invalid parameter, or API misuse */ TELNET_ENOMEM, /*!< memory allocation failure */ TELNET_EOVERFLOW, /*!< data exceeds buffer size */ TELNET_EPROTOCOL, /*!< invalid sequence of special bytes */ TELNET_ECOMPRESS /*!< error handling compressed streams */ }; typedef enum telnet_error_t telnet_error_t; /*!< Error code type. */ /*! * event codes */ enum telnet_event_type_t { TELNET_EV_DATA = 0, /*!< raw text data has been received */ TELNET_EV_SEND, /*!< data needs to be sent to the peer */ TELNET_EV_IAC, /*!< generic IAC code received */ TELNET_EV_WILL, /*!< WILL option negotiation received */ TELNET_EV_WONT, /*!< WONT option neogitation received */ TELNET_EV_DO, /*!< DO option negotiation received */ TELNET_EV_DONT, /*!< DONT option negotiation received */ TELNET_EV_SUBNEGOTIATION, /*!< sub-negotiation data received */ TELNET_EV_COMPRESS, /*!< compression has been enabled */ TELNET_EV_ZMP, /*!< ZMP command has been received */ TELNET_EV_TTYPE, /*!< TTYPE command has been received */ TELNET_EV_ENVIRON, /*!< ENVIRON command has been received */ TELNET_EV_MSSP, /*!< MSSP command has been received */ TELNET_EV_WARNING, /*!< recoverable error has occured */ TELNET_EV_ERROR /*!< non-recoverable error has occured */ }; typedef enum telnet_event_type_t telnet_event_type_t; /*!< Telnet event type. */ /*! * environ/MSSP command information */ struct telnet_environ_t { unsigned char type; /*!< either TELNET_ENVIRON_VAR or TELNET_ENVIRON_USERVAR */ const char *var; /*!< name of the variable being set */ const char *value; /*!< value of variable being set; empty string if no value */ }; /*! * event information */ union telnet_event_t { /*! * \brief Event type * * The type field will determine which of the other event structure fields * have been filled in. For instance, if the event type is TELNET_EV_ZMP, * then the zmp event field (and ONLY the zmp event field) will be filled * in. */ enum telnet_event_type_t type; /*! * data event: for DATA and SEND events */ struct data_t { enum telnet_event_type_t _type; /*!< alias for type */ const char *buffer; /*!< byte buffer */ size_t size; /*!< number of bytes in buffer */ } data; /*! * WARNING and ERROR events */ struct error_t { enum telnet_event_type_t _type; /*!< alias for type */ const char *file; /*!< file the error occured in */ const char *func; /*!< function the error occured in */ const char *msg; /*!< error message string */ int line; /*!< line of file error occured on */ telnet_error_t errcode; /*!< error code */ } error; /*! * command event: for IAC */ struct iac_t { enum telnet_event_type_t _type; /*!< alias for type */ unsigned char cmd; /*!< telnet command received */ } iac; /*! * negotiation event: WILL, WONT, DO, DONT */ struct negotiate_t { enum telnet_event_type_t _type; /*!< alias for type */ unsigned char telopt; /*!< option being negotiated */ } neg; /*! * subnegotiation event */ struct subnegotiate_t { enum telnet_event_type_t _type; /*!< alias for type */ const char *buffer; /*!< data of sub-negotiation */ size_t size; /*!< number of bytes in buffer */ unsigned char telopt; /*!< option code for negotiation */ } sub; /*! * ZMP event */ struct zmp_t { enum telnet_event_type_t _type; /*!< alias for type */ const char **argv; /*!< array of argument string */ size_t argc; /*!< number of elements in argv */ } zmp; /*! * TTYPE event */ struct ttype_t { enum telnet_event_type_t _type; /*!< alias for type */ unsigned char cmd; /*!< TELNET_TTYPE_IS or TELNET_TTYPE_SEND */ const char* name; /*!< terminal type name (IS only) */ } ttype; /*! * COMPRESS event */ struct compress_t { enum telnet_event_type_t _type; /*!< alias for type */ unsigned char state; /*!< 1 if compression is enabled, 0 if disabled */ } compress; /*! * ENVIRON/NEW-ENVIRON event */ struct environ_t { enum telnet_event_type_t _type; /*!< alias for type */ const struct telnet_environ_t *values; /*!< array of variable values */ size_t size; /*!< number of elements in values */ unsigned char cmd; /*!< SEND, IS, or INFO */ } environ; /*! * MSSP event */ struct mssp_t { enum telnet_event_type_t _type; /*!< alias for type */ const struct telnet_environ_t *values; /*!< array of variable values */ size_t size; /*!< number of elements in values */ } mssp; }; /*! * \brief event handler * * This is the type of function that must be passed to * telnet_init() when creating a new telnet object. The * function will be invoked once for every event generated * by the libtelnet protocol parser. * * \param telnet The telnet object that generated the event * \param event Event structure with details about the event * \param user_data User-supplied pointer */ typedef void (*telnet_event_handler_t)(telnet_t *telnet, telnet_event_t *event, void *user_data); /*! * telopt support table element; use telopt of -1 for end marker */ struct telnet_telopt_t { short telopt; /*!< one of the TELOPT codes or -1 */ unsigned char us; /*!< TELNET_WILL or TELNET_WONT */ unsigned char him; /*!< TELNET_DO or TELNET_DONT */ }; /*! * state tracker -- private data structure */ struct telnet_t; /*! * \brief Initialize a telnet state tracker. * * This function initializes a new state tracker, which is used for all * other libtelnet functions. Each connection must have its own * telnet state tracker object. * * \param telopts Table of TELNET options the application supports. * \param eh Event handler function called for every event. * \param flags 0 or TELNET_FLAG_PROXY. * \param user_data Optional data pointer that will be passsed to eh. * \return Telent state tracker object. */ extern telnet_t* telnet_init(const telnet_telopt_t *telopts, telnet_event_handler_t eh, unsigned char flags, void *user_data); /*! * \brief Free up any memory allocated by a state tracker. * * This function must be called when a telnet state tracker is no * longer needed (such as after the connection has been closed) to * release any memory resources used by the state tracker. * * \param telnet Telnet state tracker object. */ extern void telnet_free(telnet_t *telnet); /*! * \brief Push a byte buffer into the state tracker. * * Passes one or more bytes to the telnet state tracker for * protocol parsing. The byte buffer is most often going to be * the buffer that recv() was called for while handling the * connection. * * \param telnet Telnet state tracker object. * \param buffer Pointer to byte buffer. * \param size Number of bytes pointed to by buffer. */ extern void telnet_recv(telnet_t *telnet, const char *buffer, size_t size); /*! * \brief Send a telnet command. * * \param telnet Telnet state tracker object. * \param cmd Command to send. */ extern void telnet_iac(telnet_t *telnet, unsigned char cmd); /*! * \brief Send negotiation command. * * Internally, libtelnet uses RFC1143 option negotiation rules. * The negotiation commands sent with this function may be ignored * if they are determined to be redundant. * * \param telnet Telnet state tracker object. * \param cmd TELNET_WILL, TELNET_WONT, TELNET_DO, or TELNET_DONT. * \param opt One of the TELNET_TELOPT_* values. */ extern void telnet_negotiate(telnet_t *telnet, unsigned char cmd, unsigned char opt); /*! * Send non-command data (escapes IAC bytes). * * \param telnet Telnet state tracker object. * \param buffer Buffer of bytes to send. * \param size Number of bytes to send. */ extern void telnet_send(telnet_t *telnet, const char *buffer, size_t size); /*! * \brief Begin a sub-negotiation command. * * Sends IAC SB followed by the telopt code. All following data sent * will be part of the sub-negotiation, until telnet_finish_sb() is * called. * * \param telnet Telnet state tracker object. * \param telopt One of the TELNET_TELOPT_* values. */ extern void telnet_begin_sb(telnet_t *telnet, unsigned char telopt); /*! * \brief Finish a sub-negotiation command. * * This must be called after a call to telnet_begin_sb() to finish a * sub-negotiation command. * * \param telnet Telnet state tracker object. */ #define telnet_finish_sb(telnet) telnet_iac((telnet), TELNET_SE) /*! * \brief Shortcut for sending a complete subnegotiation buffer. * * Equivalent to: * telnet_begin_sb(telnet, telopt); * telnet_send(telnet, buffer, size); * telnet_finish_sb(telnet); * * \param telnet Telnet state tracker format. * \param telopt One of the TELNET_TELOPT_* values. * \param buffer Byte buffer for sub-negotiation data. * \param size Number of bytes to use for sub-negotiation data. */ extern void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt, const char *buffer, size_t size); /*! * \brief Begin sending compressed data. * * This function will begein sending data using the COMPRESS2 option, * which enables the use of zlib to compress data sent to the client. * The client must offer support for COMPRESS2 with option negotiation, * and zlib support must be compiled into libtelnet. * * Only the server may call this command. * * \param telnet Telnet state tracker object. */ extern void telnet_begin_compress2(telnet_t *telnet); /*! * \brief Send formatted data. * * This function is a wrapper around telnet_send(). It allows using * printf-style formatting. * * Additionally, this function will translate \\r to the CR NUL construct and * \\n with CR LF, as well as automatically escaping IAC bytes like * telnet_send(). * * \param telnet Telnet state tracker object. * \param fmt Format string. * \return Number of bytes sent. */ extern int telnet_printf(telnet_t *telnet, const char *fmt, ...) TELNET_GNU_PRINTF(2, 3); /*! * \brief Send formatted data. * * See telnet_printf(). */ extern int telnet_vprintf(telnet_t *telnet, const char *fmt, va_list va); /*! * \brief Send formatted data (no newline escaping). * * This behaves identically to telnet_printf(), except that the \\r and \\n * characters are not translated. The IAC byte is still escaped as normal * with telnet_send(). * * \param telnet Telnet state tracker object. * \param fmt Format string. * \return Number of bytes sent. */ extern int telnet_raw_printf(telnet_t *telnet, const char *fmt, ...) TELNET_GNU_PRINTF(2, 3); /*! * \brief Send formatted data (no newline escaping). * * See telnet_raw_printf(). */ extern int telnet_raw_vprintf(telnet_t *telnet, const char *fmt, va_list va); /*! * \brief Begin a new set of NEW-ENVIRON values to request or send. * * This function will begin the sub-negotiation block for sending or * requesting NEW-ENVIRON values. * * The telnet_finish_newenviron() macro must be called after this * function to terminate the NEW-ENVIRON command. * * \param telnet Telnet state tracker object. * \param type One of TELNET_ENVIRON_SEND, TELNET_ENVIRON_IS, or * TELNET_ENVIRON_INFO. */ extern void telnet_begin_newenviron(telnet_t *telnet, unsigned char type); /*! * \brief Send a NEW-ENVIRON variable name or value. * * This can only be called between calls to telnet_begin_newenviron() and * telnet_finish_newenviron(). * * \param telnet Telnet state tracker object. * \param type One of TELNET_ENVIRON_VAR, TELNET_ENVIRON_USERVAR, or * TELNET_ENVIRON_VALUE. * \param string Variable name or value. */ extern void telnet_newenviron_value(telnet_t* telnet, unsigned char type, const char *string); /*! * \brief Finish a NEW-ENVIRON command. * * This must be called after a call to telnet_begin_newenviron() to finish a * NEW-ENVIRON variable list. * * \param telnet Telnet state tracker object. */ #define telnet_finish_newenviron(telnet) telnet_finish_sb((telnet)) /*! * \brief Send the TERMINAL-TYPE SEND command. * * Sends the sequence IAC TERMINAL-TYPE SEND. * * \param telnet Telnet state tracker object. */ extern void telnet_ttype_send(telnet_t *telnet); /*! * \brief Send the TERMINAL-TYPE IS command. * * Sends the sequence IAC TERMINAL-TYPE IS "string". * * According to the RFC, the recipient of a TERMINAL-TYPE SEND shall * send the next possible terminal-type the client supports. Upon sending * the type, the client should switch modes to begin acting as the terminal * type is just sent. * * The server may continue sending TERMINAL-TYPE IS until it receives a * terminal type is understands. To indicate to the server that it has * reached the end of the available optoins, the client must send the last * terminal type a second time. When the server receives the same terminal * type twice in a row, it knows it has seen all available terminal types. * * After the last terminal type is sent, if the client receives another * TERMINAL-TYPE SEND command, it must begin enumerating the available * terminal types from the very beginning. This allows the server to * scan the available types for a preferred terminal type and, if none * is found, to then ask the client to switch to an acceptable * alternative. * * Note that if the client only supports a single terminal type, then * simply sending that one type in response to every SEND will satisfy * the behavior requirements. * * \param telnet Telnet state tracker object. * \param ttype Name of the terminal-type being sent. */ extern void telnet_ttype_is(telnet_t *telnet, const char* ttype); /*! * \brief Send a ZMP command. * * \param telnet Telnet state tracker object. * \param argc Number of ZMP commands being sent. * \param argv Array of argument strings. */ extern void telnet_send_zmp(telnet_t *telnet, size_t argc, const char **argv); /*! * \brief Send a ZMP command. * * Arguments are listed out in var-args style. After the last argument, a * NULL pointer must be passed in as a sentinel value. * * \param telnet Telnet state tracker object. */ extern void telnet_send_zmpv(telnet_t *telnet, ...); /*! * \brief Send a ZMP command. * * See telnet_send_zmpv(). */ extern void telnet_send_vzmpv(telnet_t *telnet, va_list va); /*! * \brief Begin sending a ZMP command * * \param telnet Telnet state tracker object. * \param cmd The first argument (command name) for the ZMP command. */ extern void telnet_begin_zmp(telnet_t *telnet, const char *cmd); /*! * \brief Send a ZMP command argument. * * \param telnet Telnet state tracker object. * \param arg Telnet argument string. */ extern void telnet_zmp_arg(telnet_t *telnet, const char *arg); /*! * \brief Finish a ZMP command. * * This must be called after a call to telnet_begin_zmp() to finish a * ZMP argument list. * * \param telnet Telnet state tracker object. */ #define telnet_finish_zmp(telnet) telnet_finish_sb((telnet)) /* C++ support */ #if defined(__cplusplus) } /* extern "C" */ #endif #endif /* !defined(LIBTELNET_INCLUDE) */ turnserver-3.2.3.1/src/apps/relay/userdb.h000644 001751 001751 00000011670 12315706777 020344 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __USERDB__ #define __USERDB__ #include #include #include "ns_turn_utils.h" #include "ns_turn_maps.h" #include "ns_turn_server.h" #include "apputils.h" #ifdef __cplusplus extern "C" { #endif //////////// Defines ////////////////////////////// #define DEFAULT_USERDB_FILE "turnuserdb.conf" #define AUTH_SECRET_SIZE (512) //////////// USER DB ////////////////////////////// struct auth_message { turnserver_id id; u08bits username[1025]; hmackey_t key; st_password_t pwd; get_username_resume_cb resume_func; ioa_net_data in_buffer; u64bits ctxkey; int success; }; struct _turn_user_db { turn_credential_type ct; vint total_quota; vint user_quota; vint total_current_allocs; ur_string_map *static_accounts; ur_string_map *dynamic_accounts; ur_string_map *alloc_counters; }; typedef struct _turn_user_db turn_user_db; enum _TURN_USERDB_TYPE { TURN_USERDB_TYPE_FILE=0 #if !defined(TURN_NO_PQ) ,TURN_USERDB_TYPE_PQ #endif #if !defined(TURN_NO_MYSQL) ,TURN_USERDB_TYPE_MYSQL #endif #if !defined(TURN_NO_HIREDIS) ,TURN_USERDB_TYPE_REDIS #endif }; typedef enum _TURN_USERDB_TYPE TURN_USERDB_TYPE; enum _TURNADMIN_COMMAND_TYPE { TA_COMMAND_UNKNOWN, TA_PRINT_KEY, TA_UPDATE_USER, TA_DELETE_USER, TA_LIST_USERS, TA_SET_SECRET, TA_SHOW_SECRET, TA_DEL_SECRET }; typedef enum _TURNADMIN_COMMAND_TYPE TURNADMIN_COMMAND_TYPE; /////////// SHARED SECRETS ////////////////// struct _secrets_list { char **secrets; size_t sz; }; typedef struct _secrets_list secrets_list_t; /////////// USERS PARAM ///////////////////// #define TURN_LONG_STRING_SIZE (1025) typedef struct _users_params_t { TURN_USERDB_TYPE userdb_type; char userdb[TURN_LONG_STRING_SIZE]; size_t users_number; int use_lt_credentials; int use_st_credentials; int anon_credentials; turn_user_db users; s08bits global_realm[STUN_MAX_REALM_SIZE+1]; int use_auth_secret_with_timestamp; char rest_api_separator; secrets_list_t static_auth_secrets; } users_params_t; ///////////////////////////////////////////// void init_secrets_list(secrets_list_t *sl); void init_dynamic_ip_lists(void); void update_white_and_black_lists(void); void clean_secrets_list(secrets_list_t *sl); size_t get_secrets_list_size(secrets_list_t *sl); const char* get_secrets_list_elem(secrets_list_t *sl, size_t i); void add_to_secrets_list(secrets_list_t *sl, const char* elem); /////////// USER DB CHECK ////////////////// int get_user_key(u08bits *uname, hmackey_t key, ioa_network_buffer_handle nbh); int get_user_pwd(u08bits *uname, st_password_t pwd); u08bits *start_user_check(turnserver_id id, u08bits *uname, get_username_resume_cb resume, ioa_net_data *in_buffer, u64bits ctxkey, int *postpone_reply); int check_new_allocation_quota(u08bits *username); void release_allocation_quota(u08bits *username); /////////// Handle user DB ///////////////// void read_userdb_file(int to_print); void auth_ping(void); int add_user_account(char *user, int dynamic); int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, TURNADMIN_COMMAND_TYPE ct, int is_st); int add_ip_list_range(char* range, ip_range_list_t * list); ///////////// Redis ////////////////////// #if !defined(TURN_NO_HIREDIS) #include "hiredis_libevent2.h" redis_context_handle get_redis_async_connection(struct event_base *base, char* connection_string); #endif //////////////////////////////////////////// #ifdef __cplusplus } #endif #endif /// __USERDB__/// turnserver-3.2.3.1/src/apps/relay/turncli.c000644 001751 001751 00000106400 12315706777 020527 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "libtelnet.h" #include #include #include #include #include #include #include #include "userdb.h" #include "mainrelay.h" #include "ns_turn_utils.h" #include "ns_turn_server.h" #include "ns_turn_maps.h" #include "apputils.h" #include "turncli.h" /////////////////////////////// struct cli_server cliserver; int use_cli = 1; ioa_addr cli_addr; int cli_addr_set = 0; int cli_port = CLI_DEFAULT_PORT; char cli_password[CLI_PASSWORD_LENGTH] = ""; int cli_max_output_sessions = DEFAULT_CLI_MAX_OUTPUT_SESSIONS; /////////////////////////////// struct cli_session { evutil_socket_t fd; int auth_completed; size_t cmds; struct bufferevent *bev; ioa_addr addr; telnet_t *ts; FILE* f; }; /////////////////////////////// #define CLI_PASSWORD_TRY_NUMBER (5) static const char *CLI_HELP_STR[] = {"", " ?, h, help - print this text", "", " quit, q, exit, bye - end CLI session", "", " stop, shutdown, halt - shutdown TURN Server", "", " pc - print configuration", "", " tc - toggle a configuration parameter", " (see pc command output for togglable param names)", "", " cc - change a configuration parameter", " (see pc command output for changeable param names)", "", " ps [username] - print sessions, with optional exact user match", "", " psp usernamestr - print sessions, with partial user string match", "", " psd - dump ps command output into file on the TURN server system", "", " pu [udp|tcp|dtls|tls]- print current users", "", " aas ip[:port} - add an alternate server reference", " das ip[:port] - delete an alternate server reference", " atas ip[:port] - add a TLS alternate server reference", " dtas ip[:port] - delete a TLS alternate server reference", "", NULL}; static const char *CLI_GREETING_STR[] = { "TURN Server", "rfc5766-turn-server", TURN_SOFTWARE, NULL}; static char CLI_CURSOR[] = "> "; static const telnet_telopt_t cli_telopts[] = { { TELNET_TELOPT_ECHO, TELNET_WONT, TELNET_DONT }, { TELNET_TELOPT_TTYPE, TELNET_WONT, TELNET_DONT }, { TELNET_TELOPT_COMPRESS2, TELNET_WONT, TELNET_DONT }, { TELNET_TELOPT_ZMP, TELNET_WONT, TELNET_DONT }, { TELNET_TELOPT_MSSP, TELNET_WONT, TELNET_DONT }, { TELNET_TELOPT_BINARY, TELNET_WONT, TELNET_DONT }, { TELNET_TELOPT_NAWS, TELNET_WONT, TELNET_DONT }, { -1, 0, 0 } }; struct toggleable_command { const char *cmd; vintp data; }; struct toggleable_command tcmds[] = { {"stale-nonce",&turn_params.stale_nonce}, {"stun-only",&turn_params.stun_only}, {"no-stun",&turn_params.no_stun}, {"secure-stun",&turn_params.secure_stun}, {"no-udp-relay",&turn_params.no_udp_relay}, {"no-tcp-relay",&turn_params.no_tcp_relay}, {"no-multicast-peers",&turn_params.no_multicast_peers}, {"no-loopback-peers",&turn_params.no_loopback_peers}, {"mobility",&turn_params.mobility}, {NULL,NULL} }; struct changeable_command { const char *cmd; vintp data; }; struct changeable_command ccmds[] = { {"total-quota",NULL}, {"user-quota",NULL}, {"cli-max-output-sessions",(vintp)&cli_max_output_sessions}, {NULL,NULL} }; /////////////////////////////// static void myprintf(struct cli_session *cs, const char *format, ...) { if(cs && format) { va_list args; va_start (args, format); if(cs->f) { vfprintf(cs->f, format, args); } else { telnet_vprintf(cs->ts, format, args); } va_end (args); } } static void print_str_array(struct cli_session* cs, const char** sa) { if(cs && sa) { int i=0; while(sa[i]) { myprintf(cs,"%s\n",sa[i]); i++; } } } static const char* get_flag(int val) { if(val) return "ON"; return "OFF"; } static void cli_print_flag(struct cli_session* cs, int flag, const char* name, int changeable) { if(cs && cs->ts && name) { const char *sc=""; if(changeable) sc=" (*)"; myprintf(cs," %s: %s%s\n",name,get_flag(flag),sc); } } static void cli_print_uint(struct cli_session* cs, unsigned long value, const char* name, int changeable) { if(cs && cs->ts && name) { const char *sc=""; if(changeable==1) sc=" (*)"; else if(changeable==2) sc=" (**)"; myprintf(cs," %s: %lu%s\n",name,value,sc); } } static void cli_print_str(struct cli_session* cs, const char *value, const char* name, int changeable) { if(cs && cs->ts && name && value) { if(value[0] == 0) value="empty"; const char *sc=""; if(changeable==1) sc=" (*)"; else if(changeable==2) sc=" (**)"; myprintf(cs," %s: %s%s\n",name,value,sc); } } static void cli_print_addr(struct cli_session* cs, ioa_addr *value, int use_port, const char* name, int changeable) { if(cs && cs->ts && name && value) { const char *sc=""; if(changeable==1) sc=" (*)"; else if(changeable==2) sc=" (**)"; char s[256]; if(!use_port) addr_to_string_no_port(value,(u08bits*)s); else addr_to_string(value,(u08bits*)s); myprintf(cs," %s: %s%s\n",name,s,sc); } } static void cli_print_addr_list(struct cli_session* cs, turn_server_addrs_list_t *value, int use_port, const char* name, int changeable) { if(cs && cs->ts && name && value && value->size && value->addrs) { const char *sc=""; if(changeable==1) sc=" (*)"; else if(changeable==2) sc=" (**)"; char s[256]; size_t i; for(i=0;isize;i++) { if(!use_port) addr_to_string_no_port(&(value->addrs[i]),(u08bits*)s); else addr_to_string(&(value->addrs[i]),(u08bits*)s); myprintf(cs," %s: %s%s\n",name,s,sc); } } } static void cli_print_str_array(struct cli_session* cs, char **value, size_t sz, const char* name, int changeable) { if(cs && cs->ts && name && value && sz) { const char *sc=""; if(changeable==1) sc=" (*)"; else if(changeable==2) sc=" (**)"; size_t i; for(i=0;its && name && value && value->ranges_number && value->ranges) { const char *sc=""; if(changeable==1) sc=" (*)"; else if(changeable==2) sc=" (**)"; size_t i; for(i=0;iranges_number;++i) { if(value->ranges[i]) myprintf(cs," %s: %s%s\n",name,value->ranges[i],sc); } } } static void toggle_cli_param(struct cli_session* cs, const char* pn) { if(cs && cs->ts && pn) { int i=0; while(tcmds[i].cmd && tcmds[i].data) { if(strcmp(tcmds[i].cmd,pn) == 0) { *(tcmds[i].data) = !(*(tcmds[i].data)); cli_print_flag(cs,*(tcmds[i].data),tcmds[i].cmd,0); return; } ++i; } myprintf(cs, "\n"); myprintf(cs, " Error: unknown or constant parameter: %s.\n",pn); myprintf(cs, " You can toggle only the following parameters:\n"); myprintf(cs, "\n"); i=0; while(tcmds[i].cmd && tcmds[i].data) { cli_print_flag(cs,*(tcmds[i].data),tcmds[i].cmd,0); ++i; } myprintf(cs,"\n"); } } static void change_cli_param(struct cli_session* cs, const char* pn) { if(cs && cs->ts && pn) { int i=0; while(ccmds[i].cmd) { if(strstr(pn,ccmds[i].cmd) == pn) { pn += strlen(ccmds[i].cmd); while(pn[0]==' ') ++pn; vint pnv = (vint)atoi(pn); if(ccmds[i].data) { *(ccmds[i].data) = (vint)pnv; cli_print_uint(cs,(unsigned long)(*(ccmds[i].data)),ccmds[i].cmd,2); } else if(strcmp(ccmds[i].cmd,"total-quota")==0) { turn_params.users_params.users.total_quota = pnv; cli_print_uint(cs,(unsigned long)(turn_params.users_params.users.total_quota),ccmds[i].cmd,2); } else if(strcmp(ccmds[i].cmd,"user-quota")==0) { turn_params.users_params.users.user_quota = pnv; cli_print_uint(cs,(unsigned long)(turn_params.users_params.users.user_quota),ccmds[i].cmd,2); } return; } ++i; } myprintf(cs, "\n"); myprintf(cs, " Error: unknown or constant parameter: %s.\n",pn); myprintf(cs, " You can change only the following parameters:\n"); myprintf(cs, "\n"); i=0; while(ccmds[i].cmd) { myprintf(cs," %s\n",ccmds[i].cmd); ++i; } myprintf(cs,"\n"); } } struct ps_arg { struct cli_session* cs; size_t counter; turn_time_t ct; const char *username; const char *pname; int exact_match; ur_string_map* users; size_t *user_counters; char **user_names; size_t users_number; }; static const char* pname(SOCKET_TYPE st) { switch(st) { case TCP_SOCKET: return "TCP"; case UDP_SOCKET: return "UDP"; case TLS_SOCKET: return "TLS"; case DTLS_SOCKET: return "DTLS"; case TENTATIVE_TCP_SOCKET: return "TCP/TLS"; default: ; }; return "UNKNOWN"; } static int print_session(ur_map_key_type key, ur_map_value_type value, void *arg) { if(key && value && arg) { struct ps_arg *csarg = (struct ps_arg*)arg; struct cli_session* cs = csarg->cs; struct turn_session_info *tsi = (struct turn_session_info *)value; if(csarg->users) { ur_string_map_value_type value; if(!ur_string_map_get(csarg->users, (ur_string_map_key_type)(char*)tsi->username, &value)) { const char *pn=csarg->pname; if(pn[0]) { if(!strcmp(pn,"TLS") || !strcmp(pn,"tls") || !strcmp(pn,"Tls")) { if(tsi->client_protocol != TLS_SOCKET) return 0; } else if(!strcmp(pn,"DTLS") || !strcmp(pn,"dtls") || !strcmp(pn,"Dtls")) { if(tsi->client_protocol != DTLS_SOCKET) return 0; } else if(!strcmp(pn,"TCP") || !strcmp(pn,"tcp") || !strcmp(pn,"Tcp")) { if(tsi->client_protocol != TCP_SOCKET) return 0; } else if(!strcmp(pn,"UDP") || !strcmp(pn,"udp") || !strcmp(pn,"Udp")) { if(tsi->client_protocol != UDP_SOCKET) return 0; } else { return 0; } } value = (ur_string_map_value_type)csarg->users_number; csarg->users_number += 1; csarg->user_counters = (size_t*)turn_realloc(csarg->user_counters, (size_t)value * sizeof(size_t), csarg->users_number * sizeof(size_t)); csarg->user_names = (char**)turn_realloc(csarg->user_names, (size_t)value * sizeof(char*), csarg->users_number * sizeof(char*)); csarg->user_names[(size_t)value] = strdup((char*)tsi->username); csarg->user_counters[(size_t)value] = 0; ur_string_map_put(csarg->users, (ur_string_map_key_type)(char*)tsi->username, value); } csarg->user_counters[(size_t)value] += 1; } else { if(csarg->username[0]) { if(csarg->exact_match) { if(strcmp((char*)tsi->username, csarg->username)) return 0; } else { if(!strstr((char*)tsi->username, csarg->username)) return 0; } } if(cs->f || (unsigned long)csarg->counter<(unsigned long)cli_max_output_sessions) { myprintf(cs, "\n"); myprintf(cs," %lu) id=%018llu, user <%s>:\n", (unsigned long)(csarg->counter+1), (unsigned long long)tsi->id, tsi->username); if(turn_time_before(csarg->ct, tsi->start_time)) { myprintf(cs," started: undefined time\n"); } else { myprintf(cs," started %lu secs ago\n",(unsigned long)(csarg->ct - tsi->start_time)); } if(turn_time_before(tsi->expiration_time,csarg->ct)) { myprintf(cs," expired\n"); } else { myprintf(cs," expiring in %lu secs\n",(unsigned long)(tsi->expiration_time - csarg->ct)); } myprintf(cs," client protocol %s, relay protocol %s\n",pname(tsi->client_protocol),pname(tsi->peer_protocol)); { if(!tsi->local_addr_data.saddr[0]) addr_to_string(&(tsi->local_addr_data.addr),(u08bits*)tsi->local_addr_data.saddr); if(!tsi->remote_addr_data.saddr[0]) addr_to_string(&(tsi->remote_addr_data.addr),(u08bits*)tsi->remote_addr_data.saddr); if(!tsi->relay_addr_data.saddr[0]) addr_to_string(&(tsi->relay_addr_data.addr),(u08bits*)tsi->relay_addr_data.saddr); myprintf(cs," client addr %s, server addr %s\n", tsi->remote_addr_data.saddr, tsi->local_addr_data.saddr); myprintf(cs," relay addr %s\n", tsi->relay_addr_data.saddr); } myprintf(cs," fingerprints enforced: %s\n",get_flag(tsi->enforce_fingerprints)); myprintf(cs," mobile: %s\n",get_flag(tsi->is_mobile)); if(tsi->tls_method[0]) { myprintf(cs," TLS method: %s\n",tsi->tls_method); myprintf(cs," TLS cipher: %s\n",tsi->tls_cipher); } myprintf(cs," usage: rp=%lu, rb=%lu, sp=%lu, sb=%lu\n",(unsigned long)(tsi->received_packets), (unsigned long)(tsi->received_bytes),(unsigned long)(tsi->sent_packets),(unsigned long)(tsi->sent_bytes)); myprintf(cs," rate: r=%lu, s=%lu, total=%lu (bytes per sec)\n",(unsigned long)(tsi->received_rate), (unsigned long)(tsi->sent_rate),(unsigned long)(tsi->total_rate)); if(tsi->main_peers_size) { myprintf(cs," peers:\n"); size_t i; for(i=0;imain_peers_size;++i) { if(!(tsi->main_peers_data[i].saddr[0])) addr_to_string(&(tsi->main_peers_data[i].addr),(u08bits*)tsi->main_peers_data[i].saddr); myprintf(cs," %s\n",tsi->main_peers_data[i].saddr); } if(tsi->extra_peers_size && tsi->extra_peers_data) { for(i=0;iextra_peers_size;++i) { if(!(tsi->extra_peers_data[i].saddr[0])) addr_to_string(&(tsi->extra_peers_data[i].addr),(u08bits*)tsi->extra_peers_data[i].saddr); myprintf(cs," %s\n",tsi->extra_peers_data[i].saddr); } } } } } csarg->counter += 1; } return 0; } static void print_sessions(struct cli_session* cs, const char* pn, int exact_match, int print_users) { if(cs && cs->ts && pn) { while(pn[0] == ' ') ++pn; if(pn[0] == '*') ++pn; const char *uname=""; if(!print_users) { uname = pn; pn = ""; } struct ps_arg arg = {cs,0,0,uname,pn,exact_match,NULL,NULL,NULL,0}; arg.ct = turn_time(); if(print_users) { arg.users = ur_string_map_create(NULL); } ur_map_foreach_arg(cliserver.sessions, (foreachcb_arg_type)print_session, &arg); myprintf(cs,"\n"); if(!print_users && !(cs->f)) { if((unsigned long)arg.counter > (unsigned long)cli_max_output_sessions) { myprintf(cs,"...\n"); myprintf(cs,"\n"); } } else if(arg.user_counters && arg.user_names) { size_t i; for(i=0;i, %lu sessions\n", arg.user_names[i], (unsigned long)arg.user_counters[i]); } } myprintf(cs,"\n"); } myprintf(cs," Total sessions: %lu\n", (unsigned long)arg.counter); myprintf(cs,"\n"); if(!print_users && !(cs->f)) { if((unsigned long)arg.counter > (unsigned long)cli_max_output_sessions) { myprintf(cs," Warning: too many output sessions, more than the\n"); myprintf(cs," current value of cli-max-output-sessions CLI parameter.\n"); myprintf(cs," Refine your request or increase cli-max-output-sessions value.\n"); myprintf(cs,"\n"); } } if(arg.user_counters) turn_free(arg.user_counters,sizeof(size_t)*arg.users_number); if(arg.user_names) { size_t i; for(i=0;ibev, buf, len)< 0) { return -1; } return 0; } return -1; } static void close_cli_session(struct cli_session* cs) { if(cs) { addr_debug_print(cliserver.verbose, &(cs->addr),"CLI session disconnected from"); if(cs->ts) { telnet_free(cs->ts); cs->ts = NULL; } if(cs->bev) { bufferevent_flush(cs->bev,EV_READ|EV_WRITE,BEV_FLUSH); bufferevent_disable(cs->bev,EV_READ|EV_WRITE); bufferevent_free(cs->bev); cs->bev=NULL; } if(cs->fd>=0) { close(cs->fd); cs->fd = -1; } turn_free(cs,sizeof(struct cli_session)); } } static void type_cli_cursor(struct cli_session* cs) { if(cs && (cs->bev)) { myprintf(cs, "%s", CLI_CURSOR); } } static void cli_add_alternate_server(struct cli_session* cs, const char* pn) { if(cs && cs->ts && pn && *pn) { add_alternate_server(pn); } } static void cli_add_tls_alternate_server(struct cli_session* cs, const char* pn) { if(cs && cs->ts && pn && *pn) { add_tls_alternate_server(pn); } } static void cli_del_alternate_server(struct cli_session* cs, const char* pn) { if(cs && cs->ts && pn && *pn) { del_alternate_server(pn); } } static void cli_del_tls_alternate_server(struct cli_session* cs, const char* pn) { if(cs && cs->ts && pn && *pn) { del_tls_alternate_server(pn); } } static int run_cli_input(struct cli_session* cs, const char *buf0, unsigned int len) { int ret = 0; if(cs && buf0 && cs->ts && cs->bev) { char *buf = (char*)malloc(len+1); ns_bcopy(buf0,buf,len); buf[len]=0; char *cmd = buf; while((cmd[0]==' ') || (cmd[0]=='\t')) ++cmd; size_t sl = strlen(cmd); while(sl) { char c = cmd[sl-1]; if((c==10)||(c==13)) { cmd[sl-1]=0; --sl; } else { break; } } if(sl) { cs->cmds += 1; if(cli_password[0] && !(cs->auth_completed)) { if(strcmp(cmd,cli_password)) { if(cs->cmds>=CLI_PASSWORD_TRY_NUMBER) { addr_debug_print(1, &(cs->addr),"CLI authentication error"); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"CLI authentication error\n"); close_cli_session(cs); } else { const char* ipwd="Enter password: "; myprintf(cs,"%s\n",ipwd); } } else { cs->auth_completed = 1; addr_debug_print(1, &(cs->addr),"CLI authentication success"); type_cli_cursor(cs); } } else if((strcmp(cmd,"bye") == 0)||(strcmp(cmd,"quit") == 0)||(strcmp(cmd,"exit") == 0)||(strcmp(cmd,"q") == 0)) { const char* str="Bye !"; myprintf(cs,"%s\n",str); close_cli_session(cs); ret = -1; } else if((strcmp(cmd,"halt") == 0)||(strcmp(cmd,"shutdown") == 0)||(strcmp(cmd,"stop") == 0)) { addr_debug_print(1, &(cs->addr),"Shutdown command received from CLI user"); const char* str="TURN server is shutting down"; myprintf(cs,"%s\n",str); close_cli_session(cs); turn_params.stop_turn_server = 1; sleep(10); exit(0); } else if((strcmp(cmd,"?") == 0)||(strcmp(cmd,"h") == 0)||(strcmp(cmd,"help") == 0)) { print_str_array(cs, CLI_GREETING_STR); print_str_array(cs, CLI_HELP_STR); type_cli_cursor(cs); } else if(strcmp(cmd,"pc")==0) { cli_print_configuration(cs); type_cli_cursor(cs); } else if(strstr(cmd,"tc ") == cmd) { toggle_cli_param(cs,cmd+3); type_cli_cursor(cs); } else if(strstr(cmd,"tc") == cmd) { toggle_cli_param(cs,cmd+2); type_cli_cursor(cs); } else if(strstr(cmd,"psp") == cmd) { print_sessions(cs,cmd+3,0,0); type_cli_cursor(cs); } else if(strstr(cmd,"psd") == cmd) { cmd += 3; while(cmd[0]==' ') ++cmd; if(!(cmd[0])) { const char* str="You have to provide file name for ps dump\n"; myprintf(cs,"%s\n",str); } else { cs->f = fopen(cmd,"w"); if(!(cs->f)) { const char* str="Cannot open file for writing\n"; myprintf(cs,"%s\n",str); } else { print_sessions(cs,"",1,0); fclose(cs->f); cs->f = NULL; } } type_cli_cursor(cs); } else if(strstr(cmd,"pu ") == cmd) { print_sessions(cs,cmd+3,0,1); type_cli_cursor(cs); } else if(!strcmp(cmd,"pu")) { print_sessions(cs,cmd+2,0,1); type_cli_cursor(cs); } else if(strstr(cmd,"ps") == cmd) { print_sessions(cs,cmd+2,1,0); type_cli_cursor(cs); } else if(strstr(cmd,"cc ") == cmd) { change_cli_param(cs,cmd+3); type_cli_cursor(cs); } else if(strstr(cmd,"cc") == cmd) { change_cli_param(cs,cmd+2); type_cli_cursor(cs); } else if(strstr(cmd,"aas ") == cmd) { cli_add_alternate_server(cs,cmd+4); type_cli_cursor(cs); } else if(strstr(cmd,"atas ") == cmd) { cli_add_tls_alternate_server(cs,cmd+5); type_cli_cursor(cs); } else if(strstr(cmd,"das ") == cmd) { cli_del_alternate_server(cs,cmd+4); type_cli_cursor(cs); } else if(strstr(cmd,"dtas ") == cmd) { cli_del_tls_alternate_server(cs,cmd+5); type_cli_cursor(cs); } else { const char* str="Unknown command\n"; myprintf(cs,"%s\n",str); type_cli_cursor(cs); } } else { type_cli_cursor(cs); } free(buf); } return ret; } static void cli_socket_input_handler_bev(struct bufferevent *bev, void* arg) { if (bev && arg) { struct cli_session* cs = (struct cli_session*) arg; if(!(cs->ts)) return; stun_buffer buf; if(cs->bev) { int len = (int)bufferevent_read(cs->bev, buf.buf, STUN_BUFFER_SIZE-1); if(len < 0) { close_cli_session(cs); return; } else if(len == 0) { return; } buf.len = len; buf.offset = 0; buf.buf[len]=0; telnet_recv(cs->ts, (const char *)buf.buf, (unsigned int)(buf.len)); } } } static void cli_eventcb_bev(struct bufferevent *bev, short events, void *arg) { UNUSED_ARG(bev); if (events & BEV_EVENT_CONNECTED) { // Connect okay } else if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF)) { if (arg) { struct cli_session* cs = (struct cli_session*) arg; close_cli_session(cs); } } } static void cli_telnet_event_handler(telnet_t *telnet, telnet_event_t *event, void *user_data) { if (user_data && telnet) { struct cli_session *cs = (struct cli_session *) user_data; switch (event->type){ case TELNET_EV_DATA: run_cli_input(cs, event->data.buffer, event->data.size); break; case TELNET_EV_SEND: run_cli_output(cs, event->data.buffer, event->data.size); break; case TELNET_EV_ERROR: TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TELNET error: %s", event->error.msg); break; default: ; }; } } static void cliserver_input_handler(struct evconnlistener *l, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *arg) { UNUSED_ARG(l); UNUSED_ARG(arg); UNUSED_ARG(socklen); addr_debug_print(cliserver.verbose, (ioa_addr*)sa,"CLI connected to"); struct cli_session *clisession = (struct cli_session*)turn_malloc(sizeof(struct cli_session)); ns_bzero(clisession,sizeof(struct cli_session)); set_socket_options_fd(fd, 1, sa->sa_family); clisession->fd = fd; addr_cpy(&(clisession->addr),(ioa_addr*)sa); clisession->bev = bufferevent_socket_new(cliserver.event_base, fd, BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS); bufferevent_setcb(clisession->bev, cli_socket_input_handler_bev, NULL, cli_eventcb_bev, clisession); bufferevent_setwatermark(clisession->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK); bufferevent_enable(clisession->bev, EV_READ); /* Start reading. */ clisession->ts = telnet_init(cli_telopts, cli_telnet_event_handler, 0, clisession); if(!(clisession->ts)) { const char *str = "Cannot open telnet session\n"; addr_debug_print(cliserver.verbose, (ioa_addr*)sa,str); close_cli_session(clisession); } else { print_str_array(clisession, CLI_GREETING_STR); telnet_printf(clisession->ts,"\n"); telnet_printf(clisession->ts,"Type '?' for help\n"); if(cli_password[0]) { const char* ipwd="Enter password: "; telnet_printf(clisession->ts,"%s\n",ipwd); } else { type_cli_cursor(clisession); } } } void setup_cli_thread(void) { cliserver.event_base = turn_event_base_new(); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"IO method (cli thread): %s\n",event_base_get_method(cliserver.event_base)); struct bufferevent *pair[2]; int opts = BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS; opts |= BEV_OPT_THREADSAFE; bufferevent_pair_new(cliserver.event_base, opts, pair); cliserver.in_buf = pair[0]; cliserver.out_buf = pair[1]; bufferevent_setcb(cliserver.in_buf, cli_server_receive_message, NULL, NULL, &cliserver); bufferevent_enable(cliserver.in_buf, EV_READ); if(!cli_addr_set) { if(make_ioa_addr((const u08bits*)CLI_DEFAULT_IP,0,&cli_addr)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot set cli address %s\n",CLI_DEFAULT_IP); return; } } addr_set_port(&cli_addr,cli_port); cliserver.listen_fd = socket(cli_addr.ss.sa_family, SOCK_STREAM, 0); if (cliserver.listen_fd < 0) { perror("socket"); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot open CLI socket\n"); return; } if(addr_bind(cliserver.listen_fd,&cli_addr,1)<0) { perror("Cannot bind CLI socket to addr"); char saddr[129]; addr_to_string(&cli_addr,(u08bits*)saddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot bind CLI listener socket to addr %s\n",saddr); socket_closesocket(cliserver.listen_fd); return; } socket_tcp_set_keepalive(cliserver.listen_fd); socket_set_nonblocking(cliserver.listen_fd); cliserver.l = evconnlistener_new(cliserver.event_base, cliserver_input_handler, &cliserver, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 1024, cliserver.listen_fd); if(!(cliserver.l)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot create CLI listener\n"); socket_closesocket(cliserver.listen_fd); return; } cliserver.sessions = ur_map_create(); addr_debug_print(cliserver.verbose, &cli_addr,"CLI listener opened on "); } void cli_server_receive_message(struct bufferevent *bev, void *ptr) { UNUSED_ARG(ptr); struct turn_session_info *tsi = (struct turn_session_info*)turn_malloc(sizeof(struct turn_session_info)); turn_session_info_init(tsi); int n = 0; struct evbuffer *input = bufferevent_get_input(bev); while ((n = evbuffer_remove(input, tsi, sizeof(struct turn_session_info))) > 0) { if (n != sizeof(struct turn_session_info)) { fprintf(stderr,"%s: Weird CLI buffer error: size=%d\n",__FUNCTION__,n); continue; } ur_map_value_type t = 0; if (ur_map_get(cliserver.sessions, (ur_map_key_type)tsi->id, &t) && t) { struct turn_session_info *old = (struct turn_session_info*)t; turn_session_info_clean(old); turn_free(old,sizeof(struct turn_session_info)); ur_map_del(cliserver.sessions, (ur_map_key_type)tsi->id, NULL); } if(tsi->valid) { ur_map_put(cliserver.sessions, (ur_map_key_type)tsi->id, (ur_map_value_type)tsi); tsi = (struct turn_session_info*)turn_malloc(sizeof(struct turn_session_info)); turn_session_info_init(tsi); } else { turn_session_info_clean(tsi); } } if(tsi) { turn_session_info_clean(tsi); turn_free(tsi,sizeof(struct turn_session_info)); } } int send_turn_session_info(struct turn_session_info* tsi) { int ret = -1; if(!use_cli) return ret; if(tsi) { struct evbuffer *output = bufferevent_get_output(cliserver.out_buf); if(output) { if(evbuffer_add(output,tsi,sizeof(struct turn_session_info))>=0) { ret = 0; } } } return ret; } /////////////////////////////// turnserver-3.2.3.1/src/apps/relay/turn_ports.h000644 001751 001751 00000005400 12315706777 021271 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __TURN_PORTS__ #define __TURN_PORTS__ #include "ns_turn_ioaddr.h" #include "ns_sm.h" #ifdef __cplusplus extern "C" { #endif ////////////////////////////////////////////////// #define LOW_DEFAULT_PORTS_BOUNDARY (49152) #define HIGH_DEFAULT_PORTS_BOUNDARY (65535) ////////////////////////////////////////////////// struct _turnipports; typedef struct _turnipports turnipports; ////////////////////////////////////////////////// turnipports* turnipports_create(super_memory_t *sm, u16bits start, u16bits end); void turnipports_add_ip(u08bits transport, const ioa_addr *backend_addr); int turnipports_allocate(turnipports* tp, u08bits transport, const ioa_addr *backend_addr); int turnipports_allocate_even(turnipports* tp, const ioa_addr *backend_addr, int allocate_rtcp, u64bits *reservation_token); void turnipports_release(turnipports* tp, u08bits transport, const ioa_addr *socket_addr); int turnipports_is_allocated(turnipports* tp, u08bits transport, const ioa_addr *backend_addr, u16bits port); int turnipports_is_available(turnipports* tp, u08bits transport, const ioa_addr *backend_addr, u16bits port); ////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_PORTS__ turnserver-3.2.3.1/src/apps/relay/ns_ioalib_engine_impl.c000644 001751 001751 00000257503 12315706777 023367 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "ns_turn_utils.h" #include "ns_turn_session.h" #include "ns_turn_server.h" #include "ns_turn_khash.h" #include "stun_buffer.h" #include "apputils.h" #include "ns_ioalib_impl.h" #if !defined(TURN_NO_TLS) #include #endif #include #include #if !defined(TURN_NO_HIREDIS) #include "hiredis_libevent2.h" #endif /* Compilation test: #if defined(IP_RECVTTL) #undef IP_RECVTTL #endif #if defined(IPV6_RECVHOPLIMIT) #undef IPV6_RECVHOPLIMIT #endif #if defined(IP_RECVTOS) #undef IP_RECVTOS #endif #if defined(IPV6_RECVTCLASS) #undef IPV6_RECVTCLASS #endif */ #define MAX_ERRORS_IN_UDP_BATCH (1024) struct turn_sock_extended_err { uint32_t ee_errno; /* error number */ uint8_t ee_origin; /* where the error originated */ uint8_t ee_type; /* type */ uint8_t ee_code; /* code */ uint8_t ee_pad; /* padding */ uint32_t ee_info; /* additional information */ uint32_t ee_data; /* other data */ /* More data may follow */ }; #define TRIAL_EFFORTS_TO_SEND (2) const int predef_timer_intervals[PREDEF_TIMERS_NUM] = {30,60,90,120,240,300,360,540,600,700,800,900,1800,3600}; /************** Forward function declarations ******/ static int socket_readerr(evutil_socket_t fd, ioa_addr *orig_addr); static void socket_input_handler(evutil_socket_t fd, short what, void* arg); static void socket_output_handler_bev(struct bufferevent *bev, void* arg); static void socket_input_handler_bev(struct bufferevent *bev, void* arg); static void eventcb_bev(struct bufferevent *bev, short events, void *arg); static int send_ssl_backlog_buffers(ioa_socket_handle s); static int set_accept_cb(ioa_socket_handle s, accept_cb acb, void *arg); static void close_socket_net_data(ioa_socket_handle s); /************** Utils **************************/ static const int tcp_congestion_control = 1; static const int udp_congestion_control = 1; static int bufferevent_enabled(struct bufferevent *bufev, short flags) { return (bufferevent_get_enabled(bufev) & flags); } static int is_socket_writeable(ioa_socket_handle s, size_t sz, const char *msg, int option) { UNUSED_ARG(s); UNUSED_ARG(sz); UNUSED_ARG(msg); UNUSED_ARG(option); if(!(s->done) && !(s->broken) && !(s->tobeclosed)) { switch(s->st) { case TCP_SOCKET: case TLS_SOCKET: if(s->bev) { struct evbuffer *evb = bufferevent_get_output(s->bev); if(evb) { size_t bufsz = evbuffer_get_length(evb); size_t newsz = bufsz + sz; switch(s->sat) { case TCP_CLIENT_DATA_SOCKET: case TCP_RELAY_DATA_SOCKET: switch(option) { case 0: case 1: if(newsz >= BUFFEREVENT_MAX_TCP_TO_TCP_WRITE) { return 0; } break; case 3: case 4: if(newsz >= BUFFEREVENT_MAX_TCP_TO_TCP_WRITE) { return 0; } break; default: return 1; }; break; default: if(option == 2) { if(newsz >= BUFFEREVENT_MAX_UDP_TO_TCP_WRITE) { return 0; } } }; } } break; default: ; }; } return 1; } static void log_socket_event(ioa_socket_handle s, const char *msg, int error) { if(s && (error || (s->e && s->e->verbose))) { if(!msg) msg = "General socket event"; turnsession_id id = 0; { ts_ur_super_session *ss = s->session; if (ss) { id = ss->id; } else{ return; } } TURN_LOG_LEVEL ll = TURN_LOG_LEVEL_INFO; if(error) ll = TURN_LOG_LEVEL_ERROR; { char sraddr[129]="\0"; char sladdr[129]="\0"; addr_to_string(&(s->remote_addr),(u08bits*)sraddr); addr_to_string(&(s->local_addr),(u08bits*)sladdr); if(EVUTIL_SOCKET_ERROR()) { TURN_LOG_FUNC(ll,"session %018llu: %s: %s (local %s, remote %s)\n",(unsigned long long)id, msg, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()), sladdr,sraddr); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: %s (local %s, remote %s)\n", (unsigned long long)id,msg,sladdr,sraddr); } } } } int set_df_on_ioa_socket(ioa_socket_handle s, int value) { if(s->parent_s) return 0; if (s->do_not_use_df) value = 0; if (s->current_df_relay_flag != value) { s->current_df_relay_flag = value; return set_socket_df(s->fd, s->family, value); } return 0; } void set_do_not_use_df(ioa_socket_handle s) { if(s->parent_s) return; s->do_not_use_df = 1; s->current_df_relay_flag = 1; set_socket_df(s->fd, s->family, 0); } /************** Buffer List ********************/ static int buffer_list_empty(stun_buffer_list *bufs) { if(bufs && bufs->head && bufs->tsz) return 0; return 1; } static stun_buffer_list_elem *get_elem_from_buffer_list(stun_buffer_list *bufs) { stun_buffer_list_elem *ret = NULL; if(bufs && bufs->head && bufs->tsz) { ret=bufs->head; bufs->head=ret->next; --bufs->tsz; ret->next=NULL; ret->buf.len = 0; ret->buf.offset = 0; ret->buf.coffset = 0; } return ret; } static void pop_elem_from_buffer_list(stun_buffer_list *bufs) { if(bufs && bufs->head && bufs->tsz) { stun_buffer_list_elem *ret = bufs->head; bufs->head=ret->next; --bufs->tsz; turn_free(ret,sizeof(stun_buffer_list_elem)); } } static stun_buffer_list_elem *new_blist_elem(ioa_engine_handle e) { stun_buffer_list_elem *ret = get_elem_from_buffer_list(&(e->bufs)); if(!ret) { ret = (stun_buffer_list_elem *)turn_malloc(sizeof(stun_buffer_list_elem)); ret->buf.len = 0; ret->buf.offset = 0; ret->buf.coffset = 0; ret->next = NULL; } return ret; } static inline void add_elem_to_buffer_list(stun_buffer_list *bufs, stun_buffer_list_elem *buf_elem) { buf_elem->next = bufs->head; bufs->head = buf_elem; bufs->tsz += 1; } static void add_buffer_to_buffer_list(stun_buffer_list *bufs, s08bits *buf, size_t len) { if(bufs && buf && (bufs->tszbuf.buf,len); buf_elem->buf.len = len; buf_elem->buf.offset = 0; buf_elem->buf.coffset = 0; add_elem_to_buffer_list(bufs,buf_elem); } } static void free_blist_elem(ioa_engine_handle e, stun_buffer_list_elem *buf_elem) { if(buf_elem) { if(e && (e->bufs.tszbufs), buf_elem); } else { turn_free(buf_elem,sizeof(stun_buffer_list_elem)); } } } /************** ENGINE *************************/ #define TURN_JIFFIE_SIZE (3) #define TURN_JIFFIE_LENGTH (1<<(TURN_JIFFIE_SIZE)) static void timer_handler(ioa_engine_handle e, void* arg) { UNUSED_ARG(arg); _log_time_value = turn_time(); _log_time_value_set = 1; e->jiffie = _log_time_value >> TURN_JIFFIE_SIZE; } ioa_engine_handle create_ioa_engine(super_memory_t *sm, struct event_base *eb, turnipports *tp, const s08bits* relay_ifname, size_t relays_number, s08bits **relay_addrs, int default_relays, int verbose, band_limit_t max_bps) { static int capabilities_checked = 0; if(!capabilities_checked) { capabilities_checked = 1; #if !defined(CMSG_SPACE) TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "On this platform, I am using alternative behavior of TTL/TOS according to RFC 5766.\n"); #endif #if !defined(IP_RECVTTL) TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "IPv4: On this platform, I am using alternative behavior of TTL according to RFC 5766.\n"); #endif #if !defined(IPV6_RECVHOPLIMIT) TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "IPv6: On this platform, I am using alternative behavior of TTL (HOPLIMIT) according to RFC 6156.\n"); #endif #if !defined(IP_RECVTOS) TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "IPv4: On this platform, I am using alternative behavior of TOS according to RFC 5766.\n"); #endif #if !defined(IPV6_RECVTCLASS) TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "IPv6: On this platform, I am using alternative behavior of TRAFFIC CLASS according to RFC 6156.\n"); #endif } if (!relays_number || !relay_addrs || !tp) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot create TURN engine\n", __FUNCTION__); return NULL; } else { ioa_engine_handle e = (ioa_engine_handle)allocate_super_memory_region(sm, sizeof(ioa_engine)); e->sm = sm; e->default_relays = default_relays; e->max_bpj = max_bps; e->verbose = verbose; e->tp = tp; if (eb) { e->event_base = eb; e->deallocate_eb = 0; } else { e->event_base = turn_event_base_new(); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"IO method (engine own thread): %s\n",event_base_get_method(e->event_base)); e->deallocate_eb = 1; } { int t; for(t=0;tevent_base, &duration); if(!ptv) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"FATAL: cannot create preferable timeval for %d secs (%d number)\n",predef_timer_intervals[t],t); exit(-1); } else { ns_bcopy(ptv,&(e->predef_timers[t]),sizeof(struct timeval)); e->predef_timer_intervals[t] = predef_timer_intervals[t]; } } } if (relay_ifname) STRCPY(e->relay_ifname, relay_ifname); { size_t i = 0; e->relay_addrs = (ioa_addr*)allocate_super_memory_region(sm, relays_number * sizeof(ioa_addr)+8); for (i = 0; i < relays_number; i++) { if(make_ioa_addr((u08bits*) relay_addrs[i], 0, &(e->relay_addrs[i]))<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot add a relay address: %s\n",relay_addrs[i]); } } e->relays_number = relays_number; } e->relay_addr_counter = (unsigned short) turn_random(); timer_handler(e,e); e->timer_ev = set_ioa_timer(e, 1, 0, timer_handler, e, 1, "timer_handler"); return e; } } void set_ssl_ctx(ioa_engine_handle e, SSL_CTX *tls_ctx_ssl23, SSL_CTX *tls_ctx_v1_0, #if defined(SSL_TXT_TLSV1_1) SSL_CTX *tls_ctx_v1_1, #if defined(SSL_TXT_TLSV1_2) SSL_CTX *tls_ctx_v1_2, #endif #endif SSL_CTX *dtls_ctx) { e->tls_ctx_ssl23 = tls_ctx_ssl23; e->tls_ctx_v1_0 = tls_ctx_v1_0; #if defined(SSL_TXT_TLSV1_1) e->tls_ctx_v1_1 = tls_ctx_v1_1; #if defined(SSL_TXT_TLSV1_2) e->tls_ctx_v1_2 = tls_ctx_v1_2; #endif #endif e->dtls_ctx = dtls_ctx; } void ioa_engine_set_rtcp_map(ioa_engine_handle e, rtcp_map *rtcpmap) { if(e) e->map_rtcp = rtcpmap; } static const ioa_addr* ioa_engine_get_relay_addr(ioa_engine_handle e, ioa_socket_handle client_s, int address_family, int *err_code) { if(e) { int family = AF_INET; if(address_family == STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6) family = AF_INET6; if(e->default_relays) { //No relay addrs defined - just return the client address if appropriate: ioa_addr *client_addr = get_local_addr_from_ioa_socket(client_s); if(client_addr) { switch(address_family) { case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4: if (client_addr->ss.sa_family == AF_INET) return client_addr; break; case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6: if (client_addr->ss.sa_family == AF_INET6) return client_addr; break; default: return client_addr; }; } } if (e->relays_number>0) { size_t i = 0; //Default recommended behavior: for(i=0; irelays_number; i++) { if(e->relay_addr_counter >= e->relays_number) e->relay_addr_counter = 0; ioa_addr *relay_addr = &(e->relay_addrs[e->relay_addr_counter++]); if(addr_any_no_port(relay_addr)) get_a_local_relay(family, relay_addr); switch (address_family){ case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT: case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4: if (relay_addr->ss.sa_family == AF_INET) return relay_addr; break; case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6: if (relay_addr->ss.sa_family == AF_INET6) return relay_addr; break; default: ; }; } if(address_family == STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT) { //Fallback to "find whatever is available": if(e->relay_addr_counter >= e->relays_number) e->relay_addr_counter = 0; const ioa_addr *relay_addr = &(e->relay_addrs[e->relay_addr_counter++]); return relay_addr; } *err_code = 440; } } return NULL; } /******************** Timers ****************************/ static void timer_event_handler(evutil_socket_t fd, short what, void* arg) { timer_event* te = (timer_event*)arg; if(!te) return; UNUSED_ARG(fd); if (!(what & EV_TIMEOUT)) return; if(te->e && eve(te->e->verbose)) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: timeout 0x%lx: %s\n", __FUNCTION__,(long)te, te->txt); ioa_timer_event_handler cb = te->cb; ioa_engine_handle e = te->e; void *ctx = te->ctx; cb(e, ctx); } ioa_timer_handle set_ioa_timer(ioa_engine_handle e, int secs, int ms, ioa_timer_event_handler cb, void* ctx, int persist, const s08bits *txt) { ioa_timer_handle ret = NULL; if (e && cb && secs > 0) { timer_event * te = (timer_event*) turn_malloc(sizeof(timer_event)); int flags = EV_TIMEOUT; if (persist) flags |= EV_PERSIST; struct event *ev = event_new(e->event_base, -1, flags, timer_event_handler, te); struct timeval tv; tv.tv_sec = secs; te->ctx = ctx; te->e = e; te->ev = ev; te->cb = cb; te->txt = strdup(txt); if(!ms) { tv.tv_usec = 0; int found = 0; int t; for(t=0;tpredef_timer_intervals[t] == secs) { evtimer_add(ev,&(e->predef_timers[t])); found = 1; break; } } if(!found) { evtimer_add(ev,&tv); } } else { tv.tv_usec = ms * 1000; evtimer_add(ev,&tv); } ret = te; } return ret; } void stop_ioa_timer(ioa_timer_handle th) { if (th) { timer_event *te = (timer_event *)th; EVENT_DEL(te->ev); } } void delete_ioa_timer(ioa_timer_handle th) { if (th) { stop_ioa_timer(th); timer_event *te = (timer_event *)th; if(te->txt) { turn_free(te->txt,strlen(te->txt)+1); te->txt = NULL; } turn_free(th,sizeof(timer_event)); } } /************** SOCKETS HELPERS ***********************/ static int ioa_socket_check_bandwidth(ioa_socket_handle s, size_t sz, int read) { if(s && (s->e) && sz && (s->e->max_bpj != 0) && (s->sat == CLIENT_SOCKET) && s->session) { band_limit_t max_bps = s->e->max_bpj; max_bps = max_bps<jiffie != s->e->jiffie) { s->jiffie = s->e->jiffie; s->jiffie_bytes_read = 0; s->jiffie_bytes_write = 0; if(bsz > max_bps) { return 0; } else { if(read) s->jiffie_bytes_read = bsz; else s->jiffie_bytes_write = bsz; return 1; } } else { band_limit_t nsz; if(read) nsz = s->jiffie_bytes_read + bsz; else nsz = s->jiffie_bytes_write + bsz; if(nsz > max_bps) { return 0; } else { if(read) s->jiffie_bytes_read = nsz; else s->jiffie_bytes_write = nsz; return 1; } } } return 1; } int get_ioa_socket_from_reservation(ioa_engine_handle e, u64bits in_reservation_token, ioa_socket_handle *s) { if (e && in_reservation_token && s) { *s = rtcp_map_get(e->map_rtcp, in_reservation_token); if (*s) { rtcp_map_del_savefd(e->map_rtcp, in_reservation_token); return 0; } } return -1; } /* Socket options helpers ==>> */ #define CORRECT_RAW_TTL(ttl) do { if(ttl<0 || ttl>255) ttl=TTL_DEFAULT; } while(0) #define CORRECT_RAW_TOS(tos) do { if(tos<0 || tos>255) tos=TOS_DEFAULT; } while(0) static int get_raw_socket_ttl(evutil_socket_t fd, int family) { int ttl = 0; if(family == AF_INET6) { #if !defined(IPV6_RECVHOPLIMIT) UNUSED_ARG(fd); do { return TTL_IGNORE; } while(0); #else socklen_t slen = (socklen_t)sizeof(ttl); if(getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl,&slen)<0) { perror("get HOPLIMIT on socket"); return TTL_IGNORE; } #endif } else { #if !defined(IP_RECVTTL) UNUSED_ARG(fd); do { return TTL_IGNORE; } while(0); #else socklen_t slen = (socklen_t)sizeof(ttl); if(getsockopt(fd, IPPROTO_IP, IP_TTL, &ttl,&slen)<0) { perror("get TTL on socket"); return TTL_IGNORE; } #endif } CORRECT_RAW_TTL(ttl); return ttl; } static int get_raw_socket_tos(evutil_socket_t fd, int family) { int tos = 0; if(family == AF_INET6) { #if !defined(IPV6_RECVTCLASS) UNUSED_ARG(fd); do { return TOS_IGNORE; } while(0); #else socklen_t slen = (socklen_t)sizeof(tos); if(getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tos,&slen)<0) { perror("get TCLASS on socket"); return -1; } #endif } else { #if !defined(IP_RECVTOS) UNUSED_ARG(fd); do { return TOS_IGNORE; } while(0); #else socklen_t slen = (socklen_t)sizeof(tos); if(getsockopt(fd, IPPROTO_IP, IP_TOS, &tos,&slen)<0) { perror("get TOS on socket"); return -1; } #endif } CORRECT_RAW_TOS(tos); return tos; } static int set_raw_socket_ttl(evutil_socket_t fd, int family, int ttl) { if(family == AF_INET6) { #if !defined(IPV6_RECVHOPLIMIT) UNUSED_ARG(fd); UNUSED_ARG(ttl); #else CORRECT_RAW_TTL(ttl); if(setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl,sizeof(ttl))<0) { perror("set HOPLIMIT on socket"); return -1; } #endif } else { #if !defined(IP_RECVTTL) UNUSED_ARG(fd); UNUSED_ARG(ttl); #else CORRECT_RAW_TTL(ttl); if(setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl,sizeof(ttl))<0) { perror("set TTL on socket"); return -1; } #endif } return 0; } static int set_raw_socket_tos(evutil_socket_t fd, int family, int tos) { if(family == AF_INET6) { #if !defined(IPV6_RECVTCLASS) UNUSED_ARG(fd); UNUSED_ARG(tos); #else CORRECT_RAW_TOS(tos); if(setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tos,sizeof(tos))<0) { perror("set TCLASS on socket"); return -1; } #endif } else { #if !defined(IPV6_RECVTOS) UNUSED_ARG(fd); UNUSED_ARG(tos); #else if(setsockopt(fd, IPPROTO_IP, IP_TOS, &tos,sizeof(tos))<0) { perror("set TOS on socket"); return -1; } #endif } return 0; } static int set_socket_ttl(ioa_socket_handle s, int ttl) { if(s->default_ttl < 0) //Unsupported return -1; if(ttl < 0) ttl = s->default_ttl; CORRECT_RAW_TTL(ttl); if(ttl > s->default_ttl) ttl=s->default_ttl; if(s->current_ttl != ttl) { int ret = set_raw_socket_ttl(s->fd, s->family, ttl); s->current_ttl = ttl; return ret; } return 0; } static int set_socket_tos(ioa_socket_handle s, int tos) { if(s->default_tos < 0) //Unsupported return -1; if(tos < 0) tos = s->default_tos; CORRECT_RAW_TOS(tos); if(s->current_tos != tos) { int ret = set_raw_socket_tos(s->fd, s->family, tos); s->current_tos = tos; return ret; } return 0; } int set_raw_socket_ttl_options(evutil_socket_t fd, int family) { if (family == AF_INET6) { #if !defined(IPV6_RECVHOPLIMIT) UNUSED_ARG(fd); #else int recv_ttl_on = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &recv_ttl_on, sizeof(recv_ttl_on)) < 0) { perror("cannot set recvhoplimit\n"); } #endif } else { #if !defined(IP_RECVTTL) UNUSED_ARG(fd); #else int recv_ttl_on = 1; if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &recv_ttl_on, sizeof(recv_ttl_on)) < 0) { perror("cannot set recvttl\n"); } #endif } return 0; } int set_raw_socket_tos_options(evutil_socket_t fd, int family) { if (family == AF_INET6) { #if !defined(IPV6_RECVTCLASS) UNUSED_ARG(fd); #else int recv_tos_on = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVTCLASS, &recv_tos_on, sizeof(recv_tos_on)) < 0) { perror("cannot set recvtclass\n"); } #endif } else { #if !defined(IP_RECVTOS) UNUSED_ARG(fd); #else int recv_tos_on = 1; if (setsockopt(fd, IPPROTO_IP, IP_RECVTOS, &recv_tos_on, sizeof(recv_tos_on)) < 0) { perror("cannot set recvtos\n"); } #endif } return 0; } int set_socket_options_fd(evutil_socket_t fd, int tcp, int family) { if(fd<0) return 0; set_sock_buf_size(fd,UR_CLIENT_SOCK_BUF_SIZE); if(tcp) { struct linger so_linger; so_linger.l_onoff = 1; so_linger.l_linger = 0; if(setsockopt(fd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger))<1) { //perror("setsolinger") ; } } socket_set_nonblocking(fd); if (!tcp) { set_raw_socket_ttl_options(fd, family); set_raw_socket_tos_options(fd, family); #ifdef IP_RECVERR if (family != AF_INET6) { int on = 0; #ifdef TURN_IP_RECVERR on = 1; #endif if(setsockopt(fd, IPPROTO_IP, IP_RECVERR, (void *)&on, sizeof(on))<0) perror("IP_RECVERR"); } #endif #ifdef IPV6_RECVERR if (family == AF_INET6) { int on = 0; #ifdef TURN_IP_RECVERR on = 1; #endif if(setsockopt(fd, IPPROTO_IPV6, IPV6_RECVERR, (void *)&on, sizeof(on))<0) perror("IPV6_RECVERR"); } #endif } else { int flag = 1; int result = setsockopt(fd, /* socket affected */ IPPROTO_TCP, /* set option at TCP level */ TCP_NODELAY, /* name of option */ (char*)&flag, /* value */ sizeof(int)); /* length of option value */ if (result < 0) perror("TCP_NODELAY"); socket_tcp_set_keepalive(fd); } return 0; } int set_socket_options(ioa_socket_handle s) { if(!s || (s->parent_s)) return 0; set_socket_options_fd(s->fd,((s->st == TCP_SOCKET) || (s->st == TLS_SOCKET) || (s->st == TENTATIVE_TCP_SOCKET)),s->family); s->default_ttl = get_raw_socket_ttl(s->fd, s->family); s->current_ttl = s->default_ttl; s->default_tos = get_raw_socket_tos(s->fd, s->family); s->current_tos = s->default_tos; return 0; } /* <<== Socket options helpers */ ioa_socket_handle create_unbound_ioa_socket(ioa_engine_handle e, ioa_socket_handle parent_s, int family, SOCKET_TYPE st, SOCKET_APP_TYPE sat) { evutil_socket_t fd = -1; ioa_socket_handle ret = NULL; if(!parent_s) { switch (st){ case UDP_SOCKET: fd = socket(family, SOCK_DGRAM, 0); if (fd < 0) { perror("UDP socket"); return NULL; } set_sock_buf_size(fd, UR_CLIENT_SOCK_BUF_SIZE); break; case TCP_SOCKET: fd = socket(family, SOCK_STREAM, 0); if (fd < 0) { perror("TCP socket"); return NULL; } set_sock_buf_size(fd, UR_CLIENT_SOCK_BUF_SIZE); break; default: /* we do not support other sockets in the relay position */ return NULL; } } ret = (ioa_socket*)turn_malloc(sizeof(ioa_socket)); ns_bzero(ret,sizeof(ioa_socket)); ret->magic = SOCKET_MAGIC; ret->fd = fd; ret->family = family; ret->st = st; ret->sat = sat; ret->e = e; if(parent_s) { add_socket_to_parent(parent_s, ret); } else { set_socket_options(ret); } return ret; } static int bind_ioa_socket(ioa_socket_handle s, const ioa_addr* local_addr, int reusable) { if(!s || (s->parent_s)) return 0; if (s && s->fd >= 0 && s->e && local_addr) { int res = addr_bind(s->fd, local_addr, reusable); if (res >= 0) { s->bound = 1; addr_cpy(&(s->local_addr), local_addr); if(addr_get_port(local_addr)<1) { ioa_addr tmpaddr; addr_get_from_sock(s->fd, &tmpaddr); if(addr_any(&(s->local_addr))) { addr_cpy(&(s->local_addr),&tmpaddr); } else { addr_set_port(&(s->local_addr),addr_get_port(&tmpaddr)); } } s->local_addr_known = 1; return 0; } } return -1; } int create_relay_ioa_sockets(ioa_engine_handle e, ioa_socket_handle client_s, int address_family, u08bits transport, int even_port, ioa_socket_handle *rtp_s, ioa_socket_handle *rtcp_s, uint64_t *out_reservation_token, int *err_code, const u08bits **reason, accept_cb acb, void *acbarg) { *rtp_s = NULL; if (rtcp_s) *rtcp_s = NULL; turnipports* tp = e->tp; size_t iip = 0; for (iip = 0; iip < e->relays_number; ++iip) { ioa_addr relay_addr; const ioa_addr *ra = ioa_engine_get_relay_addr(e, client_s, address_family, err_code); if(ra) addr_cpy(&relay_addr, ra); if(*err_code) { if(*err_code == 440) *reason = (const u08bits *) "Unsupported address family"; return -1; } int rtcp_port = -1; IOA_CLOSE_SOCKET(*rtp_s); if(rtcp_s) IOA_CLOSE_SOCKET(*rtcp_s); ioa_addr rtcp_local_addr; addr_cpy(&rtcp_local_addr, &relay_addr); addr_debug_print(e->verbose, &relay_addr, "Server relay addr"); int i = 0; int port = 0; ioa_addr local_addr; addr_cpy(&local_addr, &relay_addr); for (i = 0; i < 0xFFFF; i++) { port = 0; rtcp_port = -1; if (even_port < 0) { port = turnipports_allocate(tp, transport, &relay_addr); } else { port = turnipports_allocate_even(tp, &relay_addr, even_port, out_reservation_token); if (port >= 0 && even_port > 0) { IOA_CLOSE_SOCKET(*rtcp_s); *rtcp_s = create_unbound_ioa_socket(e, NULL, relay_addr.ss.sa_family, UDP_SOCKET, RELAY_RTCP_SOCKET); if (*rtcp_s == NULL) { perror("socket"); IOA_CLOSE_SOCKET(*rtp_s); addr_set_port(&local_addr, port); turnipports_release(tp, transport, &local_addr); rtcp_port = port + 1; addr_set_port(&rtcp_local_addr, rtcp_port); turnipports_release(tp, transport, &rtcp_local_addr); return -1; } sock_bind_to_device((*rtcp_s)->fd, (unsigned char*)e->relay_ifname); rtcp_port = port + 1; addr_set_port(&rtcp_local_addr, rtcp_port); if (bind_ioa_socket(*rtcp_s, &rtcp_local_addr, (transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE)) < 0) { addr_set_port(&local_addr, port); turnipports_release(tp, transport, &local_addr); turnipports_release(tp, transport, &rtcp_local_addr); rtcp_port = -1; IOA_CLOSE_SOCKET(*rtcp_s); continue; } } } if (port < 0) { IOA_CLOSE_SOCKET(*rtp_s); if (rtcp_s) IOA_CLOSE_SOCKET(*rtcp_s); rtcp_port = -1; break; } else { IOA_CLOSE_SOCKET(*rtp_s); *rtp_s = create_unbound_ioa_socket(e, NULL, relay_addr.ss.sa_family, (transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE) ? TCP_SOCKET : UDP_SOCKET, RELAY_SOCKET); if (*rtp_s == NULL) { if (rtcp_s) IOA_CLOSE_SOCKET(*rtcp_s); addr_set_port(&local_addr, port); turnipports_release(tp, transport, &local_addr); if (rtcp_port >= 0) turnipports_release(tp, transport, &rtcp_local_addr); perror("socket"); return -1; } sock_bind_to_device((*rtp_s)->fd, (unsigned char*)e->relay_ifname); addr_set_port(&local_addr, port); if (bind_ioa_socket(*rtp_s, &local_addr, (transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE)) >= 0) { break; } else { IOA_CLOSE_SOCKET(*rtp_s); if (rtcp_s) IOA_CLOSE_SOCKET(*rtcp_s); addr_set_port(&local_addr, port); turnipports_release(tp, transport, &local_addr); if (rtcp_port >= 0) turnipports_release(tp, transport, &rtcp_local_addr); rtcp_port = -1; } } } if(i>=0xFFFF) { IOA_CLOSE_SOCKET(*rtp_s); if (rtcp_s) IOA_CLOSE_SOCKET(*rtcp_s); } if (*rtp_s) { addr_set_port(&local_addr, port); addr_debug_print(e->verbose, &local_addr, "Local relay addr"); if (rtcp_s && *rtcp_s) { addr_set_port(&local_addr, port+1); addr_debug_print(e->verbose, &local_addr, "Local reserved relay addr"); } break; } } if (!(*rtp_s)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: no available ports 3\n", __FUNCTION__); IOA_CLOSE_SOCKET(*rtp_s); if (rtcp_s) IOA_CLOSE_SOCKET(*rtcp_s); return -1; } set_accept_cb(*rtp_s, acb, acbarg); if (rtcp_s && *rtcp_s && out_reservation_token && *out_reservation_token) { if (rtcp_map_put(e->map_rtcp, *out_reservation_token, *rtcp_s) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: cannot update RTCP map\n", __FUNCTION__); IOA_CLOSE_SOCKET(*rtp_s); if (rtcp_s) IOA_CLOSE_SOCKET(*rtcp_s); return -1; } } return 0; } /* RFC 6062 ==>> */ static void tcp_listener_input_handler(struct evconnlistener *l, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *arg) { UNUSED_ARG(l); ioa_socket_handle list_s = (ioa_socket_handle) arg; ioa_addr client_addr; ns_bcopy(sa,&client_addr,socklen); addr_debug_print(((list_s->e) && list_s->e->verbose), &client_addr,"tcp accepted from"); ioa_socket_handle s = create_ioa_socket_from_fd( list_s->e, fd, NULL, TCP_SOCKET, TCP_RELAY_DATA_SOCKET, &client_addr, &(list_s->local_addr)); if (s) { if(list_s->acb) { list_s->acb(s,list_s->acbarg); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Do not know what to do with accepted TCP socket\n"); close_ioa_socket(s); } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot create ioa_socket from FD\n"); socket_closesocket(fd); } } static int set_accept_cb(ioa_socket_handle s, accept_cb acb, void *arg) { if(!s || s->parent_s) return -1; if(s->st == TCP_SOCKET) { s->list_ev = evconnlistener_new(s->e->event_base, tcp_listener_input_handler, s, LEV_OPT_REUSEABLE, 1024, s->fd); if(!(s->list_ev)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: cannot start TCP listener\n", __FUNCTION__); return -1; } s->acb = acb; s->acbarg = arg; } return 0; } static void connect_eventcb(struct bufferevent *bev, short events, void *ptr) { UNUSED_ARG(bev); ioa_socket_handle ret = (ioa_socket_handle) ptr; if (ret) { connect_cb cb = ret->conn_cb; void *arg = ret->conn_arg; if (events & BEV_EVENT_CONNECTED) { ret->conn_cb = NULL; ret->conn_arg = NULL; if(ret->conn_bev) { bufferevent_disable(ret->conn_bev,EV_READ|EV_WRITE); bufferevent_free(ret->conn_bev); ret->conn_bev=NULL; } ret->connected = 1; if(cb) { cb(1,arg); } } else if (events & BEV_EVENT_ERROR) { /* An error occured while connecting. */ ret->conn_cb = NULL; ret->conn_arg = NULL; if(ret->conn_bev) { bufferevent_disable(ret->conn_bev,EV_READ|EV_WRITE); bufferevent_free(ret->conn_bev); ret->conn_bev=NULL; } if(cb) { cb(0,arg); } } } } ioa_socket_handle ioa_create_connecting_tcp_relay_socket(ioa_socket_handle s, ioa_addr *peer_addr, connect_cb cb, void *arg) { ioa_socket_handle ret = create_unbound_ioa_socket(s->e, NULL, s->family, s->st, TCP_RELAY_DATA_SOCKET); if(!ret) { return NULL; } ioa_addr new_local_addr; addr_cpy(&new_local_addr, &(s->local_addr)); #if !defined(SO_REUSEPORT) /* * trick for OSes which do not support SO_REUSEPORT. * Section 5.2 of RFC 6062 will not work correctly * for those OSes (for example, Linux pre-3.9 kernel). */ #if !defined(__CYGWIN__) && !defined(__CYGWIN32__) && !defined(__CYGWIN64__) close_socket_net_data(s); #else addr_set_port(&new_local_addr,0); #endif #endif if(bind_ioa_socket(ret, &new_local_addr,1)<0) { IOA_CLOSE_SOCKET(ret); ret = NULL; goto ccs_end; } addr_cpy(&(ret->remote_addr), peer_addr); set_ioa_socket_session(ret, s->session); if(ret->conn_bev) { bufferevent_disable(ret->conn_bev,EV_READ|EV_WRITE); bufferevent_free(ret->conn_bev); ret->conn_bev=NULL; } ret->conn_bev = bufferevent_socket_new(ret->e->event_base, ret->fd, BEV_OPT_THREADSAFE | BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS); bufferevent_setcb(ret->conn_bev, NULL, NULL, connect_eventcb, ret); ret->conn_arg = arg; ret->conn_cb = cb; if (bufferevent_socket_connect(ret->conn_bev, (struct sockaddr *) peer_addr, get_ioa_addr_len(peer_addr)) < 0) { /* Error starting connection */ set_ioa_socket_session(ret, NULL); IOA_CLOSE_SOCKET(ret); ret = NULL; goto ccs_end; } ccs_end: #if !defined(SO_REUSEPORT) #if !defined(__CYGWIN__) && !defined(__CYGWIN32__) && !defined(__CYGWIN64__) /* * trick for OSes which do not support SO_REUSEPORT. * Section 5.2 of RFC 6062 will not work correctly * for those OSes (for example, Linux pre-3.9 kernel). */ s->fd = socket(s->family, SOCK_STREAM, 0); if (s->fd < 0) { perror("TCP socket"); if(ret) { set_ioa_socket_session(ret, NULL); IOA_CLOSE_SOCKET(ret); ret = NULL; } } else { set_socket_options(s); sock_bind_to_device(s->fd, (unsigned char*)s->e->relay_ifname); if(bind_ioa_socket(s, &new_local_addr, 1)<0) { if(ret) { set_ioa_socket_session(ret, NULL); IOA_CLOSE_SOCKET(ret); ret = NULL; } } else { set_accept_cb(s, s->acb, s->acbarg); } } #endif #endif return ret; } /* <<== RFC 6062 */ void add_socket_to_parent(ioa_socket_handle parent_s, ioa_socket_handle s) { if(parent_s && s) { delete_socket_from_parent(s); s->parent_s = parent_s; s->fd = parent_s->fd; } } void delete_socket_from_parent(ioa_socket_handle s) { if(s && s->parent_s) { s->parent_s = NULL; s->fd = -1; } } void add_socket_to_map(ioa_socket_handle s, ur_addr_map *amap) { if(amap && s && (s->sockets_container != amap)) { delete_socket_from_map(s); ur_addr_map_del(amap, &(s->remote_addr),NULL); ur_addr_map_put(amap, &(s->remote_addr), (ur_addr_map_value_type)s); s->sockets_container = amap; } } void delete_socket_from_map(ioa_socket_handle s) { if(s && s->sockets_container) { ur_addr_map_del(s->sockets_container, &(s->remote_addr), NULL); s->sockets_container = NULL; } } ioa_socket_handle create_ioa_socket_from_fd(ioa_engine_handle e, ioa_socket_raw fd, ioa_socket_handle parent_s, SOCKET_TYPE st, SOCKET_APP_TYPE sat, const ioa_addr *remote_addr, const ioa_addr *local_addr) { ioa_socket_handle ret = NULL; if ((fd < 0) && !parent_s) { return NULL; } ret = (ioa_socket*)turn_malloc(sizeof(ioa_socket)); ns_bzero(ret,sizeof(ioa_socket)); ret->magic = SOCKET_MAGIC; ret->fd = fd; ret->family = local_addr->ss.sa_family; ret->st = st; ret->sat = sat; ret->e = e; if (local_addr) { ret->bound = 1; addr_cpy(&(ret->local_addr), local_addr); } if (remote_addr) { ret->connected = 1; addr_cpy(&(ret->remote_addr), remote_addr); } if(parent_s) { add_socket_to_parent(parent_s, ret); } else { set_socket_options(ret); } return ret; } /* Only must be called for DTLS_SOCKET */ ioa_socket_handle create_ioa_socket_from_ssl(ioa_engine_handle e, ioa_socket_handle parent_s, SSL* ssl, SOCKET_TYPE st, SOCKET_APP_TYPE sat, const ioa_addr *remote_addr, const ioa_addr *local_addr) { ioa_socket_handle ret = create_ioa_socket_from_fd(e, parent_s->fd, parent_s, st, sat, remote_addr, local_addr); if(ret) { ret->ssl = ssl; if(st == DTLS_SOCKET) STRCPY(ret->orig_ctx_type,"DTLSv1.0"); } return ret; } static void close_socket_net_data(ioa_socket_handle s) { if(s) { EVENT_DEL(s->read_event); if(s->list_ev) { evconnlistener_free(s->list_ev); s->list_ev = NULL; } if(s->conn_bev) { bufferevent_disable(s->conn_bev,EV_READ|EV_WRITE); bufferevent_free(s->conn_bev); s->conn_bev=NULL; } if(s->bev) { bufferevent_disable(s->bev,EV_READ|EV_WRITE); bufferevent_free(s->bev); s->bev=NULL; } if (s->ssl) { if (!s->broken) { if(!(SSL_get_shutdown(s->ssl) & SSL_SENT_SHUTDOWN)) { /* * SSL_RECEIVED_SHUTDOWN tells SSL_shutdown to act as if we had already * received a close notify from the other end. SSL_shutdown will then * send the final close notify in reply. The other end will receive the * close notify and send theirs. By this time, we will have already * closed the socket and the other end's real close notify will never be * received. In effect, both sides will think that they have completed a * clean shutdown and keep their sessions valid. This strategy will fail * if the socket is not ready for writing, in which case this hack will * lead to an unclean shutdown and lost session on the other end. */ SSL_set_shutdown(s->ssl, SSL_RECEIVED_SHUTDOWN); SSL_shutdown(s->ssl); log_socket_event(s, "SSL shutdown received, socket to be closed",0); } } SSL_free(s->ssl); s->ssl = NULL; } if (s->fd >= 0) { socket_closesocket(s->fd); s->fd = -1; } } } void detach_socket_net_data(ioa_socket_handle s) { if(s) { EVENT_DEL(s->read_event); s->read_cb = NULL; s->read_ctx = NULL; if(s->list_ev) { evconnlistener_free(s->list_ev); s->list_ev = NULL; s->acb = NULL; s->acbarg = NULL; } if(s->conn_bev) { bufferevent_disable(s->conn_bev,EV_READ|EV_WRITE); bufferevent_free(s->conn_bev); s->conn_bev=NULL; s->conn_arg=NULL; s->conn_cb=NULL; } if(s->bev) { bufferevent_disable(s->bev,EV_READ|EV_WRITE); bufferevent_free(s->bev); s->bev=NULL; } } } void close_ioa_socket(ioa_socket_handle s) { if (s) { if(s->magic != SOCKET_MAGIC) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s wrong magic on socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, s->st, s->sat); return; } if(s->done) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s double free on socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return; } s->done = 1; while(!buffer_list_empty(&(s->bufs))) pop_elem_from_buffer_list(&(s->bufs)); ioa_network_buffer_delete(s->e, s->defer_nbh); if(s->bound && s->e && s->e->tp) { turnipports_release(s->e->tp, ((s->st == TCP_SOCKET) ? STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE : STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE), &(s->local_addr)); } delete_socket_from_map(s); delete_socket_from_parent(s); close_socket_net_data(s); s->session = NULL; s->sub_session = NULL; s->magic = 0; turn_free(s,sizeof(ioa_socket)); } } ioa_socket_handle detach_ioa_socket(ioa_socket_handle s, int full_detach) { ioa_socket_handle ret = NULL; if (!s) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Detaching NULL socket\n"); } else { if((s->magic != SOCKET_MAGIC)||(s->done)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s detach on bad socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return ret; } if(s->tobeclosed) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s detach on tobeclosed socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, s->st, s->sat); return ret; } if(!(s->e)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s detach on socket without engine: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, s->st, s->sat); return ret; } s->tobeclosed = 1; if(s->parent_s) { if((s->st != UDP_SOCKET) && (s->st != DTLS_SOCKET)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s detach on non-UDP child socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, s->st, s->sat); return ret; } } evutil_socket_t udp_fd = -1; if(full_detach && s->parent_s) { udp_fd = socket(s->local_addr.ss.sa_family, SOCK_DGRAM, 0); if (udp_fd < 0) { perror("socket"); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"%s: Cannot allocate new socket\n",__FUNCTION__); return ret; } } detach_socket_net_data(s); while(!buffer_list_empty(&(s->bufs))) pop_elem_from_buffer_list(&(s->bufs)); ioa_network_buffer_delete(s->e, s->defer_nbh); ret = (ioa_socket*)turn_malloc(sizeof(ioa_socket)); if(!ret) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"%s: Cannot allocate new socket structure\n",__FUNCTION__); if(udp_fd>=0) close(udp_fd); return ret; } ns_bzero(ret,sizeof(ioa_socket)); ret->magic = SOCKET_MAGIC; ret->ssl = s->ssl; ret->fd = s->fd; ret->family = s->family; ret->st = s->st; ret->sat = s->sat; ret->bound = s->bound; ret->local_addr_known = s->local_addr_known; addr_cpy(&(ret->local_addr),&(s->local_addr)); ret->connected = s->connected; ioa_socket_handle parent_s = s->parent_s; addr_cpy(&(ret->remote_addr),&(s->remote_addr)); ur_addr_map *sockets_container = s->sockets_container; delete_socket_from_map(s); delete_socket_from_parent(s); if(full_detach && parent_s) { ret->fd = udp_fd; if(sock_bind_to_device(udp_fd, (unsigned char*)(s->e->relay_ifname))<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot bind udp server socket to device %s\n",(char*)(s->e->relay_ifname)); } if(addr_bind(udp_fd,&(s->local_addr),1)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot bind new detached udp server socket to local addr\n"); IOA_CLOSE_SOCKET(ret); return ret; } int connect_err=0; if(addr_connect(udp_fd, &(s->remote_addr), &connect_err)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot connect new detached udp server socket to remote addr\n"); IOA_CLOSE_SOCKET(ret); return ret; } set_socket_options(ret); } else { add_socket_to_parent(parent_s, ret); add_socket_to_map(ret,sockets_container); } ret->current_ttl = s->current_ttl; ret->default_ttl = s->default_ttl; ret->current_tos = s->current_tos; ret->default_tos = s->default_tos; s->ssl = NULL; s->fd = -1; } return ret; } ts_ur_super_session *get_ioa_socket_session(ioa_socket_handle s) { if(s) return s->session; return NULL; } void set_ioa_socket_session(ioa_socket_handle s, ts_ur_super_session *ss) { if(s) s->session = ss; } void clear_ioa_socket_session_if(ioa_socket_handle s, void *ss) { if(s && ((void*)(s->session)==ss)) { s->session=NULL; } } tcp_connection *get_ioa_socket_sub_session(ioa_socket_handle s) { if(s) return s->sub_session; return NULL; } void set_ioa_socket_sub_session(ioa_socket_handle s, tcp_connection *tc) { if(s) s->sub_session = tc; } int get_ioa_socket_address_family(ioa_socket_handle s) { if(!s) { return AF_INET; } else if(s->parent_s) { return s->parent_s->family; } else { return s->family; } } SOCKET_TYPE get_ioa_socket_type(ioa_socket_handle s) { if(s) return s->st; return UNKNOWN_SOCKET; } SOCKET_APP_TYPE get_ioa_socket_app_type(ioa_socket_handle s) { if(s) return s->sat; return UNKNOWN_APP_SOCKET; } void set_ioa_socket_app_type(ioa_socket_handle s, SOCKET_APP_TYPE sat) { if(s) s->sat = sat; } ioa_addr* get_local_addr_from_ioa_socket(ioa_socket_handle s) { if (s) { if(s->parent_s) { return get_local_addr_from_ioa_socket(s->parent_s); } else if (s->local_addr_known) { return &(s->local_addr); } else if (s->bound && (addr_get_port(&(s->local_addr)) > 0)) { s->local_addr_known = 1; return &(s->local_addr); } else { ioa_addr tmpaddr; if (addr_get_from_sock(s->fd, &tmpaddr) == 0) { if(addr_get_port(&tmpaddr)>0) { s->local_addr_known = 1; s->bound = 1; if(addr_any(&(s->local_addr))) { addr_cpy(&(s->local_addr),&tmpaddr); } else { addr_set_port(&(s->local_addr),addr_get_port(&tmpaddr)); } return &(s->local_addr); } if(addr_any(&(s->local_addr))) { addr_cpy(&(s->local_addr),&tmpaddr); } return &(s->local_addr); } } } return NULL; } ioa_addr* get_remote_addr_from_ioa_socket(ioa_socket_handle s) { if (s) { if (s->connected) { return &(s->remote_addr); } } return NULL; } int get_local_mtu_ioa_socket(ioa_socket_handle s) { if(s) { if(s->parent_s) return get_local_mtu_ioa_socket(s->parent_s); return get_socket_mtu(s->fd, s->family, (s->e && eve(s->e->verbose))); } return -1; } /* * Return: -1 - error, 0 or >0 - OK * *read_len -1 - no data, >=0 - data available */ int ssl_read(evutil_socket_t fd, SSL* ssl, ioa_network_buffer_handle nbh, int verbose) { int ret = 0; if (!ssl || !nbh) return -1; s08bits* buffer = (s08bits*)ioa_network_buffer_data(nbh); int buf_size = (int)ioa_network_buffer_get_capacity_udp(); int read_len = (int)ioa_network_buffer_get_size(nbh); if(read_len < 1) return -1; s08bits *new_buffer = buffer + buf_size; int old_buffer_len = read_len; int len = 0; if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: before read...\n", __FUNCTION__); } BIO *wbio = SSL_get_wbio(ssl); if(wbio) { BIO_set_fd(wbio,fd,BIO_NOCLOSE); } BIO* rbio = BIO_new_mem_buf(buffer, old_buffer_len); BIO_set_mem_eof_return(rbio, -1); ssl->rbio = rbio; int if1 = SSL_is_init_finished(ssl); do { len = SSL_read(ssl, new_buffer, buf_size); } while (len < 0 && (errno == EINTR)); int if2 = SSL_is_init_finished(ssl); if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: after read: %d\n", __FUNCTION__, len); } if(SSL_get_shutdown(ssl)) { ret = -1; } else if (!if1 && if2) { if(verbose && SSL_get_peer_certificate(ssl)) { printf("\n------------------------------------------------------------\n"); X509_NAME_print_ex_fp(stdout, X509_get_subject_name(SSL_get_peer_certificate(ssl)), 1, XN_FLAG_MULTILINE); printf("\n\n Cipher: %s\n", SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))); printf("\n------------------------------------------------------------\n\n"); } ret = 0; } else if (len < 0 && ((errno == ENOBUFS) || (errno == EAGAIN))) { if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: ENOBUFS/EAGAIN\n", __FUNCTION__); } ret = 0; } else { if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: read %d bytes\n", __FUNCTION__, (int) len); } if (len >= 0) { ret = len; } else { switch (SSL_get_error(ssl, len)){ case SSL_ERROR_NONE: //??? ret = 0; break; case SSL_ERROR_WANT_READ: ret = 0; break; case SSL_ERROR_WANT_WRITE: ret = 0; break; case SSL_ERROR_ZERO_RETURN: ret = 0; break; case SSL_ERROR_SYSCALL: { int err = errno; if (handle_socket_error()) { ret = 0; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TLS Socket read error: %d\n", err); ret = -1; } break; } case SSL_ERROR_SSL: if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SSL read error: "); s08bits buf[65536]; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s (%d)\n", ERR_error_string(ERR_get_error(), buf), SSL_get_error(ssl, len)); } if (verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SSL connection closed.\n"); ret = -1; break; default: if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Unexpected error while reading!\n"); } ret = -1; } } } if(ret>0) { ioa_network_buffer_add_offset_size(nbh, (u16bits)buf_size, 0, (size_t)ret); } BIO_free(rbio); ssl->rbio = NULL; return ret; } static int socket_readerr(evutil_socket_t fd, ioa_addr *orig_addr) { if ((fd < 0) || !orig_addr) return -1; #if defined(CMSG_SPACE) && defined(MSG_ERRQUEUE) && defined(IP_RECVERR) u08bits ecmsg[TURN_CMSG_SZ+1]; int flags = MSG_ERRQUEUE; int len = 0; struct msghdr msg; struct iovec iov; char buffer[65536]; char *cmsg = (char*)ecmsg; msg.msg_control = cmsg; msg.msg_controllen = TURN_CMSG_SZ; /* CMSG_SPACE(sizeof(recv_ttl)+sizeof(recv_tos)) */ msg.msg_name = orig_addr; msg.msg_namelen = (socklen_t)get_ioa_addr_len(orig_addr); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_iov->iov_base = buffer; msg.msg_iov->iov_len = sizeof(buffer); msg.msg_flags = 0; int try_cycle = 0; do { do { len = recvmsg(fd,&msg,flags); } while (len < 0 && (errno == EINTR)); } while((len>0)&&(try_cycle++iov_base = buffer; msg.msg_iov->iov_len = (size_t)buf_size; msg.msg_flags = 0; #if defined(MSG_ERRQUEUE) int try_cycle = 0; try_again: #endif do { len = recvmsg(fd,&msg,flags); } while (len < 0 && (errno == EINTR)); #if defined(MSG_ERRQUEUE) if(flags & MSG_ERRQUEUE) { if((len>0)&&(try_cycle++= 0) { struct cmsghdr *cmsgh; // Receive auxiliary data in msg for (cmsgh = CMSG_FIRSTHDR(&msg); cmsgh != NULL; cmsgh = CMSG_NXTHDR(&msg,cmsgh)) { int l = cmsgh->cmsg_level; int t = cmsgh->cmsg_type; switch(l) { case IPPROTO_IP: switch(t) { #if defined(IP_RECVTTL) case IP_RECVTTL: case IP_TTL: recv_ttl = *((recv_ttl_t *) CMSG_DATA(cmsgh)); break; #endif #if defined(IP_RECVTOS) case IP_RECVTOS: case IP_TOS: recv_tos = *((recv_tos_t *) CMSG_DATA(cmsgh)); break; #endif #if defined(IP_RECVERR) case IP_RECVERR: { struct turn_sock_extended_err *e=(struct turn_sock_extended_err*) CMSG_DATA(cmsgh); if(errcode) *errcode = e->ee_errno; } break; #endif default: ; /* no break */ }; break; case IPPROTO_IPV6: switch(t) { #if defined(IPV6_RECVHOPLIMIT) case IPV6_RECVHOPLIMIT: case IPV6_HOPLIMIT: recv_ttl = *((recv_ttl_t *) CMSG_DATA(cmsgh)); break; #endif #if defined(IPV6_RECVTCLASS) case IPV6_RECVTCLASS: case IPV6_TCLASS: recv_tos = *((recv_tos_t *) CMSG_DATA(cmsgh)); break; #endif #if defined(IPV6_RECVERR) case IPV6_RECVERR: { struct turn_sock_extended_err *e=(struct turn_sock_extended_err*) CMSG_DATA(cmsgh); if(errcode) *errcode = e->ee_errno; } break; #endif default: ; /* no break */ }; break; default: ; /* no break */ }; } } #endif *ttl = recv_ttl; CORRECT_RAW_TTL(*ttl); *tos = recv_tos; CORRECT_RAW_TOS(*tos); return len; } #if !defined(TURN_NO_TLS) static TURN_TLS_TYPE check_tentative_tls(ioa_socket_raw fd) { TURN_TLS_TYPE ret = TURN_TLS_NO; char s[12]; int len = 0; do { len = (int)recv(fd, s, sizeof(s), MSG_PEEK); } while (len < 0 && (errno == EINTR)); if(len>0 && ((size_t)len == sizeof(s))) { if((s[0]==22)&&(s[1]==3)&&(s[5]==1)&&(s[9]==3)) { char max_supported = (char)(TURN_TLS_TOTAL-2); if(s[10] >= max_supported) ret = (TURN_TLS_TYPE)((((int)TURN_TLS_TOTAL)-1)); else ret = (TURN_TLS_TYPE)(s[10]+1); } else if((s[2]==1)&&(s[3]==3)) { ret = TURN_TLS_SSL23; /* compatibility mode */ } else if((s[2]==1)&&(s[3]==0)&&(s[4]==2)) { ret = TURN_TLS_SSL23; /* old mode */ } } return ret; } #endif static int socket_input_worker(ioa_socket_handle s) { int len = 0; int ret = 0; size_t app_msg_len = 0; int ttl = TTL_IGNORE; int tos = TOS_IGNORE; ioa_addr remote_addr; int try_again = 0; int try_ok = 0; int try_cycle = 0; const int MAX_TRIES = 16; if(!s) return 0; if((s->magic != SOCKET_MAGIC)||(s->done)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return -1; } if(!(s->e)) return 0; if(s->tobeclosed) return 0; if(s->connected) addr_cpy(&remote_addr,&(s->remote_addr)); if(tcp_congestion_control && s->sub_session && s->bev) { if(s == s->sub_session->client_s) { if(!is_socket_writeable(s->sub_session->peer_s, STUN_BUFFER_SIZE,__FUNCTION__,0)) { if(bufferevent_enabled(s->bev,EV_READ)) { bufferevent_disable(s->bev,EV_READ); } } } else if(s == s->sub_session->peer_s) { if(!is_socket_writeable(s->sub_session->client_s, STUN_BUFFER_SIZE,__FUNCTION__,1)) { if(bufferevent_enabled(s->bev,EV_READ)) { bufferevent_disable(s->bev,EV_READ); } } } } if(s->st == TLS_SOCKET) { #if !defined(TURN_NO_TLS) SSL *ctx = bufferevent_openssl_get_ssl(s->bev); if(!ctx || SSL_get_shutdown(ctx)) { s->tobeclosed = 1; return 0; } #endif } else if(s->st == DTLS_SOCKET) { if(!(s->ssl) || SSL_get_shutdown(s->ssl)) { s->tobeclosed = 1; return 0; } } if(!(s->e)) return 0; if(s->st == TENTATIVE_TCP_SOCKET) { EVENT_DEL(s->read_event); #if !defined(TURN_NO_TLS) TURN_TLS_TYPE tls_type = check_tentative_tls(s->fd); if(tls_type) { s->st = TLS_SOCKET; if(s->ssl) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on socket: 0x%lx, st=%d, sat=%d: ssl already exist\n", __FUNCTION__,(long)s, s->st, s->sat); } if(s->bev) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on socket: 0x%lx, st=%d, sat=%d: bev already exist\n", __FUNCTION__,(long)s, s->st, s->sat); } switch(tls_type) { #if defined(SSL_TXT_TLSV1_2) case TURN_TLS_v1_2: if(s->e->tls_ctx_v1_2) { s->ssl = SSL_new(s->e->tls_ctx_v1_2); STRCPY(s->orig_ctx_type,"TLSv1.2"); break; } #endif #if defined(SSL_TXT_TLSV1_1) case TURN_TLS_v1_1: if(s->e->tls_ctx_v1_1) { s->ssl = SSL_new(s->e->tls_ctx_v1_1); STRCPY(s->orig_ctx_type,"TLSv1.1"); break; } #endif case TURN_TLS_v1_0: if(s->e->tls_ctx_v1_0) { s->ssl = SSL_new(s->e->tls_ctx_v1_0); STRCPY(s->orig_ctx_type,"TLSv1.0"); break; } default: if(s->e->tls_ctx_ssl23) { s->ssl = SSL_new(s->e->tls_ctx_ssl23); STRCPY(s->orig_ctx_type,"SSLv23"); } else { s->tobeclosed = 1; return 0; } }; s->bev = bufferevent_openssl_socket_new(s->e->event_base, s->fd, s->ssl, BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_THREADSAFE | BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS); bufferevent_setcb(s->bev, socket_input_handler_bev, socket_output_handler_bev, eventcb_bev, s); bufferevent_setwatermark(s->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK); bufferevent_enable(s->bev, EV_READ|EV_WRITE); /* Start reading. */ } else #endif //TURN_NO_TLS { s->st = TCP_SOCKET; if(s->bev) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on socket: 0x%lx, st=%d, sat=%d: bev already exist\n", __FUNCTION__,(long)s, s->st, s->sat); } s->bev = bufferevent_socket_new(s->e->event_base, s->fd, BEV_OPT_THREADSAFE | BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS); bufferevent_setcb(s->bev, socket_input_handler_bev, socket_output_handler_bev, eventcb_bev, s); bufferevent_setwatermark(s->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK); bufferevent_enable(s->bev, EV_READ|EV_WRITE); /* Start reading. */ } } try_start: if(!(s->e)) return 0; try_again=0; try_ok=0; stun_buffer_list_elem *buf_elem = new_blist_elem(s->e); len = -1; if(s->bev) { /* TCP & TLS */ struct evbuffer *inbuf = bufferevent_get_input(s->bev); if(inbuf) { ev_ssize_t blen = evbuffer_copyout(inbuf, buf_elem->buf.buf, STUN_BUFFER_SIZE); if(blen>0) { int mlen = 0; if(blen>(ev_ssize_t)STUN_BUFFER_SIZE) blen=(ev_ssize_t)STUN_BUFFER_SIZE; if(((s->st == TCP_SOCKET)||(s->st == TLS_SOCKET)) && ((s->sat == TCP_CLIENT_DATA_SOCKET)||(s->sat==TCP_RELAY_DATA_SOCKET))) { mlen = blen; } else { mlen = stun_get_message_len_str(buf_elem->buf.buf, blen, 1, &app_msg_len); } if(mlen>0 && mlen<=(int)blen) { len = (int)bufferevent_read(s->bev, buf_elem->buf.buf, mlen); if(len < 0) { ret = -1; s->tobeclosed = 1; s->broken = 1; log_socket_event(s, "socket read failed, to be closed",1); } else if(s->st == TLS_SOCKET) { #if !defined(TURN_NO_TLS) SSL *ctx = bufferevent_openssl_get_ssl(s->bev); if(!ctx || SSL_get_shutdown(ctx)) { ret = -1; s->tobeclosed = 1; } #endif } if(ret != -1) { ret = len; } } } else if(blen<0) { s->tobeclosed = 1; s->broken = 1; ret = -1; log_socket_event(s, "socket buffer copy failed, to be closed",1); } } else { s->tobeclosed = 1; s->broken = 1; ret = -1; log_socket_event(s, "socket input failed, socket to be closed",1); } if(len == 0) len = -1; } else if(s->fd>=0){ /* UDP and DTLS */ ret = udp_recvfrom(s->fd, &remote_addr, &(s->local_addr), (s08bits*)(buf_elem->buf.buf), UDP_STUN_BUFFER_SIZE, &ttl, &tos, s->e->cmsg, 0, NULL); len = ret; if(s->ssl && (len>0)) { /* DTLS */ send_ssl_backlog_buffers(s); buf_elem->buf.len = (size_t)len; ret = ssl_read(s->fd, s->ssl, (ioa_network_buffer_handle)buf_elem, ((s->e) && s->e->verbose)); addr_cpy(&remote_addr,&(s->remote_addr)); if(ret < 0) { len = -1; s->tobeclosed = 1; s->broken = 1; log_socket_event(s, "SSL read failed, to be closed",0); } else { len = (int)ioa_network_buffer_get_size((ioa_network_buffer_handle)buf_elem); } if((ret!=-1)&&(len>0)) try_again = 1; } else { /* UDP */ if(ret>=0) try_again = 1; } } else { s->tobeclosed = 1; s->broken = 1; ret = -1; log_socket_event(s, "socket unknown error, to be closed",1); } if ((ret!=-1) && (len >= 0)) { if(ioa_socket_check_bandwidth(s,(size_t)len,1)) { if(app_msg_len) buf_elem->buf.len = app_msg_len; else buf_elem->buf.len = len; if(s->read_cb) { ioa_net_data nd; ns_bzero(&nd,sizeof(ioa_net_data)); addr_cpy(&(nd.src_addr),&remote_addr); nd.nbh = buf_elem; nd.recv_ttl = ttl; nd.recv_tos = tos; s->read_cb(s, IOA_EV_READ, &nd, s->read_ctx); if(nd.nbh) free_blist_elem(s->e,buf_elem); buf_elem = NULL; try_ok = 1; } else { ioa_network_buffer_delete(s->e, s->defer_nbh); s->defer_nbh = buf_elem; buf_elem = NULL; } } } if(buf_elem) { free_blist_elem(s->e,buf_elem); buf_elem = NULL; } if(try_again && try_ok && !(s->done) && !(s->tobeclosed) && ((++try_cycle)parent_s)) { goto try_start; } return len; } static void socket_input_handler(evutil_socket_t fd, short what, void* arg) { if (!(what & EV_READ)) return; if(!arg) { read_spare_buffer(fd); return; } ioa_socket_handle s = (ioa_socket_handle)arg; if(!s) return; if((s->magic != SOCKET_MAGIC)||(s->done)) { read_spare_buffer(fd); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on bad socket, ev=%d: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(int)what,(long)s, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return; } if(fd != s->fd) { read_spare_buffer(fd); return; } if (!ioa_socket_tobeclosed(s)) socket_input_worker(s); if((s->magic != SOCKET_MAGIC)||(s->done)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s (1) on socket, ev=%d: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(int)what,(long)s, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return; } close_ioa_socket_after_processing_if_necessary(s); } void close_ioa_socket_after_processing_if_necessary(ioa_socket_handle s) { if (s && ioa_socket_tobeclosed(s)) { switch (s->sat){ case TCP_CLIENT_DATA_SOCKET: case TCP_RELAY_DATA_SOCKET: { tcp_connection *tc = s->sub_session; if (tc) { delete_tcp_connection(tc); } } break; default: { ts_ur_super_session *ss = s->session; if (ss) { turn_turnserver *server = (turn_turnserver *) ss->server; if (server) { shutdown_client_connection(server, ss, 0, "general"); } } } } } } static void socket_output_handler_bev(struct bufferevent *bev, void* arg) { UNUSED_ARG(bev); UNUSED_ARG(arg); if (tcp_congestion_control) { if (bev && arg) { ioa_socket_handle s = (ioa_socket_handle) arg; if ((s->magic != SOCKET_MAGIC)||(s->done)||ioa_socket_tobeclosed(s)||(bev != s->bev)) { return; } if (s->sub_session) { if (s == s->sub_session->client_s) { if (s->sub_session->peer_s && s->sub_session->peer_s->bev) { if (!bufferevent_enabled(s->sub_session->peer_s->bev, EV_READ)) { if (is_socket_writeable(s->sub_session->peer_s, STUN_BUFFER_SIZE, __FUNCTION__, 3)) { bufferevent_enable(s->sub_session->peer_s->bev,EV_READ); socket_input_handler_bev( s->sub_session->peer_s->bev, s->sub_session->peer_s); } } } } else if (s == s->sub_session->peer_s) { if (s->sub_session->client_s && s->sub_session->client_s->bev) { if (!bufferevent_enabled(s->sub_session->client_s->bev, EV_READ)) { if (is_socket_writeable(s->sub_session->client_s, STUN_BUFFER_SIZE, __FUNCTION__, 4)) { bufferevent_enable(s->sub_session->client_s->bev, EV_READ); socket_input_handler_bev( s->sub_session->client_s->bev, s->sub_session->client_s); } } } } } } } } static void socket_input_handler_bev(struct bufferevent *bev, void* arg) { if (bev && arg) { ioa_socket_handle s = (ioa_socket_handle) arg; if(bev != s->bev) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx: wrong bev\n", __FUNCTION__,(long)s); return; } if((s->magic != SOCKET_MAGIC)||(s->done)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__, (long) s, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return; } while (!ioa_socket_tobeclosed(s)) { if (socket_input_worker(s) <= 0) break; } if((s->magic != SOCKET_MAGIC)||(s->done)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s (1) on socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__, (long) s, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return; } if (ioa_socket_tobeclosed(s)) { switch(s->sat) { case TCP_CLIENT_DATA_SOCKET: case TCP_RELAY_DATA_SOCKET: { tcp_connection *tc = s->sub_session; if(tc) { delete_tcp_connection(tc); } } break; default: { ts_ur_super_session *ss = s->session; if (ss) { turn_turnserver *server = (turn_turnserver *)ss->server; if (server) { shutdown_client_connection(server, ss, 0, "TCP socket buffer operation error (input handler)"); } } } } } } } static void eventcb_bev(struct bufferevent *bev, short events, void *arg) { UNUSED_ARG(bev); if (events & BEV_EVENT_CONNECTED) { // Connect okay } else if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF)) { if (arg) { ioa_socket_handle s = (ioa_socket_handle) arg; if((s->st != TCP_SOCKET)&&(s->st != TLS_SOCKET)&&(s->st != TENTATIVE_TCP_SOCKET)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s: socket type is wrong on the socket: 0x%lx, st=%d, sat=%d\n",__FUNCTION__,(long)s,s->st,s->sat); return; } if(s->magic != SOCKET_MAGIC) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s: magic is wrong on the socket: 0x%lx, st=%d, sat=%d\n",__FUNCTION__,(long)s,s->st,s->sat); return; } if (s->done) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s: closed socket: 0x%lx (1): done=%d, fd=%d, br=%d, st=%d, sat=%d, tbc=%d\n", __FUNCTION__, (long) s, (int) s->done, (int) s->fd, s->broken, s->st, s->sat, s->tobeclosed); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return; } if (events & BEV_EVENT_ERROR) s->broken = 1; s->tobeclosed = 1; switch (s->sat){ case TCP_CLIENT_DATA_SOCKET: case TCP_RELAY_DATA_SOCKET: { tcp_connection *tc = s->sub_session; if (tc) { delete_tcp_connection(tc); } } break; default: { ts_ur_super_session *ss = s->session; if (ss) { turn_turnserver *server = (turn_turnserver *) ss->server; if (server) { { char sraddr[129]="\0"; addr_to_string(&(s->remote_addr),(u08bits*)sraddr); if (events & BEV_EVENT_EOF) { if(server->verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: TCP socket closed remotely %s\n",(unsigned long long)(ss->id),sraddr); if(s == ss->client_session.s) { shutdown_client_connection(server, ss, 0, "TCP connection closed by client (callback)"); } else if(s == ss->alloc.relay_session.s) { shutdown_client_connection(server, ss, 0, "TCP connection closed by peer (callback)"); } else { shutdown_client_connection(server, ss, 0, "TCP connection closed by remote party (callback)"); } } else if (events & BEV_EVENT_ERROR) { if(EVUTIL_SOCKET_ERROR()) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"session %018llu: TCP socket error: %s %s\n",(unsigned long long)(ss->id), evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()), sraddr); } else if(server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: TCP socket disconnected: %s\n",(unsigned long long)(ss->id),sraddr); } shutdown_client_connection(server, ss, 0, "TCP socket buffer operation error (callback)"); } } } } } }; } } } static int ssl_send(ioa_socket_handle s, const s08bits* buffer, int len, int verbose) { if (!s || !(s->ssl) || !buffer || (s->fd<0)) return -1; SSL *ssl = s->ssl; if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: before write: buffer=0x%lx, len=%d\n", __FUNCTION__,(long)buffer,len); } if(s->parent_s) { /* Trick only for "children" sockets: */ BIO *wbio = SSL_get_wbio(ssl); if(!wbio) return -1; int fd = BIO_get_fd(wbio,0); int sfd = s->parent_s->fd; if(sfd >= 0) { if(fd != sfd) { BIO_set_fd(wbio,sfd,BIO_NOCLOSE); } } } else { BIO *wbio = SSL_get_wbio(ssl); if(!wbio) return -1; int fd = BIO_get_fd(wbio,0); if(fd != s->fd) { BIO_set_fd(wbio,s->fd,BIO_NOCLOSE); } } int rc = 0; int try_again = 1; #if !defined(TURN_IP_RECVERR) try_again = 0; #endif try_start: do { rc = SSL_write(ssl, buffer, len); } while (rc < 0 && errno == EINTR); if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: after write: %d\n", __FUNCTION__,rc); } if (rc < 0 && ((errno == ENOBUFS) || (errno == EAGAIN))) { if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: ENOBUFS/EAGAIN\n", __FUNCTION__); } return 0; } if (rc >= 0) { if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: wrote %d bytes\n", __FUNCTION__, (int) rc); } return rc; } else { if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: failure: rc=%d, err=%d\n", __FUNCTION__, (int)rc,(int)SSL_get_error(ssl, rc)); } switch (SSL_get_error(ssl, rc)){ case SSL_ERROR_NONE: //??? if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "wrote %d bytes\n", (int) rc); } return 0; case SSL_ERROR_WANT_WRITE: return 0; case SSL_ERROR_WANT_READ: return 0; case SSL_ERROR_SYSCALL: { int err = errno; if (!handle_socket_error()) { if(s->st == DTLS_SOCKET) { if(is_connreset()) { if(try_again) { BIO *wbio = SSL_get_wbio(ssl); if(wbio) { int fd = BIO_get_fd(wbio,0); if(fd>=0) { try_again = 0; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS Socket, tring to recover write operation...\n"); socket_readerr(fd, &(s->local_addr)); goto try_start; } } } } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS Socket lost packet... fine\n"); return 0; } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS Socket write error unrecoverable: %d; buffer=0x%lx, len=%d, ssl=0x%lx\n", err, (long)buffer, (int)len, (long)ssl); return -1; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS Socket write error recoverable: %d\n", err); return 0; } } case SSL_ERROR_SSL: if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SSL write error: "); s08bits buf[65536]; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s (%d)\n", ERR_error_string(ERR_get_error(), buf), SSL_get_error(ssl, rc)); } return -1; default: if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Unexpected error while writing!\n"); } return -1; } } } static int send_ssl_backlog_buffers(ioa_socket_handle s) { int ret = 0; if(s) { stun_buffer_list_elem *buf_elem = s->bufs.head; while(buf_elem) { int rc = ssl_send(s, (s08bits*)buf_elem->buf.buf + buf_elem->buf.offset - buf_elem->buf.coffset, (size_t)buf_elem->buf.len, ((s->e) && s->e->verbose)); if(rc<1) break; ++ret; pop_elem_from_buffer_list(&(s->bufs)); buf_elem = s->bufs.head; } } return ret; } int is_connreset(void) { switch (errno) { case ECONNRESET: case ECONNREFUSED: return 1; default: ; } return 0; } int would_block(void) { #if defined(EWOULDBLOCK) if(errno == EWOULDBLOCK) return 1; #endif return (errno == EAGAIN); } int udp_send(ioa_socket_handle s, const ioa_addr* dest_addr, const s08bits* buffer, int len) { int rc = 0; evutil_socket_t fd = -1; if(!s) return -1; if(s->parent_s) fd = s->parent_s->fd; else fd = s->fd; if(fd>=0) { int try_again = 1; int cycle; #if !defined(TURN_IP_RECVERR) try_again = 0; #endif try_start: cycle = 0; if (dest_addr) { int slen = get_ioa_addr_len(dest_addr); do { rc = sendto(fd, buffer, len, 0, (const struct sockaddr*) dest_addr, (socklen_t) slen); } while ( ((rc < 0) && (errno == EINTR)) || ((rc<0) && is_connreset() && (++cyclelocal_addr)); goto try_start; } //Lost packet - sent to nowhere... fine. rc = len; } } } return rc; } int send_data_from_ioa_socket_nbh(ioa_socket_handle s, ioa_addr* dest_addr, ioa_network_buffer_handle nbh, int ttl, int tos) { int ret = -1; if(!s) return -1; if (s->done || (s->fd == -1)) { TURN_LOG_FUNC( TURN_LOG_LEVEL_INFO, "!!! %s: (1) Trying to send data from closed socket: 0x%lx (1): done=%d, fd=%d, st=%d, sat=%d\n", __FUNCTION__, (long) s, (int) s->done, (int) s->fd, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); } else if (nbh) { if(!ioa_socket_check_bandwidth(s,ioa_network_buffer_get_size(nbh),0)) { /* Bandwidth exhausted, we pretend everything is fine: */ ret = (int)(ioa_network_buffer_get_size(nbh)); } else { if (!ioa_socket_tobeclosed(s) && s->e) { if (!(s->done || (s->fd == -1))) { set_socket_ttl(s, ttl); set_socket_tos(s, tos); if (s->connected && s->bev) { if (s->st == TLS_SOCKET) { #if !defined(TURN_NO_TLS) SSL *ctx = bufferevent_openssl_get_ssl(s->bev); if (!ctx || SSL_get_shutdown(ctx)) { s->tobeclosed = 1; ret = 0; } #endif } if (!(s->tobeclosed)) { ret = (int) ioa_network_buffer_get_size(nbh); if(!udp_congestion_control || is_socket_writeable(s,(size_t)ret,__FUNCTION__,2)) { if (bufferevent_write( s->bev, ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh)) < 0) { ret = -1; perror("bufev send"); log_socket_event(s, "socket write failed, to be closed",1); s->tobeclosed = 1; s->broken = 1; } } else { //drop the packet ; } } } else if (s->ssl) { send_ssl_backlog_buffers(s); ret = ssl_send( s, (s08bits*) ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh), ((s->e) && s->e->verbose)); if (ret < 0) s->tobeclosed = 1; else if (ret == 0) add_buffer_to_buffer_list( &(s->bufs), (s08bits*) ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh)); } else if (s->fd >= 0) { if (s->connected && !(s->parent_s)) { dest_addr = NULL; /* ignore dest_addr */ } else if (!dest_addr) { dest_addr = &(s->remote_addr); } ret = udp_send(s, dest_addr, (s08bits*) ioa_network_buffer_data(nbh),ioa_network_buffer_get_size(nbh)); if (ret < 0) { s->tobeclosed = 1; #if defined(EADDRNOTAVAIL) int perr=errno; #endif perror("udp send"); #if defined(EADDRNOTAVAIL) if(dest_addr && (perr==EADDRNOTAVAIL)) { char sfrom[129]; addr_to_string(&(s->local_addr), (u08bits*)sfrom); char sto[129]; addr_to_string(dest_addr, (u08bits*)sto); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: network error: address unreachable from %s to %s\n", __FUNCTION__,sfrom,sto); } #endif } } } } } } ioa_network_buffer_delete(s->e, nbh); return ret; } int register_callback_on_ioa_socket(ioa_engine_handle e, ioa_socket_handle s, int event_type, ioa_net_event_handler cb, void* ctx, int clean_preexisting) { if(s) { if (event_type & IOA_EV_READ) { if(e) s->e = e; if(s->e && !(s->parent_s)) { switch(s->st) { case DTLS_SOCKET: case UDP_SOCKET: if(s->read_event) { if(!clean_preexisting) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: software error: buffer preset 1\n", __FUNCTION__); return -1; } } else { s->read_event = event_new(s->e->event_base,s->fd, EV_READ|EV_PERSIST, socket_input_handler, s); event_add(s->read_event,NULL); } break; case TENTATIVE_TCP_SOCKET: if(s->bev) { if(!clean_preexisting) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: software error: buffer preset 2\n", __FUNCTION__); return -1; } } else if(s->read_event) { if(!clean_preexisting) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: software error: buffer preset 3\n", __FUNCTION__); return -1; } } else { s->read_event = event_new(s->e->event_base,s->fd, EV_READ|EV_PERSIST, socket_input_handler, s); event_add(s->read_event,NULL); } break; case TCP_SOCKET: if(s->bev) { if(!clean_preexisting) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: software error: buffer preset 4\n", __FUNCTION__); return -1; } } else { s->bev = bufferevent_socket_new(s->e->event_base, s->fd, BEV_OPT_THREADSAFE | BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS); bufferevent_setcb(s->bev, socket_input_handler_bev, socket_output_handler_bev, eventcb_bev, s); bufferevent_setwatermark(s->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK); bufferevent_enable(s->bev, EV_READ|EV_WRITE); /* Start reading. */ } break; case TLS_SOCKET: if(s->bev) { if(!clean_preexisting) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: software error: buffer preset 5\n", __FUNCTION__); return -1; } } else { #if !defined(TURN_NO_TLS) if(!(s->ssl)) { //??? how we can get to this point ??? s->ssl = SSL_new(e->tls_ctx_ssl23); STRCPY(s->orig_ctx_type,"SSLv23"); s->bev = bufferevent_openssl_socket_new(s->e->event_base, s->fd, s->ssl, BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_THREADSAFE | BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS); } else { s->bev = bufferevent_openssl_socket_new(s->e->event_base, s->fd, s->ssl, BUFFEREVENT_SSL_OPEN, BEV_OPT_THREADSAFE | BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS); } bufferevent_setcb(s->bev, socket_input_handler_bev, socket_output_handler_bev, eventcb_bev, s); bufferevent_setwatermark(s->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK); bufferevent_enable(s->bev, EV_READ|EV_WRITE); /* Start reading. */ #endif } break; default: TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: software error: unknown socket type: %d\n", __FUNCTION__,(int)(s->st)); return -1; } } s->read_cb = cb; s->read_ctx = ctx; return 0; } } /* unsupported event or else */ return -1; } int ioa_socket_tobeclosed(ioa_socket_handle s) { if(s) { if(s->magic != SOCKET_MAGIC) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s: magic is wrong on the socket: 0x%lx, st=%d, sat=%d\n",__FUNCTION__,(long)s,s->st,s->sat); return 1; } if(s->done) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s: ceck on already closed socket: 0x%lx, st=%d, sat=%d\n",__FUNCTION__,(long)s,s->st,s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return 1; } if(s->broken) { log_socket_event(s, "socket broken", 0); return 1; } else if(s->tobeclosed) { log_socket_event(s, "socket to be closed", 0); return 1; } else if(s->fd < 0) { log_socket_event(s, "socket fd<0", 0); return 1; } else if(s->ssl) { if(SSL_get_shutdown(s->ssl)) { log_socket_event(s, "socket SSL shutdown", 0); return 1; } } } return 0; } void set_ioa_socket_tobeclosed(ioa_socket_handle s) { if(s) s->tobeclosed = 1; } /* * Network buffer functions */ ioa_network_buffer_handle ioa_network_buffer_allocate(ioa_engine_handle e) { stun_buffer_list_elem *buf_elem = new_blist_elem(e); buf_elem->buf.len = 0; buf_elem->buf.offset = 0; buf_elem->buf.coffset = 0; return buf_elem; } /* We do not use special header in this simple implementation */ void ioa_network_buffer_header_init(ioa_network_buffer_handle nbh) { UNUSED_ARG(nbh); } u08bits *ioa_network_buffer_data(ioa_network_buffer_handle nbh) { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; return buf_elem->buf.buf + buf_elem->buf.offset - buf_elem->buf.coffset; } size_t ioa_network_buffer_get_size(ioa_network_buffer_handle nbh) { if(!nbh) return 0; else { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; return (size_t)(buf_elem->buf.len); } } size_t ioa_network_buffer_get_capacity(ioa_network_buffer_handle nbh) { if(!nbh) return 0; else { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; if(buf_elem->buf.offset < STUN_BUFFER_SIZE) { return (STUN_BUFFER_SIZE - buf_elem->buf.offset); } return 0; } } size_t ioa_network_buffer_get_capacity_udp(void) { return UDP_STUN_BUFFER_SIZE; } void ioa_network_buffer_set_size(ioa_network_buffer_handle nbh, size_t len) { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; buf_elem->buf.len=(size_t)len; } void ioa_network_buffer_add_offset_size(ioa_network_buffer_handle nbh, u16bits offset, u08bits coffset, size_t len) { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; buf_elem->buf.len=(size_t)len; buf_elem->buf.offset += offset; buf_elem->buf.coffset += coffset; if((buf_elem->buf.offset + buf_elem->buf.len - buf_elem->buf.coffset)>=sizeof(buf_elem->buf.buf) || (buf_elem->buf.offset + sizeof(buf_elem->buf.channel) < buf_elem->buf.coffset) ) { buf_elem->buf.coffset = 0; buf_elem->buf.len = 0; buf_elem->buf.offset = 0; } } u16bits ioa_network_buffer_get_offset(ioa_network_buffer_handle nbh) { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; return buf_elem->buf.offset; } u08bits ioa_network_buffer_get_coffset(ioa_network_buffer_handle nbh) { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; return buf_elem->buf.coffset; } void ioa_network_buffer_delete(ioa_engine_handle e, ioa_network_buffer_handle nbh) { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; free_blist_elem(e,buf_elem); } /////////// REPORTING STATUS ///////////////////// void turn_report_allocation_set(void *a, turn_time_t lifetime, int refresh) { if(a) { ts_ur_super_session *ss = (ts_ur_super_session*)(((allocation*)a)->owner); if(ss) { const char* status="new"; if(refresh) status="refreshed"; turn_turnserver *server = (turn_turnserver*)ss->server; if(server) { ioa_engine_handle e = turn_server_get_engine(server); if(e && e->verbose) { if(ss->client_session.s->ssl) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: %s, username=<%s>, lifetime=%lu, cipher=%s, method=%s (%s)\n", (unsigned long long)ss->id, status, (char*)ss->username, (unsigned long)lifetime, SSL_get_cipher(ss->client_session.s->ssl), turn_get_ssl_method(ss->client_session.s->ssl, ss->client_session.s->orig_ctx_type),ss->client_session.s->orig_ctx_type); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: %s, username=<%s>, lifetime=%lu\n", (unsigned long long)ss->id, status, (char*)ss->username, (unsigned long)lifetime); } } } #if !defined(TURN_NO_HIREDIS) if(default_async_context_is_not_empty()) { char key[1024]; snprintf(key,sizeof(key),"turn/user/%s/allocation/%llu/status",(char*)ss->username, (unsigned long long)ss->id); send_message_to_redis(NULL, "set", key, "%s lifetime=%lu", status, (unsigned long)lifetime); send_message_to_redis(NULL, "publish", key, "%s lifetime=%lu", status, (unsigned long)lifetime); } #endif } } } void turn_report_allocation_delete(void *a) { if(a) { ts_ur_super_session *ss = (ts_ur_super_session*)(((allocation*)a)->owner); if(ss) { turn_turnserver *server = (turn_turnserver*)ss->server; if(server) { ioa_engine_handle e = turn_server_get_engine(server); if(e && e->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: delete: username=<%s>\n", (unsigned long long)ss->id, (char*)ss->username); } } #if !defined(TURN_NO_HIREDIS) if(default_async_context_is_not_empty()) { char key[1024]; snprintf(key,sizeof(key),"turn/user/%s/allocation/%llu/status",(char*)ss->username, (unsigned long long)ss->id); send_message_to_redis(NULL, "del", key, ""); send_message_to_redis(NULL, "publish", key, "deleted"); } #endif } } } void turn_report_allocation_delete_all(void) { #if !defined(TURN_NO_HIREDIS) delete_redis_keys("turn/user/*/allocation/*/status"); #endif } void turn_report_session_usage(void *session) { if(session) { ts_ur_super_session *ss = (ts_ur_super_session *)session; turn_turnserver *server = (turn_turnserver*)ss->server; if(server && (ss->received_packets || ss->sent_packets)) { ioa_engine_handle e = turn_server_get_engine(server); if(((ss->received_packets+ss->sent_packets)&2047)==0) { if(e && e->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: usage: username=<%s>, rp=%lu, rb=%lu, sp=%lu, sb=%lu\n", (unsigned long long)(ss->id), (char*)ss->username, (unsigned long)(ss->received_packets), (unsigned long)(ss->received_bytes),(unsigned long)(ss->sent_packets),(unsigned long)(ss->sent_bytes)); } #if !defined(TURN_NO_HIREDIS) if(default_async_context_is_not_empty()) { char key[1024]; snprintf(key,sizeof(key),"turn/user/%s/allocation/%llu/traffic",(char*)ss->username, (unsigned long long)(ss->id)); send_message_to_redis(NULL, "publish", key, "rcvp=%lu, rcvb=%lu, sentp=%lu, sentb=%lu",(unsigned long)(ss->received_packets), (unsigned long)(ss->received_bytes),(unsigned long)(ss->sent_packets),(unsigned long)(ss->sent_bytes)); } #endif ss->t_received_packets += ss->received_packets; ss->t_received_bytes += ss->received_bytes; ss->t_sent_packets += ss->sent_packets; ss->t_sent_bytes += ss->sent_bytes; { turn_time_t ct = get_turn_server_time(server); if(ct != ss->start_time) { ct = ct - ss->start_time; ss->received_rate = (u32bits)(ss->t_received_bytes / ct); ss->sent_rate = (u32bits)(ss->t_sent_bytes / ct); ss->total_rate = ss->received_rate + ss->sent_rate; } } report_turn_session_info(server,ss,0); ss->received_packets=0; ss->received_bytes=0; ss->sent_packets=0; ss->sent_bytes=0; } } } } /////////////// SSL /////////////////// const char* get_ioa_socket_tls_cipher(ioa_socket_handle s) { if(s && (s->ssl)) return SSL_get_cipher(s->ssl); return ""; } const char* get_ioa_socket_tls_method(ioa_socket_handle s) { if(s && (s->ssl)) return turn_get_ssl_method(s->ssl,"UNKNOWN"); return ""; } ///////////// Super Memory Region ////////////// #define TURN_SM_SIZE (1024<<11) struct _super_memory { pthread_mutex_t mutex_sm; char **super_memory; size_t *sm_allocated; size_t sm_total_sz; size_t sm_chunk; u32bits id; }; static void init_super_memory_region(super_memory_t *r) { if(r) { ns_bzero(r,sizeof(super_memory_t)); r->super_memory = (char**)malloc(sizeof(char*)); r->super_memory[0] = (char*)malloc(TURN_SM_SIZE); ns_bzero(r->super_memory[0],TURN_SM_SIZE); r->sm_allocated = (size_t*)malloc(sizeof(size_t*)); r->sm_allocated[0] = 0; r->sm_total_sz = TURN_SM_SIZE; r->sm_chunk = 0; while(r->id == 0) r->id = (u32bits)random(); pthread_mutex_init(&r->mutex_sm, NULL); } } void init_super_memory(void) { ; } super_memory_t* new_super_memory_region(void) { super_memory_t* r = (super_memory_t*)malloc(sizeof(super_memory_t)); init_super_memory_region(r); return r; } void* allocate_super_memory_region_func(super_memory_t *r, size_t size, const char* file, const char* func, int line) { UNUSED_ARG(file); UNUSED_ARG(func); UNUSED_ARG(line); if(!r) return malloc(size); void *ret = NULL; pthread_mutex_lock(&r->mutex_sm); size = ((size_t)((size+sizeof(void*))/(sizeof(void*)))) * sizeof(void*); if(size>=TURN_SM_SIZE) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"(%s:%s:%d): Size too large for super memory: region id = %u, chunk=%lu, total=%lu, allocated=%lu, want=%lu\n",file,func,line,(unsigned int)r->id, (unsigned long)r->sm_chunk, (unsigned long)r->sm_total_sz, (unsigned long)r->sm_allocated[r->sm_chunk],(unsigned long)size); } else { size_t i = 0; char *region = NULL; size_t *rsz = NULL; size_t chn = 0; for(i=0;i<=r->sm_chunk;++i) { size_t left = (size_t)r->sm_total_sz - r->sm_allocated[i]; if(leftsuper_memory[i]; rsz = r->sm_allocated + i; chn = i; break; } } if(!region) { r->sm_chunk += 1; r->super_memory = (char**)realloc(r->super_memory,(r->sm_chunk+1) * sizeof(char*)); r->super_memory[r->sm_chunk] = (char*)malloc(TURN_SM_SIZE); ns_bzero(r->super_memory[r->sm_chunk],TURN_SM_SIZE); r->sm_allocated = (size_t*)realloc(r->sm_allocated,(r->sm_chunk+1) * sizeof(size_t*)); r->sm_allocated[r->sm_chunk] = 0; region = r->super_memory[r->sm_chunk]; rsz = r->sm_allocated + r->sm_chunk; chn = r->sm_chunk; } { if(chn) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"(%s:%s:%d): allocated super memory: region id = %u, chunk=%lu, chunks=%lu, total=%lu, allocated=%lu, want=%lu\n",file,func,line,(unsigned int)r->id, (unsigned long)chn, (unsigned long)(r->sm_chunk+1), (unsigned long)r->sm_total_sz, (unsigned long)r->sm_allocated[chn],(unsigned long)size); char* ptr = region + *rsz; ns_bzero(ptr, size); *rsz += size; ret = ptr; } } pthread_mutex_unlock(&r->mutex_sm); if(!ret) ret = malloc(size); return ret; } void* allocate_super_memory_engine_func(ioa_engine_handle e, size_t size, const char* file, const char* func, int line) { if(e) return allocate_super_memory_region_func(e->sm,size,file,func,line); return allocate_super_memory_region_func(NULL,size,file,func,line); } ////////////////////////////////////////////////// turnserver-3.2.3.1/src/apps/stunclient/stunclient.c000644 001751 001751 00000030004 12315706777 022307 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include #include #include #include #include #include #include "ns_turn_utils.h" #include "apputils.h" #include "stun_buffer.h" #ifdef __cplusplus #include "TurnMsgLib.h" #endif //////////////////////////////////////////////////// static int udp_fd = -1; static ioa_addr real_local_addr; static int counter = 0; #ifdef __cplusplus static int run_stunclient(const char* rip, int rport, int *port, int *rfc5780, int response_port, int change_ip, int change_port, int padding) { ioa_addr remote_addr; int new_udp_fd = -1; memset((void *) &remote_addr, 0, sizeof(ioa_addr)); if (make_ioa_addr((const u08bits*) rip, rport, &remote_addr) < 0) err(-1, NULL); if (udp_fd < 0) { udp_fd = socket(remote_addr.ss.sa_family, SOCK_DGRAM, 0); if (udp_fd < 0) err(-1, NULL); if (!addr_any(&real_local_addr)) { if (addr_bind(udp_fd, &real_local_addr,0) < 0) err(-1, NULL); } } if (response_port >= 0) { new_udp_fd = socket(remote_addr.ss.sa_family, SOCK_DGRAM, 0); if (new_udp_fd < 0) err(-1, NULL); addr_set_port(&real_local_addr, response_port); if (addr_bind(new_udp_fd, &real_local_addr, 0) < 0) err(-1, NULL); } turn::StunMsgRequest req(STUN_METHOD_BINDING); req.constructBindingRequest(); if (response_port >= 0) { turn::StunAttrResponsePort rpa; rpa.setResponsePort((u16bits)response_port); try { req.addAttr(rpa); } catch(turn::WrongStunAttrFormatException &ex1) { printf("Wrong rp attr format\n"); exit(-1); } catch(turn::WrongStunBufferFormatException &ex2) { printf("Wrong stun buffer format (1)\n"); exit(-1); } catch(...) { printf("Wrong something (1)\n"); exit(-1); } } if (change_ip || change_port) { turn::StunAttrChangeRequest cra; cra.setChangeIp(change_ip); cra.setChangePort(change_port); try { req.addAttr(cra); } catch(turn::WrongStunAttrFormatException &ex1) { printf("Wrong cr attr format\n"); exit(-1); } catch(turn::WrongStunBufferFormatException &ex2) { printf("Wrong stun buffer format (2)\n"); exit(-1); } catch(...) { printf("Wrong something (2)\n"); exit(-1); } } if (padding) { turn::StunAttrPadding pa; pa.setPadding(1500); try { req.addAttr(pa); } catch(turn::WrongStunAttrFormatException &ex1) { printf("Wrong p attr format\n"); exit(-1); } catch(turn::WrongStunBufferFormatException &ex2) { printf("Wrong stun buffer format (3)\n"); exit(-1); } catch(...) { printf("Wrong something (3)\n"); exit(-1); } } { int len = 0; int slen = get_ioa_addr_len(&remote_addr); do { len = sendto(udp_fd, req.getRawBuffer(), req.getSize(), 0, (struct sockaddr*) &remote_addr, (socklen_t) slen); } while (len < 0 && ((errno == EINTR) || (errno == ENOBUFS) || (errno == EAGAIN))); if (len < 0) err(-1, NULL); } if (addr_get_from_sock(udp_fd, &real_local_addr) < 0) { printf("%s: Cannot get address from local socket\n", __FUNCTION__); } else { *port = addr_get_port(&real_local_addr); } { if(new_udp_fd >= 0) { close(udp_fd); udp_fd = new_udp_fd; new_udp_fd = -1; } } { int len = 0; stun_buffer buf; u08bits *ptr = buf.buf; int recvd = 0; const int to_recv = sizeof(buf.buf); do { len = recv(udp_fd, ptr, to_recv - recvd, 0); if (len > 0) { recvd += len; ptr += len; break; } } while (len < 0 && (errno == EINTR)); if (recvd > 0) len = recvd; buf.len = len; try { turn::StunMsgResponse res(buf.buf, sizeof(buf.buf), (size_t)buf.len, true); if (res.isCommand()) { if(res.isSuccess()) { if (res.isBindingResponse()) { ioa_addr reflexive_addr; addr_set_any(&reflexive_addr); turn::StunAttrIterator iter(res,STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS); if (!iter.eof()) { turn::StunAttrAddr addr(iter); addr.getAddr(reflexive_addr); turn::StunAttrIterator iter1(res,STUN_ATTRIBUTE_OTHER_ADDRESS); if (!iter1.eof()) { *rfc5780 = 1; printf("\n========================================\n"); printf("RFC 5780 response %d\n",++counter); ioa_addr other_addr; turn::StunAttrAddr addr1(iter1); addr1.getAddr(other_addr); turn::StunAttrIterator iter2(res,STUN_ATTRIBUTE_RESPONSE_ORIGIN); if (!iter2.eof()) { ioa_addr response_origin; turn::StunAttrAddr addr2(iter2); addr2.getAddr(response_origin); addr_debug_print(1, &response_origin, "Response origin: "); } addr_debug_print(1, &other_addr, "Other addr: "); } addr_debug_print(1, &reflexive_addr, "UDP reflexive addr"); } else { printf("Cannot read the response\n"); } } else { printf("Wrong type of response\n"); } } else { int err_code = res.getError(); std::string reason = res.getReason(); printf("The response is an error %d (%s)\n", err_code, reason.c_str()); } } else { printf("The response is not a reponse message\n"); } } catch(...) { printf("The response is not a well formed STUN message\n"); } } return 0; } #else static int run_stunclient(const char* rip, int rport, int *port, int *rfc5780, int response_port, int change_ip, int change_port, int padding) { ioa_addr remote_addr; int new_udp_fd = -1; stun_buffer buf; ns_bzero(&remote_addr, sizeof(remote_addr)); if (make_ioa_addr((const u08bits*) rip, rport, &remote_addr) < 0) err(-1, NULL); if (udp_fd < 0) { udp_fd = socket(remote_addr.ss.sa_family, SOCK_DGRAM, 0); if (udp_fd < 0) err(-1, NULL); if (!addr_any(&real_local_addr)) { if (addr_bind(udp_fd, &real_local_addr,0) < 0) err(-1, NULL); } } if (response_port >= 0) { new_udp_fd = socket(remote_addr.ss.sa_family, SOCK_DGRAM, 0); if (new_udp_fd < 0) err(-1, NULL); addr_set_port(&real_local_addr, response_port); if (addr_bind(new_udp_fd, &real_local_addr,0) < 0) err(-1, NULL); } stun_prepare_binding_request(&buf); if (response_port >= 0) { stun_attr_add_response_port_str((u08bits*) (buf.buf), (size_t*) &(buf.len), (u16bits) response_port); } if (change_ip || change_port) { stun_attr_add_change_request_str((u08bits*) buf.buf, (size_t*) &(buf.len), change_ip, change_port); } if (padding) { if(stun_attr_add_padding_str((u08bits*) buf.buf, (size_t*) &(buf.len), 1500)<0) { printf("%s: ERROR: Cannot add padding\n",__FUNCTION__); } } { int len = 0; int slen = get_ioa_addr_len(&remote_addr); do { len = sendto(udp_fd, buf.buf, buf.len, 0, (struct sockaddr*) &remote_addr, (socklen_t) slen); } while (len < 0 && ((errno == EINTR) || (errno == ENOBUFS) || (errno == EAGAIN))); if (len < 0) err(-1, NULL); } if (addr_get_from_sock(udp_fd, &real_local_addr) < 0) { printf("%s: Cannot get address from local socket\n", __FUNCTION__); } else { *port = addr_get_port(&real_local_addr); } { if(new_udp_fd >= 0) { socket_closesocket(udp_fd); udp_fd = new_udp_fd; new_udp_fd = -1; } } { int len = 0; u08bits *ptr = buf.buf; int recvd = 0; const int to_recv = sizeof(buf.buf); do { len = recv(udp_fd, ptr, to_recv - recvd, 0); if (len > 0) { recvd += len; ptr += len; break; } } while (len < 0 && ((errno == EINTR) || (errno == EAGAIN))); if (recvd > 0) len = recvd; buf.len = len; if (stun_is_command_message(&buf)) { if (stun_is_response(&buf)) { if (stun_is_success_response(&buf)) { if (stun_is_binding_response(&buf)) { ioa_addr reflexive_addr; addr_set_any(&reflexive_addr); if (stun_attr_get_first_addr(&buf, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &reflexive_addr, NULL) >= 0) { stun_attr_ref sar = stun_attr_get_first_by_type_str(buf.buf, buf.len, STUN_ATTRIBUTE_OTHER_ADDRESS); if (sar) { *rfc5780 = 1; printf("\n========================================\n"); printf("RFC 5780 response %d\n",++counter); ioa_addr other_addr; stun_attr_get_addr_str((u08bits *) buf.buf, (size_t) buf.len, sar, &other_addr, NULL); sar = stun_attr_get_first_by_type_str(buf.buf, buf.len, STUN_ATTRIBUTE_RESPONSE_ORIGIN); if (sar) { ioa_addr response_origin; stun_attr_get_addr_str((u08bits *) buf.buf, (size_t) buf.len, sar, &response_origin, NULL); addr_debug_print(1, &response_origin, "Response origin: "); } addr_debug_print(1, &other_addr, "Other addr: "); } addr_debug_print(1, &reflexive_addr, "UDP reflexive addr"); } else { printf("Cannot read the response\n"); } } else { printf("Wrong type of response\n"); } } else { int err_code = 0; u08bits err_msg[1025] = "\0"; size_t err_msg_size = sizeof(err_msg); if (stun_is_error_response(&buf, &err_code, err_msg, err_msg_size)) { printf("The response is an error %d (%s)\n", err_code, (char*) err_msg); } else { printf("The response is an unrecognized error\n"); } } } else { printf("The response is not a reponse message\n"); } } else { printf("The response is not a STUN message\n"); } } return 0; } #endif //////////////// local definitions ///////////////// static char Usage[] = "Usage: stunclient [options] address\n" "Options:\n" " -p STUN server port (Default: 3478)\n" " -L Local address to use (optional)\n" " -f Force RFC 5780 processing\n"; ////////////////////////////////////////////////// int main(int argc, char **argv) { int port = DEFAULT_STUN_PORT; char local_addr[256]="\0"; int c=0; int forceRfc5780 = 0; set_logfile("stdout"); set_system_parameters(0); ns_bzero(local_addr, sizeof(local_addr)); while ((c = getopt(argc, argv, "p:L:f")) != -1) { switch(c) { case 'f': forceRfc5780 = 1; break; case 'p': port = atoi(optarg); break; case 'L': STRCPY(local_addr, optarg); break; default: fprintf(stderr,"%s\n", Usage); exit(1); } } if(optind>=argc) { fprintf(stderr, "%s\n", Usage); exit(-1); } addr_set_any(&real_local_addr); if(local_addr[0]) { if(make_ioa_addr((const u08bits*)local_addr, 0, &real_local_addr)<0) { err(-1,NULL); } } int local_port = -1; int rfc5780 = 0; run_stunclient(argv[optind], port, &local_port, &rfc5780,-1,0,0,0); if(rfc5780 || forceRfc5780) { run_stunclient(argv[optind], port, &local_port, &rfc5780,local_port+1,1,1,0); run_stunclient(argv[optind], port, &local_port, &rfc5780,-1,1,1,1); } socket_closesocket(udp_fd); return 0; } turnserver-3.2.3.1/src/apps/common/ns_turn_utils.h000644 001751 001751 00000005250 12315706777 022141 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __TURN_ULIB__ #define __TURN_ULIB__ #if !defined(TURN_LOG_FUNC) #define TURN_LOG_FUNC turn_log_func_default #endif #include "ns_turn_ioaddr.h" #ifdef __cplusplus extern "C" { #endif //////////////////////// LOG ////////////////////////// typedef enum { TURN_LOG_LEVEL_INFO = 0, TURN_LOG_LEVEL_CONTROL, TURN_LOG_LEVEL_WARNING, TURN_LOG_LEVEL_ERROR } TURN_LOG_LEVEL; #define TURN_VERBOSE_NONE (0) #define TURN_VERBOSE_NORMAL (1) #define TURN_VERBOSE_EXTRA (2) #define eve(v) ((v)==TURN_VERBOSE_EXTRA) void set_no_stdout_log(int val); void set_log_to_syslog(int val); void turn_log_func_default(TURN_LOG_LEVEL level, const s08bits* format, ...); void addr_debug_print(int verbose, const ioa_addr *addr, const s08bits* s); /* Log */ extern volatile int _log_time_value_set; extern volatile turn_time_t _log_time_value; void rtpprintf(const char *format, ...); int vrtpprintf(TURN_LOG_LEVEL level, const char *format, va_list args); void reset_rtpprintf(void); void set_logfile(const char *fn); void rollover_logfile(void); /////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_ULIB__ turnserver-3.2.3.1/src/apps/common/apputils.c000644 001751 001751 00000053675 12315706777 021103 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "ns_turn_utils.h" #include "ns_turn_msg.h" #include "apputils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /************************/ int IS_TURN_SERVER = 0; /*********************** Sockets *********************************/ int socket_set_nonblocking(evutil_socket_t fd) { #if defined(WIN32) unsigned long nonblocking = 1; ioctlsocket(fd, FIONBIO, (unsigned long*) &nonblocking); #else if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { perror("O_NONBLOCK"); return -1; } #endif return 0; } void read_spare_buffer(evutil_socket_t fd) { if(fd >= 0) { static char buffer[65536]; recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT); } } int set_sock_buf_size(evutil_socket_t fd, int sz0) { int sz; sz = sz0; while (sz > 0) { if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const void*) (&sz), (socklen_t) sizeof(sz)) < 0) { sz = sz / 2; } else { break; } } if (sz < 1) { perror("Cannot set socket rcv size"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Cannot set rcv sock size %d on fd %d\n", sz0, fd); } sz = sz0; while (sz > 0) { if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void*) (&sz), (socklen_t) sizeof(sz)) < 0) { sz = sz / 2; } else { break; } } if (sz < 1) { perror("Cannot set socket snd size"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Cannot set snd sock size %d on fd %d\n", sz0, fd); } return 0; } int socket_tcp_set_keepalive(evutil_socket_t fd) { #ifdef SO_KEEPALIVE /* Set the keepalive option active */ { int on = 1; setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (const void*)&on, (socklen_t) sizeof(on)); } #else UNUSED_ARG(fd); #endif #ifdef SO_NOSIGPIPE { int on = 1; setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (const void*)&on, (socklen_t) sizeof(on)); } #endif return 0; } int socket_set_reusable(evutil_socket_t fd, int flag) { if (fd < 0) return -1; else { #if defined(WIN32) int use_reuseaddr = IS_TURN_SERVER; #else int use_reuseaddr = 1; #endif #if defined(SO_REUSEPORT) if (use_reuseaddr) { int on = flag; setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (const void*) &on, (socklen_t) sizeof(on)); } #endif #if defined(SO_REUSEADDR) if (use_reuseaddr) { int on = flag; int ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void*) &on, (socklen_t) sizeof(on)); if (ret < 0) perror("SO_REUSEADDR"); } #endif return 0; } } int sock_bind_to_device(evutil_socket_t fd, const unsigned char* ifname) { if (fd >= 0 && ifname && ifname[0]) { #if defined(SO_BINDTODEVICE) struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, (const char*) ifname, sizeof(ifr.ifr_name)); if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *) &ifr, sizeof(ifr)) < 0) { if (errno == EPERM) perror("You must obtain superuser privileges to bind a socket to device"); else perror("Cannot bind socket to device"); return -1; } return 0; #endif } return 0; } int addr_connect(evutil_socket_t fd, const ioa_addr* addr, int *out_errno) { if (!addr || fd < 0) return -1; else { int err = 0; do { if (addr->ss.sa_family == AF_INET) { err = connect(fd, (const struct sockaddr *) addr, sizeof(struct sockaddr_in)); } else if (addr->ss.sa_family == AF_INET6) { err = connect(fd, (const struct sockaddr *) addr, sizeof(struct sockaddr_in6)); } else { return -1; } } while (err < 0 && errno == EINTR); if(out_errno) *out_errno = errno; if (err < 0 && errno != EINPROGRESS) perror("Connect"); return err; } } int addr_bind(evutil_socket_t fd, const ioa_addr* addr, int reusable) { if (!addr || fd < 0) { return -1; } else { int ret = -1; socket_set_reusable(fd, reusable); if (addr->ss.sa_family == AF_INET) { do { ret = bind(fd, (const struct sockaddr *) addr, sizeof(struct sockaddr_in)); } while (ret < 0 && errno == EINTR); } else if (addr->ss.sa_family == AF_INET6) { const int off = 0; setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const char *) &off, sizeof(off)); do { ret = bind(fd, (const struct sockaddr *) addr, sizeof(struct sockaddr_in6)); } while (ret < 0 && errno == EINTR); } else { return -1; } if(ret<0) { int err = errno; perror("bind"); char str[129]; addr_to_string(addr,(u08bits*)str); TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Trying to bind fd %d to <%s>: errno=%d\n", fd, str, err); } return ret; } } int addr_get_from_sock(evutil_socket_t fd, ioa_addr *addr) { if (fd < 0 || !addr) return -1; else { ioa_addr a; a.ss.sa_family = AF_INET6; socklen_t socklen = get_ioa_addr_len(&a); if (getsockname(fd, (struct sockaddr*) &a, &socklen) < 0) { a.ss.sa_family = AF_INET; socklen = get_ioa_addr_len(&a); if (getsockname(fd, (struct sockaddr*) &a, &socklen) < 0) { return -1; } } addr_cpy(addr, &a); return 0; } } /////////////////// MTU ///////////////////////////////////////// int set_socket_df(evutil_socket_t fd, int family, int value) { int ret=0; #if defined(IP_DONTFRAG) && defined(IPPROTO_IP) //BSD { const int val=value; /* kernel sets DF bit on outgoing IP packets */ if(family==AF_INET) { ret = setsockopt(fd, IPPROTO_IP, IP_DONTFRAG, &val, sizeof(val)); } else { #if defined(IPV6_DONTFRAG) && defined(IPPROTO_IPV6) ret = setsockopt(fd, IPPROTO_IPV6, IPV6_DONTFRAG, &val, sizeof(val)); #else #error CANNOT SET IPV6 SOCKET DF FLAG (1) #endif } if(ret<0) { int err=errno; perror("set socket df:"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: set sockopt failed: fd=%d, err=%d, family=%d\n",__FUNCTION__,fd,err,family); } } #elif defined(IPPROTO_IP) && defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO) && defined(IP_PMTUDISC_DONT) //LINUX { /* kernel sets DF bit on outgoing IP packets */ if(family==AF_INET) { int val=IP_PMTUDISC_DO; if(!value) val=IP_PMTUDISC_DONT; ret = setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)); } else { #if defined(IPPROTO_IPV6) && defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO) && defined(IPV6_PMTUDISC_DONT) int val=IPV6_PMTUDISC_DO; if(!value) val=IPV6_PMTUDISC_DONT; ret = setsockopt(fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val)); #else #error CANNOT SET IPV6 SOCKET DF FLAG (2) #endif } if(ret<0) { perror("set DF"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: set sockopt failed\n",__FUNCTION__); } } #else //CANNOT SET SOCKET DF FLAG (3) : UNKNOWN PLATFORM UNUSED_ARG(fd); UNUSED_ARG(family); UNUSED_ARG(value); #endif return ret; } static int get_mtu_from_ssl(SSL* ssl) { int ret = SOSO_MTU; #if !defined(TURN_NO_DTLS) if(ssl) ret = BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL); #else UNUSED_ARG(ssl); #endif return ret; } static void set_query_mtu(SSL* ssl) { if(ssl) { #if defined(SSL_OP_NO_QUERY_MTU) SSL_set_options(ssl, SSL_OP_NO_QUERY_MTU); #else ; #endif } } int decrease_mtu(SSL* ssl, int mtu, int verbose) { if (!ssl) return mtu; int new_mtu = get_mtu_from_ssl(ssl); if (new_mtu < 1) new_mtu = mtu; if (new_mtu > MAX_MTU) mtu = MAX_MTU; if (new_mtu > 0 && new_mtu < MIN_MTU) mtu = MIN_MTU; else if (new_mtu < mtu) mtu = new_mtu; else mtu -= MTU_STEP; if (mtu < MIN_MTU) mtu = MIN_MTU; set_query_mtu(ssl); if (verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "1. mtu to use: %d\n", mtu); #if !defined(TURN_NO_DTLS) SSL_set_mtu(ssl,mtu); BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_SET_MTU, mtu, NULL); #endif return mtu; } int set_mtu_df(SSL* ssl, evutil_socket_t fd, int family, int mtu, int df_value, int verbose) { if(!ssl || fd<0) return 0; int ret=set_socket_df(fd, family, df_value); if(!mtu) mtu=SOSO_MTU; else if(mtuMAX_MTU) mtu=MAX_MTU; set_query_mtu(ssl); if(verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"3. mtu to use: %d\n",mtu); #if !defined(TURN_NO_DTLS) SSL_set_mtu(ssl,mtu); BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_SET_MTU, mtu, NULL); #endif if(verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"4. new mtu: %d\n",get_mtu_from_ssl(ssl)); return ret; } int get_socket_mtu(evutil_socket_t fd, int family, int verbose) { int ret = 0; UNUSED_ARG(fd); UNUSED_ARG(family); UNUSED_ARG(verbose); #if defined(IP_MTU) int val = 0; socklen_t slen=sizeof(val); if(family==AF_INET) { ret = getsockopt(fd, IPPROTO_IP, IP_MTU, &val, &slen); } else { #if defined(IPPROTO_IPV6) && defined(IPV6_MTU) ret = getsockopt(fd, IPPROTO_IPV6, IPV6_MTU, &val, &slen); #endif ; } ret = val; #endif if (verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: final=%d\n", __FUNCTION__, ret); return ret; } //////////////////// socket error handle //////////////////// int handle_socket_error() { switch (errno) { case EINTR: /* Interrupted system call. * Just ignore. */ return 1; case ENOBUFS: /* No buffers, temporary condition. * Just ignore and try later. */ return 1; case EAGAIN: #if defined(EWOULDBLOCK) #if (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif #endif return 1; case EMSGSIZE: return 1; case EBADF: /* Invalid socket. * Must close connection. */ return 0; case EHOSTDOWN: /* Host is down. * Just ignore, might be an attacker * sending fake ICMP messages. */ return 1; case ECONNRESET: case ECONNREFUSED: /* Connection reset by peer. */ return 0; case ENOMEM: /* Out of memory. * Must close connection. */ TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Out of memory!\n"); return 0; case EACCES: /* Permission denied. * Just ignore, we might be blocked * by some firewall policy. Try again * and hope for the best. */ return 1; default: /* Something unexpected happened */ TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Unexpected error! (errno = %d)\n", errno); return 0; } } //////////////////// Misc utils ////////////////////////////// char *skip_blanks(char* s) { while(*s==' ' || *s=='\t' || *s=='\n') ++s; return s; } //////////////////// Config file search ////////////////////// #define Q(x) #x #define QUOTE(x) Q(x) #define ETCDIR INSTALL_PREFIX/etc/ #define QETCDIR QUOTE(ETCDIR) #define ETCDIR1 INSTALL_PREFIX/etc/turnserver/ #define QETCDIR1 QUOTE(ETCDIR1) #define ETCDIR2 INSTALL_PREFIX/etc/rfc5766-turn-server/ #define QETCDIR2 QUOTE(ETCDIR2) static const char *config_file_search_dirs[] = {"./", "./turnserver/", "./rfc5766-turn-server/", "./etc/", "./etc/turnserver/", "./etc/rfc5766-turn-server/", "../etc/", "../etc/turnserver/", "../etc/rfc5766-turn-server/", "/etc/", "/etc/turnserver/", "/etc/rfc5766-turn-server/", "/usr/local/etc/", "/usr/local/etc/turnserver/", "/usr/local/etc/rfc5766-turn-server/", QETCDIR, QETCDIR1, QETCDIR2, NULL }; static char *c_execdir=NULL; void set_execdir(void) { /* On some systems, this may give us the execution path */ char *_var = getenv("_"); if(_var && *_var) { _var = strdup(_var); char *edir=_var; if(edir[0]!='.') edir = strstr(edir,"/"); if(edir && *edir) edir = dirname(edir); else edir = dirname(_var); if(c_execdir) turn_free(c_execdir,strlen(c_execdir)+1); c_execdir = strdup(edir); turn_free(_var,strlen(_var)+1); } } void print_abs_file_name(const char *msg1, const char *msg2, const char *fn) { char absfn[1025]; absfn[0]=0; if(fn) { while(fn[0] && fn[0]==' ') ++fn; if(fn[0]) { if(fn[0]=='/') { STRCPY(absfn,fn); } else { if(fn[0]=='.' && fn[1]=='/') fn+=2; if(!getcwd(absfn,sizeof(absfn)-1)) absfn[0]=0; size_t blen=strlen(absfn); if(blen0)) { rlim.rlim_cur = rlim.rlim_cur>>1; } return (unsigned long)rlim.rlim_cur; } } return 0; } ////////////////////// Base 64 //////////////////////////// static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; static char *decoding_table = NULL; static size_t mod_table[] = {0, 2, 1}; char *base64_encode(const unsigned char *data, size_t input_length, size_t *output_length) { *output_length = 4 * ((input_length + 2) / 3); char *encoded_data = (char*)turn_malloc(*output_length+1); if (encoded_data == NULL) return NULL; size_t i,j; for (i = 0, j = 0; i < input_length;) { u32bits octet_a = i < input_length ? data[i++] : 0; u32bits octet_b = i < input_length ? data[i++] : 0; u32bits octet_c = i < input_length ? data[i++] : 0; u32bits triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; } for (i = 0; i < mod_table[input_length % 3]; i++) encoded_data[*output_length - 1 - i] = '='; encoded_data[*output_length]=0; return encoded_data; } void build_base64_decoding_table() { decoding_table = (char*)turn_malloc(256); ns_bzero(decoding_table,256); int i; for (i = 0; i < 64; i++) decoding_table[(unsigned char) encoding_table[i]] = (char)i; } unsigned char *base64_decode(const char *data, size_t input_length, size_t *output_length) { if (decoding_table == NULL) build_base64_decoding_table(); if (input_length % 4 != 0) return NULL; *output_length = input_length / 4 * 3; if (data[input_length - 1] == '=') (*output_length)--; if (data[input_length - 2] == '=') (*output_length)--; unsigned char *decoded_data = (unsigned char*)turn_malloc(*output_length); if (decoded_data == NULL) return NULL; int i; size_t j; for (i = 0, j = 0; i < (int)input_length;) { uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]]; uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]]; uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]]; uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]]; uint32_t triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6); if (j < *output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF; if (j < *output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF; if (j < *output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF; } return decoded_data; } ////////////////// SSL ///////////////////// static const char* turn_get_method(const SSL_METHOD *method, const char* mdefault) { { if(!method) return mdefault; else { #ifndef OPENSSL_NO_SSL2 if(method == SSLv2_server_method()) { return "SSLv2"; } else if(method == SSLv2_client_method()) { return "SSLv2"; } else #endif if(method == SSLv3_server_method()) { return "SSLv3"; } else if(method == SSLv3_client_method()) { return "SSLv3"; } else if(method == SSLv23_server_method()) { return "SSLv23"; } else if(method == SSLv23_client_method()) { return "SSLv23"; } else if(method == TLSv1_server_method()) { return "TLSv1.0"; } else if(method == TLSv1_client_method()) { return "TLSv1.0"; #if defined(SSL_TXT_TLSV1_1) } else if(method == TLSv1_1_server_method()) { return "TLSv1.1"; } else if(method == TLSv1_1_client_method()) { return "TLSv1.1"; #if defined(SSL_TXT_TLSV1_2) } else if(method == TLSv1_2_server_method()) { return "TLSv1.2"; } else if(method == TLSv1_2_client_method()) { return "TLSv1.2"; #endif #endif #if !defined(TURN_NO_DTLS) } else if(method == DTLSv1_server_method()) { return "DTLSv1.0"; } else if(method == DTLSv1_client_method()) { return "DTLSv1.0"; #endif } else { if(mdefault) return mdefault; return "UNKNOWN"; } } } } const char* turn_get_ssl_method(SSL *ssl, const char* mdefault) { if(!ssl) return mdefault; else { const SSL_METHOD *method = SSL_get_ssl_method(ssl); if(!method) return mdefault; else return turn_get_method(method, mdefault); } } //////////// EVENT BASE /////////////// struct event_base *turn_event_base_new(void) { struct event_config *cfg = event_config_new(); event_config_set_flag(cfg,EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST); return event_base_new_with_config(cfg); } ////////////////////////////////////////////////////////////// turnserver-3.2.3.1/src/apps/common/hiredis_libevent2.c000644 001751 001751 00000020446 12315706777 022631 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include #include #if !defined(TURN_NO_HIREDIS) #include "hiredis_libevent2.h" #include "ns_turn_utils.h" #include #include #include #include //////////////// Libevent context /////////////////////// struct redisLibeventEvents { redisAsyncContext *context; int allocated; struct event_base *base; struct event *rev, *wev; int rev_set, wev_set; struct bufferevent *in_buf; struct bufferevent *out_buf; }; static redisAsyncContext *defaultAsyncContext = NULL; ///////////// Messages //////////////////////////// struct redis_message { char format[513]; char arg[513]; }; /////////////////// Callbacks //////////////////////////// static void redisLibeventReadEvent(int fd, short event, void *arg) { ((void)fd); ((void)event); struct redisLibeventEvents *e = (struct redisLibeventEvents*)arg; if(e) { redisAsyncHandleRead(e->context); } } static void redisLibeventWriteEvent(int fd, short event, void *arg) { ((void)fd); ((void)event); struct redisLibeventEvents *e = (struct redisLibeventEvents*)arg; if(e) { redisAsyncHandleWrite(e->context); } } static void redisLibeventAddRead(void *privdata) { struct redisLibeventEvents *e = (struct redisLibeventEvents*)privdata; if(e && (e->rev)) { event_add(e->rev,NULL); e->rev_set = 1; } } static void redisLibeventDelRead(void *privdata) { struct redisLibeventEvents *e = (struct redisLibeventEvents*)privdata; if(e && e->rev) { event_del(e->rev); e->rev_set = 0; } } static void redisLibeventAddWrite(void *privdata) { struct redisLibeventEvents *e = (struct redisLibeventEvents*)privdata; if(e && (e->wev)) { event_add(e->wev,NULL); e->wev_set = 1; } } static void redisLibeventDelWrite(void *privdata) { struct redisLibeventEvents *e = (struct redisLibeventEvents*)privdata; if(e && e->wev) { event_del(e->wev); e->wev_set = 0; } } static void redisLibeventCleanup(void *privdata) { if (privdata) { struct redisLibeventEvents *e = (struct redisLibeventEvents *) privdata; if (e->allocated) { e->allocated = 0; if (e->rev) { if(e->rev_set) event_del(e->rev); event_free(e->rev); e->rev = NULL; } if (e->wev) { if(e->wev_set) event_del(e->wev); event_free(e->wev); e->wev = NULL; } turn_free(privdata, sizeof(struct redisLibeventEvents)); } } } ///////////////////////// Send-receive /////////////////////////// static void send_message_for_redis(redisAsyncContext *ac, const struct redis_message *rm) { if(!ac) return; struct redisLibeventEvents *e = (struct redisLibeventEvents *)(ac->ev.data); if(e && rm) { struct evbuffer *output = bufferevent_get_output(e->out_buf); if(evbuffer_add(output,rm,sizeof(*rm))<0) { fprintf(stderr,"%s: Weird buffer error\n",__FUNCTION__); } } } static void receive_message_for_redis(struct bufferevent *bev, void *ptr) { if(!ptr) return; struct redisLibeventEvents *e = (struct redisLibeventEvents*)ptr; redisAsyncContext *ac = e->context; struct redis_message rm; int n = 0; struct evbuffer *input = bufferevent_get_input(bev); while ((n = evbuffer_remove(input, &rm, sizeof(rm))) > 0) { if (n != sizeof(rm)) { fprintf(stderr,"%s: Weird buffer error: size=%d\n",__FUNCTION__,n); continue; } if(ac) { redisAsyncCommand(ac, NULL, e, rm.format, rm.arg); } } } void send_message_to_redis(redis_context_handle rch, const char *command, const char *key, const char *format,...) { redisAsyncContext *ac=(redisAsyncContext *)rch; if(!ac) ac = defaultAsyncContext; if(ac) { struct redis_message rm; snprintf(rm.format,sizeof(rm.format)-3,"%s %s ", command, key); strcpy(rm.format+strlen(rm.format),"%s"); va_list args; va_start (args, format); vsnprintf(rm.arg, sizeof(rm.arg)-1, format, args); va_end (args); send_message_for_redis(ac, &rm); } } static void deleteKeysCallback(redisAsyncContext *c, void *reply0, void *privdata) { redisReply *reply = (redisReply*) reply0; if (reply) { if (reply->type == REDIS_REPLY_ERROR) printf("Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) printf("Unexpected type: %d\n", reply->type); else { size_t i; for (i = 0; i < reply->elements; ++i) { redisAsyncCommand(c, NULL, privdata, "del %s", reply->element[i]->str); } } } } void delete_redis_keys(const char *key_pattern) { redisAsyncContext *ac = defaultAsyncContext; if(ac) { redisAsyncCommand(ac, deleteKeysCallback, ac->ev.data, "keys %s", key_pattern); } } void set_default_async_context(redis_context_handle rch) { defaultAsyncContext = (redisAsyncContext*)rch; } int default_async_context_is_not_empty(void) { return (defaultAsyncContext != NULL); } ///////////////////////// Attach ///////////////////////////////// redis_context_handle redisLibeventAttach(struct event_base *base, char *ip0, int port0, char *pwd, int db) { struct redisLibeventEvents *e = NULL; redisAsyncContext *ac = NULL; char ip[256]; if(ip0 && ip0[0]) STRCPY(ip,ip0); else STRCPY(ip,"127.0.0.1"); int port = DEFAULT_REDIS_PORT; if(port0>0) port=port0; ac = redisAsyncConnect(ip, port); if (ac->err) { fprintf(stderr,"Error: %s\n", ac->errstr); return NULL; } /* Create container for context and r/w events */ e = (struct redisLibeventEvents*)turn_malloc(sizeof(struct redisLibeventEvents)); ns_bzero(e,sizeof(struct redisLibeventEvents)); e->allocated = 1; e->context = ac; e->base = base; /* Register functions to start/stop listening for events */ ac->ev.addRead = redisLibeventAddRead; ac->ev.delRead = redisLibeventDelRead; ac->ev.addWrite = redisLibeventAddWrite; ac->ev.delWrite = redisLibeventDelWrite; ac->ev.cleanup = redisLibeventCleanup; ac->ev.data = e; /* Initialize and install read/write events */ e->rev = event_new(e->base,e->context->c.fd, EV_READ,redisLibeventReadEvent, e); e->wev = event_new(e->base,e->context->c.fd, EV_WRITE,redisLibeventWriteEvent, e); if (e->rev == NULL || e->wev == NULL) { turn_free(e, sizeof(struct redisLibeventEvents)); return NULL; } event_add(e->wev, NULL); e->wev_set = 1; struct bufferevent *pair[2]; int opts = BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS; opts |= BEV_OPT_THREADSAFE; bufferevent_pair_new(base, opts, pair); e->in_buf = pair[0]; e->out_buf = pair[1]; bufferevent_setcb(e->in_buf, receive_message_for_redis, NULL, NULL, e); bufferevent_enable(e->in_buf, EV_READ); //Authentication if(pwd) redisAsyncCommand(ac, NULL, e, "AUTH %s", pwd); if(db>0) redisAsyncCommand(ac, NULL, e, "SELECT %d", db); return ac; } ///////////////////////////////////////////////////////// #endif /* TURN_NO_HIREDIS */ turnserver-3.2.3.1/src/apps/common/stun_buffer.h000644 001751 001751 00000013044 12315706777 021553 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __TURN_STUN_BUF__ #define __TURN_STUN_BUF__ #include "ns_turn_msg.h" #ifdef __cplusplus extern "C" { #endif /////////////////////////////////////////////////////////////// typedef struct _stun_buffer { u08bits channel[STUN_CHANNEL_HEADER_LENGTH]; u08bits buf[STUN_BUFFER_SIZE]; size_t len; u16bits offset; u08bits coffset; } stun_buffer; ////////////////////////////////////////////////////////////// int stun_init_buffer(stun_buffer *buf); int stun_get_size(const stun_buffer *buf); ////////////////////////////////////////////////////////////// void stun_tid_generate_in_message(stun_buffer* buf, stun_tid* id); void stun_tid_from_message(const stun_buffer *buf, stun_tid* id); /////////////////////////////////////////////////////////////// int stun_is_command_message(const stun_buffer* buf); int stun_is_request(const stun_buffer* buf); int stun_is_response(const stun_buffer* buf); int stun_is_success_response(const stun_buffer* buf); int stun_is_error_response(const stun_buffer* buf, int *err_code, u08bits *err_msg, size_t err_msg_size); int stun_is_indication(const stun_buffer* buf); u16bits stun_get_method(const stun_buffer* buf); u16bits stun_get_msg_type(const stun_buffer* buf); /////////////////////////////////////////////////////////////// void stun_init_request(u16bits method, stun_buffer* buf); void stun_init_indication(u16bits method, stun_buffer* buf); void stun_init_success_response(u16bits method, stun_buffer* buf, stun_tid* id); void stun_init_error_response(u16bits method, stun_buffer* buf, u16bits error_code, const u08bits *reason, stun_tid* id); /////////////////////////////////////////////////////////////// int stun_attr_add(stun_buffer* buf, u16bits attr, const s08bits* avalue, int alen); int stun_attr_add_channel_number(stun_buffer* buf, u16bits chnumber); int stun_attr_add_addr(stun_buffer *buf,u16bits attr_type, const ioa_addr* ca); stun_attr_ref stun_attr_get_first(const stun_buffer* buf); stun_attr_ref stun_attr_get_first_by_type(const stun_buffer* buf, u16bits attr_type); stun_attr_ref stun_attr_get_next(const stun_buffer* buf, stun_attr_ref prev); int stun_attr_get_addr(const stun_buffer *buf, stun_attr_ref attr, ioa_addr* ca, const ioa_addr *default_addr); int stun_attr_add_even_port(stun_buffer* buf, uint8_t value); int stun_attr_get_first_addr(const stun_buffer *buf, u16bits attr_type, ioa_addr* ca, const ioa_addr *default_addr); u16bits stun_attr_get_first_channel_number(const stun_buffer *buf); /////////////////////////////////////////////////////////////// int stun_get_command_message_len(const stun_buffer* buf); /////////////////////////////////////////////////////////////// int stun_init_channel_message(u16bits chnumber, stun_buffer* buf, int length, int do_padding); int stun_is_channel_message(stun_buffer* buf, u16bits* chnumber, int is_padding_madatory); /////////////////////////////////////////////////////////////// int stun_set_allocate_request(stun_buffer* buf, u32bits lifetime, int address_family, u08bits transport, int mobile); int stun_set_allocate_response(stun_buffer* buf, stun_tid* tid, const ioa_addr *relayed_addr, const ioa_addr *reflexive_addr, u32bits lifetime, int error_code, const u08bits *reason, u64bits reservation_token, char *mobile_id); /////////////////////////////////////////////////////////////// void stun_set_binding_request(stun_buffer* buf); int stun_set_binding_response(stun_buffer* buf, stun_tid* tid, const ioa_addr *reflexive_addr, int error_code, const u08bits *reason); void stun_prepare_binding_request(stun_buffer* buf); int stun_is_binding_response(const stun_buffer* buf); /////////////////////////////////////////////////////////////// u16bits stun_set_channel_bind_request(stun_buffer* buf, const ioa_addr* peer_addr, u16bits channel_number); void stun_set_channel_bind_response(stun_buffer* buf, stun_tid* tid, int error_code, const u08bits *reason); /////////////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_STUN_BUF__ turnserver-3.2.3.1/src/apps/common/ns_turn_utils.c000644 001751 001751 00000026205 12315706777 022137 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "ns_turn_utils.h" #include "ns_turn_ioalib.h" #include #include #include #include #include #include ////////// LOG TIME OPTIMIZATION /////////// static volatile turn_time_t log_start_time = 0; volatile int _log_time_value_set = 0; volatile turn_time_t _log_time_value = 0; static inline turn_time_t log_time(void) { if(!log_start_time) log_start_time = turn_time(); if(_log_time_value_set) return (_log_time_value - log_start_time); return (turn_time() - log_start_time); } ////////// MUTEXES ///////////// #define MAGIC_CODE (0xEFCD1983) int turn_mutex_lock(const turn_mutex *mutex) { if(mutex && mutex->mutex && (mutex->data == MAGIC_CODE)) { int ret = 0; ret = pthread_mutex_lock((pthread_mutex_t*)mutex->mutex); if(ret<0) { perror("Mutex lock"); } return ret; } else { printf("Uninitialized mutex\n"); return -1; } } int turn_mutex_unlock(const turn_mutex *mutex) { if(mutex && mutex->mutex && (mutex->data == MAGIC_CODE)) { int ret = 0; ret = pthread_mutex_unlock((pthread_mutex_t*)mutex->mutex); if(ret<0) { perror("Mutex unlock"); } return ret; } else { printf("Uninitialized mutex\n"); return -1; } } int turn_mutex_init(turn_mutex* mutex) { if(mutex) { mutex->data=MAGIC_CODE; mutex->mutex=turn_malloc(sizeof(pthread_mutex_t)); pthread_mutex_init((pthread_mutex_t*)mutex->mutex,NULL); return 0; } else { return -1; } } int turn_mutex_init_recursive(turn_mutex* mutex) { int ret = -1; if (mutex) { pthread_mutexattr_t attr; if (pthread_mutexattr_init(&attr) < 0) { perror("Cannot init mutex attr"); } else { if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) < 0) { perror("Cannot set type on mutex attr"); } else { mutex->mutex = turn_malloc(sizeof(pthread_mutex_t)); mutex->data = MAGIC_CODE; if ((ret = pthread_mutex_init((pthread_mutex_t*) mutex->mutex, &attr)) < 0) { perror("Cannot init mutex"); mutex->data = 0; turn_free(mutex->mutex,sizeof(pthread_mutex_t)); mutex->mutex = NULL; } } pthread_mutexattr_destroy(&attr); } } return ret; } int turn_mutex_destroy(turn_mutex* mutex) { if(mutex && mutex->mutex && mutex->data == MAGIC_CODE) { int ret = 0; ret = pthread_mutex_destroy((pthread_mutex_t*)(mutex->mutex)); turn_free(mutex->mutex, sizeof(pthread_mutex_t)); mutex->mutex=NULL; mutex->data=0; return ret; } else { return 0; } } ///////////////////////// LOG /////////////////////////////////// #if defined(TURN_LOG_FUNC_IMPL) extern void TURN_LOG_FUNC_IMPL(TURN_LOG_LEVEL level, const s08bits* format, va_list args); #endif static int no_stdout_log = 0; void set_no_stdout_log(int val) { no_stdout_log = val; } void turn_log_func_default(TURN_LOG_LEVEL level, const s08bits* format, ...) { #if !defined(TURN_LOG_FUNC_IMPL) { va_list args; va_start(args,format); vrtpprintf(level, format, args); va_end(args); } #endif { va_list args; va_start(args,format); #if defined(TURN_LOG_FUNC_IMPL) TURN_LOG_FUNC_IMPL(level,format,args); #else #define MAX_RTPPRINTF_BUFFER_SIZE (1024) char s[MAX_RTPPRINTF_BUFFER_SIZE+1]; #undef MAX_RTPPRINTF_BUFFER_SIZE if (level == TURN_LOG_LEVEL_ERROR) { snprintf(s,sizeof(s)-100,"%lu: ERROR: ",(unsigned long)log_time()); size_t slen = strlen(s); vsnprintf(s+slen,sizeof(s)-slen-1,format, args); fwrite(s,strlen(s),1,stdout); } else if(!no_stdout_log) { snprintf(s,sizeof(s)-100,"%lu: ",(unsigned long)log_time()); size_t slen = strlen(s); vsnprintf(s+slen,sizeof(s)-slen-1,format, args); fwrite(s,strlen(s),1,stdout); } #endif va_end(args); } } void addr_debug_print(int verbose, const ioa_addr *addr, const s08bits* s) { if (verbose) { if (!addr) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: EMPTY\n", s); } else { s08bits addrbuf[INET6_ADDRSTRLEN]; if (!s) s = ""; if (addr->ss.sa_family == AF_INET) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "IPv4. %s: %s:%d\n", s, inet_ntop(AF_INET, &addr->s4.sin_addr, addrbuf, INET6_ADDRSTRLEN), nswap16(addr->s4.sin_port)); } else if (addr->ss.sa_family == AF_INET6) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "IPv6. %s: %s:%d\n", s, inet_ntop(AF_INET6, &addr->s6.sin6_addr, addrbuf, INET6_ADDRSTRLEN), nswap16(addr->s6.sin6_port)); } else { if (addr_any_no_port(addr)) { TURN_LOG_FUNC( TURN_LOG_LEVEL_INFO, "IP. %s: 0.0.0.0:%d\n", s, nswap16(addr->s4.sin_port)); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: wrong IP address family: %d\n", s, (int) (addr->ss.sa_family)); } } } } } /*************************************/ #define FILE_STR_LEN (1025) static FILE* _rtpfile = NULL; static int to_syslog = 0; static char log_fn[FILE_STR_LEN]="\0"; static char log_fn_base[FILE_STR_LEN]="\0"; static turn_mutex log_mutex; static int log_mutex_inited = 0; static void log_lock(void) { if(!log_mutex_inited) { log_mutex_inited=1; turn_mutex_init_recursive(&log_mutex); } turn_mutex_lock(&log_mutex); } static void log_unlock(void) { turn_mutex_unlock(&log_mutex); } static void get_date(char *s, size_t sz) { time_t curtm; struct tm* tm_info; curtm = time(NULL); tm_info = localtime(&curtm); strftime(s, sz, "%F", tm_info); } void set_logfile(const char *fn) { if(fn) { log_lock(); if(strcmp(fn,log_fn_base)) { reset_rtpprintf(); STRCPY(log_fn_base,fn); } log_unlock(); } } void reset_rtpprintf(void) { log_lock(); if(_rtpfile) { if(_rtpfile != stdout) fclose(_rtpfile); _rtpfile = NULL; } log_unlock(); } static void set_log_file_name(char *base, char *f) { char logdate[125]; char *tail=strdup(".log"); get_date(logdate,sizeof(logdate)); char *base1=strdup(base); int len=(int)strlen(base1); --len; while(len>=0) { if((base1[len]==' ')||(base1[len]=='\t')) { base1[len]='_'; } --len; } len=(int)strlen(base1); while(len>=0) { if(base1[len]=='/') break; else if(base1[len]=='.') { turn_free(tail,strlen(tail)+1); tail=strdup(base1+len); base1[len]=0; if(strlen(tail)<2) { turn_free(tail,strlen(tail)+1); tail = strdup(".log"); } break; } --len; } len=(int)strlen(base1); if(len>0 && (base1[len-1]!='/') && (base1[len-1]!='-') && (base1[len-1]!='_')) { snprintf(f, FILE_STR_LEN, "%s_%s%s", base1,logdate,tail); } else { snprintf(f, FILE_STR_LEN, "%s%s%s", base1,logdate,tail); } turn_free(base1,strlen(base1)+1); turn_free(tail,strlen(tail)+1); } static void set_rtpfile(void) { if(to_syslog) { return; } else if (!_rtpfile) { if(log_fn_base[0]) { if(!strcmp(log_fn_base,"syslog")) { _rtpfile = stdout; to_syslog = 1; } else if(!strcmp(log_fn_base,"stdout")|| !strcmp(log_fn_base,"-")) { _rtpfile = stdout; no_stdout_log = 1; } else { set_log_file_name(log_fn_base,log_fn); _rtpfile = fopen(log_fn, "w"); } if (!_rtpfile) { fprintf(stderr,"ERROR: Cannot open log file for writing: %s\n",log_fn); } else { return; } } } if(!_rtpfile) { char logbase[FILE_STR_LEN]; char logtail[FILE_STR_LEN]; char logf[FILE_STR_LEN]; snprintf(logtail, FILE_STR_LEN, "turn_%d_", (int)getpid()); snprintf(logbase, FILE_STR_LEN, "/var/log/turnserver/%s", logtail); set_log_file_name(logbase, logf); _rtpfile = fopen(logf, "w"); if (!_rtpfile) { snprintf(logbase, FILE_STR_LEN, "/var/log/%s", logtail); set_log_file_name(logbase, logf); _rtpfile = fopen(logf, "w"); if (!_rtpfile) { snprintf(logbase, FILE_STR_LEN, "/var/tmp/%s", logtail); set_log_file_name(logbase, logf); _rtpfile = fopen(logf, "w"); if (!_rtpfile) { snprintf(logbase, FILE_STR_LEN, "/tmp/%s", logtail); set_log_file_name(logbase, logf); _rtpfile = fopen(logf, "w"); if (!_rtpfile) { snprintf(logbase, FILE_STR_LEN, "%s", logtail); set_log_file_name(logbase, logf); _rtpfile = fopen(logf, "w"); if (!_rtpfile) { _rtpfile = stdout; return; } } } } } STRCPY(log_fn_base,logbase); STRCPY(log_fn,logf); } } void set_log_to_syslog(int val) { to_syslog = val; } #define Q(x) #x #define QUOTE(x) Q(x) void rollover_logfile(void) { if(to_syslog) return; log_lock(); if(_rtpfile && log_fn[0] && (_rtpfile != stdout)) { char logf[FILE_STR_LEN]; set_log_file_name(log_fn_base,logf); if(strcmp(log_fn,logf)) { fclose(_rtpfile); log_fn[0]=0; _rtpfile = fopen(logf, "w"); if(!_rtpfile) { _rtpfile = stdout; } else { STRCPY(log_fn,logf); } } } log_unlock(); } static int get_syslog_level(TURN_LOG_LEVEL level) { switch(level) { case TURN_LOG_LEVEL_CONTROL: return LOG_NOTICE; case TURN_LOG_LEVEL_WARNING: return LOG_WARNING; case TURN_LOG_LEVEL_ERROR: return LOG_ERR; default: ; }; return LOG_INFO; } int vrtpprintf(TURN_LOG_LEVEL level, const char *format, va_list args) { /* Fix for Issue 24, raised by John Selbie: */ #define MAX_RTPPRINTF_BUFFER_SIZE (1024) char s[MAX_RTPPRINTF_BUFFER_SIZE+1]; #undef MAX_RTPPRINTF_BUFFER_SIZE size_t sz; snprintf(s, sizeof(s), "%lu: ",(unsigned long)log_time()); sz=strlen(s); vsnprintf(s+sz, sizeof(s)-1-sz, format, args); s[sizeof(s)-1]=0; if(to_syslog) { syslog(get_syslog_level(level),"%s",s); } else { log_lock(); set_rtpfile(); fprintf(_rtpfile,"%s",s); fflush(_rtpfile); log_unlock(); } return 0; } void rtpprintf(const char *format, ...) { va_list args; va_start (args, format); vrtpprintf(TURN_LOG_LEVEL_INFO, format, args); va_end (args); } ////////////////////////////////////////////////////////////////// turnserver-3.2.3.1/src/apps/common/hiredis_libevent2.h000644 001751 001751 00000004463 12315706777 022637 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __HIREDIS_LIBEVENT_H__ #define __HIREDIS_LIBEVENT_H__ #if !defined(TURN_NO_HIREDIS) #include #ifdef __cplusplus extern "C" { #endif ////////////////////////////////////// #define DEFAULT_REDIS_PORT (6379) typedef void* redis_context_handle; ////////////////////////////////////// redis_context_handle redisLibeventAttach(struct event_base *base, char *ip, int port, char *pwd, int db); void set_default_async_context(redis_context_handle rch); int default_async_context_is_not_empty(void); void send_message_to_redis(redis_context_handle rch, const char *command, const char *key, const char *format,...); void delete_redis_keys(const char *key_pattern); #ifdef __cplusplus } #endif #endif /* TURN_NO_HIREDIS */ #endif /*__HIREDIS_LIBEVENT_H__*/ turnserver-3.2.3.1/src/apps/common/stun_buffer.c000644 001751 001751 00000021772 12315706777 021555 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "stun_buffer.h" ////////////////////// BUFFERS /////////////////////////// int stun_init_buffer(stun_buffer *buf) { if(!buf) return -1; ns_bzero(buf->buf,sizeof(buf->buf)); buf->len=0; buf->offset=0; buf->coffset=0; return 0; } int stun_get_size(const stun_buffer *buf) { if(!buf) return 0; return sizeof(buf->buf); } //////////////////////////////////////////////////////////// void stun_tid_from_message(const stun_buffer *buf, stun_tid* id) { stun_tid_from_message_str(buf->buf,(size_t)(buf->len), id); } void stun_tid_generate_in_message(stun_buffer* buf, stun_tid* id) { if(buf) { stun_tid_generate_in_message_str(buf->buf, id); } } //////////////////////////////////////////////////////// static inline int is_channel_msg(const stun_buffer* buf) { if(buf && buf->len>0) { return is_channel_msg_str(buf->buf, (size_t)(buf->len)); } return 0; } int stun_is_command_message(const stun_buffer* buf) { if(!buf || buf->len<=0) return 0; else return stun_is_command_message_str(buf->buf,(size_t)(buf->len)); } int stun_is_request(const stun_buffer* buf) { return stun_is_request_str(buf->buf,(size_t)buf->len); } int stun_is_success_response(const stun_buffer* buf) { return stun_is_success_response_str(buf->buf, (size_t)(buf->len)); } int stun_is_error_response(const stun_buffer* buf, int *err_code, u08bits *err_msg, size_t err_msg_size) { return stun_is_error_response_str(buf->buf, (size_t)(buf->len), err_code, err_msg, err_msg_size); } int stun_is_response(const stun_buffer* buf) { return stun_is_response_str(buf->buf,(size_t)(buf->len)); } int stun_is_indication(const stun_buffer* buf) { if(is_channel_msg(buf)) return 0; return IS_STUN_INDICATION(stun_get_msg_type(buf)); } u16bits stun_get_method(const stun_buffer* buf) { return stun_get_method_str(buf->buf, (size_t)(buf->len)); } u16bits stun_get_msg_type(const stun_buffer* buf) { if(!buf) return (u16bits)-1; return stun_get_msg_type_str(buf->buf,(size_t)buf->len); } //////////////////////////////////////////////////////////// static void stun_init_command(u16bits message_type, stun_buffer* buf) { buf->len=stun_get_size(buf); stun_init_command_str(message_type, buf->buf, (size_t*)(&(buf->len))); } void stun_init_request(u16bits method, stun_buffer* buf) { stun_init_command(stun_make_request(method), buf); } void stun_init_indication(u16bits method, stun_buffer* buf) { stun_init_command(stun_make_indication(method), buf); } void stun_init_success_response(u16bits method, stun_buffer* buf, stun_tid* id) { buf->len=stun_get_size(buf); stun_init_success_response_str(method, buf->buf, (size_t*)(&(buf->len)), id); } void stun_init_error_response(u16bits method, stun_buffer* buf, u16bits error_code, const u08bits *reason, stun_tid* id) { buf->len=stun_get_size(buf); stun_init_error_response_str(method, buf->buf, (size_t*)(&(buf->len)), error_code, reason, id); } /////////////////////////////////////////////////////////////////////////////// int stun_get_command_message_len(const stun_buffer* buf) { return stun_get_command_message_len_str(buf->buf, (size_t)(buf->len)); } /////////////////////////////////////////////////////////////////////////////// int stun_init_channel_message(u16bits chnumber, stun_buffer* buf, int length, int do_padding) { return stun_init_channel_message_str(chnumber, buf->buf, (size_t*)(&(buf->len)), length, do_padding); } int stun_is_channel_message(stun_buffer* buf, u16bits* chnumber, int is_padding_mandatory) { if(!buf) return 0; size_t blen = (size_t)buf->len; int ret = stun_is_channel_message_str(buf->buf, &blen, chnumber, is_padding_mandatory); if(ret) { buf->len=(ssize_t)blen; } return ret; } /////////////////////////////////////////////////////////////////////////////// int stun_set_allocate_request(stun_buffer* buf, u32bits lifetime, int address_family, u08bits transport, int mobile) { return stun_set_allocate_request_str(buf->buf, (size_t*)(&(buf->len)), lifetime, address_family, transport, mobile); } int stun_set_allocate_response(stun_buffer* buf, stun_tid* tid, const ioa_addr *relayed_addr, const ioa_addr *reflexive_addr, u32bits lifetime, int error_code, const u08bits *reason, u64bits reservation_token, char *mobile_id) { return stun_set_allocate_response_str(buf->buf, (size_t*)(&(buf->len)), tid, relayed_addr, reflexive_addr, lifetime, error_code, reason, reservation_token, mobile_id); } /////////////////////////////////////////////////////////////////////////////// u16bits stun_set_channel_bind_request(stun_buffer* buf, const ioa_addr* peer_addr, u16bits channel_number) { return stun_set_channel_bind_request_str(buf->buf,(size_t*)(&(buf->len)), peer_addr, channel_number); } void stun_set_channel_bind_response(stun_buffer* buf, stun_tid* tid, int error_code, const u08bits *reason) { stun_set_channel_bind_response_str(buf->buf, (size_t*)(&(buf->len)), tid, error_code, reason); } //////////////////////////////////////////////////////////////// stun_attr_ref stun_attr_get_first(const stun_buffer* buf) { return stun_attr_get_first_str(buf->buf, (size_t)(buf->len)); } stun_attr_ref stun_attr_get_next(const stun_buffer* buf, stun_attr_ref prev) { return stun_attr_get_next_str(buf->buf, (size_t)(buf->len), prev); } int stun_attr_add(stun_buffer* buf, u16bits attr, const s08bits* avalue, int alen) { return stun_attr_add_str(buf->buf, (size_t*)(&(buf->len)), attr, (const u08bits *)avalue, alen); } int stun_attr_add_channel_number(stun_buffer* buf, u16bits chnumber) { return stun_attr_add_channel_number_str(buf->buf, (size_t *)(&(buf->len)), chnumber); } int stun_attr_add_addr(stun_buffer *buf,u16bits attr_type, const ioa_addr* ca) { return stun_attr_add_addr_str(buf->buf,(size_t*)(&(buf->len)), attr_type, ca); } int stun_attr_get_addr(const stun_buffer *buf, stun_attr_ref attr, ioa_addr* ca, const ioa_addr *default_addr) { return stun_attr_get_addr_str(buf->buf, (size_t)(buf->len), attr, ca, default_addr); } int stun_attr_get_first_addr(const stun_buffer *buf, u16bits attr_type, ioa_addr* ca, const ioa_addr *default_addr) { return stun_attr_get_first_addr_str(buf->buf, (size_t)(buf->len), attr_type, ca, default_addr); } int stun_attr_add_even_port(stun_buffer* buf, uint8_t value) { if(value) value=0x80; return stun_attr_add(buf,STUN_ATTRIBUTE_EVEN_PORT,(const s08bits*)&value,1); } u16bits stun_attr_get_first_channel_number(const stun_buffer *buf) { return stun_attr_get_first_channel_number_str(buf->buf, (size_t)(buf->len)); } stun_attr_ref stun_attr_get_first_by_type(const stun_buffer* buf, u16bits attr_type) { return stun_attr_get_first_by_type_str(buf->buf, (size_t)(buf->len), attr_type); } /////////////////////////////////////////////////////////////////////////////// void stun_set_binding_request(stun_buffer* buf) { stun_set_binding_request_str(buf->buf, (size_t*)(&(buf->len))); } int stun_set_binding_response(stun_buffer* buf, stun_tid* tid, const ioa_addr *reflexive_addr, int error_code, const u08bits *reason) { return stun_set_binding_response_str(buf->buf, (size_t*)(&(buf->len)), tid, reflexive_addr, error_code, reason, 0,0); } void stun_prepare_binding_request(stun_buffer* buf) { stun_set_binding_request_str(buf->buf, (size_t*)(&(buf->len))); } int stun_is_binding_response(const stun_buffer* buf) { return stun_is_binding_response_str(buf->buf, (size_t)(buf->len)); } /////////////////////////////////////////////////////// turnserver-3.2.3.1/src/apps/common/apputils.h000644 001751 001751 00000011610 12315706777 021067 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __APP_LIB__ #define __APP_LIB__ #include #include #include "ns_turn_ioaddr.h" #ifdef __cplusplus extern "C" { #endif //////////// Common defines /////////////////////////// #define PEER_DEFAULT_PORT (3480) #define DTLS_MAX_RECV_TIMEOUT (5) #define UR_CLIENT_SOCK_BUF_SIZE (65536) #define UR_SERVER_SOCK_BUF_SIZE (UR_CLIENT_SOCK_BUF_SIZE * 32) extern int IS_TURN_SERVER; /////////// SSL ////////////////////////// enum _TURN_TLS_TYPE { TURN_TLS_NO=0, TURN_TLS_SSL23, TURN_TLS_v1_0, #if defined(SSL_TXT_TLSV1_1) TURN_TLS_v1_1, #if defined(SSL_TXT_TLSV1_2) TURN_TLS_v1_2, #endif #endif TURN_TLS_TOTAL }; typedef enum _TURN_TLS_TYPE TURN_TLS_TYPE; ////////////////////////////////////////// #define EVENT_DEL(ev) if(ev) { event_del(ev); event_free(ev); ev=NULL; } ////////////////////////////////////////// #define ioa_socket_raw int ///////////////////////// Sockets /////////////////////////////// #if defined(WIN32) /** Do the platform-specific call needed to close a socket returned from socket() or accept(). */ #define socket_closesocket(s) closesocket(s) #else /** Do the platform-specific call needed to close a socket returned from socket() or accept(). */ #define socket_closesocket(s) close(s) #endif void read_spare_buffer(evutil_socket_t fd); int set_sock_buf_size(evutil_socket_t fd, int sz); int socket_set_reusable(evutil_socket_t fd, int reusable); int sock_bind_to_device(evutil_socket_t fd, const unsigned char* ifname); int socket_set_nonblocking(evutil_socket_t fd); int socket_tcp_set_keepalive(evutil_socket_t fd); int addr_connect(evutil_socket_t fd, const ioa_addr* addr, int *out_errno); int addr_bind(evutil_socket_t fd, const ioa_addr* addr, int reusable); int addr_get_from_sock(evutil_socket_t fd, ioa_addr *addr); int handle_socket_error(void); /////////////////////// SYS ///////////////////// void ignore_sigpipe(void); unsigned long set_system_parameters(int max_resources); ///////////////////////// MTU ////////////////////////// #define MAX_MTU (1500 - 20 - 8) #define MIN_MTU (576 - 20 - 8) #define SOSO_MTU (1300) #define MTU_STEP (68) int set_socket_df(evutil_socket_t fd, int family, int value); int set_mtu_df(SSL* ssl, evutil_socket_t fd, int family, int mtu, int df_value, int verbose); int decrease_mtu(SSL* ssl, int mtu, int verbose); int get_socket_mtu(evutil_socket_t fd, int family, int verbose); ////////////////// Misc utils ///////////////////////// char *skip_blanks(char* s); ////////////////// File search //////////////////////// char* find_config_file(const char *config_file, int print_file_name); void set_execdir(void); void print_abs_file_name(const char *msg1, const char *msg2, const char *fn); ////////////////// Base64 ///////////////////////////// char *base64_encode(const unsigned char *data, size_t input_length, size_t *output_length); void build_base64_decoding_table(void); unsigned char *base64_decode(const char *data, size_t input_length, size_t *output_length); ///////////// SSL //////////////// const char* turn_get_ssl_method(SSL *ssl, const char* mdefault); //////////// Event Base ///////////////////// struct event_base *turn_event_base_new(void); /////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__APP_LIB__ turnserver-3.2.3.1/src/apps/peer/udpserver.h000644 001751 001751 00000005101 12315706777 020706 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __UDP_SERVER__ #define __UDP_SERVER__ ////////////////////////////// #include "ns_turn_utils.h" #include #ifdef __cplusplus extern "C" { #endif ////////////////////////////// struct server_info; typedef struct server_info server_type; /////////////////////////////////////////////////// #define FUNCSTART if(server && server->verbose) turn_log_func_default(TURN_LOG_LEVEL_INFO,"%s:%d:start\n",__FUNCTION__,__LINE__) #define FUNCEND if(server && server->verbose) turn_log_func_default(TURN_LOG_LEVEL_INFO,"%s:%d:end\n",__FUNCTION__,__LINE__) /////////////////////////////////////////////////////// struct server_info { char ifname[1025]; struct event_base* event_base; int verbose; }; ////////////////////////////// server_type* start_udp_server(int verbose, const char* ifname, char **local_addresses, size_t las, int port); void run_udp_server(server_type* server); void clean_udp_server(server_type* server); /////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__UDP_SERVER__ turnserver-3.2.3.1/src/apps/peer/mainudpserver.c000644 001751 001751 00000006163 12315706777 021557 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "ns_turn_utils.h" #include "udpserver.h" #include "apputils.h" #include #include #include #include #include //////////////// local definitions ///////////////// static char Usage[] = "Usage: server [options]\n" "Options:\n" " -p Listening UDP port (Default: 3480)\n" " -d Listening interface device (optional)\n" " -L Listening address\n" " -v verbose\n"; ////////////////////////////////////////////////// int main(int argc, char **argv) { int port = PEER_DEFAULT_PORT; char **local_addr_list=NULL; size_t las = 0; int verbose = TURN_VERBOSE_NONE; int c; char ifname[1025] = "\0"; IS_TURN_SERVER = 1; set_logfile("stdout"); set_system_parameters(0); while ((c = getopt(argc, argv, "d:p:L:v")) != -1) switch (c){ case 'd': STRCPY(ifname, optarg); break; case 'p': port = atoi(optarg); break; case 'L': local_addr_list = (char**)realloc(local_addr_list,++las*sizeof(char*)); local_addr_list[las-1]=strdup(optarg); break; case 'v': verbose = TURN_VERBOSE_NORMAL; break; default: fprintf(stderr, "%s\n", Usage); exit(1); } if(las<1) { local_addr_list = (char**)realloc(local_addr_list,++las*sizeof(char*)); local_addr_list[las-1]=strdup("0.0.0.0"); local_addr_list = (char**)realloc(local_addr_list,++las*sizeof(char*)); local_addr_list[las-1]=strdup("::"); } server_type* server = start_udp_server(verbose, ifname, local_addr_list, las, port); run_udp_server(server); clean_udp_server(server); return 0; } turnserver-3.2.3.1/src/apps/peer/udpserver.c000644 001751 001751 00000011633 12315706777 020710 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "apputils.h" #include "udpserver.h" #include "stun_buffer.h" /////////////// io handlers /////////////////// static void udp_server_input_handler(evutil_socket_t fd, short what, void* arg) { if(!(what&EV_READ)) return; ioa_addr *addr = (ioa_addr*)arg; int len = 0; int slen = get_ioa_addr_len(addr); stun_buffer buffer; ioa_addr remote_addr; do { len = recvfrom(fd, buffer.buf, sizeof(buffer.buf)-1, 0, (struct sockaddr*) &remote_addr, (socklen_t*) &slen); } while(len<0 && (errno==EINTR)); buffer.len=len; if(len>=0) { do { len = sendto(fd, buffer.buf, buffer.len, 0, (const struct sockaddr*) &remote_addr, (socklen_t) slen); } while (len < 0 && ((errno == EINTR) || (errno == ENOBUFS) || (errno == EAGAIN))); } } ///////////////////// operations ////////////////////////// static int udp_create_server_socket(server_type* server, const char* ifname, const char *local_address, int port) { FUNCSTART; if(!server) return -1; evutil_socket_t udp_fd = -1; ioa_addr *server_addr = (ioa_addr*)turn_malloc(sizeof(ioa_addr)); STRCPY(server->ifname,ifname); if(make_ioa_addr((const u08bits*)local_address, port, server_addr)<0) return -1; udp_fd = socket(server_addr->ss.sa_family, SOCK_DGRAM, 0); if (udp_fd < 0) { perror("socket"); return -1; } if(sock_bind_to_device(udp_fd, (unsigned char*)server->ifname)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Cannot bind udp server socket to device %s\n",server->ifname); } set_sock_buf_size(udp_fd,UR_SERVER_SOCK_BUF_SIZE); if(addr_bind(udp_fd,server_addr,1)<0) return -1; socket_set_nonblocking(udp_fd); struct event *udp_ev = event_new(server->event_base,udp_fd,EV_READ|EV_PERSIST, udp_server_input_handler,server_addr); event_add(udp_ev,NULL); FUNCEND; return 0; } static server_type* init_server(int verbose, const char* ifname, char **local_addresses, size_t las, int port) { server_type* server=(server_type*)turn_malloc(sizeof(server_type)); if(!server) return server; ns_bzero(server,sizeof(server_type)); server->verbose=verbose; server->event_base = turn_event_base_new(); while(las) { udp_create_server_socket(server, ifname, local_addresses[--las], port); udp_create_server_socket(server, ifname, local_addresses[las], port+1); } return server; } static int clean_server(server_type* server) { if(server) { if(server->event_base) event_base_free(server->event_base); turn_free(server,sizeof(server_type)); } return 0; } /////////////////////////////////////////////////////////// static void run_events(server_type* server) { if(!server) return; struct timeval timeout; timeout.tv_sec=0; timeout.tv_usec=100000; event_base_loopexit(server->event_base, &timeout); event_base_dispatch(server->event_base); } ///////////////////////////////////////////////////////////// server_type* start_udp_server(int verbose, const char* ifname, char **local_addresses, size_t las, int port) { return init_server(verbose, ifname, local_addresses, las, port); } void run_udp_server(server_type* server) { if(server) { unsigned int cycle=0; while (1) { cycle++; run_events(server); } } } void clean_udp_server(server_type* server) { if(server) clean_server(server); } ////////////////////////////////////////////////////////////////// turnserver-3.2.3.1/src/apps/uclient/startuclient.c000644 001751 001751 00000123165 12315706777 022126 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include #include "apputils.h" #include "ns_turn_utils.h" #include "startuclient.h" #include "ns_turn_msg.h" #include "uclient.h" #include "session.h" #include ///////////////////////////////////////// #define MAX_CONNECT_EFFORTS (77) #define DTLS_MAX_CONNECT_TIMEOUT (30) #define EXTRA_CREATE_PERMS (25) static uint64_t current_reservation_token = 0; static int allocate_rtcp = 0; static const int never_allocate_rtcp = 0; ///////////////////////////////////////// int rare_event(void) { if(dos) return (((unsigned long)random()) %1000 == 777); return 0; } int not_rare_event(void) { if(dos) return ((((unsigned long)random()) %1000) < 200); return 0; } static int get_allocate_address_family(ioa_addr *relay_addr) { if(relay_addr->ss.sa_family == AF_INET) return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT; else if(relay_addr->ss.sa_family == AF_INET6) return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6; else return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_INVALID; } ///////////////////////////////////////// static SSL* tls_connect(ioa_socket_raw fd, ioa_addr *remote_addr) { int ctxtype = (int)(((unsigned long)random())%root_tls_ctx_num); SSL *ssl = SSL_new(root_tls_ctx[ctxtype]); if(use_tcp) { SSL_set_fd(ssl, fd); } else { #if defined(TURN_NO_DTLS) UNUSED_ARG(remote_addr); fprintf(stderr,"ERROR: DTLS is not supported.\n"); exit(-1); #else /* Create BIO, connect and set to already connected */ BIO *bio = BIO_new_dgram(fd, BIO_CLOSE); //bio = BIO_new_socket(fd, BIO_CLOSE); BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &remote_addr->ss); SSL_set_bio(ssl, bio, bio); { struct timeval timeout; /* Set and activate timeouts */ timeout.tv_sec = DTLS_MAX_CONNECT_TIMEOUT; timeout.tv_usec = 0; BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout); } set_mtu_df(ssl, fd, remote_addr->ss.sa_family, SOSO_MTU, !use_tcp, clnet_verbose); #endif } SSL_set_max_cert_list(ssl, 655350); if (clnet_verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "call SSL_connect...\n"); int rc = 0; do { do { rc = SSL_connect(ssl); } while (rc < 0 && errno == EINTR); if (rc > 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: client session connected with cipher %s, method=%s\n",__FUNCTION__, SSL_get_cipher(ssl),turn_get_ssl_method(ssl,NULL)); if(clnet_verbose && SSL_get_peer_certificate(ssl)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "------------------------------------------------------------\n"); X509_NAME_print_ex_fp(stdout, X509_get_subject_name(SSL_get_peer_certificate(ssl)), 1, XN_FLAG_MULTILINE); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n\n Cipher: %s\n", SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n------------------------------------------------------------\n\n"); } break; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot connect\n", __FUNCTION__); switch (SSL_get_error(ssl, rc)) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: if(!dos) usleep(1000); continue; default: { char buf[1025]; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s (%d)\n", ERR_error_string(ERR_get_error(), buf), SSL_get_error( ssl, rc)); exit(-1); } }; } } while (1); if (clnet_verbose && SSL_get_peer_certificate(ssl)) { if(use_tcp) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "------TLS---------------------------------------------------\n"); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "------DTLS---------------------------------------------------\n"); } X509_NAME_print_ex_fp(stdout, X509_get_subject_name( SSL_get_peer_certificate(ssl)), 1, XN_FLAG_MULTILINE); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n\n Cipher: %s\n", SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n------------------------------------------------------------\n\n"); } return ssl; } int socket_connect(evutil_socket_t clnet_fd, ioa_addr *remote_addr, int *connect_err) { if (addr_connect(clnet_fd, remote_addr, connect_err) < 0) { if(*connect_err == EINPROGRESS) return 0; if (*connect_err == EADDRINUSE) return +1; perror("connect"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot connect to remote addr: %d\n", __FUNCTION__,*connect_err); exit(-1); } return 0; } static int clnet_connect(uint16_t clnet_remote_port, const char *remote_address, const unsigned char* ifname, const char *local_address, int verbose, app_ur_conn_info *clnet_info) { ioa_addr local_addr; evutil_socket_t clnet_fd; int connect_err; ioa_addr remote_addr; start_socket: clnet_fd = -1; connect_err = 0; ns_bzero(&remote_addr, sizeof(ioa_addr)); if (make_ioa_addr((const u08bits*) remote_address, clnet_remote_port, &remote_addr) < 0) return -1; ns_bzero(&local_addr, sizeof(ioa_addr)); clnet_fd = socket(remote_addr.ss.sa_family, use_tcp ? SOCK_STREAM : SOCK_DGRAM, 0); if (clnet_fd < 0) { perror("socket"); exit(-1); } if (sock_bind_to_device(clnet_fd, ifname) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Cannot bind client socket to device %s\n", ifname); } set_sock_buf_size(clnet_fd, UR_CLIENT_SOCK_BUF_SIZE); if(clnet_info->is_peer && (*local_address==0)) { if(remote_addr.ss.sa_family == AF_INET6) { if (make_ioa_addr((const u08bits*) "::1", 0, &local_addr) < 0) { return -1; } } else { if (make_ioa_addr((const u08bits*) "127.0.0.1", 0, &local_addr) < 0) { return -1; } } addr_bind(clnet_fd, &local_addr, 0); } else if (strlen(local_address) > 0) { if (make_ioa_addr((const u08bits*) local_address, 0, &local_addr) < 0) return -1; addr_bind(clnet_fd, &local_addr,0); } if(clnet_info->is_peer) { ; } else if(socket_connect(clnet_fd, &remote_addr, &connect_err)>0) goto start_socket; if (clnet_info) { addr_cpy(&(clnet_info->remote_addr), &remote_addr); addr_cpy(&(clnet_info->local_addr), &local_addr); clnet_info->fd = clnet_fd; addr_get_from_sock(clnet_fd, &(clnet_info->local_addr)); STRCPY(clnet_info->lsaddr,local_address); STRCPY(clnet_info->rsaddr,remote_address); STRCPY(clnet_info->ifname,(const char*)ifname); } if (use_secure) { clnet_info->ssl = tls_connect(clnet_info->fd, &remote_addr); if (!clnet_info->ssl) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot SSL connect to remote addr\n", __FUNCTION__); exit(-1); } } if(verbose && clnet_info) { addr_debug_print(verbose, &(clnet_info->local_addr), "Connected from"); addr_debug_print(verbose, &remote_addr, "Connected to"); } if(!dos) usleep(500); return 0; } int read_mobility_ticket(app_ur_conn_info *clnet_info, stun_buffer *message) { int ret = 0; if(clnet_info && message) { stun_attr_ref s_mobile_id_sar = stun_attr_get_first_by_type(message, STUN_ATTRIBUTE_MOBILITY_TICKET); if(s_mobile_id_sar) { int smid_len = stun_attr_get_len(s_mobile_id_sar); if(smid_len>0 && (((size_t)smid_len)s_mobile_id))) { const u08bits* smid_val = stun_attr_get_value(s_mobile_id_sar); if(smid_val) { ns_bcopy(smid_val, clnet_info->s_mobile_id, (size_t)smid_len); clnet_info->s_mobile_id[smid_len] = 0; if (clnet_verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: smid=%s\n", __FUNCTION__, clnet_info->s_mobile_id); } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: smid_len=%d\n", __FUNCTION__, smid_len); ret = -1; } } } return ret; } static int clnet_allocate(int verbose, app_ur_conn_info *clnet_info, ioa_addr *relay_addr, int af, char *turn_addr, u16bits *turn_port) { int af_cycle = 0; int reopen_socket = 0; int allocate_finished; beg_allocate: allocate_finished=0; while (!allocate_finished && af_cycle++ < 32) { int allocate_sent = 0; if(reopen_socket && !use_tcp) { socket_closesocket(clnet_info->fd); clnet_info->fd = -1; if (clnet_connect(addr_get_port(&(clnet_info->remote_addr)), clnet_info->rsaddr, (u08bits*)clnet_info->ifname, clnet_info->lsaddr, verbose, clnet_info) < 0) { exit(-1); } reopen_socket = 0; } stun_buffer message; if(current_reservation_token) af = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT; if(!dos) stun_set_allocate_request(&message, 800, af, relay_transport, mobility); else stun_set_allocate_request(&message, 300, af, relay_transport, mobility); if(dont_fragment) stun_attr_add(&message, STUN_ATTRIBUTE_DONT_FRAGMENT, NULL, 0); if(!no_rtcp) { if (!never_allocate_rtcp && allocate_rtcp) { uint64_t reservation_token = ioa_ntoh64(current_reservation_token); stun_attr_add(&message, STUN_ATTRIBUTE_RESERVATION_TOKEN, (char*) (&reservation_token), 8); } else { stun_attr_add_even_port(&message, 1); } } if(add_integrity(clnet_info, &message)<0) return -1; stun_attr_add_fingerprint_str(message.buf,(size_t*)&(message.len)); while (!allocate_sent) { int len = send_buffer(clnet_info, &message,0,0); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "allocate sent\n"); } allocate_sent = 1; } else { perror("send"); exit(1); } } ////////////<<==allocate send if(not_rare_event()) return 0; ////////allocate response==>> { int allocate_received = 0; stun_buffer message; while (!allocate_received) { int len = recv_buffer(clnet_info, &message, 1, 0); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "allocate response received: \n"); } message.len = len; int err_code = 0; u08bits err_msg[129]; if (stun_is_success_response(&message)) { allocate_received = 1; allocate_finished = 1; if(clnet_info->nonce[0] || use_short_term) { SHATYPE sht = clnet_info->shatype; if(stun_check_message_integrity_str(get_turn_credentials_type(), message.buf, (size_t)(message.len), g_uname, clnet_info->realm, g_upwd, sht)<1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Wrong integrity in allocate message received from server\n"); return -1; } } if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "success\n"); } if (stun_attr_get_first_addr(&message, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, relay_addr, NULL) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: !!!: relay addr cannot be received\n", __FUNCTION__); return -1; } else { if (verbose) { ioa_addr remote_addr; memcpy(&remote_addr, relay_addr, sizeof(ioa_addr)); addr_debug_print(verbose, &remote_addr, "Received relay addr"); } } stun_attr_ref rt_sar = stun_attr_get_first_by_type( &message, STUN_ATTRIBUTE_RESERVATION_TOKEN); uint64_t rtv = stun_attr_get_reservation_token_value(rt_sar); current_reservation_token = rtv; if (verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: rtv=%llu\n", __FUNCTION__, rtv); read_mobility_ticket(clnet_info, &message); } else if (stun_is_challenge_response_str(message.buf, (size_t)message.len, &err_code,err_msg,sizeof(err_msg), clnet_info->realm,clnet_info->nonce)) { if(err_code == SHA_TOO_WEAK && (clnet_info->shatype == SHATYPE_SHA1)) { clnet_info->shatype = SHATYPE_SHA256; recalculate_restapi_hmac(); } goto beg_allocate; } else if (stun_is_error_response(&message, &err_code,err_msg,sizeof(err_msg))) { if(err_code == SHA_TOO_WEAK && (clnet_info->shatype == SHATYPE_SHA1) && use_short_term) { clnet_info->shatype = SHATYPE_SHA256; goto beg_allocate; } allocate_received = 1; if(err_code == 300) { if(clnet_info->nonce[0] || use_short_term) { SHATYPE sht = clnet_info->shatype; if(stun_check_message_integrity_str(get_turn_credentials_type(), message.buf, (size_t)(message.len), g_uname, clnet_info->realm, g_upwd, sht)<1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Wrong integrity in allocate message received from server\n"); return -1; } } ioa_addr alternate_server; if(stun_attr_get_first_addr(&message, STUN_ATTRIBUTE_ALTERNATE_SERVER, &alternate_server, NULL)==-1) { //error } else if(turn_addr && turn_port){ addr_to_string_no_port(&alternate_server, (u08bits*)turn_addr); *turn_port = (u16bits)addr_get_port(&alternate_server); } } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "error %d (%s)\n", err_code,(char*)err_msg); if (err_code != 437) { allocate_finished = 1; current_reservation_token = 0; return -1; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "trying allocate again...\n", err_code); sleep(1); reopen_socket = 1; } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "unknown allocate response\n"); /* Try again ? */ } } else { perror("recv"); exit(-1); break; } } } } ////////////<<== allocate response received if(rare_event()) return 0; if(!allocate_finished) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot complete Allocation\n"); exit(-1); } allocate_rtcp = !allocate_rtcp; if (1) { af_cycle = 0; if(clnet_info->s_mobile_id[0]) { int fd = clnet_info->fd; SSL* ssl = clnet_info->ssl; int close_now = (int)(random()%2); if(close_now) { int close_socket = (int)(random()%2); if(ssl && !close_socket) { SSL_shutdown(ssl); SSL_free(ssl); ssl = NULL; fd = -1; } else if(fd>=0) { close(fd); fd = -1; ssl = NULL; } } app_ur_conn_info ci; ns_bcopy(clnet_info,&ci,sizeof(ci)); ci.fd = -1; ci.ssl = NULL; clnet_info->fd = -1; clnet_info->ssl = NULL; //Reopen: if(clnet_connect(addr_get_port(&(ci.remote_addr)), ci.rsaddr, (unsigned char*)ci.ifname, ci.lsaddr, clnet_verbose, clnet_info)<0) { exit(-1); } if(ssl) { SSL_shutdown(ssl); SSL_free(ssl); } else if(fd>=0) { close(fd); } } beg_refresh: if(af_cycle++>32) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot complete Refresh\n"); exit(-1); } //==>>refresh request, for an example only: { int refresh_sent = 0; stun_buffer message; stun_init_request(STUN_METHOD_REFRESH, &message); uint32_t lt = htonl(600); stun_attr_add(&message, STUN_ATTRIBUTE_LIFETIME, (const char*) <, 4); if(clnet_info->s_mobile_id[0]) { stun_attr_add(&message, STUN_ATTRIBUTE_MOBILITY_TICKET, (const char*)clnet_info->s_mobile_id, strlen(clnet_info->s_mobile_id)); } if(add_integrity(clnet_info, &message)<0) return -1; stun_attr_add_fingerprint_str(message.buf,(size_t*)&(message.len)); while (!refresh_sent) { int len = send_buffer(clnet_info, &message, 0,0); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "refresh sent\n"); } refresh_sent = 1; } else { perror("send"); exit(1); } } } if(not_rare_event()) return 0; ////////refresh response==>> { int refresh_received = 0; stun_buffer message; while (!refresh_received) { int len = recv_buffer(clnet_info, &message, 1, 0); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "refresh response received: \n"); } message.len = len; int err_code = 0; u08bits err_msg[129]; if (stun_is_success_response(&message)) { read_mobility_ticket(clnet_info, &message); refresh_received = 1; if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "success\n"); } } else if (stun_is_challenge_response_str(message.buf, (size_t)message.len, &err_code,err_msg,sizeof(err_msg), clnet_info->realm,clnet_info->nonce)) { if(err_code == SHA_TOO_WEAK && (clnet_info->shatype == SHATYPE_SHA1)) { clnet_info->shatype = SHATYPE_SHA256; recalculate_restapi_hmac(); } goto beg_refresh; } else if (stun_is_error_response(&message, &err_code,err_msg,sizeof(err_msg))) { refresh_received = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "error %d (%s)\n", err_code,(char*)err_msg); return -1; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "unknown refresh response\n"); /* Try again ? */ } } else { perror("recv"); exit(-1); break; } } } } return 0; } static int turn_channel_bind(int verbose, uint16_t *chn, app_ur_conn_info *clnet_info, ioa_addr *peer_addr) { beg_bind: { int cb_sent = 0; stun_buffer message; if(negative_test) { *chn = stun_set_channel_bind_request(&message, peer_addr, (u16bits)random()); } else { *chn = stun_set_channel_bind_request(&message, peer_addr, *chn); } if(add_integrity(clnet_info, &message)<0) return -1; stun_attr_add_fingerprint_str(message.buf,(size_t*)&(message.len)); while (!cb_sent) { int len = send_buffer(clnet_info, &message, 0,0); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "channel bind sent\n"); } cb_sent = 1; } else { perror("send"); exit(1); } } } ////////////<<==channel bind send if(not_rare_event()) return 0; ////////channel bind response==>> { int cb_received = 0; stun_buffer message; while (!cb_received) { int len = recv_buffer(clnet_info, &message, 1, 0); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "cb response received: \n"); } int err_code = 0; u08bits err_msg[129]; if (stun_is_success_response(&message)) { cb_received = 1; if(clnet_info->nonce[0] || use_short_term) { SHATYPE sht = clnet_info->shatype; if(stun_check_message_integrity_str(get_turn_credentials_type(), message.buf, (size_t)(message.len), g_uname, clnet_info->realm, g_upwd, sht)<1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Wrong integrity in channel bind message received from server\n"); return -1; } } if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "success: 0x%x\n", (int) (*chn)); } } else if (stun_is_challenge_response_str(message.buf, (size_t)message.len, &err_code,err_msg,sizeof(err_msg), clnet_info->realm,clnet_info->nonce)) { if(err_code == SHA_TOO_WEAK && (clnet_info->shatype == SHATYPE_SHA1)) { clnet_info->shatype = SHATYPE_SHA256; recalculate_restapi_hmac(); } goto beg_bind; } else if (stun_is_error_response(&message, &err_code,err_msg,sizeof(err_msg))) { cb_received = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "channel bind: error %d (%s)\n", err_code,(char*)err_msg); return -1; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "unknown channel bind response\n"); /* Try again ? */ } } else { perror("recv"); exit(-1); break; } } } return 0; } static int turn_create_permission(int verbose, app_ur_conn_info *clnet_info, ioa_addr *peer_addr, int addrnum) { if(no_permissions || (addrnum<1)) return 0; char saddr[129]="\0"; if (verbose) { addr_to_string(peer_addr,(u08bits*)saddr); } beg_cp: { int cp_sent = 0; stun_buffer message; stun_init_request(STUN_METHOD_CREATE_PERMISSION, &message); { int addrindex; for(addrindex=0;addrindex 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "create perm sent: %s\n",saddr); } cp_sent = 1; } else { perror("send"); exit(1); } } } ////////////<<==create permission send if(not_rare_event()) return 0; ////////create permission response==>> { int cp_received = 0; stun_buffer message; while (!cp_received) { int len = recv_buffer(clnet_info, &message, 1, 0); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "cp response received: \n"); } int err_code = 0; u08bits err_msg[129]; if (stun_is_success_response(&message)) { cp_received = 1; if(clnet_info->nonce[0] || use_short_term) { SHATYPE sht = clnet_info->shatype; if(stun_check_message_integrity_str(get_turn_credentials_type(), message.buf, (size_t)(message.len), g_uname, clnet_info->realm, g_upwd, sht)<1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Wrong integrity in create permission message received from server\n"); return -1; } } if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "success\n"); } } else if (stun_is_challenge_response_str(message.buf, (size_t)message.len, &err_code,err_msg,sizeof(err_msg), clnet_info->realm,clnet_info->nonce)) { if(err_code == SHA_TOO_WEAK && (clnet_info->shatype == SHATYPE_SHA1)) { clnet_info->shatype = SHATYPE_SHA256; recalculate_restapi_hmac(); } goto beg_cp; } else if (stun_is_error_response(&message, &err_code,err_msg,sizeof(err_msg))) { cp_received = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "create permission error %d (%s)\n", err_code,(char*)err_msg); return -1; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "unknown create permission response\n"); /* Try again ? */ } } else { perror("recv"); exit(-1); } } } return 0; } int start_connection(uint16_t clnet_remote_port0, const char *remote_address0, const unsigned char* ifname, const char *local_address, int verbose, app_ur_conn_info *clnet_info_probe, app_ur_conn_info *clnet_info, uint16_t *chn, app_ur_conn_info *clnet_info_rtcp, uint16_t *chn_rtcp) { ioa_addr relay_addr; ioa_addr relay_addr_rtcp; ioa_addr peer_addr_rtcp; addr_cpy(&peer_addr_rtcp,&peer_addr); addr_set_port(&peer_addr_rtcp,addr_get_port(&peer_addr_rtcp)+1); /* Probe: */ if (clnet_connect(clnet_remote_port0, remote_address0, ifname, local_address, verbose, clnet_info_probe) < 0) { exit(-1); } uint16_t clnet_remote_port = clnet_remote_port0; char remote_address[1025]; STRCPY(remote_address,remote_address0); clnet_allocate(verbose, clnet_info_probe, &relay_addr, default_address_family, remote_address, &clnet_remote_port); /* Real: */ *chn = 0; if(chn_rtcp) *chn_rtcp=0; if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info) < 0) { exit(-1); } if(!no_rtcp) { if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info_rtcp) < 0) { exit(-1); } } int af = default_address_family ? default_address_family : get_allocate_address_family(&peer_addr); if (clnet_allocate(verbose, clnet_info, &relay_addr, af, NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; if(!no_rtcp) { af = default_address_family ? default_address_family : get_allocate_address_family(&peer_addr_rtcp); if (clnet_allocate(verbose, clnet_info_rtcp, &relay_addr_rtcp, af,NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; } if (!dos) { if (!do_not_use_channel) { /* These multiple "channel bind" requests are here only because * we are playing with the TURN server trying to screw it */ if (turn_channel_bind(verbose, chn, clnet_info, &peer_addr_rtcp) < 0) { exit(-1); } if(rare_event()) return 0; if (turn_channel_bind(verbose, chn, clnet_info, &peer_addr_rtcp) < 0) { exit(-1); } if(rare_event()) return 0; *chn = 0; if (turn_channel_bind(verbose, chn, clnet_info, &peer_addr) < 0) { exit(-1); } if(rare_event()) return 0; if (turn_channel_bind(verbose, chn, clnet_info, &peer_addr) < 0) { exit(-1); } if(rare_event()) return 0; if(extra_requests) { const char *sarbaddr = "164.156.178.190"; if(random() % 2 == 0) sarbaddr = "2001::172"; ioa_addr arbaddr; make_ioa_addr((const u08bits*)sarbaddr, 333, &arbaddr); int i; int maxi = (unsigned short)random() % EXTRA_CREATE_PERMS; for(i=0;i0) addr_cpy(&arbaddr[i],&arbaddr[0]); addr_set_port(&arbaddr[i], (unsigned short)random()); u08bits *u=(u08bits*)&(arbaddr[i].s4.sin_addr); u[(unsigned short)random()%4] = u[(unsigned short)random()%4] + 1; //char sss[128]; //addr_to_string(&arbaddr[i],(u08bits*)sss); //printf("%s: 111.111: %s\n",__FUNCTION__,sss); } turn_create_permission(verbose, clnet_info, arbaddr, maxi); } } else { int before=(random()%2 == 0); if(before) { if (turn_create_permission(verbose, clnet_info, &peer_addr, 1) < 0) { exit(-1); } if(rare_event()) return 0; if (turn_create_permission(verbose, clnet_info, &peer_addr_rtcp, 1) < 0) { exit(-1); } if(rare_event()) return 0; } if(extra_requests) { const char *sarbaddr = "64.56.78.90"; if(random() % 2 == 0) sarbaddr = "2001::172"; ioa_addr arbaddr[EXTRA_CREATE_PERMS]; make_ioa_addr((const u08bits*)sarbaddr, 333, &arbaddr[0]); int i; int maxi = (unsigned short)random() % EXTRA_CREATE_PERMS; for(i=0;i0) addr_cpy(&arbaddr[i],&arbaddr[0]); addr_set_port(&arbaddr[i], (unsigned short)random()); u08bits *u=(u08bits*)&(arbaddr[i].s4.sin_addr); u[(unsigned short)random()%4] = u[(unsigned short)random()%4] + 1; //char sss[128]; //addr_to_string(&arbaddr,(u08bits*)sss); //printf("%s: 111.111: %s\n",__FUNCTION__,sss); } turn_create_permission(verbose, clnet_info, arbaddr, maxi); } if(!before) { if (turn_create_permission(verbose, clnet_info, &peer_addr, 1) < 0) { exit(-1); } if(rare_event()) return 0; if (turn_create_permission(verbose, clnet_info, &peer_addr_rtcp, 1) < 0) { exit(-1); } if(rare_event()) return 0; } if (!no_rtcp) { if (turn_create_permission(verbose, clnet_info_rtcp, &peer_addr_rtcp, 1) < 0) { exit(-1); } if(rare_event()) return 0; if (turn_create_permission(verbose, clnet_info_rtcp, &peer_addr, 1) < 0) { exit(-1); } if(rare_event()) return 0; } } } addr_cpy(&(clnet_info->peer_addr), &peer_addr); if(!no_rtcp) addr_cpy(&(clnet_info_rtcp->peer_addr), &peer_addr_rtcp); return 0; } int start_c2c_connection(uint16_t clnet_remote_port0, const char *remote_address0, const unsigned char* ifname, const char *local_address, int verbose, app_ur_conn_info *clnet_info_probe, app_ur_conn_info *clnet_info1, uint16_t *chn1, app_ur_conn_info *clnet_info1_rtcp, uint16_t *chn1_rtcp, app_ur_conn_info *clnet_info2, uint16_t *chn2, app_ur_conn_info *clnet_info2_rtcp, uint16_t *chn2_rtcp) { ioa_addr relay_addr1; ioa_addr relay_addr1_rtcp; ioa_addr relay_addr2; ioa_addr relay_addr2_rtcp; *chn1 = 0; *chn2 = 0; if(chn1_rtcp) *chn1_rtcp=0; if(chn2_rtcp) *chn2_rtcp=0; /* Probe: */ if (clnet_connect(clnet_remote_port0, remote_address0, ifname, local_address, verbose, clnet_info_probe) < 0) { exit(-1); } uint16_t clnet_remote_port = clnet_remote_port0; char remote_address[1025]; STRCPY(remote_address,remote_address0); clnet_allocate(verbose, clnet_info_probe, &relay_addr1, default_address_family, remote_address, &clnet_remote_port); if(rare_event()) return 0; /* Real: */ if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info1) < 0) { exit(-1); } if(!no_rtcp) if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info1_rtcp) < 0) { exit(-1); } if(passive_tcp) clnet_info2->is_peer = 1; if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info2) < 0) { exit(-1); } if(!no_rtcp) if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info2_rtcp) < 0) { exit(-1); } if(!no_rtcp) { if (clnet_allocate(verbose, clnet_info1, &relay_addr1, default_address_family,NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; if (clnet_allocate(verbose, clnet_info1_rtcp, &relay_addr1_rtcp, default_address_family,NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; if (clnet_allocate(verbose, clnet_info2, &relay_addr2, default_address_family,NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; if (clnet_allocate(verbose, clnet_info2_rtcp, &relay_addr2_rtcp, default_address_family,NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; } else { if (clnet_allocate(verbose, clnet_info1, &relay_addr1, default_address_family,NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; if(!(clnet_info2->is_peer)) { if (clnet_allocate(verbose, clnet_info2, &relay_addr2, default_address_family,NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; } else { addr_cpy(&(clnet_info2->remote_addr),&relay_addr1); addr_cpy(&relay_addr2,&(clnet_info2->local_addr)); } } if (!do_not_use_channel) { if (turn_channel_bind(verbose, chn1, clnet_info1, &relay_addr2) < 0) { exit(-1); } if(extra_requests) { const char *sarbaddr = "164.156.178.190"; if(random() % 2 == 0) sarbaddr = "2001::172"; ioa_addr arbaddr; make_ioa_addr((const u08bits*)sarbaddr, 333, &arbaddr); int i; int maxi = (unsigned short)random() % EXTRA_CREATE_PERMS; for(i=0;i0) addr_cpy(&arbaddr[i],&arbaddr[0]); addr_set_port(&arbaddr[i], (unsigned short)random()); u08bits *u=(u08bits*)&(arbaddr[i].s4.sin_addr); u[(unsigned short)random()%4] = u[(unsigned short)random()%4] + 1; //char sss[128]; //addr_to_string(&arbaddr[i],(u08bits*)sss); //printf("%s: 111.111: %s\n",__FUNCTION__,sss); } turn_create_permission(verbose, clnet_info1, arbaddr, maxi); } if(!no_rtcp) if (turn_channel_bind(verbose, chn1_rtcp, clnet_info1_rtcp, &relay_addr2_rtcp) < 0) { exit(-1); } if(rare_event()) return 0; if (turn_channel_bind(verbose, chn2, clnet_info2, &relay_addr1) < 0) { exit(-1); } if(rare_event()) return 0; if(!no_rtcp) if (turn_channel_bind(verbose, chn2_rtcp, clnet_info2_rtcp, &relay_addr1_rtcp) < 0) { exit(-1); } if(rare_event()) return 0; } else { if (turn_create_permission(verbose, clnet_info1, &relay_addr2, 1) < 0) { exit(-1); } if(extra_requests) { const char *sarbaddr = "64.56.78.90"; if(random() % 2 == 0) sarbaddr = "2001::172"; ioa_addr arbaddr; make_ioa_addr((const u08bits*)sarbaddr, 333, &arbaddr); int i; int maxi = (unsigned short)random() % EXTRA_CREATE_PERMS; for(i=0;iis_peer)) { if (turn_create_permission(verbose, clnet_info2, &relay_addr1, 1) < 0) { exit(-1); } if(rare_event()) return 0; } if (!no_rtcp) if (turn_create_permission(verbose, clnet_info2_rtcp, &relay_addr1_rtcp, 1) < 0) { exit(-1); } if(rare_event()) return 0; } addr_cpy(&(clnet_info1->peer_addr), &relay_addr2); if(!no_rtcp) addr_cpy(&(clnet_info1_rtcp->peer_addr), &relay_addr2_rtcp); addr_cpy(&(clnet_info2->peer_addr), &relay_addr1); if(!no_rtcp) addr_cpy(&(clnet_info2_rtcp->peer_addr), &relay_addr1_rtcp); return 0; } //////////// RFC 6062 /////////////// int turn_tcp_connect(int verbose, app_ur_conn_info *clnet_info, ioa_addr *peer_addr) { { int cp_sent = 0; stun_buffer message; stun_init_request(STUN_METHOD_CONNECT, &message); stun_attr_add_addr(&message, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, peer_addr); if(add_integrity(clnet_info, &message)<0) return -1; stun_attr_add_fingerprint_str(message.buf,(size_t*)&(message.len)); while (!cp_sent) { int len = send_buffer(clnet_info, &message, 0,0); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "tcp connect sent\n"); } cp_sent = 1; } else { perror("send"); exit(1); } } } ////////////<<==connect send return 0; } static int turn_tcp_connection_bind(int verbose, app_ur_conn_info *clnet_info, app_tcp_conn_info *atc, int errorOK) { beg_cb: { int cb_sent = 0; stun_buffer message; u32bits cid = atc->cid; stun_init_request(STUN_METHOD_CONNECTION_BIND, &message); stun_attr_add(&message, STUN_ATTRIBUTE_CONNECTION_ID, (const s08bits*)&cid,4); if(add_integrity(clnet_info, &message)<0) return -1; stun_attr_add_fingerprint_str(message.buf,(size_t*)&(message.len)); while (!cb_sent) { int len = send_buffer(clnet_info, &message, 1, atc); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "connection bind sent\n"); } cb_sent = 1; } else { if(errorOK) return 0; perror("send"); exit(1); } } } ////////////<<==connection bind send if(not_rare_event()) return 0; ////////connection bind response==>> { int cb_received = 0; stun_buffer message; while (!cb_received) { int len = recv_buffer(clnet_info, &message, 1, atc); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "connect bind response received: \n"); } int err_code = 0; u08bits err_msg[129]; if (stun_is_success_response(&message)) { if(clnet_info->nonce[0] || use_short_term) { SHATYPE sht = clnet_info->shatype; if(stun_check_message_integrity_str(get_turn_credentials_type(), message.buf, (size_t)(message.len), g_uname, clnet_info->realm, g_upwd, sht)<1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Wrong integrity in connect bind message received from server\n"); return -1; } } if(stun_get_method(&message)!=STUN_METHOD_CONNECTION_BIND) continue; cb_received = 1; if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "success\n"); } atc->tcp_data_bound = 1; } else if (stun_is_challenge_response_str(message.buf, (size_t)message.len, &err_code,err_msg,sizeof(err_msg), clnet_info->realm,clnet_info->nonce)) { if(err_code == SHA_TOO_WEAK && (clnet_info->shatype == SHATYPE_SHA1)) { clnet_info->shatype = SHATYPE_SHA256; recalculate_restapi_hmac(); } goto beg_cb; } else if (stun_is_error_response(&message, &err_code,err_msg,sizeof(err_msg))) { cb_received = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "connection bind error %d (%s)\n", err_code,(char*)err_msg); return -1; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "unknown connection bind response\n"); /* Try again ? */ } } else { if(errorOK) return 0; perror("recv"); exit(-1); } } } return 0; } void tcp_data_connect(app_ur_session *elem, u32bits cid) { int clnet_fd = socket(elem->pinfo.remote_addr.ss.sa_family, SOCK_STREAM, 0); if (clnet_fd < 0) { perror("socket"); exit(-1); } if (sock_bind_to_device(clnet_fd, client_ifname) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Cannot bind client socket to device %s\n", client_ifname); } set_sock_buf_size(clnet_fd, (UR_CLIENT_SOCK_BUF_SIZE<<2)); ++elem->pinfo.tcp_conn_number; int i = (int)(elem->pinfo.tcp_conn_number-1); elem->pinfo.tcp_conn=(app_tcp_conn_info**)realloc(elem->pinfo.tcp_conn,elem->pinfo.tcp_conn_number*sizeof(app_tcp_conn_info*)); elem->pinfo.tcp_conn[i]=(app_tcp_conn_info*)turn_malloc(sizeof(app_tcp_conn_info)); ns_bzero(elem->pinfo.tcp_conn[i],sizeof(app_tcp_conn_info)); elem->pinfo.tcp_conn[i]->tcp_data_fd = clnet_fd; elem->pinfo.tcp_conn[i]->cid = cid; addr_cpy(&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr),&(elem->pinfo.local_addr)); addr_set_port(&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr),0); addr_bind(clnet_fd, &(elem->pinfo.tcp_conn[i]->tcp_data_local_addr), 1); addr_get_from_sock(clnet_fd,&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr)); { int cycle = 0; while(cycle++<1024) { int err = 0; if (addr_connect(clnet_fd, &(elem->pinfo.remote_addr),&err) < 0) { if(err == EADDRINUSE) { socket_closesocket(clnet_fd); clnet_fd = socket(elem->pinfo.remote_addr.ss.sa_family, SOCK_STREAM, 0); if (clnet_fd < 0) { perror("socket"); exit(-1); } if (sock_bind_to_device(clnet_fd, client_ifname) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Cannot bind client socket to device %s\n", client_ifname); } set_sock_buf_size(clnet_fd, UR_CLIENT_SOCK_BUF_SIZE<<2); elem->pinfo.tcp_conn[i]->tcp_data_fd = clnet_fd; addr_cpy(&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr),&(elem->pinfo.local_addr)); addr_set_port(&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr),0); addr_bind(clnet_fd, &(elem->pinfo.tcp_conn[i]->tcp_data_local_addr),1); addr_get_from_sock(clnet_fd,&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr)); continue; } else { perror("connect"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot connect to remote addr\n", __FUNCTION__); exit(-1); } } else { break; } } } if(use_secure) { elem->pinfo.tcp_conn[i]->tcp_data_ssl = tls_connect(elem->pinfo.tcp_conn[i]->tcp_data_fd, &(elem->pinfo.remote_addr)); if(!(elem->pinfo.tcp_conn[i]->tcp_data_ssl)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot SSL connect to remote addr\n", __FUNCTION__); exit(-1); } } if(turn_tcp_connection_bind(clnet_verbose, &(elem->pinfo), elem->pinfo.tcp_conn[i],0)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot BIND to tcp connection\n", __FUNCTION__); } else { socket_set_nonblocking(clnet_fd); struct event* ev = event_new(client_event_base,clnet_fd, EV_READ|EV_PERSIST,client_input_handler, elem); event_add(ev,NULL); elem->input_tcp_data_ev = ev; addr_debug_print(clnet_verbose, &(elem->pinfo.remote_addr), "TCP data network connected to"); } } ///////////////////////////////////////////////// turnserver-3.2.3.1/src/apps/uclient/uclient.h000644 001751 001751 00000007117 12315706777 021053 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __UCLIENT_ECHO__ #define __UCLIENT_ECHO__ #include "ns_turn_utils.h" #include "stun_buffer.h" #include "session.h" #include #ifdef __cplusplus extern "C" { #endif ////////////////////////////////////////////// #define STOPPING_TIME (5) #define STARTING_TCP_RELAY_TIME (10) extern int clmessage_length; extern int do_not_use_channel; extern int clnet_verbose; extern int use_tcp; extern int use_secure; extern int use_short_term; extern char cert_file[1025]; extern char pkey_file[1025]; extern int hang_on; extern int c2c; extern ioa_addr peer_addr; extern int no_rtcp; extern int default_address_family; extern int dont_fragment; extern u08bits g_uname[STUN_MAX_USERNAME_SIZE+1]; extern st_password_t g_upwd; extern char g_auth_secret[1025]; extern int g_use_auth_secret_with_timestamp; extern int use_fingerprints; extern SSL_CTX *root_tls_ctx[32]; extern int root_tls_ctx_num; extern int RTP_PACKET_INTERVAL; extern u08bits relay_transport; extern unsigned char client_ifname[1025]; extern struct event_base* client_event_base; extern int passive_tcp; extern int mandatory_channel_padding; extern int negative_test; extern int negative_protocol_test; extern int dos; extern SHATYPE shatype; extern int mobility; extern int no_permissions; extern int extra_requests; #define is_TCP_relay() (relay_transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE) void start_mclient(const char *remote_address, int port, const unsigned char* ifname, const char *local_address, int messagenumber, int mclient); int send_buffer(app_ur_conn_info *clnet_info, stun_buffer* message, int data_connection, app_tcp_conn_info *atc); int recv_buffer(app_ur_conn_info *clnet_info, stun_buffer* message, int sync, app_tcp_conn_info *atc); void client_input_handler(evutil_socket_t fd, short what, void* arg); turn_credential_type get_turn_credentials_type(void); int add_integrity(app_ur_conn_info *clnet_info, stun_buffer *message); void recalculate_restapi_hmac(void); //////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__UCLIENT_ECHO__ turnserver-3.2.3.1/src/apps/uclient/mainuclient.c000644 001751 001751 00000034016 12315706777 021711 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "apputils.h" #include "uclient.h" #include "ns_turn_utils.h" #include "apputils.h" #include "session.h" #include "stun_buffer.h" #include #include #include #include #include #include #include #include #include /////////////// extern definitions ///////////////////// int clmessage_length=100; int do_not_use_channel=0; int c2c=0; int clnet_verbose=TURN_VERBOSE_NONE; int use_tcp=0; int use_secure=0; int use_short_term=0; int hang_on=0; ioa_addr peer_addr; int no_rtcp = 0; int default_address_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT; int dont_fragment = 0; u08bits g_uname[STUN_MAX_USERNAME_SIZE+1]; st_password_t g_upwd; char g_auth_secret[1025]="\0"; int g_use_auth_secret_with_timestamp = 0; int use_fingerprints = 1; static char ca_cert_file[1025]=""; static char cipher_suite[1025]=""; char cert_file[1025]=""; char pkey_file[1025]=""; SSL_CTX *root_tls_ctx[32]; int root_tls_ctx_num = 0; u08bits relay_transport = STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE; unsigned char client_ifname[1025] = ""; int passive_tcp = 0; int mandatory_channel_padding = 0; int negative_test = 0; int negative_protocol_test = 0; int dos = 0; SHATYPE shatype = SHATYPE_SHA1; int mobility = 0; int no_permissions = 0; int extra_requests = 0; //////////////// local definitions ///////////////// static char Usage[] = "Usage: uclient [flags] [options] turn-server-ip-address\n" "Flags:\n" " -t TCP (default - UDP).\n" " -T TCP relay transport (default - UDP). Implies options -t, -y, -c, and ignores \n" " options -s, -e, -r and -g.\n" " -P Passive TCP (RFC6062 with active peer). Implies -T.\n" " -S Secure connection: TLS for TCP, DTLS for UDP.\n" " -U Secure connection with eNULL cipher.\n" " -v Verbose.\n" " -s Use send method.\n" " -y Use client-to-client connections.\n" " -h Hang on indefinitely after the last sent packet.\n" " -c No rtcp connections.\n" " -x IPv6 relay address requested.\n" " -X IPv4 relay address explicitly requested.\n" " -g Include DONT_FRAGMENT option.\n" " -A Use short-term credentials mechanism. By default, the program uses\n" " the long-term credentials mechanism if authentication is required.\n" " -D Mandatory channel padding (like in pjnath).\n" " -N Negative tests (some limited cases only).\n" " -R Negative protocol tests.\n" " -O DOS attack mode (quick connect and exit).\n" " -H SHA256 digest function for message integrity calculation.\n" " Without this option, by default, SHA1 is used.\n" " -M ICE Mobility engaged.\n" " -I Do not set permissions on TURN relay endpoints\n" " (for testing the non-standard server relay functionality).\n" " -G Generate extra requests (create permissions, channel bind).\n" "Options:\n" " -l Message length (Default: 100 Bytes).\n" " -i Certificate file (for secure connections only, optional).\n" " -k Private key file (for secure connections only).\n" " -E CA file for server certificate verification, \n" " if the server certificate to be verified.\n" " -F Cipher suite for TLS/DTLS. Default value is DEFAULT.\n" " -p TURN server port (Default: 3478 unsecure, 5349 secure).\n" " -n Number of messages to send (Default: 5).\n" " -d Local interface device (optional).\n" " -L Local address.\n" " -m Number of clients (default is 1).\n" " -e Peer address.\n" " -r Peer port (default 3480).\n" " -z Per-session packet interval in milliseconds (default is 20 ms).\n" " -u STUN/TURN user name.\n" " -w STUN/TURN user password.\n" " -W TURN REST API authentication secret. Is not compatible with -A option.\n" " -C TURN REST API username/timestamp separator symbol (character). The default value is ':'.\n"; ////////////////////////////////////////////////// void recalculate_restapi_hmac(void) { if (g_use_auth_secret_with_timestamp) { u08bits hmac[MAXSHASIZE]; unsigned int hmac_len; hmac_len = SHA256SIZEBYTES; hmac[0] = 0; if (stun_calculate_hmac(g_uname, strlen((char*) g_uname), (u08bits*) g_auth_secret, strlen(g_auth_secret), hmac, &hmac_len, SHATYPE_SHA256) >= 0) { size_t pwd_length = 0; char *pwd = base64_encode(hmac, hmac_len, &pwd_length); if (pwd) { if (pwd_length > 0) { ns_bcopy(pwd,g_upwd,pwd_length); g_upwd[pwd_length] = 0; } } free(pwd); } } } int main(int argc, char **argv) { int port = 0; int messagenumber = 5; char local_addr[256]; int c; int mclient = 1; char peer_address[129] = "\0"; int peer_port = PEER_DEFAULT_PORT; char rest_api_separator = ':'; int use_null_cipher=0; set_logfile("stdout"); set_execdir(); set_system_parameters(0); ns_bzero(local_addr, sizeof(local_addr)); while ((c = getopt(argc, argv, "d:p:l:n:L:m:e:r:u:w:i:k:z:W:C:E:F:vsyhcxXgtTSAPDNOUHMRIG")) != -1) { switch (c){ case 'G': extra_requests = 1; break; case 'F': STRCPY(cipher_suite,optarg); break; case 'I': no_permissions = 1; break; case 'M': mobility = 1; break; case 'H': shatype = SHATYPE_SHA256; break; case 'E': { char* fn = find_config_file(optarg,1); if(!fn) { fprintf(stderr,"ERROR: file %s not found\n",optarg); exit(-1); } STRCPY(ca_cert_file,fn); } break; case 'O': dos = 1; break; case 'C': rest_api_separator=*optarg; break; case 'D': mandatory_channel_padding = 1; break; case 'N': negative_test = 1; break; case 'R': negative_protocol_test = 1; break; case 'z': RTP_PACKET_INTERVAL = atoi(optarg); break; case 'A': use_short_term = 1; break; case 'u': STRCPY(g_uname, optarg); break; case 'w': STRCPY(g_upwd, optarg); break; case 'g': dont_fragment = 1; break; case 'd': STRCPY(client_ifname, optarg); break; case 'x': default_address_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6; break; case 'X': default_address_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4; break; case 'l': clmessage_length = atoi(optarg); break; case 's': do_not_use_channel = 1; break; case 'n': messagenumber = atoi(optarg); break; case 'p': port = atoi(optarg); break; case 'L': STRCPY(local_addr, optarg); break; case 'e': STRCPY(peer_address, optarg); break; case 'r': peer_port = atoi(optarg); break; case 'v': clnet_verbose = TURN_VERBOSE_NORMAL; break; case 'h': hang_on = 1; break; case 'c': no_rtcp = 1; break; case 'm': mclient = atoi(optarg); break; case 'y': c2c = 1; break; case 't': use_tcp = 1; break; case 'P': passive_tcp = 1; /* implies 'T': */ case 'T': relay_transport = STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE; break; case 'U': use_null_cipher = 1; /* implies 'S' */ /* no break */ case 'S': use_secure = 1; break; case 'W': g_use_auth_secret_with_timestamp = 1; STRCPY(g_auth_secret,optarg); break; case 'i': { char* fn = find_config_file(optarg,1); if(!fn) { fprintf(stderr,"ERROR: file %s not found\n",optarg); exit(-1); } STRCPY(cert_file,fn); free(fn); } break; case 'k': { char* fn = find_config_file(optarg,1); if(!fn) { fprintf(stderr,"ERROR: file %s not found\n",optarg); exit(-1); } STRCPY(pkey_file,fn); free(fn); } break; default: fprintf(stderr, "%s\n", Usage); exit(1); } } if(g_use_auth_secret_with_timestamp) { if(use_short_term) { fprintf(stderr,"ERROR: You cannot use authentication secret (REST API) with short-term credentials mechanism.\n"); exit(-1); } { char new_uname[1025]; const unsigned long exp_time = 600; /* one day */ if(g_uname[0]) { snprintf(new_uname,sizeof(new_uname),"%lu%c%s",(unsigned long)time(NULL) + exp_time,rest_api_separator, (char*)g_uname); } else { snprintf(new_uname,sizeof(new_uname),"%lu", (unsigned long)time(NULL) + exp_time); } STRCPY(g_uname,new_uname); } { u08bits hmac[MAXSHASIZE]; unsigned int hmac_len; switch(shatype) { case SHATYPE_SHA256: hmac_len = SHA256SIZEBYTES; break; default: hmac_len = SHA1SIZEBYTES; }; hmac[0]=0; if(stun_calculate_hmac(g_uname, strlen((char*)g_uname), (u08bits*)g_auth_secret, strlen(g_auth_secret), hmac, &hmac_len, shatype)>=0) { size_t pwd_length = 0; char *pwd = base64_encode(hmac,hmac_len,&pwd_length); if(pwd) { if(pwd_length>0) { ns_bcopy(pwd,g_upwd,pwd_length); g_upwd[pwd_length]=0; } } free(pwd); } } } if(is_TCP_relay()) { dont_fragment = 0; no_rtcp = 1; c2c = 1; use_tcp = 1; do_not_use_channel = 1; } if(port == 0) { if(use_secure) port = DEFAULT_STUN_TLS_PORT; else port = DEFAULT_STUN_PORT; } if (clmessage_length < (int) sizeof(message_info)) clmessage_length = (int) sizeof(message_info); const int max_header = 100; if(clmessage_length > (int)(STUN_BUFFER_SIZE-max_header)) { fprintf(stderr,"Message length was corrected to %d\n",(STUN_BUFFER_SIZE-max_header)); clmessage_length = (int)(STUN_BUFFER_SIZE-max_header); } if (optind >= argc) { fprintf(stderr, "%s\n", Usage); exit(-1); } if (!c2c) { if (make_ioa_addr((const u08bits*) peer_address, peer_port, &peer_addr) < 0) return -1; if(peer_addr.ss.sa_family == AF_INET6) default_address_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6; } /* SSL Init ==>> */ if(use_secure) { SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); const char *csuite = "ALL:SSLv2"; //"AES256-SHA" "DH" if(use_null_cipher) csuite = "eNULL"; else if(cipher_suite[0]) csuite=cipher_suite; if(use_tcp) { #ifndef OPENSSL_NO_SSL2 root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(SSLv2_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; #endif root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(SSLv23_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(SSLv3_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(TLSv1_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; #if defined(SSL_TXT_TLSV1_1) root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(TLSv1_1_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; #if defined(SSL_TXT_TLSV1_2) root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(TLSv1_2_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; #endif #endif } else { #if defined(TURN_NO_DTLS) fprintf(stderr,"ERROR: DTLS is not supported.\n"); exit(-1); #else if(OPENSSL_VERSION_NUMBER < 0x10000000L) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: OpenSSL version is rather old, DTLS may not be working correctly.\n"); } root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(DTLSv1_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; #endif } int sslind = 0; for(sslind = 0; sslind #include #include "ns_turn_ioaddr.h" #include "ns_turn_utils.h" #include "stun_buffer.h" #include "apputils.h" #include #ifdef __cplusplus extern "C" { #endif ///////// types //////////// enum _UR_STATE { UR_STATE_UNKNOWN=0, UR_STATE_READY, UR_STATE_DONE }; typedef enum _UR_STATE UR_STATE; //////////////// session info ////////////////////// typedef struct { /* RFC 6062 */ u32bits cid; ioa_addr tcp_data_local_addr; ioa_socket_raw tcp_data_fd; SSL *tcp_data_ssl; int tcp_data_bound; } app_tcp_conn_info; typedef struct { ioa_addr local_addr; char lsaddr[129]; ioa_addr remote_addr; char rsaddr[129]; char ifname[129]; ioa_addr peer_addr; ioa_socket_raw fd; SSL *ssl; int broken; u08bits nonce[STUN_MAX_NONCE_SIZE+1]; u08bits realm[STUN_MAX_REALM_SIZE+1]; /* RFC 6062 */ app_tcp_conn_info **tcp_conn; size_t tcp_conn_number; int is_peer; SHATYPE shatype; char s_mobile_id[33]; } app_ur_conn_info; typedef struct { app_ur_conn_info pinfo; UR_STATE state; unsigned int ctime; uint16_t chnum; int wait_cycles; int timer_cycle; int known_mtu; int completed; struct event *input_ev; struct event *input_tcp_data_ev; stun_buffer in_buffer; stun_buffer out_buffer; u32bits refresh_time; u32bits finished_time; //Msg counters: int tot_msgnum; int wmsgnum; int rmsgnum; int recvmsgnum; u32bits recvtimems; u32bits to_send_timems; //Statistics: size_t loss; u64bits latency; u64bits jitter; } app_ur_session; /////////////////////////////////////////////////////// typedef struct _message_info { int msgnum; u64bits mstime; } message_info; /////////////////////////////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__SESSION__ turnserver-3.2.3.1/src/apps/uclient/uclient.c000644 001751 001751 00000110773 12315706777 021051 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "apputils.h" #include "uclient.h" #include "startuclient.h" #include "ns_turn_utils.h" #include "session.h" #include #include #include static int verbose_packets=0; static size_t current_clients_number = 0; static int start_full_timer=0; static u32bits tot_messages=0; static u32bits tot_send_messages=0; static u64bits tot_send_bytes = 0; static u32bits tot_recv_messages=0; static u64bits tot_recv_bytes = 0; static u64bits tot_send_dropped = 0; struct event_base* client_event_base=NULL; static int client_write(app_ur_session *elem); static int client_shutdown(app_ur_session *elem); static u64bits current_time = 0; static u64bits current_mstime = 0; static char buffer_to_send[65536]="\0"; static int total_clients = 0; /* Patch for unlimited number of clients provided by ucudbm@gmail.com */ static app_ur_session** elems = NULL; #define SLEEP_INTERVAL (234) int RTP_PACKET_INTERVAL = 20; static inline s64bits time_minus(u64bits t1, u64bits t2) { return ( (s64bits)t1 - (s64bits)t2 ); } static u64bits total_loss = 0; static u64bits total_jitter = 0; static u64bits total_latency = 0; static u64bits min_latency = 0xFFFFFFFF; static u64bits max_latency = 0; static u64bits min_jitter = 0xFFFFFFFF; static u64bits max_jitter = 0; static int show_statistics = 0; /////////////////////////////////////////////////////////////////////////////// static void __turn_getMSTime(void) { static u64bits start_sec = 0; struct timespec tp={0,0}; #if defined(CLOCK_REALTIME) clock_gettime(CLOCK_REALTIME, &tp); #else tp.tv_sec = time(NULL); #endif if(!start_sec) start_sec = tp.tv_sec; if(current_time != (u64bits)((u64bits)(tp.tv_sec)-start_sec)) show_statistics = 1; current_time = (u64bits)((u64bits)(tp.tv_sec)-start_sec); current_mstime = (u64bits)((current_time * 1000) + (tp.tv_nsec/1000000)); } //////////////////////////////////////////////////////////////////// static int refresh_channel(app_ur_session* elem, u16bits method); //////////////////////// SS //////////////////////////////////////// static app_ur_session* init_app_session(app_ur_session *ss) { if(ss) { ns_bzero(ss,sizeof(app_ur_session)); ss->pinfo.fd=-1; ss->pinfo.shatype = shatype; } return ss; } static app_ur_session* create_new_ss(void) { ++current_clients_number; return init_app_session((app_ur_session*) turn_malloc(sizeof(app_ur_session))); } static void uc_delete_session_elem_data(app_ur_session* cdi) { if(cdi) { EVENT_DEL(cdi->input_ev); EVENT_DEL(cdi->input_tcp_data_ev); if(cdi->pinfo.tcp_conn) { int i = 0; for(i=0;i<(int)(cdi->pinfo.tcp_conn_number);++i) { if(cdi->pinfo.tcp_conn[i]) { if(cdi->pinfo.tcp_conn[i]->tcp_data_ssl && !(cdi->pinfo.broken)) { if(!(SSL_get_shutdown(cdi->pinfo.tcp_conn[i]->tcp_data_ssl) & SSL_SENT_SHUTDOWN)) { SSL_set_shutdown(cdi->pinfo.tcp_conn[i]->tcp_data_ssl, SSL_RECEIVED_SHUTDOWN); SSL_shutdown(cdi->pinfo.tcp_conn[i]->tcp_data_ssl); } if(cdi->pinfo.tcp_conn[i]->tcp_data_ssl) { SSL_free(cdi->pinfo.tcp_conn[i]->tcp_data_ssl); cdi->pinfo.tcp_conn[i]->tcp_data_ssl = NULL; } if(cdi->pinfo.tcp_conn[i]->tcp_data_fd>=0) { socket_closesocket(cdi->pinfo.tcp_conn[i]->tcp_data_fd); cdi->pinfo.tcp_conn[i]->tcp_data_fd=-1; } turn_free(cdi->pinfo.tcp_conn[i], 111); cdi->pinfo.tcp_conn[i]=NULL; } } } cdi->pinfo.tcp_conn_number=0; turn_free(cdi->pinfo.tcp_conn, 111); cdi->pinfo.tcp_conn=NULL; } if(cdi->pinfo.ssl && !(cdi->pinfo.broken)) { if(!(SSL_get_shutdown(cdi->pinfo.ssl) & SSL_SENT_SHUTDOWN)) { SSL_set_shutdown(cdi->pinfo.ssl, SSL_RECEIVED_SHUTDOWN); SSL_shutdown(cdi->pinfo.ssl); } } if(cdi->pinfo.ssl) { SSL_free(cdi->pinfo.ssl); cdi->pinfo.ssl = NULL; } if(cdi->pinfo.fd>=0) { socket_closesocket(cdi->pinfo.fd); } cdi->pinfo.fd=-1; } } static int remove_all_from_ss(app_ur_session* ss) { if (ss) { uc_delete_session_elem_data(ss); --current_clients_number; } return 0; } /////////////////////////////////////////////////////////////////////////////// int send_buffer(app_ur_conn_info *clnet_info, stun_buffer* message, int data_connection, app_tcp_conn_info *atc) { int rc = 0; int ret = -1; char *buffer = (char*) (message->buf); if(negative_protocol_test && (message->len>0)) { if(random()%10 == 0) { int np = (int)((unsigned long)random()%10); while(np-->0) { int pos = (int)((unsigned long)random()%(unsigned long)message->len); int val = (int)((unsigned long)random()%256); message->buf[pos]=(u08bits)val; } } } SSL *ssl = clnet_info->ssl; ioa_socket_raw fd = clnet_info->fd; if(data_connection) { if(atc) { ssl = atc->tcp_data_ssl; fd = atc->tcp_data_fd; } else if(is_TCP_relay()){ TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "trying to send tcp data buffer over unready connection: size=%d\n",(int)(message->len)); return -1; } } if (ssl) { int message_sent = 0; while (!message_sent) { if (SSL_get_shutdown(ssl)) { return -1; } int len = 0; do { len = SSL_write(ssl, buffer, (int)message->len); } while (len < 0 && ((errno == EINTR) || (errno == ENOBUFS))); if(len == (int)message->len) { if (clnet_verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "buffer sent: size=%d\n",len); } message_sent = 1; ret = len; } else { switch (SSL_get_error(ssl, len)){ case SSL_ERROR_NONE: /* Try again ? */ break; case SSL_ERROR_WANT_WRITE: /* Just try again later */ break; case SSL_ERROR_WANT_READ: /* continue with reading */ break; case SSL_ERROR_ZERO_RETURN: /* Try again */ break; case SSL_ERROR_SYSCALL: TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Socket write error 111.666: \n"); if (handle_socket_error()) break; case SSL_ERROR_SSL: { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SSL write error: \n"); char buf[1024]; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s (%d)\n", ERR_error_string(ERR_get_error(),buf), SSL_get_error(ssl, len)); } default: clnet_info->broken = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Unexpected error while writing!\n"); return -1; } } } } else if (fd >= 0) { size_t left = (size_t) (message->len); while (left > 0) { do { rc = send(fd, buffer, left, 0); } while (rc < 0 && ((errno == EINTR) || (errno == ENOBUFS))); if (rc > 0) { left -= (size_t) rc; buffer += rc; } else { tot_send_dropped+=1; break; } } if (left > 0) return -1; ret = (int) message->len; } return ret; } int recv_buffer(app_ur_conn_info *clnet_info, stun_buffer* message, int sync, app_tcp_conn_info *atc) { int rc = 0; ioa_socket_raw fd = clnet_info->fd; if (atc) fd = atc->tcp_data_fd; SSL* ssl = clnet_info->ssl; if (atc) ssl = atc->tcp_data_ssl; if (!use_secure && !use_tcp && fd >= 0) { /* Plain UDP */ do { rc = recv(fd, message->buf, sizeof(message->buf) - 1, 0); if (rc < 0 && errno == EAGAIN && sync) errno = EINTR; } while (rc < 0 && (errno == EINTR)); if (rc < 0) { return -1; } message->len = rc; } else if (use_secure && ssl && !(clnet_info->broken)) { /* TLS/DTLS */ int message_received = 0; int cycle = 0; while (!message_received && cycle++ < 100) { if (SSL_get_shutdown(ssl)) return -1; rc = 0; do { rc = SSL_read(ssl, message->buf, sizeof(message->buf) - 1); if (rc < 0 && errno == EAGAIN && sync) continue; } while (rc < 0 && (errno == EINTR)); if (rc > 0) { if (clnet_verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "response received: size=%d\n", rc); } message->len = rc; message_received = 1; } else { int sslerr = SSL_get_error(ssl, rc); switch (sslerr) { case SSL_ERROR_NONE: /* Try again ? */ break; case SSL_ERROR_WANT_WRITE: /* Just try again later */ break; case SSL_ERROR_WANT_READ: /* continue with reading */ break; case SSL_ERROR_ZERO_RETURN: /* Try again */ break; case SSL_ERROR_SYSCALL: TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Socket read error 111.999: \n"); if (handle_socket_error()) break; case SSL_ERROR_SSL: { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SSL write error: \n"); char buf[1024]; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s (%d)\n", ERR_error_string(ERR_get_error(), buf), SSL_get_error(ssl, rc)); } default: clnet_info->broken = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Unexpected error while reading: rc=%d, sslerr=%d\n", rc, sslerr); return -1; } if (!sync) break; } } } else if (!use_secure && use_tcp && fd >= 0) { /* Plain TCP */ do { rc = recv(fd, message->buf, sizeof(message->buf) - 1, MSG_PEEK); if ((rc < 0) && (errno == EAGAIN) && sync) { errno = EINTR; } } while (rc < 0 && (errno == EINTR)); if (rc > 0) { int mlen = rc; size_t app_msg_len = (size_t) rc; if (!atc) { mlen = stun_get_message_len_str(message->buf, rc, 1, &app_msg_len); } else { if (!sync) mlen = clmessage_length; if (mlen > clmessage_length) mlen = clmessage_length; app_msg_len = (size_t) mlen; } if (mlen > 0) { int rcr = 0; int rsf = 0; int cycle = 0; while (rsf < mlen && cycle++ < 128) { do { rcr = recv(fd, message->buf + rsf, (size_t) mlen - (size_t) rsf, 0); if (rcr < 0 && errno == EAGAIN && sync) errno = EINTR; } while (rcr < 0 && (errno == EINTR)); if (rcr > 0) rsf += rcr; } if (rsf < 1) return -1; if (rsf < (int) app_msg_len) { if ((size_t) (app_msg_len / (size_t) rsf) * ((size_t) (rsf)) != app_msg_len) { return -1; } } message->len = app_msg_len; rc = app_msg_len; } else { rc = 0; } } } return rc; } static int client_read(app_ur_session *elem, int is_tcp_data, app_tcp_conn_info *atc) { if (!elem) return -1; if (elem->state != UR_STATE_READY) return -1; elem->ctime = current_time; app_ur_conn_info *clnet_info = &(elem->pinfo); int err_code = 0; u08bits err_msg[129]; int rc = 0; int applen = 0; if (clnet_verbose && verbose_packets) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "before read ...\n"); } rc = recv_buffer(clnet_info, &(elem->in_buffer), 0, atc); if (clnet_verbose && verbose_packets) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "read %d bytes\n", (int) rc); } if (rc > 0) { elem->in_buffer.len = rc; uint16_t chnumber = 0; const message_info *mi = NULL; size_t buffers = 1; if(is_tcp_data) { if ((int)elem->in_buffer.len == clmessage_length) { mi = (message_info*)(elem->in_buffer.buf); } } else if (stun_is_indication(&(elem->in_buffer))) { if(use_short_term) { SHATYPE sht = elem->pinfo.shatype; if(stun_check_message_integrity_str(get_turn_credentials_type(), elem->in_buffer.buf, (size_t)(elem->in_buffer.len), g_uname, elem->pinfo.realm, g_upwd, sht)<1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Wrong integrity in indication message 0x%x received from server\n",(unsigned int)stun_get_method(&(elem->in_buffer))); return -1; } } uint16_t method = stun_get_method(&elem->in_buffer); if((method == STUN_METHOD_CONNECTION_ATTEMPT)&& is_TCP_relay()) { stun_attr_ref sar = stun_attr_get_first(&(elem->in_buffer)); u32bits cid = 0; while(sar) { int attr_type = stun_attr_get_type(sar); if(attr_type == STUN_ATTRIBUTE_CONNECTION_ID) { cid = *((const u32bits*)stun_attr_get_value(sar)); break; } sar = stun_attr_get_next_str(elem->in_buffer.buf,elem->in_buffer.len,sar); } if(negative_test) { tcp_data_connect(elem,(u64bits)random()); } else { /* positive test */ tcp_data_connect(elem,cid); } return rc; } else if (method != STUN_METHOD_DATA) { TURN_LOG_FUNC( TURN_LOG_LEVEL_INFO, "ERROR: received indication message has wrong method: 0x%x\n", (int) method); return rc; } else { stun_attr_ref sar = stun_attr_get_first_by_type(&(elem->in_buffer), STUN_ATTRIBUTE_DATA); if (!sar) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "ERROR: received DATA message has no data, size=%d\n", rc); return rc; } int rlen = stun_attr_get_len(sar); applen = rlen; if (rlen != clmessage_length) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "ERROR: received DATA message has wrong len: %d, must be %d\n", rlen, clmessage_length); tot_recv_bytes += applen; return rc; } const u08bits* data = stun_attr_get_value(sar); mi = (const message_info*) data; } } else if (stun_is_success_response(&(elem->in_buffer))) { if(elem->pinfo.nonce[0] || use_short_term) { SHATYPE sht = elem->pinfo.shatype; if(stun_check_message_integrity_str(get_turn_credentials_type(), elem->in_buffer.buf, (size_t)(elem->in_buffer.len), g_uname, elem->pinfo.realm, g_upwd, sht)<1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Wrong integrity in success message 0x%x received from server\n",(unsigned int)stun_get_method(&(elem->in_buffer))); return -1; } } if(is_TCP_relay() && (stun_get_method(&(elem->in_buffer)) == STUN_METHOD_CONNECT)) { stun_attr_ref sar = stun_attr_get_first(&(elem->in_buffer)); u32bits cid = 0; while(sar) { int attr_type = stun_attr_get_type(sar); if(attr_type == STUN_ATTRIBUTE_CONNECTION_ID) { cid = *((const u32bits*)stun_attr_get_value(sar)); break; } sar = stun_attr_get_next_str(elem->in_buffer.buf,elem->in_buffer.len,sar); } tcp_data_connect(elem,cid); } return rc; } else if (stun_is_challenge_response_str(elem->in_buffer.buf, (size_t)elem->in_buffer.len, &err_code,err_msg,sizeof(err_msg), clnet_info->realm,clnet_info->nonce)) { if(err_code == SHA_TOO_WEAK && (elem->pinfo.shatype == SHATYPE_SHA1)) { elem->pinfo.shatype = SHATYPE_SHA256; recalculate_restapi_hmac(); } if(is_TCP_relay() && (stun_get_method(&(elem->in_buffer)) == STUN_METHOD_CONNECT)) { turn_tcp_connect(clnet_verbose, &(elem->pinfo), &(elem->pinfo.peer_addr)); } else if(stun_get_method(&(elem->in_buffer)) == STUN_METHOD_REFRESH) { refresh_channel(elem, stun_get_method(&elem->in_buffer)); } return rc; } else if (stun_is_error_response(&(elem->in_buffer), NULL,NULL,0)) { return rc; } else if (stun_is_channel_message(&(elem->in_buffer), &chnumber, use_tcp)) { if (elem->chnum != chnumber) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "ERROR: received message has wrong channel: %d\n", (int) chnumber); return rc; } if (elem->in_buffer.len >= 4) { if (((int)(elem->in_buffer.len-4) < clmessage_length) || ((int)(elem->in_buffer.len-4) > clmessage_length + 3)) { TURN_LOG_FUNC( TURN_LOG_LEVEL_INFO, "ERROR: received buffer have wrong length: %d, must be %d, len=%d\n", rc, clmessage_length + 4,(int)elem->in_buffer.len); return rc; } mi = (message_info*)(elem->in_buffer.buf + 4); applen = elem->in_buffer.len -4; } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "ERROR: Unknown message received of size: %d\n",(int)(elem->in_buffer.len)); return rc; } if(mi) { /* printf("%s: 111.111: msgnum=%d, rmsgnum=%d, sent=%lu, recv=%lu\n",__FUNCTION__, mi->msgnum,elem->recvmsgnum,(unsigned long)mi->mstime,(unsigned long)current_mstime); */ if(mi->msgnum != elem->recvmsgnum+1) ++(elem->loss); else { u64bits clatency = (u64bits)time_minus(current_mstime,mi->mstime); if(clatency>max_latency) max_latency = clatency; if(clatencylatency += clatency; if(elem->rmsgnum>0) { u64bits cjitter = abs((int)(current_mstime-elem->recvtimems)-RTP_PACKET_INTERVAL); if(cjitter>max_jitter) max_jitter = cjitter; if(cjitterjitter += cjitter; } } elem->recvmsgnum = mi->msgnum; } elem->rmsgnum+=buffers; tot_recv_messages+=buffers; if(applen > 0) tot_recv_bytes += applen; else tot_recv_bytes += elem->in_buffer.len; elem->recvtimems=current_mstime; elem->wait_cycles = 0; } else if(rc == 0) { return 0; } else { return -1; } return rc; } static int client_shutdown(app_ur_session *elem) { if(!elem) return -1; elem->state=UR_STATE_DONE; elem->ctime=current_time; remove_all_from_ss(elem); if (clnet_verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"done, connection 0x%lx closed.\n",(long)elem); return 0; } static int client_write(app_ur_session *elem) { if(!elem) return -1; if(elem->state!=UR_STATE_READY) return -1; elem->ctime=current_time; message_info *mi = (message_info*)buffer_to_send; mi->msgnum=elem->wmsgnum; mi->mstime=current_mstime; app_tcp_conn_info *atc=NULL; if (is_TCP_relay()) { memcpy(elem->out_buffer.buf, buffer_to_send, clmessage_length); elem->out_buffer.len = clmessage_length; if(elem->pinfo.is_peer) { if(send(elem->pinfo.fd, elem->out_buffer.buf, clmessage_length, 0)>=0) { ++elem->wmsgnum; elem->to_send_timems += RTP_PACKET_INTERVAL; tot_send_messages++; tot_send_bytes += clmessage_length; } return 0; } if (!(elem->pinfo.tcp_conn) || !(elem->pinfo.tcp_conn_number)) { return -1; } int i = (unsigned int)(random()) % elem->pinfo.tcp_conn_number; atc = elem->pinfo.tcp_conn[i]; if(!atc->tcp_data_bound) { printf("%s: Uninitialized atc: i=%d, atc=0x%lx\n",__FUNCTION__,i,(long)atc); return -1; } } else if(!do_not_use_channel) { /* Let's always do padding: */ stun_init_channel_message(elem->chnum, &(elem->out_buffer), clmessage_length, mandatory_channel_padding || use_tcp); memcpy(elem->out_buffer.buf+4,buffer_to_send,clmessage_length); } else { stun_init_indication(STUN_METHOD_SEND, &(elem->out_buffer)); stun_attr_add(&(elem->out_buffer), STUN_ATTRIBUTE_DATA, buffer_to_send, clmessage_length); stun_attr_add_addr(&(elem->out_buffer),STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &(elem->pinfo.peer_addr)); if(dont_fragment) stun_attr_add(&(elem->out_buffer), STUN_ATTRIBUTE_DONT_FRAGMENT, NULL, 0); if (use_short_term) { if(add_integrity(&(elem->pinfo), &(elem->out_buffer))<0) return -1; } if(use_fingerprints) stun_attr_add_fingerprint_str(elem->out_buffer.buf,(size_t*)&(elem->out_buffer.len)); } if (elem->out_buffer.len > 0) { if (clnet_verbose && verbose_packets) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "before write ...\n"); } int rc=send_buffer(&(elem->pinfo),&(elem->out_buffer),1,atc); ++elem->wmsgnum; elem->to_send_timems += RTP_PACKET_INTERVAL; if(rc >= 0) { if (clnet_verbose && verbose_packets) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "wrote %d bytes\n", (int) rc); } tot_send_messages++; tot_send_bytes += clmessage_length; } else { return -1; } } return 0; } void client_input_handler(evutil_socket_t fd, short what, void* arg) { if(!(what&EV_READ)||!arg) return; UNUSED_ARG(fd); app_ur_session* elem = (app_ur_session*)arg; if(!elem) { return; } switch(elem->state) { case UR_STATE_READY: do { app_tcp_conn_info *atc = NULL; int is_tcp_data = 0; if(elem->pinfo.tcp_conn) { int i = 0; for(i=0;i<(int)(elem->pinfo.tcp_conn_number);++i) { if(elem->pinfo.tcp_conn[i]) { if((fd==elem->pinfo.tcp_conn[i]->tcp_data_fd) && (elem->pinfo.tcp_conn[i]->tcp_data_bound)) { is_tcp_data = 1; atc = elem->pinfo.tcp_conn[i]; break; } } } } int rc = client_read(elem, is_tcp_data, atc); if(rc<=0) break; } while(1); break; default: ; } } static void run_events(int short_burst) { struct timeval timeout; if(!short_burst) { timeout.tv_sec = 1; timeout.tv_usec = 0; } else { timeout.tv_sec = 0; timeout.tv_usec = 100000; } event_base_loopexit(client_event_base, &timeout); event_base_dispatch(client_event_base); } ////////////////////// main method ///////////////// static int start_client(const char *remote_address, int port, const unsigned char* ifname, const char *local_address, int messagenumber, int i) { app_ur_session* ss=create_new_ss(); app_ur_session* ss_rtcp=NULL; if(!no_rtcp) ss_rtcp = create_new_ss(); app_ur_conn_info clnet_info_probe; /* for load balancing probe */ ns_bzero(&clnet_info_probe,sizeof(clnet_info_probe)); clnet_info_probe.fd = -1; clnet_info_probe.shatype = shatype; app_ur_conn_info *clnet_info=&(ss->pinfo); app_ur_conn_info *clnet_info_rtcp=NULL; if(!no_rtcp) clnet_info_rtcp = &(ss_rtcp->pinfo); uint16_t chnum=0; uint16_t chnum_rtcp=0; start_connection(port, remote_address, ifname, local_address, clnet_verbose, &clnet_info_probe, clnet_info, &chnum, clnet_info_rtcp, &chnum_rtcp); if(clnet_info_probe.ssl) { SSL_free(clnet_info_probe.ssl); clnet_info_probe.ssl = NULL; clnet_info_probe.fd = -1; } else if(clnet_info_probe.fd != -1) { socket_closesocket(clnet_info_probe.fd); clnet_info_probe.fd = -1; } socket_set_nonblocking(clnet_info->fd); if(!no_rtcp) socket_set_nonblocking(clnet_info_rtcp->fd); struct event* ev = event_new(client_event_base,clnet_info->fd, EV_READ|EV_PERSIST,client_input_handler, ss); event_add(ev,NULL); struct event* ev_rtcp = NULL; if(!no_rtcp) { ev_rtcp = event_new(client_event_base,clnet_info_rtcp->fd, EV_READ|EV_PERSIST,client_input_handler, ss_rtcp); event_add(ev_rtcp,NULL); } ss->state=UR_STATE_READY; ss->input_ev=ev; ss->tot_msgnum=messagenumber; ss->recvmsgnum=-1; ss->chnum=chnum; if(!no_rtcp) { ss_rtcp->state=UR_STATE_READY; ss_rtcp->input_ev=ev_rtcp; ss_rtcp->tot_msgnum=ss->tot_msgnum; if(ss_rtcp->tot_msgnum<1) ss_rtcp->tot_msgnum=1; ss_rtcp->recvmsgnum=-1; ss_rtcp->chnum=chnum_rtcp; } elems[i]=ss; refresh_channel(ss,0); if(!no_rtcp) elems[i+1]=ss_rtcp; return 0; } static int start_c2c(const char *remote_address, int port, const unsigned char* ifname, const char *local_address, int messagenumber, int i) { app_ur_session* ss1=create_new_ss(); app_ur_session* ss1_rtcp=NULL; if(!no_rtcp) ss1_rtcp = create_new_ss(); app_ur_session* ss2=create_new_ss(); app_ur_session* ss2_rtcp=NULL; if(!no_rtcp) ss2_rtcp = create_new_ss(); app_ur_conn_info clnet_info_probe; /* for load balancing probe */ ns_bzero(&clnet_info_probe,sizeof(clnet_info_probe)); clnet_info_probe.fd = -1; clnet_info_probe.shatype = shatype; app_ur_conn_info *clnet_info1=&(ss1->pinfo); app_ur_conn_info *clnet_info1_rtcp=NULL; if(!no_rtcp) clnet_info1_rtcp = &(ss1_rtcp->pinfo); app_ur_conn_info *clnet_info2=&(ss2->pinfo); app_ur_conn_info *clnet_info2_rtcp=NULL; if(!no_rtcp) clnet_info2_rtcp = &(ss2_rtcp->pinfo); uint16_t chnum1=0; uint16_t chnum1_rtcp=0; uint16_t chnum2=0; uint16_t chnum2_rtcp=0; start_c2c_connection(port, remote_address, ifname, local_address, clnet_verbose, &clnet_info_probe, clnet_info1, &chnum1, clnet_info1_rtcp, &chnum1_rtcp, clnet_info2, &chnum2, clnet_info2_rtcp, &chnum2_rtcp); if(clnet_info_probe.ssl) { SSL_free(clnet_info_probe.ssl); clnet_info_probe.ssl = NULL; clnet_info_probe.fd = -1; } else if(clnet_info_probe.fd != -1) { socket_closesocket(clnet_info_probe.fd); clnet_info_probe.fd = -1; } socket_set_nonblocking(clnet_info1->fd); if(!no_rtcp) socket_set_nonblocking(clnet_info1_rtcp->fd); socket_set_nonblocking(clnet_info2->fd); if(!no_rtcp) socket_set_nonblocking(clnet_info2_rtcp->fd); struct event* ev1 = event_new(client_event_base,clnet_info1->fd, EV_READ|EV_PERSIST,client_input_handler, ss1); event_add(ev1,NULL); struct event* ev1_rtcp = NULL; if(!no_rtcp) { ev1_rtcp = event_new(client_event_base,clnet_info1_rtcp->fd, EV_READ|EV_PERSIST,client_input_handler, ss1_rtcp); event_add(ev1_rtcp,NULL); } struct event* ev2 = event_new(client_event_base,clnet_info2->fd, EV_READ|EV_PERSIST,client_input_handler, ss2); event_add(ev2,NULL); struct event* ev2_rtcp = NULL; if(!no_rtcp) { ev2_rtcp = event_new(client_event_base,clnet_info2_rtcp->fd, EV_READ|EV_PERSIST,client_input_handler, ss2_rtcp); event_add(ev2_rtcp,NULL); } ss1->state=UR_STATE_READY; ss1->input_ev=ev1; ss1->tot_msgnum=messagenumber; ss1->recvmsgnum=-1; ss1->chnum=chnum1; if(!no_rtcp) { ss1_rtcp->state=UR_STATE_READY; ss1_rtcp->input_ev=ev1_rtcp; ss1_rtcp->tot_msgnum=ss1->tot_msgnum; if(ss1_rtcp->tot_msgnum<1) ss1_rtcp->tot_msgnum=1; ss1_rtcp->recvmsgnum=-1; ss1_rtcp->chnum=chnum1_rtcp; } ss2->state=UR_STATE_READY; ss2->input_ev=ev2; ss2->tot_msgnum=ss1->tot_msgnum; ss2->recvmsgnum=-1; ss2->chnum=chnum2; if(!no_rtcp) { ss2_rtcp->state=UR_STATE_READY; ss2_rtcp->input_ev=ev2_rtcp; ss2_rtcp->tot_msgnum=ss1_rtcp->tot_msgnum; ss2_rtcp->recvmsgnum=-1; ss2_rtcp->chnum=chnum2_rtcp; } elems[i++]=ss1; if(!no_rtcp) elems[i++]=ss1_rtcp; elems[i++]=ss2; if(!no_rtcp) elems[i++]=ss2_rtcp; return 0; } static int refresh_channel(app_ur_session* elem, u16bits method) { stun_buffer message; app_ur_conn_info *clnet_info = &(elem->pinfo); if(clnet_info->is_peer) return 0; if (!method || (method == STUN_METHOD_REFRESH)) { stun_init_request(STUN_METHOD_REFRESH, &message); uint32_t lt = htonl(600); stun_attr_add(&message, STUN_ATTRIBUTE_LIFETIME, (const char*) <, 4); if(add_integrity(clnet_info, &message)<0) return -1; if(use_fingerprints) stun_attr_add_fingerprint_str(message.buf, (size_t*) &(message.len)); send_buffer(clnet_info, &message, 0,0); } if (!addr_any(&(elem->pinfo.peer_addr))) { if(!no_permissions) { if (!method || (method == STUN_METHOD_CREATE_PERMISSION)) { stun_init_request(STUN_METHOD_CREATE_PERMISSION, &message); stun_attr_add_addr(&message, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &(elem->pinfo.peer_addr)); if(add_integrity(clnet_info, &message)<0) return -1; if(use_fingerprints) stun_attr_add_fingerprint_str(message.buf, (size_t*) &(message.len)); send_buffer(&(elem->pinfo), &message, 0,0); } } if (!method || (method == STUN_METHOD_CHANNEL_BIND)) { if (STUN_VALID_CHANNEL(elem->chnum)) { stun_set_channel_bind_request(&message, &(elem->pinfo.peer_addr), elem->chnum); if(add_integrity(clnet_info, &message)<0) return -1; if(use_fingerprints) stun_attr_add_fingerprint_str(message.buf, (size_t*) &(message.len)); send_buffer(&(elem->pinfo), &message,1,0); } } } elem->refresh_time = current_mstime + 30*1000; return 0; } static inline int client_timer_handler(app_ur_session* elem) { if (elem) { if (!turn_time_before(current_mstime, elem->refresh_time)) { refresh_channel(elem, 0); } if(hang_on && elem->completed) return 0; if (!turn_time_before(current_mstime, elem->to_send_timems)) { if (elem->wmsgnum >= elem->tot_msgnum) { if (!turn_time_before(current_mstime, elem->finished_time) || (tot_recv_messages>=tot_messages)) { /* TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: elem=0x%x: 111.111: c=%d, t=%d, r=%d, w=%d\n",__FUNCTION__,(int)elem,elem->wait_cycles,elem->tot_msgnum,elem->rmsgnum,elem->wmsgnum); */ /* TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: 111.222: ly=%llu, ls=%llu, j=%llu\n",__FUNCTION__, (unsigned long long)elem->latency, (unsigned long long)elem->loss, (unsigned long long)elem->jitter); */ total_loss += elem->loss; elem->loss=0; total_latency += elem->latency; elem->latency=0; total_jitter += elem->jitter; elem->jitter=0; elem->completed = 1; if (!hang_on) { client_shutdown(elem); return 1; } else { return 0; } } } else { client_write(elem); elem->finished_time = current_mstime + STOPPING_TIME*1000; } } } return 0; } static void timer_handler(evutil_socket_t fd, short event, void *arg) { UNUSED_ARG(fd); UNUSED_ARG(event); UNUSED_ARG(arg); __turn_getMSTime(); if(start_full_timer) { int i = 0; for (i = 0; i < total_clients; ++i) { if (elems[i]) { int finished = client_timer_handler(elems[i]); if (finished) { elems[i] = NULL; } } } } } void start_mclient(const char *remote_address, int port, const unsigned char* ifname, const char *local_address, int messagenumber, int mclient) { if (mclient < 1) mclient = 1; total_clients = mclient; if(c2c) { //mclient must be a multiple of 4: if(!no_rtcp) mclient += ((4 - (mclient & 0x00000003)) & 0x00000003); else if(mclient & 0x1) ++mclient; } else { if(!no_rtcp) if(mclient & 0x1) ++mclient; } elems = (app_ur_session**)malloc(sizeof(app_ur_session)*((mclient*2)+1)+sizeof(void*)); __turn_getMSTime(); u32bits stime = current_time; memset(buffer_to_send, 7, clmessage_length); client_event_base = turn_event_base_new(); int i = 0; int tot_clients = 0; if(c2c) { if(!no_rtcp) for (i = 0; i < (mclient >> 2); i++) { if(!dos) usleep(SLEEP_INTERVAL); if (start_c2c(remote_address, port, ifname, local_address, messagenumber, i << 2) < 0) { exit(-1); } tot_clients+=4; } else for (i = 0; i < (mclient >> 1); i++) { if(!dos) usleep(SLEEP_INTERVAL); if (start_c2c(remote_address, port, ifname, local_address, messagenumber, i << 1) < 0) { exit(-1); } tot_clients+=2; } } else { if(!no_rtcp) for (i = 0; i < (mclient >> 1); i++) { if(!dos) usleep(SLEEP_INTERVAL); if (start_client(remote_address, port, ifname, local_address, messagenumber, i << 1) < 0) { exit(-1); } tot_clients+=2; } else for (i = 0; i < mclient; i++) { if(!dos) usleep(SLEEP_INTERVAL); if (start_client(remote_address, port, ifname, local_address, messagenumber, i) < 0) { exit(-1); } tot_clients++; } } if(dos) _exit(0); total_clients = tot_clients; __turn_getMSTime(); struct event *ev = event_new(client_event_base, -1, EV_TIMEOUT|EV_PERSIST, timer_handler, NULL); struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 1000; evtimer_add(ev,&tv); for(i=0;ipinfo.is_peer) { int connect_err = 0; socket_connect(elems[i]->pinfo.fd, &(elems[i]->pinfo.remote_addr), &connect_err); } } else if((i%2) == 0) { if (turn_tcp_connect(clnet_verbose, &(elems[i]->pinfo), &(elems[i]->pinfo.peer_addr)) < 0) { exit(-1); } } } run_events(1); } __turn_getMSTime(); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Total connect time is %u\n", ((unsigned int) (current_time - stime))); stime = current_time; if(is_TCP_relay()) { u64bits connect_wait_start_time = current_time; while(1) { int i = 0; int completed = 0; if(passive_tcp) { for(i=0;ipinfo.is_peer) { completed+=1; } else if(elems[i]->pinfo.tcp_conn_number>0 && elems[i]->pinfo.tcp_conn[0]->tcp_data_bound) { completed += elems[i]->pinfo.tcp_conn_number; } } if(completed >= total_clients) break; } else { for(i=0;ipinfo.tcp_conn_number>0 && elems[i]->pinfo.tcp_conn[0]->tcp_data_bound) { completed += elems[i]->pinfo.tcp_conn_number; } } if(completed >= total_clients) break; } run_events(0); if(current_time > connect_wait_start_time + STARTING_TCP_RELAY_TIME) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: %d connections are not completed\n", (int)(total_clients - completed)); break; } } } __turn_getMSTime(); stime = current_time; for(i=0;ito_send_timems = current_mstime + 1000 + ((u32bits)random())%5000; } tot_messages = elems[0]->tot_msgnum * total_clients; start_full_timer = 1; while (1) { run_events(1); int msz = (int)current_clients_number; if (msz < 1) { break; } if(show_statistics) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: msz=%d, tot_send_msgs=%lu, tot_recv_msgs=%lu, tot_send_bytes ~ %llu, tot_recv_bytes ~ %llu\n", __FUNCTION__, msz, (unsigned long) tot_send_messages, (unsigned long) tot_recv_messages, (unsigned long long) tot_send_bytes, (unsigned long long) tot_recv_bytes); show_statistics=0; } } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: tot_send_msgs=%lu, tot_recv_msgs=%lu\n", __FUNCTION__, (unsigned long) tot_send_messages, (unsigned long) tot_recv_messages); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: tot_send_bytes ~ %lu, tot_recv_bytes ~ %lu\n", __FUNCTION__, (unsigned long) tot_send_bytes, (unsigned long) tot_recv_bytes); if (client_event_base) event_base_free(client_event_base); if(tot_send_messagesbuf, (size_t*)&(message->len), g_uname, g_upwd, clnet_info->shatype)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO," Cannot add integrity to the message\n"); return -1; } } else if(clnet_info->nonce[0]) { if(stun_attr_add_integrity_by_user_str(message->buf, (size_t*)&(message->len), g_uname, clnet_info->realm, g_upwd, clnet_info->nonce, clnet_info->shatype)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO," Cannot add integrity to the message\n"); return -1; } } return 0; } /////////////////////////////////////////// turnserver-3.2.3.1/src/apps/rfc5769/rfc5769check.c000644 001751 001751 00000026513 12315706777 021151 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include #include #include #include #include #include #include "ns_turn_utils.h" #include "apputils.h" #include "stun_buffer.h" ////////////////////////////////////////////////// static SHATYPE shatype = SHATYPE_SHA1; int main(int argc, const char **argv) { int res = -1; UNUSED_ARG(argc); UNUSED_ARG(argv); set_logfile("stdout"); set_system_parameters(0); { const unsigned char reqstc[] = "\x00\x01\x00\x58" "\x21\x12\xa4\x42" "\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae" "\x80\x22\x00\x10" "STUN test client" "\x00\x24\x00\x04" "\x6e\x00\x01\xff" "\x80\x29\x00\x08" "\x93\x2f\xf9\xb1\x51\x26\x3b\x36" "\x00\x06\x00\x09" "\x65\x76\x74\x6a\x3a\x68\x36\x76\x59\x20\x20\x20" "\x00\x08\x00\x14" "\x9a\xea\xa7\x0c\xbf\xd8\xcb\x56\x78\x1e\xf2\xb5" "\xb2\xd3\xf2\x49\xc1\xb5\x71\xa2" "\x80\x28\x00\x04" "\xe5\x7a\x3b\xcf"; u08bits buf[sizeof(reqstc)]; memcpy(buf, reqstc, sizeof(reqstc)); {//fingerprintfs etc res = stun_is_command_message_full_check_str(buf, sizeof(reqstc) - 1, 1, NULL); printf("RFC 5769 message fingerprint test(0) result: "); if (res) { printf("success\n"); } else if (res == 0) { printf("failure on fingerprint(0) check\n"); exit(-1); } } {//short-term credentials u08bits uname[33]; u08bits realm[33]; u08bits upwd[33]; strcpy((char*) upwd, "VOkJxbRl1RmTxUk/WvJxBt"); res = stun_check_message_integrity_str(TURN_CREDENTIALS_SHORT_TERM, buf, sizeof(reqstc) - 1, uname, realm, upwd, shatype); printf("RFC 5769 simple request short-term credentials and integrity test result: "); if (res > 0) { printf("success\n"); } else if (res == 0) { printf("failure on integrity check\n"); exit(-1); } else { printf("failure on message structure check\n"); exit(-1); } } {//negative fingerprint buf[27] = 23; res = stun_is_command_message_full_check_str(buf, sizeof(reqstc) - 1, 1, NULL); printf("RFC 5769 NEGATIVE fingerprint test(0) result: "); if (!res) { printf("success\n"); } else if (res == 0) { printf("failure on NEGATIVE fingerprint check\n"); exit(-1); } } } { const unsigned char reqltc[] = "\x00\x01\x00\x60" "\x21\x12\xa4\x42" "\x78\xad\x34\x33\xc6\xad\x72\xc0\x29\xda\x41\x2e" "\x00\x06\x00\x12" "\xe3\x83\x9e\xe3\x83\x88\xe3\x83\xaa\xe3\x83\x83" "\xe3\x82\xaf\xe3\x82\xb9\x00\x00" "\x00\x15\x00\x1c" "\x66\x2f\x2f\x34\x39\x39\x6b\x39\x35\x34\x64\x36" "\x4f\x4c\x33\x34\x6f\x4c\x39\x46\x53\x54\x76\x79" "\x36\x34\x73\x41" "\x00\x14\x00\x0b" "\x65\x78\x61\x6d\x70\x6c\x65\x2e\x6f\x72\x67\x00" "\x00\x08\x00\x14" "\xf6\x70\x24\x65\x6d\xd6\x4a\x3e\x02\xb8\xe0\x71" "\x2e\x85\xc9\xa2\x8c\xa8\x96\x66"; u08bits user[] = "\xe3\x83\x9e\xe3\x83\x88\xe3\x83\xaa\xe3\x83\x83" "\xe3\x82\xaf\xe3\x82\xb9"; u08bits realm[33]; u08bits nonce[29]; u08bits upwd[33]; u08bits buf[sizeof(reqltc)]; memcpy(buf, reqltc, sizeof(reqltc)); u08bits uname[sizeof(user)]; memcpy(uname, user, sizeof(user)); strcpy((char*) realm, "example.org"); strcpy((char*) upwd, "TheMatrIX"); strcpy((char*)nonce,"f//499k954d6OL34oL9FSTvy64sA"); res = stun_check_message_integrity_str(TURN_CREDENTIALS_LONG_TERM, buf, sizeof(reqltc) - 1, uname, realm, upwd, shatype); printf("RFC 5769 message structure, long-term credentials and integrity test result: "); if (res > 0) { printf("success\n"); } else if (res == 0) { printf("failure on integrity check\n"); exit(-1); } else { printf("failure on message structure check\n"); exit(-1); } { //encoding test printf("RFC 5769 message encoding test result: "); size_t len = 0; u16bits message_type = STUN_METHOD_BINDING; stun_tid tid; u16bits *buf16 = (u16bits*)buf; u32bits *buf32 = (u32bits*)buf; memcpy(tid.tsx_id,"\x78\xad\x34\x33\xc6\xad\x72\xc0\x29\xda\x41\x2e",12); stun_init_buffer_str(buf,&len); message_type &= (u16bits)(0x3FFF); buf16[0]=nswap16(message_type); buf16[1]=0; buf32[1]=nswap32(STUN_MAGIC_COOKIE); stun_tid_message_cpy(buf, &tid); stun_attr_add_integrity_by_user_str(buf, &len, uname, realm, upwd, nonce, shatype); if(len != (sizeof(reqltc)-1)) { printf("failure: length %d, must be %d\n",(int)len,(int)(sizeof(reqltc)-1)); exit(-1); } if(memcmp(buf,reqltc,len)) { printf("failure: wrong message content\n"); { int lines = 29; int line = 0; int col = 0; int cols = 4; for(line = 0;line 0) { printf("success\n"); } else if (res == 0) { printf("failure on integrity check\n"); exit(-1); } else { printf("failure on message structure check\n"); exit(-1); } } {//negative fingerprint buf[27] = 23; res = stun_is_command_message_full_check_str(buf, sizeof(respv4) - 1, 1, NULL); printf("RFC 5769 NEGATIVE fingerprint test(1) result: "); if (!res) { printf("success\n"); } else if (res == 0) { printf("failure on NEGATIVE fingerprint check\n"); exit(-1); } } {//IPv4 addr ioa_addr addr4; ioa_addr addr4_test; printf("RFC 5769 IPv4 encoding result: "); res = stun_attr_get_first_addr_str(buf, sizeof(respv4)-1, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr4, NULL); if(res < 0) { printf("failure on message structure check\n"); exit(-1); } make_ioa_addr((const u08bits*)"192.0.2.1", 32853, &addr4_test); if(addr_eq(&addr4,&addr4_test)) { printf("success\n"); } else { printf("failure on IPv4 deconding check\n"); exit(-1); } } } { const unsigned char respv6[] = "\x01\x01\x00\x48" "\x21\x12\xa4\x42" "\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae" "\x80\x22\x00\x0b" "\x74\x65\x73\x74\x20\x76\x65\x63\x74\x6f\x72\x20" "\x00\x20\x00\x14" "\x00\x02\xa1\x47" "\x01\x13\xa9\xfa\xa5\xd3\xf1\x79" "\xbc\x25\xf4\xb5\xbe\xd2\xb9\xd9" "\x00\x08\x00\x14" "\xa3\x82\x95\x4e\x4b\xe6\x7b\xf1\x17\x84\xc9\x7c" "\x82\x92\xc2\x75\xbf\xe3\xed\x41" "\x80\x28\x00\x04" "\xc8\xfb\x0b\x4c"; u08bits buf[sizeof(respv6)]; { //decoding test memcpy(buf, respv6, sizeof(respv6)); res = stun_is_command_message_full_check_str(buf, sizeof(respv6) - 1, 1, NULL); printf("RFC 5769 message fingerprint test(2) result: "); if (res) { printf("success\n"); } else if (res == 0) { printf("failure on fingerprint(2) check\n"); exit(-1); } } {//short-term credentials test u08bits uname[33]; u08bits realm[33]; u08bits upwd[33]; strcpy((char*) upwd, "VOkJxbRl1RmTxUk/WvJxBt"); res = stun_check_message_integrity_str(TURN_CREDENTIALS_SHORT_TERM, buf, sizeof(respv6) - 1, uname, realm, upwd, shatype); printf("RFC 5769 IPv6 response short-term credentials and integrity test result: "); if (res > 0) { printf("success\n"); } else if (res == 0) { printf("failure on integrity check\n"); exit(-1); } else { printf("failure on message structure check\n"); exit(-1); } } {//negative decoding test buf[27] = 23; res = stun_is_command_message_full_check_str(buf, sizeof(respv6) - 1, 1, NULL); printf("RFC 5769 NEGATIVE fingerprint test(2) result: "); if (!res) { printf("success\n"); } else if (res == 0) { printf("failure on NEGATIVE fingerprint check\n"); exit(-1); } } {//IPv6 deconding test ioa_addr addr6; ioa_addr addr6_test; printf("RFC 5769 IPv6 encoding result: "); res = stun_attr_get_first_addr_str(buf, sizeof(respv6) - 1, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr6, NULL); if (res < 0) { printf("failure on message structure check\n"); exit(-1); } make_ioa_addr((const u08bits*) "2001:db8:1234:5678:11:2233:4455:6677", 32853, &addr6_test); if (addr_eq(&addr6, &addr6_test)) { printf("success\n"); } else { printf("failure on IPv6 deconding check\n"); exit(-1); } } } return 0; } turnserver-3.2.3.1/src/client++/TurnMsgLib.h000644 001751 001751 00000067442 12315706777 020443 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __LIB_TURN_MSG_CPP__ #define __LIB_TURN_MSG_CPP__ #include "ns_turn_ioaddr.h" #include "ns_turn_msg.h" #include namespace turn { class StunAttr; /** * Exception "end of buffer" */ class EndOfStunMsgException { public: EndOfStunMsgException() {} virtual ~EndOfStunMsgException() {} }; /** * Exception "wrong format of StunAttr" */ class WrongStunAttrFormatException { public: WrongStunAttrFormatException() {} virtual ~WrongStunAttrFormatException() {} }; /** * Exception "wrong format of StunBuffer" */ class WrongStunBufferFormatException { public: WrongStunBufferFormatException() {} virtual ~WrongStunBufferFormatException() {} }; /** * Iterator class for attributes */ class StunAttrIterator { public: /** * Iterator constructor: creates iterator on raw messagebuffer. */ StunAttrIterator(u08bits *buf, size_t sz) throw (WrongStunBufferFormatException) : _buf(buf), _sz(sz) { if(!stun_is_command_message_str(_buf, _sz)) { throw WrongStunBufferFormatException(); } _sar = stun_attr_get_first_str(_buf, _sz); } /** * Iterator constructor: create iterator over message. */ template StunAttrIterator(T &msg) throw (WrongStunBufferFormatException) : _buf(msg.getRawBuffer()), _sz(msg.getSize()) { if(!stun_is_command_message_str(_buf, _sz)) { throw WrongStunBufferFormatException(); } _sar = stun_attr_get_first_str(_buf, _sz); } /** * Iterator constructor: creates iterator over raw buffer, starting from first * location of an attribute of particular type. */ StunAttrIterator(u08bits *buf, size_t sz, u16bits attr_type) throw (WrongStunBufferFormatException) : _buf(buf), _sz(sz) { if(!stun_is_command_message_str(_buf, _sz)) { throw WrongStunBufferFormatException(); } _sar = stun_attr_get_first_by_type_str(_buf, _sz, attr_type); } /** * Iterator constructor: creates iterator over message, starting from first * location of an attribute of particular type. */ template StunAttrIterator(T &msg, u16bits attr_type) throw (WrongStunBufferFormatException) : _buf(msg.getRawBuffer()), _sz(msg.getSize()) { if(!stun_is_command_message_str(_buf, _sz)) { throw WrongStunBufferFormatException(); } _sar = stun_attr_get_first_by_type_str(_buf, _sz, attr_type); } /** * Moves iterator to next attribute location */ void next() throw(EndOfStunMsgException) { if(!_sar) { throw EndOfStunMsgException(); } _sar = stun_attr_get_next_str(_buf,_sz,_sar); } /** * Is the iterator finished */ bool eof() const { return (!_sar); } /** * Is the iterator at an address attribute */ bool isAddr() const { return stun_attr_is_addr(_sar); } /** * Return address family attribute value (if the iterator at the "address family" attribute. */ int getAddressFamily() const { return stun_get_requested_address_family(_sar); } /** * Get attribute type */ int getType() const { return stun_attr_get_type(_sar); } /** * Destructor */ virtual ~StunAttrIterator() {} /** * Return raw memroy field of the attribute value. * If the attribute value length is zero (0), then return NULL. */ const u08bits *getRawBuffer(size_t &sz) const throw(WrongStunAttrFormatException) { int len = stun_attr_get_len(_sar); if(len<0) throw WrongStunAttrFormatException(); sz = (size_t)len; const u08bits *value = stun_attr_get_value(_sar); return value; } friend class StunAttr; private: u08bits *_buf; size_t _sz; stun_attr_ref _sar; }; /** * Root class of all STUN attributes. * Can be also used for a generic attribute object. */ class StunAttr { public: /** * Empty constructor */ StunAttr() : _attr_type(0), _value(0), _sz(0) {} /** * Constructs attribute from iterator */ StunAttr(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) { if(iter.eof()) { throw EndOfStunMsgException(); } size_t sz = 0; const u08bits *ptr = iter.getRawBuffer(sz); if(sz>=0xFFFF) throw WrongStunAttrFormatException(); int at = iter.getType(); if(at<0) throw WrongStunAttrFormatException(); _attr_type = (u16bits)at; _sz = sz; _value=(u08bits*)turn_malloc(_sz); if(ptr) ns_bcopy(ptr,_value,_sz); } /** * Destructor */ virtual ~StunAttr() { if(_value) turn_free(_value,_sz); } /** * Return raw data representation of the attribute */ const u08bits *getRawValue(size_t &sz) const { sz=_sz; return _value; } /** * Set raw data value */ void setRawValue(u08bits *value, size_t sz) throw(WrongStunAttrFormatException) { if(sz>0xFFFF) throw WrongStunAttrFormatException(); if(_value) turn_free(_value,_sz); _sz = sz; _value=(u08bits*)turn_malloc(_sz); if(value) ns_bcopy(value,_value,_sz); } /** * Get attribute type */ u16bits getType() const { return _attr_type; } /** * Set attribute type */ void setType(u16bits at) { _attr_type = at; } /** * Add attribute to a message */ template int addToMsg(T &msg) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { if(!_attr_type) throw WrongStunAttrFormatException(); u08bits *buffer = msg.getRawBuffer(); if(buffer) { size_t sz = msg.getSize(); if(addToBuffer(buffer, sz)<0) { throw WrongStunBufferFormatException(); } msg.setSize(sz); return 0; } throw WrongStunBufferFormatException(); } protected: /** * Virtual function member to add attribute to a raw buffer */ virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { if(buffer) { if(!_value) throw WrongStunAttrFormatException(); if(stun_attr_add_str(buffer, &sz, _attr_type, _value, _sz)<0) { throw WrongStunBufferFormatException(); } return 0; } throw WrongStunBufferFormatException(); } /** * Get low-level iterator object */ static stun_attr_ref getSar(const StunAttrIterator &iter) { return iter._sar; } private: u16bits _attr_type; u08bits *_value; size_t _sz; }; /** * Channel number attribute class */ class StunAttrChannelNumber : public StunAttr { public: StunAttrChannelNumber() : _cn(0) { setType(STUN_ATTRIBUTE_CHANNEL_NUMBER); } StunAttrChannelNumber(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); _cn = stun_attr_get_channel_number(getSar(iter)); if(!_cn) throw WrongStunAttrFormatException(); } virtual ~StunAttrChannelNumber() {} u16bits getChannelNumber() const { return _cn; } void setChannelNumber(u16bits cn) { _cn = cn; } protected: virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { return stun_attr_add_channel_number_str(buffer,&sz,_cn); } private: u16bits _cn; }; /** * Even port attribute class */ class StunAttrEvenPort : public StunAttr { public: StunAttrEvenPort() : _ep(0) { setType(STUN_ATTRIBUTE_EVEN_PORT); } StunAttrEvenPort(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); _ep = stun_attr_get_even_port(getSar(iter)); } virtual ~StunAttrEvenPort() {} u08bits getEvenPort() const { return _ep; } void setEvenPort(u08bits ep) { _ep = ep; } protected: virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { return stun_attr_add_str(buffer, &sz, STUN_ATTRIBUTE_EVEN_PORT, &_ep, 1); } private: u08bits _ep; }; /** * Reservation token attribute class */ class StunAttrReservationToken : public StunAttr { public: StunAttrReservationToken() : _rt(0) { setType(STUN_ATTRIBUTE_RESERVATION_TOKEN); } StunAttrReservationToken(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); _rt = stun_attr_get_reservation_token_value(getSar(iter)); } virtual ~StunAttrReservationToken() {} u64bits getReservationToken() const { return _rt; } void setReservationToken(u64bits rt) { _rt = rt; } protected: virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { uint64_t reservation_token = ioa_ntoh64(_rt); return stun_attr_add_str(buffer, &sz, STUN_ATTRIBUTE_RESERVATION_TOKEN, (u08bits*) (&reservation_token), 8); } private: u64bits _rt; }; /** * This attribute class is used for all address attributes */ class StunAttrAddr : public StunAttr { public: StunAttrAddr(u16bits attr_type = 0) { addr_set_any(&_addr); setType(attr_type); } StunAttrAddr(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); size_t sz = 0; const u08bits *buf = iter.getRawBuffer(sz); if(stun_attr_get_addr_str(buf,sz,getSar(iter),&_addr,NULL)<0) { throw WrongStunAttrFormatException(); } } virtual ~StunAttrAddr() {} void getAddr(ioa_addr &addr) const { addr_cpy(&addr,&_addr); } void setAddr(ioa_addr &addr) { addr_cpy(&_addr,&addr); } protected: virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { return stun_attr_add_addr_str(buffer, &sz, getType(), &_addr); } private: ioa_addr _addr; }; /** * Change Request attribute class */ class StunAttrChangeRequest : public StunAttr { public: StunAttrChangeRequest() : _changeIp(0), _changePort(0) { setType(STUN_ATTRIBUTE_CHANGE_REQUEST); } StunAttrChangeRequest(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); if(stun_attr_get_change_request_str(getSar(iter), &_changeIp, &_changePort)<0) { throw WrongStunAttrFormatException(); } } virtual ~StunAttrChangeRequest() {} bool getChangeIp() const { return _changeIp; } void setChangeIp(bool ci) { if(ci) _changeIp = 1; else _changeIp = 0; } bool getChangePort() const { return _changePort; } void setChangePort(bool cp) { if(cp) _changePort = 1; else _changePort = 0; } protected: virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { return stun_attr_add_change_request_str(buffer, &sz, _changeIp, _changePort); } private: int _changeIp; int _changePort; }; /** * Change Request attribute class */ class StunAttrResponsePort : public StunAttr { public: StunAttrResponsePort() : _rp(0) { setType(STUN_ATTRIBUTE_RESPONSE_PORT); } StunAttrResponsePort(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); int rp = stun_attr_get_response_port_str(getSar(iter)); if(rp<0) { throw WrongStunAttrFormatException(); } _rp = (u16bits)rp; } virtual ~StunAttrResponsePort() {} u16bits getResponsePort() const { return _rp; } void setResponsePort(u16bits p) { _rp = p; } protected: virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { return stun_attr_add_response_port_str(buffer, &sz, _rp); } private: u16bits _rp; }; /** * Padding attribute class */ class StunAttrPadding : public StunAttr { public: StunAttrPadding() : _p(0) { setType(STUN_ATTRIBUTE_PADDING); } StunAttrPadding(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); int p = stun_attr_get_padding_len_str(getSar(iter)); if(p<0) { throw WrongStunAttrFormatException(); } _p = (u16bits)p; } virtual ~StunAttrPadding() {} u16bits getPadding() const { return _p; } /** * Set length of padding */ void setPadding(u16bits p) { _p = p; } protected: virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { return stun_attr_add_padding_str(buffer, &sz, _p); } private: u16bits _p; }; /** * Generic "STUN Message" class, base class for all messages */ class StunMsg { public: /** * Empty constructor */ StunMsg() { _allocated_sz = 0xFFFF; _buffer = (u08bits*)turn_malloc(_allocated_sz); _deallocate = true; _sz = 0; _constructed = 0; } /** * Construct message over raw buffer. * Parameter "construct" is true if the buffer is initialized. */ StunMsg(u08bits *buffer, size_t total_sz, size_t sz, bool constructed) : _buffer(buffer), _deallocate(false), _allocated_sz(total_sz), _sz(sz), _constructed(constructed) {} /** * Destructor */ virtual ~StunMsg() { if(_deallocate && _buffer) { turn_free(_buffer, _allocated_sz); } } /** * Initialize buffer */ void construct() { constructBuffer(); } /** * Checks if the message is properly constructed */ bool isValid() { return check(); } /** * get raw buffer */ u08bits *getRawBuffer() { return _buffer; } /** * Get message size in the buffer (message can be mnuch smaller than the whole buffer) */ size_t getSize() const { return _sz; } /** * Set message size */ void setSize(size_t sz) throw(WrongStunBufferFormatException) { if(sz>_allocated_sz) throw WrongStunBufferFormatException(); _sz = sz; } /** * Check if the raw buffer is a TURN "command" (request, response or indication). */ static bool isCommand(u08bits *buffer, size_t sz) { return stun_is_command_message_str(buffer, sz); } /** * Check if the current message object is a "command" (request, response, or indication). */ bool isCommand() const { return stun_is_command_message_str(_buffer, _sz); } static bool isIndication(u08bits *buffer, size_t sz) { return stun_is_indication_str(buffer, sz); } static bool isRequest(u08bits *buffer, size_t sz) { return stun_is_request_str(buffer, sz); } static bool isSuccessResponse(u08bits *buffer, size_t sz) { return stun_is_success_response_str(buffer, sz); } static bool isErrorResponse(u08bits *buffer, size_t sz, int &err_code, u08bits *err_msg, size_t err_msg_size) { return stun_is_error_response_str(buffer, sz, &err_code, err_msg, err_msg_size); } /** * Check if the raw buffer is a challenge response (the one with 401 error and realm and nonce values). */ static bool isChallengeResponse(const u08bits* buf, size_t sz, int &err_code, u08bits *err_msg, size_t err_msg_size, u08bits *realm, u08bits *nonce) { return stun_is_challenge_response_str(buf, sz, &err_code, err_msg, err_msg_size, realm, nonce); } /** * Check if the message is a channel message */ static bool isChannel(u08bits *buffer, size_t sz) { return is_channel_msg_str(buffer, sz); } /** * Check if the fingerprint is present. */ static bool isFingerprintPresent(u08bits *buffer, size_t sz) { if(!stun_is_command_message_str(buffer,sz)) return false; stun_attr_ref sar = stun_attr_get_first_by_type_str(buffer, sz, STUN_ATTRIBUTE_FINGERPRINT); if(!sar) return false; return true; } /** * Check the fingerprint */ static bool checkFingerprint(u08bits *buffer, size_t sz) { return stun_is_command_message_full_check_str(buffer, sz, 1, NULL); } /** * Add attribute to the message */ int addAttr(StunAttr &attr) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { return attr.addToMsg(*this); } /** * Get transaction ID */ virtual stun_tid getTid() const throw(WrongStunBufferFormatException) { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); stun_tid tid; stun_tid_from_message_str(_buffer,_sz,&tid); return tid; } /** * Set transaction ID */ virtual void setTid(stun_tid &tid) throw(WrongStunBufferFormatException) { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); stun_tid_message_cpy(_buffer, &tid); } /** * Add fingerprint to the message */ void addFingerprint() throw(WrongStunBufferFormatException) { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); stun_attr_add_fingerprint_str(_buffer,&_sz); } /** * Check message integrity, in secure communications. */ bool checkMessageIntegrity(turn_credential_type ct, std::string &uname, std::string &realm, std::string &upwd) const throw(WrongStunBufferFormatException) { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); u08bits *suname=(u08bits*)strdup(uname.c_str()); u08bits *srealm=(u08bits*)strdup(realm.c_str()); u08bits *supwd=(u08bits*)strdup(upwd.c_str()); SHATYPE sht = SHATYPE_SHA1; bool ret = (0< stun_check_message_integrity_str(ct,_buffer, _sz, suname, srealm, supwd, sht)); free(suname); free(srealm); free(supwd); return ret; } /** * Adds long-term message integrity data to the message. */ void addLTMessageIntegrity(std::string &uname, std::string &realm, std::string &upwd, std::string &nonce) throw(WrongStunBufferFormatException) { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); u08bits *suname=(u08bits*)strdup(uname.c_str()); u08bits *srealm=(u08bits*)strdup(realm.c_str()); u08bits *supwd=(u08bits*)strdup(upwd.c_str()); u08bits *snonce=(u08bits*)strdup(nonce.c_str()); stun_attr_add_integrity_by_user_str(_buffer, &_sz, suname, srealm, supwd, snonce, SHATYPE_SHA1); free(suname); free(srealm); free(supwd); free(snonce); } /** * Adds short-term message integrity data to the message. */ void addSTMessageIntegrity(std::string &uname, std::string &upwd) throw(WrongStunBufferFormatException) { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); u08bits *suname=(u08bits*)strdup(uname.c_str()); u08bits *supwd=(u08bits*)strdup(upwd.c_str()); stun_attr_add_integrity_by_user_short_term_str(_buffer, &_sz, suname, supwd, SHATYPE_SHA1); free(suname); free(supwd); } protected: virtual void constructBuffer() = 0; virtual bool check() = 0; protected: u08bits *_buffer; bool _deallocate; size_t _allocated_sz; size_t _sz; bool _constructed; }; /** * Class that represents the "request" flavor of STUN/TURN messages. */ class StunMsgRequest : public StunMsg { public: StunMsgRequest(u16bits method) : _method(method) {}; StunMsgRequest(u08bits *buffer, size_t total_sz, size_t sz, bool constructed) throw(WrongStunBufferFormatException) : StunMsg(buffer,total_sz,sz,constructed),_method(0) { if(constructed) { if(!stun_is_request_str(buffer,sz)) { throw WrongStunBufferFormatException(); } _method = stun_get_method_str(buffer,sz); } } virtual ~StunMsgRequest() {} /** * Get request method */ u16bits getMethod() const { return _method; } /** * Set method */ void setMethod(u16bits method) { _method = method; } /** * Construct binding request */ void constructBindingRequest() { stun_set_binding_request_str(_buffer, &_sz); } bool isBindingRequest() const { return stun_is_binding_request_str(_buffer,_sz,0); } /** * Construct allocate request */ void constructAllocateRequest(u32bits lifetime, int address_family, u08bits transport, int mobile) { stun_set_allocate_request_str(_buffer, &_sz, lifetime, address_family, transport, mobile); } /** * Construct channel bind request */ void constructChannelBindRequest(const ioa_addr &peer_addr, u16bits channel_number) { stun_set_channel_bind_request_str(_buffer, &_sz, &peer_addr, channel_number); } protected: virtual void constructBuffer() { stun_init_request_str(_method,_buffer,&_sz); _constructed = true; } virtual bool check() { if(!_constructed) return false; if(!stun_is_request_str(_buffer,_sz)) { return false; } if(_method != stun_get_method_str(_buffer,_sz)) { return false; } return true; } private: u16bits _method; }; /** * Class for STUN/TURN responses */ class StunMsgResponse : public StunMsg { public: StunMsgResponse(u16bits method, stun_tid &tid) : _method(method), _err(0), _reason(""), _tid(tid) {}; StunMsgResponse(u16bits method, int error_code, std::string reason, stun_tid &tid) : _method(method), _err(error_code), _reason(reason), _tid(tid) { }; StunMsgResponse(u08bits *buffer, size_t total_sz, size_t sz, bool constructed) throw(WrongStunBufferFormatException) : StunMsg(buffer,total_sz,sz,constructed),_method(0),_err(0),_reason("") { if(constructed) { if(!stun_is_success_response_str(buffer,sz)) { u08bits errtxt[0xFFFF]; if(!stun_is_error_response_str(buffer,sz,&_err,errtxt,sizeof(errtxt))) { throw WrongStunBufferFormatException(); } _reason = (char*)errtxt; } _method = stun_get_method_str(buffer,sz); stun_tid_from_message_str(_buffer,_sz,&_tid); } } u16bits getMethod() const { return _method; } void setMethod(u16bits method) { _method = method; } /** * Get error code */ int getError() const { return _err; } /** * Set error code */ void setError(int err) { _err = err; } /** * Get error message */ std::string getReason() const { return _reason; } /** * Set error message */ void setReason(std::string reason) { _reason = reason; } /** * Set transaction ID */ void setTid(stun_tid &tid) throw(WrongStunBufferFormatException) { _tid = tid; } /** * Get transaction ID */ virtual stun_tid getTid() const throw(WrongStunBufferFormatException) { return _tid; } /** * Check if this is a challenge response, and return realm and nonce */ bool isChallenge(std::string &realm, std::string &nonce) const { bool ret = false; if(_constructed) { int err_code; u08bits err_msg[1025]; size_t err_msg_size=sizeof(err_msg); u08bits srealm[0xFFFF]; u08bits snonce[0xFFFF]; ret = stun_is_challenge_response_str(_buffer, _sz, &err_code, err_msg, err_msg_size, srealm, snonce); if(ret) { realm = (char*)srealm; nonce = (char*)snonce; } } return ret; } bool isChallenge() const { std::string realm, nonce; return isChallenge(realm, nonce); } /** * Check if this is a success response */ bool isSuccess() const { return (_err == 0); } /** * Construct binding response */ void constructBindingResponse(stun_tid &tid, const ioa_addr &reflexive_addr, int error_code, const u08bits *reason) { stun_set_binding_response_str(_buffer, &_sz, &tid, &reflexive_addr, error_code, reason, 0 , 0); } bool isBindingResponse() const { return stun_is_binding_response_str(_buffer,_sz); } /** * Construct allocate response */ void constructAllocateResponse(stun_tid &tid, const ioa_addr &relayed_addr, const ioa_addr &reflexive_addr, u32bits lifetime, int error_code, const u08bits *reason, u64bits reservation_token, char *mobile_id) { stun_set_allocate_response_str(_buffer, &_sz, &tid, &relayed_addr, &reflexive_addr, lifetime, error_code, reason, reservation_token, mobile_id); } /** * Construct channel bind response */ void constructChannelBindResponse(stun_tid &tid, int error_code, const u08bits *reason) { stun_set_channel_bind_response_str(_buffer, &_sz, &tid, error_code, reason); } protected: virtual void constructBuffer() { if(_err) { stun_init_error_response_str(_method, _buffer, &_sz, _err, (const u08bits*)_reason.c_str(), &_tid); } else { stun_init_success_response_str(_method, _buffer, &_sz, &_tid); } _constructed = true; } virtual bool check() { if(!_constructed) return false; if(!stun_is_success_response_str(_buffer,_sz)) { u08bits errtxt[0xFFFF]; int cerr=0; if(!stun_is_error_response_str(_buffer,_sz,&cerr,errtxt,sizeof(errtxt))) { throw WrongStunBufferFormatException(); } if(cerr != _err) { throw WrongStunBufferFormatException(); } } if(_method != stun_get_method_str(_buffer,_sz)) { return false; } return true; } private: u16bits _method; int _err; std::string _reason; stun_tid _tid; }; /** * Class for STUN/TURN indications */ class StunMsgIndication : public StunMsg { public: StunMsgIndication(u16bits method) : _method(method) {}; StunMsgIndication(u08bits *buffer, size_t total_sz, size_t sz, bool constructed) throw(WrongStunBufferFormatException) : StunMsg(buffer,total_sz,sz,constructed),_method(0) { if(constructed) { if(!stun_is_indication_str(buffer,sz)) { throw WrongStunBufferFormatException(); } _method = stun_get_method_str(buffer,sz); } } virtual ~StunMsgIndication() {} u16bits getMethod() const { return _method; } void setMethod(u16bits method) { _method = method; } protected: virtual void constructBuffer() { stun_init_indication_str(_method,_buffer,&_sz); _constructed = true; } virtual bool check() { if(!_constructed) return false; if(!stun_is_indication_str(_buffer,_sz)) { return false; } if(_method != stun_get_method_str(_buffer,_sz)) { return false; } return true; } private: u16bits _method; }; /** * Channel message */ class StunMsgChannel : public StunMsg { public: StunMsgChannel(u16bits cn, int length) : _cn(cn), _len(length) {}; StunMsgChannel(u08bits *buffer, size_t total_sz, size_t sz, bool constructed) throw(WrongStunBufferFormatException) : StunMsg(buffer,total_sz,sz,constructed),_cn(0) { if(constructed) { if(!stun_is_channel_message_str(buffer,&_sz,&_cn,0)) { throw WrongStunBufferFormatException(); } if(_sz>0xFFFF || _sz<4) throw WrongStunBufferFormatException(); _len = _sz-4; } else { if(total_sz>0xFFFF || total_sz<4) throw WrongStunBufferFormatException(); _len = 0; } } virtual ~StunMsgChannel() {} u16bits getChannelNumber() const { return _cn; } void setChannelNumber(u16bits cn) { _cn = cn; } /** * Get length of message itself (excluding the 4 channel number bytes) */ size_t getLength() const { return _len; } /** * Set length of message itself (excluding the 4 channel number bytes) */ void setLength(size_t len) { _len = len; } protected: virtual void constructBuffer() { stun_init_channel_message_str(_cn,_buffer,&_sz,(int)_len,0); _constructed = true; } virtual bool check() { if(!_constructed) return false; u16bits cn = 0; if(!stun_is_channel_message_str(_buffer,&_sz,&cn,0)) { return false; } if(_cn != cn) { return false; } return true; } private: u16bits _cn; size_t _len; }; }; /* namespace */ #endif /* __LIB_TURN_MSG_CPP__ */ turnserver-3.2.3.1/src/server/ns_turn_ioalib.h000644 001751 001751 00000022136 12315706777 021315 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ /* * IO Abstraction library */ #ifndef __IOA_LIB__ #define __IOA_LIB__ #include "ns_turn_ioaddr.h" #ifdef __cplusplus extern "C" { #endif ////////////// forward declarations //////// struct _ts_ur_super_session; typedef struct _ts_ur_super_session ts_ur_super_session; struct _tcp_connection; typedef struct _tcp_connection tcp_connection; ////////////// Mutexes ///////////////////// struct _turn_mutex { u32bits data; void* mutex; }; typedef struct _turn_mutex turn_mutex; int turn_mutex_init(turn_mutex* mutex); int turn_mutex_init_recursive(turn_mutex* mutex); int turn_mutex_lock(const turn_mutex *mutex); int turn_mutex_unlock(const turn_mutex *mutex); int turn_mutex_destroy(turn_mutex* mutex); #define TURN_MUTEX_DECLARE(mutex) turn_mutex mutex; #define TURN_MUTEX_INIT(mutex) turn_mutex_init(mutex) #define TURN_MUTEX_INIT_RECURSIVE(mutex) turn_mutex_init_recursive(mutex) #define TURN_MUTEX_LOCK(mutex) turn_mutex_lock(mutex) #define TURN_MUTEX_UNLOCK(mutex) turn_mutex_unlock(mutex) #define TURN_MUTEX_DESTROY(mutex) turn_mutex_destroy(mutex) /////// Sockets ////////////////////////////// #define IOA_EV_TIMEOUT 0x01 #define IOA_EV_READ 0x02 #define IOA_EV_WRITE 0x04 #define IOA_EV_SIGNAL 0x08 #define IOA_EV_CLOSE 0x10 enum _SOCKET_TYPE { UNKNOWN_SOCKET=0, TCP_SOCKET=6, UDP_SOCKET=17, TLS_SOCKET=56, DTLS_SOCKET=250, TENTATIVE_TCP_SOCKET=255 }; typedef enum _SOCKET_TYPE SOCKET_TYPE; enum _SOCKET_APP_TYPE { UNKNOWN_APP_SOCKET, CLIENT_SOCKET, RELAY_SOCKET, RELAY_RTCP_SOCKET, TCP_CLIENT_DATA_SOCKET, TCP_RELAY_DATA_SOCKET, LISTENER_SOCKET }; typedef enum _SOCKET_APP_TYPE SOCKET_APP_TYPE; struct _ioa_socket; typedef struct _ioa_socket ioa_socket; typedef ioa_socket *ioa_socket_handle; struct _ioa_engine; typedef struct _ioa_engine ioa_engine; typedef ioa_engine *ioa_engine_handle; typedef void *ioa_timer_handle; typedef void *ioa_network_buffer_handle; /* event data for net event */ typedef struct _ioa_net_data { ioa_addr src_addr; ioa_network_buffer_handle nbh; int recv_ttl; int recv_tos; } ioa_net_data; /* Callback on TCP connection completion */ typedef void (*connect_cb)(int success, void *arg); /* Callback on accepted socket from TCP relay endpoint */ typedef void (*accept_cb)(ioa_socket_handle s, void *arg); //////// IP White/black listing /////////// struct _ip_range_list { char **ranges; ioa_addr_range **encaddrsranges; size_t ranges_number; }; typedef struct _ip_range_list ip_range_list_t; void ioa_lock_whitelist(ioa_engine_handle e); void ioa_unlock_whitelist(ioa_engine_handle e); const ip_range_list_t* ioa_get_whitelist(ioa_engine_handle e); void ioa_lock_blacklist(ioa_engine_handle e); void ioa_unlock_blacklist(ioa_engine_handle e); const ip_range_list_t* ioa_get_blacklist(ioa_engine_handle e); //////////////////////////////////////////// /* * Network buffer functions */ ioa_network_buffer_handle ioa_network_buffer_allocate(ioa_engine_handle e); void ioa_network_buffer_header_init(ioa_network_buffer_handle nbh); u08bits *ioa_network_buffer_data(ioa_network_buffer_handle nbh); size_t ioa_network_buffer_get_size(ioa_network_buffer_handle nbh); size_t ioa_network_buffer_get_capacity(ioa_network_buffer_handle nbh); size_t ioa_network_buffer_get_capacity_udp(void); void ioa_network_buffer_set_size(ioa_network_buffer_handle nbh, size_t len); void ioa_network_buffer_add_offset_size(ioa_network_buffer_handle nbh, u16bits offset, u08bits coffset, size_t len); u16bits ioa_network_buffer_get_offset(ioa_network_buffer_handle nbh); u08bits ioa_network_buffer_get_coffset(ioa_network_buffer_handle nbh); void ioa_network_buffer_delete(ioa_engine_handle e, ioa_network_buffer_handle nbh); /* * Status reporting functions */ void turn_report_allocation_set(void *a, turn_time_t lifetime, int refresh); void turn_report_allocation_delete(void *a); void turn_report_session_usage(void *session); void turn_report_allocation_delete_all(void); /* * Network event handler callback * chnum parameter is just an optimisation hint - * the function must work correctly when chnum=0 * (when no hint information is available). */ typedef void (*ioa_net_event_handler)(ioa_socket_handle s, int event_type, ioa_net_data *data, void *ctx); /* * Timer callback */ typedef void (*ioa_timer_event_handler)(ioa_engine_handle e, void *ctx); /* timers */ ioa_timer_handle set_ioa_timer(ioa_engine_handle e, int secs, int ms, ioa_timer_event_handler cb, void *ctx, int persist, const s08bits *txt); void stop_ioa_timer(ioa_timer_handle th); void delete_ioa_timer(ioa_timer_handle th); #define IOA_EVENT_DEL(E) do { if(E) { delete_ioa_timer(E); E = NULL; } } while(0) ioa_socket_handle create_unbound_ioa_socket(ioa_engine_handle e, ioa_socket_handle parent_s, int family, SOCKET_TYPE st, SOCKET_APP_TYPE sat); void inc_ioa_socket_ref_counter(ioa_socket_handle s); /* Relay socket handling */ /* * event_port == -1: no rtcp; * event_port == 0: reserve rtcp; * even_port == +1: reserve and bind rtcp. */ int create_relay_ioa_sockets(ioa_engine_handle e, ioa_socket_handle client_s, int address_family, u08bits transport, int even_port, ioa_socket_handle *rtp_s, ioa_socket_handle *rtcp_s, u64bits *out_reservation_token, int *err_code, const u08bits **reason, accept_cb acb, void *acbarg); ioa_socket_handle ioa_create_connecting_tcp_relay_socket(ioa_socket_handle s, ioa_addr *peer_addr, connect_cb cb, void *arg); int get_ioa_socket_from_reservation(ioa_engine_handle e, u64bits in_reservation_token, ioa_socket_handle *s); int get_ioa_socket_address_family(ioa_socket_handle s); SOCKET_TYPE get_ioa_socket_type(ioa_socket_handle s); SOCKET_APP_TYPE get_ioa_socket_app_type(ioa_socket_handle s); const char* get_ioa_socket_tls_method(ioa_socket_handle s); const char* get_ioa_socket_tls_cipher(ioa_socket_handle s); void set_ioa_socket_app_type(ioa_socket_handle s, SOCKET_APP_TYPE sat); ioa_addr* get_local_addr_from_ioa_socket(ioa_socket_handle s); ioa_addr* get_remote_addr_from_ioa_socket(ioa_socket_handle s); int get_local_mtu_ioa_socket(ioa_socket_handle s); ts_ur_super_session *get_ioa_socket_session(ioa_socket_handle s); void set_ioa_socket_session(ioa_socket_handle s, ts_ur_super_session *ss); void clear_ioa_socket_session_if(ioa_socket_handle s, void *ss); tcp_connection *get_ioa_socket_sub_session(ioa_socket_handle s); void set_ioa_socket_sub_session(ioa_socket_handle s, tcp_connection *tc); int register_callback_on_ioa_socket(ioa_engine_handle e, ioa_socket_handle s, int event_type, ioa_net_event_handler cb, void *ctx, int clean_preexisting); int send_data_from_ioa_socket_nbh(ioa_socket_handle s, ioa_addr* dest_addr, ioa_network_buffer_handle nbh, int ttl, int tos); void close_ioa_socket(ioa_socket_handle s); #define IOA_CLOSE_SOCKET(S) do { if(S) { close_ioa_socket(S); S = NULL; } } while(0) ioa_socket_handle detach_ioa_socket(ioa_socket_handle s, int full_detach); void detach_socket_net_data(ioa_socket_handle s); int set_df_on_ioa_socket(ioa_socket_handle s, int value); void set_do_not_use_df(ioa_socket_handle s); int ioa_socket_tobeclosed(ioa_socket_handle s); void set_ioa_socket_tobeclosed(ioa_socket_handle s); void close_ioa_socket_after_processing_if_necessary(ioa_socket_handle s); ////////////////// Base64 ///////////////////////////// char *base64_encode(const unsigned char *data, size_t input_length, size_t *output_length); void build_base64_decoding_table(void); unsigned char *base64_decode(const char *data, size_t input_length, size_t *output_length); /////////////////////////////////////// #ifdef __cplusplus } #endif #endif /* __IOA_LIB__ */ turnserver-3.2.3.1/src/server/ns_turn_maps.h000644 001751 001751 00000014711 12315706777 021016 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __TURN_MAPS__ #define __TURN_MAPS__ #include "ns_turn_ioaddr.h" #ifdef __cplusplus extern "C" { #endif //////////////// UR MAP ////////////////// struct _ur_map; typedef struct _ur_map ur_map; //////////////// Common Definitions ////// typedef u64bits ur_map_key_type; typedef unsigned long ur_map_value_type; typedef void (*ur_map_del_func)(ur_map_value_type); typedef int (*foreachcb_type)(ur_map_key_type key, ur_map_value_type value); typedef int (*foreachcb_arg_type)(ur_map_key_type key, ur_map_value_type value, void *arg); ///////////// non-local map ///////////////////// ur_map* ur_map_create(void); /** * @ret: * 0 - success * -1 - error */ int ur_map_put(ur_map* map, ur_map_key_type key, ur_map_value_type value); /** * @ret: * 1 - success * 0 - not found */ int ur_map_get(const ur_map* map, ur_map_key_type key, ur_map_value_type *value); /** * @ret: * 1 - success * 0 - not found */ int ur_map_del(ur_map* map, ur_map_key_type key,ur_map_del_func delfunc); /** * @ret: * 1 - success * 0 - not found */ int ur_map_exist(const ur_map* map, ur_map_key_type key); void ur_map_free(ur_map** map); size_t ur_map_size(const ur_map* map); int ur_map_foreach(ur_map* map, foreachcb_type func); int ur_map_foreach_arg(ur_map* map, foreachcb_arg_type func, void* arg); int ur_map_lock(const ur_map* map); int ur_map_unlock(const ur_map* map); ///////////// "local" map ///////////////////// #define LM_MAP_HASH_SIZE (8) #define LM_MAP_ARRAY_SIZE (3) typedef struct _lm_map_array { ur_map_key_type main_keys[LM_MAP_ARRAY_SIZE]; ur_map_value_type main_values[LM_MAP_ARRAY_SIZE]; size_t extra_sz; ur_map_key_type **extra_keys; ur_map_value_type **extra_values; } lm_map_array; typedef struct _lm_map { lm_map_array table[LM_MAP_HASH_SIZE]; } lm_map; void lm_map_init(lm_map *map); /** * @ret: * 0 - success * -1 - error */ int lm_map_put(lm_map* map, ur_map_key_type key, ur_map_value_type value); /** * @ret: * 1 - success * 0 - not found */ int lm_map_get(const lm_map* map, ur_map_key_type key, ur_map_value_type *value); /** * @ret: * 1 - success * 0 - not found */ int lm_map_del(lm_map* map, ur_map_key_type key,ur_map_del_func delfunc); /** * @ret: * 1 - success * 0 - not found */ int lm_map_exist(const lm_map* map, ur_map_key_type key); void lm_map_clean(lm_map* map); size_t lm_map_size(const lm_map* map); int lm_map_foreach(lm_map* map, foreachcb_type func); int lm_map_foreach_arg(lm_map* map, foreachcb_arg_type func, void* arg); //////////////// UR ADDR MAP ////////////////// typedef unsigned long ur_addr_map_value_type; #define ADDR_MAP_SIZE (1024) #define ADDR_ARRAY_SIZE (4) typedef struct _addr_elem { ioa_addr key; ur_addr_map_value_type value; } addr_elem; typedef struct _addr_list_header { addr_elem main_list[ADDR_ARRAY_SIZE]; addr_elem *extra_list; size_t extra_sz; } addr_list_header; struct _ur_addr_map { addr_list_header lists[ADDR_MAP_SIZE]; u64bits magic; }; struct _ur_addr_map; typedef struct _ur_addr_map ur_addr_map; typedef void (*ur_addr_map_func)(ur_addr_map_value_type); void ur_addr_map_init(ur_addr_map* map); void ur_addr_map_clean(ur_addr_map* map); /** * @ret: * 0 - success * -1 - error * if the addr key exists, the value is updated. */ int ur_addr_map_put(ur_addr_map* map, ioa_addr* key, ur_addr_map_value_type value); /** * @ret: * 1 - success * 0 - not found */ int ur_addr_map_get(const ur_addr_map* map, ioa_addr* key, ur_addr_map_value_type *value); /** * @ret: * 1 - success * 0 - not found */ int ur_addr_map_del(ur_addr_map* map, ioa_addr* key,ur_addr_map_func func); /** * @ret: * 1 - success * 0 - not found */ void ur_addr_map_foreach(ur_addr_map* map, ur_addr_map_func func); size_t ur_addr_map_size(const ur_addr_map* map); //////////////// UR STRING MAP ////////////////// typedef s08bits* ur_string_map_key_type; typedef void* ur_string_map_value_type; struct _ur_string_map; typedef struct _ur_string_map ur_string_map; typedef void (*ur_string_map_func)(ur_string_map_value_type); ur_string_map* ur_string_map_create(ur_string_map_func del_value_func); /** * @ret: * 0 - success * -1 - error * if the string key exists, and the value is different, return error. */ int ur_string_map_put(ur_string_map* map, const ur_string_map_key_type key, ur_string_map_value_type value); /** * @ret: * 1 - success * 0 - not found */ int ur_string_map_get(ur_string_map* map, const ur_string_map_key_type key, ur_string_map_value_type *value); /** * @ret: * 1 - success * 0 - not found */ int ur_string_map_del(ur_string_map* map, const ur_string_map_key_type key); void ur_string_map_clean(ur_string_map* map); void ur_string_map_free(ur_string_map** map); size_t ur_string_map_size(const ur_string_map* map); int ur_string_map_lock(const ur_string_map* map); int ur_string_map_unlock(const ur_string_map* map); //////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_MAPS__ turnserver-3.2.3.1/src/server/ns_turn_server.h000644 001751 001751 00000015607 12315706777 021371 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __TURN_SERVER__ #define __TURN_SERVER__ #include "ns_turn_utils.h" #include "ns_turn_session.h" #ifdef __cplusplus extern "C" { #endif //////////// ALTERNATE-SERVER ///////////// struct _turn_server_addrs_list { ioa_addr *addrs; volatile size_t size; turn_mutex m; }; typedef struct _turn_server_addrs_list turn_server_addrs_list_t; void init_turn_server_addrs_list(turn_server_addrs_list_t *l); typedef int vint; typedef vint* vintp; ////////// RFC 5780 /////////////////////// typedef int (*get_alt_addr_cb)(ioa_addr *addr, ioa_addr *alt_addr); typedef int (*send_message_cb)(ioa_engine_handle e, ioa_network_buffer_handle nbh, ioa_addr *origin, ioa_addr *destination); ////////////////////////////////////////// extern int TURN_MAX_ALLOCATE_TIMEOUT; extern int TURN_MAX_ALLOCATE_TIMEOUT_STUN_ONLY; typedef u08bits turnserver_id; enum _MESSAGE_TO_RELAY_TYPE { RMT_UNKNOWN = 0, RMT_SOCKET, RMT_CB_SOCKET, RMT_MOBILE_SOCKET }; typedef enum _MESSAGE_TO_RELAY_TYPE MESSAGE_TO_RELAY_TYPE; struct socket_message { ioa_socket_handle s; ioa_net_data nd; }; typedef enum { DONT_FRAGMENT_UNSUPPORTED=0, DONT_FRAGMENT_SUPPORTED, DONT_FRAGMENT_SUPPORT_EMULATED } dont_fragment_option_t; struct _turn_turnserver; typedef struct _turn_turnserver turn_turnserver; typedef void (*get_username_resume_cb)(int success, hmackey_t hmackey, st_password_t pwd, turn_turnserver *server, u64bits ctxkey, ioa_net_data *in_buffer); typedef u08bits *(*get_user_key_cb)(turnserver_id id, u08bits *uname, get_username_resume_cb resume, ioa_net_data *in_buffer, u64bits ctxkey, int *postpone_reply); typedef int (*check_new_allocation_quota_cb)(u08bits *username); typedef void (*release_allocation_quota_cb)(u08bits *username); typedef int (*send_socket_to_relay_cb)(turnserver_id id, u64bits cid, stun_tid *tid, ioa_socket_handle s, int message_integrity, MESSAGE_TO_RELAY_TYPE rmt, ioa_net_data *nd); typedef int (*send_turn_session_info_cb)(struct turn_session_info *tsi); struct _turn_turnserver { turnserver_id id; turnsession_id session_id_counter; ur_map *sessions_map; turn_time_t ctime; ioa_engine_handle e; int verbose; int fingerprint; int rfc5780; vintp stale_nonce; vintp stun_only; vintp no_stun; vintp secure_stun; SHATYPE shatype; get_alt_addr_cb alt_addr_cb; send_message_cb sm_cb; dont_fragment_option_t dont_fragment; int (*disconnect)(ts_ur_super_session*); turn_credential_type ct; u08bits realm[STUN_MAX_REALM_SIZE+1]; get_user_key_cb userkeycb; check_new_allocation_quota_cb chquotacb; release_allocation_quota_cb raqcb; int external_ip_set; ioa_addr external_ip; vintp no_loopback_peers; vintp no_multicast_peers; send_turn_session_info_cb send_turn_session_info; /* RFC 6062 ==>> */ vintp no_udp_relay; vintp no_tcp_relay; ur_map *tcp_relay_connections; send_socket_to_relay_cb send_socket_to_relay; /* <<== RFC 6062 */ /* Alternate servers ==>> */ turn_server_addrs_list_t *alternate_servers_list; size_t as_counter; turn_server_addrs_list_t *tls_alternate_servers_list; size_t tls_as_counter; turn_server_addrs_list_t *aux_servers_list; int self_udp_balance; /* White/black listing of address ranges */ ip_range_list_t* ip_whitelist; ip_range_list_t* ip_blacklist; /* Mobility */ vintp mobility; ur_map *mobile_connections_map; /* Server relay */ int server_relay; }; /////////////////////////////////////////// void init_turn_server(turn_turnserver* server, turnserver_id id, int verbose, ioa_engine_handle e, int stun_port, int fingerprint, dont_fragment_option_t dont_fragment, turn_credential_type ct, u08bits *realm, get_user_key_cb userkeycb, check_new_allocation_quota_cb chquotacb, release_allocation_quota_cb raqcb, ioa_addr *external_addr, vintp no_tcp_relay, vintp no_udp_relay, vintp stale_nonce, vintp stun_only, vintp no_stun, turn_server_addrs_list_t *alternate_servers_list, turn_server_addrs_list_t *tls_alternate_servers_list, turn_server_addrs_list_t *aux_servers_list, int self_udp_balance, vintp no_multicast_peers, vintp no_loopback_peers, ip_range_list_t* ip_whitelist, ip_range_list_t* ip_blacklist, send_socket_to_relay_cb send_socket_to_relay, vintp secure_stun, SHATYPE shatype, vintp mobility, int server_relay, send_turn_session_info_cb send_turn_session_info); ioa_engine_handle turn_server_get_engine(turn_turnserver *s); ////////// RFC 5780 /////////////////////// void set_rfc5780(turn_turnserver *server, get_alt_addr_cb cb, send_message_cb smcb); /////////////////////////////////////////// int open_client_connection_session(turn_turnserver* server, struct socket_message *sm); int shutdown_client_connection(turn_turnserver *server, ts_ur_super_session *ss, int force, const char* reason); void set_disconnect_cb(turn_turnserver* server, int (*disconnect)(ts_ur_super_session*)); int turnserver_accept_tcp_client_data_connection(turn_turnserver *server, tcp_connection_id tcid, stun_tid *tid, ioa_socket_handle s, int message_integrity); int report_turn_session_info(turn_turnserver *server, ts_ur_super_session *ss, int force_invalid); turn_time_t get_turn_server_time(turn_turnserver *server); /////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_SERVER__ turnserver-3.2.3.1/src/server/ns_turn_allocation.c000644 001751 001751 00000041325 12315706777 022177 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "ns_turn_allocation.h" /////////////// Permission forward declarations ///////////////// static void init_turn_permission_hashtable(turn_permission_hashtable *map); static void free_turn_permission_hashtable(turn_permission_hashtable *map); static turn_permission_info* get_from_turn_permission_hashtable(turn_permission_hashtable *map, const ioa_addr *addr); /////////////// ALLOCATION ////////////////////////////////////// void init_allocation(void *owner, allocation* a, ur_map *tcp_connections) { if(a) { ns_bzero(a,sizeof(allocation)); a->owner = owner; a->tcp_connections = tcp_connections; init_turn_permission_hashtable(&(a->addr_to_perm)); } } void clear_allocation(allocation *a) { if (a) { if(a->is_valid) turn_report_allocation_delete(a); if(a->tcs.elems) { size_t i; size_t sz = a->tcs.sz; for(i=0;itcs.elems[i]; if(tc) { delete_tcp_connection(tc); a->tcs.elems[i] = NULL; break; } } turn_free(a->tcs.elems,sz*sizeof(tcp_connection*)); a->tcs.elems = NULL; } a->tcs.sz = 0; clear_ioa_socket_session_if(a->relay_session.s, a->owner); clear_ts_ur_session_data(&(a->relay_session)); IOA_EVENT_DEL(a->lifetime_ev); /* The order is important here: */ free_turn_permission_hashtable(&(a->addr_to_perm)); ch_map_clean(&(a->chns)); a->is_valid=0; } } ts_ur_session *get_relay_session(allocation *a) { return &(a->relay_session); } ioa_socket_handle get_relay_socket(allocation *a) { return a->relay_session.s; } void set_allocation_lifetime_ev(allocation *a, turn_time_t exp_time, ioa_timer_handle ev) { if (a) { IOA_EVENT_DEL(a->lifetime_ev); a->expiration_time = exp_time; a->lifetime_ev = ev; } } int is_allocation_valid(const allocation* a) { if(a) return a->is_valid; else return 0; } void set_allocation_valid(allocation* a, int value) { if(a) a->is_valid=value; } turn_permission_info* allocation_get_permission(allocation* a, const ioa_addr *addr) { if(a) { return get_from_turn_permission_hashtable(&(a->addr_to_perm), addr); } return NULL; } ///////////////////////////// TURN_PERMISSION ///////////////////////////////// static int delete_channel_info_from_allocation_map(ur_map_key_type key, ur_map_value_type value); void turn_permission_clean(turn_permission_info* tinfo) { if (tinfo && tinfo->allocated) { if(!(tinfo->lifetime_ev)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: strange (1) permission to be cleaned\n",__FUNCTION__); } IOA_EVENT_DEL(tinfo->lifetime_ev); lm_map_foreach(&(tinfo->chns), (foreachcb_type) delete_channel_info_from_allocation_map); lm_map_clean(&(tinfo->chns)); ns_bzero(tinfo,sizeof(turn_permission_info)); } } static void init_turn_permission_hashtable(turn_permission_hashtable *map) { if (map) ns_bzero(map,sizeof(turn_permission_hashtable)); } static void free_turn_permission_hashtable(turn_permission_hashtable *map) { if(map) { size_t i; for(i=0;itable[i]); { size_t j; for(j=0;jmain_slots[j]); if(slot->info.allocated) { turn_permission_clean(&(slot->info)); } } } if(parray->extra_slots) { size_t j; for(j=0;jextra_sz;++j) { turn_permission_slot *slot = parray->extra_slots[j]; if(slot) { if(slot->info.allocated) { turn_permission_clean(&(slot->info)); } turn_free(slot,sizeof(turn_permission_slot)); } } turn_free(parray->extra_slots, parray->extra_sz * sizeof(turn_permission_slot*)); parray->extra_slots = NULL; } parray->extra_sz = 0; } } } static turn_permission_info* get_from_turn_permission_hashtable(turn_permission_hashtable *map, const ioa_addr *addr) { if (!addr || !map) return NULL; u32bits index = addr_hash_no_port(addr) & (TURN_PERMISSION_HASHTABLE_SIZE-1); turn_permission_array *parray = &(map->table[index]); { size_t i; for (i = 0; i < TURN_PERMISSION_ARRAY_SIZE; ++i) { turn_permission_slot *slot = &(parray->main_slots[i]); if (slot->info.allocated && addr_eq_no_port(&(slot->info.addr), addr)) { return &(slot->info); } } } if(parray->extra_slots) { size_t i; size_t sz = parray->extra_sz; for (i = 0; i < sz; ++i) { turn_permission_slot *slot = parray->extra_slots[i]; if (slot->info.allocated && addr_eq_no_port(&(slot->info.addr), addr)) { return &(slot->info); } } } return NULL; } static void ch_info_clean(ch_info* c) { if(c) { IOA_EVENT_DEL(c->lifetime_ev); ns_bzero(c,sizeof(ch_info)); } } static int delete_channel_info_from_allocation_map(ur_map_key_type key, ur_map_value_type value) { UNUSED_ARG(key); if(value) { ch_info* chn = (ch_info*)value; if(chn->chnum <1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: strange (0) channel to be cleaned: chnum<1\n",__FUNCTION__); } ch_info_clean(chn); } return 0; } void turn_channel_delete(ch_info* chn) { if(chn) { int port = addr_get_port(&(chn->peer_addr)); if(port<1) { char s[129]; addr_to_string(&(chn->peer_addr),(u08bits*)s); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: strange (1) channel to be cleaned: port is empty: %s\n",__FUNCTION__,s); } { turn_permission_info* tinfo = (turn_permission_info*)chn->owner; if(tinfo) { lm_map_del(&(tinfo->chns), (ur_map_key_type)port,NULL); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: strange (2) channel to be cleaned: permission is empty\n",__FUNCTION__); } } delete_channel_info_from_allocation_map((ur_map_key_type)port,(ur_map_value_type)chn); } } ch_info* allocation_get_new_ch_info(allocation* a, u16bits chnum, ioa_addr* peer_addr) { turn_permission_info* tinfo = get_from_turn_permission_hashtable(&(a->addr_to_perm), peer_addr); if (!tinfo) tinfo = allocation_add_permission(a, peer_addr); ch_info* chn = ch_map_get(&(a->chns), chnum, 1); chn->allocated = 1; chn->chnum = chnum; chn->port = addr_get_port(peer_addr); addr_cpy(&(chn->peer_addr), peer_addr); chn->owner = tinfo; lm_map_put(&(tinfo->chns), (ur_map_key_type) addr_get_port(peer_addr), (ur_map_value_type) chn); return chn; } ch_info* allocation_get_ch_info(allocation* a, u16bits chnum) { return ch_map_get(&(a->chns), chnum, 0); } ch_info* allocation_get_ch_info_by_peer_addr(allocation* a, ioa_addr* peer_addr) { turn_permission_info* tinfo = get_from_turn_permission_hashtable(&(a->addr_to_perm), peer_addr); if(tinfo) { return get_turn_channel(tinfo,peer_addr); } return NULL; } u16bits get_turn_channel_number(turn_permission_info* tinfo, ioa_addr *addr) { if (tinfo) { ur_map_value_type t = 0; if (lm_map_get(&(tinfo->chns), (ur_map_key_type)addr_get_port(addr), &t) && t) { ch_info* chn = (ch_info*) t; if (STUN_VALID_CHANNEL(chn->chnum)) { return chn->chnum; } } } return 0; } ch_info *get_turn_channel(turn_permission_info* tinfo, ioa_addr *addr) { if (tinfo) { ur_map_value_type t = 0; if (lm_map_get(&(tinfo->chns), (ur_map_key_type)addr_get_port(addr), &t) && t) { ch_info* chn = (ch_info*) t; if (STUN_VALID_CHANNEL(chn->chnum)) { return chn; } } } return NULL; } turn_permission_hashtable *allocation_get_turn_permission_hashtable(allocation *a) { return &(a->addr_to_perm); } turn_permission_info* allocation_add_permission(allocation *a, const ioa_addr* addr) { if (a && addr) { turn_permission_hashtable *map = &(a->addr_to_perm); u32bits hash = addr_hash_no_port(addr); size_t fds = (size_t) (hash & (TURN_PERMISSION_HASHTABLE_SIZE-1)); turn_permission_array *parray = &(map->table[fds]); turn_permission_slot *slot = NULL; { size_t i; for(i=0;imain_slots[i]); if(!(slot->info.allocated)) { break; } else { slot=NULL; } } } if(!slot) { size_t old_sz = parray->extra_sz; turn_permission_slot **slots = parray->extra_slots; if(slots) { size_t i; for(i=0;iinfo.allocated)) { break; } else { slot=NULL; } } } if(!slot) { size_t old_sz_mem = old_sz * sizeof(turn_permission_slot*); parray->extra_slots = (turn_permission_slot **) turn_realloc(parray->extra_slots, old_sz_mem, old_sz_mem + sizeof(turn_permission_slot*)); slots = parray->extra_slots; parray->extra_sz = old_sz + 1; slots[old_sz] = (turn_permission_slot *)turn_malloc(sizeof(turn_permission_slot)); slot = slots[old_sz]; } } ns_bzero(slot,sizeof(turn_permission_slot)); slot->info.allocated = 1; turn_permission_info *elem = &(slot->info); addr_cpy(&(elem->addr), addr); elem->owner = a; return elem; } else { return NULL; } } ch_info *ch_map_get(ch_map* map, u16bits chnum, int new_chn) { ch_info *ret = NULL; if(map) { size_t index = (size_t)(chnum & (CH_MAP_HASH_SIZE-1)); ch_map_array *a = &(map->table[index]); size_t i; for(i=0;imain_chns[i]); if(chi->allocated) { if(!new_chn && (chi->chnum == chnum)) { return chi; } } else if(new_chn) { return chi; } } size_t old_sz = a->extra_sz; if(old_sz && a->extra_chns) { for(i=0;iextra_chns[i]; if(chi) { if(chi->allocated) { if(!new_chn && (chi->chnum == chnum)) { return chi; } } else if(new_chn) { return chi; } } } } if(new_chn) { size_t old_sz_mem = old_sz * sizeof(ch_info*); a->extra_chns = (ch_info**)turn_realloc(a->extra_chns,old_sz_mem,old_sz_mem + sizeof(ch_info*)); a->extra_chns[old_sz] = (ch_info*)turn_malloc(sizeof(ch_info)); ns_bzero(a->extra_chns[old_sz],sizeof(ch_info)); a->extra_sz += 1; return a->extra_chns[old_sz]; } } return ret; } void ch_map_clean(ch_map* map) { if(map) { size_t index; for(index = 0; index < CH_MAP_HASH_SIZE; ++index) { ch_map_array *a = &(map->table[index]); size_t i; for(i=0;imain_chns[i]); if(chi->allocated) { ch_info_clean(chi); } } if(a->extra_chns) { size_t sz = a->extra_sz; for(i=0;iextra_chns[i]; if(chi) { if(chi->allocated) { ch_info_clean(chi); } turn_free(chi,sizeof(ch_info)); a->extra_chns[i] = NULL; } } turn_free(a->extra_chns, sizeof(ch_info*)*sz); a->extra_chns = NULL; } a->extra_sz = 0; } } } ////////////////// TCP connections /////////////////////////////// static void set_new_tc_id(u08bits server_id, tcp_connection *tc) { allocation *a = (allocation*)(tc->owner); ur_map *map = a->tcp_connections; u32bits newid; u32bits sid = server_id; sid = sid<<24; do { newid = 0; while (!newid) { newid = (u32bits)turn_random(); if(!newid) { continue; } newid = newid & 0x00FFFFFF; if(!newid) { continue; } newid = newid | sid; } } while(ur_map_get(map, (ur_map_key_type)newid, NULL)); tc->id = newid; ur_map_put(map, (ur_map_key_type)newid, (ur_map_value_type)tc); } tcp_connection *create_tcp_connection(u08bits server_id, allocation *a, stun_tid *tid, ioa_addr *peer_addr, int *err_code) { tcp_connection_list *tcl = &(a->tcs); if(tcl->elems) { size_t i; for(i=0;isz;++i) { tcp_connection *otc = tcl->elems[i]; if(otc) { if(addr_eq(&(otc->peer_addr),peer_addr)) { *err_code = 446; return NULL; } } } } tcp_connection *tc = (tcp_connection*)turn_malloc(sizeof(tcp_connection)); ns_bzero(tc,sizeof(tcp_connection)); addr_cpy(&(tc->peer_addr),peer_addr); if(tid) ns_bcopy(tid,&(tc->tid),sizeof(stun_tid)); tc->owner = a; int found = 0; if(a->tcs.elems) { size_t i; for(i=0;isz;++i) { tcp_connection *otc = tcl->elems[i]; if(!otc) { tcl->elems[i] = tc; found = 1; break; } } } if(!found) { size_t old_sz_mem = a->tcs.sz * sizeof(tcp_connection*); a->tcs.elems = (tcp_connection**)turn_realloc(a->tcs.elems,old_sz_mem,old_sz_mem+sizeof(tcp_connection*)); a->tcs.elems[a->tcs.sz] = tc; a->tcs.sz += 1; tcl = &(a->tcs); } set_new_tc_id(server_id, tc); return tc; } void delete_tcp_connection(tcp_connection *tc) { if(tc) { if(tc->done) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s: check on already closed tcp data connection: 0x%lx\n",__FUNCTION__,(unsigned long)tc); return; } tc->done = 1; clear_unsent_buffer(&(tc->ub_to_client)); IOA_EVENT_DEL(tc->peer_conn_timeout); IOA_EVENT_DEL(tc->conn_bind_timeout); allocation *a = (allocation*)(tc->owner); if(a) { ur_map *map = a->tcp_connections; if(map) { ur_map_del(map, (ur_map_key_type)(tc->id),NULL); } tcp_connection_list *tcl = &(a->tcs); if(tcl->elems) { size_t i; for(i=0;isz;++i) { if(tcl->elems[i] == tc) { tcl->elems[i] = NULL; break; } } } } IOA_CLOSE_SOCKET(tc->client_s); IOA_CLOSE_SOCKET(tc->peer_s); turn_free(tc,sizeof(tcp_connection)); } } tcp_connection *get_and_clean_tcp_connection_by_id(ur_map *map, tcp_connection_id id) { if(map) { ur_map_value_type t = 0; if (ur_map_get(map, (ur_map_key_type)id, &t) && t) { ur_map_del(map, (ur_map_key_type)id,NULL); return (tcp_connection*)t; } } return NULL; } tcp_connection *get_tcp_connection_by_peer(allocation *a, ioa_addr *peer_addr) { if(a && peer_addr) { tcp_connection_list *tcl = &(a->tcs); if(tcl->elems) { size_t i; size_t sz = tcl->sz; for(i=0;ielems[i]; if(tc) { if(addr_eq(&(tc->peer_addr),peer_addr)) { return tc; } } } } } return NULL; } int can_accept_tcp_connection_from_peer(allocation *a, ioa_addr *peer_addr, int server_relay) { if(server_relay) return 1; if(a && peer_addr) { return (get_from_turn_permission_hashtable(&(a->addr_to_perm), peer_addr) != NULL); } return 0; } //////////////// Unsent buffers ////////////////////// void clear_unsent_buffer(unsent_buffer *ub) { if(ub) { if(ub->bufs) { size_t sz; for(sz = 0; szsz; sz++) { ioa_network_buffer_handle nbh = ub->bufs[sz]; if(nbh) { ioa_network_buffer_delete(NULL, nbh); ub->bufs[sz] = NULL; } } turn_free(ub->bufs,sizeof(ioa_network_buffer_handle) * ub->sz); ub->bufs = NULL; } ub->sz = 0; } } void add_unsent_buffer(unsent_buffer *ub, ioa_network_buffer_handle nbh) { if(!ub || (ub->sz >= MAX_UNSENT_BUFFER_SIZE)) { ioa_network_buffer_delete(NULL, nbh); } else { ub->bufs = (ioa_network_buffer_handle*)turn_realloc(ub->bufs, sizeof(ioa_network_buffer_handle) * ub->sz, sizeof(ioa_network_buffer_handle) * (ub->sz+1)); ub->bufs[ub->sz] = nbh; ub->sz +=1; } } ioa_network_buffer_handle top_unsent_buffer(unsent_buffer *ub) { ioa_network_buffer_handle ret = NULL; if(ub && ub->bufs && ub->sz) { size_t sz; for(sz=0; szsz; ++sz) { if(ub->bufs[sz]) { ret = ub->bufs[sz]; break; } } } return ret; } void pop_unsent_buffer(unsent_buffer *ub) { if(ub && ub->bufs && ub->sz) { size_t sz; for(sz=0; szsz; ++sz) { if(ub->bufs[sz]) { ub->bufs[sz] = NULL; break; } } } } ////////////////////////////////////////////////////////////////// turnserver-3.2.3.1/src/server/ns_turn_khash.h000644 001751 001751 00000033207 12315706777 021155 0ustar00olegoleg000000 000000 /* The MIT License Copyright (c) 2008, by Attractive Chaos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Changes, 2011-2012: proprietary header added, with proprietary memory management functions. */ /* An example: #include "khash.h" KHASH_MAP_INIT_INT(32, s08bits) int main() { int ret, is_missing; khiter_t k; khash_t(32) *h = kh_init(32); k = kh_put(32, h, 5, &ret); if (!ret) kh_del(32, h, k); kh_value(h, k) = 10; k = kh_get(32, h, 10); is_missing = (k == kh_end(h)); k = kh_get(32, h, 5); kh_del(32, h, k); for (k = kh_begin(h); k != kh_end(h); ++k) if (kh_exist(h, k)) kh_value(h, k) = 1; kh_destroy(32, h); return 0; } */ /* 2008-09-19 (0.2.3): * Corrected the example * Improved interfaces 2008-09-11 (0.2.2): * Improved speed a little in kh_put() 2008-09-10 (0.2.1): * Added kh_clear() * Fixed a compiling error 2008-09-02 (0.2.0): * Changed to token concatenation which increases flexibility. 2008-08-31 (0.1.2): * Fixed a bug in kh_get(), which has not been tested previously. 2008-08-31 (0.1.1): * Added destructor */ #ifndef __AC_KHASH_H #define __AC_KHASH_H #define AC_VERSION_KHASH_H "0.2.2" #include "ns_turn_defs.h" typedef u32bits khint_t; typedef khint_t khiter_t; typedef struct _str_chunk_t { const s08bits *str; size_t len; } str_chunk_t; #define __ac_HASH_PRIME_SIZE 32 static const u32bits __ac_prime_list[__ac_HASH_PRIME_SIZE] = { 0ul, 3ul, 11ul, 23ul, 53ul, 97ul, 193ul, 389ul, 769ul, 1543ul, 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, 1610612741ul, 3221225473ul, 4294967291ul }; #define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) #define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) #define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) #define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) #define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) #define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) #define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) static const double __ac_HASH_UPPER = 0.77; #define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ typedef struct { \ khint_t n_buckets, size, n_occupied, upper_bound; \ u32bits *flags; u32bits flags_size; \ khkey_t *keys; u32bits keys_size; \ khval_t *vals; u32bits vals_size; \ } kh_##name##_t; \ static inline kh_##name##_t *kh_init_##name(void) { \ return (kh_##name##_t*)turn_calloc(1, sizeof(kh_##name##_t)); \ } \ static inline void kh_destroy_##name(kh_##name##_t *h) \ { \ if (h) { \ turn_free(h->keys,h->keys_size); turn_free(h->flags,h->flags_size); \ turn_free(h->vals, h->vals_size); \ turn_free(h, sizeof(kh_##name##_t)); \ } \ } \ static inline void kh_clear_##name(kh_##name##_t *h) \ { \ if (h && h->flags) { \ memset(h->flags, 0xaa, ((h->n_buckets>>4) + 1) * sizeof(u32bits)); \ h->size = h->n_occupied = 0; \ } \ } \ static inline khint_t kh_get_##name(kh_##name##_t *h, khkey_t key) \ { \ if (h->n_buckets) { \ khint_t inc, k, i, last; \ k = __hash_func(key); i = k % h->n_buckets; \ inc = 1 + k % (h->n_buckets - 1); last = i; \ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ if (i + inc >= h->n_buckets) i = i + inc - h->n_buckets; \ else i += inc; \ if (i == last) return h->n_buckets; \ } \ return __ac_iseither(h->flags, i)? h->n_buckets : i; \ } else return 0; \ } \ static inline void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ { \ u32bits *new_flags = 0; \ u32bits new_flags_size = 0; \ khint_t j = 1; \ { \ khint_t t = __ac_HASH_PRIME_SIZE - 1; \ while (__ac_prime_list[t] > new_n_buckets) --t; \ new_n_buckets = __ac_prime_list[t+1]; \ if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; \ else { \ new_flags_size = ((new_n_buckets>>4) + 1) * sizeof(u32bits); \ new_flags = (u32bits*)turn_malloc(new_flags_size); \ memset(new_flags, 0xaa, new_flags_size); \ if (h->n_buckets < new_n_buckets) { \ h->keys = (khkey_t*)turn_realloc(h->keys, h->keys_size, new_n_buckets * sizeof(khkey_t)); \ h->keys_size = new_n_buckets * sizeof(khkey_t); \ if (kh_is_map) { \ h->vals = (khval_t*)turn_realloc(h->vals, h->vals_size, new_n_buckets * sizeof(khval_t)); \ h->vals_size = new_n_buckets * sizeof(khval_t); \ } \ } \ } \ } \ if (j) { \ for (j = 0; j != h->n_buckets; ++j) { \ if (__ac_iseither(h->flags, j) == 0) { \ khkey_t key = h->keys[j]; \ khval_t val; \ if (kh_is_map) val = h->vals[j]; \ __ac_set_isdel_true(h->flags, j); \ while (1) { \ khint_t inc, k, i; \ k = __hash_func(key); \ i = k % new_n_buckets; \ inc = 1 + k % (new_n_buckets - 1); \ while (!__ac_isempty(new_flags, i)) { \ if (i + inc >= new_n_buckets) i = i + inc - new_n_buckets; \ else i += inc; \ } \ __ac_set_isempty_false(new_flags, i); \ if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { \ { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ __ac_set_isdel_true(h->flags, i); \ } else { \ h->keys[i] = key; \ if (kh_is_map) h->vals[i] = val; \ break; \ } \ } \ } \ } \ if (h->n_buckets > new_n_buckets) { \ h->keys = (khkey_t*)turn_realloc(h->keys, h->keys_size, new_n_buckets * sizeof(khkey_t)); \ h->keys_size = new_n_buckets * sizeof(khkey_t); \ if (kh_is_map) { \ h->vals = (khval_t*)turn_realloc(h->vals, h->vals_size, new_n_buckets * sizeof(khval_t)); \ h->vals_size = new_n_buckets * sizeof(khval_t); \ } \ } \ turn_free(h->flags, h->flags_size); \ h->flags = new_flags; \ h->flags_size = new_flags_size; \ h->n_buckets = new_n_buckets; \ h->n_occupied = h->size; \ h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ } \ } \ static inline khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ { \ khint_t x; \ if (h->n_occupied >= h->upper_bound) { \ if (h->n_buckets > (h->size<<1)) kh_resize_##name(h, h->n_buckets - 1); \ else kh_resize_##name(h, h->n_buckets + 1); \ } \ { \ khint_t inc, k, i, site, last; \ x = site = h->n_buckets; k = __hash_func(key); i = k % h->n_buckets; \ if (__ac_isempty(h->flags, i)) x = i; \ else { \ inc = 1 + k % (h->n_buckets - 1); last = i; \ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ if (__ac_isdel(h->flags, i)) site = i; \ if (i + inc >= h->n_buckets) i = i + inc - h->n_buckets; \ else i += inc; \ if (i == last) { x = site; break; } \ } \ if (x == h->n_buckets) { \ if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ else x = i; \ } \ } \ } \ if (__ac_isempty(h->flags, x)) { \ h->keys[x] = key; \ __ac_set_isboth_false(h->flags, x); \ ++h->size; ++h->n_occupied; \ *ret = 1; \ } else if (__ac_isdel(h->flags, x)) { \ h->keys[x] = key; \ __ac_set_isboth_false(h->flags, x); \ ++h->size; \ *ret = 2; \ } else *ret = 0; \ return x; \ } \ static inline void kh_del_##name(kh_##name##_t *h, khint_t x) \ { \ if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ __ac_set_isdel_true(h->flags, x); \ --h->size; \ } \ } /* --- BEGIN OF HASH FUNCTIONS --- */ #define kh_int_hash_func(key) (u32bits)((key<<3) + nswap32(key>>7)) #define kh_int_hash_equal(a, b) (a == b) #define kh_int64_hash_func(key) (u32bits)((key)>>33^(key)^(key)<<11) #define kh_int64_hash_equal(a, b) (a == b) static inline khint_t __ac_X31_hash_string(const s08bits *s) { khint_t h = *s; if (h) for (++s; *s; ++s) h = (h << 5) - h + *s; return h; } static inline khint_t __ac_X31_hash_cstring(const s08bits *s) { khint_t h = tolower((int)*s); if (h) for (++s; *s; ++s) h = (h << 5) - h + tolower((int)*s); return h; } static inline khint_t __ac_X31_hash_nstring(const str_chunk_t *s) { khint_t h = *(s->str); if (h) { size_t i; for (i = 0; i < s->len; i++) h = (h << 5) - h + s->str[i]; } return h; } static inline khint_t __ac_X31_hash_ncstring(const str_chunk_t *s) { khint_t h = tolower((int)(*(s->str))); if (h) { size_t i; for (i = 0; i < s->len; i++) h = (h << 5) - h + tolower((int)(s->str[i])); } return h; } #define kh_str_hash_func(key) __ac_X31_hash_string(key) #define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) #define kh_cstr_hash_func(key) __ac_X31_hash_cstring(key) #define kh_cstr_hash_equal(a, b) (strcasecmp(a, b) == 0) #define kh_nstr_hash_func(key) __ac_X31_hash_nstring(key) #define kh_nstr_hash_equal(a, b) (a->len == b->len && (strncmp(a->str, b->str, a->len) == 0)) #define kh_ncstr_hash_func(key) __ac_X31_hash_ncstring(key) #define kh_ncstr_hash_equal(a, b) (a->len == b->len && (strncasecmp(a->str, b->str, a->len) == 0)) /* --- END OF HASH FUNCTIONS --- */ /* Other necessary macros... */ #define khash_t(name) kh_##name##_t #define kh_init(name) kh_init_##name() #define kh_destroy(name, h) kh_destroy_##name(h) #define kh_clear(name, h) kh_clear_##name(h) #define kh_resize(name, h, s) kh_resize_##name(h, s) #define kh_put(name, h, k, r) kh_put_##name(h, k, r) #define kh_get(name, h, k) kh_get_##name(h, k) #define kh_del(name, h, k) kh_del_##name(h, k) #define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) #define kh_key(h, x) ((h)->keys[x]) #define kh_val(h, x) ((h)->vals[x]) #define kh_value(h, x) ((h)->vals[x]) #define kh_begin(h) (khint_t)(0) #define kh_end(h) ((h)->n_buckets) #define kh_size(h) ((h)->size) #define kh_n_buckets(h) ((h)->n_buckets) /* More convenient interfaces */ #define KHASH_SET_INIT_INT(name) \ KHASH_INIT(name, u32bits, s08bits, 0, kh_int_hash_func, kh_int_hash_equal) #define KHASH_MAP_INIT_INT(name, khval_t) \ KHASH_INIT(name, u32bits, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) #define KHASH_SET_INIT_INT64(name) \ KHASH_INIT(name, u64bits, s08bits, 0, kh_int64_hash_func, kh_int64_hash_equal) #define KHASH_MAP_INIT_INT64(name, khval_t) \ KHASH_INIT(name, u64bits, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) typedef const s08bits *kh_cstr_t; typedef const str_chunk_t *kh_ncstr_t; #define KHASH_SET_INIT_STR(name) \ KHASH_INIT(name, kh_cstr_t, s08bits, 0, kh_str_hash_func, kh_str_hash_equal) #define KHASH_MAP_INIT_STR(name, khval_t) \ KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) #define KHASH_SET_INIT_CSTR(name) \ KHASH_INIT(name, kh_cstr_t, s08bits, 0, kh_cstr_hash_func, kh_cstr_hash_equal) #define KHASH_MAP_INIT_CSTR(name, khval_t) \ KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_cstr_hash_func, kh_cstr_hash_equal) #define KHASH_SET_INIT_NSTR(name) \ KHASH_INIT(name, kh_ncstr_t, s08bits, 0, kh_nstr_hash_func, kh_nstr_hash_equal) #define KHASH_MAP_INIT_NSTR(name, khval_t) \ KHASH_INIT(name, kh_ncstr_t, khval_t, 1, kh_nstr_hash_func, kh_nstr_hash_equal) #define KHASH_SET_INIT_NCSTR(name) \ KHASH_INIT(name, kh_ncstr_t, s08bits, 0, kh_ncstr_hash_func, kh_ncstr_hash_equal) #define KHASH_MAP_INIT_NCSTR(name, khval_t) \ KHASH_INIT(name, kh_ncstr_t, khval_t, 1, kh_ncstr_hash_func, kh_ncstr_hash_equal) ////////////////////////////////////////////// #endif /* __AC_KHASH_H */ turnserver-3.2.3.1/src/server/ns_turn_maps_rtcp.c000644 001751 001751 00000015551 12315706777 022044 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "ns_turn_maps_rtcp.h" #include "ns_turn_utils.h" #include "ns_turn_ioaddr.h" //////////////////////////////////////////// #define MAGIC_RTCP_MAP (0x76859403) #define RTCP_TIMEOUT (300) #define MAX_TOKEN_DEL (1024) //////////////////////////////////////////// struct _rtcp_map { u32bits magic; ur_map *map; ioa_timer_handle timer_ev; TURN_MUTEX_DECLARE(mutex) }; typedef struct { ioa_socket_handle s; turn_time_t t; rtcp_token_type token; } rtcp_alloc_type; //////////////////////////////////////////// static int rtcp_map_valid(const rtcp_map *map) { return (map && (map->magic==MAGIC_RTCP_MAP) && map->map); } typedef struct { rtcp_token_type tokens[MAX_TOKEN_DEL]; int tn; turn_time_t t; } timeout_check_arg_type; static int timeout_check(ur_map_key_type key, ur_map_value_type value, void *arg) { if(value && arg) { timeout_check_arg_type *tcat=(timeout_check_arg_type*)arg; rtcp_alloc_type* rat=(rtcp_alloc_type*)value; if(turn_time_before(rat->t, tcat->t) && (tcat->tntokens[(tcat->tn)++]=key; } } return 0; } static void rtcp_map_timeout_handler(ioa_engine_handle e, void* arg) { UNUSED_ARG(e); if(!arg) return; rtcp_map* map=(rtcp_map*)arg; if(rtcp_map_valid(map)) { TURN_MUTEX_LOCK(&map->mutex); timeout_check_arg_type tcat; tcat.tn=0; tcat.t=turn_time(); ur_map_foreach_arg(map->map, timeout_check, &tcat); TURN_MUTEX_UNLOCK(&map->mutex); int i=0; for(i=0;imagic != MAGIC_RTCP_MAP) { map->magic = MAGIC_RTCP_MAP; map->map = ur_map_create(); if (e) map->timer_ev = set_ioa_timer(e, 3, 0, rtcp_map_timeout_handler, map, 1, "rtcp_map_timeout_handler"); TURN_MUTEX_INIT(&map->mutex); if (rtcp_map_valid(map)) return 0; } } return -1; } rtcp_map* rtcp_map_create(ioa_engine_handle e) { rtcp_map *map=(rtcp_map*)turn_malloc(sizeof(rtcp_map)); ns_bzero(map,sizeof(rtcp_map)); if(rtcp_map_init(map,e)<0) { turn_free(map,sizeof(rtcp_map)); return NULL; } return map; } /** * @ret: * 0 - success * -1 - error */ int rtcp_map_put(rtcp_map* map, rtcp_token_type token, ioa_socket_handle s) { if(!rtcp_map_valid(map)) return -1; else { rtcp_alloc_type *value=(rtcp_alloc_type*)turn_malloc(sizeof(rtcp_alloc_type)); if(!value) return -1; ns_bzero(value,sizeof(rtcp_alloc_type)); value->s=s; value->t=turn_time() + RTCP_TIMEOUT; value->token=token; TURN_MUTEX_LOCK(&map->mutex); int ret = ur_map_put(map->map,token,(ur_map_value_type)value); //TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: 111.111: ret=%d, token=%llu\n",__FUNCTION__,ret,token); TURN_MUTEX_UNLOCK(&map->mutex); if(ret<0) turn_free(value,sizeof(rtcp_alloc_type)); return ret; } } /** * @ret: * >=0 - success * <0 - not found */ ioa_socket_handle rtcp_map_get(const rtcp_map* map, rtcp_token_type token) { if(!rtcp_map_valid(map)) return NULL; else { ur_map_value_type value; TURN_MUTEX_LOCK(&map->mutex); int ret = ur_map_get(map->map,token,&value); //TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: 111.111: ret=%d, value=%llu, token=%llu\n",__FUNCTION__,ret,(unsigned long)value,token); TURN_MUTEX_UNLOCK(&map->mutex); if(!ret) return NULL; rtcp_alloc_type* rval=(rtcp_alloc_type*)value; if(!rval) return NULL; //TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: 111.222: ret=%d, token=%llu\n",__FUNCTION__,ret,token); return rval->s; } } static void rtcp_alloc_free(ur_map_value_type value) { rtcp_alloc_type *at = (rtcp_alloc_type *)value; if (at) { IOA_CLOSE_SOCKET(at->s); turn_free(at,sizeof(rtcp_alloc_type)); } } static void rtcp_alloc_free_savefd(ur_map_value_type value) { rtcp_alloc_type *at = (rtcp_alloc_type *) value; if (at) { turn_free(at,sizeof(rtcp_alloc_type)); } } static int foreachcb_free(ur_map_key_type key, ur_map_value_type value) { UNUSED_ARG(key); if(value) { rtcp_alloc_free(value); } return 0; } /** * @ret: * 1 - success * 0 - not found */ int rtcp_map_del(rtcp_map* map, rtcp_token_type token) { if(!rtcp_map_valid(map)) return 0; else { TURN_MUTEX_LOCK(&map->mutex); int ret = ur_map_del(map->map,token,rtcp_alloc_free); TURN_MUTEX_UNLOCK(&map->mutex); return ret; } } int rtcp_map_del_savefd(rtcp_map* map, rtcp_token_type token) { if(!rtcp_map_valid(map)) return 0; else { TURN_MUTEX_LOCK(&map->mutex); int ret = ur_map_del(map->map,token,rtcp_alloc_free_savefd); TURN_MUTEX_UNLOCK(&map->mutex); return ret; } } void rtcp_map_free(rtcp_map** map) { if(map && rtcp_map_valid(*map)) { TURN_MUTEX_LOCK(&((*map)->mutex)); IOA_EVENT_DEL((*map)->timer_ev); ur_map_foreach((*map)->map, foreachcb_free); ur_map_free(&((*map)->map)); (*map)->magic=0; TURN_MUTEX_UNLOCK(&((*map)->mutex)); TURN_MUTEX_DESTROY(&((*map)->mutex)); turn_free(*map,sizeof(rtcp_map)); *map=NULL; } } size_t rtcp_map_size(const rtcp_map* map) { if(rtcp_map_valid(map)) { TURN_MUTEX_LOCK(&map->mutex); size_t ret = ur_map_size(map->map); TURN_MUTEX_UNLOCK(&map->mutex); return ret; } else { return 0; } } //////////////////////////////////////////////////////////////// turnserver-3.2.3.1/src/server/ns_turn_session.h000644 001751 001751 00000010176 12315706777 021542 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __TURN_SESSION__ #define __TURN_SESSION__ #include "ns_turn_utils.h" #include "ns_turn_maps.h" #include "ns_turn_ioalib.h" #include "ns_turn_allocation.h" #ifdef __cplusplus extern "C" { #endif //////////////// session info ////////////////////// typedef u64bits turnsession_id; #define NONCE_MAX_SIZE (NONCE_LENGTH_32BITS*4+1) typedef u64bits mobile_id_t; struct _ts_ur_super_session { void* server; turnsession_id id; turn_time_t start_time; ts_ur_session client_session; ioa_addr default_peer_addr; allocation alloc; ioa_timer_handle to_be_allocated_timeout_ev; u08bits nonce[NONCE_MAX_SIZE]; turn_time_t nonce_expiration_time; u08bits username[STUN_MAX_USERNAME_SIZE+1]; hmackey_t hmackey; int hmackey_set; st_password_t pwd; int enforce_fingerprints; int is_tcp_relay; int to_be_closed; /* Stats */ u32bits received_packets; u32bits sent_packets; u32bits received_bytes; u32bits sent_bytes; u64bits t_received_packets; u64bits t_sent_packets; u64bits t_received_bytes; u64bits t_sent_bytes; u64bits received_rate; size_t sent_rate; size_t total_rate; /* Mobile */ int is_mobile; mobile_id_t mobile_id; char s_mobile_id[33]; }; ////// Session info for statistics ////// #define TURN_ADDR_STR_SIZE (65) #define TURN_MAIN_PEERS_ARRAY_SIZE (5) typedef struct _addr_data { ioa_addr addr; char saddr[TURN_ADDR_STR_SIZE]; } addr_data; struct turn_session_info { turnsession_id id; int valid; turn_time_t start_time; turn_time_t expiration_time; SOCKET_TYPE client_protocol; SOCKET_TYPE peer_protocol; char tls_method[17]; char tls_cipher[65]; addr_data local_addr_data; addr_data remote_addr_data; addr_data relay_addr_data; u08bits username[STUN_MAX_USERNAME_SIZE+1]; int enforce_fingerprints; /* Stats */ u64bits received_packets; u64bits sent_packets; u64bits received_bytes; u64bits sent_bytes; u32bits received_rate; u32bits sent_rate; u32bits total_rate; /* Mobile */ int is_mobile; /* Peers */ addr_data main_peers_data[TURN_MAIN_PEERS_ARRAY_SIZE]; size_t main_peers_size; addr_data *extra_peers_data; size_t extra_peers_size; }; void turn_session_info_init(struct turn_session_info* tsi); void turn_session_info_clean(struct turn_session_info* tsi); void turn_session_info_add_peer(struct turn_session_info* tsi, ioa_addr *peer); int turn_session_info_copy_from(struct turn_session_info* tsi, ts_ur_super_session *ss); ////////////// ss ///////////////////// allocation* get_allocation_ss(ts_ur_super_session *ss); /////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_SESSION__ turnserver-3.2.3.1/src/server/ns_turn_server.c000644 001751 001751 00000356043 12315706777 021366 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "ns_turn_server.h" #include "ns_turn_utils.h" #include "ns_turn_allocation.h" #include "ns_turn_msg_addr.h" #include "ns_turn_ioalib.h" /////////////////////////////////////////// #define FUNCSTART if(server && eve(server->verbose)) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s:%d:start\n",__FUNCTION__,__LINE__) #define FUNCEND if(server && eve(server->verbose)) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s:%d:end\n",__FUNCTION__,__LINE__) //////////////////////////////////////////////// #define MAX_NUMBER_OF_UNKNOWN_ATTRS (128) int TURN_MAX_ALLOCATE_TIMEOUT = 60; int TURN_MAX_ALLOCATE_TIMEOUT_STUN_ONLY = 3; /////////////////////////////////////////// static int attach_socket_to_session(turn_turnserver* server, ioa_socket_handle s, ts_ur_super_session* ss); static int check_stun_auth(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, u16bits method, int *message_integrity, int *postpone_reply, int can_resume); static int create_relay_connection(turn_turnserver* server, ts_ur_super_session *ss, u32bits lifetime, int address_family, u08bits transport, int even_port, u64bits in_reservation_token, u64bits *out_reservation_token, int *err_code, const u08bits **reason, accept_cb acb); static int refresh_relay_connection(turn_turnserver* server, ts_ur_super_session *ss, u32bits lifetime, int even_port, u64bits in_reservation_token, u64bits *out_reservation_token, int *err_code); static int write_client_connection(turn_turnserver *server, ts_ur_super_session* ss, ioa_network_buffer_handle nbh, int ttl, int tos); static void tcp_peer_accept_connection(ioa_socket_handle s, void *arg); static int read_client_connection(turn_turnserver *server, ts_ur_session *elem, ts_ur_super_session *ss, ioa_net_data *in_buffer, int can_resume, int count_usage); static int need_stun_authentication(turn_turnserver *server); /////////////////// timer ////////////////////////// static void timer_timeout_handler(ioa_engine_handle e, void *arg) { UNUSED_ARG(e); if(arg) { turn_turnserver *server=(turn_turnserver*)arg; server->ctime = turn_time(); } } turn_time_t get_turn_server_time(turn_turnserver *server) { if(server) { return server->ctime; } return turn_time(); } /////////////////// server lists /////////////////// void init_turn_server_addrs_list(turn_server_addrs_list_t *l) { if(l) { l->addrs = NULL; l->size = 0; turn_mutex_init(&(l->m)); } } /////////////////// RFC 5780 /////////////////////// void set_rfc5780(turn_turnserver *server, get_alt_addr_cb cb, send_message_cb smcb) { if(server) { if(!cb || !smcb) { server->rfc5780 = 0; server->alt_addr_cb = NULL; server->sm_cb = NULL; } else { server->rfc5780 = 1; server->alt_addr_cb = cb; server->sm_cb = smcb; } } } static int is_rfc5780(turn_turnserver *server) { if(!server) return 0; return ((server->rfc5780) && (server->alt_addr_cb)); } static int get_other_address(turn_turnserver *server, ts_ur_super_session *ss, ioa_addr *alt_addr) { if(is_rfc5780(server) && ss && ss->client_session.s) { int ret = server->alt_addr_cb(get_local_addr_from_ioa_socket(ss->client_session.s), alt_addr); return ret; } return -1; } static int send_turn_message_to(turn_turnserver *server, ioa_network_buffer_handle nbh, ioa_addr *response_origin, ioa_addr *response_destination) { if(is_rfc5780(server) && nbh && response_origin && response_destination) { return server->sm_cb(server->e, nbh, response_origin, response_destination); } return -1; } /////////////////// Peer addr check ///////////////////////////// static int good_peer_addr(turn_turnserver *server, ioa_addr *peer_addr) { if(server && peer_addr) { if(*(server->no_multicast_peers) && ioa_addr_is_multicast(peer_addr)) return 0; if(*(server->no_loopback_peers) && ioa_addr_is_loopback(peer_addr)) return 0; { int i; if(server->ip_whitelist) { // White listing of addr ranges for (i = server->ip_whitelist->ranges_number - 1; i >= 0; --i) { if (ioa_addr_in_range(server->ip_whitelist->encaddrsranges[i], peer_addr)) return 1; } } { ioa_lock_whitelist(server->e); const ip_range_list_t* wl = ioa_get_whitelist(server->e); if(wl) { // White listing of addr ranges for (i = wl->ranges_number - 1; i >= 0; --i) { if (ioa_addr_in_range(wl->encaddrsranges[i], peer_addr)) { ioa_unlock_whitelist(server->e); return 1; } } } ioa_unlock_whitelist(server->e); } if(server->ip_blacklist) { // Black listing of addr ranges for (i = server->ip_blacklist->ranges_number - 1; i >= 0; --i) { if (ioa_addr_in_range(server->ip_blacklist->encaddrsranges[i], peer_addr)) { char saddr[129]; addr_to_string_no_port(peer_addr,(u08bits*)saddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "A peer IP %s denied in the range: %s\n",saddr,server->ip_blacklist->ranges[i]); return 0; } } } { ioa_lock_blacklist(server->e); const ip_range_list_t* bl = ioa_get_blacklist(server->e); if(bl) { // Black listing of addr ranges for (i = bl->ranges_number - 1; i >= 0; --i) { if (ioa_addr_in_range(bl->encaddrsranges[i], peer_addr)) { ioa_unlock_blacklist(server->e); char saddr[129]; addr_to_string_no_port(peer_addr,(u08bits*)saddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "A peer IP %s denied in the range: %s\n",saddr,bl->ranges[i]); return 0; } } } ioa_unlock_blacklist(server->e); } } } return 1; } /////////////////// Allocation ////////////////////////////////// allocation* get_allocation_ss(ts_ur_super_session *ss) { return &(ss->alloc); } static inline ts_ur_session *get_relay_session_ss(ts_ur_super_session *ss) { return &(ss->alloc.relay_session); } static inline ioa_socket_handle get_relay_socket_ss(ts_ur_super_session *ss) { return ss->alloc.relay_session.s; } /////////// Session info /////// void turn_session_info_init(struct turn_session_info* tsi) { if(tsi) { ns_bzero(tsi,sizeof(struct turn_session_info)); } } void turn_session_info_clean(struct turn_session_info* tsi) { if(tsi) { if(tsi->extra_peers_data) { turn_free(tsi->extra_peers_data, sizeof(addr_data)*(tsi->extra_peers_size)); } turn_session_info_init(tsi); } } void turn_session_info_add_peer(struct turn_session_info* tsi, ioa_addr *peer) { if(tsi && peer) { { size_t i; for(i=0;imain_peers_size;++i) { if(addr_eq(peer, &(tsi->main_peers_data[i].addr))) { return; } } if(tsi->main_peers_size < TURN_MAIN_PEERS_ARRAY_SIZE) { addr_cpy(&(tsi->main_peers_data[tsi->main_peers_size].addr),peer); addr_to_string(&(tsi->main_peers_data[tsi->main_peers_size].addr), (u08bits*)tsi->main_peers_data[tsi->main_peers_size].saddr); tsi->main_peers_size += 1; return; } } if(tsi->extra_peers_data) { size_t sz; for(sz=0;szextra_peers_size;++sz) { if(addr_eq(peer, &(tsi->extra_peers_data[sz].addr))) { return; } } } tsi->extra_peers_data = (addr_data*)turn_realloc(tsi->extra_peers_data,tsi->extra_peers_size*sizeof(addr_data),(tsi->extra_peers_size+1)*sizeof(addr_data)); addr_cpy(&(tsi->extra_peers_data[tsi->extra_peers_size].addr),peer); addr_to_string(&(tsi->extra_peers_data[tsi->extra_peers_size].addr), (u08bits*)tsi->extra_peers_data[tsi->extra_peers_size].saddr); tsi->extra_peers_size += 1; } } struct tsi_arg { struct turn_session_info* tsi; ioa_addr *addr; }; static int turn_session_info_foreachcb(ur_map_key_type key, ur_map_value_type value, void *arg) { UNUSED_ARG(value); int port = (int)key; struct tsi_arg *ta = (struct tsi_arg *)arg; if(port && ta && ta->tsi && ta->addr) { ioa_addr a; addr_cpy(&a,ta->addr); addr_set_port(&a,port); turn_session_info_add_peer(ta->tsi,&a); } return 0; } int turn_session_info_copy_from(struct turn_session_info* tsi, ts_ur_super_session *ss) { int ret = -1; if(tsi && ss) { tsi->id = ss->id; tsi->start_time = ss->start_time; tsi->valid = ss->alloc.is_valid; if(tsi->valid) { if(ss->to_be_closed) { tsi->valid = 0; } } if(tsi->valid) { tsi->expiration_time = ss->alloc.expiration_time; if(ss->client_session.s) { tsi->client_protocol = get_ioa_socket_type(ss->client_session.s); addr_cpy(&(tsi->local_addr_data.addr),get_local_addr_from_ioa_socket(ss->client_session.s)); addr_to_string(&(tsi->local_addr_data.addr),(u08bits*)tsi->local_addr_data.saddr); addr_cpy(&(tsi->remote_addr_data.addr),get_remote_addr_from_ioa_socket(ss->client_session.s)); addr_to_string(&(tsi->remote_addr_data.addr),(u08bits*)tsi->remote_addr_data.saddr); } if(ss->alloc.relay_session.s) { tsi->peer_protocol = get_ioa_socket_type(ss->alloc.relay_session.s); addr_cpy(&(tsi->relay_addr_data.addr),get_local_addr_from_ioa_socket(ss->alloc.relay_session.s)); addr_to_string(&(tsi->relay_addr_data.addr),(u08bits*)tsi->relay_addr_data.saddr); } STRCPY(tsi->username,ss->username); tsi->enforce_fingerprints = ss->enforce_fingerprints; STRCPY(tsi->tls_method, get_ioa_socket_tls_method(ss->client_session.s)); STRCPY(tsi->tls_cipher, get_ioa_socket_tls_cipher(ss->client_session.s)); if(ss->t_received_packets > ss->received_packets) tsi->received_packets = ss->t_received_packets; else tsi->received_packets = ss->received_packets; if(ss->t_sent_packets > ss->sent_packets) tsi->sent_packets = ss->t_sent_packets; else tsi->sent_packets = ss->sent_packets; if(ss->t_received_bytes > ss->received_bytes) tsi->received_bytes = ss->t_received_bytes; else tsi->received_bytes = ss->received_bytes; if(ss->t_sent_bytes > ss->sent_bytes) tsi->sent_bytes = ss->t_sent_bytes; else tsi->sent_bytes = ss->sent_bytes; { tsi->received_rate = ss->received_rate; tsi->sent_rate = ss->sent_rate; tsi->total_rate = tsi->received_rate + tsi->sent_rate; } tsi->is_mobile = ss->is_mobile; { size_t i; for(i=0;ialloc.addr_to_perm.table[i]); { size_t j; for(j=0;jmain_slots[j]); if(slot->info.allocated) { turn_session_info_add_peer(tsi,&(slot->info.addr)); struct tsi_arg arg = { tsi, &(slot->info.addr) }; lm_map_foreach_arg(&(slot->info.chns), turn_session_info_foreachcb, &arg); } } } { turn_permission_slot **slots = parray->extra_slots; if(slots) { size_t sz = parray->extra_sz; size_t j; for(j=0;jinfo.allocated) { turn_session_info_add_peer(tsi,&(slot->info.addr)); struct tsi_arg arg = { tsi, &(slot->info.addr) }; lm_map_foreach_arg(&(slot->info.chns), turn_session_info_foreachcb, &arg); } } } } } } { tcp_connection_list *tcl = &(ss->alloc.tcs); if(tcl->elems) { size_t i; size_t sz = tcl->sz; for(i=0;ielems[i]) { tcp_connection *tc = tcl->elems[i]; if(tc) { turn_session_info_add_peer(tsi,&(tc->peer_addr)); } } } } } } ret = 0; } return ret; } int report_turn_session_info(turn_turnserver *server, ts_ur_super_session *ss, int force_invalid) { if(server && ss && server->send_turn_session_info) { struct turn_session_info tsi; turn_session_info_init(&tsi); if(turn_session_info_copy_from(&tsi,ss)<0) { turn_session_info_clean(&tsi); } else { if(force_invalid) tsi.valid = 0; if(server->send_turn_session_info(&tsi)<0) { turn_session_info_clean(&tsi); } else { return 0; } } } return -1; } /////////// SS ///////////////// static int mobile_id_to_string(mobile_id_t mid, char *dst, size_t dst_sz) { size_t output_length = 0; if(!dst) return -1; char *s = base64_encode((const unsigned char *)&mid, sizeof(mid), &output_length); if(!s) return -1; if(!output_length || (output_length+1 > dst_sz)) { turn_free(s, output_length); return -1; } ns_bcopy(s, dst, output_length); turn_free(s, output_length); dst[output_length] = 0; return (int)output_length; } static mobile_id_t string_to_mobile_id(char* src) { mobile_id_t mid = 0; if(src) { size_t output_length = 0; unsigned char *out = base64_decode(src, strlen(src), &output_length); if(out) { if(output_length == sizeof(mid)) { mid = *((mobile_id_t*)out); } turn_free(out, output_length); } } return mid; } static mobile_id_t get_new_mobile_id(turn_turnserver* server) { mobile_id_t newid = 0; if(server && server->mobile_connections_map) { ur_map *map = server->mobile_connections_map; u64bits sid = server->id; sid = sid<<56; do { while (!newid) { if(TURN_RANDOM_SIZE == sizeof(mobile_id_t)) newid = (mobile_id_t)turn_random(); else { newid = (mobile_id_t)turn_random(); newid = (newid<<32) + (mobile_id_t)turn_random(); } if(!newid) { continue; } newid = newid & 0x00FFFFFFFFFFFFFFLL; if(!newid) { continue; } newid = newid | sid; } } while(ur_map_get(map, (ur_map_key_type)newid, NULL)); } return newid; } static void put_session_into_mobile_map(ts_ur_super_session *ss) { if(ss && ss->server) { turn_turnserver* server = (turn_turnserver*)(ss->server); if(*(server->mobility) && server->mobile_connections_map) { if(!(ss->mobile_id)) { ss->mobile_id = get_new_mobile_id(server); mobile_id_to_string(ss->mobile_id, ss->s_mobile_id, sizeof(ss->s_mobile_id)); } ur_map_put(server->mobile_connections_map, (ur_map_key_type)(ss->mobile_id), (ur_map_value_type)ss); } } } static void put_session_into_map(ts_ur_super_session *ss) { if(ss && ss->server) { turn_turnserver* server = (turn_turnserver*)(ss->server); if(!(ss->id)) { ss->id = (turnsession_id)((turnsession_id)server->id * 1000000000000000LL); ss->id += ++(server->session_id_counter); ss->start_time = server->ctime; } ur_map_put(server->sessions_map, (ur_map_key_type)(ss->id), (ur_map_value_type)ss); put_session_into_mobile_map(ss); } } static void delete_session_from_mobile_map(ts_ur_super_session *ss) { if(ss && ss->server && ss->mobile_id) { turn_turnserver* server = (turn_turnserver*)(ss->server); if(server->mobile_connections_map) { ur_map_del(server->mobile_connections_map, (ur_map_key_type)(ss->mobile_id), NULL); } ss->mobile_id = 0; ss->s_mobile_id[0] = 0; } } static void delete_session_from_map(ts_ur_super_session *ss) { if(ss && ss->server) { turn_turnserver* server = (turn_turnserver*)(ss->server); ur_map_del(server->sessions_map, (ur_map_key_type)(ss->id), NULL); delete_session_from_mobile_map(ss); } } static ts_ur_super_session* get_session_from_map(turn_turnserver* server, turnsession_id sid) { ts_ur_super_session *ss = NULL; if(server) { ur_map_value_type value = 0; if(ur_map_get(server->sessions_map, (ur_map_key_type)sid, &value) && value) { ss = (ts_ur_super_session*)value; } } return ss; } static ts_ur_super_session* get_session_from_mobile_map(turn_turnserver* server, mobile_id_t mid) { ts_ur_super_session *ss = NULL; if(server && *(server->mobility) && server->mobile_connections_map && mid) { ur_map_value_type value = 0; if(ur_map_get(server->mobile_connections_map, (ur_map_key_type)mid, &value) && value) { ss = (ts_ur_super_session*)value; } } return ss; } static ts_ur_super_session* create_new_ss(turn_turnserver* server) { // //printf("%s: 111.111: session size=%lu\n",__FUNCTION__,(unsigned long)sizeof(ts_ur_super_session)); // ts_ur_super_session *ss = (ts_ur_super_session*)turn_malloc(sizeof(ts_ur_super_session)); ns_bzero(ss,sizeof(ts_ur_super_session)); ss->server = server; put_session_into_map(ss); init_allocation(ss,&(ss->alloc), server->tcp_relay_connections); return ss; } static void delete_ur_map_ss(void *p) { if (p) { ts_ur_super_session* ss = (ts_ur_super_session*) p; delete_session_from_map(ss); clear_ts_ur_session_data(&(ss->client_session)); clear_allocation(get_allocation_ss(ss)); IOA_EVENT_DEL(ss->to_be_allocated_timeout_ev); turn_free(p,sizeof(ts_ur_super_session)); } } /////////// clean all ///////////////////// static int turn_server_remove_all_from_ur_map_ss(ts_ur_super_session* ss) { if (!ss) return 0; else { int ret = 0; if(ss->alloc.is_valid) { (((turn_turnserver*)ss->server)->raqcb)(ss->username); } if (ss->client_session.s) { clear_ioa_socket_session_if(ss->client_session.s, ss); } if (get_relay_socket_ss(ss)) { clear_ioa_socket_session_if(get_relay_socket_ss(ss), ss); } delete_ur_map_ss(ss); return ret; } } ///////////////////////////////////////////////////////////////// static void client_ss_channel_timeout_handler(ioa_engine_handle e, void* arg) { UNUSED_ARG(e); if (!arg) return; ch_info* chn = (ch_info*) arg; turn_channel_delete(chn); } static void client_ss_perm_timeout_handler(ioa_engine_handle e, void* arg) { UNUSED_ARG(e); if (!arg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: empty permission to be cleaned\n",__FUNCTION__); return; } turn_permission_info* tinfo = (turn_permission_info*) arg; if(!(tinfo->allocated)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: unallocated permission to be cleaned\n",__FUNCTION__); return; } if(!(tinfo->lifetime_ev)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: strange (1) permission to be cleaned\n",__FUNCTION__); } if(!(tinfo->owner)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: strange (2) permission to be cleaned\n",__FUNCTION__); } turn_permission_clean(tinfo); } /////////////////////////////////////////////////////////////////// static int update_turn_permission_lifetime(ts_ur_super_session *ss, turn_permission_info *tinfo, turn_time_t time_delta) { if (ss && tinfo && tinfo->owner) { turn_turnserver *server = (turn_turnserver *) (ss->server); if (server) { if(!time_delta) time_delta = STUN_PERMISSION_LIFETIME; tinfo->expiration_time = server->ctime + time_delta; IOA_EVENT_DEL(tinfo->lifetime_ev); tinfo->lifetime_ev = set_ioa_timer(server->e, time_delta, 0, client_ss_perm_timeout_handler, tinfo, 0, "client_ss_channel_timeout_handler"); return 0; } } return -1; } static int update_channel_lifetime(ts_ur_super_session *ss, ch_info* chn) { if (chn) { turn_permission_info* tinfo = (turn_permission_info*) (chn->owner); if (tinfo && tinfo->owner) { turn_turnserver *server = (turn_turnserver *) (ss->server); if (server) { if (update_turn_permission_lifetime(ss, tinfo, STUN_CHANNEL_LIFETIME) < 0) return -1; chn->expiration_time = server->ctime + STUN_CHANNEL_LIFETIME; IOA_EVENT_DEL(chn->lifetime_ev); chn->lifetime_ev = set_ioa_timer(server->e, STUN_CHANNEL_LIFETIME, 0, client_ss_channel_timeout_handler, chn, 0, "client_ss_channel_timeout_handler"); return 0; } } } return -1; } /////////////// TURN /////////////////////////// #define SKIP_ATTRIBUTES case STUN_ATTRIBUTE_PRIORITY: case STUN_ATTRIBUTE_FINGERPRINT: case STUN_ATTRIBUTE_MESSAGE_INTEGRITY: break; \ case STUN_ATTRIBUTE_USERNAME: case STUN_ATTRIBUTE_REALM: case STUN_ATTRIBUTE_NONCE: \ sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh),\ ioa_network_buffer_get_size(in_buffer->nbh), sar); \ continue static u08bits get_transport_value(const u08bits *value) { if((value[0] == STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE)|| (value[0] == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE)) { return value[0]; } return 0; } static int handle_turn_allocate(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh) { allocation* a = get_allocation_ss(ss); ts_ur_session* elem = &(ss->client_session); if (is_allocation_valid(a)) { if (!stun_tid_equals(tid, &(a->tid))) { *err_code = 437; *reason = (const u08bits *)"Wrong TID"; } else { size_t len = ioa_network_buffer_get_size(nbh); ioa_addr xor_relayed_addr; ioa_addr *relayed_addr = get_local_addr_from_ioa_socket(get_relay_socket_ss(ss)); if(relayed_addr) { if(server->external_ip_set) { addr_cpy(&xor_relayed_addr, &(server->external_ip)); addr_set_port(&xor_relayed_addr,addr_get_port(relayed_addr)); } else { addr_cpy(&xor_relayed_addr, relayed_addr); } stun_set_allocate_response_str(ioa_network_buffer_data(nbh), &len, tid, &xor_relayed_addr, get_remote_addr_from_ioa_socket(elem->s), (a->expiration_time - server->ctime), 0, NULL, 0, ss->s_mobile_id); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } } } else { a = NULL; u08bits transport = 0; u32bits lifetime = 0; int even_port = -1; int dont_fragment = 0; u64bits in_reservation_token = 0; int af = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT; u08bits username[STUN_MAX_USERNAME_SIZE+1]="\0"; size_t ulen = 0; stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); if(attr_type == STUN_ATTRIBUTE_USERNAME) { const u08bits* value = stun_attr_get_value(sar); if (value) { ulen = stun_attr_get_len(sar); if(ulen>=sizeof(username)) { *err_code = 400; *reason = (const u08bits *)"User name is too long"; break; } ns_bcopy(value,username,ulen); username[ulen]=0; } } switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_MOBILITY_TICKET: if(!(*(server->mobility))) { *err_code = 501; *reason = (const u08bits *)"Mobility Forbidden"; } else if (stun_attr_get_len(sar) != 0) { *err_code = 400; *reason = (const u08bits *)"Wrong Mobility Field"; } else { ss->is_mobile = 1; } break; case STUN_ATTRIBUTE_REQUESTED_TRANSPORT: { if (stun_attr_get_len(sar) != 4) { *err_code = 400; *reason = (const u08bits *)"Wrong Transport Field"; } else if(transport) { *err_code = 400; *reason = (const u08bits *)"Duplicate Transport Fields"; } else { const u08bits* value = stun_attr_get_value(sar); if (value) { transport = get_transport_value(value); if (!transport || value[1] || value[2] || value[3]) { *err_code = 442; *reason = (const u08bits *)"Unsupported Transport Protocol"; } if((transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE) && *(server->no_tcp_relay)) { *err_code = 403; *reason = (const u08bits *)"TCP Transport is not allowed by the TURN Server configuration"; } else if((transport == STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE) && *(server->no_udp_relay)) { *err_code = 403; *reason = (const u08bits *)"UDP Transport is not allowed by the TURN Server configuration"; } else if(ss->client_session.s) { SOCKET_TYPE cst = get_ioa_socket_type(ss->client_session.s); if((transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE) && (cst!=TCP_SOCKET) && (cst!=TLS_SOCKET)) { *err_code = 400; *reason = (const u08bits *)"Wrong Transport Data"; } else { ss->is_tcp_relay = (transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE); } } } else { *err_code = 400; *reason = (const u08bits *)"Wrong Transport Data"; } } } break; case STUN_ATTRIBUTE_DONT_FRAGMENT: dont_fragment = 1; if(!(server->dont_fragment)) unknown_attrs[(*ua_num)++] = nswap16(attr_type); break; case STUN_ATTRIBUTE_LIFETIME: { if (stun_attr_get_len(sar) != 4) { *err_code = 400; *reason = (const u08bits *)"Wrong Lifetime Field"; } else { const u08bits* value = stun_attr_get_value(sar); if (!value) { *err_code = 400; *reason = (const u08bits *)"Wrong Lifetime Data"; } else { lifetime = nswap32(*((const u32bits*)value)); } } } break; case STUN_ATTRIBUTE_EVEN_PORT: { if (in_reservation_token) { *err_code = 400; *reason = (const u08bits *)"Even Port and Reservation Token cannot be used together"; } else if (even_port >= 0) { *err_code = 400; *reason = (const u08bits *)"Even Port cannot be used in this request"; } else { even_port = stun_attr_get_even_port(sar); } } break; case STUN_ATTRIBUTE_RESERVATION_TOKEN: { int len = stun_attr_get_len(sar); if (len != 8) { *err_code = 400; *reason = (const u08bits *)"Wrong Format of Reservation Token"; } else if(af != STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT) { *err_code = 400; *reason = (const u08bits *)"Address family attribute can not be used with reservation token request"; } else { if (even_port >= 0) { *err_code = 400; *reason = (const u08bits *)"Reservation Token cannot be used in this request with even port"; } else if (in_reservation_token) { *err_code = 400; *reason = (const u08bits *)"Reservation Token cannot be used in this request"; } else { in_reservation_token = stun_attr_get_reservation_token_value(sar); } } } break; case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY: { if(in_reservation_token) { *err_code = 400; *reason = (const u08bits *)"Address family attribute can not be used with reservation token request"; } else { int af_req = stun_get_requested_address_family(sar); if(af == STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT) { switch (af_req) { case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4: case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6: af = af_req; break; default: *err_code = 440; *reason = (const u08bits *)"Unsupported address family requested"; } } else { *err_code = 400; *reason = (const u08bits *)"Only one address family attribute can be used in a request"; } } } break; default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if (!transport) { *err_code = 400; if(!(*reason)) *reason = (const u08bits *)"Transport field missed or wrong"; } else if (*ua_num > 0) { *err_code = 420; if(!(*reason)) *reason = (const u08bits *)"Unknown attribute"; } else if (*err_code) { ; } else if((transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE) && (dont_fragment || in_reservation_token || (even_port!=-1))) { *err_code = 400; if(!(*reason)) *reason = (const u08bits *)"Request parameters are incompatible with TCP transport"; } else { if(*(server->mobility)) { if(!(ss->is_mobile)) { delete_session_from_mobile_map(ss); } } lifetime = stun_adjust_allocate_lifetime(lifetime); u64bits out_reservation_token = 0; if((server->chquotacb)(username)<0) { *err_code = 486; *reason = (const u08bits *)"Allocation Quota Reached"; } else if (create_relay_connection(server, ss, lifetime, af, transport, even_port, in_reservation_token, &out_reservation_token, err_code, reason, tcp_peer_accept_connection) < 0) { (server->raqcb)(username); if (!*err_code) { *err_code = 437; if(!(*reason)) *reason = (const u08bits *)"Cannot create relay endpoint"; } } else { a = get_allocation_ss(ss); set_allocation_valid(a,1); STRCPY(ss->username,username); stun_tid_cpy(&(a->tid), tid); size_t len = ioa_network_buffer_get_size(nbh); ioa_addr xor_relayed_addr; ioa_addr *relayed_addr = get_local_addr_from_ioa_socket(get_relay_socket_ss(ss)); if(relayed_addr) { if(server->external_ip_set) { addr_cpy(&xor_relayed_addr, &(server->external_ip)); addr_set_port(&xor_relayed_addr,addr_get_port(relayed_addr)); } else { addr_cpy(&xor_relayed_addr, relayed_addr); } stun_set_allocate_response_str(ioa_network_buffer_data(nbh), &len, tid, &xor_relayed_addr, get_remote_addr_from_ioa_socket(elem->s), lifetime, 0,NULL, out_reservation_token, ss->s_mobile_id); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; turn_report_allocation_set(&(ss->alloc), lifetime, 0); } } } } if (!(*resp_constructed)) { if (!(*err_code)) { *err_code = 437; } size_t len = ioa_network_buffer_get_size(nbh); stun_set_allocate_response_str(ioa_network_buffer_data(nbh), &len, tid, NULL, NULL, 0, *err_code, *reason, 0, ss->s_mobile_id); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } return 0; } static int handle_turn_refresh(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, int message_integrity, int *no_response, int can_resume) { allocation* a = get_allocation_ss(ss); if (!is_allocation_valid(a) && !(*(server->mobility))) { *err_code = 437; *reason = (const u08bits *)"Invalid allocation"; } else { u32bits lifetime = 0; int to_delete = 0; mobile_id_t mid = 0; char smid[sizeof(ss->s_mobile_id)] = "\0"; stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_MOBILITY_TICKET: { if(!(*(server->mobility))) { *err_code = 501; *reason = (const u08bits *)"Mobility forbidden"; } if(is_allocation_valid(a)) { *err_code = 400; *reason = (const u08bits *)"Mobility ticket cannot be used for a stable, already established allocation"; } else { int smid_len = stun_attr_get_len(sar); if(smid_len>0 && (((size_t)smid_len)relay_session.s); if(addr) { int is_err = 0; switch (af_req) { case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4: if(addr->ss.sa_family != AF_INET) { is_err = 1; } break; case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6: if(addr->ss.sa_family != AF_INET6) { is_err = 1; } break; default: is_err = 1; } if(is_err) { *err_code = 443; *reason = (const u08bits *)"Peer Address Family Mismatch"; } } } break; default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if (*ua_num > 0) { *err_code = 420; *reason = (const u08bits *)"Unknown attribute"; } else if (*err_code) { ; } else if(!is_allocation_valid(a)) { if(mid && smid[0]) { turnserver_id tsid = ((0xFF00000000000000LL) & mid)>>56; if(tsid != server->id) { if(server->send_socket_to_relay) { ioa_socket_handle new_s = detach_ioa_socket(ss->client_session.s, 1); if(new_s) { if(server->send_socket_to_relay(tsid, mid, tid, new_s, message_integrity, RMT_MOBILE_SOCKET, in_buffer)<0) { *err_code = 400; *reason = (const u08bits *)"Wrong mobile ticket"; } else { *no_response = 1; } } else { *err_code = 500; *reason = (const u08bits *)"Cannot create new socket"; return -1; } } else { *err_code = 500; *reason = (const u08bits *)"Server send socket procedure is not set"; } ss->to_be_closed = 1; } else { ts_ur_super_session *orig_ss = get_session_from_mobile_map(server, mid); if(!orig_ss) { *err_code = 404; *reason = (const u08bits *)"Allocation not found"; } else if(orig_ss == ss) { *err_code = 437; *reason = (const u08bits *)"Invalid allocation"; } else if(!(orig_ss->is_mobile)) { *err_code = 500; *reason = (const u08bits *)"Software error: invalid mobile allocation"; } else if(orig_ss->client_session.s == ss->client_session.s) { *err_code = 500; *reason = (const u08bits *)"Software error: invalid mobile client socket (orig)"; } else if(!(ss->client_session.s)) { *err_code = 500; *reason = (const u08bits *)"Software error: invalid mobile client socket (new)"; } else { //Check security: int postpone_reply = 0; check_stun_auth(server, orig_ss, tid, resp_constructed, err_code, reason, in_buffer, nbh, STUN_METHOD_REFRESH, &message_integrity, &postpone_reply, can_resume); if(postpone_reply) { *no_response = 1; } else if(!(*err_code)) { //Session transfer: if (to_delete) lifetime = 0; else lifetime = stun_adjust_allocate_lifetime(lifetime); if (refresh_relay_connection(server, orig_ss, lifetime, 0, 0, 0, err_code) < 0) { if (!(*err_code)) { *err_code = 437; *reason = (const u08bits *)"Cannot refresh relay connection (internal error)"; } } else { *no_response = 1; //Transfer socket: ioa_socket_handle s = detach_ioa_socket(ss->client_session.s, 0); ss->to_be_closed = 1; if(!s) { *err_code = 500; } else { if(attach_socket_to_session(server, s, orig_ss) < 0) { IOA_CLOSE_SOCKET(s); *err_code = 500; } else { delete_session_from_mobile_map(ss); delete_session_from_mobile_map(orig_ss); put_session_into_mobile_map(orig_ss); //Use new buffer and redefine ss: nbh = ioa_network_buffer_allocate(server->e); ss = orig_ss; size_t len = ioa_network_buffer_get_size(nbh); turn_report_allocation_set(&(ss->alloc), lifetime, 1); stun_init_success_response_str(STUN_METHOD_REFRESH, ioa_network_buffer_data(nbh), &len, tid); u32bits lt = nswap32(lifetime); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_LIFETIME, (const u08bits*) <, 4); ioa_network_buffer_set_size(nbh,len); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_MOBILITY_TICKET, (u08bits*)ss->s_mobile_id,strlen(ss->s_mobile_id)); ioa_network_buffer_set_size(nbh,len); { static const u08bits *field = (const u08bits *) TURN_SOFTWARE; static const size_t fsz = sizeof(TURN_SOFTWARE)-1; size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_SOFTWARE, field, fsz); ioa_network_buffer_set_size(nbh, len); } if(message_integrity) { stun_attr_add_integrity_str(server->ct,ioa_network_buffer_data(nbh),&len,ss->hmackey,ss->pwd,server->shatype); ioa_network_buffer_set_size(nbh,len); } if ((server->fingerprint) || ss->enforce_fingerprints) { if (stun_attr_add_fingerprint_str(ioa_network_buffer_data(nbh), &len) < 0) { ioa_network_buffer_delete(server->e, nbh); return -1; } ioa_network_buffer_set_size(nbh, len); } return write_client_connection(server, ss, nbh, TTL_IGNORE, TOS_IGNORE); } } } } report_turn_session_info(server,orig_ss,0); } } } else { *err_code = 437; *reason = (const u08bits *)"Invalid allocation"; } } else { if (to_delete) lifetime = 0; else lifetime = stun_adjust_allocate_lifetime(lifetime); if (refresh_relay_connection(server, ss, lifetime, 0, 0, 0, err_code) < 0) { if (!(*err_code)) { *err_code = 437; *reason = (const u08bits *)"Cannot refresh relay connection (internal error)"; } } else { turn_report_allocation_set(&(ss->alloc), lifetime, 1); size_t len = ioa_network_buffer_get_size(nbh); stun_init_success_response_str(STUN_METHOD_REFRESH, ioa_network_buffer_data(nbh), &len, tid); u32bits lt = nswap32(lifetime); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_LIFETIME, (const u08bits*) <, 4); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } } } if(!no_response) { if (!(*resp_constructed)) { if (!(*err_code)) { *err_code = 437; } size_t len = ioa_network_buffer_get_size(nbh); stun_init_error_response_str(STUN_METHOD_REFRESH, ioa_network_buffer_data(nbh), &len, *err_code, *reason, tid); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } } return 0; } /* RFC 6062 ==>> */ static void tcp_deliver_delayed_buffer(unsent_buffer *ub, ioa_socket_handle s, ts_ur_super_session *ss) { if(ub && s && ub->bufs && ub->sz && ss) { size_t i = 0; do { ioa_network_buffer_handle nbh = top_unsent_buffer(ub); if(!nbh) break; u32bits bytes = (u32bits)ioa_network_buffer_get_size(nbh); int ret = send_data_from_ioa_socket_nbh(s, NULL, nbh, TTL_IGNORE, TOS_IGNORE); if (ret < 0) { set_ioa_socket_tobeclosed(s); } else { ++(ss->sent_packets); ss->sent_bytes += bytes; turn_report_session_usage(ss); } pop_unsent_buffer(ub); } while(!ioa_socket_tobeclosed(s) && ((i++)owner; if(a) { ss=(ts_ur_super_session*)a->owner; } if((tc->state != TC_STATE_READY) || !(tc->client_s)) { add_unsent_buffer(&(tc->ub_to_client), in_buffer->nbh); in_buffer->nbh = NULL; return; } ioa_network_buffer_handle nbh = in_buffer->nbh; in_buffer->nbh = NULL; u32bits bytes = (u32bits)ioa_network_buffer_get_size(nbh); int ret = send_data_from_ioa_socket_nbh(tc->client_s, NULL, nbh, TTL_IGNORE, TOS_IGNORE); if (ret < 0) { set_ioa_socket_tobeclosed(s); } else if(ss) { ++(ss->sent_packets); ss->sent_bytes += bytes; turn_report_session_usage(ss); } } static void tcp_client_input_handler_rfc6062data(ioa_socket_handle s, int event_type, ioa_net_data *in_buffer, void *arg) { if (!(event_type & IOA_EV_READ) || !arg) return; UNUSED_ARG(s); tcp_connection *tc = (tcp_connection*)arg; ts_ur_super_session *ss=NULL; allocation *a=(allocation*)tc->owner; if(a) { ss=(ts_ur_super_session*)a->owner; } if(tc->state != TC_STATE_READY) return; if(!(tc->peer_s)) return; ioa_network_buffer_handle nbh = in_buffer->nbh; in_buffer->nbh = NULL; if(ss) { u32bits bytes = (u32bits)ioa_network_buffer_get_size(nbh); ++(ss->received_packets); ss->received_bytes += bytes; } int ret = send_data_from_ioa_socket_nbh(tc->peer_s, NULL, nbh, TTL_IGNORE, TOS_IGNORE); if (ret < 0) { set_ioa_socket_tobeclosed(s); } turn_report_session_usage(ss); } static void tcp_conn_bind_timeout_handler(ioa_engine_handle e, void *arg) { UNUSED_ARG(e); if(arg) { tcp_connection *tc = (tcp_connection *)arg; delete_tcp_connection(tc); } } static void tcp_peer_connection_completed_callback(int success, void *arg) { if(arg) { tcp_connection *tc = (tcp_connection *)arg; allocation *a = (allocation*)(tc->owner); ts_ur_super_session *ss = (ts_ur_super_session*)(a->owner); turn_turnserver *server=(turn_turnserver*)(ss->server); int err_code = 0; IOA_EVENT_DEL(tc->peer_conn_timeout); ioa_network_buffer_handle nbh = ioa_network_buffer_allocate(server->e); size_t len = ioa_network_buffer_get_size(nbh); if(success) { if(register_callback_on_ioa_socket(server->e, tc->peer_s, IOA_EV_READ, tcp_peer_input_handler, tc, 1)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: cannot set TCP peer data input callback\n", __FUNCTION__); success=0; err_code = 500; } } if(success) { tc->state = TC_STATE_PEER_CONNECTED; stun_init_success_response_str(STUN_METHOD_CONNECT, ioa_network_buffer_data(nbh), &len, &(tc->tid)); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_CONNECTION_ID, (const u08bits*)&(tc->id), 4); IOA_EVENT_DEL(tc->conn_bind_timeout); tc->conn_bind_timeout = set_ioa_timer(server->e, TCP_CONN_BIND_TIMEOUT, 0, tcp_conn_bind_timeout_handler, tc, 0, "tcp_conn_bind_timeout_handler"); } else { tc->state = TC_STATE_FAILED; if(!err_code) err_code = 447; const u08bits *reason = (const u08bits *)"Connection Timeout or Failure"; stun_init_error_response_str(STUN_METHOD_CONNECT, ioa_network_buffer_data(nbh), &len, err_code, reason, &(tc->tid)); } ioa_network_buffer_set_size(nbh,len); if(need_stun_authentication(server)) { stun_attr_add_integrity_str(server->ct,ioa_network_buffer_data(nbh),&len,ss->hmackey,ss->pwd,server->shatype); ioa_network_buffer_set_size(nbh,len); } write_client_connection(server, ss, nbh, TTL_IGNORE, TOS_IGNORE); if(!success) { delete_tcp_connection(tc); } /* test */ else if(0) { int i = 0; for(i=0;i<22;i++) { ioa_network_buffer_handle nbh_test = ioa_network_buffer_allocate(server->e); size_t len_test = ioa_network_buffer_get_size(nbh_test); u08bits *data = ioa_network_buffer_data(nbh_test); const char* data_test="111.111.111.111.111"; len_test = strlen(data_test); ns_bcopy(data_test,data,len_test); ioa_network_buffer_set_size(nbh_test,len_test); send_data_from_ioa_socket_nbh(tc->peer_s, NULL, nbh_test, TTL_IGNORE, TOS_IGNORE); } } } } static void tcp_peer_conn_timeout_handler(ioa_engine_handle e, void *arg) { UNUSED_ARG(e); tcp_peer_connection_completed_callback(0,arg); } static int tcp_start_connection_to_peer(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, allocation *a, ioa_addr *peer_addr, int *err_code, const u08bits **reason) { FUNCSTART; if(!ss || !(a->relay_session.s)) { *err_code = 500; FUNCEND; return -1; } tcp_connection *tc = get_tcp_connection_by_peer(a, peer_addr); if(tc) { *err_code = 446; *reason = (const u08bits *)"Connection Already Exists"; FUNCEND; return -1; } tc = create_tcp_connection(server->id, a, tid, peer_addr, err_code); if(!tc) { if(!(*err_code)) { *err_code = 500; } FUNCEND; return -1; } else if(*err_code) { delete_tcp_connection(tc); FUNCEND; return -1; } IOA_EVENT_DEL(tc->peer_conn_timeout); tc->peer_conn_timeout = set_ioa_timer(server->e, TCP_PEER_CONN_TIMEOUT, 0, tcp_peer_conn_timeout_handler, tc, 0, "tcp_peer_conn_timeout_handler"); ioa_socket_handle tcs = ioa_create_connecting_tcp_relay_socket(a->relay_session.s, peer_addr, tcp_peer_connection_completed_callback, tc); if(!tcs) { delete_tcp_connection(tc); *err_code = 500; FUNCEND; return -1; } tc->state = TC_STATE_CLIENT_TO_PEER_CONNECTING; IOA_CLOSE_SOCKET(tc->peer_s); tc->peer_s = tcs; set_ioa_socket_sub_session(tc->peer_s,tc); FUNCEND; return 0; } static void tcp_peer_accept_connection(ioa_socket_handle s, void *arg) { if(s) { if(!arg) { close_ioa_socket(s); return; } ts_ur_super_session *ss = (ts_ur_super_session*)arg; turn_turnserver *server=(turn_turnserver*)(ss->server); FUNCSTART; allocation *a = &(ss->alloc); ioa_addr *peer_addr = get_remote_addr_from_ioa_socket(s); tcp_connection *tc = get_tcp_connection_by_peer(a, peer_addr); if(tc) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: peer data socket with this address already exist\n", __FUNCTION__); if(tc->peer_s != s) close_ioa_socket(s); FUNCEND; return; } if(!good_peer_addr(server, peer_addr)) { u08bits saddr[256]; addr_to_string(peer_addr, saddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: an attempt to connect from a peer with forbidden address: %s\n", __FUNCTION__,saddr); close_ioa_socket(s); FUNCEND; return; } if(!can_accept_tcp_connection_from_peer(a,peer_addr,server->server_relay)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: peer has no permission to connect\n", __FUNCTION__); close_ioa_socket(s); FUNCEND; return; } stun_tid tid; ns_bzero(&tid,sizeof(stun_tid)); int err_code=0; tc = create_tcp_connection(server->id, a, &tid, peer_addr, &err_code); if(!tc) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: cannot create TCP connection\n", __FUNCTION__); close_ioa_socket(s); FUNCEND; return; } tc->state = TC_STATE_PEER_CONNECTED; tc->peer_s = s; set_ioa_socket_session(s,ss); set_ioa_socket_sub_session(s,tc); if(register_callback_on_ioa_socket(server->e, s, IOA_EV_READ, tcp_peer_input_handler, tc, 1)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: cannot set TCP peer data input callback\n", __FUNCTION__); close_ioa_socket(s); tc->peer_s = NULL; tc->state = TC_STATE_UNKNOWN; FUNCEND; return; } IOA_EVENT_DEL(tc->conn_bind_timeout); tc->conn_bind_timeout = set_ioa_timer(server->e, TCP_CONN_BIND_TIMEOUT, 0, tcp_conn_bind_timeout_handler, tc, 0, "tcp_conn_bind_timeout_handler"); ioa_network_buffer_handle nbh = ioa_network_buffer_allocate(server->e); size_t len = ioa_network_buffer_get_size(nbh); stun_init_indication_str(STUN_METHOD_CONNECTION_ATTEMPT, ioa_network_buffer_data(nbh), &len); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_CONNECTION_ID, (const u08bits*)&(tc->id), 4); stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, peer_addr); ioa_network_buffer_set_size(nbh,len); { static const u08bits *field = (const u08bits *) TURN_SOFTWARE; static const size_t fsz = sizeof(TURN_SOFTWARE)-1; size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_SOFTWARE, field, fsz); ioa_network_buffer_set_size(nbh, len); } /* We add integrity for short-term indication messages, only */ if(server->ct == TURN_CREDENTIALS_SHORT_TERM) { stun_attr_add_integrity_str(server->ct,ioa_network_buffer_data(nbh),&len,ss->hmackey,ss->pwd,server->shatype); ioa_network_buffer_set_size(nbh,len); } if ((server->fingerprint) || ss->enforce_fingerprints) { size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_fingerprint_str(ioa_network_buffer_data(nbh), &len); ioa_network_buffer_set_size(nbh, len); } write_client_connection(server, ss, nbh, TTL_IGNORE, TOS_IGNORE); FUNCEND; } } static int handle_turn_connect(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer) { FUNCSTART; ioa_addr peer_addr; int peer_found = 0; addr_set_any(&peer_addr); allocation* a = get_allocation_ss(ss); if(!(ss->is_tcp_relay)) { *err_code = 403; *reason = (const u08bits *)"Connect cannot be used with UDP relay"; } else if (!is_allocation_valid(a)) { *err_code = 437; *reason = (const u08bits *)"Allocation mismatch"; } else { stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: { if(stun_attr_get_addr_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar, &peer_addr, &(ss->default_peer_addr)) == -1) { *err_code = 400; *reason = (const u08bits *)"Bad Peer Address"; } else { ioa_addr *relay_addr = get_local_addr_from_ioa_socket(a->relay_session.s); if(relay_addr && (relay_addr->ss.sa_family != peer_addr.ss.sa_family)) { *err_code = 443; *reason = (const u08bits *)"Peer Address Family Mismatch"; } peer_found = 1; } break; } default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if (*ua_num > 0) { *err_code = 420; *reason = (const u08bits *)"Unknown attribute"; } else if (*err_code) { ; } else if (!peer_found) { *err_code = 400; *reason = (const u08bits *)"Where is Peer Address ?"; } else { if(!good_peer_addr(server,&peer_addr)) { *err_code = 403; *reason = (const u08bits *) "Forbidden IP"; } else { tcp_start_connection_to_peer(server, ss, tid, a, &peer_addr, err_code, reason); } } } FUNCEND; return 0; } static int handle_turn_connection_bind(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, int message_integrity) { allocation* a = get_allocation_ss(ss); u16bits method = STUN_METHOD_CONNECTION_BIND; if (is_allocation_valid(a)) { *err_code = 400; *reason = (const u08bits *)"Bad request: CONNECTION_BIND cannot be issued after allocation"; } else if((get_ioa_socket_type(ss->client_session.s)!=TCP_SOCKET) && (get_ioa_socket_type(ss->client_session.s)!=TLS_SOCKET)) { *err_code = 400; *reason = (const u08bits *)"Bad request: CONNECTION_BIND only possible with TCP/TLS"; } else { tcp_connection_id id = 0; stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_CONNECTION_ID: { if (stun_attr_get_len(sar) != 4) { *err_code = 400; *reason = (const u08bits *)"Wrong Connection ID field format"; } else { const u08bits* value = stun_attr_get_value(sar); if (!value) { *err_code = 400; *reason = (const u08bits *)"Wrong Connection ID field data"; } else { id = *((const u32bits*)value); //AS-IS encoding, no conversion to/from network byte order } } } break; default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if (*ua_num > 0) { *err_code = 420; *reason = (const u08bits *)"Unknown attribute"; } else if (*err_code) { ; } else { if(server->send_socket_to_relay) { turnserver_id sid = (id & 0xFF000000)>>24; ioa_socket_handle s = ss->client_session.s; if(s) { ioa_socket_handle new_s = detach_ioa_socket(s, 1); if(new_s) { if(server->send_socket_to_relay(sid, id, tid, new_s, message_integrity, RMT_CB_SOCKET, NULL)<0) { *err_code = 400; *reason = (const u08bits *)"Wrong connection id"; } } else { *err_code = 500; } } else { *err_code = 500; } } else { *err_code = 500; } ss->to_be_closed = 1; } } if (!(*resp_constructed) && ss->client_session.s && !ioa_socket_tobeclosed(ss->client_session.s)) { if (!(*err_code)) { *err_code = 437; } size_t len = ioa_network_buffer_get_size(nbh); stun_init_error_response_str(method, ioa_network_buffer_data(nbh), &len, *err_code, *reason, tid); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } return 0; } int turnserver_accept_tcp_client_data_connection(turn_turnserver *server, tcp_connection_id tcid, stun_tid *tid, ioa_socket_handle s, int message_integrity) { if(!server) return -1; FUNCSTART; tcp_connection *tc = NULL; ts_ur_super_session *ss = NULL; int err_code = 0; if(tcid && tid && s) { tc = get_and_clean_tcp_connection_by_id(server->tcp_relay_connections, tcid); if(!tc || (tc->state == TC_STATE_READY) || (tc->client_s)) { err_code = 400; } else { allocation *a = (allocation*)(tc->owner); if(!a || !(a->owner)) { err_code = 500; } else { ss = (ts_ur_super_session*)(a->owner); tc->state = TC_STATE_READY; tc->client_s = s; set_ioa_socket_session(s,ss); set_ioa_socket_sub_session(s,tc); set_ioa_socket_app_type(s,TCP_CLIENT_DATA_SOCKET); if(register_callback_on_ioa_socket(server->e, s, IOA_EV_READ, tcp_client_input_handler_rfc6062data, tc, 1)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: cannot set TCP client data input callback\n", __FUNCTION__); err_code = 500; } else { IOA_EVENT_DEL(tc->conn_bind_timeout); } } } ioa_network_buffer_handle nbh = ioa_network_buffer_allocate(server->e); if(!err_code) { size_t len = ioa_network_buffer_get_size(nbh); stun_init_success_response_str(STUN_METHOD_CONNECTION_BIND, ioa_network_buffer_data(nbh), &len, tid); ioa_network_buffer_set_size(nbh,len); } else { size_t len = ioa_network_buffer_get_size(nbh); stun_init_error_response_str(STUN_METHOD_CONNECTION_BIND, ioa_network_buffer_data(nbh), &len, err_code, NULL, tid); ioa_network_buffer_set_size(nbh,len); } { static const u08bits *field = (const u08bits *) TURN_SOFTWARE; static const size_t fsz = sizeof(TURN_SOFTWARE)-1; size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_SOFTWARE, field, fsz); ioa_network_buffer_set_size(nbh, len); } if(message_integrity && ss) { size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_integrity_str(server->ct,ioa_network_buffer_data(nbh),&len,ss->hmackey,ss->pwd,server->shatype); ioa_network_buffer_set_size(nbh,len); } if ((server->fingerprint) || (ss &&(ss->enforce_fingerprints))) { size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_fingerprint_str(ioa_network_buffer_data(nbh), &len); ioa_network_buffer_set_size(nbh, len); } if(ss && !err_code) { send_data_from_ioa_socket_nbh(s, NULL, nbh, TTL_IGNORE, TOS_IGNORE); tcp_deliver_delayed_buffer(&(tc->ub_to_client),s,ss); FUNCEND; return 0; } else { /* Just to set the necessary structures for the packet sending: */ if(register_callback_on_ioa_socket(server->e, s, IOA_EV_READ, NULL, NULL, 1)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: cannot set TCP tmp client data input callback\n", __FUNCTION__); ioa_network_buffer_delete(server->e, nbh); } else { send_data_from_ioa_socket_nbh(s, NULL, nbh, TTL_IGNORE, TOS_IGNORE); } } } if(s) { if(tc && (s==tc->client_s)) { ; } else { close_ioa_socket(s); } } FUNCEND; return -1; } /* <<== RFC 6062 */ static int handle_turn_channel_bind(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh) { FUNCSTART; u16bits chnum = 0; ioa_addr peer_addr; addr_set_any(&peer_addr); allocation* a = get_allocation_ss(ss); int addr_found = 0; if(ss->is_tcp_relay) { *err_code = 403; *reason = (const u08bits *)"Channel bind cannot be used with TCP relay"; } else if (is_allocation_valid(a)) { stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_CHANNEL_NUMBER: { if (chnum) { chnum = 0; *err_code = 400; *reason = (const u08bits *)"Channel number cannot be duplicated in this request"; break; } chnum = stun_attr_get_channel_number(sar); if (!chnum) { *err_code = 400; *reason = (const u08bits *)"Channel number cannot be zero in this request"; break; } } break; case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: { stun_attr_get_addr_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar, &peer_addr, &(ss->default_peer_addr)); ioa_addr *relay_addr = get_local_addr_from_ioa_socket(a->relay_session.s); if(relay_addr && relay_addr->ss.sa_family != peer_addr.ss.sa_family) { *err_code = 443; *reason = (const u08bits *)"Peer Address Family Mismatch"; } if(addr_get_port(&peer_addr) < 1) { *err_code = 400; *reason = (const u08bits *)"Empty port number in channel bind request"; } else { addr_found = 1; } break; } default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if (*ua_num > 0) { *err_code = 420; *reason = (const u08bits *)"Unknown attribute"; } else if (*err_code) { ; } else if (!chnum || addr_any(&peer_addr) || !addr_found) { *err_code = 400; *reason = (const u08bits *)"Bad channel bind request"; } else if(!STUN_VALID_CHANNEL(chnum)) { *err_code = 400; *reason = (const u08bits *)"Bad channel number"; } else { ch_info* chn = allocation_get_ch_info(a, chnum); turn_permission_info* tinfo = NULL; if (chn) { if (!addr_eq(&peer_addr, &(chn->peer_addr))) { *err_code = 400; *reason = (const u08bits *)"You cannot use the same channel number with different peer"; } else { tinfo = (turn_permission_info*) (chn->owner); if (!tinfo) { *err_code = 500; *reason = (const u08bits *)"Wrong permission info"; } else { if (!addr_eq_no_port(&peer_addr, &(tinfo->addr))) { *err_code = 500; *reason = (const u08bits *)"Wrong permission info and peer addr conbination"; } else if (chn->port != addr_get_port(&peer_addr)) { *err_code = 500; *reason = (const u08bits *)"Wrong port number"; } } } } else { chn = allocation_get_ch_info_by_peer_addr(a, &peer_addr); if(chn) { *err_code = 400; *reason = (const u08bits *)"You cannot use the same peer with different channel number"; } else { if(!good_peer_addr(server,&peer_addr)) { *err_code = 403; *reason = (const u08bits *) "Forbidden IP"; } else { chn = allocation_get_new_ch_info(a, chnum, &peer_addr); if (!chn) { *err_code = 500; *reason = (const u08bits *) "Cannot find channel data"; } else { tinfo = (turn_permission_info*) (chn->owner); if (!tinfo) { *err_code = 500; *reason = (const u08bits *) "Wrong turn permission info"; } } } } } if (!(*err_code) && chn && tinfo) { if (update_channel_lifetime(ss,chn) < 0) { *err_code = 500; *reason = (const u08bits *)"Cannot update channel lifetime (internal error)"; } else { size_t len = ioa_network_buffer_get_size(nbh); stun_set_channel_bind_response_str(ioa_network_buffer_data(nbh), &len, tid, 0, NULL); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } } } } FUNCEND; return 0; } static int handle_turn_binding(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, int *origin_changed, ioa_addr *response_origin, int *dest_changed, ioa_addr *response_destination, u32bits cookie, int old_stun) { FUNCSTART; ts_ur_session* elem = &(ss->client_session); int change_ip = 0; int change_port = 0; int padding = 0; int response_port_present = 0; u16bits response_port = 0; SOCKET_TYPE st = get_ioa_socket_type(ss->client_session.s); int use_reflected_from = 0; if(!(ss->client_session.s)) return -1; *origin_changed = 0; *dest_changed = 0; stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { case OLD_STUN_ATTRIBUTE_PASSWORD: SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_CHANGE_REQUEST: /* * This fix allows the client program from the Stuntman source to make STUN binding requests * to this server. * * It was provided by John Selbie, from STUNTMAN project: * * "Here's the gist of the change. Stuntman comes with a STUN client library * and client program. The client program displays the mapped IP address and * port if it gets back a successful binding response. * It also interops with JSTUN, a Java implementation of STUN. * However, the JSTUN server refuses to respond to any binding request that * doesn't have a CHANGE-REQUEST attribute in it. * ... workaround is for the client to make a request with an empty CHANGE-REQUEST * attribute (neither the ip or port bit are set)." * */ stun_attr_get_change_request_str(sar, &change_ip, &change_port); if( (!is_rfc5780(server)) && (change_ip || change_port)) { *err_code = 420; *reason = (const u08bits *)"Unknown attribute: TURN server was configured without RFC 5780 support"; break; } if(change_ip || change_port) { if(st != UDP_SOCKET) { *err_code = 400; *reason = (const u08bits *)"Wrong request: applicable only to UDP protocol"; } } break; case STUN_ATTRIBUTE_PADDING: if(response_port_present) { *err_code = 400; *reason = (const u08bits *)"Wrong request format: you cannot use PADDING and RESPONSE_PORT together"; } else if((st != UDP_SOCKET) && (st != DTLS_SOCKET)) { *err_code = 400; *reason = (const u08bits *)"Wrong request: padding applicable only to UDP and DTLS protocols"; } else { padding = 1; } break; case STUN_ATTRIBUTE_RESPONSE_PORT: if(padding) { *err_code = 400; *reason = (const u08bits *)"Wrong request format: you cannot use PADDING and RESPONSE_PORT together"; } else if(st != UDP_SOCKET) { *err_code = 400; *reason = (const u08bits *)"Wrong request: applicable only to UDP protocol"; } else { int rp = stun_attr_get_response_port_str(sar); if(rp>=0) { response_port_present = 1; response_port = (u16bits)rp; } else { *err_code = 400; *reason = (const u08bits *)"Wrong response port format"; } } break; case OLD_STUN_ATTRIBUTE_RESPONSE_ADDRESS: if(old_stun) { use_reflected_from = 1; stun_attr_get_addr_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar, response_destination, response_destination); break; } default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if (*ua_num > 0) { *err_code = 420; *reason = (const u08bits *)"Unknown attribute"; } else if (*err_code) { ; } else if(ss->client_session.s) { size_t len = ioa_network_buffer_get_size(nbh); if (stun_set_binding_response_str(ioa_network_buffer_data(nbh), &len, tid, get_remote_addr_from_ioa_socket(elem->s), 0, NULL, cookie, old_stun) >= 0) { addr_cpy(response_origin, get_local_addr_from_ioa_socket(ss->client_session.s)); *resp_constructed = 1; if(old_stun && use_reflected_from) { stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, OLD_STUN_ATTRIBUTE_REFLECTED_FROM, get_remote_addr_from_ioa_socket(ss->client_session.s)); } if(!is_rfc5780(server)) { if(old_stun) { stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, OLD_STUN_ATTRIBUTE_SOURCE_ADDRESS, response_origin); stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, OLD_STUN_ATTRIBUTE_CHANGED_ADDRESS, response_origin); } else { stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_RESPONSE_ORIGIN, response_origin); } } else if(ss->client_session.s) { ioa_addr other_address; if(get_other_address(server,ss,&other_address) == 0) { addr_cpy(response_destination, get_remote_addr_from_ioa_socket(ss->client_session.s)); if(change_ip) { *origin_changed = 1; if(change_port) { addr_cpy(response_origin,&other_address); } else { int old_port = addr_get_port(response_origin); addr_cpy(response_origin,&other_address); addr_set_port(response_origin,old_port); } } else if(change_port) { *origin_changed = 1; addr_set_port(response_origin,addr_get_port(&other_address)); } if(old_stun) { stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, OLD_STUN_ATTRIBUTE_SOURCE_ADDRESS, response_origin); stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, OLD_STUN_ATTRIBUTE_CHANGED_ADDRESS, &other_address); } else { stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_RESPONSE_ORIGIN, response_origin); stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_OTHER_ADDRESS, &other_address); } if(response_port_present) { *dest_changed = 1; addr_set_port(response_destination, (int)response_port); } if(padding) { int mtu = get_local_mtu_ioa_socket(ss->client_session.s); if(mtu<68) mtu=1500; mtu = (mtu >> 2) << 2; stun_attr_add_padding_str(ioa_network_buffer_data(nbh), &len, (u16bits)mtu); } } } } ioa_network_buffer_set_size(nbh, len); } FUNCEND; return 0; } static int handle_turn_send(turn_turnserver *server, ts_ur_super_session *ss, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer) { FUNCSTART; ioa_addr peer_addr; const u08bits* value = NULL; int len = -1; int addr_found = 0; int set_df = 0; addr_set_any(&peer_addr); allocation* a = get_allocation_ss(ss); if(ss->is_tcp_relay) { *err_code = 403; *reason = (const u08bits *)"Send cannot be used with TCP relay"; } else if (is_allocation_valid(a) && (in_buffer->recv_ttl != 0)) { stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_DONT_FRAGMENT: if(!(server->dont_fragment)) unknown_attrs[(*ua_num)++] = nswap16(attr_type); else set_df = 1; break; case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: { if (addr_found) { *err_code = 400; *reason = (const u08bits *)"Address duplication"; } else { stun_attr_get_addr_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar, &peer_addr, &(ss->default_peer_addr)); } } break; case STUN_ATTRIBUTE_DATA: { if (len >= 0) { *err_code = 400; *reason = (const u08bits *)"Data duplication"; } else { len = stun_attr_get_len(sar); value = stun_attr_get_value(sar); } } break; default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if (*err_code) { ; } else if (*ua_num > 0) { *err_code = 420; } else if (!addr_any(&peer_addr) && len >= 0) { turn_permission_info* tinfo = NULL; if(!(server->server_relay)) tinfo = allocation_get_permission(a, &peer_addr); if (tinfo || (server->server_relay)) { set_df_on_ioa_socket(get_relay_socket_ss(ss), set_df); ioa_network_buffer_handle nbh = in_buffer->nbh; if(value && len>0) { u16bits offset = (u16bits)(value - ioa_network_buffer_data(nbh)); ioa_network_buffer_add_offset_size(nbh,offset,0,len); } else { len = 0; ioa_network_buffer_set_size(nbh,len); } ioa_network_buffer_header_init(nbh); send_data_from_ioa_socket_nbh(get_relay_socket_ss(ss), &peer_addr, nbh, in_buffer->recv_ttl-1, in_buffer->recv_tos); in_buffer->nbh = NULL; } } else { *err_code = 400; *reason = (const u08bits *)"No address found"; } } FUNCEND; return 0; } static int update_permission(ts_ur_super_session *ss, ioa_addr *peer_addr) { if (!ss || !peer_addr) return -1; allocation* a = get_allocation_ss(ss); turn_permission_info* tinfo = allocation_get_permission(a, peer_addr); if (!tinfo) { tinfo = allocation_add_permission(a, peer_addr); } if (!tinfo) return -1; if (update_turn_permission_lifetime(ss, tinfo, 0) < 0) return -1; ch_info *chn = get_turn_channel(tinfo, peer_addr); if(chn) { if (update_channel_lifetime(ss, chn) < 0) return -1; } return 0; } static int handle_turn_create_permission(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh) { int ret = -1; int addr_found = 0; UNUSED_ARG(server); allocation* a = get_allocation_ss(ss); if (is_allocation_valid(a)) { { stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: { ioa_addr peer_addr; addr_set_any(&peer_addr); stun_attr_get_addr_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar, &peer_addr, &(ss->default_peer_addr)); ioa_addr *relay_addr = get_local_addr_from_ioa_socket(a->relay_session.s); if(relay_addr && (relay_addr->ss.sa_family != peer_addr.ss.sa_family)) { *err_code = 443; *reason = (const u08bits *)"Peer Address Family Mismatch"; } else if(!good_peer_addr(server, &peer_addr)) { *err_code = 403; *reason = (const u08bits *) "Forbidden IP"; } else { addr_found++; } } break; default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } } if (*ua_num > 0) { *err_code = 420; } else if (*err_code) { ; } else if (!addr_found) { *err_code = 400; *reason = (const u08bits *)"No address found"; } else { stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: { ioa_addr peer_addr; addr_set_any(&peer_addr); stun_attr_get_addr_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar, &peer_addr, &(ss->default_peer_addr)); addr_set_port(&peer_addr, 0); if (update_permission(ss, &peer_addr) < 0) { *err_code = 500; *reason = (const u08bits *)"Cannot update some permissions (critical server software error)"; } } break; default: ; } sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if(*err_code == 0) { size_t len = ioa_network_buffer_get_size(nbh); stun_init_success_response_str(STUN_METHOD_CREATE_PERMISSION, ioa_network_buffer_data(nbh), &len, tid); ioa_network_buffer_set_size(nbh,len); ret = 0; *resp_constructed = 1; } } } return ret; } // AUTH ==>> static int need_stun_authentication(turn_turnserver *server) { switch(server->ct) { case TURN_CREDENTIALS_LONG_TERM: return 1; case TURN_CREDENTIALS_SHORT_TERM: return 1; default: return 0; }; } static int create_challenge_response(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, ioa_network_buffer_handle nbh, u16bits method) { size_t len = ioa_network_buffer_get_size(nbh); stun_init_error_response_str(method, ioa_network_buffer_data(nbh), &len, *err_code, *reason, tid); *resp_constructed = 1; stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_NONCE, ss->nonce, (int)(NONCE_MAX_SIZE-1)); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_REALM, server->realm, (int)(strlen((s08bits*)(server->realm)))); ioa_network_buffer_set_size(nbh,len); return 0; } #if !defined(min) #define min(a,b) ((a)<=(b) ? (a) : (b)) #endif static void resume_processing_after_username_check(int success, hmackey_t hmackey, st_password_t pwd, turn_turnserver *server, u64bits ctxkey, ioa_net_data *in_buffer) { if(server && in_buffer && in_buffer->nbh) { ts_ur_super_session *ss = get_session_from_map(server,(turnsession_id)ctxkey); if(ss && ss->client_session.s) { turn_turnserver *server = (turn_turnserver *)ss->server; ts_ur_session *elem = &(ss->client_session); if(success) { ns_bcopy(hmackey,ss->hmackey,sizeof(hmackey_t)); ss->hmackey_set = 1; ns_bcopy(pwd,ss->pwd,sizeof(st_password_t)); } read_client_connection(server,elem,ss,in_buffer,0,0); close_ioa_socket_after_processing_if_necessary(ss->client_session.s); ioa_network_buffer_delete(server->e, in_buffer->nbh); in_buffer->nbh=NULL; } } } static int check_stun_auth(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, u16bits method, int *message_integrity, int *postpone_reply, int can_resume) { u08bits usname[STUN_MAX_USERNAME_SIZE+1]; u08bits nonce[STUN_MAX_NONCE_SIZE+1]; size_t alen = 0; if(!need_stun_authentication(server)) return 0; int new_nonce = 0; { int generate_new_nonce = 0; if(ss->nonce[0]==0) { generate_new_nonce = 1; new_nonce = 1; } if(*(server->stale_nonce)) { if(turn_time_before(ss->nonce_expiration_time,server->ctime)) { generate_new_nonce = 1; } } if(generate_new_nonce) { int i = 0; if(TURN_RANDOM_SIZE == 8) { for(i=0;i<(NONCE_LENGTH_32BITS>>1);i++) { u08bits *s = ss->nonce + 8*i; u64bits rand=(u64bits)turn_random(); snprintf((s08bits*)s, NONCE_MAX_SIZE-8*i, "%08lx",(unsigned long)rand); } } else { for(i=0;inonce + 4*i; u32bits rand=(u32bits)turn_random(); snprintf((s08bits*)s, NONCE_MAX_SIZE-4*i, "%04x",(unsigned int)rand); } } ss->nonce_expiration_time = server->ctime + STUN_NONCE_EXPIRATION_TIME; } } /* MESSAGE_INTEGRITY ATTR: */ stun_attr_ref sar = stun_attr_get_first_by_type_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), STUN_ATTRIBUTE_MESSAGE_INTEGRITY); if(!sar) { *err_code = 401; *reason = (const u08bits*)"Unauthorised"; if(server->ct != TURN_CREDENTIALS_SHORT_TERM) { return create_challenge_response(server,ss,tid,resp_constructed,err_code,reason,nbh,method); } else { return -1; } } { int sarlen = stun_attr_get_len(sar); switch(sarlen) { case SHA1SIZEBYTES: if(server->shatype != SHATYPE_SHA1) { *err_code = SHA_TOO_WEAK; return create_challenge_response(server,ss,tid,resp_constructed,err_code,reason,nbh,method); return -1; } break; case SHA256SIZEBYTES: if(server->shatype != SHATYPE_SHA256) { *err_code = 401; return create_challenge_response(server,ss,tid,resp_constructed,err_code,reason,nbh,method); return -1; } break; default: *err_code = 401; return create_challenge_response(server,ss,tid,resp_constructed,err_code,reason,nbh,method); return -1; }; } if(server->ct != TURN_CREDENTIALS_SHORT_TERM) { /* REALM ATTR: */ sar = stun_attr_get_first_by_type_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), STUN_ATTRIBUTE_REALM); if(!sar) { *err_code = 400; *reason = (const u08bits*)"Bad request"; return -1; } u08bits realm[STUN_MAX_REALM_SIZE+1]; alen = min((size_t)stun_attr_get_len(sar),sizeof(realm)-1); ns_bcopy(stun_attr_get_value(sar),realm,alen); realm[alen]=0; if(strcmp((char*)realm, (char*)(server->realm))) { *err_code = 400; *reason = (const u08bits*)"Bad request: the realm value incorrect"; return -1; } } /* USERNAME ATTR: */ sar = stun_attr_get_first_by_type_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), STUN_ATTRIBUTE_USERNAME); if(!sar) { *err_code = 400; *reason = (const u08bits*)"Bad request"; return -1; } alen = min((size_t)stun_attr_get_len(sar),sizeof(usname)-1); ns_bcopy(stun_attr_get_value(sar),usname,alen); usname[alen]=0; if(server->ct != TURN_CREDENTIALS_SHORT_TERM) { /* NONCE ATTR: */ sar = stun_attr_get_first_by_type_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), STUN_ATTRIBUTE_NONCE); if(!sar) { *err_code = 400; *reason = (const u08bits*)"Bad request"; return -1; } alen = min((size_t)stun_attr_get_len(sar),sizeof(nonce)-1); ns_bcopy(stun_attr_get_value(sar),nonce,alen); nonce[alen]=0; /* Stale Nonce check: */ if(new_nonce) { *err_code = 438; *reason = (const u08bits*)"Wrong nonce"; return create_challenge_response(server,ss,tid,resp_constructed,err_code,reason,nbh,method); } if(strcmp((s08bits*)ss->nonce,(s08bits*)nonce)) { *err_code = 438; *reason = (const u08bits*)"Stale nonce"; return create_challenge_response(server,ss,tid,resp_constructed,err_code,reason,nbh,method); } } /* Password */ if(!(ss->hmackey_set) && (ss->pwd[0] == 0)) { ur_string_map_value_type ukey = NULL; if(can_resume) { ukey = (server->userkeycb)(server->id, usname, resume_processing_after_username_check, in_buffer, ss->id, postpone_reply); if(*postpone_reply) { return 0; } } /* we always return NULL for short-term credentials here */ if(!ukey) { /* direct user pattern is supported only for long-term credentials */ TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Cannot find credentials of user <%s>\n", __FUNCTION__, (char*)usname); *err_code = 401; *reason = (const u08bits*)"Unauthorised"; if(server->ct != TURN_CREDENTIALS_SHORT_TERM) { return create_challenge_response(server,ss,tid,resp_constructed,err_code,reason,nbh,method); } else { return -1; } } ns_bcopy(ukey,ss->hmackey,16); ss->hmackey_set = 1; } /* Check integrity */ int too_weak = 0; if(stun_check_message_integrity_by_key_str(server->ct,ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), ss->hmackey, ss->pwd, server->shatype, &too_weak)<1) { if(too_weak) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: user %s credentials are incorrect: SHA function is too weak\n", __FUNCTION__, (char*)usname); *err_code = SHA_TOO_WEAK; *reason = (const u08bits*)"Unauthorised: weak SHA function is used"; if(server->ct != TURN_CREDENTIALS_SHORT_TERM) { return create_challenge_response(server,ss,tid,resp_constructed,err_code,reason,nbh,method); } else { return -1; } } TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: user %s credentials are incorrect\n", __FUNCTION__, (char*)usname); *err_code = 401; *reason = (const u08bits*)"Unauthorised"; if(server->ct != TURN_CREDENTIALS_SHORT_TERM) { return create_challenge_response(server,ss,tid,resp_constructed,err_code,reason,nbh,method); } else { return -1; } } *message_integrity = 1; return 0; } //<<== AUTH static void set_alternate_server(turn_server_addrs_list_t *asl, const ioa_addr *local_addr, size_t *counter, u16bits method, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, ioa_network_buffer_handle nbh) { if(asl && asl->size && local_addr) { size_t i; /* to prevent indefinite cycle: */ for(i=0;isize;++i) { ioa_addr *addr = &(asl->addrs[i]); if(addr_eq(addr,local_addr)) return; } for(i=0;isize;++i) { if(*counter>=asl->size) *counter = 0; ioa_addr *addr = &(asl->addrs[*counter]); *counter +=1; if(addr->ss.sa_family == local_addr->ss.sa_family) { *err_code = 300; *reason = (const u08bits *)"Redirect"; size_t len = ioa_network_buffer_get_size(nbh); stun_init_error_response_str(method, ioa_network_buffer_data(nbh), &len, *err_code, *reason, tid); *resp_constructed = 1; stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_ALTERNATE_SERVER, addr); ioa_network_buffer_set_size(nbh,len); return; } } } } #define log_method(ss, username, method, err_code) \ {\ if(!(err_code)) {\ TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,\ "session %018llu: user <%s>: incoming packet " method " processed, success\n",\ (unsigned long long)(ss->id),(const char*)(username));\ } else {\ TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,\ "session %018llu: user <%s>: incoming packet " method " processed, error %d\n",\ (unsigned long long)(ss->id), (username), (err_code));\ }\ } static int handle_turn_command(turn_turnserver *server, ts_ur_super_session *ss, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, int *resp_constructed, int can_resume) { stun_tid tid; int err_code = 0; const u08bits *reason = NULL; int no_response = 0; int message_integrity = 0; if(!(ss->client_session.s)) return -1; u16bits unknown_attrs[MAX_NUMBER_OF_UNKNOWN_ATTRS]; u16bits ua_num = 0; u16bits method = stun_get_method_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); *resp_constructed = 0; stun_tid_from_message_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), &tid); if (stun_is_request_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh))) { if((method == STUN_METHOD_BINDING) && (*(server->no_stun))) { no_response = 1; if(server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: STUN method 0x%x ignored\n", __FUNCTION__, (unsigned int)method); } } else if((method != STUN_METHOD_BINDING) && (*(server->stun_only))) { no_response = 1; if(server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: STUN method 0x%x ignored\n", __FUNCTION__, (unsigned int)method); } } else if((method != STUN_METHOD_BINDING) || (*(server->secure_stun))) { if(method == STUN_METHOD_ALLOCATE) { SOCKET_TYPE cst = get_ioa_socket_type(ss->client_session.s); turn_server_addrs_list_t *asl = server->alternate_servers_list; if(((cst == UDP_SOCKET)||(cst == DTLS_SOCKET)) && server->self_udp_balance && server->aux_servers_list && server->aux_servers_list->size) { asl = server->aux_servers_list; } else if(((cst == TLS_SOCKET) || (cst == DTLS_SOCKET)) && server->tls_alternate_servers_list && server->tls_alternate_servers_list->size) { asl = server->tls_alternate_servers_list; } if(asl && asl->size) { turn_mutex_lock(&(asl->m)); set_alternate_server(asl,get_local_addr_from_ioa_socket(ss->client_session.s),&(server->as_counter),method,&tid,resp_constructed,&err_code,&reason,nbh); turn_mutex_unlock(&(asl->m)); } } if(!err_code && !(*resp_constructed) && !no_response) { if(!(*(server->mobility)) || (method != STUN_METHOD_REFRESH) || is_allocation_valid(get_allocation_ss(ss))) { int postpone_reply = 0; check_stun_auth(server, ss, &tid, resp_constructed, &err_code, &reason, in_buffer, nbh, method, &message_integrity, &postpone_reply, can_resume); if(postpone_reply) no_response = 1; } } } if (!err_code && !(*resp_constructed) && !no_response) { switch (method){ case STUN_METHOD_ALLOCATE: { handle_turn_allocate(server, ss, &tid, resp_constructed, &err_code, &reason, unknown_attrs, &ua_num, in_buffer, nbh); if(server->verbose) { log_method(ss, ss->username, "ALLOCATE", err_code); } break; } case STUN_METHOD_CONNECT: handle_turn_connect(server, ss, &tid, &err_code, &reason, unknown_attrs, &ua_num, in_buffer); if(server->verbose) { log_method(ss, ss->username, "CONNECT", err_code); } if(!err_code) no_response = 1; break; case STUN_METHOD_CONNECTION_BIND: handle_turn_connection_bind(server, ss, &tid, resp_constructed, &err_code, &reason, unknown_attrs, &ua_num, in_buffer, nbh, message_integrity); if(server->verbose) { log_method(ss, ss->username, "CONNECTION_BIND", err_code); } break; case STUN_METHOD_REFRESH: handle_turn_refresh(server, ss, &tid, resp_constructed, &err_code, &reason, unknown_attrs, &ua_num, in_buffer, nbh, message_integrity, &no_response, can_resume); if(server->verbose) { log_method(ss, ss->username, "REFRESH", err_code); } break; case STUN_METHOD_CHANNEL_BIND: handle_turn_channel_bind(server, ss, &tid, resp_constructed, &err_code, &reason, unknown_attrs, &ua_num, in_buffer, nbh); if(server->verbose) { log_method(ss, ss->username, "CHANNEL_BIND", err_code); } break; case STUN_METHOD_CREATE_PERMISSION: handle_turn_create_permission(server, ss, &tid, resp_constructed, &err_code, &reason, unknown_attrs, &ua_num, in_buffer, nbh); if(server->verbose) { log_method(ss, ss->username, "CREATE_PERMISSION", err_code); } break; case STUN_METHOD_BINDING: { int origin_changed=0; ioa_addr response_origin; int dest_changed=0; ioa_addr response_destination; handle_turn_binding(server, ss, &tid, resp_constructed, &err_code, &reason, unknown_attrs, &ua_num, in_buffer, nbh, &origin_changed, &response_origin, &dest_changed, &response_destination, 0, 0); if(server->verbose) { log_method(ss, ss->username, "BINDING", err_code); } if(*resp_constructed && !err_code && (origin_changed || dest_changed)) { if (server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "RFC 5780 request successfully processed\n"); } { static const u08bits *field = (const u08bits *) TURN_SOFTWARE; static const size_t fsz = sizeof(TURN_SOFTWARE)-1; size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_SOFTWARE, field, fsz); ioa_network_buffer_set_size(nbh, len); } send_turn_message_to(server, nbh, &response_origin, &response_destination); no_response = 1; } break; } default: TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unsupported STUN request received, method 0x%x\n",(unsigned int)method); }; } } else if (stun_is_indication_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh))) { no_response = 1; int postpone = 0; if(server->ct == TURN_CREDENTIALS_SHORT_TERM) { check_stun_auth(server, ss, &tid, resp_constructed, &err_code, &reason, in_buffer, nbh, method, &message_integrity, &postpone, can_resume); } if (!postpone && !err_code) { switch (method){ case STUN_METHOD_BINDING: //ICE ? break; case STUN_METHOD_SEND: handle_turn_send(server, ss, &err_code, &reason, unknown_attrs, &ua_num, in_buffer); if(eve(server->verbose)) { log_method(ss, ss->username, "SEND", err_code); } break; case STUN_METHOD_DATA: err_code = 403; if(eve(server->verbose)) { log_method(ss, ss->username, "DATA", err_code); } break; default: if (server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Unsupported STUN indication received: method 0x%x\n",(unsigned int)method); } } }; } else { no_response = 1; if (server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Wrong STUN message received\n"); } } if(ss->to_be_closed || !(ss->client_session.s) || ioa_socket_tobeclosed(ss->client_session.s)) return 0; if (ua_num > 0) { err_code = 420; size_t len = ioa_network_buffer_get_size(nbh); stun_init_error_response_str(method, ioa_network_buffer_data(nbh), &len, err_code, NULL, &tid); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES, (const u08bits*) unknown_attrs, (ua_num * 2)); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } if (!no_response) { if (!(*resp_constructed)) { if (!err_code) err_code = 400; size_t len = ioa_network_buffer_get_size(nbh); stun_init_error_response_str(method, ioa_network_buffer_data(nbh), &len, err_code, reason, &tid); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } { static const u08bits *field = (const u08bits *) TURN_SOFTWARE; static const size_t fsz = sizeof(TURN_SOFTWARE)-1; size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_SOFTWARE, field, fsz); ioa_network_buffer_set_size(nbh, len); } if(message_integrity) { size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_integrity_str(server->ct,ioa_network_buffer_data(nbh),&len,ss->hmackey,ss->pwd,server->shatype); ioa_network_buffer_set_size(nbh,len); } if(err_code) { if(server->verbose) { log_method(ss, ss->username, "message", err_code); } } } else { *resp_constructed = 0; } return 0; } static int handle_old_stun_command(turn_turnserver *server, ts_ur_super_session *ss, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, int *resp_constructed, u32bits cookie) { stun_tid tid; int err_code = 0; const u08bits *reason = NULL; int no_response = 0; u16bits unknown_attrs[MAX_NUMBER_OF_UNKNOWN_ATTRS]; u16bits ua_num = 0; u16bits method = stun_get_method_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); *resp_constructed = 0; stun_tid_from_message_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), &tid); if (stun_is_request_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh))) { if(method != STUN_METHOD_BINDING) { no_response = 1; if(server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: OLD STUN method 0x%x ignored\n", __FUNCTION__, (unsigned int)method); } } if (!err_code && !(*resp_constructed) && !no_response) { int origin_changed=0; ioa_addr response_origin; int dest_changed=0; ioa_addr response_destination; handle_turn_binding(server, ss, &tid, resp_constructed, &err_code, &reason, unknown_attrs, &ua_num, in_buffer, nbh, &origin_changed, &response_origin, &dest_changed, &response_destination, cookie,1); if(server->verbose) { log_method(ss, ss->username, "OLD BINDING", err_code); } if(*resp_constructed && !err_code && (origin_changed || dest_changed)) { if (server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "RFC3489 CHANGE request successfully processed\n"); } { size_t newsz = (((sizeof(TURN_SOFTWARE))>>2) + 1)<<2; u08bits software[120]; if(newsz>sizeof(software)) newsz = sizeof(software); ns_bcopy(TURN_SOFTWARE,software,newsz); size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, OLD_STUN_ATTRIBUTE_SERVER, software, newsz); ioa_network_buffer_set_size(nbh, len); } send_turn_message_to(server, nbh, &response_origin, &response_destination); no_response = 1; } } } else { no_response = 1; if (server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Wrong OLD STUN message received\n"); } } if (ua_num > 0) { err_code = 420; size_t len = ioa_network_buffer_get_size(nbh); old_stun_init_error_response_str(method, ioa_network_buffer_data(nbh), &len, err_code, NULL, &tid, cookie); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES, (const u08bits*) unknown_attrs, (ua_num * 2)); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } if (!no_response) { if (!(*resp_constructed)) { if (!err_code) err_code = 400; size_t len = ioa_network_buffer_get_size(nbh); old_stun_init_error_response_str(method, ioa_network_buffer_data(nbh), &len, err_code, reason, &tid, cookie); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } { size_t newsz = (((sizeof(TURN_SOFTWARE))>>2) + 1)<<2; u08bits software[120]; if(newsz>sizeof(software)) newsz = sizeof(software); ns_bcopy(TURN_SOFTWARE,software,newsz); size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, OLD_STUN_ATTRIBUTE_SERVER, software, newsz); ioa_network_buffer_set_size(nbh, len); } if(err_code) { if(server->verbose) { log_method(ss, ss->username, "OLD STUN message", err_code); } } } else { *resp_constructed = 0; } return 0; } ////////////////////////////////////////////////////////////////// static int write_to_peerchannel(ts_ur_super_session* ss, u16bits chnum, ioa_net_data *in_buffer) { int rc = 0; if (ss && get_relay_socket_ss(ss) && (in_buffer->recv_ttl!=0)) { allocation* a = get_allocation_ss(ss); if (is_allocation_valid(a)) { ch_info* chn = allocation_get_ch_info(a, chnum); if (!chn) return -1; /* Channel packets are always sent with DF=0: */ set_df_on_ioa_socket(get_relay_socket_ss(ss), 0); ioa_network_buffer_handle nbh = in_buffer->nbh; ioa_network_buffer_add_offset_size(in_buffer->nbh, STUN_CHANNEL_HEADER_LENGTH, 0, ioa_network_buffer_get_size(in_buffer->nbh)-STUN_CHANNEL_HEADER_LENGTH); ioa_network_buffer_header_init(nbh); rc = send_data_from_ioa_socket_nbh(get_relay_socket_ss(ss), &(chn->peer_addr), nbh, in_buffer->recv_ttl-1, in_buffer->recv_tos); in_buffer->nbh = NULL; } } return rc; } static void client_input_handler(ioa_socket_handle s, int event_type, ioa_net_data *data, void *arg); static void peer_input_handler(ioa_socket_handle s, int event_type, ioa_net_data *data, void *arg); /////////////// Client actions ///////////////// int shutdown_client_connection(turn_turnserver *server, ts_ur_super_session *ss, int force, const char* reason) { FUNCSTART; if (!ss) return -1; ts_ur_session* elem = &(ss->client_session); if(!force) { if (elem->s && server->verbose) { char sraddr[129]="\0"; char sladdr[129]="\0"; addr_to_string(get_remote_addr_from_ioa_socket(elem->s),(u08bits*)sraddr); addr_to_string(get_remote_addr_from_ioa_socket(elem->s),(u08bits*)sladdr); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "session %018llu: closed (1st stage), user <%s>, local %s, remote %s, reason: %s\n",(unsigned long long)(ss->id),(char*)ss->username,sladdr,sraddr,reason); } IOA_CLOSE_SOCKET(elem->s); IOA_CLOSE_SOCKET(ss->alloc.relay_session.s); FUNCEND; return 0; } report_turn_session_info(server,ss,1); if (eve(server->verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "closing session 0x%lx, client socket 0x%lx in state %ld (socket session=0x%lx)\n", (long) ss, (long) elem->s, (long) (elem->state), (long)get_ioa_socket_session(elem->s)); } if (elem->state == UR_STATE_DONE) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! closing session 0x%lx, client session 0x%lx in DONE state\n", (long)ss, (long) elem); return -1; } elem->state = UR_STATE_DONE; if (ss->alloc.relay_session.state == UR_STATE_DONE) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! closing session 0x%lx, relay session 0x%lx in DONE state\n", (long)ss, (long)&(ss->alloc.relay_session)); return -1; } ss->alloc.relay_session.state = UR_STATE_DONE; if (server->disconnect) server->disconnect(ss); IOA_CLOSE_SOCKET(elem->s); IOA_CLOSE_SOCKET(ss->alloc.relay_session.s); if (server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "session %018llu: closed (2nd stage), user <%s>, reason: %s\n",(unsigned long long)(ss->id),(char*)ss->username,reason); } turn_server_remove_all_from_ur_map_ss(ss); FUNCEND; return 0; } static void client_to_be_allocated_timeout_handler(ioa_engine_handle e, void *arg) { if (!arg) return; UNUSED_ARG(e); ts_ur_super_session* ss = (ts_ur_super_session*) arg; turn_turnserver* server = (turn_turnserver*) (ss->server); if (!server) return; FUNCSTART; int to_close = 0; ioa_socket_handle s = ss->client_session.s; if(!s || ioa_socket_tobeclosed(s)) { to_close = 1; } else { ioa_socket_handle rs = ss->alloc.relay_session.s; if(!rs || ioa_socket_tobeclosed(rs)) { to_close = 1; } else if(ss->alloc.relay_session.state == UR_STATE_DONE) { to_close = 1; } else if(ss->client_session.state == UR_STATE_DONE) { to_close = 1; } else if(!(ss->alloc.lifetime_ev)) { to_close = 1; } else if(!(ss->to_be_allocated_timeout_ev)) { to_close = 1; } } if(to_close) { IOA_EVENT_DEL(ss->to_be_allocated_timeout_ev); shutdown_client_connection(server, ss, 1, "allocation watchdog determined stale session state"); } FUNCEND; } static int write_client_connection(turn_turnserver *server, ts_ur_super_session* ss, ioa_network_buffer_handle nbh, int ttl, int tos) { FUNCSTART; ts_ur_session* elem = &(ss->client_session); if (elem->state != UR_STATE_READY) { ioa_network_buffer_delete(server->e, nbh); FUNCEND; return -1; } else { ++(ss->sent_packets); ss->sent_bytes += (u32bits)ioa_network_buffer_get_size(nbh); turn_report_session_usage(ss); if (eve(server->verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: prepare to write to s 0x%lx\n", __FUNCTION__, (long) (elem->s)); } int ret = send_data_from_ioa_socket_nbh(elem->s, NULL, nbh, ttl, tos); FUNCEND; return ret; } } static void client_ss_allocation_timeout_handler(ioa_engine_handle e, void *arg) { UNUSED_ARG(e); if (!arg) return; ts_ur_super_session* ss = (ts_ur_super_session*)arg; if (!ss) return; allocation* a = get_allocation_ss(ss); turn_turnserver* server = (turn_turnserver*) (ss->server); if (!server) { clear_allocation(a); return; } FUNCSTART; shutdown_client_connection(server, ss, 0, "allocation timeout"); FUNCEND; } static int create_relay_connection(turn_turnserver* server, ts_ur_super_session *ss, u32bits lifetime, int address_family, u08bits transport, int even_port, u64bits in_reservation_token, u64bits *out_reservation_token, int *err_code, const u08bits **reason, accept_cb acb) { if (server && ss && ss->client_session.s) { allocation* a = get_allocation_ss(ss); ts_ur_session* newelem = get_relay_session_ss(ss); IOA_CLOSE_SOCKET(newelem->s); ns_bzero(newelem, sizeof(ts_ur_session)); newelem->s = NULL; ioa_socket_handle rtcp_s = NULL; if (in_reservation_token) { if (get_ioa_socket_from_reservation(server->e, in_reservation_token, &newelem->s) < 0) { IOA_CLOSE_SOCKET(newelem->s); *err_code = 508; *reason = (const u08bits *)"Cannot find reserved socket"; return -1; } addr_debug_print(server->verbose, get_local_addr_from_ioa_socket(newelem->s), "Local relay addr (RTCP)"); } else { int res = create_relay_ioa_sockets(server->e, ss->client_session.s, address_family, transport, even_port, &(newelem->s), &rtcp_s, out_reservation_token, err_code, reason, acb, ss); if (res < 0) { if(!(*err_code)) *err_code = 508; if(!(*reason)) *reason = (const u08bits *)"Cannot create socket"; IOA_CLOSE_SOCKET(newelem->s); IOA_CLOSE_SOCKET(rtcp_s); return -1; } } if (newelem->s == NULL) { IOA_CLOSE_SOCKET(rtcp_s); *err_code = 508; *reason = (const u08bits *)"Cannot create relay socket"; return -1; } if (rtcp_s) { if (out_reservation_token && *out_reservation_token) { /* OK */ } else { IOA_CLOSE_SOCKET(newelem->s); IOA_CLOSE_SOCKET(rtcp_s); *err_code = 508; *reason = (const u08bits *)"Wrong reservation tokens (internal error)"; return -1; } } newelem->state = UR_STATE_READY; /* RFC6156: do not use DF when IPv6 is involved: */ if((get_local_addr_from_ioa_socket(newelem->s)->ss.sa_family == AF_INET6) || (get_local_addr_from_ioa_socket(ss->client_session.s)->ss.sa_family == AF_INET6)) set_do_not_use_df(newelem->s); if(get_ioa_socket_type(newelem->s) != TCP_SOCKET) { register_callback_on_ioa_socket(server->e, newelem->s, IOA_EV_READ, peer_input_handler, ss, 0); } if (lifetime<1) lifetime = STUN_DEFAULT_ALLOCATE_LIFETIME; else if(lifetime>STUN_MAX_ALLOCATE_LIFETIME) lifetime = STUN_MAX_ALLOCATE_LIFETIME; ioa_timer_handle ev = set_ioa_timer(server->e, lifetime, 0, client_ss_allocation_timeout_handler, ss, 0, "client_ss_allocation_timeout_handler"); set_allocation_lifetime_ev(a, server->ctime + lifetime, ev); set_ioa_socket_session(newelem->s, ss); } return 0; } static int refresh_relay_connection(turn_turnserver* server, ts_ur_super_session *ss, u32bits lifetime, int even_port, u64bits in_reservation_token, u64bits *out_reservation_token, int *err_code) { UNUSED_ARG(even_port); UNUSED_ARG(in_reservation_token); UNUSED_ARG(out_reservation_token); UNUSED_ARG(err_code); allocation* a = get_allocation_ss(ss); if (server && ss && is_allocation_valid(a)) { if (lifetime < 1) { set_allocation_valid(a, 0); lifetime = 1; } ioa_timer_handle ev = set_ioa_timer(server->e, lifetime, 0, client_ss_allocation_timeout_handler, ss, 0, "refresh_client_ss_allocation_timeout_handler"); set_allocation_lifetime_ev(a, server->ctime + lifetime, ev); return 0; } else { return -1; } } static void write_http_echo(turn_turnserver *server, ts_ur_super_session *ss) { if(server && ss && ss->client_session.s && !(ss->to_be_closed)) { ioa_network_buffer_handle nbh_http = ioa_network_buffer_allocate(server->e); size_t len_http = ioa_network_buffer_get_size(nbh_http); u08bits *data = ioa_network_buffer_data(nbh_http); char data_http[1025]; char content_http[1025]; const char* title = "TURN Server"; snprintf(content_http,sizeof(content_http)-1,"\r\n\r\n \r\n %s\r\n \r\n \r\n %s\r\n \r\n\r\n",title,title); snprintf(data_http,sizeof(data_http)-1,"HTTP/1.1 200 OK\r\nServer: %s\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: %d\r\n\r\n%s",TURN_SOFTWARE,(int)strlen(content_http),content_http); len_http = strlen(data_http); ns_bcopy(data_http,data,len_http); ioa_network_buffer_set_size(nbh_http,len_http); send_data_from_ioa_socket_nbh(ss->client_session.s, NULL, nbh_http, TTL_IGNORE, TOS_IGNORE); } } static int read_client_connection(turn_turnserver *server, ts_ur_session *elem, ts_ur_super_session *ss, ioa_net_data *in_buffer, int can_resume, int count_usage) { FUNCSTART; if (!server || !elem || !ss || !in_buffer) { FUNCEND; return -1; } if (elem->state != UR_STATE_READY) { FUNCEND; return -1; } int ret = (int)ioa_network_buffer_get_size(in_buffer->nbh); if (ret < 0) { FUNCEND; return -1; } if(count_usage) { ++(ss->received_packets); ss->received_bytes += (u32bits)ioa_network_buffer_get_size(in_buffer->nbh); turn_report_session_usage(ss); } if (eve(server->verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: data.buffer=0x%lx, data.len=%ld\n", __FUNCTION__, (long)ioa_network_buffer_data(in_buffer->nbh), (long)ioa_network_buffer_get_size(in_buffer->nbh)); } u16bits chnum = 0; u32bits old_stun_cookie = 0; size_t blen = ioa_network_buffer_get_size(in_buffer->nbh); size_t orig_blen = blen; SOCKET_TYPE st = get_ioa_socket_type(ss->client_session.s); int is_padding_mandatory = ((st == TCP_SOCKET)||(st==TLS_SOCKET)||(st==TENTATIVE_TCP_SOCKET)); if (stun_is_channel_message_str(ioa_network_buffer_data(in_buffer->nbh), &blen, &chnum, is_padding_mandatory)) { if(ss->is_tcp_relay) { //Forbidden FUNCEND; return -1; } int rc = 0; if(blen<=orig_blen) { ioa_network_buffer_set_size(in_buffer->nbh,blen); rc = write_to_peerchannel(ss, chnum, in_buffer); } if (eve(server->verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: wrote to peer %d bytes\n", __FUNCTION__, (int) rc); } FUNCEND; return 0; } else if (stun_is_command_message_full_check_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), 0, &(ss->enforce_fingerprints))) { ioa_network_buffer_handle nbh = ioa_network_buffer_allocate(server->e); int resp_constructed = 0; u16bits method = stun_get_method_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); handle_turn_command(server, ss, in_buffer, nbh, &resp_constructed, can_resume); if((method != STUN_METHOD_BINDING) && (method != STUN_METHOD_SEND)) report_turn_session_info(server,ss,0); if(ss->to_be_closed || ioa_socket_tobeclosed(ss->client_session.s)) { FUNCEND; ioa_network_buffer_delete(server->e, nbh); return 0; } if (resp_constructed) { if ((server->fingerprint) || ss->enforce_fingerprints) { size_t len = ioa_network_buffer_get_size(nbh); if (stun_attr_add_fingerprint_str(ioa_network_buffer_data(nbh), &len) < 0) { FUNCEND ; ioa_network_buffer_delete(server->e, nbh); return -1; } ioa_network_buffer_set_size(nbh, len); } int ret = write_client_connection(server, ss, nbh, TTL_IGNORE, TOS_IGNORE); FUNCEND ; return ret; } else { ioa_network_buffer_delete(server->e, nbh); return 0; } } else if (old_stun_is_command_message_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), &old_stun_cookie) && !(*(server->no_stun))) { ioa_network_buffer_handle nbh = ioa_network_buffer_allocate(server->e); int resp_constructed = 0; handle_old_stun_command(server, ss, in_buffer, nbh, &resp_constructed, old_stun_cookie); if (resp_constructed) { int ret = write_client_connection(server, ss, nbh, TTL_IGNORE, TOS_IGNORE); FUNCEND ; return ret; } else { ioa_network_buffer_delete(server->e, nbh); return 0; } } else { SOCKET_TYPE st = get_ioa_socket_type(ss->client_session.s); if((st == TCP_SOCKET)||(st==TLS_SOCKET)||(st==TENTATIVE_TCP_SOCKET)) { if(is_http_get((char*)ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh))) write_http_echo(server,ss); } } //Unrecognized message received, ignore it FUNCEND; return -1; } static int attach_socket_to_session(turn_turnserver* server, ioa_socket_handle s, ts_ur_super_session* ss) { int ret = -1; FUNCSTART; if(s && server && ss) { if(ss->client_session.s != s) { ts_ur_session *newelem = &(ss->client_session); if(newelem->s) { IOA_CLOSE_SOCKET(newelem->s); } newelem->s = s; register_callback_on_ioa_socket(server->e, newelem->s, IOA_EV_READ, client_input_handler, ss, 0); set_ioa_socket_session(newelem->s, ss); newelem->state = UR_STATE_READY; } ret = 0; } FUNCEND; return ret; } int open_client_connection_session(turn_turnserver* server, struct socket_message *sm) { FUNCSTART; if (!server) return -1; if (!(sm->s)) return -1; ts_ur_super_session* ss = create_new_ss(server); ts_ur_session *newelem = &(ss->client_session); newelem->s = sm->s; register_callback_on_ioa_socket(server->e, newelem->s, IOA_EV_READ, client_input_handler, ss, 0); set_ioa_socket_session(newelem->s, ss); newelem->state = UR_STATE_READY; int at = TURN_MAX_ALLOCATE_TIMEOUT; if(*(server->stun_only)) at = TURN_MAX_ALLOCATE_TIMEOUT_STUN_ONLY; IOA_EVENT_DEL(ss->to_be_allocated_timeout_ev); ss->to_be_allocated_timeout_ev = set_ioa_timer(server->e, at, 0, client_to_be_allocated_timeout_handler, ss, 1, "client_to_be_allocated_timeout_handler"); if(sm->nd.nbh) { client_input_handler(newelem->s,IOA_EV_READ,&(sm->nd),ss); ioa_network_buffer_delete(server->e, sm->nd.nbh); sm->nd.nbh = NULL; } FUNCEND; return 0; } /////////////// io handlers /////////////////// static void peer_input_handler(ioa_socket_handle s, int event_type, ioa_net_data *in_buffer, void *arg) { if (!(event_type & IOA_EV_READ) || !arg) return; if(in_buffer->recv_ttl==0) return; UNUSED_ARG(s); ts_ur_super_session* ss = (ts_ur_super_session*) arg; if(!ss) return; turn_turnserver *server = (turn_turnserver*) (ss->server); if (!server) { return; } ts_ur_session* elem = get_relay_session_ss(ss); if (elem->s == NULL) { return; } int offset = STUN_CHANNEL_HEADER_LENGTH; int ilen = min((int)ioa_network_buffer_get_size(in_buffer->nbh), (int)(ioa_network_buffer_get_capacity_udp() - offset)); if (ilen >= 0) { size_t len = (size_t)(ilen); allocation* a = get_allocation_ss(ss); if (is_allocation_valid(a)) { u16bits chnum = 0; ioa_network_buffer_handle nbh = NULL; turn_permission_info* tinfo = allocation_get_permission(a, &(in_buffer->src_addr)); if (tinfo) { chnum = get_turn_channel_number(tinfo, &(in_buffer->src_addr)); } else if(!(server->server_relay)) { return; } if (chnum) { nbh = in_buffer->nbh; ioa_network_buffer_add_offset_size(nbh, 0, STUN_CHANNEL_HEADER_LENGTH, ioa_network_buffer_get_size(nbh)+STUN_CHANNEL_HEADER_LENGTH); ioa_network_buffer_header_init(nbh); SOCKET_TYPE st = get_ioa_socket_type(ss->client_session.s); int do_padding = ((st == TCP_SOCKET)||(st==TLS_SOCKET)||(st==TENTATIVE_TCP_SOCKET)); stun_init_channel_message_str(chnum, ioa_network_buffer_data(nbh), &len, len, do_padding); ioa_network_buffer_set_size(nbh,len); in_buffer->nbh = NULL; if (eve(server->verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: send channel 0x%x\n", __FUNCTION__, (int) (chnum)); } } else { nbh = ioa_network_buffer_allocate(server->e); stun_init_indication_str(STUN_METHOD_DATA, ioa_network_buffer_data(nbh), &len); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_DATA, ioa_network_buffer_data(in_buffer->nbh), (size_t)ilen); stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &(in_buffer->src_addr)); ioa_network_buffer_set_size(nbh,len); { static const u08bits *field = (const u08bits *) TURN_SOFTWARE; static const size_t fsz = sizeof(TURN_SOFTWARE)-1; size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_SOFTWARE, field, fsz); ioa_network_buffer_set_size(nbh, len); } /* We add integrity for short-term indication messages, only */ if(server->ct == TURN_CREDENTIALS_SHORT_TERM) { stun_attr_add_integrity_str(server->ct,ioa_network_buffer_data(nbh),&len,ss->hmackey,ss->pwd,server->shatype); ioa_network_buffer_set_size(nbh,len); } if ((server->fingerprint) || ss->enforce_fingerprints) { size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_fingerprint_str(ioa_network_buffer_data(nbh), &len); ioa_network_buffer_set_size(nbh, len); } } if (eve(server->verbose)) { u16bits* t = (u16bits*) ioa_network_buffer_data(nbh); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Send data: 0x%x\n", (int) (nswap16(t[0]))); } int ret = write_client_connection(server, ss, nbh, in_buffer->recv_ttl-1, in_buffer->recv_tos); if (ret < 0) { if(server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "session %018llu: client socket to be closed from peer handler: ss=0x%lx\n", (unsigned long long)(ss->id), (long)ss); } set_ioa_socket_tobeclosed(s); } } } } static void client_input_handler(ioa_socket_handle s, int event_type, ioa_net_data *data, void *arg) { if (!arg) return; UNUSED_ARG(s); UNUSED_ARG(event_type); ts_ur_super_session* ss = (ts_ur_super_session*)arg; turn_turnserver *server = (turn_turnserver*)ss->server; if (!server) { return; } ts_ur_session* elem = &(ss->client_session); if (elem->s == NULL) { return; } int ret = 0; switch (elem->state) { case UR_STATE_READY: read_client_connection(server, elem, ss, data, 1, 1); break; case UR_STATE_DONE: TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: Trying to read from closed socket: s=0x%lx\n", __FUNCTION__, (long) (elem->s)); break; default: ret = -1; } if (ret < 0) { if(server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "session %018llu: client socket error in client handler: s=0x%lx\n", (unsigned long long)(ss->id), (long) (elem->s)); } set_ioa_socket_tobeclosed(s); } else if (ss->to_be_closed) { if(server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "session %018llu: client socket to be closed in client handler: ss=0x%lx\n", (unsigned long long)(ss->id), (long)ss); } set_ioa_socket_tobeclosed(s); } } /////////////////////////////////////////////////////////// void init_turn_server(turn_turnserver* server, turnserver_id id, int verbose, ioa_engine_handle e, int stun_port, int fingerprint, dont_fragment_option_t dont_fragment, turn_credential_type ct, u08bits *realm, get_user_key_cb userkeycb, check_new_allocation_quota_cb chquotacb, release_allocation_quota_cb raqcb, ioa_addr *external_ip, vintp no_tcp_relay, vintp no_udp_relay, vintp stale_nonce, vintp stun_only, vintp no_stun, turn_server_addrs_list_t *alternate_servers_list, turn_server_addrs_list_t *tls_alternate_servers_list, turn_server_addrs_list_t *aux_servers_list, int self_udp_balance, vintp no_multicast_peers, vintp no_loopback_peers, ip_range_list_t* ip_whitelist, ip_range_list_t* ip_blacklist, send_socket_to_relay_cb send_socket_to_relay, vintp secure_stun, SHATYPE shatype, vintp mobility, int server_relay, send_turn_session_info_cb send_turn_session_info) { if (!server) return; ns_bzero(server,sizeof(turn_turnserver)); server->e = e; server->id = id; server->ctime = turn_time(); server->session_id_counter = 0; server->sessions_map = ur_map_create(); server->tcp_relay_connections = ur_map_create(); server->ct = ct; STRCPY(server->realm,realm); server->userkeycb = userkeycb; server->chquotacb = chquotacb; server->raqcb = raqcb; server->no_multicast_peers = no_multicast_peers; server->no_loopback_peers = no_loopback_peers; server->secure_stun = secure_stun; server->shatype = shatype; server->mobility = mobility; server->server_relay = server_relay; server->send_turn_session_info = send_turn_session_info; if(mobility) server->mobile_connections_map = ur_map_create(); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"turn server id=%d created\n",(int)id); server->no_tcp_relay = no_tcp_relay; server->no_udp_relay = no_udp_relay; server->alternate_servers_list = alternate_servers_list; server->tls_alternate_servers_list = tls_alternate_servers_list; server->aux_servers_list = aux_servers_list; server->self_udp_balance = self_udp_balance; server->stale_nonce = stale_nonce; server->stun_only = stun_only; server->no_stun = no_stun; server->dont_fragment = dont_fragment; server->fingerprint = fingerprint; if(external_ip) { addr_cpy(&(server->external_ip), external_ip); server->external_ip_set = 1; } if (stun_port < 1) stun_port = DEFAULT_STUN_PORT; server->verbose = verbose; server->ip_whitelist = ip_whitelist; server->ip_blacklist = ip_blacklist; server->send_socket_to_relay = send_socket_to_relay; set_ioa_timer(server->e, 1, 0, timer_timeout_handler, server, 1, "timer_timeout_handler"); } ioa_engine_handle turn_server_get_engine(turn_turnserver *s) { if(s) return s->e; return NULL; } void set_disconnect_cb(turn_turnserver* server, int(*disconnect)( ts_ur_super_session*)) { server->disconnect = disconnect; } ////////////////////////////////////////////////////////////////// turnserver-3.2.3.1/src/server/ns_turn_maps.c000644 001751 001751 00000057433 12315706777 021021 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "ns_turn_maps.h" #include "ns_turn_ioalib.h" #include "ns_turn_khash.h" KHASH_MAP_INIT_INT64(3, ur_map_value_type) #define MAGIC_HASH ((u64bits)(0x90ABCDEFL)) struct _ur_map { khash_t(3) *h; u64bits magic; TURN_MUTEX_DECLARE(mutex) }; static int ur_map_init(ur_map* map) { if(map) { map->h=kh_init(3); if(map->h) { map->magic=MAGIC_HASH; TURN_MUTEX_INIT_RECURSIVE(&(map->mutex)); return 0; } } return -1; } static int ur_map_valid(const ur_map *map) { return (map && map->h && map->magic==MAGIC_HASH); } ur_map* ur_map_create() { ur_map *map=(ur_map*)turn_malloc(sizeof(ur_map)); if(ur_map_init(map)<0) { turn_free(map,sizeof(ur_map)); return NULL; } return map; } /** * @ret: * 0 - success * -1 - error */ int ur_map_put(ur_map* map, ur_map_key_type key, ur_map_value_type value) { if(!ur_map_valid(map)) return -1; else { int ret=0; khiter_t k; k = kh_get(3, map->h, key); if(k != kh_end(map->h)) { kh_del(3, map->h, k); } k = kh_put(3,map->h,key,&ret); if (!ret) { kh_del(3, map->h, k); return -1; } kh_value(map->h, k) = value; return 0; } } /** * @ret: * 1 - success * 0 - not found */ int ur_map_get(const ur_map* map, ur_map_key_type key, ur_map_value_type *value) { if(!ur_map_valid(map)) return 0; else { khiter_t k; k = kh_get(3, map->h, key); if((k != kh_end(map->h)) && kh_exist(map->h,k)) { if(value) *value=kh_value(map->h,k); return 1; } return 0; } } /** * @ret: * 1 - success * 0 - not found */ int ur_map_del(ur_map* map, ur_map_key_type key,ur_map_del_func delfunc) { if(!ur_map_valid(map)) return 0; else { khiter_t k; k = kh_get(3, map->h, key); if((k != kh_end(map->h)) && kh_exist(map->h,k)) { if(delfunc) { delfunc(kh_value(map->h,k)); } kh_del(3,map->h,k); return 1; } return 0; } } /** * @ret: * 1 - success * 0 - not found */ int ur_map_exist(const ur_map* map, ur_map_key_type key) { if(!ur_map_valid(map)) return 0; else { khiter_t k; k = kh_get(3, map->h, key); if((k != kh_end(map->h)) && kh_exist(map->h,k)) { return 1; } return 0; } } void ur_map_free(ur_map** map) { if(map && ur_map_valid(*map)) { kh_destroy(3,(*map)->h); (*map)->h=NULL; (*map)->magic=0; TURN_MUTEX_DESTROY(&((*map)->mutex)); turn_free(*map,sizeof(ur_map)); *map=NULL; } } size_t ur_map_size(const ur_map* map) { if(ur_map_valid(map)) { return kh_size(map->h); } else { return 0; } } int ur_map_foreach(ur_map* map, foreachcb_type func) { if(map && func && ur_map_valid(map)) { khiter_t k; for (k = kh_begin((*map)->h); k != kh_end(map->h); ++k) { if (kh_exist(map->h, k)) { if(func((ur_map_key_type)(kh_key(map->h, k)), (ur_map_value_type)(kh_value(map->h, k)))) { return 1; } } } } return 0; } int ur_map_foreach_arg(ur_map* map, foreachcb_arg_type func, void* arg) { if(map && func && ur_map_valid(map)) { khiter_t k; for (k = kh_begin((*map)->h); k != kh_end(map->h); ++k) { if (kh_exist(map->h, k)) { if(func((ur_map_key_type)(kh_key(map->h, k)), (ur_map_value_type)(kh_value(map->h, k)), arg) ) { return 1; } } } } return 0; } int ur_map_lock(const ur_map* map) { if(ur_map_valid(map)) { TURN_MUTEX_LOCK((const turn_mutex*)&(map->mutex)); return 0; } return -1; } int ur_map_unlock(const ur_map* map) { if(ur_map_valid(map)) { TURN_MUTEX_UNLOCK((const turn_mutex*)&(map->mutex)); return 0; } return -1; } //////////////////// LOCAL MAPS //////////////////////////////////// void lm_map_init(lm_map *map) { if(map) { ns_bzero(map,sizeof(lm_map)); } } /** * @ret: * 0 - success * -1 - error */ int lm_map_put(lm_map* map, ur_map_key_type key, ur_map_value_type value) { int ret = -1; if(map && key && value) { size_t index = (size_t)(key & (LM_MAP_HASH_SIZE - 1)); lm_map_array *a = &(map->table[index]); size_t i; for(i=0;imain_keys[i]; ur_map_value_type value0 = a->main_values[i]; if(key0 == key) { if(value0 == value) { return 0; } else { return -1; } } if(!key0 || !value0) { a->main_keys[i] = key; a->main_values[i] = value; return 0; } } size_t esz = a->extra_sz; if(esz && a->extra_keys && a->extra_values) { for(i=0;iextra_keys[i]; ur_map_value_type *valuep = a->extra_values[i]; if(keyp && valuep) { if(!(*keyp) || !(*valuep)) { *keyp = key; *valuep = value; return 0; } } else { if(!(*keyp)) { a->extra_keys[i] = (ur_map_key_type*)turn_malloc(sizeof(ur_map_key_type)); keyp = a->extra_keys[i]; } if(!(*valuep)) { a->extra_values[i] = (ur_map_value_type*)turn_malloc(sizeof(ur_map_value_type)); valuep = a->extra_values[i]; } *keyp = key; *valuep = value; return 0; } } } size_t old_sz = esz; size_t old_sz_mem = esz * sizeof(ur_map_key_type*); a->extra_keys = (ur_map_key_type**)turn_realloc(a->extra_keys,old_sz_mem,old_sz_mem + sizeof(ur_map_key_type*)); a->extra_keys[old_sz] = (ur_map_key_type*)turn_malloc(sizeof(ur_map_key_type)); *(a->extra_keys[old_sz]) = key; old_sz_mem = esz * sizeof(ur_map_value_type*); a->extra_values = (ur_map_value_type**)turn_realloc(a->extra_values,old_sz_mem,old_sz_mem + sizeof(ur_map_value_type*)); a->extra_values[old_sz] = (ur_map_value_type*)turn_malloc(sizeof(ur_map_value_type)); *(a->extra_values[old_sz]) = value; a->extra_sz += 1; return 0; } return ret; } /** * @ret: * 1 - success * 0 - not found */ int lm_map_get(const lm_map* map, ur_map_key_type key, ur_map_value_type *value) { int ret = 0; if(map && key) { size_t index = (size_t)(key & (LM_MAP_HASH_SIZE - 1)); const lm_map_array *a = &(map->table[index]); size_t i; for(i=0;imain_keys[i]; if((key0 == key) && a->main_values[i]) { if(value) { *value = a->main_values[i]; } return 1; } } size_t esz = a->extra_sz; if(esz && a->extra_keys && a->extra_values) { for(i=0;iextra_keys[i]; ur_map_value_type *valuep = a->extra_values[i]; if(keyp && valuep) { if(*keyp == key) { if(value) *value = *valuep; return 1; } } } } } return ret; } /** * @ret: * 1 - success * 0 - not found */ int lm_map_del(lm_map* map, ur_map_key_type key,ur_map_del_func delfunc) { int ret = 0; if(map && key) { size_t index = (size_t)(key & (LM_MAP_HASH_SIZE - 1)); lm_map_array *a = &(map->table[index]); size_t i; for(i=0;imain_keys[i]; if((key0 == key) && a->main_values[i]) { if(delfunc) { delfunc(a->main_values[i]); } a->main_keys[i] = 0; a->main_values[i] = 0; return 1; } } size_t esz = a->extra_sz; if(esz && a->extra_keys && a->extra_values) { for(i=0;iextra_keys[i]; ur_map_value_type *valuep = a->extra_values[i]; if(keyp && valuep) { if(*keyp == key) { if(delfunc) delfunc(*valuep); *keyp = 0; *valuep = 0; return 1; } } } } } return ret; } /** * @ret: * 1 - success * 0 - not found */ int lm_map_exist(const lm_map* map, ur_map_key_type key) { return lm_map_get(map, key, NULL); } void lm_map_clean(lm_map* map) { size_t j; for(j=0;jtable[j]); size_t esz = a->extra_sz; if(esz) { size_t i; if(a->extra_keys) { for(i=0;iextra_keys[i]; if(keyp) { *keyp = 0; turn_free(keyp,sizeof(ur_map_key_type)); } } turn_free(a->extra_keys,esz * sizeof(ur_map_key_type)); a->extra_keys = NULL; } if(a->extra_values) { for(i=0;iextra_values[i]; if(valuep) { *valuep = 0; turn_free(valuep,sizeof(ur_map_value_type)); } } turn_free(a->extra_values,esz * sizeof(ur_map_value_type)); a->extra_values = NULL; } } } lm_map_init(map); } size_t lm_map_size(const lm_map* map) { size_t ret = 0; if(map) { size_t i; for(i=0;itable[i]); size_t j; for(j=0;jmain_keys[j] && a->main_values[j]) { ++ret; } } size_t esz = a->extra_sz; if(esz && a->extra_values && a->extra_keys) { for(j=0;jextra_keys[j]) && *(a->extra_values[j])) { ++ret; } } } } } return ret; } int lm_map_foreach(lm_map* map, foreachcb_type func) { size_t ret = 0; if(map) { size_t i; for(i=0;itable[i]); size_t j; for(j=0;jmain_keys[j] && a->main_values[j]) { if(func((ur_map_key_type)a->main_keys[j], (ur_map_value_type)a->main_values[j])) { return 1; } } } size_t esz = a->extra_sz; if(esz && a->extra_values && a->extra_keys) { for(j=0;jextra_keys[j]) && *(a->extra_values[j])) { if(func((ur_map_key_type)*(a->extra_keys[j]), (ur_map_value_type)*(a->extra_values[j]))) { return 1; } } } } } } return ret; } int lm_map_foreach_arg(lm_map* map, foreachcb_arg_type func, void* arg) { size_t ret = 0; if(map) { size_t i; for(i=0;itable[i]); size_t j; for(j=0;jmain_keys[j] && a->main_values[j]) { if(func((ur_map_key_type)a->main_keys[j], (ur_map_value_type)a->main_values[j], arg)) { return 1; } } } size_t esz = a->extra_sz; if(esz && a->extra_values && a->extra_keys) { for(j=0;jextra_keys[j]) && *(a->extra_values[j])) { if(func((ur_map_key_type)*(a->extra_keys[j]), (ur_map_value_type)*(a->extra_values[j]), arg)) { return 1; } } } } } } return ret; } //////////////////// ADDR LISTS /////////////////////////////////// static void addr_list_free(addr_list_header* slh) { if(slh) { if(slh->extra_list) { turn_free(slh->extra_list,sizeof(addr_elem)*(slh->extra_sz)); } ns_bzero(slh,sizeof(addr_list_header)); } } static void addr_list_add(addr_list_header* slh, const ioa_addr* key, ur_addr_map_value_type value) { if(!key || !value) return; addr_elem *elem = NULL; size_t i; for(i=0;imain_list[i].value)) { elem = &(slh->main_list[i]); break; } } if(!elem && slh->extra_list) { for(i=0;iextra_sz;++i) { if(!(slh->extra_list[i].value)) { elem = &(slh->extra_list[i]); break; } } } if(!elem) { size_t old_sz = slh->extra_sz; size_t old_sz_mem = old_sz * sizeof(addr_elem); slh->extra_list = (addr_elem*)turn_realloc(slh->extra_list, old_sz_mem, old_sz_mem + sizeof(addr_elem)); elem = &(slh->extra_list[old_sz]); slh->extra_sz += 1; } addr_cpy(&(elem->key),key); elem->value=value; } static void addr_list_remove(addr_list_header* slh, const ioa_addr* key, ur_addr_map_func delfunc, int *counter) { if(!slh || !key) return; if(counter) *counter = 0; size_t i; for(i=0;imain_list[i]); if(elem->value) { if(addr_eq(&(elem->key),key)) { if(delfunc && elem->value) delfunc(elem->value); elem->value = 0; if(counter) { *counter += 1; } } } } if(slh->extra_list) { for(i=0;iextra_sz;++i) { addr_elem *elem=&(slh->extra_list[i]); if(elem->value) { if(addr_eq(&(elem->key),key)) { if(delfunc && elem->value) delfunc(elem->value); elem->value = 0; if(counter) { *counter += 1; } } } } } } static void addr_list_foreach(addr_list_header* slh, ur_addr_map_func func) { if(slh && func) { size_t i; for(i=0;imain_list[i]); if(elem->value) { func(elem->value); } } if(slh->extra_list) { for(i=0;iextra_sz;++i) { addr_elem *elem=&(slh->extra_list[i]); if(elem->value) { func(elem->value); } } } } } static addr_elem* addr_list_get(addr_list_header* slh, const ioa_addr* key) { if(!slh || !key) return NULL; size_t i; for(i=0;imain_list[i]); if(elem->value) { if(addr_eq(&(elem->key),key)) { return elem; } } } if(slh->extra_list) { for(i=0;iextra_sz;++i) { addr_elem *elem=&(slh->extra_list[i]); if(elem->value) { if(addr_eq(&(elem->key),key)) { return elem; } } } } return NULL; } static const addr_elem* addr_list_get_const(const addr_list_header* slh, const ioa_addr* key) { if(!slh || !key) return NULL; size_t i; for(i=0;imain_list[i]); if(elem->value) { if(addr_eq(&(elem->key),key)) { return elem; } } } if(slh->extra_list) { for(i=0;iextra_sz;++i) { const addr_elem *elem=&(slh->extra_list[i]); if(elem->value) { if(addr_eq(&(elem->key),key)) { return elem; } } } } return NULL; } ////////// ADDR MAPS //////////////////////////////////////////// static int addr_map_index(ioa_addr* key) { u32bits hash = addr_hash(key); hash = hash & (ADDR_MAP_SIZE - 1); return (int)hash; } static addr_list_header* get_addr_list_header(ur_addr_map *map, ioa_addr* key) { return &(map->lists[addr_map_index(key)]); } static const addr_list_header* get_addr_list_header_const(const ur_addr_map *map, ioa_addr* key) { return &(map->lists[addr_map_index(key)]); } static int ur_addr_map_valid(const ur_addr_map *map) { return (map && map->magic==MAGIC_HASH); } void ur_addr_map_init(ur_addr_map* map) { if(map) { ns_bzero(map,sizeof(ur_addr_map)); map->magic=MAGIC_HASH; } } void ur_addr_map_clean(ur_addr_map* map) { if(map && ur_addr_map_valid(map)) { u32bits i=0; for(i=0;ilists[i])); } ns_bzero(map,sizeof(ur_addr_map)); } } /** * @ret: * 0 - success * -1 - error * if the addr key exists, the value is updated. */ int ur_addr_map_put(ur_addr_map* map, ioa_addr* key, ur_addr_map_value_type value) { if(!ur_addr_map_valid(map)) return -1; else { addr_list_header* slh = get_addr_list_header(map, key); addr_elem* elem = addr_list_get(slh, key); if(elem) { elem->value=value; } else { addr_list_add(slh,key,value); } return 0; } } /** * @ret: * 1 - success * 0 - not found */ int ur_addr_map_get(const ur_addr_map* map, ioa_addr* key, ur_addr_map_value_type *value) { if(!ur_addr_map_valid(map)) return 0; else { const addr_list_header* slh = get_addr_list_header_const(map, key); const addr_elem *elem = addr_list_get_const(slh, key); if(elem) { if(value) *value=elem->value; return 1; } return 0; } } /** * @ret: * 1 - success * 0 - not found */ int ur_addr_map_del(ur_addr_map* map, ioa_addr* key,ur_addr_map_func delfunc) { if(!ur_addr_map_valid(map)) return 0; else { addr_list_header* slh = get_addr_list_header(map, key); int counter=0; addr_list_remove(slh, key, delfunc, &counter); return (counter>0); } } /** * @ret: * 1 - success * 0 - not found */ void ur_addr_map_foreach(ur_addr_map* map, ur_addr_map_func func) { if(ur_addr_map_valid(map)) { u32bits i=0; for(i=0;ilists[i]); addr_list_foreach(slh, func); } } } //////////////////// STRING LISTS /////////////////////////////////// typedef struct _string_list { struct _string_list* next; } string_list; typedef struct _string_elem { string_list list; ur_string_map_key_type key; u32bits key_size; ur_string_map_value_type value; } string_elem; typedef struct _string_list_header { string_list *list; } string_list_header; static size_t string_list_size(const string_list *sl) { if(!sl) return 0; return 1+string_list_size(sl->next); } static void string_list_free(string_list_header* slh, ur_string_map_func del_value_func) { if(slh) { string_list* list=slh->list; while(list) { string_elem *elem=(string_elem*)list; string_list* tail=elem->list.next; if(elem->key) turn_free(elem->key,elem->key_size); if(del_value_func && elem->value) del_value_func(elem->value); turn_free(elem,sizeof(string_elem)); list=tail; } slh->list=NULL; } } static string_list* string_list_add(string_list* sl, const ur_string_map_key_type key, ur_string_map_value_type value) { if(!key) return sl; string_elem *elem=(string_elem*)turn_malloc(sizeof(string_elem)); elem->list.next=sl; elem->key_size = strlen(key)+1; elem->key=(s08bits*)turn_malloc(elem->key_size); ns_bcopy(key,elem->key,elem->key_size); elem->value=value; return &(elem->list); } static string_list* string_list_remove(string_list* sl, const ur_string_map_key_type key, ur_string_map_func del_value_func, int *counter) { if(!sl || !key) return sl; string_elem *elem=(string_elem*)sl; string_list* tail=elem->list.next; if(strcmp(elem->key,key)==0) { turn_free(elem->key,elem->key_size); if(del_value_func) del_value_func(elem->value); turn_free(elem,sizeof(string_elem)); if(counter) *counter+=1; sl=string_list_remove(tail, key, del_value_func, counter); } else { elem->list.next=string_list_remove(tail,key,del_value_func,counter); } return sl; } static string_elem* string_list_get(string_list* sl, const ur_string_map_key_type key) { if(!sl || !key) return NULL; string_elem *elem=(string_elem*)sl; if(strcmp(elem->key,key)==0) { return elem; } else { return string_list_get(elem->list.next, key); } } ////////// STRING MAPS //////////////////////////////////////////// #define STRING_MAP_SIZE (1024) struct _ur_string_map { string_list_header lists[STRING_MAP_SIZE]; u64bits magic; ur_string_map_func del_value_func; TURN_MUTEX_DECLARE(mutex) }; static unsigned long string_hash(const ur_string_map_key_type key) { u08bits *str=(u08bits*)key; unsigned long hash = 0; int c = 0; while ((c = *str++)) hash = c + (hash << 6) + (hash << 16) - hash; return hash; } static int string_map_index(const ur_string_map_key_type key) { return (int)(string_hash(key) % STRING_MAP_SIZE); } static string_list_header* get_string_list_header(ur_string_map *map, const ur_string_map_key_type key) { return &(map->lists[string_map_index(key)]); } static int ur_string_map_init(ur_string_map* map) { if(map) { ns_bzero(map,sizeof(ur_string_map)); map->magic=MAGIC_HASH; TURN_MUTEX_INIT_RECURSIVE(&(map->mutex)); return 0; } return -1; } static int ur_string_map_valid(const ur_string_map *map) { return (map && map->magic==MAGIC_HASH); } ur_string_map* ur_string_map_create(ur_string_map_func del_value_func) { ur_string_map *map=(ur_string_map*)turn_malloc(sizeof(ur_string_map)); if(ur_string_map_init(map)<0) { turn_free(map,sizeof(ur_string_map)); return NULL; } map->del_value_func = del_value_func; return map; } /** * @ret: * 0 - success * -1 - error * if the string key exists, and the value is different, return error. */ int ur_string_map_put(ur_string_map* map, const ur_string_map_key_type key, ur_string_map_value_type value) { if(!ur_string_map_valid(map)) return -1; else { string_list_header* slh = get_string_list_header(map, key); string_elem *elem = string_list_get(slh->list, key); if(elem) { if(elem->value != value) { if(map->del_value_func) map->del_value_func(elem->value); elem->value = value; } return 0; } slh->list=string_list_add(slh->list,key,value); return 0; } } /** * @ret: * 1 - success * 0 - not found */ int ur_string_map_get(ur_string_map* map, const ur_string_map_key_type key, ur_string_map_value_type *value) { if(!ur_string_map_valid(map)) return 0; else { string_list_header* slh = get_string_list_header(map, key); string_elem *elem = string_list_get(slh->list, key); if(elem) { if(value) *value=elem->value; return 1; } else { return 0; } } } /** * @ret: * 1 - success * 0 - not found */ int ur_string_map_del(ur_string_map* map, const ur_string_map_key_type key) { if(!ur_string_map_valid(map)) return 0; else { string_list_header* slh = get_string_list_header(map, key); int counter=0; slh->list=string_list_remove(slh->list, key, map->del_value_func, &counter); return (counter>0); } } void ur_string_map_clean(ur_string_map* map) { if (ur_string_map_valid(map)) { int i = 0; for (i = 0; i < STRING_MAP_SIZE; i++) { string_list_free(&(map->lists[i]), map->del_value_func); } } } void ur_string_map_free(ur_string_map** map) { if(map && ur_string_map_valid(*map)) { int i=0; for(i=0;ilists[i]),(*map)->del_value_func); } (*map)->magic=0; TURN_MUTEX_DESTROY(&((*map)->mutex)); turn_free(*map,sizeof(ur_string_map)); *map=NULL; } } size_t ur_string_map_size(const ur_string_map* map) { if(ur_string_map_valid(map)) { size_t ret=0; int i=0; for(i=0;ilists[i].list); } return ret; } else { return 0; } } int ur_string_map_lock(const ur_string_map* map) { if(ur_string_map_valid(map)) { TURN_MUTEX_LOCK((const turn_mutex*)&(map->mutex)); return 0; } return -1; } int ur_string_map_unlock(const ur_string_map* map) { if(ur_string_map_valid(map)) { TURN_MUTEX_UNLOCK((const turn_mutex*)&(map->mutex)); return 0; } return -1; } //////////////////////////////////////////////////////////////// turnserver-3.2.3.1/src/server/ns_turn_maps_rtcp.h000644 001751 001751 00000005063 12315706777 022046 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __TURN_RTCP_MAP__ #define __TURN_RTCP_MAP__ #include "ns_turn_maps.h" #include "ns_turn_ioalib.h" #ifdef __cplusplus extern "C" { #endif //////////////// RTCP MAP ////////////////// typedef ur_map_key_type rtcp_token_type; struct _rtcp_map; typedef struct _rtcp_map rtcp_map; //////////////////////////////////////////////// rtcp_map* rtcp_map_create(ioa_engine_handle e); /** * @ret: * 0 - success * -1 - error */ int rtcp_map_put(rtcp_map* map, rtcp_token_type key, ioa_socket_handle s); /** * @ret: * >=0 - success * <0 - not found */ ioa_socket_handle rtcp_map_get(const rtcp_map* map, rtcp_token_type token); /** * @ret: * 1 - success * 0 - not found */ int rtcp_map_del(rtcp_map* map, rtcp_token_type token); int rtcp_map_del_savefd(rtcp_map* map, rtcp_token_type token); /** * @ret: * 1 - success * 0 - not found */ void rtcp_map_free(rtcp_map** map); size_t rtcp_map_size(const rtcp_map* map); //////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_RTCP_MAP__ turnserver-3.2.3.1/src/server/ns_turn_allocation.h000644 001751 001751 00000015314 12315706777 022203 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __TURN_TURN_A_LIB__ #define __TURN_TURN_A_LIB__ #include "ns_turn_utils.h" #include "ns_turn_msg.h" #include "ns_turn_ioalib.h" #include "ns_turn_maps.h" #ifdef __cplusplus extern "C" { #endif ///////// Defines ////////// #define TCP_PEER_CONN_TIMEOUT (30) #define TCP_CONN_BIND_TIMEOUT (30) ///////// types //////////// enum _UR_STATE { UR_STATE_UNKNOWN=0, UR_STATE_READY, UR_STATE_DONE }; typedef enum _UR_STATE UR_STATE; ////////////// Network session //////////////// typedef struct { UR_STATE state; ioa_socket_handle s; int known_mtu; } ts_ur_session; static inline void clear_ts_ur_session_data(ts_ur_session* cdi) { if (cdi) IOA_CLOSE_SOCKET(cdi->s); } ////////// RFC 6062 TCP connection //////// #define MAX_UNSENT_BUFFER_SIZE (0x10) enum _TC_STATE { TC_STATE_UNKNOWN=0, TC_STATE_CLIENT_TO_PEER_CONNECTING, TC_STATE_PEER_CONNECTING, TC_STATE_PEER_CONNECTED, TC_STATE_READY, TC_STATE_FAILED }; typedef enum _TC_STATE TC_STATE; typedef u32bits tcp_connection_id; typedef struct { size_t sz; ioa_network_buffer_handle *bufs; } unsent_buffer; struct _tcp_connection { TC_STATE state; tcp_connection_id id; ioa_addr peer_addr; ioa_socket_handle client_s; ioa_socket_handle peer_s; ioa_timer_handle peer_conn_timeout; ioa_timer_handle conn_bind_timeout; stun_tid tid; void *owner; //a int done; unsent_buffer ub_to_client; }; typedef struct _tcp_connection_list { size_t sz; tcp_connection **elems; } tcp_connection_list; //////////////////////////////// #define TURN_PERMISSION_HASHTABLE_SIZE (0x8) #define TURN_PERMISSION_ARRAY_SIZE (0x3) struct _allocation; typedef struct _ch_info { u16bits chnum; int allocated; u16bits port; ioa_addr peer_addr; turn_time_t expiration_time; ioa_timer_handle lifetime_ev; void *owner; //perm } ch_info; ///////////// "channel" map ///////////////////// #define CH_MAP_HASH_SIZE (0x8) #define CH_MAP_ARRAY_SIZE (0x3) typedef struct _chn_map_array { ch_info main_chns[CH_MAP_ARRAY_SIZE]; size_t extra_sz; ch_info **extra_chns; } ch_map_array; typedef struct _ch_map { ch_map_array table[CH_MAP_HASH_SIZE]; } ch_map; ch_info *ch_map_get(ch_map* map, u16bits chnum, int new_chn); void ch_map_clean(ch_map* map); //////////////////////////// typedef struct _turn_permission_info { int allocated; lm_map chns; ioa_addr addr; turn_time_t expiration_time; ioa_timer_handle lifetime_ev; void* owner; //a } turn_permission_info; typedef struct _turn_permission_slot { turn_permission_info info; } turn_permission_slot; typedef struct _turn_permission_array { turn_permission_slot main_slots[TURN_PERMISSION_ARRAY_SIZE]; size_t extra_sz; turn_permission_slot **extra_slots; } turn_permission_array; typedef struct _turn_permission_hashtable { turn_permission_array table[TURN_PERMISSION_HASHTABLE_SIZE]; } turn_permission_hashtable; //////////////// ALLOCATION ////////////////////// typedef struct _allocation { int is_valid; stun_tid tid; turn_time_t expiration_time; ioa_timer_handle lifetime_ev; turn_permission_hashtable addr_to_perm; ts_ur_session relay_session; ch_map chns; /* chnum-to-ch_info* */ void *owner; //ss ur_map *tcp_connections; //global (per turn server) reference tcp_connection_list tcs; //local reference } allocation; //////////// CHANNELS //////////////////// u16bits get_turn_channel_number(turn_permission_info* tinfo, ioa_addr *addr); ch_info *get_turn_channel(turn_permission_info* tinfo, ioa_addr *addr); void turn_channel_delete(ch_info* chn); /////////// ALLOCATION //////////// void init_allocation(void *owner, allocation* a, ur_map *tcp_connections); void clear_allocation(allocation *a); void turn_permission_clean(turn_permission_info* tinfo); void set_allocation_lifetime_ev(allocation *a, turn_time_t exp_time, ioa_timer_handle ev); int is_allocation_valid(const allocation* a); void set_allocation_valid(allocation* a, int value); turn_permission_info* allocation_get_permission(allocation* a, const ioa_addr *addr); turn_permission_hashtable* allocation_get_turn_permission_hashtable(allocation *a); turn_permission_info* allocation_add_permission(allocation *a, const ioa_addr* addr); ch_info* allocation_get_new_ch_info(allocation* a, u16bits chnum, ioa_addr* peer_addr); ch_info* allocation_get_ch_info(allocation* a, u16bits chnum); ch_info* allocation_get_ch_info_by_peer_addr(allocation* a, ioa_addr* peer_addr); ts_ur_session *get_relay_session(allocation *a); ioa_socket_handle get_relay_socket(allocation *a); tcp_connection *get_and_clean_tcp_connection_by_id(ur_map *map, tcp_connection_id id); tcp_connection *get_tcp_connection_by_peer(allocation *a, ioa_addr *peer_addr); int can_accept_tcp_connection_from_peer(allocation *a, ioa_addr *peer_addr, int server_relay); tcp_connection *create_tcp_connection(u08bits server_id, allocation *a, stun_tid *tid, ioa_addr *peer_addr, int *err_code); void delete_tcp_connection(tcp_connection *tc); void clear_unsent_buffer(unsent_buffer *ub); void add_unsent_buffer(unsent_buffer *ub, ioa_network_buffer_handle nbh); ioa_network_buffer_handle top_unsent_buffer(unsent_buffer *ub); void pop_unsent_buffer(unsent_buffer *ub); /////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_TURN_A_LIB__ turnserver-3.2.3.1/src/client/ns_turn_msg_addr.h000644 001751 001751 00000004043 12315706777 021603 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __LIB_TURN_MSG_ADDR__ #define __LIB_TURN_MSG_ADDR__ #include "ns_turn_ioaddr.h" #ifdef __cplusplus extern "C" { #endif /////////////////////////////////////////// int stun_addr_encode(const ioa_addr* ca, u08bits *cfield, int *clen, int xor_ed, u32bits mc, const u08bits *tsx_id); int stun_addr_decode(ioa_addr* ca, const u08bits *cfield, int len, int xor_ed, u32bits mc, const u08bits *tsx_id); /////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__LIB_TURN_MSG_ADDR__ turnserver-3.2.3.1/src/client/ns_turn_msg_defs.h000644 001751 001751 00000013713 12315706777 021616 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __LIB_TURN_MSG_DEFS__ #define __LIB_TURN_MSG_DEFS__ /////////////////////////////////////////// // http://www.iana.org/assignments/stun-parameters/stun-parameters.xhtml /////////////////////////////////////////// #define STUN_HEADER_LENGTH (20) #define STUN_CHANNEL_HEADER_LENGTH (4) #define STUN_MAX_USERNAME_SIZE (513) #define STUN_MAX_REALM_SIZE (127) #define STUN_MAX_NONCE_SIZE (127) #define STUN_MAX_PWD_SIZE (127) #define STUN_MAGIC_COOKIE (0x2112A442) #define IS_STUN_REQUEST(msg_type) (((msg_type) & 0x0110) == 0x0000) #define IS_STUN_INDICATION(msg_type) (((msg_type) & 0x0110) == 0x0010) #define IS_STUN_SUCCESS_RESP(msg_type) (((msg_type) & 0x0110) == 0x0100) #define IS_STUN_ERR_RESP(msg_type) (((msg_type) & 0x0110) == 0x0110) #define GET_STUN_REQUEST(msg_type) (msg_type & 0xFEEF) #define GET_STUN_INDICATION(msg_type) ((msg_type & 0xFEEF)|0x0010) #define GET_STUN_SUCCESS_RESP(msg_type) ((msg_type & 0xFEEF)|0x0100) #define GET_STUN_ERR_RESP(msg_type) (msg_type | 0x0110) /* Lifetimes: */ #define STUN_DEFAULT_ALLOCATE_LIFETIME (600) #define STUN_MIN_ALLOCATE_LIFETIME STUN_DEFAULT_ALLOCATE_LIFETIME #define STUN_MAX_ALLOCATE_LIFETIME (3600) #define STUN_CHANNEL_LIFETIME (600) #define STUN_PERMISSION_LIFETIME (300) #define STUN_NONCE_EXPIRATION_TIME (600) /**/ #define STUN_METHOD_BINDING (0x0001) #define STUN_METHOD_ALLOCATE (0x0003) #define STUN_METHOD_REFRESH (0x0004) #define STUN_METHOD_SEND (0x0006) #define STUN_METHOD_DATA (0x0007) #define STUN_METHOD_CREATE_PERMISSION (0x0008) #define STUN_METHOD_CHANNEL_BIND (0x0009) /* RFC 6062 ==>>*/ #define STUN_METHOD_CONNECT (0x000a) #define STUN_METHOD_CONNECTION_BIND (0x000b) #define STUN_METHOD_CONNECTION_ATTEMPT (0x000c) /* <<== RFC 6062 */ #define STUN_ATTRIBUTE_MAPPED_ADDRESS (0x0001) #define OLD_STUN_ATTRIBUTE_RESPONSE_ADDRESS (0x0002) #define STUN_ATTRIBUTE_CHANGE_REQUEST (0x0003) #define OLD_STUN_ATTRIBUTE_SOURCE_ADDRESS (0x0004) #define OLD_STUN_ATTRIBUTE_CHANGED_ADDRESS (0x0005) #define STUN_ATTRIBUTE_USERNAME (0x0006) #define OLD_STUN_ATTRIBUTE_PASSWORD (0x0007) #define STUN_ATTRIBUTE_MESSAGE_INTEGRITY (0x0008) #define STUN_ATTRIBUTE_ERROR_CODE (0x0009) #define STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES (0x000A) #define OLD_STUN_ATTRIBUTE_REFLECTED_FROM (0x000B) #define STUN_ATTRIBUTE_REALM (0x0014) #define STUN_ATTRIBUTE_NONCE (0x0015) #define STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY (0x0017) #define STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS (0x0020) #define OLD_STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS (0x8020) #define STUN_ATTRIBUTE_SOFTWARE (0x8022) #define OLD_STUN_ATTRIBUTE_SERVER STUN_ATTRIBUTE_SOFTWARE #define STUN_ATTRIBUTE_ALTERNATE_SERVER (0x8023) #define STUN_ATTRIBUTE_FINGERPRINT (0x8028) #define STUN_ATTRIBUTE_CHANNEL_NUMBER (0x000C) #define STUN_ATTRIBUTE_LIFETIME (0x000D) #define STUN_ATTRIBUTE_BANDWIDTH (0x0010) #define STUN_ATTRIBUTE_XOR_PEER_ADDRESS (0x0012) #define STUN_ATTRIBUTE_DATA (0x0013) #define STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS (0x0016) #define STUN_ATTRIBUTE_EVEN_PORT (0x0018) #define STUN_ATTRIBUTE_REQUESTED_TRANSPORT (0x0019) #define STUN_ATTRIBUTE_DONT_FRAGMENT (0x001A) #define STUN_ATTRIBUTE_TIMER_VAL (0x0021) #define STUN_ATTRIBUTE_RESERVATION_TOKEN (0x0022) /* ICE */ #define STUN_ATTRIBUTE_PRIORITY (0x0024) #define STUN_ATTRIBUTE_ICE_CONTROLLED (0x8029) /* RFC 5780 */ #define STUN_ATTRIBUTE_PADDING (0x0026) #define STUN_ATTRIBUTE_RESPONSE_PORT (0x0027) #define STUN_ATTRIBUTE_RESPONSE_ORIGIN (0x802b) #define STUN_ATTRIBUTE_OTHER_ADDRESS (0x802c) /* RFC 6062 ==>> */ #define STUN_ATTRIBUTE_CONNECTION_ID (0x002a) /* <<== RFC 6062 */ #define STUN_VALID_CHANNEL(chn) ((chn)>=0x4000 && (chn)<=0x7FFF) ///////// values ////////////////// /* RFC 6156 ==>> */ #define STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4 (0x01) #define STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6 (0x02) #define STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT (0x00) #define STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_INVALID (-1) /* <<== RFC 6156 */ /* RFC 6062 ==>> */ #define STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE (6) #define STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE (17) #define STUN_ATTRIBUTE_TRANSPORT_TLS_VALUE (56) #define STUN_ATTRIBUTE_TRANSPORT_DTLS_VALUE (250) /* <<== RFC 6062 */ /* Mobility ==>> */ #define STUN_ATTRIBUTE_MOBILITY_TICKET (0x802E) #define STUN_ATTRIBUTE_MOBILITY_EVENT (0x802) #define STUN_ATTRIBUTE_MOBILITY_SUPPORT (0x8000) /* <<== Mobility */ //////////////////////////////////////////////// #endif //__LIB_TURN_MSG_DEFS__ turnserver-3.2.3.1/src/client/ns_turn_ioaddr.c000644 001751 001751 00000030453 12315706777 021264 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "ns_turn_ioaddr.h" ////////////////////////////////////////////////////////////// u32bits get_ioa_addr_len(const ioa_addr* addr) { if(addr->ss.sa_family == AF_INET) return sizeof(struct sockaddr_in); else if(addr->ss.sa_family == AF_INET6) return sizeof(struct sockaddr_in6); return 0; } /////////////////////////////////////////////////////////////// void addr_set_any(ioa_addr *addr) { if(addr) ns_bzero(addr,sizeof(ioa_addr)); } int addr_any(const ioa_addr* addr) { if(!addr) return 1; if(addr->ss.sa_family == AF_INET) { return ((addr->s4.sin_addr.s_addr==0)&&(addr->s4.sin_port==0)); } else if(addr->ss.sa_family == AF_INET6) { if(addr->s6.sin6_port!=0) return 0; else { size_t i; for(i=0;is6.sin6_addr);i++) if(((const s08bits*)&(addr->s6.sin6_addr))[i]) return 0; } } return 1; } int addr_any_no_port(const ioa_addr* addr) { if(!addr) return 1; if(addr->ss.sa_family == AF_INET) { return (addr->s4.sin_addr.s_addr==0); } else if(addr->ss.sa_family == AF_INET6) { size_t i; for(i=0;is6.sin6_addr);i++) if(((const s08bits*)(&(addr->s6.sin6_addr)))[i]) return 0; } return 1; } u32bits hash_int32(u32bits a) { a = a ^ (a>>4); a = (a^0xdeadbeef) + (a<<5); a = a ^ (a>>11); return a; } u64bits hash_int64(u64bits a) { a = a ^ (a>>4); a = (a^0xdeadbeef) + (a<<5); a = a ^ (a>>11); return a; } u32bits addr_hash(const ioa_addr *addr) { if(!addr) return 0; u32bits ret = 0; if (addr->ss.sa_family == AF_INET) { ret = hash_int32(addr->s4.sin_addr.s_addr + addr->s4.sin_port); } else { const u64bits *a = (const u64bits *) (&(addr->s6.sin6_addr)); ret = (u32bits)((hash_int64(a[0])<<3) + (hash_int64(a[1] + addr->s6.sin6_port))); } return ret; } u32bits addr_hash_no_port(const ioa_addr *addr) { if(!addr) return 0; u32bits ret = 0; if (addr->ss.sa_family == AF_INET) { ret = hash_int32(addr->s4.sin_addr.s_addr); } else { const u64bits *a = (const u64bits *) (&(addr->s6.sin6_addr)); ret = (u32bits)((hash_int64(a[0])<<3) + (hash_int64(a[1]))); } return ret; } void addr_cpy(ioa_addr* dst, const ioa_addr* src) { if(dst && src) ns_bcopy(src,dst,sizeof(ioa_addr)); } void addr_cpy4(ioa_addr* dst, const struct sockaddr_in* src) { if(src && dst) ns_bcopy(src,dst,sizeof(struct sockaddr_in)); } void addr_cpy6(ioa_addr* dst, const struct sockaddr_in6* src) { if(src && dst) ns_bcopy(src,dst,sizeof(struct sockaddr_in6)); } int addr_eq(const ioa_addr* a1, const ioa_addr *a2) { if(!a1) return (!a2); if(a1->ss.sa_family == a2->ss.sa_family) { if(a1->ss.sa_family == AF_INET) { if((int)a1->s4.sin_addr.s_addr == (int)a2->s4.sin_addr.s_addr && a1->s4.sin_port == a2->s4.sin_port) { return 1; } } else if(a1->ss.sa_family == AF_INET6) { const u64bits *p1=(const u64bits *)(&(a1->s6.sin6_addr)); const u64bits *p2=(const u64bits *)(&(a2->s6.sin6_addr)); if(p1[0]==p2[0] && p1[1]==p2[1] && a1->s6.sin6_port == a2->s6.sin6_port) { return 1; } } } return 0; } int addr_eq_no_port(const ioa_addr* a1, const ioa_addr *a2) { if(!a1) return (!a2); if(a1->ss.sa_family == a2->ss.sa_family) { if(a1->ss.sa_family == AF_INET) { if((int)a1->s4.sin_addr.s_addr == (int)a2->s4.sin_addr.s_addr) { return 1; } } else if(a1->ss.sa_family == AF_INET6) { const u64bits *p1=(const u64bits *)(&(a1->s6.sin6_addr)); const u64bits *p2=(const u64bits *)(&(a2->s6.sin6_addr)); if(p1[0]==p2[0] && p1[1]==p2[1]) { return 1; } } } return 0; } int make_ioa_addr(const u08bits* saddr, int port, ioa_addr *addr) { if(!saddr || !addr) return -1; ns_bzero(addr, sizeof(ioa_addr)); if((strlen((const s08bits*)saddr) == 0)|| (inet_pton(AF_INET, (const s08bits*)saddr, &addr->s4.sin_addr) == 1)) { addr->s4.sin_family = AF_INET; #if defined(TURN_HAS_SIN_LEN) /* tested when configured */ addr->s4.sin_len = sizeof(struct sockaddr_in); #endif addr->s4.sin_port = nswap16(port); } else if (inet_pton(AF_INET6, (const s08bits*)saddr, &addr->s6.sin6_addr) == 1) { addr->s6.sin6_family = AF_INET6; #if defined(SIN6_LEN) /* this define is required by IPv6 if used */ addr->s6.sin6_len = sizeof(struct sockaddr_in6); #endif addr->s6.sin6_port = nswap16(port); } else { return -1; } return 0; } static char* get_addr_string_and_port(char* s0, int *port) { char *s = s0; while(*s && (*s==' ')) ++s; if(*s == '[') { ++s; char *tail = strstr(s,"]"); if(tail) { *tail=0; ++tail; while(*tail && (*tail==' ')) ++tail; if(*tail==':') { ++tail; *port = atoi(tail); return s; } else if(*tail == 0) { *port = 0; return s; } } } else { char *tail = strstr(s,":"); if(tail) { *tail = 0; ++tail; *port = atoi(tail); return s; } else { *port = 0; return s; } } return NULL; } int make_ioa_addr_from_full_string(const u08bits* saddr, int default_port, ioa_addr *addr) { if(!addr) return -1; int ret = -1; int port = 0; char* s = strdup((const char*)saddr); char *sa = get_addr_string_and_port(s,&port); if(sa) { if(port<1) port = default_port; ret = make_ioa_addr((u08bits*)sa,port,addr); } turn_free(s,strlen(s)+1); return ret; } int addr_to_string(const ioa_addr* addr, u08bits* saddr) { if (addr && saddr) { s08bits addrtmp[MAX_IOA_ADDR_STRING]; if (addr->ss.sa_family == AF_INET) { inet_ntop(AF_INET, &addr->s4.sin_addr, addrtmp, INET_ADDRSTRLEN); if(addr_get_port(addr)>0) snprintf((s08bits*)saddr, MAX_IOA_ADDR_STRING, "%s:%d", addrtmp, addr_get_port(addr)); else strncpy((s08bits*)saddr, addrtmp, MAX_IOA_ADDR_STRING); } else if (addr->ss.sa_family == AF_INET6) { inet_ntop(AF_INET6, &addr->s6.sin6_addr, addrtmp, INET6_ADDRSTRLEN); if(addr_get_port(addr)>0) snprintf((s08bits*)saddr, MAX_IOA_ADDR_STRING, "[%s]:%d", addrtmp, addr_get_port(addr)); else strncpy((s08bits*)saddr, addrtmp, MAX_IOA_ADDR_STRING); } else { return -1; } return 0; } return -1; } int addr_to_string_no_port(const ioa_addr* addr, u08bits* saddr) { if (addr && saddr) { s08bits addrtmp[MAX_IOA_ADDR_STRING]; if (addr->ss.sa_family == AF_INET) { inet_ntop(AF_INET, &addr->s4.sin_addr, addrtmp, INET_ADDRSTRLEN); strncpy((s08bits*)saddr, addrtmp, MAX_IOA_ADDR_STRING); } else if (addr->ss.sa_family == AF_INET6) { inet_ntop(AF_INET6, &addr->s6.sin6_addr, addrtmp, INET6_ADDRSTRLEN); strncpy((s08bits*)saddr, addrtmp, MAX_IOA_ADDR_STRING); } else { return -1; } return 0; } return -1; } void addr_set_port(ioa_addr* addr, int port) { if(addr) { if(addr->s4.sin_family == AF_INET) { addr->s4.sin_port = nswap16(port); } else if(addr->s6.sin6_family == AF_INET6) { addr->s6.sin6_port = nswap16(port); } } } int addr_get_port(const ioa_addr* addr) { if(!addr) return 0; if(addr->s4.sin_family == AF_INET) { return nswap16(addr->s4.sin_port); } else if(addr->s6.sin6_family == AF_INET6) { return nswap16(addr->s6.sin6_port); } return 0; } ///////////////////////////////////////////////////////////////////////////// void ioa_addr_range_set(ioa_addr_range* range, const ioa_addr* addr_min, const ioa_addr* addr_max) { if(range) { if(addr_min) addr_cpy(&(range->min),addr_min); else addr_set_any(&(range->min)); if(addr_max) addr_cpy(&(range->max),addr_max); else addr_set_any(&(range->max)); } } int addr_less_eq(const ioa_addr* addr1, const ioa_addr* addr2) { if(!addr1) return 1; else if(!addr2) return 0; else { if(addr1->ss.sa_family != addr2->ss.sa_family) return (addr1->ss.sa_family < addr2->ss.sa_family); else if(addr1->ss.sa_family == AF_INET) { return ((u32bits)nswap32(addr1->s4.sin_addr.s_addr) <= (u32bits)nswap32(addr2->s4.sin_addr.s_addr)); } else if(addr1->ss.sa_family == AF_INET6) { int i; for(i=0;i<16;i++) { if((u08bits)(((const s08bits*)&(addr1->s6.sin6_addr))[i]) > (u08bits)(((const s08bits*)&(addr2->s6.sin6_addr))[i])) return 0; } return 1; } else return 1; } } int ioa_addr_in_range(const ioa_addr_range* range, const ioa_addr* addr) { if(range && addr) { if(addr_any(&(range->min)) || addr_less_eq(&(range->min),addr)) { if(addr_any(&(range->max))) { return 1; } else { return addr_less_eq(addr,&(range->max)); } } } return 0; } void ioa_addr_range_cpy(ioa_addr_range* dest, const ioa_addr_range* src) { if(dest && src) { addr_cpy(&(dest->min),&(src->min)); addr_cpy(&(dest->max),&(src->max)); } } /////// Check whether this is a good address ////////////// int ioa_addr_is_multicast(ioa_addr *addr) { if(addr) { if(addr->ss.sa_family == AF_INET) { const u08bits *u = ((const u08bits*)&(addr->s4.sin_addr)); return (u[0] > 223); } else if(addr->ss.sa_family == AF_INET6) { u08bits u = ((const u08bits*)&(addr->s6.sin6_addr))[0]; return (u == 255); } } return 0; } int ioa_addr_is_loopback(ioa_addr *addr) { if(addr) { if(addr->ss.sa_family == AF_INET) { const u08bits *u = ((const u08bits*)&(addr->s4.sin_addr)); return (u[0] == 127); } else if(addr->ss.sa_family == AF_INET6) { const u08bits *u = ((const u08bits*)&(addr->s6.sin6_addr)); if(u[7] == 1) { int i; for(i=0;i<7;++i) { if(u[i]) return 0; } return 1; } } } return 0; } /////// Map "public" address to "private" address ////////////// // Must be called only in a single-threaded context, // before the program starts spawning threads: static ioa_addr **public_addrs = NULL; static ioa_addr **private_addrs = NULL; static size_t mcount = 0; static size_t msz = 0; void ioa_addr_add_mapping(ioa_addr *apub, ioa_addr *apriv) { size_t new_size = msz + sizeof(ioa_addr*); public_addrs = (ioa_addr**)turn_realloc(public_addrs, msz, new_size); private_addrs = (ioa_addr**)turn_realloc(private_addrs, msz, new_size); public_addrs[mcount]=(ioa_addr*)turn_malloc(sizeof(ioa_addr)); private_addrs[mcount]=(ioa_addr*)turn_malloc(sizeof(ioa_addr)); addr_cpy(public_addrs[mcount],apub); addr_cpy(private_addrs[mcount],apriv); ++mcount; msz += sizeof(ioa_addr*); } void map_addr_from_public_to_private(const ioa_addr *public_addr, ioa_addr *private_addr) { size_t i; for(i=0;iss.sa_family == AF_INET || ca->ss.sa_family==0) { /* IPv4 address */ *clen=8; cfield[0]=0; cfield[1]=1; //IPv4 family if (xor_ed) { /* Port */ ((u16bits*)cfield)[1] = (ca->s4.sin_port) ^ nswap16(mc >> 16); /* Address */ ((u32bits*)cfield)[1] = (ca->s4.sin_addr.s_addr) ^ nswap32(mc); } else { /* Port */ ((u16bits*)cfield)[1]=ca->s4.sin_port; /* Address */ ((u32bits*)cfield)[1]=ca->s4.sin_addr.s_addr; } } else if (ca->ss.sa_family == AF_INET6) { /* IPv6 address */ *clen=20; cfield[0]=0; cfield[1]=2; //IPv6 family if (xor_ed) { unsigned int i; u08bits *dst = ((u08bits*)cfield)+4; const u08bits *src = (const u08bits*)&(ca->s6.sin6_addr); u32bits magic = nswap32(mc); /* Port */ ((u16bits*)cfield)[1] = ca->s6.sin6_port ^ nswap16(mc >> 16); /* Address */ for (i=0; i<4; ++i) { dst[i] = (u08bits)(src[i] ^ ((const u08bits*)&magic)[i]); } for (i=0; i<12; ++i) { dst[i+4] = (u08bits)(src[i+4] ^ tsx_id[i]); } } else { /* Port */ ((u16bits*)cfield)[1]=ca->s6.sin6_port; /* Address */ ns_bcopy(&ca->s6.sin6_addr, ((u08bits*)cfield)+4, 16); } } else { return -1; } return 0; } int stun_addr_decode(ioa_addr* ca, const u08bits *cfield, int len, int xor_ed, u32bits mc, const u08bits *tsx_id) { if(!cfield || !len || !ca || !tsx_id || (len<8)) return -1; if(cfield[0]!=0) { return -1; } int sa_family; if(cfield[1]==1) sa_family=AF_INET; else if(cfield[1]==2) sa_family=AF_INET6; else return -1; ca->ss.sa_family=sa_family; if (sa_family == AF_INET) { if(len!=8) return -1; /* IPv4 address */ /* Port */ ca->s4.sin_port=((const u16bits*)cfield)[1]; /* Address */ ca->s4.sin_addr.s_addr=((const u32bits*)cfield)[1]; if (xor_ed) { ca->s4.sin_port ^= nswap16(mc >> 16); ca->s4.sin_addr.s_addr ^= nswap32(mc); } } else if (sa_family == AF_INET6) { /* IPv6 address */ if(len!=20) return -1; /* Port */ ca->s6.sin6_port = ((const u16bits*)cfield)[1]; /* Address */ ns_bcopy(((const u08bits*)cfield)+4, &ca->s6.sin6_addr, 16); if (xor_ed) { unsigned int i; u08bits *dst; const u08bits *src; u32bits magic = nswap32(mc); /* Port */ ca->s6.sin6_port ^= nswap16(mc >> 16); /* Address */ src = ((const u08bits*)cfield)+4; dst = (u08bits*)&ca->s6.sin6_addr; for (i=0; i<4; ++i) { dst[i] = (u08bits)(src[i] ^ ((const u08bits*)&magic)[i]); } for (i=0; i<12; ++i) { dst[i+4] = (u08bits)(src[i+4] ^ tsx_id[i]); } } } else { return -1; } return 0; } ////////////////////////////////////////////////////////////////////////////// turnserver-3.2.3.1/src/client/ns_turn_ioaddr.h000644 001751 001751 00000007613 12315706777 021273 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #ifndef __IOADDR__ #define __IOADDR__ #include "ns_turn_defs.h" #ifdef __cplusplus extern "C" { #endif ///////////////////////////////////////////////////// #define MAX_IOA_ADDR_STRING (65) typedef union { struct sockaddr ss; struct sockaddr_in s4; struct sockaddr_in6 s6; } ioa_addr; typedef struct { ioa_addr min; ioa_addr max; } ioa_addr_range; //////////////////////////// u32bits get_ioa_addr_len(const ioa_addr* addr); //////////////////////////// void addr_set_any(ioa_addr *addr); int addr_any(const ioa_addr* addr); int addr_any_no_port(const ioa_addr* addr); u32bits addr_hash(const ioa_addr *addr); u32bits addr_hash_no_port(const ioa_addr *addr); void addr_cpy(ioa_addr* dst, const ioa_addr* src); void addr_cpy4(ioa_addr* dst, const struct sockaddr_in* src); void addr_cpy6(ioa_addr* dst, const struct sockaddr_in6* src); int addr_eq(const ioa_addr* a1, const ioa_addr *a2); int addr_eq_no_port(const ioa_addr* a1, const ioa_addr *a2); int make_ioa_addr(const u08bits* saddr, int port, ioa_addr *addr); int make_ioa_addr_from_full_string(const u08bits* saddr, int default_port, ioa_addr *addr); void addr_set_port(ioa_addr* addr, int port); int addr_get_port(const ioa_addr* addr); int addr_to_string(const ioa_addr* addr, u08bits* saddr); int addr_to_string_no_port(const ioa_addr* addr, u08bits* saddr); u32bits hash_int32(u32bits a); u64bits hash_int64(u64bits a); /////////////////////////////////////////// void ioa_addr_range_set(ioa_addr_range* range, const ioa_addr* addr_min, const ioa_addr* addr_max); int addr_less_eq(const ioa_addr* addr1, const ioa_addr* addr2); int ioa_addr_in_range(const ioa_addr_range* range, const ioa_addr* addr); void ioa_addr_range_cpy(ioa_addr_range* dest, const ioa_addr_range* src); /////// Check whether this is a good address ////////////// int ioa_addr_is_multicast(ioa_addr *a); int ioa_addr_is_loopback(ioa_addr *addr); /////// Map "public" address to "private" address ////////////// // Must be called only in a single-threaded context, // before the program starts spawning threads: void ioa_addr_add_mapping(ioa_addr *apub, ioa_addr *apriv); void map_addr_from_public_to_private(const ioa_addr *public_addr, ioa_addr *private_addr); void map_addr_from_private_to_public(const ioa_addr *private_addr, ioa_addr *public_addr); /////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__IOADDR__ turnserver-3.2.3.1/src/client/ns_turn_msg.c000644 001751 001751 00000123312 12315706777 020605 0ustar00olegoleg000000 000000 /* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include "ns_turn_msg.h" #include "ns_turn_msg_addr.h" ///////////// Security functions implementation from ns_turn_msg.h /////////// #include #include #include #include #include #include /////////// long turn_random(void) { long ret = 0; if(!RAND_bytes((unsigned char *)&ret,sizeof(ret))) ret = random(); return ret; } void turn_random32_size(u32bits *ar, size_t sz) { if(!RAND_bytes((unsigned char *)ar, sz<<2)<0) { size_t i; for(i=0;i>1) | ((tt & 0x0E00)>>2) | ((tt & 0x3000)>>2); } u16bits stun_get_msg_type_str(const u08bits *buf, size_t len) { if(!buf || len<2) return (u16bits)-1; return ((nswap16(((const u16bits*)buf)[0])) & 0x3FFF); } int is_channel_msg_str(const u08bits* buf, size_t blen) { return (buf && blen>=4 && STUN_VALID_CHANNEL(nswap16(((const u16bits*)buf)[0]))); } /////////////// message types ///////////////////////////////// int stun_is_command_message_str(const u08bits* buf, size_t blen) { if (buf && blen >= STUN_HEADER_LENGTH) { if (!STUN_VALID_CHANNEL(nswap16(((const u16bits*)buf)[0]))) { if ((((u08bits) buf[0]) & ((u08bits) (0xC0))) == 0) { if (nswap32(((const u32bits*)(buf))[1]) == STUN_MAGIC_COOKIE) { u16bits len = nswap16(((const u16bits*)(buf))[1]); if ((len & 0x0003) == 0) { if ((size_t) (len + STUN_HEADER_LENGTH) == blen) { return 1; } } } } } } return 0; } int old_stun_is_command_message_str(const u08bits* buf, size_t blen, u32bits *cookie) { if (buf && blen >= STUN_HEADER_LENGTH) { if (!STUN_VALID_CHANNEL(nswap16(((const u16bits*)buf)[0]))) { if ((((u08bits) buf[0]) & ((u08bits) (0xC0))) == 0) { if (nswap32(((const u32bits*)(buf))[1]) != STUN_MAGIC_COOKIE) { u16bits len = nswap16(((const u16bits*)(buf))[1]); if ((len & 0x0003) == 0) { if ((size_t) (len + STUN_HEADER_LENGTH) == blen) { *cookie = nswap32(((const u32bits*)(buf))[1]); return 1; } } } } } } return 0; } int stun_is_command_message_full_check_str(const u08bits* buf, size_t blen, int must_check_fingerprint, int *fingerprint_present) { if(!stun_is_command_message_str(buf,blen)) return 0; stun_attr_ref sar = stun_attr_get_first_by_type_str(buf, blen, STUN_ATTRIBUTE_FINGERPRINT); if(!sar) { if(fingerprint_present) *fingerprint_present = 0; if(stun_get_method_str(buf,blen) == STUN_METHOD_BINDING) { return 1; } return !must_check_fingerprint; } if(stun_attr_get_len(sar) != 4) return 0; const u32bits* fingerprint = (const u32bits*)stun_attr_get_value(sar); if(!fingerprint) return !must_check_fingerprint; u32bits crc32len = (u32bits)((((const u08bits*)fingerprint)-buf)-4); int ret = (*fingerprint == nswap32(ns_crc32(buf,crc32len) ^ ((u32bits)0x5354554e))); if(ret && fingerprint_present) *fingerprint_present = ret; return ret; } int stun_is_command_message_offset_str(const u08bits* buf, size_t blen, int offset) { return stun_is_command_message_str(buf + offset, blen); } int stun_is_request_str(const u08bits* buf, size_t len) { if(is_channel_msg_str(buf,len)) return 0; return IS_STUN_REQUEST(stun_get_msg_type_str(buf,len)); } int stun_is_success_response_str(const u08bits* buf, size_t len) { if(is_channel_msg_str(buf,len)) return 0; return IS_STUN_SUCCESS_RESP(stun_get_msg_type_str(buf,len)); } int stun_is_error_response_str(const u08bits* buf, size_t len, int *err_code, u08bits *err_msg, size_t err_msg_size) { if(is_channel_msg_str(buf,len)) return 0; if(IS_STUN_ERR_RESP(stun_get_msg_type_str(buf,len))) { if(err_code) { stun_attr_ref sar = stun_attr_get_first_by_type_str(buf, len, STUN_ATTRIBUTE_ERROR_CODE); if(sar) { if(stun_attr_get_len(sar)>=4) { const u08bits* val = (const u08bits*)stun_attr_get_value(sar); *err_code=(int)(val[2]*100 + val[3]); if(err_msg && err_msg_size>0) { err_msg[0]=0; if(stun_attr_get_len(sar)>4) { size_t msg_len = stun_attr_get_len(sar) - 4; if(msg_len>(err_msg_size-1)) msg_len=err_msg_size - 1; ns_bcopy(val+4, err_msg, msg_len); err_msg[msg_len]=0; } } } } } return 1; } return 0; } int stun_is_challenge_response_str(const u08bits* buf, size_t len, int *err_code, u08bits *err_msg, size_t err_msg_size, u08bits *realm, u08bits *nonce) { int ret = stun_is_error_response_str(buf, len, err_code, err_msg, err_msg_size); if(ret && (((*err_code) == 401) || ((*err_code) == 438) || ((*err_code) == SHA_TOO_WEAK))) { stun_attr_ref sar = stun_attr_get_first_by_type_str(buf,len,STUN_ATTRIBUTE_REALM); if(sar) { const u08bits *value = stun_attr_get_value(sar); if(value) { size_t vlen = (size_t)stun_attr_get_len(sar); ns_bcopy(value,realm,vlen); realm[vlen]=0; sar = stun_attr_get_first_by_type_str(buf,len,STUN_ATTRIBUTE_NONCE); if(sar) { value = stun_attr_get_value(sar); if(value) { vlen = (size_t)stun_attr_get_len(sar); ns_bcopy(value,nonce,vlen); nonce[vlen]=0; return 1; } } } } } return 0; } int stun_is_response_str(const u08bits* buf, size_t len) { if(is_channel_msg_str(buf,len)) return 0; if(IS_STUN_SUCCESS_RESP(stun_get_msg_type_str(buf,len))) return 1; if(IS_STUN_ERR_RESP(stun_get_msg_type_str(buf,len))) return 1; return 0; } int stun_is_indication_str(const u08bits* buf, size_t len) { if(is_channel_msg_str(buf,len)) return 0; return IS_STUN_INDICATION(stun_get_msg_type_str(buf,len)); } u16bits stun_make_request(u16bits method) { return GET_STUN_REQUEST(stun_make_type(method)); } u16bits stun_make_indication(u16bits method) { return GET_STUN_INDICATION(stun_make_type(method)); } u16bits stun_make_success_response(u16bits method) { return GET_STUN_SUCCESS_RESP(stun_make_type(method)); } u16bits stun_make_error_response(u16bits method) { return GET_STUN_ERR_RESP(stun_make_type(method)); } //////////////// INIT //////////////////////////////////////////// void stun_init_buffer_str(u08bits *buf, size_t *len) { *len=STUN_HEADER_LENGTH; ns_bzero(buf,*len); } void stun_init_command_str(u16bits message_type, u08bits* buf, size_t *len) { stun_init_buffer_str(buf,len); message_type &= (u16bits)(0x3FFF); ((u16bits*)buf)[0]=nswap16(message_type); ((u16bits*)buf)[1]=0; ((u32bits*)buf)[1]=nswap32(STUN_MAGIC_COOKIE); stun_tid_generate_in_message_str(buf,NULL); } void old_stun_init_command_str(u16bits message_type, u08bits* buf, size_t *len, u32bits cookie) { stun_init_buffer_str(buf,len); message_type &= (u16bits)(0x3FFF); ((u16bits*)buf)[0]=nswap16(message_type); ((u16bits*)buf)[1]=0; ((u32bits*)buf)[1]=nswap32(cookie); stun_tid_generate_in_message_str(buf,NULL); } void stun_init_request_str(u16bits method, u08bits* buf, size_t *len) { stun_init_command_str(stun_make_request(method), buf, len); } void stun_init_indication_str(u16bits method, u08bits* buf, size_t *len) { stun_init_command_str(stun_make_indication(method), buf, len); } void stun_init_success_response_str(u16bits method, u08bits* buf, size_t *len, stun_tid* id) { stun_init_command_str(stun_make_success_response(method), buf, len); if(id) { stun_tid_message_cpy(buf, id); } } void old_stun_init_success_response_str(u16bits method, u08bits* buf, size_t *len, stun_tid* id, u32bits cookie) { old_stun_init_command_str(stun_make_success_response(method), buf, len, cookie); if(id) { stun_tid_message_cpy(buf, id); } } static void stun_init_error_response_common_str(u08bits* buf, size_t *len, u16bits error_code, const u08bits *reason, stun_tid* id) { if (!reason) { switch (error_code){ case 300: reason = (const u08bits *) "Try Alternate"; break; case 400: reason = (const u08bits *) "Bad Request"; break; case 401: reason = (const u08bits *) "Unauthorized"; break; case 404: reason = (const u08bits *) "Not Found"; break; case 420: reason = (const u08bits *) "Unknown Attribute"; break; case 438: reason = (const u08bits *) "Stale Nonce"; break; case 500: reason = (const u08bits *) "Server Error"; break; default: reason = (const u08bits *) "Unknown Error"; break; }; } u08bits avalue[513]; avalue[0] = 0; avalue[1] = 0; avalue[2] = (u08bits) (error_code / 100); avalue[3] = (u08bits) (error_code % 100); strncpy((s08bits*) (avalue + 4), (const s08bits*) reason, sizeof(avalue)-4); avalue[sizeof(avalue)-1]=0; int alen = 4 + strlen((const s08bits*) (avalue+4)); //"Manual" padding for compatibility with classic old stun: { int rem = alen % 4; if(rem) { alen +=(4-rem); } } stun_attr_add_str(buf, len, STUN_ATTRIBUTE_ERROR_CODE, (u08bits*) avalue, alen); if (id) { stun_tid_message_cpy(buf, id); } } void old_stun_init_error_response_str(u16bits method, u08bits* buf, size_t *len, u16bits error_code, const u08bits *reason, stun_tid* id, u32bits cookie) { old_stun_init_command_str(stun_make_error_response(method), buf, len, cookie); stun_init_error_response_common_str(buf, len, error_code, reason, id); } void stun_init_error_response_str(u16bits method, u08bits* buf, size_t *len, u16bits error_code, const u08bits *reason, stun_tid* id) { stun_init_command_str(stun_make_error_response(method), buf, len); stun_init_error_response_common_str(buf, len, error_code, reason, id); } /////////// CHANNEL //////////////////////////////////////////////// int stun_init_channel_message_str(u16bits chnumber, u08bits* buf, size_t *len, int length, int do_padding) { u16bits rlen = (u16bits)length; if(length<0 || (MAX_STUN_MESSAGE_SIZE<(4+length))) return -1; ((u16bits*)(buf))[0]=nswap16(chnumber); ((u16bits*)(buf))[1]=nswap16((u16bits)length); if(do_padding && (rlen & 0x0003)) rlen = ((rlen>>2)+1)<<2; *len=4+rlen; return 0; } int stun_is_channel_message_str(const u08bits *buf, size_t *blen, u16bits* chnumber, int mandatory_padding) { u16bits datalen_header; u16bits datalen_actual; if (!blen || (*blen < 4)) return 0; u16bits chn = nswap16(((const u16bits*)(buf))[0]); if (!STUN_VALID_CHANNEL(chn)) return 0; if(*blen>(u16bits)-1) *blen=(u16bits)-1; datalen_actual = (u16bits)(*blen) - 4; datalen_header = ((const u16bits*)buf)[1]; datalen_header = nswap16(datalen_header); if (datalen_header > datalen_actual) return 0; if (datalen_header != datalen_actual) { /* maybe there are padding bytes for 32-bit alignment. Mandatory for TCP. Optional for UDP */ if(datalen_actual & 0x0003) { if(mandatory_padding) { return 0; } else if ((datalen_actual < datalen_header) || (datalen_header == 0)) { return 0; } else { u16bits diff = datalen_actual - datalen_header; if (diff > 3) return 0; } } } *blen = datalen_header + 4; if (chnumber) *chnumber = chn; return 1; } ////////// STUN message /////////////////////////////// static inline int sheadof(const char *head, const char* full) { while(*head) { if(*head != *full) return 0; ++head;++full; } return 1; } static inline const char* findstr(const char *hay, size_t slen, const char *needle) { const char *ret = NULL; if(hay && slen && needle) { size_t nlen=strlen(needle); if(nlen<=slen) { size_t smax = slen-nlen+1; size_t i; const char *sp = hay; for(i=0;i=12) { if((s[0]=='G')&&(s[1]=='E')&&(s[2]=='T')&&(s[3]==' ')) { const char *sp=findstr(s+4,blen-4,"HTTP"); if(sp) { sp += 4; size_t diff_blen = sp-s; if(diff_blen+4 <= blen) { sp=findstr(sp,blen-diff_blen,"\r\n\r\n"); if(sp) { return (int)(sp-s+4); } } } } } return 0; } int is_http_get(const char *s, size_t blen) { return is_http_get_inline(s, blen); } int stun_get_message_len_str(u08bits *buf, size_t blen, int padding, size_t *app_len) { if (buf && blen) { /* STUN request/response ? */ if (buf && blen >= STUN_HEADER_LENGTH) { if (!STUN_VALID_CHANNEL(nswap16(((const u16bits*)buf)[0]))) { if ((((u08bits) buf[0]) & ((u08bits) (0xC0))) == 0) { if (nswap32(((const u32bits*)(buf))[1]) == STUN_MAGIC_COOKIE) { u16bits len = nswap16(((const u16bits*)(buf))[1]); if ((len & 0x0003) == 0) { len += STUN_HEADER_LENGTH; if ((size_t) len <= blen) { *app_len = (size_t)len; return (int)len; } } } } } } //HTTP request ? { int http_len = is_http_get_inline(((char*)buf), blen); if((http_len>0) && ((size_t)http_len<=blen)) { *app_len = (size_t)http_len; return http_len; } } /* STUN channel ? */ if(blen>=4) { u16bits chn=nswap16(((const u16bits*)(buf))[0]); if(STUN_VALID_CHANNEL(chn)) { u16bits bret = (4+(nswap16(((const u16bits*)(buf))[1]))); *app_len = bret; if(padding && (bret & 0x0003)) { bret = ((bret>>2)+1)<<2; } if(bret<=blen) { return bret; } } } } return -1; } ////////// ALLOCATE /////////////////////////////////// int stun_set_allocate_request_str(u08bits* buf, size_t *len, u32bits lifetime, int address_family, u08bits transport, int mobile) { stun_init_request_str(STUN_METHOD_ALLOCATE, buf, len); //REQUESTED-TRANSPORT { u08bits field[4]; field[0]=transport; field[1]=0; field[2]=0; field[3]=0; if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_REQUESTED_TRANSPORT,field,sizeof(field))<0) return -1; } //LIFETIME { if(lifetime<1) lifetime=STUN_DEFAULT_ALLOCATE_LIFETIME; u32bits field=nswap32(lifetime); if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_LIFETIME,(u08bits*)(&field),sizeof(field))<0) return -1; } if(mobile) { if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_MOBILITY_TICKET,(const u08bits*)"",0)<0) return -1; } //ADRESS-FAMILY switch (address_family) { case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4: case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6: { u08bits field[4]; field[0] = (u08bits)address_family; field[1]=0; field[2]=0; field[3]=0; if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY,field,sizeof(field))<0) return -1; break; } case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT: /* ignore */ break; default: return -1; }; return 0; } int stun_set_allocate_response_str(u08bits* buf, size_t *len, stun_tid* tid, const ioa_addr *relayed_addr, const ioa_addr *reflexive_addr, u32bits lifetime, int error_code, const u08bits *reason, u64bits reservation_token, char* mobile_id) { if(!error_code) { stun_init_success_response_str(STUN_METHOD_ALLOCATE, buf, len, tid); if(relayed_addr) { if(stun_attr_add_addr_str(buf,len,STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS,relayed_addr)<0) return -1; } if(reflexive_addr) { if(stun_attr_add_addr_str(buf,len,STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,reflexive_addr)<0) return -1; } if(reservation_token) { reservation_token=nswap64(reservation_token); stun_attr_add_str(buf,len,STUN_ATTRIBUTE_RESERVATION_TOKEN,(u08bits*)(&reservation_token),8); } { if(lifetime<1) lifetime=STUN_DEFAULT_ALLOCATE_LIFETIME; else if(lifetime>STUN_MAX_ALLOCATE_LIFETIME) lifetime = STUN_MAX_ALLOCATE_LIFETIME; u32bits field=nswap32(lifetime); if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_LIFETIME,(u08bits*)(&field),sizeof(field))<0) return -1; } if(mobile_id && *mobile_id) { if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_MOBILITY_TICKET,(u08bits*)mobile_id,strlen(mobile_id))<0) return -1; } } else { stun_init_error_response_str(STUN_METHOD_ALLOCATE, buf, len, error_code, reason, tid); } return 0; } /////////////// CHANNEL BIND /////////////////////////////////////// u16bits stun_set_channel_bind_request_str(u08bits* buf, size_t *len, const ioa_addr* peer_addr, u16bits channel_number) { if(!STUN_VALID_CHANNEL(channel_number)) { channel_number = 0x4000 + ((u16bits)(((u32bits)turn_random())%(0x7FFF-0x4000+1))); } stun_init_request_str(STUN_METHOD_CHANNEL_BIND, buf, len); if(stun_attr_add_channel_number_str(buf, len, channel_number)<0) return 0; if(!peer_addr) { ioa_addr ca; ns_bzero(&ca,sizeof(ioa_addr)); if(stun_attr_add_addr_str(buf,len,STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &ca)<0) return 0; } else { if(stun_attr_add_addr_str(buf,len,STUN_ATTRIBUTE_XOR_PEER_ADDRESS, peer_addr)<0) return 0; } return channel_number; } void stun_set_channel_bind_response_str(u08bits* buf, size_t *len, stun_tid* tid, int error_code, const u08bits *reason) { if(!error_code) { stun_init_success_response_str(STUN_METHOD_CHANNEL_BIND, buf, len, tid); } else { stun_init_error_response_str(STUN_METHOD_CHANNEL_BIND, buf, len, error_code, reason, tid); } } /////////////// BINDING /////////////////////////////////////// void stun_set_binding_request_str(u08bits* buf, size_t *len) { stun_init_request_str(STUN_METHOD_BINDING, buf, len); } int stun_set_binding_response_str(u08bits* buf, size_t *len, stun_tid* tid, const ioa_addr *reflexive_addr, int error_code, const u08bits *reason, u32bits cookie, int old_stun) { if (!error_code) { if (!old_stun) { stun_init_success_response_str(STUN_METHOD_BINDING, buf, len, tid); } else { old_stun_init_success_response_str(STUN_METHOD_BINDING, buf, len, tid, cookie); } if(!old_stun) { if (stun_attr_add_addr_str(buf, len, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, reflexive_addr) < 0) return -1; } if (stun_attr_add_addr_str(buf, len, STUN_ATTRIBUTE_MAPPED_ADDRESS, reflexive_addr) < 0) return -1; } else if (!old_stun) { stun_init_error_response_str(STUN_METHOD_BINDING, buf, len, error_code, reason, tid); } else { old_stun_init_error_response_str(STUN_METHOD_BINDING, buf, len, error_code, reason, tid, cookie); } return 0; } int stun_is_binding_request_str(const u08bits* buf, size_t len, size_t offset) { if(offset < len) { buf += offset; len -= offset; if (stun_is_command_message_str(buf, len)) { if (stun_is_request_str(buf, len) && (stun_get_method_str(buf, len) == STUN_METHOD_BINDING)) { return 1; } } } return 0; } int stun_is_binding_response_str(const u08bits* buf, size_t len) { if(stun_is_command_message_str(buf,len) && (stun_get_method_str(buf,len)==STUN_METHOD_BINDING)) { if(stun_is_response_str(buf,len)) { return 1; } } return 0; } /////////////////////////////// TID /////////////////////////////// int stun_tid_equals(const stun_tid *id1, const stun_tid *id2) { if(id1==id2) return 1; if(!id1) return 0; if(!id2) return 0; { unsigned int i=0; for(i=0;itsx_id[i]!=id2->tsx_id[i]) return 0; } } return 1; } void stun_tid_cpy(stun_tid *id1, const stun_tid *id2) { if(!id1) return; if(!id2) return; ns_bcopy((const void*)(id2->tsx_id),(void*)(id1->tsx_id),STUN_TID_SIZE); } static void stun_tid_string_cpy(u08bits* s, const stun_tid* id) { if(s && id) { ns_bcopy((const void*)(id->tsx_id),s,STUN_TID_SIZE); } } static void stun_tid_from_string(const u08bits* s, stun_tid* id) { if(s && id) { ns_bcopy(s,(void*)(id->tsx_id),STUN_TID_SIZE); } } void stun_tid_from_message_str(const u08bits* buf, size_t len, stun_tid* id) { UNUSED_ARG(len); stun_tid_from_string(buf+8, id); } void stun_tid_message_cpy(u08bits* buf, const stun_tid* id) { if(buf && id) { stun_tid_string_cpy(buf+8, id); } } void stun_tid_generate(stun_tid* id) { if(id) { u32bits *w=(u32bits*)(id->tsx_id); turn_random32_size(w,3); } } void stun_tid_generate_in_message_str(u08bits* buf, stun_tid* id) { stun_tid tmp; if(!id) id=&tmp; stun_tid_generate(id); stun_tid_message_cpy(buf, id); } /////////////////// TIME //////////////////////////////////////////////////////// u32bits stun_adjust_allocate_lifetime(u32bits lifetime) { if(!lifetime) return STUN_DEFAULT_ALLOCATE_LIFETIME; else if(lifetimeSTUN_MAX_ALLOCATE_LIFETIME) return STUN_MAX_ALLOCATE_LIFETIME; return lifetime; } ////////////// ATTR ///////////////////////////////////////////////////////////// int stun_attr_get_type(stun_attr_ref attr) { if(attr) return (int)(nswap16(((const u16bits*)attr)[0])); return -1; } int stun_attr_get_len(stun_attr_ref attr) { if(attr) return (int)(nswap16(((const u16bits*)attr)[1])); return -1; } const u08bits* stun_attr_get_value(stun_attr_ref attr) { if(attr) { int len = (int)(nswap16(((const u16bits*)attr)[1])); if(len<1) return NULL; return ((const u08bits*)attr)+4; } return NULL; } int stun_get_requested_address_family(stun_attr_ref attr) { if (attr) { int len = (int) (nswap16(((const u16bits*)attr)[1])); if (len != 4) return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_INVALID; int val = ((const u08bits*) attr)[4]; switch (val){ case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4: return val; case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6: return val; default: return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_INVALID; }; } return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT; } u16bits stun_attr_get_channel_number(stun_attr_ref attr) { if(attr) { const u08bits* value = stun_attr_get_value(attr); if(value && (stun_attr_get_len(attr) >= 2)) { u16bits cn=nswap16(((const u16bits*)value)[0]); if(STUN_VALID_CHANNEL(cn)) return cn; } } return 0; } u64bits stun_attr_get_reservation_token_value(stun_attr_ref attr) { if(attr) { const u08bits* value = stun_attr_get_value(attr); if(value && (stun_attr_get_len(attr) == 8)) { return nswap64(((const u64bits*)value)[0]); } } return 0; } int stun_attr_is_addr(stun_attr_ref attr) { if(attr) { switch(stun_attr_get_type(attr)) { case STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS: case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: case STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS: case STUN_ATTRIBUTE_MAPPED_ADDRESS: case STUN_ATTRIBUTE_ALTERNATE_SERVER: case OLD_STUN_ATTRIBUTE_RESPONSE_ADDRESS: case OLD_STUN_ATTRIBUTE_SOURCE_ADDRESS: case OLD_STUN_ATTRIBUTE_CHANGED_ADDRESS: case OLD_STUN_ATTRIBUTE_REFLECTED_FROM: case STUN_ATTRIBUTE_RESPONSE_ORIGIN: case STUN_ATTRIBUTE_OTHER_ADDRESS: return 1; break; default: ; }; } return 0; } u08bits stun_attr_get_even_port(stun_attr_ref attr) { if(attr) { const u08bits* value=stun_attr_get_value(attr); if(value) { if((u08bits)(value[0]) > 0x7F) return 1; } } return 0; } stun_attr_ref stun_attr_get_first_by_type_str(const u08bits* buf, size_t len, u16bits attr_type) { stun_attr_ref attr=stun_attr_get_first_str(buf,len); while(attr) { if(stun_attr_get_type(attr) == attr_type) { return attr; } attr=stun_attr_get_next_str(buf,len,attr); } return NULL; } stun_attr_ref stun_attr_get_first_str(const u08bits* buf, size_t len) { if(stun_get_command_message_len_str(buf,len)>STUN_HEADER_LENGTH) { return (stun_attr_ref)(buf+STUN_HEADER_LENGTH); } return NULL; } stun_attr_ref stun_attr_get_next_str(const u08bits* buf, size_t len, stun_attr_ref prev) { if(!prev) return stun_attr_get_first_str(buf,len); else { const u08bits* end = buf + stun_get_command_message_len_str(buf,len); int attrlen=stun_attr_get_len(prev); u16bits rem4 = ((u16bits)attrlen) & 0x0003; if(rem4) { attrlen = attrlen+4-(int)rem4; } const u08bits* attr_end=(const u08bits*)prev+4+attrlen; if(attr_end=MAX_STUN_MESSAGE_SIZE) return -1; else { u08bits* attr_start=buf+clen; u16bits *attr_start_16t=(u16bits*)attr_start; stun_set_command_message_len_str(buf,newlen); *len = newlen; attr_start_16t[0]=nswap16(attr); attr_start_16t[1]=nswap16(alen); if(alen>0) ns_bcopy(avalue,attr_start+4,alen); return 0; } } int stun_attr_add_addr_str(u08bits *buf, size_t *len, u16bits attr_type, const ioa_addr* ca) { stun_tid tid; stun_tid_from_message_str(buf, *len, &tid); int xor_ed=0; switch(attr_type) { case STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS: case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: case STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS: xor_ed=1; break; default: ; }; ioa_addr public_addr; map_addr_from_private_to_public(ca,&public_addr); u08bits cfield[64]; int clen=0; if(stun_addr_encode(&public_addr, cfield, &clen, xor_ed, STUN_MAGIC_COOKIE, tid.tsx_id)<0) { return -1; } if(stun_attr_add_str(buf,len,attr_type,(u08bits*)(&cfield),clen)<0) return -1; return 0; } int stun_attr_get_addr_str(const u08bits *buf, size_t len, stun_attr_ref attr, ioa_addr* ca, const ioa_addr *default_addr) { stun_tid tid; stun_tid_from_message_str(buf, len, &tid); ioa_addr public_addr; ns_bzero(ca,sizeof(ioa_addr)); int attr_type = stun_attr_get_type(attr); if(attr_type<0) return -1; int xor_ed=0; switch(attr_type) { case STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS: case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: case STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS: xor_ed=1; break; default: ; }; const u08bits *cfield=stun_attr_get_value(attr); if(!cfield) return -1; if(stun_addr_decode(&public_addr, cfield, stun_attr_get_len(attr), xor_ed, STUN_MAGIC_COOKIE, tid.tsx_id)<0) { return -1; } map_addr_from_public_to_private(&public_addr, ca); if(default_addr && addr_any_no_port(ca) && !addr_any_no_port(default_addr)) { int port = addr_get_port(ca); addr_cpy(ca,default_addr); addr_set_port(ca,port); } return 0; } int stun_attr_get_first_addr_str(const u08bits *buf, size_t len, u16bits attr_type, ioa_addr* ca, const ioa_addr *default_addr) { stun_attr_ref attr=stun_attr_get_first_str(buf,len); while(attr) { if(stun_attr_is_addr(attr) && (attr_type == stun_attr_get_type(attr))) { if(stun_attr_get_addr_str(buf,len,attr,ca,default_addr)==0) { return 0; } } attr=stun_attr_get_next_str(buf,len,attr); } return -1; } int stun_attr_add_channel_number_str(u08bits* buf, size_t *len, u16bits chnumber) { u16bits field[2]; field[0]=nswap16(chnumber); field[1]=0; return stun_attr_add_str(buf,len,STUN_ATTRIBUTE_CHANNEL_NUMBER,(u08bits*)(field),sizeof(field)); } u16bits stun_attr_get_first_channel_number_str(const u08bits *buf, size_t len) { stun_attr_ref attr=stun_attr_get_first_str(buf,len); while(attr) { if(stun_attr_get_type(attr) == STUN_ATTRIBUTE_CHANNEL_NUMBER) { u16bits ret = stun_attr_get_channel_number(attr); if(STUN_VALID_CHANNEL(ret)) { return ret; } } attr=stun_attr_get_next_str(buf,len,attr); } return 0; } ////////////// FINGERPRINT //////////////////////////// int stun_attr_add_fingerprint_str(u08bits *buf, size_t *len) { u32bits crc32 = 0; stun_attr_add_str(buf, len, STUN_ATTRIBUTE_FINGERPRINT, (u08bits*)&crc32, 4); crc32 = ns_crc32(buf,*len-8); *((u32bits*)(buf+*len-4)) = nswap32(crc32 ^ ((u32bits)0x5354554e)); return 0; } ////////////// CRC /////////////////////////////////////////////// #define CRC_MASK 0xFFFFFFFFUL #define UPDATE_CRC(crc, c) crc = crctable[(u08bits)crc ^ (u08bits)(c)] ^ (crc >> 8) static const u32bits crctable[256] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, }; /* #define CRCPOLY 0xEDB88320UL reversed 0x04C11DB7 1110 1101 1001 1000 1000 0011 0010 0000 static void make_crctable(void) { uint i, j; u32bits r; for (i = 0; i < 256; ++i) { r = i; for (j = 8; j > 0; --j) { if (r & 1) r = (r >> 1) ^ CRCPOLY; else r >>= 1; } crctable[i] = r; } } */ static u32bits ns_crc32(const u08bits *buffer, u32bits len) { u32bits crc = CRC_MASK; while ( len-- ) UPDATE_CRC( crc, *buffer++ ); return (~crc); } //////////// SASLprep RFC 4013 ///////////////////////////////////////// /* We support only basic ASCII table */ int SASLprep(u08bits *s) { if(s) { u08bits *strin = s; u08bits *strout = s; for(;;) { u08bits c = *strin; if(!c) { *strout=0; break; } switch(c) { case 0xAD: ++strin; break; case 0xA0: case 0x20: *strout=0x20; ++strout; ++strin; break; case 0x7F: return -1; default: if(c<0x1F) return -1; if(c>=0x80 && c<=0x9F) return -1; *strout=c; ++strout; ++strin; }; } } return 0; } //////////////// Message Integrity //////////////////////////// size_t get_hmackey_size(SHATYPE shatype) { if(shatype == SHATYPE_SHA256) return 32; return 16; } void print_bin_func(const char *name, size_t len, const void *s, const char *func) { printf("<%s>:<%s>:len=%d:[",func,name,(int)len); size_t i; for(i=0;i orig_len) return -1; if (stun_set_command_message_len_str(buf, new_len) < 0) return -1; if(ct == TURN_CREDENTIALS_SHORT_TERM) { res = stun_calculate_hmac(buf, (size_t) new_len - 4 - shasize, pwd, strlen((char*)pwd), new_hmac, &shasize, shatype); } else { res = stun_calculate_hmac(buf, (size_t) new_len - 4 - shasize, key, get_hmackey_size(shatype), new_hmac, &shasize, shatype); } stun_set_command_message_len_str(buf, orig_len); if(res<0) return -1; old_hmac = stun_attr_get_value(sar); if(!old_hmac) return -1; if(bcmp(old_hmac,new_hmac,shasize)) return 0; return 1; } /* * Return -1 if failure, 0 if the integrity is not correct, 1 if OK */ int stun_check_message_integrity_str(turn_credential_type ct, u08bits *buf, size_t len, u08bits *uname, u08bits *realm, u08bits *upwd, SHATYPE shatype) { hmackey_t key; st_password_t pwd; if(ct == TURN_CREDENTIALS_SHORT_TERM) strncpy((char*)pwd,(char*)upwd,sizeof(st_password_t)); else if (stun_produce_integrity_key_str(uname, realm, upwd, key, shatype) < 0) return -1; return stun_check_message_integrity_by_key_str(ct, buf, len, key, pwd, shatype, NULL); } /* RFC 5780 */ int stun_attr_get_change_request_str(stun_attr_ref attr, int *change_ip, int *change_port) { if(stun_attr_get_len(attr) == 4) { const u08bits* value = stun_attr_get_value(attr); if(value) { *change_ip = (value[3] & (u08bits)0x04); *change_port = (value[3] & (u08bits)0x02); return 0; } } return -1; } int stun_attr_add_change_request_str(u08bits *buf, size_t *len, int change_ip, int change_port) { u08bits avalue[4]={0,0,0,0}; if(change_ip) { if(change_port) { avalue[3] = 0x06; } else { avalue[3] = 0x04; } } else if(change_port) { avalue[3]=0x02; } return stun_attr_add_str(buf, len, STUN_ATTRIBUTE_CHANGE_REQUEST, avalue, 4); } int stun_attr_get_response_port_str(stun_attr_ref attr) { if(stun_attr_get_len(attr) >= 2) { const u08bits* value = stun_attr_get_value(attr); if(value) { return nswap16(((const u16bits*)value)[0]); } } return -1; } int stun_attr_add_response_port_str(u08bits *buf, size_t *len, u16bits port) { u08bits avalue[4]={0,0,0,0}; u16bits *port_ptr = (u16bits*)avalue; *port_ptr = nswap16(port); return stun_attr_add_str(buf, len, STUN_ATTRIBUTE_RESPONSE_PORT, avalue, 4); } int stun_attr_get_padding_len_str(stun_attr_ref attr) { int len = stun_attr_get_len(attr); if(len<0) return -1; return (u16bits)len; } int stun_attr_add_padding_str(u08bits *buf, size_t *len, u16bits padding_len) { u08bits avalue[0xFFFF]; ns_bzero(avalue,padding_len); return stun_attr_add_str(buf, len, STUN_ATTRIBUTE_PADDING, avalue, padding_len); } /////////////////////////////////////////////////////////////// turnserver-3.2.3.1/turndb/schema.userdb.redis000644 001751 001751 00000005737 12315706777 021121 0ustar00olegoleg000000 000000 Redis database for user authentication and peer permissions has the following schema: 1) For the long-term credentials there must be keys "turn/user//key" and the values must be the the hmackeys. For example, for the user "gorst", realm "north.gov" and password "hero", there must be key "turn/user/gorst/key" with value "7da2270ccfa49786e0115366d3a3d14d". Alternatively, the password may be stored in clear text format. Then the key will be "turn/user/gorst/password" and the key will be simply "hero". 2) For the short-term credentials, the passwords are stored always on clear text format. So, there will be key "turn/user/gorst/password" and the value will be "hero". 3) For the shared secrets (REST API), several key/value pairs may be used (same as in SQL schema). The key will be "turn/secret/" and the value will be "". For example, if we have secrets "hero1", "hero2" and "hero3", then we will have keys "turn/secret/A", "turn/secret/B", "turn/secret/777" and their values will be "hero1", "hero2", "hero3". The turnserver will issue command "keys turn/secret/*" it it will try to use the returned keys in arbitrary order. 4) The "white" and "black" peer IP ranges are stored as keys of the following form: "turn/allowed-peer-ip/" "turn/denied-peer-ip/" The meaning of the keys is the same as the meaning of allowed-peer-ip and denied-peer-ip turnserver command-line option. The only difference is that the option values are "static" (they remain the same for the lifetime of the turnserver process) but the database records can be dynamically changed and they will be almost immediately "seen" by the turnserver process. 5) Example of a Redis user database setup. This example sets user database for the following security mechanism variants: * long-term credentials with open passwords; * long-term credentials with hashed passwords and realm north.gov; * short-term credentials mechanism; * TURN REST API with shared secret "logen"; * Black and white IP peer lists used. The shell command would be: redis-cli </allocation//status", and the values may be "new lifetime=..." or "refreshed lifetime=...". There may be multiple allocations under the same user name. 2) Additionally, the same information is reported through the "publish" mechanism. The same keys are used and the same status is reported, with the addition of status "deleted". The user session that is interested in those statuses, must subscribe to the events as: psubscribe turn/user/*/allocation/*/status 3) Allocation traffic information is reported through the "publish" mechanism. The keys are "turn/user//allocation//traffic". The application that is interested in the traffic information must subscribe to the events as: psubscribe turn/user/*/allocation/*/traffic Or, to receive all allocation events: psubscribe turn/user/*/allocation/* 4) Redis database configuration parameters TURN Server connects to the Redis and keeps the same connection during the TURN server lifetime. That means that we have to take care about that connection - it must NOT expire. You have to take care about Redis connection parameters, the timeout and the keepalive. The following settings must be in your Redis config file (/etc/redis.conf or /usr/local/etc/redis.conf): .......... timeout 0 .......... tcp-keepalive 60 .......... turnserver-3.2.3.1/turndb/testredisdbsetup.sh000755 001751 001751 00000000710 12315706777 021264 0ustar00olegoleg000000 000000 #!/bin/sh redis-cli <