Browse Source

Imported Upstream version 4.1.0.1

Oleg Moskalenko 11 years ago
parent
commit
fbd142b317
53 changed files with 5225 additions and 1391 deletions
  1. 3 0
      AUTHORS
  2. 12 0
      ChangeLog
  3. 56 23
      INSTALL
  4. 8 2
      Makefile.in
  5. 4 0
      README.turnadmin
  6. 24 7
      README.turnserver
  7. 4 0
      README.turnutils
  8. 8 2
      STATUS
  9. 6 13
      TODO
  10. 60 4
      configure
  11. 14 4
      examples/etc/turnserver.conf
  12. 33 0
      examples/scripts/longtermsecuredb/secure_relay_with_db_mongo.sh
  13. 8 1
      man/man1/turnadmin.1
  14. 28 7
      man/man1/turnserver.1
  15. 7 1
      man/man1/turnutils.1
  16. 1 1
      postinstall.txt
  17. 1 10
      rpm/CentOS6.pre.build.sh
  18. 1 1
      rpm/build.settings.sh
  19. 9 1
      rpm/turnserver.spec
  20. 5 4
      src/apps/common/stun_buffer.c
  21. 3 2
      src/apps/common/stun_buffer.h
  22. 939 0
      src/apps/relay/dbdrivers/dbd_mongo.c
  23. 49 0
      src/apps/relay/dbdrivers/dbd_mongo.h
  24. 874 0
      src/apps/relay/dbdrivers/dbd_mysql.c
  25. 49 0
      src/apps/relay/dbdrivers/dbd_mysql.h
  26. 673 0
      src/apps/relay/dbdrivers/dbd_pgsql.c
  27. 49 0
      src/apps/relay/dbdrivers/dbd_pgsql.h
  28. 1150 0
      src/apps/relay/dbdrivers/dbd_redis.c
  29. 49 0
      src/apps/relay/dbdrivers/dbd_redis.h
  30. 90 0
      src/apps/relay/dbdrivers/dbdriver.c
  31. 78 0
      src/apps/relay/dbdrivers/dbdriver.h
  32. 71 14
      src/apps/relay/mainrelay.c
  33. 0 2
      src/apps/relay/mainrelay.h
  34. 83 35
      src/apps/relay/netengine.c
  35. 9 7
      src/apps/relay/ns_ioalib_engine_impl.c
  36. 35 22
      src/apps/relay/turncli.c
  37. 34 959
      src/apps/relay/userdb.c
  38. 8 1
      src/apps/relay/userdb.h
  39. 7 1
      src/apps/uclient/mainuclient.c
  40. 0 1
      src/apps/uclient/session.h
  41. 99 31
      src/apps/uclient/startuclient.c
  42. 1 0
      src/apps/uclient/uclient.h
  43. 5 4
      src/client++/TurnMsgLib.h
  44. 19 15
      src/client/ns_turn_msg.c
  45. 2 2
      src/client/ns_turn_msg.h
  46. 3 3
      src/ns_turn_defs.h
  47. 59 13
      src/server/ns_turn_allocation.c
  48. 18 20
      src/server/ns_turn_allocation.h
  49. 1 1
      src/server/ns_turn_ioalib.h
  50. 412 173
      src/server/ns_turn_server.c
  51. 4 4
      src/server/ns_turn_session.h
  52. 14 0
      turndb/schema.mongo.sh
  53. 46 0
      turndb/testmongosetup.sh

+ 3 - 0
AUTHORS

@@ -44,3 +44,6 @@ Mutsutoshi Yoshimoto <[email protected]>
 	TCP routing: testing and bug fixes
 	(since v3.2.2.7)
 
+Federico Pinna <[email protected]>
+	MongoDB support
+	(since v4.1.0.1)

+ 12 - 0
ChangeLog

@@ -1,3 +1,15 @@
+06/25/2014 Oleg Moskalenko <[email protected]>
+Version 4.1.0.1 'Vitari':
+	- SSODA (double allocation) draft support added.
+	- DB "driver" abstraction and MongoDB support (by Federico Pinna).
+	- multiple origins supported per request.
+	- "allocation mismatch" condition fixed (merged from rfc5766-turn-server).
+	- STUN BINDING response fixed in the case of -X (external address) option.
+	- "pu" CLI command fixed.
+	- session cleaning fixed in TCP relay functionality (RFC 6062).
+	- some crash conditions fixed.
+	- working on compilation warnings.
+
 06/13/2014 Oleg Moskalenko <[email protected]>
 Version 4.0.1.3 'Severard':
 	- Redis DB connection status fixed (Issue 129).

+ 56 - 23
INSTALL

@@ -50,11 +50,11 @@ The turn*.conf config files are in /etc directory.
 The service start-up control scripts will be in /etc/init.d/coturn and
 in /etc/defaults/coturn files.
 
-3) ArchLinux has alse the predecessor of the TURN server package:
+3) ArchLinux has this TURN server package:
 
-https://aur.archlinux.org/packages/rfc5766-turn-server/
+https://aur.archlinux.org/packages/coturn/
 
-4) OpenSUSE has a package, too:
+4) OpenSUSE has an older package, too:
 
 https://build.opensuse.org/package/show/home:ehauenstein/rfc5766-turn-server
 
@@ -212,13 +212,13 @@ do not allows that, then after the installation, you may have to adjust the
 system-wide shared library search path by using "ldconfig -n <libdirname>" 
 (Linux), "ldconfig -m <libdirname>" (BSD) or "crle -u -l <libdirname>" 
 (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.
+(optionally) PostgreSQL and/or MySQL (MariaDB) and/or MongoDB 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
 
@@ -277,8 +277,8 @@ 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.
+optional, the MySQL (MariaDB) C client development setup is optional, the MongoDB
+C Driver 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 
@@ -290,6 +290,7 @@ libraries can be downloaded from their web sites:
  - http://www.libevent.org (required);
  - http://www.postgresql.org (optional);
  - http://www.mysql.org (or http://mariadb.org) (optional);
+ - https://github.com/mongodb/mongo-c-driver (optional);
  - http://redis.io (optional).
  
 The installations are pretty straightforward - the usual 
@@ -352,6 +353,11 @@ installation:
 	- you have to install gcc first:
 		$ sudo yum install gcc
 
+	- mongo-c-driver packages are not available. MongoDB support 
+  will not be compiled, unless you install it "manually" before 
+  the TURN server compilation. Refer to https://github.com/mongodb/mongo-c-driver
+  for installation instructions of the driver.
+		
 	- 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 
@@ -381,7 +387,7 @@ like this:
 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. 
+and/or MySQL and/or MongoDB 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:
 
@@ -412,17 +418,17 @@ 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: See "PostgreSQL setup" and "MySQL setup" and "MongoDB 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
+NOTE: If you do not install PostgreSQL or MySQL or MongoDB 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.
+will need PostgreSQL or MySQL or MongoDB or Redis.
 
-NOTE: To run PostgreSQL or MySQL or Redis server on the same system, 
+NOTE: To run PostgreSQL or MySQL or MongoDB 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 
+MongoDB 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.   
@@ -892,7 +898,34 @@ Or in the turnserver.conf file:
 
 mysql-userdb="host=localhost dbname=turn user=turn password=turn connect_timeout=30"
 
-XVI. Redis setup
+XVI. MongoDB setup
+
+The MongoDB setup is well documented on their site http://docs.mongodb.org/manual/. 
+
+Note: if your system has a "standard" plain vanilla UNIX "make" utility
+(that is not a GNU make) then you will have to use the GNU make to compile 
+the Mongo driver, because the Mongo compilation process was written with 
+the "proprietary" GNU extentions. For example, in FreeBSD in will have to use 
+"gmake" command. 
+
+If the TURN server was compiled with MongoDB support (mongo-c-driver is the C client 
+library for MongoDB), then we can use the TURN server database parameter 
+--mongo-userdb. The value of this parameter is a connection string 
+for the MongoDB database. The format of the connection string is described at 
+http://hergert.me/docs/mongo-c-driver/mongoc_uri.html:
+
+"mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]"
+
+So, an example of the MongoDB database parameter in the TURN server command 
+line would be:
+
+--mongo-userdb="mongodb://localhost:27017/turndb"
+
+Or in the turnserver.conf file:
+
+mongo-userdb="mongodb://localhost:27017/turndb"
+
+XVII. Redis setup
 
 The Redis setup is well documented on their site http://redis.io. 
 The TURN Server Redis database schema description can be found 
@@ -961,20 +994,20 @@ Redis TURN admin commands:
   $ 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
+XVIII. Performance tuning
 
 This topic is covered in the wiki page:
 
 http://code.google.com/p/coturn/wiki/turn_performance_and_load_balance
 
-XVI. TURN Server setup
+XIX. TURN Server setup
 
 Read the project wiki pages: http://code.google.com/p/coturn/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
+XX. 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 

+ 8 - 2
Makefile.in

@@ -27,8 +27,11 @@ 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}
+USERDB_HEADERS = src/apps/relay/dbdrivers/dbdriver.h src/apps/relay/dbdrivers/dbd_pgsql.h src/apps/relay/dbdrivers/dbd_mysql.h src/apps/relay/dbdrivers/dbd_mongo.h src/apps/relay/dbdrivers/dbd_redis.h
+USERDB_MODS = src/apps/relay/dbdrivers/dbdriver.c src/apps/relay/dbdrivers/dbd_pgsql.c src/apps/relay/dbdrivers/dbd_mysql.c src/apps/relay/dbdrivers/dbd_mongo.c src/apps/relay/dbdrivers/dbd_redis.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} ${USERDB_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} ${USERDB_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
@@ -129,7 +132,10 @@ install:	all ${MAKE_DEPS}
 	${INSTALL_DATA} postinstall.txt ${DESTDIR}${DOCSDIR}
 	${INSTALL_DATA} turndb/schema.sql ${DESTDIR}${DOCSDIR}
 	${INSTALL_DATA} turndb/schema.sql ${DESTDIR}${SCHEMADIR}
+	${INSTALL_DATA} turndb/schema.mongo.sh ${DESTDIR}${DOCSDIR}
+	${INSTALL_DATA} turndb/schema.mongo.sh ${DESTDIR}${SCHEMADIR}
 	${INSTALL_DATA} turndb/testredisdbsetup.sh ${DESTDIR}${SCHEMADIR}
+	${INSTALL_DATA} turndb/testmongosetup.sh ${DESTDIR}${SCHEMADIR}
 	${INSTALL_DATA} turndb/testsqldbsetup.sql ${DESTDIR}${SCHEMADIR}
 	${INSTALL_DATA} turndb/schema.userdb.redis ${DESTDIR}${DOCSDIR}
 	${INSTALL_DATA} turndb/schema.userdb.redis ${DESTDIR}${SCHEMADIR}

+ 4 - 0
README.turnadmin

@@ -92,6 +92,8 @@ Options with required values:
 			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.
+-J, --mongo-userdb	MongoDB user database connection string.
+			See the --mysql-mongo 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.
@@ -237,3 +239,5 @@ to see the man page.
 	Peter Dunkley <[email protected]>
 	
 	Mutsutoshi Yoshimoto <[email protected]>
+
+	Federico Pinna <[email protected]>

+ 24 - 7
README.turnserver

@@ -57,7 +57,7 @@ turnserver - a TURN relay server implementation.
   
   SYNOPSIS
   
-$ turnserver [-n | -c <config-file> ] [flags] [ --userdb=<userdb-file> | --psql-userdb=<db-conn-string> | --mysql-userdb=<db-conn-string>  | --redis-userdb=<db-conn-string> ] [-z | --no-auth | -a | --lt-cred-mech ] [options]
+$ turnserver [-n | -c <config-file> ] [flags] [ --userdb=<userdb-file> | --psql-userdb=<db-conn-string> | --mysql-userdb=<db-conn-string>  | --mongo-userdb=<db-conn-string>  | --redis-userdb=<db-conn-string> ] [-z | --no-auth | -a | --lt-cred-mech ] [options]
 $ turnserver -h
   
   DESCRIPTION						
@@ -113,6 +113,17 @@ User database settings:
 		Also, see http://www.mysql.org or http://mariadb.org 
 		for full MySQL documentation.
 		
+-J, --mongo-userdb	User database connection string for MongoDB. 
+		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:
+		 
+		"mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]"
+		See the INSTALL file for more explanations and examples.
+		
+		Also, see http://docs.mongodb.org/manual/
+		for full MongoDB 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.
@@ -138,9 +149,9 @@ Flags:
 
 -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.
+			PostgreSQL DB or MySQL DB or MongoDB 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.
+	                a PostgreSQL or MySQL or MongoDB 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 
@@ -425,7 +436,9 @@ Options with required values:
 			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.
+			the system log (syslog), as if the option "--syslog" was set. 
+			In the runtime, the logfile can be reset with the SIGHUP signal 
+			to the turnserver process.
 				
 --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 
@@ -459,7 +472,7 @@ Options with required values:
 			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
+			and actually Redis can be used for status/statistics and MySQL or MongoDB or PostgreSQL can
 			be used for the user database.
 			The connection string has the same parameters as redis-userdb connection string.
 
@@ -531,7 +544,7 @@ for that you have a number of options:
 	
 	b) userdb config file.
 	
-	c) a database table (PostgreSQL or MySQL). You will have to set keys with 
+	c) a database table (PostgreSQL or MySQL or MongoDB). You will have to set keys with 
 	turnadmin utility (see docs and wiki for turnadmin). You cannot use open passwords 
 	in the database.
 
@@ -715,7 +728,9 @@ turndb/testredisdbsetup.sh as an example.
 can be administered separately, and each realm can have its own set of users and its own
 performance options (max-bps, user-quota, total-quota).
 
-7) Of course, the turnserver can be used in non-secure mode, when users are allowed to establish
+7) If you use MongoDB, the database will be setup for you automatically.
+
+8) 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:
@@ -890,3 +905,5 @@ SEE ALSO
 	Peter Dunkley <[email protected]>
 	
 	Mutsutoshi Yoshimoto <[email protected]>
+
+	Federico Pinna <[email protected]>

+ 4 - 0
README.turnutils

@@ -115,6 +115,8 @@ Flags:
 
 -B  	Random disconnect after a few initial packets.
 
+-Z  Dual allocation.
+
 Options with required values:  
 
 -l      Message length (Default: 100 Bytes).
@@ -333,3 +335,5 @@ SEE ALSO
 	Peter Dunkley <[email protected]>
 	
 	Mutsutoshi Yoshimoto <[email protected]>
+	
+	Federico Pinna <[email protected]>

+ 8 - 2
STATUS

@@ -93,9 +93,15 @@ compatibility.
 
 39) Congestion avoidance implemented, for all protocols.
 
-40) Multi-tenant server implemented.
+40) Coturn project forked from rfc5766-turn-server.
 
-41) Coturn project forked from rfc5766-turn-server.
+41) Multi-tenant server implemented (ORIGIN support).
+
+42) Bandwidth draft support added.
+
+43) MongoDB support added.
+
+44) Double (dual) allocation added (SSODA draft).
  
 Things to be implemented in future (the development roadmap) 
 are described in the TODO file.

+ 6 - 13
TODO

@@ -55,24 +55,17 @@
 
 ==================================================================
 
-1) Follow the draft ICE endpoint mobility standard and apply changes 
-when necessary:
+1) For extra difficult NAT/FWs, consider implementing Websockets.
 
-   https://ietf.org/doc/draft-wing-mmusic-ice-mobility/
+2) MS TURN, MS STUN extensions.
 
-2) For extra difficult NAT/FWs, consider implementing Websockets.
+3) ALPN with TLS and DTLS (when OpenSSL 1.0.2 is available).
 
-3) MS TURN, MS STUN extensions.
+4) Redirect draft.
 
-4) Multiple origins.
+5) New security oAuth draft.
 
-5) ALPN with TLS and DTLS (when OpenSSL 1.0.2 is available).
-
-6) Redirect draft.
-
-7) Dual allocation draft.
-
-8) New security oAuth draft.
+6) DTLS 1.2 (when available).
 
 ==================================================================
 

+ 60 - 4
configure

