1
0
mirror of https://github.com/coturn/coturn.git synced 2025-10-29 05:51:10 +01:00
coturn/src/apps/common/ns_turn_utils.c
2014-10-15 06:44:53 +00:00

863 lines
19 KiB
C

/*
* Copyright (C) 2011, 2012, 2013 Citrix Systems
*
* 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 "ns_turn_utils.h"
#include "ns_turn_ioalib.h"
#include "ns_turn_msg_defs.h"
#include <event2/http.h>
#include <time.h>
#include <pthread.h>
#include <syslog.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
////////// LOG TIME OPTIMIZATION ///////////
static volatile turn_time_t log_start_time = 0;
volatile int _log_time_value_set = 0;
volatile turn_time_t _log_time_value = 0;
static inline turn_time_t log_time(void)
{
if(!log_start_time)
log_start_time = turn_time();
if(_log_time_value_set)
return (_log_time_value - log_start_time);
return (turn_time() - log_start_time);
}
////////// MUTEXES /////////////
#define MAGIC_CODE (0xEFCD1983)
int turn_mutex_lock(const turn_mutex *mutex) {
if(mutex && mutex->mutex && (mutex->data == MAGIC_CODE)) {
int ret = 0;
ret = pthread_mutex_lock((pthread_mutex_t*)mutex->mutex);
if(ret<0) {
perror("Mutex lock");
}
return ret;
} else {
printf("Uninitialized mutex\n");
return -1;
}
}
int turn_mutex_unlock(const turn_mutex *mutex) {
if(mutex && mutex->mutex && (mutex->data == MAGIC_CODE)) {
int ret = 0;
ret = pthread_mutex_unlock((pthread_mutex_t*)mutex->mutex);
if(ret<0) {
perror("Mutex unlock");
}
return ret;
} else {
printf("Uninitialized mutex\n");
return -1;
}
}
int turn_mutex_init(turn_mutex* mutex) {
if(mutex) {
mutex->data=MAGIC_CODE;
mutex->mutex=turn_malloc(sizeof(pthread_mutex_t));
pthread_mutex_init((pthread_mutex_t*)mutex->mutex,NULL);
return 0;
} else {
return -1;
}
}
int turn_mutex_init_recursive(turn_mutex* mutex) {
int ret = -1;
if (mutex) {
pthread_mutexattr_t attr;
if (pthread_mutexattr_init(&attr) < 0) {
perror("Cannot init mutex attr");
} else {
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) < 0) {
perror("Cannot set type on mutex attr");
} else {
mutex->mutex = turn_malloc(sizeof(pthread_mutex_t));
mutex->data = MAGIC_CODE;
if ((ret = pthread_mutex_init((pthread_mutex_t*) mutex->mutex,
&attr)) < 0) {
perror("Cannot init mutex");
mutex->data = 0;
turn_free(mutex->mutex,sizeof(pthread_mutex_t));
mutex->mutex = NULL;
}
}
pthread_mutexattr_destroy(&attr);
}
}
return ret;
}
int turn_mutex_destroy(turn_mutex* mutex) {
if(mutex && mutex->mutex && mutex->data == MAGIC_CODE) {
int ret = 0;
ret = pthread_mutex_destroy((pthread_mutex_t*)(mutex->mutex));
turn_free(mutex->mutex, sizeof(pthread_mutex_t));
mutex->mutex=NULL;
mutex->data=0;
return ret;
} else {
return 0;
}
}
///////////////////////// LOG ///////////////////////////////////
#if defined(TURN_LOG_FUNC_IMPL)
extern void TURN_LOG_FUNC_IMPL(TURN_LOG_LEVEL level, const s08bits* format, va_list args);
#endif
static int no_stdout_log = 0;
void set_no_stdout_log(int val)
{
no_stdout_log = val;
}
void turn_log_func_default(TURN_LOG_LEVEL level, const s08bits* format, ...)
{
#if !defined(TURN_LOG_FUNC_IMPL)
{
va_list args;
va_start(args,format);
vrtpprintf(level, format, args);
va_end(args);
}
#endif
{
va_list args;
va_start(args,format);
#if defined(TURN_LOG_FUNC_IMPL)
TURN_LOG_FUNC_IMPL(level,format,args);
#else
#define MAX_RTPPRINTF_BUFFER_SIZE (1024)
char s[MAX_RTPPRINTF_BUFFER_SIZE+1];
#undef MAX_RTPPRINTF_BUFFER_SIZE
if (level == TURN_LOG_LEVEL_ERROR) {
snprintf(s,sizeof(s)-100,"%lu: ERROR: ",(unsigned long)log_time());
size_t slen = strlen(s);
vsnprintf(s+slen,sizeof(s)-slen-1,format, args);
fwrite(s,strlen(s),1,stdout);
} else if(!no_stdout_log) {
snprintf(s,sizeof(s)-100,"%lu: ",(unsigned long)log_time());
size_t slen = strlen(s);
vsnprintf(s+slen,sizeof(s)-slen-1,format, args);
fwrite(s,strlen(s),1,stdout);
}
#endif
va_end(args);
}
}
void addr_debug_print(int verbose, const ioa_addr *addr, const s08bits* s)
{
if (verbose) {
if (!addr) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: EMPTY\n", s);
} else {
s08bits addrbuf[INET6_ADDRSTRLEN];
if (!s)
s = "";
if (addr->ss.sa_family == AF_INET) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "IPv4. %s: %s:%d\n", s, inet_ntop(AF_INET,
&addr->s4.sin_addr, addrbuf, INET6_ADDRSTRLEN),
nswap16(addr->s4.sin_port));
} else if (addr->ss.sa_family == AF_INET6) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "IPv6. %s: %s:%d\n", s, inet_ntop(AF_INET6,
&addr->s6.sin6_addr, addrbuf, INET6_ADDRSTRLEN),
nswap16(addr->s6.sin6_port));
} else {
if (addr_any_no_port(addr)) {
TURN_LOG_FUNC(
TURN_LOG_LEVEL_INFO,
"IP. %s: 0.0.0.0:%d\n",
s,
nswap16(addr->s4.sin_port));
} else {
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: wrong IP address family: %d\n", s,
(int) (addr->ss.sa_family));
}
}
}
}
}
/*************************************/
#define FILE_STR_LEN (1025)
static FILE* _rtpfile = NULL;
static int to_syslog = 0;
static int simple_log = 0;
static char log_fn[FILE_STR_LEN]="\0";
static char log_fn_base[FILE_STR_LEN]="\0";
static turn_mutex log_mutex;
static int log_mutex_inited = 0;
static void log_lock(void) {
if(!log_mutex_inited) {
log_mutex_inited=1;
turn_mutex_init_recursive(&log_mutex);
}
turn_mutex_lock(&log_mutex);
}
static void log_unlock(void) {
turn_mutex_unlock(&log_mutex);
}
static void get_date(char *s, size_t sz) {
time_t curtm;
struct tm* tm_info;
curtm = time(NULL);
tm_info = localtime(&curtm);
strftime(s, sz, "%F", tm_info);
}
void set_logfile(const char *fn)
{
if(fn) {
log_lock();
if(strcmp(fn,log_fn_base)) {
reset_rtpprintf();
STRCPY(log_fn_base,fn);
}
log_unlock();
}
}
void reset_rtpprintf(void)
{
log_lock();
if(_rtpfile) {
if(_rtpfile != stdout)
fclose(_rtpfile);
_rtpfile = NULL;
}
log_unlock();
}
#define set_log_file_name(base, f) set_log_file_name_func(base, f, sizeof(f))
static void set_log_file_name_func(char *base, char *f, size_t fsz)
{
if(simple_log) {
strncpy(f,base,fsz);
return;
}
char logdate[125];
char *tail=turn_strdup(".log");
get_date(logdate,sizeof(logdate));
char *base1=turn_strdup(base);
int len=(int)strlen(base1);
--len;
while(len>=0) {
if((base1[len]==' ')||(base1[len]=='\t')) {
base1[len]='_';
}
--len;
}
len=(int)strlen(base1);
while(len>=0) {
if(base1[len]=='/')
break;
else if(base1[len]=='.') {
turn_free(tail,strlen(tail)+1);
tail=turn_strdup(base1+len);
base1[len]=0;
if(strlen(tail)<2) {
turn_free(tail,strlen(tail)+1);
tail = turn_strdup(".log");
}
break;
}
--len;
}
len=(int)strlen(base1);
if(len>0 && (base1[len-1]!='/') && (base1[len-1]!='-') && (base1[len-1]!='_')) {
snprintf(f, FILE_STR_LEN, "%s_%s%s", base1,logdate,tail);
} else {
snprintf(f, FILE_STR_LEN, "%s%s%s", base1,logdate,tail);
}
turn_free(base1,strlen(base1)+1);
turn_free(tail,strlen(tail)+1);
}
static void sighup_callback_handler(int signum)
{
if(signum == SIGHUP) {
printf("%s: resetting the log file\n",__FUNCTION__);
reset_rtpprintf();
}
}
static void set_rtpfile(void)
{
if(to_syslog) {
return;
} else if (!_rtpfile) {
signal(SIGHUP, sighup_callback_handler);
if(log_fn_base[0]) {
if(!strcmp(log_fn_base,"syslog")) {
_rtpfile = stdout;
to_syslog = 1;
} else if(!strcmp(log_fn_base,"stdout")|| !strcmp(log_fn_base,"-")) {
_rtpfile = stdout;
no_stdout_log = 1;
} else {
set_log_file_name(log_fn_base,log_fn);
_rtpfile = fopen(log_fn, "w");
if(_rtpfile)
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "log file opened: %s\n", log_fn);
}
if (!_rtpfile) {
fprintf(stderr,"ERROR: Cannot open log file for writing: %s\n",log_fn);
} else {
return;
}
}
}
if(!_rtpfile) {
char logbase[FILE_STR_LEN];
char logtail[FILE_STR_LEN];
char logf[FILE_STR_LEN];
if(simple_log)
snprintf(logtail, FILE_STR_LEN, "turn.log");
else
snprintf(logtail, FILE_STR_LEN, "turn_%d_", (int)getpid());
snprintf(logbase, FILE_STR_LEN, "/var/log/turnserver/%s", logtail);
set_log_file_name(logbase, logf);
_rtpfile = fopen(logf, "w");
if(_rtpfile)
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "log file opened: %s\n", logf);
else {
snprintf(logbase, FILE_STR_LEN, "/var/log/%s", logtail);
set_log_file_name(logbase, logf);
_rtpfile = fopen(logf, "w");
if(_rtpfile)
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "log file opened: %s\n", logf);
else {
snprintf(logbase, FILE_STR_LEN, "/var/tmp/%s", logtail);
set_log_file_name(logbase, logf);
_rtpfile = fopen(logf, "w");
if(_rtpfile)
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "log file opened: %s\n", logf);
else {
snprintf(logbase, FILE_STR_LEN, "/tmp/%s", logtail);
set_log_file_name(logbase, logf);
_rtpfile = fopen(logf, "w");
if(_rtpfile)
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "log file opened: %s\n", logf);
else {
snprintf(logbase, FILE_STR_LEN, "%s", logtail);
set_log_file_name(logbase, logf);
_rtpfile = fopen(logf, "w");
if(_rtpfile)
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "log file opened: %s\n", logf);
else {
_rtpfile = stdout;
return;
}
}
}
}
}
STRCPY(log_fn_base,logbase);
STRCPY(log_fn,logf);
}
}
void set_log_to_syslog(int val)
{
to_syslog = val;
}
void set_simple_log(int val)
{
simple_log = val;
}
#define Q(x) #x
#define QUOTE(x) Q(x)
void rollover_logfile(void)
{
if(to_syslog || !(log_fn[0]))
return;
{
FILE *f = fopen(log_fn,"r");
if(!f) {
fprintf(stderr, "log file is damaged\n");
reset_rtpprintf();
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "log file reopened: %s\n", log_fn);
return;
} else {
fclose(f);
}
}
if(simple_log)
return;
log_lock();
if(_rtpfile && log_fn[0] && (_rtpfile != stdout)) {
char logf[FILE_STR_LEN];
set_log_file_name(log_fn_base,logf);
if(strcmp(log_fn,logf)) {
fclose(_rtpfile);
log_fn[0]=0;
_rtpfile = fopen(logf, "w");
if(_rtpfile) {
STRCPY(log_fn,logf);
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "log file opened: %s\n", log_fn);
} else {
_rtpfile = stdout;
}
}
}
log_unlock();
}
static int get_syslog_level(TURN_LOG_LEVEL level)
{
switch(level) {
case TURN_LOG_LEVEL_CONTROL:
return LOG_NOTICE;
case TURN_LOG_LEVEL_WARNING:
return LOG_WARNING;
case TURN_LOG_LEVEL_ERROR:
return LOG_ERR;
default:
;
};
return LOG_INFO;
}
int vrtpprintf(TURN_LOG_LEVEL level, const char *format, va_list args)
{
/* Fix for Issue 24, raised by John Selbie: */
#define MAX_RTPPRINTF_BUFFER_SIZE (1024)
char s[MAX_RTPPRINTF_BUFFER_SIZE+1];
#undef MAX_RTPPRINTF_BUFFER_SIZE
size_t sz;
snprintf(s, sizeof(s), "%lu: ",(unsigned long)log_time());
sz=strlen(s);
vsnprintf(s+sz, sizeof(s)-1-sz, format, args);
s[sizeof(s)-1]=0;
if(to_syslog) {
syslog(get_syslog_level(level),"%s",s);
} else {
log_lock();
set_rtpfile();
if(fprintf(_rtpfile,"%s",s)<0) {
reset_rtpprintf();
} else if(fflush(_rtpfile)<0) {
reset_rtpprintf();
}
log_unlock();
}
return 0;
}
void rtpprintf(const char *format, ...)
{
va_list args;
va_start (args, format);
vrtpprintf(TURN_LOG_LEVEL_INFO, format, args);
va_end (args);
}
///////////// ORIGIN ///////////////////
int get_default_protocol_port(const char* scheme, size_t slen)
{
if(scheme && (slen>0)) {
switch(slen) {
case 3:
if(!memcmp("ftp",scheme,3))
return 21;
if(!memcmp("svn",scheme,3))
return 3690;
if(!memcmp("ssh",scheme,4))
return 22;
if(!memcmp("sip",scheme,3))
return 5060;
break;
case 4:
if(!memcmp("http",scheme,4))
return 80;
if(!memcmp("ldap",scheme,4))
return 389;
if(!memcmp("sips",scheme,4))
return 5061;
if(!memcmp("turn",scheme,4))
return 3478;
if(!memcmp("stun",scheme,4))
return 3478;
break;
case 5:
if(!memcmp("https",scheme,5))
return 443;
if(!memcmp("ldaps",scheme,5))
return 636;
if(!memcmp("turns",scheme,5))
return 5349;
if(!memcmp("stuns",scheme,5))
return 5349;
break;
case 6:
if(!memcmp("telnet",scheme,6))
return 23;
if(!memcmp("radius",scheme,6))
return 1645;
break;
case 7:
if(!memcmp("svn+ssh",scheme,7))
return 22;
break;
default:
return 0;
};
}
return 0;
}
int get_canonic_origin(const char* o, char *co, int sz)
{
int ret = -1;
if(o && o[0] && co) {
co[0]=0;
struct evhttp_uri *uri = evhttp_uri_parse(o);
if(uri) {
const char *scheme = evhttp_uri_get_scheme(uri);
if(scheme && scheme[0]) {
size_t schlen = strlen(scheme);
if((schlen<(size_t)sz) && (schlen<STUN_MAX_ORIGIN_SIZE)) {
const char *host = evhttp_uri_get_host(uri);
if(host && host[0]) {
char otmp[STUN_MAX_ORIGIN_SIZE+STUN_MAX_ORIGIN_SIZE];
ns_bcopy(scheme,otmp,schlen);
otmp[schlen]=0;
{
unsigned char *s = (unsigned char*)otmp;
while(*s) {
*s = (unsigned char)tolower((int)*s);
++s;
}
}
int port = evhttp_uri_get_port(uri);
if(port<1) {
port = get_default_protocol_port(otmp, schlen);
}
if(port>0)
snprintf(otmp+schlen,sizeof(otmp)-schlen-1,"://%s:%d",host,port);
else
snprintf(otmp+schlen,sizeof(otmp)-schlen-1,"://%s",host);
{
unsigned char *s = (unsigned char*)otmp + schlen + 3;
while(*s) {
*s = (unsigned char)tolower((int)*s);
++s;
}
}
strncpy(co,otmp,sz);
co[sz]=0;
ret = 0;
}
}
}
evhttp_uri_free(uri);
}
}
if(ret<0) {
strncpy(co,o,sz);
co[sz]=0;
}
return ret;
}
//////////////////////////////////////////////////////////////////
#ifdef __cplusplus
#if defined(TURN_MEMORY_DEBUG)
#include <map>
#include <set>
#include <string>
static volatile int tmm_init = 0;
static pthread_mutex_t tm;
typedef void* ptrtype;
typedef std::set<ptrtype> ptrs_t;
typedef std::map<std::string,ptrs_t> str_to_ptrs_t;
typedef std::map<ptrtype,std::string> ptr_to_str_t;
static str_to_ptrs_t str_to_ptrs;
static ptr_to_str_t ptr_to_str;
static void tm_init(void) {
if(!tmm_init) {
pthread_mutex_init(&tm,NULL);
tmm_init = 1;
}
}
static void add_tm_ptr(void *ptr, const char *id) {
UNUSED_ARG(ptr);
UNUSED_ARG(id);
if(!ptr)
return;
std::string sid(id);
str_to_ptrs_t::iterator iter;
pthread_mutex_lock(&tm);
iter = str_to_ptrs.find(sid);
if(iter == str_to_ptrs.end()) {
std::set<ptrtype> sp;
sp.insert(ptr);
str_to_ptrs[sid]=sp;
} else {
iter->second.insert(ptr);
}
ptr_to_str[ptr]=sid;
pthread_mutex_unlock(&tm);
}
static void del_tm_ptr(void *ptr, const char *id) {
UNUSED_ARG(ptr);
UNUSED_ARG(id);
if(!ptr)
return;
pthread_mutex_lock(&tm);
ptr_to_str_t::iterator pts_iter = ptr_to_str.find(ptr);
if(pts_iter == ptr_to_str.end()) {
printf("Tring to free unknown pointer (1): %s\n",id);
} else {
std::string sid = pts_iter->second;
ptr_to_str.erase(pts_iter);
str_to_ptrs_t::iterator iter = str_to_ptrs.find(sid);
if(iter == str_to_ptrs.end()) {
printf("Tring to free unknown pointer (2): %s\n",id);
} else {
iter->second.erase(ptr);
}
}
pthread_mutex_unlock(&tm);
}
static void tm_id(char *id, const char* function, int line) {
sprintf(id,"%s:%d",function,line);
}
#define TM_START() char id[128];tm_id(id,function,line);tm_init()
extern "C" void* debug_ptr_add_func(void *ptr, const char* function, int line) {
TM_START();
add_tm_ptr(ptr,id);
return ptr;
}
extern "C" void debug_ptr_del_func(void *ptr, const char* function, int line) {
TM_START();
del_tm_ptr(ptr,id);
}
extern "C" void tm_print_func(void);
void tm_print_func(void) {
pthread_mutex_lock(&tm);
printf("=============================================\n");
for(str_to_ptrs_t::const_iterator iter=str_to_ptrs.begin();iter != str_to_ptrs.end();++iter) {
if(iter->second.size())
printf("%s: %s: %d\n",__FUNCTION__,iter->first.c_str(),(int)(iter->second.size()));
}
printf("=============================================\n");
pthread_mutex_unlock(&tm);
}
extern "C" void *turn_malloc_func(size_t sz, const char* function, int line);
void *turn_malloc_func(size_t sz, const char* function, int line) {
TM_START();
void *ptr = malloc(sz);
add_tm_ptr(ptr,id);
return ptr;
}
extern "C" void *turn_realloc_func(void *ptr, size_t old_sz, size_t new_sz, const char* function, int line);
void *turn_realloc_func(void *ptr, size_t old_sz, size_t new_sz, const char* function, int line) {
UNUSED_ARG(old_sz);
TM_START();
if(ptr)
del_tm_ptr(ptr,id);
ptr = realloc(ptr,new_sz);
add_tm_ptr(ptr,id);
return ptr;
}
extern "C" void turn_free_func(void *ptr, size_t sz, const char* function, int line);
void turn_free_func(void *ptr, size_t sz, const char* function, int line) {
UNUSED_ARG(sz);
TM_START();
del_tm_ptr(ptr,id);
free(ptr);
}
extern "C" void turn_free_simple(void *ptr);
void turn_free_simple(void *ptr) {
tm_init();
del_tm_ptr(ptr,__FUNCTION__);
free(ptr);
}
extern "C" void *turn_calloc_func(size_t number, size_t size, const char* function, int line);
void *turn_calloc_func(size_t number, size_t size, const char* function, int line) {
TM_START();
void *ptr = calloc(number,size);
add_tm_ptr(ptr,id);
return ptr;
}
extern "C" char *turn_strdup_func(const char* s, const char* function, int line);
char *turn_strdup_func(const char* s, const char* function, int line) {
TM_START();
char *ptr = strdup(s);
add_tm_ptr(ptr,id);
return ptr;
}
#endif
#endif
//////////////////////////////////////////////////////////////////