turnserver-4.5.1.1/0000775000175000017500000000000013436577655012612 5ustar misimisiturnserver-4.5.1.1/Dockerfile.build0000664000175000017500000000033713436577655015705 0ustar misimisiFROM ubuntu:16.04 RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ emacs-nox \ build-essential \ libssl-dev sqlite3 \ libsqlite3-dev \ libevent-dev \ g++ \ libboost-dev turnserver-4.5.1.1/turndb/0000775000175000017500000000000013436577655014110 5ustar misimisiturnserver-4.5.1.1/turndb/schema.userdb.redis0000664000175000017500000001405313436577655017666 0ustar misimisi I. The database Redis database for user authentication and peer permissions has the following schema: 1) For the long-term credentials there must be keys "turn/realm//user//key" and the values must be the the hmackeys. For example, for the user "gorst", realm "north.gov" and password "hero", there must be key "turn/realm/north.gov/user/gorst/key" with value "7da2270ccfa49786e0115366d3a3d14d". 2) For the shared secrets (REST API), several key/value pairs may be used (same as in SQL schema). The secrets are stored as members of an unordered set. The name of the set will be "turn/realm//secret" and the value(s) will be the secret(s). For example, if we have secrets "hero1", "hero2" and "hero3", then we will have set "turn/realm/north.gov/secret" with values "hero1", "hero2" and "hero3". The turnserver will try to use the secrets in arbitrary order. 3) The "white" and "black" peer IP ranges are stored as unordered sets of the following names: "turn/realm//allowed-peer-ip" and "turn/realm//denied-peer-ip". The meaning of the keys is the same as the meaning of allowed-peer-ip and denied-peer-ip turnserver command-line option (with the addition of the realm option). The only difference is that the turnserver option values are "static" (they remain the same for the lifetime of the turnserver process) but the database records can be dynamically changed and they will be almost immediately "seen" by the turnserver process. 4) For the oAuth authentication, there is a hash structure with the key "turn/oauth/kid/". The kid structure fields are: ikm_key - (optional) base64-encoded key ("input keying material"). timestamp - (optional) the timestamp (in seconds) when the key lifetime started. lifetime - (optional) the key lifetime in seconds; the default value is 0 - unlimited lifetime. as_rs_alg - oAuth token encryption algorithm; the valid values are "A256GCM", "A128GCM" (see http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-5.1). The default value is "A256GCM". realm - optionally, a kid can be assigned to a realm that is different from the default realm. 5) admin users (over https interface) are maintained as keys of form: "turn/admin_user/ with hash members "password" and, optionally, "realm". II. Extra realms data in the database We can use more than one realm with the same instance of the TURN server. This is done in two ways: 1) through the third-party authentication option. An oAuth kid can be optionally assigned to a realm. When the user provides kid, and the database record for that kid contains a non-empty non-default realm, then the user is treated as belonging to that realm. 2) the ORIGIN mechanism - users with different ORIGINS are placed into different realms. The database includes information about the relationships between the ORIGIN and realms, and about the extra realms database numbers. The relationship between ORIGIN and realm is set as keys of form: "turn/origin/" with the realm-names as the value. Many different ORIGIN keys may have the same realm. If the ORIGIN value is not found in the database or the ORIGIN field is missed in the initial allocate request, then the default realm is assumed. III) Example of a Redis default user database setup. This example sets user database for: * long-term credentials with hashed passwords and with default realm "north.gov"; * long-term credentials with open passwords and with default realm "north.gov"; * TURN REST API with shared secrets "logen", etc; * Black and white IP peer lists used. * Information how to match ORIGIN field with extra realms (if used). If no origin match found or the ORIGIN field is absent in the ALLOCATE request then the default realm is used. * The realm performance parameters: "max_bps", "total_quota" and "user_quota" (same names as the turnserver configuration options, with the same meanings). * The oAuth data for the key with kid "oldempire" and key value "12345678901234567890123456789012", and default realm. * The admin user 'skarling', realm 'north.gov', with password 'hoodless'; * The global admin user 'bayaz' with password 'magi'; The shell command would be: $ redis-cli </allocation//status", and the values may be "new lifetime=..." or "refreshed lifetime=...". There may be multiple allocations under the same user name. 2) Additionally, the same information is reported through the "publish" mechanism. The same keys are used and the same status is reported, with the addition of status "deleted". The user session that is interested in those statuses, must subscribe to the events as: psubscribe turn/realm/*/user/*/allocation/*/status 3) Allocation traffic information is reported through the "publish" mechanism. The keys are "turn/user//allocation//traffic". The application that is interested in the traffic information must subscribe to the events as: psubscribe turn/realm/*/user/*/allocation/*/traffic Total traffic information is also reported when the allocation is deleted. The keys are "turn/user//allocation//total_traffic". Applications interested in the total amount of traffic per allocation can subscribe to these events as: psubscribe turn/realm/*/user/*/allocation/*/total_traffic Or, to receive all allocation events: psubscribe turn/realm/*/user/*/allocation/* 4) Redis database configuration parameters TURN Server connects to the Redis and keeps the same connection during the TURN server lifetime. That means that we have to take care about that connection - it must NOT expire. You have to take care about Redis connection parameters, the timeout and the keepalive. The following settings must be in your Redis config file (/etc/redis.conf or /usr/local/etc/redis.conf): .......... timeout 0 .......... tcp-keepalive 60 .......... turnserver-4.5.1.1/turndb/schema.sql0000664000175000017500000000203213436577655016066 0ustar misimisi CREATE TABLE turnusers_lt ( realm varchar(127) default '', name varchar(512), hmackey char(128), PRIMARY KEY (realm,name) ); CREATE TABLE turn_secret ( realm varchar(127) default '', value varchar(256), primary key (realm,value) ); CREATE TABLE allowed_peer_ip ( realm varchar(127) default '', ip_range varchar(256), primary key (realm,ip_range) ); CREATE TABLE denied_peer_ip ( realm varchar(127) default '', ip_range varchar(256), primary key (realm,ip_range) ); CREATE TABLE turn_origin_to_realm ( origin varchar(127), realm varchar(127), primary key (origin) ); CREATE TABLE turn_realm_option ( realm varchar(127) default '', opt varchar(32), value varchar(128), primary key (realm,opt) ); CREATE TABLE oauth_key ( kid varchar(128), ikm_key varchar(256), timestamp bigint default 0, lifetime integer default 0, as_rs_alg varchar(64) default '', realm varchar(127), primary key (kid) ); CREATE TABLE admin_user ( name varchar(32), realm varchar(127), password varchar(127), primary key (name) ); turnserver-4.5.1.1/turndb/testredisdbsetup.sh0000775000175000017500000000364613436577655020055 0ustar misimisi#!/bin/sh # ninefingers:password: youhavetoberealistic # gorst:password: hero # whirrun:password: sword # stranger-come-knocking:password: civilization # # bayaz admin user password: magi # skarling admin user password: hoodless redis-cli <h=kh_init(3); if(map->h) { map->magic=MAGIC_HASH; TURN_MUTEX_INIT_RECURSIVE(&(map->mutex)); return 0; } } return -1; } #define ur_map_valid(map) ((map) && ((map)->h) && ((map)->magic==MAGIC_HASH)) ur_map* ur_map_create() { ur_map *map=(ur_map*)turn_malloc(sizeof(ur_map)); if(ur_map_init(map)<0) { turn_free(map,sizeof(ur_map)); return NULL; } return map; } /** * @ret: * 0 - success * -1 - error */ int ur_map_put(ur_map* map, ur_map_key_type key, ur_map_value_type value) { if(!ur_map_valid(map)) return -1; else { int ret=0; khiter_t k; k = kh_get(3, map->h, key); if(k != kh_end(map->h)) { kh_del(3, map->h, k); } k = kh_put(3,map->h,key,&ret); if (!ret) { kh_del(3, map->h, k); return -1; } kh_value(map->h, k) = value; return 0; } } /** * @ret: * 1 - success * 0 - not found */ int ur_map_get(const ur_map* map, ur_map_key_type key, ur_map_value_type *value) { if(!ur_map_valid(map)) return 0; else { khiter_t k; k = kh_get(3, map->h, key); if((k != kh_end(map->h)) && kh_exist(map->h,k)) { if(value) *value=kh_value(map->h,k); return 1; } return 0; } } /** * @ret: * 1 - success * 0 - not found */ int ur_map_del(ur_map* map, ur_map_key_type key,ur_map_del_func delfunc) { if(!ur_map_valid(map)) return 0; else { khiter_t k; k = kh_get(3, map->h, key); if((k != kh_end(map->h)) && kh_exist(map->h,k)) { if(delfunc) { delfunc(kh_value(map->h,k)); } kh_del(3,map->h,k); return 1; } return 0; } } /** * @ret: * 1 - success * 0 - not found */ int ur_map_exist(const ur_map* map, ur_map_key_type key) { if(!ur_map_valid(map)) return 0; else { khiter_t k; k = kh_get(3, map->h, key); if((k != kh_end(map->h)) && kh_exist(map->h,k)) { return 1; } return 0; } } void ur_map_free(ur_map** map) { if(map && ur_map_valid(*map)) { { static int khctest=0; if(khctest) kh_clear(3,(*map)->h); } kh_destroy(3,(*map)->h); (*map)->h=NULL; (*map)->magic=0; TURN_MUTEX_DESTROY(&((*map)->mutex)); turn_free(*map,sizeof(ur_map)); *map=NULL; } } size_t ur_map_size(const ur_map* map) { if(ur_map_valid(map)) { return kh_size(map->h); } else { return 0; } } int ur_map_foreach(ur_map* map, foreachcb_type func) { if(map && func && ur_map_valid(map)) { khiter_t k; for (k = kh_begin((*map)->h); k != kh_end(map->h); ++k) { if (kh_exist(map->h, k)) { if(func((ur_map_key_type)(kh_key(map->h, k)), (ur_map_value_type)(kh_value(map->h, k)))) { return 1; } } } } return 0; } int ur_map_foreach_arg(ur_map* map, foreachcb_arg_type func, void* arg) { if(map && func && ur_map_valid(map)) { khiter_t k; for (k = kh_begin((*map)->h); k != kh_end(map->h); ++k) { if (kh_exist(map->h, k)) { if(func((ur_map_key_type)(kh_key(map->h, k)), (ur_map_value_type)(kh_value(map->h, k)), arg) ) { return 1; } } } } return 0; } int ur_map_lock(const ur_map* map) { if(ur_map_valid(map)) { TURN_MUTEX_LOCK((const turn_mutex*)&(map->mutex)); return 0; } return -1; } int ur_map_unlock(const ur_map* map) { if(ur_map_valid(map)) { TURN_MUTEX_UNLOCK((const turn_mutex*)&(map->mutex)); return 0; } return -1; } //////////////////// LOCAL MAPS //////////////////////////////////// void lm_map_init(lm_map *map) { if(map) { ns_bzero(map,sizeof(lm_map)); } } /** * @ret: * 0 - success * -1 - error */ int lm_map_put(lm_map* map, ur_map_key_type key, ur_map_value_type value) { int ret = -1; if(map && key && value) { size_t index = (size_t)(key & (LM_MAP_HASH_SIZE - 1)); lm_map_array *a = &(map->table[index]); size_t i; for(i=0;imain_keys[i]; ur_map_value_type value0 = a->main_values[i]; if(key0 == key) { if(value0 == value) { return 0; } else { return -1; } } if(!key0 || !value0) { a->main_keys[i] = key; a->main_values[i] = value; return 0; } } size_t esz = a->extra_sz; if(esz && a->extra_keys && a->extra_values) { for(i=0;iextra_keys[i]; ur_map_value_type *valuep = a->extra_values[i]; if(keyp && valuep) { if(!(*keyp) || !(*valuep)) { *keyp = key; *valuep = value; return 0; } } else { if(!(*keyp)) { a->extra_keys[i] = (ur_map_key_type*)turn_malloc(sizeof(ur_map_key_type)); keyp = a->extra_keys[i]; } if(!(*valuep)) { a->extra_values[i] = (ur_map_value_type*)turn_malloc(sizeof(ur_map_value_type)); valuep = a->extra_values[i]; } *keyp = key; *valuep = value; return 0; } } } size_t old_sz = esz; size_t old_sz_mem = esz * sizeof(ur_map_key_type*); a->extra_keys = (ur_map_key_type**)turn_realloc(a->extra_keys,old_sz_mem,old_sz_mem + sizeof(ur_map_key_type*)); a->extra_keys[old_sz] = (ur_map_key_type*)turn_malloc(sizeof(ur_map_key_type)); *(a->extra_keys[old_sz]) = key; old_sz_mem = esz * sizeof(ur_map_value_type*); a->extra_values = (ur_map_value_type**)turn_realloc(a->extra_values,old_sz_mem,old_sz_mem + sizeof(ur_map_value_type*)); a->extra_values[old_sz] = (ur_map_value_type*)turn_malloc(sizeof(ur_map_value_type)); *(a->extra_values[old_sz]) = value; a->extra_sz += 1; return 0; } return ret; } /** * @ret: * 1 - success * 0 - not found */ int lm_map_get(const lm_map* map, ur_map_key_type key, ur_map_value_type *value) { int ret = 0; if(map && key) { size_t index = (size_t)(key & (LM_MAP_HASH_SIZE - 1)); const lm_map_array *a = &(map->table[index]); size_t i; for(i=0;imain_keys[i]; if((key0 == key) && a->main_values[i]) { if(value) { *value = a->main_values[i]; } return 1; } } size_t esz = a->extra_sz; if(esz && a->extra_keys && a->extra_values) { for(i=0;iextra_keys[i]; ur_map_value_type *valuep = a->extra_values[i]; if(keyp && valuep) { if(*keyp == key) { if(value) *value = *valuep; return 1; } } } } } return ret; } /** * @ret: * 1 - success * 0 - not found */ int lm_map_del(lm_map* map, ur_map_key_type key,ur_map_del_func delfunc) { int ret = 0; if(map && key) { size_t index = (size_t)(key & (LM_MAP_HASH_SIZE - 1)); lm_map_array *a = &(map->table[index]); size_t i; for(i=0;imain_keys[i]; if((key0 == key) && a->main_values[i]) { if(delfunc) { delfunc(a->main_values[i]); } a->main_keys[i] = 0; a->main_values[i] = 0; return 1; } } size_t esz = a->extra_sz; if(esz && a->extra_keys && a->extra_values) { for(i=0;iextra_keys[i]; ur_map_value_type *valuep = a->extra_values[i]; if(keyp && valuep) { if(*keyp == key) { if(delfunc) delfunc(*valuep); *keyp = 0; *valuep = 0; return 1; } } } } } return ret; } /** * @ret: * 1 - success * 0 - not found */ int lm_map_exist(const lm_map* map, ur_map_key_type key) { return lm_map_get(map, key, NULL); } void lm_map_clean(lm_map* map) { size_t j; for(j=0;jtable[j]); size_t esz = a->extra_sz; if(esz) { size_t i; if(a->extra_keys) { for(i=0;iextra_keys[i]; if(keyp) { *keyp = 0; turn_free(keyp,sizeof(ur_map_key_type)); } } turn_free(a->extra_keys,esz * sizeof(ur_map_key_type)); a->extra_keys = NULL; } if(a->extra_values) { for(i=0;iextra_values[i]; if(valuep) { *valuep = 0; turn_free(valuep,sizeof(ur_map_value_type)); } } turn_free(a->extra_values,esz * sizeof(ur_map_value_type)); a->extra_values = NULL; } } } lm_map_init(map); } size_t lm_map_size(const lm_map* map) { size_t ret = 0; if(map) { size_t i; for(i=0;itable[i]); size_t j; for(j=0;jmain_keys[j] && a->main_values[j]) { ++ret; } } size_t esz = a->extra_sz; if(esz && a->extra_values && a->extra_keys) { for(j=0;jextra_keys[j]) && *(a->extra_values[j])) { ++ret; } } } } } return ret; } int lm_map_foreach(lm_map* map, foreachcb_type func) { size_t ret = 0; if(map) { size_t i; for(i=0;itable[i]); size_t j; for(j=0;jmain_keys[j] && a->main_values[j]) { if(func((ur_map_key_type)a->main_keys[j], (ur_map_value_type)a->main_values[j])) { return 1; } } } size_t esz = a->extra_sz; if(esz && a->extra_values && a->extra_keys) { for(j=0;jextra_keys[j]) && *(a->extra_values[j])) { if(func((ur_map_key_type)*(a->extra_keys[j]), (ur_map_value_type)*(a->extra_values[j]))) { return 1; } } } } } } return ret; } int lm_map_foreach_arg(lm_map* map, foreachcb_arg_type func, void* arg) { size_t ret = 0; if(map) { size_t i; for(i=0;itable[i]); size_t j; for(j=0;jmain_keys[j] && a->main_values[j]) { if(func((ur_map_key_type)a->main_keys[j], (ur_map_value_type)a->main_values[j], arg)) { return 1; } } } size_t esz = a->extra_sz; if(esz && a->extra_values && a->extra_keys) { for(j=0;jextra_keys[j]) && *(a->extra_values[j])) { if(func((ur_map_key_type)*(a->extra_keys[j]), (ur_map_value_type)*(a->extra_values[j]), arg)) { return 1; } } } } } } return ret; } //////////////////// ADDR LISTS /////////////////////////////////// static void addr_list_free(addr_list_header* slh) { if(slh) { if(slh->extra_list) { turn_free(slh->extra_list,sizeof(addr_elem)*(slh->extra_sz)); } ns_bzero(slh,sizeof(addr_list_header)); } } static void addr_list_add(addr_list_header* slh, const ioa_addr* key, ur_addr_map_value_type value) { if(!key || !value) return; addr_elem *elem = NULL; size_t i; for(i=0;imain_list[i].value)) { elem = &(slh->main_list[i]); break; } } if(!elem && slh->extra_list) { for(i=0;iextra_sz;++i) { if(!(slh->extra_list[i].value)) { elem = &(slh->extra_list[i]); break; } } } if(!elem) { size_t old_sz = slh->extra_sz; size_t old_sz_mem = old_sz * sizeof(addr_elem); slh->extra_list = (addr_elem*)turn_realloc(slh->extra_list, old_sz_mem, old_sz_mem + sizeof(addr_elem)); elem = &(slh->extra_list[old_sz]); slh->extra_sz += 1; } addr_cpy(&(elem->key),key); elem->value=value; } static void addr_list_remove(addr_list_header* slh, const ioa_addr* key, ur_addr_map_func delfunc, int *counter) { if(!slh || !key) return; if(counter) *counter = 0; size_t i; for(i=0;imain_list[i]); if(elem->value) { if(addr_eq(&(elem->key),key)) { if(delfunc && elem->value) delfunc(elem->value); elem->value = 0; if(counter) { *counter += 1; } } } } if(slh->extra_list) { for(i=0;iextra_sz;++i) { addr_elem *elem=&(slh->extra_list[i]); if(elem->value) { if(addr_eq(&(elem->key),key)) { if(delfunc && elem->value) delfunc(elem->value); elem->value = 0; if(counter) { *counter += 1; } } } } } } static void addr_list_foreach(addr_list_header* slh, ur_addr_map_func func) { if(slh && func) { size_t i; for(i=0;imain_list[i]); if(elem->value) { func(elem->value); } } if(slh->extra_list) { for(i=0;iextra_sz;++i) { addr_elem *elem=&(slh->extra_list[i]); if(elem->value) { func(elem->value); } } } } } static size_t addr_list_num_elements(const addr_list_header* slh) { size_t ret = 0; if (slh) { size_t i; for (i = 0; i < ADDR_ARRAY_SIZE; ++i) { const addr_elem *elem = &(slh->main_list[i]); if (elem->value) { ++ret; } } if (slh->extra_list) { for (i = 0; i < slh->extra_sz; ++i) { addr_elem *elem = &(slh->extra_list[i]); if (elem->value) { ++ret; } } } } return ret; } static size_t addr_list_size(const addr_list_header* slh) { size_t ret = 0; if (slh) { ret += ADDR_ARRAY_SIZE; ret += slh->extra_sz; } return ret; } static addr_elem* addr_list_get(addr_list_header* slh, const ioa_addr* key) { if(!slh || !key) return NULL; size_t i; for(i=0;imain_list[i]); if(elem->value) { if(addr_eq(&(elem->key),key)) { return elem; } } } if(slh->extra_list) { for(i=0;iextra_sz;++i) { addr_elem *elem=&(slh->extra_list[i]); if(elem->value) { if(addr_eq(&(elem->key),key)) { return elem; } } } } return NULL; } static const addr_elem* addr_list_get_const(const addr_list_header* slh, const ioa_addr* key) { if(!slh || !key) return NULL; size_t i; for(i=0;imain_list[i]); if(elem->value) { if(addr_eq(&(elem->key),key)) { return elem; } } } if(slh->extra_list) { for(i=0;iextra_sz;++i) { const addr_elem *elem=&(slh->extra_list[i]); if(elem->value) { if(addr_eq(&(elem->key),key)) { return elem; } } } } return NULL; } ////////// ADDR MAPS //////////////////////////////////////////// #define addr_map_index(key) (addr_hash((key)) & (ADDR_MAP_SIZE - 1)) #define get_addr_list_header(map, key) (&((map)->lists[addr_map_index((key))])) #define ur_addr_map_valid(map) ((map) && ((map)->magic==MAGIC_HASH)) void ur_addr_map_init(ur_addr_map* map) { if(map) { ns_bzero(map,sizeof(ur_addr_map)); map->magic=MAGIC_HASH; } } void ur_addr_map_clean(ur_addr_map* map) { if(map && ur_addr_map_valid(map)) { u32bits i=0; for(i=0;ilists[i])); } ns_bzero(map,sizeof(ur_addr_map)); } } /** * @ret: * 0 - success * -1 - error * if the addr key exists, the value is updated. */ int ur_addr_map_put(ur_addr_map* map, ioa_addr* key, ur_addr_map_value_type value) { if(!ur_addr_map_valid(map)) return -1; else { addr_list_header* slh = get_addr_list_header(map, key); addr_elem* elem = addr_list_get(slh, key); if(elem) { elem->value=value; } else { addr_list_add(slh,key,value); } return 0; } } /** * @ret: * 1 - success * 0 - not found */ int ur_addr_map_get(const ur_addr_map* map, ioa_addr* key, ur_addr_map_value_type *value) { if(!ur_addr_map_valid(map)) return 0; else { const addr_list_header* slh = get_addr_list_header(map, key); const addr_elem *elem = addr_list_get_const(slh, key); if(elem) { if(value) *value=elem->value; return 1; } return 0; } } /** * @ret: * 1 - success * 0 - not found */ int ur_addr_map_del(ur_addr_map* map, ioa_addr* key,ur_addr_map_func delfunc) { if(!ur_addr_map_valid(map)) return 0; else { addr_list_header* slh = get_addr_list_header(map, key); int counter=0; addr_list_remove(slh, key, delfunc, &counter); return (counter>0); } } /** * @ret: * 1 - success * 0 - not found */ void ur_addr_map_foreach(ur_addr_map* map, ur_addr_map_func func) { if(ur_addr_map_valid(map)) { u32bits i=0; for(i=0;ilists[i]); addr_list_foreach(slh, func); } } } size_t ur_addr_map_num_elements(const ur_addr_map* map) { size_t ret = 0; if (ur_addr_map_valid(map)) { u32bits i = 0; for (i = 0; i < ADDR_MAP_SIZE; i++) { const addr_list_header* slh = &(map->lists[i]); ret += addr_list_num_elements(slh); } } return ret; } size_t ur_addr_map_size(const ur_addr_map* map) { size_t ret = 0; if (ur_addr_map_valid(map)) { u32bits i = 0; for (i = 0; i < ADDR_MAP_SIZE; i++) { const addr_list_header* slh = &(map->lists[i]); ret += addr_list_size(slh); } } return ret; } //////////////////// STRING LISTS /////////////////////////////////// typedef struct _string_list { struct _string_list* next; } string_list; typedef struct _string_elem { string_list list; ur_string_map_key_type key; u32bits key_size; ur_string_map_value_type value; } string_elem; typedef struct _string_list_header { string_list *list; } string_list_header; static size_t string_list_size(const string_list *sl) { if(!sl) return 0; return 1+string_list_size(sl->next); } static void string_list_free(string_list_header* slh, ur_string_map_func del_value_func) { if(slh) { string_list* list=slh->list; while(list) { string_elem *elem=(string_elem*)list; string_list* tail=elem->list.next; if(elem->key) turn_free(elem->key,elem->key_size); if(del_value_func && elem->value) del_value_func(elem->value); turn_free(elem,sizeof(string_elem)); list=tail; } slh->list=NULL; } } static string_list* string_list_add(string_list* sl, const ur_string_map_key_type key, ur_string_map_value_type value) { if(!key) return sl; string_elem *elem=(string_elem*)turn_malloc(sizeof(string_elem)); elem->list.next=sl; elem->key_size = strlen(key)+1; elem->key=(s08bits*)turn_malloc(elem->key_size); ns_bcopy(key,elem->key,elem->key_size); elem->value=value; return &(elem->list); } static string_list* string_list_remove(string_list* sl, const ur_string_map_key_type key, ur_string_map_func del_value_func, int *counter) { if(!sl || !key) return sl; string_elem *elem=(string_elem*)sl; string_list* tail=elem->list.next; if(strcmp(elem->key,key)==0) { turn_free(elem->key,elem->key_size); if(del_value_func) del_value_func(elem->value); turn_free(elem,sizeof(string_elem)); if(counter) *counter+=1; sl=string_list_remove(tail, key, del_value_func, counter); } else { elem->list.next=string_list_remove(tail,key,del_value_func,counter); } return sl; } static string_elem* string_list_get(string_list* sl, const ur_string_map_key_type key) { if(!sl || !key) return NULL; string_elem *elem=(string_elem*)sl; if(strcmp(elem->key,key)==0) { return elem; } else { return string_list_get(elem->list.next, key); } } ////////// STRING MAPS //////////////////////////////////////////// #define STRING_MAP_SIZE (1024) struct _ur_string_map { string_list_header lists[STRING_MAP_SIZE]; u64bits magic; ur_string_map_func del_value_func; TURN_MUTEX_DECLARE(mutex) }; static u32bits string_hash(const ur_string_map_key_type key) { u08bits *str=(u08bits*)key; u32bits hash = 0; int c = 0; while ((c = *str++)) hash = c + (hash << 6) + (hash << 16) - hash; return hash; } static int string_map_index(const ur_string_map_key_type key) { return (int)(string_hash(key) % STRING_MAP_SIZE); } static string_list_header* get_string_list_header(ur_string_map *map, const ur_string_map_key_type key) { return &(map->lists[string_map_index(key)]); } static int ur_string_map_init(ur_string_map* map) { if(map) { ns_bzero(map,sizeof(ur_string_map)); map->magic=MAGIC_HASH; TURN_MUTEX_INIT_RECURSIVE(&(map->mutex)); return 0; } return -1; } static int ur_string_map_valid(const ur_string_map *map) { return (map && map->magic==MAGIC_HASH); } ur_string_map* ur_string_map_create(ur_string_map_func del_value_func) { ur_string_map *map=(ur_string_map*)turn_malloc(sizeof(ur_string_map)); if(ur_string_map_init(map)<0) { turn_free(map,sizeof(ur_string_map)); return NULL; } map->del_value_func = del_value_func; return map; } /** * @ret: * 0 - success * -1 - error * if the string key exists, and the value is different, return error. */ int ur_string_map_put(ur_string_map* map, const ur_string_map_key_type key, ur_string_map_value_type value) { if(!ur_string_map_valid(map)) return -1; else { string_list_header* slh = get_string_list_header(map, key); string_elem *elem = string_list_get(slh->list, key); if(elem) { if(elem->value != value) { if(map->del_value_func) map->del_value_func(elem->value); elem->value = value; } return 0; } slh->list=string_list_add(slh->list,key,value); return 0; } } /** * @ret: * 1 - success * 0 - not found */ int ur_string_map_get(ur_string_map* map, const ur_string_map_key_type key, ur_string_map_value_type *value) { if(!ur_string_map_valid(map)) return 0; else { string_list_header* slh = get_string_list_header(map, key); string_elem *elem = string_list_get(slh->list, key); if(elem) { if(value) *value=elem->value; return 1; } else { return 0; } } } /** * @ret: * 1 - success * 0 - not found */ int ur_string_map_del(ur_string_map* map, const ur_string_map_key_type key) { if(!ur_string_map_valid(map)) return 0; else { string_list_header* slh = get_string_list_header(map, key); int counter=0; slh->list=string_list_remove(slh->list, key, map->del_value_func, &counter); return (counter>0); } } void ur_string_map_clean(ur_string_map* map) { if (ur_string_map_valid(map)) { int i = 0; for (i = 0; i < STRING_MAP_SIZE; i++) { string_list_free(&(map->lists[i]), map->del_value_func); } } } void ur_string_map_free(ur_string_map** map) { if(map && ur_string_map_valid(*map)) { int i=0; for(i=0;ilists[i]),(*map)->del_value_func); } (*map)->magic=0; TURN_MUTEX_DESTROY(&((*map)->mutex)); turn_free(*map,sizeof(ur_string_map)); *map=NULL; } } size_t ur_string_map_size(const ur_string_map* map) { if(ur_string_map_valid(map)) { size_t ret=0; int i=0; for(i=0;ilists[i].list); } return ret; } else { return 0; } } int ur_string_map_lock(const ur_string_map* map) { if(ur_string_map_valid(map)) { TURN_MUTEX_LOCK((const turn_mutex*)&(map->mutex)); return 0; } return -1; } int ur_string_map_unlock(const ur_string_map* map) { if(ur_string_map_valid(map)) { TURN_MUTEX_UNLOCK((const turn_mutex*)&(map->mutex)); return 0; } return -1; } //////////////////////////////////////////////////////////////// turnserver-4.5.1.1/src/server/ns_turn_maps_rtcp.c0000664000175000017500000001514113436577655020615 0ustar misimisi/* * 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_maps_rtcp.h" #include "ns_turn_utils.h" #include "ns_turn_ioaddr.h" //////////////////////////////////////////// #define MAGIC_RTCP_MAP (0x76859403) #define RTCP_TIMEOUT (300) #define MAX_TOKEN_DEL (1024) //////////////////////////////////////////// struct _rtcp_map { u32bits magic; ur_map *map; ioa_timer_handle timer_ev; TURN_MUTEX_DECLARE(mutex) }; typedef struct { ioa_socket_handle s; turn_time_t t; rtcp_token_type token; } rtcp_alloc_type; //////////////////////////////////////////// static int rtcp_map_valid(const rtcp_map *map) { return (map && (map->magic==MAGIC_RTCP_MAP) && map->map); } typedef struct { rtcp_token_type tokens[MAX_TOKEN_DEL]; int tn; turn_time_t t; } timeout_check_arg_type; static int timeout_check(ur_map_key_type key, ur_map_value_type value, void *arg) { if(value && arg) { timeout_check_arg_type *tcat=(timeout_check_arg_type*)arg; rtcp_alloc_type* rat=(rtcp_alloc_type*)value; if(turn_time_before(rat->t, tcat->t) && (tcat->tntokens[(tcat->tn)++]=key; } } return 0; } static void rtcp_alloc_free(ur_map_value_type value) { rtcp_alloc_type *at = (rtcp_alloc_type *)value; if (at) { IOA_CLOSE_SOCKET(at->s); turn_free(at,sizeof(rtcp_alloc_type)); } } static void rtcp_alloc_free_savefd(ur_map_value_type value) { rtcp_alloc_type *at = (rtcp_alloc_type *) value; if (at) { turn_free(at,sizeof(rtcp_alloc_type)); } } static int foreachcb_free(ur_map_key_type key, ur_map_value_type value) { UNUSED_ARG(key); if(value) { rtcp_alloc_free(value); } return 0; } /** * @ret: * 1 - success * 0 - not found */ static int rtcp_map_del(rtcp_map* map, rtcp_token_type token) { if(!rtcp_map_valid(map)) return 0; else { TURN_MUTEX_LOCK(&map->mutex); int ret = ur_map_del(map->map,token,rtcp_alloc_free); TURN_MUTEX_UNLOCK(&map->mutex); return ret; } } static int rtcp_map_del_savefd(rtcp_map* map, rtcp_token_type token) { if(!rtcp_map_valid(map)) return 0; else { int ret = ur_map_del(map->map,token,rtcp_alloc_free_savefd); return ret; } } static void rtcp_map_timeout_handler(ioa_engine_handle e, void* arg) { UNUSED_ARG(e); if(!arg) return; rtcp_map* map=(rtcp_map*)arg; if(rtcp_map_valid(map)) { TURN_MUTEX_LOCK(&map->mutex); timeout_check_arg_type tcat; tcat.tn=0; tcat.t=turn_time(); ur_map_foreach_arg(map->map, timeout_check, &tcat); TURN_MUTEX_UNLOCK(&map->mutex); int i=0; for(i=0;imagic != MAGIC_RTCP_MAP) { map->magic = MAGIC_RTCP_MAP; map->map = ur_map_create(); if (e) map->timer_ev = set_ioa_timer(e, 3, 0, rtcp_map_timeout_handler, map, 1, "rtcp_map_timeout_handler"); TURN_MUTEX_INIT(&map->mutex); if (rtcp_map_valid(map)) return 0; } } return -1; } rtcp_map* rtcp_map_create(ioa_engine_handle e) { rtcp_map *map=(rtcp_map*)turn_malloc(sizeof(rtcp_map)); ns_bzero(map,sizeof(rtcp_map)); if(rtcp_map_init(map,e)<0) { turn_free(map,sizeof(rtcp_map)); return NULL; } return map; } /** * @ret: * 0 - success * -1 - error */ int rtcp_map_put(rtcp_map* map, rtcp_token_type token, ioa_socket_handle s) { if(!rtcp_map_valid(map)) return -1; else { rtcp_alloc_type *value=(rtcp_alloc_type*)turn_malloc(sizeof(rtcp_alloc_type)); if(!value) return -1; ns_bzero(value,sizeof(rtcp_alloc_type)); value->s=s; value->t=turn_time() + RTCP_TIMEOUT; value->token=token; TURN_MUTEX_LOCK(&map->mutex); int ret = ur_map_put(map->map,token,(ur_map_value_type)value); //TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: 111.111: ret=%d, token=%llu\n",__FUNCTION__,ret,token); TURN_MUTEX_UNLOCK(&map->mutex); if(ret<0) turn_free(value,sizeof(rtcp_alloc_type)); return ret; } } /** * @ret: * >=0 - success * <0 - not found */ ioa_socket_handle rtcp_map_get(rtcp_map* map, rtcp_token_type token) { ioa_socket_handle s = NULL; if (rtcp_map_valid(map)) { ur_map_value_type value; TURN_MUTEX_LOCK(&map->mutex); int ret = ur_map_get(map->map, token, &value); if (ret) { rtcp_alloc_type* rval = (rtcp_alloc_type*) value; if (rval) { s = rval->s; rtcp_map_del_savefd(map, token); } } TURN_MUTEX_UNLOCK(&map->mutex); } return s; } void rtcp_map_free(rtcp_map** map) { if(map && rtcp_map_valid(*map)) { TURN_MUTEX_LOCK(&((*map)->mutex)); IOA_EVENT_DEL((*map)->timer_ev); ur_map_foreach((*map)->map, foreachcb_free); ur_map_free(&((*map)->map)); (*map)->magic=0; TURN_MUTEX_UNLOCK(&((*map)->mutex)); TURN_MUTEX_DESTROY(&((*map)->mutex)); turn_free(*map,sizeof(rtcp_map)); *map=NULL; } } size_t rtcp_map_size(const rtcp_map* map) { if(rtcp_map_valid(map)) { TURN_MUTEX_LOCK(&map->mutex); size_t ret = ur_map_size(map->map); TURN_MUTEX_UNLOCK(&map->mutex); return ret; } else { return 0; } } //////////////////////////////////////////////////////////////// turnserver-4.5.1.1/src/server/ns_turn_khash.h0000664000175000017500000003320713436577655017733 0ustar misimisi/* The MIT License Copyright (c) 2008, by Attractive Chaos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Changes, 2011-2012: proprietary header added, with proprietary memory management functions. */ /* An example: #include "khash.h" KHASH_MAP_INIT_INT(32, s08bits) int main() { int ret, is_missing; khiter_t k; khash_t(32) *h = kh_init(32); k = kh_put(32, h, 5, &ret); if (!ret) kh_del(32, h, k); kh_value(h, k) = 10; k = kh_get(32, h, 10); is_missing = (k == kh_end(h)); k = kh_get(32, h, 5); kh_del(32, h, k); for (k = kh_begin(h); k != kh_end(h); ++k) if (kh_exist(h, k)) kh_value(h, k) = 1; kh_destroy(32, h); return 0; } */ /* 2008-09-19 (0.2.3): * Corrected the example * Improved interfaces 2008-09-11 (0.2.2): * Improved speed a little in kh_put() 2008-09-10 (0.2.1): * Added kh_clear() * Fixed a compiling error 2008-09-02 (0.2.0): * Changed to token concatenation which increases flexibility. 2008-08-31 (0.1.2): * Fixed a bug in kh_get(), which has not been tested previously. 2008-08-31 (0.1.1): * Added destructor */ #ifndef __AC_KHASH_H #define __AC_KHASH_H #define AC_VERSION_KHASH_H "0.2.2" #include "ns_turn_defs.h" typedef u32bits khint_t; typedef khint_t khiter_t; typedef struct _str_chunk_t { const s08bits *str; size_t len; } str_chunk_t; #define __ac_HASH_PRIME_SIZE 32 static const u32bits __ac_prime_list[__ac_HASH_PRIME_SIZE] = { 0ul, 3ul, 11ul, 23ul, 53ul, 97ul, 193ul, 389ul, 769ul, 1543ul, 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, 1610612741ul, 3221225473ul, 4294967291ul }; #define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) #define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) #define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) #define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) #define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) #define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) #define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) static const double __ac_HASH_UPPER = 0.77; #define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ typedef struct { \ khint_t n_buckets, size, n_occupied, upper_bound; \ u32bits *flags; u32bits flags_size; \ khkey_t *keys; u32bits keys_size; \ khval_t *vals; u32bits vals_size; \ } kh_##name##_t; \ static inline kh_##name##_t *kh_init_##name(void) { \ return (kh_##name##_t*)turn_calloc(1, sizeof(kh_##name##_t)); \ } \ static inline void kh_destroy_##name(kh_##name##_t *h) \ { \ if (h) { \ turn_free(h->keys,h->keys_size); turn_free(h->flags,h->flags_size); \ turn_free(h->vals, h->vals_size); \ turn_free(h, sizeof(kh_##name##_t)); \ } \ } \ static inline void kh_clear_##name(kh_##name##_t *h) \ { \ if (h && h->flags) { \ memset(h->flags, 0xaa, ((h->n_buckets>>4) + 1) * sizeof(u32bits)); \ h->size = h->n_occupied = 0; \ } \ } \ static inline khint_t kh_get_##name(kh_##name##_t *h, khkey_t key) \ { \ if (h->n_buckets) { \ khint_t inc, k, i, last; \ k = __hash_func(key); i = k % h->n_buckets; \ inc = 1 + k % (h->n_buckets - 1); last = i; \ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ if (i + inc >= h->n_buckets) i = i + inc - h->n_buckets; \ else i += inc; \ if (i == last) return h->n_buckets; \ } \ return __ac_iseither(h->flags, i)? h->n_buckets : i; \ } else return 0; \ } \ static inline void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ { \ u32bits *new_flags = 0; \ u32bits new_flags_size = 0; \ khint_t j = 1; \ { \ khint_t t = __ac_HASH_PRIME_SIZE - 1; \ while (__ac_prime_list[t] > new_n_buckets) --t; \ new_n_buckets = __ac_prime_list[t+1]; \ if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; \ else { \ new_flags_size = ((new_n_buckets>>4) + 1) * sizeof(u32bits); \ new_flags = (u32bits*)turn_malloc(new_flags_size); \ memset(new_flags, 0xaa, new_flags_size); \ if (h->n_buckets < new_n_buckets) { \ h->keys = (khkey_t*)turn_realloc(h->keys, h->keys_size, new_n_buckets * sizeof(khkey_t)); \ h->keys_size = new_n_buckets * sizeof(khkey_t); \ if (kh_is_map) { \ h->vals = (khval_t*)turn_realloc(h->vals, h->vals_size, new_n_buckets * sizeof(khval_t)); \ h->vals_size = new_n_buckets * sizeof(khval_t); \ } \ } \ } \ } \ if (j) { \ for (j = 0; j != h->n_buckets; ++j) { \ if (__ac_iseither(h->flags, j) == 0) { \ khkey_t key = h->keys[j]; \ khval_t val; \ if (kh_is_map) val = h->vals[j]; \ __ac_set_isdel_true(h->flags, j); \ while (1) { \ khint_t inc, k, i; \ k = __hash_func(key); \ i = k % new_n_buckets; \ inc = 1 + k % (new_n_buckets - 1); \ while (!__ac_isempty(new_flags, i)) { \ if (i + inc >= new_n_buckets) i = i + inc - new_n_buckets; \ else i += inc; \ } \ __ac_set_isempty_false(new_flags, i); \ if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { \ { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ __ac_set_isdel_true(h->flags, i); \ } else { \ h->keys[i] = key; \ if (kh_is_map) h->vals[i] = val; \ break; \ } \ } \ } \ } \ if (h->n_buckets > new_n_buckets) { \ h->keys = (khkey_t*)turn_realloc(h->keys, h->keys_size, new_n_buckets * sizeof(khkey_t)); \ h->keys_size = new_n_buckets * sizeof(khkey_t); \ if (kh_is_map) { \ h->vals = (khval_t*)turn_realloc(h->vals, h->vals_size, new_n_buckets * sizeof(khval_t)); \ h->vals_size = new_n_buckets * sizeof(khval_t); \ } \ } \ turn_free(h->flags, h->flags_size); \ h->flags = new_flags; \ h->flags_size = new_flags_size; \ h->n_buckets = new_n_buckets; \ h->n_occupied = h->size; \ h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ } \ } \ static inline khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ { \ khint_t x; \ if (h->n_occupied >= h->upper_bound) { \ if (h->n_buckets > (h->size<<1)) kh_resize_##name(h, h->n_buckets - 1); \ else kh_resize_##name(h, h->n_buckets + 1); \ } \ { \ khint_t inc, k, i, site, last; \ x = site = h->n_buckets; k = __hash_func(key); i = k % h->n_buckets; \ if (__ac_isempty(h->flags, i)) x = i; \ else { \ inc = 1 + k % (h->n_buckets - 1); last = i; \ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ if (__ac_isdel(h->flags, i)) site = i; \ if (i + inc >= h->n_buckets) i = i + inc - h->n_buckets; \ else i += inc; \ if (i == last) { x = site; break; } \ } \ if (x == h->n_buckets) { \ if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ else x = i; \ } \ } \ } \ if (__ac_isempty(h->flags, x)) { \ h->keys[x] = key; \ __ac_set_isboth_false(h->flags, x); \ ++h->size; ++h->n_occupied; \ *ret = 1; \ } else if (__ac_isdel(h->flags, x)) { \ h->keys[x] = key; \ __ac_set_isboth_false(h->flags, x); \ ++h->size; \ *ret = 2; \ } else *ret = 0; \ return x; \ } \ static inline void kh_del_##name(kh_##name##_t *h, khint_t x) \ { \ if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ __ac_set_isdel_true(h->flags, x); \ --h->size; \ } \ } /* --- BEGIN OF HASH FUNCTIONS --- */ #define kh_int_hash_func(key) (u32bits)((key<<3) + nswap32(key>>7)) #define kh_int_hash_equal(a, b) (a == b) #define kh_int64_hash_func(key) (u32bits)((key)>>33^(key)^(key)<<11) #define kh_int64_hash_equal(a, b) (a == b) static inline khint_t __ac_X31_hash_string(const s08bits *s) { khint_t h = *s; if (h) for (++s; *s; ++s) h = (h << 5) - h + *s; return h; } static inline khint_t __ac_X31_hash_cstring(const s08bits *s) { khint_t h = tolower((int)*s); if (h) for (++s; *s; ++s) h = (h << 5) - h + tolower((int)*s); return h; } static inline khint_t __ac_X31_hash_nstring(const str_chunk_t *s) { khint_t h = *(s->str); if (h) { size_t i; for (i = 0; i < s->len; i++) h = (h << 5) - h + s->str[i]; } return h; } static inline khint_t __ac_X31_hash_ncstring(const str_chunk_t *s) { khint_t h = tolower((int)(*(s->str))); if (h) { size_t i; for (i = 0; i < s->len; i++) h = (h << 5) - h + tolower((int)(s->str[i])); } return h; } #define kh_str_hash_func(key) __ac_X31_hash_string(key) #define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) #define kh_cstr_hash_func(key) __ac_X31_hash_cstring(key) #define kh_cstr_hash_equal(a, b) (strcasecmp(a, b) == 0) #define kh_nstr_hash_func(key) __ac_X31_hash_nstring(key) #define kh_nstr_hash_equal(a, b) (a->len == b->len && (strncmp(a->str, b->str, a->len) == 0)) #define kh_ncstr_hash_func(key) __ac_X31_hash_ncstring(key) #define kh_ncstr_hash_equal(a, b) (a->len == b->len && (strncasecmp(a->str, b->str, a->len) == 0)) /* --- END OF HASH FUNCTIONS --- */ /* Other necessary macros... */ #define khash_t(name) kh_##name##_t #define kh_init(name) kh_init_##name() #define kh_destroy(name, h) kh_destroy_##name(h) #define kh_clear(name, h) kh_clear_##name(h) #define kh_resize(name, h, s) kh_resize_##name(h, s) #define kh_put(name, h, k, r) kh_put_##name(h, k, r) #define kh_get(name, h, k) kh_get_##name(h, k) #define kh_del(name, h, k) kh_del_##name(h, k) #define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) #define kh_key(h, x) ((h)->keys[x]) #define kh_val(h, x) ((h)->vals[x]) #define kh_value(h, x) ((h)->vals[x]) #define kh_begin(h) (khint_t)(0) #define kh_end(h) ((h)->n_buckets) #define kh_size(h) ((h)->size) #define kh_n_buckets(h) ((h)->n_buckets) /* More convenient interfaces */ #define KHASH_SET_INIT_INT(name) \ KHASH_INIT(name, u32bits, s08bits, 0, kh_int_hash_func, kh_int_hash_equal) #define KHASH_MAP_INIT_INT(name, khval_t) \ KHASH_INIT(name, u32bits, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) #define KHASH_SET_INIT_INT64(name) \ KHASH_INIT(name, u64bits, s08bits, 0, kh_int64_hash_func, kh_int64_hash_equal) #define KHASH_MAP_INIT_INT64(name, khval_t) \ KHASH_INIT(name, u64bits, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) typedef const s08bits *kh_cstr_t; typedef const str_chunk_t *kh_ncstr_t; #define KHASH_SET_INIT_STR(name) \ KHASH_INIT(name, kh_cstr_t, s08bits, 0, kh_str_hash_func, kh_str_hash_equal) #define KHASH_MAP_INIT_STR(name, khval_t) \ KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) #define KHASH_SET_INIT_CSTR(name) \ KHASH_INIT(name, kh_cstr_t, s08bits, 0, kh_cstr_hash_func, kh_cstr_hash_equal) #define KHASH_MAP_INIT_CSTR(name, khval_t) \ KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_cstr_hash_func, kh_cstr_hash_equal) #define KHASH_SET_INIT_NSTR(name) \ KHASH_INIT(name, kh_ncstr_t, s08bits, 0, kh_nstr_hash_func, kh_nstr_hash_equal) #define KHASH_MAP_INIT_NSTR(name, khval_t) \ KHASH_INIT(name, kh_ncstr_t, khval_t, 1, kh_nstr_hash_func, kh_nstr_hash_equal) #define KHASH_SET_INIT_NCSTR(name) \ KHASH_INIT(name, kh_ncstr_t, s08bits, 0, kh_ncstr_hash_func, kh_ncstr_hash_equal) #define KHASH_MAP_INIT_NCSTR(name, khval_t) \ KHASH_INIT(name, kh_ncstr_t, khval_t, 1, kh_ncstr_hash_func, kh_ncstr_hash_equal) ////////////////////////////////////////////// #endif /* __AC_KHASH_H */ turnserver-4.5.1.1/src/server/ns_turn_maps_rtcp.h0000664000175000017500000000460413436577655020624 0ustar misimisi/* * 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. */ #ifndef __TURN_RTCP_MAP__ #define __TURN_RTCP_MAP__ #include "ns_turn_maps.h" #include "ns_turn_ioalib.h" #ifdef __cplusplus extern "C" { #endif //////////////// RTCP MAP ////////////////// typedef ur_map_key_type rtcp_token_type; struct _rtcp_map; typedef struct _rtcp_map rtcp_map; //////////////////////////////////////////////// rtcp_map* rtcp_map_create(ioa_engine_handle e); /** * @ret: * 0 - success * -1 - error */ int rtcp_map_put(rtcp_map* map, rtcp_token_type key, ioa_socket_handle s); /** * @ret: * >=0 - success * <0 - not found */ ioa_socket_handle rtcp_map_get(rtcp_map* map, rtcp_token_type token); /** * @ret: * 1 - success * 0 - not found */ void rtcp_map_free(rtcp_map** map); size_t rtcp_map_size(const rtcp_map* map); //////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_RTCP_MAP__ turnserver-4.5.1.1/src/server/ns_turn_server.c0000664000175000017500000043114713436577655020143 0ustar misimisi/* * 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_server.h" #include "ns_turn_utils.h" #include "ns_turn_allocation.h" #include "ns_turn_msg_addr.h" #include "ns_turn_ioalib.h" #include "../apps/relay/ns_ioalib_impl.h" /////////////////////////////////////////// #define FUNCSTART if(server && eve(server->verbose)) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s:%d:start\n",__FUNCTION__,__LINE__) #define FUNCEND if(server && eve(server->verbose)) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s:%d:end\n",__FUNCTION__,__LINE__) //////////////////////////////////////////////// static inline int get_family(int stun_family, ioa_engine_handle e, ioa_socket_handle client_socket) { switch(stun_family) { case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4: return AF_INET; break; case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6: return AF_INET6; break; case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT: if(e->default_relays && get_ioa_socket_address_family(client_socket) == AF_INET6) return AF_INET6; else return AF_INET; default: return AF_INET; }; } //////////////////////////////////////////////// const char * get_version(turn_turnserver *server) { if(server && !*server->prod) { return (const char *) TURN_SOFTWARE; } else { return (const char *) "None"; } } #define MAX_NUMBER_OF_UNKNOWN_ATTRS (128) int TURN_MAX_ALLOCATE_TIMEOUT = 60; int TURN_MAX_ALLOCATE_TIMEOUT_STUN_ONLY = 3; static inline void log_method(ts_ur_super_session* ss, const char *method, int err_code, const u08bits *reason) { if(ss) { if(!method) method = "unknown"; if(!err_code) { if(ss->origin[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "session %018llu: origin <%s> realm <%s> user <%s>: incoming packet %s processed, success\n", (unsigned long long)(ss->id), (const char*)(ss->origin),(const char*)(ss->realm_options.name),(const char*)(ss->username),method); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "session %018llu: realm <%s> user <%s>: incoming packet %s processed, success\n", (unsigned long long)(ss->id), (const char*)(ss->realm_options.name),(const char*)(ss->username),method); } } else { if(!reason) reason=get_default_reason(err_code); if(ss->origin[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "session %018llu: origin <%s> realm <%s> user <%s>: incoming packet %s processed, error %d: %s\n", (unsigned long long)(ss->id), (const char*)(ss->origin),(const char*)(ss->realm_options.name),(const char*)(ss->username), method, err_code, reason); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "session %018llu: realm <%s> user <%s>: incoming packet %s processed, error %d: %s\n", (unsigned long long)(ss->id), (const char*)(ss->realm_options.name),(const char*)(ss->username), method, err_code, reason); } } } } /////////////////////////////////////////// static int attach_socket_to_session(turn_turnserver* server, ioa_socket_handle s, ts_ur_super_session* ss); static int check_stun_auth(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, u16bits method, int *message_integrity, int *postpone_reply, int can_resume); static int create_relay_connection(turn_turnserver* server, ts_ur_super_session *ss, u32bits lifetime, int address_family, u08bits transport, int even_port, u64bits in_reservation_token, u64bits *out_reservation_token, int *err_code, const u08bits **reason, accept_cb acb); static int refresh_relay_connection(turn_turnserver* server, ts_ur_super_session *ss, u32bits lifetime, int even_port, u64bits in_reservation_token, u64bits *out_reservation_token, int *err_code, int family); static int write_client_connection(turn_turnserver *server, ts_ur_super_session* ss, ioa_network_buffer_handle nbh, int ttl, int tos); static void tcp_peer_accept_connection(ioa_socket_handle s, void *arg); static int read_client_connection(turn_turnserver *server, ts_ur_super_session *ss, ioa_net_data *in_buffer, int can_resume, int count_usage); static int need_stun_authentication(turn_turnserver *server, ts_ur_super_session *ss); /////////////////// timer ////////////////////////// static void timer_timeout_handler(ioa_engine_handle e, void *arg) { UNUSED_ARG(e); if(arg) { turn_turnserver *server=(turn_turnserver*)arg; server->ctime = turn_time(); } } turn_time_t get_turn_server_time(turn_turnserver *server) { if(server) { return server->ctime; } return turn_time(); } /////////////////// quota ////////////////////// static int inc_quota(ts_ur_super_session* ss, u08bits *username) { if(ss && !(ss->quota_used) && ss->server && ((turn_turnserver*)ss->server)->chquotacb && username) { if(((turn_turnserver*)ss->server)->ct == TURN_CREDENTIALS_LONG_TERM) { if(!(ss->origin_set)) { return -1; } } if((((turn_turnserver*)ss->server)->chquotacb)(username, ss->oauth, (u08bits*)ss->realm_options.name)<0) { return -1; } else { STRCPY(ss->username,username); ss->quota_used = 1; } } return 0; } static void dec_quota(ts_ur_super_session* ss) { if(ss && ss->quota_used && ss->server && ((turn_turnserver*)ss->server)->raqcb) { ss->quota_used = 0; (((turn_turnserver*)ss->server)->raqcb)(ss->username, ss->oauth, (u08bits*)ss->realm_options.name); } } static void dec_bps(ts_ur_super_session* ss) { if(ss && ss->server) { if(ss->bps) { if(((turn_turnserver*)ss->server)->allocate_bps_func) { ((turn_turnserver*)ss->server)->allocate_bps_func(ss->bps,0); } ss->bps = 0; } } } /////////////////// server lists /////////////////// void init_turn_server_addrs_list(turn_server_addrs_list_t *l) { if(l) { l->addrs = NULL; l->size = 0; turn_mutex_init(&(l->m)); } } /////////////////// RFC 5780 /////////////////////// void set_rfc5780(turn_turnserver *server, get_alt_addr_cb cb, send_message_cb smcb) { if(server) { if(!cb || !smcb) { server->rfc5780 = 0; server->alt_addr_cb = NULL; server->sm_cb = NULL; } else { server->rfc5780 = 1; server->alt_addr_cb = cb; server->sm_cb = smcb; } } } static int is_rfc5780(turn_turnserver *server) { if(!server) return 0; return ((server->rfc5780) && (server->alt_addr_cb)); } static int get_other_address(turn_turnserver *server, ts_ur_super_session *ss, ioa_addr *alt_addr) { if(is_rfc5780(server) && ss && ss->client_socket) { int ret = server->alt_addr_cb(get_local_addr_from_ioa_socket(ss->client_socket), alt_addr); return ret; } return -1; } static int send_turn_message_to(turn_turnserver *server, ioa_network_buffer_handle nbh, ioa_addr *response_origin, ioa_addr *response_destination) { if(is_rfc5780(server) && nbh && response_origin && response_destination) { return server->sm_cb(server->e, nbh, response_origin, response_destination); } return -1; } /////////////////// Peer addr check ///////////////////////////// static int good_peer_addr(turn_turnserver *server, const char* realm, ioa_addr *peer_addr) { #define CHECK_REALM(r) if((r)[0] && realm && realm[0] && strcmp((r),realm)) continue if(server && peer_addr) { if(*(server->no_multicast_peers) && ioa_addr_is_multicast(peer_addr)) return 0; if( !*(server->allow_loopback_peers) && ioa_addr_is_loopback(peer_addr)) return 0; { int i; if(server->ip_whitelist) { // White listing of addr ranges for (i = server->ip_whitelist->ranges_number - 1; i >= 0; --i) { CHECK_REALM(server->ip_whitelist->rs[i].realm); if (ioa_addr_in_range(&(server->ip_whitelist->rs[i].enc), peer_addr)) return 1; } } { ioa_lock_whitelist(server->e); const ip_range_list_t* wl = ioa_get_whitelist(server->e); if(wl) { // White listing of addr ranges for (i = wl->ranges_number - 1; i >= 0; --i) { CHECK_REALM(wl->rs[i].realm); if (ioa_addr_in_range(&(wl->rs[i].enc), peer_addr)) { ioa_unlock_whitelist(server->e); return 1; } } } ioa_unlock_whitelist(server->e); } if(server->ip_blacklist) { // Black listing of addr ranges for (i = server->ip_blacklist->ranges_number - 1; i >= 0; --i) { CHECK_REALM(server->ip_blacklist->rs[i].realm); if (ioa_addr_in_range(&(server->ip_blacklist->rs[i].enc), peer_addr)) { char saddr[129]; addr_to_string_no_port(peer_addr,(u08bits*)saddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "A peer IP %s denied in the range: %s\n",saddr,server->ip_blacklist->rs[i].str); return 0; } } } { ioa_lock_blacklist(server->e); const ip_range_list_t* bl = ioa_get_blacklist(server->e); if(bl) { // Black listing of addr ranges for (i = bl->ranges_number - 1; i >= 0; --i) { CHECK_REALM(bl->rs[i].realm); if (ioa_addr_in_range(&(bl->rs[i].enc), peer_addr)) { ioa_unlock_blacklist(server->e); char saddr[129]; addr_to_string_no_port(peer_addr,(u08bits*)saddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "A peer IP %s denied in the range: %s\n",saddr,bl->rs[i].str); return 0; } } } ioa_unlock_blacklist(server->e); } } } #undef CHECK_REALM return 1; } /////////////////// Allocation ////////////////////////////////// allocation* get_allocation_ss(ts_ur_super_session *ss) { return &(ss->alloc); } static inline relay_endpoint_session *get_relay_session_ss(ts_ur_super_session *ss, int family) { return get_relay_session(&(ss->alloc),family); } static inline ioa_socket_handle get_relay_socket_ss(ts_ur_super_session *ss, int family) { return get_relay_socket(&(ss->alloc),family); } /////////// Session info /////// void turn_session_info_init(struct turn_session_info* tsi) { if(tsi) { ns_bzero(tsi,sizeof(struct turn_session_info)); } } void turn_session_info_clean(struct turn_session_info* tsi) { if(tsi) { if(tsi->extra_peers_data) { turn_free(tsi->extra_peers_data, sizeof(addr_data)*(tsi->extra_peers_size)); } turn_session_info_init(tsi); } } void turn_session_info_add_peer(struct turn_session_info* tsi, ioa_addr *peer) { if(tsi && peer) { { size_t i; for(i=0;imain_peers_size;++i) { if(addr_eq(peer, &(tsi->main_peers_data[i].addr))) { return; } } if(tsi->main_peers_size < TURN_MAIN_PEERS_ARRAY_SIZE) { addr_cpy(&(tsi->main_peers_data[tsi->main_peers_size].addr),peer); addr_to_string(&(tsi->main_peers_data[tsi->main_peers_size].addr), (u08bits*)tsi->main_peers_data[tsi->main_peers_size].saddr); tsi->main_peers_size += 1; return; } } if(tsi->extra_peers_data) { size_t sz; for(sz=0;szextra_peers_size;++sz) { if(addr_eq(peer, &(tsi->extra_peers_data[sz].addr))) { return; } } } tsi->extra_peers_data = (addr_data*)turn_realloc(tsi->extra_peers_data,tsi->extra_peers_size*sizeof(addr_data),(tsi->extra_peers_size+1)*sizeof(addr_data)); addr_cpy(&(tsi->extra_peers_data[tsi->extra_peers_size].addr),peer); addr_to_string(&(tsi->extra_peers_data[tsi->extra_peers_size].addr), (u08bits*)tsi->extra_peers_data[tsi->extra_peers_size].saddr); tsi->extra_peers_size += 1; } } struct tsi_arg { struct turn_session_info* tsi; ioa_addr *addr; }; static int turn_session_info_foreachcb(ur_map_key_type key, ur_map_value_type value, void *arg) { UNUSED_ARG(value); int port = (int)key; struct tsi_arg *ta = (struct tsi_arg *)arg; if(port && ta && ta->tsi && ta->addr) { ioa_addr a; addr_cpy(&a,ta->addr); addr_set_port(&a,port); turn_session_info_add_peer(ta->tsi,&a); } return 0; } int turn_session_info_copy_from(struct turn_session_info* tsi, ts_ur_super_session *ss) { int ret = -1; if(tsi && ss) { tsi->id = ss->id; tsi->bps = ss->bps; tsi->start_time = ss->start_time; tsi->valid = is_allocation_valid(&(ss->alloc)) && !(ss->to_be_closed) && (ss->quota_used); if(tsi->valid) { if(ss->alloc.relay_sessions[ALLOC_IPV4_INDEX].s) { tsi->expiration_time = ss->alloc.relay_sessions[ALLOC_IPV4_INDEX].expiration_time; if(ss->alloc.relay_sessions[ALLOC_IPV6_INDEX].s) { if(turn_time_before(tsi->expiration_time,ss->alloc.relay_sessions[ALLOC_IPV6_INDEX].expiration_time)) { tsi->expiration_time = ss->alloc.relay_sessions[ALLOC_IPV6_INDEX].expiration_time; } } } else if(ss->alloc.relay_sessions[ALLOC_IPV6_INDEX].s) { tsi->expiration_time = ss->alloc.relay_sessions[ALLOC_IPV6_INDEX].expiration_time; } if(ss->client_socket) { tsi->client_protocol = get_ioa_socket_type(ss->client_socket); addr_cpy(&(tsi->local_addr_data.addr),get_local_addr_from_ioa_socket(ss->client_socket)); addr_to_string(&(tsi->local_addr_data.addr),(u08bits*)tsi->local_addr_data.saddr); addr_cpy(&(tsi->remote_addr_data.addr),get_remote_addr_from_ioa_socket(ss->client_socket)); addr_to_string(&(tsi->remote_addr_data.addr),(u08bits*)tsi->remote_addr_data.saddr); } { if(ss->alloc.relay_sessions[ALLOC_IPV4_INDEX].s) { tsi->peer_protocol = get_ioa_socket_type(ss->alloc.relay_sessions[ALLOC_IPV4_INDEX].s); if(ss->alloc.is_valid) { addr_cpy(&(tsi->relay_addr_data_ipv4.addr),get_local_addr_from_ioa_socket(ss->alloc.relay_sessions[ALLOC_IPV4_INDEX].s)); addr_to_string(&(tsi->relay_addr_data_ipv4.addr),(u08bits*)tsi->relay_addr_data_ipv4.saddr); } } if(ss->alloc.relay_sessions[ALLOC_IPV6_INDEX].s) { tsi->peer_protocol = get_ioa_socket_type(ss->alloc.relay_sessions[ALLOC_IPV6_INDEX].s); if(ss->alloc.is_valid) { addr_cpy(&(tsi->relay_addr_data_ipv6.addr),get_local_addr_from_ioa_socket(ss->alloc.relay_sessions[ALLOC_IPV6_INDEX].s)); addr_to_string(&(tsi->relay_addr_data_ipv6.addr),(u08bits*)tsi->relay_addr_data_ipv6.saddr); } } } STRCPY(tsi->username,ss->username); tsi->enforce_fingerprints = ss->enforce_fingerprints; STRCPY(tsi->tls_method, get_ioa_socket_tls_method(ss->client_socket)); STRCPY(tsi->tls_cipher, get_ioa_socket_tls_cipher(ss->client_socket)); STRCPY(tsi->realm, ss->realm_options.name); STRCPY(tsi->origin, ss->origin); if(ss->t_received_packets > ss->received_packets) tsi->received_packets = ss->t_received_packets; else tsi->received_packets = ss->received_packets; if(ss->t_sent_packets > ss->sent_packets) tsi->sent_packets = ss->t_sent_packets; else tsi->sent_packets = ss->sent_packets; if(ss->t_received_bytes > ss->received_bytes) tsi->received_bytes = ss->t_received_bytes; else tsi->received_bytes = ss->received_bytes; if(ss->t_sent_bytes > ss->sent_bytes) tsi->sent_bytes = ss->t_sent_bytes; else tsi->sent_bytes = ss->sent_bytes; { tsi->received_rate = ss->received_rate; tsi->sent_rate = ss->sent_rate; tsi->total_rate = tsi->received_rate + tsi->sent_rate; } tsi->is_mobile = ss->is_mobile; { size_t i; for(i=0;ialloc.addr_to_perm.table[i]); { size_t j; for(j=0;jmain_slots[j]); if(slot->info.allocated) { turn_session_info_add_peer(tsi,&(slot->info.addr)); struct tsi_arg arg = { tsi, &(slot->info.addr) }; lm_map_foreach_arg(&(slot->info.chns), turn_session_info_foreachcb, &arg); } } } { turn_permission_slot **slots = parray->extra_slots; if(slots) { size_t sz = parray->extra_sz; size_t j; for(j=0;jinfo.allocated) { turn_session_info_add_peer(tsi,&(slot->info.addr)); struct tsi_arg arg = { tsi, &(slot->info.addr) }; lm_map_foreach_arg(&(slot->info.chns), turn_session_info_foreachcb, &arg); } } } } } } { tcp_connection_list *tcl = &(ss->alloc.tcs); if(tcl->elems) { size_t i; size_t sz = tcl->sz; for(i=0;ielems[i]) { tcp_connection *tc = tcl->elems[i]; if(tc) { turn_session_info_add_peer(tsi,&(tc->peer_addr)); } } } } } } ret = 0; } return ret; } int report_turn_session_info(turn_turnserver *server, ts_ur_super_session *ss, int force_invalid) { if(server && ss && server->send_turn_session_info) { struct turn_session_info tsi; turn_session_info_init(&tsi); if(turn_session_info_copy_from(&tsi,ss)<0) { turn_session_info_clean(&tsi); } else { if(force_invalid) tsi.valid = 0; if(server->send_turn_session_info(&tsi)<0) { turn_session_info_clean(&tsi); } else { return 0; } } } return -1; } /////////// SS ///////////////// static int mobile_id_to_string(mobile_id_t mid, char *dst, size_t dst_sz) { size_t output_length = 0; if(!dst) return -1; char *s = base64_encode((const unsigned char *)&mid, sizeof(mid), &output_length); if(!s) return -1; if(!output_length || (output_length+1 > dst_sz)) { turn_free(s, output_length); return -1; } ns_bcopy(s, dst, output_length); turn_free(s, output_length); dst[output_length] = 0; return (int)output_length; } static mobile_id_t string_to_mobile_id(char* src) { mobile_id_t mid = 0; if(src) { size_t output_length = 0; unsigned char *out = base64_decode(src, strlen(src), &output_length); if(out) { if(output_length == sizeof(mid)) { mid = *((mobile_id_t*)out); } turn_free(out, output_length); } } return mid; } static mobile_id_t get_new_mobile_id(turn_turnserver* server) { mobile_id_t newid = 0; if(server && server->mobile_connections_map) { ur_map *map = server->mobile_connections_map; u64bits sid = server->id; sid = sid<<56; do { while (!newid) { if(TURN_RANDOM_SIZE == sizeof(mobile_id_t)) newid = (mobile_id_t)turn_random(); else { newid = (mobile_id_t)turn_random(); newid = (newid<<32) + (mobile_id_t)turn_random(); } if(!newid) { continue; } newid = newid & 0x00FFFFFFFFFFFFFFLL; if(!newid) { continue; } newid = newid | sid; } } while(ur_map_get(map, (ur_map_key_type)newid, NULL)); } return newid; } static void put_session_into_mobile_map(ts_ur_super_session *ss) { if(ss && ss->server) { turn_turnserver* server = (turn_turnserver*)(ss->server); if(*(server->mobility) && server->mobile_connections_map) { if(!(ss->mobile_id)) { ss->mobile_id = get_new_mobile_id(server); mobile_id_to_string(ss->mobile_id, ss->s_mobile_id, sizeof(ss->s_mobile_id)); } ur_map_put(server->mobile_connections_map, (ur_map_key_type)(ss->mobile_id), (ur_map_value_type)ss); } } } static void put_session_into_map(ts_ur_super_session *ss) { if(ss && ss->server) { turn_turnserver* server = (turn_turnserver*)(ss->server); if(!(ss->id)) { ss->id = (turnsession_id)((turnsession_id)server->id * TURN_SESSION_ID_FACTOR); ss->id += ++(server->session_id_counter); ss->start_time = server->ctime; } ur_map_put(server->sessions_map, (ur_map_key_type)(ss->id), (ur_map_value_type)ss); put_session_into_mobile_map(ss); } } static void delete_session_from_mobile_map(ts_ur_super_session *ss) { if(ss && ss->server && ss->mobile_id) { turn_turnserver* server = (turn_turnserver*)(ss->server); if(server->mobile_connections_map) { ur_map_del(server->mobile_connections_map, (ur_map_key_type)(ss->mobile_id), NULL); } ss->mobile_id = 0; ss->s_mobile_id[0] = 0; } } static void delete_session_from_map(ts_ur_super_session *ss) { if(ss && ss->server) { turn_turnserver* server = (turn_turnserver*)(ss->server); ur_map_del(server->sessions_map, (ur_map_key_type)(ss->id), NULL); delete_session_from_mobile_map(ss); } } static ts_ur_super_session* get_session_from_map(turn_turnserver* server, turnsession_id sid) { ts_ur_super_session *ss = NULL; if(server) { ur_map_value_type value = 0; if(ur_map_get(server->sessions_map, (ur_map_key_type)sid, &value) && value) { ss = (ts_ur_super_session*)value; } } return ss; } void turn_cancel_session(turn_turnserver *server, turnsession_id sid) { if(server) { ts_ur_super_session* ts = get_session_from_map(server, sid); if(ts) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Session %018llu to be forcefully canceled\n",(unsigned long long)sid); shutdown_client_connection(server, ts, 0, "Forceful shutdown"); } } } static ts_ur_super_session* get_session_from_mobile_map(turn_turnserver* server, mobile_id_t mid) { ts_ur_super_session *ss = NULL; if(server && *(server->mobility) && server->mobile_connections_map && mid) { ur_map_value_type value = 0; if(ur_map_get(server->mobile_connections_map, (ur_map_key_type)mid, &value) && value) { ss = (ts_ur_super_session*)value; } } return ss; } static ts_ur_super_session* create_new_ss(turn_turnserver* server) { // //printf("%s: 111.111: session size=%lu\n",__FUNCTION__,(unsigned long)sizeof(ts_ur_super_session)); // ts_ur_super_session *ss = (ts_ur_super_session*)turn_malloc(sizeof(ts_ur_super_session)); ns_bzero(ss,sizeof(ts_ur_super_session)); ss->server = server; get_default_realm_options(&(ss->realm_options)); put_session_into_map(ss); init_allocation(ss,&(ss->alloc), server->tcp_relay_connections); return ss; } static void delete_ur_map_ss(void *p) { if (p) { ts_ur_super_session* ss = (ts_ur_super_session*) p; delete_session_from_map(ss); IOA_CLOSE_SOCKET(ss->client_socket); clear_allocation(get_allocation_ss(ss)); IOA_EVENT_DEL(ss->to_be_allocated_timeout_ev); turn_free(p,sizeof(ts_ur_super_session)); } } /////////// clean all ///////////////////// static int turn_server_remove_all_from_ur_map_ss(ts_ur_super_session* ss) { if (!ss) return 0; else { int ret = 0; if (ss->client_socket) { clear_ioa_socket_session_if(ss->client_socket, ss); } if (get_relay_socket_ss(ss,AF_INET)) { clear_ioa_socket_session_if(get_relay_socket_ss(ss,AF_INET), ss); } if (get_relay_socket_ss(ss,AF_INET6)) { clear_ioa_socket_session_if(get_relay_socket_ss(ss,AF_INET6), ss); } delete_ur_map_ss(ss); return ret; } } ///////////////////////////////////////////////////////////////// static void client_ss_channel_timeout_handler(ioa_engine_handle e, void* arg) { UNUSED_ARG(e); if (!arg) return; ch_info* chn = (ch_info*) arg; turn_channel_delete(chn); } static void client_ss_perm_timeout_handler(ioa_engine_handle e, void* arg) { UNUSED_ARG(e); if (!arg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: empty permission to be cleaned\n",__FUNCTION__); return; } turn_permission_info* tinfo = (turn_permission_info*) arg; if(!(tinfo->allocated)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: unallocated permission to be cleaned\n",__FUNCTION__); return; } if(!(tinfo->lifetime_ev)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: strange (1) permission to be cleaned\n",__FUNCTION__); } if(!(tinfo->owner)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: strange (2) permission to be cleaned\n",__FUNCTION__); } turn_permission_clean(tinfo); } /////////////////////////////////////////////////////////////////// static int update_turn_permission_lifetime(ts_ur_super_session *ss, turn_permission_info *tinfo, turn_time_t time_delta) { if (ss && tinfo && tinfo->owner) { turn_turnserver *server = (turn_turnserver *) (ss->server); if (server) { if(!time_delta) time_delta = *(server->permission_lifetime); tinfo->expiration_time = server->ctime + time_delta; IOA_EVENT_DEL(tinfo->lifetime_ev); tinfo->lifetime_ev = set_ioa_timer(server->e, time_delta, 0, client_ss_perm_timeout_handler, tinfo, 0, "client_ss_channel_timeout_handler"); if(server->verbose) { tinfo->verbose = 1; tinfo->session_id = ss->id; char s[257]="\0"; addr_to_string(&(tinfo->addr),(u08bits*)s); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "session %018llu: peer %s lifetime updated: %lu\n",(unsigned long long)ss->id,s,(unsigned long)time_delta); } return 0; } } return -1; } static int update_channel_lifetime(ts_ur_super_session *ss, ch_info* chn) { if (chn) { turn_permission_info* tinfo = (turn_permission_info*) (chn->owner); if (tinfo && tinfo->owner) { turn_turnserver *server = (turn_turnserver *) (ss->server); if (server) { if (update_turn_permission_lifetime(ss, tinfo, *(server->channel_lifetime)) < 0) return -1; chn->expiration_time = server->ctime + *(server->channel_lifetime); IOA_EVENT_DEL(chn->lifetime_ev); chn->lifetime_ev = set_ioa_timer(server->e, *(server->channel_lifetime), 0, client_ss_channel_timeout_handler, chn, 0, "client_ss_channel_timeout_handler"); return 0; } } } return -1; } /////////////// TURN /////////////////////////// #define SKIP_ATTRIBUTES case STUN_ATTRIBUTE_OAUTH_ACCESS_TOKEN: case STUN_ATTRIBUTE_PRIORITY: case STUN_ATTRIBUTE_FINGERPRINT: case STUN_ATTRIBUTE_MESSAGE_INTEGRITY: break; \ case STUN_ATTRIBUTE_USERNAME: case STUN_ATTRIBUTE_REALM: case STUN_ATTRIBUTE_NONCE: case STUN_ATTRIBUTE_ORIGIN: \ sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh),\ ioa_network_buffer_get_size(in_buffer->nbh), sar); \ continue static u08bits get_transport_value(const u08bits *value) { if((value[0] == STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE)|| (value[0] == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE)) { return value[0]; } return 0; } static int handle_turn_allocate(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh) { int err_code4 = 0; int err_code6 = 0; allocation* alloc = get_allocation_ss(ss); if (is_allocation_valid(alloc)) { if (!stun_tid_equals(tid, &(alloc->tid))) { *err_code = 437; *reason = (const u08bits *)"Wrong TID"; } else { size_t len = ioa_network_buffer_get_size(nbh); ioa_addr xor_relayed_addr1, *pxor_relayed_addr1=NULL; ioa_addr xor_relayed_addr2, *pxor_relayed_addr2=NULL; ioa_addr *relayed_addr1 = get_local_addr_from_ioa_socket(get_relay_socket_ss(ss,AF_INET)); ioa_addr *relayed_addr2 = get_local_addr_from_ioa_socket(get_relay_socket_ss(ss,AF_INET6)); if(get_relay_session_failure(alloc,AF_INET)) { addr_set_any(&xor_relayed_addr1); pxor_relayed_addr1 = &xor_relayed_addr1; } else if(relayed_addr1) { if(server->external_ip_set) { addr_cpy(&xor_relayed_addr1, &(server->external_ip)); addr_set_port(&xor_relayed_addr1,addr_get_port(relayed_addr1)); } else { addr_cpy(&xor_relayed_addr1, relayed_addr1); } pxor_relayed_addr1 = &xor_relayed_addr1; } if(get_relay_session_failure(alloc,AF_INET6)) { addr_set_any(&xor_relayed_addr2); pxor_relayed_addr2 = &xor_relayed_addr2; } else if(relayed_addr2) { if(server->external_ip_set) { addr_cpy(&xor_relayed_addr2, &(server->external_ip)); addr_set_port(&xor_relayed_addr2,addr_get_port(relayed_addr2)); } else { addr_cpy(&xor_relayed_addr2, relayed_addr2); } pxor_relayed_addr2 = &xor_relayed_addr2; } if(pxor_relayed_addr1 || pxor_relayed_addr2) { u32bits lifetime = 0; if(pxor_relayed_addr1) { lifetime = (get_relay_session(alloc,pxor_relayed_addr1->ss.sa_family)->expiration_time - server->ctime); } else if(pxor_relayed_addr2) { lifetime = (get_relay_session(alloc,pxor_relayed_addr2->ss.sa_family)->expiration_time - server->ctime); } stun_set_allocate_response_str(ioa_network_buffer_data(nbh), &len, tid, pxor_relayed_addr1, pxor_relayed_addr2, get_remote_addr_from_ioa_socket(ss->client_socket), lifetime,*(server->max_allocate_lifetime), 0, NULL, 0, ss->s_mobile_id); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } } } else { u08bits transport = 0; turn_time_t lifetime = 0; int even_port = -1; int dont_fragment = 0; u64bits in_reservation_token = 0; int af4 = 0; int af6 = 0; u08bits username[STUN_MAX_USERNAME_SIZE+1]="\0"; size_t ulen = 0; band_limit_t bps = 0; band_limit_t max_bps = 0; stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); if(attr_type == STUN_ATTRIBUTE_USERNAME) { const u08bits* value = stun_attr_get_value(sar); if (value) { ulen = stun_attr_get_len(sar); if(ulen>=sizeof(username)) { *err_code = 400; *reason = (const u08bits *)"User name is too long"; break; } ns_bcopy(value,username,ulen); username[ulen]=0; if(!is_secure_string(username,1)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: wrong username: %s\n", __FUNCTION__, (char*)username); username[0]=0; *err_code = 400; break; } } } switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_NEW_BANDWIDTH: bps = stun_attr_get_bandwidth(sar); break; case STUN_ATTRIBUTE_MOBILITY_TICKET: if(!(*(server->mobility))) { *err_code = 405; *reason = (const u08bits *)"Mobility Forbidden"; } else if (stun_attr_get_len(sar) != 0) { *err_code = 400; *reason = (const u08bits *)"Wrong Mobility Field"; } else { ss->is_mobile = 1; } break; case STUN_ATTRIBUTE_REQUESTED_TRANSPORT: { if (stun_attr_get_len(sar) != 4) { *err_code = 400; *reason = (const u08bits *)"Wrong Transport Field"; } else if(transport) { *err_code = 400; *reason = (const u08bits *)"Duplicate Transport Fields"; } else { const u08bits* value = stun_attr_get_value(sar); if (value) { transport = get_transport_value(value); if (!transport) { *err_code = 442; } if((transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE) && *(server->no_tcp_relay)) { *err_code = 442; *reason = (const u08bits *)"TCP Transport is not allowed by the TURN Server configuration"; } else if((transport == STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE) && *(server->no_udp_relay)) { *err_code = 442; *reason = (const u08bits *)"UDP Transport is not allowed by the TURN Server configuration"; } else if(ss->client_socket) { SOCKET_TYPE cst = get_ioa_socket_type(ss->client_socket); if((transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE) && !is_stream_socket(cst)) { *err_code = 400; *reason = (const u08bits *)"Wrong Transport Data"; } else { ss->is_tcp_relay = (transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE); } } } else { *err_code = 400; *reason = (const u08bits *)"Wrong Transport Data"; } } } break; case STUN_ATTRIBUTE_DONT_FRAGMENT: dont_fragment = 1; if(!(server->dont_fragment)) unknown_attrs[(*ua_num)++] = nswap16(attr_type); break; case STUN_ATTRIBUTE_LIFETIME: { if (stun_attr_get_len(sar) != 4) { *err_code = 400; *reason = (const u08bits *)"Wrong Lifetime Field"; } else { const u08bits* value = stun_attr_get_value(sar); if (!value) { *err_code = 400; *reason = (const u08bits *)"Wrong Lifetime Data"; } else { lifetime = nswap32(*((const u32bits*)value)); } } } break; case STUN_ATTRIBUTE_EVEN_PORT: { if (in_reservation_token) { *err_code = 400; *reason = (const u08bits *)"Even Port and Reservation Token cannot be used together"; } else { even_port = stun_attr_get_even_port(sar); if(even_port) { if (af4 && af6) { *err_code = 400; *reason = (const u08bits *)"Even Port cannot be used with Dual Allocation"; } } } } break; case STUN_ATTRIBUTE_RESERVATION_TOKEN: { int len = stun_attr_get_len(sar); if (len != 8) { *err_code = 400; *reason = (const u08bits *)"Wrong Format of Reservation Token"; } else if(af4 || af6) { *err_code = 400; *reason = (const u08bits *)"Address family attribute can not be used with reservation token request"; } else { if (even_port >= 0) { *err_code = 400; *reason = (const u08bits *)"Reservation Token cannot be used in this request with even port"; } else if (in_reservation_token) { *err_code = 400; *reason = (const u08bits *)"Reservation Token cannot be used in this request"; } else { in_reservation_token = stun_attr_get_reservation_token_value(sar); } } } break; case STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY: if(even_port>0) { *err_code = 400; *reason = (const u08bits *)"Even Port cannot be used with Dual Allocation"; break; } /* Falls through. */ case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY: { if(in_reservation_token) { *err_code = 400; *reason = (const u08bits *)"Address family attribute can not be used with reservation token request"; } else if(af4 || af6) { *err_code = 400; *reason = (const u08bits *)"Extra address family attribute can not be used in the request"; } else { int af_req = stun_get_requested_address_family(sar); switch (af_req) { case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4: if(attr_type == STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY) { *err_code = 400; *reason = (const u08bits *)"Invalid value of the additional address family attribute"; } else { af4 = af_req; } break; case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6: if(attr_type == STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY) { af4 = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4; } af6 = af_req; break; default: *err_code = 440; } } } break; default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if (!transport) { *err_code = 400; if(!(*reason)) *reason = (const u08bits *)"Transport field missed or wrong"; } else if (*ua_num > 0) { *err_code = 420; } else if (*err_code) { ; } else if((transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE) && (dont_fragment || in_reservation_token || (even_port!=-1))) { *err_code = 400; if(!(*reason)) *reason = (const u08bits *)"Request parameters are incompatible with TCP transport"; } else { if(*(server->mobility)) { if(!(ss->is_mobile)) { delete_session_from_mobile_map(ss); } } lifetime = stun_adjust_allocate_lifetime(lifetime, *(server->max_allocate_lifetime), ss->max_session_time_auth); u64bits out_reservation_token = 0; if(inc_quota(ss, username)<0) { *err_code = 486; } else { if(server->allocate_bps_func) { max_bps = ss->realm_options.perf_options.max_bps; if(max_bps && (!bps || (bps && (bps>max_bps)))) { bps = max_bps; } if(bps && (ss->bps == 0)) { ss->bps = server->allocate_bps_func(bps,1); if(!(ss->bps)) { *err_code = 486; *reason = (const u08bits *)"Allocation Bandwidth Quota Reached"; } } } if(af4) af4 = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4; if(af6) af6 = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6; if(af4 && af6) { if(server->external_ip_set) { *err_code = 440; *reason = (const u08bits *)"Dual allocation cannot be supported in the current server configuration"; } if(even_port > 0) { *err_code = 440; *reason = (const u08bits *)"Dual allocation cannot be supported with even-port functionality"; } } if(!(*err_code)) { if(!af4 && !af6) { int a_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT; if (server->keep_address_family) { switch(get_ioa_socket_address_family(ss->client_socket)) { case AF_INET6 : a_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6; break; case AF_INET : a_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4; break; } } int res = create_relay_connection(server, ss, lifetime, a_family, transport, even_port, in_reservation_token, &out_reservation_token, err_code, reason, tcp_peer_accept_connection); if(res<0) { set_relay_session_failure(alloc,AF_INET); if(!(*err_code)) { *err_code = 437; } } } else if(!af4 && af6) { int af6res = create_relay_connection(server, ss, lifetime, af6, transport, even_port, in_reservation_token, &out_reservation_token, err_code, reason, tcp_peer_accept_connection); if(af6res<0) { set_relay_session_failure(alloc,AF_INET6); if(!(*err_code)) { *err_code = 437; } } } else if(af4 && !af6) { int af4res = create_relay_connection(server, ss, lifetime, af4, transport, even_port, in_reservation_token, &out_reservation_token, err_code, reason, tcp_peer_accept_connection); if(af4res<0) { set_relay_session_failure(alloc,AF_INET); if(!(*err_code)) { *err_code = 437; } } } else { const u08bits *reason4 = NULL; const u08bits *reason6 = NULL; { int af4res = create_relay_connection(server, ss, lifetime, af4, transport, even_port, in_reservation_token, &out_reservation_token, &err_code4, &reason4, tcp_peer_accept_connection); if(af4res<0) { set_relay_session_failure(alloc,AF_INET); if(!err_code4) { err_code4 = 440; } } } { int af6res = create_relay_connection(server, ss, lifetime, af6, transport, even_port, in_reservation_token, &out_reservation_token, &err_code6, &reason6, tcp_peer_accept_connection); if(af6res<0) { set_relay_session_failure(alloc,AF_INET6); if(!err_code6) { err_code6 = 440; } } } if(err_code4 && err_code6) { if(reason4) { *err_code = err_code4; *reason = reason4; } else if(reason6) { *err_code = err_code6; *reason = reason6; } else { *err_code = err_code4; } } } } if (*err_code) { if(!(*reason)) { *reason = (const u08bits *)"Cannot create relay endpoint(s)"; } } else { set_allocation_valid(alloc,1); stun_tid_cpy(&(alloc->tid), tid); size_t len = ioa_network_buffer_get_size(nbh); ioa_addr xor_relayed_addr1, *pxor_relayed_addr1=NULL; ioa_addr xor_relayed_addr2, *pxor_relayed_addr2=NULL; ioa_addr *relayed_addr1 = get_local_addr_from_ioa_socket(get_relay_socket_ss(ss,AF_INET)); ioa_addr *relayed_addr2 = get_local_addr_from_ioa_socket(get_relay_socket_ss(ss,AF_INET6)); if(get_relay_session_failure(alloc,AF_INET)) { addr_set_any(&xor_relayed_addr1); pxor_relayed_addr1 = &xor_relayed_addr1; } else if(relayed_addr1) { if(server->external_ip_set) { addr_cpy(&xor_relayed_addr1, &(server->external_ip)); addr_set_port(&xor_relayed_addr1,addr_get_port(relayed_addr1)); } else { addr_cpy(&xor_relayed_addr1, relayed_addr1); } pxor_relayed_addr1 = &xor_relayed_addr1; } if(get_relay_session_failure(alloc,AF_INET6)) { addr_set_any(&xor_relayed_addr2); pxor_relayed_addr2 = &xor_relayed_addr2; } else if(relayed_addr2) { if(server->external_ip_set) { addr_cpy(&xor_relayed_addr2, &(server->external_ip)); addr_set_port(&xor_relayed_addr2,addr_get_port(relayed_addr2)); } else { addr_cpy(&xor_relayed_addr2, relayed_addr2); } pxor_relayed_addr2 = &xor_relayed_addr2; } if(pxor_relayed_addr1 || pxor_relayed_addr2) { stun_set_allocate_response_str(ioa_network_buffer_data(nbh), &len, tid, pxor_relayed_addr1, pxor_relayed_addr2, get_remote_addr_from_ioa_socket(ss->client_socket), lifetime, *(server->max_allocate_lifetime),0,NULL, out_reservation_token, ss->s_mobile_id); if(ss->bps) { stun_attr_add_bandwidth_str(ioa_network_buffer_data(nbh), &len, ss->bps); } ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; turn_report_allocation_set(&(ss->alloc), lifetime, 0); } } } } } if (!(*resp_constructed)) { if (!(*err_code)) { *err_code = 437; } size_t len = ioa_network_buffer_get_size(nbh); stun_set_allocate_response_str(ioa_network_buffer_data(nbh), &len, tid, NULL, NULL, NULL, 0, *(server->max_allocate_lifetime), *err_code, *reason, 0, ss->s_mobile_id); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } if(*resp_constructed && !(*err_code)) { if(err_code4) { size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_address_error_code(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4, err_code4); ioa_network_buffer_set_size(nbh,len); } if(err_code6) { size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_address_error_code(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6, err_code6); ioa_network_buffer_set_size(nbh,len); } } return 0; } static void copy_auth_parameters(ts_ur_super_session *orig_ss, ts_ur_super_session *ss) { if(orig_ss && ss) { dec_quota(ss); ns_bcopy(orig_ss->nonce,ss->nonce,sizeof(ss->nonce)); ss->nonce_expiration_time = orig_ss->nonce_expiration_time; ns_bcopy(&(orig_ss->realm_options),&(ss->realm_options),sizeof(ss->realm_options)); ns_bcopy(orig_ss->username,ss->username,sizeof(ss->username)); ss->hmackey_set = orig_ss->hmackey_set; ns_bcopy(orig_ss->hmackey,ss->hmackey,sizeof(ss->hmackey)); ss->oauth = orig_ss->oauth; ns_bcopy(orig_ss->origin,ss->origin,sizeof(ss->origin)); ss->origin_set = orig_ss->origin_set; ns_bcopy(orig_ss->pwd,ss->pwd,sizeof(ss->pwd)); ss->max_session_time_auth = orig_ss->max_session_time_auth; inc_quota(ss,ss->username); } } static int handle_turn_refresh(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, int message_integrity, int *no_response, int can_resume) { allocation* a = get_allocation_ss(ss); int af4c = 0; int af6c = 0; int af4 = 0; int af6 = 0; { int i; for(i = 0;irelay_sessions[i].s && !ioa_socket_tobeclosed(a->relay_sessions[i].s)) { int family = get_ioa_socket_address_family(a->relay_sessions[i].s); if(AF_INET == family) { af4c = 1; } else if(AF_INET6 == family) { af6c = 1; } } } } if (!is_allocation_valid(a) && !(*(server->mobility))) { *err_code = 437; *reason = (const u08bits *)"Invalid allocation"; } else { turn_time_t lifetime = 0; int to_delete = 0; mobile_id_t mid = 0; char smid[sizeof(ss->s_mobile_id)] = "\0"; stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_MOBILITY_TICKET: { if(!(*(server->mobility))) { *err_code = 405; *reason = (const u08bits *)"Mobility forbidden"; } else { int smid_len = stun_attr_get_len(sar); if(smid_len>0 && (((size_t)smid_len)old_mobile_id)) { *err_code = 400; *reason = (const u08bits *)"Mobility ticket cannot be used for a stable, already established allocation"; } } } else { *err_code = 400; *reason = (const u08bits *)"Mobility ticket has wrong length"; } } } break; case STUN_ATTRIBUTE_LIFETIME: { if (stun_attr_get_len(sar) != 4) { *err_code = 400; *reason = (const u08bits *)"Wrong Lifetime field format"; } else { const u08bits* value = stun_attr_get_value(sar); if (!value) { *err_code = 400; *reason = (const u08bits *)"Wrong lifetime field data"; } else { lifetime = nswap32(*((const u32bits*)value)); if (!lifetime) to_delete = 1; } } } break; case STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY: /* deprecated, for backward compatibility with older versions of TURN-bis */ case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY: { int af_req = stun_get_requested_address_family(sar); { int is_err = 0; switch (af_req) { case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4: if(!af4c) { is_err = 1; } else { af4 = 1; } break; case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6: if(!af6c) { is_err = 1; } else { af6 = 1; } break; default: is_err = 1; } if(is_err) { *err_code = 443; *reason = (const u08bits *)"Peer Address Family Mismatch (1)"; } } } break; default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if (*ua_num > 0) { *err_code = 420; } else if (*err_code) { ; } else if(!is_allocation_valid(a)) { if(mid && smid[0]) { turnserver_id tsid = ((0xFF00000000000000LL) & mid)>>56; if(tsid != server->id) { if(server->send_socket_to_relay) { ioa_socket_handle new_s = detach_ioa_socket(ss->client_socket); if(new_s) { if(server->send_socket_to_relay(tsid, mid, tid, new_s, message_integrity, RMT_MOBILE_SOCKET, in_buffer, can_resume)<0) { *err_code = 400; *reason = (const u08bits *)"Wrong mobile ticket"; } else { *no_response = 1; } } else { *err_code = 500; *reason = (const u08bits *)"Cannot create new socket"; return -1; } } else { *err_code = 500; *reason = (const u08bits *)"Server send socket procedure is not set"; } ss->to_be_closed = 1; } else { ts_ur_super_session *orig_ss = get_session_from_mobile_map(server, mid); if(!orig_ss || orig_ss->to_be_closed || ioa_socket_tobeclosed(orig_ss->client_socket)) { *err_code = 404; *reason = (const u08bits *)"Allocation not found"; } else if(orig_ss == ss) { *err_code = 437; *reason = (const u08bits *)"Invalid allocation"; } else if(!(orig_ss->is_mobile)) { *err_code = 500; *reason = (const u08bits *)"Software error: invalid mobile allocation"; } else if(orig_ss->client_socket == ss->client_socket) { *err_code = 500; *reason = (const u08bits *)"Software error: invalid mobile client socket (orig)"; } else if(!(ss->client_socket)) { *err_code = 500; *reason = (const u08bits *)"Software error: invalid mobile client socket (new)"; } else { get_realm_options_by_name(orig_ss->realm_options.name, &(ss->realm_options)); //Check security: int postpone_reply = 0; if(!(ss->hmackey_set)) { copy_auth_parameters(orig_ss,ss); } if(check_stun_auth(server, ss, tid, resp_constructed, err_code, reason, in_buffer, nbh, STUN_METHOD_REFRESH, &message_integrity, &postpone_reply, can_resume)<0) { if(!(*err_code)) { *err_code = 401; } } if(postpone_reply) { *no_response = 1; } else if(!(*err_code)) { //Session transfer: if (to_delete) lifetime = 0; else { lifetime = stun_adjust_allocate_lifetime(lifetime, *(server->max_allocate_lifetime), ss->max_session_time_auth); } if (af4c && refresh_relay_connection(server, orig_ss, lifetime, 0, 0, 0, err_code, AF_INET) < 0) { if (!(*err_code)) { *err_code = 437; *reason = (const u08bits *)"Cannot refresh relay connection (internal error)"; } } else if (af6c && refresh_relay_connection(server, orig_ss, lifetime, 0, 0, 0, err_code, AF_INET6) < 0) { if (!(*err_code)) { *err_code = 437; *reason = (const u08bits *)"Cannot refresh relay connection (internal error)"; } } else { //Transfer socket: ioa_socket_handle s = detach_ioa_socket(ss->client_socket); ss->to_be_closed = 1; if(!s) { *err_code = 500; } else { if(attach_socket_to_session(server, s, orig_ss) < 0) { if(orig_ss->client_socket != s) { IOA_CLOSE_SOCKET(s); } *err_code = 500; } else { if(ss->hmackey_set) { copy_auth_parameters(ss,orig_ss); } delete_session_from_mobile_map(ss); delete_session_from_mobile_map(orig_ss); put_session_into_mobile_map(orig_ss); //Use new buffer and redefine ss: nbh = ioa_network_buffer_allocate(server->e); dec_quota(ss); ss = orig_ss; inc_quota(ss,ss->username); ss->old_mobile_id = mid; size_t len = ioa_network_buffer_get_size(nbh); turn_report_allocation_set(&(ss->alloc), lifetime, 1); stun_init_success_response_str(STUN_METHOD_REFRESH, ioa_network_buffer_data(nbh), &len, tid); u32bits lt = nswap32(lifetime); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_LIFETIME, (const u08bits*) <, 4); ioa_network_buffer_set_size(nbh,len); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_MOBILITY_TICKET, (u08bits*)ss->s_mobile_id,strlen(ss->s_mobile_id)); ioa_network_buffer_set_size(nbh,len); { const u08bits *field = (const u08bits *) get_version(server); size_t fsz = strlen(get_version(server)); size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_SOFTWARE, field, fsz); ioa_network_buffer_set_size(nbh, len); } if(message_integrity) { stun_attr_add_integrity_str(server->ct,ioa_network_buffer_data(nbh),&len,ss->hmackey,ss->pwd,SHATYPE_DEFAULT); ioa_network_buffer_set_size(nbh,len); } if ((server->fingerprint) || ss->enforce_fingerprints) { if (stun_attr_add_fingerprint_str(ioa_network_buffer_data(nbh), &len) < 0) { *err_code = 500; ioa_network_buffer_delete(server->e, nbh); return -1; } ioa_network_buffer_set_size(nbh, len); } *no_response = 1; return write_client_connection(server, ss, nbh, TTL_IGNORE, TOS_IGNORE); } } } } report_turn_session_info(server,orig_ss,0); } } } else { *err_code = 437; *reason = (const u08bits *)"Invalid allocation"; } } else { if (to_delete) lifetime = 0; else { lifetime = stun_adjust_allocate_lifetime(lifetime, *(server->max_allocate_lifetime), ss->max_session_time_auth); } if(!af4 && !af6) { af4 = af4c; af6 = af6c; } if (af4 && refresh_relay_connection(server, ss, lifetime, 0, 0, 0, err_code, AF_INET) < 0) { if (!(*err_code)) { *err_code = 437; *reason = (const u08bits *)"Cannot refresh relay connection (internal error)"; } } else if (af6 && refresh_relay_connection(server, ss, lifetime, 0, 0, 0, err_code, AF_INET6) < 0) { if (!(*err_code)) { *err_code = 437; *reason = (const u08bits *)"Cannot refresh relay connection (internal error)"; } } else { turn_report_allocation_set(&(ss->alloc), lifetime, 1); size_t len = ioa_network_buffer_get_size(nbh); stun_init_success_response_str(STUN_METHOD_REFRESH, ioa_network_buffer_data(nbh), &len, tid); if(ss->s_mobile_id[0]) { stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_MOBILITY_TICKET, (u08bits*)ss->s_mobile_id,strlen(ss->s_mobile_id)); ioa_network_buffer_set_size(nbh,len); } u32bits lt = nswap32(lifetime); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_LIFETIME, (const u08bits*) <, 4); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } } } if(!no_response) { if (!(*resp_constructed)) { if (!(*err_code)) { *err_code = 437; } size_t len = ioa_network_buffer_get_size(nbh); stun_init_error_response_str(STUN_METHOD_REFRESH, ioa_network_buffer_data(nbh), &len, *err_code, *reason, tid); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } } return 0; } /* RFC 6062 ==>> */ static void tcp_deliver_delayed_buffer(unsent_buffer *ub, ioa_socket_handle s, ts_ur_super_session *ss) { if(ub && s && ub->bufs && ub->sz && ss) { size_t i = 0; do { ioa_network_buffer_handle nbh = top_unsent_buffer(ub); if(!nbh) break; u32bits bytes = (u32bits)ioa_network_buffer_get_size(nbh); int ret = send_data_from_ioa_socket_nbh(s, NULL, nbh, TTL_IGNORE, TOS_IGNORE, NULL); if (ret < 0) { set_ioa_socket_tobeclosed(s); } else { ++(ss->sent_packets); ss->sent_bytes += bytes; turn_report_session_usage(ss, 0); } pop_unsent_buffer(ub); } while(!ioa_socket_tobeclosed(s) && ((i++)owner; if(a) { ss=(ts_ur_super_session*)a->owner; } if((tc->state != TC_STATE_READY) || !(tc->client_s)) { add_unsent_buffer(&(tc->ub_to_client), in_buffer->nbh); in_buffer->nbh = NULL; return; } ioa_network_buffer_handle nbh = in_buffer->nbh; in_buffer->nbh = NULL; u32bits bytes = (u32bits)ioa_network_buffer_get_size(nbh); int ret = send_data_from_ioa_socket_nbh(tc->client_s, NULL, nbh, TTL_IGNORE, TOS_IGNORE, NULL); if (ret < 0) { set_ioa_socket_tobeclosed(s); } else if(ss) { ++(ss->sent_packets); ss->sent_bytes += bytes; turn_report_session_usage(ss, 0); } } static void tcp_client_input_handler_rfc6062data(ioa_socket_handle s, int event_type, ioa_net_data *in_buffer, void *arg, int can_resume) { if (!(event_type & IOA_EV_READ) || !arg) return; UNUSED_ARG(s); UNUSED_ARG(can_resume); tcp_connection *tc = (tcp_connection*)arg; ts_ur_super_session *ss=NULL; allocation *a=(allocation*)tc->owner; if(a) { ss=(ts_ur_super_session*)a->owner; } if(tc->state != TC_STATE_READY) return; if(!(tc->peer_s)) return; ioa_network_buffer_handle nbh = in_buffer->nbh; in_buffer->nbh = NULL; if(ss) { u32bits bytes = (u32bits)ioa_network_buffer_get_size(nbh); ++(ss->received_packets); ss->received_bytes += bytes; } int ret = send_data_from_ioa_socket_nbh(tc->peer_s, NULL, nbh, TTL_IGNORE, TOS_IGNORE, NULL); if (ret < 0) { set_ioa_socket_tobeclosed(s); } turn_report_session_usage(ss, 0); } static void tcp_conn_bind_timeout_handler(ioa_engine_handle e, void *arg) { UNUSED_ARG(e); if(arg) { tcp_connection *tc = (tcp_connection *)arg; delete_tcp_connection(tc); } } static void tcp_peer_connection_completed_callback(int success, void *arg) { if(arg) { tcp_connection *tc = (tcp_connection *)arg; allocation *a = (allocation*)(tc->owner); ts_ur_super_session *ss = (ts_ur_super_session*)(a->owner); turn_turnserver *server=(turn_turnserver*)(ss->server); int err_code = 0; IOA_EVENT_DEL(tc->peer_conn_timeout); ioa_network_buffer_handle nbh = ioa_network_buffer_allocate(server->e); size_t len = ioa_network_buffer_get_size(nbh); if(success) { if(register_callback_on_ioa_socket(server->e, tc->peer_s, IOA_EV_READ, tcp_peer_input_handler, tc, 1)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: cannot set TCP peer data input callback\n", __FUNCTION__); success=0; err_code = 500; } } if(success) { tc->state = TC_STATE_PEER_CONNECTED; stun_init_success_response_str(STUN_METHOD_CONNECT, ioa_network_buffer_data(nbh), &len, &(tc->tid)); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_CONNECTION_ID, (const u08bits*)&(tc->id), 4); IOA_EVENT_DEL(tc->conn_bind_timeout); tc->conn_bind_timeout = set_ioa_timer(server->e, TCP_CONN_BIND_TIMEOUT, 0, tcp_conn_bind_timeout_handler, tc, 0, "tcp_conn_bind_timeout_handler"); } else { tc->state = TC_STATE_FAILED; if(!err_code) { err_code = 447; } { char ls[257]="\0"; char rs[257]="\0"; ioa_addr *laddr = get_local_addr_from_ioa_socket(ss->client_socket); if(laddr) addr_to_string(laddr,(u08bits*)ls); addr_to_string(&(tc->peer_addr),(u08bits*)rs); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: failure to connect from %s to %s\n", __FUNCTION__, ls,rs); } stun_init_error_response_str(STUN_METHOD_CONNECT, ioa_network_buffer_data(nbh), &len, err_code, NULL, &(tc->tid)); } ioa_network_buffer_set_size(nbh,len); if(need_stun_authentication(server, ss)) { stun_attr_add_integrity_str(server->ct,ioa_network_buffer_data(nbh),&len,ss->hmackey,ss->pwd,SHATYPE_DEFAULT); ioa_network_buffer_set_size(nbh,len); } write_client_connection(server, ss, nbh, TTL_IGNORE, TOS_IGNORE); if(!success) { delete_tcp_connection(tc); } /* test */ else if(0) { int i = 0; for(i=0;i<22;i++) { ioa_network_buffer_handle nbh_test = ioa_network_buffer_allocate(server->e); size_t len_test = ioa_network_buffer_get_size(nbh_test); u08bits *data = ioa_network_buffer_data(nbh_test); const char* data_test="111.111.111.111.111"; len_test = strlen(data_test); ns_bcopy(data_test,data,len_test); ioa_network_buffer_set_size(nbh_test,len_test); send_data_from_ioa_socket_nbh(tc->peer_s, NULL, nbh_test, TTL_IGNORE, TOS_IGNORE, NULL); } } } } static void tcp_peer_conn_timeout_handler(ioa_engine_handle e, void *arg) { UNUSED_ARG(e); tcp_peer_connection_completed_callback(0,arg); } static int tcp_start_connection_to_peer(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, allocation *a, ioa_addr *peer_addr, int *err_code, const u08bits **reason) { FUNCSTART; if(!ss) { *err_code = 500; *reason = (const u08bits *)"Server error: empty session"; FUNCEND; return -1; } if(!peer_addr) { *err_code = 500; *reason = (const u08bits *)"Server error: empty peer addr"; FUNCEND; return -1; } if(!get_relay_socket(a,peer_addr->ss.sa_family)) { *err_code = 500; *reason = (const u08bits *)"Server error: no relay connection created"; FUNCEND; return -1; } tcp_connection *tc = get_tcp_connection_by_peer(a, peer_addr); if(tc) { *err_code = 446; FUNCEND; return -1; } tc = create_tcp_connection(server->id, a, tid, peer_addr, err_code); if(!tc) { if(!(*err_code)) { *err_code = 500; *reason = (const u08bits *)"Server error: TCP connection object creation failed"; } FUNCEND; return -1; } else if(*err_code) { delete_tcp_connection(tc); FUNCEND; return -1; } IOA_EVENT_DEL(tc->peer_conn_timeout); tc->peer_conn_timeout = set_ioa_timer(server->e, TCP_PEER_CONN_TIMEOUT, 0, tcp_peer_conn_timeout_handler, tc, 0, "tcp_peer_conn_timeout_handler"); ioa_socket_handle tcs = ioa_create_connecting_tcp_relay_socket(get_relay_socket(a,peer_addr->ss.sa_family), peer_addr, tcp_peer_connection_completed_callback, tc); if(!tcs) { delete_tcp_connection(tc); *err_code = 500; *reason = (const u08bits *)"Server error: TCP relay socket for connection cannot be created"; FUNCEND; return -1; } tc->state = TC_STATE_CLIENT_TO_PEER_CONNECTING; if(tc->peer_s != tcs) { IOA_CLOSE_SOCKET(tc->peer_s); tc->peer_s = tcs; } set_ioa_socket_sub_session(tc->peer_s,tc); FUNCEND; return 0; } static void tcp_peer_accept_connection(ioa_socket_handle s, void *arg) { if(s) { if(!arg) { close_ioa_socket(s); return; } ts_ur_super_session *ss = (ts_ur_super_session*)arg; turn_turnserver *server=(turn_turnserver*)(ss->server); FUNCSTART; allocation *a = &(ss->alloc); ioa_addr *peer_addr = get_remote_addr_from_ioa_socket(s); if(!peer_addr) { close_ioa_socket(s); FUNCEND; return; } tcp_connection *tc = get_tcp_connection_by_peer(a, peer_addr); if(tc) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: peer data socket with this address already exist\n", __FUNCTION__); if(tc->peer_s != s) close_ioa_socket(s); FUNCEND; return; } if(!good_peer_addr(server, ss->realm_options.name, peer_addr)) { u08bits saddr[256]; addr_to_string(peer_addr, saddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: an attempt to connect from a peer with forbidden address: %s\n", __FUNCTION__,saddr); close_ioa_socket(s); FUNCEND; return; } if(!can_accept_tcp_connection_from_peer(a,peer_addr,server->server_relay)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: peer has no permission to connect\n", __FUNCTION__); close_ioa_socket(s); FUNCEND; return; } stun_tid tid; ns_bzero(&tid,sizeof(stun_tid)); int err_code=0; tc = create_tcp_connection(server->id, a, &tid, peer_addr, &err_code); if(!tc) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: cannot create TCP connection\n", __FUNCTION__); close_ioa_socket(s); FUNCEND; return; } tc->state = TC_STATE_PEER_CONNECTED; tc->peer_s = s; set_ioa_socket_session(s,ss); set_ioa_socket_sub_session(s,tc); if(register_callback_on_ioa_socket(server->e, s, IOA_EV_READ, tcp_peer_input_handler, tc, 1)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: cannot set TCP peer data input callback\n", __FUNCTION__); IOA_CLOSE_SOCKET(tc->peer_s); tc->state = TC_STATE_UNKNOWN; FUNCEND; return; } IOA_EVENT_DEL(tc->conn_bind_timeout); tc->conn_bind_timeout = set_ioa_timer(server->e, TCP_CONN_BIND_TIMEOUT, 0, tcp_conn_bind_timeout_handler, tc, 0, "tcp_conn_bind_timeout_handler"); ioa_network_buffer_handle nbh = ioa_network_buffer_allocate(server->e); size_t len = ioa_network_buffer_get_size(nbh); stun_init_indication_str(STUN_METHOD_CONNECTION_ATTEMPT, ioa_network_buffer_data(nbh), &len); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_CONNECTION_ID, (const u08bits*)&(tc->id), 4); stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, peer_addr); ioa_network_buffer_set_size(nbh,len); { const u08bits *field = (const u08bits *) get_version(server); size_t fsz = strlen(get_version(server)); size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_SOFTWARE, field, fsz); ioa_network_buffer_set_size(nbh, len); } if ((server->fingerprint) || ss->enforce_fingerprints) { size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_fingerprint_str(ioa_network_buffer_data(nbh), &len); ioa_network_buffer_set_size(nbh, len); } write_client_connection(server, ss, nbh, TTL_IGNORE, TOS_IGNORE); FUNCEND; } } static int handle_turn_connect(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer) { FUNCSTART; ioa_addr peer_addr; int peer_found = 0; addr_set_any(&peer_addr); allocation* a = get_allocation_ss(ss); if(!(ss->is_tcp_relay)) { *err_code = 403; *reason = (const u08bits *)"Connect cannot be used with UDP relay"; } else if (!is_allocation_valid(a)) { *err_code = 437; } else { stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: { if(stun_attr_get_addr_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar, &peer_addr, NULL) == -1) { *err_code = 400; *reason = (const u08bits *)"Bad Peer Address"; } else { if(!get_relay_socket(a,peer_addr.ss.sa_family)) { *err_code = 443; *reason = (const u08bits *)"Peer Address Family Mismatch (2)"; } peer_found = 1; } break; } default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if (*ua_num > 0) { *err_code = 420; } else if (*err_code) { ; } else if (!peer_found) { *err_code = 400; *reason = (const u08bits *)"Where is Peer Address ?"; } else { if(!good_peer_addr(server,ss->realm_options.name,&peer_addr)) { *err_code = 403; *reason = (const u08bits *) "Forbidden IP"; } else { tcp_start_connection_to_peer(server, ss, tid, a, &peer_addr, err_code, reason); } } } FUNCEND; return 0; } static int handle_turn_connection_bind(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, int message_integrity, int can_resume) { allocation* a = get_allocation_ss(ss); u16bits method = STUN_METHOD_CONNECTION_BIND; if(ss->to_be_closed) { *err_code = 400; } else if (is_allocation_valid(a)) { *err_code = 400; *reason = (const u08bits *)"Bad request: CONNECTION_BIND cannot be issued after allocation"; } else if(!is_stream_socket(get_ioa_socket_type(ss->client_socket))) { *err_code = 400; *reason = (const u08bits *)"Bad request: CONNECTION_BIND only possible with TCP/TLS"; } else { tcp_connection_id id = 0; stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_CONNECTION_ID: { if (stun_attr_get_len(sar) != 4) { *err_code = 400; *reason = (const u08bits *)"Wrong Connection ID field format"; } else { const u08bits* value = stun_attr_get_value(sar); if (!value) { *err_code = 400; *reason = (const u08bits *)"Wrong Connection ID field data"; } else { id = *((const u32bits*)value); //AS-IS encoding, no conversion to/from network byte order } } } break; default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if (*ua_num > 0) { *err_code = 420; } else if (*err_code) { ; } else { if(server->send_socket_to_relay) { turnserver_id sid = (id & 0xFF000000)>>24; ioa_socket_handle s = ss->client_socket; if(s && !ioa_socket_tobeclosed(s)) { ioa_socket_handle new_s = detach_ioa_socket(s); if(new_s) { if(server->send_socket_to_relay(sid, id, tid, new_s, message_integrity, RMT_CB_SOCKET, in_buffer, can_resume)<0) { *err_code = 400; *reason = (const u08bits *)"Wrong connection id"; } } else { *err_code = 500; } } else { *err_code = 500; } } else { *err_code = 500; } ss->to_be_closed = 1; } } if (!(*resp_constructed) && ss->client_socket && !ioa_socket_tobeclosed(ss->client_socket)) { if (!(*err_code)) { *err_code = 437; } size_t len = ioa_network_buffer_get_size(nbh); stun_init_error_response_str(method, ioa_network_buffer_data(nbh), &len, *err_code, *reason, tid); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } return 0; } int turnserver_accept_tcp_client_data_connection(turn_turnserver *server, tcp_connection_id tcid, stun_tid *tid, ioa_socket_handle s, int message_integrity, ioa_net_data *in_buffer, int can_resume) { if(!server) return -1; FUNCSTART; tcp_connection *tc = NULL; ts_ur_super_session *ss = NULL; int err_code = 0; const u08bits *reason = NULL; ioa_socket_handle s_to_delete = s; if(tcid && tid && s) { tc = get_tcp_connection_by_id(server->tcp_relay_connections, tcid); ioa_network_buffer_handle nbh = ioa_network_buffer_allocate(server->e); int resp_constructed = 0; if(!tc || (tc->state == TC_STATE_READY) || (tc->client_s)) { err_code = 400; } else { allocation *a = (allocation*)(tc->owner); if(!a || !(a->owner)) { err_code = 500; } else { ss = (ts_ur_super_session*)(a->owner); if(ss->to_be_closed || ioa_socket_tobeclosed(ss->client_socket)) { err_code = 404; } else { //Check security: int postpone_reply = 0; check_stun_auth(server, ss, tid, &resp_constructed, &err_code, &reason, in_buffer, nbh, STUN_METHOD_CONNECTION_BIND, &message_integrity, &postpone_reply, can_resume); if(postpone_reply) { ioa_network_buffer_delete(server->e, nbh); return 0; } else if(!err_code) { tc->state = TC_STATE_READY; tc->client_s = s; s_to_delete = NULL; set_ioa_socket_session(s,ss); set_ioa_socket_sub_session(s,tc); set_ioa_socket_app_type(s,TCP_CLIENT_DATA_SOCKET); if(register_callback_on_ioa_socket(server->e, s, IOA_EV_READ, tcp_client_input_handler_rfc6062data, tc, 1)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: cannot set TCP client data input callback\n", __FUNCTION__); err_code = 500; } else { IOA_EVENT_DEL(tc->conn_bind_timeout); } } } } } if(tc) get_and_clean_tcp_connection_by_id(server->tcp_relay_connections, tcid); if(!resp_constructed) { if(!err_code) { size_t len = ioa_network_buffer_get_size(nbh); stun_init_success_response_str(STUN_METHOD_CONNECTION_BIND, ioa_network_buffer_data(nbh), &len, tid); ioa_network_buffer_set_size(nbh,len); } else { size_t len = ioa_network_buffer_get_size(nbh); stun_init_error_response_str(STUN_METHOD_CONNECTION_BIND, ioa_network_buffer_data(nbh), &len, err_code, NULL, tid); ioa_network_buffer_set_size(nbh,len); } } { size_t fsz = strlen(get_version(server)); const u08bits *field = (const u08bits *) get_version(server); size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_SOFTWARE, field, fsz); ioa_network_buffer_set_size(nbh, len); } if(message_integrity && ss) { size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_integrity_str(server->ct,ioa_network_buffer_data(nbh),&len,ss->hmackey,ss->pwd,SHATYPE_DEFAULT); ioa_network_buffer_set_size(nbh,len); } if ((server->fingerprint) || (ss &&(ss->enforce_fingerprints))) { size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_fingerprint_str(ioa_network_buffer_data(nbh), &len); ioa_network_buffer_set_size(nbh, len); } if(server->verbose) { log_method(ss, "CONNECTION_BIND", err_code, reason); } if(ss && !err_code) { send_data_from_ioa_socket_nbh(s, NULL, nbh, TTL_IGNORE, TOS_IGNORE, NULL); tcp_deliver_delayed_buffer(&(tc->ub_to_client),s,ss); IOA_CLOSE_SOCKET(s_to_delete); FUNCEND; return 0; } else { /* Just to set the necessary structures for the packet sending: */ if(register_callback_on_ioa_socket(server->e, s, IOA_EV_READ, NULL, NULL, 1)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: cannot set TCP tmp client data input callback\n", __FUNCTION__); ioa_network_buffer_delete(server->e, nbh); } else { send_data_from_ioa_socket_nbh(s, NULL, nbh, TTL_IGNORE, TOS_IGNORE, NULL); } } } IOA_CLOSE_SOCKET(s_to_delete); FUNCEND; return -1; } /* <<== RFC 6062 */ static int handle_turn_channel_bind(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh) { FUNCSTART; u16bits chnum = 0; ioa_addr peer_addr; addr_set_any(&peer_addr); allocation* a = get_allocation_ss(ss); int addr_found = 0; if(ss->is_tcp_relay) { *err_code = 403; *reason = (const u08bits *)"Channel bind cannot be used with TCP relay"; } else if (is_allocation_valid(a)) { stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_CHANNEL_NUMBER: { if (chnum) { chnum = 0; *err_code = 400; *reason = (const u08bits *)"Channel number cannot be duplicated in this request"; break; } chnum = stun_attr_get_channel_number(sar); if (!chnum) { *err_code = 400; *reason = (const u08bits *)"Channel number cannot be zero in this request"; break; } } break; case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: { stun_attr_get_addr_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar, &peer_addr, NULL); if(!get_relay_socket(a,peer_addr.ss.sa_family)) { *err_code = 443; *reason = (const u08bits *)"Peer Address Family Mismatch (3)"; } if(addr_get_port(&peer_addr) < 1) { *err_code = 400; *reason = (const u08bits *)"Empty port number in channel bind request"; } else { addr_found = 1; } break; } default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if (*ua_num > 0) { *err_code = 420; } else if (*err_code) { ; } else if (!chnum || addr_any(&peer_addr) || !addr_found) { *err_code = 400; *reason = (const u08bits *)"Bad channel bind request"; } else if(!STUN_VALID_CHANNEL(chnum)) { *err_code = 400; *reason = (const u08bits *)"Bad channel number"; } else { ch_info* chn = allocation_get_ch_info(a, chnum); turn_permission_info* tinfo = NULL; if (chn) { if (!addr_eq(&peer_addr, &(chn->peer_addr))) { *err_code = 400; *reason = (const u08bits *)"You cannot use the same channel number with different peer"; } else { tinfo = (turn_permission_info*) (chn->owner); if (!tinfo) { *err_code = 500; *reason = (const u08bits *)"Wrong permission info"; } else { if (!addr_eq_no_port(&peer_addr, &(tinfo->addr))) { *err_code = 500; *reason = (const u08bits *)"Wrong permission info and peer addr combination"; } else if (chn->port != addr_get_port(&peer_addr)) { *err_code = 500; *reason = (const u08bits *)"Wrong port number"; } } } } else { chn = allocation_get_ch_info_by_peer_addr(a, &peer_addr); if(chn) { *err_code = 400; *reason = (const u08bits *)"You cannot use the same peer with different channel number"; } else { if(!good_peer_addr(server,ss->realm_options.name,&peer_addr)) { *err_code = 403; *reason = (const u08bits *) "Forbidden IP"; } else { chn = allocation_get_new_ch_info(a, chnum, &peer_addr); if (!chn) { *err_code = 500; *reason = (const u08bits *) "Cannot find channel data"; } else { tinfo = (turn_permission_info*) (chn->owner); if (!tinfo) { *err_code = 500; *reason = (const u08bits *) "Wrong turn permission info"; } } } } } if (!(*err_code) && chn && tinfo) { if (update_channel_lifetime(ss,chn) < 0) { *err_code = 500; *reason = (const u08bits *)"Cannot update channel lifetime (internal error)"; } else { size_t len = ioa_network_buffer_get_size(nbh); stun_set_channel_bind_response_str(ioa_network_buffer_data(nbh), &len, tid, 0, NULL); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; if(!(ss->is_mobile)) { if(get_ioa_socket_type(ss->client_socket) == UDP_SOCKET || get_ioa_socket_type(ss->client_socket) == TCP_SOCKET || get_ioa_socket_type(ss->client_socket) == SCTP_SOCKET) { if(get_ioa_socket_type(get_relay_socket(&(ss->alloc),peer_addr.ss.sa_family)) == UDP_SOCKET) { chn->kernel_channel = CREATE_TURN_CHANNEL_KERNEL(chn->chnum, get_ioa_socket_address_family(ss->client_socket), peer_addr.ss.sa_family, (get_ioa_socket_type(ss->client_socket)==UDP_SOCKET ? IPPROTO_UDP : IPPROTO_TCP), &(get_remote_addr_from_ioa_socket(ss->client_socket)->ss), &(get_local_addr_from_ioa_socket(ss->client_socket)->ss), &(get_local_addr_from_ioa_socket(get_relay_socket(&(ss->alloc),peer_addr.ss.sa_family))), &(get_remote_addr_from_ioa_socket(get_relay_socket(&(ss->alloc),peer_addr.ss.sa_family))) ); } } } } } } } FUNCEND; return 0; } static int handle_turn_binding(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, int *origin_changed, ioa_addr *response_origin, int *dest_changed, ioa_addr *response_destination, u32bits cookie, int old_stun) { FUNCSTART; int change_ip = 0; int change_port = 0; int padding = 0; int response_port_present = 0; u16bits response_port = 0; SOCKET_TYPE st = get_ioa_socket_type(ss->client_socket); int use_reflected_from = 0; if(!(ss->client_socket)) return -1; *origin_changed = 0; *dest_changed = 0; stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { case OLD_STUN_ATTRIBUTE_PASSWORD: SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_CHANGE_REQUEST: /* * This fix allows the client program from the Stuntman source to make STUN binding requests * to this server. * * It was provided by John Selbie, from STUNTMAN project: * * "Here's the gist of the change. Stuntman comes with a STUN client library * and client program. The client program displays the mapped IP address and * port if it gets back a successful binding response. * It also interops with JSTUN, a Java implementation of STUN. * However, the JSTUN server refuses to respond to any binding request that * doesn't have a CHANGE-REQUEST attribute in it. * ... workaround is for the client to make a request with an empty CHANGE-REQUEST * attribute (neither the ip or port bit are set)." * */ stun_attr_get_change_request_str(sar, &change_ip, &change_port); if( (!is_rfc5780(server)) && (change_ip || change_port)) { *err_code = 420; *reason = (const u08bits *)"Unknown attribute: TURN server was configured without RFC 5780 support"; break; } if(change_ip || change_port) { if(st != UDP_SOCKET) { *err_code = 400; *reason = (const u08bits *)"Wrong request: applicable only to UDP protocol"; } } break; case STUN_ATTRIBUTE_PADDING: if(response_port_present) { *err_code = 400; *reason = (const u08bits *)"Wrong request format: you cannot use PADDING and RESPONSE_PORT together"; } else if((st != UDP_SOCKET) && (st != DTLS_SOCKET)) { *err_code = 400; *reason = (const u08bits *)"Wrong request: padding applicable only to UDP and DTLS protocols"; } else { padding = 1; } break; case STUN_ATTRIBUTE_RESPONSE_PORT: if(padding) { *err_code = 400; *reason = (const u08bits *)"Wrong request format: you cannot use PADDING and RESPONSE_PORT together"; } else if(st != UDP_SOCKET) { *err_code = 400; *reason = (const u08bits *)"Wrong request: applicable only to UDP protocol"; } else { int rp = stun_attr_get_response_port_str(sar); if(rp>=0) { response_port_present = 1; response_port = (u16bits)rp; } else { *err_code = 400; *reason = (const u08bits *)"Wrong response port format"; } } break; case OLD_STUN_ATTRIBUTE_RESPONSE_ADDRESS: if(old_stun) { use_reflected_from = 1; stun_attr_get_addr_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar, response_destination, response_destination); } break; default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if (*ua_num > 0) { *err_code = 420; } else if (*err_code) { ; } else if(ss->client_socket && get_remote_addr_from_ioa_socket(ss->client_socket)) { size_t len = ioa_network_buffer_get_size(nbh); if (stun_set_binding_response_str(ioa_network_buffer_data(nbh), &len, tid, get_remote_addr_from_ioa_socket(ss->client_socket), 0, NULL, cookie, old_stun) >= 0) { addr_cpy(response_origin, get_local_addr_from_ioa_socket(ss->client_socket)); *resp_constructed = 1; if(old_stun && use_reflected_from) { stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, OLD_STUN_ATTRIBUTE_REFLECTED_FROM, get_remote_addr_from_ioa_socket(ss->client_socket)); } if(!is_rfc5780(server)) { if(old_stun) { stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, OLD_STUN_ATTRIBUTE_SOURCE_ADDRESS, response_origin); stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, OLD_STUN_ATTRIBUTE_CHANGED_ADDRESS, response_origin); } else { stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_RESPONSE_ORIGIN, response_origin); } } else if(ss->client_socket) { ioa_addr other_address; if(get_other_address(server,ss,&other_address) == 0) { addr_cpy(response_destination, get_remote_addr_from_ioa_socket(ss->client_socket)); if(change_ip) { *origin_changed = 1; if(change_port) { addr_cpy(response_origin,&other_address); } else { int old_port = addr_get_port(response_origin); addr_cpy(response_origin,&other_address); addr_set_port(response_origin,old_port); } } else if(change_port) { *origin_changed = 1; addr_set_port(response_origin,addr_get_port(&other_address)); } if(old_stun) { stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, OLD_STUN_ATTRIBUTE_SOURCE_ADDRESS, response_origin); stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, OLD_STUN_ATTRIBUTE_CHANGED_ADDRESS, &other_address); } else { stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_RESPONSE_ORIGIN, response_origin); stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_OTHER_ADDRESS, &other_address); } if(response_port_present) { *dest_changed = 1; addr_set_port(response_destination, (int)response_port); } if(padding) { int mtu = get_local_mtu_ioa_socket(ss->client_socket); if(mtu<68) mtu=1500; mtu = (mtu >> 2) << 2; stun_attr_add_padding_str(ioa_network_buffer_data(nbh), &len, (u16bits)mtu); } } } } ioa_network_buffer_set_size(nbh, len); } FUNCEND; return 0; } static int handle_turn_send(turn_turnserver *server, ts_ur_super_session *ss, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer) { FUNCSTART; ioa_addr peer_addr; const u08bits* value = NULL; int len = -1; int addr_found = 0; int set_df = 0; addr_set_any(&peer_addr); allocation* a = get_allocation_ss(ss); if(ss->is_tcp_relay) { *err_code = 403; *reason = (const u08bits *)"Send cannot be used with TCP relay"; } else if (is_allocation_valid(a) && (in_buffer->recv_ttl != 0)) { stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_DONT_FRAGMENT: if(!(server->dont_fragment)) unknown_attrs[(*ua_num)++] = nswap16(attr_type); else set_df = 1; break; case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: { if (addr_found) { *err_code = 400; *reason = (const u08bits *)"Address duplication"; } else { stun_attr_get_addr_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar, &peer_addr, NULL); } } break; case STUN_ATTRIBUTE_DATA: { if (len >= 0) { *err_code = 400; *reason = (const u08bits *)"Data duplication"; } else { len = stun_attr_get_len(sar); value = stun_attr_get_value(sar); } } break; default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if (*err_code) { ; } else if (*ua_num > 0) { *err_code = 420; } else if (!addr_any(&peer_addr) && len >= 0) { turn_permission_info* tinfo = NULL; if(!(server->server_relay)) tinfo = allocation_get_permission(a, &peer_addr); if (tinfo || (server->server_relay)) { set_df_on_ioa_socket(get_relay_socket_ss(ss,peer_addr.ss.sa_family), set_df); ioa_network_buffer_handle nbh = in_buffer->nbh; if(value && len>0) { u16bits offset = (u16bits)(value - ioa_network_buffer_data(nbh)); ioa_network_buffer_add_offset_size(nbh,offset,0,len); } else { len = 0; ioa_network_buffer_set_size(nbh,len); } ioa_network_buffer_header_init(nbh); send_data_from_ioa_socket_nbh(get_relay_socket_ss(ss,peer_addr.ss.sa_family), &peer_addr, nbh, in_buffer->recv_ttl-1, in_buffer->recv_tos, NULL); in_buffer->nbh = NULL; } } else { *err_code = 400; *reason = (const u08bits *)"No address found"; } } FUNCEND; return 0; } static int update_permission(ts_ur_super_session *ss, ioa_addr *peer_addr) { if (!ss || !peer_addr) return -1; allocation* a = get_allocation_ss(ss); turn_permission_info* tinfo = allocation_get_permission(a, peer_addr); if (!tinfo) { tinfo = allocation_add_permission(a, peer_addr); } if (!tinfo) return -1; if (update_turn_permission_lifetime(ss, tinfo, 0) < 0) return -1; ch_info *chn = get_turn_channel(tinfo, peer_addr); if(chn) { if (update_channel_lifetime(ss, chn) < 0) return -1; } return 0; } static int handle_turn_create_permission(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh) { int ret = -1; int addr_found = 0; UNUSED_ARG(server); allocation* a = get_allocation_ss(ss); if (is_allocation_valid(a)) { { stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar && (!(*err_code)) && (*ua_num < MAX_NUMBER_OF_UNKNOWN_ATTRS)) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: { ioa_addr peer_addr; addr_set_any(&peer_addr); stun_attr_get_addr_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar, &peer_addr, NULL); if(!get_relay_socket(a,peer_addr.ss.sa_family)) { *err_code = 443; *reason = (const u08bits *)"Peer Address Family Mismatch (4)"; } else if(!good_peer_addr(server, ss->realm_options.name, &peer_addr)) { *err_code = 403; *reason = (const u08bits *) "Forbidden IP"; } else { addr_found++; } } break; default: if(attr_type>=0x0000 && attr_type<=0x7FFF) unknown_attrs[(*ua_num)++] = nswap16(attr_type); }; sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } } if (*ua_num > 0) { *err_code = 420; } else if (*err_code) { ; } else if (!addr_found) { *err_code = 400; *reason = (const u08bits *)"No address found"; } else { stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); while (sar) { int attr_type = stun_attr_get_type(sar); switch (attr_type) { SKIP_ATTRIBUTES; case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: { ioa_addr peer_addr; addr_set_any(&peer_addr); stun_attr_get_addr_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar, &peer_addr, NULL); addr_set_port(&peer_addr, 0); if (update_permission(ss, &peer_addr) < 0) { *err_code = 500; *reason = (const u08bits *)"Cannot update some permissions (critical server software error)"; } } break; default: ; } sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if(*err_code == 0) { size_t len = ioa_network_buffer_get_size(nbh); stun_init_success_response_str(STUN_METHOD_CREATE_PERMISSION, ioa_network_buffer_data(nbh), &len, tid); ioa_network_buffer_set_size(nbh,len); ret = 0; *resp_constructed = 1; } } } return ret; } // AUTH ==>> static int need_stun_authentication(turn_turnserver *server, ts_ur_super_session *ss) { UNUSED_ARG(ss); if(server) { switch(server->ct) { case TURN_CREDENTIALS_LONG_TERM: return 1; default: ; }; } return 0; } static int create_challenge_response(ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, ioa_network_buffer_handle nbh, u16bits method) { size_t len = ioa_network_buffer_get_size(nbh); stun_init_error_response_str(method, ioa_network_buffer_data(nbh), &len, *err_code, *reason, tid); *resp_constructed = 1; stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_NONCE, ss->nonce, (int)(NONCE_MAX_SIZE-1)); char *realm = ss->realm_options.name; stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_REALM, (u08bits*)realm, (int)(strlen((s08bits*)(realm)))); if(ss->server) { turn_turnserver* server = (turn_turnserver*)ss->server; if(server->oauth) { const char *server_name = server->oauth_server_name; if(!(server_name && server_name[0])) { server_name = realm; } stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_THIRD_PARTY_AUTHORIZATION, (const u08bits*)(server_name), strlen(server_name)); } } ioa_network_buffer_set_size(nbh,len); return 0; } #if !defined(min) #define min(a,b) ((a)<=(b) ? (a) : (b)) #endif static void resume_processing_after_username_check(int success, int oauth, int max_session_time, hmackey_t hmackey, password_t pwd, turn_turnserver *server, u64bits ctxkey, ioa_net_data *in_buffer, u08bits *realm) { if(server && in_buffer && in_buffer->nbh) { ts_ur_super_session *ss = get_session_from_map(server,(turnsession_id)ctxkey); if(ss && ss->client_socket) { turn_turnserver *server = (turn_turnserver *)ss->server; if(success) { ns_bcopy(hmackey,ss->hmackey,sizeof(hmackey_t)); ss->hmackey_set = 1; ss->oauth = oauth; ss->max_session_time_auth = (turn_time_t)max_session_time; ns_bcopy(pwd,ss->pwd,sizeof(password_t)); if(realm && realm[0] && strcmp((char*)realm,ss->realm_options.name)) { dec_quota(ss); get_realm_options_by_name((char*)realm, &(ss->realm_options)); inc_quota(ss,ss->username); } } read_client_connection(server,ss,in_buffer,0,0); close_ioa_socket_after_processing_if_necessary(ss->client_socket); ioa_network_buffer_delete(server->e, in_buffer->nbh); in_buffer->nbh=NULL; } } } static int check_stun_auth(turn_turnserver *server, ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, u16bits method, int *message_integrity, int *postpone_reply, int can_resume) { u08bits usname[STUN_MAX_USERNAME_SIZE+1]; u08bits nonce[STUN_MAX_NONCE_SIZE+1]; u08bits realm[STUN_MAX_REALM_SIZE+1]; size_t alen = 0; if(!need_stun_authentication(server, ss)) return 0; int new_nonce = 0; { int generate_new_nonce = 0; if(ss->nonce[0]==0) { generate_new_nonce = 1; new_nonce = 1; } if(*(server->stale_nonce)) { if(turn_time_before(ss->nonce_expiration_time,server->ctime)) { generate_new_nonce = 1; } } if(generate_new_nonce) { int i = 0; if(TURN_RANDOM_SIZE == 8) { for(i=0;i<(NONCE_LENGTH_32BITS>>1);i++) { u08bits *s = ss->nonce + 8*i; u64bits rand=(u64bits)turn_random(); snprintf((s08bits*)s, NONCE_MAX_SIZE-8*i, "%08lx",(unsigned long)rand); } } else { for(i=0;inonce + 4*i; u32bits rand=(u32bits)turn_random(); snprintf((s08bits*)s, NONCE_MAX_SIZE-4*i, "%04x",(unsigned int)rand); } } ss->nonce_expiration_time = server->ctime + *(server->stale_nonce); } } /* MESSAGE_INTEGRITY ATTR: */ stun_attr_ref sar = stun_attr_get_first_by_type_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), STUN_ATTRIBUTE_MESSAGE_INTEGRITY); if(!sar) { *err_code = 401; return create_challenge_response(ss,tid,resp_constructed,err_code,reason,nbh,method); } { int sarlen = stun_attr_get_len(sar); switch(sarlen) { case SHA1SIZEBYTES: break; case SHA256SIZEBYTES: case SHA384SIZEBYTES: case SHA512SIZEBYTES: default: *err_code = 401; return create_challenge_response(ss,tid,resp_constructed,err_code,reason,nbh,method); }; } { /* REALM ATTR: */ sar = stun_attr_get_first_by_type_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), STUN_ATTRIBUTE_REALM); if(!sar) { *err_code = 400; return -1; } alen = min((size_t)stun_attr_get_len(sar),sizeof(realm)-1); ns_bcopy(stun_attr_get_value(sar),realm,alen); realm[alen]=0; if(!is_secure_string(realm,0)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: wrong realm: %s\n", __FUNCTION__, (char*)realm); realm[0]=0; *err_code = 400; return -1; } if(method == STUN_METHOD_CONNECTION_BIND) { get_realm_options_by_name((char *)realm, &(ss->realm_options)); } else if(strcmp((char*)realm, (char*)(ss->realm_options.name))) { if(!(ss->oauth)){ if(method == STUN_METHOD_ALLOCATE) { *err_code = 437; *reason = (const u08bits*)"Allocation mismatch: wrong credentials: the realm value is incorrect"; } else { *err_code = 441; *reason = (const u08bits*)"Wrong credentials: the realm value is incorrect"; } return -1; } else { ns_bcopy(ss->realm_options.name,realm,sizeof(ss->realm_options.name)); } } } /* USERNAME ATTR: */ sar = stun_attr_get_first_by_type_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), STUN_ATTRIBUTE_USERNAME); if(!sar) { *err_code = 400; return -1; } alen = min((size_t)stun_attr_get_len(sar),sizeof(usname)-1); ns_bcopy(stun_attr_get_value(sar),usname,alen); usname[alen]=0; if(!is_secure_string(usname,1)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: wrong username: %s\n", __FUNCTION__, (char*)usname); usname[0]=0; *err_code = 400; return -1; } else if(ss->username[0]) { if(strcmp((char*)ss->username,(char*)usname)) { if(ss->oauth) { ss->hmackey_set = 0; STRCPY(ss->username,usname); } else { if(method == STUN_METHOD_ALLOCATE) { *err_code = 437; *reason = (const u08bits*)"Allocation mismatch: wrong credentials"; } else { *err_code = 441; } return -1; } } } else { STRCPY(ss->username,usname); } { /* NONCE ATTR: */ sar = stun_attr_get_first_by_type_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), STUN_ATTRIBUTE_NONCE); if(!sar) { *err_code = 400; return -1; } alen = min((size_t)stun_attr_get_len(sar),sizeof(nonce)-1); ns_bcopy(stun_attr_get_value(sar),nonce,alen); nonce[alen]=0; /* Stale Nonce check: */ if(new_nonce) { *err_code = 438; *reason = (const u08bits*)"Wrong nonce"; return create_challenge_response(ss,tid,resp_constructed,err_code,reason,nbh,method); } if(strcmp((s08bits*)ss->nonce,(s08bits*)nonce)) { *err_code = 438; *reason = (const u08bits*)"Stale nonce"; return create_challenge_response(ss,tid,resp_constructed,err_code,reason,nbh,method); } } /* Password */ if(!(ss->hmackey_set) && (ss->pwd[0] == 0)) { if(can_resume) { (server->userkeycb)(server->id, server->ct, server->oauth, &(ss->oauth), usname, realm, resume_processing_after_username_check, in_buffer, ss->id, postpone_reply); if(*postpone_reply) { return 0; } } TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Cannot find credentials of user <%s>\n", __FUNCTION__, (char*)usname); *err_code = 401; return create_challenge_response(ss,tid,resp_constructed,err_code,reason,nbh,method); } /* Check integrity */ if(stun_check_message_integrity_by_key_str(server->ct,ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), ss->hmackey, ss->pwd, SHATYPE_DEFAULT)<1) { if(can_resume) { (server->userkeycb)(server->id, server->ct, server->oauth, &(ss->oauth), usname, realm, resume_processing_after_username_check, in_buffer, ss->id, postpone_reply); if(*postpone_reply) { return 0; } } TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: user %s credentials are incorrect\n", __FUNCTION__, (char*)usname); *err_code = 401; return create_challenge_response(ss,tid,resp_constructed,err_code,reason,nbh,method); } *message_integrity = 1; return 0; } //<<== AUTH static void set_alternate_server(turn_server_addrs_list_t *asl, const ioa_addr *local_addr, size_t *counter, u16bits method, stun_tid *tid, int *resp_constructed, int *err_code, const u08bits **reason, ioa_network_buffer_handle nbh) { if(asl && asl->size && local_addr) { size_t i; /* to prevent indefinite cycle: */ for(i=0;isize;++i) { ioa_addr *addr = &(asl->addrs[i]); if(addr_eq(addr,local_addr)) return; } for(i=0;isize;++i) { if(*counter>=asl->size) *counter = 0; ioa_addr *addr = &(asl->addrs[*counter]); *counter +=1; if(addr->ss.sa_family == local_addr->ss.sa_family) { *err_code = 300; size_t len = ioa_network_buffer_get_size(nbh); stun_init_error_response_str(method, ioa_network_buffer_data(nbh), &len, *err_code, *reason, tid); *resp_constructed = 1; stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_ALTERNATE_SERVER, addr); ioa_network_buffer_set_size(nbh,len); return; } } } } static int handle_turn_command(turn_turnserver *server, ts_ur_super_session *ss, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, int *resp_constructed, int can_resume) { stun_tid tid; int err_code = 0; const u08bits *reason = NULL; int no_response = 0; int message_integrity = 0; if(!(ss->client_socket)) return -1; u16bits unknown_attrs[MAX_NUMBER_OF_UNKNOWN_ATTRS]; u16bits ua_num = 0; u16bits method = stun_get_method_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); *resp_constructed = 0; stun_tid_from_message_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), &tid); if (stun_is_request_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh))) { if((method == STUN_METHOD_BINDING) && (*(server->no_stun))) { no_response = 1; if(server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: STUN method 0x%x ignored\n", __FUNCTION__, (unsigned int)method); } } else if((method != STUN_METHOD_BINDING) && (*(server->stun_only))) { no_response = 1; if(server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: STUN method 0x%x ignored\n", __FUNCTION__, (unsigned int)method); } } else if((method != STUN_METHOD_BINDING) || (*(server->secure_stun))) { if(method == STUN_METHOD_ALLOCATE) { allocation *a = get_allocation_ss(ss); if(is_allocation_valid(a)) { if(!stun_tid_equals(&(a->tid), &tid)) { err_code = 437; reason = (const u08bits *)"Mismatched allocation: wrong transaction ID"; } } if(!err_code) { SOCKET_TYPE cst = get_ioa_socket_type(ss->client_socket); turn_server_addrs_list_t *asl = server->alternate_servers_list; if(((cst == UDP_SOCKET)||(cst == DTLS_SOCKET)) && server->self_udp_balance && server->aux_servers_list && server->aux_servers_list->size) { asl = server->aux_servers_list; } else if(((cst == TLS_SOCKET) || (cst == DTLS_SOCKET) ||(cst == TLS_SCTP_SOCKET)) && server->tls_alternate_servers_list && server->tls_alternate_servers_list->size) { asl = server->tls_alternate_servers_list; } if(asl && asl->size) { turn_mutex_lock(&(asl->m)); set_alternate_server(asl,get_local_addr_from_ioa_socket(ss->client_socket),&(server->as_counter),method,&tid,resp_constructed,&err_code,&reason,nbh); turn_mutex_unlock(&(asl->m)); } } } /* check that the realm is the same as in the original request */ if(ss->origin_set) { stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); int origin_found = 0; int norigins = 0; while(sar && !origin_found) { if(stun_attr_get_type(sar) == STUN_ATTRIBUTE_ORIGIN) { int sarlen = stun_attr_get_len(sar); if(sarlen>0) { ++norigins; char *o = (char*)turn_malloc(sarlen+1); ns_bcopy(stun_attr_get_value(sar),o,sarlen); o[sarlen]=0; char *corigin = (char*)turn_malloc(STUN_MAX_ORIGIN_SIZE+1); corigin[0]=0; if(get_canonic_origin(o,corigin,STUN_MAX_ORIGIN_SIZE)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Wrong origin format: %s\n", __FUNCTION__, o); } if(!strncmp(ss->origin,corigin,STUN_MAX_ORIGIN_SIZE)) { origin_found = 1; } turn_free(corigin,sarlen+1); turn_free(o,sarlen+1); } } sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } if(server->check_origin && *(server->check_origin)) { if(ss->origin[0]) { if(!origin_found) { err_code = 441; reason = (const u08bits *)"The origin attribute does not match the initial session origin value"; if(server->verbose) { char smethod[129]; stun_method_str(method,smethod); log_method(ss, smethod, err_code, reason); } } } else if(norigins > 0){ err_code = 441; reason = (const u08bits *)"The origin attribute is empty, does not match the initial session origin value"; if(server->verbose) { char smethod[129]; stun_method_str(method,smethod); log_method(ss, smethod, err_code, reason); } } } } /* get the initial origin value */ if(!err_code && !(ss->origin_set) && (method == STUN_METHOD_ALLOCATE)) { stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); int origin_found = 0; while(sar && !origin_found) { if(stun_attr_get_type(sar) == STUN_ATTRIBUTE_ORIGIN) { int sarlen = stun_attr_get_len(sar); if(sarlen>0) { char *o = (char*)turn_malloc(sarlen+1); ns_bcopy(stun_attr_get_value(sar),o,sarlen); o[sarlen]=0; char *corigin = (char*)turn_malloc(STUN_MAX_ORIGIN_SIZE+1); corigin[0]=0; if(get_canonic_origin(o,corigin,STUN_MAX_ORIGIN_SIZE)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Wrong origin format: %s\n", __FUNCTION__, o); } strncpy(ss->origin,corigin,STUN_MAX_ORIGIN_SIZE); turn_free(corigin,sarlen+1); turn_free(o,sarlen+1); origin_found = get_realm_options_by_origin(ss->origin,&(ss->realm_options)); } } sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } ss->origin_set = 1; } if(!err_code && !(*resp_constructed) && !no_response) { if(method == STUN_METHOD_CONNECTION_BIND) { ; } else if(!(*(server->mobility)) || (method != STUN_METHOD_REFRESH) || is_allocation_valid(get_allocation_ss(ss))) { int postpone_reply = 0; check_stun_auth(server, ss, &tid, resp_constructed, &err_code, &reason, in_buffer, nbh, method, &message_integrity, &postpone_reply, can_resume); if(postpone_reply) no_response = 1; } } } if (!err_code && !(*resp_constructed) && !no_response) { switch (method){ case STUN_METHOD_ALLOCATE: { handle_turn_allocate(server, ss, &tid, resp_constructed, &err_code, &reason, unknown_attrs, &ua_num, in_buffer, nbh); if(server->verbose) { log_method(ss, "ALLOCATE", err_code, reason); } break; } case STUN_METHOD_CONNECT: handle_turn_connect(server, ss, &tid, &err_code, &reason, unknown_attrs, &ua_num, in_buffer); if(server->verbose) { log_method(ss, "CONNECT", err_code, reason); } if(!err_code) no_response = 1; break; case STUN_METHOD_CONNECTION_BIND: handle_turn_connection_bind(server, ss, &tid, resp_constructed, &err_code, &reason, unknown_attrs, &ua_num, in_buffer, nbh, message_integrity, can_resume); if(server->verbose && err_code) { log_method(ss, "CONNECTION_BIND", err_code, reason); } break; case STUN_METHOD_REFRESH: handle_turn_refresh(server, ss, &tid, resp_constructed, &err_code, &reason, unknown_attrs, &ua_num, in_buffer, nbh, message_integrity, &no_response, can_resume); if(server->verbose) { log_method(ss, "REFRESH", err_code, reason); } break; case STUN_METHOD_CHANNEL_BIND: handle_turn_channel_bind(server, ss, &tid, resp_constructed, &err_code, &reason, unknown_attrs, &ua_num, in_buffer, nbh); if(server->verbose) { log_method(ss, "CHANNEL_BIND", err_code, reason); } break; case STUN_METHOD_CREATE_PERMISSION: handle_turn_create_permission(server, ss, &tid, resp_constructed, &err_code, &reason, unknown_attrs, &ua_num, in_buffer, nbh); if(server->verbose) { log_method(ss, "CREATE_PERMISSION", err_code, reason); } break; case STUN_METHOD_BINDING: { int origin_changed=0; ioa_addr response_origin; int dest_changed=0; ioa_addr response_destination; handle_turn_binding(server, ss, &tid, resp_constructed, &err_code, &reason, unknown_attrs, &ua_num, in_buffer, nbh, &origin_changed, &response_origin, &dest_changed, &response_destination, 0, 0); if(server->verbose) { log_method(ss, "BINDING", err_code, reason); } if(*resp_constructed && !err_code && (origin_changed || dest_changed)) { if (server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "RFC 5780 request successfully processed\n"); } { const u08bits *field = (const u08bits *) get_version(server); size_t fsz = strlen(get_version(server)); size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_SOFTWARE, field, fsz); ioa_network_buffer_set_size(nbh, len); } send_turn_message_to(server, nbh, &response_origin, &response_destination); no_response = 1; } break; } default: TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unsupported STUN request received, method 0x%x\n",(unsigned int)method); }; } } else if (stun_is_indication_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh))) { no_response = 1; int postpone = 0; if (!postpone && !err_code) { switch (method){ case STUN_METHOD_BINDING: //ICE ? break; case STUN_METHOD_SEND: handle_turn_send(server, ss, &err_code, &reason, unknown_attrs, &ua_num, in_buffer); if(eve(server->verbose)) { log_method(ss, "SEND", err_code, reason); } break; case STUN_METHOD_DATA: err_code = 403; if(eve(server->verbose)) { log_method(ss, "DATA", err_code, reason); } break; default: if (server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Unsupported STUN indication received: method 0x%x\n",(unsigned int)method); } } }; } else { no_response = 1; if (server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Wrong STUN message received\n"); } } if(ss->to_be_closed || !(ss->client_socket) || ioa_socket_tobeclosed(ss->client_socket)) return 0; if (ua_num > 0) { err_code = 420; size_t len = ioa_network_buffer_get_size(nbh); stun_init_error_response_str(method, ioa_network_buffer_data(nbh), &len, err_code, NULL, &tid); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES, (const u08bits*) unknown_attrs, (ua_num * 2)); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } if (!no_response) { if (!(*resp_constructed)) { if (!err_code) err_code = 400; size_t len = ioa_network_buffer_get_size(nbh); stun_init_error_response_str(method, ioa_network_buffer_data(nbh), &len, err_code, reason, &tid); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } { const u08bits *field = (const u08bits *) get_version(server); size_t fsz = strlen(get_version(server)); size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_SOFTWARE, field, fsz); ioa_network_buffer_set_size(nbh, len); } if(message_integrity) { size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_integrity_str(server->ct,ioa_network_buffer_data(nbh),&len,ss->hmackey,ss->pwd,SHATYPE_DEFAULT); ioa_network_buffer_set_size(nbh,len); } if(err_code) { if(server->verbose) { log_method(ss, "message", err_code, reason); } } } else { *resp_constructed = 0; } return 0; } static int handle_old_stun_command(turn_turnserver *server, ts_ur_super_session *ss, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, int *resp_constructed, u32bits cookie) { stun_tid tid; int err_code = 0; const u08bits *reason = NULL; int no_response = 0; u16bits unknown_attrs[MAX_NUMBER_OF_UNKNOWN_ATTRS]; u16bits ua_num = 0; u16bits method = stun_get_method_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); *resp_constructed = 0; stun_tid_from_message_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), &tid); if (stun_is_request_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh))) { if(method != STUN_METHOD_BINDING) { no_response = 1; if(server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: OLD STUN method 0x%x ignored\n", __FUNCTION__, (unsigned int)method); } } if (!err_code && !(*resp_constructed) && !no_response) { int origin_changed=0; ioa_addr response_origin; int dest_changed=0; ioa_addr response_destination; handle_turn_binding(server, ss, &tid, resp_constructed, &err_code, &reason, unknown_attrs, &ua_num, in_buffer, nbh, &origin_changed, &response_origin, &dest_changed, &response_destination, cookie,1); if(server->verbose) { log_method(ss, "OLD BINDING", err_code, reason); } if(*resp_constructed && !err_code && (origin_changed || dest_changed)) { if (server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "RFC3489 CHANGE request successfully processed\n"); } { size_t oldsz = strlen(get_version(server)); size_t newsz = (((oldsz)>>2) + 1)<<2; u08bits software[120]; ns_bzero(software,sizeof(software)); if(newsz>sizeof(software)) newsz = sizeof(software); ns_bcopy(get_version(server),software,oldsz); size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, OLD_STUN_ATTRIBUTE_SERVER, software, newsz); ioa_network_buffer_set_size(nbh, len); } send_turn_message_to(server, nbh, &response_origin, &response_destination); no_response = 1; } } } else { no_response = 1; if (server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Wrong OLD STUN message received\n"); } } if (ua_num > 0) { err_code = 420; size_t len = ioa_network_buffer_get_size(nbh); old_stun_init_error_response_str(method, ioa_network_buffer_data(nbh), &len, err_code, NULL, &tid, cookie); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES, (const u08bits*) unknown_attrs, (ua_num * 2)); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } if (!no_response) { if (!(*resp_constructed)) { if (!err_code) err_code = 400; size_t len = ioa_network_buffer_get_size(nbh); old_stun_init_error_response_str(method, ioa_network_buffer_data(nbh), &len, err_code, reason, &tid, cookie); ioa_network_buffer_set_size(nbh,len); *resp_constructed = 1; } { size_t oldsz = strlen(get_version(server)); size_t newsz = (((oldsz)>>2) + 1)<<2; u08bits software[120]; ns_bzero(software,sizeof(software)); if(newsz>sizeof(software)) newsz = sizeof(software); ns_bcopy(get_version(server),software,oldsz); size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, OLD_STUN_ATTRIBUTE_SERVER, software, newsz); ioa_network_buffer_set_size(nbh, len); } if(err_code) { if(server->verbose) { log_method(ss, "OLD STUN message", err_code, reason); } } } else { *resp_constructed = 0; } return 0; } ////////////////////////////////////////////////////////////////// static int write_to_peerchannel(ts_ur_super_session* ss, u16bits chnum, ioa_net_data *in_buffer) { int rc = 0; if (ss && (in_buffer->recv_ttl!=0)) { allocation* a = get_allocation_ss(ss); if (is_allocation_valid(a)) { ch_info* chn = allocation_get_ch_info(a, chnum); if (!chn) return -1; /* Channel packets are always sent with DF=0: */ set_df_on_ioa_socket(get_relay_socket_ss(ss, chn->peer_addr.ss.sa_family), 0); ioa_network_buffer_handle nbh = in_buffer->nbh; ioa_network_buffer_add_offset_size(in_buffer->nbh, STUN_CHANNEL_HEADER_LENGTH, 0, ioa_network_buffer_get_size(in_buffer->nbh)-STUN_CHANNEL_HEADER_LENGTH); ioa_network_buffer_header_init(nbh); rc = send_data_from_ioa_socket_nbh(get_relay_socket_ss(ss, chn->peer_addr.ss.sa_family), &(chn->peer_addr), nbh, in_buffer->recv_ttl-1, in_buffer->recv_tos, NULL); in_buffer->nbh = NULL; } } return rc; } static void client_input_handler(ioa_socket_handle s, int event_type, ioa_net_data *data, void *arg, int can_resume); static void peer_input_handler(ioa_socket_handle s, int event_type, ioa_net_data *data, void *arg, int can_resume); /////////////// Client actions ///////////////// int shutdown_client_connection(turn_turnserver *server, ts_ur_super_session *ss, int force, const char* reason) { FUNCSTART; if (!ss) return -1; turn_report_session_usage(ss, 1); dec_quota(ss); dec_bps(ss); allocation* alloc = get_allocation_ss(ss); if (!is_allocation_valid(alloc)) { force = 1; } if(!force && ss->is_mobile) { if (ss->client_socket && server->verbose) { char sraddr[129]="\0"; char sladdr[129]="\0"; addr_to_string(get_remote_addr_from_ioa_socket(ss->client_socket),(u08bits*)sraddr); addr_to_string(get_local_addr_from_ioa_socket(ss->client_socket),(u08bits*)sladdr); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "session %018llu: closed (1st stage), user <%s> realm <%s> origin <%s>, local %s, remote %s, reason: %s\n",(unsigned long long)(ss->id),(char*)ss->username,(char*)ss->realm_options.name,(char*)ss->origin, sladdr,sraddr,reason); } IOA_CLOSE_SOCKET(ss->client_socket); FUNCEND; return 0; } if (eve(server->verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "closing session 0x%lx, client socket 0x%lx (socket session=0x%lx)\n", (long) ss, (long) ss->client_socket, (long)get_ioa_socket_session(ss->client_socket)); } if (server->disconnect) server->disconnect(ss); if (server->verbose) { char sraddr[129]="\0"; char sladdr[129]="\0"; addr_to_string(get_remote_addr_from_ioa_socket(ss->client_socket),(u08bits*)sraddr); addr_to_string(get_local_addr_from_ioa_socket(ss->client_socket),(u08bits*)sladdr); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "session %018llu: closed (2nd stage), user <%s> realm <%s> origin <%s>, local %s, remote %s, reason: %s\n", (unsigned long long)(ss->id), (char*)ss->username,(char*)ss->realm_options.name,(char*)ss->origin, sladdr,sraddr, reason); } IOA_CLOSE_SOCKET(ss->client_socket); { int i; for(i=0;ialloc.relay_sessions[i].s); } } turn_server_remove_all_from_ur_map_ss(ss); FUNCEND; return 0; } static void client_to_be_allocated_timeout_handler(ioa_engine_handle e, void *arg) { if (!arg) return; UNUSED_ARG(e); ts_ur_super_session* ss = (ts_ur_super_session*) arg; turn_turnserver* server = (turn_turnserver*) (ss->server); if (!server) return; FUNCSTART; int to_close = 0; ioa_socket_handle s = ss->client_socket; if(!s || ioa_socket_tobeclosed(s)) { to_close = 1; } else if(get_ioa_socket_app_type(s) == HTTPS_CLIENT_SOCKET) { ; } else { ioa_socket_handle rs4 = ss->alloc.relay_sessions[ALLOC_IPV4_INDEX].s; ioa_socket_handle rs6 = ss->alloc.relay_sessions[ALLOC_IPV6_INDEX].s; if((!rs4 || ioa_socket_tobeclosed(rs4)) && (!rs6 || ioa_socket_tobeclosed(rs6))) { to_close = 1; } else if(ss->client_socket == NULL) { to_close = 1; } else if(!(ss->alloc.relay_sessions[ALLOC_IPV4_INDEX].lifetime_ev) && !(ss->alloc.relay_sessions[ALLOC_IPV6_INDEX].lifetime_ev)) { to_close = 1; } else if(!(ss->to_be_allocated_timeout_ev)) { to_close = 1; } } if(to_close) { IOA_EVENT_DEL(ss->to_be_allocated_timeout_ev); shutdown_client_connection(server, ss, 1, "allocation watchdog determined stale session state"); } FUNCEND; } static int write_client_connection(turn_turnserver *server, ts_ur_super_session* ss, ioa_network_buffer_handle nbh, int ttl, int tos) { FUNCSTART; if (!(ss->client_socket)) { ioa_network_buffer_delete(server->e, nbh); FUNCEND; return -1; } else { if (eve(server->verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: prepare to write to s 0x%lx\n", __FUNCTION__, (long) (ss->client_socket)); } int skip = 0; int ret = send_data_from_ioa_socket_nbh(ss->client_socket, NULL, nbh, ttl, tos, &skip); if(!skip) { ++(ss->sent_packets); ss->sent_bytes += (u32bits)ioa_network_buffer_get_size(nbh); turn_report_session_usage(ss, 0); } FUNCEND; return ret; } } static void client_ss_allocation_timeout_handler(ioa_engine_handle e, void *arg) { UNUSED_ARG(e); if (!arg) return; relay_endpoint_session *rsession = (relay_endpoint_session*)arg; if(!(rsession->s)) return; ts_ur_super_session* ss = get_ioa_socket_session(rsession->s); if (!ss) return; allocation* a = get_allocation_ss(ss); turn_turnserver* server = (turn_turnserver*) (ss->server); if (!server) { clear_allocation(a); return; } FUNCSTART; int family = get_ioa_socket_address_family(rsession->s); set_allocation_family_invalid(a,family); if(!get_relay_socket(a, AF_INET) && !get_relay_socket(a, AF_INET6)) { shutdown_client_connection(server, ss, 0, "allocation timeout"); } FUNCEND; } static int create_relay_connection(turn_turnserver* server, ts_ur_super_session *ss, u32bits lifetime, int address_family, u08bits transport, int even_port, u64bits in_reservation_token, u64bits *out_reservation_token, int *err_code, const u08bits **reason, accept_cb acb) { if (server && ss && ss->client_socket && !ioa_socket_tobeclosed(ss->client_socket)) { allocation* a = get_allocation_ss(ss); relay_endpoint_session* newelem = NULL; ioa_socket_handle rtcp_s = NULL; if (in_reservation_token) { ioa_socket_handle s = NULL; if ((get_ioa_socket_from_reservation(server->e, in_reservation_token,&s) < 0)|| !s || ioa_socket_tobeclosed(s)) { IOA_CLOSE_SOCKET(s); *err_code = 404; *reason = (const u08bits *)"Cannot find reserved socket"; return -1; } int family = get_ioa_socket_address_family(s); newelem = get_relay_session_ss(ss,family); if(newelem->s != s) { IOA_CLOSE_SOCKET(newelem->s); ns_bzero(newelem, sizeof(relay_endpoint_session)); newelem->s = s; } addr_debug_print(server->verbose, get_local_addr_from_ioa_socket(newelem->s), "Local relay addr (RTCP)"); } else { int family = get_family(address_family,server->e,ss->client_socket); newelem = get_relay_session_ss(ss,family); IOA_CLOSE_SOCKET(newelem->s); ns_bzero(newelem, sizeof(relay_endpoint_session)); newelem->s = NULL; int res = create_relay_ioa_sockets(server->e, ss->client_socket, address_family, transport, even_port, &(newelem->s), &rtcp_s, out_reservation_token, err_code, reason, acb, ss); if (res < 0) { if(!(*err_code)) *err_code = 508; if(!(*reason)) *reason = (const u08bits *)"Cannot create socket"; IOA_CLOSE_SOCKET(newelem->s); IOA_CLOSE_SOCKET(rtcp_s); return -1; } } if (newelem->s == NULL) { IOA_CLOSE_SOCKET(rtcp_s); *err_code = 508; *reason = (const u08bits *)"Cannot create relay socket"; return -1; } if (rtcp_s) { if (out_reservation_token && *out_reservation_token) { /* OK */ } else { IOA_CLOSE_SOCKET(newelem->s); IOA_CLOSE_SOCKET(rtcp_s); *err_code = 500; *reason = (const u08bits *)"Wrong reservation tokens (internal error)"; return -1; } } /* RFC6156: do not use DF when IPv6 is involved: */ if((get_ioa_socket_address_family(newelem->s) == AF_INET6) || (get_ioa_socket_address_family(ss->client_socket) == AF_INET6)) set_do_not_use_df(newelem->s); if(get_ioa_socket_type(newelem->s) != TCP_SOCKET) { if(register_callback_on_ioa_socket(server->e, newelem->s, IOA_EV_READ,peer_input_handler, ss, 0)<0) { return -1; } } if (lifetime<1) lifetime = STUN_DEFAULT_ALLOCATE_LIFETIME; else if(lifetime>(u32bits)*(server->max_allocate_lifetime)) lifetime = (u32bits)*(server->max_allocate_lifetime); ioa_timer_handle ev = set_ioa_timer(server->e, lifetime, 0, client_ss_allocation_timeout_handler, newelem, 0, "client_ss_allocation_timeout_handler"); set_allocation_lifetime_ev(a, server->ctime + lifetime, ev, get_ioa_socket_address_family(newelem->s)); set_ioa_socket_session(newelem->s, ss); } return 0; } static int refresh_relay_connection(turn_turnserver* server, ts_ur_super_session *ss, u32bits lifetime, int even_port, u64bits in_reservation_token, u64bits *out_reservation_token, int *err_code, int family) { UNUSED_ARG(even_port); UNUSED_ARG(in_reservation_token); UNUSED_ARG(out_reservation_token); UNUSED_ARG(err_code); allocation* a = get_allocation_ss(ss); if (server && ss && is_allocation_valid(a)) { if (lifetime < 1) { lifetime = 1; } ioa_timer_handle ev = set_ioa_timer(server->e, lifetime, 0, client_ss_allocation_timeout_handler, get_relay_session(a,family), 0, "refresh_client_ss_allocation_timeout_handler"); set_allocation_lifetime_ev(a, server->ctime + lifetime, ev, family); return 0; } else { return -1; } } static int read_client_connection(turn_turnserver *server, ts_ur_super_session *ss, ioa_net_data *in_buffer, int can_resume, int count_usage) { FUNCSTART; if (!server || !ss || !in_buffer || !(ss->client_socket) || ss->to_be_closed || ioa_socket_tobeclosed(ss->client_socket)) { FUNCEND; return -1; } int ret = (int)ioa_network_buffer_get_size(in_buffer->nbh); if (ret < 0) { FUNCEND; return -1; } if(count_usage) { ++(ss->received_packets); ss->received_bytes += (u32bits)ioa_network_buffer_get_size(in_buffer->nbh); turn_report_session_usage(ss, 0); } if (eve(server->verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: data.buffer=0x%lx, data.len=%ld\n", __FUNCTION__, (long)ioa_network_buffer_data(in_buffer->nbh), (long)ioa_network_buffer_get_size(in_buffer->nbh)); } u16bits chnum = 0; u32bits old_stun_cookie = 0; size_t blen = ioa_network_buffer_get_size(in_buffer->nbh); size_t orig_blen = blen; SOCKET_TYPE st = get_ioa_socket_type(ss->client_socket); SOCKET_APP_TYPE sat = get_ioa_socket_app_type(ss->client_socket); int is_padding_mandatory = is_stream_socket(st); if(sat == HTTP_CLIENT_SOCKET) { if(server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: HTTP connection input: %s\n", __FUNCTION__, (char*)ioa_network_buffer_data(in_buffer->nbh)); } handle_http_echo(ss->client_socket); } else if(sat == HTTPS_CLIENT_SOCKET) { //??? } else if (stun_is_channel_message_str(ioa_network_buffer_data(in_buffer->nbh), &blen, &chnum, is_padding_mandatory)) { if(ss->is_tcp_relay) { //Forbidden FUNCEND; return -1; } int rc = 0; if(blen<=orig_blen) { ioa_network_buffer_set_size(in_buffer->nbh,blen); rc = write_to_peerchannel(ss, chnum, in_buffer); } if (eve(server->verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: wrote to peer %d bytes\n", __FUNCTION__, (int) rc); } FUNCEND; return 0; } else if (stun_is_command_message_full_check_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), 0, &(ss->enforce_fingerprints))) { ioa_network_buffer_handle nbh = ioa_network_buffer_allocate(server->e); int resp_constructed = 0; u16bits method = stun_get_method_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)); handle_turn_command(server, ss, in_buffer, nbh, &resp_constructed, can_resume); if((method != STUN_METHOD_BINDING) && (method != STUN_METHOD_SEND)) report_turn_session_info(server,ss,0); if(ss->to_be_closed || ioa_socket_tobeclosed(ss->client_socket)) { FUNCEND; ioa_network_buffer_delete(server->e, nbh); return 0; } if (resp_constructed) { if ((server->fingerprint) || ss->enforce_fingerprints) { size_t len = ioa_network_buffer_get_size(nbh); if (stun_attr_add_fingerprint_str(ioa_network_buffer_data(nbh), &len) < 0) { FUNCEND ; ioa_network_buffer_delete(server->e, nbh); return -1; } ioa_network_buffer_set_size(nbh, len); } int ret = write_client_connection(server, ss, nbh, TTL_IGNORE, TOS_IGNORE); FUNCEND ; return ret; } else { ioa_network_buffer_delete(server->e, nbh); return 0; } } else if (old_stun_is_command_message_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), &old_stun_cookie) && !(*(server->no_stun))) { ioa_network_buffer_handle nbh = ioa_network_buffer_allocate(server->e); int resp_constructed = 0; handle_old_stun_command(server, ss, in_buffer, nbh, &resp_constructed, old_stun_cookie); if (resp_constructed) { int ret = write_client_connection(server, ss, nbh, TTL_IGNORE, TOS_IGNORE); FUNCEND ; return ret; } else { ioa_network_buffer_delete(server->e, nbh); return 0; } } else { SOCKET_TYPE st = get_ioa_socket_type(ss->client_socket); if(is_stream_socket(st)) { if(is_http((char*)ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh))) { const char *proto = "HTTP"; ioa_network_buffer_data(in_buffer->nbh)[ioa_network_buffer_get_size(in_buffer->nbh)] = 0; if (*server->web_admin_listen_on_workers) { if(st==TLS_SOCKET) { proto = "HTTPS"; set_ioa_socket_app_type(ss->client_socket,HTTPS_CLIENT_SOCKET); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: %s (%s %s) request: %s\n", __FUNCTION__, proto, get_ioa_socket_cipher(ss->client_socket), get_ioa_socket_ssl_method(ss->client_socket), (char*)ioa_network_buffer_data(in_buffer->nbh)); if(server->send_https_socket) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s socket to be detached: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)ss->client_socket, get_ioa_socket_type(ss->client_socket), get_ioa_socket_app_type(ss->client_socket)); ioa_socket_handle new_s = detach_ioa_socket(ss->client_socket); if(new_s) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s new detached socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)new_s, get_ioa_socket_type(new_s), get_ioa_socket_app_type(new_s)); server->send_https_socket(new_s); } ss->to_be_closed = 1; } } else { set_ioa_socket_app_type(ss->client_socket,HTTP_CLIENT_SOCKET); if(server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: %s request: %s\n", __FUNCTION__, proto, (char*)ioa_network_buffer_data(in_buffer->nbh)); } handle_http_echo(ss->client_socket); } return 0; } else { ss->to_be_closed = 1; return 0; } } } } //Unrecognized message received, ignore it FUNCEND; return -1; } static int attach_socket_to_session(turn_turnserver* server, ioa_socket_handle s, ts_ur_super_session* ss) { int ret = -1; FUNCSTART; if(s && server && ss && !ioa_socket_tobeclosed(s)) { if(ss->client_socket != s) { IOA_CLOSE_SOCKET(ss->client_socket); ss->client_socket = s; if(register_callback_on_ioa_socket(server->e, s, IOA_EV_READ, client_input_handler, ss, 0)<0) { return -1; } set_ioa_socket_session(s, ss); } ret = 0; } FUNCEND; return ret; } int open_client_connection_session(turn_turnserver* server, struct socket_message *sm) { FUNCSTART; if (!server) return -1; if (!(sm->s)) return -1; ts_ur_super_session* ss = create_new_ss(server); ss->client_socket = sm->s; if(register_callback_on_ioa_socket(server->e, ss->client_socket, IOA_EV_READ, client_input_handler, ss, 0)<0) { return -1; } set_ioa_socket_session(ss->client_socket, ss); int at = TURN_MAX_ALLOCATE_TIMEOUT; if(*(server->stun_only)) at = TURN_MAX_ALLOCATE_TIMEOUT_STUN_ONLY; IOA_EVENT_DEL(ss->to_be_allocated_timeout_ev); ss->to_be_allocated_timeout_ev = set_ioa_timer(server->e, at, 0, client_to_be_allocated_timeout_handler, ss, 1, "client_to_be_allocated_timeout_handler"); if(sm->nd.nbh) { client_input_handler(ss->client_socket,IOA_EV_READ,&(sm->nd),ss,sm->can_resume); ioa_network_buffer_delete(server->e, sm->nd.nbh); sm->nd.nbh = NULL; } FUNCEND; return 0; } /////////////// io handlers /////////////////// static void peer_input_handler(ioa_socket_handle s, int event_type, ioa_net_data *in_buffer, void *arg, int can_resume) { if (!(event_type & IOA_EV_READ) || !arg) return; if(in_buffer->recv_ttl==0) return; UNUSED_ARG(can_resume); if(!s || ioa_socket_tobeclosed(s)) return; ts_ur_super_session* ss = (ts_ur_super_session*) arg; if(!ss) return; if(ss->to_be_closed) return; if(!(ss->client_socket) || ioa_socket_tobeclosed(ss->client_socket)) return; turn_turnserver *server = (turn_turnserver*) (ss->server); if (!server) return; relay_endpoint_session* elem = get_relay_session_ss(ss, get_ioa_socket_address_family(s)); if (elem->s == NULL) { return; } int offset = STUN_CHANNEL_HEADER_LENGTH; int ilen = min((int)ioa_network_buffer_get_size(in_buffer->nbh), (int)(ioa_network_buffer_get_capacity_udp() - offset)); if (ilen >= 0) { allocation* a = get_allocation_ss(ss); if (is_allocation_valid(a)) { u16bits chnum = 0; ioa_network_buffer_handle nbh = NULL; turn_permission_info* tinfo = allocation_get_permission(a, &(in_buffer->src_addr)); if (tinfo) { chnum = get_turn_channel_number(tinfo, &(in_buffer->src_addr)); } else if(!(server->server_relay)) { return; } if (chnum) { size_t len = (size_t)(ilen); nbh = in_buffer->nbh; ioa_network_buffer_add_offset_size(nbh, 0, STUN_CHANNEL_HEADER_LENGTH, ioa_network_buffer_get_size(nbh)+STUN_CHANNEL_HEADER_LENGTH); ioa_network_buffer_header_init(nbh); SOCKET_TYPE st = get_ioa_socket_type(ss->client_socket); int do_padding = is_stream_socket(st); stun_init_channel_message_str(chnum, ioa_network_buffer_data(nbh), &len, len, do_padding); ioa_network_buffer_set_size(nbh,len); in_buffer->nbh = NULL; if (eve(server->verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: send channel 0x%x\n", __FUNCTION__, (int) (chnum)); } } else { size_t len = 0; nbh = ioa_network_buffer_allocate(server->e); stun_init_indication_str(STUN_METHOD_DATA, ioa_network_buffer_data(nbh), &len); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_DATA, ioa_network_buffer_data(in_buffer->nbh), (size_t)ilen); stun_attr_add_addr_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &(in_buffer->src_addr)); ioa_network_buffer_set_size(nbh,len); { const u08bits *field = (const u08bits *) get_version(server); size_t fsz = strlen(get_version(server)); size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_SOFTWARE, field, fsz); ioa_network_buffer_set_size(nbh, len); } if ((server->fingerprint) || ss->enforce_fingerprints) { size_t len = ioa_network_buffer_get_size(nbh); stun_attr_add_fingerprint_str(ioa_network_buffer_data(nbh), &len); ioa_network_buffer_set_size(nbh, len); } } if (eve(server->verbose)) { u16bits* t = (u16bits*) ioa_network_buffer_data(nbh); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Send data: 0x%x\n", (int) (nswap16(t[0]))); } write_client_connection(server, ss, nbh, in_buffer->recv_ttl-1, in_buffer->recv_tos); } } } static void client_input_handler(ioa_socket_handle s, int event_type, ioa_net_data *data, void *arg, int can_resume) { if (!arg) return; UNUSED_ARG(s); UNUSED_ARG(event_type); ts_ur_super_session* ss = (ts_ur_super_session*)arg; turn_turnserver *server = (turn_turnserver*)ss->server; if (!server) { return; } if (ss->client_socket != s) { return; } read_client_connection(server, ss, data, can_resume, 1); if (ss->to_be_closed) { if(server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "session %018llu: client socket to be closed in client handler: ss=0x%lx\n", (unsigned long long)(ss->id), (long)ss); } set_ioa_socket_tobeclosed(s); } } /////////////////////////////////////////////////////////// void init_turn_server(turn_turnserver* server, turnserver_id id, int verbose, ioa_engine_handle e, turn_credential_type ct, int stun_port, int fingerprint, dont_fragment_option_t dont_fragment, get_user_key_cb userkeycb, check_new_allocation_quota_cb chquotacb, release_allocation_quota_cb raqcb, ioa_addr *external_ip, vintp check_origin, vintp no_tcp_relay, vintp no_udp_relay, vintp stale_nonce, vintp max_allocate_lifetime, vintp channel_lifetime, vintp permission_lifetime, vintp stun_only, vintp no_stun, vintp prod, vintp web_admin_listen_on_workers, turn_server_addrs_list_t *alternate_servers_list, turn_server_addrs_list_t *tls_alternate_servers_list, turn_server_addrs_list_t *aux_servers_list, int self_udp_balance, vintp no_multicast_peers, vintp allow_loopback_peers, ip_range_list_t* ip_whitelist, ip_range_list_t* ip_blacklist, send_socket_to_relay_cb send_socket_to_relay, vintp secure_stun, vintp mobility, int server_relay, send_turn_session_info_cb send_turn_session_info, send_https_socket_cb send_https_socket, allocate_bps_cb allocate_bps_func, int oauth, const char* oauth_server_name, int keep_address_family) { if (!server) return; ns_bzero(server,sizeof(turn_turnserver)); server->e = e; server->id = id; server->ctime = turn_time(); server->session_id_counter = 0; server->sessions_map = ur_map_create(); server->tcp_relay_connections = ur_map_create(); server->ct = ct; server->userkeycb = userkeycb; server->chquotacb = chquotacb; server->raqcb = raqcb; server->no_multicast_peers = no_multicast_peers; server->allow_loopback_peers = allow_loopback_peers; server->secure_stun = secure_stun; server->mobility = mobility; server->server_relay = server_relay; server->send_turn_session_info = send_turn_session_info; server->send_https_socket = send_https_socket; server->oauth = oauth; if(oauth) server->oauth_server_name = oauth_server_name; if(mobility) server->mobile_connections_map = ur_map_create(); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"turn server id=%d created\n",(int)id); server->check_origin = check_origin; server->no_tcp_relay = no_tcp_relay; server->no_udp_relay = no_udp_relay; server->alternate_servers_list = alternate_servers_list; server->tls_alternate_servers_list = tls_alternate_servers_list; server->aux_servers_list = aux_servers_list; server->self_udp_balance = self_udp_balance; server->stale_nonce = stale_nonce; server->max_allocate_lifetime = max_allocate_lifetime; server->channel_lifetime = channel_lifetime; server->permission_lifetime = permission_lifetime; server->stun_only = stun_only; server->no_stun = no_stun; server->prod = prod; server-> web_admin_listen_on_workers = web_admin_listen_on_workers; server->dont_fragment = dont_fragment; server->fingerprint = fingerprint; if(external_ip) { addr_cpy(&(server->external_ip), external_ip); server->external_ip_set = 1; } if (stun_port < 1) stun_port = DEFAULT_STUN_PORT; server->verbose = verbose; server->ip_whitelist = ip_whitelist; server->ip_blacklist = ip_blacklist; server->send_socket_to_relay = send_socket_to_relay; server->allocate_bps_func = allocate_bps_func; server->keep_address_family = keep_address_family; set_ioa_timer(server->e, 1, 0, timer_timeout_handler, server, 1, "timer_timeout_handler"); } ioa_engine_handle turn_server_get_engine(turn_turnserver *s) { if(s) return s->e; return NULL; } void set_disconnect_cb(turn_turnserver* server, int(*disconnect)( ts_ur_super_session*)) { server->disconnect = disconnect; } ////////////////////////////////////////////////////////////////// turnserver-4.5.1.1/src/server/ns_turn_ioalib.h0000664000175000017500000002401613436577655020072 0ustar misimisi/* * 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. */ /* * IO Abstraction library */ #ifndef __IOA_LIB__ #define __IOA_LIB__ #include "ns_turn_ioaddr.h" #ifdef __cplusplus extern "C" { #endif ////////////// forward declarations //////// struct _ts_ur_super_session; typedef struct _ts_ur_super_session ts_ur_super_session; struct _tcp_connection; typedef struct _tcp_connection tcp_connection; ////////////// Mutexes ///////////////////// struct _turn_mutex { u32bits data; void* mutex; }; typedef struct _turn_mutex turn_mutex; int turn_mutex_init(turn_mutex* mutex); int turn_mutex_init_recursive(turn_mutex* mutex); int turn_mutex_lock(const turn_mutex *mutex); int turn_mutex_unlock(const turn_mutex *mutex); int turn_mutex_destroy(turn_mutex* mutex); #define TURN_MUTEX_DECLARE(mutex) turn_mutex mutex; #define TURN_MUTEX_INIT(mutex) turn_mutex_init(mutex) #define TURN_MUTEX_INIT_RECURSIVE(mutex) turn_mutex_init_recursive(mutex) #define TURN_MUTEX_LOCK(mutex) turn_mutex_lock(mutex) #define TURN_MUTEX_UNLOCK(mutex) turn_mutex_unlock(mutex) #define TURN_MUTEX_DESTROY(mutex) turn_mutex_destroy(mutex) /////// Sockets ////////////////////////////// #define IOA_EV_TIMEOUT 0x01 #define IOA_EV_READ 0x02 #define IOA_EV_WRITE 0x04 #define IOA_EV_SIGNAL 0x08 #define IOA_EV_CLOSE 0x10 enum _SOCKET_TYPE { UNKNOWN_SOCKET=0, TCP_SOCKET=6, UDP_SOCKET=17, TLS_SOCKET=56, SCTP_SOCKET=132, TLS_SCTP_SOCKET=133, DTLS_SOCKET=250, TENTATIVE_SCTP_SOCKET=254, TENTATIVE_TCP_SOCKET=255 }; typedef enum _SOCKET_TYPE SOCKET_TYPE; enum _SOCKET_APP_TYPE { UNKNOWN_APP_SOCKET, CLIENT_SOCKET, HTTP_CLIENT_SOCKET, HTTPS_CLIENT_SOCKET, RELAY_SOCKET, RELAY_RTCP_SOCKET, TCP_CLIENT_DATA_SOCKET, TCP_RELAY_DATA_SOCKET, LISTENER_SOCKET }; typedef enum _SOCKET_APP_TYPE SOCKET_APP_TYPE; struct _ioa_socket; typedef struct _ioa_socket ioa_socket; typedef ioa_socket *ioa_socket_handle; struct _ioa_engine; typedef struct _ioa_engine ioa_engine; typedef ioa_engine *ioa_engine_handle; typedef void *ioa_timer_handle; typedef void *ioa_network_buffer_handle; /* event data for net event */ typedef struct _ioa_net_data { ioa_addr src_addr; ioa_network_buffer_handle nbh; int recv_ttl; int recv_tos; } ioa_net_data; /* Callback on TCP connection completion */ typedef void (*connect_cb)(int success, void *arg); /* Callback on accepted socket from TCP relay endpoint */ typedef void (*accept_cb)(ioa_socket_handle s, void *arg); ////////// REALM //////////// struct _realm_options_t; typedef struct _realm_options_t realm_options_t; //////// IP White/black listing /////////// struct _ip_range { char str[257]; char realm[513]; ioa_addr_range enc; }; typedef struct _ip_range ip_range_t; struct _ip_range_list { ip_range_t *rs; size_t ranges_number; }; typedef struct _ip_range_list ip_range_list_t; void ioa_lock_whitelist(ioa_engine_handle e); void ioa_unlock_whitelist(ioa_engine_handle e); const ip_range_list_t* ioa_get_whitelist(ioa_engine_handle e); void ioa_lock_blacklist(ioa_engine_handle e); void ioa_unlock_blacklist(ioa_engine_handle e); const ip_range_list_t* ioa_get_blacklist(ioa_engine_handle e); //////////////////////////////////////////// /* * Network buffer functions */ ioa_network_buffer_handle ioa_network_buffer_allocate(ioa_engine_handle e); void ioa_network_buffer_header_init(ioa_network_buffer_handle nbh); u08bits *ioa_network_buffer_data(ioa_network_buffer_handle nbh); size_t ioa_network_buffer_get_size(ioa_network_buffer_handle nbh); size_t ioa_network_buffer_get_capacity(ioa_network_buffer_handle nbh); size_t ioa_network_buffer_get_capacity_udp(void); void ioa_network_buffer_set_size(ioa_network_buffer_handle nbh, size_t len); void ioa_network_buffer_add_offset_size(ioa_network_buffer_handle nbh, u16bits offset, u08bits coffset, size_t len); u16bits ioa_network_buffer_get_offset(ioa_network_buffer_handle nbh); u08bits ioa_network_buffer_get_coffset(ioa_network_buffer_handle nbh); void ioa_network_buffer_delete(ioa_engine_handle e, ioa_network_buffer_handle nbh); /* * Status reporting functions */ void turn_report_allocation_set(void *a, turn_time_t lifetime, int refresh); void turn_report_allocation_delete(void *a); void turn_report_session_usage(void *session, int force_invalid); /* * Network event handler callback * chnum parameter is just an optimisation hint - * the function must work correctly when chnum=0 * (when no hint information is available). */ typedef void (*ioa_net_event_handler)(ioa_socket_handle s, int event_type, ioa_net_data *data, void *ctx, int can_resume); /* * Timer callback */ typedef void (*ioa_timer_event_handler)(ioa_engine_handle e, void *ctx); /* timers */ ioa_timer_handle set_ioa_timer(ioa_engine_handle e, int secs, int ms, ioa_timer_event_handler cb, void *ctx, int persist, const s08bits *txt); void stop_ioa_timer(ioa_timer_handle th); void delete_ioa_timer(ioa_timer_handle th); #define IOA_EVENT_DEL(E) do { if(E) { delete_ioa_timer(E); E = NULL; } } while(0) ioa_socket_handle create_unbound_relay_ioa_socket(ioa_engine_handle e, int family, SOCKET_TYPE st, SOCKET_APP_TYPE sat); void inc_ioa_socket_ref_counter(ioa_socket_handle s); /* Relay socket handling */ /* * event_port == -1: no rtcp; * event_port == 0: reserve rtcp; * even_port == +1: reserve and bind rtcp. */ int create_relay_ioa_sockets(ioa_engine_handle e, ioa_socket_handle client_s, int address_family, u08bits transport, int even_port, ioa_socket_handle *rtp_s, ioa_socket_handle *rtcp_s, u64bits *out_reservation_token, int *err_code, const u08bits **reason, accept_cb acb, void *acbarg); ioa_socket_handle ioa_create_connecting_tcp_relay_socket(ioa_socket_handle s, ioa_addr *peer_addr, connect_cb cb, void *arg); int get_ioa_socket_from_reservation(ioa_engine_handle e, u64bits in_reservation_token, ioa_socket_handle *s); int get_ioa_socket_address_family(ioa_socket_handle s); int is_stream_socket(int st); int is_tcp_socket(int st); int is_sctp_socket(int st); const char* socket_type_name(SOCKET_TYPE st); const char* get_ioa_socket_cipher(ioa_socket_handle s); const char* get_ioa_socket_ssl_method(ioa_socket_handle s); SOCKET_TYPE get_ioa_socket_type(ioa_socket_handle s); SOCKET_APP_TYPE get_ioa_socket_app_type(ioa_socket_handle s); const char* get_ioa_socket_tls_method(ioa_socket_handle s); const char* get_ioa_socket_tls_cipher(ioa_socket_handle s); void set_ioa_socket_app_type(ioa_socket_handle s, SOCKET_APP_TYPE sat); ioa_addr* get_local_addr_from_ioa_socket(ioa_socket_handle s); ioa_addr* get_remote_addr_from_ioa_socket(ioa_socket_handle s); int get_local_mtu_ioa_socket(ioa_socket_handle s); ts_ur_super_session *get_ioa_socket_session(ioa_socket_handle s); void set_ioa_socket_session(ioa_socket_handle s, ts_ur_super_session *ss); void clear_ioa_socket_session_if(ioa_socket_handle s, void *ss); tcp_connection *get_ioa_socket_sub_session(ioa_socket_handle s); void set_ioa_socket_sub_session(ioa_socket_handle s, tcp_connection *tc); int register_callback_on_ioa_socket(ioa_engine_handle e, ioa_socket_handle s, int event_type, ioa_net_event_handler cb, void *ctx, int clean_preexisting); int send_data_from_ioa_socket_nbh(ioa_socket_handle s, ioa_addr* dest_addr, ioa_network_buffer_handle nbh, int ttl, int tos, int *skip); void close_ioa_socket(ioa_socket_handle s); #define IOA_CLOSE_SOCKET(S) do { if(S) { close_ioa_socket(S); S = NULL; } } while(0) ioa_socket_handle detach_ioa_socket(ioa_socket_handle s); void detach_socket_net_data(ioa_socket_handle s); int set_df_on_ioa_socket(ioa_socket_handle s, int value); void set_do_not_use_df(ioa_socket_handle s); int ioa_socket_tobeclosed(ioa_socket_handle s); void set_ioa_socket_tobeclosed(ioa_socket_handle s); void close_ioa_socket_after_processing_if_necessary(ioa_socket_handle s); ////////////////// Base64 ///////////////////////////// char *base64_encode(const unsigned char *data, size_t input_length, size_t *output_length); void build_base64_decoding_table(void); unsigned char *base64_decode(const char *data, size_t input_length, size_t *output_length); ///////////// Realm /////////////////// void get_default_realm_options(realm_options_t* ro); int get_realm_options_by_origin(char *origin, realm_options_t* ro); void get_realm_options_by_name(char *realm, realm_options_t* ro); int get_canonic_origin(const char* o, char *co, int sz); int get_default_protocol_port(const char* scheme, size_t slen); ///////////// HTTP //////////////////// void handle_http_echo(ioa_socket_handle s); /////////////////////////////////////// #ifdef __cplusplus } #endif #endif /* __IOA_LIB__ */ turnserver-4.5.1.1/src/server/ns_turn_maps.h0000664000175000017500000001500213436577655017566 0ustar misimisi/* * 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. */ #ifndef __TURN_MAPS__ #define __TURN_MAPS__ #include "ns_turn_ioaddr.h" #ifdef __cplusplus extern "C" { #endif //////////////// UR MAP ////////////////// struct _ur_map; typedef struct _ur_map ur_map; //////////////// Common Definitions ////// typedef u64bits ur_map_key_type; typedef unsigned long ur_map_value_type; typedef void (*ur_map_del_func)(ur_map_value_type); typedef int (*foreachcb_type)(ur_map_key_type key, ur_map_value_type value); typedef int (*foreachcb_arg_type)(ur_map_key_type key, ur_map_value_type value, void *arg); ///////////// non-local map ///////////////////// ur_map* ur_map_create(void); /** * @ret: * 0 - success * -1 - error */ int ur_map_put(ur_map* map, ur_map_key_type key, ur_map_value_type value); /** * @ret: * 1 - success * 0 - not found */ int ur_map_get(const ur_map* map, ur_map_key_type key, ur_map_value_type *value); /** * @ret: * 1 - success * 0 - not found */ int ur_map_del(ur_map* map, ur_map_key_type key,ur_map_del_func delfunc); /** * @ret: * 1 - success * 0 - not found */ int ur_map_exist(const ur_map* map, ur_map_key_type key); void ur_map_free(ur_map** map); size_t ur_map_size(const ur_map* map); int ur_map_foreach(ur_map* map, foreachcb_type func); int ur_map_foreach_arg(ur_map* map, foreachcb_arg_type func, void* arg); int ur_map_lock(const ur_map* map); int ur_map_unlock(const ur_map* map); ///////////// "local" map ///////////////////// #define LM_MAP_HASH_SIZE (8) #define LM_MAP_ARRAY_SIZE (3) typedef struct _lm_map_array { ur_map_key_type main_keys[LM_MAP_ARRAY_SIZE]; ur_map_value_type main_values[LM_MAP_ARRAY_SIZE]; size_t extra_sz; ur_map_key_type **extra_keys; ur_map_value_type **extra_values; } lm_map_array; typedef struct _lm_map { lm_map_array table[LM_MAP_HASH_SIZE]; } lm_map; void lm_map_init(lm_map *map); /** * @ret: * 0 - success * -1 - error */ int lm_map_put(lm_map* map, ur_map_key_type key, ur_map_value_type value); /** * @ret: * 1 - success * 0 - not found */ int lm_map_get(const lm_map* map, ur_map_key_type key, ur_map_value_type *value); /** * @ret: * 1 - success * 0 - not found */ int lm_map_del(lm_map* map, ur_map_key_type key,ur_map_del_func delfunc); /** * @ret: * 1 - success * 0 - not found */ int lm_map_exist(const lm_map* map, ur_map_key_type key); void lm_map_clean(lm_map* map); size_t lm_map_size(const lm_map* map); int lm_map_foreach(lm_map* map, foreachcb_type func); int lm_map_foreach_arg(lm_map* map, foreachcb_arg_type func, void* arg); //////////////// UR ADDR MAP ////////////////// typedef unsigned long ur_addr_map_value_type; #define ADDR_MAP_SIZE (1024) #define ADDR_ARRAY_SIZE (4) typedef struct _addr_elem { ioa_addr key; ur_addr_map_value_type value; } addr_elem; typedef struct _addr_list_header { addr_elem main_list[ADDR_ARRAY_SIZE]; addr_elem *extra_list; size_t extra_sz; } addr_list_header; struct _ur_addr_map { addr_list_header lists[ADDR_MAP_SIZE]; u64bits magic; }; struct _ur_addr_map; typedef struct _ur_addr_map ur_addr_map; typedef void (*ur_addr_map_func)(ur_addr_map_value_type); void ur_addr_map_init(ur_addr_map* map); void ur_addr_map_clean(ur_addr_map* map); /** * @ret: * 0 - success * -1 - error * if the addr key exists, the value is updated. */ int ur_addr_map_put(ur_addr_map* map, ioa_addr* key, ur_addr_map_value_type value); /** * @ret: * 1 - success * 0 - not found */ int ur_addr_map_get(const ur_addr_map* map, ioa_addr* key, ur_addr_map_value_type *value); /** * @ret: * 1 - success * 0 - not found */ int ur_addr_map_del(ur_addr_map* map, ioa_addr* key,ur_addr_map_func func); /** * @ret: * 1 - success * 0 - not found */ void ur_addr_map_foreach(ur_addr_map* map, ur_addr_map_func func); size_t ur_addr_map_num_elements(const ur_addr_map* map); size_t ur_addr_map_size(const ur_addr_map* map); //////////////// UR STRING MAP ////////////////// typedef s08bits* ur_string_map_key_type; typedef void* ur_string_map_value_type; struct _ur_string_map; typedef struct _ur_string_map ur_string_map; typedef void (*ur_string_map_func)(ur_string_map_value_type); ur_string_map* ur_string_map_create(ur_string_map_func del_value_func); /** * @ret: * 0 - success * -1 - error * if the string key exists, and the value is different, return error. */ int ur_string_map_put(ur_string_map* map, const ur_string_map_key_type key, ur_string_map_value_type value); /** * @ret: * 1 - success * 0 - not found */ int ur_string_map_get(ur_string_map* map, const ur_string_map_key_type key, ur_string_map_value_type *value); /** * @ret: * 1 - success * 0 - not found */ int ur_string_map_del(ur_string_map* map, const ur_string_map_key_type key); void ur_string_map_clean(ur_string_map* map); void ur_string_map_free(ur_string_map** map); size_t ur_string_map_size(const ur_string_map* map); int ur_string_map_lock(const ur_string_map* map); int ur_string_map_unlock(const ur_string_map* map); //////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_MAPS__ turnserver-4.5.1.1/src/server/ns_turn_allocation.h0000664000175000017500000001642613436577655020766 0ustar misimisi/* * 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. */ #ifndef __TURN_TURN_A_LIB__ #define __TURN_TURN_A_LIB__ #include "ns_turn_utils.h" #include "ns_turn_msg.h" #include "ns_turn_ioalib.h" #include "ns_turn_maps.h" #ifdef __cplusplus extern "C" { #endif ///////// Defines ////////// #define TCP_PEER_CONN_TIMEOUT (30) #define TCP_CONN_BIND_TIMEOUT (30) ////////////// Network session //////////////// typedef struct { ioa_socket_handle s; turn_time_t expiration_time; ioa_timer_handle lifetime_ev; } relay_endpoint_session; static inline void clear_relay_endpoint_session_data(relay_endpoint_session* cdi) { if (cdi) IOA_CLOSE_SOCKET(cdi->s); } ////////// RFC 6062 TCP connection //////// #define MAX_UNSENT_BUFFER_SIZE (0x10) enum _TC_STATE { TC_STATE_UNKNOWN=0, TC_STATE_CLIENT_TO_PEER_CONNECTING, TC_STATE_PEER_CONNECTING, TC_STATE_PEER_CONNECTED, TC_STATE_READY, TC_STATE_FAILED }; typedef enum _TC_STATE TC_STATE; typedef u32bits tcp_connection_id; typedef struct { size_t sz; ioa_network_buffer_handle *bufs; } unsent_buffer; struct _tcp_connection { TC_STATE state; tcp_connection_id id; ioa_addr peer_addr; ioa_socket_handle client_s; ioa_socket_handle peer_s; ioa_timer_handle peer_conn_timeout; ioa_timer_handle conn_bind_timeout; stun_tid tid; void *owner; //a int done; unsent_buffer ub_to_client; }; typedef struct _tcp_connection_list { size_t sz; tcp_connection **elems; } tcp_connection_list; //////////////////////////////// #define TURN_PERMISSION_HASHTABLE_SIZE (0x8) #define TURN_PERMISSION_ARRAY_SIZE (0x3) struct _allocation; typedef struct _ch_info { u16bits chnum; int allocated; u16bits port; ioa_addr peer_addr; turn_time_t expiration_time; ioa_timer_handle lifetime_ev; void *owner; //perm TURN_CHANNEL_HANDLER_KERNEL kernel_channel; } ch_info; ///////////// "channel" map ///////////////////// #define CH_MAP_HASH_SIZE (0x8) #define CH_MAP_ARRAY_SIZE (0x3) typedef struct _chn_map_array { ch_info main_chns[CH_MAP_ARRAY_SIZE]; size_t extra_sz; ch_info **extra_chns; } ch_map_array; typedef struct _ch_map { ch_map_array table[CH_MAP_HASH_SIZE]; } ch_map; ch_info *ch_map_get(ch_map* map, u16bits chnum, int new_chn); void ch_map_clean(ch_map* map); //////////////////////////// typedef struct _turn_permission_info { int allocated; lm_map chns; ioa_addr addr; turn_time_t expiration_time; ioa_timer_handle lifetime_ev; void* owner; //a int verbose; unsigned long long session_id; } turn_permission_info; typedef struct _turn_permission_slot { turn_permission_info info; } turn_permission_slot; typedef struct _turn_permission_array { turn_permission_slot main_slots[TURN_PERMISSION_ARRAY_SIZE]; size_t extra_sz; turn_permission_slot **extra_slots; } turn_permission_array; typedef struct _turn_permission_hashtable { turn_permission_array table[TURN_PERMISSION_HASHTABLE_SIZE]; } turn_permission_hashtable; //////////////// ALLOCATION ////////////////////// #define ALLOC_IPV4_INDEX (0) #define ALLOC_IPV6_INDEX (1) #define ALLOC_PROTOCOLS_NUMBER (2) #define ALLOC_INDEX(family) ((((family)==AF_INET6)) ? ALLOC_IPV6_INDEX : ALLOC_IPV4_INDEX ) #define ALLOC_INDEX_ADDR(addr) ALLOC_INDEX(((addr)->ss).sa_family) typedef struct _allocation { int is_valid; stun_tid tid; turn_permission_hashtable addr_to_perm; relay_endpoint_session relay_sessions[ALLOC_PROTOCOLS_NUMBER]; int relay_sessions_failure[ALLOC_PROTOCOLS_NUMBER]; ch_map chns; /* chnum-to-ch_info* */ void *owner; //ss ur_map *tcp_connections; //global (per turn server) reference tcp_connection_list tcs; //local reference } allocation; //////////// CHANNELS //////////////////// u16bits get_turn_channel_number(turn_permission_info* tinfo, ioa_addr *addr); ch_info *get_turn_channel(turn_permission_info* tinfo, ioa_addr *addr); void turn_channel_delete(ch_info* chn); /////////// ALLOCATION //////////// void init_allocation(void *owner, allocation* a, ur_map *tcp_connections); void clear_allocation(allocation *a); void turn_permission_clean(turn_permission_info* tinfo); void set_allocation_lifetime_ev(allocation *a, turn_time_t exp_time, ioa_timer_handle ev, int family); int is_allocation_valid(const allocation* a); void set_allocation_valid(allocation* a, int value); turn_permission_info* allocation_get_permission(allocation* a, const ioa_addr *addr); turn_permission_hashtable* allocation_get_turn_permission_hashtable(allocation *a); turn_permission_info* allocation_add_permission(allocation *a, const ioa_addr* addr); ch_info* allocation_get_new_ch_info(allocation* a, u16bits chnum, ioa_addr* peer_addr); ch_info* allocation_get_ch_info(allocation* a, u16bits chnum); ch_info* allocation_get_ch_info_by_peer_addr(allocation* a, ioa_addr* peer_addr); relay_endpoint_session *get_relay_session(allocation *a, int family); int get_relay_session_failure(allocation *a, int family); void set_relay_session_failure(allocation *a, int family); ioa_socket_handle get_relay_socket(allocation *a, int family); void set_allocation_family_invalid(allocation *a, int family); tcp_connection *get_and_clean_tcp_connection_by_id(ur_map *map, tcp_connection_id id); tcp_connection *get_tcp_connection_by_id(ur_map *map, tcp_connection_id id); tcp_connection *get_tcp_connection_by_peer(allocation *a, ioa_addr *peer_addr); int can_accept_tcp_connection_from_peer(allocation *a, ioa_addr *peer_addr, int server_relay); tcp_connection *create_tcp_connection(u08bits server_id, allocation *a, stun_tid *tid, ioa_addr *peer_addr, int *err_code); void delete_tcp_connection(tcp_connection *tc); void clear_unsent_buffer(unsent_buffer *ub); void add_unsent_buffer(unsent_buffer *ub, ioa_network_buffer_handle nbh); ioa_network_buffer_handle top_unsent_buffer(unsent_buffer *ub); void pop_unsent_buffer(unsent_buffer *ub); /////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_TURN_A_LIB__ turnserver-4.5.1.1/src/server/ns_turn_server.h0000664000175000017500000002024613436577655020142 0ustar misimisi/* * 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. */ #ifndef __TURN_SERVER__ #define __TURN_SERVER__ #include "ns_turn_utils.h" #include "ns_turn_session.h" #ifdef __cplusplus extern "C" { #endif //////////// defines ////////////// #define TURN_SESSION_ID_FACTOR (1000000000000000LL) //////////// ALTERNATE-SERVER ///////////// struct _turn_server_addrs_list { ioa_addr *addrs; volatile size_t size; turn_mutex m; }; typedef struct _turn_server_addrs_list turn_server_addrs_list_t; void init_turn_server_addrs_list(turn_server_addrs_list_t *l); ////////// RFC 5780 /////////////////////// typedef int (*get_alt_addr_cb)(ioa_addr *addr, ioa_addr *alt_addr); typedef int (*send_message_cb)(ioa_engine_handle e, ioa_network_buffer_handle nbh, ioa_addr *origin, ioa_addr *destination); ////////////////////////////////////////// extern int TURN_MAX_ALLOCATE_TIMEOUT; extern int TURN_MAX_ALLOCATE_TIMEOUT_STUN_ONLY; typedef u08bits turnserver_id; enum _MESSAGE_TO_RELAY_TYPE { RMT_UNKNOWN = 0, RMT_SOCKET, RMT_CB_SOCKET, RMT_MOBILE_SOCKET, RMT_CANCEL_SESSION }; typedef enum _MESSAGE_TO_RELAY_TYPE MESSAGE_TO_RELAY_TYPE; struct socket_message { ioa_socket_handle s; ioa_net_data nd; int can_resume; }; typedef enum { DONT_FRAGMENT_UNSUPPORTED=0, DONT_FRAGMENT_SUPPORTED, DONT_FRAGMENT_SUPPORT_EMULATED } dont_fragment_option_t; struct _turn_turnserver; typedef struct _turn_turnserver turn_turnserver; typedef void (*get_username_resume_cb)(int success, int oauth, int max_session_time, hmackey_t hmackey, password_t pwd, turn_turnserver *server, u64bits ctxkey, ioa_net_data *in_buffer, u08bits* realm); typedef u08bits *(*get_user_key_cb)(turnserver_id id, turn_credential_type ct, int in_oauth, int *out_oauth, u08bits *uname, u08bits *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, u64bits ctxkey, int *postpone_reply); typedef int (*check_new_allocation_quota_cb)(u08bits *username, int oauth, u08bits *realm); typedef void (*release_allocation_quota_cb)(u08bits *username, int oauth, u08bits *realm); typedef int (*send_socket_to_relay_cb)(turnserver_id id, u64bits cid, stun_tid *tid, ioa_socket_handle s, int message_integrity, MESSAGE_TO_RELAY_TYPE rmt, ioa_net_data *nd, int can_resume); typedef int (*send_turn_session_info_cb)(struct turn_session_info *tsi); typedef void (*send_https_socket_cb)(ioa_socket_handle s); typedef band_limit_t (*allocate_bps_cb)(band_limit_t bps, int positive); struct _turn_turnserver { turnserver_id id; turnsession_id session_id_counter; ur_map *sessions_map; turn_time_t ctime; ioa_engine_handle e; int verbose; int fingerprint; int rfc5780; vintp check_origin; vintp stale_nonce; vintp max_allocate_lifetime; vintp channel_lifetime; vintp permission_lifetime; vintp stun_only; vintp no_stun; vintp prod; vintp web_admin_listen_on_workers; vintp secure_stun; turn_credential_type ct; get_alt_addr_cb alt_addr_cb; send_message_cb sm_cb; dont_fragment_option_t dont_fragment; int (*disconnect)(ts_ur_super_session*); get_user_key_cb userkeycb; check_new_allocation_quota_cb chquotacb; release_allocation_quota_cb raqcb; int external_ip_set; ioa_addr external_ip; vintp allow_loopback_peers; vintp no_multicast_peers; send_turn_session_info_cb send_turn_session_info; send_https_socket_cb send_https_socket; /* RFC 6062 ==>> */ vintp no_udp_relay; vintp no_tcp_relay; ur_map *tcp_relay_connections; send_socket_to_relay_cb send_socket_to_relay; /* <<== RFC 6062 */ /* Alternate servers ==>> */ turn_server_addrs_list_t *alternate_servers_list; size_t as_counter; turn_server_addrs_list_t *tls_alternate_servers_list; size_t tls_as_counter; turn_server_addrs_list_t *aux_servers_list; int self_udp_balance; /* White/black listing of address ranges */ ip_range_list_t* ip_whitelist; ip_range_list_t* ip_blacklist; /* Mobility */ vintp mobility; ur_map *mobile_connections_map; /* Server relay */ int server_relay; /* Bandwidth draft: */ allocate_bps_cb allocate_bps_func; /* oAuth: */ int oauth; const char* oauth_server_name; /* Keep Address Family */ int keep_address_family; }; const char * get_version(turn_turnserver *server); /////////////////////////////////////////// void init_turn_server(turn_turnserver* server, turnserver_id id, int verbose, ioa_engine_handle e, turn_credential_type ct, int stun_port, int fingerprint, dont_fragment_option_t dont_fragment, get_user_key_cb userkeycb, check_new_allocation_quota_cb chquotacb, release_allocation_quota_cb raqcb, ioa_addr *external_addr, vintp check_origin, vintp no_tcp_relay, vintp no_udp_relay, vintp stale_nonce, vintp max_allocate_lifetime, vintp channel_lifetime, vintp permission_lifetime, vintp stun_only, vintp no_stun, vintp prod, vintp web_admin_listen_on_workers, turn_server_addrs_list_t *alternate_servers_list, turn_server_addrs_list_t *tls_alternate_servers_list, turn_server_addrs_list_t *aux_servers_list, int self_udp_balance, vintp no_multicast_peers, vintp allow_loopback_peers, ip_range_list_t* ip_whitelist, ip_range_list_t* ip_blacklist, send_socket_to_relay_cb send_socket_to_relay, vintp secure_stun, vintp mobility, int server_relay, send_turn_session_info_cb send_turn_session_info, send_https_socket_cb send_https_socket, allocate_bps_cb allocate_bps_func, int oauth, const char* oauth_server_name, int keep_address_family); ioa_engine_handle turn_server_get_engine(turn_turnserver *s); ////////// RFC 5780 /////////////////////// void set_rfc5780(turn_turnserver *server, get_alt_addr_cb cb, send_message_cb smcb); /////////////////////////////////////////// int open_client_connection_session(turn_turnserver* server, struct socket_message *sm); int shutdown_client_connection(turn_turnserver *server, ts_ur_super_session *ss, int force, const char* reason); void set_disconnect_cb(turn_turnserver* server, int (*disconnect)(ts_ur_super_session*)); int turnserver_accept_tcp_client_data_connection(turn_turnserver *server, tcp_connection_id tcid, stun_tid *tid, ioa_socket_handle s, int message_integrity, ioa_net_data *nd, int can_resume); int report_turn_session_info(turn_turnserver *server, ts_ur_super_session *ss, int force_invalid); turn_time_t get_turn_server_time(turn_turnserver *server); void turn_cancel_session(turn_turnserver *server, turnsession_id sid); /////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_SERVER__ turnserver-4.5.1.1/src/server/ns_turn_allocation.c0000664000175000017500000004471613436577655020764 0ustar misimisi/* * 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_allocation.h" /////////////// Permission forward declarations ///////////////// static void init_turn_permission_hashtable(turn_permission_hashtable *map); static void free_turn_permission_hashtable(turn_permission_hashtable *map); static turn_permission_info* get_from_turn_permission_hashtable(turn_permission_hashtable *map, const ioa_addr *addr); /////////////// ALLOCATION ////////////////////////////////////// void init_allocation(void *owner, allocation* a, ur_map *tcp_connections) { if(a) { ns_bzero(a,sizeof(allocation)); a->owner = owner; a->tcp_connections = tcp_connections; init_turn_permission_hashtable(&(a->addr_to_perm)); } } void clear_allocation(allocation *a) { if (a) { if(a->is_valid) turn_report_allocation_delete(a); if(a->tcs.elems) { size_t i; size_t sz = a->tcs.sz; for(i=0;itcs.elems[i]; if(tc) { delete_tcp_connection(tc); a->tcs.elems[i] = NULL; } } turn_free(a->tcs.elems,sz*sizeof(tcp_connection*)); a->tcs.elems = NULL; } a->tcs.sz = 0; { int i; for(i = 0;irelay_sessions[i].s, a->owner); clear_relay_endpoint_session_data(&(a->relay_sessions[i])); IOA_EVENT_DEL(a->relay_sessions[i].lifetime_ev); } } /* The order is important here: */ free_turn_permission_hashtable(&(a->addr_to_perm)); ch_map_clean(&(a->chns)); a->is_valid=0; } } relay_endpoint_session *get_relay_session(allocation *a, int family) { if(a) return &(a->relay_sessions[ALLOC_INDEX(family)]); return NULL; } int get_relay_session_failure(allocation *a, int family) { if(a) return a->relay_sessions_failure[ALLOC_INDEX(family)]; return 0; } void set_relay_session_failure(allocation *a, int family) { if(a) a->relay_sessions_failure[ALLOC_INDEX(family)] = 1; } ioa_socket_handle get_relay_socket(allocation *a, int family) { if(a) return a->relay_sessions[ALLOC_INDEX(family)].s; return NULL; } void set_allocation_family_invalid(allocation *a, int family) { if(a) { size_t index = ALLOC_INDEX(family); if(a->relay_sessions[index].s) { if(a->tcs.elems) { size_t i; size_t sz = a->tcs.sz; for(i=0;itcs.elems[i]; if(tc) { if(tc->peer_s && (get_ioa_socket_address_family(tc->peer_s) == family)) { delete_tcp_connection(tc); a->tcs.elems[i] = NULL; } } } } clear_ioa_socket_session_if(a->relay_sessions[index].s, a->owner); clear_relay_endpoint_session_data(&(a->relay_sessions[index])); IOA_EVENT_DEL(a->relay_sessions[index].lifetime_ev); } } } void set_allocation_lifetime_ev(allocation *a, turn_time_t exp_time, ioa_timer_handle ev, int family) { if (a) { IOA_EVENT_DEL(a->relay_sessions[ALLOC_INDEX(family)].lifetime_ev); a->relay_sessions[ALLOC_INDEX(family)].expiration_time = exp_time; a->relay_sessions[ALLOC_INDEX(family)].lifetime_ev = ev; } } int is_allocation_valid(const allocation* a) { if(a) return a->is_valid; else return 0; } void set_allocation_valid(allocation* a, int value) { if(a) a->is_valid=value; } turn_permission_info* allocation_get_permission(allocation* a, const ioa_addr *addr) { if(a) { return get_from_turn_permission_hashtable(&(a->addr_to_perm), addr); } return NULL; } ///////////////////////////// TURN_PERMISSION ///////////////////////////////// static int delete_channel_info_from_allocation_map(ur_map_key_type key, ur_map_value_type value); void turn_permission_clean(turn_permission_info* tinfo) { if (tinfo && tinfo->allocated) { if(tinfo->verbose) { char s[257]="\0"; addr_to_string(&(tinfo->addr),(u08bits*)s); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "session %018llu: peer %s deleted\n",tinfo->session_id,s); } if(!(tinfo->lifetime_ev)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: strange (1) permission to be cleaned\n",__FUNCTION__); } IOA_EVENT_DEL(tinfo->lifetime_ev); lm_map_foreach(&(tinfo->chns), (foreachcb_type) delete_channel_info_from_allocation_map); lm_map_clean(&(tinfo->chns)); ns_bzero(tinfo,sizeof(turn_permission_info)); } } static void init_turn_permission_hashtable(turn_permission_hashtable *map) { if (map) ns_bzero(map,sizeof(turn_permission_hashtable)); } static void free_turn_permission_hashtable(turn_permission_hashtable *map) { if(map) { size_t i; for(i=0;itable[i]); { size_t j; for(j=0;jmain_slots[j]); if(slot->info.allocated) { turn_permission_clean(&(slot->info)); } } } if(parray->extra_slots) { size_t j; for(j=0;jextra_sz;++j) { turn_permission_slot *slot = parray->extra_slots[j]; if(slot) { if(slot->info.allocated) { turn_permission_clean(&(slot->info)); } turn_free(slot,sizeof(turn_permission_slot)); } } turn_free(parray->extra_slots, parray->extra_sz * sizeof(turn_permission_slot*)); parray->extra_slots = NULL; } parray->extra_sz = 0; } } } static turn_permission_info* get_from_turn_permission_hashtable(turn_permission_hashtable *map, const ioa_addr *addr) { if (!addr || !map) return NULL; u32bits index = addr_hash_no_port(addr) & (TURN_PERMISSION_HASHTABLE_SIZE-1); turn_permission_array *parray = &(map->table[index]); { size_t i; for (i = 0; i < TURN_PERMISSION_ARRAY_SIZE; ++i) { turn_permission_slot *slot = &(parray->main_slots[i]); if (slot->info.allocated && addr_eq_no_port(&(slot->info.addr), addr)) { return &(slot->info); } } } if(parray->extra_slots) { size_t i; size_t sz = parray->extra_sz; for (i = 0; i < sz; ++i) { turn_permission_slot *slot = parray->extra_slots[i]; if (slot->info.allocated && addr_eq_no_port(&(slot->info.addr), addr)) { return &(slot->info); } } } return NULL; } static void ch_info_clean(ch_info* c) { if (c) { if (c->kernel_channel) { DELETE_TURN_CHANNEL_KERNEL(c->kernel_channel); c->kernel_channel = 0; } IOA_EVENT_DEL(c->lifetime_ev); ns_bzero(c,sizeof(ch_info)); } } static int delete_channel_info_from_allocation_map(ur_map_key_type key, ur_map_value_type value) { UNUSED_ARG(key); if(value) { ch_info* chn = (ch_info*)value; if(chn->chnum <1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: strange (0) channel to be cleaned: chnum<1\n",__FUNCTION__); } ch_info_clean(chn); } return 0; } void turn_channel_delete(ch_info* chn) { if(chn) { int port = addr_get_port(&(chn->peer_addr)); if(port<1) { char s[129]; addr_to_string(&(chn->peer_addr),(u08bits*)s); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: strange (1) channel to be cleaned: port is empty: %s\n",__FUNCTION__,s); } { turn_permission_info* tinfo = (turn_permission_info*)chn->owner; if(tinfo) { lm_map_del(&(tinfo->chns), (ur_map_key_type)port,NULL); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s: strange (2) channel to be cleaned: permission is empty\n",__FUNCTION__); } } delete_channel_info_from_allocation_map((ur_map_key_type)port,(ur_map_value_type)chn); } } ch_info* allocation_get_new_ch_info(allocation* a, u16bits chnum, ioa_addr* peer_addr) { turn_permission_info* tinfo = get_from_turn_permission_hashtable(&(a->addr_to_perm), peer_addr); if (!tinfo) tinfo = allocation_add_permission(a, peer_addr); ch_info* chn = ch_map_get(&(a->chns), chnum, 1); chn->allocated = 1; chn->chnum = chnum; chn->port = addr_get_port(peer_addr); addr_cpy(&(chn->peer_addr), peer_addr); chn->owner = tinfo; lm_map_put(&(tinfo->chns), (ur_map_key_type) addr_get_port(peer_addr), (ur_map_value_type) chn); return chn; } ch_info* allocation_get_ch_info(allocation* a, u16bits chnum) { return ch_map_get(&(a->chns), chnum, 0); } ch_info* allocation_get_ch_info_by_peer_addr(allocation* a, ioa_addr* peer_addr) { turn_permission_info* tinfo = get_from_turn_permission_hashtable(&(a->addr_to_perm), peer_addr); if(tinfo) { return get_turn_channel(tinfo,peer_addr); } return NULL; } u16bits get_turn_channel_number(turn_permission_info* tinfo, ioa_addr *addr) { if (tinfo) { ur_map_value_type t = 0; if (lm_map_get(&(tinfo->chns), (ur_map_key_type)addr_get_port(addr), &t) && t) { ch_info* chn = (ch_info*) t; if (STUN_VALID_CHANNEL(chn->chnum)) { return chn->chnum; } } } return 0; } ch_info *get_turn_channel(turn_permission_info* tinfo, ioa_addr *addr) { if (tinfo) { ur_map_value_type t = 0; if (lm_map_get(&(tinfo->chns), (ur_map_key_type)addr_get_port(addr), &t) && t) { ch_info* chn = (ch_info*) t; if (STUN_VALID_CHANNEL(chn->chnum)) { return chn; } } } return NULL; } turn_permission_hashtable *allocation_get_turn_permission_hashtable(allocation *a) { return &(a->addr_to_perm); } turn_permission_info* allocation_add_permission(allocation *a, const ioa_addr* addr) { if (a && addr) { turn_permission_hashtable *map = &(a->addr_to_perm); u32bits hash = addr_hash_no_port(addr); size_t fds = (size_t) (hash & (TURN_PERMISSION_HASHTABLE_SIZE-1)); turn_permission_array *parray = &(map->table[fds]); turn_permission_slot *slot = NULL; { size_t i; for(i=0;imain_slots[i]); if(!(slot->info.allocated)) { break; } else { slot=NULL; } } } if(!slot) { size_t old_sz = parray->extra_sz; turn_permission_slot **slots = parray->extra_slots; if(slots) { size_t i; for(i=0;iinfo.allocated)) { break; } else { slot=NULL; } } } if(!slot) { size_t old_sz_mem = old_sz * sizeof(turn_permission_slot*); parray->extra_slots = (turn_permission_slot **) turn_realloc(parray->extra_slots, old_sz_mem, old_sz_mem + sizeof(turn_permission_slot*)); slots = parray->extra_slots; parray->extra_sz = old_sz + 1; slots[old_sz] = (turn_permission_slot *)turn_malloc(sizeof(turn_permission_slot)); slot = slots[old_sz]; } } ns_bzero(slot,sizeof(turn_permission_slot)); slot->info.allocated = 1; turn_permission_info *elem = &(slot->info); addr_cpy(&(elem->addr), addr); elem->owner = a; return elem; } else { return NULL; } } ch_info *ch_map_get(ch_map* map, u16bits chnum, int new_chn) { ch_info *ret = NULL; if(map) { size_t index = (size_t)(chnum & (CH_MAP_HASH_SIZE-1)); ch_map_array *a = &(map->table[index]); size_t i; for(i=0;imain_chns[i]); if(chi->allocated) { if(!new_chn && (chi->chnum == chnum)) { return chi; } } else if(new_chn) { return chi; } } size_t old_sz = a->extra_sz; if(old_sz && a->extra_chns) { for(i=0;iextra_chns[i]; if(chi) { if(chi->allocated) { if(!new_chn && (chi->chnum == chnum)) { return chi; } } else if(new_chn) { return chi; } } } } if(new_chn) { size_t old_sz_mem = old_sz * sizeof(ch_info*); a->extra_chns = (ch_info**)turn_realloc(a->extra_chns,old_sz_mem,old_sz_mem + sizeof(ch_info*)); a->extra_chns[old_sz] = (ch_info*)turn_malloc(sizeof(ch_info)); ns_bzero(a->extra_chns[old_sz],sizeof(ch_info)); a->extra_sz += 1; return a->extra_chns[old_sz]; } } return ret; } void ch_map_clean(ch_map* map) { if(map) { size_t index; for(index = 0; index < CH_MAP_HASH_SIZE; ++index) { ch_map_array *a = &(map->table[index]); size_t i; for(i=0;imain_chns[i]); if(chi->allocated) { ch_info_clean(chi); } } if(a->extra_chns) { size_t sz = a->extra_sz; for(i=0;iextra_chns[i]; if(chi) { if(chi->allocated) { ch_info_clean(chi); } turn_free(chi,sizeof(ch_info)); a->extra_chns[i] = NULL; } } turn_free(a->extra_chns, sizeof(ch_info*)*sz); a->extra_chns = NULL; } a->extra_sz = 0; } } } ////////////////// TCP connections /////////////////////////////// static void set_new_tc_id(u08bits server_id, tcp_connection *tc) { allocation *a = (allocation*)(tc->owner); ur_map *map = a->tcp_connections; u32bits newid; u32bits sid = server_id; sid = sid<<24; do { newid = 0; while (!newid) { newid = (u32bits)turn_random(); if(!newid) { continue; } newid = newid & 0x00FFFFFF; if(!newid) { continue; } newid = newid | sid; } } while(ur_map_get(map, (ur_map_key_type)newid, NULL)); tc->id = newid; ur_map_put(map, (ur_map_key_type)newid, (ur_map_value_type)tc); } tcp_connection *create_tcp_connection(u08bits server_id, allocation *a, stun_tid *tid, ioa_addr *peer_addr, int *err_code) { tcp_connection_list *tcl = &(a->tcs); if(tcl->elems) { size_t i; for(i=0;isz;++i) { tcp_connection *otc = tcl->elems[i]; if(otc) { if(addr_eq(&(otc->peer_addr),peer_addr)) { *err_code = 446; return NULL; } } } } tcp_connection *tc = (tcp_connection*)turn_malloc(sizeof(tcp_connection)); ns_bzero(tc,sizeof(tcp_connection)); addr_cpy(&(tc->peer_addr),peer_addr); if(tid) ns_bcopy(tid,&(tc->tid),sizeof(stun_tid)); tc->owner = a; int found = 0; if(a->tcs.elems) { size_t i; for(i=0;isz;++i) { tcp_connection *otc = tcl->elems[i]; if(!otc) { tcl->elems[i] = tc; found = 1; break; } } } if(!found) { size_t old_sz_mem = a->tcs.sz * sizeof(tcp_connection*); a->tcs.elems = (tcp_connection**)turn_realloc(a->tcs.elems,old_sz_mem,old_sz_mem+sizeof(tcp_connection*)); a->tcs.elems[a->tcs.sz] = tc; a->tcs.sz += 1; tcl = &(a->tcs); } set_new_tc_id(server_id, tc); return tc; } void delete_tcp_connection(tcp_connection *tc) { if(tc) { if(tc->done) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s: check on already closed tcp data connection: 0x%lx\n",__FUNCTION__,(unsigned long)tc); return; } tc->done = 1; clear_unsent_buffer(&(tc->ub_to_client)); IOA_EVENT_DEL(tc->peer_conn_timeout); IOA_EVENT_DEL(tc->conn_bind_timeout); allocation *a = (allocation*)(tc->owner); if(a) { ur_map *map = a->tcp_connections; if(map) { ur_map_del(map, (ur_map_key_type)(tc->id),NULL); } tcp_connection_list *tcl = &(a->tcs); if(tcl->elems) { size_t i; for(i=0;isz;++i) { if(tcl->elems[i] == tc) { tcl->elems[i] = NULL; break; } } } } IOA_CLOSE_SOCKET(tc->client_s); IOA_CLOSE_SOCKET(tc->peer_s); turn_free(tc,sizeof(tcp_connection)); } } tcp_connection *get_and_clean_tcp_connection_by_id(ur_map *map, tcp_connection_id id) { if(map) { ur_map_value_type t = 0; if (ur_map_get(map, (ur_map_key_type)id, &t) && t) { ur_map_del(map, (ur_map_key_type)id,NULL); return (tcp_connection*)t; } } return NULL; } tcp_connection *get_tcp_connection_by_id(ur_map *map, tcp_connection_id id) { if(map) { ur_map_value_type t = 0; if (ur_map_get(map, (ur_map_key_type)id, &t) && t) { return (tcp_connection*)t; } } return NULL; } tcp_connection *get_tcp_connection_by_peer(allocation *a, ioa_addr *peer_addr) { if(a && peer_addr) { tcp_connection_list *tcl = &(a->tcs); if(tcl->elems) { size_t i; size_t sz = tcl->sz; for(i=0;ielems[i]; if(tc) { if(addr_eq(&(tc->peer_addr),peer_addr)) { return tc; } } } } } return NULL; } int can_accept_tcp_connection_from_peer(allocation *a, ioa_addr *peer_addr, int server_relay) { if(server_relay) return 1; if(a && peer_addr) { return (get_from_turn_permission_hashtable(&(a->addr_to_perm), peer_addr) != NULL); } return 0; } //////////////// Unsent buffers ////////////////////// void clear_unsent_buffer(unsent_buffer *ub) { if(ub) { if(ub->bufs) { size_t sz; for(sz = 0; szsz; sz++) { ioa_network_buffer_handle nbh = ub->bufs[sz]; if(nbh) { ioa_network_buffer_delete(NULL, nbh); ub->bufs[sz] = NULL; } } turn_free(ub->bufs,sizeof(ioa_network_buffer_handle) * ub->sz); ub->bufs = NULL; } ub->sz = 0; } } void add_unsent_buffer(unsent_buffer *ub, ioa_network_buffer_handle nbh) { if(!ub || (ub->sz >= MAX_UNSENT_BUFFER_SIZE)) { ioa_network_buffer_delete(NULL, nbh); } else { ub->bufs = (ioa_network_buffer_handle*)turn_realloc(ub->bufs, sizeof(ioa_network_buffer_handle) * ub->sz, sizeof(ioa_network_buffer_handle) * (ub->sz+1)); ub->bufs[ub->sz] = nbh; ub->sz +=1; } } ioa_network_buffer_handle top_unsent_buffer(unsent_buffer *ub) { ioa_network_buffer_handle ret = NULL; if(ub && ub->bufs && ub->sz) { size_t sz; for(sz=0; szsz; ++sz) { if(ub->bufs[sz]) { ret = ub->bufs[sz]; break; } } } return ret; } void pop_unsent_buffer(unsent_buffer *ub) { if(ub && ub->bufs && ub->sz) { size_t sz; for(sz=0; szsz; ++sz) { if(ub->bufs[sz]) { ub->bufs[sz] = NULL; break; } } } } ////////////////////////////////////////////////////////////////// turnserver-4.5.1.1/src/apps/0000775000175000017500000000000013436577655014344 5ustar misimisiturnserver-4.5.1.1/src/apps/stunclient/0000775000175000017500000000000013436577655016534 5ustar misimisiturnserver-4.5.1.1/src/apps/stunclient/stunclient.c0000664000175000017500000003021613436577655021072 0ustar misimisi/* * 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 #include #include #include #include #include #include "ns_turn_utils.h" #include "apputils.h" #include "stun_buffer.h" #ifdef __cplusplus #include "TurnMsgLib.h" #endif //////////////////////////////////////////////////// static int udp_fd = -1; static ioa_addr real_local_addr; static int counter = 0; #ifdef __cplusplus static int run_stunclient(const char* rip, int rport, int *port, int *rfc5780, int response_port, int change_ip, int change_port, int padding) { ioa_addr remote_addr; int new_udp_fd = -1; memset((void *) &remote_addr, 0, sizeof(ioa_addr)); if (make_ioa_addr((const u08bits*) rip, rport, &remote_addr) < 0) err(-1, NULL); if (udp_fd < 0) { udp_fd = socket(remote_addr.ss.sa_family, SOCK_DGRAM, 0); if (udp_fd < 0) err(-1, NULL); if (!addr_any(&real_local_addr)) { if (addr_bind(udp_fd, &real_local_addr,0,1,UDP_SOCKET) < 0) err(-1, NULL); } } if (response_port >= 0) { new_udp_fd = socket(remote_addr.ss.sa_family, SOCK_DGRAM, 0); if (new_udp_fd < 0) err(-1, NULL); addr_set_port(&real_local_addr, response_port); if (addr_bind(new_udp_fd, &real_local_addr, 0, 1, UDP_SOCKET) < 0) err(-1, NULL); } turn::StunMsgRequest req(STUN_METHOD_BINDING); req.constructBindingRequest(); if (response_port >= 0) { turn::StunAttrResponsePort rpa; rpa.setResponsePort((u16bits)response_port); try { req.addAttr(rpa); } catch(turn::WrongStunAttrFormatException &ex1) { printf("Wrong rp attr format\n"); exit(-1); } catch(turn::WrongStunBufferFormatException &ex2) { printf("Wrong stun buffer format (1)\n"); exit(-1); } catch(...) { printf("Wrong something (1)\n"); exit(-1); } } if (change_ip || change_port) { turn::StunAttrChangeRequest cra; cra.setChangeIp(change_ip); cra.setChangePort(change_port); try { req.addAttr(cra); } catch(turn::WrongStunAttrFormatException &ex1) { printf("Wrong cr attr format\n"); exit(-1); } catch(turn::WrongStunBufferFormatException &ex2) { printf("Wrong stun buffer format (2)\n"); exit(-1); } catch(...) { printf("Wrong something (2)\n"); exit(-1); } } if (padding) { turn::StunAttrPadding pa; pa.setPadding(1500); try { req.addAttr(pa); } catch(turn::WrongStunAttrFormatException &ex1) { printf("Wrong p attr format\n"); exit(-1); } catch(turn::WrongStunBufferFormatException &ex2) { printf("Wrong stun buffer format (3)\n"); exit(-1); } catch(...) { printf("Wrong something (3)\n"); exit(-1); } } { int len = 0; int slen = get_ioa_addr_len(&remote_addr); do { len = sendto(udp_fd, req.getRawBuffer(), req.getSize(), 0, (struct sockaddr*) &remote_addr, (socklen_t) slen); } while (len < 0 && ((errno == EINTR) || (errno == ENOBUFS) || (errno == EAGAIN))); if (len < 0) err(-1, NULL); } if (addr_get_from_sock(udp_fd, &real_local_addr) < 0) { printf("%s: Cannot get address from local socket\n", __FUNCTION__); } else { *port = addr_get_port(&real_local_addr); } { if(new_udp_fd >= 0) { close(udp_fd); udp_fd = new_udp_fd; new_udp_fd = -1; } } { int len = 0; stun_buffer buf; u08bits *ptr = buf.buf; int recvd = 0; const int to_recv = sizeof(buf.buf); do { len = recv(udp_fd, ptr, to_recv - recvd, 0); if (len > 0) { recvd += len; ptr += len; break; } } while (len < 0 && (errno == EINTR)); if (recvd > 0) len = recvd; buf.len = len; try { turn::StunMsgResponse res(buf.buf, sizeof(buf.buf), (size_t)buf.len, true); if (res.isCommand()) { if(res.isSuccess()) { if (res.isBindingResponse()) { ioa_addr reflexive_addr; addr_set_any(&reflexive_addr); turn::StunAttrIterator iter(res,STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS); if (!iter.eof()) { turn::StunAttrAddr addr(iter); addr.getAddr(reflexive_addr); turn::StunAttrIterator iter1(res,STUN_ATTRIBUTE_OTHER_ADDRESS); if (!iter1.eof()) { *rfc5780 = 1; printf("\n========================================\n"); printf("RFC 5780 response %d\n",++counter); ioa_addr other_addr; turn::StunAttrAddr addr1(iter1); addr1.getAddr(other_addr); turn::StunAttrIterator iter2(res,STUN_ATTRIBUTE_RESPONSE_ORIGIN); if (!iter2.eof()) { ioa_addr response_origin; turn::StunAttrAddr addr2(iter2); addr2.getAddr(response_origin); addr_debug_print(1, &response_origin, "Response origin: "); } addr_debug_print(1, &other_addr, "Other addr: "); } addr_debug_print(1, &reflexive_addr, "UDP reflexive addr"); } else { printf("Cannot read the response\n"); } } else { printf("Wrong type of response\n"); } } else { int err_code = res.getError(); std::string reason = res.getReason(); printf("The response is an error %d (%s)\n", err_code, reason.c_str()); } } else { printf("The response is not a response message\n"); } } catch(...) { printf("The response is not a well formed STUN message\n"); } } return 0; } #else static int run_stunclient(const char* rip, int rport, int *port, int *rfc5780, int response_port, int change_ip, int change_port, int padding) { ioa_addr remote_addr; int new_udp_fd = -1; stun_buffer buf; ns_bzero(&remote_addr, sizeof(remote_addr)); if (make_ioa_addr((const u08bits*) rip, rport, &remote_addr) < 0) err(-1, NULL); if (udp_fd < 0) { udp_fd = socket(remote_addr.ss.sa_family, CLIENT_DGRAM_SOCKET_TYPE, CLIENT_DGRAM_SOCKET_PROTOCOL); if (udp_fd < 0) err(-1, NULL); if (!addr_any(&real_local_addr)) { if (addr_bind(udp_fd, &real_local_addr,0,1,UDP_SOCKET) < 0) err(-1, NULL); } } if (response_port >= 0) { new_udp_fd = socket(remote_addr.ss.sa_family, CLIENT_DGRAM_SOCKET_TYPE, CLIENT_DGRAM_SOCKET_PROTOCOL); if (new_udp_fd < 0) err(-1, NULL); addr_set_port(&real_local_addr, response_port); if (addr_bind(new_udp_fd, &real_local_addr,0,1,UDP_SOCKET) < 0) err(-1, NULL); } stun_prepare_binding_request(&buf); if (response_port >= 0) { stun_attr_add_response_port_str((u08bits*) (buf.buf), (size_t*) &(buf.len), (u16bits) response_port); } if (change_ip || change_port) { stun_attr_add_change_request_str((u08bits*) buf.buf, (size_t*) &(buf.len), change_ip, change_port); } if (padding) { if(stun_attr_add_padding_str((u08bits*) buf.buf, (size_t*) &(buf.len), 1500)<0) { printf("%s: ERROR: Cannot add padding\n",__FUNCTION__); } } { int len = 0; int slen = get_ioa_addr_len(&remote_addr); do { len = sendto(udp_fd, buf.buf, buf.len, 0, (struct sockaddr*) &remote_addr, (socklen_t) slen); } while (len < 0 && ((errno == EINTR) || (errno == ENOBUFS) || (errno == EAGAIN))); if (len < 0) err(-1, NULL); } if (addr_get_from_sock(udp_fd, &real_local_addr) < 0) { printf("%s: Cannot get address from local socket\n", __FUNCTION__); } else { *port = addr_get_port(&real_local_addr); } { if(new_udp_fd >= 0) { socket_closesocket(udp_fd); udp_fd = new_udp_fd; new_udp_fd = -1; } } { int len = 0; u08bits *ptr = buf.buf; int recvd = 0; const int to_recv = sizeof(buf.buf); do { len = recv(udp_fd, ptr, to_recv - recvd, 0); if (len > 0) { recvd += len; ptr += len; break; } } while (len < 0 && ((errno == EINTR) || (errno == EAGAIN))); if (recvd > 0) len = recvd; buf.len = len; if (stun_is_command_message(&buf)) { if (stun_is_response(&buf)) { if (stun_is_success_response(&buf)) { if (stun_is_binding_response(&buf)) { ioa_addr reflexive_addr; addr_set_any(&reflexive_addr); if (stun_attr_get_first_addr(&buf, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &reflexive_addr, NULL) >= 0) { stun_attr_ref sar = stun_attr_get_first_by_type_str(buf.buf, buf.len, STUN_ATTRIBUTE_OTHER_ADDRESS); if (sar) { *rfc5780 = 1; printf("\n========================================\n"); printf("RFC 5780 response %d\n",++counter); ioa_addr other_addr; stun_attr_get_addr_str((u08bits *) buf.buf, (size_t) buf.len, sar, &other_addr, NULL); sar = stun_attr_get_first_by_type_str(buf.buf, buf.len, STUN_ATTRIBUTE_RESPONSE_ORIGIN); if (sar) { ioa_addr response_origin; stun_attr_get_addr_str((u08bits *) buf.buf, (size_t) buf.len, sar, &response_origin, NULL); addr_debug_print(1, &response_origin, "Response origin: "); } addr_debug_print(1, &other_addr, "Other addr: "); } addr_debug_print(1, &reflexive_addr, "UDP reflexive addr"); } else { printf("Cannot read the response\n"); } } else { printf("Wrong type of response\n"); } } else { int err_code = 0; u08bits err_msg[1025] = "\0"; size_t err_msg_size = sizeof(err_msg); if (stun_is_error_response(&buf, &err_code, err_msg, err_msg_size)) { printf("The response is an error %d (%s)\n", err_code, (char*) err_msg); } else { printf("The response is an unrecognized error\n"); } } } else { printf("The response is not a response message\n"); } } else { printf("The response is not a STUN message\n"); } } return 0; } #endif //////////////// local definitions ///////////////// static char Usage[] = "Usage: stunclient [options] address\n" "Options:\n" " -p STUN server port (Default: 3478)\n" " -L Local address to use (optional)\n" " -f Force RFC 5780 processing\n"; ////////////////////////////////////////////////// int main(int argc, char **argv) { int port = DEFAULT_STUN_PORT; char local_addr[256]="\0"; int c=0; int forceRfc5780 = 0; set_logfile("stdout"); set_system_parameters(0); ns_bzero(local_addr, sizeof(local_addr)); while ((c = getopt(argc, argv, "p:L:f")) != -1) { switch(c) { case 'f': forceRfc5780 = 1; break; case 'p': port = atoi(optarg); break; case 'L': STRCPY(local_addr, optarg); break; default: fprintf(stderr,"%s\n", Usage); exit(1); } } if(optind>=argc) { fprintf(stderr, "%s\n", Usage); exit(-1); } addr_set_any(&real_local_addr); if(local_addr[0]) { if(make_ioa_addr((const u08bits*)local_addr, 0, &real_local_addr)<0) { err(-1,NULL); } } int local_port = -1; int rfc5780 = 0; run_stunclient(argv[optind], port, &local_port, &rfc5780,-1,0,0,0); if(rfc5780 || forceRfc5780) { run_stunclient(argv[optind], port, &local_port, &rfc5780,local_port+1,1,1,0); run_stunclient(argv[optind], port, &local_port, &rfc5780,-1,1,1,1); } socket_closesocket(udp_fd); return 0; } turnserver-4.5.1.1/src/apps/rfc5769/0000775000175000017500000000000013436577655015451 5ustar misimisiturnserver-4.5.1.1/src/apps/rfc5769/rfc5769check.c0000664000175000017500000003703713436577655017732 0ustar misimisi/* * 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 #include #include #include #include #include #include "ns_turn_utils.h" #include "apputils.h" #include "stun_buffer.h" //////////// OAUTH ////////////////// static const char* encs[]={ #if !defined(TURN_NO_GCM) "A128GCM", "A256GCM", #endif NULL}; static int print_extra = 0; void print_field5769(const char* name, const void* f0, size_t len); void print_field5769(const char* name, const void* f0, size_t len) { const unsigned char* f = (const unsigned char*)f0; printf("\nfield %s %lu==>>\n",name,(unsigned long)len); size_t i; for(i = 0;i1) print_extra = 1; set_logfile("stdout"); set_system_parameters(0); { const unsigned char reqstc[] = "\x00\x01\x00\x58" "\x21\x12\xa4\x42" "\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae" "\x80\x22\x00\x10" "STUN test client" "\x00\x24\x00\x04" "\x6e\x00\x01\xff" "\x80\x29\x00\x08" "\x93\x2f\xf9\xb1\x51\x26\x3b\x36" "\x00\x06\x00\x09" "\x65\x76\x74\x6a\x3a\x68\x36\x76\x59\x20\x20\x20" "\x00\x08\x00\x14" "\x9a\xea\xa7\x0c\xbf\xd8\xcb\x56\x78\x1e\xf2\xb5" "\xb2\xd3\xf2\x49\xc1\xb5\x71\xa2" "\x80\x28\x00\x04" "\xe5\x7a\x3b\xcf"; u08bits buf[sizeof(reqstc)]; memcpy(buf, reqstc, sizeof(reqstc)); {//fingerprintfs etc res = stun_is_command_message_full_check_str(buf, sizeof(reqstc) - 1, 1, NULL); printf("RFC 5769 message fingerprint test(0) result: "); if (res) { printf("success\n"); } else if (res == 0) { printf("failure on fingerprint(0) check\n"); exit(-1); } } {//short-term credentials u08bits uname[33]; u08bits realm[33]; u08bits upwd[33]; strcpy((char*) upwd, "VOkJxbRl1RmTxUk/WvJxBt"); res = stun_check_message_integrity_str(TURN_CREDENTIALS_SHORT_TERM, buf, sizeof(reqstc) - 1, uname, realm, upwd, shatype); printf("RFC 5769 simple request short-term credentials and integrity test result: "); if (res > 0) { printf("success\n"); } else if (res == 0) { printf("failure on integrity check\n"); exit(-1); } else { printf("failure on message structure check\n"); exit(-1); } } {//negative fingerprint buf[27] = 23; res = stun_is_command_message_full_check_str(buf, sizeof(reqstc) - 1, 1, NULL); printf("RFC 5769 NEGATIVE fingerprint test(0) result: "); if (!res) { printf("success\n"); } else if (res == 0) { printf("failure on NEGATIVE fingerprint check\n"); exit(-1); } } } { const unsigned char reqltc[] = "\x00\x01\x00\x60" "\x21\x12\xa4\x42" "\x78\xad\x34\x33\xc6\xad\x72\xc0\x29\xda\x41\x2e" "\x00\x06\x00\x12" "\xe3\x83\x9e\xe3\x83\x88\xe3\x83\xaa\xe3\x83\x83" "\xe3\x82\xaf\xe3\x82\xb9\x00\x00" "\x00\x15\x00\x1c" "\x66\x2f\x2f\x34\x39\x39\x6b\x39\x35\x34\x64\x36" "\x4f\x4c\x33\x34\x6f\x4c\x39\x46\x53\x54\x76\x79" "\x36\x34\x73\x41" "\x00\x14\x00\x0b" "\x65\x78\x61\x6d\x70\x6c\x65\x2e\x6f\x72\x67\x00" "\x00\x08\x00\x14" "\xf6\x70\x24\x65\x6d\xd6\x4a\x3e\x02\xb8\xe0\x71" "\x2e\x85\xc9\xa2\x8c\xa8\x96\x66"; u08bits user[] = "\xe3\x83\x9e\xe3\x83\x88\xe3\x83\xaa\xe3\x83\x83" "\xe3\x82\xaf\xe3\x82\xb9"; u08bits realm[33]; u08bits nonce[29]; u08bits upwd[33]; u08bits buf[sizeof(reqltc)]; memcpy(buf, reqltc, sizeof(reqltc)); u08bits uname[sizeof(user)]; memcpy(uname, user, sizeof(user)); strcpy((char*) realm, "example.org"); strcpy((char*) upwd, "TheMatrIX"); strcpy((char*)nonce,"f//499k954d6OL34oL9FSTvy64sA"); res = stun_check_message_integrity_str(TURN_CREDENTIALS_LONG_TERM, buf, sizeof(reqltc) - 1, uname, realm, upwd, shatype); printf("RFC 5769 message structure, long-term credentials and integrity test result: "); if (res > 0) { printf("success\n"); } else if (res == 0) { printf("failure on integrity check\n"); exit(-1); } else { printf("failure on message structure check\n"); exit(-1); } { //encoding test printf("RFC 5769 message encoding test result: "); size_t len = 0; u16bits message_type = STUN_METHOD_BINDING; stun_tid tid; u16bits *buf16 = (u16bits*)buf; u32bits *buf32 = (u32bits*)buf; memcpy(tid.tsx_id,"\x78\xad\x34\x33\xc6\xad\x72\xc0\x29\xda\x41\x2e",12); stun_init_buffer_str(buf,&len); message_type &= (u16bits)(0x3FFF); buf16[0]=nswap16(message_type); buf16[1]=0; buf32[1]=nswap32(STUN_MAGIC_COOKIE); stun_tid_message_cpy(buf, &tid); stun_attr_add_integrity_by_user_str(buf, &len, uname, realm, upwd, nonce, shatype); if(len != (sizeof(reqltc)-1)) { printf("failure: length %d, must be %d\n",(int)len,(int)(sizeof(reqltc)-1)); exit(-1); } if(memcmp(buf,reqltc,len)) { printf("failure: wrong message content\n"); { int lines = 29; int line = 0; int col = 0; int cols = 4; for(line = 0;line 0) { printf("success\n"); } else if (res == 0) { printf("failure on integrity check\n"); exit(-1); } else { printf("failure on message structure check\n"); exit(-1); } } {//negative fingerprint buf[27] = 23; res = stun_is_command_message_full_check_str(buf, sizeof(respv4) - 1, 1, NULL); printf("RFC 5769 NEGATIVE fingerprint test(1) result: "); if (!res) { printf("success\n"); } else if (res == 0) { printf("failure on NEGATIVE fingerprint check\n"); exit(-1); } } {//IPv4 addr ioa_addr addr4; ioa_addr addr4_test; printf("RFC 5769 IPv4 encoding result: "); res = stun_attr_get_first_addr_str(buf, sizeof(respv4)-1, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr4, NULL); if(res < 0) { printf("failure on message structure check\n"); exit(-1); } make_ioa_addr((const u08bits*)"192.0.2.1", 32853, &addr4_test); if(addr_eq(&addr4,&addr4_test)) { printf("success\n"); } else { printf("failure on IPv4 deconding check\n"); exit(-1); } } } { const unsigned char respv6[] = "\x01\x01\x00\x48" "\x21\x12\xa4\x42" "\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae" "\x80\x22\x00\x0b" "\x74\x65\x73\x74\x20\x76\x65\x63\x74\x6f\x72\x20" "\x00\x20\x00\x14" "\x00\x02\xa1\x47" "\x01\x13\xa9\xfa\xa5\xd3\xf1\x79" "\xbc\x25\xf4\xb5\xbe\xd2\xb9\xd9" "\x00\x08\x00\x14" "\xa3\x82\x95\x4e\x4b\xe6\x7b\xf1\x17\x84\xc9\x7c" "\x82\x92\xc2\x75\xbf\xe3\xed\x41" "\x80\x28\x00\x04" "\xc8\xfb\x0b\x4c"; u08bits buf[sizeof(respv6)]; { //decoding test memcpy(buf, respv6, sizeof(respv6)); res = stun_is_command_message_full_check_str(buf, sizeof(respv6) - 1, 1, NULL); printf("RFC 5769 message fingerprint test(2) result: "); if (res) { printf("success\n"); } else if (res == 0) { printf("failure on fingerprint(2) check\n"); exit(-1); } } {//short-term credentials test u08bits uname[33]; u08bits realm[33]; u08bits upwd[33]; strcpy((char*) upwd, "VOkJxbRl1RmTxUk/WvJxBt"); res = stun_check_message_integrity_str(TURN_CREDENTIALS_SHORT_TERM, buf, sizeof(respv6) - 1, uname, realm, upwd, shatype); printf("RFC 5769 IPv6 response short-term credentials and integrity test result: "); if (res > 0) { printf("success\n"); } else if (res == 0) { printf("failure on integrity check\n"); exit(-1); } else { printf("failure on message structure check\n"); exit(-1); } } {//negative decoding test buf[27] = 23; res = stun_is_command_message_full_check_str(buf, sizeof(respv6) - 1, 1, NULL); printf("RFC 5769 NEGATIVE fingerprint test(2) result: "); if (!res) { printf("success\n"); } else if (res == 0) { printf("failure on NEGATIVE fingerprint check\n"); exit(-1); } } {//IPv6 deconding test ioa_addr addr6; ioa_addr addr6_test; printf("RFC 5769 IPv6 encoding result: "); res = stun_attr_get_first_addr_str(buf, sizeof(respv6) - 1, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr6, NULL); if (res < 0) { printf("failure on message structure check\n"); exit(-1); } make_ioa_addr((const u08bits*) "2001:db8:1234:5678:11:2233:4455:6677", 32853, &addr6_test); if (addr_eq(&addr6, &addr6_test)) { printf("success\n"); } else { printf("failure on IPv6 deconding check\n"); exit(-1); } } } { if(check_oauth()<0) exit(-1); } return 0; } turnserver-4.5.1.1/src/apps/common/0000775000175000017500000000000013436577655015634 5ustar misimisiturnserver-4.5.1.1/src/apps/common/ns_turn_utils.c0000664000175000017500000004714013436577655020716 0ustar misimisi/* * 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 #include #include #include #include #include #include #include ////////// 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 volatile int to_reset_log_file = 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) { to_reset_log_file = 1; } } static void set_rtpfile(void) { if(to_reset_log_file) { printf("%s: resetting the log file\n",__FUNCTION__); reset_rtpprintf(); to_reset_log_file = 0; } 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) && (schlen0) 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 #include #include static volatile int tmm_init = 0; static pthread_mutex_t tm; typedef void* ptrtype; typedef std::set ptrs_t; typedef std::map str_to_ptrs_t; typedef std::map 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 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 //////////////////////////////// int is_secure_string(const u08bits *string, int sanitizesql) { int ret = 0; if(string) { unsigned char *s0 = (unsigned char*)turn_strdup((const char*)string); unsigned char *s = s0; while(*s) { *s = (unsigned char)tolower((int)*s); ++s; } s = s0; if(strstr((char*)s," ")||strstr((char*)s,"\t")||strstr((char*)s,"'")||strstr((char*)s,"\"")||strstr((char*)s,"\n")||strstr((char*)s,"\r")||strstr((char*)s,"\\")) { ; } else if(sanitizesql && strstr((char*)s,"union")&&strstr((char*)s,"select")) { ; } else { ret = 1; } turn_free(s,strlen((char*)s)); } return ret; } ////////////////////////////////////////////////////////////////// turnserver-4.5.1.1/src/apps/common/stun_buffer.h0000664000175000017500000001317213436577655020333 0ustar misimisi/* * 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. */ #ifndef __TURN_STUN_BUF__ #define __TURN_STUN_BUF__ #include "ns_turn_msg.h" #ifdef __cplusplus extern "C" { #endif /////////////////////////////////////////////////////////////// typedef struct _stun_buffer { u08bits channel[STUN_CHANNEL_HEADER_LENGTH]; u08bits buf[STUN_BUFFER_SIZE]; size_t len; u16bits offset; u08bits coffset; } stun_buffer; ////////////////////////////////////////////////////////////// int stun_init_buffer(stun_buffer *buf); int stun_get_size(const stun_buffer *buf); ////////////////////////////////////////////////////////////// void stun_tid_generate_in_message(stun_buffer* buf, stun_tid* id); void stun_tid_from_message(const stun_buffer *buf, stun_tid* id); /////////////////////////////////////////////////////////////// int stun_is_command_message(const stun_buffer* buf); int stun_is_request(const stun_buffer* buf); int stun_is_response(const stun_buffer* buf); int stun_is_success_response(const stun_buffer* buf); int stun_is_error_response(const stun_buffer* buf, int *err_code, u08bits *err_msg, size_t err_msg_size); int stun_is_indication(const stun_buffer* buf); u16bits stun_get_method(const stun_buffer* buf); u16bits stun_get_msg_type(const stun_buffer* buf); /////////////////////////////////////////////////////////////// void stun_init_request(u16bits method, stun_buffer* buf); void stun_init_indication(u16bits method, stun_buffer* buf); void stun_init_success_response(u16bits method, stun_buffer* buf, stun_tid* id); void stun_init_error_response(u16bits method, stun_buffer* buf, u16bits error_code, const u08bits *reason, stun_tid* id); /////////////////////////////////////////////////////////////// int stun_attr_add(stun_buffer* buf, u16bits attr, const s08bits* avalue, int alen); int stun_attr_add_channel_number(stun_buffer* buf, u16bits chnumber); int stun_attr_add_addr(stun_buffer *buf,u16bits attr_type, const ioa_addr* ca); stun_attr_ref stun_attr_get_first(const stun_buffer* buf); stun_attr_ref stun_attr_get_first_by_type(const stun_buffer* buf, u16bits attr_type); stun_attr_ref stun_attr_get_next(const stun_buffer* buf, stun_attr_ref prev); int stun_attr_get_addr(const stun_buffer *buf, stun_attr_ref attr, ioa_addr* ca, const ioa_addr *default_addr); int stun_attr_add_even_port(stun_buffer* buf, uint8_t value); int stun_attr_get_first_addr(const stun_buffer *buf, u16bits attr_type, ioa_addr* ca, const ioa_addr *default_addr); u16bits stun_attr_get_first_channel_number(const stun_buffer *buf); /////////////////////////////////////////////////////////////// int stun_get_command_message_len(const stun_buffer* buf); /////////////////////////////////////////////////////////////// int stun_init_channel_message(u16bits chnumber, stun_buffer* buf, int length, int do_padding); int stun_is_channel_message(stun_buffer* buf, u16bits* chnumber, int is_padding_madatory); /////////////////////////////////////////////////////////////// int stun_set_allocate_request(stun_buffer* buf, u32bits lifetime, int af4, int af6, u08bits transport, int mobile, const char* rt, int ep); int stun_set_allocate_response(stun_buffer* buf, stun_tid* tid, const ioa_addr *relayed_addr1, const ioa_addr *relayed_addr2, const ioa_addr *reflexive_addr, u32bits lifetime, u32bits max_lifetime, int error_code, const u08bits *reason, u64bits reservation_token, char *mobile_id); /////////////////////////////////////////////////////////////// void stun_set_binding_request(stun_buffer* buf); int stun_set_binding_response(stun_buffer* buf, stun_tid* tid, const ioa_addr *reflexive_addr, int error_code, const u08bits *reason); void stun_prepare_binding_request(stun_buffer* buf); int stun_is_binding_response(const stun_buffer* buf); /////////////////////////////////////////////////////////////// u16bits stun_set_channel_bind_request(stun_buffer* buf, const ioa_addr* peer_addr, u16bits channel_number); void stun_set_channel_bind_response(stun_buffer* buf, stun_tid* tid, int error_code, const u08bits *reason); /////////////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_STUN_BUF__ turnserver-4.5.1.1/src/apps/common/ns_turn_openssl.h0000664000175000017500000000403313436577655021240 0ustar misimisi/* * 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. */ #ifndef __NST_OPENSSL_LIB__ #define __NST_OPENSSL_LIB__ #include #include #include #include #include #include #include #include #include #include #if (defined LIBRESSL_VERSION_NUMBER && OPENSSL_VERSION_NUMBER == 0x20000000L) #undef OPENSSL_VERSION_NUMBER #define OPENSSL_VERSION_NUMBER 0x1000107FL #endif #endif //__NST_OPENSSL_LIB__ turnserver-4.5.1.1/src/apps/common/apputils.c0000664000175000017500000006432713436577655017655 0ustar misimisi/* * 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_msg.h" #include "apputils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(TURN_NO_SCTP) && defined(TURN_SCTP_INCLUDE) #include TURN_SCTP_INCLUDE #endif /************************/ int IS_TURN_SERVER = 0; /*********************** Sockets *********************************/ int socket_set_nonblocking(evutil_socket_t fd) { #if defined(WIN32) unsigned long nonblocking = 1; ioctlsocket(fd, FIONBIO, (unsigned long*) &nonblocking); #else if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { perror("O_NONBLOCK"); return -1; } #endif return 0; } void read_spare_buffer(evutil_socket_t fd) { if(fd >= 0) { static char buffer[65536]; recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT); } } int set_sock_buf_size(evutil_socket_t fd, int sz0) { int sz; sz = sz0; while (sz > 0) { if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const void*) (&sz), (socklen_t) sizeof(sz)) < 0) { sz = sz / 2; } else { break; } } if (sz < 1) { perror("Cannot set socket rcv size"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Cannot set rcv sock size %d on fd %d\n", sz0, fd); } sz = sz0; while (sz > 0) { if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void*) (&sz), (socklen_t) sizeof(sz)) < 0) { sz = sz / 2; } else { break; } } if (sz < 1) { perror("Cannot set socket snd size"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Cannot set snd sock size %d on fd %d\n", sz0, fd); } return 0; } int socket_tcp_set_keepalive(evutil_socket_t fd,SOCKET_TYPE st) { UNUSED_ARG(st); #ifdef SO_KEEPALIVE /* Set the keepalive option active */ { int on = 1; setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (const void*)&on, (socklen_t) sizeof(on)); } #else UNUSED_ARG(fd); #endif #ifdef SO_NOSIGPIPE { int on = 1; setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (const void*)&on, (socklen_t) sizeof(on)); } #endif return 0; } int socket_set_reusable(evutil_socket_t fd, int flag, SOCKET_TYPE st) { UNUSED_ARG(st); if (fd < 0) return -1; else { #if defined(WIN32) int use_reuseaddr = IS_TURN_SERVER; #else int use_reuseaddr = 1; #endif #if defined(SO_REUSEADDR) if (use_reuseaddr) { int on = flag; int ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void*) &on, (socklen_t) sizeof(on)); if (ret < 0) perror("SO_REUSEADDR"); } #endif #if !defined(TURN_NO_SCTP) #if defined(SCTP_REUSE_PORT) if (use_reuseaddr) { if(is_sctp_socket(st)) { int on = flag; int ret = setsockopt(fd, IPPROTO_SCTP, SCTP_REUSE_PORT, (const void*) &on, (socklen_t) sizeof(on)); if (ret < 0) perror("SCTP_REUSE_PORT"); } } #endif #endif #if defined(SO_REUSEPORT) if (use_reuseaddr) { int on = flag; setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (const void*) &on, (socklen_t) sizeof(on)); } #endif return 0; } } int sock_bind_to_device(evutil_socket_t fd, const unsigned char* ifname) { if (fd >= 0 && ifname && ifname[0]) { #if defined(SO_BINDTODEVICE) struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, (const char*) ifname, sizeof(ifr.ifr_name)); if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *) &ifr, sizeof(ifr)) < 0) { if (errno == EPERM) perror("You must obtain superuser privileges to bind a socket to device"); else perror("Cannot bind socket to device"); return -1; } return 0; #endif } return 0; } int addr_connect(evutil_socket_t fd, const ioa_addr* addr, int *out_errno) { if (!addr || fd < 0) return -1; else { int err = 0; do { if (addr->ss.sa_family == AF_INET) { err = connect(fd, (const struct sockaddr *) addr, sizeof(struct sockaddr_in)); } else if (addr->ss.sa_family == AF_INET6) { err = connect(fd, (const struct sockaddr *) addr, sizeof(struct sockaddr_in6)); } else { return -1; } } while (err < 0 && errno == EINTR); if(out_errno) *out_errno = errno; if (err < 0 && errno != EINPROGRESS) perror("Connect"); return err; } } int addr_bind(evutil_socket_t fd, const ioa_addr* addr, int reusable, int debug, SOCKET_TYPE st) { if (!addr || fd < 0) { return -1; } else { int ret = -1; socket_set_reusable(fd, reusable, st); if (addr->ss.sa_family == AF_INET) { do { ret = bind(fd, (const struct sockaddr *) addr, sizeof(struct sockaddr_in)); } while (ret < 0 && errno == EINTR); } else if (addr->ss.sa_family == AF_INET6) { const int off = 0; setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const char *) &off, sizeof(off)); do { ret = bind(fd, (const struct sockaddr *) addr, sizeof(struct sockaddr_in6)); } while (ret < 0 && errno == EINTR); } else { return -1; } if(ret<0) { if(debug) { int err = errno; perror("bind"); char str[129]; addr_to_string(addr,(u08bits*)str); TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Trying to bind fd %d to <%s>: errno=%d\n", fd, str, err); } } return ret; } } int addr_get_from_sock(evutil_socket_t fd, ioa_addr *addr) { if (fd < 0 || !addr) return -1; else { ioa_addr a; a.ss.sa_family = AF_INET6; socklen_t socklen = get_ioa_addr_len(&a); if (getsockname(fd, (struct sockaddr*) &a, &socklen) < 0) { a.ss.sa_family = AF_INET; socklen = get_ioa_addr_len(&a); if (getsockname(fd, (struct sockaddr*) &a, &socklen) < 0) { return -1; } } addr_cpy(addr, &a); return 0; } } int get_raw_socket_ttl(evutil_socket_t fd, int family) { int ttl = 0; if(family == AF_INET6) { #if !defined(IPV6_UNICAST_HOPS) UNUSED_ARG(fd); do { return TTL_IGNORE; } while(0); #else socklen_t slen = (socklen_t)sizeof(ttl); if(getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl,&slen)<0) { perror("get HOPLIMIT on socket"); return TTL_IGNORE; } #endif } else { #if !defined(IP_TTL) UNUSED_ARG(fd); do { return TTL_IGNORE; } while(0); #else socklen_t slen = (socklen_t)sizeof(ttl); if(getsockopt(fd, IPPROTO_IP, IP_TTL, &ttl,&slen)<0) { perror("get TTL on socket"); return TTL_IGNORE; } #endif } CORRECT_RAW_TTL(ttl); return ttl; } int get_raw_socket_tos(evutil_socket_t fd, int family) { int tos = 0; if(family == AF_INET6) { #if !defined(IPV6_TCLASS) UNUSED_ARG(fd); do { return TOS_IGNORE; } while(0); #else socklen_t slen = (socklen_t)sizeof(tos); if(getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tos,&slen)<0) { perror("get TCLASS on socket"); return -1; } #endif } else { #if !defined(IP_TOS) UNUSED_ARG(fd); do { return TOS_IGNORE; } while(0); #else socklen_t slen = (socklen_t)sizeof(tos); if(getsockopt(fd, IPPROTO_IP, IP_TOS, &tos,&slen)<0) { perror("get TOS on socket"); return -1; } #endif } CORRECT_RAW_TOS(tos); return tos; } int set_raw_socket_ttl(evutil_socket_t fd, int family, int ttl) { if(family == AF_INET6) { #if !defined(IPV6_UNICAST_HOPS) UNUSED_ARG(fd); UNUSED_ARG(ttl); #else CORRECT_RAW_TTL(ttl); if(setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl,sizeof(ttl))<0) { perror("set HOPLIMIT on socket"); return -1; } #endif } else { #if !defined(IP_TTL) UNUSED_ARG(fd); UNUSED_ARG(ttl); #else CORRECT_RAW_TTL(ttl); if(setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl,sizeof(ttl))<0) { perror("set TTL on socket"); return -1; } #endif } return 0; } int set_raw_socket_tos(evutil_socket_t fd, int family, int tos) { if(family == AF_INET6) { #if !defined(IPV6_TCLASS) UNUSED_ARG(fd); UNUSED_ARG(tos); #else CORRECT_RAW_TOS(tos); if(setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tos,sizeof(tos))<0) { perror("set TCLASS on socket"); return -1; } #endif } else { #if !defined(IP_TOS) UNUSED_ARG(fd); UNUSED_ARG(tos); #else if(setsockopt(fd, IPPROTO_IP, IP_TOS, &tos,sizeof(tos))<0) { perror("set TOS on socket"); return -1; } #endif } return 0; } int is_stream_socket(int st) { switch(st) { case TCP_SOCKET: case TLS_SOCKET: case TENTATIVE_TCP_SOCKET: case SCTP_SOCKET: case TLS_SCTP_SOCKET: case TENTATIVE_SCTP_SOCKET: return 1; default: ; } return 0; } int is_tcp_socket(int st) { switch(st) { case TCP_SOCKET: case TLS_SOCKET: case TENTATIVE_TCP_SOCKET: return 1; default: ; } return 0; } int is_sctp_socket(int st) { switch(st) { case SCTP_SOCKET: case TLS_SCTP_SOCKET: case TENTATIVE_SCTP_SOCKET: return 1; default: ; } return 0; } const char* socket_type_name(SOCKET_TYPE st) { switch(st) { case TCP_SOCKET: return "TCP"; case SCTP_SOCKET: return "SCTP"; case UDP_SOCKET: return "UDP"; case TLS_SOCKET: return "TLS/TCP"; case TLS_SCTP_SOCKET: return "TLS/SCTP"; case DTLS_SOCKET: return "DTLS"; case TENTATIVE_TCP_SOCKET: return "TLS/TCP ?"; case TENTATIVE_SCTP_SOCKET: return "TLS/SCTP ?"; default: ; }; return "UNKNOWN"; } /////////////////// MTU ///////////////////////////////////////// int set_socket_df(evutil_socket_t fd, int family, int value) { int ret=0; #if defined(IP_DONTFRAG) && defined(IPPROTO_IP) //BSD { const int val=value; /* kernel sets DF bit on outgoing IP packets */ if(family==AF_INET) { ret = setsockopt(fd, IPPROTO_IP, IP_DONTFRAG, &val, sizeof(val)); } else { #if defined(IPV6_DONTFRAG) && defined(IPPROTO_IPV6) ret = setsockopt(fd, IPPROTO_IPV6, IPV6_DONTFRAG, &val, sizeof(val)); #else #error CANNOT SET IPV6 SOCKET DF FLAG (1) #endif } if(ret<0) { int err=errno; perror("set socket df:"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: set sockopt failed: fd=%d, err=%d, family=%d\n",__FUNCTION__,fd,err,family); } } #elif defined(IPPROTO_IP) && defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO) && defined(IP_PMTUDISC_DONT) //LINUX { /* kernel sets DF bit on outgoing IP packets */ if(family==AF_INET) { int val=IP_PMTUDISC_DO; if(!value) val=IP_PMTUDISC_DONT; ret = setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)); } else { #if defined(IPPROTO_IPV6) && defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO) && defined(IPV6_PMTUDISC_DONT) int val=IPV6_PMTUDISC_DO; if(!value) val=IPV6_PMTUDISC_DONT; ret = setsockopt(fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val)); #else #error CANNOT SET IPV6 SOCKET DF FLAG (2) #endif } if(ret<0) { perror("set DF"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: set sockopt failed\n",__FUNCTION__); } } #else //CANNOT SET SOCKET DF FLAG (3) : UNKNOWN PLATFORM UNUSED_ARG(fd); UNUSED_ARG(family); UNUSED_ARG(value); #endif return ret; } static int get_mtu_from_ssl(SSL* ssl) { int ret = SOSO_MTU; #if DTLS_SUPPORTED if(ssl) ret = BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL); #else UNUSED_ARG(ssl); #endif return ret; } static void set_query_mtu(SSL* ssl) { if(ssl) { #if defined(SSL_OP_NO_QUERY_MTU) SSL_set_options(ssl, SSL_OP_NO_QUERY_MTU); #else ; #endif } } int decrease_mtu(SSL* ssl, int mtu, int verbose) { if (!ssl) return mtu; int new_mtu = get_mtu_from_ssl(ssl); if (new_mtu < 1) new_mtu = mtu; if (new_mtu > MAX_MTU) mtu = MAX_MTU; if (new_mtu > 0 && new_mtu < MIN_MTU) mtu = MIN_MTU; else if (new_mtu < mtu) mtu = new_mtu; else mtu -= MTU_STEP; if (mtu < MIN_MTU) mtu = MIN_MTU; set_query_mtu(ssl); if (verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "1. mtu to use: %d\n", mtu); #if DTLS_SUPPORTED SSL_set_mtu(ssl,mtu); BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_SET_MTU, mtu, NULL); #endif return mtu; } int set_mtu_df(SSL* ssl, evutil_socket_t fd, int family, int mtu, int df_value, int verbose) { if(!ssl || fd<0) return 0; int ret=set_socket_df(fd, family, df_value); if(!mtu) mtu=SOSO_MTU; else if(mtuMAX_MTU) mtu=MAX_MTU; set_query_mtu(ssl); if(verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"3. mtu to use: %d\n",mtu); #if DTLS_SUPPORTED SSL_set_mtu(ssl,mtu); BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_SET_MTU, mtu, NULL); #endif if(verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"4. new mtu: %d\n",get_mtu_from_ssl(ssl)); return ret; } int get_socket_mtu(evutil_socket_t fd, int family, int verbose) { int ret = 0; UNUSED_ARG(fd); UNUSED_ARG(family); UNUSED_ARG(verbose); #if defined(IP_MTU) int val = 0; socklen_t slen=sizeof(val); if(family==AF_INET) { ret = getsockopt(fd, IPPROTO_IP, IP_MTU, &val, &slen); } else { #if defined(IPPROTO_IPV6) && defined(IPV6_MTU) ret = getsockopt(fd, IPPROTO_IPV6, IPV6_MTU, &val, &slen); #endif ; } ret = val; #endif if (verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: final=%d\n", __FUNCTION__, ret); return ret; } //////////////////// socket error handle //////////////////// int handle_socket_error() { switch (errno) { case EINTR: /* Interrupted system call. * Just ignore. */ return 1; case ENOBUFS: /* No buffers, temporary condition. * Just ignore and try later. */ return 1; case EAGAIN: #if defined(EWOULDBLOCK) #if (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif #endif return 1; case EMSGSIZE: return 1; case EBADF: /* Invalid socket. * Must close connection. */ return 0; case EHOSTDOWN: /* Host is down. * Just ignore, might be an attacker * sending fake ICMP messages. */ return 1; case ECONNRESET: case ECONNREFUSED: /* Connection reset by peer. */ return 0; case ENOMEM: /* Out of memory. * Must close connection. */ TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Out of memory!\n"); return 0; case EACCES: /* Permission denied. * Just ignore, we might be blocked * by some firewall policy. Try again * and hope for the best. */ return 1; default: /* Something unexpected happened */ TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Unexpected error! (errno = %d)\n", errno); return 0; } } //////////////////// Misc utils ////////////////////////////// char *skip_blanks(char* s) { while(*s==' ' || *s=='\t' || *s=='\n') ++s; return s; } //////////////////// Config file search ////////////////////// #define Q(x) #x #define QUOTE(x) Q(x) #define ETCDIR INSTALL_PREFIX/etc/ #define QETCDIR QUOTE(ETCDIR) #define ETCDIR1 INSTALL_PREFIX/etc/turnserver/ #define QETCDIR1 QUOTE(ETCDIR1) #define ETCDIR2 INSTALL_PREFIX/etc/coturn/ #define QETCDIR2 QUOTE(ETCDIR2) static const char *config_file_search_dirs[] = {"./", "./turnserver/", "./coturn/", "./etc/", "./etc/turnserver/", "./etc/coturn/", "../etc/", "../etc/turnserver/", "../etc/coturn/", "/etc/", "/etc/turnserver/", "/etc/coturn/", "/usr/local/etc/", "/usr/local/etc/turnserver/", "/usr/local/etc/coturn/", QETCDIR, QETCDIR1, QETCDIR2, NULL }; static char *c_execdir=NULL; void set_execdir(void) { /* On some systems, this may give us the execution path */ char *_var = getenv("_"); if(_var && *_var) { _var = turn_strdup(_var); char *edir=_var; if(edir[0]!='.') edir = strstr(edir,"/"); if(edir && *edir) edir = dirname(edir); else edir = dirname(_var); if(c_execdir) turn_free(c_execdir,strlen(c_execdir)+1); c_execdir = turn_strdup(edir); turn_free(_var,strlen(_var)+1); } } void print_abs_file_name(const char *msg1, const char *msg2, const char *fn) { char absfn[1025]; absfn[0]=0; if(fn) { while(fn[0] && fn[0]==' ') ++fn; if(fn[0]) { if(fn[0]=='/') { STRCPY(absfn,fn); } else { if(fn[0]=='.' && fn[1]=='/') fn+=2; if(!getcwd(absfn,sizeof(absfn)-1)) absfn[0]=0; size_t blen=strlen(absfn); if(blen0)) { rlim.rlim_cur = rlim.rlim_cur>>1; } return (unsigned long)rlim.rlim_cur; } } return 0; } ////////////////////// Base 64 //////////////////////////// static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; static char *decoding_table = NULL; static size_t mod_table[] = {0, 2, 1}; char *base64_encode(const unsigned char *data, size_t input_length, size_t *output_length) { *output_length = 4 * ((input_length + 2) / 3); char *encoded_data = (char*)turn_malloc(*output_length+1); if (encoded_data == NULL) return NULL; size_t i,j; for (i = 0, j = 0; i < input_length;) { u32bits octet_a = i < input_length ? data[i++] : 0; u32bits octet_b = i < input_length ? data[i++] : 0; u32bits octet_c = i < input_length ? data[i++] : 0; u32bits triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; } for (i = 0; i < mod_table[input_length % 3]; i++) encoded_data[*output_length - 1 - i] = '='; encoded_data[*output_length]=0; return encoded_data; } void build_base64_decoding_table() { decoding_table = (char*)turn_malloc(256); ns_bzero(decoding_table,256); int i; for (i = 0; i < 64; i++) decoding_table[(unsigned char) encoding_table[i]] = (char)i; } unsigned char *base64_decode(const char *data, size_t input_length, size_t *output_length) { if (decoding_table == NULL) build_base64_decoding_table(); if (input_length % 4 != 0) return NULL; *output_length = input_length / 4 * 3; if (data[input_length - 1] == '=') (*output_length)--; if (data[input_length - 2] == '=') (*output_length)--; unsigned char *decoded_data = (unsigned char*)turn_malloc(*output_length); if (decoded_data == NULL) return NULL; int i; size_t j; for (i = 0, j = 0; i < (int)input_length;) { uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]]; uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]]; uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]]; uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]]; uint32_t triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6); if (j < *output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF; if (j < *output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF; if (j < *output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF; } return decoded_data; } ////////////////// SSL ///////////////////// static const char* turn_get_method(const SSL_METHOD *method, const char* mdefault) { { if(!method) return mdefault; else { if(method == SSLv23_server_method()) { return "SSLv23"; } else if(method == SSLv23_client_method()) { return "SSLv23"; } else if(method == TLSv1_server_method()) { return "TLSv1.0"; } else if(method == TLSv1_client_method()) { return "TLSv1.0"; #if TLSv1_1_SUPPORTED } else if(method == TLSv1_1_server_method()) { return "TLSv1.1"; } else if(method == TLSv1_1_client_method()) { return "TLSv1.1"; #if TLSv1_2_SUPPORTED } else if(method == TLSv1_2_server_method()) { return "TLSv1.2"; } else if(method == TLSv1_2_client_method()) { return "TLSv1.2"; #endif #endif #if DTLS_SUPPORTED } else if(method == DTLSv1_server_method()) { return "DTLSv1.0"; } else if(method == DTLSv1_client_method()) { return "DTLSv1.0"; #if DTLSv1_2_SUPPORTED } else if(method == DTLSv1_2_server_method()) { return "DTLSv1.2"; } else if(method == DTLSv1_2_client_method()) { return "DTLSv1.2"; #endif #endif } else { if(mdefault) return mdefault; return "UNKNOWN"; } } } } const char* turn_get_ssl_method(SSL *ssl, const char* mdefault) { const char* ret = "unknown"; if(!ssl) { ret = mdefault; } else { const SSL_METHOD *method = SSL_get_ssl_method(ssl); if(!method) { ret = mdefault; } else { ret = turn_get_method(method, mdefault); } } return ret; } //////////// EVENT BASE /////////////// struct event_base *turn_event_base_new(void) { struct event_config *cfg = event_config_new(); event_config_set_flag(cfg,EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST); return event_base_new_with_config(cfg); } /////////// OAUTH ///////////////// void convert_oauth_key_data_raw(const oauth_key_data_raw *raw, oauth_key_data *oakd) { if(raw && oakd) { ns_bzero(oakd,sizeof(oauth_key_data)); oakd->timestamp = (turn_time_t)raw->timestamp; oakd->lifetime = raw->lifetime; ns_bcopy(raw->as_rs_alg,oakd->as_rs_alg,sizeof(oakd->as_rs_alg)); ns_bcopy(raw->kid,oakd->kid,sizeof(oakd->kid)); if(raw->ikm_key[0]) { size_t ikm_key_size = 0; char *ikm_key = (char*)base64_decode(raw->ikm_key,strlen(raw->ikm_key),&ikm_key_size); if(ikm_key) { ns_bcopy(ikm_key,oakd->ikm_key,ikm_key_size); oakd->ikm_key_size = ikm_key_size; turn_free(ikm_key,ikm_key_size); } } } } ////////////////////////////////////////////////////////////// turnserver-4.5.1.1/src/apps/common/hiredis_libevent2.h0000664000175000017500000000435713436577655021417 0ustar misimisi/* * 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. */ #ifndef __HIREDIS_LIBEVENT_H__ #define __HIREDIS_LIBEVENT_H__ #include #ifdef __cplusplus extern "C" { #endif ////////////////////////////////////// #define DEFAULT_REDIS_PORT (6379) typedef void* redis_context_handle; ////////////////////////////////////// #if !defined(TURN_NO_HIREDIS) void redis_async_init(void); redis_context_handle redisLibeventAttach(struct event_base *base, char *ip, int port, char *pwd, int db); void send_message_to_redis(redis_context_handle rch, const char *command, const char *key, const char *format,...); int is_redis_asyncconn_good(redis_context_handle rch); #endif /* TURN_NO_HIREDIS */ #ifdef __cplusplus } #endif #endif /*__HIREDIS_LIBEVENT_H__*/ turnserver-4.5.1.1/src/apps/common/apputils.h0000664000175000017500000001550613436577655017655 0ustar misimisi/* * 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. */ #ifndef __APP_LIB__ #define __APP_LIB__ #include #include "ns_turn_openssl.h" #include "ns_turn_ioaddr.h" #include "ns_turn_msg_defs.h" #include "ns_turn_ioalib.h" #ifdef __cplusplus extern "C" { #endif //////////// Common defines /////////////////////////// #define PEER_DEFAULT_PORT (3480) #define DTLS_MAX_RECV_TIMEOUT (5) #define UR_CLIENT_SOCK_BUF_SIZE (65536) #define UR_SERVER_SOCK_BUF_SIZE (UR_CLIENT_SOCK_BUF_SIZE * 32) extern int IS_TURN_SERVER; /* ALPN */ #define OPENSSL_FIRST_ALPN_VERSION (0x10002003L) #if OPENSSL_VERSION_NUMBER >= OPENSSL_FIRST_ALPN_VERSION #define ALPN_SUPPORTED 1 #else #define ALPN_SUPPORTED 0 #endif /* TLS */ #if defined(TURN_NO_TLS) #define TLS_SUPPORTED 0 #define TLSv1_1_SUPPORTED 0 #define TLSv1_2_SUPPORTED 0 #else #define TLS_SUPPORTED 1 #if defined(SSL_OP_NO_TLSv1_1) #define TLSv1_1_SUPPORTED 1 #else #define TLSv1_1_SUPPORTED 0 #endif #if defined(SSL_OP_NO_TLSv1_2) #define TLSv1_2_SUPPORTED 1 #else #define TLSv1_2_SUPPORTED 0 #endif #endif #if defined(TURN_NO_DTLS) || (!defined(DTLS_CTRL_LISTEN) && (OPENSSL_VERSION_NUMBER < 0x10100000L)) #define DTLS_SUPPORTED 0 #define DTLSv1_2_SUPPORTED 0 #else #define DTLS_SUPPORTED 1 #if defined(SSL_OP_NO_DTLSv1_2) #define DTLSv1_2_SUPPORTED 1 #else #define DTLSv1_2_SUPPORTED 0 #endif #endif #if OPENSSL_VERSION_NUMBER >= OPENSSL_FIRST_ALPN_VERSION #define SSL_SESSION_ECDH_AUTO_SUPPORTED 1 #else #define SSL_SESSION_ECDH_AUTO_SUPPORTED 0 #endif /////////// SSL ////////////////////////// enum _TURN_TLS_TYPE { TURN_TLS_NO=0, TURN_TLS_SSL23, TURN_TLS_v1_0, #if TLSv1_1_SUPPORTED TURN_TLS_v1_1, #if TLSv1_2_SUPPORTED TURN_TLS_v1_2, #endif #endif TURN_TLS_TOTAL }; typedef enum _TURN_TLS_TYPE TURN_TLS_TYPE; //////////////////////////////////////////// struct _oauth_key_data_raw { char kid[OAUTH_KID_SIZE+1]; char ikm_key[OAUTH_KEY_SIZE+1]; u64bits timestamp; u32bits lifetime; char as_rs_alg[OAUTH_ALG_SIZE+1]; char realm[STUN_MAX_REALM_SIZE+1]; }; typedef struct _oauth_key_data_raw oauth_key_data_raw; ////////////////////////////////////////// #define EVENT_DEL(ev) if(ev) { event_del(ev); event_free(ev); ev=NULL; } ////////////////////////////////////////// #define ioa_socket_raw int ///////////////////////// Sockets /////////////////////////////// #if defined(WIN32) /** Do the platform-specific call needed to close a socket returned from socket() or accept(). */ #define socket_closesocket(s) closesocket(s) #else /** Do the platform-specific call needed to close a socket returned from socket() or accept(). */ #define socket_closesocket(s) close(s) #endif void read_spare_buffer(evutil_socket_t fd); int set_sock_buf_size(evutil_socket_t fd, int sz); int socket_set_reusable(evutil_socket_t fd, int reusable, SOCKET_TYPE st); int sock_bind_to_device(evutil_socket_t fd, const unsigned char* ifname); int socket_set_nonblocking(evutil_socket_t fd); int socket_tcp_set_keepalive(evutil_socket_t fd, SOCKET_TYPE st); int addr_connect(evutil_socket_t fd, const ioa_addr* addr, int *out_errno); int addr_bind(evutil_socket_t fd, const ioa_addr* addr, int reusable, int debug, SOCKET_TYPE st); int addr_get_from_sock(evutil_socket_t fd, ioa_addr *addr); int handle_socket_error(void); #define CORRECT_RAW_TTL(ttl) do { if(ttl<0 || ttl>255) ttl=TTL_DEFAULT; } while(0) #define CORRECT_RAW_TOS(tos) do { if(tos<0 || tos>255) tos=TOS_DEFAULT; } while(0) int set_raw_socket_tos(evutil_socket_t fd, int family, int tos); int set_raw_socket_ttl(evutil_socket_t fd, int family, int ttl); int get_raw_socket_tos(evutil_socket_t fd, int family); int get_raw_socket_ttl(evutil_socket_t fd, int family); /////////////////////// SYS ///////////////////// void ignore_sigpipe(void); unsigned long set_system_parameters(int max_resources); ///////////////////////// MTU ////////////////////////// #define MAX_MTU (1500 - 20 - 8) #define MIN_MTU (576 - 20 - 8) #define SOSO_MTU (1300) #define MTU_STEP (68) int set_socket_df(evutil_socket_t fd, int family, int value); int set_mtu_df(SSL* ssl, evutil_socket_t fd, int family, int mtu, int df_value, int verbose); int decrease_mtu(SSL* ssl, int mtu, int verbose); int get_socket_mtu(evutil_socket_t fd, int family, int verbose); ////////////////// Misc utils ///////////////////////// char *skip_blanks(char* s); ////////////////// File search //////////////////////// char* find_config_file(const char *config_file, int print_file_name); void set_execdir(void); void print_abs_file_name(const char *msg1, const char *msg2, const char *fn); ////////////////// Base64 ///////////////////////////// char *base64_encode(const unsigned char *data, size_t input_length, size_t *output_length); void build_base64_decoding_table(void); unsigned char *base64_decode(const char *data, size_t input_length, size_t *output_length); ///////////// SSL //////////////// const char* turn_get_ssl_method(SSL *ssl, const char* mdefault); ////////////// OAUTH UTILS //////////////// void convert_oauth_key_data_raw(const oauth_key_data_raw *raw, oauth_key_data *oakd); //////////// Event Base ///////////////////// struct event_base *turn_event_base_new(void); /////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__APP_LIB__ turnserver-4.5.1.1/src/apps/common/stun_buffer.c0000664000175000017500000002215713436577655020331 0ustar misimisi/* * 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 "stun_buffer.h" ////////////////////// BUFFERS /////////////////////////// int stun_init_buffer(stun_buffer *buf) { if(!buf) return -1; ns_bzero(buf->buf,sizeof(buf->buf)); buf->len=0; buf->offset=0; buf->coffset=0; return 0; } int stun_get_size(const stun_buffer *buf) { if(!buf) return 0; return sizeof(buf->buf); } //////////////////////////////////////////////////////////// void stun_tid_from_message(const stun_buffer *buf, stun_tid* id) { stun_tid_from_message_str(buf->buf,(size_t)(buf->len), id); } void stun_tid_generate_in_message(stun_buffer* buf, stun_tid* id) { if(buf) { stun_tid_generate_in_message_str(buf->buf, id); } } //////////////////////////////////////////////////////// static inline int is_channel_msg(const stun_buffer* buf) { if(buf && buf->len>0) { return is_channel_msg_str(buf->buf, (size_t)(buf->len)); } return 0; } int stun_is_command_message(const stun_buffer* buf) { if(!buf || buf->len<=0) return 0; else return stun_is_command_message_str(buf->buf,(size_t)(buf->len)); } int stun_is_request(const stun_buffer* buf) { return stun_is_request_str(buf->buf,(size_t)buf->len); } int stun_is_success_response(const stun_buffer* buf) { return stun_is_success_response_str(buf->buf, (size_t)(buf->len)); } int stun_is_error_response(const stun_buffer* buf, int *err_code, u08bits *err_msg, size_t err_msg_size) { return stun_is_error_response_str(buf->buf, (size_t)(buf->len), err_code, err_msg, err_msg_size); } int stun_is_response(const stun_buffer* buf) { return stun_is_response_str(buf->buf,(size_t)(buf->len)); } int stun_is_indication(const stun_buffer* buf) { if(is_channel_msg(buf)) return 0; return IS_STUN_INDICATION(stun_get_msg_type(buf)); } u16bits stun_get_method(const stun_buffer* buf) { return stun_get_method_str(buf->buf, (size_t)(buf->len)); } u16bits stun_get_msg_type(const stun_buffer* buf) { if(!buf) return (u16bits)-1; return stun_get_msg_type_str(buf->buf,(size_t)buf->len); } //////////////////////////////////////////////////////////// static void stun_init_command(u16bits message_type, stun_buffer* buf) { buf->len=stun_get_size(buf); stun_init_command_str(message_type, buf->buf, (size_t*)(&(buf->len))); } void stun_init_request(u16bits method, stun_buffer* buf) { stun_init_command(stun_make_request(method), buf); } void stun_init_indication(u16bits method, stun_buffer* buf) { stun_init_command(stun_make_indication(method), buf); } void stun_init_success_response(u16bits method, stun_buffer* buf, stun_tid* id) { buf->len=stun_get_size(buf); stun_init_success_response_str(method, buf->buf, (size_t*)(&(buf->len)), id); } void stun_init_error_response(u16bits method, stun_buffer* buf, u16bits error_code, const u08bits *reason, stun_tid* id) { buf->len=stun_get_size(buf); stun_init_error_response_str(method, buf->buf, (size_t*)(&(buf->len)), error_code, reason, id); } /////////////////////////////////////////////////////////////////////////////// int stun_get_command_message_len(const stun_buffer* buf) { return stun_get_command_message_len_str(buf->buf, (size_t)(buf->len)); } /////////////////////////////////////////////////////////////////////////////// int stun_init_channel_message(u16bits chnumber, stun_buffer* buf, int length, int do_padding) { return stun_init_channel_message_str(chnumber, buf->buf, (size_t*)(&(buf->len)), length, do_padding); } int stun_is_channel_message(stun_buffer* buf, u16bits* chnumber, int is_padding_mandatory) { if(!buf) return 0; size_t blen = (size_t)buf->len; int ret = stun_is_channel_message_str(buf->buf, &blen, chnumber, is_padding_mandatory); if(ret) { buf->len=(ssize_t)blen; } return ret; } /////////////////////////////////////////////////////////////////////////////// int stun_set_allocate_request(stun_buffer* buf, u32bits lifetime, int af4, int af6, u08bits transport, int mobile, const char *rt, int ep) { return stun_set_allocate_request_str(buf->buf, (size_t*)(&(buf->len)), lifetime, af4, af6, transport, mobile, rt, ep); } int stun_set_allocate_response(stun_buffer* buf, stun_tid* tid, const ioa_addr *relayed_addr1, const ioa_addr *relayed_addr2, const ioa_addr *reflexive_addr, u32bits lifetime, u32bits max_lifetime, int error_code, const u08bits *reason, u64bits reservation_token, char *mobile_id) { return stun_set_allocate_response_str(buf->buf, (size_t*)(&(buf->len)), tid, relayed_addr1, relayed_addr2, reflexive_addr, lifetime, max_lifetime, error_code, reason, reservation_token, mobile_id); } /////////////////////////////////////////////////////////////////////////////// u16bits stun_set_channel_bind_request(stun_buffer* buf, const ioa_addr* peer_addr, u16bits channel_number) { return stun_set_channel_bind_request_str(buf->buf,(size_t*)(&(buf->len)), peer_addr, channel_number); } void stun_set_channel_bind_response(stun_buffer* buf, stun_tid* tid, int error_code, const u08bits *reason) { stun_set_channel_bind_response_str(buf->buf, (size_t*)(&(buf->len)), tid, error_code, reason); } //////////////////////////////////////////////////////////////// stun_attr_ref stun_attr_get_first(const stun_buffer* buf) { return stun_attr_get_first_str(buf->buf, (size_t)(buf->len)); } stun_attr_ref stun_attr_get_next(const stun_buffer* buf, stun_attr_ref prev) { return stun_attr_get_next_str(buf->buf, (size_t)(buf->len), prev); } int stun_attr_add(stun_buffer* buf, u16bits attr, const s08bits* avalue, int alen) { return stun_attr_add_str(buf->buf, (size_t*)(&(buf->len)), attr, (const u08bits *)avalue, alen); } int stun_attr_add_channel_number(stun_buffer* buf, u16bits chnumber) { return stun_attr_add_channel_number_str(buf->buf, (size_t *)(&(buf->len)), chnumber); } int stun_attr_add_addr(stun_buffer *buf,u16bits attr_type, const ioa_addr* ca) { return stun_attr_add_addr_str(buf->buf,(size_t*)(&(buf->len)), attr_type, ca); } int stun_attr_get_addr(const stun_buffer *buf, stun_attr_ref attr, ioa_addr* ca, const ioa_addr *default_addr) { return stun_attr_get_addr_str(buf->buf, (size_t)(buf->len), attr, ca, default_addr); } int stun_attr_get_first_addr(const stun_buffer *buf, u16bits attr_type, ioa_addr* ca, const ioa_addr *default_addr) { return stun_attr_get_first_addr_str(buf->buf, (size_t)(buf->len), attr_type, ca, default_addr); } int stun_attr_add_even_port(stun_buffer* buf, uint8_t value) { if(value) value=0x80; return stun_attr_add(buf,STUN_ATTRIBUTE_EVEN_PORT,(const s08bits*)&value,1); } u16bits stun_attr_get_first_channel_number(const stun_buffer *buf) { return stun_attr_get_first_channel_number_str(buf->buf, (size_t)(buf->len)); } stun_attr_ref stun_attr_get_first_by_type(const stun_buffer* buf, u16bits attr_type) { return stun_attr_get_first_by_type_str(buf->buf, (size_t)(buf->len), attr_type); } /////////////////////////////////////////////////////////////////////////////// void stun_set_binding_request(stun_buffer* buf) { stun_set_binding_request_str(buf->buf, (size_t*)(&(buf->len))); } int stun_set_binding_response(stun_buffer* buf, stun_tid* tid, const ioa_addr *reflexive_addr, int error_code, const u08bits *reason) { return stun_set_binding_response_str(buf->buf, (size_t*)(&(buf->len)), tid, reflexive_addr, error_code, reason, 0,0); } void stun_prepare_binding_request(stun_buffer* buf) { stun_set_binding_request_str(buf->buf, (size_t*)(&(buf->len))); } int stun_is_binding_response(const stun_buffer* buf) { return stun_is_binding_response_str(buf->buf, (size_t)(buf->len)); } /////////////////////////////////////////////////////// turnserver-4.5.1.1/src/apps/common/ns_turn_utils.h0000664000175000017500000000556713436577655020732 0ustar misimisi/* * 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. */ #ifndef __TURN_ULIB__ #define __TURN_ULIB__ #if !defined(TURN_LOG_FUNC) //#define TURN_LOG_FUNC(level, ...) printf (__VA_ARGS__) #define TURN_LOG_FUNC turn_log_func_default #endif #include "ns_turn_ioaddr.h" #ifdef __cplusplus extern "C" { #endif //////////////////////// LOG ////////////////////////// typedef enum { TURN_LOG_LEVEL_INFO = 0, TURN_LOG_LEVEL_CONTROL, TURN_LOG_LEVEL_WARNING, TURN_LOG_LEVEL_ERROR } TURN_LOG_LEVEL; #define TURN_VERBOSE_NONE (0) #define TURN_VERBOSE_NORMAL (1) #define TURN_VERBOSE_EXTRA (2) #define eve(v) ((v)==TURN_VERBOSE_EXTRA) void set_no_stdout_log(int val); void set_log_to_syslog(int val); void set_simple_log(int val); void turn_log_func_default(TURN_LOG_LEVEL level, const s08bits* format, ...); void addr_debug_print(int verbose, const ioa_addr *addr, const s08bits* s); /* Log */ extern volatile int _log_time_value_set; extern volatile turn_time_t _log_time_value; void rtpprintf(const char *format, ...); int vrtpprintf(TURN_LOG_LEVEL level, const char *format, va_list args); void reset_rtpprintf(void); void set_logfile(const char *fn); void rollover_logfile(void); /////////////////////////////////////////////////////// int is_secure_string(const u08bits *string, int sanitizesql); /////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_ULIB__ turnserver-4.5.1.1/src/apps/common/hiredis_libevent2.c0000664000175000017500000002235713436577655021412 0ustar misimisi/* * 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 #include #if !defined(TURN_NO_HIREDIS) #include "hiredis_libevent2.h" #include "ns_turn_utils.h" #include #include #include #include //////////////// Libevent context /////////////////////// struct redisLibeventEvents { redisAsyncContext *context; int invalid; int allocated; struct event_base *base; struct event *rev, *wev; int rev_set, wev_set; char *ip; int port; char *pwd; int db; }; ///////////// Messages //////////////////////////// struct redis_message { char format[513]; char arg[513]; }; /////////////////// forward declarations /////////////// static void redis_reconnect(struct redisLibeventEvents *e); ////////////////////////////////////////////////////////// static int redis_le_valid(struct redisLibeventEvents *e) { return (e && !(e->invalid) && (e->context)); } /////////////////// Callbacks //////////////////////////// static void redisLibeventReadEvent(int fd, short event, void *arg) { ((void)fd); ((void)event); struct redisLibeventEvents *e = (struct redisLibeventEvents*)arg; if(redis_le_valid(e)) { { char buf[8]; int len = 0; do { len = recv(fd,buf,sizeof(buf),MSG_PEEK); } while((len<0)&&(errno == EINTR)); if(len<1) { e->invalid = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Redis connection broken: e=0x%lx\n", __FUNCTION__, ((unsigned long)e)); } } if(redis_le_valid(e)) { redisAsyncHandleRead(e->context); } } else { redis_reconnect(e); } } static void redisLibeventWriteEvent(int fd, short event, void *arg) { ((void)fd); ((void)event); struct redisLibeventEvents *e = (struct redisLibeventEvents*)arg; if(redis_le_valid(e)) { redisAsyncHandleWrite(e->context); } } static void redisLibeventAddRead(void *privdata) { struct redisLibeventEvents *e = (struct redisLibeventEvents*)privdata; if(e && (e->rev) && !(e->rev_set)) { event_add(e->rev,NULL); e->rev_set = 1; } } static void redisLibeventDelRead(void *privdata) { struct redisLibeventEvents *e = (struct redisLibeventEvents*)privdata; if(e && e->rev && e->rev_set) { event_del(e->rev); e->rev_set = 0; } } static void redisLibeventAddWrite(void *privdata) { struct redisLibeventEvents *e = (struct redisLibeventEvents*)privdata; if(e && (e->wev) && !(e->wev_set)) { event_add(e->wev,NULL); e->wev_set = 1; } } static void redisLibeventDelWrite(void *privdata) { struct redisLibeventEvents *e = (struct redisLibeventEvents*)privdata; if(e && e->wev && e->wev_set) { event_del(e->wev); e->wev_set = 0; } } static void redisLibeventCleanup(void *privdata) { if (privdata) { struct redisLibeventEvents *e = (struct redisLibeventEvents *) privdata; if (e->allocated) { if (e->rev) { if(e->rev_set) event_del(e->rev); event_free(e->rev); e->rev = NULL; } e->rev_set = 0; if (e->wev) { if(e->wev_set) event_del(e->wev); event_free(e->wev); e->wev = NULL; } e->wev_set = 0; e->context = NULL; } } } ///////////////////////// Send-receive /////////////////////////// void redis_async_init(void) { ; } int is_redis_asyncconn_good(redis_context_handle rch) { if(rch) { struct redisLibeventEvents *e = (struct redisLibeventEvents*)rch; if(redis_le_valid(e)) return 1; } return 0; } void send_message_to_redis(redis_context_handle rch, const char *command, const char *key, const char *format,...) { if(!rch) { return; } else { struct redisLibeventEvents *e = (struct redisLibeventEvents*)rch; if(!redis_le_valid(e)) { redis_reconnect(e); } if(!redis_le_valid(e)) { ; } else { redisAsyncContext *ac=e->context; struct redis_message rm; snprintf(rm.format,sizeof(rm.format)-3,"%s %s ", command, key); strcpy(rm.format+strlen(rm.format),"%s"); va_list args; va_start (args, format); vsnprintf(rm.arg, sizeof(rm.arg)-1, format, args); va_end (args); if((redisAsyncCommand(ac, NULL, e, rm.format, rm.arg)!=REDIS_OK)) { e->invalid = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Redis connection broken: ac=0x%lx, e=0x%lx\n", __FUNCTION__,(unsigned long)ac,(unsigned long)e); } } } } ///////////////////////// Attach ///////////////////////////////// redis_context_handle redisLibeventAttach(struct event_base *base, char *ip0, int port0, char *pwd, int db) { struct redisLibeventEvents *e = NULL; redisAsyncContext *ac = NULL; char ip[256]; if(ip0 && ip0[0]) STRCPY(ip,ip0); else STRCPY(ip,"127.0.0.1"); int port = DEFAULT_REDIS_PORT; if(port0>0) port=port0; ac = redisAsyncConnect(ip, port); if (!ac) { fprintf(stderr,"Error: %s:%s\n", ac->errstr, ac->c.errstr); return NULL; } /* Create container for context and r/w events */ e = (struct redisLibeventEvents*)turn_malloc(sizeof(struct redisLibeventEvents)); ns_bzero(e,sizeof(struct redisLibeventEvents)); e->allocated = 1; e->context = ac; e->base = base; e->ip = turn_strdup(ip); e->port = port; if(pwd) e->pwd = turn_strdup(pwd); e->db = db; /* Register functions to start/stop listening for events */ ac->ev.addRead = redisLibeventAddRead; ac->ev.delRead = redisLibeventDelRead; ac->ev.addWrite = redisLibeventAddWrite; ac->ev.delWrite = redisLibeventDelWrite; ac->ev.cleanup = redisLibeventCleanup; ac->ev.data = e; /* Initialize and install read/write events */ e->rev = event_new(e->base,e->context->c.fd, EV_READ|EV_PERSIST,redisLibeventReadEvent, e); e->wev = event_new(e->base,e->context->c.fd, EV_WRITE,redisLibeventWriteEvent, e); if (e->rev == NULL || e->wev == NULL) { turn_free(e, sizeof(struct redisLibeventEvents)); return NULL; } event_add(e->wev, NULL); e->wev_set = 1; //Authentication if(redis_le_valid(e) && pwd) { if(redisAsyncCommand(ac, NULL, e, "AUTH %s", pwd)!=REDIS_OK) { e->invalid = 1; } } if(redis_le_valid(e)) { if(redisAsyncCommand(ac, NULL, e, "SELECT %d", db)!=REDIS_OK) { e->invalid = 1; } } return (redis_context_handle)e; } static void redis_reconnect(struct redisLibeventEvents *e) { if(!e || !(e->allocated)) return; if (e->rev) { if(e->rev_set) event_del(e->rev); event_free(e->rev); e->rev = NULL; } e->rev_set = 0; if (e->wev) { if(e->wev_set) event_del(e->wev); event_free(e->wev); e->wev = NULL; } e->wev_set = 0; redisAsyncContext *ac = NULL; if(e->context) { e->context = NULL; } ac = redisAsyncConnect(e->ip, e->port); if(!ac) { return; } e->context = ac; /* Register functions to start/stop listening for events */ ac->ev.addRead = redisLibeventAddRead; ac->ev.delRead = redisLibeventDelRead; ac->ev.addWrite = redisLibeventAddWrite; ac->ev.delWrite = redisLibeventDelWrite; ac->ev.cleanup = redisLibeventCleanup; ac->ev.data = e; /* Initialize and install read/write events */ e->rev = event_new(e->base,e->context->c.fd, EV_READ,redisLibeventReadEvent, e); e->wev = event_new(e->base,e->context->c.fd, EV_WRITE,redisLibeventWriteEvent, e); if (e->rev == NULL || e->wev == NULL) { return; } event_add(e->wev, NULL); e->wev_set = 1; e->invalid = 0; //Authentication if(redis_le_valid(e) && e->pwd) { if(redisAsyncCommand(ac, NULL, e, "AUTH %s", e->pwd)!=REDIS_OK) { e->invalid = 1; } } if(redis_le_valid(e)) { if(redisAsyncCommand(ac, NULL, e, "SELECT %d", e->db)!=REDIS_OK) { e->invalid = 1; } } if(redis_le_valid(e)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: Re-connected to redis, async\n", __FUNCTION__); } } ///////////////////////////////////////////////////////// #endif /* TURN_NO_HIREDIS */ turnserver-4.5.1.1/src/apps/natdiscovery/0000775000175000017500000000000013436577655017056 5ustar misimisiturnserver-4.5.1.1/src/apps/natdiscovery/natdiscovery.c0000664000175000017500000006072513436577655021746 0ustar misimisi/* * 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 #include #include #include #include #include #include "ns_turn_utils.h" #include "apputils.h" #include "stun_buffer.h" #ifdef __cplusplus #include "TurnMsgLib.h" #endif //////////////////////////////////////////////////// static int udp_fd = -1; static int udp_fd2 = -1; static int counter = 0; #ifdef __cplusplus static int init_socket(int *socketfd, ioa_addr *local_addr, int local_port, ioa_addr *remote_addr){ int ret=0; if (local_port >= 0) { addr_set_port(local_addr, local_port); } *socketfd = socket(remote_addr->ss.sa_family, SOCK_DGRAM, 0); if (udp_fd < 0) err(-1, NULL); if (!addr_any(local_addr)) { if (addr_bind(*socketfd, local_addr,0,1,UDP_SOCKET) < 0) err(-1, NULL); } return ret; } static int stunclient_send(int sockfd, ioa_addr *local_addr, int *local_port, ioa_addr *remote_addr, int change_ip, int change_port, int padding, int response_port){ int ret=0; turn::StunMsgRequest req(STUN_METHOD_BINDING); req.constructBindingRequest(); if (response_port >= 0) { turn::StunAttrResponsePort rpa; rpa.setResponsePort((u16bits)response_port); try { req.addAttr(rpa); } catch(turn::WrongStunAttrFormatException &ex1) { printf("Wrong rp attr format\n"); exit(-1); } catch(turn::WrongStunBufferFormatException &ex2) { printf("Wrong stun buffer format (1)\n"); exit(-1); } catch(...) { printf("Wrong something (1)\n"); exit(-1); } } if (change_ip || change_port) { turn::StunAttrChangeRequest cra; cra.setChangeIp(change_ip); cra.setChangePort(change_port); try { req.addAttr(cra); } catch(turn::WrongStunAttrFormatException &ex1) { printf("Wrong cr attr format\n"); exit(-1); } catch(turn::WrongStunBufferFormatException &ex2) { printf("Wrong stun buffer format (2)\n"); exit(-1); } catch(...) { printf("Wrong something (2)\n"); exit(-1); } } if (padding) { turn::StunAttrPadding pa; pa.setPadding(1500); try { req.addAttr(pa); } catch(turn::WrongStunAttrFormatException &ex1) { printf("Wrong p attr format\n"); exit(-1); } catch(turn::WrongStunBufferFormatException &ex2) { printf("Wrong stun buffer format (3)\n"); exit(-1); } catch(...) { printf("Wrong something (3)\n"); exit(-1); } } { int len = 0; int slen = get_ioa_addr_len(remote_addr); do { len = sendto(sockfd, req.getRawBuffer(), req.getSize(), 0, (struct sockaddr*) remote_addr, (socklen_t) slen); } while (len < 0 && ((errno == EINTR) || (errno == ENOBUFS) || (errno == EAGAIN))); if (len < 0) err(-1, NULL); } if (addr_get_from_sock(sockfd, local_addr) < 0) { printf("%s: Cannot get address from local socket\n", __FUNCTION__); } else { *local_port = addr_get_port(local_addr); } return ret; } static int stunclient_receive(int sockfd, ioa_addr *local_addr, ioa_addr *reflexive_addr, ioa_addr *other_addr, int *rfc5780){ int ret=0; { int len = 0; stun_buffer buf; u08bits *ptr = buf.buf; int recvd = 0; const int to_recv = sizeof(buf.buf); struct timeval tv; tv.tv_sec = 3; /* 3 Secs Timeout */ tv.tv_usec = 0; // Not init'ing this can cause strange errors setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval)); do { len = recv(sockfd, ptr, to_recv - recvd, 0); if (len > 0) { recvd += len; ptr += len; break; } } while (len < 0 && (errno == EINTR)); if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { printf("STUN receive timeout..\n"); ret = 1; return ret; } if (recvd > 0) len = recvd; buf.len = len; try { turn::StunMsgResponse res(buf.buf, sizeof(buf.buf), (size_t)buf.len, true); if (res.isCommand()) { if(res.isSuccess()) { if (res.isBindingResponse()) { turn::StunAttrIterator iter(res,STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS); if (!iter.eof()) { turn::StunAttrAddr addr(iter); addr.getAddr(*reflexive_addr); turn::StunAttrIterator iter1(res,STUN_ATTRIBUTE_OTHER_ADDRESS); if (!iter1.eof()) { *rfc5780 = 1; printf("\n========================================\n"); printf("RFC 5780 response %d\n",++counter); turn::StunAttrIterator iter2(res,STUN_ATTRIBUTE_MAPPED_ADDRESS); if (!iter2.eof()) { ioa_addr mapped_addr; addr_set_any(&mapped_addr); turn::StunAttrAddr addr2(iter2); addr2.getAddr(mapped_addr); if (!addr_eq(&mapped_addr,reflexive_addr)){ printf("-= ALG detected! Mapped and XOR-Mapped differ! =-\n"); addr_debug_print(1, &mapped_addr, "Mapped Address: "); } else { printf("No ALG: Mapped == XOR-Mapped\n"); } } else { printf("Not received mapped address attribute!\n"); } turn::StunAttrAddr addr1(iter1); addr1.getAddr(*other_addr); turn::StunAttrIterator iter3(res,STUN_ATTRIBUTE_RESPONSE_ORIGIN); if (!iter3.eof()) { ioa_addr response_origin; turn::StunAttrAddr addr3(iter3); addr3.getAddr(response_origin); addr_debug_print(1, &response_origin, "Response origin: "); } addr_debug_print(1, other_addr, "Other addr: "); } addr_debug_print(1, reflexive_addr, "UDP reflexive addr"); addr_debug_print(1, local_addr, "Local addr: "); } else { printf("Cannot read the response\n"); } } else { printf("Wrong type of response\n"); } } else { int err_code = res.getError(); std::string reason = res.getReason(); printf("The response is an error %d (%s)\n", err_code, reason.c_str()); } } else { printf("The response is not a response message\n"); } } catch(...) { turn::StunMsgRequest msg(buf.buf, sizeof(buf.buf), (size_t)buf.len, true); if (msg.isRequest(buf.buf,(size_t)buf.len)) { printf("Received a request (maybe a successful hairpinning)\n"); return ret; } else { printf("The response is not a well formed STUN message\n"); } ret=1; } } return ret; } static int run_stunclient(ioa_addr *local_addr, ioa_addr *remote_addr, ioa_addr *reflexive_addr, ioa_addr *other_addr, int *local_port, int *rfc5780, int change_ip, int change_port, int padding){ int ret=0; ret=init_socket(&udp_fd, local_addr, *local_port, remote_addr); ret=stunclient_send(udp_fd, local_addr, local_port, remote_addr, change_ip, change_port, padding, -1); ret=stunclient_receive(udp_fd, local_addr, reflexive_addr, other_addr, rfc5780); close(udp_fd); return ret; } static int run_stunclient_hairpinning(ioa_addr *local_addr, ioa_addr *remote_addr, ioa_addr *reflexive_addr, ioa_addr *other_addr, int *local_port, int *rfc5780, int change_ip, int change_port, int padding){ int ret=0; init_socket(&udp_fd,local_addr,*local_port,remote_addr); ret=stunclient_send(udp_fd, local_addr, local_port, remote_addr, change_ip, change_port, padding, -1); ret=stunclient_receive(udp_fd, local_addr, reflexive_addr, other_addr, rfc5780); addr_cpy(remote_addr,reflexive_addr); addr_set_port(local_addr, 0); init_socket(&udp_fd2,local_addr,0,remote_addr); ret=stunclient_send(udp_fd2, local_addr, local_port, remote_addr, change_ip, change_port, padding, -1); ret=stunclient_receive(udp_fd, local_addr, reflexive_addr, other_addr, rfc5780); if(ret){ ret=stunclient_receive(udp_fd2, local_addr, reflexive_addr, other_addr, rfc5780); } close(udp_fd); close(udp_fd2); return ret; } static int run_stunclient_lifetime(int timer,ioa_addr *local_addr, ioa_addr *remote_addr, ioa_addr *reflexive_addr, ioa_addr *other_addr, int *local_port, int *rfc5780, int change_ip, int change_port, int padding){ int ret=0; int response_port; init_socket(&udp_fd, local_addr, *local_port, remote_addr); ret=stunclient_send(udp_fd, local_addr, local_port, remote_addr, change_ip, change_port, padding, -1); ret=stunclient_receive(udp_fd, local_addr, reflexive_addr, other_addr, rfc5780); addr_set_port(local_addr, 0); sleep(timer); init_socket(&udp_fd2,local_addr,0,remote_addr); response_port=addr_get_port(reflexive_addr); ret=stunclient_send(udp_fd2, local_addr, local_port, remote_addr, change_ip, change_port, padding, response_port); ret=stunclient_receive(udp_fd, local_addr, reflexive_addr, other_addr, rfc5780); socket_closesocket(udp_fd); socket_closesocket(udp_fd2); return ret; } #else static int init_socket(int *socketfd, ioa_addr *local_addr, int local_port, ioa_addr *remote_addr){ int ret=0; *socketfd = socket(remote_addr->ss.sa_family, CLIENT_DGRAM_SOCKET_TYPE, CLIENT_DGRAM_SOCKET_PROTOCOL); if (udp_fd < 0) err(-1, NULL); if (local_port >= 0) { addr_set_port(local_addr, local_port); } if (!addr_any(local_addr)) { if (addr_bind(*socketfd, local_addr,0,1,UDP_SOCKET) < 0) { err(-1, NULL); } } return ret; } static int stunclient_send(stun_buffer *buf, int sockfd, ioa_addr *local_addr, int *local_port, ioa_addr *remote_addr, int change_ip, int change_port, int padding, int response_port){ int ret=0; stun_prepare_binding_request(buf); if (response_port >= 0) { stun_attr_add_response_port_str((u08bits*) (buf->buf), (size_t*) &(buf->len), (u16bits) response_port); } if (change_ip || change_port) { stun_attr_add_change_request_str((u08bits*) buf->buf, (size_t*) &(buf->len), change_ip, change_port); } if (padding) { if(stun_attr_add_padding_str((u08bits*) buf->buf, (size_t*) &(buf->len), 1500)<0) { printf("%s: ERROR: Cannot add padding\n",__FUNCTION__); } } { int len = 0; int slen = get_ioa_addr_len(remote_addr); do { len = sendto(sockfd, buf->buf, buf->len, 0, (struct sockaddr*) remote_addr, (socklen_t) slen); } while (len < 0 && ((errno == EINTR) || (errno == ENOBUFS) || (errno == EAGAIN))); if (len < 0) err(-1, NULL); } if (addr_get_from_sock(sockfd, local_addr) < 0) { printf("%s: Cannot get address from local socket\n", __FUNCTION__); } else { *local_port = addr_get_port(local_addr); } return ret; } static int stunclient_receive(stun_buffer *buf, int sockfd, ioa_addr *local_addr, ioa_addr *reflexive_addr, ioa_addr *other_addr, int *rfc5780){ int ret=0; { int len = 0; u08bits *ptr = buf->buf; int recvd = 0; const int to_recv = sizeof(buf->buf); struct timeval tv; tv.tv_sec = 3; /* 3 Secs Timeout */ tv.tv_usec = 0; // Not init'ing this can cause strange errors setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval)); do { len = recv(sockfd, ptr, to_recv - recvd, 0); if (len > 0) { recvd += len; ptr += len; break; } } while (len < 0 && (errno == EINTR)); if (recvd > 0) len = recvd; buf->len = len; if (stun_is_command_message(buf)) { if (stun_is_response(buf)) { if (stun_is_success_response(buf)) { if (stun_is_binding_response(buf)) { addr_set_any(reflexive_addr); if (stun_attr_get_first_addr(buf, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, reflexive_addr, NULL) >= 0) { stun_attr_ref sar = stun_attr_get_first_by_type_str(buf->buf, buf->len, STUN_ATTRIBUTE_OTHER_ADDRESS); if (sar) { *rfc5780 = 1; printf("\n========================================\n"); printf("RFC 5780 response %d\n",++counter); ioa_addr mapped_addr; addr_set_any(&mapped_addr); if (stun_attr_get_first_addr(buf, STUN_ATTRIBUTE_MAPPED_ADDRESS, &mapped_addr, NULL) >= 0) { if (!addr_eq(&mapped_addr,reflexive_addr)){ printf("-= ALG detected! Mapped and XOR-Mapped differ! =-\n"); addr_debug_print(1, &mapped_addr, "Mapped Address: "); }else { printf("No ALG: Mapped == XOR-Mapped\n"); } } else { printf("Not received mapped address attribute.\n"); } stun_attr_get_addr_str((u08bits *) buf->buf, (size_t) buf->len, sar, other_addr, NULL); sar = stun_attr_get_first_by_type_str(buf->buf, buf->len, STUN_ATTRIBUTE_RESPONSE_ORIGIN); if (sar) { ioa_addr response_origin; stun_attr_get_addr_str((u08bits *) buf->buf, (size_t) buf->len, sar, &response_origin, NULL); addr_debug_print(1, &response_origin, "Response origin: "); } addr_debug_print(1, other_addr, "Other addr: "); } addr_debug_print(1, reflexive_addr, "UDP reflexive addr"); addr_debug_print(1, local_addr, "Local addr: "); } else { printf("Cannot read the response\n"); } } else { printf("Wrong type of response\n"); } } else { int err_code = 0; u08bits err_msg[1025] = "\0"; size_t err_msg_size = sizeof(err_msg); if (stun_is_error_response(buf, &err_code, err_msg, err_msg_size)) { printf("The response is an error %d (%s)\n", err_code, (char*) err_msg); } else { printf("The response is an unrecognized error\n"); } } } else if (stun_is_request(buf)) { printf("Received a request (maybe a successful hairpinning)\n"); } else { printf("The response is not a response message\n"); ret=1; } } else { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { printf("STUN receive timeout..\n"); }else{ printf("The response is not a STUN message\n"); } ret=1; } } return ret; } static int run_stunclient(ioa_addr *local_addr, ioa_addr *remote_addr, ioa_addr *reflexive_addr, ioa_addr *other_addr, int *local_port, int *rfc5780, int change_ip, int change_port, int padding){ int ret=0; stun_buffer buf; init_socket(&udp_fd, local_addr, *local_port, remote_addr); ret=stunclient_send(&buf, udp_fd, local_addr, local_port, remote_addr, change_ip, change_port, padding, -1); ret=stunclient_receive(&buf, udp_fd, local_addr, reflexive_addr, other_addr, rfc5780); socket_closesocket(udp_fd); return ret; } static int run_stunclient_hairpinning(ioa_addr *local_addr, ioa_addr *remote_addr, ioa_addr *reflexive_addr, ioa_addr *other_addr, int *local_port, int *rfc5780, int change_ip, int change_port, int padding){ int ret=0; stun_buffer buf; stun_buffer buf2; init_socket(&udp_fd, local_addr, *local_port, remote_addr); ret=stunclient_send(&buf, udp_fd, local_addr, local_port, remote_addr, change_ip, change_port, padding, -1); ret=stunclient_receive(&buf, udp_fd, local_addr, reflexive_addr, other_addr, rfc5780); addr_cpy(remote_addr,reflexive_addr); addr_set_port(local_addr, 0); init_socket(&udp_fd2,local_addr,0,remote_addr); ret=stunclient_send(&buf2, udp_fd2, local_addr, local_port, remote_addr, change_ip, change_port, padding, -1); ret=stunclient_receive(&buf, udp_fd, local_addr, reflexive_addr, other_addr, rfc5780); if(ret){ ret=stunclient_receive(&buf2, udp_fd2, local_addr, reflexive_addr, other_addr, rfc5780); } socket_closesocket(udp_fd); socket_closesocket(udp_fd2); return ret; } static int run_stunclient_lifetime(int timer,ioa_addr *local_addr, ioa_addr *remote_addr, ioa_addr *reflexive_addr, ioa_addr *other_addr, int *local_port, int *rfc5780, int change_ip, int change_port, int padding){ int ret=0; stun_buffer buf; stun_buffer buf2; int response_port; init_socket(&udp_fd, local_addr, *local_port, remote_addr); ret=stunclient_send(&buf, udp_fd, local_addr, local_port, remote_addr, change_ip, change_port, padding, -1); ret=stunclient_receive(&buf, udp_fd, local_addr, reflexive_addr, other_addr, rfc5780); addr_set_port(local_addr, 0); sleep(timer); init_socket(&udp_fd2,local_addr,0,remote_addr); response_port=addr_get_port(reflexive_addr); ret=stunclient_send(&buf2, udp_fd2, local_addr, local_port, remote_addr, change_ip, change_port, padding, response_port); ret=stunclient_receive(&buf, udp_fd, local_addr, reflexive_addr, other_addr, rfc5780); socket_closesocket(udp_fd); socket_closesocket(udp_fd2); return ret; } #endif //////////////// local definitions ///////////////// static char Usage[] = "Usage: natdiscovery [options] address\n" "Options:\n" " -m NAT mapping behavior discovery\n" " -f NAT filtering behavior discovery\n" " -t NAT mapping lifetime behavior discovery\n" " Requires a timer (-T)\n" " -c NAT collision behavior discovery\n" " Requires an alternative IP address (-A)\n" " -H NAT hairpinning behavior discovery\n" " -P Add 1500 byte Padding to the behavior discovery\n" " Applicable with all except NAT mapping Lifetime discovery\n" " -p STUN server port (Default: 3478)\n" " -L Local address to use (optional)\n" " -l Local port to use (use with -L)\n" " -A Local alrernative address to use\n" " Used by collision behavior discovery\n" " -T Mapping lifetime timer (sec)\n" " Used by mapping lifetime behavior discovery\n"; ////////////////////////////////////////////////// static void init(int first, ioa_addr *local_addr, ioa_addr *remote_addr, int *local_port, int port, int *rfc5780, char* local_addr_string, char* remote_param) { addr_set_any(local_addr); if(local_addr_string[0]) { if(make_ioa_addr((const u08bits*)local_addr_string, 0, local_addr)<0) { err(-1,NULL); } } if (!first) *local_port=-1; *rfc5780 = 0; if (make_ioa_addr((const u08bits*)remote_param, port, remote_addr) < 0) err(-1, NULL); } static void discoveryresult(const char *decision){ printf("\n========================================\n"); printf("%s",decision); printf("\n========================================\n"); } int main(int argc, char **argv) { int remote_port = DEFAULT_STUN_PORT; char local_addr_string[256]="\0"; char local2_addr_string[256]="\0"; int c=0; int mapping = 0; int filtering = 0; int lifetime=0; int timer=-1; int collision = 0; int padding = 0; int hairpinning = 0; int local_port=-1; int rfc5780; int first=1; ioa_addr other_addr, reflexive_addr, tmp_addr, remote_addr, local_addr, local2_addr; set_logfile("stdout"); set_system_parameters(0); ns_bzero(local_addr_string, sizeof(local_addr_string)); ns_bzero(local2_addr_string, sizeof(local2_addr_string)); addr_set_any(&remote_addr); addr_set_any(&other_addr); addr_set_any(&reflexive_addr); addr_set_any(&tmp_addr); while ((c = getopt(argc, argv, "mftcPHp:L:l:A:T:")) != -1) { switch(c) { case 'm': mapping=1; break; case 'f': filtering=1; break; case 't': lifetime=1; break; case 'c': collision=1; break; case 'H': hairpinning=1; break; case 'P': padding=1; break; case 'p': remote_port = atoi(optarg); break; case 'L': STRCPY(local_addr_string, optarg); break; case 'l': local_port = atoi(optarg); break; case 'A': STRCPY(local2_addr_string, optarg); break; case 'T': timer = atoi(optarg); break; default: fprintf(stderr,"%s\n", Usage); exit(1); } } if(optind>=argc) { fprintf(stderr, "%s\n", Usage); exit(-1); } if(collision && !strcmp(local2_addr_string,"\0")){ fprintf(stderr, "Use \"-A\" to add an Alternative local IP address.\n"); fprintf(stderr, "It is mandatory with \"-c\" collision behavior detection..\n"); exit(-1); } if(lifetime && timer==-1){ fprintf(stderr, "Use \"-T\" to add a timer value (in sec).\n"); fprintf(stderr, "It is mandatory with \"-b\" mapping lifetime behavior detection..\n"); exit(-1); } if(local_port>=0 && !strcmp(local_addr_string,"\0")){ fprintf(stderr, "To use local port please specify local address \"-L\"!\n"); fprintf(stderr, "We need to know the address familly to set the port.\n"); exit(-1); } if(lifetime) { printf("\n-= Mapping Lifetime Behavior Discovery =-\n"); init(first, &local_addr, &remote_addr, &local_port, remote_port, &rfc5780, local_addr_string, argv[optind]); first=0; run_stunclient_lifetime(timer, &local_addr, &remote_addr, &reflexive_addr, &other_addr, &local_port, &rfc5780,0,0,padding); } if(hairpinning) { printf("\n-= Hairpinning Behavior Discovery =-\n"); init(first, &local_addr, &remote_addr, &local_port, remote_port, &rfc5780, local_addr_string, argv[optind]); first=0; run_stunclient_hairpinning(&local_addr, &remote_addr, &reflexive_addr, &other_addr, &local_port, &rfc5780,0,0,padding); } if(mapping) { printf("\n-= Mapping Behavior Discovery =-\n"); init(first,&local_addr, &remote_addr, &local_port, remote_port, &rfc5780, local_addr_string, argv[optind]); first=0; run_stunclient(&local_addr, &remote_addr, &reflexive_addr, &other_addr, &local_port, &rfc5780,0,0,padding); if (addr_eq(&local_addr,&reflexive_addr)){ discoveryresult("No NAT! (Endpoint Independent Mapping)"); } if(rfc5780) { if(!addr_any(&other_addr)){ addr_cpy(&tmp_addr, &reflexive_addr); addr_cpy(&remote_addr, &other_addr); addr_set_port(&remote_addr, remote_port); run_stunclient(&local_addr, &remote_addr, &reflexive_addr, &other_addr, &local_port, &rfc5780,0,0,padding); if(addr_eq(&tmp_addr,&reflexive_addr)){ discoveryresult("NAT with Endpoint Independent Mapping!"); } else { addr_cpy(&tmp_addr, &reflexive_addr); addr_cpy(&remote_addr, &other_addr); run_stunclient(&local_addr, &remote_addr, &reflexive_addr, &other_addr, &local_port, &rfc5780,0,0,padding); if(addr_eq(&tmp_addr,&reflexive_addr)){ discoveryresult("NAT with Address Dependent Mapping!"); } else { discoveryresult("NAT with Address and Port Dependent Mapping!"); } } } } } if(filtering) { printf("\n-= Filtering Behavior Discovery =-\n"); init(first,&local_addr, &remote_addr, &local_port, remote_port, &rfc5780, local_addr_string, argv[optind]); first=0; run_stunclient(&local_addr, &remote_addr, &reflexive_addr, &other_addr, &local_port, &rfc5780,0,0,padding); if(addr_eq(&local_addr, &reflexive_addr)){ discoveryresult("No NAT! (Endpoint Independent Mapping)"); } if(rfc5780) { if(!addr_any(&other_addr)){ int res=0; res=run_stunclient(&local_addr, &remote_addr, &reflexive_addr, &other_addr, &local_port, &rfc5780,1,1,padding); if (!res) { discoveryresult("NAT with Endpoint Independent Filtering!"); } else { res=0; res=run_stunclient(&local_addr, &remote_addr, &reflexive_addr, &other_addr, &local_port, &rfc5780,0,1,padding); if(!res){ discoveryresult("NAT with Address Dependent Filtering!"); } else { discoveryresult("NAT with Address and Port Dependent Filtering!"); } } } } } if(collision) { printf("\n-= Collision Behavior Discovery =-\n"); init(first,&local_addr, &remote_addr, &local_port, remote_port, &rfc5780, local_addr_string, argv[optind]); first=0; addr_set_any(&local2_addr); if(local2_addr_string[0]) { if(make_ioa_addr((const u08bits*)local2_addr_string, 0, &local2_addr)<0) { err(-1,NULL); } } run_stunclient(&local_addr, &remote_addr, &reflexive_addr, &other_addr, &local_port, &rfc5780,0,0,padding); addr_set_port(&local2_addr,addr_get_port(&local_addr)); run_stunclient(&local2_addr, &remote_addr, &reflexive_addr, &other_addr, &local_port, &rfc5780,0,0,padding); } if (!filtering && !mapping && !collision && !hairpinning && !lifetime) { printf("Please use either -f or -m or -c or -t or -H parameter for Filtering or Mapping behavior discovery.\n"); } socket_closesocket(udp_fd); socket_closesocket(udp_fd2); return 0; } turnserver-4.5.1.1/src/apps/peer/0000775000175000017500000000000013436577655015277 5ustar misimisiturnserver-4.5.1.1/src/apps/peer/udpserver.c0000664000175000017500000001171713436577655017471 0ustar misimisi/* * 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 "apputils.h" #include "udpserver.h" #include "stun_buffer.h" /////////////// io handlers /////////////////// static void udp_server_input_handler(evutil_socket_t fd, short what, void* arg) { if(!(what&EV_READ)) return; ioa_addr *addr = (ioa_addr*)arg; int len = 0; int slen = get_ioa_addr_len(addr); stun_buffer buffer; ioa_addr remote_addr; do { len = recvfrom(fd, buffer.buf, sizeof(buffer.buf)-1, 0, (struct sockaddr*) &remote_addr, (socklen_t*) &slen); } while(len<0 && (errno==EINTR)); buffer.len=len; if(len>=0) { do { len = sendto(fd, buffer.buf, buffer.len, 0, (const struct sockaddr*) &remote_addr, (socklen_t) slen); } while (len < 0 && ((errno == EINTR) || (errno == ENOBUFS) || (errno == EAGAIN))); } } ///////////////////// operations ////////////////////////// static int udp_create_server_socket(server_type* server, const char* ifname, const char *local_address, int port) { FUNCSTART; if(!server) return -1; evutil_socket_t udp_fd = -1; ioa_addr *server_addr = (ioa_addr*)turn_malloc(sizeof(ioa_addr)); STRCPY(server->ifname,ifname); if(make_ioa_addr((const u08bits*)local_address, port, server_addr)<0) return -1; udp_fd = socket(server_addr->ss.sa_family, RELAY_DGRAM_SOCKET_TYPE, RELAY_DGRAM_SOCKET_PROTOCOL); if (udp_fd < 0) { perror("socket"); return -1; } if(sock_bind_to_device(udp_fd, (unsigned char*)server->ifname)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Cannot bind udp server socket to device %s\n",server->ifname); } set_sock_buf_size(udp_fd,UR_SERVER_SOCK_BUF_SIZE); if(addr_bind(udp_fd,server_addr,1,1,UDP_SOCKET)<0) return -1; socket_set_nonblocking(udp_fd); struct event *udp_ev = event_new(server->event_base,udp_fd,EV_READ|EV_PERSIST, udp_server_input_handler,server_addr); event_add(udp_ev,NULL); FUNCEND; return 0; } static server_type* init_server(int verbose, const char* ifname, char **local_addresses, size_t las, int port) { server_type* server=(server_type*)turn_malloc(sizeof(server_type)); if(!server) return server; ns_bzero(server,sizeof(server_type)); server->verbose=verbose; server->event_base = turn_event_base_new(); while(las) { udp_create_server_socket(server, ifname, local_addresses[--las], port); udp_create_server_socket(server, ifname, local_addresses[las], port+1); } return server; } static int clean_server(server_type* server) { if(server) { if(server->event_base) event_base_free(server->event_base); turn_free(server,sizeof(server_type)); } return 0; } /////////////////////////////////////////////////////////// static void run_events(server_type* server) { if(!server) return; struct timeval timeout; timeout.tv_sec=0; timeout.tv_usec=100000; event_base_loopexit(server->event_base, &timeout); event_base_dispatch(server->event_base); } ///////////////////////////////////////////////////////////// server_type* start_udp_server(int verbose, const char* ifname, char **local_addresses, size_t las, int port) { return init_server(verbose, ifname, local_addresses, las, port); } void run_udp_server(server_type* server) { if(server) { unsigned int cycle=0; while (1) { cycle++; run_events(server); } } } void clean_udp_server(server_type* server) { if(server) clean_server(server); } ////////////////////////////////////////////////////////////////// turnserver-4.5.1.1/src/apps/peer/mainudpserver.c0000664000175000017500000000616313436577655020335 0ustar misimisi/* * 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 "udpserver.h" #include "apputils.h" #include #include #include #include #include //////////////// local definitions ///////////////// static char Usage[] = "Usage: server [options]\n" "Options:\n" " -p Listening UDP port (Default: 3480)\n" " -d Listening interface device (optional)\n" " -L Listening address\n" " -v verbose\n"; ////////////////////////////////////////////////// int main(int argc, char **argv) { int port = PEER_DEFAULT_PORT; char **local_addr_list=NULL; size_t las = 0; int verbose = TURN_VERBOSE_NONE; int c; char ifname[1025] = "\0"; IS_TURN_SERVER = 1; set_logfile("stdout"); set_system_parameters(0); while ((c = getopt(argc, argv, "d:p:L:v")) != -1) switch (c){ case 'd': STRCPY(ifname, optarg); break; case 'p': port = atoi(optarg); break; case 'L': local_addr_list = (char**)realloc(local_addr_list,++las*sizeof(char*)); local_addr_list[las-1]=strdup(optarg); break; case 'v': verbose = TURN_VERBOSE_NORMAL; break; default: fprintf(stderr, "%s\n", Usage); exit(1); } if(las<1) { local_addr_list = (char**)realloc(local_addr_list,++las*sizeof(char*)); local_addr_list[las-1]=strdup("0.0.0.0"); local_addr_list = (char**)realloc(local_addr_list,++las*sizeof(char*)); local_addr_list[las-1]=strdup("::"); } server_type* server = start_udp_server(verbose, ifname, local_addr_list, las, port); run_udp_server(server); clean_udp_server(server); return 0; } turnserver-4.5.1.1/src/apps/peer/udpserver.h0000664000175000017500000000510113436577655017464 0ustar misimisi/* * 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. */ #ifndef __UDP_SERVER__ #define __UDP_SERVER__ ////////////////////////////// #include "ns_turn_utils.h" #include #ifdef __cplusplus extern "C" { #endif ////////////////////////////// struct server_info; typedef struct server_info server_type; /////////////////////////////////////////////////// #define FUNCSTART if(server && server->verbose) turn_log_func_default(TURN_LOG_LEVEL_INFO,"%s:%d:start\n",__FUNCTION__,__LINE__) #define FUNCEND if(server && server->verbose) turn_log_func_default(TURN_LOG_LEVEL_INFO,"%s:%d:end\n",__FUNCTION__,__LINE__) /////////////////////////////////////////////////////// struct server_info { char ifname[1025]; struct event_base* event_base; int verbose; }; ////////////////////////////// server_type* start_udp_server(int verbose, const char* ifname, char **local_addresses, size_t las, int port); void run_udp_server(server_type* server); void clean_udp_server(server_type* server); /////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__UDP_SERVER__ turnserver-4.5.1.1/src/apps/uclient/0000775000175000017500000000000013436577655016007 5ustar misimisiturnserver-4.5.1.1/src/apps/uclient/mainuclient.c0000664000175000017500000003570213436577655020472 0ustar misimisi/* * 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 "apputils.h" #include "uclient.h" #include "ns_turn_utils.h" #include "apputils.h" #include "session.h" #include "stun_buffer.h" #include #include #include #include #include #include #include #include "ns_turn_openssl.h" /////////////// extern definitions ///////////////////// int clmessage_length=100; int do_not_use_channel=0; int c2c=0; int clnet_verbose=TURN_VERBOSE_NONE; int use_tcp=0; int use_sctp=0; int use_secure=0; int hang_on=0; ioa_addr peer_addr; int no_rtcp = 0; int default_address_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT; int dont_fragment = 0; u08bits g_uname[STUN_MAX_USERNAME_SIZE+1]; password_t g_upwd; char g_auth_secret[1025]="\0"; int g_use_auth_secret_with_timestamp = 0; int use_fingerprints = 1; static char ca_cert_file[1025]=""; static char cipher_suite[1025]=""; char cert_file[1025]=""; char pkey_file[1025]=""; SSL_CTX *root_tls_ctx[32]; int root_tls_ctx_num = 0; u08bits relay_transport = STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE; unsigned char client_ifname[1025] = ""; int passive_tcp = 0; int mandatory_channel_padding = 0; int negative_test = 0; int negative_protocol_test = 0; int dos = 0; int random_disconnect = 0; SHATYPE shatype = SHATYPE_DEFAULT; int mobility = 0; int no_permissions = 0; int extra_requests = 0; char origin[STUN_MAX_ORIGIN_SIZE+1] = "\0"; band_limit_t bps = 0; int dual_allocation = 0; int oauth = 0; oauth_key okey_array[3]; static oauth_key_data_raw okdr_array[3] = { {"north","MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEK",0,0,"A256GCM","crinna.org"}, {"union","MTIzNDU2Nzg5MDEyMzQ1Ngo=",0,0,"A128GCM","north.gov"}, {"oldempire","MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIK",0,0,"A256GCM",""} }; //////////////// local definitions ///////////////// static char Usage[] = "Usage: uclient [flags] [options] turn-server-ip-address\n" "Flags:\n" " -t TCP (default - UDP).\n" " -b SCTP (default - UDP).\n" " -T TCP relay transport (default - UDP). Implies options -t, -y, -c, and ignores \n" " options -s, -e, -r and -g. Can be used together with -b\n" " -P Passive TCP (RFC6062 with active peer). Implies -T.\n" " -S Secure connection: TLS for TCP, DTLS for UDP.\n" " -U Secure connection with eNULL cipher.\n" " -v Verbose.\n" " -s Use send method.\n" " -y Use client-to-client connections.\n" " -h Hang on indefinitely after the last sent packet.\n" " -c No rtcp connections.\n" " -x IPv6 relay address requested.\n" " -X IPv4 relay address explicitly requested.\n" " -g Include DONT_FRAGMENT option.\n" " -D Mandatory channel padding (like in pjnath).\n" " -N Negative tests (some limited cases only).\n" " -R Negative protocol tests.\n" " -O DOS attack mode (quick connect and exit).\n" " -M ICE Mobility engaged.\n" " -I Do not set permissions on TURN relay endpoints\n" " (for testing the non-standard server relay functionality).\n" " -G Generate extra requests (create permissions, channel bind).\n" " -B Random disconnect after a few initial packets.\n" " -Z Dual allocation (implies -c).\n" " -J Use oAuth with default test keys kid='north', 'union' or 'oldempire'.\n" "Options:\n" " -l Message length (Default: 100 Bytes).\n" " -i Certificate file (for secure connections only, optional).\n" " -k Private key file (for secure connections only).\n" " -E CA file for server certificate verification, \n" " if the server certificate to be verified.\n" " -p TURN server port (Default: 3478 unsecure, 5349 secure).\n" " -n Number of messages to send (Default: 5).\n" " -d Local interface device (optional).\n" " -L Local address.\n" " -m Number of clients (default is 1).\n" " -e Peer address.\n" " -r Peer port (default 3480).\n" " -z Per-session packet interval in milliseconds (default is 20 ms).\n" " -u STUN/TURN user name.\n" " -w STUN/TURN user password.\n" " -W TURN REST API \"plain text\" secret.\n" " -C TURN REST API timestamp/username separator symbol (character). The default value is ':'.\n" " -F Cipher suite for TLS/DTLS. Default value is DEFAULT.\n" " -o - the ORIGIN STUN attribute value.\n" " -a Bandwidth for the bandwidth request in ALLOCATE. The default value is zero.\n"; ////////////////////////////////////////////////// int main(int argc, char **argv) { int port = 0; int messagenumber = 5; char local_addr[256]; int c; int mclient = 1; char peer_address[129] = "\0"; int peer_port = PEER_DEFAULT_PORT; char rest_api_separator = ':'; int use_null_cipher=0; set_logfile("stdout"); set_execdir(); set_system_parameters(0); ns_bzero(local_addr, sizeof(local_addr)); while ((c = getopt(argc, argv, "a:d:p:l:n:L:m:e:r:u:w:i:k:z:W:C:E:F:o:bZvsyhcxXgtTSAPDNOUMRIGBJ")) != -1) { switch (c){ case 'J': { oauth = 1; oauth_key_data okd_array[3]; convert_oauth_key_data_raw(&okdr_array[0], &okd_array[0]); convert_oauth_key_data_raw(&okdr_array[1], &okd_array[1]); convert_oauth_key_data_raw(&okdr_array[2], &okd_array[2]); char err_msg[1025] = "\0"; size_t err_msg_size = sizeof(err_msg) - 1; if (convert_oauth_key_data(&okd_array[0], &okey_array[0], err_msg, err_msg_size) < 0) { fprintf(stderr, "%s\n", err_msg); exit(-1); } if (convert_oauth_key_data(&okd_array[1], &okey_array[1], err_msg, err_msg_size) < 0) { fprintf(stderr, "%s\n", err_msg); exit(-1); } if (convert_oauth_key_data(&okd_array[2], &okey_array[2], err_msg, err_msg_size) < 0) { fprintf(stderr, "%s\n", err_msg); exit(-1); } } break; case 'a': bps = (band_limit_t)strtoul(optarg,NULL,10); break; case 'o': STRCPY(origin,optarg); break; case 'B': random_disconnect = 1; break; case 'G': extra_requests = 1; break; case 'F': STRCPY(cipher_suite,optarg); break; case 'I': no_permissions = 1; break; case 'M': mobility = 1; break; case 'E': { char* fn = find_config_file(optarg,1); if(!fn) { fprintf(stderr,"ERROR: file %s not found\n",optarg); exit(-1); } STRCPY(ca_cert_file,fn); } break; case 'O': dos = 1; break; case 'C': rest_api_separator=*optarg; break; case 'D': mandatory_channel_padding = 1; break; case 'N': negative_test = 1; break; case 'R': negative_protocol_test = 1; break; case 'z': RTP_PACKET_INTERVAL = atoi(optarg); break; case 'Z': dual_allocation = 1; break; case 'u': STRCPY(g_uname, optarg); break; case 'w': STRCPY(g_upwd, optarg); break; case 'g': dont_fragment = 1; break; case 'd': STRCPY(client_ifname, optarg); break; case 'x': default_address_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6; break; case 'X': default_address_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4; break; case 'l': clmessage_length = atoi(optarg); break; case 's': do_not_use_channel = 1; break; case 'n': messagenumber = atoi(optarg); break; case 'p': port = atoi(optarg); break; case 'L': STRCPY(local_addr, optarg); break; case 'e': STRCPY(peer_address, optarg); break; case 'r': peer_port = atoi(optarg); break; case 'v': clnet_verbose = TURN_VERBOSE_NORMAL; break; case 'h': hang_on = 1; break; case 'c': no_rtcp = 1; break; case 'm': mclient = atoi(optarg); break; case 'y': c2c = 1; break; case 't': use_tcp = 1; break; case 'b': use_sctp = 1; use_tcp = 1; break; case 'P': passive_tcp = 1; /* implies 'T': */ /* no break */ /* Falls through. */ case 'T': relay_transport = STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE; break; case 'U': use_null_cipher = 1; /* implies 'S' */ /* no break */ /* Falls through. */ case 'S': use_secure = 1; break; case 'W': g_use_auth_secret_with_timestamp = 1; STRCPY(g_auth_secret,optarg); break; case 'i': { char* fn = find_config_file(optarg,1); if(!fn) { fprintf(stderr,"ERROR: file %s not found\n",optarg); exit(-1); } STRCPY(cert_file,fn); free(fn); } break; case 'k': { char* fn = find_config_file(optarg,1); if(!fn) { fprintf(stderr,"ERROR: file %s not found\n",optarg); exit(-1); } STRCPY(pkey_file,fn); free(fn); } break; default: fprintf(stderr, "%s\n", Usage); exit(1); } } if(dual_allocation) { no_rtcp = 1; } if(g_use_auth_secret_with_timestamp) { { char new_uname[1025]; const unsigned long exp_time = 3600 * 24; /* one day */ if(g_uname[0]) { snprintf(new_uname,sizeof(new_uname),"%lu%c%s",(unsigned long)time(NULL) + exp_time,rest_api_separator, (char*)g_uname); } else { snprintf(new_uname,sizeof(new_uname),"%lu", (unsigned long)time(NULL) + exp_time); } STRCPY(g_uname,new_uname); } { u08bits hmac[MAXSHASIZE]; unsigned int hmac_len; switch(shatype) { case SHATYPE_SHA256: hmac_len = SHA256SIZEBYTES; break; case SHATYPE_SHA384: hmac_len = SHA384SIZEBYTES; break; case SHATYPE_SHA512: hmac_len = SHA512SIZEBYTES; break; default: hmac_len = SHA1SIZEBYTES; }; hmac[0]=0; if(stun_calculate_hmac(g_uname, strlen((char*)g_uname), (u08bits*)g_auth_secret, strlen(g_auth_secret), hmac, &hmac_len, shatype)>=0) { size_t pwd_length = 0; char *pwd = base64_encode(hmac,hmac_len,&pwd_length); if(pwd) { if(pwd_length>0) { ns_bcopy(pwd,g_upwd,pwd_length); g_upwd[pwd_length]=0; } } free(pwd); } } } if(is_TCP_relay()) { dont_fragment = 0; no_rtcp = 1; c2c = 1; use_tcp = 1; do_not_use_channel = 1; } if(port == 0) { if(use_secure) port = DEFAULT_STUN_TLS_PORT; else port = DEFAULT_STUN_PORT; } if (clmessage_length < (int) sizeof(message_info)) clmessage_length = (int) sizeof(message_info); const int max_header = 100; if(clmessage_length > (int)(STUN_BUFFER_SIZE-max_header)) { fprintf(stderr,"Message length was corrected to %d\n",(STUN_BUFFER_SIZE-max_header)); clmessage_length = (int)(STUN_BUFFER_SIZE-max_header); } if (optind >= argc) { fprintf(stderr, "%s\n", Usage); exit(-1); } if (!c2c) { if (make_ioa_addr((const u08bits*) peer_address, peer_port, &peer_addr) < 0) { return -1; } if(peer_addr.ss.sa_family == AF_INET6) { default_address_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6; } else if(peer_addr.ss.sa_family == AF_INET) { default_address_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4; } } /* SSL Init ==>> */ if(use_secure) { SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); const char *csuite = "ALL"; //"AES256-SHA" "DH" if(use_null_cipher) csuite = "eNULL"; else if(cipher_suite[0]) csuite=cipher_suite; if(use_tcp) { root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(SSLv23_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(TLSv1_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; #if TLSv1_1_SUPPORTED root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(TLSv1_1_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; #if TLSv1_2_SUPPORTED root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(TLSv1_2_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; #endif #endif } else { #if !DTLS_SUPPORTED fprintf(stderr,"ERROR: DTLS is not supported.\n"); exit(-1); #else if(OPENSSL_VERSION_NUMBER < 0x10000000L) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: OpenSSL version is rather old, DTLS may not be working correctly.\n"); } root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(DTLSv1_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; #if DTLSv1_2_SUPPORTED root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(DTLSv1_2_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; #endif #endif } int sslind = 0; for(sslind = 0; sslind #include #include "ns_turn_openssl.h" #include static int verbose_packets=0; static size_t current_clients_number = 0; static int start_full_timer=0; static u32bits tot_messages=0; static u32bits tot_send_messages=0; static u64bits tot_send_bytes = 0; static u32bits tot_recv_messages=0; static u64bits tot_recv_bytes = 0; static u64bits tot_send_dropped = 0; struct event_base* client_event_base=NULL; static int client_write(app_ur_session *elem); static int client_shutdown(app_ur_session *elem); static u64bits current_time = 0; static u64bits current_mstime = 0; static char buffer_to_send[65536]="\0"; static int total_clients = 0; /* Patch for unlimited number of clients provided by ucudbm@gmail.com */ static app_ur_session** elems = NULL; #define SLEEP_INTERVAL (234) #define MAX_LISTENING_CYCLE_NUMBER (7) int RTP_PACKET_INTERVAL = 20; static inline s64bits time_minus(u64bits t1, u64bits t2) { return ( (s64bits)t1 - (s64bits)t2 ); } static u64bits total_loss = 0; static u64bits total_jitter = 0; static u64bits total_latency = 0; static u64bits min_latency = 0xFFFFFFFF; static u64bits max_latency = 0; static u64bits min_jitter = 0xFFFFFFFF; static u64bits max_jitter = 0; static int show_statistics = 0; /////////////////////////////////////////////////////////////////////////////// static void __turn_getMSTime(void) { static u64bits start_sec = 0; struct timespec tp={0,0}; #if defined(CLOCK_REALTIME) clock_gettime(CLOCK_REALTIME, &tp); #else tp.tv_sec = time(NULL); #endif if(!start_sec) start_sec = tp.tv_sec; if(current_time != (u64bits)((u64bits)(tp.tv_sec)-start_sec)) show_statistics = 1; current_time = (u64bits)((u64bits)(tp.tv_sec)-start_sec); current_mstime = (u64bits)((current_time * 1000) + (tp.tv_nsec/1000000)); } //////////////////////////////////////////////////////////////////// static int refresh_channel(app_ur_session* elem, u16bits method, uint32_t lt); //////////////////////// SS //////////////////////////////////////// static app_ur_session* init_app_session(app_ur_session *ss) { if(ss) { ns_bzero(ss,sizeof(app_ur_session)); ss->pinfo.fd=-1; } return ss; } static app_ur_session* create_new_ss(void) { ++current_clients_number; return init_app_session((app_ur_session*) turn_malloc(sizeof(app_ur_session))); } static void uc_delete_session_elem_data(app_ur_session* cdi) { if(cdi) { EVENT_DEL(cdi->input_ev); EVENT_DEL(cdi->input_tcp_data_ev); if(cdi->pinfo.tcp_conn) { int i = 0; for(i=0;i<(int)(cdi->pinfo.tcp_conn_number);++i) { if(cdi->pinfo.tcp_conn[i]) { if(cdi->pinfo.tcp_conn[i]->tcp_data_ssl && !(cdi->pinfo.broken)) { if(!(SSL_get_shutdown(cdi->pinfo.tcp_conn[i]->tcp_data_ssl) & SSL_SENT_SHUTDOWN)) { SSL_set_shutdown(cdi->pinfo.tcp_conn[i]->tcp_data_ssl, SSL_RECEIVED_SHUTDOWN); SSL_shutdown(cdi->pinfo.tcp_conn[i]->tcp_data_ssl); } if(cdi->pinfo.tcp_conn[i]->tcp_data_ssl) { SSL_FREE(cdi->pinfo.tcp_conn[i]->tcp_data_ssl); } if(cdi->pinfo.tcp_conn[i]->tcp_data_fd>=0) { socket_closesocket(cdi->pinfo.tcp_conn[i]->tcp_data_fd); cdi->pinfo.tcp_conn[i]->tcp_data_fd=-1; } turn_free(cdi->pinfo.tcp_conn[i], 111); cdi->pinfo.tcp_conn[i]=NULL; } } } cdi->pinfo.tcp_conn_number=0; if(cdi->pinfo.tcp_conn) { turn_free(cdi->pinfo.tcp_conn, 111); cdi->pinfo.tcp_conn=NULL; } } if(cdi->pinfo.ssl && !(cdi->pinfo.broken)) { if(!(SSL_get_shutdown(cdi->pinfo.ssl) & SSL_SENT_SHUTDOWN)) { SSL_set_shutdown(cdi->pinfo.ssl, SSL_RECEIVED_SHUTDOWN); SSL_shutdown(cdi->pinfo.ssl); } } if(cdi->pinfo.ssl) { SSL_FREE(cdi->pinfo.ssl); } if(cdi->pinfo.fd>=0) { socket_closesocket(cdi->pinfo.fd); } cdi->pinfo.fd=-1; } } static int remove_all_from_ss(app_ur_session* ss) { if (ss) { uc_delete_session_elem_data(ss); --current_clients_number; } return 0; } /////////////////////////////////////////////////////////////////////////////// int send_buffer(app_ur_conn_info *clnet_info, stun_buffer* message, int data_connection, app_tcp_conn_info *atc) { int rc = 0; int ret = -1; char *buffer = (char*) (message->buf); if(negative_protocol_test && (message->len>0)) { if(random()%10 == 0) { int np = (int)((unsigned long)random()%10); while(np-->0) { int pos = (int)((unsigned long)random()%(unsigned long)message->len); int val = (int)((unsigned long)random()%256); message->buf[pos]=(u08bits)val; } } } SSL *ssl = clnet_info->ssl; ioa_socket_raw fd = clnet_info->fd; if(data_connection) { if(atc) { ssl = atc->tcp_data_ssl; fd = atc->tcp_data_fd; } else if(is_TCP_relay()){ TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "trying to send tcp data buffer over unready connection: size=%d\n",(int)(message->len)); return -1; } } if (ssl) { int message_sent = 0; while (!message_sent) { if (SSL_get_shutdown(ssl)) { return -1; } int len = 0; do { len = SSL_write(ssl, buffer, (int)message->len); } while (len < 0 && ((errno == EINTR) || (errno == ENOBUFS))); if(len == (int)message->len) { if (clnet_verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "buffer sent: size=%d\n",len); } message_sent = 1; ret = len; } else { switch (SSL_get_error(ssl, len)){ case SSL_ERROR_NONE: /* Try again ? */ break; case SSL_ERROR_WANT_WRITE: /* Just try again later */ break; case SSL_ERROR_WANT_READ: /* continue with reading */ break; case SSL_ERROR_ZERO_RETURN: /* Try again */ break; case SSL_ERROR_SYSCALL: TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Socket write error 111.666: \n"); if (handle_socket_error()) break; /* Falls through. */ case SSL_ERROR_SSL: { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SSL write error: \n"); char buf[1024]; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s (%d)\n", ERR_error_string(ERR_get_error(),buf), SSL_get_error(ssl, len)); } /* Falls through. */ default: clnet_info->broken = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Unexpected error while writing!\n"); return -1; } } } } else if (fd >= 0) { size_t left = (size_t) (message->len); while (left > 0) { do { rc = send(fd, buffer, left, 0); } while (rc <= 0 && ((errno == EINTR) || (errno == ENOBUFS) || (errno == EAGAIN))); if (rc > 0) { left -= (size_t) rc; buffer += rc; } else { tot_send_dropped+=1; break; } } if (left > 0) return -1; ret = (int) message->len; } return ret; } static int wait_fd(int fd, unsigned int cycle) { if(fd>=(int)FD_SETSIZE) { return 1; } else { fd_set fds; FD_ZERO(&fds); FD_SET(fd,&fds); if(dos && cycle==0) return 0; struct timeval start_time; struct timeval ctime; gettimeofday(&start_time,NULL); ctime.tv_sec = start_time.tv_sec; ctime.tv_usec = start_time.tv_usec; int rc = 0; do { struct timeval timeout = {0,0}; if(cycle == 0) { timeout.tv_usec = 500000; } else { timeout.tv_sec = 1; while(--cycle) timeout.tv_sec = timeout.tv_sec + timeout.tv_sec; if(ctime.tv_sec > start_time.tv_sec) { if(ctime.tv_sec >= start_time.tv_sec + timeout.tv_sec) { break; } else { timeout.tv_sec -= (ctime.tv_sec - start_time.tv_sec); } } } rc = select(fd+1,&fds,NULL,NULL,&timeout); if((rc<0) && (errno == EINTR)) { gettimeofday(&ctime,NULL); } else { break; } } while(1); return rc; } } int recv_buffer(app_ur_conn_info *clnet_info, stun_buffer* message, int sync, int data_connection, app_tcp_conn_info *atc, stun_buffer* request_message) { int rc = 0; stun_tid tid; u16bits method = 0; if(request_message) { stun_tid_from_message(request_message, &tid); method = stun_get_method(request_message); } ioa_socket_raw fd = clnet_info->fd; if (atc) fd = atc->tcp_data_fd; SSL* ssl = clnet_info->ssl; if (atc) ssl = atc->tcp_data_ssl; recv_again: if(!use_tcp && sync && request_message && (fd>=0)) { unsigned int cycle = 0; while(cycle < MAX_LISTENING_CYCLE_NUMBER) { int serc = wait_fd(fd,cycle); if(serc>0) break; if(serc<0) { return -1; } if(send_buffer(clnet_info, request_message, data_connection, atc)<=0) return -1; ++cycle; } } if (!use_secure && !use_tcp && fd >= 0) { /* Plain UDP */ do { rc = recv(fd, message->buf, sizeof(message->buf) - 1, 0); if (rc < 0 && errno == EAGAIN && sync) errno = EINTR; } while (rc < 0 && (errno == EINTR)); if (rc < 0) { return -1; } message->len = rc; } else if (use_secure && !use_tcp && ssl && !(clnet_info->broken)) { /* DTLS */ int message_received = 0; int cycle = 0; while (!message_received && cycle++ < 100) { if (SSL_get_shutdown(ssl)) return -1; rc = 0; do { rc = SSL_read(ssl, message->buf, sizeof(message->buf) - 1); if (rc < 0 && errno == EAGAIN && sync) continue; } while (rc < 0 && (errno == EINTR)); if (rc > 0) { if (clnet_verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "response received: size=%d\n", rc); } message->len = rc; message_received = 1; } else { int sslerr = SSL_get_error(ssl, rc); switch (sslerr) { case SSL_ERROR_NONE: /* Try again ? */ break; case SSL_ERROR_WANT_WRITE: /* Just try again later */ break; case SSL_ERROR_WANT_READ: /* continue with reading */ break; case SSL_ERROR_ZERO_RETURN: /* Try again */ break; case SSL_ERROR_SYSCALL: TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Socket read error 111.999: \n"); if (handle_socket_error()) break; /* Falls through. */ case SSL_ERROR_SSL: { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SSL write error: \n"); char buf[1024]; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s (%d)\n", ERR_error_string(ERR_get_error(), buf), SSL_get_error(ssl, rc)); } /* Falls through. */ default: clnet_info->broken = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Unexpected error while reading: rc=%d, sslerr=%d\n", rc, sslerr); return -1; } if (!sync) break; } } } else if (use_secure && use_tcp && ssl && !(clnet_info->broken)) { /* TLS*/ int message_received = 0; int cycle = 0; while (!message_received && cycle++ < 100) { if (SSL_get_shutdown(ssl)) return -1; rc = 0; do { rc = SSL_read(ssl, message->buf, sizeof(message->buf) - 1); if (rc < 0 && errno == EAGAIN && sync) continue; } while (rc < 0 && (errno == EINTR)); if (rc > 0) { if (clnet_verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "response received: size=%d\n", rc); } message->len = rc; message_received = 1; } else { int sslerr = SSL_get_error(ssl, rc); switch (sslerr) { case SSL_ERROR_NONE: /* Try again ? */ break; case SSL_ERROR_WANT_WRITE: /* Just try again later */ break; case SSL_ERROR_WANT_READ: /* continue with reading */ break; case SSL_ERROR_ZERO_RETURN: /* Try again */ break; case SSL_ERROR_SYSCALL: TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Socket read error 111.999: \n"); if (handle_socket_error()) break; /* Falls through. */ case SSL_ERROR_SSL: { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SSL write error: \n"); char buf[1024]; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s (%d)\n", ERR_error_string(ERR_get_error(), buf), SSL_get_error(ssl, rc)); } /* Falls through. */ default: clnet_info->broken = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Unexpected error while reading: rc=%d, sslerr=%d\n", rc, sslerr); return -1; } if (!sync) break; } } } else if (!use_secure && use_tcp && fd >= 0) { /* Plain TCP */ do { rc = recv(fd, message->buf, sizeof(message->buf) - 1, MSG_PEEK); if ((rc < 0) && (errno == EAGAIN) && sync) { errno = EINTR; } } while (rc < 0 && (errno == EINTR)); if (rc > 0) { int mlen = rc; size_t app_msg_len = (size_t) rc; if (!atc) { mlen = stun_get_message_len_str(message->buf, rc, 1, &app_msg_len); } else { if (!sync) mlen = clmessage_length; if (mlen > clmessage_length) mlen = clmessage_length; app_msg_len = (size_t) mlen; } if (mlen > 0) { int rcr = 0; int rsf = 0; int cycle = 0; while (rsf < mlen && cycle++ < 128) { do { rcr = recv(fd, message->buf + rsf, (size_t) mlen - (size_t) rsf, 0); if (rcr < 0 && errno == EAGAIN && sync) errno = EINTR; } while (rcr < 0 && (errno == EINTR)); if (rcr > 0) rsf += rcr; } if (rsf < 1) return -1; if (rsf < (int) app_msg_len) { if ((size_t) (app_msg_len / (size_t) rsf) * ((size_t) (rsf)) != app_msg_len) { return -1; } } message->len = app_msg_len; rc = app_msg_len; } else { rc = 0; } } } if(rc>0) { if(request_message) { stun_tid recv_tid; u16bits recv_method = 0; stun_tid_from_message(message, &recv_tid); recv_method = stun_get_method(message); if(method != recv_method) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Received wrong response method: 0x%x, expected 0x%x; trying again...\n",(unsigned int)recv_method,(unsigned int)method); goto recv_again; } if(memcmp(tid.tsx_id,recv_tid.tsx_id,STUN_TID_SIZE)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Received wrong response tid; trying again...\n"); goto recv_again; } } } return rc; } static int client_read(app_ur_session *elem, int is_tcp_data, app_tcp_conn_info *atc) { if (!elem) return -1; if (elem->state != UR_STATE_READY) return -1; elem->ctime = current_time; app_ur_conn_info *clnet_info = &(elem->pinfo); int err_code = 0; u08bits err_msg[129]; int rc = 0; int applen = 0; if (clnet_verbose && verbose_packets) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "before read ...\n"); } rc = recv_buffer(clnet_info, &(elem->in_buffer), 0, is_tcp_data, atc, NULL); if (clnet_verbose && verbose_packets) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "read %d bytes\n", (int) rc); } if (rc > 0) { elem->in_buffer.len = rc; uint16_t chnumber = 0; message_info mi; int miset=0; size_t buffers = 1; if(is_tcp_data) { if ((int)elem->in_buffer.len == clmessage_length) { ns_bcopy((elem->in_buffer.buf), &mi, sizeof(message_info)); miset=1; } } else if (stun_is_indication(&(elem->in_buffer))) { uint16_t method = stun_get_method(&elem->in_buffer); if((method == STUN_METHOD_CONNECTION_ATTEMPT)&& is_TCP_relay()) { stun_attr_ref sar = stun_attr_get_first(&(elem->in_buffer)); u32bits cid = 0; while(sar) { int attr_type = stun_attr_get_type(sar); if(attr_type == STUN_ATTRIBUTE_CONNECTION_ID) { cid = *((const u32bits*)stun_attr_get_value(sar)); break; } sar = stun_attr_get_next_str(elem->in_buffer.buf,elem->in_buffer.len,sar); } if(negative_test) { tcp_data_connect(elem,(u64bits)random()); } else { /* positive test */ tcp_data_connect(elem,cid); } return rc; } else if (method != STUN_METHOD_DATA) { TURN_LOG_FUNC( TURN_LOG_LEVEL_INFO, "ERROR: received indication message has wrong method: 0x%x\n", (int) method); return rc; } else { stun_attr_ref sar = stun_attr_get_first_by_type(&(elem->in_buffer), STUN_ATTRIBUTE_DATA); if (!sar) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "ERROR: received DATA message has no data, size=%d\n", rc); return rc; } int rlen = stun_attr_get_len(sar); applen = rlen; if (rlen != clmessage_length) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "ERROR: received DATA message has wrong len: %d, must be %d\n", rlen, clmessage_length); tot_recv_bytes += applen; return rc; } const u08bits* data = stun_attr_get_value(sar); ns_bcopy(data, &mi, sizeof(message_info)); miset=1; } } else if (stun_is_success_response(&(elem->in_buffer))) { if(elem->pinfo.nonce[0]) { if(check_integrity(&(elem->pinfo), &(elem->in_buffer))<0) return -1; } if(is_TCP_relay() && (stun_get_method(&(elem->in_buffer)) == STUN_METHOD_CONNECT)) { stun_attr_ref sar = stun_attr_get_first(&(elem->in_buffer)); u32bits cid = 0; while(sar) { int attr_type = stun_attr_get_type(sar); if(attr_type == STUN_ATTRIBUTE_CONNECTION_ID) { cid = *((const u32bits*)stun_attr_get_value(sar)); break; } sar = stun_attr_get_next_str(elem->in_buffer.buf,elem->in_buffer.len,sar); } tcp_data_connect(elem,cid); } return rc; } else if (stun_is_challenge_response_str(elem->in_buffer.buf, (size_t)elem->in_buffer.len, &err_code,err_msg,sizeof(err_msg), clnet_info->realm,clnet_info->nonce, clnet_info->server_name, &(clnet_info->oauth))) { if(is_TCP_relay() && (stun_get_method(&(elem->in_buffer)) == STUN_METHOD_CONNECT)) { turn_tcp_connect(clnet_verbose, &(elem->pinfo), &(elem->pinfo.peer_addr)); } else if(stun_get_method(&(elem->in_buffer)) == STUN_METHOD_REFRESH) { refresh_channel(elem, stun_get_method(&elem->in_buffer),600); } return rc; } else if (stun_is_error_response(&(elem->in_buffer), NULL,NULL,0)) { return rc; } else if (stun_is_channel_message(&(elem->in_buffer), &chnumber, use_tcp)) { if (elem->chnum != chnumber) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "ERROR: received message has wrong channel: %d\n", (int) chnumber); return rc; } if (elem->in_buffer.len >= 4) { if (((int)(elem->in_buffer.len-4) < clmessage_length) || ((int)(elem->in_buffer.len-4) > clmessage_length + 3)) { TURN_LOG_FUNC( TURN_LOG_LEVEL_INFO, "ERROR: received buffer have wrong length: %d, must be %d, len=%d\n", rc, clmessage_length + 4,(int)elem->in_buffer.len); return rc; } ns_bcopy(elem->in_buffer.buf + 4, &mi, sizeof(message_info)); miset=1; applen = elem->in_buffer.len -4; } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "ERROR: Unknown message received of size: %d\n",(int)(elem->in_buffer.len)); return rc; } if(miset) { /* printf("%s: 111.111: msgnum=%d, rmsgnum=%d, sent=%lu, recv=%lu\n",__FUNCTION__, mi->msgnum,elem->recvmsgnum,(unsigned long)mi->mstime,(unsigned long)current_mstime); */ if(mi.msgnum != elem->recvmsgnum+1) ++(elem->loss); else { u64bits clatency = (u64bits)time_minus(current_mstime,mi.mstime); if(clatency>max_latency) max_latency = clatency; if(clatencylatency += clatency; if(elem->rmsgnum>0) { u64bits cjitter = abs((int)(current_mstime-elem->recvtimems)-RTP_PACKET_INTERVAL); if(cjitter>max_jitter) max_jitter = cjitter; if(cjitterjitter += cjitter; } } elem->recvmsgnum = mi.msgnum; } elem->rmsgnum+=buffers; tot_recv_messages+=buffers; if(applen > 0) tot_recv_bytes += applen; else tot_recv_bytes += elem->in_buffer.len; elem->recvtimems=current_mstime; elem->wait_cycles = 0; } else if(rc == 0) { return 0; } else { return -1; } return rc; } static int client_shutdown(app_ur_session *elem) { if(!elem) return -1; elem->state=UR_STATE_DONE; elem->ctime=current_time; remove_all_from_ss(elem); if (clnet_verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"done, connection 0x%lx closed.\n",(long)elem); return 0; } static int client_write(app_ur_session *elem) { if(!elem) return -1; if(elem->state!=UR_STATE_READY) return -1; elem->ctime=current_time; message_info *mi = (message_info*)buffer_to_send; mi->msgnum=elem->wmsgnum; mi->mstime=current_mstime; app_tcp_conn_info *atc=NULL; if (is_TCP_relay()) { memcpy(elem->out_buffer.buf, buffer_to_send, clmessage_length); elem->out_buffer.len = clmessage_length; if(elem->pinfo.is_peer) { if(send(elem->pinfo.fd, elem->out_buffer.buf, clmessage_length, 0)>=0) { ++elem->wmsgnum; elem->to_send_timems += RTP_PACKET_INTERVAL; tot_send_messages++; tot_send_bytes += clmessage_length; } return 0; } if (!(elem->pinfo.tcp_conn) || !(elem->pinfo.tcp_conn_number)) { return -1; } int i = (unsigned int)(random()) % elem->pinfo.tcp_conn_number; atc = elem->pinfo.tcp_conn[i]; if(!atc->tcp_data_bound) { printf("%s: Uninitialized atc: i=%d, atc=0x%lx\n",__FUNCTION__,i,(long)atc); return -1; } } else if(!do_not_use_channel) { /* Let's always do padding: */ stun_init_channel_message(elem->chnum, &(elem->out_buffer), clmessage_length, mandatory_channel_padding || use_tcp); memcpy(elem->out_buffer.buf+4,buffer_to_send,clmessage_length); } else { stun_init_indication(STUN_METHOD_SEND, &(elem->out_buffer)); stun_attr_add(&(elem->out_buffer), STUN_ATTRIBUTE_DATA, buffer_to_send, clmessage_length); stun_attr_add_addr(&(elem->out_buffer),STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &(elem->pinfo.peer_addr)); if(dont_fragment) stun_attr_add(&(elem->out_buffer), STUN_ATTRIBUTE_DONT_FRAGMENT, NULL, 0); if(use_fingerprints) stun_attr_add_fingerprint_str(elem->out_buffer.buf,(size_t*)&(elem->out_buffer.len)); } if (elem->out_buffer.len > 0) { if (clnet_verbose && verbose_packets) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "before write ...\n"); } int rc=send_buffer(&(elem->pinfo),&(elem->out_buffer),1,atc); ++elem->wmsgnum; elem->to_send_timems += RTP_PACKET_INTERVAL; if(rc >= 0) { if (clnet_verbose && verbose_packets) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "wrote %d bytes\n", (int) rc); } tot_send_messages++; tot_send_bytes += clmessage_length; } else { return -1; } } return 0; } void client_input_handler(evutil_socket_t fd, short what, void* arg) { if(!(what&EV_READ)||!arg) return; UNUSED_ARG(fd); app_ur_session* elem = (app_ur_session*)arg; if(!elem) { return; } switch(elem->state) { case UR_STATE_READY: do { app_tcp_conn_info *atc = NULL; int is_tcp_data = 0; if(elem->pinfo.tcp_conn) { int i = 0; for(i=0;i<(int)(elem->pinfo.tcp_conn_number);++i) { if(elem->pinfo.tcp_conn[i]) { if((fd==elem->pinfo.tcp_conn[i]->tcp_data_fd) && (elem->pinfo.tcp_conn[i]->tcp_data_bound)) { is_tcp_data = 1; atc = elem->pinfo.tcp_conn[i]; break; } } } } int rc = client_read(elem, is_tcp_data, atc); if(rc<=0) break; } while(1); break; default: ; } } static void run_events(int short_burst) { struct timeval timeout; if(!short_burst) { timeout.tv_sec = 1; timeout.tv_usec = 0; } else { timeout.tv_sec = 0; timeout.tv_usec = 100000; } event_base_loopexit(client_event_base, &timeout); event_base_dispatch(client_event_base); } ////////////////////// main method ///////////////// static int start_client(const char *remote_address, int port, const unsigned char* ifname, const char *local_address, int messagenumber, int i) { app_ur_session* ss=create_new_ss(); app_ur_session* ss_rtcp=NULL; if(!no_rtcp) ss_rtcp = create_new_ss(); app_ur_conn_info clnet_info_probe; /* for load balancing probe */ ns_bzero(&clnet_info_probe,sizeof(clnet_info_probe)); clnet_info_probe.fd = -1; app_ur_conn_info *clnet_info=&(ss->pinfo); app_ur_conn_info *clnet_info_rtcp=NULL; if(!no_rtcp) clnet_info_rtcp = &(ss_rtcp->pinfo); uint16_t chnum=0; uint16_t chnum_rtcp=0; start_connection(port, remote_address, ifname, local_address, clnet_verbose, &clnet_info_probe, clnet_info, &chnum, clnet_info_rtcp, &chnum_rtcp); if(clnet_info_probe.ssl) { SSL_FREE(clnet_info_probe.ssl); clnet_info_probe.fd = -1; } else if(clnet_info_probe.fd != -1) { socket_closesocket(clnet_info_probe.fd); clnet_info_probe.fd = -1; } socket_set_nonblocking(clnet_info->fd); if(!no_rtcp) socket_set_nonblocking(clnet_info_rtcp->fd); struct event* ev = event_new(client_event_base,clnet_info->fd, EV_READ|EV_PERSIST,client_input_handler, ss); event_add(ev,NULL); struct event* ev_rtcp = NULL; if(!no_rtcp) { ev_rtcp = event_new(client_event_base,clnet_info_rtcp->fd, EV_READ|EV_PERSIST,client_input_handler, ss_rtcp); event_add(ev_rtcp,NULL); } ss->state=UR_STATE_READY; ss->input_ev=ev; ss->tot_msgnum=messagenumber; ss->recvmsgnum=-1; ss->chnum=chnum; if(!no_rtcp) { ss_rtcp->state=UR_STATE_READY; ss_rtcp->input_ev=ev_rtcp; ss_rtcp->tot_msgnum=ss->tot_msgnum; if(ss_rtcp->tot_msgnum<1) ss_rtcp->tot_msgnum=1; ss_rtcp->recvmsgnum=-1; ss_rtcp->chnum=chnum_rtcp; } elems[i]=ss; refresh_channel(ss, 0, 600); if(!no_rtcp) elems[i+1]=ss_rtcp; return 0; } static int start_c2c(const char *remote_address, int port, const unsigned char* ifname, const char *local_address, int messagenumber, int i) { app_ur_session* ss1=create_new_ss(); app_ur_session* ss1_rtcp=NULL; if(!no_rtcp) ss1_rtcp = create_new_ss(); app_ur_session* ss2=create_new_ss(); app_ur_session* ss2_rtcp=NULL; if(!no_rtcp) ss2_rtcp = create_new_ss(); app_ur_conn_info clnet_info_probe; /* for load balancing probe */ ns_bzero(&clnet_info_probe,sizeof(clnet_info_probe)); clnet_info_probe.fd = -1; app_ur_conn_info *clnet_info1=&(ss1->pinfo); app_ur_conn_info *clnet_info1_rtcp=NULL; if(!no_rtcp) clnet_info1_rtcp = &(ss1_rtcp->pinfo); app_ur_conn_info *clnet_info2=&(ss2->pinfo); app_ur_conn_info *clnet_info2_rtcp=NULL; if(!no_rtcp) clnet_info2_rtcp = &(ss2_rtcp->pinfo); uint16_t chnum1=0; uint16_t chnum1_rtcp=0; uint16_t chnum2=0; uint16_t chnum2_rtcp=0; start_c2c_connection(port, remote_address, ifname, local_address, clnet_verbose, &clnet_info_probe, clnet_info1, &chnum1, clnet_info1_rtcp, &chnum1_rtcp, clnet_info2, &chnum2, clnet_info2_rtcp, &chnum2_rtcp); if(clnet_info_probe.ssl) { SSL_FREE(clnet_info_probe.ssl); clnet_info_probe.fd = -1; } else if(clnet_info_probe.fd != -1) { socket_closesocket(clnet_info_probe.fd); clnet_info_probe.fd = -1; } socket_set_nonblocking(clnet_info1->fd); if(!no_rtcp) socket_set_nonblocking(clnet_info1_rtcp->fd); socket_set_nonblocking(clnet_info2->fd); if(!no_rtcp) socket_set_nonblocking(clnet_info2_rtcp->fd); struct event* ev1 = event_new(client_event_base,clnet_info1->fd, EV_READ|EV_PERSIST,client_input_handler, ss1); event_add(ev1,NULL); struct event* ev1_rtcp = NULL; if(!no_rtcp) { ev1_rtcp = event_new(client_event_base,clnet_info1_rtcp->fd, EV_READ|EV_PERSIST,client_input_handler, ss1_rtcp); event_add(ev1_rtcp,NULL); } struct event* ev2 = event_new(client_event_base,clnet_info2->fd, EV_READ|EV_PERSIST,client_input_handler, ss2); event_add(ev2,NULL); struct event* ev2_rtcp = NULL; if(!no_rtcp) { ev2_rtcp = event_new(client_event_base,clnet_info2_rtcp->fd, EV_READ|EV_PERSIST,client_input_handler, ss2_rtcp); event_add(ev2_rtcp,NULL); } ss1->state=UR_STATE_READY; ss1->input_ev=ev1; ss1->tot_msgnum=messagenumber; ss1->recvmsgnum=-1; ss1->chnum=chnum1; if(!no_rtcp) { ss1_rtcp->state=UR_STATE_READY; ss1_rtcp->input_ev=ev1_rtcp; ss1_rtcp->tot_msgnum=ss1->tot_msgnum; if(ss1_rtcp->tot_msgnum<1) ss1_rtcp->tot_msgnum=1; ss1_rtcp->recvmsgnum=-1; ss1_rtcp->chnum=chnum1_rtcp; } ss2->state=UR_STATE_READY; ss2->input_ev=ev2; ss2->tot_msgnum=ss1->tot_msgnum; ss2->recvmsgnum=-1; ss2->chnum=chnum2; if(!no_rtcp) { ss2_rtcp->state=UR_STATE_READY; ss2_rtcp->input_ev=ev2_rtcp; ss2_rtcp->tot_msgnum=ss1_rtcp->tot_msgnum; ss2_rtcp->recvmsgnum=-1; ss2_rtcp->chnum=chnum2_rtcp; } elems[i++]=ss1; if(!no_rtcp) elems[i++]=ss1_rtcp; elems[i++]=ss2; if(!no_rtcp) elems[i++]=ss2_rtcp; return 0; } static int refresh_channel(app_ur_session* elem, u16bits method, uint32_t lt) { stun_buffer message; app_ur_conn_info *clnet_info = &(elem->pinfo); if(clnet_info->is_peer) return 0; if (!method || (method == STUN_METHOD_REFRESH)) { stun_init_request(STUN_METHOD_REFRESH, &message); lt = htonl(lt); stun_attr_add(&message, STUN_ATTRIBUTE_LIFETIME, (const char*) <, 4); if(dual_allocation && !mobility) { int t = ((u08bits)random())%3; if(t) { u08bits field[4]; field[0] = (t==1) ? (u08bits)STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4 : (u08bits)STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6; field[1]=0; field[2]=0; field[3]=0; stun_attr_add(&message, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY, (const char*) field, 4); } } add_origin(&message); if(add_integrity(clnet_info, &message)<0) return -1; if(use_fingerprints) stun_attr_add_fingerprint_str(message.buf, (size_t*) &(message.len)); send_buffer(clnet_info, &message, 0,0); } if (lt && !addr_any(&(elem->pinfo.peer_addr))) { if(!no_permissions) { if (!method || (method == STUN_METHOD_CREATE_PERMISSION)) { stun_init_request(STUN_METHOD_CREATE_PERMISSION, &message); stun_attr_add_addr(&message, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &(elem->pinfo.peer_addr)); add_origin(&message); if(add_integrity(clnet_info, &message)<0) return -1; if(use_fingerprints) stun_attr_add_fingerprint_str(message.buf, (size_t*) &(message.len)); send_buffer(&(elem->pinfo), &message, 0,0); } } if (!method || (method == STUN_METHOD_CHANNEL_BIND)) { if (STUN_VALID_CHANNEL(elem->chnum)) { stun_set_channel_bind_request(&message, &(elem->pinfo.peer_addr), elem->chnum); add_origin(&message); if(add_integrity(clnet_info, &message)<0) return -1; if(use_fingerprints) stun_attr_add_fingerprint_str(message.buf, (size_t*) &(message.len)); send_buffer(&(elem->pinfo), &message,1,0); } } } elem->refresh_time = current_mstime + 30*1000; return 0; } static inline int client_timer_handler(app_ur_session* elem, int *done) { if (elem) { if (!turn_time_before(current_mstime, elem->refresh_time)) { refresh_channel(elem, 0, 600); } if(hang_on && elem->completed) return 0; int max_num = 50; int cur_num = 0; while (!turn_time_before(current_mstime, elem->to_send_timems)) { if(cur_num++>=max_num) break; if (elem->wmsgnum >= elem->tot_msgnum) { if (!turn_time_before(current_mstime, elem->finished_time) || (tot_recv_messages>=tot_messages)) { /* TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: elem=0x%x: 111.111: c=%d, t=%d, r=%d, w=%d\n",__FUNCTION__,(int)elem,elem->wait_cycles,elem->tot_msgnum,elem->rmsgnum,elem->wmsgnum); */ /* TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: 111.222: ly=%llu, ls=%llu, j=%llu\n",__FUNCTION__, (unsigned long long)elem->latency, (unsigned long long)elem->loss, (unsigned long long)elem->jitter); */ total_loss += elem->loss; elem->loss=0; total_latency += elem->latency; elem->latency=0; total_jitter += elem->jitter; elem->jitter=0; elem->completed = 1; if (!hang_on) { refresh_channel(elem,0,0); client_shutdown(elem); return 1; } else { return 0; } } } else { *done += 1; client_write(elem); elem->finished_time = current_mstime + STOPPING_TIME*1000; } } } return 0; } static void timer_handler(evutil_socket_t fd, short event, void *arg) { UNUSED_ARG(fd); UNUSED_ARG(event); UNUSED_ARG(arg); __turn_getMSTime(); if(start_full_timer) { int i = 0; int done = 0; for (i = 0; i < total_clients; ++i) { if (elems[i]) { int finished = client_timer_handler(elems[i], &done); if (finished) { elems[i] = NULL; } } } if(done>5 && (dos || random_disconnect)) { for (i = 0; i < total_clients; ++i) { if (elems[i]) { close(elems[i]->pinfo.fd); elems[i]->pinfo.fd = -1; } } } } } void start_mclient(const char *remote_address, int port, const unsigned char* ifname, const char *local_address, int messagenumber, int mclient) { if (mclient < 1) mclient = 1; total_clients = mclient; if(c2c) { //mclient must be a multiple of 4: if(!no_rtcp) mclient += ((4 - (mclient & 0x00000003)) & 0x00000003); else if(mclient & 0x1) ++mclient; } else { if(!no_rtcp) if(mclient & 0x1) ++mclient; } elems = (app_ur_session**)turn_malloc(sizeof(app_ur_session)*((mclient*2)+1)+sizeof(void*)); __turn_getMSTime(); u32bits stime = current_time; memset(buffer_to_send, 7, clmessage_length); client_event_base = turn_event_base_new(); int i = 0; int tot_clients = 0; if(c2c) { if(!no_rtcp) for (i = 0; i < (mclient >> 2); i++) { if(!dos) usleep(SLEEP_INTERVAL); if (start_c2c(remote_address, port, ifname, local_address, messagenumber, i << 2) < 0) { exit(-1); } tot_clients+=4; } else for (i = 0; i < (mclient >> 1); i++) { if(!dos) usleep(SLEEP_INTERVAL); if (start_c2c(remote_address, port, ifname, local_address, messagenumber, i << 1) < 0) { exit(-1); } tot_clients+=2; } } else { if(!no_rtcp) for (i = 0; i < (mclient >> 1); i++) { if(!dos) usleep(SLEEP_INTERVAL); if (start_client(remote_address, port, ifname, local_address, messagenumber, i << 1) < 0) { exit(-1); } tot_clients+=2; } else for (i = 0; i < mclient; i++) { if(!dos) usleep(SLEEP_INTERVAL); if (start_client(remote_address, port, ifname, local_address, messagenumber, i) < 0) { exit(-1); } tot_clients++; } } if(dos) _exit(0); total_clients = tot_clients; __turn_getMSTime(); struct event *ev = event_new(client_event_base, -1, EV_TIMEOUT|EV_PERSIST, timer_handler, NULL); struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 1000; evtimer_add(ev,&tv); for(i=0;ipinfo.is_peer) { int connect_err = 0; socket_connect(elems[i]->pinfo.fd, &(elems[i]->pinfo.remote_addr), &connect_err); } } else { int j = 0; for(j=i+1;jpinfo), &(elems[j]->pinfo.relay_addr)) < 0) { exit(-1); } } } } run_events(1); } __turn_getMSTime(); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Total connect time is %u\n", ((unsigned int) (current_time - stime))); stime = current_time; if(is_TCP_relay()) { u64bits connect_wait_start_time = current_time; while(1) { int i = 0; int completed = 0; if(passive_tcp) { for(i=0;ipinfo.is_peer) { completed+=1; } else if(elems[i]->pinfo.tcp_conn_number>0 && elems[i]->pinfo.tcp_conn[0]->tcp_data_bound) { completed += elems[i]->pinfo.tcp_conn_number; } } if(completed >= total_clients) break; } else { for(i=0;ipinfo.tcp_conn_number;j++) { if(elems[i]->pinfo.tcp_conn[j]->tcp_data_bound) { completed++; } } } if(completed >= total_clients*(total_clients-1)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%d connections are completed\n",(int)(completed)); break; } } run_events(0); if(current_time > connect_wait_start_time + STARTING_TCP_RELAY_TIME + total_clients) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: %d connections are completed, not enough\n", (int)(completed)); break; } } } __turn_getMSTime(); stime = current_time; for(i=0;ito_send_timems = current_mstime + 1000 + ((u32bits)random())%5000; } tot_messages = elems[0]->tot_msgnum * total_clients; start_full_timer = 1; while (1) { run_events(1); int msz = (int)current_clients_number; if (msz < 1) { break; } if(show_statistics) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: msz=%d, tot_send_msgs=%lu, tot_recv_msgs=%lu, tot_send_bytes ~ %llu, tot_recv_bytes ~ %llu\n", __FUNCTION__, msz, (unsigned long) tot_send_messages, (unsigned long) tot_recv_messages, (unsigned long long) tot_send_bytes, (unsigned long long) tot_recv_bytes); show_statistics=0; } } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: tot_send_msgs=%lu, tot_recv_msgs=%lu\n", __FUNCTION__, (unsigned long) tot_send_messages, (unsigned long) tot_recv_messages); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: tot_send_bytes ~ %lu, tot_recv_bytes ~ %lu\n", __FUNCTION__, (unsigned long) tot_send_bytes, (unsigned long) tot_recv_bytes); if (client_event_base) event_base_free(client_event_base); if(tot_send_messagesnonce[0]) { if(oauth && clnet_info->oauth) { u16bits method = stun_get_method_str(message->buf, message->len); int cok = clnet_info->cok; if(((method == STUN_METHOD_ALLOCATE) || (method == STUN_METHOD_REFRESH)) || !(clnet_info->key_set)) { cok=((unsigned short)random())%3; clnet_info->cok = cok; oauth_token otoken; encoded_oauth_token etoken; u08bits nonce[12]; RAND_bytes((unsigned char*)nonce,12); long halflifetime = OAUTH_SESSION_LIFETIME/2; long random_lifetime = 0; while(!random_lifetime) { random_lifetime = random(); } if(random_lifetime<0) random_lifetime=-random_lifetime; random_lifetime = random_lifetime % halflifetime; otoken.enc_block.lifetime = (uint32_t)(halflifetime + random_lifetime); otoken.enc_block.timestamp = ((uint64_t)turn_time()) << 16; if(shatype == SHATYPE_SHA256) { otoken.enc_block.key_length = 32; } else if(shatype == SHATYPE_SHA384) { otoken.enc_block.key_length = 48; } else if(shatype == SHATYPE_SHA512) { otoken.enc_block.key_length = 64; } else { otoken.enc_block.key_length = 20; } RAND_bytes((unsigned char *)(otoken.enc_block.mac_key), otoken.enc_block.key_length); if(encode_oauth_token(clnet_info->server_name, &etoken, &(okey_array[cok]), &otoken, nonce)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO," Cannot encode token\n"); return -1; } stun_attr_add_str(message->buf, (size_t*)&(message->len), STUN_ATTRIBUTE_OAUTH_ACCESS_TOKEN, (const u08bits*)etoken.token, (int)etoken.size); ns_bcopy(otoken.enc_block.mac_key,clnet_info->key,otoken.enc_block.key_length); clnet_info->key_set = 1; } if(stun_attr_add_integrity_by_key_str(message->buf, (size_t*)&(message->len), (u08bits*)okey_array[cok].kid, clnet_info->realm, clnet_info->key, clnet_info->nonce, shatype)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO," Cannot add integrity to the message\n"); return -1; } //self-test: { password_t pwd; if(stun_check_message_integrity_by_key_str(get_turn_credentials_type(), message->buf, (size_t)(message->len), clnet_info->key, pwd, shatype)<1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR," Self-test of integrity does not comple correctly !\n"); return -1; } } } else { if(stun_attr_add_integrity_by_user_str(message->buf, (size_t*)&(message->len), g_uname, clnet_info->realm, g_upwd, clnet_info->nonce, shatype)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO," Cannot add integrity to the message\n"); return -1; } } } return 0; } int check_integrity(app_ur_conn_info *clnet_info, stun_buffer *message) { SHATYPE sht = shatype; if(oauth && clnet_info->oauth) { password_t pwd; return stun_check_message_integrity_by_key_str(get_turn_credentials_type(), message->buf, (size_t)(message->len), clnet_info->key, pwd, sht); } else { if(stun_check_message_integrity_str(get_turn_credentials_type(), message->buf, (size_t)(message->len), g_uname, clnet_info->realm, g_upwd, sht)<1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Wrong integrity in a message received from server\n"); return -1; } } return 0; } SOCKET_TYPE get_socket_type() { if(use_sctp) { if(use_secure) { return TLS_SCTP_SOCKET; } else { return SCTP_SOCKET; } } else if(use_tcp) { if(use_secure) { return TLS_SOCKET; } else { return TCP_SOCKET; } } else { if(use_secure) { return DTLS_SOCKET; } else { return UDP_SOCKET; } } } /////////////////////////////////////////// turnserver-4.5.1.1/src/apps/uclient/session.h0000664000175000017500000000675413436577655017657 0ustar misimisi/* * 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. */ #ifndef __SESSION__ #define __SESSION__ #include #include #include "ns_turn_ioaddr.h" #include "ns_turn_utils.h" #include "stun_buffer.h" #include "apputils.h" #include "ns_turn_openssl.h" #ifdef __cplusplus extern "C" { #endif ///////// types //////////// enum _UR_STATE { UR_STATE_UNKNOWN=0, UR_STATE_READY, UR_STATE_DONE }; typedef enum _UR_STATE UR_STATE; //////////////// session info ////////////////////// typedef struct { /* RFC 6062 */ u32bits cid; ioa_addr tcp_data_local_addr; ioa_socket_raw tcp_data_fd; SSL *tcp_data_ssl; int tcp_data_bound; } app_tcp_conn_info; typedef struct { ioa_addr local_addr; char lsaddr[129]; ioa_addr remote_addr; char rsaddr[129]; char ifname[129]; ioa_addr peer_addr; ioa_addr relay_addr; ioa_socket_raw fd; SSL *ssl; int broken; u08bits nonce[STUN_MAX_NONCE_SIZE+1]; u08bits realm[STUN_MAX_REALM_SIZE+1]; /* oAuth */ int oauth; u08bits server_name[STUN_MAX_SERVER_NAME_SIZE+1]; hmackey_t key; int key_set; int cok; /* RFC 6062 */ app_tcp_conn_info **tcp_conn; size_t tcp_conn_number; int is_peer; char s_mobile_id[33]; } app_ur_conn_info; typedef struct { app_ur_conn_info pinfo; UR_STATE state; unsigned int ctime; uint16_t chnum; int wait_cycles; int timer_cycle; int completed; struct event *input_ev; struct event *input_tcp_data_ev; stun_buffer in_buffer; stun_buffer out_buffer; u32bits refresh_time; u32bits finished_time; //Msg counters: int tot_msgnum; int wmsgnum; int rmsgnum; int recvmsgnum; u32bits recvtimems; u32bits to_send_timems; //Statistics: size_t loss; u64bits latency; u64bits jitter; } app_ur_session; /////////////////////////////////////////////////////// typedef struct _message_info { int msgnum; u64bits mstime; } message_info; /////////////////////////////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__SESSION__ turnserver-4.5.1.1/src/apps/uclient/startuclient.c0000664000175000017500000013012013436577655020671 0ustar misimisi/* * 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 #include "apputils.h" #include "ns_turn_utils.h" #include "startuclient.h" #include "ns_turn_msg.h" #include "uclient.h" #include "session.h" #include "ns_turn_openssl.h" ///////////////////////////////////////// #define MAX_CONNECT_EFFORTS (77) #define DTLS_MAX_CONNECT_TIMEOUT (30) #define MAX_TLS_CYCLES (32) #define EXTRA_CREATE_PERMS (25) static uint64_t current_reservation_token = 0; static int allocate_rtcp = 0; static const int never_allocate_rtcp = 0; #if ALPN_SUPPORTED static const unsigned char kALPNProtos[] = "\x08http/1.1\x09stun.turn\x12stun.nat-discovery"; static const size_t kALPNProtosLen = sizeof(kALPNProtos) - 1; #endif ///////////////////////////////////////// int rare_event(void) { if(dos) return (((unsigned long)random()) %1000 == 777); return 0; } int not_rare_event(void) { if(dos) return ((((unsigned long)random()) %1000) < 200); return 0; } static int get_allocate_address_family(ioa_addr *relay_addr) { if(relay_addr->ss.sa_family == AF_INET) return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT; else if(relay_addr->ss.sa_family == AF_INET6) return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6; else return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_INVALID; } ///////////////////////////////////////// static SSL* tls_connect(ioa_socket_raw fd, ioa_addr *remote_addr, int *try_again, int connect_cycle) { int ctxtype = (int)(((unsigned long)random())%root_tls_ctx_num); SSL *ssl; ssl = SSL_NEW(root_tls_ctx[ctxtype]); #if ALPN_SUPPORTED SSL_set_alpn_protos(ssl, kALPNProtos, kALPNProtosLen); #endif if(use_tcp) { SSL_set_fd(ssl, fd); } else { #if !DTLS_SUPPORTED UNUSED_ARG(remote_addr); fprintf(stderr,"ERROR: DTLS is not supported.\n"); exit(-1); #else /* Create BIO, connect and set to already connected */ BIO *bio = BIO_new_dgram(fd, BIO_CLOSE); //bio = BIO_new_socket(fd, BIO_CLOSE); BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &remote_addr->ss); SSL_set_bio(ssl, bio, bio); { struct timeval timeout; /* Set and activate timeouts */ timeout.tv_sec = DTLS_MAX_CONNECT_TIMEOUT; timeout.tv_usec = 0; BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout); } set_mtu_df(ssl, fd, remote_addr->ss.sa_family, SOSO_MTU, !use_tcp, clnet_verbose); #endif } SSL_set_max_cert_list(ssl, 655350); if (clnet_verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "call SSL_connect...\n"); int rc = 0; do { do { rc = SSL_connect(ssl); } while (rc < 0 && errno == EINTR); int orig_errno = errno; if (rc > 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: client session connected with cipher %s, method=%s\n",__FUNCTION__, SSL_get_cipher(ssl),turn_get_ssl_method(ssl,NULL)); if(clnet_verbose && SSL_get_peer_certificate(ssl)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "------------------------------------------------------------\n"); X509_NAME_print_ex_fp(stdout, X509_get_subject_name(SSL_get_peer_certificate(ssl)), 1, XN_FLAG_MULTILINE); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n\n Cipher: %s\n", SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n------------------------------------------------------------\n\n"); } break; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot connect: rc=%d, ctx=%d\n", __FUNCTION__,rc,ctxtype); switch (SSL_get_error(ssl, rc)) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: if(!dos) usleep(1000); continue; default: { char buf[1025]; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "errno=%d, err=%d, %s (%d)\n",orig_errno, (int)ERR_get_error(), ERR_error_string(ERR_get_error(), buf), (int)SSL_get_error(ssl, rc)); if(connect_cycleis_peer && (*local_address==0)) { if(remote_addr.ss.sa_family == AF_INET6) { if (make_ioa_addr((const u08bits*) "::1", 0, &local_addr) < 0) { return -1; } } else { if (make_ioa_addr((const u08bits*) "127.0.0.1", 0, &local_addr) < 0) { return -1; } } addr_bind(clnet_fd, &local_addr, 0, 1, get_socket_type()); } else if (strlen(local_address) > 0) { if (make_ioa_addr((const u08bits*) local_address, 0, &local_addr) < 0) return -1; addr_bind(clnet_fd, &local_addr,0,1,get_socket_type()); } if(clnet_info->is_peer) { ; } else if(socket_connect(clnet_fd, &remote_addr, &connect_err)>0) goto start_socket; if (clnet_info) { addr_cpy(&(clnet_info->remote_addr), &remote_addr); addr_cpy(&(clnet_info->local_addr), &local_addr); clnet_info->fd = clnet_fd; addr_get_from_sock(clnet_fd, &(clnet_info->local_addr)); STRCPY(clnet_info->lsaddr,local_address); STRCPY(clnet_info->rsaddr,remote_address); STRCPY(clnet_info->ifname,(const char*)ifname); } if (use_secure) { int try_again = 0; clnet_info->ssl = tls_connect(clnet_info->fd, &remote_addr,&try_again,connect_cycle++); if (!clnet_info->ssl) { if(try_again) { goto start_socket; } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot SSL connect to remote addr\n", __FUNCTION__); exit(-1); } } if(verbose && clnet_info) { addr_debug_print(verbose, &(clnet_info->local_addr), "Connected from"); addr_debug_print(verbose, &remote_addr, "Connected to"); } if(!dos) usleep(500); return 0; } int read_mobility_ticket(app_ur_conn_info *clnet_info, stun_buffer *message) { int ret = 0; if(clnet_info && message) { stun_attr_ref s_mobile_id_sar = stun_attr_get_first_by_type(message, STUN_ATTRIBUTE_MOBILITY_TICKET); if(s_mobile_id_sar) { int smid_len = stun_attr_get_len(s_mobile_id_sar); if(smid_len>0 && (((size_t)smid_len)s_mobile_id))) { const u08bits* smid_val = stun_attr_get_value(s_mobile_id_sar); if(smid_val) { ns_bcopy(smid_val, clnet_info->s_mobile_id, (size_t)smid_len); clnet_info->s_mobile_id[smid_len] = 0; if (clnet_verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: smid=%s\n", __FUNCTION__, clnet_info->s_mobile_id); } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: smid_len=%d\n", __FUNCTION__, smid_len); ret = -1; } } } return ret; } void add_origin(stun_buffer *message) { if(message && origin[0]) { const char* some_origin = "https://carleon.gov:443"; stun_attr_add(message, STUN_ATTRIBUTE_ORIGIN, some_origin, strlen(some_origin)); stun_attr_add(message, STUN_ATTRIBUTE_ORIGIN, origin, strlen(origin)); some_origin = "ftp://uffrith.net"; stun_attr_add(message, STUN_ATTRIBUTE_ORIGIN, some_origin, strlen(some_origin)); } } static int clnet_allocate(int verbose, app_ur_conn_info *clnet_info, ioa_addr *relay_addr, int af, char *turn_addr, u16bits *turn_port) { int af_cycle = 0; int reopen_socket = 0; int allocate_finished; stun_buffer request_message, response_message; beg_allocate: allocate_finished=0; while (!allocate_finished && af_cycle++ < 32) { int allocate_sent = 0; if(reopen_socket && !use_tcp) { socket_closesocket(clnet_info->fd); clnet_info->fd = -1; if (clnet_connect(addr_get_port(&(clnet_info->remote_addr)), clnet_info->rsaddr, (u08bits*)clnet_info->ifname, clnet_info->lsaddr, verbose, clnet_info) < 0) { exit(-1); } reopen_socket = 0; } int af4 = dual_allocation || (af == STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4); int af6 = dual_allocation || (af == STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6); uint64_t reservation_token = 0; char* rt = NULL; int ep = !no_rtcp && !dual_allocation; if(!no_rtcp) { if (!never_allocate_rtcp && allocate_rtcp) { reservation_token = ioa_ntoh64(current_reservation_token); rt = (char*) (&reservation_token); } } if(is_TCP_relay()) { ep = -1; } else if(rt) { ep = -1; } else if(!ep) { ep = (((u08bits)random()) % 2); ep = ep-1; } if(!dos) stun_set_allocate_request(&request_message, UCLIENT_SESSION_LIFETIME, af4, af6, relay_transport, mobility, rt, ep); else stun_set_allocate_request(&request_message, UCLIENT_SESSION_LIFETIME/3, af4, af6, relay_transport, mobility, rt, ep); if(bps) stun_attr_add_bandwidth_str(request_message.buf, (size_t*)(&(request_message.len)), bps); if(dont_fragment) stun_attr_add(&request_message, STUN_ATTRIBUTE_DONT_FRAGMENT, NULL, 0); add_origin(&request_message); if(add_integrity(clnet_info, &request_message)<0) return -1; stun_attr_add_fingerprint_str(request_message.buf,(size_t*)&(request_message.len)); while (!allocate_sent) { int len = send_buffer(clnet_info, &request_message,0,0); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "allocate sent\n"); } allocate_sent = 1; } else { perror("send"); exit(1); } } ////////////<<==allocate send if(not_rare_event()) return 0; ////////allocate response==>> { int allocate_received = 0; while (!allocate_received) { int len = recv_buffer(clnet_info, &response_message, 1, 0, NULL, &request_message); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "allocate response received: \n"); } response_message.len = len; int err_code = 0; u08bits err_msg[129]; if (stun_is_success_response(&response_message)) { allocate_received = 1; allocate_finished = 1; if(clnet_info->nonce[0]) { if(check_integrity(clnet_info, &response_message)<0) return -1; } if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "success\n"); } { int found = 0; stun_attr_ref sar = stun_attr_get_first(&response_message); while (sar) { int attr_type = stun_attr_get_type(sar); if(attr_type == STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS) { if (stun_attr_get_addr(&response_message, sar, relay_addr, NULL) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: !!!: relay addr cannot be received (1)\n", __FUNCTION__); return -1; } else { if (verbose) { ioa_addr raddr; memcpy(&raddr, relay_addr,sizeof(ioa_addr)); addr_debug_print(verbose, &raddr,"Received relay addr"); } if(!addr_any(relay_addr)) { if(relay_addr->ss.sa_family == AF_INET) { if(default_address_family != STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6) { found = 1; addr_cpy(&(clnet_info->relay_addr),relay_addr); break; } } if(relay_addr->ss.sa_family == AF_INET6) { if(default_address_family == STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6) { found = 1; addr_cpy(&(clnet_info->relay_addr),relay_addr); break; } } } } } sar = stun_attr_get_next(&response_message,sar); } if(!found) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: !!!: relay addr cannot be received (2)\n", __FUNCTION__); return -1; } } stun_attr_ref rt_sar = stun_attr_get_first_by_type( &response_message, STUN_ATTRIBUTE_RESERVATION_TOKEN); uint64_t rtv = stun_attr_get_reservation_token_value(rt_sar); current_reservation_token = rtv; if (verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: rtv=%llu\n", __FUNCTION__, (long long unsigned int)rtv); read_mobility_ticket(clnet_info, &response_message); } else if (stun_is_challenge_response_str(response_message.buf, (size_t)response_message.len, &err_code,err_msg,sizeof(err_msg), clnet_info->realm,clnet_info->nonce, clnet_info->server_name, &(clnet_info->oauth))) { goto beg_allocate; } else if (stun_is_error_response(&response_message, &err_code,err_msg,sizeof(err_msg))) { allocate_received = 1; if(err_code == 300) { if(clnet_info->nonce[0]) { if(check_integrity(clnet_info, &response_message)<0) return -1; } ioa_addr alternate_server; if(stun_attr_get_first_addr(&response_message, STUN_ATTRIBUTE_ALTERNATE_SERVER, &alternate_server, NULL)==-1) { //error } else if(turn_addr && turn_port){ addr_to_string_no_port(&alternate_server, (u08bits*)turn_addr); *turn_port = (u16bits)addr_get_port(&alternate_server); } } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "error %d (%s)\n", err_code,(char*)err_msg); if (err_code != 437) { allocate_finished = 1; current_reservation_token = 0; return -1; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "trying allocate again %d...\n", err_code); sleep(1); reopen_socket = 1; } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "unknown allocate response\n"); /* Try again ? */ } } else { perror("recv"); exit(-1); break; } } } } ////////////<<== allocate response received if(rare_event()) return 0; if(!allocate_finished) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot complete Allocation\n"); exit(-1); } allocate_rtcp = !allocate_rtcp; if (1) { af_cycle = 0; if(clnet_info->s_mobile_id[0]) { int fd = clnet_info->fd; SSL* ssl = clnet_info->ssl; int close_now = (int)(random()%2); if(close_now) { int close_socket = (int)(random()%2); if(ssl && !close_socket) { SSL_shutdown(ssl); SSL_FREE(ssl); fd = -1; } else if(fd>=0) { close(fd); fd = -1; ssl = NULL; } } app_ur_conn_info ci; ns_bcopy(clnet_info,&ci,sizeof(ci)); ci.fd = -1; ci.ssl = NULL; clnet_info->fd = -1; clnet_info->ssl = NULL; //Reopen: if(clnet_connect(addr_get_port(&(ci.remote_addr)), ci.rsaddr, (unsigned char*)ci.ifname, ci.lsaddr, clnet_verbose, clnet_info)<0) { exit(-1); } if(ssl) { SSL_shutdown(ssl); SSL_FREE(ssl); } else if(fd>=0) { close(fd); } } beg_refresh: if(af_cycle++>32) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot complete Refresh\n"); exit(-1); } //==>>refresh request, for an example only: { int refresh_sent = 0; stun_init_request(STUN_METHOD_REFRESH, &request_message); uint32_t lt = htonl(UCLIENT_SESSION_LIFETIME); stun_attr_add(&request_message, STUN_ATTRIBUTE_LIFETIME, (const char*) <, 4); if(clnet_info->s_mobile_id[0]) { stun_attr_add(&request_message, STUN_ATTRIBUTE_MOBILITY_TICKET, (const char*)clnet_info->s_mobile_id, strlen(clnet_info->s_mobile_id)); } if(dual_allocation && !mobility) { int t = ((u08bits)random())%3; if(t) { u08bits field[4]; field[0] = (t==1) ? (u08bits)STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4 : (u08bits)STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6; field[1]=0; field[2]=0; field[3]=0; stun_attr_add(&request_message, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY, (const char*) field, 4); } } add_origin(&request_message); if(add_integrity(clnet_info, &request_message)<0) return -1; stun_attr_add_fingerprint_str(request_message.buf,(size_t*)&(request_message.len)); while (!refresh_sent) { int len = send_buffer(clnet_info, &request_message, 0,0); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "refresh sent\n"); } refresh_sent = 1; if(clnet_info->s_mobile_id[0]) { usleep(10000); send_buffer(clnet_info, &request_message, 0,0); } } else { perror("send"); exit(1); } } } if(not_rare_event()) return 0; ////////refresh response==>> { int refresh_received = 0; while (!refresh_received) { int len = recv_buffer(clnet_info, &response_message, 1, 0, NULL, &request_message); if(clnet_info->s_mobile_id[0]) { len = recv_buffer(clnet_info, &response_message, 1, 0, NULL, &request_message); } if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "refresh response received: \n"); } response_message.len = len; int err_code = 0; u08bits err_msg[129]; if (stun_is_success_response(&response_message)) { read_mobility_ticket(clnet_info, &response_message); refresh_received = 1; if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "success\n"); } } else if (stun_is_challenge_response_str(response_message.buf, (size_t)response_message.len, &err_code,err_msg,sizeof(err_msg), clnet_info->realm,clnet_info->nonce, clnet_info->server_name, &(clnet_info->oauth))) { goto beg_refresh; } else if (stun_is_error_response(&response_message, &err_code,err_msg,sizeof(err_msg))) { refresh_received = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "error %d (%s)\n", err_code,(char*)err_msg); return -1; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "unknown refresh response\n"); /* Try again ? */ } } else { perror("recv"); exit(-1); break; } } } } return 0; } static int turn_channel_bind(int verbose, uint16_t *chn, app_ur_conn_info *clnet_info, ioa_addr *peer_addr) { stun_buffer request_message, response_message; beg_bind: { int cb_sent = 0; if(negative_test) { *chn = stun_set_channel_bind_request(&request_message, peer_addr, (u16bits)random()); } else { *chn = stun_set_channel_bind_request(&request_message, peer_addr, *chn); } add_origin(&request_message); if(add_integrity(clnet_info, &request_message)<0) return -1; stun_attr_add_fingerprint_str(request_message.buf,(size_t*)&(request_message.len)); while (!cb_sent) { int len = send_buffer(clnet_info, &request_message, 0,0); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "channel bind sent\n"); } cb_sent = 1; } else { perror("send"); exit(1); } } } ////////////<<==channel bind send if(not_rare_event()) return 0; ////////channel bind response==>> { int cb_received = 0; while (!cb_received) { int len = recv_buffer(clnet_info, &response_message, 1, 0, NULL, &request_message); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "cb response received: \n"); } int err_code = 0; u08bits err_msg[129]; if (stun_is_success_response(&response_message)) { cb_received = 1; if(clnet_info->nonce[0]) { if(check_integrity(clnet_info, &response_message)<0) return -1; } if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "success: 0x%x\n", (int) (*chn)); } } else if (stun_is_challenge_response_str(response_message.buf, (size_t)response_message.len, &err_code,err_msg,sizeof(err_msg), clnet_info->realm,clnet_info->nonce, clnet_info->server_name, &(clnet_info->oauth))) { goto beg_bind; } else if (stun_is_error_response(&response_message, &err_code,err_msg,sizeof(err_msg))) { cb_received = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "channel bind: error %d (%s)\n", err_code,(char*)err_msg); return -1; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "unknown channel bind response\n"); /* Try again ? */ } } else { perror("recv"); exit(-1); break; } } } return 0; } static int turn_create_permission(int verbose, app_ur_conn_info *clnet_info, ioa_addr *peer_addr, int addrnum) { if(no_permissions || (addrnum<1)) return 0; char saddr[129]="\0"; if (verbose) { addr_to_string(peer_addr,(u08bits*)saddr); } stun_buffer request_message, response_message; beg_cp: { int cp_sent = 0; stun_init_request(STUN_METHOD_CREATE_PERMISSION, &request_message); { int addrindex; for(addrindex=0;addrindex 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "create perm sent: %s\n",saddr); } cp_sent = 1; } else { perror("send"); exit(1); } } } ////////////<<==create permission send if(not_rare_event()) return 0; ////////create permission response==>> { int cp_received = 0; while (!cp_received) { int len = recv_buffer(clnet_info, &response_message, 1, 0, NULL, &request_message); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "cp response received: \n"); } int err_code = 0; u08bits err_msg[129]; if (stun_is_success_response(&response_message)) { cp_received = 1; if(clnet_info->nonce[0]) { if(check_integrity(clnet_info, &response_message)<0) return -1; } if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "success\n"); } } else if (stun_is_challenge_response_str(response_message.buf, (size_t)response_message.len, &err_code,err_msg,sizeof(err_msg), clnet_info->realm,clnet_info->nonce, clnet_info->server_name, &(clnet_info->oauth))) { goto beg_cp; } else if (stun_is_error_response(&response_message, &err_code,err_msg,sizeof(err_msg))) { cp_received = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "create permission error %d (%s)\n", err_code,(char*)err_msg); return -1; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "unknown create permission response\n"); /* Try again ? */ } } else { perror("recv"); exit(-1); } } } return 0; } int start_connection(uint16_t clnet_remote_port0, const char *remote_address0, const unsigned char* ifname, const char *local_address, int verbose, app_ur_conn_info *clnet_info_probe, app_ur_conn_info *clnet_info, uint16_t *chn, app_ur_conn_info *clnet_info_rtcp, uint16_t *chn_rtcp) { ioa_addr relay_addr; ioa_addr relay_addr_rtcp; ioa_addr peer_addr_rtcp; addr_cpy(&peer_addr_rtcp,&peer_addr); addr_set_port(&peer_addr_rtcp,addr_get_port(&peer_addr_rtcp)+1); /* Probe: */ if (clnet_connect(clnet_remote_port0, remote_address0, ifname, local_address, verbose, clnet_info_probe) < 0) { exit(-1); } uint16_t clnet_remote_port = clnet_remote_port0; char remote_address[1025]; STRCPY(remote_address,remote_address0); clnet_allocate(verbose, clnet_info_probe, &relay_addr, default_address_family, remote_address, &clnet_remote_port); /* Real: */ *chn = 0; if(chn_rtcp) *chn_rtcp=0; if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info) < 0) { exit(-1); } if(!no_rtcp) { if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info_rtcp) < 0) { exit(-1); } } int af = default_address_family ? default_address_family : get_allocate_address_family(&peer_addr); if (clnet_allocate(verbose, clnet_info, &relay_addr, af, NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; if(!no_rtcp) { af = default_address_family ? default_address_family : get_allocate_address_family(&peer_addr_rtcp); if (clnet_allocate(verbose, clnet_info_rtcp, &relay_addr_rtcp, af,NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; } if (!dos) { if (!do_not_use_channel) { /* These multiple "channel bind" requests are here only because * we are playing with the TURN server trying to screw it */ if (turn_channel_bind(verbose, chn, clnet_info, &peer_addr_rtcp) < 0) { exit(-1); } if(rare_event()) return 0; if (turn_channel_bind(verbose, chn, clnet_info, &peer_addr_rtcp) < 0) { exit(-1); } if(rare_event()) return 0; *chn = 0; if (turn_channel_bind(verbose, chn, clnet_info, &peer_addr) < 0) { exit(-1); } if(rare_event()) return 0; if (turn_channel_bind(verbose, chn, clnet_info, &peer_addr) < 0) { exit(-1); } if(rare_event()) return 0; if(extra_requests) { const char *sarbaddr = "164.156.178.190"; if(random() % 2 == 0) sarbaddr = "2001::172"; ioa_addr arbaddr; make_ioa_addr((const u08bits*)sarbaddr, 333, &arbaddr); int i; int maxi = (unsigned short)random() % EXTRA_CREATE_PERMS; for(i=0;i0) addr_cpy(&arbaddr[i],&arbaddr[0]); addr_set_port(&arbaddr[i], (unsigned short)random()); u08bits *u=(u08bits*)&(arbaddr[i].s4.sin_addr); u[(unsigned short)random()%4] = u[(unsigned short)random()%4] + 1; //char sss[128]; //addr_to_string(&arbaddr[i],(u08bits*)sss); //printf("%s: 111.111: %s\n",__FUNCTION__,sss); } turn_create_permission(verbose, clnet_info, arbaddr, maxi); } } else { int before=(random()%2 == 0); if(before) { if (turn_create_permission(verbose, clnet_info, &peer_addr, 1) < 0) { exit(-1); } if(rare_event()) return 0; if (turn_create_permission(verbose, clnet_info, &peer_addr_rtcp, 1) < 0) { exit(-1); } if(rare_event()) return 0; } if(extra_requests) { const char *sarbaddr = "64.56.78.90"; if(random() % 2 == 0) sarbaddr = "2001::172"; ioa_addr arbaddr[EXTRA_CREATE_PERMS]; make_ioa_addr((const u08bits*)sarbaddr, 333, &arbaddr[0]); int i; int maxi = (unsigned short)random() % EXTRA_CREATE_PERMS; for(i=0;i0) addr_cpy(&arbaddr[i],&arbaddr[0]); addr_set_port(&arbaddr[i], (unsigned short)random()); u08bits *u=(u08bits*)&(arbaddr[i].s4.sin_addr); u[(unsigned short)random()%4] = u[(unsigned short)random()%4] + 1; //char sss[128]; //addr_to_string(&arbaddr,(u08bits*)sss); //printf("%s: 111.111: %s\n",__FUNCTION__,sss); } turn_create_permission(verbose, clnet_info, arbaddr, maxi); } if(!before) { if (turn_create_permission(verbose, clnet_info, &peer_addr, 1) < 0) { exit(-1); } if(rare_event()) return 0; if (turn_create_permission(verbose, clnet_info, &peer_addr_rtcp, 1) < 0) { exit(-1); } if(rare_event()) return 0; } if (!no_rtcp) { if (turn_create_permission(verbose, clnet_info_rtcp, &peer_addr_rtcp, 1) < 0) { exit(-1); } if(rare_event()) return 0; if (turn_create_permission(verbose, clnet_info_rtcp, &peer_addr, 1) < 0) { exit(-1); } if(rare_event()) return 0; } } } addr_cpy(&(clnet_info->peer_addr), &peer_addr); if(!no_rtcp) addr_cpy(&(clnet_info_rtcp->peer_addr), &peer_addr_rtcp); return 0; } int start_c2c_connection(uint16_t clnet_remote_port0, const char *remote_address0, const unsigned char* ifname, const char *local_address, int verbose, app_ur_conn_info *clnet_info_probe, app_ur_conn_info *clnet_info1, uint16_t *chn1, app_ur_conn_info *clnet_info1_rtcp, uint16_t *chn1_rtcp, app_ur_conn_info *clnet_info2, uint16_t *chn2, app_ur_conn_info *clnet_info2_rtcp, uint16_t *chn2_rtcp) { ioa_addr relay_addr1; ioa_addr relay_addr1_rtcp; ioa_addr relay_addr2; ioa_addr relay_addr2_rtcp; *chn1 = 0; *chn2 = 0; if(chn1_rtcp) *chn1_rtcp=0; if(chn2_rtcp) *chn2_rtcp=0; /* Probe: */ if (clnet_connect(clnet_remote_port0, remote_address0, ifname, local_address, verbose, clnet_info_probe) < 0) { exit(-1); } uint16_t clnet_remote_port = clnet_remote_port0; char remote_address[1025]; STRCPY(remote_address,remote_address0); clnet_allocate(verbose, clnet_info_probe, &relay_addr1, default_address_family, remote_address, &clnet_remote_port); if(rare_event()) return 0; /* Real: */ if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info1) < 0) { exit(-1); } if(!no_rtcp) if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info1_rtcp) < 0) { exit(-1); } if(passive_tcp) clnet_info2->is_peer = 1; if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info2) < 0) { exit(-1); } if(!no_rtcp) if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info2_rtcp) < 0) { exit(-1); } if(!no_rtcp) { if (clnet_allocate(verbose, clnet_info1, &relay_addr1, default_address_family,NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; if (clnet_allocate(verbose, clnet_info1_rtcp, &relay_addr1_rtcp, default_address_family,NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; if (clnet_allocate(verbose, clnet_info2, &relay_addr2, default_address_family,NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; if (clnet_allocate(verbose, clnet_info2_rtcp, &relay_addr2_rtcp, default_address_family,NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; } else { if (clnet_allocate(verbose, clnet_info1, &relay_addr1, default_address_family,NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; if(!(clnet_info2->is_peer)) { if (clnet_allocate(verbose, clnet_info2, &relay_addr2, default_address_family,NULL,NULL) < 0) { exit(-1); } if(rare_event()) return 0; } else { addr_cpy(&(clnet_info2->remote_addr),&relay_addr1); addr_cpy(&relay_addr2,&(clnet_info2->local_addr)); } } if (!do_not_use_channel) { if (turn_channel_bind(verbose, chn1, clnet_info1, &relay_addr2) < 0) { exit(-1); } if(extra_requests) { const char *sarbaddr = "164.156.178.190"; if(random() % 2 == 0) sarbaddr = "2001::172"; ioa_addr arbaddr; make_ioa_addr((const u08bits*)sarbaddr, 333, &arbaddr); int i; int maxi = (unsigned short)random() % EXTRA_CREATE_PERMS; for(i=0;i0) addr_cpy(&arbaddr[i],&arbaddr[0]); addr_set_port(&arbaddr[i], (unsigned short)random()); u08bits *u=(u08bits*)&(arbaddr[i].s4.sin_addr); u[(unsigned short)random()%4] = u[(unsigned short)random()%4] + 1; //char sss[128]; //addr_to_string(&arbaddr[i],(u08bits*)sss); //printf("%s: 111.111: %s\n",__FUNCTION__,sss); } turn_create_permission(verbose, clnet_info1, arbaddr, maxi); } if(!no_rtcp) if (turn_channel_bind(verbose, chn1_rtcp, clnet_info1_rtcp, &relay_addr2_rtcp) < 0) { exit(-1); } if(rare_event()) return 0; if (turn_channel_bind(verbose, chn2, clnet_info2, &relay_addr1) < 0) { exit(-1); } if(rare_event()) return 0; if(!no_rtcp) if (turn_channel_bind(verbose, chn2_rtcp, clnet_info2_rtcp, &relay_addr1_rtcp) < 0) { exit(-1); } if(rare_event()) return 0; } else { if (turn_create_permission(verbose, clnet_info1, &relay_addr2, 1) < 0) { exit(-1); } if(extra_requests) { const char *sarbaddr = "64.56.78.90"; if(random() % 2 == 0) sarbaddr = "2001::172"; ioa_addr arbaddr; make_ioa_addr((const u08bits*)sarbaddr, 333, &arbaddr); int i; int maxi = (unsigned short)random() % EXTRA_CREATE_PERMS; for(i=0;iis_peer)) { if (turn_create_permission(verbose, clnet_info2, &relay_addr1, 1) < 0) { exit(-1); } if(rare_event()) return 0; } if (!no_rtcp) if (turn_create_permission(verbose, clnet_info2_rtcp, &relay_addr1_rtcp, 1) < 0) { exit(-1); } if(rare_event()) return 0; } addr_cpy(&(clnet_info1->peer_addr), &relay_addr2); if(!no_rtcp) addr_cpy(&(clnet_info1_rtcp->peer_addr), &relay_addr2_rtcp); addr_cpy(&(clnet_info2->peer_addr), &relay_addr1); if(!no_rtcp) addr_cpy(&(clnet_info2_rtcp->peer_addr), &relay_addr1_rtcp); return 0; } //////////// RFC 6062 /////////////// int turn_tcp_connect(int verbose, app_ur_conn_info *clnet_info, ioa_addr *peer_addr) { { int cp_sent = 0; stun_buffer message; stun_init_request(STUN_METHOD_CONNECT, &message); stun_attr_add_addr(&message, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, peer_addr); add_origin(&message); if(add_integrity(clnet_info, &message)<0) return -1; stun_attr_add_fingerprint_str(message.buf,(size_t*)&(message.len)); while (!cp_sent) { int len = send_buffer(clnet_info, &message, 0,0); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "tcp connect sent\n"); } cp_sent = 1; } else { perror("send"); exit(1); } } } ////////////<<==connect send return 0; } static int turn_tcp_connection_bind(int verbose, app_ur_conn_info *clnet_info, app_tcp_conn_info *atc, int errorOK) { stun_buffer request_message, response_message; beg_cb: { int cb_sent = 0; u32bits cid = atc->cid; stun_init_request(STUN_METHOD_CONNECTION_BIND, &request_message); stun_attr_add(&request_message, STUN_ATTRIBUTE_CONNECTION_ID, (const s08bits*)&cid,4); add_origin(&request_message); if(add_integrity(clnet_info, &request_message)<0) return -1; stun_attr_add_fingerprint_str(request_message.buf,(size_t*)&(request_message.len)); while (!cb_sent) { int len = send_buffer(clnet_info, &request_message, 1, atc); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "connection bind sent\n"); } cb_sent = 1; } else { if(errorOK) return 0; perror("send"); exit(1); } } } ////////////<<==connection bind send if(not_rare_event()) return 0; ////////connection bind response==>> { int cb_received = 0; while (!cb_received) { int len = recv_buffer(clnet_info, &response_message, 1, 1, atc, &request_message); if (len > 0) { if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "connect bind response received: \n"); } int err_code = 0; u08bits err_msg[129]; if (stun_is_success_response(&response_message)) { if(clnet_info->nonce[0]) { if(check_integrity(clnet_info, &response_message)<0) return -1; } if(stun_get_method(&response_message)!=STUN_METHOD_CONNECTION_BIND) continue; cb_received = 1; if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "success\n"); } atc->tcp_data_bound = 1; } else if (stun_is_challenge_response_str(response_message.buf, (size_t)response_message.len, &err_code,err_msg,sizeof(err_msg), clnet_info->realm,clnet_info->nonce, clnet_info->server_name, &(clnet_info->oauth))) { goto beg_cb; } else if (stun_is_error_response(&response_message, &err_code,err_msg,sizeof(err_msg))) { cb_received = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "connection bind error %d (%s)\n", err_code,(char*)err_msg); return -1; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "unknown connection bind response\n"); /* Try again ? */ } } else { if(errorOK) return 0; perror("recv"); exit(-1); } } } return 0; } void tcp_data_connect(app_ur_session *elem, u32bits cid) { int clnet_fd; int connect_cycle = 0; again: clnet_fd = socket(elem->pinfo.remote_addr.ss.sa_family, CLIENT_STREAM_SOCKET_TYPE, CLIENT_STREAM_SOCKET_PROTOCOL); if (clnet_fd < 0) { perror("socket"); exit(-1); } if (sock_bind_to_device(clnet_fd, client_ifname) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Cannot bind client socket to device %s\n", client_ifname); } set_sock_buf_size(clnet_fd, (UR_CLIENT_SOCK_BUF_SIZE<<2)); ++elem->pinfo.tcp_conn_number; int i = (int)(elem->pinfo.tcp_conn_number-1); elem->pinfo.tcp_conn=(app_tcp_conn_info**)turn_realloc(elem->pinfo.tcp_conn,0,elem->pinfo.tcp_conn_number*sizeof(app_tcp_conn_info*)); elem->pinfo.tcp_conn[i]=(app_tcp_conn_info*)turn_malloc(sizeof(app_tcp_conn_info)); ns_bzero(elem->pinfo.tcp_conn[i],sizeof(app_tcp_conn_info)); elem->pinfo.tcp_conn[i]->tcp_data_fd = clnet_fd; elem->pinfo.tcp_conn[i]->cid = cid; addr_cpy(&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr),&(elem->pinfo.local_addr)); addr_set_port(&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr),0); addr_bind(clnet_fd, &(elem->pinfo.tcp_conn[i]->tcp_data_local_addr), 1, 1, TCP_SOCKET); addr_get_from_sock(clnet_fd,&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr)); { int cycle = 0; while(cycle++<1024) { int err = 0; if (addr_connect(clnet_fd, &(elem->pinfo.remote_addr),&err) < 0) { if(err == EADDRINUSE) { socket_closesocket(clnet_fd); clnet_fd = socket(elem->pinfo.remote_addr.ss.sa_family, CLIENT_STREAM_SOCKET_TYPE, CLIENT_STREAM_SOCKET_PROTOCOL); if (clnet_fd < 0) { perror("socket"); exit(-1); } if (sock_bind_to_device(clnet_fd, client_ifname) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Cannot bind client socket to device %s\n", client_ifname); } set_sock_buf_size(clnet_fd, UR_CLIENT_SOCK_BUF_SIZE<<2); elem->pinfo.tcp_conn[i]->tcp_data_fd = clnet_fd; addr_cpy(&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr),&(elem->pinfo.local_addr)); addr_set_port(&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr),0); addr_bind(clnet_fd, &(elem->pinfo.tcp_conn[i]->tcp_data_local_addr),1,1,TCP_SOCKET); addr_get_from_sock(clnet_fd,&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr)); continue; } else { perror("connect"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot connect to remote addr\n", __FUNCTION__); exit(-1); } } else { break; } } } if(use_secure) { int try_again = 0; elem->pinfo.tcp_conn[i]->tcp_data_ssl = tls_connect(elem->pinfo.tcp_conn[i]->tcp_data_fd, &(elem->pinfo.remote_addr),&try_again, connect_cycle++); if(!(elem->pinfo.tcp_conn[i]->tcp_data_ssl)) { if(try_again) { --elem->pinfo.tcp_conn_number; goto again; } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot SSL connect to remote addr\n", __FUNCTION__); exit(-1); } } if(turn_tcp_connection_bind(clnet_verbose, &(elem->pinfo), elem->pinfo.tcp_conn[i],0)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot BIND to tcp connection\n", __FUNCTION__); } else { socket_set_nonblocking(clnet_fd); struct event* ev = event_new(client_event_base,clnet_fd, EV_READ|EV_PERSIST,client_input_handler, elem); event_add(ev,NULL); elem->input_tcp_data_ev = ev; addr_debug_print(clnet_verbose, &(elem->pinfo.remote_addr), "TCP data network connected to"); } } ///////////////////////////////////////////////// turnserver-4.5.1.1/src/apps/relay/0000775000175000017500000000000013436577655015460 5ustar misimisiturnserver-4.5.1.1/src/apps/relay/userdb.c0000664000175000017500000007776513436577655017136 0ustar misimisi/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "userdb.h" #include "dbdrivers/dbdriver.h" #include "mainrelay.h" #include "ns_turn_utils.h" #include "ns_turn_server.h" #include "ns_turn_maps.h" #include "apputils.h" //////////// REALM ////////////// static realm_params_t *default_realm_params_ptr = NULL; static ur_string_map *realms = NULL; static turn_mutex o_to_realm_mutex; static ur_string_map *o_to_realm = NULL; static secrets_list_t realms_list; void lock_realms(void) { ur_string_map_lock(realms); } void unlock_realms(void) { ur_string_map_unlock(realms); } void update_o_to_realm(ur_string_map * o_to_realm_new) { TURN_MUTEX_LOCK(&o_to_realm_mutex); ur_string_map_free(&o_to_realm); o_to_realm = o_to_realm_new; TURN_MUTEX_UNLOCK(&o_to_realm_mutex); } void create_default_realm() { if(default_realm_params_ptr) { return; } static realm_params_t _default_realm_params = { 1, { "\0", /* name */ {0,0,0} }, {0,NULL} }; /* init everything: */ TURN_MUTEX_INIT_RECURSIVE(&o_to_realm_mutex); init_secrets_list(&realms_list); o_to_realm = ur_string_map_create(turn_free_simple); default_realm_params_ptr = &_default_realm_params; realms = ur_string_map_create(NULL); lock_realms(); default_realm_params_ptr->status.alloc_counters = ur_string_map_create(NULL); unlock_realms(); } void get_default_realm_options(realm_options_t* ro) { if(ro) { lock_realms(); ns_bcopy(&(default_realm_params_ptr->options),ro,sizeof(realm_options_t)); unlock_realms(); } } void set_default_realm_name(char *realm) { lock_realms(); ur_string_map_value_type value = (ur_string_map_value_type)default_realm_params_ptr; STRCPY(default_realm_params_ptr->options.name,realm); ur_string_map_put(realms, (ur_string_map_key_type)default_realm_params_ptr->options.name, value); add_to_secrets_list(&realms_list, realm); unlock_realms(); } realm_params_t* get_realm(char* name) { if(name && name[0]) { lock_realms(); ur_string_map_value_type value = 0; ur_string_map_key_type key = (ur_string_map_key_type)name; if (ur_string_map_get(realms, key, &value)) { unlock_realms(); return (realm_params_t*)value; } else { realm_params_t *ret = (realm_params_t*)turn_malloc(sizeof(realm_params_t)); ns_bcopy(default_realm_params_ptr,ret,sizeof(realm_params_t)); STRCPY(ret->options.name,name); value = (ur_string_map_value_type)ret; ur_string_map_put(realms, key, value); ret->status.alloc_counters = ur_string_map_create(NULL); add_to_secrets_list(&realms_list, name); unlock_realms(); return ret; } } return default_realm_params_ptr; } int get_realm_data(char* name, realm_params_t* rp) { lock_realms(); ns_bcopy(get_realm(name),rp,sizeof(realm_params_t)); unlock_realms(); return 0; } int get_realm_options_by_origin(char *origin, realm_options_t* ro) { ur_string_map_value_type value = 0; TURN_MUTEX_LOCK(&o_to_realm_mutex); if (ur_string_map_get(o_to_realm, (ur_string_map_key_type) origin, &value) && value) { char *realm = turn_strdup((char*)value); TURN_MUTEX_UNLOCK(&o_to_realm_mutex); realm_params_t rp; get_realm_data(realm, &rp); ns_bcopy(&(rp.options),ro,sizeof(realm_options_t)); turn_free(realm,strlen(realm)+1); return 1; } else { TURN_MUTEX_UNLOCK(&o_to_realm_mutex); get_default_realm_options(ro); return 0; } } void get_realm_options_by_name(char *realm, realm_options_t* ro) { realm_params_t rp; get_realm_data(realm, &rp); ns_bcopy(&(rp.options),ro,sizeof(realm_options_t)); } int change_total_quota(char *realm, int value) { int ret = value; lock_realms(); realm_params_t* rp = get_realm(realm); rp->options.perf_options.total_quota = value; unlock_realms(); return ret; } int change_user_quota(char *realm, int value) { int ret = value; lock_realms(); realm_params_t* rp = get_realm(realm); rp->options.perf_options.user_quota = value; unlock_realms(); return ret; } static void must_set_admin_realm(void *realm0) { char* realm = (char*)realm0; if(!realm || !realm[0]) { fprintf(stderr, "The operation cannot be completed: the realm must be set.\n"); exit(-1); } } static void must_set_admin_user(void *user0) { char* user = (char*)user0; if(!user || !user[0]) { fprintf(stderr, "The operation cannot be completed: the user must be set.\n"); exit(-1); } } static void must_set_admin_pwd(void *pwd0) { char* pwd = (char*)pwd0; if(!pwd || !pwd[0]) { fprintf(stderr, "The operation cannot be completed: the password must be set.\n"); exit(-1); } } static void must_set_admin_origin(void *origin0) { char* origin = (char*)origin0; if(!origin || !origin[0]) { fprintf(stderr, "The operation cannot be completed: the origin must be set.\n"); exit(-1); } } /////////// SHARED SECRETS ///////////////// void init_secrets_list(secrets_list_t *sl) { if(sl) { ns_bzero(sl,sizeof(secrets_list_t)); } } void clean_secrets_list(secrets_list_t *sl) { if(sl) { if(sl->secrets) { size_t i = 0; for(i = 0;isz;++i) { if(sl->secrets[i]) { turn_free(sl->secrets[i], strlen(sl->secrets[i])+1); } } turn_free(sl->secrets,(sl->sz)*sizeof(char*)); sl->secrets = NULL; sl->sz = 0; } } } size_t get_secrets_list_size(secrets_list_t *sl) { if(sl && sl->secrets) { return sl->sz; } return 0; } const char* get_secrets_list_elem(secrets_list_t *sl, size_t i) { if(get_secrets_list_size(sl)>i) { return sl->secrets[i]; } return NULL; } void add_to_secrets_list(secrets_list_t *sl, const char* elem) { if(sl && elem) { sl->secrets = (char**)turn_realloc(sl->secrets,0,(sizeof(char*)*(sl->sz+1))); sl->secrets[sl->sz] = turn_strdup(elem); sl->sz += 1; } } //////////////////////////////////////////// static int get_auth_secrets(secrets_list_t *sl, u08bits *realm) { int ret = -1; const turn_dbdriver_t * dbd = get_dbdriver(); clean_secrets_list(sl); if(get_secrets_list_size(&turn_params.default_users_db.ram_db.static_auth_secrets)) { size_t i = 0; for(i=0;iget_auth_secrets) { ret = (*dbd->get_auth_secrets)(sl, realm); } return ret; } /* * Timestamp retrieval */ static turn_time_t get_rest_api_timestamp(char *usname) { turn_time_t ts = 0; int ts_set = 0; char *col = strchr(usname,turn_params.rest_api_separator); if(col) { if(col == usname) { usname +=1; } else { char *ptr = usname; int found_non_figure = 0; while(ptr < col) { if(!(ptr[0]>='0' && ptr[0]<='9')) { found_non_figure=1; break; } ++ptr; } if(found_non_figure) { ts = (turn_time_t)atol(col+1); ts_set = 1; } else { *col=0; ts = (turn_time_t)atol(usname); ts_set = 1; *col=turn_params.rest_api_separator; } } } if(!ts_set) { ts = (turn_time_t)atol(usname); } return ts; } static char *get_real_username(char *usname) { if(usname[0] && turn_params.use_auth_secret_with_timestamp) { char *col=strchr(usname,turn_params.rest_api_separator); if(col) { if(col == usname) { usname +=1; } else { char *ptr = usname; int found_non_figure = 0; while(ptr < col) { if(!(ptr[0]>='0' && ptr[0]<='9')) { found_non_figure=1; break; } ++ptr; } if(!found_non_figure) { usname = col+1; } else { *col=0; usname = turn_strdup(usname); *col=turn_params.rest_api_separator; return usname; } } } } return turn_strdup(usname); } /* * Password retrieval */ int get_user_key(int in_oauth, int *out_oauth, int *max_session_time, u08bits *usname, u08bits *realm, hmackey_t key, ioa_network_buffer_handle nbh) { int ret = -1; if(max_session_time) *max_session_time = 0; if(in_oauth && out_oauth && usname && usname[0]) { stun_attr_ref sar = stun_attr_get_first_by_type_str(ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh), STUN_ATTRIBUTE_OAUTH_ACCESS_TOKEN); if(sar) { int len = stun_attr_get_len(sar); const u08bits *value = stun_attr_get_value(sar); *out_oauth = 1; if(len>0 && value) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->get_oauth_key) { oauth_key_data_raw rawKey; ns_bzero(&rawKey,sizeof(rawKey)); int gres = (*(dbd->get_oauth_key))(usname,&rawKey); if(gres<0) return ret; if(!rawKey.kid[0]) return ret; if(rawKey.lifetime) { if(!turn_time_before(turn_time(),(turn_time_t)(rawKey.timestamp + rawKey.lifetime+OAUTH_TIME_DELTA))) { return ret; } } oauth_key_data okd; ns_bzero(&okd,sizeof(okd)); convert_oauth_key_data_raw(&rawKey, &okd); char err_msg[1025] = "\0"; size_t err_msg_size = sizeof(err_msg) - 1; oauth_key okey; ns_bzero(&okey,sizeof(okey)); if (convert_oauth_key_data(&okd, &okey, err_msg, err_msg_size) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s\n", err_msg); return -1; } oauth_token dot; ns_bzero((&dot),sizeof(dot)); encoded_oauth_token etoken; ns_bzero(&etoken,sizeof(etoken)); if((size_t)len > sizeof(etoken.token)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Encoded oAuth token is too large\n"); return -1; } ns_bcopy(value,etoken.token,(size_t)len); etoken.size = (size_t)len; const char* server_name = (char*)turn_params.oauth_server_name; if(!(server_name && server_name[0])) { server_name = (char*)realm; if(!(server_name && server_name[0])) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot determine oAuth server name"); return -1; } } if (decode_oauth_token((const u08bits *) server_name, &etoken,&okey, &dot) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot decode oauth token\n"); return -1; } switch(dot.enc_block.key_length) { case SHA1SIZEBYTES: break; case SHA256SIZEBYTES: case SHA384SIZEBYTES: case SHA512SIZEBYTES: default: TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong size of the MAC key in oAuth token(3): %d\n",(int)dot.enc_block.key_length); return -1; }; password_t pwdtmp; if(stun_check_message_integrity_by_key_str(TURN_CREDENTIALS_LONG_TERM, ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh), dot.enc_block.mac_key, pwdtmp, SHATYPE_DEFAULT)>0) { turn_time_t lifetime = (turn_time_t)(dot.enc_block.lifetime); if(lifetime) { turn_time_t ts = (turn_time_t)(dot.enc_block.timestamp >> 16); turn_time_t to = ts + lifetime + OAUTH_TIME_DELTA; turn_time_t ct = turn_time(); if(!turn_time_before(ct,to)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "oAuth token is too old\n"); return -1; } if(max_session_time) { *max_session_time = to - ct; } } ns_bcopy(dot.enc_block.mac_key,key,dot.enc_block.key_length); if(rawKey.realm[0]) { ns_bcopy(rawKey.realm,realm,sizeof(rawKey.realm)); } ret = 0; } } } } } if(out_oauth && *out_oauth) { return ret; } if(turn_params.use_auth_secret_with_timestamp) { turn_time_t ctime = (turn_time_t) time(NULL); turn_time_t ts = 0; secrets_list_t sl; size_t sll = 0; init_secrets_list(&sl); if(get_auth_secrets(&sl, realm)<0) return ret; ts = get_rest_api_timestamp((char*)usname); if(!turn_time_before(ts, ctime)) { u08bits hmac[MAXSHASIZE]; unsigned int hmac_len; password_t pwdtmp; hmac[0] = 0; stun_attr_ref sar = stun_attr_get_first_by_type_str(ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh), STUN_ATTRIBUTE_MESSAGE_INTEGRITY); if (!sar) return -1; int sarlen = stun_attr_get_len(sar); switch(sarlen) { case SHA1SIZEBYTES: hmac_len = SHA1SIZEBYTES; break; case SHA256SIZEBYTES: case SHA384SIZEBYTES: case SHA512SIZEBYTES: default: return -1; }; for(sll=0;sll=0) { size_t pwd_length = 0; char *pwd = base64_encode(hmac,hmac_len,&pwd_length); if(pwd) { if(pwd_length<1) { turn_free(pwd,strlen(pwd)+1); } else { if(stun_produce_integrity_key_str((u08bits*)usname, realm, (u08bits*)pwd, key, SHATYPE_DEFAULT)>=0) { if(stun_check_message_integrity_by_key_str(TURN_CREDENTIALS_LONG_TERM, ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh), key, pwdtmp, SHATYPE_DEFAULT)>0) { ret = 0; } } turn_free(pwd,pwd_length); if(ret==0) break; } } } } } } clean_secrets_list(&sl); return ret; } ur_string_map_value_type ukey = NULL; ur_string_map_lock(turn_params.default_users_db.ram_db.static_accounts); if(ur_string_map_get(turn_params.default_users_db.ram_db.static_accounts, (ur_string_map_key_type)usname, &ukey)) { ret = 0; } ur_string_map_unlock(turn_params.default_users_db.ram_db.static_accounts); if(ret==0) { size_t sz = get_hmackey_size(SHATYPE_DEFAULT); ns_bcopy(ukey,key,sz); return 0; } const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->get_user_key) { ret = (*(dbd->get_user_key))(usname, realm, key); } return ret; } u08bits *start_user_check(turnserver_id id, turn_credential_type ct, int in_oauth, int *out_oauth, u08bits *usname, u08bits *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, u64bits ctxkey, int *postpone_reply) { *postpone_reply = 1; struct auth_message am; ns_bzero(&am,sizeof(struct auth_message)); am.id = id; am.ct = ct; am.in_oauth = in_oauth; am.out_oauth = *out_oauth; STRCPY(am.username,usname); STRCPY(am.realm,realm); am.resume_func = resume; memcpy(&(am.in_buffer),in_buffer,sizeof(ioa_net_data)); in_buffer->nbh = NULL; am.ctxkey = ctxkey; send_auth_message_to_auth_server(&am); return NULL; } int check_new_allocation_quota(u08bits *user, int oauth, u08bits *realm) { int ret = 0; if (user || oauth) { u08bits *username = oauth ? (u08bits*)turn_strdup("") : (u08bits*)get_real_username((char*)user); realm_params_t *rp = get_realm((char*)realm); ur_string_map_lock(rp->status.alloc_counters); if (rp->options.perf_options.total_quota && (rp->status.total_current_allocs >= rp->options.perf_options.total_quota)) { ret = -1; } else if(username[0]){ ur_string_map_value_type value = 0; if (!ur_string_map_get(rp->status.alloc_counters, (ur_string_map_key_type) username, &value)) { value = (ur_string_map_value_type) 1; ur_string_map_put(rp->status.alloc_counters, (ur_string_map_key_type) username, value); ++(rp->status.total_current_allocs); } else { if ((rp->options.perf_options.user_quota) && ((size_t) value >= (size_t)(rp->options.perf_options.user_quota))) { ret = -1; } else { value = (ur_string_map_value_type)(((size_t)value) + 1); ur_string_map_put(rp->status.alloc_counters, (ur_string_map_key_type) username, value); ++(rp->status.total_current_allocs); } } } else { ++(rp->status.total_current_allocs); } turn_free(username,strlen((char*)username)+1); ur_string_map_unlock(rp->status.alloc_counters); } return ret; } void release_allocation_quota(u08bits *user, int oauth, u08bits *realm) { if (user) { u08bits *username = oauth ? (u08bits*)turn_strdup("") : (u08bits*)get_real_username((char*)user); realm_params_t *rp = get_realm((char*)realm); ur_string_map_lock(rp->status.alloc_counters); if(username[0]) { ur_string_map_value_type value = 0; ur_string_map_get(rp->status.alloc_counters, (ur_string_map_key_type) username, &value); if (value) { value = (ur_string_map_value_type)(((size_t)value) - 1); ur_string_map_put(rp->status.alloc_counters, (ur_string_map_key_type) username, value); } } if (rp->status.total_current_allocs) --(rp->status.total_current_allocs); ur_string_map_unlock(rp->status.alloc_counters); turn_free(username, strlen((char*)username)+1); } } ////////////////////////////////// int add_static_user_account(char *user) { /* Realm is either default or empty for users taken from file or command-line */ if(user && !turn_params.use_auth_secret_with_timestamp) { char *s = strstr(user, ":"); if(!s || (s==user) || (strlen(s)<2)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user account: %s\n",user); } else { size_t ulen = s-user; char *usname = (char*)turn_malloc(sizeof(char)*(ulen+1)); strncpy(usname,user,ulen); usname[ulen]=0; if(SASLprep((u08bits*)usname)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user name: %s\n",user); turn_free(usname,sizeof(char)*(ulen+1)); return -1; } s = skip_blanks(s+1); hmackey_t *key = (hmackey_t*)turn_malloc(sizeof(hmackey_t)); if(strstr(s,"0x")==s) { char *keysource = s + 2; size_t sz = get_hmackey_size(SHATYPE_DEFAULT); if(strlen(keysource)options.name, (u08bits*)s, *key, SHATYPE_DEFAULT); } { ur_string_map_lock(turn_params.default_users_db.ram_db.static_accounts); ur_string_map_put(turn_params.default_users_db.ram_db.static_accounts, (ur_string_map_key_type)usname, (ur_string_map_value_type)*key); ur_string_map_unlock(turn_params.default_users_db.ram_db.static_accounts); } turn_params.default_users_db.ram_db.users_number++; turn_free(usname,strlen(usname)+1); return 0; } } return -1; } ////////////////// Admin ///////////////////////// static int list_users(u08bits *realm, int is_admin) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd) { if(is_admin) { if(dbd->list_admin_users) { (*dbd->list_admin_users)(0); } } else { if(dbd->list_users) { (*dbd->list_users)(realm,NULL,NULL); } } } return 0; } static int show_secret(u08bits *realm) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->list_secrets) { (*dbd->list_secrets)(realm,NULL,NULL); } return 0; } static int del_secret(u08bits *secret, u08bits *realm) { must_set_admin_realm(realm); const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->del_secret) { (*dbd->del_secret)(secret, realm); } return 0; } static int set_secret(u08bits *secret, u08bits *realm) { if(!secret || (secret[0]==0)) return 0; must_set_admin_realm(realm); del_secret(secret, realm); const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->set_secret) { (*dbd->set_secret)(secret, realm); } return 0; } static int add_origin(u08bits *origin0, u08bits *realm) { u08bits origin[STUN_MAX_ORIGIN_SIZE+1]; get_canonic_origin((const char *)origin0, (char *)origin, sizeof(origin)-1); const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->add_origin) { (*dbd->add_origin)(origin, realm); } return 0; } static int del_origin(u08bits *origin0) { u08bits origin[STUN_MAX_ORIGIN_SIZE+1]; get_canonic_origin((const char *)origin0, (char *)origin, sizeof(origin)-1); const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->del_origin) { (*dbd->del_origin)(origin); } return 0; } static int list_origins(u08bits *realm) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->list_origins) { (*dbd->list_origins)(realm,NULL,NULL); } return 0; } static int set_realm_option_one(u08bits *realm, unsigned long value, const char* opt) { if(value == (unsigned long)-1) return 0; const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->set_realm_option_one) { (*dbd->set_realm_option_one)(realm, value, opt); } return 0; } static int set_realm_option(u08bits *realm, perf_options_t *po) { set_realm_option_one(realm,(unsigned long)po->max_bps,"max-bps"); set_realm_option_one(realm,(unsigned long)po->user_quota,"user-quota"); set_realm_option_one(realm,(unsigned long)po->total_quota,"total-quota"); return 0; } static int list_realm_options(u08bits *realm) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->list_realm_options) { (*dbd->list_realm_options)(realm); } return 0; } int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08bits *origin, TURNADMIN_COMMAND_TYPE ct, perf_options_t *po, int is_admin) { hmackey_t key; char skey[sizeof(hmackey_t) * 2 + 1]; if (ct == TA_LIST_USERS) { return list_users(realm, is_admin); } if (ct == TA_LIST_ORIGINS) { return list_origins(realm); } if (ct == TA_SHOW_SECRET) { return show_secret(realm); } if (ct == TA_SET_SECRET) { return set_secret(secret, realm); } if (ct == TA_DEL_SECRET) { return del_secret(secret, realm); } if (ct == TA_ADD_ORIGIN) { must_set_admin_origin(origin); must_set_admin_realm(realm); return add_origin(origin, realm); } if (ct == TA_DEL_ORIGIN) { must_set_admin_origin(origin); return del_origin(origin); } if (ct == TA_SET_REALM_OPTION) { must_set_admin_realm(realm); if (!(po && (po->max_bps != ((band_limit_t) -1) || po->total_quota >= 0 || po->user_quota >= 0))) { fprintf(stderr, "The operation cannot be completed: a realm option must be set.\n"); exit(-1); } return set_realm_option(realm, po); } if (ct == TA_LIST_REALM_OPTIONS) { return list_realm_options(realm); } must_set_admin_user(user); if (ct != TA_DELETE_USER && !is_admin) { must_set_admin_pwd(pwd); { stun_produce_integrity_key_str(user, realm, pwd, key, SHATYPE_DEFAULT); size_t i = 0; size_t sz = get_hmackey_size(SHATYPE_DEFAULT); int maxsz = (int) (sz * 2) + 1; char *s = skey; for (i = 0; (i < sz) && (maxsz > 2); i++) { snprintf(s, (size_t) (sz * 2), "%02x", (unsigned int) key[i]); maxsz -= 2; s += 2; } skey[sz * 2] = 0; } } const turn_dbdriver_t * dbd = get_dbdriver(); if (ct == TA_PRINT_KEY) { printf("0x%s\n", skey); } else if (dbd) { if(!is_admin) must_set_admin_realm(realm); if (ct == TA_DELETE_USER) { if(is_admin) { if (dbd->del_admin_user) (*dbd->del_admin_user)(user); } else { if (dbd->del_user) (*dbd->del_user)(user, realm); } } else if (ct == TA_UPDATE_USER) { if(is_admin) { must_set_admin_pwd(pwd); if (dbd->set_admin_user) { password_t password; generate_new_enc_password((char*)pwd,(char*)password); (*dbd->set_admin_user)(user, realm, password); } } else { if (dbd->set_user_key) (*dbd->set_user_key)(user, realm, skey); } } } return 0; } /////////// PING ////////////// void auth_ping(redis_context_handle rch) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->auth_ping) { (*dbd->auth_ping)(rch); } } ///////////////// TEST ///////////////// #if defined(DB_TEST) void run_db_test(void) { turn_dbdriver_t * dbd = get_dbdriver(); if (dbd) { printf("DB TEST 1:\n"); dbd->list_oauth_keys(); printf("DB TEST 2:\n"); oauth_key_data_raw key_; oauth_key_data_raw *key=&key_; dbd->get_oauth_key((const u08bits*)"north",key); printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s\n", key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->as_rs_alg); printf("DB TEST 3:\n"); STRCPY(key->as_rs_alg,"as_rs_alg"); STRCPY(key->ikm_key,"ikm_key"); STRCPY(key->kid,"kid"); key->timestamp = 123; key->lifetime = 456; dbd->del_oauth_key((const u08bits*)"kid"); dbd->set_oauth_key(key); dbd->list_oauth_keys(); printf("DB TEST 4:\n"); dbd->get_oauth_key((const u08bits*)"kid",key); printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s\n", key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->as_rs_alg); printf("DB TEST 5:\n"); dbd->del_oauth_key((const u08bits*)"kid"); dbd->list_oauth_keys(); printf("DB TEST 6:\n"); dbd->get_oauth_key((const u08bits*)"north",key); oauth_key_data oakd; convert_oauth_key_data_raw(key, &oakd); printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s\n", oakd.kid, oakd.ikm_key, (unsigned long long)oakd.timestamp, (unsigned long)oakd.lifetime, oakd.as_rs_alg); oauth_key oak; char err_msg[1025]; err_msg[0]=0; if(convert_oauth_key_data(&oakd,&oak,err_msg,sizeof(err_msg)-1)<0) { printf(" ERROR: %s\n",err_msg); } else { printf(" OK!\n"); } printf("DB TEST END\n"); } } #endif ///////////////// WHITE/BLACK IP LISTS /////////////////// #if !defined(TURN_NO_RWLOCK) static pthread_rwlock_t* whitelist_rwlock = NULL; static pthread_rwlock_t* blacklist_rwlock = NULL; #else static turn_mutex whitelist_mutex; static turn_mutex blacklist_mutex; #endif static ip_range_list_t* ipwhitelist = NULL; static ip_range_list_t* ipblacklist = NULL; void init_dynamic_ip_lists(void) { #if !defined(TURN_NO_RWLOCK) whitelist_rwlock = (pthread_rwlock_t*) turn_malloc(sizeof(pthread_rwlock_t)); pthread_rwlock_init(whitelist_rwlock, NULL); blacklist_rwlock = (pthread_rwlock_t*) turn_malloc(sizeof(pthread_rwlock_t)); pthread_rwlock_init(blacklist_rwlock, NULL); #else turn_mutex_init(&whitelist_mutex); turn_mutex_init(&blacklist_mutex); #endif } void ioa_lock_whitelist(ioa_engine_handle e) { UNUSED_ARG(e); #if !defined(TURN_NO_RWLOCK) pthread_rwlock_rdlock(whitelist_rwlock); #else turn_mutex_lock(&whitelist_mutex); #endif } void ioa_unlock_whitelist(ioa_engine_handle e) { UNUSED_ARG(e); #if !defined(TURN_NO_RWLOCK) pthread_rwlock_unlock(whitelist_rwlock); #else turn_mutex_unlock(&whitelist_mutex); #endif } static void ioa_wrlock_whitelist(ioa_engine_handle e) { UNUSED_ARG(e); #if !defined(TURN_NO_RWLOCK) pthread_rwlock_wrlock(whitelist_rwlock); #else turn_mutex_lock(&whitelist_mutex); #endif } const ip_range_list_t* ioa_get_whitelist(ioa_engine_handle e) { UNUSED_ARG(e); return ipwhitelist; } void ioa_lock_blacklist(ioa_engine_handle e) { UNUSED_ARG(e); #if !defined(TURN_NO_RWLOCK) pthread_rwlock_rdlock(blacklist_rwlock); #else turn_mutex_lock(&blacklist_mutex); #endif } void ioa_unlock_blacklist(ioa_engine_handle e) { UNUSED_ARG(e); #if !defined(TURN_NO_RWLOCK) pthread_rwlock_unlock(blacklist_rwlock); #else turn_mutex_unlock(&blacklist_mutex); #endif } static void ioa_wrlock_blacklist(ioa_engine_handle e) { UNUSED_ARG(e); #if !defined(TURN_NO_RWLOCK) pthread_rwlock_wrlock(blacklist_rwlock); #else turn_mutex_lock(&blacklist_mutex); #endif } const ip_range_list_t* ioa_get_blacklist(ioa_engine_handle e) { UNUSED_ARG(e); return ipblacklist; } ip_range_list_t* get_ip_list(const char *kind) { ip_range_list_t *ret = (ip_range_list_t*) turn_malloc(sizeof(ip_range_list_t)); ns_bzero(ret,sizeof(ip_range_list_t)); const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->get_ip_list) { (*dbd->get_ip_list)(kind, ret); } return ret; } void ip_list_free(ip_range_list_t *l) { if(l) { if(l->rs) turn_free(l->rs,l->ranges_number * sizeof(ip_range_t)); turn_free(l,sizeof(ip_range_list_t)); } } void update_white_and_black_lists(void) { { ip_range_list_t *wl = get_ip_list("allowed"); ip_range_list_t *owl = NULL; ioa_wrlock_whitelist(NULL); owl = ipwhitelist; ipwhitelist = wl; ioa_unlock_whitelist(NULL); ip_list_free(owl); } { ip_range_list_t *bl = get_ip_list("denied"); ip_range_list_t *obl = NULL; ioa_wrlock_blacklist(NULL); obl = ipblacklist; ipblacklist = bl; ioa_unlock_blacklist(NULL); ip_list_free(obl); } } /////////////// add ACL record /////////////////// int add_ip_list_range(const char * range0, const char * realm, ip_range_list_t * list) { char *range = turn_strdup(range0); char* separator = strchr(range, '-'); if (separator) { *separator = '\0'; } ioa_addr min, max; if (make_ioa_addr((const u08bits*) range, 0, &min) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address format: %s\n", range); turn_free(range,0); return -1; } if (separator) { if (make_ioa_addr((const u08bits*) separator + 1, 0, &max) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address format: %s\n", separator + 1); turn_free(range,0); return -1; } } else { // Doesn't have a '-' character in it, so assume that this is a single address addr_cpy(&max, &min); } if (separator) *separator = '-'; ++(list->ranges_number); list->rs = (ip_range_t*) turn_realloc(list->rs, 0, sizeof(ip_range_t) * list->ranges_number); STRCPY(list->rs[list->ranges_number - 1].str,range); if(realm) STRCPY(list->rs[list->ranges_number - 1].realm,realm); else list->rs[list->ranges_number - 1].realm[0]=0; turn_free(range,0); ioa_addr_range_set(&(list->rs[list->ranges_number - 1].enc), &min, &max); return 0; } int check_ip_list_range(const char * range0) { char *range = turn_strdup(range0); char* separator = strchr(range, '-'); if (separator) { *separator = '\0'; } ioa_addr min, max; if (make_ioa_addr((const u08bits*) range, 0, &min) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address range format: %s\n", range); turn_free(range,0); return -1; } if (separator) { if (make_ioa_addr((const u08bits*) separator + 1, 0, &max) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address range format: %s\n", separator + 1); turn_free(range,0); return -1; } } else { // Doesn't have a '-' character in it, so assume that this is a single address addr_cpy(&max, &min); } if (separator) *separator = '-'; turn_free(range,0); return 0; } /////////// REALM ////////////// void reread_realms(void) { { realm_params_t* defrp = get_realm(NULL); lock_realms(); defrp->options.perf_options.max_bps = turn_params.max_bps; defrp->options.perf_options.total_quota = turn_params.total_quota; defrp->options.perf_options.user_quota = turn_params.user_quota; unlock_realms(); } const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->reread_realms) { (*dbd->reread_realms)(&realms_list); } } /////////////////////////////// turnserver-4.5.1.1/src/apps/relay/ns_ioalib_engine_impl.c0000664000175000017500000027354013436577655022144 0ustar misimisi/* * 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_session.h" #include "ns_turn_server.h" #include "ns_turn_khash.h" #include "stun_buffer.h" #include "apputils.h" #include "ns_ioalib_impl.h" #if TLS_SUPPORTED #include #endif #include #include "ns_turn_openssl.h" #if !defined(TURN_NO_HIREDIS) #include "hiredis_libevent2.h" #endif #if !defined(TURN_NO_SCTP) && defined(TURN_SCTP_INCLUDE) #include TURN_SCTP_INCLUDE #endif /* Compilation test: #if defined(IP_RECVTTL) #undef IP_RECVTTL #endif #if defined(IPV6_RECVHOPLIMIT) #undef IPV6_RECVHOPLIMIT #endif #if defined(IP_RECVTOS) #undef IP_RECVTOS #endif #if defined(IPV6_RECVTCLASS) #undef IPV6_RECVTCLASS #endif */ #define MAX_ERRORS_IN_UDP_BATCH (1024) struct turn_sock_extended_err { uint32_t ee_errno; /* error number */ uint8_t ee_origin; /* where the error originated */ uint8_t ee_type; /* type */ uint8_t ee_code; /* code */ uint8_t ee_pad; /* padding */ uint32_t ee_info; /* additional information */ uint32_t ee_data; /* other data */ /* More data may follow */ }; #define TRIAL_EFFORTS_TO_SEND (2) #define SSL_MAX_RENEG_NUMBER (3) const int predef_timer_intervals[PREDEF_TIMERS_NUM] = {30,60,90,120,240,300,360,540,600,700,800,900,1800,3600}; /************** Forward function declarations ******/ static int socket_readerr(evutil_socket_t fd, ioa_addr *orig_addr); static void socket_input_handler(evutil_socket_t fd, short what, void* arg); static void socket_output_handler_bev(struct bufferevent *bev, void* arg); static void socket_input_handler_bev(struct bufferevent *bev, void* arg); static void eventcb_bev(struct bufferevent *bev, short events, void *arg); static int send_ssl_backlog_buffers(ioa_socket_handle s); static int set_accept_cb(ioa_socket_handle s, accept_cb acb, void *arg); static void close_socket_net_data(ioa_socket_handle s); /************** Utils **************************/ static const int tcp_congestion_control = 1; static int bufferevent_enabled(struct bufferevent *bufev, short flags) { return (bufferevent_get_enabled(bufev) & flags); } static int is_socket_writeable(ioa_socket_handle s, size_t sz, const char *msg, int option) { UNUSED_ARG(sz); UNUSED_ARG(msg); UNUSED_ARG(option); if (!s) return 0; if (!(s->done) && !(s->broken) && !(s->tobeclosed)) { switch (s->st){ case SCTP_SOCKET: case TLS_SCTP_SOCKET: case TCP_SOCKET: case TLS_SOCKET: if (s->bev) { struct evbuffer *evb = bufferevent_get_output(s->bev); if (evb) { size_t bufsz = evbuffer_get_length(evb); size_t newsz = bufsz + sz; switch (s->sat){ case TCP_CLIENT_DATA_SOCKET: case TCP_RELAY_DATA_SOCKET: switch (option){ case 0: case 1: if (newsz >= BUFFEREVENT_MAX_TCP_TO_TCP_WRITE) { return 0; } break; case 3: case 4: if (newsz >= BUFFEREVENT_MAX_TCP_TO_TCP_WRITE) { return 0; } break; default: return 1; } ; break; default: if (option == 2) { if (newsz >= BUFFEREVENT_MAX_UDP_TO_TCP_WRITE) { return 0; } } }; } } break; default: ; }; } return 1; } static void log_socket_event(ioa_socket_handle s, const char *msg, int error) { if(s && (error || (s->e && s->e->verbose))) { if(!msg) msg = "General socket event"; turnsession_id id = 0; { ts_ur_super_session *ss = s->session; if (ss) { id = ss->id; } else{ return; } } TURN_LOG_LEVEL ll = TURN_LOG_LEVEL_INFO; if(error) ll = TURN_LOG_LEVEL_ERROR; UNUSED_ARG(ll); { char sraddr[129]="\0"; char sladdr[129]="\0"; addr_to_string(&(s->remote_addr),(u08bits*)sraddr); addr_to_string(&(s->local_addr),(u08bits*)sladdr); if(EVUTIL_SOCKET_ERROR()) { TURN_LOG_FUNC(ll,"session %018llu: %s: %s (local %s, remote %s)\n",(unsigned long long)id, msg, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()), sladdr,sraddr); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: %s (local %s, remote %s)\n", (unsigned long long)id,msg,sladdr,sraddr); } } } } int set_df_on_ioa_socket(ioa_socket_handle s, int value) { if(!s) return 0; if(s->parent_s) return 0; if (s->do_not_use_df) value = 0; if (s->current_df_relay_flag != value) { s->current_df_relay_flag = value; return set_socket_df(s->fd, s->family, value); } return 0; } void set_do_not_use_df(ioa_socket_handle s) { if(s->parent_s) return; s->do_not_use_df = 1; s->current_df_relay_flag = 1; set_socket_df(s->fd, s->family, 0); } /************** Buffer List ********************/ static int buffer_list_empty(stun_buffer_list *bufs) { if(bufs && bufs->head && bufs->tsz) return 0; return 1; } static stun_buffer_list_elem *get_elem_from_buffer_list(stun_buffer_list *bufs) { stun_buffer_list_elem *ret = NULL; if(bufs && bufs->head && bufs->tsz) { ret=bufs->head; bufs->head=ret->next; --bufs->tsz; ret->next=NULL; ret->buf.len = 0; ret->buf.offset = 0; ret->buf.coffset = 0; } return ret; } static void pop_elem_from_buffer_list(stun_buffer_list *bufs) { if(bufs && bufs->head && bufs->tsz) { stun_buffer_list_elem *ret = bufs->head; bufs->head=ret->next; --bufs->tsz; turn_free(ret,sizeof(stun_buffer_list_elem)); } } static stun_buffer_list_elem *new_blist_elem(ioa_engine_handle e) { stun_buffer_list_elem *ret = get_elem_from_buffer_list(&(e->bufs)); if(!ret) { ret = (stun_buffer_list_elem *)turn_malloc(sizeof(stun_buffer_list_elem)); ret->buf.len = 0; ret->buf.offset = 0; ret->buf.coffset = 0; ret->next = NULL; } return ret; } static inline void add_elem_to_buffer_list(stun_buffer_list *bufs, stun_buffer_list_elem *buf_elem) { buf_elem->next = bufs->head; bufs->head = buf_elem; bufs->tsz += 1; } static void add_buffer_to_buffer_list(stun_buffer_list *bufs, s08bits *buf, size_t len) { if(bufs && buf && (bufs->tszbuf.buf,len); buf_elem->buf.len = len; buf_elem->buf.offset = 0; buf_elem->buf.coffset = 0; add_elem_to_buffer_list(bufs,buf_elem); } } static void free_blist_elem(ioa_engine_handle e, stun_buffer_list_elem *buf_elem) { if(buf_elem) { if(e && (e->bufs.tszbufs), buf_elem); } else { turn_free(buf_elem,sizeof(stun_buffer_list_elem)); } } } /************** ENGINE *************************/ static void timer_handler(ioa_engine_handle e, void* arg) { UNUSED_ARG(arg); _log_time_value = turn_time(); _log_time_value_set = 1; e->jiffie = _log_time_value; } ioa_engine_handle create_ioa_engine(super_memory_t *sm, struct event_base *eb, turnipports *tp, const s08bits* relay_ifname, size_t relays_number, s08bits **relay_addrs, int default_relays, int verbose #if !defined(TURN_NO_HIREDIS) ,const char* redis_report_connection_string #endif ) { static int capabilities_checked = 0; if(!capabilities_checked) { capabilities_checked = 1; #if !defined(CMSG_SPACE) TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "On this platform, I am using alternative behavior of TTL/TOS according to RFC 5766.\n"); #endif #if !defined(IP_RECVTTL) || !defined(IP_TTL) TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "IPv4: On this platform, I am using alternative behavior of TTL according to RFC 5766.\n"); #endif #if !defined(IPV6_RECVHOPLIMIT) || !defined(IPV6_HOPLIMIT) TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "IPv6: On this platform, I am using alternative behavior of TTL (HOPLIMIT) according to RFC 6156.\n"); #endif #if !defined(IP_RECVTOS) || !defined(IP_TOS) TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "IPv4: On this platform, I am using alternative behavior of TOS according to RFC 5766.\n"); #endif #if !defined(IPV6_RECVTCLASS) || !defined(IPV6_TCLASS) TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "IPv6: On this platform, I am using alternative behavior of TRAFFIC CLASS according to RFC 6156.\n"); #endif } if (!relays_number || !relay_addrs || !tp) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Cannot create TURN engine\n", __FUNCTION__); return NULL; } else { ioa_engine_handle e = (ioa_engine_handle)allocate_super_memory_region(sm, sizeof(ioa_engine)); e->sm = sm; e->default_relays = default_relays; e->verbose = verbose; e->tp = tp; if (eb) { e->event_base = eb; e->deallocate_eb = 0; } else { e->event_base = turn_event_base_new(); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"IO method (engine own thread): %s\n",event_base_get_method(e->event_base)); e->deallocate_eb = 1; } #if !defined(TURN_NO_HIREDIS) if(redis_report_connection_string && *redis_report_connection_string) { e->rch = get_redis_async_connection(e->event_base, redis_report_connection_string, 0); } #endif { int t; for(t=0;tevent_base, &duration); if(!ptv) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"FATAL: cannot create preferable timeval for %d secs (%d number)\n",predef_timer_intervals[t],t); exit(-1); } else { ns_bcopy(ptv,&(e->predef_timers[t]),sizeof(struct timeval)); e->predef_timer_intervals[t] = predef_timer_intervals[t]; } } } if (relay_ifname) STRCPY(e->relay_ifname, relay_ifname); { size_t i = 0; e->relay_addrs = (ioa_addr*)allocate_super_memory_region(sm, relays_number * sizeof(ioa_addr)+8); for (i = 0; i < relays_number; i++) { if(make_ioa_addr((u08bits*) relay_addrs[i], 0, &(e->relay_addrs[i]))<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot add a relay address: %s\n",relay_addrs[i]); } } e->relays_number = relays_number; } e->relay_addr_counter = (unsigned short) turn_random(); timer_handler(e,e); e->timer_ev = set_ioa_timer(e, 1, 0, timer_handler, e, 1, "timer_handler"); return e; } } void ioa_engine_set_rtcp_map(ioa_engine_handle e, rtcp_map *rtcpmap) { if(e) e->map_rtcp = rtcpmap; } static const ioa_addr* ioa_engine_get_relay_addr(ioa_engine_handle e, ioa_socket_handle client_s, int address_family, int *err_code) { if(e) { int family = AF_INET; if(address_family == STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6) family = AF_INET6; if(e->default_relays) { //No relay addrs defined - just return the client address if appropriate: ioa_addr *client_addr = get_local_addr_from_ioa_socket(client_s); if(client_addr) { switch(address_family) { case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4: if (client_addr->ss.sa_family == AF_INET) return client_addr; break; case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6: if (client_addr->ss.sa_family == AF_INET6) return client_addr; break; default: return client_addr; }; } } if (e->relays_number>0) { size_t i = 0; //Default recommended behavior: for(i=0; irelays_number; i++) { if(e->relay_addr_counter >= e->relays_number) e->relay_addr_counter = 0; ioa_addr *relay_addr = &(e->relay_addrs[e->relay_addr_counter++]); if(addr_any_no_port(relay_addr)) get_a_local_relay(family, relay_addr); switch (address_family){ case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT: case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4: if (relay_addr->ss.sa_family == AF_INET) return relay_addr; break; case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6: if (relay_addr->ss.sa_family == AF_INET6) return relay_addr; break; default: ; }; } if(address_family == STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT) { //Fallback to "find whatever is available": if(e->relay_addr_counter >= e->relays_number) e->relay_addr_counter = 0; const ioa_addr *relay_addr = &(e->relay_addrs[e->relay_addr_counter++]); return relay_addr; } *err_code = 440; } } return NULL; } /******************** Timers ****************************/ static void timer_event_handler(evutil_socket_t fd, short what, void* arg) { timer_event* te = (timer_event*)arg; if(!te) return; UNUSED_ARG(fd); if (!(what & EV_TIMEOUT)) return; if(te->e && eve(te->e->verbose)) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: timeout 0x%lx: %s\n", __FUNCTION__,(long)te, te->txt); ioa_timer_event_handler cb = te->cb; ioa_engine_handle e = te->e; void *ctx = te->ctx; cb(e, ctx); } ioa_timer_handle set_ioa_timer(ioa_engine_handle e, int secs, int ms, ioa_timer_event_handler cb, void* ctx, int persist, const s08bits *txt) { ioa_timer_handle ret = NULL; if (e && cb && secs > 0) { timer_event * te = (timer_event*) turn_malloc(sizeof(timer_event)); int flags = EV_TIMEOUT; if (persist) flags |= EV_PERSIST; struct event *ev = event_new(e->event_base, -1, flags, timer_event_handler, te); struct timeval tv; tv.tv_sec = secs; te->ctx = ctx; te->e = e; te->ev = ev; te->cb = cb; te->txt = turn_strdup(txt); if(!ms) { tv.tv_usec = 0; int found = 0; int t; for(t=0;tpredef_timer_intervals[t] == secs) { evtimer_add(ev,&(e->predef_timers[t])); found = 1; break; } } if(!found) { evtimer_add(ev,&tv); } } else { tv.tv_usec = ms * 1000; evtimer_add(ev,&tv); } ret = te; } return ret; } void stop_ioa_timer(ioa_timer_handle th) { if (th) { timer_event *te = (timer_event *)th; EVENT_DEL(te->ev); } } void delete_ioa_timer(ioa_timer_handle th) { if (th) { stop_ioa_timer(th); timer_event *te = (timer_event *)th; if(te->txt) { turn_free(te->txt,strlen(te->txt)+1); te->txt = NULL; } turn_free(th,sizeof(timer_event)); } } /************** SOCKETS HELPERS ***********************/ int ioa_socket_check_bandwidth(ioa_socket_handle s, ioa_network_buffer_handle nbh, int read) { if(s && (s->e) && nbh && ((s->sat == CLIENT_SOCKET) || (s->sat == RELAY_SOCKET) || (s->sat == RELAY_RTCP_SOCKET)) && (s->session)) { size_t sz = ioa_network_buffer_get_size(nbh); band_limit_t max_bps = s->session->bps; if(max_bps<1) return 1; struct traffic_bytes *traffic = &(s->data_traffic); if(s->sat == CLIENT_SOCKET) { u08bits *buf = ioa_network_buffer_data(nbh); if(stun_is_command_message_str(buf,sz)) { u16bits method = stun_get_method_str(buf,sz); if((method != STUN_METHOD_SEND) && (method != STUN_METHOD_DATA)) { traffic = &(s->control_traffic); } } } band_limit_t bsz = (band_limit_t)sz; if(s->jiffie != s->e->jiffie) { s->jiffie = s->e->jiffie; traffic->jiffie_bytes_read = 0; traffic->jiffie_bytes_write = 0; if(bsz > max_bps) { return 0; } else { if(read) traffic->jiffie_bytes_read = bsz; else traffic->jiffie_bytes_write = bsz; return 1; } } else { band_limit_t nsz; if(read) nsz = traffic->jiffie_bytes_read + bsz; else nsz = traffic->jiffie_bytes_write + bsz; if(nsz > max_bps) { return 0; } else { if(read) traffic->jiffie_bytes_read = nsz; else traffic->jiffie_bytes_write = nsz; return 1; } } } return 1; } int get_ioa_socket_from_reservation(ioa_engine_handle e, u64bits in_reservation_token, ioa_socket_handle *s) { if (e && in_reservation_token && s) { *s = rtcp_map_get(e->map_rtcp, in_reservation_token); if (*s) { return 0; } } return -1; } /* Socket options helpers ==>> */ static int set_socket_ttl(ioa_socket_handle s, int ttl) { if(s->default_ttl < 0) //Unsupported return -1; if(ttl < 0) ttl = s->default_ttl; CORRECT_RAW_TTL(ttl); if(ttl > s->default_ttl) ttl=s->default_ttl; if(s->current_ttl != ttl) { int ret = set_raw_socket_ttl(s->fd, s->family, ttl); s->current_ttl = ttl; return ret; } return 0; } static int set_socket_tos(ioa_socket_handle s, int tos) { if(s->default_tos < 0) //Unsupported return -1; if(tos < 0) tos = s->default_tos; CORRECT_RAW_TOS(tos); if(s->current_tos != tos) { int ret = set_raw_socket_tos(s->fd, s->family, tos); s->current_tos = tos; return ret; } return 0; } int set_raw_socket_ttl_options(evutil_socket_t fd, int family) { if (family == AF_INET6) { #if !defined(IPV6_RECVHOPLIMIT) UNUSED_ARG(fd); #else int recv_ttl_on = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &recv_ttl_on, sizeof(recv_ttl_on)) < 0) { perror("cannot set recvhoplimit\n"); } #endif } else { #if !defined(IP_RECVTTL) UNUSED_ARG(fd); #else int recv_ttl_on = 1; if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &recv_ttl_on, sizeof(recv_ttl_on)) < 0) { perror("cannot set recvttl\n"); } #endif } return 0; } int set_raw_socket_tos_options(evutil_socket_t fd, int family) { if (family == AF_INET6) { #if !defined(IPV6_RECVTCLASS) UNUSED_ARG(fd); #else int recv_tos_on = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVTCLASS, &recv_tos_on, sizeof(recv_tos_on)) < 0) { perror("cannot set recvtclass\n"); } #endif } else { #if !defined(IP_RECVTOS) UNUSED_ARG(fd); #else int recv_tos_on = 1; if (setsockopt(fd, IPPROTO_IP, IP_RECVTOS, &recv_tos_on, sizeof(recv_tos_on)) < 0) { perror("cannot set recvtos\n"); } #endif } return 0; } int set_socket_options_fd(evutil_socket_t fd, SOCKET_TYPE st, int family) { if(fd<0) return 0; set_sock_buf_size(fd,UR_CLIENT_SOCK_BUF_SIZE); if(is_tcp_socket(st)) { /* <<== FREEBSD fix */ struct linger so_linger; so_linger.l_onoff = 1; so_linger.l_linger = 0; if(setsockopt(fd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger))<1) { //perror("setsolinger") ; } } socket_set_nonblocking(fd); if (!is_stream_socket(st)) { set_raw_socket_ttl_options(fd, family); set_raw_socket_tos_options(fd, family); #ifdef IP_RECVERR if (family != AF_INET6) { int on = 0; #ifdef TURN_IP_RECVERR on = 1; #endif if(setsockopt(fd, IPPROTO_IP, IP_RECVERR, (void *)&on, sizeof(on))<0) perror("IP_RECVERR"); } #endif #ifdef IPV6_RECVERR if (family == AF_INET6) { int on = 0; #ifdef TURN_IP_RECVERR on = 1; #endif if(setsockopt(fd, IPPROTO_IPV6, IPV6_RECVERR, (void *)&on, sizeof(on))<0) perror("IPV6_RECVERR"); } #endif } else { int flag = 1; if(is_tcp_socket(st)) { setsockopt(fd, /* socket affected */ IPPROTO_TCP, /* set option at TCP level */ TCP_NODELAY, /* name of option */ (char*)&flag, /* value */ sizeof(int)); /* length of option value */ } else { #if defined(SCTP_NODELAY) setsockopt(fd, /* socket affected */ IPPROTO_SCTP, /* set option at SCTP level */ SCTP_NODELAY, /* name of option */ (char*)&flag, /* value */ sizeof(int)); /* length of option value */ #endif } socket_tcp_set_keepalive(fd,st); } return 0; } int set_socket_options(ioa_socket_handle s) { if(!s || (s->parent_s)) return 0; set_socket_options_fd(s->fd,s->st,s->family); s->default_ttl = get_raw_socket_ttl(s->fd, s->family); s->current_ttl = s->default_ttl; s->default_tos = get_raw_socket_tos(s->fd, s->family); s->current_tos = s->default_tos; return 0; } /* <<== Socket options helpers */ ioa_socket_handle create_unbound_relay_ioa_socket(ioa_engine_handle e, int family, SOCKET_TYPE st, SOCKET_APP_TYPE sat) { evutil_socket_t fd = -1; ioa_socket_handle ret = NULL; switch (st){ case UDP_SOCKET: fd = socket(family, RELAY_DGRAM_SOCKET_TYPE, RELAY_DGRAM_SOCKET_PROTOCOL); if (fd < 0) { perror("UDP socket"); return NULL; } set_sock_buf_size(fd, UR_CLIENT_SOCK_BUF_SIZE); break; case TCP_SOCKET: fd = socket(family, RELAY_STREAM_SOCKET_TYPE, RELAY_STREAM_SOCKET_PROTOCOL); if (fd < 0) { perror("TCP socket"); return NULL; } set_sock_buf_size(fd, UR_CLIENT_SOCK_BUF_SIZE); break; default: /* we do not support other sockets in the relay position */ return NULL; } ret = (ioa_socket*)turn_malloc(sizeof(ioa_socket)); ns_bzero(ret,sizeof(ioa_socket)); ret->magic = SOCKET_MAGIC; ret->fd = fd; ret->family = family; ret->st = st; ret->sat = sat; ret->e = e; set_socket_options(ret); return ret; } static int bind_ioa_socket(ioa_socket_handle s, const ioa_addr* local_addr, int reusable) { if(!s || (s->parent_s)) return 0; if (s && s->fd >= 0 && s->e && local_addr) { int res = addr_bind(s->fd, local_addr, reusable,1,s->st); if (res >= 0) { s->bound = 1; addr_cpy(&(s->local_addr), local_addr); if(addr_get_port(local_addr)<1) { ioa_addr tmpaddr; addr_get_from_sock(s->fd, &tmpaddr); if(addr_any(&(s->local_addr))) { addr_cpy(&(s->local_addr),&tmpaddr); } else { addr_set_port(&(s->local_addr),addr_get_port(&tmpaddr)); } } s->local_addr_known = 1; return 0; } } return -1; } int create_relay_ioa_sockets(ioa_engine_handle e, ioa_socket_handle client_s, int address_family, u08bits transport, int even_port, ioa_socket_handle *rtp_s, ioa_socket_handle *rtcp_s, uint64_t *out_reservation_token, int *err_code, const u08bits **reason, accept_cb acb, void *acbarg) { *rtp_s = NULL; if (rtcp_s) *rtcp_s = NULL; turnipports* tp = e->tp; size_t iip = 0; for (iip = 0; iip < e->relays_number; ++iip) { ioa_addr relay_addr; const ioa_addr *ra = ioa_engine_get_relay_addr(e, client_s, address_family, err_code); if(ra) addr_cpy(&relay_addr, ra); if(*err_code) { if(*err_code == 440) *reason = (const u08bits *) "Unsupported address family"; return -1; } int rtcp_port = -1; IOA_CLOSE_SOCKET(*rtp_s); if(rtcp_s) IOA_CLOSE_SOCKET(*rtcp_s); ioa_addr rtcp_local_addr; addr_cpy(&rtcp_local_addr, &relay_addr); int i = 0; int port = 0; ioa_addr local_addr; addr_cpy(&local_addr, &relay_addr); for (i = 0; i < 0xFFFF; i++) { port = 0; rtcp_port = -1; if (even_port < 0) { port = turnipports_allocate(tp, transport, &relay_addr); } else { port = turnipports_allocate_even(tp, &relay_addr, even_port, out_reservation_token); if (port >= 0 && even_port > 0) { IOA_CLOSE_SOCKET(*rtcp_s); *rtcp_s = create_unbound_relay_ioa_socket(e, relay_addr.ss.sa_family, UDP_SOCKET, RELAY_RTCP_SOCKET); if (*rtcp_s == NULL) { perror("socket"); IOA_CLOSE_SOCKET(*rtp_s); addr_set_port(&local_addr, port); turnipports_release(tp, transport, &local_addr); rtcp_port = port + 1; addr_set_port(&rtcp_local_addr, rtcp_port); turnipports_release(tp, transport, &rtcp_local_addr); return -1; } sock_bind_to_device((*rtcp_s)->fd, (unsigned char*)e->relay_ifname); rtcp_port = port + 1; addr_set_port(&rtcp_local_addr, rtcp_port); if (bind_ioa_socket(*rtcp_s, &rtcp_local_addr, (transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE)) < 0) { addr_set_port(&local_addr, port); turnipports_release(tp, transport, &local_addr); turnipports_release(tp, transport, &rtcp_local_addr); rtcp_port = -1; IOA_CLOSE_SOCKET(*rtcp_s); continue; } } } if (port < 0) { IOA_CLOSE_SOCKET(*rtp_s); if (rtcp_s) IOA_CLOSE_SOCKET(*rtcp_s); rtcp_port = -1; break; } else { IOA_CLOSE_SOCKET(*rtp_s); *rtp_s = create_unbound_relay_ioa_socket(e, relay_addr.ss.sa_family, (transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE) ? TCP_SOCKET : UDP_SOCKET, RELAY_SOCKET); if (*rtp_s == NULL) { int rtcp_bound = 0; if (rtcp_s && *rtcp_s) { rtcp_bound = (*rtcp_s)->bound; IOA_CLOSE_SOCKET(*rtcp_s); } addr_set_port(&local_addr, port); turnipports_release(tp, transport, &local_addr); if (rtcp_port >= 0 && !rtcp_bound) { addr_set_port(&rtcp_local_addr, rtcp_port); turnipports_release(tp, transport, &rtcp_local_addr); } perror("socket"); return -1; } sock_bind_to_device((*rtp_s)->fd, (unsigned char*)e->relay_ifname); addr_set_port(&local_addr, port); if (bind_ioa_socket(*rtp_s, &local_addr, (transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE)) >= 0) { break; } else { IOA_CLOSE_SOCKET(*rtp_s); int rtcp_bound = 0; if (rtcp_s && *rtcp_s) { rtcp_bound = (*rtcp_s)->bound; IOA_CLOSE_SOCKET(*rtcp_s); } addr_set_port(&local_addr, port); turnipports_release(tp, transport, &local_addr); if (rtcp_port >= 0 && !rtcp_bound) { addr_set_port(&rtcp_local_addr, rtcp_port); turnipports_release(tp, transport, &rtcp_local_addr); } rtcp_port = -1; } } } if(i>=0xFFFF) { IOA_CLOSE_SOCKET(*rtp_s); if (rtcp_s) IOA_CLOSE_SOCKET(*rtcp_s); } if (*rtp_s) { addr_set_port(&local_addr, port); addr_debug_print(e->verbose, &local_addr, "Local relay addr"); if (rtcp_s && *rtcp_s) { addr_set_port(&local_addr, port+1); addr_debug_print(e->verbose, &local_addr, "Local reserved relay addr"); } break; } } if (!(*rtp_s)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: no available ports 3\n", __FUNCTION__); IOA_CLOSE_SOCKET(*rtp_s); if (rtcp_s) IOA_CLOSE_SOCKET(*rtcp_s); return -1; } set_accept_cb(*rtp_s, acb, acbarg); if (rtcp_s && *rtcp_s && out_reservation_token && *out_reservation_token) { if (rtcp_map_put(e->map_rtcp, *out_reservation_token, *rtcp_s) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: cannot update RTCP map\n", __FUNCTION__); IOA_CLOSE_SOCKET(*rtp_s); if (rtcp_s) IOA_CLOSE_SOCKET(*rtcp_s); return -1; } } return 0; } /* RFC 6062 ==>> */ static void tcp_listener_input_handler(struct evconnlistener *l, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *arg) { UNUSED_ARG(l); ioa_socket_handle list_s = (ioa_socket_handle) arg; ioa_addr client_addr; ns_bcopy(sa,&client_addr,socklen); addr_debug_print(((list_s->e) && list_s->e->verbose), &client_addr,"tcp accepted from"); ioa_socket_handle s = create_ioa_socket_from_fd( list_s->e, fd, NULL, TCP_SOCKET, TCP_RELAY_DATA_SOCKET, &client_addr, &(list_s->local_addr)); if (s) { if(list_s->acb) { list_s->acb(s,list_s->acbarg); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Do not know what to do with accepted TCP socket\n"); close_ioa_socket(s); } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot create ioa_socket from FD\n"); socket_closesocket(fd); } } static int set_accept_cb(ioa_socket_handle s, accept_cb acb, void *arg) { if(!s || s->parent_s) return -1; if(s->st == TCP_SOCKET) { s->list_ev = evconnlistener_new(s->e->event_base, tcp_listener_input_handler, s, LEV_OPT_REUSEABLE, 1024, s->fd); if(!(s->list_ev)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: cannot start TCP listener\n", __FUNCTION__); return -1; } s->acb = acb; s->acbarg = arg; } return 0; } static void connect_eventcb(struct bufferevent *bev, short events, void *ptr) { UNUSED_ARG(bev); ioa_socket_handle ret = (ioa_socket_handle) ptr; if (ret) { connect_cb cb = ret->conn_cb; void *arg = ret->conn_arg; if (events & BEV_EVENT_CONNECTED) { ret->conn_cb = NULL; ret->conn_arg = NULL; BUFFEREVENT_FREE(ret->conn_bev); ret->connected = 1; if(cb) { cb(1,arg); } } else if (events & BEV_EVENT_ERROR) { /* An error occured while connecting. */ ret->conn_cb = NULL; ret->conn_arg = NULL; BUFFEREVENT_FREE(ret->conn_bev); if(cb) { cb(0,arg); } } } } ioa_socket_handle ioa_create_connecting_tcp_relay_socket(ioa_socket_handle s, ioa_addr *peer_addr, connect_cb cb, void *arg) { ioa_socket_handle ret = create_unbound_relay_ioa_socket(s->e, s->family, s->st, TCP_RELAY_DATA_SOCKET); if(!ret) { return NULL; } ioa_addr new_local_addr; addr_cpy(&new_local_addr, &(s->local_addr)); #if !defined(SO_REUSEPORT) /* * trick for OSes which do not support SO_REUSEPORT. * Section 5.2 of RFC 6062 will not work correctly * for those OSes (for example, Linux pre-3.9 kernel). */ #if !defined(__CYGWIN__) && !defined(__CYGWIN32__) && !defined(__CYGWIN64__) close_socket_net_data(s); #else addr_set_port(&new_local_addr,0); #endif #endif if(bind_ioa_socket(ret, &new_local_addr,1)<0) { IOA_CLOSE_SOCKET(ret); ret = NULL; goto ccs_end; } addr_cpy(&(ret->remote_addr), peer_addr); set_ioa_socket_session(ret, s->session); BUFFEREVENT_FREE(ret->conn_bev); ret->conn_bev = bufferevent_socket_new(ret->e->event_base, ret->fd, TURN_BUFFEREVENTS_OPTIONS); debug_ptr_add(ret->conn_bev); bufferevent_setcb(ret->conn_bev, NULL, NULL, connect_eventcb, ret); ret->conn_arg = arg; ret->conn_cb = cb; if (bufferevent_socket_connect(ret->conn_bev, (struct sockaddr *) peer_addr, get_ioa_addr_len(peer_addr)) < 0) { /* Error starting connection */ set_ioa_socket_session(ret, NULL); IOA_CLOSE_SOCKET(ret); ret = NULL; goto ccs_end; } ccs_end: #if !defined(SO_REUSEPORT) #if !defined(__CYGWIN__) && !defined(__CYGWIN32__) && !defined(__CYGWIN64__) /* * trick for OSes which do not support SO_REUSEPORT. * Section 5.2 of RFC 6062 will not work correctly * for those OSes (for example, Linux pre-3.9 kernel). */ s->fd = socket(s->family, RELAY_STREAM_SOCKET_TYPE, RELAY_STREAM_SOCKET_PROTOCOL); if (s->fd < 0) { perror("TCP socket"); if(ret) { set_ioa_socket_session(ret, NULL); IOA_CLOSE_SOCKET(ret); ret = NULL; } } else { set_socket_options(s); sock_bind_to_device(s->fd, (unsigned char*)s->e->relay_ifname); if(bind_ioa_socket(s, &new_local_addr, 1)<0) { if(ret) { set_ioa_socket_session(ret, NULL); IOA_CLOSE_SOCKET(ret); ret = NULL; } } else { set_accept_cb(s, s->acb, s->acbarg); } } #endif #endif return ret; } /* <<== RFC 6062 */ void add_socket_to_parent(ioa_socket_handle parent_s, ioa_socket_handle s) { if(parent_s && s) { delete_socket_from_parent(s); s->parent_s = parent_s; s->fd = parent_s->fd; } } void delete_socket_from_parent(ioa_socket_handle s) { if(s && s->parent_s) { s->parent_s = NULL; s->fd = -1; } } void add_socket_to_map(ioa_socket_handle s, ur_addr_map *amap) { if(amap && s && (s->sockets_container != amap)) { delete_socket_from_map(s); ur_addr_map_del(amap, &(s->remote_addr),NULL); ur_addr_map_put(amap, &(s->remote_addr), (ur_addr_map_value_type)s); s->sockets_container = amap; } } void delete_socket_from_map(ioa_socket_handle s) { if(s && s->sockets_container) { ur_addr_map_del(s->sockets_container, &(s->remote_addr), NULL); s->sockets_container = NULL; } } ioa_socket_handle create_ioa_socket_from_fd(ioa_engine_handle e, ioa_socket_raw fd, ioa_socket_handle parent_s, SOCKET_TYPE st, SOCKET_APP_TYPE sat, const ioa_addr *remote_addr, const ioa_addr *local_addr) { ioa_socket_handle ret = NULL; if ((fd < 0) && !parent_s) { return NULL; } ret = (ioa_socket*)turn_malloc(sizeof(ioa_socket)); ns_bzero(ret,sizeof(ioa_socket)); ret->magic = SOCKET_MAGIC; ret->fd = fd; ret->st = st; ret->sat = sat; ret->e = e; if (local_addr) { ret->family = local_addr->ss.sa_family; ret->bound = 1; addr_cpy(&(ret->local_addr), local_addr); } if (remote_addr) { ret->connected = 1; if(!(ret->family)) ret->family = remote_addr->ss.sa_family; addr_cpy(&(ret->remote_addr), remote_addr); } if(parent_s) { add_socket_to_parent(parent_s, ret); } else { set_socket_options(ret); } return ret; } static void ssl_info_callback(SSL *ssl, int where, int ret) { UNUSED_ARG(ret); UNUSED_ARG(ssl); UNUSED_ARG(where); #if OPENSSL_VERSION_NUMBER < 0x10100000L #if defined(SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS) if (0 != (where & SSL_CB_HANDSHAKE_START)) { ioa_socket_handle s = (ioa_socket_handle)SSL_get_app_data(ssl); if(s) { ++(s->ssl_renegs); } } else if (0 != (where & SSL_CB_HANDSHAKE_DONE)) { if(ssl->s3) { ioa_socket_handle s = (ioa_socket_handle)SSL_get_app_data(ssl); if(s) { if(s->ssl_renegs>SSL_MAX_RENEG_NUMBER) { ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS; } } } } #endif #endif } typedef void (*ssl_info_callback_t)(const SSL *ssl,int type,int val); static void set_socket_ssl(ioa_socket_handle s, SSL *ssl) { if(s && (s->ssl != ssl)) { if(s->ssl) { SSL_set_app_data(s->ssl,NULL); SSL_set_info_callback(s->ssl, (ssl_info_callback_t)NULL); } s->ssl = ssl; if(ssl) { SSL_set_app_data(ssl,s); SSL_set_info_callback(ssl, (ssl_info_callback_t)ssl_info_callback); } } } /* Only must be called for DTLS_SOCKET */ ioa_socket_handle create_ioa_socket_from_ssl(ioa_engine_handle e, ioa_socket_handle parent_s, SSL* ssl, SOCKET_TYPE st, SOCKET_APP_TYPE sat, const ioa_addr *remote_addr, const ioa_addr *local_addr) { if(!parent_s) return NULL; ioa_socket_handle ret = create_ioa_socket_from_fd(e, parent_s->fd, parent_s, st, sat, remote_addr, local_addr); if(ret) { set_socket_ssl(ret,ssl); } return ret; } static void close_socket_net_data(ioa_socket_handle s) { if(s) { EVENT_DEL(s->read_event); if(s->list_ev) { evconnlistener_free(s->list_ev); s->list_ev = NULL; } BUFFEREVENT_FREE(s->conn_bev); BUFFEREVENT_FREE(s->bev); if (s->ssl) { if (!s->broken) { if(!(SSL_get_shutdown(s->ssl) & SSL_SENT_SHUTDOWN)) { /* * SSL_RECEIVED_SHUTDOWN tells SSL_shutdown to act as if we had already * received a close notify from the other end. SSL_shutdown will then * send the final close notify in reply. The other end will receive the * close notify and send theirs. By this time, we will have already * closed the socket and the other end's real close notify will never be * received. In effect, both sides will think that they have completed a * clean shutdown and keep their sessions valid. This strategy will fail * if the socket is not ready for writing, in which case this hack will * lead to an unclean shutdown and lost session on the other end. */ SSL_set_shutdown(s->ssl, SSL_RECEIVED_SHUTDOWN); SSL_shutdown(s->ssl); log_socket_event(s, "SSL shutdown received, socket to be closed",0); } } SSL_FREE(s->ssl); } if (s->fd >= 0) { socket_closesocket(s->fd); s->fd = -1; } } } void detach_socket_net_data(ioa_socket_handle s) { if(s) { EVENT_DEL(s->read_event); s->read_cb = NULL; s->read_ctx = NULL; if(s->list_ev) { evconnlistener_free(s->list_ev); s->list_ev = NULL; } s->acb = NULL; s->acbarg = NULL; BUFFEREVENT_FREE(s->conn_bev); s->conn_arg=NULL; s->conn_cb=NULL; BUFFEREVENT_FREE(s->bev); } } void close_ioa_socket(ioa_socket_handle s) { if (s) { if(s->magic != SOCKET_MAGIC) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s wrong magic on socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, s->st, s->sat); return; } if(s->done) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s double free on socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return; } s->done = 1; while(!buffer_list_empty(&(s->bufs))) pop_elem_from_buffer_list(&(s->bufs)); ioa_network_buffer_delete(s->e, s->defer_nbh); if(s->bound && s->e && s->e->tp && ((s->sat == RELAY_SOCKET)||(s->sat == RELAY_RTCP_SOCKET))) { turnipports_release(s->e->tp, ((s->st == TCP_SOCKET) ? STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE : STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE), &(s->local_addr)); } if(s->special_session) { turn_free(s->special_session,s->special_session_size); s->special_session = NULL; } s->special_session_size = 0; delete_socket_from_map(s); delete_socket_from_parent(s); close_socket_net_data(s); s->session = NULL; s->sub_session = NULL; s->magic = 0; turn_free(s,sizeof(ioa_socket)); } } ioa_socket_handle detach_ioa_socket(ioa_socket_handle s) { ioa_socket_handle ret = NULL; if (!s) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Detaching NULL socket\n"); } else { if((s->magic != SOCKET_MAGIC)||(s->done)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s detach on bad socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return ret; } if(s->tobeclosed) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s detach on tobeclosed socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, s->st, s->sat); return ret; } if(!(s->e)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s detach on socket without engine: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, s->st, s->sat); return ret; } s->tobeclosed = 1; if(s->parent_s) { if((s->st != UDP_SOCKET) && (s->st != DTLS_SOCKET)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "!!! %s detach on non-UDP child socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, s->st, s->sat); return ret; } } evutil_socket_t udp_fd = -1; if(s->parent_s) { udp_fd = socket(s->local_addr.ss.sa_family, CLIENT_DGRAM_SOCKET_TYPE, CLIENT_DGRAM_SOCKET_PROTOCOL); if (udp_fd < 0) { perror("socket"); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"%s: Cannot allocate new socket\n",__FUNCTION__); return ret; } if(sock_bind_to_device(udp_fd, (unsigned char*)(s->e->relay_ifname))<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot bind udp server socket to device %s\n",(char*)(s->e->relay_ifname)); } if(addr_bind(udp_fd,&(s->local_addr),1,1,UDP_SOCKET)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot bind new detached udp server socket to local addr\n"); close(udp_fd); return ret; } int connect_err=0; if(addr_connect(udp_fd, &(s->remote_addr), &connect_err)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot connect new detached udp server socket to remote addr\n"); close(udp_fd); return ret; } set_raw_socket_ttl_options(udp_fd, s->local_addr.ss.sa_family); set_raw_socket_tos_options(udp_fd, s->local_addr.ss.sa_family); } detach_socket_net_data(s); while(!buffer_list_empty(&(s->bufs))) pop_elem_from_buffer_list(&(s->bufs)); ioa_network_buffer_delete(s->e, s->defer_nbh); ret = (ioa_socket*)turn_malloc(sizeof(ioa_socket)); if(!ret) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"%s: Cannot allocate new socket structure\n",__FUNCTION__); if(udp_fd>=0) close(udp_fd); return ret; } ns_bzero(ret,sizeof(ioa_socket)); ret->magic = SOCKET_MAGIC; SSL* ssl = s->ssl; set_socket_ssl(s,NULL); set_socket_ssl(ret,ssl); ret->fd = s->fd; ret->family = get_ioa_socket_address_family(s); ret->st = s->st; ret->sat = s->sat; ret->bound = s->bound; ret->local_addr_known = s->local_addr_known; addr_cpy(&(ret->local_addr),&(s->local_addr)); ret->connected = s->connected; addr_cpy(&(ret->remote_addr),&(s->remote_addr)); delete_socket_from_map(s); delete_socket_from_parent(s); if(udp_fd>=0) { ret->fd = udp_fd; set_socket_options(ret); } ret->current_ttl = s->current_ttl; ret->default_ttl = s->default_ttl; ret->current_tos = s->current_tos; ret->default_tos = s->default_tos; s->fd = -1; } return ret; } ts_ur_super_session *get_ioa_socket_session(ioa_socket_handle s) { if(s) return s->session; return NULL; } void set_ioa_socket_session(ioa_socket_handle s, ts_ur_super_session *ss) { if(s) s->session = ss; } void clear_ioa_socket_session_if(ioa_socket_handle s, void *ss) { if(s && ((void*)(s->session)==ss)) { s->session=NULL; } } tcp_connection *get_ioa_socket_sub_session(ioa_socket_handle s) { if(s) return s->sub_session; return NULL; } void set_ioa_socket_sub_session(ioa_socket_handle s, tcp_connection *tc) { if(s) s->sub_session = tc; } int get_ioa_socket_address_family(ioa_socket_handle s) { int first_time = 1; beg: if (!(s && (s->magic == SOCKET_MAGIC) && !(s->done))) { return AF_INET; } else if(first_time && s->parent_s && (s != s->parent_s)) { first_time = 0; s = s->parent_s; goto beg; } else { return s->family; } } SOCKET_TYPE get_ioa_socket_type(ioa_socket_handle s) { if(s) return s->st; return UNKNOWN_SOCKET; } SOCKET_APP_TYPE get_ioa_socket_app_type(ioa_socket_handle s) { if(s) return s->sat; return UNKNOWN_APP_SOCKET; } void set_ioa_socket_app_type(ioa_socket_handle s, SOCKET_APP_TYPE sat) { if(s) s->sat = sat; } ioa_addr* get_local_addr_from_ioa_socket(ioa_socket_handle s) { if (s && (s->magic == SOCKET_MAGIC) && !(s->done)) { if(s->parent_s) { s = s->parent_s; } if (s->local_addr_known) { return &(s->local_addr); } else if (s->bound && (addr_get_port(&(s->local_addr)) > 0)) { s->local_addr_known = 1; return &(s->local_addr); } else { ioa_addr tmpaddr; if (addr_get_from_sock(s->fd, &tmpaddr) == 0) { if(addr_get_port(&tmpaddr)>0) { s->local_addr_known = 1; s->bound = 1; if(addr_any(&(s->local_addr))) { addr_cpy(&(s->local_addr),&tmpaddr); } else { addr_set_port(&(s->local_addr),addr_get_port(&tmpaddr)); } return &(s->local_addr); } if(addr_any(&(s->local_addr))) { addr_cpy(&(s->local_addr),&tmpaddr); } return &(s->local_addr); } } } return NULL; } ioa_addr* get_remote_addr_from_ioa_socket(ioa_socket_handle s) { if (s && (s->magic == SOCKET_MAGIC) && !(s->done)) { if (s->connected) { return &(s->remote_addr); } } return NULL; } int get_local_mtu_ioa_socket(ioa_socket_handle s) { if(s) { if(s->parent_s) s = s->parent_s; return get_socket_mtu(s->fd, s->family, (s->e && eve(s->e->verbose))); } return -1; } /* * Return: -1 - error, 0 or >0 - OK * *read_len -1 - no data, >=0 - data available */ int ssl_read(evutil_socket_t fd, SSL* ssl, ioa_network_buffer_handle nbh, int verbose) { int ret = 0; if (!ssl || !nbh) return -1; s08bits* buffer = (s08bits*)ioa_network_buffer_data(nbh); int buf_size = (int)ioa_network_buffer_get_capacity_udp(); int read_len = (int)ioa_network_buffer_get_size(nbh); if(read_len < 1) return -1; s08bits *new_buffer = buffer + buf_size; int old_buffer_len = read_len; int len = 0; if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: before read...\n", __FUNCTION__); } BIO *wbio = SSL_get_wbio(ssl); if(wbio) { BIO_set_fd(wbio,fd,BIO_NOCLOSE); } BIO* rbio = BIO_new_mem_buf(buffer, old_buffer_len); BIO_set_mem_eof_return(rbio, -1); #if OPENSSL_VERSION_NUMBER < 0x10100000L ssl->rbio = rbio; #else SSL_set0_rbio(ssl,rbio); #endif int if1 = SSL_is_init_finished(ssl); do { len = SSL_read(ssl, new_buffer, buf_size); } while (len < 0 && (errno == EINTR)); int if2 = SSL_is_init_finished(ssl); if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: after read: %d\n", __FUNCTION__, len); } if(SSL_get_shutdown(ssl)) { ret = -1; } else if (!if1 && if2) { if(verbose && SSL_get_peer_certificate(ssl)) { printf("\n------------------------------------------------------------\n"); X509_NAME_print_ex_fp(stdout, X509_get_subject_name(SSL_get_peer_certificate(ssl)), 1, XN_FLAG_MULTILINE); printf("\n\n Cipher: %s\n", SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))); printf("\n------------------------------------------------------------\n\n"); } ret = 0; } else if (len < 0 && ((errno == ENOBUFS) || (errno == EAGAIN))) { if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: ENOBUFS/EAGAIN\n", __FUNCTION__); } ret = 0; } else { if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: read %d bytes\n", __FUNCTION__, (int) len); } if (len >= 0) { ret = len; } else { switch (SSL_get_error(ssl, len)){ case SSL_ERROR_NONE: //??? ret = 0; break; case SSL_ERROR_WANT_READ: ret = 0; break; case SSL_ERROR_WANT_WRITE: ret = 0; break; case SSL_ERROR_ZERO_RETURN: ret = 0; break; case SSL_ERROR_SYSCALL: { int err = errno; if (handle_socket_error()) { ret = 0; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TLS Socket read error: %d\n", err); ret = -1; } break; } case SSL_ERROR_SSL: if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SSL read error: "); s08bits buf[65536]; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s (%d)\n", ERR_error_string(ERR_get_error(), buf), SSL_get_error(ssl, len)); } if (verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SSL connection closed.\n"); ret = -1; break; default: if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Unexpected error while reading!\n"); } ret = -1; } } } if(ret>0) { ioa_network_buffer_add_offset_size(nbh, (u16bits)buf_size, 0, (size_t)ret); } #if OPENSSL_VERSION_NUMBER < 0x10100000L ssl->rbio = NULL; BIO_free(rbio); #else SSL_set0_rbio(ssl,NULL); #endif return ret; } static int socket_readerr(evutil_socket_t fd, ioa_addr *orig_addr) { if ((fd < 0) || !orig_addr) return -1; #if defined(CMSG_SPACE) && defined(MSG_ERRQUEUE) && defined(IP_RECVERR) u08bits ecmsg[TURN_CMSG_SZ+1]; int flags = MSG_ERRQUEUE; int len = 0; struct msghdr msg; struct iovec iov; char buffer[65536]; char *cmsg = (char*)ecmsg; msg.msg_control = cmsg; msg.msg_controllen = TURN_CMSG_SZ; /* CMSG_SPACE(sizeof(recv_ttl)+sizeof(recv_tos)) */ msg.msg_name = orig_addr; msg.msg_namelen = (socklen_t)get_ioa_addr_len(orig_addr); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_iov->iov_base = buffer; msg.msg_iov->iov_len = sizeof(buffer); msg.msg_flags = 0; int try_cycle = 0; do { do { len = recvmsg(fd,&msg,flags); } while (len < 0 && (errno == EINTR)); } while((len>0)&&(try_cycle++iov_base = buffer; msg.msg_iov->iov_len = (size_t)buf_size; msg.msg_flags = 0; #if defined(MSG_ERRQUEUE) int try_cycle = 0; try_again: #endif do { len = recvmsg(fd,&msg,flags); } while (len < 0 && (errno == EINTR)); #if defined(MSG_ERRQUEUE) if(flags & MSG_ERRQUEUE) { if((len>0)&&(try_cycle++= 0) { struct cmsghdr *cmsgh; // Receive auxiliary data in msg for (cmsgh = CMSG_FIRSTHDR(&msg); cmsgh != NULL; cmsgh = CMSG_NXTHDR(&msg,cmsgh)) { int l = cmsgh->cmsg_level; int t = cmsgh->cmsg_type; switch(l) { case IPPROTO_IP: switch(t) { #if defined(IP_RECVTTL) && !defined(__sparc_v9__) case IP_RECVTTL: case IP_TTL: recv_ttl = *((recv_ttl_t *) CMSG_DATA(cmsgh)); break; #endif #if defined(IP_RECVTOS) case IP_RECVTOS: case IP_TOS: recv_tos = *((recv_tos_t *) CMSG_DATA(cmsgh)); break; #endif #if defined(IP_RECVERR) case IP_RECVERR: { struct turn_sock_extended_err *e=(struct turn_sock_extended_err*) CMSG_DATA(cmsgh); if(errcode) *errcode = e->ee_errno; } break; #endif default: ; /* no break */ }; break; case IPPROTO_IPV6: switch(t) { #if defined(IPV6_RECVHOPLIMIT) && !defined(__sparc_v9__) case IPV6_RECVHOPLIMIT: case IPV6_HOPLIMIT: recv_ttl = *((recv_ttl_t *) CMSG_DATA(cmsgh)); break; #endif #if defined(IPV6_RECVTCLASS) case IPV6_RECVTCLASS: case IPV6_TCLASS: recv_tos = *((recv_tos_t *) CMSG_DATA(cmsgh)); break; #endif #if defined(IPV6_RECVERR) case IPV6_RECVERR: { struct turn_sock_extended_err *e=(struct turn_sock_extended_err*) CMSG_DATA(cmsgh); if(errcode) *errcode = e->ee_errno; } break; #endif default: ; /* no break */ }; break; default: ; /* no break */ }; } } #endif *ttl = recv_ttl; CORRECT_RAW_TTL(*ttl); *tos = recv_tos; CORRECT_RAW_TOS(*tos); return len; } #if TLS_SUPPORTED static TURN_TLS_TYPE check_tentative_tls(ioa_socket_raw fd) { TURN_TLS_TYPE ret = TURN_TLS_NO; char s[12]; int len = 0; do { len = (int)recv(fd, s, sizeof(s), MSG_PEEK); } while (len < 0 && (errno == EINTR)); if(len>0 && ((size_t)len == sizeof(s))) { if((s[0]==22)&&(s[1]==3)&&(s[5]==1)&&(s[9]==3)) { char max_supported = (char)(TURN_TLS_TOTAL-2); if(s[10] > max_supported) ret = TURN_TLS_SSL23; /* compatibility mode */ else ret = (TURN_TLS_TYPE)(s[10]+1); } else if((s[2]==1)&&(s[3]==3)) { ret = TURN_TLS_SSL23; /* compatibility mode */ } } return ret; } #endif static int socket_input_worker(ioa_socket_handle s) { int len = 0; int ret = 0; size_t app_msg_len = 0; int ttl = TTL_IGNORE; int tos = TOS_IGNORE; ioa_addr remote_addr; int try_again = 0; int try_ok = 0; int try_cycle = 0; const int MAX_TRIES = 16; if(!s) return 0; if((s->magic != SOCKET_MAGIC)||(s->done)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return -1; } if(!(s->e)) return 0; if(s->tobeclosed) return 0; if(s->connected) addr_cpy(&remote_addr,&(s->remote_addr)); if(tcp_congestion_control && s->sub_session && s->bev) { if(s == s->sub_session->client_s && (s->sub_session->peer_s)) { if(!is_socket_writeable(s->sub_session->peer_s, STUN_BUFFER_SIZE,__FUNCTION__,0)) { if(bufferevent_enabled(s->bev,EV_READ)) { bufferevent_disable(s->bev,EV_READ); } } } else if(s == s->sub_session->peer_s && (s->sub_session->client_s)) { if(!is_socket_writeable(s->sub_session->client_s, STUN_BUFFER_SIZE,__FUNCTION__,1)) { if(bufferevent_enabled(s->bev,EV_READ)) { bufferevent_disable(s->bev,EV_READ); } } } } if((s->st == TLS_SOCKET)||(s->st == TLS_SCTP_SOCKET)) { #if TLS_SUPPORTED SSL *ctx = bufferevent_openssl_get_ssl(s->bev); if(!ctx || SSL_get_shutdown(ctx)) { s->tobeclosed = 1; return 0; } #endif } else if(s->st == DTLS_SOCKET) { if(!(s->ssl) || SSL_get_shutdown(s->ssl)) { s->tobeclosed = 1; return 0; } } if(!(s->e)) return 0; if(s->st == TENTATIVE_TCP_SOCKET) { EVENT_DEL(s->read_event); #if TLS_SUPPORTED TURN_TLS_TYPE tls_type = check_tentative_tls(s->fd); if(tls_type) { s->st = TLS_SOCKET; if(s->ssl) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on socket: 0x%lx, st=%d, sat=%d: ssl already exist\n", __FUNCTION__,(long)s, s->st, s->sat); } if(s->bev) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on socket: 0x%lx, st=%d, sat=%d: bev already exist\n", __FUNCTION__,(long)s, s->st, s->sat); } switch(tls_type) { #if TLSv1_2_SUPPORTED case TURN_TLS_v1_2: if(s->e->tls_ctx_v1_2) { set_socket_ssl(s,SSL_NEW(s->e->tls_ctx_v1_2)); } break; #endif #if TLSv1_1_SUPPORTED case TURN_TLS_v1_1: if(s->e->tls_ctx_v1_1) { set_socket_ssl(s,SSL_NEW(s->e->tls_ctx_v1_1)); } break; #endif case TURN_TLS_v1_0: if(s->e->tls_ctx_v1_0) { set_socket_ssl(s,SSL_NEW(s->e->tls_ctx_v1_0)); } break; default: if(s->e->tls_ctx_ssl23) { set_socket_ssl(s,SSL_NEW(s->e->tls_ctx_ssl23)); } else { s->tobeclosed = 1; return 0; } }; if(s->ssl) { s->bev = bufferevent_openssl_socket_new(s->e->event_base, s->fd, s->ssl, BUFFEREVENT_SSL_ACCEPTING, TURN_BUFFEREVENTS_OPTIONS); debug_ptr_add(s->bev); bufferevent_setcb(s->bev, socket_input_handler_bev, socket_output_handler_bev, eventcb_bev, s); bufferevent_setwatermark(s->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK); bufferevent_enable(s->bev, EV_READ|EV_WRITE); /* Start reading. */ } } else #endif //TLS_SUPPORTED { s->st = TCP_SOCKET; if(s->bev) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on socket: 0x%lx, st=%d, sat=%d: bev already exist\n", __FUNCTION__,(long)s, s->st, s->sat); } s->bev = bufferevent_socket_new(s->e->event_base, s->fd, TURN_BUFFEREVENTS_OPTIONS); debug_ptr_add(s->bev); bufferevent_setcb(s->bev, socket_input_handler_bev, socket_output_handler_bev, eventcb_bev, s); bufferevent_setwatermark(s->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK); bufferevent_enable(s->bev, EV_READ|EV_WRITE); /* Start reading. */ } } else if(s->st == TENTATIVE_SCTP_SOCKET) { EVENT_DEL(s->read_event); #if TLS_SUPPORTED TURN_TLS_TYPE tls_type = check_tentative_tls(s->fd); if(tls_type) { s->st = TLS_SCTP_SOCKET; if(s->ssl) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on socket: 0x%lx, st=%d, sat=%d: ssl already exist\n", __FUNCTION__,(long)s, s->st, s->sat); } if(s->bev) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on socket: 0x%lx, st=%d, sat=%d: bev already exist\n", __FUNCTION__,(long)s, s->st, s->sat); } switch(tls_type) { #if TLSv1_2_SUPPORTED case TURN_TLS_v1_2: if(s->e->tls_ctx_v1_2) { set_socket_ssl(s,SSL_NEW(s->e->tls_ctx_v1_2)); } break; #endif #if TLSv1_1_SUPPORTED case TURN_TLS_v1_1: if(s->e->tls_ctx_v1_1) { set_socket_ssl(s,SSL_NEW(s->e->tls_ctx_v1_1)); } break; #endif case TURN_TLS_v1_0: if(s->e->tls_ctx_v1_0) { set_socket_ssl(s,SSL_NEW(s->e->tls_ctx_v1_0)); } break; default: if(s->e->tls_ctx_ssl23) { set_socket_ssl(s,SSL_NEW(s->e->tls_ctx_ssl23)); } else { s->tobeclosed = 1; return 0; } }; if(s->ssl) { s->bev = bufferevent_openssl_socket_new(s->e->event_base, s->fd, s->ssl, BUFFEREVENT_SSL_ACCEPTING, TURN_BUFFEREVENTS_OPTIONS); debug_ptr_add(s->bev); bufferevent_setcb(s->bev, socket_input_handler_bev, socket_output_handler_bev, eventcb_bev, s); bufferevent_setwatermark(s->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK); bufferevent_enable(s->bev, EV_READ|EV_WRITE); /* Start reading. */ } } else #endif //TLS_SUPPORTED { s->st = SCTP_SOCKET; if(s->bev) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on socket: 0x%lx, st=%d, sat=%d: bev already exist\n", __FUNCTION__,(long)s, s->st, s->sat); } s->bev = bufferevent_socket_new(s->e->event_base, s->fd, TURN_BUFFEREVENTS_OPTIONS); debug_ptr_add(s->bev); bufferevent_setcb(s->bev, socket_input_handler_bev, socket_output_handler_bev, eventcb_bev, s); bufferevent_setwatermark(s->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK); bufferevent_enable(s->bev, EV_READ|EV_WRITE); /* Start reading. */ } } try_start: if(!(s->e)) return 0; try_again=0; try_ok=0; stun_buffer_list_elem *buf_elem = new_blist_elem(s->e); len = -1; if(s->bev) { /* TCP & TLS & SCTP & SCTP/TLS */ struct evbuffer *inbuf = bufferevent_get_input(s->bev); if(inbuf) { ev_ssize_t blen = evbuffer_copyout(inbuf, buf_elem->buf.buf, STUN_BUFFER_SIZE); if(blen>0) { int mlen = 0; if(blen>(ev_ssize_t)STUN_BUFFER_SIZE) blen=(ev_ssize_t)STUN_BUFFER_SIZE; if(is_stream_socket(s->st) && ((s->sat == TCP_CLIENT_DATA_SOCKET)||(s->sat==TCP_RELAY_DATA_SOCKET))) { mlen = blen; } else { mlen = stun_get_message_len_str(buf_elem->buf.buf, blen, 1, &app_msg_len); } if(mlen>0 && mlen<=(int)blen) { len = (int)bufferevent_read(s->bev, buf_elem->buf.buf, mlen); if(len < 0) { ret = -1; s->tobeclosed = 1; s->broken = 1; log_socket_event(s, "socket read failed, to be closed",1); } else if((s->st == TLS_SOCKET)||(s->st == TLS_SCTP_SOCKET)) { #if TLS_SUPPORTED SSL *ctx = bufferevent_openssl_get_ssl(s->bev); if(!ctx || SSL_get_shutdown(ctx)) { ret = -1; s->tobeclosed = 1; } #endif } if(ret != -1) { ret = len; } } } else if(blen<0) { s->tobeclosed = 1; s->broken = 1; ret = -1; log_socket_event(s, "socket buffer copy failed, to be closed",1); } } else { s->tobeclosed = 1; s->broken = 1; ret = -1; log_socket_event(s, "socket input failed, socket to be closed",1); } if(len == 0) len = -1; } else if(s->fd>=0){ /* UDP and DTLS */ ret = udp_recvfrom(s->fd, &remote_addr, &(s->local_addr), (s08bits*)(buf_elem->buf.buf), UDP_STUN_BUFFER_SIZE, &ttl, &tos, s->e->cmsg, 0, NULL); len = ret; if(s->ssl && (len>0)) { /* DTLS */ send_ssl_backlog_buffers(s); buf_elem->buf.len = (size_t)len; ret = ssl_read(s->fd, s->ssl, (ioa_network_buffer_handle)buf_elem, ((s->e) && s->e->verbose)); addr_cpy(&remote_addr,&(s->remote_addr)); if(ret < 0) { len = -1; s->tobeclosed = 1; s->broken = 1; log_socket_event(s, "SSL read failed, to be closed",0); } else { len = (int)ioa_network_buffer_get_size((ioa_network_buffer_handle)buf_elem); } if((ret!=-1)&&(len>0)) try_again = 1; } else { /* UDP */ if(ret>=0) try_again = 1; } } else { s->tobeclosed = 1; s->broken = 1; ret = -1; log_socket_event(s, "socket unknown error, to be closed",1); } if ((ret!=-1) && (len >= 0)) { if(app_msg_len) buf_elem->buf.len = app_msg_len; else buf_elem->buf.len = len; if(ioa_socket_check_bandwidth(s,buf_elem,1)) { if(s->read_cb) { ioa_net_data nd; ns_bzero(&nd,sizeof(ioa_net_data)); addr_cpy(&(nd.src_addr),&remote_addr); nd.nbh = buf_elem; nd.recv_ttl = ttl; nd.recv_tos = tos; s->read_cb(s, IOA_EV_READ, &nd, s->read_ctx, 1); if(nd.nbh) free_blist_elem(s->e,buf_elem); buf_elem = NULL; try_ok = 1; } else { ioa_network_buffer_delete(s->e, s->defer_nbh); s->defer_nbh = buf_elem; buf_elem = NULL; } } } if(buf_elem) { free_blist_elem(s->e,buf_elem); buf_elem = NULL; } if(try_again && try_ok && !(s->done) && !(s->tobeclosed) && ((++try_cycle)parent_s)) { goto try_start; } return len; } static void socket_input_handler(evutil_socket_t fd, short what, void* arg) { if (!(what & EV_READ)) return; if(!arg) { read_spare_buffer(fd); return; } ioa_socket_handle s = (ioa_socket_handle)arg; if(!s) { read_spare_buffer(fd); return; } if((s->magic != SOCKET_MAGIC)||(s->done)) { read_spare_buffer(fd); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on bad socket, ev=%d: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(int)what,(long)s, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return; } if(fd != s->fd) { read_spare_buffer(fd); return; } if (!ioa_socket_tobeclosed(s)) socket_input_worker(s); else read_spare_buffer(fd); if((s->magic != SOCKET_MAGIC)||(s->done)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s (1) on socket, ev=%d: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(int)what,(long)s, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return; } close_ioa_socket_after_processing_if_necessary(s); } void close_ioa_socket_after_processing_if_necessary(ioa_socket_handle s) { if (s && ioa_socket_tobeclosed(s)) { if(s->special_session) { turn_free(s->special_session,s->special_session_size); s->special_session = NULL; } s->special_session_size = 0; if(!(s->session) && !(s->sub_session)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s https server socket closed: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, get_ioa_socket_type(s), get_ioa_socket_app_type(s)); IOA_CLOSE_SOCKET(s); return; } switch (s->sat){ case TCP_CLIENT_DATA_SOCKET: case TCP_RELAY_DATA_SOCKET: { tcp_connection *tc = s->sub_session; if (tc) { delete_tcp_connection(tc); s->sub_session = NULL; } } break; default: { ts_ur_super_session *ss = s->session; if (ss) { turn_turnserver *server = (turn_turnserver *) ss->server; if (server) { shutdown_client_connection(server, ss, 0, "general"); } } } } } } static void socket_output_handler_bev(struct bufferevent *bev, void* arg) { UNUSED_ARG(bev); UNUSED_ARG(arg); if (tcp_congestion_control) { if (bev && arg) { ioa_socket_handle s = (ioa_socket_handle) arg; if(s->in_write) return; if ((s->magic != SOCKET_MAGIC)||(s->done)||(bev != s->bev)) { return; } if (s->tobeclosed) { if (bufferevent_enabled(bev,EV_READ)) { bufferevent_disable(bev,EV_READ); } return; } if (s->sub_session) { if (s == s->sub_session->client_s) { if (s->sub_session->peer_s && s->sub_session->peer_s->bev) { if (!bufferevent_enabled(s->sub_session->peer_s->bev, EV_READ)) { if (is_socket_writeable(s->sub_session->peer_s, STUN_BUFFER_SIZE, __FUNCTION__, 3)) { bufferevent_enable(s->sub_session->peer_s->bev,EV_READ); socket_input_handler_bev( s->sub_session->peer_s->bev, s->sub_session->peer_s); } } } } else if (s == s->sub_session->peer_s) { if (s->sub_session->client_s && s->sub_session->client_s->bev) { if (!bufferevent_enabled(s->sub_session->client_s->bev, EV_READ)) { if (is_socket_writeable(s->sub_session->client_s, STUN_BUFFER_SIZE, __FUNCTION__, 4)) { bufferevent_enable(s->sub_session->client_s->bev, EV_READ); socket_input_handler_bev( s->sub_session->client_s->bev, s->sub_session->client_s); } } } } } } } } static int read_spare_buffer_bev(struct bufferevent *bev) { if(bev) { char some_buffer[8192]; bufferevent_read(bev, some_buffer, sizeof(some_buffer)); } return 0; } static void socket_input_handler_bev(struct bufferevent *bev, void* arg) { if (bev) { if(!arg) { read_spare_buffer_bev(bev); return; } ioa_socket_handle s = (ioa_socket_handle) arg; if(bev != s->bev) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx: wrong bev\n", __FUNCTION__,(long)s); read_spare_buffer_bev(bev); return; } if((s->magic != SOCKET_MAGIC)||(s->done)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__, (long) s, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); read_spare_buffer_bev(bev); return; } { size_t cycle = 0; do { if(ioa_socket_tobeclosed(s)) { read_spare_buffer_bev(s->bev); break; } if (socket_input_worker(s) <= 0) break; } while((cycle++<128) && (s->bev)); } if((s->magic != SOCKET_MAGIC)||(s->done)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s (1) on socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__, (long) s, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return; } close_ioa_socket_after_processing_if_necessary(s); } } static void eventcb_bev(struct bufferevent *bev, short events, void *arg) { UNUSED_ARG(bev); if (events & BEV_EVENT_CONNECTED) { // Connect okay } else if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF)) { if (arg) { ioa_socket_handle s = (ioa_socket_handle) arg; if(!is_stream_socket(s->st)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s: socket type is wrong on the socket: 0x%lx, st=%d, sat=%d\n",__FUNCTION__,(long)s,s->st,s->sat); return; } if(s->magic != SOCKET_MAGIC) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s: magic is wrong on the socket: 0x%lx, st=%d, sat=%d\n",__FUNCTION__,(long)s,s->st,s->sat); return; } if (s->done) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s: closed socket: 0x%lx (1): done=%d, fd=%d, br=%d, st=%d, sat=%d, tbc=%d\n", __FUNCTION__, (long) s, (int) s->done, (int) s->fd, s->broken, s->st, s->sat, s->tobeclosed); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return; } if (events & BEV_EVENT_ERROR) s->broken = 1; s->tobeclosed = 1; if(s->special_session) { turn_free(s->special_session,s->special_session_size); s->special_session = NULL; } s->special_session_size = 0; if(!(s->session) && !(s->sub_session)) { char sraddr[129]="\0"; addr_to_string(&(s->remote_addr),(u08bits*)sraddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s https server socket closed: 0x%lx, st=%d, sat=%d, remote addr=%s\n", __FUNCTION__,(long)s, get_ioa_socket_type(s), get_ioa_socket_app_type(s),sraddr); IOA_CLOSE_SOCKET(s); return; } switch (s->sat){ case TCP_CLIENT_DATA_SOCKET: case TCP_RELAY_DATA_SOCKET: { tcp_connection *tc = s->sub_session; if (tc) { delete_tcp_connection(tc); s->sub_session = NULL; } } break; default: { ts_ur_super_session *ss = s->session; if (ss) { turn_turnserver *server = (turn_turnserver *) ss->server; if (server) { { char sraddr[129]="\0"; addr_to_string(&(s->remote_addr),(u08bits*)sraddr); if (events & BEV_EVENT_EOF) { if(server->verbose) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: %s socket closed remotely %s\n", (unsigned long long)(ss->id),socket_type_name(s->st),sraddr); if(s == ss->client_socket) { char msg[256]; snprintf(msg,sizeof(msg)-1,"%s connection closed by client (callback)",socket_type_name(s->st)); shutdown_client_connection(server, ss, 0, msg); } else if(s == ss->alloc.relay_sessions[ALLOC_IPV4_INDEX].s) { char msg[256]; snprintf(msg,sizeof(msg)-1,"%s connection closed by peer (ipv4 callback)",socket_type_name(s->st)); shutdown_client_connection(server, ss, 0, msg); } else if(s == ss->alloc.relay_sessions[ALLOC_IPV6_INDEX].s) { char msg[256]; snprintf(msg,sizeof(msg)-1,"%s connection closed by peer (ipv6 callback)",socket_type_name(s->st)); shutdown_client_connection(server, ss, 0, msg); } else { char msg[256]; snprintf(msg,sizeof(msg)-1,"%s connection closed by remote party (callback)",socket_type_name(s->st)); shutdown_client_connection(server, ss, 0, msg); } } else if (events & BEV_EVENT_ERROR) { if(EVUTIL_SOCKET_ERROR()) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"session %018llu: %s socket error: %s %s\n",(unsigned long long)(ss->id), socket_type_name(s->st),evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()), sraddr); } else if(server->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: %s socket disconnected: %s\n", (unsigned long long)(ss->id),socket_type_name(s->st),sraddr); } char msg[256]; snprintf(msg,sizeof(msg)-1,"%s socket buffer operation error (callback)",socket_type_name(s->st)); shutdown_client_connection(server, ss, 0, msg); } } } } } }; } } } static int ssl_send(ioa_socket_handle s, const s08bits* buffer, int len, int verbose) { if (!s || !(s->ssl) || !buffer || (s->fd<0)) return -1; SSL *ssl = s->ssl; if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: before write: buffer=0x%lx, len=%d\n", __FUNCTION__,(long)buffer,len); } if(s->parent_s) { /* Trick only for "children" sockets: */ BIO *wbio = SSL_get_wbio(ssl); if(!wbio) return -1; int fd = BIO_get_fd(wbio,0); int sfd = s->parent_s->fd; if(sfd >= 0) { if(fd != sfd) { BIO_set_fd(wbio,sfd,BIO_NOCLOSE); } } } else { BIO *wbio = SSL_get_wbio(ssl); if(!wbio) return -1; int fd = BIO_get_fd(wbio,0); if(fd != s->fd) { BIO_set_fd(wbio,s->fd,BIO_NOCLOSE); } } int rc = 0; int try_again = 1; #if !defined(TURN_IP_RECVERR) try_again = 0; #endif try_start: do { rc = SSL_write(ssl, buffer, len); } while (rc < 0 && errno == EINTR); if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: after write: %d\n", __FUNCTION__,rc); } if (rc < 0 && ((errno == ENOBUFS) || (errno == EAGAIN))) { if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: ENOBUFS/EAGAIN\n", __FUNCTION__); } return 0; } if (rc >= 0) { if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: wrote %d bytes\n", __FUNCTION__, (int) rc); } return rc; } else { if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: failure: rc=%d, err=%d\n", __FUNCTION__, (int)rc,(int)SSL_get_error(ssl, rc)); } switch (SSL_get_error(ssl, rc)){ case SSL_ERROR_NONE: //??? if (eve(verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "wrote %d bytes\n", (int) rc); } return 0; case SSL_ERROR_WANT_WRITE: return 0; case SSL_ERROR_WANT_READ: return 0; case SSL_ERROR_SYSCALL: { int err = errno; if (!handle_socket_error()) { if(s->st == DTLS_SOCKET) { if(is_connreset()) { if(try_again) { BIO *wbio = SSL_get_wbio(ssl); if(wbio) { int fd = BIO_get_fd(wbio,0); if(fd>=0) { try_again = 0; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS Socket, tring to recover write operation...\n"); socket_readerr(fd, &(s->local_addr)); goto try_start; } } } } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS Socket lost packet... fine\n"); return 0; } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS Socket write error unrecoverable: %d; buffer=0x%lx, len=%d, ssl=0x%lx\n", err, (long)buffer, (int)len, (long)ssl); return -1; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS Socket write error recoverable: %d\n", err); return 0; } } case SSL_ERROR_SSL: if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SSL write error: "); s08bits buf[65536]; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s (%d)\n", ERR_error_string(ERR_get_error(), buf), SSL_get_error(ssl, rc)); } return -1; default: if (verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Unexpected error while writing!\n"); } return -1; } } } static int send_ssl_backlog_buffers(ioa_socket_handle s) { int ret = 0; if(s) { stun_buffer_list_elem *buf_elem = s->bufs.head; while(buf_elem) { int rc = ssl_send(s, (s08bits*)buf_elem->buf.buf + buf_elem->buf.offset - buf_elem->buf.coffset, (size_t)buf_elem->buf.len, ((s->e) && s->e->verbose)); if(rc<1) break; ++ret; pop_elem_from_buffer_list(&(s->bufs)); buf_elem = s->bufs.head; } } return ret; } int is_connreset(void) { switch (errno) { case ECONNRESET: case ECONNREFUSED: return 1; default: ; } return 0; } int would_block(void) { #if defined(EWOULDBLOCK) if(errno == EWOULDBLOCK) return 1; #endif return (errno == EAGAIN); } int udp_send(ioa_socket_handle s, const ioa_addr* dest_addr, const s08bits* buffer, int len) { int rc = 0; evutil_socket_t fd = -1; if(!s) return -1; if(s->parent_s) fd = s->parent_s->fd; else fd = s->fd; if(fd>=0) { int try_again = 1; int cycle; #if !defined(TURN_IP_RECVERR) try_again = 0; #endif try_start: cycle = 0; if (dest_addr) { int slen = get_ioa_addr_len(dest_addr); do { rc = sendto(fd, buffer, len, 0, (const struct sockaddr*) dest_addr, (socklen_t) slen); } while ( ((rc < 0) && (errno == EINTR)) || ((rc<0) && is_connreset() && (++cyclelocal_addr)); goto try_start; } //Lost packet - sent to nowhere... fine. rc = len; } } } return rc; } int send_data_from_ioa_socket_nbh(ioa_socket_handle s, ioa_addr* dest_addr, ioa_network_buffer_handle nbh, int ttl, int tos, int *skip) { int ret = -1; if(!s) { ioa_network_buffer_delete(NULL, nbh); return -1; } if (s->done || (s->fd == -1)) { TURN_LOG_FUNC( TURN_LOG_LEVEL_INFO, "!!! %s: (1) Trying to send data from closed socket: 0x%lx (1): done=%d, fd=%d, st=%d, sat=%d\n", __FUNCTION__, (long) s, (int) s->done, (int) s->fd, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); } else if (nbh) { if(!ioa_socket_check_bandwidth(s,nbh,0)) { /* Bandwidth exhausted, we pretend everything is fine: */ ret = (int)(ioa_network_buffer_get_size(nbh)); if(skip) *skip = 1; } else { if (!ioa_socket_tobeclosed(s) && s->e) { if (!(s->done || (s->fd == -1))) { set_socket_ttl(s, ttl); set_socket_tos(s, tos); if (s->connected && s->bev) { if ((s->st == TLS_SOCKET)||(s->st == TLS_SCTP_SOCKET)) { #if TLS_SUPPORTED SSL *ctx = bufferevent_openssl_get_ssl(s->bev); if (!ctx || SSL_get_shutdown(ctx)) { s->tobeclosed = 1; ret = 0; } #endif } if (!(s->tobeclosed)) { ret = (int) ioa_network_buffer_get_size(nbh); if (!tcp_congestion_control || is_socket_writeable( s, (size_t) ret, __FUNCTION__, 2)) { s->in_write = 1; if (bufferevent_write(s->bev, ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh)) < 0) { ret = -1; perror("bufev send"); log_socket_event( s, "socket write failed, to be closed", 1); s->tobeclosed = 1; s->broken = 1; } /* bufferevent_flush(s->bev, EV_READ|EV_WRITE, BEV_FLUSH); */ s->in_write = 0; } else { //drop the packet ; } } } else if (s->ssl) { send_ssl_backlog_buffers(s); ret = ssl_send( s, (s08bits*) ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh), ((s->e) && s->e->verbose)); if (ret < 0) s->tobeclosed = 1; else if (ret == 0) add_buffer_to_buffer_list( &(s->bufs), (s08bits*) ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh)); } else if (s->fd >= 0) { if (s->connected && !(s->parent_s)) { dest_addr = NULL; /* ignore dest_addr */ } else if (!dest_addr) { dest_addr = &(s->remote_addr); } ret = udp_send(s, dest_addr, (s08bits*) ioa_network_buffer_data(nbh),ioa_network_buffer_get_size(nbh)); if (ret < 0) { s->tobeclosed = 1; #if defined(EADDRNOTAVAIL) int perr=errno; #endif perror("udp send"); #if defined(EADDRNOTAVAIL) if(dest_addr && (perr==EADDRNOTAVAIL)) { char sfrom[129]; addr_to_string(&(s->local_addr), (u08bits*)sfrom); char sto[129]; addr_to_string(dest_addr, (u08bits*)sto); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: network error: address unreachable from %s to %s\n", __FUNCTION__,sfrom,sto); } #endif } } } } } } ioa_network_buffer_delete(s->e, nbh); return ret; } int send_data_from_ioa_socket_tcp(ioa_socket_handle s, const void *data, size_t sz) { int ret = -1; if(s && data) { if (s->done || (s->fd == -1) || ioa_socket_tobeclosed(s) || !(s->e)) { TURN_LOG_FUNC( TURN_LOG_LEVEL_INFO, "!!! %s: (1) Trying to send data from bad socket: 0x%lx (1): done=%d, fd=%d, st=%d, sat=%d\n", __FUNCTION__, (long) s, (int) s->done, (int) s->fd, s->st, s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); } else if (s->connected && s->bev) { if ((s->st == TLS_SOCKET)||(s->st == TLS_SCTP_SOCKET)) { #if TLS_SUPPORTED SSL *ctx = bufferevent_openssl_get_ssl(s->bev); if (!ctx || SSL_get_shutdown(ctx)) { s->tobeclosed = 1; ret = 0; } #endif } if (!(s->tobeclosed)) { ret = (int)sz; s->in_write = 1; if (bufferevent_write(s->bev, data, sz) < 0) { ret = -1; perror("bufev send"); log_socket_event(s, "socket write failed, to be closed", 1); s->tobeclosed = 1; s->broken = 1; } s->in_write = 0; } } } return ret; } int send_str_from_ioa_socket_tcp(ioa_socket_handle s, const void *data) { if(data) { return send_data_from_ioa_socket_tcp(s, data, strlen((const char*)data)); } else { return 0; } } int send_ulong_from_ioa_socket_tcp(ioa_socket_handle s, size_t data) { char str[129]; snprintf(str,sizeof(str)-1,"%lu",(unsigned long)data); return send_str_from_ioa_socket_tcp(s,str); } int register_callback_on_ioa_socket(ioa_engine_handle e, ioa_socket_handle s, int event_type, ioa_net_event_handler cb, void* ctx, int clean_preexisting) { if(s) { if (event_type & IOA_EV_READ) { if(e) s->e = e; if(s->e && !(s->parent_s)) { switch(s->st) { case DTLS_SOCKET: case UDP_SOCKET: if(s->read_event) { if(!clean_preexisting) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: software error: buffer preset 1\n", __FUNCTION__); return -1; } } else { s->read_event = event_new(s->e->event_base,s->fd, EV_READ|EV_PERSIST, socket_input_handler, s); event_add(s->read_event,NULL); } break; case TENTATIVE_TCP_SOCKET: case TENTATIVE_SCTP_SOCKET: if(s->bev) { if(!clean_preexisting) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: software error: buffer preset 2\n", __FUNCTION__); return -1; } } else if(s->read_event) { if(!clean_preexisting) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: software error: buffer preset 3\n", __FUNCTION__); return -1; } } else { s->read_event = event_new(s->e->event_base,s->fd, EV_READ|EV_PERSIST, socket_input_handler, s); event_add(s->read_event,NULL); } break; case SCTP_SOCKET: case TCP_SOCKET: if(s->bev) { if(!clean_preexisting) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: software error: buffer preset 4\n", __FUNCTION__); return -1; } } else { #if TLS_SUPPORTED if(check_tentative_tls(s->fd)) { s->tobeclosed = 1; return -1; } #endif s->bev = bufferevent_socket_new(s->e->event_base, s->fd, TURN_BUFFEREVENTS_OPTIONS); debug_ptr_add(s->bev); bufferevent_setcb(s->bev, socket_input_handler_bev, socket_output_handler_bev, eventcb_bev, s); bufferevent_setwatermark(s->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK); bufferevent_enable(s->bev, EV_READ|EV_WRITE); /* Start reading. */ } break; case TLS_SCTP_SOCKET: case TLS_SOCKET: if(s->bev) { if(!clean_preexisting) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: software error: buffer preset 5\n", __FUNCTION__); return -1; } } else { #if TLS_SUPPORTED if(!(s->ssl)) { //??? how we can get to this point ??? set_socket_ssl(s,SSL_NEW(e->tls_ctx_ssl23)); s->bev = bufferevent_openssl_socket_new(s->e->event_base, s->fd, s->ssl, BUFFEREVENT_SSL_ACCEPTING, TURN_BUFFEREVENTS_OPTIONS); debug_ptr_add(s->bev); } else { s->bev = bufferevent_openssl_socket_new(s->e->event_base, s->fd, s->ssl, BUFFEREVENT_SSL_OPEN, TURN_BUFFEREVENTS_OPTIONS); debug_ptr_add(s->bev); } bufferevent_setcb(s->bev, socket_input_handler_bev, socket_output_handler_bev, eventcb_bev, s); bufferevent_setwatermark(s->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK); bufferevent_enable(s->bev, EV_READ|EV_WRITE); /* Start reading. */ #endif } break; default: TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: software error: unknown socket type: %d\n", __FUNCTION__,(int)(s->st)); return -1; } } s->read_cb = cb; s->read_ctx = ctx; return 0; } } /* unsupported event or else */ return -1; } int ioa_socket_tobeclosed(ioa_socket_handle s) { if(s) { if(s->magic != SOCKET_MAGIC) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s: magic is wrong on the socket: 0x%lx, st=%d, sat=%d\n",__FUNCTION__,(long)s,s->st,s->sat); return 1; } if(s->done) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s: check on already closed socket: 0x%lx, st=%d, sat=%d\n",__FUNCTION__,(long)s,s->st,s->sat); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s); return 1; } if(s->tobeclosed) { return 1; } else if(s->broken) { s->tobeclosed = 1; log_socket_event(s, "socket broken", 0); return 1; } else if(s->fd < 0) { s->tobeclosed = 1; log_socket_event(s, "socket fd<0", 0); return 1; } else if(s->ssl) { if(SSL_get_shutdown(s->ssl)) { s->tobeclosed = 1; log_socket_event(s, "socket SSL shutdown", 0); return 1; } } } return 0; } void set_ioa_socket_tobeclosed(ioa_socket_handle s) { if(s) s->tobeclosed = 1; } /* * Network buffer functions */ ioa_network_buffer_handle ioa_network_buffer_allocate(ioa_engine_handle e) { stun_buffer_list_elem *buf_elem = new_blist_elem(e); buf_elem->buf.len = 0; buf_elem->buf.offset = 0; buf_elem->buf.coffset = 0; return buf_elem; } /* We do not use special header in this simple implementation */ void ioa_network_buffer_header_init(ioa_network_buffer_handle nbh) { UNUSED_ARG(nbh); } u08bits *ioa_network_buffer_data(ioa_network_buffer_handle nbh) { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; return buf_elem->buf.buf + buf_elem->buf.offset - buf_elem->buf.coffset; } size_t ioa_network_buffer_get_size(ioa_network_buffer_handle nbh) { if(!nbh) return 0; else { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; return (size_t)(buf_elem->buf.len); } } size_t ioa_network_buffer_get_capacity(ioa_network_buffer_handle nbh) { if(!nbh) return 0; else { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; if(buf_elem->buf.offset < STUN_BUFFER_SIZE) { return (STUN_BUFFER_SIZE - buf_elem->buf.offset); } return 0; } } size_t ioa_network_buffer_get_capacity_udp(void) { return UDP_STUN_BUFFER_SIZE; } void ioa_network_buffer_set_size(ioa_network_buffer_handle nbh, size_t len) { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; buf_elem->buf.len=(size_t)len; } void ioa_network_buffer_add_offset_size(ioa_network_buffer_handle nbh, u16bits offset, u08bits coffset, size_t len) { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; buf_elem->buf.len=(size_t)len; buf_elem->buf.offset += offset; buf_elem->buf.coffset += coffset; if((buf_elem->buf.offset + buf_elem->buf.len - buf_elem->buf.coffset)>=sizeof(buf_elem->buf.buf) || (buf_elem->buf.offset + sizeof(buf_elem->buf.channel) < buf_elem->buf.coffset) ) { buf_elem->buf.coffset = 0; buf_elem->buf.len = 0; buf_elem->buf.offset = 0; } } u16bits ioa_network_buffer_get_offset(ioa_network_buffer_handle nbh) { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; return buf_elem->buf.offset; } u08bits ioa_network_buffer_get_coffset(ioa_network_buffer_handle nbh) { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; return buf_elem->buf.coffset; } void ioa_network_buffer_delete(ioa_engine_handle e, ioa_network_buffer_handle nbh) { stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh; free_blist_elem(e,buf_elem); } /////////// REPORTING STATUS ///////////////////// const char* get_ioa_socket_cipher(ioa_socket_handle s) { if(s && s->ssl) { return SSL_get_cipher(s->ssl); } return "no SSL"; } const char* get_ioa_socket_ssl_method(ioa_socket_handle s) { if(s && s->ssl) { return turn_get_ssl_method(s->ssl, "UNKNOWN"); } return "no SSL"; } void turn_report_allocation_set(void *a, turn_time_t lifetime, int refresh) { if(a) { ts_ur_super_session *ss = (ts_ur_super_session*)(((allocation*)a)->owner); if(ss) { const char* status="new"; if(refresh) status="refreshed"; turn_turnserver *server = (turn_turnserver*)ss->server; if(server) { ioa_engine_handle e = turn_server_get_engine(server); if(e && e->verbose && ss->client_socket) { if(ss->client_socket->ssl) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: %s, realm=<%s>, username=<%s>, lifetime=%lu, cipher=%s, method=%s\n", (unsigned long long)ss->id, status, (char*)ss->realm_options.name, (char*)ss->username, (unsigned long)lifetime, SSL_get_cipher(ss->client_socket->ssl), turn_get_ssl_method(ss->client_socket->ssl, "UNKNOWN")); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: %s, realm=<%s>, username=<%s>, lifetime=%lu\n", (unsigned long long)ss->id, status, (char*)ss->realm_options.name, (char*)ss->username, (unsigned long)lifetime); } } #if !defined(TURN_NO_HIREDIS) { char key[1024]; if(ss->realm_options.name[0]) { snprintf(key,sizeof(key),"turn/realm/%s/user/%s/allocation/%018llu/status",ss->realm_options.name,(char*)ss->username, (unsigned long long)ss->id); } else { snprintf(key,sizeof(key),"turn/user/%s/allocation/%018llu/status",(char*)ss->username, (unsigned long long)ss->id); } send_message_to_redis(e->rch, "set", key, "%s lifetime=%lu", status, (unsigned long)lifetime); send_message_to_redis(e->rch, "publish", key, "%s lifetime=%lu", status, (unsigned long)lifetime); } #endif } } } } void turn_report_allocation_delete(void *a) { if(a) { ts_ur_super_session *ss = (ts_ur_super_session*)(((allocation*)a)->owner); if(ss) { turn_turnserver *server = (turn_turnserver*)ss->server; if(server) { ioa_engine_handle e = turn_server_get_engine(server); if(e && e->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: delete: realm=<%s>, username=<%s>\n", (unsigned long long)ss->id, (char*)ss->realm_options.name, (char*)ss->username); } #if !defined(TURN_NO_HIREDIS) { char key[1024]; if(ss->realm_options.name[0]) { snprintf(key,sizeof(key),"turn/realm/%s/user/%s/allocation/%018llu/status",ss->realm_options.name,(char*)ss->username, (unsigned long long)ss->id); } else { snprintf(key,sizeof(key),"turn/user/%s/allocation/%018llu/status",(char*)ss->username, (unsigned long long)ss->id); } send_message_to_redis(e->rch, "del", key, ""); send_message_to_redis(e->rch, "publish", key, "deleted"); // report total traffic usage for this allocation if(ss->realm_options.name[0]) { snprintf(key, sizeof(key), "turn/realm/%s/user/%s/allocation/%018llu/total_traffic", ss->realm_options.name, (char*)ss->username, (unsigned long long)ss->id); } else { snprintf(key, sizeof(key), "turn/user/%s/allocation/%018llu/total_traffic", (char*)ss->username, (unsigned long long)ss->id); } send_message_to_redis(e->rch, "publish", key, "rcvp=%lu, rcvb=%lu, sentp=%lu, sentb=%lu", (unsigned long)(ss->t_received_packets), (unsigned long)(ss->t_received_bytes), (unsigned long)(ss->t_sent_packets), (unsigned long)(ss->t_sent_bytes)); } #endif } } } } void turn_report_session_usage(void *session, int force_invalid) { if(session) { ts_ur_super_session *ss = (ts_ur_super_session *)session; turn_turnserver *server = (turn_turnserver*)ss->server; if(server && (ss->received_packets || ss->sent_packets || force_invalid)) { ioa_engine_handle e = turn_server_get_engine(server); if(((ss->received_packets+ss->sent_packets)&2047)==0 || force_invalid) { if(e && e->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"session %018llu: usage: realm=<%s>, username=<%s>, rp=%lu, rb=%lu, sp=%lu, sb=%lu\n", (unsigned long long)(ss->id), (char*)ss->realm_options.name, (char*)ss->username, (unsigned long)(ss->received_packets), (unsigned long)(ss->received_bytes),(unsigned long)(ss->sent_packets),(unsigned long)(ss->sent_bytes)); } #if !defined(TURN_NO_HIREDIS) { char key[1024]; if(ss->realm_options.name[0]) { snprintf(key,sizeof(key),"turn/realm/%s/user/%s/allocation/%018llu/traffic",ss->realm_options.name,(char*)ss->username, (unsigned long long)(ss->id)); } else { snprintf(key,sizeof(key),"turn/user/%s/allocation/%018llu/traffic",(char*)ss->username, (unsigned long long)(ss->id)); } send_message_to_redis(e->rch, "publish", key, "rcvp=%lu, rcvb=%lu, sentp=%lu, sentb=%lu",(unsigned long)(ss->received_packets), (unsigned long)(ss->received_bytes),(unsigned long)(ss->sent_packets),(unsigned long)(ss->sent_bytes)); } #endif ss->t_received_packets += ss->received_packets; ss->t_received_bytes += ss->received_bytes; ss->t_sent_packets += ss->sent_packets; ss->t_sent_bytes += ss->sent_bytes; { turn_time_t ct = get_turn_server_time(server); if(ct != ss->start_time) { ct = ct - ss->start_time; ss->received_rate = (u32bits)(ss->t_received_bytes / ct); ss->sent_rate = (u32bits)(ss->t_sent_bytes / ct); ss->total_rate = ss->received_rate + ss->sent_rate; } } report_turn_session_info(server,ss,force_invalid); ss->received_packets=0; ss->received_bytes=0; ss->sent_packets=0; ss->sent_bytes=0; } } } } /////////////// SSL /////////////////// const char* get_ioa_socket_tls_cipher(ioa_socket_handle s) { if(s && (s->ssl)) return SSL_get_cipher(s->ssl); return ""; } const char* get_ioa_socket_tls_method(ioa_socket_handle s) { if(s && (s->ssl)) return turn_get_ssl_method(s->ssl,"UNKNOWN"); return ""; } ///////////// Super Memory Region ////////////// #define TURN_SM_SIZE (1024<<11) struct _super_memory { pthread_mutex_t mutex_sm; char **super_memory; size_t *sm_allocated; size_t sm_total_sz; size_t sm_chunk; u32bits id; }; static void init_super_memory_region(super_memory_t *r) { if(r) { ns_bzero(r,sizeof(super_memory_t)); r->super_memory = (char**)turn_malloc(sizeof(char*)); r->super_memory[0] = (char*)turn_malloc(TURN_SM_SIZE); ns_bzero(r->super_memory[0],TURN_SM_SIZE); r->sm_allocated = (size_t*)turn_malloc(sizeof(size_t*)); r->sm_allocated[0] = 0; r->sm_total_sz = TURN_SM_SIZE; r->sm_chunk = 0; while(r->id == 0) r->id = (u32bits)random(); pthread_mutex_init(&r->mutex_sm, NULL); } } void init_super_memory(void) { ; } super_memory_t* new_super_memory_region(void) { super_memory_t* r = (super_memory_t*)turn_malloc(sizeof(super_memory_t)); init_super_memory_region(r); return r; } void* allocate_super_memory_region_func(super_memory_t *r, size_t size, const char* file, const char* func, int line) { UNUSED_ARG(file); UNUSED_ARG(func); UNUSED_ARG(line); void *ret = NULL; if(!r) { ret = turn_malloc(size); ns_bzero(ret, size); return ret; } pthread_mutex_lock(&r->mutex_sm); size = ((size_t)((size+sizeof(void*))/(sizeof(void*)))) * sizeof(void*); if(size>=TURN_SM_SIZE) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"(%s:%s:%d): Size too large for super memory: region id = %u, chunk=%lu, total=%lu, allocated=%lu, want=%lu\n",file,func,line,(unsigned int)r->id, (unsigned long)r->sm_chunk, (unsigned long)r->sm_total_sz, (unsigned long)r->sm_allocated[r->sm_chunk],(unsigned long)size); } else { size_t i = 0; char *region = NULL; size_t *rsz = NULL; for(i=0;i<=r->sm_chunk;++i) { size_t left = (size_t)r->sm_total_sz - r->sm_allocated[i]; if(leftsuper_memory[i]; rsz = r->sm_allocated + i; break; } } if(!region) { r->sm_chunk += 1; r->super_memory = (char**)turn_realloc(r->super_memory,0, (r->sm_chunk+1) * sizeof(char*)); r->super_memory[r->sm_chunk] = (char*)turn_malloc(TURN_SM_SIZE); ns_bzero(r->super_memory[r->sm_chunk],TURN_SM_SIZE); r->sm_allocated = (size_t*)turn_realloc(r->sm_allocated,0,(r->sm_chunk+1) * sizeof(size_t*)); r->sm_allocated[r->sm_chunk] = 0; region = r->super_memory[r->sm_chunk]; rsz = r->sm_allocated + r->sm_chunk; } { char* ptr = region + *rsz; ns_bzero(ptr, size); *rsz += size; ret = ptr; } } pthread_mutex_unlock(&r->mutex_sm); if(!ret) { ret = turn_malloc(size); ns_bzero(ret, size); } return ret; } void* allocate_super_memory_engine_func(ioa_engine_handle e, size_t size, const char* file, const char* func, int line) { if(e) return allocate_super_memory_region_func(e->sm,size,file,func,line); return allocate_super_memory_region_func(NULL,size,file,func,line); } ////////////////////////////////////////////////// turnserver-4.5.1.1/src/apps/relay/ns_ioalib_impl.h0000664000175000017500000002112713436577655020614 0ustar misimisi/* * 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. */ /* * IO Abstraction library */ #ifndef __IOA_LIBIMPL__ #define __IOA_LIBIMPL__ #include #include #include #include #include "ns_turn_openssl.h" #include "ns_turn_ioalib.h" #include "turn_ports.h" #include "ns_turn_maps_rtcp.h" #include "ns_turn_maps.h" #include "ns_turn_server.h" #include "apputils.h" #include "stun_buffer.h" #include "userdb.h" #include "ns_sm.h" #include #ifdef __cplusplus extern "C" { #endif ////////////////////////////////////////////////////// #define MAX_BUFFER_QUEUE_SIZE_PER_ENGINE (64) #define MAX_SOCKET_BUFFER_BACKLOG (16) #define BUFFEREVENT_HIGH_WATERMARK (128<<10) #define BUFFEREVENT_MAX_UDP_TO_TCP_WRITE (64<<9) #define BUFFEREVENT_MAX_TCP_TO_TCP_WRITE (192<<10) typedef struct _stun_buffer_list_elem { struct _stun_buffer_list_elem *next; stun_buffer buf; } stun_buffer_list_elem; typedef struct _stun_buffer_list { stun_buffer_list_elem *head; size_t tsz; } stun_buffer_list; /* * New connection callback */ struct cb_socket_message { turnserver_id id; tcp_connection_id connection_id; stun_tid tid; ioa_socket_handle s; int message_integrity; ioa_net_data nd; int can_resume; }; struct cancelled_session_message { turnsession_id id; }; struct relay_server { turnserver_id id; super_memory_t* sm; struct event_base* event_base; struct bufferevent *in_buf; struct bufferevent *out_buf; struct bufferevent *auth_in_buf; struct bufferevent *auth_out_buf; ioa_engine_handle ioa_eng; turn_turnserver server; pthread_t thr; }; struct message_to_relay { MESSAGE_TO_RELAY_TYPE t; struct relay_server *relay_server; union { struct socket_message sm; struct cb_socket_message cb_sm; struct cancelled_session_message csm; } m; }; struct relay_server; typedef struct relay_server *relay_server_handle; typedef int (*ioa_engine_new_connection_event_handler)(ioa_engine_handle e, struct message_to_relay *sm); typedef int (*ioa_engine_udp_event_handler)(relay_server_handle rs, struct message_to_relay *sm); #define TURN_CMSG_SZ (65536) #define PREDEF_TIMERS_NUM (14) extern const int predef_timer_intervals[PREDEF_TIMERS_NUM]; struct _ioa_engine { super_memory_t *sm; struct event_base *event_base; int deallocate_eb; int verbose; turnipports* tp; rtcp_map *map_rtcp; stun_buffer_list bufs; SSL_CTX *tls_ctx_ssl23; SSL_CTX *tls_ctx_v1_0; #if TLSv1_1_SUPPORTED SSL_CTX *tls_ctx_v1_1; #if TLSv1_2_SUPPORTED SSL_CTX *tls_ctx_v1_2; #endif #endif #if DTLS_SUPPORTED SSL_CTX *dtls_ctx; #endif #if DTLSv1_2_SUPPORTED SSL_CTX *dtls_ctx_v1_2; #endif turn_time_t jiffie; /* bandwidth check interval */ ioa_timer_handle timer_ev; s08bits cmsg[TURN_CMSG_SZ+1]; int predef_timer_intervals[PREDEF_TIMERS_NUM]; struct timeval predef_timers[PREDEF_TIMERS_NUM]; /* Relays */ s08bits relay_ifname[1025]; int default_relays; size_t relays_number; size_t relay_addr_counter; ioa_addr *relay_addrs; redis_context_handle rch; }; #define SOCKET_MAGIC (0xABACADEF) struct traffic_bytes { band_limit_t jiffie_bytes_read; band_limit_t jiffie_bytes_write; }; struct _ioa_socket { evutil_socket_t fd; struct _ioa_socket *parent_s; u32bits magic; ur_addr_map *sockets_container; /* relay container for UDP sockets */ struct bufferevent *bev; ioa_network_buffer_handle defer_nbh; int family; SOCKET_TYPE st; SOCKET_APP_TYPE sat; SSL* ssl; u32bits ssl_renegs; int in_write; int bound; int local_addr_known; ioa_addr local_addr; int connected; ioa_addr remote_addr; ioa_engine_handle e; struct event *read_event; ioa_net_event_handler read_cb; void *read_ctx; int done; ts_ur_super_session* session; int current_df_relay_flag; /* RFC6156: if IPv6 is involved, do not use DF: */ int do_not_use_df; int tobeclosed; int broken; int default_ttl; int current_ttl; int default_tos; int current_tos; stun_buffer_list bufs; turn_time_t jiffie; /* bandwidth check interval */ struct traffic_bytes data_traffic; struct traffic_bytes control_traffic; /* RFC 6062 ==>> */ //Connection session: tcp_connection *sub_session; //Connect: struct bufferevent *conn_bev; connect_cb conn_cb; void *conn_arg; //Accept: struct evconnlistener *list_ev; accept_cb acb; void *acbarg; /* <<== RFC 6062 */ void *special_session; size_t special_session_size; }; typedef struct _timer_event { struct event *ev; ioa_engine_handle e; ioa_timer_event_handler cb; void *ctx; s08bits* txt; } timer_event; /////////////////////////////////// /* realm */ void create_default_realm(void); int get_realm_data(char* name, realm_params_t* rp); /* engine handling */ ioa_engine_handle create_ioa_engine(super_memory_t *sm, struct event_base *eb, turnipports* tp, const s08bits* relay_if, size_t relays_number, s08bits **relay_addrs, int default_relays, int verbose #if !defined(TURN_NO_HIREDIS) ,const char* redis_report_connection_string #endif ); void ioa_engine_set_rtcp_map(ioa_engine_handle e, rtcp_map *rtcpmap); ioa_socket_handle create_ioa_socket_from_fd(ioa_engine_handle e, ioa_socket_raw fd, ioa_socket_handle parent_s, SOCKET_TYPE st, SOCKET_APP_TYPE sat, const ioa_addr *remote_addr, const ioa_addr *local_addr); ioa_socket_handle create_ioa_socket_from_ssl(ioa_engine_handle e, ioa_socket_handle parent_s, SSL* ssl, SOCKET_TYPE st, SOCKET_APP_TYPE sat, const ioa_addr *remote_addr, const ioa_addr *local_addr); int get_a_local_relay(int family, ioa_addr *relay_addr); void add_socket_to_parent(ioa_socket_handle parent_s, ioa_socket_handle s); void delete_socket_from_parent(ioa_socket_handle s); void add_socket_to_map(ioa_socket_handle s, ur_addr_map *amap); void delete_socket_from_map(ioa_socket_handle s); int is_connreset(void); int would_block(void); int udp_send(ioa_socket_handle s, const ioa_addr* dest_addr, const s08bits* buffer, int len); int udp_recvfrom(evutil_socket_t fd, ioa_addr* orig_addr, const ioa_addr *like_addr, s08bits* buffer, int buf_size, int *ttl, int *tos, s08bits *ecmsg, int flags, u32bits *errcode); int ssl_read(evutil_socket_t fd, SSL* ssl, ioa_network_buffer_handle nbh, int verbose); int set_raw_socket_ttl_options(evutil_socket_t fd, int family); int set_raw_socket_tos_options(evutil_socket_t fd, int family); int set_socket_options_fd(evutil_socket_t fd, SOCKET_TYPE st, int family); int set_socket_options(ioa_socket_handle s); int send_session_cancellation_to_relay(turnsession_id sid); int send_data_from_ioa_socket_tcp(ioa_socket_handle s, const void *data, size_t sz); int send_str_from_ioa_socket_tcp(ioa_socket_handle s, const void *data); int send_ulong_from_ioa_socket_tcp(ioa_socket_handle s, size_t data); int ioa_socket_check_bandwidth(ioa_socket_handle s, ioa_network_buffer_handle nbh, int read); ///////////////////////// SUPER MEMORY //////// #define allocate_super_memory_engine(e,size) allocate_super_memory_engine_func(e, size, __FILE__, __FUNCTION__, __LINE__) void* allocate_super_memory_engine_func(ioa_engine_handle e, size_t size, const char* file, const char* func, int line); ///////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif /* __IOA_LIBIMPL__ */ turnserver-4.5.1.1/src/apps/relay/libtelnet.c0000664000175000017500000011740413436577655017615 0ustar misimisi/* * libtelnet - TELNET protocol handling library * * Sean Middleditch * sean@sourcemud.org * * The author or authors of this code dedicate any and all copyright interest * in this code to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and successors. We * intend this dedication to be an overt act of relinquishment in perpetuity of * all present and future rights to this code under copyright law. */ /** * Minor fixes by Oleg Moskalenko */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include /* Win32 compatibility */ #if defined(_WIN32) # define vsnprintf _vsnprintf # define __func__ __FUNCTION__ # define ZLIB_WINAPI 1 #endif #if defined(HAVE_ZLIB) # include #endif #include "libtelnet.h" /* inlinable functions */ #if defined(__GNUC__) || __STDC_VERSION__ >= 199901L # define INLINE __inline__ #else # define INLINE #endif /* helper for Q-method option tracking */ #define Q_US(q) ((q).state & 0x0F) #define Q_HIM(q) (((q).state & 0xF0) >> 4) #define Q_MAKE(us,him) ((us) | ((him) << 4)) /* helper for the negotiation routines */ #define NEGOTIATE_EVENT(telnet,cmd,opt) \ ev.type = (cmd); \ ev.neg.telopt = (opt); \ (telnet)->eh((telnet), &ev, (telnet)->ud); /* telnet state codes */ enum telnet_state_t { TELNET_STATE_DATA = 0, TELNET_STATE_IAC, TELNET_STATE_WILL, TELNET_STATE_WONT, TELNET_STATE_DO, TELNET_STATE_DONT, TELNET_STATE_SB, TELNET_STATE_SB_DATA, TELNET_STATE_SB_DATA_IAC }; typedef enum telnet_state_t telnet_state_t; /* telnet state tracker */ struct telnet_t { /* user data */ void *ud; /* telopt support table */ const telnet_telopt_t *telopts; /* event handler */ telnet_event_handler_t eh; #if defined(HAVE_ZLIB) /* zlib (mccp2) compression */ z_stream *z; #endif /* RFC1143 option negotiation states */ struct telnet_rfc1143_t *q; /* sub-request buffer */ char *buffer; /* current size of the buffer */ size_t buffer_size; /* current buffer write position (also length of buffer data) */ size_t buffer_pos; /* current state */ enum telnet_state_t state; /* option flags */ unsigned char flags; /* current subnegotiation telopt */ unsigned char sb_telopt; /* length of RFC1143 queue */ unsigned char q_size; }; /* RFC1143 option negotiation state */ typedef struct telnet_rfc1143_t { unsigned char telopt; unsigned char state; } telnet_rfc1143_t; /* RFC1143 state names */ #define Q_NO 0 #define Q_YES 1 #define Q_WANTNO 2 #define Q_WANTYES 3 #define Q_WANTNO_OP 4 #define Q_WANTYES_OP 5 /* buffer sizes */ static const size_t _buffer_sizes[] = { 0, 512, 2048, 8192, 16384, }; static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) / sizeof(_buffer_sizes[0]); /* error generation function */ static telnet_error_t _error(telnet_t *telnet, unsigned line, const char* func, telnet_error_t err, int fatal, const char *fmt, ...) { telnet_event_t ev; char buffer[512]; va_list va; /* format informational text */ va_start(va, fmt); vsnprintf(buffer, sizeof(buffer), fmt, va); va_end(va); /* send error event to the user */ ev.type = fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING; ev.error.file = __FILE__; ev.error.func = func; ev.error.line = line; ev.error.msg = buffer; telnet->eh(telnet, &ev, telnet->ud); return err; } #if defined(HAVE_ZLIB) /* initialize the zlib box for a telnet box; if deflate is non-zero, it * initializes zlib for delating (compression), otherwise for inflating * (decompression). returns TELNET_EOK on success, something else on * failure. */ telnet_error_t _init_zlib(telnet_t *telnet, int deflate, int err_fatal) { z_stream *z; int rs; /* if compression is already enabled, fail loudly */ if (telnet->z != 0) return _error(telnet, __LINE__, __func__, TELNET_EBADVAL, err_fatal, "cannot initialize compression twice"); /* allocate zstream box */ if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0) return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal, "malloc() failed: %s", strerror(errno)); /* initialize */ if (deflate) { if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) { free(z); return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, err_fatal, "deflateInit() failed: %s", zError(rs)); } telnet->flags |= TELNET_PFLAG_DEFLATE; } else { if ((rs = inflateInit(z)) != Z_OK) { free(z); return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, err_fatal, "inflateInit() failed: %s", zError(rs)); } telnet->flags &= ~TELNET_PFLAG_DEFLATE; } telnet->z = z; return TELNET_EOK; } #endif /* defined(HAVE_ZLIB) */ /* push bytes out, compressing them first if need be */ static void _send(telnet_t *telnet, const char *buffer, size_t size) { telnet_event_t ev; #if defined(HAVE_ZLIB) /* if we have a deflate (compression) zlib box, use it */ if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) { char deflate_buffer[1024]; int rs; /* initialize z state */ telnet->z->next_in = (unsigned char *)buffer; telnet->z->avail_in = size; telnet->z->next_out = (unsigned char *)deflate_buffer; telnet->z->avail_out = sizeof(deflate_buffer); /* deflate until buffer exhausted and all output is produced */ while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) { /* compress */ if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) { _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1, "deflate() failed: %s", zError(rs)); deflateEnd(telnet->z); free(telnet->z); telnet->z = 0; break; } /* send event */ ev.type = TELNET_EV_SEND; ev.data.buffer = deflate_buffer; ev.data.size = sizeof(deflate_buffer) - telnet->z->avail_out; telnet->eh(telnet, &ev, telnet->ud); /* prepare output buffer for next run */ telnet->z->next_out = (unsigned char *)deflate_buffer; telnet->z->avail_out = sizeof(deflate_buffer); } /* do not continue with remaining code */ return; } #endif /* defined(HAVE_ZLIB) */ ev.type = TELNET_EV_SEND; ev.data.buffer = buffer; ev.data.size = size; telnet->eh(telnet, &ev, telnet->ud); } /* to send bags of unsigned chars */ #define _sendu(t, d, s) _send((t), (const char*)(d), (s)) /* check if we support a particular telopt; if us is non-zero, we * check if we (local) supports it, otherwise we check if he (remote) * supports it. return non-zero if supported, zero if not supported. */ static INLINE int _check_telopt(telnet_t *telnet, unsigned char telopt, int us) { int i; /* if we have no telopts table, we obviously don't support it */ if (telnet->telopts == 0) return 0; /* loop unti found or end marker (us and him both 0) */ for (i = 0; telnet->telopts[i].telopt != -1; ++i) { if (telnet->telopts[i].telopt == telopt) { if (us && telnet->telopts[i].us == TELNET_WILL) return 1; else if (!us && telnet->telopts[i].him == TELNET_DO) return 1; else return 0; } } /* not found, so not supported */ return 0; } /* retrieve RFC1143 option state */ static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet, unsigned char telopt) { telnet_rfc1143_t empty; int i; /* search for entry */ for (i = 0; i != telnet->q_size; ++i) { if (telnet->q[i].telopt == telopt) { return telnet->q[i]; } } /* not found, return empty value */ empty.telopt = telopt; empty.state = 0; return empty; } /* save RFC1143 option state */ static INLINE void _set_rfc1143(telnet_t *telnet, unsigned char telopt, char us, char him) { telnet_rfc1143_t *qtmp; int i; /* search for entry */ for (i = 0; i != telnet->q_size; ++i) { if (telnet->q[i].telopt == telopt) { telnet->q[i].state = Q_MAKE(us,him); return; } } /* we're going to need to track state for it, so grow the queue * by 4 (four) elements and put the telopt into it; bail on allocation * error. we go by four because it seems like a reasonable guess as * to the number of enabled options for most simple code, and it * allows for an acceptable number of reallocations for complex code. */ if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q, sizeof(telnet_rfc1143_t) * (telnet->q_size + 4))) == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "realloc() failed: %s", strerror(errno)); return; } memset(&qtmp[telnet->q_size], 0, sizeof(telnet_rfc1143_t) * 4); telnet->q = qtmp; telnet->q[telnet->q_size].telopt = telopt; telnet->q[telnet->q_size].state = Q_MAKE(us, him); telnet->q_size += 4; } /* send negotiation bytes */ static INLINE void _send_negotiate(telnet_t *telnet, unsigned char cmd, unsigned char telopt) { unsigned char bytes[3]; bytes[0] = TELNET_IAC; bytes[1] = cmd; bytes[2] = telopt; _sendu(telnet, bytes, 3); } /* negotiation handling magic for RFC1143 */ static void _negotiate(telnet_t *telnet, unsigned char telopt) { telnet_event_t ev; telnet_rfc1143_t q; /* in PROXY mode, just pass it thru and do nothing */ if (telnet->flags & TELNET_FLAG_PROXY) { switch ((int)telnet->state) { case TELNET_STATE_WILL: NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); break; case TELNET_STATE_WONT: NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt); break; case TELNET_STATE_DO: NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); break; case TELNET_STATE_DONT: NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt); break; } return; } /* lookup the current state of the option */ q = _get_rfc1143(telnet, telopt); /* start processing... */ switch ((int)telnet->state) { /* request to enable option on remote end or confirm DO */ case TELNET_STATE_WILL: switch (Q_HIM(q)) { case Q_NO: if (_check_telopt(telnet, telopt, 0)) { _set_rfc1143(telnet, telopt, Q_US(q), Q_YES); _send_negotiate(telnet, TELNET_DO, telopt); NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); } else _send_negotiate(telnet, TELNET_DONT, telopt); break; case Q_WANTNO: _set_rfc1143(telnet, telopt, Q_US(q), Q_NO); NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt); _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "DONT answered by WILL"); break; case Q_WANTNO_OP: _set_rfc1143(telnet, telopt, Q_US(q), Q_YES); NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "DONT answered by WILL"); break; case Q_WANTYES: _set_rfc1143(telnet, telopt, Q_US(q), Q_YES); NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); break; case Q_WANTYES_OP: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO); _send_negotiate(telnet, TELNET_DONT, telopt); NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); break; } break; /* request to disable option on remote end, confirm DONT, reject DO */ case TELNET_STATE_WONT: switch (Q_HIM(q)) { case Q_YES: _set_rfc1143(telnet, telopt, Q_US(q), Q_NO); _send_negotiate(telnet, TELNET_DONT, telopt); NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt); break; case Q_WANTNO: _set_rfc1143(telnet, telopt, Q_US(q), Q_NO); NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt); break; case Q_WANTNO_OP: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES); NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); break; case Q_WANTYES: case Q_WANTYES_OP: _set_rfc1143(telnet, telopt, Q_US(q), Q_NO); break; } break; /* request to enable option on local end or confirm WILL */ case TELNET_STATE_DO: switch (Q_US(q)) { case Q_NO: if (_check_telopt(telnet, telopt, 1)) { _set_rfc1143(telnet, telopt, Q_YES, Q_HIM(q)); _send_negotiate(telnet, TELNET_WILL, telopt); NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); } else _send_negotiate(telnet, TELNET_WONT, telopt); break; case Q_WANTNO: _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q)); NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt); _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "WONT answered by DO"); break; case Q_WANTNO_OP: _set_rfc1143(telnet, telopt, Q_YES, Q_HIM(q)); NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "WONT answered by DO"); break; case Q_WANTYES: _set_rfc1143(telnet, telopt, Q_YES, Q_HIM(q)); NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); break; case Q_WANTYES_OP: _set_rfc1143(telnet, telopt, Q_WANTNO, Q_HIM(q)); _send_negotiate(telnet, TELNET_WONT, telopt); NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); break; } break; /* request to disable option on local end, confirm WONT, reject WILL */ case TELNET_STATE_DONT: switch (Q_US(q)) { case Q_YES: _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q)); _send_negotiate(telnet, TELNET_WONT, telopt); NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt); break; case Q_WANTNO: _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q)); NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt); break; case Q_WANTNO_OP: _set_rfc1143(telnet, telopt, Q_WANTYES, Q_HIM(q)); _send_negotiate(telnet, TELNET_WILL, telopt); NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); break; case Q_WANTYES: case Q_WANTYES_OP: _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q)); break; } break; } } /* process an ENVIRON/NEW-ENVIRON subnegotiation buffer * * the algorithm and approach used here is kind of a hack, * but it reduces the number of memory allocations we have * to make. * * we copy the bytes back into the buffer, starting at the very * beginning, which makes it easy to handle the ENVIRON ESC * escape mechanism as well as ensure the variable name and * value strings are NUL-terminated, all while fitting inside * of the original buffer. */ static int _environ_telnet(telnet_t *telnet, unsigned char type, char* buffer, size_t size) { telnet_event_t ev; struct telnet_environ_t *values = 0; char *c, *last, *out; size_t index, count; /* if we have no data, just pass it through */ if (size == 0) { return 0; } /* first byte must be a valid command */ if ((unsigned)buffer[0] != TELNET_ENVIRON_SEND && (unsigned)buffer[0] != TELNET_ENVIRON_IS && (unsigned)buffer[0] != TELNET_ENVIRON_INFO) { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "telopt %d subneg has invalid command", type); return 0; } /* store ENVIRON command */ ev.environ.cmd = buffer[0]; /* if we have no arguments, send an event with no data end return */ if (size == 1) { /* no list of variables given */ ev.environ.values = 0; ev.environ.size = 0; /* invoke event with our arguments */ ev.type = TELNET_EV_ENVIRON; telnet->eh(telnet, &ev, telnet->ud); return 1; } /* very second byte must be VAR or USERVAR, if present */ if ((unsigned)buffer[1] != TELNET_ENVIRON_VAR && (unsigned)buffer[1] != TELNET_ENVIRON_USERVAR) { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "telopt %d subneg missing variable type", type); return 0; } /* ensure last byte is not an escape byte (makes parsing later easier) */ if ((unsigned)buffer[size - 1] == TELNET_ENVIRON_ESC) { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "telopt %d subneg ends with ESC", type); return 0; } /* count arguments; each valid entry starts with VAR or USERVAR */ count = 0; for (c = buffer + 1; c < buffer + size; ++c) { if (*c == TELNET_ENVIRON_VAR || *c == TELNET_ENVIRON_USERVAR) { ++count; } else if (*c == TELNET_ENVIRON_ESC) { /* skip the next byte */ ++c; } } /* allocate argument array, bail on error */ if ((values = (struct telnet_environ_t *)calloc(count, sizeof(struct telnet_environ_t))) == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "calloc() failed: %s", strerror(errno)); return 0; } /* parse argument array strings */ out = buffer; c = buffer + 1; for (index = 0; index != count; ++index) { /* remember the variable type (will be VAR or USERVAR) */ values[index].type = *c++; /* scan until we find an end-marker, and buffer up unescaped * bytes into our buffer */ last = out; while (c < buffer + size) { /* stop at the next variable or at the value */ if ((unsigned)*c == TELNET_ENVIRON_VAR || (unsigned)*c == TELNET_ENVIRON_VALUE || (unsigned)*c == TELNET_ENVIRON_USERVAR) { break; } /* buffer next byte (taking into account ESC) */ if (*c == TELNET_ENVIRON_ESC) { ++c; } *out++ = *c++; } *out++ = '\0'; /* store the variable name we have just received */ values[index].var = last; values[index].value = ""; /* if we got a value, find the next end marker and * store the value; otherwise, store empty string */ if (c < buffer + size && *c == TELNET_ENVIRON_VALUE) { ++c; last = out; while (c < buffer + size) { /* stop when we find the start of the next variable */ if ((unsigned)*c == TELNET_ENVIRON_VAR || (unsigned)*c == TELNET_ENVIRON_USERVAR) { break; } /* buffer next byte (taking into account ESC) */ if (*c == TELNET_ENVIRON_ESC) { ++c; } *out++ = *c++; } *out++ = '\0'; /* store the variable value */ values[index].value = last; } } /* pass values array and count to event */ ev.environ.values = values; ev.environ.size = count; /* invoke event with our arguments */ ev.type = TELNET_EV_ENVIRON; telnet->eh(telnet, &ev, telnet->ud); /* clean up */ free(values); return 1; } /* process an MSSP subnegotiation buffer */ static int _mssp_telnet(telnet_t *telnet, char* buffer, size_t size) { telnet_event_t ev; struct telnet_environ_t *values; char *var = 0; char *c, *last, *out; size_t i, count; unsigned char next_type; /* if we have no data, just pass it through */ if (size == 0) { return 0; } /* first byte must be a VAR */ if ((unsigned)buffer[0] != TELNET_MSSP_VAR) { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "MSSP subnegotiation has invalid data"); return 0; } /* count the arguments, any part that starts with VALUE */ for (count = 0, i = 0; i != size; ++i) { if ((unsigned)buffer[i] == TELNET_MSSP_VAL) { ++count; } } /* allocate argument array, bail on error */ if ((values = (struct telnet_environ_t *)calloc(count, sizeof(struct telnet_environ_t))) == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "calloc() failed: %s", strerror(errno)); return 0; } ev.mssp.values = values; ev.mssp.size = count; /* allocate strings in argument array */ out = last = buffer; next_type = buffer[0]; for (i = 0, c = buffer + 1; c < buffer + size;) { /* search for end marker */ while (c < buffer + size && (unsigned)*c != TELNET_MSSP_VAR && (unsigned)*c != TELNET_MSSP_VAL) { *out++ = *c++; } *out++ = '\0'; /* if it's a variable name, just store the name for now */ if (next_type == TELNET_MSSP_VAR) { var = last; } else if (next_type == TELNET_MSSP_VAL && var != 0) { values[i].var = var; values[i].value = last; ++i; } else { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "invalid MSSP subnegotiation data"); free(values); return 0; } /* remember our next type and increment c for next loop run */ last = out; next_type = *c++; } /* invoke event with our arguments */ ev.type = TELNET_EV_MSSP; telnet->eh(telnet, &ev, telnet->ud); /* clean up */ free(values); return 0; } /* parse ZMP command subnegotiation buffers */ static int _zmp_telnet(telnet_t *telnet, const char* buffer, size_t size) { telnet_event_t ev; const char **argv; const char *c; size_t i, argc; /* make sure this is a valid ZMP buffer */ if (size == 0 || buffer[size - 1] != 0) { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "incomplete ZMP frame"); return 0; } /* count arguments */ for (argc = 0, c = buffer; c != buffer + size; ++argc) c += strlen(c) + 1; /* allocate argument array, bail on error */ if ((argv = (const char **)calloc(argc, sizeof(char *))) == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "calloc() failed: %s", strerror(errno)); return 0; } /* populate argument array */ for (i = 0, c = buffer; i != argc; ++i) { argv[i] = c; c += strlen(c) + 1; } /* invoke event with our arguments */ ev.type = TELNET_EV_ZMP; ev.zmp.argv = argv; ev.zmp.argc = argc; telnet->eh(telnet, &ev, telnet->ud); /* clean up */ free(argv); return 0; } /* parse TERMINAL-TYPE command subnegotiation buffers */ static int _ttype_telnet(telnet_t *telnet, const char* buffer, size_t size) { telnet_event_t ev; /* make sure request is not empty */ if (size == 0) { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "incomplete TERMINAL-TYPE request"); return 0; } /* make sure request has valid command type */ if (buffer[0] != TELNET_TTYPE_IS && buffer[0] != TELNET_TTYPE_SEND) { _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "TERMINAL-TYPE request has invalid type"); return 0; } /* send proper event */ if (buffer[0] == TELNET_TTYPE_IS) { char *name; /* allocate space for name */ if ((name = (char *)malloc(size)) == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "malloc() failed: %s", strerror(errno)); return 0; } memcpy(name, buffer + 1, size - 1); name[size - 1] = '\0'; ev.type = TELNET_EV_TTYPE; ev.ttype.cmd = TELNET_TTYPE_IS; ev.ttype.name = name; telnet->eh(telnet, &ev, telnet->ud); /* clean up */ free(name); } else { ev.type = TELNET_EV_TTYPE; ev.ttype.cmd = TELNET_TTYPE_SEND; ev.ttype.name = 0; telnet->eh(telnet, &ev, telnet->ud); } return 0; } /* process a subnegotiation buffer; return non-zero if the current buffer * must be aborted and reprocessed due to COMPRESS2 being activated */ static int _subnegotiate(telnet_t *telnet) { telnet_event_t ev; /* standard subnegotiation event */ ev.type = TELNET_EV_SUBNEGOTIATION; ev.sub.telopt = telnet->sb_telopt; ev.sub.buffer = telnet->buffer; ev.sub.size = telnet->buffer_pos; telnet->eh(telnet, &ev, telnet->ud); switch (telnet->sb_telopt) { #if defined(HAVE_ZLIB) /* received COMPRESS2 begin marker, setup our zlib box and * start handling the compressed stream if it's not already. */ case TELNET_TELOPT_COMPRESS2: if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) { if (_init_zlib(telnet, 0, 1) != TELNET_EOK) return 0; /* notify app that compression was enabled */ ev.type = TELNET_EV_COMPRESS; ev.compress.state = 1; telnet->eh(telnet, &ev, telnet->ud); return 1; } return 0; #endif /* defined(HAVE_ZLIB) */ /* specially handled subnegotiation telopt types */ case TELNET_TELOPT_ZMP: return _zmp_telnet(telnet, telnet->buffer, telnet->buffer_pos); case TELNET_TELOPT_TTYPE: return _ttype_telnet(telnet, telnet->buffer, telnet->buffer_pos); case TELNET_TELOPT_ENVIRON: case TELNET_TELOPT_NEW_ENVIRON: return _environ_telnet(telnet, telnet->sb_telopt, telnet->buffer, telnet->buffer_pos); case TELNET_TELOPT_MSSP: return _mssp_telnet(telnet, telnet->buffer, telnet->buffer_pos); default: return 0; } } /* initialize a telnet state tracker */ telnet_t *telnet_init(const telnet_telopt_t *telopts, telnet_event_handler_t eh, unsigned char flags, void *user_data) { /* allocate structure */ struct telnet_t *telnet = (telnet_t*)calloc(1, sizeof(telnet_t)); if (telnet == 0) return 0; /* initialize data */ telnet->ud = user_data; telnet->telopts = telopts; telnet->eh = eh; telnet->flags = flags; return telnet; } /* free up any memory allocated by a state tracker */ void telnet_free(telnet_t *telnet) { /* free sub-request buffer */ if (telnet->buffer != 0) { free(telnet->buffer); telnet->buffer = 0; telnet->buffer_size = 0; telnet->buffer_pos = 0; } #if defined(HAVE_ZLIB) /* free zlib box */ if (telnet->z != 0) { if (telnet->flags & TELNET_PFLAG_DEFLATE) deflateEnd(telnet->z); else inflateEnd(telnet->z); free(telnet->z); telnet->z = 0; } #endif /* defined(HAVE_ZLIB) */ /* free RFC1143 queue */ if (telnet->q) { free(telnet->q); telnet->q = 0; telnet->q_size = 0; } /* free the telnet structure itself */ free(telnet); } /* push a byte into the telnet buffer */ static telnet_error_t _buffer_byte(telnet_t *telnet, unsigned char byte) { char *new_buffer; size_t i; /* check if we're out of room */ if (telnet->buffer_pos == telnet->buffer_size) { /* find the next buffer size */ for (i = 0; i != _buffer_sizes_count; ++i) { if (_buffer_sizes[i] == telnet->buffer_size) { break; } } /* overflow -- can't grow any more */ if (i >= _buffer_sizes_count - 1) { _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0, "subnegotiation buffer size limit reached"); return TELNET_EOVERFLOW; } /* (re)allocate buffer */ new_buffer = (char *)realloc(telnet->buffer, _buffer_sizes[i + 1]); if (new_buffer == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "realloc() failed"); return TELNET_ENOMEM; } telnet->buffer = new_buffer; telnet->buffer_size = _buffer_sizes[i + 1]; } /* push the byte, all set */ telnet->buffer[telnet->buffer_pos++] = byte; return TELNET_EOK; } static void _process(telnet_t *telnet, const char *buffer, size_t size) { telnet_event_t ev; unsigned char byte; size_t i, start; for (i = start = 0; i != size; ++i) { byte = buffer[i]; switch (telnet->state) { /* regular data */ case TELNET_STATE_DATA: /* on an IAC byte, pass through all pending bytes and * switch states */ if (byte == TELNET_IAC) { if (i != start) { ev.type = TELNET_EV_DATA; ev.data.buffer = buffer + start; ev.data.size = i - start; telnet->eh(telnet, &ev, telnet->ud); } telnet->state = TELNET_STATE_IAC; } break; /* IAC command */ case TELNET_STATE_IAC: switch (byte) { /* subnegotiation */ case TELNET_SB: telnet->state = TELNET_STATE_SB; break; /* negotiation commands */ case TELNET_WILL: telnet->state = TELNET_STATE_WILL; break; case TELNET_WONT: telnet->state = TELNET_STATE_WONT; break; case TELNET_DO: telnet->state = TELNET_STATE_DO; break; case TELNET_DONT: telnet->state = TELNET_STATE_DONT; break; /* IAC escaping */ case TELNET_IAC: /* event */ ev.type = TELNET_EV_DATA; ev.data.buffer = (char*)&byte; ev.data.size = 1; telnet->eh(telnet, &ev, telnet->ud); /* state update */ start = i + 1; telnet->state = TELNET_STATE_DATA; break; /* some other command */ default: /* event */ ev.type = TELNET_EV_IAC; ev.iac.cmd = byte; telnet->eh(telnet, &ev, telnet->ud); /* state update */ start = i + 1; telnet->state = TELNET_STATE_DATA; } break; /* negotiation commands */ case TELNET_STATE_WILL: case TELNET_STATE_WONT: case TELNET_STATE_DO: case TELNET_STATE_DONT: _negotiate(telnet, byte); start = i + 1; telnet->state = TELNET_STATE_DATA; break; /* subnegotiation -- determine subnegotiation telopt */ case TELNET_STATE_SB: telnet->sb_telopt = byte; telnet->buffer_pos = 0; telnet->state = TELNET_STATE_SB_DATA; break; /* subnegotiation -- buffer bytes until end request */ case TELNET_STATE_SB_DATA: /* IAC command in subnegotiation -- either IAC SE or IAC IAC */ if (byte == TELNET_IAC) { telnet->state = TELNET_STATE_SB_DATA_IAC; /* buffer the byte, or bail if we can't */ } else if (_buffer_byte(telnet, byte) != TELNET_EOK) { start = i + 1; telnet->state = TELNET_STATE_DATA; } break; /* IAC escaping inside a subnegotiation */ case TELNET_STATE_SB_DATA_IAC: switch (byte) { /* end subnegotiation */ case TELNET_SE: /* return to default state */ start = i + 1; telnet->state = TELNET_STATE_DATA; /* process subnegotiation */ if (_subnegotiate(telnet) != 0) { /* any remaining bytes in the buffer are compressed. * we have to re-invoke telnet_recv to get those * bytes inflated and abort trying to process the * remaining compressed bytes in the current _process * buffer argument */ telnet_recv(telnet, &buffer[start], size - start); return; } break; /* escaped IAC byte */ case TELNET_IAC: /* push IAC into buffer */ if (_buffer_byte(telnet, TELNET_IAC) != TELNET_EOK) { start = i + 1; telnet->state = TELNET_STATE_DATA; } else { telnet->state = TELNET_STATE_SB_DATA; } break; /* something else -- protocol error. attempt to process * content in subnegotiation buffer, then evaluate the * given command as an IAC code. */ default: _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, "unexpected byte after IAC inside SB: %d", byte); /* enter IAC state */ start = i + 1; telnet->state = TELNET_STATE_IAC; /* process subnegotiation; see comment in * TELNET_STATE_SB_DATA_IAC about invoking telnet_recv() */ if (_subnegotiate(telnet) != 0) { telnet_recv(telnet, &buffer[start], size - start); return; } else { /* recursive call to get the current input byte processed * as a regular IAC command. we could use a goto, but * that would be gross. */ _process(telnet, (char *)&byte, 1); } break; } break; } } /* pass through any remaining bytes */ if (telnet->state == TELNET_STATE_DATA && i != start) { ev.type = TELNET_EV_DATA; ev.data.buffer = buffer + start; ev.data.size = i - start; telnet->eh(telnet, &ev, telnet->ud); } } /* push a bytes into the state tracker */ void telnet_recv(telnet_t *telnet, const char *buffer, size_t size) { #if defined(HAVE_ZLIB) /* if we have an inflate (decompression) zlib stream, use it */ if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) { char inflate_buffer[1024]; int rs; /* initialize zlib state */ telnet->z->next_in = (unsigned char*)buffer; telnet->z->avail_in = size; telnet->z->next_out = (unsigned char *)inflate_buffer; telnet->z->avail_out = sizeof(inflate_buffer); /* inflate until buffer exhausted and all output is produced */ while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) { /* reset output buffer */ /* decompress */ rs = inflate(telnet->z, Z_SYNC_FLUSH); /* process the decompressed bytes on success */ if (rs == Z_OK || rs == Z_STREAM_END) _process(telnet, inflate_buffer, sizeof(inflate_buffer) - telnet->z->avail_out); else _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1, "inflate() failed: %s", zError(rs)); /* prepare output buffer for next run */ telnet->z->next_out = (unsigned char *)inflate_buffer; telnet->z->avail_out = sizeof(inflate_buffer); /* on error (or on end of stream) disable further inflation */ if (rs != Z_OK) { telnet_event_t ev; /* disable compression */ inflateEnd(telnet->z); free(telnet->z); telnet->z = 0; /* send event */ ev.type = TELNET_EV_COMPRESS; ev.compress.state = 0; telnet->eh(telnet, &ev, telnet->ud); break; } } /* COMPRESS2 is not negotiated, just process */ } else #endif /* defined(HAVE_ZLIB) */ _process(telnet, buffer, size); } /* send an iac command */ void telnet_iac(telnet_t *telnet, unsigned char cmd) { unsigned char bytes[2]; bytes[0] = TELNET_IAC; bytes[1] = cmd; _sendu(telnet, bytes, 2); } /* send negotiation */ void telnet_negotiate(telnet_t *telnet, unsigned char cmd, unsigned char telopt) { telnet_rfc1143_t q; /* if we're in proxy mode, just send it now */ if (telnet->flags & TELNET_FLAG_PROXY) { unsigned char bytes[3]; bytes[0] = TELNET_IAC; bytes[1] = cmd; bytes[2] = telopt; _sendu(telnet, bytes, 3); return; } /* get current option states */ q = _get_rfc1143(telnet, telopt); switch (cmd) { /* advertise willingess to support an option */ case TELNET_WILL: switch (Q_US(q)) { case Q_NO: _set_rfc1143(telnet, telopt, Q_WANTYES, Q_HIM(q)); _send_negotiate(telnet, TELNET_WILL, telopt); break; case Q_WANTNO: _set_rfc1143(telnet, telopt, Q_WANTNO_OP, Q_HIM(q)); break; case Q_WANTYES_OP: _set_rfc1143(telnet, telopt, Q_WANTYES, Q_HIM(q)); break; } break; /* force turn-off of locally enabled option */ case TELNET_WONT: switch (Q_US(q)) { case Q_YES: _set_rfc1143(telnet, telopt, Q_WANTNO, Q_HIM(q)); _send_negotiate(telnet, TELNET_WONT, telopt); break; case Q_WANTYES: _set_rfc1143(telnet, telopt, Q_WANTYES_OP, Q_HIM(q)); break; case Q_WANTNO_OP: _set_rfc1143(telnet, telopt, Q_WANTNO, Q_HIM(q)); break; } break; /* ask remote end to enable an option */ case TELNET_DO: switch (Q_HIM(q)) { case Q_NO: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES); _send_negotiate(telnet, TELNET_DO, telopt); break; case Q_WANTNO: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO_OP); break; case Q_WANTYES_OP: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES); break; } break; /* demand remote end disable an option */ case TELNET_DONT: switch (Q_HIM(q)) { case Q_YES: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO); _send_negotiate(telnet, TELNET_DONT, telopt); break; case Q_WANTYES: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES_OP); break; case Q_WANTNO_OP: _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO); break; } break; } } /* send non-command data (escapes IAC bytes) */ void telnet_send(telnet_t *telnet, const char *buffer, size_t size) { size_t i, l; for (l = i = 0; i != size; ++i) { /* dump prior portion of text, send escaped bytes */ if (buffer[i] == (char)TELNET_IAC) { /* dump prior text if any */ if (i != l) { _send(telnet, buffer + l, i - l); } l = i + 1; /* send escape */ telnet_iac(telnet, TELNET_IAC); } } /* send whatever portion of buffer is left */ if (i != l) { _send(telnet, buffer + l, i - l); } } /* send subnegotiation header */ void telnet_begin_sb(telnet_t *telnet, unsigned char telopt) { unsigned char sb[3]; sb[0] = TELNET_IAC; sb[1] = TELNET_SB; sb[2] = telopt; _sendu(telnet, sb, 3); } /* send complete subnegotiation */ void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt, const char *buffer, size_t size) { unsigned char bytes[5]; bytes[0] = TELNET_IAC; bytes[1] = TELNET_SB; bytes[2] = telopt; bytes[3] = TELNET_IAC; bytes[4] = TELNET_SE; _sendu(telnet, bytes, 3); telnet_send(telnet, buffer, size); _sendu(telnet, bytes + 3, 2); #if defined(HAVE_ZLIB) /* if we're a proxy and we just sent the COMPRESS2 marker, we must * make sure all further data is compressed if not already. */ if (telnet->flags & TELNET_FLAG_PROXY && telopt == TELNET_TELOPT_COMPRESS2) { telnet_event_t ev; if (_init_zlib(telnet, 1, 1) != TELNET_EOK) return; /* notify app that compression was enabled */ ev.type = TELNET_EV_COMPRESS; ev.compress.state = 1; telnet->eh(telnet, &ev, telnet->ud); } #endif /* defined(HAVE_ZLIB) */ } void telnet_begin_compress2(telnet_t *telnet) { UNUSED_ARG(telnet); #if defined(HAVE_ZLIB) static const unsigned char compress2[] = { TELNET_IAC, TELNET_SB, TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE }; telnet_event_t ev; /* attempt to create output stream first, bail if we can't */ if (_init_zlib(telnet, 1, 0) != TELNET_EOK) return; /* send compression marker. we send directly to the event handler * instead of passing through _send because _send would result in * the compress marker itself being compressed. */ ev.type = TELNET_EV_SEND; ev.data.buffer = (const char*)compress2; ev.data.size = sizeof(compress2); telnet->eh(telnet, &ev, telnet->ud); /* notify app that compression was successfully enabled */ ev.type = TELNET_EV_COMPRESS; ev.compress.state = 1; telnet->eh(telnet, &ev, telnet->ud); #endif /* defined(HAVE_ZLIB) */ } /* send formatted data with \r and \n translation in addition to IAC IAC */ int telnet_vprintf(telnet_t *telnet, const char *fmt, va_list va) { static const char CRLF[] = { '\r', '\n' }; static const char CRNUL[] = { '\r', '\0' }; char buffer[1024]; char *output = buffer; int rs, i, l; /* format */ rs = vsnprintf(buffer, sizeof(buffer), fmt, va); if ((size_t)rs >= sizeof(buffer)) { output = (char*)malloc(rs + 1); if (output == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "malloc() failed: %s", strerror(errno)); return -1; } rs = vsnprintf(output, rs + 1, fmt, va); } /* send */ for (l = i = 0; i != rs; ++i) { /* special characters */ if (output[i] == (char)TELNET_IAC || output[i] == '\r' || output[i] == '\n') { /* dump prior portion of text */ if (i != l) _send(telnet, output + l, i - l); l = i + 1; /* IAC -> IAC IAC */ if (output[i] == (char)TELNET_IAC) telnet_iac(telnet, TELNET_IAC); /* automatic translation of \r -> CRNUL */ else if (output[i] == '\r') _send(telnet, CRNUL, 2); /* automatic translation of \n -> CRLF */ else if (output[i] == '\n') _send(telnet, CRLF, 2); } } /* send whatever portion of output is left */ if (i != l) { _send(telnet, output + l, i - l); } /* free allocated memory, if any */ if (output != buffer) { free(output); } return rs; } /* see telnet_vprintf */ int telnet_printf(telnet_t *telnet, const char *fmt, ...) { va_list va; int rs; va_start(va, fmt); rs = telnet_vprintf(telnet, fmt, va); va_end(va); return rs; } /* send formatted data through telnet_send */ int telnet_raw_vprintf(telnet_t *telnet, const char *fmt, va_list va) { char buffer[1024]; char *output = buffer; int rs; /* format; allocate more space if necessary */ rs = vsnprintf(buffer, sizeof(buffer), fmt, va); if ((size_t)rs >= sizeof(buffer)) { output = (char*)malloc(rs + 1); if (output == 0) { _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, "malloc() failed: %s", strerror(errno)); return -1; } rs = vsnprintf(output, rs + 1, fmt, va); } /* send out the formatted data */ telnet_send(telnet, output, rs); /* release allocated memory, if any */ if (output != buffer) { free(output); } return rs; } /* see telnet_raw_vprintf */ int telnet_raw_printf(telnet_t *telnet, const char *fmt, ...) { va_list va; int rs; va_start(va, fmt); rs = telnet_raw_vprintf(telnet, fmt, va); va_end(va); return rs; } /* begin NEW-ENVIRON subnegotation */ void telnet_begin_newenviron(telnet_t *telnet, unsigned char cmd) { telnet_begin_sb(telnet, TELNET_TELOPT_NEW_ENVIRON); telnet_send(telnet, (char*)&cmd, 1); } /* send a NEW-ENVIRON value */ void telnet_newenviron_value(telnet_t *telnet, unsigned char type, const char *string) { telnet_send(telnet, (char*)&type, 1); if (string != 0) { telnet_send(telnet, string, strlen(string)); } } /* send TERMINAL-TYPE SEND command */ void telnet_ttype_send(telnet_t *telnet) { static const unsigned char SEND[] = { TELNET_IAC, TELNET_SB, TELNET_TELOPT_TTYPE, TELNET_TTYPE_SEND, TELNET_IAC, TELNET_SE }; _sendu(telnet, SEND, sizeof(SEND)); } /* send TERMINAL-TYPE IS command */ void telnet_ttype_is(telnet_t *telnet, const char* ttype) { static const unsigned char IS[] = { TELNET_IAC, TELNET_SB, TELNET_TELOPT_TTYPE, TELNET_TTYPE_IS }; _sendu(telnet, IS, sizeof(IS)); _send(telnet, ttype, strlen(ttype)); telnet_finish_sb(telnet); } /* send ZMP data */ void telnet_send_zmp(telnet_t *telnet, size_t argc, const char **argv) { size_t i; /* ZMP header */ telnet_begin_zmp(telnet, argv[0]); /* send out each argument, including trailing NUL byte */ for (i = 1; i != argc; ++i) telnet_zmp_arg(telnet, argv[i]); /* ZMP footer */ telnet_finish_zmp(telnet); } /* send ZMP data using varargs */ void telnet_send_vzmpv(telnet_t *telnet, va_list va) { const char* arg; /* ZMP header */ telnet_begin_sb(telnet, TELNET_TELOPT_ZMP); /* send out each argument, including trailing NUL byte */ while ((arg = va_arg(va, const char *)) != 0) telnet_zmp_arg(telnet, arg); /* ZMP footer */ telnet_finish_zmp(telnet); } /* see telnet_send_vzmpv */ void telnet_send_zmpv(telnet_t *telnet, ...) { va_list va; va_start(va, telnet); telnet_send_vzmpv(telnet, va); va_end(va); } /* begin a ZMP command */ void telnet_begin_zmp(telnet_t *telnet, const char *cmd) { telnet_begin_sb(telnet, TELNET_TELOPT_ZMP); telnet_zmp_arg(telnet, cmd); } /* send a ZMP argument */ void telnet_zmp_arg(telnet_t *telnet, const char* arg) { telnet_send(telnet, arg, strlen(arg) + 1); } turnserver-4.5.1.1/src/apps/relay/turn_ports.h0000664000175000017500000000540013436577655020047 0ustar misimisi/* * 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. */ #ifndef __TURN_PORTS__ #define __TURN_PORTS__ #include "ns_turn_ioaddr.h" #include "ns_sm.h" #ifdef __cplusplus extern "C" { #endif ////////////////////////////////////////////////// #define LOW_DEFAULT_PORTS_BOUNDARY (49152) #define HIGH_DEFAULT_PORTS_BOUNDARY (65535) ////////////////////////////////////////////////// struct _turnipports; typedef struct _turnipports turnipports; ////////////////////////////////////////////////// turnipports* turnipports_create(super_memory_t *sm, u16bits start, u16bits end); void turnipports_add_ip(u08bits transport, const ioa_addr *backend_addr); int turnipports_allocate(turnipports* tp, u08bits transport, const ioa_addr *backend_addr); int turnipports_allocate_even(turnipports* tp, const ioa_addr *backend_addr, int allocate_rtcp, u64bits *reservation_token); void turnipports_release(turnipports* tp, u08bits transport, const ioa_addr *socket_addr); int turnipports_is_allocated(turnipports* tp, u08bits transport, const ioa_addr *backend_addr, u16bits port); int turnipports_is_available(turnipports* tp, u08bits transport, const ioa_addr *backend_addr, u16bits port); ////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_PORTS__ turnserver-4.5.1.1/src/apps/relay/mainrelay.h0000664000175000017500000002256413436577655017623 0ustar misimisi/* * 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. */ #if !defined(__MAIN_RELAY__) #define __MAIN_RELAY__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ns_turn_openssl.h" #include "ns_turn_utils.h" #include "ns_turn_khash.h" #include "userdb.h" #include "turn_admin_server.h" #include "tls_listener.h" #include "dtls_listener.h" #include "ns_turn_server.h" #include "ns_turn_maps.h" #include "apputils.h" #include "ns_ioalib_impl.h" #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x10100000L #include #endif #ifdef __cplusplus extern "C" { #endif ////////////// DEFINES //////////////////////////// #define DEFAULT_CONFIG_FILE "turnserver.conf" #define DEFAULT_CIPHER_LIST "DEFAULT" /* "ALL:eNULL:aNULL:NULL" */ #define DEFAULT_EC_CURVE_NAME "prime256v1" #define MAX_NUMBER_OF_GENERAL_RELAY_SERVERS ((u08bits)(0x80)) #define TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP MAX_NUMBER_OF_GENERAL_RELAY_SERVERS #define TURNSERVER_ID_BOUNDARY_BETWEEN_UDP_AND_TCP TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP #define DEFAULT_CPUS_NUMBER (2) /////////// TYPES /////////////////////////////////// enum _DH_KEY_SIZE { DH_566, DH_1066, DH_2066, DH_CUSTOM }; typedef enum _DH_KEY_SIZE DH_KEY_SIZE; ///////// LISTENER SERVER TYPES ///////////////////// struct message_to_listener_to_client { ioa_addr origin; ioa_addr destination; ioa_network_buffer_handle nbh; }; enum _MESSAGE_TO_LISTENER_TYPE { LMT_UNKNOWN, LMT_TO_CLIENT }; typedef enum _MESSAGE_TO_LISTENER_TYPE MESSAGE_TO_LISTENER_TYPE; struct message_to_listener { MESSAGE_TO_LISTENER_TYPE t; union { struct message_to_listener_to_client tc; } m; }; struct listener_server { rtcp_map* rtcpmap; turnipports* tp; struct event_base* event_base; ioa_engine_handle ioa_eng; struct bufferevent *in_buf; struct bufferevent *out_buf; char **addrs; ioa_addr **encaddrs; size_t addrs_number; size_t services_number; dtls_listener_relay_server_type ***udp_services; dtls_listener_relay_server_type ***dtls_services; dtls_listener_relay_server_type ***aux_udp_services; }; enum _NET_ENG_VERSION { NEV_UNKNOWN=0, NEV_MIN, NEV_UDP_SOCKET_PER_SESSION=NEV_MIN, NEV_UDP_SOCKET_PER_ENDPOINT, NEV_UDP_SOCKET_PER_THREAD, NEV_MAX=NEV_UDP_SOCKET_PER_THREAD, NEV_TOTAL }; typedef enum _NET_ENG_VERSION NET_ENG_VERSION; /////////// PARAMS ////////////////////////////////// typedef struct _turn_params_ { //////////////// OpenSSL group ////////////////////// SSL_CTX *tls_ctx_ssl23; SSL_CTX *tls_ctx_v1_0; #if TLSv1_1_SUPPORTED SSL_CTX *tls_ctx_v1_1; #if TLSv1_2_SUPPORTED SSL_CTX *tls_ctx_v1_2; #endif #endif #if DTLS_SUPPORTED SSL_CTX *dtls_ctx; #if DTLSv1_2_SUPPORTED SSL_CTX *dtls_ctx_v1_2; #endif #endif DH_KEY_SIZE dh_key_size; char cipher_list[1025]; char ec_curve_name[33]; char ca_cert_file[1025]; char cert_file[1025]; char pkey_file[1025]; char tls_password[513]; char dh_file[1025]; int no_tlsv1; int no_tlsv1_1; int no_tlsv1_2; int no_tls; int no_dtls; struct event *tls_ctx_update_ev; pthread_mutex_t tls_mutex; //////////////// Common params //////////////////// int verbose; int turn_daemon; int prod; int web_admin_listen_on_workers; int do_not_use_config_file; char pidfile[1025]; //////////////// Listener server ///////////////// int listener_port; int tls_listener_port; int alt_listener_port; int alt_tls_listener_port; int rfc5780; int no_udp; int no_tcp; vint no_tcp_relay; vint no_udp_relay; char listener_ifname[1025]; char redis_statsdb[1025]; int use_redis_statsdb; struct listener_server listener; ip_range_list_t ip_whitelist; ip_range_list_t ip_blacklist; NET_ENG_VERSION net_engine_version; const char* net_engine_version_txt[NEV_TOTAL]; //////////////// Relay servers ///////////// u16bits min_port; u16bits max_port; vint check_origin; vint no_multicast_peers; vint allow_loopback_peers; char relay_ifname[1025]; size_t relays_number; char **relay_addrs; int default_relays; // Single global public IP. // If multiple public IPs are used // then ioa_addr mapping must be used. ioa_addr *external_ip; turnserver_id general_relay_servers_number; turnserver_id udp_relay_servers_number; ////////////// Auth server //////////////// char oauth_server_name[1025]; char domain[1025]; int oauth; /////////////// AUX SERVERS //////////////// turn_server_addrs_list_t aux_servers_list; int udp_self_balance; /////////////// ALTERNATE SERVERS //////////////// turn_server_addrs_list_t alternate_servers_list; turn_server_addrs_list_t tls_alternate_servers_list; int stop_turn_server; ////////////// MISC PARAMS //////////////// vint stun_only; vint no_stun; vint secure_stun; int server_relay; int fingerprint; char rest_api_separator; vint stale_nonce; vint max_allocate_lifetime; vint channel_lifetime; vint permission_lifetime; vint mobility; turn_credential_type ct; int use_auth_secret_with_timestamp; band_limit_t max_bps; band_limit_t bps_capacity; band_limit_t bps_capacity_allocated; vint total_quota; vint user_quota; /////// Users DB /////////// default_users_db_t default_users_db; /////// CPUs ////////////// unsigned long cpus; ///////// Encryption ///////// char secret_key_file[1025]; unsigned char secret_key[1025]; int keep_address_family; } turn_params_t; extern turn_params_t turn_params; //////////////// Listener server ///////////////// static inline int get_alt_listener_port(void) { if(turn_params.alt_listener_port<1) return turn_params.listener_port + 1; return turn_params.alt_listener_port; } static inline int get_alt_tls_listener_port(void) { if(turn_params.alt_tls_listener_port<1) return turn_params.tls_listener_port + 1; return turn_params.alt_tls_listener_port; } void add_aux_server(const char *saddr); void add_alternate_server(const char *saddr); void del_alternate_server(const char *saddr); void add_tls_alternate_server(const char *saddr); void del_tls_alternate_server(const char *saddr); ////////// Addrs //////////////////// void add_listener_addr(const char* addr); int add_relay_addr(const char* addr); ////////// SSL CTX //////////////////// void set_ssl_ctx(ioa_engine_handle e, turn_params_t *params); ///////// Auth //////////////// void send_auth_message_to_auth_server(struct auth_message *am); /////////// Setup server //////// void init_listener(void); void setup_server(void); void run_listener_server(struct listener_server *ls); ////////// BPS //////////////// band_limit_t get_bps_capacity_allocated(void); band_limit_t get_bps_capacity(void); void set_bps_capacity(band_limit_t value); band_limit_t get_max_bps(void); void set_max_bps(band_limit_t value); ///////// AES ENCRYPTION AND DECRYPTION //////// struct ctr_state { unsigned char ivec[16]; unsigned int num; unsigned char ecount[16]; }; void generate_aes_128_key(char* filePath, unsigned char* returnedKey); unsigned char *base64encode (const void *b64_encode_this, int encode_this_many_bytes); void encrypt_aes_128(unsigned char* in, const unsigned char* mykey); unsigned char *base64decode (const void *b64_decode_this, int decode_this_many_bytes); void decrypt_aes_128(char* in, const unsigned char* mykey); int decodedTextSize(char *input); char* decryptPassword(char* in, const unsigned char* mykey); int init_ctr(struct ctr_state *state, const unsigned char iv[8]); /////////////////////////////// #ifdef __cplusplus } #endif #endif //__MAIN_RELAY__ turnserver-4.5.1.1/src/apps/relay/dbdrivers/0000775000175000017500000000000013436577655017444 5ustar misimisiturnserver-4.5.1.1/src/apps/relay/dbdrivers/dbd_pgsql.h0000664000175000017500000000345713436577655021565 0ustar misimisi/* * Copyright (C) 2011, 2012, 2013 Citrix Systems * Copyright (C) 2014 Vivocha S.p.A. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef __DBD_POSTGRESQL__ #define __DBD_POSTGRESQL__ #include "dbdriver.h" #ifdef __cplusplus extern "C" { #endif const turn_dbdriver_t * get_pgsql_dbdriver(void); #ifdef __cplusplus } #endif #endif /// __DBD_POSTGRESQL__/// turnserver-4.5.1.1/src/apps/relay/dbdrivers/dbd_mongo.c0000664000175000017500000012013513436577655021542 0ustar misimisi/* * Copyright (C) 2011, 2012, 2013 Citrix Systems * Copyright (C) 2014 Vivocha S.p.A. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "../mainrelay.h" #include "dbd_mongo.h" #if !defined(TURN_NO_MONGO) #include #include /////////////////////////////////////////////////////////////////////////////////////////////////////////// const char * MONGO_DEFAULT_DB = "turn"; struct _MONGO { mongoc_uri_t * uri; mongoc_client_t * client; const char * database; }; typedef struct _MONGO MONGO; static void mongo_logger(mongoc_log_level_t log_level, const char * log_domain, const char * message, void * user_data) { UNUSED_ARG(log_domain); UNUSED_ARG(user_data); TURN_LOG_LEVEL l = 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 turnserver-4.5.1.1/src/apps/relay/dbdrivers/dbd_redis.c0000664000175000017500000011266613436577655021543 0ustar misimisi/* * Copyright (C) 2011, 2012, 2013 Citrix Systems * Copyright (C) 2014 Vivocha S.p.A. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "../mainrelay.h" #include "dbd_redis.h" #if !defined(TURN_NO_HIREDIS) #include "hiredis_libevent2.h" #include /////////////////////////////////////////////////////////////////////////////////////////////////////////// static int donot_print_connection_success = 0; static void turnFreeRedisReply(void *reply) { if(reply) { freeReplyObject(reply); } } struct _Ryconninfo { char *host; char *dbname; char *password; unsigned int connect_timeout; unsigned int port; }; typedef struct _Ryconninfo Ryconninfo; static void RyconninfoFree(Ryconninfo *co) { if(co) { if(co->host) turn_free(co->host, strlen(co->host)+1); if(co->dbname) turn_free(co->dbname, strlen(co->dbname)+1); if(co->password) turn_free(co->password, strlen(co->password)+1); ns_bzero(co,sizeof(Ryconninfo)); } } static Ryconninfo *RyconninfoParse(const char *userdb, char **errmsg) { Ryconninfo *co = (Ryconninfo*) turn_malloc(sizeof(Ryconninfo)); ns_bzero(co,sizeof(Ryconninfo)); if (userdb) { char *s0 = turn_strdup(userdb); char *s = s0; while (s && *s) { while (*s && (*s == ' ')) ++s; char *snext = strstr(s, " "); if (snext) { *snext = 0; ++snext; } char* seq = strstr(s, "="); if (!seq) { RyconninfoFree(co); co = NULL; if (errmsg) { *errmsg = turn_strdup(s); } break; } *seq = 0; if (!strcmp(s, "host")) co->host = turn_strdup(seq + 1); else if (!strcmp(s, "ip")) co->host = turn_strdup(seq + 1); else if (!strcmp(s, "addr")) co->host = turn_strdup(seq + 1); else if (!strcmp(s, "ipaddr")) co->host = turn_strdup(seq + 1); else if (!strcmp(s, "hostaddr")) co->host = turn_strdup(seq + 1); else if (!strcmp(s, "dbname")) co->dbname = turn_strdup(seq + 1); else if (!strcmp(s, "db")) co->dbname = turn_strdup(seq + 1); else if (!strcmp(s, "database")) co->dbname = turn_strdup(seq + 1); else if (!strcmp(s, "user")) ; else if (!strcmp(s, "uname")) ; else if (!strcmp(s, "name")) ; else if (!strcmp(s, "username")) ; else if (!strcmp(s, "password")) co->password = turn_strdup(seq + 1); else if (!strcmp(s, "pwd")) co->password = turn_strdup(seq + 1); else if (!strcmp(s, "passwd")) co->password = turn_strdup(seq + 1); else if (!strcmp(s, "secret")) co->password = turn_strdup(seq + 1); else if (!strcmp(s, "port")) co->port = (unsigned int) atoi(seq + 1); else if (!strcmp(s, "p")) co->port = (unsigned int) atoi(seq + 1); else if (!strcmp(s, "connect_timeout")) co->connect_timeout = (unsigned int) atoi(seq + 1); else if (!strcmp(s, "timeout")) co->connect_timeout = (unsigned int) atoi(seq + 1); else { RyconninfoFree(co); co = NULL; if (errmsg) { *errmsg = turn_strdup(s); } break; } s = snext; } turn_free(s0, strlen(s0)+1); } if(co) { if(!(co->dbname)) co->dbname=turn_strdup("0"); if(!(co->host)) co->host=turn_strdup("127.0.0.1"); if(!(co->password)) co->password=turn_strdup(""); } return co; } redis_context_handle get_redis_async_connection(struct event_base *base, const char* connection_string, int delete_keys) { redis_context_handle ret = NULL; char *errmsg = NULL; if(base && connection_string && connection_string[0]) { Ryconninfo *co = RyconninfoParse(connection_string, &errmsg); if (!co) { if (errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open Redis DB connection <%s>, connection string format error: %s\n", connection_string, errmsg); turn_free(errmsg,strlen(errmsg)+1); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open Redis DB connection <%s>, connection string format error\n", connection_string); } } else if (errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open Redis DB connection <%s>, connection string format error: %s\n", connection_string, errmsg); turn_free(errmsg,strlen(errmsg)+1); RyconninfoFree(co); } else { if(delete_keys) { redisContext *rc = NULL; char ip[256] = "\0"; int port = DEFAULT_REDIS_PORT; if (co->host) STRCPY(ip,co->host); if (!ip[0]) STRCPY(ip,"127.0.0.1"); if (co->port) port = (int) (co->port); if (co->connect_timeout) { struct timeval tv; tv.tv_usec = 0; tv.tv_sec = (time_t) (co->connect_timeout); rc = redisConnectWithTimeout(ip, port, tv); } else { rc = redisConnect(ip, port); } if (!rc) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot initialize Redis DB async connection\n"); } else { if (co->password) { turnFreeRedisReply(redisCommand(rc, "AUTH %s", co->password)); } if (co->dbname) { turnFreeRedisReply(redisCommand(rc, "select %s", co->dbname)); } { redisReply *reply = (redisReply*)redisCommand(rc, "keys turn/*/allocation/*/status"); if(reply) { secrets_list_t keys; size_t isz = 0; char s[513]; init_secrets_list(&keys); if (reply->type == REDIS_REPLY_ERROR) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); } else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { size_t i; for (i = 0; i < reply->elements; ++i) { add_to_secrets_list(&keys,reply->element[i]->str); } } for(isz=0;iszhost, co->port, co->password, atoi(co->dbname)); if (!ret) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot initialize Redis DB connection\n"); } else if (is_redis_asyncconn_good(ret) && !donot_print_connection_success) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Redis DB async connection to be used: %s\n", connection_string); donot_print_connection_success = 1; } RyconninfoFree(co); } } return ret; } static redisContext *get_redis_connection(void) { persistent_users_db_t *pud = get_persistent_users_db(); redisContext *redisconnection = (redisContext*)pthread_getspecific(connection_key); if(redisconnection) { if(redisconnection->err) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Cannot connect to redis, err=%d, flags=0x%lx\n", __FUNCTION__,(int)redisconnection->err,(unsigned long)redisconnection->flags); redisFree(redisconnection); redisconnection = NULL; (void) pthread_setspecific(connection_key, redisconnection); } } if (!redisconnection) { char *errmsg = NULL; Ryconninfo *co = RyconninfoParse(pud->userdb, &errmsg); if (!co) { if (errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open Redis DB connection <%s>, connection string format error: %s\n", pud->userdb, errmsg); turn_free(errmsg,strlen(errmsg)+1); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open Redis DB connection <%s>, connection string format error\n", pud->userdb); } } else if (errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open Redis DB connection <%s>, connection string format error: %s\n", pud->userdb, errmsg); turn_free(errmsg,strlen(errmsg)+1); RyconninfoFree(co); } else { char ip[256] = "\0"; int port = DEFAULT_REDIS_PORT; if (co->host) STRCPY(ip,co->host); if (!ip[0]) STRCPY(ip,"127.0.0.1"); if (co->port) port = (int) (co->port); if (co->connect_timeout) { struct timeval tv; tv.tv_usec = 0; tv.tv_sec = (time_t) (co->connect_timeout); redisconnection = redisConnectWithTimeout(ip, port, tv); } else { redisconnection = redisConnect(ip, port); } if (redisconnection) { if(redisconnection->err) { if(redisconnection->errstr[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Redis: %s\n",redisconnection->errstr); } redisFree(redisconnection); redisconnection = NULL; } else if (co->password) { void *reply = redisCommand(redisconnection, "AUTH %s", co->password); if(!reply) { if(redisconnection->err && redisconnection->errstr[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Redis: %s\n",redisconnection->errstr); } redisFree(redisconnection); redisconnection = NULL; } else { turnFreeRedisReply(reply); if (co->dbname) { reply = redisCommand(redisconnection, "select %s", co->dbname); if(!reply) { if(redisconnection->err && redisconnection->errstr[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Redis: %s\n",redisconnection->errstr); } redisFree(redisconnection); redisconnection = NULL; } else { turnFreeRedisReply(reply); } } } } } if (!redisconnection) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot initialize Redis DB connection\n"); } else if (!donot_print_connection_success) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Redis DB sync connection success: %s\n", pud->userdb); donot_print_connection_success = 1; } RyconninfoFree(co); } if(redisconnection) { (void) pthread_setspecific(connection_key, redisconnection); } } return redisconnection; } static int set_redis_realm_opt(char *realm, const char* key, unsigned long *value) { int found = 0; redisContext *rc = get_redis_connection(); if(rc) { redisReply *rget = NULL; char s[1025]; snprintf(s, sizeof(s), "get turn/realm/%s/%s", realm, key); rget = (redisReply *) redisCommand(rc, s); if (rget) { if (rget->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); else if (rget->type != REDIS_REPLY_STRING) { if (rget->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); } else { lock_realms(); *value = (unsigned long)atol(rget->str); unlock_realms(); found = 1; } turnFreeRedisReply(rget); } } return found; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// static int redis_get_auth_secrets(secrets_list_t *sl, u08bits *realm) { int ret = -1; redisContext *rc = get_redis_connection(); if (rc) { redisReply *reply = (redisReply*) redisCommand(rc, "smembers turn/realm/%s/secret", (char*) realm); if (reply) { if (reply->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { size_t i; for (i = 0; i < reply->elements; ++i) { add_to_secrets_list(sl, reply->element[i]->str); } } ret = 0; turnFreeRedisReply(reply); } } return ret; } static int redis_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) { int ret = -1; redisContext * rc = get_redis_connection(); if(rc) { char s[TURN_LONG_STRING_SIZE]; snprintf(s,sizeof(s),"get turn/realm/%s/user/%s/key", (char*)realm, usname); redisReply *rget = (redisReply *)redisCommand(rc, s); if(rget) { if (rget->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); else if (rget->type != REDIS_REPLY_STRING) { if (rget->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); } else { size_t sz = get_hmackey_size(SHATYPE_DEFAULT); if(strlen(rget->str)str,usname); } else if(convert_string_key_to_binary(rget->str, key, sz)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key: %s, user %s\n",rget->str,usname); } else { ret = 0; } } turnFreeRedisReply(rget); } } return ret; } static int redis_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) { int ret = -1; redisContext * rc = get_redis_connection(); if(rc) { char s[TURN_LONG_STRING_SIZE]; ns_bzero(key,sizeof(oauth_key_data_raw)); STRCPY(key->kid,kid); snprintf(s,sizeof(s),"hgetall turn/oauth/kid/%s", (const char*)kid); redisReply *reply = (redisReply *)redisCommand(rc, s); if(reply) { if (reply->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else if(reply->elements > 1) { size_t i; for (i = 0; i < (reply->elements)/2; ++i) { char *kw = reply->element[2*i]->str; char *val = reply->element[2*i+1]->str; if(kw) { if(!strcmp(kw,"as_rs_alg")) { STRCPY(key->as_rs_alg,val); } else if(!strcmp(kw,"realm")) { STRCPY(key->realm,val); } else if(!strcmp(kw,"ikm_key")) { STRCPY(key->ikm_key,val); } else if(!strcmp(kw,"timestamp")) { key->timestamp = (u64bits)strtoull(val,NULL,10); } else if(!strcmp(kw,"lifetime")) { key->lifetime = (u32bits)strtoul(val,NULL,10); } } } ret = 0; } turnFreeRedisReply(reply); } } return ret; } static int redis_set_user_key(u08bits *usname, u08bits *realm, const char *key) { int ret = -1; redisContext *rc = get_redis_connection(); if(rc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement),"set turn/realm/%s/user/%s/key %s",(char*)realm,usname,key); turnFreeRedisReply(redisCommand(rc, statement)); turnFreeRedisReply(redisCommand(rc, "save")); ret = 0; } return ret; } static int redis_set_oauth_key(oauth_key_data_raw *key) { int ret = -1; redisContext *rc = get_redis_connection(); if(rc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement),"hmset turn/oauth/kid/%s ikm_key %s as_rs_alg %s timestamp %llu lifetime %lu realm %s", key->kid,key->ikm_key,key->as_rs_alg,(unsigned long long)key->timestamp,(unsigned long)key->lifetime,key->realm); turnFreeRedisReply(redisCommand(rc, statement)); turnFreeRedisReply(redisCommand(rc, "save")); ret = 0; } return ret; } static int redis_del_user(u08bits *usname, u08bits *realm) { int ret = -1; redisContext *rc = get_redis_connection(); if(rc) { char statement[TURN_LONG_STRING_SIZE]; { snprintf(statement,sizeof(statement),"del turn/realm/%s/user/%s/key",(char*)realm,usname); turnFreeRedisReply(redisCommand(rc, statement)); } turnFreeRedisReply(redisCommand(rc, "save")); ret = 0; } return ret; } static int redis_del_oauth_key(const u08bits *kid) { int ret = -1; redisContext *rc = get_redis_connection(); if(rc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement),"del turn/oauth/kid/%s",(const char*)kid); turnFreeRedisReply(redisCommand(rc, statement)); turnFreeRedisReply(redisCommand(rc, "save")); ret = 0; } return ret; } static int redis_list_users(u08bits *realm, secrets_list_t *users, secrets_list_t *realms) { int ret = -1; redisContext *rc = get_redis_connection(); u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; if(rc) { secrets_list_t keys; size_t isz = 0; init_secrets_list(&keys); redisReply *reply = NULL; { if(realm && realm[0]) { reply = (redisReply*)redisCommand(rc, "keys turn/realm/%s/user/*/key", (char*)realm); } else { reply = (redisReply*)redisCommand(rc, "keys turn/realm/*/user/*/key"); } if(reply) { if (reply->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { size_t i; for (i = 0; i < reply->elements; ++i) { add_to_secrets_list(&keys,reply->element[i]->str); } } turnFreeRedisReply(reply); } } size_t rhsz=strlen("turn/realm/"); size_t uhsz = strlen("user/"); for(isz=0;isztype == REDIS_REPLY_ERROR) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); } else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } } else { size_t i; for (i = 0; i < reply->elements; ++i) { add_to_secrets_list(&keys,reply->element[i]->str); } } turnFreeRedisReply(reply); } } for(isz=0;iszkid); 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); } } } clean_secrets_list(&keys); ret = 0; return ret; } static int redis_list_secrets(u08bits *realm, secrets_list_t *secrets, secrets_list_t *realms) { int ret = -1; u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; donot_print_connection_success = 1; redisContext *rc = get_redis_connection(); if (rc) { redisReply *reply = NULL; if (realm && realm[0]) { reply = (redisReply*) redisCommand(rc, "keys turn/realm/%s/secret", (char*) realm); } else { reply = (redisReply*) redisCommand(rc, "keys turn/realm/*/secret"); } if (reply) { secrets_list_t keys; size_t isz = 0; char s[257]; init_secrets_list(&keys); if (reply->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { size_t i; for (i = 0; i < reply->elements; ++i) { add_to_secrets_list(&keys, reply->element[i]->str); } } size_t rhsz=strlen("turn/realm/"); for (isz = 0; isz < keys.sz; ++isz) { snprintf(s, sizeof(s), "smembers %s", keys.secrets[isz]); redisReply *rget = (redisReply *) redisCommand(rc, s); if (rget) { if (rget->type == REDIS_REPLY_ERROR) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); } else if (rget->type == REDIS_REPLY_STRING) { printf("%s\n", rget->str); } else if (rget->type != REDIS_REPLY_ARRAY) { if (rget->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); } else { char *s = keys.secrets[isz]; char *sh = strstr(s,"turn/realm/"); if(sh != s) continue; sh += rhsz; char* st = strchr(sh,'/'); if(!st) continue; *st=0; const char *rval = sh; size_t i; for (i = 0; i < rget->elements; ++i) { const char *kval = rget->element[i]->str; 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); } } } } turnFreeRedisReply(rget); } clean_secrets_list(&keys); turnFreeRedisReply(reply); ret = 0; } } return ret; } static int redis_del_secret(u08bits *secret, u08bits *realm) { int ret = -1; donot_print_connection_success = 1; redisContext *rc = get_redis_connection(); if (rc) { turnFreeRedisReply(redisCommand(rc, "srem turn/realm/%s/secret %s", (char*) realm, (char*) secret)); turnFreeRedisReply(redisCommand(rc, "save")); ret = 0; } return ret; } static int redis_set_secret(u08bits *secret, u08bits *realm) { int ret = -1; donot_print_connection_success = 1; redisContext *rc = get_redis_connection(); if (rc) { char s[TURN_LONG_STRING_SIZE]; redis_del_secret(secret, realm); snprintf(s, sizeof(s), "sadd turn/realm/%s/secret %s", (char*) realm, secret); turnFreeRedisReply(redisCommand(rc, s)); turnFreeRedisReply(redisCommand(rc, "save")); ret = 0; } return ret; } static int redis_set_permission_ip(const char *kind, u08bits *realm, const char* ip, int del) { int ret = -1; u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; donot_print_connection_success = 1; redisContext *rc = get_redis_connection(); if (rc) { char s[TURN_LONG_STRING_SIZE]; if(del) { snprintf(s, sizeof(s), "srem turn/realm/%s/%s-peer-ip %s", (char*) realm, kind, ip); } else { snprintf(s, sizeof(s), "sadd turn/realm/%s/%s-peer-ip %s", (char*) realm, kind, ip); } turnFreeRedisReply(redisCommand(rc, s)); turnFreeRedisReply(redisCommand(rc, "save")); ret = 0; } return ret; } static int redis_add_origin(u08bits *origin, u08bits *realm) { int ret = -1; redisContext *rc = get_redis_connection(); if(rc) { char s[TURN_LONG_STRING_SIZE]; snprintf(s,sizeof(s),"set turn/origin/%s %s", (char*)origin, (char*)realm); turnFreeRedisReply(redisCommand(rc, s)); turnFreeRedisReply(redisCommand(rc, "save")); ret = 0; } return ret; } static int redis_del_origin(u08bits *origin) { int ret = -1; redisContext *rc = get_redis_connection(); if(rc) { char s[TURN_LONG_STRING_SIZE]; snprintf(s,sizeof(s),"del turn/origin/%s", (char*)origin); turnFreeRedisReply(redisCommand(rc, s)); turnFreeRedisReply(redisCommand(rc, "save")); ret = 0; } return ret; } static int redis_list_origins(u08bits *realm, secrets_list_t *origins, secrets_list_t *realms) { int ret = -1; u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; donot_print_connection_success = 1; redisContext *rc = get_redis_connection(); if(rc) { secrets_list_t keys; size_t isz = 0; init_secrets_list(&keys); redisReply *reply = NULL; { reply = (redisReply*)redisCommand(rc, "keys turn/origin/*"); if(reply) { if (reply->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { size_t i; size_t offset = strlen("turn/origin/"); for (i = 0; i < reply->elements; ++i) { add_to_secrets_list(&keys,reply->element[i]->str+offset); } } turnFreeRedisReply(reply); } } for(isz=0;isztype == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_STRING) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { if(!(realm && realm[0] && strcmp((char*)realm,reply->str))) { if(origins) { add_to_secrets_list(origins,o); if(realms) { add_to_secrets_list(realms,reply->str); } } else { printf("%s ==>> %s\n",o,reply->str); } } } turnFreeRedisReply(reply); } } clean_secrets_list(&keys); ret = 0; } return ret; } static int redis_set_realm_option_one(u08bits *realm, unsigned long value, const char* opt) { int ret = -1; redisContext *rc = get_redis_connection(); if(rc) { char s[TURN_LONG_STRING_SIZE]; if(value>0) snprintf(s,sizeof(s),"set turn/realm/%s/%s %lu", (char*)realm, opt, (unsigned long)value); else snprintf(s,sizeof(s),"del turn/realm/%s/%s", (char*)realm, opt); turnFreeRedisReply(redisCommand(rc, s)); turnFreeRedisReply(redisCommand(rc, "save")); ret = 0; } return ret; } static int redis_list_realm_options(u08bits *realm) { int ret = -1; donot_print_connection_success = 1; redisContext *rc = get_redis_connection(); if(rc) { secrets_list_t keys; size_t isz = 0; init_secrets_list(&keys); redisReply *reply = NULL; { if(realm && realm[0]) { reply = (redisReply*)redisCommand(rc, "keys turn/realm/%s/*",realm); } else { reply = (redisReply*)redisCommand(rc, "keys turn/realm/*"); } if(reply) { if (reply->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { size_t i; for (i = 0; i < reply->elements; ++i) { if(strstr(reply->element[i]->str,"/max-bps")|| strstr(reply->element[i]->str,"/total-quota")|| strstr(reply->element[i]->str,"/user-quota")) { add_to_secrets_list(&keys,reply->element[i]->str); } } } turnFreeRedisReply(reply); } } size_t offset = strlen("turn/realm/"); for(isz=0;isztype == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_STRING) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { printf("%s = %s\n",o+offset,reply->str); } turnFreeRedisReply(reply); } } clean_secrets_list(&keys); ret = 0; } return ret; } static void redis_auth_ping(void * rch) { redisContext *rc = get_redis_connection(); if(rc) { turnFreeRedisReply(redisCommand(rc, "keys turn/origin/*")); } if(rch) send_message_to_redis((redis_context_handle)rch, "publish", "__XXX__", "__YYY__"); } static int redis_get_ip_list(const char *kind, ip_range_list_t * list) { int ret = -1; redisContext *rc = get_redis_connection(); if (rc) { char statement[TURN_LONG_STRING_SIZE]; const char* header = "turn/realm/"; size_t header_len = strlen(header); snprintf(statement, sizeof(statement), "keys %s*/%s-peer-ip", header,kind); redisReply *reply = (redisReply*) redisCommand(rc, statement); if (reply) { secrets_list_t keys; size_t isz = 0; char s[257]; init_secrets_list(&keys); if (reply->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { size_t i; for (i = 0; i < reply->elements; ++i) { add_to_secrets_list(&keys, reply->element[i]->str); } } for (isz = 0; isz < keys.sz; ++isz) { char *realm = NULL; snprintf(s, sizeof(s), "smembers %s", keys.secrets[isz]); redisReply *rget = (redisReply *) redisCommand(rc, s); char *ptr = ((char*)keys.secrets[isz])+header_len; char *sep = strstr(ptr, "/"); if (sep) { *sep = 0; realm = ptr; } if (rget) { if (rget->type == REDIS_REPLY_ERROR) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); } else if (rget->type == REDIS_REPLY_STRING) { add_ip_list_range(rget->str, realm, list); } else if (rget->type != REDIS_REPLY_ARRAY) { if (rget->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); } else { size_t i; for (i = 0; i < rget->elements; ++i) { add_ip_list_range(rget->element[i]->str, realm, list); } } turnFreeRedisReply(rget); } if(sep) { *sep='/'; } } clean_secrets_list(&keys); turnFreeRedisReply(reply); ret = 0; } } return ret; } static void redis_reread_realms(secrets_list_t * realms_list) { redisContext *rc = get_redis_connection(); if (rc) { redisReply *reply = (redisReply*) redisCommand(rc, "keys turn/origin/*"); if (reply) { ur_string_map *o_to_realm_new = ur_string_map_create(turn_free_simple); secrets_list_t keys; init_secrets_list(&keys); size_t isz = 0; char s[1025]; if (reply->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else { size_t i; for (i = 0; i < reply->elements; ++i) { add_to_secrets_list(&keys, reply->element[i]->str); } } size_t offset = strlen("turn/origin/"); for (isz = 0; isz < keys.sz; ++isz) { char *origin = keys.secrets[isz] + offset; snprintf(s, sizeof(s), "get %s", keys.secrets[isz]); redisReply *rget = (redisReply *) redisCommand(rc, s); if (rget) { if (rget->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str); else if (rget->type != REDIS_REPLY_STRING) { if (rget->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type); } else { get_realm(rget->str); ur_string_map_value_type value = turn_strdup(rget->str); ur_string_map_put(o_to_realm_new, (const ur_string_map_key_type) origin, value); } turnFreeRedisReply(rget); } } clean_secrets_list(&keys); update_o_to_realm(o_to_realm_new); turnFreeRedisReply(reply); } { size_t i = 0; size_t rlsz = 0; lock_realms(); rlsz = realms_list->sz; unlock_realms(); for (i = 0; isecrets[i]; realm_params_t* rp = get_realm(realm); { unsigned long value = 0; if(!set_redis_realm_opt(realm,"max-bps",&value)) { lock_realms(); rp->options.perf_options.max_bps = turn_params.max_bps; unlock_realms(); } else { rp->options.perf_options.max_bps = (band_limit_t)value; } } { unsigned long value = 0; if(!set_redis_realm_opt(realm,"total-quota",&value)) { lock_realms(); rp->options.perf_options.total_quota = turn_params.total_quota; unlock_realms(); } else { rp->options.perf_options.total_quota = (vint)value; } } { unsigned long value = 0; if(!set_redis_realm_opt(realm,"user-quota",&value)) { lock_realms(); rp->options.perf_options.user_quota = turn_params.user_quota; unlock_realms(); } else { rp->options.perf_options.user_quota = (vint)value; } } } } } } ///////////////////////////////////////////////////// static int redis_get_admin_user(const u08bits *usname, u08bits *realm, password_t pwd) { int ret = -1; redisContext * rc = get_redis_connection(); if(rc) { char s[TURN_LONG_STRING_SIZE]; realm[0]=0; pwd[0]=0; snprintf(s,sizeof(s),"hgetall turn/admin_user/%s", (const char*)usname); redisReply *reply = (redisReply *)redisCommand(rc, s); if(reply) { if (reply->type == REDIS_REPLY_ERROR) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } else if(reply->elements > 1) { size_t i; for (i = 0; i < (reply->elements)/2; ++i) { char *kw = reply->element[2*i]->str; char *val = reply->element[2*i+1]->str; if(kw) { if(!strcmp(kw,"realm")) { strncpy((char*)realm,val,STUN_MAX_REALM_SIZE); } else if(!strcmp(kw,"password")) { strncpy((char*)pwd,val,STUN_MAX_PWD_SIZE); ret = 0; } } } } turnFreeRedisReply(reply); } } return ret; } static int redis_set_admin_user(const u08bits *usname, const u08bits *realm, const password_t pwd) { int ret = -1; donot_print_connection_success = 1; redisContext *rc = get_redis_connection(); if(rc) { char statement[TURN_LONG_STRING_SIZE]; if(realm[0]) { snprintf(statement,sizeof(statement),"hmset turn/admin_user/%s realm %s password %s",usname,realm,pwd); } else { snprintf(statement,sizeof(statement),"hmset turn/admin_user/%s password %s",usname,pwd); } turnFreeRedisReply(redisCommand(rc, statement)); turnFreeRedisReply(redisCommand(rc, "save")); ret = 0; } return ret; } static int redis_del_admin_user(const u08bits *usname) { int ret = -1; donot_print_connection_success = 1; redisContext *rc = get_redis_connection(); if(rc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement),"del turn/admin_user/%s",(const char*)usname); turnFreeRedisReply(redisCommand(rc, statement)); turnFreeRedisReply(redisCommand(rc, "save")); ret = 0; } return ret; } static int redis_list_admin_users(int no_print) { int ret = -1; donot_print_connection_success = 1; redisContext *rc = get_redis_connection(); secrets_list_t keys; size_t isz = 0; init_secrets_list(&keys); if(rc) { redisReply *reply = NULL; reply = (redisReply*)redisCommand(rc, "keys turn/admin_user/*"); if(reply) { if (reply->type == REDIS_REPLY_ERROR) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str); } else if (reply->type != REDIS_REPLY_ARRAY) { if (reply->type != REDIS_REPLY_NIL) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type); } } else { size_t i; for (i = 0; i < reply->elements; ++i) { add_to_secrets_list(&keys,reply->element[i]->str); } } turnFreeRedisReply(reply); } } ret = 0; for(isz=0;isz /////////////////////////////////////////////////////////////////////////////////////////////////////////// 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) turn_free(co->host,strlen(co->host)+1); if(co->dbname) turn_free(co->dbname, strlen(co->dbname)+1); if(co->user) turn_free(co->user, strlen(co->user)+1); if(co->password) turn_free(co->password, strlen(co->password)+1); if(co->key) turn_free(co->key, strlen(co->key)+1); if(co->ca) turn_free(co->ca, strlen(co->ca)+1); if(co->cert) turn_free(co->cert, strlen(co->cert)+1); if(co->capath) turn_free(co->capath, strlen(co->capath)+1); if(co->cipher) turn_free(co->cipher, strlen(co->cipher)+1); ns_bzero(co,sizeof(Myconninfo)); } } 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)); strcpy(out,last); return out; } static Myconninfo *MyconninfoParse(char *userdb, char **errmsg) { Myconninfo *co = (Myconninfo*)turn_malloc(sizeof(Myconninfo)); ns_bzero(co,sizeof(Myconninfo)); if(userdb) { char *s0=turn_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 = turn_strdup(s); } break; } *seq = 0; if(!strcmp(s,"host")) co->host = turn_strdup(seq+1); else if(!strcmp(s,"ip")) co->host = turn_strdup(seq+1); else if(!strcmp(s,"addr")) co->host = turn_strdup(seq+1); else if(!strcmp(s,"ipaddr")) co->host = turn_strdup(seq+1); else if(!strcmp(s,"hostaddr")) co->host = turn_strdup(seq+1); else if(!strcmp(s,"dbname")) co->dbname = turn_strdup(seq+1); else if(!strcmp(s,"db")) co->dbname = turn_strdup(seq+1); else if(!strcmp(s,"database")) co->dbname = turn_strdup(seq+1); else if(!strcmp(s,"user")) co->user = turn_strdup(seq+1); else if(!strcmp(s,"uname")) co->user = turn_strdup(seq+1); else if(!strcmp(s,"name")) co->user = turn_strdup(seq+1); else if(!strcmp(s,"username")) co->user = turn_strdup(seq+1); else if(!strcmp(s,"password")) co->password = turn_strdup(seq+1); else if(!strcmp(s,"pwd")) co->password = turn_strdup(seq+1); else if(!strcmp(s,"passwd")) co->password = turn_strdup(seq+1); else if(!strcmp(s,"secret")) co->password = turn_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 = turn_strdup(seq+1); else if(!strcmp(s,"ssl-key")) co->key = turn_strdup(seq+1); else if(!strcmp(s,"ca")) co->ca = turn_strdup(seq+1); else if(!strcmp(s,"ssl-ca")) co->ca = turn_strdup(seq+1); else if(!strcmp(s,"capath")) co->capath = turn_strdup(seq+1); else if(!strcmp(s,"ssl-capath")) co->capath = turn_strdup(seq+1); else if(!strcmp(s,"cert")) co->cert = turn_strdup(seq+1); else if(!strcmp(s,"ssl-cert")) co->cert = turn_strdup(seq+1); else if(!strcmp(s,"cipher")) co->cipher = turn_strdup(seq+1); else if(!strcmp(s,"ssl-cipher")) co->cipher = turn_strdup(seq+1); else { MyconninfoFree(co); co = NULL; if(errmsg) { *errmsg = turn_strdup(s); } break; } s = snext; } turn_free(s0, strlen(s0)+1); } if(co) { if(!(co->dbname)) co->dbname=turn_strdup("0"); if(!(co->host)) co->host=turn_strdup("127.0.0.1"); if(!(co->user)) co->user=turn_strdup(""); if(!(co->password)) co->password=turn_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,errmsg); turn_free(errmsg,strlen(errmsg)+1); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open MySQL DB connection <%s>, connection string format error\n",pud->userdb); } } else if(errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open MySQL DB connection <%s>, connection string format error: %s\n",pud->userdb,errmsg); turn_free(errmsg,strlen(errmsg)+1); MyconninfoFree(co); } else if(!(co->dbname)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "MySQL Database name is not provided: <%s>\n",pud->userdb); MyconninfoFree(co); } else { mydbconnection = mysql_init(NULL); if(!mydbconnection) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot initialize MySQL DB connection\n"); } else { if(co->connect_timeout) mysql_options(mydbconnection,MYSQL_OPT_CONNECT_TIMEOUT,&(co->connect_timeout)); 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); 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); 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, u08bits *realm) { int ret = -1; MYSQL * myc = get_mydb_connection(); if(myc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement)-1,"select value from turn_secret where realm='%s'",realm); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)==1) { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0]) { unsigned long *lengths = mysql_fetch_lengths(mres); if(lengths) { size_t sz = lengths[0]; char auth_secret[TURN_LONG_STRING_SIZE]; ns_bcopy(row[0],auth_secret,sz); auth_secret[sz]=0; add_to_secrets_list(sl,auth_secret); } } } } ret = 0; } if(mres) mysql_free_result(mres); } } return ret; } static int mysql_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) { int ret = -1; MYSQL * myc = get_mydb_connection(); if(myc) { char statement[TURN_LONG_STRING_SIZE]; /* 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]kid,kid); ns_bcopy(row[0],key->ikm_key,lengths[0]); key->ikm_key[lengths[0]]=0; char stimestamp[128]; ns_bcopy(row[1],stimestamp,lengths[1]); stimestamp[lengths[1]]=0; key->timestamp = (u64bits)strtoull(stimestamp,NULL,10); char slifetime[128]; ns_bcopy(row[2],slifetime,lengths[2]); slifetime[lengths[2]]=0; key->lifetime = (u32bits)strtoul(slifetime,NULL,10); ns_bcopy(row[3],key->as_rs_alg,lengths[3]); key->as_rs_alg[lengths[3]]=0; ns_bcopy(row[4],key->realm,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) { ns_bcopy(row[0],key->ikm_key,lengths[0]); key->ikm_key[lengths[0]]=0; char stimestamp[128]; ns_bcopy(row[1],stimestamp,lengths[1]); stimestamp[lengths[1]]=0; key->timestamp = (u64bits)strtoull(stimestamp,NULL,10); char slifetime[128]; ns_bcopy(row[2],slifetime,lengths[2]); slifetime[lengths[2]]=0; key->lifetime = (u32bits)strtoul(slifetime,NULL,10); ns_bcopy(row[3],key->as_rs_alg,lengths[3]); key->as_rs_alg[lengths[3]]=0; ns_bcopy(row[4],key->realm,lengths[4]); key->realm[lengths[4]]=0; ns_bcopy(row[5],key->kid,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(u08bits *usname, u08bits *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(u08bits *usname, u08bits *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 u08bits *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(u08bits *realm, secrets_list_t *users, secrets_list_t *realms) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; u08bits 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(u08bits *realm, secrets_list_t *secrets, secrets_list_t *realms) { int ret = -1; u08bits 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(u08bits *secret, u08bits *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(u08bits *secret, u08bits *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, u08bits *realm, const char* ip, int del) { int ret = -1; u08bits 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(u08bits *origin, u08bits *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(u08bits *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(u08bits *realm, secrets_list_t *origins, secrets_list_t *realms) { int ret = -1; u08bits 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(u08bits *realm, unsigned long value, const char* opt) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if (myc) { { snprintf(statement,sizeof(statement),"delete from turn_realm_option where realm='%s' and opt='%s'",realm,opt); mysql_query(myc, statement); } if(value>0) { snprintf(statement,sizeof(statement),"insert into turn_realm_option (realm,opt,value) values('%s','%s','%lu')",realm,opt,(unsigned long)value); int res = mysql_query(myc, statement); if (res) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "Error inserting realm option information: %s\n", mysql_error(myc)); } else { ret = 0; } } } return ret; } static int mysql_list_realm_options(u08bits *realm) { int ret = -1; donot_print_connection_success = 1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if(myc) { if(realm && realm[0]) { snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option where realm='%s' order by realm,opt",realm); } else { snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option order by realm,opt"); } int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=3) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0] && row[1] && row[2]) { printf("%s[%s]=%s\n",row[1],row[0],row[2]); } } } ret = 0; } if(mres) mysql_free_result(mres); } } return ret; } static void mysql_auth_ping(void * rch) { UNUSED_ARG(rch); 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]; ns_bcopy(row[0],kval,sz); kval[sz]=0; sz = lengths[1]; char rval[TURN_LONG_STRING_SIZE]; ns_bcopy(row[1],rval,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(turn_free_simple); for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0] && row[1]) { unsigned long *lengths = mysql_fetch_lengths(mres); if(lengths) { size_t sz = lengths[0]; char oval[513]; ns_bcopy(row[0],oval,sz); oval[sz]=0; char *rval=turn_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, (const ur_string_map_key_type) oval, value); } } } } update_o_to_realm(o_to_realm_new); } if(mres) mysql_free_result(mres); } } { size_t i = 0; size_t rlsz = 0; lock_realms(); rlsz = realms_list->sz; unlock_realms(); for (i = 0; isecrets[i]; realm_params_t* rp = get_realm(realm); lock_realms(); rp->options.perf_options.max_bps = turn_params.max_bps; unlock_realms(); lock_realms(); rp->options.perf_options.total_quota = turn_params.total_quota; unlock_realms(); lock_realms(); rp->options.perf_options.user_quota = turn_params.user_quota; unlock_realms(); } } snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option"); int res = mysql_query(myc, statement); if(res == 0) { MYSQL_RES *mres = mysql_store_result(myc); if(mres && mysql_field_count(myc)==3) { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0] && row[1] && row[2]) { unsigned long *lengths = mysql_fetch_lengths(mres); if(lengths) { char rval[513]; size_t sz = lengths[0]; ns_bcopy(row[0],rval,sz); rval[sz]=0; char oval[513]; sz = lengths[1]; ns_bcopy(row[1],oval,sz); oval[sz]=0; char vval[513]; sz = lengths[2]; ns_bcopy(row[2],vval,sz); vval[sz]=0; realm_params_t* rp = get_realm(rval); if(!strcmp(oval,"max-bps")) rp->options.perf_options.max_bps = (band_limit_t)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 u08bits *usname, u08bits *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 u08bits *usname, const u08bits *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 u08bits *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 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 }; const turn_dbdriver_t * get_mysql_dbdriver(void) { return &driver; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// #else const turn_dbdriver_t * get_mysql_dbdriver(void) { return NULL; } #endif turnserver-4.5.1.1/src/apps/relay/dbdrivers/dbdriver.h0000664000175000017500000000751513436577655021426 0ustar misimisi/* * Copyright (C) 2011, 2012, 2013 Citrix Systems * Copyright (C) 2014 Vivocha S.p.A. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef __DBDRIVER__ #define __DBDRIVER__ #include "../userdb.h" #include "ns_turn_msg_defs.h" #include #ifdef __cplusplus extern "C" { #endif //////////////////////////////////////////// extern pthread_key_t connection_key; extern pthread_once_t connection_key_once; typedef struct _turn_dbdriver_t { int (*get_auth_secrets)(secrets_list_t *sl, u08bits *realm); int (*get_user_key)(u08bits *usname, u08bits *realm, hmackey_t key); int (*set_user_key)(u08bits *usname, u08bits *realm, const char *key); int (*del_user)(u08bits *usname, u08bits *realm); int (*list_users)(u08bits *realm, secrets_list_t *users, secrets_list_t *realms); int (*list_secrets)(u08bits *realm, secrets_list_t *secrets, secrets_list_t *realms); int (*del_secret)(u08bits *secret, u08bits *realm); int (*set_secret)(u08bits *secret, u08bits *realm); int (*add_origin)(u08bits *origin, u08bits *realm); int (*del_origin)(u08bits *origin); int (*list_origins)(u08bits *realm, secrets_list_t *origins, secrets_list_t *realms); int (*set_realm_option_one)(u08bits *realm, unsigned long value, const char* opt); int (*list_realm_options)(u08bits *realm); void (*auth_ping)(void * rch); int (*get_ip_list)(const char *kind, ip_range_list_t * list); int (*set_permission_ip)(const char *kind, u08bits *realm, const char* ip, int del); void (*reread_realms)(secrets_list_t * realms_list); int (*set_oauth_key)(oauth_key_data_raw *key); int (*get_oauth_key)(const u08bits *kid, oauth_key_data_raw *key); int (*del_oauth_key)(const u08bits *kid); int (*list_oauth_keys)(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts,secrets_list_t *realms); int (*get_admin_user)(const u08bits *usname, u08bits *realm, password_t pwd); int (*set_admin_user)(const u08bits *usname, const u08bits *realm, const password_t pwd); int (*del_admin_user)(const u08bits *usname); int (*list_admin_users)(int no_print); } turn_dbdriver_t; /////////// USER DB CHECK ////////////////// int convert_string_key_to_binary(char* keysource, hmackey_t key, size_t sz); persistent_users_db_t * get_persistent_users_db(void); const turn_dbdriver_t * get_dbdriver(void); //////////////////////////////////////////// #ifdef __cplusplus } #endif #endif /// __DBDRIVER__/// turnserver-4.5.1.1/src/apps/relay/dbdrivers/dbd_sqlite.c0000664000175000017500000011201713436577655021724 0ustar misimisi/* * 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_sqlite.h" #if !defined(TURN_NO_SQLITE) #include #include #include #include #include ////////////////////////////////////////////////// static pthread_mutex_t rc_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t rc_cond = PTHREAD_COND_INITIALIZER; static int read_threads = 0; static int write_level = 0; static pthread_t write_thread = 0; static void sqlite_lock(int write) { pthread_t pths = pthread_self(); int can_move = 0; while (!can_move) { pthread_mutex_lock(&rc_mutex); if (write) { if (((write_thread == 0) && (read_threads < 1)) || (write_thread == pths)) { can_move = 1; ++write_level; write_thread = pths; } } else { if ((!write_thread) || (write_thread == pths)) { can_move = 1; ++read_threads; } } if (!can_move) { pthread_cond_wait(&rc_cond, &rc_mutex); } pthread_mutex_unlock(&rc_mutex); } } static void sqlite_unlock(int write) { pthread_mutex_lock(&rc_mutex); if (write) { if (!(--write_level)) { write_thread = 0; pthread_cond_broadcast(&rc_cond); } } else { if (!(--read_threads)) { pthread_cond_broadcast(&rc_cond); } } pthread_mutex_unlock(&rc_mutex); } ////////////////////////////////////////////////// static int sqlite_init_multithreaded(void) { #if defined(SQLITE_CONFIG_MULTITHREAD) sqlite3_shutdown(); if (sqlite3_threadsafe() > 0) { int retCode = sqlite3_config(SQLITE_CONFIG_MULTITHREAD); if (retCode != SQLITE_OK) { retCode = sqlite3_config(SQLITE_CONFIG_SERIALIZED); if (retCode != SQLITE_OK) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "setting sqlite thread safe mode to serialized failed!!! return code: %d\n", retCode); return -1; } } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Your SQLite database is not compiled to be threadsafe.\n"); return -1; } #endif return 0; } static int donot_print_connection_success = 0; static void fix_user_directory(char *dir0) { char *dir = dir0; while(*dir == ' ') ++dir; if(*dir == '~') { char *home=getenv("HOME"); if(!home) { struct passwd *pwd = getpwuid(getuid()); if(!pwd) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot figure out the user's HOME directory (1)\n"); } else { home = pwd->pw_dir; if(!home) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot figure out the user's HOME directory\n"); return; } } } size_t szh = strlen(home); size_t sz = strlen(dir0)+1+szh; char* dir_fixed = (char*)turn_malloc(sz); strncpy(dir_fixed,home,szh); strncpy(dir_fixed+szh,dir+1,(sz-szh-1)); strncpy(dir0,dir_fixed,sz); turn_free(dir_fixed,sz); } } static void init_sqlite_database(sqlite3 *sqliteconnection) { const char * statements[] = { "CREATE TABLE turnusers_lt ( realm varchar(127) default '', name varchar(512), hmackey char(128), PRIMARY KEY (realm,name))", "CREATE TABLE turn_secret (realm varchar(127) default '', value varchar(127), primary key (realm,value))", "CREATE TABLE allowed_peer_ip (realm varchar(127) default '', ip_range varchar(256), primary key (realm,ip_range))", "CREATE TABLE denied_peer_ip (realm varchar(127) default '', ip_range varchar(256), primary key (realm,ip_range))", "CREATE TABLE turn_origin_to_realm (origin varchar(127),realm varchar(127),primary key (origin))", "CREATE TABLE turn_realm_option (realm varchar(127) default '', opt varchar(32), value varchar(128), primary key (realm,opt))", "CREATE TABLE oauth_key (kid varchar(128),ikm_key varchar(256),timestamp bigint default 0,lifetime integer default 0,as_rs_alg varchar(64) default '',realm varchar(127) default '',primary key (kid))", "CREATE TABLE admin_user (name varchar(32), realm varchar(127), password varchar(127), primary key (name))", NULL }; int i = 0; while(statements[i]) { sqlite3_stmt *statement = NULL; int rc = 0; if ((rc = sqlite3_prepare(sqliteconnection, statements[i], -1, &statement, 0)) == SQLITE_OK) { sqlite3_step(statement); } sqlite3_finalize(statement); ++i; } } static sqlite3 * get_sqlite_connection(void) { persistent_users_db_t *pud = get_persistent_users_db(); sqlite3 *sqliteconnection = (sqlite3 *)pthread_getspecific(connection_key); if(!sqliteconnection) { fix_user_directory(pud->userdb); sqlite_init_multithreaded(); int rc = sqlite3_open(pud->userdb, &sqliteconnection); if(!sqliteconnection || (rc != SQLITE_OK)) { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open SQLite DB connection: <%s>, runtime error:\n %s\n (If your intention is to use an SQLite database for the TURN server, then\n check and fix, if necessary, the effective permissions of the TURN server\n process and of the DB directory and then re-start the TURN server)\n",pud->userdb,errmsg); if(sqliteconnection) { sqlite3_close(sqliteconnection); sqliteconnection=NULL; } turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_UNKNOWN; } else { init_sqlite_database(sqliteconnection); if(!donot_print_connection_success){ TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SQLite DB connection success: %s\n",pud->userdb); donot_print_connection_success = 1; } } if(sqliteconnection) { (void) pthread_setspecific(connection_key, sqliteconnection); } } return sqliteconnection; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// static int sqlite_get_auth_secrets(secrets_list_t *sl, u08bits *realm) { int ret = -1; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; snprintf(statement, sizeof(statement) - 1, "select value from turn_secret where realm='%s'", realm); sqlite_lock(0); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { int ctotal = sqlite3_column_count(st); ret = 0; while (ctotal > 0) { int res = sqlite3_step(st); if (res == SQLITE_ROW) { int type = sqlite3_column_type(st, 0); if (type != SQLITE_NULL) add_to_secrets_list(sl, (const char*) sqlite3_column_text(st, 0)); } else if (res == SQLITE_DONE) { break; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); ret = -1; break; } } } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(0); } return ret; } static int sqlite_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) { int ret = -1; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; /* 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); sqlite_lock(0); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { int res = sqlite3_step(st); if (res == SQLITE_ROW) { char *kval = turn_strdup((const char*) sqlite3_column_text(st, 0)); size_t sz = get_hmackey_size(SHATYPE_DEFAULT); if (convert_string_key_to_binary(kval, key, sz) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key: %s, user %s\n", kval, usname); } else { ret = 0; } turn_free(kval,strlen(kval)+1); } } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(0); } return ret; } static int sqlite_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; /* 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); sqlite3 *sqliteconnection = get_sqlite_connection(); if(sqliteconnection) { sqlite_lock(0); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { int res = sqlite3_step(st); if (res == SQLITE_ROW) { STRCPY(key->ikm_key,sqlite3_column_text(st, 0)); key->timestamp = (u64bits)strtoll((const char*)sqlite3_column_text(st, 1),NULL,10); key->lifetime = (u32bits)strtol((const char*)sqlite3_column_text(st, 2),NULL,10); STRCPY(key->as_rs_alg,sqlite3_column_text(st, 3)); STRCPY(key->realm,sqlite3_column_text(st, 4)); STRCPY(key->kid,kid); ret = 0; } } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(0); } return ret; } static int sqlite_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_; donot_print_connection_success=1; int ret = -1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,as_rs_alg,realm,kid from oauth_key order by kid"); sqlite3 *sqliteconnection = get_sqlite_connection(); if(sqliteconnection) { sqlite_lock(0); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { ret = 0; while (1) { int res = sqlite3_step(st); if (res == SQLITE_ROW) { STRCPY(key->ikm_key,sqlite3_column_text(st, 0)); key->timestamp = (u64bits)strtoll((const char*)sqlite3_column_text(st, 1),NULL,10); key->lifetime = (u32bits)strtol((const char*)sqlite3_column_text(st, 2),NULL,10); STRCPY(key->as_rs_alg,sqlite3_column_text(st, 3)); STRCPY(key->realm,sqlite3_column_text(st, 4)); STRCPY(key->kid,sqlite3_column_text(st, 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\n", key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->as_rs_alg); } } else if (res == SQLITE_DONE) { break; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); ret = -1; break; } } } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(0); } return ret; } static int sqlite_set_user_key(u08bits *usname, u08bits *realm, const char *key) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; donot_print_connection_success=1; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { sqlite_lock(1); snprintf(statement, sizeof(statement), "insert or replace into turnusers_lt (realm,name,hmackey) values('%s','%s','%s')", realm, usname, key); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { sqlite3_step(st); ret = 0; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(1); } return ret; } static int sqlite_set_oauth_key(oauth_key_data_raw *key) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; donot_print_connection_success=1; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { snprintf( statement, sizeof(statement), "insert or replace 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); sqlite_lock(1); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { sqlite3_step(st); ret = 0; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error updating SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(1); } return ret; } static int sqlite_del_user(u08bits *usname, u08bits *realm) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; donot_print_connection_success=1; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { snprintf(statement, sizeof(statement), "delete from turnusers_lt where name='%s' and realm='%s'", usname, realm); sqlite_lock(1); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { sqlite3_step(st); ret = 0; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(1); } return ret; } static int sqlite_del_oauth_key(const u08bits *kid) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; donot_print_connection_success=1; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { snprintf(statement, sizeof(statement), "delete from oauth_key where kid = '%s'", (const char*) kid); sqlite_lock(1); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { sqlite3_step(st); ret = 0; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(1); } return ret; } static int sqlite_list_users(u08bits *realm, secrets_list_t *users, secrets_list_t *realms) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; donot_print_connection_success=1; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { 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"); } sqlite_lock(0); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { ret = 0; while (1) { int res = sqlite3_step(st); if (res == SQLITE_ROW) { const char* kval = (const char*) sqlite3_column_text(st, 0); const char* rval = (const char*) sqlite3_column_text(st, 1); 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); } } else if (res == SQLITE_DONE) { break; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); ret = -1; break; } } } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(0); } return ret; } static int sqlite_list_secrets(u08bits *realm, secrets_list_t *secrets, secrets_list_t *realms) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; sqlite3_stmt *st = NULL; int rc = 0; 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; sqlite3 *sqliteconnection = get_sqlite_connection(); if(sqliteconnection) { sqlite_lock(0); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { int res = 0; while(1) { res = sqlite3_step(st); if (res == SQLITE_ROW) { ret = 0; const char* kval = (const char*) sqlite3_column_text(st, 0); if(kval) { const char* rval = (const char*) sqlite3_column_text(st, 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); } } } else if (res == SQLITE_DONE) { break; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); ret = -1; break; } } } sqlite3_finalize(st); sqlite_unlock(0); } return ret; } static int sqlite_del_secret(u08bits *secret, u08bits *realm) { int ret = -1; donot_print_connection_success=1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { 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); sqlite_lock(1); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { sqlite3_step(st); ret = 0; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(1); } return ret; } static int sqlite_set_secret(u08bits *secret, u08bits *realm) { int ret = -1; donot_print_connection_success = 1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { snprintf(statement,sizeof(statement),"insert or replace into turn_secret (realm,value) values('%s','%s')",realm,secret); sqlite_lock(1); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { sqlite3_step(st); ret = 0; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(1); } return ret; } static int sqlite_add_origin(u08bits *origin, u08bits *realm) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; donot_print_connection_success=1; sqlite3 *sqliteconnection = get_sqlite_connection(); if(sqliteconnection) { snprintf(statement,sizeof(statement),"insert or replace into turn_origin_to_realm (origin,realm) values('%s','%s')",origin,realm); sqlite_lock(1); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { sqlite3_step(st); ret = 0; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(1); } return ret; } static int sqlite_del_origin(u08bits *origin) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; donot_print_connection_success=1; sqlite3 *sqliteconnection = get_sqlite_connection(); if(sqliteconnection) { snprintf(statement,sizeof(statement),"delete from turn_origin_to_realm where origin='%s'",origin); sqlite_lock(1); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { sqlite3_step(st); ret = 0; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(1); } return ret; } static int sqlite_list_origins(u08bits *realm, secrets_list_t *origins, secrets_list_t *realms) { int ret = -1; u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; donot_print_connection_success = 1; sqlite3_stmt *st = NULL; int rc = 0; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { 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"); } sqlite_lock(0); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { ret = 0; while (1) { int res = sqlite3_step(st); if (res == SQLITE_ROW) { const char* kval = (const char*) sqlite3_column_text(st, 0); const char* rval = (const char*) sqlite3_column_text(st, 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); } } else if (res == SQLITE_DONE) { break; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); ret = -1; break; } } } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(0); } return ret; } static int sqlite_set_realm_option_one(u08bits *realm, unsigned long value, const char* opt) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; donot_print_connection_success=1; sqlite3 *sqliteconnection = get_sqlite_connection(); if(sqliteconnection) { if(value>0) { snprintf(statement,sizeof(statement),"insert or replace into turn_realm_option (realm,opt,value) values('%s','%s','%lu')",realm,opt,(unsigned long)value); sqlite_lock(1); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { sqlite3_step(st); ret = 0; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(1); } } return ret; } static int sqlite_list_realm_options(u08bits *realm) { int ret = -1; donot_print_connection_success = 1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { 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"); } sqlite_lock(0); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { ret = 0; while (1) { int res = sqlite3_step(st); if (res == SQLITE_ROW) { const char* rval = (const char*) sqlite3_column_text(st, 0); const char* oval = (const char*) sqlite3_column_text(st, 1); const char* vval = (const char*) sqlite3_column_text(st, 2); printf("%s[%s]=%s\n",oval,rval,vval); } else if (res == SQLITE_DONE) { break; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); ret = -1; break; } } } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(0); } return ret; } static void sqlite_auth_ping(void * rch) { UNUSED_ARG(rch); } static int sqlite_get_ip_list(const char *kind, ip_range_list_t * list) { int ret = -1; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; snprintf(statement, sizeof(statement), "select ip_range,realm from %s_peer_ip", kind); sqlite_lock(0); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { ret = 0; while (1) { int res = sqlite3_step(st); if (res == SQLITE_ROW) { const char* kval = (const char*) sqlite3_column_text(st, 0); const char* rval = (const char*) sqlite3_column_text(st, 1); add_ip_list_range(kval, rval, list); } else if (res == SQLITE_DONE) { break; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); ret = -1; break; } } } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(0); } return ret; } static int sqlite_set_permission_ip(const char *kind, u08bits *realm, const char* ip, int del) { int ret = -1; u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; donot_print_connection_success=1; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { sqlite_lock(1); 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 or replace into %s_peer_ip (realm,ip_range) values('%s','%s')", kind, (char*)realm, ip); } if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { sqlite3_step(st); ret = 0; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error updating SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(1); } return ret; } static void sqlite_reread_realms(secrets_list_t * realms_list) { sqlite3 *sqliteconnection = get_sqlite_connection(); if(sqliteconnection) { char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; { snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm"); sqlite_lock(0); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { ur_string_map *o_to_realm_new = ur_string_map_create(turn_free_simple); while (1) { int res = sqlite3_step(st); if (res == SQLITE_ROW) { char* oval = turn_strdup((const char*) sqlite3_column_text(st, 0)); char* rval = turn_strdup((const char*) sqlite3_column_text(st, 1)); get_realm(rval); ur_string_map_value_type value = rval; ur_string_map_put(o_to_realm_new, (const ur_string_map_key_type) oval, value); turn_free(oval,strlen(oval)+1); } else if (res == SQLITE_DONE) { break; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); break; } } update_o_to_realm(o_to_realm_new); } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(0); } { { size_t i = 0; size_t rlsz = 0; lock_realms(); rlsz = realms_list->sz; unlock_realms(); for (i = 0; isecrets[i]; realm_params_t* rp = get_realm(realm); lock_realms(); rp->options.perf_options.max_bps = turn_params.max_bps; unlock_realms(); lock_realms(); rp->options.perf_options.total_quota = turn_params.total_quota; unlock_realms(); lock_realms(); rp->options.perf_options.user_quota = turn_params.user_quota; unlock_realms(); } } sqlite_lock(0); snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option"); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { while (1) { int res = sqlite3_step(st); if (res == SQLITE_ROW) { char* rval = turn_strdup((const char*) sqlite3_column_text(st, 0)); const char* oval = (const char*) sqlite3_column_text(st, 1); const char* vval = (const char*) sqlite3_column_text(st, 2); 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); } turn_free(rval,strlen(rval)+1); } else if (res == SQLITE_DONE) { break; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); break; } } } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(0); } } } //////////////////////////////////////////////////// static int sqlite_get_admin_user(const u08bits *usname, u08bits *realm, password_t pwd) { int ret = -1; realm[0]=0; pwd[0]=0; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; snprintf(statement, sizeof(statement), "select realm,password from admin_user where name='%s'", usname); sqlite_lock(0); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { int res = sqlite3_step(st); if (res == SQLITE_ROW) { const char *kval = (const char*) sqlite3_column_text(st, 0); if(kval) { strncpy((char*)realm,kval,STUN_MAX_REALM_SIZE); } kval = (const char*) sqlite3_column_text(st, 1); if(kval) { strncpy((char*)pwd,kval,STUN_MAX_PWD_SIZE); } ret = 0; } } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(0); } return ret; } static int sqlite_set_admin_user(const u08bits *usname, const u08bits *realm, const password_t pwd) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; donot_print_connection_success=1; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { sqlite_lock(1); snprintf(statement, sizeof(statement), "insert or replace into admin_user (realm,name,password) values('%s','%s','%s')", realm, usname, pwd); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { sqlite3_step(st); ret = 0; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(1); } return ret; } static int sqlite_del_admin_user(const u08bits *usname) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; donot_print_connection_success=1; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { snprintf(statement, sizeof(statement), "delete from admin_user where name='%s'", usname); sqlite_lock(1); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { sqlite3_step(st); ret = 0; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(1); } return ret; } static int sqlite_list_admin_users(int no_print) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; donot_print_connection_success=1; sqlite3 *sqliteconnection = get_sqlite_connection(); if (sqliteconnection) { snprintf(statement, sizeof(statement), "select name,realm from admin_user order by realm,name"); sqlite_lock(0); if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) { ret = 0; while (1) { int res = sqlite3_step(st); if (res == SQLITE_ROW) { const char* kval = (const char*) sqlite3_column_text(st, 0); const char* rval = (const char*) sqlite3_column_text(st, 1); if(!no_print) { if (rval && *rval) { printf("%s[%s]\n", kval, rval); } else { printf("%s\n", kval); } } ++ret; } else if (res == SQLITE_DONE) { break; } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); ret = -1; break; } } } else { const char* errmsg = sqlite3_errmsg(sqliteconnection); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg); } sqlite3_finalize(st); sqlite_unlock(0); } return ret; } /////////////////////////////////////////////////////// static const turn_dbdriver_t driver = { &sqlite_get_auth_secrets, &sqlite_get_user_key, &sqlite_set_user_key, &sqlite_del_user, &sqlite_list_users, &sqlite_list_secrets, &sqlite_del_secret, &sqlite_set_secret, &sqlite_add_origin, &sqlite_del_origin, &sqlite_list_origins, &sqlite_set_realm_option_one, &sqlite_list_realm_options, &sqlite_auth_ping, &sqlite_get_ip_list, &sqlite_set_permission_ip, &sqlite_reread_realms, &sqlite_set_oauth_key, &sqlite_get_oauth_key, &sqlite_del_oauth_key, &sqlite_list_oauth_keys, &sqlite_get_admin_user, &sqlite_set_admin_user, &sqlite_del_admin_user, &sqlite_list_admin_users }; ////////////////////////////////////////////////// const turn_dbdriver_t * get_sqlite_dbdriver(void) { return &driver; } ////////////////////////////////////////////////// #else const turn_dbdriver_t * get_sqlite_dbdriver(void) { return NULL; } #endif turnserver-4.5.1.1/src/apps/relay/dbdrivers/dbd_redis.h0000664000175000017500000000344013436577655021535 0ustar misimisi/* * Copyright (C) 2011, 2012, 2013 Citrix Systems * Copyright (C) 2014 Vivocha S.p.A. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef __DBD_REDIS__ #define __DBD_REDIS__ #include "dbdriver.h" #ifdef __cplusplus extern "C" { #endif const turn_dbdriver_t * get_redis_dbdriver(void); #ifdef __cplusplus } #endif #endif /// __DBD_REDIS__/// turnserver-4.5.1.1/src/apps/relay/dbdrivers/dbd_pgsql.c0000664000175000017500000007000613436577655021552 0ustar misimisi/* * Copyright (C) 2011, 2012, 2013 Citrix Systems * Copyright (C) 2014 Vivocha S.p.A. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "../mainrelay.h" #include "dbd_pgsql.h" #if !defined(TURN_NO_PQ) #include /////////////////////////////////////////////////////////////////////////////////////////////////////////// static int donot_print_connection_success = 0; static PGconn *get_pqdb_connection(void) { persistent_users_db_t *pud = get_persistent_users_db(); PGconn *pqdbconnection = (PGconn*)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,errmsg); turn_free(errmsg,strlen(errmsg)+1); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection: <%s>, unknown connection string format error\n",pud->userdb); } } else { PQconninfoFree(co); if(errmsg) turn_free(errmsg,strlen(errmsg)+1); pqdbconnection = PQconnectdb(pud->userdb); if(!pqdbconnection) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection: <%s>, runtime error\n",pud->userdb); } else { ConnStatusType status = PQstatus(pqdbconnection); if(status != CONNECTION_OK) { PQfinish(pqdbconnection); pqdbconnection = NULL; TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection: <%s>, runtime error\n",pud->userdb); } else if(!donot_print_connection_success){ TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "PostgreSQL DB connection success: %s\n",pud->userdb); 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, u08bits *realm) { int ret = -1; PGconn * pqc = get_pqdb_connection(); if(pqc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement)-1,"select value from turn_secret where realm='%s'",realm); PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc)); } else { int i = 0; for(i=0;iikm_key,PQgetvalue(res,0,0)); key->timestamp = (u64bits)strtoll(PQgetvalue(res,0,1),NULL,10); key->lifetime = (u32bits)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;iikm_key,PQgetvalue(res,i,0)); key->timestamp = (u64bits)strtoll(PQgetvalue(res,i,1),NULL,10); key->lifetime = (u32bits)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(u08bits *usname, u08bits *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(u08bits *usname, u08bits *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 u08bits *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(u08bits *realm, secrets_list_t *users, secrets_list_t *realms) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; u08bits 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> %s\n",kval,rval); } } } } ret = 0; } if(res) { PQclear(res); } } return ret; } static int pgsql_set_realm_option_one(u08bits *realm, unsigned long value, const char* opt) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if(pqc) { { snprintf(statement,sizeof(statement),"delete from turn_realm_option where realm='%s' and opt='%s'",realm,opt); PGresult *res = PQexec(pqc, statement); if(res) { PQclear(res); } } if(value>0) { snprintf(statement,sizeof(statement),"insert into turn_realm_option (realm,opt,value) values('%s','%s','%lu')",realm,opt,(unsigned long)value); PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting realm option information: %s\n",PQerrorMessage(pqc)); } else { ret = 0; } if(res) { PQclear(res); } } } return ret; } static int pgsql_list_realm_options(u08bits *realm) { int ret = -1; donot_print_connection_success = 1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if(pqc) { if(realm && realm[0]) { snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option where realm='%s' order by realm,opt",realm); } else { snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option order by realm,opt"); } PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc)); } else { int i = 0; for(i=0;isz; unlock_realms(); for (i = 0; isecrets[i]; realm_params_t* rp = get_realm(realm); lock_realms(); rp->options.perf_options.max_bps = turn_params.max_bps; unlock_realms(); lock_realms(); rp->options.perf_options.total_quota = turn_params.total_quota; unlock_realms(); lock_realms(); rp->options.perf_options.user_quota = turn_params.user_quota; unlock_realms(); } } snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option"); PGresult *res = PQexec(pqc, statement); if(res && (PQresultStatus(res) == PGRES_TUPLES_OK)) { int i = 0; for(i=0;ioptions.perf_options.max_bps = (band_limit_t)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 u08bits *usname, u08bits *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 u08bits *usname, const u08bits *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 u08bits *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 #ifdef __cplusplus extern "C" { #endif ////////////////////////////////////////////////////// struct _super_memory; typedef struct _super_memory super_memory_t; ////////////////////////////////////////////////////// void init_super_memory(void); super_memory_t* new_super_memory_region(void); #define allocate_super_memory_region(region,size) allocate_super_memory_region_func(region, size, __FILE__, __FUNCTION__, __LINE__) void* allocate_super_memory_region_func(super_memory_t *region, size_t size, const char* file, const char* func, int line); ///////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif /* __IOA_SM__ */ turnserver-4.5.1.1/src/apps/relay/mainrelay.c0000664000175000017500000031635113436577655017616 0ustar misimisi/* * 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 "mainrelay.h" #if (defined LIBRESSL_VERSION_NUMBER && OPENSSL_VERSION_NUMBER == 0x20000000L) #undef OPENSSL_VERSION_NUMBER #define OPENSSL_VERSION_NUMBER 0x1000107FL #endif ////// TEMPORARY data ////////// static int use_lt_credentials = 0; static int anon_credentials = 0; //long term credential static int use_ltc = 0; //timelimited long term credential static int use_tltc = 0; ////// ALPN ////////// #if ALPN_SUPPORTED char STUN_ALPN[128] = "stun.nat-discovery"; char TURN_ALPN[128] = "stun.turn"; char HTTP_ALPN[128] = "http/1.1"; #endif ////// TURNDB ////////////// #if defined(Q) #undef Q #endif #define Q(x) #x #if defined(QUOTE) #undef QUOTE #endif #define QUOTE(x) Q(x) #define DEFAULT_USERDB_FILE QUOTE(TURNDB) //////TURN PARAMS STRUCTURE DEFINITION ////// #define DEFAULT_GENERAL_RELAY_SERVERS_NUMBER (1) turn_params_t turn_params = { NULL, NULL, #if TLSv1_1_SUPPORTED NULL, #if TLSv1_2_SUPPORTED NULL, #endif #endif #if DTLS_SUPPORTED NULL, #endif #if DTLSv1_2_SUPPORTED NULL, #endif DH_1066, "", "", "", "turn_server_cert.pem","turn_server_pkey.pem", "", "", 0,0,0, #if !TLS_SUPPORTED 1, #else 0, #endif #if !DTLS_SUPPORTED 1, #else 0, #endif NULL, PTHREAD_MUTEX_INITIALIZER, //////////////// Common params //////////////////// TURN_VERBOSE_NONE,0,0,0,0, "/var/run/turnserver.pid", DEFAULT_STUN_PORT,DEFAULT_STUN_TLS_PORT,0,0,1, 0,0,0,0, "", "",0, { NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0,0,NULL,NULL,NULL }, {NULL, 0},{NULL, 0}, NEV_UNKNOWN, { "Unknown", "UDP listening socket per session", "UDP thread per network endpoint", "UDP thread per CPU core" }, //////////////// Relay servers ////////////////////////////////// LOW_DEFAULT_PORTS_BOUNDARY,HIGH_DEFAULT_PORTS_BOUNDARY,0,0,0,"", 0,NULL,0,NULL,DEFAULT_GENERAL_RELAY_SERVERS_NUMBER,0, ////////////// Auth server ///////////////////////////////////// "","",0, /////////////// AUX SERVERS //////////////// {NULL,0,{0,NULL}},0, /////////////// ALTERNATE SERVERS //////////////// {NULL,0,{0,NULL}},{NULL,0,{0,NULL}}, /////////////// stop server //////////////// 0, /////////////// MISC PARAMS //////////////// 0, /* stun_only */ 0, /* no_stun */ 0, /* secure_stun */ 0, /* server_relay */ 0, /* fingerprint */ ':', /* rest_api_separator */ STUN_DEFAULT_NONCE_EXPIRATION_TIME, /* stale_nonce */ STUN_DEFAULT_MAX_ALLOCATE_LIFETIME, /* max_allocate_lifetime */ STUN_DEFAULT_CHANNEL_LIFETIME, /* channel_lifetime */ STUN_DEFAULT_PERMISSION_LIFETIME, /* permission_lifetime */ 0, /* mobility */ TURN_CREDENTIALS_NONE, /* ct */ 0, /* use_auth_secret_with_timestamp */ 0, /* max_bps */ 0, /* bps_capacity */ 0, /* bps_capacity_allocated */ 0, /* total_quota */ 0, /* user_quota */ ///////////// Users DB ////////////// { (TURN_USERDB_TYPE)0, {"\0"}, {0,NULL, {NULL,0}} }, ///////////// CPUs ////////////////// DEFAULT_CPUS_NUMBER, ///////// Encryption ///////// "", /* secret_key_file */ "", /* secret_key */ 0 /* keep_address_family */ }; //////////////// OpenSSL Init ////////////////////// static void openssl_setup(void); /* * openssl genrsa -out pkey 2048 * openssl req -new -key pkey -out cert.req * openssl x509 -req -days 365 -in cert.req -signkey pkey -out cert * */ //////////// Common static process params //////// static gid_t procgroupid = 0; static uid_t procuserid = 0; static gid_t procgroupid_set = 0; static uid_t procuserid_set = 0; static char procusername[1025]="\0"; static char procgroupname[1025]="\0"; ////////////// Configuration functionality //////////////////////////////// static void read_config_file(int argc, char **argv, int pass); static void reload_ssl_certs(evutil_socket_t sock, short events, void *args); ////////////////////////////////////////////////// static int make_local_listeners_list(void) { int ret = 0; struct ifaddrs * ifs = NULL; struct ifaddrs * ifa = NULL; char saddr[INET6_ADDRSTRLEN] = ""; if((getifaddrs(&ifs) == 0) && ifs) { for (ifa = ifs; ifa != NULL; ifa = ifa->ifa_next) { if(!(ifa->ifa_flags & IFF_UP)) continue; if(!(ifa->ifa_addr)) continue; if (ifa ->ifa_addr->sa_family == AF_INET) { if(!inet_ntop(AF_INET, &((struct sockaddr_in *) ifa->ifa_addr)->sin_addr, saddr, INET_ADDRSTRLEN)) continue; if(strstr(saddr,"169.254.") == saddr) continue; if(!strcmp(saddr,"0.0.0.0")) continue; } else if (ifa->ifa_addr->sa_family == AF_INET6) { if(!inet_ntop(AF_INET6, &((struct sockaddr_in6 *) ifa->ifa_addr)->sin6_addr, saddr, INET6_ADDRSTRLEN)) continue; if(strstr(saddr,"fe80") == saddr) continue; if(!strcmp(saddr,"::")) continue; } else { continue; } add_listener_addr(saddr); if(!(ifa->ifa_flags & IFF_LOOPBACK)) ret++; } freeifaddrs(ifs); } return ret; } static int make_local_relays_list(int allow_local, int family) { struct ifaddrs * ifs = NULL; struct ifaddrs * ifa = NULL; char saddr[INET6_ADDRSTRLEN] = ""; getifaddrs(&ifs); int counter = 0; if (ifs) { for (ifa = ifs; ifa != NULL; ifa = ifa->ifa_next) { if(!(ifa->ifa_flags & IFF_UP)) continue; if(!(ifa->ifa_name)) continue; if(!(ifa ->ifa_addr)) continue; if(!allow_local && (ifa->ifa_flags & IFF_LOOPBACK)) continue; if (ifa ->ifa_addr->sa_family == AF_INET) { if(family != AF_INET) continue; if(!inet_ntop(AF_INET, &((struct sockaddr_in *) ifa->ifa_addr)->sin_addr, saddr, INET_ADDRSTRLEN)) continue; if(strstr(saddr,"169.254.") == saddr) continue; if(!strcmp(saddr,"0.0.0.0")) continue; } else if (ifa->ifa_addr->sa_family == AF_INET6) { if(family != AF_INET6) continue; if(!inet_ntop(AF_INET6, &((struct sockaddr_in6 *) ifa->ifa_addr)->sin6_addr, saddr, INET6_ADDRSTRLEN)) continue; if(strstr(saddr,"fe80") == saddr) continue; if(!strcmp(saddr,"::")) continue; } else continue; if(add_relay_addr(saddr)>0) { counter += 1; } } freeifaddrs(ifs); } return counter; } int get_a_local_relay(int family, ioa_addr *relay_addr) { struct ifaddrs * ifs = NULL; int allow_local = 0; int ret = -1; char saddr[INET6_ADDRSTRLEN] = ""; getifaddrs(&ifs); if (ifs) { galr_start: { struct ifaddrs *ifa = NULL; for (ifa = ifs; ifa != NULL ; ifa = ifa->ifa_next) { if (!(ifa->ifa_flags & IFF_UP)) continue; if (!(ifa->ifa_name)) continue; if (!(ifa->ifa_addr)) continue; if (!allow_local && (ifa->ifa_flags & IFF_LOOPBACK)) continue; if (ifa->ifa_addr->sa_family == AF_INET) { if (family != AF_INET) continue; if (!inet_ntop(AF_INET, &((struct sockaddr_in *) ifa->ifa_addr)->sin_addr, saddr, INET_ADDRSTRLEN)) continue; if (strstr(saddr, "169.254.") == saddr) continue; if (!strcmp(saddr, "0.0.0.0")) continue; } else if (ifa->ifa_addr->sa_family == AF_INET6) { if (family != AF_INET6) continue; if (!inet_ntop(AF_INET6, &((struct sockaddr_in6 *) ifa->ifa_addr)->sin6_addr, saddr, INET6_ADDRSTRLEN)) continue; if (strstr(saddr, "fe80") == saddr) continue; if (!strcmp(saddr, "::")) continue; } else continue; if (make_ioa_addr((const u08bits*) saddr, 0, relay_addr) < 0) { continue; } else { ret = 0; break; } } } if(ret<0 && !allow_local) { allow_local = 1; goto galr_start; } freeifaddrs(ifs); } return -1; } ////////////////////////////////////////////////// static char Usage[] = "Usage: turnserver [options]\n" "Options:\n" " -d, --listening-device Listener interface device (NOT RECOMMENDED. Optional, Linux only).\n" " -p, --listening-port TURN listener port (Default: 3478).\n" " Note: actually, TLS & DTLS sessions can connect to the \"plain\" TCP & UDP port(s), too,\n" " if allowed by configuration.\n" " --tls-listening-port TURN listener port for TLS & DTLS listeners\n" " (Default: 5349).\n" " Note: actually, \"plain\" TCP & UDP sessions can connect to the TLS & DTLS port(s), too,\n" " if allowed by configuration. The TURN server\n" " \"automatically\" recognizes the type of traffic. Actually, two listening\n" " endpoints (the \"plain\" one and the \"tls\" one) are equivalent in terms of\n" " functionality; but we keep both endpoints to satisfy the RFC 5766 specs.\n" " For secure TCP connections, we currently support SSL version 3 and\n" " TLS versions 1.0, 1.1 and 1.2. For secure UDP connections, we support\n" " DTLS version 1.\n" " --alt-listening-port Alternative listening port for STUN CHANGE_REQUEST (in RFC 5780 sense, \n" " or in old RFC 3489 sense, default is \"listening port plus one\").\n" " --alt-tls-listening-port Alternative listening port for TLS and DTLS,\n" " the default is \"TLS/DTLS port plus one\".\n" " -L, --listening-ip Listener IP address of relay server. Multiple listeners can be specified.\n" " --aux-server Auxiliary STUN/TURN server listening endpoint.\n" " Auxiliary servers do not have alternative ports and\n" " they do not support RFC 5780 functionality (CHANGE REQUEST).\n" " Valid formats are 1.2.3.4:5555 for IPv4 and [1:2::3:4]:5555 for IPv6.\n" " --udp-self-balance (recommended for older Linuxes only) Automatically balance UDP traffic\n" " over auxiliary servers (if configured).\n" " The load balancing is using the ALTERNATE-SERVER mechanism.\n" " The TURN client must support 300 ALTERNATE-SERVER response for this functionality.\n" " -i, --relay-device Relay interface device for relay sockets (NOT RECOMMENDED. Optional, Linux only).\n" " -E, --relay-ip Relay address (the local IP address that will be used to relay the\n" " packets to the peer).\n" " Multiple relay addresses may be used.\n" " The same IP(s) can be used as both listening IP(s) and relay IP(s).\n" " If no relay IP(s) specified, then the turnserver will apply the default\n" " policy: it will decide itself which relay addresses to be used, and it\n" " will always be using the client socket IP address as the relay IP address\n" " of the TURN session (if the requested relay address family is the same\n" " as the family of the client socket).\n" " -X, --external-ip TURN Server public/private address mapping, if the server is behind NAT.\n" " In that situation, if a -X is used in form \"-X ip\" then that ip will be reported\n" " as relay IP address of all allocations. This scenario works only in a simple case\n" " when one single relay address is be used, and no STUN CHANGE_REQUEST\n" " functionality is required.\n" " That single relay address must be mapped by NAT to the 'external' IP.\n" " For that 'external' IP, NAT must forward ports directly (relayed port 12345\n" " must be always mapped to the same 'external' port 12345).\n" " In more complex case when more than one IP address is involved,\n" " that option must be used several times in the command line, each entry must\n" " have form \"-X public-ip/private-ip\", to map all involved addresses.\n" " --allow-loopback-peers Allow peers on the loopback addresses (127.x.x.x and ::1).\n" " --no-multicast-peers Disallow peers on well-known broadcast addresses (224.0.0.0 and above, and FFXX:*).\n" " -m, --relay-threads Number of relay threads to handle the established connections\n" " (in addition to authentication thread and the listener thread).\n" " If explicitly set to 0 then application runs in single-threaded mode.\n" " If not set then a default OS-dependent optimal algorithm will be employed.\n" " The default thread number is the number of CPUs.\n" " In older systems (pre-Linux 3.9) the number of UDP relay threads always equals\n" " the number of listening endpoints (unless -m 0 is set).\n" " --min-port Lower bound of the UDP port range for relay endpoints allocation.\n" " Default value is 49152, according to RFC 5766.\n" " --max-port Upper bound of the UDP port range for relay endpoints allocation.\n" " Default value is 65535, according to RFC 5766.\n" " -v, --verbose 'Moderate' verbose mode.\n" " -V, --Verbose Extra verbose mode, very annoying (for debug purposes only).\n" " -o, --daemon Start process as daemon (detach from current shell).\n" " --prod Production mode: hide the software version.\n" " -f, --fingerprint Use fingerprints in the TURN messages.\n" " -a, --lt-cred-mech Use the long-term credential mechanism.\n" " -z, --no-auth Do not use any credential mechanism, allow anonymous access.\n" " -u, --user User account, in form 'username:password', for long-term credentials.\n" " Cannot be used with TURN REST API.\n" " -r, --realm The default realm to be used for the users when no explicit\n" " origin/realm relationship was found in the database.\n" " Must be used with long-term credentials \n" " mechanism or with TURN REST API.\n" " --check-origin-consistency The flag that sets the origin consistency check:\n" " across the session, all requests must have the same\n" " main ORIGIN attribute value (if the ORIGIN was\n" " initially used by the session).\n" " -q, --user-quota Per-user allocation quota: how many concurrent allocations a user can create.\n" " This option can also be set through the database, for a particular realm.\n" " -Q, --total-quota Total allocations quota: global limit on concurrent allocations.\n" " This option can also be set through the database, for a particular realm.\n" " -s, --max-bps Default max bytes-per-second bandwidth a TURN session is allowed to handle\n" " (input and output network streams are treated separately). Anything above\n" " that limit will be dropped or temporary suppressed\n" " (within the available buffer limits).\n" " This option can also be set through the database, for a particular realm.\n" " -B, --bps-capacity Maximum server capacity.\n" " Total bytes-per-second bandwidth the TURN server is allowed to allocate\n" " for the sessions, combined (input and output network streams are treated separately).\n" " -c Configuration file name (default - turnserver.conf).\n" #if !defined(TURN_NO_SQLITE) " -b, , --db, --userdb SQLite database file name; default - /var/db/turndb or\n" " /usr/local/var/db/turndb or /var/lib/turn/turndb.\n" #endif #if !defined(TURN_NO_PQ) " -e, --psql-userdb, --sql-userdb PostgreSQL database connection string, if used (default - empty, no PostreSQL DB used).\n" " This database can be used for long-term credentials mechanism users,\n" " and it can store the secret value(s) for secret-based timed authentication in TURN RESP API.\n" " See http://www.postgresql.org/docs/8.4/static/libpq-connect.html for 8.x PostgreSQL\n" " versions format, see \n" " http://www.postgresql.org/docs/9.2/static/libpq-connect.html#LIBPQ-CONNSTRING\n" " for 9.x and newer connection string formats.\n" #endif #if !defined(TURN_NO_MYSQL) " -M, --mysql-userdb MySQL database connection string, if used (default - empty, no MySQL DB used).\n" " This database can be used for long-term credentials mechanism users,\n" " and it can store the secret value(s) for secret-based timed authentication in TURN RESP API.\n" " The connection string my be space-separated list of parameters:\n" " \"host= dbname= user= \\\n password= port= connect_timeout= read_timeout=\".\n\n" " The connection string parameters for the secure communications (SSL):\n" " ca, capath, cert, key, cipher\n" " (see http://dev.mysql.com/doc/refman/5.1/en/ssl-options.html for the\n" " command options description).\n\n" " All connection-string parameters are optional.\n\n" " --secret-key-file This is the file path which contain secret key of aes encryption while using MySQL password encryption.\n" " If you want to use in the MySQL connection string the password in encrypted format,\n" " then set in this option the file path of the secret key. The key which is used to encrypt MySQL password.\n" " Warning: If this option is set, then MySQL password must be set in \"mysql-userdb\" option in encrypted format!\n" " If you want to use cleartext password then do not set this option!\n" #endif #if !defined(TURN_NO_MONGO) " -J, --mongo-userdb MongoDB connection string, if used (default - empty, no MongoDB used).\n" " This database can be used for long-term credentials mechanism users,\n" " and it can store the secret value(s) for secret-based timed authentication in TURN RESP API.\n" #endif #if !defined(TURN_NO_HIREDIS) " -N, --redis-userdb Redis user database connection string, if used (default - empty, no Redis DB used).\n" " This database can be used for long-term credentials mechanism users,\n" " and it can store the secret value(s) for secret-based timed authentication in TURN RESP API.\n" " The connection string my be space-separated list of parameters:\n" " \"host= dbname= \\\n password= port= connect_timeout=\".\n\n" " All connection-string parameters are optional.\n\n" " -O, --redis-statsdb Redis status and statistics database connection string, if used \n" " (default - empty, no Redis stats DB used).\n" " This database keeps allocations status information, and it can be also used for publishing\n" " and delivering traffic and allocation event notifications.\n" " The connection string has the same parameters as redis-userdb connection string.\n" #endif " --use-auth-secret TURN REST API flag.\n" " Flag that sets a special authorization option that is based upon authentication secret\n" " (TURN Server REST API, see TURNServerRESTAPI.pdf). This option is used with timestamp.\n" " --static-auth-secret 'Static' authentication secret value (a string) for TURN REST API only.\n" " If not set, then the turn server will try to use the 'dynamic' value\n" " in turn_secret table in user database (if present).\n" " That database value can be changed on-the-fly\n" " by a separate program, so this is why it is 'dynamic'.\n" " Multiple shared secrets can be used (both in the database and in the \"static\" fashion).\n" " --server-name Server name used for\n" " the oAuth authentication purposes.\n" " The default value is the realm name.\n" " --oauth Support oAuth authentication.\n" " -n Do not use configuration file, take all parameters from the command line only.\n" " --cert Certificate file, PEM format. Same file search rules\n" " applied as for the configuration file.\n" " If both --no-tls and --no_dtls options\n" " are specified, then this parameter is not needed.\n" " --pkey Private key file, PEM format. Same file search rules\n" " applied as for the configuration file.\n" " If both --no-tls and --no-dtls options\n" " --pkey-pwd If the private key file is encrypted, then this password to be used.\n" " --cipher-list <\"cipher-string\"> Allowed OpenSSL cipher list for TLS/DTLS connections.\n" " Default value is \"DEFAULT\".\n" " --CA-file CA file in OpenSSL format.\n" " Forces TURN server to verify the client SSL certificates.\n" " By default, no CA is set and no client certificate check is performed.\n" " --ec-curve-name Curve name for EC ciphers, if supported by OpenSSL\n" " library (TLS and DTLS). The default value is prime256v1,\n" " if pre-OpenSSL 1.0.2 is used. With OpenSSL 1.0.2+,\n" " an optimal curve will be automatically calculated, if not defined\n" " by this option.\n" " --dh566 Use 566 bits predefined DH TLS key. Default size of the predefined key is 1066.\n" " --dh2066 Use 2066 bits predefined DH TLS key. Default size of the predefined key is 1066.\n" " --dh-file Use custom DH TLS key, stored in PEM format in the file.\n" " Flags --dh566 and --dh2066 are ignored when the DH key is taken from a file.\n" " --no-tlsv1 Do not allow TLSv1/DTLSv1 protocol.\n" " --no-tlsv1_1 Do not allow TLSv1.1 protocol.\n" " --no-tlsv1_2 Do not allow TLSv1.2/DTLSv1.2 protocol.\n" " --no-udp Do not start UDP client listeners.\n" " --no-tcp Do not start TCP client listeners.\n" " --no-tls Do not start TLS client listeners.\n" " --no-dtls Do not start DTLS client listeners.\n" " --no-udp-relay Do not allow UDP relay endpoints, use only TCP relay option.\n" " --no-tcp-relay Do not allow TCP relay endpoints, use only UDP relay options.\n" " -l, --log-file Option to set the full path name of the log file.\n" " By default, the turnserver tries to open a log file in\n" " /var/log/turnserver/, /var/log, /var/tmp, /tmp and . (current) directories\n" " (which open operation succeeds first that file will be used).\n" " With this option you can set the definite log file name.\n" " The special names are \"stdout\" and \"-\" - they will force everything\n" " to the stdout; and \"syslog\" name will force all output to the syslog.\n" " --no-stdout-log Flag to prevent stdout log messages.\n" " By default, all log messages are going to both stdout and to\n" " a log file. With this option everything will be going to the log file only\n" " (unless the log file itself is stdout).\n" " --syslog Output all log information into the system log (syslog), do not use the file output.\n" " --simple-log This flag means that no log file rollover will be used, and the log file\n" " name will be constructed as-is, without PID and date appendage.\n" " This option can be used, for example, together with the logrotate tool.\n" " --stale-nonce[=] Use extra security with nonce value having limited lifetime (default 600 secs).\n" " --max-allocate-lifetime Set the maximum value for the allocation lifetime. Default to 3600 secs.\n" " --channel-lifetime Set the lifetime for channel binding, default to 600 secs.\n" " This value MUST not be changed for production purposes.\n" " --permission-lifetime Set the value for the lifetime of the permission. Default to 300 secs.\n" " This MUST not be changed for production purposes.\n" " -S, --stun-only Option to set standalone STUN operation only, all TURN requests will be ignored.\n" " --no-stun Option to suppress STUN functionality, only TURN requests will be processed.\n" " --alternate-server Set the TURN server to redirect the allocate requests (UDP and TCP services).\n" " Multiple alternate-server options can be set for load balancing purposes.\n" " See the docs for more information.\n" " --tls-alternate-server Set the TURN server to redirect the allocate requests (DTLS and TLS services).\n" " Multiple alternate-server options can be set for load balancing purposes.\n" " See the docs for more information.\n" " -C, --rest-api-separator This is the timestamp/username separator symbol (character) in TURN REST API.\n" " The default value is ':'.\n" " --max-allocate-timeout= Max time, in seconds, allowed for full allocation establishment. Default is 60.\n" " --allowed-peer-ip= Specifies an ip or range of ips that are explicitly allowed to connect to the \n" " turn server. Multiple allowed-peer-ip can be set.\n" " --denied-peer-ip= Specifies an ip or range of ips that are not allowed to connect to the turn server.\n" " Multiple denied-peer-ip can be set.\n" " --pidfile <\"pid-file-name\"> File name to store the pid of the process.\n" " Default is /var/run/turnserver.pid (if superuser account is used) or\n" " /var/tmp/turnserver.pid .\n" " --secure-stun Require authentication of the STUN Binding request.\n" " By default, the clients are allowed anonymous access to the STUN Binding functionality.\n" " --proc-user User name to run the turnserver process.\n" " After the initialization, the turnserver process\n" " will make an attempt to change the current user ID to that user.\n" " --proc-group Group name to run the turnserver process.\n" " After the initialization, the turnserver process\n" " will make an attempt to change the current group ID to that group.\n" " --mobility Mobility with ICE (MICE) specs support.\n" " -K, --keep-address-family TURN server allocates address family according TURN\n" " Client <=> Server communication address family. \n" " !! It breaks RFC6156 section-4.2 (violates default IPv4) !!\n" " --no-cli Turn OFF the CLI support. By default it is always ON.\n" " --cli-ip= Local system IP address to be used for CLI server endpoint. Default value\n" " is 127.0.0.1.\n" " --cli-port= CLI server port. Default is 5766.\n" " --cli-password= CLI access password. Default is empty (no password).\n" " For the security reasons, it is recommended to use the encrypted\n" " for of the password (see the -P command in the turnadmin utility).\n" " The dollar signs in the encrypted form must be escaped.\n" " --web-admin Enable Turn Web-admin support. By default it is disabled.\n" " --web-admin-ip= Local system IP address to be used for Web-admin server endpoint. Default value\n" " is 127.0.0.1.\n" " --web-admin-port= Web-admin server port. Default is 8080.\n" " --web-admin-listen-on-workers Enable for web-admin server to listens on STUN/TURN workers STUN/TURN ports.\n" " By default it is disabled for security resons!\n" " (This behavior used to be the default behavior, and was enabled by default.)\n" " --server-relay Server relay. NON-STANDARD AND DANGEROUS OPTION. Only for those applications\n" " when we want to run server applications on the relay endpoints.\n" " This option eliminates the IP permissions check on the packets\n" " incoming to the relay endpoints.\n" " --cli-max-output-sessions Maximum number of output sessions in ps CLI command.\n" " This value can be changed on-the-fly in CLI. The default value is 256.\n" " --ne=[1|2|3] Set network engine type for the process (for internal purposes).\n" " -h Help\n" "\n" " For more information, see the wiki pages:\n" "\n" " https://github.com/coturn/coturn/wiki/\n" "\n"; static char AdminUsage[] = "Usage: turnadmin [command] [options]\n" "\nCommands:\n\n" " -P, --generate-encrypted-password Generate and print to the standard\n" " output an encrypted form of a password\n" " (for web admin user or CLI). See wiki, README or man\n" " pages for more detailed description.\n" " -k, --key generate long-term credential mechanism key for a user\n" " -a, --add add/update a long-term mechanism user\n" " -A, --add-admin add/update a web admin user\n" " -d, --delete delete a long-term mechanism user\n" " -D, --delete-admin delete an admin user\n" " -l, --list list all long-term mechanism users\n" " -L, --list-admin list all admin users\n" " -s, --set-secret= Add shared secret for TURN RESP API\n" " -S, --show-secret Show stored shared secrets for TURN REST API\n" " -X, --delete-secret= Delete a shared secret\n" " --delete-all-secrets Delete all shared secrets for REST API\n" " -O, --add-origin Add origin-to-realm relation.\n" " -R, --del-origin Delete origin-to-realm relation.\n" " -I, --list-origins List origin-to-realm relations.\n" " -g, --set-realm-option Set realm params: max-bps, total-quota, user-quota.\n" " -G, --list-realm-options List realm params.\n" " -E, --generate-encrypted-password-aes Generate and print to the standard\n" " output an encrypted form of password with AES-128\n" "\nOptions with mandatory values:\n\n" #if !defined(TURN_NO_SQLITE) " -b, --db, --userdb SQLite database file, default value is /var/db/turndb or\n" " /usr/local/var/db/turndb or /var/lib/turn/turndb.\n" #endif #if !defined(TURN_NO_PQ) " -e, --psql-userdb, --sql-userdb PostgreSQL user database connection string, if PostgreSQL DB is used.\n" #endif #if !defined(TURN_NO_MYSQL) " -M, --mysql-userdb MySQL user database connection string, if MySQL DB is used.\n" #endif #if !defined(TURN_NO_MONGO) " -J, --mongo-userdb MongoDB user database connection string, if MongoDB is used.\n" #endif #if !defined(TURN_NO_HIREDIS) " -N, --redis-userdb Redis user database connection string, if Redis DB is used.\n" #endif " -u, --user Username\n" " -r, --realm Realm\n" " -p, --password Password\n" " -x, --key-path Generates a 128 bit key into the given path.\n" " -f, --file-key-path Contains a 128 bit key in the given path.\n" " -v, --verify Verify a given base64 encrypted type password.\n" #if !defined(TURN_NO_SQLITE) || !defined(TURN_NO_PQ) || !defined(TURN_NO_MYSQL) || !defined(TURN_NO_MONGO) || !defined(TURN_NO_HIREDIS) " -o, --origin Origin\n" #endif " --max-bps Set value of realm's max-bps parameter.\n" " Setting to zero value means removal of the option.\n" " --total-quota Set value of realm's total-quota parameter.\n" " Setting to zero value means removal of the option.\n" " --user-quota Set value of realm's user-quota parameter.\n" " Setting to zero value means removal of the option.\n" " -h, --help Help\n"; #define OPTIONS "c:d:p:L:E:X:i:m:l:r:u:b:B:e:M:J:N:O:q:Q:s:C:K:vVofhznaAS" #define ADMIN_OPTIONS "PEgGORIHKYlLkaADSdb:e:M:J:N:u:r:p:s:X:o:h:x:v:f:" enum EXTRA_OPTS { NO_UDP_OPT=256, NO_TCP_OPT, NO_TLS_OPT, NO_DTLS_OPT, NO_UDP_RELAY_OPT, NO_TCP_RELAY_OPT, TLS_PORT_OPT, ALT_PORT_OPT, ALT_TLS_PORT_OPT, CERT_FILE_OPT, PKEY_FILE_OPT, PKEY_PWD_OPT, MIN_PORT_OPT, MAX_PORT_OPT, STALE_NONCE_OPT, MAX_ALLOCATE_LIFETIME_OPT, CHANNEL_LIFETIME_OPT, PERMISSION_LIFETIME_OPT, AUTH_SECRET_OPT, DEL_ALL_AUTH_SECRETS_OPT, STATIC_AUTH_SECRET_VAL_OPT, AUTH_SECRET_TS_EXP, /* deprecated */ NO_STDOUT_LOG_OPT, SYSLOG_OPT, SIMPLE_LOG_OPT, AUX_SERVER_OPT, UDP_SELF_BALANCE_OPT, ALTERNATE_SERVER_OPT, TLS_ALTERNATE_SERVER_OPT, NO_MULTICAST_PEERS_OPT, ALLOW_LOOPBACK_PEERS_OPT, MAX_ALLOCATE_TIMEOUT_OPT, ALLOWED_PEER_IPS, DENIED_PEER_IPS, CIPHER_LIST_OPT, PIDFILE_OPT, SECURE_STUN_OPT, CA_FILE_OPT, DH_FILE_OPT, NO_STUN_OPT, PROC_USER_OPT, PROC_GROUP_OPT, MOBILITY_OPT, NO_CLI_OPT, CLI_IP_OPT, CLI_PORT_OPT, CLI_PASSWORD_OPT, WEB_ADMIN_OPT, WEB_ADMIN_IP_OPT, WEB_ADMIN_PORT_OPT, WEB_ADMIN_LISTEN_ON_WORKERS_OPT, SERVER_RELAY_OPT, CLI_MAX_SESSIONS_OPT, EC_CURVE_NAME_OPT, DH566_OPT, DH2066_OPT, NE_TYPE_OPT, NO_SSLV2_OPT, /*deprecated*/ NO_SSLV3_OPT, /*deprecated*/ NO_TLSV1_OPT, NO_TLSV1_1_OPT, NO_TLSV1_2_OPT, CHECK_ORIGIN_CONSISTENCY_OPT, ADMIN_MAX_BPS_OPT, ADMIN_TOTAL_QUOTA_OPT, ADMIN_USER_QUOTA_OPT, SERVER_NAME_OPT, OAUTH_OPT, PROD_OPT, NO_HTTP_OPT, SECRET_KEY_OPT }; struct myoption { const char *name; /* name of long option */ int has_arg; /* whether option takes an argument */ int *flag; /* if not NULL, set *flag to val when option found */ int val; /* if flag is not NULL, value to set *flag to. */ /* if flag is NULL, return value */ }; struct uoptions { union { const struct myoption *m; const struct option *o; } u; }; static const struct myoption long_options[] = { { "listening-device", required_argument, NULL, 'd' }, { "listening-port", required_argument, NULL, 'p' }, { "tls-listening-port", required_argument, NULL, TLS_PORT_OPT }, { "alt-listening-port", required_argument, NULL, ALT_PORT_OPT }, { "alt-tls-listening-port", required_argument, NULL, ALT_TLS_PORT_OPT }, { "listening-ip", required_argument, NULL, 'L' }, { "relay-device", required_argument, NULL, 'i' }, { "relay-ip", required_argument, NULL, 'E' }, { "external-ip", required_argument, NULL, 'X' }, { "relay-threads", required_argument, NULL, 'm' }, { "min-port", required_argument, NULL, MIN_PORT_OPT }, { "max-port", required_argument, NULL, MAX_PORT_OPT }, { "lt-cred-mech", optional_argument, NULL, 'a' }, { "no-auth", optional_argument, NULL, 'z' }, { "user", required_argument, NULL, 'u' }, { "userdb", required_argument, NULL, 'b' }, { "db", required_argument, NULL, 'b' }, #if !defined(TURN_NO_PQ) { "psql-userdb", required_argument, NULL, 'e' }, { "sql-userdb", required_argument, NULL, 'e' }, #endif #if !defined(TURN_NO_MYSQL) { "mysql-userdb", required_argument, NULL, 'M' }, #endif #if !defined(TURN_NO_MONGO) { "mongo-userdb", required_argument, NULL, 'J' }, #endif #if !defined(TURN_NO_HIREDIS) { "redis-userdb", required_argument, NULL, 'N' }, { "redis-statsdb", required_argument, NULL, 'O' }, #endif { "use-auth-secret", optional_argument, NULL, AUTH_SECRET_OPT }, { "static-auth-secret", required_argument, NULL, STATIC_AUTH_SECRET_VAL_OPT }, /* deprecated: */ { "secret-ts-exp-time", optional_argument, NULL, AUTH_SECRET_TS_EXP }, { "realm", required_argument, NULL, 'r' }, { "server-name", required_argument, NULL, SERVER_NAME_OPT }, { "oauth", optional_argument, NULL, OAUTH_OPT }, { "user-quota", required_argument, NULL, 'q' }, { "total-quota", required_argument, NULL, 'Q' }, { "max-bps", required_argument, NULL, 's' }, { "bps-capacity", required_argument, NULL, 'B' }, { "verbose", optional_argument, NULL, 'v' }, { "Verbose", optional_argument, NULL, 'V' }, { "daemon", optional_argument, NULL, 'o' }, { "prod", optional_argument, NULL, PROD_OPT }, { "fingerprint", optional_argument, NULL, 'f' }, { "check-origin-consistency", optional_argument, NULL, CHECK_ORIGIN_CONSISTENCY_OPT }, { "no-udp", optional_argument, NULL, NO_UDP_OPT }, { "no-tcp", optional_argument, NULL, NO_TCP_OPT }, { "no-tls", optional_argument, NULL, NO_TLS_OPT }, { "no-dtls", optional_argument, NULL, NO_DTLS_OPT }, { "no-udp-relay", optional_argument, NULL, NO_UDP_RELAY_OPT }, { "no-tcp-relay", optional_argument, NULL, NO_TCP_RELAY_OPT }, { "stale-nonce", optional_argument, NULL, STALE_NONCE_OPT }, { "max-allocate-lifetime", optional_argument, NULL, MAX_ALLOCATE_LIFETIME_OPT }, { "channel-lifetime", optional_argument, NULL, CHANNEL_LIFETIME_OPT }, { "permission-lifetime", optional_argument, NULL, PERMISSION_LIFETIME_OPT }, { "stun-only", optional_argument, NULL, 'S' }, { "no-stun", optional_argument, NULL, NO_STUN_OPT }, { "cert", required_argument, NULL, CERT_FILE_OPT }, { "pkey", required_argument, NULL, PKEY_FILE_OPT }, { "pkey-pwd", required_argument, NULL, PKEY_PWD_OPT }, { "log-file", required_argument, NULL, 'l' }, { "no-stdout-log", optional_argument, NULL, NO_STDOUT_LOG_OPT }, { "syslog", optional_argument, NULL, SYSLOG_OPT }, { "simple-log", optional_argument, NULL, SIMPLE_LOG_OPT }, { "aux-server", required_argument, NULL, AUX_SERVER_OPT }, { "udp-self-balance", optional_argument, NULL, UDP_SELF_BALANCE_OPT }, { "alternate-server", required_argument, NULL, ALTERNATE_SERVER_OPT }, { "tls-alternate-server", required_argument, NULL, TLS_ALTERNATE_SERVER_OPT }, { "rest-api-separator", required_argument, NULL, 'C' }, { "max-allocate-timeout", required_argument, NULL, MAX_ALLOCATE_TIMEOUT_OPT }, { "no-multicast-peers", optional_argument, NULL, NO_MULTICAST_PEERS_OPT }, { "allow-loopback-peers", optional_argument, NULL, ALLOW_LOOPBACK_PEERS_OPT }, { "allowed-peer-ip", required_argument, NULL, ALLOWED_PEER_IPS }, { "denied-peer-ip", required_argument, NULL, DENIED_PEER_IPS }, { "cipher-list", required_argument, NULL, CIPHER_LIST_OPT }, { "pidfile", required_argument, NULL, PIDFILE_OPT }, { "secure-stun", optional_argument, NULL, SECURE_STUN_OPT }, { "CA-file", required_argument, NULL, CA_FILE_OPT }, { "dh-file", required_argument, NULL, DH_FILE_OPT }, { "proc-user", required_argument, NULL, PROC_USER_OPT }, { "proc-group", required_argument, NULL, PROC_GROUP_OPT }, { "mobility", optional_argument, NULL, MOBILITY_OPT }, { "no-cli", optional_argument, NULL, NO_CLI_OPT }, { "cli-ip", required_argument, NULL, CLI_IP_OPT }, { "cli-port", required_argument, NULL, CLI_PORT_OPT }, { "cli-password", required_argument, NULL, CLI_PASSWORD_OPT }, { "web-admin", optional_argument, NULL, WEB_ADMIN_OPT }, { "web-admin-ip", required_argument, NULL, WEB_ADMIN_IP_OPT }, { "web-admin-port", required_argument, NULL, WEB_ADMIN_PORT_OPT }, { "web-admin-listen-on-workers", optional_argument, NULL, WEB_ADMIN_LISTEN_ON_WORKERS_OPT }, { "server-relay", optional_argument, NULL, SERVER_RELAY_OPT }, { "cli-max-output-sessions", required_argument, NULL, CLI_MAX_SESSIONS_OPT }, { "ec-curve-name", required_argument, NULL, EC_CURVE_NAME_OPT }, { "dh566", optional_argument, NULL, DH566_OPT }, { "dh2066", optional_argument, NULL, DH2066_OPT }, { "ne", required_argument, NULL, NE_TYPE_OPT }, { "no-sslv2", optional_argument, NULL, NO_SSLV2_OPT }, /* deprecated */ { "no-sslv3", optional_argument, NULL, NO_SSLV3_OPT }, /* deprecated */ { "no-tlsv1", optional_argument, NULL, NO_TLSV1_OPT }, { "no-tlsv1_1", optional_argument, NULL, NO_TLSV1_1_OPT }, { "no-tlsv1_2", optional_argument, NULL, NO_TLSV1_2_OPT }, { "secret-key-file", required_argument, NULL, SECRET_KEY_OPT }, { "keep-address-family", optional_argument, NULL, 'K' }, { NULL, no_argument, NULL, 0 } }; static const struct myoption admin_long_options[] = { {"generate-encrypted-password", no_argument, NULL, 'P' }, {"generate-encrypted-password-aes", no_argument, NULL, 'E'}, { "key", no_argument, NULL, 'k' }, { "add", no_argument, NULL, 'a' }, { "delete", no_argument, NULL, 'd' }, { "list", no_argument, NULL, 'l' }, { "list-admin", no_argument, NULL, 'L' }, { "set-secret", required_argument, NULL, 's' }, { "show-secret", no_argument, NULL, 'S' }, { "delete-secret", required_argument, NULL, 'X' }, { "delete-all-secrets", no_argument, NULL, DEL_ALL_AUTH_SECRETS_OPT }, { "add-admin", no_argument, NULL, 'A' }, { "delete-admin", no_argument, NULL, 'D' }, #if !defined(TURN_NO_SQLITE) { "userdb", required_argument, NULL, 'b' }, { "db", required_argument, NULL, 'b' }, #endif #if !defined(TURN_NO_PQ) { "psql-userdb", required_argument, NULL, 'e' }, { "sql-userdb", required_argument, NULL, 'e' }, #endif #if !defined(TURN_NO_MYSQL) { "mysql-userdb", required_argument, NULL, 'M' }, #endif #if !defined(TURN_NO_MONGO) { "mongo-userdb", required_argument, NULL, 'J' }, #endif #if !defined(TURN_NO_HIREDIS) { "redis-userdb", required_argument, NULL, 'N' }, #endif { "user", required_argument, NULL, 'u' }, { "realm", required_argument, NULL, 'r' }, { "password", required_argument, NULL, 'p' }, { "file-key-path", required_argument, NULL, 'f' }, { "verify", required_argument, NULL, 'v' }, { "key-path", required_argument, NULL, 'x'}, { "add-origin", no_argument, NULL, 'O' }, { "del-origin", no_argument, NULL, 'R' }, { "list-origins", required_argument, NULL, 'I' }, { "origin", required_argument, NULL, 'o' }, { "set-realm-option", no_argument, NULL, 'g' }, { "list-realm-option", no_argument, NULL, 'G' }, { "user-quota", required_argument, NULL, ADMIN_USER_QUOTA_OPT }, { "total-quota", required_argument, NULL, ADMIN_TOTAL_QUOTA_OPT }, { "max-bps", required_argument, NULL, ADMIN_MAX_BPS_OPT }, { "help", no_argument, NULL, 'h' }, { NULL, no_argument, NULL, 0 } }; int init_ctr(struct ctr_state *state, const unsigned char iv[8]){ state->num = 0; memset(state->ecount, 0, 16); memset(state->ivec + 8, 0, 8); memcpy(state->ivec, iv, 8); return 1; } unsigned char *base64encode (const void *b64_encode_this, int encode_this_many_bytes){ BIO *b64_bio, *mem_bio; //Declares two OpenSSL BIOs: a base64 filter and a memory BIO. BUF_MEM *mem_bio_mem_ptr; //Pointer to a "memory BIO" structure holding our base64 data. b64_bio = BIO_new(BIO_f_base64()); //Initialize our base64 filter BIO. mem_bio = BIO_new(BIO_s_mem()); //Initialize our memory sink BIO. BIO_push(b64_bio, mem_bio); //Link the BIOs by creating a filter-sink BIO chain. BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL); //No newlines every 64 characters or less. BIO_write(b64_bio, b64_encode_this, encode_this_many_bytes); //Records base64 encoded data. (void)BIO_flush(b64_bio); //Flush data. Necessary for b64 encoding, because of pad characters. BIO_get_mem_ptr(mem_bio, &mem_bio_mem_ptr); //Store address of mem_bio's memory structure. (void)BIO_set_close(mem_bio, BIO_NOCLOSE); //Permit access to mem_ptr after BIOs are destroyed. BIO_free_all(b64_bio); //Destroys all BIOs in chain, starting with b64 (i.e. the 1st one). BUF_MEM_grow(mem_bio_mem_ptr, (*mem_bio_mem_ptr).length + 1); //Makes space for end null. (*mem_bio_mem_ptr).data[(*mem_bio_mem_ptr).length] = '\0'; //Adds null-terminator to tail. return (unsigned char*)(*mem_bio_mem_ptr).data; //Returns base-64 encoded data. (See: "buf_mem_st" struct). } void encrypt_aes_128(unsigned char* in, const unsigned char* mykey){ int j=0,k=0; int totalSize=0; AES_KEY key; unsigned char iv[8] = {0}; //changed unsigned char out[1024]; //changed AES_set_encrypt_key(mykey, 128, &key); char total[256]; int size=0; struct ctr_state state; init_ctr(&state, iv); #if OPENSSL_VERSION_NUMBER >= 0x10100000L CRYPTO_ctr128_encrypt(in, out, strlen((char*)in), &key, state.ivec, state.ecount, &state.num, (block128_f)AES_encrypt); #else AES_ctr128_encrypt(in, out, strlen((char*)in), &key, state.ivec, state.ecount, &state.num); #endif totalSize += strlen((char*)in); size = strlen((char*)in); for (j = 0; j< size; j++) { total[k++]=out[j]; } unsigned char *base64_encoded = base64encode(total, totalSize); printf("%s\n",base64_encoded); } void generate_aes_128_key(char* filePath, unsigned char* returnedKey){ int i; int part; FILE* fptr; char key[16]; struct timespec times; clock_gettime(CLOCK_REALTIME,×); srand(times.tv_nsec); for (i = 0; i < 16; i++) { part = (rand() % 3); if(part == 0){ key[i] = (rand() % 10) + 48; } else if(part == 1){ key[i] = (rand() % 26) + 65; } else if(part == 2){ key[i] = (rand() % 26) + 97; } } fptr = fopen(filePath, "w"); for(i = 0; i < 16; i++){ fputc(key[i], fptr); } strcpy((char*)returnedKey, key); fclose(fptr); } unsigned char *base64decode (const void *b64_decode_this, int decode_this_many_bytes){ BIO *b64_bio, *mem_bio; //Declares two OpenSSL BIOs: a base64 filter and a memory BIO. unsigned char *base64_decoded = (unsigned char*)calloc( (decode_this_many_bytes*3)/4+1, sizeof(char) ); //+1 = null. b64_bio = BIO_new(BIO_f_base64()); //Initialize our base64 filter BIO. mem_bio = BIO_new(BIO_s_mem()); //Initialize our memory source BIO. BIO_write(mem_bio, b64_decode_this, decode_this_many_bytes); //Base64 data saved in source. BIO_push(b64_bio, mem_bio); //Link the BIOs by creating a filter-source BIO chain. BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL); //Don't require trailing newlines. int decoded_byte_index = 0; //Index where the next base64_decoded byte should be written. while ( 0 < BIO_read(b64_bio, base64_decoded+decoded_byte_index, 1) ){ //Read byte-by-byte. decoded_byte_index++; //Increment the index until read of BIO decoded data is complete. } //Once we're done reading decoded data, BIO_read returns -1 even though there's no error. BIO_free_all(b64_bio); //Destroys all BIOs in chain, starting with b64 (i.e. the 1st one). return base64_decoded; //Returns base-64 decoded data with trailing null terminator. } int decodedTextSize(char *input){ int i=0; int result=0,padding=0; int size=strlen(input); for (i = 0; i < size; ++i) { if(input[i]=='='){ padding++; } } result=(strlen(input)/4*3)-padding; return result; } void decrypt_aes_128(char* in, const unsigned char* mykey){ unsigned char iv[8] = {0}; AES_KEY key; unsigned char outdata[256]; 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); 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); printf("%s\n",last); } static int get_int_value(const char* s, int default_value) { if (!s || !(s[0])) return default_value; return atoi(s); } static int get_bool_value(const char* s) { if(!s || !(s[0])) return 1; if(s[0]=='0' || s[0]=='n' || s[0]=='N' || s[0]=='f' || s[0]=='F') return 0; if(s[0]=='y' || s[0]=='Y' || s[0]=='t' || s[0]=='T') return 1; if(s[0]>'0' && s[0]<='9') return 1; if(!strcmp(s,"off") || !strcmp(s,"OFF") || !strcmp(s,"Off")) return 0; if(!strcmp(s,"on") || !strcmp(s,"ON") || !strcmp(s,"On")) return 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown boolean value: %s. You can use on/off, yes/no, 1/0, true/false.\n",s); exit(-1); } static void set_option(int c, char *value) { if(value && value[0]=='=') { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: option -%c is possibly used incorrectly. The short form of the option must be used as this: -%c , no \'equals\' sign may be used, that sign is used only with long form options (like --user=).\n",(char)c,(char)c); } switch (c) { case 'K': turn_params.keep_address_family = get_bool_value(value); break; case SERVER_NAME_OPT: STRCPY(turn_params.oauth_server_name,value); break; case OAUTH_OPT: if(!ENC_ALG_NUM) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: option --oauth is not supported; ignored.\n"); } else { turn_params.oauth = get_bool_value(value); } break; case NO_SSLV2_OPT: //deprecated break; case NO_SSLV3_OPT: //deprecated break; case NO_TLSV1_OPT: turn_params.no_tlsv1 = get_bool_value(value); break; case NO_TLSV1_1_OPT: turn_params.no_tlsv1_1 = get_bool_value(value); break; case NO_TLSV1_2_OPT: turn_params.no_tlsv1_2 = get_bool_value(value); break; case NE_TYPE_OPT: { int ne = atoi(value); if((ne<(int)NEV_MIN)||(ne>(int)NEV_MAX)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "ERROR: wrong version of the network engine: %d\n",ne); } turn_params.net_engine_version = (NET_ENG_VERSION)ne; } break; case DH566_OPT: if(get_bool_value(value)) turn_params.dh_key_size = DH_566; break; case DH2066_OPT: if(get_bool_value(value)) turn_params.dh_key_size = DH_2066; break; case EC_CURVE_NAME_OPT: STRCPY(turn_params.ec_curve_name,value); break; case CLI_MAX_SESSIONS_OPT: cli_max_output_sessions = atoi(value); break; case SERVER_RELAY_OPT: turn_params.server_relay = get_bool_value(value); break; case MOBILITY_OPT: turn_params.mobility = get_bool_value(value); break; case NO_CLI_OPT: use_cli = !get_bool_value(value); break; case CLI_IP_OPT: if(make_ioa_addr((const u08bits*)value,0,&cli_addr)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot set cli address: %s\n",value); } else{ cli_addr_set = 1; } break; case CLI_PORT_OPT: cli_port = atoi(value); break; case CLI_PASSWORD_OPT: STRCPY(cli_password,value); break; case WEB_ADMIN_OPT: use_web_admin = get_bool_value(value); break; case WEB_ADMIN_IP_OPT: if(make_ioa_addr((const u08bits*)value, 0, &web_admin_addr) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot set web-admin address: %s\n", value); } else { web_admin_addr_set = 1; } break; case WEB_ADMIN_PORT_OPT: web_admin_port = atoi(value); break; case WEB_ADMIN_LISTEN_ON_WORKERS_OPT: turn_params.web_admin_listen_on_workers = get_bool_value(value); break; case PROC_USER_OPT: { struct passwd* pwd = getpwnam(value); if(!pwd) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown user name: %s\n",value); exit(-1); } else { procuserid = pwd->pw_uid; procuserid_set = 1; STRCPY(procusername,value); } } break; case PROC_GROUP_OPT: { struct group* gr = getgrnam(value); if(!gr) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown group name: %s\n",value); exit(-1); } else { procgroupid = gr->gr_gid; procgroupid_set = 1; STRCPY(procgroupname,value); } } break; case 'i': STRCPY(turn_params.relay_ifname, value); break; case 'm': #if defined(OPENSSL_THREADS) if(atoi(value)>MAX_NUMBER_OF_GENERAL_RELAY_SERVERS) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: max number of relay threads is 128.\n"); turn_params.general_relay_servers_number = MAX_NUMBER_OF_GENERAL_RELAY_SERVERS; } else if(atoi(value)<=0) { turn_params.general_relay_servers_number = 0; } else { turn_params.general_relay_servers_number = atoi(value); } #else TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: OpenSSL version is too old OR does not support threading,\n I am using single thread for relaying.\n"); #endif break; case 'd': STRCPY(turn_params.listener_ifname, value); break; case 'p': turn_params.listener_port = atoi(value); break; case TLS_PORT_OPT: turn_params.tls_listener_port = atoi(value); break; case ALT_PORT_OPT: turn_params.alt_listener_port = atoi(value); break; case ALT_TLS_PORT_OPT: turn_params.alt_tls_listener_port = atoi(value); break; case MIN_PORT_OPT: turn_params.min_port = atoi(value); break; case MAX_PORT_OPT: turn_params.max_port = atoi(value); break; case SECURE_STUN_OPT: turn_params.secure_stun = get_bool_value(value); break; case NO_MULTICAST_PEERS_OPT: turn_params.no_multicast_peers = get_bool_value(value); break; case ALLOW_LOOPBACK_PEERS_OPT: turn_params.allow_loopback_peers = get_bool_value(value); break; case STALE_NONCE_OPT: turn_params.stale_nonce = get_int_value(value, STUN_DEFAULT_NONCE_EXPIRATION_TIME); break; case MAX_ALLOCATE_LIFETIME_OPT: turn_params.max_allocate_lifetime = get_int_value(value, STUN_DEFAULT_MAX_ALLOCATE_LIFETIME); break; case CHANNEL_LIFETIME_OPT: turn_params.channel_lifetime = get_int_value(value, STUN_DEFAULT_CHANNEL_LIFETIME); break; case PERMISSION_LIFETIME_OPT: turn_params.permission_lifetime = get_int_value(value, STUN_DEFAULT_PERMISSION_LIFETIME); break; case MAX_ALLOCATE_TIMEOUT_OPT: TURN_MAX_ALLOCATE_TIMEOUT = atoi(value); TURN_MAX_ALLOCATE_TIMEOUT_STUN_ONLY = atoi(value); break; case 'S': turn_params.stun_only = get_bool_value(value); break; case NO_STUN_OPT: turn_params.no_stun = get_bool_value(value); break; case 'L': add_listener_addr(value); break; case 'E': add_relay_addr(value); break; case 'X': if(value) { char *div = strchr(value,'/'); if(div) { char *nval=turn_strdup(value); div = strchr(nval,'/'); div[0]=0; ++div; ioa_addr apub,apriv; if(make_ioa_addr((const u08bits*)nval,0,&apub)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"-X : Wrong address format: %s\n",nval); } else { if(make_ioa_addr((const u08bits*)div,0,&apriv)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"-X : Wrong address format: %s\n",div); } else { ioa_addr_add_mapping(&apub,&apriv); } } turn_free(nval,strlen(nval)+1); } else { if(turn_params.external_ip) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "You cannot define external IP more than once in the configuration\n"); } else { turn_params.external_ip = (ioa_addr*)allocate_super_memory_engine(turn_params.listener.ioa_eng, sizeof(ioa_addr)); if(make_ioa_addr((const u08bits*)value,0,turn_params.external_ip)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"-X : Wrong address format: %s\n",value); turn_free(turn_params.external_ip,sizeof(ioa_addr)); turn_params.external_ip = NULL; } } } } break; case 'v': if(turn_params.verbose != TURN_VERBOSE_EXTRA){ if(get_bool_value(value)) { turn_params.verbose = TURN_VERBOSE_NORMAL; } else { turn_params.verbose = TURN_VERBOSE_NONE; } } break; case 'V': if(get_bool_value(value)) { turn_params.verbose = TURN_VERBOSE_EXTRA; } break; case 'o': turn_params.turn_daemon = get_bool_value(value); break; case 'a': if (get_bool_value(value)) { turn_params.ct = TURN_CREDENTIALS_LONG_TERM; use_lt_credentials=1; use_ltc=1; } else { turn_params.ct = TURN_CREDENTIALS_UNDEFINED; use_lt_credentials=0; } break; case 'z': if (!get_bool_value(value)) { turn_params.ct = TURN_CREDENTIALS_UNDEFINED; anon_credentials = 0; } else { turn_params.ct = TURN_CREDENTIALS_NONE; anon_credentials = 1; } break; case PROD_OPT: turn_params.prod = get_bool_value(value); break; case 'f': turn_params.fingerprint = get_bool_value(value); break; case 'u': add_static_user_account(value); break; case 'b': #if defined(TURN_NO_SQLITE) TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: Options -b, --userdb and --db are not supported because SQLite is not supported in this build.\n"); #else STRCPY(turn_params.default_users_db.persistent_users_db.userdb, value); turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_SQLITE; #endif break; #if !defined(TURN_NO_PQ) case 'e': STRCPY(turn_params.default_users_db.persistent_users_db.userdb, value); turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_PQ; break; #endif #if !defined(TURN_NO_MYSQL) case 'M': STRCPY(turn_params.default_users_db.persistent_users_db.userdb, value); turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MYSQL; break; #endif #if !defined(TURN_NO_MONGO) case 'J': STRCPY(turn_params.default_users_db.persistent_users_db.userdb, value); turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MONGO; break; #endif #if !defined(TURN_NO_HIREDIS) case 'N': STRCPY(turn_params.default_users_db.persistent_users_db.userdb, value); turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_REDIS; break; case 'O': STRCPY(turn_params.redis_statsdb, value); turn_params.use_redis_statsdb = 1; break; #endif case AUTH_SECRET_OPT: turn_params.use_auth_secret_with_timestamp = 1; use_tltc = 1; turn_params.ct = TURN_CREDENTIALS_LONG_TERM; use_lt_credentials = 1; break; case STATIC_AUTH_SECRET_VAL_OPT: add_to_secrets_list(&turn_params.default_users_db.ram_db.static_auth_secrets,value); turn_params.use_auth_secret_with_timestamp = 1; use_tltc = 1; turn_params.ct = TURN_CREDENTIALS_LONG_TERM; use_lt_credentials = 1; break; case AUTH_SECRET_TS_EXP: TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: Option --secret-ts-exp-time deprecated and has no effect.\n"); break; case 'r': set_default_realm_name(value); break; case 'q': turn_params.user_quota = (vint)atoi(value); get_realm(NULL)->options.perf_options.user_quota = atoi(value); break; case 'Q': turn_params.total_quota = (vint)atoi(value); get_realm(NULL)->options.perf_options.total_quota = atoi(value); break; case 's': turn_params.max_bps = (band_limit_t)strtoul(value,NULL,10); get_realm(NULL)->options.perf_options.max_bps = (band_limit_t)strtoul(value,NULL,10); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%lu bytes per second allowed per session\n",(unsigned long)turn_params.max_bps); break; case 'B': turn_params.bps_capacity = (band_limit_t)strtoul(value,NULL,10); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%lu bytes per second allowed, combined server capacity\n",(unsigned long)turn_params.bps_capacity); break; case CHECK_ORIGIN_CONSISTENCY_OPT: turn_params.check_origin = get_bool_value(value); break; case NO_UDP_OPT: turn_params.no_udp = get_bool_value(value); break; case NO_TCP_OPT: turn_params.no_tcp = get_bool_value(value); break; case NO_UDP_RELAY_OPT: turn_params.no_udp_relay = get_bool_value(value); break; case NO_TCP_RELAY_OPT: turn_params.no_tcp_relay = get_bool_value(value); break; case NO_TLS_OPT: #if !TLS_SUPPORTED turn_params.no_tls = 1; #else turn_params.no_tls = get_bool_value(value); #endif break; case NO_DTLS_OPT: #if DTLS_SUPPORTED turn_params.no_dtls = get_bool_value(value); #else turn_params.no_dtls = 1; #endif break; case CERT_FILE_OPT: STRCPY(turn_params.cert_file,value); break; case CA_FILE_OPT: STRCPY(turn_params.ca_cert_file,value); break; case DH_FILE_OPT: STRCPY(turn_params.dh_file,value); break; case SECRET_KEY_OPT: STRCPY(turn_params.secret_key_file,value); break; case PKEY_FILE_OPT: STRCPY(turn_params.pkey_file,value); break; case PKEY_PWD_OPT: STRCPY(turn_params.tls_password,value); break; case ALTERNATE_SERVER_OPT: add_alternate_server(value); break; case AUX_SERVER_OPT: add_aux_server(value); break; case UDP_SELF_BALANCE_OPT: turn_params.udp_self_balance = get_bool_value(value); break; case TLS_ALTERNATE_SERVER_OPT: add_tls_alternate_server(value); break; case ALLOWED_PEER_IPS: if (add_ip_list_range(value, NULL, &turn_params.ip_whitelist) == 0) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "White listing: %s\n", value); break; case DENIED_PEER_IPS: if (add_ip_list_range(value, NULL, &turn_params.ip_blacklist) == 0) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Black listing: %s\n", value); break; case CIPHER_LIST_OPT: STRCPY(turn_params.cipher_list,value); break; case PIDFILE_OPT: STRCPY(turn_params.pidfile,value); break; case 'C': if(value && *value) { turn_params.rest_api_separator=*value; } break; /* these options have been already taken care of before: */ case 'l': case NO_STDOUT_LOG_OPT: case SYSLOG_OPT: case SIMPLE_LOG_OPT: case 'c': case 'n': case 'h': break; default: fprintf(stderr,"\n%s\n", Usage); exit(-1); } } static int parse_arg_string(char *sarg, int *c, char **value) { int i = 0; char *name = sarg; while(*sarg) { if((*sarg==' ') || (*sarg=='=') || (*sarg=='\t')) { *sarg=0; do { ++sarg; } while((*sarg==' ') || (*sarg=='=') || (*sarg=='\t')); *value = sarg; break; } ++sarg; *value=sarg; } if(value && *value && **value=='\"') { *value += 1; size_t len = strlen(*value); while(len>0 && ( ((*value)[len-1]=='\n') || ((*value)[len-1]=='\r') || ((*value)[len-1]==' ') || ((*value)[len-1]=='\t') ) ) { (*value)[--len]=0; } if(len>0 && (*value)[len-1]=='\"') { (*value)[--len]=0; } } while(long_options[i].name) { if(strcmp(long_options[i].name,name)) { ++i; continue; } *c=long_options[i].val; return 0; } return -1; } static void read_config_file(int argc, char **argv, int pass) { static char config_file[1025] = DEFAULT_CONFIG_FILE; if(pass == 0) { if (argv) { int i = 0; for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "-c")) { if (i < argc - 1) { STRCPY(config_file, argv[i + 1]); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Wrong usage of -c option\n"); } } else if (!strcmp(argv[i], "-n")) { turn_params.do_not_use_config_file = 1; config_file[0]=0; return; } else if (!strcmp(argv[i], "-h")) { printf("\n%s\n",Usage); exit(0); } } } } if (!turn_params.do_not_use_config_file && config_file[0]) { FILE *f = NULL; char *full_path_to_config_file = NULL; full_path_to_config_file = find_config_file(config_file, pass); if (full_path_to_config_file) f = fopen(full_path_to_config_file, "r"); if (f) { char sbuf[1025]; char sarg[1035]; for (;;) { char *s = fgets(sbuf, sizeof(sbuf) - 1, f); if (!s) break; s = skip_blanks(s); if (s[0] == '#') continue; if (!s[0]) continue; size_t slen = strlen(s); while (slen && ((s[slen - 1] == 10) || (s[slen - 1] == 13))) s[--slen] = 0; if (slen) { int c = 0; char *value = NULL; STRCPY(sarg, s); if (parse_arg_string(sarg, &c, &value) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Bad configuration format: %s\n", sarg); } else if((pass == 0) && (c == 'l')) { set_logfile(value); } else if((pass==0) && (c==NO_STDOUT_LOG_OPT)) { set_no_stdout_log(get_bool_value(value)); } else if((pass==0) && (c==SYSLOG_OPT)) { set_log_to_syslog(get_bool_value(value)); } else if((pass==0) && (c==SIMPLE_LOG_OPT)) { set_simple_log(get_bool_value(value)); } else if((pass == 0) && (c != 'u')) { set_option(c, value); } else if((pass > 0) && (c == 'u')) { set_option(c, value); } if (s[slen - 1] == 59) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Check config! The following line ends with semicolon: \"%s\" \n",s); } } } fclose(f); } else TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: Cannot find config file: %s. Default and command-line settings will be used.\n", config_file); if (full_path_to_config_file) { turn_free(full_path_to_config_file, strlen(full_path_to_config_file)+1); full_path_to_config_file = NULL; } } } static int adminmain(int argc, char **argv) { int c = 0; TURNADMIN_COMMAND_TYPE ct = TA_COMMAND_UNKNOWN; int is_admin = 0; FILE* fptr; unsigned char generated_key[16]; //changed u08bits user[STUN_MAX_USERNAME_SIZE+1]="\0"; u08bits realm[STUN_MAX_REALM_SIZE+1]="\0"; u08bits pwd[STUN_MAX_PWD_SIZE+1]="\0"; u08bits secret[AUTH_SECRET_SIZE+1]="\0"; u08bits origin[STUN_MAX_ORIGIN_SIZE+1]="\0"; perf_options_t po = {(band_limit_t)-1,-1,-1}; struct uoptions uo; uo.u.m = admin_long_options; int print_enc_password = 0; int print_enc_aes_password = 0; while (((c = getopt_long(argc, argv, ADMIN_OPTIONS, uo.u.o, NULL)) != -1)) { switch (c){ case 'P': if(pwd[0]) { char result[257]; generate_new_enc_password((char*)pwd, result); printf("%s\n",result); exit(0); } print_enc_password = 1; break; case 'E': print_enc_aes_password = 1; break; case 'g': ct = TA_SET_REALM_OPTION; break; case 'G': ct = TA_LIST_REALM_OPTIONS; break; case ADMIN_USER_QUOTA_OPT: po.user_quota = (vint)atoi(optarg); break; case ADMIN_TOTAL_QUOTA_OPT: po.total_quota = (vint)atoi(optarg); break; case ADMIN_MAX_BPS_OPT: po.max_bps = (vint)atoi(optarg); break; case 'O': ct = TA_ADD_ORIGIN; break; case 'R': ct = TA_DEL_ORIGIN; break; case 'I': ct = TA_LIST_ORIGINS; break; case 'o': STRCPY(origin,optarg); break; case 'k': ct = TA_PRINT_KEY; break; case 'a': ct = TA_UPDATE_USER; break; case 'd': ct = TA_DELETE_USER; break; case 'A': ct = TA_UPDATE_USER; is_admin = 1; break; case 'D': ct = TA_DELETE_USER; is_admin = 1; break; case 'l': ct = TA_LIST_USERS; break; case 'L': ct = TA_LIST_USERS; is_admin = 1; break; case 's': ct = TA_SET_SECRET; STRCPY(secret,optarg); break; case 'S': ct = TA_SHOW_SECRET; break; case 'X': ct = TA_DEL_SECRET; if(optarg) STRCPY(secret,optarg); break; case DEL_ALL_AUTH_SECRETS_OPT: ct = TA_DEL_SECRET; break; #if !defined(TURN_NO_SQLITE) case 'b': STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg); turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_SQLITE; break; #endif #if !defined(TURN_NO_PQ) case 'e': STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg); turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_PQ; break; #endif #if !defined(TURN_NO_MYSQL) case 'M': STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg); turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MYSQL; break; #endif #if !defined(TURN_NO_MONGO) case 'J': STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg); turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MONGO; break; #endif #if !defined(TURN_NO_HIREDIS) case 'N': STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg); turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_REDIS; break; #endif case 'u': STRCPY(user,optarg); if(!is_secure_string((u08bits*)user,1)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user name structure or symbols, choose another name: %s\n",user); exit(-1); } if(SASLprep((u08bits*)user)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user name: %s\n",user); exit(-1); } break; case 'r': set_default_realm_name(optarg); STRCPY(realm,optarg); if(SASLprep((u08bits*)realm)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong realm: %s\n",realm); exit(-1); } break; case 'p': STRCPY(pwd,optarg); if(SASLprep((u08bits*)pwd)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong password: %s\n",pwd); exit(-1); } if(print_enc_password) { char result[257]; generate_new_enc_password((char*)pwd, result); printf("%s\n",result); exit(0); } if(print_enc_aes_password){ encrypt_aes_128(pwd, generated_key); exit(0); } break; case 'x': generate_aes_128_key(optarg, generated_key); exit(0); break; case 'f': fptr = fopen((char*)optarg, "r"); if(fptr == NULL){ printf("No such file like %s\n", (char*)optarg); } else{ fseek (fptr, 0, SEEK_SET); if( fread(generated_key, sizeof(char), 16, fptr) !=0 ){ TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: Secret-Key file is empty\n",__FUNCTION__); } fclose (fptr); } break; case 'v': decrypt_aes_128((char*)optarg, generated_key); exit(0); case 'h': printf("\n%s\n", AdminUsage); exit(0); break; default: fprintf(stderr,"\n%s\n", AdminUsage); exit(-1); } } #if !defined(TURN_NO_SQLITE) if(!strlen(turn_params.default_users_db.persistent_users_db.userdb) && (turn_params.default_users_db.userdb_type == TURN_USERDB_TYPE_SQLITE)) STRCPY(turn_params.default_users_db.persistent_users_db.userdb,DEFAULT_USERDB_FILE); #endif if(ct == TA_COMMAND_UNKNOWN) { fprintf(stderr,"\n%s\n", AdminUsage); exit(-1); } argc -= optind; argv += optind; if(argc != 0) { fprintf(stderr,"\n%s\n", AdminUsage); exit(-1); } return adminuser(user, realm, pwd, secret, origin, ct, &po, is_admin); } static void print_features(unsigned long mfn) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\nRFC 3489/5389/5766/5780/6062/6156 STUN/TURN Server\nVersion %s\n",TURN_SOFTWARE); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\nMax number of open files/sockets allowed for this process: %lu\n",mfn); if(turn_params.net_engine_version == NEV_UDP_SOCKET_PER_ENDPOINT) mfn = mfn/3; else mfn = mfn/2; mfn = ((unsigned long)(mfn/500))*500; if(mfn<500) mfn = 500; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\nDue to the open files/sockets limitation,\nmax supported number of TURN Sessions possible is: %lu (approximately)\n",mfn); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n\n==== Show him the instruments, Practical Frost: ====\n\n"); /* Frost stepped forward and opened the polished case with a theatrical flourish. It was a masterful piece of craftsmanship. As the lid was pulled back, the many trays inside lifted and fanned out, displaying Glokta’s tools in all their gruesome glory. There were blades of every size and shape, needles curved and straight, bottles of oil and acid, nails and screws, clamps and pliers, saws, hammers, chisels. Metal, wood and glass glittered in the bright lamplight, all polished to mirror brightness and honed to a murderous sharpness. */ #if !TLS_SUPPORTED TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TLS is not supported\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TLS supported\n"); #endif #if !DTLS_SUPPORTED TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS is not supported\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS supported\n"); #if DTLSv1_2_SUPPORTED TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS 1.2 supported\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS 1.2 is not supported\n"); #endif #endif #if ALPN_SUPPORTED TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TURN/STUN ALPN supported\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TURN/STUN ALPN is not supported\n"); #endif if(!ENC_ALG_NUM) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Third-party authorization (oAuth) is not supported\n"); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Third-party authorization (oAuth) supported\n"); #if defined(TURN_NO_GCM) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "GCM (AEAD) is not supported\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "GCM (AEAD) supported\n"); #endif } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "OpenSSL compile-time version: %s (0x%lx)\n",OPENSSL_VERSION_TEXT,OPENSSL_VERSION_NUMBER); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n"); #if !defined(TURN_NO_SQLITE) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SQLite supported, default database location is %s\n",DEFAULT_USERDB_FILE); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SQLite is not supported\n"); #endif #if !defined(TURN_NO_HIREDIS) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Redis supported\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Redis is not supported\n"); #endif #if !defined(TURN_NO_PQ) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "PostgreSQL supported\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "PostgreSQL is not supported\n"); #endif #if !defined(TURN_NO_MYSQL) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MySQL supported\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MySQL is not supported\n"); #endif #if !defined(TURN_NO_MONGO) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MongoDB supported\n"); #else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MongoDB is not supported\n"); #endif TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Default Net Engine version: %d (%s)\n\n=====================================================\n\n", (int)turn_params.net_engine_version, turn_params.net_engine_version_txt[(int)turn_params.net_engine_version]); } #if defined(__linux__) || defined(__LINUX__) || defined(__linux) || defined(linux__) || defined(LINUX) || defined(__LINUX) || defined(LINUX__) #include #endif static void set_network_engine(void) { if(turn_params.net_engine_version != NEV_UNKNOWN) return; turn_params.net_engine_version = NEV_UDP_SOCKET_PER_ENDPOINT; #if defined(SO_REUSEPORT) #if defined(__linux__) || defined(__LINUX__) || defined(__linux) || defined(linux__) || defined(LINUX) || defined(__LINUX) || defined(LINUX__) turn_params.net_engine_version = NEV_UDP_SOCKET_PER_THREAD; #else /* BSD ? */ turn_params.net_engine_version = NEV_UDP_SOCKET_PER_SESSION; #endif /* Linux */ #else /* defined(SO_REUSEPORT) */ #if defined(__linux__) || defined(__LINUX__) || defined(__linux) || defined(linux__) || defined(LINUX) || defined(__LINUX) || defined(LINUX__) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33) //net_engine_version = NEV_UDP_SOCKET_PER_SESSION; turn_params.net_engine_version = NEV_UDP_SOCKET_PER_ENDPOINT; #else turn_params.net_engine_version = NEV_UDP_SOCKET_PER_ENDPOINT; #endif /* Linux version */ #endif /* Linux */ #endif /* defined(SO_REUSEPORT) */ } static void drop_privileges(void) { if(procgroupid_set) { if(getgid() != procgroupid) { if (setgid(procgroupid) != 0) { perror("setgid: Unable to change group privileges"); exit(-1); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "New GID: %s(%lu)\n", procgroupname, (unsigned long)procgroupid); } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Keep GID: %s(%lu)\n", procgroupname, (unsigned long)procgroupid); } } if(procuserid_set) { if(procuserid != getuid()) { if (setuid(procuserid) != 0) { perror("setuid: Unable to change user privileges"); exit(-1); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "New UID: %s(%lu)\n", procusername, (unsigned long)procuserid); } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Keep UID: %s(%lu)\n", procusername, (unsigned long)procuserid); } } } static void init_domain(void) { #if !defined(TURN_NO_GETDOMAINNAME) if(getdomainname(turn_params.domain,sizeof(turn_params.domain)-1)<0) { turn_params.domain[0]=0; } else if(!strcmp(turn_params.domain,"(none)")) { turn_params.domain[0]=0; } #endif } int main(int argc, char **argv) { int c = 0; IS_TURN_SERVER = 1; set_execdir(); init_super_memory(); #if !defined(TURN_NO_HIREDIS) redis_async_init(); #endif init_domain(); create_default_realm(); init_turn_server_addrs_list(&turn_params.alternate_servers_list); init_turn_server_addrs_list(&turn_params.tls_alternate_servers_list); init_turn_server_addrs_list(&turn_params.aux_servers_list); set_network_engine(); init_listener(); init_secrets_list(&turn_params.default_users_db.ram_db.static_auth_secrets); init_dynamic_ip_lists(); if (!strstr(argv[0], "turnadmin")) { struct uoptions uo; uo.u.m = long_options; while (((c = getopt_long(argc, argv, OPTIONS, uo.u.o, NULL)) != -1)) { switch (c){ case 'l': set_logfile(optarg); break; case NO_STDOUT_LOG_OPT: set_no_stdout_log(get_bool_value(optarg)); break; case SYSLOG_OPT: set_log_to_syslog(get_bool_value(optarg)); break; case SIMPLE_LOG_OPT: set_simple_log(get_bool_value(optarg)); break; default: ; } } } optind = 0; #if !TLS_SUPPORTED turn_params.no_tls = 1; #endif #if !DTLS_SUPPORTED turn_params.no_dtls = 1; #endif #if defined(_SC_NPROCESSORS_ONLN) { turn_params.cpus = (long)sysconf(_SC_NPROCESSORS_CONF); if(turn_params.cpusMAX_NUMBER_OF_GENERAL_RELAY_SERVERS) turn_params.cpus = MAX_NUMBER_OF_GENERAL_RELAY_SERVERS; turn_params.general_relay_servers_number = (turnserver_id)turn_params.cpus; } #endif ns_bzero(&turn_params.default_users_db,sizeof(default_users_db_t)); turn_params.default_users_db.ram_db.static_accounts = ur_string_map_create(turn_free_simple); if(strstr(argv[0],"turnadmin")) return adminmain(argc,argv); read_config_file(argc,argv,0); struct uoptions uo; uo.u.m = long_options; while (((c = getopt_long(argc, argv, OPTIONS, uo.u.o, NULL)) != -1)) { if(c != 'u') set_option(c,optarg); } read_config_file(argc,argv,1); { unsigned long mfn = set_system_parameters(1); print_features(mfn); } if(!get_realm(NULL)->options.name[0]) { STRCPY(get_realm(NULL)->options.name,turn_params.domain); } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Domain name: %s\n",turn_params.domain); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Default realm: %s\n",get_realm(NULL)->options.name); if(turn_params.oauth && turn_params.oauth_server_name[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "oAuth server name: %s\n",turn_params.oauth_server_name); } optind = 0; while (((c = getopt_long(argc, argv, OPTIONS, uo.u.o, NULL)) != -1)) { if(c == 'u') { set_option(c,optarg); } } if(turn_params.bps_capacity && !(turn_params.max_bps)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIG ERROR: If you set the --bps-capacity option, then you must set --max-bps options, too.\n"); exit(-1); } if(turn_params.no_udp_relay && turn_params.no_tcp_relay) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIG ERROR: --no-udp-relay and --no-tcp-relay options cannot be used together.\n"); exit(-1); } if(turn_params.no_udp_relay) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\nCONFIG: --no-udp-relay: UDP relay endpoints are not allowed.\n"); } if(turn_params.no_tcp_relay) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\nCONFIG: --no-tcp-relay: TCP relay endpoints are not allowed.\n"); } if(turn_params.server_relay) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "\nCONFIG: WARNING: --server-relay: NON-STANDARD AND DANGEROUS OPTION.\n"); } #if !defined(TURN_NO_SQLITE) if(!strlen(turn_params.default_users_db.persistent_users_db.userdb) && (turn_params.default_users_db.userdb_type == TURN_USERDB_TYPE_SQLITE)) STRCPY(turn_params.default_users_db.persistent_users_db.userdb,DEFAULT_USERDB_FILE); #endif argc -= optind; argv += optind; if(argc>0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIGURATION ALERT: Unknown argument: %s\n",argv[argc-1]); } if(use_lt_credentials && anon_credentials) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIG ERROR: -a and -z options cannot be used together.\n"); exit(-1); } if(use_ltc && use_tltc) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "\nCONFIGURATION ALERT: You specified --lt-cred-mech and --use-auth-secret in the same time.\n" "Be aware that you could not mix the username/password and the shared secret based auth methohds. \n" "Shared secret overrides username/password based auth method. Check your configuration!\n"); } if(turn_params.allow_loopback_peers) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "CONFIG WARNING: allow_loopback_peers opens a possible security vulnerability. Do not use in production!!\n"); if(cli_password[0]==0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIG ERROR: allow_loopback_peers and empty cli password cannot be used together.\n"); exit(-1); } } if(cli_password[0]==0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIG ERROR: Empty cli-password, and so telnet cli interface is disabled! Please set a non empty cli-password!\n"); use_cli = 0; } if(!use_lt_credentials && !anon_credentials) { if(turn_params.default_users_db.ram_db.users_number) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "\nCONFIGURATION ALERT: you specified long-term user accounts, (-u option) \n but you did not specify the long-term credentials option\n (-a or --lt-cred-mech option).\n I am turning --lt-cred-mech ON for you, but double-check your configuration.\n"); turn_params.ct = TURN_CREDENTIALS_LONG_TERM; use_lt_credentials=1; } else { turn_params.ct = TURN_CREDENTIALS_NONE; use_lt_credentials=0; } } if(use_lt_credentials) { if(!get_realm(NULL)->options.name[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "\nCONFIGURATION ALERT: you did specify the long-term credentials usage\n but you did not specify the default realm option (-r option).\n Check your configuration.\n"); } } if(anon_credentials) { if(turn_params.default_users_db.ram_db.users_number) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "\nCONFIGURATION ALERT: you specified user accounts, (-u option) \n but you also specified the anonymous user access option (-z or --no-auth option).\n User accounts will be ignored.\n"); turn_params.ct = TURN_CREDENTIALS_NONE; use_lt_credentials=0; } } openssl_setup(); int local_listeners = 0; if (!turn_params.listener.addrs_number) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "NO EXPLICIT LISTENER ADDRESS(ES) ARE CONFIGURED\n"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "===========Discovering listener addresses: =========\n"); int maddrs = make_local_listeners_list(); if((maddrs<1) || !turn_params.listener.addrs_number) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Cannot configure any meaningful IP listener address\n", __FUNCTION__); fprintf(stderr,"\n%s\n", Usage); exit(-1); } local_listeners = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "=====================================================\n"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Total: %d 'real' addresses discovered\n",maddrs); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "=====================================================\n"); } if (!turn_params.relays_number) { if(!local_listeners && turn_params.listener.addrs_number && turn_params.listener.addrs) { size_t la = 0; for(la=0;lass.sa_family) { ioa_addr_add_mapping(turn_params.external_ip,&ra); } } } } } if(turn_params.turn_daemon) { #if !defined(TURN_HAS_DAEMON) pid_t pid = fork(); if(pid>0) exit(0); if(pid<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "ERROR: Cannot start daemon process\n"); exit(-1); } #else if(daemon(1,0)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "ERROR: Cannot start daemon process\n"); exit(-1); } reset_rtpprintf(); #endif } if(turn_params.pidfile[0]) { char s[2049]; FILE *f = fopen(turn_params.pidfile,"w"); if(f) { STRCPY(s,turn_params.pidfile); } else { snprintf(s,sizeof(s),"Cannot create pid file: %s",turn_params.pidfile); perror(s); TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "%s\n", s); { const char *pfs[] = {"/var/run/turnserver.pid", "/var/spool/turnserver.pid", "/var/turnserver.pid", "/var/tmp/turnserver.pid", "/tmp/turnserver.pid", "turnserver.pid", NULL}; const char **ppfs = pfs; while(*ppfs) { f = fopen(*ppfs,"w"); if(f) { STRCPY(s,*ppfs); break; } else { ++ppfs; } } } } if(f) { fprintf(f,"%lu\n",(unsigned long)getpid()); fclose(f); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "pid file created: %s\n", s); } } setup_server(); struct event *ev = evsignal_new(turn_params.listener.event_base, SIGUSR2, reload_ssl_certs, NULL); event_add(ev, NULL); drop_privileges(); run_listener_server(&(turn_params.listener)); return 0; } ////////// OpenSSL locking //////////////////////////////////////// #if defined(OPENSSL_THREADS) static char some_buffer[65536]; //array larger than anything that OpenSSL may need: static pthread_mutex_t mutex_buf[256]; static int mutex_buf_initialized = 0; void coturn_locking_function(int mode, int n, const char *file, int line); void coturn_locking_function(int mode, int n, const char *file, int line) { UNUSED_ARG(file); UNUSED_ARG(line); if(mutex_buf_initialized && (n < CRYPTO_num_locks())) { if (mode & CRYPTO_LOCK) pthread_mutex_lock(&(mutex_buf[n])); else pthread_mutex_unlock(&(mutex_buf[n])); } } #if OPENSSL_VERSION_NUMBER >= 0x10000000L void coturn_id_function(CRYPTO_THREADID *ctid); void coturn_id_function(CRYPTO_THREADID *ctid) { UNUSED_ARG(ctid); CRYPTO_THREADID_set_numeric(ctid, (unsigned long)pthread_self()); } #else unsigned long coturn_id_function(void); unsigned long coturn_id_function(void) { return (unsigned long)pthread_self(); } #endif #endif static int THREAD_setup(void) { #if defined(OPENSSL_THREADS) int i; some_buffer[0] = 0; for (i = 0; i < CRYPTO_num_locks(); i++) { pthread_mutex_init(&(mutex_buf[i]), NULL); } mutex_buf_initialized = 1; #if OPENSSL_VERSION_NUMBER >= 0x10000000L CRYPTO_THREADID_set_callback(coturn_id_function); #else CRYPTO_set_id_callback(coturn_id_function); #endif CRYPTO_set_locking_callback(coturn_locking_function); #endif return 1; } int THREAD_cleanup(void); int THREAD_cleanup(void) { #if defined(OPENSSL_THREADS) int i; if (!mutex_buf_initialized) return 0; #if OPENSSL_VERSION_NUMBER >= 0x10000000L CRYPTO_THREADID_set_callback(NULL); #else CRYPTO_set_id_callback(NULL); #endif CRYPTO_set_locking_callback(NULL); for (i = 0; i < CRYPTO_num_locks(); i++) { pthread_mutex_destroy(&(mutex_buf[i])); } mutex_buf_initialized = 0; #endif return 1; } static void adjust_key_file_name(char *fn, const char* file_title, int critical) { char *full_path_to_file = NULL; if(!fn[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"\nERROR: you must set the %s file parameter\n",file_title); goto keyerr; } else { full_path_to_file = find_config_file(fn, 1); { FILE *f = full_path_to_file ? fopen(full_path_to_file,"r") : NULL; if(!f) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING,"WARNING: cannot find %s file: %s (1)\n",file_title,fn); goto keyerr; } else { fclose(f); } } if(!full_path_to_file) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING,"WARNING: cannot find %s file: %s (2)\n",file_title,fn); goto keyerr; } strncpy(fn,full_path_to_file,sizeof(turn_params.cert_file)-1); fn[sizeof(turn_params.cert_file)-1]=0; if(full_path_to_file) turn_free(full_path_to_file,strlen(full_path_to_file)+1); return; } keyerr: { if(critical) { turn_params.no_tls = 1; turn_params.no_dtls = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING,"WARNING: cannot start TLS and DTLS listeners because %s file is not set properly\n",file_title); } if(full_path_to_file) turn_free(full_path_to_file,strlen(full_path_to_file)+1); return; } } static void adjust_key_file_names(void) { if(turn_params.ca_cert_file[0]) adjust_key_file_name(turn_params.ca_cert_file,"CA",1); adjust_key_file_name(turn_params.cert_file,"certificate",1); adjust_key_file_name(turn_params.pkey_file,"private key",1); if(turn_params.dh_file[0]) adjust_key_file_name(turn_params.dh_file,"DH key",0); } static DH *get_dh566(void) { unsigned char dh566_p[] = { 0x36,0x53,0xA8,0x9C,0x3C,0xF1,0xD1,0x1B,0x2D,0xA2,0x64,0xDE, 0x59,0x3B,0xE3,0x8C,0x27,0x74,0xC2,0xBE,0x9B,0x6D,0x56,0xE7, 0xDF,0xFF,0x67,0x6A,0xD2,0x0C,0xE8,0x9E,0x52,0x00,0x05,0xB3, 0x53,0xF7,0x1C,0x41,0xB2,0xAC,0x38,0x16,0x32,0x3A,0x8E,0x90, 0x6C,0x7E,0xD1,0x44,0xCB,0xF9,0x2D,0x1E,0x4A,0x9A,0x32,0x81, 0x58,0xE1,0xE1,0x17,0xC1,0x9C,0xF1,0x1E,0x96,0x2D,0x5F }; // -----BEGIN DH PARAMETERS----- //MEwCRzZTqJw88dEbLaJk3lk744wndMK+m21W59//Z2rSDOieUgAFs1P3HEGyrDgW //MjqOkGx+0UTL+S0eSpoygVjh4RfBnPEeli1fAgEF // -----END DH PARAMETERS----- unsigned char dh566_g[] = { 0x05 }; DH *dh; if ((dh = DH_new()) == NULL ) return (NULL ); #if OPENSSL_VERSION_NUMBER < 0x10100000L dh->p = BN_bin2bn(dh566_p, sizeof(dh566_p), NULL ); dh->g = BN_bin2bn(dh566_g, sizeof(dh566_g), NULL ); if ((dh->p == NULL )|| (dh->g == NULL)){ DH_free(dh); return(NULL);} #else DH_set0_pqg(dh, BN_bin2bn(dh566_p, sizeof(dh566_p), NULL ), NULL, BN_bin2bn(dh566_g, sizeof(dh566_g), NULL )); #endif return (dh); } static DH *get_dh1066(void) { unsigned char dh1066_p[] = { 0x02,0x0E,0x26,0x6F,0xAA,0x9F,0xA8,0xE5,0x3F,0x70,0x88,0xF1, 0xA9,0x29,0xAE,0x1A,0x2B,0xA8,0x2F,0xE8,0xE5,0x0E,0x81,0x78, 0xD7,0x12,0x41,0xDC,0xE2,0xD5,0x10,0x6F,0x8A,0x35,0x23,0xCE, 0x66,0x93,0x67,0x14,0xEA,0x0A,0x61,0xD4,0x43,0x63,0x5C,0xDF, 0xDE,0xF5,0xB9,0xC6,0xB4,0x8C,0xBA,0x1A,0x25,0x9F,0x73,0x0F, 0x1E,0x1A,0x97,0x42,0x2E,0x60,0x9E,0x4C,0x3C,0x70,0x6A,0xFB, 0xDD,0xAA,0x7A,0x48,0xA5,0x1E,0x87,0xC8,0xA3,0x5E,0x26,0x40, 0x1B,0xDE,0x08,0x5E,0xA2,0xB8,0xE8,0x76,0x43,0xE8,0xF1,0x4B, 0x35,0x4C,0x38,0x92,0xB9,0xFF,0x61,0xE6,0x6C,0xBA,0xF9,0x16, 0x36,0x3C,0x69,0x2D,0x57,0x90,0x62,0x8A,0xD0,0xD4,0xFB,0xB2, 0x5A,0x61,0x99,0xA9,0xE8,0x93,0x80,0xA2,0xB7,0xDC,0xB1,0x6A, 0xAF,0xE3 }; // -----BEGIN DH PARAMETERS----- // MIGMAoGGAg4mb6qfqOU/cIjxqSmuGiuoL+jlDoF41xJB3OLVEG+KNSPOZpNnFOoK // YdRDY1zf3vW5xrSMuholn3MPHhqXQi5gnkw8cGr73ap6SKUeh8ijXiZAG94IXqK4 // 6HZD6PFLNUw4krn/YeZsuvkWNjxpLVeQYorQ1PuyWmGZqeiTgKK33LFqr+MCAQI= // -----END DH PARAMETERS----- unsigned char dh1066_g[] = { 0x02 }; DH *dh; if ((dh = DH_new()) == NULL ) return (NULL ); #if OPENSSL_VERSION_NUMBER < 0x10100000L dh->p = BN_bin2bn(dh1066_p, sizeof(dh1066_p), NULL ); dh->g = BN_bin2bn(dh1066_g, sizeof(dh1066_g), NULL ); if ((dh->p == NULL )|| (dh->g == NULL)){ DH_free(dh); return(NULL);} #else DH_set0_pqg(dh, BN_bin2bn(dh1066_p, sizeof(dh1066_p), NULL ), NULL, BN_bin2bn(dh1066_g, sizeof(dh1066_g), NULL )); #endif return (dh); } static DH *get_dh2066(void) { unsigned char dh2066_p[] = { 0x03,0x31,0x77,0x20,0x58,0xA6,0x69,0xA3,0x9D,0x2D,0x5E,0xE0, 0x5C,0x46,0x82,0x0F,0x9E,0x80,0xF0,0x00,0x2A,0xF9,0x0F,0x62, 0x1F,0x89,0xCE,0x7D,0x2A,0xFD,0xC5,0x9A,0x7C,0x6A,0x60,0x2C, 0xF1,0xDD,0xD4,0x4D,0x6B,0xCD,0xE9,0x95,0xDB,0x42,0x97,0xBA, 0xE4,0xAF,0x41,0x38,0x8F,0x57,0x31,0xA4,0x39,0xDD,0x31,0xC3, 0x6F,0x98,0x0E,0xE3,0xB1,0x43,0xD1,0x36,0xB0,0x01,0x28,0x42, 0x71,0xD3,0xB0,0x36,0xA0,0x47,0x99,0x25,0x9B,0x32,0xF5,0x86, 0xB1,0x13,0x5C,0x24,0x8D,0x8D,0x7F,0xE2,0x7F,0x9A,0xC1,0x52, 0x58,0xC0,0x63,0xAA,0x00,0x7C,0x1F,0x11,0xBD,0xAC,0x4C,0x2D, 0xE0,0xA2,0x9D,0x4E,0x21,0xE4,0x0B,0xCD,0x24,0x92,0xD2,0x37, 0x27,0x84,0x59,0x90,0x46,0x2F,0xD5,0xB9,0x27,0x93,0x18,0x88, 0xBD,0x91,0x5B,0x87,0x55,0x56,0xD8,0x1B,0xE4,0xCF,0x1C,0xAA, 0xBC,0xCF,0x80,0x1E,0x35,0x2D,0xB1,0xBC,0x35,0x31,0x92,0x62, 0x3C,0x91,0x8D,0x62,0xDA,0xCF,0x83,0x63,0x12,0x4B,0x30,0x80, 0xEE,0x82,0x3C,0x2C,0xD2,0x17,0x13,0x1F,0xF9,0x62,0x33,0x5C, 0x63,0xD8,0x75,0x5B,0xAA,0x16,0x5A,0x36,0x49,0x17,0x77,0xB7, 0x74,0xBD,0x3E,0x3F,0x98,0x20,0x59,0x5E,0xC7,0x72,0xE8,0xA3, 0x89,0x21,0xB4,0x3C,0x25,0xF4,0xF4,0x21,0x96,0x5A,0xA6,0x77, 0xFF,0x2C,0x3A,0xFC,0x98,0x5F,0xC1,0xBF,0x2A,0xCF,0xB8,0x62, 0x67,0x23,0xE8,0x2F,0xCC,0x7B,0x32,0x1B,0x6B,0x33,0x67,0x0A, 0xCB,0xD0,0x1F,0x65,0xD7,0x84,0x54,0xF6,0xF1,0x88,0xB5,0xBB, 0x0C,0x63,0x65,0x34,0xE4,0x66,0x4B }; // -----BEGIN DH PARAMETERS----- //MIIBCgKCAQMDMXcgWKZpo50tXuBcRoIPnoDwACr5D2Ific59Kv3FmnxqYCzx3dRN //a83pldtCl7rkr0E4j1cxpDndMcNvmA7jsUPRNrABKEJx07A2oEeZJZsy9YaxE1wk //jY1/4n+awVJYwGOqAHwfEb2sTC3gop1OIeQLzSSS0jcnhFmQRi/VuSeTGIi9kVuH //VVbYG+TPHKq8z4AeNS2xvDUxkmI8kY1i2s+DYxJLMIDugjws0hcTH/liM1xj2HVb //qhZaNkkXd7d0vT4/mCBZXsdy6KOJIbQ8JfT0IZZapnf/LDr8mF/BvyrPuGJnI+gv //zHsyG2szZwrL0B9l14RU9vGItbsMY2U05GZLAgEF // -----END DH PARAMETERS----- unsigned char dh2066_g[] = { 0x05 }; DH *dh; if ((dh = DH_new()) == NULL ) return (NULL ); #if OPENSSL_VERSION_NUMBER < 0x10100000L dh->p = BN_bin2bn(dh2066_p, sizeof(dh2066_p), NULL ); dh->g = BN_bin2bn(dh2066_g, sizeof(dh2066_g), NULL ); if ((dh->p == NULL )|| (dh->g == NULL)){ DH_free(dh); return(NULL);} #else DH_set0_pqg(dh, BN_bin2bn(dh2066_p, sizeof(dh2066_p), NULL ), NULL, BN_bin2bn(dh2066_g, sizeof(dh2066_g), NULL )); #endif return (dh); } static int pem_password_func(char *buf, int size, int rwflag, void *password) { UNUSED_ARG(rwflag); strncpy(buf, (char * )(password), size); buf[size - 1] = 0; return (strlen(buf)); } #if ALPN_SUPPORTED static int ServerALPNCallback(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { UNUSED_ARG(ssl); UNUSED_ARG(arg); unsigned char sa_len = (unsigned char)strlen(STUN_ALPN); unsigned char ta_len = (unsigned char)strlen(TURN_ALPN); unsigned char ha_len = (unsigned char)strlen(HTTP_ALPN); int found_http = 0; const unsigned char *ptr = in; while(ptr < (in+inlen)) { unsigned char current_len = *ptr; if(ptr+1+current_len > in+inlen) break; if((!turn_params.no_stun) && (current_len == sa_len) && (memcmp(ptr+1,STUN_ALPN,sa_len)==0)) { *out = ptr+1; *outlen = sa_len; SSL_set_app_data(ssl,STUN_ALPN); return SSL_TLSEXT_ERR_OK; } if((!turn_params.stun_only) && (current_len == ta_len) && (memcmp(ptr+1,TURN_ALPN,ta_len)==0)) { *out = ptr+1; *outlen = ta_len; SSL_set_app_data(ssl,TURN_ALPN); return SSL_TLSEXT_ERR_OK; } if((current_len == ha_len) && (memcmp(ptr+1,HTTP_ALPN,ha_len)==0)) { *out = ptr+1; *outlen = ha_len; SSL_set_app_data(ssl,HTTP_ALPN); found_http = 1; } ptr += 1 + current_len; } if(found_http) return SSL_TLSEXT_ERR_OK; return SSL_TLSEXT_ERR_NOACK; //??? } #endif static void set_ctx(SSL_CTX** out, const char *protocol, const SSL_METHOD* method) { SSL_CTX* ctx = SSL_CTX_new(method); int err = 0; #if ALPN_SUPPORTED SSL_CTX_set_alpn_select_cb(ctx, ServerALPNCallback, NULL); #endif SSL_CTX_set_default_passwd_cb_userdata(ctx, turn_params.tls_password); SSL_CTX_set_default_passwd_cb(ctx, pem_password_func); if(!(turn_params.cipher_list[0])) STRCPY(turn_params.cipher_list,DEFAULT_CIPHER_LIST); SSL_CTX_set_cipher_list(ctx, turn_params.cipher_list); SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); if (!SSL_CTX_use_certificate_chain_file(ctx, turn_params.cert_file)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: no certificate found\n", protocol); err = 1; } else { print_abs_file_name(protocol, ": Certificate", turn_params.cert_file); } if (!SSL_CTX_use_PrivateKey_file(ctx, turn_params.pkey_file, SSL_FILETYPE_PEM)) { if (!SSL_CTX_use_RSAPrivateKey_file(ctx, turn_params.pkey_file, SSL_FILETYPE_PEM)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: no valid private key found, or invalid private key password provided\n", protocol); err = 1; } else { print_abs_file_name(protocol, ": Private RSA key", turn_params.pkey_file); } } else { print_abs_file_name(protocol, ": Private key", turn_params.pkey_file); } if (!SSL_CTX_check_private_key(ctx)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: invalid private key\n", protocol); err = 1; } if(turn_params.ca_cert_file[0]) { if (!SSL_CTX_load_verify_locations(ctx, turn_params.ca_cert_file, NULL )) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot load CA from file: %s\n", turn_params.ca_cert_file); err = 1; } SSL_CTX_set_client_CA_list(ctx,SSL_load_client_CA_file(turn_params.ca_cert_file)); /* Set to require peer (client) certificate verification */ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE, NULL); /* Set the verification depth to 9 */ SSL_CTX_set_verify_depth(ctx, 9); } else { SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); } #if !defined(OPENSSL_NO_EC) && defined(OPENSSL_EC_NAMED_CURVE) { //Elliptic curve algorithms: int nid = 0; int set_auto_curve = 0; const char* curve_name = turn_params.ec_curve_name; if (!(curve_name[0])) { #if !SSL_SESSION_ECDH_AUTO_SUPPORTED curve_name = DEFAULT_EC_CURVE_NAME; #endif set_auto_curve = 1; } if(curve_name[0]) { { nid = OBJ_sn2nid(curve_name); if (nid == 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"unknown curve name: %s\n",curve_name); curve_name = DEFAULT_EC_CURVE_NAME; nid = OBJ_sn2nid(curve_name); set_auto_curve = 1; } } { EC_KEY *ecdh = EC_KEY_new_by_curve_name(nid); if (!ecdh) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: allocate EC suite\n",__FUNCTION__); set_auto_curve = 1; } else { SSL_CTX_set_tmp_ecdh(ctx, ecdh); EC_KEY_free(ecdh); } } } if(set_auto_curve) { #if SSL_SESSION_ECDH_AUTO_SUPPORTED #if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_CTX_set_ecdh_auto(ctx,1); #endif #endif set_auto_curve = 0; } } #endif {//DH algorithms: DH *dh = NULL; if(turn_params.dh_file[0]) { FILE *paramfile = fopen(turn_params.dh_file, "r"); if (!paramfile) { perror("Cannot open DH file"); } else { dh = PEM_read_DHparams(paramfile, NULL, NULL, NULL); fclose(paramfile); if(dh) { turn_params.dh_key_size = DH_CUSTOM; } } } if(!dh) { if(turn_params.dh_key_size == DH_566) dh = get_dh566(); else if(turn_params.dh_key_size == DH_2066) dh = get_dh2066(); else dh = get_dh1066(); } /* if(!dh) { dh = DH_new(); DH_generate_parameters_ex(dh, 32, DH_GENERATOR_2, 0); DH_generate_key(dh); } */ if(!dh) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: cannot allocate DH suite\n",__FUNCTION__); err = 1; } else { if (1 != SSL_CTX_set_tmp_dh (ctx, dh)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: cannot set DH\n",__FUNCTION__); err = 1; } DH_free (dh); } } {//secret key if(turn_params.secret_key_file[0]) { FILE *f = fopen(turn_params.secret_key_file, "r"); if (!f) { perror("Cannot open Secret-Key file"); } else { fseek (f, 0, SEEK_SET); if ( fread(turn_params.secret_key, sizeof(char), 16, f) != 0 ){ TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: Secret-Key file is empty\n",__FUNCTION__); } fclose (f); } } } { int op = 0; #if defined(SSL_OP_NO_SSLv2) op |= SSL_OP_NO_SSLv2; #endif #if defined(SSL_OP_NO_SSLv2) op |= SSL_OP_NO_SSLv3; #endif if(turn_params.no_tlsv1) op |= SSL_OP_NO_TLSv1; #if defined(SSL_OP_NO_TLSv1_1) if(turn_params.no_tlsv1_1) op |= SSL_OP_NO_TLSv1_1; #endif #if defined(SSL_OP_NO_TLSv1_2) if(turn_params.no_tlsv1_2) op |= SSL_OP_NO_TLSv1_2; #endif #if defined(SSL_OP_NO_DTLSv1) && DTLS_SUPPORTED if(turn_params.no_tlsv1) op |= SSL_OP_NO_DTLSv1; #endif #if defined(SSL_OP_NO_DTLSv1_2) && DTLSv1_2_SUPPORTED if(turn_params.no_tlsv1_2) op |= SSL_OP_NO_DTLSv1_2; #endif #if defined(SSL_OP_CIPHER_SERVER_PREFERENCE) op |= SSL_OP_CIPHER_SERVER_PREFERENCE; #endif #if defined(SSL_OP_SINGLE_DH_USE) op |= SSL_OP_SINGLE_DH_USE; #endif #if defined(SSL_OP_SINGLE_ECDH_USE) op |= SSL_OP_SINGLE_ECDH_USE; #endif SSL_CTX_set_options(ctx, op); } if (*out == NULL) { // Always initialize, even if issues were encountered *out = ctx; } else if (!err) { SSL_CTX_free(*out); *out = ctx; } } static void openssl_load_certificates(void); static void openssl_setup(void) { THREAD_setup(); SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); #if !TLS_SUPPORTED if(!turn_params.no_tls) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "WARNING: TLS is not supported\n"); turn_params.no_tls = 1; } #endif if(!(turn_params.no_tls && turn_params.no_dtls) && !turn_params.cert_file[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING,"\nWARNING: certificate file is not specified, I cannot start TLS/DTLS services.\nOnly 'plain' UDP/TCP listeners can be started.\n"); turn_params.no_tls = 1; turn_params.no_dtls = 1; } if(!(turn_params.no_tls && turn_params.no_dtls) && !turn_params.pkey_file[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING,"\nWARNING: private key file is not specified, I cannot start TLS/DTLS services.\nOnly 'plain' UDP/TCP listeners can be started.\n"); turn_params.no_tls = 1; turn_params.no_dtls = 1; } if(!(turn_params.no_tls && turn_params.no_dtls)) { adjust_key_file_names(); } openssl_load_certificates(); } static void openssl_load_certificates(void) { pthread_mutex_lock(&turn_params.tls_mutex); if(!turn_params.no_tls) { set_ctx(&turn_params.tls_ctx_ssl23,"SSL23",SSLv23_server_method()); /*compatibility mode */ if(!turn_params.no_tlsv1) { set_ctx(&turn_params.tls_ctx_v1_0,"TLS1.0",TLSv1_server_method()); } #if TLSv1_1_SUPPORTED if(!turn_params.no_tlsv1_1) { set_ctx(&turn_params.tls_ctx_v1_1,"TLS1.1",TLSv1_1_server_method()); } #if TLSv1_2_SUPPORTED if(!turn_params.no_tlsv1_2) { set_ctx(&turn_params.tls_ctx_v1_2,"TLS1.2",TLSv1_2_server_method()); } #endif #endif TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TLS cipher suite: %s\n",turn_params.cipher_list); } if(!turn_params.no_dtls) { #if !DTLS_SUPPORTED TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "ERROR: DTLS is not supported.\n"); #else if(OPENSSL_VERSION_NUMBER < 0x10000000L) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: TURN Server was compiled with rather old OpenSSL version, DTLS may not be working correctly.\n"); } #if DTLSv1_2_SUPPORTED set_ctx(&turn_params.dtls_ctx,"DTLS",DTLS_server_method()); set_ctx(&turn_params.dtls_ctx_v1_2,"DTLS1.2",DTLSv1_2_server_method()); SSL_CTX_set_read_ahead(turn_params.dtls_ctx_v1_2, 1); #else set_ctx(&turn_params.dtls_ctx,"DTLS",DTLSv1_server_method()); #endif SSL_CTX_set_read_ahead(turn_params.dtls_ctx, 1); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS cipher suite: %s\n",turn_params.cipher_list); #endif } pthread_mutex_unlock(&turn_params.tls_mutex); } static void reload_ssl_certs(evutil_socket_t sock, short events, void *args) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Reloading TLS certificates and keys\n"); openssl_load_certificates(); if (turn_params.tls_ctx_update_ev != NULL) event_active(turn_params.tls_ctx_update_ev, EV_READ, 0); UNUSED_ARG(sock); UNUSED_ARG(events); UNUSED_ARG(args); } /////////////////////////////// turnserver-4.5.1.1/src/apps/relay/turn_admin_server.h0000664000175000017500000000674413436577655021372 0ustar misimisi/* * 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. */ #ifndef __TURN_ADMIN_SERVER__ #define __TURN_ADMIN_SERVER__ #include #include #include #include #include #include "ns_turn_utils.h" #include "ns_turn_maps.h" #include "ns_turn_server.h" #include "apputils.h" #ifdef __cplusplus extern "C" { #endif //////////////////////////////////////////// #define ADMIN_USER_MAX_LENGTH (32) struct admin_session { int as_ok; char as_login[ADMIN_USER_MAX_LENGTH + 1]; char as_realm[STUN_MAX_REALM_SIZE + 1]; char as_eff_realm[STUN_MAX_REALM_SIZE + 1]; size_t number_of_user_sessions; }; struct admin_server { evutil_socket_t listen_fd; struct event_base* event_base; ioa_engine_handle e; int verbose; struct evconnlistener *l; struct bufferevent *in_buf; struct bufferevent *out_buf; struct bufferevent *https_in_buf; struct bufferevent *https_out_buf; ur_map *sessions; pthread_t thr; }; /////////////////////////////////////////// extern struct admin_server adminserver; extern int use_cli; #define CLI_DEFAULT_IP ("127.0.0.1") extern ioa_addr cli_addr; extern int cli_addr_set; #define CLI_DEFAULT_PORT (5766) extern int cli_port; #define CLI_PASSWORD_LENGTH (129) extern char cli_password[CLI_PASSWORD_LENGTH]; #define DEFAULT_CLI_MAX_OUTPUT_SESSIONS (256) extern int cli_max_output_sessions; extern int use_web_admin; #define WEB_ADMIN_DEFAULT_IP ("127.0.0.1") extern ioa_addr web_admin_addr; extern int web_admin_addr_set; #define WEB_ADMIN_DEFAULT_PORT (8080) extern int web_admin_port; //////////////////////////////////////////// void setup_admin_thread(void); void admin_server_receive_message(struct bufferevent *bev, void *ptr); void https_admin_server_receive_message(struct bufferevent *bev, void *ptr); int send_turn_session_info(struct turn_session_info* tsi); void send_https_socket(ioa_socket_handle s); //////////////////////////////////////////// #ifdef __cplusplus } #endif #endif /// __TURN_ADMIN_SERVER__/// turnserver-4.5.1.1/src/apps/relay/turn_ports.c0000664000175000017500000002720013436577655020044 0ustar misimisi/* * 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_maps.h" #include "ns_turn_msg_defs.h" #include "ns_turn_ioalib.h" #include "ns_ioalib_impl.h" #include "turn_ports.h" ////////// DATA //////////////////////////////////////////// #define PORTS_SIZE (0xFFFF+1) #define TPS_OUT_OF_RANGE ((u32bits)(-1)) #define TPS_TAKEN_SINGLE ((u32bits)(-2)) #define TPS_TAKEN_EVEN ((u32bits)(-3)) #define TPS_TAKEN_ODD ((u32bits)(-4)) struct _turnports { u32bits status[PORTS_SIZE]; u32bits low; u32bits high; u16bits range_start; u16bits range_stop; u16bits ports[PORTS_SIZE]; TURN_MUTEX_DECLARE(mutex) }; typedef struct _turnports turnports; /////////////// TURNPORTS statics ////////////////////////// static turnports* turnports_create(super_memory_t *sm, u16bits start, u16bits end); static u16bits turnports_size(turnports* tp); static int turnports_allocate(turnports* tp); static int turnports_allocate_even(turnports* tp, int allocate_rtcp, u64bits *reservation_token); static void turnports_release(turnports* tp, u16bits port); static int turnports_is_allocated(turnports* tp, u16bits port); static int turnports_is_available(turnports* tp, u16bits port); /////////////// UTILS ////////////////////////////////////// static int is_taken(u32bits status) { int ret = -1; switch (status) { case TPS_TAKEN_SINGLE : case TPS_TAKEN_EVEN : case TPS_TAKEN_ODD : ret = 1; break; default: ret = 0; }; return ret; } static void turnports_randomize(turnports* tp) { if(tp) { unsigned int size=(unsigned int)(tp->high-tp->low); unsigned int i=0; unsigned int cycles=size*10; for(i=0;ilow + (u16bits)(((unsigned long)random())%((unsigned long)size))); u16bits port2 = (u16bits)(tp->low + (u16bits)(((unsigned long)random())%((unsigned long)size))); if(port1!=port2) { int pos1=tp->status[port1]; int pos2=tp->status[port2]; int tmp=(int)tp->status[port1]; tp->status[port1]=tp->status[port2]; tp->status[port2]=(u32bits)tmp; tmp=(int)tp->ports[pos1]; tp->ports[pos1]=tp->ports[pos2]; tp->ports[pos2]=(u16bits)tmp; } } } } static void turnports_init(turnports* tp, u16bits start, u16bits end) { tp->low=start; tp->high=((u32bits)end)+1; tp->range_start=start; tp->range_stop=end; int i=0; for(i=0;istatus[i]=TPS_OUT_OF_RANGE; tp->ports[i]=(u16bits)i; } for(i=start;i<=end;i++) { tp->status[i]=(u32bits)i; tp->ports[i]=(u16bits)i; } for(i=((int)end)+1;istatus[i]=TPS_OUT_OF_RANGE; tp->ports[i]=(u16bits)i; } turnports_randomize(tp); TURN_MUTEX_INIT_RECURSIVE(&(tp->mutex)); } /////////////// FUNC /////////////////////////////////////// turnports* turnports_create(super_memory_t *sm, u16bits start, u16bits end) { if(start>end) return NULL; turnports* ret=(turnports*)allocate_super_memory_region(sm, sizeof(turnports)); turnports_init(ret,start,end); return ret; } u16bits turnports_size(turnports* tp) { if(!tp) return 0; else { TURN_MUTEX_LOCK(&tp->mutex); u16bits ret = (u16bits)((tp->high-tp->low)); TURN_MUTEX_UNLOCK(&tp->mutex); return ret; } } int turnports_allocate(turnports* tp) { int port=-1; TURN_MUTEX_LOCK(&tp->mutex); if(tp) { while(1) { if(tp->high <= tp->low) { TURN_MUTEX_UNLOCK(&tp->mutex); return -1; } int position=(u16bits)(tp->low & 0x0000FFFF); port=(int)tp->ports[position]; if(port<(int)(tp->range_start) || port>((int)(tp->range_stop))) { TURN_MUTEX_UNLOCK(&tp->mutex); return -1; } if(is_taken(tp->status[port])) { ++(tp->low); continue; } if(tp->status[port]!=tp->low) { ++(tp->low); continue; } tp->status[port]=TPS_TAKEN_SINGLE; ++(tp->low); break; } } TURN_MUTEX_UNLOCK(&tp->mutex); return port; } void turnports_release(turnports* tp, u16bits port) { TURN_MUTEX_LOCK(&tp->mutex); if(tp && port>=tp->range_start && port<=tp->range_stop) { u16bits position=(u16bits)(tp->high & 0x0000FFFF); if(is_taken(tp->status[port])) { tp->status[port]=tp->high; tp->ports[position]=port; ++(tp->high); } } TURN_MUTEX_UNLOCK(&tp->mutex); } int turnports_allocate_even(turnports* tp, int allocate_rtcp, u64bits *reservation_token) { if(tp) { TURN_MUTEX_LOCK(&tp->mutex); u16bits size = turnports_size(tp); if(size>1) { u16bits i=0; for(i=0;imutex); return port; } else { int rtcp_port=port+1; if(rtcp_port>tp->range_stop) { turnports_release(tp,port); } else if(!turnports_is_available(tp,rtcp_port)) { turnports_release(tp,port); } else { tp->status[port]=TPS_TAKEN_EVEN; tp->status[rtcp_port]=TPS_TAKEN_ODD; if(reservation_token) { u16bits *v16=(u16bits*)reservation_token; u32bits *v32=(u32bits*)reservation_token; v16[0]=(u16bits)(tp->ports[(u16bits)(tp->low & 0x0000FFFF)]); v16[1]=(u16bits)(tp->ports[(u16bits)(tp->high & 0x0000FFFF)]); v32[1]=(u32bits)turn_random(); } TURN_MUTEX_UNLOCK(&tp->mutex); return port; } } } } } TURN_MUTEX_UNLOCK(&tp->mutex); } return -1; } int turnports_is_allocated(turnports* tp, u16bits port) { if(!tp) return 0; else { TURN_MUTEX_LOCK(&tp->mutex); int ret = is_taken(tp->status[port]); TURN_MUTEX_UNLOCK(&tp->mutex); return ret; } } int turnports_is_available(turnports* tp, u16bits port) { if(tp) { TURN_MUTEX_LOCK(&tp->mutex); u32bits status = tp->status[port]; if((status!=TPS_OUT_OF_RANGE) && !is_taken(status)) { u16bits position=(u16bits)(status & 0x0000FFFF); if(tp->ports[position]==port) { TURN_MUTEX_UNLOCK(&tp->mutex); return 1; } } TURN_MUTEX_UNLOCK(&tp->mutex); } return 0; } /////////////////// IP-mapped PORTS ///////////////////////////////////// struct _turnipports { super_memory_t *sm; u16bits start; u16bits end; ur_addr_map ip_to_turnports_udp; ur_addr_map ip_to_turnports_tcp; TURN_MUTEX_DECLARE(mutex) }; ////////////////////////////////////////////////// static ur_addr_map *get_map(turnipports *tp, u08bits transport) { if(transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE) return &(tp->ip_to_turnports_tcp); return &(tp->ip_to_turnports_udp); } ////////////////////////////////////////////////// static turnipports* turnipports_singleton = NULL; turnipports* turnipports_create(super_memory_t *sm, u16bits start, u16bits end) { turnipports *ret = (turnipports*) allocate_super_memory_region(sm, sizeof(turnipports)); ret->sm = sm; ur_addr_map_init(&(ret->ip_to_turnports_udp)); ur_addr_map_init(&(ret->ip_to_turnports_tcp)); ret->start = start; ret->end = end; TURN_MUTEX_INIT_RECURSIVE(&(ret->mutex)); turnipports_singleton = ret; return ret; } static turnports* turnipports_add(turnipports* tp, u08bits transport, const ioa_addr *backend_addr) { ur_addr_map_value_type t = 0; if (tp && backend_addr) { ioa_addr ba; addr_cpy(&ba, backend_addr); addr_set_port(&ba, 0); TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex)); if (!ur_addr_map_get(get_map(tp, transport), &ba, &t)) { t = (ur_addr_map_value_type) turnports_create(tp->sm, tp->start, tp->end); ur_addr_map_put(get_map(tp, transport), &ba, t); } TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex)); } return (turnports*) t; } void turnipports_add_ip(u08bits transport, const ioa_addr *backend_addr) { turnipports_add(turnipports_singleton, transport, backend_addr); } int turnipports_allocate(turnipports* tp, u08bits transport, const ioa_addr *backend_addr) { int ret = -1; if (tp && backend_addr) { TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex)); turnports *t = turnipports_add(tp, transport, backend_addr); ret = turnports_allocate(t); TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex)); } return ret; } int turnipports_allocate_even(turnipports* tp, const ioa_addr *backend_addr, int allocate_rtcp, u64bits *reservation_token) { int ret = -1; if (tp && backend_addr) { TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex)); turnports *t = turnipports_add(tp, STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE, backend_addr); ret = turnports_allocate_even(t, allocate_rtcp, reservation_token); TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex)); } return ret; } void turnipports_release(turnipports* tp, u08bits transport, const ioa_addr *socket_addr) { if (tp && socket_addr) { ioa_addr ba; ur_addr_map_value_type t; addr_cpy(&ba, socket_addr); addr_set_port(&ba, 0); TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex)); if (ur_addr_map_get(get_map(tp, transport), &ba, &t)) { turnports_release((turnports*) t, addr_get_port(socket_addr)); } TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex)); } } int turnipports_is_allocated(turnipports* tp, u08bits transport, const ioa_addr *backend_addr, u16bits port) { int ret = 0; if (tp && backend_addr) { ioa_addr ba; ur_addr_map_value_type t; addr_cpy(&ba, backend_addr); addr_set_port(&ba, 0); TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex)); if (ur_addr_map_get(get_map(tp,transport), &ba, &t)) { ret = turnports_is_allocated((turnports*) t, port); } TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex)); } return ret; } int turnipports_is_available(turnipports* tp, u08bits transport, const ioa_addr *backend_addr, u16bits port) { int ret = 0; if (tp && backend_addr) { ioa_addr ba; ur_addr_map_value_type t; addr_cpy(&ba, backend_addr); addr_set_port(&ba, 0); TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex)); if (!ur_addr_map_get(get_map(tp,transport), &ba, &t)) { ret = 1; } else { ret = turnports_is_available((turnports*) t, port); } TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex)); } return ret; } ////////////////////////////////////////////////////////////////// turnserver-4.5.1.1/src/apps/relay/tls_listener.c0000664000175000017500000002346113436577655020341 0ustar misimisi/* * 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 "apputils.h" #include "mainrelay.h" #include "ns_turn_utils.h" #include "tls_listener.h" #include "ns_ioalib_impl.h" #include /////////////////////////////////////////////////// #define FUNCSTART if(server && eve(server->verbose)) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s:%d:start\n",__FUNCTION__,__LINE__) #define FUNCEND if(server && eve(server->verbose)) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s:%d:end\n",__FUNCTION__,__LINE__) struct tls_listener_relay_server_info { char ifname[1025]; ioa_addr addr; ioa_engine_handle e; int verbose; struct evconnlistener *l; struct evconnlistener *sctp_l; struct message_to_relay sm; ioa_engine_new_connection_event_handler connect_cb; struct relay_server *relay_server; }; /////////////// io handlers /////////////////// static void server_input_handler(struct evconnlistener *l, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *arg) { UNUSED_ARG(l); tls_listener_relay_server_type * server = (tls_listener_relay_server_type*) arg; if(!(server->connect_cb)) { socket_closesocket(fd); return; } FUNCSTART; if (!server) return; ns_bcopy(sa,&(server->sm.m.sm.nd.src_addr),socklen); addr_debug_print(server->verbose, &(server->sm.m.sm.nd.src_addr),"tcp or tls connected to"); SOCKET_TYPE st = TENTATIVE_TCP_SOCKET; if(turn_params.no_tls) st = TCP_SOCKET; else if(turn_params.no_tcp) st = TLS_SOCKET; ioa_socket_handle ioas = create_ioa_socket_from_fd( server->e, fd, NULL, st, CLIENT_SOCKET, &(server->sm.m.sm.nd.src_addr), &(server->addr)); if (ioas) { server->sm.m.sm.nd.recv_ttl = TTL_IGNORE; server->sm.m.sm.nd.recv_tos = TOS_IGNORE; server->sm.m.sm.nd.nbh = NULL; server->sm.m.sm.s = ioas; server->sm.m.sm.can_resume = 1; server->sm.relay_server = server->relay_server; int rc = server->connect_cb(server->e, &(server->sm)); if (rc < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot create tcp or tls session\n"); } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot create ioa_socket from FD\n"); socket_closesocket(fd); } FUNCEND ; } #if !defined(TURN_NO_SCTP) static void sctp_server_input_handler(struct evconnlistener *l, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *arg) { UNUSED_ARG(l); tls_listener_relay_server_type * server = (tls_listener_relay_server_type*) arg; if(!(server->connect_cb)) { socket_closesocket(fd); return; } FUNCSTART; if (!server) return; ns_bcopy(sa,&(server->sm.m.sm.nd.src_addr),socklen); addr_debug_print(server->verbose, &(server->sm.m.sm.nd.src_addr),"sctp or tls/sctp connected to"); SOCKET_TYPE st = TENTATIVE_SCTP_SOCKET; if(turn_params.no_tls) st = SCTP_SOCKET; else if(turn_params.no_tcp) st = TLS_SCTP_SOCKET; ioa_socket_handle ioas = create_ioa_socket_from_fd( server->e, fd, NULL, st, CLIENT_SOCKET, &(server->sm.m.sm.nd.src_addr), &(server->addr)); if (ioas) { server->sm.m.sm.nd.recv_ttl = TTL_IGNORE; server->sm.m.sm.nd.recv_tos = TOS_IGNORE; server->sm.m.sm.nd.nbh = NULL; server->sm.m.sm.s = ioas; server->sm.m.sm.can_resume = 1; server->sm.relay_server = server->relay_server; int rc = server->connect_cb(server->e, &(server->sm)); if (rc < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot create sctp or tls/sctp session\n"); } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot create ioa_socket from FD\n"); socket_closesocket(fd); } FUNCEND ; } #endif ///////////////////// operations ////////////////////////// static int create_server_listener(tls_listener_relay_server_type* server) { FUNCSTART; if(!server) return -1; evutil_socket_t tls_listen_fd = -1; tls_listen_fd = socket(server->addr.ss.sa_family, CLIENT_STREAM_SOCKET_TYPE, CLIENT_STREAM_SOCKET_PROTOCOL); if (tls_listen_fd < 0) { perror("socket"); return -1; } if(sock_bind_to_device(tls_listen_fd, (unsigned char*)server->ifname)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Cannot bind listener socket to device %s\n",server->ifname); } { const int max_binding_time = 60; int addr_bind_cycle = 0; retry_addr_bind: if(addr_bind(tls_listen_fd,&server->addr,1,1,TCP_SOCKET)<0) { perror("Cannot bind local socket to addr"); char saddr[129]; addr_to_string(&server->addr,(u08bits*)saddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING,"Cannot bind TLS/TCP listener socket to addr %s\n",saddr); if(addr_bind_cycle++l = evconnlistener_new(server->e->event_base, server_input_handler, server, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 1024, tls_listen_fd); if(!(server->l)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Cannot create TLS listener\n"); socket_closesocket(tls_listen_fd); return -1; } if(!turn_params.no_tcp && !turn_params.no_tls) addr_debug_print(server->verbose, &server->addr,"TLS/TCP listener opened on "); else if(!turn_params.no_tls) addr_debug_print(server->verbose, &server->addr,"TLS listener opened on "); else if(!turn_params.no_tcp) addr_debug_print(server->verbose, &server->addr,"TCP listener opened on "); FUNCEND; return 0; } #if !defined(TURN_NO_SCTP) static int sctp_create_server_listener(tls_listener_relay_server_type* server) { FUNCSTART; if(!server) return -1; evutil_socket_t tls_listen_fd = -1; tls_listen_fd = socket(server->addr.ss.sa_family, SCTP_CLIENT_STREAM_SOCKET_TYPE, SCTP_CLIENT_STREAM_SOCKET_PROTOCOL); if (tls_listen_fd < 0) { perror("socket"); return -1; } if(sock_bind_to_device(tls_listen_fd, (unsigned char*)server->ifname)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Cannot bind listener socket to device %s\n",server->ifname); } if(addr_bind(tls_listen_fd,&server->addr,1,0,SCTP_SOCKET)<0) { close(tls_listen_fd); return -1; } socket_tcp_set_keepalive(tls_listen_fd,SCTP_SOCKET); socket_set_nonblocking(tls_listen_fd); server->sctp_l = evconnlistener_new(server->e->event_base, sctp_server_input_handler, server, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 1024, tls_listen_fd); if(!(server->sctp_l)) { socket_closesocket(tls_listen_fd); return -1; } if (!turn_params.no_tls) addr_debug_print(server->verbose, &server->addr, "TLS/SCTP listener opened on "); else addr_debug_print(server->verbose, &server->addr, "SCTP listener opened on "); FUNCEND; return 0; } #endif static int init_server(tls_listener_relay_server_type* server, const char* ifname, const char *local_address, int port, int verbose, ioa_engine_handle e, ioa_engine_new_connection_event_handler send_socket, struct relay_server *relay_server) { if(!server) return -1; server->connect_cb = send_socket; server->relay_server = relay_server; if(ifname) STRCPY(server->ifname,ifname); if(make_ioa_addr((const u08bits*)local_address, port, &server->addr)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot create a TCP/TLS listener for address: %s\n",local_address); return -1; } server->verbose=verbose; server->e = e; #if !defined(TURN_NO_SCTP) sctp_create_server_listener(server); #endif return create_server_listener(server); } /////////////////////////////////////////////////////////// tls_listener_relay_server_type* create_tls_listener_server(const char* ifname, const char *local_address, int port, int verbose, ioa_engine_handle e, ioa_engine_new_connection_event_handler send_socket, struct relay_server *relay_server) { tls_listener_relay_server_type* server = (tls_listener_relay_server_type*) allocate_super_memory_engine(e,sizeof(tls_listener_relay_server_type)); if (init_server(server, ifname, local_address, port, verbose, e, send_socket, relay_server) < 0) { return NULL ; } else { return server; } } ////////////////////////////////////////////////////////////////// turnserver-4.5.1.1/src/apps/relay/http_server.c0000664000175000017500000002445713436577655020205 0ustar misimisi/* * Copyright (C) 2011, 2012, 2013, 2014 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_ioalib_impl.h" #include "http_server.h" #include #include #include ////////////////////////////////////// struct headers_list { size_t n; char **keys; char **values; }; struct http_headers { struct evkeyvalq *uri_headers; struct headers_list *post_headers; }; ////////////////////////////////////// static void write_http_echo(ioa_socket_handle s) { if(s && !ioa_socket_tobeclosed(s)) { SOCKET_APP_TYPE sat = get_ioa_socket_app_type(s); if((sat == HTTP_CLIENT_SOCKET) || (sat == HTTPS_CLIENT_SOCKET)) { ioa_network_buffer_handle nbh_http = ioa_network_buffer_allocate(s->e); size_t len_http = ioa_network_buffer_get_size(nbh_http); u08bits *data = ioa_network_buffer_data(nbh_http); char data_http[1025]; char content_http[1025]; const char* title = "TURN Server"; snprintf(content_http,sizeof(content_http)-1,"\r\n\r\n \r\n %s\r\n \r\n \r\n %s
use https connection for the admin session\r\n \r\n\r\n",title,title); snprintf(data_http,sizeof(data_http)-1,"HTTP/1.0 200 OK\r\nServer: %s\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: %d\r\n\r\n%.906s",TURN_SOFTWARE,(int)strlen(content_http),content_http); len_http = strlen(data_http); ns_bcopy(data_http,data,len_http); ioa_network_buffer_set_size(nbh_http,len_http); send_data_from_ioa_socket_nbh(s, NULL, nbh_http, TTL_IGNORE, TOS_IGNORE,NULL); } } } void handle_http_echo(ioa_socket_handle s) { write_http_echo(s); } const char* get_http_date_header() { static char buffer_date[256]; static char buffer_header[1025]; static const char* wds[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; static const char* mons[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; time_t now = time(NULL); struct tm *gmtm = gmtime(&now); buffer_header[0]=0; buffer_date[0]=0; if(gmtm) { snprintf(buffer_date,sizeof(buffer_date)-1,"%s, %d %s %d %d:%d:%d GMT",wds[gmtm->tm_wday], gmtm->tm_mday, mons[gmtm->tm_mon], gmtm->tm_year+1900, gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec); buffer_date[sizeof(buffer_date)-1]=0; snprintf(buffer_header,sizeof(buffer_header)-1,"Date: %s\r\n",buffer_date); buffer_header[sizeof(buffer_header)-1]=0; } return buffer_header; } /////////////////////////////////////////////// static struct headers_list * post_parse(char *data, size_t data_len) { while((*data=='\r')||(*data=='\n')) ++data; char *post_data = (char*)calloc(data_len + 1, sizeof(char)); memcpy(post_data, data, data_len); char *fmarker = NULL; char *fsplit = strtok_r(post_data, "&", &fmarker); struct headers_list *list = (struct headers_list*)malloc(sizeof(struct headers_list)); ns_bzero(list,sizeof(struct headers_list)); while (fsplit != NULL) { char *vmarker = NULL; char *key = strtok_r(fsplit, "=", &vmarker); char *value = strtok_r(NULL, "=", &vmarker); char empty[1]; empty[0]=0; value = value ? value : empty; value = evhttp_decode_uri(value); char *p = value; while (*p) { if (*p == '+') *p = ' '; p++; } list->keys = (char**)realloc(list->keys,sizeof(char*)*(list->n+1)); list->keys[list->n] = strdup(key); list->values = (char**)realloc(list->values,sizeof(char*)*(list->n+1)); list->values[list->n] = value; ++(list->n); fsplit = strtok_r(NULL, "&", &fmarker); } free(post_data); return list; } static struct http_request* parse_http_request_1(struct http_request* ret, char* request, int parse_post) { if(ret && request) { char* s = strstr(request," HTTP/"); if(!s) { free(ret); ret = NULL; } else { *s = 0; struct evhttp_uri *uri = evhttp_uri_parse(request); if(!uri) { free(ret); ret = NULL; } else { const char *query = evhttp_uri_get_query(uri); if(query) { struct evkeyvalq* kv = (struct evkeyvalq*)malloc(sizeof(struct evkeyvalq)); ns_bzero(kv,sizeof(struct evkeyvalq)); if(evhttp_parse_query_str(query, kv)<0) { free(ret); ret = NULL; } else { ret->headers = (struct http_headers*)malloc(sizeof(struct http_headers)); ns_bzero(ret->headers,sizeof(struct http_headers)); ret->headers->uri_headers = kv; } } const char *path = evhttp_uri_get_path(uri); if(path && ret) ret->path = strdup(path); evhttp_uri_free(uri); if(parse_post && ret) { char *body = strstr(s+1,"\r\n\r\n"); if(body && body[0]) { if(!ret->headers) { ret->headers = (struct http_headers*)malloc(sizeof(struct http_headers)); ns_bzero(ret->headers,sizeof(struct http_headers)); } ret->headers->post_headers = post_parse(body,strlen(body)); } } } *s = ' '; } } return ret; } struct http_request* parse_http_request(char* request) { struct http_request* ret = NULL; if(request) { ret = (struct http_request*)malloc(sizeof(struct http_request)); ns_bzero(ret,sizeof(struct http_request)); if(strstr(request,"GET ") == request) { ret->rtype = HRT_GET; ret = parse_http_request_1(ret,request+4,0); } else if(strstr(request,"HEAD ") == request) { ret->rtype = HRT_HEAD; ret = parse_http_request_1(ret,request+5,0); } else if(strstr(request,"POST ") == request) { ret->rtype = HRT_POST; ret = parse_http_request_1(ret,request+5,1); } else if(strstr(request,"PUT ") == request) { ret->rtype = HRT_PUT; ret = parse_http_request_1(ret,request+4,1); } else if(strstr(request,"DELETE ") == request) { ret->rtype = HRT_DELETE; ret = parse_http_request_1(ret,request+7,1); } else { free(ret); ret = NULL; } } return ret; } static const char * get_headers_list_value(struct headers_list *h, const char* key) { const char* ret = NULL; if(h && h->keys && h->values && key && key[0]) { size_t i = 0; for(i=0;in;++i) { if(h->keys[i] && !strcmp(key,h->keys[i]) && h->values[i]) { ret = h->values[i]; break; } } } return ret; } static void free_headers_list(struct headers_list *h) { if(h) { if(h->keys) { size_t i = 0; for(i=0;in;++i) { if(h->keys[i]) { free(h->keys[i]); h->keys[i]=NULL; } } free(h->keys); h->keys = NULL; } if(h->values) { size_t i = 0; for(i=0;in;++i) { if(h->values[i]) { free(h->values[i]); h->values[i]=NULL; } } free(h->values); h->values = NULL; } h->n = 0; free(h); } } const char *get_http_header_value(const struct http_request *request, const char* key, const char* default_value) { const char *ret = NULL; if(key && key[0] && request && request->headers) { if(request->headers->uri_headers) { ret = evhttp_find_header(request->headers->uri_headers,key); } if(!ret && request->headers->post_headers) { ret = get_headers_list_value(request->headers->post_headers,key); } } if(!ret) { ret = default_value; } return ret; } void free_http_request(struct http_request *request) { if(request) { if(request->path) { free(request->path); request->path = NULL; } if(request->headers) { if(request->headers->uri_headers) { evhttp_clear_headers(request->headers->uri_headers); free(request->headers->uri_headers); request->headers->uri_headers = NULL; } if(request->headers->post_headers) { free_headers_list(request->headers->post_headers); request->headers->post_headers = NULL; } free(request->headers); request->headers = NULL; } free(request); } } //////////////////////////////////////////// struct str_buffer { size_t capacity; size_t sz; char* buffer; }; struct str_buffer* str_buffer_new(void) { struct str_buffer* ret = (struct str_buffer*)malloc(sizeof(struct str_buffer)); ns_bzero(ret,sizeof(struct str_buffer)); ret->buffer = (char*)malloc(1); ret->buffer[0] = 0; ret->capacity = 1; return ret; } void str_buffer_append(struct str_buffer* sb, const char* str) { if(sb && str && str[0]) { size_t len = strlen(str); while(sb->sz + len + 1 > sb->capacity) { sb->capacity += len + 1024; sb->buffer = (char*)realloc(sb->buffer,sb->capacity); } ns_bcopy(str,sb->buffer+sb->sz,len+1); sb->sz += len; } } void str_buffer_append_sz(struct str_buffer* sb, size_t sz) { char ssz[129]; snprintf(ssz,sizeof(ssz)-1,"%lu",(unsigned long)sz); str_buffer_append(sb,ssz); } void str_buffer_append_sid(struct str_buffer* sb, turnsession_id sid) { char ssz[129]; snprintf(ssz,sizeof(ssz)-1,"%018llu",(unsigned long long)sid); str_buffer_append(sb,ssz); } const char* str_buffer_get_str(const struct str_buffer *sb) { if(sb) { return sb->buffer; } return NULL; } size_t str_buffer_get_str_len(const struct str_buffer *sb) { if(sb) { return sb->sz; } return 0; } void str_buffer_free(struct str_buffer *sb) { if(sb) { free(sb->buffer); free(sb); } } /////////////////////////////////////////////// turnserver-4.5.1.1/src/apps/relay/dtls_listener.c0000664000175000017500000007066013436577655020510 0ustar misimisi/* * 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 "apputils.h" #include "mainrelay.h" #include "dtls_listener.h" #include "ns_ioalib_impl.h" #include "ns_turn_openssl.h" #include /* #define REQUEST_CLIENT_CERT */ /////////////////////////////////////////////////// #define FUNCSTART if(server && eve(server->verbose)) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s:%d:start\n",__FUNCTION__,__LINE__) #define FUNCEND if(server && eve(server->verbose)) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s:%d:end\n",__FUNCTION__,__LINE__) #define COOKIE_SECRET_LENGTH (32) #define MAX_SINGLE_UDP_BATCH (16) struct dtls_listener_relay_server_info { char ifname[1025]; ioa_addr addr; ioa_engine_handle e; turn_turnserver *ts; int verbose; #if DTLS_SUPPORTED SSL_CTX *dtls_ctx; #if DTLSv1_2_SUPPORTED SSL_CTX *dtls_ctx_v1_2; #endif #endif struct event *udp_listen_ev; ioa_socket_handle udp_listen_s; ur_addr_map *children_ss; /* map of socket children on remote addr */ struct message_to_relay sm; int slen0; ioa_engine_new_connection_event_handler connect_cb; }; ///////////// forward declarations //////// static int create_server_socket(dtls_listener_relay_server_type* server, int report_creation); static int clean_server(dtls_listener_relay_server_type* server); static int reopen_server_socket(dtls_listener_relay_server_type* server, evutil_socket_t fd); ///////////// dtls message types ////////// int is_dtls_handshake_message(const unsigned char* buf, int len); int is_dtls_data_message(const unsigned char* buf, int len); int is_dtls_alert_message(const unsigned char* buf, int len); int is_dtls_cipher_change_message(const unsigned char* buf, int len); int get_dtls_version(const unsigned char* buf, int len); int is_dtls_message(const unsigned char* buf, int len); int is_dtls_handshake_message(const unsigned char* buf, int len) { return (buf && len>3 && buf[0]==0x16 && buf[1]==0xfe && ((buf[2]==0xff)||(buf[2]==0xfd))); } int is_dtls_data_message(const unsigned char* buf, int len) { return (buf && len>3 && buf[0]==0x17 && buf[1]==0xfe && ((buf[2]==0xff)||(buf[2]==0xfd))); } int is_dtls_alert_message(const unsigned char* buf, int len) { return (buf && len>3 && buf[0]==0x15 && buf[1]==0xfe && ((buf[2]==0xff)||(buf[2]==0xfd))); } int is_dtls_cipher_change_message(const unsigned char* buf, int len) { return (buf && len>3 && buf[0]==0x14 && buf[1]==0xfe && ((buf[2]==0xff)||(buf[2]==0xfd))); } int is_dtls_message(const unsigned char* buf, int len) { if(buf && (len>3) && (buf[1])==0xfe && ((buf[2]==0xff)||(buf[2]==0xfd))) { switch (buf[0]) { case 0x14: case 0x15: case 0x16: case 0x17: return 1; default: ; } } return 0; } /* 0 - 1.0, 1 - 1.2 */ int get_dtls_version(const unsigned char* buf, int len) { if(buf && (len>3) && (buf[2] == 0xfd)) return 1; return 0; } ///////////// utils ///////////////////// #if DTLS_SUPPORTED static void calculate_cookie(SSL* ssl, unsigned char *cookie_secret, unsigned int cookie_length) { long rv=(long)ssl; long inum=(cookie_length-(((long)cookie_secret)%sizeof(long)))/sizeof(long); long i=0; long *ip=(long*)cookie_secret; for(i=0;i= 0x10100000L const #endif unsigned char *cookie, unsigned int cookie_len) { unsigned int resultlength=0; unsigned char result[COOKIE_SECRET_LENGTH]; generate_cookie(ssl, result, &resultlength); if (cookie_len == resultlength && memcmp(result, cookie, resultlength) == 0) { //TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: cookies are OK, length=%u\n",__FUNCTION__,cookie_len); return 1; } else { //TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"%s: cookies are OK, length=%u\n",__FUNCTION__,cookie_len); return 0; } } /////////////// io handlers /////////////////// static ioa_socket_handle dtls_accept_client_connection( dtls_listener_relay_server_type* server, ioa_socket_handle sock, SSL *ssl, ioa_addr *remote_addr, ioa_addr *local_addr, ioa_network_buffer_handle nbh) { FUNCSTART; if (!ssl) return NULL; int rc = ssl_read(sock->fd, ssl, nbh, server->verbose); if (rc < 0) return NULL; addr_debug_print(server->verbose, remote_addr, "Accepted connection from"); ioa_socket_handle ioas = create_ioa_socket_from_ssl(server->e, sock, ssl, DTLS_SOCKET, CLIENT_SOCKET, remote_addr, local_addr); if(ioas) { addr_cpy(&(server->sm.m.sm.nd.src_addr),remote_addr); server->sm.m.sm.nd.recv_ttl = TTL_IGNORE; server->sm.m.sm.nd.recv_tos = TOS_IGNORE; server->sm.m.sm.s = ioas; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot create ioa_socket from SSL\n"); } FUNCEND ; return ioas; } static ioa_socket_handle dtls_server_input_handler(dtls_listener_relay_server_type* server, ioa_socket_handle s, ioa_network_buffer_handle nbh) { FUNCSTART; if (!server || !nbh) { return NULL; } SSL* connecting_ssl = NULL; BIO *wbio = NULL; struct timeval timeout; /* Create BIO */ wbio = BIO_new_dgram(s->fd, BIO_NOCLOSE); (void)BIO_dgram_set_peer(wbio, (struct sockaddr*) &(server->sm.m.sm.nd.src_addr)); /* Set and activate timeouts */ timeout.tv_sec = DTLS_MAX_RECV_TIMEOUT; timeout.tv_usec = 0; BIO_ctrl(wbio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout); #if DTLSv1_2_SUPPORTED if(get_dtls_version(ioa_network_buffer_data(nbh), (int)ioa_network_buffer_get_size(nbh)) == 1) { connecting_ssl = SSL_NEW(server->dtls_ctx_v1_2); } else { connecting_ssl = SSL_NEW(server->dtls_ctx); } #else { connecting_ssl = SSL_NEW(server->dtls_ctx); } #endif SSL_set_accept_state(connecting_ssl); SSL_set_bio(connecting_ssl, NULL, wbio); SSL_set_options(connecting_ssl, SSL_OP_COOKIE_EXCHANGE); SSL_set_max_cert_list(connecting_ssl, 655350); ioa_socket_handle rc = dtls_accept_client_connection(server, s, connecting_ssl, &(server->sm.m.sm.nd.src_addr), &(server->addr), nbh); if (!rc) { if (!(SSL_get_shutdown(connecting_ssl) & SSL_SENT_SHUTDOWN)) { SSL_set_shutdown(connecting_ssl, SSL_RECEIVED_SHUTDOWN); SSL_shutdown(connecting_ssl); } SSL_FREE(connecting_ssl); } return rc; } #endif static int handle_udp_packet(dtls_listener_relay_server_type *server, struct message_to_relay *sm, ioa_engine_handle ioa_eng, turn_turnserver *ts) { int verbose = ioa_eng->verbose; ioa_socket_handle s = sm->m.sm.s; ur_addr_map_value_type mvt = 0; if(!(server->children_ss)) { server->children_ss = (ur_addr_map*)allocate_super_memory_engine(server->e, sizeof(ur_addr_map)); ur_addr_map_init(server->children_ss); } ur_addr_map *amap = server->children_ss; ioa_socket_handle chs = NULL; if ((ur_addr_map_get(amap, &(sm->m.sm.nd.src_addr), &mvt) > 0) && mvt) { chs = (ioa_socket_handle) mvt; } if (chs && !ioa_socket_tobeclosed(chs) && (chs->sockets_container == amap) && (chs->magic == SOCKET_MAGIC)) { s = chs; sm->m.sm.s = s; if(s->ssl) { int sslret = ssl_read(s->fd, s->ssl, sm->m.sm.nd.nbh, verbose); if(sslret < 0) { ioa_network_buffer_delete(ioa_eng, sm->m.sm.nd.nbh); sm->m.sm.nd.nbh = NULL; ts_ur_super_session *ss = (ts_ur_super_session *) s->session; if (ss) { turn_turnserver *server = (turn_turnserver *) ss->server; if (server) { shutdown_client_connection(server, ss, 0, "SSL read error"); } } else { close_ioa_socket(s); } ur_addr_map_del(amap, &(sm->m.sm.nd.src_addr), NULL); sm->m.sm.s = NULL; s = NULL; chs = NULL; } else if(ioa_network_buffer_get_size(sm->m.sm.nd.nbh)>0) { ; } else { ioa_network_buffer_delete(ioa_eng, sm->m.sm.nd.nbh); sm->m.sm.nd.nbh = NULL; } } if(s && ioa_socket_check_bandwidth(s,sm->m.sm.nd.nbh,1)) { s->e = ioa_eng; if (s && s->read_cb && sm->m.sm.nd.nbh) { s->read_cb(s, IOA_EV_READ, &(sm->m.sm.nd), s->read_ctx, 1); ioa_network_buffer_delete(ioa_eng, sm->m.sm.nd.nbh); sm->m.sm.nd.nbh = NULL; if (ioa_socket_tobeclosed(s)) { ts_ur_super_session *ss = (ts_ur_super_session *) s->session; if (ss) { turn_turnserver *server = (turn_turnserver *) ss->server; if (server) { shutdown_client_connection(server, ss, 0, "UDP packet processing error"); } } } } } } else { if (chs && ioa_socket_tobeclosed(chs)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: socket to be closed\n", __FUNCTION__); { u08bits saddr[129]; u08bits rsaddr[129]; long thrid = (long) pthread_self(); addr_to_string(get_local_addr_from_ioa_socket(chs),saddr); addr_to_string(get_remote_addr_from_ioa_socket(chs),rsaddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: 111.111: thrid=0x%lx: Amap = 0x%lx, socket container=0x%lx, local addr %s, remote addr %s, s=0x%lx, done=%d, tbc=%d\n", __FUNCTION__, thrid, (long) amap, (long) (chs->sockets_container), (char*) saddr, (char*) rsaddr, (long) s, (int) (chs->done), (int) (chs->tobeclosed)); } } if (chs && (chs->magic != SOCKET_MAGIC)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: wrong socket magic\n", __FUNCTION__); } if (chs && (chs->sockets_container != amap)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: wrong socket container\n", __FUNCTION__); { u08bits saddr[129]; u08bits rsaddr[129]; long thrid = (long) pthread_self(); addr_to_string(get_local_addr_from_ioa_socket(chs),saddr); addr_to_string(get_remote_addr_from_ioa_socket(chs),rsaddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: 111.222: thrid=0x%lx: Amap = 0x%lx, socket container=0x%lx, local addr %s, remote addr %s, s=0x%lx, done=%d, tbc=%d, st=%d, sat=%d\n", __FUNCTION__, thrid, (long) amap, (long) (chs->sockets_container), (char*) saddr, (char*) rsaddr, (long) chs, (int) (chs->done), (int) (chs->tobeclosed), (int) (chs->st), (int) (chs->sat)); } } chs = NULL; #if DTLS_SUPPORTED if (!turn_params.no_dtls && is_dtls_handshake_message(ioa_network_buffer_data(sm->m.sm.nd.nbh), (int)ioa_network_buffer_get_size(sm->m.sm.nd.nbh))) { chs = dtls_server_input_handler(server,s, sm->m.sm.nd.nbh); ioa_network_buffer_delete(server->e, sm->m.sm.nd.nbh); sm->m.sm.nd.nbh = NULL; } #endif if(!chs) { chs = create_ioa_socket_from_fd(ioa_eng, s->fd, s, UDP_SOCKET, CLIENT_SOCKET, &(sm->m.sm.nd.src_addr), get_local_addr_from_ioa_socket(s)); } s = chs; sm->m.sm.s = s; if (s) { if(verbose) { u08bits saddr[129]; u08bits rsaddr[129]; addr_to_string(get_local_addr_from_ioa_socket(s),saddr); addr_to_string(get_remote_addr_from_ioa_socket(s),rsaddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: New UDP endpoint: local addr %s, remote addr %s\n", __FUNCTION__, (char*) saddr,(char*) rsaddr); } s->e = ioa_eng; add_socket_to_map(s, amap); if(open_client_connection_session(ts, &(sm->m.sm))<0) { return -1; } } } return 0; } static int create_new_connected_udp_socket( dtls_listener_relay_server_type* server, ioa_socket_handle s) { evutil_socket_t udp_fd = socket(s->local_addr.ss.sa_family, CLIENT_DGRAM_SOCKET_TYPE, CLIENT_DGRAM_SOCKET_PROTOCOL); if (udp_fd < 0) { perror("socket"); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Cannot allocate new socket\n", __FUNCTION__); return -1; } if (sock_bind_to_device(udp_fd, (unsigned char*) (s->e->relay_ifname)) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot bind udp server socket to device %s\n", (char*) (s->e->relay_ifname)); } ioa_socket_handle ret = (ioa_socket*) turn_malloc(sizeof(ioa_socket)); if (!ret) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Cannot allocate new socket structure\n", __FUNCTION__); close(udp_fd); return -1; } ns_bzero(ret, sizeof(ioa_socket)); ret->magic = SOCKET_MAGIC; ret->fd = udp_fd; ret->family = s->family; ret->st = s->st; ret->sat = CLIENT_SOCKET; ret->local_addr_known = 1; addr_cpy(&(ret->local_addr), &(s->local_addr)); if (addr_bind(udp_fd,&(s->local_addr),1,1,UDP_SOCKET) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot bind new detached udp server socket to local addr\n"); IOA_CLOSE_SOCKET(ret); return -1; } ret->bound = 1; { int connect_err = 0; if (addr_connect(udp_fd, &(server->sm.m.sm.nd.src_addr), &connect_err) < 0) { char sl[129]; char sr[129]; addr_to_string(&(ret->local_addr),(u08bits*)sl); addr_to_string(&(server->sm.m.sm.nd.src_addr),(u08bits*)sr); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot connect new detached udp client socket from local addr %s to remote addr %s\n",sl,sr); IOA_CLOSE_SOCKET(ret); return -1; } } ret->connected = 1; addr_cpy(&(ret->remote_addr), &(server->sm.m.sm.nd.src_addr)); set_socket_options(ret); ret->current_ttl = s->current_ttl; ret->default_ttl = s->default_ttl; ret->current_tos = s->current_tos; ret->default_tos = s->default_tos; #if DTLS_SUPPORTED if (!turn_params.no_dtls && is_dtls_handshake_message( ioa_network_buffer_data(server->sm.m.sm.nd.nbh), (int) ioa_network_buffer_get_size( server->sm.m.sm.nd.nbh))) { SSL* connecting_ssl = NULL; BIO *wbio = NULL; struct timeval timeout; /* Create BIO */ wbio = BIO_new_dgram(ret->fd, BIO_NOCLOSE); (void) BIO_dgram_set_peer(wbio, (struct sockaddr*) &(server->sm.m.sm.nd.src_addr)); BIO_ctrl(wbio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &(server->sm.m.sm.nd.src_addr)); /* Set and activate timeouts */ timeout.tv_sec = DTLS_MAX_RECV_TIMEOUT; timeout.tv_usec = 0; BIO_ctrl(wbio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout); #if DTLSv1_2_SUPPORTED if(get_dtls_version(ioa_network_buffer_data(server->sm.m.sm.nd.nbh), (int)ioa_network_buffer_get_size(server->sm.m.sm.nd.nbh)) == 1) { connecting_ssl = SSL_NEW(server->dtls_ctx_v1_2); } else { connecting_ssl = SSL_NEW(server->dtls_ctx); } #else { connecting_ssl = SSL_NEW(server->dtls_ctx); } #endif SSL_set_accept_state(connecting_ssl); SSL_set_bio(connecting_ssl, NULL, wbio); SSL_set_options(connecting_ssl, SSL_OP_COOKIE_EXCHANGE); SSL_set_max_cert_list(connecting_ssl, 655350); int rc = ssl_read(ret->fd, connecting_ssl, server->sm.m.sm.nd.nbh, server->verbose); if (rc < 0) { if (!(SSL_get_shutdown(connecting_ssl) & SSL_SENT_SHUTDOWN)) { SSL_set_shutdown(connecting_ssl, SSL_RECEIVED_SHUTDOWN); SSL_shutdown(connecting_ssl); } SSL_FREE(connecting_ssl); IOA_CLOSE_SOCKET(ret); return -1; } addr_debug_print(server->verbose, &(server->sm.m.sm.nd.src_addr), "Accepted DTLS connection from"); ret->ssl = connecting_ssl; ioa_network_buffer_delete(server->e, server->sm.m.sm.nd.nbh); server->sm.m.sm.nd.nbh = NULL; ret->st = DTLS_SOCKET; } #endif server->sm.m.sm.s = ret; return server->connect_cb(server->e, &(server->sm)); } static void udp_server_input_handler(evutil_socket_t fd, short what, void* arg) { int cycle = 0; dtls_listener_relay_server_type* server = (dtls_listener_relay_server_type*)arg; ioa_socket_handle s = server->udp_listen_s; FUNCSTART; if (!(what & EV_READ)) { return; } //printf_server_socket(server, fd); ioa_network_buffer_handle *elem = NULL; start_udp_cycle: elem = (ioa_network_buffer_handle *)ioa_network_buffer_allocate(server->e); server->sm.m.sm.nd.nbh = elem; server->sm.m.sm.nd.recv_ttl = TTL_IGNORE; server->sm.m.sm.nd.recv_tos = TOS_IGNORE; server->sm.m.sm.can_resume = 1; addr_set_any(&(server->sm.m.sm.nd.src_addr)); ssize_t bsize = 0; int flags = MSG_DONTWAIT; bsize = udp_recvfrom(fd, &(server->sm.m.sm.nd.src_addr), &(server->addr), (s08bits*)ioa_network_buffer_data(elem), (int)ioa_network_buffer_get_capacity_udp(), &(server->sm.m.sm.nd.recv_ttl), &(server->sm.m.sm.nd.recv_tos), server->e->cmsg, flags, NULL); int conn_reset = is_connreset(); int to_block = would_block(); if (bsize < 0) { if(to_block) { ioa_network_buffer_delete(server->e, server->sm.m.sm.nd.nbh); server->sm.m.sm.nd.nbh = NULL; FUNCEND; return; } #if defined(MSG_ERRQUEUE) //Linux int eflags = MSG_ERRQUEUE | MSG_DONTWAIT; static s08bits buffer[65535]; u32bits errcode = 0; ioa_addr orig_addr; int ttl = 0; int tos = 0; int slen = server->slen0; udp_recvfrom(fd, &orig_addr, &(server->addr), buffer, (int) sizeof(buffer), &ttl, &tos, server->e->cmsg, eflags, &errcode); //try again... do { bsize = recvfrom(fd, ioa_network_buffer_data(elem), ioa_network_buffer_get_capacity_udp(), flags, (struct sockaddr*) &(server->sm.m.sm.nd.src_addr), (socklen_t*) &slen); } while (bsize < 0 && (errno == EINTR)); conn_reset = is_connreset(); to_block = would_block(); #endif if(conn_reset) { ioa_network_buffer_delete(server->e, server->sm.m.sm.nd.nbh); server->sm.m.sm.nd.nbh = NULL; reopen_server_socket(server,fd); FUNCEND; return; } } if(bsize<0) { if(!to_block && !conn_reset) { int ern=errno; perror(__FUNCTION__); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: recvfrom error %d\n",__FUNCTION__,ern); } ioa_network_buffer_delete(server->e, server->sm.m.sm.nd.nbh); server->sm.m.sm.nd.nbh = NULL; FUNCEND; return; } if (bsize > 0) { int rc = 0; ioa_network_buffer_set_size(elem, (size_t)bsize); if(server->connect_cb) { rc = create_new_connected_udp_socket(server, s); if(rc<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot handle UDP packet, size %d\n",(int)bsize); } } else { server->sm.m.sm.s = s; rc = handle_udp_packet(server, &(server->sm), server->e, server->ts); } if(rc < 0) { if(eve(server->e->verbose)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot handle UDP event\n"); } } } ioa_network_buffer_delete(server->e, server->sm.m.sm.nd.nbh); server->sm.m.sm.nd.nbh = NULL; if((bsize>0) && (cycle++addr.ss.sa_family, CLIENT_DGRAM_SOCKET_TYPE, CLIENT_DGRAM_SOCKET_PROTOCOL); if (udp_listen_fd < 0) { perror("socket"); return -1; } server->udp_listen_s = create_ioa_socket_from_fd(server->e, udp_listen_fd, NULL, UDP_SOCKET, LISTENER_SOCKET, NULL, &(server->addr)); set_sock_buf_size(udp_listen_fd,UR_SERVER_SOCK_BUF_SIZE); if(sock_bind_to_device(udp_listen_fd, (unsigned char*)server->ifname)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Cannot bind listener socket to device %s\n",server->ifname); } set_raw_socket_ttl_options(udp_listen_fd, server->addr.ss.sa_family); set_raw_socket_tos_options(udp_listen_fd, server->addr.ss.sa_family); { const int max_binding_time = 60; int addr_bind_cycle = 0; retry_addr_bind: if(addr_bind(udp_listen_fd,&server->addr,1,1,UDP_SOCKET)<0) { perror("Cannot bind local socket to addr"); char saddr[129]; addr_to_string(&server->addr,(u08bits*)saddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING,"Cannot bind DTLS/UDP listener socket to addr %s\n",saddr); if(addr_bind_cycle++udp_listen_ev = event_new(server->e->event_base,udp_listen_fd, EV_READ|EV_PERSIST,udp_server_input_handler, server); event_add(server->udp_listen_ev,NULL); } if(report_creation) { if(!turn_params.no_udp && !turn_params.no_dtls) addr_debug_print(server->verbose, &server->addr,"DTLS/UDP listener opened on"); else if(!turn_params.no_dtls) addr_debug_print(server->verbose, &server->addr,"DTLS listener opened on"); else if(!turn_params.no_udp) addr_debug_print(server->verbose, &server->addr,"UDP listener opened on"); } FUNCEND; return 0; } static int reopen_server_socket(dtls_listener_relay_server_type* server, evutil_socket_t fd) { UNUSED_ARG(fd); if(!server) return 0; FUNCSTART; { EVENT_DEL(server->udp_listen_ev); if(server->udp_listen_s->fd>=0) { socket_closesocket(server->udp_listen_s->fd); server->udp_listen_s->fd = -1; } if (!(server->udp_listen_s)) { return create_server_socket(server,1); } ioa_socket_raw udp_listen_fd = socket(server->addr.ss.sa_family, CLIENT_DGRAM_SOCKET_TYPE, CLIENT_DGRAM_SOCKET_PROTOCOL); if (udp_listen_fd < 0) { perror("socket"); FUNCEND; return -1; } server->udp_listen_s->fd = udp_listen_fd; /* some UDP sessions may fail due to the race condition here */ set_socket_options(server->udp_listen_s); set_sock_buf_size(udp_listen_fd, UR_SERVER_SOCK_BUF_SIZE); if (sock_bind_to_device(udp_listen_fd, (unsigned char*) server->ifname) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Cannot bind listener socket to device %s\n", server->ifname); } if(addr_bind(udp_listen_fd,&server->addr,1,1,UDP_SOCKET)<0) { perror("Cannot bind local socket to addr"); char saddr[129]; addr_to_string(&server->addr,(u08bits*)saddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Cannot bind listener socket to addr %s\n",saddr); return -1; } server->udp_listen_ev = event_new(server->e->event_base, udp_listen_fd, EV_READ | EV_PERSIST, udp_server_input_handler, server); event_add(server->udp_listen_ev, NULL ); } if (!turn_params.no_udp && !turn_params.no_dtls) addr_debug_print(server->verbose, &server->addr, "DTLS/UDP listener opened on "); else if (!turn_params.no_dtls) addr_debug_print(server->verbose, &server->addr, "DTLS listener opened on "); else if (!turn_params.no_udp) addr_debug_print(server->verbose, &server->addr, "UDP listener opened on "); FUNCEND; return 0; } #if defined(REQUEST_CLIENT_CERT) static int dtls_verify_callback (int ok, X509_STORE_CTX *ctx) { /* This function should ask the user * if he trusts the received certificate. * Here we always trust. */ if(ok && ctx) return 1; return -1; } #endif static int init_server(dtls_listener_relay_server_type* server, const char* ifname, const char *local_address, int port, int verbose, ioa_engine_handle e, turn_turnserver *ts, int report_creation, ioa_engine_new_connection_event_handler send_socket) { if(!server) return -1; #if DTLS_SUPPORTED server->dtls_ctx = e->dtls_ctx; #if DTLSv1_2_SUPPORTED server->dtls_ctx_v1_2 = e->dtls_ctx_v1_2; #endif #endif server->ts = ts; server->connect_cb = send_socket; if(ifname) STRCPY(server->ifname,ifname); if(make_ioa_addr((const u08bits*)local_address, port, &server->addr)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot create a DTLS/UDP listener for address: %s\n",local_address); return -1; } server->slen0 = get_ioa_addr_len(&(server->addr)); server->verbose=verbose; server->e = e; #if DTLS_SUPPORTED if(server->dtls_ctx) { #if defined(REQUEST_CLIENT_CERT) /* If client has to authenticate, then */ SSL_CTX_set_verify(server->dtls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, dtls_verify_callback); #endif SSL_CTX_set_read_ahead(server->dtls_ctx, 1); SSL_CTX_set_cookie_generate_cb(server->dtls_ctx, generate_cookie); SSL_CTX_set_cookie_verify_cb(server->dtls_ctx, verify_cookie); } #if DTLSv1_2_SUPPORTED if(server->dtls_ctx_v1_2) { #if defined(REQUEST_CLIENT_CERT) /* If client has to authenticate, then */ SSL_CTX_set_verify(server->dtls_ctx_v1_2, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, dtls_verify_callback); #endif SSL_CTX_set_read_ahead(server->dtls_ctx_v1_2, 1); SSL_CTX_set_cookie_generate_cb(server->dtls_ctx_v1_2, generate_cookie); SSL_CTX_set_cookie_verify_cb(server->dtls_ctx_v1_2, verify_cookie); } #endif #endif return create_server_socket(server, report_creation); } static int clean_server(dtls_listener_relay_server_type* server) { if(server) { EVENT_DEL(server->udp_listen_ev); close_ioa_socket(server->udp_listen_s); server->udp_listen_s = NULL; } return 0; } /////////////////////////////////////////////////////////// dtls_listener_relay_server_type* create_dtls_listener_server(const char* ifname, const char *local_address, int port, int verbose, ioa_engine_handle e, turn_turnserver *ts, int report_creation, ioa_engine_new_connection_event_handler send_socket) { dtls_listener_relay_server_type* server=(dtls_listener_relay_server_type*) allocate_super_memory_engine(e,sizeof(dtls_listener_relay_server_type)); if(init_server(server, ifname, local_address, port, verbose, e, ts, report_creation, send_socket)<0) { return NULL; } else { return server; } } ioa_engine_handle get_engine(dtls_listener_relay_server_type* server) { if(server) return server->e; return NULL; } //////////// UDP send //////////////// void udp_send_message(dtls_listener_relay_server_type *server, ioa_network_buffer_handle nbh, ioa_addr *dest) { if(server && dest && nbh && (server->udp_listen_s)) udp_send(server->udp_listen_s, dest, (s08bits*)ioa_network_buffer_data(nbh), (int)ioa_network_buffer_get_size(nbh)); } ////////////////////////////////////////////////////////////////// turnserver-4.5.1.1/src/apps/relay/dtls_listener.h0000664000175000017500000000505613436577655020512 0ustar misimisi/* * 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. */ #ifndef __DTLS_LISTENER__ #define __DTLS_LISTENER__ #include "ns_turn_utils.h" #include "ns_ioalib_impl.h" #include "ns_turn_server.h" #include #ifdef __cplusplus extern "C" { #endif /////////////////////////////////////////// struct dtls_listener_relay_server_info; typedef struct dtls_listener_relay_server_info dtls_listener_relay_server_type; /////////////////////////////////////////// dtls_listener_relay_server_type* create_dtls_listener_server(const char* ifname, const char *local_address, int port, int verbose, ioa_engine_handle e, turn_turnserver *ts, int report_creation, ioa_engine_new_connection_event_handler send_socket); void udp_send_message(dtls_listener_relay_server_type *server, ioa_network_buffer_handle nbh, ioa_addr *dest); ioa_engine_handle get_engine(dtls_listener_relay_server_type* server); /////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__DTLS_LISTENER__ turnserver-4.5.1.1/src/apps/relay/turn_admin_server.c0000664000175000017500000035115013436577655021357 0ustar misimisi/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include "libtelnet.h" #include #include #include #include #include #include #include #include #include "userdb.h" #include "mainrelay.h" #include "ns_turn_utils.h" #include "ns_turn_server.h" #include "ns_turn_maps.h" #include "apputils.h" #include "turn_admin_server.h" #include "http_server.h" #include "dbdrivers/dbdriver.h" #include "tls_listener.h" /////////////////////////////// struct admin_server adminserver; int use_cli = 1; ioa_addr cli_addr; int cli_addr_set = 0; int cli_port = CLI_DEFAULT_PORT; char cli_password[CLI_PASSWORD_LENGTH] = ""; int cli_max_output_sessions = DEFAULT_CLI_MAX_OUTPUT_SESSIONS; int use_web_admin = 0; ioa_addr web_admin_addr; int web_admin_addr_set = 0; int web_admin_port = WEB_ADMIN_DEFAULT_PORT; /////////////////////////////// struct cli_session { evutil_socket_t fd; int auth_completed; size_t cmds; struct bufferevent *bev; ioa_addr addr; telnet_t *ts; FILE* f; char realm[STUN_MAX_REALM_SIZE+1]; char origin[STUN_MAX_ORIGIN_SIZE+1]; realm_params_t *rp; }; /////////////////////////////// #define CLI_PASSWORD_TRY_NUMBER (5) static const char *CLI_HELP_STR[] = {"", " ?, h, help - print this text", "", " quit, q, exit, bye - end CLI session", "", " stop, shutdown, halt - shutdown TURN Server", "", " pc - print configuration", "", " sr - set CLI session realm", "", " ur - unset CLI session realm", "", " so - set CLI session origin", "", " uo - unset CLI session origin", "", " tc - toggle a configuration parameter", " (see pc command output for togglable param names)", "", " cc - change a configuration parameter", " (see pc command output for changeable param names)", "", " ps [username] - print sessions, with optional exact user match", "", " psp - print sessions, with partial user string match", "", " psd - dump ps command output into file on the TURN server system", "", " pu [udp|tcp|dtls|tls]- print current users", "", " lr - log reset", "", " aas ip[:port} - add an alternate server reference", " das ip[:port] - delete an alternate server reference", " atas ip[:port] - add a TLS alternate server reference", " dtas ip[:port] - delete a TLS alternate server reference", "", " cs - cancel session, forcefully" "", NULL}; static const char *CLI_GREETING_STR[] = { "TURN Server", TURN_SOFTWARE, NULL}; static char CLI_CURSOR[] = "> "; static const telnet_telopt_t cli_telopts[] = { { TELNET_TELOPT_ECHO, TELNET_WONT, TELNET_DONT }, { TELNET_TELOPT_TTYPE, TELNET_WONT, TELNET_DONT }, { TELNET_TELOPT_COMPRESS2, TELNET_WONT, TELNET_DONT }, { TELNET_TELOPT_ZMP, TELNET_WONT, TELNET_DONT }, { TELNET_TELOPT_MSSP, TELNET_WONT, TELNET_DONT }, { TELNET_TELOPT_BINARY, TELNET_WONT, TELNET_DONT }, { TELNET_TELOPT_NAWS, TELNET_WONT, TELNET_DONT }, { -1, 0, 0 } }; struct toggleable_command { const char *cmd; vintp data; }; struct toggleable_command tcmds[] = { {"stale-nonce",&turn_params.stale_nonce}, {"stun-only",&turn_params.stun_only}, {"no-stun",&turn_params.no_stun}, {"secure-stun",&turn_params.secure_stun}, {"no-udp-relay",&turn_params.no_udp_relay}, {"no-tcp-relay",&turn_params.no_tcp_relay}, {"no-multicast-peers",&turn_params.no_multicast_peers}, {"allow-loopback-peers",&turn_params.allow_loopback_peers}, {"mobility",&turn_params.mobility}, {NULL,NULL} }; /////////////////////////////// static void myprintf(struct cli_session *cs, const char *format, ...) { if(cs && format) { va_list args; va_start (args, format); if(cs->f) { vfprintf(cs->f, format, args); } else { telnet_vprintf(cs->ts, format, args); } va_end (args); } } static void log_reset(struct cli_session* cs) { if(cs) { reset_rtpprintf(); myprintf(cs," log reset done\n"); } } static void print_str_array(struct cli_session* cs, const char** sa) { if(cs && sa) { int i=0; while(sa[i]) { myprintf(cs,"%s\n",sa[i]); i++; } } } static const char* get_flag(int val) { if(val) return "ON"; return "OFF"; } static void cli_print_flag(struct cli_session* cs, int flag, const char* name, int changeable) { if(cs && cs->ts && name) { const char *sc=""; if(changeable) sc=" (*)"; myprintf(cs," %s: %s%s\n",name,get_flag(flag),sc); } } static void cli_print_uint(struct cli_session* cs, unsigned long value, const char* name, int changeable) { if(cs && cs->ts && name) { const char *sc=""; if(changeable==1) sc=" (*)"; else if(changeable==2) sc=" (**)"; myprintf(cs," %s: %lu%s\n",name,value,sc); } } static void cli_print_str(struct cli_session* cs, const char *value, const char* name, int changeable) { if(cs && cs->ts && name && value) { if(value[0] == 0) value="empty"; const char *sc=""; if(changeable==1) sc=" (*)"; else if(changeable==2) sc=" (**)"; myprintf(cs," %s: %s%s\n",name,value,sc); } } static void cli_print_addr(struct cli_session* cs, ioa_addr *value, int use_port, const char* name, int changeable) { if(cs && cs->ts && name && value) { const char *sc=""; if(changeable==1) sc=" (*)"; else if(changeable==2) sc=" (**)"; char s[256]; if(!use_port) addr_to_string_no_port(value,(u08bits*)s); else addr_to_string(value,(u08bits*)s); myprintf(cs," %s: %s%s\n",name,s,sc); } } static void cli_print_addr_list(struct cli_session* cs, turn_server_addrs_list_t *value, int use_port, const char* name, int changeable) { if(cs && cs->ts && name && value && value->size && value->addrs) { const char *sc=""; if(changeable==1) sc=" (*)"; else if(changeable==2) sc=" (**)"; char s[256]; size_t i; for(i=0;isize;i++) { if(!use_port) addr_to_string_no_port(&(value->addrs[i]),(u08bits*)s); else addr_to_string(&(value->addrs[i]),(u08bits*)s); myprintf(cs," %s: %s%s\n",name,s,sc); } } } static void cli_print_str_array(struct cli_session* cs, char **value, size_t sz, const char* name, int changeable) { if(cs && cs->ts && name && value && sz) { const char *sc=""; if(changeable==1) sc=" (*)"; else if(changeable==2) sc=" (**)"; size_t i; for(i=0;its && name && value && value->ranges_number && value->rs) { const char *sc=""; if(changeable==1) sc=" (*)"; else if(changeable==2) sc=" (**)"; size_t i; for(i=0;iranges_number;++i) { if(value->rs[i].realm[0]) { if(cs->realm[0] && strcmp(cs->realm,value->rs[i].realm)) { continue; } else { myprintf(cs," %s: %s (%s)%s\n",name,value->rs[i].str,value->rs[i].realm,sc); } } else { myprintf(cs," %s: %s%s\n",name,value->rs[i].str,sc); } } } } static void toggle_cli_param(struct cli_session* cs, const char* pn) { if(cs && cs->ts && pn) { int i=0; while(tcmds[i].cmd && tcmds[i].data) { if(strcmp(tcmds[i].cmd,pn) == 0) { *(tcmds[i].data) = !(*(tcmds[i].data)); cli_print_flag(cs,*(tcmds[i].data),tcmds[i].cmd,0); return; } ++i; } myprintf(cs, "\n"); myprintf(cs, " Error: unknown or constant parameter: %s.\n",pn); myprintf(cs, " You can toggle only the following parameters:\n"); myprintf(cs, "\n"); i=0; while(tcmds[i].cmd && tcmds[i].data) { cli_print_flag(cs,*(tcmds[i].data),tcmds[i].cmd,0); ++i; } myprintf(cs,"\n"); } } static void change_cli_param(struct cli_session* cs, const char* pn) { if(cs && cs->ts && pn) { if(strstr(pn,"total-quota")==pn) { turn_params.total_quota = atoi(pn+strlen("total-quota")); cli_print_uint(cs,(unsigned long)turn_params.total_quota,"total-quota",2); return; } else if(strstr(pn,"user-quota")==pn) { turn_params.user_quota = atoi(pn+strlen("user-quota")); cli_print_uint(cs,(unsigned long)turn_params.user_quota,"user-quota",2); return; } else if(strstr(pn,"max-bps")==pn) { set_max_bps((band_limit_t)strtoul(pn+strlen("max-bps"),NULL,10)); cli_print_uint(cs,(unsigned long)get_max_bps(),"max-bps",2); return; } else if(strstr(pn,"bps-capacity")==pn) { set_bps_capacity((band_limit_t)strtoul(pn+strlen("bps-capacity"),NULL,10)); cli_print_uint(cs,(unsigned long)get_bps_capacity(),"bps-capacity",2); return; } else if(strstr(pn,"cli-max-output-sessions")==pn) { cli_max_output_sessions = atoi(pn+strlen("cli-max-output-sessions")); cli_print_uint(cs,(unsigned long)cli_max_output_sessions,"cli-max-output-sessions",2); return; } myprintf(cs, "\n"); myprintf(cs, " Error: unknown or constant parameter: %s.\n",pn); myprintf(cs, "\n"); } } struct ps_arg { struct cli_session* cs; size_t counter; turn_time_t ct; const char *username; const char *pname; int exact_match; ur_string_map* users; size_t *user_counters; char **user_names; size_t users_number; }; static int print_session(ur_map_key_type key, ur_map_value_type value, void *arg) { if(key && value && arg) { struct ps_arg *csarg = (struct ps_arg*)arg; struct cli_session* cs = csarg->cs; struct turn_session_info *tsi = (struct turn_session_info *)value; if(cs->realm[0] && strcmp(cs->realm,tsi->realm)) return 0; if(cs->origin[0] && strcmp(cs->origin,tsi->origin)) return 0; if(csarg->users) { const char *pn=csarg->pname; if(pn[0]) { if(!strcmp(pn,"TLS") || !strcmp(pn,"tls") || !strcmp(pn,"Tls")) { if((tsi->client_protocol != TLS_SOCKET)&&(tsi->client_protocol != TLS_SCTP_SOCKET)) return 0; } else if(!strcmp(pn,"DTLS") || !strcmp(pn,"dtls") || !strcmp(pn,"Dtls")) { if(tsi->client_protocol != DTLS_SOCKET) return 0; } else if(!strcmp(pn,"TCP") || !strcmp(pn,"tcp") || !strcmp(pn,"Tcp")) { if((tsi->client_protocol != TCP_SOCKET)&&(tsi->client_protocol != SCTP_SOCKET)) return 0; } else if(!strcmp(pn,"UDP") || !strcmp(pn,"udp") || !strcmp(pn,"Udp")) { if(tsi->client_protocol != UDP_SOCKET) return 0; } else { return 0; } } ur_string_map_value_type value; if(!ur_string_map_get(csarg->users, (ur_string_map_key_type)(char*)tsi->username, &value)) { value = (ur_string_map_value_type)csarg->users_number; csarg->users_number += 1; csarg->user_counters = (size_t*)turn_realloc(csarg->user_counters, (size_t)value * sizeof(size_t), csarg->users_number * sizeof(size_t)); csarg->user_names = (char**)turn_realloc(csarg->user_names, (size_t)value * sizeof(char*), csarg->users_number * sizeof(char*)); csarg->user_names[(size_t)value] = turn_strdup((char*)tsi->username); csarg->user_counters[(size_t)value] = 0; ur_string_map_put(csarg->users, (ur_string_map_key_type)(char*)tsi->username, value); } csarg->user_counters[(size_t)value] += 1; } else { if(csarg->username[0]) { if(csarg->exact_match) { if(strcmp((char*)tsi->username, csarg->username)) return 0; } else { if(!strstr((char*)tsi->username, csarg->username)) return 0; } } if(cs->f || (unsigned long)csarg->counter<(unsigned long)cli_max_output_sessions) { myprintf(cs, "\n"); myprintf(cs," %lu) id=%018llu, user <%s>:\n", (unsigned long)(csarg->counter+1), (unsigned long long)tsi->id, tsi->username); if(tsi->realm[0]) myprintf(cs," realm: %s\n",tsi->realm); if(tsi->origin[0]) myprintf(cs," origin: %s\n",tsi->origin); if(turn_time_before(csarg->ct, tsi->start_time)) { myprintf(cs," started: undefined time\n"); } else { myprintf(cs," started %lu secs ago\n",(unsigned long)(csarg->ct - tsi->start_time)); } if(turn_time_before(tsi->expiration_time,csarg->ct)) { myprintf(cs," expired\n"); } else { myprintf(cs," expiring in %lu secs\n",(unsigned long)(tsi->expiration_time - csarg->ct)); } myprintf(cs," client protocol %s, relay protocol %s\n",socket_type_name(tsi->client_protocol),socket_type_name(tsi->peer_protocol)); { if(!tsi->local_addr_data.saddr[0]) addr_to_string(&(tsi->local_addr_data.addr),(u08bits*)tsi->local_addr_data.saddr); if(!tsi->remote_addr_data.saddr[0]) addr_to_string(&(tsi->remote_addr_data.addr),(u08bits*)tsi->remote_addr_data.saddr); if(!tsi->relay_addr_data_ipv4.saddr[0]) addr_to_string(&(tsi->relay_addr_data_ipv4.addr),(u08bits*)tsi->relay_addr_data_ipv4.saddr); if(!tsi->relay_addr_data_ipv6.saddr[0]) addr_to_string(&(tsi->relay_addr_data_ipv6.addr),(u08bits*)tsi->relay_addr_data_ipv6.saddr); myprintf(cs," client addr %s, server addr %s\n", tsi->remote_addr_data.saddr, tsi->local_addr_data.saddr); if(tsi->relay_addr_data_ipv4.saddr[0]) { myprintf(cs," relay addr %s\n", tsi->relay_addr_data_ipv4.saddr); } if(tsi->relay_addr_data_ipv6.saddr[0]) { myprintf(cs," relay addr %s\n", tsi->relay_addr_data_ipv6.saddr); } } myprintf(cs," fingerprints enforced: %s\n",get_flag(tsi->enforce_fingerprints)); myprintf(cs," mobile: %s\n",get_flag(tsi->is_mobile)); if(tsi->tls_method[0]) { myprintf(cs," TLS method: %s\n",tsi->tls_method); myprintf(cs," TLS cipher: %s\n",tsi->tls_cipher); } if(tsi->bps) myprintf(cs," Max throughput: %lu bytes per second\n",(unsigned long)tsi->bps); myprintf(cs," usage: rp=%lu, rb=%lu, sp=%lu, sb=%lu\n",(unsigned long)(tsi->received_packets), (unsigned long)(tsi->received_bytes),(unsigned long)(tsi->sent_packets),(unsigned long)(tsi->sent_bytes)); myprintf(cs," rate: r=%lu, s=%lu, total=%lu (bytes per sec)\n",(unsigned long)(tsi->received_rate), (unsigned long)(tsi->sent_rate),(unsigned long)(tsi->total_rate)); if(tsi->main_peers_size) { myprintf(cs," peers:\n"); size_t i; for(i=0;imain_peers_size;++i) { if(!(tsi->main_peers_data[i].saddr[0])) addr_to_string(&(tsi->main_peers_data[i].addr),(u08bits*)tsi->main_peers_data[i].saddr); myprintf(cs," %s\n",tsi->main_peers_data[i].saddr); } if(tsi->extra_peers_size && tsi->extra_peers_data) { for(i=0;iextra_peers_size;++i) { if(!(tsi->extra_peers_data[i].saddr[0])) addr_to_string(&(tsi->extra_peers_data[i].addr),(u08bits*)tsi->extra_peers_data[i].saddr); myprintf(cs," %s\n",tsi->extra_peers_data[i].saddr); } } } } } csarg->counter += 1; } return 0; } static void cancel_session(struct cli_session* cs, const char* ssid) { if(cs && cs->ts && ssid && *ssid) { turnsession_id sid = strtoull(ssid,NULL,10); send_session_cancellation_to_relay(sid); } } static void print_sessions(struct cli_session* cs, const char* pn, int exact_match, int print_users) { if(cs && cs->ts && pn) { while(pn[0] == ' ') ++pn; if(pn[0] == '*') ++pn; const char *uname=""; if(!print_users) { uname = pn; pn = ""; } struct ps_arg arg = {cs,0,0,uname,pn,exact_match,NULL,NULL,NULL,0}; arg.ct = turn_time(); if(print_users) { arg.users = ur_string_map_create(NULL); } ur_map_foreach_arg(adminserver.sessions, (foreachcb_arg_type)print_session, &arg); myprintf(cs,"\n"); if(!print_users && !(cs->f)) { if((unsigned long)arg.counter > (unsigned long)cli_max_output_sessions) { myprintf(cs,"...\n"); myprintf(cs,"\n"); } } else if(arg.user_counters && arg.user_names) { size_t i; for(i=0;i, %lu sessions\n", arg.user_names[i], (unsigned long)arg.user_counters[i]); } } myprintf(cs,"\n"); } { char ts[1025]; snprintf(ts,sizeof(ts)," Total sessions"); if(cs->realm[0]) { snprintf(ts+strlen(ts),sizeof(ts)-strlen(ts)," for realm %s",cs->realm); if(cs->origin[0]) snprintf(ts+strlen(ts),sizeof(ts)-strlen(ts)," and for origin %s",cs->origin); } else { if(cs->origin[0]) snprintf(ts+strlen(ts),sizeof(ts)-strlen(ts)," for origin %s",cs->origin); } snprintf(ts+strlen(ts),sizeof(ts)-strlen(ts),": %lu", (unsigned long)arg.counter); myprintf(cs,"%s\n", ts); myprintf(cs,"\n"); } if(!print_users && !(cs->f)) { if((unsigned long)arg.counter > (unsigned long)cli_max_output_sessions) { myprintf(cs," Warning: too many output sessions, more than the\n"); myprintf(cs," current value of cli-max-output-sessions CLI parameter.\n"); myprintf(cs," Refine your request or increase cli-max-output-sessions value.\n"); myprintf(cs,"\n"); } } if(arg.user_counters) turn_free(arg.user_counters,sizeof(size_t)*arg.users_number); if(arg.user_names) { size_t i; for(i=0;ioptions.name; if(rn[0]) cli_print_str(cs,rn,"Default realm",0); } if(cs->realm[0]) cli_print_str(cs,cs->realm,"CLI session realm",0); else cli_print_str(cs,get_realm(NULL)->options.name,"CLI session realm",0); if(cs->origin[0]) cli_print_str(cs,cs->origin,"CLI session origin",0); if(turn_params.ct == TURN_CREDENTIALS_LONG_TERM) cli_print_flag(cs,1,"Long-term authorization mechanism",0); else cli_print_flag(cs,1,"Anonymous credentials",0); cli_print_flag(cs,turn_params.use_auth_secret_with_timestamp,"TURN REST API support",0); if(turn_params.use_auth_secret_with_timestamp && turn_params.rest_api_separator) cli_print_uint(cs,turn_params.rest_api_separator,"TURN REST API separator ASCII number",0); myprintf(cs,"\n"); cli_print_uint(cs,(unsigned long)cs->rp->status.total_current_allocs,"total-current-allocs",0); myprintf(cs,"\n"); cli_print_uint(cs,(unsigned long)turn_params.total_quota,"Default total-quota",2); cli_print_uint(cs,(unsigned long)turn_params.user_quota,"Default user-quota",2); cli_print_uint(cs,(unsigned long)get_bps_capacity(),"Total server bps-capacity",2); cli_print_uint(cs,(unsigned long)get_bps_capacity_allocated(),"Allocated bps-capacity",0); cli_print_uint(cs,(unsigned long)get_max_bps(),"Default max-bps",2); myprintf(cs,"\n"); cli_print_uint(cs,(unsigned long)cs->rp->options.perf_options.total_quota,"current realm total-quota",0); cli_print_uint(cs,(unsigned long)cs->rp->options.perf_options.user_quota,"current realm user-quota",0); cli_print_uint(cs,(unsigned long)cs->rp->options.perf_options.max_bps,"current realm max-bps",0); myprintf(cs,"\n"); cli_print_uint(cs,(unsigned long)cli_max_output_sessions,"cli-max-output-sessions",2); { myprintf(cs,"\n"); const char *str=" (Note 1: parameters with (*) are toggleable)"; myprintf(cs,"%s\n",str); myprintf(cs,"\n"); str=" (Note 2: parameters with (**) are changeable)"; myprintf(cs,"%s\n",str); myprintf(cs,"\n"); } } } static void close_cli_session(struct cli_session* cs); static int run_cli_output(struct cli_session* cs, const char *buf, unsigned int len) { if(cs && buf && len) { if(bufferevent_write(cs->bev, buf, len)< 0) { return -1; } return 0; } return -1; } static void close_cli_session(struct cli_session* cs) { if(cs) { addr_debug_print(adminserver.verbose, &(cs->addr),"CLI session disconnected from"); if(cs->ts) { telnet_free(cs->ts); cs->ts = NULL; } BUFFEREVENT_FREE(cs->bev); if(cs->fd>=0) { close(cs->fd); cs->fd = -1; } turn_free(cs,sizeof(struct cli_session)); } } static void type_cli_cursor(struct cli_session* cs) { if(cs && (cs->bev)) { myprintf(cs, "%s", CLI_CURSOR); } } static void cli_add_alternate_server(struct cli_session* cs, const char* pn) { if(cs && cs->ts && pn && *pn) { add_alternate_server(pn); } } static void cli_add_tls_alternate_server(struct cli_session* cs, const char* pn) { if(cs && cs->ts && pn && *pn) { add_tls_alternate_server(pn); } } static void cli_del_alternate_server(struct cli_session* cs, const char* pn) { if(cs && cs->ts && pn && *pn) { del_alternate_server(pn); } } static void cli_del_tls_alternate_server(struct cli_session* cs, const char* pn) { if(cs && cs->ts && pn && *pn) { del_tls_alternate_server(pn); } } static int run_cli_input(struct cli_session* cs, const char *buf0, unsigned int len) { int ret = 0; if(cs && buf0 && cs->ts && cs->bev) { char *buf = (char*)turn_malloc(len+1); ns_bcopy(buf0,buf,len); buf[len]=0; char *cmd = buf; while((cmd[0]==' ') || (cmd[0]=='\t')) ++cmd; size_t sl = strlen(cmd); while(sl) { char c = cmd[sl-1]; if((c==10)||(c==13)) { cmd[sl-1]=0; --sl; } else { break; } } if(sl) { cs->cmds += 1; if(cli_password[0] && !(cs->auth_completed)) { if(check_password(cmd,cli_password)) { if(cs->cmds>=CLI_PASSWORD_TRY_NUMBER) { addr_debug_print(1, &(cs->addr),"CLI authentication error"); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"CLI authentication error\n"); close_cli_session(cs); } else { const char* ipwd="Enter password: "; myprintf(cs,"%s\n",ipwd); } } else { cs->auth_completed = 1; addr_debug_print(1, &(cs->addr),"CLI authentication success"); type_cli_cursor(cs); } } else if((strcmp(cmd,"bye") == 0)||(strcmp(cmd,"quit") == 0)||(strcmp(cmd,"exit") == 0)||(strcmp(cmd,"q") == 0)) { const char* str="Bye !"; myprintf(cs,"%s\n",str); close_cli_session(cs); ret = -1; } else if((strcmp(cmd,"halt") == 0)||(strcmp(cmd,"shutdown") == 0)||(strcmp(cmd,"stop") == 0)) { addr_debug_print(1, &(cs->addr),"Shutdown command received from CLI user"); const char* str="TURN server is shutting down"; myprintf(cs,"%s\n",str); close_cli_session(cs); turn_params.stop_turn_server = 1; sleep(10); exit(0); } else if((strcmp(cmd,"?") == 0)||(strcmp(cmd,"h") == 0)||(strcmp(cmd,"help") == 0)) { print_str_array(cs, CLI_GREETING_STR); print_str_array(cs, CLI_HELP_STR); type_cli_cursor(cs); } else if(strcmp(cmd,"pc")==0) { cli_print_configuration(cs); type_cli_cursor(cs); } else if(strstr(cmd,"tc ") == cmd) { toggle_cli_param(cs,cmd+3); } else if(strstr(cmd,"sr ") == cmd) { STRCPY(cs->realm,cmd+3); cs->rp = get_realm(cs->realm); type_cli_cursor(cs); } else if(strcmp(cmd,"ur") == 0) { cs->realm[0]=0; cs->rp = get_realm(NULL); type_cli_cursor(cs); } else if(strstr(cmd,"so ") == cmd) { STRCPY(cs->origin,cmd+3); type_cli_cursor(cs); } else if(strcmp(cmd,"uo") == 0) { cs->origin[0]=0; type_cli_cursor(cs); } else if(strstr(cmd,"tc") == cmd) { toggle_cli_param(cs,cmd+2); type_cli_cursor(cs); } else if(strstr(cmd,"psp") == cmd) { print_sessions(cs,cmd+3,0,0); type_cli_cursor(cs); } else if(strstr(cmd,"psd") == cmd) { cmd += 3; while(cmd[0]==' ') ++cmd; if(!(cmd[0])) { const char* str="You have to provide file name for ps dump\n"; myprintf(cs,"%s\n",str); } else { cs->f = fopen(cmd,"w"); if(!(cs->f)) { const char* str="Cannot open file for writing\n"; myprintf(cs,"%s\n",str); } else { print_sessions(cs,"",1,0); fclose(cs->f); cs->f = NULL; } } type_cli_cursor(cs); } else if(strstr(cmd,"pu ") == cmd) { print_sessions(cs,cmd+3,0,1); type_cli_cursor(cs); } else if(!strcmp(cmd,"pu")) { print_sessions(cs,cmd+2,0,1); type_cli_cursor(cs); } else if(strstr(cmd,"ps") == cmd) { print_sessions(cs,cmd+2,1,0); type_cli_cursor(cs); } else if(strstr(cmd,"cs ") == cmd) { cancel_session(cs,cmd+3); type_cli_cursor(cs); } else if(strstr(cmd,"lr") == cmd) { log_reset(cs); type_cli_cursor(cs); } else if(strstr(cmd,"cc ") == cmd) { change_cli_param(cs,cmd+3); type_cli_cursor(cs); } else if(strstr(cmd,"cc") == cmd) { change_cli_param(cs,cmd+2); type_cli_cursor(cs); } else if(strstr(cmd,"aas ") == cmd) { cli_add_alternate_server(cs,cmd+4); type_cli_cursor(cs); } else if(strstr(cmd,"atas ") == cmd) { cli_add_tls_alternate_server(cs,cmd+5); type_cli_cursor(cs); } else if(strstr(cmd,"das ") == cmd) { cli_del_alternate_server(cs,cmd+4); type_cli_cursor(cs); } else if(strstr(cmd,"dtas ") == cmd) { cli_del_tls_alternate_server(cs,cmd+5); type_cli_cursor(cs); } else { const char* str="Unknown command\n"; myprintf(cs,"%s\n",str); type_cli_cursor(cs); } } else { type_cli_cursor(cs); } turn_free(buf,len+1); } return ret; } static void cli_socket_input_handler_bev(struct bufferevent *bev, void* arg) { if (bev && arg) { struct cli_session* cs = (struct cli_session*) arg; if(!(cs->ts)) return; stun_buffer buf; if(cs->bev) { int len = (int)bufferevent_read(cs->bev, buf.buf, STUN_BUFFER_SIZE-1); if(len < 0) { close_cli_session(cs); return; } else if(len == 0) { return; } buf.len = len; buf.offset = 0; buf.buf[len]=0; telnet_recv(cs->ts, (const char *)buf.buf, (unsigned int)(buf.len)); } } } static void cli_eventcb_bev(struct bufferevent *bev, short events, void *arg) { UNUSED_ARG(bev); if (events & BEV_EVENT_CONNECTED) { // Connect okay } else if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF)) { if (arg) { struct cli_session* cs = (struct cli_session*) arg; close_cli_session(cs); } } } static void cli_telnet_event_handler(telnet_t *telnet, telnet_event_t *event, void *user_data) { if (user_data && telnet) { struct cli_session *cs = (struct cli_session *) user_data; switch (event->type){ case TELNET_EV_DATA: run_cli_input(cs, event->data.buffer, event->data.size); break; case TELNET_EV_SEND: run_cli_output(cs, event->data.buffer, event->data.size); break; case TELNET_EV_ERROR: TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TELNET error: %s", event->error.msg); break; default: ; }; } } static void cliserver_input_handler(struct evconnlistener *l, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *arg) { UNUSED_ARG(l); UNUSED_ARG(arg); UNUSED_ARG(socklen); addr_debug_print(adminserver.verbose, (ioa_addr*)sa,"CLI connected to"); struct cli_session *clisession = (struct cli_session*)turn_malloc(sizeof(struct cli_session)); ns_bzero(clisession,sizeof(struct cli_session)); clisession->rp = get_realm(NULL); set_socket_options_fd(fd, TCP_SOCKET, sa->sa_family); clisession->fd = fd; addr_cpy(&(clisession->addr),(ioa_addr*)sa); clisession->bev = bufferevent_socket_new(adminserver.event_base, fd, TURN_BUFFEREVENTS_OPTIONS); debug_ptr_add(clisession->bev); bufferevent_setcb(clisession->bev, cli_socket_input_handler_bev, NULL, cli_eventcb_bev, clisession); bufferevent_setwatermark(clisession->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK); bufferevent_enable(clisession->bev, EV_READ); /* Start reading. */ clisession->ts = telnet_init(cli_telopts, cli_telnet_event_handler, 0, clisession); if(!(clisession->ts)) { const char *str = "Cannot open telnet session\n"; addr_debug_print(adminserver.verbose, (ioa_addr*)sa,str); close_cli_session(clisession); } else { print_str_array(clisession, CLI_GREETING_STR); telnet_printf(clisession->ts,"\n"); telnet_printf(clisession->ts,"Type '?' for help\n"); if(cli_password[0]) { const char* ipwd="Enter password: "; telnet_printf(clisession->ts,"%s\n",ipwd); } else { type_cli_cursor(clisession); } } } static void web_admin_input_handler(ioa_socket_handle s, int event_type, ioa_net_data *in_buffer, void *arg, int can_resume) { UNUSED_ARG(event_type); UNUSED_ARG(can_resume); UNUSED_ARG(arg); int to_be_closed = 0; int buffer_size = (int)ioa_network_buffer_get_size(in_buffer->nbh); if (buffer_size > 0) { SOCKET_TYPE st = get_ioa_socket_type(s); if(is_stream_socket(st)) { if(is_http((char*)ioa_network_buffer_data(in_buffer->nbh), buffer_size)) { const char *proto = "HTTP"; ioa_network_buffer_data(in_buffer->nbh)[buffer_size] = 0; if(st == TLS_SOCKET) { proto = "HTTPS"; set_ioa_socket_app_type(s, HTTPS_CLIENT_SOCKET); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: %s (%s %s) request: %s\n", __FUNCTION__, proto, get_ioa_socket_cipher(s), get_ioa_socket_ssl_method(s), (char*)ioa_network_buffer_data(in_buffer->nbh)); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s socket to be detached: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, get_ioa_socket_type(s), get_ioa_socket_app_type(s)); ioa_socket_handle new_s = detach_ioa_socket(s); if(new_s) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s new detached socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)new_s, get_ioa_socket_type(new_s), get_ioa_socket_app_type(new_s)); send_https_socket(new_s); } to_be_closed = 1; } else { set_ioa_socket_app_type(s, HTTP_CLIENT_SOCKET); if(adminserver.verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: %s request: %s\n", __FUNCTION__, proto, (char*)ioa_network_buffer_data(in_buffer->nbh)); } handle_http_echo(s); } } } } if (to_be_closed) { if(adminserver.verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: web-admin socket to be closed in client handler: s=0x%lx\n", __FUNCTION__, (long)s); } set_ioa_socket_tobeclosed(s); } } static int send_socket_to_admin_server(ioa_engine_handle e, struct message_to_relay *sm) { // sm->relay_server is null for us. sm->t = RMT_SOCKET; if (sm->m.sm.s->defer_nbh) { if (!sm->m.sm.nd.nbh) { sm->m.sm.nd.nbh = sm->m.sm.s->defer_nbh; sm->m.sm.s->defer_nbh = NULL; } else { ioa_network_buffer_delete(e, sm->m.sm.s->defer_nbh); sm->m.sm.s->defer_nbh = NULL; } } ioa_socket_handle s = sm->m.sm.s; if (!s) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: web-admin socket EMPTY\n", __FUNCTION__); } else if (s->read_event || s->bev) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: web-admin socket wrongly preset: 0x%lx : 0x%lx\n", __FUNCTION__, (long) s->read_event, (long) s->bev); IOA_CLOSE_SOCKET(s); sm->m.sm.s = NULL; } else { s->e = e; struct socket_message *msg = &(sm->m.sm); if(register_callback_on_ioa_socket(e, msg->s, IOA_EV_READ, web_admin_input_handler, NULL, 0) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: Failed to register callback on web-admin ioa socket\n", __FUNCTION__); IOA_CLOSE_SOCKET(s); sm->m.sm.s = NULL; } else { if(msg->nd.nbh) { web_admin_input_handler(msg->s, IOA_EV_READ, &(msg->nd), NULL, msg->can_resume); ioa_network_buffer_delete(e, msg->nd.nbh); msg->nd.nbh = NULL; } } } ioa_network_buffer_delete(e, sm->m.sm.nd.nbh); sm->m.sm.nd.nbh = NULL; return 0; } void setup_admin_thread(void) { adminserver.event_base = turn_event_base_new(); super_memory_t* sm = new_super_memory_region(); adminserver.e = create_ioa_engine(sm, adminserver.event_base, turn_params.listener.tp, turn_params.relay_ifname, turn_params.relays_number, turn_params.relay_addrs, turn_params.default_relays, turn_params.verbose #if !defined(TURN_NO_HIREDIS) ,turn_params.redis_statsdb #endif ); if(use_web_admin) { // Support encryption on this ioa engine // because the web-admin needs HTTPS set_ssl_ctx(adminserver.e, &turn_params); } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"IO method (admin thread): %s\n",event_base_get_method(adminserver.event_base)); { struct bufferevent *pair[2]; bufferevent_pair_new(adminserver.event_base, TURN_BUFFEREVENTS_OPTIONS, pair); adminserver.in_buf = pair[0]; adminserver.out_buf = pair[1]; bufferevent_setcb(adminserver.in_buf, admin_server_receive_message, NULL, NULL, &adminserver); bufferevent_enable(adminserver.in_buf, EV_READ); } { struct bufferevent *pair[2]; bufferevent_pair_new(adminserver.event_base, TURN_BUFFEREVENTS_OPTIONS, pair); adminserver.https_in_buf = pair[0]; adminserver.https_out_buf = pair[1]; bufferevent_setcb(adminserver.https_in_buf, https_admin_server_receive_message, NULL, NULL, &adminserver); bufferevent_enable(adminserver.https_in_buf, EV_READ); } // Setup the web-admin server if(use_web_admin) { if(!web_admin_addr_set) { if(make_ioa_addr((const u08bits*)WEB_ADMIN_DEFAULT_IP, 0, &web_admin_addr) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot set web-admin address %s\n", WEB_ADMIN_DEFAULT_IP); return; } } addr_set_port(&web_admin_addr, web_admin_port); char saddr[129]; addr_to_string_no_port(&web_admin_addr,(u08bits*)saddr); tls_listener_relay_server_type *tls_service = create_tls_listener_server(turn_params.listener_ifname, saddr, web_admin_port, turn_params.verbose, adminserver.e, send_socket_to_admin_server, NULL); if (tls_service == NULL) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot create web-admin listener\n"); return; } addr_debug_print(adminserver.verbose, &web_admin_addr, "web-admin listener opened on "); } if(use_cli) { if(!cli_addr_set) { if(make_ioa_addr((const u08bits*)CLI_DEFAULT_IP,0,&cli_addr)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot set cli address %s\n",CLI_DEFAULT_IP); return; } } addr_set_port(&cli_addr,cli_port); adminserver.listen_fd = socket(cli_addr.ss.sa_family, ADMIN_STREAM_SOCKET_TYPE, ADMIN_STREAM_SOCKET_PROTOCOL); if (adminserver.listen_fd < 0) { perror("socket"); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot open CLI socket\n"); return; } if(addr_bind(adminserver.listen_fd,&cli_addr,1,1,TCP_SOCKET)<0) { perror("Cannot bind CLI socket to addr"); char saddr[129]; addr_to_string(&cli_addr,(u08bits*)saddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot bind CLI listener socket to addr %s\n",saddr); socket_closesocket(adminserver.listen_fd); return; } socket_tcp_set_keepalive(adminserver.listen_fd,TCP_SOCKET); socket_set_nonblocking(adminserver.listen_fd); adminserver.l = evconnlistener_new(adminserver.event_base, cliserver_input_handler, &adminserver, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 1024, adminserver.listen_fd); if(!(adminserver.l)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot create CLI listener\n"); socket_closesocket(adminserver.listen_fd); return; } addr_debug_print(adminserver.verbose, &cli_addr,"CLI listener opened on "); } adminserver.sessions = ur_map_create(); } void admin_server_receive_message(struct bufferevent *bev, void *ptr) { UNUSED_ARG(ptr); struct turn_session_info *tsi = (struct turn_session_info*)turn_malloc(sizeof(struct turn_session_info)); turn_session_info_init(tsi); int n = 0; struct evbuffer *input = bufferevent_get_input(bev); while ((n = evbuffer_remove(input, tsi, sizeof(struct turn_session_info))) > 0) { if (n != sizeof(struct turn_session_info)) { fprintf(stderr,"%s: Weird CLI buffer error: size=%d\n",__FUNCTION__,n); continue; } ur_map_value_type t = 0; if (ur_map_get(adminserver.sessions, (ur_map_key_type)tsi->id, &t) && t) { struct turn_session_info *old = (struct turn_session_info*)t; turn_session_info_clean(old); turn_free(old,sizeof(struct turn_session_info)); ur_map_del(adminserver.sessions, (ur_map_key_type)tsi->id, NULL); } if(tsi->valid) { ur_map_put(adminserver.sessions, (ur_map_key_type)tsi->id, (ur_map_value_type)tsi); tsi = (struct turn_session_info*)turn_malloc(sizeof(struct turn_session_info)); turn_session_info_init(tsi); } else { turn_session_info_clean(tsi); } } if(tsi) { turn_session_info_clean(tsi); turn_free(tsi,sizeof(struct turn_session_info)); } } int send_turn_session_info(struct turn_session_info* tsi) { int ret = -1; if(tsi) { struct evbuffer *output = bufferevent_get_output(adminserver.out_buf); if(output) { if(evbuffer_add(output,tsi,sizeof(struct turn_session_info))>=0) { ret = 0; } } } return ret; } /////////// HTTPS ///////////// enum _AS_FORM { AS_FORM_LOGON, AS_FORM_LOGOUT, AS_FORM_PC, AS_FORM_HOME, AS_FORM_TOGGLE, AS_FORM_UPDATE, AS_FORM_PS, AS_FORM_USERS, AS_FORM_SS, AS_FORM_OS, AS_FORM_OAUTH, AS_FORM_OAUTH_SHOW_KEYS, AS_FORM_UNKNOWN }; typedef enum _AS_FORM AS_FORM; #define HR_USERNAME "uname" #define HR_PASSWORD "pwd" #define HR_PASSWORD1 "pwd1" #define HR_REALM "realm" #define HR_ADD_USER "add_user" #define HR_ADD_REALM "add_user_realm" #define HR_ADD_SECRET "add_secret" #define HR_ADD_ORIGIN "add_origin" #define HR_CLIENT_PROTOCOL "cprotocol" #define HR_USER_PATTERN "puser" #define HR_MAX_SESSIONS "maxsess" #define HR_CANCEL_SESSION "cs" #define HR_DELETE_USER "du" #define HR_DELETE_REALM "dr" #define HR_DELETE_SECRET "ds" #define HR_DELETE_ORIGIN "do" #define HR_DELETE_IP "dip" #define HR_DELETE_IP_REALM "dipr" #define HR_DELETE_IP_KIND "dipk" #define HR_ADD_IP "aip" #define HR_ADD_IP_REALM "aipr" #define HR_ADD_IP_KIND "aipk" #define HR_UPDATE_PARAMETER "togglepar" #define HR_ADD_OAUTH_KID "oauth_kid" #define HR_ADD_OAUTH_REALM "oauth_realm" #define HR_ADD_OAUTH_TS "oauth_ts" #define HR_ADD_OAUTH_LT "oauth_lt" #define HR_ADD_OAUTH_IKM "oauth_ikm" #define HR_ADD_OAUTH_TEA "oauth_tea" #define HR_DELETE_OAUTH_KID "oauth_kid_del" #define HR_OAUTH_KID "kid" struct form_name { AS_FORM form; const char* name; }; static struct form_name form_names[] = { {AS_FORM_LOGON,"/logon"}, {AS_FORM_LOGOUT,"/logout"}, {AS_FORM_PC,"/pc"}, {AS_FORM_HOME,"/home"}, {AS_FORM_TOGGLE,"/toggle"}, {AS_FORM_UPDATE,"/update"}, {AS_FORM_PS,"/ps"}, {AS_FORM_USERS,"/us"}, {AS_FORM_SS,"/ss"}, {AS_FORM_OS,"/os"}, {AS_FORM_OAUTH,"/oauth"}, {AS_FORM_OAUTH_SHOW_KEYS,"/oauth_show_keys"}, {AS_FORM_UNKNOWN,NULL} }; #define admin_title "TURN Server (https admin connection)" #define __bold_admin_title "TURN Server
https admin connection
\r\n" #define bold_admin_title get_bold_admin_title() static ioa_socket_handle current_socket = NULL; static char *get_bold_admin_title(void) { static char sbat[1025]; STRCPY(sbat,__bold_admin_title); if(current_socket && current_socket->special_session) { struct admin_session* as = (struct admin_session*)current_socket->special_session; if(as->as_ok) { if(as->as_login[0]) { char *dst=sbat+strlen(sbat); snprintf(dst,ADMIN_USER_MAX_LENGTH*2+2," admin user: %s
\r\n",as->as_login); } if(as->as_realm[0]) { char *dst=sbat+strlen(sbat); snprintf(dst,STUN_MAX_REALM_SIZE*2," admin session realm: %s
\r\n",as->as_realm); } else if(as->as_eff_realm[0]) { char *dst=sbat+strlen(sbat); snprintf(dst,STUN_MAX_REALM_SIZE*2," admin session realm: %s
\r\n",as->as_eff_realm); } } } return sbat; } static int wrong_html_name(const char* s) { int ret = 0; if(s) { char* v=evhttp_encode_uri(s); ret = strcmp(v,s); free(v); } return ret; } static int is_as_ok(ioa_socket_handle s) { return (s && s->special_session && ((struct admin_session*)s->special_session)->as_ok); } static int is_superuser(void) { return (is_as_ok(current_socket) && (!((struct admin_session*)current_socket->special_session)->as_realm[0])); } static char* current_realm(void) { if(current_socket && current_socket->special_session && ((struct admin_session*)current_socket->special_session)->as_ok) { return ((struct admin_session*)current_socket->special_session)->as_realm; } else { static char bad_realm[1025] = "_ERROR:UNKNOWN_REALM__"; return bad_realm; } } static char* current_eff_realm(void) { char* r = current_realm(); if(r && r[0]) return r; else if(current_socket && current_socket->special_session && ((struct admin_session*)current_socket->special_session)->as_ok) { return ((struct admin_session*)current_socket->special_session)->as_eff_realm; } else { static char bad_eff_realm[1025] = "_ERROR:UNKNOWN_REALM__"; return bad_eff_realm; } } static size_t current_max_output_sessions(void) { if(current_socket && current_socket->special_session && ((struct admin_session*)current_socket->special_session)->as_ok) { return ((struct admin_session*)current_socket->special_session)->number_of_user_sessions; } return DEFAULT_CLI_MAX_OUTPUT_SESSIONS; } static void set_current_max_output_sessions(size_t value) { if(current_socket && current_socket->special_session && ((struct admin_session*)current_socket->special_session)->as_ok) { ((struct admin_session*)current_socket->special_session)->number_of_user_sessions = value; } } static void https_cancel_session(const char* ssid) { if(ssid && *ssid) { turnsession_id sid = (turnsession_id)strtoull(ssid,NULL,10); send_session_cancellation_to_relay(sid); } } static void https_print_top_page_header(struct str_buffer *sb) { str_buffer_append(sb,"\r\n\r\n \r\n "); str_buffer_append(sb,admin_title); str_buffer_append(sb,"\r\n \r\n \r\n "); str_buffer_append(sb,bold_admin_title); } static void https_print_page_header(struct str_buffer *sb) { https_print_top_page_header(sb); str_buffer_append(sb,"
home page
\r\n
logout
\r\n"); str_buffer_append(sb,"
\r\n"); } static void https_finish_page(struct str_buffer *sb, ioa_socket_handle s, int cclose) { str_buffer_append(sb,"\r\n\r\n"); send_str_from_ioa_socket_tcp(s,"HTTP/1.1 200 OK\r\nServer: "); if(!turn_params.prod) { send_str_from_ioa_socket_tcp(s,TURN_SOFTWARE); } send_str_from_ioa_socket_tcp(s,"\r\n"); send_str_from_ioa_socket_tcp(s,get_http_date_header()); if(cclose) { send_str_from_ioa_socket_tcp(s,"Connection: close\r\n"); } send_str_from_ioa_socket_tcp(s,"Content-Type: text/html; charset=UTF-8\r\nContent-Length: "); send_ulong_from_ioa_socket_tcp(s,str_buffer_get_str_len(sb)); send_str_from_ioa_socket_tcp(s,"\r\n\r\n"); send_str_from_ioa_socket_tcp(s,str_buffer_get_str(sb)); str_buffer_free(sb); } static AS_FORM get_form(const char* path) { if(path) { size_t i = 0; while(form_names[i].name) { if(!strcmp(form_names[i].name,path)) return form_names[i].form; ++i; } } return AS_FORM_UNKNOWN; } static void write_https_logon_page(ioa_socket_handle s) { if(s && !ioa_socket_tobeclosed(s)) { struct str_buffer* sb = str_buffer_new(); https_print_top_page_header(sb); int we_have_admin_users = 0; const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->list_admin_users) { int ausers = dbd->list_admin_users(1); if(ausers>0) { we_have_admin_users = 1; } } if(!we_have_admin_users) { str_buffer_append(sb,"
To use the HTTPS admin connection, you have to set the database table admin_user with the admin user accounts.
\r\n"); } else { str_buffer_append(sb,"

\r\n"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
Admin user information: user name:

password:


\r\n"); str_buffer_append(sb,"
\r\n"); } https_finish_page(sb,s,!we_have_admin_users); } } static void write_https_home_page(ioa_socket_handle s) { if(s && !ioa_socket_tobeclosed(s)) { if(!is_as_ok(s)) { write_https_logon_page(s); } else { struct str_buffer* sb = str_buffer_new(); https_print_page_header(sb); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
Actions:\r\n"); str_buffer_append(sb," Realm name: "); } else { str_buffer_append(sb,"> "); } str_buffer_append(sb,"
"); str_buffer_append(sb,"
Configuration Parameters"); str_buffer_append(sb,"
TURN Sessions"); str_buffer_append(sb,"
Users"); str_buffer_append(sb,"
Shared Secrets (for TURN REST API)"); str_buffer_append(sb,"
Origins"); if(is_superuser()) { if(ENC_ALG_NUM>0) { str_buffer_append(sb,"
oAuth keys"); } } str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
\r\n"); https_finish_page(sb,s,0); } } } static void sbprintf(struct str_buffer *sb, const char *format, ...) { if(sb && format) { va_list args; va_start (args, format); char s[1025]="\0"; vsnprintf(s,sizeof(s)-1,format, args); str_buffer_append(sb,s); va_end (args); } } static void https_print_flag(struct str_buffer* sb, int flag, const char* name, const char* param_name) { if(sb && name) { if(!is_superuser()) param_name = 0; if(!param_name) { sbprintf(sb,"%s%s\r\n",name,get_flag(flag)); } else { sbprintf(sb,"%s%s\r\n",name,HR_UPDATE_PARAMETER,param_name,get_flag(flag)); } } } static void https_print_uint(struct str_buffer* sb, unsigned long value, const char* name, const char* param_name) { if(sb && name) { if(!is_superuser()) param_name = 0; if(!param_name) { if(value) { sbprintf(sb,"%s%lu\r\n",name,value); } else { sbprintf(sb,"%s \r\n",name); } } else { if(value) { sbprintf(sb,"%s
\r\n",name,form_names[AS_FORM_UPDATE].name,HR_UPDATE_PARAMETER,param_name,param_name,value); } else { sbprintf(sb,"%s
\r\n",name,form_names[AS_FORM_UPDATE].name,HR_UPDATE_PARAMETER,param_name,param_name); } } } } static void https_print_str(struct str_buffer* sb, const char *value, const char* name, const char* param_name) { if(sb && name && value) { if(!is_superuser()) param_name = 0; if(!param_name) { sbprintf(sb,"%s%s\r\n",name,value); } else { sbprintf(sb,"%s
\r\n",name,form_names[AS_FORM_UPDATE].name,HR_UPDATE_PARAMETER,param_name,param_name,value); } } } static void https_print_str_array(struct str_buffer* sb, char **value, size_t sz, const char* name) { if(sb && name && value && sz) { size_t i; for(i=0;i %s %s\r\n",name,value[i]); } } } } static void https_print_addr(struct str_buffer* sb, ioa_addr *value, int use_port, const char* name) { if(sb && name && value) { char s[256]; if(!use_port) addr_to_string_no_port(value,(u08bits*)s); else addr_to_string(value,(u08bits*)s); sbprintf(sb," %s %s\r\n",name,s); } } static size_t https_print_addr_list(struct str_buffer* sb, turn_server_addrs_list_t *value, int use_port, const char* name) { if(sb && name && value && value->size && value->addrs) { char s[256]; size_t i; for(i=0;isize;i++) { if(!use_port) addr_to_string_no_port(&(value->addrs[i]),(u08bits*)s); else addr_to_string(&(value->addrs[i]),(u08bits*)s); sbprintf(sb," %s %s\r\n",name,s); } return i; } return 0; } static const char* change_ip_addr_html(int dynamic,const char* kind,const char* ip,const char *realm, char *buffer, size_t sz) { if(!buffer || !sz) { return ""; } else { buffer[0]=0; if(dynamic && kind && ip) { if(!realm) realm=""; if(current_realm()[0] && strcmp(current_realm(),realm)) { //delete forbidden } else { char *eip = evhttp_encode_uri(ip); snprintf(buffer,sz-1,"delete",form_names[AS_FORM_UPDATE].name,HR_DELETE_IP_KIND,kind,HR_DELETE_IP_REALM,realm,HR_DELETE_IP,eip); free(eip); } } return buffer; } } static void https_print_ip_range_list(struct str_buffer* sb, ip_range_list_t *value, const char* name, const char* kind, int dynamic) { if(sb && name) { if(value && value->rs) { size_t i; char buffer[1025]; for(i=0;iranges_number;++i) { if(value->rs[i].realm[0]) { if(current_eff_realm()[0] && strcmp(current_eff_realm(),value->rs[i].realm)) { continue; } else { sbprintf(sb," %s %s [%s] %s\r\n",name,value->rs[i].str,value->rs[i].realm, change_ip_addr_html(dynamic,kind,value->rs[i].str,value->rs[i].realm,buffer,sizeof(buffer))); } } else { sbprintf(sb," %s %s %s\r\n",name,value->rs[i].str, change_ip_addr_html(dynamic,kind,value->rs[i].str,value->rs[i].realm,buffer,sizeof(buffer))); } } } if(dynamic) { sbprintf(sb," Add %s",name); sbprintf(sb,"
IP range:",form_names[AS_FORM_UPDATE].name,HR_ADD_IP_KIND,kind,HR_ADD_IP); sbprintf(sb,"Realm: "); sbprintf(sb,"
\r\n"); } } } static void toggle_param(const char* pn) { if(is_superuser()) { if(pn) { int i=0; while(tcmds[i].cmd && tcmds[i].data) { if(strcmp(tcmds[i].cmd,pn) == 0) { *(tcmds[i].data) = !(*(tcmds[i].data)); return; } ++i; } } } } static void update_param(const char* pn, const char *value) { if(pn) { if(!value) value = "0"; if(is_superuser()) { if(strstr(pn,"total-quota")==pn) { turn_params.total_quota = atoi(value); } else if(strstr(pn,"user-quota")==pn) { turn_params.user_quota = atoi(value); } else if(strstr(pn,"max-bps")==pn) { set_max_bps((band_limit_t)strtoul(value,NULL,10)); } else if(strstr(pn,"bps-capacity")==pn) { set_bps_capacity((band_limit_t)strtoul(value,NULL,10)); } } { realm_params_t *rp = get_realm(current_eff_realm()); if(!rp) rp = get_realm(NULL); const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->set_realm_option_one) { if(strstr(pn,"cr-total-quota")==pn) { rp->options.perf_options.total_quota = atoi(value); dbd->set_realm_option_one((u08bits*)rp->options.name,rp->options.perf_options.total_quota,"total-quota"); } else if(strstr(pn,"cr-user-quota")==pn) { rp->options.perf_options.user_quota = atoi(value); dbd->set_realm_option_one((u08bits*)rp->options.name,rp->options.perf_options.user_quota,"user-quota"); } else if(strstr(pn,"cr-max-bps")==pn) { rp->options.perf_options.max_bps = (band_limit_t)strtoul(value,NULL,10); dbd->set_realm_option_one((u08bits*)rp->options.name,rp->options.perf_options.max_bps,"max-bps"); } } } } } static void https_print_empty_row(struct str_buffer* sb, size_t span) { str_buffer_append(sb,"
"); } static void write_pc_page(ioa_socket_handle s) { if(s && !ioa_socket_tobeclosed(s)) { if(!is_as_ok(s)) { write_https_logon_page(s); } else { struct str_buffer* sb = str_buffer_new(); https_print_page_header(sb); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"Configuration Parameters:

\r\n"); str_buffer_append(sb,"\r\n"); { https_print_flag(sb,turn_params.verbose,"verbose",0); https_print_flag(sb,turn_params.turn_daemon,"daemon process",0); https_print_flag(sb,turn_params.stale_nonce,"stale-nonce","stale-nonce"); https_print_flag(sb,turn_params.stun_only,"stun-only","stun-only"); https_print_flag(sb,turn_params.no_stun,"no-stun","no-stun"); https_print_flag(sb,turn_params.secure_stun,"secure-stun","secure-stun"); https_print_flag(sb,turn_params.do_not_use_config_file,"do-not-use-config-file",0); https_print_flag(sb,turn_params.rfc5780,"RFC5780 support",0); https_print_uint(sb,(unsigned int)turn_params.net_engine_version,"net engine version",0); https_print_str(sb,turn_params.net_engine_version_txt[(int)turn_params.net_engine_version],"net engine",0); https_print_flag(sb,turn_params.fingerprint,"enforce fingerprints",0); https_print_flag(sb,turn_params.mobility,"mobility","mobility"); https_print_flag(sb,turn_params.udp_self_balance,"udp-self-balance",0); https_print_str(sb,turn_params.pidfile,"pidfile",0); https_print_uint(sb,(unsigned long)getuid(),"process user ID",0); https_print_uint(sb,(unsigned long)getgid(),"process group ID",0); { char wd[1025]; if(getcwd(wd,sizeof(wd)-1)) { https_print_str(sb,wd,"process dir",0); } } https_print_empty_row(sb,2); if(turn_params.cipher_list[0]) https_print_str(sb,turn_params.cipher_list,"cipher-list",0); else https_print_str(sb,DEFAULT_CIPHER_LIST,"cipher-list",0); https_print_str(sb,turn_params.ec_curve_name,"ec-curve-name",0); { if(turn_params.dh_key_size == DH_CUSTOM) https_print_str(sb,turn_params.dh_file,"dh-file",0); else { unsigned int dh_key_length = 1066; if(turn_params.dh_key_size == DH_566) dh_key_length = 566; else if(turn_params.dh_key_size == DH_2066) dh_key_length = 2066; https_print_uint(sb,(unsigned long)dh_key_length,"DH-key-length",0); } } https_print_str(sb,turn_params.ca_cert_file,"Certificate Authority file",0); https_print_str(sb,turn_params.cert_file,"Certificate file",0); https_print_str(sb,turn_params.pkey_file,"Private Key file",0); https_print_empty_row(sb,2); https_print_str_array(sb,turn_params.listener.addrs,turn_params.listener.addrs_number,"Listener addr"); if(turn_params.listener_ifname[0]) https_print_str(sb,turn_params.listener_ifname,"listener-ifname",0); https_print_flag(sb,turn_params.no_udp,"no-udp",0); https_print_flag(sb,turn_params.no_tcp,"no-tcp",0); https_print_flag(sb,turn_params.no_dtls,"no-dtls",0); https_print_flag(sb,turn_params.no_tls,"no-tls",0); https_print_flag(sb,(!turn_params.no_tlsv1 && !turn_params.no_tls),"TLSv1.0",0); https_print_flag(sb,(!turn_params.no_tlsv1_1 && !turn_params.no_tls),"TLSv1.1",0); https_print_flag(sb,(!turn_params.no_tlsv1_2 && !turn_params.no_tls),"TLSv1.2",0); https_print_uint(sb,(unsigned long)turn_params.listener_port,"listener-port",0); https_print_uint(sb,(unsigned long)turn_params.tls_listener_port,"tls-listener-port",0); https_print_uint(sb,(unsigned long)turn_params.alt_listener_port,"alt-listener-port",0); https_print_uint(sb,(unsigned long)turn_params.alt_tls_listener_port,"alt-tls-listener-port",0); https_print_addr(sb,turn_params.external_ip,0,"External public IP"); https_print_empty_row(sb,2); { size_t an = https_print_addr_list(sb,&turn_params.aux_servers_list,1,"Aux server"); an += https_print_addr_list(sb,&turn_params.alternate_servers_list,1,"Alternate server"); an += https_print_addr_list(sb,&turn_params.tls_alternate_servers_list,1,"TLS alternate server"); if(an) { https_print_empty_row(sb,2); } } https_print_str_array(sb,turn_params.relay_addrs,turn_params.relays_number,"Relay addr"); if(turn_params.relay_ifname[0]) https_print_str(sb,turn_params.relay_ifname,"relay-ifname",0); https_print_flag(sb,turn_params.server_relay,"server-relay",0); https_print_flag(sb,turn_params.no_udp_relay,"no-udp-relay","no-udp-relay"); https_print_flag(sb,turn_params.no_tcp_relay,"no-tcp-relay","no-tcp-relay"); https_print_uint(sb,(unsigned long)turn_params.min_port,"min-port",0); https_print_uint(sb,(unsigned long)turn_params.max_port,"max-port",0); https_print_flag(sb,turn_params.no_multicast_peers,"no-multicast-peers","no-multicast-peers"); https_print_flag(sb,turn_params.allow_loopback_peers,"allow-loopback-peers","allow-loopback-peers"); https_print_empty_row(sb,2); if(turn_params.default_users_db.persistent_users_db.userdb[0]) { switch(turn_params.default_users_db.userdb_type) { #if !defined(TURN_NO_SQLITE) case TURN_USERDB_TYPE_SQLITE: https_print_str(sb,"SQLite","DB type",0); break; #endif #if !defined(TURN_NO_PQ) case TURN_USERDB_TYPE_PQ: https_print_str(sb,"Postgres","DB type",0); break; #endif #if !defined(TURN_NO_MYSQL) case TURN_USERDB_TYPE_MYSQL: https_print_str(sb,"MySQL/MariaDB","DB type",0); break; #endif #if !defined(TURN_NO_MONGO) case TURN_USERDB_TYPE_MONGO: https_print_str(sb,"MongoDB","DB type",0); break; #endif #if !defined(TURN_NO_HIREDIS) case TURN_USERDB_TYPE_REDIS: https_print_str(sb,"redis","DB type",0); break; #endif default: https_print_str(sb,"unknown","DB type",0); }; if(is_superuser()) { https_print_str(sb,turn_params.default_users_db.persistent_users_db.userdb,"DB",0); } } else { https_print_str(sb,"none","DB type",0); https_print_str(sb,"none","DB",0); } #if !defined(TURN_NO_HIREDIS) if(is_superuser()) { if(turn_params.use_redis_statsdb && turn_params.redis_statsdb[0]) { https_print_str(sb,turn_params.redis_statsdb,"Redis Statistics DB",0); } } #endif https_print_empty_row(sb,2); if(turn_params.ct == TURN_CREDENTIALS_LONG_TERM) https_print_flag(sb,1,"Long-term authorization mechanism",0); else https_print_flag(sb,1,"Anonymous credentials",0); https_print_flag(sb,turn_params.use_auth_secret_with_timestamp,"TURN REST API support",0); if(turn_params.use_auth_secret_with_timestamp) { if(!turn_params.rest_api_separator || ((unsigned int)turn_params.rest_api_separator == (unsigned int)':')) { https_print_str(sb,":","TURN REST API separator",0); } else { https_print_uint(sb,turn_params.rest_api_separator,"TURN REST API separator ASCII number",0); } } https_print_empty_row(sb,2); if(is_superuser()) { char * rn = get_realm(NULL)->options.name; if(rn[0]) https_print_str(sb,rn,"Default realm",0); } realm_params_t *rp = get_realm(current_eff_realm()); if(!rp) rp = get_realm(NULL); https_print_str(sb,rp->options.name,"Admin session (current) realm",0); https_print_uint(sb,(unsigned long)rp->options.perf_options.total_quota,"current realm max number of sessions (total-quota)","cr-total-quota"); https_print_uint(sb,(unsigned long)rp->options.perf_options.user_quota,"current realm max sessions per user (user-quota)","cr-user-quota"); https_print_uint(sb,(unsigned long)rp->options.perf_options.max_bps,"current realm max-bps (per session)","cr-max-bps"); https_print_empty_row(sb,2); https_print_uint(sb,(unsigned long)rp->status.total_current_allocs,"total-current-allocs",0); https_print_empty_row(sb,2); https_print_uint(sb,(unsigned long)turn_params.total_quota,"Default total-quota (per realm)","total-quota"); https_print_uint(sb,(unsigned long)turn_params.user_quota,"Default user-quota (per realm)","user-quota"); https_print_uint(sb,(unsigned long)get_bps_capacity(),"Total bps-capacity (per server)","bps-capacity"); https_print_uint(sb,(unsigned long)get_bps_capacity_allocated(),"Allocated bps-capacity (per server)",0); https_print_uint(sb,(unsigned long)get_max_bps(),"Default max-bps (per session)","max-bps"); https_print_empty_row(sb,2); https_print_ip_range_list(sb,&turn_params.ip_whitelist,"Whitelist IP (static)",NULL,0); { ip_range_list_t* l = get_ip_list("allowed"); https_print_ip_range_list(sb,l,"Whitelist IP (dynamic)","allowed",1); ip_list_free(l); } https_print_empty_row(sb,2); https_print_ip_range_list(sb,&turn_params.ip_blacklist,"Blacklist IP (static)", NULL, 0); { ip_range_list_t* l = get_ip_list("denied"); https_print_ip_range_list(sb,l,"Blacklist IP (dynamic)", "denied", 1); ip_list_free(l); } } str_buffer_append(sb,"\r\n
ParameterValue
\r\n"); https_finish_page(sb,s,0); } } } struct https_ps_arg { struct str_buffer* sb; size_t counter; turn_time_t ct; const char* client_protocol; const char* user_pattern; size_t max_sessions; turnsession_id cs; }; static int https_print_session(ur_map_key_type key, ur_map_value_type value, void *arg) { if(key && value && arg) { struct https_ps_arg *csarg = (struct https_ps_arg*)arg; struct str_buffer* sb = csarg->sb; struct turn_session_info *tsi = (struct turn_session_info *)value; if(current_eff_realm()[0] && strcmp(current_eff_realm(),tsi->realm)) return 0; if(csarg->user_pattern[0]) { if(!strstr((char*)tsi->username,csarg->user_pattern)) { return 0; } } if(csarg->cs == tsi->id) { return 0; } { const char *pn=csarg->client_protocol; if(pn[0]) { if(!strcmp(pn,"TLS") || !strcmp(pn,"tls") || !strcmp(pn,"Tls")) { if((tsi->client_protocol != TLS_SOCKET)&&(tsi->client_protocol != TLS_SCTP_SOCKET)) return 0; } else if(!strcmp(pn,"DTLS") || !strcmp(pn,"dtls") || !strcmp(pn,"Dtls")) { if(tsi->client_protocol != DTLS_SOCKET) return 0; } else if(!strcmp(pn,"TCP") || !strcmp(pn,"tcp") || !strcmp(pn,"Tcp")) { if((tsi->client_protocol != TCP_SOCKET)&&(tsi->client_protocol != SCTP_SOCKET)) return 0; } else if(!strcmp(pn,"UDP") || !strcmp(pn,"udp") || !strcmp(pn,"Udp")) { if(tsi->client_protocol != UDP_SOCKET) return 0; } else { return 0; } } } if((unsigned long)csarg->counter<(unsigned long)csarg->max_sessions) { str_buffer_append(sb,""); str_buffer_append_sz(sb,(size_t)(csarg->counter+1)); str_buffer_append(sb,""); str_buffer_append_sid(sb,tsi->id); str_buffer_append(sb,"
cancel"); str_buffer_append(sb,""); str_buffer_append(sb,(char*)tsi->username); str_buffer_append(sb,""); str_buffer_append(sb,tsi->realm); str_buffer_append(sb,""); str_buffer_append(sb,tsi->origin); str_buffer_append(sb,""); if(turn_time_before(csarg->ct, tsi->start_time)) { str_buffer_append(sb,"undefined time\n"); } else { str_buffer_append_sz(sb,(size_t)(csarg->ct - tsi->start_time)); } str_buffer_append(sb,""); if(turn_time_before(tsi->expiration_time,csarg->ct)) { str_buffer_append(sb,"expired"); } else { str_buffer_append_sz(sb,(size_t)(tsi->expiration_time - csarg->ct)); } str_buffer_append(sb,""); str_buffer_append(sb,socket_type_name(tsi->client_protocol)); str_buffer_append(sb,""); str_buffer_append(sb,socket_type_name(tsi->peer_protocol)); str_buffer_append(sb,""); { if(!tsi->local_addr_data.saddr[0]) addr_to_string(&(tsi->local_addr_data.addr),(u08bits*)tsi->local_addr_data.saddr); if(!tsi->remote_addr_data.saddr[0]) addr_to_string(&(tsi->remote_addr_data.addr),(u08bits*)tsi->remote_addr_data.saddr); if(!tsi->relay_addr_data_ipv4.saddr[0]) addr_to_string(&(tsi->relay_addr_data_ipv4.addr),(u08bits*)tsi->relay_addr_data_ipv4.saddr); if(!tsi->relay_addr_data_ipv6.saddr[0]) addr_to_string(&(tsi->relay_addr_data_ipv6.addr),(u08bits*)tsi->relay_addr_data_ipv6.saddr); str_buffer_append(sb,tsi->remote_addr_data.saddr); str_buffer_append(sb,""); str_buffer_append(sb,tsi->local_addr_data.saddr); str_buffer_append(sb,""); str_buffer_append(sb,tsi->relay_addr_data_ipv4.saddr); str_buffer_append(sb,""); str_buffer_append(sb,tsi->relay_addr_data_ipv6.saddr); str_buffer_append(sb,""); str_buffer_append(sb,get_flag(tsi->enforce_fingerprints)); str_buffer_append(sb,""); str_buffer_append(sb,get_flag(tsi->is_mobile)); str_buffer_append(sb,""); str_buffer_append(sb,tsi->tls_method); str_buffer_append(sb,""); str_buffer_append(sb,tsi->tls_cipher); str_buffer_append(sb,""); str_buffer_append_sz(sb,(size_t)tsi->bps); str_buffer_append(sb,""); { char str[1025]; snprintf(str,sizeof(str)-1,"rp=%lu, rb=%lu, sp=%lu, sb=%lu\n",(unsigned long)(tsi->received_packets), (unsigned long)(tsi->received_bytes),(unsigned long)(tsi->sent_packets),(unsigned long)(tsi->sent_bytes)); str_buffer_append(sb,str); str_buffer_append(sb,""); } { char str[1025]; snprintf(str,sizeof(str)-1,"r=%lu, s=%lu, total=%lu (bytes per sec)\n",(unsigned long)(tsi->received_rate), (unsigned long)(tsi->sent_rate),(unsigned long)(tsi->total_rate)); str_buffer_append(sb,str); str_buffer_append(sb,""); } if(tsi->main_peers_size) { size_t i; for(i=0;imain_peers_size;++i) { if(!(tsi->main_peers_data[i].saddr[0])) addr_to_string(&(tsi->main_peers_data[i].addr),(u08bits*)tsi->main_peers_data[i].saddr); str_buffer_append(sb," "); str_buffer_append(sb,tsi->main_peers_data[i].saddr); str_buffer_append(sb," "); } if(tsi->extra_peers_size && tsi->extra_peers_data) { for(i=0;iextra_peers_size;++i) { if(!(tsi->extra_peers_data[i].saddr[0])) addr_to_string(&(tsi->extra_peers_data[i].addr),(u08bits*)tsi->extra_peers_data[i].saddr); str_buffer_append(sb," "); str_buffer_append(sb,tsi->extra_peers_data[i].saddr); str_buffer_append(sb," "); } } } str_buffer_append(sb,""); } } csarg->counter += 1; } return 0; } static size_t https_print_sessions(struct str_buffer* sb, const char* client_protocol, const char* user_pattern, size_t max_sessions, turnsession_id cs) { struct https_ps_arg arg = {sb,0,0,client_protocol,user_pattern,max_sessions,cs}; arg.ct = turn_time(); ur_map_foreach_arg(adminserver.sessions, (foreachcb_arg_type)https_print_session, &arg); return arg.counter; } static void write_ps_page(ioa_socket_handle s, const char* client_protocol, const char* user_pattern, size_t max_sessions, turnsession_id cs) { if(s && !ioa_socket_tobeclosed(s)) { if(!is_as_ok(s)) { write_https_logon_page(s); } else { struct str_buffer* sb = str_buffer_new(); https_print_page_header(sb); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
Filter:\r\n"); str_buffer_append(sb,"
Realm name: "); str_buffer_append(sb," Client protocol: "); str_buffer_append(sb," User name contains:

"); str_buffer_append(sb," Max number of output sessions in the page:
"); str_buffer_append(sb,"
"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
TURN Sessions:

\r\n"); str_buffer_append(sb,"\r\n"); size_t total_sz = https_print_sessions(sb,client_protocol,user_pattern,max_sessions,cs); str_buffer_append(sb,"\r\n
NSession IDUserRealmOriginAge, secsExpires, secsClient protocolRelay protocolClient addrServer addrRelay addr (IPv4)Relay addr (IPv6)FingerprintsMobileTLS methodTLS cipherBPS (allocated)PacketsRatePeers
\r\n"); str_buffer_append(sb,"
Total sessions = "); str_buffer_append_sz(sb,total_sz); str_buffer_append(sb,"
\r\n"); https_finish_page(sb,s,0); } } } static size_t https_print_users(struct str_buffer* sb) { size_t ret = 0; const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->list_users) { secrets_list_t users,realms; init_secrets_list(&users); init_secrets_list(&realms); dbd->list_users((u08bits*)current_eff_realm(),&users,&realms); size_t sz = get_secrets_list_size(&users); size_t i; for(i=0;i"); str_buffer_append_sz(sb,i+1); str_buffer_append(sb,""); str_buffer_append(sb,""); str_buffer_append(sb,get_secrets_list_elem(&users,i)); str_buffer_append(sb,""); if(!current_eff_realm()[0]) { str_buffer_append(sb,""); str_buffer_append(sb,get_secrets_list_elem(&realms,i)); str_buffer_append(sb,""); } str_buffer_append(sb," delete"); str_buffer_append(sb,""); str_buffer_append(sb,""); ++ret; } clean_secrets_list(&users); clean_secrets_list(&realms); } return ret; } static void write_users_page(ioa_socket_handle s, const u08bits *add_user, const u08bits *add_realm, const char* msg) { if(s && !ioa_socket_tobeclosed(s)) { if(!is_as_ok(s)) { write_https_logon_page(s); } else { struct str_buffer* sb = str_buffer_new(); https_print_page_header(sb); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
Filter:\r\n"); str_buffer_append(sb,"
Realm name: "); str_buffer_append(sb,"
"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
User:\r\n"); if(msg && msg[0]) { str_buffer_append(sb,"
"); str_buffer_append(sb,msg); str_buffer_append(sb,"

"); } str_buffer_append(sb,"
Realm name:
\r\n"); str_buffer_append(sb,"
User name:
\r\n"); str_buffer_append(sb,"
Password:
\r\n"); str_buffer_append(sb,"
Confirm password:

\r\n"); str_buffer_append(sb,"
"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
Users:

\r\n"); str_buffer_append(sb,"\r\n"); str_buffer_append(sb,""); if(!current_eff_realm()[0]) { str_buffer_append(sb,""); } str_buffer_append(sb,""); str_buffer_append(sb,"\r\n"); size_t total_sz = https_print_users(sb); str_buffer_append(sb,"\r\n
NNameRealm
\r\n"); str_buffer_append(sb,"
Total users = "); str_buffer_append_sz(sb,total_sz); str_buffer_append(sb,"
\r\n"); https_finish_page(sb,s,0); } } } static size_t https_print_secrets(struct str_buffer* sb) { size_t ret = 0; const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->list_secrets) { secrets_list_t secrets,realms; init_secrets_list(&secrets); init_secrets_list(&realms); dbd->list_secrets((u08bits*)current_eff_realm(),&secrets,&realms); size_t sz = get_secrets_list_size(&secrets); size_t i; for(i=0;i"); str_buffer_append_sz(sb,i+1); str_buffer_append(sb,""); str_buffer_append(sb,""); str_buffer_append(sb,get_secrets_list_elem(&secrets,i)); str_buffer_append(sb,""); if(!current_eff_realm()[0]) { str_buffer_append(sb,""); str_buffer_append(sb,get_secrets_list_elem(&realms,i)); str_buffer_append(sb,""); } str_buffer_append(sb," delete"); str_buffer_append(sb,""); str_buffer_append(sb,""); ++ret; } clean_secrets_list(&secrets); clean_secrets_list(&realms); } return ret; } static void write_shared_secrets_page(ioa_socket_handle s, const char* add_secret, const char* add_realm, const char* msg) { if(s && !ioa_socket_tobeclosed(s)) { if(!is_as_ok(s)) { write_https_logon_page(s); } else { struct str_buffer* sb = str_buffer_new(); https_print_page_header(sb); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
Filter:\r\n"); str_buffer_append(sb,"
Realm name: "); str_buffer_append(sb,"
"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
Secret:\r\n"); if(msg && msg[0]) { str_buffer_append(sb,"
"); str_buffer_append(sb,msg); str_buffer_append(sb,"

"); } str_buffer_append(sb,"
Realm name:
\r\n"); str_buffer_append(sb,"
Secret:
\r\n"); str_buffer_append(sb,"
"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
Shared secrets:

\r\n"); str_buffer_append(sb,"\r\n"); str_buffer_append(sb,""); if(!current_eff_realm()[0]) { str_buffer_append(sb,""); } str_buffer_append(sb,""); str_buffer_append(sb,"\r\n"); size_t total_sz = https_print_secrets(sb); str_buffer_append(sb,"\r\n
NValueRealm
\r\n"); str_buffer_append(sb,"
Total secrets = "); str_buffer_append_sz(sb,total_sz); str_buffer_append(sb,"
\r\n"); https_finish_page(sb,s,0); } } } static size_t https_print_origins(struct str_buffer* sb) { size_t ret = 0; const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->list_origins) { secrets_list_t origins,realms; init_secrets_list(&origins); init_secrets_list(&realms); dbd->list_origins((u08bits*)current_eff_realm(),&origins,&realms); size_t sz = get_secrets_list_size(&origins); size_t i; for(i=0;i"); str_buffer_append_sz(sb,i+1); str_buffer_append(sb,""); str_buffer_append(sb,""); str_buffer_append(sb,get_secrets_list_elem(&origins,i)); str_buffer_append(sb,""); if(!current_eff_realm()[0]) { str_buffer_append(sb,""); str_buffer_append(sb,get_secrets_list_elem(&realms,i)); str_buffer_append(sb,""); } if(is_superuser()) { str_buffer_append(sb," delete"); str_buffer_append(sb,""); } str_buffer_append(sb,""); ++ret; } clean_secrets_list(&origins); clean_secrets_list(&realms); } return ret; } static void write_origins_page(ioa_socket_handle s, const char* add_origin, const char* add_realm, const char* msg) { if(s && !ioa_socket_tobeclosed(s)) { if(!is_as_ok(s)) { write_https_logon_page(s); } else { struct str_buffer* sb = str_buffer_new(); https_print_page_header(sb); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
Filter:\r\n"); str_buffer_append(sb,"
Realm name: "); str_buffer_append(sb,"
"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
\r\n"); if(is_superuser()) { str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
Origin:\r\n"); if(msg && msg[0]) { str_buffer_append(sb,"
"); str_buffer_append(sb,msg); str_buffer_append(sb,"

"); } str_buffer_append(sb,"
Realm name:
\r\n"); str_buffer_append(sb,"
Origin:
\r\n"); str_buffer_append(sb,"
"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
\r\n"); } str_buffer_append(sb,"
Origins:

\r\n"); str_buffer_append(sb,"\r\n"); str_buffer_append(sb,""); if(!current_eff_realm()[0]) { str_buffer_append(sb,""); } if(is_superuser()) { str_buffer_append(sb,""); } str_buffer_append(sb,"\r\n"); size_t total_sz = https_print_origins(sb); str_buffer_append(sb,"\r\n
NValueRealm
\r\n"); str_buffer_append(sb,"
Total origins = "); str_buffer_append_sz(sb,total_sz); str_buffer_append(sb,"
\r\n"); https_finish_page(sb,s,0); } } } static size_t https_print_oauth_keys(struct str_buffer* sb) { size_t ret = 0; const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->list_oauth_keys) { secrets_list_t kids,teas,tss,lts,realms; init_secrets_list(&kids); init_secrets_list(&teas); init_secrets_list(&tss); init_secrets_list(<s); init_secrets_list(&realms); dbd->list_oauth_keys(&kids,&teas,&tss,<s,&realms); size_t sz = get_secrets_list_size(&kids); size_t i; for(i=0;i"); str_buffer_append_sz(sb,i+1); str_buffer_append(sb,""); str_buffer_append(sb,""); str_buffer_append(sb,get_secrets_list_elem(&kids,i)); str_buffer_append(sb,""); str_buffer_append(sb," show "); str_buffer_append(sb,""); str_buffer_append(sb,get_secrets_list_elem(&tss,i)); str_buffer_append(sb,""); str_buffer_append(sb,""); str_buffer_append(sb,get_secrets_list_elem(<s,i)); str_buffer_append(sb,""); str_buffer_append(sb,""); str_buffer_append(sb,get_secrets_list_elem(&teas,i)); str_buffer_append(sb,""); str_buffer_append(sb,""); str_buffer_append(sb,get_secrets_list_elem(&realms,i)); str_buffer_append(sb,""); { str_buffer_append(sb," delete"); str_buffer_append(sb,""); } str_buffer_append(sb,""); ++ret; } clean_secrets_list(&kids); clean_secrets_list(&teas); clean_secrets_list(&tss); clean_secrets_list(<s); clean_secrets_list(&realms); } return ret; } static void write_https_oauth_show_keys(ioa_socket_handle s, const char* kid) { if(s && !ioa_socket_tobeclosed(s)) { if(!is_as_ok(s)) { write_https_logon_page(s); } else if(!is_superuser()) { write_https_home_page(s); } else { struct str_buffer* sb = str_buffer_new(); https_print_page_header(sb); str_buffer_append(sb,"back to oauth list

\r\n"); if(kid && kid[0]) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->get_oauth_key) { oauth_key_data_raw key; if((*dbd->get_oauth_key)((const u08bits*)kid,&key)<0) { str_buffer_append(sb,"data retrieval error"); } else { oauth_key_data okd; ns_bzero(&okd,sizeof(okd)); convert_oauth_key_data_raw(&key, &okd); char err_msg[1025] = "\0"; size_t err_msg_size = sizeof(err_msg) - 1; oauth_key okey; ns_bzero(&okey,sizeof(okey)); if (convert_oauth_key_data(&okd, &okey, err_msg, err_msg_size) < 0) { str_buffer_append(sb,err_msg); } else { str_buffer_append(sb,"\r\n"); if(key.ikm_key[0]) { str_buffer_append(sb,"\r\n"); } str_buffer_append(sb,"
Base64-encoded key:"); str_buffer_append(sb,key.ikm_key); str_buffer_append(sb,"
\r\n"); } } } } https_finish_page(sb,s,0); } } } static void write_https_oauth_page(ioa_socket_handle s, const char* add_kid, const char* add_ikm, const char* add_tea, const char *add_ts, const char* add_lt, const char* add_realm, const char* msg) { if(s && !ioa_socket_tobeclosed(s)) { if(!is_as_ok(s)) { write_https_logon_page(s); } else if(!is_superuser()) { write_https_home_page(s); } else { struct str_buffer* sb = str_buffer_new(); https_print_page_header(sb); { str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
oAuth key:\r\n"); if(msg && msg[0]) { str_buffer_append(sb,"
"); str_buffer_append(sb,msg); str_buffer_append(sb,"

"); } str_buffer_append(sb,"\r\n"); str_buffer_append(sb,"\r\n
"); { if(!add_kid) add_kid=""; str_buffer_append(sb,"
KID (required):
\r\n"); } str_buffer_append(sb,"
"); { if(!add_ts) add_ts=""; str_buffer_append(sb,"
Timestamp, secs (optional):
\r\n"); } str_buffer_append(sb,"
"); { if(!add_lt) add_lt=""; str_buffer_append(sb,"
Lifetime, secs (optional):
\r\n"); } str_buffer_append(sb,"
"); { if(!add_ikm) add_ikm = ""; str_buffer_append(sb,"
Base64-encoded input keying material (required):
"); str_buffer_append(sb,"
\r\n"); } str_buffer_append(sb,"
"); { if(!add_realm) add_realm = ""; str_buffer_append(sb,"
Realm (optional):
\r\n"); } str_buffer_append(sb,"
"); { str_buffer_append(sb,"
Token encryption algorithm (required):
\r\n"); if(!add_tea || !add_tea[0]) add_tea = "A256GCM"; str_buffer_append(sb,"A128GCM\r\n
\r\n"); str_buffer_append(sb,"A256GCM\r\n
\r\n"); } str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
"); str_buffer_append(sb,"
\r\n"); str_buffer_append(sb,"
\r\n"); } str_buffer_append(sb,"
OAuth keys:

\r\n"); str_buffer_append(sb,"\r\n"); str_buffer_append(sb,""); str_buffer_append(sb,""); str_buffer_append(sb,""); str_buffer_append(sb,""); str_buffer_append(sb,""); str_buffer_append(sb,""); str_buffer_append(sb,"\r\n"); size_t total_sz = https_print_oauth_keys(sb); str_buffer_append(sb,"\r\n
NKIDkeysTimestamp, secsLifetime,secsToken encryption algorithmRealm
\r\n"); str_buffer_append(sb,"
Total oAuth keys = "); str_buffer_append_sz(sb,total_sz); str_buffer_append(sb,"
\r\n"); https_finish_page(sb,s,0); } } } static void handle_toggle_request(ioa_socket_handle s, struct http_request* hr) { if(s && hr) { const char *param = get_http_header_value(hr, HR_UPDATE_PARAMETER, NULL); toggle_param(param); } } static void handle_update_request(ioa_socket_handle s, struct http_request* hr) { if(s && hr) { { const char *param = get_http_header_value(hr, HR_UPDATE_PARAMETER, NULL); if(param) { update_param(param,get_http_header_value(hr,param,"")); } } { const char* eip = get_http_header_value(hr, HR_DELETE_IP, NULL); if(eip && eip[0]) { char* ip = evhttp_decode_uri(eip); const char* r = get_http_header_value(hr, HR_DELETE_IP_REALM,""); const char* kind = get_http_header_value(hr, HR_DELETE_IP_KIND,""); const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->set_permission_ip) { if(!r || !r[0]) { r = current_realm(); } if(current_realm()[0] && strcmp(current_realm(),r)) { //forbidden } else { u08bits realm[STUN_MAX_REALM_SIZE+1]="\0"; STRCPY(realm,r); dbd->set_permission_ip(kind, realm, ip, 1); } } free(ip); } } { const char* eip = get_http_header_value(hr, HR_ADD_IP,NULL); if(eip && eip[0]) { char* ip = evhttp_decode_uri(eip); if(check_ip_list_range(ip)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address range format: %s\n", ip); } else { const char* r = get_http_header_value(hr, HR_ADD_IP_REALM,""); const char* kind = get_http_header_value(hr, HR_ADD_IP_KIND,""); const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->set_permission_ip) { if(!r || !r[0]) { r = current_realm(); } if(current_realm()[0] && strcmp(current_realm(),r)) { //forbidden } else { u08bits realm[STUN_MAX_REALM_SIZE+1]="\0"; STRCPY(realm,r); dbd->set_permission_ip(kind, realm, ip, 0); } } } free(ip); } } } } static void handle_logon_request(ioa_socket_handle s, struct http_request* hr) { if(s && hr) { const char *uname = get_http_header_value(hr, HR_USERNAME, NULL); const char *pwd = get_http_header_value(hr, HR_PASSWORD, NULL); struct admin_session* as = (struct admin_session*)s->special_session; if(!as) { as = (struct admin_session*)turn_malloc(sizeof(struct admin_session)); ns_bzero(as,sizeof(struct admin_session)); s->special_session = as; s->special_session_size = sizeof(struct admin_session); } if(!(as->as_ok) && uname && is_secure_string((const u08bits*)uname,1) && pwd) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->get_admin_user) { password_t password; char realm[STUN_MAX_REALM_SIZE+1]="\0"; if((*(dbd->get_admin_user))((const u08bits*)uname,(u08bits*)realm,password)>=0) { if(!check_password(pwd,(char*)password)) { STRCPY(as->as_login,uname); STRCPY(as->as_realm,realm); as->as_eff_realm[0]=0; as->as_ok = 1; as->number_of_user_sessions = DEFAULT_CLI_MAX_OUTPUT_SESSIONS; } } } } } } static void handle_logout_request(ioa_socket_handle s, struct http_request* hr) { UNUSED_ARG(hr); if(s) { struct admin_session* as = (struct admin_session*)s->special_session; if(as) { as->as_login[0] = 0; as->as_ok = 0; as->as_realm[0] = 0; as->as_eff_realm[0] = 0; } } } static void handle_https(ioa_socket_handle s, ioa_network_buffer_handle nbh) { current_socket = s; if(turn_params.verbose) { if(nbh) { ((char*)ioa_network_buffer_data(nbh))[ioa_network_buffer_get_size(nbh)] = 0; if(!strstr((char*)ioa_network_buffer_data(nbh),"pwd")) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: HTTPS connection input: %s\n", __FUNCTION__, (char*)ioa_network_buffer_data(nbh)); } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: HTTPS connection initial input\n", __FUNCTION__); } } if(!nbh) { write_https_logon_page(s); } else { ((char*)ioa_network_buffer_data(nbh))[ioa_network_buffer_get_size(nbh)] = 0; struct http_request* hr = parse_http_request((char*)ioa_network_buffer_data(nbh)); if(!hr) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: wrong HTTPS request (I cannot parse it)\n", __FUNCTION__); write_https_logon_page(s); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: HTTPS request, path %s\n", __FUNCTION__,hr->path); AS_FORM form = get_form(hr->path); switch(form) { case AS_FORM_PC: { if(is_as_ok(s)) { const char *realm0 = get_http_header_value(hr, HR_REALM, current_realm()); if(!is_superuser()) realm0 = current_realm(); strncpy(current_eff_realm(),realm0,STUN_MAX_REALM_SIZE); write_pc_page(s); } else { write_https_logon_page(s); } break; } case AS_FORM_PS: { if(is_as_ok(s)) { const char *realm0 = get_http_header_value(hr, HR_REALM, current_realm()); if(!is_superuser()) realm0 = current_realm(); strncpy(current_eff_realm(),realm0,STUN_MAX_REALM_SIZE); const char* client_protocol = get_http_header_value(hr, HR_CLIENT_PROTOCOL, ""); const char* user_pattern = get_http_header_value(hr, HR_USER_PATTERN, ""); turnsession_id csid=0; const char* ssid = get_http_header_value(hr, HR_CANCEL_SESSION, NULL); if(ssid) { https_cancel_session(ssid); csid = (turnsession_id)strtoull(ssid,NULL,10); } size_t max_sessions = current_max_output_sessions(); const char* s_max_sessions = get_http_header_value(hr, HR_MAX_SESSIONS,NULL); if(s_max_sessions) { max_sessions=strtoul(s_max_sessions,NULL,10); if(!max_sessions) max_sessions = current_max_output_sessions(); set_current_max_output_sessions(max_sessions); } if(!max_sessions) max_sessions = DEFAULT_CLI_MAX_OUTPUT_SESSIONS; write_ps_page(s,client_protocol,user_pattern,max_sessions,csid); } else { write_https_logon_page(s); } break; } case AS_FORM_USERS: { if(is_as_ok(s)) { { const char *realm0 = get_http_header_value(hr, HR_REALM, current_realm()); if(!is_superuser()) realm0 = current_realm(); strncpy(current_eff_realm(),realm0,STUN_MAX_REALM_SIZE); } { const u08bits *user = (const u08bits*)get_http_header_value(hr, HR_DELETE_USER, NULL); if(user && user[0]) { const u08bits *realm = (const u08bits*)get_http_header_value(hr, HR_DELETE_REALM, ""); if(!is_superuser()) { realm = (const u08bits*)current_realm(); } if(realm && realm[0]) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->del_user) { u08bits u[STUN_MAX_USERNAME_SIZE+1]; u08bits r[STUN_MAX_REALM_SIZE+1]; STRCPY(u,user); STRCPY(r,realm); dbd->del_user(u,r); } } } } const u08bits *add_realm = (const u08bits*)current_eff_realm(); const u08bits *add_user = (const u08bits*)get_http_header_value(hr, HR_ADD_USER,""); const char* msg = ""; if(wrong_html_name((const char*)add_user)) { msg = "Error: wrong user name"; add_user = (const u08bits*)""; } if(add_user[0]) { add_realm = (const u08bits*)get_http_header_value(hr, HR_ADD_REALM, current_realm()); if(!is_superuser()) { add_realm = (const u08bits*)current_realm(); } if(!add_realm[0]) { add_realm=(const u08bits*)current_eff_realm(); } if(!add_realm[0]) { add_realm = (const u08bits*)get_realm(NULL)->options.name; } if(wrong_html_name((const char*)add_realm)) { msg = "Error: wrong realm name"; add_realm = (const u08bits*)""; } if(add_realm[0]) { const u08bits *pwd = (const u08bits*)get_http_header_value(hr, HR_PASSWORD, NULL); const u08bits *pwd1 = (const u08bits*)get_http_header_value(hr, HR_PASSWORD1, NULL); if(pwd && pwd1 && pwd[0] && pwd1[0] && !strcmp((const char*)pwd,(const char*)pwd1)) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->set_user_key) { hmackey_t key; char skey[sizeof(hmackey_t) * 2 + 1]; { u08bits u[STUN_MAX_USERNAME_SIZE+1]; u08bits r[STUN_MAX_REALM_SIZE+1]; u08bits p[STUN_MAX_PWD_SIZE+1]; STRCPY(u,add_user); STRCPY(r,add_realm); STRCPY(p,pwd); stun_produce_integrity_key_str(u, r, p, key, SHATYPE_DEFAULT); size_t i = 0; size_t sz = get_hmackey_size(SHATYPE_DEFAULT); int maxsz = (int) (sz * 2) + 1; char *s = skey; for (i = 0; (i < sz) && (maxsz > 2); i++) { snprintf(s, (size_t) (sz * 2), "%02x", (unsigned int) key[i]); maxsz -= 2; s += 2; } skey[sz * 2] = 0; (*dbd->set_user_key)(u, r, skey); } add_realm=(const u08bits*)""; add_user=(const u08bits*)""; } } else { msg = "Error: wrong password"; } } } write_users_page(s,add_user,add_realm,msg); } else { write_https_logon_page(s); } break; } case AS_FORM_SS: { if(is_as_ok(s)) { { const char *realm0 = get_http_header_value(hr, HR_REALM, current_realm()); if(!is_superuser()) realm0 = current_realm(); strncpy(current_eff_realm(),realm0,STUN_MAX_REALM_SIZE); } { const u08bits *secret = (const u08bits*)get_http_header_value(hr, HR_DELETE_SECRET, NULL); if(secret && secret[0]) { const u08bits *realm = (const u08bits*)get_http_header_value(hr, HR_DELETE_REALM, NULL); if(!is_superuser()) { realm = (const u08bits*)current_realm(); } if(realm && realm[0]) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->del_secret) { u08bits ss[AUTH_SECRET_SIZE+1]; u08bits r[STUN_MAX_REALM_SIZE+1]; STRCPY(ss,secret); STRCPY(r,realm); dbd->del_secret(ss,r); } } } } const u08bits *add_realm = (const u08bits*)current_eff_realm(); const u08bits *add_secret = (const u08bits*)get_http_header_value(hr, HR_ADD_SECRET, ""); const char* msg = ""; if(wrong_html_name((const char*)add_secret)) { msg = "Error: wrong secret value"; add_secret = (const u08bits*)""; } if(add_secret[0]) { add_realm = (const u08bits*)get_http_header_value(hr, HR_ADD_REALM, current_realm()); if(!is_superuser()) { add_realm = (const u08bits*)current_realm(); } if(!add_realm[0]) { add_realm=(const u08bits*)current_eff_realm(); } if(!add_realm[0]) { add_realm = (const u08bits*)get_realm(NULL)->options.name; } if(wrong_html_name((const char*)add_realm)) { msg = "Error: wrong realm name"; add_realm = (const u08bits*)""; } if(add_realm[0]) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->set_secret) { u08bits ss[AUTH_SECRET_SIZE+1]; u08bits r[STUN_MAX_REALM_SIZE+1]; STRCPY(ss,add_secret); STRCPY(r,add_realm); (*dbd->set_secret)(ss, r); } add_secret=(const u08bits*)""; add_realm=(const u08bits*)""; } } write_shared_secrets_page(s,(const char*)add_secret,(const char*)add_realm,msg); } else { write_https_logon_page(s); } break; } case AS_FORM_OS: { if(is_as_ok(s)) { { const char *realm0 = get_http_header_value(hr, HR_REALM, current_realm()); if(!is_superuser()) realm0 = current_realm(); strncpy(current_eff_realm(),realm0,STUN_MAX_REALM_SIZE); } if(is_superuser()) { const u08bits *origin = (const u08bits*)get_http_header_value(hr, HR_DELETE_ORIGIN, NULL); if(origin && origin[0]) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->del_origin) { u08bits o[STUN_MAX_ORIGIN_SIZE+1]; STRCPY(o,origin); dbd->del_origin(o); u08bits corigin[STUN_MAX_ORIGIN_SIZE+1]; get_canonic_origin((const char *)origin, (char *)corigin, sizeof(corigin)-1); dbd->del_origin(corigin); } } } const u08bits *add_realm = (const u08bits*)current_eff_realm(); const u08bits *add_origin = (const u08bits*)get_http_header_value(hr, HR_ADD_ORIGIN, ""); const char* msg = ""; u08bits corigin[STUN_MAX_ORIGIN_SIZE+1]; get_canonic_origin((const char *)add_origin, (char *)corigin, sizeof(corigin)-1); if(corigin[0]) { add_realm = (const u08bits*)get_http_header_value(hr, HR_ADD_REALM, current_realm()); if(!is_superuser()) { add_realm = (const u08bits*)current_realm(); } if(!add_realm[0]) { add_realm=(const u08bits*)current_eff_realm(); } if(!add_realm[0]) { add_realm = (const u08bits*)get_realm(NULL)->options.name; } if(add_realm[0]) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->add_origin) { u08bits o[STUN_MAX_ORIGIN_SIZE+1]; u08bits r[STUN_MAX_REALM_SIZE+1]; STRCPY(o,corigin); STRCPY(r,add_realm); (*dbd->add_origin)(o, r); } add_origin=(const u08bits*)""; add_realm=(const u08bits*)""; } } write_origins_page(s,(const char*)add_origin,(const char*)add_realm,msg); } else { write_https_logon_page(s); } break; } case AS_FORM_OAUTH_SHOW_KEYS: { if(!is_as_ok(s)) { write_https_logon_page(s); } else if(!is_superuser()) { write_https_home_page(s); } else { const char* kid = get_http_header_value(hr,HR_OAUTH_KID,""); write_https_oauth_show_keys(s,kid); } break; } case AS_FORM_OAUTH: { if(!is_as_ok(s)) { write_https_logon_page(s); } else if(!is_superuser()) { write_https_home_page(s); } else { { const char* del_kid = get_http_header_value(hr,HR_DELETE_OAUTH_KID,""); if(del_kid[0]) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->del_oauth_key) { (*dbd->del_oauth_key)((const u08bits*)del_kid); } } } const char* add_kid = ""; const char* add_ts = "0"; const char* add_lt = "0"; const char* add_ikm = ""; const char* add_tea = ""; const char* add_realm = ""; const char* msg = ""; add_kid = get_http_header_value(hr,HR_ADD_OAUTH_KID,""); if(add_kid[0]) { add_ikm = get_http_header_value(hr,HR_ADD_OAUTH_IKM,""); add_ts = get_http_header_value(hr,HR_ADD_OAUTH_TS,""); add_lt = get_http_header_value(hr,HR_ADD_OAUTH_LT,""); add_tea = get_http_header_value(hr,HR_ADD_OAUTH_TEA,""); add_realm = get_http_header_value(hr,HR_ADD_OAUTH_REALM,""); int keys_ok = (add_ikm[0] != 0); if(!keys_ok) { msg = "You must enter the key value."; } else { oauth_key_data_raw key; ns_bzero(&key,sizeof(key)); STRCPY(key.kid,add_kid); if(add_lt && add_lt[0]) { key.lifetime = (u32bits)strtoul(add_lt,NULL,10); if(key.lifetime) { if(add_ts && add_ts[0]) { key.timestamp = (u64bits)strtoull(add_ts,NULL,10); } if(!key.timestamp) { key.timestamp = (u64bits)time(NULL); } } } else if(add_ts && add_ts[0]) { key.timestamp = (u64bits)strtoull(add_ts,NULL,10); } if(add_realm && add_realm[0]) STRCPY(key.realm,add_realm); STRCPY(key.ikm_key,add_ikm); STRCPY(key.as_rs_alg,add_tea); const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->set_oauth_key) { if((*dbd->set_oauth_key)(&key)<0) { msg = "Cannot insert oAuth key into the database"; } else { add_kid = ""; add_ts = "0"; add_lt = "0"; add_ikm = ""; add_tea = ""; add_realm = ""; } } } } write_https_oauth_page(s,add_kid,add_ikm,add_tea,add_ts,add_lt,add_realm,msg); } break; } case AS_FORM_TOGGLE: if(is_as_ok(s)) { handle_toggle_request(s,hr); write_pc_page(s); } else { write_https_logon_page(s); } break; case AS_FORM_UPDATE: if(is_as_ok(s)) { handle_update_request(s,hr); write_pc_page(s); } else { write_https_logon_page(s); } break; case AS_FORM_LOGON: if(!is_as_ok(s)) { handle_logon_request(s,hr); if(is_as_ok(s)) { write_https_home_page(s); } else { write_https_logon_page(s); } } else { write_https_home_page(s); } break; case AS_FORM_LOGOUT: handle_logout_request(s,hr); write_https_logon_page(s); break; default: { const char *realm0 = get_http_header_value(hr, HR_REALM, current_realm()); if(!is_superuser()) realm0 = current_realm(); strncpy(current_eff_realm(),realm0,STUN_MAX_REALM_SIZE); write_https_home_page(s); } }; free_http_request(hr); } } current_socket = NULL; } static void https_input_handler(ioa_socket_handle s, int event_type, ioa_net_data *data, void *arg, int can_resume) { UNUSED_ARG(arg); UNUSED_ARG(s); UNUSED_ARG(event_type); UNUSED_ARG(can_resume); handle_https(s,data->nbh); ioa_network_buffer_delete(adminserver.e, data->nbh); data->nbh = NULL; } void https_admin_server_receive_message(struct bufferevent *bev, void *ptr) { UNUSED_ARG(ptr); ioa_socket_handle s= NULL; int n = 0; struct evbuffer *input = bufferevent_get_input(bev); while ((n = evbuffer_remove(input, &s, sizeof(s))) > 0) { if (n != sizeof(s)) { fprintf(stderr,"%s: Weird HTTPS CLI buffer error: size=%d\n",__FUNCTION__,n); continue; } register_callback_on_ioa_socket(adminserver.e, s, IOA_EV_READ, https_input_handler, NULL, 0); handle_https(s,NULL); } } void send_https_socket(ioa_socket_handle s) { struct evbuffer *output = bufferevent_get_output(adminserver.https_out_buf); if(output) { evbuffer_add(output,&s,sizeof(s)); } } /////////////////////////////// turnserver-4.5.1.1/src/apps/relay/userdb.h0000664000175000017500000001405613436577655017123 0ustar misimisi/* * 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. */ #ifndef __USERDB__ #define __USERDB__ #include #include #include "hiredis_libevent2.h" #include "ns_turn_utils.h" #include "ns_turn_maps.h" #include "ns_turn_server.h" #include "apputils.h" #ifdef __cplusplus extern "C" { #endif //////////// REALM ////////////// struct _realm_status_t; typedef struct _realm_status_t realm_status_t; struct _realm_params_t; typedef struct _realm_params_t realm_params_t; struct _realm_status_t { vint total_current_allocs; ur_string_map *alloc_counters; }; struct _realm_params_t { int is_default_realm; realm_options_t options; realm_status_t status; }; void lock_realms(void); void unlock_realms(void); void update_o_to_realm(ur_string_map * o_to_realm_new); //////////// USER DB ////////////////////////////// struct auth_message { turnserver_id id; turn_credential_type ct; int in_oauth; int out_oauth; int max_session_time; u08bits username[STUN_MAX_USERNAME_SIZE + 1]; u08bits realm[STUN_MAX_REALM_SIZE + 1]; hmackey_t key; password_t pwd; get_username_resume_cb resume_func; ioa_net_data in_buffer; u64bits ctxkey; int success; }; enum _TURN_USERDB_TYPE { #if !defined(TURN_NO_SQLITE) TURN_USERDB_TYPE_UNKNOWN=-1, TURN_USERDB_TYPE_SQLITE=0 #else TURN_USERDB_TYPE_UNKNOWN=0 #endif #if !defined(TURN_NO_PQ) ,TURN_USERDB_TYPE_PQ #endif #if !defined(TURN_NO_MYSQL) ,TURN_USERDB_TYPE_MYSQL #endif #if !defined(TURN_NO_MONGO) ,TURN_USERDB_TYPE_MONGO #endif #if !defined(TURN_NO_HIREDIS) ,TURN_USERDB_TYPE_REDIS #endif }; typedef enum _TURN_USERDB_TYPE TURN_USERDB_TYPE; enum _TURNADMIN_COMMAND_TYPE { TA_COMMAND_UNKNOWN, TA_PRINT_KEY, TA_UPDATE_USER, TA_DELETE_USER, TA_LIST_USERS, TA_SET_SECRET, TA_SHOW_SECRET, TA_DEL_SECRET, TA_ADD_ORIGIN, TA_DEL_ORIGIN, TA_LIST_ORIGINS, TA_SET_REALM_OPTION, TA_LIST_REALM_OPTIONS }; typedef enum _TURNADMIN_COMMAND_TYPE TURNADMIN_COMMAND_TYPE; /////////// SHARED SECRETS ////////////////// struct _secrets_list { char **secrets; size_t sz; }; typedef struct _secrets_list secrets_list_t; /////////// USERS PARAM ///////////////////// #define TURN_LONG_STRING_SIZE (1025) typedef struct _ram_users_db_t { size_t users_number; ur_string_map *static_accounts; secrets_list_t static_auth_secrets; } ram_users_db_t; typedef struct _persistent_users_db_t { char userdb[TURN_LONG_STRING_SIZE]; } persistent_users_db_t; typedef struct _default_users_db_t { TURN_USERDB_TYPE userdb_type; persistent_users_db_t persistent_users_db; ram_users_db_t ram_db; } default_users_db_t; ///////////////////////////////////////////// realm_params_t* get_realm(char* name); void set_default_realm_name(char *realm); int change_total_quota(char *realm, int value); int change_user_quota(char *realm, int value); ///////////////////////////////////////////// void init_secrets_list(secrets_list_t *sl); void init_dynamic_ip_lists(void); void update_white_and_black_lists(void); void clean_secrets_list(secrets_list_t *sl); size_t get_secrets_list_size(secrets_list_t *sl); const char* get_secrets_list_elem(secrets_list_t *sl, size_t i); void add_to_secrets_list(secrets_list_t *sl, const char* elem); /////////// USER DB CHECK ////////////////// int get_user_key(int in_oauth, int *out_oauth, int *max_session_time, u08bits *uname, u08bits *realm, hmackey_t key, ioa_network_buffer_handle nbh); u08bits *start_user_check(turnserver_id id, turn_credential_type ct, int in_oauth, int *out_oauth, u08bits *uname, u08bits *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, u64bits ctxkey, int *postpone_reply); int check_new_allocation_quota(u08bits *username, int oauth, u08bits *realm); void release_allocation_quota(u08bits *username, int oauth, u08bits *realm); /////////// Handle user DB ///////////////// #if defined(DB_TEST) void run_db_test(void); #endif void auth_ping(redis_context_handle rch); void reread_realms(void); int add_static_user_account(char *user); int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08bits *origin, TURNADMIN_COMMAND_TYPE ct, perf_options_t* po, int is_admin); int add_ip_list_range(const char* range, const char* realm, ip_range_list_t * list); int check_ip_list_range(const char* range); ip_range_list_t* get_ip_list(const char *kind); void ip_list_free(ip_range_list_t *l); ///////////// Redis ////////////////////// #if !defined(TURN_NO_HIREDIS) redis_context_handle get_redis_async_connection(struct event_base *base, const char* connection_string, int delete_keys); #endif //////////////////////////////////////////// #ifdef __cplusplus } #endif #endif /// __USERDB__/// turnserver-4.5.1.1/src/apps/relay/http_server.h0000664000175000017500000000572413436577655020206 0ustar misimisi/* * Copyright (C) 2011, 2012, 2013, 2014 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. */ #ifndef __TURN_HTTP_SERVER__ #define __TURN_HTTP_SERVER__ #include "ns_turn_utils.h" #include "ns_turn_server.h" #include "apputils.h" #include #include #ifdef __cplusplus extern "C" { #endif ///////// HTTP REQUEST ////////// enum _HTTP_REQUEST_TYPE { HRT_UNKNOWN=0, HRT_GET, HRT_HEAD, HRT_POST, HRT_PUT, HRT_DELETE }; typedef enum _HTTP_REQUEST_TYPE HTTP_REQUEST_TYPE; struct http_headers; struct http_request { HTTP_REQUEST_TYPE rtype; char *path; struct http_headers *headers; }; struct http_request* parse_http_request(char* request); const char *get_http_header_value(const struct http_request *request, const char* key, const char* def); void free_http_request(struct http_request *request); const char* get_http_date_header(void); //////////////////////////////////////////// struct str_buffer; struct str_buffer* str_buffer_new(void); void str_buffer_append(struct str_buffer* sb, const char* str); void str_buffer_append_sz(struct str_buffer* sb, size_t sz); void str_buffer_append_sid(struct str_buffer* sb, turnsession_id sid); const char* str_buffer_get_str(const struct str_buffer *sb); size_t str_buffer_get_str_len(const struct str_buffer *sb); void str_buffer_free(struct str_buffer *sb); //////////////////////////////////////////// void handle_http_echo(ioa_socket_handle s); //////////////////////////////////////////// #ifdef __cplusplus } #endif #endif /// __TURN_HTTP_SERVER__/// turnserver-4.5.1.1/src/apps/relay/tls_listener.h0000664000175000017500000000440513436577655020343 0ustar misimisi/* * 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. */ #ifndef __TLS_LISTENER__ #define __TLS_LISTENER__ #include #include "ns_turn_utils.h" #include "ns_ioalib_impl.h" #ifdef __cplusplus extern "C" { #endif /////////////////////////////////////////// struct tls_listener_relay_server_info; typedef struct tls_listener_relay_server_info tls_listener_relay_server_type; /////////////////////////////////////////// tls_listener_relay_server_type* create_tls_listener_server(const char* ifname, const char *local_address, int port, int verbose, ioa_engine_handle e, ioa_engine_new_connection_event_handler send_socket, struct relay_server *relay_server); /////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TLS_LISTENER__ turnserver-4.5.1.1/src/apps/relay/libtelnet.h0000664000175000017500000005046013436577655017620 0ustar misimisi/*! * \brief libtelnet - TELNET protocol handling library * * SUMMARY: * * libtelnet is a library for handling the TELNET protocol. It includes * routines for parsing incoming data from a remote peer as well as formatting * data to send to the remote peer. * * libtelnet uses a callback-oriented API, allowing application-specific * handling of various events. The callback system is also used for buffering * outgoing protocol data, allowing the application to maintain control over * the actual socket connection. * * Features supported include the full TELNET protocol, Q-method option * negotiation, ZMP, MCCP2, MSSP, and NEW-ENVIRON. * * CONFORMS TO: * * RFC854 - http://www.faqs.org/rfcs/rfc854.html * RFC855 - http://www.faqs.org/rfcs/rfc855.html * RFC1091 - http://www.faqs.org/rfcs/rfc1091.html * RFC1143 - http://www.faqs.org/rfcs/rfc1143.html * RFC1408 - http://www.faqs.org/rfcs/rfc1408.html * RFC1572 - http://www.faqs.org/rfcs/rfc1572.html * * LICENSE: * * The author or authors of this code dedicate any and all copyright interest * in this code to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and successors. We * intend this dedication to be an overt act of relinquishment in perpetuity of * all present and future rights to this code under copyright law. * * \file libtelnet.h * * \version 0.21 * * \author Sean Middleditch */ /** * Minor fixes by Oleg Moskalenko */ #if !defined(LIBTELNET_INCLUDE) #define LIBTELNET_INCLUDE 1 /* standard C headers necessary for the libtelnet API */ #include /* C++ support */ #if defined(__cplusplus) extern "C" { #endif /* printf type checking feature in GCC and some other compilers */ #if __GNUC__ # define TELNET_GNU_PRINTF(f,a) __attribute__((format(printf, f, a))) /*!< internal helper */ #else # define TELNET_GNU_PRINTF(f,a) /*!< internal helper */ #endif /*! Telnet state tracker object type. */ typedef struct telnet_t telnet_t; /*! Telnet event object type. */ typedef union telnet_event_t telnet_event_t; /*! Telnet option table element type. */ typedef struct telnet_telopt_t telnet_telopt_t; /*! \name Telnet commands */ /*@{*/ /*! Telnet commands and special values. */ #define TELNET_IAC 255 #define TELNET_DONT 254 #define TELNET_DO 253 #define TELNET_WONT 252 #define TELNET_WILL 251 #define TELNET_SB 250 #define TELNET_GA 249 #define TELNET_EL 248 #define TELNET_EC 247 #define TELNET_AYT 246 #define TELNET_AO 245 #define TELNET_IP 244 #define TELNET_BREAK 243 #define TELNET_DM 242 #define TELNET_NOP 241 #define TELNET_SE 240 #define TELNET_EOR 239 #define TELNET_ABORT 238 #define TELNET_SUSP 237 #define TELNET_EOF 236 /*@}*/ /*! \name Telnet option values. */ /*@{*/ /*! Telnet options. */ #define TELNET_TELOPT_BINARY 0 #define TELNET_TELOPT_ECHO 1 #define TELNET_TELOPT_RCP 2 #define TELNET_TELOPT_SGA 3 #define TELNET_TELOPT_NAMS 4 #define TELNET_TELOPT_STATUS 5 #define TELNET_TELOPT_TM 6 #define TELNET_TELOPT_RCTE 7 #define TELNET_TELOPT_NAOL 8 #define TELNET_TELOPT_NAOP 9 #define TELNET_TELOPT_NAOCRD 10 #define TELNET_TELOPT_NAOHTS 11 #define TELNET_TELOPT_NAOHTD 12 #define TELNET_TELOPT_NAOFFD 13 #define TELNET_TELOPT_NAOVTS 14 #define TELNET_TELOPT_NAOVTD 15 #define TELNET_TELOPT_NAOLFD 16 #define TELNET_TELOPT_XASCII 17 #define TELNET_TELOPT_LOGOUT 18 #define TELNET_TELOPT_BM 19 #define TELNET_TELOPT_DET 20 #define TELNET_TELOPT_SUPDUP 21 #define TELNET_TELOPT_SUPDUPOUTPUT 22 #define TELNET_TELOPT_SNDLOC 23 #define TELNET_TELOPT_TTYPE 24 #define TELNET_TELOPT_EOR 25 #define TELNET_TELOPT_TUID 26 #define TELNET_TELOPT_OUTMRK 27 #define TELNET_TELOPT_TTYLOC 28 #define TELNET_TELOPT_3270REGIME 29 #define TELNET_TELOPT_X3PAD 30 #define TELNET_TELOPT_NAWS 31 #define TELNET_TELOPT_TSPEED 32 #define TELNET_TELOPT_LFLOW 33 #define TELNET_TELOPT_LINEMODE 34 #define TELNET_TELOPT_XDISPLOC 35 #define TELNET_TELOPT_ENVIRON 36 #define TELNET_TELOPT_AUTHENTICATION 37 #define TELNET_TELOPT_ENCRYPT 38 #define TELNET_TELOPT_NEW_ENVIRON 39 #define TELNET_TELOPT_MSSP 70 #define TELNET_TELOPT_COMPRESS2 86 #define TELNET_TELOPT_ZMP 93 #define TELNET_TELOPT_EXOPL 255 #define TELNET_TELOPT_MCCP2 86 /*@}*/ /*! \name Protocol codes for TERMINAL-TYPE commands. */ /*@{*/ /*! TERMINAL-TYPE codes. */ #define TELNET_TTYPE_IS 0 #define TELNET_TTYPE_SEND 1 /*@}*/ /*! \name Protocol codes for NEW-ENVIRON/ENVIRON commands. */ /*@{*/ /*! NEW-ENVIRON/ENVIRON codes. */ #define TELNET_ENVIRON_IS 0 #define TELNET_ENVIRON_SEND 1 #define TELNET_ENVIRON_INFO 2 #define TELNET_ENVIRON_VAR 0 #define TELNET_ENVIRON_VALUE 1 #define TELNET_ENVIRON_ESC 2 #define TELNET_ENVIRON_USERVAR 3 /*@}*/ /*! \name Protocol codes for MSSP commands. */ /*@{*/ /*! MSSP codes. */ #define TELNET_MSSP_VAR 1 #define TELNET_MSSP_VAL 2 /*@}*/ /*! \name Telnet state tracker flags. */ /*@{*/ /*! Control behavior of telnet state tracker. */ #define TELNET_FLAG_PROXY (1<<0) #define TELNET_PFLAG_DEFLATE (1<<7) /*@}*/ #if !defined(UNUSED_ARG) #define UNUSED_ARG(A) do { A=A; } while(0) #endif /*! * error codes */ enum telnet_error_t { TELNET_EOK = 0, /*!< no error */ TELNET_EBADVAL, /*!< invalid parameter, or API misuse */ TELNET_ENOMEM, /*!< memory allocation failure */ TELNET_EOVERFLOW, /*!< data exceeds buffer size */ TELNET_EPROTOCOL, /*!< invalid sequence of special bytes */ TELNET_ECOMPRESS /*!< error handling compressed streams */ }; typedef enum telnet_error_t telnet_error_t; /*!< Error code type. */ /*! * event codes */ enum telnet_event_type_t { TELNET_EV_DATA = 0, /*!< raw text data has been received */ TELNET_EV_SEND, /*!< data needs to be sent to the peer */ TELNET_EV_IAC, /*!< generic IAC code received */ TELNET_EV_WILL, /*!< WILL option negotiation received */ TELNET_EV_WONT, /*!< WONT option neogitation received */ TELNET_EV_DO, /*!< DO option negotiation received */ TELNET_EV_DONT, /*!< DONT option negotiation received */ TELNET_EV_SUBNEGOTIATION, /*!< sub-negotiation data received */ TELNET_EV_COMPRESS, /*!< compression has been enabled */ TELNET_EV_ZMP, /*!< ZMP command has been received */ TELNET_EV_TTYPE, /*!< TTYPE command has been received */ TELNET_EV_ENVIRON, /*!< ENVIRON command has been received */ TELNET_EV_MSSP, /*!< MSSP command has been received */ TELNET_EV_WARNING, /*!< recoverable error has occured */ TELNET_EV_ERROR /*!< non-recoverable error has occured */ }; typedef enum telnet_event_type_t telnet_event_type_t; /*!< Telnet event type. */ /*! * environ/MSSP command information */ struct telnet_environ_t { unsigned char type; /*!< either TELNET_ENVIRON_VAR or TELNET_ENVIRON_USERVAR */ const char *var; /*!< name of the variable being set */ const char *value; /*!< value of variable being set; empty string if no value */ }; /*! * event information */ union telnet_event_t { /*! * \brief Event type * * The type field will determine which of the other event structure fields * have been filled in. For instance, if the event type is TELNET_EV_ZMP, * then the zmp event field (and ONLY the zmp event field) will be filled * in. */ enum telnet_event_type_t type; /*! * data event: for DATA and SEND events */ struct data_t { enum telnet_event_type_t _type; /*!< alias for type */ const char *buffer; /*!< byte buffer */ size_t size; /*!< number of bytes in buffer */ } data; /*! * WARNING and ERROR events */ struct error_t { enum telnet_event_type_t _type; /*!< alias for type */ const char *file; /*!< file the error occured in */ const char *func; /*!< function the error occured in */ const char *msg; /*!< error message string */ int line; /*!< line of file error occured on */ telnet_error_t errcode; /*!< error code */ } error; /*! * command event: for IAC */ struct iac_t { enum telnet_event_type_t _type; /*!< alias for type */ unsigned char cmd; /*!< telnet command received */ } iac; /*! * negotiation event: WILL, WONT, DO, DONT */ struct negotiate_t { enum telnet_event_type_t _type; /*!< alias for type */ unsigned char telopt; /*!< option being negotiated */ } neg; /*! * subnegotiation event */ struct subnegotiate_t { enum telnet_event_type_t _type; /*!< alias for type */ const char *buffer; /*!< data of sub-negotiation */ size_t size; /*!< number of bytes in buffer */ unsigned char telopt; /*!< option code for negotiation */ } sub; /*! * ZMP event */ struct zmp_t { enum telnet_event_type_t _type; /*!< alias for type */ const char **argv; /*!< array of argument string */ size_t argc; /*!< number of elements in argv */ } zmp; /*! * TTYPE event */ struct ttype_t { enum telnet_event_type_t _type; /*!< alias for type */ unsigned char cmd; /*!< TELNET_TTYPE_IS or TELNET_TTYPE_SEND */ const char* name; /*!< terminal type name (IS only) */ } ttype; /*! * COMPRESS event */ struct compress_t { enum telnet_event_type_t _type; /*!< alias for type */ unsigned char state; /*!< 1 if compression is enabled, 0 if disabled */ } compress; /*! * ENVIRON/NEW-ENVIRON event */ struct environ_t { enum telnet_event_type_t _type; /*!< alias for type */ const struct telnet_environ_t *values; /*!< array of variable values */ size_t size; /*!< number of elements in values */ unsigned char cmd; /*!< SEND, IS, or INFO */ } environ; /*! * MSSP event */ struct mssp_t { enum telnet_event_type_t _type; /*!< alias for type */ const struct telnet_environ_t *values; /*!< array of variable values */ size_t size; /*!< number of elements in values */ } mssp; }; /*! * \brief event handler * * This is the type of function that must be passed to * telnet_init() when creating a new telnet object. The * function will be invoked once for every event generated * by the libtelnet protocol parser. * * \param telnet The telnet object that generated the event * \param event Event structure with details about the event * \param user_data User-supplied pointer */ typedef void (*telnet_event_handler_t)(telnet_t *telnet, telnet_event_t *event, void *user_data); /*! * telopt support table element; use telopt of -1 for end marker */ struct telnet_telopt_t { short telopt; /*!< one of the TELOPT codes or -1 */ unsigned char us; /*!< TELNET_WILL or TELNET_WONT */ unsigned char him; /*!< TELNET_DO or TELNET_DONT */ }; /*! * state tracker -- private data structure */ struct telnet_t; /*! * \brief Initialize a telnet state tracker. * * This function initializes a new state tracker, which is used for all * other libtelnet functions. Each connection must have its own * telnet state tracker object. * * \param telopts Table of TELNET options the application supports. * \param eh Event handler function called for every event. * \param flags 0 or TELNET_FLAG_PROXY. * \param user_data Optional data pointer that will be passsed to eh. * \return Telent state tracker object. */ extern telnet_t* telnet_init(const telnet_telopt_t *telopts, telnet_event_handler_t eh, unsigned char flags, void *user_data); /*! * \brief Free up any memory allocated by a state tracker. * * This function must be called when a telnet state tracker is no * longer needed (such as after the connection has been closed) to * release any memory resources used by the state tracker. * * \param telnet Telnet state tracker object. */ extern void telnet_free(telnet_t *telnet); /*! * \brief Push a byte buffer into the state tracker. * * Passes one or more bytes to the telnet state tracker for * protocol parsing. The byte buffer is most often going to be * the buffer that recv() was called for while handling the * connection. * * \param telnet Telnet state tracker object. * \param buffer Pointer to byte buffer. * \param size Number of bytes pointed to by buffer. */ extern void telnet_recv(telnet_t *telnet, const char *buffer, size_t size); /*! * \brief Send a telnet command. * * \param telnet Telnet state tracker object. * \param cmd Command to send. */ extern void telnet_iac(telnet_t *telnet, unsigned char cmd); /*! * \brief Send negotiation command. * * Internally, libtelnet uses RFC1143 option negotiation rules. * The negotiation commands sent with this function may be ignored * if they are determined to be redundant. * * \param telnet Telnet state tracker object. * \param cmd TELNET_WILL, TELNET_WONT, TELNET_DO, or TELNET_DONT. * \param opt One of the TELNET_TELOPT_* values. */ extern void telnet_negotiate(telnet_t *telnet, unsigned char cmd, unsigned char opt); /*! * Send non-command data (escapes IAC bytes). * * \param telnet Telnet state tracker object. * \param buffer Buffer of bytes to send. * \param size Number of bytes to send. */ extern void telnet_send(telnet_t *telnet, const char *buffer, size_t size); /*! * \brief Begin a sub-negotiation command. * * Sends IAC SB followed by the telopt code. All following data sent * will be part of the sub-negotiation, until telnet_finish_sb() is * called. * * \param telnet Telnet state tracker object. * \param telopt One of the TELNET_TELOPT_* values. */ extern void telnet_begin_sb(telnet_t *telnet, unsigned char telopt); /*! * \brief Finish a sub-negotiation command. * * This must be called after a call to telnet_begin_sb() to finish a * sub-negotiation command. * * \param telnet Telnet state tracker object. */ #define telnet_finish_sb(telnet) telnet_iac((telnet), TELNET_SE) /*! * \brief Shortcut for sending a complete subnegotiation buffer. * * Equivalent to: * telnet_begin_sb(telnet, telopt); * telnet_send(telnet, buffer, size); * telnet_finish_sb(telnet); * * \param telnet Telnet state tracker format. * \param telopt One of the TELNET_TELOPT_* values. * \param buffer Byte buffer for sub-negotiation data. * \param size Number of bytes to use for sub-negotiation data. */ extern void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt, const char *buffer, size_t size); /*! * \brief Begin sending compressed data. * * This function will begein sending data using the COMPRESS2 option, * which enables the use of zlib to compress data sent to the client. * The client must offer support for COMPRESS2 with option negotiation, * and zlib support must be compiled into libtelnet. * * Only the server may call this command. * * \param telnet Telnet state tracker object. */ extern void telnet_begin_compress2(telnet_t *telnet); /*! * \brief Send formatted data. * * This function is a wrapper around telnet_send(). It allows using * printf-style formatting. * * Additionally, this function will translate \\r to the CR NUL construct and * \\n with CR LF, as well as automatically escaping IAC bytes like * telnet_send(). * * \param telnet Telnet state tracker object. * \param fmt Format string. * \return Number of bytes sent. */ extern int telnet_printf(telnet_t *telnet, const char *fmt, ...) TELNET_GNU_PRINTF(2, 3); /*! * \brief Send formatted data. * * See telnet_printf(). */ extern int telnet_vprintf(telnet_t *telnet, const char *fmt, va_list va); /*! * \brief Send formatted data (no newline escaping). * * This behaves identically to telnet_printf(), except that the \\r and \\n * characters are not translated. The IAC byte is still escaped as normal * with telnet_send(). * * \param telnet Telnet state tracker object. * \param fmt Format string. * \return Number of bytes sent. */ extern int telnet_raw_printf(telnet_t *telnet, const char *fmt, ...) TELNET_GNU_PRINTF(2, 3); /*! * \brief Send formatted data (no newline escaping). * * See telnet_raw_printf(). */ extern int telnet_raw_vprintf(telnet_t *telnet, const char *fmt, va_list va); /*! * \brief Begin a new set of NEW-ENVIRON values to request or send. * * This function will begin the sub-negotiation block for sending or * requesting NEW-ENVIRON values. * * The telnet_finish_newenviron() macro must be called after this * function to terminate the NEW-ENVIRON command. * * \param telnet Telnet state tracker object. * \param type One of TELNET_ENVIRON_SEND, TELNET_ENVIRON_IS, or * TELNET_ENVIRON_INFO. */ extern void telnet_begin_newenviron(telnet_t *telnet, unsigned char type); /*! * \brief Send a NEW-ENVIRON variable name or value. * * This can only be called between calls to telnet_begin_newenviron() and * telnet_finish_newenviron(). * * \param telnet Telnet state tracker object. * \param type One of TELNET_ENVIRON_VAR, TELNET_ENVIRON_USERVAR, or * TELNET_ENVIRON_VALUE. * \param string Variable name or value. */ extern void telnet_newenviron_value(telnet_t* telnet, unsigned char type, const char *string); /*! * \brief Finish a NEW-ENVIRON command. * * This must be called after a call to telnet_begin_newenviron() to finish a * NEW-ENVIRON variable list. * * \param telnet Telnet state tracker object. */ #define telnet_finish_newenviron(telnet) telnet_finish_sb((telnet)) /*! * \brief Send the TERMINAL-TYPE SEND command. * * Sends the sequence IAC TERMINAL-TYPE SEND. * * \param telnet Telnet state tracker object. */ extern void telnet_ttype_send(telnet_t *telnet); /*! * \brief Send the TERMINAL-TYPE IS command. * * Sends the sequence IAC TERMINAL-TYPE IS "string". * * According to the RFC, the recipient of a TERMINAL-TYPE SEND shall * send the next possible terminal-type the client supports. Upon sending * the type, the client should switch modes to begin acting as the terminal * type is just sent. * * The server may continue sending TERMINAL-TYPE IS until it receives a * terminal type is understands. To indicate to the server that it has * reached the end of the available optoins, the client must send the last * terminal type a second time. When the server receives the same terminal * type twice in a row, it knows it has seen all available terminal types. * * After the last terminal type is sent, if the client receives another * TERMINAL-TYPE SEND command, it must begin enumerating the available * terminal types from the very beginning. This allows the server to * scan the available types for a preferred terminal type and, if none * is found, to then ask the client to switch to an acceptable * alternative. * * Note that if the client only supports a single terminal type, then * simply sending that one type in response to every SEND will satisfy * the behavior requirements. * * \param telnet Telnet state tracker object. * \param ttype Name of the terminal-type being sent. */ extern void telnet_ttype_is(telnet_t *telnet, const char* ttype); /*! * \brief Send a ZMP command. * * \param telnet Telnet state tracker object. * \param argc Number of ZMP commands being sent. * \param argv Array of argument strings. */ extern void telnet_send_zmp(telnet_t *telnet, size_t argc, const char **argv); /*! * \brief Send a ZMP command. * * Arguments are listed out in var-args style. After the last argument, a * NULL pointer must be passed in as a sentinel value. * * \param telnet Telnet state tracker object. */ extern void telnet_send_zmpv(telnet_t *telnet, ...); /*! * \brief Send a ZMP command. * * See telnet_send_zmpv(). */ extern void telnet_send_vzmpv(telnet_t *telnet, va_list va); /*! * \brief Begin sending a ZMP command * * \param telnet Telnet state tracker object. * \param cmd The first argument (command name) for the ZMP command. */ extern void telnet_begin_zmp(telnet_t *telnet, const char *cmd); /*! * \brief Send a ZMP command argument. * * \param telnet Telnet state tracker object. * \param arg Telnet argument string. */ extern void telnet_zmp_arg(telnet_t *telnet, const char *arg); /*! * \brief Finish a ZMP command. * * This must be called after a call to telnet_begin_zmp() to finish a * ZMP argument list. * * \param telnet Telnet state tracker object. */ #define telnet_finish_zmp(telnet) telnet_finish_sb((telnet)) /* C++ support */ #if defined(__cplusplus) } /* extern "C" */ #endif #endif /* !defined(LIBTELNET_INCLUDE) */ turnserver-4.5.1.1/src/apps/relay/netengine.c0000664000175000017500000017037213436577655017612 0ustar misimisi/* * 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 "mainrelay.h" //////////// Barrier for the threads ////////////// #if !defined(TURN_NO_THREAD_BARRIERS) static unsigned int barrier_count = 0; static pthread_barrier_t barrier; #endif ////////////// Auth Server //////////////// typedef unsigned char authserver_id; struct auth_server { authserver_id id; struct event_base* event_base; struct bufferevent *in_buf; struct bufferevent *out_buf; pthread_t thr; redis_context_handle rch; }; #define MIN_AUTHSERVER_NUMBER (3) static authserver_id authserver_number = MIN_AUTHSERVER_NUMBER; static struct auth_server authserver[256]; ////////////////////////////////////////////// #define get_real_general_relay_servers_number() (turn_params.general_relay_servers_number > 1 ? turn_params.general_relay_servers_number : 1) #define get_real_udp_relay_servers_number() (turn_params.udp_relay_servers_number > 1 ? turn_params.udp_relay_servers_number : 1) static struct relay_server *general_relay_servers[1+((turnserver_id)-1)]; static struct relay_server *udp_relay_servers[1+((turnserver_id)-1)]; ////////////////////////////////////////////// static void run_events(struct event_base *eb, ioa_engine_handle e); static void setup_relay_server(struct relay_server *rs, ioa_engine_handle e, int to_set_rfc5780); /////////////// BARRIERS /////////////////// #if !defined(PTHREAD_BARRIER_SERIAL_THREAD) #define PTHREAD_BARRIER_SERIAL_THREAD (-1) #endif static void barrier_wait_func(const char* func, int line) { #if !defined(TURN_NO_THREAD_BARRIERS) int br = 0; do { br = pthread_barrier_wait(&barrier); if ((br < 0)&&(br != PTHREAD_BARRIER_SERIAL_THREAD)) { int err = errno; perror("barrier wait"); printf("%s:%s:%d: %d\n", __FUNCTION__, func,line,err); } } while (((br < 0)&&(br != PTHREAD_BARRIER_SERIAL_THREAD)) && (errno == EINTR)); #else UNUSED_ARG(func); UNUSED_ARG(line); sleep(5); #endif } #define barrier_wait() barrier_wait_func(__FUNCTION__,__LINE__) /////////////// Bandwidth ////////////////// static pthread_mutex_t mutex_bps; static band_limit_t allocate_bps(band_limit_t bps, int positive) { band_limit_t ret = 0; if(bps>0) { pthread_mutex_lock(&mutex_bps); if(positive) { if(!(turn_params.bps_capacity)) { ret = bps; turn_params.bps_capacity_allocated += ret; } else if(turn_params.bps_capacity_allocated < turn_params.bps_capacity) { band_limit_t reserve = turn_params.bps_capacity - turn_params.bps_capacity_allocated; if(reserve <= bps) { ret = reserve; turn_params.bps_capacity_allocated = turn_params.bps_capacity; } else { ret = bps; turn_params.bps_capacity_allocated += ret; } } } else { if(turn_params.bps_capacity_allocated >= bps) { turn_params.bps_capacity_allocated -= bps; } else { turn_params.bps_capacity_allocated = 0; } } pthread_mutex_unlock(&mutex_bps); } return ret; } band_limit_t get_bps_capacity_allocated(void) { band_limit_t ret = 0; pthread_mutex_lock(&mutex_bps); ret = turn_params.bps_capacity_allocated; pthread_mutex_unlock(&mutex_bps); return ret; } band_limit_t get_bps_capacity(void) { band_limit_t ret = 0; pthread_mutex_lock(&mutex_bps); ret = turn_params.bps_capacity; pthread_mutex_unlock(&mutex_bps); return ret; } void set_bps_capacity(band_limit_t value) { pthread_mutex_lock(&mutex_bps); turn_params.bps_capacity = value; pthread_mutex_unlock(&mutex_bps); } band_limit_t get_max_bps(void) { band_limit_t ret = 0; pthread_mutex_lock(&mutex_bps); ret = turn_params.max_bps; pthread_mutex_unlock(&mutex_bps); return ret; } void set_max_bps(band_limit_t value) { pthread_mutex_lock(&mutex_bps); turn_params.max_bps = value; pthread_mutex_unlock(&mutex_bps); } /////////////// AUX SERVERS //////////////// static void add_aux_server_list(const char *saddr, turn_server_addrs_list_t *list) { if(saddr && list) { ioa_addr addr; if(make_ioa_addr_from_full_string((const u08bits*)saddr, 0, &addr)!=0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong full address format: %s\n",saddr); } else { list->addrs = (ioa_addr*)turn_realloc(list->addrs,0,sizeof(ioa_addr)*(list->size+1)); addr_cpy(&(list->addrs[(list->size)++]),&addr); { u08bits s[1025]; addr_to_string(&addr, s); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Aux server: %s\n",s); } } } } void add_aux_server(const char *saddr) { add_aux_server_list(saddr,&turn_params.aux_servers_list); } /////////////// ALTERNATE SERVERS //////////////// static void add_alt_server(const char *saddr, int default_port, turn_server_addrs_list_t *list) { if(saddr && list) { ioa_addr addr; turn_mutex_lock(&(list->m)); if(make_ioa_addr_from_full_string((const u08bits*)saddr, default_port, &addr)!=0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong IP address format: %s\n",saddr); } else { list->addrs = (ioa_addr*)turn_realloc(list->addrs,0,sizeof(ioa_addr)*(list->size+1)); addr_cpy(&(list->addrs[(list->size)++]),&addr); { u08bits s[1025]; addr_to_string(&addr, s); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Alternate server added: %s\n",s); } } turn_mutex_unlock(&(list->m)); } } static void del_alt_server(const char *saddr, int default_port, turn_server_addrs_list_t *list) { if(saddr && list && list->size && list->addrs) { ioa_addr addr; turn_mutex_lock(&(list->m)); if(make_ioa_addr_from_full_string((const u08bits*)saddr, default_port, &addr)!=0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong IP address format: %s\n",saddr); } else { size_t i; int found = 0; for(i=0;isize;++i) { if(addr_eq(&(list->addrs[i]),&addr)) { found = 1; break; } } if(found) { size_t j; ioa_addr *new_addrs = (ioa_addr*)turn_malloc(sizeof(ioa_addr)*(list->size-1)); for(j=0;jaddrs[j])); } for(j=i;jsize-1;++j) { addr_cpy(&(new_addrs[j]),&(list->addrs[j+1])); } turn_free(list->addrs,0); list->addrs = new_addrs; list->size -= 1; { u08bits s[1025]; addr_to_string(&addr, s); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Alternate server removed: %s\n",s); } del_alt_server(saddr, default_port, list); } } turn_mutex_unlock(&(list->m)); } } void add_alternate_server(const char *saddr) { add_alt_server(saddr,DEFAULT_STUN_PORT,&turn_params.alternate_servers_list); } void del_alternate_server(const char *saddr) { del_alt_server(saddr,DEFAULT_STUN_PORT,&turn_params.alternate_servers_list); } void add_tls_alternate_server(const char *saddr) { add_alt_server(saddr,DEFAULT_STUN_TLS_PORT,&turn_params.tls_alternate_servers_list); } void del_tls_alternate_server(const char *saddr) { del_alt_server(saddr,DEFAULT_STUN_TLS_PORT,&turn_params.tls_alternate_servers_list); } ////////////////////////////////////////////////// typedef struct update_ssl_ctx_cb_args { ioa_engine_handle engine; turn_params_t *params; struct event *next; } update_ssl_ctx_cb_args_t; static void update_ssl_ctx(evutil_socket_t sock, short events, update_ssl_ctx_cb_args_t *args) { ioa_engine_handle e = args->engine; turn_params_t *params = args->params; pthread_mutex_lock(&turn_params.tls_mutex); e->tls_ctx_ssl23 = params->tls_ctx_ssl23; e->tls_ctx_v1_0 = params->tls_ctx_v1_0; #if TLSv1_1_SUPPORTED e->tls_ctx_v1_1 = params->tls_ctx_v1_1; #if TLSv1_2_SUPPORTED e->tls_ctx_v1_2 = params->tls_ctx_v1_2; #endif #endif #if DTLS_SUPPORTED e->dtls_ctx = params->dtls_ctx; #endif #if DTLSv1_2_SUPPORTED e->dtls_ctx_v1_2 = params->dtls_ctx_v1_2; #endif struct event *next = args->next; pthread_mutex_unlock(&turn_params.tls_mutex); if (next != NULL) event_active(next, EV_READ, 0); UNUSED_ARG(sock); UNUSED_ARG(events); } void set_ssl_ctx(ioa_engine_handle e, turn_params_t *params) { update_ssl_ctx_cb_args_t *args = (update_ssl_ctx_cb_args_t *)turn_malloc(sizeof(update_ssl_ctx_cb_args_t)); args->engine = e; args->params = params; args->next = NULL; update_ssl_ctx(-1, 0, args); struct event_base *base = e->event_base; if (base != NULL) { struct event *ev = event_new(base, -1, EV_PERSIST, (event_callback_fn)update_ssl_ctx, (void *)args); pthread_mutex_lock(&turn_params.tls_mutex); args->next = params->tls_ctx_update_ev; params->tls_ctx_update_ev = ev; pthread_mutex_unlock(&turn_params.tls_mutex); } } ////////////////////////////////////////////////// void add_listener_addr(const char* addr) { ioa_addr baddr; if(make_ioa_addr((const u08bits*)addr,0,&baddr)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot add a listener address: %s\n",addr); } else { char sbaddr[129]; addr_to_string_no_port(&baddr,(u08bits*)sbaddr); size_t i = 0; for(i=0;i=0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, " relay %s initialization...\n",turn_params.relay_addrs[i]); turnipports_add_ip(STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE, &baddr); turnipports_add_ip(STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE, &baddr); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, " relay %s initialization done\n",turn_params.relay_addrs[i]); } } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Relay ports initialization done\n"); } ////////////////////////////////////////////////// // communications between listener and relays ==>> static int handle_relay_message(relay_server_handle rs, struct message_to_relay *sm); static pthread_mutex_t auth_message_counter_mutex = PTHREAD_MUTEX_INITIALIZER; static authserver_id auth_message_counter = 1; void send_auth_message_to_auth_server(struct auth_message *am) { pthread_mutex_lock(&auth_message_counter_mutex); if(auth_message_counter>=authserver_number) auth_message_counter = 1; else if(auth_message_counter<1) auth_message_counter = 1; authserver_id sn = auth_message_counter++; pthread_mutex_unlock(&auth_message_counter_mutex); struct evbuffer *output = bufferevent_get_output(authserver[sn].out_buf); if(evbuffer_add(output,am,sizeof(struct auth_message))<0) { fprintf(stderr,"%s: Weird buffer error\n",__FUNCTION__); } } static void auth_server_receive_message(struct bufferevent *bev, void *ptr) { UNUSED_ARG(ptr); struct auth_message am; int n = 0; struct evbuffer *input = bufferevent_get_input(bev); while ((n = evbuffer_remove(input, &am, sizeof(struct auth_message))) > 0) { if (n != sizeof(struct auth_message)) { fprintf(stderr,"%s: Weird buffer error: size=%d\n",__FUNCTION__,n); continue; } { hmackey_t key; if(get_user_key(am.in_oauth,&(am.out_oauth),&(am.max_session_time),am.username,am.realm,key,am.in_buffer.nbh)<0) { am.success = 0; } else { ns_bcopy(key,am.key,sizeof(hmackey_t)); am.success = 1; } } size_t dest = am.id; struct evbuffer *output = NULL; if(dest>=TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP) { dest -= TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP; if(dest >= get_real_udp_relay_servers_number()) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Too large UDP relay number: %d\n", __FUNCTION__,(int)dest); } else if(!(udp_relay_servers[dest])) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Wrong UDP relay number: %d, total %d\n", __FUNCTION__,(int)dest, (int)get_real_udp_relay_servers_number()); } else { output = bufferevent_get_output(udp_relay_servers[dest]->auth_out_buf); } } else { if(dest >= get_real_general_relay_servers_number()) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Too large general relay number: %d, total %d\n", __FUNCTION__,(int)dest,(int)get_real_general_relay_servers_number()); } else if(!(general_relay_servers[dest])) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Wrong general relay number: %d, total %d\n", __FUNCTION__,(int)dest,(int)get_real_general_relay_servers_number()); } else { output = bufferevent_get_output(general_relay_servers[dest]->auth_out_buf); } } if(output) evbuffer_add(output,&am,sizeof(struct auth_message)); else { ioa_network_buffer_delete(NULL, am.in_buffer.nbh); am.in_buffer.nbh = NULL; } } } static int send_socket_to_general_relay(ioa_engine_handle e, struct message_to_relay *sm) { struct relay_server *rdest = sm->relay_server; if(!rdest) { size_t dest = (hash_int32(addr_get_port(&(sm->m.sm.nd.src_addr)))) % get_real_general_relay_servers_number(); rdest = general_relay_servers[dest]; } struct message_to_relay *smptr = sm; smptr->t = RMT_SOCKET; struct evbuffer *output = NULL; int success = 0; if(!rdest) { goto label_end; } output = bufferevent_get_output(rdest->out_buf); if(output) { if(evbuffer_add(output,smptr,sizeof(struct message_to_relay))<0) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Cannot add message to relay output buffer\n", __FUNCTION__); } else { success = 1; smptr->m.sm.nd.nbh=NULL; } } label_end: if(!success) { ioa_network_buffer_delete(e, smptr->m.sm.nd.nbh); smptr->m.sm.nd.nbh=NULL; IOA_CLOSE_SOCKET(smptr->m.sm.s); return -1; } return 0; } static int send_socket_to_relay(turnserver_id id, u64bits cid, stun_tid *tid, ioa_socket_handle s, int message_integrity, MESSAGE_TO_RELAY_TYPE rmt, ioa_net_data *nd, int can_resume) { int ret = -1; struct message_to_relay sm; ns_bzero(&sm,sizeof(struct message_to_relay)); sm.t = rmt; ioa_socket_handle s_to_delete = s; struct relay_server *rs = NULL; if(id>=TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP) { size_t dest = id-TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP; if(dest >= get_real_udp_relay_servers_number()) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Too large UDP relay number: %d, rmt=%d, total=%d\n", __FUNCTION__,(int)dest,(int)rmt, (int)get_real_udp_relay_servers_number()); goto err; } rs = udp_relay_servers[dest]; if(!rs) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Wrong UDP relay number: %d, rmt=%d, total=%d\n", __FUNCTION__,(int)dest,(int)rmt, (int)get_real_udp_relay_servers_number()); goto err; } } else { size_t dest = id; if(dest >= get_real_general_relay_servers_number()) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Too large general relay number: %d, rmt=%d, total=%d\n", __FUNCTION__,(int)dest,(int)rmt, (int)get_real_general_relay_servers_number()); goto err; } rs = general_relay_servers[dest]; if(!rs) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Wrong general relay number: %d, rmt=%d, total=%d\n", __FUNCTION__,(int)dest,(int)rmt, (int)get_real_general_relay_servers_number()); goto err; } } switch (rmt) { case(RMT_CB_SOCKET): { if(nd && nd->nbh) { sm.m.cb_sm.id = id; sm.m.cb_sm.connection_id = (tcp_connection_id)cid; stun_tid_cpy(&(sm.m.cb_sm.tid),tid); sm.m.cb_sm.s = s; sm.m.cb_sm.message_integrity = message_integrity; addr_cpy(&(sm.m.cb_sm.nd.src_addr),&(nd->src_addr)); sm.m.cb_sm.nd.recv_tos = nd->recv_tos; sm.m.cb_sm.nd.recv_ttl = nd->recv_ttl; sm.m.cb_sm.nd.nbh = nd->nbh; sm.m.cb_sm.can_resume = can_resume; nd->nbh = NULL; s_to_delete = NULL; ret = 0; } break; } case (RMT_MOBILE_SOCKET): { if(nd && nd->nbh) { sm.m.sm.s = s; addr_cpy(&(sm.m.sm.nd.src_addr),&(nd->src_addr)); sm.m.sm.nd.recv_tos = nd->recv_tos; sm.m.sm.nd.recv_ttl = nd->recv_ttl; sm.m.sm.nd.nbh = nd->nbh; sm.m.sm.can_resume = can_resume; nd->nbh = NULL; s_to_delete = NULL; ret = 0; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Empty buffer with mobile socket\n",__FUNCTION__); } break; } default: { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: UNKNOWN RMT message: %d\n",__FUNCTION__,(int)rmt); } } if(ret == 0) { struct evbuffer *output = bufferevent_get_output(rs->out_buf); if(output) { evbuffer_add(output,&sm,sizeof(struct message_to_relay)); } else { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Empty output buffer\n", __FUNCTION__); ret = -1; s_to_delete = s; } } err: IOA_CLOSE_SOCKET(s_to_delete); if(nd && nd->nbh) { ioa_network_buffer_delete(NULL, nd->nbh); nd->nbh = NULL; } if(ret<0) { if(rmt == RMT_MOBILE_SOCKET) { ioa_network_buffer_delete(NULL, sm.m.sm.nd.nbh); sm.m.sm.nd.nbh = NULL; } else if(rmt == RMT_CB_SOCKET) { ioa_network_buffer_delete(NULL, sm.m.cb_sm.nd.nbh); sm.m.cb_sm.nd.nbh = NULL; } } return ret; } int send_session_cancellation_to_relay(turnsession_id sid) { int ret = 0; struct message_to_relay sm; ns_bzero(&sm,sizeof(struct message_to_relay)); sm.t = RMT_CANCEL_SESSION; turnserver_id id = (turnserver_id)(sid / TURN_SESSION_ID_FACTOR); struct relay_server *rs = NULL; if(id>=TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP) { size_t dest = id-TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP; if(dest >= get_real_udp_relay_servers_number()) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Too large UDP relay number: %d, total=%d\n", __FUNCTION__,(int)dest,(int)get_real_udp_relay_servers_number()); ret = -1; goto err; } rs = udp_relay_servers[dest]; if(!rs) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Wrong UDP relay number: %d, total=%d\n", __FUNCTION__,(int)dest,(int)get_real_udp_relay_servers_number()); ret = -1; goto err; } } else { size_t dest = id; if(dest >= get_real_general_relay_servers_number()) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Too large general relay number: %d, total=%d\n", __FUNCTION__,(int)dest,(int)get_real_general_relay_servers_number()); ret = -1; goto err; } rs = general_relay_servers[dest]; if(!rs) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Wrong general relay number: %d, total=%d\n", __FUNCTION__,(int)dest,(int)get_real_general_relay_servers_number()); ret = -1; goto err; } } sm.relay_server = rs; sm.m.csm.id = sid; { struct evbuffer *output = bufferevent_get_output(rs->out_buf); if(output) { evbuffer_add(output,&sm,sizeof(struct message_to_relay)); } else { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "%s: Empty output buffer\n", __FUNCTION__); ret = -1; } } err: return ret; } static int handle_relay_message(relay_server_handle rs, struct message_to_relay *sm) { if(rs && sm) { switch (sm->t) { case RMT_CANCEL_SESSION: { turn_cancel_session(&(rs->server),sm->m.csm.id); } break; case RMT_SOCKET: { if (sm->m.sm.s->defer_nbh) { if (!sm->m.sm.nd.nbh) { sm->m.sm.nd.nbh = sm->m.sm.s->defer_nbh; sm->m.sm.s->defer_nbh = NULL; } else { ioa_network_buffer_delete(rs->ioa_eng, sm->m.sm.s->defer_nbh); sm->m.sm.s->defer_nbh = NULL; } } ioa_socket_handle s = sm->m.sm.s; if (!s) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: socket EMPTY\n",__FUNCTION__); } else if (s->read_event || s->bev) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: socket wrongly preset: 0x%lx : 0x%lx\n", __FUNCTION__, (long) s->read_event, (long) s->bev); IOA_CLOSE_SOCKET(s); sm->m.sm.s = NULL; } else { s->e = rs->ioa_eng; if(open_client_connection_session(&(rs->server), &(sm->m.sm))<0) { IOA_CLOSE_SOCKET(s); sm->m.sm.s = NULL; } } ioa_network_buffer_delete(rs->ioa_eng, sm->m.sm.nd.nbh); sm->m.sm.nd.nbh = NULL; } break; case RMT_CB_SOCKET: turnserver_accept_tcp_client_data_connection(&(rs->server), sm->m.cb_sm.connection_id, &(sm->m.cb_sm.tid), sm->m.cb_sm.s, sm->m.cb_sm.message_integrity, &(sm->m.cb_sm.nd), /*sm->m.cb_sm.can_resume*/ /* Note: we cannot resume this call, it must be authenticated in-place. * There are two reasons for that: * 1) Technical. That's very difficult with the current code structure. * 2) Security (more important). We do not want 'stealing' connections between the users. * */ 0); ioa_network_buffer_delete(rs->ioa_eng, sm->m.cb_sm.nd.nbh); sm->m.cb_sm.nd.nbh = NULL; break; case RMT_MOBILE_SOCKET: { ioa_socket_handle s = sm->m.sm.s; if (!s) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: mobile socket EMPTY\n",__FUNCTION__); } else if (s->read_event || s->bev) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: mobile socket wrongly preset: 0x%lx : 0x%lx\n", __FUNCTION__, (long) s->read_event, (long) s->bev); IOA_CLOSE_SOCKET(s); sm->m.sm.s = NULL; } else { s->e = rs->ioa_eng; if(open_client_connection_session(&(rs->server), &(sm->m.sm))<0) { IOA_CLOSE_SOCKET(s); sm->m.sm.s = NULL; } } ioa_network_buffer_delete(rs->ioa_eng, sm->m.sm.nd.nbh); sm->m.sm.nd.nbh = NULL; break; } default: { perror("Weird buffer type\n"); } } } return 0; } static void handle_relay_auth_message(struct relay_server *rs, struct auth_message *am) { am->resume_func(am->success, am->out_oauth, am->max_session_time, am->key, am->pwd, &(rs->server), am->ctxkey, &(am->in_buffer), am->realm); if (am->in_buffer.nbh) { ioa_network_buffer_delete(rs->ioa_eng, am->in_buffer.nbh); am->in_buffer.nbh = NULL; } } static void relay_receive_message(struct bufferevent *bev, void *ptr) { struct message_to_relay sm; int n = 0; struct evbuffer *input = bufferevent_get_input(bev); struct relay_server *rs = (struct relay_server *)ptr; while ((n = evbuffer_remove(input, &sm, sizeof(struct message_to_relay))) > 0) { if (n != sizeof(struct message_to_relay)) { perror("Weird buffer error\n"); continue; } handle_relay_message(rs, &sm); } } static void relay_receive_auth_message(struct bufferevent *bev, void *ptr) { struct auth_message am; int n = 0; struct evbuffer *input = bufferevent_get_input(bev); struct relay_server *rs = (struct relay_server *)ptr; while ((n = evbuffer_remove(input, &am, sizeof(struct auth_message))) > 0) { if (n != sizeof(struct auth_message)) { perror("Weird auth_buffer error\n"); continue; } handle_relay_auth_message(rs, &am); } } static int send_message_from_listener_to_client(ioa_engine_handle e, ioa_network_buffer_handle nbh, ioa_addr *origin, ioa_addr *destination) { struct message_to_listener mm; mm.t = LMT_TO_CLIENT; addr_cpy(&(mm.m.tc.origin),origin); addr_cpy(&(mm.m.tc.destination),destination); mm.m.tc.nbh = ioa_network_buffer_allocate(e); ioa_network_buffer_header_init(mm.m.tc.nbh); ns_bcopy(ioa_network_buffer_data(nbh),ioa_network_buffer_data(mm.m.tc.nbh),ioa_network_buffer_get_size(nbh)); ioa_network_buffer_set_size(mm.m.tc.nbh,ioa_network_buffer_get_size(nbh)); struct evbuffer *output = bufferevent_get_output(turn_params.listener.out_buf); evbuffer_add(output,&mm,sizeof(struct message_to_listener)); return 0; } static void listener_receive_message(struct bufferevent *bev, void *ptr) { UNUSED_ARG(ptr); struct message_to_listener mm; int n = 0; struct evbuffer *input = bufferevent_get_input(bev); while ((n = evbuffer_remove(input, &mm, sizeof(struct message_to_listener))) > 0) { if (n != sizeof(struct message_to_listener)) { perror("Weird buffer error\n"); continue; } if (mm.t != LMT_TO_CLIENT) { perror("Weird buffer type\n"); continue; } size_t relay_thread_index = 0; if(turn_params.net_engine_version == NEV_UDP_SOCKET_PER_THREAD) { size_t ri; for(ri=0;rithr == pthread_self()) { relay_thread_index=ri; break; } } } size_t i; int found = 0; for(i=0;i1) { /* UDP: */ if(!turn_params.no_udp) { barrier_count += turn_params.listener.addrs_number; if(turn_params.rfc5780) { barrier_count += turn_params.listener.addrs_number; } } if(!turn_params.no_dtls && (turn_params.no_udp || (turn_params.listener_port != turn_params.tls_listener_port))) { barrier_count += turn_params.listener.addrs_number; if(turn_params.rfc5780) { barrier_count += turn_params.listener.addrs_number; } } if(!turn_params.no_udp || !turn_params.no_dtls) { barrier_count += (unsigned int)turn_params.aux_servers_list.size; } } #endif #if !defined(TURN_NO_THREAD_BARRIERS) { if(pthread_barrier_init(&barrier,NULL,barrier_count)<0) perror("barrier init"); } #endif } static void setup_socket_per_endpoint_udp_listener_servers(void) { size_t i = 0; /* Adjust udp relay number */ if(turn_params.general_relay_servers_number>1) { if (!turn_params.no_udp) { turn_params.udp_relay_servers_number += turn_params.listener.addrs_number; if (turn_params.rfc5780) { turn_params.udp_relay_servers_number += turn_params.listener.addrs_number; } } if (!turn_params.no_dtls && (turn_params.no_udp || (turn_params.listener_port != turn_params.tls_listener_port))) { turn_params.udp_relay_servers_number += turn_params.listener.addrs_number; if (turn_params.rfc5780) { turn_params.udp_relay_servers_number += turn_params.listener.addrs_number; } } if (!turn_params.no_udp || !turn_params.no_dtls) { turn_params.udp_relay_servers_number += (unsigned int) turn_params.aux_servers_list.size; } } { if (!turn_params.no_udp || !turn_params.no_dtls) { for (i = 0; i < get_real_udp_relay_servers_number(); i++) { ioa_engine_handle e = turn_params.listener.ioa_eng; int is_5780 = turn_params.rfc5780; if(turn_params.general_relay_servers_number<=1) { while(!(general_relay_servers[0]->ioa_eng)) sched_yield(); udp_relay_servers[i] = general_relay_servers[0]; continue; } else if(turn_params.general_relay_servers_number>1) { e = create_new_listener_engine(); is_5780 = is_5780 && (i >= (size_t) (turn_params.aux_servers_list.size)); } super_memory_t *sm = new_super_memory_region(); struct relay_server* udp_rs = (struct relay_server*) allocate_super_memory_region(sm, sizeof(struct relay_server)); udp_rs->id = (turnserver_id) i + TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP; udp_rs->sm = sm; setup_relay_server(udp_rs, e, is_5780); udp_relay_servers[i] = udp_rs; } } } int udp_relay_server_index = 0; /* Create listeners */ /* Aux UDP servers */ for(i=0; iioa_eng, sizeof(dtls_listener_relay_server_type*)); turn_params.listener.aux_udp_services[index][0] = create_dtls_listener_server(turn_params.listener_ifname, saddr, port, turn_params.verbose, udp_relay_servers[udp_relay_server_index]->ioa_eng, &(udp_relay_servers[udp_relay_server_index]->server), 1, NULL); if(turn_params.general_relay_servers_number>1) { ++udp_relay_server_index; pthread_t thr; if(pthread_create(&thr, NULL, run_udp_listener_thread, turn_params.listener.aux_udp_services[index][0])) { perror("Cannot create aux listener thread\n"); exit(-1); } pthread_detach(thr); } } } /* Main servers */ for(i=0; iioa_eng, sizeof(dtls_listener_relay_server_type*)); turn_params.listener.udp_services[index][0] = create_dtls_listener_server(turn_params.listener_ifname, turn_params.listener.addrs[i], turn_params.listener_port, turn_params.verbose, udp_relay_servers[udp_relay_server_index]->ioa_eng, &(udp_relay_servers[udp_relay_server_index]->server), 1, NULL); if(turn_params.general_relay_servers_number>1) { ++udp_relay_server_index; pthread_t thr; if(pthread_create(&thr, NULL, run_udp_listener_thread, turn_params.listener.udp_services[index][0])) { perror("Cannot create listener thread\n"); exit(-1); } pthread_detach(thr); } if(turn_params.rfc5780) { turn_params.listener.udp_services[index+1] = (dtls_listener_relay_server_type**)allocate_super_memory_engine(udp_relay_servers[udp_relay_server_index]->ioa_eng, sizeof(dtls_listener_relay_server_type*)); turn_params.listener.udp_services[index+1][0] = create_dtls_listener_server(turn_params.listener_ifname, turn_params.listener.addrs[i], get_alt_listener_port(), turn_params.verbose, udp_relay_servers[udp_relay_server_index]->ioa_eng, &(udp_relay_servers[udp_relay_server_index]->server), 1, NULL); if(turn_params.general_relay_servers_number>1) { ++udp_relay_server_index; pthread_t thr; if(pthread_create(&thr, NULL, run_udp_listener_thread, turn_params.listener.udp_services[index+1][0])) { perror("Cannot create listener thread\n"); exit(-1); } pthread_detach(thr); } } } else { turn_params.listener.udp_services[index] = NULL; if(turn_params.rfc5780) turn_params.listener.udp_services[index+1] = NULL; } if(!turn_params.no_dtls && (turn_params.no_udp || (turn_params.listener_port != turn_params.tls_listener_port))) { turn_params.listener.dtls_services[index] = (dtls_listener_relay_server_type**)allocate_super_memory_engine(udp_relay_servers[udp_relay_server_index]->ioa_eng, sizeof(dtls_listener_relay_server_type*)); turn_params.listener.dtls_services[index][0] = create_dtls_listener_server(turn_params.listener_ifname, turn_params.listener.addrs[i], turn_params.tls_listener_port, turn_params.verbose, udp_relay_servers[udp_relay_server_index]->ioa_eng, &(udp_relay_servers[udp_relay_server_index]->server), 1, NULL); if(turn_params.general_relay_servers_number>1) { ++udp_relay_server_index; pthread_t thr; if(pthread_create(&thr, NULL, run_udp_listener_thread, turn_params.listener.dtls_services[index][0])) { perror("Cannot create listener thread\n"); exit(-1); } pthread_detach(thr); } if(turn_params.rfc5780) { turn_params.listener.dtls_services[index+1] = (dtls_listener_relay_server_type**)allocate_super_memory_engine(udp_relay_servers[udp_relay_server_index]->ioa_eng, sizeof(dtls_listener_relay_server_type*)); turn_params.listener.dtls_services[index+1][0] = create_dtls_listener_server(turn_params.listener_ifname, turn_params.listener.addrs[i], get_alt_tls_listener_port(), turn_params.verbose, udp_relay_servers[udp_relay_server_index]->ioa_eng, &(udp_relay_servers[udp_relay_server_index]->server), 1, NULL); if(turn_params.general_relay_servers_number>1) { ++udp_relay_server_index; pthread_t thr; if(pthread_create(&thr, NULL, run_udp_listener_thread, turn_params.listener.dtls_services[index+1][0])) { perror("Cannot create listener thread\n"); exit(-1); } pthread_detach(thr); } } } else { turn_params.listener.dtls_services[index] = NULL; if(turn_params.rfc5780) turn_params.listener.dtls_services[index+1] = NULL; } } } static void setup_socket_per_thread_udp_listener_servers(void) { size_t i = 0; size_t relayindex = 0; /* Create listeners */ for(relayindex=0;relayindexioa_eng) || !(general_relay_servers[relayindex]->server.e)) sched_yield(); } /* Aux UDP servers */ for(i=0; iioa_eng, &(general_relay_servers[relayindex]->server), !relayindex, NULL); } } } /* Main servers */ for(i=0; iioa_eng, &(general_relay_servers[relayindex]->server), !relayindex, NULL); } if(turn_params.rfc5780) { turn_params.listener.udp_services[index+1] = (dtls_listener_relay_server_type**)allocate_super_memory_engine(turn_params.listener.ioa_eng, sizeof(dtls_listener_relay_server_type*) * get_real_general_relay_servers_number()); for(relayindex=0;relayindexioa_eng, &(general_relay_servers[relayindex]->server), !relayindex, NULL); } } } else { turn_params.listener.udp_services[index] = NULL; if(turn_params.rfc5780) turn_params.listener.udp_services[index+1] = NULL; } if(!turn_params.no_dtls && (turn_params.no_udp || (turn_params.listener_port != turn_params.tls_listener_port))) { turn_params.listener.dtls_services[index] = (dtls_listener_relay_server_type**)allocate_super_memory_engine(turn_params.listener.ioa_eng, sizeof(dtls_listener_relay_server_type*) * get_real_general_relay_servers_number()); for(relayindex=0;relayindexioa_eng, &(general_relay_servers[relayindex]->server), !relayindex, NULL); } if(turn_params.rfc5780) { turn_params.listener.dtls_services[index+1] = (dtls_listener_relay_server_type**)allocate_super_memory_engine(turn_params.listener.ioa_eng, sizeof(dtls_listener_relay_server_type*) * get_real_general_relay_servers_number()); for(relayindex=0;relayindexioa_eng, &(general_relay_servers[relayindex]->server), !relayindex, NULL); } } } else { turn_params.listener.dtls_services[index] = NULL; if(turn_params.rfc5780) turn_params.listener.dtls_services[index+1] = NULL; } } } static void setup_socket_per_session_udp_listener_servers(void) { size_t i = 0; /* Aux UDP servers */ for(i=0; iss.sa_family == turn_params.listener.encaddrs[i]->ss.sa_family) { index=i; break; } } } if(index!=0xffff) { for(i=0;iss.sa_family == addr->ss.sa_family) { addr_cpy(alt_addr,caddr); addr_set_port(alt_addr, alt_port); return 0; } } } } } return -1; } static void run_events(struct event_base *eb, ioa_engine_handle e) { if(!eb && e) eb = e->event_base; if (!eb) return; struct timeval timeout; timeout.tv_sec = 5; timeout.tv_usec = 0; event_base_loopexit(eb, &timeout); event_base_dispatch(eb); #if !defined(TURN_NO_HIREDIS) if(e) send_message_to_redis(e->rch, "publish", "__XXX__", "__YYY__"); #endif } void run_listener_server(struct listener_server *ls) { unsigned int cycle = 0; while (!turn_params.stop_turn_server) { if (eve(turn_params.verbose)) { if ((cycle++ & 15) == 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cycle=%u\n", __FUNCTION__, cycle); } } run_events(ls->event_base, ls->ioa_eng); rollover_logfile(); tm_print(); } } static void setup_relay_server(struct relay_server *rs, ioa_engine_handle e, int to_set_rfc5780) { struct bufferevent *pair[2]; if(e) { rs->event_base = e->event_base; rs->ioa_eng = e; } else { rs->event_base = turn_event_base_new(); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"IO method (general relay thread): %s\n",event_base_get_method(rs->event_base)); rs->ioa_eng = create_ioa_engine(rs->sm, rs->event_base, turn_params.listener.tp, turn_params.relay_ifname, turn_params.relays_number, turn_params.relay_addrs, turn_params.default_relays, turn_params.verbose #if !defined(TURN_NO_HIREDIS) ,turn_params.redis_statsdb #endif ); set_ssl_ctx(rs->ioa_eng, &turn_params); ioa_engine_set_rtcp_map(rs->ioa_eng, turn_params.listener.rtcpmap); } bufferevent_pair_new(rs->event_base, TURN_BUFFEREVENTS_OPTIONS, pair); rs->in_buf = pair[0]; rs->out_buf = pair[1]; bufferevent_setcb(rs->in_buf, relay_receive_message, NULL, NULL, rs); bufferevent_enable(rs->in_buf, EV_READ); bufferevent_pair_new(rs->event_base, TURN_BUFFEREVENTS_OPTIONS, pair); rs->auth_in_buf = pair[0]; rs->auth_out_buf = pair[1]; bufferevent_setcb(rs->auth_in_buf, relay_receive_auth_message, NULL, NULL, rs); bufferevent_enable(rs->auth_in_buf, EV_READ); init_turn_server(&(rs->server), rs->id, turn_params.verbose, rs->ioa_eng, turn_params.ct, 0, turn_params.fingerprint, DONT_FRAGMENT_SUPPORTED, start_user_check, check_new_allocation_quota, release_allocation_quota, turn_params.external_ip, &turn_params.check_origin, &turn_params.no_tcp_relay, &turn_params.no_udp_relay, &turn_params.stale_nonce, &turn_params.max_allocate_lifetime, &turn_params.channel_lifetime, &turn_params.permission_lifetime, &turn_params.stun_only, &turn_params.no_stun, &turn_params.prod, &turn_params.web_admin_listen_on_workers, &turn_params.alternate_servers_list, &turn_params.tls_alternate_servers_list, &turn_params.aux_servers_list, turn_params.udp_self_balance, &turn_params.no_multicast_peers, &turn_params.allow_loopback_peers, &turn_params.ip_whitelist, &turn_params.ip_blacklist, send_socket_to_relay, &turn_params.secure_stun, &turn_params.mobility, turn_params.server_relay, send_turn_session_info, send_https_socket, allocate_bps, turn_params.oauth, turn_params.oauth_server_name, turn_params.keep_address_family); if(to_set_rfc5780) { set_rfc5780(&(rs->server), get_alt_addr, send_message_from_listener_to_client); } if(turn_params.net_engine_version == NEV_UDP_SOCKET_PER_THREAD) { setup_tcp_listener_servers(rs->ioa_eng, rs); } } static void *run_general_relay_thread(void *arg) { static int always_true = 1; struct relay_server *rs = (struct relay_server *)arg; int udp_reuses_the_same_relay_server = (turn_params.general_relay_servers_number<=1) || (turn_params.net_engine_version == NEV_UDP_SOCKET_PER_THREAD) || (turn_params.net_engine_version == NEV_UDP_SOCKET_PER_SESSION); int we_need_rfc5780 = udp_reuses_the_same_relay_server && turn_params.rfc5780; ignore_sigpipe(); setup_relay_server(rs, NULL, we_need_rfc5780); barrier_wait(); while(always_true) { run_events(rs->event_base, rs->ioa_eng); } return arg; } static void setup_general_relay_servers(void) { size_t i = 0; for(i=0;iid = (turnserver_id)i; general_relay_servers[i]->sm = NULL; setup_relay_server(general_relay_servers[i], turn_params.listener.ioa_eng, ((turn_params.net_engine_version == NEV_UDP_SOCKET_PER_THREAD) || (turn_params.net_engine_version == NEV_UDP_SOCKET_PER_SESSION)) && turn_params.rfc5780); general_relay_servers[i]->thr = pthread_self(); } else { super_memory_t *sm = new_super_memory_region(); general_relay_servers[i] = (struct relay_server*)allocate_super_memory_region(sm,sizeof(struct relay_server)); general_relay_servers[i]->id = (turnserver_id)i; general_relay_servers[i]->sm = sm; if(pthread_create(&(general_relay_servers[i]->thr), NULL, run_general_relay_thread, general_relay_servers[i])) { perror("Cannot create relay thread\n"); exit(-1); } pthread_detach(general_relay_servers[i]->thr); } } } static int run_auth_server_flag = 1; static void* run_auth_server_thread(void *arg) { ignore_sigpipe(); struct auth_server *as = (struct auth_server*)arg; authserver_id id = as->id; if(id == 0) { reread_realms(); update_white_and_black_lists(); barrier_wait(); while(run_auth_server_flag) { #if defined(DB_TEST) run_db_test(); #endif sleep(5); reread_realms(); update_white_and_black_lists(); } } else { ns_bzero(as,sizeof(struct auth_server)); as->id = id; as->event_base = turn_event_base_new(); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"IO method (auth thread): %s\n",event_base_get_method(as->event_base)); struct bufferevent *pair[2]; bufferevent_pair_new(as->event_base, TURN_BUFFEREVENTS_OPTIONS, pair); as->in_buf = pair[0]; as->out_buf = pair[1]; bufferevent_setcb(as->in_buf, auth_server_receive_message, NULL, NULL, as); bufferevent_enable(as->in_buf, EV_READ); #if !defined(TURN_NO_HIREDIS) as->rch = get_redis_async_connection(as->event_base, turn_params.redis_statsdb, 1); #endif barrier_wait(); while(run_auth_server_flag) { auth_ping(as->rch); run_events(as->event_base,NULL); } } return arg; } static void setup_auth_server(struct auth_server *as) { pthread_attr_t attr; if(pthread_attr_init(&attr) || pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) || pthread_create(&(as->thr), &attr, run_auth_server_thread, as)) { perror("Cannot create auth thread\n"); exit(-1); } } static void* run_admin_server_thread(void *arg) { ignore_sigpipe(); setup_admin_thread(); barrier_wait(); while(adminserver.event_base) { run_events(adminserver.event_base,NULL); } return arg; } static void setup_admin_server(void) { ns_bzero(&adminserver,sizeof(struct admin_server)); adminserver.listen_fd = -1; adminserver.verbose = turn_params.verbose; if(pthread_create(&(adminserver.thr), NULL, run_admin_server_thread, &adminserver)) { perror("Cannot create cli thread\n"); exit(-1); } pthread_detach(adminserver.thr); } void setup_server(void) { evthread_use_pthreads(); pthread_mutex_init(&mutex_bps, NULL); authserver_number = 1 + (authserver_id)(turn_params.cpus / 2); if(authserver_number < MIN_AUTHSERVER_NUMBER) authserver_number = MIN_AUTHSERVER_NUMBER; #if !defined(TURN_NO_THREAD_BARRIERS) /* relay threads plus auth threads plus main listener thread */ /* plus admin thread */ /* udp address listener thread(s) will start later */ barrier_count = turn_params.general_relay_servers_number+authserver_number+1+1; #endif setup_listener(); allocate_relay_addrs_ports(); setup_barriers(); setup_general_relay_servers(); if(turn_params.net_engine_version == NEV_UDP_SOCKET_PER_THREAD) setup_socket_per_thread_udp_listener_servers(); else if(turn_params.net_engine_version == NEV_UDP_SOCKET_PER_ENDPOINT) setup_socket_per_endpoint_udp_listener_servers(); else if(turn_params.net_engine_version == NEV_UDP_SOCKET_PER_SESSION) setup_socket_per_session_udp_listener_servers(); if(turn_params.net_engine_version != NEV_UDP_SOCKET_PER_THREAD) { setup_tcp_listener_servers(turn_params.listener.ioa_eng, NULL); } { int tot = 0; if(udp_relay_servers[0]) { tot = get_real_udp_relay_servers_number(); } if(tot) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Total UDP servers: %d\n",(int)tot); } } { int tot = get_real_general_relay_servers_number(); if(tot) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Total General servers: %d\n",(int)tot); int i; for(i = 0;i #include #include #include #include #include #include #include #include "ns_turn_utils.h" #include "apputils.h" #include "stun_buffer.h" //////////////////////////////////////////////////// #define OAUTH_TOKEN_SIZE 1000 //TODO: find insted of 1000 the real max of encoded token length #define OAUTH_MAC_KEY_SIZE 32 #define OAUTH_LTK_ID_SIZE 32 #define OAUTH_LTK_SIZE 32 #define OAUTH_LTK_BASE64ENCODED_SIZE 44 #define OAUTH_TOKEN_LIFETIME 3600 #define OAUTH_AS_RS_ALG_SIZE 7 #define OAUTH_SERVER_NAME_SIZE 255 #define OAUTH_GCM_NONCE_BASE64ENCODED_SIZE 16 #define OAUTH_HMAC_ALG_SIZE 20 static int setup_ikm_key(const char *kid, const char *ikm_key, const turn_time_t key_timestamp, const turn_time_t key_lifetime, const char *as_rs_alg, oauth_key *key) { ns_bzero(key,sizeof(*key)); oauth_key_data okd; ns_bzero(&okd,sizeof(okd)); { oauth_key_data_raw okdr; ns_bzero(&okdr,sizeof(okdr)); STRCPY(okdr.kid,kid); STRCPY(okdr.ikm_key,ikm_key); STRCPY(okdr.as_rs_alg,as_rs_alg); okdr.timestamp = key_timestamp; okdr.lifetime = key_lifetime; convert_oauth_key_data_raw(&okdr, &okd); } char err_msg[1025] = "\0"; size_t err_msg_size = sizeof(err_msg) - 1; if (convert_oauth_key_data(&okd, key, err_msg, err_msg_size) < 0) { fprintf(stderr, "%s\n", err_msg); return -1; } return 0; } static int encode_token(const char* server_name, const char* gcm_nonce, const char* mac_key, const uint64_t token_timestamp, const uint32_t token_lifetime, const oauth_key key, char* base64encoded_etoken) { oauth_token ot; ns_bzero(&ot,sizeof(ot)); const size_t mac_key_length=strlen(mac_key); ot.enc_block.key_length = (uint16_t)mac_key_length; STRCPY(ot.enc_block.mac_key,mac_key); ot.enc_block.timestamp = token_timestamp; ot.enc_block.lifetime = token_lifetime; encoded_oauth_token etoken; ns_bzero(&etoken,sizeof(etoken)); // TODO: avoid this hack if (!*gcm_nonce) gcm_nonce=NULL; if (encode_oauth_token((const u08bits *) server_name, &etoken, &key, &ot,(const u08bits *) gcm_nonce) < 0) { fprintf(stderr, "%s: cannot encode oauth token\n", __FUNCTION__); return -1; } size_t base64encoded_etoken_length; const char *tmp=base64_encode((unsigned char *)(etoken.token), etoken.size, &base64encoded_etoken_length); STRCPY(base64encoded_etoken,tmp); return 0; } static int validate_decode_token(const char* server_name, const oauth_key key, const char* base64encoded_etoken, oauth_token* dot) { ns_bzero((dot),sizeof(*dot)); encoded_oauth_token etoken; ns_bzero(&etoken,sizeof(etoken)); const size_t base64encoded_etoken_length=strlen(base64encoded_etoken); const unsigned char *tmp = base64_decode(base64encoded_etoken,base64encoded_etoken_length,&etoken.size); memcpy(etoken.token,tmp,etoken.size); if (decode_oauth_token((const u08bits *) server_name, &etoken, &key, dot) < 0) { fprintf(stderr, "%s: cannot decode oauth token\n", __FUNCTION__); return -1; } else { return 0; }; } static void print_token_body(oauth_token* dot) { printf("\n"); printf("Token non-encrpyted body:\n"); printf("{\n"); size_t base64encoded_nonce_length; const char *base64encoded_nonce = base64_encode((unsigned char *)dot->enc_block.nonce, dot->enc_block.nonce_length,&base64encoded_nonce_length); printf(" nonce: %s\n", base64encoded_nonce); printf(" nonce length: %d\n", (int) dot->enc_block.nonce_length); printf("Token encrpyted body:\n"); printf("{\n"); printf(" mac key: %s\n", (char*) dot->enc_block.mac_key); printf(" mac key length: %d\n", (int) dot->enc_block.key_length); time_t time=dot->enc_block.timestamp>>16; unsigned msec=(dot->enc_block.timestamp & 0xFFFF)*64; printf(" timestamp:\n"); printf(" unixtime: %u (localtime: %s )", (unsigned int)time, ctime(&time)); printf(" msec:%u\n", msec); printf(" lifetime: %lu\n", (unsigned long) dot->enc_block.lifetime); printf("}\n"); } //////////////// local definitions ///////////////// const char Usage[] = "Usage: oauth [ -e / -d ] [options]\n" "Options:\n" "\n" " -h, --help usage\n\n" " -v, --verbose verbose mode\n\n" " -e, --encrypt encrypt token\n" " -d, --decrypt decrypt validate token\n\n" " -i, --server-name server name (max. 255 char)\n" " -j, --auth-key-id Auth key id (max. 32 char)\n" " -k, --auth-key base64 encoded Auth key\n" " -l --auth-key-timestamp Auth key timestamp (sec since epoch)\n" " -m, --auth-key-lifetime Auth key lifetime in sec\n" " -n, --auth-key-as-rs-alg Authorization Server(AS) - Resource Server (RS) encryption algorithm\n" " -o, --token-nonce base64 encoded nonce base64(12 octet) = 16 char\n" " -p, --token-mac-key base64 encoded MAC key base64(32 octet) = 44 char\n" " -q, --token-timestamp timestamp in format 64 bit unsigned (Native format - Unix),\n" " so 48 bit for secs since epoch UTC + 16 bit for 1/64000 fractions of a second.\n" " e.g.: the actual unixtimestamp 16 bit left shifted. (Default: actual gmtime)\n" " -r, --token-lifetime lifetime in sec (Default: 3600)\n" " -t, --token base64 encoded encrypted token for validation and decryption\n" " -u, --hmac-alg stun client hmac algorithm\n"; ////////////////////////////////////////////////// int main(int argc, char **argv) { oauth_key key; //init vars with default values char gcm_nonce[OAUTH_GCM_NONCE_SIZE+1]=""; char mac_key[OAUTH_MAC_KEY_SIZE+1]=""; time_t current_time = time(NULL); struct tm* gmt = gmtime(¤t_time); uint64_t token_timestamp = (unsigned long long)mktime(gmt) << 16; uint32_t token_lifetime = OAUTH_TOKEN_LIFETIME; //oauth_key char kid[OAUTH_LTK_ID_SIZE+1] = ""; char base64encoded_ltk[OAUTH_LTK_BASE64ENCODED_SIZE+1]=""; turn_time_t key_timestamp = 0; turn_time_t key_lifetime = 0; char as_rs_alg[OAUTH_AS_RS_ALG_SIZE+1]="A256GCM"; char server_name[OAUTH_SERVER_NAME_SIZE+1] = ""; char base64encoded_etoken[OAUTH_TOKEN_SIZE]=""; //TODO: replace SHA1 with an option. Actualy both big browser chrome and mozilla supports AFAIU implemented only SHA1. char hmac_alg[OAUTH_HMAC_ALG_SIZE+1]="HMAC-SHA1"; static int verbose_flag=0; static int encrypt_flag=0; static int decrypt_flag=0; static struct option long_options[] = { /* These options set a flag. */ {"verbose", no_argument, &verbose_flag, 1}, {"encrypt", no_argument, &encrypt_flag, 1}, {"decrypt", no_argument, &decrypt_flag, 1}, {"help", no_argument, 0, 'h'}, {"server-name", required_argument, 0, 'i'}, {"auth-key-id", required_argument, 0, 'j'}, {"auth-key", required_argument, 0, 'k'}, {"auth-key-timestamp", required_argument, 0, 'l'}, {"auth-key-lifetime", required_argument, 0, 'm'}, {"auth-key-as-rs-alg", required_argument, 0, 'n'}, {"token-nonce", required_argument, 0, 'o'}, {"token-mac-key", required_argument, 0, 'p'}, {"token-timestamp", required_argument, 0, 'q'}, {"token-lifetime", required_argument, 0, 'r'}, {"token", required_argument, 0, 't'}, {"hmac-alg", required_argument, 0, 'u'}, {0, 0, 0, 0} }; /* getopt_long stores the option index here. */ int option_index = 0; //tmp vars size_t nonce_size=0; char *nonce_val; size_t mac_key_size; char *mac_key_val; int i; int c=0; set_logfile("stdout"); set_system_parameters(0); while ((c = getopt_long(argc, argv, "hvedi:j:k:l:m:n:o:p:q:r:t:u:",long_options, &option_index)) != -1) { switch(c) { case 'h': fprintf(stderr, "%s\n", Usage); exit(1); break; case 'v': verbose_flag=1; break; case 'e': encrypt_flag=1; break; case 'd': decrypt_flag=1; break; case 'i': //server-name if ( strlen(optarg) <= OAUTH_SERVER_NAME_SIZE ) { STRCPY(server_name,optarg); } else { fprintf(stderr,"Server-name must not exceed %d!\n", OAUTH_LTK_ID_SIZE ); exit(1); } break; case 'j': //auth-key-id if ( strlen(optarg) <= OAUTH_LTK_ID_SIZE ) { STRCPY(kid,optarg); } else { fprintf(stderr,"Key ID must not exceed %d!\n", OAUTH_LTK_ID_SIZE ); exit(1); } break; case 'k': //auth-key if ( strlen(optarg) <= OAUTH_LTK_BASE64ENCODED_SIZE ) { STRCPY(base64encoded_ltk,optarg); } else { fprintf(stderr,"Key must not exceed %d!\n", OAUTH_LTK_BASE64ENCODED_SIZE ); exit(1); } break; case 'l': //auth-key-timestamp key_timestamp = atoi(optarg); break; case 'm': //auth-key-lifetime key_lifetime=atoi(optarg); break; case 'n': //auth-key-as-rs-alg if ( strlen(optarg) <= OAUTH_AS_RS_ALG_SIZE ) { STRCPY(as_rs_alg,optarg); } else { fprintf(stderr,"AS-RS Alg must not exceed %d!\n", OAUTH_AS_RS_ALG_SIZE ); exit(1); } break; case 'o': //token-nonce nonce_val = (char*)base64_decode(optarg,strlen(optarg),&nonce_size); if (nonce_size > OAUTH_GCM_NONCE_SIZE){ nonce_size=OAUTH_GCM_NONCE_SIZE; } strncpy(gcm_nonce,nonce_val,nonce_size); gcm_nonce[ nonce_size + 1 ]='\0'; break; case 'p': //token-mac-key mac_key_val = (char*)base64_decode(optarg,strlen(optarg),&mac_key_size); if (mac_key_size > OAUTH_MAC_KEY_SIZE){ mac_key_size=OAUTH_MAC_KEY_SIZE; } strncpy(mac_key,mac_key_val,mac_key_size); mac_key[mac_key_size+1]='\0'; break; case 'q': //token-timestamp token_timestamp=strtoull(optarg,0,10); break; case 'r': //token-lifetime token_lifetime=atoi(optarg); break; case 't': if ( strlen(optarg) <= OAUTH_TOKEN_SIZE ) { STRCPY(base64encoded_etoken,optarg); } else { fprintf(stderr,"base64 encoded encrypted token must not exceed %d!\n", OAUTH_TOKEN_SIZE ); exit(1); } break; case 'u': //hmac-alg if ( strlen(optarg) <= OAUTH_HMAC_ALG_SIZE ) { STRCPY(hmac_alg,optarg); } else { fprintf(stderr,"STUN client HMAC Alg must not exceed %d!\n", OAUTH_HMAC_ALG_SIZE ); exit(1); } break; default: fprintf(stderr,"%s\n", Usage); exit(1); break; } } for (i = optind; i < argc; i++) printf ("Non-option argument %s\n", argv[i]); if(optind>argc) { fprintf(stderr, "%s\n", Usage); exit(-1); } if (!(encrypt_flag || decrypt_flag)){ fprintf(stderr, "Use either encrypt or decrypt.\nPlease use -h or --help for the detailed help\n"); exit(-1); } //check if we have required params //TODO: more compact warnning handling if (encrypt_flag || decrypt_flag){ if (strlen(server_name) == 0) { fprintf(stderr, "For encode/decode --server-name/-i is mandatory \n"); exit(-1); } if (strlen(kid) == 0){ fprintf(stderr, "For encode/decode --auth-key-id/-j is mandatory \n"); exit(-1); } if (strlen(base64encoded_ltk) == 0){ fprintf(stderr, "For encode/decode --auth-key/-k is mandatory \n"); exit(-1); } if (key_timestamp == 0){ fprintf(stderr, "For encode/decode --auth-key-timestamp/-l is mandatory \n"); exit(-1); } if (key_lifetime == 0){ fprintf(stderr, "For encode/decode --auth-key-lifetime/-m is mandatory \n"); exit(-1); } if (encrypt_flag && strlen(mac_key) == 0) { fprintf(stderr, "For encode --token-mac-key/-p is mandatory \n"); exit(-1); } if (!encrypt_flag && decrypt_flag && strlen(base64encoded_etoken) == 0) { fprintf(stderr, "For decode --token/-t is mandatory \n"); exit(-1); } // Expiry warnings if ( (unsigned long long)key_timestamp<<16 > token_timestamp +((unsigned long long)token_lifetime << 16) ) { fprintf(stderr,"\nWARNING: Token expiry is earlear then Auth key life time start timestamp!!\n\n"); } else { if( (unsigned long long)key_timestamp<<16 > token_timestamp) { fprintf(stderr,"\nWARNING: Token life time start timestamp is earlier then Auth key start timestamp!!\n\n"); } } if( (unsigned long long)( key_timestamp + key_lifetime )<<16 < token_timestamp ) { fprintf(stderr,"\nWARNING: Auth key will expire before token lifetime start timestamp!!\n\n"); } else { if( (unsigned long long)( key_timestamp + key_lifetime)<<16 < token_timestamp + ((unsigned long long)token_lifetime << 16) ) { fprintf(stderr,"\nWARNING: Auth key will expire before token expiry!!\n\n"); } } if ( setup_ikm_key(kid, base64encoded_ltk, key_timestamp, key_lifetime, as_rs_alg, &key) == 0 ) { if(encrypt_flag) { if (encode_token(server_name, gcm_nonce, mac_key, token_timestamp, token_lifetime, key, base64encoded_etoken) == 0 ) { printf("{\n"); printf(" \"access_token\":\"%s\",\n",base64encoded_etoken); printf(" \"token_type\":\"pop\",\n"); printf(" \"expires_in\":%d,\n",token_lifetime); printf(" \"kid\":\"%s\",\n",kid); printf(" \"key\":\"%s\",\n",mac_key); printf(" \"alg\":\"%s\"\n",hmac_alg); printf("}\n"); } else { fprintf(stderr, "Error during token encode\n"); exit(-1); } } if (decrypt_flag) { oauth_token dot; if ( validate_decode_token(server_name, key, base64encoded_etoken,&dot) == 0) { printf("-=Valid token!=-\n"); if (verbose_flag) print_token_body(&dot); } else { fprintf(stderr, "Error during token validation and decoding\n"); exit(-1); } } } else { fprintf(stderr, "Error during key setup\n"); exit(-1); } } return 0; } turnserver-4.5.1.1/src/client++/0000775000175000017500000000000013436577655015005 5ustar misimisiturnserver-4.5.1.1/src/client++/TurnMsgLib.h0000664000175000017500000006773613436577655017227 0ustar misimisi/* * 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. */ #ifndef __LIB_TURN_MSG_CPP__ #define __LIB_TURN_MSG_CPP__ #include "ns_turn_ioaddr.h" #include "ns_turn_msg.h" #include namespace turn { class StunAttr; /** * Exception "end of buffer" */ class EndOfStunMsgException { public: EndOfStunMsgException() {} virtual ~EndOfStunMsgException() {} }; /** * Exception "wrong format of StunAttr" */ class WrongStunAttrFormatException { public: WrongStunAttrFormatException() {} virtual ~WrongStunAttrFormatException() {} }; /** * Exception "wrong format of StunBuffer" */ class WrongStunBufferFormatException { public: WrongStunBufferFormatException() {} virtual ~WrongStunBufferFormatException() {} }; /** * Iterator class for attributes */ class StunAttrIterator { public: /** * Iterator constructor: creates iterator on raw messagebuffer. */ StunAttrIterator(u08bits *buf, size_t sz) throw (WrongStunBufferFormatException) : _buf(buf), _sz(sz) { if(!stun_is_command_message_str(_buf, _sz)) { throw WrongStunBufferFormatException(); } _sar = stun_attr_get_first_str(_buf, _sz); } /** * Iterator constructor: create iterator over message. */ template StunAttrIterator(T &msg) throw (WrongStunBufferFormatException) : _buf(msg.getRawBuffer()), _sz(msg.getSize()) { if(!stun_is_command_message_str(_buf, _sz)) { throw WrongStunBufferFormatException(); } _sar = stun_attr_get_first_str(_buf, _sz); } /** * Iterator constructor: creates iterator over raw buffer, starting from first * location of an attribute of particular type. */ StunAttrIterator(u08bits *buf, size_t sz, u16bits attr_type) throw (WrongStunBufferFormatException) : _buf(buf), _sz(sz) { if(!stun_is_command_message_str(_buf, _sz)) { throw WrongStunBufferFormatException(); } _sar = stun_attr_get_first_by_type_str(_buf, _sz, attr_type); } /** * Iterator constructor: creates iterator over message, starting from first * location of an attribute of particular type. */ template StunAttrIterator(T &msg, u16bits attr_type) throw (WrongStunBufferFormatException) : _buf(msg.getRawBuffer()), _sz(msg.getSize()) { if(!stun_is_command_message_str(_buf, _sz)) { throw WrongStunBufferFormatException(); } _sar = stun_attr_get_first_by_type_str(_buf, _sz, attr_type); } /** * Moves iterator to next attribute location */ void next() throw(EndOfStunMsgException) { if(!_sar) { throw EndOfStunMsgException(); } _sar = stun_attr_get_next_str(_buf,_sz,_sar); } /** * Is the iterator finished */ bool eof() const { return (!_sar); } /** * Is the iterator at an address attribute */ bool isAddr() const { return stun_attr_is_addr(_sar); } /** * Return address family attribute value (if the iterator at the "address family" attribute. */ int getAddressFamily() const { return stun_get_requested_address_family(_sar); } /** * Get attribute type */ int getType() const { return stun_attr_get_type(_sar); } /** * Destructor */ virtual ~StunAttrIterator() {} /** * Return raw memroy field of the attribute value. * If the attribute value length is zero (0), then return NULL. */ const u08bits *getRawBuffer(size_t &sz) const throw(WrongStunAttrFormatException) { int len = stun_attr_get_len(_sar); if(len<0) throw WrongStunAttrFormatException(); sz = (size_t)len; const u08bits *value = stun_attr_get_value(_sar); return value; } friend class StunAttr; private: u08bits *_buf; size_t _sz; stun_attr_ref _sar; }; /** * Root class of all STUN attributes. * Can be also used for a generic attribute object. */ class StunAttr { public: /** * Empty constructor */ StunAttr() : _attr_type(0), _value(0), _sz(0) {} /** * Constructs attribute from iterator */ StunAttr(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) { if(iter.eof()) { throw EndOfStunMsgException(); } size_t sz = 0; const u08bits *ptr = iter.getRawBuffer(sz); if(sz>=0xFFFF) throw WrongStunAttrFormatException(); int at = iter.getType(); if(at<0) throw WrongStunAttrFormatException(); _attr_type = (u16bits)at; _sz = sz; _value=(u08bits*)turn_malloc(_sz); if(ptr) ns_bcopy(ptr,_value,_sz); } /** * Destructor */ virtual ~StunAttr() { if(_value) turn_free(_value,_sz); } /** * Return raw data representation of the attribute */ const u08bits *getRawValue(size_t &sz) const { sz=_sz; return _value; } /** * Set raw data value */ void setRawValue(u08bits *value, size_t sz) throw(WrongStunAttrFormatException) { if(sz>0xFFFF) throw WrongStunAttrFormatException(); if(_value) turn_free(_value,_sz); _sz = sz; _value=(u08bits*)turn_malloc(_sz); if(value) ns_bcopy(value,_value,_sz); } /** * Get attribute type */ u16bits getType() const { return _attr_type; } /** * Set attribute type */ void setType(u16bits at) { _attr_type = at; } /** * Add attribute to a message */ template int addToMsg(T &msg) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { if(!_attr_type) throw WrongStunAttrFormatException(); u08bits *buffer = msg.getRawBuffer(); if(buffer) { size_t sz = msg.getSize(); if(addToBuffer(buffer, sz)<0) { throw WrongStunBufferFormatException(); } msg.setSize(sz); return 0; } throw WrongStunBufferFormatException(); } protected: /** * Virtual function member to add attribute to a raw buffer */ virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { if(buffer) { if(!_value) throw WrongStunAttrFormatException(); if(stun_attr_add_str(buffer, &sz, _attr_type, _value, _sz)<0) { throw WrongStunBufferFormatException(); } return 0; } throw WrongStunBufferFormatException(); } /** * Get low-level iterator object */ static stun_attr_ref getSar(const StunAttrIterator &iter) { return iter._sar; } private: u16bits _attr_type; u08bits *_value; size_t _sz; }; /** * Channel number attribute class */ class StunAttrChannelNumber : public StunAttr { public: StunAttrChannelNumber() : _cn(0) { setType(STUN_ATTRIBUTE_CHANNEL_NUMBER); } StunAttrChannelNumber(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); _cn = stun_attr_get_channel_number(getSar(iter)); if(!_cn) throw WrongStunAttrFormatException(); } virtual ~StunAttrChannelNumber() {} u16bits getChannelNumber() const { return _cn; } void setChannelNumber(u16bits cn) { _cn = cn; } protected: virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { return stun_attr_add_channel_number_str(buffer,&sz,_cn); } private: u16bits _cn; }; /** * Even port attribute class */ class StunAttrEvenPort : public StunAttr { public: StunAttrEvenPort() : _ep(0) { setType(STUN_ATTRIBUTE_EVEN_PORT); } StunAttrEvenPort(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); _ep = stun_attr_get_even_port(getSar(iter)); } virtual ~StunAttrEvenPort() {} u08bits getEvenPort() const { return _ep; } void setEvenPort(u08bits ep) { _ep = ep; } protected: virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { return stun_attr_add_str(buffer, &sz, STUN_ATTRIBUTE_EVEN_PORT, &_ep, 1); } private: u08bits _ep; }; /** * Reservation token attribute class */ class StunAttrReservationToken : public StunAttr { public: StunAttrReservationToken() : _rt(0) { setType(STUN_ATTRIBUTE_RESERVATION_TOKEN); } StunAttrReservationToken(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); _rt = stun_attr_get_reservation_token_value(getSar(iter)); } virtual ~StunAttrReservationToken() {} u64bits getReservationToken() const { return _rt; } void setReservationToken(u64bits rt) { _rt = rt; } protected: virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { uint64_t reservation_token = ioa_ntoh64(_rt); return stun_attr_add_str(buffer, &sz, STUN_ATTRIBUTE_RESERVATION_TOKEN, (u08bits*) (&reservation_token), 8); } private: u64bits _rt; }; /** * This attribute class is used for all address attributes */ class StunAttrAddr : public StunAttr { public: StunAttrAddr(u16bits attr_type = 0) { addr_set_any(&_addr); setType(attr_type); } StunAttrAddr(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); size_t sz = 0; const u08bits *buf = iter.getRawBuffer(sz); if(stun_attr_get_addr_str(buf,sz,getSar(iter),&_addr,NULL)<0) { throw WrongStunAttrFormatException(); } } virtual ~StunAttrAddr() {} void getAddr(ioa_addr &addr) const { addr_cpy(&addr,&_addr); } void setAddr(ioa_addr &addr) { addr_cpy(&_addr,&addr); } protected: virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { return stun_attr_add_addr_str(buffer, &sz, getType(), &_addr); } private: ioa_addr _addr; }; /** * Change Request attribute class */ class StunAttrChangeRequest : public StunAttr { public: StunAttrChangeRequest() : _changeIp(0), _changePort(0) { setType(STUN_ATTRIBUTE_CHANGE_REQUEST); } StunAttrChangeRequest(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); if(stun_attr_get_change_request_str(getSar(iter), &_changeIp, &_changePort)<0) { throw WrongStunAttrFormatException(); } } virtual ~StunAttrChangeRequest() {} bool getChangeIp() const { return _changeIp; } void setChangeIp(bool ci) { if(ci) _changeIp = 1; else _changeIp = 0; } bool getChangePort() const { return _changePort; } void setChangePort(bool cp) { if(cp) _changePort = 1; else _changePort = 0; } protected: virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { return stun_attr_add_change_request_str(buffer, &sz, _changeIp, _changePort); } private: int _changeIp; int _changePort; }; /** * Change Request attribute class */ class StunAttrResponsePort : public StunAttr { public: StunAttrResponsePort() : _rp(0) { setType(STUN_ATTRIBUTE_RESPONSE_PORT); } StunAttrResponsePort(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); int rp = stun_attr_get_response_port_str(getSar(iter)); if(rp<0) { throw WrongStunAttrFormatException(); } _rp = (u16bits)rp; } virtual ~StunAttrResponsePort() {} u16bits getResponsePort() const { return _rp; } void setResponsePort(u16bits p) { _rp = p; } protected: virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { return stun_attr_add_response_port_str(buffer, &sz, _rp); } private: u16bits _rp; }; /** * Padding attribute class */ class StunAttrPadding : public StunAttr { public: StunAttrPadding() : _p(0) { setType(STUN_ATTRIBUTE_PADDING); } StunAttrPadding(const StunAttrIterator &iter) throw(WrongStunAttrFormatException, EndOfStunMsgException) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); int p = stun_attr_get_padding_len_str(getSar(iter)); if(p<0) { throw WrongStunAttrFormatException(); } _p = (u16bits)p; } virtual ~StunAttrPadding() {} u16bits getPadding() const { return _p; } /** * Set length of padding */ void setPadding(u16bits p) { _p = p; } protected: virtual int addToBuffer(u08bits *buffer, size_t &sz) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { return stun_attr_add_padding_str(buffer, &sz, _p); } private: u16bits _p; }; /** * Generic "STUN Message" class, base class for all messages */ class StunMsg { public: /** * Empty constructor */ StunMsg() { _allocated_sz = 0xFFFF; _buffer = (u08bits*)turn_malloc(_allocated_sz); _deallocate = true; _sz = 0; _constructed = 0; } /** * Construct message over raw buffer. * Parameter "construct" is true if the buffer is initialized. */ StunMsg(u08bits *buffer, size_t total_sz, size_t sz, bool constructed) : _buffer(buffer), _deallocate(false), _allocated_sz(total_sz), _sz(sz), _constructed(constructed) {} /** * Destructor */ virtual ~StunMsg() { if(_deallocate && _buffer) { turn_free(_buffer, _allocated_sz); } } /** * Initialize buffer */ void construct() { constructBuffer(); } /** * Checks if the message is properly constructed */ bool isValid() { return check(); } /** * get raw buffer */ u08bits *getRawBuffer() { return _buffer; } /** * Get message size in the buffer (message can be mnuch smaller than the whole buffer) */ size_t getSize() const { return _sz; } /** * Set message size */ void setSize(size_t sz) throw(WrongStunBufferFormatException) { if(sz>_allocated_sz) throw WrongStunBufferFormatException(); _sz = sz; } /** * Check if the raw buffer is a TURN "command" (request, response or indication). */ static bool isCommand(u08bits *buffer, size_t sz) { return stun_is_command_message_str(buffer, sz); } /** * Check if the current message object is a "command" (request, response, or indication). */ bool isCommand() const { return stun_is_command_message_str(_buffer, _sz); } static bool isIndication(u08bits *buffer, size_t sz) { return stun_is_indication_str(buffer, sz); } static bool isRequest(u08bits *buffer, size_t sz) { return stun_is_request_str(buffer, sz); } static bool isSuccessResponse(u08bits *buffer, size_t sz) { return stun_is_success_response_str(buffer, sz); } static bool isErrorResponse(u08bits *buffer, size_t sz, int &err_code, u08bits *err_msg, size_t err_msg_size) { return stun_is_error_response_str(buffer, sz, &err_code, err_msg, err_msg_size); } /** * Check if the raw buffer is a challenge response (the one with 401 error and realm and nonce values). */ static bool isChallengeResponse(const u08bits* buf, size_t sz, int &err_code, u08bits *err_msg, size_t err_msg_size, u08bits *realm, u08bits *nonce, u08bits *server_name, int *oauth) { return stun_is_challenge_response_str(buf, sz, &err_code, err_msg, err_msg_size, realm, nonce, server_name, oauth); } /** * Check if the message is a channel message */ static bool isChannel(u08bits *buffer, size_t sz) { return is_channel_msg_str(buffer, sz); } /** * Check if the fingerprint is present. */ static bool isFingerprintPresent(u08bits *buffer, size_t sz) { if(!stun_is_command_message_str(buffer,sz)) return false; stun_attr_ref sar = stun_attr_get_first_by_type_str(buffer, sz, STUN_ATTRIBUTE_FINGERPRINT); if(!sar) return false; return true; } /** * Check the fingerprint */ static bool checkFingerprint(u08bits *buffer, size_t sz) { return stun_is_command_message_full_check_str(buffer, sz, 1, NULL); } /** * Add attribute to the message */ int addAttr(StunAttr &attr) throw(WrongStunAttrFormatException, WrongStunBufferFormatException) { return attr.addToMsg(*this); } /** * Get transaction ID */ virtual stun_tid getTid() const throw(WrongStunBufferFormatException) { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); stun_tid tid; stun_tid_from_message_str(_buffer,_sz,&tid); return tid; } /** * Set transaction ID */ virtual void setTid(stun_tid &tid) throw(WrongStunBufferFormatException) { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); stun_tid_message_cpy(_buffer, &tid); } /** * Add fingerprint to the message */ void addFingerprint() throw(WrongStunBufferFormatException) { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); stun_attr_add_fingerprint_str(_buffer,&_sz); } /** * Check message integrity, in secure communications. */ bool checkMessageIntegrity(turn_credential_type ct, std::string &uname, std::string &realm, std::string &upwd) const throw(WrongStunBufferFormatException) { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); u08bits *suname=(u08bits*)strdup(uname.c_str()); u08bits *srealm=(u08bits*)strdup(realm.c_str()); u08bits *supwd=(u08bits*)strdup(upwd.c_str()); SHATYPE sht = SHATYPE_SHA1; bool ret = (0< stun_check_message_integrity_str(ct,_buffer, _sz, suname, srealm, supwd, sht)); free(suname); free(srealm); free(supwd); return ret; } /** * Adds long-term message integrity data to the message. */ void addLTMessageIntegrity(std::string &uname, std::string &realm, std::string &upwd, std::string &nonce) throw(WrongStunBufferFormatException) { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); u08bits *suname=(u08bits*)strdup(uname.c_str()); u08bits *srealm=(u08bits*)strdup(realm.c_str()); u08bits *supwd=(u08bits*)strdup(upwd.c_str()); u08bits *snonce=(u08bits*)strdup(nonce.c_str()); stun_attr_add_integrity_by_user_str(_buffer, &_sz, suname, srealm, supwd, snonce, SHATYPE_SHA1); free(suname); free(srealm); free(supwd); free(snonce); } /** * Adds short-term message integrity data to the message. */ void addSTMessageIntegrity(std::string &uname, std::string &upwd) throw(WrongStunBufferFormatException) { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); u08bits *suname=(u08bits*)strdup(uname.c_str()); u08bits *supwd=(u08bits*)strdup(upwd.c_str()); stun_attr_add_integrity_by_user_short_term_str(_buffer, &_sz, suname, supwd, SHATYPE_SHA1); free(suname); free(supwd); } protected: virtual void constructBuffer() = 0; virtual bool check() = 0; protected: u08bits *_buffer; bool _deallocate; size_t _allocated_sz; size_t _sz; bool _constructed; }; /** * Class that represents the "request" flavor of STUN/TURN messages. */ class StunMsgRequest : public StunMsg { public: StunMsgRequest(u16bits method) : _method(method) {}; StunMsgRequest(u08bits *buffer, size_t total_sz, size_t sz, bool constructed) throw(WrongStunBufferFormatException) : StunMsg(buffer,total_sz,sz,constructed),_method(0) { if(constructed) { if(!stun_is_request_str(buffer,sz)) { throw WrongStunBufferFormatException(); } _method = stun_get_method_str(buffer,sz); } } virtual ~StunMsgRequest() {} /** * Get request method */ u16bits getMethod() const { return _method; } /** * Set method */ void setMethod(u16bits method) { _method = method; } /** * Construct binding request */ void constructBindingRequest() { stun_set_binding_request_str(_buffer, &_sz); } bool isBindingRequest() const { return stun_is_binding_request_str(_buffer,_sz,0); } /** * Construct allocate request */ void constructAllocateRequest(u32bits lifetime, int af4, int af6, u08bits transport, int mobile, const char* rt, int ep) { stun_set_allocate_request_str(_buffer, &_sz, lifetime, af4, af6, transport, mobile, rt, ep); } /** * Construct channel bind request */ void constructChannelBindRequest(const ioa_addr &peer_addr, u16bits channel_number) { stun_set_channel_bind_request_str(_buffer, &_sz, &peer_addr, channel_number); } protected: virtual void constructBuffer() { stun_init_request_str(_method,_buffer,&_sz); _constructed = true; } virtual bool check() { if(!_constructed) return false; if(!stun_is_request_str(_buffer,_sz)) { return false; } if(_method != stun_get_method_str(_buffer,_sz)) { return false; } return true; } private: u16bits _method; }; /** * Class for STUN/TURN responses */ class StunMsgResponse : public StunMsg { public: StunMsgResponse(u16bits method, stun_tid &tid) : _method(method), _err(0), _reason(""), _tid(tid) {}; StunMsgResponse(u16bits method, int error_code, std::string reason, stun_tid &tid) : _method(method), _err(error_code), _reason(reason), _tid(tid) { }; StunMsgResponse(u08bits *buffer, size_t total_sz, size_t sz, bool constructed) throw(WrongStunBufferFormatException) : StunMsg(buffer,total_sz,sz,constructed),_method(0),_err(0),_reason("") { if(constructed) { if(!stun_is_success_response_str(buffer,sz)) { u08bits errtxt[0xFFFF]; if(!stun_is_error_response_str(buffer,sz,&_err,errtxt,sizeof(errtxt))) { throw WrongStunBufferFormatException(); } _reason = (char*)errtxt; } _method = stun_get_method_str(buffer,sz); stun_tid_from_message_str(_buffer,_sz,&_tid); } } u16bits getMethod() const { return _method; } void setMethod(u16bits method) { _method = method; } /** * Get error code */ int getError() const { return _err; } /** * Set error code */ void setError(int err) { _err = err; } /** * Get error message */ std::string getReason() const { return _reason; } /** * Set error message */ void setReason(std::string reason) { _reason = reason; } /** * Set transaction ID */ void setTid(stun_tid &tid) throw(WrongStunBufferFormatException) { _tid = tid; } /** * Get transaction ID */ virtual stun_tid getTid() const throw(WrongStunBufferFormatException) { return _tid; } /** * Check if this is a challenge response, and return realm and nonce */ bool isChallenge(std::string &realm, std::string &nonce) const { bool ret = false; if(_constructed) { int err_code; u08bits err_msg[1025]; size_t err_msg_size=sizeof(err_msg); u08bits srealm[0xFFFF]; u08bits snonce[0xFFFF]; ret = stun_is_challenge_response_str(_buffer, _sz, &err_code, err_msg, err_msg_size, srealm, snonce, NULL, NULL); if(ret) { realm = (char*)srealm; nonce = (char*)snonce; } } return ret; } bool isChallenge() const { std::string realm, nonce; return isChallenge(realm, nonce); } /** * Check if this is a success response */ bool isSuccess() const { return (_err == 0); } /** * Construct binding response */ void constructBindingResponse(stun_tid &tid, const ioa_addr &reflexive_addr, int error_code, const u08bits *reason) { stun_set_binding_response_str(_buffer, &_sz, &tid, &reflexive_addr, error_code, reason, 0 , 0); } bool isBindingResponse() const { return stun_is_binding_response_str(_buffer,_sz); } /** * Construct allocate response */ void constructAllocateResponse(stun_tid &tid, const ioa_addr &relayed_addr1, const ioa_addr &relayed_addr2, const ioa_addr &reflexive_addr, u32bits lifetime, int error_code, const u08bits *reason, u64bits reservation_token, char *mobile_id) { stun_set_allocate_response_str(_buffer, &_sz, &tid, &relayed_addr1, &relayed_addr2, &reflexive_addr, lifetime, STUN_DEFAULT_MAX_ALLOCATE_LIFETIME, error_code, reason, reservation_token, mobile_id); } /** * Construct channel bind response */ void constructChannelBindResponse(stun_tid &tid, int error_code, const u08bits *reason) { stun_set_channel_bind_response_str(_buffer, &_sz, &tid, error_code, reason); } protected: virtual void constructBuffer() { if(_err) { stun_init_error_response_str(_method, _buffer, &_sz, _err, (const u08bits*)_reason.c_str(), &_tid); } else { stun_init_success_response_str(_method, _buffer, &_sz, &_tid); } _constructed = true; } virtual bool check() { if(!_constructed) return false; if(!stun_is_success_response_str(_buffer,_sz)) { u08bits errtxt[0xFFFF]; int cerr=0; if(!stun_is_error_response_str(_buffer,_sz,&cerr,errtxt,sizeof(errtxt))) { throw WrongStunBufferFormatException(); } if(cerr != _err) { throw WrongStunBufferFormatException(); } } if(_method != stun_get_method_str(_buffer,_sz)) { return false; } return true; } private: u16bits _method; int _err; std::string _reason; stun_tid _tid; }; /** * Class for STUN/TURN indications */ class StunMsgIndication : public StunMsg { public: StunMsgIndication(u16bits method) : _method(method) {}; StunMsgIndication(u08bits *buffer, size_t total_sz, size_t sz, bool constructed) throw(WrongStunBufferFormatException) : StunMsg(buffer,total_sz,sz,constructed),_method(0) { if(constructed) { if(!stun_is_indication_str(buffer,sz)) { throw WrongStunBufferFormatException(); } _method = stun_get_method_str(buffer,sz); } } virtual ~StunMsgIndication() {} u16bits getMethod() const { return _method; } void setMethod(u16bits method) { _method = method; } protected: virtual void constructBuffer() { stun_init_indication_str(_method,_buffer,&_sz); _constructed = true; } virtual bool check() { if(!_constructed) return false; if(!stun_is_indication_str(_buffer,_sz)) { return false; } if(_method != stun_get_method_str(_buffer,_sz)) { return false; } return true; } private: u16bits _method; }; /** * Channel message */ class StunMsgChannel : public StunMsg { public: StunMsgChannel(u16bits cn, int length) : _cn(cn), _len(length) {}; StunMsgChannel(u08bits *buffer, size_t total_sz, size_t sz, bool constructed) throw(WrongStunBufferFormatException) : StunMsg(buffer,total_sz,sz,constructed),_cn(0) { if(constructed) { if(!stun_is_channel_message_str(buffer,&_sz,&_cn,0)) { throw WrongStunBufferFormatException(); } if(_sz>0xFFFF || _sz<4) throw WrongStunBufferFormatException(); _len = _sz-4; } else { if(total_sz>0xFFFF || total_sz<4) throw WrongStunBufferFormatException(); _len = 0; } } virtual ~StunMsgChannel() {} u16bits getChannelNumber() const { return _cn; } void setChannelNumber(u16bits cn) { _cn = cn; } /** * Get length of message itself (excluding the 4 channel number bytes) */ size_t getLength() const { return _len; } /** * Set length of message itself (excluding the 4 channel number bytes) */ void setLength(size_t len) { _len = len; } protected: virtual void constructBuffer() { stun_init_channel_message_str(_cn,_buffer,&_sz,(int)_len,0); _constructed = true; } virtual bool check() { if(!_constructed) return false; u16bits cn = 0; if(!stun_is_channel_message_str(_buffer,&_sz,&cn,0)) { return false; } if(_cn != cn) { return false; } return true; } private: u16bits _cn; size_t _len; }; }; /* namespace */ #endif /* __LIB_TURN_MSG_CPP__ */ turnserver-4.5.1.1/src/client/0000775000175000017500000000000013436577655014657 5ustar misimisiturnserver-4.5.1.1/src/client/ns_turn_msg_addr.c0000664000175000017500000001120413436577655020351 0ustar misimisi/* * 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_msg_addr.h" ////////////////////////////////////////////////////////////////////////////// int stun_addr_encode(const ioa_addr* ca, u08bits *cfield, int *clen, int xor_ed, u32bits mc, const u08bits *tsx_id) { if(!cfield || !clen || !ca || !tsx_id) return -1; if (ca->ss.sa_family == AF_INET || ca->ss.sa_family==0) { /* IPv4 address */ *clen=8; cfield[0]=0; cfield[1]=1; //IPv4 family if (xor_ed) { /* Port */ ((u16bits*)cfield)[1] = (ca->s4.sin_port) ^ nswap16(mc >> 16); /* Address */ ((u32bits*)cfield)[1] = (ca->s4.sin_addr.s_addr) ^ nswap32(mc); } else { /* Port */ ((u16bits*)cfield)[1]=ca->s4.sin_port; /* Address */ ((u32bits*)cfield)[1]=ca->s4.sin_addr.s_addr; } } else if (ca->ss.sa_family == AF_INET6) { /* IPv6 address */ *clen=20; cfield[0]=0; cfield[1]=2; //IPv6 family if (xor_ed) { unsigned int i; u08bits *dst = ((u08bits*)cfield)+4; const u08bits *src = (const u08bits*)&(ca->s6.sin6_addr); u32bits magic = nswap32(mc); /* Port */ ((u16bits*)cfield)[1] = ca->s6.sin6_port ^ nswap16(mc >> 16); /* Address */ for (i=0; i<4; ++i) { dst[i] = (u08bits)(src[i] ^ ((const u08bits*)&magic)[i]); } for (i=0; i<12; ++i) { dst[i+4] = (u08bits)(src[i+4] ^ tsx_id[i]); } } else { /* Port */ ((u16bits*)cfield)[1]=ca->s6.sin6_port; /* Address */ ns_bcopy(&ca->s6.sin6_addr, ((u08bits*)cfield)+4, 16); } } else { return -1; } return 0; } int stun_addr_decode(ioa_addr* ca, const u08bits *cfield, int len, int xor_ed, u32bits mc, const u08bits *tsx_id) { if(!cfield || !len || !ca || !tsx_id || (len<8)) return -1; if(cfield[0]!=0) { return -1; } int sa_family; if(cfield[1]==1) sa_family=AF_INET; else if(cfield[1]==2) sa_family=AF_INET6; else return -1; ca->ss.sa_family=sa_family; if (sa_family == AF_INET) { if(len!=8) return -1; /* IPv4 address */ /* Port */ ca->s4.sin_port=((const u16bits*)cfield)[1]; /* Address */ ca->s4.sin_addr.s_addr=((const u32bits*)cfield)[1]; if (xor_ed) { ca->s4.sin_port ^= nswap16(mc >> 16); ca->s4.sin_addr.s_addr ^= nswap32(mc); } } else if (sa_family == AF_INET6) { /* IPv6 address */ if(len!=20) return -1; /* Port */ ca->s6.sin6_port = ((const u16bits*)cfield)[1]; /* Address */ ns_bcopy(((const u08bits*)cfield)+4, &ca->s6.sin6_addr, 16); if (xor_ed) { unsigned int i; u08bits *dst; const u08bits *src; u32bits magic = nswap32(mc); /* Port */ ca->s6.sin6_port ^= nswap16(mc >> 16); /* Address */ src = ((const u08bits*)cfield)+4; dst = (u08bits*)&ca->s6.sin6_addr; for (i=0; i<4; ++i) { dst[i] = (u08bits)(src[i] ^ ((const u08bits*)&magic)[i]); } for (i=0; i<12; ++i) { dst[i+4] = (u08bits)(src[i+4] ^ tsx_id[i]); } } } else { return -1; } return 0; } ////////////////////////////////////////////////////////////////////////////// turnserver-4.5.1.1/src/client/ns_turn_msg.c0000664000175000017500000021513113436577655017364 0ustar misimisi/* * 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_msg.h" #include "ns_turn_msg_addr.h" ///////////// Security functions implementation from ns_turn_msg.h /////////// #include "ns_turn_openssl.h" /////////// #include /////////// static void generate_random_nonce(unsigned char *nonce, size_t sz); /////////// int stun_method_str(u16bits method, char *smethod) { int ret = 0; const char* s = "UNKNOWN"; switch(method) { case STUN_METHOD_BINDING: s = "BINDING"; break; case STUN_METHOD_ALLOCATE: s = "ALLOCATE"; break; case STUN_METHOD_REFRESH: s = "REFRESH"; break; case STUN_METHOD_SEND: s = "SEND"; break; case STUN_METHOD_DATA: s = "DATA"; break; case STUN_METHOD_CREATE_PERMISSION: s = "CREATE_PERMISSION"; break; case STUN_METHOD_CHANNEL_BIND: s = "CHANNEL_BIND"; break; case STUN_METHOD_CONNECT: s = "CONNECT"; break; case STUN_METHOD_CONNECTION_BIND: s = "CONNECTION_BIND"; break; case STUN_METHOD_CONNECTION_ATTEMPT: s = "CONNECTION_ATTEMPT"; break; default: ret = -1; }; if(smethod) { ns_bcopy(s,smethod,strlen(s)+1); } return ret; } long turn_random(void) { long ret = 0; if(!RAND_bytes((unsigned char *)&ret,sizeof(ret))) ret = random(); return ret; } static void turn_random_tid_size(void *id) { u32bits *ar=(u32bits*)id; if(!RAND_pseudo_bytes((unsigned char *)ar,12)) { size_t i; for(i=0;i<3;++i) { ar[i] = (u32bits)random(); } } } int stun_calculate_hmac(const u08bits *buf, size_t len, const u08bits *key, size_t keylen, u08bits *hmac, unsigned int *hmac_len, SHATYPE shatype) { ERR_clear_error(); UNUSED_ARG(shatype); if(shatype == SHATYPE_SHA256) { #if !defined(OPENSSL_NO_SHA256) && defined(SHA256_DIGEST_LENGTH) if (!HMAC(EVP_sha256(), key, keylen, buf, len, hmac, hmac_len)) { return -1; } #else fprintf(stderr,"SHA256 is not supported\n"); return -1; #endif } else if(shatype == SHATYPE_SHA384) { #if !defined(OPENSSL_NO_SHA384) && defined(SHA384_DIGEST_LENGTH) if (!HMAC(EVP_sha384(), key, keylen, buf, len, hmac, hmac_len)) { return -1; } #else fprintf(stderr,"SHA384 is not supported\n"); return -1; #endif } else if(shatype == SHATYPE_SHA512) { #if !defined(OPENSSL_NO_SHA512) && defined(SHA512_DIGEST_LENGTH) if (!HMAC(EVP_sha512(), key, keylen, buf, len, hmac, hmac_len)) { return -1; } #else fprintf(stderr,"SHA512 is not supported\n"); return -1; #endif } else if (!HMAC(EVP_sha1(), key, keylen, buf, len, hmac, hmac_len)) { return -1; } return 0; } int stun_produce_integrity_key_str(u08bits *uname, u08bits *realm, u08bits *upwd, hmackey_t key, SHATYPE shatype) { ERR_clear_error(); UNUSED_ARG(shatype); size_t ulen = strlen((s08bits*)uname); size_t rlen = strlen((s08bits*)realm); size_t plen = strlen((s08bits*)upwd); size_t sz = ulen+1+rlen+1+plen+1+10; size_t strl = ulen+1+rlen+1+plen; u08bits *str = (u08bits*)turn_malloc(sz+1); strncpy((s08bits*)str,(s08bits*)uname,sz); str[ulen]=':'; strncpy((s08bits*)str+ulen+1,(s08bits*)realm,sz-ulen-1); str[ulen+1+rlen]=':'; strncpy((s08bits*)str+ulen+1+rlen+1,(s08bits*)upwd,sz-ulen-1-rlen-1); str[strl]=0; if(shatype == SHATYPE_SHA256) { #if !defined(OPENSSL_NO_SHA256) && defined(SHA256_DIGEST_LENGTH) #if OPENSSL_VERSION_NUMBER < 0x10100000L unsigned int keylen = 0; EVP_MD_CTX ctx; EVP_DigestInit(&ctx,EVP_sha256()); EVP_DigestUpdate(&ctx,str,strl); EVP_DigestFinal(&ctx,key,&keylen); EVP_MD_CTX_cleanup(&ctx); #else unsigned int keylen = 0; EVP_MD_CTX *ctx = EVP_MD_CTX_new(); EVP_DigestInit(ctx,EVP_sha256()); EVP_DigestUpdate(ctx,str,strl); EVP_DigestFinal(ctx,key,&keylen); EVP_MD_CTX_free(ctx); #endif #else fprintf(stderr,"SHA256 is not supported\n"); return -1; #endif } else if(shatype == SHATYPE_SHA384) { #if !defined(OPENSSL_NO_SHA384) && defined(SHA384_DIGEST_LENGTH) #if OPENSSL_VERSION_NUMBER < 0x10100000L unsigned int keylen = 0; EVP_MD_CTX ctx; EVP_DigestInit(&ctx,EVP_sha384()); EVP_DigestUpdate(&ctx,str,strl); EVP_DigestFinal(&ctx,key,&keylen); EVP_MD_CTX_cleanup(&ctx); #else unsigned int keylen = 0; EVP_MD_CTX *ctx = EVP_MD_CTX_new(); EVP_DigestInit(ctx,EVP_sha384()); EVP_DigestUpdate(ctx,str,strl); EVP_DigestFinal(ctx,key,&keylen); EVP_MD_CTX_free(ctx); #endif #else fprintf(stderr,"SHA384 is not supported\n"); return -1; #endif } else if(shatype == SHATYPE_SHA512) { #if !defined(OPENSSL_NO_SHA512) && defined(SHA512_DIGEST_LENGTH) #if OPENSSL_VERSION_NUMBER < 0x10100000L unsigned int keylen = 0; EVP_MD_CTX ctx; EVP_DigestInit(&ctx,EVP_sha512()); EVP_DigestUpdate(&ctx,str,strl); EVP_DigestFinal(&ctx,key,&keylen); EVP_MD_CTX_cleanup(&ctx); #else unsigned int keylen = 0; EVP_MD_CTX *ctx = EVP_MD_CTX_new(); EVP_DigestInit(ctx,EVP_sha512()); EVP_DigestUpdate(ctx,str,strl); EVP_DigestFinal(ctx,key,&keylen); EVP_MD_CTX_free(ctx); #endif #else fprintf(stderr,"SHA512 is not supported\n"); return -1; #endif } else { MD5_CTX ctx; MD5_Init(&ctx); MD5_Update(&ctx,str,strl); MD5_Final(key,&ctx); } turn_free(str,sz+1); return 0; } #define PWD_SALT_SIZE (8) static void readable_string(unsigned char *orig, unsigned char *out, size_t sz) { size_t i = 0; out[0]=0; for(i = 0; i < sz; ++i) { snprintf((char*)(out + (i * 2)), 4, "%02x", (unsigned int)orig[i]); } } static void generate_enc_password(const char* pwd, char *result, const unsigned char *orig_salt) { unsigned char salt[PWD_SALT_SIZE+1]; if(!orig_salt) { generate_random_nonce(salt, PWD_SALT_SIZE); } else { ns_bcopy(orig_salt,salt,PWD_SALT_SIZE); salt[PWD_SALT_SIZE]=0; } unsigned char rsalt[PWD_SALT_SIZE*2+1]; readable_string(salt,rsalt,PWD_SALT_SIZE); result[0]='$'; result[1]='5'; result[2]='$'; ns_bcopy((char*)rsalt,result+3,PWD_SALT_SIZE+PWD_SALT_SIZE); result[3+PWD_SALT_SIZE+PWD_SALT_SIZE]='$'; unsigned char* out = (unsigned char*)(result+3+PWD_SALT_SIZE+PWD_SALT_SIZE+1); { #if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_MD_CTX ctx; #if !defined(OPENSSL_NO_SHA256) && defined(SHA256_DIGEST_LENGTH) EVP_DigestInit(&ctx,EVP_sha256()); #else EVP_DigestInit(&ctx,EVP_sha1()); #endif EVP_DigestUpdate(&ctx,salt,PWD_SALT_SIZE); EVP_DigestUpdate(&ctx,pwd,strlen(pwd)); { unsigned char hash[129]; unsigned int keylen = 0; EVP_DigestFinal(&ctx,hash,&keylen); readable_string(hash,out,keylen); } EVP_MD_CTX_cleanup(&ctx); #else EVP_MD_CTX *ctx = EVP_MD_CTX_new(); #if !defined(OPENSSL_NO_SHA256) && defined(SHA256_DIGEST_LENGTH) EVP_DigestInit(ctx,EVP_sha256()); #else EVP_DigestInit(ctx,EVP_sha1()); #endif EVP_DigestUpdate(ctx,salt,PWD_SALT_SIZE); EVP_DigestUpdate(ctx,pwd,strlen(pwd)); { unsigned char hash[129]; unsigned int keylen = 0; EVP_DigestFinal(ctx,hash,&keylen); readable_string(hash,out,keylen); } EVP_MD_CTX_free(ctx); #endif } } void generate_new_enc_password(const char* pwd, char *result) { generate_enc_password(pwd, result, NULL); } static int encrypted_password(const char* pin, unsigned char* salt) { size_t min_len = 3+PWD_SALT_SIZE+PWD_SALT_SIZE+1+32; if(strlen(pin)>=min_len) { if((pin[0]=='$') && (pin[1]=='5') && (pin[2]=='$') && (pin[3+PWD_SALT_SIZE+PWD_SALT_SIZE]=='$')) { size_t i = 0; for(i=0;i>1) | ((tt & 0x0E00)>>2) | ((tt & 0x3000)>>2); } u16bits stun_get_msg_type_str(const u08bits *buf, size_t len) { if(!buf || len<2) return (u16bits)-1; return ((nswap16(((const u16bits*)buf)[0])) & 0x3FFF); } int is_channel_msg_str(const u08bits* buf, size_t blen) { return (buf && blen>=4 && STUN_VALID_CHANNEL(nswap16(((const u16bits*)buf)[0]))); } /////////////// message types ///////////////////////////////// int stun_is_command_message_str(const u08bits* buf, size_t blen) { if (buf && blen >= STUN_HEADER_LENGTH) { if (!STUN_VALID_CHANNEL(nswap16(((const u16bits*)buf)[0]))) { if ((((u08bits) buf[0]) & ((u08bits) (0xC0))) == 0) { if (nswap32(((const u32bits*)(buf))[1]) == STUN_MAGIC_COOKIE) { u16bits len = nswap16(((const u16bits*)(buf))[1]); if ((len & 0x0003) == 0) { if ((size_t) (len + STUN_HEADER_LENGTH) == blen) { return 1; } } } } } } return 0; } int old_stun_is_command_message_str(const u08bits* buf, size_t blen, u32bits *cookie) { if (buf && blen >= STUN_HEADER_LENGTH) { if (!STUN_VALID_CHANNEL(nswap16(((const u16bits*)buf)[0]))) { if ((((u08bits) buf[0]) & ((u08bits) (0xC0))) == 0) { if (nswap32(((const u32bits*)(buf))[1]) != STUN_MAGIC_COOKIE) { u16bits len = nswap16(((const u16bits*)(buf))[1]); if ((len & 0x0003) == 0) { if ((size_t) (len + STUN_HEADER_LENGTH) == blen) { *cookie = nswap32(((const u32bits*)(buf))[1]); return 1; } } } } } } return 0; } int stun_is_command_message_full_check_str(const u08bits* buf, size_t blen, int must_check_fingerprint, int *fingerprint_present) { if(!stun_is_command_message_str(buf,blen)) return 0; stun_attr_ref sar = stun_attr_get_first_by_type_str(buf, blen, STUN_ATTRIBUTE_FINGERPRINT); if(!sar) { if(fingerprint_present) *fingerprint_present = 0; if(stun_get_method_str(buf,blen) == STUN_METHOD_BINDING) { return 1; } return !must_check_fingerprint; } if(stun_attr_get_len(sar) != 4) return 0; const u32bits* fingerprint = (const u32bits*)stun_attr_get_value(sar); if(!fingerprint) return !must_check_fingerprint; u32bits crc32len = (u32bits)((((const u08bits*)fingerprint)-buf)-4); int ret = (*fingerprint == nswap32(ns_crc32(buf,crc32len) ^ ((u32bits)0x5354554e))); if(ret && fingerprint_present) *fingerprint_present = ret; return ret; } int stun_is_command_message_offset_str(const u08bits* buf, size_t blen, int offset) { return stun_is_command_message_str(buf + offset, blen); } int stun_is_request_str(const u08bits* buf, size_t len) { if(is_channel_msg_str(buf,len)) return 0; return IS_STUN_REQUEST(stun_get_msg_type_str(buf,len)); } int stun_is_success_response_str(const u08bits* buf, size_t len) { if(is_channel_msg_str(buf,len)) return 0; return IS_STUN_SUCCESS_RESP(stun_get_msg_type_str(buf,len)); } int stun_is_error_response_str(const u08bits* buf, size_t len, int *err_code, u08bits *err_msg, size_t err_msg_size) { if(is_channel_msg_str(buf,len)) return 0; if(IS_STUN_ERR_RESP(stun_get_msg_type_str(buf,len))) { if(err_code) { stun_attr_ref sar = stun_attr_get_first_by_type_str(buf, len, STUN_ATTRIBUTE_ERROR_CODE); if(sar) { if(stun_attr_get_len(sar)>=4) { const u08bits* val = (const u08bits*)stun_attr_get_value(sar); *err_code=(int)(val[2]*100 + val[3]); if(err_msg && err_msg_size>0) { err_msg[0]=0; if(stun_attr_get_len(sar)>4) { size_t msg_len = stun_attr_get_len(sar) - 4; if(msg_len>(err_msg_size-1)) msg_len=err_msg_size - 1; ns_bcopy(val+4, err_msg, msg_len); err_msg[msg_len]=0; } } } } } return 1; } return 0; } int stun_is_challenge_response_str(const u08bits* buf, size_t len, int *err_code, u08bits *err_msg, size_t err_msg_size, u08bits *realm, u08bits *nonce, u08bits *server_name, int *oauth) { int ret = stun_is_error_response_str(buf, len, err_code, err_msg, err_msg_size); if(ret && (((*err_code) == 401) || ((*err_code) == 438) )) { stun_attr_ref sar = stun_attr_get_first_by_type_str(buf,len,STUN_ATTRIBUTE_REALM); if(sar) { int found_oauth = 0; const u08bits *value = stun_attr_get_value(sar); if(value) { size_t vlen = (size_t)stun_attr_get_len(sar); ns_bcopy(value,realm,vlen); realm[vlen]=0; { stun_attr_ref sar = stun_attr_get_first_by_type_str(buf,len,STUN_ATTRIBUTE_THIRD_PARTY_AUTHORIZATION); if(sar) { const u08bits *value = stun_attr_get_value(sar); if(value) { size_t vlen = (size_t)stun_attr_get_len(sar); if(vlen>0) { if(server_name) { ns_bcopy(value,server_name,vlen); } found_oauth = 1; } } } } sar = stun_attr_get_first_by_type_str(buf,len,STUN_ATTRIBUTE_NONCE); if(sar) { value = stun_attr_get_value(sar); if(value) { vlen = (size_t)stun_attr_get_len(sar); ns_bcopy(value,nonce,vlen); nonce[vlen]=0; if(oauth) { *oauth = found_oauth; } return 1; } } } } } return 0; } int stun_is_response_str(const u08bits* buf, size_t len) { if(is_channel_msg_str(buf,len)) return 0; if(IS_STUN_SUCCESS_RESP(stun_get_msg_type_str(buf,len))) return 1; if(IS_STUN_ERR_RESP(stun_get_msg_type_str(buf,len))) return 1; return 0; } int stun_is_indication_str(const u08bits* buf, size_t len) { if(is_channel_msg_str(buf,len)) return 0; return IS_STUN_INDICATION(stun_get_msg_type_str(buf,len)); } u16bits stun_make_request(u16bits method) { return GET_STUN_REQUEST(stun_make_type(method)); } u16bits stun_make_indication(u16bits method) { return GET_STUN_INDICATION(stun_make_type(method)); } u16bits stun_make_success_response(u16bits method) { return GET_STUN_SUCCESS_RESP(stun_make_type(method)); } u16bits stun_make_error_response(u16bits method) { return GET_STUN_ERR_RESP(stun_make_type(method)); } //////////////// INIT //////////////////////////////////////////// void stun_init_buffer_str(u08bits *buf, size_t *len) { *len=STUN_HEADER_LENGTH; ns_bzero(buf,*len); } void stun_init_command_str(u16bits message_type, u08bits* buf, size_t *len) { stun_init_buffer_str(buf,len); message_type &= (u16bits)(0x3FFF); ((u16bits*)buf)[0]=nswap16(message_type); ((u16bits*)buf)[1]=0; ((u32bits*)buf)[1]=nswap32(STUN_MAGIC_COOKIE); stun_tid_generate_in_message_str(buf,NULL); } void old_stun_init_command_str(u16bits message_type, u08bits* buf, size_t *len, u32bits cookie) { stun_init_buffer_str(buf,len); message_type &= (u16bits)(0x3FFF); ((u16bits*)buf)[0]=nswap16(message_type); ((u16bits*)buf)[1]=0; ((u32bits*)buf)[1]=nswap32(cookie); stun_tid_generate_in_message_str(buf,NULL); } void stun_init_request_str(u16bits method, u08bits* buf, size_t *len) { stun_init_command_str(stun_make_request(method), buf, len); } void stun_init_indication_str(u16bits method, u08bits* buf, size_t *len) { stun_init_command_str(stun_make_indication(method), buf, len); } void stun_init_success_response_str(u16bits method, u08bits* buf, size_t *len, stun_tid* id) { stun_init_command_str(stun_make_success_response(method), buf, len); if(id) { stun_tid_message_cpy(buf, id); } } void old_stun_init_success_response_str(u16bits method, u08bits* buf, size_t *len, stun_tid* id, u32bits cookie) { old_stun_init_command_str(stun_make_success_response(method), buf, len, cookie); if(id) { stun_tid_message_cpy(buf, id); } } const u08bits* get_default_reason(int error_code) { const u08bits* reason = (const u08bits *) "Unknown error"; switch (error_code){ case 300: reason = (const u08bits *) "Try Alternate"; break; case 400: reason = (const u08bits *) "Bad Request"; break; case 401: reason = (const u08bits *) "Unauthorized"; break; case 403: reason = (const u08bits *) "Forbidden"; break; case 404: reason = (const u08bits *) "Not Found"; break; case 420: reason = (const u08bits *) "Unknown Attribute"; break; case 437: reason = (const u08bits *) "Allocation Mismatch"; break; case 438: reason = (const u08bits *) "Stale Nonce"; break; case 440: reason = (const u08bits *) "Address Family not Supported"; break; case 441: reason = (const u08bits *) "Wrong Credentials"; break; case 442: reason = (const u08bits *) "Unsupported Transport Protocol"; break; case 443: reason = (const u08bits *) "Peer Address Family Mismatch"; break; case 446: reason = (const u08bits *) "Connection Already Exists"; break; case 447: reason = (const u08bits *) "Connection Timeout or Failure"; break; case 486: reason = (const u08bits *) "Allocation Quota Reached"; break; case 487: reason = (const u08bits *) "Role Conflict"; break; case 500: reason = (const u08bits *) "Server Error"; break; case 508: reason = (const u08bits *) "Insufficient Capacity"; break; default: ; }; return reason; } static void stun_init_error_response_common_str(u08bits* buf, size_t *len, u16bits error_code, const u08bits *reason, stun_tid* id) { if (!reason || !strcmp((const char*)reason,"Unknown error")) { reason = get_default_reason(error_code); } u08bits avalue[513]; avalue[0] = 0; avalue[1] = 0; avalue[2] = (u08bits) (error_code / 100); avalue[3] = (u08bits) (error_code % 100); strncpy((s08bits*) (avalue + 4), (const s08bits*) reason, sizeof(avalue)-4); avalue[sizeof(avalue)-1]=0; int alen = 4 + strlen((const s08bits*) (avalue+4)); //"Manual" padding for compatibility with classic old stun: { int rem = alen % 4; if(rem) { alen +=(4-rem); } } stun_attr_add_str(buf, len, STUN_ATTRIBUTE_ERROR_CODE, (u08bits*) avalue, alen); if (id) { stun_tid_message_cpy(buf, id); } } void old_stun_init_error_response_str(u16bits method, u08bits* buf, size_t *len, u16bits error_code, const u08bits *reason, stun_tid* id, u32bits cookie) { old_stun_init_command_str(stun_make_error_response(method), buf, len, cookie); stun_init_error_response_common_str(buf, len, error_code, reason, id); } void stun_init_error_response_str(u16bits method, u08bits* buf, size_t *len, u16bits error_code, const u08bits *reason, stun_tid* id) { stun_init_command_str(stun_make_error_response(method), buf, len); stun_init_error_response_common_str(buf, len, error_code, reason, id); } /////////// CHANNEL //////////////////////////////////////////////// int stun_init_channel_message_str(u16bits chnumber, u08bits* buf, size_t *len, int length, int do_padding) { u16bits rlen = (u16bits)length; if(length<0 || (MAX_STUN_MESSAGE_SIZE<(4+length))) return -1; ((u16bits*)(buf))[0]=nswap16(chnumber); ((u16bits*)(buf))[1]=nswap16((u16bits)length); if(do_padding && (rlen & 0x0003)) rlen = ((rlen>>2)+1)<<2; *len=4+rlen; return 0; } int stun_is_channel_message_str(const u08bits *buf, size_t *blen, u16bits* chnumber, int mandatory_padding) { u16bits datalen_header; u16bits datalen_actual; if (!blen || (*blen < 4)) return 0; u16bits chn = nswap16(((const u16bits*)(buf))[0]); if (!STUN_VALID_CHANNEL(chn)) return 0; if(*blen>(u16bits)-1) *blen=(u16bits)-1; datalen_actual = (u16bits)(*blen) - 4; datalen_header = ((const u16bits*)buf)[1]; datalen_header = nswap16(datalen_header); if (datalen_header > datalen_actual) return 0; if (datalen_header != datalen_actual) { /* maybe there are padding bytes for 32-bit alignment. Mandatory for TCP. Optional for UDP */ if(datalen_actual & 0x0003) { if(mandatory_padding) { return 0; } else if ((datalen_actual < datalen_header) || (datalen_header == 0)) { return 0; } else { u16bits diff = datalen_actual - datalen_header; if (diff > 3) return 0; } } } *blen = datalen_header + 4; if (chnumber) *chnumber = chn; return 1; } ////////// STUN message /////////////////////////////// static inline int sheadof(const char *head, const char* full, int ignore_case) { while(*head) { if(*head != *full) { if(ignore_case && (tolower((int)*head)==tolower((int)*full))) { //OK } else { return 0; } } ++head;++full; } return 1; } static inline const char* findstr(const char *hay, size_t slen, const char *needle, int ignore_case) { const char *ret = NULL; if(hay && slen && needle) { size_t nlen=strlen(needle); if(nlen<=slen) { size_t smax = slen-nlen+1; size_t i; const char *sp = hay; for(i=0;i=12) { if((strstr(s,"GET ")==s) ||(strstr(s,"POST ")==s) || (strstr(s,"DELETE ")==s) || (strstr(s,"PUT ")==s)) { const char *sp=findstr(s+4,blen-4," HTTP/",0); if(sp) { sp += 6; size_t diff_blen = sp-s; if(diff_blen+4 <= blen) { sp=findstr(sp,blen-diff_blen,"\r\n\r\n",0); if(sp) { int ret_len = (int)(sp-s+4); const char* clheader = "content-length: "; const char* cl = findstr(s,sp-s,clheader,1); if(cl) { unsigned long clen = strtoul(cl+strlen(clheader),NULL,10); if(clen>0 && clen<(0x0FFFFFFF)) { ret_len += (int)clen; } } return ret_len; } } } } } return 0; } int is_http(const char *s, size_t blen) { return is_http_inline(s, blen); } int stun_get_message_len_str(u08bits *buf, size_t blen, int padding, size_t *app_len) { if (buf && blen) { /* STUN request/response ? */ if (buf && blen >= STUN_HEADER_LENGTH) { if (!STUN_VALID_CHANNEL(nswap16(((const u16bits*)buf)[0]))) { if ((((u08bits) buf[0]) & ((u08bits) (0xC0))) == 0) { if (nswap32(((const u32bits*)(buf))[1]) == STUN_MAGIC_COOKIE) { u16bits len = nswap16(((const u16bits*)(buf))[1]); if ((len & 0x0003) == 0) { len += STUN_HEADER_LENGTH; if ((size_t) len <= blen) { *app_len = (size_t)len; return (int)len; } } } } } } //HTTP request ? { int http_len = is_http_inline(((char*)buf), blen); if((http_len>0) && ((size_t)http_len<=blen)) { *app_len = (size_t)http_len; return http_len; } } /* STUN channel ? */ if(blen>=4) { u16bits chn=nswap16(((const u16bits*)(buf))[0]); if(STUN_VALID_CHANNEL(chn)) { u16bits bret = (4+(nswap16(((const u16bits*)(buf))[1]))); *app_len = bret; if(padding && (bret & 0x0003)) { bret = ((bret>>2)+1)<<2; } if(bret<=blen) { return bret; } } } } return -1; } ////////// ALLOCATE /////////////////////////////////// int stun_set_allocate_request_str(u08bits* buf, size_t *len, u32bits lifetime, int af4, int af6, u08bits transport, int mobile, const char* rt, int ep) { stun_init_request_str(STUN_METHOD_ALLOCATE, buf, len); //REQUESTED-TRANSPORT { u08bits field[4]; field[0]=transport; field[1]=0; field[2]=0; field[3]=0; if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_REQUESTED_TRANSPORT,field,sizeof(field))<0) return -1; } //LIFETIME { if(lifetime<1) lifetime=STUN_DEFAULT_ALLOCATE_LIFETIME; u32bits field=nswap32(lifetime); if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_LIFETIME,(u08bits*)(&field),sizeof(field))<0) return -1; } //MICE if(mobile) { if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_MOBILITY_TICKET,(const u08bits*)"",0)<0) return -1; } if(ep>-1) { uint8_t value = ep ? 0x80 : 0x00; if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_EVEN_PORT,(const u08bits*)&value,1)<0) return -1; } //RESERVATION-TOKEN, EVEN-PORT and DUAL-ALLOCATION are mutually exclusive: if(rt) { stun_attr_add_str(buf,len, STUN_ATTRIBUTE_RESERVATION_TOKEN, (const u08bits*) rt, 8); } else { //ADRESS-FAMILY if (af4 && !af6) { u08bits field[4]; field[0] = (u08bits)STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4; field[1]=0; field[2]=0; field[3]=0; if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY,field,sizeof(field))<0) return -1; } if (af6 && !af4) { u08bits field[4]; field[0] = (u08bits)STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6; field[1]=0; field[2]=0; field[3]=0; if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY,field,sizeof(field))<0) return -1; } if (af4 && af6) { u08bits field[4]; field[0] = (u08bits)STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6; field[1]=0; field[2]=0; field[3]=0; if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY,field,sizeof(field))<0) return -1; } } return 0; } int stun_set_allocate_response_str(u08bits* buf, size_t *len, stun_tid* tid, const ioa_addr *relayed_addr1, const ioa_addr *relayed_addr2, const ioa_addr *reflexive_addr, u32bits lifetime, u32bits max_lifetime, int error_code, const u08bits *reason, u64bits reservation_token, char* mobile_id) { if(!error_code) { stun_init_success_response_str(STUN_METHOD_ALLOCATE, buf, len, tid); if(relayed_addr1) { if(stun_attr_add_addr_str(buf,len,STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS,relayed_addr1)<0) return -1; } if(relayed_addr2) { if(stun_attr_add_addr_str(buf,len,STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS,relayed_addr2)<0) return -1; } if(reflexive_addr) { if(stun_attr_add_addr_str(buf,len,STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,reflexive_addr)<0) return -1; } if(reservation_token) { reservation_token=nswap64(reservation_token); stun_attr_add_str(buf,len,STUN_ATTRIBUTE_RESERVATION_TOKEN,(u08bits*)(&reservation_token),8); } { if(lifetime<1) lifetime=STUN_DEFAULT_ALLOCATE_LIFETIME; else if(lifetime>max_lifetime) lifetime = max_lifetime; u32bits field=nswap32(lifetime); if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_LIFETIME,(u08bits*)(&field),sizeof(field))<0) return -1; } if(mobile_id && *mobile_id) { if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_MOBILITY_TICKET,(u08bits*)mobile_id,strlen(mobile_id))<0) return -1; } } else { stun_init_error_response_str(STUN_METHOD_ALLOCATE, buf, len, error_code, reason, tid); } return 0; } /////////////// CHANNEL BIND /////////////////////////////////////// u16bits stun_set_channel_bind_request_str(u08bits* buf, size_t *len, const ioa_addr* peer_addr, u16bits channel_number) { if(!STUN_VALID_CHANNEL(channel_number)) { channel_number = 0x4000 + ((u16bits)(((u32bits)turn_random())%(0x7FFF-0x4000+1))); } stun_init_request_str(STUN_METHOD_CHANNEL_BIND, buf, len); if(stun_attr_add_channel_number_str(buf, len, channel_number)<0) return 0; if(!peer_addr) { ioa_addr ca; ns_bzero(&ca,sizeof(ioa_addr)); if(stun_attr_add_addr_str(buf,len,STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &ca)<0) return 0; } else { if(stun_attr_add_addr_str(buf,len,STUN_ATTRIBUTE_XOR_PEER_ADDRESS, peer_addr)<0) return 0; } return channel_number; } void stun_set_channel_bind_response_str(u08bits* buf, size_t *len, stun_tid* tid, int error_code, const u08bits *reason) { if(!error_code) { stun_init_success_response_str(STUN_METHOD_CHANNEL_BIND, buf, len, tid); } else { stun_init_error_response_str(STUN_METHOD_CHANNEL_BIND, buf, len, error_code, reason, tid); } } /////////////// BINDING /////////////////////////////////////// void stun_set_binding_request_str(u08bits* buf, size_t *len) { stun_init_request_str(STUN_METHOD_BINDING, buf, len); } int stun_set_binding_response_str(u08bits* buf, size_t *len, stun_tid* tid, const ioa_addr *reflexive_addr, int error_code, const u08bits *reason, u32bits cookie, int old_stun) { if (!error_code) { if (!old_stun) { stun_init_success_response_str(STUN_METHOD_BINDING, buf, len, tid); } else { old_stun_init_success_response_str(STUN_METHOD_BINDING, buf, len, tid, cookie); } if(!old_stun && reflexive_addr) { if (stun_attr_add_addr_str(buf, len, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, reflexive_addr) < 0) return -1; } if(reflexive_addr) { if (stun_attr_add_addr_str(buf, len, STUN_ATTRIBUTE_MAPPED_ADDRESS, reflexive_addr) < 0) return -1; } } else if (!old_stun) { stun_init_error_response_str(STUN_METHOD_BINDING, buf, len, error_code, reason, tid); } else { old_stun_init_error_response_str(STUN_METHOD_BINDING, buf, len, error_code, reason, tid, cookie); } return 0; } int stun_is_binding_request_str(const u08bits* buf, size_t len, size_t offset) { if(offset < len) { buf += offset; len -= offset; if (stun_is_command_message_str(buf, len)) { if (stun_is_request_str(buf, len) && (stun_get_method_str(buf, len) == STUN_METHOD_BINDING)) { return 1; } } } return 0; } int stun_is_binding_response_str(const u08bits* buf, size_t len) { if(stun_is_command_message_str(buf,len) && (stun_get_method_str(buf,len)==STUN_METHOD_BINDING)) { if(stun_is_response_str(buf,len)) { return 1; } } return 0; } /////////////////////////////// TID /////////////////////////////// int stun_tid_equals(const stun_tid *id1, const stun_tid *id2) { if(id1==id2) return 1; if(!id1) return 0; if(!id2) return 0; { unsigned int i=0; for(i=0;itsx_id[i]!=id2->tsx_id[i]) return 0; } } return 1; } void stun_tid_cpy(stun_tid *id1, const stun_tid *id2) { if(!id1) return; if(!id2) return; ns_bcopy((const void*)(id2->tsx_id),(void*)(id1->tsx_id),STUN_TID_SIZE); } static void stun_tid_string_cpy(u08bits* s, const stun_tid* id) { if(s && id) { ns_bcopy((const void*)(id->tsx_id),s,STUN_TID_SIZE); } } static void stun_tid_from_string(const u08bits* s, stun_tid* id) { if(s && id) { ns_bcopy(s,(void*)(id->tsx_id),STUN_TID_SIZE); } } void stun_tid_from_message_str(const u08bits* buf, size_t len, stun_tid* id) { UNUSED_ARG(len); stun_tid_from_string(buf+8, id); } void stun_tid_message_cpy(u08bits* buf, const stun_tid* id) { if(buf && id) { stun_tid_string_cpy(buf+8, id); } } void stun_tid_generate(stun_tid* id) { if(id) { turn_random_tid_size(id->tsx_id); } } void stun_tid_generate_in_message_str(u08bits* buf, stun_tid* id) { stun_tid tmp; if(!id) id=&tmp; stun_tid_generate(id); stun_tid_message_cpy(buf, id); } /////////////////// TIME //////////////////////////////////////////////////////// turn_time_t stun_adjust_allocate_lifetime(turn_time_t lifetime, turn_time_t max_allowed_lifetime, turn_time_t max_lifetime) { if(!lifetime) lifetime = STUN_DEFAULT_ALLOCATE_LIFETIME; else if(lifetimemax_allowed_lifetime) lifetime = max_allowed_lifetime; if(max_lifetime && (max_lifetime < lifetime)) { lifetime = max_lifetime; } return lifetime; } ////////////// ATTR ///////////////////////////////////////////////////////////// int stun_attr_get_type(stun_attr_ref attr) { if(attr) return (int)(nswap16(((const u16bits*)attr)[0])); return -1; } int stun_attr_get_len(stun_attr_ref attr) { if(attr) return (int)(nswap16(((const u16bits*)attr)[1])); return -1; } const u08bits* stun_attr_get_value(stun_attr_ref attr) { if(attr) { int len = (int)(nswap16(((const u16bits*)attr)[1])); if(len<1) return NULL; return ((const u08bits*)attr)+4; } return NULL; } int stun_get_requested_address_family(stun_attr_ref attr) { if (attr) { int len = (int) (nswap16(((const u16bits*)attr)[1])); if (len != 4) return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_INVALID; int val = ((const u08bits*) attr)[4]; switch (val){ case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4: return val; case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6: return val; default: return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_INVALID; }; } return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT; } u16bits stun_attr_get_channel_number(stun_attr_ref attr) { if(attr) { const u08bits* value = stun_attr_get_value(attr); if(value && (stun_attr_get_len(attr) >= 2)) { u16bits cn=nswap16(((const u16bits*)value)[0]); if(STUN_VALID_CHANNEL(cn)) return cn; } } return 0; } band_limit_t stun_attr_get_bandwidth(stun_attr_ref attr) { if(attr) { const u08bits* value = stun_attr_get_value(attr); if(value && (stun_attr_get_len(attr) >= 4)) { u32bits bps=nswap32(((const u32bits*)value)[0]); return (band_limit_t)(bps << 7); } } return 0; } u64bits stun_attr_get_reservation_token_value(stun_attr_ref attr) { if(attr) { const u08bits* value = stun_attr_get_value(attr); if(value && (stun_attr_get_len(attr) == 8)) { u64bits token; ns_bcopy(value, &token, sizeof(u64bits)); return nswap64(token); } } return 0; } int stun_attr_is_addr(stun_attr_ref attr) { if(attr) { switch(stun_attr_get_type(attr)) { case STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS: case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: case STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS: case STUN_ATTRIBUTE_MAPPED_ADDRESS: case STUN_ATTRIBUTE_ALTERNATE_SERVER: case OLD_STUN_ATTRIBUTE_RESPONSE_ADDRESS: case OLD_STUN_ATTRIBUTE_SOURCE_ADDRESS: case OLD_STUN_ATTRIBUTE_CHANGED_ADDRESS: case OLD_STUN_ATTRIBUTE_REFLECTED_FROM: case STUN_ATTRIBUTE_RESPONSE_ORIGIN: case STUN_ATTRIBUTE_OTHER_ADDRESS: return 1; break; default: ; }; } return 0; } u08bits stun_attr_get_even_port(stun_attr_ref attr) { if(attr) { const u08bits* value=stun_attr_get_value(attr); if(value) { if((u08bits)(value[0]) > 0x7F) return 1; } } return 0; } stun_attr_ref stun_attr_get_first_by_type_str(const u08bits* buf, size_t len, u16bits attr_type) { stun_attr_ref attr=stun_attr_get_first_str(buf,len); while(attr) { if(stun_attr_get_type(attr) == attr_type) { return attr; } attr=stun_attr_get_next_str(buf,len,attr); } return NULL; } stun_attr_ref stun_attr_get_first_str(const u08bits* buf, size_t len) { if(stun_get_command_message_len_str(buf,len)>STUN_HEADER_LENGTH) { return (stun_attr_ref)(buf+STUN_HEADER_LENGTH); } return NULL; } stun_attr_ref stun_attr_get_next_str(const u08bits* buf, size_t len, stun_attr_ref prev) { if(!prev) return stun_attr_get_first_str(buf,len); else { const u08bits* end = buf + stun_get_command_message_len_str(buf,len); int attrlen=stun_attr_get_len(prev); u16bits rem4 = ((u16bits)attrlen) & 0x0003; if(rem4) { attrlen = attrlen+4-(int)rem4; } const u08bits* attr_end=(const u08bits*)prev+4+attrlen; if(attr_end=MAX_STUN_MESSAGE_SIZE) return -1; else { u08bits* attr_start=buf+clen; u16bits *attr_start_16t=(u16bits*)attr_start; stun_set_command_message_len_str(buf,newlen); *len = newlen; attr_start_16t[0]=nswap16(attr); attr_start_16t[1]=nswap16(alen); if(alen>0) ns_bcopy(avalue,attr_start+4,alen); return 0; } } int stun_attr_add_addr_str(u08bits *buf, size_t *len, u16bits attr_type, const ioa_addr* ca) { stun_tid tid; stun_tid_from_message_str(buf, *len, &tid); int xor_ed=0; switch(attr_type) { case STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS: case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: case STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS: xor_ed=1; break; default: ; }; ioa_addr public_addr; map_addr_from_private_to_public(ca,&public_addr); u08bits cfield[64]; int clen=0; if(stun_addr_encode(&public_addr, cfield, &clen, xor_ed, STUN_MAGIC_COOKIE, tid.tsx_id)<0) { return -1; } if(stun_attr_add_str(buf,len,attr_type,(u08bits*)(&cfield),clen)<0) return -1; return 0; } int stun_attr_get_addr_str(const u08bits *buf, size_t len, stun_attr_ref attr, ioa_addr* ca, const ioa_addr *default_addr) { stun_tid tid; stun_tid_from_message_str(buf, len, &tid); ioa_addr public_addr; addr_set_any(ca); addr_set_any(&public_addr); int attr_type = stun_attr_get_type(attr); if(attr_type<0) return -1; int xor_ed=0; switch(attr_type) { case STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS: case STUN_ATTRIBUTE_XOR_PEER_ADDRESS: case STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS: xor_ed=1; break; default: ; }; const u08bits *cfield=stun_attr_get_value(attr); if(!cfield) return -1; if(stun_addr_decode(&public_addr, cfield, stun_attr_get_len(attr), xor_ed, STUN_MAGIC_COOKIE, tid.tsx_id)<0) { return -1; } map_addr_from_public_to_private(&public_addr, ca); if(default_addr && addr_any_no_port(ca) && !addr_any_no_port(default_addr)) { int port = addr_get_port(ca); addr_cpy(ca,default_addr); addr_set_port(ca,port); } return 0; } int stun_attr_get_first_addr_str(const u08bits *buf, size_t len, u16bits attr_type, ioa_addr* ca, const ioa_addr *default_addr) { stun_attr_ref attr=stun_attr_get_first_str(buf,len); while(attr) { if(stun_attr_is_addr(attr) && (attr_type == stun_attr_get_type(attr))) { if(stun_attr_get_addr_str(buf,len,attr,ca,default_addr)==0) { return 0; } } attr=stun_attr_get_next_str(buf,len,attr); } return -1; } int stun_attr_add_channel_number_str(u08bits* buf, size_t *len, u16bits chnumber) { u16bits field[2]; field[0]=nswap16(chnumber); field[1]=0; return stun_attr_add_str(buf,len,STUN_ATTRIBUTE_CHANNEL_NUMBER,(u08bits*)(field),sizeof(field)); } int stun_attr_add_bandwidth_str(u08bits* buf, size_t *len, band_limit_t bps0) { u32bits bps = (band_limit_t)(bps0 >> 7); u32bits field=nswap32(bps); return stun_attr_add_str(buf,len,STUN_ATTRIBUTE_NEW_BANDWIDTH,(u08bits*)(&field),sizeof(field)); } int stun_attr_add_address_error_code(u08bits* buf, size_t *len, int requested_address_family, int error_code) { const u08bits *reason = get_default_reason(error_code); u08bits avalue[513]; avalue[0] = (u08bits)requested_address_family; avalue[1] = 0; avalue[2] = (u08bits) (error_code / 100); avalue[3] = (u08bits) (error_code % 100); strncpy((s08bits*) (avalue + 4), (const s08bits*) reason, sizeof(avalue)-4); avalue[sizeof(avalue)-1]=0; int alen = 4 + strlen((const s08bits*) (avalue+4)); //"Manual" padding for compatibility with classic old stun: { int rem = alen % 4; if(rem) { alen +=(4-rem); } } stun_attr_add_str(buf, len, STUN_ATTRIBUTE_ADDRESS_ERROR_CODE, (u08bits*) avalue, alen); return 0; } int stun_attr_get_address_error_code(u08bits* buf, size_t len, int *requested_address_family, int *error_code) { if(requested_address_family) { *requested_address_family = 0; } if(error_code) { *error_code = 0; } if(buf && len) { stun_attr_ref sar = stun_attr_get_first_by_type_str(buf, len, STUN_ATTRIBUTE_ADDRESS_ERROR_CODE); if(sar) { const u08bits* value = stun_attr_get_value(sar); if(!value) { return -1; } else { int alen = stun_attr_get_len(sar); if(alen != 4) { return -1; } if(requested_address_family) { *requested_address_family = value[0]; } if(error_code) { *error_code = (int)(value[2]*100+value[3]); } return 0; } } } return 0; } u16bits stun_attr_get_first_channel_number_str(const u08bits *buf, size_t len) { stun_attr_ref attr=stun_attr_get_first_str(buf,len); while(attr) { if(stun_attr_get_type(attr) == STUN_ATTRIBUTE_CHANNEL_NUMBER) { u16bits ret = stun_attr_get_channel_number(attr); if(STUN_VALID_CHANNEL(ret)) { return ret; } } attr=stun_attr_get_next_str(buf,len,attr); } return 0; } ////////////// FINGERPRINT //////////////////////////// int stun_attr_add_fingerprint_str(u08bits *buf, size_t *len) { u32bits crc32 = 0; stun_attr_add_str(buf, len, STUN_ATTRIBUTE_FINGERPRINT, (u08bits*)&crc32, 4); crc32 = ns_crc32(buf,*len-8); *((u32bits*)(buf+*len-4)) = nswap32(crc32 ^ ((u32bits)0x5354554e)); return 0; } ////////////// CRC /////////////////////////////////////////////// #define CRC_MASK 0xFFFFFFFFUL #define UPDATE_CRC(crc, c) crc = crctable[(u08bits)crc ^ (u08bits)(c)] ^ (crc >> 8) static const u32bits crctable[256] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, }; /* #define CRCPOLY 0xEDB88320UL reversed 0x04C11DB7 1110 1101 1001 1000 1000 0011 0010 0000 static void make_crctable(void) { uint i, j; u32bits r; for (i = 0; i < 256; ++i) { r = i; for (j = 8; j > 0; --j) { if (r & 1) r = (r >> 1) ^ CRCPOLY; else r >>= 1; } crctable[i] = r; } } */ static u32bits ns_crc32(const u08bits *buffer, u32bits len) { u32bits crc = CRC_MASK; while ( len-- ) UPDATE_CRC( crc, *buffer++ ); return (~crc); } //////////// SASLprep RFC 4013 ///////////////////////////////////////// /* We support only basic ASCII table */ int SASLprep(u08bits *s) { if(s) { u08bits *strin = s; u08bits *strout = s; for(;;) { u08bits c = *strin; if(!c) { *strout=0; break; } switch(c) { case 0xAD: ++strin; break; case 0xA0: case 0x20: *strout=0x20; ++strout; ++strin; break; case 0x7F: return -1; default: if(c<0x1F) return -1; if(c>=0x80 && c<=0x9F) return -1; *strout=c; ++strout; ++strin; }; } } return 0; } //////////////// Message Integrity //////////////////////////// size_t get_hmackey_size(SHATYPE shatype) { if(shatype == SHATYPE_SHA256) return 32; if(shatype == SHATYPE_SHA384) return 48; if(shatype == SHATYPE_SHA512) return 64; return 16; } void print_bin_func(const char *name, size_t len, const void *s, const char *func) { printf("<%s>:<%s>:len=%d:[",func,name,(int)len); size_t i; for(i=0;i orig_len) return -1; if (stun_set_command_message_len_str(buf, new_len) < 0) return -1; if(ct == TURN_CREDENTIALS_SHORT_TERM) { res = stun_calculate_hmac(buf, (size_t) new_len - 4 - shasize, pwd, strlen((char*)pwd), new_hmac, &shasize, shatype); } else { res = stun_calculate_hmac(buf, (size_t) new_len - 4 - shasize, key, get_hmackey_size(shatype), new_hmac, &shasize, shatype); } stun_set_command_message_len_str(buf, orig_len); if(res<0) return -1; old_hmac = stun_attr_get_value(sar); if(!old_hmac) return -1; if(bcmp(old_hmac,new_hmac,shasize)) return 0; return +1; } /* * Return -1 if failure, 0 if the integrity is not correct, 1 if OK */ int stun_check_message_integrity_str(turn_credential_type ct, u08bits *buf, size_t len, u08bits *uname, u08bits *realm, u08bits *upwd, SHATYPE shatype) { hmackey_t key; password_t pwd; if(ct == TURN_CREDENTIALS_SHORT_TERM) strncpy((char*)pwd,(char*)upwd,sizeof(password_t)); else if (stun_produce_integrity_key_str(uname, realm, upwd, key, shatype) < 0) return -1; return stun_check_message_integrity_by_key_str(ct, buf, len, key, pwd, shatype); } /* RFC 5780 */ int stun_attr_get_change_request_str(stun_attr_ref attr, int *change_ip, int *change_port) { if(stun_attr_get_len(attr) == 4) { const u08bits* value = stun_attr_get_value(attr); if(value) { *change_ip = (value[3] & (u08bits)0x04); *change_port = (value[3] & (u08bits)0x02); return 0; } } return -1; } int stun_attr_add_change_request_str(u08bits *buf, size_t *len, int change_ip, int change_port) { u08bits avalue[4]={0,0,0,0}; if(change_ip) { if(change_port) { avalue[3] = 0x06; } else { avalue[3] = 0x04; } } else if(change_port) { avalue[3]=0x02; } return stun_attr_add_str(buf, len, STUN_ATTRIBUTE_CHANGE_REQUEST, avalue, 4); } int stun_attr_get_response_port_str(stun_attr_ref attr) { if(stun_attr_get_len(attr) >= 2) { const u08bits* value = stun_attr_get_value(attr); if(value) { return nswap16(((const u16bits*)value)[0]); } } return -1; } int stun_attr_add_response_port_str(u08bits *buf, size_t *len, u16bits port) { u08bits avalue[4]={0,0,0,0}; u16bits *port_ptr = (u16bits*)avalue; *port_ptr = nswap16(port); return stun_attr_add_str(buf, len, STUN_ATTRIBUTE_RESPONSE_PORT, avalue, 4); } int stun_attr_get_padding_len_str(stun_attr_ref attr) { int len = stun_attr_get_len(attr); if(len<0) return -1; return (u16bits)len; } int stun_attr_add_padding_str(u08bits *buf, size_t *len, u16bits padding_len) { u08bits avalue[0xFFFF]; ns_bzero(avalue,padding_len); return stun_attr_add_str(buf, len, STUN_ATTRIBUTE_PADDING, avalue, padding_len); } /* OAUTH */ #define OAUTH_ERROR(...) fprintf(stderr,__VA_ARGS__) static void remove_spaces(char *s) { char *sfns = s; while(*sfns) { if(*sfns != ' ') break; ++sfns; } if(*sfns) { if(sfns != s) { while(*sfns && (*sfns != ' ')) { *s = *sfns; ++s; ++sfns; }; *s = 0; } else { while(*s) { if(*s == ' ') { *s = 0; break; } ++s; } } } } static void normalize_algorithm(char *s) { char c = *s; while(c) { if(c=='_') *s='-'; else if((c>='a')&&(c<='z')) { *s = c - 'a' + 'A'; } ++s; c = *s; } } size_t calculate_enc_key_length(ENC_ALG a); size_t calculate_enc_key_length(ENC_ALG a) { switch(a) { #if !defined(TURN_NO_GCM) case A128GCM: return 16; #endif default: break; }; return 32; } size_t calculate_auth_key_length(ENC_ALG a); size_t calculate_auth_key_length(ENC_ALG a) { switch(a) { #if !defined(TURN_NO_GCM) case A256GCM: case A128GCM: return 0; #endif default: break; }; return 0; } int calculate_key(char *key, size_t key_size, char *new_key, size_t new_key_size); int calculate_key(char *key, size_t key_size, char *new_key, size_t new_key_size) { UNUSED_ARG(key_size); ns_bcopy(key,new_key,new_key_size); return 0; } int convert_oauth_key_data(const oauth_key_data *oakd0, oauth_key *key, char *err_msg, size_t err_msg_size) { if(oakd0 && key) { oauth_key_data oakd_obj; ns_bcopy(oakd0,&oakd_obj,sizeof(oauth_key_data)); oauth_key_data *oakd = &oakd_obj; if(!(oakd->ikm_key_size)) { if(err_msg) { snprintf(err_msg,err_msg_size,"key is not defined"); } } remove_spaces(oakd->kid); remove_spaces(oakd->as_rs_alg); normalize_algorithm(oakd->as_rs_alg); if(!(oakd->kid[0])) { if(err_msg) { snprintf(err_msg,err_msg_size,"KID is not defined"); } OAUTH_ERROR("KID is not defined\n"); return -1; } ns_bzero(key,sizeof(oauth_key)); STRCPY(key->kid,oakd->kid); ns_bcopy(oakd->ikm_key,key->ikm_key,sizeof(key->ikm_key)); key->ikm_key_size = oakd->ikm_key_size; key->timestamp = oakd->timestamp; key->lifetime = oakd->lifetime; if(!(key->timestamp)) key->timestamp = OAUTH_DEFAULT_TIMESTAMP; if(!(key->lifetime)) key->lifetime = OAUTH_DEFAULT_LIFETIME; key->as_rs_alg = ENC_ALG_ERROR; #if !defined(TURN_NO_GCM) key->as_rs_alg = ENC_ALG_DEFAULT; if(!strcmp(oakd->as_rs_alg,"A128GCM")) { key->as_rs_alg = A128GCM; key->auth_key_size = 0; key->auth_key[0] = 0; } else if(!strcmp(oakd->as_rs_alg,"A256GCM")) { key->as_rs_alg = A256GCM; key->auth_key_size = 0; key->auth_key[0] = 0; } else #endif { if(err_msg) { snprintf(err_msg,err_msg_size,"Wrong oAuth token encryption algorithm: %s (2)\n",oakd->as_rs_alg); } OAUTH_ERROR("Wrong oAuth token encryption algorithm: %s (3)\n",oakd->as_rs_alg); return -1; } #if !defined(TURN_NO_GCM) key->auth_key_size = calculate_auth_key_length(key->as_rs_alg); if(key->auth_key_size) { if(calculate_key(key->ikm_key,key->ikm_key_size,key->auth_key,key->auth_key_size)<0) { return -1; } } key->as_rs_key_size = calculate_enc_key_length(key->as_rs_alg); if(calculate_key(key->ikm_key,key->ikm_key_size,key->as_rs_key,key->as_rs_key_size)<0) { return -1; } #endif } return 0; } const EVP_CIPHER *get_cipher_type(ENC_ALG enc_alg); const EVP_CIPHER *get_cipher_type(ENC_ALG enc_alg) { switch(enc_alg) { #if !defined(TURN_NO_GCM) case A128GCM: return EVP_aes_128_gcm(); case A256GCM: return EVP_aes_256_gcm(); #endif default: break; } OAUTH_ERROR("%s: Unsupported enc algorithm: %d\n",__FUNCTION__,(int)enc_alg); return NULL; } int my_EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl); int my_EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl) { int cycle = 0; int out_len = 0; while((out_len>\n",name); size_t i; for(i = 0;i",(unsigned int)f[i]); } printf("\n<<==field %s\n",name); } int encode_oauth_token_normal(const u08bits *server_name, encoded_oauth_token *etoken, const oauth_key *key, const oauth_token *dtoken); int encode_oauth_token_normal(const u08bits *server_name, encoded_oauth_token *etoken, const oauth_key *key, const oauth_token *dtoken) { UNUSED_ARG(server_name); UNUSED_ARG(etoken); UNUSED_ARG(key); UNUSED_ARG(dtoken); /* if(server_name && etoken && key && dtoken && (dtoken->enc_block.key_length<=128)) { unsigned char orig_field[MAX_ENCODED_OAUTH_TOKEN_SIZE]; ns_bzero(orig_field,sizeof(orig_field)); size_t len = 0; *((uint16_t*)(orig_field+len)) = nswap16(dtoken->enc_block.key_length); len +=2; ns_bcopy(dtoken->enc_block.mac_key,orig_field+len,dtoken->enc_block.key_length); len += dtoken->enc_block.key_length; *((uint64_t*)(orig_field+len)) = nswap64(dtoken->enc_block.timestamp); len += 8; *((uint32_t*)(orig_field+len)) = nswap32(dtoken->enc_block.lifetime); len += 4; const EVP_CIPHER * cipher = get_cipher_type(key->as_rs_alg); if(!cipher) return -1; unsigned char *encoded_field = (unsigned char*)etoken->token; EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX_init(&ctx); EVP_EncryptInit_ex(&ctx, cipher, NULL, (const unsigned char *)key->as_rs_key, NULL); EVP_CIPHER_CTX_set_padding(&ctx,1); int outl=0; my_EVP_EncryptUpdate(&ctx, encoded_field, &outl, orig_field, (int)len); if(outl % OAUTH_ENC_ALG_BLOCK_SIZE) { int tmp_outl = 0; EVP_EncryptFinal_ex(&ctx, encoded_field + outl, &tmp_outl); outl += tmp_outl; } EVP_CIPHER_CTX_cleanup(&ctx); size_t sn_len = strlen((const char*)server_name); ns_bcopy(server_name,encoded_field+outl,sn_len); outl += sn_len; const EVP_MD *md = get_auth_type(key->auth_alg); if(!md) return -1; unsigned int hmac_len = EVP_MD_size(md); if (!HMAC(md, key->auth_key, key->auth_key_size, encoded_field, outl, encoded_field + outl, &hmac_len)) { return -1; } update_hmac_len(key->auth_alg, &hmac_len); ns_bcopy(encoded_field + outl, encoded_field + outl - sn_len, hmac_len); outl -= sn_len; outl += hmac_len; //encoded+hmac etoken->size = outl; return 0; } */ return -1; } int decode_oauth_token_normal(const u08bits *server_name, const encoded_oauth_token *etoken, const oauth_key *key, oauth_token *dtoken); int decode_oauth_token_normal(const u08bits *server_name, const encoded_oauth_token *etoken, const oauth_key *key, oauth_token *dtoken) { UNUSED_ARG(server_name); UNUSED_ARG(etoken); UNUSED_ARG(key); UNUSED_ARG(dtoken); /* if(server_name && etoken && key && dtoken) { size_t mac_size = calculate_auth_output_length(key->auth_alg); size_t min_encoded_field_size = 2+4+8+1; if(etoken->size < mac_size+min_encoded_field_size) { OAUTH_ERROR("%s: token size too small: %d, mac_size=%d, min_encoded_field_size=%d\n",__FUNCTION__,(int)etoken->size,(int)mac_size,(int)min_encoded_field_size); return -1; } const unsigned char* encoded_field = (const unsigned char*)etoken->token; unsigned int encoded_field_size = (unsigned int)etoken->size-mac_size; const unsigned char* mac = ((const unsigned char*)etoken->token) + etoken->size - mac_size; { const EVP_MD *md = get_auth_type(key->auth_alg); if(!md) return -1; unsigned int hmac_len = EVP_MD_size(md); update_hmac_len(key->auth_alg,&hmac_len); if(hmac_len != mac_size) { OAUTH_ERROR("%s: mac size is wrong: %d, must be %d\n",__FUNCTION__,(int)mac_size,(int)hmac_len); return -1; } unsigned char efield[MAX_ENCODED_OAUTH_TOKEN_SIZE]; unsigned char check_mac[MAXSHASIZE]; ns_bcopy(encoded_field,efield,encoded_field_size); size_t sn_len = strlen((const char*)server_name); ns_bcopy(server_name,efield+encoded_field_size,sn_len); if (!HMAC(md, key->auth_key, key->auth_key_size, efield, encoded_field_size+sn_len, check_mac, &hmac_len)) { return -1; } if(ns_bcmp(check_mac,mac,mac_size)) { OAUTH_ERROR("%s: token integrity check failed\n",__FUNCTION__); return -1; } } unsigned char decoded_field[MAX_ENCODED_OAUTH_TOKEN_SIZE]; const EVP_CIPHER * cipher = get_cipher_type(key->as_rs_alg); if(!cipher) return -1; EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX_init(&ctx); EVP_DecryptInit_ex(&ctx, cipher, NULL, (const unsigned char *)key->as_rs_key, NULL); EVP_CIPHER_CTX_set_padding(&ctx,1); int outl=0; my_EVP_DecryptUpdate(&ctx, decoded_field, &outl, encoded_field, (int)encoded_field_size); int tmp_outl = 0; EVP_DecryptFinal_ex(&ctx, decoded_field + outl, &tmp_outl); outl += tmp_outl; EVP_CIPHER_CTX_cleanup(&ctx); size_t len = 0; dtoken->enc_block.key_length = nswap16(*((uint16_t*)(decoded_field+len))); len += 2; ns_bcopy(decoded_field+len,dtoken->enc_block.mac_key,dtoken->enc_block.key_length); len += dtoken->enc_block.key_length; dtoken->enc_block.timestamp = nswap64(*((uint64_t*)(decoded_field+len))); len += 8; dtoken->enc_block.lifetime = nswap32(*((uint32_t*)(decoded_field+len))); len += 4; return 0; } */ return -1; } static void generate_random_nonce(unsigned char *nonce, size_t sz) { if(!RAND_bytes(nonce, sz)) { size_t i; for(i=0;ienc_block.key_length<=MAXSHASIZE)) { unsigned char orig_field[MAX_ENCODED_OAUTH_TOKEN_SIZE]; ns_bzero(orig_field,sizeof(orig_field)); unsigned char nonce[OAUTH_GCM_NONCE_SIZE]; if(nonce0) { ns_bcopy(nonce0,nonce,sizeof(nonce)); } else { generate_random_nonce(nonce, sizeof(nonce)); } size_t len = 0; *((uint16_t*)(orig_field+len)) = nswap16(OAUTH_GCM_NONCE_SIZE); len +=2; ns_bcopy(nonce,orig_field+len,OAUTH_GCM_NONCE_SIZE); len += OAUTH_GCM_NONCE_SIZE; *((uint16_t*)(orig_field+len)) = nswap16(dtoken->enc_block.key_length); len +=2; ns_bcopy(dtoken->enc_block.mac_key,orig_field+len,dtoken->enc_block.key_length); len += dtoken->enc_block.key_length; uint64_t ts = nswap64(dtoken->enc_block.timestamp); ns_bcopy( &ts, (orig_field+len), sizeof(ts)); len += sizeof(ts); *((uint32_t*)(orig_field+len)) = nswap32(dtoken->enc_block.lifetime); len += 4; const EVP_CIPHER * cipher = get_cipher_type(key->as_rs_alg); if(!cipher) return -1; #if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX *ctxp = &ctx; #else EVP_CIPHER_CTX *ctxp = EVP_CIPHER_CTX_new(); #endif EVP_CIPHER_CTX_init(ctxp); /* Initialize the encryption operation. */ if(1 != EVP_EncryptInit_ex(ctxp, cipher, NULL, NULL, NULL)) return -1; EVP_CIPHER_CTX_set_padding(ctxp,1); /* Set IV length if default 12 bytes (96 bits) is not appropriate */ if(1 != EVP_CIPHER_CTX_ctrl(ctxp, EVP_CTRL_GCM_SET_IVLEN, OAUTH_GCM_NONCE_SIZE, NULL)) return -1; /* Initialize key and IV */ if(1 != EVP_EncryptInit_ex(ctxp, NULL, NULL, (const unsigned char *)key->as_rs_key, nonce)) return -1; int outl=0; size_t sn_len = strlen((const char*)server_name); /* Provide any AAD data. This can be called zero or more times as * required */ if(1 != my_EVP_EncryptUpdate(ctxp, NULL, &outl, server_name, (int)sn_len)) return -1; outl=0; unsigned char *encoded_field = (unsigned char*)etoken->token; ns_bcopy(orig_field,encoded_field,OAUTH_GCM_NONCE_SIZE + 2); encoded_field += OAUTH_GCM_NONCE_SIZE + 2; unsigned char *start_field = orig_field + OAUTH_GCM_NONCE_SIZE + 2; len -= OAUTH_GCM_NONCE_SIZE + 2; if(1 != my_EVP_EncryptUpdate(ctxp, encoded_field, &outl, start_field, (int)len)) return -1; int tmp_outl = 0; EVP_EncryptFinal_ex(ctxp, encoded_field + outl, &tmp_outl); outl += tmp_outl; EVP_CIPHER_CTX_ctrl(ctxp, EVP_CTRL_GCM_GET_TAG, OAUTH_GCM_TAG_SIZE, encoded_field + outl); outl += OAUTH_GCM_TAG_SIZE; etoken->size = 2 + OAUTH_GCM_NONCE_SIZE + outl; #if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_CIPHER_CTX_cleanup(ctxp); #else EVP_CIPHER_CTX_free(ctxp); #endif return 0; } return -1; } static int decode_oauth_token_gcm(const u08bits *server_name, const encoded_oauth_token *etoken, const oauth_key *key, oauth_token *dtoken) { if(server_name && etoken && key && dtoken) { unsigned char snl[2]; ns_bcopy((const unsigned char*)(etoken->token),snl,2); const unsigned char *csnl = snl; uint16_t nonce_len = nswap16(*((const uint16_t*)csnl)); dtoken->enc_block.nonce_length = nonce_len; size_t min_encoded_field_size = 2+4+8+nonce_len+2+OAUTH_GCM_TAG_SIZE+1; if(etoken->size < min_encoded_field_size) { OAUTH_ERROR("%s: token size too small: %d\n",__FUNCTION__,(int)etoken->size); return -1; } const unsigned char* encoded_field = (const unsigned char*)(etoken->token + nonce_len + 2); unsigned int encoded_field_size = (unsigned int)etoken->size - nonce_len - 2 - OAUTH_GCM_TAG_SIZE; const unsigned char* nonce = ((const unsigned char*)etoken->token + 2); ns_bcopy(nonce,dtoken->enc_block.nonce,nonce_len); unsigned char tag[OAUTH_GCM_TAG_SIZE]; ns_bcopy(((const unsigned char*)etoken->token) + nonce_len + 2 + encoded_field_size, tag ,sizeof(tag)); unsigned char decoded_field[MAX_ENCODED_OAUTH_TOKEN_SIZE]; const EVP_CIPHER * cipher = get_cipher_type(key->as_rs_alg); if(!cipher) { OAUTH_ERROR("%s: Cannot find cipher for algorithm: %d\n",__FUNCTION__,(int)key->as_rs_alg); return -1; } #if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX *ctxp = &ctx; #else EVP_CIPHER_CTX *ctxp = EVP_CIPHER_CTX_new(); #endif EVP_CIPHER_CTX_init(ctxp); /* Initialize the decryption operation. */ if(1 != EVP_DecryptInit_ex(ctxp, cipher, NULL, NULL, NULL)) { OAUTH_ERROR("%s: Cannot initialize decryption\n",__FUNCTION__); return -1; } //EVP_CIPHER_CTX_set_padding(&ctx,1); /* Set IV length if default 12 bytes (96 bits) is not appropriate */ if(1 != EVP_CIPHER_CTX_ctrl(ctxp, EVP_CTRL_GCM_SET_IVLEN, nonce_len, NULL)) { OAUTH_ERROR("%s: Cannot set nonce length\n",__FUNCTION__); return -1; } /* Initialize key and IV */ if(1 != EVP_DecryptInit_ex(ctxp, NULL, NULL, (const unsigned char *)key->as_rs_key, nonce)) { OAUTH_ERROR("%s: Cannot set nonce\n",__FUNCTION__); return -1; } /* Set expected tag value. A restriction in OpenSSL 1.0.1c and earlier + * required the tag before any AAD or ciphertext */ EVP_CIPHER_CTX_ctrl (ctxp, EVP_CTRL_GCM_SET_TAG, OAUTH_GCM_TAG_SIZE, tag); int outl=0; size_t sn_len = strlen((const char*)server_name); /* Provide any AAD data. This can be called zero or more times as * required */ if(1 != my_EVP_DecryptUpdate(ctxp, NULL, &outl, server_name, (int)sn_len)) { OAUTH_ERROR("%s: Cannot decrypt update server_name: %s, len=%d\n",__FUNCTION__,server_name,(int)sn_len); return -1; } if(1 != my_EVP_DecryptUpdate(ctxp, decoded_field, &outl, encoded_field, (int)encoded_field_size)) { OAUTH_ERROR("%s: Cannot decrypt update\n",__FUNCTION__); return -1; } int tmp_outl = 0; if(EVP_DecryptFinal_ex(ctxp, decoded_field + outl, &tmp_outl)<1) { #if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_CIPHER_CTX_cleanup(ctxp); #else EVP_CIPHER_CTX_free(ctxp); #endif OAUTH_ERROR("%s: token integrity check failed\n",__FUNCTION__); return -1; } outl += tmp_outl; #if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_CIPHER_CTX_cleanup(ctxp); #else EVP_CIPHER_CTX_free(ctxp); #endif size_t len = 0; dtoken->enc_block.key_length = nswap16(*((uint16_t*)(decoded_field+len))); len += 2; ns_bcopy(decoded_field+len,dtoken->enc_block.mac_key,dtoken->enc_block.key_length); len += dtoken->enc_block.key_length; uint64_t ts; ns_bcopy((decoded_field+len),&ts,sizeof(ts)); dtoken->enc_block.timestamp = nswap64(ts); len += sizeof(ts); uint32_t lt; ns_bcopy((decoded_field+len),<,sizeof(lt)); dtoken->enc_block.lifetime = nswap32(lt); len += sizeof(lt); return 0; } return -1; } #endif int encode_oauth_token(const u08bits *server_name, encoded_oauth_token *etoken, const oauth_key *key, const oauth_token *dtoken, const u08bits *nonce) { UNUSED_ARG(nonce); if(server_name && etoken && key && dtoken) { switch(key->as_rs_alg) { #if !defined(TURN_NO_GCM) case A256GCM: case A128GCM: return encode_oauth_token_gcm(server_name, etoken,key,dtoken,nonce); #endif default: fprintf(stderr,"Unsupported AS_RS algorithm: %d\n",(int)key->as_rs_alg); break; }; } return -1; } int decode_oauth_token(const u08bits *server_name, const encoded_oauth_token *etoken, const oauth_key *key, oauth_token *dtoken) { if(server_name && etoken && key && dtoken) { switch(key->as_rs_alg) { #if !defined(TURN_NO_GCM) case A256GCM: case A128GCM: return decode_oauth_token_gcm(server_name, etoken,key,dtoken); #endif default: fprintf(stderr,"Unsupported AS_RS algorithm: %d\n",(int)key->as_rs_alg); break; }; } return -1; } /////////////////////////////////////////////////////////////// turnserver-4.5.1.1/src/client/ns_turn_msg_defs.h0000664000175000017500000002036113436577655020371 0ustar misimisi/* * 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. */ #ifndef __LIB_TURN_MSG_DEFS__ #define __LIB_TURN_MSG_DEFS__ #include "ns_turn_msg_defs_experimental.h" /////////////////////////////////////////// // http://www.iana.org/assignments/stun-parameters/stun-parameters.xhtml /////////////////////////////////////////// #define STUN_HEADER_LENGTH (20) #define STUN_CHANNEL_HEADER_LENGTH (4) #define STUN_MAX_USERNAME_SIZE (512) #define STUN_MAX_REALM_SIZE (127) #define STUN_MAX_NONCE_SIZE (127) #define STUN_MAX_SERVER_NAME_SIZE (1025) #define STUN_MAX_PWD_SIZE (256) #define AUTH_SECRET_SIZE STUN_MAX_PWD_SIZE #define STUN_MAGIC_COOKIE (0x2112A442) #define IS_STUN_REQUEST(msg_type) (((msg_type) & 0x0110) == 0x0000) #define IS_STUN_INDICATION(msg_type) (((msg_type) & 0x0110) == 0x0010) #define IS_STUN_SUCCESS_RESP(msg_type) (((msg_type) & 0x0110) == 0x0100) #define IS_STUN_ERR_RESP(msg_type) (((msg_type) & 0x0110) == 0x0110) #define GET_STUN_REQUEST(msg_type) (msg_type & 0xFEEF) #define GET_STUN_INDICATION(msg_type) ((msg_type & 0xFEEF)|0x0010) #define GET_STUN_SUCCESS_RESP(msg_type) ((msg_type & 0xFEEF)|0x0100) #define GET_STUN_ERR_RESP(msg_type) (msg_type | 0x0110) /* Lifetimes: */ #define STUN_DEFAULT_ALLOCATE_LIFETIME (600) #define STUN_MIN_ALLOCATE_LIFETIME STUN_DEFAULT_ALLOCATE_LIFETIME #define STUN_DEFAULT_MAX_ALLOCATE_LIFETIME (3600) #define STUN_DEFAULT_CHANNEL_LIFETIME (600) #define STUN_DEFAULT_NONCE_EXPIRATION_TIME (600) #define STUN_DEFAULT_PERMISSION_LIFETIME (300) /**/ #define STUN_METHOD_BINDING (0x0001) #define STUN_METHOD_ALLOCATE (0x0003) #define STUN_METHOD_REFRESH (0x0004) #define STUN_METHOD_SEND (0x0006) #define STUN_METHOD_DATA (0x0007) #define STUN_METHOD_CREATE_PERMISSION (0x0008) #define STUN_METHOD_CHANNEL_BIND (0x0009) /* RFC 6062 ==>>*/ #define STUN_METHOD_CONNECT (0x000a) #define STUN_METHOD_CONNECTION_BIND (0x000b) #define STUN_METHOD_CONNECTION_ATTEMPT (0x000c) /* <<== RFC 6062 */ #define STUN_ATTRIBUTE_MAPPED_ADDRESS (0x0001) #define OLD_STUN_ATTRIBUTE_RESPONSE_ADDRESS (0x0002) #define STUN_ATTRIBUTE_CHANGE_REQUEST (0x0003) #define OLD_STUN_ATTRIBUTE_SOURCE_ADDRESS (0x0004) #define OLD_STUN_ATTRIBUTE_CHANGED_ADDRESS (0x0005) #define STUN_ATTRIBUTE_USERNAME (0x0006) #define OLD_STUN_ATTRIBUTE_PASSWORD (0x0007) #define STUN_ATTRIBUTE_MESSAGE_INTEGRITY (0x0008) #define STUN_ATTRIBUTE_ERROR_CODE (0x0009) #define STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES (0x000A) #define OLD_STUN_ATTRIBUTE_REFLECTED_FROM (0x000B) #define STUN_ATTRIBUTE_REALM (0x0014) #define STUN_ATTRIBUTE_NONCE (0x0015) #define STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY (0x0017) #define STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS (0x0020) #define OLD_STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS (0x8020) #define STUN_ATTRIBUTE_SOFTWARE (0x8022) #define OLD_STUN_ATTRIBUTE_SERVER STUN_ATTRIBUTE_SOFTWARE #define STUN_ATTRIBUTE_ALTERNATE_SERVER (0x8023) #define STUN_ATTRIBUTE_FINGERPRINT (0x8028) #define STUN_ATTRIBUTE_CHANNEL_NUMBER (0x000C) #define STUN_ATTRIBUTE_LIFETIME (0x000D) #define STUN_ATTRIBUTE_BANDWIDTH (0x0010) #define STUN_ATTRIBUTE_XOR_PEER_ADDRESS (0x0012) #define STUN_ATTRIBUTE_DATA (0x0013) #define STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS (0x0016) #define STUN_ATTRIBUTE_EVEN_PORT (0x0018) #define STUN_ATTRIBUTE_REQUESTED_TRANSPORT (0x0019) #define STUN_ATTRIBUTE_DONT_FRAGMENT (0x001A) #define STUN_ATTRIBUTE_TIMER_VAL (0x0021) #define STUN_ATTRIBUTE_RESERVATION_TOKEN (0x0022) /* ICE */ #define STUN_ATTRIBUTE_PRIORITY (0x0024) #define STUN_ATTRIBUTE_ICE_CONTROLLED (0x8029) /* RFC 5780 */ #define STUN_ATTRIBUTE_PADDING (0x0026) #define STUN_ATTRIBUTE_RESPONSE_PORT (0x0027) #define STUN_ATTRIBUTE_RESPONSE_ORIGIN (0x802B) #define STUN_ATTRIBUTE_OTHER_ADDRESS (0x802C) /* RFC 6062 ==>> */ #define STUN_ATTRIBUTE_CONNECTION_ID (0x002A) /* <<== RFC 6062 */ #define STUN_VALID_CHANNEL(chn) ((chn)>=0x4000 && (chn)<=0x7FFF) ///////// extra values ////////////////// /* RFC 6156 ==>> */ #define STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4 (0x01) #define STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6 (0x02) #define STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT (0x00) #define STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_INVALID (-1) /* <<== RFC 6156 */ /* RFC 6062 ==>> */ #define STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE (6) #define STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE (17) #define STUN_ATTRIBUTE_TRANSPORT_TLS_VALUE (56) #define STUN_ATTRIBUTE_TRANSPORT_DTLS_VALUE (250) /* <<== RFC 6062 */ /* SHA ==>> */ #define SHA1SIZEBYTES (20) #define SHA256SIZEBYTES (32) #define SHA384SIZEBYTES (48) #define SHA512SIZEBYTES (64) #define MAXSHASIZE (128) enum _SHATYPE { SHATYPE_ERROR = -1, SHATYPE_DEFAULT=0, SHATYPE_SHA1=SHATYPE_DEFAULT, SHATYPE_SHA256, SHATYPE_SHA384, SHATYPE_SHA512 }; typedef enum _SHATYPE SHATYPE; #define shatype_name(sht) ((sht == SHATYPE_SHA1) ? "SHA1" : ((sht == SHATYPE_SHA256) ? "SHA256" : ((sht == SHATYPE_SHA384) ? "SHA384" : "SHA512"))) /* <<== SHA */ /* OAUTH TOKEN ENC ALG ==> */ enum _ENC_ALG { ENC_ALG_ERROR=-1, #if !defined(TURN_NO_GCM) ENC_ALG_DEFAULT=0, A256GCM=ENC_ALG_DEFAULT, A128GCM, #endif ENC_ALG_NUM }; typedef enum _ENC_ALG ENC_ALG; /* <<== OAUTH TOKEN ENC ALG */ /** * oAuth struct */ #define STUN_ATTRIBUTE_THIRD_PARTY_AUTHORIZATION (0x802E) #define STUN_ATTRIBUTE_OAUTH_ACCESS_TOKEN (0x001B) #define OAUTH_KID_SIZE (128) #define OAUTH_HASH_FUNC_SIZE (64) #define OAUTH_ALG_SIZE (64) #define OAUTH_KEY_SIZE (256) #define OAUTH_GCM_NONCE_SIZE (12) #define OAUTH_MAX_NONCE_SIZE (256) #define OAUTH_GCM_TAG_SIZE (16) #define OAUTH_ENC_ALG_BLOCK_SIZE (16) #define OAUTH_DEFAULT_LIFETIME (0) #define OAUTH_DEFAULT_TIMESTAMP (turn_time()) #define OAUTH_TIME_DELTA (5) struct _oauth_key_data { char kid[OAUTH_KID_SIZE+1]; char ikm_key[OAUTH_KEY_SIZE+1]; size_t ikm_key_size; turn_time_t timestamp; turn_time_t lifetime; char as_rs_alg[OAUTH_ALG_SIZE+1]; }; typedef struct _oauth_key_data oauth_key_data; struct _oauth_key { char kid[OAUTH_KID_SIZE+1]; char ikm_key[OAUTH_KEY_SIZE+1]; size_t ikm_key_size; turn_time_t timestamp; turn_time_t lifetime; ENC_ALG as_rs_alg; char as_rs_key[OAUTH_KEY_SIZE+1]; size_t as_rs_key_size; char auth_key[OAUTH_KEY_SIZE+1]; size_t auth_key_size; }; typedef struct _oauth_key oauth_key; struct _oauth_encrypted_block { uint16_t nonce_length; uint8_t nonce[OAUTH_MAX_NONCE_SIZE]; uint16_t key_length; uint8_t mac_key[MAXSHASIZE]; uint64_t timestamp; uint32_t lifetime; }; typedef struct _oauth_encrypted_block oauth_encrypted_block; struct _oauth_token { oauth_encrypted_block enc_block; }; typedef struct _oauth_token oauth_token; #define MAX_ENCODED_OAUTH_TOKEN_SIZE (1024) struct _encoded_oauth_token { char token[MAX_ENCODED_OAUTH_TOKEN_SIZE]; size_t size; }; typedef struct _encoded_oauth_token encoded_oauth_token; //////////////////////////////////////////////// #endif //__LIB_TURN_MSG_DEFS__ turnserver-4.5.1.1/src/client/ns_turn_ioaddr.h0000664000175000017500000000761313436577655020051 0ustar misimisi/* * 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. */ #ifndef __IOADDR__ #define __IOADDR__ #include "ns_turn_defs.h" #ifdef __cplusplus extern "C" { #endif ///////////////////////////////////////////////////// #define MAX_IOA_ADDR_STRING (65) typedef union { struct sockaddr ss; struct sockaddr_in s4; struct sockaddr_in6 s6; } ioa_addr; typedef struct { ioa_addr min; ioa_addr max; } ioa_addr_range; //////////////////////////// u32bits get_ioa_addr_len(const ioa_addr* addr); //////////////////////////// void addr_set_any(ioa_addr *addr); int addr_any(const ioa_addr* addr); int addr_any_no_port(const ioa_addr* addr); u32bits addr_hash(const ioa_addr *addr); u32bits addr_hash_no_port(const ioa_addr *addr); void addr_cpy(ioa_addr* dst, const ioa_addr* src); void addr_cpy4(ioa_addr* dst, const struct sockaddr_in* src); void addr_cpy6(ioa_addr* dst, const struct sockaddr_in6* src); int addr_eq(const ioa_addr* a1, const ioa_addr *a2); int addr_eq_no_port(const ioa_addr* a1, const ioa_addr *a2); int make_ioa_addr(const u08bits* saddr, int port, ioa_addr *addr); int make_ioa_addr_from_full_string(const u08bits* saddr, int default_port, ioa_addr *addr); void addr_set_port(ioa_addr* addr, int port); int addr_get_port(const ioa_addr* addr); int addr_to_string(const ioa_addr* addr, u08bits* saddr); int addr_to_string_no_port(const ioa_addr* addr, u08bits* saddr); u32bits hash_int32(u32bits a); u64bits hash_int64(u64bits a); /////////////////////////////////////////// void ioa_addr_range_set(ioa_addr_range* range, const ioa_addr* addr_min, const ioa_addr* addr_max); int addr_less_eq(const ioa_addr* addr1, const ioa_addr* addr2); int ioa_addr_in_range(const ioa_addr_range* range, const ioa_addr* addr); void ioa_addr_range_cpy(ioa_addr_range* dest, const ioa_addr_range* src); /////// Check whether this is a good address ////////////// int ioa_addr_is_multicast(ioa_addr *a); int ioa_addr_is_loopback(ioa_addr *addr); /////// Map "public" address to "private" address ////////////// // Must be called only in a single-threaded context, // before the program starts spawning threads: void ioa_addr_add_mapping(ioa_addr *apub, ioa_addr *apriv); void map_addr_from_public_to_private(const ioa_addr *public_addr, ioa_addr *private_addr); void map_addr_from_private_to_public(const ioa_addr *private_addr, ioa_addr *public_addr); /////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__IOADDR__ turnserver-4.5.1.1/src/client/ns_turn_ioaddr.c0000664000175000017500000003426013436577655020042 0ustar misimisi/* * 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_ioaddr.h" #include #include ////////////////////////////////////////////////////////////// u32bits get_ioa_addr_len(const ioa_addr* addr) { if(addr->ss.sa_family == AF_INET) return sizeof(struct sockaddr_in); else if(addr->ss.sa_family == AF_INET6) return sizeof(struct sockaddr_in6); return 0; } /////////////////////////////////////////////////////////////// void addr_set_any(ioa_addr *addr) { if(addr) ns_bzero(addr,sizeof(ioa_addr)); } int addr_any(const ioa_addr* addr) { if(!addr) return 1; if(addr->ss.sa_family == AF_INET) { return ((addr->s4.sin_addr.s_addr==0)&&(addr->s4.sin_port==0)); } else if(addr->ss.sa_family == AF_INET6) { if(addr->s6.sin6_port!=0) return 0; else { size_t i; for(i=0;is6.sin6_addr);i++) if(((const s08bits*)&(addr->s6.sin6_addr))[i]) return 0; } } return 1; } int addr_any_no_port(const ioa_addr* addr) { if(!addr) return 1; if(addr->ss.sa_family == AF_INET) { return (addr->s4.sin_addr.s_addr==0); } else if(addr->ss.sa_family == AF_INET6) { size_t i; for(i=0;is6.sin6_addr);i++) if(((const s08bits*)(&(addr->s6.sin6_addr)))[i]) return 0; } return 1; } u32bits hash_int32(u32bits a) { a = a ^ (a>>4); a = (a^0xdeadbeef) + (a<<5); a = a ^ (a>>11); return a; } u64bits hash_int64(u64bits a) { a = a ^ (a>>4); a = (a^0xdeadbeefdeadbeefLL) + (a<<5); a = a ^ (a>>11); return a; } u32bits addr_hash(const ioa_addr *addr) { if(!addr) return 0; u32bits ret = 0; if (addr->ss.sa_family == AF_INET) { ret = hash_int32(addr->s4.sin_addr.s_addr + addr->s4.sin_port); } else { u64bits a[2]; ns_bcopy(&(addr->s6.sin6_addr), &a, sizeof(a)); ret = (u32bits)((hash_int64(a[0])<<3) + (hash_int64(a[1] + addr->s6.sin6_port))); } return ret; } u32bits addr_hash_no_port(const ioa_addr *addr) { if(!addr) return 0; u32bits ret = 0; if (addr->ss.sa_family == AF_INET) { ret = hash_int32(addr->s4.sin_addr.s_addr); } else { u64bits a[2]; ns_bcopy(&(addr->s6.sin6_addr), &a, sizeof(a)); ret = (u32bits)((hash_int64(a[0])<<3) + (hash_int64(a[1]))); } return ret; } void addr_cpy(ioa_addr* dst, const ioa_addr* src) { if(dst && src) ns_bcopy(src,dst,sizeof(ioa_addr)); } void addr_cpy4(ioa_addr* dst, const struct sockaddr_in* src) { if(src && dst) ns_bcopy(src,dst,sizeof(struct sockaddr_in)); } void addr_cpy6(ioa_addr* dst, const struct sockaddr_in6* src) { if(src && dst) ns_bcopy(src,dst,sizeof(struct sockaddr_in6)); } int addr_eq(const ioa_addr* a1, const ioa_addr *a2) { if(!a1) return (!a2); else if(!a2) return (!a1); if(a1->ss.sa_family == a2->ss.sa_family) { if(a1->ss.sa_family == AF_INET && a1->s4.sin_port == a2->s4.sin_port) { if((int)a1->s4.sin_addr.s_addr == (int)a2->s4.sin_addr.s_addr) { return 1; } } else if(a1->ss.sa_family == AF_INET6 && a1->s6.sin6_port == a2->s6.sin6_port) { if( memcmp(&(a1->s6.sin6_addr), &(a2->s6.sin6_addr) ,sizeof(struct in6_addr)) == 0 ) { return 1; } } } return 0; } int addr_eq_no_port(const ioa_addr* a1, const ioa_addr *a2) { if(!a1) return (!a2); else if(!a2) return (!a1); if(a1->ss.sa_family == a2->ss.sa_family) { if(a1->ss.sa_family == AF_INET) { if((int)a1->s4.sin_addr.s_addr == (int)a2->s4.sin_addr.s_addr) { return 1; } } else if(a1->ss.sa_family == AF_INET6) { if( memcmp(&(a1->s6.sin6_addr), &(a2->s6.sin6_addr) ,sizeof(struct in6_addr)) == 0 ) { return 1; } } } return 0; } int make_ioa_addr(const u08bits* saddr0, int port, ioa_addr *addr) { if(!saddr0 || !addr) return -1; char ssaddr[257]; STRCPY(ssaddr,saddr0); char* saddr=ssaddr; while(*saddr == ' ') ++saddr; size_t len=strlen(saddr); while(len>0) { if(saddr[len-1]==' ') { saddr[len-1]=0; --len; } else { break; } } ns_bzero(addr, sizeof(ioa_addr)); if((len == 0)|| (inet_pton(AF_INET, saddr, &addr->s4.sin_addr) == 1)) { addr->s4.sin_family = AF_INET; #if defined(TURN_HAS_SIN_LEN) /* tested when configured */ addr->s4.sin_len = sizeof(struct sockaddr_in); #endif addr->s4.sin_port = nswap16(port); } else if (inet_pton(AF_INET6, saddr, &addr->s6.sin6_addr) == 1) { addr->s6.sin6_family = AF_INET6; #if defined(SIN6_LEN) /* this define is required by IPv6 if used */ addr->s6.sin6_len = sizeof(struct sockaddr_in6); #endif addr->s6.sin6_port = nswap16(port); } else { struct addrinfo addr_hints; struct addrinfo *addr_result = NULL; int err; memset(&addr_hints, 0, sizeof(struct addrinfo)); addr_hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ addr_hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ addr_hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ addr_hints.ai_protocol = 0; /* Any protocol */ addr_hints.ai_canonname = NULL; addr_hints.ai_addr = NULL; addr_hints.ai_next = NULL; err = getaddrinfo(saddr, NULL, &addr_hints, &addr_result); if ((err != 0)||(!addr_result)) { fprintf(stderr,"error resolving '%s' hostname: %s\n",saddr,gai_strerror(err)); return -1; } int family = AF_INET; struct addrinfo *addr_result_orig = addr_result; int found = 0; beg_af: while(addr_result) { if(addr_result->ai_family == family) { if (addr_result->ai_family == AF_INET) { ns_bcopy(addr_result->ai_addr, addr, addr_result->ai_addrlen); addr->s4.sin_port = nswap16(port); #if defined(TURN_HAS_SIN_LEN) /* tested when configured */ addr->s4.sin_len = sizeof(struct sockaddr_in); #endif found = 1; break; } else if (addr_result->ai_family == AF_INET6) { ns_bcopy(addr_result->ai_addr, addr, addr_result->ai_addrlen); addr->s6.sin6_port = nswap16(port); #if defined(SIN6_LEN) /* this define is required by IPv6 if used */ addr->s6.sin6_len = sizeof(struct sockaddr_in6); #endif found = 1; break; } } addr_result = addr_result->ai_next; } if(!found && family == AF_INET) { family = AF_INET6; addr_result = addr_result_orig; goto beg_af; } freeaddrinfo(addr_result_orig); } return 0; } static char* get_addr_string_and_port(char* s0, int *port) { char *s = s0; while(*s && (*s==' ')) ++s; if(*s == '[') { ++s; char *tail = strstr(s,"]"); if(tail) { *tail=0; ++tail; while(*tail && (*tail==' ')) ++tail; if(*tail==':') { ++tail; *port = atoi(tail); return s; } else if(*tail == 0) { *port = 0; return s; } } } else { char *tail = strstr(s,":"); if(tail) { *tail = 0; ++tail; *port = atoi(tail); return s; } else { *port = 0; return s; } } return NULL; } int make_ioa_addr_from_full_string(const u08bits* saddr, int default_port, ioa_addr *addr) { if(!addr) return -1; int ret = -1; int port = 0; char* s = turn_strdup((const char*)saddr); char *sa = get_addr_string_and_port(s,&port); if(sa) { if(port<1) port = default_port; ret = make_ioa_addr((u08bits*)sa,port,addr); } turn_free(s,strlen(s)+1); return ret; } int addr_to_string(const ioa_addr* addr, u08bits* saddr) { if (addr && saddr) { s08bits addrtmp[INET6_ADDRSTRLEN]; if (addr->ss.sa_family == AF_INET) { inet_ntop(AF_INET, &addr->s4.sin_addr, addrtmp, INET_ADDRSTRLEN); if(addr_get_port(addr)>0) snprintf((s08bits*)saddr, MAX_IOA_ADDR_STRING, "%s:%d", addrtmp, addr_get_port(addr)); else strncpy((s08bits*)saddr, addrtmp, MAX_IOA_ADDR_STRING); } else if (addr->ss.sa_family == AF_INET6) { inet_ntop(AF_INET6, &addr->s6.sin6_addr, addrtmp, INET6_ADDRSTRLEN); if(addr_get_port(addr)>0) snprintf((s08bits*)saddr, MAX_IOA_ADDR_STRING, "[%s]:%d", addrtmp, addr_get_port(addr)); else strncpy((s08bits*)saddr, addrtmp, MAX_IOA_ADDR_STRING); } else { return -1; } return 0; } return -1; } int addr_to_string_no_port(const ioa_addr* addr, u08bits* saddr) { if (addr && saddr) { s08bits addrtmp[MAX_IOA_ADDR_STRING]; if (addr->ss.sa_family == AF_INET) { inet_ntop(AF_INET, &addr->s4.sin_addr, addrtmp, INET_ADDRSTRLEN); strncpy((s08bits*)saddr, addrtmp, MAX_IOA_ADDR_STRING); } else if (addr->ss.sa_family == AF_INET6) { inet_ntop(AF_INET6, &addr->s6.sin6_addr, addrtmp, INET6_ADDRSTRLEN); strncpy((s08bits*)saddr, addrtmp, MAX_IOA_ADDR_STRING); } else { return -1; } return 0; } return -1; } void addr_set_port(ioa_addr* addr, int port) { if(addr) { if(addr->s4.sin_family == AF_INET) { addr->s4.sin_port = nswap16(port); } else if(addr->s6.sin6_family == AF_INET6) { addr->s6.sin6_port = nswap16(port); } } } int addr_get_port(const ioa_addr* addr) { if(!addr) return 0; if(addr->s4.sin_family == AF_INET) { return nswap16(addr->s4.sin_port); } else if(addr->s6.sin6_family == AF_INET6) { return nswap16(addr->s6.sin6_port); } return 0; } ///////////////////////////////////////////////////////////////////////////// void ioa_addr_range_set(ioa_addr_range* range, const ioa_addr* addr_min, const ioa_addr* addr_max) { if(range) { if(addr_min) addr_cpy(&(range->min),addr_min); else addr_set_any(&(range->min)); if(addr_max) addr_cpy(&(range->max),addr_max); else addr_set_any(&(range->max)); } } int addr_less_eq(const ioa_addr* addr1, const ioa_addr* addr2) { if(!addr1) return 1; else if(!addr2) return 0; else { if(addr1->ss.sa_family != addr2->ss.sa_family) return (addr1->ss.sa_family < addr2->ss.sa_family); else if(addr1->ss.sa_family == AF_INET) { return ((u32bits)nswap32(addr1->s4.sin_addr.s_addr) <= (u32bits)nswap32(addr2->s4.sin_addr.s_addr)); } else if(addr1->ss.sa_family == AF_INET6) { int i; for(i=0;i<16;i++) { if((u08bits)(((const s08bits*)&(addr1->s6.sin6_addr))[i]) > (u08bits)(((const s08bits*)&(addr2->s6.sin6_addr))[i])) return 0; } return 1; } else return 1; } } int ioa_addr_in_range(const ioa_addr_range* range, const ioa_addr* addr) { if(range && addr) { if(addr_any(&(range->min)) || addr_less_eq(&(range->min),addr)) { if(addr_any(&(range->max))) { return 1; } else { return addr_less_eq(addr,&(range->max)); } } } return 0; } void ioa_addr_range_cpy(ioa_addr_range* dest, const ioa_addr_range* src) { if(dest && src) { addr_cpy(&(dest->min),&(src->min)); addr_cpy(&(dest->max),&(src->max)); } } /////// Check whether this is a good address ////////////// int ioa_addr_is_multicast(ioa_addr *addr) { if(addr) { if(addr->ss.sa_family == AF_INET) { const u08bits *u = ((const u08bits*)&(addr->s4.sin_addr)); return (u[0] > 223); } else if(addr->ss.sa_family == AF_INET6) { u08bits u = ((const u08bits*)&(addr->s6.sin6_addr))[0]; return (u == 255); } } return 0; } int ioa_addr_is_loopback(ioa_addr *addr) { if(addr) { if(addr->ss.sa_family == AF_INET) { const u08bits *u = ((const u08bits*)&(addr->s4.sin_addr)); return (u[0] == 127); } else if(addr->ss.sa_family == AF_INET6) { const u08bits *u = ((const u08bits*)&(addr->s6.sin6_addr)); if(u[7] == 1) { int i; for(i=0;i<7;++i) { if(u[i]) return 0; } return 1; } } } return 0; } /////// Map "public" address to "private" address ////////////// // Must be called only in a single-threaded context, // before the program starts spawning threads: static ioa_addr **public_addrs = NULL; static ioa_addr **private_addrs = NULL; static size_t mcount = 0; static size_t msz = 0; void ioa_addr_add_mapping(ioa_addr *apub, ioa_addr *apriv) { size_t new_size = msz + sizeof(ioa_addr*); public_addrs = (ioa_addr**)turn_realloc(public_addrs, msz, new_size); private_addrs = (ioa_addr**)turn_realloc(private_addrs, msz, new_size); public_addrs[mcount]=(ioa_addr*)turn_malloc(sizeof(ioa_addr)); private_addrs[mcount]=(ioa_addr*)turn_malloc(sizeof(ioa_addr)); addr_cpy(public_addrs[mcount],apub); addr_cpy(private_addrs[mcount],apriv); ++mcount; msz += sizeof(ioa_addr*); } void map_addr_from_public_to_private(const ioa_addr *public_addr, ioa_addr *private_addr) { size_t i; for(i=0;i> */ #define STUN_MAX_ORIGIN_SIZE (127) #define STUN_ATTRIBUTE_ORIGIN (0x802F) /* <<== Origin */ /* Mobility ==>> */ /* conflicts with third-party authorization ! 0x802E is used for third-party authorization now */ #define STUN_ATTRIBUTE_MOBILITY_TICKET (0x8030) /* <<== Mobility */ /* Bandwidth */ #define STUN_ATTRIBUTE_NEW_BANDWIDTH (0x8000 + STUN_ATTRIBUTE_BANDWIDTH) /* <<== Bandwidth */ ////////////// SSODA /////////////////// #define STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY (0x8032) #define STUN_ATTRIBUTE_ADDRESS_ERROR_CODE (0x8033) #endif //__LIB_TURN_MSG_DEFS_NEW__ turnserver-4.5.1.1/src/client/ns_turn_msg_addr.h0000664000175000017500000000404313436577655020361 0ustar misimisi/* * 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. */ #ifndef __LIB_TURN_MSG_ADDR__ #define __LIB_TURN_MSG_ADDR__ #include "ns_turn_ioaddr.h" #ifdef __cplusplus extern "C" { #endif /////////////////////////////////////////// int stun_addr_encode(const ioa_addr* ca, u08bits *cfield, int *clen, int xor_ed, u32bits mc, const u08bits *tsx_id); int stun_addr_decode(ioa_addr* ca, const u08bits *cfield, int len, int xor_ed, u32bits mc, const u08bits *tsx_id); /////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__LIB_TURN_MSG_ADDR__ turnserver-4.5.1.1/src/ns_turn_defs.h0000664000175000017500000001735013436577655016251 0ustar misimisi/* * 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. */ #ifndef __IOADEFS__ #define __IOADEFS__ #define TURN_SERVER_VERSION "4.5.1.1" #define TURN_SERVER_VERSION_NAME "dan Eider" #define TURN_SOFTWARE "Coturn-" TURN_SERVER_VERSION " '" TURN_SERVER_VERSION_NAME "'" #if (defined(__unix__) || defined(unix)) && !defined(USG) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* NS types: */ #define s08bits char #define s16bits int16_t #define s32bits int32_t #define s64bits int64_t #define u08bits unsigned char #define u16bits uint16_t #define u32bits uint32_t #define u64bits uint64_t #define ns_bcopy(src,dst,sz) bcopy((src),(dst),(sz)) #define ns_bzero(ptr,sz) bzero((ptr),(sz)) #define ns_bcmp(ptr1,ptr2,sz) bcmp((ptr1),(ptr2),(sz)) #define nswap16(s) ntohs(s) #define nswap32(ul) ntohl(ul) #define nswap64(ull) ioa_ntoh64(ull) static inline u64bits _ioa_ntoh64(u64bits v) { #if BYTE_ORDER == LITTLE_ENDIAN u08bits *src = (u08bits*) &v; u08bits* dst = src + 7; while (src < dst) { u08bits vdst = *dst; *(dst--) = *src; *(src++) = vdst; } #elif BYTE_ORDER == BIG_ENDIAN /* OK */ #else #error WRONG BYTE_ORDER SETTING #endif return v; } /* TTL */ #define TTL_IGNORE ((int)(-1)) #define TTL_DEFAULT (64) /* TOS */ #define TOS_IGNORE ((int)(-1)) #define TOS_DEFAULT (0) #define ioa_ntoh64 _ioa_ntoh64 #define ioa_hton64 _ioa_ntoh64 #if defined(TURN_MEMORY_DEBUG) #if defined(TURN_LOG_FUNC) #undef TURN_LOG_FUNC #endif #define TURN_LOG_FUNC(level, ...) printf (__VA_ARGS__) void tm_print_func(void); void *turn_malloc_func(size_t 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); void turn_free_func(void *ptr, size_t sz, const char* function, int line); void turn_free_simple(void *ptr); void *turn_calloc_func(size_t number, size_t size, const char* function, int line); char *turn_strdup_func(const char* s, const char* function, int line); void* debug_ptr_add_func(void *ptr, const char* function, int line); void debug_ptr_del_func(void *ptr, const char* function, int line); #define debug_ptr_add(ptr) debug_ptr_add_func((ptr),__FUNCTION__,__LINE__) #define debug_ptr_del(ptr) debug_ptr_del_func((ptr),__FUNCTION__,__LINE__) #define tm_print() tm_print_func() #define turn_malloc(sz) turn_malloc_func((size_t)(sz),__FUNCTION__,__LINE__) #define turn_free(ptr,sz) turn_free_func((ptr),(size_t)(sz),__FUNCTION__,__LINE__) #define turn_realloc(ptr, old_sz, new_sz) turn_realloc_func((ptr),(size_t)(old_sz),(size_t)(new_sz),__FUNCTION__,__LINE__) #define turn_calloc(number, sz) turn_calloc_func((number),(size_t)(sz),__FUNCTION__,__LINE__) #define turn_strdup(s) turn_strdup_func((s),__FUNCTION__,__LINE__) #define SSL_NEW(ctx) ((SSL*)debug_ptr_add(SSL_new(ctx))) #else #define debug_ptr_add(ptr) #define debug_ptr_del(ptr) #define tm_print() #define turn_malloc(sz) malloc((size_t)(sz)) #define turn_free(ptr,sz) free((ptr)) #define turn_realloc(ptr, old_sz, new_sz) realloc((ptr),(size_t)(new_sz)) #define turn_calloc(number, sz) calloc((number),(size_t)(sz)) #define turn_strdup(s) strdup((s)) #define turn_free_simple free #define SSL_NEW(ctx) SSL_new(ctx) #endif #define SSL_FREE(ssl) do { debug_ptr_del(ssl); SSL_free(ssl); ssl = NULL; } while(0) #define BUFFEREVENT_FREE(be) do { if(be) { debug_ptr_del(be); bufferevent_flush(be,EV_READ|EV_WRITE,BEV_FLUSH); bufferevent_disable(be,EV_READ|EV_WRITE); bufferevent_free(be); be = NULL;} } while(0) #define turn_time() ((turn_time_t)time(NULL)) typedef int vint; typedef vint* vintp; typedef u32bits turn_time_t; #define turn_time_before(t1,t2) ((((s32bits)(t1))-((s32bits)(t2))) < 0) #if !defined(UNUSED_ARG) #define UNUSED_ARG(A) do { A=A; } while(0) #endif #define MAX_STUN_MESSAGE_SIZE (65507) #define STUN_BUFFER_SIZE (MAX_STUN_MESSAGE_SIZE) #define UDP_STUN_BUFFER_SIZE (1024<<4) #define NONCE_LENGTH_32BITS (4) #define DEFAULT_STUN_PORT (3478) #define DEFAULT_STUN_TLS_PORT (5349) #if BYTE_ORDER == LITTLE_ENDIAN #define DEFAULT_STUN_PORT_NBO (0x960D) #elif BYTE_ORDER == BIG_ENDIAN #define DEFAULT_STUN_PORT_NBO (0x0D96) #else #error WRONG BYTE_ORDER SETTING #endif #define STRCPY(dst,src) \ do { if((const char*)(dst) != (const char*)(src)) { \ if(sizeof(dst)==sizeof(char*))\ strcpy(((char*)(dst)),(const char*)(src));\ else {\ size_t szdst = sizeof((dst));\ strncpy((char*)(dst),(const char*)(src),szdst);\ ((char*)(dst))[szdst-1] = 0;\ }\ } } while(0) //////////////// Bufferevents ///////////////////// #define TURN_BUFFEREVENTS_OPTIONS (BEV_OPT_DEFER_CALLBACKS | BEV_OPT_THREADSAFE | BEV_OPT_UNLOCK_CALLBACKS) //////////////// KERNEL-LEVEL CHANNEL HANDLERS ///////// #if !defined(TURN_CHANNEL_HANDLER_KERNEL) #define TURN_CHANNEL_HANDLER_KERNEL void* #endif #if !defined(CREATE_TURN_CHANNEL_KERNEL) #define CREATE_TURN_CHANNEL_KERNEL(channel_number, address_family_client, address_family_peer, protocol_client, client_addr, local_addr, local_relay_addr, peer_addr) ((TURN_CHANNEL_HANDLER_KERNEL)(1)) #endif #if !defined(DELETE_TURN_CHANNEL_KERNEL) #define DELETE_TURN_CHANNEL_KERNEL(handler) #endif //////////////////////////////////////////////////////// #if !defined(IPPROTO_SCTP) #define TURN_NO_SCTP #endif #define CLIENT_DGRAM_SOCKET_TYPE SOCK_DGRAM #define CLIENT_DGRAM_SOCKET_PROTOCOL IPPROTO_IP #define CLIENT_STREAM_SOCKET_TYPE SOCK_STREAM #define CLIENT_STREAM_SOCKET_PROTOCOL IPPROTO_IP #define SCTP_CLIENT_STREAM_SOCKET_TYPE SOCK_STREAM #if !defined(TURN_NO_SCTP) #define SCTP_CLIENT_STREAM_SOCKET_PROTOCOL IPPROTO_SCTP #else #define SCTP_CLIENT_STREAM_SOCKET_PROTOCOL IPPROTO_IP #endif #define RELAY_DGRAM_SOCKET_TYPE SOCK_DGRAM #define RELAY_DGRAM_SOCKET_PROTOCOL IPPROTO_IP #define RELAY_STREAM_SOCKET_TYPE SOCK_STREAM #define RELAY_STREAM_SOCKET_PROTOCOL IPPROTO_IP #define ADMIN_STREAM_SOCKET_TYPE SOCK_STREAM #define ADMIN_STREAM_SOCKET_PROTOCOL IPPROTO_IP //////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif /* __IODEFS__ */ turnserver-4.5.1.1/TODO0000664000175000017500000000521313436577655013303 0ustar misimisi================================================================== ### I. PLATFORMS SUPPORT ### ================================================================== 1) Fedora official package. 2) MS Windows support. Cygwin is supported. A "real" MS-Windows port would involve a usable GUI. ================================================================== ### II. DOCS ### ================================================================== 1) User's manual. 2) Developer's manual. ================================================================== ### III. NETWORK ENGINE ### ================================================================== 1) Kernel module for data channels. ================================================================== ### IV. PERFORMANCE OPTIMIZATION ### ================================================================== 1) A smarter load balancer has to be implemented. The load balancer has to have a heartbeat channels with the slave servers, currently it is only just a dumb round-robin load distributor. ================================================================== ### V. SECURITY ### ================================================================== 1) EC curve new features in OpenSSL 1.0.2 ================================================================== ### VI. STANDARDS SUPPORT ### ================================================================== 1) For extra difficult NAT/FWs, consider implementing Websockets. 2) Redirect draft. 3) STUN-bis: new integrity attribute, algorithms. 4) Third-party authorization updates. ================================================================== ### VII. MISC FEATURES ### ================================================================== 1) Locale support (?). Currently we assume that all text data is 8-bits ASCII encoded, like C locale. It would be nice to support localized strings (both 8-bits and 2-bytes). But I am not sure whether this is really important, given the essentially backend nature of the TURN Server. The TURN server is so deeply "hidden" in the network infrastructure that the significant code complication may be unjustified. 2) Traffic recording (for selected allocations). That would be a helpful feature for a large enterprise (for testing and security purposes). 3) Ganglia monitoring. 4) Key exchange mechanism for oAuth. ================================================================== ### VIII. CODING STUFF ### ================================================================== 1) Peer app for TCP relay. ================================================================== turnserver-4.5.1.1/LICENSE.OpenSSL0000664000175000017500000001420713436577655015105 0ustar misimisi LICENSE ISSUES ============== The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit. See below for the actual license texts. Actually both licenses are BSD-style Open Source licenses. In case of any license issues related to OpenSSL please contact openssl-core@openssl.org. OpenSSL License --------------- /* ==================================================================== * Copyright (c) 1998-2008 The OpenSSL Project. 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. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * openssl-core@openssl.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.openssl.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED 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 OpenSSL PROJECT OR * ITS 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. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). * */ Original SSLeay License ----------------------- /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@cryptsoft.com). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@cryptsoft.com). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * "This product includes cryptographic software written by * Eric Young (eay@cryptsoft.com)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] */ turnserver-4.5.1.1/examples/0000775000175000017500000000000013436577655014430 5ustar misimisiturnserver-4.5.1.1/examples/cpu-mem.sh0000775000175000017500000000036613436577655016337 0ustar misimisi#!/bin/bash while sleep 1; do DATE="$(date)"; PS="$( ps -p `pidof turnserver` -o 'pcpu,pmem,rss,vsz')"; TOP="$( top -p `pidof turnserver` -n1 -b -H -c )" OUTPUT=$DATE"\n\n"$TOP"\n\nps\n"$PS"\n"; echo -e "$OUTPUT" | tee -a cpu-mem.log; done; turnserver-4.5.1.1/examples/var/0000775000175000017500000000000013436577655015220 5ustar misimisiturnserver-4.5.1.1/examples/var/db/0000775000175000017500000000000013436577655015605 5ustar misimisiturnserver-4.5.1.1/examples/var/db/turndb0000664000175000017500000005400013436577655017025 0ustar misimisiSQLite format 3@ ÕÕ-æ öûö  Æ’[éµ>4,D!9Mcrinna.orgstranger-come-knockingd43cb678560259a1839bff61c19de15e5!Mcrinna.orgwhirrun6972e85e51f36e53b0b61759c5a5219a2Mnorth.govgorst7da2270ccfa49786e0115366d3a3d14d8#Mnorth.govninefingersbc807ee29df3c9ffa736523fb2c4e8ee ––¼ÓçŠv %!9crinna.orgstranger-come-knocking!crinna.orgwhirrunnorth.govgorst# north.govninefingers ®íØÄ®!crinna.orglibrary!crinna.orgnorthnorth.govbloody9north.govlogen ««Â×í!crinna.orglibrary!crinna.orgnorthnorth.govbloody9 north.govlogen :µ:Ce oldempireMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIKA256GCM4=unionMTIzNDU2Nzg5MDEyMzQ1Ngo=A128GCMnorth.govIe!northMDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEKA256GCMcrinna.org ·îÓ·§‰‰2.'1.2.3-1.2.3.4'1.2.3.4#1.2.3-4.5.6!'crinna.org172.17.13.202'north.gov172.17.13.201 '172.17.13.200 µîµÒÒÒÒ511.2.3-1.2.3.41.2.3.4-1.2.3-4.5.6!'crinna.org172.17.13.202'north.gov172.17.13.201 ' 172.17.13.200 —áÕ­—…!crinna.org123::77&Anorth.gov172.17.17.133-172.17.19.56  123::45 A172.17.13.133-172.17.14.56 ”Ôᔫ!crinna.org123::77(Anorth.gov172.17.17.133-172.17.19.56  123::45 A 172.17.13.133-172.17.14.56 Öêê\\\\\0%%‚#tableturnusers_ltturnusers_ltCREATE TABLE turnusers_lt ( realm varchar(512) default '', name varchar(512), hmackey char(128), PRIMARY KEY (realm,name) )7K%indexsqlite_autoindex_turnusers_lt_1turnusers_lt†9•7!++ytableallowed_peer_ipallowed_peer_ipCREATE TABLE allowed_peer_ip ( realm varchar(512) default '', ip_range varchar(256), primary key (realm,ip_range) ) HH‡(eL=Q+indexsqlite_autoindex_allowed_peer_ip_1allowed_peer_ip  ))wtabledenied_peer_ipdenied_peer_ip CREATE TABLE denied_peer_ip ( realm varchar(512) default '', ip_range varchar(256), primary key (realm,ip_range) ); O)indexsqlite_autoindex_denied_peer_ip_1denied_peer_ip  55Ytableturn_origin_to_realmturn_origin_to_realmCREATE TABLE turn_origin_to_realm ( origin varchar(512), realm varchar(512), primary key (origin) )G [5indexsqlite_autoindex_turn_origin_to_realm_1turn_origin_to_realm1 //‚tableturn_realm_optionturn_realm_optionCREATE TABLE turn_realm_option ( realm varchar(512) default '', opt varchar(32), value varchar(128), primary key (realm,opt) ) ¹Ý¹Ÿ"7!https://bligh.edu:443crinna.org!5!http://crinna.org:80crinna.org ÎèÎÎ7https://bligh.edu:4435 http://crinna.org:80 «*ˆLj---j!j #north.govtotal-quota12000!!crinna.orguser-quota8000 !north.govuser-quota10000!#crinna.orgtotal-quota10000äU9 ÔlŸlº†††Ô!!crinna.orguser-quota!north.govuser-quota !#crinna.orgtotal-quota#north.govtotal-quota , ß÷ßíí oldempire union north †P߆éÍ##gtableturn_secretturn_secretCREATE TABLE turn_secret ( realm varchar(127) default '', value varchar(127), primary key (realm,value) )5I#indexsqlite_autoindex_turn_secret_1turn_secret !!ctableadmin_useradmin_userCREATE TABLE admin_user ( name varchar(32), realm varchar(127), password varchar(127), primary key (name) )3G!indexsqlite_autoindex_admin_user_1admin_user[AU/indexsqlite_autoindex_turn_realm_option_1turn_realm_optionŽ aƒtableoauth_keyoauth_keyCREATE TABLE oauth_key ( kid varchar(128), ikm_key varchar(256), timestamp bigint default 0, lifetime integer default 0, as_rs_alg varchar(64) default '', realm varchar(127), primary key (kid) )1Eindexsqlite_autoindex_oauth_key_1oauth_key 4”4Õ_^ 5bayaz$5$e018513e9de69e73$5cbdd2e29e04ca46aeb022268a7460d3a3468de193dcb2b95f064901769f455fj5skarlingnorth.gov$5$6fc35c3b0c7d4633$27fca7574f9b79d0cb93ae03e45379470cbbdfcacdd6401f97ebc620f31f54f2 êêôô bayaz  skarlingturnserver-4.5.1.1/examples/run_all_clients.sh0000775000175000017500000000044013436577655020142 0ustar misimisi#!/bin/bash for i in secure_udp_client.sh secure_dtls_client.sh secure_tcp_client_c2c_tcp_relay.sh secure_tls_client_c2c_tcp_relay.sh secure_tls_client.sh secure_udp_client.sh secure_sctp_client.sh secure_tcp_client.sh secure_udp_c2c.sh; do echo $i ./scripts/longtermsecure/$i $@ done turnserver-4.5.1.1/examples/scripts/0000775000175000017500000000000013436577655016117 5ustar misimisiturnserver-4.5.1.1/examples/scripts/readme.txt0000664000175000017500000000217713436577655020124 0ustar misimisiThis directory contains various example scripts for the TURN server functionality illustration. 1) peer.sh starts the "peer" application that serves as a peer for all examples. 2) "basic" directory contains set of scripts which works together to demonstrate very basic anynymous functionality of the TURN server. The "peer.sh" must be used, too. 3) "longtermsecure" directory contains set of scripts demonstrating the long-term authentication mechanism (peer.sh to be used, too). 4) "longtermsecuredb" shows how to start TURN server with database. The clients from the directory "longtermsecure" can be used with the relay scripts in the "longtermsecuredb" directory. Of course, the database (SQLite, PostgreSQL, MySQL, Redis or MongoDB) must be set for these scripts to work correctly. 5) "restapi" shows how to use TURN REST API. 6) "loadbalance" shows how to use the simple load-balancing mechanism based upon the ALTERNATE-SERVER functionality. 7) "selfloadbalance" shows how to use the "self-load-balance" TURN server capabilities. 8) "mobile" shows the "mobile" connections - how the TURN session can change its client address. turnserver-4.5.1.1/examples/scripts/longtermsecure/0000775000175000017500000000000013436577655021155 5ustar misimisiturnserver-4.5.1.1/examples/scripts/longtermsecure/secure_relay.sh0000775000175000017500000000360613436577655024203 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server in # secure mode (when authentication is used) - see option -a # that means "use long-term credential mechanism". # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 10 relay threads (-m 10) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) "--user=ninefingers:youhavetoberealistic" means # "allow user 'ninefinger' with password 'youhavetoberealistic' ". # 7) "--user=gorst:hero" means "allow user 'gorst' with password 'hero' ". # 8) "--cert=turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) "-v" means normal verbose mode (with some moderate logging). # 12) --cipher-list=ALL means that we support all OpenSSL ciphers # 13) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:youhavetoberealistic --user=gorst:hero -r north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -v --cipher-list=ALL --cli-password=secret --db=var/db/turndb $@ turnserver-4.5.1.1/examples/scripts/longtermsecure/secure_tls_client_c2c_tcp_relay.sh0000775000175000017500000000277713436577655030030 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN TLS client # with the long-term credentials mechanism and with # TCP relay endpoints (RFC 6062). # # Options: # # 1) -T is present, it means that TCP networking is used, with TCP # relay endpoints (RFC 6062. # 2) -S means that "secure protocol", that is TLS in the case of TCP, # will be used between the client and the TURN Server. # 3) -i absent. # 4) -k sets private key file for TLS. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -y means that the clients will connect to the 'neighbor' clients, no peer app will be used. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u gorst means that if the server challenges the client with # authentication challenge, then we use account "gorst". # 11) -w hero sets the password for the account as "hero". # 12) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/postgres/9.2-pgdg/lib PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -T -S -k turn_client_pkey.pem -n 1000 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 turnserver-4.5.1.1/examples/scripts/longtermsecure/secure_tls_client_cert.sh0000775000175000017500000000311513436577655026237 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN DTLS client # with the long-term credentials mechanism and with certificate check. # # Options: # # 1) -t means that TCP networking is used. # 2) -S means "SSL protocol with default encryption" # 3) -i sets certificate file for TLS. -R sets certificate check mode. # -E sets CA file for certificate check. # 4) -k sets private key file for TLS. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer IPv4 address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u bolt means that if the server challenges the client with # authentication challenge, then we use account "bolt". # 11) -w kwyjibo sets the password for the account. # 12) -s option means that the client will be using "send" mechanism for data. # 13) 127.0.0.1 (the last parameter) is the TURN Server IP address. # We use IPv6 - to - IPv4 here to illustrate how the TURN Server # converts the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -t -S -i turn_server_cert.pem -k turn_server_pkey.pem -E turn_server_cert.pem -n 1000 -m 10 -l 170 -e 127.0.0.1 -X -g -u bolt -w kwyjibo -s $@ 127.0.0.1 turnserver-4.5.1.1/examples/scripts/longtermsecure/secure_tls_client.sh0000775000175000017500000000262013436577655025222 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN TLS client # with the long-term credentials mechanism. # # Options: # # 1) -t is present, it means that TCP networking is used. # 2) -S means "SSL/TLS protocol with default cipher". # 3) -i absent. # 4) -k sets private key file for TLS. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u gorst means that if the server challenges the client with # authentication challenge, then we use account "gorst". # 11) -w hero sets the password for the account as "hero". # 12) -s option means that the client will be using "send" mechanism for data. # 13) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -t -S -k turn_client_pkey.pem -n 1000 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 turnserver-4.5.1.1/examples/scripts/longtermsecure/secure_dtls_client.sh0000775000175000017500000000276013436577655025373 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN DTLS client # with the long-term credentials mechanism. # # Options: # # 1) -t is absent, it means that UDP networking is used. # 2) -S means "SSL protocol with default encryption" # 3) -i absent. # 4) -k sets private key file for TLS. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer IPv4 address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u ninefingers means that if the server challenges the client with # authentication challenge, then we use account "ninefingers". # 11) -w youhavetoberealistic sets the password for the account. # 12) -s option absent - that means that the client will be using # the channel mechanism for data. # 13) 127.0.0.1 (the last parameter) is the TURN Server IP address. # We use IPv6 - to - IPv4 here to illustrate how the TURN Server # converts the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -S -k turn_client_pkey.pem -n 1000 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic $@ 127.0.0.1 turnserver-4.5.1.1/examples/scripts/longtermsecure/secure_dtls_client_cert.sh0000775000175000017500000000313013436577655026400 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN DTLS client # with the long-term credentials mechanism and with certificate check. # # Options: # # 1) -t is absent, it means that UDP networking is used. # 2) -S means "SSL protocol with default encryption" # 3) -i sets certificate file for TLS. -R sets certificate check mode. # -E sets CA file for certificate check. # 4) -k sets private key file for TLS. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer IPv4 address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u bolt means that if the server challenges the client with # authentication challenge, then we use account "bolt". # 11) -w kwyjibo sets the password for the account. # 12) -s option means that the client will be using "send" mechanism for data. # 13) 127.0.0.1 (the last parameter) is the TURN Server IP address. # We use IPv6 - to - IPv4 here to illustrate how the TURN Server # converts the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -S -i turn_server_cert.pem -k turn_server_pkey.pem -E turn_server_cert.pem -n 1000 -m 10 -l 170 -e 127.0.0.1 -g -u bolt -w kwyjibo -s -X $@ 127.0.0.1 turnserver-4.5.1.1/examples/scripts/longtermsecure/secure_sctp_client.sh0000775000175000017500000000265013436577655025374 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN TLS client # with the long-term credentials mechanism. # # Options: # # 1) -b is present, it means that SCTP networking is used. # 2) -S means "SSL/TLS protocol with default cipher" will be used over SCTP. # 3) -i absent. # 4) -k sets private key file for TLS. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u gorst means that if the server challenges the client with # authentication challenge, then we use account "gorst". # 11) -w hero sets the password for the account as "hero". # 12) -s option means that the client will be using "send" mechanism for data. # 13) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -b -S -k turn_client_pkey.pem -n 1000 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 turnserver-4.5.1.1/examples/scripts/longtermsecure/secure_tcp_client_c2c_tcp_relay.sh0000775000175000017500000000243713436577655030005 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN TCP client # with the long-term credentials mechanism and with # TCP relay endpoints (RFC 6062). # # Options: # # 1) -T is present, it means that TCP networking is used, with TCP relay endpoints (RFC 6062). # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -y means that the clients will connect to the 'neighbor' clients, no peer app will be used. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u gorst means that if the server challenges the client with # authentication challenge, then we use account "gorst". # 11) -w hero sets the password for the account as "hero". # 12) 127.0.0.1 (the last parameter) is the TURN Server IP address. We use IPv4 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -T -n 1000 -m 10 -l 170 -y -g -u gorst -w hero $@ 127.0.0.1 turnserver-4.5.1.1/examples/scripts/longtermsecure/secure_relay_cert.sh0000775000175000017500000000405113436577655025213 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server in # secure mode (when authentication is used) - see option -a # that means "use long-term credential mechanism". # # This script shows how to use certificate check option. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 10 relay threads (-m 10) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r bolt.co" means "use authentication realm 'bolt.co'" # 6) "--user=ninefingers:youhavetoberealistic" means "allow user # 'ninefinger' with password 'youhavetoberealistic'.". # 7) "--user=bolt:kwyjibo" means "allow user 'bolt' with password 'kwyjibo' ". # 8) "--cert=..." sets the OpenSSL certificate file name. # 9) "--pkey=..." sets the OpenSSL private key name. # 10) --CA-file sets the CA file for client certificate check. # 11) "--log-file=stdout" means that all log output will go to the stdout. # 12) "-v" means normal verbose mode (with some moderate logging). # 13) --cipher-list="ALL:!eNULL:!aNULL:!NULL" measn "all ciphers, except anonymous". # 14) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:youhavetoberealistic --user=bolt:kwyjibo -r bolt.co --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --CA-file=turn_server_cert.pem --log-file=stdout -v --cipher-list="ALL:!eNULL:!aNULL:!NULL" --cli-password=secret --db=var/db/turndb $@ turnserver-4.5.1.1/examples/scripts/longtermsecure/secure_udp_client.sh0000775000175000017500000000254013436577655025211 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN UDP client # with the long-term credentials mechanism. # # Options: # # 1) -t is absent, it means that UDP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u ninefingers means that if the server challenges the client with # authentication challenge, then we use account "ninefingers". # 11) -w youhavetoberealistic sets the password for the account as "youhavetoberealistic". # 12) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 13) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -n 1000 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic $@ ::1 turnserver-4.5.1.1/examples/scripts/longtermsecure/secure_tcp_client.sh0000775000175000017500000000244113436577655025207 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN TCP client # with the long-term credentials mechanism. # # Options: # # 1) -t is present, it means that TCP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u gorst means that if the server challenges the client with # authentication challenge, then we use account "gorst". # 11) -w hero sets the password for the account as "hero". # 12) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 13) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -t -n 3000 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 turnserver-4.5.1.1/examples/scripts/longtermsecure/secure_dos_attack.sh0000775000175000017500000000314513436577655025201 0ustar misimisi#!/bin/sh # # This is an example of a script to run a DOS attack in a # "secure" environment # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ while [ 0 ] ; do PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -O -n 10 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -O -n 10 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -O -S -k turn_client_pkey.pem -n 10 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -O -t -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -O -T -n 10 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -O -T -S -k turn_client_pkey.pem -n 10 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -O -t -S -k turn_client_pkey.pem -n 10 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 & sleep 2 type killall >>/dev/null 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then killall turnutils_uclient >>/dev/null 2>>/dev/null fi type pkill >>/dev/null 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then pkill turnutils_u >>/dev/null 2>>/dev/null pkill turnutils_uclie >>/dev/null 2>>/dev/null pkill turnutils_uclient >>/dev/null 2>>/dev/null else sleep 10 fi done turnserver-4.5.1.1/examples/scripts/longtermsecure/secure_udp_c2c.sh0000775000175000017500000000262213436577655024403 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN UDP client # with the long-term credentials mechanism, # in client-to-client communication patter. # # Options: # # 1) -t is absent, it means that UDP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -y means that the clients will be connecting to each other and the peer will not be used. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u ninefingers means that if the server challenges the client with # authentication challenge, then we use account "ninefingers". # 11) -w youhavetoberealistic sets the password for the account as "youhavetoberealistic". # 12) -s option is present - it means that the client will be using # the DATA mechanism for data. # 13) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -s -n 1000 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y $@ ::1 turnserver-4.5.1.1/examples/scripts/restapi/0000775000175000017500000000000013436577655017566 5ustar misimisiturnserver-4.5.1.1/examples/scripts/restapi/shared_secret_maintainer.pl0000775000175000017500000000416013436577655025151 0ustar misimisi#!/usr/bin/perl # # This is an example of Perl script maintaining dynamic shared secret # database for the REST API # use strict; use warnings; use DBI; use HTTP::Request::Common; my $DBNAME="turn"; my $DBUSERNAME="turn"; my $DBPWD="turn"; my $DBHOST="localhost"; my $webserver = 'http://example.com/'; my $old_secret = ""; my $current_secret=""; my $INTERVAL=3600; my $dbh; $dbh = DBI->connect("DBI:mysql:$DBNAME;host=$DBHOST", $DBUSERNAME, $DBPWD) || die "Could not connect to database: $DBI::errstr"; $dbh->do('CREATE TABLE IF NOT EXISTS turn_secret (value varchar(512))'); my $c = $dbh->do("delete from turn_secret"); print "Deleted $c rows\n"; $dbh->disconnect(); do { $dbh = DBI->connect("DBI:mysql:$DBNAME;host=$DBHOST", $DBUSERNAME, $DBPWD) || die "Could not connect to database: $DBI::errstr"; $dbh->do('CREATE TABLE IF NOT EXISTS turn_secret (value varchar(512))'); if(length($current_secret)) { if(length($old_secret)) { remove_secret($dbh, $old_secret); } $old_secret=$current_secret; } print "CURRENT SECRET TO BE (RE)GENERATED\n"; $current_secret = generate_secret(); insert_secret($dbh, $current_secret); $dbh->disconnect(); # # Web server interaction example: # Here we can put code to submit this secret to the web server: # my $req = POST($webserver, Content => [param => $current_secret]); $req->method('PUT'); print $req->as_string,"\n"; # # Alternatively, you can use this link for compute-on-demand: # https://github.com/alfreddatakillen/computeengineondemand # # write your code here. # sleep($INTERVAL); } while(1); sub remove_secret { my $dbh = shift; my $secret=shift; my $c = $dbh->do("delete from turn_secret where value = '$secret'"); print "Deleted $c rows\n"; } sub insert_secret { my $dbh = shift; my $secret=shift; my $c = $dbh->do("insert into turn_secret values('$secret')"); print "Inserted $c rows\n"; } sub generate_secret { my @chars = ('0'..'9', 'A'..'F'); my $len = 8; my $string; while($len--){ $string .= $chars[rand @chars] }; return $string; } turnserver-4.5.1.1/examples/scripts/restapi/secure_relay_secret_with_db_mongo.sh0000775000175000017500000000373013436577655027056 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server in # secure 'dynamic' 'secret' mode (see TURNServerRESTAPI.pdf) # with MongoDB database for users information # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) --use-auth-secret means that we are using 'secret' authentication mode. # Absense of --static-auth-secret value means that we will be taking the secret value # from the database ('dynamic' mode). # 6) --realm=north.gov sets realm value as "north.gov". # 7) --mongo-userdb="mongodb://localhost/coturn" # means that local MongoDB database "coturn" will be used. # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) --cipher-list=ALL means that we support all OpenSSL ciphers # 12) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --use-auth-secret --realm=north.gov --mongo-userdb="mongodb://localhost/coturn" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/restapi/secure_relay_secret_with_db_mysql.sh0000775000175000017500000000422613436577655027105 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server in # secure 'dynamic' 'secret' mode (see TURNServerRESTAPI.pdf) # with MySQL database for users information # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) --use-auth-secret means that we are using 'secret' authentication mode. # Absense of --static-auth-secret value means that we will be taking the secret value # from the database ('dynamic' mode). # 6) --realm=north.gov sets realm value as "north.gov". # 7) --mysql-userdb="host=localhost dbname=coturn user=turn password=turn connect_timeout=30" # means that local MySQL database "coturn" will be used, with database user "turn" and # with database user password "turn", and connection timeout 30 seconds. # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) --cipher-list=ALL means that we support all OpenSSL ciphers # 12) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --use-auth-secret --realm=north.gov --mysql-userdb="host=localhost dbname=coturn user=turn password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/restapi/secure_relay_secret.sh0000775000175000017500000000364513436577655024164 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server in # secure 'static' 'secret' mode (see TURNServerRESTAPI.pdf) # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) --use-auth-secret means that we are using 'secret' authentication mode. # 6) --static-auth-secret=logen means that we will be using 'static' secret value. # 7) --realm=north.gov sets realm value as "north.gov". # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) "-q 100" means that single user can create no more than 100 sessions # 12) "-Q 300" means that there may be no more than 300 sessions totally # 13) --cipher-list=ALL means that we support all OpenSSL ciphers # 14) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --use-auth-secret --static-auth-secret=logen --realm=north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -q 100 -Q 300 --cipher-list=ALL --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/restapi/secure_relay_secret_with_db_redis.sh0000775000175000017500000000422413436577655027044 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server in # secure 'dynamic' 'secret' mode (see TURNServerRESTAPI.pdf) # with Redis database for users information # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) --use-auth-secret means that we are using 'secret' authentication mode. # Absense of --static-auth-secret value means that we will be taking the secret value # from the database ('dynamic' mode). # 6) --realm=north.gov sets realm value as "north.gov". # 7) --redis-userdb="ip=127.0.0.1 dbname=2 password=turn connect_timeout=30" # means that local Redis database 0 will be used, with database # password "turn", and connection timeout 30 seconds. # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) --cipher-list=ALL means that we support all OpenSSL ciphers # 12) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --use-auth-secret --realm=north.gov --redis-userdb="ip=127.0.0.1 dbname=2 password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --redis-statsdb="ip=127.0.0.1 dbname=3 password=turn connect_timeout=30" --cipher-list=ALL --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/restapi/secure_relay_secret_with_db_sqlite.sh0000775000175000017500000000364713436577655027247 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server in # secure 'dynamic' 'secret' mode (see TURNServerRESTAPI.pdf) # with SQLite database for users information # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) --use-auth-secret means that we are using 'secret' authentication mode. # Absense of --static-auth-secret value means that we will be taking the secret value # from the database ('dynamic' mode). # 6) --realm=north.gov sets realm value as "north.gov". # 7) --db= # means that local SQLite database will be used. # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) --cipher-list=ALL means that we support all OpenSSL ciphers # 12) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --use-auth-secret --realm=north.gov --db="var/db/turndb" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/restapi/secure_udp_client_with_secret.sh0000775000175000017500000000246713436577655026232 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN UDP client # with the long-term credentials mechanism and with # secret-based authorization (see TURNServerRESTAPI.pdf document). # # Options: # # 1) -t is absent, it means that UDP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u ninefingers means that if the server challenges the client with # authentication challenge, then we use account "ninefingers". # 11) -W logen sets the secret for the secret-based authentication as "logen". # 12) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 13) ::1 (the last parameter) is the TURN Server IPv6 address. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -z 5 -n 10000 -s -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -W logen $@ ::1 turnserver-4.5.1.1/examples/scripts/restapi/secure_relay_secret_with_db_psql.sh0000775000175000017500000000423513436577655026717 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server in # secure 'dynamic' 'secret' mode (see TURNServerRESTAPI.pdf) # with PostgreSQL database for users information # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) --use-auth-secret means that we are using 'secret' authentication mode. # Absense of --static-auth-secret value means that we will be taking the secret value # from the database ('dynamic' mode). # 6)--realm=north.gov sets realm value as "north.gov". # 7) --psql-userdb="host=localhost dbname=coturn user=turn password=turn connect_timeout=30" # means that local PostgreSQL database "coturn" will be used, with database user "turn" and # with database user password "turn", and connection timeout 30 seconds. # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) --cipher-list=ALL means that we support all OpenSSL ciphers # 12) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --use-auth-secret --realm=north.gov --psql-userdb="host=localhost dbname=coturn user=turn password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/peer.sh0000775000175000017500000000071513436577655017414 0ustar misimisi#!/bin/sh # # This is a script for the peer application, # for testing only purposes. It opens UDP echo-like sockets # on IPv4 address 127.0.0.1 and IPv6 address ::1. # The default port 3480 is used. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:bin/:../bin:${PATH} turnutils_peer -L 127.0.0.1 -L ::1 -L 0.0.0.0 $@ turnserver-4.5.1.1/examples/scripts/rfc5769.sh0000775000175000017500000000055113436577655017564 0ustar misimisi#!/bin/sh # # This is a script for RFC 5769 STUN protocol check. # It checks whether the main code was compiled correctly. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:bin/:../bin:${PATH} turnutils_rfc5769check $@ turnserver-4.5.1.1/examples/scripts/loadbalance/0000775000175000017500000000000013436577655020344 5ustar misimisiturnserver-4.5.1.1/examples/scripts/loadbalance/tcp_c2c_tcp_relay.sh0000775000175000017500000000227113436577655024264 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN TCP client # with the long-term credentials mechanism and with # TCP relay endpoints (RFC 6062). # # Options: # # 1) -T is present, it means that TCP networking is used, with TCP relay endpoints (RFC 6062). # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -y means that the clients will connect to the 'neighbor' clients, no peer app will be used. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u gorst means that if the server challenges the client with # authentication challenge, then we use account "gorst". # 11) -w hero sets the password for the account as "hero". # 12) 127.0.0.1 (the last parameter) is the TURN Server IP address. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -T -n 1000 -m 10 -l 170 -y -g -u gorst -w hero $@ 127.0.0.1 turnserver-4.5.1.1/examples/scripts/loadbalance/udp_c2c.sh0000775000175000017500000000247213436577655022227 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN UDP client # with the long-term credentials mechanism, # in client-to-client communication patter. # # Options: # # 1) -t is absent, it means that UDP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -y means that the clients will be connecting to each other and the peer will not be used. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u ninefingers means that if the server challenges the client with # authentication challenge, then we use account "ninefingers". # 11) -w youhavetoberealistic sets the password for the account as "youhavetoberealistic". # 12) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 13) 127.0.0.1 (the last parameter) is the TURN Server IP address. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -n 1000 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y $@ 127.0.0.1 turnserver-4.5.1.1/examples/scripts/loadbalance/master_relay.sh0000775000175000017500000000363113436577655023375 0ustar misimisi#!/bin/sh # # This is an example of a MASTER TURN server that distributes # the load among several "slave" TURN servers. # # The TURN Server is started in # secure mode (when authentication is used) - see option -a # that means "use long-term credential mechanism". # # We start here a TURN Server listening on IPv4 address # 127.0.0.1. We use 127.0.0.1 as the relay address, too. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) "--user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee" means # "allow user 'ninefinger' with generated key '0xbc807ee29df3c9ffa736523fb2c4e8ee' ". # 7) "--user=gorst:hero" means "allow user 'gorst' with password 'hero' ". # 8) "--log-file=stdout" means that all log output will go to the stdout. # 9) "-v" means normal verbose mode (with some moderate logging). # 10) --no-dtls and --no-tls measn that we are not using DTLS & TLS protocols here # (for the sake of simplicity). # 11) --alternate-server options set the "slave" servers. # 12) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --syslog -a -L 127.0.0.1 -E 127.0.0.1 --allow-loopback-peers --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee --user=gorst:hero -r north.gov --log-file=stdout -v --no-dtls --no-tls --alternate-server=127.0.0.1:3333 --alternate-server=127.0.0.1:4444 --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/loadbalance/slave_relay_1.sh0000775000175000017500000000353613436577655023440 0ustar misimisi#!/bin/sh # # This is an example of a SLAVE TURN server that accepts # the redirected requests. # # The TURN Server is started in # secure mode (when authentication is used) - see option -a # that means "use long-term credential mechanism". # # We start here a TURN Server listening on IPv4 address # 127.0.0.1. We use 127.0.0.1 as the relay address, too. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 10000 and max UDP relay port 19999 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) "--user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee" means # "allow user 'ninefinger' with generated key '0xbc807ee29df3c9ffa736523fb2c4e8ee' ". # 7) "--user=gorst:hero" means "allow user 'gorst' with password 'hero' ". # 8) "--log-file=stdout" means that all log output will go to the stdout. # 9) "-v" means normal verbose mode (with some moderate logging). # 10) --no-dtls and --no-tls measn that we are not using DTLS & TLS protocols here # (for the sake of simplicity). # 11) -p 3333 means that we are using UDP & TCP listening port 3333. # 12) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --syslog -a -L 127.0.0.1 -E 127.0.0.1 --allow-loopback-peers --max-bps=3000000 -f -m 3 --min-port=10000 --max-port=19999 --user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee --user=gorst:hero -r north.gov --log-file=stdout -v --no-dtls --no-tls -p 3333 --cli-port=5767 --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/loadbalance/slave_relay_2.sh0000775000175000017500000000353613436577655023441 0ustar misimisi#!/bin/sh # # This is an example of a SLAVE TURN server that accepts # the redirected requests. # # The TURN Server is started in # secure mode (when authentication is used) - see option -a # that means "use long-term credential mechanism". # # We start here a TURN Server listening on IPv4 address # 127.0.0.1. We use 127.0.0.1 as the relay address, too. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 20000 and max UDP relay port 29999 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) "--user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee" means # "allow user 'ninefinger' with generated key '0xbc807ee29df3c9ffa736523fb2c4e8ee' ". # 7) "--user=gorst:hero" means "allow user 'gorst' with password 'hero' ". # 8) "--log-file=stdout" means that all log output will go to the stdout. # 9) "-v" means normal verbose mode (with some moderate logging). # 10) --no-dtls and --no-tls measn that we are not using DTLS & TLS protocols here # (for the sake of simplicity). # 11) -p 4444 means that we are using UDP & TCP listening port 4444. # 12) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --syslog -a -L 127.0.0.1 -E 127.0.0.1 --allow-loopback-peers --max-bps=3000000 -f -m 3 --min-port=20000 --max-port=29999 --user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee --user=gorst:hero -r north.gov --log-file=stdout -v --no-dtls --no-tls -p 4444 --cli-port=5768 --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/oauth.sh0000775000175000017500000000277113436577655017605 0ustar misimisi#!/bin/bash OAUTH_UTILITY=bin/turnutils_oauth echo "--------------create an access_token---------------" $OAUTH_UTILITY -e --server-name example.com --auth-key-id 1234 --auth-key SEdrajMyS0pHaXV5MDk4c2RmYXFiTmpPaWF6NzE5MjM= --auth-key-timestamp 249213600 --auth-key-lifetime 21600 --token-mac-key WmtzanB3ZW9peFhtdm42NzUzNG0= --token-timestamp 16333642137600 --token-lifetime=3600 echo "---------------create and validate and print out the decoded access_token---------------" $OAUTH_UTILITY -v -d -e --server-name example.com --auth-key-id 1234 --auth-key SEdrajMyS0pHaXV5MDk4c2RmYXFiTmpPaWF6NzE5MjM= --auth-key-timestamp 249213600 --auth-key-lifetime 21600 --token-mac-key WmtzanB3ZW9peFhtdm42NzUzNG0= --token-timestamp 16333642137600 --token-lifetime=3600 echo -e "\n---------------just validate only the access_token---------------" $OAUTH_UTILITY -d --server-name example.com --auth-key-id 1234 --auth-key SEdrajMyS0pHaXV5MDk4c2RmYXFiTmpPaWF6NzE5MjM= --auth-key-timestamp 249213600 --auth-key-lifetime 21600 --token AAy1JBYVLo16iq9gFdHyyknmx5T/Lq9YlbxgUdLcStOFS0H8xhHceHOL2f49qxp4uBpGuuLeLqk+RcAa5uP2EQ== --token-lifetime=3600 echo -e "\n---------------validate and print out the decoded access_token---------------" $OAUTH_UTILITY -v -d --server-name example.com --auth-key-id 1234 --auth-key SEdrajMyS0pHaXV5MDk4c2RmYXFiTmpPaWF6NzE5MjM= --auth-key-timestamp 249213600 --auth-key-lifetime 21600 --token AAy1JBYVLo16iq9gFdHyyknmx5T/Lq9YlbxgUdLcStOFS0H8xhHceHOL2f49qxp4uBpGuuLeLqk+RcAa5uP2EQ== --token-lifetime=3600 turnserver-4.5.1.1/examples/scripts/basic/0000775000175000017500000000000013436577655017200 5ustar misimisiturnserver-4.5.1.1/examples/scripts/basic/dos_attack.sh0000775000175000017500000000155613436577655021662 0ustar misimisi#!/bin/sh # # This is an example of a script for DOS attack emulation if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ while [ 0 ] ; do PATH=examples/bin/:../bin/:bin/:${PATH} turnutils_uclient -O -D -G -n 1 -m 12 -e 127.0.0.1 -X -g $@ ::1 & PATH=examples/bin/:../bin/:bin/:${PATH} turnutils_uclient -O -G -n 1 -m 12 -y -s $@ 127.0.0.1 & PATH=examples/bin/:../bin:bin/:${PATH} turnutils_uclient -O -G -t -n 1 -m 12 -e 127.0.0.1 -X -g $@ ::1 & PATH=examples/bin/:../bin:bin/:${PATH} turnutils_uclient -O -G -T -n 1 -m 12 -y -s $@ 127.0.0.1 & sleep 1 type killall >>/dev/null 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then killall turnutils_uclient >>/dev/null 2>>/dev/null else type pkill >>/dev/null 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then pkill turnutils_u >>/dev/null 2>>/dev/null fi fi done turnserver-4.5.1.1/examples/scripts/basic/relay.sh0000775000175000017500000000235113436577655020654 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server in # non-secure mode (when authentication is not used). # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # Other options: # set bandwidth limit on client session 3000000 bytes per second (--max-bps) # use fingerprints (-f) # use 3 relay threads (-m 3) # use min UDP relay port 32355 and max UDP relay port 65535 # --no-tls and --no-dtls mean that we are not trying to # --no-auth means that no authentication to be used, # allow anonymous users. # start TLS and DTLS services. # --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="bin:../bin:../../bin:${PATH}" turnserver -v --syslog -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --cli-password secred --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --no-tls --no-dtls --no-auth --db="var/db/turndb" $@ turnserver-4.5.1.1/examples/scripts/basic/tcp_client.sh0000775000175000017500000000213713436577655021666 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "unsecure" TURN TCP client. # Options: # 1) -t is present, it means that TCP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 11) -X means that IPv4 relay address is requested. # 12) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin:bin/:${PATH} turnutils_uclient -t -n 1000 -m 10 -l 3037 -e 127.0.0.1 -g -X $@ ::1 turnserver-4.5.1.1/examples/scripts/basic/udp_c2c_client.sh0000775000175000017500000000230413436577655022413 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "unsecure" TURN UDP client, # in client-to-client fashion (when client talks to another client # through their corresponding allocated relayed endpoints). # Options: # 1) -t is absent, it means that UDP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -y means "client to client" communication pattern. # the client calculates the peer address # (which is the allocated relayed endpoint of the next client in array of clients). # 8) -l 170 means that the payload size of the packets is 170 bytes # like average audio RTP packet). # 9) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 10) 127.0.0.1 (the last parameter) is the TURN Server IP address. # 11) -z 5 means that we want 5 ms interval between the packets (per each session). # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:bin/:${PATH} turnutils_uclient -n 1000 -m 10 -y -l 170 -z 15 $@ 127.0.0.1 turnserver-4.5.1.1/examples/scripts/basic/tcp_client_c2c_tcp_relay.sh0000775000175000017500000000201213436577655024447 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "unsecure" TURN TCP client # with TCP relay endpoints (RFC 6062). # Options: # 1) -T is present, it means that TCP networking is used with TCP relay endpoints. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -y means that the clients will connect to the 'neighbor' clients, no peer app will be used. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin:bin/:${PATH} turnutils_uclient -T -n 1000 -m 10 -l 170 -y -g $@ ::1 turnserver-4.5.1.1/examples/scripts/basic/udp_client.sh0000775000175000017500000000223713436577655021671 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "unsecure" TURN UDP client. # Options: # 0) -D means "mandatory padding", like pjnath does; # 1) -t is absent, it means that UDP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 171 means that the payload size of the packets is 171 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 11) -X means that IPv4 relay address is requested. # 12) 127.0.0.1 (the last parameter) is the TURN Server IP address. We use IPv4 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:bin/:${PATH} turnutils_uclient -D -n 1000 -m 10 -l 171 -e 127.0.0.1 -g -X $@ 127.0.0.1 turnserver-4.5.1.1/examples/scripts/mobile/0000775000175000017500000000000013436577655017366 5ustar misimisiturnserver-4.5.1.1/examples/scripts/mobile/mobile_tls_client_c2c_tcp_relay.sh0000775000175000017500000000312013436577655026201 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN TLS client # with "mobile" option and with the long-term credentials mechanism and with # TCP relay endpoints (RFC 6062). # # Options: # # 1) -T is present, it means that TCP networking is used, with TCP # relay endpoints (RFC 6062. # 2) -S means that "secure protocol", that is TLS in the case of TCP, # will be used between the client and the TURN Server. # 3) -i absent. # 4) -k sets private key file for TLS. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -y means that the clients will connect to the 'neighbor' clients, no peer app will be used. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u gorst means that if the server challenges the client with # authentication challenge, then we use account "gorst". # 11) -w hero sets the password for the account as "hero". # 12) -M turns on the Mobile ICE TURN functionality. # 13) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/postgres/9.2-pgdg/lib PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -T -S -k turn_client_pkey.pem -n 1000 -m 10 -l 170 -y -g -u gorst -w hero -M $@ ::1 turnserver-4.5.1.1/examples/scripts/mobile/mobile_udp_client.sh0000775000175000017500000000266113436577655023407 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN UDP client # with "mobile" option and with the long-term credentials mechanism. # # Options: # # 1) -t is absent, it means that UDP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u ninefingers means that if the server challenges the client with # authentication challenge, then we use account "ninefingers". # 11) -w youhavetoberealistic sets the password for the account as "youhavetoberealistic". # 12) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 13) -M turns on the Mobile ICE TURN functionality. # 14) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -n 1000 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic -M $@ ::1 turnserver-4.5.1.1/examples/scripts/mobile/mobile_dtls_client.sh0000775000175000017500000000304513436577655023562 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" TURN DTLS client # with "mobile" option and the long-term credentials mechanism. # # Options: # # 1) -t is absent, it means that UDP networking is used. # 2) -S means "SSL protocol with default encryption" # 3) -i absent. # 4) -k sets private key file for TLS. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer IPv4 address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u ninefingers means that if the server challenges the client with # authentication challenge, then we use account "ninefingers". # 11) -w youhavetoberealistic sets the password for the account. # 12) -s option means that the client will be using "send" mechanism for data. # 13) -M turns on the Mobile ICE TURN functionality. # 14) 127.0.0.1 (the last parameter) is the TURN Server IP address. # We use IPv6 - to - IPv4 here to illustrate how the TURN Server # converts the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -S -k turn_client_pkey.pem -n 1000 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic -s -M $@ 127.0.0.1 turnserver-4.5.1.1/examples/scripts/mobile/mobile_relay.sh0000775000175000017500000000376513436577655022403 0ustar misimisi#!/bin/sh # # This is an example how to start a "mobile" TURN Server in # secure mode (when authentication is used) - see option -a # that means "use long-term credential mechanism". # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 10 relay threads (-m 10) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) "--user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee" means # "allow user 'ninefinger' with generated key '0xbc807ee29df3c9ffa736523fb2c4e8ee' ". # 7) "--user=gorst:hero" means "allow user 'gorst' with password 'hero' ". # 8) "--cert=turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) "-v" means normal verbose mode (with some moderate logging). # 12) "--mobility" turns on the Mobile ICE TURN functionality. # 13) --cipher-list=ALL means that we support all OpenSSL ciphers # 14) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee --user=gorst:hero -r north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -v --mobility --cipher-list=ALL --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/mobile/mobile_tcp_client.sh0000775000175000017500000000254313436577655023404 0ustar misimisi#!/bin/sh # # This is an example of a script to run a "secure" "mobile" # TURN TCP client with the long-term credentials mechanism. # # Options: # # 1) -t is present, it means that TCP networking is used. # 5) -n 1000 means 1000 messages per single emulated client. Messages # are sent with interval of 20 milliseconds, to emulate an RTP stream. # 6) -m 10 means that 10 clients are emulated. # 7) -l 170 means that the payload size of the packets is 170 bytes # (like average audio RTP packet). # 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1. # 9) -g means "set DONT_FRAGMENT parameter in TURN requests". # 10) -u gorst means that if the server challenges the client with # authentication challenge, then we use account "gorst". # 11) -w hero sets the password for the account as "hero". # 12) -s option is absent - it means that the client will be using # the "channel" mechanism for data. # 13) -M turns on the Mobile ICE TURN functionality. # 14) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here # to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back. # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -t -n 3000 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -M $@ ::1 turnserver-4.5.1.1/examples/scripts/selfloadbalance/0000775000175000017500000000000013436577655021216 5ustar misimisiturnserver-4.5.1.1/examples/scripts/selfloadbalance/secure_relay.sh0000775000175000017500000000461413436577655024244 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server # with self-udp-balancing, in secure mode # (when authentication is used) - see option -a # that means "use long-term credential mechanism". # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) --aux-server=... options start two auxiliary severs on IP address 127.0.0.1 # and ports 12345 and 12346, and two auxiliary servers on IP adress ::1 # with the same ports. # 2) --self-udp-balance option forces the server to distribute the load from the # main server points to the auxiliary servers through the ALTERNATE-SERVER # mechanism. # 3) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 4) use fingerprints (-f) # 5) use 10 relay threads (-m 10) # 6) use min UDP relay port 32355 and max UDP relay port 65535 # 7) "-r north.gov" means "use authentication realm north.gov" # 8) "--user=ninefingers:youhavetoberealistic" means # "allow user 'ninefinger' with password 'youhavetoberealistic' ". # 9) "--user=gorst:hero" means "allow user 'gorst' with password 'hero' ". # 10) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 11) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 12) "--log-file=stdout" means that all log output will go to the stdout. # 13) "-v" means normal verbose mode (with some moderate logging). # 14) --cipher-list=ALL means that we support all OpenSSL ciphers # 15) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --aux-server=127.0.0.1:12345 --aux-server=[::1]:12345 --aux-server=127.0.0.1:12346 --aux-server=[::1]:12346 --udp-self-balance --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:youhavetoberealistic --user=gorst:hero -r north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL --db=var/db/turndb --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/selfloadbalance/secure_dos_attack.sh0000775000175000017500000002503613436577655025245 0ustar misimisi#!/bin/sh # # This is an example of a script to run a DOS attack # in a "secure" environment on a server with # self-load-balancing option # if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ export SLEEP_TIME=11 while [ 0 ] ; do rm -rf /var/log/turnserver/* ########################## PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -n 30 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -S -k turn_client_pkey.pem -n 10 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -t -n 50 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -T -n 30 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -T -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -t -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -n 30 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y -p 12345 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic -p 12345 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s -p 12345 $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -t -n 50 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -T -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -T -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -t -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -n 30 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y -p 12346 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic -p 12346 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s -p 12346 $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -t -n 50 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12346 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -G -T -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12346 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -T -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12346 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -G -t -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12346 $@ ::1 & ########################### PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -B -N -R -G -n 30 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -B -N -R -G -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -B -N -R -G -S -k turn_client_pkey.pem -n 10 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -B -N -R -G -t -n 50 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -B -N -R -G -T -n 30 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -B -N -R -G -T -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -B -N -R -G -t -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -B -N -R -G -n 30 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y -p 12345 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -B -N -R -G -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic -p 12345 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -B -N -R -G -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s -p 12345 $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -B -N -R -G -t -n 50 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -B -N -R -G -T -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -B -N -R -G -T -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -B -N -R -G -t -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -B -N -R -G -n 30 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y -p 12346 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -B -N -R -G -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic -p 12346 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -B -N -R -G -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s -p 12346 $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -B -N -R -G -t -n 50 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12346 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -B -N -R -G -T -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12346 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -B -N -R -G -T -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12346 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -B -N -R -G -t -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12346 $@ ::1 & ########################### PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -n 30 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -S -k turn_client_pkey.pem -n 10 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -t -n 50 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -T -n 30 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -T -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -y -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -t -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -n 30 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y -p 12345 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic -p 12345 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s -p 12345 $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -t -n 50 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -T -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -T -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -t -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12345 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -n 30 -m 10 -l 170 -g -u ninefingers -w youhavetoberealistic -y -p 12346 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u ninefingers -w youhavetoberealistic -p 12346 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e ::1 -x -g -u ninefingers -w youhavetoberealistic -s -p 12346 $@ 127.0.0.1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -t -n 50 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12346 $@ ::1 & PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -O -N -R -G -T -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12346 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -T -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -y -g -u gorst -w hero -p 12346 $@ ::1 & PATH=examples/bin/:../bin:./bin/:${PATH} turnutils_uclient -O -N -R -G -t -S -k turn_client_pkey.pem -n 30 -m 10 -l 170 -e 127.0.0.1 -X -g -u gorst -w hero -p 12346 $@ ::1 & ######################### sleep ${SLEEP_TIME} type killall >>/dev/null 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then killall turnutils_uclient >>/dev/null 2>>/dev/null fi type pkill >>/dev/null 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then pkill turnutils_u >>/dev/null 2>>/dev/null pkill turnutils_uclie >>/dev/null 2>>/dev/null pkill turnutils_uclient >>/dev/null 2>>/dev/null else sleep 10 fi done turnserver-4.5.1.1/examples/scripts/longtermsecuredb/0000775000175000017500000000000013436577655021463 5ustar misimisiturnserver-4.5.1.1/examples/scripts/longtermsecuredb/secure_relay_with_db_mysql.sh0000775000175000017500000000376613436577655027445 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server in # secure mode with MySQL database for users # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) --mysql-userdb="host=localhost dbname=coturn user=turn password=turn connect_timeout=30 read_timeout=30" # means that local MySQL database "coturn" will be used, with database user "turn" and # database user password "turn", and connection timeout 30 seconds. # 7) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 8) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 9) "--log-file=stdout" means that all log output will go to the stdout. # 10) --cipher-list=ALL means that we support all OpenSSL ciphers # 11) --oauth - support oAuth security dialog # 12) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --server-name="blackdow.carleon.gov" -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 -r north.gov --mysql-userdb="host=localhost dbname=coturn user=turn password=turn connect_timeout=30 read_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL --oauth --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/longtermsecuredb/secure_relay_with_db_mongo.sh0000775000175000017500000000331213436577655027402 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server in # secure mode with MongoDB database for users # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) --mongo-userdb="mongodb://localhost/coturn" # means that local MongoDB database "turn" will be used. # 7) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 8) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 9) "--log-file=stdout" means that all log output will go to the stdout. # 10) --cipher-list=ALL means that we support all OpenSSL ciphers # 11) --oauth - support oAuth security dialog # 12) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 -r north.gov --mongo-userdb="mongodb://localhost/coturn" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL --oauth --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/longtermsecuredb/secure_relay_with_db_psql.sh0000775000175000017500000000457413436577655027255 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server in # secure mode with Postgres database for users # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) --psql-userdb="host=localhost dbname=coturn user=turn password=turn connect_timeout=30" # means that local database "coturn" will be used, with database user "turn" and database user # password "turn". # 7) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 8) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 9) "--log-file=stdout" means that all log output will go to the stdout. # 10) --cipher-list=ALL means that we support all OpenSSL ciphers # 11) --oauth - support oAuth security dialog # 12) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --server-name="blackdow.carleon.gov" -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 -r north.gov --psql-userdb="host=localhost dbname=coturn user=turn password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL --oauth --cli-password=secret $@ # Newer PostgreSQL style connection string example: # PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --server-name="blackdow.carleon.gov" -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 -r north.gov --psql-userdb=postgresql://turn:turn@/turn --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL --oauth --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/longtermsecuredb/secure_relay_with_db_redis.sh0000775000175000017500000000422613436577655027376 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server in # secure mode with Redis database for users # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) --redis-userdb="ip=127.0.0.1 dbname=2 password=turn connect_timeout=30" # means that local Redis database 0 will be used, # database password is "turn", and connection timeout 30 seconds. # 7) --redis-statsdb="ip=127.0.0.1 dbname=3 password=turn connect_timeout=30" # means that we want to use Redis for status and statistics information, # and this will be the database number 3. # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 10) "--log-file=stdout" means that all log output will go to the stdout. # 11) --cipher-list=ALL means that we support all OpenSSL ciphers # 12) --oauth - support oAuth security dialog # 13) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --server-name="blackdow.carleon.gov" --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 ---allow-loopback-peers -max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 -r north.gov --redis-userdb="ip=127.0.0.1 dbname=2 password=turn connect_timeout=30" --redis-statsdb="ip=127.0.0.1 dbname=3 password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL --oauth --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/longtermsecuredb/secure_relay_with_db_mysql_ssl.sh0000775000175000017500000000417113436577655030315 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server in # secure mode with SSL connection to a MySQL database for users # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) --mysql-userdb="host=localhost dbname=coturn user=turn password=turn cipher=DHE-RSA-AES256-SHA connect_timeout=30 read_timeout=30" # means that local MySQL database "coturn" will be used, with database user "turn" and # database user password "turn", and with SSL connection with cipher DHE-RSA-AES256-SHA, # and connection timeout 30 seconds. # 7) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 8) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 9) "--log-file=stdout" means that all log output will go to the stdout. # 10) --cipher-list=ALL means that we support all OpenSSL ciphers # 11) --oauth - support oAuth security dialog # 12) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --server-name="blackdow.carleon.gov" -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 -r north.gov --mysql-userdb="host=localhost dbname=coturn user=turn password=turn cipher=DHE-RSA-AES256-SHA connect_timeout=30 read_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL --oauth --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/longtermsecuredb/secure_relay_with_db_sqlite.sh0000775000175000017500000000334613436577655027573 0ustar misimisi#!/bin/sh # # This is an example how to start a TURN Server in # secure mode with SQLite database for users # with the long-term credentials mechanism. # # We start here a TURN Server listening on IPv4 address # 127.0.0.1 and on IPv6 address ::1. We use 127.0.0.1 as # IPv4 relay address, and we use ::1 as IPv6 relay address. # # Other options: # # 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps). # 2) use fingerprints (-f) # 3) use 3 relay threads (-m 3) # 4) use min UDP relay port 32355 and max UDP relay port 65535 # 5) "-r north.gov" means "use authentication realm north.gov" # 6) --db= # means that local database will be used. # 7) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. # 8) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name. # 9) "--log-file=stdout" means that all log output will go to the stdout. # 10) --cipher-list=ALL means that we support all OpenSSL ciphers # 11) --oauth - support oAuth security dialog # 12) --cli-password=secret means that cli password set to "secret" # Other parameters (config file name, etc) are default. if [ -d examples ] ; then cd examples fi export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/ PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --server-name="blackdow.carleon.gov" -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --allow-loopback-peers --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 -r north.gov --db="var/db/turndb" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL --oauth --cli-password=secret $@ turnserver-4.5.1.1/examples/scripts/pack.sh0000775000175000017500000000052713436577655017400 0ustar misimisi#!/bin/sh # Run it from the root of the coturn source tree V=4.5.1.0 PACKDIR=`pwd`/../coturn-releases/ SRCDIR=`pwd` DDIR=turnserver-${V} cd ${SRCDIR}/ make distclean cd ${PACKDIR} rm -rf tmp mkdir tmp cd tmp mkdir ${DDIR} cp -R ${SRCDIR}/* ${DDIR}/ tar cvfz ../${DDIR}.tar.gz ${DDIR} cd .. rm -rf tmp cp -a ${SRCDIR}/ChangeLog ${PACKDIR} turnserver-4.5.1.1/examples/etc/0000775000175000017500000000000013436577655015203 5ustar misimisiturnserver-4.5.1.1/examples/etc/turn_server_pkey.pem0000664000175000017500000000321713436577655021317 0ustar misimisi-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAv6bYkERhZ43RjW4EuqCaTq5g+D+lJI/GwlVzdzQ3+F4clMQD R1kp1nX+9AvwjCXz3AYwY1H9CqjmjGM4R9uNJJseK/aJd2DUFADkF+7I674XwX8U 2Fy5on9jqWq3jdbb8eg/awcTBdrNLWNPquwfS2KVdooj9yPkqnO0c3ko1/OzIQCc s09O3l/MPt+aOsHk3B9l79ZRs3zWkylI+we0Fnc+7tZEpsCztA+KCCoiJf7NenOv VhdKg7D1AXuzJ/P/Euvc3+CIiS9HI4pWLopY1k+HydLeIcopqSbg9CRIKe1HOL8Y TvCm2ZoTqgijwWUlGtwEDf2xxUQX/TLYiW8JFQIDAQABAoIBADUPHCXUyKLCwKFH NEf27sGZxX71H+NfaseioLT/3/8DDyagncfDB7I4OL2YEKC8YScpD3xv1n59BFcZ oRtDzW+1AkVpm+VRCWYAWSXHFhkuJ6WKaVr9UOeMHStqQCcktP/kLKqU6s9UJDnM pOHNPVzBjl+jHxHs/gGyxuKxSH2Anwkrzpiv5j0obKFnw3QtAqeZRs1NlvPtYt2S eihZWr8r8LqylPk9ga9MYmO79Yr+EPVaqd6bmz4MpZJ4/7LEjx03Q6azdMCPhFNY cYzPIDZFEj81Zj/tqA2MU/uTTUUrcXint4dHRJs34m5N68PV1Y1XhhH6FG0+X711 ZymudoECgYEA/ChS5zmmOoLoaq2441+PzQbDP45qR6+G4slHwC8RDZhsYw0hQnp9 n44Qagpt74J4FjxT20BdE714DZP32IqagUwatWRQ+z3UoGafkJSNc5JSEogwZ65C nC8RI1pPHLEvE8IzBJiqUA1kbMOMfTYW694wdN9JVZang05/AXaJzm8CgYEAwpJ8 nJRR9JFweHRrRgnrVk0Qi+ABbN9T/nhPXYab2vjBfeBOTA1Mob0M3zMJDCnL2i+D K1GzE6WaYHElr45j2Wfphd/rRTk74WR4BaPpTCGaAhBQNn0ufqUkKsCPEAlTU+nG iyXP4OvdMPjEBckjbKm/mlX7m0njSHAY6SWNorsCgYEAi8Yubk3efwChpMC3hBIs vBHLmSdwclwyAPRh+X4djdO4AQ/+J8OObytond86IVHJD0pRkW+UKKUWLzCeakIq cxGknHgHC72yZ1d7i8FMx4uMQwmLC23lLn5ImbgtslHlLqavcRTPE6DY0hFzhtS8 z/JSGfbLx83C/V49uKnkqbECgYA6h1oYt70XdpCAi3ShcuZp5XCuwslq+JsJlyM4 nP9RFTcPKGQlGHMOzBGNKor0L7Z0gYpRg5f8tvoDPMX7UzfR9CIY9UyOXDMZD+HS wIWzMwBi0olueqV7zy1b9uSSDFwWh+IDhXJM1GaLDqnYm7KeQ0mxoV+4TLej2KSF rZg3dQKBgQCVrVxFV8jHBsRsH5PzMx6pUSAollmuyte9mGU1MIE7EZf+LEQIAjGZ 9jvtAILYVJXwVZv1/zNxldUfBNuWc95ft+Gg7FEN0p0uLpdYNXQUcXuJaJ9tJ1td ZfvRcrUXdFNKYt9/yaGeHVaIQfp4W1faZD7OnII7EOVkUKyv/qNGAA== -----END RSA PRIVATE KEY----- turnserver-4.5.1.1/examples/etc/turnserver.conf0000664000175000017500000006260013436577655020275 0ustar misimisi# Coturn TURN SERVER configuration file # # Boolean values note: where boolean value is supposed to be used, # you can use '0', 'off', 'no', 'false', 'f' as 'false, # and you can use '1', 'on', 'yes', 'true', 't' as 'true' # If the value is missed, then it means 'true'. # # Listener interface device (optional, Linux only). # NOT RECOMMENDED. # #listening-device=eth0 # TURN listener port for UDP and TCP (Default: 3478). # Note: actually, TLS & DTLS sessions can connect to the # "plain" TCP & UDP port(s), too - if allowed by configuration. # #listening-port=3478 # TURN listener port for TLS (Default: 5349). # Note: actually, "plain" TCP & UDP sessions can connect to the TLS & DTLS # port(s), too - if allowed by configuration. The TURN server # "automatically" recognizes the type of traffic. Actually, two listening # endpoints (the "plain" one and the "tls" one) are equivalent in terms of # functionality; but we keep both endpoints to satisfy the RFC 5766 specs. # For secure TCP connections, we currently support SSL version 3 and # TLS version 1.0, 1.1 and 1.2. # For secure UDP connections, we support DTLS version 1. # #tls-listening-port=5349 # Alternative listening port for UDP and TCP listeners; # default (or zero) value means "listening port plus one". # This is needed for RFC 5780 support # (STUN extension specs, NAT behavior discovery). The TURN Server # supports RFC 5780 only if it is started with more than one # listening IP address of the same family (IPv4 or IPv6). # RFC 5780 is supported only by UDP protocol, other protocols # are listening to that endpoint only for "symmetry". # #alt-listening-port=0 # Alternative listening port for TLS and DTLS protocols. # Default (or zero) value means "TLS listening port plus one". # #alt-tls-listening-port=0 # Listener IP address of relay server. Multiple listeners can be specified. # If no IP(s) specified in the config file or in the command line options, # then all IPv4 and IPv6 system IPs will be used for listening. # #listening-ip=172.17.19.101 #listening-ip=10.207.21.238 #listening-ip=2607:f0d0:1002:51::4 # Auxiliary STUN/TURN server listening endpoint. # Aux servers have almost full TURN and STUN functionality. # The (minor) limitations are: # # 1) Auxiliary servers do not have alternative ports and # they do not support STUN RFC 5780 functionality (CHANGE REQUEST). # # 2) Auxiliary servers also are never returning ALTERNATIVE-SERVER reply. # # Valid formats are 1.2.3.4:5555 for IPv4 and [1:2::3:4]:5555 for IPv6. # # There may be multiple aux-server options, each will be used for listening # to client requests. # #aux-server=172.17.19.110:33478 #aux-server=[2607:f0d0:1002:51::4]:33478 # (recommended for older Linuxes only) # Automatically balance UDP traffic over auxiliary servers (if configured). # The load balancing is using the ALTERNATE-SERVER mechanism. # The TURN client must support 300 ALTERNATE-SERVER response for this # functionality. # #udp-self-balance # Relay interface device for relay sockets (optional, Linux only). # NOT RECOMMENDED. # #relay-device=eth1 # Relay address (the local IP address that will be used to relay the # packets to the peer). # Multiple relay addresses may be used. # The same IP(s) can be used as both listening IP(s) and relay IP(s). # # If no relay IP(s) specified, then the turnserver will apply the default # policy: it will decide itself which relay addresses to be used, and it # will always be using the client socket IP address as the relay IP address # of the TURN session (if the requested relay address family is the same # as the family of the client socket). # #relay-ip=172.17.19.105 #relay-ip=2607:f0d0:1002:51::5 # For Amazon EC2 users: # # TURN Server public/private address mapping, if the server is behind NAT. # In that situation, if a -X is used in form "-X " then that ip will be reported # as relay IP address of all allocations. This scenario works only in a simple case # when one single relay address is be used, and no RFC5780 functionality is required. # That single relay address must be mapped by NAT to the 'external' IP. # The "external-ip" value, if not empty, is returned in XOR-RELAYED-ADDRESS field. # For that 'external' IP, NAT must forward ports directly (relayed port 12345 # must be always mapped to the same 'external' port 12345). # # In more complex case when more than one IP address is involved, # that option must be used several times, each entry must # have form "-X ", to map all involved addresses. # RFC5780 NAT discovery STUN functionality will work correctly, # if the addresses are mapped properly, even when the TURN server itself # is behind A NAT. # # By default, this value is empty, and no address mapping is used. # #external-ip=60.70.80.91 # #OR: # #external-ip=60.70.80.91/172.17.19.101 #external-ip=60.70.80.92/172.17.19.102 # Number of the relay threads to handle the established connections # (in addition to authentication thread and the listener thread). # If explicitly set to 0 then application runs relay process in a # single thread, in the same thread with the listener process # (the authentication thread will still be a separate thread). # # If this parameter is not set, then the default OS-dependent # thread pattern algorithm will be employed. Usually the default # algorithm is the most optimal, so you have to change this option # only if you want to make some fine tweaks. # # In the older systems (Linux kernel before 3.9), # the number of UDP threads is always one thread per network listening # endpoint - including the auxiliary endpoints - unless 0 (zero) or # 1 (one) value is set. # #relay-threads=0 # Lower and upper bounds of the UDP relay endpoints: # (default values are 49152 and 65535) # #min-port=49152 #max-port=65535 # Uncomment to run TURN server in 'normal' 'moderate' verbose mode. # By default the verbose mode is off. #verbose # Uncomment to run TURN server in 'extra' verbose mode. # This mode is very annoying and produces lots of output. # Not recommended under any normal circumstances. # #Verbose # Uncomment to use fingerprints in the TURN messages. # By default the fingerprints are off. # #fingerprint # Uncomment to use long-term credential mechanism. # By default no credentials mechanism is used (any user allowed). # #lt-cred-mech # This option is opposite to lt-cred-mech. # (TURN Server with no-auth option allows anonymous access). # If neither option is defined, and no users are defined, # then no-auth is default. If at least one user is defined, # in this file or in command line or in usersdb file, then # lt-cred-mech is default. # #no-auth # TURN REST API flag. # (Time Limited Long Term Credential) # Flag that sets a special authorization option that is based upon authentication secret. # # This feature's purpose is to support "TURN Server REST API", see # "TURN REST API" link in the project's page # https://github.com/coturn/coturn/ # # This option is used with timestamp: # # usercombo -> "timestamp:userid" # turn user -> usercombo # turn password -> base64(hmac(secret key, usercombo)) # # This allows TURN credentials to be accounted for a specific user id. # If you don't have a suitable id, the timestamp alone can be used. # This option is just turning on secret-based authentication. # The actual value of the secret is defined either by option static-auth-secret, # or can be found in the turn_secret table in the database (see below). # # Read more about it: # - https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00 # - https://www.ietf.org/proceedings/87/slides/slides-87-behave-10.pdf # # Be aware that use-auth-secret overrides some part of lt-cred-mech. # Notice that this feature depends internally on lt-cred-mech, so if you set # use-auth-secret then it enables internally automatically lt-cred-mech option # like if you enable both. # # You can use only one of the to auth mechanisms in the same time because, # both mechanism use the username and password validation in different way. # # This way be aware that you can't use both auth mechnaism in the same time! # Use in config either the lt-cred-mech or the use-auth-secret # to avoid any confusion. # #use-auth-secret # 'Static' authentication secret value (a string) for TURN REST API only. # If not set, then the turn server # will try to use the 'dynamic' value in turn_secret table # in user database (if present). The database-stored value can be changed on-the-fly # by a separate program, so this is why that other mode is 'dynamic'. # #static-auth-secret=north # Server name used for # the oAuth authentication purposes. # The default value is the realm name. # #server-name=blackdow.carleon.gov # Flag that allows oAuth authentication. # #oauth # 'Static' user accounts for long term credentials mechanism, only. # This option cannot be used with TURN REST API. # 'Static' user accounts are NOT dynamically checked by the turnserver process, # so that they can NOT be changed while the turnserver is running. # #user=username1:key1 #user=username2:key2 # OR: #user=username1:password1 #user=username2:password2 # # Keys must be generated by turnadmin utility. The key value depends # on user name, realm, and password: # # Example: # $ turnadmin -k -u ninefingers -r north.gov -p youhavetoberealistic # Output: 0xbc807ee29df3c9ffa736523fb2c4e8ee # ('0x' in the beginning of the key is what differentiates the key from # password. If it has 0x then it is a key, otherwise it is a password). # # The corresponding user account entry in the config file will be: # #user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee # Or, equivalently, with open clear password (less secure): #user=ninefingers:youhavetoberealistic # # SQLite database file name. # # Default file name is /var/db/turndb or /usr/local/var/db/turndb or # /var/lib/turn/turndb. # #userdb=/var/db/turndb # PostgreSQL database connection string in the case that we are using PostgreSQL # as the user database. # This database can be used for long-term credential mechanism # and it can store the secret value for secret-based timed authentication in TURN RESP API. # See http://www.postgresql.org/docs/8.4/static/libpq-connect.html for 8.x PostgreSQL # versions connection string format, see # http://www.postgresql.org/docs/9.2/static/libpq-connect.html#LIBPQ-CONNSTRING # for 9.x and newer connection string formats. # #psql-userdb="host= dbname= user= password= connect_timeout=30" # MySQL database connection string in the case that we are using MySQL # as the user database. # This database can be used for long-term credential mechanism # and it can store the secret value for secret-based timed authentication in TURN RESP API. # # Optional connection string parameters for the secure communications (SSL): # ca, capath, cert, key, cipher # (see http://dev.mysql.com/doc/refman/5.1/en/ssl-options.html for the # command options description). # # Use string format as below (space separated parameters, all optional): # #mysql-userdb="host= dbname= user= password= port= connect_timeout= read_timeout=" # If you want to use in the MySQL connection string the password in encrypted format, # then set in this option the MySQL password encryption secret key file. # # Warning: If this option is set, then mysql password must be set in "mysql-userdb" in encrypted format! # If you want to use cleartext password then do not set this option! # # This is the file path which contain secret key of aes encryption while using password encryption. # #secret-key-file=/path/ # MongoDB database connection string in the case that we are using MongoDB # as the user database. # This database can be used for long-term credential mechanism # and it can store the secret value for secret-based timed authentication in TURN RESP API. # Use string format is described at http://hergert.me/docs/mongo-c-driver/mongoc_uri.html # #mongo-userdb="mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]" # Redis database connection string in the case that we are using Redis # as the user database. # This database can be used for long-term credential mechanism # and it can store the secret value for secret-based timed authentication in TURN RESP API. # Use string format as below (space separated parameters, all optional): # #redis-userdb="ip= dbname= password= port= connect_timeout=" # Redis status and statistics database connection string, if used (default - empty, no Redis stats DB used). # This database keeps allocations status information, and it can be also used for publishing # and delivering traffic and allocation event notifications. # The connection string has the same parameters as redis-userdb connection string. # Use string format as below (space separated parameters, all optional): # #redis-statsdb="ip= dbname= password= port= connect_timeout=" # The default realm to be used for the users when no explicit # origin/realm relationship was found in the database, or if the TURN # server is not using any database (just the commands-line settings # and the userdb file). Must be used with long-term credentials # mechanism or with TURN REST API. # # Note: If default realm is not specified at all, then realm falls back to the host domain name. # If domain name is empty string, or '(None)', then it is initialized to am empty string. # #realm=mycompany.org # The flag that sets the origin consistency # check: across the session, all requests must have the same # main ORIGIN attribute value (if the ORIGIN was # initially used by the session). # #check-origin-consistency # Per-user allocation quota. # default value is 0 (no quota, unlimited number of sessions per user). # This option can also be set through the database, for a particular realm. # #user-quota=0 # Total allocation quota. # default value is 0 (no quota). # This option can also be set through the database, for a particular realm. # #total-quota=0 # Max bytes-per-second bandwidth a TURN session is allowed to handle # (input and output network streams are treated separately). Anything above # that limit will be dropped or temporary suppressed (within # the available buffer limits). # This option can also be set through the database, for a particular realm. # #max-bps=0 # # Maximum server capacity. # Total bytes-per-second bandwidth the TURN server is allowed to allocate # for the sessions, combined (input and output network streams are treated separately). # # bps-capacity=0 # Uncomment if no UDP client listener is desired. # By default UDP client listener is always started. # #no-udp # Uncomment if no TCP client listener is desired. # By default TCP client listener is always started. # #no-tcp # Uncomment if no TLS client listener is desired. # By default TLS client listener is always started. # #no-tls # Uncomment if no DTLS client listener is desired. # By default DTLS client listener is always started. # #no-dtls # Uncomment if no UDP relay endpoints are allowed. # By default UDP relay endpoints are enabled (like in RFC 5766). # #no-udp-relay # Uncomment if no TCP relay endpoints are allowed. # By default TCP relay endpoints are enabled (like in RFC 6062). # #no-tcp-relay # Uncomment if extra security is desired, # with nonce value having limited lifetime. # By default, the nonce value is unique for a session, # and has unlimited lifetime. # Set this option to limit the nonce lifetime. # It defaults to 600 secs (10 min) if no value is provided. After that delay, # the client will get 438 error and will have to re-authenticate itself. # #stale-nonce=600 # Uncomment if you want to set the maximum allocation # time before it has to be refreshed. # Default is 3600s. # #max-allocate-lifetime=3600 # Uncomment to set the lifetime for the channel. # Default value is 600 secs (10 minutes). # This value MUST not be changed for production purposes. # #channel-lifetime=600 # Uncomment to set the permission lifetime. # Default to 300 secs (5 minutes). # In production this value MUST not be changed, # however it can be useful for test purposes. # #permission-lifetime=300 # Certificate file. # Use an absolute path or path relative to the # configuration file. # #cert=/usr/local/etc/turn_server_cert.pem # Private key file. # Use an absolute path or path relative to the # configuration file. # Use PEM file format. # #pkey=/usr/local/etc/turn_server_pkey.pem # Private key file password, if it is in encoded format. # This option has no default value. # #pkey-pwd=... # Allowed OpenSSL cipher list for TLS/DTLS connections. # Default value is "DEFAULT". # #cipher-list="DEFAULT" # CA file in OpenSSL format. # Forces TURN server to verify the client SSL certificates. # By default it is not set: there is no default value and the client # certificate is not checked. # # Example: #CA-file=/etc/ssh/id_rsa.cert # Curve name for EC ciphers, if supported by OpenSSL # library (TLS and DTLS). The default value is prime256v1, # if pre-OpenSSL 1.0.2 is used. With OpenSSL 1.0.2+, # an optimal curve will be automatically calculated, if not defined # by this option. # #ec-curve-name=prime256v1 # Use 566 bits predefined DH TLS key. Default size of the key is 1066. # #dh566 # Use 2066 bits predefined DH TLS key. Default size of the key is 1066. # #dh2066 # Use custom DH TLS key, stored in PEM format in the file. # Flags --dh566 and --dh2066 are ignored when the DH key is taken from a file. # #dh-file= # Flag to prevent stdout log messages. # By default, all log messages are going to both stdout and to # the configured log file. With this option everything will be # going to the configured log only (unless the log file itself is stdout). # #no-stdout-log # Option to set the log file name. # By default, the turnserver tries to open a log file in # /var/log, /var/tmp, /tmp and current directories directories # (which open operation succeeds first that file will be used). # With this option you can set the definite log file name. # The special names are "stdout" and "-" - they will force everything # to the stdout. Also, the "syslog" name will force everything to # the system log (syslog). # In the runtime, the logfile can be reset with the SIGHUP signal # to the turnserver process. # #log-file=/var/tmp/turn.log # Option to redirect all log output into system log (syslog). # #syslog # This flag means that no log file rollover will be used, and the log file # name will be constructed as-is, without PID and date appendage. # This option can be used, for example, together with the logrotate tool. # #simple-log # Option to set the "redirection" mode. The value of this option # will be the address of the alternate server for UDP & TCP service in form of # [:]. The server will send this value in the attribute # ALTERNATE-SERVER, with error 300, on ALLOCATE request, to the client. # Client will receive only values with the same address family # as the client network endpoint address family. # See RFC 5389 and RFC 5766 for ALTERNATE-SERVER functionality description. # The client must use the obtained value for subsequent TURN communications. # If more than one --alternate-server options are provided, then the functionality # can be more accurately described as "load-balancing" than a mere "redirection". # If the port number is omitted, then the default port # number 3478 for the UDP/TCP protocols will be used. # Colon (:) characters in IPv6 addresses may conflict with the syntax of # the option. To alleviate this conflict, literal IPv6 addresses are enclosed # in square brackets in such resource identifiers, for example: # [2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478 . # Multiple alternate servers can be set. They will be used in the # round-robin manner. All servers in the pool are considered of equal weight and # the load will be distributed equally. For example, if we have 4 alternate servers, # then each server will receive 25% of ALLOCATE requests. A alternate TURN server # address can be used more than one time with the alternate-server option, so this # can emulate "weighting" of the servers. # # Examples: #alternate-server=1.2.3.4:5678 #alternate-server=11.22.33.44:56789 #alternate-server=5.6.7.8 #alternate-server=[2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478 # Option to set alternative server for TLS & DTLS services in form of # :. If the port number is omitted, then the default port # number 5349 for the TLS/DTLS protocols will be used. See the previous # option for the functionality description. # # Examples: #tls-alternate-server=1.2.3.4:5678 #tls-alternate-server=11.22.33.44:56789 #tls-alternate-server=[2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478 # Option to suppress TURN functionality, only STUN requests will be processed. # Run as STUN server only, all TURN requests will be ignored. # By default, this option is NOT set. # #stun-only # Option to suppress STUN functionality, only TURN requests will be processed. # Run as TURN server only, all STUN requests will be ignored. # By default, this option is NOT set. # #no-stun # This is the timestamp/username separator symbol (character) in TURN REST API. # The default value is ':'. # rest-api-separator=: # Flag that can be used to allow peers on the loopback addresses (127.x.x.x and ::1). # This is an extra security measure. # # (To avoid any security issue that allowing loopback access may raise, # the no-loopback-peers option is replaced by allow-loopback-peers.) # # Allow it only for testing in a development environment! # In production it adds a possible security vulnerability, so for security reasons # it is not allowed using it together with empty cli-password. # #allow-loopback-peers # Flag that can be used to disallow peers on well-known broadcast addresses (224.0.0.0 and above, and FFXX:*). # This is an extra security measure. # #no-multicast-peers # Option to set the max time, in seconds, allowed for full allocation establishment. # Default is 60 seconds. # #max-allocate-timeout=60 # Option to allow or ban specific ip addresses or ranges of ip addresses. # If an ip address is specified as both allowed and denied, then the ip address is # considered to be allowed. This is useful when you wish to ban a range of ip # addresses, except for a few specific ips within that range. # # This can be used when you do not want users of the turn server to be able to access # machines reachable by the turn server, but would otherwise be unreachable from the # internet (e.g. when the turn server is sitting behind a NAT) # # Examples: # denied-peer-ip=83.166.64.0-83.166.95.255 # allowed-peer-ip=83.166.68.45 # File name to store the pid of the process. # Default is /var/run/turnserver.pid (if superuser account is used) or # /var/tmp/turnserver.pid . # #pidfile="/var/run/turnserver.pid" # Require authentication of the STUN Binding request. # By default, the clients are allowed anonymous access to the STUN Binding functionality. # #secure-stun # Mobility with ICE (MICE) specs support. # #mobility # Allocate Address Family according # If enabled then TURN server allocates address family according the TURN # Client <=> Server communication address family. # (By default coTURN works according RFC 6156.) # !!Warning: Enabling this option breaks RFC6156 section-4.2 (violates use default IPv4)!! # #keep-address-family # User name to run the process. After the initialization, the turnserver process # will make an attempt to change the current user ID to that user. # #proc-user= # Group name to run the process. After the initialization, the turnserver process # will make an attempt to change the current group ID to that group. # #proc-group= # Turn OFF the CLI support. # By default it is always ON. # See also options cli-ip and cli-port. # #no-cli #Local system IP address to be used for CLI server endpoint. Default value # is 127.0.0.1. # #cli-ip=127.0.0.1 # CLI server port. Default is 5766. # #cli-port=5766 # CLI access password. Default is empty (no password). # For the security reasons, it is recommended to use the encrypted # for of the password (see the -P command in the turnadmin utility). # # Secure form for password 'qwerty': # #cli-password=$5$79a316b350311570$81df9cfb9af7f5e5a76eada31e7097b663a0670f99a3c07ded3f1c8e59c5658a # # Or unsecure form for the same password: # #cli-password=qwerty # Enable Web-admin support on https. By default it is Disabled. # If it is enabled it also enables a http a simple static banner page # with a small reminder that the admin page is available only on https. # #web-admin # Local system IP address to be used for Web-admin server endpoint. Default value is 127.0.0.1. # #web-admin-ip=127.0.0.1 # Web-admin server port. Default is 8080. # #web-admin-port=8080 # Web-admin server listen on STUN/TURN worker threads # By default it is disabled for security resons! (Not recommended in any production environment!) # #web-admin-listen-on-workers # Server relay. NON-STANDARD AND DANGEROUS OPTION. # Only for those applications when we want to run # server applications on the relay endpoints. # This option eliminates the IP permissions check on # the packets incoming to the relay endpoints. # #server-relay # Maximum number of output sessions in ps CLI command. # This value can be changed on-the-fly in CLI. The default value is 256. # #cli-max-output-sessions # Set network engine type for the process (for internal purposes). # #ne=[1|2|3] # Do not allow an TLS/DTLS version of protocol # #no-tlsv1 #no-tlsv1_1 #no-tlsv1_2 turnserver-4.5.1.1/examples/etc/turn_client_cert.pem0000664000175000017500000000254313436577655021255 0ustar misimisi-----BEGIN CERTIFICATE----- MIIDzjCCArYCCQD3YHhln4EqhDANBgkqhkiG9w0BAQUFADCBpzELMAkGA1UEBhMC VVMxCzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxXYWxudXQgQ3JlZWsxKzApBgNVBAoT IlJGQzU3NjYgVFVSTiBTZXJ2ZXIgcHVibGljIHByb2plY3QxFDASBgNVBAsTC2Rl dmVsb3BtZW50MQ0wCwYDVQQDEwRPbGVnMSIwIAYJKoZIhvcNAQkBFhNtb20wNDAy NjdAZ21haWwuY29tMCAXDTEyMTEyNzAwNDEwNVoYDzIxMTIxMTAzMDA0MTA1WjCB pzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxXYWxudXQgQ3Jl ZWsxKzApBgNVBAoTIlJGQzU3NjYgVFVSTiBTZXJ2ZXIgcHVibGljIHByb2plY3Qx FDASBgNVBAsTC2RldmVsb3BtZW50MQ0wCwYDVQQDEwRPbGVnMSIwIAYJKoZIhvcN AQkBFhNtb20wNDAyNjdAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEA3huHvPYyvNZBK91bP3O1dBdOj93YQ3812BTcRMjEYnvSyyEosxFd dEnILgDiFK//pFnDtwm7FxOCtVwRQ0+8qGTH4vH0EIpKTBsaafKH3L9CYe40pwcm BJHvclOa4vl2Ghi09+M0UEHdokkM77K9rpXx7aZILoICkqnoAuBe0TY8D5PBXinM gtk7HlrvANxSmPHAAaGQ5t/+jfTWVH1UYCpogTgCKYPbNi+joKu6oEz+qRKAqDYd FY6/Qpiv7reYiNiVhM7HGNY27FkKDJDBhsmZRmtTIEdYFfcWPZvv69L7Rf1skOXF Vm5/to3HArJJF+lz6YGj0C3pE6dZt6sUmQIDAQABMA0GCSqGSIb3DQEBBQUAA4IB AQAhXgGdXXf0dMPdkfl4jv4dqFNSmax6wmeNc+oJC9qIFVDLsdAaAWXZ+pZHYIMR UN8mQobsIZdfPQ0gs8CgUwrKziAjA92y2Q/I7vsg83qRLhysGC5etYMD/wlySDDS AJKraevDPTEdmfNstCblubNG2PIeqV1isWtPMqB2dMsCeyzJXVyfD0QcABzFv4Fs MMy7JI7MsctNh1tjV/0TsddDMeMLs22rix5fS8MZ6uunFzIuJ0MshFNehXFuvz0B uNmn0k7djUm3h+2Avs3YGCo/8GtqHapc/lva/9gT+iEW0e7i0Ru5Jhar66VMzJqv +wEhQafC77d3vWHtXQU8dYmM -----END CERTIFICATE----- turnserver-4.5.1.1/examples/etc/turn_server_cert.pem0000664000175000017500000000247213436577655021306 0ustar misimisi-----BEGIN CERTIFICATE----- MIIDsDCCApgCCQCmgrJCiQlGOTANBgkqhkiG9w0BAQUFADCBmDELMAkGA1UEBhMC VVMxCzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxXYWxudXQgQ3JlZWsxHDAaBgNVBAoT E1RVUk4gU2VydmVyIHByb2plY3QxFDASBgNVBAsTC0RldmVsb3BtZW50MQ0wCwYD VQQDEwRPbGVnMSIwIAYJKoZIhvcNAQkBFhNtb20wNDAyNjdAZ21haWwuY29tMCAX DTEyMTEyNTA4MjAxNloYDzIxMTIxMTAxMDgyMDE2WjCBmDELMAkGA1UEBhMCVVMx CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxXYWxudXQgQ3JlZWsxHDAaBgNVBAoTE1RV Uk4gU2VydmVyIHByb2plY3QxFDASBgNVBAsTC0RldmVsb3BtZW50MQ0wCwYDVQQD EwRPbGVnMSIwIAYJKoZIhvcNAQkBFhNtb20wNDAyNjdAZ21haWwuY29tMIIBIjAN BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv6bYkERhZ43RjW4EuqCaTq5g+D+l JI/GwlVzdzQ3+F4clMQDR1kp1nX+9AvwjCXz3AYwY1H9CqjmjGM4R9uNJJseK/aJ d2DUFADkF+7I674XwX8U2Fy5on9jqWq3jdbb8eg/awcTBdrNLWNPquwfS2KVdooj 9yPkqnO0c3ko1/OzIQCcs09O3l/MPt+aOsHk3B9l79ZRs3zWkylI+we0Fnc+7tZE psCztA+KCCoiJf7NenOvVhdKg7D1AXuzJ/P/Euvc3+CIiS9HI4pWLopY1k+HydLe IcopqSbg9CRIKe1HOL8YTvCm2ZoTqgijwWUlGtwEDf2xxUQX/TLYiW8JFQIDAQAB MA0GCSqGSIb3DQEBBQUAA4IBAQATbrBOLV4e8Qmsby9+srxXsdbNc60PmDZ4WiZ1 IElfWmzM7wGXm9sJg1PX/7T24R1tbwZGLIhZnkhecG372GChULZJ9Pdjh0Ab2nK5 LRKHXTpjp/xOJvx0JMCIIyRnGZT1nABPOk8uEjNW8PaU6yhQ4f5nKaSOgYGRCln6 dcy5vylCsyD9Q7GXs0KOC38XD+Ycv6VLX4zKJ2Yum50Wt643nLjG9RlGT3FXWJ1K HUbPC5TO6bcYLdiTjaYr+X8xC/x6h/Ngdo/16w7fRmQQ4uS+TVXrg8ITmI71KX/I m7C9jbsubwzrhW84oZXYf+o/0ATtEAhiVLnHifKCCYikqfVj -----END CERTIFICATE----- turnserver-4.5.1.1/examples/etc/turn_client_pkey.pem0000664000175000017500000000321313436577655021263 0ustar misimisi-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEA3huHvPYyvNZBK91bP3O1dBdOj93YQ3812BTcRMjEYnvSyyEo sxFddEnILgDiFK//pFnDtwm7FxOCtVwRQ0+8qGTH4vH0EIpKTBsaafKH3L9CYe40 pwcmBJHvclOa4vl2Ghi09+M0UEHdokkM77K9rpXx7aZILoICkqnoAuBe0TY8D5PB XinMgtk7HlrvANxSmPHAAaGQ5t/+jfTWVH1UYCpogTgCKYPbNi+joKu6oEz+qRKA qDYdFY6/Qpiv7reYiNiVhM7HGNY27FkKDJDBhsmZRmtTIEdYFfcWPZvv69L7Rf1s kOXFVm5/to3HArJJF+lz6YGj0C3pE6dZt6sUmQIDAQABAoIBAH5ITN8FZEe10gws qUrkcRD2h3aI/gMyetzGz45UUERmfq17xvY5M1eA884kNmbowoMhfoO9hqBSOYkA Ndh9p5he5L+GLeyRlDi9WEFQ4iqCnC2uEEW/bMBAcVIhcvkGOT4ROiOPDRlsuaUh v7cxe2OeYZVra7L1vJzC+eVYyNBN5CgK8w08MPEkupQS9+Jvr0QWCikRz187cG45 EiDMrBKyJNE9lY6u4P8gJ+/NgaASWP/D3kbsjiQ2OwSGLrwDAvWC7Bx2GK3/0goA btp7YGaWvp+mE5V91cOW+PfweC5Do4MjOr4ToNkczW0AxKE5o94yo56h+II5bX6N z65VvtkCgYEA/Sq/3S2yup/Oodzj003KG4skWYFrj7KXeXgm7RZcpNwkd8JaFXJ/ Cwl7/3bkRv6RHLmXX/2hcNWlxq3u6Efs1EjtycdArU68kO01vLdExJYIzHKmHikV n+T4hukxGDzObxn3lH1KcOodh/x572Uufn79dewoZCPzH8t/jiMOWGcCgYEA4JfN 66Kq/oDookqenM9Ij5l6zeeNwzMjIlkU2eG0DAH0KdsBN/hTGGGRQVBk03YREQmK crEhGAZxzfrX5fK11UVG3C2pqAtrVe6FuD32vFUpP1MO0ftSA889NoEwGdNZV4pV Mk0+6xVCNOatj2inMXlQq5s68WfCzkiWD7uLCv8CgYBcwuYsF4tuYBGpMzNzAAS2 1OPLu+T6cPiZdFHm+xOVAGiITPkO9LXiCGabsydvb+UhvkrdzCP0IQQt6RsplvkK y3H9RfnHxprHC3NuI0SaN1Mf/j4pvOoEfTQm0pi/hcAp6zzQ9ptpBg8t/W98LPm9 NbCPHamrD5UMqFajcOrXrwKBgD8D2M8IcRm/aYY/kYlFz4Ia+g3Trj7alj0I6YTI gw/rbGph/FGL5ySsG2lL+T4rnlY9aw8LC9IF3OCCRRlLpCEWsu8MENIJgjA2IGa1 XAkzi8MstrfL4BMZjn9AeBKG7kZVldnrOoATEuRs5L2cC20iMLQ1dbBOAKaITzJS 2IxZAoGBAKqwr/uennxJrnMtpjLBgcphoU3aXJZvzzDqlOaqzJp6Xmbese4sDEe0 hvVHreigDzOnGnqL/vSjTDWaLqS/O1iE7p+UrGIkZj/Zl6Jk54OX6AHmWE2LhdlU FYgIQKX7fuocpF1Dpe7xEeVwvdp+UqbDzHQg1CWGe1cBPYDYIkSH -----END RSA PRIVATE KEY----- turnserver-4.5.1.1/build-docker.sh0000775000175000017500000000055613436577655015523 0ustar misimisi#!/bin/bash set -o xtrace dir=`pwd` echo "$dir" build_image=coturnbuild dockerargs="--privileged -v ${dir}:/root/coturn -w /root/coturn" container_env=' -e "INSIDECONTAINER=-incontainer=true"' docker="docker run --rm -it ${dockerargs} ${container_env} ${build_image}" docker build -f Dockerfile.build -t ${build_image} . ${docker} bash -c "./configure && make" turnserver-4.5.1.1/AUTHORS0000664000175000017500000000245413436577655013667 0ustar misimisiOleg Moskalenko : General design and implementation (2011-2013); Gabor Kovesdan, http://kovesdan.org : FreeBSD packaging (since v1.5.2.6); Daniel Pocock, http://danielpocock.com : Debian packaging (since v1.8.3.6); John Selbie (jselbie@gmail.com) : Stuntman interoperability, RFC5780 fixes MS Windows port work (since v1.8.3.6); Lee Sylvester : Status and statistics - ideas and pilot implementation (since v1.8.4.0); Erik Johnston : Access Control Lists, 2013 (since v1.8.5.0); Roman Lisagor : Testing, code optimization (since v1.8.6.0); Vladimir Tsanev : configure script and Makefile fixes, Arch Linux port (since v1.8.6.1); Po-sheng Lin : Libevent dependencies cleaning (since v2.0.1.1); Peter Dunkley : CentOS/Fedora port (since v2.6.6.1) Mutsutoshi Yoshimoto : TCP routing: testing and bug fixes (since v3.2.2.7) Federico Pinna : MongoDB support (since v4.1.0.1) Bradley T. Hughes : FreeBSD port (since v4.1.2.1) Mészáros Mihály : OAuth utility, NAT behavior Discovery (since v4.5.0.4) turnserver-4.5.1.1/rpm/0000775000175000017500000000000013436577655013410 5ustar misimisiturnserver-4.5.1.1/rpm/build.settings.sh0000775000175000017500000000033213436577655016703 0ustar misimisi#!/bin/bash # Common settings script. TURNVERSION=4.5.1.1 BUILDDIR=~/rpmbuild ARCH=`uname -p` TURNSERVER_GIT_URL=https://github.com/coturn/coturn.git WGETOPTIONS="--no-check-certificate" RPMOPTIONS="-ivh --force" turnserver-4.5.1.1/rpm/CentOS6.pre.build.sh0000775000175000017500000000377413436577655017066 0ustar misimisi#!/bin/bash # CentOS6 preparation script. CPWD=`pwd` . ./common.pre.build.sh cd ${CPWD} LIBEVENT_MAJOR_VERSION=2 LIBEVENT_VERSION=${LIBEVENT_MAJOR_VERSION}.0.21 LIBEVENT_DISTRO=libevent-${LIBEVENT_VERSION}-stable.tar.gz LIBEVENT_SPEC_DIR=libevent.rpm LIBEVENT_SPEC_GIT_URL=https://github.com/coturn/coturn/raw/libevent.rpm LIBEVENT_SPEC_FILE=libevent.spec # Common packs PACKS="mysql-devel sqlite sqlite-devel" sudo yum -y install ${PACKS} ER=$? if ! [ ${ER} -eq 0 ] ; then echo "Cannot install package(s) ${PACKS}" cd ${CPWD} exit -1 fi # Libevent2: if ! [ -f ${BUILDDIR}/SPECS/${LIBEVENT_SPEC_FILE} ] ; then cd ${BUILDDIR}/tmp rm -rf ${LIBEVENT_SPEC_DIR} mkdir ${LIBEVENT_SPEC_DIR} cd ${LIBEVENT_SPEC_DIR} wget ${WGETOPTIONS} ${LIBEVENT_SPEC_GIT_URL}/${LIBEVENT_SPEC_FILE} ER=$? if ! [ ${ER} -eq 0 ] ; then cd ${CPWD} exit -1 fi wget ${WGETOPTIONS} ${LIBEVENT_SPEC_GIT_URL}/${LIBEVENT_DISTRO} ER=$? if ! [ ${ER} -eq 0 ] ; then cd ${CPWD} exit -1 fi cd .. if ! [ -f ${LIBEVENT_SPEC_DIR}/${LIBEVENT_SPEC_FILE} ] ; then echo "ERROR: cannot download ${LIBEVENT_SPEC_FILE} file" cd ${CPWD} exit -1 fi cp ${LIBEVENT_SPEC_DIR}/${LIBEVENT_SPEC_FILE} ${BUILDDIR}/SPECS cp ${LIBEVENT_SPEC_DIR}/${LIBEVENT_DISTRO} ${BUILDDIR}/SOURCES fi cd ${BUILDDIR}/SPECS rpmbuild -ba ${BUILDDIR}/SPECS/${LIBEVENT_SPEC_FILE} ER=$? if ! [ ${ER} -eq 0 ] ; then cd ${CPWD} exit -1 fi PACK=${BUILDDIR}/RPMS/${ARCH}/libevent-${LIBEVENT_MAJOR_VERSION}*.rpm sudo rpm ${RPMOPTIONS} ${PACK} ER=$? if ! [ ${ER} -eq 0 ] ; then echo "Cannot install packages ${PACK}" cd ${CPWD} exit -1 fi PACK=${BUILDDIR}/RPMS/${ARCH}/libevent-devel*.rpm sudo rpm ${RPMOPTIONS} ${PACK} ER=$? if ! [ ${ER} -eq 0 ] ; then echo "Cannot install packages ${PACK}" cd ${CPWD} exit -1 fi # EPEL (for hiredis) cd ${CPWD} ./epel6.install.sh # Platform file echo "CentOS6.8" > ${BUILDDIR}/platform cp ${CPWD}/epel6.install.sh ${BUILDDIR}/install.sh cd ${CPWD} turnserver-4.5.1.1/rpm/build.instructions.txt0000664000175000017500000000352213436577655020015 0ustar misimisiMANUAL PROCESS FOR CENTOS 6: The first thing you need to build/use the TURN server with CentOS is to build and install libevent 2.x.x. CentOS 6 ships with libevent 1.x.x. You can find a .spec file to build libevent 2.x.x here: https://github.com/crocodilertc/libevent To build libevent: 1) Install the dependencies for building libevent: gcc, make, redhat-rpm-config, doxygen, openssl-devel, rpm-build 2) $ mkdir ~/rpmbuild 3) $ mkdir ~/rpmbuild/SOURCES 4) $ mkdir ~/rpmbuild/SPECS 5) Put the tarball for libevent (https://github.com/downloads/libevent/libevent/libevent-2.0.21-stable.tar.gz) and put it into ~/rpmbuild/SOURCES 6) Put the .spec for libevent (https://raw.github.com/crocodilertc/libevent/master/libevent.spec) into ~/rpmbuild/SPECS 7) Build the RPMs, "rpmbuild -ba ~/rpmbuild/SPECS/libevent.spec" To build the TURN server: 1) Install libevent and libevent-devel rpms 2) Install EPEL (http://fedoraproject.org/wiki/EPEL) - needed for hiredis 3) Install the dependencies for building the TURN server: gcc, make, redhat-rpm-config, openssl-devel, libevent-devel >= 2.0.0, sqlite, sqlite-devel, mysql-devel (or mariadb-devel), postgresql-devel, hiredis-devel 4) $ mkdir ~/rpmbuild 5) $ mkdir ~/rpmbuild/SOURCES 6) Export the TURN server from Github, "git clone https://github.com/coturn/coturn.git" 7) Create a tarball, "tar zcf ~/rpmbuild/SOURCES/turnserver-2.6.7.0.tar.gz turnserver-2.6.7.0" 8) Build the RPMs, "rpmbuild -ta ~/rpmbuild/SOURCES/turnserver-2.6.7.0.tar.gz" AUTOMATED PROCESS FOR CENTOS 6: $ cd <...>/coturn/rpm $ ./CentOS6.pre.build.sh $ ./build.sh (then see the tarball in ~/rpmbuild/RPMS/) AUTOMATED PROCESS FOR Fedora: $ cd <...>/coturn/rpm $ ./Fedora.pre.build.sh $ ./build.sh (then see the tarball in ~/rpmbuild/RPMS/) turnserver-4.5.1.1/rpm/CentOS7.pre.build.sh0000775000175000017500000000074113436577655017056 0ustar misimisi#!/bin/bash # CentOS7 preparation script. CPWD=`pwd` . ./common.pre.build.sh cd ${CPWD} # Common packs PACKS="libevent-devel mariadb-devel sqlite sqlite-devel" sudo yum -y install ${PACKS} ER=$? if ! [ ${ER} -eq 0 ] ; then echo "Cannot install package(s) ${PACKS}" cd ${CPWD} exit -1 fi # EPEL (for hiredis) cd ${CPWD} ./epel7.install.sh # Platform file echo "CentOS7.4" > ${BUILDDIR}/platform cp ${CPWD}/epel7.install.sh ${BUILDDIR}/install.sh cd ${CPWD} turnserver-4.5.1.1/rpm/turnserver.sysconfig0000664000175000017500000000006413436577655017555 0ustar misimisi# # TURN Server startup options # EXTRA_OPTIONS="" turnserver-4.5.1.1/rpm/epel6.install.sh0000775000175000017500000000117413436577655016432 0ustar misimisi#!/bin/bash CPWD=`pwd` # Epel installation script EPEL=epel-release-6-8.noarch EPELRPM=${EPEL}.rpm BUILDDIR=~/rpmbuild WGETOPTIONS="--no-check-certificate" RPMOPTIONS="-ivh --force" mkdir -p ${BUILDDIR} mkdir -p ${BUILDDIR}/RPMS sudo yum -y install wget cd ${BUILDDIR}/RPMS if ! [ -f ${EPELRPM} ] ; then wget ${WGETOPTIONS} http://download.fedoraproject.org/pub/epel/6/i386/${EPELRPM} ER=$? if ! [ ${ER} -eq 0 ] ; then cd ${CPWD} exit -1 fi fi PACK=${EPELRPM} sudo rpm ${RPMOPTIONS} ${PACK} ER=$? if ! [ ${ER} -eq 0 ] ; then echo "Cannot install package ${PACK}" cd ${CPWD} exit -1 fi cd ${CPWD} turnserver-4.5.1.1/rpm/turnserver.init.el0000664000175000017500000000260213436577655017113 0ustar misimisi#!/bin/bash # # Startup script for TURN Server # # chkconfig: 345 85 15 # description: RFC 5766 TURN Server # # processname: turnserver # pidfile: /var/run/turnserver/turnserver.pid # config: /etc/turnserver/turnserver.conf # ### BEGIN INIT INFO # Provides: turnserver # Required-Start: $local_fs $network # Short-Description: RFC 5766 TURN Server # Description: RFC 5766 TURN Server ### END INIT INFO # Source function library. . /etc/rc.d/init.d/functions TURN=/usr/bin/turnserver PROG=turnserver TURNCFG=/etc/turnserver/$PROG.conf PID_FILE=/var/run/turnserver/$PROG.pid LOCK_FILE=/var/lock/subsys/$PROG DEFAULTS=/etc/sysconfig/$PROG RETVAL=0 USER=turnserver start() { echo -n $"Starting $PROG: " daemon --user=$USER $TURN $OPTIONS RETVAL=$? if [ $RETVAL = 0 ]; then pidofproc $TURN > $PID_FILE RETVAL=$? [ $RETVAL = 0 ] && touch $LOCK_FILE && success fi echo return $RETVAL } stop() { echo -n $"Stopping $PROG: " killproc $TURN RETVAL=$? echo [ $RETVAL = 0 ] && rm -f $LOCK_FILE $PID_FILE } [ -f $DEFAULTS ] && . $DEFAULTS OPTIONS="-o -c $TURNCFG $EXTRA_OPTIONS" # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status $TURN RETVAL=$? ;; restart) stop start ;; condrestart) if [ -f $PID_FILE ] ; then stop start fi ;; *) echo $"Usage: $PROG {start|stop|restart|condrestart|status|help}" exit 1 esac exit $RETVAL turnserver-4.5.1.1/rpm/uninstall.turnserver.sh0000775000175000017500000000040213436577655020172 0ustar misimisi#!/bin/sh for i in `rpm -q -a | grep turnserver-utils-` do echo $i sudo rpm -e $i done for i in `rpm -q -a | grep turnserver-client-libs-` do echo $i sudo rpm -e $i done for i in `rpm -q -a | grep turnserver.*-` do echo $i sudo rpm -e $i done turnserver-4.5.1.1/rpm/common.pre.build.sh0000775000175000017500000000066613436577655017132 0ustar misimisi#!/bin/bash # Common preparation script. . ./build.settings.sh # DIRS rm -rf ${BUILDDIR} mkdir -p ${BUILDDIR} mkdir -p ${BUILDDIR}/SOURCES mkdir -p ${BUILDDIR}/SPECS mkdir -p ${BUILDDIR}/RPMS mkdir -p ${BUILDDIR}/tmp # Common packs PACKS="make gcc redhat-rpm-config rpm-build doxygen openssl-devel git wget" sudo yum -y install ${PACKS} ER=$? if ! [ ${ER} -eq 0 ] ; then echo "Cannot install packages ${PACKS}" exit -1 fi turnserver-4.5.1.1/rpm/Fedora.pre.build.sh0000775000175000017500000000051013436577655017026 0ustar misimisi#!/bin/bash CPWD=`pwd` # Fedora preparation script. . ./common.pre.build.sh PACKS="libevent-devel mariadb-devel sqlite sqlite-devel" sudo yum -y install ${PACKS} ER=$? if ! [ ${ER} -eq 0 ] ; then echo "Cannot install package(s) ${PACKS}" cd ${CPWD} exit -1 fi echo "Fedora20" > ${BUILDDIR}/platform cd ${CPWD} turnserver-4.5.1.1/rpm/build.sh0000775000175000017500000000401313436577655015044 0ustar misimisi#!/bin/bash CPWD=`pwd` . ./build.settings.sh # Required packages PACKS="postgresql-devel hiredis-devel" sudo yum -y install ${PACKS} ER=$? if ! [ ${ER} -eq 0 ] ; then echo "Cannot install packages ${PACKS}" cd ${CPWD} exit -1 fi # TURN cd ${BUILDDIR}/tmp rm -rf turnserver-${TURNVERSION} git clone ${TURNSERVER_GIT_URL} --branch ${TURNVERSION} turnserver-${TURNVERSION} ER=$? if ! [ ${ER} -eq 0 ] ; then git clone ${TURNSERVER_GIT_URL} turnserver-${TURNVERSION} ER=$? if ! [ ${ER} -eq 0 ] ; then cd ${CPWD} exit -1 fi fi tar zcf ${BUILDDIR}/SOURCES/turnserver-${TURNVERSION}.tar.gz turnserver-${TURNVERSION} ER=$? if ! [ ${ER} -eq 0 ] ; then cd ${CPWD} exit -1 fi rpmbuild -ta ${BUILDDIR}/SOURCES/turnserver-${TURNVERSION}.tar.gz ER=$? if ! [ ${ER} -eq 0 ] ; then cd ${CPWD} exit -1 fi # Make binary tarball cd ${BUILDDIR}/RPMS/${ARCH} mkdir -p di mv *debuginfo* di mv *devel* di rm -rf turnserver-${TURNVERSION} mkdir turnserver-${TURNVERSION} mv *.rpm turnserver-${TURNVERSION}/ rm -rf turnserver-${TURNVERSION}/install.sh if [ -f ${BUILDDIR}/install.sh ] ; then cat ${BUILDDIR}/install.sh > turnserver-${TURNVERSION}/install.sh else echo "#!/bin/sh" > turnserver-${TURNVERSION}/install.sh fi cat <>turnserver-${TURNVERSION}/install.sh sudo yum -y install openssl sudo yum -y install telnet sudo yum -y install sqlite for i in *.rpm ; do sudo yum -y install \${i} ER=\$? if ! [ \${ER} -eq 0 ] ; then sudo rpm -Uvh \${i} ER=\$? if ! [ \${ER} -eq 0 ] ; then sudo rpm -ivh --force \${i} ER=\$? if ! [ \${ER} -eq 0 ] ; then echo "ERROR: cannot install package \${i}" exit -1 fi fi fi done echo SUCCESS ! EOF chmod a+x turnserver-${TURNVERSION}/install.sh cp ${CPWD}/uninstall.turnserver.sh turnserver-${TURNVERSION}/ chmod a+x turnserver-${TURNVERSION}/uninstall.turnserver.sh PLATFORM=`cat ${BUILDDIR}/platform` tar cvfz turnserver-${TURNVERSION}-${PLATFORM}-${ARCH}.tar.gz turnserver-${TURNVERSION} cd ${CPWD} turnserver-4.5.1.1/rpm/turnserver-tmpfiles.conf0000664000175000017500000000005713436577655020321 0ustar misimisid /run/turnserver 0750 turnserver turnserver - turnserver-4.5.1.1/rpm/turnserver.service.fc0000664000175000017500000000113113436577655017574 0ustar misimisi[Unit] Description=coturn Documentation=man:coturn(1) man:turnadmin(1) man:turnserver(1) After=syslog.target network.target [Service] User=turnserver Group=turnserver Type=forking EnvironmentFile=/etc/sysconfig/turnserver PIDFile=/var/run/turnserver/turnserver.pid ExecStart=/usr/bin/turnserver -o -c /etc/turnserver/turnserver.conf $EXTRA_OPTIONS ExecStopPost=/usr/bin/rm -f /var/run/turnserver/turnserver.pid Restart=on-abort LimitCORE=infinity LimitNOFILE=999999 LimitNPROC=60000 LimitRTPRIO=infinity LimitRTTIME=7000000 CPUSchedulingPolicy=other UMask=0007 [Install] WantedBy=multi-user.target turnserver-4.5.1.1/rpm/turnserver.spec0000664000175000017500000003772513436577655016521 0ustar misimisiName: turnserver Version: 4.5.1.1 Release: 0%{dist} Summary: Coturn TURN Server Group: System Environment/Libraries License: BSD URL: https://github.com/coturn/coturn/ Source0: http://turnserver.open-sys.org/downloads/v%{version}/%{name}-%{version}.tar.gz BuildRequires: gcc, make, redhat-rpm-config, sqlite-devel BuildRequires: openssl-devel, libevent-devel >= 2.0.0, postgresql-devel BuildRequires: hiredis-devel Requires: openssl, sqlite, libevent >= 2.0.0, mysql-libs, postgresql-libs Requires: hiredis, perl-DBI, perl-libwww-perl Requires: telnet %if 0%{?el6} BuildRequires: epel-release, mysql-devel Requires: epel-release, mysql-libs %else BuildRequires: mariadb-devel Requires: mariadb-libs %endif %description The TURN Server is a VoIP media traffic NAT traversal server and gateway. It can be used as a general-purpose network traffic TURN server/gateway, too. This implementation also includes some extra features. Supported RFCs: TURN specs: - RFC 5766 - base TURN specs - RFC 6062 - TCP relaying TURN extension - RFC 6156 - IPv6 extension for TURN - Experimental DTLS support as client protocol. STUN specs: - RFC 3489 - "classic" STUN - RFC 5389 - base "new" STUN specs - RFC 5769 - test vectors for STUN protocol testing - RFC 5780 - NAT behavior discovery support The implementation fully supports the following client-to-TURN-server protocols: - UDP (per RFC 5766) - TCP (per RFC 5766 and RFC 6062) - TLS (per RFC 5766 and RFC 6062); TLS1.0/TLS1.1/TLS1.2 - DTLS (experimental non-standard feature) Supported relay protocols: - UDP (per RFC 5766) - TCP (per RFC 6062) Supported user databases (for user repository, with passwords or keys, if authentication is required): - SQLite - MySQL - PostgreSQL - Redis Redis can also be used for status and statistics storage and notification. Supported TURN authentication mechanisms: - long-term - TURN REST API (a modification of the long-term mechanism, for time-limited secret-based authentication, for WebRTC applications) The load balancing can be implemented with the following tools (either one or a combination of them): - network load-balancer server - DNS-based load balancing - built-in ALTERNATE-SERVER mechanism. %package utils Summary: TURN client utils Group: System Environment/Libraries Requires: turnserver-client-libs = %{version}-%{release} %description utils This package contains the TURN client utils. %package client-libs Summary: TURN client library Group: System Environment/Libraries Requires: openssl, libevent >= 2.0.0 %description client-libs This package contains the TURN client library. %package client-devel Summary: TURN client development headers. Group: Development/Libraries Requires: turnserver-client-libs = %{version}-%{release} %description client-devel This package contains the TURN client development headers. %prep %setup -q -n %{name}-%{version} %build PREFIX=%{_prefix} CONFDIR=%{_sysconfdir}/%{name} EXAMPLESDIR=%{_datadir}/%{name} \ MANPREFIX=%{_datadir} LIBDIR=%{_libdir} ./configure make %install rm -rf $RPM_BUILD_ROOT DESTDIR=$RPM_BUILD_ROOT make install mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig install -m644 rpm/turnserver.sysconfig \ $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/turnserver sed -i -e "s/#syslog/syslog/g" \ -e "s/#no-stdout-log/no-stdout-log/g" \ $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/turnserver.conf.default %if 0%{?el6} mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d install -m755 rpm/turnserver.init.el \ $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d/turnserver %else sed -i -e "s/#pidfile/pidfile/g" \ -e "s:/var/run/turnserver.pid:/var/run/turnserver/turnserver.pid:g" \ $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/turnserver.conf.default mkdir -p $RPM_BUILD_ROOT/%{_unitdir} install -m755 rpm/turnserver.service.fc \ $RPM_BUILD_ROOT/%{_unitdir}/turnserver.service %endif mv $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/turnserver.conf.default $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/turnserver.conf %{__install} -Dpm 0644 rpm/turnserver-tmpfiles.conf %{buildroot}%{_tmpfilesdir}/turnserver.conf mkdir -p %{buildroot}%{_localstatedir}/run/turnserver %clean rm -rf "$RPM_BUILD_ROOT" %pre %{_sbindir}/groupadd -r turnserver 2> /dev/null || : %{_sbindir}/useradd -r -g turnserver -s /bin/false -c "TURN Server daemon" -d \ %{_datadir}/%{name} turnserver 2> /dev/null || : %post %if 0%{?el6} /sbin/chkconfig --add turnserver %else /bin/systemctl --system daemon-reload %endif %preun if [ $1 = 0 ]; then %if 0%{?el6} /sbin/service turnserver stop > /dev/null 2>&1 /sbin/chkconfig --del turnserver %else /bin/systemctl stop turnserver.service /bin/systemctl disable turnserver.service 2> /dev/null %endif fi %postun %if 0%{?fedora} /bin/systemctl --system daemon-reload %endif %files %defattr(-,root,root) %{_bindir}/turnserver %{_bindir}/turnadmin %attr(0640,turnserver,turnserver) %{_localstatedir}/db/turndb %{_mandir}/man1/coturn.1.gz %{_mandir}/man1/turnserver.1.gz %{_mandir}/man1/turnadmin.1.gz %dir %attr(-,turnserver,turnserver) %{_sysconfdir}/%{name} %config(noreplace) %attr(0644,turnserver,turnserver) %{_sysconfdir}/%{name}/turnserver.conf %dir %attr(0750,turnserver,turnserver) %{_localstatedir}/run/turnserver %config(noreplace) %{_sysconfdir}/sysconfig/turnserver %if 0%{?el6} %config %{_sysconfdir}/rc.d/init.d/turnserver %else %config %{_unitdir}/turnserver.service %{_tmpfilesdir}/turnserver.conf %endif %dir %{_docdir}/%{name} %{_docdir}/%{name}/LICENSE %{_docdir}/%{name}/INSTALL %{_docdir}/%{name}/postinstall.txt %{_docdir}/%{name}/README.turnadmin %{_docdir}/%{name}/README.turnserver %{_docdir}/%{name}/schema.sql %{_docdir}/%{name}/schema.mongo.sh %{_docdir}/%{name}/schema.stats.redis %{_docdir}/%{name}/schema.userdb.redis %dir %{_datadir}/%{name} %{_datadir}/%{name}/schema.sql %{_datadir}/%{name}/schema.mongo.sh %{_datadir}/%{name}/schema.stats.redis %{_datadir}/%{name}/schema.userdb.redis %{_datadir}/%{name}/testredisdbsetup.sh %{_datadir}/%{name}/testmongosetup.sh %{_datadir}/%{name}/testsqldbsetup.sql %dir %{_datadir}/%{name}/etc %{_datadir}/%{name}/etc/turn_server_cert.pem %{_datadir}/%{name}/etc/turn_server_pkey.pem %{_datadir}/%{name}/etc/turnserver.conf %dir %{_datadir}/%{name}/scripts %{_datadir}/%{name}/scripts/peer.sh %{_datadir}/%{name}/scripts/oauth.sh %{_datadir}/%{name}/scripts/readme.txt %{_datadir}/%{name}/scripts/pack.sh %dir %{_datadir}/%{name}/scripts/basic %{_datadir}/%{name}/scripts/basic/dos_attack.sh %{_datadir}/%{name}/scripts/basic/relay.sh %{_datadir}/%{name}/scripts/basic/tcp_client.sh %{_datadir}/%{name}/scripts/basic/tcp_client_c2c_tcp_relay.sh %{_datadir}/%{name}/scripts/basic/udp_c2c_client.sh %{_datadir}/%{name}/scripts/basic/udp_client.sh %dir %{_datadir}/%{name}/scripts/loadbalance %{_datadir}/%{name}/scripts/loadbalance/master_relay.sh %{_datadir}/%{name}/scripts/loadbalance/slave_relay_1.sh %{_datadir}/%{name}/scripts/loadbalance/slave_relay_2.sh %{_datadir}/%{name}/scripts/loadbalance/tcp_c2c_tcp_relay.sh %{_datadir}/%{name}/scripts/loadbalance/udp_c2c.sh %dir %{_datadir}/%{name}/scripts/longtermsecure %{_datadir}/%{name}/scripts/longtermsecure/secure_dos_attack.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_dtls_client.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_dtls_client_cert.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_tls_client_cert.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_relay.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_relay_cert.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_tcp_client.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_tcp_client_c2c_tcp_relay.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_tls_client.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_tls_client_c2c_tcp_relay.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_udp_c2c.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_udp_client.sh %{_datadir}/%{name}/scripts/longtermsecure/secure_sctp_client.sh %dir %{_datadir}/%{name}/scripts/longtermsecuredb %{_datadir}/%{name}/scripts/longtermsecuredb/secure_relay_with_db_mysql.sh %{_datadir}/%{name}/scripts/longtermsecuredb/secure_relay_with_db_mysql_ssl.sh %{_datadir}/%{name}/scripts/longtermsecuredb/secure_relay_with_db_mongo.sh %{_datadir}/%{name}/scripts/longtermsecuredb/secure_relay_with_db_psql.sh %{_datadir}/%{name}/scripts/longtermsecuredb/secure_relay_with_db_redis.sh %{_datadir}/%{name}/scripts/longtermsecuredb/secure_relay_with_db_sqlite.sh %dir %{_datadir}/%{name}/scripts/restapi %{_datadir}/%{name}/scripts/restapi/secure_relay_secret.sh %{_datadir}/%{name}/scripts/restapi/secure_relay_secret_with_db_mysql.sh %{_datadir}/%{name}/scripts/restapi/secure_relay_secret_with_db_psql.sh %{_datadir}/%{name}/scripts/restapi/secure_relay_secret_with_db_redis.sh %{_datadir}/%{name}/scripts/restapi/secure_relay_secret_with_db_mongo.sh %{_datadir}/%{name}/scripts/restapi/secure_relay_secret_with_db_sqlite.sh %{_datadir}/%{name}/scripts/restapi/secure_udp_client_with_secret.sh %{_datadir}/%{name}/scripts/restapi/shared_secret_maintainer.pl %dir %{_datadir}/%{name}/scripts/selfloadbalance %{_datadir}/%{name}/scripts/selfloadbalance/secure_dos_attack.sh %{_datadir}/%{name}/scripts/selfloadbalance/secure_relay.sh %dir %{_datadir}/%{name}/scripts/mobile %{_datadir}/%{name}/scripts/mobile/mobile_relay.sh %{_datadir}/%{name}/scripts/mobile/mobile_dtls_client.sh %{_datadir}/%{name}/scripts/mobile/mobile_tcp_client.sh %{_datadir}/%{name}/scripts/mobile/mobile_tls_client_c2c_tcp_relay.sh %{_datadir}/%{name}/scripts/mobile/mobile_udp_client.sh %files utils %defattr(-,root,root) %{_bindir}/turnutils_peer %{_bindir}/turnutils_stunclient %{_bindir}/turnutils_uclient %{_bindir}/turnutils_oauth %{_bindir}/turnutils_natdiscovery %{_mandir}/man1/turnutils.1.gz %{_mandir}/man1/turnutils_peer.1.gz %{_mandir}/man1/turnutils_stunclient.1.gz %{_mandir}/man1/turnutils_uclient.1.gz %{_mandir}/man1/turnutils_oauth.1.gz %{_mandir}/man1/turnutils_natdiscovery.1.gz %dir %{_docdir}/%{name} %{_docdir}/%{name}/LICENSE %{_docdir}/%{name}/README.turnutils %dir %{_datadir}/%{name} %dir %{_datadir}/%{name}/etc %{_datadir}/%{name}/etc/turn_client_cert.pem %{_datadir}/%{name}/etc/turn_client_pkey.pem %files client-libs %{_docdir}/%{name}/LICENSE %{_libdir}/libturnclient.a %files client-devel %{_docdir}/%{name}/LICENSE %dir %{_includedir}/turn %{_includedir}/turn/ns_turn_defs.h %dir %{_includedir}/turn/client %{_includedir}/turn/client/ns_turn_ioaddr.h %{_includedir}/turn/client/ns_turn_msg_addr.h %{_includedir}/turn/client/ns_turn_msg_defs.h %{_includedir}/turn/client/ns_turn_msg_defs_experimental.h %{_includedir}/turn/client/ns_turn_msg.h %{_includedir}/turn/client/TurnMsgLib.h %changelog * Sat Mar 2 2019 Mészáros Mihály - Sync to 4.5.1.1 * Thu Dec 6 2018 Mészáros Mihály - Sync to 4.5.1.0 * Thu Sep 27 2018 Oleg Moskalenko - Sync to 4.5.0.8 * Sun Dec 10 2017 Oleg Moskalenko - Sync to 4.5.0.7 * Mon Oct 17 2016 Oleg Moskalenko - Sync to 4.5.0.6 * Sat Aug 27 2016 Oleg Moskalenko - Sync to 4.5.0.5 * Sat Aug 20 2016 Oleg Moskalenko - Sync to 4.5.0.4 * Sun Oct 11 2015 Oleg Moskalenko - Sync to 4.5.0.3 * Tue Sep 29 2015 Oleg Moskalenko - Sync to 4.5.0.2 * Sun Sep 13 2015 Oleg Moskalenko - Sync to 4.5.0.1 * Sat Jul 18 2015 Oleg Moskalenko - Sync to 4.4.5.4 * Sat Jun 20 2015 Oleg Moskalenko - Sync to 4.4.5.3 * Fri May 29 2015 Oleg Moskalenko - Sync to 4.4.5.2 * Tue Mar 31 2015 Oleg Moskalenko - Sync to 4.4.4.2 * Sun Mar 15 2015 Oleg Moskalenko - Sync to 4.4.4.1 * Sat Feb 28 2015 Oleg Moskalenko - Sync to 4.4.2.3 * Wed Feb 18 2015 Oleg Moskalenko - Sync to 4.4.2.2 * Tue Feb 3 2015 Oleg Moskalenko - Sync to 4.4.2.1 * Sun Feb 1 2015 Oleg Moskalenko - Sync to 4.4.1.2 * Sat Jan 24 2015 Oleg Moskalenko - Sync to 4.4.1.1 * Wed Dec 24 2014 Oleg Moskalenko - Sync to 4.3.3.1 * Sun Dec 14 2014 Oleg Moskalenko - Sync to 4.3.2.2 * Sat Nov 29 2014 Oleg Moskalenko - Sync to 4.3.1.3 * Mon Nov 24 2014 Oleg Moskalenko - Sync to 4.3.1.2 * Sat Nov 22 2014 Oleg Moskalenko - Sync to 4.3.1.1 * Fri Nov 07 2014 Oleg Moskalenko - Sync to 4.2.3.1 * Sun Oct 26 2014 Oleg Moskalenko - Sync to 4.2.2.2 * Sun Oct 05 2014 Oleg Moskalenko - Sync to 4.2.1.2 * Thu Aug 14 2014 Oleg Moskalenko - Sync to 4.1.2.1 * Tue Jul 29 2014 Oleg Moskalenko - Sync to 4.1.1.1 * Tue Jul 22 2014 Oleg Moskalenko - Sync to 4.1.0.2 * Wed Jun 25 2014 Oleg Moskalenko - Sync to 4.0.1.4 * Fri Jun 13 2014 Oleg Moskalenko - Sync to 4.0.1.3 * Fri Jun 06 2014 Oleg Moskalenko - Sync to 4.0.1.2 * Sun May 18 2014 Oleg Moskalenko - Sync to 4.0.0.2 * Wed May 07 2014 Oleg Moskalenko - Sync to 4.0.0.1 * Wed Apr 30 2014 Oleg Moskalenko - Sync to 4.0.0.0 * Tue Feb 04 2014 Oleg Moskalenko - Sync to 3.2.2.6 * Sat Jan 25 2014 Oleg Moskalenko - Sync to 3.2.2.5 * Fri Jan 24 2014 Oleg Moskalenko - Sync to 3.2.2.4 * Thu Jan 23 2014 Oleg Moskalenko - Sync to 3.2.2.3 * Tue Jan 21 2014 Oleg Moskalenko - Sync to 3.2.2.2 * Sat Jan 11 2014 Oleg Moskalenko - CPU optimization, added to 3.2.2.1 * Mon Jan 06 2014 Oleg Moskalenko - Linux epoll performance improvements, added to 3.2.1.4 * Mon Jan 06 2014 Oleg Moskalenko - Telnet client installation added to 3.2.1.3 * Sun Jan 05 2014 Oleg Moskalenko - Sync to 3.2.1.2 * Fri Jan 03 2014 Oleg Moskalenko - Sync to 3.2.1.1 * Thu Dec 26 2013 Oleg Moskalenko - Sync to 3.2.1.0 * Wed Dec 25 2013 Oleg Moskalenko - Sync to 3.1.6.0 * Mon Dec 23 2013 Oleg Moskalenko - Sync to 3.1.5.3 * Fri Dec 20 2013 Oleg Moskalenko - Sync to 3.1.5.1 * Thu Dec 19 2013 Oleg Moskalenko - Sync to 3.1.4.2 * Sat Dec 14 2013 Oleg Moskalenko - Sync to 3.1.3.1 * Wed Dec 11 2013 Oleg Moskalenko - OpenSSL installation fixed 3.1.2.3 * Tue Dec 10 2013 Oleg Moskalenko - Updated to version 3.1.2.2 * Mon Dec 09 2013 Oleg Moskalenko - Updated to version 3.1.2.1 * Sun Dec 01 2013 Oleg Moskalenko - Updated to version 3.1.1.0 * Sat Nov 30 2013 Oleg Moskalenko - Updated to version 3.0.2.1. * Thu Nov 28 2013 Oleg Moskalenko - Config file setting fixed: version 3.0.1.4. * Wed Nov 27 2013 Oleg Moskalenko - Config file setting fixed: version 3.0.1.3. * Mon Nov 25 2013 Oleg Moskalenko - Updated to version 3.0.1.2 * Sun Nov 10 2013 Oleg Moskalenko - Updated to version 3.0.0.0 * Fri Nov 8 2013 Oleg Moskalenko - Updated to version 2.6.7.2 * Thu Nov 7 2013 Oleg Moskalenko - Updated to version 2.6.7.1 * Sun Nov 3 2013 Oleg Moskalenko - Updated to version 2.6.7.0 * Sat Nov 2 2013 Peter Dunkley - Added Fedora support * Thu Oct 31 2013 Oleg Moskalenko - Updated to version 2.6.6.2 * Sun Oct 27 2013 Oleg Moskalenko - Updated to version 2.6.6.1 * Sun Oct 27 2013 Peter Dunkley - Updated to version 2.6.6.0 * Fri May 3 2013 Peter Dunkley - First version turnserver-4.5.1.1/rpm/epel7.install.sh0000775000175000017500000000011513436577655016425 0ustar misimisi#!/bin/bash # Epel installation script sudo yum -y install epel-release turnserver-4.5.1.1/INSTALL0000664000175000017500000012465313436577655013656 0ustar misimisiI. TURN Server as a standard OS package At the present time, several operation systems have this project pre-packaged: 1) New Linuxes in Debian family have package "coturn": http://packages.qa.debian.org/r/coturn.html which can be installed the usual way: apt install coturn If instead you are using the Debian package from the project download site, then follow these instructions: Unpack the archive: $ tar xvfz turnserver-<...>.tar.gz Read the INSTALl file: $ cat INSTALL Install the *.deb file: $ sudo apt-get update $ sudo apt-get install gdebi-core $ sudo gdebi coturn*.deb (to install the bare package without any dependencies, type: $ sudo dpkg -i coturn_*_*.deb) After the install, read the documentation in /usr/share/doc/coturn directory. All binaries will be installed in /usr/bin/ directory. The turn*.conf config files are in /etc directory. The service start-up control scripts will be in /etc/init.d/coturn and in /etc/defaults/coturn files. 2) ArchLinux has this TURN server package: https://aur.archlinux.org/packages/coturn/ 3) FreeBSD and OpenSUSE have the predecessor of this project packaged (rfc5766-turn-server). II. DOWNLOAD You have to download the archive file turnserver-*.tar.gz and unpack it: $ tar xfz turnserver-*.tgz it will create the directory 'turnserver-*' with all sources, build files, examples and documentation. III. BUILD If you are sure that you system is ready for the build (see the section "Extra libraries and Utilities" below) then you can build the system. First, you have to run the configure script: $ cd turnserver-* $ ./configure It will create a Makefile customized for your system. By default, the generated Makefile will install everything to: - /usr on Solaris. - /usr/pkg on NetBSD. - /usr/local everywhere else. The binaries will be copied to the bin subdirectory of the installation destination, config files copied to etc subdirectory. The default SQLite database will be created in var/db/turndb. There will be also documents, examples and some other files, in separate directories. You can change the root configured destination directory by setting PREFIX variable in the configure command line. For example: $ PREFIX=/opt ./configure Or: $ ./configure --prefix=/opt You can change the auxiliary configured destination sub-directories by setting BINDIR, CONFDIR, MANPREFIX, EXAMPLESDIR, DOCSDIR, LIBDIR, SCHEMADIR, LOCALSTATEDIR, TURNDBDIR and TURNINCLUDEDIR variables in the configure command line. For example: $ PREFIX=/opt BINDIR=/opt/bin64 CONFDIR=/opt/conf ./configure Or: $ ./configure --prefix=/opt --bindir=/opt/bin64 --confdir=/opt/conf You also can change the compilation and link options by setting common build variables in the configure command line. For example: $ CC=clang CFLAGS=-D_CAURIB LDFLAGS=-lshanka ./configure --prefix=/opt/shy See below a separate INSTALL section for more details. The script "configure" is a proprietary script. It will create a Makefile that you can use to build the project: $ make The make command without options will do the following: - compile the code. - create bin/ sub-directory and put the TURN server, TURN admin and "utility" programs there. - create lib/ sub-directory and put the client library there. - create include/turn/ sub-directory and put include files there. - create sqlite/turndb default empty database that will be copied to var/db/ during the installation. The TURN programs can be either called directly, or a shell scripts can be used. The script examples are located in examples/scripts directory. These scripts are just examples: you can run them successfully for the tests, but you will have to change the script parameters for your real environment. The command: $ sudo make install will install everything into the system file structure (see below). (NOTE: On NetBSD, use "su root -c"). The command: $ sudo make deinstall will remove all installed TURN Server files from your system. The command: $ make clean will clean all results of the build and configuration actions. Do not run "make clean" before "make deinstall". The "clean" command will remove the Makefile and you will not be able to "deinstall" then. If that has happened, then run ./configure and make again, then deinstall and then clean. NOTE: On most modern systems, the build will produce dynamically linked executables. If you want statically linked executables, you have to modify, accordingly, the Makefile.in template file. IV. OPENSSL If you are using the OpenSSL that is coming with your system, and you are OK with it, then you do not have to read this chapter. If your system has an outdated OpenSSL version, or if you need some very fresh OpenSSL features that are not present in the current usual stable version, then you may have to compile (and run) your TURN server with a different OpenSSL version. For example, if you need ALPN feature, or DTLS1.2, and your system comes with OpenSSL 1.0.1, you will not be able to use those features unless you install OpenSSL 1.0.2 and compile and run the TURN server with the newer version. The problem is, it is usually not safe to replace the system's OpenSSL with a different version. Some systems are "bound" to its "native" OpenSSL installations, and their behavior may become unpredictable with the newer versions. So you want to preserve your system's OpenSSL but you want to compile and to run the TURN server with newer OpenSSL version. There are different ways to do that. We are suggesting the following: 1) Download the OpenSSL version from openssl.org. 2) Let's assume that we want to install the "custom" OpenSSL into /opt. Configure and build OpenSSL as: $ ./config --prefix=/opt $ make $ make install Those commands will install OpenSSL into /opt, with static libraries (no dynamic libraries). 3) Build the TURN server: $ ./configure --prefix=/opt $ make Those commands will build the TURN server binaries, statically linked against the newer OpenSSL. 4) Then you can run the TURN server without setting the dynamic libraries paths - because it has been linked statically against the newer OpenSSL libraries. One potential problem is that libevent2 is using the OpenSSL, too. So, ideally, to be 100% safe of all potential discrepancies in the runtime, we'd suggesting rebuilding libevent2 with the newer OpenSSL, too. V. INSTALL This step is optional. You can run the turnserver from the original build directory, successfully, without installing the TURN server into your system. You have to install the turnserver only if you want to integrate the turnserver in your system. Run the command: $ make install It will install turnserver in /usr/local/ directory (or to whatever directory was set in the PREFIX variable). You will have to copy /usr/local/etc/turnserver.conf.default to /usr/local/etc/turnserver.conf file and adjust your runtime configuration. This command will also: - copy the content of examples subdirectory into PREFIX/share/examples/turnserver/ directory; - copy the generated default empty SQLite database from sqlite/turndb to /usr/local/var/db or to /var/db/turndb; - copy the content of include/turn subdirectory into PREFIX/include/turn/ directory; - copy the database schema file turndb/schema.sql into PREFIX/share/turnserver/ directory; - copy all docs into PREFIX/share/doc/turnserver/ directory. The installation destination of "make install" can be changed by using DESTDIR variable, for example: $ ./configure --prefix=/usr $ make $ make DESTDIR=/opt install In this example, the root installation directory will be /opt/usr. The "configure" script by default generates a Makefile with "rpath" option set for the dynamic libraries linking (if your system and your compiler allow that option). If that is not desirable (like in some OS packaging procedures), then run the "configure" script with --disable-rpath option. If you are not using the rpath linking option, then after the installation, you may have to adjust the system-wide shared library search path with "ldconfig -n " (Linux), "ldconfig -m " (BSD) or "crle -u -l " (Solaris). Your system must be able to find the libevent2, openssl and (optionally) SQLite and/or PostgreSQL and/or MySQL (MariaDB) and/or MongoDB and/or Redis shared libraries, either with the help of the system-wide library search configuration or by using LD_LIBRARY_PATH. "make install" will make a non-guaranteed effort to add automatically PREFIX/lib and /usr/local/lib to the libraries search path, but if you have some libraries in different non-default directories then you will have to add them manually to the search path, or you will have to adjust LD_LIBRARY_PATH. VI. PLATFORMS The TURN Server is using generic *NIX system APIs and is supposed to be usable on wide range of *NIX systems. The following platforms are supported (both i386 and x86_64 variants when applicable): - Linux, - BSD family (Mac OS X, FreeBSD, NetBSD, OpenBSD), - Solaris 11, - Cygwin It must work on other *NIXes, as well. The configure script and/or Makefile may need adjustments for other *NIXes not mentioned above. The code of the client messaging library can be compiled and used on Windows, too, but it is not supported for now. VI. COMPILERS The TURN Server is written in C programming language, for portability and for the performance reasons. The tested C compilers are: - gcc 3.4.4 thru 4.8.x - clang 3.0 or better - Solaris Studio 12 C compiler It may be compiled with other compilers, too. The code is compatible with C++ compiler, and a C++ compiler (like g++) can be used for the compilation, too: $ CC=g++ ./configure $ make VIII. WHICH EXTRA LIBRARIES AND UTILITIES YOU NEED In addition to common *NIX OS services and libraries, to compile this code, OpenSSL (version 1.0.0a or better recommended) and libevent2 (version 2.0.5 or better) are required, SQLite C development library and header is optional, the PostgreSQL C client development setup is optional, the MySQL (MariaDB) C client development setup is optional, the MongoDB C Driver and the Hiredis development files for Redis database access are all optional. For development build, the development headers and the libraries to link with, are to be installed. For the runtime, only the runtime setup is required. If the build is modified for static linking, then even runtime installation is not needed. OpenSSL, SQLite, libevent2, PostgreSQL, MySQL (or MariaDB) and Hiredis libraries can be downloaded from their web sites: - http://www.openssl.org (required); - http://www.libevent.org (required); - http://www.sqlite.org (optional); - http://www.postgresql.org (optional); - http://www.mysql.org (or http://mariadb.org) (optional); - https://github.com/mongodb/mongo-c-driver (optional); - http://redis.io (optional). The installations are pretty straightforward - the usual "./configure" and "make install" commands. Install them into their default locations - the configure script and the Makefile are assuming that they are installed in their default locations. If not, then you will have to modify those. Most modern popular systems (FreeBSD, Linux Ubuntu/Debian/Mint, Amazon Linux, Fedora) have a simpler way of the third party tools installation: *) FreeBSD (the FRESH ports database is assumed to be installed, with the turnserver port included): $ cd /usr/ports/net/turnserver $ sudo make install clear That's it - that command will install the TURN server with all necesary thrid-party tools. If you system have no fresh ports repository: $ cd /usr/ports/security/openssl/ $ sudo make install clean $ cd /usr/ports/databases/sqlite3/ $ sudo make install clean $ cd /usr/ports/devel/libevent2/ $ sudo make install clean $ cd /usr/ports/databases/postgresql84-client/ (or any other version) $ sudo make install clean $ cd /usr/ports/databases/mysql51-client/ (or any other version) $ sudo make install clean $ cd /usr/ports/databases/hiredis/ $ sudo make install clean **) Linux Ubuntu, Debian, Mint: $ sudo apt-get install libssl-dev $ sudo apt-get install libsqlite3 (or sqlite3) $ sudo apt-get install libsqlite3-dev (or sqlite3-dev) $ sudo apt-get install libevent-dev $ sudo apt-get install libpq-dev $ sudo apt-get install mysql-client $ sudo apt-get install libmysqlclient-dev $ sudo apt-get install libhiredis-dev or you can use Synaptic or other software center. ***) Fedora: $ sudo yum install openssl-devel $ sudo yum install sqlite $ sudo yum install sqlite-devel $ sudo yum install libevent $ sudo yum install libevent-devel $ sudo yum install postgresql-devel $ sudo yum install postgresql-server $ sudo yum install mysql-devel $ sudo yum install mysql-server $ sudo yum install hiredis $ sudo yum install hiredis-devel ****) Amazon Linux is similar to Fedora, but: - you have to install gcc first: $ sudo yum install gcc - mongo-c-driver packages are not available "automatically". MongoDB support will not be compiled, unless you install it "manually" before the TURN server compilation. Refer to https://github.com/mongodb/mongo-c-driver for installation instructions of the driver. - hiredis packages are not available, so do not issue the hiredis installation commands. Redis support will not be compiled, unless you install it "manually" before the TURN server compilation. For Amazon EC2 AMIs, we install the redis manually in the system. But the TURN server can be perfectly installed without redis support - if you do not need it. *****) Older Debian family Linuxes are using some packages with different names. ******) On some CentOS / RedHat 6.x systems you have to install libevent2 "manually", and optionally you have to download and install Hiredis, but everything else can be found in the software repository. Also, if you would like to make an RPM for CentOS, check the directory rpm/ with the instructions. NOTE: If your tools are installed in non-standard locations, you will have to adjust CFLAGS and LDFLAGS environment variables for TURN server ./configure script. For example, to configure the TURN server with Solaris 11 PostgreSQL 32-bits setup, you may use a command like this: $ CFLAGS="${CFLAGS} -I/usr/postgres/9.2-pgdg/include/" LDFLAGS="${LDFLAGS} -L/usr/postgres/9.2-pgdg/lib/" ./configure Dynamic library paths: You may also have to adjust the turn server start script, add all the dynamic runtime library paths to LD_LIBRARY_PATH. Or you may find that it would be more convenient to adjust the system-wide shared library search path by using commands: on Linux: $ ldconfig -n or on BSD: $ ldconfig -m or on Solaris: $ crle -u -l On Mac OS X, you have three different choices for dynamic libraries handling: 1) Use DYLD_LIBRARY_PATH environment variable in runtime; OR 2) Before the compilation, check the dynamic libraries and adjust their identification names, if necessary, to the absolute library path or to @rpath/. For example, the MySQL dynamic library may need that adjustment. You will have to use "adjust_name_tool" with -id option for that; OR 3) After the compilation, you can use the same tool, "adjust_name_tool", with option -change, to adjust the library paths values in the binary, where necessary. All library paths must be absolute paths or @rpath/... . See also the next section. NOTE: See "SQLite setup" and "PostgreSQL setup" and "MySQL setup" and "MongoDB setup" and "Redis setup" sections below for more database setup information. NOTE: If you do not install SQLite or PostgreSQL or MySQL or MongoDB or Redis, then you will be limited to the command-line options for user database. It will work great for development setup, but for real runtime systems you will need SQLite or PostgreSQL or MySQL or MongoDB or Redis. NOTE: To run PostgreSQL or MySQL or MongoDB or Redis server on the same system, you will also have to install a corresponding PostgreSQL or MySQL or MongoDB or Redis server package. The DB C development packages only provide development libraries, and client libraries only provide client access utilities and runtime libraries. The server packages may include everything - client, C development and server runtime. NOTE: OpenSSL to be installed before libevent2. When libevent2 is building, it is checking whether OpenSSL has been already installed, and which version of OpenSSL. If the OpenSSL is missed, or too old, then libevent_openssl library is not being created during the build, and you will not be able to compile the TURN Server with TLS support. NOTE: An older libevent version, version 1.x.x, is often included in some *NIX distributions. That version has its deficiencies and is inferior to the newer libevent2, especially in the performance department. This is why we are not providing backward compatibility with the older libevent 1.x version. If you have a system with older libevent, then you have to install the new libevent2 from their web site. It was tested with older *NIXes (like FreeBSD 6.x) and it works just fine. NOTE: SQLite must be of version 3.x. NOTE: For extra security features (like DTLS) support, OpenSSL version 1.0.0a or newer is recommended. Older versions do not support DTLS, reliably, in some cases. For example, the Debian 'Squeeze' Linux supplies 0.9.8 version of OpenSSL, that does not work correctly with DTLS over IPv6. If your system already has an older version of OpenSSL installed (usually in directory /usr) then you may want to install your newer OpenSSL "over" the old one (because it will most probably will not allow removal of the old one). When installing the newer OpenSSL, run the OpenSSL's configure command like this: $ ./config --prefix=/usr that will set the installation prefix to /usr (without "--prefix=/usr" by default it would be installed to /usr/local). This is necessary if you want to overwrite your existing older OpenSSL installation. IX. BUILDING WITH NON-DEFAULT PREFIX DIRECTORY Say, you have an older system with old openssl and old libevent library and you do not want to change that, but you still want to build the turnserver. Do the following steps: 1) Download new openssl from openssl.org. 2) Configure and build new openssl and install it into /opt: $ ./config --prefix=/opt $ make $ make install 3) Download the latest libevent2 from libevent.org, configure and install it into /opt: $ ./configure --prefix=/opt $ make $ make install 4) Change directory to coturn and build it: $ ./configure --prefix=/opt $ make After that, you can either use it locally, or install it into /opt. But remember that to run it, you have to adjust your LD_LIBRARY_PATH, like that: $ LD_LIBRARY_PATH=/opt/lib ./bin/turnserver An alternative would be adjusting the system-wide shared library search path by using $ ldconfig -n (Linux) $ ldconfig -m (BSD) $ crle -u -l (Solaris) X. TEST SCRIPTS First of all, you can use the test vectors from RFC 5769 to double-check that the STUN/TURN message encoding algorithms work properly. Run the utility: $ cd examples $ ./scripts/rfc5769.sh It will perform several protocol checks and print the results on the output. If anything has compiled wrongly (TURN Server, or OpenSSL libraries) then you will see some errors. Now, you can perform the TURN functionality test (bare minimum TURN example). If everything compiled properly, then the following programs must run together successfully, simulating TURN network routing in local loopback networking environment: Open two shell screens or consoles: In shell number 1, run TURN server application: $ cd examples $ ./scripts/basic/relay.sh In shell number 2, run test client application: $ cd examples $ ./scripts/basic/udp_c2c_client.sh If the client application produces output and in approximately 22 seconds prints the jitter, loss and round-trip-delay statistics, then everything is fine. There is another more complex test: In shell number 1, run TURN server application: $ cd examples $ ./scripts/basic/relay.sh In shell number 2, run "peer" application: $ cd examples $ ./scripts/peer.sh In shell number 3, run test client application: $ cd examples $ ./scripts/basic/udp_client.sh (or ./scripts/basic/tcp_client.sh) There is a similar set of examples/scripts/longtermsecure/* scripts for TURN environment with long-term authentication mechanism. This set of scripts is more complex, and checking the scripts options is useful for understanding how the TURN Server works: In shell number 1, run secure TURN server application: $ cd examples $ ./scripts/longtermsecure/secure_relay.sh In shell number 2, run "peer" application: $ cd examples $ ./scripts/peer.sh In shell number 3, run secure test client application: $ cd examples $ ./scripts/longtermsecure/secure_udp_client.sh (or ./scripts/longtermsecure/secure_tcp_client.sh) (or ./scripts/longtermsecure/secure_tls_client.sh) (or ./scripts/longtermsecure/secure_dtls_client.sh) (or ./scripts/longtermsecure/secure_sctp_client.sh) (or ./scripts/longtermsecure/secure_udp_c2c.sh for "peerless" client-to-client communications) The provided scripts are set for the local loopback communications, as an example and as a test environment. Real networking IPs must be used in real work environments. Try wireshark to check the communications between client, turnserver and the peer. Check the README.* files and the comments in the scripts relay.sh and secure_relay.sh as a guidance how to run the TURN server. XI. OS X compilation notes OS X usually has an older version of openssl supplied, with some Apple additions. The the "native" openssl will work, within its limitations, but the best option is to install a good fresh openssl development library, from http://www.openssl.org. You will have to handle the dynamic linking of the generated binaries, or use the static linking (see the section OPENSSL). XII. MS Windows and Cygwin support Currently, this project cannot be compiled under MS Windows. As the project is using fairly straightforward *NIX API, it is supported under Cygwin environment in MS Windows. One note for Cygwin users: we recommended libevent2 installation from the cygwin "ports" site: http://sourceware.org/cygwinports/ . You will have to install libevent2 runtime and libevent-devel packages. "Manual" libevent2 compilation and installation in Cygwin is not recommended and does not garantee a good outcome. XIII. CLIENT API LIBRARY. The compilation process will create lib/ sub-directory with libturnclient.a library. The header files for this library are located in include/turn/client/ sub-directory. The C++ wrapper for the messaging functionality is located in TurnMsgLib.h header. An example of C++ code can be found in stunclient.c file. This file is compiled as a C++ program if C++ compiler is used, and as a C program if C compiler is used. XIV. DOCS After installation, the man page turnserver(1) must be available. The man page is located in man/man1 subdirectory. If you want to see the man page without installation, run the command: $ man -M man turnserver HTML-formatted client library functions reference is located in docs/html subdirectory of the original archive tree. After the installation, it will be placed in PREFIX/share/doc/turnserver/html. XV. SQLite setup The site http://www.sqlite.org site has excellent extensive documentation. The default SQLite database location for the TURN Server is /usr/local/var/db/turndb or /var/db/turndb (depending on the platform). The database schema for the TURN server is very minimalistic and is located in project's turndb/schema.sql file, or in the system's PREFIX/share/turnserver/schema.sql file after the turnserver installation: If you would like to created a new fresh SQLite TURN database: $ sqlite3 < turndb/schema.sql The schema description: # Table for long-term credentials mechanism authorization: # CREATE TABLE turnusers_lt ( realm varchar(127) default '', name varchar(512), hmackey char(128), PRIMARY KEY (realm,name) ); The field hmackey contains HEX string representation of the key. We do not store the user open passwords for long-term credentials, for security reasons. Storing only the HMAC key has its own implications - if you change the realm, you will have to update the HMAC keys of all users, because the realm is used for the HMAC key generation. The key must be up to 32 characters (HEX representation of 16 bytes) for SHA1: # Table holding shared secrets for secret-based authorization # (REST API). Shared secret can be stored either in unsecure open # plain form, or in encrypted form (see turnadmin docs). # It can only be used together with the long-term # mechanism: # CREATE TABLE turn_secret ( realm varchar(127) default '', value varchar(127), primary key (realm,value) ); # Table holding "white" allowed peer IP ranges. # CREATE TABLE allowed_peer_ip ( realm varchar(127) default '', ip_range varchar(256), primary key (realm,ip_range) ); # Table holding "black" denied peer IP ranges. # CREATE TABLE denied_peer_ip ( realm varchar(127) default '', ip_range varchar(256), primary key (realm,ip_range) ); # Table to match origin to realm. # Multiple origins may have the same realm. # If no realm is found or the origin is absent # then the default realm is used. # CREATE TABLE turn_origin_to_realm ( origin varchar(127), realm varchar(127), primary key (origin,realm) ); # Realm options. # Valid options are 'max-bps', # 'total-quota' and 'user-quota'. # Values for them are integers (in text form). # CREATE TABLE turn_realm_option ( realm varchar(127) default '', opt varchar(32), value varchar(128), primary key (realm,opt) ); # oAuth key storage table. # CREATE TABLE oauth_key ( kid varchar(128), ikm_key varchar(256), timestamp bigint default 0, lifetime integer default 0, as_rs_alg varchar(64) default '', realm varchar(127) default '', primary key (kid) ); The oauth_key table fields meanings are: kid: the kid of the key; ikm_key - base64-encoded key ("input keying material"); timestamp - (optional) the timestamp (in seconds) when the key lifetime starts; lifetime - (optional) the key lifetime in seconds; the default value is 0 - unlimited lifetime. as_rs_alg - oAuth token encryption algorithm; the valid values are "A256GCM", "A128GCM" (see http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-5.1). The default value is "A256GCM"; realm - (optional) can be used to set the user realm (if the field is not empty). # Https access admin users. # Leave this table empty if you do not want # remote https access to the admin functions. # Web user password can be stored either in unsecure open # plain form, or in encrypted form (see turnadmin docs). # CREATE TABLE admin_user ( name varchar(32), realm varchar(127), password varchar(127), primary key (name) ); You can use turnadmin program to manage the database - you can either use turnadmin to add/modify/delete users, or you can use turnadmin to produce the hmac keys and modify the database with your favorite tools. When starting the turnserver, the --db parameter will be, for example: turnserver ... --db="/var/db/turndb" You will have to use the program turnadmin to fill the database, or you can do that manually with psql. Fill in users, for example: Shared secret for the TURN REST API (realm north.gov): $ bin/turnadmin -s logen -r north.gov -b "/var/db/turndb" Long-term credentials mechanism: $ bin/turnadmin -a -b "/var/db/turndb" -u gorst -r north.gov -p hero $ bin/turnadmin -a -b "/var/db/turndb" -u ninefingers -r north.gov -p youhavetoberealistic Admin users: $ bin/turnadmin -A -b "/var/db/turndb" -u gorst -p hero $ bin/turnadmin -A -b "/var/db/turndb" -u ninefingers -p youhavetoberealistic -r north.gov XVI. PostgreSQL setup The site http://www.postgresql.org site has excellent extensive documentation. For a quick-start guide, you can take a look into this page: http://www.freebsddiary.org/postgresql.php. That page is written for FreeBSD users, but it has lots of generic information applicable to other *NIXes, too. For the psql-userdb TURN server parameter, you can either set a PostgreSQL connection string, or a PostgreSQL URI, see the link: For 8.4 PostgreSQL version: http://www.postgresql.org/docs/8.4/static/libpq-connect.html For newer 9.x versions: http://www.postgresql.org/docs/9.2/static/libpq-connect.html#LIBPQ-CONNSTRING. In the PostgreSQL connection string or URI, you can set the host, the access port, the database name, the user, and the user password (if the access is secured). Numerous other parameters can be set, see the links above. The TURN server will blindly use that connection string without any modifications. You are responsible for the right connection string format. Below are the steps to setup the PostgreSQL database server from scratch: 1) Install PostgreSQL server. After the installation, do not forget to initialize the postgres root database directory: $ sudo bash $ su -l pgsql $ initdb -D /usr/local/pgsql/data 2) Find and edit Postgres' pg_hba.conf file to set the access options (see docs). On different systems, it may be located in different places. Set the lines for local access as "trust" for now (you can change it later), for TCP/IP access set the value as "md5". To set TCP/IP access from any host, use "0.0.0.0/0" for IPv4, and "::/0" for IPv6. 3) Edit postgresql.conf file to allow TCP/IP access - uncomment and edit the "listen_addresses" option (see docs). On different systems, this file may be located in different places. 4) Restart your system or restart the postgresql server, for example: $ sudo /etc/init.d/postgresql stop $ sudo /etc/init.d/postgresql start The scripts may also be in /usr/local/etc/init.d, or in /etc/rc.d/, or in /usr/local/etc/rc.d/ . 5) Check /etc/passwd file to find out which user account is used for the PostgreSQL admin access on your system (it may be "pgsql", or "postgres", or "postgresql"). Let's assume that this is "postgres" account. 6) Create a database for the TURN purposes, with name, say, "turn": $ createdb -U postgres coturn 7) Create a user for the TURN with name, say, "turn": $ psql -U postgres coturn turn=# create user turn with password 'turn'; turn=# Ctrl-D 8) Create the TURN users database schema. The database schema for the TURN server is very minimalistic and is located in project's turndb/schema.sql file, or in the system's PREFIX/share/turnserver/schema.sql file after the turnserver installation: $ cat turndb/schema.sql | psql -U turn -d coturn NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "turnusers_lt_pkey" for table "turnusers_lt" CREATE TABLE CREATE TABLE See the SQLite section for the detailed database schema explanation. To fill the database with test data: cat turndb/testsqldbsetup.sql | psql -U turn -d coturn You can use turnadmin program to manage the database - you can either use turnadmin to add/modify/delete users, or you can use turnadmin to produce the hmac keys and modify the database with your favorite tools. More examples of database schema creation: psql -h -U -d < turndb/schema.sql (old style for 8.4) psql postgresql://username:password@/databasename < turndb/schema.sql (newer style for 9.x, UNIX domain local sockets) Or: psql postgresql://username:password@hostname:port/databasename < turndb/schema.sql (newer style for 9.x, TCP/IP access) Below, the string "postgresql://turn:turn@/turn" is the connection URI. Of course, the administrators can play with the connection string as they want. When starting the turnserver, the psql-userdb parameter will be, for example: turnserver ... --psql-userdb="host=localhost dbname=coturn user=turn password=turn connect_timeout=30" Or, for 9.x PostgreSQL versions: turnserver ... --psql-userdb=postgresql://username:password@/databasename ... 9) You are ready to use the TURN database. The database name is "turn", the user name is "turn", the user password is "turn". Of course, you can choose your own names. Now, you will have to use the program turnadmin to fill the database, or you can do that manually with psql. Fill in users, for example: Shared secret for the TURN REST API (realm north.gov): $ bin/turnadmin -s logen -r north.gov -e "host=localhost dbname=coturn user=turn password=turn" Long-term credentials mechanism: $ bin/turnadmin -a -e "host=localhost dbname=coturn user=turn password=turn" -u gorst -r north.gov -p hero $ bin/turnadmin -a -e "host=localhost dbname=coturn user=turn password=turn" -u ninefingers -r north.gov -p youhavetoberealistic Admin users: $ bin/turnadmin -A -e "host=localhost dbname=coturn user=turn password=turn" -u gorst -p hero $ bin/turnadmin -A -e "host=localhost dbname=coturn user=turn password=turn" -u ninefingers -p youhavetoberealistic -r north.gov XVII. MySQL (MariaDB) setup The MySQL setup is similar to PostgreSQL (the same idea), and is well documented on their site http://www.mysql.org. The TURN Server database schema is the same as for PostgreSQL and you can find it in turndb/schema.sql file, or in the system's PREFIX/share/turnserver/schema.sql file after the turnserver installation. The general setup is similar to PostgreSQL setup procedure: 1) Check that the mysql server access is OK. Immediately after the MySQL server installation, it must be accessible, at the very minimum, at the localhost with the root account. 2) Login into mysql console from root account: $ sudo bash # mysql mysql (or mysql -p mysql if mysql account password set) 3) Add 'turn' user with 'turn' password (for example): > create user 'turn'@'localhost' identified by 'turn'; 4) Create database 'coturn' (for example) and grant privileges to user 'turn': > create database coturn character set latin1; > grant all on coturn.* to 'turn'@'localhost'; > flush privileges; Ctrl-D 5) Create database schema: $ mysql -p -u turn coturn < turndb/schema.sql Enter password: turn $ Fill in test database data, if this is a test database (not a production database): $ mysql -p -u turn coturn < turndb/testsqldbsetup.sql 6) Fill in users, for example: Shared secret for the TURN REST API (realm north.gov): $ bin/turnadmin -s logen -r north.gov -M "host=localhost dbname=coturn user=turn password=turn" Long-term credentials mechanism: $ bin/turnadmin -a -M "host=localhost dbname=coturn user=turn password=turn" -u gorst -r north.gov -p hero $ bin/turnadmin -a -M "host=localhost dbname=coturn user=turn password=turn" -u ninefingers -r north.gov -p youhavetoberealistic Admin users: $ bin/turnadmin -A -M "host=localhost dbname=coturn user=turn password=turn" -u gorst -p hero $ bin/turnadmin -A -M "host=localhost dbname=coturn user=turn password=turn" -u ninefingers -p youhavetoberealistic -r north.gov 7) Now we can use mysql in the turnserver. If the TURN server was compiled with MySQL support, then we can use the TURN server database parameter --mysql-userdb. The value of this parameter is a connection string for the MySQL database. As "native" MySQL does not have such a feature as "connection string", the TURN server parses the connection string and converts it into MySQL database connection parameter. The format of the MySQL connection string is: "host= dbname= user= password= port= connect_timeout= read_timeout=" (all parameters are optional) So, an example of the MySQL database parameter in the TURN server command line would be: --mysql-userdb="host=localhost dbname=coturn user=turn password=turn connect_timeout=30 read_timeout=30" Or in the turnserver.conf file: mysql-userdb="host=localhost dbname=coturn user=turn password=turn connect_timeout=30 read_timeout=30" If you have to use a secure MySQL connection (SSL) then you have to use also the optional connection string parameters for the secure communications: ca, capath, cert, key, cipher (see http://dev.mysql.com/doc/refman/5.1/en/ssl-options.html for the command options description). XVIII. MongoDB setup The MongoDB setup is well documented on their site http://docs.mongodb.org/manual/. Note: if your system has a "standard" plain vanilla UNIX "make" utility (that is not a GNU make) then you will have to use the GNU make to compile the Mongo driver, because the Mongo compilation process was written with the "proprietary" GNU extensions. For example, in FreeBSD in will have to use "gmake" command. If the TURN server was compiled with MongoDB support (mongo-c-driver is the C client library for MongoDB), then we can use the TURN server database parameter --mongo-userdb. The value of this parameter is a connection string for the MongoDB database. The format of the connection string is described at http://hergert.me/docs/mongo-c-driver/mongoc_uri.html: "mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]" So, an example of the MongoDB database parameter in the TURN server command line would be: --mongo-userdb="mongodb://localhost:27017/coturn" Or in the turnserver.conf file: mongo-userdb="mongodb://localhost:27017/coturn" The meanings of the MongoDB keys are the same as for the other databases, see the explanations for the Postgres, for example. See the file testmongosetup.sh for the database structure examples. XIX. Redis setup The Redis setup is well documented on their site http://redis.io. The TURN Server Redis database schema description can be found in schema.userdb.redis and schema.stats.redis files. Those files are located either in the turndb subdirectory of the main source code directory, or in /usr/local/share/turnserver/ after the installation, or somewhere in /usr/share/ directory, depending on the OS and on the instalation package. If the TURN server was compiled with Hiredis support (Hiredis is the C client library for Redis), then we can use the TURN server database parameter --redis-userdb. The value of this parameter is a connection string for the Redis database. As "native" Redis does not have such a feature as "connection string", the TURN server parses the connection string and converts it into Redis database access parameter. The format of the Redis connection string is: "ip= dbname= password= port= connect_timeout=" (all parameters are optional) So, an example of the Redis database parameter in the TURN server command line would be: --redis-userdb="ip=127.0.0.1 dbname=2 password=turn connect_timeout=30" Or in the turnserver.conf file: redis-userdb="ip=127.0.0.1 dbname=2 password=turn connect_timeout=30" Redis can be also used for the TURN allocation status check and for status and traffic notifications. See the explanation in the turndb/schema.stats.redis file, and an example in turndb/testredisdbsetup.sh file. One special thing about TURN Redis security setup is that you can store open passwords for long-term credentials in Redis. You cannot set open passwords for long-term credentials in SQLite or MySQL or PostgreSQL - with those DBs, you have to use the keys only. With Redis, you have a choice - keys or open passwords. You also have to take care about Redis connection parameters, the timeout and the keepalive. The following settings must be in your Redis config file (/etc/redis.conf or /usr/local/etc/redis.conf): .......... timeout 0 .......... tcp-keepalive 60 .......... Redis TURN admin commands: Shared secret for the TURN REST API (realm north.gov): $ bin/turnadmin -s logen -r north.gov -N "host=localhost dbname=2 user=turn password=turn" Long-term credentials mechanism: $ bin/turnadmin -a -N "host=localhost dbname=2 user=turn password=turn" -u gorst -r north.gov -p hero $ bin/turnadmin -a -N "host=localhost dbname=2 user=turn password=turn" -u ninefingers -r north.gov -p youhavetoberealistic Admin users: $ bin/turnadmin -A -N "host=localhost dbname=2 user=turn password=turn" -u gorst -p hero $ bin/turnadmin -A -N "host=localhost dbname=2 user=turn password=turn" -u ninefingers -p youhavetoberealistic -r north.gov See the file testredisdbsetup.sh for the data structure examples. XX. Performance tuning This topic is covered in the wiki page: https://github.com/coturn/coturn/wiki/TURN-Performance-and-Load-Balance XXI. TURN Server setup Read the project wiki pages: https://github.com/coturn/coturn/wiki Also, check the project from page links to the TURN/WebRTC configuration examples. It may give you an idea how it can be done. XXII. HTTPS Management Interface The turnserver process provides an HTTPS Web access as statistics and basic management interface. The turnserver listens to incoming HTTPS admin connections on the same ports as the main TURN/STUN listener. The Web admin pages are basic and self-explanatory. To make the HTTPS interface active, the database table admin_user must be populated with the admin user account(s). An admin user can be a superuser (if not assigned to a particular realm) or a restricted user (if assigned to a realm). The restricted admin users can perform only limited actions, within their corresponding realms. XXIII. Telnet CLI management interface You have a telnet interface (enabled by default) to access the turnserver process, to view its state, to gather some statistical information, and to make some changes on-the-fly. You can access that CLI interface with telnet or putty program (in telnet mode). The process by default listens to port 5766 on IP address 127.0.0.1 for the telnet connections. WARNING: all telnet communications are going unencrypted over the network. For security reasons, we advise using the loopback IP addresses for CLI (127.0.0.1 or ::1). The CLI may have a password configured, but that password is transferred over the network unencrypted, too. So sticking to the local system CLI access, and accessing the turnserver system terminal with ssh only, would be a wise decision. XXIV. ALPN support. Starting with version 4.3.2.1, the TURN server supports the ALPN STUN specifications (http://tools.ietf.org/html/draft-ietf-tram-alpn-08). If the ALPN functionality is needed, then OpenSSL version 1.0.2 or newer has to be used. See OPENSSL section for the OpenSSL upgrade hints. XXV. SCTP support Starting with version 4.4.3.1, the TURN server supports 'native' SCTP. On the client side, the TURN server, additionally, supports SCTP and TLS-over-SCTP. The relay side is not changing - the relay communications will still be UDP or TCP. turnserver-4.5.1.1/NOTE0000664000175000017500000000010513436577655013276 0ustar misimisiThis project is active in Github: https://github.com/coturn/coturn/ turnserver-4.5.1.1/Makefile.in0000775000175000017500000002525513436577655014673 0ustar misimisi LIBEVENT_INCLUDE = -I${PREFIX}/include/ -I/usr/local/include/ INCFLAGS = -Isrc -Isrc/apps/common -Isrc/server -Isrc/client -Isrc/client++ ${LIBEVENT_INCLUDE} CFLAGS += ${INCFLAGS} MAKE_DEPS = Makefile LIBCLIENTTURN_HEADERS = src/ns_turn_defs.h src/client++/TurnMsgLib.h src/client/ns_turn_ioaddr.h src/client/ns_turn_msg.h src/client/ns_turn_msg_defs.h src/client/ns_turn_msg_defs_experimental.h src/client/ns_turn_msg_addr.h LIBCLIENTTURN_MODS = src/client/ns_turn_ioaddr.c src/client/ns_turn_msg_addr.c src/client/ns_turn_msg.c LIBCLIENTTURN_DEPS = ${LIBCLIENTTURN_HEADERS} ${MAKE_DEPS} LIBCLIENTTURN_OBJS = build/obj/ns_turn_ioaddr.o build/obj/ns_turn_msg_addr.o build/obj/ns_turn_msg.o SERVERTURN_HEADERS = src/server/ns_turn_allocation.h src/server/ns_turn_ioalib.h src/server/ns_turn_khash.h src/server/ns_turn_maps_rtcp.h src/server/ns_turn_maps.h src/server/ns_turn_server.h src/server/ns_turn_session.h SERVERTURN_DEPS = ${LIBCLIENTTURN_HEADERS} ${SERVERTURN_HEADERS} ${MAKE_DEPS} SERVERTURN_MODS = ${LIBCLIENTTURN_MODS} src/server/ns_turn_allocation.c src/server/ns_turn_maps_rtcp.c src/server/ns_turn_maps.c src/server/ns_turn_server.c COMMON_HEADERS = src/apps/common/apputils.h src/apps/common/ns_turn_openssl.h src/apps/common/ns_turn_utils.h src/apps/common/stun_buffer.h COMMON_MODS = src/apps/common/apputils.c src/apps/common/ns_turn_utils.c src/apps/common/stun_buffer.c COMMON_DEPS = ${LIBCLIENTTURN_DEPS} ${COMMON_MODS} ${COMMON_HEADERS} IMPL_HEADERS = src/apps/relay/ns_ioalib_impl.h src/apps/relay/ns_sm.h src/apps/relay/turn_ports.h IMPL_MODS = src/apps/relay/ns_ioalib_engine_impl.c src/apps/relay/turn_ports.c src/apps/relay/http_server.c IMPL_DEPS = ${COMMON_DEPS} ${IMPL_HEADERS} ${IMPL_MODS} HIREDIS_HEADERS = src/apps/common/hiredis_libevent2.h HIREDIS_MODS = src/apps/common/hiredis_libevent2.c USERDB_HEADERS = src/apps/relay/dbdrivers/dbdriver.h src/apps/relay/dbdrivers/dbd_sqlite.h src/apps/relay/dbdrivers/dbd_pgsql.h src/apps/relay/dbdrivers/dbd_mysql.h src/apps/relay/dbdrivers/dbd_mongo.h src/apps/relay/dbdrivers/dbd_redis.h USERDB_MODS = src/apps/relay/dbdrivers/dbdriver.c src/apps/relay/dbdrivers/dbd_sqlite.c src/apps/relay/dbdrivers/dbd_pgsql.c src/apps/relay/dbdrivers/dbd_mysql.c src/apps/relay/dbdrivers/dbd_mongo.c src/apps/relay/dbdrivers/dbd_redis.c SERVERAPP_HEADERS = src/apps/relay/userdb.h src/apps/relay/tls_listener.h src/apps/relay/mainrelay.h src/apps/relay/turn_admin_server.h src/apps/relay/dtls_listener.h src/apps/relay/libtelnet.h ${HIREDIS_HEADERS} ${USERDB_HEADERS} SERVERAPP_MODS = src/apps/relay/mainrelay.c src/apps/relay/netengine.c src/apps/relay/libtelnet.c src/apps/relay/turn_admin_server.c src/apps/relay/userdb.c src/apps/relay/tls_listener.c src/apps/relay/dtls_listener.c ${HIREDIS_MODS} ${USERDB_MODS} SERVERAPP_DEPS = ${SERVERTURN_MODS} ${SERVERTURN_DEPS} ${SERVERAPP_MODS} ${SERVERAPP_HEADERS} ${COMMON_DEPS} ${IMPL_DEPS} lib/libturnclient.a TURN_BUILD_RESULTS = bin/turnutils_oauth bin/turnutils_natdiscovery bin/turnutils_stunclient bin/turnutils_rfc5769check bin/turnutils_uclient bin/turnserver bin/turnutils_peer lib/libturnclient.a include/turn/ns_turn_defs.h sqlite_empty_db .PHONY: all test check clean distclean sqlite_empty_db install deinstall uninstall reinstall all: ${TURN_BUILD_RESULTS} test: check check: bin/turnutils_rfc5769check bin/turnutils_rfc5769check include/turn/ns_turn_defs.h: src/ns_turn_defs.h ${RMCMD} include ${MKBUILDDIR} include/turn/client cp -pf src/client/*.h include/turn/client/ cp -pf src/client++/*.h include/turn/client/ cp -pf src/ns_turn_defs.h include/turn/ bin/turnutils_uclient: ${COMMON_DEPS} src/apps/uclient/session.h lib/libturnclient.a src/apps/uclient/mainuclient.c src/apps/uclient/uclient.c src/apps/uclient/uclient.h src/apps/uclient/startuclient.c src/apps/uclient/startuclient.h ${MKBUILDDIR} bin ${CC} ${CPPFLAGS} ${CFLAGS} src/apps/uclient/uclient.c src/apps/uclient/startuclient.c src/apps/uclient/mainuclient.c ${COMMON_MODS} -o $@ -Llib -lturnclient -Llib ${LDFLAGS} bin/turnutils_natdiscovery: ${COMMON_DEPS} lib/libturnclient.a src/apps/natdiscovery/natdiscovery.c pwd ${MKBUILDDIR} bin ${CC} ${CPPFLAGS} ${CFLAGS} src/apps/natdiscovery/natdiscovery.c ${COMMON_MODS} -o $@ -Llib -lturnclient -Llib ${LDFLAGS} bin/turnutils_oauth: ${COMMON_DEPS} lib/libturnclient.a src/apps/oauth/oauth.c pwd ${MKBUILDDIR} bin ${CC} ${CPPFLAGS} ${CFLAGS} src/apps/oauth/oauth.c ${COMMON_MODS} -o $@ -Llib -lturnclient -Llib ${LDFLAGS} bin/turnutils_stunclient: ${COMMON_DEPS} lib/libturnclient.a src/apps/stunclient/stunclient.c pwd ${MKBUILDDIR} bin ${CC} ${CPPFLAGS} ${CFLAGS} src/apps/stunclient/stunclient.c ${COMMON_MODS} -o $@ -Llib -lturnclient -Llib ${LDFLAGS} bin/turnutils_rfc5769check: ${COMMON_DEPS} lib/libturnclient.a src/apps/rfc5769/rfc5769check.c pwd ${MKBUILDDIR} bin ${CC} ${CPPFLAGS} ${CFLAGS} src/apps/rfc5769/rfc5769check.c ${COMMON_MODS} -o $@ -Llib -lturnclient -Llib ${LDFLAGS} bin/turnserver: ${SERVERAPP_DEPS} ${MKBUILDDIR} bin ${RMCMD} bin/turnadmin ${CC} ${CPPFLAGS} ${CFLAGS} ${DBCFLAGS} ${IMPL_MODS} -Ilib ${SERVERAPP_MODS} ${COMMON_MODS} ${SERVERTURN_MODS} -o $@ ${DBLIBS} ${LDFLAGS} cd bin; ln -s turnserver turnadmin bin/turnutils_peer: ${COMMON_DEPS} ${LIBCLIENTTURN_MODS} ${LIBCLIENTTURN_DEPS} lib/libturnclient.a src/apps/peer/mainudpserver.c src/apps/peer/udpserver.h src/apps/peer/udpserver.c ${MKBUILDDIR} bin ${CC} ${CPPFLAGS} ${CFLAGS} src/apps/peer/mainudpserver.c src/apps/peer/udpserver.c ${COMMON_MODS} -o $@ -Llib -lturnclient -Llib ${LDFLAGS} ### Client Library: lib/libturnclient.a: ${LIBCLIENTTURN_OBJS} ${LIBCLIENTTURN_DEPS} ${MKBUILDDIR} lib ${ARCHIVERCMD} $@ ${LIBCLIENTTURN_OBJS} build/obj/ns_turn_ioaddr.o: src/client/ns_turn_ioaddr.c ${LIBCLIENTTURN_DEPS} ${MKBUILDDIR} build/obj ${CC} ${CPPFLAGS} ${CFLAGS} -c src/client/ns_turn_ioaddr.c -o $@ build/obj/ns_turn_msg_addr.o: src/client/ns_turn_msg_addr.c ${LIBCLIENTTURN_DEPS} ${MKBUILDDIR} build/obj ${CC} ${CPPFLAGS} ${CFLAGS} -c src/client/ns_turn_msg_addr.c -o $@ build/obj/ns_turn_msg.o: src/client/ns_turn_msg.c ${LIBCLIENTTURN_DEPS} ${MKBUILDDIR} build/obj ${CC} ${CPPFLAGS} ${CFLAGS} -c src/client/ns_turn_msg.c -o $@ ### Clean all: clean: ${RMCMD} bin build lib obj *bak *~ */*~ */*/*~ */*/*/*~ *core */*core */*/*core include tmp sqlite distclean: clean ${RMCMD} Makefile ### SQLite empty database: sqlite_empty_db : sqlite/turndb sqlite/turndb : turndb/schema.sql ${MKDIR} sqlite ${RMCMD} sqlite/turndb ${SQLITE_CMD} sqlite/turndb < turndb/schema.sql ### Install all: install: all ${MAKE_DEPS} ${MKDIR} ${DESTDIR}${PREFIX} ${MKDIR} ${DESTDIR}${BINDIR} ${MKDIR} ${DESTDIR}${TURNDBDIR} ${MKDIR} ${DESTDIR}${MANPREFIX}/man/man1 ${MKDIR} ${DESTDIR}${CONFDIR} ${MKDIR} ${DESTDIR}${LIBDIR} ${MKDIR} ${DESTDIR}${EXAMPLESDIR} ${MKDIR} ${DESTDIR}${DOCSDIR} ${MKDIR} ${DESTDIR}${SCHEMADIR} ${MKDIR} ${DESTDIR}${TURNINCLUDEDIR} ${INSTALL_PROGRAM} bin/turnserver ${DESTDIR}${BINDIR} ${INSTALL_PROGRAM} bin/turnadmin ${DESTDIR}${BINDIR} ${INSTALL_PROGRAM} bin/turnutils_uclient ${DESTDIR}${BINDIR} ${INSTALL_PROGRAM} bin/turnutils_peer ${DESTDIR}${BINDIR} ${INSTALL_PROGRAM} bin/turnutils_stunclient ${DESTDIR}${BINDIR} ${INSTALL_PROGRAM} bin/turnutils_oauth ${DESTDIR}${BINDIR} ${INSTALL_PROGRAM} bin/turnutils_natdiscovery ${DESTDIR}${BINDIR} ${INSTALL_MAN} man/man1/turnserver.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_MAN} man/man1/turnadmin.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_MAN} man/man1/turnutils.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_MAN} man/man1/turnutils_uclient.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_MAN} man/man1/turnutils_stunclient.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_MAN} man/man1/turnutils_oauth.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_MAN} man/man1/turnutils_natdiscovery.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_MAN} man/man1/turnutils_peer.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_MAN} man/man1/coturn.1 ${DESTDIR}${MANPREFIX}/man/man1/ ${INSTALL_STATIC_LIB} lib/libturnclient.a ${DESTDIR}${LIBDIR} ${INSTALL_DATA} LICENSE ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} README.turnserver ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} README.turnadmin ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} README.turnutils ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} INSTALL ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} postinstall.txt ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} turndb/schema.sql ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} turndb/schema.sql ${DESTDIR}${SCHEMADIR} ${INSTALL_DATA} turndb/schema.mongo.sh ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} turndb/schema.mongo.sh ${DESTDIR}${SCHEMADIR} ${INSTALL_DATA} turndb/testredisdbsetup.sh ${DESTDIR}${SCHEMADIR} ${INSTALL_DATA} turndb/testmongosetup.sh ${DESTDIR}${SCHEMADIR} ${INSTALL_DATA} turndb/testsqldbsetup.sql ${DESTDIR}${SCHEMADIR} ${INSTALL_DATA} turndb/schema.userdb.redis ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} turndb/schema.userdb.redis ${DESTDIR}${SCHEMADIR} ${INSTALL_DATA} turndb/schema.stats.redis ${DESTDIR}${DOCSDIR} ${INSTALL_DATA} turndb/schema.stats.redis ${DESTDIR}${SCHEMADIR} if [ -f sqlite/turndb ] ; then ${INSTALL_DATA} sqlite/turndb ${DESTDIR}${TURNDBDIR}/turndb; fi ${INSTALL_DATA} examples/etc/turnserver.conf ${DESTDIR}${CONFDIR}/turnserver.conf.default ${INSTALL_DIR} examples/etc ${DESTDIR}${EXAMPLESDIR} ${INSTALL_DIR} examples/scripts ${DESTDIR}${EXAMPLESDIR} ${RMCMD} ${DESTDIR}${EXAMPLESDIR}/scripts/rfc5769.sh ${INSTALL_DIR} include/turn/client ${DESTDIR}${TURNINCLUDEDIR} ${INSTALL_DATA} include/turn/ns_turn_defs.h ${DESTDIR}${TURNINCLUDEDIR} ${MORECMD} ${DESTDIR}${DOCSDIR}/postinstall.txt deinstall: ${MAKE_DEPS} ${PKILL_PROGRAM} turnserver || ${ECHO_CMD} OK ${RMCMD} ${DESTDIR}${TURNDBDIR}/turndb ${RMCMD} ${DESTDIR}${DOCSDIR} ${RMCMD} ${DESTDIR}${SCHEMADIR} ${RMCMD} ${DESTDIR}${BINDIR}/turnserver ${RMCMD} ${DESTDIR}${BINDIR}/turnadmin ${RMCMD} ${DESTDIR}${BINDIR}/turnutils_peer ${RMCMD} ${DESTDIR}${BINDIR}/turnutils_uclient ${RMCMD} ${DESTDIR}${BINDIR}/turnutils_stunclient ${RMCMD} ${DESTDIR}${BINDIR}/turnutils_oauth ${RMCMD} ${DESTDIR}${BINDIR}/turnutils_natdiscovery ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/turnserver.1 ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/turnadmin.1 ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/turnutils.1 ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/turnutils_uclient.1 ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/turnutils_stunclient.1 ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/turnutils_oauth.1 ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/turnutils_natdiscovery.1 ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/turnutils_peer.1 ${RMCMD} ${DESTDIR}${MANPREFIX}/man/man1/coturn.1 ${RMCMD} ${DESTDIR}${LIBDIR}/libturnclient.a ${RMCMD} ${DESTDIR}${EXAMPLESDIR} ${RMCMD} ${DESTDIR}${CONFDIR}/turnserver.conf.default ${RMCMD} ${DESTDIR}${TURNINCLUDEDIR} uninstall: deinstall reinstall: deinstall install turnserver-4.5.1.1/configure0000775000175000017500000010040213436577655014516 0ustar misimisi#!/bin/sh # Proprietary configure script of Coturn project cleanup() { rm -rf ${TMPCPROGC} rm -rf ${TMPCPROGB} rm -rf ${TH_TMPCPROGC} rm -rf ${TH_TMPCPROGB} rm -rf ${GCM_TMPCPROGC} rm -rf ${GCM_TMPCPROGB} rm -rf ${PQ_TMPCPROGC} rm -rf ${PQ_TMPCPROGB} rm -rf ${MYSQL_TMPCPROGC} rm -rf ${MYSQL_TMPCPROGB} rm -rf ${MONGO_TMPCPROGC} rm -rf ${MONGO_TMPCPROGB} rm -rf ${D_TMPCPROGC} rm -rf ${D_TMPCPROGB} rm -rf ${SQL_TMPCPROGC} rm -rf ${SQL_TMPCPROGO} rm -rf ${E_TMPCPROGC} rm -rf ${E_TMPCPROGO} rm -rf ${HR_TMPCPROGC} rm -rf ${HR_TMPCPROGB} rm -rf ${TMPCADDRPROGO} } testlibraw() { ${CC} ${TMPCPROGC} -o ${TMPCPROGB} ${OSCFLAGS} ${OSLIBS} -${1} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Library option -${1} cannot be used" return 0 else OSLIBS="${OSLIBS} -${1}" return 1 fi } testsqlite_comp() { SQLITE_LIBS=-lsqlite3 ${CC} -c ${SQL_TMPCPROGC} -o ${SQL_TMPCPROGO} ${OSCFLAGS} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "SQLite development is not installed properly" return 0 else DBLIBS="${DBLIBS} ${SQLITE_LIBS}" return 1 fi } testlibevent2_comp() { ${CC} -c ${E_TMPCPROGC} -o ${E_TMPCPROGO} ${OSCFLAGS} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Libevent2 development is not installed properly" return 0 else return 1 fi } testhiredis() { for inc in ${INCLUDEDIR}/hiredis /usr/local/include/hiredis /usr/hiredis /usr/include/hiredis do if [ -d ${inc} ] ; then HIREDISCFLAGS="${HIREDISCFLAGS} -I${inc}" fi done HIREDISLIBS=-lhiredis ${CC} ${HR_TMPCPROGC} -o ${HR_TMPCPROGB} ${OSCFLAGS} ${DBLIBS} ${HIREDISCFLAGS} ${HIREDISLIBS} ${OSLIBS} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} ${ECHO_CMD} "HIREDIS DEVELOPMENT LIBRARY (libhiredis.*) AND/OR HEADERS (hiredis/*.h)" ${ECHO_CMD} " ARE NOT INSTALLED PROPERLY ON THIS SYSTEM." ${ECHO_CMD} " THAT'S OK BUT THE TURN SERVER IS BUILDING WITHOUT REDIS SUPPORT." ${ECHO_CMD} return 0 else DBCFLAGS="${DBCFLAGS} ${HIREDISCFLAGS}" DBLIBS="${DBLIBS} ${HIREDISLIBS}" return 1 fi } testlibpq() { POSTCFLAGS="-I${PREFIX}/pgsql/include -I${PREFIX}/include/pgsql/ -I${PREFIX}/include/postgres/ -I${PREFIX}/postgres/include/ -I${PREFIX}/include/postgresql/ -I${PREFIX}/postgresql/include/" POSTCFLAGS="${POSTCFLAGS} -I/usr/local/pgsql/include -I/usr/local/include/pgsql/ -I/usr/local/include/postgres/ -I/usr/local/postgres/include/ -I/usr/local/include/postgresql/ -I/usr/local/postgresql/include/" POSTCFLAGS="${POSTCFLAGS} -I/usr/pgsql/include -I/usr/include/pgsql/ -I/usr/include/postgres/ -I/usr/postgres/include/ -I/usr/include/postgresql/ -I/usr/postgresql/include/" for ilib in ${PREFIX}/pgsql/lib ${PREFIX}/lib/pgsql ${PREFIX}/lib64/pgsql /usr/local/pgsql/lib /usr/local/lib/pgsql /usr/local/lib64/pgsql /usr/pgsql/lib /usr/lib/pgsql /usr/lib64/pgsql ${PREFIX}/postgres/lib ${PREFIX}/lib/postgres ${PREFIX}/lib64/postgres /usr/local/postgres/lib /usr/local/lib/postgres /usr/local/lib64/postgres /usr/postgres/lib /usr/lib/postgres /usr/lib64/postgres ${PREFIX}/postgresql/lib ${PREFIX}/lib/postgresql ${PREFIX}/lib64/postgresql /usr/local/postgresql/lib /usr/local/lib/postgresql /usr/local/lib64/postgresql /usr/postgresql/lib /usr/lib/postgresql /usr/lib64/postgresql do if [ -d ${ilib} ] ; then POSTLIBS="${POSTLIBS} -L${ilib}" if ! [ -z "${TURN_ACCEPT_RPATH}" ] ; then TURN_RPATH="${TURN_RPATH} -Wl,-rpath,${ilib}" fi fi done POSTLIBS="${OSLIBS} ${POSTLIBS} -lpq" ${CC} ${PQ_TMPCPROGC} -o ${PQ_TMPCPROGB} ${OSCFLAGS} ${DBCFLAGS} ${POSTCFLAGS} ${DBLIBS} ${POSTLIBS} ${OSLIBS} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} ${ECHO_CMD} "POSTGRESQL DEVELOPMENT LIBRARY (libpq.a) AND/OR HEADER (libpq-fe.h)" ${ECHO_CMD} " ARE NOT INSTALLED PROPERLY ON THIS SYSTEM." ${ECHO_CMD} " THAT'S OK BUT THE TURN SERVER IS BUILDING WITHOUT POSTGRESQL DATABASE SUPPORT." ${ECHO_CMD} return 0 else DBCFLAGS="${DBCFLAGS} ${POSTCFLAGS}" DBLIBS="${DBLIBS} ${POSTLIBS}" return 1 fi } testlibmysql() { MYSQL_CFLAGS="-I${PREFIX}/mysql/include -I${PREFIX}/include/mysql/" MYSQL_CFLAGS="${MYSQL_CFLAGS} -I/usr/local/mysql/include -I/usr/local/include/mysql/" MYSQL_CFLAGS="${MYSQL_CFLAGS} -I/usr/mysql/include -I/usr/include/mysql/" for ilib in ${PREFIX}/mysql/lib ${PREFIX}/lib/mysql ${PREFIX}/lib64/mysql /usr/local/mysql/lib /usr/local/lib/mysql /usr/local/lib64/mysql /usr/mysql/lib /usr/lib/mysql /usr/lib64/mysql do if [ -d ${ilib} ] ; then MYSQL_LIBS="${MYSQL_LIBS} -L${ilib}" if ! [ -z "${TURN_ACCEPT_RPATH}" ] ; then TURN_RPATH="${TURN_RPATH} -Wl,-rpath,${ilib}" fi fi done MYSQL_LIBS="${OSLIBS} ${MYSQL_LIBS} -lmysqlclient" ${CC} ${MYSQL_TMPCPROGC} -o ${MYSQL_TMPCPROGB} ${OSCFLAGS} ${DBCFLAGS} ${DBLIBS} ${MYSQL_CFLAGS} ${MYSQL_LIBS} ${OSLIBS} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} ${ECHO_CMD} "MYSQL DEVELOPMENT LIBRARY (libmysqlclient) AND/OR HEADER (mysql.h)" ${ECHO_CMD} " ARE NOT INSTALLED PROPERLY ON THIS SYSTEM." ${ECHO_CMD} " THAT'S OK BUT THE TURN SERVER IS BUILDING WITHOUT MYSQL DATABASE SUPPORT." ${ECHO_CMD} return 0 else DBCFLAGS="${DBCFLAGS} ${MYSQL_CFLAGS}" DBLIBS="${DBLIBS} ${MYSQL_LIBS}" return 1 fi } testlibmongoc() { for inc in ${INCLUDEDIR}/libmongoc-1.0 ${INCLUDEDIR}/libbson-1.0 /usr/local/include/libmongoc-1.0 /usr/local/include/libbson-1.0 /usr/libmongoc-1.0 /usr/libbson-1.0 /usr/include/libbson-1.0/ /usr/include/libmongoc-1.0/ do if [ -d ${inc} ] ; then MONGO_CFLAGS="${MONGO_CFLAGS} -I${inc}" fi done MONGO_LIBS="-lmongoc-1.0 -lbson-1.0" ${CC} ${MONGO_TMPCPROGC} -o ${MONGO_TMPCPROGB} ${OSCFLAGS} ${DBCFLAGS} ${DBLIBS} ${MONGO_CFLAGS} ${MONGO_LIBS} ${OSLIBS} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} ${ECHO_CMD} "MONGODB DEVELOPMENT LIBRARIES (libmongoc-1.0 and libbson-1.0) AND/OR HEADER (mongoc.h)" ${ECHO_CMD} " ARE NOT INSTALLED PROPERLY ON THIS SYSTEM." ${ECHO_CMD} " THAT'S OK BUT THE TURN SERVER IS BUILDING WITHOUT MONGODB SUPPORT." ${ECHO_CMD} return 0 else DBCFLAGS="${DBCFLAGS} ${MONGO_CFLAGS}" DBLIBS="${DBLIBS} ${MONGO_LIBS}" return 1 fi } testlib() { testlibraw l${1} } pthread_testlib() { SYSTEM=`uname` if [ "${SYSTEM}" = "DragonFly" ] ; then OSLIBS="${OSLIBS} -pthread" TURN_NO_SCTP=1 fi ISBSD=`uname | grep -i bsd` if ! [ -z "${ISBSD}" ] ; then OSLIBS="${OSLIBS} -pthread" fi if [ -z "${PTHREAD_LIBS}" ] ; then ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then return 1 else ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} -pthread 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then OSLIBS="${OSLIBS} -pthread" return 1 else ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} -lpthread 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then OSLIBS="${OSLIBS} -lpthread" return 1 fi fi fi else OSLIBS="${OSLIBS} ${PTHREAD_LIBS}" fi ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then return 1 else ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} -D_GNU_SOURCE 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Older GNU pthread library found" OSCFLAGS="${OSCFLAGS} -D_GNU_SOURCE" return 1 else ${ECHO_CMD} "Do not use pthreads" fi fi return 0 } pthread_testbarriers() { ${ECHO_CMD} "pthread_barrier_t barrier;" >> ${TH_TMPCPROGC} ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "pthread barriers not found" OSCFLAGS="${OSCFLAGS} -DTURN_NO_THREAD_BARRIERS" fi } gcm_testlib() { if [ -z "${TURN_NO_GCM}" ] ; then ${CC} ${GCM_TMPCPROGC} -o ${GCM_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then return 1 else return 0 fi else return 0 fi } testdaemon() { ${CC} ${D_TMPCPROGC} -o ${D_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then OSCFLAGS="${OSCFLAGS} -DTURN_HAS_DAEMON" fi } test_sin_len() { TMPCADDRPROGC=src/client/ns_turn_ioaddr.c ${CC} -c ${OSCFLAGS} -DTURN_HAS_SIN_LEN -Isrc ${TMPCADDRPROGC} -o ${TMPCADDRPROGO} 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then OSCFLAGS="${OSCFLAGS} -DTURN_HAS_SIN_LEN" ${ECHO_CMD} "Sockets code is fine: sin_len field present" else ${CC} -c ${OSCFLAGS} -Isrc ${TMPCADDRPROGC} -o ${TMPCADDRPROGO} 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Sockets code is fine: no sin_len field present" else ${ECHO_CMD} "WARNING: trial compilation failed: src/client/ns_turn_ioaddr.c" fi fi } ######################### # Start ######################### cleanup ######################### # To be set: ######################### if [ -z "${ECHO_CMD}" ] ; then ECHO_CMD=echo fi if [ -z "${FIND_CMD}" ] ; then FIND_CMD=find fi if [ -z "${PORTNAME}" ] ; then PORTNAME=turnserver fi ######################### # Installation directory options ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=BINDIR ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) BINDIR=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated | --localstate | --localstat) ac_prev=LOCALSTATEDIR ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* | --localstate=* | --localstat=*) LOCALSTATEDIR=$ac_optarg ;; -turndbdir | --turndbdir | --turndbdi | --turndbd | --turndb | --turnd) ac_prev=TURNDBDIR ;; -turndbdir=* | --turndbdir=* | --turndbdi=* | --turndbd=* | --turndb=* | --turnd=*) TURNDBDIR=$ac_optarg ;; -datadir | --datadir | --datadi | --datad | -schemadir | --schemadir) ac_prev=SCHEMADIR ;; -datadir=* | --datadir=* | --datadi=* | --datad=* | -schemadir=* | --schemadir=*) SCHEMADIR=$ac_optarg ;; -docdir | --docdir | --docdi | --doc | --do | -docsdir | --docsdir | --docsdi | --docs) ac_prev=DOCDIR ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=* | -docsdir=* | --docsdir=* | --docsdi=* | --docs=*) DOCSDIR=$ac_optarg ;; -examplesdir | --examplesdir | -examples | --examples) ac_prev=EXAMPLESDIR ;; -examplesdir=* | --examplesdir=* | -examples=* | --examples=*) EXAMPLESDIR=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=INCLUDEDIR ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) INCLUDEDIR=$ac_optarg ;; -turnincludedir | --turnincludedir) ac_prev=TURNINCLUDEDIR ;; -turnincludedir=* | --turnincludedir=*) TURNINCLUDEDIR=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=LIBDIR ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) LIBDIR=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m | -manprefix | --manprefix) ac_prev=MAXPREFIX ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=* | -manprefix=* | --manprefix=*) MANPREFIX=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=PREFIX ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) PREFIX=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy | -confdir | --confdir) ac_prev=CONFDIR ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=* | -confdir=* | --confdir=*) CONFDIR=$ac_optarg ;; -disable-rpath | --disable-rpath) TURN_DISABLE_RPATH=1 ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi ############################################# if [ -z "${PREFIX}" ] ; then if [ -z "${prefix}" ] ; then SYSTEM=`uname` if [ "${SYSTEM}" = "NetBSD" ] ; then # A little tough guy PREFIX=/usr/pkg elif [ "${SYSTEM}" = "SunOS" ] ; then # A fat guy PREFIX=/usr else # An ordinary person PREFIX=/usr/local fi else PREFIX=${prefix} fi fi if [ -z "${BINDIR}" ] ; then if [ -z "${bindir}" ] ; then BINDIR=${PREFIX}/bin else BINDIR=${bindir} fi fi if [ -z "${LOCALSTATEDIR}" ] ; then if [ -z "${localstatedir}" ] ; then if [ "${PREFIX}" = "/usr" ] ; then LOCALSTATEDIR=/var else LOCALSTATEDIR=${PREFIX}/var fi else LOCALSTATEDIR=${localstatedir} fi fi if [ -z "${CONFDIR}" ] ; then if [ -z "${confdir}" ] ; then CONFDIR=${PREFIX}/etc else CONFDIR=${confdir} fi fi if [ -z "${MANPREFIX}" ] ; then if [ -z "${manprefix}" ] ; then MANPREFIX=${PREFIX} else MANPREFIX=${manprefix} fi fi if [ -z "${EXAMPLESDIR}" ] ; then if [ -z "${examplesdir}" ] ; then EXAMPLESDIR=${PREFIX}/share/examples/${PORTNAME} else EXAMPLESDIR=${examplesdir} fi fi if [ -z "${DOCSDIR}" ] ; then if [ -z "${docsdir}" ] ; then DOCSDIR=${PREFIX}/share/doc/${PORTNAME} else DOCSDIR=${docsdir} fi fi if [ -z "${LIBDIR}" ] ; then if [ -z "${libdir}" ] ; then LIBDIR=${PREFIX}/lib else LIBDIR=${libdir} fi fi if [ -z "${SCHEMADIR}" ] ; then if [ -z "${schemadir}" ] ; then SCHEMADIR=${PREFIX}/share/${PORTNAME} else SCHEMADIR=${schemadir} fi fi if [ -z "${INCLUDEDIR}" ] ; then if [ -z "${includedir}" ] ; then INCLUDEDIR=${PREFIX}/include else INCLUDEDIR=${includedir} fi fi if [ -z "${TURNINCLUDEDIR}" ] ; then if [ -z "${turnincludedir}" ] ; then TURNINCLUDEDIR=${INCLUDEDIR}/turn else TURNINCLUDEDIR=${turnincludedir} fi fi ############################################### if [ -z "${ARCHIVERCMD}" ] ; then ARCHIVERCMD="ar -r" fi if [ -z "${MORECMD}" ]; then MORECMD="cat" fi OSCFLAGS="-I${INCLUDEDIR} -I${PREFIX}/include/ -I/usr/local/include ${CFLAGS}" OSLIBS="${LDFLAGS}" for ilib in ${PREFIX}/lib/event2/ ${PREFIX}/lib/ /usr/local/lib/event2/ /usr/local/lib/ ${PREFIX}/lib64/event2/ ${PREFIX}/lib64/ /usr/local/lib64/event2/ /usr/local/lib64/ do if [ -d ${ilib} ] ; then OSLIBS="${OSLIBS} -L${ilib}" TURN_RPATH="${TURN_RPATH} -Wl,-rpath,${ilib}" fi done SYSTEM=`uname` if [ "${SYSTEM}" = "NetBSD" ] ; then OSCFLAGS="${OSCFLAGS} -I/usr/pkg/include" OSLIBS="-L/usr/pkg/lib ${OSLIBS}" if ! [ -z "${TURN_ACCEPT_RPATH}" ] ; then TURN_RPATH="${TURN_RPATH} -Wl,-rpath,/usr/pkg/lib" fi fi ########################### # Install shell commands ########################### type ginstall 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then INSTALL_PROGRAM="ginstall" INSTALL_MAN="ginstall" INSTALL_SCRIPT="ginstall" INSTALL_SHARED_LIB="ginstall" INSTALL_STATIC_LIB="ginstall" INSTALL_DATA="ginstall" MKDIR="ginstall -d" else type install 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then INSTALL_PROGRAM="install" INSTALL_MAN="install" INSTALL_SCRIPT="install" INSTALL_SHARED_LIB="install" INSTALL_STATIC_LIB="install" INSTALL_DATA="install" MKDIR="install -d" else INSTALL_PROGRAM="cp -pf" INSTALL_MAN="cp -pf" INSTALL_SCRIPT="cp -pf" INSTALL_SHARED_LIB="cp -pf" INSTALL_STATIC_LIB="cp -pf" INSTALL_DATA="cp -pf" MKDIR="mkdir -p" fi fi type pkill 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then PKILL_PROGRAM="pkill" else PKILL_PROGRAM="${ECHO_CMD}" fi INSTALL_DIR="cp -rpf" MKBUILDDIR="mkdir -p" RMCMD="rm -rf" type sqlite3 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then SQLITE_CMD="sqlite3" else type sqlite 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then SQLITE_CMD="sqlite" else SQLITE_CMD=${ECHO_CMD} fi fi ############################# # Adjustments for Solaris ############################# SYSTEM=`uname` if [ "${SYSTEM}" = "SunOS" ] ; then # Solaris ? is this you ?! OSCFLAGS="${OSCFLAGS} -D__EXTENSIONS__ -D_XOPEN_SOURCE=500 -DTURN_NO_GETDOMAINNAME" OSLIBS="${OSLIBS} -lnsl" TURN_NO_SCTP=1 fi ######################### # Temporary DIR location: ######################### TMPDIR="." if [ -d /var/tmp ] ; then TMPDIR="/var/tmp" elif [ -d /tmp ] ; then TMPDIR=/tmp fi ${ECHO_CMD} Use TMP dir ${TMPDIR} ######################### # Basic C test programs ######################### TMPCADDRPROGO=${TMPDIR}/__test__ccomp_addr_$$.o TMPCPROG=__test__ccomp__$$ TMPCPROGC=${TMPDIR}/${TMPCPROG}.c TMPCPROGB=${TMPDIR}/${TMPCPROG} cat > ${TMPCPROGC} < int main(int argc, char** argv) { return (int)(argv[argc][0]); } ! TH_TMPCPROG=__test__ccomp__pthread__$$ TH_TMPCPROGC=${TMPDIR}/${TH_TMPCPROG}.c TH_TMPCPROGB=${TMPDIR}/${TH_TMPCPROG} cat > ${TH_TMPCPROGC} < #include int main(int argc, char** argv) { pthread_mutexattr_settype(0,PTHREAD_MUTEX_RECURSIVE); return (int)pthread_create(0,0,0,0)+(int)(argv[argc][0]); } ! GCM_TMPCPROG=__test__ccomp__gcm__$$ GCM_TMPCPROGC=${TMPDIR}/${GCM_TMPCPROG}.c GCM_TMPCPROGB=${TMPDIR}/${GCM_TMPCPROG} cat > ${GCM_TMPCPROGC} < #include #include int main(int argc, char** argv) { return (int)EVP_CIPH_GCM_MODE; } ! D_TMPCPROG=__test__ccomp__daemon__$$ D_TMPCPROGC=${TMPDIR}/${D_TMPCPROG}.c D_TMPCPROGB=${TMPDIR}/${D_TMPCPROG} cat > ${D_TMPCPROGC} < #include int main(int argc, char** argv) { return (int)daemon(0,0)+(int)(argv[argc][0]); } ! E_TMPCPROG=__test__ccomp__libevent2__$$ E_TMPCPROGC=${TMPDIR}/${E_TMPCPROG}.c E_TMPCPROGO=${TMPDIR}/${E_TMPCPROG}.o cat > ${E_TMPCPROGC} < #include int main(int argc, char** argv) { return (int)(argv[argc][0]); } ! SQL_TMPCPROG=__test__ccomp__sqlite__$$ SQL_TMPCPROGC=${TMPDIR}/${SQL_TMPCPROG}.c SQL_TMPCPROGO=${TMPDIR}/${SQL_TMPCPROG}.o cat > ${SQL_TMPCPROGC} < #include int main(int argc, char** argv) { return (int)(argv[argc][0]); } ! HR_TMPCPROG=__test__ccomp__hiredis__$$ HR_TMPCPROGC=${TMPDIR}/${HR_TMPCPROG}.c HR_TMPCPROGB=${TMPDIR}/${HR_TMPCPROG} cat > ${HR_TMPCPROGC} < #include #include int main(int argc, char** argv) { redisAsyncHandleRead(NULL); return (int)(argv[argc][0]); } ! PQ_TMPCPROG=__test__ccomp__libpq__$$ PQ_TMPCPROGC=${TMPDIR}/${PQ_TMPCPROG}.c PQ_TMPCPROGB=${TMPDIR}/${PQ_TMPCPROG} cat > ${PQ_TMPCPROGC} < #include int main(int argc, char** argv) { return (argc+(PQprotocolVersion(NULL))+(int)(argv[0][0])); } ! MYSQL_TMPCPROG=__test__ccomp__libmysql__$$ MYSQL_TMPCPROGC=${TMPDIR}/${MYSQL_TMPCPROG}.c MYSQL_TMPCPROGB=${TMPDIR}/${MYSQL_TMPCPROG} cat > ${MYSQL_TMPCPROGC} < #include int main(int argc, char** argv) { return (argc+ (int)(mysql_real_connect(NULL, NULL, NULL, NULL, NULL, 0, NULL, 0)!=0)+ (int)(argv[0][0])); } ! MONGO_TMPCPROG=__test__ccomp__libmongoc__$$ MONGO_TMPCPROGC=${TMPDIR}/${MONGO_TMPCPROG}.c MONGO_TMPCPROGB=${TMPDIR}/${MONGO_TMPCPROG} cat > ${MONGO_TMPCPROGC} < int main(int argc, char** argv) { return (argc+ (int)(mongoc_client_new("mongodb://localhost:27017")!=0)+ (int)(argv[0][0])); } ! ########################## # What is our compiler ? ########################## if [ -z "${CC}" ] ; then CC=cc ${CC} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then CC=gcc ${CC} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then CC=clang ${CC} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then CC=unknown fi fi fi fi ${ECHO_CMD} "Compiler: ${CC}" if [ -z "${TURN_ACCEPT_RPATH}" ] ; then ${CC} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} -Wl,-rpath,/usr/lib 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then TURN_ACCEPT_RPATH=1 fi fi ${CC} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "ERROR: cannot use compiler ${CC} properly" cleanup exit fi ########################### # Check if we can use GNU # or Clang compiler flags ########################### GNUOSCFLAGS="-g ${GNUOSCFLAGS}" GNUOSCFLAGS="${GNUOSCFLAGS} -Wall -Wno-deprecated-declarations -Wextra -Wformat-security -Wnested-externs -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wcast-qual" GNUOSCFLAGS="${GNUOSCFLAGS}" ${CC} -Werror ${GNUOSCFLAGS} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Hm..." ${CC} -Wall ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Not an ordinary GNU or Clang compiler" else ${ECHO_CMD} "g++ or something..." GNUOSCFLAGS="-g -Wall -Wno-deprecated-declarations -Wextra -Wformat-security -Wpointer-arith -Wcast-qual" ${CC} -Werror ${GNUOSCFLAGS} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Not an ordinary g++ compiler" GNUOSCFLAGS="-x c++ -g -Wall -Wno-deprecated-declarations -Wextra -Wformat-security -Wpointer-arith -Wcast-qual" ${CC} -Werror ${GNUOSCFLAGS} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Not an ordinary c++ compiler" else ${ECHO_CMD} "Clang++ compiler ?" OSCFLAGS="${GNUOSCFLAGS} ${OSCFLAGS}" fi else OSCFLAGS="${GNUOSCFLAGS} ${OSCFLAGS}" fi fi else OSCFLAGS="${GNUOSCFLAGS} ${OSCFLAGS}" fi ########################### # Test some general-purpose # libraries ########################### testlib socket testlib rt testlib wldap32 ER=$? if ! [ ${ER} -eq 0 ] ; then echo "CYGWIN ?" fi testlib wldap64 testlib intl testlib nsl testlib resolv ########################### # Test sockets compilation ########################### test_sin_len ########################### # Can we use multi-threading ? ########################### pthread_testlib ER=$? if [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "ERROR: Cannot find pthread library functions." exit fi if [ -z ${TURN_NO_THREAD_BARRIERS} ] ; then pthread_testbarriers else TURN_NO_THREAD_BARRIERS="-DTURN_NO_THREAD_BARRIERS" fi if [ -z ${TURN_IP_RECVERR} ] ; then ${ECHO_CMD} "Ignore IP_RECVERR" else ${ECHO_CMD} "Use IP_RECVERR" TURN_IP_RECVERR="-DTURN_IP_RECVERR" OSCFLAGS="${OSCFLAGS} ${TURN_IP_RECVERR}" fi ########################### # Can we use daemon ? ########################### testdaemon ########################### # Test OpenSSL installation ########################### testlib crypto ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Crypto SSL lib found." else ${ECHO_CMD} "ERROR: OpenSSL Crypto development libraries are not installed properly in required location." ${ECHO_CMD} "Abort." cleanup exit fi testlib ssl ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "SSL lib found." else ${ECHO_CMD} "ERROR: OpenSSL development libraries are not installed properly in required location." ${ECHO_CMD} "Abort." cleanup exit fi ########################### # Can we use GCM cipher ? ########################### if [ -z ${TURN_NO_GCM} ] ; then gcm_testlib ER=$? if [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "WARNING: Cannot find GCM support." ${ECHO_CMD} "Turning GCM off." TURN_NO_GCM="-DTURN_NO_GCM" fi else TURN_NO_GCM="-DTURN_NO_GCM" fi ########################### # Test Libevent2 setup ########################### testlibevent2_comp ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Libevent2 development found." else ${ECHO_CMD} "ERROR: Libevent2 development libraries are not installed properly in required location." ${ECHO_CMD} "ERROR: may be you have just too old libevent tool - then you have to upgrade it." ${ECHO_CMD} "See the INSTALL file." ${ECHO_CMD} "Abort." cleanup exit fi testlib event_core ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Libevent2 runtime found." testlib event_extra ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Libevent2 runtime 'extra' found." else ${ECHO_CMD} "ERROR: Libevent2 'extra' runtime library is not installed properly in required location." ${ECHO_CMD} "See the INSTALL file." ${ECHO_CMD} "Abort." cleanup exit fi else testlib event ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Libevent2 runtime found (old style)." else ${ECHO_CMD} "ERROR: Libevent2 runtime libraries are not installed properly in required location." ${ECHO_CMD} "See the INSTALL file." ${ECHO_CMD} "Abort." cleanup exit fi fi if [ -z "${TURN_NO_TLS}" ] ; then testlib event_openssl ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Libevent2 openssl found." else ${ECHO_CMD} "ERROR: Libevent2 development libraries are not compiled with OpenSSL support." ${ECHO_CMD} "TLS will be disabled." TURN_NO_TLS="-DTURN_NO_TLS" fi else TURN_NO_TLS="-DTURN_NO_TLS" fi testlib event_pthreads ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Libevent2 pthreads found." else ${ECHO_CMD} "ERROR: Libevent2 development libraries are not compiled with threads support." exit fi ########################### # Test SQLite setup ########################### if [ -z "${TURN_NO_SQLITE}" ] ; then testlib sqlite3 ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "SQLite library found." else ${ECHO_CMD} "SQLite3 development library cannot be found." TURN_NO_SQLITE="-DTURN_NO_SQLITE" fi if [ -z "${TURN_NO_SQLITE}" ] ; then testsqlite_comp ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "SQLite development found." else ${ECHO_CMD} "SQLite development libraries are not installed properly in required location." TURN_NO_SQLITE="-DTURN_NO_SQLITE" fi fi if ! [ -z "${TURN_NO_SQLITE}" ] ; then ${ECHO_CMD} ${ECHO_CMD} "SQLite DEVELOPMENT LIBRARY (libsqlite3) AND/OR HEADER (sqlite3.h)" ${ECHO_CMD} " ARE NOT INSTALLED PROPERLY ON THIS SYSTEM." ${ECHO_CMD} " THAT'S OK BUT THE TURN SERVER IS BUILDING WITHOUT SQLITE SUPPORT." ${ECHO_CMD} fi else TURN_NO_SQLITE="-DTURN_NO_SQLITE" SQLITE_CMD=${ECHO_CMD} fi if [ -z "${TURNDBDIR}" ] ; then TURNDBDIR=${LOCALSTATEDIR}/db fi ########################### # Test PostgreSQL ########################### if [ -z "${TURN_NO_PQ}" ] ; then testlibpq ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "PostgreSQL found." else TURN_NO_PQ="-DTURN_NO_PQ" fi else TURN_NO_PQ="-DTURN_NO_PQ" fi ########################### # Test MySQL ########################### if [ -z "${TURN_NO_MYSQL}" ] ; then testlibmysql ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "MySQL found." else TURN_NO_MYSQL="-DTURN_NO_MYSQL" fi else TURN_NO_MYSQL="-DTURN_NO_MYSQL" fi ########################### # Test MongoDB ########################### if [ -z "${TURN_NO_MONGO}" ] ; then testlibmongoc ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "MongoDB found." else TURN_NO_MONGO="-DTURN_NO_MONGO" fi else TURN_NO_MONGO="-DTURN_NO_MONGO" fi ########################### # Test Redis ########################### if [ -z "${TURN_NO_HIREDIS}" ] ; then testhiredis ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Hiredis found." else TURN_NO_HIREDIS="-DTURN_NO_HIREDIS" fi else TURN_NO_HIREDIS="-DTURN_NO_HIREDIS" fi ############################### # LDCONFIG ############################### if [ -z "${LDCONFIG}" ] ; then ISBSD=`uname | grep -i bsd` if [ -z "${ISBSD}" ] ; then ISLINUX=`uname | grep -i linux` if [ -z "${ISLINUX}" ] ; then SYSTEM=`uname` if [ "${SYSTEM}" = "SunOS" ] ; then LDCONFIG="crle -u -l" else LDCONFIG=${ECHO_CMD} fi else LDCONFIG="ldconfig -n" fi else LDCONFIG="ldconfig -m" fi fi ############################### # SCTP ############################### if [ -z "${TURN_NO_SCTP}" ] ; then if [ -z "${TURN_SCTP_INCLUDE}" ] ; then if [ -f /usr/include/netinet/sctp.h ] ; then TURN_SCTP_INCLUDE="-DTURN_SCTP_INCLUDE=\"\"" fi else TURN_SCTP_INCLUDE="-DTURN_SCTP_INCLUDE=\"\\\"${TURN_SCTP_INCLUDE}\\\"\"" fi else TURN_NO_SCTP="-DTURN_NO_SCTP" fi ############################### # So, what we have now: ############################### OSCFLAGS="${OSCFLAGS} ${TURN_NO_SCTP} ${TURN_SCTP_INCLUDE} ${TURN_NO_THREAD_BARRIERS} ${TURN_NO_DTLS} ${TURN_NO_GCM} ${TURN_NO_TLS} -DINSTALL_PREFIX=${PREFIX} -DTURNDB=${TURNDBDIR}/turndb" if ! [ -z "${TURN_ACCEPT_RPATH}" ] ; then if [ -z "${TURN_DISABLE_RPATH}" ] ; then TURN_RPATH="${TURN_RPATH} -Wl,-rpath,/usr/local/lib" OSLIBS="${OSLIBS} ${TURN_RPATH}" fi fi ${ECHO_CMD} PREFIX="${PREFIX}" LOCALSTATEDIR="${LOCALSTATEDIR}" OSLIBS="${OSLIBS}" DBLIBS="${DBLIBS}" OSCFLAGS="${OSCFLAGS}" DBCFLAGS="${DBCFLAGS}" $@ ############################### # Make make: ############################### ${ECHO_CMD} "#################################" > Makefile ${ECHO_CMD} "# Generated by configure script #" >> Makefile ${ECHO_CMD} "#################################" >> Makefile ${ECHO_CMD} "ECHO_CMD = ${ECHO_CMD}" >> Makefile ${ECHO_CMD} "CC = ${CC}" >> Makefile ${ECHO_CMD} "LDFLAGS += ${OSLIBS}" >> Makefile ${ECHO_CMD} "DBLIBS += ${DBLIBS}" >> Makefile ${ECHO_CMD} "CFLAGS += ${OSCFLAGS}" >> Makefile ${ECHO_CMD} "CPPFLAGS = ${CPPFLAGS}" >> Makefile ${ECHO_CMD} "DBCFLAGS += ${DBCFLAGS} ${TURN_NO_PQ} ${TURN_NO_MYSQL} ${TURN_NO_SQLITE} ${TURN_NO_MONGO} ${TURN_NO_HIREDIS}" >> Makefile ${ECHO_CMD} "#" >> Makefile ${ECHO_CMD} "PORTNAME = ${PORTNAME}" >> Makefile ${ECHO_CMD} "PREFIX = ${PREFIX}" >> Makefile ${ECHO_CMD} "prefix = ${PREFIX}" >> Makefile ${ECHO_CMD} "BINDIR = ${BINDIR}" >> Makefile ${ECHO_CMD} "bindir = ${BINDIR}" >> Makefile ${ECHO_CMD} "LOCALSTATEDIR = ${LOCALSTATEDIR}" >> Makefile ${ECHO_CMD} "localstatedir = ${LOCALSTATEDIR}" >> Makefile ${ECHO_CMD} "TURNDBDIR = ${TURNDBDIR}" >> Makefile ${ECHO_CMD} "turndbdir = ${TURNDBDIR}" >> Makefile ${ECHO_CMD} "CONFDIR = ${CONFDIR}" >> Makefile ${ECHO_CMD} "confdir = ${CONFDIR}" >> Makefile ${ECHO_CMD} "MANPREFIX = ${MANPREFIX}" >> Makefile ${ECHO_CMD} "manprefix = ${MANPREFIX}" >> Makefile ${ECHO_CMD} "EXAMPLESDIR = ${EXAMPLESDIR}" >> Makefile ${ECHO_CMD} "examplesdir = ${EXAMPLESDIR}" >> Makefile ${ECHO_CMD} "DOCSDIR = ${DOCSDIR}" >> Makefile ${ECHO_CMD} "docsdir = ${DOCSDIR}" >> Makefile ${ECHO_CMD} "LIBDIR = ${LIBDIR}" >> Makefile ${ECHO_CMD} "libdir = ${LIBDIR}" >> Makefile ${ECHO_CMD} "SCHEMADIR = ${SCHEMADIR}" >> Makefile ${ECHO_CMD} "schemadir = ${SCHEMADIR}" >> Makefile ${ECHO_CMD} "INCLUDEDIR = ${INCLUDEDIR}" >> Makefile ${ECHO_CMD} "includedir = ${INCLUDEDIR}" >> Makefile ${ECHO_CMD} "TURNINCLUDEDIR = ${TURNINCLUDEDIR}" >> Makefile ${ECHO_CMD} "turnincludedir = ${TURNINCLUDEDIR}" >> Makefile ${ECHO_CMD} "#" >> Makefile ${ECHO_CMD} "ARCHIVERCMD = ${ARCHIVERCMD}" >> Makefile ${ECHO_CMD} "MKDIR = ${MKDIR}" >> Makefile ${ECHO_CMD} "SQLITE_CMD = ${SQLITE_CMD}" >> Makefile ${ECHO_CMD} "INSTALL_PROGRAM = ${INSTALL_PROGRAM}" >> Makefile ${ECHO_CMD} "PKILL_PROGRAM = ${PKILL_PROGRAM}" >> Makefile ${ECHO_CMD} "INSTALL_MAN = ${INSTALL_MAN}" >> Makefile ${ECHO_CMD} "INSTALL_SCRIPT = ${INSTALL_SCRIPT}" >> Makefile ${ECHO_CMD} "INSTALL_SHARED_LIB = ${INSTALL_SHARED_LIB}" >> Makefile ${ECHO_CMD} "INSTALL_STATIC_LIB = ${INSTALL_STATIC_LIB}" >> Makefile ${ECHO_CMD} "INSTALL_DATA = ${INSTALL_DATA}" >> Makefile ${ECHO_CMD} "INSTALL_DIR = ${INSTALL_DIR}" >> Makefile ${ECHO_CMD} "MKBUILDDIR = ${MKBUILDDIR}" >> Makefile ${ECHO_CMD} "RMCMD = ${RMCMD}" >> Makefile ${ECHO_CMD} "MORECMD = ${MORECMD}" >> Makefile ${ECHO_CMD} "LDCONFIG=${LDCONFIG}" >> Makefile ${ECHO_CMD} "################################" >> Makefile ${ECHO_CMD} "" >> Makefile cat Makefile.in >> Makefile ############################### # End: ############################### cleanup ${ECHO_CMD} "Makefile created: success." turnserver-4.5.1.1/README.turnutils0000664000175000017500000003171213436577655015546 0ustar misimisiGENERAL INFORMATION A set of turnutils_* programs provides some utility functionality to be used for testing and for setting up the TURN server. 1. turnutils_uclient: emulates multiple UDP,TCP,TLS or DTLS clients. (this program is provided for the testing purposes only !) The compiled binary image of this program is located in bin/ sub-directory. 2. turnutils_peer: a simple stateless UDP-only "echo" server, to be used as the final server in relay pattern ("peer"). For every incoming UDP packet, it simply echoes it back. (this program is provided for the testing purposes only !) When the test clients are communicating in the client-to-client manner (when the "turnutils_uclient" program is used with "-y" option) then the turnutils_peer is not needed. The compiled binary image of this program is located in bin/ subdirectory. 3. turnutils_stunclient: a simple STUN client example. The compiled binary image of this program is located in bin/ subdirectory. 4. turnutils_rfc5769check: a utility that checks the correctness of the STUN/TURN protocol implementation. This utility is used only for the compilation check procedure, it is not copied to the installation destination. In the "examples/scripts" subdirectory, you will find the examples of command lines to be used to run the programs. The scripts are meant to be run from examples/ subdirectory, for example: $ cd examples $ ./scripts/secure_relay.sh 5. turnutils_natdiscovery: a utility that provides NAT behavior discovery according RFC5780. This utility discovers the actual NAT Mapping and Filtering behavior, etc. Be aware that on TURN server side two different listening IP addresses should be configured to be able to work properly! 6. turnutils_oauth: a utility that provides OAuth access_token generation(AEAD encryption), validation and decryption. This utility inputs all the keys and lifetimes and any related information that needed for creation and validationi of an access_token. It outputs a JSON with all OAuth PoP parameters that need to pass to the client. Output is generated accoriding RFC7635 Appendix B, Figure 8. For more details, and for the access_token structure, read rfc7635, and see script in examples/scripts/oauth.sh. ===================================== NAME turnutils_uclient - this client emulation application is supplied for the test purposes only. SYNOPSIS $ turnutils_uclient [-tTSvsyhcxg] [options] DESCRIPTION It was designed to simulate multiple clients. It uses asynch IO API in libevent to handle multiple clients. A client connects to the relay, negotiates the session, and sends multiple (configured number) messages to the server (relay), expecting the same number of replies. The length of the messages is configurable. The message is an arbitrary octet stream. The number of the messages to send is configurable. Flags: -t Use TCP for communications between client and TURN server (default is UDP). -b Use SCTP for communications between client and TURN server (default is UDP). -T Use TCP for the relay transport (default - UDP). Implies options -t, -y, -c, and ignores flags and options -s, -e, -r and -g. Can be used together with -b. -P Passive TCP (RFC6062 with active peer). Implies -T. -S Secure SSL connection: SSL/TLS for TCP, DTLS for UDP, TLS/SCTP for SCTP. -U Secure unencrypted connection (suite eNULL): SSL/TLS for TCP, DTLS for UDP. -v Verbose. -s Use "Send" method in TURN; by default, it uses TURN Channels. -y Use client-to-client connections: RTP/RTCP pair of channels to another RTP/RTCP pair of channels. with this option the turnutils_peer application is not used, as the allocated relay endpoints are talking to each other. -h Hang on indefinitely after the last sent packet. -c Do not create rtcp connections. -x Request IPv6 relay address (RFC6156). -X IPv4 relay address explicitly requested. -g Set DONT_FRAGMENT parameter in TURN requests. -D Do mandatory channel padding even for UDP (like pjnath). -N do negative tests (some limited cases only). -R do negative protocol tests. -O DOS attack mode. -M Use TURN ICE Mobility. -I Do not set permissions on TURN relay endpoints (for testing the non-standard server relay functionality). -G Generate extra requests (create permissions, channel bind). -B Random disconnect after a few initial packets. -Z Dual allocation (SSODA). Implies -c option. -J Use oAuth with default test key kid='north'. Options with required values: -l Message length (Default: 100 Bytes). -i Certificate file (for secure connections only, optional). -k Private key file (for secure connections only). -E CA file for server certificate verification, if the server certificate to be verified. -p TURN Server port (Defaults: 3478 unsecure, 5349 secure). -n Number of messages to send (Default: 5). -d Local interface device (optional, Linux only). -L Local IP address (optional). -m Number of clients (Default: 1, 2 or 4, depending on options). -e Peer address. -r Peer port (Default: 3480). -z Per-session packet interval in milliseconds (Default: 20). -u STUN/TURN user name. -w STUN/TURN user password. -W TURN REST API secret. The "plain text" secret e.g. "north" that is stored in the value column of the turn_secret table in the database if dynamic, or the static-auth-secret value set in the configuration file if using static. -C This is the timestamp/username separator symbol (character) in TURN REST API. The default value is :. -F Cipher suite for TLS/DTLS. Default value is DEFAULT. -o the ORIGIN STUN attribute value. -a Bandwidth for the bandwidth request in ALLOCATE. The default value is zero. See the examples in the "examples/scripts" directory. ====================================== NAME turnutils_peer - a simple UDP-only echo backend server. SYNOPSIS $ turnutils_peer [-v] [options] DESCRIPTION This application is used for the test purposes only, as a peer for the turnutils_uclient application. Options with required values: -p Listening UDP port (Default: 3480). -d Listening interface device (optional) -L Listening address of turnutils_peer server. Multiple listening addresses can be used, IPv4 and IPv6. If no listener address(es) defined, then it listens on all IPv4 and IPv6 addresses. -v Verbose ======================================== NAME turnutils_stunclient - a basic STUN client. SYNOPSIS $ turnutils_stunclient [options] DESCRIPTION It sends a "new" STUN RFC 5389 request (over UDP) and shows the reply information. Options with required values: -p STUN server port (Default: 3478). -L Local address to use (optional). -f Force RFC 5780 processing. The turnutils_stunclient program checks the results of the first request, and if it finds that the STUN server supports RFC 5780 (the binding response reveals that) then the turnutils_stunclient makes a couple more requests with different parameters, to demonstrate the NAT discovery capabilities. This utility does not support the "old" "classic" STUN protocol (RFC 3489). ===================================== NAME turnutils_rfc5769check - a utility that tests the correctness of STUN protocol implementation. SYNOPSIS $ turnutils_rfc5769check DESCRIPTION turnutils_rfc5769check tests the correctness of STUN protocol implementation against the test vectors predefined in RFC 5769 and prints the results of the tests on the screen. This utility is used only for the compilation check procedure, it is not copied to the installation destination. Usage: $ turnutils_rfc5769check ===================================== NAME turnutils_natdiscovery - a utility that discovers NAT mapping and filtering behavior according RFC5780. SYNOPSIS $ turnutils_natdiscovery [options] DESCRIPTION turnutils_natdiscovery discovers the NAT Mapping and Filtering behavior, to determine if that NAT is currently using Endpoint-Independent, Address-Dependent, or Address and Port-Dependent Mapping and/or to determine if that NAT is currently using Endpoint-Independent, Address-Dependent, or Address and Port-Dependent Filtering. Use either -m, -f, -c, -H flag to discover NAT behavior. Flags: -m NAT mapping behavior discovery -f NAT filtering behavior discovery -t NAT mapping lifetime behavior discovery Requires a timer (-T) -c NAT collision behavior discovery -H NAT hairpinning behavior discovery -P Add 1500 byte Padding to the behavior discovery Applicable with all except NAT mapping Lifetime discovery Options with required values: -p STUN server port (Default: 3478) -L Local address to use (optional) -l Local port to use (use with -L) -A Secondary Local address (optional) Required for collision discovery -T Mapping lifetime timer (sec) Used by mapping lifetime behavior discovery Usage: $ turnutils_natdiscovery -m -f stun.example.com ===================================== NAME turnutils_oauth - a utility that helps OAuth access_token generation/encryption and validation/decyption SYNOPSIS $ turnutils_oauth [options] DESCRIPTION turnutils_oauth utilitiy provides help in OAuth access_token encryption and/or decryption with AEAD (Atuthenticated Encryption with Associated Data). It helps for an Auth Server in access_token creation, and also for debugging purposes it helps the access_token validation and decryption. This utility inputs all the keys and lifetimes and any related information that are needed for encryption or decryption of an access_token. It outputs a JSON with all OAuth PoP parameters that need to pass to the client. Output is generated accoriding RFC7635 Appendix B, Figure 8. This utility could help to build an Auth Server service, but be awere that this utility does not generate "session key" / "mac_key" and not verifies lifetime of "session key" / "mac_key" or "Auth key". For more details, and for the access_token structure, read rfc7635, and see the example in examples/scripts/oauth.sh. Use either -e and/or -d flag to encrypt or decrypt access_token. Flags: -h, --help usage -v, --verbose verbose mode -e, --encrypt encrypt token -d, --decrypt decrypt validate token Options with required values: -i, --server-name server name (max. 255 char) -j, --auth-key-id Auth key id (max. 32 char) -k, --auth-key base64 encoded Auth key -l --auth-key-timestamp Auth key timestamp (sec since epoch) -m, --auth-key-lifetime Auth key lifetime in sec -n, --auth-key-as-rs-alg Authorization Server(AS) - Resource Server(RS) encryption algorithm -o, --token-nonce base64 encoded nonce base64(12 octet) = 16 char -p, --token-mac-key base64 encoded MAC key base64(32 octet) = 44 char -q, --token-timestamp timestamp in format 64 bit unsigned (Native format - Unix), so 48 bit for secs since epoch UTC + 16 bit for 1/64000 fractions of a second. e.g.: the actual unixtimestamp 16 bit left shifted. (Default: actual gmtime) -r, --token-lifetime lifetime in sec (Default: 3600) -t, --token base64 encoded encrypted token for validation and decryption -u, --hmac-alg stun client hmac algorithm Usage: $ turnutils_natdiscovery =================================== DOCS After installation, run the command: $ man turnutils or in the project root directory: $ man -M man turnutils to see the man page. ===================================== FILES /etc/turnserver.conf /var/db/turndb /usr/local/var/db/turndb /var/lib/turn/turndb /usr/local/etc/turnserver.conf ================================= DIRECTORIES /usr/local/share/turnserver /usr/local/share/doc/turnserver /usr/local/share/examples/turnserver =================================== STANDARDS new STUN RFC 5389 TURN RFC 5766 TURN-TCP extension RFC 6062 TURN IPv6 extension RFC 6156 STUN/TURN test vectors RFC 5769 STUN NAT behavior discovery RFC 5780 ==================================== SEE ALSO turnserver, turnadmin ====================================== WEB RESOURCES project page: https://github.com/coturn/coturn/ Wiki page: https://github.com/coturn/coturn/wiki forum: https://groups.google.com/forum/?fromgroups=#!forum/turn-server-project-rfc5766-turn-server/ ====================================== AUTHORS Oleg Moskalenko Gabor Kovesdan http://kovesdan.org/ Daniel Pocock http://danielpocock.com/ John Selbie (jselbie@gmail.com) Lee Sylvester Erik Johnston Roman Lisagor Vladimir Tsanev Po-sheng Lin Peter Dunkley Mutsutoshi Yoshimoto Federico Pinna Bradley T. Hughes Mihaly Meszaros turnserver-4.5.1.1/README.turnserver0000664000175000017500000012425513436577655015721 0ustar misimisiGENERAL INFORMATION The TURN Server project contains the source code of a TURN server and TURN client messaging library. Also, some extra programs provided, for testing-only purposes. See the INSTALL file for the building instructions. After the build, you will have the following binary images: 1. turnserver: TURN Server relay. The compiled binary image of the TURN Server program is located in bin/ sub-directory. 2. turnadmin: TURN administration tool. See README.turnadmin and turnadmin man page. 3. turnutils_uclient. See README.turnutils and turnutils man page. 4. turnutils_peer. See README.turnutils and turnutils man page. 5. turnutils_stunclient. See README.turnutils and turnutils man page. 6. turnutils_rfc5769check. See README.turnutils and turnutils man page. In the "examples/scripts" sub-directory, you will find the examples of command lines to be used to run the programs. The scripts are meant to be run from examples/ sub-directory, for example: $ cd examples $ ./scripts/secure_relay.sh RUNNING THE TURN SERVER Options note: turnserver has long and short option names, for most options. Some options have only long form, some options have only short form. Their syntax somewhat different, if an argument is required: The short form must be used as this (for example): $ turnserver -L 12.34.56.78 The long form equivalent must use the "=" character: $ turnserver --listening-ip=12.34.56.78 If this is a flag option (no argument required) then their usage are the same, for example: $ turnserver -a is equivalent to: $ turnserver --lt-cred-mech ===================================== NAME turnserver - a TURN relay server implementation. SYNOPSIS $ turnserver [-n | -c ] [flags] [ --userdb= | --psql-userdb= | --mysql-userdb= | --mongo-userdb= | --redis-userdb= ] [-z | --no-auth | -a | --lt-cred-mech ] [options] $ turnserver -h DESCRIPTION Config file settings: -n Do not use configuration file, use only command line parameters. -c Configuration file name (default - turnserver.conf). The format of config file can be seen in the supplied examples/etc/turnserver.conf example file. Long names of the options are used as the configuration items names in the file. If not an absolute path is supplied, then the file is searched in the following directories: * current directory * current directory etc/ sub-directory * upper directory level etc/ * /etc/ * /usr/local/etc/ * installation directory /etc User database settings: -b, --db, --userdb SQLite user database file name (default - /var/db/turndb or /usr/local/var/db/turndb or /var/lib/turn/turndb). -e, --psql-userdb User database connection string for PostgreSQL. This database can be used for long-term credentials mechanism, and it can store the secret value for secret-based timed authentication in TURN RESP API. The connection string format is like that: "host= dbname= user= password= connect_timeout=" (for 8.x or newer Postgres). Or: "postgresql://username:password@hostname:port/databasename" (for 9.x or newer Postgres). See the INSTALL file for more explanations and examples. Also, see http://www.PostgreSQL.org for full PostgreSQL documentation. -M, --mysql-userdb User database connection string for MySQL or MariaDB. This database can be used for long-term credentials mechanism, and it can store the secret value for secret-based timed authentication in TURN RESP API. The connection string format is like that: "host= dbname= user= password= connect_timeout= read_timeout=" See the INSTALL file for more explanations and examples. Also, see http://www.mysql.org or http://mariadb.org for full MySQL documentation. Optional connection string parameters for the secure communications (SSL): ca, capath, cert, key, cipher (see http://dev.mysql.com/doc/refman/5.1/en/ssl-options.html for the command options description). --secret-key-file This is the file path which contain secret key of aes encryption while using MySQL password encryption. If you want to use in the MySQL connection string the password in encrypted format, then set in this option the file path of the secret key. The key which is used to encrypt MySQL password. Warning: If this option is set, then MySQL password must be set in "mysql-userdb" option in encrypted format! If you want to use cleartext password then do not set this option! -J, --mongo-userdb User database connection string for MongoDB. This database can be used for long-term credentials mechanism, and it can store the secret value for secret-based timed authentication in TURN RESP API. The connection string format is like that: "mongodb://username:password@host:port/database?options" See the INSTALL file for more explanations and examples. Also, see http://docs.mongodb.org/manual/ for full MongoDB documentation. -N, --redis-userdb User database connection string for Redis. This database can be used for long-term credentials mechanism, and it can store the secret value for secret-based timed authentication in TURN RESP API. The connection string format is like that: "ip= dbname= password= connect_timeout=" See the INSTALL file for more explanations and examples. Also, see http://redis.io for full Redis documentation. Flags: -v, --verbose Moderate verbose mode. -V, --Verbose Extra verbose mode, very annoying and not recommended. -o, --daemon Run server as daemon. --prod Production mode: hide the software version. -f, --fingerprint Use fingerprints in the TURN messages. If an incoming request contains a fingerprint, then TURN server will always add fingerprints to the messages in this session, regardless of the per-server setting. -a, --lt-cred-mech Use long-term credentials mechanism (this one you need for WebRTC usage). -z, --no-auth Do not use any credentials mechanism, allow anonymous access. Opposite to -a and -A options. This is default option when no authentication-related options are set. By default, no credential mechanism is used - any user is allowed. --use-auth-secret TURN REST API flag. Flag that sets a special WebRTC authorization option that is based upon authentication secret. The feature purpose is to support "TURN Server REST API" as described in the TURN REST API section below. This option uses timestamp as part of combined username: usercombo -> "timestamp:username", turn user -> usercombo, turn password -> base64(hmac(input_buffer = usercombo, key = shared-secret)). This allows TURN credentials to be accounted for a specific user id. If you don't have a suitable id, the timestamp alone can be used. This option is just turns on secret-based authentication. The actual value of the secret is defined either by option static-auth-secret, or can be found in the turn_secret table in the database. --oauth Support oAuth authentication, as in the third-party STUN/TURN RFC 7635. --dh566 Use 566 bits predefined DH TLS key. Default size of the key is 1066. --dh2066 Use 2066 bits predefined DH TLS key. Default size of the key is 1066. --no-tlsv1 Do not allow TLSv1/DTLSv1 protocol. --no-tlsv1_1 Do not allow TLSv1.1 protocol. --no-tlsv1_2 Do not allow TLSv1.2/DTLSv1.2 protocol. --no-udp Do not start UDP client listeners. --no-tcp Do not start TCP client listeners. --no-tls Do not start TLS client listeners. --no-dtls Do not start DTLS client listeners. --no-udp-relay Do not allow UDP relay endpoints defined in RFC 5766, use only TCP relay endpoints as defined in RFC 6062. --no-tcp-relay Do not allow TCP relay endpoints defined in RFC 6062, use only UDP relay endpoints as defined in RFC 5766. --no-stdout-log Flag to prevent stdout log messages. By default, all log messages are going to both stdout and to the configured log file. With this option everything will be going to the log file only (unless the log file itself is stdout). --syslog With this flag, all log will be redirected to the system log (syslog). --simple-log This flag means that no log file rollover will be used, and the log file name will be constructed as-is, without PID and date appendage. This option can be used, for example, together with the logrotate tool. --secure-stun Require authentication of the STUN Binding request. By default, the clients are allowed anonymous access to the STUN Binding functionality. -S, --stun-only Run as STUN server only, all TURN requests will be ignored. Option to suppress TURN functionality, only STUN requests will be processed. --no-stun Run as TURN server only, all STUN requests will be ignored. Option to suppress STUN functionality, only TURN requests will be processed. --allow-loopback-peers Allow peers on the loopback addresses (127.x.x.x and ::1). Allow it only for testing in a development environment! In production it adds a possible security vulnerability, and so due to security reasons, it is not allowed using it together with empty cli-password. --no-multicast-peers Disallow peers on well-known broadcast addresses (224.0.0.0 and above, and FFXX:*). --mobility Mobility with ICE (MICE) specs support. --no-cli Turn OFF the CLI support. By default it is always ON. See also options --cli-ip and --cli-port. --server-relay Server relay. NON-STANDARD AND DANGEROUS OPTION. Only for those applications when we want to run server applications on the relay endpoints. This option eliminates the IP permissions check on the packets incoming to the relay endpoints. See http://tools.ietf.org/search/rfc5766#section-17.2.3 . --udp-self-balance (recommended for older Linuxes only) Automatically balance UDP traffic over auxiliary servers (if configured). The load balancing is using the ALTERNATE-SERVER mechanism. The TURN client must support 300 ALTERNATE-SERVER response for this functionality. --check-origin-consistency The flag that sets the origin consistency check: across the session, all requests must have the same main ORIGIN attribute value (if the ORIGIN was initially used by the session). -h Help. Options with values: --stale-nonce[=] Use extra security with nonce value having limited lifetime, in seconds (default 600 secs). --max-allocate-lifetime Set the maximum value for the allocation lifetime. Default to 3600 secs. --channel-lifetime Set the lifetime for channel binding, default to 600 secs. This value MUST not be changed for production purposes. --permission-lifetime Set the value for the lifetime of the permission. Default to 300 secs. This MUST not be changed for production purposes. -d, --listening-device Listener interface device. (NOT RECOMMENDED. Optional functionality, Linux only). The turnserver process must have root privileges to bind the listening endpoint to a device. If turnserver must run as a process without root privileges, then just do not use this setting. -L, --listening-ip Listener IP address of relay server. Multiple listeners can be specified, for example: -L ip1 -L ip2 -L ip3 If no IP(s) specified, then all IPv4 and IPv6 system IPs will be used for listening. The same ip(s) can be used as both listening and relay ip(s). -p, --listening-port TURN listener port for UDP and TCP listeners (Default: 3478). Note: actually, TLS & DTLS sessions can connect to the "plain" TCP & UDP port(s), too - if allowed by configuration. --tls-listening-port TURN listener port for TLS and DTLS listeners (Default: 5349). Note: actually, "plain" TCP & UDP sessions can connect to the TLS & DTLS port(s), too - if allowed by configuration. The TURN server "automatically" recognizes the type of traffic. Actually, two listening endpoints (the "plain" one and the "tls" one) are equivalent in terms of functionality; but we keep both endpoints to satisfy the RFC 5766 specs. For secure TCP connections, we currently support SSL version 3 and TLS versions 1.0, 1.1, 1.2. For secure UDP connections, we support DTLS version 1. --alt-listening-port Alternative listening port for UDP and TCP listeners; default (or zero) value means "listening port plus one". This is needed for STUN CHANGE_REQUEST - in RFC 5780 sense or in old RFC 3489 sense - for NAT behavior discovery). The TURN Server supports CHANGE_REQUEST only if it is started with more than one listening IP address of the same family (IPv4 or IPv6). The CHANGE_REQUEST is only supported by UDP protocol, other protocols are listening on that endpoint only for "symmetry". --alt-tls-listening-port Alternative listening port for TLS and DTLS protocols. Default (or zero) value means "TLS listening port plus one". --aux-server Auxiliary STUN/TURN server listening endpoint. Aux servers have almost full TURN and STUN functionality. The (minor) limitations are: 1) Auxiliary servers do not have alternative ports and they do not support STUN RFC 5780 functionality (CHANGE REQUEST). 2) Auxiliary servers also are never returning ALTERNATIVE-SERVER reply. Valid formats are 1.2.3.4:5555 for IPv4 and [1:2::3:4]:5555 for IPv6. There may be multiple aux-server options, each will be used for listening to client requests. -i, --relay-device Relay interface device for relay sockets (NOT RECOMMENDED. Optional, Linux only). -E, --relay-ip Relay address (the local IP address that will be used to relay the packets to the peer). Multiple relay addresses may be used: -E ip1 -E ip2 -E ip3 The same IP(s) can be used as both listening IP(s) and relay IP(s). If no relay IP(s) specified, then the turnserver will apply the default policy: it will decide itself which relay addresses to be used, and it will always be using the client socket IP address as the relay IP address of the TURN session (if the requested relay address family is the same as the family of the client socket). -X, --external-ip TURN Server public/private address mapping, if the server is behind NAT. In that situation, if a -X is used in form "-X " then that ip will be reported as relay IP address of all allocations. This scenario works only in a simple case when one single relay address is be used, and no CHANGE_REQUEST functionality is required. That single relay address must be mapped by NAT to the 'external' IP. The "external-ip" value, if not empty, is returned in XOR-RELAYED-ADDRESS field. For that 'external' IP, NAT must forward ports directly (relayed port 12345 must be always mapped to the same 'external' port 12345). In more complex case when more than one IP address is involved, that option must be used several times, each entry must have form "-X ", to map all involved addresses. CHANGE_REQUEST (RFC5780 or RFC3489) NAT discovery STUN functionality will work correctly, if the addresses are mapped properly, even when the TURN server itself is behind A NAT. By default, this value is empty, and no address mapping is used. -m, --relay-threads Number of the relay threads to handle the established connections (in addition to authentication thread and the listener thread). If explicitly set to 0 then application runs relay process in a single thread, in the same thread with the listener process (the authentication thread will still be a separate thread). If not set, then a default optimal algorithm will be employed (OS-dependent). In the older Linux systems (before Linux kernel 3.9), the number of UDP threads is always one threads per network listening endpoint - unless "-m 0" or "-m 1" is set. --min-port Lower bound of the UDP port range for relay endpoints allocation. Default value is 49152, according to RFC 5766. --max-port Upper bound of the UDP port range for relay endpoints allocation. Default value is 65535, according to RFC 5766. -u, --user Long-term security mechanism credentials user account, in the column-separated form username:key. Multiple user accounts may be used in the command line. The key is either the user password, or the key is generated by turnadmin command. In the second case, the key must be prepended with 0x symbols. The key is calculated over the user name, the user realm, and the user password. This setting may not be used with TURN REST API. -r, --realm The default realm to be used for the users when no explicit origin/realm relationship was found in the database, or if the TURN server is not using any database (just the commands-line settings and the userdb file). Must be used with long-term credentials mechanism or with TURN REST API. -C, --rest-api-separator This is the timestamp/username separator symbol (character) in TURN REST API. The default value is :. -q, --user-quota Per-user allocations quota: how many concurrent allocations a user can create. This option can also be set through the database, for a particular realm. -Q, --total-quota Total allocations quota: global limit on concurrent allocations. This option can also be set through the database, for a particular realm. -s, --max-bps Max bytes-per-second bandwidth a TURN session is allowed to handle (input and output network streams are treated separately). Anything above that limit will be dropped or temporary suppressed (within the available buffer limits). This option can also be set through the database, for a particular realm. -B, --bps-capacity Maximum server capacity. Total bytes-per-second bandwidth the TURN server is allowed to allocate for the sessions, combined (input and output network streams are treated separately). --static-auth-secret Static authentication secret value (a string) for TURN REST API only. If not set, then the turn server will try to use the dynamic value in turn_secret table in user database (if present). The database-stored value can be changed on-the-fly by a separate program, so this is why that other mode is dynamic. Multiple shared secrets can be used (both in the database and in the "static" fashion). --server-name Server name used for the oAuth authentication purposes. The default value is the realm name. --cert Certificate file, PEM format. Same file search rules applied as for the configuration file. If both --no-tls and --no-dtls options are specified, then this parameter is not needed. Default value is turn_server_cert.pem. --pkey Private key file, PEM format. Same file search rules applied as for the configuration file. If both --no-tls and --no-dtls options are specified, then this parameter is not needed. Default value is turn_server_pkey.pem. --pkey-pwd If the private key file is encrypted, then this password to be used. --cipher-list Allowed OpenSSL cipher list for TLS/DTLS connections. Default value is "DEFAULT". --CA-file CA file in OpenSSL format. Forces TURN server to verify the client SSL certificates. By default, no CA is set and no client certificate check is performed. --ec-curve-name Curve name for EC ciphers, if supported by OpenSSL library (TLS and DTLS). The default value is prime256v1, if pre-OpenSSL 1.0.2 is used. With OpenSSL 1.0.2+, an optimal curve will be automatically calculated, if not defined by this option. --dh-file Use custom DH TLS key, stored in PEM format in the file. Flags --dh566 and --dh2066 are ignored when the DH key is taken from a file. -l, --log-file Option to set the full path name of the log file. By default, the turnserver tries to open a log file in /var/log/turnserver, /var/log, /var/tmp, /tmp and . (current) directories (which file open operation succeeds first that file will be used). With this option you can set the definite log file name. The special names are "stdout" and "-" - they will force everything to the stdout. Also, "syslog" name will redirect everything into the system log (syslog), as if the option "--syslog" was set. In the runtime, the logfile can be reset with the SIGHUP signal to the turnserver process. --alternate-server Option to set the "redirection" mode. The value of this option will be the address of the alternate server for UDP & TCP service in form of [:]. The server will send this value in the attribute ALTERNATE-SERVER, with error 300, on ALLOCATE request, to the client. Client will receive only values with the same address family as the client network endpoint address family. See RFC 5389 and RFC 5766 for ALTERNATE-SERVER functionality description. The client must use the obtained value for subsequent TURN communications. If more than one --alternate-server options are provided, then the functionality can be more accurately described as "load-balancing" than a mere "redirection". If the port number is omitted, then the default port number 3478 for the UDP/TCP protocols will be used. Colon (:) characters in IPv6 addresses may conflict with the syntax of the option. To alleviate this conflict, literal IPv6 addresses are enclosed in square brackets in such resource identifiers, for example: [2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478 . Multiple alternate servers can be set. They will be used in the round-robin manner. All servers in the pool are considered of equal weight and the load will be distributed equally. For example, if we have 4 alternate servers, then each server will receive 25% of ALLOCATE requests. An alternate TURN server address can be used more than one time with the alternate-server option, so this can emulate "weighting" of the servers. --tls-alternate-server Option to set alternative server for TLS & DTLS services in form of :. If the port number is omitted, then the default port number 5349 for the TLS/DTLS protocols will be used. See the previous option for the functionality description. -O, --redis-statsdb Redis status and statistics database connection string, if used (default - empty, no Redis stats DB used). This database keeps allocations status information, and it can be also used for publishing and delivering traffic and allocation event notifications. This database option can be used independently of --redis-userdb option, and actually Redis can be used for status/statistics and SQLite or MySQL or MongoDB or PostgreSQL can be used for the user database. The connection string has the same parameters as redis-userdb connection string. --max-allocate-timeout Max time, in seconds, allowed for full allocation establishment. Default is 60 seconds. --denied-peer-ip= --allowed-peer-ip= Options to ban or allow specific ip addresses or ranges of ip addresses. If an ip address is specified as both allowed and denied, then the ip address is considered to be allowed. This is useful when you wish to ban a range of ip addresses, except for a few specific ips within that range. This can be used when you do not want users of the turn server to be able to access machines reachable by the turn server, but would otherwise be unreachable from the internet (e.g. when the turn server is sitting behind a NAT). The 'white" and "black" peer IP ranges can also be dynamically changed in the database. The allowed/denied addresses (white/black lists) rules are very simple: 1) If there is no rule for an address, then it is allowed; 2) If there is an "allowed" rule that fits the address then it is allowed - no matter what; 3) If there is no "allowed" rule that fits the address, and if there is a "denied" rule that fits the address, then it is denied. --pidfile File name to store the pid of the process. Default is /var/run/turnserver.pid (if superuser account is used) or /var/tmp/turnserver.pid . --proc-user User name to run the process. After the initialization, the turnserver process will make an attempt to change the current user ID to that user. --proc-group Group name to run the process. After the initialization, the turnserver process will make an attempt to change the current group ID to that group. -K, --keep-address-family TURN server allocates address family according TURN Client <=> Server communication address family. !! It breaks RFC6156 section-4.2 (violates default IPv4) !! --cli-ip Local system IP address to be used for CLI management interface. The turnserver process can be accessed for management with telnet, at this IP address and on the CLI port (see the next parameter). Default value is 127.0.0.1. You can use telnet or putty (in telnet mode) to access the CLI management interface. --cli-port CLI management interface listening port. Default is 5766. --cli-password CLI access password. Default is empty (no password). For the security reasons, it is recommended to use the encrypted form of the password (see the -P command in the turnadmin utility). The dollar signs in the encrypted form must be escaped. --cli-max-output-sessions Maximum number of output sessions in ps CLI command. This value can be changed on-the-fly in CLI. The default value is 256. --web-admin Enable Turn Web-admin support. By default it is disabled. --web-admin-ip= Local system IP address to be used for Web-admin server endpoint. Default value is 127.0.0.1. --web-admin-port= Web-admin server port. Default is 8080. --web-admin-listen-on-workers Enable for web-admin server to listens on STUN/TURN workers STUN/TURN ports. By default it is disabled for security resons! (This behavior used to be the default behavior, and was enabled by default.) --ne=[1|2|3] Set network engine type for the process (for internal purposes). ================================== LOAD BALANCE AND PERFORMANCE TUNING This topic is covered in the wiki page: https://github.com/coturn/coturn/wiki/turn_performance_and_load_balance =================================== WEBRTC USAGE This is a set of notes for the WebRTC users: 1) WebRTC uses long-term authentication mechanism, so you have to use -a option (or --lt-cred-mech). WebRTC relaying will not work with anonymous access. With -a option, do not forget to set the default realm (-r option). You will also have to set up the user accounts, for that you have a number of options: a) command-line options (-u). b) a database table (SQLite or PostgreSQL or MySQL or MongoDB). You will have to set keys with turnadmin utility (see docs and wiki for turnadmin). You cannot use open passwords in the database. c) Redis key/value pair(s), if Redis is used. You key use either keys or open passwords with Redis; see turndb/testredisdbsetup.sh file. d) You also can use the TURN REST API. You will need shared secret(s) set either through the command line option, or through the config file, or through the database table or Redis key/value pairs. 2) Usually WebRTC uses fingerprinting (-f). 3) -v option may be nice to see the connected clients. 4) -X is needed if you are running your TURN server behind a NAT. 5) --min-port and --max-port may be needed if you want to limit the relay endpoints ports number range. =================================== TURN REST API In WebRTC, the browser obtains the TURN connection information from the web server. This information is a secure information - because it contains the necessary TURN credentials. As these credentials are transmitted over the public networks, we have a potential security breach. If we have to transmit a valuable information over the public network, then this information has to have a limited lifetime. Then the guy who obtains this information without permission will be able to perform only limited damage. This is how the idea of TURN REST API - time-limited TURN credentials - appeared. This security mechanism is based upon the long-term credentials mechanism. The main idea of the REST API is that the web server provides the credentials to the client, but those credentials can be used only limited time by an application that has to create a TURN server connection. The "classic" long-term credentials mechanism (LTCM) is described here: http://tools.ietf.org/html/rfc5389#section-10.2 http://tools.ietf.org/html/rfc5389#section-15.4 For authentication, each user must know two things: the username and the password. Optionally, the user must supply the ORIGIN value, so that the server can figure out the realm to be used for the user. The nonce and the realm values are supplied by the TURN server. But LTCM is not saying anything about the nature and about the persistence of the username and of the password; and this is used by the REST API. In the TURN REST API, there is no persistent passwords for users. A user has just the username. The password is always temporary, and it is generated by the web server on-demand, when the user accesses the WebRTC page. And, actually, a temporary one-time session only, username is provided to the user, too. The temporary user is generated as: temporary-username="timestamp" + ":" + "username" where username is the persistent user name, and the timestamp format is just seconds sinse 1970 - the same value as time(NULL) function returns. The temporary password is obtained as HMAC-SHA1 function over the temporary username, with shared secret as the HMAC key, and then the result is encoded: temporary-password = base64_encode(hmac-sha1(shared-secret, temporary-username)) Both the TURN server and the web server know the same shared secret. How the shared secret is distributed among the involved entities is left to the WebRTC deployment details - this is beyond the scope of the TURN REST API. So, a timestamp is used for the temporary password calculation, and this timestamp can be retrieved from the temporary username. This information is valuable, but only temporary, while the timestamp is not expired. Without knowledge of the shared secret, a new temporary password cannot be generated. This is all formally described in Justin's Uberti TURN REST API document that can be obtained following the link "TURN REST API" in the TURN Server project's page https://github.com/coturn/coturn/. Once the temporary username and password are obtained by the client (browser) application, then the rest is just 'classic" long-term credentials mechanism. For developers, we are going to describe it step-by-step below: - a new TURN client sends a request command to the TURN server. Optionally, it adds the ORIGIN field to it. - TURN server sees that this is a new client and the message is not authenticated. - the TURN server generates a random nonce string, and return the error 401 to the client, with nonce and realm included. If the ORIGIN field was present in the client request, it may affect the realm value that the server chooses for the client. - the client sees the 401 error and it extracts two values from the error response: the nonce and the realm. - the client uses username, realm and password to produce a key: key = MD5(username ":" realm ":" SASLprep(password)) (SASLprep is described here: http://tools.ietf.org/html/rfc4013) - the client forms a new request, adds username, realm and nonce to the request. Then, the client calculates and adds the integrity field to the request. This is the trickiest part of the process, and it is described in the end of section 15.4: http://tools.ietf.org/html/rfc5389#section-15.4 - the client, optionally, adds the fingerprint field. This may be also a tricky procedure, described in section 15.5 of the same document. WebRTC usually uses fingerprinted TURN messages. - the TURN server receives the request, reads the username. - then the TURN server checks that the nonce and the realm in the request are the valid ones. - then the TURN server calculates the key. - then the TURN server calculates the integrity field. - then the TURN server compares the calculated integrity field with the received one - they must be the same. If the integrity fields differ, then the request is rejected. In subsequent communications, the client may go with exactly the same sequence, but for optimization usually the client, having already information about realm and nonce, pre-calculates the integrity string for each request, so that the 401 error response becomes unnecessary. The TURN server may use "--stale-nonce" option for extra security: in some time, the nonce expires and the client will obtain 438 error response with the new nonce, and the client will have to start using the new nonce. In subsequent communications, the sever and the client will always assume the same password - the original password becomes the session parameter and is never expiring. So the password is not changing while the session is valid and unexpired. So, if the session is properly maintained, it may go forever, even if the user password has been already changed (in the database). The session simply is using the old password. Once the session got disconnected, the client will have to use the new password to re-connect (if the password has been changed). An example when a new shared secret is generated every hour by the TURN server box and then supplied to the web server, remotely, is provided in the script examples/scripts/restapi/shared_secret_maintainer.pl . A very important thing is that the nonce must be totally random and it must be different for different clients and different sessions. =================================== DATABASES For the user database, the turnserver has the following options: 1) Users can be set in the command line, with multiple -u or --user options. Obviously, only a few users can be set that way, and their credentials are fixed for the turnserver process lifetime. 2) Users can be stored in SQLite DB. The default SQLite database file is /var/db/turndb or /usr/local/var/db/turndb or /var/lib/turn/turndb. 3) Users can be stored in PostgreSQL database, if the turnserver was compiled with PostgreSQL support. Each time turnserver checks user credentials, it reads the database (asynchronously, of course, so that the current flow of packets is not delayed in any way), so any change in the database content is immediately visible by the turnserver. This is the way if you need the best scalability. The schema for the database can be found in schema.sql file. For long-term credentials, you have to set the "keys" for the users; the "keys" are generated by the turnadmin utility. For the key generation, you need username, password and the realm. All users in the database must use the same realm value; if down the road you will decide to change the realm name, then you will have to re-generate all user keys (that can be done in a batch script). See the file turndb/testsqldbsetup.sql as an example. 4) The same is true for MySQL database. The same schema file is applicable. The same considerations are applicable. 5) The same is true for the Redis database, but the Redis database has aa different schema - it can be found (in the form of explanation) in schema.userdb.redis. Also, in Redis you can store both "keys" and open passwords (for long term credentials) - the "open password" option is less secure but more convenient for low-security environments. See the file turndb/testredisdbsetup.sh as an example. 6) If a database is used, then users can be divided into multiple independent realms. Each realm can be administered separately, and each realm can have its own set of users and its own performance options (max-bps, user-quota, total-quota). 7) If you use MongoDB, the database will be setup for you automatically. 8) Of course, the turnserver can be used in non-secure mode, when users are allowed to establish sessions anonymously. But in most cases (like WebRTC) that will not work. For the status and statistics database, there are two choices: 1) The simplest choice is not to use it. Do not set --redis-statsdb option, and this functionality will be simply ignored. 2) If you choose to use it, then set the --redis-statsdb option. This may be the same database as in --redis-userdb option, or it may be a different database. You may want to use different database for security or convenience reasons. Also, you can use different database management systems for the user database and for the ststus and statistics database. For example, you can use MySQL as the user database, and you can use redis for the statistics. Or you can use Redis for both. So, we have 6 choices for the user management, and 2 choices for the statistics management. These two are totally independent. So, you have overall 6*2=12 ways to handle persistent information, choose any for your convenience. You do not have to handle the database information "manually" - the turnadmin program can handle everything for you. For PostgreSQL and MySQL you will just have to create an empty database with schema.sql SQL script. With Redis, you do not have to do even that - just run turnadmin and it will set the users for you (see the turnadmin manuals). If you are using SQLite, then the turnserver or turnadmin will initialize the empty database, for you, when started. The TURN server installation process creates an empty initialized SQLite database in the default location (/var/db/turndb or /usr/local/var/db/turndb or /var/lib/turn/turndb, depending on the system). ================================= ALPN The server supports ALPNs "stun.turn" and "stun.nat-discovery", when compiled with OpenSSL 1.0.2 or newer. If the server receives a TLS/DTLS ClientHello message that contains one or both of those ALPNs, then the server chooses the first stun.* label and sends it back (in the ServerHello) in the ALPN extension field. If no stun.* label is found, then the server does not include the ALPN information into the ServerHello. ================================= LIBRARIES In the lib/ sub-directory the build process will create TURN client messaging library. In the include/ sub-directory, the necessary include files will be placed. The C++ wrapper for the messaging functionality is located in TurnMsgLib.h header. An example of C++ code can be found in stunclient.c file. ================================= DOCS After installation, run the command: $ man turnserver or in the project root directory: $ man -M man turnserver to see the man page. In the docs/html subdirectory of the original archive tree, you will find the client library reference. After the installation, it will be placed in PREFIX/share/doc/turnserver/html. ================================= LOGS When the TURN Server starts, it makes efforts to create a log file turn_.log in the following directories: * /var/log * /log/ * /var/tmp * /tmp * current directory If all efforts failed (due to the system permission settings) then all log messages are sent only to the standard output of the process. This behavior can be controlled by --log-file, --syslog and --no-stdout-log options. ================================= HTTPS MANAGEMENT INTERFACE The turnserver process provides an HTTPS Web access as statistics and basic management interface. The turnserver listens to incoming HTTPS admin connections on the same ports as the main TURN/STUN listener. The Web admin pages are basic and self-explanatory. To make the HTTPS interface active, the database table admin_user must be populated with the admin user account(s). An admin user can be a superuser (if not assigned to a particular realm) or a restricted user (if assigned to a realm). The restricted admin users can perform only limited actions, within their corresponding realms. ================================= TELNET CLI The turnserver process provides a telnet CLI access as statistics and basic management interface. By default, the turnserver starts a telnet CLI listener on IP 127.0.0.1 and port 5766. That can be changed by the command-cline options of the turnserver process (see --cli-ip and --cli-port options). The full list of telnet CLI commands is provided in "help" command output in the telnet CLI. ================================= CLUSTERS TURN Server can be a part of the cluster installation. But, to support the "even port" functionality (RTP/RTCP streams pairs) the client requests from a particular IP must be delivered to the same TURN Server instance, so it requires some networking setup massaging for the cluster. The reason is that the RTP and RTCP relaying endpoints must be allocated on the same relay IP. It would be possible to design a scheme with the application-level requests forwarding (and we may do that later) but it would affect the performance. ================================= FILES /etc/turnserver.conf /var/db/turndb /usr/local/var/db/turndb /var/lib/turn/turndb /usr/local/etc/turnserver.conf ================================= DIRECTORIES /usr/local/share/turnserver /usr/local/share/doc/turnserver /usr/local/share/examples/turnserver ================================= STANDARDS obsolete STUN RFC 3489 new STUN RFC 5389 TURN RFC 5766 TURN-TCP extension RFC 6062 TURN IPv6 extension RFC 6156 STUN/TURN test vectors RFC 5769 STUN NAT behavior discovery RFC 5780 ================================= SEE ALSO turnadmin, turnutils ====================================== WEB RESOURCES project page: https://github.com/coturn/coturn/ Wiki page: https://github.com/coturn/coturn/wiki forum: https://groups.google.com/forum/?fromgroups=#!forum/turn-server-project-rfc5766-turn-server ====================================== AUTHORS Oleg Moskalenko Gabor Kovesdan http://kovesdan.org/ Daniel Pocock http://danielpocock.com/ John Selbie (jselbie@gmail.com) Lee Sylvester Erik Johnston Roman Lisagor Vladimir Tsanev Po-sheng Lin Peter Dunkley Mutsutoshi Yoshimoto Federico Pinna Bradley T. Hughes Mihaly Meszaros turnserver-4.5.1.1/README.turnadmin0000664000175000017500000001617313436577655015502 0ustar misimisiGENERAL INFORMATION turnadmin is a TURN administration tool. This tool can be used to manage the user accounts (add/remove users, generate TURN keys for the users). For security reasons, we do not recommend storing passwords openly. The better option is to use pre-processed "keys" which are then used for authentication. These keys are generated by turnadmin. Turnadmin is a link to turnserver binary, but turnadmin performs different functions. Options note: turnadmin has long and short option names, for most options. Some options have only long form, some options have only short form. Their syntax somewhat different, if an argument is required: The short form must be used as this (for example): $ turnadmin -u ... The long form equivalent must use the "=" character: $ turnadmin --user= ... If this is a flag option (no argument required) then their usage are the same, for example: $ turnadmin -k ... is equivalent to: $ turnadmin --key ... You have always the use the -r option with commands for long term credentials - because data for multiple realms can be stored in the same database. ===================================== NAME turnadmin - a TURN relay administration tool. SYNOPSIS $ turnadmin [command] [options] $ turnadmin [ -h | --help] DESCRIPTION Commands: -P, --generate-encrypted-password Generate and print to the standard output an encrypted form of a password (for web admin user or CLI). The value then can be used as a safe key for the password storage on disk or in the database. Every invocation for the same password produces a different result. The for mat of the encrypted password is: $5$<...salt...>$<...sha256(salt+password)...>. Salt is 16 characters, the sha256 output is 64 characters. Character 5 is the algorithm id (sha256). Only sha256 is supported as the hash function. -k, --key Generate key for a long-term credentials mechanism user. -a, --add Add or update a long-term user. -A, --add-admin Add or update an admin user. -d, --delete Delete a long-term user. -D, --delete-admin Delete an admin user. -l, --list List long-term users in the database. -L, --list-admin List admin users in the database. -s, --set-secret= Add shared secret for TURN RESP API -S, --show-secret Show stored shared secrets for TURN REST API -X, --delete-secret= Delete a shared secret. --delete-all_secrets Delete all shared secrets for REST API. -O, --add-origin Add origin-to-realm relation. -R, --del-origin Delete origin-to-realm relation. -I, --list-origins List origin-to-realm relations. -g, --set-realm-option Set realm params: max-bps, total-quota, user-quota. -G, --list-realm-options List realm params. -E, --generate-encrypted-password-aes Generate and print to the standard output an encrypted form of password with AES-128 Options with required values: -b, --db, --userdb SQLite user database file name (default - /var/db/turndb or /usr/local/var/db/turndb or /var/lib/turn/turndb). See the same option in the turnserver section. -e, --psql-userdb PostgreSQL user database connection string. See the --psql-userdb option in the turnserver section. -M, --mysql-userdb MySQL user database connection string. See the --mysql-userdb option in the turnserver section. -J, --mongo-userdb MongoDB user database connection string. See the --mysql-mongo option in the turnserver section. -N, --redis-userdb Redis user database connection string. See the --redis-userdb option in the turnserver section. -u, --user User name. -r, --realm Realm. -p, --password Password. -x, --key-path Generates a 128 bit key into the given path. -f, --file-key-path Contains a 128 bit key in the given path. -v, --verify Verify a given base64 encrypted type password. -o, --origin Origin --max-bps Set value of realm's max-bps parameter. --total-quota Set value of realm's total-quota parameter. --user-quota Set value of realm's user-quota parameter. -h, --help Help. Command examples: Generate an encrypted form of a password: $ turnadmin -P -p Generate a key: $ turnadmin -k -u -r -p Add/update a user in the in the database: $ turnadmin -a [-b | -e | -M | -N ] -u -r -p Delete a user from the database: $ turnadmin -d [-b | -e | -M | -N ] -u -r List all long-term users in MySQL database: $ turnadmin -l --mysql-userdb="" -r List all admin users in Redis database: $ turnadmin -L --redis-userdb="" Set secret in MySQL database: $ turnadmin -s --mysql-userdb="" -r Show secret stored in PostgreSQL database: $ turnadmin -S --psql-userdb="" -r Set origin-to-realm relation in MySQL database: $ turnadmin --mysql-userdb="" -r -o Delete origin-to-realm relation from Redis DB: $ turnadmin --redis-userdb="" -o List all origin-to-realm relations in Redis DB: $ turnadmin --redis-userdb="" -I List the origin-to-realm relations in PostgreSQL DB for a single realm: $ turnadmin --psql-userdb="" -I -r Create new key file for mysql password encryption: $ turnadmin -E --key-path Create encrypted mysql password: $ turnadmin -E --file-key-path -p Verify/decrypt encrypted password: $ turnadmin --file-key-path -v Help: $ turnadmin -h ======================================= DOCS After installation, run the command: $ man turnadmin or in the project root directory: $ man -M man turnadmin to see the man page. ===================================== FILES /etc/turnserver.conf /var/db/turndb /usr/local/var/db/turndb /var/lib/turn/turndb /usr/local/etc/turnserver.conf ===================================== DIRECTORIES /usr/local/share/turnserver /usr/local/share/doc/turnserver /usr/local/share/examples/turnserver ====================================== SEE ALSO turnserver, turnutils ====================================== WEB RESOURCES project page: https://github.com/coturn/coturn/ Wiki page: https://github.com/coturn/coturn/wiki forum: https://groups.google.com/forum/?fromgroups=#!forum/turn-server-project-rfc5766-turn-server/ ====================================== AUTHORS Oleg Moskalenko Gabor Kovesdan http://kovesdan.org/ Daniel Pocock http://danielpocock.com/ John Selbie (jselbie@gmail.com) Lee Sylvester Erik Johnston Roman Lisagor Vladimir Tsanev Po-sheng Lin Peter Dunkley Mutsutoshi Yoshimoto Federico Pinna Bradley T. Hughes Mihaly Meszaros turnserver-4.5.1.1/README.md0000664000175000017500000001401013436577655014065 0ustar misimisi[![Build Status](https://travis-ci.org/coturn/coturn.svg?branch=master)](https://travis-ci.org/coturn/coturn) **_This project evolved from rfc5766-turn-server project (https://code.google.com/p/rfc5766-turn-server/). There are many new advanced TURN specs which are going far beyond the original RFC 5766 document. This project takes the code of rfc5766-turn-server as the starter, and adds new advanced features to it._** [Downloads page](https://github.com/coturn/coturn/wiki/Downloads) [Wiki pages](https://github.com/coturn/coturn/wiki/) # Free open source implementation of TURN and STUN Server # The TURN Server is a VoIP media traffic NAT traversal server and gateway. It can be used as a general-purpose network traffic TURN server and gateway, too. On-line management interface (over telnet or over HTTPS) for the TURN server is available. The implementation also includes some extra experimental features. Supported RFCs: TURN specs: * RFC 5766 - base TURN specs * RFC 6062 - TCP relaying TURN extension * RFC 6156 - IPv6 extension for TURN * RFC 7443 - ALPN support for STUN & TURN * RFC 7635 - oAuth third-party TURN/STUN authorization * DTLS support (http://tools.ietf.org/html/draft-petithuguenin-tram-turn-dtls-00). * Mobile ICE (MICE) support (http://tools.ietf.org/html/draft-wing-tram-turn-mobility-02). * TURN REST API (http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00) * Origin field in TURN (Multi-tenant TURN Server) (https://tools.ietf.org/html/draft-ietf-tram-stun-origin-06) * TURN Bandwidth draft specs (http://tools.ietf.org/html/draft-thomson-tram-turn-bandwidth-01) * TURN-bis (with dual allocation) draft specs (http://tools.ietf.org/html/draft-ietf-tram-turnbis-04). STUN specs: * RFC 3489 - "classic" STUN * RFC 5389 - base "new" STUN specs * RFC 5769 - test vectors for STUN protocol testing * RFC 5780 - NAT behavior discovery support * RFC 7443 - ALPN support for STUN & TURN * RFC 7635 - oAuth third-party TURN/STUN authorization Supported ICE and related specs: * RFC 5245 - ICE * RFC 5768 – ICE–SIP * RFC 6336 – ICE–IANA Registry * RFC 6544 – ICE–TCP * RFC 5928 - TURN Resolution Mechanism The implementation fully supports the following client-to-TURN-server protocols: * UDP (per RFC 5766) * TCP (per RFC 5766 and RFC 6062) * TLS (per RFC 5766 and RFC 6062): TLS1.0/TLS1.1/TLS1.2; ECDHE is supported. * DTLS (http://tools.ietf.org/html/draft-petithuguenin-tram-turn-dtls-00): DTLS versions 1.0 and 1.2. * SCTP (experimental implementation). Supported relay protocols: * UDP (per RFC 5766) * TCP (per RFC 6062) Supported user databases (for user repository, with passwords or keys, if authentication is required): * SQLite * MySQL * PostgreSQL * Redis * MongoDB Redis can also be used for status and statistics storage and notification. Supported message integrity digest algorithms: * HMAC-SHA1, with MD5-hashed keys (as required by STUN and TURN standards) Supported TURN authentication mechanisms: * 'classic' long-term credentials mechanism; * TURN REST API (a modification of the long-term mechanism, for time-limited secret-based authentication, for WebRTC applications: http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00); * experimental third-party oAuth-based client authorization option; When used as a part of an ICE solution, for VoIP connectivity, this TURN server can handle thousands simultaneous calls per CPU (when TURN protocol is used) or tens of thousands calls when only STUN protocol is used. For virtually unlimited scalability a load balancing scheme can be used. The load balancing can be implemented with the following tools (either one or a combination of them): * DNS SRV based load balancing; * built-in 300 ALTERNATE-SERVER mechanism (requires 300 response support by the TURN client); * network load-balancer server. Traffic bandwidth limitation and congestion avoidance algorithms implemented. The supported project target platforms are: * Linux (Debian, Ubuntu, Mint, CentOS, Fedora, Redhat, Amazon Linux, Arch Linux, OpenSUSE) * BSD (FreeBSD, NetBSD, OpenBSD, DragonFlyBSD) * Solaris 11 * Mac OS X * Cygwin (for non-production R&D purposes) Other server platforms can be supported by request. Any client platform is supported, including Android, iOS, Linux, OS X, Windows, and Windows Phone. This project can be successfully used on other `*NIX` platforms, too, but that is not officially supported. The implementation is supposed to be simple, easy to install and configure. The project focuses on performance, scalability and simplicity. The aim is to provide an enterprise-grade TURN solution. To achieve high performance and scalability, the TURN server is implemented with the following features: * High-performance industrial-strength Network IO engine libevent2 is used * Configurable multi-threading model implemented to allow full usage of available CPU resources (if OS allows multi-threading) * Multiple listening and relay addresses can be configured * Efficient memory model used * The TURN project code can be used in a custom proprietary networking environment. In the TURN server code, an abstract networking API is used. Only couple files in the project have to be re-written to plug-in the TURN server into a proprietary environment. With this project, only implementation for standard UNIX Networking/IO API is provided, but the user can implement any other environment. The TURN server code was originally developed for a high-performance proprietary corporate environment, then adopted for UNIX Networking API * The TURN server works as a user space process, without imposing any special requirements on the system To download the TURN Server software, the client messaging library and the test programs, click the tab "Downloads". Contact information: https://groups.google.com/forum/#!forum/turn-server-project-rfc5766-turn-server email:mom040267@gmail.com ### Feedback is very welcome (bugs, issues, suggestions, stories, questions). ### ### Volunteers are welcome, too. ### turnserver-4.5.1.1/docker/0000775000175000017500000000000013436577655014061 5ustar misimisiturnserver-4.5.1.1/docker/docker-compose-redis.yml0000664000175000017500000000223213436577655020621 0ustar misimisiversion: "3" services: # Redis redis: build: context: ./redis restart: unless-stopped volumes: - redis-data:/data env_file: - redis/redis.env networks: - backend # coTURN coturn: build: context: ./coturn restart: always volumes: - ${PWD}/coturn/turnserver.conf:/etc/turnserver.conf - ${PWD}/coturn/privkey.pem:/etc/ssl/private/privkey.pem - ${PWD}/coturn/cert.pem:/etc/ssl/certs/cert.pem ports: ## STUN/TURN - "3478:3478" - "3478:3478/udp" - "3479:3479" - "3479:3479/udp" - "80:80" - "80:80/udp" ## STUN/TURN SSL - "5349:5349" - "5349:5349/udp" - "5350:5350" - "5350:5350/udp" - "443:443" - "443:443/udp" # Relay Ports # - "49152-65535:49152-65535" # - "49152-65535:49152-65535/udp" networks: - frontend - backend depends_on: - redis env_file: - coturn/coturn.env # DB - redis/redis.env volumes: redis-data: networks: frontend: driver: bridge ipam: driver: default config: - subnet: 172.16.238.0/24 backend: internal: true turnserver-4.5.1.1/docker/docker-compose-mongodb.yml0000664000175000017500000000223613436577655021144 0ustar misimisiversion: "3" services: # MongoDB mongodb: image: mongo restart: unless-stopped volumes: - mongodb-data:/data/db env_file: - mongodb/mongodb.env networks: - backend # coTURN coturn: build: context: ./coturn restart: always volumes: - ${PWD}/coturn/turnserver.conf:/etc/turnserver.conf - ${PWD}/coturn/privkey.pem:/etc/ssl/private/privkey.pem - ${PWD}/coturn/cert.pem:/etc/ssl/certs/cert.pem ports: ## STUN/TURN - "3478:3478" - "3478:3478/udp" - "3479:3479" - "3479:3479/udp" - "80:80" - "80:80/udp" ## STUN/TURN SSL - "5349:5349" - "5349:5349/udp" - "5350:5350" - "5350:5350/udp" - "443:443" - "443:443/udp" # Relay Ports # - "49152-65535:49152-65535" # - "49152-65535:49152-65535/udp" networks: - frontend - backend depends_on: - mongodb env_file: - coturn/coturn.env # DB - mongodb/mongodb.env volumes: mongodb-data: networks: frontend: driver: bridge ipam: driver: default config: - subnet: 172.16.238.0/24 backend: internal: true turnserver-4.5.1.1/docker/mongodb/0000775000175000017500000000000013436577655015506 5ustar misimisiturnserver-4.5.1.1/docker/mongodb/mongodb.env0000664000175000017500000000014713436577655017647 0ustar misimisi#MONGO_INITDB_ROOT_USERNAME=coturn #MONGO_INITDB_ROOT_PASSWORD=CHANGE_ME #MONGO_INITDB_DATABASE=coturn turnserver-4.5.1.1/docker/docker-compose-postgresql.yml0000664000175000017500000000233713436577655021724 0ustar misimisiversion: "3" services: # PostgreSQL postgresql: build: context: ./postgresql restart: unless-stopped volumes: - postgresql-data:/var/lib/postgresql/data env_file: - postgresql/postgresql.env networks: - backend # coTURN coturn: build: context: ./coturn restart: always volumes: - ${PWD}/coturn/turnserver.conf:/etc/turnserver.conf - ${PWD}/coturn/privkey.pem:/etc/ssl/private/privkey.pem - ${PWD}/coturn/cert.pem:/etc/ssl/certs/cert.pem ports: ## STUN/TURN - "3478:3478" - "3478:3478/udp" - "3479:3479" - "3479:3479/udp" - "80:80" - "80:80/udp" ## STUN/TURN SSL - "5349:5349" - "5349:5349/udp" - "5350:5350" - "5350:5350/udp" - "443:443" - "443:443/udp" # Relay Ports # - "49152-65535:49152-65535" # - "49152-65535:49152-65535/udp" networks: - frontend - backend depends_on: - postgresql env_file: - coturn/coturn.env # DB - postgresql/postgresql.env volumes: postgresql-data: networks: frontend: driver: bridge ipam: driver: default config: - subnet: 172.16.238.0/24 backend: internal: true turnserver-4.5.1.1/docker/README.docker0000664000175000017500000000077413436577655016217 0ustar misimisiBefore you begin * copy db schema run ./cp_schema.sh * edit turnserver/turnserver.cfg according your db selection (mysql or postgresql or redis or mongodb) # start docker-compose -f docker-compose-all.yml up --build --detach # restart Notice: May restart needed for coturn container, if it could not access database yet, due initialization delay. docker restart docker_coturn_1 # stop docker-compose -f docker-compose-all.yml down # Or Stop with volume removal docker-compose down --volumes turnserver-4.5.1.1/docker/mysql/0000775000175000017500000000000013436577655015226 5ustar misimisiturnserver-4.5.1.1/docker/mysql/init-coturn-db.sql0000664000175000017500000000005413436577655020604 0ustar misimisiALTER DATABASE coturn CHARACTER SET latin1; turnserver-4.5.1.1/docker/mysql/schema.sql0000664000175000017500000000203213436577655017204 0ustar misimisi CREATE TABLE turnusers_lt ( realm varchar(127) default '', name varchar(512), hmackey char(128), PRIMARY KEY (realm,name) ); CREATE TABLE turn_secret ( realm varchar(127) default '', value varchar(256), primary key (realm,value) ); CREATE TABLE allowed_peer_ip ( realm varchar(127) default '', ip_range varchar(256), primary key (realm,ip_range) ); CREATE TABLE denied_peer_ip ( realm varchar(127) default '', ip_range varchar(256), primary key (realm,ip_range) ); CREATE TABLE turn_origin_to_realm ( origin varchar(127), realm varchar(127), primary key (origin) ); CREATE TABLE turn_realm_option ( realm varchar(127) default '', opt varchar(32), value varchar(128), primary key (realm,opt) ); CREATE TABLE oauth_key ( kid varchar(128), ikm_key varchar(256), timestamp bigint default 0, lifetime integer default 0, as_rs_alg varchar(64) default '', realm varchar(127), primary key (kid) ); CREATE TABLE admin_user ( name varchar(32), realm varchar(127), password varchar(127), primary key (name) ); turnserver-4.5.1.1/docker/mysql/Dockerfile0000664000175000017500000000021413436577655017215 0ustar misimisi### init db with coturn schema FROM mariadb ADD init-coturn-db.sql /docker-entrypoint-initdb.d ADD schema.sql /docker-entrypoint-initdb.d turnserver-4.5.1.1/docker/mysql/mysql.env0000664000175000017500000000014113436577655017101 0ustar misimisiMYSQL_ROOT_PASSWORD=CHANGE_ME MYSQL_USER=coturn MYSQL_PASSWORD=CHANGE_ME MYSQL_DATABASE=coturn turnserver-4.5.1.1/docker/docker-compose-mysql.yml0000664000175000017500000000226013436577655020661 0ustar misimisiversion: "3" services: # MySQL mariadb mysql: build: context: ./mysql restart: unless-stopped volumes: - mysql-data:/var/lib/mysql/data env_file: - mysql/mysql.env networks: - backend # coTURN coturn: build: context: ./coturn restart: always volumes: - ${PWD}/coturn/turnserver.conf:/etc/turnserver.conf - ${PWD}/coturn/privkey.pem:/etc/ssl/private/privkey.pem - ${PWD}/coturn/cert.pem:/etc/ssl/certs/cert.pem ports: ## STUN/TURN - "3478:3478" - "3478:3478/udp" - "3479:3479" - "3479:3479/udp" - "80:80" - "80:80/udp" ## STUN/TURN SSL - "5349:5349" - "5349:5349/udp" - "5350:5350" - "5350:5350/udp" - "443:443" - "443:443/udp" # Relay Ports # - "49152-65535:49152-65535" # - "49152-65535:49152-65535/udp" networks: - frontend - backend depends_on: - mysql env_file: - coturn/coturn.env # DB - mysql/mysql.env volumes: mysql-data: networks: frontend: driver: bridge ipam: driver: default config: - subnet: 172.16.238.0/24 backend: internal: true turnserver-4.5.1.1/docker/docker-compose-all.yml0000664000175000017500000000370613436577655020272 0ustar misimisiversion: "3" services: # MySQL mariadb mysql: build: context: ./mysql restart: unless-stopped volumes: - mysql-data:/var/lib/mysql/data env_file: - mysql/mysql.env networks: - backend # PostgreSQL postgresql: build: context: ./postgresql restart: unless-stopped volumes: - postgresql-data:/var/lib/postgresql/data env_file: - postgresql/postgresql.env networks: - backend # Redis redis: build: context: ./redis restart: unless-stopped volumes: - redis-data:/data env_file: - redis/redis.env networks: - backend # MongoDB mongodb: image: mongo restart: unless-stopped volumes: - mongodb-data:/data/db env_file: - mongodb/mongodb.env networks: - backend # coTURN coturn: build: context: ./coturn restart: always volumes: - ${PWD}/coturn/turnserver.conf:/etc/turnserver.conf - ${PWD}/coturn/privkey.pem:/etc/ssl/private/privkey.pem - ${PWD}/coturn/cert.pem:/etc/ssl/certs/cert.pem ports: ## STUN/TURN - "3478:3478" - "3478:3478/udp" - "3479:3479" - "3479:3479/udp" - "80:80" - "80:80/udp" ## STUN/TURN SSL - "5349:5349" - "5349:5349/udp" - "5350:5350" - "5350:5350/udp" - "443:443" - "443:443/udp" # Relay Ports # - "49152-65535:49152-65535" # - "49152-65535:49152-65535/udp" networks: - frontend - backend depends_on: - mysql - postgresql - redis - mongodb env_file: - coturn/coturn.env # DB - mysql/mysql.env - postgresql/postgresql.env - redis/redis.env - mongodb/mongodb.env volumes: mysql-data: postgresql-data: redis-data: mongodb-data: networks: frontend: driver: bridge ipam: driver: default config: - subnet: 172.16.238.0/24 backend: internal: true turnserver-4.5.1.1/docker/postgresql/0000775000175000017500000000000013436577655016264 5ustar misimisiturnserver-4.5.1.1/docker/postgresql/postgresql.env0000664000175000017500000000010413436577655021174 0ustar misimisiPOSTGRES_USER=coturn POSTGRES_PASSWORD=CHANGE_ME POSTGRES_DB=coturn turnserver-4.5.1.1/docker/postgresql/schema.sql0000664000175000017500000000203213436577655020242 0ustar misimisi CREATE TABLE turnusers_lt ( realm varchar(127) default '', name varchar(512), hmackey char(128), PRIMARY KEY (realm,name) ); CREATE TABLE turn_secret ( realm varchar(127) default '', value varchar(256), primary key (realm,value) ); CREATE TABLE allowed_peer_ip ( realm varchar(127) default '', ip_range varchar(256), primary key (realm,ip_range) ); CREATE TABLE denied_peer_ip ( realm varchar(127) default '', ip_range varchar(256), primary key (realm,ip_range) ); CREATE TABLE turn_origin_to_realm ( origin varchar(127), realm varchar(127), primary key (origin) ); CREATE TABLE turn_realm_option ( realm varchar(127) default '', opt varchar(32), value varchar(128), primary key (realm,opt) ); CREATE TABLE oauth_key ( kid varchar(128), ikm_key varchar(256), timestamp bigint default 0, lifetime integer default 0, as_rs_alg varchar(64) default '', realm varchar(127), primary key (kid) ); CREATE TABLE admin_user ( name varchar(32), realm varchar(127), password varchar(127), primary key (name) ); turnserver-4.5.1.1/docker/postgresql/Dockerfile0000664000175000017500000000013113436577655020251 0ustar misimisi### init db with coturn schema FROM postgres ADD schema.sql /docker-entrypoint-initdb.d turnserver-4.5.1.1/docker/redis/0000775000175000017500000000000013436577655015167 5ustar misimisiturnserver-4.5.1.1/docker/redis/redis.env0000664000175000017500000000000013436577655016775 0ustar misimisiturnserver-4.5.1.1/docker/redis/redis.conf0000664000175000017500000000006113436577655017141 0ustar misimisitimeout 0 tcp-keepalive 60 requirepass CHANGE_ME turnserver-4.5.1.1/docker/redis/Dockerfile0000664000175000017500000000022613436577655017161 0ustar misimisi### init db with coturn schema FROM redis COPY redis.conf /usr/local/etc/redis/redis.conf CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ] turnserver-4.5.1.1/docker/coturn/0000775000175000017500000000000013436577655015373 5ustar misimisiturnserver-4.5.1.1/docker/coturn/turnserver.conf0000664000175000017500000006172613436577655020475 0ustar misimisi# Coturn TURN SERVER configuration file # # Boolean values note: where boolean value is supposed to be used, # you can use '0', 'off', 'no', 'false', 'f' as 'false, # and you can use '1', 'on', 'yes', 'true', 't' as 'true' # If the value is missed, then it means 'true'. # # Listener interface device (optional, Linux only). # NOT RECOMMENDED. # #listening-device=eth0 # TURN listener port for UDP and TCP (Default: 3478). # Note: actually, TLS & DTLS sessions can connect to the # "plain" TCP & UDP port(s), too - if allowed by configuration. # listening-port=3478 # TURN listener port for TLS (Default: 5349). # Note: actually, "plain" TCP & UDP sessions can connect to the TLS & DTLS # port(s), too - if allowed by configuration. The TURN server # "automatically" recognizes the type of traffic. Actually, two listening # endpoints (the "plain" one and the "tls" one) are equivalent in terms of # functionality; but we keep both endpoints to satisfy the RFC 5766 specs. # For secure TCP connections, we currently support SSL version 3 and # TLS version 1.0, 1.1 and 1.2. # For secure UDP connections, we support DTLS version 1. # tls-listening-port=5349 # Alternative listening port for UDP and TCP listeners; # default (or zero) value means "listening port plus one". # This is needed for RFC 5780 support # (STUN extension specs, NAT behavior discovery). The TURN Server # supports RFC 5780 only if it is started with more than one # listening IP address of the same family (IPv4 or IPv6). # RFC 5780 is supported only by UDP protocol, other protocols # are listening to that endpoint only for "symmetry". # #alt-listening-port=0 # Alternative listening port for TLS and DTLS protocols. # Default (or zero) value means "TLS listening port plus one". # #alt-tls-listening-port=0 # Listener IP address of relay server. Multiple listeners can be specified. # If no IP(s) specified in the config file or in the command line options, # then all IPv4 and IPv6 system IPs will be used for listening. # #listening-ip=172.17.19.101 #listening-ip=10.207.21.238 #listening-ip=2607:f0d0:1002:51::4 # Auxiliary STUN/TURN server listening endpoint. # Aux servers have almost full TURN and STUN functionality. # The (minor) limitations are: # # 1) Auxiliary servers do not have alternative ports and # they do not support STUN RFC 5780 functionality (CHANGE REQUEST). # # 2) Auxiliary servers also are never returning ALTERNATIVE-SERVER reply. # # Valid formats are 1.2.3.4:5555 for IPv4 and [1:2::3:4]:5555 for IPv6. # # There may be multiple aux-server options, each will be used for listening # to client requests. # #aux-server=172.17.19.110:33478 #aux-server=[2607:f0d0:1002:51::4]:33478 # (recommended for older Linuxes only) # Automatically balance UDP traffic over auxiliary servers (if configured). # The load balancing is using the ALTERNATE-SERVER mechanism. # The TURN client must support 300 ALTERNATE-SERVER response for this # functionality. # #udp-self-balance # Relay interface device for relay sockets (optional, Linux only). # NOT RECOMMENDED. # #relay-device=eth1 # Relay address (the local IP address that will be used to relay the # packets to the peer). # Multiple relay addresses may be used. # The same IP(s) can be used as both listening IP(s) and relay IP(s). # # If no relay IP(s) specified, then the turnserver will apply the default # policy: it will decide itself which relay addresses to be used, and it # will always be using the client socket IP address as the relay IP address # of the TURN session (if the requested relay address family is the same # as the family of the client socket). # #relay-ip=172.17.19.105 #relay-ip=2607:f0d0:1002:51::5 # For Amazon EC2 users: # # TURN Server public/private address mapping, if the server is behind NAT. # In that situation, if a -X is used in form "-X " then that ip will be reported # as relay IP address of all allocations. This scenario works only in a simple case # when one single relay address is be used, and no RFC5780 functionality is required. # That single relay address must be mapped by NAT to the 'external' IP. # The "external-ip" value, if not empty, is returned in XOR-RELAYED-ADDRESS field. # For that 'external' IP, NAT must forward ports directly (relayed port 12345 # must be always mapped to the same 'external' port 12345). # # In more complex case when more than one IP address is involved, # that option must be used several times, each entry must # have form "-X ", to map all involved addresses. # RFC5780 NAT discovery STUN functionality will work correctly, # if the addresses are mapped properly, even when the TURN server itself # is behind A NAT. # # By default, this value is empty, and no address mapping is used. # #external-ip=60.70.80.91 # #OR: # #external-ip=60.70.80.91/172.17.19.101 #external-ip=60.70.80.92/172.17.19.102 #external-ip=60.70.80.92/172.17.19.102 external-ip=193.224.22.37 # Number of the relay threads to handle the established connections # (in addition to authentication thread and the listener thread). # If explicitly set to 0 then application runs relay process in a # single thread, in the same thread with the listener process # (the authentication thread will still be a separate thread). # # If this parameter is not set, then the default OS-dependent # thread pattern algorithm will be employed. Usually the default # algorithm is the most optimal, so you have to change this option # only if you want to make some fine tweaks. # # In the older systems (Linux kernel before 3.9), # the number of UDP threads is always one thread per network listening # endpoint - including the auxiliary endpoints - unless 0 (zero) or # 1 (one) value is set. # #relay-threads=0 # Lower and upper bounds of the UDP relay endpoints: # (default values are 49152 and 65535) # min-port=49152 max-port=65535 # Uncomment to run TURN server in 'normal' 'moderate' verbose mode. # By default the verbose mode is off. verbose # Uncomment to run TURN server in 'extra' verbose mode. # This mode is very annoying and produces lots of output. # Not recommended under any normal circumstances. # #Verbose # Uncomment to use fingerprints in the TURN messages. # By default the fingerprints are off. # fingerprint # Uncomment to use long-term credential mechanism. # By default no credentials mechanism is used (any user allowed). # lt-cred-mech # This option is opposite to lt-cred-mech. # (TURN Server with no-auth option allows anonymous access). # If neither option is defined, and no users are defined, # then no-auth is default. If at least one user is defined, # in this file or in command line or in usersdb file, then # lt-cred-mech is default. # #no-auth # TURN REST API flag. # (Time Limited Long Term Credential) # Flag that sets a special authorization option that is based upon authentication secret. # # This feature's purpose is to support "TURN Server REST API", see # "TURN REST API" link in the project's page # https://github.com/coturn/coturn/ # # This option is used with timestamp: # # usercombo -> "timestamp:userid" # turn user -> usercombo # turn password -> base64(hmac(secret key, usercombo)) # # This allows TURN credentials to be accounted for a specific user id. # If you don't have a suitable id, the timestamp alone can be used. # This option is just turning on secret-based authentication. # The actual value of the secret is defined either by option static-auth-secret, # or can be found in the turn_secret table in the database (see below). # # Read more about it: # - https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00 # - https://www.ietf.org/proceedings/87/slides/slides-87-behave-10.pdf # # Be aware that use-auth-secret overrides some part of lt-cred-mech. # Notice that this feature depends internally on lt-cred-mech, so if you set # use-auth-secret then it enables internally automatically lt-cred-mech option # like if you enable both. # # You can use only one of the to auth mechanisms in the same time because, # both mechanism use the username and password validation in different way. # # This way be aware that you can't use both auth mechnaism in the same time! # Use in config either the lt-cred-mech or the use-auth-secret # to avoid any confusion. # #use-auth-secret # 'Static' authentication secret value (a string) for TURN REST API only. # If not set, then the turn server # will try to use the 'dynamic' value in turn_secret table # in user database (if present). The database-stored value can be changed on-the-fly # by a separate program, so this is why that other mode is 'dynamic'. # #static-auth-secret=north # Server name used for # the oAuth authentication purposes. # The default value is the realm name. # #server-name=blackdow.carleon.gov # Flag that allows oAuth authentication. # #oauth # 'Static' user accounts for long term credentials mechanism, only. # This option cannot be used with TURN REST API. # 'Static' user accounts are NOT dynamically checked by the turnserver process, # so that they can NOT be changed while the turnserver is running. # #user=username1:key1 #user=username2:key2 # OR: #user=username1:password1 #user=username2:password2 # # Keys must be generated by turnadmin utility. The key value depends # on user name, realm, and password: # # Example: # $ turnadmin -k -u ninefingers -r north.gov -p youhavetoberealistic # Output: 0xbc807ee29df3c9ffa736523fb2c4e8ee # ('0x' in the beginning of the key is what differentiates the key from # password. If it has 0x then it is a key, otherwise it is a password). # # The corresponding user account entry in the config file will be: # #user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee # Or, equivalently, with open clear password (less secure): #user=ninefingers:youhavetoberealistic # # SQLite database file name. # # Default file name is /var/db/turndb or /usr/local/var/db/turndb or # /var/lib/turn/turndb. # #userdb=/var/db/turndb # PostgreSQL database connection string in the case that we are using PostgreSQL # as the user database. # This database can be used for long-term credential mechanism # and it can store the secret value for secret-based timed authentication in TURN RESP API. # See http://www.postgresql.org/docs/8.4/static/libpq-connect.html for 8.x PostgreSQL # versions connection string format, see # http://www.postgresql.org/docs/9.2/static/libpq-connect.html#LIBPQ-CONNSTRING # for 9.x and newer connection string formats. # #psql-userdb="host= dbname= user= password= connect_timeout=30" #psql-userdb="host=postgresql dbname=coturn user=coturn password=CHANGE_ME connect_timeout=30" # MySQL database connection string in the case that we are using MySQL # as the user database. # This database can be used for long-term credential mechanism # and it can store the secret value for secret-based timed authentication in TURN RESP API. # # Optional connection string parameters for the secure communications (SSL): # ca, capath, cert, key, cipher # (see http://dev.mysql.com/doc/refman/5.1/en/ssl-options.html for the # command options description). # # Use string format as below (space separated parameters, all optional): # #mysql-userdb="host= dbname= user= password= port= connect_timeout= read_timeout=" mysql-userdb="host=mysql dbname=coturn user=coturn password=CHANGE_ME port=3306 connect_timeout=10 read_timeout=10" # If you want to use in the MySQL connection string the password in encrypted format, # then set in this option the MySQL password encryption secret key file. # # Warning: If this option is set, then mysql password must be set in "mysql-userdb" in encrypted format! # If you want to use cleartext password then do not set this option! # # This is the file path which contain secret key of aes encryption while using password encryption. # #secret-key-file=/path/ # MongoDB database connection string in the case that we are using MongoDB # as the user database. # This database can be used for long-term credential mechanism # and it can store the secret value for secret-based timed authentication in TURN RESP API. # Use string format is described at http://hergert.me/docs/mongo-c-driver/mongoc_uri.html # #mongo-userdb="mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]" #mongo-userdb="mongodb://coturn:CHANGE_ME@mongodb/coturn" #mongo-userdb="mongodb://mongodb/coturn" # Redis database connection string in the case that we are using Redis # as the user database. # This database can be used for long-term credential mechanism # and it can store the secret value for secret-based timed authentication in TURN RESP API. # Use string format as below (space separated parameters, all optional): # #redis-userdb="ip= dbname= password= port= connect_timeout=" #redis-userdb="ip=redis dbname=2 password=CHANGE_ME connect_timeout=30" # Redis status and statistics database connection string, if used (default - empty, no Redis stats DB used). # This database keeps allocations status information, and it can be also used for publishing # and delivering traffic and allocation event notifications. # The connection string has the same parameters as redis-userdb connection string. # Use string format as below (space separated parameters, all optional): # #redis-statsdb="ip= dbname= password= port= connect_timeout=" #redis-statsdb="ip=redis dbname=2 password=CHANGE_ME connect_timeout=30" # The default realm to be used for the users when no explicit # origin/realm relationship was found in the database, or if the TURN # server is not using any database (just the commands-line settings # and the userdb file). Must be used with long-term credentials # mechanism or with TURN REST API. # # Note: If default realm is not specified at all, then realm falls back to the host domain name. # If domain name is empty string, or '(None)', then it is initialized to am empty string. # #realm=mycompany.org realm=example.org # The flag that sets the origin consistency # check: across the session, all requests must have the same # main ORIGIN attribute value (if the ORIGIN was # initially used by the session). # #check-origin-consistency # Per-user allocation quota. # default value is 0 (no quota, unlimited number of sessions per user). # This option can also be set through the database, for a particular realm. # #user-quota=0 # Total allocation quota. # default value is 0 (no quota). # This option can also be set through the database, for a particular realm. # #total-quota=0 # Max bytes-per-second bandwidth a TURN session is allowed to handle # (input and output network streams are treated separately). Anything above # that limit will be dropped or temporary suppressed (within # the available buffer limits). # This option can also be set through the database, for a particular realm. # #max-bps=0 # # Maximum server capacity. # Total bytes-per-second bandwidth the TURN server is allowed to allocate # for the sessions, combined (input and output network streams are treated separately). # # bps-capacity=0 # Uncomment if no UDP client listener is desired. # By default UDP client listener is always started. # #no-udp # Uncomment if no TCP client listener is desired. # By default TCP client listener is always started. # #no-tcp # Uncomment if no TLS client listener is desired. # By default TLS client listener is always started. # #no-tls # Uncomment if no DTLS client listener is desired. # By default DTLS client listener is always started. # #no-dtls # Uncomment if no UDP relay endpoints are allowed. # By default UDP relay endpoints are enabled (like in RFC 5766). # #no-udp-relay # Uncomment if no TCP relay endpoints are allowed. # By default TCP relay endpoints are enabled (like in RFC 6062). # #no-tcp-relay # Uncomment if extra security is desired, # with nonce value having limited lifetime. # By default, the nonce value is unique for a session, # and has unlimited lifetime. # Set this option to limit the nonce lifetime. # It defaults to 600 secs (10 min) if no value is provided. After that delay, # the client will get 438 error and will have to re-authenticate itself. # #stale-nonce=600 # Uncomment if you want to set the maximum allocation # time before it has to be refreshed. # Default is 3600s. # #max-allocate-lifetime=3600 # Uncomment to set the lifetime for the channel. # Default value is 600 secs (10 minutes). # This value MUST not be changed for production purposes. # #channel-lifetime=600 # Uncomment to set the permission lifetime. # Default to 300 secs (5 minutes). # In production this value MUST not be changed, # however it can be useful for test purposes. # #permission-lifetime=300 # Certificate file. # Use an absolute path or path relative to the # configuration file. # #cert=/usr/local/etc/turn_server_cert.pem cert=/etc/ssl/certs/cert.pem # Private key file. # Use an absolute path or path relative to the # configuration file. # Use PEM file format. # #pkey=/usr/local/etc/turn_server_pkey.pem pkey=/etc/ssl/private/privkey.pem # Private key file password, if it is in encoded format. # This option has no default value. # #pkey-pwd=... # Allowed OpenSSL cipher list for TLS/DTLS connections. # Default value is "DEFAULT". # #cipher-list="DEFAULT" # CA file in OpenSSL format. # Forces TURN server to verify the client SSL certificates. # By default it is not set: there is no default value and the client # certificate is not checked. # # Example: #CA-file=/etc/ssh/id_rsa.cert # Curve name for EC ciphers, if supported by OpenSSL # library (TLS and DTLS). The default value is prime256v1, # if pre-OpenSSL 1.0.2 is used. With OpenSSL 1.0.2+, # an optimal curve will be automatically calculated, if not defined # by this option. # #ec-curve-name=prime256v1 # Use 566 bits predefined DH TLS key. Default size of the key is 1066. # #dh566 # Use 2066 bits predefined DH TLS key. Default size of the key is 1066. # #dh2066 # Use custom DH TLS key, stored in PEM format in the file. # Flags --dh566 and --dh2066 are ignored when the DH key is taken from a file. # #dh-file= # Flag to prevent stdout log messages. # By default, all log messages are going to both stdout and to # the configured log file. With this option everything will be # going to the configured log only (unless the log file itself is stdout). # #no-stdout-log # Option to set the log file name. # By default, the turnserver tries to open a log file in # /var/log, /var/tmp, /tmp and current directories directories # (which open operation succeeds first that file will be used). # With this option you can set the definite log file name. # The special names are "stdout" and "-" - they will force everything # to the stdout. Also, the "syslog" name will force everything to # the system log (syslog). # In the runtime, the logfile can be reset with the SIGHUP signal # to the turnserver process. # #log-file=/var/tmp/turn.log # Option to redirect all log output into system log (syslog). # syslog # This flag means that no log file rollover will be used, and the log file # name will be constructed as-is, without PID and date appendage. # This option can be used, for example, together with the logrotate tool. # #simple-log # Option to set the "redirection" mode. The value of this option # will be the address of the alternate server for UDP & TCP service in form of # [:]. The server will send this value in the attribute # ALTERNATE-SERVER, with error 300, on ALLOCATE request, to the client. # Client will receive only values with the same address family # as the client network endpoint address family. # See RFC 5389 and RFC 5766 for ALTERNATE-SERVER functionality description. # The client must use the obtained value for subsequent TURN communications. # If more than one --alternate-server options are provided, then the functionality # can be more accurately described as "load-balancing" than a mere "redirection". # If the port number is omitted, then the default port # number 3478 for the UDP/TCP protocols will be used. # Colon (:) characters in IPv6 addresses may conflict with the syntax of # the option. To alleviate this conflict, literal IPv6 addresses are enclosed # in square brackets in such resource identifiers, for example: # [2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478 . # Multiple alternate servers can be set. They will be used in the # round-robin manner. All servers in the pool are considered of equal weight and # the load will be distributed equally. For example, if we have 4 alternate servers, # then each server will receive 25% of ALLOCATE requests. A alternate TURN server # address can be used more than one time with the alternate-server option, so this # can emulate "weighting" of the servers. # # Examples: #alternate-server=1.2.3.4:5678 #alternate-server=11.22.33.44:56789 #alternate-server=5.6.7.8 #alternate-server=[2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478 # Option to set alternative server for TLS & DTLS services in form of # :. If the port number is omitted, then the default port # number 5349 for the TLS/DTLS protocols will be used. See the previous # option for the functionality description. # # Examples: #tls-alternate-server=1.2.3.4:5678 #tls-alternate-server=11.22.33.44:56789 #tls-alternate-server=[2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478 # Option to suppress TURN functionality, only STUN requests will be processed. # Run as STUN server only, all TURN requests will be ignored. # By default, this option is NOT set. # #stun-only # Option to suppress STUN functionality, only TURN requests will be processed. # Run as TURN server only, all STUN requests will be ignored. # By default, this option is NOT set. # #no-stun # This is the timestamp/username separator symbol (character) in TURN REST API. # The default value is ':'. # rest-api-separator=: # Flag that can be used to disallow peers on the loopback addresses (127.x.x.x and ::1). # This is an extra security measure. # no-loopback-peers # Flag that can be used to disallow peers on well-known broadcast addresses (224.0.0.0 and above, and FFXX:*). # This is an extra security measure. # #no-multicast-peers # Option to set the max time, in seconds, allowed for full allocation establishment. # Default is 60 seconds. # #max-allocate-timeout=60 # Option to allow or ban specific ip addresses or ranges of ip addresses. # If an ip address is specified as both allowed and denied, then the ip address is # considered to be allowed. This is useful when you wish to ban a range of ip # addresses, except for a few specific ips within that range. # # This can be used when you do not want users of the turn server to be able to access # machines reachable by the turn server, but would otherwise be unreachable from the # internet (e.g. when the turn server is sitting behind a NAT) # # Examples: # denied-peer-ip=83.166.64.0-83.166.95.255 # allowed-peer-ip=83.166.68.45 # File name to store the pid of the process. # Default is /var/run/turnserver.pid (if superuser account is used) or # /var/tmp/turnserver.pid . # #pidfile="/var/run/turnserver.pid" # Require authentication of the STUN Binding request. # By default, the clients are allowed anonymous access to the STUN Binding functionality. # #secure-stun # Mobility with ICE (MICE) specs support. # #mobility # Allocate Address Family according # If enabled then TURN server allocates address family according the TURN # Client <=> Server communication address family. # (By default coTURN works according RFC 6156.) # !!Warning: Enabling this option breaks RFC6156 section-4.2 (violates use default IPv4)!! # #keep-address-family # User name to run the process. After the initialization, the turnserver process # will make an attempt to change the current user ID to that user. # #proc-user= # Group name to run the process. After the initialization, the turnserver process # will make an attempt to change the current group ID to that group. # #proc-group= # Turn OFF the CLI support. # By default it is always ON. # See also options cli-ip and cli-port. # #no-cli #Local system IP address to be used for CLI server endpoint. Default value # is 127.0.0.1. # cli-ip=127.0.0.1 # CLI server port. Default is 5766. # cli-port=5766 # CLI access password. Default is empty (no password). # For the security reasons, it is recommended to use the encrypted # for of the password (see the -P command in the turnadmin utility). # # Secure form for password 'qwerty': # #cli-password=$5$79a316b350311570$81df9cfb9af7f5e5a76eada31e7097b663a0670f99a3c07ded3f1c8e59c5658a # # Or unsecure form for the same password: # #cli-password=qwerty cli-password=CHANGE_ME # Server relay. NON-STANDARD AND DANGEROUS OPTION. # Only for those applications when we want to run # server applications on the relay endpoints. # This option eliminates the IP permissions check on # the packets incoming to the relay endpoints. # #server-relay # Maximum number of output sessions in ps CLI command. # This value can be changed on-the-fly in CLI. The default value is 256. # #cli-max-output-sessions # Set network engine type for the process (for internal purposes). # #ne=[1|2|3] # Do not allow an TLS/DTLS version of protocol # #no-tlsv1 #no-tlsv1_1 #no-tlsv1_2 turnserver-4.5.1.1/docker/coturn/Dockerfile0000664000175000017500000000464413436577655017375 0ustar misimisi### 1. stage: create build image FROM debian:stable AS coturn-build ENV BUILD_PREFIX /usr/local/src # Install build dependencies RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ apt-get install -y build-essential git debhelper dpkg-dev libssl-dev libevent-dev sqlite3 libsqlite3-dev postgresql-client libpq-dev default-mysql-client default-libmysqlclient-dev libhiredis-dev libmongoc-dev libbson-dev # Clone coTURN WORKDIR ${BUILD_PREFIX} RUN git clone https://github.com/coturn/coturn.git # Build coTURN WORKDIR coturn RUN ./configure RUN make ### 2. stage: create production image FROM debian:stable AS coturn ENV INSTALL_PREFIX /usr/local ENV BUILD_PREFIX /usr/local/src ENV TURNSERVER_GROUP turnserver ENV TURNSERVER_USER turnserver COPY --from=coturn-build ${BUILD_PREFIX}/coturn/bin/ ${INSTALL_PREFIX}/bin/ COPY --from=coturn-build ${BUILD_PREFIX}/coturn/man/ ${INSTALL_PREFIX}/man/ #COPY turnserver.conf ${INSTALL_PREFIX}/etc COPY --from=coturn-build ${BUILD_PREFIX}/coturn/sqlite/turndb ${INSTALL_PREFIX}/var/db/turndb COPY --from=coturn-build ${BUILD_PREFIX}/coturn/turndb ${INSTALL_PREFIX}/turndb # Install lib dependencies RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ apt-get install -y libc6>=2.15 libevent-core-2.0-5>=2.0.10-stable libevent-extra-2.0-5>=2.0.10-stable libevent-openssl-2.0-5>=2.0.10-stable libevent-pthreads-2.0-5>=2.0.10-stable libhiredis0.13>=0.13.1 libmariadbclient18>=5.5.36 libpq5>=8.4~ libsqlite3-0>=3.6.0 libssl1.1>=1.1.0 libmongoc-1.0 libbson-1.0 RUN apt-get install -y mysql-client postgresql-client redis-tools mongodb-clients RUN if ! getent group "$TURNSERVER_GROUP" >/dev/null; then \ addgroup --system "$TURNSERVER_GROUP" || exit 1 ;\ fi \ && \ if ! getent passwd "$TURNSERVER_USER" >/dev/null; then \ adduser --system \ --home / \ --shell /bin/false \ --no-create-home \ --ingroup "$TURNSERVER_GROUP" \ --disabled-password \ --disabled-login \ --gecos "turnserver daemon" \ "$TURNSERVER_USER" || exit 1; \ fi # set startup parameters # SUTN/TURN PORTS EXPOSE 3478 3479 3478/udp 3479/udp 80 80/udp EXPOSE 5349 5350 5349/udp 5350/udp 443 443/udp # CLI EXPOSE 5766 # Relay Ports EXPOSE 49152-65535 49152-65535/udp #COPY ./docker-entrypoint.sh / #ENTRYPOINT ["/docker-entrypoint.sh"] WORKDIR ${INSTALL_PREFIX} CMD ${INSTALL_PREFIX}/bin/turnserver turnserver-4.5.1.1/docker/coturn/coturn.env0000664000175000017500000000002313436577655017412 0ustar misimisi# for future usage turnserver-4.5.1.1/docker/cp-schema.sh0000775000175000017500000000011713436577655016257 0ustar misimisi#!/bin/bash cp ../turndb/schema.sql mysql/ cp ../turndb/schema.sql postgresql/ turnserver-4.5.1.1/ChangeLog0000664000175000017500000012340313436577655014367 0ustar misimisi02/03/2019 Oleg Moskalenko Mihály Mészáros Version 4.5.1.1 'dan Eider': - merge PR #330 missing \r\n after http Connection:close (by gribunin) - merge PR #303 fix typo enpoint (by Majid Motallebikashani) - merge PR #129 seperate http web-admin listener (by Thibaut ACKERMANN) - regression from 4.5.1.0 * readd pwd check * add to config missing web-admin-listen-on-workers option - merge docker branch * Add Docker file for all database backend. - merge sparc64 branch * Fix mem alingment issue on 64 bit architecture That issue caused earlier "bus error" on sparc64 and armhf - merge PR #336 Clarify Debian install (by David-dp-) - merge PR #339 RPM build fix (by Peter Hudec ) 24/11/2018 Oleg Moskalenko Mihály Mészáros Version 4.5.1.0 'dan Eider': Consider to change config file after upgrade, because it contains some not backward compatible breaking changes !! - Security fixes Many thanks to Nicolas Edet (Cisco) !! who reported all of the following issues: * DB/SQL injection in stun realm. Fix: add extra string validation. * DB/SQL injection in web-admin interface lack of admin user validation. Fix: add extra string validation. * Fix for earlier unsafe default settings: o HTTPS administrator interface should be disabled by default It could be enbled with "web-admin" option. o Default configuration allowed earlier forwarding traffic from an external interface to loopback interface. Now it has been changed and option name is also changed! !!BREAKING change!! Don't forget to change config!! "no-loopback-peers" replaced by "allow-loopback-peers" o Unauthenticated telnet admin interface runs on the loopback interface, which can be accessed by exploiting the loopback relay that was enabled by default. * Add username string sanity check on web admin interface to avoid any sql-injection attacks. - Admin portal does not list TCP session ( reported and fixed by Nicolas Edet ) - Fix memory leak in read_config_file (by Thibaut Ackermann) - Add a release helper script. - Web Admin interface use own listener (it is disableb by default) (by Thibaut ACKERMANN) !!BREAKING change!! Don't forget to change/review config!! * Add new option "web-admin-ip" to set listener ip. By default (127.0.0.1) * Add new option "web-admin-port" to set webadmin listen port * Add new option "web-admin-listen-on-workers" to change back to earlier behaviour and listen web admin on all worker processes and ports. - Not allow to start server if "allow-loopback-peers" set without "cli-password" !!BREAKING change!! Don't forget to change config!! Added a warning if cli-password is empty or missing, but allow-loopback-peers set, and so loopback allocation is enalbed. 27/09/2018 Oleg Moskalenko Mihály Mészáros Version 4.5.0.8 'dan Eider': - Travis CI integration - to avoid warnings add compiler comment hint to fallthrough - reload-tls-certs PR#236 (by Arne Georg GisnÃ¥s Gleditsch) - minor fixes PR#223 (by Pavel Kretov) move rm Makefile to distclean list all phony targets - Fix typo PR#253 (by Orsiris de Jong) - Fix stuck IPv6 connections. (issue #217) THX to damencho, vol4iniche - Spelling fixes. - Add a warning if --lt-cred-mech and --use-auth-secret both presents. - Revert "Add the realm parameter in the example config file (by Domenico)" - Fix for Verbose config file option -v cli option override - Add a Notice to config about realm default value is the domain name. - Update total allocation usage on client shutdown - Fix total and user quota mix-up - Fixed typos in postinstall.txt (by Prashanth Rajaram) - MySQL password encryption (by Mustafa Bingül & Erdem Duman) - Do not write to log before logging configuration is read from a config file (by eiver) - Add more explanation on use-auth-secret / REST in example config (by Krithin Sitaram) - Add a Warning if lines in config file ends with semicolon (by heyheyjc) - Fix --prod pointer bug - Fix auth server thread detach race (by weishuyin) - New Feature: Add -K --keep-address-family Be aware if you enable it, then it breaks rfc6156 section 4.2 (default IPv4 family fallback) - Fix dtls double free crash - Fix compilation errors and warnings (by Oleg) 12/10/2017 Oleg Moskalenko Version 4.5.0.7 'dan Eider': - Misc security improvements. 10/17/2016 Oleg Moskalenko Version 4.5.0.6 'dan Eider': - Typos in the text fixed. - TLS1.2 support fixed. - uclient minor performance tweak. - Issue #113 fixed (by David Benjamin) - Report total traffic used per allocation via Redis (by Bradley T. Hughes) - Add the realm parameter in the example config file (by Domenico) 08/27/2016 Oleg Moskalenko Version 4.5.0.5 'dan Eider': - Typos in the text fixed. - LibreSSL compatibility fixed. - "read_timeout" option support for MySQL. - new NAT behavior discovery utilty. - new OAuth access_token encrypt/decrypt utilty. - improved configurability: added parameters for allocate, channel and permission lifetimes (by Richard Garnier). 08/20/2016 Oleg Moskalenko Version 4.5.0.4 'dan Eider': - OpenSSL 1.1.0 support added. - CentOS 7 installation updated. - hiredis and mongo compilation configuration fixed (fix provided by Harsha Bellur). - RPM: Systemd optimization. - REST API option fixed. - Thread creation error handling fixed. - Issue #36 fixed. 11/15/2015 Oleg Moskalenko Version 4.5.0.3 'dan Eider': - SSLv3 support removed. That provides extra security and compatibility with OpenSSL distributions or clones that do not support SSLv3 (like LibreSSL 2.3.0). This fix is required for fresh FreeBSD and for Debian unstable. - Compilation and configuration cleaning. - Fix for non-interactive shells. - RPM: Fixed mongo-c-driver include path (by Sergey Safarov). - RPM: Fixed startup daemon as non root user (by Sergey Safarov). - RPM: Systemd optimized for high-volume network traffic (by Sergey Safarov). 9/29/2015 Oleg Moskalenko Version 4.5.0.2 'dan Eider': - DTLS segmentation fault fixed; 9/13/2015 Oleg Moskalenko Version 4.5.0.1 'dan Eider': - multiple realms based on oAuth (third-party authorization); - STUN attributes conflict resolution; - SIGHUP handler fixed; - error message logging improved; - mongo test db files fixed. 7/18/2015 Oleg Moskalenko Version 4.4.5.4 'Ardee West': - moved to github. 6/20/2015 Oleg Moskalenko Version 4.4.5.3 'Ardee West': - third-party authorization STUN attributes adjusted according to the values assigned by IANA. - SQL injection security hole fixed. 5/29/2015 Oleg Moskalenko Version 4.4.5.2 'Ardee West': - dual allocation adjusted according to the new TURN-bis draft; - options sha256, sha384, sha512 retired as non-standard; - third-party authorization (oAuth) updated according to the version 16 of the draft; - C++ compilation fixes; - cosmetic fixes; - fixed binary package for CentOS 7.1; - support for older SQLite versions added; - compilation support for older CentOS release 5.x added; - Issue 11 fixed; - Issue 12 fixed. 3/31/2015 Oleg Moskalenko Version 4.4.4.2 'Ardee West': - SCTP fixes; 3/15/2015 Oleg Moskalenko Version 4.4.4.1 'Ardee West': - 'native' SCTP support (experimental); - option of encrypted stored passwords for web admin users; - option of encrypted stored password for CLI user; 2/28/2015 Oleg Moskalenko Version 4.4.2.3 'Ardee West': - bandwidth control fixed; - STUN/TURN control traffic counted separately from data traffic, for the sake of the bandwidth control; - higher bandwidth limit capacity on 64 bits systems; - redis operations with the realm options fixed; 2/18/2015 Oleg Moskalenko Version 4.4.2.2 'Ardee West': - admin_user table schema fixed; - REST API docs fixed; - Amazon AWS uses syslog; 2/3/2015 Oleg Moskalenko Version 4.4.2.1 'Ardee West': - (HMAC-)SHA-512 and -384 algorithms added; - TOS (DiffServer) and TTL IP header field handling fixed; - updates according to the new third-party-auth draft (oauth); - peer logging added; 2/1/2015 Oleg Moskalenko Version 4.4.1.2 'Ardee West': - SSODA updates according to turnbis specs; - TRANSPORT attribute handling fixed; - hostname-to-IP-address resolution fix; - updates for Solaris (name resolution libraries); 1/24/2015 Oleg Moskalenko Version 4.4.1.1 'Ardee West': - https admin server; - SSLv2 support cancelled (security concern fixed); - The server-side short-term credentials mechanism support cancelled; - OpenSSL 1.1.0 supported; - shared secrets fixed in MongoDB: multiple secrets per realm allowed; - shared secrets admin fixed in Redis; 12/24/2014 Oleg Moskalenko Version 4.3.3.1 'Tolomei': - multiple authentication threads; - database code cleaned; 12/14/2014 Oleg Moskalenko Version 4.3.2.2 'Tolomei': - Redis read message queue bug fixed; - STUN/TURN ALPN supported (when compiled with OpenSSL 1.0.2+ ); - DTLS v1.2 supported (when compiled with OpenSSL 1.0.2+ ); - Auto optimal ECDH parameters (when compiled with OpenSSL 1.0.2+ ); - TLS/DTLS code cleaning. 11/29/2014 Oleg Moskalenko Version 4.3.1.3 'Tolomei': - Reliability fixes (Issue 141 from rfc5766-turn-server). - HTTP/HTTPS echo fixed. - External address mapping fixes for Amazon EC2. - Minor docs improvements. 11/23/2014 Oleg Moskalenko Version 4.3.1.2 'Tolomei': - Debian package fixes. 11/22/2014 Oleg Moskalenko Version 4.3.1.1 'Tolomei': - SQLite supported as the default user database. - Support of the flat-file user database removed. - TLS connection procedure improved in uclient test program. 11/07/2014 Oleg Moskalenko Version 4.2.3.1 'Monza': - Request re-transmission implemented in uclient test program. - TLS connection procedure improved in uclient test program. 10/26/2014 Oleg Moskalenko Version 4.2.2.2 'Monza': - Black- and white- IP lists are divided per realm (the DB schema for those two tables changed); - Updated Redis database schema. - Debian UFW file added (Issue 1 fixed). - TCP/TLS tests extended. - Relay RTCP sockets ports allocation fixed. - List of libraries cleaned. - SSL renegotiation callback fixed. 10/05/2014 Oleg Moskalenko Version 4.2.1.2 'Monza': - oAuth security experimental implementation; - The "TLS renegotiation" DoS attack prevention implemented; - FQDN as relay-ip and listener-ip parameters (issue 6) (patch provided by Iñaki Baz Castillo); - redis user key operation fixed. - redis, mysql and psql db operations fixed. - SHA-256 memory leak fixed. - Mobility ticket retransmission fixed. - Move debian package from SVN to GIT. - Move secondary download area to coturn.net. - Quota allocation fixed. - Core dump fixed. - Bandwidth allocation fixed. - Memory code cleaning. - Logging fixed. 08/14/2014 Oleg Moskalenko Version 4.1.2.1 'Vitari': - The origin attribute is verified in the subsequent session messages (server flag --check-origin-consistency). - MySQL SSL connection support. - Crash fixed when the DB connection string is incorrect. - Minor docs fixes. 07/29/2014 Oleg Moskalenko Version 4.1.1.1 'Vitari': - Forceful server-side session cancellation implemented (in telnet console). 07/22/2014 Oleg Moskalenko Version 4.1.0.2 'Vitari': - SSODA (double allocation) draft support added. - DB "driver" abstraction and MongoDB support (by Federico Pinna). - multiple origins supported per request. - "allocation mismatch" condition fixed (merged from rfc5766-turn-server). - STUN BINDING response fixed in the case of -X (external address) option. - "pu" CLI command fixed. - session cleaning fixed in TCP relay functionality (RFC 6062). - some crash conditions fixed. - working on compilation warnings. 06/21/2014 Oleg Moskalenko Version 4.0.1.3 'Severard': - Redis DB connection status fixed (Issue 129). - Logfile reset on SIGHUP (Gustavo Garcia suggestion). - Log reset CLI command. - Some error code corrections: * "Mobility forbidden" error changed, to value 405. * "Wrong credentials" situation is now treated as error 441. 06/06/2014 Oleg Moskalenko Version 4.0.1.2 'Severard': - Bandwidth draft implemented. - Issues 126, 127 and 128 fixes merged from rfc5766-turn-server. 05/18/2014 Oleg Moskalenko Version 4.0.0.2 'Threetrees': - Code cleaning. 05/07/2014 Oleg Moskalenko Version 4.0.0.1 'Threetrees': - Kernel channel placeholder definitions. 05/02/2014 Oleg Moskalenko Version 4.0.0.0 'Threetrees': - Multi-tenant server. 04/13/2014 Oleg Moskalenko Version 3.2.3.6 'Marshal West': - Addresses logging fixed. - Redis admin options fixed. - Redis compilation cleaned. 04/07/2014 Oleg Moskalenko Version 3.2.3.5 'Marshal West': - Mobile allocation quota fixed (issue 121); - --simple-log option added (Issue 122); - documentation fixes (REST API, Redis). 04/06/2014 Oleg Moskalenko Version 3.2.3.4 'Marshal West': - Mobile TCP sessions fixed (issue 120); - log information improvements. 04/04/2014 Oleg Moskalenko Version 3.2.3.3 'Marshal West': - Pkey and cert file descriptors to be closed on initialization (issue 118); - Address bind indefinite cycle on start-up fixed (Issue 119); - Allocation counters time lag improved. 03/30/2014 Oleg Moskalenko Version 3.2.3.2 'Marshal West': - Allocation counters fixed (issue 117); - a possible core dump in the server code fixed; - a possible memory leak in server fixed. 03/29/2014 Oleg Moskalenko Version 3.2.3.1 'Marshal West': - TCP congestion avoidance completed. - Read and write streams are treated separately in bandwidth control. - Test client fixed. - Experimental SHA256 key storage supported. 03/17/2014 Oleg Moskalenko Version 3.2.2.912 'Marshal West': - TCP-in-TCP congestion avoidance implemented. - UDP-in-TCP congestion avoidance improved. - Alternate-server code cleaned. 03/10/2014 Oleg Moskalenko Version 3.2.2.911 'Marshal West': - "Congestion control" for UDP-inside-TCP tunneling; - memory management improvements; - socket logging improvements; - debug info added to CentOS and Fedora RPMs; - TCP traffic buffering improved; - Thread barriers cleaned; - TCP memory leak fixed; - minor TCP test client improvement. 03/09/2014 Oleg Moskalenko Version 3.2.2.910 'Marshal West': - Log messages extended and cleaned. - Some memory cleaning. 03/02/2014 Oleg Moskalenko Version 3.2.2.9 'Marshal West': - Issue 113 fixed (TCP rate limit fixed); - Issue 114 fixed (TCP stability). 02/18/2014 Oleg Moskalenko Version 3.2.2.8 'Marshal West': - Issue 102: SO_BSDCOMPAT socket option removed; - Issue 104: check for the REALM attribute value; - Issue 105: no-cli segfault fixed; - Issue 106: MESSAGE-INTEGRITY removed from DATA indication; - Issue 108: Server should return 438 on unknown nonce; - Issue 109: make the random functions stronger (mostly for transaction ID and for nonce); - Issue 111: fix valgrind warning on memory initialization. - Issue 112: RTCP sockets logging. 02/12/2014 Oleg Moskalenko Version 3.2.2.7 'Marshal West': - Possible indefinite cycle fixed in TCP/TCP routing (Issue 99); - Address 0.0.0.0 can be used as a listener address (Issue 100); - DHCP-configured servers supported (Issue 101); 02/04/2014 Oleg Moskalenko Version 3.2.2.6 'Marshal West': - Channel traffic memory copy elimination. - Send indication memory copy elimination. - DTLS traffic processing memory copy eliminated. - Mobility forbidden error code number fixed - according to the new draft document. - getsockname() usage minimized. - port allocation improved. - default relay behavior fixed (when no relay addresses defined). - atomic create permission request handling (Issue 97). 01/25/2014 Oleg Moskalenko Version 3.2.2.5 'Marshal West': - code optimization. 01/24/2014 Oleg Moskalenko Version 3.2.2.4 'Marshal West': - HMAC key handling fixed (Issue 96). 01/23/2014 Oleg Moskalenko Version 3.2.2.3 'Marshal West': - Security fix (issue 95). - Default "implicit" relay IP allocation policy is more usable (issue 94 fixed). - SSLv2 fixed (for those who are still using it) (issue 93 fixed). - Cosmetic changes. 01/19/2014 Oleg Moskalenko Version 3.2.2.1 'Marshal West': - CPU/memory cache optimization (memory locality). - torture tests enhanced. - stability fixes. - minor possible memory leak fix. - new TLS options: --no-sslv2, --no-sslv3, --no-tlsv1, --no-tlsv1_1, --no-tlsv1_2 01/06/2014 Oleg Moskalenko Version 3.2.1.4 'Marshal West': - Linux epoll performance improvements. - DTLS minor fix. 01/06/2014 Oleg Moskalenko Version 3.2.1.3 'Marshal West': - Telnet client added to installation when necessary. 01/05/2014 Oleg Moskalenko Version 3.2.1.2 'Marshal West': - Config file adjusted for DragonFly. 01/03/2014 Oleg Moskalenko Version 3.2.1.1 'Marshal West': - Minor TLS fix. - Default cipher list is DEFAULT now. 12/26/2013 Oleg Moskalenko Version 3.2.1.0 'Marshal West': - Optimized TCP network engine for Linux 3.9+. - Security fix: DH and ECDH temporary keys are now regenerated for each TLS or DTLS session. - Fix for systems with multiple CPU cores (more than 128). - DH TLS key now can be configured as 566, 1066 (default) or 2066 bits. - DH TLS key can be taken from a PEM file. - Issue 91 (test client crash) fixed. - Configurable net engine type. 12/25/2013 Oleg Moskalenko Version 3.1.6.0 'Arch Lector': - Timers optimization: linked list timers structure for often-used intervals. 12/23/2013 Oleg Moskalenko Version 3.1.5.3 'Arch Lector': - HTTP "keep-alive" support improved. - TCP channel "fortification". 12/19/2013 Oleg Moskalenko Version 3.1.5.1 'Arch Lector': - Private key password allowed for encrypted keys. - HTTP "keep-alive" supported. - "psd" CLI command added (ps dump to file). 12/18/2013 Oleg Moskalenko Version 3.1.4.2 'Arch Lector': - Time functions optimization. - Online changes to the alternate servers list thru telnet CLI. - Certificate chain files allowed. 12/13/2013 Oleg Moskalenko Version 3.1.3.1 'Arch Lector': - "Start time" ps command info added. - Protocol option added to "pu" command. - "Delete allocation" debug message fixed. - "Allocation id" debug info message fixed. - RFC6062 usage statistics fixed. - Info/Debug messages cleaned. 12/11/2013 Oleg Moskalenko Version 3.1.2.3 'Arch Lector': - CentOS 6 package fixed. 12/10/2013 Oleg Moskalenko Version 3.1.2.2 'Arch Lector': - ps output typo fixed (TLS params). - configurable EC curve name. - CLI TLS-related information extended. - "print users" (pu) CLI command added. 12/09/2013 Oleg Moskalenko Version 3.1.2.1 'Arch Lector': - DH cipher suites basic implementation. - Elliptic Curve cipher suites basic implementation. - RFC 6062 crash fixed. - More CLI parameters added. - Redis allocation statistics fixed. - Number of cli max session lines configurable. - uclient cipher suite configurable. 12/08/2013 Oleg Moskalenko Version 3.1.1.0 'Arch Lector': - Telnet CLI. - RFC 6062 internal messaging fixed. - Server relay endpoints (a non-standard feature). - "atomic line" stdout log print. - printed help minor fix. - client program does not necessary require certificate for TLS. - docs fixes. - allocation quota bug fixed. 11/29/2013 Oleg Moskalenko Version 3.0.2.1 'Practical Frost': - TCP stability fixes. - RFC 6062 "first packet(s)" bug fixed. - RFC 6062 stability fixes. - Multithreaded Mobile ICE. 11/28/2013 Oleg Moskalenko Version 3.0.1.4 'Practical Frost': - CentOS/Fedora packaging fixed. - PID file fixed. 11/26/2013 Oleg Moskalenko Version 3.0.1.3 'Practical Frost': - Misc cosmetic changes. - CentOS/Fedora packaging fixed. 11/25/2013 Oleg Moskalenko Version 3.0.1.2 'Practical Frost': - Mobility draft implemented. - DTLS communications fixes. - UDP Linux optimization. - Log output time starts with 0. - A new "drop root privileges" options: --proc-user and --proc-group added. - SHA256 agility updated: 426 error code on too weak SHA function. - "-m 0" and "-m 1" options improved. - non-threading environment support dropped. - stability fixes. - OpenSUSE support added. 11/10/2013 Oleg Moskalenko Version 3.0.0.0 'Practical Frost': - New network engine for Linux kernel 3.9+. 11/08/2013 Oleg Moskalenko Version 2.6.7.2 'Harding Grim': - SHA256 agility updated: 441 error code on too weak SHA function. 11/07/2013 Oleg Moskalenko Version 2.6.7.1 'Harding Grim': - CentOS / Fedora uninstall script. - Debian compilation error fixed. - OpenSSL 0.9.7 and earlier build fixed. - NetBSD build fixed. 11/03/2013 Oleg Moskalenko , Peter Dunkley Version 2.6.7.0 'Harding Grim': - CentOS 6 pre-compiled distribution. - Fedora pre-compiled distribution. - TURN_NO_TLS case compilation cleaning. - Text files cleaning. - Issue 68 fixed (no-stun option added). 10/27/2013 Oleg Moskalenko Version 2.6.6.1 'Harding Grim': - SHA256 added as a non-standard message integrity option. - CentOS rpm specs added. - Peter Dunkley added to the authors list. 10/20/2013 Oleg Moskalenko Version 2.6.6.0 'Harding Grim': - Cygwin loopback relay interfaces fixed (Issue 62). - rpath added to the Makefile (Issue 63). - CONFLICTS added to FreeBSD port Makefile (Issue 64). - Certificate check options, for server and for the test client (Issue 65). - Some compilation cleaning. 10/09/2013 Oleg Moskalenko Version 2.6.5.2 'Harding Grim': - Documentation changes. - Redis-related memory leak fixed (Issue 61). 09/25/2013 Oleg Moskalenko Version 2.6.4.1 'Harding Grim': - Crash on uninitialized redis db name is fixed (Issue 59). - Optional authentication of STUN Binding request is implemented (Issue 60). 09/16/2013 Oleg Moskalenko Version 2.6.3.1 'Harding Grim': - Issue 58: support changing white/black IP lists while server is running. database tables (keys for redis) added for that new functionality. 09/03/2013 Oleg Moskalenko Version 2.6.2.2 'Harding Grim': - Issue 52: RFC 6062 relay endpoints connection process fixed for Linux pre-3.9 kernel. 09/03/2013 Oleg Moskalenko Version 2.6.2.1 'Harding Grim': - UDP performance improvements. - Issue 56: DTLS scaleability improvements. - Issue 55: DTLS support in Cygwin. - Issue 57: --pidfile option - Issue 52: RFC 6062 relay endpoints connection process fixed. - Issue 53: Fingerprints added to the indications. - Issue 54: Long-term credentials mechanism integrity and software attributes added to the indications. 08/11/2013 Oleg Moskalenko Version 2.6.1.4 'Harding Grim': - UDP memory leak fixed. 08/11/2013 Oleg Moskalenko Version 2.6.1.3 'Harding Grim': - DTLS crash fix. 08/10/2013 Oleg Moskalenko Version 2.6.1.2 'Harding Grim': - TLS buffer decreased to avoid memory problems. - TLS BIO object fix. - UDP socket open/reopen process fixed. 08/08/2013 Oleg Moskalenko Version 2.6.1.1 'Harding Grim': - Network optimization: * "pure" UDP setup optimized (when no DTLS configured); * Auxiliary listening endpoints (configured by --aux-server=). * --udp-self-balance option to balance the UDP traffic among the aux endpoints (for clients supporting 300 ALTERNATE-SERVER response). - Security improvements: * no authentication required on the load balancer server (Issue 50). * REST API improvement: = --secret-ts-exp-time option deprecated; = In REST API timestamp, we are now using the expiration time (Issue 31). * Configurable cipher suite in the TURN server. * SSLv3 support. * TLS 1.1 and 1.2 support. * SSLv2 "encapsulation" mode support. * NULL OpenSSL cipher is allowed to be negotiated between server and client. * -U option (NULL cipher) added to the test client. * DTLS crash fixed on overload. - STUN enhancements and fixes: * Classic STUN transaction ID fixed (Issue 48). * Classic STUN attribute ERROR fixed (Issue 49). * Unused RFC 5780 functionality removed from TCP, TLS and DTLS relays. * resources optimization for stun-only: short connection expiration time. 07/26/2013 Oleg Moskalenko , Vladimir Tsanev Version 2.5.2.1 'Shivers': - log file placement changes. - Base64 encode/decode memory initialization fix. 07/23/2013 Oleg Moskalenko , Po-sheng Lin Version 2.5.1.2 'Shivers': - getopt fix in client test programs. - cosmetic changes. - allow anonymous alternate-server functionality. 07/21/2013 Oleg Moskalenko Version 2.5.1.1 'Shivers': - Improved "split" network engine: two different threading models for TCP and UDP. - DTLS crash fixed. - Multithreading with Cygwin. 07/20/2013 Oleg Moskalenko Version 2.1.3.1 'Shivers': - DTLS improvements for DOS attacks - deeper optimization for DOS attack (mostly for Linux) 07/19/2013 Oleg Moskalenko Version 2.1.2.0 'Shivers': - deeper optimization for DOS attack (mostly for Linux) 07/18/2013 Oleg Moskalenko , Po-sheng Lin Version 2.1.1.1 'Shivers': - udp fixes. - Makefile cleaning. - Dependencies cleaning. - DOS attack client emulation. - DOS attack defense logic added to the server. 07/14/2013 Oleg Moskalenko Version 2.0.0.0 'Shivers': - new networking engine: - scalable UDP socket model. - multithreaded TCP relay implemented. - race condition fixed in authentication of TCP sessions. - Cygwin "port" fixed. 06/23/2013 Oleg Moskalenko , Vladimir Tsanev Version 1.8.7.0 'Black Dow': - Added support for obsolete "classic" STUN RFC 3489; - Full TURN support for Cygwin implemented: MS-Win UDP sockets fixed; - Relay threads number changed; - Fedora warnings fixed; - turndb/testdbsetup.sh example file added; - Multiple Makefile and ./configure script fixes implemented: * Changes taken from Arch Linux port; * Manpages installation and deinstallation; * rfc5769check utility removed from installation, it is used for the compilation result test only and makes no sense for the end user; * "--parameter" format support in ./configure script; it allows simpler native OS package definitions (like in Debian package); * Mac OS X linking warnings removed. * pthread test fixed. 06/08/2013 Oleg Moskalenko Version 1.8.6.3 'Black Dow': - DONT-FRAGMENT flag removed on UDP listening (clients-facing) sockets. - UDP fix for Cygwin only: UDP channels work fine now. - docs fixes. 06/06/2013 Oleg Moskalenko Version 1.8.6.2 'Black Dow': - Just cosmetic re-packaging for Debian, tarball warnings removed. 06/05/2013 Oleg Moskalenko Version 1.8.6.1 'Black Dow': - Peer permissions bug fixed. 06/03/2013 Oleg Moskalenko Version 1.8.6.0 'Black Dow': - Optimization. - Mac OS X compilation fixes. 06/01/2013 Oleg Moskalenko Version 1.8.5.4 'Black Dow': - Issues 29 and 30 fixed (channels padding). - minor fixes. - Mac OS X compilation fixes. - Cygwin-related compilation fixes and INSTALL additions. 05/31/2013 Oleg Moskalenko Version 1.8.5.3 'Black Dow': - REST API extra script example and docs extension. 05/26/2013 Oleg Moskalenko Version 1.8.5.1 'Black Dow': - Config file parsing fixed (Issue 28) 05/20/2013 Oleg Moskalenko , Erik Johnston Version 1.8.5.0 'Black Dow': - IP access control lists. - log file name fix. - alt-* ports default behavior changed. - "passive TCP" option in uclient. 05/18/2013 Oleg Moskalenko Version 1.8.4.5 'Black Dow': - socket conditions cleaned (SIGPIPE, etc) 05/17/2013 Oleg Moskalenko Version 1.8.4.4 'Black Dow': - configuration and installation adjusted for: - NetBSD; - Solaris; - OpenBSD; - Screen messages fixed; - code security fixes. 05/15/2013 Oleg Moskalenko Version 1.8.4.3 'Black Dow': - Compilation warning removed. - Log file fixed (Issue 26) 05/15/2013 Oleg Moskalenko Version 1.8.4.2 'Black Dow': - repackaging for Debian compliance. Docs separated. 05/14/2013 Oleg Moskalenko Version 1.8.4.1 'Black Dow': - Cosmetics (docs, warnings, etc). - More complex case of TURN-server-behind-NAT is implemented, when multiple public-ip/private-ip mappings are involved. 05/13/2013 Oleg Moskalenko Version 1.8.4.0 'Black Dow': - Redis DB support added. - Crash on help text fixed. - Max allocation time can be changed in the command-line or in the config file. 05/09/2013 Oleg Moskalenko Version 1.8.3.9 'Black Dow': - No changes - just the tarball is repackaged for Debian compatibility. 05/07/2013 Oleg Moskalenko Version 1.8.3.8 'Black Dow': - multicast and loopback addresses disallow options added. - option to direct all log messages to the system log (syslog). 05/02/2013 Oleg Moskalenko Version 1.8.3.7 'Black Dow': - Allocation status log. 05/01/2013 Oleg Moskalenko Version 1.8.3.6 'Black Dow': - Stuntman client interoperability fixed. - Manpages installation fixed. 04/30/2013 Oleg Moskalenko Version 1.8.3.5 'Black Dow': - Lintian fixes. 04/27/2013 Oleg Moskalenko Version 1.8.3.4 'Black Dow': - Installation fixes. 04/26/2013 Oleg Moskalenko Version 1.8.3.3 'Black Dow': - Log file midnight rollover implemented (Issue 15). 04/25/2013 Oleg Moskalenko Version 1.8.3.1 'Black Dow': - Configurable REST API separator symbol (Issue 16). - Stale Nonce bug fixed (Issue 17). - Minor client fix. 04/21/2013 Oleg Moskalenko Version 1.8.3.0 'Black Dow': - STUN stand-alone functionality improved according to RFC 5389. - ALTERNATE-SERVER implemented as "light" load balancing feature. - stun-only option implemented. - scripts directory reorganized. 04/19/2013 Oleg Moskalenko Version 1.8.2.1 'Black Dow': - Misc docs fixes. 04/13/2013 Oleg Moskalenko Version 1.8.2.0 'Black Dow': - Multiple database shared secrets supported for REST API. - Added support for some OpenSSL FIPS versions (like openssl 0.9.8e-fips-rhel5). 04/13/2013 Oleg Moskalenko Version 1.8.1.3 'Black Dow': - Maintenance (docs, etc). - Added partial support for Cygwin. Only TCP & TLS protocols are support for client-to-server communications (as in RFC 5766 and RFC 6062). UDP supported only for relay communications. DTLS is not supported at all. The problem is in Winsock UDP sockets implementation. 04/11/2013 Oleg Moskalenko Version 1.8.1.2 'Black Dow': - Work on configuration and build. 04/9/2013 Oleg Moskalenko Version 1.8.1.1 'Black Dow': - Docs improvements. - Load balancing use case added to TurnNetworks.pdf. - Verbose mode split into 'normal' and 'extra' modes. - Logging extended and fixed. 04/7/2013 Oleg Moskalenko Version 1.8.1.0 'Black Dow': - Compilation flags improved. - utility programs renamed and moved to bin/ directory. - README and turnserver man page separated into three sections - turnserver, turnadmin, turnutils. 04/6/2013 Oleg Moskalenko Version 1.8.0.6 'Black Dow': - Added option "--psql-userdb" for better visual separation between PostgreSQL and MySQL stuff. - turnadmin flat files handling fixed. - added set/show commands to turnadmin for secret key. 04/6/2013 Oleg Moskalenko Version 1.8.0.5 'Black Dow': - turnadmin MySQL connection fixed. - minor cosmetic changes. - Added "list" commands for long-term and short-term users, to turnadmin. 04/5/2013 Oleg Moskalenko Version 1.8.0.4 'Black Dow': - Minor compilation fixes. - Minor docs fixes. - "connect_timeout" option support for MySQL. 04/5/2013 Oleg Moskalenko Version 1.8.0.3 'Black Dow': - Issue 11 (secret timestamp check) fixed. 04/4/2013 Oleg Moskalenko Version 1.8.0.2 'Black Dow': - TCP sockets flush removed. - rfc5769check utility removed from the Makefile. 04/4/2013 Oleg Moskalenko Version 1.8.0.1 'Black Dow': - Some short-term auth problems fixed. - rfc5769check utility added to the Makefile and upgraded. 04/3/2013 Oleg Moskalenko Version 1.8.0.0 'Black Dow': - Short-term credentials mechanism implemented. 04/2/2013 Oleg Moskalenko Version 1.7.3.1 'Superior Glokta': - Listeners code cleaned. - The default number of extra relay threads changes from 0 to 1. 04/1/2013 Oleg Moskalenko Version 1.7.3.0 'Superior Glokta': - Issue 10 fixed: log file control options. Two options added: --no-stdout-log and --log-file. 03/29/2013 Oleg Moskalenko Version 1.7.2.0 'Superior Glokta': - Issue 9 fixed (uclient). - Secret-based authentication implemented (see TURNServerRESTAPI.pdf). - Uclient docs fixed. - database schema extended (table for the secret added). 03/27/2013 Oleg Moskalenko Version 1.7.1.2 'Superior Glokta': - CHANNEL BIND request handling fixed: now it produces an error when client is trying to tie the same peer address to different channels. - uclient and peer test apps upgraded so that RTP channels are talking to and RTCP channels are talking to in client-to-peer communication patterns. - compilation warning is fixed when MySQL is not used. 03/27/2013 Oleg Moskalenko Version 1.7.1.1 'Superior Glokta': - CONNECT response fixed in RFC 6062. - uclient checks server responses integrity. 03/26/2013 Oleg Moskalenko Version 1.7.1.0 'Superior Glokta': - MySQL support added for the user keys repository. - PostgreSQL support improved. - Docs fixed. - 64 bits platform fixes. 03/23/2013 Oleg Moskalenko Version 1.7.0.0 'Glokta': - Authentication fix. - PostgreSQL database can be used as the user keys repository. 03/21/2013 Oleg Moskalenko Version 1.6.1.3 'Whirrun': - UDP segmentation fault fixed 03/21/2013 Oleg Moskalenko Version 1.6.1.2 'Whirrun': - RFC 6062 fix 03/21/2013 Oleg Moskalenko Version 1.6.1.1 'Whirrun': - Authentication error fixed 03/19/2013 Oleg Moskalenko Version 1.6.1.0 'Whirrun': - --stale-nonce option - working on userdb - "hang on" option fixed in uclient 03/18/2013 Oleg Moskalenko Version 1.6.0.2 'Whirrun': - working on userdb - c++ compilation fix 03/17/2013 Oleg Moskalenko Version 1.6.0.1 'Whirrun': - uclient performance improved - TurnNetworks.pdf document added 03/15/2013 Oleg Moskalenko Version 1.6.0.0 'Whirrun': - "Pure" TCP relaying (RFC 6062) implemented. - Network interactions fixes. - RFC 6062 test scripts added. 03/03/2013 Oleg Moskalenko Version 1.5.2.8 'Iosiv Lestek': - authorization processing improvements. - peer application fixed. - some ICE attributes added. 02/27/2013 Oleg Moskalenko Version 1.5.2.7 'Iosiv Lestek': - authorization processing improvements - Issue 4 fixed. - secure client-to-client script added 02/22/2013 Oleg Moskalenko Version 1.5.2.6 'Iosiv Lestek': - strcpy/strncpy fixed - some screen messages fixed - uclient statistics fixed - software attribute fixed - example scripts fixed 02/16/2013 Oleg Moskalenko Version 1.5.2.5 'Lestek': - uclient application fixed - Docs fixes 02/14/2013 Oleg Moskalenko Version 1.5.2.4 'Lestek': - Crash fixed on unconfigured interfaces - Docs fixes 02/12/2013 Oleg Moskalenko Version 1.5.2.3 'Lestek': - Added feature: TURN Server always uses fingerprints in a session if the session client is using fingerprints. - Default unsecure alternative port changed to 3479, default secure alternative port changed to 5350. - TURN Server always trying to search for default certificate file turn_server_cert.pem and for default private key file turn_server_pkey.pem, if not certificate or private key is explicitly configured. - configurable packet rate in the uclient test program. - default peer port changed to 3480. - -z, --no-auth option added to turnserver. 02/11/2013 Oleg Moskalenko Version 1.5.2.2 'Lestek': - Some cleanup added to the network input handlers. 02/9/2013 Oleg Moskalenko Version 1.5.2.1 'Lestek': - Binding requests do not require authentication. - SOFTWARE in the end of the message. 02/8/2013 Oleg Moskalenko Version 1.5.2.0 'Lestek': - NAT discovery fixed (RFC5780). 02/8/2013 Oleg Moskalenko Version 1.5.1.6 'Calder': - Installation instructions fixed. 02/8/2013 Oleg Moskalenko Version 1.5.1.5 'Calder': - Mac compilation fixes. - Fixes for old Linuxes. 02/7/2013 Oleg Moskalenko Version 1.5.1.4 'Calder': - Configuration alert (warning) messages. - Relay addresses by default use listener addresses. - Realm/user sequence fixed in the config file reading. 01/27/2013 Oleg Moskalenko Version 1.5.1.3 'Calder': - 'External' IP implemented for TURN-server-behind-NAT situation. 01/26/2013 Oleg Moskalenko Version 1.5.1.2 'Calder': - Alternative ports moved to 20000-ish territory. - Docs fixes. 01/22/2013 Oleg Moskalenko Version 1.5.1.1 'Calder': - Docs fixes. 01/22/2013 Oleg Moskalenko Version 1.5.1.0 'Calder': - C++ compatible headers and build. - C++ library header. - HTML-formatted development reference. 01/14/2013 Oleg Moskalenko Version 1.5.0.0 'Calder': - RFC 5769 check utility implemented. - RFC 5780 STUN extension implemented. 01/13/2013 Oleg Moskalenko Version 1.4.2.5 'Scale': - Issue 2 fixed. 01/08/2013 Oleg Moskalenko Version 1.4.2.4 'Scale': - Bogus "Bind to device" error message removed (Linux). - Docs improvements. 01/08/2013 Oleg Moskalenko Version 1.4.2.3 'Scale': - Bandwidth limitation implemented (--max-bps option). - DTLS communications improved. 01/07/2013 Oleg Moskalenko Version 1.4.2.2 'Scale': - Output messages fixed. - Peer test application accepts multiple listening addresses. - config search directories improved. 01/06/2013 Oleg Moskalenko Version 1.4.2.1 'Scale': - Examples directory structure fixed - Installation fixes - Output messages fixed 01/05/2013 Oleg Moskalenko Version 1.4.2 'Scale': - Daemon execution improved - Installation fixes - Added comments to the scripts 01/04/2013 Oleg Moskalenko Version 1.4.1.2 'Scale': - Configure script introduced - Installation fixes - Run as daemon 01/01/2013 Oleg Moskalenko Version 1.4.1 'Scale': - Options fixes - Build fixes - Script fixes - Installation fixes 12/31/2012 Oleg Moskalenko Version 1.4 'Scale': - Separate file for the dynamic user database - Build fixes - Script fixes - Logging fixes 12/29/2012 Oleg Moskalenko Version 1.3.0.2 'Ferro': - Debian 'squeeze' compilation fix 12/26/2012 Oleg Moskalenko Version 1.3.0.1 'Ferro': - install procedure minor improvements 12/24/2012 Oleg Moskalenko Version 1.3 'Ferro': - default conf file renamed to turnserver.conf - build script improved - client library linking fixed - install procedure 12/23/2012 Oleg Moskalenko Version 1.2.3 'Luthar': - turnserver options fixed - man page renamed to turnserver 12/22/2012 Oleg Moskalenko Version 1.2.2: - Man page fix 12/21/2012 Oleg Moskalenko Version 1.2.1 'Juvens': - Man page 12/21/2012 Oleg Moskalenko Version 1.2 'Euz': - Project cleaning 12/20/2012 Oleg Moskalenko Version 1.1 'no name': - DTLS extension 12/17/2012 Oleg Moskalenko Version 1.0 'no name': - RFC 5766 - RFC 6156 turnserver-4.5.1.1/LICENSE0000664000175000017500000000312613436577655013621 0ustar misimisi/* * TURN Server - RFC5766 TURN Server implementation * 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. */ turnserver-4.5.1.1/make-man.sh0000775000175000017500000000213413436577655014637 0ustar misimisi#!/bin/sh mkdir -p man/man1 rm -rf man/man1/*1 txt2man -s 1 -t TURN -I turnserver -I turnadmin -I turnutils -I turnutils_uclient -I turnutils_stunclient -I turnutils_rfc5769check -I turnutils_peer -I turnutils_natdiscovery -I turnutils_oauth -B "TURN Server" README.turnserver | sed -e 's/-/\\-/g' > man/man1/turnserver.1 txt2man -s 1 -t TURN -I turnserver -I turnadmin -I turnutils -I turnutils_uclient -I turnutils_stunclient -I turnutils_rfc5769check -I turnutils_peer -I turnutils_natdiscovery -I turnutils_oauth -B "TURN Server" README.turnadmin | sed -e 's/-/\\-/g'> man/man1/turnadmin.1 txt2man -s 1 -t TURN -I turnserver -I turnadmin -I turnutils -I turnutils_uclient -I turnutils_stunclient -I turnutils_rfc5769check -I turnutils_peer -I turnutils_natdiscovery -I turnutils_oauth -B "TURN Server" README.turnutils | sed -e 's/-/\\-/g' > man/man1/turnutils.1 cd man/man1 ln -s turnutils.1 turnutils_uclient.1 ln -s turnutils.1 turnutils_peer.1 ln -s turnutils.1 turnutils_stunclient.1 ln -s turnutils.1 turnutils_natdiscovery.1 ln -s turnutils.1 turnutils_oauth.1 ln -s turnserver.1 coturn.1 cd ../.. turnserver-4.5.1.1/man/0000775000175000017500000000000013436577655013365 5ustar misimisiturnserver-4.5.1.1/man/man1/0000775000175000017500000000000013436577655014221 5ustar misimisiturnserver-4.5.1.1/man/man1/turnutils.10000664000175000017500000003545213436577655016365 0ustar misimisi.\" Text automatically generated by txt2man .TH TURN 1 "29 January 2019" "" "" .SH GENERAL INFORMATION A set of turnutils_* programs provides some utility functionality to be used for testing and for setting up the TURN server. .TP .B 1. \fIturnutils_uclient\fP: emulates multiple UDP,TCP,TLS or DTLS clients. (this program is provided for the testing purposes only !) The compiled binary image of this program is located in bin/ sub\-directory. .TP .B 2. \fIturnutils_peer\fP: a simple stateless UDP\-only "echo" server, to be used as the final server in relay pattern ("peer"). For every incoming UDP packet, it simply echoes it back. (this program is provided for the testing purposes only !) When the test clients are communicating in the client\-to\-client manner (when the "\fIturnutils_uclient\fP" program is used with "\fB\-y\fP" option) then the \fIturnutils_peer\fP is not needed. .PP The compiled binary image of this program is located in bin/ subdirectory. .TP .B 3. \fIturnutils_stunclient\fP: a simple STUN client example. The compiled binary image of this program is located in bin/ subdirectory. .TP .B 4. \fIturnutils_rfc5769check\fP: a utility that checks the correctness of the STUN/TURN protocol implementation. This utility is used only for the compilation check procedure, it is not copied to the installation destination. .PP In the "examples/scripts" subdirectory, you will find the examples of command lines to be used to run the programs. The scripts are meant to be run from examples/ subdirectory, for example: .PP $ cd examples .PP $ ./scripts/secure_relay.sh .TP .B 5. \fIturnutils_natdiscovery\fP: a utility that provides NAT behavior discovery according RFC5780. This utility discovers the actual NAT Mapping and Filtering behavior, etc. Be aware that on TURN server side two different listening IP addresses should be configured to be able to work properly! .TP .B 6. \fIturnutils_oauth\fP: a utility that provides OAuth access_token \fBgeneration\fP(AEAD encryption), validation and decryption. This utility inputs all the keys and lifetimes and any related information that needed for creation and validationi of an access_token. It outputs a JSON with all OAuth PoP parameters that need to pass to the client. Output is generated accoriding RFC7635 Appendix B, Figure 8. .PP For more details, and for the access_token structure, read rfc7635, and see script in examples/scripts/oauth.sh. .RE .PP .RS ===================================== .SS NAME \fB \fBturnutils_uclient \fP\- this client emulation application is supplied for the test purposes only. \fB .SS SYNOPSIS .nf .fam C $ \fIturnutils_uclient\fP [\fB\-tTSvsyhcxg\fP] [\fIoptions\fP] .fam T .fi .fam T .fi .SS DESCRIPTION It was designed to simulate multiple clients. It uses asynch IO API in libevent to handle multiple clients. A client connects to the relay, negotiates the session, and sends multiple (configured number) messages to the server (relay), expecting the same number of replies. The length of the messages is configurable. The message is an arbitrary octet stream. The number of the messages to send is configurable. .PP Flags: .TP .B \fB\-t\fP Use TCP for communications between client and TURN server (default is UDP). .TP .B \fB\-b\fP Use SCTP for communications between client and TURN server (default is UDP). .TP .B \fB\-T\fP Use TCP for the relay transport (default \- UDP). Implies \fIoptions\fP \fB\-t\fP, \fB\-y\fP, \fB\-c\fP, and ignores flags and \fIoptions\fP \fB\-s\fP, \fB\-e\fP, \fB\-r\fP and \fB\-g\fP. Can be used together with \fB\-b\fP. .TP .B \fB\-P\fP Passive TCP (RFC6062 with active peer). Implies \fB\-T\fP. .TP .B \fB\-S\fP Secure SSL connection: SSL/TLS for TCP, DTLS for UDP, TLS/SCTP for SCTP. .TP .B \fB\-U\fP Secure unencrypted connection (suite eNULL): SSL/TLS for TCP, DTLS for UDP. .TP .B \fB\-v\fP Verbose. .TP .B \fB\-s\fP Use "Send" method in TURN; by default, it uses TURN Channels. .TP .B \fB\-y\fP Use client\-to\-client connections: RTP/RTCP pair of channels to another RTP/RTCP pair of channels. with this option the \fIturnutils_peer\fP application is not used, as the allocated relay endpoints are talking to each other. .TP .B \fB\-h\fP Hang on indefinitely after the last sent packet. .TP .B \fB\-c\fP Do not create rtcp connections. .TP .B \fB\-x\fP Request IPv6 relay address (RFC6156). .TP .B \fB\-X\fP IPv4 relay address explicitly requested. .TP .B \fB\-g\fP Set DONT_FRAGMENT parameter in TURN requests. .TP .B \fB\-D\fP Do mandatory channel padding even for UDP (like pjnath). .TP .B \fB\-N\fP do negative tests (some limited cases only). .TP .B \fB\-R\fP do negative protocol tests. .TP .B \fB\-O\fP DOS attack mode. .TP .B \fB\-M\fP Use TURN ICE Mobility. .TP .B \fB\-I\fP Do not set permissions on TURN relay endpoints (for testing the non\-standard server relay functionality). .TP .B \fB\-G\fP Generate extra requests (create permissions, channel bind). .TP .B \fB\-B\fP Random disconnect after a few initial packets. .TP .B \fB\-Z\fP Dual allocation (SSODA). Implies \fB\-c\fP option. .TP .B \fB\-J\fP Use oAuth with default test key kid='north'. .PP Options with required values: .TP .B \fB\-l\fP Message length (Default: 100 Bytes). .TP .B \fB\-i\fP Certificate file (for secure connections only, optional). .TP .B \fB\-k\fP Private key file (for secure connections only). .TP .B \fB\-E\fP CA file for server certificate verification, if the server certificate to be verified. .TP .B \fB\-p\fP \fBTURN Server\fP port (Defaults: 3478 unsecure, 5349 secure). .TP .B \fB\-n\fP Number of messages to send (Default: 5). .TP .B \fB\-d\fP Local interface device (optional, Linux only). .TP .B \fB\-L\fP Local IP address (optional). .TP .B \fB\-m\fP Number of clients (Default: 1, 2 or 4, depending on \fIoptions\fP). .TP .B \fB\-e\fP Peer address. .TP .B \fB\-r\fP Peer port (Default: 3480). .TP .B \fB\-z\fP Per\-session packet interval in milliseconds (Default: 20). .TP .B \fB\-u\fP STUN/TURN user name. .TP .B \fB\-w\fP STUN/TURN user password. .TP .B \fB\-W\fP TURN REST API secret. The "plain text" secret e.g. "north" that is stored in the value column of the turn_secret table in the database if dynamic, or the static\-auth\-secret value set in the configuration file if using static. .TP .B \fB\-C\fP This is the timestamp/username separator symbol (character) in TURN REST API. The default value is :. .TP .B \fB\-F\fP Cipher suite for TLS/DTLS. Default value is DEFAULT. .TP .B \fB\-o\fP the ORIGIN STUN attribute value. .TP .B \fB\-a\fP Bandwidth for the bandwidth request in ALLOCATE. The default value is zero. .PP See the examples in the "examples/scripts" directory. .PP ====================================== .SS NAME \fB \fBturnutils_peer \fP\- a simple UDP\-only echo backend server. \fB .SS SYNOPSIS .nf .fam C $ \fIturnutils_peer\fP [\fB\-v\fP] [\fIoptions\fP] .fam T .fi .fam T .fi .SS DESCRIPTION This application is used for the test purposes only, as a peer for the \fIturnutils_uclient\fP application. .PP Options with required values: .TP .B \fB\-p\fP Listening UDP port (Default: 3480). .TP .B \fB\-d\fP Listening interface device (optional) .TP .B \fB\-L\fP Listening address of \fIturnutils_peer\fP server. Multiple listening addresses can be used, IPv4 and IPv6. If no listener \fBaddress\fP(es) defined, then it listens on all IPv4 and IPv6 addresses. .TP .B \fB\-v\fP Verbose .PP ======================================== .SS NAME \fB \fBturnutils_stunclient \fP\- a basic STUN client. \fB .SS SYNOPSIS .nf .fam C $ \fIturnutils_stunclient\fP [\fIoptions\fP] .fam T .fi .fam T .fi .SS DESCRIPTION It sends a "new" STUN RFC 5389 request (over UDP) and shows the reply information. .PP Options with required values: .TP .B \fB\-p\fP STUN server port (Default: 3478). .TP .B \fB\-L\fP Local address to use (optional). .TP .B \fB\-f\fP Force RFC 5780 processing. .PP The \fIturnutils_stunclient\fP program checks the results of the first request, and if it finds that the STUN server supports RFC 5780 (the binding response reveals that) then the \fIturnutils_stunclient\fP makes a couple more requests with different parameters, to demonstrate the NAT discovery capabilities. .PP This utility does not support the "old" "classic" STUN protocol (RFC 3489). .PP ===================================== .SS NAME \fB \fBturnutils_rfc5769check \fP\- a utility that tests the correctness of STUN protocol implementation. \fB .SS SYNOPSIS .nf .fam C $ \fIturnutils_rfc5769check\fP .fam T .fi .fam T .fi .SS DESCRIPTION \fIturnutils_rfc5769check\fP tests the correctness of STUN protocol implementation against the test vectors predefined in RFC 5769 and prints the results of the tests on the screen. This utility is used only for the compilation check procedure, it is not copied to the installation destination. .PP Usage: .PP $ \fIturnutils_rfc5769check\fP .PP ===================================== .SS NAME \fB \fBturnutils_natdiscovery \fP\- a utility that discovers NAT mapping and filtering \fBbehavior according RFC5780. \fB .SS SYNOPSIS .nf .fam C $ \fIturnutils_natdiscovery\fP [\fIoptions\fP] .fam T .fi .fam T .fi .SS DESCRIPTION \fIturnutils_natdiscovery\fP discovers the NAT Mapping and Filtering behavior, to determine if that NAT is currently using Endpoint\-Independent, Address\-Dependent, or Address and Port\-Dependent Mapping and/or to determine if that NAT is currently using Endpoint\-Independent, Address\-Dependent, or Address and Port\-Dependent Filtering. .PP Use either \fB\-m\fP, \fB\-f\fP, \fB\-c\fP, \fB\-H\fP flag to discover NAT behavior. .PP Flags: .TP .B \fB\-m\fP NAT mapping behavior discovery .TP .B \fB\-f\fP NAT filtering behavior discovery .TP .B \fB\-t\fP NAT mapping lifetime behavior discovery Requires a timer (\fB\-T\fP) .TP .B \fB\-c\fP NAT collision behavior discovery .TP .B \fB\-H\fP NAT hairpinning behavior discovery .TP .B \fB\-P\fP Add 1500 byte Padding to the behavior discovery Applicable with all except NAT mapping Lifetime discovery .PP Options with required values: .TP .B \fB\-p\fP STUN server port (Default: 3478) .TP .B \fB\-L\fP Local address to use (optional) .TP .B \fB\-l\fP Local port to use (use with \fB\-L\fP) .TP .B \fB\-A\fP Secondary Local address (optional) Required for collision discovery .TP .B \fB\-T\fP Mapping lifetime timer (sec) Used by mapping lifetime behavior discovery .PP Usage: .PP $ \fIturnutils_natdiscovery\fP \fB\-m\fP \fB\-f\fP stun.example.com .PP ===================================== .SS NAME \fB \fBturnutils_oauth \fP\- a utility that helps OAuth access_token generation/encryption and validation/decyption \fB .SS SYNOPSIS .nf .fam C $ \fIturnutils_oauth\fP [\fIoptions\fP] .fam T .fi .fam T .fi .SS DESCRIPTION \fIturnutils_oauth\fP utilitiy provides help in OAuth access_token encryption and/or decryption with AEAD (Atuthenticated Encryption with Associated Data). It helps for an Auth Server in access_token creation, and also for debugging purposes it helps the access_token validation and decryption. This utility inputs all the keys and lifetimes and any related information that are needed for encryption or decryption of an access_token. It outputs a JSON with all OAuth PoP parameters that need to pass to the client. Output is generated accoriding RFC7635 Appendix B, Figure 8. This utility could help to build an Auth Server service, but be awere that this utility does not generate "session key" / "mac_key" and not verifies lifetime of "session key" / "mac_key" or "Auth key". For more details, and for the access_token structure, read rfc7635, and see the example in examples/scripts/oauth.sh. .PP Use either \fB\-e\fP and/or \fB\-d\fP flag to encrypt or decrypt access_token. .PP Flags: .TP .B \fB\-h\fP, \fB\-\-help\fP usage .TP .B \fB\-v\fP, \fB\-\-verbose\fP verbose mode .TP .B \fB\-e\fP, \fB\-\-encrypt\fP encrypt token .TP .B \fB\-d\fP, \fB\-\-decrypt\fP decrypt validate token .PP Options with required values: .TP .B \fB\-i\fP, \fB\-\-server\-name\fP server name (max. 255 char) .TP .B \fB\-j\fP, \fB\-\-auth\-key\-id\fP Auth key id (max. 32 char) .TP .B \fB\-k\fP, \fB\-\-auth\-key\fP base64 encoded Auth key .TP .B \fB\-l\fP \fB\-\-auth\-key\-timestamp\fP Auth key timestamp (sec since epoch) .TP .B \fB\-m\fP, \fB\-\-auth\-key\-lifetime\fP Auth key lifetime in sec .TP .B \fB\-n\fP, \fB\-\-auth\-key\-as\-rs\-alg\fP Authorization \fBServer\fP(AS) \- Resource \fBServer\fP(RS) encryption algorithm .TP .B \fB\-o\fP, \fB\-\-token\-nonce\fP base64 encoded nonce \fBbase64\fP(12 octet) = 16 char .TP .B \fB\-p\fP, \fB\-\-token\-mac\-key\fP base64 encoded MAC key \fBbase64\fP(32 octet) = 44 char .TP .B \fB\-q\fP, \fB\-\-token\-timestamp\fP timestamp in format 64 bit unsigned (Native format \- Unix), so 48 bit for secs since epoch UTC + 16 bit for 1/64000 fractions of a second. e.g.: the actual unixtimestamp 16 bit left shifted. (Default: actual gmtime) .TP .B \fB\-r\fP, \fB\-\-token\-lifetime\fP lifetime in sec (Default: 3600) .TP .B \fB\-t\fP, \fB\-\-token\fP base64 encoded encrypted token for validation and decryption .TP .B \fB\-u\fP, \fB\-\-hmac\-alg\fP stun client hmac algorithm .PP Usage: .PP $ \fIturnutils_natdiscovery\fP .PP =================================== .SH DOCS After installation, run the command: .PP $ man \fIturnutils\fP .PP or in the project root directory: .PP $ man \fB\-M\fP man \fIturnutils\fP .PP to see the man page. .PP ===================================== .SH FILES /etc/turnserver.conf .PP /var/db/turndb .PP /usr/local/var/db/turndb .PP /var/lib/turn/turndb .PP /usr/local/etc/turnserver.conf .PP ================================= .SH DIRECTORIES /usr/local/share/\fIturnserver\fP .PP /usr/local/share/doc/\fIturnserver\fP .PP /usr/local/share/examples/\fIturnserver\fP .PP =================================== .SH STANDARDS new STUN RFC 5389 .PP TURN RFC 5766 .PP TURN\-TCP extension RFC 6062 .PP TURN IPv6 extension RFC 6156 .PP STUN/TURN test vectors RFC 5769 .PP STUN NAT behavior discovery RFC 5780 .PP ==================================== .SH SEE ALSO \fIturnserver\fP, \fIturnadmin\fP .RE .PP ====================================== .SS WEB RESOURCES project page: .PP https://github.com/coturn/coturn/ .PP Wiki page: .PP https://github.com/coturn/coturn/wiki .PP forum: .PP https://groups.google.com/forum/?fromgroups=#!forum/turn\-server\-project\-rfc5766\-turn\-server/ .RE .PP ====================================== .SS AUTHORS Oleg Moskalenko .PP Gabor Kovesdan http://kovesdan.org/ .PP Daniel Pocock http://danielpocock.com/ .PP John Selbie (jselbie@gmail.com) .PP Lee Sylvester .PP Erik Johnston .PP Roman Lisagor .PP Vladimir Tsanev .PP Po\-sheng Lin .PP Peter Dunkley .PP Mutsutoshi Yoshimoto .PP Federico Pinna .PP Bradley T. Hughes .PP Mihaly Meszaros turnserver-4.5.1.1/man/man1/turnutils_uclient.10000777000175000017500000000000013436577655022225 2turnutils.1ustar misimisiturnserver-4.5.1.1/man/man1/turnserver.10000664000175000017500000013153113436577655016526 0ustar misimisi.\" Text automatically generated by txt2man .TH TURN 1 "29 January 2019" "" "" .SH GENERAL INFORMATION The \fBTURN Server\fP project contains the source code of a TURN server and TURN client messaging library. Also, some extra programs provided, for testing\-only purposes. .PP See the INSTALL file for the building instructions. .PP After the build, you will have the following binary images: .TP .B 1. \fIturnserver\fP: \fBTURN Server\fP relay. The compiled binary image of the \fBTURN Server\fP program is located in bin/ sub\-directory. .TP .B 2. \fIturnadmin\fP: TURN administration tool. See README.turnadmin and \fIturnadmin\fP man page. .TP .B 3. turnutils_uclient. See README.turnutils and \fIturnutils\fP man page. .TP .B 4. turnutils_peer. See README.turnutils and \fIturnutils\fP man page. .TP .B 5. turnutils_stunclient. See README.turnutils and \fIturnutils\fP man page. .TP .B 6. turnutils_rfc5769check. See README.turnutils and \fIturnutils\fP man page. .PP In the "examples/scripts" sub\-directory, you will find the examples of command lines to be used to run the programs. The scripts are meant to be run from examples/ sub\-directory, for example: .PP $ cd examples $ ./scripts/secure_relay.sh .SH RUNNING THE TURN SERVER Options note: \fIturnserver\fP has long and short option names, for most options. Some options have only long form, some options have only short form. Their syntax somewhat different, if an argument is required: .PP The short form must be used as this (for example): .PP .nf .fam C $ turnserver \-L 12.34.56.78 .fam T .fi The long form equivalent must use the "=" character: .PP .nf .fam C $ turnserver \-\-listening\-ip=12.34.56.78 .fam T .fi If this is a flag option (no argument required) then their usage are the same, for example: .PP .nf .fam C $ turnserver \-a .fam T .fi is equivalent to: .PP .nf .fam C $ turnserver \-\-lt\-cred\-mech .fam T .fi ===================================== .SS NAME \fB \fBturnserver \fP\- a TURN relay server implementation. \fB .SS SYNOPSIS .nf .fam C $ \fIturnserver\fP [\fB\-n\fP | \fB\-c\fP ] [\fIflags\fP] [ \fB\-\-userdb\fP= | \fB\-\-psql\-userdb\fP= | \fB\-\-mysql\-userdb\fP= | \fB\-\-mongo\-userdb\fP= | \fB\-\-redis\-userdb\fP= ] [\fB\-z\fP | \fB\-\-no\-auth\fP | \fB\-a\fP | \fB\-\-lt\-cred\-mech\fP ] [\fIoptions\fP] $ \fIturnserver\fP \fB\-h\fP .fam T .fi .fam T .fi .SS DESCRIPTION .TP .B Config file settings: .TP .B \fB\-n\fP Do not use configuration file, use only command line parameters. .TP .B \fB\-c\fP Configuration file name (default \- turnserver.conf). The format of config file can be seen in the supplied examples/etc/turnserver.conf example file. Long names of the \fIoptions\fP are used as the configuration items names in the file. If not an absolute path is supplied, then the file is searched in the following directories: .RS .IP \(bu 3 current directory .IP \(bu 3 current directory etc/ sub\-directory .IP \(bu 3 upper directory level etc/ .IP \(bu 3 /etc/ .IP \(bu 3 /usr/local/etc/ .IP \(bu 3 installation directory /etc .RE .TP .B User database settings: .TP .B \fB\-b\fP, \fB\-\-db\fP, \fB\-\-userdb\fP SQLite user database file name (default \- /var/db/turndb or /usr/local/var/db/turndb or /var/lib/turn/turndb). .TP .B \fB\-e\fP, \fB\-\-psql\-userdb\fP User database connection string for PostgreSQL. This database can be used for long\-term credentials mechanism, and it can store the secret value for secret\-based timed authentication in TURN RESP API. The connection string format is like that: .RS .PP "host= dbname= user= password= connect_timeout=" (for 8.x or newer Postgres). .PP Or: .PP "postgresql://username:password@hostname:port/databasename" (for 9.x or newer Postgres). .PP See the INSTALL file for more explanations and examples. .PP Also, see http://www.PostgreSQL.org for full PostgreSQL documentation. .RE .TP .B \fB\-M\fP, \fB\-\-mysql\-userdb\fP User database connection string for MySQL or MariaDB. This database can be used for long\-term credentials mechanism, and it can store the secret value for secret\-based timed authentication in TURN RESP API. The connection string format is like that: .RS .PP "host= dbname= user= password= connect_timeout= read_timeout=" .PP See the INSTALL file for more explanations and examples. .PP Also, see http://www.mysql.org or http://mariadb.org for full MySQL documentation. .PP Optional connection string parameters for the secure communications (SSL): ca, capath, cert, key, cipher (see http://dev.mysql.com/doc/refman/5.1/en/ssl\-options.html for the command \fIoptions\fP description). .RE .TP .B \fB\-\-secret\-key\-file\fP This is the file path which contain secret key of aes encryption while using MySQL password encryption. If you want to use in the MySQL connection string the password in encrypted format, then set in this option the file path of the secret key. The key which is used to encrypt MySQL password. Warning: If this option is set, then MySQL password must be set in "mysql\-userdb" option in encrypted format! If you want to use cleartext password then do not set this option! .TP .B \fB\-J\fP, \fB\-\-mongo\-userdb\fP User database connection string for MongoDB. This database can be used for long\-term credentials mechanism, and it can store the secret value for secret\-based timed authentication in TURN RESP API. The connection string format is like that: .RS .PP "mongodb://username:password@host:port/database?\fIoptions\fP" .PP See the INSTALL file for more explanations and examples. .PP Also, see http://docs.mongodb.org/manual/ for full MongoDB documentation. .RE .TP .B \fB\-N\fP, \fB\-\-redis\-userdb\fP User database connection string for Redis. This database can be used for long\-term credentials mechanism, and it can store the secret value for secret\-based timed authentication in TURN RESP API. The connection string format is like that: .RS .PP "ip= dbname= password= connect_timeout=" .PP See the INSTALL file for more explanations and examples. .PP Also, see http://redis.io for full Redis documentation. .RE .TP .B Flags: .TP .B \fB\-v\fP, \fB\-\-verbose\fP Moderate verbose mode. .TP .B \fB\-V\fP, \fB\-\-Verbose\fP Extra verbose mode, very annoying and not recommended. .TP .B \fB\-o\fP, \fB\-\-daemon\fP Run server as daemon. .TP .B \fB\-\-prod\fP Production mode: hide the software version. .TP .B \fB\-f\fP, \fB\-\-fingerprint\fP Use fingerprints in the TURN messages. If an incoming request contains a fingerprint, then TURN server will always add fingerprints to the messages in this session, regardless of the per\-server setting. .TP .B \fB\-a\fP, \fB\-\-lt\-cred\-mech\fP Use long\-term credentials mechanism (this one you need for WebRTC usage). .TP .B \fB\-z\fP, \fB\-\-no\-auth\fP Do not use any credentials mechanism, allow anonymous access. Opposite to \fB\-a\fP and \fB\-A\fP \fIoptions\fP. This is default option when no authentication\-related \fIoptions\fP are set. By default, no credential mechanism is used \- any user is allowed. .TP .B \fB\-\-use\-auth\-secret\fP TURN REST API flag. Flag that sets a special WebRTC authorization option that is based upon authentication secret. The feature purpose is to support "\fBTURN Server\fP REST API" as described in the TURN REST API section below. This option uses timestamp as part of combined username: usercombo \-> "timestamp:username", turn user \-> usercombo, turn password \-> \fBbase64\fP(hmac(input_buffer = usercombo, key = shared\-secret)). This allows TURN credentials to be accounted for a specific user id. If you don't have a suitable id, the timestamp alone can be used. This option is just turns on secret\-based authentication. The actual value of the secret is defined either by option static\-auth\-secret, or can be found in the turn_secret table in the database. .TP .B \fB\-\-oauth\fP Support oAuth authentication, as in the third\-party STUN/TURN RFC 7635. .TP .B \fB\-\-dh566\fP Use 566 bits predefined DH TLS key. Default size of the key is 1066. .TP .B \fB\-\-dh2066\fP Use 2066 bits predefined DH TLS key. Default size of the key is 1066. .TP .B \fB\-\-no\-tlsv1\fP Do not allow TLSv1/DTLSv1 protocol. .TP .B \fB\-\-no\-tlsv1_1\fP Do not allow TLSv1.1 protocol. .TP .B \fB\-\-no\-tlsv1_2\fP Do not allow TLSv1.2/DTLSv1.2 protocol. .TP .B \fB\-\-no\-udp\fP Do not start UDP client listeners. .TP .B \fB\-\-no\-tcp\fP Do not start TCP client listeners. .TP .B \fB\-\-no\-tls\fP Do not start TLS client listeners. .TP .B \fB\-\-no\-dtls\fP Do not start DTLS client listeners. .TP .B \fB\-\-no\-udp\-relay\fP Do not allow UDP relay endpoints defined in RFC 5766, use only TCP relay endpoints as defined in RFC 6062. .TP .B \fB\-\-no\-tcp\-relay\fP Do not allow TCP relay endpoints defined in RFC 6062, use only UDP relay endpoints as defined in RFC 5766. .TP .B \fB\-\-no\-stdout\-log\fP Flag to prevent stdout log messages. By default, all log messages are going to both stdout and to the configured log file. With this option everything will be going to the log file only (unless the log file itself is stdout). .TP .B \fB\-\-syslog\fP With this flag, all log will be redirected to the system log (syslog). .TP .B \fB\-\-simple\-log\fP This flag means that no log file rollover will be used, and the log file name will be constructed as\-is, without PID and date appendage. This option can be used, for example, together with the logrotate tool. .TP .B \fB\-\-secure\-stun\fP Require authentication of the STUN Binding request. By default, the clients are allowed anonymous access to the STUN Binding functionality. .TP .B \fB\-S\fP, \fB\-\-stun\-only\fP Run as STUN server only, all TURN requests will be ignored. Option to suppress TURN functionality, only STUN requests will be processed. .TP .B \fB\-\-no\-stun\fP Run as TURN server only, all STUN requests will be ignored. Option to suppress STUN functionality, only TURN requests will be processed. .TP .B \fB\-\-allow\-loopback\-peers\fP Allow peers on the loopback addresses (127.x.x.x and ::1). Allow it only for testing in a development environment! In production it adds a possible security vulnerability, and so due to security reasons, it is not allowed using it together with empty cli\-password. .TP .B \fB\-\-no\-multicast\-peers\fP Disallow peers on well\-known broadcast addresses (224.0.0.0 and above, and FFXX:*). .TP .B \fB\-\-mobility\fP Mobility with ICE (MICE) specs support. .TP .B \fB\-\-no\-cli\fP Turn OFF the CLI support. By default it is always ON. See also \fIoptions\fP \fB\-\-cli\-ip\fP and \fB\-\-cli\-port\fP. .TP .B \fB\-\-server\-relay\fP Server relay. NON\-STANDARD AND DANGEROUS OPTION. Only for those applications when we want to run server applications on the relay endpoints. This option eliminates the IP permissions check on the packets incoming to the relay endpoints. See http://tools.ietf.org/search/rfc5766#section\-17.2.3 . .TP .B \fB\-\-udp\-self\-balance\fP (recommended for older Linuxes only) Automatically balance UDP traffic over auxiliary servers (if configured). The load balancing is using the ALTERNATE\-SERVER mechanism. The TURN client must support 300 ALTERNATE\-SERVER response for this functionality. .TP .B \fB\-\-check\-origin\-consistency\fP The flag that sets the origin consistency check: across the session, all requests must have the same main ORIGIN attribute value (if the ORIGIN was initially used by the session). .TP .B \fB\-h\fP Help. .TP .B Options with values: .TP .B \fB\-\-stale\-nonce\fP[=] Use extra security with nonce value having limited lifetime, in seconds (default 600 secs). .TP .B \fB\-\-max\-allocate\-lifetime\fP Set the maximum value for the allocation lifetime. Default to 3600 secs. .TP .B \fB\-\-channel\-lifetime\fP Set the lifetime for channel binding, default to 600 secs. This value MUST not be changed for production purposes. .TP .B \fB\-\-permission\-lifetime\fP Set the value for the lifetime of the permission. Default to 300 secs. This MUST not be changed for production purposes. .TP .B \fB\-d\fP, \fB\-\-listening\-device\fP Listener interface device. (NOT RECOMMENDED. Optional functionality, Linux only). The \fIturnserver\fP process must have root privileges to bind the listening endpoint to a device. If \fIturnserver\fP must run as a process without root privileges, then just do not use this setting. .TP .B \fB\-L\fP, \fB\-\-listening\-ip\fP Listener IP address of relay server. Multiple listeners can be specified, for example: \fB\-L\fP ip1 \fB\-L\fP ip2 \fB\-L\fP ip3 If no \fBIP\fP(s) specified, then all IPv4 and IPv6 system IPs will be used for listening. The same \fBip\fP(s) can be used as both listening and relay \fBip\fP(s). .TP .B \fB\-p\fP, \fB\-\-listening\-port\fP TURN listener port for UDP and TCP listeners (Default: 3478). Note: actually, TLS & DTLS sessions can connect to the "plain" TCP & UDP \fBport\fP(s), too \- if allowed by configuration. .TP .B \fB\-\-tls\-listening\-port\fP TURN listener port for TLS and DTLS listeners (Default: 5349). Note: actually, "plain" TCP & UDP sessions can connect to the TLS & DTLS \fBport\fP(s), too \- if allowed by configuration. The TURN server "automatically" recognizes the type of traffic. Actually, two listening endpoints (the "plain" one and the "tls" one) are equivalent in terms of functionality; but we keep both endpoints to satisfy the RFC 5766 specs. For secure TCP connections, we currently support SSL version 3 and TLS versions 1.0, 1.1, 1.2. For secure UDP connections, we support DTLS version 1. .TP .B \fB\-\-alt\-listening\-port\fP Alternative listening port for UDP and TCP listeners; default (or zero) value means "listening port plus one". This is needed for STUN CHANGE_REQUEST \- in RFC 5780 sense or in old RFC 3489 sense \- for NAT behavior discovery). The \fBTURN Server\fP supports CHANGE_REQUEST only if it is started with more than one listening IP address of the same family (IPv4 or IPv6). The CHANGE_REQUEST is only supported by UDP protocol, other protocols are listening on that endpoint only for "symmetry". .TP .B \fB\-\-alt\-tls\-listening\-port\fP Alternative listening port for TLS and DTLS protocols. Default (or zero) value means "TLS listening port plus one". .TP .B \fB\-\-aux\-server\fP Auxiliary STUN/TURN server listening endpoint. Aux servers have almost full TURN and STUN functionality. The (minor) limitations are: .RS .IP 1) 4 Auxiliary servers do not have alternative ports and they do not support STUN RFC 5780 functionality (CHANGE REQUEST). .IP 2) 4 Auxiliary servers also are never returning ALTERNATIVE\-SERVER reply. .RE .PP Valid formats are 1.2.3.4:5555 for IPv4 and [1:2::3:4]:5555 for IPv6. There may be multiple aux\-server \fIoptions\fP, each will be used for listening to client requests. .TP .B \fB\-i\fP, \fB\-\-relay\-device\fP Relay interface device for relay sockets (NOT RECOMMENDED. Optional, Linux only). .TP .B \fB\-E\fP, \fB\-\-relay\-ip\fP Relay address (the local IP address that will be used to relay the packets to the peer). Multiple relay addresses may be used: \fB\-E\fP ip1 \fB\-E\fP ip2 \fB\-E\fP ip3 The same \fBIP\fP(s) can be used as both listening \fBIP\fP(s) and relay \fBIP\fP(s). If no relay \fBIP\fP(s) specified, then the \fIturnserver\fP will apply the default policy: it will decide itself which relay addresses to be used, and it will always be using the client socket IP address as the relay IP address of the TURN session (if the requested relay address family is the same as the family of the client socket). .TP .B \fB\-X\fP, \fB\-\-external\-ip\fP \fBTURN Server\fP public/private address mapping, if the server is behind NAT. In that situation, if a \fB\-X\fP is used in form "\fB\-X\fP " then that ip will be reported as relay IP address of all allocations. This scenario works only in a simple case when one single relay address is be used, and no CHANGE_REQUEST functionality is required. That single relay address must be mapped by NAT to the 'external' IP. The "external\-ip" value, if not empty, is returned in XOR\-RELAYED\-ADDRESS field. For that 'external' IP, NAT must forward ports directly (relayed port 12345 must be always mapped to the same 'external' port 12345). In more complex case when more than one IP address is involved, that option must be used several times, each entry must have form "\fB\-X\fP ", to map all involved addresses. CHANGE_REQUEST (RFC5780 or RFC3489) NAT discovery STUN functionality will work correctly, if the addresses are mapped properly, even when the TURN server itself is behind A NAT. By default, this value is empty, and no address mapping is used. .TP .B \fB\-m\fP, \fB\-\-relay\-threads\fP Number of the relay threads to handle the established connections (in addition to authentication thread and the listener thread). If explicitly set to 0 then application runs relay process in a single thread, in the same thread with the listener process (the authentication thread will still be a separate thread). If not set, then a default optimal algorithm will be employed (OS\-dependent). In the older Linux systems (before Linux kernel 3.9), the number of UDP threads is always one threads per network listening endpoint \- unless "\fB\-m\fP 0" or "\fB\-m\fP 1" is set. .TP .B \fB\-\-min\-port\fP Lower bound of the UDP port range for relay endpoints allocation. Default value is 49152, according to RFC 5766. .TP .B \fB\-\-max\-port\fP Upper bound of the UDP port range for relay endpoints allocation. Default value is 65535, according to RFC 5766. .TP .B \fB\-u\fP, \fB\-\-user\fP Long\-term security mechanism credentials user account, in the column\-separated form username:key. Multiple user accounts may be used in the command line. The key is either the user password, or the key is generated by \fIturnadmin\fP command. In the second case, the key must be prepended with 0x symbols. The key is calculated over the user name, the user realm, and the user password. This setting may not be used with TURN REST API. .TP .B \fB\-r\fP, \fB\-\-realm\fP The default realm to be used for the users when no explicit origin/realm relationship was found in the database, or if the TURN server is not using any database (just the commands\-line settings and the userdb file). Must be used with long\-term credentials mechanism or with TURN REST API. .TP .B \fB\-C\fP, \fB\-\-rest\-api\-separator\fP This is the timestamp/username separator symbol (character) in TURN REST API. The default value is :. .TP .B \fB\-q\fP, \fB\-\-user\-quota\fP Per\-user allocations quota: how many concurrent allocations a user can create. This option can also be set through the database, for a particular realm. .TP .B \fB\-Q\fP, \fB\-\-total\-quota\fP Total allocations quota: global limit on concurrent allocations. This option can also be set through the database, for a particular realm. .TP .B \fB\-s\fP, \fB\-\-max\-bps\fP Max bytes\-per\-second bandwidth a TURN session is allowed to handle (input and output network streams are treated separately). Anything above that limit will be dropped or temporary suppressed (within the available buffer limits). This option can also be set through the database, for a particular realm. .TP .B \fB\-B\fP, \fB\-\-bps\-capacity\fP Maximum server capacity. Total bytes\-per\-second bandwidth the TURN server is allowed to allocate for the sessions, combined (input and output network streams are treated separately). .TP .B \fB\-\-static\-auth\-secret\fP Static authentication secret value (a string) for TURN REST API only. If not set, then the turn server will try to use the dynamic value in turn_secret table in user database (if present). The database\-stored value can be changed on\-the\-fly by a separate program, so this is why that other mode is dynamic. Multiple shared secrets can be used (both in the database and in the "static" fashion). .TP .B \fB\-\-server\-name\fP Server name used for the oAuth authentication purposes. The default value is the realm name. .TP .B \fB\-\-cert\fP Certificate file, PEM format. Same file search rules applied as for the configuration file. If both \fB\-\-no\-tls\fP and \fB\-\-no\-dtls\fP \fIoptions\fP are specified, then this parameter is not needed. Default value is turn_server_cert.pem. .TP .B \fB\-\-pkey\fP Private key file, PEM format. Same file search rules applied as for the configuration file. If both \fB\-\-no\-tls\fP and \fB\-\-no\-dtls\fP \fIoptions\fP are specified, then this parameter is not needed. Default value is turn_server_pkey.pem. .TP .B \fB\-\-pkey\-pwd\fP If the private key file is encrypted, then this password to be used. .TP .B \fB\-\-cipher\-list\fP Allowed OpenSSL cipher list for TLS/DTLS connections. Default value is "DEFAULT". .TP .B \fB\-\-CA\-file\fP CA file in OpenSSL format. Forces TURN server to verify the client SSL certificates. By default, no CA is set and no client certificate check is performed. .TP .B \fB\-\-ec\-curve\-name\fP Curve name for EC ciphers, if supported by OpenSSL library (TLS and DTLS). The default value is prime256v1, if pre\-OpenSSL 1.0.2 is used. With OpenSSL 1.0.2+, an optimal curve will be automatically calculated, if not defined by this option. .TP .B \fB\-\-dh\-file\fP Use custom DH TLS key, stored in PEM format in the file. Flags \fB\-\-dh566\fP and \fB\-\-dh2066\fP are ignored when the DH key is taken from a file. .TP .B \fB\-l\fP, \fB\-\-log\-file\fP Option to set the full path name of the log file. By default, the \fIturnserver\fP tries to open a log file in /var/log/\fIturnserver\fP, /var/log, /var/tmp, /tmp and . (current) directories (which file open operation succeeds first that file will be used). With this option you can set the definite log file name. The special names are "stdout" and "\-" \- they will force everything to the stdout. Also, "syslog" name will redirect everything into the system log (syslog), as if the option "\fB\-\-syslog\fP" was set. In the runtime, the logfile can be reset with the SIGHUP signal to the \fIturnserver\fP process. .TP .B \fB\-\-alternate\-server\fP Option to set the "redirection" mode. The value of this option will be the address of the alternate server for UDP & TCP service in form of [:]. The server will send this value in the attribute ALTERNATE\-SERVER, with error 300, on ALLOCATE request, to the client. Client will receive only values with the same address family as the client network endpoint address family. See RFC 5389 and RFC 5766 for ALTERNATE\-SERVER functionality description. The client must use the obtained value for subsequent TURN communications. If more than one \fB\-\-alternate\-server\fP \fIoptions\fP are provided, then the functionality can be more accurately described as "load\-balancing" than a mere "redirection". If the port number is omitted, then the default port number 3478 for the UDP/TCP protocols will be used. Colon (:) characters in IPv6 addresses may conflict with the syntax of the option. To alleviate this conflict, literal IPv6 addresses are enclosed in square brackets in such resource identifiers, for example: [2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478 . Multiple alternate servers can be set. They will be used in the round\-robin manner. All servers in the pool are considered of equal weight and the load will be distributed equally. For example, if we have 4 alternate servers, then each server will receive 25% of ALLOCATE requests. An alternate TURN server address can be used more than one time with the alternate\-server option, so this can emulate "weighting" of the servers. .TP .B \fB\-\-tls\-alternate\-server\fP Option to set alternative server for TLS & DTLS services in form of :. If the port number is omitted, then the default port number 5349 for the TLS/DTLS protocols will be used. See the previous option for the functionality description. .TP .B \fB\-O\fP, \fB\-\-redis\-statsdb\fP Redis status and statistics database connection string, if used (default \- empty, no Redis stats DB used). This database keeps allocations status information, and it can be also used for publishing and delivering traffic and allocation event notifications. This database option can be used independently of \fB\-\-redis\-userdb\fP option, and actually Redis can be used for status/statistics and SQLite or MySQL or MongoDB or PostgreSQL can be used for the user database. The connection string has the same parameters as redis\-userdb connection string. .TP .B \fB\-\-max\-allocate\-timeout\fP Max time, in seconds, allowed for full allocation establishment. Default is 60 seconds. .PP \fB\-\-denied\-peer\-ip\fP= .PP \fB\-\-allowed\-peer\-ip\fP= Options to ban or allow specific ip addresses or ranges of ip addresses. If an ip address is specified as both allowed and denied, then the ip address is considered to be allowed. This is useful when you wish to ban a range of ip addresses, except for a few specific ips within that range. This can be used when you do not want users of the turn server to be able to access machines reachable by the turn server, but would otherwise be unreachable from the internet (e.g. when the turn server is sitting behind a NAT). The 'white" and "black" peer IP ranges can also be dynamically changed in the database. The allowed/denied addresses (white/black lists) rules are very simple: .RS .IP 1) 4 If there is no rule for an address, then it is allowed; .IP 2) 4 If there is an "allowed" rule that fits the address then it is allowed \- no matter what; .IP 3) 4 If there is no "allowed" rule that fits the address, and if there is a "denied" rule that fits the address, then it is denied. .RE .TP .B \fB\-\-pidfile\fP File name to store the pid of the process. Default is /var/run/turnserver.pid (if superuser account is used) or /var/tmp/turnserver.pid . .TP .B \fB\-\-proc\-user\fP User name to run the process. After the initialization, the \fIturnserver\fP process will make an attempt to change the current user ID to that user. .TP .B \fB\-\-proc\-group\fP Group name to run the process. After the initialization, the \fIturnserver\fP process will make an attempt to change the current group ID to that group. .TP .B \fB\-K\fP, \fB\-\-keep\-address\-family\fP TURN server allocates address family according TURN Client <=> Server communication address family. !! It breaks RFC6156 section\-4.2 (violates default IPv4) !! .TP .B \fB\-\-cli\-ip\fP Local system IP address to be used for CLI management interface. The \fIturnserver\fP process can be accessed for management with telnet, at this IP address and on the CLI port (see the next parameter). Default value is 127.0.0.1. You can use telnet or putty (in telnet mode) to access the CLI management interface. .TP .B \fB\-\-cli\-port\fP CLI management interface listening port. Default is 5766. .TP .B \fB\-\-cli\-password\fP CLI access password. Default is empty (no password). For the security reasons, it is recommended to use the encrypted form of the password (see the \fB\-P\fP command in the \fIturnadmin\fP utility). The dollar signs in the encrypted form must be escaped. .TP .B \fB\-\-cli\-max\-output\-sessions\fP Maximum number of output sessions in ps CLI command. This value can be changed on\-the\-fly in CLI. The default value is 256. .TP .B \fB\-\-web\-admin\fP Enable Turn Web\-admin support. By default it is disabled. .TP .B \fB\-\-web\-admin\-ip\fP= Local system IP address to be used for Web\-admin server endpoint. Default value is 127.0.0.1. .TP .B \fB\-\-web\-admin\-port\fP= Web\-admin server port. Default is 8080. .TP .B \fB\-\-web\-admin\-listen\-on\-workers\fP Enable for web\-admin server to listens on STUN/TURN workers STUN/TURN ports. By default it is disabled for security resons! (This behavior used to be the default behavior, and was enabled by default.) .TP .B \fB\-\-ne\fP=[1|2|3] Set network engine type for the process (for internal purposes). .PP ================================== .SH LOAD BALANCE AND PERFORMANCE TUNING This topic is covered in the wiki page: .PP https://github.com/coturn/coturn/wiki/turn_performance_and_load_balance .PP =================================== .SH WEBRTC USAGE This is a set of notes for the WebRTC users: .IP 1) 4 WebRTC uses long\-term authentication mechanism, so you have to use \fB\-a\fP option (or \fB\-\-lt\-cred\-mech\fP). WebRTC relaying will not work with anonymous access. With \fB\-a\fP option, do not forget to set the default realm (\fB\-r\fP option). You will also have to set up the user accounts, for that you have a number of \fIoptions\fP: .PP .nf .fam C a) command\-line options (\-u). b) a database table (SQLite or PostgreSQL or MySQL or MongoDB). You will have to set keys with turnadmin utility (see docs and wiki for turnadmin). You cannot use open passwords in the database. c) Redis key/value pair(s), if Redis is used. You key use either keys or open passwords with Redis; see turndb/testredisdbsetup.sh file. d) You also can use the TURN REST API. You will need shared secret(s) set either through the command line option, or through the config file, or through the database table or Redis key/value pairs. .fam T .fi .IP 2) 4 Usually WebRTC uses fingerprinting (\fB\-f\fP). .IP 3) 4 \fB\-v\fP option may be nice to see the connected clients. .IP 4) 4 \fB\-X\fP is needed if you are running your TURN server behind a NAT. .IP 5) 4 \fB\-\-min\-port\fP and \fB\-\-max\-port\fP may be needed if you want to limit the relay endpoints ports number range. .PP =================================== .SH TURN REST API In WebRTC, the browser obtains the TURN connection information from the web server. This information is a secure information \- because it contains the necessary TURN credentials. As these credentials are transmitted over the public networks, we have a potential security breach. .PP If we have to transmit a valuable information over the public network, then this information has to have a limited lifetime. Then the guy who obtains this information without permission will be able to perform only limited damage. .PP This is how the idea of TURN REST API \- time\-limited TURN credentials \- appeared. This security mechanism is based upon the long\-term credentials mechanism. The main idea of the REST API is that the web server provides the credentials to the client, but those credentials can be used only limited time by an application that has to create a TURN server connection. .PP The "classic" long\-term credentials mechanism (LTCM) is described here: .PP http://tools.ietf.org/html/rfc5389#section\-10.2 .PP http://tools.ietf.org/html/rfc5389#section\-15.4 .PP For authentication, each user must know two things: the username and the password. Optionally, the user must supply the ORIGIN value, so that the server can figure out the realm to be used for the user. The nonce and the realm values are supplied by the TURN server. But LTCM is not saying anything about the nature and about the persistence of the username and of the password; and this is used by the REST API. .PP In the TURN REST API, there is no persistent passwords for users. A user has just the username. The password is always temporary, and it is generated by the web server on\-demand, when the user accesses the WebRTC page. And, actually, a temporary one\-time session only, username is provided to the user, too. .PP The temporary user is generated as: .PP temporary\-username="timestamp" + ":" + "username" .PP where username is the persistent user name, and the timestamp format is just seconds sinse 1970 \- the same value as \fBtime\fP(NULL) function returns. .PP The temporary password is obtained as HMAC\-SHA1 function over the temporary username, with shared secret as the HMAC key, and then the result is encoded: .PP temporary\-password = \fBbase64_encode\fP(hmac\-sha1(shared\-secret, temporary\-username)) .PP Both the TURN server and the web server know the same shared secret. How the shared secret is distributed among the involved entities is left to the WebRTC deployment details \- this is beyond the scope of the TURN REST API. .PP So, a timestamp is used for the temporary password calculation, and this timestamp can be retrieved from the temporary username. This information is valuable, but only temporary, while the timestamp is not expired. Without knowledge of the shared secret, a new temporary password cannot be generated. .PP This is all formally described in Justin's Uberti TURN REST API document that can be obtained following the link "TURN REST API" in the \fBTURN Server\fP project's page https://github.com/coturn/coturn/. .PP Once the temporary username and password are obtained by the client (browser) application, then the rest is just 'classic" long\-term credentials mechanism. For developers, we are going to describe it step\-by\-step below: .RS .IP \(bu 3 a new TURN client sends a request command to the TURN server. Optionally, it adds the ORIGIN field to it. .IP \(bu 3 TURN server sees that this is a new client and the message is not authenticated. .IP \(bu 3 the TURN server generates a random nonce string, and return the error 401 to the client, with nonce and realm included. If the ORIGIN field was present in the client request, it may affect the realm value that the server chooses for the client. .IP \(bu 3 the client sees the 401 error and it extracts two values from the error response: the nonce and the realm. .IP \(bu 3 the client uses username, realm and password to produce a key: .PP .nf .fam C key = MD5(username ":" realm ":" SASLprep(password)) .fam T .fi (SASLprep is described here: http://tools.ietf.org/html/rfc4013) .IP \(bu 3 the client forms a new request, adds username, realm and nonce to the request. Then, the client calculates and adds the integrity field to the request. This is the trickiest part of the process, and it is described in the end of section 15.4: http://tools.ietf.org/html/rfc5389#section\-15.4 .IP \(bu 3 the client, optionally, adds the fingerprint field. This may be also a tricky procedure, described in section 15.5 of the same document. WebRTC usually uses fingerprinted TURN messages. .IP \(bu 3 the TURN server receives the request, reads the username. .IP \(bu 3 then the TURN server checks that the nonce and the realm in the request are the valid ones. .IP \(bu 3 then the TURN server calculates the key. .IP \(bu 3 then the TURN server calculates the integrity field. .IP \(bu 3 then the TURN server compares the calculated integrity field with the received one \- they must be the same. If the integrity fields differ, then the request is rejected. .RE .PP In subsequent communications, the client may go with exactly the same sequence, but for optimization usually the client, having already information about realm and nonce, pre\-calculates the integrity string for each request, so that the 401 error response becomes unnecessary. The TURN server may use "\fB\-\-stale\-nonce\fP" option for extra security: in some time, the nonce expires and the client will obtain 438 error response with the new nonce, and the client will have to start using the new nonce. .PP In subsequent communications, the sever and the client will always assume the same password \- the original password becomes the session parameter and is never expiring. So the password is not changing while the session is valid and unexpired. So, if the session is properly maintained, it may go forever, even if the user password has been already changed (in the database). The session simply is using the old password. Once the session got disconnected, the client will have to use the new password to re\-connect (if the password has been changed). .PP An example when a new shared secret is generated every hour by the TURN server box and then supplied to the web server, remotely, is provided in the script examples/scripts/restapi/shared_secret_maintainer.pl . .PP A very important thing is that the nonce must be totally random and it must be different for different clients and different sessions. .PP =================================== .SH DATABASES For the user database, the \fIturnserver\fP has the following \fIoptions\fP: .IP 1) 4 Users can be set in the command line, with multiple \fB\-u\fP or \fB\-\-user\fP \fIoptions\fP. Obviously, only a few users can be set that way, and their credentials are fixed for the \fIturnserver\fP process lifetime. .IP 2) 4 Users can be stored in SQLite DB. The default SQLite database file is /var/db/turndb or /usr/local/var/db/turndb or /var/lib/turn/turndb. .IP 3) 4 Users can be stored in PostgreSQL database, if the \fIturnserver\fP was compiled with PostgreSQL support. Each time \fIturnserver\fP checks user credentials, it reads the database (asynchronously, of course, so that the current flow of packets is not delayed in any way), so any change in the database content is immediately visible by the \fIturnserver\fP. This is the way if you need the best scalability. The schema for the database can be found in schema.sql file. For long\-term credentials, you have to set the "keys" for the users; the "keys" are generated by the \fIturnadmin\fP utility. For the key generation, you need username, password and the realm. All users in the database must use the same realm value; if down the road you will decide to change the realm name, then you will have to re\-generate all user keys (that can be done in a batch script). See the file turndb/testsqldbsetup.sql as an example. .IP 4) 4 The same is true for MySQL database. The same schema file is applicable. The same considerations are applicable. .IP 5) 4 The same is true for the Redis database, but the Redis database has aa different schema \- it can be found (in the form of explanation) in schema.userdb.redis. Also, in Redis you can store both "keys" and open passwords (for long term credentials) \- the "open password" option is less secure but more convenient for low\-security environments. See the file turndb/testredisdbsetup.sh as an example. .IP 6) 4 If a database is used, then users can be divided into multiple independent realms. Each realm can be administered separately, and each realm can have its own set of users and its own performance \fIoptions\fP (max\-bps, user\-quota, total\-quota). .IP 7) 4 If you use MongoDB, the database will be setup for you automatically. .IP 8) 4 Of course, the \fIturnserver\fP can be used in non\-secure mode, when users are allowed to establish sessions anonymously. But in most cases (like WebRTC) that will not work. .PP For the status and statistics database, there are two choices: .IP 1) 4 The simplest choice is not to use it. Do not set \fB\-\-redis\-statsdb\fP option, and this functionality will be simply ignored. .IP 2) 4 If you choose to use it, then set the \fB\-\-redis\-statsdb\fP option. This may be the same database as in \fB\-\-redis\-userdb\fP option, or it may be a different database. You may want to use different database for security or convenience reasons. Also, you can use different database management systems for the user database and for the ststus and statistics database. For example, you can use MySQL as the user database, and you can use redis for the statistics. Or you can use Redis for both. .PP So, we have 6 choices for the user management, and 2 choices for the statistics management. These two are totally independent. So, you have overall 6*2=12 ways to handle persistent information, choose any for your convenience. .PP You do not have to handle the database information "manually" \- the \fIturnadmin\fP program can handle everything for you. For PostgreSQL and MySQL you will just have to create an empty database with schema.sql SQL script. With Redis, you do not have to do even that \- just run \fIturnadmin\fP and it will set the users for you (see the \fIturnadmin\fP manuals). If you are using SQLite, then the \fIturnserver\fP or \fIturnadmin\fP will initialize the empty database, for you, when started. The TURN server installation process creates an empty initialized SQLite database in the default location (/var/db/turndb or /usr/local/var/db/turndb or /var/lib/turn/turndb, depending on the system). .PP ================================= .SH ALPN The server supports ALPNs "stun.turn" and "stun.nat\-discovery", when compiled with OpenSSL 1.0.2 or newer. If the server receives a TLS/DTLS ClientHello message that contains one or both of those ALPNs, then the server chooses the first stun.* label and sends it back (in the ServerHello) in the ALPN extension field. If no stun.* label is found, then the server does not include the ALPN information into the ServerHello. .PP ================================= .SH LIBRARIES In the lib/ sub\-directory the build process will create TURN client messaging library. In the include/ sub\-directory, the necessary include files will be placed. The C++ wrapper for the messaging functionality is located in TurnMsgLib.h header. An example of C++ code can be found in stunclient.c file. .PP ================================= .SH DOCS After installation, run the command: .PP $ man \fIturnserver\fP .PP or in the project root directory: .PP $ man \fB\-M\fP man \fIturnserver\fP .PP to see the man page. .PP In the docs/html subdirectory of the original archive tree, you will find the client library reference. After the installation, it will be placed in PREFIX/share/doc/\fIturnserver\fP/html. .PP ================================= .SH LOGS When the \fBTURN Server\fP starts, it makes efforts to create a log file turn_.log in the following directories: .RS .IP \(bu 3 /var/log .IP \(bu 3 /log/ .IP \(bu 3 /var/tmp .IP \(bu 3 /tmp .IP \(bu 3 current directory .RE .PP If all efforts failed (due to the system permission settings) then all log messages are sent only to the standard output of the process. .PP This behavior can be controlled by \fB\-\-log\-file\fP, \fB\-\-syslog\fP and \fB\-\-no\-stdout\-log\fP \fIoptions\fP. .PP ================================= .SH HTTPS MANAGEMENT INTERFACE The \fIturnserver\fP process provides an HTTPS Web access as statistics and basic management interface. The \fIturnserver\fP listens to incoming HTTPS admin connections on the same ports as the main TURN/STUN listener. The Web admin pages are basic and self\-explanatory. .PP To make the HTTPS interface active, the database table admin_user must be populated with the admin user \fBaccount\fP(s). An admin user can be a superuser (if not assigned to a particular realm) or a restricted user (if assigned to a realm). The restricted admin users can perform only limited actions, within their corresponding realms. .PP ================================= .SH TELNET CLI The \fIturnserver\fP process provides a telnet CLI access as statistics and basic management interface. By default, the \fIturnserver\fP starts a telnet CLI listener on IP 127.0.0.1 and port 5766. That can be changed by the command\-cline \fIoptions\fP of the \fIturnserver\fP process (see \fB\-\-cli\-ip\fP and \fB\-\-cli\-port\fP \fIoptions\fP). The full list of telnet CLI commands is provided in "help" command output in the telnet CLI. .PP ================================= .SH CLUSTERS \fBTURN Server\fP can be a part of the cluster installation. But, to support the "even port" functionality (RTP/RTCP streams pairs) the client requests from a particular IP must be delivered to the same \fBTURN Server\fP instance, so it requires some networking setup massaging for the cluster. The reason is that the RTP and RTCP relaying endpoints must be allocated on the same relay IP. It would be possible to design a scheme with the application\-level requests forwarding (and we may do that later) but it would affect the performance. .PP ================================= .SH FILES /etc/turnserver.conf .PP /var/db/turndb .PP /usr/local/var/db/turndb .PP /var/lib/turn/turndb .PP /usr/local/etc/turnserver.conf .PP ================================= .SH DIRECTORIES /usr/local/share/\fIturnserver\fP .PP /usr/local/share/doc/\fIturnserver\fP .PP /usr/local/share/examples/\fIturnserver\fP .PP ================================= .SH STANDARDS obsolete STUN RFC 3489 .PP new STUN RFC 5389 .PP TURN RFC 5766 .PP TURN\-TCP extension RFC 6062 .PP TURN IPv6 extension RFC 6156 .PP STUN/TURN test vectors RFC 5769 .PP STUN NAT behavior discovery RFC 5780 .PP ================================= .SH SEE ALSO \fIturnadmin\fP, \fIturnutils\fP .RE .PP ====================================== .SS WEB RESOURCES project page: .PP https://github.com/coturn/coturn/ .PP Wiki page: .PP https://github.com/coturn/coturn/wiki .PP forum: .PP https://groups.google.com/forum/?fromgroups=#!forum/turn\-server\-project\-rfc5766\-turn\-server .PP ====================================== .SS AUTHORS Oleg Moskalenko .PP Gabor Kovesdan http://kovesdan.org/ .PP Daniel Pocock http://danielpocock.com/ .PP John Selbie (jselbie@gmail.com) .PP Lee Sylvester .PP Erik Johnston .PP Roman Lisagor .PP Vladimir Tsanev .PP Po\-sheng Lin .PP Peter Dunkley .PP Mutsutoshi Yoshimoto .PP Federico Pinna .PP Bradley T. Hughes .PP Mihaly Meszaros turnserver-4.5.1.1/man/man1/turnutils_stunclient.10000777000175000017500000000000013436577655022752 2turnutils.1ustar misimisiturnserver-4.5.1.1/man/man1/turnutils_peer.10000777000175000017500000000000013436577655021515 2turnutils.1ustar misimisiturnserver-4.5.1.1/man/man1/turnutils_oauth.10000777000175000017500000000000013436577655021702 2turnutils.1ustar misimisiturnserver-4.5.1.1/man/man1/turnadmin.10000664000175000017500000002165513436577655016315 0ustar misimisi.\" Text automatically generated by txt2man .TH TURN 1 "29 January 2019" "" "" .SH GENERAL INFORMATION \fIturnadmin\fP is a TURN administration tool. This tool can be used to manage the user accounts (add/remove users, generate TURN keys for the users). For security reasons, we do not recommend storing passwords openly. The better option is to use pre\-processed "keys" which are then used for authentication. These keys are generated by \fIturnadmin\fP. Turnadmin is a link to \fIturnserver\fP binary, but \fIturnadmin\fP performs different functions. .PP Options note: \fIturnadmin\fP has long and short option names, for most options. Some options have only long form, some options have only short form. Their syntax somewhat different, if an argument is required: .PP The short form must be used as this (for example): .PP .nf .fam C $ turnadmin \-u \.\.\. .fam T .fi The long form equivalent must use the "=" character: .PP .nf .fam C $ turnadmin \-\-user= \.\.\. .fam T .fi If this is a flag option (no argument required) then their usage are the same, for example: .PP .nf .fam C $ turnadmin \-k \.\.\. .fam T .fi is equivalent to: .PP .nf .fam C $ turnadmin \-\-key \.\.\. .fam T .fi You have always the use the \fB\-r\fP option with commands for long term credentials \- because data for multiple realms can be stored in the same database. .PP ===================================== .SS NAME \fB \fBturnadmin \fP\- a TURN relay administration tool. \fB .SS SYNOPSIS $ \fIturnadmin\fP [command] [options] .PP $ \fIturnadmin\fP [ \fB\-h\fP | \fB\-\-help\fP] .SS DESCRIPTION .TP .B Commands: .TP .B \fB\-P\fP, \fB\-\-generate\-encrypted\-password\fP Generate and print to the standard output an encrypted form of a password (for web admin user or CLI). The value then can be used as a safe key for the password storage on disk or in the database. Every invocation for the same password produces a different result. The for mat of the encrypted password is: $5$<\.\.\.salt\.\.\.>$<\.\.\.sha256(salt+password)\.\.\.>. Salt is 16 characters, the sha256 output is 64 characters. Character 5 is the algorithm id (sha256). Only sha256 is supported as the hash function. .TP .B \fB\-k\fP, \fB\-\-key\fP Generate key for a long\-term credentials mechanism user. .TP .B \fB\-a\fP, \fB\-\-add\fP Add or update a long\-term user. .TP .B \fB\-A\fP, \fB\-\-add\-admin\fP Add or update an admin user. .TP .B \fB\-d\fP, \fB\-\-delete\fP Delete a long\-term user. .TP .B \fB\-D\fP, \fB\-\-delete\-admin\fP Delete an admin user. .TP .B \fB\-l\fP, \fB\-\-list\fP List long\-term users in the database. .TP .B \fB\-L\fP, \fB\-\-list\-admin\fP List admin users in the database. .PP \fB\-s\fP, \fB\-\-set\-secret\fP= Add shared secret for TURN RESP API .TP .B \fB\-S\fP, \fB\-\-show\-secret\fP Show stored shared secrets for TURN REST API .PP \fB\-X\fP, \fB\-\-delete\-secret\fP= Delete a shared secret. .RS .TP .B \fB\-\-delete\-all_secrets\fP Delete all shared secrets for REST API. .RE .TP .B \fB\-O\fP, \fB\-\-add\-origin\fP Add origin\-to\-realm relation. .TP .B \fB\-R\fP, \fB\-\-del\-origin\fP Delete origin\-to\-realm relation. .TP .B \fB\-I\fP, \fB\-\-list\-origins\fP List origin\-to\-realm relations. .TP .B \fB\-g\fP, \fB\-\-set\-realm\-option\fP Set realm params: max\-bps, total\-quota, user\-quota. .TP .B \fB\-G\fP, \fB\-\-list\-realm\-options\fP List realm params. .TP .B \fB\-E\fP, \fB\-\-generate\-encrypted\-password\-aes\fP Generate and print to the standard output an encrypted form of password with AES\-128 .TP .B Options with required values: .TP .B \fB\-b\fP, \fB\-\-db\fP, \fB\-\-userdb\fP SQLite user database file name (default \- /var/db/turndb or /usr/local/var/db/turndb or /var/lib/turn/turndb). See the same option in the \fIturnserver\fP section. .TP .B \fB\-e\fP, \fB\-\-psql\-userdb\fP PostgreSQL user database connection string. See the \fB\-\-psql\-userdb\fP option in the \fIturnserver\fP section. .TP .B \fB\-M\fP, \fB\-\-mysql\-userdb\fP MySQL user database connection string. See the \fB\-\-mysql\-userdb\fP option in the \fIturnserver\fP section. .TP .B \fB\-J\fP, \fB\-\-mongo\-userdb\fP MongoDB user database connection string. See the \fB\-\-mysql\-mongo\fP option in the \fIturnserver\fP section. .TP .B \fB\-N\fP, \fB\-\-redis\-userdb\fP Redis user database connection string. See the \fB\-\-redis\-userdb\fP option in the \fIturnserver\fP section. .TP .B \fB\-u\fP, \fB\-\-user\fP User name. .TP .B \fB\-r\fP, \fB\-\-realm\fP Realm. .TP .B \fB\-p\fP, \fB\-\-password\fP Password. .TP .B \fB\-x\fP, \fB\-\-key\-path\fP Generates a 128 bit key into the given path. .TP .B \fB\-f\fP, \fB\-\-file\-key\-path\fP Contains a 128 bit key in the given path. .TP .B \fB\-v\fP, \fB\-\-verify\fP Verify a given base64 encrypted type password. .TP .B \fB\-o\fP, \fB\-\-origin\fP Origin .TP .B \fB\-\-max\-bps\fP Set value of realm's max\-bps parameter. .TP .B \fB\-\-total\-quota\fP Set value of realm's total\-quota parameter. .TP .B \fB\-\-user\-quota\fP Set value of realm's user\-quota parameter. .TP .B \fB\-h\fP, \fB\-\-help\fP Help. .TP .B Command examples: .PP Generate an encrypted form of a password: .PP $ \fIturnadmin\fP \fB\-P\fP \fB\-p\fP .PP Generate a key: .PP $ \fIturnadmin\fP \fB\-k\fP \fB\-u\fP \fB\-r\fP \fB\-p\fP .PP Add/update a user in the in the database: .PP $ \fIturnadmin\fP \fB\-a\fP [\fB\-b\fP | \fB\-e\fP | \fB\-M\fP | \fB\-N\fP ] \fB\-u\fP \fB\-r\fP \fB\-p\fP .PP Delete a user from the database: .PP $ \fIturnadmin\fP \fB\-d\fP [\fB\-b\fP | \fB\-e\fP | \fB\-M\fP | \fB\-N\fP ] \fB\-u\fP \fB\-r\fP .PP List all long\-term users in MySQL database: .PP $ \fIturnadmin\fP \fB\-l\fP \fB\-\-mysql\-userdb\fP="" \fB\-r\fP .PP List all admin users in Redis database: .PP $ \fIturnadmin\fP \fB\-L\fP \fB\-\-redis\-userdb\fP="" .PP Set secret in MySQL database: .PP $ \fIturnadmin\fP \fB\-s\fP \fB\-\-mysql\-userdb\fP="" \fB\-r\fP .PP Show secret stored in PostgreSQL database: .PP $ \fIturnadmin\fP \fB\-S\fP \fB\-\-psql\-userdb\fP="" \fB\-r\fP .PP Set origin\-to\-realm relation in MySQL database: .PP $ \fIturnadmin\fP \fB\-\-mysql\-userdb\fP="" \fB\-r\fP \fB\-o\fP .PP Delete origin\-to\-realm relation from Redis DB: .PP $ \fIturnadmin\fP \fB\-\-redis\-userdb\fP="" \fB\-o\fP .PP List all origin\-to\-realm relations in Redis DB: .PP $ \fIturnadmin\fP \fB\-\-redis\-userdb\fP="" \fB\-I\fP .PP List the origin\-to\-realm relations in PostgreSQL DB for a single realm: .PP $ \fIturnadmin\fP \fB\-\-psql\-userdb\fP="" \fB\-I\fP \fB\-r\fP .PP Create new key file for mysql password encryption: .PP $ \fIturnadmin\fP \fB\-E\fP \fB\-\-key\-path\fP .PP Create encrypted mysql password: .PP $ \fIturnadmin\fP \fB\-E\fP \fB\-\-file\-key\-path\fP \fB\-p\fP .PP Verify/decrypt encrypted password: .PP $ \fIturnadmin\fP \fB\-\-file\-key\-path\fP \fB\-v\fP .RE .PP .RS .TP .B Help: .PP $ \fIturnadmin\fP \fB\-h\fP .PP ======================================= .SS DOCS After installation, run the command: .PP $ man \fIturnadmin\fP .PP or in the project root directory: .PP $ man \fB\-M\fP man \fIturnadmin\fP .PP to see the man page. .PP ===================================== .SS FILES /etc/turnserver.conf .PP /var/db/turndb .PP /usr/local/var/db/turndb .PP /var/lib/turn/turndb .PP /usr/local/etc/turnserver.conf .PP ===================================== .SS DIRECTORIES /usr/local/share/\fIturnserver\fP .PP /usr/local/share/doc/\fIturnserver\fP .PP /usr/local/share/examples/\fIturnserver\fP .PP ====================================== .SS SEE ALSO \fIturnserver\fP, \fIturnutils\fP .RE .PP ====================================== .SS WEB RESOURCES project page: .PP https://github.com/coturn/coturn/ .PP Wiki page: .PP https://github.com/coturn/coturn/wiki .PP forum: .PP https://groups.google.com/forum/?fromgroups=#!forum/turn\-server\-project\-rfc5766\-turn\-server/ .RE .PP ====================================== .SS AUTHORS Oleg Moskalenko .PP Gabor Kovesdan http://kovesdan.org/ .PP Daniel Pocock http://danielpocock.com/ .PP John Selbie (jselbie@gmail.com) .PP Lee Sylvester .PP Erik Johnston .PP Roman Lisagor .PP Vladimir Tsanev .PP Po\-sheng Lin .PP Peter Dunkley .PP Mutsutoshi Yoshimoto .PP Federico Pinna .PP Bradley T. Hughes .PP Mihaly Meszaros turnserver-4.5.1.1/man/man1/turnutils_natdiscovery.10000777000175000017500000000000013436577655023274 2turnutils.1ustar misimisiturnserver-4.5.1.1/man/man1/coturn.10000777000175000017500000000000013436577655020111 2turnserver.1ustar misimisi