@@ -13,6 +13,8 @@ cleanup() {
 	rm -rf ${PQ_TMPCPROGB}
 	rm -rf ${MYSQL_TMPCPROGC}
 	rm -rf ${MYSQL_TMPCPROGB}
+	rm -rf ${MONGO_TMPCPROGC}
+	rm -rf ${MONGO_TMPCPROGB}
 	rm -rf ${D_TMPCPROGC}
 	rm -rf ${D_TMPCPROGB}
 	rm -rf ${E_TMPCPROGC}
@@ -124,6 +126,30 @@ testlibmysql() {
     fi
 }
 
+testlibmongoc() {
+    for inc in ${PREFIX}/libmongoc-1.0 ${PREFIX}/libbson-1.0 /usr/local/include/libmongoc-1.0 /usr/local/include/libbson-1.0 /usr/libmongoc-1.0 -I/usr/libbson-1.0
+    do
+      if [ -d ${inc} ] ; then
+        MONGO_CFLAGS="${MONGO_CFLAGS} -I${inc}"
+      fi
+    done
+    MONGO_LIBS="-lmongoc-1.0 -lbson-1.0"
+    ${CC} ${MONGO_TMPCPROGC} -o ${MONGO_TMPCPROGB} ${OSCFLAGS} ${DBCFLAGS} ${DBLIBS} ${MONGO_CFLAGS} ${MONGO_LIBS} ${OSLIBS} 2>>/dev/null
+    ER=$?
+    if ! [ ${ER} -eq 0 ] ; then
+    	${ECHO_CMD}
+		${ECHO_CMD} "MONGODB DEVELOPMENT LIBRARIES (libmongoc-1.0 and libbson-1.0) AND/OR HEADER (mongoc.h)"
+		${ECHO_CMD} "	ARE NOT INSTALLED PROPERLY ON THIS SYSTEM."
+		${ECHO_CMD} "	THAT'S OK BUT THE TURN SERVER IS BUILDING WITHOUT MONGODB SUPPORT."
+		${ECHO_CMD}
+		return 0
+    else
+		DBCFLAGS="${DBCFLAGS} ${MONGO_CFLAGS}"
+		DBLIBS="${DBLIBS} ${MONGO_LIBS}"
+		return 1
+    fi
+}
+
 testlib() {
     testlibraw l${1}
 }
@@ -666,6 +692,19 @@ int main(int argc, char** argv) {
 }
 !
 
+MONGO_TMPCPROG=__test__ccomp__libmongoc__$$
+MONGO_TMPCPROGC=${TMPDIR}/${MONGO_TMPCPROG}.c
+MONGO_TMPCPROGB=${TMPDIR}/${MONGO_TMPCPROG}
+
+cat > ${MONGO_TMPCPROGC} <<!
+#include <mongoc.h>
+int main(int argc, char** argv) {
+    return (argc+
+    	(int)(mongoc_client_new("mongodb://localhost:27017")!=0)+
+    	(int)(argv[0][0]));
+}
+!
+
 ##########################
 # What is our compiler ?
 ##########################
@@ -714,7 +753,7 @@ fi
 
 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"
+GNUOSCFLAGS="${GNUOSCFLAGS}"
 
 ${CC} -Werror ${GNUOSCFLAGS} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null
 ER=$?
@@ -726,12 +765,12 @@ 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"
+		GNUOSCFLAGS="-g -Wall -Wno-deprecated-declarations -Wextra -Wformat-security -Wpointer-arith -Wcast-qual"
 		${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"
+	    	GNUOSCFLAGS="-x c++ -g -Wall -Wno-deprecated-declarations -Wextra -Wformat-security -Wpointer-arith -Wcast-qual"
 	    	${CC} -Werror ${GNUOSCFLAGS} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null
 	    	ER=$?
 	    	if ! [ ${ER} -eq 0 ] ; then
@@ -940,6 +979,23 @@ else
 	TURN_NO_MYSQL="-DTURN_NO_MYSQL"
 fi
 
+###########################
+# Test MongoDB
+###########################
+
+if [ -z "${TURN_NO_MONGO}" ] ; then
+
+	testlibmongoc
+	ER=$?
+	if ! [ ${ER} -eq 0 ] ; then
+		${ECHO_CMD} "MongoDB found."
+	else
+		TURN_NO_MONGO="-DTURN_NO_MONGO"
+	fi
+else
+	TURN_NO_MONGO="-DTURN_NO_MONGO"
+fi
+
 ###########################
 # Test Redis
 ###########################
@@ -1012,7 +1068,7 @@ ${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} "DBCFLAGS += ${DBCFLAGS} ${TURN_NO_PQ} ${TURN_NO_MYSQL} ${TURN_NO_MONGO} ${TURN_NO_HIREDIS}" >> Makefile
 ${ECHO_CMD} "#" >> Makefile
 ${ECHO_CMD} "PORTNAME = ${PORTNAME}" >> Makefile
 ${ECHO_CMD} "PREFIX = ${PREFIX}" >> Makefile

+ 14 - 4
examples/etc/turnserver.conf

@@ -161,14 +161,14 @@
 # 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.
+# PostgreSQL DB or MySQL DB or MongoDB 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.
+# MySQL or MongoDB or Redis database for user password storage.
 #
 #st-cred-mech
 
@@ -241,7 +241,7 @@
 # '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.
+# mechanism required PostgreSQL or MySQL or MongoDB 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.
 #
@@ -268,6 +268,14 @@
 #
 #mysql-userdb="host=<host> dbname=<database-name> user=<database-user> password=<database-user-password> port=<port> connect_timeout=<seconds>"
 
+# MongoDB database connection string in the case that we are using MongoDB
+# 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 is described at http://hergert.me/docs/mongo-c-driver/mongoc_uri.html
+#
+#mongo-userdb="mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]"
+
 # 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
@@ -421,7 +429,9 @@
 # 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).
+# the system log (syslog). 
+# In the runtime, the logfile can be reset with the SIGHUP signal 
+# to the turnserver process.
 #
 #log-file=/var/tmp/turn.log
 

+ 33 - 0
examples/scripts/longtermsecuredb/secure_relay_with_db_mongo.sh

@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# This is an example how to start a TURN Server in
+# secure mode with MongoDB 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) --mongo-userdb="mongodb://localhost/coturn" 
+# means that local MongoDB database "turn" will be used.
+# 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/
+export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/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 --mongo-userdb="mongodb://localhost/coturn" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL:SSLv2 $@

+ 8 - 1
man/man1/turnadmin.1

@@ -1,5 +1,5 @@
 .\" Text automatically generated by txt2man
-.TH TURN 1 "29 May 2014" "" ""
+.TH TURN 1 "15 July 2014" "" ""
 .SH GENERAL INFORMATION
 
 \fIturnadmin\fP is a TURN administration tool. This tool can be used to manage 
@@ -152,6 +152,11 @@ MySQL user database connection string.
 See the \fB\-\-mysql\-userdb\fP option in the \fIturnserver\fP section.
 .TP
 .B
+\fB\-J\fP, \fB\-\-mongo\-userdb\fP
+MongoDB user database connection string.
+See the \fB\-\-mysql\-mongo\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.
@@ -323,3 +328,5 @@ Po\-sheng Lin <[email protected]>
 Peter Dunkley <[email protected]>
 .PP
 Mutsutoshi Yoshimoto <[email protected]>
+.PP
+Federico Pinna <[email protected]>

+ 28 - 7
man/man1/turnserver.1

@@ -1,5 +1,5 @@
 .\" Text automatically generated by txt2man
-.TH TURN 1 "29 May 2014" "" ""
+.TH TURN 1 "15 July 2014" "" ""
 .SH GENERAL INFORMATION
 
 The \fBTURN Server\fP project contains the source code of a TURN server and TURN client 
@@ -87,7 +87,7 @@ is equivalent to:
 .nf
 .fam C
 
-$ \fIturnserver\fP [\fB\-n\fP | \fB\-c\fP <config\-file> ] [\fIflags\fP] [ \fB\-\-userdb\fP=<userdb\-file> | \fB\-\-psql\-userdb\fP=<db\-conn\-string> | \fB\-\-mysql\-userdb\fP=<db\-conn\-string>  | \fB\-\-redis\-userdb\fP=<db\-conn\-string> ] [\fB\-z\fP | \fB\-\-no\-auth\fP | \fB\-a\fP | \fB\-\-lt\-cred\-mech\fP ] [\fIoptions\fP]
+$ \fIturnserver\fP [\fB\-n\fP | \fB\-c\fP <config\-file> ] [\fIflags\fP] [ \fB\-\-userdb\fP=<userdb\-file> | \fB\-\-psql\-userdb\fP=<db\-conn\-string> | \fB\-\-mysql\-userdb\fP=<db\-conn\-string>  | \fB\-\-mongo\-userdb\fP=<db\-conn\-string>  | \fB\-\-redis\-userdb\fP=<db\-conn\-string> ] [\fB\-z\fP | \fB\-\-no\-auth\fP | \fB\-a\fP | \fB\-\-lt\-cred\-mech\fP ] [\fIoptions\fP]
 $ \fIturnserver\fP \fB\-h\fP
 
 .fam T
@@ -173,6 +173,21 @@ for full MySQL documentation.
 .RE
 .TP
 .B
+\fB\-J\fP, \fB\-\-mongo\-userdb\fP
+User database connection string for MongoDB. 
+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
+"mongodb://[username:password@]host1[:port1][,host2[:port2],\.\.\.[,hostN[:portN]]][/[database][?\fIoptions\fP]]"
+See the INSTALL file for more explanations and examples.
+.PP
+Also, see http://docs.mongodb.org/manual/
+for full MongoDB 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,
@@ -212,12 +227,12 @@ per\-server setting.
 \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.
+PostgreSQL DB or MySQL DB or MongoDB 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.
+a PostgreSQL or MySQL or MongoDB or Redis DB for short term passwords storage.
 .TP
 .B
 \fB\-z\fP, \fB\-\-no\-auth\fP
@@ -625,7 +640,9 @@ 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.
+the system log (syslog), as if the option "\fB\-\-syslog\fP" was set. 
+In the runtime, the logfile can be reset with the SIGHUP signal 
+to the \fIturnserver\fP process.
 .TP
 .B
 \fB\-\-alternate\-server\fP
@@ -665,7 +682,7 @@ Redis status and statistics database connection string, if used (default \- empt
 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
+and actually Redis can be used for status/statistics and MySQL or MongoDB or PostgreSQL can
 be used for the user database.
 The connection string has the same parameters as redis\-userdb connection string.
 .TP
@@ -760,7 +777,7 @@ for that you have a number of \fIoptions\fP:
 
         b) userdb config file.
 
-        c) a database table (PostgreSQL or MySQL). You will have to set keys with 
+        c) a database table (PostgreSQL or MySQL or MongoDB). You will have to set keys with 
         turnadmin utility (see docs and wiki for turnadmin). You cannot use open passwords 
         in the database.
 
@@ -961,6 +978,8 @@ If a database is used, then users can be divided into multiple independent realm
 can be administered separately, and each realm can have its own set of users and its own
 performance \fIoptions\fP (max\-bps, user\-quota, total\-quota).
 .IP 7) 4
+If you use MongoDB, the database will be setup for you automatically.
+.IP 8) 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
@@ -1133,3 +1152,5 @@ Po\-sheng Lin <[email protected]>
 Peter Dunkley <[email protected]>
 .PP
 Mutsutoshi Yoshimoto <[email protected]>
+.PP
+Federico Pinna <[email protected]>

+ 7 - 1
man/man1/turnutils.1

@@ -1,5 +1,5 @@
 .\" Text automatically generated by txt2man
-.TH TURN 1 "29 May 2014" "" ""
+.TH TURN 1 "15 July 2014" "" ""
 .SH GENERAL INFORMATION
 
 A set of turnutils_* programs provides some utility functionality to be used
@@ -173,6 +173,10 @@ Generate extra requests (create permissions, channel bind).
 Random disconnect after a few initial packets.
 .TP
 .B
+\fB\-Z\fP
+Dual allocation.
+.TP
+.B
 Options with required values:
 .TP
 .B
@@ -449,3 +453,5 @@ Po\-sheng Lin <[email protected]>
 Peter Dunkley <[email protected]>
 .PP
 Mutsutoshi Yoshimoto <[email protected]>
+.PP
+Federico Pinna <[email protected]>

+ 1 - 1
postinstall.txt

@@ -10,7 +10,7 @@ service, you have to:
 
 	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.
+	file, or set up PostgreSQL or MySQL or MongoDB 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

+ 1 - 10
rpm/CentOS6.pre.build.sh

@@ -29,16 +29,6 @@ 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}
@@ -56,6 +46,7 @@ if ! [ -f ${BUILDDIR}/SPECS/${LIBEVENT_SPEC_FILE} ] ; then
     fi
 
     cp ${LIBEVENT_SPEC_DIR}/${LIBEVENT_SPEC_FILE} ${BUILDDIR}/SPECS
+    cp ${LIBEVENT_SPEC_DIR}/${LIBEVENT_DISTRO} ${BUILDDIR}/SOURCES
 fi
 
 cd ${BUILDDIR}/SPECS

+ 1 - 1
rpm/build.settings.sh

@@ -2,7 +2,7 @@
 
 # Common settings script.
 
-TURNVERSION=4.0.1.3
+TURNVERSION=4.1.0.1
 BUILDDIR=~/rpmbuild
 ARCH=`uname -p`
 TURNSERVER_SVN_URL=http://coturn.googlecode.com/svn

+ 9 - 1
rpm/turnserver.spec

@@ -1,5 +1,5 @@
 Name:		turnserver
-Version:	4.0.1.3
+Version:	4.1.0.1
 Release:	0%{dist}
 Summary:	Coturn TURN Server
 
@@ -188,13 +188,16 @@ fi
 %{_docdir}/%{name}/README.turnadmin
 %{_docdir}/%{name}/README.turnserver
 %{_docdir}/%{name}/schema.sql
+%{_docdir}/%{name}/schema.mongo.sh
 %{_docdir}/%{name}/schema.stats.redis
 %{_docdir}/%{name}/schema.userdb.redis
 %dir %{_datadir}/%{name}
 %{_datadir}/%{name}/schema.sql
+%{_datadir}/%{name}/schema.mongo.sh
 %{_datadir}/%{name}/schema.stats.redis
 %{_datadir}/%{name}/schema.userdb.redis
 %{_datadir}/%{name}/testredisdbsetup.sh
+%{_datadir}/%{name}/testmongosetup.sh
 %{_datadir}/%{name}/testsqldbsetup.sql
 %dir %{_datadir}/%{name}/etc
 %{_datadir}/%{name}/etc/turn_server_cert.pem
