coturn/src/apps/relay/dbdrivers/dbd_mongo.c
2015-09-14 00:16:13 -07:00

1395 lines
40 KiB
C

/*
* Copyright (C) 2011, 2012, 2013 Citrix Systems
* Copyright (C) 2014 Vivocha S.p.A.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "../mainrelay.h"
#include "dbd_mongo.h"
#if !defined(TURN_NO_MONGO)
#include <mongoc.h>
#include <bson.h>
///////////////////////////////////////////////////////////////////////////////////////////////////////////
const char * MONGO_DEFAULT_DB = "turn";
struct _MONGO {
mongoc_uri_t * uri;
mongoc_client_t * client;
const char * database;
};
typedef struct _MONGO MONGO;
static void mongo_logger(mongoc_log_level_t log_level, const char * log_domain, const char * message, void * user_data) {
UNUSED_ARG(log_domain);
UNUSED_ARG(user_data);
TURN_LOG_LEVEL l = TURN_LOG_LEVEL_INFO;
UNUSED_ARG(l);
switch(log_level) {
case MONGOC_LOG_LEVEL_ERROR:
l = TURN_LOG_LEVEL_ERROR;
break;
case MONGOC_LOG_LEVEL_WARNING:
l = TURN_LOG_LEVEL_WARNING;
break;
default:
l = TURN_LOG_LEVEL_INFO;
break;
}
TURN_LOG_FUNC(l, "%s\n", message);
}
static void MongoFree(MONGO * info) {
if(info) {
if(info->uri) mongoc_uri_destroy(info->uri);
if(info->client) mongoc_client_destroy(info->client);
turn_free(info, sizeof(MONGO));
}
}
static MONGO * get_mongodb_connection(void) {
persistent_users_db_t * pud = get_persistent_users_db();
MONGO * mydbconnection = (MONGO *) pthread_getspecific(connection_key);
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;
if(mydbconnection) {
(void) pthread_setspecific(connection_key, mydbconnection);
}
TURN_LOG_FUNC(
TURN_LOG_LEVEL_INFO,
"Opened MongoDB URI <%s>\n",
pud->userdb);
}
}
}
return mydbconnection;
}
static mongoc_collection_t * mongo_get_collection(const char * name) {
MONGO * mc = get_mongodb_connection();
if(!mc) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error getting 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(SHATYPE_DEFAULT) * 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_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
mongoc_collection_t * collection = mongo_get_collection("oauth_key");
if (!collection)
return -1;
bson_t query;
bson_init(&query);
BSON_APPEND_UTF8(&query, "kid", (const char *)kid);
bson_t fields;
bson_init(&fields);
BSON_APPEND_INT32(&fields, "lifetime", 1);
BSON_APPEND_INT32(&fields, "timestamp", 1);
BSON_APPEND_INT32(&fields, "as_rs_alg", 1);
BSON_APPEND_INT32(&fields, "realm", 1);
BSON_APPEND_INT32(&fields, "ikm_key", 1);
mongoc_cursor_t * cursor;
cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 1, 0,
&query, &fields, NULL);
int ret = -1;
ns_bzero(key,sizeof(oauth_key_data_raw));
STRCPY(key->kid,kid);
if (!cursor) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,
"Error querying MongoDB collection 'oauth_key'\n");
} else {
const bson_t * item;
uint32_t length;
bson_iter_t iter;
if (mongoc_cursor_next(cursor, &item)) {
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "as_rs_alg") && BSON_ITER_HOLDS_UTF8(&iter)) {
STRCPY(key->as_rs_alg,bson_iter_utf8(&iter, &length));
}
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) {
STRCPY(key->realm,bson_iter_utf8(&iter, &length));
}
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "ikm_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
STRCPY(key->ikm_key,bson_iter_utf8(&iter, &length));
}
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "timestamp") && BSON_ITER_HOLDS_INT64(&iter)) {
key->timestamp = (u64bits)bson_iter_int64(&iter);
}
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "lifetime") && BSON_ITER_HOLDS_INT32(&iter)) {
key->lifetime = (u32bits)bson_iter_int32(&iter);
}
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 user key information\n");
} else {
ret = 0;
}
mongoc_collection_destroy(collection);
bson_destroy(&doc);
bson_destroy(&query);
return ret;
}
static int mongo_set_oauth_key(oauth_key_data_raw *key) {
mongoc_collection_t * collection = mongo_get_collection("oauth_key");
if(!collection)
return -1;
bson_t query;
bson_init(&query);
BSON_APPEND_UTF8(&query, "kid", (const char *)key->kid);
bson_t doc;
bson_init(&doc);
BSON_APPEND_UTF8(&doc, "kid", (const char *)key->kid);
BSON_APPEND_UTF8(&doc, "as_rs_alg", (const char *)key->as_rs_alg);
BSON_APPEND_UTF8(&doc, "realm", (const char *)key->realm);
BSON_APPEND_UTF8(&doc, "ikm_key", (const char *)key->ikm_key);
BSON_APPEND_INT64(&doc, "timestamp", (int64_t)key->timestamp);
BSON_APPEND_INT32(&doc, "lifetime", (int32_t)key->lifetime);
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 oauth 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, u08bits *realm) {
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);
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_del_oauth_key(const u08bits *kid) {
mongoc_collection_t * collection = mongo_get_collection("oauth_key");
if(!collection)
return -1;
bson_t query;
bson_init(&query);
BSON_APPEND_UTF8(&query, "kid", (const char *)kid);
int ret = -1;
if (!mongoc_collection_delete(collection, MONGOC_DELETE_SINGLE_REMOVE, &query, NULL, NULL)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting oauth key information\n");
} else {
ret = 0;
}
mongoc_collection_destroy(collection);
bson_destroy(&query);
return ret;
}
static int mongo_list_users(u08bits *realm, secrets_list_t *users, secrets_list_t *realms)
{
const char * collection_name = "turnusers_lt";
mongoc_collection_t * collection = mongo_get_collection(collection_name);
u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
if(!realm) realm=realm0;
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, 1);
bson_append_int32(&child, "name", -1, 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, "name", 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 '%s'\n", collection_name);
} else {
const bson_t * item;
uint32_t length;
bson_iter_t iter;
bson_iter_t iter_realm;
const char * value;
while (mongoc_cursor_next(cursor, &item)) {
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "name") && BSON_ITER_HOLDS_UTF8(&iter)) {
value = bson_iter_utf8(&iter, &length);
if (length) {
const char *rval = "";
if (bson_iter_init(&iter_realm, item) && bson_iter_find(&iter_realm, "realm") && BSON_ITER_HOLDS_UTF8(&iter_realm)) {
rval = bson_iter_utf8(&iter_realm, &length);
}
if(users) {
add_to_secrets_list(users,value);
if(realms) {
if(rval && *rval) {
add_to_secrets_list(realms,rval);
} else {
add_to_secrets_list(realms,(char*)realm);
}
}
} else {
printf("%s[%s]\n", value, rval);
}
}
}
}
mongoc_cursor_destroy(cursor);
ret = 0;
}
mongoc_collection_destroy(collection);
bson_destroy(&query);
bson_destroy(&fields);
return ret;
}
static int mongo_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts,secrets_list_t *realms) {
const char * collection_name = "oauth_key";
mongoc_collection_t * collection = mongo_get_collection(collection_name);
if(!collection)
return -1;
bson_t query;
bson_init(&query);
bson_t child;
bson_append_document_begin(&query, "$orderby", -1, &child);
bson_append_int32(&child, "kid", -1, 1);
bson_append_document_end(&query, &child);
bson_append_document_begin(&query, "$query", -1, &child);
bson_append_document_end(&query, &child);
bson_t fields;
bson_init(&fields);
BSON_APPEND_INT32(&fields, "kid", 1);
BSON_APPEND_INT32(&fields, "lifetime", 1);
BSON_APPEND_INT32(&fields, "timestamp", 1);
BSON_APPEND_INT32(&fields, "as_rs_alg", 1);
BSON_APPEND_INT32(&fields, "realm", 1);
BSON_APPEND_INT32(&fields, "ikm_key", 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;
oauth_key_data_raw key_;
oauth_key_data_raw *key=&key_;
uint32_t length;
bson_iter_t iter;
while (mongoc_cursor_next(cursor, &item)) {
ns_bzero(key,sizeof(oauth_key_data_raw));
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "kid") && BSON_ITER_HOLDS_UTF8(&iter)) {
STRCPY(key->kid,bson_iter_utf8(&iter, &length));
}
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "as_rs_alg") && BSON_ITER_HOLDS_UTF8(&iter)) {
STRCPY(key->as_rs_alg,bson_iter_utf8(&iter, &length));
}
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) {
STRCPY(key->realm,bson_iter_utf8(&iter, &length));
}
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "ikm_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
STRCPY(key->ikm_key,bson_iter_utf8(&iter, &length));
}
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "timestamp") && BSON_ITER_HOLDS_INT64(&iter)) {
key->timestamp = (u64bits)bson_iter_int64(&iter);
}
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "lifetime") && BSON_ITER_HOLDS_INT32(&iter)) {
key->lifetime = (u32bits)bson_iter_int32(&iter);
}
if(kids) {
add_to_secrets_list(kids,key->kid);
add_to_secrets_list(teas,key->as_rs_alg);
add_to_secrets_list(realms,key->realm);
{
char ts[256];
snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp);
add_to_secrets_list(tss,ts);
}
{
char lt[256];
snprintf(lt,sizeof(lt)-1,"%lu",(unsigned long)key->lifetime);
add_to_secrets_list(lts,lt);
}
} else {
printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s, realm=%s\n",
key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime,
key->as_rs_alg, key->realm);
}
}
mongoc_cursor_destroy(cursor);
ret = 0;
}
mongoc_collection_destroy(collection);
bson_destroy(&query);
bson_destroy(&fields);
return ret;
}
static int mongo_list_secrets(u08bits *realm, secrets_list_t *secrets, secrets_list_t *realms)
{
mongoc_collection_t * collection = mongo_get_collection("turn_secret");
u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
if(!realm) realm=realm0;
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, 1);
bson_append_int32(&child, "value", -1, 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, "value", 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 'turn_secret'\n");
} else {
const bson_t * item;
uint32_t length;
bson_iter_t iter;
bson_iter_t iter_realm;
const char * value;
while (mongoc_cursor_next(cursor, &item)) {
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "value") && BSON_ITER_HOLDS_UTF8(&iter)) {
value = bson_iter_utf8(&iter, &length);
if (length) {
const char *rval = "";
if (bson_iter_init(&iter_realm, item) && bson_iter_find(&iter_realm, "realm") && BSON_ITER_HOLDS_UTF8(&iter_realm)) {
rval = bson_iter_utf8(&iter_realm, &length);
}
if(secrets) {
add_to_secrets_list(secrets,value);
if(realms) {
if(rval && *rval) {
add_to_secrets_list(realms,rval);
} else {
add_to_secrets_list(realms,(char*)realm);
}
}
} else {
printf("%s[%s]\n", value, rval);
}
}
}
}
mongoc_cursor_destroy(cursor);
ret = 0;
}
mongoc_collection_destroy(collection);
bson_destroy(&query);
bson_destroy(&fields);
return ret;
}
static int mongo_del_secret(u08bits *secret, u08bits *realm) {
mongoc_collection_t * collection = mongo_get_collection("turn_secret");
if(!collection)
return -1;
bson_t query;
bson_init(&query);
BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
if(secret && (secret[0]!=0)) {
BSON_APPEND_UTF8(&query, "value", (const char *)secret);
}
mongoc_collection_delete(collection, MONGOC_DELETE_NONE, &query, NULL, NULL);
mongoc_collection_destroy(collection);
bson_destroy(&query);
return 0;
}
static int mongo_set_secret(u08bits *secret, u08bits *realm) {
mongoc_collection_t * collection = mongo_get_collection("turn_secret");
if(!collection)
return -1;
bson_t query;
bson_init(&query);
BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
BSON_APPEND_UTF8(&query, "value", (const char *)secret);
int res = mongoc_collection_insert(collection, MONGOC_INSERT_NONE, &query, NULL, NULL);
mongoc_collection_destroy(collection);
bson_destroy(&query);
if (!res) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating secret key information\n");
return -1;
} else {
return 0;
}
}
static int mongo_set_permission_ip(const char *kind, u08bits *realm, const char* ip, int del)
{
char sub_collection_name[129];
snprintf(sub_collection_name,sizeof(sub_collection_name)-1,"%s_peer_ip",kind);
mongoc_collection_t * collection = mongo_get_collection("realm");
if(!collection)
return -1;
int ret = -1;
u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
if(!realm) realm=realm0;
bson_t query, doc, child;
bson_init(&query);
BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
bson_init(&doc);
if(del) {
bson_append_document_begin(&doc, "$pull", -1, &child);
} else {
bson_append_document_begin(&doc, "$addToSet", -1, &child);
}
BSON_APPEND_UTF8(&child, sub_collection_name, (const char *)ip);
bson_append_document_end(&doc, &child);
mongoc_update_flags_t flags = MONGOC_UPDATE_NONE;
if(del) {
flags = MONGOC_UPDATE_MULTI_UPDATE;
} else {
flags = MONGOC_UPDATE_UPSERT;
}
if (!mongoc_collection_update(collection, flags, &query, &doc, NULL, NULL)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting permission ip information\n");
} else {
ret = 0;
}
mongoc_collection_destroy(collection);
bson_destroy(&query);
bson_destroy(&doc);
return ret;
}
static int mongo_add_origin(u08bits *origin, u08bits *realm)
{
mongoc_collection_t * collection = mongo_get_collection("realm");
if(!collection)
return -1;
int ret = -1;
u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
if(!realm) realm=realm0;
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, secrets_list_t *origins, secrets_list_t *realms)
{
mongoc_collection_t * collection = mongo_get_collection("realm");
if(!collection)
return -1;
u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
if(!realm) realm=realm0;
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);
if(origins) {
add_to_secrets_list(origins,_origin);
if(realms) {
add_to_secrets_list(realms,_realm);
}
} else {
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);
size_t klen = 9 + strlen(opt);
char * _k = (char *)turn_malloc(klen);
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);
}
turn_free(_k,klen);
int ret = -1;
if (!mongoc_collection_update(collection, MONGOC_UPDATE_MULTI_UPDATE, &query, &doc, NULL, NULL)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting origin information\n");
} else {
ret = 0;
}
mongoc_collection_destroy(collection);
bson_destroy(&query);
bson_destroy(&doc);
return ret;
}
static int mongo_list_realm_options(u08bits *realm) {
mongoc_collection_t * collection = mongo_get_collection("realm");
if(!collection)
return -1;
bson_t query, child;
bson_init(&query);
bson_append_document_begin(&query, "$orderby", -1, &child);
BSON_APPEND_INT32(&child, "realm", 1);
bson_append_document_end(&query, &child);
bson_append_document_begin(&query, "$query", -1, &child);
if (realm && realm[0]) {
BSON_APPEND_UTF8(&child, "realm", (const char *)realm);
}
bson_append_document_end(&query, &child);
bson_t fields;
bson_init(&fields);
BSON_APPEND_INT32(&fields, "options", 1);
BSON_APPEND_INT32(&fields, "realm", 1);
mongoc_cursor_t * cursor;
cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
int ret = -1;
if (!cursor) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'realm'\n");
} else {
const bson_t * item;
uint32_t length;
bson_iter_t iter;
while (mongoc_cursor_next(cursor, &item)) {
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) {
const char * _realm = bson_iter_utf8(&iter, &length);
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "options") && BSON_ITER_HOLDS_DOCUMENT(&iter)) {
const uint8_t *docbuf = NULL;
uint32_t doclen = 0;
bson_t options;
bson_iter_t options_iter;
bson_iter_document(&iter, &doclen, &docbuf);
bson_init_static(&options, docbuf, doclen);
if (bson_iter_init(&options_iter, &options)) {
while(bson_iter_next(&options_iter)) {
const char * _k = bson_iter_key(&options_iter);
if (BSON_ITER_HOLDS_DOUBLE(&options_iter)) {
int32_t _v = (int32_t)bson_iter_double(&options_iter);
printf("%s[%s]=%d\n", _k, _realm, _v);
} else if (BSON_ITER_HOLDS_INT32(&options_iter)) {
int32_t _v = bson_iter_int32(&options_iter);
printf("%s[%s]=%d\n", _k, _realm, _v);
} else if (BSON_ITER_HOLDS_INT64(&options_iter)) {
int32_t _v = (int32_t)bson_iter_int64(&options_iter);
printf("%s[%s]=%d\n", _k, _realm, _v);
}
}
}
}
}
}
mongoc_cursor_destroy(cursor);
ret = 0;
}
mongoc_collection_destroy(collection);
bson_destroy(&query);
bson_destroy(&fields);
return ret;
}
static void mongo_auth_ping(void * rch) {
UNUSED_ARG(rch);
// NOOP
}
static int mongo_read_realms_ip_lists(const char *kind, ip_range_list_t * list)
{
int ret = 0;
char field_name[129];
sprintf(field_name, "%s_peer_ip", kind);
mongoc_collection_t * collection = mongo_get_collection("realm");
if (!collection)
return ret;
bson_t query;
bson_init(&query);
bson_t fields;
bson_init(&fields);
BSON_APPEND_INT32(&fields, "realm", 1);
BSON_APPEND_INT32(&fields, field_name, 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");
ret = -1;
} else {
const bson_t * item;
uint32_t length;
bson_iter_t iter;
char realm[513];
while (mongoc_cursor_next(cursor, &item)) {
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm")
&& BSON_ITER_HOLDS_UTF8(&iter)) {
STRCPY(realm,bson_iter_utf8(&iter, &length));
if (bson_iter_init(&iter, item) && bson_iter_find(&iter,
field_name) && BSON_ITER_HOLDS_ARRAY(&iter)) {
const uint8_t *docbuf = NULL;
uint32_t doclen = 0;
bson_t ip_range_array;
bson_iter_t ip_range_iter;
bson_iter_array(&iter, &doclen, &docbuf);
bson_init_static(&ip_range_array, docbuf, doclen);
if (bson_iter_init(&ip_range_iter, &ip_range_array)) {
while (bson_iter_next(&ip_range_iter)) {
if (BSON_ITER_HOLDS_UTF8(&ip_range_iter)) {
const char* ip_range = bson_iter_utf8(&ip_range_iter, &length);
add_ip_list_range(ip_range, realm, list);
}
}
}
}
}
}
mongoc_cursor_destroy(cursor);
}
mongoc_collection_destroy(collection);
bson_destroy(&query);
bson_destroy(&fields);
return ret;
}
static int mongo_get_ip_list(const char *kind, ip_range_list_t * list) {
return mongo_read_realms_ip_lists(kind, list);
}
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(turn_free_simple);
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 = turn_strdup(bson_iter_utf8(&iter, &length));
get_realm(_realm);
if (bson_iter_init(&iter, item) && bson_iter_find(&iter,
"origin") && BSON_ITER_HOLDS_ARRAY(&iter)) {
const uint8_t *docbuf = NULL;
uint32_t doclen = 0;
bson_t origin_array;
bson_iter_t origin_iter;
bson_iter_array(&iter, &doclen, &docbuf);
bson_init_static(&origin_array, docbuf, doclen);
if (bson_iter_init(&origin_iter, &origin_array)) {
while (bson_iter_next(&origin_iter)) {
if (BSON_ITER_HOLDS_UTF8(&origin_iter)) {
char* _origin = turn_strdup(bson_iter_utf8(&origin_iter, &length));
char *rval = turn_strdup(_realm);
ur_string_map_value_type value =
(ur_string_map_value_type) (rval);
ur_string_map_put(o_to_realm_new,
(const ur_string_map_key_type) _origin,
value);
turn_free(_origin,strlen(_origin)+1);
}
}
}
}
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);
uint64_t _v = 0;
if (BSON_ITER_HOLDS_DOUBLE(&options_iter)) {
_v = (uint64_t) bson_iter_double(&options_iter);
} else if (BSON_ITER_HOLDS_INT32(&options_iter)) {
_v = (uint64_t)bson_iter_int32(&options_iter);
} else if (BSON_ITER_HOLDS_INT64(&options_iter)) {
_v = (uint64_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);
}
}
}
}
}
turn_free(_realm,strlen(_realm)+1);
}
}
update_o_to_realm(o_to_realm_new);
mongoc_cursor_destroy(cursor);
}
mongoc_collection_destroy(collection);
bson_destroy(&query);
bson_destroy(&fields);
}
/////////////////////////////////////////////////
static int mongo_get_admin_user(const u08bits *usname, u08bits *realm, password_t pwd)
{
mongoc_collection_t * collection = mongo_get_collection("admin_user");
if(!collection)
return -1;
realm[0]=0;
pwd[0]=0;
bson_t query;
bson_init(&query);
BSON_APPEND_UTF8(&query, "name", (const char *)usname);
bson_t fields;
bson_init(&fields);
BSON_APPEND_INT32(&fields, "realm", 1);
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 'admin_user'\n");
} else {
const bson_t * item;
uint32_t length;
bson_iter_t iter;
if (mongoc_cursor_next(cursor, &item)) {
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) {
strncpy((char*)realm,bson_iter_utf8(&iter, &length),STUN_MAX_REALM_SIZE);
ret = 0;
}
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "password") && BSON_ITER_HOLDS_UTF8(&iter)) {
strncpy((char*)pwd,bson_iter_utf8(&iter, &length),STUN_MAX_PWD_SIZE);
ret = 0;
}
}
mongoc_cursor_destroy(cursor);
}
mongoc_collection_destroy(collection);
bson_destroy(&query);
bson_destroy(&fields);
return ret;
}
static int mongo_set_admin_user(const u08bits *usname, const u08bits *realm, const password_t pwd)
{
mongoc_collection_t * collection = mongo_get_collection("admin_user");
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, "realm", (const char *)realm);
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 admin user information\n");
} else {
ret = 0;
}
mongoc_collection_destroy(collection);
bson_destroy(&doc);
bson_destroy(&query);
return ret;
}
static int mongo_del_admin_user(const u08bits *usname)
{
mongoc_collection_t * collection = mongo_get_collection("admin_user");
if(!collection)
return -1;
bson_t query;
bson_init(&query);
BSON_APPEND_UTF8(&query, "name", (const char *)usname);
int ret = -1;
if (!mongoc_collection_delete(collection, MONGOC_DELETE_SINGLE_REMOVE, &query, NULL, NULL)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting admin user information\n");
} else {
ret = 0;
}
mongoc_collection_destroy(collection);
bson_destroy(&query);
return ret;
}
static int mongo_list_admin_users(int no_print)
{
const char * collection_name = "admin_user";
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);
bson_append_document_end(&query, &child);
bson_t fields;
bson_init(&fields);
BSON_APPEND_INT32(&fields, "name", 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 '%s'\n", collection_name);
} else {
const bson_t * item;
uint32_t length;
bson_iter_t iter;
bson_iter_t iter_realm;
const char * value;
ret = 0;
while (mongoc_cursor_next(cursor, &item)) {
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "name") && BSON_ITER_HOLDS_UTF8(&iter)) {
value = bson_iter_utf8(&iter, &length);
if (length) {
const char *realm = "";
if (bson_iter_init(&iter_realm, item) && bson_iter_find(&iter_realm, "realm") && BSON_ITER_HOLDS_UTF8(&iter_realm)) {
realm = bson_iter_utf8(&iter_realm, &length);
}
++ret;
if(!no_print) {
if(realm && *realm) {
printf("%s[%s]\n", value, realm);
} else {
printf("%s\n", value);
}
}
}
}
}
mongoc_cursor_destroy(cursor);
}
mongoc_collection_destroy(collection);
bson_destroy(&query);
bson_destroy(&fields);
return ret;
}
//////////////////////////////////////////////////////////
static const turn_dbdriver_t driver = {
&mongo_get_auth_secrets,
&mongo_get_user_key,
&mongo_set_user_key,
&mongo_del_user,
&mongo_list_users,
&mongo_list_secrets,
&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_set_permission_ip,
&mongo_reread_realms,
&mongo_set_oauth_key,
&mongo_get_oauth_key,
&mongo_del_oauth_key,
&mongo_list_oauth_keys,
&mongo_get_admin_user,
&mongo_set_admin_user,
&mongo_del_admin_user,
&mongo_list_admin_users
};
const turn_dbdriver_t * get_mongo_dbdriver(void) {
return &driver;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
#else
const turn_dbdriver_t * get_mongo_dbdriver(void) {
return NULL;
}
#endif