turnserver-4.5.2/0000755000175000017500000000000013776656461012452 5ustar misimisiturnserver-4.5.2/LICENSE.OpenSSL0000644000175000017500000001420713776656461014745 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.2/src/0000755000175000017500000000000013776656461013241 5ustar misimisiturnserver-4.5.2/src/server/0000755000175000017500000000000013776656461014547 5ustar misimisiturnserver-4.5.2/src/server/ns_turn_khash.h0000644000175000017500000003267113776656461017577 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, char) 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 uint32_t khint_t; typedef khint_t khiter_t; typedef struct _str_chunk_t { const char *str; size_t len; } str_chunk_t; #define __ac_HASH_PRIME_SIZE 32 static const uint32_t __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; \ uint32_t *flags; uint32_t flags_size; \ khkey_t *keys; uint32_t keys_size; \ khval_t *vals; uint32_t vals_size; \ } kh_##name##_t; \ static inline kh_##name##_t *kh_init_##name(void) { \ return (kh_##name##_t*)calloc(1, sizeof(kh_##name##_t)); \ } \ static inline void kh_destroy_##name(kh_##name##_t *h) \ { \ if (h) { \ free(h->keys); free(h->flags); \ free(h->vals); \ free(h); \ } \ } \ static inline void kh_clear_##name(kh_##name##_t *h) \ { \ if (h && h->flags) { \ memset(h->flags, 0xaa, ((h->n_buckets>>4) + 1) * sizeof(uint32_t)); \ 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) \ { \ uint32_t *new_flags = 0; \ uint32_t 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(uint32_t); \ new_flags = (uint32_t*)malloc(new_flags_size); \ memset(new_flags, 0xaa, new_flags_size); \ if (h->n_buckets < new_n_buckets) { \ h->keys = (khkey_t*)realloc(h->keys, new_n_buckets * sizeof(khkey_t)); \ h->keys_size = new_n_buckets * sizeof(khkey_t); \ if (kh_is_map) { \ h->vals = (khval_t*)realloc(h->vals, 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*)realloc(h->keys, new_n_buckets * sizeof(khkey_t)); \ h->keys_size = new_n_buckets * sizeof(khkey_t); \ if (kh_is_map) { \ h->vals = (khval_t*)realloc(h->vals, new_n_buckets * sizeof(khval_t)); \ h->vals_size = new_n_buckets * sizeof(khval_t); \ } \ } \ free(h->flags); \ 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) (uint32_t)((key<<3) + nswap32(key>>7)) #define kh_int_hash_equal(a, b) (a == b) #define kh_int64_hash_func(key) (uint32_t)((key)>>33^(key)^(key)<<11) #define kh_int64_hash_equal(a, b) (a == b) static inline khint_t __ac_X31_hash_string(const char *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 char *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, uint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) #define KHASH_MAP_INIT_INT(name, khval_t) \ KHASH_INIT(name, uint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) #define KHASH_SET_INIT_INT64(name) \ KHASH_INIT(name, uint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) #define KHASH_MAP_INIT_INT64(name, khval_t) \ KHASH_INIT(name, uint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) typedef const char *kh_cstr_t; typedef const str_chunk_t *kh_ncstr_t; #define KHASH_SET_INIT_STR(name) \ KHASH_INIT(name, kh_cstr_t, char, 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, char, 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, char, 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, char, 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.2/src/server/ns_turn_ioalib.h0000644000175000017500000002444513776656461017740 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 { uint32_t 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, TCP_SOCKET_PROXY=253, 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); uint8_t *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, uint16_t offset, uint8_t coffset, size_t len); uint16_t ioa_network_buffer_get_offset(ioa_network_buffer_handle nbh); uint8_t 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 char *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, uint8_t transport, int even_port, ioa_socket_handle *rtp_s, ioa_socket_handle *rtcp_s, uint64_t *out_reservation_token, int *err_code, const uint8_t **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, uint64_t 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); ///////////// ACME ///////////////////// int try_acme_redirect(char *req, size_t len, const char *url, ioa_socket_handle s); ///////////// ACME ///////////////////// int try_acme_redirect(char *req, size_t len, const char *url, ioa_socket_handle s); /////////////////////////////////////// #ifdef __cplusplus } #endif #endif /* __IOA_LIB__ */ turnserver-4.5.2/src/server/ns_turn_server.h0000644000175000017500000002055213776656461020002 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 uint8_t 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, uint64_t ctxkey, ioa_net_data *in_buffer, uint8_t* realm); typedef uint8_t *(*get_user_key_cb)(turnserver_id id, turn_credential_type ct, int in_oauth, int *out_oauth, uint8_t *uname, uint8_t *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, uint64_t ctxkey, int *postpone_reply); typedef int (*check_new_allocation_quota_cb)(uint8_t *username, int oauth, uint8_t *realm); typedef void (*release_allocation_quota_cb)(uint8_t *username, int oauth, uint8_t *realm); typedef int (*send_socket_to_relay_cb)(turnserver_id id, uint64_t 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 no_software_attribute; 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; /* ACME redirect URL */ const char* acme_redirect; /* Keep Address Family */ int keep_address_family; /* Log Binding Requrest */ vintp log_binding; }; 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 no_software_attribute, 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, const char* acme_redirect, int keep_address_family, vintp log_binding); 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.2/src/server/ns_turn_allocation.c0000644000175000017500000004413213776656461020614 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) { 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; } } free(a->tcs.elems); 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),(uint8_t*)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)); bzero(tinfo,sizeof(turn_permission_info)); } } static void init_turn_permission_hashtable(turn_permission_hashtable *map) { if (map) 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)); } free(slot); } } free(parray->extra_slots); 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; uint32_t 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); 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),(uint8_t*)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, uint16_t 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, uint16_t 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; } uint16_t 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); uint32_t 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 **) realloc(parray->extra_slots, old_sz_mem + sizeof(turn_permission_slot*)); slots = parray->extra_slots; parray->extra_sz = old_sz + 1; slots[old_sz] = (turn_permission_slot *)malloc(sizeof(turn_permission_slot)); slot = slots[old_sz]; } } 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, uint16_t 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**)realloc(a->extra_chns,old_sz_mem + sizeof(ch_info*)); a->extra_chns[old_sz] = (ch_info*)malloc(sizeof(ch_info)); 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); } free(chi); a->extra_chns[i] = NULL; } } free(a->extra_chns); a->extra_chns = NULL; } a->extra_sz = 0; } } } ////////////////// TCP connections /////////////////////////////// static void set_new_tc_id(uint8_t server_id, tcp_connection *tc) { allocation *a = (allocation*)(tc->owner); ur_map *map = a->tcp_connections; uint32_t newid; uint32_t sid = server_id; sid = sid<<24; do { newid = 0; while (!newid) { newid = (uint32_t)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(uint8_t 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*)malloc(sizeof(tcp_connection)); bzero(tc,sizeof(tcp_connection)); addr_cpy(&(tc->peer_addr),peer_addr); if(tid) 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**)realloc(a->tcs.elems,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); free(tc); } } 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; } } free(ub->bufs); 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*)realloc(ub->bufs, 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.2/src/server/ns_turn_allocation.h0000644000175000017500000001643513776656461020626 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 uint32_t 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 { uint16_t chnum; int allocated; uint16_t 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, uint16_t 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 //////////////////// uint16_t 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, uint16_t chnum, ioa_addr* peer_addr); ch_info* allocation_get_ch_info(allocation* a, uint16_t 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(uint8_t 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.2/src/server/ns_turn_maps.h0000644000175000017500000001500113776656461017425 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 uint64_t 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]; uint64_t 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 char* 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.2/src/server/ns_turn_session.h0000644000175000017500000001245213776656461020157 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_SESSION__ #define __TURN_SESSION__ #include "ns_turn_utils.h" #include "ns_turn_maps.h" #include "ns_turn_ioalib.h" #include "ns_turn_allocation.h" #ifdef __cplusplus extern "C" { #endif ////////// REALM //////////// typedef struct _perf_options_t { volatile band_limit_t max_bps; vint total_quota; vint user_quota; } perf_options_t; struct _realm_options_t { char name[STUN_MAX_REALM_SIZE + 1]; perf_options_t perf_options; }; //////////////// session info ////////////////////// typedef uint64_t turnsession_id; #define NONCE_MAX_SIZE (NONCE_LENGTH_32BITS*4+1) typedef uint64_t mobile_id_t; struct _ts_ur_super_session { void* server; turnsession_id id; turn_time_t start_time; ioa_socket_handle client_socket; allocation alloc; ioa_timer_handle to_be_allocated_timeout_ev; int enforce_fingerprints; int is_tcp_relay; int to_be_closed; /* Auth */ uint8_t nonce[NONCE_MAX_SIZE]; turn_time_t nonce_expiration_time; uint8_t username[STUN_MAX_USERNAME_SIZE+1]; hmackey_t hmackey; int hmackey_set; password_t pwd; int quota_used; int oauth; turn_time_t max_session_time_auth; /* Realm */ realm_options_t realm_options; int origin_set; char origin[STUN_MAX_ORIGIN_SIZE + 1]; /* Stats */ uint32_t received_packets; uint32_t sent_packets; uint32_t received_bytes; uint32_t sent_bytes; uint64_t t_received_packets; uint64_t t_sent_packets; uint64_t t_received_bytes; uint64_t t_sent_bytes; uint64_t received_rate; size_t sent_rate; size_t total_rate; uint32_t peer_received_packets; uint32_t peer_sent_packets; uint32_t peer_received_bytes; uint32_t peer_sent_bytes; uint32_t t_peer_received_packets; uint32_t t_peer_sent_packets; uint32_t t_peer_received_bytes; uint32_t t_peer_sent_bytes; uint64_t peer_received_rate; size_t peer_sent_rate; size_t peer_total_rate; /* Mobile */ int is_mobile; mobile_id_t mobile_id; mobile_id_t old_mobile_id; char s_mobile_id[33]; /* Bandwidth */ band_limit_t bps; }; ////// Session info for statistics ////// #define TURN_ADDR_STR_SIZE (65) #define TURN_MAIN_PEERS_ARRAY_SIZE (5) typedef struct _addr_data { ioa_addr addr; char saddr[TURN_ADDR_STR_SIZE]; } addr_data; struct turn_session_info { turnsession_id id; int valid; turn_time_t start_time; turn_time_t expiration_time; SOCKET_TYPE client_protocol; SOCKET_TYPE peer_protocol; char tls_method[17]; char tls_cipher[65]; addr_data local_addr_data; addr_data remote_addr_data; addr_data relay_addr_data_ipv4; addr_data relay_addr_data_ipv6; uint8_t username[STUN_MAX_USERNAME_SIZE+1]; int enforce_fingerprints; /* Stats */ uint64_t received_packets; uint64_t sent_packets; uint64_t received_bytes; uint64_t sent_bytes; uint32_t received_rate; uint32_t sent_rate; uint32_t total_rate; uint64_t peer_received_packets; uint64_t peer_sent_packets; uint64_t peer_received_bytes; uint64_t peer_sent_bytes; uint32_t peer_received_rate; uint32_t peer_sent_rate; uint32_t peer_total_rate; /* Mobile */ int is_mobile; /* Peers */ addr_data main_peers_data[TURN_MAIN_PEERS_ARRAY_SIZE]; size_t main_peers_size; addr_data *extra_peers_data; size_t extra_peers_size; /* Realm */ char realm[STUN_MAX_REALM_SIZE + 1]; char origin[STUN_MAX_ORIGIN_SIZE + 1]; /* Bandwidth */ band_limit_t bps; }; void turn_session_info_init(struct turn_session_info* tsi); void turn_session_info_clean(struct turn_session_info* tsi); void turn_session_info_add_peer(struct turn_session_info* tsi, ioa_addr *peer); int turn_session_info_copy_from(struct turn_session_info* tsi, ts_ur_super_session *ss); ////////////// ss ///////////////////// allocation* get_allocation_ss(ts_ur_super_session *ss); /////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_SESSION__ turnserver-4.5.2/src/server/ns_turn_maps_rtcp.c0000644000175000017500000001471713776656461020465 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 { uint32_t 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); free(at); } } static void rtcp_alloc_free_savefd(ur_map_value_type value) { rtcp_alloc_type *at = (rtcp_alloc_type *) value; if (at) { free(at); } } 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*)malloc(sizeof(rtcp_map)); bzero(map,sizeof(rtcp_map)); if(rtcp_map_init(map,e)<0) { free(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*)malloc(sizeof(rtcp_alloc_type)); if(!value) return -1; 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) free(value); 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)); free(*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.2/src/server/ns_turn_maps.c0000644000175000017500000006031313776656461017426 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_ioalib.h" #include "ns_turn_khash.h" KHASH_MAP_INIT_INT64(3, ur_map_value_type) #define MAGIC_HASH ((uint64_t)(0x90ABCDEFL)) struct _ur_map { khash_t(3) *h; uint64_t magic; TURN_MUTEX_DECLARE(mutex) }; static int ur_map_init(ur_map* map) { if(map) { map->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*)malloc(sizeof(ur_map)); if(ur_map_init(map)<0) { free(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)); free(*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) { 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*)malloc(sizeof(ur_map_key_type)); keyp = a->extra_keys[i]; } if(!(*valuep)) { a->extra_values[i] = (ur_map_value_type*)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**)realloc(a->extra_keys,old_sz_mem + sizeof(ur_map_key_type*)); a->extra_keys[old_sz] = (ur_map_key_type*)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**)realloc(a->extra_values,old_sz_mem + sizeof(ur_map_value_type*)); a->extra_values[old_sz] = (ur_map_value_type*)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; free(keyp); } } free(a->extra_keys); a->extra_keys = NULL; } if(a->extra_values) { for(i=0;iextra_values[i]; if(valuep) { *valuep = 0; free(valuep); } } free(a->extra_values); 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) { free(slh->extra_list); } 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*)realloc(slh->extra_list, 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) { 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)) { uint32_t i=0; for(i=0;ilists[i])); } 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)) { uint32_t 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)) { uint32_t 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)) { uint32_t 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; uint32_t 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) free(elem->key); if(del_value_func && elem->value) del_value_func(elem->value); free(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*)malloc(sizeof(string_elem)); elem->list.next=sl; elem->key_size = strlen(key)+1; elem->key=(char*)malloc(elem->key_size); 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) { free(elem->key); if(del_value_func) del_value_func(elem->value); free(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]; uint64_t magic; ur_string_map_func del_value_func; TURN_MUTEX_DECLARE(mutex) }; static uint32_t string_hash(const ur_string_map_key_type key) { uint8_t *str=(uint8_t*)key; uint32_t 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) { 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*)malloc(sizeof(ur_string_map)); if(ur_string_map_init(map)<0) { free(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)); free(*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.2/src/server/ns_turn_maps_rtcp.h0000644000175000017500000000460413776656461020464 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.2/src/server/ns_turn_server.c0000644000175000017500000043453613776656461020010 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->no_software_attribute) { 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 uint8_t *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 uint8_t **reason, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, uint16_t method, int *message_integrity, int *postpone_reply, int can_resume); static int create_relay_connection(turn_turnserver* server, ts_ur_super_session *ss, uint32_t lifetime, int address_family, uint8_t transport, int even_port, uint64_t in_reservation_token, uint64_t *out_reservation_token, int *err_code, const uint8_t **reason, accept_cb acb); static int refresh_relay_connection(turn_turnserver* server, ts_ur_super_session *ss, uint32_t lifetime, int even_port, uint64_t in_reservation_token, uint64_t *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, uint8_t *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, (uint8_t*)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, (uint8_t*)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; if (ioa_addr_is_zero(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,(uint8_t*)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,(uint8_t*)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) { bzero(tsi,sizeof(struct turn_session_info)); } } void turn_session_info_clean(struct turn_session_info* tsi) { if(tsi) { if(tsi->extra_peers_data) { free(tsi->extra_peers_data); } 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), (uint8_t*)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*)realloc(tsi->extra_peers_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), (uint8_t*)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),(uint8_t*)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),(uint8_t*)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),(uint8_t*)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),(uint8_t*)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; if (ss->t_peer_received_packets > ss->peer_received_packets) tsi->peer_received_packets = ss->t_peer_received_packets; else tsi->peer_received_packets = ss->peer_received_packets; if (ss->t_peer_sent_packets > ss->peer_sent_packets) tsi->peer_sent_packets = ss->t_peer_sent_packets; else tsi->peer_sent_packets = ss->peer_sent_packets; if (ss->t_peer_received_bytes > ss->peer_received_bytes) tsi->peer_received_bytes = ss->t_peer_received_bytes; else tsi->peer_received_bytes = ss->peer_received_bytes; if (ss->t_peer_sent_bytes > ss->peer_sent_bytes) tsi->peer_sent_bytes = ss->t_peer_sent_bytes; else tsi->peer_sent_bytes = ss->peer_sent_bytes; { tsi->received_rate = ss->received_rate; tsi->sent_rate = ss->sent_rate; tsi->total_rate = tsi->received_rate + tsi->sent_rate; tsi->peer_received_rate = ss->peer_received_rate; tsi->peer_sent_rate = ss->peer_sent_rate; tsi->peer_total_rate = tsi->peer_received_rate + tsi->peer_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)) { free(s); return -1; } bcopy(s, dst, output_length); free(s); 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); } free(out); } } 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; uint64_t 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*)malloc(sizeof(ts_ur_super_session)); 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); free(p); } } /////////// 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),(uint8_t*)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 uint8_t get_transport_value(const uint8_t *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 uint8_t **reason, uint16_t *unknown_attrs, uint16_t *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 uint8_t *)"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) { uint32_t 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 { uint8_t transport = 0; turn_time_t lifetime = 0; int even_port = -1; int dont_fragment = 0; uint64_t in_reservation_token = 0; int af4 = 0; int af6 = 0; uint8_t 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 uint8_t* value = stun_attr_get_value(sar); if (value) { ulen = stun_attr_get_len(sar); if(ulen>=sizeof(username)) { *err_code = 400; *reason = (const uint8_t *)"User name is too long"; break; } 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 uint8_t *)"Mobility Forbidden"; } else if (stun_attr_get_len(sar) != 0) { *err_code = 400; *reason = (const uint8_t *)"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 uint8_t *)"Wrong Transport Field"; } else if(transport) { *err_code = 400; *reason = (const uint8_t *)"Duplicate Transport Fields"; } else { const uint8_t* 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 uint8_t *)"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 uint8_t *)"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 uint8_t *)"Wrong Transport Data"; } else { ss->is_tcp_relay = (transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE); } } } else { *err_code = 400; *reason = (const uint8_t *)"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 uint8_t *)"Wrong Lifetime Field"; } else { const uint8_t* value = stun_attr_get_value(sar); if (!value) { *err_code = 400; *reason = (const uint8_t *)"Wrong Lifetime Data"; } else { lifetime = nswap32(*((const uint32_t*)value)); } } } break; case STUN_ATTRIBUTE_EVEN_PORT: { if (in_reservation_token) { *err_code = 400; *reason = (const uint8_t *)"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 uint8_t *)"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 uint8_t *)"Wrong Format of Reservation Token"; } else if(af4 || af6) { *err_code = 400; *reason = (const uint8_t *)"Address family attribute can not be used with reservation token request"; } else { if (even_port >= 0) { *err_code = 400; *reason = (const uint8_t *)"Reservation Token cannot be used in this request with even port"; } else if (in_reservation_token) { *err_code = 400; *reason = (const uint8_t *)"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 uint8_t *)"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 uint8_t *)"Address family attribute can not be used with reservation token request"; } else if(af4 || af6) { *err_code = 400; *reason = (const uint8_t *)"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 uint8_t *)"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 uint8_t *)"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 uint8_t *)"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); uint64_t 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 uint8_t *)"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 uint8_t *)"Dual allocation cannot be supported in the current server configuration"; } if(even_port > 0) { *err_code = 440; *reason = (const uint8_t *)"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 uint8_t *reason4 = NULL; const uint8_t *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 uint8_t *)"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); bcopy(orig_ss->nonce,ss->nonce,sizeof(ss->nonce)); ss->nonce_expiration_time = orig_ss->nonce_expiration_time; bcopy(&(orig_ss->realm_options),&(ss->realm_options),sizeof(ss->realm_options)); bcopy(orig_ss->username,ss->username,sizeof(ss->username)); ss->hmackey_set = orig_ss->hmackey_set; bcopy(orig_ss->hmackey,ss->hmackey,sizeof(ss->hmackey)); ss->oauth = orig_ss->oauth; bcopy(orig_ss->origin,ss->origin,sizeof(ss->origin)); ss->origin_set = orig_ss->origin_set; 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 uint8_t **reason, uint16_t *unknown_attrs, uint16_t *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 uint8_t *)"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 uint8_t *)"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 uint8_t *)"Mobility ticket cannot be used for a stable, already established allocation"; } } } else { *err_code = 400; *reason = (const uint8_t *)"Mobility ticket has wrong length"; } } } break; case STUN_ATTRIBUTE_LIFETIME: { if (stun_attr_get_len(sar) != 4) { *err_code = 400; *reason = (const uint8_t *)"Wrong Lifetime field format"; } else { const uint8_t* value = stun_attr_get_value(sar); if (!value) { *err_code = 400; *reason = (const uint8_t *)"Wrong lifetime field data"; } else { lifetime = nswap32(*((const uint32_t*)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 uint8_t *)"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 uint8_t *)"Wrong mobile ticket"; } else { *no_response = 1; } } else { *err_code = 500; *reason = (const uint8_t *)"Cannot create new socket"; return -1; } } else { *err_code = 500; *reason = (const uint8_t *)"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 uint8_t *)"Allocation not found"; } else if(orig_ss == ss) { *err_code = 437; *reason = (const uint8_t *)"Invalid allocation"; } else if(!(orig_ss->is_mobile)) { *err_code = 500; *reason = (const uint8_t *)"Software error: invalid mobile allocation"; } else if(orig_ss->client_socket == ss->client_socket) { *err_code = 500; *reason = (const uint8_t *)"Software error: invalid mobile client socket (orig)"; } else if(!(ss->client_socket)) { *err_code = 500; *reason = (const uint8_t *)"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 uint8_t *)"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 uint8_t *)"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); uint32_t lt = nswap32(lifetime); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_LIFETIME, (const uint8_t*) <, 4); ioa_network_buffer_set_size(nbh,len); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_MOBILITY_TICKET, (uint8_t*)ss->s_mobile_id,strlen(ss->s_mobile_id)); ioa_network_buffer_set_size(nbh,len); { const uint8_t *field = (const uint8_t *) 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 uint8_t *)"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 uint8_t *)"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 uint8_t *)"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, (uint8_t*)ss->s_mobile_id,strlen(ss->s_mobile_id)); ioa_network_buffer_set_size(nbh,len); } uint32_t lt = nswap32(lifetime); stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_LIFETIME, (const uint8_t*) <, 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; uint32_t bytes = (uint32_t)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; uint32_t bytes = (uint32_t)ioa_network_buffer_get_size(nbh); if (ss) { ++(ss->peer_received_packets); ss->peer_received_bytes += bytes; } 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; } if (ss) { 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; uint32_t bytes = (uint32_t)ioa_network_buffer_get_size(nbh); if(ss) { ++(ss->received_packets); ss->received_bytes += bytes; } int skip = 0; int ret = send_data_from_ioa_socket_nbh(tc->peer_s, NULL, nbh, TTL_IGNORE, TOS_IGNORE, &skip); if (ret < 0) { set_ioa_socket_tobeclosed(s); } if (!skip && ss) { ++(ss->peer_sent_packets); ss->peer_sent_bytes += bytes; } if(ss) 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 uint8_t*)&(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,(uint8_t*)ls); addr_to_string(&(tc->peer_addr),(uint8_t*)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); uint8_t *data = ioa_network_buffer_data(nbh_test); const char* data_test="111.111.111.111.111"; len_test = strlen(data_test); 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 uint8_t **reason) { FUNCSTART; if(!ss) { *err_code = 500; *reason = (const uint8_t *)"Server error: empty session"; FUNCEND; return -1; } if(!peer_addr) { *err_code = 500; *reason = (const uint8_t *)"Server error: empty peer addr"; FUNCEND; return -1; } if(!get_relay_socket(a,peer_addr->ss.sa_family)) { *err_code = 500; *reason = (const uint8_t *)"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 uint8_t *)"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 uint8_t *)"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)) { uint8_t 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; 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 uint8_t*)&(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 uint8_t *field = (const uint8_t *) 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 uint8_t **reason, uint16_t *unknown_attrs, uint16_t *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 uint8_t *)"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 uint8_t *)"Bad Peer Address"; } else { if(!get_relay_socket(a,peer_addr.ss.sa_family)) { *err_code = 443; *reason = (const uint8_t *)"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 uint8_t *)"Where is Peer Address ?"; } else { if(!good_peer_addr(server,ss->realm_options.name,&peer_addr)) { *err_code = 403; *reason = (const uint8_t *) "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 uint8_t **reason, uint16_t *unknown_attrs, uint16_t *ua_num, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, int message_integrity, int can_resume) { allocation* a = get_allocation_ss(ss); uint16_t method = STUN_METHOD_CONNECTION_BIND; if(ss->to_be_closed) { *err_code = 400; } else if (is_allocation_valid(a)) { *err_code = 400; *reason = (const uint8_t *)"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 uint8_t *)"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 uint8_t *)"Wrong Connection ID field format"; } else { const uint8_t* value = stun_attr_get_value(sar); if (!value) { *err_code = 400; *reason = (const uint8_t *)"Wrong Connection ID field data"; } else { id = *((const uint32_t*)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 uint8_t *)"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 uint8_t *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 uint8_t *field = (const uint8_t *) 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 uint8_t **reason, uint16_t *unknown_attrs, uint16_t *ua_num, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh) { FUNCSTART; uint16_t 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 uint8_t *)"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 uint8_t *)"Channel number cannot be duplicated in this request"; break; } chnum = stun_attr_get_channel_number(sar); if (!chnum) { *err_code = 400; *reason = (const uint8_t *)"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 uint8_t *)"Peer Address Family Mismatch (3)"; } if(addr_get_port(&peer_addr) < 1) { *err_code = 400; *reason = (const uint8_t *)"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 uint8_t *)"Bad channel bind request"; } else if(!STUN_VALID_CHANNEL(chnum)) { *err_code = 400; *reason = (const uint8_t *)"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 uint8_t *)"You cannot use the same channel number with different peer"; } else { tinfo = (turn_permission_info*) (chn->owner); if (!tinfo) { *err_code = 500; *reason = (const uint8_t *)"Wrong permission info"; } else { if (!addr_eq_no_port(&peer_addr, &(tinfo->addr))) { *err_code = 500; *reason = (const uint8_t *)"Wrong permission info and peer addr combination"; } else if (chn->port != addr_get_port(&peer_addr)) { *err_code = 500; *reason = (const uint8_t *)"Wrong port number"; } } } } else { chn = allocation_get_ch_info_by_peer_addr(a, &peer_addr); if(chn) { *err_code = 400; *reason = (const uint8_t *)"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 uint8_t *) "Forbidden IP"; } else { chn = allocation_get_new_ch_info(a, chnum, &peer_addr); if (!chn) { *err_code = 500; *reason = (const uint8_t *) "Cannot find channel data"; } else { tinfo = (turn_permission_info*) (chn->owner); if (!tinfo) { *err_code = 500; *reason = (const uint8_t *) "Wrong turn permission info"; } } } } } if (!(*err_code) && chn && tinfo) { if (update_channel_lifetime(ss,chn) < 0) { *err_code = 500; *reason = (const uint8_t *)"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 uint8_t **reason, uint16_t *unknown_attrs, uint16_t *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, uint32_t cookie, int old_stun) { FUNCSTART; int change_ip = 0; int change_port = 0; int padding = 0; int response_port_present = 0; uint16_t 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 uint8_t *)"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 uint8_t *)"Wrong request: applicable only to UDP protocol"; } } break; case STUN_ATTRIBUTE_PADDING: if(response_port_present) { *err_code = 400; *reason = (const uint8_t *)"Wrong request format: you cannot use PADDING and RESPONSE_PORT together"; } else if((st != UDP_SOCKET) && (st != DTLS_SOCKET)) { *err_code = 400; *reason = (const uint8_t *)"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 uint8_t *)"Wrong request format: you cannot use PADDING and RESPONSE_PORT together"; } else if(st != UDP_SOCKET) { *err_code = 400; *reason = (const uint8_t *)"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 = (uint16_t)rp; } else { *err_code = 400; *reason = (const uint8_t *)"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, (uint16_t)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 uint8_t **reason, uint16_t *unknown_attrs, uint16_t *ua_num, ioa_net_data *in_buffer) { FUNCSTART; ioa_addr peer_addr; const uint8_t* 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 uint8_t *)"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 uint8_t *)"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 uint8_t *)"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) { uint16_t offset = (uint16_t)(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); int skip = 0; 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, &skip); if (!skip) { ++(ss->peer_sent_packets); ss->peer_sent_bytes += len; turn_report_session_usage(ss, 0); } in_buffer->nbh = NULL; } } else { *err_code = 400; *reason = (const uint8_t *)"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 uint8_t **reason, uint16_t *unknown_attrs, uint16_t *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 uint8_t *)"Peer Address Family Mismatch (4)"; } else if(!good_peer_addr(server, ss->realm_options.name, &peer_addr)) { *err_code = 403; *reason = (const uint8_t *) "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 uint8_t *)"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 uint8_t *)"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 uint8_t **reason, ioa_network_buffer_handle nbh, uint16_t 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, (uint8_t*)realm, (int)(strlen((char*)(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 uint8_t*)(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, uint64_t ctxkey, ioa_net_data *in_buffer, uint8_t *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) { 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; 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 uint8_t **reason, ioa_net_data *in_buffer, ioa_network_buffer_handle nbh, uint16_t method, int *message_integrity, int *postpone_reply, int can_resume) { uint8_t usname[STUN_MAX_USERNAME_SIZE+1]; uint8_t nonce[STUN_MAX_NONCE_SIZE+1]; uint8_t 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++) { uint8_t *s = ss->nonce + 8*i; uint64_t rand=(uint64_t)turn_random(); snprintf((char*)s, NONCE_MAX_SIZE-8*i, "%08lx",(unsigned long)rand); } } else { for(i=0;inonce + 4*i; uint32_t rand=(uint32_t)turn_random(); snprintf((char*)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); 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 uint8_t*)"Allocation mismatch: wrong credentials: the realm value is incorrect"; } else { *err_code = 441; *reason = (const uint8_t*)"Wrong credentials: the realm value is incorrect"; } return -1; } else { 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); 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 uint8_t*)"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); bcopy(stun_attr_get_value(sar),nonce,alen); nonce[alen]=0; /* Stale Nonce check: */ if(new_nonce) { *err_code = 438; *reason = (const uint8_t*)"Wrong nonce"; return create_challenge_response(ss,tid,resp_constructed,err_code,reason,nbh,method); } if(strcmp((char*)ss->nonce,(char*)nonce)) { *err_code = 438; *reason = (const uint8_t*)"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, uint16_t method, stun_tid *tid, int *resp_constructed, int *err_code, const uint8_t **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 uint8_t *reason = NULL; int no_response = 0; int message_integrity = 0; if(!(ss->client_socket)) return -1; uint16_t unknown_attrs[MAX_NUMBER_OF_UNKNOWN_ATTRS]; uint16_t ua_num = 0; uint16_t 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 uint8_t *)"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*)malloc(sarlen+1); bcopy(stun_attr_get_value(sar),o,sarlen); o[sarlen]=0; char *corigin = (char*)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; } free(corigin); free(o); } } 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 uint8_t *)"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 uint8_t *)"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*)malloc(sarlen+1); bcopy(stun_attr_get_value(sar),o,sarlen); o[sarlen]=0; char *corigin = (char*)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); free(corigin); free(o); 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 && server->log_binding) { log_method(ss, "BINDING", err_code, reason); } if(*resp_constructed && !err_code && (origin_changed || dest_changed)) { if (server->verbose && server->log_binding) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "RFC 5780 request successfully processed\n"); } { const uint8_t *field = (const uint8_t *) 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 uint8_t*) 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 uint8_t *field = (const uint8_t *) 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, uint32_t cookie) { stun_tid tid; int err_code = 0; const uint8_t *reason = NULL; int no_response = 0; uint16_t unknown_attrs[MAX_NUMBER_OF_UNKNOWN_ATTRS]; uint16_t ua_num = 0; uint16_t 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 && *(server->log_binding)) { 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; uint8_t software[120]; bzero(software,sizeof(software)); if(newsz>sizeof(software)) newsz = sizeof(software); 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 uint8_t*) 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; uint8_t software[120]; bzero(software,sizeof(software)); if(newsz>sizeof(software)) newsz = sizeof(software); 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, uint16_t 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); int skip = 0; 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, &skip); if (!skip && rc > -1) { ++(ss->peer_sent_packets); ss->peer_sent_bytes += (uint32_t)ioa_network_buffer_get_size(in_buffer->nbh); turn_report_session_usage(ss, 0); } 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),(uint8_t*)sraddr); addr_to_string(get_local_addr_from_ioa_socket(ss->client_socket),(uint8_t*)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),(uint8_t*)sraddr); addr_to_string(get_local_addr_from_ioa_socket(ss->client_socket),(uint8_t*)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 && ret>-1) { ++(ss->sent_packets); ss->sent_bytes += (uint32_t)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, uint32_t lifetime, int address_family, uint8_t transport, int even_port, uint64_t in_reservation_token, uint64_t *out_reservation_token, int *err_code, const uint8_t **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 uint8_t *)"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); 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); 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 uint8_t *)"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 uint8_t *)"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 uint8_t *)"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>(uint32_t)*(server->max_allocate_lifetime)) lifetime = (uint32_t)*(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, uint32_t lifetime, int even_port, uint64_t in_reservation_token, uint64_t *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 += (uint32_t)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)); } uint16_t chnum = 0; uint32_t 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; uint16_t 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"; if ((st == TCP_SOCKET) && ( try_acme_redirect( (char*)ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), server->acme_redirect, ss->client_socket ) == 0 ) ) { ss->to_be_closed = 1; return 0; } else 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), ioa_network_buffer_get_size(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, ioa_network_buffer_get_size(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) { ++(ss->peer_received_packets); ss->peer_received_bytes += ilen; turn_report_session_usage(ss, 0); allocation* a = get_allocation_ss(ss); if (is_allocation_valid(a)) { uint16_t 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 uint8_t *field = (const uint8_t *) 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)) { uint16_t* t = (uint16_t*) 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 no_software_attribute, 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, const char* acme_redirect, int keep_address_family, vintp log_binding) { if (!server) return; 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(); server->acme_redirect = acme_redirect; 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->no_software_attribute = no_software_attribute; 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"); server->log_binding = log_binding; } 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.2/src/client/0000755000175000017500000000000013776656461014517 5ustar misimisiturnserver-4.5.2/src/client/ns_turn_msg_defs.h0000644000175000017500000002036113776656461020231 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.2/src/client/ns_turn_msg.c0000644000175000017500000022055113776656461017226 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(uint16_t 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) { 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) { uint32_t *ar=(uint32_t*)id; if(!RAND_pseudo_bytes((unsigned char *)ar,12)) { size_t i; for(i=0;i<3;++i) { ar[i] = (uint32_t)random(); } } } int stun_calculate_hmac(const uint8_t *buf, size_t len, const uint8_t *key, size_t keylen, uint8_t *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, (int)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, (int)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, (int)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, (int)keylen, buf, len, hmac, hmac_len)) { return -1; } return 0; } int stun_produce_integrity_key_str(const uint8_t *uname, const uint8_t *realm, const uint8_t *upwd, hmackey_t key, SHATYPE shatype) { int ret; ERR_clear_error(); UNUSED_ARG(shatype); size_t ulen = strlen((const char*)uname); size_t rlen = strlen((const char*)realm); size_t plen = strlen((const char*)upwd); size_t sz = ulen+1+rlen+1+plen+1+10; size_t strl = ulen+1+rlen+1+plen; uint8_t *str = (uint8_t*)malloc(sz+1); strncpy((char*)str,(const char*)uname,sz); str[ulen]=':'; strncpy((char*)str+ulen+1,(const char*)realm,sz-ulen-1); str[ulen+1+rlen]=':'; strncpy((char*)str+ulen+1+rlen+1,(const char*)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 ret = 0; #else fprintf(stderr,"SHA256 is not supported\n"); ret = -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 ret = 0; #else fprintf(stderr,"SHA384 is not supported\n"); ret = -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 ret = 0; #else fprintf(stderr,"SHA512 is not supported\n"); ret = -1; #endif } else { #if OPENSSL_VERSION_NUMBER < 0x10100000L unsigned int keylen = 0; EVP_MD_CTX ctx; EVP_MD_CTX_init(&ctx); #if defined EVP_MD_CTX_FLAG_NON_FIPS_ALLOW && !defined(LIBRESSL_VERSION_NUMBER) if (FIPS_mode()) { EVP_MD_CTX_set_flags(&ctx,EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); } #endif EVP_DigestInit_ex(&ctx,EVP_md5(), NULL); 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(); #if defined EVP_MD_CTX_FLAG_NON_FIPS_ALLOW && ! defined(LIBRESSL_VERSION_NUMBER) if (FIPS_mode()) { EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); } #endif EVP_DigestInit_ex(ctx,EVP_md5(), NULL); EVP_DigestUpdate(ctx,str,strl); EVP_DigestFinal(ctx,key,&keylen); EVP_MD_CTX_free(ctx); #endif ret = 0; } free(str); return ret; } #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 { 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]='$'; 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 len) { return -1; } return bufLen; } static int stun_set_command_message_len_str(uint8_t* buf, int len) { if(len>1) | ((tt & 0x0E00)>>2) | ((tt & 0x3000)>>2); } uint16_t stun_get_msg_type_str(const uint8_t *buf, size_t len) { if(!buf || len<2) return (uint16_t)-1; return ((nswap16(((const uint16_t*)buf)[0])) & 0x3FFF); } int is_channel_msg_str(const uint8_t* buf, size_t blen) { return (buf && blen>=4 && STUN_VALID_CHANNEL(nswap16(((const uint16_t*)buf)[0]))); } /////////////// message types ///////////////////////////////// int stun_is_command_message_str(const uint8_t* buf, size_t blen) { if (buf && blen >= STUN_HEADER_LENGTH) { if (!STUN_VALID_CHANNEL(nswap16(((const uint16_t*)buf)[0]))) { if ((((uint8_t) buf[0]) & ((uint8_t) (0xC0))) == 0) { if (nswap32(((const uint32_t*)(buf))[1]) == STUN_MAGIC_COOKIE) { uint16_t len = nswap16(((const uint16_t*)(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 uint8_t* buf, size_t blen, uint32_t *cookie) { if (buf && blen >= STUN_HEADER_LENGTH) { if (!STUN_VALID_CHANNEL(nswap16(((const uint16_t*)buf)[0]))) { if ((((uint8_t) buf[0]) & ((uint8_t) (0xC0))) == 0) { if (nswap32(((const uint32_t*)(buf))[1]) != STUN_MAGIC_COOKIE) { uint16_t len = nswap16(((const uint16_t*)(buf))[1]); if ((len & 0x0003) == 0) { if ((size_t) (len + STUN_HEADER_LENGTH) == blen) { *cookie = nswap32(((const uint32_t*)(buf))[1]); return 1; } } } } } } return 0; } int stun_is_command_message_full_check_str(const uint8_t* 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 uint32_t* fingerprint = (const uint32_t*)stun_attr_get_value(sar); if(!fingerprint) return !must_check_fingerprint; uint32_t crc32len = (uint32_t)((((const uint8_t*)fingerprint)-buf)-4); int ret = (*fingerprint == nswap32(ns_crc32(buf,crc32len) ^ ((uint32_t)0x5354554e))); if(ret && fingerprint_present) *fingerprint_present = ret; return ret; } int stun_is_command_message_offset_str(const uint8_t* buf, size_t blen, int offset) { return stun_is_command_message_str(buf + offset, blen); } int stun_is_request_str(const uint8_t* 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 uint8_t* 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 uint8_t* buf, size_t len, int *err_code, uint8_t *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 uint8_t* val = (const uint8_t*)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; bcopy(val+4, err_msg, msg_len); err_msg[msg_len]=0; } } } } } return 1; } return 0; } int stun_is_challenge_response_str(const uint8_t* buf, size_t len, int *err_code, uint8_t *err_msg, size_t err_msg_size, uint8_t *realm, uint8_t *nonce, uint8_t *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 uint8_t *value = stun_attr_get_value(sar); if(value) { size_t vlen = (size_t)stun_attr_get_len(sar); bcopy(value,realm,vlen); realm[vlen]=0; { sar = stun_attr_get_first_by_type_str(buf,len,STUN_ATTRIBUTE_THIRD_PARTY_AUTHORIZATION); if(sar) { value = stun_attr_get_value(sar); if(value) { vlen = (size_t)stun_attr_get_len(sar); if(vlen>0) { if(server_name) { 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); bcopy(value,nonce,vlen); nonce[vlen]=0; if(oauth) { *oauth = found_oauth; } return 1; } } } } } return 0; } int stun_is_response_str(const uint8_t* 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 uint8_t* buf, size_t len) { if(is_channel_msg_str(buf,len)) return 0; return IS_STUN_INDICATION(stun_get_msg_type_str(buf,len)); } uint16_t stun_make_request(uint16_t method) { return GET_STUN_REQUEST(stun_make_type(method)); } uint16_t stun_make_indication(uint16_t method) { return GET_STUN_INDICATION(stun_make_type(method)); } uint16_t stun_make_success_response(uint16_t method) { return GET_STUN_SUCCESS_RESP(stun_make_type(method)); } uint16_t stun_make_error_response(uint16_t method) { return GET_STUN_ERR_RESP(stun_make_type(method)); } //////////////// INIT //////////////////////////////////////////// void stun_init_buffer_str(uint8_t *buf, size_t *len) { *len=STUN_HEADER_LENGTH; bzero(buf,*len); } void stun_init_command_str(uint16_t message_type, uint8_t* buf, size_t *len) { stun_init_buffer_str(buf,len); message_type &= (uint16_t)(0x3FFF); ((uint16_t*)buf)[0]=nswap16(message_type); ((uint16_t*)buf)[1]=0; ((uint32_t*)buf)[1]=nswap32(STUN_MAGIC_COOKIE); stun_tid_generate_in_message_str(buf,NULL); } void old_stun_init_command_str(uint16_t message_type, uint8_t* buf, size_t *len, uint32_t cookie) { stun_init_buffer_str(buf,len); message_type &= (uint16_t)(0x3FFF); ((uint16_t*)buf)[0]=nswap16(message_type); ((uint16_t*)buf)[1]=0; ((uint32_t*)buf)[1]=nswap32(cookie); stun_tid_generate_in_message_str(buf,NULL); } void stun_init_request_str(uint16_t method, uint8_t* buf, size_t *len) { stun_init_command_str(stun_make_request(method), buf, len); } void stun_init_indication_str(uint16_t method, uint8_t* buf, size_t *len) { stun_init_command_str(stun_make_indication(method), buf, len); } void stun_init_success_response_str(uint16_t method, uint8_t* 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(uint16_t method, uint8_t* buf, size_t *len, stun_tid* id, uint32_t cookie) { old_stun_init_command_str(stun_make_success_response(method), buf, len, cookie); if(id) { stun_tid_message_cpy(buf, id); } } const uint8_t* get_default_reason(int error_code) { const uint8_t* reason = (const uint8_t *) "Unknown error"; switch (error_code){ case 300: reason = (const uint8_t *) "Try Alternate"; break; case 400: reason = (const uint8_t *) "Bad Request"; break; case 401: reason = (const uint8_t *) "Unauthorized"; break; case 403: reason = (const uint8_t *) "Forbidden"; break; case 404: reason = (const uint8_t *) "Not Found"; break; case 420: reason = (const uint8_t *) "Unknown Attribute"; break; case 437: reason = (const uint8_t *) "Allocation Mismatch"; break; case 438: reason = (const uint8_t *) "Stale Nonce"; break; case 440: reason = (const uint8_t *) "Address Family not Supported"; break; case 441: reason = (const uint8_t *) "Wrong Credentials"; break; case 442: reason = (const uint8_t *) "Unsupported Transport Protocol"; break; case 443: reason = (const uint8_t *) "Peer Address Family Mismatch"; break; case 446: reason = (const uint8_t *) "Connection Already Exists"; break; case 447: reason = (const uint8_t *) "Connection Timeout or Failure"; break; case 486: reason = (const uint8_t *) "Allocation Quota Reached"; break; case 487: reason = (const uint8_t *) "Role Conflict"; break; case 500: reason = (const uint8_t *) "Server Error"; break; case 508: reason = (const uint8_t *) "Insufficient Capacity"; break; default: ; }; return reason; } static void stun_init_error_response_common_str(uint8_t* buf, size_t *len, uint16_t error_code, const uint8_t *reason, stun_tid* id) { if (!reason || !strcmp((const char*)reason,"Unknown error")) { reason = get_default_reason(error_code); } uint8_t avalue[513]; avalue[0] = 0; avalue[1] = 0; avalue[2] = (uint8_t) (error_code / 100); avalue[3] = (uint8_t) (error_code % 100); strncpy((char*) (avalue + 4), (const char*) reason, sizeof(avalue)-4); avalue[sizeof(avalue)-1]=0; int alen = 4 + (int)strlen((const char*) (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, (uint8_t*) avalue, alen); if (id) { stun_tid_message_cpy(buf, id); } } void old_stun_init_error_response_str(uint16_t method, uint8_t* buf, size_t *len, uint16_t error_code, const uint8_t *reason, stun_tid* id, uint32_t 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(uint16_t method, uint8_t* buf, size_t *len, uint16_t error_code, const uint8_t *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(uint16_t chnumber, uint8_t* buf, size_t *len, int length, int do_padding) { uint16_t rlen = (uint16_t)length; if(length<0 || (MAX_STUN_MESSAGE_SIZE<(4+length))) return -1; ((uint16_t*)(buf))[0]=nswap16(chnumber); ((uint16_t*)(buf))[1]=nswap16((uint16_t)length); if(do_padding && (rlen & 0x0003)) rlen = ((rlen>>2)+1)<<2; *len=4+rlen; return 0; } int stun_is_channel_message_str(const uint8_t *buf, size_t *blen, uint16_t* chnumber, int mandatory_padding) { uint16_t datalen_header; uint16_t datalen_actual; if (!blen || (*blen < 4)) return 0; uint16_t chn = nswap16(((const uint16_t*)(buf))[0]); if (!STUN_VALID_CHANNEL(chn)) return 0; if(*blen>(uint16_t)-1) *blen=(uint16_t)-1; datalen_actual = (uint16_t)(*blen) - 4; datalen_header = ((const uint16_t*)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 { uint16_t 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(uint8_t *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 uint16_t*)buf)[0]))) { if ((((uint8_t) buf[0]) & ((uint8_t) (0xC0))) == 0) { if (nswap32(((const uint32_t*)(buf))[1]) == STUN_MAGIC_COOKIE) { uint16_t len = nswap16(((const uint16_t*)(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) { uint16_t chn=nswap16(((const uint16_t*)(buf))[0]); if(STUN_VALID_CHANNEL(chn)) { uint16_t bret = (4+(nswap16(((const uint16_t*)(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(uint8_t* buf, size_t *len, uint32_t lifetime, int af4, int af6, uint8_t transport, int mobile, const char* rt, int ep) { stun_init_request_str(STUN_METHOD_ALLOCATE, buf, len); //REQUESTED-TRANSPORT { uint8_t 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; uint32_t field=nswap32(lifetime); if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_LIFETIME,(uint8_t*)(&field),sizeof(field))<0) return -1; } //MICE if(mobile) { if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_MOBILITY_TICKET,(const uint8_t*)"",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 uint8_t*)&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 uint8_t*) rt, 8); } else { //ADRESS-FAMILY if (af4 && !af6) { uint8_t field[4]; field[0] = (uint8_t)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) { uint8_t field[4]; field[0] = (uint8_t)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) { uint8_t field[4]; field[0] = (uint8_t)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(uint8_t* buf, size_t *len, stun_tid* tid, const ioa_addr *relayed_addr1, const ioa_addr *relayed_addr2, const ioa_addr *reflexive_addr, uint32_t lifetime, uint32_t max_lifetime, int error_code, const uint8_t *reason, uint64_t 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,(uint8_t*)(&reservation_token),8); } { if(lifetime<1) lifetime=STUN_DEFAULT_ALLOCATE_LIFETIME; else if(lifetime>max_lifetime) lifetime = max_lifetime; uint32_t field=nswap32(lifetime); if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_LIFETIME,(uint8_t*)(&field),sizeof(field))<0) return -1; } if(mobile_id && *mobile_id) { if(stun_attr_add_str(buf,len,STUN_ATTRIBUTE_MOBILITY_TICKET,(uint8_t*)mobile_id,(int)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 /////////////////////////////////////// uint16_t stun_set_channel_bind_request_str(uint8_t* buf, size_t *len, const ioa_addr* peer_addr, uint16_t channel_number) { if(!STUN_VALID_CHANNEL(channel_number)) { channel_number = 0x4000 + ((uint16_t)(((uint32_t)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; 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(uint8_t* buf, size_t *len, stun_tid* tid, int error_code, const uint8_t *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(uint8_t* buf, size_t *len) { stun_init_request_str(STUN_METHOD_BINDING, buf, len); } int stun_set_binding_response_str(uint8_t* buf, size_t *len, stun_tid* tid, const ioa_addr *reflexive_addr, int error_code, const uint8_t *reason, uint32_t 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 uint8_t* 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 uint8_t* 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; bcopy((const void*)(id2->tsx_id),(void*)(id1->tsx_id),STUN_TID_SIZE); } static void stun_tid_string_cpy(uint8_t* s, const stun_tid* id) { if(s && id) { bcopy((const void*)(id->tsx_id),s,STUN_TID_SIZE); } } static void stun_tid_from_string(const uint8_t* s, stun_tid* id) { if(s && id) { bcopy(s,(void*)(id->tsx_id),STUN_TID_SIZE); } } void stun_tid_from_message_str(const uint8_t* buf, size_t len, stun_tid* id) { UNUSED_ARG(len); stun_tid_from_string(buf+8, id); } void stun_tid_message_cpy(uint8_t* 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(uint8_t* 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 uint16_t*)attr)[0])); return -1; } int stun_attr_get_len(stun_attr_ref attr) { if(attr) return (int)(nswap16(((const uint16_t*)attr)[1])); return -1; } const uint8_t* stun_attr_get_value(stun_attr_ref attr) { if(attr) { int len = (int)(nswap16(((const uint16_t*)attr)[1])); if(len<1) return NULL; return ((const uint8_t*)attr)+4; } return NULL; } int stun_get_requested_address_family(stun_attr_ref attr) { if (attr) { int len = (int) (nswap16(((const uint16_t*)attr)[1])); if (len != 4) return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_INVALID; int val = ((const uint8_t*) 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; } uint16_t stun_attr_get_channel_number(stun_attr_ref attr) { if(attr) { const uint8_t* value = stun_attr_get_value(attr); if(value && (stun_attr_get_len(attr) >= 2)) { uint16_t cn=nswap16(((const uint16_t*)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 uint8_t* value = stun_attr_get_value(attr); if(value && (stun_attr_get_len(attr) >= 4)) { uint32_t bps=nswap32(((const uint32_t*)value)[0]); return (band_limit_t)(bps << 7); } } return 0; } uint64_t stun_attr_get_reservation_token_value(stun_attr_ref attr) { if(attr) { const uint8_t* value = stun_attr_get_value(attr); if(value && (stun_attr_get_len(attr) == 8)) { uint64_t token; bcopy(value, &token, sizeof(uint64_t)); 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; } uint8_t stun_attr_get_even_port(stun_attr_ref attr) { if(attr) { const uint8_t* value=stun_attr_get_value(attr); if(value) { if((uint8_t)(value[0]) > 0x7F) return 1; } } return 0; } stun_attr_ref stun_attr_get_first_by_type_str(const uint8_t* buf, size_t len, uint16_t 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; } static stun_attr_ref stun_attr_check_valid(stun_attr_ref attr, size_t remaining) { if(remaining >= 4) { /* Read the size of the attribute */ size_t attrlen = stun_attr_get_len(attr); remaining -= 4; /* Round to boundary */ uint16_t rem4 = ((uint16_t)attrlen) & 0x0003; if(rem4) { attrlen = attrlen+4-(int)rem4; } /* Check that there's enough space remaining */ if(attrlen <= remaining) { return attr; } } return NULL; } stun_attr_ref stun_attr_get_first_str(const uint8_t* buf, size_t len) { int bufLen = stun_get_command_message_len_str(buf,len); if(bufLen > STUN_HEADER_LENGTH) { stun_attr_ref attr = (stun_attr_ref)(buf+STUN_HEADER_LENGTH); return stun_attr_check_valid(attr, bufLen - STUN_HEADER_LENGTH); } return NULL; } stun_attr_ref stun_attr_get_next_str(const uint8_t* buf, size_t len, stun_attr_ref prev) { if(!prev) return stun_attr_get_first_str(buf,len); else { const uint8_t* end = buf + stun_get_command_message_len_str(buf,len); int attrlen=stun_attr_get_len(prev); uint16_t rem4 = ((uint16_t)attrlen) & 0x0003; if(rem4) { attrlen = attrlen+4-(int)rem4; } /* Note the order here: operations on attrlen are untrusted as they may overflow */ if(attrlen < end - (const uint8_t*)prev - 4) { const uint8_t* attr_end=(const uint8_t*)prev+4+attrlen; return stun_attr_check_valid(attr_end, end - attr_end); } return NULL; } } int stun_attr_add_str(uint8_t* buf, size_t *len, uint16_t attr, const uint8_t* avalue, int alen) { if(alen<0) alen=0; uint8_t tmp[1]; if(!avalue) { alen=0; avalue=tmp; } int clen = stun_get_command_message_len_str(buf,*len); int newlen = clen + 4 + alen; int newlenrem4=newlen & 0x00000003; if(newlenrem4) { newlen=newlen+(4-newlenrem4); } if(newlen>=MAX_STUN_MESSAGE_SIZE) return -1; else { uint8_t* attr_start=buf+clen; uint16_t *attr_start_16t=(uint16_t*)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) bcopy(avalue,attr_start+4,alen); return 0; } } int stun_attr_add_addr_str(uint8_t *buf, size_t *len, uint16_t 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); uint8_t 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,(uint8_t*)(&cfield),clen)<0) return -1; return 0; } int stun_attr_get_addr_str(const uint8_t *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 uint8_t *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 uint8_t *buf, size_t len, uint16_t 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(uint8_t* buf, size_t *len, uint16_t chnumber) { uint16_t field[2]; field[0]=nswap16(chnumber); field[1]=0; return stun_attr_add_str(buf,len,STUN_ATTRIBUTE_CHANNEL_NUMBER,(uint8_t*)(field),sizeof(field)); } int stun_attr_add_bandwidth_str(uint8_t* buf, size_t *len, band_limit_t bps0) { uint32_t bps = (uint32_t)(band_limit_t)(bps0 >> 7); uint32_t field=nswap32(bps); return stun_attr_add_str(buf,len,STUN_ATTRIBUTE_NEW_BANDWIDTH,(uint8_t*)(&field),sizeof(field)); } int stun_attr_add_address_error_code(uint8_t* buf, size_t *len, int requested_address_family, int error_code) { const uint8_t *reason = get_default_reason(error_code); uint8_t avalue[513]; avalue[0] = (uint8_t)requested_address_family; avalue[1] = 0; avalue[2] = (uint8_t) (error_code / 100); avalue[3] = (uint8_t) (error_code % 100); strncpy((char*) (avalue + 4), (const char*) reason, sizeof(avalue)-4); avalue[sizeof(avalue)-1]=0; int alen = 4 + (int)strlen((const char*) (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, (uint8_t*) avalue, alen); return 0; } int stun_attr_get_address_error_code(uint8_t* 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 uint8_t* 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; } uint16_t stun_attr_get_first_channel_number_str(const uint8_t *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) { uint16_t 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(uint8_t *buf, size_t *len) { uint32_t crc32 = 0; stun_attr_add_str(buf, len, STUN_ATTRIBUTE_FINGERPRINT, (uint8_t*)&crc32, 4); crc32 = ns_crc32(buf,(int)*len-8); *((uint32_t*)(buf+*len-4)) = nswap32(crc32 ^ ((uint32_t)0x5354554e)); return 0; } ////////////// CRC /////////////////////////////////////////////// #define CRC_MASK 0xFFFFFFFFUL #define UPDATE_CRC(crc, c) crc = crctable[(uint8_t)crc ^ (uint8_t)(c)] ^ (crc >> 8) static const uint32_t 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; uint32_t 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 uint32_t ns_crc32(const uint8_t *buffer, uint32_t len) { uint32_t crc = CRC_MASK; while ( len-- ) UPDATE_CRC( crc, *buffer++ ); return (~crc); } //////////// SASLprep RFC 4013 ///////////////////////////////////////// /* We support only basic ASCII table */ int SASLprep(uint8_t *s) { if(s) { uint8_t *strin = s; uint8_t *strout = s; for(;;) { uint8_t 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, uint8_t *buf, size_t len, const uint8_t *uname, const uint8_t *realm, const uint8_t *upwd, SHATYPE shatype) { hmackey_t key; password_t pwd; if(ct == TURN_CREDENTIALS_SHORT_TERM) strncpy((char*)pwd,(const 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 uint8_t* value = stun_attr_get_value(attr); if(value) { *change_ip = (value[3] & (uint8_t)0x04); *change_port = (value[3] & (uint8_t)0x02); return 0; } } return -1; } int stun_attr_add_change_request_str(uint8_t *buf, size_t *len, int change_ip, int change_port) { uint8_t 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 uint8_t* value = stun_attr_get_value(attr); if(value) { return nswap16(((const uint16_t*)value)[0]); } } return -1; } int stun_attr_add_response_port_str(uint8_t *buf, size_t *len, uint16_t port) { uint8_t avalue[4]={0,0,0,0}; uint16_t *port_ptr = (uint16_t*)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 (uint16_t)len; } int stun_attr_add_padding_str(uint8_t *buf, size_t *len, uint16_t padding_len) { uint8_t avalue[0xFFFF]; 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); 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; 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; } bzero(key,sizeof(oauth_key)); STRCPY(key->kid,oakd->kid); 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 uint8_t *server_name, encoded_oauth_token *etoken, const oauth_key *key, const oauth_token *dtoken); int encode_oauth_token_normal(const uint8_t *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]; bzero(orig_field,sizeof(orig_field)); size_t len = 0; *((uint16_t*)(orig_field+len)) = nswap16(dtoken->enc_block.key_length); len +=2; 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); 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); 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 uint8_t *server_name, const encoded_oauth_token *etoken, const oauth_key *key, oauth_token *dtoken); int decode_oauth_token_normal(const uint8_t *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]; bcopy(encoded_field,efield,encoded_field_size); size_t sn_len = strlen((const char*)server_name); 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(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; 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, (int)sz)) { size_t i; for(i=0;ienc_block.key_length<=MAXSHASIZE)) { unsigned char orig_field[MAX_ENCODED_OAUTH_TOKEN_SIZE]; bzero(orig_field,sizeof(orig_field)); unsigned char nonce[OAUTH_GCM_NONCE_SIZE]; if(nonce0) { 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; 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; 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); 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; 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 uint8_t *server_name, const encoded_oauth_token *etoken, const oauth_key *key, oauth_token *dtoken) { if(server_name && etoken && key && dtoken) { unsigned char snl[2]; 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); bcopy(nonce,dtoken->enc_block.nonce,nonce_len); unsigned char tag[OAUTH_GCM_TAG_SIZE]; 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; bcopy(decoded_field+len,dtoken->enc_block.mac_key,dtoken->enc_block.key_length); len += dtoken->enc_block.key_length; uint64_t ts; bcopy((decoded_field+len),&ts,sizeof(ts)); dtoken->enc_block.timestamp = nswap64(ts); len += sizeof(ts); uint32_t lt; 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 uint8_t *server_name, encoded_oauth_token *etoken, const oauth_key *key, const oauth_token *dtoken, const uint8_t *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 uint8_t *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.2/src/client/ns_turn_ioaddr.c0000644000175000017500000003557713776656461017716 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 ////////////////////////////////////////////////////////////// uint32_t 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) 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 char*)&(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 char*)(&(addr->s6.sin6_addr)))[i]) return 0; } return 1; } uint32_t hash_int32(uint32_t a) { a = a ^ (a>>4); a = (a^0xdeadbeef) + (a<<5); a = a ^ (a>>11); return a; } uint64_t hash_int64(uint64_t a) { a = a ^ (a>>4); a = (a^0xdeadbeefdeadbeefLL) + (a<<5); a = a ^ (a>>11); return a; } uint32_t addr_hash(const ioa_addr *addr) { if(!addr) return 0; uint32_t ret = 0; if (addr->ss.sa_family == AF_INET) { ret = hash_int32(addr->s4.sin_addr.s_addr + addr->s4.sin_port); } else { uint64_t a[2]; bcopy(&(addr->s6.sin6_addr), &a, sizeof(a)); ret = (uint32_t)((hash_int64(a[0])<<3) + (hash_int64(a[1] + addr->s6.sin6_port))); } return ret; } uint32_t addr_hash_no_port(const ioa_addr *addr) { if(!addr) return 0; uint32_t ret = 0; if (addr->ss.sa_family == AF_INET) { ret = hash_int32(addr->s4.sin_addr.s_addr); } else { uint64_t a[2]; bcopy(&(addr->s6.sin6_addr), &a, sizeof(a)); ret = (uint32_t)((hash_int64(a[0])<<3) + (hash_int64(a[1]))); } return ret; } void addr_cpy(ioa_addr* dst, const ioa_addr* src) { if(dst && src) bcopy(src,dst,sizeof(ioa_addr)); } void addr_cpy4(ioa_addr* dst, const struct sockaddr_in* src) { if(src && dst) bcopy(src,dst,sizeof(struct sockaddr_in)); } void addr_cpy6(ioa_addr* dst, const struct sockaddr_in6* src) { if(src && dst) 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 uint8_t* 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; } } 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) { 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) { 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 uint8_t* saddr, int default_port, ioa_addr *addr) { if(!addr) return -1; int ret = -1; int port = 0; char* s = 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((uint8_t*)sa,port,addr); } free(s); return ret; } int addr_to_string(const ioa_addr* addr, uint8_t* saddr) { if (addr && saddr) { char 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((char*)saddr, MAX_IOA_ADDR_STRING, "%s:%d", addrtmp, addr_get_port(addr)); else strncpy((char*)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((char*)saddr, MAX_IOA_ADDR_STRING, "[%s]:%d", addrtmp, addr_get_port(addr)); else strncpy((char*)saddr, addrtmp, MAX_IOA_ADDR_STRING); } else { return -1; } return 0; } return -1; } int addr_to_string_no_port(const ioa_addr* addr, uint8_t* saddr) { if (addr && saddr) { char addrtmp[MAX_IOA_ADDR_STRING]; if (addr->ss.sa_family == AF_INET) { inet_ntop(AF_INET, &addr->s4.sin_addr, addrtmp, INET_ADDRSTRLEN); strncpy((char*)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((char*)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 ((uint32_t)nswap32(addr1->s4.sin_addr.s_addr) <= (uint32_t)nswap32(addr2->s4.sin_addr.s_addr)); } else if(addr1->ss.sa_family == AF_INET6) { int i; for(i=0;i<16;i++) { if((uint8_t)(((const char*)&(addr1->s6.sin6_addr))[i]) > (uint8_t)(((const char*)&(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 uint8_t *u = ((const uint8_t*)&(addr->s4.sin_addr)); return (u[0] > 223); } else if(addr->ss.sa_family == AF_INET6) { uint8_t u = ((const uint8_t*)&(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 uint8_t *u = ((const uint8_t*)&(addr->s4.sin_addr)); return (u[0] == 127); } else if(addr->ss.sa_family == AF_INET6) { const uint8_t *u = ((const uint8_t*)&(addr->s6.sin6_addr)); if(u[15] == 1) { int i; for(i=0;i<15;++i) { if(u[i]) return 0; } return 1; } } } return 0; } /* To avoid a vulnerability this function checks whether the addr is in 0.0.0.0/8 or ::/128. Source from (INADDR_ANY) 0.0.0.0/32 and (in6addr_any) ::/128 routed to loopback on Linux systems for old BSD backward compatibility. https://github.com/torvalds/linux/blob/a2f5ea9e314ba6778f885c805c921e9362ec0420/net/ipv6/tcp_ipv6.c#L182 To avoid any trouble we match the whole 0.0.0.0/8 that defined in RFC6890 as local network "this". */ int ioa_addr_is_zero(ioa_addr *addr) { if(addr) { if(addr->ss.sa_family == AF_INET) { const uint8_t *u = ((const uint8_t*)&(addr->s4.sin_addr)); return (u[0] == 0); } else if(addr->ss.sa_family == AF_INET6) { const uint8_t *u = ((const uint8_t*)&(addr->s6.sin6_addr)); int i; for(i=0;i<=15;++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**)realloc(public_addrs, new_size); private_addrs = (ioa_addr**)realloc(private_addrs, new_size); public_addrs[mcount]=(ioa_addr*)malloc(sizeof(ioa_addr)); private_addrs[mcount]=(ioa_addr*)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.2/src/client/ns_turn_msg.h0000644000175000017500000002634313776656461017236 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__ #define __LIB_TURN_MSG__ #include "ns_turn_ioaddr.h" #include "ns_turn_msg_defs.h" #ifdef __cplusplus extern "C" { #endif /////////////////////////////////// /** * Structure holding the STUN message Transaction ID */ #define STUN_TID_SIZE (12) typedef struct { /** * Binary array */ uint8_t tsx_id[STUN_TID_SIZE]; } stun_tid; typedef enum { TURN_CREDENTIALS_NONE = 0, TURN_CREDENTIALS_LONG_TERM, TURN_CREDENTIALS_SHORT_TERM, TURN_CREDENTIALS_UNDEFINED } turn_credential_type; /** * HMAC key */ typedef uint8_t hmackey_t[64]; typedef uint8_t password_t[STUN_MAX_PWD_SIZE+1]; typedef unsigned long band_limit_t; /////////////////////////////////// typedef const void* stun_attr_ref; ////////////////////////////////////////////////////////////// int stun_tid_equals(const stun_tid *id1, const stun_tid *id2); void stun_tid_cpy(stun_tid *id_dst, const stun_tid *id_src); void stun_tid_generate(stun_tid* id); /////////////////////////////////////////////////////////////// uint16_t stun_make_type(uint16_t method); uint16_t stun_make_request(uint16_t method); uint16_t stun_make_indication(uint16_t method); uint16_t stun_make_success_response(uint16_t method); uint16_t stun_make_error_response(uint16_t method); /////////////////////////////////////////////////////////////// turn_time_t stun_adjust_allocate_lifetime(turn_time_t lifetime, turn_time_t max_allowed_lifetime, turn_time_t max_lifetime); ///////////// STR //////////////////////////////////////////////// int stun_method_str(uint16_t method, char *smethod); int stun_get_message_len_str(uint8_t *buf, size_t len, int padding, size_t *app_len); void stun_init_buffer_str(uint8_t *buf, size_t *len); void stun_init_command_str(uint16_t message_type, uint8_t* buf, size_t *len); void old_stun_init_command_str(uint16_t message_type, uint8_t* buf, size_t *len, uint32_t cookie); void stun_init_request_str(uint16_t method, uint8_t* buf, size_t *len); void stun_init_indication_str(uint16_t method, uint8_t* buf, size_t *len); void stun_init_success_response_str(uint16_t method, uint8_t* buf, size_t *len, stun_tid* id); void old_stun_init_success_response_str(uint16_t method, uint8_t* buf, size_t *len, stun_tid* id, uint32_t cookie); void stun_init_error_response_str(uint16_t method, uint8_t* buf, size_t *len, uint16_t error_code, const uint8_t *reason, stun_tid* id); void old_stun_init_error_response_str(uint16_t method, uint8_t* buf, size_t *len, uint16_t error_code, const uint8_t *reason, stun_tid* id, uint32_t cookie); int stun_init_channel_message_str(uint16_t chnumber, uint8_t* buf, size_t *len, int length, int do_padding); int stun_is_command_message_str(const uint8_t* buf, size_t blen); int old_stun_is_command_message_str(const uint8_t* buf, size_t blen, uint32_t *cookie); int stun_is_command_message_full_check_str(const uint8_t* buf, size_t blen, int must_check_fingerprint, int *fingerprint_present); int stun_is_command_message_offset_str(const uint8_t* buf, size_t blen, int offset); int stun_is_request_str(const uint8_t* buf, size_t len); int stun_is_success_response_str(const uint8_t* buf, size_t len); int stun_is_error_response_str(const uint8_t* buf, size_t len, int *err_code, uint8_t *err_msg, size_t err_msg_size); int stun_is_challenge_response_str(const uint8_t* buf, size_t len, int *err_code, uint8_t *err_msg, size_t err_msg_size, uint8_t *realm, uint8_t *nonce, uint8_t *server_name, int *oauth); int stun_is_response_str(const uint8_t* buf, size_t len); int stun_is_indication_str(const uint8_t* buf, size_t len); uint16_t stun_get_method_str(const uint8_t *buf, size_t len); uint16_t stun_get_msg_type_str(const uint8_t *buf, size_t len); int stun_is_channel_message_str(const uint8_t *buf, size_t *blen, uint16_t* chnumber, int mandatory_padding); int is_channel_msg_str(const uint8_t* buf, size_t blen); void stun_set_binding_request_str(uint8_t* buf, size_t *len); int stun_set_binding_response_str(uint8_t* buf, size_t *len, stun_tid* tid, const ioa_addr *reflexive_addr, int error_code, const uint8_t *reason, uint32_t cookie, int old_stun); int stun_is_binding_request_str(const uint8_t* buf, size_t len, size_t offset); int stun_is_binding_response_str(const uint8_t* buf, size_t len); void stun_tid_from_message_str(const uint8_t* buf, size_t len, stun_tid* id); void stun_tid_message_cpy(uint8_t *buf, const stun_tid* id); void stun_tid_generate_in_message_str(uint8_t* buf, stun_tid* id); int stun_get_command_message_len_str(const uint8_t* buf, size_t len); const uint8_t* get_default_reason(int error_code); int stun_attr_is_addr(stun_attr_ref attr); int stun_attr_get_type(stun_attr_ref attr); int stun_attr_get_len(stun_attr_ref attr); const uint8_t* stun_attr_get_value(stun_attr_ref attr); uint16_t stun_attr_get_channel_number(stun_attr_ref attr); band_limit_t stun_attr_get_bandwidth(stun_attr_ref attr); uint8_t stun_attr_get_even_port(stun_attr_ref attr); uint64_t stun_attr_get_reservation_token_value(stun_attr_ref attr); stun_attr_ref stun_attr_get_first_by_type_str(const uint8_t* buf, size_t len, uint16_t attr_type); stun_attr_ref stun_attr_get_first_str(const uint8_t* buf, size_t len); stun_attr_ref stun_attr_get_next_str(const uint8_t* buf, size_t len, stun_attr_ref prev); int stun_attr_add_str(uint8_t* buf, size_t *len, uint16_t attr, const uint8_t* avalue, int alen); int stun_attr_add_addr_str(uint8_t *buf, size_t *len, uint16_t attr_type, const ioa_addr* ca); int stun_attr_get_addr_str(const uint8_t *buf, size_t len, stun_attr_ref attr, ioa_addr* ca, const ioa_addr *default_addr); int stun_attr_get_first_addr_str(const uint8_t *buf, size_t len, uint16_t attr_type, ioa_addr* ca, const ioa_addr *default_addr); int stun_attr_add_channel_number_str(uint8_t* buf, size_t *len, uint16_t chnumber); int stun_attr_add_bandwidth_str(uint8_t* buf, size_t *len, band_limit_t bps); int stun_attr_add_address_error_code(uint8_t* buf, size_t *len, int requested_address_family, int error_code); /* return +1 if present, 0 if not, -1 if error: */ int stun_attr_get_address_error_code(uint8_t* buf, size_t len, int *requested_address_family, int *error_code); uint16_t stun_attr_get_first_channel_number_str(const uint8_t *buf, size_t len); int stun_set_allocate_request_str(uint8_t* buf, size_t *len, uint32_t lifetime, int af4, int af6, uint8_t transport, int mobile, const char* rt, int ep); int stun_set_allocate_response_str(uint8_t* buf, size_t *len, stun_tid* tid, const ioa_addr *relayed_addr1, const ioa_addr *relayed_addr2, const ioa_addr *reflexive_addr, uint32_t lifetime, uint32_t max_lifetime, int error_code, const uint8_t *reason, uint64_t reservation_token, char *mobile_id); uint16_t stun_set_channel_bind_request_str(uint8_t* buf, size_t *len, const ioa_addr* peer_addr, uint16_t channel_number); void stun_set_channel_bind_response_str(uint8_t* buf, size_t *len, stun_tid* tid, int error_code, const uint8_t *reason); int stun_get_requested_address_family(stun_attr_ref attr); int stun_attr_add_fingerprint_str(uint8_t *buf, size_t *len); int SASLprep(uint8_t *s); #define print_bin(str, len, field) print_bin_func(str,len,field,__FUNCTION__) void print_bin_func(const char *name, size_t len, const void *s, const char *func); /* * Return -1 if failure, 0 if the integrity is not correct, 1 if OK */ int stun_check_message_integrity_by_key_str(turn_credential_type ct, uint8_t *buf, size_t len, hmackey_t key, password_t pwd, SHATYPE shatype); int stun_check_message_integrity_str(turn_credential_type ct, uint8_t *buf, size_t len, const uint8_t *uname, const uint8_t *realm, const uint8_t *upwd, SHATYPE shatype); int stun_attr_add_integrity_str(turn_credential_type ct, uint8_t *buf, size_t *len, hmackey_t key, password_t pwd, SHATYPE shatype); int stun_attr_add_integrity_by_key_str(uint8_t *buf, size_t *len, const uint8_t *uname, const uint8_t *realm, hmackey_t key, const uint8_t *nonce, SHATYPE shatype); int stun_attr_add_integrity_by_user_str(uint8_t *buf, size_t *len, const uint8_t *uname, const uint8_t *realm, const uint8_t *upwd, const uint8_t *nonce, SHATYPE shatype); int stun_attr_add_integrity_by_user_short_term_str(uint8_t *buf, size_t *len, const uint8_t *uname, password_t pwd, SHATYPE shatype); size_t get_hmackey_size(SHATYPE shatype); /* * To be implemented with openssl */ #define TURN_RANDOM_SIZE (sizeof(long)) long turn_random(void); int stun_produce_integrity_key_str(const uint8_t *uname, const uint8_t *realm, const uint8_t *upwd, hmackey_t key, SHATYPE shatype); int stun_calculate_hmac(const uint8_t *buf, size_t len, const uint8_t *key, size_t sz, uint8_t *hmac, unsigned int *hmac_len, SHATYPE shatype); /* RFC 5780 */ int stun_attr_get_change_request_str(stun_attr_ref attr, int *change_ip, int *change_port); int stun_attr_add_change_request_str(uint8_t *buf, size_t *len, int change_ip, int change_port); int stun_attr_get_response_port_str(stun_attr_ref attr); int stun_attr_add_response_port_str(uint8_t *buf, size_t *len, uint16_t port); int stun_attr_get_padding_len_str(stun_attr_ref attr); int stun_attr_add_padding_str(uint8_t *buf, size_t *len, uint16_t padding_len); /* HTTP */ int is_http(const char *s, size_t blen); /* OAUTH */ int convert_oauth_key_data(const oauth_key_data *oakd, oauth_key *key, char *err_msg, size_t err_msg_size); int decode_oauth_token(const uint8_t *server_name, const encoded_oauth_token *etoken, const oauth_key *key, oauth_token *dtoken); int encode_oauth_token(const uint8_t *server_name, encoded_oauth_token *etoken, const oauth_key *key, const oauth_token *dtoken, const uint8_t *nonce); /* Encrypted password */ void generate_new_enc_password(const char* pwd, char *result); int check_password(const char* pin, const char* pwd); /////////////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__LIB_TURN_MSG__ turnserver-4.5.2/src/client/ns_turn_msg_addr.c0000644000175000017500000001121313776656461020211 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, uint8_t *cfield, int *clen, int xor_ed, uint32_t mc, const uint8_t *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 */ ((uint16_t*)cfield)[1] = (ca->s4.sin_port) ^ nswap16(mc >> 16); /* Address */ ((uint32_t*)cfield)[1] = (ca->s4.sin_addr.s_addr) ^ nswap32(mc); } else { /* Port */ ((uint16_t*)cfield)[1]=ca->s4.sin_port; /* Address */ ((uint32_t*)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; uint8_t *dst = ((uint8_t*)cfield)+4; const uint8_t *src = (const uint8_t*)&(ca->s6.sin6_addr); uint32_t magic = nswap32(mc); /* Port */ ((uint16_t*)cfield)[1] = ca->s6.sin6_port ^ nswap16(mc >> 16); /* Address */ for (i=0; i<4; ++i) { dst[i] = (uint8_t)(src[i] ^ ((const uint8_t*)&magic)[i]); } for (i=0; i<12; ++i) { dst[i+4] = (uint8_t)(src[i+4] ^ tsx_id[i]); } } else { /* Port */ ((uint16_t*)cfield)[1]=ca->s6.sin6_port; /* Address */ bcopy(&ca->s6.sin6_addr, ((uint8_t*)cfield)+4, 16); } } else { return -1; } return 0; } int stun_addr_decode(ioa_addr* ca, const uint8_t *cfield, int len, int xor_ed, uint32_t mc, const uint8_t *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 uint16_t*)cfield)[1]; /* Address */ ca->s4.sin_addr.s_addr=((const uint32_t*)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 uint16_t*)cfield)[1]; /* Address */ bcopy(((const uint8_t*)cfield)+4, &ca->s6.sin6_addr, 16); if (xor_ed) { unsigned int i; uint8_t *dst; const uint8_t *src; uint32_t magic = nswap32(mc); /* Port */ ca->s6.sin6_port ^= nswap16(mc >> 16); /* Address */ src = ((const uint8_t*)cfield)+4; dst = (uint8_t*)&ca->s6.sin6_addr; for (i=0; i<4; ++i) { dst[i] = (uint8_t)(src[i] ^ ((const uint8_t*)&magic)[i]); } for (i=0; i<12; ++i) { dst[i+4] = (uint8_t)(src[i+4] ^ tsx_id[i]); } } } else { return -1; } return 0; } ////////////////////////////////////////////////////////////////////////////// turnserver-4.5.2/src/client/ns_turn_ioaddr.h0000644000175000017500000000767013776656461017714 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; //////////////////////////// uint32_t 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); uint32_t addr_hash(const ioa_addr *addr); uint32_t 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 uint8_t* saddr, int port, ioa_addr *addr); int make_ioa_addr_from_full_string(const uint8_t* 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, uint8_t* saddr); int addr_to_string_no_port(const ioa_addr* addr, uint8_t* saddr); uint32_t hash_int32(uint32_t a); uint64_t hash_int64(uint64_t 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); int ioa_addr_is_zero(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.2/src/client/ns_turn_msg_addr.h0000644000175000017500000000404513776656461020223 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, uint8_t *cfield, int *clen, int xor_ed, uint32_t mc, const uint8_t *tsx_id); int stun_addr_decode(ioa_addr* ca, const uint8_t *cfield, int len, int xor_ed, uint32_t mc, const uint8_t *tsx_id); /////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__LIB_TURN_MSG_ADDR__ turnserver-4.5.2/src/client++/0000755000175000017500000000000013776656461014645 5ustar misimisiturnserver-4.5.2/src/client++/TurnMsgLib.h0000644000175000017500000006410313776656461017050 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(uint8_t *buf, size_t sz) : _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) : _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(uint8_t *buf, size_t sz, uint16_t attr_type) : _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, uint16_t attr_type) : _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() { 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 uint8_t *getRawBuffer(size_t &sz) const { int len = stun_attr_get_len(_sar); if(len<0) throw WrongStunAttrFormatException(); sz = (size_t)len; const uint8_t *value = stun_attr_get_value(_sar); return value; } friend class StunAttr; private: uint8_t *_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) { if(iter.eof()) { throw EndOfStunMsgException(); } size_t sz = 0; const uint8_t *ptr = iter.getRawBuffer(sz); if(sz>=0xFFFF) throw WrongStunAttrFormatException(); int at = iter.getType(); if(at<0) throw WrongStunAttrFormatException(); _attr_type = (uint16_t)at; _sz = sz; _value=(uint8_t*)malloc(_sz); if(ptr) bcopy(ptr,_value,_sz); } /** * Destructor */ virtual ~StunAttr() { if(_value) free(_value); } /** * Return raw data representation of the attribute */ const uint8_t *getRawValue(size_t &sz) const { sz=_sz; return _value; } /** * Set raw data value */ void setRawValue(uint8_t *value, size_t sz) { if(sz>0xFFFF) throw WrongStunAttrFormatException(); if(_value) free(_value); _sz = sz; _value=(uint8_t*)malloc(_sz); if(value) bcopy(value,_value,_sz); } /** * Get attribute type */ uint16_t getType() const { return _attr_type; } /** * Set attribute type */ void setType(uint16_t at) { _attr_type = at; } /** * Add attribute to a message */ template int addToMsg(T &msg) { if(!_attr_type) throw WrongStunAttrFormatException(); uint8_t *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(uint8_t *buffer, size_t &sz) { 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: uint16_t _attr_type; uint8_t *_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) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); _cn = stun_attr_get_channel_number(getSar(iter)); if(!_cn) throw WrongStunAttrFormatException(); } virtual ~StunAttrChannelNumber() {} uint16_t getChannelNumber() const { return _cn; } void setChannelNumber(uint16_t cn) { _cn = cn; } protected: virtual int addToBuffer(uint8_t *buffer, size_t &sz) { return stun_attr_add_channel_number_str(buffer,&sz,_cn); } private: uint16_t _cn; }; /** * Even port attribute class */ class StunAttrEvenPort : public StunAttr { public: StunAttrEvenPort() : _ep(0) { setType(STUN_ATTRIBUTE_EVEN_PORT); } StunAttrEvenPort(const StunAttrIterator &iter) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); _ep = stun_attr_get_even_port(getSar(iter)); } virtual ~StunAttrEvenPort() {} uint8_t getEvenPort() const { return _ep; } void setEvenPort(uint8_t ep) { _ep = ep; } protected: virtual int addToBuffer(uint8_t *buffer, size_t &sz) { return stun_attr_add_str(buffer, &sz, STUN_ATTRIBUTE_EVEN_PORT, &_ep, 1); } private: uint8_t _ep; }; /** * Reservation token attribute class */ class StunAttrReservationToken : public StunAttr { public: StunAttrReservationToken() : _rt(0) { setType(STUN_ATTRIBUTE_RESERVATION_TOKEN); } StunAttrReservationToken(const StunAttrIterator &iter) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); _rt = stun_attr_get_reservation_token_value(getSar(iter)); } virtual ~StunAttrReservationToken() {} uint64_t getReservationToken() const { return _rt; } void setReservationToken(uint64_t rt) { _rt = rt; } protected: virtual int addToBuffer(uint8_t *buffer, size_t &sz) { uint64_t reservation_token = ioa_ntoh64(_rt); return stun_attr_add_str(buffer, &sz, STUN_ATTRIBUTE_RESERVATION_TOKEN, (uint8_t*) (&reservation_token), 8); } private: uint64_t _rt; }; /** * This attribute class is used for all address attributes */ class StunAttrAddr : public StunAttr { public: StunAttrAddr(uint16_t attr_type = 0) { addr_set_any(&_addr); setType(attr_type); } StunAttrAddr(const StunAttrIterator &iter) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); size_t sz = 0; const uint8_t *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(uint8_t *buffer, size_t &sz) { 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) : 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(uint8_t *buffer, size_t &sz) { 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) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); int rp = stun_attr_get_response_port_str(getSar(iter)); if(rp<0) { throw WrongStunAttrFormatException(); } _rp = (uint16_t)rp; } virtual ~StunAttrResponsePort() {} uint16_t getResponsePort() const { return _rp; } void setResponsePort(uint16_t p) { _rp = p; } protected: virtual int addToBuffer(uint8_t *buffer, size_t &sz) { return stun_attr_add_response_port_str(buffer, &sz, _rp); } private: uint16_t _rp; }; /** * Padding attribute class */ class StunAttrPadding : public StunAttr { public: StunAttrPadding() : _p(0) { setType(STUN_ATTRIBUTE_PADDING); } StunAttrPadding(const StunAttrIterator &iter) : StunAttr(iter) { if(iter.eof()) throw EndOfStunMsgException(); int p = stun_attr_get_padding_len_str(getSar(iter)); if(p<0) { throw WrongStunAttrFormatException(); } _p = (uint16_t)p; } virtual ~StunAttrPadding() {} uint16_t getPadding() const { return _p; } /** * Set length of padding */ void setPadding(uint16_t p) { _p = p; } protected: virtual int addToBuffer(uint8_t *buffer, size_t &sz) { return stun_attr_add_padding_str(buffer, &sz, _p); } private: uint16_t _p; }; /** * Generic "STUN Message" class, base class for all messages */ class StunMsg { public: /** * Empty constructor */ StunMsg() { _allocated_sz = 0xFFFF; _buffer = (uint8_t*)malloc(_allocated_sz); _deallocate = true; _sz = 0; _constructed = 0; } /** * Construct message over raw buffer. * Parameter "construct" is true if the buffer is initialized. */ StunMsg(uint8_t *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) { free(_buffer); } } /** * Initialize buffer */ void construct() { constructBuffer(); } /** * Checks if the message is properly constructed */ bool isValid() { return check(); } /** * get raw buffer */ uint8_t *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) { if(sz>_allocated_sz) throw WrongStunBufferFormatException(); _sz = sz; } /** * Check if the raw buffer is a TURN "command" (request, response or indication). */ static bool isCommand(uint8_t *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(uint8_t *buffer, size_t sz) { return stun_is_indication_str(buffer, sz); } static bool isRequest(uint8_t *buffer, size_t sz) { return stun_is_request_str(buffer, sz); } static bool isSuccessResponse(uint8_t *buffer, size_t sz) { return stun_is_success_response_str(buffer, sz); } static bool isErrorResponse(uint8_t *buffer, size_t sz, int &err_code, uint8_t *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 uint8_t* buf, size_t sz, int &err_code, uint8_t *err_msg, size_t err_msg_size, uint8_t *realm, uint8_t *nonce, uint8_t *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(uint8_t *buffer, size_t sz) { return is_channel_msg_str(buffer, sz); } /** * Check if the fingerprint is present. */ static bool isFingerprintPresent(uint8_t *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(uint8_t *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) { return attr.addToMsg(*this); } /** * Get transaction ID */ virtual stun_tid getTid() const { 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) { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); stun_tid_message_cpy(_buffer, &tid); } /** * Add fingerprint to the message */ void addFingerprint() { 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 { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); uint8_t *suname=(uint8_t*)strdup(uname.c_str()); uint8_t *srealm=(uint8_t*)strdup(realm.c_str()); uint8_t *supwd=(uint8_t*)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) { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); uint8_t *suname=(uint8_t*)strdup(uname.c_str()); uint8_t *srealm=(uint8_t*)strdup(realm.c_str()); uint8_t *supwd=(uint8_t*)strdup(upwd.c_str()); uint8_t *snonce=(uint8_t*)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) { if(!_constructed || !isCommand()) throw WrongStunBufferFormatException(); uint8_t *suname=(uint8_t*)strdup(uname.c_str()); uint8_t *supwd=(uint8_t*)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: uint8_t *_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(uint16_t method) : _method(method) {}; StunMsgRequest(uint8_t *buffer, size_t total_sz, size_t sz, bool constructed) : 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 */ uint16_t getMethod() const { return _method; } /** * Set method */ void setMethod(uint16_t 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(uint32_t lifetime, int af4, int af6, uint8_t 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, uint16_t 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: uint16_t _method; }; /** * Class for STUN/TURN responses */ class StunMsgResponse : public StunMsg { public: StunMsgResponse(uint16_t method, stun_tid &tid) : _method(method), _err(0), _reason(""), _tid(tid) {}; StunMsgResponse(uint16_t method, int error_code, std::string reason, stun_tid &tid) : _method(method), _err(error_code), _reason(reason), _tid(tid) { }; StunMsgResponse(uint8_t *buffer, size_t total_sz, size_t sz, bool constructed) : StunMsg(buffer,total_sz,sz,constructed),_method(0),_err(0),_reason("") { if(constructed) { if(!stun_is_success_response_str(buffer,sz)) { uint8_t 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); } } uint16_t getMethod() const { return _method; } void setMethod(uint16_t 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) { _tid = tid; } /** * Get transaction ID */ virtual stun_tid getTid() const { 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; uint8_t err_msg[1025]; size_t err_msg_size=sizeof(err_msg); uint8_t srealm[0xFFFF]; uint8_t 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 uint8_t *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, uint32_t lifetime, int error_code, const uint8_t *reason, uint64_t 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 uint8_t *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 uint8_t*)_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)) { uint8_t 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: uint16_t _method; int _err; std::string _reason; stun_tid _tid; }; /** * Class for STUN/TURN indications */ class StunMsgIndication : public StunMsg { public: StunMsgIndication(uint16_t method) : _method(method) {}; StunMsgIndication(uint8_t *buffer, size_t total_sz, size_t sz, bool constructed) : 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() {} uint16_t getMethod() const { return _method; } void setMethod(uint16_t 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: uint16_t _method; }; /** * Channel message */ class StunMsgChannel : public StunMsg { public: StunMsgChannel(uint16_t cn, int length) : _cn(cn), _len(length) {}; StunMsgChannel(uint8_t *buffer, size_t total_sz, size_t sz, bool constructed) : 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() {} uint16_t getChannelNumber() const { return _cn; } void setChannelNumber(uint16_t 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; uint16_t cn = 0; if(!stun_is_channel_message_str(_buffer,&_sz,&cn,0)) { return false; } if(_cn != cn) { return false; } return true; } private: uint16_t _cn; size_t _len; }; }; /* namespace */ #endif /* __LIB_TURN_MSG_CPP__ */ turnserver-4.5.2/src/apps/0000755000175000017500000000000013776656461014204 5ustar misimisiturnserver-4.5.2/src/apps/peer/0000755000175000017500000000000013776656461015137 5ustar misimisiturnserver-4.5.2/src/apps/peer/udpserver.h0000644000175000017500000000510113776656461017324 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.2/src/apps/peer/udpserver.c0000644000175000017500000001165113776656461017326 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*)malloc(sizeof(ioa_addr)); STRCPY(server->ifname,ifname); if(make_ioa_addr((const uint8_t*)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*)malloc(sizeof(server_type)); if(!server) return server; 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); free(server); } 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.2/src/apps/peer/mainudpserver.c0000644000175000017500000000616313776656461020175 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.2/src/apps/stunclient/0000755000175000017500000000000013776656461016374 5ustar misimisiturnserver-4.5.2/src/apps/stunclient/stunclient.c0000644000175000017500000003021213776656461020726 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 uint8_t*) 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((uint16_t)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; uint8_t *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; bzero(&remote_addr, sizeof(remote_addr)); if (make_ioa_addr((const uint8_t*) 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((uint8_t*) (buf.buf), (size_t*) &(buf.len), (uint16_t) response_port); } if (change_ip || change_port) { stun_attr_add_change_request_str((uint8_t*) buf.buf, (size_t*) &(buf.len), change_ip, change_port); } if (padding) { if(stun_attr_add_padding_str((uint8_t*) 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; uint8_t *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((uint8_t *) 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((uint8_t *) 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; uint8_t 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); 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 uint8_t*)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.2/src/apps/relay/0000755000175000017500000000000013776656461015320 5ustar misimisiturnserver-4.5.2/src/apps/relay/tls_listener.h0000644000175000017500000000440513776656461020203 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.2/src/apps/relay/ns_ioalib_impl.h0000644000175000017500000002110213776656461020445 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; char cmsg[TURN_CMSG_SZ+1]; int predef_timer_intervals[PREDEF_TIMERS_NUM]; struct timeval predef_timers[PREDEF_TIMERS_NUM]; /* Relays */ char 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; uint32_t 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; uint32_t 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; char* 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 char* relay_if, size_t relays_number, char **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 char* buffer, int len); int udp_recvfrom(evutil_socket_t fd, ioa_addr* orig_addr, const ioa_addr *like_addr, char* buffer, int buf_size, int *ttl, int *tos, char *ecmsg, int flags, uint32_t *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.2/src/apps/relay/libtelnet.h0000644000175000017500000005046013776656461017460 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.2/src/apps/relay/turn_admin_server.c0000644000175000017500000035067013776656461021225 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,(uint8_t*)s); else addr_to_string(value,(uint8_t*)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]),(uint8_t*)s); else addr_to_string(&(value->addrs[i]),(uint8_t*)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*)realloc(csarg->user_counters, csarg->users_number * sizeof(size_t)); csarg->user_names = (char**)realloc(csarg->user_names, csarg->users_number * sizeof(char*)); csarg->user_names[(size_t)value] = 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),(uint8_t*)tsi->local_addr_data.saddr); if(!tsi->remote_addr_data.saddr[0]) addr_to_string(&(tsi->remote_addr_data.addr),(uint8_t*)tsi->remote_addr_data.saddr); if(!tsi->relay_addr_data_ipv4.saddr[0]) addr_to_string(&(tsi->relay_addr_data_ipv4.addr),(uint8_t*)tsi->relay_addr_data_ipv4.saddr); if(!tsi->relay_addr_data_ipv6.saddr[0]) addr_to_string(&(tsi->relay_addr_data_ipv6.addr),(uint8_t*)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),(uint8_t*)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),(uint8_t*)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) free(arg.user_counters); 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; } free(cs); } } 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*)malloc(len+1); 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); } free(buf); } 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*)malloc(sizeof(struct cli_session)); 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); 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 uint8_t*)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,(uint8_t*)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 uint8_t*)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,(uint8_t*)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*)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); free(old); 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*)malloc(sizeof(struct turn_session_info)); turn_session_info_init(tsi); } else { turn_session_info_clean(tsi); } } if(tsi) { turn_session_info_clean(tsi); free(tsi); } } 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.no_software_attribute) { 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,(uint8_t*)s); else addr_to_string(value,(uint8_t*)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]),(uint8_t*)s); else addr_to_string(&(value->addrs[i]),(uint8_t*)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((uint8_t*)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((uint8_t*)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((uint8_t*)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),(uint8_t*)tsi->local_addr_data.saddr); if(!tsi->remote_addr_data.saddr[0]) addr_to_string(&(tsi->remote_addr_data.addr),(uint8_t*)tsi->remote_addr_data.saddr); if(!tsi->relay_addr_data_ipv4.saddr[0]) addr_to_string(&(tsi->relay_addr_data_ipv4.addr),(uint8_t*)tsi->relay_addr_data_ipv4.saddr); if(!tsi->relay_addr_data_ipv6.saddr[0]) addr_to_string(&(tsi->relay_addr_data_ipv6.addr),(uint8_t*)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),(uint8_t*)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),(uint8_t*)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((uint8_t*)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 uint8_t *add_user, const uint8_t *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((uint8_t*)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((uint8_t*)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 uint8_t*)kid,&key)<0) { str_buffer_append(sb,"data retrieval error"); } else { oauth_key_data okd; 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; 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 if (strcmp(kind, "allowed") != 0 && strcmp(kind, "denied") != 0) { //forbidden } else { uint8_t 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 if (strcmp(kind, "allowed") != 0 && strcmp(kind, "denied") != 0) { //forbidden } else { uint8_t 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*)malloc(sizeof(struct admin_session)); 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 uint8_t*)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 uint8_t*)uname,(uint8_t*)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 uint8_t *user = (const uint8_t*)get_http_header_value(hr, HR_DELETE_USER, NULL); if(user && user[0]) { const uint8_t *realm = (const uint8_t*)get_http_header_value(hr, HR_DELETE_REALM, ""); if(!is_superuser()) { realm = (const uint8_t*)current_realm(); } if(realm && realm[0]) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->del_user) { uint8_t u[STUN_MAX_USERNAME_SIZE+1]; uint8_t r[STUN_MAX_REALM_SIZE+1]; STRCPY(u,user); STRCPY(r,realm); dbd->del_user(u,r); } } } } const uint8_t *add_realm = (const uint8_t*)current_eff_realm(); const uint8_t *add_user = (const uint8_t*)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 uint8_t*)""; } if(add_user[0]) { add_realm = (const uint8_t*)get_http_header_value(hr, HR_ADD_REALM, current_realm()); if(!is_superuser()) { add_realm = (const uint8_t*)current_realm(); } if(!add_realm[0]) { add_realm=(const uint8_t*)current_eff_realm(); } if(!add_realm[0]) { add_realm = (const uint8_t*)get_realm(NULL)->options.name; } if(wrong_html_name((const char*)add_realm)) { msg = "Error: wrong realm name"; add_realm = (const uint8_t*)""; } if(add_realm[0]) { const uint8_t *pwd = (const uint8_t*)get_http_header_value(hr, HR_PASSWORD, NULL); const uint8_t *pwd1 = (const uint8_t*)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]; { uint8_t u[STUN_MAX_USERNAME_SIZE+1]; uint8_t r[STUN_MAX_REALM_SIZE+1]; uint8_t 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 uint8_t*)""; add_user=(const uint8_t*)""; } } 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 uint8_t *secret = (const uint8_t*)get_http_header_value(hr, HR_DELETE_SECRET, NULL); if(secret && secret[0]) { const uint8_t *realm = (const uint8_t*)get_http_header_value(hr, HR_DELETE_REALM, NULL); if(!is_superuser()) { realm = (const uint8_t*)current_realm(); } if(realm && realm[0]) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->del_secret) { uint8_t ss[AUTH_SECRET_SIZE+1]; uint8_t r[STUN_MAX_REALM_SIZE+1]; STRCPY(ss,secret); STRCPY(r,realm); dbd->del_secret(ss,r); } } } } const uint8_t *add_realm = (const uint8_t*)current_eff_realm(); const uint8_t *add_secret = (const uint8_t*)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 uint8_t*)""; } if(add_secret[0]) { add_realm = (const uint8_t*)get_http_header_value(hr, HR_ADD_REALM, current_realm()); if(!is_superuser()) { add_realm = (const uint8_t*)current_realm(); } if(!add_realm[0]) { add_realm=(const uint8_t*)current_eff_realm(); } if(!add_realm[0]) { add_realm = (const uint8_t*)get_realm(NULL)->options.name; } if(wrong_html_name((const char*)add_realm)) { msg = "Error: wrong realm name"; add_realm = (const uint8_t*)""; } if(add_realm[0]) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->set_secret) { uint8_t ss[AUTH_SECRET_SIZE+1]; uint8_t r[STUN_MAX_REALM_SIZE+1]; STRCPY(ss,add_secret); STRCPY(r,add_realm); (*dbd->set_secret)(ss, r); } add_secret=(const uint8_t*)""; add_realm=(const uint8_t*)""; } } 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 uint8_t *origin = (const uint8_t*)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) { uint8_t o[STUN_MAX_ORIGIN_SIZE+1]; STRCPY(o,origin); dbd->del_origin(o); uint8_t corigin[STUN_MAX_ORIGIN_SIZE+1]; get_canonic_origin((const char *)origin, (char *)corigin, sizeof(corigin)-1); dbd->del_origin(corigin); } } } const uint8_t *add_realm = (const uint8_t*)current_eff_realm(); const uint8_t *add_origin = (const uint8_t*)get_http_header_value(hr, HR_ADD_ORIGIN, ""); const char* msg = ""; uint8_t corigin[STUN_MAX_ORIGIN_SIZE+1]; get_canonic_origin((const char *)add_origin, (char *)corigin, sizeof(corigin)-1); if(corigin[0]) { add_realm = (const uint8_t*)get_http_header_value(hr, HR_ADD_REALM, current_realm()); if(!is_superuser()) { add_realm = (const uint8_t*)current_realm(); } if(!add_realm[0]) { add_realm=(const uint8_t*)current_eff_realm(); } if(!add_realm[0]) { add_realm = (const uint8_t*)get_realm(NULL)->options.name; } if(add_realm[0]) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->add_origin) { uint8_t o[STUN_MAX_ORIGIN_SIZE+1]; uint8_t r[STUN_MAX_REALM_SIZE+1]; STRCPY(o,corigin); STRCPY(r,add_realm); (*dbd->add_origin)(o, r); } add_origin=(const uint8_t*)""; add_realm=(const uint8_t*)""; } } 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 uint8_t*)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; bzero(&key,sizeof(key)); STRCPY(key.kid,add_kid); if(add_lt && add_lt[0]) { key.lifetime = (uint32_t)strtoul(add_lt,NULL,10); if(key.lifetime) { if(add_ts && add_ts[0]) { key.timestamp = (uint64_t)strtoull(add_ts,NULL,10); } if(!key.timestamp) { key.timestamp = (uint64_t)time(NULL); } } } else if(add_ts && add_ts[0]) { key.timestamp = (uint64_t)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.2/src/apps/relay/turn_ports.h0000644000175000017500000000540513776656461017714 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, uint16_t start, uint16_t end); void turnipports_add_ip(uint8_t transport, const ioa_addr *backend_addr); int turnipports_allocate(turnipports* tp, uint8_t transport, const ioa_addr *backend_addr); int turnipports_allocate_even(turnipports* tp, const ioa_addr *backend_addr, int allocate_rtcp, uint64_t *reservation_token); void turnipports_release(turnipports* tp, uint8_t transport, const ioa_addr *socket_addr); int turnipports_is_allocated(turnipports* tp, uint8_t transport, const ioa_addr *backend_addr, uint16_t port); int turnipports_is_available(turnipports* tp, uint8_t transport, const ioa_addr *backend_addr, uint16_t port); ////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_PORTS__ turnserver-4.5.2/src/apps/relay/mainrelay.h0000644000175000017500000002325713776656461017463 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 #if !defined(TURN_NO_SYSTEMD) #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 ((uint8_t)(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 no_software_attribute; int web_admin_listen_on_workers; int do_not_use_config_file; char pidfile[1025]; char acme_redirect[1025]; //////////////// Listener server ///////////////// int listener_port; int tls_listener_port; int alt_listener_port; int alt_tls_listener_port; int tcp_proxy_port; int rfc5780; int no_udp; int no_tcp; int tcp_use_proxy; 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 ///////////// uint16_t min_port; uint16_t 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; #if !defined(TURN_NO_PROMETHEUS) int prometheus; #endif /////// 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; int no_auth_pings; int no_dynamic_ip_list; int no_dynamic_realms; vint log_binding; } 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.2/src/apps/relay/dtls_listener.c0000644000175000017500000007067313776656461020354 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__); { uint8_t saddr[129]; uint8_t 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__); { uint8_t saddr[129]; uint8_t 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 && turn_params.log_binding) { uint8_t saddr[129]; uint8_t 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*) 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; } 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),(uint8_t*)sl); addr_to_string(&(server->sm.m.sm.nd.src_addr),(uint8_t*)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), (char*)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 char buffer[65535]; uint32_t 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,(uint8_t*)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,(uint8_t*)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 uint8_t*)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, (char*)ioa_network_buffer_data(nbh), (int)ioa_network_buffer_get_size(nbh)); } ////////////////////////////////////////////////////////////////// turnserver-4.5.2/src/apps/relay/acme.c0000644000175000017500000000561613776656461016401 0ustar misimisi /* * Copyright (C) 2020 Jens Elkner. All rights reserved. * * License: MIT - see https://opensource.org/licenses/MIT */ #include "acme.h" #include "ns_ioalib_impl.h" #define GET_ACME_PREFIX "GET /.well-known/acme-challenge/" #define GET_ACME_PREFIX_LEN 32 static int is_acme_req(char *req, size_t len) { static const char *A = " - 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ _ abcdefghijklmnopqrstuvwxyz "; int c, i, k; // Check first request line. Should be like: GET path HTTP/1.x if (strncmp(req, GET_ACME_PREFIX, GET_ACME_PREFIX_LEN)) return -1; // Usually (for LE) the "method path" is 32 + 43 = 55 chars. But other // implementations may choose longer pathes. We define PATHMAX = 127 chars // to be prepared for "DoS" attacks (STUN msg size max. is ~ 64K). len -= 21; // min size of trailing headers if (len > 131) len = 131; for (i=GET_ACME_PREFIX_LEN; i < (int) len; i++) { // find the end of the path if (req[i] != ' ') continue; // consider path < 10 chars invalid. Also we wanna see a "trailer". if (i < (GET_ACME_PREFIX_LEN + 10) || strncmp(req + i, " HTTP/1.", 8)) return -2; // finally check for allowed chars for (k=GET_ACME_PREFIX_LEN; k < i; k++) { c = req[k]; if ((c > 127) || (A[c] == ' ')) return -3; } // all checks passed: sufficient for us to answer with a redirect return i; } return -4; // end of path not found } int try_acme_redirect(char *req, size_t len, const char *url, ioa_socket_handle s) { static const char *HTML = "301 Moved Permanently\

301 Moved Permanently

"; char http_response[1024]; size_t plen, rlen; if (url == NULL || url[0] == '\0' || req == NULL || s == 0 ) return 1; if (len < (GET_ACME_PREFIX_LEN + 32) || len > (512 - GET_ACME_PREFIX_LEN) || (plen = is_acme_req(req, len)) < (GET_ACME_PREFIX_LEN + 1)) return 2; req[plen] = '\0'; snprintf(http_response, sizeof(http_response) - 1, "HTTP/1.1 301 Moved Permanently\r\n" "Content-Type: text/html\r\n" "Content-Length: %ld\r\n" "Connection: close\r\n" "Location: %s%s\r\n" "\r\n%s", strlen(HTML), url, req + GET_ACME_PREFIX_LEN, HTML); rlen = strlen(http_response); #ifdef LIBEV_OK ioa_network_buffer_handle nbh_acme = ioa_network_buffer_allocate(s->e); uint8_t *data = ioa_network_buffer_data(nbh_acme); bcopy(http_response, data, rlen); ioa_network_buffer_set_size(nbh_acme, rlen); send_data_from_ioa_socket_nbh(s, NULL, nbh_acme, TTL_IGNORE, TOS_IGNORE, NULL); #else if (write(s->fd, http_response, rlen) == -1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Sending redirect to '%s%s' failed",url, req + GET_ACME_PREFIX_LEN); } else if (((turn_turnserver *)s->session->server)->verbose) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "ACME redirected to %s%s\n", url, req + GET_ACME_PREFIX_LEN); } #endif req[plen] = ' '; return 0; } turnserver-4.5.2/src/apps/relay/dbdrivers/0000755000175000017500000000000013776656461017304 5ustar misimisiturnserver-4.5.2/src/apps/relay/dbdrivers/dbd_mongo.c0000644000175000017500000012045613776656461021410 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); free(info); } } 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 *) malloc(sizeof(MONGO)); bzero(mydbconnection, 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, uint8_t *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(uint8_t *usname, uint8_t *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]; 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 uint8_t *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; 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 = (uint64_t)bson_iter_int64(&iter); } if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "lifetime") && BSON_ITER_HOLDS_INT32(&iter)) { key->lifetime = (uint32_t)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(uint8_t *usname, uint8_t *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(uint8_t *usname, uint8_t *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 uint8_t *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(uint8_t *realm, secrets_list_t *users, secrets_list_t *realms) { const char * collection_name = "turnusers_lt"; mongoc_collection_t * collection = mongo_get_collection(collection_name); uint8_t 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)) { 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 = (uint64_t)bson_iter_int64(&iter); } if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "lifetime") && BSON_ITER_HOLDS_INT32(&iter)) { key->lifetime = (uint32_t)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(uint8_t *realm, secrets_list_t *secrets, secrets_list_t *realms) { mongoc_collection_t * collection = mongo_get_collection("turn_secret"); uint8_t 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(uint8_t *secret, uint8_t *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(uint8_t *secret, uint8_t *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, uint8_t *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; uint8_t 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(uint8_t *origin, uint8_t *realm) { mongoc_collection_t * collection = mongo_get_collection("realm"); if(!collection) return -1; int ret = -1; uint8_t 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(uint8_t *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(uint8_t *realm, secrets_list_t *origins, secrets_list_t *realms) { mongoc_collection_t * collection = mongo_get_collection("realm"); if(!collection) return -1; uint8_t 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(uint8_t *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 *)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); } free(_k); 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(uint8_t *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(free); 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 = 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 = strdup(bson_iter_utf8(&origin_iter, &length)); char *rval = strdup(_realm); ur_string_map_value_type value = (ur_string_map_value_type) (rval); ur_string_map_put(o_to_realm_new, (ur_string_map_key_type) _origin, value); free(_origin); } } } } 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); } } } } } free(_realm); } } 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 uint8_t *usname, uint8_t *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 uint8_t *usname, const uint8_t *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 uint8_t *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 void mongo_disconnect(void) { MONGO * mongoconnection = (MONGO *) pthread_getspecific(connection_key); if (mongoconnection) { MongoFree(mongoconnection); mongoconnection = NULL; } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MongoDB connection was closed.\n"); } ////////////////////////////////////////////////////////// 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, &mongo_disconnect }; 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.2/src/apps/relay/dbdrivers/dbd_pgsql.h0000644000175000017500000000345713776656461021425 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.2/src/apps/relay/dbdrivers/dbd_mysql.c0000644000175000017500000011422413776656461021432 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_mysql.h" #if !defined(TURN_NO_MYSQL) #include /////////////////////////////////////////////////////////////////////////////////////////////////////////// static int donot_print_connection_success = 0; struct _Myconninfo { char *host; char *dbname; char *user; char *password; unsigned int port; unsigned int connect_timeout; unsigned int read_timeout; /* SSL ==>> */ char *key; char *ca; char *cert; char *capath; char *cipher; /* <<== SSL : see http://dev.mysql.com/doc/refman/5.0/en/mysql-ssl-set.html */ }; typedef struct _Myconninfo Myconninfo; static void MyconninfoFree(Myconninfo *co) { if(co) { if(co->host) free(co->host); if(co->dbname) free(co->dbname); if(co->user) free(co->user); if(co->password) free(co->password); if(co->key) free(co->key); if(co->ca) free(co->ca); if(co->cert) free(co->cert); if(co->capath) free(co->capath); if(co->cipher) free(co->cipher); 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*)malloc(sizeof(Myconninfo)); bzero(co,sizeof(Myconninfo)); if(userdb) { char *s0=strdup(userdb); char *s = s0; while(s && *s) { while(*s && (*s==' ')) ++s; char *snext = strstr(s," "); if(snext) { *snext = 0; ++snext; } char* seq = strstr(s,"="); if(!seq) { MyconninfoFree(co); co = NULL; if(errmsg) { *errmsg = strdup(s); } break; } *seq = 0; if(!strcmp(s,"host")) co->host = strdup(seq+1); else if(!strcmp(s,"ip")) co->host = strdup(seq+1); else if(!strcmp(s,"addr")) co->host = strdup(seq+1); else if(!strcmp(s,"ipaddr")) co->host = strdup(seq+1); else if(!strcmp(s,"hostaddr")) co->host = strdup(seq+1); else if(!strcmp(s,"dbname")) co->dbname = strdup(seq+1); else if(!strcmp(s,"db")) co->dbname = strdup(seq+1); else if(!strcmp(s,"database")) co->dbname = strdup(seq+1); else if(!strcmp(s,"user")) co->user = strdup(seq+1); else if(!strcmp(s,"uname")) co->user = strdup(seq+1); else if(!strcmp(s,"name")) co->user = strdup(seq+1); else if(!strcmp(s,"username")) co->user = strdup(seq+1); else if(!strcmp(s,"password")) co->password = strdup(seq+1); else if(!strcmp(s,"pwd")) co->password = strdup(seq+1); else if(!strcmp(s,"passwd")) co->password = strdup(seq+1); else if(!strcmp(s,"secret")) co->password = strdup(seq+1); else if(!strcmp(s,"port")) co->port = (unsigned int)atoi(seq+1); else if(!strcmp(s,"p")) co->port = (unsigned int)atoi(seq+1); else if(!strcmp(s,"connect_timeout")) co->connect_timeout = (unsigned int)atoi(seq+1); else if(!strcmp(s,"timeout")) co->connect_timeout = (unsigned int)atoi(seq+1); else if(!strcmp(s,"read_timeout")) co->read_timeout = (unsigned int)atoi(seq+1); else if(!strcmp(s,"key")) co->key = strdup(seq+1); else if(!strcmp(s,"ssl-key")) co->key = strdup(seq+1); else if(!strcmp(s,"ca")) co->ca = strdup(seq+1); else if(!strcmp(s,"ssl-ca")) co->ca = strdup(seq+1); else if(!strcmp(s,"capath")) co->capath = strdup(seq+1); else if(!strcmp(s,"ssl-capath")) co->capath = strdup(seq+1); else if(!strcmp(s,"cert")) co->cert = strdup(seq+1); else if(!strcmp(s,"ssl-cert")) co->cert = strdup(seq+1); else if(!strcmp(s,"cipher")) co->cipher = strdup(seq+1); else if(!strcmp(s,"ssl-cipher")) co->cipher = strdup(seq+1); else { MyconninfoFree(co); co = NULL; if(errmsg) { *errmsg = strdup(s); } break; } s = snext; } free(s0); } if(co) { if(!(co->dbname)) co->dbname=strdup("0"); if(!(co->host)) co->host=strdup("127.0.0.1"); if(!(co->user)) co->user=strdup(""); if(!(co->password)) co->password=strdup(""); } return co; } static MYSQL *get_mydb_connection(void) { persistent_users_db_t *pud = get_persistent_users_db(); MYSQL *mydbconnection = (MYSQL*)pthread_getspecific(connection_key); if(mydbconnection) { if(mysql_ping(mydbconnection)) { mysql_close(mydbconnection); mydbconnection=NULL; (void) pthread_setspecific(connection_key, mydbconnection); } } if(!mydbconnection) { char *errmsg=NULL; Myconninfo *co=MyconninfoParse(pud->userdb, &errmsg); if(!co) { if(errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open MySQL DB connection <%s>, connection string format error: %s\n",pud->userdb,errmsg); free(errmsg); } 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); free(errmsg); MyconninfoFree(co); } else if(!(co->dbname)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "MySQL Database name is not provided: <%s>\n",pud->userdb); 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, uint8_t *realm) { int ret = -1; MYSQL * myc = get_mydb_connection(); if(myc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement)-1,"select value from turn_secret where realm='%s'",realm); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)==1) { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0]) { unsigned long *lengths = mysql_fetch_lengths(mres); if(lengths) { size_t sz = lengths[0]; char auth_secret[TURN_LONG_STRING_SIZE]; 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(uint8_t *usname, uint8_t *realm, hmackey_t key) { int ret = -1; MYSQL * myc = get_mydb_connection(); if(myc) { char statement[TURN_LONG_STRING_SIZE]; /* direct user input eliminated - there is no SQL injection problem (since version 4.4.5.3) */ snprintf(statement,sizeof(statement),"select hmackey from turnusers_lt where name='%s' and realm='%s'",usname,realm); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { MYSQL_ROW row = mysql_fetch_row(mres); if(row && row[0]) { unsigned long *lengths = mysql_fetch_lengths(mres); if(lengths) { size_t sz = get_hmackey_size(SHATYPE_DEFAULT)*2; if(lengths[0]kid,kid); bcopy(row[0],key->ikm_key,lengths[0]); key->ikm_key[lengths[0]]=0; char stimestamp[128]; bcopy(row[1],stimestamp,lengths[1]); stimestamp[lengths[1]]=0; key->timestamp = (uint64_t)strtoull(stimestamp,NULL,10); char slifetime[128]; bcopy(row[2],slifetime,lengths[2]); slifetime[lengths[2]]=0; key->lifetime = (uint32_t)strtoul(slifetime,NULL,10); bcopy(row[3],key->as_rs_alg,lengths[3]); key->as_rs_alg[lengths[3]]=0; 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) { bcopy(row[0],key->ikm_key,lengths[0]); key->ikm_key[lengths[0]]=0; char stimestamp[128]; bcopy(row[1],stimestamp,lengths[1]); stimestamp[lengths[1]]=0; key->timestamp = (uint64_t)strtoull(stimestamp,NULL,10); char slifetime[128]; bcopy(row[2],slifetime,lengths[2]); slifetime[lengths[2]]=0; key->lifetime = (uint32_t)strtoul(slifetime,NULL,10); bcopy(row[3],key->as_rs_alg,lengths[3]); key->as_rs_alg[lengths[3]]=0; bcopy(row[4],key->realm,lengths[4]); key->realm[lengths[4]]=0; 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(uint8_t *usname, uint8_t *realm, const char *key) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if(myc) { snprintf(statement,sizeof(statement),"insert into turnusers_lt (realm,name,hmackey) values('%s','%s','%s')",realm,usname,key); int res = mysql_query(myc, statement); if(!res) { ret = 0; } else { snprintf(statement,sizeof(statement),"update turnusers_lt set hmackey='%s' where name='%s' and realm='%s'",key,usname,realm); res = mysql_query(myc, statement); if(!res) { ret = 0; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user key information: %s\n",mysql_error(myc)); } } } return ret; } static int mysql_set_oauth_key(oauth_key_data_raw *key) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if(myc) { snprintf(statement,sizeof(statement),"insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg,realm) values('%s','%s',%llu,%lu,'%s','%s')", key->kid,key->ikm_key,(unsigned long long)key->timestamp,(unsigned long)key->lifetime, key->as_rs_alg,key->realm); int res = mysql_query(myc, statement); if(res) { snprintf(statement,sizeof(statement),"update oauth_key set ikm_key='%s',timestamp=%lu,lifetime=%lu, as_rs_alg='%s', realm='%s' where kid='%s'",key->ikm_key,(unsigned long)key->timestamp,(unsigned long)key->lifetime, key->as_rs_alg,key->realm,key->kid); res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating oauth key information: %s\n",mysql_error(myc)); } else { ret = 0; } } else { ret = 0; } } return ret; } static int mysql_del_user(uint8_t *usname, uint8_t *realm) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if(myc) { snprintf(statement,sizeof(statement),"delete from turnusers_lt where name='%s' and realm='%s'",usname,realm); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting user key information: %s\n",mysql_error(myc)); } else { ret = 0; } } return ret; } static int mysql_del_oauth_key(const uint8_t *kid) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if(myc) { snprintf(statement,sizeof(statement),"delete from oauth_key where kid = '%s'",(const char*)kid); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting oauth key information: %s\n",mysql_error(myc)); } else { ret = 0; } } return ret; } static int mysql_list_users(uint8_t *realm, secrets_list_t *users, secrets_list_t *realms) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; MYSQL * myc = get_mydb_connection(); if(myc) { if(realm[0]) { snprintf(statement,sizeof(statement),"select name, realm from turnusers_lt where realm='%s' order by name",realm); } else { snprintf(statement,sizeof(statement),"select name, realm from turnusers_lt order by realm,name"); } int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=2) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0]) { if(users) { add_to_secrets_list(users,row[0]); if(realms) { if(row[1]) { add_to_secrets_list(realms,row[1]); } else { add_to_secrets_list(realms,(char*)realm); } } } else { printf("%s[%s]\n", row[0], row[1]); } } } } ret = 0; } if(mres) mysql_free_result(mres); } } return ret; } static int mysql_list_secrets(uint8_t *realm, secrets_list_t *secrets, secrets_list_t *realms) { int ret = -1; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; char statement[TURN_LONG_STRING_SIZE]; if (realm[0]) { snprintf(statement, sizeof(statement), "select value,realm from turn_secret where realm='%s' order by value", realm); } else { snprintf(statement, sizeof(statement), "select value,realm from turn_secret order by realm,value"); } donot_print_connection_success=1; MYSQL * myc = get_mydb_connection(); if(myc) { int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=2) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { const char* kval = row[0]; if(kval) { const char* rval = row[1]; if(secrets) { add_to_secrets_list(secrets,kval); if(realms) { if(rval && *rval) { add_to_secrets_list(realms,rval); } else { add_to_secrets_list(realms,(char*)realm); } } } else { printf("%s[%s]\n",kval,rval); } } } } ret = 0; } if(mres) mysql_free_result(mres); } } return ret; } static int mysql_del_secret(uint8_t *secret, uint8_t *realm) { int ret = -1; donot_print_connection_success=1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if (myc) { if(!secret || (secret[0]==0)) snprintf(statement,sizeof(statement),"delete from turn_secret where realm='%s'",realm); else snprintf(statement,sizeof(statement),"delete from turn_secret where value='%s' and realm='%s'",secret,realm); mysql_query(myc, statement); ret = 0; } return ret; } static int mysql_set_secret(uint8_t *secret, uint8_t *realm) { int ret = -1; donot_print_connection_success = 1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if (myc) { snprintf(statement,sizeof(statement),"insert into turn_secret (realm,value) values('%s','%s')",realm,secret); int res = mysql_query(myc, statement); if (res) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "Error inserting/updating secret key information: %s\n", mysql_error(myc)); } else { ret = 0; } } return ret; } static int mysql_set_permission_ip(const char *kind, uint8_t *realm, const char* ip, int del) { int ret = -1; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; donot_print_connection_success = 1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if (myc) { if(del) { snprintf(statement, sizeof(statement), "delete from %s_peer_ip where realm = '%s' and ip_range = '%s'", kind, (char*)realm, ip); } else { snprintf(statement, sizeof(statement), "insert into %s_peer_ip (realm,ip_range) values('%s','%s')", kind, (char*)realm, ip); } int res = mysql_query(myc, statement); if (res) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "Error inserting permission ip information: %s\n", mysql_error(myc)); } else { ret = 0; } } return ret; } static int mysql_add_origin(uint8_t *origin, uint8_t *realm) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if (myc) { snprintf(statement,sizeof(statement),"insert into turn_origin_to_realm (origin,realm) values('%s','%s')",origin,realm); int res = mysql_query(myc, statement); if (res) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "Error inserting origin information: %s\n", mysql_error(myc)); } else { ret = 0; } } return ret; } static int mysql_del_origin(uint8_t *origin) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if (myc) { snprintf(statement,sizeof(statement),"delete from turn_origin_to_realm where origin='%s'",origin); int res = mysql_query(myc, statement); if (res) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "Error deleting origin information: %s\n", mysql_error(myc)); } else { ret = 0; } } return ret; } static int mysql_list_origins(uint8_t *realm, secrets_list_t *origins, secrets_list_t *realms) { int ret = -1; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; donot_print_connection_success = 1; MYSQL * myc = get_mydb_connection(); if(myc) { char statement[TURN_LONG_STRING_SIZE]; if(realm && realm[0]) { snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm where realm='%s' order by origin",realm); } else { snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm order by realm,origin"); } int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=2) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0] && row[1]) { const char* kval = row[0]; const char* rval = row[1]; if(origins) { add_to_secrets_list(origins,kval); if(realms) { if(rval && *rval) { add_to_secrets_list(realms,rval); } else { add_to_secrets_list(realms,(char*)realm); } } } else { printf("%s ==>> %s\n",kval,rval); } } } } ret = 0; } if(mres) mysql_free_result(mres); } } return ret; } static int mysql_set_realm_option_one(uint8_t *realm, unsigned long value, const char* opt) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if (myc) { { snprintf(statement,sizeof(statement),"delete from turn_realm_option where realm='%s' and opt='%s'",realm,opt); mysql_query(myc, statement); } if(value>0) { snprintf(statement,sizeof(statement),"insert into turn_realm_option (realm,opt,value) values('%s','%s','%lu')",realm,opt,(unsigned long)value); int res = mysql_query(myc, statement); if (res) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "Error inserting realm option information: %s\n", mysql_error(myc)); } else { ret = 0; } } } return ret; } static int mysql_list_realm_options(uint8_t *realm) { int ret = -1; donot_print_connection_success = 1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if(myc) { if(realm && realm[0]) { snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option where realm='%s' order by realm,opt",realm); } else { snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option order by realm,opt"); } int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=3) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0] && row[1] && row[2]) { printf("%s[%s]=%s\n",row[1],row[0],row[2]); } } } ret = 0; } if(mres) mysql_free_result(mres); } } return ret; } static void mysql_auth_ping(void * rch) { UNUSED_ARG(rch); MYSQL * myc = get_mydb_connection(); if(myc) { char statement[TURN_LONG_STRING_SIZE]; STRCPY(statement,"select value from turn_secret"); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { mysql_free_result(mres); } } } } static int mysql_get_ip_list(const char *kind, ip_range_list_t * list) { int ret = -1; MYSQL * myc = get_mydb_connection(); if(myc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement),"select ip_range,realm from %s_peer_ip",kind); int res = mysql_query(myc, statement); if(res) { static int wrong_table_reported = 0; if(!wrong_table_reported) { wrong_table_reported = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information; probably, the tables 'allowed_peer_ip' and/or 'denied_peer_ip' have to be upgraded to include the realm column.\n"); } snprintf(statement, sizeof(statement), "select ip_range,'' from %s_peer_ip", kind); res = mysql_query(myc, statement); } if(res == 0) { MYSQL_RES *mres = mysql_store_result(myc); if(mres && mysql_field_count(myc)==2) { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0]) { unsigned long *lengths = mysql_fetch_lengths(mres); if(lengths) { size_t sz = lengths[0]; char kval[TURN_LONG_STRING_SIZE]; bcopy(row[0],kval,sz); kval[sz]=0; sz = lengths[1]; char rval[TURN_LONG_STRING_SIZE]; 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(free); for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0] && row[1]) { unsigned long *lengths = mysql_fetch_lengths(mres); if(lengths) { size_t sz = lengths[0]; char oval[513]; bcopy(row[0],oval,sz); oval[sz]=0; char *rval=strdup(row[1]); get_realm(rval); ur_string_map_value_type value = (ur_string_map_value_type)rval; ur_string_map_put(o_to_realm_new, (ur_string_map_key_type) oval, value); } } } } update_o_to_realm(o_to_realm_new); } if(mres) mysql_free_result(mres); } } { size_t i = 0; size_t rlsz = 0; lock_realms(); rlsz = realms_list->sz; unlock_realms(); for (i = 0; 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]; bcopy(row[0],rval,sz); rval[sz]=0; char oval[513]; sz = lengths[1]; bcopy(row[1],oval,sz); oval[sz]=0; char vval[513]; sz = lengths[2]; 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 uint8_t *usname, uint8_t *realm, password_t pwd) { int ret = -1; realm[0]=0; pwd[0]=0; MYSQL * myc = get_mydb_connection(); if(myc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement),"select realm,password from admin_user where name='%s'",usname); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=2) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { MYSQL_ROW row = mysql_fetch_row(mres); if(row && row[0]) { strncpy((char*)realm,row[0],STUN_MAX_REALM_SIZE); strncpy((char*)pwd,row[1],STUN_MAX_PWD_SIZE); ret = 0; } } if(mres) mysql_free_result(mres); } } return ret; } static int mysql_set_admin_user(const uint8_t *usname, const uint8_t *realm, const password_t pwd) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; donot_print_connection_success=1; MYSQL * myc = get_mydb_connection(); if(myc) { snprintf(statement,sizeof(statement),"insert into admin_user (realm,name,password) values('%s','%s','%s')",realm,usname,pwd); int res = mysql_query(myc, statement); if(!res) { ret = 0; } else { snprintf(statement,sizeof(statement),"update admin_user set realm='%s',password='%s' where name='%s'",realm,pwd,usname); res = mysql_query(myc, statement); if(!res) { ret = 0; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user key information: %s\n",mysql_error(myc)); } } } return ret; } static int mysql_del_admin_user(const uint8_t *usname) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; donot_print_connection_success=1; MYSQL * myc = get_mydb_connection(); if(myc) { snprintf(statement,sizeof(statement),"delete from admin_user where name='%s'",usname); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting admin user information: %s\n",mysql_error(myc)); } else { ret = 0; } } return ret; } static int mysql_list_admin_users(int no_print) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; donot_print_connection_success=1; MYSQL * myc = get_mydb_connection(); if(myc) { snprintf(statement,sizeof(statement),"select name, realm from admin_user order by realm,name"); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=2) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { ret = 0; for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { ++ret; if(row[0] && !no_print) { if(row[1] && row[1][0]) { printf("%s[%s]\n",row[0],row[1]); } else { printf("%s\n",row[0]); } } } } } if(mres) mysql_free_result(mres); } } return ret; } static void mysql_disconnect(void) { MYSQL *mydbconnection = (MYSQL*)pthread_getspecific(connection_key); if (mydbconnection) { mysql_close(mydbconnection); mydbconnection=NULL; } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MySQL connection was closed.\n"); } ////////////////////////////////////////////////////// static const turn_dbdriver_t driver = { &mysql_get_auth_secrets, &mysql_get_user_key, &mysql_set_user_key, &mysql_del_user, &mysql_list_users, &mysql_list_secrets, &mysql_del_secret, &mysql_set_secret, &mysql_add_origin, &mysql_del_origin, &mysql_list_origins, &mysql_set_realm_option_one, &mysql_list_realm_options, &mysql_auth_ping, &mysql_get_ip_list, &mysql_set_permission_ip, &mysql_reread_realms, &mysql_set_oauth_key, &mysql_get_oauth_key, &mysql_del_oauth_key, &mysql_list_oauth_keys, &mysql_get_admin_user, &mysql_set_admin_user, &mysql_del_admin_user, &mysql_list_admin_users, &mysql_disconnect }; const turn_dbdriver_t * get_mysql_dbdriver(void) { return &driver; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// #else const turn_dbdriver_t * get_mysql_dbdriver(void) { return NULL; } #endif turnserver-4.5.2/src/apps/relay/dbdrivers/dbdriver.h0000644000175000017500000000755113776656461021266 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, uint8_t *realm); int (*get_user_key)(uint8_t *usname, uint8_t *realm, hmackey_t key); int (*set_user_key)(uint8_t *usname, uint8_t *realm, const char *key); int (*del_user)(uint8_t *usname, uint8_t *realm); int (*list_users)(uint8_t *realm, secrets_list_t *users, secrets_list_t *realms); int (*list_secrets)(uint8_t *realm, secrets_list_t *secrets, secrets_list_t *realms); int (*del_secret)(uint8_t *secret, uint8_t *realm); int (*set_secret)(uint8_t *secret, uint8_t *realm); int (*add_origin)(uint8_t *origin, uint8_t *realm); int (*del_origin)(uint8_t *origin); int (*list_origins)(uint8_t *realm, secrets_list_t *origins, secrets_list_t *realms); int (*set_realm_option_one)(uint8_t *realm, unsigned long value, const char* opt); int (*list_realm_options)(uint8_t *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, uint8_t *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 uint8_t *kid, oauth_key_data_raw *key); int (*del_oauth_key)(const uint8_t *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 uint8_t *usname, uint8_t *realm, password_t pwd); int (*set_admin_user)(const uint8_t *usname, const uint8_t *realm, const password_t pwd); int (*del_admin_user)(const uint8_t *usname); int (*list_admin_users)(int no_print); void (*disconnect)(void); } 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.2/src/apps/relay/dbdrivers/dbd_sqlite.h0000644000175000017500000000344413776656461021574 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_SQLITE__ #define __DBD_SQLITE__ #include "dbdriver.h" #ifdef __cplusplus extern "C" { #endif const turn_dbdriver_t * get_sqlite_dbdriver(void); #ifdef __cplusplus } #endif #endif /// __DBD_SQLITE__/// turnserver-4.5.2/src/apps/relay/dbdrivers/dbdriver.c0000644000175000017500000000634013776656461021254 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 "apputils.h" #include "dbdriver.h" #include "dbd_sqlite.h" #include "dbd_pgsql.h" #include "dbd_mysql.h" #include "dbd_mongo.h" #include "dbd_redis.h" static void make_connection_key(void) { (void) pthread_key_create(&connection_key, NULL); } pthread_key_t connection_key; pthread_once_t connection_key_once = PTHREAD_ONCE_INIT; int convert_string_key_to_binary(char* keysource, hmackey_t key, size_t sz) { char is[3]; size_t i; unsigned int v; is[2]=0; for(i=0;i /////////////////////////////////////////////////////////////////////////////////////////////////////////// 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); free(errmsg); } 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) free(errmsg); pqdbconnection = PQconnectdb(pud->userdb); if(!pqdbconnection) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection: <%s>, runtime error\n",pud->userdb); } 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, uint8_t *realm) { int ret = -1; PGconn * pqc = get_pqdb_connection(); if(pqc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement)-1,"select value from turn_secret where realm='%s'",realm); PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc)); } else { int i = 0; for(i=0;iikm_key,PQgetvalue(res,0,0)); key->timestamp = (uint64_t)strtoll(PQgetvalue(res,0,1),NULL,10); key->lifetime = (uint32_t)strtol(PQgetvalue(res,0,2),NULL,10); STRCPY(key->as_rs_alg,PQgetvalue(res,0,3)); STRCPY(key->realm,PQgetvalue(res,0,4)); STRCPY(key->kid,kid); ret = 0; } if(res) { PQclear(res); } } return ret; } static int pgsql_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts,secrets_list_t *realms) { oauth_key_data_raw key_; oauth_key_data_raw *key=&key_; int ret = -1; char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,as_rs_alg,realm,kid from oauth_key order by kid"); PGconn * pqc = get_pqdb_connection(); if(pqc) { PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc)); } else { int i = 0; for(i=0;iikm_key,PQgetvalue(res,i,0)); key->timestamp = (uint64_t)strtoll(PQgetvalue(res,i,1),NULL,10); key->lifetime = (uint32_t)strtol(PQgetvalue(res,i,2),NULL,10); STRCPY(key->as_rs_alg,PQgetvalue(res,i,3)); STRCPY(key->realm,PQgetvalue(res,i,4)); STRCPY(key->kid,PQgetvalue(res,i,5)); if(kids) { add_to_secrets_list(kids,key->kid); add_to_secrets_list(teas,key->as_rs_alg); add_to_secrets_list(realms,key->realm); { char ts[256]; snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp); add_to_secrets_list(tss,ts); } { char lt[256]; snprintf(lt,sizeof(lt)-1,"%lu",(unsigned long)key->lifetime); add_to_secrets_list(lts,lt); } } else { printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s, realm=%s\n", key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->as_rs_alg,key->realm); } ret = 0; } } if(res) { PQclear(res); } } return ret; } static int pgsql_set_user_key(uint8_t *usname, uint8_t *realm, const char *key) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if(pqc) { snprintf(statement,sizeof(statement),"insert into turnusers_lt (realm,name,hmackey) values('%s','%s','%s')",realm,usname,key); PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { if(res) { PQclear(res); } snprintf(statement,sizeof(statement),"update turnusers_lt set hmackey='%s' where name='%s' and realm='%s'",key,usname,realm); res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user information: %s\n",PQerrorMessage(pqc)); } else { ret = 0; } } if(res) { PQclear(res); } } return ret; } static int pgsql_set_oauth_key(oauth_key_data_raw *key) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if(pqc) { snprintf(statement,sizeof(statement),"insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg,realm) values('%s','%s',%llu,%lu,'%s','%s')", key->kid,key->ikm_key,(unsigned long long)key->timestamp,(unsigned long)key->lifetime, key->as_rs_alg,key->realm); PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { if(res) { PQclear(res); } snprintf(statement,sizeof(statement),"update oauth_key set ikm_key='%s',timestamp=%lu,lifetime=%lu, as_rs_alg='%s', realm='%s' where kid='%s'",key->ikm_key,(unsigned long)key->timestamp,(unsigned long)key->lifetime, key->as_rs_alg,key->realm,key->kid); res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating oauth_key information: %s\n",PQerrorMessage(pqc)); } else { ret = 0; } } else { ret = 0; } if(res) { PQclear(res); } } return ret; } static int pgsql_del_user(uint8_t *usname, uint8_t *realm) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if(pqc) { snprintf(statement,sizeof(statement),"delete from turnusers_lt where name='%s' and realm='%s'",usname,realm); PGresult *res = PQexec(pqc, statement); if(res) { PQclear(res); ret = 0; } } return ret; } static int pgsql_del_oauth_key(const uint8_t *kid) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if(pqc) { snprintf(statement,sizeof(statement),"delete from oauth_key where kid = '%s'",(const char*)kid); PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting oauth_key information: %s\n",PQerrorMessage(pqc)); } else { ret = 0; } if(res) { PQclear(res); } } return ret; } static int pgsql_list_users(uint8_t *realm, secrets_list_t *users, secrets_list_t *realms) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; PGconn *pqc = get_pqdb_connection(); if(pqc) { if(realm[0]) { snprintf(statement,sizeof(statement),"select name,realm from turnusers_lt where realm='%s' order by name",realm); } else { snprintf(statement,sizeof(statement),"select name,realm from turnusers_lt order by realm,name"); } PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc)); } else { int i = 0; for(i=0;i> %s\n",kval,rval); } } } } ret = 0; } if(res) { PQclear(res); } } return ret; } static int pgsql_set_realm_option_one(uint8_t *realm, unsigned long value, const char* opt) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if(pqc) { { snprintf(statement,sizeof(statement),"delete from turn_realm_option where realm='%s' and opt='%s'",realm,opt); PGresult *res = PQexec(pqc, statement); if(res) { PQclear(res); } } if(value>0) { snprintf(statement,sizeof(statement),"insert into turn_realm_option (realm,opt,value) values('%s','%s','%lu')",realm,opt,(unsigned long)value); PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting realm option information: %s\n",PQerrorMessage(pqc)); } else { ret = 0; } if(res) { PQclear(res); } } } return ret; } static int pgsql_list_realm_options(uint8_t *realm) { int ret = -1; donot_print_connection_success = 1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if(pqc) { if(realm && realm[0]) { snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option where realm='%s' order by realm,opt",realm); } else { snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option order by realm,opt"); } PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc)); } else { int i = 0; for(i=0;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 uint8_t *usname, uint8_t *realm, password_t pwd) { int ret = -1; realm[0]=0; pwd[0]=0; PGconn * pqc = get_pqdb_connection(); if(pqc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement),"select realm,password from admin_user where name='%s'",usname); PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res)!=1)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc)); } else { const char *kval = PQgetvalue(res,0,0); if(kval) { strncpy((char*)realm,kval,STUN_MAX_REALM_SIZE); } kval = (const char*) PQgetvalue(res,0,1); if(kval) { strncpy((char*)pwd,kval,STUN_MAX_PWD_SIZE); } ret = 0; } if(res) PQclear(res); } return ret; } static int pgsql_set_admin_user(const uint8_t *usname, const uint8_t *realm, const password_t pwd) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; donot_print_connection_success=1; PGconn *pqc = get_pqdb_connection(); if(pqc) { snprintf(statement,sizeof(statement),"insert into admin_user (realm,name,password) values('%s','%s','%s')",realm,usname,pwd); PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { if(res) { PQclear(res); } snprintf(statement,sizeof(statement),"update admin_user set password='%s',realm='%s' where name='%s'",pwd,realm,usname); res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user information: %s\n",PQerrorMessage(pqc)); } else { ret = 0; } } if(res) { PQclear(res); } } return ret; } static int pgsql_del_admin_user(const uint8_t *usname) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; donot_print_connection_success=1; PGconn *pqc = get_pqdb_connection(); if(pqc) { snprintf(statement,sizeof(statement),"delete from admin_user where name='%s'",usname); PGresult *res = PQexec(pqc, statement); if(res) { PQclear(res); ret = 0; } } return ret; } static int pgsql_list_admin_users(int no_print) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; donot_print_connection_success=1; PGconn *pqc = get_pqdb_connection(); if(pqc) { snprintf(statement,sizeof(statement),"select name,realm,password from admin_user order by realm,name"); } PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc)); } else { int i = 0; ret = 0; for(i=0;i /////////////////////////////////////////////////////////////////////////////////////////////////////////// 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) free(co->host); if(co->dbname) free(co->dbname); if(co->password) free(co->password); bzero(co,sizeof(Ryconninfo)); } } static Ryconninfo *RyconninfoParse(const char *userdb, char **errmsg) { Ryconninfo *co = (Ryconninfo*) malloc(sizeof(Ryconninfo)); bzero(co,sizeof(Ryconninfo)); if (userdb) { char *s0 = strdup(userdb); char *s = s0; while (s && *s) { while (*s && (*s == ' ')) ++s; char *snext = strstr(s, " "); if (snext) { *snext = 0; ++snext; } char* seq = strstr(s, "="); if (!seq) { RyconninfoFree(co); co = NULL; if (errmsg) { *errmsg = strdup(s); } break; } *seq = 0; if (!strcmp(s, "host")) co->host = strdup(seq + 1); else if (!strcmp(s, "ip")) co->host = strdup(seq + 1); else if (!strcmp(s, "addr")) co->host = strdup(seq + 1); else if (!strcmp(s, "ipaddr")) co->host = strdup(seq + 1); else if (!strcmp(s, "hostaddr")) co->host = strdup(seq + 1); else if (!strcmp(s, "dbname")) co->dbname = strdup(seq + 1); else if (!strcmp(s, "db")) co->dbname = strdup(seq + 1); else if (!strcmp(s, "database")) co->dbname = strdup(seq + 1); else if (!strcmp(s, "user")) ; else if (!strcmp(s, "uname")) ; else if (!strcmp(s, "name")) ; else if (!strcmp(s, "username")) ; else if (!strcmp(s, "password")) co->password = strdup(seq + 1); else if (!strcmp(s, "pwd")) co->password = strdup(seq + 1); else if (!strcmp(s, "passwd")) co->password = strdup(seq + 1); else if (!strcmp(s, "secret")) co->password = strdup(seq + 1); else if (!strcmp(s, "port")) co->port = (unsigned int) atoi(seq + 1); else if (!strcmp(s, "p")) co->port = (unsigned int) atoi(seq + 1); else if (!strcmp(s, "connect_timeout")) co->connect_timeout = (unsigned int) atoi(seq + 1); else if (!strcmp(s, "timeout")) co->connect_timeout = (unsigned int) atoi(seq + 1); else { RyconninfoFree(co); co = NULL; if (errmsg) { *errmsg = strdup(s); } break; } s = snext; } free(s0); } if(co) { if(!(co->dbname)) co->dbname=strdup("0"); if(!(co->host)) co->host=strdup("127.0.0.1"); if(!(co->password)) co->password=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); free(errmsg); } 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); free(errmsg); 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); free(errmsg); } 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); free(errmsg); 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, uint8_t *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(uint8_t *usname, uint8_t *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 uint8_t *kid, oauth_key_data_raw *key) { int ret = -1; redisContext * rc = get_redis_connection(); if(rc) { char s[TURN_LONG_STRING_SIZE]; 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 = (uint64_t)strtoull(val,NULL,10); } else if(!strcmp(kw,"lifetime")) { key->lifetime = (uint32_t)strtoul(val,NULL,10); } } } ret = 0; } turnFreeRedisReply(reply); } } return ret; } static int redis_set_user_key(uint8_t *usname, uint8_t *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(uint8_t *usname, uint8_t *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 uint8_t *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(uint8_t *realm, secrets_list_t *users, secrets_list_t *realms) { int ret = -1; redisContext *rc = get_redis_connection(); uint8_t 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(uint8_t *realm, secrets_list_t *secrets, secrets_list_t *realms) { int ret = -1; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; 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(uint8_t *secret, uint8_t *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(uint8_t *secret, uint8_t *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, uint8_t *realm, const char* ip, int del) { int ret = -1; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; donot_print_connection_success = 1; 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(uint8_t *origin, uint8_t *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(uint8_t *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(uint8_t *realm, secrets_list_t *origins, secrets_list_t *realms) { int ret = -1; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; donot_print_connection_success = 1; 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(uint8_t *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(uint8_t *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(free); 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 = strdup(rget->str); ur_string_map_put(o_to_realm_new, (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 uint8_t *usname, uint8_t *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 uint8_t *usname, const uint8_t *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 uint8_t *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 #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*)malloc(sz); strncpy(dir_fixed,home,szh); strncpy(dir_fixed+szh,dir+1,(sz-szh-1)); strncpy(dir0,dir_fixed,sz); free(dir_fixed); } } 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, uint8_t *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(uint8_t *usname, uint8_t *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 = 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; } free(kval); } } 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 uint8_t *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 = (uint64_t)strtoll((const char*)sqlite3_column_text(st, 1),NULL,10); key->lifetime = (uint32_t)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 = (uint64_t)strtoll((const char*)sqlite3_column_text(st, 1),NULL,10); key->lifetime = (uint32_t)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(uint8_t *usname, uint8_t *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(uint8_t *usname, uint8_t *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 uint8_t *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(uint8_t *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; uint8_t 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(uint8_t *realm, secrets_list_t *secrets, secrets_list_t *realms) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; 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(uint8_t *secret, uint8_t *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(uint8_t *secret, uint8_t *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(uint8_t *origin, uint8_t *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(uint8_t *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(uint8_t *realm, secrets_list_t *origins, secrets_list_t *realms) { int ret = -1; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; donot_print_connection_success = 1; 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(uint8_t *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(uint8_t *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, uint8_t *realm, const char* ip, int del) { int ret = -1; uint8_t 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(free); while (1) { int res = sqlite3_step(st); if (res == SQLITE_ROW) { char* oval = strdup((const char*) sqlite3_column_text(st, 0)); char* rval = 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, (ur_string_map_key_type) oval, value); free(oval); } 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 = 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); } free(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); 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 uint8_t *usname, uint8_t *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 uint8_t *usname, const uint8_t *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 uint8_t *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 void sqlite_disconnect(void) { sqlite3 *sqliteconnection = (sqlite3 *)pthread_getspecific(connection_key); if (sqliteconnection) { sqlite3_close(sqliteconnection); sqliteconnection=NULL; } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SQLite connection was closed.\n"); } /////////////////////////////////////////////////////// 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, &sqlite_disconnect }; ////////////////////////////////////////////////// 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.2/src/apps/relay/dbdrivers/dbd_mysql.h0000644000175000017500000000344013776656461021434 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_MYSQL__ #define __DBD_MYSQL__ #include "dbdriver.h" #ifdef __cplusplus extern "C" { #endif const turn_dbdriver_t * get_mysql_dbdriver(void); #ifdef __cplusplus } #endif #endif /// __DBD_MYSQL__/// turnserver-4.5.2/src/apps/relay/dbdrivers/dbd_redis.h0000644000175000017500000000344013776656461021375 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.2/src/apps/relay/turn_ports.c0000644000175000017500000002730213776656461017707 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 ((uint32_t)(-1)) #define TPS_TAKEN_SINGLE ((uint32_t)(-2)) #define TPS_TAKEN_EVEN ((uint32_t)(-3)) #define TPS_TAKEN_ODD ((uint32_t)(-4)) struct _turnports { uint32_t status[PORTS_SIZE]; uint32_t low; uint32_t high; uint16_t range_start; uint16_t range_stop; uint16_t ports[PORTS_SIZE]; TURN_MUTEX_DECLARE(mutex) }; typedef struct _turnports turnports; /////////////// TURNPORTS statics ////////////////////////// static turnports* turnports_create(super_memory_t *sm, uint16_t start, uint16_t end); static uint16_t turnports_size(turnports* tp); static int turnports_allocate(turnports* tp); static int turnports_allocate_even(turnports* tp, int allocate_rtcp, uint64_t *reservation_token); static void turnports_release(turnports* tp, uint16_t port); static int turnports_is_allocated(turnports* tp, uint16_t port); static int turnports_is_available(turnports* tp, uint16_t port); /////////////// UTILS ////////////////////////////////////// static int is_taken(uint32_t 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 + (uint16_t)(((unsigned long)random())%((unsigned long)size))); uint16_t port2 = (uint16_t)(tp->low + (uint16_t)(((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]=(uint32_t)tmp; tmp=(int)tp->ports[pos1]; tp->ports[pos1]=tp->ports[pos2]; tp->ports[pos2]=(uint16_t)tmp; } } } } static void turnports_init(turnports* tp, uint16_t start, uint16_t end) { tp->low=start; tp->high=((uint32_t)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]=(uint16_t)i; } for(i=start;i<=end;i++) { tp->status[i]=(uint32_t)i; tp->ports[i]=(uint16_t)i; } for(i=((int)end)+1;istatus[i]=TPS_OUT_OF_RANGE; tp->ports[i]=(uint16_t)i; } turnports_randomize(tp); TURN_MUTEX_INIT_RECURSIVE(&(tp->mutex)); } /////////////// FUNC /////////////////////////////////////// turnports* turnports_create(super_memory_t *sm, uint16_t start, uint16_t end) { if(start>end) return NULL; turnports* ret=(turnports*)allocate_super_memory_region(sm, sizeof(turnports)); turnports_init(ret,start,end); return ret; } uint16_t turnports_size(turnports* tp) { if(!tp) return 0; else { TURN_MUTEX_LOCK(&tp->mutex); uint16_t ret = (uint16_t)((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=(uint16_t)(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, uint16_t port) { TURN_MUTEX_LOCK(&tp->mutex); if(tp && port>=tp->range_start && port<=tp->range_stop) { uint16_t position=(uint16_t)(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, uint64_t *reservation_token) { if(tp) { TURN_MUTEX_LOCK(&tp->mutex); uint16_t size = turnports_size(tp); if(size>1) { uint16_t 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) { uint16_t *v16=(uint16_t*)reservation_token; uint32_t *v32=(uint32_t*)reservation_token; v16[0]=(uint16_t)(tp->ports[(uint16_t)(tp->low & 0x0000FFFF)]); v16[1]=(uint16_t)(tp->ports[(uint16_t)(tp->high & 0x0000FFFF)]); v32[1]=(uint32_t)turn_random(); } TURN_MUTEX_UNLOCK(&tp->mutex); return port; } } } } } TURN_MUTEX_UNLOCK(&tp->mutex); } return -1; } int turnports_is_allocated(turnports* tp, uint16_t 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, uint16_t port) { if(tp) { TURN_MUTEX_LOCK(&tp->mutex); uint32_t status = tp->status[port]; if((status!=TPS_OUT_OF_RANGE) && !is_taken(status)) { uint16_t position=(uint16_t)(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; uint16_t start; uint16_t 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, uint8_t 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, uint16_t start, uint16_t 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, uint8_t 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(uint8_t transport, const ioa_addr *backend_addr) { turnipports_add(turnipports_singleton, transport, backend_addr); } int turnipports_allocate(turnipports* tp, uint8_t 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, uint64_t *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, uint8_t 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, uint8_t transport, const ioa_addr *backend_addr, uint16_t 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, uint8_t transport, const ioa_addr *backend_addr, uint16_t 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.2/src/apps/relay/prom_server.h0000644000175000017500000000322513776656461020036 0ustar misimisi #ifndef __PROM_SERVER_H__ #define __PROM_SERVER_H__ #if !defined(TURN_NO_PROMETHEUS) #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #include #include #include #ifdef __cplusplus } #endif /* __clplusplus */ #define DEFAULT_PROM_SERVER_PORT (9641) extern prom_counter_t *turn_new_allocation; extern prom_counter_t *turn_refreshed_allocation; extern prom_counter_t *turn_deleted_allocation; extern prom_counter_t *turn_traffic_rcvp; extern prom_counter_t *turn_traffic_rcvb; extern prom_counter_t *turn_traffic_sentp; extern prom_counter_t *turn_traffic_sentb; extern prom_counter_t *turn_traffic_peer_rcvp; extern prom_counter_t *turn_traffic_peer_rcvb; extern prom_counter_t *turn_traffic_peer_sentp; extern prom_counter_t *turn_traffic_peer_sentb; extern prom_counter_t *turn_total_traffic_rcvp; extern prom_counter_t *turn_total_traffic_rcvb; extern prom_counter_t *turn_total_traffic_sentp; extern prom_counter_t *turn_total_traffic_sentb; extern prom_counter_t *turn_total_traffic_peer_rcvp; extern prom_counter_t *turn_total_traffic_peer_rcvb; extern prom_counter_t *turn_total_traffic_peer_sentp; extern prom_counter_t *turn_total_traffic_peer_sentb; #define TURN_ALLOC_STR_MAX_SIZE (20) #ifdef __cplusplus extern "C" { #endif int start_prometheus_server(void); void prom_set_finished_traffic(const char* realm, const char* user, unsigned long rsvp, unsigned long rsvb, unsigned long sentp, unsigned long sentb, bool peer); #endif /* TURN_NO_PROMETHEUS */ #ifdef __cplusplus } #endif /* __clplusplus */ #endif /* __PROM_SERVER_H__ */turnserver-4.5.2/src/apps/relay/mainrelay.c0000644000175000017500000032572413776656461017462 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" #include "dbdrivers/dbdriver.h" #if !defined(TURN_NO_PROMETHEUS) #include "prom_server.h" #endif #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_2066, "", "", "", "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,0,1, 0,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 */ #if !defined(TURN_NO_PROMETHEUS) 0, /* prometheus disabled by default */ #endif ///////////// 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 */ 0, /* no_auth_pings */ 0, /* no_dynamic_ip_list */ 0, /* no_dynamic_realms */ 0 /* log_binding */ }; //////////////// 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 uint8_t*) 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" " --tcp-proxy-port Support connections from TCP loadbalancer on this port. The loadbalancer should\n" " use the binary proxy protocol (https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)\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" " --no-software-attribute Production mode: hide the software version (formerly --prod).\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 REST 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 REST 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 REST 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 REST 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 #if !defined(TURN_NO_PROMETHEUS) " --prometheus Enable prometheus metrics. It is disabled by default. If it is enabled it will listen on port 9641 unther the path /metrics\n" " also the path / on this port can be used as a health check\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" " --no-auth-pings Disable periodic health checks to 'dynamic' auth secret tables.\n" " --no-dynamic-ip-list Do not use dynamic allowed/denied peer ip list.\n" " --no-dynamic-realms Do not use dynamic realm assignment and options.\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 2066.\n" " --dh1066 Use 1066 bits predefined DH TLS key. Default size of the predefined key is 2066.\n" " --dh-file Use custom DH TLS key, stored in PEM format in the file.\n" " Flags --dh566 and --dh1066 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" " --new-log-timestamp Enable full ISO-8601 timestamp in all logs.\n" " --new-log-timestamp-format Set timestamp format (in strftime(1) format)\n" " --log-binding Log STUN binding request. It is now disabled by default to avoid DoS attacks.\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" " --acme-redirect Redirect ACME, i.e. HTTP GET requests matching '^/.well-known/acme-challenge/(.*)' to '$1'.\n" " Default is '', i.e. no special handling for such requests.\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"; 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 REST 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, TCP_PROXY_PORT_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, PROMETHEUS_OPT, AUTH_SECRET_OPT, NO_AUTH_PINGS_OPT, NO_DYNAMIC_IP_LIST_OPT, NO_DYNAMIC_REALMS_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, NEW_LOG_TIMESTAMP_OPT, NEW_LOG_TIMESTAMP_FORMAT_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, DH1066_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, NO_SOFTWARE_ATTRIBUTE_OPT, NO_HTTP_OPT, SECRET_KEY_OPT, ACME_REDIRECT_OPT, LOG_BINDING_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 }, { "tcp-proxy-port", required_argument, NULL, TCP_PROXY_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 #if !defined(TURN_NO_PROMETHEUS) { "prometheus", optional_argument, NULL, PROMETHEUS_OPT }, #endif { "use-auth-secret", optional_argument, NULL, AUTH_SECRET_OPT }, { "static-auth-secret", required_argument, NULL, STATIC_AUTH_SECRET_VAL_OPT }, { "no-auth-pings", optional_argument, NULL, NO_AUTH_PINGS_OPT }, { "no-dynamic-ip-list", optional_argument, NULL, NO_DYNAMIC_IP_LIST_OPT }, { "no-dynamic-realms", optional_argument, NULL, NO_DYNAMIC_REALMS_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' }, /* deprecated: */ { "prod", optional_argument, NULL, NO_SOFTWARE_ATTRIBUTE_OPT }, { "no-software-attribute", optional_argument, NULL, NO_SOFTWARE_ATTRIBUTE_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 }, { "new-log-timestamp", optional_argument, NULL, NEW_LOG_TIMESTAMP_OPT }, { "new-log-timestamp-format", required_argument, NULL, NEW_LOG_TIMESTAMP_FORMAT_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 }, { "dh1066", optional_argument, NULL, DH1066_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' }, { "acme-redirect", required_argument, NULL, ACME_REDIRECT_OPT }, { "log-binding", optional_argument, NULL, LOG_BINDING_OPT }, { 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 == 0) { 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 DH1066_OPT: if(get_bool_value(value)) turn_params.dh_key_size = DH_1066; 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 uint8_t*)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 uint8_t*)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 TCP_PROXY_PORT_OPT: turn_params.tcp_proxy_port = atoi(value); turn_params.tcp_use_proxy = 1; 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=strdup(value); div = strchr(nval,'/'); div[0]=0; ++div; ioa_addr apub,apriv; if(make_ioa_addr((const uint8_t*)nval,0,&apub)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"-X : Wrong address format: %s\n",nval); } else { if(make_ioa_addr((const uint8_t*)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); if (add_ip_list_range((const char *)div, NULL, &turn_params.ip_whitelist) == 0) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Whitelisting external-ip private part: %s\n", div); } } free(nval); } 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 uint8_t*)value,0,turn_params.external_ip)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"-X : Wrong address format: %s\n",value); free(turn_params.external_ip); 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 NO_SOFTWARE_ATTRIBUTE_OPT: turn_params.no_software_attribute = 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 #if !defined(TURN_NO_PROMETHEUS) case PROMETHEUS_OPT: turn_params.prometheus = 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 NO_AUTH_PINGS_OPT: turn_params.no_auth_pings = 1; break; case NO_DYNAMIC_IP_LIST_OPT: turn_params.no_dynamic_ip_list = 1; break; case NO_DYNAMIC_REALMS_OPT: turn_params.no_dynamic_realms = 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 ACME_REDIRECT_OPT: STRCPY(turn_params.acme_redirect,value); break; case 'C': if(value && *value) { turn_params.rest_api_separator=*value; } break; case LOG_BINDING_OPT: turn_params.log_binding = get_bool_value(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 NEW_LOG_TIMESTAMP_OPT: case NEW_LOG_TIMESTAMP_FORMAT_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); // strip white-spaces from config file lines end while (slen && isspace(s[slen - 1])) 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==NEW_LOG_TIMESTAMP_OPT)) { use_new_log_timestamp_format=1; } else if ((pass==0) && (c==NEW_LOG_TIMESTAMP_FORMAT_OPT)) { set_turn_log_timestamp_format(value); } else if((pass == 1) && (c != 'u')) { set_option(c, value); } else if((pass == 2) && (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) { free(full_path_to_config_file); full_path_to_config_file = NULL; } } } static int disconnect_database(void) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->disconnect) { dbd->disconnect(); } return 0; } static int adminmain(int argc, char **argv) { int c = 0; int rc = 0; TURNADMIN_COMMAND_TYPE ct = TA_COMMAND_UNKNOWN; int is_admin = 0; FILE* fptr; unsigned char generated_key[16]; //changed uint8_t user[STUN_MAX_USERNAME_SIZE+1]="\0"; uint8_t realm[STUN_MAX_REALM_SIZE+1]="\0"; uint8_t pwd[STUN_MAX_PWD_SIZE+1]="\0"; uint8_t secret[AUTH_SECRET_SIZE+1]="\0"; uint8_t 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((uint8_t*)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((uint8_t*)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((uint8_t*)realm)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong realm: %s\n",realm); exit(-1); } break; case 'p': STRCPY(pwd,optarg); if(SASLprep((uint8_t*)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); rc = fread(generated_key, sizeof(char), 16, fptr); if( rc == 0 ){ TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: Secret-Key file is empty\n",__FUNCTION__); } else{ if( rc != 16 ){ TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: Secret-Key length is not enough\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); } int result = adminuser(user, realm, pwd, secret, origin, ct, &po, is_admin); disconnect_database(); return result; } 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 == 0) { 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) { setgroups(0, NULL); 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; case NEW_LOG_TIMESTAMP_OPT: use_new_log_timestamp_format=1; break; case NEW_LOG_TIMESTAMP_FORMAT_OPT: set_turn_log_timestamp_format(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 bzero(&turn_params.default_users_db,sizeof(default_users_db_t)); turn_params.default_users_db.ram_db.static_accounts = ur_string_map_create(free); if(strstr(argv[0],"turnadmin")) return adminmain(argc,argv); // Zero pass apply the log options. read_config_file(argc,argv,0); // First pass read other config options read_config_file(argc,argv,1); 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); } // Second pass read -u options read_config_file(argc,argv,2); { 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.acme_redirect[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "ACME redirect URL: %s\n",turn_params.acme_redirect); } 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 methods. \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 && use_cli) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIG ERROR: allow_loopback_peers and empty cli password cannot be used together.\n"); exit(-1); } } if(use_cli && cli_password[0]==0 && use_cli) { 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(); #if !defined(TURN_NO_PROMETHEUS) if (start_prometheus_server()){ TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCould not start Prometheus collector!\n"); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\nPrometheus collector started sucessfully.\n"); } #endif run_listener_server(&(turn_params.listener)); disconnect_database(); 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 && OPENSSL_VERSION_NUMBER <= OPENSSL_VERSION_1_1_1 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 && OPENSSL_VERSION_NUMBER <= OPENSSL_VERSION_1_1_1 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) free(full_path_to_file); 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) free(full_path_to_file); 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; int rc = 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_1066) dh = get_dh1066(); else dh = get_dh2066(); } /* 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); rc = fread(turn_params.secret_key, sizeof(char), 16, f); if( rc == 0 ){ TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: Secret-Key file is empty\n",__FUNCTION__); } else{ if( rc != 16 ){ TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: Secret-Key length is not enough\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.2/src/apps/relay/turn_admin_server.h0000644000175000017500000000674413776656461021232 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.2/src/apps/relay/dtls_listener.h0000644000175000017500000000505613776656461020352 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.2/src/apps/relay/http_server.c0000644000175000017500000002474413776656461020044 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); uint8_t *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); 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; --data_len; } if (data_len) { char *post_data = (char*)calloc(data_len + 1, sizeof(char)); if (post_data != NULL) { 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)); bzero(list,sizeof(struct headers_list)); while (fsplit != NULL) { char *vmarker = NULL; char *key = strtok_r(fsplit, "=", &vmarker); if (key == NULL) break; else { 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; } } return NULL; } 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)); 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)); 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)); 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)); 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)); 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); } 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.2/src/apps/relay/userdb.h0000644000175000017500000001406013776656461016756 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; uint8_t username[STUN_MAX_USERNAME_SIZE + 1]; uint8_t realm[STUN_MAX_REALM_SIZE + 1]; hmackey_t key; password_t pwd; get_username_resume_cb resume_func; ioa_net_data in_buffer; uint64_t 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, uint8_t *uname, uint8_t *realm, hmackey_t key, ioa_network_buffer_handle nbh); uint8_t *start_user_check(turnserver_id id, turn_credential_type ct, int in_oauth, int *out_oauth, uint8_t *uname, uint8_t *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, uint64_t ctxkey, int *postpone_reply); int check_new_allocation_quota(uint8_t *username, int oauth, uint8_t *realm); void release_allocation_quota(uint8_t *username, int oauth, uint8_t *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(uint8_t *user, uint8_t *realm, uint8_t *pwd, uint8_t *secret, uint8_t *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.2/src/apps/relay/http_server.h0000644000175000017500000000572413776656461020046 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.2/src/apps/relay/libtelnet.c0000644000175000017500000011740413776656461017455 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.2/src/apps/relay/prom_server.c0000644000175000017500000001267113776656461020036 0ustar misimisi#if !defined(TURN_NO_PROMETHEUS) #include "mainrelay.h" #include "prom_server.h" prom_counter_t *turn_traffic_rcvp; prom_counter_t *turn_traffic_rcvb; prom_counter_t *turn_traffic_sentp; prom_counter_t *turn_traffic_sentb; prom_counter_t *turn_traffic_peer_rcvp; prom_counter_t *turn_traffic_peer_rcvb; prom_counter_t *turn_traffic_peer_sentp; prom_counter_t *turn_traffic_peer_sentb; prom_counter_t *turn_total_traffic_rcvp; prom_counter_t *turn_total_traffic_rcvb; prom_counter_t *turn_total_traffic_sentp; prom_counter_t *turn_total_traffic_sentb; prom_counter_t *turn_total_traffic_peer_rcvp; prom_counter_t *turn_total_traffic_peer_rcvb; prom_counter_t *turn_total_traffic_peer_sentp; prom_counter_t *turn_total_traffic_peer_sentb; int start_prometheus_server(void){ if (turn_params.prometheus == 0){ return 1; } prom_collector_registry_default_init(); const char *label[] = {"realm", "user"}; // Create traffic counter metrics turn_traffic_rcvp = prom_collector_registry_must_register_metric(prom_counter_new("turn_traffic_rcvp", "Represents finsihed sessions received packets", 2, label)); turn_traffic_rcvb = prom_collector_registry_must_register_metric(prom_counter_new("turn_traffic_rcvb", "Represents finsihed sessions received bytes", 2, label)); turn_traffic_sentp = prom_collector_registry_must_register_metric(prom_counter_new("turn_traffic_sentp", "Represents finsihed sessions sent packets", 2, label)); turn_traffic_sentb = prom_collector_registry_must_register_metric(prom_counter_new("turn_traffic_sentb", "Represents finsihed sessions sent bytes", 2, label)); // Create finsihed sessions traffic for peers counter metrics turn_traffic_peer_rcvp = prom_collector_registry_must_register_metric(prom_counter_new("turn_traffic_peer_rcvp", "Represents finsihed sessions peer received packets", 2, label)); turn_traffic_peer_rcvb = prom_collector_registry_must_register_metric(prom_counter_new("turn_traffic_peer_rcvb", "Represents finsihed sessions peer received bytes", 2, label)); turn_traffic_peer_sentp = prom_collector_registry_must_register_metric(prom_counter_new("turn_traffic_peer_sentp", "Represents finsihed sessions peer sent packets", 2, label)); turn_traffic_peer_sentb = prom_collector_registry_must_register_metric(prom_counter_new("turn_traffic_peer_sentb", "Represents finsihed sessions peer sent bytes", 2, label)); // Create total finished traffic counter metrics turn_total_traffic_rcvp = prom_collector_registry_must_register_metric(prom_counter_new("turn_total_traffic_rcvp", "Represents total finsihed sessions received packets", 0, NULL)); turn_total_traffic_rcvb = prom_collector_registry_must_register_metric(prom_counter_new("turn_total_traffic_rcvb", "Represents total finsihed sessions received bytes", 0, NULL)); turn_total_traffic_sentp = prom_collector_registry_must_register_metric(prom_counter_new("turn_total_traffic_sentp", "Represents total finsihed sessions sent packets", 0, NULL)); turn_total_traffic_sentb = prom_collector_registry_must_register_metric(prom_counter_new("turn_total_traffic_sentb", "Represents total finsihed sessions sent bytes", 0, NULL)); // Create total finsihed sessions traffic for peers counter metrics turn_total_traffic_peer_rcvp = prom_collector_registry_must_register_metric(prom_counter_new("turn_total_traffic_peer_rcvp", "Represents total finsihed sessions peer received packets", 0, NULL)); turn_total_traffic_peer_rcvb = prom_collector_registry_must_register_metric(prom_counter_new("turn_total_traffic_peer_rcvb", "Represents total finsihed sessions peer received bytes", 0, NULL)); turn_total_traffic_peer_sentp = prom_collector_registry_must_register_metric(prom_counter_new("turn_total_traffic_peer_sentp", "Represents total finsihed sessions peer sent packets", 0, NULL)); turn_total_traffic_peer_sentb = prom_collector_registry_must_register_metric(prom_counter_new("turn_total_traffic_peer_sentb", "Represents total finsihed sessions peer sent bytes", 0, NULL)); promhttp_set_active_collector_registry(NULL); struct MHD_Daemon *daemon = promhttp_start_daemon(MHD_USE_SELECT_INTERNALLY, DEFAULT_PROM_SERVER_PORT, NULL, NULL); if (daemon == NULL) { return 1; } return 0; } void prom_set_finished_traffic(const char* realm, const char* user, unsigned long rsvp, unsigned long rsvb, unsigned long sentp, unsigned long sentb, bool peer){ if (turn_params.prometheus == 1){ const char *label[] = {realm, user}; if (peer){ prom_counter_add(turn_traffic_peer_rcvp, rsvp, label); prom_counter_add(turn_traffic_peer_rcvb, rsvb, label); prom_counter_add(turn_traffic_peer_sentp, sentp, label); prom_counter_add(turn_traffic_peer_sentb, sentb, label); prom_counter_add(turn_total_traffic_peer_rcvp, rsvp, NULL); prom_counter_add(turn_total_traffic_peer_rcvb, rsvb, NULL); prom_counter_add(turn_total_traffic_peer_sentp, sentp, NULL); prom_counter_add(turn_total_traffic_peer_sentb, sentb, NULL); } else { prom_counter_add(turn_traffic_rcvp, rsvp, label); prom_counter_add(turn_traffic_rcvb, rsvb, label); prom_counter_add(turn_traffic_sentp, sentp, label); prom_counter_add(turn_traffic_sentb, sentb, label); prom_counter_add(turn_total_traffic_rcvp, rsvp, NULL); prom_counter_add(turn_total_traffic_rcvb, rsvb, NULL); prom_counter_add(turn_total_traffic_sentp, sentp, NULL); prom_counter_add(turn_total_traffic_sentb, sentb, NULL); } } } #endif /* TURN_NO_PROMETHEUS */ turnserver-4.5.2/src/apps/relay/acme.h0000644000175000017500000000372613776656461016406 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_ACME__ #define __TURN_ACME__ #include "ns_turn_utils.h" #include "ns_turn_server.h" #include "apputils.h" #include #include #ifdef __cplusplus extern "C" { #endif ///////////// ACME ///////////////////// int try_acme_redirect(char *req, size_t len, const char *url, ioa_socket_handle s); /////////////////////////////////////// #ifdef __cplusplus } #endif #endif /// __TURN_ACME__ /// turnserver-4.5.2/src/apps/relay/userdb.c0000644000175000017500000007704513776656461016765 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(free); 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(); 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*)malloc(sizeof(realm_params_t)); 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(); 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 = strdup((char*)value); TURN_MUTEX_UNLOCK(&o_to_realm_mutex); realm_params_t rp; get_realm_data(realm, &rp); bcopy(&(rp.options),ro,sizeof(realm_options_t)); free(realm); 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); 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) { 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]) { free(sl->secrets[i]); } } free(sl->secrets); 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**)realloc(sl->secrets,(sizeof(char*)*(sl->sz+1))); sl->secrets[sl->sz] = strdup(elem); sl->sz += 1; } } //////////////////////////////////////////// static int get_auth_secrets(secrets_list_t *sl, uint8_t *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 = strdup(usname); *col=turn_params.rest_api_separator; return usname; } } } } return strdup(usname); } /* * Password retrieval */ int get_user_key(int in_oauth, int *out_oauth, int *max_session_time, uint8_t *usname, uint8_t *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 uint8_t *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; 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; 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; 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; bzero((&dot),sizeof(dot)); encoded_oauth_token etoken; 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; } 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 uint8_t *) 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; } } bcopy(dot.enc_block.mac_key,key,dot.enc_block.key_length); if(rawKey.realm[0]) { 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)) { uint8_t 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) { free(pwd); } else { if(stun_produce_integrity_key_str((uint8_t*)usname, realm, (uint8_t*)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; } } free(pwd); 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); 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; } uint8_t *start_user_check(turnserver_id id, turn_credential_type ct, int in_oauth, int *out_oauth, uint8_t *usname, uint8_t *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, uint64_t ctxkey, int *postpone_reply) { *postpone_reply = 1; struct auth_message am; 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(uint8_t *user, int oauth, uint8_t *realm) { int ret = 0; if (user || oauth) { uint8_t *username = oauth ? (uint8_t*)strdup("") : (uint8_t*)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); } free(username); ur_string_map_unlock(rp->status.alloc_counters); } return ret; } void release_allocation_quota(uint8_t *user, int oauth, uint8_t *realm) { if (user) { uint8_t *username = oauth ? (uint8_t*)strdup("") : (uint8_t*)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); free(username); } } ////////////////////////////////// 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*)malloc(sizeof(char)*(ulen+1)); strncpy(usname,user,ulen); usname[ulen]=0; if(SASLprep((uint8_t*)usname)<0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user name: %s\n",user); free(usname); return -1; } s = skip_blanks(s+1); hmackey_t *key = (hmackey_t*)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, (uint8_t*)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++; free(usname); return 0; } } return -1; } ////////////////// Admin ///////////////////////// static int list_users(uint8_t *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(uint8_t *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(uint8_t *secret, uint8_t *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(uint8_t *secret, uint8_t *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(uint8_t *origin0, uint8_t *realm) { uint8_t 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(uint8_t *origin0) { uint8_t 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(uint8_t *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(uint8_t *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(uint8_t *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(uint8_t *realm) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->list_realm_options) { (*dbd->list_realm_options)(realm); } return 0; } int adminuser(uint8_t *user, uint8_t *realm, uint8_t *pwd, uint8_t *secret, uint8_t *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 uint8_t*)"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 uint8_t*)"kid"); dbd->set_oauth_key(key); dbd->list_oauth_keys(); printf("DB TEST 4:\n"); dbd->get_oauth_key((const uint8_t*)"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 uint8_t*)"kid"); dbd->list_oauth_keys(); printf("DB TEST 6:\n"); dbd->get_oauth_key((const uint8_t*)"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*) malloc(sizeof(pthread_rwlock_t)); pthread_rwlock_init(whitelist_rwlock, NULL); blacklist_rwlock = (pthread_rwlock_t*) 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*) malloc(sizeof(ip_range_list_t)); bzero(ret,sizeof(ip_range_list_t)); const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->get_ip_list && !turn_params.no_dynamic_ip_list) { (*dbd->get_ip_list)(kind, ret); } return ret; } void ip_list_free(ip_range_list_t *l) { if(l) { if(l->rs) free(l->rs); free(l); } } 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 = strdup(range0); char* separator = strchr(range, '-'); if (separator) { *separator = '\0'; } ioa_addr min, max; if (make_ioa_addr((const uint8_t*) range, 0, &min) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address format: %s\n", range); free(range); return -1; } if (separator) { if (make_ioa_addr((const uint8_t*) separator + 1, 0, &max) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address format: %s\n", separator + 1); free(range); 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*) realloc(list->rs, 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; free(range); 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 = strdup(range0); char* separator = strchr(range, '-'); if (separator) { *separator = '\0'; } ioa_addr min, max; if (make_ioa_addr((const uint8_t*) range, 0, &min) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address range format: %s\n", range); free(range); return -1; } if (separator) { if (make_ioa_addr((const uint8_t*) separator + 1, 0, &max) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address range format: %s\n", separator + 1); free(range); 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 = '-'; free(range); 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 && !turn_params.no_dynamic_realms) { (*dbd->reread_realms)(&realms_list); } } /////////////////////////////// turnserver-4.5.2/src/apps/relay/netengine.c0000644000175000017500000017105213776656461017446 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 uint8_t*)saddr, 0, &addr)!=0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong full address format: %s\n",saddr); } else { list->addrs = (ioa_addr*)realloc(list->addrs,sizeof(ioa_addr)*(list->size+1)); addr_cpy(&(list->addrs[(list->size)++]),&addr); { uint8_t 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 uint8_t*)saddr, default_port, &addr)!=0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong IP address format: %s\n",saddr); } else { list->addrs = (ioa_addr*)realloc(list->addrs,sizeof(ioa_addr)*(list->size+1)); addr_cpy(&(list->addrs[(list->size)++]),&addr); { uint8_t 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 uint8_t*)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*)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])); } free(list->addrs); list->addrs = new_addrs; list->size -= 1; { uint8_t 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 *)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 uint8_t*)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,(uint8_t*)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 { 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, uint64_t 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; 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; 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); 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 !defined(TURN_NO_SYSTEMD) sd_notify (0, "READY=1"); #endif 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(); } #if !defined(TURN_NO_SYSTEMD) sd_notify (0, "STOPPING=1"); #endif } 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.no_software_attribute, &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.acme_redirect, turn_params.keep_address_family, &turn_params.log_binding); 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 { 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) { if (!turn_params.no_auth_pings) { 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) { 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 #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.2/src/apps/relay/ns_ioalib_engine_impl.c0000644000175000017500000031350613776656461022001 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 !defined(TURN_NO_PROMETHEUS) #include "prom_server.h" #endif #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),(uint8_t*)sraddr); addr_to_string(&(s->local_addr),(uint8_t*)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; free(ret); } } 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 *)malloc(sizeof(stun_buffer_list_elem)); if (ret) { ret->next = NULL; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Cannot allocate memory for STUN buffer!\n", __FUNCTION__); } } if(ret) { bzero(&ret->buf, sizeof(stun_buffer)); } 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, char *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 { free(buf_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 char* relay_ifname, size_t relays_number, char **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 { 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((uint8_t*) 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 char *txt) { ioa_timer_handle ret = NULL; if (e && cb && secs > 0) { timer_event * te = (timer_event*) 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 = 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) { free(te->txt); te->txt = NULL; } free(th); } } /************** 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) { uint8_t *buf = ioa_network_buffer_data(nbh); if(stun_is_command_message_str(buf,sz)) { uint16_t 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, uint64_t 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*)malloc(sizeof(ioa_socket)); 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, uint8_t transport, int even_port, ioa_socket_handle *rtp_s, ioa_socket_handle *rtcp_s, uint64_t *out_reservation_token, int *err_code, const uint8_t **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 uint8_t *) "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; 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); 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*)malloc(sizeof(ioa_socket)); 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) { free(s->special_session); 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; free(s); } } 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*)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; } 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; char* buffer = (char*)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; char *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 || defined LIBRESSL_VERSION_NUMBER 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: "); char 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, (uint16_t)buf_size, 0, (size_t)ret); } #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined LIBRESSL_VERSION_NUMBER 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) uint8_t 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 size_t proxy_string_field(char *field, size_t max, uint8_t *buf, size_t index, size_t len) { size_t count = 0; while((index < len) && (count < max)) { if((0x20 == buf[index]) || (0x0D == buf[index])) { field[count] = 0x00; return ++index; } field[count++] = buf[index++]; } return 0; } static ssize_t socket_parse_proxy_v1(ioa_socket_handle s, uint8_t *buf, size_t len) { if(len < 11) { return 0 ; } /* Check for proxy-v1 magic field */ char magic[] = {0x50, 0x52, 0x4F, 0x58, 0x59, 0x20}; if(memcmp(magic, buf, sizeof(magic))) { return -1; } /* Read family */ char tcp4[] = {0x54, 0x43, 0x50, 0x34, 0x20}; char tcp6[] = {0x54, 0x43, 0x50, 0x36, 0x20}; int family; if(0 == memcmp(tcp4, &buf[6], sizeof(tcp4))) { /* IPv4 */ family = AF_INET; } else if(0 == memcmp(tcp6, &buf[6], sizeof(tcp6))) { /* IPv6 */ family = AF_INET6; } else { return -1; } char saddr[40]; char daddr[40]; char sport[6]; char dport[6]; size_t tlen = 11; /* Read source address */ tlen = proxy_string_field(saddr, sizeof(saddr), buf, tlen, len); if(0 == tlen) return -1; /* Read dest address */ tlen = proxy_string_field(daddr, sizeof(daddr), buf, tlen, len); if(0 == tlen) return -1; /* Read source port */ tlen = proxy_string_field(sport, sizeof(sport), buf, tlen, len); if(0 == tlen) return -1; /* Read dest port */ tlen = proxy_string_field(dport, sizeof(dport), buf, tlen, len); if(0 == tlen) return -1; /* Final line feed */ if ((len <= tlen) || (0x0A != buf[tlen])) return -1; tlen++; int sport_int = atoi(sport); int dport_int = atoi(dport); if((sport_int < 0) || (0xFFFF < sport_int)) return -1; if((dport_int < 0) || (0xFFFF < dport_int)) return -1; if (AF_INET == family) { struct sockaddr_in remote, local; remote.sin_family = local.sin_family = AF_INET; if(1 != inet_pton(AF_INET, saddr, &remote.sin_addr.s_addr)) return -1; if(1 != inet_pton(AF_INET, daddr, &local.sin_addr.s_addr)) return -1; remote.sin_port = htons((uint16_t)sport_int); local.sin_port = htons((uint16_t)dport_int); addr_cpy4(&(s->local_addr), &local); addr_cpy4(&(s->remote_addr), &remote); } else { struct sockaddr_in6 remote, local; remote.sin6_family = local.sin6_family = AF_INET6; if(1 != inet_pton(AF_INET6, saddr, &remote.sin6_addr.s6_addr)) return -1; if(1 != inet_pton(AF_INET6, daddr, &local.sin6_addr.s6_addr)) return -1; remote.sin6_port = htons((uint16_t)sport_int); local.sin6_port = htons((uint16_t)dport_int); addr_cpy6(&(s->local_addr), &local); addr_cpy6(&(s->remote_addr), &remote); } return tlen; } static ssize_t socket_parse_proxy_v2(ioa_socket_handle s, uint8_t *buf, size_t len) { if(len < 16){ return 0 ; } /* Check for proxy-v2 magic field */ char magic[] = {0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A}; if(memcmp(magic, buf, sizeof(magic))){ return -1; } /* Check version */ uint8_t version = buf[12] >> 4; if(version != 2) return -1; /* Read data */ uint8_t command = buf[12] & 0xF; uint8_t family = buf[13] >> 4; uint8_t proto = buf[13] & 0xF; size_t plen = ((size_t)buf[14] << 8) | buf[15]; size_t tlen = 16 + plen; if(len < tlen) return 0; /* A local connection is used by the proxy itself and does not carry a valid address */ if(command == 0) return tlen; /* Accept only proxied TCP connections */ if(command != 1 || proto != 1) return -1; /* Read the address */ if(family == 1 && plen >= 12){ /* IPv4 */ struct sockaddr_in remote, local; remote.sin_family = local.sin_family = AF_INET; memcpy(&remote.sin_addr.s_addr, &buf[16], 4); memcpy(&local.sin_addr.s_addr, &buf[20], 4); memcpy(&remote.sin_port, &buf[24], 2); memcpy(&local.sin_port, &buf[26], 2); addr_cpy4(&(s->local_addr), &local); addr_cpy4(&(s->remote_addr), &remote); }else if(family == 2 && plen >= 36){ /* IPv6 */ struct sockaddr_in6 remote, local; remote.sin6_family = local.sin6_family = AF_INET6; memcpy(&remote.sin6_addr.s6_addr, &buf[16], 16); memcpy(&local.sin6_addr.s6_addr, &buf[32], 16); memcpy(&remote.sin6_port, &buf[48], 2); memcpy(&local.sin6_port, &buf[50], 2); addr_cpy6(&(s->local_addr), &local); addr_cpy6(&(s->remote_addr), &remote); }else{ return -1; } return tlen; } static ssize_t socket_parse_proxy(ioa_socket_handle s, uint8_t *buf, size_t len) { ssize_t tlen = socket_parse_proxy_v2(s, buf, len); if(-1 == tlen) { tlen = socket_parse_proxy_v1(s, buf, len); } return tlen; } 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); 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); 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); 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); 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(s->st == TCP_SOCKET_PROXY){ ssize_t tlen = socket_parse_proxy(s, buf_elem->buf.buf, blen); blen = 0; if (tlen < 0){ s->tobeclosed = 1; s->broken = 1; ret = -1; log_socket_event(s, "proxy protocol violated",1); }else if(tlen > 0){ bufferevent_read(s->bev, buf_elem->buf.buf, tlen); blen = evbuffer_copyout(inbuf, buf_elem->buf.buf, STUN_BUFFER_SIZE); s->st = TCP_SOCKET; } } if(blen){ 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), (char*)(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; 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) { free(s->special_session); 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) { free(s->special_session); 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),(uint8_t*)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),(uint8_t*)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 char* 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: "); char 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, (char*)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 char* 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, (char*) 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), (char*) 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, (char*) 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), (uint8_t*)sfrom); char sto[129]; addr_to_string(dest_addr, (uint8_t*)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: case TCP_SOCKET_PROXY: 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((s->sat != TCP_CLIENT_DATA_SOCKET) && (s->sat != TCP_RELAY_DATA_SOCKET) && 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); 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); } else { s->bev = bufferevent_openssl_socket_new(s->e->event_base, s->fd, s->ssl, BUFFEREVENT_SSL_OPEN, TURN_BUFFEREVENTS_OPTIONS); } 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); } uint8_t *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, uint16_t offset, uint8_t 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; } } uint16_t 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; } uint8_t 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)); if (ss->realm_options.name[0]) { snprintf(key, sizeof(key), "turn/realm/%s/user/%s/allocation/%018llu/total_traffic/peer", ss->realm_options.name, (char*)ss->username, (unsigned long long)(ss->id)); } else { snprintf(key, sizeof(key), "turn/user/%s/allocation/%018llu/total_traffic/peer", (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_peer_received_packets), (unsigned long)(ss->t_peer_received_bytes), (unsigned long)(ss->t_peer_sent_packets), (unsigned long)(ss->t_peer_sent_bytes)); } #endif #if !defined(TURN_NO_PROMETHEUS) { if(ss->realm_options.name[0]){ // Set prometheus traffic metrics prom_set_finished_traffic(ss->realm_options.name, (const char*)ss->username, (unsigned long)(ss->t_received_packets), (unsigned long)(ss->t_received_bytes), (unsigned long)(ss->t_sent_packets), (unsigned long)(ss->t_sent_bytes), false); prom_set_finished_traffic(ss->realm_options.name, (const char*)ss->username, (unsigned long)(ss->t_peer_received_packets), (unsigned long)(ss->t_peer_received_bytes), (unsigned long)(ss->t_peer_sent_packets), (unsigned long)(ss->t_peer_sent_bytes), true); } else { // Set prometheus traffic metrics prom_set_finished_traffic(NULL, (const char*)ss->username, (unsigned long)(ss->t_received_packets), (unsigned long)(ss->t_received_bytes), (unsigned long)(ss->t_sent_packets), (unsigned long)(ss->t_sent_bytes), false); prom_set_finished_traffic(NULL, (const char*)ss->username, (unsigned long)(ss->t_peer_received_packets), (unsigned long)(ss->t_peer_received_bytes), (unsigned long)(ss->t_peer_sent_packets), (unsigned long)(ss->t_peer_sent_bytes), true); } } #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+ss->peer_received_packets+ss->peer_sent_packets)&4095)==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)); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "session %018llu: peer 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->peer_received_packets), (unsigned long)(ss->peer_received_bytes), (unsigned long)(ss->peer_sent_packets), (unsigned long)(ss->peer_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)); if (ss->realm_options.name[0]) { snprintf(key, sizeof(key), "turn/realm/%s/user/%s/allocation/%018llu/traffic/peer", ss->realm_options.name, (char*)ss->username, (unsigned long long)(ss->id)); } else { snprintf(key, sizeof(key), "turn/user/%s/allocation/%018llu/traffic/peer", (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->peer_received_packets), (unsigned long)(ss->peer_received_bytes), (unsigned long)(ss->peer_sent_packets), (unsigned long)(ss->peer_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; ss->t_peer_received_packets += ss->peer_received_packets; ss->t_peer_received_bytes += ss->peer_received_bytes; ss->t_peer_sent_packets += ss->peer_sent_packets; ss->t_peer_sent_bytes += ss->peer_sent_bytes; { turn_time_t ct = get_turn_server_time(server); if(ct != ss->start_time) { ct = ct - ss->start_time; ss->received_rate = (uint32_t)(ss->t_received_bytes / ct); ss->sent_rate = (uint32_t)(ss->t_sent_bytes / ct); ss->total_rate = ss->received_rate + ss->sent_rate; ss->peer_received_rate = (uint32_t)(ss->t_peer_received_bytes / ct); ss->peer_sent_rate = (uint32_t)(ss->t_peer_sent_bytes / ct); ss->peer_total_rate = ss->peer_received_rate + ss->peer_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; ss->peer_received_packets = 0; ss->peer_received_bytes = 0; ss->peer_sent_packets = 0; ss->peer_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; uint32_t id; }; static void init_super_memory_region(super_memory_t *r) { if(r) { bzero(r,sizeof(super_memory_t)); r->super_memory = (char**)malloc(sizeof(char*)); r->super_memory[0] = (char*)malloc(TURN_SM_SIZE); bzero(r->super_memory[0],TURN_SM_SIZE); r->sm_allocated = (size_t*)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 = (uint32_t)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*)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 = malloc(size); 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**)realloc(r->super_memory,(r->sm_chunk+1) * sizeof(char*)); r->super_memory[r->sm_chunk] = (char*)malloc(TURN_SM_SIZE); bzero(r->super_memory[r->sm_chunk],TURN_SM_SIZE); r->sm_allocated = (size_t*)realloc(r->sm_allocated,(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; bzero(ptr, size); *rsz += size; ret = ptr; } } pthread_mutex_unlock(&r->mutex_sm); if(!ret) { ret = malloc(size); 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.2/src/apps/relay/tls_listener.c0000644000175000017500000002355013776656461020200 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; 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.tcp_use_proxy) st = TCP_SOCKET_PROXY; else 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; 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,(uint8_t*)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 uint8_t*)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.2/src/apps/rfc5769/0000755000175000017500000000000013776656461015311 5ustar misimisiturnserver-4.5.2/src/apps/rfc5769/rfc5769check.c0000644000175000017500000003702313776656461017565 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"; uint8_t 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 uint8_t uname[33]; uint8_t realm[33]; uint8_t 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"; uint8_t user[] = "\xe3\x83\x9e\xe3\x83\x88\xe3\x83\xaa\xe3\x83\x83" "\xe3\x82\xaf\xe3\x82\xb9"; uint8_t realm[33]; uint8_t nonce[29]; uint8_t upwd[33]; uint8_t buf[sizeof(reqltc)]; memcpy(buf, reqltc, sizeof(reqltc)); uint8_t 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; uint16_t message_type = STUN_METHOD_BINDING; stun_tid tid; uint16_t *buf16 = (uint16_t*)buf; uint32_t *buf32 = (uint32_t*)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 &= (uint16_t)(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 uint8_t*)"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"; uint8_t 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 uint8_t uname[33]; uint8_t realm[33]; uint8_t 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 uint8_t*) "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.2/src/apps/common/0000755000175000017500000000000013776656461015474 5ustar misimisiturnserver-4.5.2/src/apps/common/apputils.c0000644000175000017500000006152413776656461017511 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,(uint8_t*)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 TCP_SOCKET_PROXY: 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 = strdup(_var); char *edir=_var; if(edir[0]!='.') edir = strstr(edir,"/"); if(edir && *edir) edir = dirname(edir); else edir = dirname(_var); if(c_execdir) free(c_execdir); c_execdir = strdup(edir); free(_var); } } 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[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*)malloc(*output_length+1); if (encoded_data == NULL) return NULL; size_t i,j; for (i = 0, j = 0; i < input_length;) { uint32_t octet_a = i < input_length ? data[i++] : 0; uint32_t octet_b = i < input_length ? data[i++] : 0; uint32_t octet_c = i < input_length ? data[i++] : 0; uint32_t 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*)malloc(256); 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*)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 ///////////////////// const char* turn_get_ssl_method(SSL *ssl, const char* mdefault) { const char* ret = "unknown"; if(!ssl) { ret = mdefault; } else { ret = SSL_get_version(ssl); } 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) { bzero(oakd,sizeof(oauth_key_data)); oakd->timestamp = (turn_time_t)raw->timestamp; oakd->lifetime = raw->lifetime; bcopy(raw->as_rs_alg,oakd->as_rs_alg,sizeof(oakd->as_rs_alg)); 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) { bcopy(ikm_key,oakd->ikm_key,ikm_key_size); oakd->ikm_key_size = ikm_key_size; free(ikm_key); } } } } ////////////////////////////////////////////////////////////// turnserver-4.5.2/src/apps/common/ns_turn_utils.h0000644000175000017500000000572113776656461020562 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 set_turn_log_timestamp_format(char* new_format); void turn_log_func_default(TURN_LOG_LEVEL level, const char* format, ...); void addr_debug_print(int verbose, const ioa_addr *addr, const char* s); /* Log */ extern volatile int _log_time_value_set; extern volatile turn_time_t _log_time_value; extern int use_new_log_timestamp_format; 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 uint8_t *string, int sanitizesql); /////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_ULIB__ turnserver-4.5.2/src/apps/common/stun_buffer.c0000644000175000017500000002217513776656461020171 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; 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, uint8_t *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)); } uint16_t stun_get_method(const stun_buffer* buf) { return stun_get_method_str(buf->buf, (size_t)(buf->len)); } uint16_t stun_get_msg_type(const stun_buffer* buf) { if(!buf) return (uint16_t)-1; return stun_get_msg_type_str(buf->buf,(size_t)buf->len); } //////////////////////////////////////////////////////////// static void stun_init_command(uint16_t 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(uint16_t method, stun_buffer* buf) { stun_init_command(stun_make_request(method), buf); } void stun_init_indication(uint16_t method, stun_buffer* buf) { stun_init_command(stun_make_indication(method), buf); } void stun_init_success_response(uint16_t 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(uint16_t method, stun_buffer* buf, uint16_t error_code, const uint8_t *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(uint16_t 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, uint16_t* 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, uint32_t lifetime, int af4, int af6, uint8_t 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, uint32_t lifetime, uint32_t max_lifetime, int error_code, const uint8_t *reason, uint64_t 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); } /////////////////////////////////////////////////////////////////////////////// uint16_t stun_set_channel_bind_request(stun_buffer* buf, const ioa_addr* peer_addr, uint16_t 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 uint8_t *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, uint16_t attr, const char* avalue, int alen) { return stun_attr_add_str(buf->buf, (size_t*)(&(buf->len)), attr, (const uint8_t *)avalue, alen); } int stun_attr_add_channel_number(stun_buffer* buf, uint16_t chnumber) { return stun_attr_add_channel_number_str(buf->buf, (size_t *)(&(buf->len)), chnumber); } int stun_attr_add_addr(stun_buffer *buf,uint16_t 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, uint16_t 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 char*)&value,1); } uint16_t 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, uint16_t 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 uint8_t *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.2/src/apps/common/hiredis_libevent2.c0000644000175000017500000002226413776656461021247 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*)malloc(sizeof(struct redisLibeventEvents)); bzero(e,sizeof(struct redisLibeventEvents)); e->allocated = 1; e->context = ac; e->base = base; e->ip = strdup(ip); e->port = port; if(pwd) e->pwd = 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) { free(e); 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.2/src/apps/common/apputils.h0000644000175000017500000001551013776656461017510 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]; uint64_t timestamp; uint32_t 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.2/src/apps/common/hiredis_libevent2.h0000644000175000017500000000435713776656461021257 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.2/src/apps/common/ns_turn_openssl.h0000644000175000017500000000357313776656461021110 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 #endif //__NST_OPENSSL_LIB__ turnserver-4.5.2/src/apps/common/stun_buffer.h0000644000175000017500000001321513776656461020171 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 { uint8_t channel[STUN_CHANNEL_HEADER_LENGTH]; uint8_t buf[STUN_BUFFER_SIZE]; size_t len; uint16_t offset; uint8_t 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, uint8_t *err_msg, size_t err_msg_size); int stun_is_indication(const stun_buffer* buf); uint16_t stun_get_method(const stun_buffer* buf); uint16_t stun_get_msg_type(const stun_buffer* buf); /////////////////////////////////////////////////////////////// void stun_init_request(uint16_t method, stun_buffer* buf); void stun_init_indication(uint16_t method, stun_buffer* buf); void stun_init_success_response(uint16_t method, stun_buffer* buf, stun_tid* id); void stun_init_error_response(uint16_t method, stun_buffer* buf, uint16_t error_code, const uint8_t *reason, stun_tid* id); /////////////////////////////////////////////////////////////// int stun_attr_add(stun_buffer* buf, uint16_t attr, const char* avalue, int alen); int stun_attr_add_channel_number(stun_buffer* buf, uint16_t chnumber); int stun_attr_add_addr(stun_buffer *buf,uint16_t 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, uint16_t 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, uint16_t attr_type, ioa_addr* ca, const ioa_addr *default_addr); uint16_t 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(uint16_t chnumber, stun_buffer* buf, int length, int do_padding); int stun_is_channel_message(stun_buffer* buf, uint16_t* chnumber, int is_padding_madatory); /////////////////////////////////////////////////////////////// int stun_set_allocate_request(stun_buffer* buf, uint32_t lifetime, int af4, int af6, uint8_t 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, uint32_t lifetime, uint32_t max_lifetime, int error_code, const uint8_t *reason, uint64_t 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 uint8_t *reason); void stun_prepare_binding_request(stun_buffer* buf); int stun_is_binding_response(const stun_buffer* buf); /////////////////////////////////////////////////////////////// uint16_t stun_set_channel_bind_request(stun_buffer* buf, const ioa_addr* peer_addr, uint16_t channel_number); void stun_set_channel_bind_response(stun_buffer* buf, stun_tid* tid, int error_code, const uint8_t *reason); /////////////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__TURN_STUN_BUF__ turnserver-4.5.2/src/apps/common/ns_turn_utils.c0000644000175000017500000003707513776656461020564 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=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 = 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; free(mutex->mutex); 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)); free(mutex->mutex); 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 char* format, va_list args); #endif static int no_stdout_log = 0; void set_no_stdout_log(int val) { no_stdout_log = val; } #define MAX_LOG_TIMESTAMP_FORMAT_LEN 48 static char turn_log_timestamp_format[MAX_LOG_TIMESTAMP_FORMAT_LEN] = "%FT%T%z"; void set_turn_log_timestamp_format(char* new_format) { strncpy(turn_log_timestamp_format, new_format, MAX_LOG_TIMESTAMP_FORMAT_LEN-1); } int use_new_log_timestamp_format = 0; void addr_debug_print(int verbose, const ioa_addr *addr, const char* s) { if (verbose) { if (!addr) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: EMPTY\n", s); } else { char 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=strdup(".log"); get_date(logdate,sizeof(logdate)); char *base1=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]=='.') { free(tail); tail=strdup(base1+len); base1[len]=0; if(strlen(tail)<2) { free(tail); tail = 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); } free(base1); free(tail); } 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, "a"); 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()); if (snprintf(logbase, FILE_STR_LEN, "/var/log/turnserver/%s", logtail)<0) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "String truncation occured.\n"); set_log_file_name(logbase, logf); _rtpfile = fopen(logf, "a"); if(_rtpfile) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "log file opened: %s\n", logf); else { if (snprintf(logbase, FILE_STR_LEN, "/var/log/%s", logtail)<0) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "String truncation occured.\n"); set_log_file_name(logbase, logf); _rtpfile = fopen(logf, "a"); if(_rtpfile) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "log file opened: %s\n", logf); else { if (snprintf(logbase, FILE_STR_LEN, "/var/tmp/%s", logtail)<0) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "String truncation occured.\n"); set_log_file_name(logbase, logf); _rtpfile = fopen(logf, "a"); if(_rtpfile) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "log file opened: %s\n", logf); else { if (snprintf(logbase, FILE_STR_LEN, "/tmp/%s", logtail)<0) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "String truncation occured.\n"); set_log_file_name(logbase, logf); _rtpfile = fopen(logf, "a"); 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, "a"); 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; } void turn_log_func_default(TURN_LOG_LEVEL level, const char* format, ...) { va_list args; va_start(args,format); #if defined(TURN_LOG_FUNC_IMPL) TURN_LOG_FUNC_IMPL(level,format,args); #else /* 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 so_far = 0; if (use_new_log_timestamp_format) { time_t now = time(NULL); so_far += strftime(s, sizeof(s), turn_log_timestamp_format, localtime(&now)); } else { so_far += snprintf(s, sizeof(s), "%lu: ", (unsigned long)log_time()); } so_far += snprintf(s + so_far, sizeof(s)-100, (level == TURN_LOG_LEVEL_ERROR) ? ": ERROR: " : ": "); so_far += vsnprintf(s + so_far,sizeof(s) - (so_far+1), format, args); /* always write to stdout */ fwrite(s, so_far, 1, stdout); /* write to syslog or to log file */ 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(); } #endif 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,3)) 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; } ////////////////////////////////////////////////////////////////// int is_secure_string(const uint8_t *string, int sanitizesql) { int ret = 0; if(string) { unsigned char *s0 = (unsigned char*)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; } free(s); } return ret; } ////////////////////////////////////////////////////////////////// turnserver-4.5.2/src/apps/oauth/0000755000175000017500000000000013776656461015324 5ustar misimisiturnserver-4.5.2/src/apps/oauth/oauth.c0000644000175000017500000004161413776656461016616 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 "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) { bzero(key,sizeof(*key)); oauth_key_data okd; bzero(&okd,sizeof(okd)); { oauth_key_data_raw okdr; 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; 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; bzero(&etoken,sizeof(etoken)); // TODO: avoid this hack if (!*gcm_nonce) gcm_nonce=NULL; if (encode_oauth_token((const uint8_t *) server_name, &etoken, &key, &ot,(const uint8_t *) 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) { bzero((dot),sizeof(*dot)); encoded_oauth_token etoken; 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 uint8_t *) 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.2/src/apps/natdiscovery/0000755000175000017500000000000013776656461016716 5ustar misimisiturnserver-4.5.2/src/apps/natdiscovery/natdiscovery.c0000644000175000017500000006072113776656461021602 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((uint16_t)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; uint8_t *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((uint8_t*) (buf->buf), (size_t*) &(buf->len), (uint16_t) response_port); } if (change_ip || change_port) { stun_attr_add_change_request_str((uint8_t*) buf->buf, (size_t*) &(buf->len), change_ip, change_port); } if (padding) { if(stun_attr_add_padding_str((uint8_t*) 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; uint8_t *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((uint8_t *) 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((uint8_t *) 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; uint8_t 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 uint8_t*)local_addr_string, 0, local_addr)<0) { err(-1,NULL); } } if (!first) *local_port=-1; *rfc5780 = 0; if (make_ioa_addr((const uint8_t*)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); bzero(local_addr_string, sizeof(local_addr_string)); 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 uint8_t*)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.2/src/apps/uclient/0000755000175000017500000000000013776656461015647 5ustar misimisiturnserver-4.5.2/src/apps/uclient/mainuclient.c0000644000175000017500000003567413776656461020342 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; uint8_t 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; uint8_t 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); 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); } { uint8_t 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), (uint8_t*)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) { 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 uint8_t*) 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 "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 uint8_t*) "::1", 0, &local_addr) < 0) { return -1; } } else { if (make_ioa_addr((const uint8_t*) "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 uint8_t*) 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 uint8_t* smid_val = stun_attr_get_value(s_mobile_id_sar); if(smid_val) { 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, uint16_t *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, (uint8_t*)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 = (((uint8_t)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; uint8_t 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, (uint8_t*)turn_addr); *turn_port = (uint16_t)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; 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 = ((uint8_t)random())%3; if(t) { uint8_t field[4]; field[0] = (t==1) ? (uint8_t)STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4 : (uint8_t)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; uint8_t 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, (uint16_t)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; uint8_t 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,(uint8_t*)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; uint8_t 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 uint8_t*)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()); uint8_t *u=(uint8_t*)&(arbaddr[i].s4.sin_addr); u[(unsigned short)random()%4] = u[(unsigned short)random()%4] + 1; //char sss[128]; //addr_to_string(&arbaddr[i],(uint8_t*)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 uint8_t*)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()); uint8_t *u=(uint8_t*)&(arbaddr[i].s4.sin_addr); u[(unsigned short)random()%4] = u[(unsigned short)random()%4] + 1; //char sss[128]; //addr_to_string(&arbaddr,(uint8_t*)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 uint8_t*)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()); uint8_t *u=(uint8_t*)&(arbaddr[i].s4.sin_addr); u[(unsigned short)random()%4] = u[(unsigned short)random()%4] + 1; //char sss[128]; //addr_to_string(&arbaddr[i],(uint8_t*)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 uint8_t*)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; uint32_t cid = atc->cid; stun_init_request(STUN_METHOD_CONNECTION_BIND, &request_message); stun_attr_add(&request_message, STUN_ATTRIBUTE_CONNECTION_ID, (const char*)&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; uint8_t 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, uint32_t 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**)realloc(elem->pinfo.tcp_conn,elem->pinfo.tcp_conn_number*sizeof(app_tcp_conn_info*)); elem->pinfo.tcp_conn[i]=(app_tcp_conn_info*)malloc(sizeof(app_tcp_conn_info)); 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.2/src/apps/uclient/session.h0000644000175000017500000000676413776656461017520 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 */ uint32_t 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; uint8_t nonce[STUN_MAX_NONCE_SIZE+1]; uint8_t realm[STUN_MAX_REALM_SIZE+1]; /* oAuth */ int oauth; uint8_t 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; uint32_t refresh_time; uint32_t finished_time; //Msg counters: int tot_msgnum; int wmsgnum; int rmsgnum; int recvmsgnum; uint32_t recvtimems; uint32_t to_send_timems; //Statistics: size_t loss; uint64_t latency; uint64_t jitter; } app_ur_session; /////////////////////////////////////////////////////// typedef struct _message_info { int msgnum; uint64_t mstime; } message_info; /////////////////////////////////////////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__SESSION__ turnserver-4.5.2/src/apps/uclient/startuclient.h0000644000175000017500000000600013776656461020535 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 __STARTCLIENT_TURN__ #define __STARTCLIENT_TURN__ #include "ns_turn_utils.h" #include "session.h" #ifdef __cplusplus extern "C" { #endif ///////////////////////////////////////////////////////// int rare_event(void); int not_rare_event(void); void add_origin(stun_buffer *message); int start_c2c_connection(uint16_t clnet_remote_port, const char *remote_address, 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); int start_connection(uint16_t clnet_remote_port, const char *remote_address, 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); int turn_tcp_connect(int verbose, app_ur_conn_info *clnet_info, ioa_addr *peer_addr); void tcp_data_connect(app_ur_session *elem, uint32_t cid); int socket_connect(evutil_socket_t clnet_fd, ioa_addr *remote_addr, int *connect_err); int read_mobility_ticket(app_ur_conn_info *clnet_info, stun_buffer *message); //////////////////////////////////////////// #ifdef __cplusplus } #endif #endif //__STARTCLIENT_TURN__ turnserver-4.5.2/src/apps/uclient/uclient.c0000644000175000017500000012634713776656461017473 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 "startuclient.h" #include "ns_turn_utils.h" #include "session.h" #include #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 uint32_t tot_messages=0; static uint32_t tot_send_messages=0; static uint64_t tot_send_bytes = 0; static uint32_t tot_recv_messages=0; static uint64_t tot_recv_bytes = 0; static uint64_t 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 uint64_t current_time = 0; static uint64_t 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 int64_t time_minus(uint64_t t1, uint64_t t2) { return ( (int64_t)t1 - (int64_t)t2 ); } static uint64_t total_loss = 0; static uint64_t total_jitter = 0; static uint64_t total_latency = 0; static uint64_t min_latency = 0xFFFFFFFF; static uint64_t max_latency = 0; static uint64_t min_jitter = 0xFFFFFFFF; static uint64_t max_jitter = 0; static int show_statistics = 0; /////////////////////////////////////////////////////////////////////////////// static void __turn_getMSTime(void) { static uint64_t 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 != (uint64_t)((uint64_t)(tp.tv_sec)-start_sec)) show_statistics = 1; current_time = (uint64_t)((uint64_t)(tp.tv_sec)-start_sec); current_mstime = (uint64_t)((current_time * 1000) + (tp.tv_nsec/1000000)); } //////////////////////////////////////////////////////////////////// static int refresh_channel(app_ur_session* elem, uint16_t method, uint32_t lt); //////////////////////// SS //////////////////////////////////////// static app_ur_session* init_app_session(app_ur_session *ss) { if(ss) { 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*) 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; } free(cdi->pinfo.tcp_conn[i]); cdi->pinfo.tcp_conn[i]=NULL; } } } cdi->pinfo.tcp_conn_number=0; if(cdi->pinfo.tcp_conn) { free(cdi->pinfo.tcp_conn); 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]=(uint8_t)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; uint16_t 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; uint16_t 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; uint8_t 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) { bcopy((elem->in_buffer.buf), &mi, sizeof(message_info)); miset=1; } else { /* TODO: make a more clean fix */ buffers=(int)elem->in_buffer.len / clmessage_length; } } 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)); uint32_t cid = 0; while(sar) { int attr_type = stun_attr_get_type(sar); if(attr_type == STUN_ATTRIBUTE_CONNECTION_ID) { cid = *((const uint32_t*)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,(uint64_t)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 uint8_t* data = stun_attr_get_value(sar); 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)); uint32_t cid = 0; while(sar) { int attr_type = stun_attr_get_type(sar); if(attr_type == STUN_ATTRIBUTE_CONNECTION_ID) { cid = *((const uint32_t*)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; } 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 { uint64_t clatency = (uint64_t)time_minus(current_mstime,mi.mstime); if(clatency>max_latency) max_latency = clatency; if(clatencylatency += clatency; if(elem->rmsgnum>0) { uint64_t 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 */ 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 */ 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, uint16_t 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 = ((uint8_t)random())%3; if(t) { uint8_t field[4]; field[0] = (t==1) ? (uint8_t)STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4 : (uint8_t)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**)malloc(sizeof(app_ur_session)*((mclient*2)+1)+sizeof(void*)); __turn_getMSTime(); uint32_t 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()) { uint64_t 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 + ((uint32_t)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) { uint16_t 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; uint8_t 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 uint8_t*)etoken.token, (int)etoken.size); 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), (uint8_t*)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.2/src/ns_turn_defs.h0000644000175000017500000001312013776656461016100 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.2" #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 #if defined(__APPLE__) || defined(__DARWIN__) || defined(__MACH__) #define __APPLE_USE_RFC_3542 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define nswap16(s) ntohs(s) #define nswap32(ul) ntohl(ul) #define nswap64(ull) ioa_ntoh64(ull) static inline uint64_t _ioa_ntoh64(uint64_t v) { #if BYTE_ORDER == LITTLE_ENDIAN uint8_t *src = (uint8_t*) &v; uint8_t* dst = src + 7; while (src < dst) { uint8_t 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 #define BUFFEREVENT_FREE(be) do { if(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 uint32_t turn_time_t; #define turn_time_before(t1,t2) ((((int32_t)(t1))-((int32_t)(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.2/netarch.txt0000644000175000017500000001177513776656461014652 0ustar misimisi Coturn architecture, part 1 Network architecture I. INTRODUCTION This document assumes that the reader is familiar with the various TURN specifications. The goal of this document is to provide general information for the Coturn administrators and code developers about organization of the network interaction in Coturn. Coturn is a TURN relay server that has several general types of main network interaction: 1) Session establishment and maintenance negotiations with the client application. 2) Accepting packets to be relayed from the Client application, on the client-facing sockets, and relaying those packets, through the relay sockets, to the Peer application. 3) Accepting packets to be relayed from the Peer application, on the peer-facing relay sockets, and relaying those packets, through the Client sockets, to the Client application. There are other, secondary, interactions: 1) Communications with the database servers. 2) Communications with the telnet admin console. 3) Communications with the client admin browser, over HTTPS. This document concentrates on the main network communications. It will describe how those communicatiuons are organized in the Coturn code. The key to the understanding how Coturn works is the notions of "listeners" and "general relay servers". II. LISTENERS In Coturn, a "listener" is the entity that initiates dialog with the new client. When a new client sends its first packet to TURN, then it is initially accepted by the UDP listener (the code in dtls_listener.c) or by TCP listener (the code in tls_listener.c). The listeners are smart enough to recognize whether the new session is a TLS session or "plain" protocol session, and it handles necessary SSL keys and negotiations. The listener then creates a client endpoint (depending on the protocol and on the "network engine" - see below). What happens next depends on the "network engine" that the Coturn is using in runtime. If the relay server that will be handling that session is located in a different thread, then the listener will "send" the endpoint to that relay server (see the "connect_cb" callback function). If the relay server is located in the same thread as the listener, then the listener will call the session establishment function itself. See the function open_client_connection_session() and where and how it is called in various cases, for reference. The listeners (and the relay servers) configuration is initiated in the function setup_server() in netengine.c. First, setup_listener() creates the necessary generic data structures for the listeners. Second, network-engine-specific functions associate listeners with the execution threads and with the relay servers. There may be multiple listeners in the server, and they may be running in different threads. III. RELAY SERVERS The relay servers take control over the client sessions after the initial contact was established by the listeners. The relay server will be reading the session sockets (the client and the relay sockets) and perform the necessary actions on them, according to the TURN specs. There can be multiple relay servers in the system, running in different threads. The client sessions are distributed among them in fairly random manner, for load balancing. The relay server will be responsible for the session as long as the session exists. It will exclusively handle all session communications. Thus, the session will stay within the same thread for its lifetime. The performance benefit is that there will be no CPU context switching when the session packets are handled. There is one exception when a relay server will transfer a session to another relay server: the mobility functionality. When the client address changes, it may require that the session must be using a different thread - and a different relay server, as the result. The the original relay server will have to pack the session, say "farewell" to it and ship it to another relay server. The destination relay server will adopt the session and the session will stay with the new relay server - until the next client address change. IV. NETWORK ENGINES UDP communications are rather under-developed, comparing to the TCP communications, in modern operational systems. Because TURN stresses UDP communications, UDP performance is very important. Different OS's have different capabilities, so Coturn, being a portable server, had to employ different strategies for different systems. There are three "network engines" (or rather "network threading patterns") implemented in Coturn: 1) UDP listener thread per frontend IP (FreeBSD, Solaris) with multiple UDP/TCP relay servers. Listeners and relays are in different threads. //TODO 2) UDP listener and relay thread per frontend IP, with multiple TCP relay threads (early Linux). The listener and the relay servers are related, form pairs and are working in the same thread. //TODO 3) Multiple UDP and TCP listeners and relay per each frontend IP (advanced Linuxes). The listener and the relay servers are related, form pairs and are working in the same thread. //TODO turnserver-4.5.2/TODO0000644000175000017500000000521313776656461013143 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.2/ChangeLog0000644000175000017500000014010113776656461014221 0ustar misimisi10/01/2021 Oleg Moskalenko Mihály Mészáros Version 4.5.2 'dan Eider': - fix null pointer dereference in case of out of memory. (thanks to Thomas Moeller for the report) - merge PR #517 (by wolmi) * add prometheus metrics - merge PR #637 (by David Florness) * Delete trailing whitespace in example configuration files - merge PR #631 (by Debabrata Deka) * Add architecture ppc64le to travis build - merge PR #627 (by Samuel) * Fix misleading option in doc (prometheus) - merge PR #643 (by tupelo-schneck) * Allow RFC6062 TCP relay data to look like TLS - merge PR #655 (by plinss) * Add support for proxy protocol V1 - merge PR #618 (by Paul Wayper) * Print full date and time in logs * Add new options: "new-log-timestamp" and "new-log-timestamp-format" - merge PR #599 (by Cédric Krier) * Do not use FIPS and remove hardcode OPENSSL_VERSION_NUMBER with LibreSSL - update Docker mongoDB and fix with workaround the missing systemctl - merge PR #660 (by Camden Narzt) * fix compilation on macOS Big Sur - merge PR #546 (by jelmd) * Add ACME redirect url - merge PR #551 (by jelmd) * support of --acme-redirect - merge PR #672 further acme fixes (by jemld) * fix acme security, redundancy, consistency - Disable binding request logging to avoid DoS attacks. (Breaking change!) * Add new --log-binding option to enable binding request logging - Fix stale-nonce documentation. Resolves #604 - Version number is changed to semver 2.0 - Merge PR #288 (by Hristo Venev) * pkg-config, and various cleanups in configure file - Add systemd notification for better systemd integration - Fix Issue #621 (by ycaibb) * Fix: Null pointer dereference on tcp_client_input_handler_rfc6062data function - Fix Issue #600 (by ycaibb) * Fix: use-after-free vulnerability on write_to_peerchannel function - Fix Issue #601 (by ycaibb) * Fix: use-after-free vulnerability on write_client_connection function - Little refactoring prometheus * Fix c++ support * Simplify (as agreed in Issue #666) * Remove session id/allocation labels * Remove per session metrics. We should later add more counters. - Fix CVE-2020-26262 (credits: Enable-Security) * Fix ipv6 ::1 loopback check * Not allow allocate peer address 0.0.0.0/8 and ::/128 * For more details see the github security advisory: https://github.com/coturn/coturn/security/advisories/GHSA-6g6j-r9rf-cm7p 24/06/2020 Oleg Moskalenko Mihály Mészáros Version 4.5.1.3 'dan Eider': - merge PR #575: (by osterik) * fix rpm packaging - merge PR #576: (by osterik) * tell tar to not include the metadata into release - merge PR #574: (by DevRockstarZ) * change Docker turnserver.conf to latest turnserver.conf - merge PR #566: (by bpcurse) * Remove reference to SSLv3 - merge PR #579: (by islamoglus) *Ignore MD5 for BoringSSL - merge PR #577: (by osterik) *build RPM from local folder instead of git repo - Fix for CVE-2020-4067 * STUN response buffer not initialized properly * The issue found and reported #583 by Felix Dörre all credits belongs to him. 30/04/2020 Oleg Moskalenko Mihály Mészáros Version 4.5.1.2 'dan Eider': - merge regression fix: (by Mathieu Brunot) * Do not display empty CLI passwd alert if CLI is not enabled - merge PR #359: (by bradleythughes) * Remove turn_free_simple * Remove turn_malloc() * Remote turn_realloc() * Remote turn_free() * Remove turn_calloc() * Remove turn_strdup() * Remove SSL_NEW() and SSL_FREE() * Remove pointer debugging machinery * Remove ns_bzero(), ns_bcopy(), and ns_bcmp() * Remove [su]{08,16,32,64}bits type defines - merge PR #327 (by Alexander Terczka) * Strip white-spaces from config file lines end - merge PR #386 (by Thibaut ACKERMANN) * fix the webadmin ip permission add/delete sql injection - merge PR #390 (by Thibaut ACKERMANN) * fix mongo driver crash when invalid connection string is used - merge PR #392 enhanced fread return length check (by islamoglus) - merge PR #367 disconnect database gracefully (by Shu Muto) - merge PR #382 (by islamoglus) * Using SSL_get_version method for BoringSSL compatibility * Now we put in turn_session_info->tls_method the real TLS version. Earlier we put UNKNOWN in this field if it was a TLS protocol that was not defined supportel TLS protocol during compile time. - merge PR #276 Add systemd service example (by Liberasys) - merge PR #284 Add bandwidth usage reporting packet/bandwidth usage by peers - merge PR #381 Modifying configure to enable compile with private libraries - merge PR #455 Typo corrected (by chanduthedev) - merge PR #417 Append only to log files rather to override them (by robert-scheck) - merge PR #442 Updated incorrect string length check for 'ssh' (by chanduthedev) - merge PR #449 Fix Dockerfile for latest Debian (by rao-donut) - http server NULL dereference * Reported (by quarkslab.com, cisco/talos) * CVE-2020-6061 / TALOS-2020-0984 - http server out of bound read * Reported (by quarkslab.com, cisco/talos) * CVE-2020-6061 / TALOS-2020-0984 - merge PR #472 STUN input validation (by bobsayshilol) - merge PR #398 FIPS (by byronclark) - merge PR #478 prod (by alepolidori) - merge PR #463 fix typos and grammar (by xthursdayx) - update travis config ubuntu/mac images - merge PR #466 added null check for second char (by chanduthedev) - merge PR #470 compiler warning fixes (by bobsayshilol) - merge PR #475 Update README.docker (by raksonibs) - merge PR #471 Fix a memory leak when an SHATYPE isn't supported (by bobsayshilol) - merge PR #488 Fix typos about INSTALL filenames (by raccoonback) - fix compiler warning comparison between signed and unsigned integer expressions - fix compiler warning string truncation - change Diffie Hellman default key length from 1066 to 2066 - merge PR #522 drop of supplementary group IDs (by weberhofer) - merge PR #514 Unify spelling of Coturn (by paulmenzel) - merge PR#506 Rename "prod" config option to "no-software-attribute" (by dbrgn) - merge PR #519 fix config extension in README.docker (by ooookai) - merge PR #516 change sql data dir in docker-compose-all.yml (by raghumuppa) - merge PR #513 remove trailing spaces from READMEs (by paulmenzel) - merge PR #525 add flags to disable periodic use of dynamic tables (by gfodor) 02/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.2/rpm/0000755000175000017500000000000013776656461013250 5ustar misimisiturnserver-4.5.2/rpm/turnserver.sysconfig0000644000175000017500000000006413776656461017415 0ustar misimisi# # TURN Server startup options # EXTRA_OPTIONS="" turnserver-4.5.2/rpm/common.pre.build.sh0000755000175000017500000000066613776656461016772 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.2/rpm/uninstall.turnserver.sh0000755000175000017500000000040213776656461020032 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.2/rpm/epel6.install.sh0000755000175000017500000000117413776656461016272 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.2/rpm/turnserver.service.fc0000644000175000017500000000105213776656461017436 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=notify EnvironmentFile=/etc/sysconfig/turnserver ExecStart=/usr/bin/turnserver -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.2/rpm/turnserver.spec0000644000175000017500000004014513776656461016347 0ustar misimisiName: turnserver Version: 4.5.2 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/cacert.pem %{_datadir}/%{name}/etc/coturn.service %{_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 * Sun jan 10 2 2021 Mészáros Mihály - Sync to 4.5.2 * 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.2/rpm/build.instructions.txt0000644000175000017500000000352213776656461017655 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.2/rpm/CentOS7.pre.build.sh0000755000175000017500000000074113776656461016716 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.2/rpm/Fedora.pre.build.sh0000755000175000017500000000051013776656461016666 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.2/rpm/build.settings.sh0000755000175000017500000000024013776656461016541 0ustar misimisi#!/bin/bash # Common settings script. TURNVERSION=4.5.2 BUILDDIR=~/rpmbuild ARCH=`uname -p` WGETOPTIONS="--no-check-certificate" RPMOPTIONS="-ivh --force" turnserver-4.5.2/rpm/CentOS6.pre.build.sh0000755000175000017500000000377413776656461016726 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.2/rpm/turnserver-tmpfiles.conf0000644000175000017500000000005713776656461020161 0ustar misimisid /run/turnserver 0750 turnserver turnserver - turnserver-4.5.2/rpm/epel7.install.sh0000755000175000017500000000011513776656461016265 0ustar misimisi#!/bin/bash # Epel installation script sudo yum -y install epel-release turnserver-4.5.2/rpm/turnserver.init.el0000644000175000017500000000260213776656461016753 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.2/rpm/build.sh0000755000175000017500000000367513776656461014721 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 #create archive from local folder cd ${BUILDDIR}/tmp rm -rf turnserver-${TURNVERSION} mkdir -p ${BUILDDIR}/tmp/turnserver-${TURNVERSION} cp -R ${CPWD}/.. ${BUILDDIR}/tmp/turnserver-${TURNVERSION} tar zcf ${BUILDDIR}/SOURCES/turnserver-${TURNVERSION}.tar.gz turnserver-${TURNVERSION} ER=$? if ! [ ${ER} -eq 0 ] ; then cd ${CPWD} exit -1 fi #build package from archive 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.2/AUTHORS0000644000175000017500000000245413776656461013527 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.2/README.turnadmin0000644000175000017500000001620113776656461015332 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 format 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 REST 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 Mihály Mészáros ACTIVE MAINTAINERS Mihály Mészáros turnserver-4.5.2/INSTALL0000644000175000017500000012505313776656461013511 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. XXV. Prometheus support. See for source and releases for debian packages: https://github.com/digitalocean/prometheus-client-c turnserver-4.5.2/README.turnutils0000644000175000017500000003176013776656461015411 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 Mihály Mészáros ACTIVE MAINTAINERS Mihály Mészáros turnserver-4.5.2/Makefile.in0000755000175000017500000002537513776656461014536 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 src/apps/relay/acme.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 src/apps/relay/prom_server.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 src/apps/relay/prom_server.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.2/turndb/0000755000175000017500000000000013776656461013750 5ustar misimisiturnserver-4.5.2/turndb/schema.stats.redis0000644000175000017500000000422313776656461017376 0ustar misimisiRedis database for allocation statuses and for event notifications has the following schema: 1) The allocation status is stored as keys "turn/user//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 Additionally peer traffic is available with the key "turn/user//allocation//traffic/peer". The application that is interested in the peer traffic information must subscribe to the events as: psubscribe turn/realm/*/user/*/allocation/*/traffic/peer Total traffic information is also reported when the allocation is deleted. The keys are "turn/user//allocation//total_traffic" or "turn/user//allocation//total_traffic/peer". Applications interested in the total amount of traffic per allocation can subscribe to these events as: psubscribe turn/realm/*/user/*/allocation/*/total_traffic psubscribe turn/realm/*/user/*/allocation/*/total_traffic/peer 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.2/turndb/testsqldbsetup.sql0000644000175000017500000000535313776656461017565 0ustar misimisi insert into turnusers_lt (realm, name, hmackey) values('north.gov','ninefingers','bc807ee29df3c9ffa736523fb2c4e8ee'); insert into turnusers_lt (realm, name, hmackey) values('north.gov','gorst','7da2270ccfa49786e0115366d3a3d14d'); insert into turnusers_lt (realm, name, hmackey) values('crinna.org','whirrun','6972e85e51f36e53b0b61759c5a5219a'); insert into turnusers_lt (realm, name, hmackey) values('crinna.org','stranger-come-knocking','d43cb678560259a1839bff61c19de15e'); insert into turn_secret (realm,value) values('north.gov','logen'); insert into turn_secret (realm,value) values('north.gov','bloody9'); insert into turn_secret (realm,value) values('crinna.org','north'); insert into turn_secret (realm,value) values('crinna.org','library'); insert into admin_user (name, realm, password) values('skarling','north.gov','$5$6fc35c3b0c7d4633$27fca7574f9b79d0cb93ae03e45379470cbbdfcacdd6401f97ebc620f31f54f2'); insert into admin_user (name, realm, password) values('bayaz','','$5$e018513e9de69e73$5cbdd2e29e04ca46aeb022268a7460d3a3468de193dcb2b95f064901769f455f'); insert into turn_origin_to_realm (origin,realm) values('http://crinna.org:80','crinna.org'); insert into turn_origin_to_realm (origin,realm) values('https://bligh.edu:443','crinna.org'); insert into turn_realm_option (realm,opt,value) values('north.gov','max-bps','500000'); insert into turn_realm_option (realm,opt,value) values('crinna.org','max-bps','400000'); insert into turn_realm_option (realm,opt,value) values('north.gov','total-quota','12000'); insert into turn_realm_option (realm,opt,value) values('crinna.org','total-quota','10000'); insert into turn_realm_option (realm,opt,value) values('north.gov','user-quota','10000'); insert into turn_realm_option (realm,opt,value) values('crinna.org','user-quota','8000'); insert into allowed_peer_ip (ip_range) values('172.17.13.200'); insert into allowed_peer_ip (realm,ip_range) values('north.gov','172.17.13.201'); insert into allowed_peer_ip (realm,ip_range) values('crinna.org','172.17.13.202'); insert into denied_peer_ip (ip_range) values('172.17.13.133-172.17.14.56'); insert into denied_peer_ip (ip_range) values('123::45'); insert into denied_peer_ip (realm,ip_range) values('north.gov','172.17.17.133-172.17.19.56'); insert into denied_peer_ip (realm,ip_range) values('crinna.org','123::77'); insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg,realm) values('north','MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEK',0,0,'A256GCM','crinna.org'); insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg,realm) values('union','MTIzNDU2Nzg5MDEyMzQ1Ngo=',0,0,'A128GCM','north.gov'); insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg,realm) values('oldempire','MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIK',0,0,'A256GCM',''); turnserver-4.5.2/turndb/schema.sql0000644000175000017500000000203213776656461015726 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.2/turndb/testmongosetup.sh0000755000175000017500000000512013776656461017405 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 mongo $* </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 <>/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 } # testpkg pkg1 pkg2 ... # If all libraries are found, sets PKG_CFLAGS/PKG_LIBS and returns success. # Otherwise, returns failure. testpkg() { PKG_LIBS="$($PKGCONFIG --libs "$@" 2>/dev/null)" if [ $? -ne 0 ] ; then return 1 fi PKG_CFLAGS="$($PKGCONFIG --cflags "$@")" } # testpkg_db pkg1 pkg2 ... # If all libraries are found, adds them to DBCFLAGS/DBLIBS and returns success. # Otherwise, returns failure. testpkg_db() { testpkg "$@" || return $? DBCFLAGS="${DBCFLAGS} ${PKG_CFLAGS}" DBLIBS="${DBLIBS} ${PKG_LIBS}" } # testpkg_common pkg1 pkg2 ... # If all libraries are found, adds them to OSCFLAGS/OSLIBS and returns success. # Otherwise, returns failure. testpkg_common() { testpkg "$@" || return $? OSCFLAGS="${OSCFLAGS} ${PKG_CFLAGS}" OSLIBS="${OSLIBS} ${PKG_LIBS}" } testlib() { testlibraw l${1} } pthread_testlib() { if [ -n "${PTHREAD_LIBS}" ] ; then OSLIBS="${OSLIBS} ${PTHREAD_LIBS}" return fi if [ "$(uname)" = "DragonFly" ] ; then OSLIBS="${OSLIBS} -pthread" TURN_NO_SCTP=1 return fi if [ -n "$(uname | grep -i bsd)" ] ; then OSLIBS="${OSLIBS} -pthread" return fi ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then return fi ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} -pthread 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then OSLIBS="${OSLIBS} -pthread" return fi ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} -lpthread 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then OSLIBS="${OSLIBS} -lpthread" return fi ${CC} ${TH_TMPCPROGC} -o ${TH_TMPCPROGB} ${OSCFLAGS} ${OSLIBS} 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then return fi ${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 fi ${ECHO_CMD} "Do not use pthreads" return 1 } 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="${CFLAGS}" OSLIBS="${LDFLAGS}" 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 # If acme_redirect does not work, send_data_from_ioa_socket_nbh() probably # does not work. Set LIBEV_OK=1 to use a workaround for it. if [ -z "${LIBEV_OK}" ]; then LIBEV_OK=1 if [ "${SYSTEM}" = "Linux" ]; then OS=$( lsb_release -si 2>/dev/null ) [ "${OS}" = "Ubuntu" ] && LIBEV_OK=0 fi fi [ "${LIBEV_OK}" = "1" ] && OSCFLAGS="${OSCFLAGS} -DLIBEV_OK" ########################### # 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]); } ! ########################## # What is our compiler ? ########################## if [ -z "${CC}" ] ; then for CC in cc gcc clang; do ${CC} ${TMPCPROGC} ${OSCFLAGS} -o ${TMPCPROGB} 2>>/dev/null ER=$? if [ ${ER} -eq 0 ] ; then break fi CC= done if [ -z "$CC" ] ; then ${ECHO_CMD} "ERROR: failed to a find working C compiler" cleanup exit fi else ${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 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 ########################## # Which pkg-config? ########################## if [ -z "$PKGCONFIG" ] ; then for PKGCONFIG in pkgconf pkg-config ; do if type "$PKGCONFIG" 2>/dev/null ; then break fi PKGCONFIG= done if [ -z "$PKGCONFIG" ] ; then ${ECHO_CMD} "ERROR: pkg-config not found" cleanup exit fi else if ! type "$PKGCONFIG" 2>/dev/null ; then ${ECHO_CMD} "ERROR: cannot use $PKGCONFIG" cleanup exit fi fi ${ECHO_CMD} "pkg-config: $PKGCONFIG" ########################### # 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} -ne 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 ########################### if [ -n "${SSL_CFLAGS}" ] && [ -n "${SSL_LIBS}" ]; then ${CC} ${TMPCPROGC} ${SSL_CFLAGS} -o ${TMPCPROGB} ${OSCFLAGS} ${OSLIBS} ${SSL_LIBS} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Private SSL Library option cannot be used" exit else OSCFLAGS="${OSCFLAGS} ${SSL_CFLAGS}" OSLIBS="${OSLIBS} ${SSL_LIBS}" fi else if testpkg_common libcrypto; then ${ECHO_CMD} "OpenSSL Crypto lib found." else testlib crypto ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "OpenSSL Crypto lib found." else ${ECHO_CMD} "ERROR: OpenSSL Crypto development libraries are not installed properly in required location." ${ECHO_CMD} "Abort." cleanup exit fi fi if testpkg_common libssl; then ${ECHO_CMD} "OpenSSL lib found." else testlib ssl ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "OpenSSL lib found." else ${ECHO_CMD} "ERROR: OpenSSL development libraries are not installed properly in required location." ${ECHO_CMD} "Abort." cleanup exit fi fi 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 ########################### if [ -n "${EVENT_CFLAGS}" ] && [ -n "${EVENT_LIBS}" ]; then ${CC} ${TMPCPROGC} ${EVENT_CFLAGS} -o ${TMPCPROGB} ${OSCFLAGS} ${OSLIBS} ${EVENT_LIBS} 2>>/dev/null ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Private Event Library option cannot be used" exit else OSCFLAGS="${OSCFLAGS} ${EVENT_CFLAGS}" OSLIBS="${OSLIBS} ${EVENT_LIBS}" fi else if testpkg_common libevent_core libevent_extra libevent_openssl libevent_pthreads || testpkg_common libevent libevent_openssl libevent_pthreads; then ${ECHO_CMD} "Libevent2 runtime 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 fi ########################### # Test Prometheus ########################### if [ -z "${TURN_NO_PROMETHEUS}" ] ; then testlib prom ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Prometheus lib found." testlib promhttp ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Prometheus http lib found." testlib microhttpd ER=$? if ! [ ${ER} -eq 0 ] ; then ${ECHO_CMD} "Microhttpd lib found." else ${ECHO_CMD} ${ECHO_CMD} "Warning: microhttpd development libraries are not installed properly in required location." ${ECHO_CMD} "Prometheus support will be disabled." ${ECHO_CMD} "See the INSTALL file." ${ECHO_CMD} OSCFLAGS="${OSCFLAGS} -DTURN_NO_PROMETHEUS" fi else ${ECHO_CMD} ${ECHO_CMD} "Warning: Libpromhttp development libraries are not installed properly in required location." ${ECHO_CMD} "Prometheus support will be disabled." ${ECHO_CMD} "See the INSTALL file." ${ECHO_CMD} OSCFLAGS="${OSCFLAGS} -DTURN_NO_PROMETHEUS" fi else ${ECHO_CMD} ${ECHO_CMD} "Warning: Libprom development libraries are not installed properly in required location." ${ECHO_CMD} "Prometheus support will be disabled." ${ECHO_CMD} "See the INSTALL file." ${ECHO_CMD} OSCFLAGS="${OSCFLAGS} -DTURN_NO_PROMETHEUS" fi else OSCFLAGS="${OSCFLAGS} -DTURN_NO_PROMETHEUS" fi ########################### # Test libsystemd ########################### if [ -z "${TURN_NO_SYSTEMD}" ] ; then if testpkg_common libsystemd; then ${ECHO_CMD} "Systemd library found." else ${ECHO_CMD} "Systemd library not found. Building without systemd support." TURN_NO_SYSTEMD="-DTURN_NO_SYSTEMD" fi else TURN_NO_SYSTEMD="-DTURN_NO_SYSTEMD" fi ########################### # Test SQLite3 setup ########################### if [ -z "${TURN_NO_SQLITE}" ] ; then if testpkg_db sqlite3; then ${ECHO_CMD} "SQLite3 library found." else ${ECHO_CMD} "SQLite3 development library not found. Building without SQLite3 support." TURN_NO_SQLITE="-DTURN_NO_SQLITE" 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 if testpkg_db libpq; then ${ECHO_CMD} "PostgreSQL found." else ${ECHO_CMD} "PostgreSQL not found. Building without PostgreSQL support." TURN_NO_PQ="-DTURN_NO_PQ" fi else TURN_NO_PQ="-DTURN_NO_PQ" fi ########################### # Test MySQL ########################### if [ -z "${TURN_NO_MYSQL}" ] ; then if testpkg_db mariadb || testpkg_db mysqlclient ; then ${ECHO_CMD} "MySQL found." else ${ECHO_CMD} "MySQL not found. Building without MySQL support." TURN_NO_MYSQL="-DTURN_NO_MYSQL" fi else TURN_NO_MYSQL="-DTURN_NO_MYSQL" fi ########################### # Test MongoDB ########################### if [ -z "${TURN_NO_MONGO}" ] ; then if testpkg_db libmongoc-1.0; then ${ECHO_CMD} "MongoDB found." else ${ECHO_CMD} "MongoDB not found. Building without MongoDB support." TURN_NO_MONGO="-DTURN_NO_MONGO" fi else TURN_NO_MONGO="-DTURN_NO_MONGO" fi ########################### # Test Redis ########################### if [ -z "${TURN_NO_HIREDIS}" ] ; then if testpkg_db hiredis; then ${ECHO_CMD} "Hiredis found." else ${ECHO_CMD} "Hiredis not found. Building without hiredis support." 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} ${TURN_NO_SYSTEMD}" >> 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.2/README.md0000644000175000017500000001425713776656461013742 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. By default a [prometheus](https://prometheus.io/) exporter endpoint is disabled, if it is enabeled it will listen on port 9641 under path /metrics 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:misi@majd.eu mom040267@gmail.com ### Feedback is very welcome (bugs, issues, suggestions, stories, questions). ### ### Volunteers are welcome, too. ### turnserver-4.5.2/postinstall.txt0000644000175000017500000000267513776656461015601 0ustar misimisi================================================================== 1) If your system supports automatic start-up system daemon services, then to enable the turnserver as a system service that is automatically started, you have to: a) Create and edit /etc/turnserver.conf or /usr/local/etc/turnserver.conf . Use /usr/local/etc/turnserver.conf.default as an example. b) For user accounts settings: set up SQLite or PostgreSQL or MySQL or MongoDB or Redis database for user accounts. Use /usr/local/share/turnserver/schema.sql as SQL database schema, or use /usr/local/share/turnserver/schema.userdb.redis as Redis database schema description and/or /usr/local/share/turnserver/schema.stats.redis as Redis status & statistics database schema description. If you are using SQLite, the default database location is in /var/db/turndb or in /usr/local/var/db/turndb or in /var/lib/turn/turndb. c) add whatever is necessary to enable start-up daemon for the /usr/local/bin/turnserver. 2) If you do not want the turnserver to be a system service, then you can start/stop it "manually", using the "turnserver" executable with appropriate options (see the documentation). 3) To create database schema, use schema in file /usr/local/share/turnserver/schema.sql. 4) For additional information, run: $ man turnserver $ man turnadmin $ man turnutils ================================================================== turnserver-4.5.2/NOTE0000644000175000017500000000010513776656461013136 0ustar misimisiThis project is active in Github: https://github.com/coturn/coturn/ turnserver-4.5.2/docker/0000755000175000017500000000000013776656461013721 5ustar misimisiturnserver-4.5.2/docker/docker-compose-postgresql.yml0000644000175000017500000000233713776656461021564 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.2/docker/docker-compose-all.yml0000644000175000017500000000367413776656461020136 0ustar misimisiversion: "3" services: # MySQL mariadb mysql: build: context: ./mysql restart: unless-stopped volumes: - mysql-data:/var/lib/mysql env_file: - mysql/mysql.env networks: - backend # PostgreSQL postgresql: build: context: ./postgresql restart: unless-stopped volumes: - postgresql-data:/var/lib/postgresql 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.2/docker/redis/0000755000175000017500000000000013776656461015027 5ustar misimisiturnserver-4.5.2/docker/redis/redis.conf0000644000175000017500000000006113776656461017001 0ustar misimisitimeout 0 tcp-keepalive 60 requirepass CHANGE_ME turnserver-4.5.2/docker/redis/Dockerfile0000644000175000017500000000022613776656461017021 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.2/docker/redis/redis.env0000644000175000017500000000000013776656461016635 0ustar misimisiturnserver-4.5.2/docker/docker-compose-mysql.yml0000644000175000017500000000226013776656461020521 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.2/docker/docker-compose-redis.yml0000644000175000017500000000223213776656461020461 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.2/docker/mongodb/0000755000175000017500000000000013776656461015346 5ustar misimisiturnserver-4.5.2/docker/mongodb/mongodb.env0000644000175000017500000000014713776656461017507 0ustar misimisi#MONGO_INITDB_ROOT_USERNAME=coturn #MONGO_INITDB_ROOT_PASSWORD=CHANGE_ME #MONGO_INITDB_DATABASE=coturn turnserver-4.5.2/docker/README.docker0000644000175000017500000000077113776656461016054 0ustar misimisiBefore you begin * copy db schema run ./cp-schema.sh * edit coturn/turnserver.conf 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.2/docker/cp-schema.sh0000755000175000017500000000011713776656461016117 0ustar misimisi#!/bin/bash cp ../turndb/schema.sql mysql/ cp ../turndb/schema.sql postgresql/ turnserver-4.5.2/docker/docker-compose-mongodb.yml0000644000175000017500000000223613776656461021004 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.2/docker/mysql/0000755000175000017500000000000013776656461015066 5ustar misimisiturnserver-4.5.2/docker/mysql/schema.sql0000644000175000017500000000203213776656461017044 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.2/docker/mysql/mysql.env0000644000175000017500000000014113776656461016741 0ustar misimisiMYSQL_ROOT_PASSWORD=CHANGE_ME MYSQL_USER=coturn MYSQL_PASSWORD=CHANGE_ME MYSQL_DATABASE=coturn turnserver-4.5.2/docker/mysql/Dockerfile0000644000175000017500000000021413776656461017055 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.2/docker/mysql/init-coturn-db.sql0000644000175000017500000000005413776656461020444 0ustar misimisiALTER DATABASE coturn CHARACTER SET latin1; turnserver-4.5.2/docker/coturn/0000755000175000017500000000000013776656461015233 5ustar misimisiturnserver-4.5.2/docker/coturn/coturn.env0000644000175000017500000000002313776656461017252 0ustar misimisi# for future usage turnserver-4.5.2/docker/coturn/Dockerfile0000644000175000017500000000563213776656461017233 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 pkg-config libssl-dev libevent-dev sqlite3 libsqlite3-dev postgresql-client libpq-dev default-mysql-client default-libmysqlclient-dev libhiredis-dev libmongoc-dev libbson-dev libsystemd-dev # Clone Coturn WORKDIR ${BUILD_PREFIX} RUN git clone https://github.com/coturn/coturn.git # Build Coturn WORKDIR ${BUILD_PREFIX}/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 libevent-core-2.1-6 libevent-extra-2.1-6 libevent-openssl-2.1-6 libevent-pthreads-2.1-6 libhiredis0.14 libmariadbclient-dev libpq5 libsqlite3-0 libssl1.1 libmongoc-1.0-0 libbson-1.0-0 RUN apt-get install -y default-mysql-client postgresql-client redis-tools # Workaround for MongoDB RUN ln -s /bin/echo /bin/systemctl # Install MongoDB RUN apt-get update && \ apt-get install -y wget gnupg && \ wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | apt-key add - && \ echo "deb http://repo.mongodb.org/apt/debian stretch/mongodb-org/4.4 main" | tee /etc/apt/sources.list.d/mongodb-org-4.4.list && \ echo "deb http://deb.debian.org/debian/ stretch main" | tee /etc/apt/sources.list.d/debian-stretch.list && \ apt-get update && \ apt-get install -y libcurl3 mongodb-org mongodb-org-server mongodb-org 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.2/docker/coturn/turnserver.conf0000644000175000017500000006366413776656461020340 0ustar misimisi# Coturn TURN SERVER configuration file # # Boolean values note: where a boolean value is supposed to be used, # you can use '0', 'off', 'no', 'false', or 'f' as 'false, # and you can use '1', 'on', 'yes', 'true', or 't' as 'true' # If the value is missing, then it means 'true' by default. # # 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 Coturn keeps both endpoints to satisfy the RFC 5766 specs. # For secure TCP connections, Coturn currently supports SSL version 3 and # TLS version 1.0, 1.1 and 1.2. # For secure UDP connections, Coturn supports 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 # Some network setups will require using a TCP reverse proxy in front # of the STUN server. If the proxy port option is set a single listener # is started on the given port that accepts connections using the # haproxy proxy protocol v2. # (https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) # #tcp-proxy-port=5555 # 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=193.224.22.37 # #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 optimal, so you have to change this option # 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 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 the opposite of 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, 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, then the timestamp alone can be used. # This option is enabled by turning on secret-based authentication. # The actual value of the secret is defined either by the 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 parts of lt-cred-mech. # The use-auth-secret feature depends internally on lt-cred-mech, so if you set # this option then it automatically enables lt-cred-mech internally # as if you had enabled both. # # Note that you can use only one auth mechanism at the same time! This is because, # both mechanisms conduct username and password validation in different ways. # # Use either lt-cred-mech or use-auth-secret in the conf # 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 the turn_secret table # in the user database (if present). The database-stored value can be changed on-the-fly # by a separate program, so this is why that mode is considered '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 the 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 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. # # The 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 you are using PostgreSQL # as the user database. # This database can be used for the long-term credential mechanism # and it can store the secret value for secret-based timed authentication in TURN REST 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 you are using MySQL # as the user database. # This database can be used for the long-term credential mechanism # and it can store the secret value for secret-based timed authentication in TURN REST 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 the string format below (space separated parameters, all optional): # mysql-userdb="host=mysql dbname=coturn user=coturn password=CHANGE_ME port=3306 connect_timeout=10 read_timeout=10" # If you want to use an encrypted password in the MySQL connection string, # then set the MySQL password encryption secret key file with this option. # # Warning: If this option is set, then the mysql password must be set in "mysql-userdb" in an encrypted format! # If you want to use a cleartext password then do not set this option! # # This is the file path for the aes encrypted secret key used for password encryption. # #secret-key-file=/path/ # MongoDB database connection string in the case that you 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 REST API. # Use the string format 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 you 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 REST API. # Use the string format 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 the string format 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 is 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 the default realm is not specified, then realm falls back to the host domain name. # If the domain name string is empty, or set to '(None)', then it is initialized as an empty string. # realm=example.org # This flag 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 temporarily 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 a limited lifetime. # The nonce value is unique for a session. # Set this option to limit the nonce lifetime. # Set it to 0 for unlimited 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. # Use PEM file format. # 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=/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 this 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 2066. # #dh566 # Use 1066 bits predefined DH TLS key. Default size of the key is 2066. # #dh1066 # 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 go to both stdout and to # the configured log file. With this option everything will # go 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 the current directory # (Whichever file open operation succeeds first 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 the 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 the description of ALTERNATE-SERVER functionality. # The client must use the obtained value for subsequent TURN communications. # If more than one --alternate-server option is 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 you 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 hide software version. Enhance security when used in production. # Revealing the specific software version of the agent through the # SOFTWARE attribute might allow them to become more vulnerable to # attacks against software that is known to contain security holes. # Implementers SHOULD make usage of the SOFTWARE attribute a # configurable option (https://tools.ietf.org/html/rfc5389#section-16.1.2) # #no-software-attribute # 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 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 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 that you use the encrypted # form 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=CHANGE_ME # 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 you 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.2/docker/postgresql/0000755000175000017500000000000013776656461016124 5ustar misimisiturnserver-4.5.2/docker/postgresql/schema.sql0000644000175000017500000000203213776656461020102 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.2/docker/postgresql/Dockerfile0000644000175000017500000000013113776656461020111 0ustar misimisi### init db with coturn schema FROM postgres ADD schema.sql /docker-entrypoint-initdb.d turnserver-4.5.2/docker/postgresql/postgresql.env0000644000175000017500000000010413776656461021034 0ustar misimisiPOSTGRES_USER=coturn POSTGRES_PASSWORD=CHANGE_ME POSTGRES_DB=coturn turnserver-4.5.2/README.turnserver0000644000175000017500000012637013776656461015561 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 SYSTEMD If the systemd development library is available, then it will notify systemd about the server status. 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 REST 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 REST 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 REST 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 REST 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. --no-software-attribute 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 2066. --dh1066 Use 1066 bits predefined DH TLS key. Default size of the key is 2066. --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. --new-log-timestamp Enable full ISO-8601 timestamp in all logs. --new-log-timestamp-format Set timestamp format (in strftime(1) format) --log-binding Log STUN binding request. It is now disabled by default to avoid DoS attacks. --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). --prometheus Enable prometheus metrics. By default it is disabled. Would listen on port 9641 unther the path /metrics also the path / on this port can be used as a health check -h Help. Options with values: --stale-nonce[=] Use extra security with nonce value having limited lifetime, in seconds (default 600 secs). Set it to 0 for unlimited nonce lifetime. --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". --tcp-proxy-port Support connections from TCP loadbalancer on this port. The loadbalancer should use the binary proxy protocol. (https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) --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). --no-auth-pings Disable periodic health checks to 'dynamic' auth secret tables. --no-dynamic-ip-list Do not use dynamic allowed/denied peer ip list. --no-dynamic-realms Do not use dynamic realm assignment and options. --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 --dh1066 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 . --acme-redirect Redirect ACME/RFC8555 (like Let's Encrypt challenge) requests, i.e. HTTP GET requests matching '^/.well-known/acme-challenge/(.*)' to $1 with $1 == (.*). No validation of will be done, so make sure you do not forget the trailing slash. If is an empty string (the default value), no special handling of such requests will be done. --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 since 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 server 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 Mihály Mészáros ACTIVE MAINTAINERS Mihály Mészáros turnserver-4.5.2/examples/0000755000175000017500000000000013776656461014270 5ustar misimisiturnserver-4.5.2/examples/var/0000755000175000017500000000000013776656461015060 5ustar misimisiturnserver-4.5.2/examples/var/db/0000755000175000017500000000000013776656461015445 5ustar misimisiturnserver-4.5.2/examples/var/db/turndb0000644000175000017500000005400013776656461016665 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_lt97!++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-quota10000U9 ll!!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 atableoauth_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 44_^ 5bayaz$5$e018513e9de69e73$5cbdd2e29e04ca46aeb022268a7460d3a3468de193dcb2b95f064901769f455fj5skarlingnorth.gov$5$6fc35c3b0c7d4633$27fca7574f9b79d0cb93ae03e45379470cbbdfcacdd6401f97ebc620f31f54f2  bayaz  skarlingturnserver-4.5.2/examples/etc/0000755000175000017500000000000013776656461015043 5ustar misimisiturnserver-4.5.2/examples/etc/turn_client_cert.pem0000777000175000017500000000000013776656461025752 2../ca/turn_client_cert.pemustar misimisiturnserver-4.5.2/examples/etc/turn_client_pkey.pem0000777000175000017500000000000013776656461026000 2../ca/turn_client_pkey.pemustar misimisiturnserver-4.5.2/examples/etc/coturn.service0000644000175000017500000000072713776656461017745 0ustar misimisi[Unit] Description=Coturn STUN/TURN Server Documentation=man:coturn(1) man:turnadmin(1) man:turnserver(1) After=network.target After=network-online.target After=remote-fs.target Wants=network-online.target [Service] User=turnserver Group=turnserver Type=notify RuntimeDirectory=turnserver ExecStart=/usr/bin/turnserver -c /etc/turnserver.conf Restart=on-failure InaccessibleDirectories=/home PrivateTmp=yes [Install] WantedBy=multi-user.target Alias=turnserver.service turnserver-4.5.2/examples/etc/turn_server_cert.pem0000777000175000017500000000000013776656461026032 2../ca/turn_server_cert.pemustar misimisiturnserver-4.5.2/examples/etc/cacert.pem0000777000175000017500000000000013776656461022031 2../ca/CA/cacert.pemustar misimisiturnserver-4.5.2/examples/etc/turnserver.conf0000644000175000017500000006573313776656461020147 0ustar misimisi# Coturn TURN SERVER configuration file # # Boolean values note: where a boolean value is supposed to be used, # you can use '0', 'off', 'no', 'false', or 'f' as 'false, # and you can use '1', 'on', 'yes', 'true', or 't' as 'true' # If the value is missing, then it means 'true' by default. # # 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 Coturn keeps both endpoints to satisfy the RFC 5766 specs. # For secure TCP connections, Coturn currently supports # TLS version 1.0, 1.1 and 1.2. # For secure UDP connections, Coturn supports 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 # Some network setups will require using a TCP reverse proxy in front # of the STUN server. If the proxy port option is set a single listener # is started on the given port that accepts connections using the # haproxy proxy protocol v2. # (https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) # #tcp-proxy-port=5555 # 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 optimal, so you have to change this option # 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 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 the opposite of 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, in command line or in usersdb file, then # lt-cred-mech is default. # #no-auth # Enable prometheus exporter # If enabled the turnserver will expose an endpoint with stats on a prometheus format # this endpoint is listening on a different port to not conflict with other configurations. # # You can simply run the turnserver and access the port 9641 and path /metrics # # For mor info on the prometheus exporter and metrics # https://prometheus.io/docs/introduction/overview/ # https://prometheus.io/docs/concepts/data_model/ # #prometheus # 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, then the timestamp alone can be used. # This option is enabled by turning on secret-based authentication. # The actual value of the secret is defined either by the 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 parts of lt-cred-mech. # The use-auth-secret feature depends internally on lt-cred-mech, so if you set # this option then it automatically enables lt-cred-mech internally # as if you had enabled both. # # Note that you can use only one auth mechanism at the same time! This is because, # both mechanisms conduct username and password validation in different ways. # # Use either lt-cred-mech or use-auth-secret in the conf # 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 the turn_secret table # in the user database (if present). The database-stored value can be changed on-the-fly # by a separate program, so this is why that mode is considered '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 the 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 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. # # The 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 you are using PostgreSQL # as the user database. # This database can be used for the long-term credential mechanism # and it can store the secret value for secret-based timed authentication in TURN REST 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 you are using MySQL # as the user database. # This database can be used for the long-term credential mechanism # and it can store the secret value for secret-based timed authentication in TURN REST 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 the string format below (space separated parameters, all optional): # #mysql-userdb="host= dbname= user= password= port= connect_timeout= read_timeout=" # If you want to use an encrypted password in the MySQL connection string, # then set the MySQL password encryption secret key file with this option. # # Warning: If this option is set, then the mysql password must be set in "mysql-userdb" in an encrypted format! # If you want to use a cleartext password then do not set this option! # # This is the file path for the aes encrypted secret key used for password encryption. # #secret-key-file=/path/ # MongoDB database connection string in the case that you 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 REST API. # Use the string format 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 you 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 REST API. # Use the string format 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 the string format 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 is 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 the default realm is not specified, then realm falls back to the host domain name. # If the domain name string is empty, or set to '(None)', then it is initialized as an empty string. # #realm=mycompany.org # This flag 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 temporarily 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 a limited lifetime. # The nonce value is unique for a session. # Set this option to limit the nonce lifetime. # Set it to 0 for unlimited 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. # Use PEM file format. # #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 this 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 2066. # #dh566 # Use 1066 bits predefined DH TLS key. Default size of the key is 2066. # #dh1066 # 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 go to both stdout and to # the configured log file. With this option everything will # go 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 the current directory # (Whichever file open operation succeeds first 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 # Enable full ISO-8601 timestamp in all logs. #new-log-timestamp # Set timestamp format (in strftime(1) format) #new-log-timestamp-format "%FT%T%z" # Disabled by default binding logging in verbose log mode to avoid DoS attacks. # Enable binding logging and UDP endpoint logs in verbose log mode. #log-binding # Option to set the "redirection" mode. The value of this option # will be the address of the alternate server for UDP & TCP service in the 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 the description of ALTERNATE-SERVER functionality. # The client must use the obtained value for subsequent TURN communications. # If more than one --alternate-server option is 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 you 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 hide software version. Enhance security when used in production. # Revealing the specific software version of the agent through the # SOFTWARE attribute might allow them to become more vulnerable to # attacks against software that is known to contain security holes. # Implementers SHOULD make usage of the SOFTWARE attribute a # configurable option (https://tools.ietf.org/html/rfc5389#section-16.1.2) # #no-software-attribute # 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 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 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 that you use the encrypted # form 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 #acme-redirect=http://redirectserver/.well-known/acme-challenge/ # Redirect ACME, i.e. HTTP GET requests matching '^/.well-known/acme-challenge/(.*)' to '$1'. # Default is '', i.e. no special handling for such requests. # Server relay. NON-STANDARD AND DANGEROUS OPTION. # Only for those applications when you 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.2/examples/etc/turn_server_pkey.pem0000777000175000017500000000000013776656461026060 2../ca/turn_server_pkey.pemustar misimisiturnserver-4.5.2/examples/scripts/0000755000175000017500000000000013776656461015757 5ustar misimisiturnserver-4.5.2/examples/scripts/oauth.sh0000755000175000017500000000277113776656461017445 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.2/examples/scripts/longtermsecuredb/0000755000175000017500000000000013776656461021323 5ustar misimisiturnserver-4.5.2/examples/scripts/longtermsecuredb/secure_relay_with_db_mongo.sh0000755000175000017500000000331213776656461027242 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.2/examples/scripts/longtermsecuredb/secure_relay_with_db_mysql.sh0000755000175000017500000000376613776656461027305 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.2/examples/scripts/longtermsecuredb/secure_relay_with_db_redis.sh0000755000175000017500000000422613776656461027236 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.2/examples/scripts/longtermsecuredb/secure_relay_with_db_psql.sh0000755000175000017500000000457413776656461027115 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.2/examples/scripts/longtermsecuredb/secure_relay_with_db_sqlite.sh0000755000175000017500000000334613776656461027433 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.2/examples/scripts/longtermsecuredb/secure_relay_with_db_mysql_ssl.sh0000755000175000017500000000417113776656461030155 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.2/examples/scripts/mobile/0000755000175000017500000000000013776656461017226 5ustar misimisiturnserver-4.5.2/examples/scripts/mobile/mobile_tcp_client.sh0000755000175000017500000000254313776656461023244 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.2/examples/scripts/mobile/mobile_tls_client_c2c_tcp_relay.sh0000755000175000017500000000312013776656461026041 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.2/examples/scripts/mobile/mobile_udp_client.sh0000755000175000017500000000266113776656461023247 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.2/examples/scripts/mobile/mobile_dtls_client.sh0000755000175000017500000000304513776656461023422 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.2/examples/scripts/mobile/mobile_relay.sh0000755000175000017500000000376513776656461022243 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.2/examples/scripts/loadbalance/0000755000175000017500000000000013776656461020204 5ustar misimisiturnserver-4.5.2/examples/scripts/loadbalance/slave_relay_2.sh0000755000175000017500000000353613776656461023301 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.2/examples/scripts/loadbalance/master_relay.sh0000755000175000017500000000363113776656461023235 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.2/examples/scripts/loadbalance/tcp_c2c_tcp_relay.sh0000755000175000017500000000227113776656461024124 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.2/examples/scripts/loadbalance/slave_relay_1.sh0000755000175000017500000000353613776656461023300 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.2/examples/scripts/loadbalance/udp_c2c.sh0000755000175000017500000000247213776656461022067 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.2/examples/scripts/peer.sh0000755000175000017500000000071513776656461017254 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.2/examples/scripts/restapi/0000755000175000017500000000000013776656461017426 5ustar misimisiturnserver-4.5.2/examples/scripts/restapi/secure_udp_client_with_secret.sh0000755000175000017500000000246713776656461026072 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.2/examples/scripts/restapi/secure_relay_secret_with_db_redis.sh0000755000175000017500000000422413776656461026704 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.2/examples/scripts/restapi/shared_secret_maintainer.pl0000755000175000017500000000416013776656461025011 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.2/examples/scripts/restapi/secure_relay_secret.sh0000755000175000017500000000364513776656461024024 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.2/examples/scripts/restapi/secure_relay_secret_with_db_sqlite.sh0000755000175000017500000000364713776656461027107 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.2/examples/scripts/restapi/secure_relay_secret_with_db_psql.sh0000755000175000017500000000423513776656461026557 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.2/examples/scripts/restapi/secure_relay_secret_with_db_mongo.sh0000755000175000017500000000373013776656461026716 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.2/examples/scripts/restapi/secure_relay_secret_with_db_mysql.sh0000755000175000017500000000422613776656461026745 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.2/examples/scripts/selfloadbalance/0000755000175000017500000000000013776656461021056 5ustar misimisiturnserver-4.5.2/examples/scripts/selfloadbalance/secure_relay.sh0000755000175000017500000000461413776656461024104 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.2/examples/scripts/selfloadbalance/secure_dos_attack.sh0000755000175000017500000002503613776656461025105 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.2/examples/scripts/basic/0000755000175000017500000000000013776656461017040 5ustar misimisiturnserver-4.5.2/examples/scripts/basic/relay.sh0000755000175000017500000000235113776656461020514 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.2/examples/scripts/basic/udp_client.sh0000755000175000017500000000223713776656461021531 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.2/examples/scripts/basic/tcp_client_c2c_tcp_relay.sh0000755000175000017500000000201213776656461024307 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.2/examples/scripts/basic/dos_attack.sh0000755000175000017500000000155613776656461021522 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.2/examples/scripts/basic/udp_c2c_client.sh0000755000175000017500000000230413776656461022253 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.2/examples/scripts/basic/tcp_client.sh0000755000175000017500000000213713776656461021526 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.2/examples/scripts/rfc5769.sh0000755000175000017500000000055113776656461017424 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.2/examples/scripts/readme.txt0000644000175000017500000000217713776656461017764 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.2/examples/scripts/pack.sh0000755000175000017500000000061613776656461017237 0ustar misimisi#!/bin/sh # Run it from the root of the coturn source tree V=4.5.2 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}/ #tell tar to not include the metadata COPYFILE_DISABLE=1 tar cvfz ../${DDIR}.tar.gz ${DDIR} cd .. rm -rf tmp cp -a ${SRCDIR}/ChangeLog ${PACKDIR} turnserver-4.5.2/examples/scripts/longtermsecure/0000755000175000017500000000000013776656461021015 5ustar misimisiturnserver-4.5.2/examples/scripts/longtermsecure/secure_dtls_client.sh0000755000175000017500000000276013776656461025233 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.2/examples/scripts/longtermsecure/secure_tls_client_c2c_tcp_relay.sh0000755000175000017500000000277713776656461027670 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.2/examples/scripts/longtermsecure/secure_tls_client_cert.sh0000755000175000017500000000310313776656461026074 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 cacert.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.2/examples/scripts/longtermsecure/secure_tcp_client.sh0000755000175000017500000000244113776656461025047 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.2/examples/scripts/longtermsecure/secure_dtls_client_cert.sh0000755000175000017500000000311613776656461026244 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 cacert.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.2/examples/scripts/longtermsecure/secure_tls_client.sh0000755000175000017500000000262013776656461025062 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.2/examples/scripts/longtermsecure/secure_relay.sh0000755000175000017500000000360613776656461024043 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.2/examples/scripts/longtermsecure/secure_udp_c2c.sh0000755000175000017500000000262213776656461024243 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.2/examples/scripts/longtermsecure/secure_sctp_client.sh0000755000175000017500000000265013776656461025234 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.2/examples/scripts/longtermsecure/secure_dos_attack.sh0000755000175000017500000000314513776656461025041 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.2/examples/scripts/longtermsecure/secure_udp_client.sh0000755000175000017500000000254013776656461025051 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.2/examples/scripts/longtermsecure/secure_tcp_client_c2c_tcp_relay.sh0000755000175000017500000000243713776656461027645 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.2/examples/scripts/longtermsecure/secure_relay_cert.sh0000755000175000017500000000403713776656461025057 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=cacert.pem --log-file=stdout -v --cipher-list="ALL:!eNULL:!aNULL:!NULL" --cli-password=secret --db=var/db/turndb $@ turnserver-4.5.2/examples/ca/0000755000175000017500000000000013776656461014653 5ustar misimisiturnserver-4.5.2/examples/ca/run.sh0000755000175000017500000000047613776656461016025 0ustar misimisi#!/bin/bash #set -x # key passwd: coTURN cp /usr/lib/ssl/misc/CA.pl ./CA.pl patch < CA.pl.diff export OPENSSL_CONFIG="-config openssl.conf" ./CA.pl -newca for i in "server" "client"; do ./CA.pl -newreq-nodes ./CA.pl -signCA mv newcert.pem turn_${i}_cert.pem mv newkey.pem turn_${i}_pkey.pem rm newreq.pem done; turnserver-4.5.2/examples/ca/openssl.conf0000644000175000017500000002563513776656461017220 0ustar misimisi# # OpenSSL example configuration file. # This is mostly being used for generation of certificate requests. # # Note that you can include other files from the main configuration # file using the .include directive. #.include filename # This definition stops the following lines choking if HOME isn't # defined. HOME = . # Extra OBJECT IDENTIFIER info: #oid_file = $ENV::HOME/.oid oid_section = new_oids # System default openssl_conf = default_conf # To use this configuration file with the "-extfile" option of the # "openssl x509" utility, name here the section containing the # X.509v3 extensions to use: # extensions = # (Alternatively, use a configuration file that has only # X.509v3 extensions in its main [= default] section.) [ new_oids ] # We can add new OIDs in here for use by 'ca', 'req' and 'ts'. # Add a simple OID like this: # testoid1=1.2.3.4 # Or use config file substitution like this: # testoid2=${testoid1}.5.6 # Policies used by the TSA examples. tsa_policy1 = 1.2.3.4.1 tsa_policy2 = 1.2.3.4.5.6 tsa_policy3 = 1.2.3.4.5.7 #################################################################### [ ca ] default_ca = CA_default # The default ca section #################################################################### [ CA_default ] dir = ./CA # Where everything is kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crl are kept database = $dir/index.txt # database index file. #unique_subject = no # Set to 'no' to allow creation of # several certs with same subject. new_certs_dir = $dir/newcerts # default place for new certs. certificate = $dir/cacert.pem # The CA certificate serial = $dir/serial # The current serial number crlnumber = $dir/crlnumber # the current crl number # must be commented out to leave a V1 CRL crl = $dir/crl.pem # The current CRL private_key = $dir/private/cakey.pem# The private key x509_extensions = usr_cert # The extensions to add to the cert # Comment out the following two lines for the "traditional" # (and highly broken) format. name_opt = ca_default # Subject Name options cert_opt = ca_default # Certificate field options # Extension copying option: use with caution. # copy_extensions = copy # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs # so this is commented out by default to leave a V1 CRL. # crlnumber must also be commented out to leave a V1 CRL. # crl_extensions = crl_ext default_days = 3650 # how long to certify for default_crl_days= 30 # how long before next CRL default_md = default # use public key default MD preserve = no # keep passed DN ordering # A few difference way of specifying how similar the request should look # For type CA, the listed attributes must be the same, and the optional # and supplied fields are just that :-) policy = policy_match # For the CA policy [ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional # For the 'anything' policy # At this point in time, you must list all acceptable 'object' # types. [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional #################################################################### [ req ] default_bits = 2048 default_keyfile = privkey.pem distinguished_name = req_distinguished_name attributes = req_attributes x509_extensions = v3_ca # The extensions to add to the self signed cert # Passwords for private keys if not present they will be prompted for # input_password = secret # output_password = secret # This sets a mask for permitted string types. There are several options. # default: PrintableString, T61String, BMPString. # pkix : PrintableString, BMPString (PKIX recommendation before 2004) # utf8only: only UTF8Strings (PKIX recommendation after 2004). # nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). # MASK:XXXX a literal mask value. # WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. string_mask = utf8only # req_extensions = v3_req # The extensions to add to a certificate request [ req_distinguished_name ] countryName = Country Name (2 letter code) countryName_default = HU countryName_min = 2 countryName_max = 2 stateOrProvinceName = State or Province Name (full name) stateOrProvinceName_default = Hungary localityName = Locality Name (eg, city) localityName_default = Debrecen 0.organizationName = Organization Name (eg, company) 0.organizationName_default = coTURN # we can do this but it is not needed normally :-) #1.organizationName = Second Organization Name (eg, company) #1.organizationName_default = World Wide Web Pty Ltd #organizationalUnitName = Organizational Unit Name (eg, section) #organizationalUnitName_default = commonName = Common Name (e.g. server FQDN or YOUR name) commonName_max = 64 emailAddress = Email Address emailAddress_default = misi@majd.eu emailAddress_max = 64 # SET-ex3 = SET extension number 3 [ req_attributes ] #challengePassword = A challenge password #challengePassword_min = 4 #challengePassword_max = 20 #unstructuredName = An optional company name [ usr_cert ] # These extensions are added when 'ca' signs a request. # This goes against PKIX guidelines but some CAs do it and some software # requires this to avoid interpreting an end user certificate as a CA. basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. # nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign # For normal client use this is typical # nsCertType = client, email # and for everything including object signing: # nsCertType = client, email, objsign # This is typical in keyUsage for a client certificate. # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. nsComment = "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer # This stuff is for subjectAltName and issuerAltname. # Import the email address. # subjectAltName=email:copy # An alternative to produce certificates that aren't # deprecated according to PKIX. # subjectAltName=email:move # Copy subject details # issuerAltName=issuer:copy #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem #nsBaseUrl #nsRevocationUrl #nsRenewalUrl #nsCaPolicyUrl #nsSslServerName # This is required for TSA certificates. # extendedKeyUsage = critical,timeStamping [ v3_req ] # Extensions to add to a certificate request basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment [ v3_ca ] # Extensions for a typical CA # PKIX recommendation. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer basicConstraints = critical,CA:true # Key usage: this is typical for a CA certificate. However since it will # prevent it being used as an test self-signed certificate it is best # left out by default. # keyUsage = cRLSign, keyCertSign # Some might want this also # nsCertType = sslCA, emailCA # Include email address in subject alt name: another PKIX recommendation # subjectAltName=email:copy # Copy issuer details # issuerAltName=issuer:copy # DER hex encoding of an extension: beware experts only! # obj=DER:02:03 # Where 'obj' is a standard or added object # You can even override a supported extension: # basicConstraints= critical, DER:30:03:01:01:FF [ crl_ext ] # CRL extensions. # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. # issuerAltName=issuer:copy authorityKeyIdentifier=keyid:always [ proxy_cert_ext ] # These extensions should be added when creating a proxy certificate # This goes against PKIX guidelines but some CAs do it and some software # requires this to avoid interpreting an end user certificate as a CA. basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. # nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign # For normal client use this is typical # nsCertType = client, email # and for everything including object signing: # nsCertType = client, email, objsign # This is typical in keyUsage for a client certificate. # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. nsComment = "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer # This stuff is for subjectAltName and issuerAltname. # Import the email address. # subjectAltName=email:copy # An alternative to produce certificates that aren't # deprecated according to PKIX. # subjectAltName=email:move # Copy subject details # issuerAltName=issuer:copy #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem #nsBaseUrl #nsRevocationUrl #nsRenewalUrl #nsCaPolicyUrl #nsSslServerName # This really needs to be in place for it to be a proxy certificate. proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo #################################################################### [ tsa ] default_tsa = tsa_config1 # the default TSA section [ tsa_config1 ] # These are used by the TSA reply generation only. dir = ./CA # TSA root directory serial = $dir/tsaserial # The current serial number (mandatory) crypto_device = builtin # OpenSSL engine to use for signing signer_cert = $dir/tsacert.pem # The TSA signing certificate # (optional) certs = $dir/cacert.pem # Certificate chain to include in reply # (optional) signer_key = $dir/private/tsakey.pem # The TSA private key (optional) signer_digest = sha256 # Signing digest to use. (Optional) default_policy = tsa_policy1 # Policy if request did not specify it # (optional) other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) digests = sha1, sha256, sha384, sha512 # Acceptable message digests (mandatory) accuracy = secs:1, millisecs:500, microsecs:100 # (optional) clock_precision_digits = 0 # number of digits after dot. (optional) ordering = yes # Is ordering defined for timestamps? # (optional, default: no) tsa_name = yes # Must the TSA name be included in the reply? # (optional, default: no) ess_cert_id_chain = no # Must the ESS cert id chain be included? # (optional, default: no) ess_cert_id_alg = sha1 # algorithm to compute certificate # identifier (optional, default: sha1) [default_conf] ssl_conf = ssl_sect [ssl_sect] system_default = system_default_sect [system_default_sect] MinProtocol = TLSv1.2 CipherString = DEFAULT@SECLEVEL=2 turnserver-4.5.2/examples/ca/turn_client_cert.pem0000644000175000017500000001056213776656461020725 0ustar misimisiCertificate: Data: Version: 3 (0x2) Serial Number: 4c:9b:ec:95:d1:21:49:1d:5d:65:a7:1a:61:46:67:dd:42:18:65:48 Signature Algorithm: sha256WithRSAEncryption Issuer: C=HU, ST=Hungary, O=coTURN, CN=CA/emailAddress=misi@majd.eu Validity Not Before: Mar 5 09:05:42 2020 GMT Not After : Mar 3 09:05:42 2030 GMT Subject: C=HU, ST=Hungary, L=Debrecen, O=coTURN, CN=Client/emailAddress=misi@majd.eu Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:af:6d:38:31:23:12:12:e7:5a:8d:ed:1c:02:7e: bf:c2:ef:7a:d1:c0:b2:4b:b4:38:9b:a7:5d:dd:01: 2c:a0:e7:7c:5b:7a:4d:71:4b:c9:5b:77:e8:b3:4c: 92:5b:8c:43:57:b6:c9:8c:44:66:6a:9e:8c:f2:76: 58:a2:f5:38:a3:4f:ef:af:5a:c7:bf:e5:72:98:c0: b8:2e:a1:75:cc:16:8b:bf:a3:6a:e6:fd:c9:25:35: 92:31:b2:78:2a:42:7b:a1:ce:25:be:32:45:6e:0b: 36:22:f8:6c:9c:f3:8f:bf:c8:8c:79:d5:59:02:f5: de:1f:67:fc:ef:c7:27:88:a7:35:b1:d7:ee:dc:1c: 74:11:fc:3c:56:33:b5:e7:88:ce:f3:ce:db:b9:3c: e0:eb:15:bc:00:5f:29:f4:9c:8e:4d:61:df:da:aa: f4:fc:fb:e7:4b:75:dc:dc:cf:f0:4b:3b:67:cf:bf: 35:b8:0f:5b:20:94:60:dd:3b:e5:7a:ec:0e:30:2c: c1:fb:f6:21:5b:ed:80:34:9d:59:5c:95:39:a2:61: a4:13:fa:57:b9:f5:85:d4:a1:bf:91:cf:d7:dc:ac: fa:32:47:ee:d2:86:9b:14:d1:35:88:1e:2d:9f:39: 74:86:de:f1:04:de:e1:39:2f:a8:91:bf:8b:f7:4f: 7c:e5 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 32:BA:14:26:42:B6:5B:9E:3C:F1:53:1A:FD:DB:CB:FE:B1:A2:74:6C X509v3 Authority Key Identifier: keyid:1C:27:5E:40:39:8C:EC:71:C7:ED:E9:2A:56:C9:9E:DF:48:EA:82:42 X509v3 Basic Constraints: critical CA:TRUE Signature Algorithm: sha256WithRSAEncryption 6b:93:56:56:81:fb:34:9e:15:2e:3e:b2:2c:73:72:60:f2:1a: a8:bf:c3:f0:c7:57:00:48:37:2a:1c:63:71:1b:29:f4:2b:dc: 64:07:f8:72:80:65:18:c7:74:23:c1:02:00:d8:93:1d:4f:2b: 8c:46:34:1e:d2:6a:5c:ab:8d:ff:a7:fe:e5:c2:bf:33:55:ea: 2b:e2:70:e9:24:4c:4d:31:d4:dd:10:55:f5:bb:2c:a5:ec:f6: 8f:7a:05:1c:6c:7d:cf:85:6b:29:a7:bd:fe:a2:bc:00:45:b8: ac:70:c7:c9:67:93:0a:5c:d7:52:a3:c9:fc:6c:ef:52:b2:6b: bc:5b:f9:e1:9b:27:07:39:28:28:7f:a0:70:62:af:4f:42:82: dd:ec:23:4d:fc:8e:19:51:87:cc:d0:29:d5:27:44:9c:fa:b5: 51:ea:31:eb:51:84:3f:07:5b:c0:57:5d:2a:c7:15:ed:9c:46: ac:8e:14:8b:4d:82:0e:b4:6a:47:db:37:f3:03:08:86:b6:25: 0b:92:6d:99:a9:99:45:4e:38:45:e0:a2:4e:e7:34:50:51:ab: f8:c8:ef:26:3d:7f:9f:8f:45:20:cf:f5:31:27:b6:00:3a:e0: 4a:d5:62:9a:29:27:9b:aa:3a:95:56:1c:d7:65:15:ce:35:10: 2a:7e:cc:b6 -----BEGIN CERTIFICATE----- MIIDrDCCApSgAwIBAgIUTJvsldEhSR1dZacaYUZn3UIYZUgwDQYJKoZIhvcNAQEL BQAwWjELMAkGA1UEBhMCSFUxEDAOBgNVBAgMB0h1bmdhcnkxDzANBgNVBAoMBmNv VFVSTjELMAkGA1UEAwwCQ0ExGzAZBgkqhkiG9w0BCQEWDG1pc2lAbWFqZC5ldTAe Fw0yMDAzMDUwOTA1NDJaFw0zMDAzMDMwOTA1NDJaMHExCzAJBgNVBAYTAkhVMRAw DgYDVQQIDAdIdW5nYXJ5MREwDwYDVQQHDAhEZWJyZWNlbjEPMA0GA1UECgwGY29U VVJOMQ8wDQYDVQQDDAZDbGllbnQxGzAZBgkqhkiG9w0BCQEWDG1pc2lAbWFqZC5l dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK9tODEjEhLnWo3tHAJ+ v8LvetHAsku0OJunXd0BLKDnfFt6TXFLyVt36LNMkluMQ1e2yYxEZmqejPJ2WKL1 OKNP769ax7/lcpjAuC6hdcwWi7+jaub9ySU1kjGyeCpCe6HOJb4yRW4LNiL4bJzz j7/IjHnVWQL13h9n/O/HJ4inNbHX7twcdBH8PFYzteeIzvPO27k84OsVvABfKfSc jk1h39qq9Pz750t13NzP8Es7Z8+/NbgPWyCUYN075XrsDjAswfv2IVvtgDSdWVyV OaJhpBP6V7n1hdShv5HP19ys+jJH7tKGmxTRNYgeLZ85dIbe8QTe4TkvqJG/i/dP fOUCAwEAAaNTMFEwHQYDVR0OBBYEFDK6FCZCtluePPFTGv3by/6xonRsMB8GA1Ud IwQYMBaAFBwnXkA5jOxxx+3pKlbJnt9I6oJCMA8GA1UdEwEB/wQFMAMBAf8wDQYJ KoZIhvcNAQELBQADggEBAGuTVlaB+zSeFS4+sixzcmDyGqi/w/DHVwBINyocY3Eb KfQr3GQH+HKAZRjHdCPBAgDYkx1PK4xGNB7Salyrjf+n/uXCvzNV6ivicOkkTE0x 1N0QVfW7LKXs9o96BRxsfc+Faymnvf6ivABFuKxwx8lnkwpc11Kjyfxs71Kya7xb +eGbJwc5KCh/oHBir09Cgt3sI038jhlRh8zQKdUnRJz6tVHqMetRhD8HW8BXXSrH Fe2cRqyOFItNgg60akfbN/MDCIa2JQuSbZmpmUVOOEXgok7nNFBRq/jI7yY9f5+P RSDP9TEntgA64ErVYpopJ5uqOpVWHNdlFc41ECp+zLY= -----END CERTIFICATE----- turnserver-4.5.2/examples/ca/CA/0000755000175000017500000000000013776656461015136 5ustar misimisiturnserver-4.5.2/examples/ca/CA/index.txt.attr.old0000644000175000017500000000002513776656461020531 0ustar misimisiunique_subject = yes turnserver-4.5.2/examples/ca/CA/index.txt.old0000644000175000017500000000041113776656461017557 0ustar misimisiV 30190707090510Z 4C9BEC95D121491D5D65A71A614667DD42186546 unknown /C=HU/ST=Hungary/O=coTURN/CN=CA/emailAddress=misi@majd.eu V 300303090521Z 4C9BEC95D121491D5D65A71A614667DD42186547 unknown /C=HU/ST=Hungary/L=Debrecen/O=coTURN/CN=Server/emailAddress=misi@majd.eu turnserver-4.5.2/examples/ca/CA/private/0000755000175000017500000000000013776656461016610 5ustar misimisiturnserver-4.5.2/examples/ca/CA/private/cakey.pem0000644000175000017500000000347613776656461020421 0ustar misimisi-----BEGIN ENCRYPTED PRIVATE KEY----- MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIeK2OY7PJbzYCAggA MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECKP+q72oc4q7BIIEyHkaZfqjSX9W HIHqbQtHOMlAtqSxmAyV6C3pXLwNuEpo4cYwyPUdJwMNxm8OjsxuH708daZu5QWl 7EVNV4WY9ff4/4geJAp9ZrqJN5TsgFIUyss5NzHjTMPUz/yunr0Hk5OOVLusTCqF Ys0Qdo2Gy33NZCK53U22pa0S/szppN4DIDujSOuUAiyxJdz12cCUyw/OlAXvDLJb I9oObKWpbYBtJSLk5aWblZDUTVmFWngkTIc76wchBXu7WntLjXdMG2lv4Gy/ozUb vsYvEADNRJFOpYyfWvmEFNKvEcVxfzshnms9TdzhDCmYhmYR+NfamYq5Om+81Pv3 h+z1Zd7x3uYs8NM+DbRKhwHS6jkQCxelWdQbeSJj/Fz9VpWSrJlkmhXI+7qkBCsv DVoz017Y2zK/iM5JRPTH65tnNMeH61Zj4EOHBEzMBE6EvugJcSqPXfBKtVMwVAzV Mva8gtOlMN0Ce9dmG+HZKDek6S++5AbkxuOwRb+YOVXjUrNXXf0YqglM9Nb/RCr4 Z+gkuTCwARJZqjebZnUw1mSZp2R89X774wNDHAlw96tSW2OZlfPmbvXBnwT7QwPm YBZT6CrLL7LEIs0G5zFh1L/PCQi7EyNaE9Ixw52nqc5Ej2M6Rj6XcdCRdw5IKmh/ BbTzD0LxfNh+XKpAIzkuNfGkwUVtfldmfpW3xRKzI1o+rbgDGMA/eEFYWmyE9326 /vsv7daE4zWAG4O5OdGKMKBABCqM92X2YU7bZoNQS25dy7uZsQ8zvkcI1Q1GKMW0 Lg2oDTSTSrPRVgLAcb0o06Frvler5F277OBfBm1+6+7aL3hct4TZjb+0pp5SuxrS 7PpRXMFYzbQ+Z7YrRv6uwrrxVl99Ok/jBGLYT+CllZ+PNvRbcgsy0xUIz6KTbQQZ H4qqkObdKFHQLqfP9+YUwjE2akR/prOR2Dfoq648L/eEF4qpGCADaXFoHODWfiqz VQHvLP4FN4ppYn3jB4lSTIl+7s92XznK5aN5AERRdUIfjPnZB8lQkDP/qwwCI0Ki SRxUtsrMef1biTKL5HI3On2wPLFQCGVEmiQoD8uEqaB/vAdJy5ZdQ3HA547TxLmy TJ6je8QMFUcO3n1pJWeUHuL+WyGrcstOEkZiFQyVpAFFeS7h6u2UI7HyNXGaP1mk +vWulewlMjWHw05qG9wLqEiDkpZgmx4garfWbR2rggBu1Jlg4svS2jdmytuKQ735 E1e5g7TCSzv6sHzdHfQ2WaVvfM5YfxqWpgPhNH2t7rScoLTvI2txyhpIIEIMn+ip tBM15Ai+L92gr4wLJlsBOcKOWSN46ucqQsGla3so0PZAtU4hVPEJ+PzaR2czStUk MzrKfG1qox+JW8BBiW2zV2idKy2440Sn/NSqMyvZgEFn7GDaAcTsZi2FhRLT1Fg+ 2c5viBTaCRdh20QDQQu3skEhbFU5GjeZEqCO25hX5L3BZPnQtwQujc2RU9aGWwPm o/nrp8ilBRI18qFdxfqFEV6ftdVNXlrV+cMgtuwPNX6vnmKWjN67/cDIUML3ab+e 9cx0rBvCBvMn7Q0AvY/RcsVP0DaLmov7ciuvih0ptCgYThov7FJ2V+q+2LbNLwSc qpi/6R+l6bIjP0UITKZlug== -----END ENCRYPTED PRIVATE KEY----- turnserver-4.5.2/examples/ca/CA/index.txt.attr0000644000175000017500000000002513776656461017754 0ustar misimisiunique_subject = yes turnserver-4.5.2/examples/ca/CA/careq.pem0000644000175000017500000000176513776656461016745 0ustar misimisi-----BEGIN CERTIFICATE REQUEST----- MIICsjCCAZoCAQAwbTELMAkGA1UEBhMCSFUxEDAOBgNVBAgMB0h1bmdhcnkxETAP BgNVBAcMCERlYnJlY2VuMQ8wDQYDVQQKDAZjb1RVUk4xCzAJBgNVBAMMAkNBMRsw GQYJKoZIhvcNAQkBFgxtaXNpQG1hamQuZXUwggEiMA0GCSqGSIb3DQEBAQUAA4IB DwAwggEKAoIBAQDYdipZRHPaJTiTVNjFKxG9MIAhX0eVfetePpgNp6gwjAdtGu6J wUzMZIGQs6tUH5tyI8UvCjJSvietL1HuYp7tRNC6qnJnA6LuoONdnjfs7gspWejY 1YShbTZdhWsNc6Ay/rb6me+MeKkC9Dq9E7wam3JVC+cM7WgAwud4St/OFCqZ8d6X FmBE8fz4dOUzMcz5/12ewcfGIXVICCb1fPFW7BXFfyQPCAN04NoQvz2QZwkesj+0 9BXfU+ho6NEoji03+eA6oykAPQpmfHGrVOXa/kQYPLS+xc5JJozMq4iPt+OtW9+y 1KP4qQZPOG63BbM6vWPN9yYV4Jj9MH7TM1aNAgMBAAGgADANBgkqhkiG9w0BAQsF AAOCAQEAmvXWsoJQneJFFHb+qTNjkA3sHduyB+kQ5qUVlFoT6U6IKyWnVUqAKc9a eFKw94yq/01cqOBd4MWKTg9k/wjjmkJA9WtXMrVq8HW1rKVRCCJxtzUKTR3pet/z gs3YwbTlqpljtpn3qEzspMaeyvh391A4IVykDZHGR12+4LqZhoUyGl1QJ7KgQwGM +Vi2TL3fY8PDxvGFmGvWnUIWYkB31vAuDz1xOqm2JlP0kTHMUPiVBlwJVuHdATy2 sWZEzsNnXBt2vAVwhTdFEajF4ut8guPQWW8XcTiaEOGJUIY8J4Yb2wqHk+4HsIFV i2vua41jc90Ki3EA0+QDB7BJAvC4yw== -----END CERTIFICATE REQUEST----- turnserver-4.5.2/examples/ca/CA/cacert.pem0000644000175000017500000001050613776656461017104 0ustar misimisiCertificate: Data: Version: 3 (0x2) Serial Number: 4c:9b:ec:95:d1:21:49:1d:5d:65:a7:1a:61:46:67:dd:42:18:65:46 Signature Algorithm: sha256WithRSAEncryption Issuer: C=HU, ST=Hungary, O=coTURN, CN=CA/emailAddress=misi@majd.eu Validity Not Before: Mar 5 09:05:10 2020 GMT Not After : Jul 7 09:05:10 3019 GMT Subject: C=HU, ST=Hungary, O=coTURN, CN=CA/emailAddress=misi@majd.eu Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:d8:76:2a:59:44:73:da:25:38:93:54:d8:c5:2b: 11:bd:30:80:21:5f:47:95:7d:eb:5e:3e:98:0d:a7: a8:30:8c:07:6d:1a:ee:89:c1:4c:cc:64:81:90:b3: ab:54:1f:9b:72:23:c5:2f:0a:32:52:be:27:ad:2f: 51:ee:62:9e:ed:44:d0:ba:aa:72:67:03:a2:ee:a0: e3:5d:9e:37:ec:ee:0b:29:59:e8:d8:d5:84:a1:6d: 36:5d:85:6b:0d:73:a0:32:fe:b6:fa:99:ef:8c:78: a9:02:f4:3a:bd:13:bc:1a:9b:72:55:0b:e7:0c:ed: 68:00:c2:e7:78:4a:df:ce:14:2a:99:f1:de:97:16: 60:44:f1:fc:f8:74:e5:33:31:cc:f9:ff:5d:9e:c1: c7:c6:21:75:48:08:26:f5:7c:f1:56:ec:15:c5:7f: 24:0f:08:03:74:e0:da:10:bf:3d:90:67:09:1e:b2: 3f:b4:f4:15:df:53:e8:68:e8:d1:28:8e:2d:37:f9: e0:3a:a3:29:00:3d:0a:66:7c:71:ab:54:e5:da:fe: 44:18:3c:b4:be:c5:ce:49:26:8c:cc:ab:88:8f:b7: e3:ad:5b:df:b2:d4:a3:f8:a9:06:4f:38:6e:b7:05: b3:3a:bd:63:cd:f7:26:15:e0:98:fd:30:7e:d3:33: 56:8d Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 1C:27:5E:40:39:8C:EC:71:C7:ED:E9:2A:56:C9:9E:DF:48:EA:82:42 X509v3 Authority Key Identifier: keyid:1C:27:5E:40:39:8C:EC:71:C7:ED:E9:2A:56:C9:9E:DF:48:EA:82:42 X509v3 Basic Constraints: critical CA:TRUE Signature Algorithm: sha256WithRSAEncryption b4:d5:d9:7a:46:1e:1a:95:02:b5:7e:86:45:16:26:d5:8a:11: b9:34:98:58:df:cd:0c:d5:a5:f2:cc:24:1a:22:f4:c7:3e:50: 39:40:f5:d6:e8:3b:9c:05:e9:f9:95:9b:c2:01:3b:69:d5:ba: 4f:cf:7c:a6:7c:6e:f4:24:a3:d1:88:e2:29:60:ca:6d:b0:ee: a6:b8:d1:5f:49:d5:08:a6:c2:79:3a:3f:8a:63:ec:53:ef:48: 00:8c:61:d2:0f:38:e0:00:ac:6d:a6:bf:ed:6a:42:c3:cf:4e: e3:0d:48:c5:a7:6d:5e:af:5a:e4:30:26:ba:19:2a:a5:57:da: ce:b7:b6:45:24:fb:36:b6:a3:6c:55:ca:9f:91:19:29:db:a4: 22:d4:45:53:b9:79:6a:a7:5e:90:a3:4d:3b:c1:b6:2b:52:41: 97:7d:9e:0c:cf:0a:5f:ce:0e:fe:bf:a9:e5:b7:60:17:f5:93: 4b:b5:6d:2d:51:a6:c1:54:65:f9:e1:5c:21:8d:3d:19:0c:dc: 2c:c9:17:40:65:15:d0:ad:98:06:a0:11:aa:87:b3:2d:03:29: 37:24:f6:42:a8:d5:58:ae:55:20:c3:37:a3:62:33:36:34:73: 98:bc:70:30:aa:33:b0:e4:86:b6:d9:22:79:1f:3f:68:6f:f5: 66:75:e8:70 -----BEGIN CERTIFICATE----- MIIDlzCCAn+gAwIBAgIUTJvsldEhSR1dZacaYUZn3UIYZUYwDQYJKoZIhvcNAQEL BQAwWjELMAkGA1UEBhMCSFUxEDAOBgNVBAgMB0h1bmdhcnkxDzANBgNVBAoMBmNv VFVSTjELMAkGA1UEAwwCQ0ExGzAZBgkqhkiG9w0BCQEWDG1pc2lAbWFqZC5ldTAg Fw0yMDAzMDUwOTA1MTBaGA8zMDE5MDcwNzA5MDUxMFowWjELMAkGA1UEBhMCSFUx EDAOBgNVBAgMB0h1bmdhcnkxDzANBgNVBAoMBmNvVFVSTjELMAkGA1UEAwwCQ0Ex GzAZBgkqhkiG9w0BCQEWDG1pc2lAbWFqZC5ldTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBANh2KllEc9olOJNU2MUrEb0wgCFfR5V9614+mA2nqDCMB20a 7onBTMxkgZCzq1Qfm3IjxS8KMlK+J60vUe5inu1E0LqqcmcDou6g412eN+zuCylZ 6NjVhKFtNl2Faw1zoDL+tvqZ74x4qQL0Or0TvBqbclUL5wztaADC53hK384UKpnx 3pcWYETx/Ph05TMxzPn/XZ7Bx8YhdUgIJvV88VbsFcV/JA8IA3Tg2hC/PZBnCR6y P7T0Fd9T6Gjo0SiOLTf54DqjKQA9CmZ8catU5dr+RBg8tL7FzkkmjMyriI+3461b 37LUo/ipBk84brcFszq9Y833JhXgmP0wftMzVo0CAwEAAaNTMFEwHQYDVR0OBBYE FBwnXkA5jOxxx+3pKlbJnt9I6oJCMB8GA1UdIwQYMBaAFBwnXkA5jOxxx+3pKlbJ nt9I6oJCMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBALTV2XpG HhqVArV+hkUWJtWKEbk0mFjfzQzVpfLMJBoi9Mc+UDlA9dboO5wF6fmVm8IBO2nV uk/PfKZ8bvQko9GI4ilgym2w7qa40V9J1Qimwnk6P4pj7FPvSACMYdIPOOAArG2m v+1qQsPPTuMNSMWnbV6vWuQwJroZKqVX2s63tkUk+za2o2xVyp+RGSnbpCLURVO5 eWqnXpCjTTvBtitSQZd9ngzPCl/ODv6/qeW3YBf1k0u1bS1RpsFUZfnhXCGNPRkM 3CzJF0BlFdCtmAagEaqHsy0DKTck9kKo1ViuVSDDN6NiMzY0c5i8cDCqM7DkhrbZ InkfP2hv9WZ16HA= -----END CERTIFICATE----- turnserver-4.5.2/examples/ca/CA/serial0000644000175000017500000000005113776656461016334 0ustar misimisi4C9BEC95D121491D5D65A71A614667DD42186549 turnserver-4.5.2/examples/ca/CA/newcerts/0000755000175000017500000000000013776656461016770 5ustar misimisiturnserver-4.5.2/examples/ca/CA/newcerts/4C9BEC95D121491D5D65A71A614667DD42186548.pem0000644000175000017500000001056213776656461024112 0ustar misimisiCertificate: Data: Version: 3 (0x2) Serial Number: 4c:9b:ec:95:d1:21:49:1d:5d:65:a7:1a:61:46:67:dd:42:18:65:48 Signature Algorithm: sha256WithRSAEncryption Issuer: C=HU, ST=Hungary, O=coTURN, CN=CA/emailAddress=misi@majd.eu Validity Not Before: Mar 5 09:05:42 2020 GMT Not After : Mar 3 09:05:42 2030 GMT Subject: C=HU, ST=Hungary, L=Debrecen, O=coTURN, CN=Client/emailAddress=misi@majd.eu Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:af:6d:38:31:23:12:12:e7:5a:8d:ed:1c:02:7e: bf:c2:ef:7a:d1:c0:b2:4b:b4:38:9b:a7:5d:dd:01: 2c:a0:e7:7c:5b:7a:4d:71:4b:c9:5b:77:e8:b3:4c: 92:5b:8c:43:57:b6:c9:8c:44:66:6a:9e:8c:f2:76: 58:a2:f5:38:a3:4f:ef:af:5a:c7:bf:e5:72:98:c0: b8:2e:a1:75:cc:16:8b:bf:a3:6a:e6:fd:c9:25:35: 92:31:b2:78:2a:42:7b:a1:ce:25:be:32:45:6e:0b: 36:22:f8:6c:9c:f3:8f:bf:c8:8c:79:d5:59:02:f5: de:1f:67:fc:ef:c7:27:88:a7:35:b1:d7:ee:dc:1c: 74:11:fc:3c:56:33:b5:e7:88:ce:f3:ce:db:b9:3c: e0:eb:15:bc:00:5f:29:f4:9c:8e:4d:61:df:da:aa: f4:fc:fb:e7:4b:75:dc:dc:cf:f0:4b:3b:67:cf:bf: 35:b8:0f:5b:20:94:60:dd:3b:e5:7a:ec:0e:30:2c: c1:fb:f6:21:5b:ed:80:34:9d:59:5c:95:39:a2:61: a4:13:fa:57:b9:f5:85:d4:a1:bf:91:cf:d7:dc:ac: fa:32:47:ee:d2:86:9b:14:d1:35:88:1e:2d:9f:39: 74:86:de:f1:04:de:e1:39:2f:a8:91:bf:8b:f7:4f: 7c:e5 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 32:BA:14:26:42:B6:5B:9E:3C:F1:53:1A:FD:DB:CB:FE:B1:A2:74:6C X509v3 Authority Key Identifier: keyid:1C:27:5E:40:39:8C:EC:71:C7:ED:E9:2A:56:C9:9E:DF:48:EA:82:42 X509v3 Basic Constraints: critical CA:TRUE Signature Algorithm: sha256WithRSAEncryption 6b:93:56:56:81:fb:34:9e:15:2e:3e:b2:2c:73:72:60:f2:1a: a8:bf:c3:f0:c7:57:00:48:37:2a:1c:63:71:1b:29:f4:2b:dc: 64:07:f8:72:80:65:18:c7:74:23:c1:02:00:d8:93:1d:4f:2b: 8c:46:34:1e:d2:6a:5c:ab:8d:ff:a7:fe:e5:c2:bf:33:55:ea: 2b:e2:70:e9:24:4c:4d:31:d4:dd:10:55:f5:bb:2c:a5:ec:f6: 8f:7a:05:1c:6c:7d:cf:85:6b:29:a7:bd:fe:a2:bc:00:45:b8: ac:70:c7:c9:67:93:0a:5c:d7:52:a3:c9:fc:6c:ef:52:b2:6b: bc:5b:f9:e1:9b:27:07:39:28:28:7f:a0:70:62:af:4f:42:82: dd:ec:23:4d:fc:8e:19:51:87:cc:d0:29:d5:27:44:9c:fa:b5: 51:ea:31:eb:51:84:3f:07:5b:c0:57:5d:2a:c7:15:ed:9c:46: ac:8e:14:8b:4d:82:0e:b4:6a:47:db:37:f3:03:08:86:b6:25: 0b:92:6d:99:a9:99:45:4e:38:45:e0:a2:4e:e7:34:50:51:ab: f8:c8:ef:26:3d:7f:9f:8f:45:20:cf:f5:31:27:b6:00:3a:e0: 4a:d5:62:9a:29:27:9b:aa:3a:95:56:1c:d7:65:15:ce:35:10: 2a:7e:cc:b6 -----BEGIN CERTIFICATE----- MIIDrDCCApSgAwIBAgIUTJvsldEhSR1dZacaYUZn3UIYZUgwDQYJKoZIhvcNAQEL BQAwWjELMAkGA1UEBhMCSFUxEDAOBgNVBAgMB0h1bmdhcnkxDzANBgNVBAoMBmNv VFVSTjELMAkGA1UEAwwCQ0ExGzAZBgkqhkiG9w0BCQEWDG1pc2lAbWFqZC5ldTAe Fw0yMDAzMDUwOTA1NDJaFw0zMDAzMDMwOTA1NDJaMHExCzAJBgNVBAYTAkhVMRAw DgYDVQQIDAdIdW5nYXJ5MREwDwYDVQQHDAhEZWJyZWNlbjEPMA0GA1UECgwGY29U VVJOMQ8wDQYDVQQDDAZDbGllbnQxGzAZBgkqhkiG9w0BCQEWDG1pc2lAbWFqZC5l dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK9tODEjEhLnWo3tHAJ+ v8LvetHAsku0OJunXd0BLKDnfFt6TXFLyVt36LNMkluMQ1e2yYxEZmqejPJ2WKL1 OKNP769ax7/lcpjAuC6hdcwWi7+jaub9ySU1kjGyeCpCe6HOJb4yRW4LNiL4bJzz j7/IjHnVWQL13h9n/O/HJ4inNbHX7twcdBH8PFYzteeIzvPO27k84OsVvABfKfSc jk1h39qq9Pz750t13NzP8Es7Z8+/NbgPWyCUYN075XrsDjAswfv2IVvtgDSdWVyV OaJhpBP6V7n1hdShv5HP19ys+jJH7tKGmxTRNYgeLZ85dIbe8QTe4TkvqJG/i/dP fOUCAwEAAaNTMFEwHQYDVR0OBBYEFDK6FCZCtluePPFTGv3by/6xonRsMB8GA1Ud IwQYMBaAFBwnXkA5jOxxx+3pKlbJnt9I6oJCMA8GA1UdEwEB/wQFMAMBAf8wDQYJ KoZIhvcNAQELBQADggEBAGuTVlaB+zSeFS4+sixzcmDyGqi/w/DHVwBINyocY3Eb KfQr3GQH+HKAZRjHdCPBAgDYkx1PK4xGNB7Salyrjf+n/uXCvzNV6ivicOkkTE0x 1N0QVfW7LKXs9o96BRxsfc+Faymnvf6ivABFuKxwx8lnkwpc11Kjyfxs71Kya7xb +eGbJwc5KCh/oHBir09Cgt3sI038jhlRh8zQKdUnRJz6tVHqMetRhD8HW8BXXSrH Fe2cRqyOFItNgg60akfbN/MDCIa2JQuSbZmpmUVOOEXgok7nNFBRq/jI7yY9f5+P RSDP9TEntgA64ErVYpopJ5uqOpVWHNdlFc41ECp+zLY= -----END CERTIFICATE----- turnserver-4.5.2/examples/ca/CA/newcerts/4C9BEC95D121491D5D65A71A614667DD42186547.pem0000644000175000017500000001056213776656461024111 0ustar misimisiCertificate: Data: Version: 3 (0x2) Serial Number: 4c:9b:ec:95:d1:21:49:1d:5d:65:a7:1a:61:46:67:dd:42:18:65:47 Signature Algorithm: sha256WithRSAEncryption Issuer: C=HU, ST=Hungary, O=coTURN, CN=CA/emailAddress=misi@majd.eu Validity Not Before: Mar 5 09:05:21 2020 GMT Not After : Mar 3 09:05:21 2030 GMT Subject: C=HU, ST=Hungary, L=Debrecen, O=coTURN, CN=Server/emailAddress=misi@majd.eu Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:bc:db:f7:17:35:17:7c:46:79:64:89:61:5f:ac: cf:8f:6d:97:13:87:8a:d6:f1:ab:df:f6:69:4e:04: 57:c1:4d:6c:3d:77:c9:50:0d:3d:b6:89:cd:ac:00: b5:02:45:e4:4c:78:ef:6f:18:7e:57:4e:bc:62:4d: f6:de:6c:c8:77:ea:c5:b2:b4:65:2d:46:76:bf:5e: 5f:f8:45:78:55:f4:4d:20:ac:91:f0:4f:23:cb:5d: 40:29:44:de:9c:f7:0a:e6:48:a4:80:35:dd:cb:e8: 02:90:59:f7:31:f9:4c:50:fe:98:ef:dd:7f:60:51: 2d:44:0a:14:a2:57:96:51:36:3f:73:66:db:45:5f: bd:9d:f4:82:3a:ce:ab:75:4f:d0:90:6d:43:d1:7b: 2f:77:31:88:db:2f:4a:a9:4e:62:39:c7:14:7f:39: ef:e2:08:b7:18:a7:6c:f8:d9:35:d5:a3:f8:64:f5: 02:51:22:1b:8e:7a:c5:44:ae:df:b1:17:0b:71:df: 09:82:89:49:70:c5:9b:a0:f3:3c:02:48:75:e7:81: f9:24:51:56:24:3b:ff:b8:68:d3:13:2e:a2:f4:d1: 70:33:a9:7a:d6:17:fd:ca:a5:6b:13:74:c9:ce:b6: 26:4f:01:ff:eb:ba:b5:f9:a1:70:80:da:11:df:a3: 7b:4f Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 38:C1:E5:77:D3:01:6B:7A:A7:D8:18:6B:50:D6:FA:0E:D6:D9:B4:4F X509v3 Authority Key Identifier: keyid:1C:27:5E:40:39:8C:EC:71:C7:ED:E9:2A:56:C9:9E:DF:48:EA:82:42 X509v3 Basic Constraints: critical CA:TRUE Signature Algorithm: sha256WithRSAEncryption a3:37:55:68:68:02:9f:af:d6:b1:38:b3:d8:bf:30:27:33:6f: 21:4c:09:ee:cf:24:d2:eb:cf:1c:7a:15:98:6d:10:94:e0:4a: 1f:88:5c:43:90:09:78:c1:a6:82:06:16:f2:8c:d1:3a:c5:3b: 99:67:35:3c:00:bf:9f:a2:6a:e7:33:85:83:88:72:88:e4:d2: 83:1c:6c:49:92:5f:51:80:0d:92:0f:99:4d:cb:2a:18:4d:68: b7:b6:d1:de:54:22:71:88:8d:04:45:c5:13:34:8d:52:7a:f7: 2a:e7:cb:b2:41:20:7b:ef:aa:d0:58:93:b5:e6:b5:fa:8b:22: a3:ed:a7:81:9b:ca:50:f7:d0:bd:5f:f2:52:6d:8b:af:af:64: 36:9d:6d:81:ce:50:29:b7:db:d0:ac:a3:1d:78:77:90:29:a3: 84:10:69:13:e9:47:fc:e1:1e:c2:74:55:61:11:65:2d:77:e1: ca:9f:2d:6f:2f:76:f6:69:bc:09:50:9a:b0:48:05:a2:53:e6: 93:46:81:0d:04:8b:cd:fb:a4:a7:82:08:78:f9:87:dc:0a:07: 91:1f:de:09:fa:00:5a:16:1a:2b:5c:83:10:03:33:2f:ad:8c: 9a:eb:94:0f:77:b1:9b:ec:e6:0e:dc:84:dd:35:3f:b5:8a:d2: 06:0e:88:d7 -----BEGIN CERTIFICATE----- MIIDrDCCApSgAwIBAgIUTJvsldEhSR1dZacaYUZn3UIYZUcwDQYJKoZIhvcNAQEL BQAwWjELMAkGA1UEBhMCSFUxEDAOBgNVBAgMB0h1bmdhcnkxDzANBgNVBAoMBmNv VFVSTjELMAkGA1UEAwwCQ0ExGzAZBgkqhkiG9w0BCQEWDG1pc2lAbWFqZC5ldTAe Fw0yMDAzMDUwOTA1MjFaFw0zMDAzMDMwOTA1MjFaMHExCzAJBgNVBAYTAkhVMRAw DgYDVQQIDAdIdW5nYXJ5MREwDwYDVQQHDAhEZWJyZWNlbjEPMA0GA1UECgwGY29U VVJOMQ8wDQYDVQQDDAZTZXJ2ZXIxGzAZBgkqhkiG9w0BCQEWDG1pc2lAbWFqZC5l dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALzb9xc1F3xGeWSJYV+s z49tlxOHitbxq9/2aU4EV8FNbD13yVANPbaJzawAtQJF5Ex4728YfldOvGJN9t5s yHfqxbK0ZS1Gdr9eX/hFeFX0TSCskfBPI8tdQClE3pz3CuZIpIA13cvoApBZ9zH5 TFD+mO/df2BRLUQKFKJXllE2P3Nm20VfvZ30gjrOq3VP0JBtQ9F7L3cxiNsvSqlO YjnHFH857+IItxinbPjZNdWj+GT1AlEiG456xUSu37EXC3HfCYKJSXDFm6DzPAJI deeB+SRRViQ7/7ho0xMuovTRcDOpetYX/cqlaxN0yc62Jk8B/+u6tfmhcIDaEd+j e08CAwEAAaNTMFEwHQYDVR0OBBYEFDjB5XfTAWt6p9gYa1DW+g7W2bRPMB8GA1Ud IwQYMBaAFBwnXkA5jOxxx+3pKlbJnt9I6oJCMA8GA1UdEwEB/wQFMAMBAf8wDQYJ KoZIhvcNAQELBQADggEBAKM3VWhoAp+v1rE4s9i/MCczbyFMCe7PJNLrzxx6FZht EJTgSh+IXEOQCXjBpoIGFvKM0TrFO5lnNTwAv5+iauczhYOIcojk0oMcbEmSX1GA DZIPmU3LKhhNaLe20d5UInGIjQRFxRM0jVJ69yrny7JBIHvvqtBYk7XmtfqLIqPt p4GbylD30L1f8lJti6+vZDadbYHOUCm329Csox14d5Apo4QQaRPpR/zhHsJ0VWER ZS134cqfLW8vdvZpvAlQmrBIBaJT5pNGgQ0Ei837pKeCCHj5h9wKB5Ef3gn6AFoW GitcgxADMy+tjJrrlA93sZvs5g7chN01P7WK0gYOiNc= -----END CERTIFICATE----- turnserver-4.5.2/examples/ca/CA/newcerts/4C9BEC95D121491D5D65A71A614667DD42186546.pem0000644000175000017500000001050613776656461024106 0ustar misimisiCertificate: Data: Version: 3 (0x2) Serial Number: 4c:9b:ec:95:d1:21:49:1d:5d:65:a7:1a:61:46:67:dd:42:18:65:46 Signature Algorithm: sha256WithRSAEncryption Issuer: C=HU, ST=Hungary, O=coTURN, CN=CA/emailAddress=misi@majd.eu Validity Not Before: Mar 5 09:05:10 2020 GMT Not After : Jul 7 09:05:10 3019 GMT Subject: C=HU, ST=Hungary, O=coTURN, CN=CA/emailAddress=misi@majd.eu Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:d8:76:2a:59:44:73:da:25:38:93:54:d8:c5:2b: 11:bd:30:80:21:5f:47:95:7d:eb:5e:3e:98:0d:a7: a8:30:8c:07:6d:1a:ee:89:c1:4c:cc:64:81:90:b3: ab:54:1f:9b:72:23:c5:2f:0a:32:52:be:27:ad:2f: 51:ee:62:9e:ed:44:d0:ba:aa:72:67:03:a2:ee:a0: e3:5d:9e:37:ec:ee:0b:29:59:e8:d8:d5:84:a1:6d: 36:5d:85:6b:0d:73:a0:32:fe:b6:fa:99:ef:8c:78: a9:02:f4:3a:bd:13:bc:1a:9b:72:55:0b:e7:0c:ed: 68:00:c2:e7:78:4a:df:ce:14:2a:99:f1:de:97:16: 60:44:f1:fc:f8:74:e5:33:31:cc:f9:ff:5d:9e:c1: c7:c6:21:75:48:08:26:f5:7c:f1:56:ec:15:c5:7f: 24:0f:08:03:74:e0:da:10:bf:3d:90:67:09:1e:b2: 3f:b4:f4:15:df:53:e8:68:e8:d1:28:8e:2d:37:f9: e0:3a:a3:29:00:3d:0a:66:7c:71:ab:54:e5:da:fe: 44:18:3c:b4:be:c5:ce:49:26:8c:cc:ab:88:8f:b7: e3:ad:5b:df:b2:d4:a3:f8:a9:06:4f:38:6e:b7:05: b3:3a:bd:63:cd:f7:26:15:e0:98:fd:30:7e:d3:33: 56:8d Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 1C:27:5E:40:39:8C:EC:71:C7:ED:E9:2A:56:C9:9E:DF:48:EA:82:42 X509v3 Authority Key Identifier: keyid:1C:27:5E:40:39:8C:EC:71:C7:ED:E9:2A:56:C9:9E:DF:48:EA:82:42 X509v3 Basic Constraints: critical CA:TRUE Signature Algorithm: sha256WithRSAEncryption b4:d5:d9:7a:46:1e:1a:95:02:b5:7e:86:45:16:26:d5:8a:11: b9:34:98:58:df:cd:0c:d5:a5:f2:cc:24:1a:22:f4:c7:3e:50: 39:40:f5:d6:e8:3b:9c:05:e9:f9:95:9b:c2:01:3b:69:d5:ba: 4f:cf:7c:a6:7c:6e:f4:24:a3:d1:88:e2:29:60:ca:6d:b0:ee: a6:b8:d1:5f:49:d5:08:a6:c2:79:3a:3f:8a:63:ec:53:ef:48: 00:8c:61:d2:0f:38:e0:00:ac:6d:a6:bf:ed:6a:42:c3:cf:4e: e3:0d:48:c5:a7:6d:5e:af:5a:e4:30:26:ba:19:2a:a5:57:da: ce:b7:b6:45:24:fb:36:b6:a3:6c:55:ca:9f:91:19:29:db:a4: 22:d4:45:53:b9:79:6a:a7:5e:90:a3:4d:3b:c1:b6:2b:52:41: 97:7d:9e:0c:cf:0a:5f:ce:0e:fe:bf:a9:e5:b7:60:17:f5:93: 4b:b5:6d:2d:51:a6:c1:54:65:f9:e1:5c:21:8d:3d:19:0c:dc: 2c:c9:17:40:65:15:d0:ad:98:06:a0:11:aa:87:b3:2d:03:29: 37:24:f6:42:a8:d5:58:ae:55:20:c3:37:a3:62:33:36:34:73: 98:bc:70:30:aa:33:b0:e4:86:b6:d9:22:79:1f:3f:68:6f:f5: 66:75:e8:70 -----BEGIN CERTIFICATE----- MIIDlzCCAn+gAwIBAgIUTJvsldEhSR1dZacaYUZn3UIYZUYwDQYJKoZIhvcNAQEL BQAwWjELMAkGA1UEBhMCSFUxEDAOBgNVBAgMB0h1bmdhcnkxDzANBgNVBAoMBmNv VFVSTjELMAkGA1UEAwwCQ0ExGzAZBgkqhkiG9w0BCQEWDG1pc2lAbWFqZC5ldTAg Fw0yMDAzMDUwOTA1MTBaGA8zMDE5MDcwNzA5MDUxMFowWjELMAkGA1UEBhMCSFUx EDAOBgNVBAgMB0h1bmdhcnkxDzANBgNVBAoMBmNvVFVSTjELMAkGA1UEAwwCQ0Ex GzAZBgkqhkiG9w0BCQEWDG1pc2lAbWFqZC5ldTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBANh2KllEc9olOJNU2MUrEb0wgCFfR5V9614+mA2nqDCMB20a 7onBTMxkgZCzq1Qfm3IjxS8KMlK+J60vUe5inu1E0LqqcmcDou6g412eN+zuCylZ 6NjVhKFtNl2Faw1zoDL+tvqZ74x4qQL0Or0TvBqbclUL5wztaADC53hK384UKpnx 3pcWYETx/Ph05TMxzPn/XZ7Bx8YhdUgIJvV88VbsFcV/JA8IA3Tg2hC/PZBnCR6y P7T0Fd9T6Gjo0SiOLTf54DqjKQA9CmZ8catU5dr+RBg8tL7FzkkmjMyriI+3461b 37LUo/ipBk84brcFszq9Y833JhXgmP0wftMzVo0CAwEAAaNTMFEwHQYDVR0OBBYE FBwnXkA5jOxxx+3pKlbJnt9I6oJCMB8GA1UdIwQYMBaAFBwnXkA5jOxxx+3pKlbJ nt9I6oJCMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBALTV2XpG HhqVArV+hkUWJtWKEbk0mFjfzQzVpfLMJBoi9Mc+UDlA9dboO5wF6fmVm8IBO2nV uk/PfKZ8bvQko9GI4ilgym2w7qa40V9J1Qimwnk6P4pj7FPvSACMYdIPOOAArG2m v+1qQsPPTuMNSMWnbV6vWuQwJroZKqVX2s63tkUk+za2o2xVyp+RGSnbpCLURVO5 eWqnXpCjTTvBtitSQZd9ngzPCl/ODv6/qeW3YBf1k0u1bS1RpsFUZfnhXCGNPRkM 3CzJF0BlFdCtmAagEaqHsy0DKTck9kKo1ViuVSDDN6NiMzY0c5i8cDCqM7DkhrbZ InkfP2hv9WZ16HA= -----END CERTIFICATE----- turnserver-4.5.2/examples/ca/CA/serial.old0000644000175000017500000000005113776656461017111 0ustar misimisi4C9BEC95D121491D5D65A71A614667DD42186548 turnserver-4.5.2/examples/ca/CA/crlnumber0000644000175000017500000000000313776656461017043 0ustar misimisi01 turnserver-4.5.2/examples/ca/CA/index.txt0000644000175000017500000000062413776656461017010 0ustar misimisiV 30190707090510Z 4C9BEC95D121491D5D65A71A614667DD42186546 unknown /C=HU/ST=Hungary/O=coTURN/CN=CA/emailAddress=misi@majd.eu V 300303090521Z 4C9BEC95D121491D5D65A71A614667DD42186547 unknown /C=HU/ST=Hungary/L=Debrecen/O=coTURN/CN=Server/emailAddress=misi@majd.eu V 300303090542Z 4C9BEC95D121491D5D65A71A614667DD42186548 unknown /C=HU/ST=Hungary/L=Debrecen/O=coTURN/CN=Client/emailAddress=misi@majd.eu turnserver-4.5.2/examples/ca/turn_client_pkey.pem0000644000175000017500000000325413776656461020740 0ustar misimisi-----BEGIN PRIVATE KEY----- MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCvbTgxIxIS51qN 7RwCfr/C73rRwLJLtDibp13dASyg53xbek1xS8lbd+izTJJbjENXtsmMRGZqnozy dlii9TijT++vWse/5XKYwLguoXXMFou/o2rm/cklNZIxsngqQnuhziW+MkVuCzYi +Gyc84+/yIx51VkC9d4fZ/zvxyeIpzWx1+7cHHQR/DxWM7XniM7zztu5PODrFbwA Xyn0nI5NYd/aqvT8++dLddzcz/BLO2fPvzW4D1sglGDdO+V67A4wLMH79iFb7YA0 nVlclTmiYaQT+le59YXUob+Rz9fcrPoyR+7ShpsU0TWIHi2fOXSG3vEE3uE5L6iR v4v3T3zlAgMBAAECggEBAINzP+vx75UirwQybA6ik2aqtEmALxnzDYf1PaxhOOPJ EbIqTuVaeKOFkmToN7NJwxxy50un5WZ3L/5vF7PkNHCLcXrgd1UfxWMY5eprKi2n p0gOWAiGmra7EbUTml9wOdvg8P84BDaVSBekNx7Ukx6OVFTmvTAutCascSfq/4Cx K71zaW/I9hrU8oNDBDzolVW4gW8ObNLGhoDqmvkoXrlrGEBNqkuErbbYZA1k/001 lurEh7Zp7Kp6jjHcRm83a7bWiRYGtv1K9kR9MKKLW7au8zyjYcesTvS2QjY+k20W vE2kmyAosbJShFzTmZn8kwgh6c0BPyFDEI5XleMeefECgYEA6ZhgG87wyU4RDU1N PxLV9ufbSYpW91KP1iuZ5Z6QdLGWZeWKjvxtoLAa3z9ceIBVvFqCGDn4DfwIaNLe tGsjeyXre1R3/B0S/oAJbmbRV4pWl/jSzgbzCTGW7x1mpqgpJdHFmTbqTxkNB6cM fpzTPfM012KfRglD9D+2DTOCyEsCgYEAwECXQRIe7/657J68GHSBCaQ+rzDL3nRe exe4duHyXok0yohk7OiPepKQ1hdYq2PHhGEj6b5OgFppWeA66M/ndjX4S10oCtN0 oEb7honFz4ZmHmqQ6UotAuBx7tq06v+KI/eTvefTVh9mujdwMW4sAowhx9Dw6PkR ipFCdi458Y8CgYEAhJ//ySoYKaMKKWw/NFVkZ9fB+CH0OF2GzslYijcZuzdstZO6 tG37bCUwTJozzTLH+rXEcS7QeFglCibXTMYbkfq4lQAjU1/KffaB5E26A6LGgWhD f7gQWqLuF/qwYmTNX+yW7ONx6tDFRhgBDw3JHb4svTEATwpJq65UlXAui7sCgYBD krBXO8JKApNg+s4MHm74b5VkyFbv4qEOzOCWUIZ6+ejnQxeOOZOstnVX+q681v5a pjYUQ0KeVKjw4SJzkBe/8epKuvyHCZnVd/2SZTx0271q9XPnu52khDUnihHLA3SP fcadGi2q+LCHxVKW3S1028JH1EXI7TpgJPxiQ480OwKBgQDmi0BiSFaxNVcJm+pq rbmK2pRPl49VOlc7px89ilZgoIeU8jwWQyqXRooarFhV1H0SA6oh52jYljiIIFVn qwKfS3Sjo6iW3ytjGcRLeNS0Sk8D2XMky7Mw120ZxatTsKw3ztmYFAlSYdxRMnue zkYzcxL3N2LvHeY8SOwyxayfxg== -----END PRIVATE KEY----- turnserver-4.5.2/examples/ca/CA.pl.diff0000644000175000017500000000123613776656461016404 0ustar misimisi--- CA.pl 2019-10-12 19:56:43.000000000 +0000 +++ CA.pl 2020-03-05 07:58:41.112690266 +0000 @@ -25,8 +25,8 @@ my $verbose = 1; my $OPENSSL_CONFIG = $ENV{"OPENSSL_CONFIG"} || ""; -my $DAYS = "-days 365"; -my $CADAYS = "-days 1095"; # 3 years +my $DAYS = "-days 36500"; +my $CADAYS = "-days 365000"; # 1000 years my $REQ = "$openssl req $OPENSSL_CONFIG"; my $CA = "$openssl ca $OPENSSL_CONFIG"; my $VERIFY = "$openssl verify"; @@ -34,7 +34,7 @@ my $PKCS12 = "$openssl pkcs12"; # default openssl.cnf file has setup as per the following -my $CATOP = "./demoCA"; +my $CATOP = "./CA"; my $CAKEY = "cakey.pem"; my $CAREQ = "careq.pem"; my $CACERT = "cacert.pem"; turnserver-4.5.2/examples/ca/turn_server_cert.pem0000644000175000017500000001056213776656461020755 0ustar misimisiCertificate: Data: Version: 3 (0x2) Serial Number: 4c:9b:ec:95:d1:21:49:1d:5d:65:a7:1a:61:46:67:dd:42:18:65:47 Signature Algorithm: sha256WithRSAEncryption Issuer: C=HU, ST=Hungary, O=coTURN, CN=CA/emailAddress=misi@majd.eu Validity Not Before: Mar 5 09:05:21 2020 GMT Not After : Mar 3 09:05:21 2030 GMT Subject: C=HU, ST=Hungary, L=Debrecen, O=coTURN, CN=Server/emailAddress=misi@majd.eu Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:bc:db:f7:17:35:17:7c:46:79:64:89:61:5f:ac: cf:8f:6d:97:13:87:8a:d6:f1:ab:df:f6:69:4e:04: 57:c1:4d:6c:3d:77:c9:50:0d:3d:b6:89:cd:ac:00: b5:02:45:e4:4c:78:ef:6f:18:7e:57:4e:bc:62:4d: f6:de:6c:c8:77:ea:c5:b2:b4:65:2d:46:76:bf:5e: 5f:f8:45:78:55:f4:4d:20:ac:91:f0:4f:23:cb:5d: 40:29:44:de:9c:f7:0a:e6:48:a4:80:35:dd:cb:e8: 02:90:59:f7:31:f9:4c:50:fe:98:ef:dd:7f:60:51: 2d:44:0a:14:a2:57:96:51:36:3f:73:66:db:45:5f: bd:9d:f4:82:3a:ce:ab:75:4f:d0:90:6d:43:d1:7b: 2f:77:31:88:db:2f:4a:a9:4e:62:39:c7:14:7f:39: ef:e2:08:b7:18:a7:6c:f8:d9:35:d5:a3:f8:64:f5: 02:51:22:1b:8e:7a:c5:44:ae:df:b1:17:0b:71:df: 09:82:89:49:70:c5:9b:a0:f3:3c:02:48:75:e7:81: f9:24:51:56:24:3b:ff:b8:68:d3:13:2e:a2:f4:d1: 70:33:a9:7a:d6:17:fd:ca:a5:6b:13:74:c9:ce:b6: 26:4f:01:ff:eb:ba:b5:f9:a1:70:80:da:11:df:a3: 7b:4f Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 38:C1:E5:77:D3:01:6B:7A:A7:D8:18:6B:50:D6:FA:0E:D6:D9:B4:4F X509v3 Authority Key Identifier: keyid:1C:27:5E:40:39:8C:EC:71:C7:ED:E9:2A:56:C9:9E:DF:48:EA:82:42 X509v3 Basic Constraints: critical CA:TRUE Signature Algorithm: sha256WithRSAEncryption a3:37:55:68:68:02:9f:af:d6:b1:38:b3:d8:bf:30:27:33:6f: 21:4c:09:ee:cf:24:d2:eb:cf:1c:7a:15:98:6d:10:94:e0:4a: 1f:88:5c:43:90:09:78:c1:a6:82:06:16:f2:8c:d1:3a:c5:3b: 99:67:35:3c:00:bf:9f:a2:6a:e7:33:85:83:88:72:88:e4:d2: 83:1c:6c:49:92:5f:51:80:0d:92:0f:99:4d:cb:2a:18:4d:68: b7:b6:d1:de:54:22:71:88:8d:04:45:c5:13:34:8d:52:7a:f7: 2a:e7:cb:b2:41:20:7b:ef:aa:d0:58:93:b5:e6:b5:fa:8b:22: a3:ed:a7:81:9b:ca:50:f7:d0:bd:5f:f2:52:6d:8b:af:af:64: 36:9d:6d:81:ce:50:29:b7:db:d0:ac:a3:1d:78:77:90:29:a3: 84:10:69:13:e9:47:fc:e1:1e:c2:74:55:61:11:65:2d:77:e1: ca:9f:2d:6f:2f:76:f6:69:bc:09:50:9a:b0:48:05:a2:53:e6: 93:46:81:0d:04:8b:cd:fb:a4:a7:82:08:78:f9:87:dc:0a:07: 91:1f:de:09:fa:00:5a:16:1a:2b:5c:83:10:03:33:2f:ad:8c: 9a:eb:94:0f:77:b1:9b:ec:e6:0e:dc:84:dd:35:3f:b5:8a:d2: 06:0e:88:d7 -----BEGIN CERTIFICATE----- MIIDrDCCApSgAwIBAgIUTJvsldEhSR1dZacaYUZn3UIYZUcwDQYJKoZIhvcNAQEL BQAwWjELMAkGA1UEBhMCSFUxEDAOBgNVBAgMB0h1bmdhcnkxDzANBgNVBAoMBmNv VFVSTjELMAkGA1UEAwwCQ0ExGzAZBgkqhkiG9w0BCQEWDG1pc2lAbWFqZC5ldTAe Fw0yMDAzMDUwOTA1MjFaFw0zMDAzMDMwOTA1MjFaMHExCzAJBgNVBAYTAkhVMRAw DgYDVQQIDAdIdW5nYXJ5MREwDwYDVQQHDAhEZWJyZWNlbjEPMA0GA1UECgwGY29U VVJOMQ8wDQYDVQQDDAZTZXJ2ZXIxGzAZBgkqhkiG9w0BCQEWDG1pc2lAbWFqZC5l dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALzb9xc1F3xGeWSJYV+s z49tlxOHitbxq9/2aU4EV8FNbD13yVANPbaJzawAtQJF5Ex4728YfldOvGJN9t5s yHfqxbK0ZS1Gdr9eX/hFeFX0TSCskfBPI8tdQClE3pz3CuZIpIA13cvoApBZ9zH5 TFD+mO/df2BRLUQKFKJXllE2P3Nm20VfvZ30gjrOq3VP0JBtQ9F7L3cxiNsvSqlO YjnHFH857+IItxinbPjZNdWj+GT1AlEiG456xUSu37EXC3HfCYKJSXDFm6DzPAJI deeB+SRRViQ7/7ho0xMuovTRcDOpetYX/cqlaxN0yc62Jk8B/+u6tfmhcIDaEd+j e08CAwEAAaNTMFEwHQYDVR0OBBYEFDjB5XfTAWt6p9gYa1DW+g7W2bRPMB8GA1Ud IwQYMBaAFBwnXkA5jOxxx+3pKlbJnt9I6oJCMA8GA1UdEwEB/wQFMAMBAf8wDQYJ KoZIhvcNAQELBQADggEBAKM3VWhoAp+v1rE4s9i/MCczbyFMCe7PJNLrzxx6FZht EJTgSh+IXEOQCXjBpoIGFvKM0TrFO5lnNTwAv5+iauczhYOIcojk0oMcbEmSX1GA DZIPmU3LKhhNaLe20d5UInGIjQRFxRM0jVJ69yrny7JBIHvvqtBYk7XmtfqLIqPt p4GbylD30L1f8lJti6+vZDadbYHOUCm329Csox14d5Apo4QQaRPpR/zhHsJ0VWER ZS134cqfLW8vdvZpvAlQmrBIBaJT5pNGgQ0Ei837pKeCCHj5h9wKB5Ef3gn6AFoW GitcgxADMy+tjJrrlA93sZvs5g7chN01P7WK0gYOiNc= -----END CERTIFICATE----- turnserver-4.5.2/examples/ca/turn_server_pkey.pem0000644000175000017500000000325013776656461020764 0ustar misimisi-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC82/cXNRd8Rnlk iWFfrM+PbZcTh4rW8avf9mlOBFfBTWw9d8lQDT22ic2sALUCReRMeO9vGH5XTrxi TfbebMh36sWytGUtRna/Xl/4RXhV9E0grJHwTyPLXUApRN6c9wrmSKSANd3L6AKQ Wfcx+UxQ/pjv3X9gUS1EChSiV5ZRNj9zZttFX72d9II6zqt1T9CQbUPRey93MYjb L0qpTmI5xxR/Oe/iCLcYp2z42TXVo/hk9QJRIhuOesVErt+xFwtx3wmCiUlwxZug 8zwCSHXngfkkUVYkO/+4aNMTLqL00XAzqXrWF/3KpWsTdMnOtiZPAf/rurX5oXCA 2hHfo3tPAgMBAAECggEALGPXVBEakA9QgRz5Ui+gKaoslF6Ld7IeH+ofHkNPDRRR mLELFFHIa5tASGlyIjKjUoYqYQZ0y7ip9sE0gVs4U1dPWI2mKlohlyFrlUNe4XUm m8N0GfPAChDE/+48FNDMMwxn/eqrUz4ZPCypOYnLMk5lTBvX0J/D7/Yem3nSzwt1 qkZoijxZH5IvJAJkBWvucRuJ8XxHzOAo2V2Y+wTdilcJhfCvqGC0rkydjaN6TtRW HWKvAOa7hEegNBbZhHhKfw5ovQwj9Cnr2+8gaTSw5gVaZNnhCO+TlUfQHIBH9rmt 82SHu1QoYSGMvkjlrrKhRYHrx+4P4TXoZ6eB1hl3QQKBgQDmwUOkh6qwL2dtcrF1 bVdRZjb1bw6L8qZAgUkcA1IaLVUlhjEJZGXAoPbLn6Vq+jfOvaYLmzEaLcpn3pfx Hwcb1vnNW7dlXC1vpIWXPZP4IPJV4XsL1AgoEj6mgETHxvC+4cLc2gaMY5o5TzUv VdV/A7SIqxAyPccXt1u/eITfNwKBgQDRhVTTJiBsGGjOetfgNqNGxpkKB6W4cET9 EyC1c7Lh40lioA2G8lzhFCdK9VZ+cAT51Bmkr5jq29EyMafSy3e4+PG8ZLHVL0ll qBY4vSzHQNcGvUgh+15g6ISgCbM0eSsAea3LY+fmchz6mBS6DhyMkYPSbV+7YvHJ PSnfTkTgqQKBgQCO+SQOJzjs3RI6UBv/4/V8K9bVjy/2Kiw0P2arAqu2KGxfSZvM c/ZPuevwEkSN2ecGI59kBY4Q6FpGrTZ7YXwoFbTFNpSVKt3EFK3pHXA3B0LfT0vL 8l3zZgqHY2Y6WdsEiiEQcc4o4fXGmHsdjxMvFX6gR01Ls9dNrIAeTHAXVQKBgGoL Q72C5JIRYKpw/mYbAVTHG5o5+KR7Hk/AqKNuJbGyqefi/jW44U2CN8j2l4pzA/G2 aiwyPAFStHTlMP29waC7Tw59IIy33Dw5cNXS2aEXrj1Y+/NHGKOPy+B8SFlcomkh LNduf2bhhs1Gv+bTUZvL4p5UgUmEcL/b1x+Qq8fRAoGBAIpNCp4W+TsPUJcQKoWm L61RVr5GaHv7/qxQvYaXIVCq8/gZAbJi3/A9ieTrF72uuOZ+ajzFHDUiiDs19y67 mCvCchPgqzLy9iSs6mm8fmS6kJnWn04I+7DOfe7kScUnD5WkyNaTYAeOqvdWzl/i B1hQJJ9GzZG5Rztlotm5m/JY -----END PRIVATE KEY----- turnserver-4.5.2/examples/run_all_clients.sh0000755000175000017500000000044013776656461020002 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.2/examples/cpu-mem.sh0000755000175000017500000000036613776656461016177 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.2/man/0000755000175000017500000000000013776656461013225 5ustar misimisiturnserver-4.5.2/man/man1/0000755000175000017500000000000013776656461014061 5ustar misimisiturnserver-4.5.2/man/man1/turnutils_uclient.10000777000175000017500000000000013776656461022067 2turnutils.1ustar misimisiturnserver-4.5.2/man/man1/turnadmin.10000644000175000017500000002200013776656461016136 0ustar misimisi.\" Text automatically generated by txt2man .TH TURN 1 "10 January 2021" "" "" .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. .SH ===================================== .SS NAME \fB \fBturnadmin \fP\- a TURN relay administration tool. \fB .SS SYNOPSIS .nf .fam C $ \fIturnadmin\fP [\fIcommand\fP] [\fIoptions\fP] $ \fIturnadmin\fP [ \fB\-h\fP | \fB\-\-help\fP] .fam T .fi .fam T .fi .SS DESCRIPTION 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 format 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 REST 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\fP\-\fIoptions\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 .PP 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. .PP 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 Help: .PP $ \fIturnadmin\fP \fB\-h\fP .SH ======================================= .SS DOCS After installation, run the \fIcommand\fP: .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. .SH ===================================== .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 .SH ===================================== .SS DIRECTORIES /usr/local/share/\fIturnserver\fP .PP /usr/local/share/doc/\fIturnserver\fP .PP /usr/local/share/examples/\fIturnserver\fP .SH ====================================== .SS SEE ALSO \fIturnserver\fP, \fIturnutils\fP .SH ====================================== .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/ .SH ====================================== .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 Mihály Mészáros .SS ACTIVE MAINTAINERS Mihály Mészáros turnserver-4.5.2/man/man1/turnutils_stunclient.10000777000175000017500000000000013776656461022614 2turnutils.1ustar misimisiturnserver-4.5.2/man/man1/coturn.10000777000175000017500000000000013776656461017753 2turnserver.1ustar misimisiturnserver-4.5.2/man/man1/turnutils_peer.10000777000175000017500000000000013776656461021357 2turnutils.1ustar misimisiturnserver-4.5.2/man/man1/turnutils_oauth.10000777000175000017500000000000013776656461021544 2turnutils.1ustar misimisiturnserver-4.5.2/man/man1/turnserver.10000644000175000017500000013427413776656461016375 0ustar misimisi.\" Text automatically generated by txt2man .TH TURN 1 "10 January 2021" "" "" .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 SYSTEMD If the systemd development library is available, then it will notify systemd about the server status. .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 .SH ===================================== .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 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 .PP 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 REST 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 REST 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 REST 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 REST 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 .PP 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. .PP \fB\-\-no\-software\-attribute\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 2066. .TP .B \fB\-\-dh1066\fP Use 1066 bits predefined DH TLS key. Default size of the key is 2066. .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\-\-new\-log\-timestamp\fP Enable full ISO\-8601 timestamp in all logs. .TP .B \fB\-\-new\-log\-timestamp\-format\fP Set timestamp format (in \fBstrftime\fP(1) format) .TP .B \fB\-\-log\-binding\fP Log STUN binding request. It is now disabled by default to avoid DoS attacks. .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). .RS .TP .B \fB\-\-prometheus\fP Enable prometheus metrics. By default it is disabled. Would listen on port 9641 unther the path /metrics also the path / on this port can be used as a health check .RE .TP .B \fB\-h\fP Help. .PP Options with values: .TP .B \fB\-\-stale\-nonce\fP[=] Use extra security with nonce value having limited lifetime, in seconds (default 600 secs). Set it to 0 for unlimited nonce lifetime. .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\-\-tcp\-proxy\-port\fP Support connections from TCP loadbalancer on this port. The loadbalancer should use the binary proxy protocol. (https://www.haproxy.org/download/1.8/doc/proxy\-protocol.txt) .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). .RS .TP .B \fB\-\-no\-auth\-pings\fP Disable periodic health checks to 'dynamic' auth secret tables. .TP .B \fB\-\-no\-dynamic\-ip\-list\fP Do not use dynamic allowed/denied peer ip list. .TP .B \fB\-\-no\-dynamic\-realms\fP Do not use dynamic realm assignment and \fIoptions\fP. .RE .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\-\-dh1066\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\-\-acme\-redirect\fP Redirect ACME/RFC8555 (like Let's Encrypt challenge) requests, i.e. HTTP GET requests matching '^/.well\-known/acme\-challenge/(.*)' to $1 with $1 == (.*). No validation of will be done, so make sure you do not forget the trailing slash. If is an empty string (the default value), no special handling of such requests will be done. .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). .SH ================================== .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 .SH =================================== .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. .SH =================================== .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 since 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 server 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. .SH =================================== .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). .SH ================================= .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. .SH ================================= .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. .SH ================================= .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. .SH ================================= .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. .SH ================================= .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. .SH ================================= .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. .SH ================================= .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. .SH ================================= .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 .SH ================================= .SH DIRECTORIES /usr/local/share/\fIturnserver\fP .PP /usr/local/share/doc/\fIturnserver\fP .PP /usr/local/share/examples/\fIturnserver\fP .SH ================================= .SH STANDARDS obsolete STUN RFC 3489 .PP new STUN RFC 5389 .SH TURN RFC 5766 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 .SH ================================= .SH SEE ALSO \fIturnadmin\fP, \fIturnutils\fP .SH ====================================== .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 .SH ====================================== .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 .RE .PP Mihály Mészáros .SS ACTIVE MAINTAINERS Mihály Mészáros turnserver-4.5.2/man/man1/turnutils.10000644000175000017500000003553313776656461016225 0ustar misimisi.\" Text automatically generated by txt2man .TH TURN 1 "10 January 2021" "" "" .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 .SH ===================================== .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. .SH ====================================== .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 .SH ======================================== .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). .SH ===================================== .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 .SH ===================================== .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 .SH ===================================== .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 .SH =================================== .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. .SH ===================================== .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 .SH ================================= .SH DIRECTORIES /usr/local/share/\fIturnserver\fP .PP /usr/local/share/doc/\fIturnserver\fP .PP /usr/local/share/examples/\fIturnserver\fP .SH =================================== .SH STANDARDS new STUN RFC 5389 .SH TURN RFC 5766 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 .SH ==================================== .SH SEE ALSO \fIturnserver\fP, \fIturnadmin\fP .SH ====================================== .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/ .SH ====================================== .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 Mihály Mészáros .SS ACTIVE MAINTAINERS Mihály Mészáros turnserver-4.5.2/man/man1/turnutils_natdiscovery.10000777000175000017500000000000013776656461023136 2turnutils.1ustar misimisiturnserver-4.5.2/make-man.sh0000755000175000017500000000213413776656461014477 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 ../..