@@ -232,6 +235,7 @@ fi
 %{_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_mongo.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
@@ -289,6 +293,10 @@ fi
 %{_includedir}/turn/client/TurnMsgLib.h
 
 %changelog
+* Fri Jul 11 2014 Oleg Moskalenko <[email protected]>
+  - Sync to 4.1.0.1
+* Wed Jun 25 2014 Oleg Moskalenko <[email protected]>
+  - Sync to 4.0.1.4
 * Fri Jun 13 2014 Oleg Moskalenko <[email protected]>
   - Sync to 4.0.1.3
 * Fri Jun 06 2014 Oleg Moskalenko <[email protected]>

+ 5 - 4
src/apps/common/stun_buffer.c

@@ -153,17 +153,18 @@ int stun_is_channel_message(stun_buffer* buf, u16bits* chnumber, int is_padding_
 
 ///////////////////////////////////////////////////////////////////////////////
 
-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_request(stun_buffer* buf, u32bits lifetime, int af4, int af6, u08bits transport, int mobile) {
+  return stun_set_allocate_request_str(buf->buf, (size_t*)(&(buf->len)), lifetime, af4, af6, transport, mobile);
 }
 
 int stun_set_allocate_response(stun_buffer* buf, stun_tid* tid, 
-			       const ioa_addr *relayed_addr, const ioa_addr *reflexive_addr,
+			       const ioa_addr *relayed_addr1, const ioa_addr *relayed_addr2,
+			       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, 
+					relayed_addr1, relayed_addr2, reflexive_addr,
 					lifetime, error_code, reason,
 					reservation_token, mobile_id);
 

+ 3 - 2
src/apps/common/stun_buffer.h

@@ -101,9 +101,10 @@ int stun_is_channel_message(stun_buffer* buf, u16bits* chnumber, int is_padding_
 
 ///////////////////////////////////////////////////////////////
 
-int stun_set_allocate_request(stun_buffer* buf, u32bits lifetime, int address_family, u08bits transport, int mobile);
+int stun_set_allocate_request(stun_buffer* buf, u32bits lifetime, int af4, int af6, 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,
+			       const ioa_addr *relayed_addr1, const ioa_addr *relayed_addr2,
+			       const ioa_addr *reflexive_addr,
 			       u32bits lifetime,
 			       int error_code, const u08bits *reason,
 			       u64bits reservation_token, char *mobile_id);

+ 939 - 0
src/apps/relay/dbdrivers/dbd_mongo.c

@@ -0,0 +1,939 @@
+/*
+ * Copyright (C) 2011, 2012, 2013 Citrix Systems
+ * Copyright (C) 2014 Vivocha S.p.A.
+ *
+ * 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"
+#include "dbd_mongo.h"
+
+#if !defined(TURN_NO_MONGO)
+#include <mongoc.h>
+#include <bson.h>
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+const char * MONGO_DEFAULT_DB = "turn";
+
+struct _MONGO {
+  mongoc_uri_t * uri;
+  mongoc_client_t * client;
+  const char * database;
+};
+
+typedef struct _MONGO MONGO;
+
+static void mongo_logger(mongoc_log_level_t log_level, const char * log_domain, const char * message, void * user_data) {
+	UNUSED_ARG(log_domain);
+	UNUSED_ARG(user_data);
+
+  TURN_LOG_LEVEL l;
+  
+  switch(log_level) {
+    case MONGOC_LOG_LEVEL_ERROR:
+      l = TURN_LOG_LEVEL_ERROR;
+      break;
+    case MONGOC_LOG_LEVEL_WARNING:
+      l = TURN_LOG_LEVEL_WARNING;
+      break;
+    default:
+      l = TURN_LOG_LEVEL_INFO;
+      break;
+  }
+	TURN_LOG_FUNC(l, "%s\n", message);
+}
+
+static void MongoFree(MONGO * info) {
+	if(info) {
+		if(info->uri) mongoc_uri_destroy(info->uri);
+		if(info->client) mongoc_client_destroy(info->client);
+    turn_free(info, sizeof(MONGO));
+	}
+}
+
+static MONGO * get_mongodb_connection(void) {
+
+	persistent_users_db_t * pud = get_persistent_users_db();
+
+	MONGO * mydbconnection = (MONGO *) (pud->connection);
+
+	if (!mydbconnection) {
+		mongoc_init();
+		mongoc_log_set_handler(&mongo_logger, NULL);
+
+		mydbconnection = (MONGO *) turn_malloc(sizeof(MONGO));
+		mydbconnection->uri = mongoc_uri_new(pud->userdb);
+
+		if (!mydbconnection->uri) {
+			TURN_LOG_FUNC(
+					TURN_LOG_LEVEL_ERROR,
+					"Cannot open parse MongoDB URI <%s>, connection string format error\n",
+					pud->userdb);
+			MongoFree(mydbconnection);
+			mydbconnection = NULL;
+		} else {
+			mydbconnection->client = mongoc_client_new_from_uri(
+					mydbconnection->uri);
+			if (!mydbconnection->client) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,
+						"Cannot initialize MongoDB connection\n");
+				MongoFree(mydbconnection);
+				mydbconnection = NULL;
+			} else {
+				mydbconnection->database = mongoc_uri_get_database(
+						mydbconnection->uri);
+				if (!mydbconnection->database)
+					mydbconnection->database = MONGO_DEFAULT_DB;
+				pud->connection = mydbconnection;
+				TURN_LOG_FUNC(
+					TURN_LOG_LEVEL_INFO,
+					"Opened MongoDB URI <%s>\n",
+					pud->userdb);
+			}
+		}
+	}
+	return mydbconnection;
+}
+
+static mongoc_collection_t * mongo_get_collection(const char * name) {
+	MONGO * mc = get_mongodb_connection();
+
+	if(!mc) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error gettting a connection to MongoDB\n");
+		return NULL;
+	}
+    
+  mongoc_collection_t * collection;
+  collection = mongoc_client_get_collection(mc->client, mc->database, name);
+
+  if (!collection) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MongoDB collection '%s'\n", name);
+  }
+
+  return collection;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static int mongo_get_auth_secrets(secrets_list_t *sl, u08bits *realm) {
+  mongoc_collection_t * collection = mongo_get_collection("turn_secret"); 
+
+	if(!collection)
+    return -1;
+    
+  bson_t query;
+  bson_init(&query);
+  BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
+
+  bson_t fields;
+  bson_init(&fields);
+  BSON_APPEND_INT32(&fields, "value", 1);
+  
+  mongoc_cursor_t * cursor;
+  cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
+
+  int ret = -1;
+  
+  if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'turn_secret'\n");
+  } else {
+    const bson_t * item;
+    uint32_t length;
+    bson_iter_t iter;
+    const char * value;
+    while(mongoc_cursor_next(cursor, &item)) {
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "value") && BSON_ITER_HOLDS_UTF8(&iter)) {
+        value = bson_iter_utf8(&iter, &length);
+				add_to_secrets_list(sl, value);
+      }
+    }
+    mongoc_cursor_destroy(cursor);
+    ret = 0;
+  }
+
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  bson_destroy(&fields);
+  return ret;
+}
+  
+static int mongo_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
+  mongoc_collection_t * collection = mongo_get_collection("turnusers_lt"); 
+
+	if(!collection)
+    return -1;
+    
+  bson_t query;
+  bson_init(&query);
+  BSON_APPEND_UTF8(&query, "name", (const char *)usname);
+  BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
+
+  bson_t fields;
+  bson_init(&fields);
+  BSON_APPEND_INT32(&fields, "hmackey", 1);
+  
+  mongoc_cursor_t * cursor;
+  cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 1, 0, &query, &fields, NULL);
+  
+  int ret = -1;
+
+  if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'turnusers_lt'\n");
+  } else {
+    const bson_t * item;
+    uint32_t length;
+    bson_iter_t iter;
+    const char * value;
+    if (mongoc_cursor_next(cursor, &item)) {
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "hmackey") && BSON_ITER_HOLDS_UTF8(&iter)) {
+        value = bson_iter_utf8(&iter, &length);
+				size_t sz = get_hmackey_size(turn_params.shatype) * 2;
+				if(length < sz) {
+					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key format: string length=%d (must be %d): user %s\n", (int)length, (int)sz, usname);
+				} else {
+					char kval[sizeof(hmackey_t) + sizeof(hmackey_t) + 1];
+					ns_bcopy(value, kval, sz);
+					kval[sz] = 0;
+					if(convert_string_key_to_binary(kval, key, sz / 2) < 0) {
+						TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key: %s, user %s\n", kval, usname);
+					} else {
+						ret = 0;
+					}
+				}
+      }
+    }
+    mongoc_cursor_destroy(cursor);
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  bson_destroy(&fields);
+  return ret;
+}
+  
+static int mongo_get_user_pwd(u08bits *usname, st_password_t pwd) {
+  mongoc_collection_t * collection = mongo_get_collection("turnusers_st"); 
+
+	if(!collection)
+    return -1;
+    
+  bson_t query;
+  bson_init(&query);
+  BSON_APPEND_UTF8(&query, "name", (const char *)usname);
+
+  bson_t fields;
+  bson_init(&fields);
+  BSON_APPEND_INT32(&fields, "password", 1);
+  
+  mongoc_cursor_t * cursor;
+  cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 1, 0, &query, &fields, NULL);
+  
+  int ret = -1;
+
+  if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'turnusers_st'\n");
+  } else {
+    const bson_t * item;
+    uint32_t length;
+    bson_iter_t iter;
+    const char * value;
+    if (mongoc_cursor_next(cursor, &item)) {
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "password") && BSON_ITER_HOLDS_UTF8(&iter)) {
+        value = bson_iter_utf8(&iter, &length);
+
+				if(length < 1) {
+					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong password data for user %s, size in MongoDB is zero(0)\n", usname);
+				} else {
+					ns_bcopy(value, pwd, length);
+					pwd[length] = 0;
+					ret = 0;
+				}
+			}
+    }
+    mongoc_cursor_destroy(cursor);
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  bson_destroy(&fields);
+  return ret;
+}
+  
+static int mongo_set_user_key(u08bits *usname, u08bits *realm, const char *key) {
+  mongoc_collection_t * collection = mongo_get_collection("turnusers_lt"); 
+
+	if(!collection)
+    return -1;
+    
+  bson_t query;
+  bson_init(&query);
+  BSON_APPEND_UTF8(&query, "name", (const char *)usname);
+  BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
+      
+  bson_t doc;
+  bson_init(&doc);
+  BSON_APPEND_UTF8(&doc, "name", (const char *)usname);
+  BSON_APPEND_UTF8(&doc, "realm", (const char *)realm);
+  BSON_APPEND_UTF8(&doc, "hmackey", (const char *)key);
+
+  int ret = -1;
+  
+  if (!mongoc_collection_update(collection, MONGOC_UPDATE_UPSERT, &query, &doc, NULL, NULL)) {
+    TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating secret key information\n");
+  } else {
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&doc);
+  bson_destroy(&query);
+  return ret;
+}
+  
+static int mongo_set_user_pwd(u08bits *usname, st_password_t pwd) {
+  mongoc_collection_t * collection = mongo_get_collection("turnusers_st");
+
+	if(!collection)
+    return -1;
+    
+  bson_t query;
+  bson_init(&query);
+  BSON_APPEND_UTF8(&query, "name", (const char *)usname);
+  
+  bson_t doc;
+  bson_init(&doc);
+  BSON_APPEND_UTF8(&doc, "name", (const char *)usname);
+  BSON_APPEND_UTF8(&doc, "password", (const char *)pwd);
+
+  int ret = -1;
+  
+  if (!mongoc_collection_update(collection, MONGOC_UPDATE_UPSERT, &query, &doc, NULL, NULL)) {
+    TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating secret key information\n");
+  } else {
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&doc);
+  bson_destroy(&query);
+  return ret;
+}
+  
+static int mongo_del_user(u08bits *usname, int is_st, u08bits *realm) {
+  mongoc_collection_t * collection = mongo_get_collection(is_st ? "turnusers_st" : "turnusers_lt");
+
+	if(!collection)
+    return -1;
+    
+  bson_t query;
+  bson_init(&query);
+  BSON_APPEND_UTF8(&query, "name", (const char *)usname);
+  if(!is_st) {
+    BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
+  }
+  
+  int ret = -1;    
+
+  if (!mongoc_collection_delete(collection, MONGOC_DELETE_SINGLE_REMOVE, &query, NULL, NULL)) {
+    TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting user key information\n");
+  } else {
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  return ret;
+}
+  
+static int mongo_list_users(int is_st, u08bits *realm) {
+  const char * collection_name = is_st ? "turnusers_st" : "turnusers_lt";
+  mongoc_collection_t * collection = mongo_get_collection(collection_name); 
+
+	if(!collection)
+    return -1;
+    
+  bson_t query, child;
+  bson_init(&query);
+  bson_append_document_begin(&query, "$orderby", -1, &child);
+  bson_append_int32(&child, "name", -1, 1);
+  bson_append_document_end(&query, &child);
+  bson_append_document_begin(&query, "$query", -1, &child);
+  if (!is_st && realm && realm[0]) {
+    BSON_APPEND_UTF8(&child, "realm", (const char *)realm);
+  }
+  bson_append_document_end(&query, &child);
+
+  bson_t fields;
+  bson_init(&fields);
+  BSON_APPEND_INT32(&fields, "name", 1);
+  if(!is_st) BSON_APPEND_INT32(&fields, "realm", 1);
+
+  mongoc_cursor_t * cursor;
+  cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
+
+  int ret = -1;
+  
+  if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection '%s'\n", collection_name);
+  } else {
+    const bson_t * item;
+    uint32_t length;
+    bson_iter_t iter;
+    bson_iter_t iter_realm;
+    const char * value;
+    while (mongoc_cursor_next(cursor, &item)) {
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "name") && BSON_ITER_HOLDS_UTF8(&iter)) {
+    		value = bson_iter_utf8(&iter, &length);
+    		if (length) {
+        		const char *realm = "";
+    			if (!is_st && bson_iter_init(&iter_realm, item) && bson_iter_find(&iter_realm, "realm") && BSON_ITER_HOLDS_UTF8(&iter_realm)) {
+    				realm = bson_iter_utf8(&iter_realm, &length);
+    			}
+    			if(realm && *realm) {
+    				printf("%s[%s]\n", value, realm);
+    			} else {
+    				printf("%s\n", value);
+    			}
+    		}
+    	}
+    }
+    mongoc_cursor_destroy(cursor);
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  bson_destroy(&fields);
+  return ret;
+}
+  
+static int mongo_show_secret(u08bits *realm) {
+  mongoc_collection_t * collection = mongo_get_collection("turn_secret"); 
+
+	if(!collection)
+    return -1;
+    
+  bson_t query;
+  bson_init(&query);
+  BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
+
+  bson_t fields;
+  bson_init(&fields);
+  BSON_APPEND_INT32(&fields, "value", 1);
+
+  mongoc_cursor_t * cursor;
+  cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
+
+  int ret = -1;
+  
+  if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'turn_secret'\n");
+  } else {
+    const bson_t * item;
+    uint32_t length;
+    bson_iter_t iter;
+    const char * value;
+    while (mongoc_cursor_next(cursor, &item)) {
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "value") && BSON_ITER_HOLDS_UTF8(&iter)) {
+        value = bson_iter_utf8(&iter, &length);
+        if (length) {
+					printf("%s\n", value);
+        }
+      }
+    }
+    mongoc_cursor_destroy(cursor);
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  bson_destroy(&fields);
+  return ret;
+}
+  
+static int mongo_del_secret(u08bits *secret, u08bits *realm) {
+  mongoc_collection_t * collection = mongo_get_collection("turn_secret"); 
+
+	if(!collection)
+    return -1;
+    
+  bson_t query;
+  bson_init(&query);
+  BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
+	if(secret && (secret[0]!=0)) {
+    BSON_APPEND_UTF8(&query, "value", (const char *)secret);
+	}
+
+  mongoc_collection_delete(collection, MONGOC_DELETE_NONE, &query, NULL, NULL);
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  return 0;
+}
+  
+static int mongo_set_secret(u08bits *secret, u08bits *realm) {
+  mongoc_collection_t * collection = mongo_get_collection("turn_secret"); 
+
+	if(!collection)
+    return -1;
+    
+  bson_t query;
+  bson_init(&query);
+  BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
+  BSON_APPEND_UTF8(&query, "value", (const char *)secret);
+
+  int res = mongoc_collection_insert(collection, MONGOC_INSERT_NONE, &query, NULL, NULL);
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+
+  if (!res) {
+    TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating secret key information\n");
+    return -1;
+  } else {
+    return 0;
+  }
+}
+  
+static int mongo_add_origin(u08bits *origin, u08bits *realm) {
+  mongoc_collection_t * collection = mongo_get_collection("realm"); 
+
+	if(!collection)
+    return -1;
+    
+  int ret = -1;
+  
+  bson_t query, doc, child;
+  bson_init(&query);
+  BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
+  bson_init(&doc);
+  bson_append_document_begin(&doc, "$addToSet", -1, &child);
+  BSON_APPEND_UTF8(&child, "origin", (const char *)origin);
+  bson_append_document_end(&doc, &child);
+
+  if (!mongoc_collection_update(collection, MONGOC_UPDATE_UPSERT, &query, &doc, NULL, NULL)) {
+    TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating realm origin information\n");
+  } else {
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  bson_destroy(&doc);
+  return ret;
+}
+  
+static int mongo_del_origin(u08bits *origin) {
+  mongoc_collection_t * collection = mongo_get_collection("realm"); 
+
+	if(!collection)
+    return -1;
+    
+  int ret = -1;
+  
+  bson_t query, doc, child;
+  bson_init(&query);
+  bson_init(&doc);
+  bson_append_document_begin(&doc, "$pull", -1, &child);
+  BSON_APPEND_UTF8(&child, "origin", (const char *)origin);
+  bson_append_document_end(&doc, &child);
+
+  if (!mongoc_collection_update(collection, MONGOC_UPDATE_MULTI_UPDATE, &query, &doc, NULL, NULL)) {
+    TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting origin information\n");
+  } else {
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  bson_destroy(&doc);
+  return ret;
+}
+  
+static int mongo_list_origins(u08bits *realm) {
+  mongoc_collection_t * collection = mongo_get_collection("realm"); 
+
+	if(!collection)
+    return -1;
+
+  bson_t query, child;
+  bson_init(&query);
+  bson_append_document_begin(&query, "$orderby", -1, &child);
+  BSON_APPEND_INT32(&child, "realm", 1);
+  bson_append_document_end(&query, &child);
+  bson_append_document_begin(&query, "$query", -1, &child);
+  if (realm && realm[0]) {
+    BSON_APPEND_UTF8(&child, "realm", (const char *)realm);
+  }
+  bson_append_document_end(&query, &child);
+
+  bson_t fields;
+  bson_init(&fields);
+  BSON_APPEND_INT32(&fields, "origin", 1);
+  BSON_APPEND_INT32(&fields, "realm", 1);
+  
+  mongoc_cursor_t * cursor;
+  cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
+
+  int ret = -1;
+  
+  if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'realm'\n");
+  } else {
+    const bson_t * item;
+    uint32_t length;
+    bson_iter_t iter;
+
+    while (mongoc_cursor_next(cursor, &item)) {
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) {
+        const char * _realm = bson_iter_utf8(&iter, &length);
+
+        if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "origin") && BSON_ITER_HOLDS_ARRAY(&iter)) {
+          const uint8_t *docbuf = NULL;
+          uint32_t doclen = 0;
+          bson_t origin_array;
+          bson_iter_t origin_iter;
+
+          bson_iter_array(&iter, &doclen, &docbuf);
+          bson_init_static(&origin_array, docbuf, doclen);
+
+          if (bson_iter_init(&origin_iter, &origin_array)) {
+            while(bson_iter_next(&origin_iter)) {
+              if (BSON_ITER_HOLDS_UTF8(&origin_iter)) {
+                const char * _origin = bson_iter_utf8(&origin_iter, &length);
+  							printf("%s ==>> %s\n", _realm, _origin);
+              }
+            }
+          }
+        }
+      }
+    }
+    mongoc_cursor_destroy(cursor);
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  bson_destroy(&fields);
+  return ret;
+}
+  
+static int mongo_set_realm_option_one(u08bits *realm, unsigned long value, const char* opt) {
+  mongoc_collection_t * collection = mongo_get_collection("realm"); 
+
+	if(!collection)
+    return -1;
+    
+  bson_t query, doc, child;
+  bson_init(&query);
+  BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
+  bson_init(&doc);
+  
+  char * _k = (char *)turn_malloc(9 + strlen(opt));
+  strcpy(_k, "options.");
+  strcat(_k, opt);
+  
+  if (value > 0) {
+    bson_append_document_begin(&doc, "$set", -1, &child);
+    BSON_APPEND_INT32(&child, _k, (int32_t)value);
+    bson_append_document_end(&doc, &child);
+  } else {
+    bson_append_document_begin(&doc, "$unset", -1, &child);
+    BSON_APPEND_INT32(&child, _k, 1);
+    bson_append_document_end(&doc, &child);
+  }
+  free(_k);
+  
+  int ret = -1;
+  
+  if (!mongoc_collection_update(collection, MONGOC_UPDATE_MULTI_UPDATE, &query, &doc, NULL, NULL)) {
+    TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting origin information\n");
+  } else {
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  bson_destroy(&doc);
+  return ret;
+}
+  
+static int mongo_list_realm_options(u08bits *realm) {
+  mongoc_collection_t * collection = mongo_get_collection("realm"); 
+
+	if(!collection)
+    return -1;
+    
+  bson_t query, child;
+  bson_init(&query);
+  bson_append_document_begin(&query, "$orderby", -1, &child);
+  BSON_APPEND_INT32(&child, "realm", 1);
+  bson_append_document_end(&query, &child);
+  bson_append_document_begin(&query, "$query", -1, &child);
+  if (realm && realm[0]) {
+    BSON_APPEND_UTF8(&child, "realm", (const char *)realm);
+  }
+  bson_append_document_end(&query, &child);
+
+  bson_t fields;
+  bson_init(&fields);
+  BSON_APPEND_INT32(&fields, "options", 1);
+  BSON_APPEND_INT32(&fields, "realm", 1);
+  
+  mongoc_cursor_t * cursor;
+  cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
+
+  int ret = -1;
+  
+  if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'realm'\n");
+  } else {
+    const bson_t * item;
+    uint32_t length;
+    bson_iter_t iter;
+
+    while (mongoc_cursor_next(cursor, &item)) {
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) {
+        const char * _realm = bson_iter_utf8(&iter, &length);
+
+        if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "options") && BSON_ITER_HOLDS_DOCUMENT(&iter)) {
+          const uint8_t *docbuf = NULL;
+          uint32_t doclen = 0;
+          bson_t options;
+          bson_iter_t options_iter;
+
+          bson_iter_document(&iter, &doclen, &docbuf);
+          bson_init_static(&options, docbuf, doclen);
+
+          if (bson_iter_init(&options_iter, &options)) {
+            while(bson_iter_next(&options_iter)) {
+              const char * _k = bson_iter_key(&options_iter);
+              if (BSON_ITER_HOLDS_DOUBLE(&options_iter)) {
+                int32_t _v = (int32_t)bson_iter_double(&options_iter);
+								printf("%s[%s]=%d\n", _k, _realm, _v);
+              } else if (BSON_ITER_HOLDS_INT32(&options_iter)) {
+                int32_t _v = bson_iter_int32(&options_iter);
+								printf("%s[%s]=%d\n", _k, _realm, _v);
+              } else if (BSON_ITER_HOLDS_INT64(&options_iter)) {
+                int32_t _v = (int32_t)bson_iter_int64(&options_iter);
+								printf("%s[%s]=%d\n", _k, _realm, _v);
+              }
+            }
+          }
+        }
+      }
+    }
+    mongoc_cursor_destroy(cursor);
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  bson_destroy(&fields);
+  return ret;
+}
+  
+static void mongo_auth_ping(void * rch) {
+	UNUSED_ARG(rch);
+  // NOOP
+}
+  
+static int mongo_get_ip_list(const char *kind, ip_range_list_t * list) {
+  char * collection_name = 	(char *)turn_malloc(strlen(kind) + 9);
+  sprintf(collection_name, "%s_peer_ip", kind);
+  mongoc_collection_t * collection = mongo_get_collection(collection_name); 
+  turn_free(collection_name, strlen(kind) + 9);
+
+	if(!collection)
+    return -1;
+    
+  bson_t query;
+  bson_init(&query);
+  
+  bson_t fields;
+  bson_init(&fields);
+  BSON_APPEND_INT32(&fields, "ip_range", 1);
+  
+  mongoc_cursor_t * cursor;
+  cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
+  
+  int ret = -1;
+  
+  if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection '%s'\n", collection_name);
+  } else {
+    const bson_t * item;
+    uint32_t length;
+    bson_iter_t iter;
+    const char * value;
+    while(mongoc_cursor_next(cursor, &item)) {
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "ip_range") && BSON_ITER_HOLDS_UTF8(&iter)) {
+        value = bson_iter_utf8(&iter, &length);
+		add_ip_list_range(value, list);
+      }
+    }
+    mongoc_cursor_destroy(cursor);
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  bson_destroy(&fields);
+  return ret;
+}
+  
+static void mongo_reread_realms(secrets_list_t * realms_list) {
+	UNUSED_ARG(realms_list);
+
+  mongoc_collection_t * collection = mongo_get_collection("realm"); 
+
+	if(!collection)
+    return;
+
+  bson_t query;
+  bson_init(&query);
+
+  bson_t fields;
+  bson_init(&fields);
+  BSON_APPEND_INT32(&fields, "realm", 1);
+  BSON_APPEND_INT32(&fields, "origin", 1);
+  BSON_APPEND_INT32(&fields, "options", 1);
+  
+  mongoc_cursor_t * cursor;
+  cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
+
+  if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'realm'\n");
+  } else {
+		ur_string_map *o_to_realm_new = ur_string_map_create(free);
+
+    const bson_t * item;
+    uint32_t length;
+    bson_iter_t iter;
+
+    while (mongoc_cursor_next(cursor, &item)) {
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) {
+        char * _realm = strdup(bson_iter_utf8(&iter, &length));
+
+        get_realm(_realm);
+
+        if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "origin") && BSON_ITER_HOLDS_ARRAY(&iter)) {
+          const uint8_t *docbuf = NULL;
+          uint32_t doclen = 0;
+          bson_t origin_array;
+          bson_iter_t origin_iter;
+
+          bson_iter_array(&iter, &doclen, &docbuf);
+          bson_init_static(&origin_array, docbuf, doclen);
+
+          if (bson_iter_init(&origin_iter, &origin_array)) {
+            while(bson_iter_next(&origin_iter)) {
+              if (BSON_ITER_HOLDS_UTF8(&origin_iter)) {
+                char * _origin = strdup(bson_iter_utf8(&origin_iter, &length));
+				char *rval = strdup(_realm);
+				ur_string_map_value_type value = (ur_string_map_value_type)(rval);
+				ur_string_map_put(o_to_realm_new, (const ur_string_map_key_type) _origin, value);
+				free(_origin);
+              }
+            }
+          }
+        }
+        
+  			realm_params_t* rp = get_realm(_realm);
+  			lock_realms();
+  			rp->options.perf_options.max_bps = turn_params.max_bps;
+  			rp->options.perf_options.total_quota = turn_params.total_quota;
+  			rp->options.perf_options.user_quota = turn_params.user_quota;
+  			unlock_realms();
+
+        if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "options") && BSON_ITER_HOLDS_DOCUMENT(&iter)) {
+          const uint8_t *docbuf = NULL;
+          uint32_t doclen = 0;
+          bson_t options;
+          bson_iter_t options_iter;
+
+          bson_iter_document(&iter, &doclen, &docbuf);
+          bson_init_static(&options, docbuf, doclen);
+
+          if (bson_iter_init(&options_iter, &options)) {
+            while(bson_iter_next(&options_iter)) {
+              const char * _k = bson_iter_key(&options_iter);
+              int32_t _v = 0;
+              if (BSON_ITER_HOLDS_DOUBLE(&options_iter)) {
+                _v = (int32_t)bson_iter_double(&options_iter);
+              } else if (BSON_ITER_HOLDS_INT32(&options_iter)) {
+                _v = bson_iter_int32(&options_iter);
+              } else if (BSON_ITER_HOLDS_INT64(&options_iter)) {
+                _v = (int32_t)bson_iter_int64(&options_iter);
+              }
+              if (_v) {
+								if(!strcmp(_k,"max-bps"))
+									rp->options.perf_options.max_bps = (band_limit_t)_v;
+								else if(!strcmp(_k,"total-quota"))
+									rp->options.perf_options.total_quota = (vint)_v;
+								else if(!strcmp(_k,"user-quota"))
+									rp->options.perf_options.user_quota = (vint)_v;
+								else {
+									TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown realm option: %s\n", _k);
+								}
+              }
+            }
+          }
+        }
+        free(_realm);
+      }
+    }
+    update_o_to_realm(o_to_realm_new);
+    mongoc_cursor_destroy(cursor);
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  bson_destroy(&fields);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static turn_dbdriver_t driver = {
+  &mongo_get_auth_secrets,
+  &mongo_get_user_key,
+  &mongo_get_user_pwd,
+  &mongo_set_user_key,
+  &mongo_set_user_pwd,
+  &mongo_del_user,
+  &mongo_list_users,
+  &mongo_show_secret,
+  &mongo_del_secret,
+  &mongo_set_secret,
+  &mongo_add_origin,
+  &mongo_del_origin,
+  &mongo_list_origins,
+  &mongo_set_realm_option_one,
+  &mongo_list_realm_options,
+  &mongo_auth_ping,
+  &mongo_get_ip_list,
+  &mongo_reread_realms
+};
+
+turn_dbdriver_t * get_mongo_dbdriver(void) {
+  return &driver;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#else
+
+turn_dbdriver_t * get_mongo_dbdriver(void) {
+  return NULL;
+}
+
+#endif

+ 49 - 0
src/apps/relay/dbdrivers/dbd_mongo.h

@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011, 2012, 2013 Citrix Systems
+ * Copyright (C) 2014 Vivocha S.p.A.
+ *
+ * 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 __DBD_MONGODB__
+#define __DBD_MONGODB__
+
+#include "dbdriver.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+turn_dbdriver_t * get_mongo_dbdriver(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/// __DBD_MONGODB__///
+

+ 874 - 0
src/apps/relay/dbdrivers/dbd_mysql.c

@@ -0,0 +1,874 @@
+/*
+ * Copyright (C) 2011, 2012, 2013 Citrix Systems
+ * Copyright (C) 2014 Vivocha S.p.A.
+ *
+ * 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"
+#include "dbd_mysql.h"
+
+#if !defined(TURN_NO_MYSQL)
+#include <mysql.h>
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static int donot_print_connection_success = 0;
+
+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) {
+	persistent_users_db_t *pud = get_persistent_users_db();
+
+	MYSQL *mydbconnection = (MYSQL*)(pud->connection);
+
+	if(mydbconnection) {
+		if(mysql_ping(mydbconnection)) {
+			mysql_close(mydbconnection);
+			mydbconnection=NULL;
+		}
+	}
+
+	if(!mydbconnection) {
+		char *errmsg=NULL;
+		Myconninfo *co=MyconninfoParse(pud->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",pud->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",pud->userdb);
+			}
+		} else if(errmsg) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open MySQL DB connection <%s>, connection string format error: %s\n",pud->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",pud->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",pud->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",pud->userdb);
+				}
+			}
+			MyconninfoFree(co);
+		}
+		pud->connection = mydbconnection;
+	}
+	return mydbconnection;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static int mysql_get_auth_secrets(secrets_list_t *sl, u08bits *realm) {
+  int ret = -1;
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		char statement[TURN_LONG_STRING_SIZE];
+		snprintf(statement,sizeof(statement)-1,"select value from turn_secret where realm='%s'",realm);
+		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) {
+				for(;;) {
+					MYSQL_ROW row = mysql_fetch_row(mres);
+					if(!row) {
+						break;
+					} else {
+						if(row[0]) {
+							unsigned long *lengths = mysql_fetch_lengths(mres);
+							if(lengths) {
+								size_t sz = lengths[0];
+								char auth_secret[TURN_LONG_STRING_SIZE];
+								ns_bcopy(row[0],auth_secret,sz);
+								auth_secret[sz]=0;
+								add_to_secrets_list(sl,auth_secret);
+							}
+						}
+					}
+				}
+				ret = 0;
+			}
+
+			if(mres)
+				mysql_free_result(mres);
+		}
+	}
+  return ret;
+}
+  
+static int mysql_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
+  int ret = -1;
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		char statement[TURN_LONG_STRING_SIZE];
+		snprintf(statement,sizeof(statement),"select hmackey from turnusers_lt where name='%s' and realm='%s'",usname,realm);
+		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) {
+						size_t sz = get_hmackey_size(turn_params.shatype)*2;
+						if(lengths[0]<sz) {
+							TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key format: string length=%d (must be %d): user %s\n",(int)lengths[0],(int)sz,usname);
+						} else {
+							char kval[sizeof(hmackey_t)+sizeof(hmackey_t)+1];
+							ns_bcopy(row[0],kval,sz);
+							kval[sz]=0;
+							if(convert_string_key_to_binary(kval, key, sz/2)<0) {
+								TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key: %s, user %s\n",kval,usname);
+							} else {
+								ret = 0;
+							}
+						}
+					}
+				}
+			}
+
+			if(mres)
+				mysql_free_result(mres);
+		}
+	}
+  return ret;
+}
+  
+static int mysql_get_user_pwd(u08bits *usname, st_password_t pwd) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	snprintf(statement,sizeof(statement),"select password from turnusers_st where name='%s'",usname);
+
+	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);
+		}
+	}
+  return ret;
+}
+  
+static int mysql_set_user_key(u08bits *usname, u08bits *realm, const char *key) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+	  snprintf(statement,sizeof(statement),"insert into turnusers_lt (realm,name,hmackey) values('%s','%s','%s')",realm,usname,key);
+		int res = mysql_query(myc, statement);
+		if(res) {
+		  snprintf(statement,sizeof(statement),"update turnusers_lt set hmackey='%s' where name='%s' and realm='%s'",key,usname,realm);
+			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));
+			}
+		}
+	}
+  return ret;
+}
+  
+static int mysql_set_user_pwd(u08bits *usname, st_password_t pwd) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+	  snprintf(statement,sizeof(statement),"insert into turnusers_st values('%s','%s')",usname,pwd);
+		int res = mysql_query(myc, statement);
+		if(res) {
+		  snprintf(statement,sizeof(statement),"update turnusers_st set password='%s' where name='%s'",pwd,usname);
+			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));
+			} else {
+			  ret = 0;
+			}
+		}
+	}
+  return ret;
+}
+  
+static int mysql_del_user(u08bits *usname, int is_st, u08bits *realm) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		if(is_st) {
+		  snprintf(statement,sizeof(statement),"delete from turnusers_st where name='%s'",usname);
+		} else {
+		  snprintf(statement,sizeof(statement),"delete from turnusers_lt where name='%s' and realm='%s'",usname,realm);
+		}
+		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));
+		} else {
+		  ret = 0;
+		}
+	}
+  return ret;
+}
+  
+static int mysql_list_users(int is_st, u08bits *realm) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		if(is_st) {
+		  snprintf(statement,sizeof(statement),"select name,'' from turnusers_st order by name");
+		} else if(realm && realm[0]) {
+		  snprintf(statement,sizeof(statement),"select name, realm from turnusers_lt where realm='%s' order by name",realm);
+		} else {
+		  snprintf(statement,sizeof(statement),"select name, realm from turnusers_lt order by name");
+		}
+		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)!=2) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement);
+			} else {
+				for(;;) {
+					MYSQL_ROW row = mysql_fetch_row(mres);
+					if(!row) {
+						break;
+					} else {
+						if(row[0]) {
+							if(row[1] && row[1][0]) {
+								printf("%s[%s]\n",row[0],row[1]);
+							} else {
+								printf("%s\n",row[0]);
+							}
+						}
+					}
+				}
+        ret = 0;
+			}
+
+			if(mres)
+				mysql_free_result(mres);
+		}
+	}
+  return ret;
+}
+  
+static int mysql_show_secret(u08bits *realm) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	snprintf(statement,sizeof(statement)-1,"select value from turn_secret where realm='%s'",realm);
+
+	donot_print_connection_success=1;
+
+	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 {
+				for(;;) {
+					MYSQL_ROW row = mysql_fetch_row(mres);
+					if(!row) {
+						break;
+					} else {
+						if(row[0]) {
+							printf("%s\n",row[0]);
+						}
+					}
+				}
+        ret = 0;
+			}
+
+			if(mres)
+				mysql_free_result(mres);
+		}
+	}
+  return ret;
+}
+  
+static int mysql_del_secret(u08bits *secret, u08bits *realm) {
+  int ret = 1;
+	donot_print_connection_success=1;
+	char statement[TURN_LONG_STRING_SIZE];
+	MYSQL * myc = get_mydb_connection();
+	if (myc) {
+		if(!secret || (secret[0]==0))
+		  snprintf(statement,sizeof(statement),"delete from turn_secret where realm='%s'",realm);
+		else
+		  snprintf(statement,sizeof(statement),"delete from turn_secret where value='%s' and realm='%s'",secret,realm);
+		mysql_query(myc, statement);
+    ret = 0;
+	}
+  return ret;
+}
+  
+static int mysql_set_secret(u08bits *secret, u08bits *realm) {
+  int ret = 1;
+	donot_print_connection_success = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	MYSQL * myc = get_mydb_connection();
+	if (myc) {
+	  snprintf(statement,sizeof(statement),"insert into turn_secret (realm,value) values('%s','%s')",realm,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));
+	  } else {
+	    ret = 0;
+	  }
+	}
+  return ret;
+}
+  
+static int mysql_add_origin(u08bits *origin, u08bits *realm) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	MYSQL * myc = get_mydb_connection();
+	if (myc) {
+		snprintf(statement,sizeof(statement),"insert into turn_origin_to_realm (origin,realm) values('%s','%s')",origin,realm);
+		int res = mysql_query(myc, statement);
+		if (res) {
+			TURN_LOG_FUNC(
+			  TURN_LOG_LEVEL_ERROR,
+			  "Error inserting origin information: %s\n",
+			  mysql_error(myc));
+		} else {
+		  ret = 0;
+		}
+	}
+  return ret;
+}
+  
+static int mysql_del_origin(u08bits *origin) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	MYSQL * myc = get_mydb_connection();
+	if (myc) {
+		snprintf(statement,sizeof(statement),"delete from turn_origin_to_realm where origin='%s'",origin);
+		int res = mysql_query(myc, statement);
+		if (res) {
+			TURN_LOG_FUNC(
+			  TURN_LOG_LEVEL_ERROR,
+			  "Error deleting origin information: %s\n",
+			  mysql_error(myc));
+		} else {
+		  ret = 0;
+		}
+	}
+  return ret;
+}
+  
+static int mysql_list_origins(u08bits *realm) {
+  int ret = 1;
+	donot_print_connection_success = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		if(realm && realm[0]) {
+			snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm where realm='%s' order by origin",realm);
+		} else {
+			snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm order by origin,realm");
+		}
+		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)!=2) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement);
+			} else {
+				for(;;) {
+					MYSQL_ROW row = mysql_fetch_row(mres);
+					if(!row) {
+						break;
+					} else {
+						if(row[0] && row[1]) {
+							printf("%s ==>> %s\n",row[0],row[1]);
+						}
+					}
+				}
+        ret = 0;
+			}
+
+			if(mres)
+				mysql_free_result(mres);
+		}
+	}
+  return ret;
+}
+  
+static int mysql_set_realm_option_one(u08bits *realm, unsigned long value, const char* opt) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	MYSQL * myc = get_mydb_connection();
+	if (myc) {
+		{
+			snprintf(statement,sizeof(statement),"delete from turn_realm_option where realm='%s' and opt='%s'",realm,opt);
+			mysql_query(myc, statement);
+		}
+		if(value>0) {
+			snprintf(statement,sizeof(statement),"insert into turn_realm_option (realm,opt,value) values('%s','%s','%lu')",realm,opt,(unsigned long)value);
+			int res = mysql_query(myc, statement);
+			if (res) {
+				TURN_LOG_FUNC(
+							TURN_LOG_LEVEL_ERROR,
+							"Error inserting realm option information: %s\n",
+							mysql_error(myc));
+			} else {
+			  ret = 0;
+			}
+		}
+	}
+  return ret;
+}
+  
+static int mysql_list_realm_options(u08bits *realm) {
+  int ret = 1;
+	donot_print_connection_success = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		if(realm && realm[0]) {
+			snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option where realm='%s' order by realm,opt",realm);
+		} else {
+			snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option order by realm,opt");
+		}
+		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)!=3) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement);
+			} else {
+				for(;;) {
+					MYSQL_ROW row = mysql_fetch_row(mres);
+					if(!row) {
+						break;
+					} else {
+						if(row[0] && row[1] && row[2]) {
+							printf("%s[%s]=%s\n",row[1],row[0],row[2]);
+						}
+					}
+				}
+        ret = 0;
+			}
+
+			if(mres)
+				mysql_free_result(mres);
+		}
+	}
+  return ret;
+}
+  
+static void mysql_auth_ping(void * rch) {
+	UNUSED_ARG(rch);
+	donot_print_connection_success = 1;
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		char statement[TURN_LONG_STRING_SIZE];
+		STRCPY(statement,"select value from turn_secret");
+		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 {
+				mysql_free_result(mres);
+			}
+		}
+	}
+}
+  
+static int mysql_get_ip_list(const char *kind, ip_range_list_t * list) {
+  int ret = 1;
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		char statement[TURN_LONG_STRING_SIZE];
+		snprintf(statement,sizeof(statement),"select ip_range from %s_peer_ip",kind);
+		int res = mysql_query(myc, statement);
+		if(res == 0) {
+			MYSQL_RES *mres = mysql_store_result(myc);
+			if(mres && mysql_field_count(myc)==1) {
+				for(;;) {
+					MYSQL_ROW row = mysql_fetch_row(mres);
+					if(!row) {
+						break;
+					} else {
+						if(row[0]) {
+							unsigned long *lengths = mysql_fetch_lengths(mres);
+							if(lengths) {
+								size_t sz = lengths[0];
+								char kval[TURN_LONG_STRING_SIZE];
+								ns_bcopy(row[0],kval,sz);
+								kval[sz]=0;
+								add_ip_list_range(kval,list);
+							}
+						}
+					}
+				}
+        ret = 0;
+			}
+
+			if(mres)
+				mysql_free_result(mres);
+		}
+	}
+  return ret;
+}
+  
+static void mysql_reread_realms(secrets_list_t * realms_list) {
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		char statement[TURN_LONG_STRING_SIZE];
+		{
+			snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm");
+			int res = mysql_query(myc, statement);
+			if(res == 0) {
+				MYSQL_RES *mres = mysql_store_result(myc);
+				if(mres && mysql_field_count(myc)==2) {
+
+					ur_string_map *o_to_realm_new = ur_string_map_create(free);
+
+					for(;;) {
+						MYSQL_ROW row = mysql_fetch_row(mres);
+						if(!row) {
+							break;
+						} else {
+							if(row[0] && row[1]) {
+								unsigned long *lengths = mysql_fetch_lengths(mres);
+								if(lengths) {
+									size_t sz = lengths[0];
+									char oval[513];
+									ns_bcopy(row[0],oval,sz);
+									oval[sz]=0;
+									char *rval=strdup(row[1]);
+									get_realm(rval);
+									ur_string_map_value_type value = (ur_string_map_value_type)rval;
+									ur_string_map_put(o_to_realm_new, (const ur_string_map_key_type) oval, value);
+								}
+							}
+						}
+					}
+
+					update_o_to_realm(o_to_realm_new);
+				}
+
+				if(mres)
+					mysql_free_result(mres);
+			}
+		}
+		{
+			size_t i = 0;
+			size_t rlsz = 0;
+
+			lock_realms();
+			rlsz = realms_list->sz;
+			unlock_realms();
+
+			for (i = 0; i<rlsz; ++i) {
+
+				char *realm = realms_list->secrets[i];
+
+				realm_params_t* rp = get_realm(realm);
+
+				lock_realms();
+				rp->options.perf_options.max_bps = turn_params.max_bps;
+				unlock_realms();
+
+				lock_realms();
+				rp->options.perf_options.total_quota = turn_params.total_quota;
+				unlock_realms();
+
+				lock_realms();
+				rp->options.perf_options.user_quota = turn_params.user_quota;
+				unlock_realms();
+
+			}
+		}
+
+		snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option");
+		int res = mysql_query(myc, statement);
+		if(res == 0) {
+			MYSQL_RES *mres = mysql_store_result(myc);
+			if(mres && mysql_field_count(myc)==3) {
+
+				for(;;) {
+					MYSQL_ROW row = mysql_fetch_row(mres);
+					if(!row) {
+						break;
+					} else {
+						if(row[0] && row[1] && row[2]) {
+							unsigned long *lengths = mysql_fetch_lengths(mres);
+							if(lengths) {
+								char rval[513];
+								size_t sz = lengths[0];
+								ns_bcopy(row[0],rval,sz);
+								rval[sz]=0;
+								char oval[513];
+								sz = lengths[1];
+								ns_bcopy(row[1],oval,sz);
+								oval[sz]=0;
+								char vval[513];
+								sz = lengths[2];
+								ns_bcopy(row[2],vval,sz);
+								vval[sz]=0;
+								realm_params_t* rp = get_realm(rval);
+								if(!strcmp(oval,"max-bps"))
+									rp->options.perf_options.max_bps = (band_limit_t)atol(vval);
+								else if(!strcmp(oval,"total-quota"))
+									rp->options.perf_options.total_quota = (vint)atoi(vval);
+								else if(!strcmp(oval,"user-quota"))
+									rp->options.perf_options.user_quota = (vint)atoi(vval);
+								else {
+									TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown realm option: %s\n", oval);
+								}
+							}
+						}
+					}
+				}
+			}
+
+			if(mres)
+				mysql_free_result(mres);
+		}
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static turn_dbdriver_t driver = {
+  &mysql_get_auth_secrets,
+  &mysql_get_user_key,
+  &mysql_get_user_pwd,
+  &mysql_set_user_key,
+  &mysql_set_user_pwd,
+  &mysql_del_user,
+  &mysql_list_users,
+  &mysql_show_secret,
+  &mysql_del_secret,
+  &mysql_set_secret,
+  &mysql_add_origin,
+  &mysql_del_origin,
+  &mysql_list_origins,
+  &mysql_set_realm_option_one,
+  &mysql_list_realm_options,
+  &mysql_auth_ping,
+  &mysql_get_ip_list,
+  &mysql_reread_realms
+};
+
+turn_dbdriver_t * get_mysql_dbdriver(void) {
+  return &driver;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#else
+
+turn_dbdriver_t * get_mysql_dbdriver(void) {
+  return NULL;
+}
+
+#endif

+ 49 - 0
src/apps/relay/dbdrivers/dbd_mysql.h

@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011, 2012, 2013 Citrix Systems
+ * Copyright (C) 2014 Vivocha S.p.A.
+ *
+ * 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 __DBD_MYSQL__
+#define __DBD_MYSQL__
+
+#include "dbdriver.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+turn_dbdriver_t * get_mysql_dbdriver(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/// __DBD_MYSQL__///
+

+ 673 - 0
src/apps/relay/dbdrivers/dbd_pgsql.c

@@ -0,0 +1,673 @@
+/*
+ * Copyright (C) 2011, 2012, 2013 Citrix Systems
+ * Copyright (C) 2014 Vivocha S.p.A.
+ *
+ * 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"
+#include "dbd_pgsql.h"
+
+#if !defined(TURN_NO_PQ)
+#include <libpq-fe.h>
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static int donot_print_connection_success = 0;
+
+static PGconn *get_pqdb_connection(void) {
+	persistent_users_db_t *pud = get_persistent_users_db();
+
+	PGconn *pqdbconnection = (PGconn*)(pud->connection);
+	if(pqdbconnection) {
+		ConnStatusType status = PQstatus(pqdbconnection);
+		if(status != CONNECTION_OK) {
+			PQfinish(pqdbconnection);
+			pqdbconnection = NULL;
+		}
+	}
+	if(!pqdbconnection) {
+		char *errmsg=NULL;
+		PQconninfoOption *co = PQconninfoParse(pud->userdb, &errmsg);
+		if(!co) {
+			if(errmsg) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection <%s>, connection string format error: %s\n",pud->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",pud->userdb);
+			}
+		} else {
+			PQconninfoFree(co);
+			if(errmsg)
+				turn_free(errmsg,strlen(errmsg)+1);
+			pqdbconnection = PQconnectdb(pud->userdb);
+			if(!pqdbconnection) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection: <%s>, runtime error\n",pud->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",pud->userdb);
+				} else if(!donot_print_connection_success){
+					TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "PostgreSQL DB connection success: %s\n",pud->userdb);
+				}
+			}
+		}
+		pud->connection = pqdbconnection;
+	}
+	return pqdbconnection;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static int pgsql_get_auth_secrets(secrets_list_t *sl, u08bits *realm) {
+  int ret = 1;
+	PGconn * pqc = get_pqdb_connection();
+	if(pqc) {
+		char statement[TURN_LONG_STRING_SIZE];
+		snprintf(statement,sizeof(statement)-1,"select value from turn_secret where realm='%s'",realm);
+		PGresult *res = PQexec(pqc, statement);
+
+		if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc));
+		} else {
+			int i = 0;
+			for(i=0;i<PQntuples(res);i++) {
+				char *kval = PQgetvalue(res,i,0);
+				if(kval) {
+					add_to_secrets_list(sl,kval);
+				}
+			}
+			ret = 0;
+		}
+
+		if(res) {
+			PQclear(res);
+		}
+	}
+  return ret;
+}
+  
+static int pgsql_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
+  int ret = 1;
+	PGconn * pqc = get_pqdb_connection();
+	if(pqc) {
+		char statement[TURN_LONG_STRING_SIZE];
+		snprintf(statement,sizeof(statement),"select hmackey from turnusers_lt where name='%s' and realm='%s'",usname,realm);
+		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)len<sz*2)||(strlen(kval)<sz*2)) {
+					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key format: %s, user %s\n",kval,usname);
+				} else if(convert_string_key_to_binary(kval, key, sz)<0) {
+					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key: %s, user %s\n",kval,usname);
+				} else {
+					ret = 0;
+				}
+			} else {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong hmackey data for user %s: NULL\n",usname);
+			}
+		}
+
+		if(res)
+			PQclear(res);
+
+	}
+  return ret;
+}
+  
+static int pgsql_get_user_pwd(u08bits *usname, st_password_t pwd) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	snprintf(statement,sizeof(statement),"select password from turnusers_st where name='%s'",usname);
+
+	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);
+		}
+	}
+  return ret;
+}
+  
+static int pgsql_set_user_key(u08bits *usname, u08bits *realm, const char *key) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	PGconn *pqc = get_pqdb_connection();
+	if(pqc) {
+	  snprintf(statement,sizeof(statement),"insert into turnusers_lt (realm,name,hmackey) values('%s','%s','%s')",realm,usname,key);
+
+		PGresult *res = PQexec(pqc, statement);
+		if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
+			if(res) {
+				PQclear(res);
+			}
+		  snprintf(statement,sizeof(statement),"update turnusers_lt set hmackey='%s' where name='%s' and realm='%s'",key,usname,realm);
+			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));
+			} else {
+			  ret = 0;
+			}
+		}
+		if(res) {
+			PQclear(res);
+		}
+	}
+  return ret;
+}
+  
+static int pgsql_set_user_pwd(u08bits *usname, st_password_t pwd) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	PGconn *pqc = get_pqdb_connection();
+	if(pqc) {
+	  snprintf(statement,sizeof(statement),"insert into turnusers_st values('%s','%s')",usname,pwd);
+    PGresult *res = PQexec(pqc, statement);
+		if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
+			if(res) {
+				PQclear(res);
+			}
+		  snprintf(statement,sizeof(statement),"update turnusers_st set password='%s' where name='%s'",pwd,usname);
+			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));
+			} else {
+			  ret = 0;
+			}
+		}
+		if(res) {
+			PQclear(res);
+		}
+	}
+  return ret;
+}
+  
+static int pgsql_del_user(u08bits *usname, int is_st, u08bits *realm) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	PGconn *pqc = get_pqdb_connection();
+	if(pqc) {
+		if(is_st) {
+		  snprintf(statement,sizeof(statement),"delete from turnusers_st where name='%s'",usname);
+		} else {
+		  snprintf(statement,sizeof(statement),"delete from turnusers_lt where name='%s' and realm='%s'",usname,realm);
+		}
+		PGresult *res = PQexec(pqc, statement);
+		if(res) {
+			PQclear(res);
+      ret = 0;
+		}
+	}
+  return ret;
+}
+  
+static int pgsql_list_users(int is_st, u08bits *realm) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	PGconn *pqc = get_pqdb_connection();
+	if(pqc) {
+		if(is_st) {
+		  snprintf(statement,sizeof(statement),"select name,'' from turnusers_st order by name");
+		} else if(realm && realm[0]) {
+		  snprintf(statement,sizeof(statement),"select name,realm from turnusers_lt where realm='%s' order by name",realm);
+		} else {
+		  snprintf(statement,sizeof(statement),"select name,realm from turnusers_lt order by name");
+		}
+		PGresult *res = PQexec(pqc, statement);
+		if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc));
+		} else {
+			int i = 0;
+			for(i=0;i<PQntuples(res);i++) {
+				char *kval = PQgetvalue(res,i,0);
+				if(kval) {
+					char *rval = PQgetvalue(res,i,1);
+					if(rval && *rval) {
+						printf("%s[%s]\n",kval,rval);
+					} else {
+						printf("%s\n",kval);
+					}
+				}
+			}
+			ret = 0;
+		}
+		if(res) {
+			PQclear(res);
+		}
+	}
+  return ret;
+}
+  
+static int pgsql_show_secret(u08bits *realm) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	snprintf(statement,sizeof(statement)-1,"select value from turn_secret where realm='%s'",realm);
+
+	donot_print_connection_success=1;
+
+	PGconn *pqc = get_pqdb_connection();
+	if(pqc) {
+		PGresult *res = PQexec(pqc, statement);
+		if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc));
+		} else {
+			int i = 0;
+			for(i=0;i<PQntuples(res);i++) {
+				char *kval = PQgetvalue(res,i,0);
+				if(kval) {
+					printf("%s\n",kval);
+				}
+			}
+      ret = 0;
+		}
+		if(res) {
+			PQclear(res);
+		}
+	}
+  return ret;
+}
+  
+static int pgsql_del_secret(u08bits *secret, u08bits *realm) {
+  int ret = 1;
+	donot_print_connection_success=1;
+	char statement[TURN_LONG_STRING_SIZE];
+	PGconn *pqc = get_pqdb_connection();
+	if (pqc) {
+		if(!secret || (secret[0]==0))
+		  snprintf(statement,sizeof(statement),"delete from turn_secret where realm='%s'",realm);
+		else
+		  snprintf(statement,sizeof(statement),"delete from turn_secret where value='%s' and realm='%s'",secret,realm);
+
+		PGresult *res = PQexec(pqc, statement);
+		if (res) {
+			PQclear(res);
+      ret = 0;
+		}
+	}
+  return ret;
+}
+  
+static int pgsql_set_secret(u08bits *secret, u08bits *realm) {
+  int ret = 1;
+	donot_print_connection_success = 1;
+  char statement[TURN_LONG_STRING_SIZE];
+	PGconn *pqc = get_pqdb_connection();
+	if (pqc) {
+	  snprintf(statement,sizeof(statement),"insert into turn_secret (realm,value) values('%s','%s')",realm,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));
+	  } else {
+	    ret = 0;
+	  }
+	  if (res) {
+	    PQclear(res);
+	  }
+	}
+
+  return ret;
+}
+  
+static int pgsql_add_origin(u08bits *origin, u08bits *realm) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	PGconn *pqc = get_pqdb_connection();
+	if(pqc) {
+		snprintf(statement,sizeof(statement),"insert into turn_origin_to_realm (origin,realm) values('%s','%s')",origin,realm);
+		PGresult *res = PQexec(pqc, statement);
+		if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting origin information: %s\n",PQerrorMessage(pqc));
+		} else {
+		  ret = 0;
+		}
+		if(res) {
+			PQclear(res);
+		}
+	}
+  return ret;
+}
+  
+static int pgsql_del_origin(u08bits *origin) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	PGconn *pqc = get_pqdb_connection();
+	if(pqc) {
+		snprintf(statement,sizeof(statement),"delete from turn_origin_to_realm where origin='%s'",origin);
+		PGresult *res = PQexec(pqc, statement);
+		if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting origin information: %s\n",PQerrorMessage(pqc));
+		} else {
+		  ret = 0;
+		}
+		if(res) {
+			PQclear(res);
+		}
+	}
+  return ret;
+}
+  
+static int pgsql_list_origins(u08bits *realm) {
+  int ret = 1;
+	donot_print_connection_success = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	PGconn *pqc = get_pqdb_connection();
+	if(pqc) {
+		if(realm && realm[0]) {
+		  snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm where realm='%s' order by origin",realm);
+		} else {
+		  snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm order by origin,realm");
+		}
+		PGresult *res = PQexec(pqc, statement);
+		if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc));
+		} else {
+			int i = 0;
+			for(i=0;i<PQntuples(res);i++) {
+				char *oval = PQgetvalue(res,i,0);
+				if(oval) {
+					char *rval = PQgetvalue(res,i,1);
+					if(rval) {
+						printf("%s ==>> %s\n",oval,rval);
+					}
+				}
+			}
+      ret = 0;
+		}
+		if(res) {
+			PQclear(res);
+		}
+	}
+  return ret;
+}
+  
+static int pgsql_set_realm_option_one(u08bits *realm, unsigned long value, const char* opt) {
+  int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	PGconn *pqc = get_pqdb_connection();
+	if(pqc) {
+		{
+			snprintf(statement,sizeof(statement),"delete from turn_realm_option where realm='%s' and opt='%s'",realm,opt);
+			PGresult *res = PQexec(pqc, statement);
+			if(res) {
+				PQclear(res);
+			}
+		}
+		if(value>0) {
+			snprintf(statement,sizeof(statement),"insert into turn_realm_option (realm,opt,value) values('%s','%s','%lu')",realm,opt,(unsigned long)value);
+			PGresult *res = PQexec(pqc, statement);
+			if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting realm option information: %s\n",PQerrorMessage(pqc));
+			} else {
+			  ret = 0;
+			}
+			if(res) {
+				PQclear(res);
+			}
+		}
+	}
+  return ret;
+}
+  
+static int pgsql_list_realm_options(u08bits *realm) {
+  int ret = 1;
+	donot_print_connection_success = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	PGconn *pqc = get_pqdb_connection();
+	if(pqc) {
+		if(realm && realm[0]) {
+			snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option where realm='%s' order by realm,opt",realm);
+		} else {
+			snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option order by realm,opt");
+		}
+		PGresult *res = PQexec(pqc, statement);
+		if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc));
+		} else {
+			int i = 0;
+			for(i=0;i<PQntuples(res);i++) {
+				char *rval = PQgetvalue(res,i,0);
+				if(rval) {
+					char *oval = PQgetvalue(res,i,1);
+					if(oval) {
+						char *vval = PQgetvalue(res,i,2);
+						if(vval) {
+							printf("%s[%s]=%s\n",oval,rval,vval);
+						}
+					}
+				}
+			}
+      ret = 0;
+		}
+		if(res) {
+			PQclear(res);
+		}
+	}
+  return ret;
+}
+  
+static void pgsql_auth_ping(void * rch) {
+	UNUSED_ARG(rch);
+	donot_print_connection_success = 1;
+	PGconn * pqc = get_pqdb_connection();
+	if(pqc) {
+		char statement[TURN_LONG_STRING_SIZE];
+		STRCPY(statement,"select value from turn_secret");
+		PGresult *res = PQexec(pqc, statement);
+
+		if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc));
+		}
+
+		if(res) {
+			PQclear(res);
+		}
+	}
+}
+  
+static int pgsql_get_ip_list(const char *kind, ip_range_list_t * list) {
+  int ret = 1;
+	PGconn * pqc = get_pqdb_connection();
+	if(pqc) {
+		char statement[TURN_LONG_STRING_SIZE];
+		snprintf(statement,sizeof(statement),"select ip_range from %s_peer_ip",kind);
+		PGresult *res = PQexec(pqc, statement);
+
+		if(res && (PQresultStatus(res) == PGRES_TUPLES_OK)) {
+			int i = 0;
+			for(i=0;i<PQntuples(res);i++) {
+				char *kval = PQgetvalue(res,i,0);
+				if(kval) {
+					add_ip_list_range(kval,list);
+				}
+			}
+      ret = 0;
+		}
+
+		if(res) {
+			PQclear(res);
+		}
+	}
+  return ret;
+}
+  
+static void pgsql_reread_realms(secrets_list_t * realms_list) {
+	PGconn * pqc = get_pqdb_connection();
+	if(pqc) {
+		char statement[TURN_LONG_STRING_SIZE];
+
+		{
+			snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm");
+			PGresult *res = PQexec(pqc, statement);
+
+			if(res && (PQresultStatus(res) == PGRES_TUPLES_OK)) {
+
+				ur_string_map *o_to_realm_new = ur_string_map_create(free);
+
+				int i = 0;
+				for(i=0;i<PQntuples(res);i++) {
+					char *oval = PQgetvalue(res,i,0);
+					if(oval) {
+						char *rval = PQgetvalue(res,i,1);
+						if(rval) {
+							get_realm(rval);
+							ur_string_map_value_type value = strdup(rval);
+							ur_string_map_put(o_to_realm_new, (const ur_string_map_key_type) oval, value);
+						}
+					}
+				}
+
+        update_o_to_realm(o_to_realm_new);
+			}
+
+			if(res) {
+				PQclear(res);
+			}
+		}
+
+		{
+			{
+				size_t i = 0;
+				size_t rlsz = 0;
+
+				lock_realms();
+				rlsz = realms_list->sz;
+				unlock_realms();
+
+				for (i = 0; i<rlsz; ++i) {
+
+					char *realm = realms_list->secrets[i];
+
+					realm_params_t* rp = get_realm(realm);
+
+					lock_realms();
+					rp->options.perf_options.max_bps = turn_params.max_bps;
+					unlock_realms();
+
+					lock_realms();
+					rp->options.perf_options.total_quota = turn_params.total_quota;
+					unlock_realms();
+
+					lock_realms();
+					rp->options.perf_options.user_quota = turn_params.user_quota;
+					unlock_realms();
+
+				}
+			}
+
+			snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option");
+			PGresult *res = PQexec(pqc, statement);
+
+			if(res && (PQresultStatus(res) == PGRES_TUPLES_OK)) {
+
+				int i = 0;
+				for(i=0;i<PQntuples(res);i++) {
+					char *rval = PQgetvalue(res,i,0);
+					char *oval = PQgetvalue(res,i,1);
+					char *vval = PQgetvalue(res,i,2);
+					if(rval && oval && vval) {
+						realm_params_t* rp = get_realm(rval);
+						if(!strcmp(oval,"max-bps"))
+							rp->options.perf_options.max_bps = (band_limit_t)atol(vval);
+						else if(!strcmp(oval,"total-quota"))
+							rp->options.perf_options.total_quota = (vint)atoi(vval);
+						else if(!strcmp(oval,"user-quota"))
+							rp->options.perf_options.user_quota = (vint)atoi(vval);
+						else {
+							TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown realm option: %s\n", oval);
+						}
+					}
+				}
+			}
+
+			if(res) {
+				PQclear(res);
+			}
+		}
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static turn_dbdriver_t driver = {
+  &pgsql_get_auth_secrets,
+  &pgsql_get_user_key,
+  &pgsql_get_user_pwd,
+  &pgsql_set_user_key,
+  &pgsql_set_user_pwd,
+  &pgsql_del_user,
+  &pgsql_list_users,
+  &pgsql_show_secret,
+  &pgsql_del_secret,
+  &pgsql_set_secret,
+  &pgsql_add_origin,
+  &pgsql_del_origin,
+  &pgsql_list_origins,
+  &pgsql_set_realm_option_one,
+  &pgsql_list_realm_options,
+  &pgsql_auth_ping,
+  &pgsql_get_ip_list,
+  &pgsql_reread_realms
+};
+
+turn_dbdriver_t * get_pgsql_dbdriver(void) {
+  return &driver;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#else
+
+turn_dbdriver_t * get_pgsql_dbdriver(void) {
+  return NULL;
+}
+
+#endif

+ 49 - 0
src/apps/relay/dbdrivers/dbd_pgsql.h

@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011, 2012, 2013 Citrix Systems
+ * Copyright (C) 2014 Vivocha S.p.A.
+ *
+ * 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 __DBD_POSTGRESQL__
+#define __DBD_POSTGRESQL__
+
+#include "dbdriver.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+turn_dbdriver_t * get_pgsql_dbdriver(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/// __DBD_POSTGRESQL__///
+

+ 1150 - 0
src/apps/relay/dbdrivers/dbd_redis.c

@@ -0,0 +1,1150 @@
+/*
+ * Copyright (C) 2011, 2012, 2013 Citrix Systems
+ * Copyright (C) 2014 Vivocha S.p.A.
+ *
+ * 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"
+#include "dbd_redis.h"
+
+#if !defined(TURN_NO_HIREDIS)
+#include "hiredis_libevent2.h"
+#include <hiredis/hiredis.h>
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static int donot_print_connection_success = 0;
+
+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(const 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, const char* connection_string, int delete_keys) {
+	redis_context_handle ret = NULL;
+
+	char *errmsg = NULL;
+	if(base  && connection_string  && connection_string[0]) {
+		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", connection_string, 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", connection_string);
+			}
+		} else if (errmsg) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open Redis DB connection <%s>, connection string format error: %s\n", connection_string, errmsg);
+			turn_free(errmsg,strlen(errmsg)+1);
+			RyconninfoFree(co);
+		} else {
+
+			if(delete_keys) {
+
+				redisContext *rc = NULL;
+
+				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);
+					rc = redisConnectWithTimeout(ip, port, tv);
+				} else {
+					rc = redisConnect(ip, port);
+				}
+
+				if (!rc) {
+					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot initialize Redis DB async connection\n");
+				} else {
+					if (co->password) {
+						turnFreeRedisReply(redisCommand(rc, "AUTH %s", co->password));
+					}
+					if (co->dbname) {
+						turnFreeRedisReply(redisCommand(rc, "select %s", co->dbname));
+					}
+					{
+						redisReply *reply = (redisReply*)redisCommand(rc, "keys turn/*/allocation/*/status");
+						if(reply) {
+							secrets_list_t keys;
+							size_t isz = 0;
+							char s[513];
+
+							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;isz<keys.sz;++isz) {
+
+								snprintf(s,sizeof(s),"del %s", keys.secrets[isz]);
+								turnFreeRedisReply(redisCommand(rc, s));
+							}
+
+							clean_secrets_list(&keys);
+
+							turnFreeRedisReply(reply);
+						}
+					}
+					redisFree(rc);
+				}
+			}
+
+			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 (is_redis_asyncconn_good(ret) && !donot_print_connection_success) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Redis DB async connection to be used: %s\n", connection_string);
+			}
+			RyconninfoFree(co);
+		}
+	}
+
+	return ret;
+}
+
+static redisContext *get_redis_connection(void) {
+	persistent_users_db_t *pud = get_persistent_users_db();
+
+	redisContext *redisconnection = (redisContext*)(pud->connection);
+
+	if(redisconnection) {
+		if(redisconnection->err) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot connect to redis, err=%d, flags=0x%x\n", __FUNCTION__,(int)redisconnection->err,(unsigned long)redisconnection->flags);
+			redisFree(redisconnection);
+			pud->connection = NULL;
+			redisconnection = NULL;
+		}
+	}
+
+	if (!redisconnection) {
+
+		char *errmsg = NULL;
+		Ryconninfo *co = RyconninfoParse(pud->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", pud->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", pud->userdb);
+			}
+		} else if (errmsg) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open Redis DB connection <%s>, connection string format error: %s\n", pud->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) {
+				if(redisconnection->err) {
+					if(redisconnection->errstr[0]) {
+						TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Redis: %s\n",redisconnection->errstr);
+					}
+					redisFree(redisconnection);
+					redisconnection = NULL;
+				} else if (co->password) {
+					void *reply = redisCommand(redisconnection, "AUTH %s", co->password);
+					if(!reply) {
+						if(redisconnection->err && redisconnection->errstr[0]) {
+							TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Redis: %s\n",redisconnection->errstr);
+						}
+						redisFree(redisconnection);
+						redisconnection = NULL;
+					} else {
+						turnFreeRedisReply(reply);
+						if (co->dbname) {
+							reply = redisCommand(redisconnection, "select %s", co->dbname);
+							if(!reply) {
+								if(redisconnection->err && redisconnection->errstr[0]) {
+									TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Redis: %s\n",redisconnection->errstr);
+								}
+								redisFree(redisconnection);
+								redisconnection = NULL;
+							} else {
+								turnFreeRedisReply(reply);
+							}
+						}
+					}
+				}
+			}
+
+			if (!redisconnection) {
+				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 sync connection success: %s\n", pud->userdb);
+			}
+
+			RyconninfoFree(co);
+		}
+		pud->connection = redisconnection;
+
+		pud = get_persistent_users_db();
+
+		redisconnection = (redisContext*)(pud->connection);
+	}
+
+	return redisconnection;
+}
+
+static int set_redis_realm_opt(char *realm, const char* key, unsigned long *value)
+{
+	int found = 0;
+
+	redisContext *rc = get_redis_connection();
+
+	if(rc) {
+		redisReply *rget = NULL;
+
+		char s[1025];
+
+		snprintf(s, sizeof(s), "get turn/realm/%s/%s", realm, key);
+
+		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 {
+				lock_realms();
+				*value = (unsigned long)atol(rget->str);
+				unlock_realms();
+				found = 1;
+			}
+			turnFreeRedisReply(rget);
+		}
+	}
+
+	return found;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static int redis_get_auth_secrets(secrets_list_t *sl, u08bits *realm) {
+  int ret = 1;
+	redisContext *rc = get_redis_connection();
+	if(rc) {
+		redisReply *reply = (redisReply*)redisCommand(rc, "keys turn/realm/%s/secret/*", (char*)realm);
+		if(reply) {
+
+			secrets_list_t keys;
+			size_t isz = 0;
+			char s[257];
+
+			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;isz<keys.sz;++isz) {
+				snprintf(s,sizeof(s),"get %s", keys.secrets[isz]);
+				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 {
+						add_to_secrets_list(sl,rget->str);
+					}
+					turnFreeRedisReply(rget);
+				}
+			}
+
+			clean_secrets_list(&keys);
+
+			ret = 0;
+
+			turnFreeRedisReply(reply);
+		}
+	}
+  return ret;
+}
+  
+static int redis_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
+  int ret = 1;
+	redisContext * rc = get_redis_connection();
+	if(rc) {
+		char s[TURN_LONG_STRING_SIZE];
+		snprintf(s,sizeof(s),"get turn/realm/%s/user/%s/key", (char*)realm, 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 {
+				size_t sz = get_hmackey_size(turn_params.shatype);
+				if(strlen(rget->str)<sz*2) {
+					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key format: %s, user %s\n",rget->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/realm/%s/user/%s/password", (char*)realm, 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, realm, (u08bits*)rget->str, key, turn_params.shatype)>=0) {
+						ret = 0;
+					}
+				}
+				turnFreeRedisReply(rget);
+			}
+		}
+	}
+  return ret;
+}
+  
+static int redis_get_user_pwd(u08bits *usname, st_password_t pwd) {
+  int ret = 1;
+	redisContext * rc = get_redis_connection();
+	if(rc) {
+		char s[TURN_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);
+		}
+	}
+  return ret;
+}
+  
+static int redis_set_user_key(u08bits *usname, u08bits *realm, const char *key) {
+  int ret = 1;
+	redisContext *rc = get_redis_connection();
+	if(rc) {
+		char statement[TURN_LONG_STRING_SIZE];
+	  snprintf(statement,sizeof(statement),"set turn/realm/%s/user/%s/key %s",(char*)realm,usname,key);
+	  turnFreeRedisReply(redisCommand(rc, statement));
+	  snprintf(statement,sizeof(statement),"del turn/realm/%s/user/%s/password",(char*)realm,usname);
+	  turnFreeRedisReply(redisCommand(rc, statement));
+		turnFreeRedisReply(redisCommand(rc, "save"));
+    ret = 0;
+	}
+  return ret;
+}
+  
+static int redis_set_user_pwd(u08bits *usname, st_password_t pwd) {
+  int ret = 1;
+	redisContext *rc = get_redis_connection();
+	if(rc) {
+		char statement[TURN_LONG_STRING_SIZE];
+	  snprintf(statement,sizeof(statement),"set turn/user/%s/password %s",usname,pwd);
+	  turnFreeRedisReply(redisCommand(rc, statement));
+		turnFreeRedisReply(redisCommand(rc, "save"));
+    ret = 0;
+	}
+  return ret;
+}
+  
+static int redis_del_user(u08bits *usname, int is_st, u08bits *realm) {
+  int ret = 1;
+	redisContext *rc = get_redis_connection();
+	if(rc) {
+		char statement[TURN_LONG_STRING_SIZE];
+		if(is_st) {
+		  snprintf(statement,sizeof(statement),"del turn/user/%s/password",usname);
+		  turnFreeRedisReply(redisCommand(rc, statement));
+		} else {
+		  snprintf(statement,sizeof(statement),"del turn/realm/%s/user/%s/key",(char*)realm,usname);
+		  turnFreeRedisReply(redisCommand(rc, statement));
+		  snprintf(statement,sizeof(statement),"del turn/realm/%s/user/%s/password",(char*)realm,usname);
+		  turnFreeRedisReply(redisCommand(rc, statement));
+		}
+
+		turnFreeRedisReply(redisCommand(rc, "save"));
+    ret = 0;
+	}
+  return ret;
+}
+  
+static int redis_list_users(int is_st, u08bits *realm) {
+  int ret = 1;
+	redisContext *rc = get_redis_connection();
+	if(rc) {
+		secrets_list_t keys;
+		size_t isz = 0;
+
+		init_secrets_list(&keys);
+
+		redisReply *reply = NULL;
+
+		if(!is_st) {
+
+			if(realm && realm[0]) {
+				reply = (redisReply*)redisCommand(rc, "keys turn/realm/%s/user/*/key", (char*)realm);
+			} else {
+				reply = (redisReply*)redisCommand(rc, "keys turn/realm/*/user/*/key");
+			}
+			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);
+			}
+
+			if(realm && realm[0]) {
+				reply = (redisReply*)redisCommand(rc, "keys turn/realm/%s/user/*/password", (char*)realm);
+			} else {
+				reply = (redisReply*)redisCommand(rc, "keys turn/realm/*/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);
+			}
+		} else {
+
+			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;isz<keys.sz;++isz) {
+			char *s = keys.secrets[isz];
+			char *sh = strstr(s,"/user/");
+			if(sh) {
+				sh += 6;
+				char* st = strchr(sh,'/');
+				if(st)
+					*st=0;
+				printf("%s\n",sh);
+			}
+		}
+
+		clean_secrets_list(&keys);
+		ret = 0;
+	}
+  return ret;
+}
+  
+static int redis_show_secret(u08bits *realm) {
+  int ret = 1;
+	donot_print_connection_success = 1;
+	redisContext *rc = get_redis_connection();
+	if(rc) {
+		redisReply *reply = NULL;
+		if(realm && realm[0]) {
+			reply = (redisReply*)redisCommand(rc, "keys turn/realm/%s/secret/*",(char*)realm);
+		} else {
+			reply = (redisReply*)redisCommand(rc, "keys turn/realm/*/secret/*");
+		}
+		if(reply) {
+			secrets_list_t keys;
+			size_t isz = 0;
+			char s[257];
+
+			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;isz<keys.sz;++isz) {
+				snprintf(s,sizeof(s),"get %s", keys.secrets[isz]);
+				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 {
+						printf("%s\n",rget->str);
+					}
+				}
+				turnFreeRedisReply(rget);
+			}
+
+			clean_secrets_list(&keys);
+
+			turnFreeRedisReply(reply);
+      ret = 0;
+		}
+	}
+  return ret;
+}
+  
+static int redis_del_secret(u08bits *secret, u08bits *realm) {
+  int ret = 1;
+	donot_print_connection_success = 1;
+	redisContext *rc = get_redis_connection();
+	if(rc) {
+		redisReply *reply = (redisReply*)redisCommand(rc, "keys turn/realm/%s/secret/*", (char*)realm);
+		if(reply) {
+			secrets_list_t keys;
+			size_t isz = 0;
+			char s[TURN_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;isz<keys.sz;++isz) {
+				if(!secret || (secret[0]==0)) {
+					snprintf(s,sizeof(s),"del %s", keys.secrets[isz]);
+					turnFreeRedisReply(redisCommand(rc, s));
+				} else {
+					snprintf(s,sizeof(s),"get %s", keys.secrets[isz]);
+					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 {
+							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);
+      ret = 0;
+		}
+	}
+  return ret;
+}
+  
+static int redis_set_secret(u08bits *secret, u08bits *realm) {
+  int ret = 1;
+	donot_print_connection_success = 1;
+	redisContext *rc = get_redis_connection();
+	if(rc) {
+		char s[TURN_LONG_STRING_SIZE];
+
+		redis_del_secret(secret, realm);
+
+		snprintf(s,sizeof(s),"set turn/realm/%s/secret/%lu %s", (char*)realm, (unsigned long)turn_time(), secret);
+
+		turnFreeRedisReply(redisCommand(rc, s));
+		turnFreeRedisReply(redisCommand(rc, "save"));
+    ret = 0;
+	}
+  return ret;
+}
+  
+static int redis_add_origin(u08bits *origin, u08bits *realm) {
+  int ret = 1;
+	redisContext *rc = get_redis_connection();
+	if(rc) {
+		char s[TURN_LONG_STRING_SIZE];
+
+		snprintf(s,sizeof(s),"set turn/origin/%s %s", (char*)origin, (char*)realm);
+
+		turnFreeRedisReply(redisCommand(rc, s));
+		turnFreeRedisReply(redisCommand(rc, "save"));
+    ret = 0;
+	}
+  return ret;
+}
+  
+static int redis_del_origin(u08bits *origin) {
+  int ret = 1;
+	redisContext *rc = get_redis_connection();
+	if(rc) {
+		char s[TURN_LONG_STRING_SIZE];
+
+		snprintf(s,sizeof(s),"del turn/origin/%s", (char*)origin);
+
+		turnFreeRedisReply(redisCommand(rc, s));
+		turnFreeRedisReply(redisCommand(rc, "save"));
+    ret = 0;
+	}
+  return ret;
+}
+  
+static int redis_list_origins(u08bits *realm) {
+  int ret = 1;
+	donot_print_connection_success = 1;
+	redisContext *rc = get_redis_connection();
+	if(rc) {
+		secrets_list_t keys;
+		size_t isz = 0;
+
+		init_secrets_list(&keys);
+
+		redisReply *reply = NULL;
+
+		{
+			reply = (redisReply*)redisCommand(rc, "keys turn/origin/*");
+			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;
+					size_t offset = strlen("turn/origin/");
+					for (i = 0; i < reply->elements; ++i) {
+						add_to_secrets_list(&keys,reply->element[i]->str+offset);
+					}
+				}
+				turnFreeRedisReply(reply);
+			}
+		}
+
+		for(isz=0;isz<keys.sz;++isz) {
+			char *o = keys.secrets[isz];
+
+			reply = (redisReply*)redisCommand(rc, "get turn/origin/%s",o);
+			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_STRING) {
+					if (reply->type != REDIS_REPLY_NIL)
+						TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type);
+				} else {
+					if(!(realm && realm[0] && strcmp((char*)realm,reply->str))) {
+						printf("%s ==>> %s\n",o,reply->str);
+					}
+				}
+				turnFreeRedisReply(reply);
+			}
+		}
+
+		clean_secrets_list(&keys);
+    ret = 0;
+	}
+  return ret;
+}
+  
+static int redis_set_realm_option_one(u08bits *realm, unsigned long value, const char* opt) {
+  int ret = 1;
+	redisContext *rc = get_redis_connection();
+	if(rc) {
+		char s[TURN_LONG_STRING_SIZE];
+
+		if(value>0)
+			snprintf(s,sizeof(s),"set turn/realm/%s/%s %lu", (char*)realm, opt, (unsigned long)value);
+		else
+			snprintf(s,sizeof(s),"del turn/realm/%s/%s", (char*)realm, opt);
+
+		turnFreeRedisReply(redisCommand(rc, s));
+		turnFreeRedisReply(redisCommand(rc, "save"));
+    ret = 0;
+	}
+  return ret;
+}
+  
+static int redis_list_realm_options(u08bits *realm) {
+  int ret = 1;
+	donot_print_connection_success = 1;
+	redisContext *rc = get_redis_connection();
+	if(rc) {
+		secrets_list_t keys;
+		size_t isz = 0;
+
+		init_secrets_list(&keys);
+
+		redisReply *reply = NULL;
+
+		{
+			if(realm && realm[0]) {
+				reply = (redisReply*)redisCommand(rc, "keys turn/realm/%s/*",realm);
+			} else {
+				reply = (redisReply*)redisCommand(rc, "keys turn/realm/*");
+			}
+			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) {
+						if(strstr(reply->element[i]->str,"/max-bps")||
+							strstr(reply->element[i]->str,"/total-quota")||
+							strstr(reply->element[i]->str,"/user-quota")) {
+							add_to_secrets_list(&keys,reply->element[i]->str);
+						}
+					}
+				}
+				turnFreeRedisReply(reply);
+			}
+		}
+
+		size_t offset = strlen("turn/realm/");
+
+		for(isz=0;isz<keys.sz;++isz) {
+			char *o = keys.secrets[isz];
+
+			reply = (redisReply*)redisCommand(rc, "get %s",o);
+			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_STRING) {
+					if (reply->type != REDIS_REPLY_NIL)
+						TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type);
+				} else {
+					printf("%s = %s\n",o+offset,reply->str);
+				}
+				turnFreeRedisReply(reply);
+			}
+		}
+
+		clean_secrets_list(&keys);
+    ret = 0;
+	}
+  return ret;
+}
+  
+static void redis_auth_ping(void * rch) {
+	donot_print_connection_success = 1;
+	redisContext *rc = get_redis_connection();
+	if(rc) {
+		turnFreeRedisReply(redisCommand(rc, "keys turn/origin/*"));
+	}
+	if(rch)
+		send_message_to_redis((redis_context_handle)rch, "publish", "__XXX__", "__YYY__");
+}
+  
+static int redis_get_ip_list(const char *kind, ip_range_list_t * list) {
+  int ret = 1;
+	redisContext *rc = get_redis_connection();
+	if(rc) {
+		char statement[TURN_LONG_STRING_SIZE];
+		snprintf(statement,sizeof(statement),"keys turn/%s-peer-ip/*", kind);
+		redisReply *reply = (redisReply*)redisCommand(rc, statement);
+		if(reply) {
+			secrets_list_t keys;
+			size_t isz = 0;
+			char s[257];
+
+			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;isz<keys.sz;++isz) {
+				snprintf(s,sizeof(s),"get %s", keys.secrets[isz]);
+				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 {
+						add_ip_list_range(rget->str,list);
+					}
+					turnFreeRedisReply(rget);
+				}
+			}
+
+			clean_secrets_list(&keys);
+
+			turnFreeRedisReply(reply);
+      ret = 0;
+		}
+	}
+  return ret;
+}
+  
+static void redis_reread_realms(secrets_list_t * realms_list) {
+	redisContext *rc = get_redis_connection();
+	if (rc) {
+
+		redisReply *reply = (redisReply*) redisCommand(rc, "keys turn/origin/*");
+		if (reply) {
+
+			ur_string_map *o_to_realm_new = ur_string_map_create(free);
+
+			secrets_list_t keys;
+
+			init_secrets_list(&keys);
+
+			size_t isz = 0;
+
+			char s[1025];
+
+			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);
+				}
+			}
+
+			size_t offset = strlen("turn/origin/");
+
+			for (isz = 0; isz < keys.sz; ++isz) {
+				char *origin = keys.secrets[isz] + offset;
+				snprintf(s, sizeof(s), "get %s", keys.secrets[isz]);
+				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 {
+						get_realm(rget->str);
+						ur_string_map_value_type value = strdup(rget->str);
+						ur_string_map_put(o_to_realm_new, (const ur_string_map_key_type) origin, value);
+					}
+					turnFreeRedisReply(rget);
+				}
+			}
+
+			clean_secrets_list(&keys);
+
+      update_o_to_realm(o_to_realm_new);
+
+			turnFreeRedisReply(reply);
+		}
+
+		{
+			size_t i = 0;
+			size_t rlsz = 0;
+
+			lock_realms();
+			rlsz = realms_list->sz;
+			unlock_realms();
+
+			for (i = 0; i<rlsz; ++i) {
+				char *realm = realms_list->secrets[i];
+				realm_params_t* rp = get_realm(realm);
+				unsigned long value = 0;
+				if(!set_redis_realm_opt(realm,"max-bps",&value)) {
+					lock_realms();
+					rp->options.perf_options.max_bps = turn_params.max_bps;
+					unlock_realms();
+				}
+				rp->options.perf_options.max_bps = (band_limit_t)value;
+				if(!set_redis_realm_opt(realm,"total-quota",&value)) {
+					lock_realms();
+					rp->options.perf_options.total_quota = turn_params.total_quota;
+					unlock_realms();
+				}
+				rp->options.perf_options.total_quota = (vint)value;
+				if(!set_redis_realm_opt(realm,"user-quota",&value)) {
+					lock_realms();
+					rp->options.perf_options.user_quota = turn_params.user_quota;
+					unlock_realms();
+				}
+				rp->options.perf_options.user_quota = (vint)value;
+			}
+		}
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static turn_dbdriver_t driver = {
+  &redis_get_auth_secrets,
+  &redis_get_user_key,
+  &redis_get_user_pwd,
+  &redis_set_user_key,
+  &redis_set_user_pwd,
+  &redis_del_user,
+  &redis_list_users,
+  &redis_show_secret,
+  &redis_del_secret,
+  &redis_set_secret,
+  &redis_add_origin,
+  &redis_del_origin,
+  &redis_list_origins,
+  &redis_set_realm_option_one,
+  &redis_list_realm_options,
+  &redis_auth_ping,
+  &redis_get_ip_list,
+  &redis_reread_realms
+};
+
+turn_dbdriver_t * get_redis_dbdriver(void) {
+  return &driver;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#else
+
+turn_dbdriver_t * get_redis_dbdriver(void) {
+  return NULL;
+}
+
+#endif

+ 49 - 0
src/apps/relay/dbdrivers/dbd_redis.h

@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011, 2012, 2013 Citrix Systems
+ * Copyright (C) 2014 Vivocha S.p.A.
+ *
+ * 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 __DBD_REDIS__
+#define __DBD_REDIS__
+
+#include "dbdriver.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+turn_dbdriver_t * get_redis_dbdriver(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/// __DBD_REDIS__///
+

+ 90 - 0
src/apps/relay/dbdrivers/dbdriver.c

@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011, 2012, 2013 Citrix Systems
+ * Copyright (C) 2014 Vivocha S.p.A.
+ *
+ * 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"
+#include "dbdriver.h"
+#include "dbd_pgsql.h"
+#include "dbd_mysql.h"
+#include "dbd_mongo.h"
+#include "dbd_redis.h"
+
+static turn_dbdriver_t * _driver;
+
+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<sz;i++) {
+		is[0]=keysource[i*2];
+		is[1]=keysource[i*2+1];
+		sscanf(is,"%02x",&v);
+		key[i]=(unsigned char)v;
+	}
+	return 0;
+}
+
+persistent_users_db_t * get_persistent_users_db(void) {
+	return &(turn_params.default_users_db.persistent_users_db);
+}
+
+turn_dbdriver_t * get_dbdriver() {
+  if (!_driver) {
+    switch(turn_params.default_users_db.userdb_type) {
+#if !defined(TURN_NO_PQ)
+    case TURN_USERDB_TYPE_PQ:
+      _driver = get_pgsql_dbdriver();
+      break;
+#endif
+#if !defined(TURN_NO_MYSQL)
+    case TURN_USERDB_TYPE_MYSQL:
+      _driver = get_mysql_dbdriver();
+      break;
+#endif
+#if !defined(TURN_NO_MONGO)
+    case TURN_USERDB_TYPE_MONGO:
+      _driver = get_mongo_dbdriver();
+      break;
+#endif
+#if !defined(TURN_NO_HIREDIS)
+    case TURN_USERDB_TYPE_REDIS:
+      _driver = get_redis_dbdriver();
+      break;
+#endif
+    default:
+      break;
+    }
+  }
+  return _driver;
+}
+
+
+

