1
0
mirror of https://github.com/coturn/coturn.git synced 2025-10-27 13:00:59 +01:00
coturn/src/apps/relay/dbdrivers/dbd_pgsql.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

1005 lines
31 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_pgsql.h"
#include "../mainrelay.h"
#if !defined(TURN_NO_PQ)
#include <libpq-fe.h>
///////////////////////////////////////////////////////////////////////////////////////////////////////////
static int donot_print_connection_success = 0;
static PGconn *get_pqdb_connection(void) {
persistent_users_db_t *pud = get_persistent_users_db();
PGconn *pqdbconnection = (PGconn *)pthread_getspecific(connection_key);
if (pqdbconnection) {
ConnStatusType status = PQstatus(pqdbconnection);
if (status != CONNECTION_OK) {
PQfinish(pqdbconnection);
pqdbconnection = NULL;
(void)pthread_setspecific(connection_key, pqdbconnection);
}
}
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_sanitized, errmsg);
free(errmsg);
} else {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,
"Cannot open PostgreSQL DB connection: <%s>, unknown connection string format error\n",
pud->userdb_sanitized);
}
} else {
PQconninfoFree(co);
if (errmsg) {
free(errmsg);
}
pqdbconnection = PQconnectdb(pud->userdb);
if (!pqdbconnection) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection: <%s>, runtime error\n",
pud->userdb_sanitized);
} 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_sanitized);
} else if (!donot_print_connection_success) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "PostgreSQL DB connection success: %s\n", pud->userdb_sanitized);
donot_print_connection_success = 1;
}
}
}
if (pqdbconnection) {
(void)pthread_setspecific(connection_key, pqdbconnection);
}
}
return pqdbconnection;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
static int pgsql_get_auth_secrets(secrets_list_t *sl, uint8_t *realm) {
int ret = -1;
PGconn *pqc = get_pqdb_connection();
if (pqc) {
char statement[TURN_LONG_STRING_SIZE];
snprintf(statement, sizeof(statement) - 1, "select value from turn_secret where realm='%s'", realm);
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n", PQerrorMessage(pqc));
} else {
int i = 0;
for (i = 0; i < PQntuples(res); i++) {
char *kval = PQgetvalue(res, i, 0);
if (kval) {
add_to_secrets_list(sl, kval);
}
}
ret = 0;
}
if (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_get_user_key(uint8_t *usname, uint8_t *realm, hmackey_t key) {
int ret = -1;
PGconn *pqc = get_pqdb_connection();
if (pqc) {
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);
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(SHATYPE_DEFAULT);
if (((size_t)len < sz * 2) || (strlen(kval) < sz * 2)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key format: %s, user %s\n", kval, usname);
} else {
convert_string_key_to_binary(kval, key, sz);
ret = 0;
}
} else {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong hmackey data for user %s: NULL\n", usname);
}
}
if (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_get_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);
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 {
STRCPY(key->ikm_key, PQgetvalue(res, 0, 0));
key->timestamp = (uint64_t)strtoll(PQgetvalue(res, 0, 1), NULL, 10);
key->lifetime = (uint32_t)strtol(PQgetvalue(res, 0, 2), NULL, 10);
STRCPY(key->as_rs_alg, PQgetvalue(res, 0, 3));
STRCPY(key->realm, PQgetvalue(res, 0, 4));
STRCPY(key->kid, kid);
ret = 0;
}
if (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_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");
PGconn *pqc = get_pqdb_connection();
if (pqc) {
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n", PQerrorMessage(pqc));
} else {
int i = 0;
for (i = 0; i < PQntuples(res); i++) {
STRCPY(key->ikm_key, PQgetvalue(res, i, 0));
key->timestamp = (uint64_t)strtoll(PQgetvalue(res, i, 1), NULL, 10);
key->lifetime = (uint32_t)strtol(PQgetvalue(res, i, 2), NULL, 10);
STRCPY(key->as_rs_alg, PQgetvalue(res, i, 3));
STRCPY(key->realm, PQgetvalue(res, i, 4));
STRCPY(key->kid, PQgetvalue(res, i, 5));
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);
}
ret = 0;
}
}
if (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_set_user_key(uint8_t *usname, uint8_t *realm, const char *key) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
PGconn *pqc = get_pqdb_connection();
if (pqc) {
snprintf(statement, sizeof(statement), "insert into turnusers_lt (realm,name,hmackey) values('%s','%s','%s')",
realm, usname, key);
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
if (res) {
PQclear(res);
}
snprintf(statement, sizeof(statement), "update turnusers_lt set hmackey='%s' where name='%s' and realm='%s'", key,
usname, realm);
res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user information: %s\n", PQerrorMessage(pqc));
} else {
ret = 0;
}
}
if (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_set_oauth_key(oauth_key_data_raw *key) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
PGconn *pqc = get_pqdb_connection();
if (pqc) {
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);
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
if (res) {
PQclear(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 = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating oauth_key information: %s\n",
PQerrorMessage(pqc));
} else {
ret = 0;
}
} else {
ret = 0;
}
if (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_del_user(uint8_t *usname, uint8_t *realm) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
PGconn *pqc = get_pqdb_connection();
if (pqc) {
snprintf(statement, sizeof(statement), "delete from turnusers_lt where name='%s' and realm='%s'", usname, realm);
PGresult *res = PQexec(pqc, statement);
if (res) {
PQclear(res);
ret = 0;
}
}
return ret;
}
static int pgsql_del_oauth_key(const uint8_t *kid) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
PGconn *pqc = get_pqdb_connection();
if (pqc) {
snprintf(statement, sizeof(statement), "delete from oauth_key where kid = '%s'", (const char *)kid);
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting oauth_key information: %s\n", PQerrorMessage(pqc));
} else {
ret = 0;
}
if (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_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;
}
PGconn *pqc = get_pqdb_connection();
if (pqc) {
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");
}
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n", PQerrorMessage(pqc));
} else {
int i = 0;
for (i = 0; i < PQntuples(res); i++) {
char *kval = PQgetvalue(res, i, 0);
if (kval) {
char *rval = PQgetvalue(res, i, 1);
if (rval) {
if (users) {
add_to_secrets_list(users, 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 (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_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;
PGconn *pqc = get_pqdb_connection();
if (pqc) {
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n", PQerrorMessage(pqc));
} else {
int i = 0;
for (i = 0; i < PQntuples(res); i++) {
char *kval = PQgetvalue(res, i, 0);
if (kval) {
char *rval = PQgetvalue(res, i, 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 (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_del_secret(uint8_t *secret, uint8_t *realm) {
int ret = -1;
donot_print_connection_success = 1;
char statement[TURN_LONG_STRING_SIZE];
PGconn *pqc = get_pqdb_connection();
if (pqc) {
if (!secret || (secret[0] == 0)) {
snprintf(statement, sizeof(statement), "delete from turn_secret where realm='%s'", realm);
} else {
snprintf(statement, sizeof(statement), "delete from turn_secret where value='%s' and realm='%s'", secret, realm);
}
PGresult *res = PQexec(pqc, statement);
if (res) {
PQclear(res);
ret = 0;
}
}
return ret;
}
static int pgsql_set_secret(uint8_t *secret, uint8_t *realm) {
int ret = -1;
donot_print_connection_success = 1;
char statement[TURN_LONG_STRING_SIZE];
PGconn *pqc = get_pqdb_connection();
if (pqc) {
snprintf(statement, sizeof(statement), "insert into turn_secret (realm,value) values('%s','%s')", realm, secret);
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating secret key information: %s\n", PQerrorMessage(pqc));
} else {
ret = 0;
}
if (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_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];
PGconn *pqc = get_pqdb_connection();
if (pqc) {
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);
}
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting ip permission information: %s\n", PQerrorMessage(pqc));
} else {
ret = 0;
}
if (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_add_origin(uint8_t *origin, uint8_t *realm) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
PGconn *pqc = get_pqdb_connection();
if (pqc) {
snprintf(statement, sizeof(statement), "insert into turn_origin_to_realm (origin,realm) values('%s','%s')", origin,
realm);
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting origin information: %s\n", PQerrorMessage(pqc));
} else {
ret = 0;
}
if (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_del_origin(uint8_t *origin) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
PGconn *pqc = get_pqdb_connection();
if (pqc) {
snprintf(statement, sizeof(statement), "delete from turn_origin_to_realm where origin='%s'", origin);
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting origin information: %s\n", PQerrorMessage(pqc));
} else {
ret = 0;
}
if (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_list_origins(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;
PGconn *pqc = get_pqdb_connection();
if (pqc) {
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");
}
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n", PQerrorMessage(pqc));
} else {
int i = 0;
for (i = 0; i < PQntuples(res); i++) {
char *kval = PQgetvalue(res, i, 0);
if (kval) {
char *rval = PQgetvalue(res, i, 1);
if (rval) {
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 (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_set_realm_option_one(uint8_t *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(uint8_t *realm) {
int ret = -1;
donot_print_connection_success = 1;
char statement[TURN_LONG_STRING_SIZE];
PGconn *pqc = get_pqdb_connection();
if (pqc) {
if (realm && realm[0]) {
snprintf(statement, sizeof(statement),
"select realm,opt,value from turn_realm_option where realm='%s' order by realm,opt", realm);
} else {
snprintf(statement, sizeof(statement), "select realm,opt,value from turn_realm_option order by realm,opt");
}
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n", PQerrorMessage(pqc));
} else {
int i = 0;
for (i = 0; i < PQntuples(res); i++) {
char *rval = PQgetvalue(res, i, 0);
if (rval) {
char *oval = PQgetvalue(res, i, 1);
if (oval) {
char *vval = PQgetvalue(res, i, 2);
if (vval) {
printf("%s[%s]=%s\n", oval, rval, vval);
}
}
}
}
ret = 0;
}
if (res) {
PQclear(res);
}
}
return ret;
}
static void pgsql_auth_ping(void *rch) {
UNUSED_ARG(rch);
PGconn *pqc = get_pqdb_connection();
if (pqc) {
char statement[TURN_LONG_STRING_SIZE];
STRCPY(statement, "select value from turn_secret");
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n", PQerrorMessage(pqc));
}
if (res) {
PQclear(res);
}
}
}
static int pgsql_get_ip_list(const char *kind, ip_range_list_t *list) {
int ret = -1;
PGconn *pqc = get_pqdb_connection();
if (pqc) {
char statement[TURN_LONG_STRING_SIZE];
snprintf(statement, sizeof(statement), "select ip_range,realm from %s_peer_ip", kind);
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
static int wrong_table_reported = 0;
if (!wrong_table_reported) {
wrong_table_reported = 1;
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,
"Error retrieving PostgreSQL DB information: %s; probably, the tables 'allowed_peer_ip' and/or "
"'denied_peer_ip' have to be upgraded to include the realm column.\n",
PQerrorMessage(pqc));
}
snprintf(statement, sizeof(statement), "select ip_range,'' from %s_peer_ip", kind);
res = PQexec(pqc, statement);
}
if (res && (PQresultStatus(res) == PGRES_TUPLES_OK)) {
int i = 0;
for (i = 0; i < PQntuples(res); i++) {
char *kval = PQgetvalue(res, i, 0);
char *rval = PQgetvalue(res, i, 1);
if (kval) {
add_ip_list_range(kval, rval, list);
}
}
ret = 0;
}
if (res) {
PQclear(res);
}
}
return ret;
}
static void pgsql_reread_realms(secrets_list_t *realms_list) {
PGconn *pqc = get_pqdb_connection();
if (pqc) {
char statement[TURN_LONG_STRING_SIZE];
{
snprintf(statement, sizeof(statement), "select origin,realm from turn_origin_to_realm");
PGresult *res = PQexec(pqc, statement);
if (res && (PQresultStatus(res) == PGRES_TUPLES_OK)) {
ur_string_map *o_to_realm_new = ur_string_map_create(free);
int i = 0;
for (i = 0; i < PQntuples(res); i++) {
char *oval = PQgetvalue(res, i, 0);
if (oval) {
char *rval = PQgetvalue(res, i, 1);
if (rval) {
get_realm(rval);
ur_string_map_value_type value = strdup(rval);
if (!ur_string_map_put(o_to_realm_new, (ur_string_map_key_type)oval, value)) {
free(value);
}
}
}
}
update_o_to_realm(o_to_realm_new);
}
if (res) {
PQclear(res);
}
}
{
{
size_t i = 0;
size_t rlsz = 0;
lock_realms();
rlsz = realms_list->sz;
unlock_realms();
for (i = 0; i < rlsz; ++i) {
char *realm = realms_list->secrets[i];
realm_params_t *rp = get_realm(realm);
lock_realms();
rp->options.perf_options.max_bps = turn_params.max_bps;
unlock_realms();
lock_realms();
rp->options.perf_options.total_quota = turn_params.total_quota;
unlock_realms();
lock_realms();
rp->options.perf_options.user_quota = turn_params.user_quota;
unlock_realms();
}
}
snprintf(statement, sizeof(statement), "select realm,opt,value from turn_realm_option");
PGresult *res = PQexec(pqc, statement);
if (res && (PQresultStatus(res) == PGRES_TUPLES_OK)) {
int i = 0;
for (i = 0; i < PQntuples(res); i++) {
char *rval = PQgetvalue(res, i, 0);
char *oval = PQgetvalue(res, i, 1);
char *vval = PQgetvalue(res, i, 2);
if (rval && oval && vval) {
realm_params_t *rp = get_realm(rval);
if (!strcmp(oval, "max-bps")) {
rp->options.perf_options.max_bps = (band_limit_t)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 (res) {
PQclear(res);
}
}
}
}
//////////////////////////////////////////////
static int pgsql_get_admin_user(const uint8_t *usname, uint8_t *realm, password_t pwd) {
int ret = -1;
realm[0] = 0;
pwd[0] = 0;
PGconn *pqc = get_pqdb_connection();
if (pqc) {
char statement[TURN_LONG_STRING_SIZE];
snprintf(statement, sizeof(statement), "select realm,password from admin_user where name='%s'", usname);
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 {
const char *kval = PQgetvalue(res, 0, 0);
if (kval) {
strncpy((char *)realm, kval, STUN_MAX_REALM_SIZE);
}
kval = (const char *)PQgetvalue(res, 0, 1);
if (kval) {
strncpy((char *)pwd, kval, STUN_MAX_PWD_SIZE);
}
ret = 0;
}
if (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_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;
PGconn *pqc = get_pqdb_connection();
if (pqc) {
snprintf(statement, sizeof(statement), "insert into admin_user (realm,name,password) values('%s','%s','%s')", realm,
usname, pwd);
PGresult *res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
if (res) {
PQclear(res);
}
snprintf(statement, sizeof(statement), "update admin_user set password='%s',realm='%s' where name='%s'", pwd,
realm, usname);
res = PQexec(pqc, statement);
if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user information: %s\n", PQerrorMessage(pqc));
} else {
ret = 0;
}
}
if (res) {
PQclear(res);
}
}
return ret;
}
static int pgsql_del_admin_user(const uint8_t *usname) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
donot_print_connection_success = 1;
PGconn *pqc = get_pqdb_connection();
if (pqc) {
snprintf(statement, sizeof(statement), "delete from admin_user where name='%s'", usname);
PGresult *res = PQexec(pqc, statement);
if (res) {
PQclear(res);
ret = 0;
}
}
return ret;
}
static int pgsql_list_admin_users(int no_print) {
int ret = -1;
char statement[TURN_LONG_STRING_SIZE];
donot_print_connection_success = 1;
PGconn *pqc = get_pqdb_connection();
if (pqc) {
snprintf(statement, sizeof(statement), "select name,realm,password from admin_user order by realm,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;
ret = 0;
for (i = 0; i < PQntuples(res); i++) {
char *kval = PQgetvalue(res, i, 0);
++ret;
if (kval && !no_print) {
char *rval = PQgetvalue(res, i, 1);
if (rval && *rval) {
printf("%s[%s]\n", kval, rval);
} else {
printf("%s\n", kval);
}
}
}
}
if (res) {
PQclear(res);
}
return ret;
}
static void pgsql_disconnect(void) {
PGconn *pqdbconnection = (PGconn *)pthread_getspecific(connection_key);
if (pqdbconnection) {
PQfinish(pqdbconnection);
pqdbconnection = NULL;
}
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "PostgreSQL connection was closed.\n");
}
/////////////////////////////////////////////////////////////
static const turn_dbdriver_t driver = {
&pgsql_get_auth_secrets, &pgsql_get_user_key, &pgsql_set_user_key, &pgsql_del_user,
&pgsql_list_users, &pgsql_list_secrets, &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_set_permission_ip,
&pgsql_reread_realms, &pgsql_set_oauth_key, &pgsql_get_oauth_key, &pgsql_del_oauth_key,
&pgsql_list_oauth_keys, &pgsql_get_admin_user, &pgsql_set_admin_user, &pgsql_del_admin_user,
&pgsql_list_admin_users, &pgsql_disconnect};
const turn_dbdriver_t *get_pgsql_dbdriver(void) { return &driver; }
///////////////////////////////////////////////////////////////////////////////////////////////////////////
#else
const turn_dbdriver_t *get_pgsql_dbdriver(void) { return NULL; }
#endif