coturn/src/apps/relay/dbdrivers/dbd_mysql.c
Michael Jones 66a85ef09e
Address clang-tidy warnings in db files (#1405)
The general approach here was:

- Always declare variables as close to where they are defined as
possible.
- Check for pre-conditions of functions before doing work (e.g. ensure
we can connect to the DB before doing a bunch of string formatting)
- Keep the scope of mutexes as reasonably small as practical.
- Use idiomatic C11, such as for-loops over the thing being iterated,
not while() loops over constants, or variables that aren't modified.
- Prefer if(fail){return} function-body after over `if(not fail){
function-body inside if} return;

Clang-tidy returns a clean bill of health, but while going through this
file i noticed a lot of things that raise questions.

Lack of checking column counts. Lack of handling the possibility of
multiple return values. Questionably handling of strings. Complete lack
of checking function inputs for invalid values (e.g. nullptr).

I'm not going to fix those, my organization doesn't USE the DB drivers,
so i have little interest in re-working the logic beyond addressing
clang-tidy warnings for my own sanity, but i did add TODO comments for
someone else to look at in the future.



Additional note: While the changes look very invasive.... they aren't.

I don't think there is a way to get github to ignore whitespace in the
filediff, but if someone were to compare the commit locally, they'll see
that almost all of the changes are just adjusting indentation.
2024-05-29 20:44:23 -07:00

1342 lines
43 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 "dbd_mysql.h"
#include "../mainrelay.h"
#if !defined(TURN_NO_MYSQL)
#include <mysql.h>
///////////////////////////////////////////////////////////////////////////////////////////////////////////
static int donot_print_connection_success = 0;
struct _Myconninfo {
char *host;
char *dbname;
char *user;
char *password;
unsigned int port;
unsigned int connect_timeout;
unsigned int read_timeout;
/* SSL ==>> */
char *key;
char *ca;
char *cert;
char *capath;
char *cipher;
/* <<== SSL : see http://dev.mysql.com/doc/refman/5.0/en/mysql-ssl-set.html */
};
typedef struct _Myconninfo Myconninfo;
static void MyconninfoFree(Myconninfo *co) {
if (co) {
if (co->host) {
free(co->host);
}
if (co->dbname) {
free(co->dbname);
}
if (co->user) {
free(co->user);
}
if (co->password) {
free(co->password);
}
if (co->key) {
free(co->key);
}
if (co->ca) {
free(co->ca);
}
if (co->cert) {
free(co->cert);
}
if (co->capath) {
free(co->capath);
}
if (co->cipher) {
free(co->cipher);
}
memset(co, 0, sizeof(Myconninfo));
free(co);
}
}
char *decryptPassword(char *in, const unsigned char *mykey) {
char *out;
unsigned char iv[8] = {0}; // changed
AES_KEY key;
unsigned char outdata[256]; // changed
AES_set_encrypt_key(mykey, 128, &key);
int newTotalSize = decodedTextSize(in);
int bytes_to_decode = strlen(in);
unsigned char *encryptedText = base64decode(in, bytes_to_decode); // changed
char last[1024] = "";
struct ctr_state state;
init_ctr(&state, iv);
memset(outdata, '\0', sizeof(outdata));
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
CRYPTO_ctr128_encrypt(encryptedText, outdata, newTotalSize, &key, state.ivec, state.ecount, &state.num,
(block128_f)AES_encrypt);
#else
AES_ctr128_encrypt(encryptedText, outdata, newTotalSize, &key, state.ivec, state.ecount, &state.num);
#endif
strcat(last, (char *)outdata);
out = (char *)malloc(sizeof(char) * (strlen(last) + 1)); // add 1 to allocate space for terminating '\0'
strcpy(out, last);
return out;
}
static Myconninfo *MyconninfoParse(char *userdb, char **errmsg) {
Myconninfo *co = (Myconninfo *)malloc(sizeof(Myconninfo));
memset(co, 0, 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 if (!strcmp(s, "read_timeout")) {
co->read_timeout = (unsigned int)atoi(seq + 1);
} else if (!strcmp(s, "key")) {
co->key = strdup(seq + 1);
} else if (!strcmp(s, "ssl-key")) {
co->key = strdup(seq + 1);
} else if (!strcmp(s, "ca")) {
co->ca = strdup(seq + 1);
} else if (!strcmp(s, "ssl-ca")) {
co->ca = strdup(seq + 1);
} else if (!strcmp(s, "capath")) {
co->capath = strdup(seq + 1);
} else if (!strcmp(s, "ssl-capath")) {
co->capath = strdup(seq + 1);
} else if (!strcmp(s, "cert")) {
co->cert = strdup(seq + 1);
} else if (!strcmp(s, "ssl-cert")) {
co->cert = strdup(seq + 1);
} else if (!strcmp(s, "cipher")) {
co->cipher = strdup(seq + 1);
} else if (!strcmp(s, "ssl-cipher")) {
co->cipher = strdup(seq + 1);
} else {
MyconninfoFree(co);
co = NULL;
if (errmsg) {
*errmsg = strdup(s);
}
break;
}
s = snext;
}
free(s0);
}
if (co) {
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 *)pthread_getspecific(connection_key);
if (mydbconnection) {
if (mysql_ping(mydbconnection)) {
mysql_close(mydbconnection);
mydbconnection = NULL;
(void)pthread_setspecific(connection_key, mydbconnection);
}
}
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_sanitized, errmsg);
free(errmsg);
} else {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open MySQL DB connection <%s>, connection string format error\n",
pud->userdb_sanitized);
}
} else if (errmsg) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open MySQL DB connection <%s>, connection string format error: %s\n",
pud->userdb_sanitized, errmsg);
free(errmsg);
MyconninfoFree(co);
} else if (!(co->dbname)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "MySQL Database name is not provided: <%s>\n", pud->userdb_sanitized);
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));
}
if (co->read_timeout) {
mysql_options(mydbconnection, MYSQL_OPT_READ_TIMEOUT, &(co->read_timeout));
}
if (co->ca || co->capath || co->cert || co->cipher || co->key) {
mysql_ssl_set(mydbconnection, co->key, co->cert, co->ca, co->capath, co->cipher);
}
if (turn_params.secret_key_file[0]) {
co->password = decryptPassword(co->password, turn_params.secret_key);
}
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_sanitized);
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_sanitized);
if (turn_params.secret_key_file[0]) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Encryption with AES is activated.\n");
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Connection is secure.\n");
} else {
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Connection is not secure.\n");
}
donot_print_connection_success = 1;
}
}
MyconninfoFree(co);
}
if (mydbconnection) {
(void)pthread_setspecific(connection_key, mydbconnection);
}
}
return mydbconnection;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
static int mysql_get_auth_secrets(secrets_list_t *sl, uint8_t *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];
memcpy(auth_secret, row[0], 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(uint8_t *usname, uint8_t *realm, hmackey_t key) {
int ret = -1;
MYSQL *myc = get_mydb_connection();
if (myc) {
char statement[TURN_LONG_STRING_SIZE];
/* direct user input eliminated - there is no SQL injection problem (since version 4.4.5.3) */
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(SHATYPE_DEFAULT) * 2;
if (lengths[0] < sz) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key format: string length=%d (must be %d): user %s\n",
(int)lengths[0], (int)sz, usname);
} else {
char kval[sizeof(hmackey_t) + sizeof(hmackey_t) + 1];
memcpy(kval, row[0], sz);
kval[sz] = 0;
convert_string_key_to_binary(kval, key, sz / 2);
ret = 0;
}
}
}
}
if (mres) {
mysql_free_result(mres);
}
}
}
return ret;
}
static int mysql_get_oauth_key(const uint8_t *kid, oauth_key_data_raw *key) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
/* direct user input eliminated - there is no SQL injection problem (since version 4.4.5.3) */
snprintf(statement, sizeof(statement),
"select ikm_key,timestamp,lifetime,as_rs_alg,realm from oauth_key where kid='%s'", (const char *)kid);
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) != 5) {
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) {
STRCPY(key->kid, kid);
memcpy(key->ikm_key, row[0], lengths[0]);
key->ikm_key[lengths[0]] = 0;
char stimestamp[128];
memcpy(stimestamp, row[1], lengths[1]);
stimestamp[lengths[1]] = 0;
key->timestamp = (uint64_t)strtoull(stimestamp, NULL, 10);
char slifetime[128];
memcpy(slifetime, row[2], lengths[2]);
slifetime[lengths[2]] = 0;
key->lifetime = (uint32_t)strtoul(slifetime, NULL, 10);
memcpy(key->as_rs_alg, row[3], lengths[3]);
key->as_rs_alg[lengths[3]] = 0;
memcpy(key->realm, row[4], lengths[4]);
key->realm[lengths[4]] = 0;
ret = 0;
}
}
}
if (mres) {
mysql_free_result(mres);
}
}
}
return ret;
}
static int mysql_list_oauth_keys(secrets_list_t *kids, secrets_list_t *teas, secrets_list_t *tss, secrets_list_t *lts,
secrets_list_t *realms) {
oauth_key_data_raw key_;
oauth_key_data_raw *key = &key_;
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
snprintf(statement, sizeof(statement),
"select ikm_key,timestamp,lifetime,as_rs_alg,realm,kid from oauth_key order by kid");
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) != 6) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n", statement);
} else {
MYSQL_ROW row = mysql_fetch_row(mres);
while (row) {
unsigned long *lengths = mysql_fetch_lengths(mres);
if (lengths) {
memcpy(key->ikm_key, row[0], lengths[0]);
key->ikm_key[lengths[0]] = 0;
char stimestamp[128];
memcpy(stimestamp, row[1], lengths[1]);
stimestamp[lengths[1]] = 0;
key->timestamp = (uint64_t)strtoull(stimestamp, NULL, 10);
char slifetime[128];
memcpy(slifetime, row[2], lengths[2]);
slifetime[lengths[2]] = 0;
key->lifetime = (uint32_t)strtoul(slifetime, NULL, 10);
memcpy(key->as_rs_alg, row[3], lengths[3]);
key->as_rs_alg[lengths[3]] = 0;
memcpy(key->realm, row[4], lengths[4]);
key->realm[lengths[4]] = 0;
memcpy(key->kid, row[5], lengths[5]);
key->kid[lengths[5]] = 0;
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);
}
}
row = mysql_fetch_row(mres);
}
}
if (mres) {
mysql_free_result(mres);
}
}
}
return ret;
}
static int mysql_set_user_key(uint8_t *usname, uint8_t *realm, const char *key) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
MYSQL *myc = get_mydb_connection();
if (myc) {
snprintf(statement, sizeof(statement), "insert into turnusers_lt (realm,name,hmackey) values('%s','%s','%s')",
realm, usname, key);
int res = mysql_query(myc, statement);
if (!res) {
ret = 0;
} else {
snprintf(statement, sizeof(statement), "update turnusers_lt set hmackey='%s' where name='%s' and realm='%s'", key,
usname, realm);
res = mysql_query(myc, statement);
if (!res) {
ret = 0;
} else {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user key information: %s\n", mysql_error(myc));
}
}
}
return ret;
}
static int mysql_set_oauth_key(oauth_key_data_raw *key) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
MYSQL *myc = get_mydb_connection();
if (myc) {
snprintf(
statement, sizeof(statement),
"insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg,realm) values('%s','%s',%llu,%lu,'%s','%s')",
key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->as_rs_alg,
key->realm);
int res = mysql_query(myc, statement);
if (res) {
snprintf(
statement, sizeof(statement),
"update oauth_key set ikm_key='%s',timestamp=%lu,lifetime=%lu, as_rs_alg='%s', realm='%s' where kid='%s'",
key->ikm_key, (unsigned long)key->timestamp, (unsigned long)key->lifetime, key->as_rs_alg, key->realm,
key->kid);
res = mysql_query(myc, statement);
if (res) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating oauth key information: %s\n", mysql_error(myc));
} else {
ret = 0;
}
} else {
ret = 0;
}
}
return ret;
}
static int mysql_del_user(uint8_t *usname, uint8_t *realm) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
MYSQL *myc = get_mydb_connection();
if (myc) {
snprintf(statement, sizeof(statement), "delete from turnusers_lt where name='%s' and realm='%s'", usname, realm);
int res = mysql_query(myc, statement);
if (res) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting user key information: %s\n", mysql_error(myc));
} else {
ret = 0;
}
}
return ret;
}
static int mysql_del_oauth_key(const uint8_t *kid) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
MYSQL *myc = get_mydb_connection();
if (myc) {
snprintf(statement, sizeof(statement), "delete from oauth_key where kid = '%s'", (const char *)kid);
int res = mysql_query(myc, statement);
if (res) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting oauth key information: %s\n", mysql_error(myc));
} else {
ret = 0;
}
}
return ret;
}
static int mysql_list_users(uint8_t *realm, secrets_list_t *users, secrets_list_t *realms) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
uint8_t realm0[STUN_MAX_REALM_SIZE + 1] = "\0";
if (!realm) {
realm = realm0;
}
MYSQL *myc = get_mydb_connection();
if (myc) {
if (realm[0]) {
snprintf(statement, sizeof(statement), "select name, realm from turnusers_lt where realm='%s' order by name",
realm);
} else {
snprintf(statement, sizeof(statement), "select name, realm from turnusers_lt order by realm,name");
}
int res = mysql_query(myc, statement);
if (res) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n", mysql_error(myc));
} else {
MYSQL_RES *mres = mysql_store_result(myc);
if (!mres) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n", mysql_error(myc));
} else if (mysql_field_count(myc) != 2) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n", statement);
} else {
for (;;) {
MYSQL_ROW row = mysql_fetch_row(mres);
if (!row) {
break;
} else {
if (row[0]) {
if (users) {
add_to_secrets_list(users, row[0]);
if (realms) {
if (row[1]) {
add_to_secrets_list(realms, row[1]);
} else {
add_to_secrets_list(realms, (char *)realm);
}
}
} else {
printf("%s[%s]\n", row[0], row[1]);
}
}
}
}
ret = 0;
}
if (mres) {
mysql_free_result(mres);
}
}
}
return ret;
}
static int mysql_list_secrets(uint8_t *realm, secrets_list_t *secrets, secrets_list_t *realms) {
int ret = -1;
uint8_t realm0[STUN_MAX_REALM_SIZE + 1] = "\0";
if (!realm) {
realm = realm0;
}
char statement[TURN_LONG_STRING_SIZE];
if (realm[0]) {
snprintf(statement, sizeof(statement), "select value,realm from turn_secret where realm='%s' order by value",
realm);
} else {
snprintf(statement, sizeof(statement), "select value,realm from turn_secret order by realm,value");
}
donot_print_connection_success = 1;
MYSQL *myc = get_mydb_connection();
if (myc) {
int res = mysql_query(myc, statement);
if (res) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n", mysql_error(myc));
} else {
MYSQL_RES *mres = mysql_store_result(myc);
if (!mres) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n", mysql_error(myc));
} else if (mysql_field_count(myc) != 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 {
const char *kval = row[0];
if (kval) {
const char *rval = row[1];
if (secrets) {
add_to_secrets_list(secrets, kval);
if (realms) {
if (rval && *rval) {
add_to_secrets_list(realms, rval);
} else {
add_to_secrets_list(realms, (char *)realm);
}
}
} else {
printf("%s[%s]\n", kval, rval);
}
}
}
}
ret = 0;
}
if (mres) {
mysql_free_result(mres);
}
}
}
return ret;
}
static int mysql_del_secret(uint8_t *secret, uint8_t *realm) {
int ret = -1;
donot_print_connection_success = 1;
char statement[TURN_LONG_STRING_SIZE];
MYSQL *myc = get_mydb_connection();
if (myc) {
if (!secret || (secret[0] == 0)) {
snprintf(statement, sizeof(statement), "delete from turn_secret where realm='%s'", realm);
} else {
snprintf(statement, sizeof(statement), "delete from turn_secret where value='%s' and realm='%s'", secret, realm);
}
mysql_query(myc, statement);
ret = 0;
}
return ret;
}
static int mysql_set_secret(uint8_t *secret, uint8_t *realm) {
int ret = -1;
donot_print_connection_success = 1;
char statement[TURN_LONG_STRING_SIZE];
MYSQL *myc = get_mydb_connection();
if (myc) {
snprintf(statement, sizeof(statement), "insert into turn_secret (realm,value) values('%s','%s')", realm, secret);
int res = mysql_query(myc, statement);
if (res) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating secret key information: %s\n", mysql_error(myc));
} else {
ret = 0;
}
}
return ret;
}
static int mysql_set_permission_ip(const char *kind, uint8_t *realm, const char *ip, int del) {
int ret = -1;
uint8_t realm0[STUN_MAX_REALM_SIZE + 1] = "\0";
if (!realm) {
realm = realm0;
}
donot_print_connection_success = 1;
char statement[TURN_LONG_STRING_SIZE];
MYSQL *myc = get_mydb_connection();
if (myc) {
if (del) {
snprintf(statement, sizeof(statement), "delete from %s_peer_ip where realm = '%s' and ip_range = '%s'", kind,
(char *)realm, ip);
} else {
snprintf(statement, sizeof(statement), "insert into %s_peer_ip (realm,ip_range) values('%s','%s')", kind,
(char *)realm, ip);
}
int res = mysql_query(myc, statement);
if (res) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting permission ip information: %s\n", mysql_error(myc));
} else {
ret = 0;
}
}
return ret;
}
static int mysql_add_origin(uint8_t *origin, uint8_t *realm) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
MYSQL *myc = get_mydb_connection();
if (myc) {
snprintf(statement, sizeof(statement), "insert into turn_origin_to_realm (origin,realm) values('%s','%s')", origin,
realm);
int res = mysql_query(myc, statement);
if (res) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting origin information: %s\n", mysql_error(myc));
} else {
ret = 0;
}
}
return ret;
}
static int mysql_del_origin(uint8_t *origin) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
MYSQL *myc = get_mydb_connection();
if (myc) {
snprintf(statement, sizeof(statement), "delete from turn_origin_to_realm where origin='%s'", origin);
int res = mysql_query(myc, statement);
if (res) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting origin information: %s\n", mysql_error(myc));
} else {
ret = 0;
}
}
return ret;
}
static int mysql_list_origins(uint8_t *realm, secrets_list_t *origins, secrets_list_t *realms) {
int ret = -1;
uint8_t realm0[STUN_MAX_REALM_SIZE + 1] = "\0";
if (!realm) {
realm = realm0;
}
donot_print_connection_success = 1;
MYSQL *myc = get_mydb_connection();
if (myc) {
char statement[TURN_LONG_STRING_SIZE];
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 realm,origin");
}
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]) {
const char *kval = row[0];
const char *rval = row[1];
if (origins) {
add_to_secrets_list(origins, kval);
if (realms) {
if (rval && *rval) {
add_to_secrets_list(realms, rval);
} else {
add_to_secrets_list(realms, (char *)realm);
}
}
} else {
printf("%s ==>> %s\n", kval, rval);
}
}
}
}
ret = 0;
}
if (mres) {
mysql_free_result(mres);
}
}
}
return ret;
}
static int mysql_set_realm_option_one(uint8_t *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(uint8_t *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);
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,realm from %s_peer_ip", kind);
int res = mysql_query(myc, statement);
if (res) {
static int wrong_table_reported = 0;
if (!wrong_table_reported) {
wrong_table_reported = 1;
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,
"Error retrieving MySQL DB information; probably, the tables 'allowed_peer_ip' and/or "
"'denied_peer_ip' have to be upgraded to include the realm column.\n");
}
snprintf(statement, sizeof(statement), "select ip_range,'' from %s_peer_ip", kind);
res = mysql_query(myc, statement);
}
if (res == 0) {
MYSQL_RES *mres = mysql_store_result(myc);
if (mres && mysql_field_count(myc) == 2) {
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];
memcpy(kval, row[0], sz);
kval[sz] = 0;
sz = lengths[1];
char rval[TURN_LONG_STRING_SIZE];
memcpy(rval, row[1], sz);
rval[sz] = 0;
add_ip_list_range(kval, rval, 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];
memcpy(oval, row[0], sz);
oval[sz] = 0;
char *rval = strdup(row[1]);
get_realm(rval);
ur_string_map_value_type value = (ur_string_map_value_type)rval;
ur_string_map_put(o_to_realm_new, (ur_string_map_key_type)oval, value);
}
}
}
}
update_o_to_realm(o_to_realm_new);
}
if (mres) {
mysql_free_result(mres);
}
}
}
{
size_t i = 0;
size_t rlsz = 0;
lock_realms();
rlsz = realms_list->sz;
unlock_realms();
for (i = 0; i < rlsz; ++i) {
char *realm = realms_list->secrets[i];
realm_params_t *rp = get_realm(realm);
lock_realms();
rp->options.perf_options.max_bps = turn_params.max_bps;
unlock_realms();
lock_realms();
rp->options.perf_options.total_quota = turn_params.total_quota;
unlock_realms();
lock_realms();
rp->options.perf_options.user_quota = turn_params.user_quota;
unlock_realms();
}
}
snprintf(statement, sizeof(statement), "select realm,opt,value from turn_realm_option");
int res = mysql_query(myc, statement);
if (res == 0) {
MYSQL_RES *mres = mysql_store_result(myc);
if (mres && mysql_field_count(myc) == 3) {
for (;;) {
MYSQL_ROW row = mysql_fetch_row(mres);
if (!row) {
break;
} else {
if (row[0] && row[1] && row[2]) {
unsigned long *lengths = mysql_fetch_lengths(mres);
if (lengths) {
char rval[513];
size_t sz = lengths[0];
memcpy(rval, row[0], sz);
rval[sz] = 0;
char oval[513];
sz = lengths[1];
memcpy(oval, row[1], sz);
oval[sz] = 0;
char vval[513];
sz = lengths[2];
memcpy(vval, row[2], 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)strtoul(vval, NULL, 10);
} 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 int mysql_get_admin_user(const uint8_t *usname, uint8_t *realm, password_t pwd) {
int ret = -1;
realm[0] = 0;
pwd[0] = 0;
MYSQL *myc = get_mydb_connection();
if (myc) {
char statement[TURN_LONG_STRING_SIZE];
snprintf(statement, sizeof(statement), "select realm,password from admin_user where name='%s'", usname);
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 {
MYSQL_ROW row = mysql_fetch_row(mres);
if (row && row[0]) {
strncpy((char *)realm, row[0], STUN_MAX_REALM_SIZE);
strncpy((char *)pwd, row[1], STUN_MAX_PWD_SIZE);
ret = 0;
}
}
if (mres) {
mysql_free_result(mres);
}
}
}
return ret;
}
static int mysql_set_admin_user(const uint8_t *usname, const uint8_t *realm, const password_t pwd) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
donot_print_connection_success = 1;
MYSQL *myc = get_mydb_connection();
if (myc) {
snprintf(statement, sizeof(statement), "insert into admin_user (realm,name,password) values('%s','%s','%s')", realm,
usname, pwd);
int res = mysql_query(myc, statement);
if (!res) {
ret = 0;
} else {
snprintf(statement, sizeof(statement), "update admin_user set realm='%s',password='%s' where name='%s'", realm,
pwd, usname);
res = mysql_query(myc, statement);
if (!res) {
ret = 0;
} else {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user key information: %s\n", mysql_error(myc));
}
}
}
return ret;
}
static int mysql_del_admin_user(const uint8_t *usname) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
donot_print_connection_success = 1;
MYSQL *myc = get_mydb_connection();
if (myc) {
snprintf(statement, sizeof(statement), "delete from admin_user where name='%s'", usname);
int res = mysql_query(myc, statement);
if (res) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting admin user information: %s\n", mysql_error(myc));
} else {
ret = 0;
}
}
return ret;
}
static int mysql_list_admin_users(int no_print) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
donot_print_connection_success = 1;
MYSQL *myc = get_mydb_connection();
if (myc) {
snprintf(statement, sizeof(statement), "select name, realm from admin_user order by realm,name");
int res = mysql_query(myc, statement);
if (res) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n", mysql_error(myc));
} else {
MYSQL_RES *mres = mysql_store_result(myc);
if (!mres) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n", mysql_error(myc));
} else if (mysql_field_count(myc) != 2) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n", statement);
} else {
ret = 0;
for (;;) {
MYSQL_ROW row = mysql_fetch_row(mres);
if (!row) {
break;
} else {
++ret;
if (row[0] && !no_print) {
if (row[1] && row[1][0]) {
printf("%s[%s]\n", row[0], row[1]);
} else {
printf("%s\n", row[0]);
}
}
}
}
}
if (mres) {
mysql_free_result(mres);
}
}
}
return ret;
}
static void mysql_disconnect(void) {
MYSQL *mydbconnection = (MYSQL *)pthread_getspecific(connection_key);
if (mydbconnection) {
mysql_close(mydbconnection);
mydbconnection = NULL;
}
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MySQL connection was closed.\n");
}
//////////////////////////////////////////////////////
static const turn_dbdriver_t driver = {
&mysql_get_auth_secrets, &mysql_get_user_key, &mysql_set_user_key, &mysql_del_user,
&mysql_list_users, &mysql_list_secrets, &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_set_permission_ip,
&mysql_reread_realms, &mysql_set_oauth_key, &mysql_get_oauth_key, &mysql_del_oauth_key,
&mysql_list_oauth_keys, &mysql_get_admin_user, &mysql_set_admin_user, &mysql_del_admin_user,
&mysql_list_admin_users, &mysql_disconnect};
const turn_dbdriver_t *get_mysql_dbdriver(void) { return &driver; }
///////////////////////////////////////////////////////////////////////////////////////////////////////////
#else
const turn_dbdriver_t *get_mysql_dbdriver(void) { return NULL; }
#endif