+ 78 - 0
src/apps/relay/dbdrivers/dbdriver.h

@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2011, 2012, 2013 Citrix Systems
+ * Copyright (C) 2014 Vivocha S.p.A.
+ *
+ * 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 __DBDRIVER__
+#define __DBDRIVER__
+
+#include "../userdb.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+////////////////////////////////////////////
+
+typedef struct _turn_dbdriver_t {
+  int (*get_auth_secrets)(secrets_list_t *sl, u08bits *realm);
+  int (*get_user_key)(u08bits *usname, u08bits *realm, hmackey_t key);
+  int (*get_user_pwd)(u08bits *usname, st_password_t pwd);
+  int (*set_user_key)(u08bits *usname, u08bits *realm, const char *key);
+  int (*set_user_pwd)(u08bits *usname, st_password_t pwd);
+  int (*del_user)(u08bits *usname, int is_st, u08bits *realm);
+  int (*list_users)(int is_st, u08bits *realm);
+  int (*show_secret)(u08bits *realm);
+  int (*del_secret)(u08bits *secret, u08bits *realm);
+  int (*set_secret)(u08bits *secret, u08bits *realm);
+  int (*add_origin)(u08bits *origin, u08bits *realm);
+  int (*del_origin)(u08bits *origin);
+  int (*list_origins)(u08bits *realm);
+  int (*set_realm_option_one)(u08bits *realm, unsigned long value, const char* opt);
+  int (*list_realm_options)(u08bits *realm);
+  void (*auth_ping)(void * rch);
+  int (*get_ip_list)(const char *kind, ip_range_list_t * list);
+  void (*reread_realms)(secrets_list_t * realms_list);
+} turn_dbdriver_t;
+
+/////////// USER DB CHECK //////////////////
+
+int convert_string_key_to_binary(char* keysource, hmackey_t key, size_t sz);
+persistent_users_db_t * get_persistent_users_db(void);
+turn_dbdriver_t * get_dbdriver(void);
+
+////////////////////////////////////////////
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/// __DBDRIVER__///
+

