Kaynağa Gözat

MongoDB support: initial checkin

mom040267 11 yıl önce
ebeveyn
işleme
5c3b773d9c

+ 47 - 20
INSTALL

@@ -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,28 @@ 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/. 
+
+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 +988,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 

+ 5 - 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

+ 2 - 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.

+ 19 - 6
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 
@@ -461,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.
 
@@ -533,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.
 
@@ -717,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:

+ 56 - 0
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 ?
 ##########################
@@ -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_MYSQL="-DTURN_NO_MONGO"
+	fi
+else
+	TURN_NO_MYSQL="-DTURN_NO_MONGO"
+fi
+
 ###########################
 # Test Redis
 ###########################

+ 11 - 3
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

+ 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

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

@@ -0,0 +1,916 @@
+/*
+ * 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_mydb_connection(const char *realm) {
+	UNUSED_ARG(realm);
+  
+	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;
+    	}
+    }
+  }
+	return mydbconnection;
+}
+
+static mongoc_collection_t * mongo_get_collection(const char * name) {
+	MONGO * mc = get_mydb_connection(NULL);
+
+	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);
+
+  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, "name") && 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_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, 0, &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);
+              printf("key %s, type %d\n", _k, bson_iter_type(&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, "value") && 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));
+
+        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);
+								ur_string_map_value_type value = strdup(_origin);
+								ur_string_map_put(o_to_realm_new, (const ur_string_map_key_type)_realm, value);
+              }
+            }
+          }
+        }
+        
+  			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;
+              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__///
+

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

@@ -0,0 +1,870 @@
+/*
+ * 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 from turnusers_lt where realm='%s' order by name",realm);
+		} else {
+		  snprintf(statement,sizeof(statement),"select name 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)!=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_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 = 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(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__///
+

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

@@ -0,0 +1,668 @@
+/*
+ * 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 from turnusers_lt where realm='%s' order by name",realm);
+		} else {
+		  snprintf(statement,sizeof(statement),"select name 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) {
+					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__///
+

+ 40 - 8
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,
@@ -701,6 +709,9 @@ static const struct myoption 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' },
@@ -776,7 +787,7 @@ static const struct myoption admin_long_options[] = {
 				{ "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' },
@@ -792,6 +803,9 @@ static const struct myoption 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
@@ -799,7 +813,7 @@ static const struct myoption 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' },
@@ -1086,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);
@@ -1411,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);
@@ -1444,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);
@@ -1558,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

+ 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

+ 5 - 0
src/apps/relay/turncli.c

@@ -764,6 +764,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);

Dosya farkı çok büyük olduğundan ihmal edildi
+ 31 - 958
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 //////////////////////
 

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor