From 5c3b773d9c0038abda6fb968b481d6a6e75c113f Mon Sep 17 00:00:00 2001 From: mom040267 Date: Fri, 11 Jul 2014 07:06:05 +0000 Subject: [PATCH] MongoDB support: initial checkin --- INSTALL | 67 +- Makefile.in | 7 +- README.turnadmin | 2 + README.turnserver | 25 +- configure | 56 + examples/etc/turnserver.conf | 14 +- postinstall.txt | 2 +- src/apps/relay/dbdrivers/dbd_mongo.c | 916 ++++++++++ src/apps/relay/dbdrivers/dbd_mongo.h | 49 + src/apps/relay/dbdrivers/dbd_mysql.c | 870 +++++++++ src/apps/relay/dbdrivers/dbd_mysql.h | 49 + src/apps/relay/dbdrivers/dbd_pgsql.c | 668 +++++++ src/apps/relay/dbdrivers/dbd_pgsql.h | 49 + src/apps/relay/dbdrivers/dbd_redis.c | 1150 ++++++++++++ src/apps/relay/dbdrivers/dbd_redis.h | 49 + src/apps/relay/dbdrivers/dbdriver.c | 90 + src/apps/relay/dbdrivers/dbdriver.h | 78 + src/apps/relay/mainrelay.c | 48 +- src/apps/relay/mainrelay.h | 2 - src/apps/relay/turncli.c | 5 + src/apps/relay/userdb.c | 2529 +------------------------- src/apps/relay/userdb.h | 9 +- 22 files changed, 4262 insertions(+), 2472 deletions(-) create mode 100644 src/apps/relay/dbdrivers/dbd_mongo.c create mode 100644 src/apps/relay/dbdrivers/dbd_mongo.h create mode 100644 src/apps/relay/dbdrivers/dbd_mysql.c create mode 100644 src/apps/relay/dbdrivers/dbd_mysql.h create mode 100644 src/apps/relay/dbdrivers/dbd_pgsql.c create mode 100644 src/apps/relay/dbdrivers/dbd_pgsql.h create mode 100644 src/apps/relay/dbdrivers/dbd_redis.c create mode 100644 src/apps/relay/dbdrivers/dbd_redis.h create mode 100644 src/apps/relay/dbdrivers/dbdriver.c create mode 100644 src/apps/relay/dbdrivers/dbdriver.h diff --git a/INSTALL b/INSTALL index ff60b585..21d41c0a 100644 --- a/INSTALL +++ b/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 " (Linux), "ldconfig -m " (BSD) or "crle -u -l " (Solaris). Your system must be able to find the libevent2, openssl and -(optionally) PostgreSQL and/or MySQL (MariaDB) and/or Redis shared libraries, -either with the help of the system-wide library search configuration or by -using LD_LIBRARY_PATH. "make install" will make a non-garantied effort to add -automatically PREFIX/lib and /usr/local/lib to the libraries search path, -but if you have some libraries in different non-default directories -you will have to add them manually to the search path, or you -will have to adjust LD_LIBRARY_PATH. +(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 diff --git a/Makefile.in b/Makefile.in index cea66b34..171164d3 100755 --- a/Makefile.in +++ b/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 diff --git a/README.turnadmin b/README.turnadmin index fb5f176d..ab0233f3 100644 --- a/README.turnadmin +++ b/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. diff --git a/README.turnserver b/README.turnserver index 888a7e0b..555a72f0 100644 --- a/README.turnserver +++ b/README.turnserver @@ -57,7 +57,7 @@ turnserver - a TURN relay server implementation. SYNOPSIS -$ turnserver [-n | -c ] [flags] [ --userdb= | --psql-userdb= | --mysql-userdb= | --redis-userdb= ] [-z | --no-auth | -a | --lt-cred-mech ] [options] +$ turnserver [-n | -c ] [flags] [ --userdb= | --psql-userdb= | --mysql-userdb= | --mongo-userdb= | --redis-userdb= ] [-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: diff --git a/configure b/configure index 2e156038..0ad77694 100755 --- a/configure +++ b/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} < +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 ########################### diff --git a/examples/etc/turnserver.conf b/examples/etc/turnserver.conf index f5dc5f8a..b389b79b 100644 --- a/examples/etc/turnserver.conf +++ b/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= dbname= user= password= port= connect_timeout=" +# 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 diff --git a/postinstall.txt b/postinstall.txt index c05efceb..b6546c1a 100644 --- a/postinstall.txt +++ b/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 diff --git a/src/apps/relay/dbdrivers/dbd_mongo.c b/src/apps/relay/dbdrivers/dbd_mongo.c new file mode 100644 index 00000000..75dbec42 --- /dev/null +++ b/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 +#include + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// + +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 diff --git a/src/apps/relay/dbdrivers/dbd_mongo.h b/src/apps/relay/dbdrivers/dbd_mongo.h new file mode 100644 index 00000000..66ed0a07 --- /dev/null +++ b/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__/// + diff --git a/src/apps/relay/dbdrivers/dbd_mysql.c b/src/apps/relay/dbdrivers/dbd_mysql.c new file mode 100644 index 00000000..6027ffd7 --- /dev/null +++ b/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 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// + +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]> %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; isecrets[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 diff --git a/src/apps/relay/dbdrivers/dbd_mysql.h b/src/apps/relay/dbdrivers/dbd_mysql.h new file mode 100644 index 00000000..91b15906 --- /dev/null +++ b/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__/// + diff --git a/src/apps/relay/dbdrivers/dbd_pgsql.c b/src/apps/relay/dbdrivers/dbd_pgsql.c new file mode 100644 index 00000000..d688e1c7 --- /dev/null +++ b/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 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// + +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> %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;isz; + unlock_realms(); + + for (i = 0; isecrets[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;ioptions.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 diff --git a/src/apps/relay/dbdrivers/dbd_pgsql.h b/src/apps/relay/dbdrivers/dbd_pgsql.h new file mode 100644 index 00000000..7c36e66e --- /dev/null +++ b/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__/// + diff --git a/src/apps/relay/dbdrivers/dbd_redis.c b/src/apps/relay/dbdrivers/dbd_redis.c new file mode 100644 index 00000000..4a8e900a --- /dev/null +++ b/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 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// + +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;iszhost, 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;isztype == REDIS_REPLY_ERROR) + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); + else if (rget->type != REDIS_REPLY_STRING) { + if (rget->type != REDIS_REPLY_NIL) + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); + } else { + add_to_secrets_list(sl,rget->str); + } + turnFreeRedisReply(rget); + } + } + + clean_secrets_list(&keys); + + ret = 0; + + turnFreeRedisReply(reply); + } + } + 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)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;isztype == REDIS_REPLY_ERROR) + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); + else if (reply->type != REDIS_REPLY_ARRAY) { + if (reply->type != REDIS_REPLY_NIL) + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); + } else { + size_t i; + for (i = 0; i < reply->elements; ++i) { + add_to_secrets_list(&keys,reply->element[i]->str); + } + } + + for(isz=0;isztype == REDIS_REPLY_ERROR) + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); + else if (rget->type != REDIS_REPLY_STRING) { + if (rget->type != REDIS_REPLY_NIL) + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); + } else { + printf("%s\n",rget->str); + } + } + turnFreeRedisReply(rget); + } + + clean_secrets_list(&keys); + + turnFreeRedisReply(reply); + 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;isztype == REDIS_REPLY_ERROR) + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); + else if (rget->type != REDIS_REPLY_STRING) { + if (rget->type != REDIS_REPLY_NIL) + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); + } else { + if(!strcmp((char*)secret,rget->str)) { + snprintf(s,sizeof(s),"del %s", keys.secrets[isz]); + turnFreeRedisReply(redisCommand(rc, s)); + } + } + turnFreeRedisReply(rget); + } + } + } + + turnFreeRedisReply(redisCommand(rc, "save")); + + clean_secrets_list(&keys); + + turnFreeRedisReply(reply); + 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;isztype == 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;isztype == 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;isztype == REDIS_REPLY_ERROR) + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); + else if (rget->type != REDIS_REPLY_STRING) { + if (rget->type != REDIS_REPLY_NIL) + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); + } else { + add_ip_list_range(rget->str,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; isecrets[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 diff --git a/src/apps/relay/dbdrivers/dbd_redis.h b/src/apps/relay/dbdrivers/dbd_redis.h new file mode 100644 index 00000000..6ba27b9f --- /dev/null +++ b/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__/// + diff --git a/src/apps/relay/dbdrivers/dbdriver.c b/src/apps/relay/dbdrivers/dbdriver.c new file mode 100644 index 00000000..807ad715 --- /dev/null +++ b/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 dbname= user= \\\n password= port= connect_timeout=\".\n" " All parameters are optional.\n" #endif +#if !defined(TURN_NO_MONGO) +" -J, --mongo-userdb 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 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= Add shared secret for TURN RESP API\n" " -S, --show-secret Show stored shared secrets for TURN REST API\n" " -X, --delete-secret= Delete a shared secret\n" @@ -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 diff --git a/src/apps/relay/mainrelay.h b/src/apps/relay/mainrelay.h index e0ffb118..22b16902 100644 --- a/src/apps/relay/mainrelay.h +++ b/src/apps/relay/mainrelay.h @@ -83,8 +83,6 @@ #include "ns_ioalib_impl.h" -#include "hiredis_libevent2.h" - #ifdef __cplusplus extern "C" { #endif diff --git a/src/apps/relay/turncli.c b/src/apps/relay/turncli.c index e6dd2e9e..b25ce026 100644 --- a/src/apps/relay/turncli.c +++ b/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); diff --git a/src/apps/relay/userdb.c b/src/apps/relay/userdb.c index 953acd9b..59f75df6 100644 --- a/src/apps/relay/userdb.c +++ b/src/apps/relay/userdb.c @@ -39,19 +39,6 @@ #include #include -#if !defined(TURN_NO_PQ) -#include -#endif - -#if !defined(TURN_NO_MYSQL) -#include -#endif - -#if !defined(TURN_NO_HIREDIS) -#include "hiredis_libevent2.h" -#include -#endif - #include #include @@ -65,6 +52,7 @@ #include #include "userdb.h" +#include "dbdrivers/dbdriver.h" #include "mainrelay.h" #include "ns_turn_utils.h" @@ -74,12 +62,6 @@ #include "apputils.h" -//////////// USER DB ////////////////////////////// - -#define LONG_STRING_SIZE (TURN_LONG_STRING_SIZE) - -static int donot_print_connection_success=0; - //////////// REALM ////////////// static realm_params_t *default_realm_params_ptr = NULL; @@ -98,6 +80,21 @@ static turn_mutex o_to_realm_mutex; static ur_string_map *o_to_realm = NULL; static secrets_list_t realms_list; +void lock_realms(void) { + ur_string_map_lock(realms); +} + +void unlock_realms(void) { + ur_string_map_unlock(realms); +} + +void update_o_to_realm(ur_string_map * o_to_realm_new) { + TURN_MUTEX_LOCK(&o_to_realm_mutex); + ur_string_map_free(&o_to_realm); + o_to_realm = o_to_realm_new; + TURN_MUTEX_UNLOCK(&o_to_realm_mutex); +} + void create_new_realm(char* name) { realm_params_t *ret = NULL; @@ -319,626 +316,12 @@ void add_to_secrets_list(secrets_list_t *sl, const char* elem) } } -/////////// USER DB CHECK ////////////////// - -static persistent_users_db_t* get_persistent_users_db(void) -{ - return &(turn_params.default_users_db.persistent_users_db); -} - -static int convert_string_key_to_binary(char* keysource, hmackey_t key, size_t sz) { - { - char is[3]; - size_t i; - unsigned int v; - is[2]=0; - for(i=0;iconnection); - 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; -} - -#endif - -#if !defined(TURN_NO_MYSQL) - -struct _Myconninfo { - char *host; - char *dbname; - char *user; - char *password; - unsigned int port; - unsigned int connect_timeout; -}; - -typedef struct _Myconninfo Myconninfo; - -static void MyconninfoFree(Myconninfo *co) { - if(co) { - if(co->host) turn_free(co->host,strlen(co->host)+1); - if(co->dbname) turn_free(co->dbname, strlen(co->dbname)+1); - if(co->user) turn_free(co->user, strlen(co->user)+1); - if(co->password) turn_free(co->password, strlen(co->password)+1); - ns_bzero(co,sizeof(Myconninfo)); - } -} - -static Myconninfo *MyconninfoParse(char *userdb, char **errmsg) -{ - Myconninfo *co = (Myconninfo*)turn_malloc(sizeof(Myconninfo)); - ns_bzero(co,sizeof(Myconninfo)); - if(userdb) { - char *s0=strdup(userdb); - char *s = s0; - - while(s && *s) { - - while(*s && (*s==' ')) ++s; - char *snext = strstr(s," "); - if(snext) { - *snext = 0; - ++snext; - } - - char* seq = strstr(s,"="); - if(!seq) { - MyconninfoFree(co); - co = NULL; - if(errmsg) { - *errmsg = strdup(s); - } - break; - } - - *seq = 0; - if(!strcmp(s,"host")) - co->host = strdup(seq+1); - else if(!strcmp(s,"ip")) - co->host = strdup(seq+1); - else if(!strcmp(s,"addr")) - co->host = strdup(seq+1); - else if(!strcmp(s,"ipaddr")) - co->host = strdup(seq+1); - else if(!strcmp(s,"hostaddr")) - co->host = strdup(seq+1); - else if(!strcmp(s,"dbname")) - co->dbname = strdup(seq+1); - else if(!strcmp(s,"db")) - co->dbname = strdup(seq+1); - else if(!strcmp(s,"database")) - co->dbname = strdup(seq+1); - else if(!strcmp(s,"user")) - co->user = strdup(seq+1); - else if(!strcmp(s,"uname")) - co->user = strdup(seq+1); - else if(!strcmp(s,"name")) - co->user = strdup(seq+1); - else if(!strcmp(s,"username")) - co->user = strdup(seq+1); - else if(!strcmp(s,"password")) - co->password = strdup(seq+1); - else if(!strcmp(s,"pwd")) - co->password = strdup(seq+1); - else if(!strcmp(s,"passwd")) - co->password = strdup(seq+1); - else if(!strcmp(s,"secret")) - co->password = strdup(seq+1); - else if(!strcmp(s,"port")) - co->port = (unsigned int)atoi(seq+1); - else if(!strcmp(s,"p")) - co->port = (unsigned int)atoi(seq+1); - else if(!strcmp(s,"connect_timeout")) - co->connect_timeout = (unsigned int)atoi(seq+1); - else if(!strcmp(s,"timeout")) - co->connect_timeout = (unsigned int)atoi(seq+1); - else { - MyconninfoFree(co); - co = NULL; - if(errmsg) { - *errmsg = strdup(s); - } - break; - } - - s = snext; - } - - turn_free(s0, strlen(s0)+1); - } - - if(!(co->dbname)) - co->dbname=strdup("0"); - if(!(co->host)) - co->host=strdup("127.0.0.1"); - if(!(co->user)) - co->user=strdup(""); - if(!(co->password)) - co->password=strdup(""); - - return co; -} - -static MYSQL *get_mydb_connection(void) -{ - - if(!is_mysql_userdb()) - return NULL; - - 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; -} - -#endif - - -#if !defined(TURN_NO_HIREDIS) - -static void turnFreeRedisReply(void *reply) -{ - if(reply) { - freeReplyObject(reply); - } -} - -struct _Ryconninfo { - char *host; - char *dbname; - char *password; - unsigned int connect_timeout; - unsigned int port; -}; - -typedef struct _Ryconninfo Ryconninfo; - -static void RyconninfoFree(Ryconninfo *co) { - if(co) { - if(co->host) turn_free(co->host, strlen(co->host)+1); - if(co->dbname) turn_free(co->dbname, strlen(co->username)+1); - if(co->password) turn_free(co->password, strlen(co->password)+1); - ns_bzero(co,sizeof(Ryconninfo)); - } -} - -static Ryconninfo *RyconninfoParse(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;iszhost, 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) -{ - if(!is_redis_userdb()) - return NULL; - - 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; -} - -#endif +//////////////////////////////////////////// static int get_auth_secrets(secrets_list_t *sl, u08bits *realm) { - UNUSED_ARG(realm); - int ret = -1; + turn_dbdriver_t * dbd = get_dbdriver(); clean_secrets_list(sl); @@ -949,120 +332,10 @@ static int get_auth_secrets(secrets_list_t *sl, u08bits *realm) } ret=0; } - -#if !defined(TURN_NO_PQ) - PGconn * pqc = get_pqdb_connection(); - if(pqc) { - char statement[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;itype == REDIS_REPLY_ERROR) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); - else if (reply->type != REDIS_REPLY_ARRAY) { - if (reply->type != REDIS_REPLY_NIL) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); - } else { - size_t i; - for (i = 0; i < reply->elements; ++i) { - add_to_secrets_list(&keys,reply->element[i]->str); - } - } - - for(isz=0;isztype == REDIS_REPLY_ERROR) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); - else if (rget->type != REDIS_REPLY_STRING) { - if (rget->type != REDIS_REPLY_NIL) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); - } else { - add_to_secrets_list(sl,rget->str); - } - turnFreeRedisReply(rget); - } - } - - clean_secrets_list(&keys); - - ret = 0; - - turnFreeRedisReply(reply); - } - } -#endif + + if (dbd && dbd->get_auth_secrets) { + ret = (*dbd->get_auth_secrets)(sl, realm); + } return ret; } @@ -1252,129 +525,10 @@ int get_user_key(u08bits *usname, u08bits *realm, hmackey_t key, ioa_network_buf return 0; } -#if !defined(TURN_NO_PQ) - { - PGconn * pqc = get_pqdb_connection(); - if(pqc) { - char statement[LONG_STRING_SIZE]; - snprintf(statement,sizeof(statement),"select hmackey from turnusers_lt where name='%s' 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)lentype == REDIS_REPLY_ERROR) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); - else if (rget->type != REDIS_REPLY_STRING) { - if (rget->type != REDIS_REPLY_NIL) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); - } else { - size_t sz = get_hmackey_size(turn_params.shatype); - if(strlen(rget->str)str,usname); - } else if(convert_string_key_to_binary(rget->str, key, sz)<0) { - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key: %s, user %s\n",rget->str,usname); - } else { - ret = 0; - } - } - turnFreeRedisReply(rget); - } - if(ret != 0) { - snprintf(s,sizeof(s),"get turn/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); - } - } - } - } -#endif + turn_dbdriver_t * dbd = get_dbdriver(); + if (dbd && dbd->get_user_key) { + ret = (*dbd->get_user_key)(usname, realm, key); + } return ret; } @@ -1386,93 +540,12 @@ int get_user_pwd(u08bits *usname, st_password_t pwd) { int ret = -1; - UNUSED_ARG(pwd); + turn_dbdriver_t * dbd = get_dbdriver(); + if (dbd && dbd->get_user_pwd) { + ret = (*dbd->get_user_pwd)(usname, pwd); + } - char statement[LONG_STRING_SIZE]; - snprintf(statement,sizeof(statement),"select password from turnusers_st where name='%s'",usname); - - { -#if !defined(TURN_NO_PQ) - PGconn * pqc = get_pqdb_connection(); - if(pqc) { - PGresult *res = PQexec(pqc, statement); - - if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res)!=1)) { - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc)); - } else { - char *kval = PQgetvalue(res,0,0); - if(kval) { - strncpy((char*)pwd,kval,sizeof(st_password_t)); - ret = 0; - } else { - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong password data for user %s: NULL\n",usname); - } - } - - if(res) { - PQclear(res); - } - } -#endif -#if !defined(TURN_NO_MYSQL) - MYSQL * myc = get_mydb_connection(); - if(myc) { - int res = mysql_query(myc, statement); - if(res) { - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); - } else { - MYSQL_RES *mres = mysql_store_result(myc); - if(!mres) { - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); - } else if(mysql_field_count(myc)!=1) { - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); - } else { - MYSQL_ROW row = mysql_fetch_row(mres); - if(row && row[0]) { - unsigned long *lengths = mysql_fetch_lengths(mres); - if(lengths) { - if(lengths[0]<1) { - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong password data for user %s, size in MySQL DB is zero(0)\n",usname); - } else { - ns_bcopy(row[0],pwd,lengths[0]); - pwd[lengths[0]]=0; - ret = 0; - } - } - } - } - - if(mres) - mysql_free_result(mres); - } - } -#endif -#if !defined(TURN_NO_HIREDIS) - { - redisContext * rc = get_redis_connection(); - if(rc) { - char s[LONG_STRING_SIZE]; - snprintf(s,sizeof(s),"get turn/user/%s/password", usname); - redisReply *rget = (redisReply *)redisCommand(rc, s); - if(rget) { - if (rget->type == REDIS_REPLY_ERROR) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); - else if (rget->type != REDIS_REPLY_STRING) { - if (rget->type != REDIS_REPLY_NIL) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); - } else { - strncpy((char*)pwd,rget->str,SHORT_TERM_PASSWORD_SIZE); - pwd[SHORT_TERM_PASSWORD_SIZE]=0; - ret = 0; - } - turnFreeRedisReply(rget); - } - } - } -#endif - } - - return ret; + return ret; } u08bits *start_user_check(turnserver_id id, turn_credential_type ct, u08bits *usname, u08bits *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, u64bits ctxkey, int *postpone_reply) @@ -1585,7 +658,7 @@ void read_userdb_file(int to_print) if (f) { - char sbuf[LONG_STRING_SIZE]; + char sbuf[TURN_LONG_STRING_SIZE]; ur_string_map_lock(turn_params.default_users_db.ram_db.dynamic_accounts); @@ -1682,170 +755,11 @@ int add_user_account(char *user, int dynamic) static int list_users(int is_st, u08bits *realm) { - UNUSED_ARG(realm); - - donot_print_connection_success = 1; - - if(is_pqsql_userdb()){ -#if !defined(TURN_NO_PQ) - char statement[LONG_STRING_SIZE]; - PGconn *pqc = get_pqdb_connection(); - if(pqc) { - if(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;itype == REDIS_REPLY_ERROR) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); - else if (reply->type != REDIS_REPLY_ARRAY) { - if (reply->type != REDIS_REPLY_NIL) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); - } else { - size_t i; - for (i = 0; i < reply->elements; ++i) { - add_to_secrets_list(&keys,reply->element[i]->str); - } - } - 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;iszlist_users) { + (*dbd->list_users)(is_st, realm); } else if(!is_st) { - read_userdb_file(1); - } return 0; @@ -1853,116 +767,11 @@ static int list_users(int is_st, u08bits *realm) static int show_secret(u08bits *realm) { - char statement[LONG_STRING_SIZE]; - snprintf(statement,sizeof(statement)-1,"select value from turn_secret where realm='%s'",realm); - - donot_print_connection_success=1; - must_set_admin_realm(realm); - if(is_pqsql_userdb()){ -#if !defined(TURN_NO_PQ) - 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;itype == REDIS_REPLY_ERROR) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); - else if (reply->type != REDIS_REPLY_ARRAY) { - if (reply->type != REDIS_REPLY_NIL) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); - } else { - size_t i; - for (i = 0; i < reply->elements; ++i) { - add_to_secrets_list(&keys,reply->element[i]->str); - } - } - - for(isz=0;isztype == REDIS_REPLY_ERROR) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); - else if (rget->type != REDIS_REPLY_STRING) { - if (rget->type != REDIS_REPLY_NIL) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); - } else { - printf("%s\n",rget->str); - } - } - turnFreeRedisReply(rget); - } - - clean_secrets_list(&keys); - - turnFreeRedisReply(reply); - } - } -#endif + turn_dbdriver_t * dbd = get_dbdriver(); + if (dbd && dbd->show_secret) { + (*dbd->show_secret)(realm); } return 0; @@ -1970,99 +779,14 @@ static int show_secret(u08bits *realm) static int del_secret(u08bits *secret, u08bits *realm) { - UNUSED_ARG(secret); - must_set_admin_realm(realm); - donot_print_connection_success=1; - - if (is_pqsql_userdb()) { -#if !defined(TURN_NO_PQ) - char statement[LONG_STRING_SIZE]; - PGconn *pqc = get_pqdb_connection(); - if (pqc) { - if(!secret || (secret[0]==0)) - snprintf(statement,sizeof(statement),"delete from turn_secret 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); - } - } -#endif - } else if (is_mysql_userdb()) { -#if !defined(TURN_NO_MYSQL) - char statement[LONG_STRING_SIZE]; - MYSQL * myc = get_mydb_connection(); - if (myc) { - if(!secret || (secret[0]==0)) - snprintf(statement,sizeof(statement),"delete from turn_secret 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); - } -#endif - } else if(is_redis_userdb()) { -#if !defined(TURN_NO_HIREDIS) - redisContext *rc = get_redis_connection(); - if(rc) { - redisReply *reply = (redisReply*)redisCommand(rc, "keys turn/realm/%s/secret/*", (char*)realm); - if(reply) { - secrets_list_t keys; - size_t isz = 0; - char s[LONG_STRING_SIZE]; - - init_secrets_list(&keys); - - if (reply->type == REDIS_REPLY_ERROR) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); - else if (reply->type != REDIS_REPLY_ARRAY) { - if (reply->type != REDIS_REPLY_NIL) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); - } else { - size_t i; - for (i = 0; i < reply->elements; ++i) { - add_to_secrets_list(&keys,reply->element[i]->str); - } - } - - for(isz=0;isztype == REDIS_REPLY_ERROR) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); - else if (rget->type != REDIS_REPLY_STRING) { - if (rget->type != REDIS_REPLY_NIL) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); - } else { - if(!strcmp((char*)secret,rget->str)) { - snprintf(s,sizeof(s),"del %s", keys.secrets[isz]); - turnFreeRedisReply(redisCommand(rc, s)); - } - } - turnFreeRedisReply(rget); - } - } - } - - turnFreeRedisReply(redisCommand(rc, "save")); - - clean_secrets_list(&keys); - - turnFreeRedisReply(reply); - } - } -#endif + turn_dbdriver_t * dbd = get_dbdriver(); + if (dbd && dbd->del_secret) { + (*dbd->del_secret)(secret, realm); } - return 0; + return 0; } static int set_secret(u08bits *secret, u08bits *realm) { @@ -2072,57 +796,11 @@ static int set_secret(u08bits *secret, u08bits *realm) { must_set_admin_realm(realm); - donot_print_connection_success = 1; - del_secret(secret, realm); - if (is_pqsql_userdb()) { -#if !defined(TURN_NO_PQ) - char statement[LONG_STRING_SIZE]; - PGconn *pqc = get_pqdb_connection(); - if (pqc) { - snprintf(statement,sizeof(statement),"insert into turn_secret (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)); - } - if (res) { - PQclear(res); - } - } -#endif - } else if (is_mysql_userdb()) { -#if !defined(TURN_NO_MYSQL) - char statement[LONG_STRING_SIZE]; - MYSQL * myc = get_mydb_connection(); - if (myc) { - snprintf(statement,sizeof(statement),"insert into turn_secret (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)); - } - } -#endif - } else if(is_redis_userdb()) { -#if !defined(TURN_NO_HIREDIS) - redisContext *rc = get_redis_connection(); - if(rc) { - char s[LONG_STRING_SIZE]; - - del_secret(secret, 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")); - } -#endif + turn_dbdriver_t * dbd = get_dbdriver(); + if (dbd && dbd->set_secret) { + (*dbd->set_secret)(secret, realm); } return 0; @@ -2130,242 +808,37 @@ static int set_secret(u08bits *secret, u08bits *realm) { static int add_origin(u08bits *origin0, u08bits *realm) { - UNUSED_ARG(realm); - UNUSED_ARG(origin0); - char origin[STUN_MAX_ORIGIN_SIZE+1]; - get_canonic_origin((char*)origin0, origin, sizeof(origin)-1); -#if !defined(TURN_NO_PQ) - if(is_pqsql_userdb()) { - char statement[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)); - } - if(res) { - PQclear(res); - } - } + u08bits origin[STUN_MAX_ORIGIN_SIZE+1]; + + get_canonic_origin((const char *)origin0, (char *)origin, sizeof(origin)-1); + + turn_dbdriver_t * dbd = get_dbdriver(); + if (dbd && dbd->add_origin) { + (*dbd->add_origin)(origin, realm); } -#endif -#if !defined(TURN_NO_MYSQL) - if (is_mysql_userdb()) { - char statement[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)); - } - } - } -#endif - -#if !defined(TURN_NO_HIREDIS) - if(is_redis_userdb()) { - redisContext *rc = get_redis_connection(); - if(rc) { - char s[LONG_STRING_SIZE]; - - snprintf(s,sizeof(s),"set turn/origin/%s %s", (char*)origin, (char*)realm); - - turnFreeRedisReply(redisCommand(rc, s)); - turnFreeRedisReply(redisCommand(rc, "save")); - } - } -#endif return 0; } static int del_origin(u08bits *origin0) { - UNUSED_ARG(origin0); - char origin[STUN_MAX_ORIGIN_SIZE+1]; - get_canonic_origin((char*)origin0, origin, sizeof(origin)-1); -#if !defined(TURN_NO_PQ) - if(is_pqsql_userdb()) { - char statement[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)); - } - if(res) { - PQclear(res); - } - } + u08bits origin[STUN_MAX_ORIGIN_SIZE+1]; + + get_canonic_origin((const char *)origin0, (char *)origin, sizeof(origin)-1); + + turn_dbdriver_t * dbd = get_dbdriver(); + if (dbd && dbd->del_origin) { + (*dbd->del_origin)(origin); } -#endif -#if !defined(TURN_NO_MYSQL) - if (is_mysql_userdb()) { - char statement[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)); - } - } - } -#endif - -#if !defined(TURN_NO_HIREDIS) - if(is_redis_userdb()) { - redisContext *rc = get_redis_connection(); - if(rc) { - char s[LONG_STRING_SIZE]; - - snprintf(s,sizeof(s),"del turn/origin/%s", (char*)origin); - - turnFreeRedisReply(redisCommand(rc, s)); - turnFreeRedisReply(redisCommand(rc, "save")); - } - } -#endif return 0; } static int list_origins(u08bits *realm) { - UNUSED_ARG(realm); - - donot_print_connection_success = 1; - - if(is_pqsql_userdb()){ -#if !defined(TURN_NO_PQ) - char statement[LONG_STRING_SIZE]; - PGconn *pqc = get_pqdb_connection(); - if(pqc) { - if(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> %s\n",oval,rval); - } - } - } - } - if(res) { - PQclear(res); - } - } -#endif - } else if(is_mysql_userdb()){ -#if !defined(TURN_NO_MYSQL) - char statement[LONG_STRING_SIZE]; - MYSQL * myc = get_mydb_connection(); - if(myc) { - if(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]); - } - } - } - } - - if(mres) - mysql_free_result(mres); - } - } -#endif - } else if(is_redis_userdb()) { -#if !defined(TURN_NO_HIREDIS) - 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;isztype == 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); - } -#endif + turn_dbdriver_t * dbd = get_dbdriver(); + if (dbd && dbd->list_origins) { + (*dbd->list_origins)(realm); } return 0; @@ -2373,76 +846,14 @@ static int list_origins(u08bits *realm) static int set_realm_option_one(u08bits *realm, unsigned long value, const char* opt) { - UNUSED_ARG(realm); - UNUSED_ARG(value); - UNUSED_ARG(opt); if(value == (unsigned long)-1) return 0; -#if !defined(TURN_NO_PQ) - if(is_pqsql_userdb()) { - char statement[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)); - } - if(res) { - PQclear(res); - } - } - } + + turn_dbdriver_t * dbd = get_dbdriver(); + if (dbd && dbd->set_realm_option_one) { + (*dbd->set_realm_option_one)(realm, value, opt); } -#endif -#if !defined(TURN_NO_MYSQL) - if (is_mysql_userdb()) { - char statement[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)); - } - } - } - } -#endif - -#if !defined(TURN_NO_HIREDIS) - if(is_redis_userdb()) { - redisContext *rc = get_redis_connection(); - if(rc) { - char s[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")); - } - } -#endif return 0; } @@ -2456,141 +867,9 @@ static int set_realm_option(u08bits *realm, perf_options_t *po) static int list_realm_options(u08bits *realm) { - UNUSED_ARG(realm); - - donot_print_connection_success = 1; - - if(is_pqsql_userdb()){ -#if !defined(TURN_NO_PQ) - char statement[LONG_STRING_SIZE]; - PGconn *pqc = get_pqdb_connection(); - if(pqc) { - if(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;itype == REDIS_REPLY_ERROR) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); - else if (reply->type != REDIS_REPLY_ARRAY) { - if (reply->type != REDIS_REPLY_NIL) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); - } else { - size_t i; - for (i = 0; i < reply->elements; ++i) { - 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;isztype == 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); - } -#endif + turn_dbdriver_t * dbd = get_dbdriver(); + if (dbd && dbd->list_realm_options) { + (*dbd->list_realm_options)(realm); } return 0; @@ -2598,13 +877,10 @@ static int list_realm_options(u08bits *realm) int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08bits *origin, TURNADMIN_COMMAND_TYPE ct, int is_st, - perf_options_t *po) -{ + perf_options_t *po) { hmackey_t key; char skey[sizeof(hmackey_t)*2+1]; - donot_print_connection_success = 1; - st_password_t passwd; if(ct == TA_LIST_USERS) { @@ -2674,152 +950,40 @@ int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08b } } + turn_dbdriver_t * dbd = get_dbdriver(); + if(ct == TA_PRINT_KEY) { if(!is_st) { printf("0x%s\n",skey); } - } else if(is_pqsql_userdb()){ - -#if !defined(TURN_NO_PQ) + } else if(dbd) { if(!is_st) { must_set_admin_realm(realm); } - - char statement[LONG_STRING_SIZE]; - PGconn *pqc = get_pqdb_connection(); - if(pqc) { - if(ct == TA_DELETE_USER) { - if(is_st) { - snprintf(statement,sizeof(statement),"delete from turnusers_st where name='%s'",user); - } else { - snprintf(statement,sizeof(statement),"delete from turnusers_lt where name='%s' and realm='%s'",user,realm); - } - PGresult *res = PQexec(pqc, statement); - if(res) { - PQclear(res); - } - } - - if(ct == TA_UPDATE_USER) { - if(is_st) { - snprintf(statement,sizeof(statement),"insert into turnusers_st values('%s','%s')",user,passwd); - } else { - snprintf(statement,sizeof(statement),"insert into turnusers_lt (realm,name,hmackey) values('%s','%s','%s')",realm,user,skey); - } - PGresult *res = PQexec(pqc, statement); - if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { - if(res) { - PQclear(res); - } - if(is_st) { - snprintf(statement,sizeof(statement),"update turnusers_st set password='%s' where name='%s'",passwd,user); - } else { - snprintf(statement,sizeof(statement),"update turnusers_lt set hmackey='%s' where name='%s' and realm='%s'",skey,user,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)); - } - } - if(res) { - PQclear(res); - } - } - } -#endif - } else if(is_mysql_userdb()){ - -#if !defined(TURN_NO_MYSQL) - - if(!is_st) { - must_set_admin_realm(realm); - } - - char statement[LONG_STRING_SIZE]; - MYSQL * myc = get_mydb_connection(); - if(myc) { - if(ct == TA_DELETE_USER) { - if(is_st) { - snprintf(statement,sizeof(statement),"delete from turnusers_st where name='%s'",user); - } else { - snprintf(statement,sizeof(statement),"delete from turnusers_lt where name='%s' and realm='%s'",user,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)); - } - } - - if(ct == TA_UPDATE_USER) { - if(is_st) { - snprintf(statement,sizeof(statement),"insert into turnusers_st values('%s','%s')",user,passwd); - } else { - snprintf(statement,sizeof(statement),"insert into turnusers_lt (realm,name,hmackey) values('%s','%s','%s')",realm,user,skey); - } - int res = mysql_query(myc, statement); - if(res) { - if(is_st) { - snprintf(statement,sizeof(statement),"update turnusers_st set password='%s' where name='%s'",passwd,user); - } else { - snprintf(statement,sizeof(statement),"update turnusers_lt set hmackey='%s' where name='%s' and realm='%s'",skey,user,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)); - } - } - } - } -#endif - } else if(is_redis_userdb()) { - -#if !defined(TURN_NO_HIREDIS) - - if(!is_st) { - must_set_admin_realm(realm); - } - - redisContext *rc = get_redis_connection(); - if(rc) { - char statement[LONG_STRING_SIZE]; - - if(ct == TA_DELETE_USER) { - if(is_st) { - snprintf(statement,sizeof(statement),"del turn/user/%s/password",user); - turnFreeRedisReply(redisCommand(rc, statement)); - } else { - snprintf(statement,sizeof(statement),"del turn/realm/%s/user/%s/key",(char*)realm,user); - turnFreeRedisReply(redisCommand(rc, statement)); - snprintf(statement,sizeof(statement),"del turn/realm/%s/user/%s/password",(char*)realm,user); - turnFreeRedisReply(redisCommand(rc, statement)); - } - } - - if(ct == TA_UPDATE_USER) { - if(is_st) { - snprintf(statement,sizeof(statement),"set turn/user/%s/password %s",user,passwd); - turnFreeRedisReply(redisCommand(rc, statement)); - } else { - snprintf(statement,sizeof(statement),"set turn/realm/%s/user/%s/key %s",(char*)realm,user,skey); - turnFreeRedisReply(redisCommand(rc, statement)); - snprintf(statement,sizeof(statement),"del turn/realm/%s/user/%s/password",(char*)realm,user); - turnFreeRedisReply(redisCommand(rc, statement)); - } - } - - turnFreeRedisReply(redisCommand(rc, "save")); - } -#endif + + if (ct == TA_DELETE_USER) { + if (dbd->del_user) + (*dbd->del_user)(user, is_st, realm); + } else if (ct == TA_UPDATE_USER) { + if (is_st) { + if (dbd->set_user_pwd) + (*dbd->set_user_pwd)(user, passwd); + } else { + if (dbd->set_user_key) + (*dbd->set_user_key)(user, realm, skey); + } + } + } else if(!is_st) { persistent_users_db_t *pud = get_persistent_users_db(); char *full_path_to_userdb_file = find_config_file(pud->userdb, 1); FILE *f = full_path_to_userdb_file ? fopen(full_path_to_userdb_file,"r") : NULL; int found = 0; - char us[LONG_STRING_SIZE]; + char us[TURN_LONG_STRING_SIZE]; size_t i = 0; char **content = NULL; size_t csz = 0; @@ -2832,8 +996,8 @@ int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08b TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "File %s not found, will be created.\n",pud->userdb); } else { - char sarg[LONG_STRING_SIZE]; - char sbuf[LONG_STRING_SIZE]; + char sarg[TURN_LONG_STRING_SIZE]; + char sbuf[TURN_LONG_STRING_SIZE]; for (;;) { char *s0 = fgets(sbuf, sizeof(sbuf) - 1, f); @@ -2931,55 +1095,10 @@ int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08b void auth_ping(redis_context_handle rch) { - UNUSED_ARG(rch); - - donot_print_connection_success = 1; - -#if !defined(TURN_NO_PQ) - PGconn * pqc = get_pqdb_connection(); - if(pqc) { - char statement[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); - } + turn_dbdriver_t * dbd = get_dbdriver(); + if (dbd && dbd->auth_ping) { + (*dbd->auth_ping)(rch); } -#endif - -#if !defined(TURN_NO_MYSQL) - MYSQL * myc = get_mydb_connection(); - if(myc) { - char statement[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); - } - } - } -#endif - -#if !defined(TURN_NO_HIREDIS) - redisContext *rc = get_redis_connection(); - if(rc) { - turnFreeRedisReply(redisCommand(rc, "keys turn/origin/*")); - } - if(rch) - send_message_to_redis(rch, "publish", "__XXX__", "__YYY__"); -#endif - } ///////////////// WHITE/BLACK IP LISTS /////////////////// @@ -3077,114 +1196,13 @@ const ip_range_list_t* ioa_get_blacklist(ioa_engine_handle e) static ip_range_list_t* get_ip_list(const char *kind) { - UNUSED_ARG(kind); ip_range_list_t *ret = (ip_range_list_t*)turn_malloc(sizeof(ip_range_list_t)); ns_bzero(ret,sizeof(ip_range_list_t)); -#if !defined(TURN_NO_PQ) - PGconn * pqc = get_pqdb_connection(); - if(pqc) { - char statement[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;iget_ip_list) { + (*dbd->get_ip_list)(kind, ret); } -#endif - -#if !defined(TURN_NO_MYSQL) - MYSQL * myc = get_mydb_connection(); - if(myc) { - char statement[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[LONG_STRING_SIZE]; - ns_bcopy(row[0],kval,sz); - kval[sz]=0; - add_ip_list_range(kval,ret); - } - } - } - } - } - - if(mres) - mysql_free_result(mres); - } - } -#endif - -#if !defined(TURN_NO_HIREDIS) - redisContext *rc = get_redis_connection(); - if(rc) { - char statement[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;isztype == REDIS_REPLY_ERROR) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); - else if (rget->type != REDIS_REPLY_STRING) { - if (rget->type != REDIS_REPLY_NIL) - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); - } else { - add_ip_list_range(rget->str,ret); - } - turnFreeRedisReply(rget); - } - } - - clean_secrets_list(&keys); - - turnFreeRedisReply(reply); - } - } -#endif return ret; } @@ -3231,7 +1249,7 @@ void update_white_and_black_lists(void) /////////////// add ACL record /////////////////// -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) { char* separator = strchr(range, '-'); @@ -3273,41 +1291,6 @@ int add_ip_list_range(char* range, ip_range_list_t * list) /////////// REALM ////////////// -#if !defined(TURN_NO_HIREDIS) -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 { - ur_string_map_lock(realms); - *value = (unsigned long)atol(rget->str); - ur_string_map_unlock(realms); - found = 1; - } - turnFreeRedisReply(rget); - } - } - - return found; -} -#endif - void reread_realms(void) { { @@ -3319,322 +1302,10 @@ void reread_realms(void) ur_string_map_unlock(realms); } -#if !defined(TURN_NO_PQ) - PGconn * pqc = get_pqdb_connection(); - if(pqc) { - char statement[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;ioptions.perf_options.max_bps = turn_params.max_bps; - ur_string_map_unlock(realms); - - ur_string_map_lock(realms); - rp->options.perf_options.total_quota = turn_params.total_quota; - ur_string_map_unlock(realms); - - ur_string_map_lock(realms); - rp->options.perf_options.user_quota = turn_params.user_quota; - ur_string_map_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;ioptions.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); - } - } + turn_dbdriver_t * dbd = get_dbdriver(); + if (dbd && dbd->reread_realms) { + (*dbd->reread_realms)(&realms_list); } -#endif - -#if !defined(TURN_NO_MYSQL) - MYSQL * myc = get_mydb_connection(); - if(myc) { - char statement[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); - } - } - } - } - - TURN_MUTEX_LOCK(&o_to_realm_mutex); - ur_string_map_free(&o_to_realm); - o_to_realm = o_to_realm_new; - TURN_MUTEX_UNLOCK(&o_to_realm_mutex); - } - - if(mres) - mysql_free_result(mres); - } - } - { - size_t i = 0; - size_t rlsz = 0; - - ur_string_map_lock(realms); - rlsz = realms_list.sz; - ur_string_map_unlock(realms); - - for (i = 0; ioptions.perf_options.max_bps = turn_params.max_bps; - ur_string_map_unlock(realms); - - ur_string_map_lock(realms); - rp->options.perf_options.total_quota = turn_params.total_quota; - ur_string_map_unlock(realms); - - ur_string_map_lock(realms); - rp->options.perf_options.user_quota = turn_params.user_quota; - ur_string_map_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); - } - } -#endif - -#if !defined(TURN_NO_HIREDIS) - if (is_redis_userdb()) { - 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); - - TURN_MUTEX_LOCK(&o_to_realm_mutex); - ur_string_map_free(&o_to_realm); - o_to_realm = o_to_realm_new; - TURN_MUTEX_UNLOCK(&o_to_realm_mutex); - - turnFreeRedisReply(reply); - } - - { - size_t i = 0; - size_t rlsz = 0; - - ur_string_map_lock(realms); - rlsz = realms_list.sz; - ur_string_map_unlock(realms); - - for (i = 0; ioptions.perf_options.max_bps = turn_params.max_bps; - ur_string_map_unlock(realms); - } - rp->options.perf_options.max_bps = (band_limit_t)value; - if(!set_redis_realm_opt(realm,"total-quota",&value)) { - ur_string_map_lock(realms); - rp->options.perf_options.total_quota = turn_params.total_quota; - ur_string_map_unlock(realms); - } - rp->options.perf_options.total_quota = (vint)value; - if(!set_redis_realm_opt(realm,"user-quota",&value)) { - ur_string_map_lock(realms); - rp->options.perf_options.user_quota = turn_params.user_quota; - ur_string_map_unlock(realms); - } - rp->options.perf_options.user_quota = (vint)value; - } - } - } - } -#endif } /////////////////////////////// diff --git a/src/apps/relay/userdb.h b/src/apps/relay/userdb.h index ae4cf4a5..a529d1bb 100644 --- a/src/apps/relay/userdb.h +++ b/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 //////////////////////