+ 71 - 14
src/apps/relay/mainrelay.c

@@ -427,6 +427,11 @@ static char Usage[] = "Usage: turnserver [options]\n"
 "	        	          		\"host=<ip-addr> dbname=<database-name> user=<database-user> \\\n								password=<database-user-password> port=<db-port> connect_timeout=<seconds>\".\n"
 "	        	          		All parameters are optional.\n"
 #endif
+#if !defined(TURN_NO_MONGO)
+" -J, --mongo-userdb	<connection-string>	MongoDB connection string, if used (default - empty, no MongoDB 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"
+#endif
 #if !defined(TURN_NO_HIREDIS)
 " -N, --redis-userdb	<connection-string>	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"
@@ -557,7 +562,7 @@ static char AdminUsage[] = "Usage: turnadmin [command] [options]\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) || !defined(TURN_NO_HIREDIS)
+#if !defined(TURN_NO_PQ) || !defined(TURN_NO_MYSQL) || !defined(TURN_NO_MONGO) || !defined(TURN_NO_HIREDIS)
 	"	-s, --set-secret=<value>	Add shared secret for TURN RESP API\n"
 	"	-S, --show-secret		Show stored shared secrets for TURN REST API\n"
 	"	-X, --delete-secret=<value>	Delete a shared secret\n"
@@ -576,13 +581,16 @@ static char AdminUsage[] = "Usage: turnadmin [command] [options]\n"
 #if !defined(TURN_NO_MYSQL)
 	"	-M, --mysql-userdb		MySQL user database connection string, if MySQL DB is used.\n"
 #endif
+#if !defined(TURN_NO_MONGO)
+	"	-J, --mongo-userdb		MongoDB user database connection string, if MongoDB 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"
-#if !defined(TURN_NO_PQ) || !defined(TURN_NO_MYSQL) || !defined(TURN_NO_HIREDIS)
+#if !defined(TURN_NO_PQ) || !defined(TURN_NO_MYSQL) || !defined(TURN_NO_MONGO) || !defined(TURN_NO_HIREDIS)
 	"	-o, --origin			Origin\n"
 #endif
 	"	-H, --sha256			Use SHA256 digest function to be used for the message integrity.\n"
@@ -595,9 +603,9 @@ static char AdminUsage[] = "Usage: turnadmin [command] [options]\n"
 	"					Setting to zero value means removal of the option.\n"
 	"	-h, --help			Help\n";
 
-#define OPTIONS "c:d:p:L:E:X:i:m:l:r:u:b:B:e:M:N:O:q:Q:s:C:vVofhznaAS"
-
-#define ADMIN_OPTIONS "gGORIHlLkaADSdb:e:M:N:u:r:p:s:X:o:h"
+#define OPTIONS "c:d:p:L:E:X:i:m:l:r:u:b:B:e:M:J:N:O:q:Q:s:C:vVofhznaAS"
+  
+#define ADMIN_OPTIONS "gGORIHlLkaADSdb:e:M:J:N:u:r:p:s:X:o:h"
 
 enum EXTRA_OPTS {
 	NO_UDP_OPT=256,
@@ -661,7 +669,22 @@ enum EXTRA_OPTS {
 	ADMIN_USER_QUOTA_OPT
 };
 
-static struct option long_options[] = {
+struct myoption {
+        const char *name;     /* name of long option */
+        int has_arg;    /* whether option takes an argument */
+        int *flag;      /* if not NULL, set *flag to val when option found */
+        int val;        /* if flag is not NULL, value to set *flag to. */
+                        /* if flag is NULL, return value */
+};
+
+struct uoptions {
+	union {
+		const struct myoption *m;
+		const struct option *o;
+	} u;
+};
+
+static const struct myoption long_options[] = {
 				{ "listening-device", required_argument, NULL, 'd' },
 				{ "listening-port", required_argument, NULL, 'p' },
 				{ "tls-listening-port", required_argument, NULL, TLS_PORT_OPT },
@@ -686,6 +709,9 @@ static struct option long_options[] = {
 #if !defined(TURN_NO_MYSQL)
 				{ "mysql-userdb", required_argument, NULL, 'M' },
 #endif
+#if !defined(TURN_NO_MONGO)
+				{ "mongo-userdb", required_argument, NULL, 'J' },
+#endif
 #if !defined(TURN_NO_HIREDIS)
 				{ "redis-userdb", required_argument, NULL, 'N' },
 				{ "redis-statsdb", required_argument, NULL, 'O' },
@@ -755,13 +781,13 @@ static struct option long_options[] = {
 				{ NULL, no_argument, NULL, 0 }
 };
 
-static struct option admin_long_options[] = {
+static const struct myoption 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) || !defined(TURN_NO_HIREDIS)
+#if !defined(TURN_NO_PQ) || !defined(TURN_NO_MYSQL) || !defined(TURN_NO_MONGO) || !defined(TURN_NO_HIREDIS)
 				{ "set-secret", required_argument, NULL, 's' },
 				{ "show-secret", no_argument, NULL, 'S' },
 				{ "delete-secret", required_argument, NULL, 'X' },
@@ -777,6 +803,9 @@ static struct option admin_long_options[] = {
 #if !defined(TURN_NO_MYSQL)
 				{ "mysql-userdb", required_argument, NULL, 'M' },
 #endif
+#if !defined(TURN_NO_MONGO)
+				{ "mongo-userdb", required_argument, NULL, 'J' },
+#endif
 #if !defined(TURN_NO_HIREDIS)
 				{ "redis-userdb", required_argument, NULL, 'N' },
 #endif
@@ -784,7 +813,7 @@ static struct option admin_long_options[] = {
 				{ "realm", required_argument, NULL, 'r' },
 				{ "password", required_argument, NULL, 'p' },
 				{ "sha256", no_argument, NULL, 'H' },
-#if !defined(TURN_NO_PQ) || !defined(TURN_NO_MYSQL) || !defined(TURN_NO_HIREDIS)
+#if !defined(TURN_NO_PQ) || !defined(TURN_NO_MYSQL) || !defined(TURN_NO_MONGO) || !defined(TURN_NO_HIREDIS)
 				{ "add-origin", no_argument, NULL, 'O' },
 				{ "del-origin", no_argument, NULL, 'R' },
 				{ "list-origins", required_argument, NULL, 'I' },
@@ -1071,6 +1100,12 @@ static void set_option(int c, char *value)
 		turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MYSQL;
 		break;
 #endif
+#if !defined(TURN_NO_MONGO)
+	case 'J':
+		STRCPY(turn_params.default_users_db.persistent_users_db.userdb, value);
+		turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MONGO;
+		break;
+#endif
 #if !defined(TURN_NO_HIREDIS)
 	case 'N':
 		STRCPY(turn_params.default_users_db.persistent_users_db.userdb, value);
@@ -1340,7 +1375,10 @@ static int adminmain(int argc, char **argv)
 	u08bits origin[STUN_MAX_ORIGIN_SIZE+1]="";
 	perf_options_t po = {(band_limit_t)-1,-1,-1};
 
-	while (((c = getopt_long(argc, argv, ADMIN_OPTIONS, admin_long_options, NULL)) != -1)) {
+	struct uoptions uo;
+	uo.u.m = admin_long_options;
+
+	while (((c = getopt_long(argc, argv, ADMIN_OPTIONS, uo.u.o, NULL)) != -1)) {
 		switch (c){
 		case 'g':
 			ct = TA_SET_REALM_OPTION;
@@ -1393,7 +1431,7 @@ static int adminmain(int argc, char **argv)
 			ct = TA_LIST_USERS;
 			is_st = 1;
 			break;
-#if !defined(TURN_NO_PQ) || !defined(TURN_NO_MYSQL) || !defined(TURN_NO_HIREDIS)
+#if !defined(TURN_NO_PQ) || !defined(TURN_NO_MYSQL) || !defined(TURN_NO_MONGO) || !defined(TURN_NO_HIREDIS)
 		case 's':
 			ct = TA_SET_SECRET;
 			STRCPY(secret,optarg);
@@ -1426,6 +1464,12 @@ static int adminmain(int argc, char **argv)
 		  turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MYSQL;
 		  break;
 #endif
+#if !defined(TURN_NO_MONGO)
+		case 'J':
+		  STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg);
+		  turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MONGO;
+		  break;
+#endif
 #if !defined(TURN_NO_HIREDIS)
 		case 'N':
 		  STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg);
@@ -1540,6 +1584,12 @@ static void print_features(unsigned long mfn)
 	TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MySQL is not supported\n");
 #endif
 
+#if !defined(TURN_NO_MONGO)
+	TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MongoDB supported\n");
+#else
+	TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MongoDB is not supported\n");
+#endif
+
 #if defined(OPENSSL_THREADS)
 	//TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "OpenSSL multithreading supported\n");
 #else
@@ -1640,7 +1690,11 @@ int main(int argc, char **argv)
 	init_dynamic_ip_lists();
 
 	if (!strstr(argv[0], "turnadmin")) {
-		while (((c = getopt_long(argc, argv, OPTIONS, long_options, NULL)) != -1)) {
+
+		struct uoptions uo;
+		uo.u.m = long_options;
+
+		while (((c = getopt_long(argc, argv, OPTIONS, uo.u.o, NULL)) != -1)) {
 			switch (c){
 			case 'l':
 				set_logfile(optarg);
@@ -1700,7 +1754,10 @@ int main(int argc, char **argv)
 
 	read_config_file(argc,argv,0);
 
-	while (((c = getopt_long(argc, argv, OPTIONS, long_options, NULL)) != -1)) {
+	struct uoptions uo;
+	uo.u.m = long_options;
+
+	while (((c = getopt_long(argc, argv, OPTIONS, uo.u.o, NULL)) != -1)) {
 		if(c != 'u')
 			set_option(c,optarg);
 	}
@@ -1709,7 +1766,7 @@ int main(int argc, char **argv)
 
 	optind = 0;
 
-	while (((c = getopt_long(argc, argv, OPTIONS, long_options, NULL)) != -1)) {
+	while (((c = getopt_long(argc, argv, OPTIONS, uo.u.o, NULL)) != -1)) {
 	  if(c == 'u') {
 	    set_option(c,optarg);
 	  }

+ 0 - 2
src/apps/relay/mainrelay.h

@@ -83,8 +83,6 @@
 
 #include "ns_ioalib_impl.h"
 
-#include "hiredis_libevent2.h"
-
 #ifdef __cplusplus
 extern "C" {
 #endif

+ 83 - 35
src/apps/relay/netengine.c

@@ -42,8 +42,8 @@ static pthread_barrier_t barrier;
 #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 struct relay_server *general_relay_servers[1+((turnserver_id)-1)];
+static struct relay_server *udp_relay_servers[1+((turnserver_id)-1)];
 
 //////////////////////////////////////////////
 
@@ -401,6 +401,11 @@ static void auth_server_receive_message(struct bufferevent *bev, void *ptr)
 		      TURN_LOG_LEVEL_ERROR,
 		      "%s: Too large UDP relay number: %d\n",
 		      __FUNCTION__,(int)dest);
+      } else if(!(udp_relay_servers[dest])) {
+    	  TURN_LOG_FUNC(
+    	  	TURN_LOG_LEVEL_ERROR,
+    	  	"%s: Wrong UDP relay number: %d, total %d\n",
+    	  	__FUNCTION__,(int)dest, (int)get_real_udp_relay_servers_number());
       } else {
     	  output = bufferevent_get_output(udp_relay_servers[dest]->auth_out_buf);
       }
@@ -408,8 +413,13 @@ static void auth_server_receive_message(struct bufferevent *bev, void *ptr)
       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);
+		      "%s: Too large general relay number: %d, total %d\n",
+		      __FUNCTION__,(int)dest,(int)get_real_general_relay_servers_number());
+      } else if(!(general_relay_servers[dest])) {
+    	  TURN_LOG_FUNC(
+    	  		      TURN_LOG_LEVEL_ERROR,
+    	  		      "%s: Wrong general relay number: %d, total %d\n",
+    	  		      __FUNCTION__,(int)dest,(int)get_real_general_relay_servers_number());
       } else {
     	  output = bufferevent_get_output(general_relay_servers[dest]->auth_out_buf);
       }
@@ -437,35 +447,36 @@ static int send_socket_to_general_relay(ioa_engine_handle e, struct message_to_r
 
 	smptr->t = RMT_SOCKET;
 
-	{
-		struct evbuffer *output = NULL;
-		int success = 0;
-
-		output = bufferevent_get_output(rdest->out_buf);
+	struct evbuffer *output = NULL;
+	int success = 0;
 
-		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 {
+	if(!rdest) {
+		success = -1;
+		goto label_end;
+	}
 
-				success = 1;
-				smptr->m.sm.nd.nbh=NULL;
-			}
+	output = bufferevent_get_output(rdest->out_buf);
 
-		}
+	if(output) {
 
-		if(!success) {
-			ioa_network_buffer_delete(e, smptr->m.sm.nd.nbh);
+		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;
+		}
+	}
 
-			IOA_CLOSE_SOCKET(smptr->m.sm.s);
+	label_end:
 
-			return -1;
-		}
+	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;
@@ -487,23 +498,39 @@ static int send_socket_to_relay(turnserver_id id, u64bits cid, stun_tid *tid, io
 		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);
+					"%s: Too large UDP relay number: %d, rmt=%d, total=%d\n",
+					__FUNCTION__,(int)dest,(int)rmt, (int)get_real_udp_relay_servers_number());
 			ret = -1;
 			goto err;
 		}
 		rs = udp_relay_servers[dest];
+		if(!rs) {
+			TURN_LOG_FUNC(
+				TURN_LOG_LEVEL_ERROR,
+					"%s: Wrong UDP relay number: %d, rmt=%d, total=%d\n",
+					__FUNCTION__,(int)dest,(int)rmt, (int)get_real_udp_relay_servers_number());
+			ret = -1;
+			goto err;
+		}
 	} 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);
+					"%s: Too large general relay number: %d, rmt=%d, total=%d\n",
+					__FUNCTION__,(int)dest,(int)rmt, (int)get_real_general_relay_servers_number());
 			ret = -1;
 			goto err;
 		}
 		rs = general_relay_servers[dest];
+		if(!rs) {
+			TURN_LOG_FUNC(
+				TURN_LOG_LEVEL_ERROR,
+				"%s: Wrong general relay number: %d, rmt=%d, total=%d\n",
+				__FUNCTION__,(int)dest,(int)rmt, (int)get_real_general_relay_servers_number());
+			ret = -1;
+			goto err;
+		}
 	}
 
 	switch (rmt) {
@@ -749,7 +776,12 @@ static void listener_receive_message(struct bufferevent *bev, void *ptr)
 		if(turn_params.net_engine_version == NEV_UDP_SOCKET_PER_THREAD) {
 			size_t ri;
 			for(ri=0;ri<get_real_general_relay_servers_number();ri++) {
-				if(general_relay_servers[ri]->thr == pthread_self()) {
+				if(!(general_relay_servers[ri])) {
+					TURN_LOG_FUNC(
+					    	  TURN_LOG_LEVEL_ERROR,
+					    	       "%s: Wrong general relay number: %d, total %d\n",
+					    	       __FUNCTION__,(int)ri,(int)get_real_general_relay_servers_number());
+				} else if(general_relay_servers[ri]->thr == pthread_self()) {
 					relay_thread_index=ri;
 					break;
 				}
@@ -891,7 +923,7 @@ static void setup_listener(void)
 		bufferevent_enable(turn_params.listener.in_buf, EV_READ);
 	}
 
-	if(turn_params.listener.addrs_number<2) {
+	if(turn_params.listener.addrs_number<2 || turn_params.external_ip) {
 		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 {
@@ -979,7 +1011,6 @@ static void setup_socket_per_endpoint_udp_listener_servers(void)
 
 	{
 		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++) {
 
@@ -1520,8 +1551,6 @@ 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;i<get_real_general_relay_servers_number();i++) {
 
 		if(turn_params.general_relay_servers_number == 0) {
@@ -1658,6 +1687,25 @@ void setup_server(void)
 		setup_tcp_listener_servers(turn_params.listener.ioa_eng, NULL);
 	}
 
+	{
+		int tot = 0;
+		if(udp_relay_servers[0]) {
+			tot = get_real_udp_relay_servers_number();
+		}
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Total UDP servers: %d\n",(int)tot);
+	}
+
+	{
+		int tot = get_real_general_relay_servers_number();
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Total General servers: %d\n",(int)tot);
+		int i;
+		for(i = 0;i<tot;i++) {
+			if(!(general_relay_servers[i])) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"General server %d is not initialized !\n",(int)i);
+			}
+		}
+	}
+
 	setup_auth_server();
 	if(use_cli)
 		setup_cli_server();

+ 9 - 7
src/apps/relay/ns_ioalib_engine_impl.c

@@ -2761,10 +2761,12 @@ static void eventcb_bev(struct bufferevent *bev, short events, void *arg)
 							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) {
+								if(s == ss->client_socket) {
 									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 if(s == ss->alloc.relay_sessions[ALLOC_IPV4_INDEX].s) {
+									shutdown_client_connection(server, ss, 0, "TCP connection closed by peer (ipv4 callback)");
+								} else if(s == ss->alloc.relay_sessions[ALLOC_IPV6_INDEX].s) {
+									shutdown_client_connection(server, ss, 0, "TCP connection closed by peer (ipv6 callback)");
 								} else {
 									shutdown_client_connection(server, ss, 0, "TCP connection closed by remote party (callback)");
 								}
@@ -3432,10 +3434,10 @@ void turn_report_allocation_set(void *a, turn_time_t lifetime, int refresh)
 			turn_turnserver *server = (turn_turnserver*)ss->server;
 			if(server) {
 				ioa_engine_handle e = turn_server_get_engine(server);
-				if(e && e->verbose && ss->client_session.s) {
-					if(ss->client_session.s->ssl) {
-						TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: %s, realm=<%s>, username=<%s>, lifetime=%lu, cipher=%s, method=%s (%s)\n", (unsigned long long)ss->id, status, (char*)ss->realm_options.name, (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);
+				if(e && e->verbose && ss->client_socket) {
+					if(ss->client_socket->ssl) {
+						TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: %s, realm=<%s>, username=<%s>, lifetime=%lu, cipher=%s, method=%s (%s)\n", (unsigned long long)ss->id, status, (char*)ss->realm_options.name, (char*)ss->username, (unsigned long)lifetime, SSL_get_cipher(ss->client_socket->ssl),
+							turn_get_ssl_method(ss->client_socket->ssl, ss->client_socket->orig_ctx_type),ss->client_socket->orig_ctx_type);
 					} else {
 						TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: %s, realm=<%s>, username=<%s>, lifetime=%lu\n", (unsigned long long)ss->id, status, (char*)ss->realm_options.name, (char*)ss->username, (unsigned long)lifetime);
 					}

+ 35 - 22
src/apps/relay/turncli.c

@@ -433,26 +433,28 @@ static int print_session(ur_map_key_type key, ur_map_value_type value, void *arg
 					return 0;
 
 		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 {
+
+			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;
 				}
+			}
+
+			ur_string_map_value_type value;
+			if(!ur_string_map_get(csarg->users, (ur_string_map_key_type)(char*)tsi->username, &value)) {
 				value = (ur_string_map_value_type)csarg->users_number;
 				csarg->users_number += 1;
 				csarg->user_counters = (size_t*)turn_realloc(csarg->user_counters,
@@ -502,13 +504,19 @@ static int print_session(ur_map_key_type key, ur_map_value_type value, void *arg
 						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);
+					if(!tsi->relay_addr_data_ipv4.saddr[0])
+						addr_to_string(&(tsi->relay_addr_data_ipv4.addr),(u08bits*)tsi->relay_addr_data_ipv4.saddr);
+					if(!tsi->relay_addr_data_ipv6.saddr[0])
+						addr_to_string(&(tsi->relay_addr_data_ipv6.addr),(u08bits*)tsi->relay_addr_data_ipv6.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);
+					if(tsi->relay_addr_data_ipv4.saddr[0]) {
+						myprintf(cs,"      relay addr %s\n", tsi->relay_addr_data_ipv4.saddr);
+					}
+					if(tsi->relay_addr_data_ipv6.saddr[0]) {
+						myprintf(cs,"      relay addr %s\n", tsi->relay_addr_data_ipv6.saddr);
+					}
 				}
 				myprintf(cs,"      fingerprints enforced: %s\n",get_flag(tsi->enforce_fingerprints));
 				myprintf(cs,"      mobile: %s\n",get_flag(tsi->is_mobile));
@@ -758,6 +766,11 @@ static void cli_print_configuration(struct cli_session* cs)
 				cli_print_str(cs,"MySQL/MariaDB","DB type",0);
 				break;
 #endif
+#if !defined(TURN_NO_MONGO)
+			case TURN_USERDB_TYPE_MONGO:
+				cli_print_str(cs,"MongoDB","DB type",0);
+				break;
+#endif
 #if !defined(TURN_NO_HIREDIS)
 			case TURN_USERDB_TYPE_REDIS:
 				cli_print_str(cs,"redis","DB type",0);

File diff suppressed because it is too large
+ 34 - 959
src/apps/relay/userdb.c


+ 8 - 1
src/apps/relay/userdb.h

@@ -77,6 +77,10 @@ struct _realm_params_t {
 
 };
 
+void lock_realms(void);
+void unlock_realms(void);
+void update_o_to_realm(ur_string_map * o_to_realm_new);
+
 //////////// USER DB //////////////////////////////
 
 struct auth_message {
@@ -100,6 +104,9 @@ enum _TURN_USERDB_TYPE {
 #if !defined(TURN_NO_MYSQL)
 	,TURN_USERDB_TYPE_MYSQL
 #endif
+#if !defined(TURN_NO_MONGO)
+	,TURN_USERDB_TYPE_MONGO
+#endif
 #if !defined(TURN_NO_HIREDIS)
 	,TURN_USERDB_TYPE_REDIS
 #endif
@@ -194,7 +201,7 @@ void reread_realms(void);
 int add_user_account(char *user, int dynamic);
 int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08bits *origin, TURNADMIN_COMMAND_TYPE ct, int is_st, perf_options_t* po);
 
-int add_ip_list_range(char* range, ip_range_list_t * list);
+int add_ip_list_range(const char* range, ip_range_list_t * list);
 
 ///////////// Redis //////////////////////
 

+ 7 - 1
src/apps/uclient/mainuclient.c

@@ -95,6 +95,8 @@ char origin[STUN_MAX_ORIGIN_SIZE+1] = "\0";
 
 band_limit_t bps = 0;
 
+int dual_allocation = 0;
+
 //////////////// local definitions /////////////////
 
 static char Usage[] =
@@ -127,6 +129,7 @@ static char Usage[] =
   "		(for testing the non-standard server relay functionality).\n"
   "	-G	Generate extra requests (create permissions, channel bind).\n"
   " -B  Random disconnect after a few initial packets.\n"
+  " -Z  Dual allocation.\n"
   "Options:\n"
   "	-l	Message length (Default: 100 Bytes).\n"
   "	-i	Certificate file (for secure connections only, optional).\n"
@@ -201,7 +204,7 @@ int main(int argc, char **argv)
 
 	ns_bzero(local_addr, sizeof(local_addr));
 
-	while ((c = getopt(argc, argv, "a:d:p:l:n:L:m:e:r:u:w:i:k:z:W:C:E:F:o:vsyhcxXgtTSAPDNOUHMRIGB")) != -1) {
+	while ((c = getopt(argc, argv, "a:d:p:l:n:L:m:e:r:u:w:i:k:z:W:C:E:F:o:ZvsyhcxXgtTSAPDNOUHMRIGB")) != -1) {
 		switch (c){
 		case 'a':
 			bps = (band_limit_t)atol(optarg);
@@ -255,6 +258,9 @@ int main(int argc, char **argv)
 		case 'z':
 			RTP_PACKET_INTERVAL = atoi(optarg);
 			break;
+		case 'Z':
+			dual_allocation = 1;
+			break;
 		case 'A':
 			use_short_term = 1;
 			break;

+ 0 - 1
src/apps/uclient/session.h

@@ -95,7 +95,6 @@ typedef struct {
   uint16_t chnum;
   int wait_cycles;
   int timer_cycle;
-  int known_mtu;
   int completed;
   struct event *input_ev;
   struct event *input_tcp_data_ev;

+ 99 - 31
src/apps/uclient/startuclient.c

@@ -306,7 +306,9 @@ static int clnet_allocate(int verbose,
 		app_ur_conn_info *clnet_info,
 		ioa_addr *relay_addr,
 		int af,
-		char *turn_addr, u16bits *turn_port) {
+		char *turn_addr, u16bits *turn_port,
+		stun_tid *in_tid,
+		stun_tid *out_tid) {
 
 	int af_cycle = 0;
 	int reopen_socket = 0;
@@ -332,20 +334,35 @@ static int clnet_allocate(int verbose,
 		}
 
 		stun_buffer message;
-		if(current_reservation_token)
+		if(!in_tid && current_reservation_token) {
 			af = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT;
+		}
+
+		int af4 = dual_allocation || (af == STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4);
+		int af6 = dual_allocation || (af == STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6);
+
+		if(!no_rtcp && !in_tid) {
+			if (!never_allocate_rtcp && allocate_rtcp) {
+				af4 = 0;
+				af6 = 0;
+			}
+		}
 
 		if(!dos)
-			stun_set_allocate_request(&message, 800, af, relay_transport, mobility);
+			stun_set_allocate_request(&message, 800, af4, af6, relay_transport, mobility);
 		else
-			stun_set_allocate_request(&message, 300, af, relay_transport, mobility);
+			stun_set_allocate_request(&message, 300, af4, af6, relay_transport, mobility);
 
 		if(bps)
 			stun_attr_add_bandwidth_str(message.buf, (size_t*)(&(message.len)), bps);
 
+		if(in_tid) {
+			stun_tid_message_cpy(message.buf, in_tid);
+		}
+
 		if(dont_fragment)
 			stun_attr_add(&message, STUN_ATTRIBUTE_DONT_FRAGMENT, NULL, 0);
-		if(!no_rtcp) {
+		if(!no_rtcp && !in_tid) {
 		  if (!never_allocate_rtcp && allocate_rtcp) {
 		    uint64_t reservation_token = ioa_ntoh64(current_reservation_token);
 		    stun_attr_add(&message, STUN_ATTRIBUTE_RESERVATION_TOKEN,
@@ -355,13 +372,22 @@ static int clnet_allocate(int verbose,
 		  }
 		}
 
-		if(origin[0])
+		if(origin[0]) {
+			const char* some_origin = "https://carleon.gov:443";
+			stun_attr_add(&message, STUN_ATTRIBUTE_ORIGIN, some_origin, strlen(some_origin));
 			stun_attr_add(&message, STUN_ATTRIBUTE_ORIGIN, origin, strlen(origin));
+			some_origin = "ftp://uffrith.net";
+			stun_attr_add(&message, STUN_ATTRIBUTE_ORIGIN, some_origin, strlen(some_origin));
+		}
 
 		if(add_integrity(clnet_info, &message)<0) return -1;
 
 		stun_attr_add_fingerprint_str(message.buf,(size_t*)&(message.len));
 
+		if(out_tid) {
+			stun_tid_from_message_str(message.buf, (size_t)message.len, out_tid);
+		}
+
 		while (!allocate_sent) {
 
 			int len = send_buffer(clnet_info, &message,0,0);
@@ -414,22 +440,55 @@ static int clnet_allocate(int verbose,
 						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");
+						{
+							int found = 0;
+
+							stun_attr_ref sar = stun_attr_get_first(&message);
+							while (sar) {
+
+								int attr_type = stun_attr_get_type(sar);
+								if(attr_type == STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS) {
+
+									if (stun_attr_get_addr(&message, sar, relay_addr, NULL) < 0) {
+										TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,
+											"%s: !!!: relay addr cannot be received (1)\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");
+										}
+
+										if(!addr_any(relay_addr)) {
+											if(relay_addr->ss.sa_family == AF_INET) {
+												if(default_address_family != STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6) {
+													found = 1;
+													break;
+												}
+											}
+											if(relay_addr->ss.sa_family == AF_INET6) {
+												if(default_address_family == STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6) {
+													found = 1;
+													break;
+												}
+											}
+										}
+									}
+								}
+
+								sar = stun_attr_get_next(&message,sar);
+							}
+
+							if(!found) {
+								TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,
+										"%s: !!!: relay addr cannot be received (2)\n",
+										__FUNCTION__);
+								return -1;
 							}
 						}
+
 						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);
@@ -514,7 +573,9 @@ static int clnet_allocate(int verbose,
 	  exit(-1);
 	}
 
-	allocate_rtcp = !allocate_rtcp;
+	if(!in_tid) {
+		allocate_rtcp = !allocate_rtcp;
+	}
 
 	if (1) {
 
@@ -895,7 +956,9 @@ int start_connection(uint16_t 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);
+	stun_tid tid;
+
+	clnet_allocate(verbose, clnet_info_probe, &relay_addr, default_address_family, remote_address, &clnet_remote_port,NULL,NULL);
 
 	/* Real: */
 
@@ -915,15 +978,20 @@ int start_connection(uint16_t clnet_remote_port0,
 	}
 
 	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) {
+	if (clnet_allocate(verbose, clnet_info, &relay_addr, af, NULL,NULL,NULL,&tid) < 0) {
 	  exit(-1);
 	}
 
+	//strcpy((char*)g_uname,"qqq");
+	//if (clnet_allocate(verbose, clnet_info, &relay_addr, af, NULL,NULL,&tid,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) {
+	  if (clnet_allocate(verbose, clnet_info_rtcp, &relay_addr_rtcp, af,NULL,NULL,NULL,NULL) < 0) {
 	    exit(-1);
 	  }
 	  if(rare_event()) return 0;
@@ -1110,7 +1178,7 @@ int start_c2c_connection(uint16_t 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);
+	clnet_allocate(verbose, clnet_info_probe, &relay_addr1, default_address_family, remote_address, &clnet_remote_port,NULL,NULL);
 
 	if(rare_event()) return 0;
 
@@ -1143,7 +1211,7 @@ int start_c2c_connection(uint16_t clnet_remote_port0,
 
 	if(!no_rtcp) {
 
-	  if (clnet_allocate(verbose, clnet_info1, &relay_addr1, default_address_family,NULL,NULL)
+	  if (clnet_allocate(verbose, clnet_info1, &relay_addr1, default_address_family,NULL,NULL,NULL,NULL)
 	      < 0) {
 	    exit(-1);
 	  }
@@ -1151,13 +1219,13 @@ int start_c2c_connection(uint16_t clnet_remote_port0,
 	  if(rare_event()) return 0;
 
 	  if (clnet_allocate(verbose, clnet_info1_rtcp,
-			   &relay_addr1_rtcp, default_address_family,NULL,NULL) < 0) {
+			   &relay_addr1_rtcp, default_address_family,NULL,NULL,NULL,NULL) < 0) {
 	    exit(-1);
 	  }
 	  
 	  if(rare_event()) return 0;
 
-	  if (clnet_allocate(verbose, clnet_info2, &relay_addr2, default_address_family,NULL,NULL)
+	  if (clnet_allocate(verbose, clnet_info2, &relay_addr2, default_address_family,NULL,NULL,NULL,NULL)
 	      < 0) {
 	    exit(-1);
 	  }
@@ -1165,20 +1233,20 @@ int start_c2c_connection(uint16_t clnet_remote_port0,
 	  if(rare_event()) return 0;
 
 	  if (clnet_allocate(verbose, clnet_info2_rtcp,
-			   &relay_addr2_rtcp, default_address_family,NULL,NULL) < 0) {
+			   &relay_addr2_rtcp, default_address_family,NULL,NULL,NULL,NULL) < 0) {
 	    exit(-1);
 	  }
 
 	  if(rare_event()) return 0;
 	} else {
 
-	  if (clnet_allocate(verbose, clnet_info1, &relay_addr1, default_address_family,NULL,NULL)
+	  if (clnet_allocate(verbose, clnet_info1, &relay_addr1, default_address_family,NULL,NULL,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) {
+		  if (clnet_allocate(verbose, clnet_info2, &relay_addr2, default_address_family,NULL,NULL,NULL,NULL) < 0) {
 			  exit(-1);
 		  }
 		  if(rare_event()) return 0;

+ 1 - 0
src/apps/uclient/uclient.h

@@ -82,6 +82,7 @@ extern int mobility;
 extern int no_permissions;
 extern int extra_requests;
 extern band_limit_t bps;
+extern int dual_allocation;
 
 extern char origin[STUN_MAX_ORIGIN_SIZE+1];
 

+ 5 - 4
src/client++/TurnMsgLib.h

@@ -848,8 +848,8 @@ public:
 	/**
 	 * 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);
+	void constructAllocateRequest(u32bits lifetime, int af4, int af6, u08bits transport, int mobile) {
+		stun_set_allocate_request_str(_buffer, &_sz, lifetime, af4, af6, transport, mobile);
 	}
 
 	/**
@@ -1011,13 +1011,14 @@ public:
 	 * Construct allocate response
 	 */
 	void constructAllocateResponse(stun_tid &tid,
-					   const ioa_addr &relayed_addr,
+					   const ioa_addr &relayed_addr1,
+					   const ioa_addr &relayed_addr2,
 					   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,
+						   &relayed_addr1, &relayed_addr2,
 						   &reflexive_addr,
 						   lifetime, error_code, reason,
 						   reservation_token, mobile_id);

+ 19 - 15
src/client/ns_turn_msg.c

@@ -644,7 +644,7 @@ int stun_get_message_len_str(u08bits *buf, size_t blen, int padding, size_t *app
 
 ////////// ALLOCATE ///////////////////////////////////
 
-int stun_set_allocate_request_str(u08bits* buf, size_t *len, u32bits lifetime, int address_family,
+int stun_set_allocate_request_str(u08bits* buf, size_t *len, u32bits lifetime, int af4, int af6,
 				u08bits transport, int mobile) {
 
   stun_init_request_str(STUN_METHOD_ALLOCATE, buf, len);
@@ -671,30 +671,30 @@ int stun_set_allocate_request_str(u08bits* buf, size_t *len, u32bits lifetime, i
   }
 
   //ADRESS-FAMILY
-  switch (address_family) {
-  case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4:
-  case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6:
-  {
+  if (af4) {
 	  u08bits field[4];
-	  field[0] = (u08bits)address_family;
+	  field[0] = (u08bits)STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4;
 	  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;
+
+  if (af6) {
+  	  u08bits field[4];
+  	  field[0] = (u08bits)STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6;
+  	  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;
   };
 
   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,
+				   const ioa_addr *relayed_addr1, const ioa_addr *relayed_addr2,
+				   const ioa_addr *reflexive_addr,
 				   u32bits lifetime, int error_code, const u08bits *reason,
 				   u64bits reservation_token, char* mobile_id) {
 
@@ -702,8 +702,12 @@ int stun_set_allocate_response_str(u08bits* buf, size_t *len, stun_tid* tid,
 
     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(relayed_addr1) {
+      if(stun_attr_add_addr_str(buf,len,STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS,relayed_addr1)<0) return -1;
+    }
+
+    if(relayed_addr2) {
+      if(stun_attr_add_addr_str(buf,len,STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS,relayed_addr2)<0) return -1;
     }
     
     if(reflexive_addr) {

+ 2 - 2
src/client/ns_turn_msg.h

@@ -155,9 +155,9 @@ int stun_attr_add_channel_number_str(u08bits* buf, size_t *len, u16bits chnumber
 int stun_attr_add_bandwidth_str(u08bits* buf, size_t *len, band_limit_t bps);
 u16bits stun_attr_get_first_channel_number_str(const u08bits *buf, size_t len);
 
-int stun_set_allocate_request_str(u08bits* buf, size_t *len, u32bits lifetime, int address_family, u08bits transport, int mobile);
+int stun_set_allocate_request_str(u08bits* buf, size_t *len, u32bits lifetime, int af4, int af6, u08bits transport, int mobile);
 int stun_set_allocate_response_str(u08bits* buf, size_t *len, stun_tid* tid, 
-				   const ioa_addr *relayed_addr,
+				   const ioa_addr *relayed_addr1, const ioa_addr *relayed_addr2,
 				   const ioa_addr *reflexive_addr,
 				   u32bits lifetime, int error_code, const u08bits *reason,
 				   u64bits reservation_token, char *mobile_id);

+ 3 - 3
src/ns_turn_defs.h

@@ -31,9 +31,9 @@
 #ifndef __IOADEFS__
 #define __IOADEFS__
 
-#define TURN_SERVER_VERSION "4.0.1.3"
-#define TURN_SERVER_VERSION_NAME "Severard"
-#define TURN_SOFTWARE "Coturn-"TURN_SERVER_VERSION" '"TURN_SERVER_VERSION_NAME"'"
+#define TURN_SERVER_VERSION "4.1.0.1"
+#define TURN_SERVER_VERSION_NAME "Vitari"
+#define TURN_SOFTWARE "Coturn-" TURN_SERVER_VERSION " '" TURN_SERVER_VERSION_NAME "'"
 
 #if (defined(__unix__) || defined(unix)) && !defined(USG)
 #include <sys/param.h>

+ 59 - 13
src/server/ns_turn_allocation.c

@@ -62,7 +62,6 @@ void clear_allocation(allocation *a)
 				if(tc) {
 					delete_tcp_connection(tc);
 					a->tcs.elems[i] = NULL;
-					break;
 				}
 			}
 			turn_free(a->tcs.elems,sz*sizeof(tcp_connection*));
@@ -70,10 +69,14 @@ void clear_allocation(allocation *a)
 		}
 		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);
+		{
+			int i;
+			for(i = 0;i<ALLOC_PROTOCOLS_NUMBER; ++i) {
+				clear_ioa_socket_session_if(a->relay_sessions[i].s, a->owner);
+				clear_relay_endpoint_session_data(&(a->relay_sessions[i]));
+				IOA_EVENT_DEL(a->relay_sessions[i].lifetime_ev);
+			}
+		}
 
 		/* The order is important here: */
 		free_turn_permission_hashtable(&(a->addr_to_perm));
@@ -83,22 +86,65 @@ void clear_allocation(allocation *a)
 	}
 }
 
-ts_ur_session *get_relay_session(allocation *a)
+relay_endpoint_session *get_relay_session(allocation *a, int family)
+{
+	if(a)
+		return &(a->relay_sessions[ALLOC_INDEX(family)]);
+	return NULL;
+}
+
+int get_relay_session_failure(allocation *a, int family)
+{
+	if(a)
+		return a->relay_sessions_failure[ALLOC_INDEX(family)];
+	return 0;
+}
+
+void set_relay_session_failure(allocation *a, int family)
 {
-	return &(a->relay_session);
+	if(a)
+		a->relay_sessions_failure[ALLOC_INDEX(family)] = 1;
 }
 
-ioa_socket_handle get_relay_socket(allocation *a)
+ioa_socket_handle get_relay_socket(allocation *a, int family)
 {
-	return a->relay_session.s;
+	if(a)
+		return a->relay_sessions[ALLOC_INDEX(family)].s;
+	return NULL;
+}
+
+void set_allocation_family_invalid(allocation *a, int family)
+{
+	if(a) {
+		size_t index = ALLOC_INDEX(family);
+		if(a->relay_sessions[index].s) {
+			if(a->tcs.elems) {
+				size_t i;
+				size_t sz = a->tcs.sz;
+				for(i=0;i<sz;++i) {
+					tcp_connection *tc = a->tcs.elems[i];
+					if(tc) {
+						if(tc->peer_s && (get_ioa_socket_address_family(tc->peer_s) == family)) {
+							delete_tcp_connection(tc);
+							a->tcs.elems[i] = NULL;
+						}
+					}
+				}
+			}
+
+			clear_ioa_socket_session_if(a->relay_sessions[index].s, a->owner);
+			clear_relay_endpoint_session_data(&(a->relay_sessions[index]));
+			IOA_EVENT_DEL(a->relay_sessions[index].lifetime_ev);
+		}
+	}
 }
 
-void set_allocation_lifetime_ev(allocation *a, turn_time_t exp_time, ioa_timer_handle ev)
+void set_allocation_lifetime_ev(allocation *a, turn_time_t exp_time, ioa_timer_handle ev, int family)
 {
 	if (a) {
-		IOA_EVENT_DEL(a->lifetime_ev);
-		a->expiration_time = exp_time;
-		a->lifetime_ev = ev;
+		IOA_EVENT_DEL(a->relay_sessions[ALLOC_INDEX(family)].lifetime_ev);
+		a->relay_sessions[ALLOC_INDEX(family)].expiration_time = exp_time;
+		a->relay_sessions[ALLOC_INDEX(family)].lifetime_ev = ev;
 	}
 }
 

+ 18 - 20
src/server/ns_turn_allocation.h

@@ -45,26 +45,16 @@ extern "C" {
 #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;
+	turn_time_t expiration_time;
+	ioa_timer_handle lifetime_ev;
+} relay_endpoint_session;
 
-static inline void clear_ts_ur_session_data(ts_ur_session* cdi)
+static inline void clear_relay_endpoint_session_data(relay_endpoint_session* cdi)
 {
 	if (cdi)
 		IOA_CLOSE_SOCKET(cdi->s);
@@ -175,13 +165,18 @@ typedef struct _turn_permission_hashtable {
 
 //////////////// ALLOCATION //////////////////////
 
+#define ALLOC_IPV4_INDEX (0)
+#define ALLOC_IPV6_INDEX (1)
+#define ALLOC_PROTOCOLS_NUMBER (2)
+#define ALLOC_INDEX(family) ((((family)==AF_INET6)) ? ALLOC_IPV6_INDEX : ALLOC_IPV4_INDEX )
+#define ALLOC_INDEX_ADDR(addr) ALLOC_INDEX(((addr)->ss).sa_family)
+
 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;
+  relay_endpoint_session relay_sessions[ALLOC_PROTOCOLS_NUMBER];
+  int relay_sessions_failure[ALLOC_PROTOCOLS_NUMBER];
   ch_map chns; /* chnum-to-ch_info* */
   void *owner; //ss
   ur_map *tcp_connections; //global (per turn server) reference
@@ -202,7 +197,7 @@ 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);
+void set_allocation_lifetime_ev(allocation *a, turn_time_t exp_time, ioa_timer_handle ev, int family);
 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);
@@ -213,8 +208,11 @@ ch_info* allocation_get_new_ch_info(allocation* a, u16bits chnum, ioa_addr* peer
 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);
+relay_endpoint_session *get_relay_session(allocation *a, int family);
+int get_relay_session_failure(allocation *a, int family);
+void set_relay_session_failure(allocation *a, int family);
+ioa_socket_handle get_relay_socket(allocation *a, int family);
+void set_allocation_family_invalid(allocation *a, int family);
 
 tcp_connection *get_and_clean_tcp_connection_by_id(ur_map *map, tcp_connection_id id);
 tcp_connection *get_tcp_connection_by_id(ur_map *map, tcp_connection_id id);

+ 1 - 1
src/server/ns_turn_ioalib.h

@@ -260,7 +260,7 @@ unsigned char *base64_decode(const char *data,
 ///////////// Realm ///////////////////
 
 void get_default_realm_options(realm_options_t* ro);
-void get_realm_options_by_origin(char *origin, realm_options_t* ro);
+int get_realm_options_by_origin(char *origin, realm_options_t* ro);
 void get_realm_options_by_name(char *realm, realm_options_t* ro);
 int get_canonic_origin(const char* o, char *co, int sz);
 int get_default_protocol_port(const char* scheme, size_t slen);

File diff suppressed because it is too large
+ 412 - 173
src/server/ns_turn_server.c


+ 4 - 4
src/server/ns_turn_session.h

@@ -69,8 +69,7 @@ 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;
+  ioa_socket_handle client_socket;
   allocation alloc;
   ioa_timer_handle to_be_allocated_timeout_ev;
   u08bits nonce[NONCE_MAX_SIZE];
@@ -128,7 +127,8 @@ struct turn_session_info {
 	char tls_cipher[65];
 	addr_data local_addr_data;
 	addr_data remote_addr_data;
-	addr_data relay_addr_data;
+	addr_data relay_addr_data_ipv4;
+	addr_data relay_addr_data_ipv6;
 	u08bits username[STUN_MAX_USERNAME_SIZE+1];
 	int enforce_fingerprints;
 /* Stats */
@@ -147,7 +147,7 @@ struct turn_session_info {
 	addr_data *extra_peers_data;
 	size_t extra_peers_size;
 /* Realm */
-	char realm[STUN_MAX_REALM_SIZE+1];
+	char realm[STUN_MAX_REALM_SIZE + 1];
 	char origin[STUN_MAX_ORIGIN_SIZE + 1];
 /* Bandwidth */
 	band_limit_t bps;

+ 14 - 0
turndb/schema.mongo.sh

@@ -0,0 +1,14 @@
+#!/bin/sh
+
+mongo $* <<EOF
+
+use coturn;
+
+db.turnusers_lt.ensureIndex({ realm: 1, name: 1 }, { unique: 1 });
+db.turnusers_st.ensureIndex({ name: 1 }, { unique: 1 });
+db.turn_secret.ensureIndex({ realm: 1 }, { unique: 1 });
+db.realm.ensureIndex({ realm: 1 }, { unique: 1 });
+
+exit
+
+EOF

+ 46 - 0
turndb/testmongosetup.sh

@@ -0,0 +1,46 @@
+#!/bin/sh
+
+mongo $* <<EOF
+
+use coturn;
+
+db.turnusers_lt.insert({ realm: 'north.gov', name: 'ninefingers', hmackey: 'bc807ee29df3c9ffa736523fb2c4e8ee' });
+db.turnusers_lt.insert({ realm: 'north.gov', name: 'gorst', hmackey: '7da2270ccfa49786e0115366d3a3d14d' });
+db.turnusers_lt.insert({ realm: 'crinna.org', name: 'whirrun', hmackey: '6972e85e51f36e53b0b61759c5a5219a' });
+db.turnusers_lt.insert({ realm: 'crinna.org', name: 'stranger-come-knocking', hmackey: 'd43cb678560259a1839bff61c19de15e' });
+
+db.turnusers_st.insert({ name: 'ninefingers', password: 'youhavetoberealistic'});
+db.turnusers_st.insert({ name: 'gorst', password: 'hero'});
+db.turnusers_st.insert({ name: 'whirrun', password: 'sword'});
+db.turnusers_st.insert({ name: 'stranger-come-knocking', password: 'civilization'});
+
+db.turn_secret.insert({ realm: 'north.gov', value: 'logen' });
+db.turn_secret.insert({ realm: 'crinna.org', value: 'north' });
+
+db.realm.insert({
+  realm: 'north.gov',
+  options: {
+    "max-bps" : 500000,
+    "user-quota" : 10000,
+    "total-quota" : 12000 
+  }
+});
+
+db.realm.insert({
+  realm: 'crinna.org',
+  origin: [ 'http://crinna.org:80', 'https://bligh.edu:443' ],
+  options: {
+    "max-bps" : 400000,
+    "user-quota" : 8000,
+    "total-quota" : 10000 
+  }
+});
+
+db.allowed_peer_ip.insert({ ip_range: '172.17.13.200' });
+
+db.denied_peer_ip.insert({ ip_range: '172.17.13.133-172.17.14.56' });
+db.denied_peer_ip.insert({ ip_range: '123::45' });
+
+exit
+
+EOF

Some files were not shown because too many files changed in this diff