Преглед изворни кода

Imported Upstream version 4.2.1.2

Oleg Moskalenko пре 11 година
родитељ
комит
5736eb2811
66 измењених фајлова са 3648 додато и 683 уклоњено
  1. 4 0
      AUTHORS
  2. 18 0
      ChangeLog
  3. 65 7
      INSTALL
  4. 2 0
      README.turnadmin
  5. 8 0
      README.turnserver
  6. 4 0
      README.turnutils
  7. 2 0
      STATUS
  8. 8 5
      TODO
  9. 50 2
      configure
  10. 11 1
      examples/etc/turnserver.conf
  11. 2 1
      examples/scripts/longtermsecuredb/secure_relay_with_db_mongo.sh
  12. 2 1
      examples/scripts/longtermsecuredb/secure_relay_with_db_mysql.sh
  13. 2 1
      examples/scripts/longtermsecuredb/secure_relay_with_db_mysql_ssl.sh
  14. 3 2
      examples/scripts/longtermsecuredb/secure_relay_with_db_psql.sh
  15. 2 1
      examples/scripts/longtermsecuredb/secure_relay_with_db_redis.sh
  16. 3 1
      man/man1/turnadmin.1
  17. 13 1
      man/man1/turnserver.1
  18. 7 1
      man/man1/turnutils.1
  19. 1 1
      rpm/build.settings.sh
  20. 3 1
      rpm/turnserver.spec
  21. 52 3
      src/apps/common/apputils.c
  22. 21 0
      src/apps/common/apputils.h
  23. 5 5
      src/apps/common/hiredis_libevent2.c
  24. 196 4
      src/apps/common/ns_turn_utils.c
  25. 1 2
      src/apps/common/ns_turn_utils.h
  26. 348 107
      src/apps/relay/dbdrivers/dbd_mongo.c
  27. 228 51
      src/apps/relay/dbdrivers/dbd_mysql.c
  28. 156 20
      src/apps/relay/dbdrivers/dbd_pgsql.c
  29. 170 43
      src/apps/relay/dbdrivers/dbd_redis.c
  30. 3 2
      src/apps/relay/dbdrivers/dbdriver.c
  31. 6 0
      src/apps/relay/dbdrivers/dbdriver.h
  32. 4 12
      src/apps/relay/dtls_listener.c
  33. 51 11
      src/apps/relay/mainrelay.c
  34. 4 2
      src/apps/relay/mainrelay.h
  35. 54 29
      src/apps/relay/netengine.c
  36. 102 45
      src/apps/relay/ns_ioalib_engine_impl.c
  37. 2 3
      src/apps/relay/ns_ioalib_impl.h
  38. 0 2
      src/apps/relay/tls_listener.c
  39. 5 5
      src/apps/relay/turncli.c
  40. 266 83
      src/apps/relay/userdb.c
  41. 11 4
      src/apps/relay/userdb.h
  42. 180 0
      src/apps/rfc5769/rfc5769check.c
  43. 42 1
      src/apps/uclient/mainuclient.c
  44. 6 0
      src/apps/uclient/session.h
  45. 30 42
      src/apps/uclient/startuclient.c
  46. 92 15
      src/apps/uclient/uclient.c
  47. 7 0
      src/apps/uclient/uclient.h
  48. 4 3
      src/client++/TurnMsgLib.h
  49. 62 6
      src/client/ns_turn_ioaddr.c
  50. 792 25
      src/client/ns_turn_msg.c
  51. 8 2
      src/client/ns_turn_msg.h
  52. 1 0
      src/client/ns_turn_msg_defs.h
  53. 115 4
      src/client/ns_turn_msg_defs_new.h
  54. 37 6
      src/ns_turn_defs.h
  55. 2 2
      src/server/ns_turn_ioalib.h
  56. 76 0
      src/server/ns_turn_maps.c
  57. 1 0
      src/server/ns_turn_maps.h
  58. 196 105
      src/server/ns_turn_server.c
  59. 11 5
      src/server/ns_turn_server.h
  60. 11 7
      src/server/ns_turn_session.h
  61. 1 0
      turndb/schema.mongo.sh
  62. 13 0
      turndb/schema.sql
  63. 39 1
      turndb/schema.userdb.redis
  64. 12 0
      turndb/testmongosetup.sh
  65. 12 0
      turndb/testredisdbsetup.sh
  66. 3 0
      turndb/testsqldbsetup.sql

+ 4 - 0
AUTHORS

@@ -47,3 +47,7 @@ Mutsutoshi Yoshimoto <[email protected]>
 Federico Pinna <[email protected]>
 	MongoDB support
 	(since v4.1.0.1)
+
+Bradley T. Hughes <[email protected]>
+	FreeBSD port
+	(since v4.1.2.1)

+ 18 - 0
ChangeLog

@@ -1,3 +1,21 @@
+10/05/2014 Oleg Moskalenko <[email protected]>
+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 clening.
+	- Logging fixed.
+	
 08/14/2014 Oleg Moskalenko <[email protected]>
 Version 4.1.2.1 'Vitari':
 	- The origin attribute is verified in the subsequent 

+ 65 - 7
INSTALL

@@ -706,6 +706,15 @@ CREATE TABLE turnusers_lt (
     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 32 characters (HEX representation of 16 bytes) for SHA1,
+or 64 characters (HEX representation of 32 bytes) for SHA256.
+
 # Table for short-term credentials mechanism authorisation:
 #
 CREATE TABLE turnusers_st (
@@ -760,14 +769,56 @@ CREATE TABLE turn_realm_option (
 	primary key (realm,opt)
 );
 
-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.
+# oAuth key storage table.
+#
+CREATE TABLE oauth_key (
+	kid varchar(128), /* 
+	ikm_key varchar(256) default '',
+	timestamp bigint default 0,
+	lifetime integer default 0,
+	hkdf_hash_func varchar(64) default '',
+	as_rs_alg varchar(64) default '',
+	as_rs_key varchar(256) default '',
+	auth_alg varchar(64) default '',
+	auth_key varchar(256) default '',
+	primary key (kid)
+);
 
-The key must be 32 characters (HEX representation of 16 bytes) for SHA1,
-or 64 characters (HEX representation of 32 bytes) for SHA256.
+The oauth_key table fields meanings are:
+
+	kid: the kid of the key;
+
+	ikm_key - (optional) base64-encoded key ("input keying material");
+		The ikm_key is not needed if the as_rs_key and auth_key are defined
+		explicitly in the database;
+		
+	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.
+	
+	hkdf_hash_func - (optional) hash function for HKDF procedure; the 
+		valid values are SHA-1 and SHA-256, with SHA-256 as default;
+		The hkdf_hash_func is not needed if the as_rs_key and auth_key 
+		are defined explicitly in the database;
+		
+	as_rs_alg - oAuth token encryption algorithm; the valid values are
+		"AES-128-CBC" and "AES-256-CBC", , "AEAD-AES-128-GCM",
+		"AEAD-AES-256-GCM".
+		The default value is "AES-256-CBC";
+		
+	as_rs_key - (optional) base64-encoded AS-RS key. If not defined, then 
+		calculated with ikm_key and hkdf_hash_func. The as_rs_key length 
+		is defined by as_rs_alg.
+		
+	auth_alg - (optional) oAuth token authentication algorithm; the valid values are
+		"HMAC-SHA-256-128", "HMAC-SHA-256" and "HMAC-SHA-1".		  
+		The default value is "HMAC-SHA-256-128".
+		
+	auth_key - (optional) base64-encoded AUTH key. If not defined, then 
+		calculated with ikm_key and hkdf_hash_func. The auth_key length 
+		is defined by auth_alg.
 
 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 
@@ -930,6 +981,11 @@ Or in the turnserver.conf file:
 
 mongo-userdb="mongodb://localhost:27017/turndb"
 
+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. 
+
 XVII. Redis setup
 
 The Redis setup is well documented on their site http://redis.io. 
@@ -998,6 +1054,8 @@ Redis TURN admin commands:
    
   $ bin/turnadmin -A -N "host=localhost dbname=0 user=turn password=turn" -u gorst -r north.gov -p hero
   $ bin/turnadmin -A -N "host=localhost dbname=0 user=turn password=turn" -u ninefingers -r north.gov -p youhavetoberealistic 
+  
+See the file testredisdbsetup.sh for the data structure examples.
 
 XVIII. Performance tuning
 

+ 2 - 0
README.turnadmin

@@ -241,3 +241,5 @@ to see the man page.
 	Mutsutoshi Yoshimoto <[email protected]>
 
 	Federico Pinna <[email protected]>
+
+	Bradley T. Hughes <[email protected]>

+ 8 - 0
README.turnserver

@@ -190,6 +190,8 @@ Flags:
 			This option can be used with long-term credentials mechanisms only -
 			it does not make much sense with the short-term mechanism.
 			
+--oauth			Support oAuth authentication, as in the third-party TURN specs document.
+			
 --dh566			Use 566 bits predefined DH TLS key. Default size of the key is 1066.
 
 --dh2066		Use 2066 bits predefined DH TLS key. Default size of the key is 1066.
@@ -420,6 +422,10 @@ Options with required values:
 			value can be changed on-the-fly by a separate program, so this is why
 			that other mode is dynamic. Multiple shared secrets can be used
 			(both in the database and in the "static" fashion).
+			
+--server-name		Server name used for
+			the oAuth authentication purposes.
+			The default value is the realm name.
 
 --cert			Certificate file, PEM format. Same file 
 			search rules applied as for the configuration 
@@ -928,3 +934,5 @@ https://groups.google.com/forum/?fromgroups=#!forum/turn-server-project-rfc5766-
 	Mutsutoshi Yoshimoto <[email protected]>
 
 	Federico Pinna <[email protected]>
+
+	Bradley T. Hughes <[email protected]>

+ 4 - 0
README.turnutils

@@ -117,6 +117,8 @@ Flags:
 
 -Z	Dual allocation (SSODA draft support).
 
+-J	Use oAuth with default test key kid='north'.
+
 Options with required values:  
 
 -l      Message length (Default: 100 Bytes).
@@ -337,3 +339,5 @@ SEE ALSO
 	Mutsutoshi Yoshimoto <[email protected]>
 	
 	Federico Pinna <[email protected]>
+
+	Bradley T. Hughes <[email protected]>

+ 2 - 0
STATUS

@@ -104,6 +104,8 @@ compatibility.
 44) Double (dual) allocation added (SSODA draft).
 
 45) Secure MySQL connection implemented.
+
+46) Third-party security mechanism (through oAuth) implemented.
  
 Things to be implemented in future (the development roadmap) 
 are described in the TODO file.

+ 8 - 5
TODO

@@ -47,7 +47,6 @@
 
 ==================================================================
 
-1) New security oAuth draft.
 
 ==================================================================
 
@@ -57,13 +56,13 @@
 
 1) For extra difficult NAT/FWs, consider implementing Websockets.
 
-2) MS TURN, MS STUN extensions.
+2) ALPN with TLS and DTLS (when OpenSSL 1.0.2 is available).
 
-3) ALPN with TLS and DTLS (when OpenSSL 1.0.2 is available).
+3) Redirect draft.
 
-4) Redirect draft.
+4) DTLS 1.2 (when available).
 
-5) DTLS 1.2 (when available).
+5) TURN Proxy ? http://tools.ietf.org/html/draft-schwartz-rtcweb-return-00
 
 ==================================================================
 
@@ -95,6 +94,10 @@
 
 4) Ganglia monitoring.
 
+5) Web API to the database (oAuth keys, for example).
+
+6) Key exchange mechanism for oAuth.
+
 ==================================================================
 
 ###   VIII. CODING STUFF   ###

+ 50 - 2
configure

@@ -9,6 +9,8 @@ cleanup() {
 	rm -rf ${TH_TMPCPROGB}
 	rm -rf ${DTLS_TMPCPROGC}
 	rm -rf ${DTLS_TMPCPROGB}
+	rm -rf ${GCM_TMPCPROGC}
+	rm -rf ${GCM_TMPCPROGB}
 	rm -rf ${PQ_TMPCPROGC}
 	rm -rf ${PQ_TMPCPROGB}
 	rm -rf ${MYSQL_TMPCPROGC}
@@ -237,6 +239,21 @@ dtls_testlib() {
     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
@@ -567,7 +584,7 @@ SYSTEM=`uname`
 
 if [ "${SYSTEM}" = "SunOS" ] ; then
 # Solaris ? is this you ?!
-    OSCFLAGS="${OSCFLAGS} -D__EXTENSIONS__ -D_XOPEN_SOURCE=500"
+    OSCFLAGS="${OSCFLAGS} -D__EXTENSIONS__ -D_XOPEN_SOURCE=500 -DTURN_NO_GETDOMAINNAME"
 fi
 
 #########################
@@ -628,6 +645,19 @@ int main(int argc, char** argv) {
 }
 !
 
+GCM_TMPCPROG=__test__ccomp__gcm__$$
+GCM_TMPCPROGC=${TMPDIR}/${GCM_TMPCPROG}.c
+GCM_TMPCPROGB=${TMPDIR}/${GCM_TMPCPROG}
+
+cat > ${GCM_TMPCPROGC} <<!
+#include <stdlib.h>
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+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}
@@ -885,6 +915,24 @@ else
 	TURN_NO_DTLS="-DTURN_NO_DTLS"
 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
 ###########################
@@ -1044,7 +1092,7 @@ fi
 # So, what we have now:
 ###############################
 
-OSCFLAGS="${OSCFLAGS} ${TURN_NO_THREAD_BARRIERS} ${TURN_NO_DTLS} ${TURN_NO_TLS} -DINSTALL_PREFIX=${PREFIX}"
+OSCFLAGS="${OSCFLAGS} ${TURN_NO_THREAD_BARRIERS} ${TURN_NO_DTLS} ${TURN_NO_GCM} ${TURN_NO_TLS} -DINSTALL_PREFIX=${PREFIX}"
 
 if ! [ -z "${TURN_ACCEPT_RPATH}" ] ; then
   if [ -z "${TURN_DISABLE_RPATH}" ] ; then

+ 11 - 1
examples/etc/turnserver.conf

@@ -208,7 +208,17 @@
 # in user database (if present). The database-stored  value can be changed on-the-fly
 # by a separate program, so this is why that other mode is 'dynamic'.
 #
-#static-auth-secret 	
+#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 to support oAuth authentication.
+#
+#oauth
 
 # 'Static' user accounts for long term credentials mechanism, only.
 # This option cannot be used with TURN REST API or with short-term credentials

+ 2 - 1
examples/scripts/longtermsecuredb/secure_relay_with_db_mongo.sh

@@ -21,6 +21,7 @@
 # 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:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
+# 11) --oauth - accept oAuth security dialog
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -30,4 +31,4 @@ 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 --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:SSLv2 $@
+PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --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:SSLv2 --oauth $@

+ 2 - 1
examples/scripts/longtermsecuredb/secure_relay_with_db_mysql.sh

@@ -22,6 +22,7 @@
 # 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:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
+# 11) --oauth - accept oAuth security dialog
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -31,4 +32,4 @@ 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 --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" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL:SSLv2 $@
+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 --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" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL:SSLv2 --oauth $@

+ 2 - 1
examples/scripts/longtermsecuredb/secure_relay_with_db_mysql_ssl.sh

@@ -23,6 +23,7 @@
 # 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:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
+# 11) --oauth - accept oAuth security dialog
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -32,4 +33,4 @@ 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 --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 -r north.gov --mysql-userdb="host=localhost dbname=turn user=turn password=turn cipher=DHE-RSA-AES256-SHA connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL:SSLv2 $@
+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 --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" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL:SSLv2 --oauth $@

+ 3 - 2
examples/scripts/longtermsecuredb/secure_relay_with_db_psql.sh

@@ -22,6 +22,7 @@
 # 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:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
+# 11) --oauth - accept oAuth security dialog
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -31,7 +32,7 @@ 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 --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:SSLv2 $@ 
+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 --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:SSLv2 --oauth $@ 
 
 # Newer PostgreSQL style connection string example:
-# PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --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:SSLv2 $@
+# 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 --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:SSLv2 --oauth $@

+ 2 - 1
examples/scripts/longtermsecuredb/secure_relay_with_db_redis.sh

@@ -25,6 +25,7 @@
 # 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:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
+# 12) --oauth - accept oAuth security dialog
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -34,4 +35,4 @@ 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 --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:SSLv2 $@
+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 --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:SSLv2 --oauth $@

+ 3 - 1
man/man1/turnadmin.1

@@ -1,5 +1,5 @@
 .\" Text automatically generated by txt2man
-.TH TURN 1 "18 August 2014" "" ""
+.TH TURN 1 "28 September 2014" "" ""
 .SH GENERAL INFORMATION
 
 \fIturnadmin\fP is a TURN administration tool. This tool can be used to manage 
@@ -330,3 +330,5 @@ Peter Dunkley <[email protected]>
 Mutsutoshi Yoshimoto <[email protected]>
 .PP
 Federico Pinna <[email protected]>
+.PP
+Bradley T. Hughes <[email protected]>

+ 13 - 1
man/man1/turnserver.1

@@ -1,5 +1,5 @@
 .\" Text automatically generated by txt2man
-.TH TURN 1 "18 August 2014" "" ""
+.TH TURN 1 "28 September 2014" "" ""
 .SH GENERAL INFORMATION
 
 The \fBTURN Server\fP project contains the source code of a TURN server and TURN client 
@@ -276,6 +276,10 @@ This option can be used with long\-term credentials mechanisms only \-
 it does not make much sense with the short\-term mechanism.
 .TP
 .B
+\fB\-\-oauth\fP
+Support oAuth authentication.
+.TP
+.B
 \fB\-\-dh566\fP
 Use 566 bits predefined DH TLS key. Default size of the key is 1066.
 .TP
@@ -612,6 +616,12 @@ that other mode is dynamic. Multiple shared secrets can be used
 (both in the database and in the "static" fashion).
 .TP
 .B
+\fB\-\-server\-name\fP
+Server name used for
+the oAuth authentication purposes.
+The default value is the realm name.
+.TP
+.B
 \fB\-\-cert\fP
 Certificate file, PEM format. Same file 
 search rules applied as for the configuration 
@@ -1176,3 +1186,5 @@ Peter Dunkley <[email protected]>
 Mutsutoshi Yoshimoto <[email protected]>
 .PP
 Federico Pinna <[email protected]>
+.PP
+Bradley T. Hughes <[email protected]>

+ 7 - 1
man/man1/turnutils.1

@@ -1,5 +1,5 @@
 .\" Text automatically generated by txt2man
-.TH TURN 1 "18 August 2014" "" ""
+.TH TURN 1 "28 September 2014" "" ""
 .SH GENERAL INFORMATION
 
 A set of turnutils_* programs provides some utility functionality to be used
@@ -177,6 +177,10 @@ Random disconnect after a few initial packets.
 Dual allocation (SSODA draft support).
 .TP
 .B
+\fB\-J\fP
+Use oAuth with default test key kid='north'.
+.TP
+.B
 Options with required values:
 .TP
 .B
@@ -455,3 +459,5 @@ Peter Dunkley <[email protected]>
 Mutsutoshi Yoshimoto <[email protected]>
 .PP
 Federico Pinna <[email protected]>
+.PP
+Bradley T. Hughes <[email protected]>

+ 1 - 1
rpm/build.settings.sh

@@ -2,7 +2,7 @@
 
 # Common settings script.
 
-TURNVERSION=4.1.2.1
+TURNVERSION=4.2.1.2
 BUILDDIR=~/rpmbuild
 ARCH=`uname -p`
 TURNSERVER_SVN_URL=http://coturn.googlecode.com/svn

+ 3 - 1
rpm/turnserver.spec

@@ -1,5 +1,5 @@
 Name:		turnserver
-Version:	4.1.2.1
+Version:	4.2.1.2
 Release:	0%{dist}
 Summary:	Coturn TURN Server
 
@@ -294,6 +294,8 @@ fi
 %{_includedir}/turn/client/TurnMsgLib.h
 
 %changelog
+* Sun Oct 05 2014 Oleg Moskalenko <[email protected]>
+  - Sync to 4.2.1.2
 * Thu Aug 14 2014 Oleg Moskalenko <[email protected]>
   - Sync to 4.1.2.1
 * Tue Jul 29 2014 Oleg Moskalenko <[email protected]>

+ 52 - 3
src/apps/common/apputils.c

@@ -549,7 +549,7 @@ void set_execdir(void)
   /* On some systems, this may give us the execution path */
   char *_var = getenv("_");
   if(_var && *_var) {
-    _var = strdup(_var);
+    _var = turn_strdup(_var);
     char *edir=_var;
     if(edir[0]!='.') 
       edir = strstr(edir,"/");
@@ -559,7 +559,7 @@ void set_execdir(void)
       edir = dirname(_var);
     if(c_execdir)
       turn_free(c_execdir,strlen(c_execdir)+1);
-    c_execdir = strdup(edir);
+    c_execdir = turn_strdup(edir);
     turn_free(_var,strlen(_var)+1);
   }
 }
@@ -604,7 +604,7 @@ char* find_config_file(const char *config_file, int print_file_name)
 			FILE *f = fopen(config_file, "r");
 			if (f) {
 				fclose(f);
-				full_path_to_config_file = strdup(config_file);
+				full_path_to_config_file = turn_strdup(config_file);
 			}
 		} else {
 			int i = 0;
@@ -899,4 +899,53 @@ struct event_base *turn_event_base_new(void)
 	return event_base_new_with_config(cfg);
 }
 
+/////////// OAUTH /////////////////
+
+void convert_oauth_key_data_raw(const oauth_key_data_raw *raw, oauth_key_data *oakd)
+{
+	if(raw && oakd) {
+
+		ns_bzero(oakd,sizeof(oauth_key_data));
+
+		oakd->timestamp = (turn_time_t)raw->timestamp;
+		oakd->lifetime = raw->lifetime;
+
+		ns_bcopy(raw->as_rs_alg,oakd->as_rs_alg,sizeof(oakd->as_rs_alg));
+		ns_bcopy(raw->auth_alg,oakd->auth_alg,sizeof(oakd->auth_alg));
+		ns_bcopy(raw->hkdf_hash_func,oakd->hkdf_hash_func,sizeof(oakd->hkdf_hash_func));
+		ns_bcopy(raw->kid,oakd->kid,sizeof(oakd->kid));
+
+		if(raw->ikm_key[0]) {
+			size_t ikm_key_size = 0;
+			char *ikm_key = (char*)base64_decode(raw->ikm_key,strlen(raw->ikm_key),&ikm_key_size);
+			if(ikm_key) {
+				ns_bcopy(ikm_key,oakd->ikm_key,ikm_key_size);
+				oakd->ikm_key_size = ikm_key_size;
+				turn_free(ikm_key,ikm_key_size);
+			}
+		}
+
+		if(raw->as_rs_key[0]) {
+			size_t as_rs_key_size = 0;
+			char *as_rs_key = (char*)base64_decode(raw->as_rs_key,strlen(raw->as_rs_key),&as_rs_key_size);
+			if(as_rs_key) {
+				ns_bcopy(as_rs_key,oakd->as_rs_key,as_rs_key_size);
+				oakd->as_rs_key_size = as_rs_key_size;
+				turn_free(as_rs_key,as_rs_key_size);
+			}
+		}
+
+		if(raw->auth_key[0]) {
+			size_t auth_key_size = 0;
+			char *auth_key = (char*)base64_decode(raw->auth_key,strlen(raw->auth_key),&auth_key_size);
+			if(auth_key) {
+				ns_bcopy(auth_key,oakd->auth_key,auth_key_size);
+				oakd->auth_key_size = auth_key_size;
+				turn_free(auth_key,auth_key_size);
+			}
+		}
+
+	}
+}
+
 //////////////////////////////////////////////////////////////

+ 21 - 0
src/apps/common/apputils.h

@@ -36,6 +36,7 @@
 #include <openssl/ssl.h>
 
 #include "ns_turn_ioaddr.h"
+#include "ns_turn_msg_defs.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -69,6 +70,22 @@ enum _TURN_TLS_TYPE {
 
 typedef enum _TURN_TLS_TYPE TURN_TLS_TYPE;
 
+////////////////////////////////////////////
+
+struct _oauth_key_data_raw {
+	char kid[OAUTH_KID_SIZE+1];
+	char ikm_key[OAUTH_KEY_SIZE+1];
+	u64bits timestamp;
+	u32bits lifetime;
+	char hkdf_hash_func[OAUTH_HASH_FUNC_SIZE+1];
+	char as_rs_alg[OAUTH_ALG_SIZE+1];
+	char as_rs_key[OAUTH_KEY_SIZE+1];
+	char auth_alg[OAUTH_ALG_SIZE+1];
+	char auth_key[OAUTH_KEY_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; }
@@ -150,6 +167,10 @@ unsigned char *base64_decode(const char *data,
 
 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);

+ 5 - 5
src/apps/common/hiredis_libevent2.c

@@ -91,7 +91,7 @@ static void redisLibeventReadEvent(int fd, short event, void *arg) {
 		  } while((len<0)&&(errno == EINTR));
 		  if(len<1) {
 			  e->invalid = 1;
-			  TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Redis connection broken: e=0x%lx\n", __FUNCTION__, (unsigned long)e);
+			  TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Redis connection broken: e=0x%lx\n", __FUNCTION__, ((unsigned long)e));
 		  }
 	  }
 	  if(redis_le_valid(e)) {
@@ -213,7 +213,7 @@ void send_message_to_redis(redis_context_handle rch, const char *command, const
 
 			if((redisAsyncCommand(ac, NULL, e, rm.format, rm.arg)!=REDIS_OK)) {
 				e->invalid = 1;
-				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Redis connection broken: ac=0x%lx, e=0x%x\n", __FUNCTION__,(unsigned long)ac,(unsigned long)e);
+				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);
 			}
 		}
 	}
@@ -250,10 +250,10 @@ redis_context_handle redisLibeventAttach(struct event_base *base, char *ip0, int
   e->allocated = 1;
   e->context = ac;
   e->base = base;
-  e->ip = strdup(ip);
+  e->ip = turn_strdup(ip);
   e->port = port;
   if(pwd)
-	  e->pwd = strdup(pwd);
+	  e->pwd = turn_strdup(pwd);
   e->db = db;
 
   /* Register functions to start/stop listening for events */
@@ -372,7 +372,7 @@ static void redis_reconnect(struct redisLibeventEvents *e)
   }
 
   if(redis_le_valid(e)) {
-	  TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Re-connected to redis, async\n", __FUNCTION__);
+	  TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: Re-connected to redis, async\n", __FUNCTION__);
   }
 }
 

+ 196 - 4
src/apps/common/ns_turn_utils.c

@@ -32,6 +32,8 @@
 #include "ns_turn_ioalib.h"
 #include "ns_turn_msg_defs.h"
 
+#include <event2/http.h>
+
 #include <time.h>
 
 #include <pthread.h>
@@ -293,11 +295,11 @@ static void set_log_file_name_func(char *base, char *f, size_t fsz)
 	}
 
 	char logdate[125];
-	char *tail=strdup(".log");
+	char *tail=turn_strdup(".log");
 
 	get_date(logdate,sizeof(logdate));
 
-	char *base1=strdup(base);
+	char *base1=turn_strdup(base);
 
 	int len=(int)strlen(base1);
 
@@ -317,11 +319,11 @@ static void set_log_file_name_func(char *base, char *f, size_t fsz)
 			break;
 		else if(base1[len]=='.') {
 			turn_free(tail,strlen(tail)+1);
-			tail=strdup(base1+len);
+			tail=turn_strdup(base1+len);
 			base1[len]=0;
 			if(strlen(tail)<2) {
 				turn_free(tail,strlen(tail)+1);
-				tail = strdup(".log");
+				tail = turn_strdup(".log");
 			}
 			break;
 		}
@@ -654,3 +656,193 @@ int get_canonic_origin(const char* o, char *co, int sz)
 }
 
 //////////////////////////////////////////////////////////////////
+
+#ifdef __cplusplus
+#if defined(TURN_MEMORY_DEBUG)
+
+#include <map>
+#include <set>
+#include <string>
+
+using namespace std;
+
+static volatile int tmm_init = 0;
+static pthread_mutex_t tm;
+
+typedef void* ptrtype;
+typedef set<ptrtype> ptrs_t;
+typedef map<string,ptrs_t> str_to_ptrs_t;
+typedef map<ptrtype,string> ptr_to_str_t;
+
+static str_to_ptrs_t str_to_ptrs;
+static ptr_to_str_t ptr_to_str;
+
+static void tm_init(void) {
+  if(!tmm_init) {
+    pthread_mutex_init(&tm,NULL);
+    tmm_init = 1;
+  }
+}
+
+static void add_tm_ptr(void *ptr, const char *id) {
+
+  UNUSED_ARG(ptr);
+  UNUSED_ARG(id);
+
+  if(!ptr)
+    return;
+
+  string sid(id);
+
+  str_to_ptrs_t::iterator iter;
+
+  pthread_mutex_lock(&tm);
+
+  iter = str_to_ptrs.find(sid);
+
+  if(iter == str_to_ptrs.end()) {
+    set<ptrtype> sp;
+    sp.insert(ptr);
+    str_to_ptrs[sid]=sp;
+  } else {
+	iter->second.insert(ptr);
+  }
+
+  ptr_to_str[ptr]=sid;
+
+  pthread_mutex_unlock(&tm);
+}
+
+static void del_tm_ptr(void *ptr, const char *id) {
+
+  UNUSED_ARG(ptr);
+  UNUSED_ARG(id);
+
+  if(!ptr)
+    return;
+
+  pthread_mutex_lock(&tm);
+
+  ptr_to_str_t::iterator pts_iter = ptr_to_str.find(ptr);
+  if(pts_iter == ptr_to_str.end()) {
+
+	  printf("Tring to free unknown pointer (1): %s\n",id);
+
+  } else {
+
+    string sid = pts_iter->second;
+    ptr_to_str.erase(pts_iter);
+
+    str_to_ptrs_t::iterator iter = str_to_ptrs.find(sid);
+
+    if(iter == str_to_ptrs.end()) {
+
+    	printf("Tring to free unknown pointer (2): %s\n",id);
+
+    } else {
+
+      iter->second.erase(ptr);
+
+    }
+  }
+
+  pthread_mutex_unlock(&tm);
+}
+
+static void tm_id(char *id, const char* file, int line) {
+  sprintf(id,"%s:%d",file,line);
+}
+
+#define TM_START() char id[128];tm_id(id,file,line);tm_init()
+
+extern "C" void tm_print_func(void);
+void tm_print_func(void) {
+  pthread_mutex_lock(&tm);
+  printf("=============================================\n");
+  for(str_to_ptrs_t::const_iterator iter=str_to_ptrs.begin();iter != str_to_ptrs.end();++iter) {
+	  if(iter->second.size())
+		  printf("%s: %s: %d\n",__FUNCTION__,iter->first.c_str(),(int)(iter->second.size()));
+  }
+  printf("=============================================\n");
+  pthread_mutex_unlock(&tm);
+} 
+
+extern "C" void *turn_malloc_func(size_t sz, const char* file, int line);
+void *turn_malloc_func(size_t sz, const char* file, int line) {
+
+  TM_START();
+
+  void *ptr = malloc(sz);
+  
+  add_tm_ptr(ptr,id);
+
+  return ptr;
+}
+
+extern "C" void *turn_realloc_func(void *ptr, size_t old_sz, size_t new_sz, const char* file, int line);
+void *turn_realloc_func(void *ptr, size_t old_sz, size_t new_sz, const char* file, int line) {
+
+  UNUSED_ARG(old_sz);
+
+  TM_START();
+
+  if(ptr)
+	  del_tm_ptr(ptr,id);
+
+  ptr = realloc(ptr,new_sz);
+
+  add_tm_ptr(ptr,id);
+
+  return ptr;
+}
+
+extern "C" void turn_free_func(void *ptr, size_t sz, const char* file, int line);
+void turn_free_func(void *ptr, size_t sz, const char* file, int line) {
+
+  UNUSED_ARG(sz);
+
+  TM_START();
+
+  del_tm_ptr(ptr,id);
+
+  free(ptr);
+}
+
+extern "C" void turn_free_simple(void *ptr);
+void turn_free_simple(void *ptr) {
+
+  tm_init();
+
+  del_tm_ptr(ptr,__FUNCTION__);
+
+  free(ptr);
+}
+
+extern "C" void *turn_calloc_func(size_t number, size_t size, const char* file, int line);
+void *turn_calloc_func(size_t number, size_t size, const char* file, int line) {
+  
+  TM_START();
+
+  void *ptr = calloc(number,size);
+
+  add_tm_ptr(ptr,id);
+
+  return ptr;
+}
+
+extern "C" char *turn_strdup_func(const char* s, const char* file, int line);
+char *turn_strdup_func(const char* s, const char* file, int line) {
+
+  TM_START();
+
+  char *ptr = strdup(s);
+
+  add_tm_ptr(ptr,id);
+
+  return ptr;
+}
+
+#endif
+#endif
+
+//////////////////////////////////////////////////////////////////

+ 1 - 2
src/apps/common/ns_turn_utils.h

@@ -32,13 +32,12 @@
 #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"
 
-#include <event2/http.h>
-
 #ifdef __cplusplus
 extern "C" {
 #endif

+ 348 - 107
src/apps/relay/dbdrivers/dbd_mongo.c

@@ -52,8 +52,10 @@ static void mongo_logger(mongoc_log_level_t log_level, const char * log_domain,
 	UNUSED_ARG(log_domain);
 	UNUSED_ARG(user_data);
 
-  TURN_LOG_LEVEL l;
+  TURN_LOG_LEVEL l = TURN_LOG_LEVEL_INFO;
   
+  UNUSED_ARG(l);
+
   switch(log_level) {
     case MONGOC_LOG_LEVEL_ERROR:
       l = TURN_LOG_LEVEL_ERROR;
@@ -72,7 +74,7 @@ static void MongoFree(MONGO * info) {
 	if(info) {
 		if(info->uri) mongoc_uri_destroy(info->uri);
 		if(info->client) mongoc_client_destroy(info->client);
-    turn_free(info, sizeof(MONGO));
+		turn_free(info, sizeof(MONGO));
 	}
 }
 
@@ -234,6 +236,79 @@ static int mongo_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
   bson_destroy(&fields);
   return ret;
 }
+
+static int mongo_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
+
+	mongoc_collection_t * collection = mongo_get_collection("oauth_key");
+
+	if (!collection)
+		return -1;
+
+	bson_t query;
+	bson_init(&query);
+	BSON_APPEND_UTF8(&query, "kid", (const char *)kid);
+
+	bson_t fields;
+	bson_init(&fields);
+	BSON_APPEND_INT32(&fields, "lifetime", 1);
+	BSON_APPEND_INT32(&fields, "timestamp", 1);
+	BSON_APPEND_INT32(&fields, "as_rs_alg", 1);
+	BSON_APPEND_INT32(&fields, "as_rs_key", 1);
+	BSON_APPEND_INT32(&fields, "auth_alg", 1);
+	BSON_APPEND_INT32(&fields, "auth_key", 1);
+	BSON_APPEND_INT32(&fields, "hkdf_hash_func", 1);
+	BSON_APPEND_INT32(&fields, "ikm_key", 1);
+
+	mongoc_cursor_t * cursor;
+	cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 1, 0,
+			&query, &fields, NULL);
+
+	int ret = -1;
+
+	ns_bzero(key,sizeof(oauth_key_data_raw));
+	STRCPY(key->kid,kid);
+
+	if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,
+				"Error querying MongoDB collection 'oauth_key'\n");
+	} else {
+		const bson_t * item;
+		uint32_t length;
+		bson_iter_t iter;
+		if (mongoc_cursor_next(cursor, &item)) {
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "as_rs_alg") && BSON_ITER_HOLDS_UTF8(&iter)) {
+				STRCPY(key->as_rs_alg,bson_iter_utf8(&iter, &length));
+			}
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "as_rs_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
+				STRCPY(key->as_rs_key,bson_iter_utf8(&iter, &length));
+			}
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "auth_alg") && BSON_ITER_HOLDS_UTF8(&iter)) {
+				STRCPY(key->auth_alg,bson_iter_utf8(&iter, &length));
+			}
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "auth_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
+				STRCPY(key->auth_key,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, "hkdf_hash_func") && BSON_ITER_HOLDS_UTF8(&iter)) {
+				STRCPY(key->hkdf_hash_func,bson_iter_utf8(&iter, &length));
+			}
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "timestamp") && BSON_ITER_HOLDS_INT64(&iter)) {
+				key->timestamp = (u64bits)bson_iter_int64(&iter);
+			}
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "lifetime") && BSON_ITER_HOLDS_INT32(&iter)) {
+				key->lifetime = (u32bits)bson_iter_int32(&iter);
+			}
+			ret = 0;
+		}
+		mongoc_cursor_destroy(cursor);
+	}
+	mongoc_collection_destroy(collection);
+	bson_destroy(&query);
+	bson_destroy(&fields);
+	return ret;
+}
   
 static int mongo_get_user_pwd(u08bits *usname, st_password_t pwd) {
   mongoc_collection_t * collection = mongo_get_collection("turnusers_st"); 
@@ -302,7 +377,43 @@ static int mongo_set_user_key(u08bits *usname, u08bits *realm, 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 secret key information\n");
+    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, "as_rs_key", (const char *)key->as_rs_key);
+  BSON_APPEND_UTF8(&doc, "auth_alg", (const char *)key->auth_alg);
+  BSON_APPEND_UTF8(&doc, "auth_key", (const char *)key->auth_key);
+  BSON_APPEND_UTF8(&doc, "hkdf_hash_func", (const char *)key->hkdf_hash_func);
+  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;
   }
@@ -364,6 +475,29 @@ static int mongo_del_user(u08bits *usname, int is_st, u08bits *realm) {
   bson_destroy(&query);
   return ret;
 }
+
+static int mongo_del_oauth_key(const u08bits *kid) {
+
+  mongoc_collection_t * collection = mongo_get_collection("oauth_key");
+
+  if(!collection)
+    return -1;
+
+  bson_t query;
+  bson_init(&query);
+  BSON_APPEND_UTF8(&query, "kid", (const char *)kid);
+
+  int ret = -1;
+
+  if (!mongoc_collection_delete(collection, MONGOC_DELETE_SINGLE_REMOVE, &query, NULL, NULL)) {
+    TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting oauth key information\n");
+  } else {
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  return ret;
+}
   
 static int mongo_list_users(int is_st, u08bits *realm) {
   const char * collection_name = is_st ? "turnusers_st" : "turnusers_lt";
@@ -425,6 +559,92 @@ static int mongo_list_users(int is_st, u08bits *realm) {
   bson_destroy(&fields);
   return ret;
 }
+
+static int mongo_list_oauth_keys(void) {
+
+  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, "as_rs_key", 1);
+  BSON_APPEND_INT32(&fields, "auth_alg", 1);
+  BSON_APPEND_INT32(&fields, "auth_key", 1);
+  BSON_APPEND_INT32(&fields, "hkdf_hash_func", 1);
+  BSON_APPEND_INT32(&fields, "ikm_key", 1);
+
+  mongoc_cursor_t * cursor;
+  cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
+
+  int ret = -1;
+
+  if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection '%s'\n", collection_name);
+  } else {
+    const bson_t * item;
+	oauth_key_data_raw key_;
+	oauth_key_data_raw *key=&key_;
+    uint32_t length;
+    bson_iter_t iter;
+    while (mongoc_cursor_next(cursor, &item)) {
+
+    	ns_bzero(key,sizeof(oauth_key_data_raw));
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "kid") && BSON_ITER_HOLDS_UTF8(&iter)) {
+    		STRCPY(key->kid,bson_iter_utf8(&iter, &length));
+    	}
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "as_rs_alg") && BSON_ITER_HOLDS_UTF8(&iter)) {
+    	    STRCPY(key->as_rs_alg,bson_iter_utf8(&iter, &length));
+    	}
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "as_rs_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
+    		STRCPY(key->as_rs_key,bson_iter_utf8(&iter, &length));
+    	}
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "auth_alg") && BSON_ITER_HOLDS_UTF8(&iter)) {
+    		STRCPY(key->auth_alg,bson_iter_utf8(&iter, &length));
+    	}
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "auth_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
+    		STRCPY(key->auth_key,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, "hkdf_hash_func") && BSON_ITER_HOLDS_UTF8(&iter)) {
+    		STRCPY(key->hkdf_hash_func,bson_iter_utf8(&iter, &length));
+    	}
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "timestamp") && BSON_ITER_HOLDS_INT64(&iter)) {
+    		key->timestamp = (u64bits)bson_iter_int64(&iter);
+    	}
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "lifetime") && BSON_ITER_HOLDS_INT32(&iter)) {
+    		key->lifetime = (u32bits)bson_iter_int32(&iter);
+    	}
+    	printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, hkdf_hash_func=%s, as_rs_alg=%s, as_rs_key=%s, auth_alg=%s, auth_key=%s\n",
+    		key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->hkdf_hash_func,
+    		key->as_rs_alg, key->as_rs_key, key->auth_alg, key->auth_key);
+    }
+    mongoc_cursor_destroy(cursor);
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  bson_destroy(&fields);
+  return ret;
+}
   
 static int mongo_show_secret(u08bits *realm) {
   mongoc_collection_t * collection = mongo_get_collection("turn_secret"); 
@@ -642,7 +862,8 @@ static int mongo_set_realm_option_one(u08bits *realm, unsigned long value, const
   BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
   bson_init(&doc);
   
-  char * _k = (char *)turn_malloc(9 + strlen(opt));
+  size_t klen = 9 + strlen(opt);
+  char * _k = (char *)turn_malloc(klen);
   strcpy(_k, "options.");
   strcat(_k, opt);
   
@@ -655,7 +876,7 @@ static int mongo_set_realm_option_one(u08bits *realm, unsigned long value, const
     BSON_APPEND_INT32(&child, _k, 1);
     bson_append_document_end(&doc, &child);
   }
-  free(_k);
+  turn_free(_k,klen);
   
   int ret = -1;
   
@@ -792,113 +1013,129 @@ static int mongo_get_ip_list(const char *kind, ip_range_list_t * list) {
   return ret;
 }
   
-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, (const 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();
+static void mongo_reread_realms(secrets_list_t * realms_list) {
 
-        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;
+	UNUSED_ARG(realms_list);
 
-          bson_iter_document(&iter, &doclen, &docbuf);
-          bson_init_static(&options, docbuf, doclen);
+	mongoc_collection_t * collection = mongo_get_collection("realm");
+
+	if (!collection)
+		return;
+
+	bson_t query;
+	bson_init(&query);
+
+	bson_t fields;
+	bson_init(&fields);
+	BSON_APPEND_INT32(&fields, "realm", 1);
+	BSON_APPEND_INT32(&fields, "origin", 1);
+	BSON_APPEND_INT32(&fields, "options", 1);
+
+	mongoc_cursor_t * cursor;
+	cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0,
+			&query, &fields, NULL);
+
+	if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,
+				"Error querying MongoDB collection 'realm'\n");
+	} else {
+		ur_string_map *o_to_realm_new = ur_string_map_create(turn_free_simple);
+
+		const bson_t * item;
+		uint32_t length;
+		bson_iter_t iter;
+
+		while (mongoc_cursor_next(cursor, &item)) {
+
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm")
+					&& BSON_ITER_HOLDS_UTF8(&iter)) {
+
+				char * _realm = turn_strdup(bson_iter_utf8(&iter, &length));
+
+				get_realm(_realm);
+
+				if (bson_iter_init(&iter, item) && bson_iter_find(&iter,
+						"origin") && BSON_ITER_HOLDS_ARRAY(&iter)) {
+					const uint8_t *docbuf = NULL;
+					uint32_t doclen = 0;
+					bson_t origin_array;
+					bson_iter_t origin_iter;
+
+					bson_iter_array(&iter, &doclen, &docbuf);
+					bson_init_static(&origin_array, docbuf, doclen);
+
+					if (bson_iter_init(&origin_iter, &origin_array)) {
+						while (bson_iter_next(&origin_iter)) {
+							if (BSON_ITER_HOLDS_UTF8(&origin_iter)) {
+								char* _origin =	turn_strdup(bson_iter_utf8(&origin_iter, &length));
+								char *rval = turn_strdup(_realm);
+								ur_string_map_value_type value =
+										(ur_string_map_value_type) (rval);
+								ur_string_map_put(o_to_realm_new,
+										(const ur_string_map_key_type) _origin,
+										value);
+								turn_free(_origin,strlen(_origin)+1);
+							}
+						}
+					}
+				}
 
-          if (bson_iter_init(&options_iter, &options)) {
-            while(bson_iter_next(&options_iter)) {
-              const char * _k = bson_iter_key(&options_iter);
-              int32_t _v = 0;
-              if (BSON_ITER_HOLDS_DOUBLE(&options_iter)) {
-                _v = (int32_t)bson_iter_double(&options_iter);
-              } else if (BSON_ITER_HOLDS_INT32(&options_iter)) {
-                _v = bson_iter_int32(&options_iter);
-              } else if (BSON_ITER_HOLDS_INT64(&options_iter)) {
-                _v = (int32_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;
+				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);
+							int32_t _v = 0;
+							if (BSON_ITER_HOLDS_DOUBLE(&options_iter)) {
+								_v = (int32_t) bson_iter_double(&options_iter);
+							} else if (BSON_ITER_HOLDS_INT32(&options_iter)) {
+								_v = bson_iter_int32(&options_iter);
+							} else if (BSON_ITER_HOLDS_INT64(&options_iter)) {
+								_v = (int32_t) bson_iter_int64(&options_iter);
+							}
+							if (_v) {
+								if (!strcmp(_k, "max-bps"))
+									rp->options.perf_options.max_bps
+											= (band_limit_t) _v;
+								else if (!strcmp(_k, "total-quota"))
+									rp->options.perf_options.total_quota
+											= (vint) _v;
+								else if (!strcmp(_k, "user-quota"))
+									rp->options.perf_options.user_quota
+											= (vint) _v;
 								else {
-									TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown realm option: %s\n", _k);
+									TURN_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);
+							}
+						}
+					}
+				}
+				turn_free(_realm,strlen(_realm)+1);
+			}
+		}
+		update_o_to_realm(o_to_realm_new);
+		mongoc_cursor_destroy(cursor);
+	}
+	mongoc_collection_destroy(collection);
+	bson_destroy(&query);
+	bson_destroy(&fields);
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -921,7 +1158,11 @@ static turn_dbdriver_t driver = {
   &mongo_list_realm_options,
   &mongo_auth_ping,
   &mongo_get_ip_list,
-  &mongo_reread_realms
+  &mongo_reread_realms,
+  &mongo_set_oauth_key,
+  &mongo_get_oauth_key,
+  &mongo_del_oauth_key,
+  &mongo_list_oauth_keys
 };
 
 turn_dbdriver_t * get_mongo_dbdriver(void) {

+ 228 - 51
src/apps/relay/dbdrivers/dbd_mysql.c

@@ -76,7 +76,7 @@ static Myconninfo *MyconninfoParse(char *userdb, char **errmsg) {
 	Myconninfo *co = (Myconninfo*)turn_malloc(sizeof(Myconninfo));
 	ns_bzero(co,sizeof(Myconninfo));
 	if(userdb) {
-		char *s0=strdup(userdb);
+		char *s0=turn_strdup(userdb);
 		char *s = s0;
 
 		while(s && *s) {
@@ -93,44 +93,44 @@ static Myconninfo *MyconninfoParse(char *userdb, char **errmsg) {
 				MyconninfoFree(co);
 				co = NULL;
 				if(errmsg) {
-					*errmsg = strdup(s);
+					*errmsg = turn_strdup(s);
 				}
 				break;
 			}
 
 			*seq = 0;
 			if(!strcmp(s,"host"))
-				co->host = strdup(seq+1);
+				co->host = turn_strdup(seq+1);
 			else if(!strcmp(s,"ip"))
-				co->host = strdup(seq+1);
+				co->host = turn_strdup(seq+1);
 			else if(!strcmp(s,"addr"))
-				co->host = strdup(seq+1);
+				co->host = turn_strdup(seq+1);
 			else if(!strcmp(s,"ipaddr"))
-				co->host = strdup(seq+1);
+				co->host = turn_strdup(seq+1);
 			else if(!strcmp(s,"hostaddr"))
-				co->host = strdup(seq+1);
+				co->host = turn_strdup(seq+1);
 			else if(!strcmp(s,"dbname"))
-				co->dbname = strdup(seq+1);
+				co->dbname = turn_strdup(seq+1);
 			else if(!strcmp(s,"db"))
-				co->dbname = strdup(seq+1);
+				co->dbname = turn_strdup(seq+1);
 			else if(!strcmp(s,"database"))
-				co->dbname = strdup(seq+1);
+				co->dbname = turn_strdup(seq+1);
 			else if(!strcmp(s,"user"))
-				co->user = strdup(seq+1);
+				co->user = turn_strdup(seq+1);
 			else if(!strcmp(s,"uname"))
-				co->user = strdup(seq+1);
+				co->user = turn_strdup(seq+1);
 			else if(!strcmp(s,"name"))
-				co->user = strdup(seq+1);
+				co->user = turn_strdup(seq+1);
 			else if(!strcmp(s,"username"))
-				co->user = strdup(seq+1);
+				co->user = turn_strdup(seq+1);
 			else if(!strcmp(s,"password"))
-				co->password = strdup(seq+1);
+				co->password = turn_strdup(seq+1);
 			else if(!strcmp(s,"pwd"))
-				co->password = strdup(seq+1);
+				co->password = turn_strdup(seq+1);
 			else if(!strcmp(s,"passwd"))
-				co->password = strdup(seq+1);
+				co->password = turn_strdup(seq+1);
 			else if(!strcmp(s,"secret"))
-				co->password = strdup(seq+1);
+				co->password = turn_strdup(seq+1);
 			else if(!strcmp(s,"port"))
 				co->port = (unsigned int)atoi(seq+1);
 			else if(!strcmp(s,"p"))
@@ -140,30 +140,30 @@ static Myconninfo *MyconninfoParse(char *userdb, char **errmsg) {
 			else if(!strcmp(s,"timeout"))
 				co->connect_timeout = (unsigned int)atoi(seq+1);
 			else if(!strcmp(s,"key"))
-				co->key = strdup(seq+1);
+				co->key = turn_strdup(seq+1);
 			else if(!strcmp(s,"ssl-key"))
-				co->key = strdup(seq+1);
+				co->key = turn_strdup(seq+1);
 			else if(!strcmp(s,"ca"))
-				co->ca = strdup(seq+1);
+				co->ca = turn_strdup(seq+1);
 			else if(!strcmp(s,"ssl-ca"))
-				co->ca = strdup(seq+1);
+				co->ca = turn_strdup(seq+1);
 			else if(!strcmp(s,"capath"))
-				co->capath = strdup(seq+1);
+				co->capath = turn_strdup(seq+1);
 			else if(!strcmp(s,"ssl-capath"))
-				co->capath = strdup(seq+1);
+				co->capath = turn_strdup(seq+1);
 			else if(!strcmp(s,"cert"))
-				co->cert = strdup(seq+1);
+				co->cert = turn_strdup(seq+1);
 			else if(!strcmp(s,"ssl-cert"))
-				co->cert = strdup(seq+1);
+				co->cert = turn_strdup(seq+1);
 			else if(!strcmp(s,"cipher"))
-				co->cipher = strdup(seq+1);
+				co->cipher = turn_strdup(seq+1);
 			else if(!strcmp(s,"ssl-cipher"))
-				co->cipher = strdup(seq+1);
+				co->cipher = turn_strdup(seq+1);
 			else {
 				MyconninfoFree(co);
 				co = NULL;
 				if(errmsg) {
-					*errmsg = strdup(s);
+					*errmsg = turn_strdup(s);
 				}
 				break;
 			}
@@ -176,13 +176,13 @@ static Myconninfo *MyconninfoParse(char *userdb, char **errmsg) {
 
 	if(co) {
 		if(!(co->dbname))
-			co->dbname=strdup("0");
+			co->dbname=turn_strdup("0");
 		if(!(co->host))
-			co->host=strdup("127.0.0.1");
+			co->host=turn_strdup("127.0.0.1");
 		if(!(co->user))
-			co->user=strdup("");
+			co->user=turn_strdup("");
 		if(!(co->password))
-			co->password=strdup("");
+			co->password=turn_strdup("");
 	}
 
 	return co;
@@ -335,7 +335,9 @@ static int mysql_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
 }
   
 static int mysql_get_user_pwd(u08bits *usname, st_password_t pwd) {
-  int ret = 1;
+
+  int ret = -1;
+
 	char statement[TURN_LONG_STRING_SIZE];
 	snprintf(statement,sizeof(statement),"select password from turnusers_st where name='%s'",usname);
 
@@ -372,9 +374,143 @@ static int mysql_get_user_pwd(u08bits *usname, st_password_t pwd) {
 	}
   return ret;
 }
+
+static int mysql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
+
+	int ret = -1;
+	char statement[TURN_LONG_STRING_SIZE];
+	snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key from oauth_key where kid='%s'",(const char*)kid);
+
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		int res = mysql_query(myc, statement);
+		if(res) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc));
+		} else {
+			MYSQL_RES *mres = mysql_store_result(myc);
+			if(!mres) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc));
+			} else if(mysql_field_count(myc)!=8) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement);
+			} else {
+				MYSQL_ROW row = mysql_fetch_row(mres);
+				if(row && row[0]) {
+					unsigned long *lengths = mysql_fetch_lengths(mres);
+					if(lengths) {
+						STRCPY((char*)key->kid,kid);
+						ns_bcopy(row[0],key->ikm_key,lengths[0]);
+						key->ikm_key[lengths[0]]=0;
+
+						char stimestamp[128];
+						ns_bcopy(row[1],stimestamp,lengths[1]);
+						stimestamp[lengths[1]]=0;
+						key->timestamp = (u64bits)strtoull(stimestamp,NULL,10);
+
+						char slifetime[128];
+						ns_bcopy(row[2],slifetime,lengths[2]);
+						slifetime[lengths[2]]=0;
+						key->lifetime = (u32bits)strtoul(slifetime,NULL,10);
+
+						ns_bcopy(row[3],key->hkdf_hash_func,lengths[3]);
+						key->hkdf_hash_func[lengths[3]]=0;
+
+						ns_bcopy(row[4],key->as_rs_alg,lengths[4]);
+						key->as_rs_alg[lengths[4]]=0;
+
+						ns_bcopy(row[5],key->as_rs_key,lengths[5]);
+						key->as_rs_key[lengths[5]]=0;
+
+						ns_bcopy(row[6],key->auth_alg,lengths[6]);
+						key->auth_alg[lengths[6]]=0;
+
+						ns_bcopy(row[7],key->auth_key,lengths[7]);
+						key->auth_key[lengths[7]]=0;
+
+						ret = 0;
+					}
+				}
+			}
+
+			if(mres)
+				mysql_free_result(mres);
+		}
+	}
+	return ret;
+}
+
+static int mysql_list_oauth_keys(void) {
+
+	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,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key,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)!=9) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement);
+			} else {
+				MYSQL_ROW row = mysql_fetch_row(mres);
+				while(row) {
+					unsigned long *lengths = mysql_fetch_lengths(mres);
+					if(lengths) {
+
+						ns_bcopy(row[0],key->ikm_key,lengths[0]);
+						key->ikm_key[lengths[0]]=0;
+
+						char stimestamp[128];
+						ns_bcopy(row[1],stimestamp,lengths[1]);
+						stimestamp[lengths[1]]=0;
+						key->timestamp = (u64bits)strtoull(stimestamp,NULL,10);
+
+						char slifetime[128];
+						ns_bcopy(row[2],slifetime,lengths[2]);
+						slifetime[lengths[2]]=0;
+						key->lifetime = (u32bits)strtoul(slifetime,NULL,10);
+
+						ns_bcopy(row[3],key->hkdf_hash_func,lengths[3]);
+						key->hkdf_hash_func[lengths[3]]=0;
+						ns_bcopy(row[4],key->as_rs_alg,lengths[4]);
+						key->as_rs_alg[lengths[4]]=0;
+
+						ns_bcopy(row[5],key->as_rs_key,lengths[5]);
+						key->as_rs_key[lengths[5]]=0;
+
+						ns_bcopy(row[6],key->auth_alg,lengths[6]);
+						key->auth_alg[lengths[6]]=0;
+
+						ns_bcopy(row[7],key->auth_key,lengths[7]);
+						key->auth_key[lengths[7]]=0;
+
+						ns_bcopy(row[8],key->kid,lengths[8]);
+						key->kid[lengths[8]]=0;
+
+						printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, hkdf_hash_func=%s, as_rs_alg=%s, as_rs_key=%s, auth_alg=%s, auth_key=%s\n",
+								key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->hkdf_hash_func,
+								key->as_rs_alg, key->as_rs_key, key->auth_alg, key->auth_key);
+					}
+					row = mysql_fetch_row(mres);
+				}
+			}
+
+			if(mres)
+				mysql_free_result(mres);
+		}
+	}
+
+	return ret;
+}
   
 static int mysql_set_user_key(u08bits *usname, u08bits *realm, const char *key) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	MYSQL * myc = get_mydb_connection();
 	if(myc) {
@@ -390,9 +526,30 @@ static int mysql_set_user_key(u08bits *usname, u08bits *realm, const char *key)
 	}
   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,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key) values('%s','%s',%llu,%lu,'%s','%s','%s','%s','%s')",
+					  key->kid,key->ikm_key,(unsigned long long)key->timestamp,(unsigned long)key->lifetime,
+					  key->hkdf_hash_func,key->as_rs_alg,key->as_rs_key,key->auth_alg,key->auth_key);
+		int res = mysql_query(myc, statement);
+		if(res) {
+			snprintf(statement,sizeof(statement),"update oauth_key set ikm_key='%s',timestamp=%lu,lifetime=%lu, hkdf_hash_func = '%s', as_rs_alg='%s',as_rs_key='%s',auth_alg='%s',auth_key='%s' where kid='%s'",key->ikm_key,(unsigned long)key->timestamp,(unsigned long)key->lifetime,
+							  key->hkdf_hash_func,key->as_rs_alg,key->as_rs_key,key->auth_alg,key->auth_key,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));
+			}
+		}
+	}
+  return ret;
+}
   
 static int mysql_set_user_pwd(u08bits *usname, st_password_t pwd) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	MYSQL * myc = get_mydb_connection();
 	if(myc) {
@@ -412,7 +569,7 @@ static int mysql_set_user_pwd(u08bits *usname, st_password_t pwd) {
 }
   
 static int mysql_del_user(u08bits *usname, int is_st, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	MYSQL * myc = get_mydb_connection();
 	if(myc) {
@@ -430,9 +587,25 @@ static int mysql_del_user(u08bits *usname, int is_st, u08bits *realm) {
 	}
   return ret;
 }
+
+static int mysql_del_oauth_key(const u08bits *kid) {
+	int ret = -1;
+	char statement[TURN_LONG_STRING_SIZE];
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		snprintf(statement,sizeof(statement),"delete from oauth_key where kid = '%s'",(const char*)kid);
+		int res = mysql_query(myc, statement);
+		if(res) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting oauth key information: %s\n",mysql_error(myc));
+		} else {
+		  ret = 0;
+		}
+	}
+	return ret;
+}
   
 static int mysql_list_users(int is_st, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	MYSQL * myc = get_mydb_connection();
 	if(myc) {
@@ -478,7 +651,7 @@ static int mysql_list_users(int is_st, u08bits *realm) {
 }
   
 static int mysql_show_secret(u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	snprintf(statement,sizeof(statement)-1,"select value from turn_secret where realm='%s'",realm);
 
@@ -517,7 +690,7 @@ static int mysql_show_secret(u08bits *realm) {
 }
   
 static int mysql_del_secret(u08bits *secret, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	donot_print_connection_success=1;
 	char statement[TURN_LONG_STRING_SIZE];
 	MYSQL * myc = get_mydb_connection();
@@ -533,7 +706,7 @@ static int mysql_del_secret(u08bits *secret, u08bits *realm) {
 }
   
 static int mysql_set_secret(u08bits *secret, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	donot_print_connection_success = 1;
 	char statement[TURN_LONG_STRING_SIZE];
 	MYSQL * myc = get_mydb_connection();
@@ -553,7 +726,7 @@ static int mysql_set_secret(u08bits *secret, u08bits *realm) {
 }
   
 static int mysql_add_origin(u08bits *origin, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	MYSQL * myc = get_mydb_connection();
 	if (myc) {
@@ -572,7 +745,7 @@ static int mysql_add_origin(u08bits *origin, u08bits *realm) {
 }
   
 static int mysql_del_origin(u08bits *origin) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	MYSQL * myc = get_mydb_connection();
 	if (myc) {
@@ -591,7 +764,7 @@ static int mysql_del_origin(u08bits *origin) {
 }
   
 static int mysql_list_origins(u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	donot_print_connection_success = 1;
 	char statement[TURN_LONG_STRING_SIZE];
 	MYSQL * myc = get_mydb_connection();
@@ -632,7 +805,7 @@ static int mysql_list_origins(u08bits *realm) {
 }
   
 static int mysql_set_realm_option_one(u08bits *realm, unsigned long value, const char* opt) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	MYSQL * myc = get_mydb_connection();
 	if (myc) {
@@ -657,7 +830,7 @@ static int mysql_set_realm_option_one(u08bits *realm, unsigned long value, const
 }
   
 static int mysql_list_realm_options(u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	donot_print_connection_success = 1;
 	char statement[TURN_LONG_STRING_SIZE];
 	MYSQL * myc = get_mydb_connection();
@@ -719,7 +892,7 @@ static void mysql_auth_ping(void * rch) {
 }
   
 static int mysql_get_ip_list(const char *kind, ip_range_list_t * list) {
-  int ret = 1;
+  int ret = -1;
 	MYSQL * myc = get_mydb_connection();
 	if(myc) {
 		char statement[TURN_LONG_STRING_SIZE];
@@ -745,7 +918,7 @@ static int mysql_get_ip_list(const char *kind, ip_range_list_t * list) {
 						}
 					}
 				}
-        ret = 0;
+				ret = 0;
 			}
 
 			if(mres)
@@ -766,7 +939,7 @@ static void mysql_reread_realms(secrets_list_t * realms_list) {
 				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);
+					ur_string_map *o_to_realm_new = ur_string_map_create(turn_free_simple);
 
 					for(;;) {
 						MYSQL_ROW row = mysql_fetch_row(mres);
@@ -780,7 +953,7 @@ static void mysql_reread_realms(secrets_list_t * realms_list) {
 									char oval[513];
 									ns_bcopy(row[0],oval,sz);
 									oval[sz]=0;
-									char *rval=strdup(row[1]);
+									char *rval=turn_strdup(row[1]);
 									get_realm(rval);
 									ur_string_map_value_type value = (ur_string_map_value_type)rval;
 									ur_string_map_put(o_to_realm_new, (const ur_string_map_key_type) oval, value);
@@ -893,7 +1066,11 @@ static turn_dbdriver_t driver = {
   &mysql_list_realm_options,
   &mysql_auth_ping,
   &mysql_get_ip_list,
-  &mysql_reread_realms
+  &mysql_reread_realms,
+  &mysql_set_oauth_key,
+  &mysql_get_oauth_key,
+  &mysql_del_oauth_key,
+  &mysql_list_oauth_keys
 };
 
 turn_dbdriver_t * get_mysql_dbdriver(void) {

+ 156 - 20
src/apps/relay/dbdrivers/dbd_pgsql.c

@@ -86,7 +86,7 @@ static PGconn *get_pqdb_connection(void) {
 ///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
 static int pgsql_get_auth_secrets(secrets_list_t *sl, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	PGconn * pqc = get_pqdb_connection();
 	if(pqc) {
 		char statement[TURN_LONG_STRING_SIZE];
@@ -114,7 +114,7 @@ static int pgsql_get_auth_secrets(secrets_list_t *sl, u08bits *realm) {
 }
   
 static int pgsql_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
-  int ret = 1;
+  int ret = -1;
 	PGconn * pqc = get_pqdb_connection();
 	if(pqc) {
 		char statement[TURN_LONG_STRING_SIZE];
@@ -148,7 +148,7 @@ static int pgsql_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
 }
   
 static int pgsql_get_user_pwd(u08bits *usname, st_password_t pwd) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	snprintf(statement,sizeof(statement),"select password from turnusers_st where name='%s'",usname);
 
@@ -174,9 +174,89 @@ static int pgsql_get_user_pwd(u08bits *usname, st_password_t pwd) {
 	}
   return ret;
 }
+
+static int pgsql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
+
+	int ret = -1;
+
+	char statement[TURN_LONG_STRING_SIZE];
+	snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key from oauth_key where kid='%s'",(const char*)kid);
+
+	PGconn * pqc = get_pqdb_connection();
+	if(pqc) {
+		PGresult *res = PQexec(pqc, statement);
+
+		if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res)!=1)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc));
+		} else {
+			STRCPY((char*)key->ikm_key,PQgetvalue(res,0,0));
+			key->timestamp = (u64bits)strtoll(PQgetvalue(res,0,1),NULL,10);
+			key->lifetime = (u32bits)strtol(PQgetvalue(res,0,2),NULL,10);
+			STRCPY((char*)key->hkdf_hash_func,PQgetvalue(res,0,3));
+			STRCPY((char*)key->as_rs_alg,PQgetvalue(res,0,4));
+			STRCPY((char*)key->as_rs_key,PQgetvalue(res,0,5));
+			STRCPY((char*)key->auth_alg,PQgetvalue(res,0,6));
+			STRCPY((char*)key->auth_key,PQgetvalue(res,0,7));
+			STRCPY((char*)key->kid,kid);
+			ret = 0;
+		}
+
+		if(res) {
+			PQclear(res);
+		}
+	}
+
+	return ret;
+}
+
+static int pgsql_list_oauth_keys(void) {
+
+	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,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key,kid from oauth_key order by kid");
+
+	PGconn * pqc = get_pqdb_connection();
+	if(pqc) {
+		PGresult *res = PQexec(pqc, statement);
+
+		if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc));
+		} else {
+			int i = 0;
+			for(i=0;i<PQntuples(res);i++) {
+
+				STRCPY((char*)key->ikm_key,PQgetvalue(res,i,0));
+				key->timestamp = (u64bits)strtoll(PQgetvalue(res,i,1),NULL,10);
+				key->lifetime = (u32bits)strtol(PQgetvalue(res,i,2),NULL,10);
+				STRCPY((char*)key->hkdf_hash_func,PQgetvalue(res,i,3));
+				STRCPY((char*)key->as_rs_alg,PQgetvalue(res,i,4));
+				STRCPY((char*)key->as_rs_key,PQgetvalue(res,i,5));
+				STRCPY((char*)key->auth_alg,PQgetvalue(res,i,6));
+				STRCPY((char*)key->auth_key,PQgetvalue(res,i,7));
+				STRCPY((char*)key->kid,PQgetvalue(res,i,8));
+
+				printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, hkdf_hash_func=%s, as_rs_alg=%s, as_rs_key=%s, auth_alg=%s, auth_key=%s\n",
+						key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->hkdf_hash_func,
+						key->as_rs_alg, key->as_rs_key, key->auth_alg, key->auth_key);
+
+				ret = 0;
+			}
+		}
+
+		if(res) {
+			PQclear(res);
+		}
+	}
+
+	return ret;
+}
   
 static int pgsql_set_user_key(u08bits *usname, u08bits *realm, const char *key) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	PGconn *pqc = get_pqdb_connection();
 	if(pqc) {
@@ -201,9 +281,40 @@ static int pgsql_set_user_key(u08bits *usname, u08bits *realm, const char *key)
 	}
   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,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key) values('%s','%s',%llu,%lu,'%s','%s','%s','%s','%s')",
+			  key->kid,key->ikm_key,(unsigned long long)key->timestamp,(unsigned long)key->lifetime,
+			  key->hkdf_hash_func,key->as_rs_alg,key->as_rs_key,key->auth_alg,key->auth_key);
+
+	  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, hkdf_hash_func = '%s', as_rs_alg='%s',as_rs_key='%s',auth_alg='%s',auth_key='%s' where kid='%s'",key->ikm_key,(unsigned long)key->timestamp,(unsigned long)key->lifetime,
+				  key->hkdf_hash_func,key->as_rs_alg,key->as_rs_key,key->auth_alg,key->auth_key,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;
+		  }
+	  }
+	  if(res) {
+		  PQclear(res);
+	  }
+  }
+  return ret;
+}
+
 static int pgsql_set_user_pwd(u08bits *usname, st_password_t pwd) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	PGconn *pqc = get_pqdb_connection();
 	if(pqc) {
@@ -229,7 +340,7 @@ static int pgsql_set_user_pwd(u08bits *usname, st_password_t pwd) {
 }
   
 static int pgsql_del_user(u08bits *usname, int is_st, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	PGconn *pqc = get_pqdb_connection();
 	if(pqc) {
@@ -246,9 +357,30 @@ static int pgsql_del_user(u08bits *usname, int is_st, u08bits *realm) {
 	}
   return ret;
 }
+
+static int pgsql_del_oauth_key(const u08bits *kid) {
+
+  int ret = -1;
+  char statement[TURN_LONG_STRING_SIZE];
+  PGconn *pqc = get_pqdb_connection();
+  if(pqc) {
+	  snprintf(statement,sizeof(statement),"delete from oauth_key where kid = '%s'",(const char*)kid);
+
+	  PGresult *res = PQexec(pqc, statement);
+	  if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
+		  TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting oauth_key information: %s\n",PQerrorMessage(pqc));
+	  } else {
+		  ret = 0;
+	  }
+	  if(res) {
+		  PQclear(res);
+	  }
+  }
+  return ret;
+}
   
 static int pgsql_list_users(int is_st, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	PGconn *pqc = get_pqdb_connection();
 	if(pqc) {
@@ -285,7 +417,7 @@ static int pgsql_list_users(int is_st, u08bits *realm) {
 }
   
 static int pgsql_show_secret(u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	snprintf(statement,sizeof(statement)-1,"select value from turn_secret where realm='%s'",realm);
 
@@ -314,7 +446,7 @@ static int pgsql_show_secret(u08bits *realm) {
 }
   
 static int pgsql_del_secret(u08bits *secret, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	donot_print_connection_success=1;
 	char statement[TURN_LONG_STRING_SIZE];
 	PGconn *pqc = get_pqdb_connection();
@@ -334,7 +466,7 @@ static int pgsql_del_secret(u08bits *secret, u08bits *realm) {
 }
   
 static int pgsql_set_secret(u08bits *secret, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	donot_print_connection_success = 1;
   char statement[TURN_LONG_STRING_SIZE];
 	PGconn *pqc = get_pqdb_connection();
@@ -358,7 +490,7 @@ static int pgsql_set_secret(u08bits *secret, u08bits *realm) {
 }
   
 static int pgsql_add_origin(u08bits *origin, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	PGconn *pqc = get_pqdb_connection();
 	if(pqc) {
@@ -377,7 +509,7 @@ static int pgsql_add_origin(u08bits *origin, u08bits *realm) {
 }
   
 static int pgsql_del_origin(u08bits *origin) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	PGconn *pqc = get_pqdb_connection();
 	if(pqc) {
@@ -396,7 +528,7 @@ static int pgsql_del_origin(u08bits *origin) {
 }
   
 static int pgsql_list_origins(u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	donot_print_connection_success = 1;
 	char statement[TURN_LONG_STRING_SIZE];
 	PGconn *pqc = get_pqdb_connection();
@@ -430,7 +562,7 @@ static int pgsql_list_origins(u08bits *realm) {
 }
   
 static int pgsql_set_realm_option_one(u08bits *realm, unsigned long value, const char* opt) {
-  int ret = 1;
+  int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	PGconn *pqc = get_pqdb_connection();
 	if(pqc) {
@@ -458,7 +590,7 @@ static int pgsql_set_realm_option_one(u08bits *realm, unsigned long value, const
 }
   
 static int pgsql_list_realm_options(u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	donot_print_connection_success = 1;
 	char statement[TURN_LONG_STRING_SIZE];
 	PGconn *pqc = get_pqdb_connection();
@@ -514,7 +646,7 @@ static void pgsql_auth_ping(void * rch) {
 }
   
 static int pgsql_get_ip_list(const char *kind, ip_range_list_t * list) {
-  int ret = 1;
+  int ret = -1;
 	PGconn * pqc = get_pqdb_connection();
 	if(pqc) {
 		char statement[TURN_LONG_STRING_SIZE];
@@ -550,7 +682,7 @@ static void pgsql_reread_realms(secrets_list_t * realms_list) {
 
 			if(res && (PQresultStatus(res) == PGRES_TUPLES_OK)) {
 
-				ur_string_map *o_to_realm_new = ur_string_map_create(free);
+				ur_string_map *o_to_realm_new = ur_string_map_create(turn_free_simple);
 
 				int i = 0;
 				for(i=0;i<PQntuples(res);i++) {
@@ -559,7 +691,7 @@ static void pgsql_reread_realms(secrets_list_t * realms_list) {
 						char *rval = PQgetvalue(res,i,1);
 						if(rval) {
 							get_realm(rval);
-							ur_string_map_value_type value = strdup(rval);
+							ur_string_map_value_type value = turn_strdup(rval);
 							ur_string_map_put(o_to_realm_new, (const ur_string_map_key_type) oval, value);
 						}
 					}
@@ -655,7 +787,11 @@ static turn_dbdriver_t driver = {
   &pgsql_list_realm_options,
   &pgsql_auth_ping,
   &pgsql_get_ip_list,
-  &pgsql_reread_realms
+  &pgsql_reread_realms,
+  &pgsql_set_oauth_key,
+  &pgsql_get_oauth_key,
+  &pgsql_del_oauth_key,
+  &pgsql_list_oauth_keys
 };
 
 turn_dbdriver_t * get_pgsql_dbdriver(void) {

+ 170 - 43
src/apps/relay/dbdrivers/dbd_redis.c

@@ -59,7 +59,7 @@ typedef struct _Ryconninfo Ryconninfo;
 static void RyconninfoFree(Ryconninfo *co) {
 	if(co) {
 		if(co->host) turn_free(co->host, strlen(co->host)+1);
-		if(co->dbname) turn_free(co->dbname, strlen(co->username)+1);
+		if(co->dbname) turn_free(co->dbname, strlen(co->dbname)+1);
 		if(co->password) turn_free(co->password, strlen(co->password)+1);
 		ns_bzero(co,sizeof(Ryconninfo));
 	}
@@ -69,7 +69,7 @@ static Ryconninfo *RyconninfoParse(const char *userdb, char **errmsg) {
 	Ryconninfo *co = (Ryconninfo*) turn_malloc(sizeof(Ryconninfo));
 	ns_bzero(co,sizeof(Ryconninfo));
 	if (userdb) {
-		char *s0 = strdup(userdb);
+		char *s0 = turn_strdup(userdb);
 		char *s = s0;
 
 		while (s && *s) {
@@ -87,28 +87,28 @@ static Ryconninfo *RyconninfoParse(const char *userdb, char **errmsg) {
 				RyconninfoFree(co);
 				co = NULL;
 				if (errmsg) {
-					*errmsg = strdup(s);
+					*errmsg = turn_strdup(s);
 				}
 				break;
 			}
 
 			*seq = 0;
 			if (!strcmp(s, "host"))
-				co->host = strdup(seq + 1);
+				co->host = turn_strdup(seq + 1);
 			else if (!strcmp(s, "ip"))
-				co->host = strdup(seq + 1);
+				co->host = turn_strdup(seq + 1);
 			else if (!strcmp(s, "addr"))
-				co->host = strdup(seq + 1);
+				co->host = turn_strdup(seq + 1);
 			else if (!strcmp(s, "ipaddr"))
-				co->host = strdup(seq + 1);
+				co->host = turn_strdup(seq + 1);
 			else if (!strcmp(s, "hostaddr"))
-				co->host = strdup(seq + 1);
+				co->host = turn_strdup(seq + 1);
 			else if (!strcmp(s, "dbname"))
-				co->dbname = strdup(seq + 1);
+				co->dbname = turn_strdup(seq + 1);
 			else if (!strcmp(s, "db"))
-				co->dbname = strdup(seq + 1);
+				co->dbname = turn_strdup(seq + 1);
 			else if (!strcmp(s, "database"))
-				co->dbname = strdup(seq + 1);
+				co->dbname = turn_strdup(seq + 1);
 			else if (!strcmp(s, "user"))
 				;
 			else if (!strcmp(s, "uname"))
@@ -118,13 +118,13 @@ static Ryconninfo *RyconninfoParse(const char *userdb, char **errmsg) {
 			else if (!strcmp(s, "username"))
 				;
 			else if (!strcmp(s, "password"))
-				co->password = strdup(seq + 1);
+				co->password = turn_strdup(seq + 1);
 			else if (!strcmp(s, "pwd"))
-				co->password = strdup(seq + 1);
+				co->password = turn_strdup(seq + 1);
 			else if (!strcmp(s, "passwd"))
-				co->password = strdup(seq + 1);
+				co->password = turn_strdup(seq + 1);
 			else if (!strcmp(s, "secret"))
-				co->password = strdup(seq + 1);
+				co->password = turn_strdup(seq + 1);
 			else if (!strcmp(s, "port"))
 				co->port = (unsigned int) atoi(seq + 1);
 			else if (!strcmp(s, "p"))
@@ -137,7 +137,7 @@ static Ryconninfo *RyconninfoParse(const char *userdb, char **errmsg) {
 				RyconninfoFree(co);
 				co = NULL;
 				if (errmsg) {
-					*errmsg = strdup(s);
+					*errmsg = turn_strdup(s);
 				}
 				break;
 			}
@@ -150,11 +150,11 @@ static Ryconninfo *RyconninfoParse(const char *userdb, char **errmsg) {
 
 	if(co) {
 		if(!(co->dbname))
-			co->dbname=strdup("0");
+			co->dbname=turn_strdup("0");
 		if(!(co->host))
-			co->host=strdup("127.0.0.1");
+			co->host=turn_strdup("127.0.0.1");
 		if(!(co->password))
-			co->password=strdup("");
+			co->password=turn_strdup("");
 	}
 
 	return co;
@@ -268,7 +268,7 @@ static redisContext *get_redis_connection(void) {
 
 	if(redisconnection) {
 		if(redisconnection->err) {
-			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot connect to redis, err=%d, flags=0x%x\n", __FUNCTION__,(int)redisconnection->err,(unsigned long)redisconnection->flags);
+			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);
 			pud->connection = NULL;
 			redisconnection = NULL;
@@ -397,7 +397,7 @@ static int set_redis_realm_opt(char *realm, const char* key, unsigned long *valu
 ///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
 static int redis_get_auth_secrets(secrets_list_t *sl, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
 		redisReply *reply = (redisReply*)redisCommand(rc, "keys turn/realm/%s/secret/*", (char*)realm);
@@ -448,7 +448,7 @@ static int redis_get_auth_secrets(secrets_list_t *sl, u08bits *realm) {
 }
   
 static int redis_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
-  int ret = 1;
+  int ret = -1;
 	redisContext * rc = get_redis_connection();
 	if(rc) {
 		char s[TURN_LONG_STRING_SIZE];
@@ -472,7 +472,7 @@ static int redis_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
 			}
 			turnFreeRedisReply(rget);
 		}
-		if(ret != 0) {
+		if(ret == 0) {
 			snprintf(s,sizeof(s),"get turn/realm/%s/user/%s/password", (char*)realm, usname);
 			rget = (redisReply *)redisCommand(rc, s);
 			if(rget) {
@@ -492,9 +492,57 @@ static int redis_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
 	}
   return ret;
 }
+
+static int redis_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
+  int ret = -1;
+  redisContext * rc = get_redis_connection();
+  if(rc) {
+	char s[TURN_LONG_STRING_SIZE];
+	ns_bzero(key,sizeof(oauth_key_data_raw));
+	STRCPY(key->kid,kid);
+	snprintf(s,sizeof(s),"hgetall turn/oauth/kid/%s", (const char*)kid);
+	redisReply *reply = (redisReply *)redisCommand(rc, s);
+	if(reply) {
+		if (reply->type == REDIS_REPLY_ERROR)
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str);
+		else if (reply->type != REDIS_REPLY_ARRAY) {
+			if (reply->type != REDIS_REPLY_NIL)
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type);
+		} else if(reply->elements > 1) {
+			size_t i;
+			for (i = 0; i < (reply->elements)/2; ++i) {
+				char *kw = reply->element[2*i]->str;
+				char *val = reply->element[2*i+1]->str;
+				if(kw) {
+					if(!strcmp(kw,"as_rs_alg")) {
+						STRCPY(key->as_rs_alg,val);
+					} else if(!strcmp(kw,"as_rs_key")) {
+						STRCPY(key->as_rs_key,val);
+					} else if(!strcmp(kw,"auth_key")) {
+						STRCPY(key->auth_key,val);
+					} else if(!strcmp(kw,"auth_alg")) {
+						STRCPY(key->auth_alg,val);
+					} else if(!strcmp(kw,"ikm_key")) {
+						STRCPY(key->ikm_key,val);
+					} else if(!strcmp(kw,"hkdf_hash_func")) {
+						STRCPY(key->hkdf_hash_func,val);
+					} else if(!strcmp(kw,"timestamp")) {
+						key->timestamp = (u64bits)strtoull(val,NULL,10);
+					} else if(!strcmp(kw,"lifetime")) {
+						key->lifetime = (u32bits)strtoul(val,NULL,10);
+					}
+				}
+			}
+			ret = 0;
+		}
+		turnFreeRedisReply(reply);
+	}
+  }
+  return ret;
+}
   
 static int redis_get_user_pwd(u08bits *usname, st_password_t pwd) {
-  int ret = 1;
+  int ret = -1;
 	redisContext * rc = get_redis_connection();
 	if(rc) {
 		char s[TURN_LONG_STRING_SIZE];
@@ -518,7 +566,7 @@ static int redis_get_user_pwd(u08bits *usname, st_password_t pwd) {
 }
   
 static int redis_set_user_key(u08bits *usname, u08bits *realm, const char *key) {
-  int ret = 1;
+  int ret = -1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
 		char statement[TURN_LONG_STRING_SIZE];
@@ -531,22 +579,36 @@ static int redis_set_user_key(u08bits *usname, u08bits *realm, const char *key)
 	}
   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' hkdf_hash_func '%s' as_rs_alg '%s' as_rs_key '%s' auth_alg '%s' auth_key '%s' timestamp %llu lifetime %lu",
+			key->kid,key->ikm_key,key->hkdf_hash_func,key->as_rs_alg,key->as_rs_key,key->auth_alg,key->auth_key,(unsigned long long)key->timestamp,(unsigned long)key->lifetime);
+	turnFreeRedisReply(redisCommand(rc, statement));
+	turnFreeRedisReply(redisCommand(rc, "save"));
+    ret = 0;
+  }
+  return ret;
+}
   
 static int redis_set_user_pwd(u08bits *usname, st_password_t pwd) {
-  int ret = 1;
+  int ret = -1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
 		char statement[TURN_LONG_STRING_SIZE];
-	  snprintf(statement,sizeof(statement),"set turn/user/%s/password %s",usname,pwd);
-	  turnFreeRedisReply(redisCommand(rc, statement));
+		snprintf(statement,sizeof(statement),"set turn/user/%s/password %s",usname,pwd);
+		turnFreeRedisReply(redisCommand(rc, statement));
 		turnFreeRedisReply(redisCommand(rc, "save"));
-    ret = 0;
+		ret = 0;
 	}
   return ret;
 }
   
 static int redis_del_user(u08bits *usname, int is_st, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
 		char statement[TURN_LONG_STRING_SIZE];
@@ -565,9 +627,22 @@ static int redis_del_user(u08bits *usname, int is_st, u08bits *realm) {
 	}
   return ret;
 }
+
+static int redis_del_oauth_key(const u08bits *kid) {
+  int ret = -1;
+  redisContext *rc = get_redis_connection();
+  if(rc) {
+	char statement[TURN_LONG_STRING_SIZE];
+	snprintf(statement,sizeof(statement),"del turn/oauth/kid/%s",(const char*)kid);
+	turnFreeRedisReply(redisCommand(rc, statement));
+	turnFreeRedisReply(redisCommand(rc, "save"));
+    ret = 0;
+  }
+  return ret;
+}
   
 static int redis_list_users(int is_st, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
 		secrets_list_t keys;
@@ -656,9 +731,57 @@ static int redis_list_users(int is_st, u08bits *realm) {
 	}
   return ret;
 }
+
+static int redis_list_oauth_keys(void) {
+  int ret = -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/oauth/kid/*");
+	  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);
+	}
+  }
+
+  for(isz=0;isz<keys.sz;++isz) {
+	char *s = keys.secrets[isz];
+	s += strlen("turn/oauth/kid/");
+	oauth_key_data_raw key_;
+	oauth_key_data_raw *key=&key_;
+	if(redis_get_oauth_key((const u08bits*)s,key) == 0) {
+		printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, hkdf_hash_func=%s, as_rs_alg=%s, as_rs_key=%s, auth_alg=%s, auth_key=%s\n",
+		    key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->hkdf_hash_func,
+		    key->as_rs_alg, key->as_rs_key, key->auth_alg, key->auth_key);
+	}
+  }
+
+  clean_secrets_list(&keys);
+  ret = 0;
+
+  return ret;
+}
   
 static int redis_show_secret(u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	donot_print_connection_success = 1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
@@ -713,7 +836,7 @@ static int redis_show_secret(u08bits *realm) {
 }
   
 static int redis_del_secret(u08bits *secret, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	donot_print_connection_success = 1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
@@ -773,7 +896,7 @@ static int redis_del_secret(u08bits *secret, u08bits *realm) {
 }
   
 static int redis_set_secret(u08bits *secret, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	donot_print_connection_success = 1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
@@ -791,7 +914,7 @@ static int redis_set_secret(u08bits *secret, u08bits *realm) {
 }
   
 static int redis_add_origin(u08bits *origin, u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
 		char s[TURN_LONG_STRING_SIZE];
@@ -806,7 +929,7 @@ static int redis_add_origin(u08bits *origin, u08bits *realm) {
 }
   
 static int redis_del_origin(u08bits *origin) {
-  int ret = 1;
+  int ret = -1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
 		char s[TURN_LONG_STRING_SIZE];
@@ -821,7 +944,7 @@ static int redis_del_origin(u08bits *origin) {
 }
   
 static int redis_list_origins(u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	donot_print_connection_success = 1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
@@ -879,7 +1002,7 @@ static int redis_list_origins(u08bits *realm) {
 }
   
 static int redis_set_realm_option_one(u08bits *realm, unsigned long value, const char* opt) {
-  int ret = 1;
+  int ret = -1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
 		char s[TURN_LONG_STRING_SIZE];
@@ -897,7 +1020,7 @@ static int redis_set_realm_option_one(u08bits *realm, unsigned long value, const
 }
   
 static int redis_list_realm_options(u08bits *realm) {
-  int ret = 1;
+  int ret = -1;
 	donot_print_connection_success = 1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
@@ -972,7 +1095,7 @@ static void redis_auth_ping(void * rch) {
 }
   
 static int redis_get_ip_list(const char *kind, ip_range_list_t * list) {
-  int ret = 1;
+  int ret = -1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
 		char statement[TURN_LONG_STRING_SIZE];
@@ -1029,7 +1152,7 @@ static void redis_reread_realms(secrets_list_t * realms_list) {
 		redisReply *reply = (redisReply*) redisCommand(rc, "keys turn/origin/*");
 		if (reply) {
 
-			ur_string_map *o_to_realm_new = ur_string_map_create(free);
+			ur_string_map *o_to_realm_new = ur_string_map_create(turn_free_simple);
 
 			secrets_list_t keys;
 
@@ -1065,7 +1188,7 @@ static void redis_reread_realms(secrets_list_t * realms_list) {
 							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_value_type value = turn_strdup(rget->str);
 						ur_string_map_put(o_to_realm_new, (const ur_string_map_key_type) origin, value);
 					}
 					turnFreeRedisReply(rget);
@@ -1134,7 +1257,11 @@ static turn_dbdriver_t driver = {
   &redis_list_realm_options,
   &redis_auth_ping,
   &redis_get_ip_list,
-  &redis_reread_realms
+  &redis_reread_realms,
+  &redis_set_oauth_key,
+  &redis_get_oauth_key,
+  &redis_del_oauth_key,
+  &redis_list_oauth_keys
 };
 
 turn_dbdriver_t * get_redis_dbdriver(void) {

+ 3 - 2
src/apps/relay/dbdrivers/dbdriver.c

@@ -30,6 +30,9 @@
  */
 
 #include "../mainrelay.h"
+
+#include "apputils.h"
+
 #include "dbdriver.h"
 #include "dbd_pgsql.h"
 #include "dbd_mysql.h"
@@ -86,5 +89,3 @@ turn_dbdriver_t * get_dbdriver() {
   return _driver;
 }
 
-
-

+ 6 - 0
src/apps/relay/dbdrivers/dbdriver.h

@@ -34,6 +34,8 @@
 
 #include "../userdb.h"
 
+#include "ns_turn_msg_defs_new.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -59,6 +61,10 @@ typedef struct _turn_dbdriver_t {
   void (*auth_ping)(void * rch);
   int (*get_ip_list)(const char *kind, ip_range_list_t * list);
   void (*reread_realms)(secrets_list_t * realms_list);
+  int (*set_oauth_key)(oauth_key_data_raw *key);
+  int (*get_oauth_key)(const u08bits *kid, oauth_key_data_raw *key);
+  int (*del_oauth_key)(const u08bits *kid);
+  int (*list_oauth_keys)(void);
 } turn_dbdriver_t;
 
 /////////// USER DB CHECK //////////////////

+ 4 - 12
src/apps/relay/dtls_listener.c

@@ -229,9 +229,6 @@ static ioa_socket_handle dtls_accept_client_connection(
 
 	ioa_socket_handle ioas = create_ioa_socket_from_ssl(server->e, sock, ssl, DTLS_SOCKET, CLIENT_SOCKET, remote_addr, local_addr);
 	if(ioas) {
-
-		ioas->listener_server = server;
-
 		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;
@@ -437,7 +434,6 @@ static int handle_udp_packet(dtls_listener_relay_server_type *server,
 					__FUNCTION__, (char*) saddr,(char*) rsaddr);
 			}
 			s->e = ioa_eng;
-			s->listener_server = server;
 			add_socket_to_map(s, amap);
 			open_client_connection_session(ts, &(sm->m.sm));
 		}
@@ -565,7 +561,6 @@ static int create_new_connected_udp_socket(
 				"Accepted DTLS connection from");
 
 		ret->ssl = connecting_ssl;
-		ret->listener_server = server;
 
 		ioa_network_buffer_delete(server->e, server->sm.m.sm.nd.nbh);
 		server->sm.m.sm.nd.nbh = NULL;
@@ -583,9 +578,8 @@ static void udp_server_input_handler(evutil_socket_t fd, short what, void* arg)
 {
 	int cycle = 0;
 
-	ioa_socket_handle s = (ioa_socket_handle)arg;
-
-	dtls_listener_relay_server_type* server = (dtls_listener_relay_server_type*)s->listener_server;
+	dtls_listener_relay_server_type* server = (dtls_listener_relay_server_type*)arg;
+	ioa_socket_handle s = server->udp_listen_s;
 
 	FUNCSTART;
 
@@ -726,8 +720,6 @@ static int create_server_socket(dtls_listener_relay_server_type* server, int rep
 
 	  server->udp_listen_s = create_ioa_socket_from_fd(server->e, udp_listen_fd, NULL, UDP_SOCKET, LISTENER_SOCKET, NULL, &(server->addr));
 
-	  server->udp_listen_s->listener_server = server;
-
 	  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) {
@@ -756,7 +748,7 @@ static int create_server_socket(dtls_listener_relay_server_type* server, int rep
 
 	  server->udp_listen_ev = event_new(server->e->event_base,udp_listen_fd,
 				    EV_READ|EV_PERSIST,udp_server_input_handler,
-				    server->udp_listen_s);
+				    server);
 
 	  event_add(server->udp_listen_ev,NULL);
   }
@@ -827,7 +819,7 @@ static int reopen_server_socket(dtls_listener_relay_server_type* server, evutil_
 
 		server->udp_listen_ev = event_new(server->e->event_base, udp_listen_fd,
 				EV_READ | EV_PERSIST, udp_server_input_handler,
-				server->udp_listen_s);
+				server);
 
 		event_add(server->udp_listen_ev, NULL );
 	}

+ 51 - 11
src/apps/relay/mainrelay.c

@@ -85,6 +85,7 @@ LOW_DEFAULT_PORTS_BOUNDARY,HIGH_DEFAULT_PORTS_BOUNDARY,0,0,0,"",
 0,NULL,0,NULL,DEFAULT_GENERAL_RELAY_SERVERS_NUMBER,0,
 ////////////// Auth server /////////////////////////////////////
 {NULL,NULL,NULL,0,NULL},
+"","",0,
 /////////////// AUX SERVERS ////////////////
 {NULL,0,{0,NULL}},0,
 /////////////// ALTERNATE SERVERS ////////////////
@@ -463,6 +464,10 @@ static char Usage[] = "Usage: turnserver [options]\n"
 "						That database value can be changed on-the-fly\n"
 "						by a separate program, so this is why it is 'dynamic'.\n"
 "						Multiple shared secrets can be used (both in the database and in the \"static\" fashion).\n"
+" --server-name					Server name used for\n"
+"						the oAuth authentication purposes.\n"
+"						The default value is the realm name.\n"
+" --oauth					Support oAuth authentication.\n"
 " -n						Do not use configuration file, take all parameters from the command line only.\n"
 " --cert			<filename>		Certificate file, PEM format. Same file search rules\n"
 "						applied as for the configuration file.\n"
@@ -677,7 +682,9 @@ enum EXTRA_OPTS {
 	CHECK_ORIGIN_CONSISTENCY_OPT,
 	ADMIN_MAX_BPS_OPT,
 	ADMIN_TOTAL_QUOTA_OPT,
-	ADMIN_USER_QUOTA_OPT
+	ADMIN_USER_QUOTA_OPT,
+	SERVER_NAME_OPT,
+	OAUTH_OPT
 };
 
 struct myoption {
@@ -731,6 +738,8 @@ static const struct myoption long_options[] = {
 				{ "static-auth-secret", required_argument, NULL, STATIC_AUTH_SECRET_VAL_OPT },
 /* deprecated: */		{ "secret-ts-exp-time", optional_argument, NULL, AUTH_SECRET_TS_EXP },
 				{ "realm", required_argument, NULL, 'r' },
+				{ "server-name", required_argument, NULL, SERVER_NAME_OPT },
+				{ "oauth", optional_argument, NULL, OAUTH_OPT },
 				{ "user-quota", required_argument, NULL, 'q' },
 				{ "total-quota", required_argument, NULL, 'Q' },
 				{ "max-bps", required_argument, NULL, 's' },
@@ -859,6 +868,12 @@ static void set_option(int c, char *value)
   }
 
   switch (c) {
+  case SERVER_NAME_OPT:
+	  STRCPY(turn_params.oauth_server_name,value);
+	  break;
+  case OAUTH_OPT:
+	  turn_params.oauth = get_bool_value(value);
+	  break;
   case NO_SSLV2_OPT:
 	  turn_params.no_sslv2 = get_bool_value(value);
 	  break;
@@ -1019,7 +1034,7 @@ static void set_option(int c, char *value)
 		if(value) {
 			char *div = strchr(value,'/');
 			if(div) {
-				char *nval=strdup(value);
+				char *nval=turn_strdup(value);
 				div = strchr(nval,'/');
 				div[0]=0;
 				++div;
@@ -1556,7 +1571,6 @@ static int adminmain(int argc, char **argv)
 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 == 1)
 		mfn = mfn/3;
@@ -1581,6 +1595,12 @@ static void print_features(unsigned long mfn)
 	TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS supported\n");
 #endif
 
+#if defined(TURN_NO_GCM)
+	TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "AEAD is not supported\n");
+#else
+	TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "AEAD supported\n");
+#endif
+
 #if !defined(TURN_NO_HIREDIS)
 	TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Redis supported\n");
 #else
@@ -1678,6 +1698,15 @@ static void drop_privileges(void)
 	}
 }
 
+static void init_domain(void)
+{
+#if !defined(TURN_NO_GETDOMAINNAME)
+	getdomainname(turn_params.domain,sizeof(turn_params.domain)-1);
+	if(!strcmp(turn_params.domain,"(none)")) 
+	  turn_params.domain[0]=0;
+#endif
+}
+
 int main(int argc, char **argv)
 {
 	int c = 0;
@@ -1692,7 +1721,8 @@ int main(int argc, char **argv)
 	redis_async_init();
 #endif
 
-	create_new_realm(NULL);
+	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);
@@ -1755,8 +1785,8 @@ int main(int argc, char **argv)
 #endif
 
 	ns_bzero(&turn_params.default_users_db,sizeof(default_users_db_t));
-	turn_params.default_users_db.ram_db.static_accounts = ur_string_map_create(free);
-	turn_params.default_users_db.ram_db.dynamic_accounts = ur_string_map_create(free);
+	turn_params.default_users_db.ram_db.static_accounts = ur_string_map_create(turn_free_simple);
+	turn_params.default_users_db.ram_db.dynamic_accounts = ur_string_map_create(turn_free_simple);
 
 	if(strstr(argv[0],"turnadmin"))
 		return adminmain(argc,argv);
@@ -1779,6 +1809,16 @@ int main(int argc, char **argv)
 
 	read_config_file(argc,argv,1);
 
+	if(!get_realm(NULL)->options.name[0]) {
+		STRCPY(get_realm(NULL)->options.name,turn_params.domain);
+	}
+
+	TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Domain name: %s\n",turn_params.domain);
+	TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Default realm: %s\n",get_realm(NULL)->options.name);
+	if(turn_params.oauth && turn_params.oauth_server_name[0]) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "oAuth server name: %s\n",turn_params.oauth_server_name);
+	}
+
 	optind = 0;
 
 	while (((c = getopt_long(argc, argv, OPTIONS, uo.u.o, NULL)) != -1)) {
@@ -1878,7 +1918,7 @@ int main(int argc, char **argv)
 		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, "Cannot configure any meaningful IP listener address\n", __FUNCTION__);
+			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);
 		}
@@ -1913,7 +1953,7 @@ int main(int argc, char **argv)
 		}
 
 		if (!turn_params.relays_number) {
-			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "You must specify the relay address(es)\n",
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: You must specify the relay address(es)\n",
 							__FUNCTION__);
 			fprintf(stderr,"\n%s\n", Usage);
 			exit(-1);
@@ -2305,7 +2345,7 @@ static void set_ctx(SSL_CTX* ctx, const char *protocol)
 		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");
+				      "%s: ERROR: allocate EC suite\n",__FUNCTION__);
 		} else {
 			SSL_CTX_set_tmp_ecdh(ctx, ecdh);
 			EC_KEY_free(ecdh);
@@ -2339,10 +2379,10 @@ static void set_ctx(SSL_CTX* ctx, const char *protocol)
 		}
 
 		if(!dh) {
-			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: cannot allocate DH suite\n");
+		  TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: cannot allocate DH suite\n",__FUNCTION__);
 		} else {
 			if (1 != SSL_CTX_set_tmp_dh (ctx, dh)) {
-				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: cannot set DH\n");
+			  TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: cannot set DH\n",__FUNCTION__);
 			}
 			DH_free (dh);
 		}

+ 4 - 2
src/apps/relay/mainrelay.h

@@ -51,6 +51,7 @@
 #include <sys/time.h>
 #include <sys/stat.h>
 #include <sys/resource.h>
+#include <sys/utsname.h>
 
 #include <pwd.h>
 #include <grp.h>
@@ -65,8 +66,6 @@
 #include <openssl/crypto.h>
 #include <openssl/opensslv.h>
 
-#include <sys/utsname.h>
-
 #include "ns_turn_utils.h"
 #include "ns_turn_khash.h"
 
@@ -272,6 +271,9 @@ typedef struct _turn_params_ {
 ////////////// Auth server ////////////////
 
   struct auth_server authserver;
+  char oauth_server_name[1025];
+  char domain[1025];
+  int oauth;
 
 /////////////// AUX SERVERS ////////////////
 

+ 54 - 29
src/apps/relay/netengine.c

@@ -107,7 +107,6 @@ static band_limit_t allocate_bps(band_limit_t bps, int positive)
 
 			if(turn_params.bps_capacity_allocated >= bps) {
 				turn_params.bps_capacity_allocated -= bps;
-				ret = turn_params.bps_capacity_allocated;
 			} else {
 				turn_params.bps_capacity_allocated = 0;
 			}
@@ -115,6 +114,7 @@ static band_limit_t allocate_bps(band_limit_t bps, int positive)
 
 		pthread_mutex_unlock(&mutex_bps);
 	}
+
 	return ret;
 }
 
@@ -168,7 +168,7 @@ static void add_aux_server_list(const char *saddr, turn_server_addrs_list_t *lis
 		if(make_ioa_addr_from_full_string((const u08bits*)saddr, 0, &addr)!=0) {
 			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong full address format: %s\n",saddr);
 		} else {
-			list->addrs = (ioa_addr*)realloc(list->addrs,sizeof(ioa_addr)*(list->size+1));
+		  list->addrs = (ioa_addr*)turn_realloc(list->addrs,0,sizeof(ioa_addr)*(list->size+1));
 			addr_cpy(&(list->addrs[(list->size)++]),&addr);
 			{
 				u08bits s[1025];
@@ -196,7 +196,7 @@ static void add_alt_server(const char *saddr, int default_port, turn_server_addr
 		if(make_ioa_addr_from_full_string((const u08bits*)saddr, default_port, &addr)!=0) {
 			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong IP address format: %s\n",saddr);
 		} else {
-			list->addrs = (ioa_addr*)realloc(list->addrs,sizeof(ioa_addr)*(list->size+1));
+		  list->addrs = (ioa_addr*)turn_realloc(list->addrs,0,sizeof(ioa_addr)*(list->size+1));
 			addr_cpy(&(list->addrs[(list->size)++]),&addr);
 			{
 				u08bits s[1025];
@@ -233,7 +233,7 @@ static void del_alt_server(const char *saddr, int default_port, turn_server_addr
 			if(found) {
 
 				size_t j;
-				ioa_addr *new_addrs = (ioa_addr*)malloc(sizeof(ioa_addr)*(list->size-1));
+				ioa_addr *new_addrs = (ioa_addr*)turn_malloc(sizeof(ioa_addr)*(list->size-1));
 				for(j=0;j<i;++j) {
 					addr_cpy(&(new_addrs[j]),&(list->addrs[j]));
 				}
@@ -241,7 +241,7 @@ static void del_alt_server(const char *saddr, int default_port, turn_server_addr
 					addr_cpy(&(new_addrs[j]),&(list->addrs[j+1]));
 				}
 
-				free(list->addrs);
+				turn_free(list->addrs,0);
 				list->addrs = new_addrs;
 				list->size -= 1;
 
@@ -286,6 +286,10 @@ void add_listener_addr(const char* addr) {
 	if(make_ioa_addr((const u08bits*)addr,0,&baddr)<0) {
 		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot add a listener address: %s\n",addr);
 	} else {
+
+	  char sbaddr[129];
+	  addr_to_string_no_port(&baddr,(u08bits*)sbaddr);
+
 		size_t i = 0;
 		for(i=0;i<turn_params.listener.addrs_number;++i) {
 			if(addr_eq(turn_params.listener.encaddrs[turn_params.listener.addrs_number-1],&baddr)) {
@@ -294,12 +298,12 @@ void add_listener_addr(const char* addr) {
 		}
 		++turn_params.listener.addrs_number;
 		++turn_params.listener.services_number;
-		turn_params.listener.addrs = (char**)realloc(turn_params.listener.addrs, sizeof(char*)*turn_params.listener.addrs_number);
-		turn_params.listener.addrs[turn_params.listener.addrs_number-1]=strdup(addr);
-		turn_params.listener.encaddrs = (ioa_addr**)realloc(turn_params.listener.encaddrs, sizeof(ioa_addr*)*turn_params.listener.addrs_number);
+		turn_params.listener.addrs = (char**)turn_realloc(turn_params.listener.addrs, 0, sizeof(char*)*turn_params.listener.addrs_number);
+		turn_params.listener.addrs[turn_params.listener.addrs_number-1]=turn_strdup(sbaddr);
+		turn_params.listener.encaddrs = (ioa_addr**)turn_realloc(turn_params.listener.encaddrs, 0, sizeof(ioa_addr*)*turn_params.listener.addrs_number);
 		turn_params.listener.encaddrs[turn_params.listener.addrs_number-1]=(ioa_addr*)turn_malloc(sizeof(ioa_addr));
 		addr_cpy(turn_params.listener.encaddrs[turn_params.listener.addrs_number-1],&baddr);
-		TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Listener address to use: %s\n",addr);
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Listener address to use: %s\n",sbaddr);
 	}
 }
 
@@ -321,8 +325,8 @@ int add_relay_addr(const char* addr) {
 		}
 
 		++turn_params.relays_number;
-		turn_params.relay_addrs = (char**)realloc(turn_params.relay_addrs, sizeof(char*)*turn_params.relays_number);
-		turn_params.relay_addrs[turn_params.relays_number-1]=strdup(sbaddr);
+		turn_params.relay_addrs = (char**)turn_realloc(turn_params.relay_addrs, 0, sizeof(char*)*turn_params.relays_number);
+		turn_params.relay_addrs[turn_params.relays_number-1]=turn_strdup(sbaddr);
 
 		TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Relay address to use: %s\n",sbaddr);
 		return 1;
@@ -374,6 +378,8 @@ static void auth_server_receive_message(struct bufferevent *bev, void *ptr)
     
     if(am.ct == TURN_CREDENTIALS_SHORT_TERM) {
       st_password_t pwd;
+      am.in_oauth = 0;
+      am.out_oauth = 0;
       if(get_user_pwd(am.username,pwd)<0) {
     	  am.success = 0;
       } else {
@@ -382,7 +388,7 @@ static void auth_server_receive_message(struct bufferevent *bev, void *ptr)
       }
     } else {
       hmackey_t key;
-      if(get_user_key(am.username,am.realm,key,am.in_buffer.nbh)<0) {
+      if(get_user_key(am.in_oauth,&(am.out_oauth),&(am.max_session_time),am.username,am.realm,key,am.in_buffer.nbh)<0) {
     	  am.success = 0;
       } else {
     	  ns_bcopy(key,am.key,sizeof(hmackey_t));
@@ -451,7 +457,6 @@ static int send_socket_to_general_relay(ioa_engine_handle e, struct message_to_r
 	int success = 0;
 
 	if(!rdest) {
-		success = -1;
 		goto label_end;
 	}
 
@@ -486,12 +491,14 @@ static int send_socket_to_relay(turnserver_id id, u64bits cid, stun_tid *tid, io
 				int message_integrity, MESSAGE_TO_RELAY_TYPE rmt, ioa_net_data *nd,
 				int can_resume)
 {
-	int ret = 0;
+	int ret = -1;
 
 	struct message_to_relay sm;
 	ns_bzero(&sm,sizeof(struct message_to_relay));
 	sm.t = rmt;
 
+	ioa_socket_handle s_to_delete = s;
+
 	struct relay_server *rs = NULL;
 	if(id>=TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP) {
 		size_t dest = id-TURNSERVER_ID_BOUNDARY_BETWEEN_TCP_AND_UDP;
@@ -500,7 +507,6 @@ static int send_socket_to_relay(turnserver_id id, u64bits cid, stun_tid *tid, io
 					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());
-			ret = -1;
 			goto err;
 		}
 		rs = udp_relay_servers[dest];
@@ -509,7 +515,6 @@ static int send_socket_to_relay(turnserver_id id, u64bits cid, stun_tid *tid, io
 				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());
-			ret = -1;
 			goto err;
 		}
 	} else {
@@ -519,7 +524,6 @@ static int send_socket_to_relay(turnserver_id id, u64bits cid, stun_tid *tid, io
 					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());
-			ret = -1;
 			goto err;
 		}
 		rs = general_relay_servers[dest];
@@ -528,7 +532,6 @@ static int send_socket_to_relay(turnserver_id id, u64bits cid, stun_tid *tid, io
 				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());
-			ret = -1;
 			goto err;
 		}
 	}
@@ -550,6 +553,8 @@ static int send_socket_to_relay(turnserver_id id, u64bits cid, stun_tid *tid, io
 			sm.m.cb_sm.can_resume = can_resume;
 
 			nd->nbh = NULL;
+			s_to_delete = NULL;
+			ret = 0;
 		}
 
 		break;
@@ -563,17 +568,19 @@ static int send_socket_to_relay(turnserver_id id, u64bits cid, stun_tid *tid, io
 			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__);
-			ret = -1;
 		}
 
 		break;
 	}
 	default: {
 		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: UNKNOWN RMT message: %d\n",__FUNCTION__,(int)rmt);
-		ret = -1;
 	}
 	}
 
@@ -588,16 +595,19 @@ static int send_socket_to_relay(turnserver_id id, u64bits cid, stun_tid *tid, io
 					"%s: Empty output buffer\n",
 					__FUNCTION__);
 			ret = -1;
+			s_to_delete = s;
 		}
 	}
 
  err:
-	if(ret < 0) {
-	  IOA_CLOSE_SOCKET(s);
-	  if(nd) {
-	    ioa_network_buffer_delete(NULL, nd->nbh);
-	    nd->nbh = NULL;
-	  }
+
+	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;
@@ -713,6 +723,7 @@ static int handle_relay_message(relay_server_handle rs, struct message_to_relay
 					"%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;
 				open_client_connection_session(&(rs->server), &(sm->m.sm));
@@ -726,7 +737,13 @@ static int handle_relay_message(relay_server_handle rs, struct message_to_relay
 
 			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);
+				/*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;
@@ -744,6 +761,7 @@ static int handle_relay_message(relay_server_handle rs, struct message_to_relay
 									"%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;
 				open_client_connection_session(&(rs->server), &(sm->m.sm));
@@ -764,7 +782,7 @@ static int handle_relay_message(relay_server_handle rs, struct message_to_relay
 
 static void handle_relay_auth_message(struct relay_server *rs, struct auth_message *am)
 {
-	am->resume_func(am->success, am->key, am->pwd,
+	am->resume_func(am->success, am->out_oauth, am->max_session_time, am->key, am->pwd,
 				&(rs->server), am->ctxkey, &(am->in_buffer));
 	if (am->in_buffer.nbh) {
 		ioa_network_buffer_delete(rs->ioa_eng, am->in_buffer.nbh);
@@ -1521,6 +1539,9 @@ void run_listener_server(struct listener_server *ls)
 		run_events(ls->event_base, ls->ioa_eng);
 
 		rollover_logfile();
+
+		tm_print();
+
 	}
 }
 
@@ -1590,7 +1611,8 @@ static void setup_relay_server(struct relay_server *rs, ioa_engine_handle e, int
 			 &turn_params.secure_stun, turn_params.shatype, &turn_params.mobility,
 			 turn_params.server_relay,
 			 send_turn_session_info,
-			 allocate_bps);
+			 allocate_bps,
+			 turn_params.oauth, turn_params.oauth_server_name);
 	
 	if(to_set_rfc5780) {
 		set_rfc5780(&(rs->server), get_alt_addr, send_message_from_listener_to_client);
@@ -1686,6 +1708,9 @@ static void* run_auth_server_thread(void *arg)
 		read_userdb_file(0);
 		update_white_and_black_lists();
 		auth_ping(authserver->rch);
+#if defined(DB_TEST)
+		run_db_test();
+#endif
 	}
 
 	return arg;

+ 102 - 45
src/apps/relay/ns_ioalib_engine_impl.c

@@ -80,6 +80,8 @@ struct turn_sock_extended_err {
 
 #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 ******/
@@ -186,6 +188,8 @@ static void log_socket_event(ioa_socket_handle s, const char *msg, int error) {
 		if(error)
 			ll = TURN_LOG_LEVEL_ERROR;
 
+		UNUSED_ARG(ll);
+
 		{
 			char sraddr[129]="\0";
 			char sladdr[129]="\0";
@@ -365,7 +369,7 @@ ioa_engine_handle create_ioa_engine(super_memory_t *sm,
 	}
 
 	if (!relays_number || !relay_addrs || !tp) {
-		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot create TURN engine\n", __FUNCTION__);
+		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));
@@ -574,7 +578,7 @@ ioa_timer_handle set_ioa_timer(ioa_engine_handle e, int secs, int ms, ioa_timer_
 		te->e = e;
 		te->ev = ev;
 		te->cb = cb;
-		te->txt = strdup(txt);
+		te->txt = turn_strdup(txt);
 
 		if(!ms) {
 			tv.tv_usec = 0;
@@ -1446,16 +1450,23 @@ void add_socket_to_map(ioa_socket_handle s, ur_addr_map *amap)
 				&(s->remote_addr),
 				(ur_addr_map_value_type)s);
 		s->sockets_container = amap;
+
+		//printf("%s: 111.111: amap=0x%lx: ne=%lu, sz=%lu\n",__FUNCTION__,(unsigned long)amap,(unsigned long)ur_addr_map_num_elements(amap),(unsigned long)ur_addr_map_size(amap));
 	}
 }
 
 void delete_socket_from_map(ioa_socket_handle s)
 {
 	if(s && s->sockets_container) {
+
+		//ur_addr_map *amap = s->sockets_container;
+
 		ur_addr_map_del(s->sockets_container,
 				&(s->remote_addr),
 				NULL);
 		s->sockets_container = NULL;
+
+		//printf("%s: 111.222: amap=0x%lx: ne=%lu, sz=%lu\n",__FUNCTION__,(unsigned long)amap,(unsigned long)ur_addr_map_num_elements(amap),(unsigned long)ur_addr_map_size(amap));
 	}
 }
 
@@ -1476,18 +1487,20 @@ ioa_socket_handle create_ioa_socket_from_fd(ioa_engine_handle e,
 	ret->magic = SOCKET_MAGIC;
 
 	ret->fd = fd;
-	ret->family = local_addr->ss.sa_family;
 	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);
 	}
 
@@ -1500,6 +1513,44 @@ ioa_socket_handle create_ioa_socket_from_fd(ioa_engine_handle e,
 	return ret;
 }
 
+static void ssl_info_callback(SSL *ssl, int where, int ret) {
+
+    UNUSED_ARG(ret);
+
+    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;
+    			}
+    		}
+    	}
+    }
+}
+
+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)
 {
@@ -1509,7 +1560,7 @@ ioa_socket_handle create_ioa_socket_from_ssl(ioa_engine_handle e, ioa_socket_han
 	ioa_socket_handle ret = create_ioa_socket_from_fd(e, parent_s->fd, parent_s, st, sat, remote_addr, local_addr);
 
 	if(ret) {
-		ret->ssl = ssl;
+		set_socket_ssl(ret,ssl);
 		if(st == DTLS_SOCKET)
 			STRCPY(ret->orig_ctx_type,"DTLSv1.0");
 	}
@@ -1576,16 +1627,16 @@ void detach_socket_net_data(ioa_socket_handle s)
 		if(s->list_ev) {
 			evconnlistener_free(s->list_ev);
 			s->list_ev = NULL;
-			s->acb = NULL;
-			s->acbarg = NULL;
 		}
+		s->acb = NULL;
+		s->acbarg = NULL;
 		if(s->conn_bev) {
 			bufferevent_disable(s->conn_bev,EV_READ|EV_WRITE);
 			bufferevent_free(s->conn_bev);
 			s->conn_bev=NULL;
-			s->conn_arg=NULL;
-			s->conn_cb=NULL;
 		}
+		s->conn_arg=NULL;
+		s->conn_cb=NULL;
 		if(s->bev) {
 			bufferevent_disable(s->bev,EV_READ|EV_WRITE);
 			bufferevent_free(s->bev);
@@ -1694,13 +1745,16 @@ ioa_socket_handle detach_ioa_socket(ioa_socket_handle s, int full_detach)
 
 		ret->magic = SOCKET_MAGIC;
 
-		ret->username_hash = s->username_hash;
 		ret->realm_hash = s->realm_hash;
 
-		ret->ssl = s->ssl;
+		set_socket_ssl(ret,s->ssl);
 		ret->fd = s->fd;
 
-		ret->family = s->family;
+		if(s->parent_s)
+			ret->family = s->parent_s->family;
+		else
+			ret->family = s->family;
+
 		ret->st = s->st;
 		ret->sat = s->sat;
 		ret->bound = s->bound;
@@ -1752,7 +1806,7 @@ ioa_socket_handle detach_ioa_socket(ioa_socket_handle s, int full_detach)
 		ret->current_tos = s->current_tos;
 		ret->default_tos = s->default_tos;
 
-		s->ssl = NULL;
+		set_socket_ssl(s,NULL);
 		s->fd = -1;
 	}
 
@@ -1795,6 +1849,8 @@ void set_ioa_socket_sub_session(ioa_socket_handle s, tcp_connection *tc)
 int get_ioa_socket_address_family(ioa_socket_handle s) {
 	if(!s) {
 		return AF_INET;
+	} else if(s->done) {
+		return s->family;
 	} else if(s->parent_s) {
 		return s->parent_s->family;
 	} else {
@@ -1821,7 +1877,7 @@ 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)) {
@@ -1855,6 +1911,7 @@ ioa_addr* get_local_addr_from_ioa_socket(ioa_socket_handle s)
 			}
 		}
 	}
+
 	return NULL;
 }
 
@@ -1866,6 +1923,7 @@ ioa_addr* get_remote_addr_from_ioa_socket(ioa_socket_handle s)
 			return &(s->remote_addr);
 		}
 	}
+
 	return NULL;
 }
 
@@ -2324,7 +2382,7 @@ static int socket_input_worker(ioa_socket_handle s)
 #if defined(SSL_TXT_TLSV1_2)
 			case TURN_TLS_v1_2:
 				if(s->e->tls_ctx_v1_2) {
-					s->ssl = SSL_new(s->e->tls_ctx_v1_2);
+					set_socket_ssl(s,SSL_new(s->e->tls_ctx_v1_2));
 					STRCPY(s->orig_ctx_type,"TLSv1.2");
 				}
 				break;
@@ -2332,35 +2390,37 @@ static int socket_input_worker(ioa_socket_handle s)
 #if defined(SSL_TXT_TLSV1_1)
 			case TURN_TLS_v1_1:
 				if(s->e->tls_ctx_v1_1) {
-					s->ssl = SSL_new(s->e->tls_ctx_v1_1);
+					set_socket_ssl(s,SSL_new(s->e->tls_ctx_v1_1));
 					STRCPY(s->orig_ctx_type,"TLSv1.1");
 				}
 				break;
 #endif
 			case TURN_TLS_v1_0:
 				if(s->e->tls_ctx_v1_0) {
-					s->ssl = SSL_new(s->e->tls_ctx_v1_0);
+					set_socket_ssl(s,SSL_new(s->e->tls_ctx_v1_0));
 					STRCPY(s->orig_ctx_type,"TLSv1.0");
 				}
 				break;
 			default:
 				if(s->e->tls_ctx_ssl23) {
-					s->ssl = SSL_new(s->e->tls_ctx_ssl23);
+					set_socket_ssl(s,SSL_new(s->e->tls_ctx_ssl23));
 					STRCPY(s->orig_ctx_type,"SSLv23");
 				} else {
 					s->tobeclosed = 1;
 					return 0;
 				}
 			};
-			s->bev = bufferevent_openssl_socket_new(s->e->event_base,
+			if(s->ssl) {
+				s->bev = bufferevent_openssl_socket_new(s->e->event_base,
 								s->fd,
 								s->ssl,
 								BUFFEREVENT_SSL_ACCEPTING,
 								BEV_OPT_THREADSAFE | BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS);
-			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. */
+				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 //TURN_NO_TLS
 		{
@@ -3212,7 +3272,7 @@ int register_callback_on_ioa_socket(ioa_engine_handle e, ioa_socket_handle s, in
 #if !defined(TURN_NO_TLS)
 						if(!(s->ssl)) {
 							//??? how we can get to this point ???
-							s->ssl = SSL_new(e->tls_ctx_ssl23);
+							set_socket_ssl(s,SSL_new(e->tls_ctx_ssl23));
 							STRCPY(s->orig_ctx_type,"SSLv23");
 							s->bev = bufferevent_openssl_socket_new(s->e->event_base,
 											s->fd,
@@ -3301,14 +3361,9 @@ static u32bits string_hash(const u08bits *str) {
   return hash;
 }
 
-int check_username_hash(ioa_socket_handle s, u08bits *username, u08bits *realm)
+int check_realm_hash(ioa_socket_handle s, u08bits *realm)
 {
 	if(s) {
-		if(username && username[0]) {
-			if(s->username_hash != string_hash(username)) {
-				return 0;
-			}
-		}
 		if(realm && realm[0]) {
 			if(s->realm_hash != string_hash(realm)) {
 				return 0;
@@ -3318,12 +3373,9 @@ int check_username_hash(ioa_socket_handle s, u08bits *username, u08bits *realm)
 	return 1;
 }
 
-void set_username_hash(ioa_socket_handle s, u08bits *username, u08bits *realm)
+void set_realm_hash(ioa_socket_handle s, u08bits *realm)
 {
 	if(s) {
-		if(username && username[0]) {
-			s->username_hash = string_hash(username);
-		}
 		if(realm && realm[0]) {
 			s->realm_hash = string_hash(realm);
 		}
@@ -3570,11 +3622,11 @@ static void init_super_memory_region(super_memory_t *r)
 	if(r) {
 		ns_bzero(r,sizeof(super_memory_t));
 
-		r->super_memory = (char**)malloc(sizeof(char*));
-		r->super_memory[0] = (char*)malloc(TURN_SM_SIZE);
+		r->super_memory = (char**)turn_malloc(sizeof(char*));
+		r->super_memory[0] = (char*)turn_malloc(TURN_SM_SIZE);
 		ns_bzero(r->super_memory[0],TURN_SM_SIZE);
 
-		r->sm_allocated = (size_t*)malloc(sizeof(size_t*));
+		r->sm_allocated = (size_t*)turn_malloc(sizeof(size_t*));
 		r->sm_allocated[0] = 0;
 
 		r->sm_total_sz = TURN_SM_SIZE;
@@ -3594,7 +3646,7 @@ 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));
+	super_memory_t* r = (super_memory_t*)turn_malloc(sizeof(super_memory_t));
 	init_super_memory_region(r);
 	return r;
 }
@@ -3605,11 +3657,14 @@ void* allocate_super_memory_region_func(super_memory_t *r, size_t size, const ch
 	UNUSED_ARG(func);
 	UNUSED_ARG(line);
 
-	if(!r)
-		return malloc(size);
-
 	void *ret = NULL;
 
+	if(!r) {
+		ret = turn_malloc(size);
+		ns_bzero(ret, size);
+		return ret;
+	}
+
 	pthread_mutex_lock(&r->mutex_sm);
 
 	size = ((size_t)((size+sizeof(void*))/(sizeof(void*)))) * sizeof(void*);
@@ -3638,10 +3693,10 @@ void* allocate_super_memory_region_func(super_memory_t *r, size_t size, const ch
 
 		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);
+			r->super_memory = (char**)turn_realloc(r->super_memory,0,(r->sm_chunk+1) * sizeof(char*));
+			r->super_memory[r->sm_chunk] = (char*)turn_malloc(TURN_SM_SIZE);
 			ns_bzero(r->super_memory[r->sm_chunk],TURN_SM_SIZE);
-			r->sm_allocated = (size_t*)realloc(r->sm_allocated,(r->sm_chunk+1) * sizeof(size_t*));
+			r->sm_allocated = (size_t*)turn_realloc(r->sm_allocated,0,(r->sm_chunk+1) * sizeof(size_t*));
 			r->sm_allocated[r->sm_chunk] = 0;
 			region = r->super_memory[r->sm_chunk];
 			rsz = r->sm_allocated + r->sm_chunk;
@@ -3660,8 +3715,10 @@ void* allocate_super_memory_region_func(super_memory_t *r, size_t size, const ch
 
 	pthread_mutex_unlock(&r->mutex_sm);
 
-	if(!ret)
-		ret = malloc(size);
+	if(!ret) {
+		ret = turn_malloc(size);
+		ns_bzero(ret, size);
+	}
 
 	return ret;
 }

+ 2 - 3
src/apps/relay/ns_ioalib_impl.h

@@ -170,7 +170,6 @@ struct _ioa_socket
 {
 	evutil_socket_t fd;
 	struct _ioa_socket *parent_s;
-	void *listener_server;
 	u32bits magic;
 	ur_addr_map *sockets_container; /* relay container for UDP sockets */
 	struct bufferevent *bev;
@@ -179,6 +178,7 @@ struct _ioa_socket
 	SOCKET_TYPE st;
 	SOCKET_APP_TYPE sat;
 	SSL* ssl;
+	u32bits ssl_renegs;
 	int in_write;
 	char orig_ctx_type[16];
 	int bound;
@@ -213,7 +213,6 @@ struct _ioa_socket
 	connect_cb conn_cb;
 	void *conn_arg;
 	//Transferable sockets user data
-	u32bits username_hash;
 	u32bits realm_hash;
 	//Accept:
 	struct evconnlistener *list_ev;
@@ -235,7 +234,7 @@ typedef struct _timer_event
 
 /* realm */
 
-void create_new_realm(char* name);
+void create_default_realm(void);
 int get_realm_data(char* name, realm_params_t* rp);
 
 /* engine handling */

+ 0 - 2
src/apps/relay/tls_listener.c

@@ -98,8 +98,6 @@ static void server_input_handler(struct evconnlistener *l, evutil_socket_t fd,
 
 	if (ioas) {
 
-		ioas->listener_server = server;
-
 		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;

+ 5 - 5
src/apps/relay/turncli.c

@@ -465,7 +465,7 @@ static int print_session(ur_map_key_type key, ur_map_value_type value, void *arg
 				csarg->user_names = (char**)turn_realloc(csarg->user_names,
 						(size_t)value * sizeof(char*),
 						csarg->users_number * sizeof(char*));
-				csarg->user_names[(size_t)value] = strdup((char*)tsi->username);
+				csarg->user_names[(size_t)value] = turn_strdup((char*)tsi->username);
 				csarg->user_counters[(size_t)value] = 0;
 				ur_string_map_put(csarg->users, (ur_string_map_key_type)(char*)tsi->username, value);
 			}
@@ -820,9 +820,9 @@ static void cli_print_configuration(struct cli_session* cs)
 			cli_print_flag(cs,1,"Short-term authorization mechanism",0);
 		else
 			cli_print_flag(cs,1,"Anonymous credentials",0);
-		cli_print_flag(cs,turn_params.use_auth_secret_with_timestamp,"REST API support",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,"REST API separator ASCII number",0);
+			cli_print_uint(cs,turn_params.rest_api_separator,"TURN REST API separator ASCII number",0);
 
 		myprintf(cs,"\n");
 
@@ -939,7 +939,7 @@ static int run_cli_input(struct cli_session* cs, const char *buf0, unsigned int
 
 	if(cs && buf0 && cs->ts && cs->bev) {
 
-		char *buf = (char*)malloc(len+1);
+		char *buf = (char*)turn_malloc(len+1);
 		ns_bcopy(buf0,buf,len);
 		buf[len]=0;
 
@@ -1078,7 +1078,7 @@ static int run_cli_input(struct cli_session* cs, const char *buf0, unsigned int
 			type_cli_cursor(cs);
 		}
 
-		free(buf);
+		turn_free(buf,len+1);
 	}
 
 	return ret;

+ 266 - 83
src/apps/relay/userdb.c

@@ -65,15 +65,6 @@
 //////////// REALM //////////////
 
 static realm_params_t *default_realm_params_ptr = NULL;
-static const realm_params_t _default_realm_params =
-{
-  1,
-  {
-	"\0", /* name */
-    {0,0,0}
-  },
-  {0,NULL}
-};
 
 static ur_string_map *realms = NULL;
 static turn_mutex o_to_realm_mutex;
@@ -95,40 +86,30 @@ void update_o_to_realm(ur_string_map * o_to_realm_new) {
   TURN_MUTEX_UNLOCK(&o_to_realm_mutex);
 }
 
-void create_new_realm(char* name)
+void create_default_realm()
 {
-	realm_params_t *ret = NULL;
-
-	if((name == NULL)||(name[0]==0)) {
-		if(default_realm_params_ptr) {
-			return;
-		}
-		/* 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 = (realm_params_t*)malloc(sizeof(realm_params_t));
-		ns_bcopy(&_default_realm_params,default_realm_params_ptr,sizeof(realm_params_t));
-		realms = ur_string_map_create(NULL);
-		ur_string_map_lock(realms);
-		ret = default_realm_params_ptr;
-	} else {
-		ur_string_map_value_type value = 0;
-		ur_string_map_lock(realms);
-		if (!ur_string_map_get(realms, (const ur_string_map_key_type) name, &value)) {
-			ret = (realm_params_t*)turn_malloc(sizeof(realm_params_t));
-			ns_bcopy(default_realm_params_ptr,ret,sizeof(realm_params_t));
-			STRCPY(ret->options.name,name);
-			value = (ur_string_map_value_type)ret;
-			ur_string_map_put(realms, (const ur_string_map_key_type) name, value);
-			add_to_secrets_list(&realms_list, name);
-		} else {
-			ur_string_map_unlock(realms);
-			return;
-		}
+	if(default_realm_params_ptr) {
+		return;
 	}
 
-	ret->status.alloc_counters =  ur_string_map_create(NULL);
+	static realm_params_t _default_realm_params =
+	{
+	  1,
+	  {
+		"\0", /* name */
+	    {0,0,0}
+	  },
+	  {0,NULL}
+	};
+
+	/* init everything: */
+	TURN_MUTEX_INIT_RECURSIVE(&o_to_realm_mutex);
+	init_secrets_list(&realms_list);
+	o_to_realm = ur_string_map_create(turn_free_simple);
+	default_realm_params_ptr = &_default_realm_params;
+	realms = ur_string_map_create(NULL);
+	ur_string_map_lock(realms);
+	default_realm_params_ptr->status.alloc_counters =  ur_string_map_create(NULL);
 	ur_string_map_unlock(realms);
 }
 
@@ -188,12 +169,12 @@ 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);
+		char *realm = turn_strdup((char*)value);
 		TURN_MUTEX_UNLOCK(&o_to_realm_mutex);
 		realm_params_t rp;
 		get_realm_data(realm, &rp);
 		ns_bcopy(&(rp.options),ro,sizeof(realm_options_t));
-		free(realm);
+		turn_free(realm,strlen(realm)+1);
 		return 1;
 	} else {
 		TURN_MUTEX_UNLOCK(&o_to_realm_mutex);
@@ -310,9 +291,9 @@ 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)
 {
 	if(sl && elem) {
-		sl->secrets = (char**)realloc(sl->secrets,(sizeof(char*)*(sl->sz+1)));
-		sl->secrets[sl->sz] = strdup(elem);
-		sl->sz += 1;
+	  sl->secrets = (char**)turn_realloc(sl->secrets,0,(sizeof(char*)*(sl->sz+1)));
+	  sl->secrets[sl->sz] = turn_strdup(elem);
+	  sl->sz += 1;
 	}
 }
 
@@ -384,7 +365,7 @@ static turn_time_t get_rest_api_timestamp(char *usname)
 
 static char *get_real_username(char *usname)
 {
-	if(turn_params.use_auth_secret_with_timestamp) {
+	if(usname[0] && turn_params.use_auth_secret_with_timestamp) {
 		char *col=strchr(usname,turn_params.rest_api_separator);
 		if(col) {
 			if(col == usname) {
@@ -403,7 +384,7 @@ static char *get_real_username(char *usname)
 					usname = col+1;
 				} else {
 					*col=0;
-					usname = strdup(usname);
+					usname = turn_strdup(usname);
 					*col=turn_params.rest_api_separator;
 					return usname;
 				}
@@ -411,16 +392,145 @@ static char *get_real_username(char *usname)
 		}
 	}
 
-	return strdup(usname);
+	return turn_strdup(usname);
 }
 
 /*
- * Long-term mechanism password retrieval
+ * Password retrieval
  */
-int get_user_key(u08bits *usname, u08bits *realm, hmackey_t key, ioa_network_buffer_handle nbh)
+int get_user_key(int in_oauth, int *out_oauth, int *max_session_time, u08bits *usname, u08bits *realm, hmackey_t key, ioa_network_buffer_handle nbh)
 {
 	int ret = -1;
 
+	if(max_session_time)
+		*max_session_time = 0;
+
+	if(in_oauth && out_oauth && usname && usname[0] && realm && realm[0]) {
+
+		stun_attr_ref sar = stun_attr_get_first_by_type_str(ioa_network_buffer_data(nbh),
+								ioa_network_buffer_get_size(nbh),
+								STUN_ATTRIBUTE_OAUTH_ACCESS_TOKEN);
+		if(sar) {
+
+			int len = stun_attr_get_len(sar);
+			const u08bits *value = stun_attr_get_value(sar);
+
+			*out_oauth = 1;
+
+			if(len>0 && value) {
+
+				turn_dbdriver_t * dbd = get_dbdriver();
+
+				if (dbd && dbd->get_oauth_key) {
+
+					oauth_key_data_raw rawKey;
+					ns_bzero(&rawKey,sizeof(rawKey));
+
+					int gres = (*(dbd->get_oauth_key))(usname,&rawKey);
+					if(gres<0)
+						return ret;
+
+					if(!rawKey.kid[0])
+						return ret;
+
+					if(rawKey.lifetime) {
+						if(!turn_time_before(turn_time(),(turn_time_t)(rawKey.timestamp + rawKey.lifetime+OAUTH_TIME_DELTA))) {
+							return ret;
+						}
+					}
+
+					oauth_key_data okd;
+					ns_bzero(&okd,sizeof(okd));
+
+					convert_oauth_key_data_raw(&rawKey, &okd);
+
+					char err_msg[1025] = "\0";
+					size_t err_msg_size = sizeof(err_msg) - 1;
+
+					oauth_key okey;
+					ns_bzero(&okey,sizeof(okey));
+
+					if (convert_oauth_key_data(&okd, &okey, err_msg, err_msg_size) < 0) {
+						TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s\n", err_msg);
+						return -1;
+					}
+
+					oauth_token dot;
+					ns_bzero((&dot),sizeof(dot));
+
+					encoded_oauth_token etoken;
+					ns_bzero(&etoken,sizeof(etoken));
+
+					if((size_t)len > sizeof(etoken.token)) {
+						TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Encoded oAuth token is too large\n");
+						return -1;
+					}
+					ns_bcopy(value,etoken.token,(size_t)len);
+					etoken.size = (size_t)len;
+
+					const char* server_name = (char*)turn_params.oauth_server_name;
+					if(!(server_name && server_name[0])) {
+						server_name = (char*)realm;
+					}
+
+					if (decode_oauth_token((const u08bits *) server_name, &etoken,&okey, &dot) < 0) {
+						TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot decode oauth token\n");
+						return -1;
+					}
+
+					switch(dot.enc_block.key_length) {
+					case SHA1SIZEBYTES:
+						if(turn_params.shatype != SHATYPE_SHA1) {
+							TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong size of the MAC key in oAuth token(1): %d\n",(int)dot.enc_block.key_length);
+							return -1;
+						}
+						break;
+					case SHA256SIZEBYTES:
+						if(turn_params.shatype != SHATYPE_SHA256) {
+							TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong size of the MAC key in oAuth token(2): %d\n",(int)dot.enc_block.key_length);
+							return -1;
+						}
+						break;
+					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;
+					};
+
+					st_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,
+								turn_params.shatype,NULL)>0) {
+
+						turn_time_t lifetime = (turn_time_t)(dot.enc_block.lifetime);
+						if(lifetime) {
+							turn_time_t ts = (turn_time_t)(dot.enc_block.timestamp >> 16);
+							turn_time_t to = ts + lifetime + OAUTH_TIME_DELTA;
+							turn_time_t ct = turn_time();
+							if(!turn_time_before(ct,to)) {
+								TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "oAuth token is too old\n");
+								return -1;
+							}
+							if(max_session_time) {
+								*max_session_time = to - ct;
+							}
+						}
+
+						ns_bcopy(dot.enc_block.mac_key,key,dot.enc_block.key_length);
+
+						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);
@@ -527,7 +637,7 @@ int get_user_key(u08bits *usname, u08bits *realm, hmackey_t key, ioa_network_buf
 
   turn_dbdriver_t * dbd = get_dbdriver();
   if (dbd && dbd->get_user_key) {
-    ret = (*dbd->get_user_key)(usname, realm, key);
+    ret = (*(dbd->get_user_key))(usname, realm, key);
   }
 
 	return ret;
@@ -548,7 +658,7 @@ int get_user_pwd(u08bits *usname, st_password_t pwd)
   return ret;
 }
 
-u08bits *start_user_check(turnserver_id id, turn_credential_type ct, u08bits *usname, u08bits *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, u64bits ctxkey, int *postpone_reply)
+u08bits *start_user_check(turnserver_id id, turn_credential_type ct, int in_oauth, int *out_oauth, u08bits *usname, u08bits *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, u64bits ctxkey, int *postpone_reply)
 {
 	*postpone_reply = 1;
 
@@ -556,6 +666,8 @@ u08bits *start_user_check(turnserver_id id, turn_credential_type ct, u08bits *us
 	ns_bzero(&am,sizeof(struct auth_message));
 	am.id = id;
 	am.ct = ct;
+	am.in_oauth = in_oauth;
+	am.out_oauth = *out_oauth;
 	STRCPY(am.username,usname);
 	STRCPY(am.realm,realm);
 	am.resume_func = resume;
@@ -568,11 +680,11 @@ u08bits *start_user_check(turnserver_id id, turn_credential_type ct, u08bits *us
 	return NULL;
 }
 
-int check_new_allocation_quota(u08bits *user, u08bits *realm)
+int check_new_allocation_quota(u08bits *user, int oauth, u08bits *realm)
 {
 	int ret = 0;
-	if (user) {
-		u08bits *username = (u08bits*)get_real_username((char*)user);
+	if (user || oauth) {
+		u08bits *username = oauth ? (u08bits*)turn_strdup("") : (u08bits*)get_real_username((char*)user);
 		realm_params_t *rp = get_realm((char*)realm);
 		ur_string_map_lock(rp->status.alloc_counters);
 		if (rp->options.perf_options.total_quota && (rp->status.total_current_allocs >= rp->options.perf_options.total_quota)) {
@@ -595,28 +707,30 @@ int check_new_allocation_quota(u08bits *user, u08bits *realm)
 		} else {
 			++(rp->status.total_current_allocs);
 		}
-		turn_free(username,strlen(username)+1);
+		turn_free(username,strlen((char*)username)+1);
 		ur_string_map_unlock(rp->status.alloc_counters);
 	}
 	return ret;
 }
 
-void release_allocation_quota(u08bits *user, u08bits *realm)
+void release_allocation_quota(u08bits *user, int oauth, u08bits *realm)
 {
 	if (user) {
-		u08bits *username = (u08bits*)get_real_username((char*)user);
+		u08bits *username = oauth ? (u08bits*)turn_strdup("") : (u08bits*)get_real_username((char*)user);
 		realm_params_t *rp = get_realm((char*)realm);
 		ur_string_map_lock(rp->status.alloc_counters);
-		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(username[0]) {
+			ur_string_map_value_type value = 0;
+			ur_string_map_get(rp->status.alloc_counters, (ur_string_map_key_type) username, &value);
+			if (value) {
+				value = (ur_string_map_value_type)(((size_t)value) - 1);
+				ur_string_map_put(rp->status.alloc_counters, (ur_string_map_key_type) username, value);
+			}
 		}
 		if (rp->status.total_current_allocs)
 			--(rp->status.total_current_allocs);
 		ur_string_map_unlock(rp->status.alloc_counters);
-		turn_free(username, strlen(username)+1);
+		turn_free(username, strlen((char*)username)+1);
 	}
 }
 
@@ -725,8 +839,8 @@ int add_user_account(char *user, int dynamic)
 					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key format: %s\n",s);
 				} if(convert_string_key_to_binary(keysource, *key, sz)<0) {
 					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key: %s\n",s);
-					free(usname);
-					free(key);
+					turn_free(usname,strlen(usname)+1);
+					turn_free(key,sizeof(hmackey_t));
 					return -1;
 				}
 			} else {
@@ -743,7 +857,7 @@ int add_user_account(char *user, int dynamic)
 				ur_string_map_unlock(turn_params.default_users_db.ram_db.static_accounts);
 			}
 			turn_params.default_users_db.ram_db.users_number++;
-			free(usname);
+			turn_free(usname,strlen(usname)+1);
 			return 0;
 		}
 	}
@@ -1039,8 +1153,8 @@ int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08b
 				}
 
 				add_and_cont:
-				content = (char**)realloc(content, sizeof(char*) * (++csz));
-				content[csz - 1] = strdup(s0);
+				content = (char**)turn_realloc(content, 0, sizeof(char*) * (++csz));
+				content[csz - 1] = turn_strdup(s0);
 			}
 
 			fclose(f);
@@ -1054,12 +1168,12 @@ int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08b
 		  for(i=0;i<sz;i++) {
 		    snprintf(us+strlen(us),sizeof(us)-strlen(us),"%02x",(unsigned int)key[i]);
 		  }
-		  content = (char**)realloc(content,sizeof(char*)*(++csz));
-		  content[csz-1]=strdup(us);
+		  content = (char**)turn_realloc(content,0,sizeof(char*)*(++csz));
+		  content[csz-1]=turn_strdup(us);
 		}
 
 		if(!full_path_to_userdb_file)
-			full_path_to_userdb_file=strdup(pud->userdb);
+			full_path_to_userdb_file=turn_strdup(pud->userdb);
 
 		size_t dirsz = strlen(full_path_to_userdb_file)+21;
 		char *dir = (char*)turn_malloc(dirsz+1);
@@ -1085,7 +1199,7 @@ int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08b
 		fclose(f);
 
 		rename(dir,full_path_to_userdb_file);
-		free(dir);
+		turn_free(dir,dirsz+1);
 	}
 
 	return 0;
@@ -1101,6 +1215,75 @@ void auth_ping(redis_context_handle rch)
 	}
 }
 
+///////////////// TEST /////////////////
+
+#if defined(DB_TEST)
+
+void run_db_test(void)
+{
+	turn_dbdriver_t * dbd = get_dbdriver();
+	if (dbd) {
+
+		printf("DB TEST 1:\n");
+		dbd->list_oauth_keys();
+
+		printf("DB TEST 2:\n");
+		oauth_key_data_raw key_;
+		oauth_key_data_raw *key=&key_;
+		dbd->get_oauth_key((const u08bits*)"north",key);
+		printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, hkdf_hash_func=%s, as_rs_alg=%s, as_rs_key=%s, auth_alg=%s, auth_key=%s\n",
+		    		key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->hkdf_hash_func,
+		    		key->as_rs_alg, key->as_rs_key, key->auth_alg, key->auth_key);
+
+		printf("DB TEST 3:\n");
+
+		STRCPY(key->as_rs_alg,"as_rs_alg");
+		STRCPY(key->as_rs_key,"as_rs_key");
+		STRCPY(key->auth_alg,"auth_alg");
+		STRCPY(key->auth_key,"auth_key");
+		STRCPY(key->hkdf_hash_func,"hkdf");
+		STRCPY(key->ikm_key,"ikm_key");
+		STRCPY(key->kid,"kid");
+		key->timestamp = 123;
+		key->lifetime = 456;
+		dbd->del_oauth_key((const u08bits*)"kid");
+		dbd->set_oauth_key(key);
+		dbd->list_oauth_keys();
+
+		printf("DB TEST 4:\n");
+		dbd->get_oauth_key((const u08bits*)"kid",key);
+		printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, hkdf_hash_func=%s, as_rs_alg=%s, as_rs_key=%s, auth_alg=%s, auth_key=%s\n",
+		    		key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->hkdf_hash_func,
+		    		key->as_rs_alg, key->as_rs_key, key->auth_alg, key->auth_key);
+
+		printf("DB TEST 5:\n");
+		dbd->del_oauth_key((const u08bits*)"kid");
+		dbd->list_oauth_keys();
+
+		printf("DB TEST 6:\n");
+
+		dbd->get_oauth_key((const u08bits*)"north",key);
+
+		oauth_key_data oakd;
+		convert_oauth_key_data_raw(key, &oakd);
+		printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, hkdf_hash_func=%s, as_rs_alg=%s, as_rs_key_size=%d, auth_alg=%s, auth_key_size=%d\n",
+				    		oakd.kid, oakd.ikm_key, (unsigned long long)oakd.timestamp, (unsigned long)oakd.lifetime, oakd.hkdf_hash_func,
+				    		oakd.as_rs_alg, (int)oakd.as_rs_key_size, oakd.auth_alg, (int)oakd.auth_key_size);
+
+		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)
@@ -1213,15 +1396,15 @@ static void ip_list_free(ip_range_list_t *l)
 		size_t i;
 		for(i=0;i<l->ranges_number;++i) {
 			if(l->ranges && l->ranges[i])
-				free(l->ranges[i]);
+			  turn_free(l->ranges[i],0);
 			if(l->encaddrsranges && l->encaddrsranges[i])
-				free(l->encaddrsranges[i]);
+			  turn_free(l->encaddrsranges[i],0);
 		}
 		if(l->ranges)
-			free(l->ranges);
+		  turn_free(l->ranges,0);
 		if(l->encaddrsranges)
-			free(l->encaddrsranges);
-		free(l);
+		  turn_free(l->encaddrsranges,0);
+		turn_free(l,sizeof(ip_range_list_t));
 	}
 }
 
@@ -1251,7 +1434,7 @@ void update_white_and_black_lists(void)
 
 int add_ip_list_range(const char * range0, ip_range_list_t * list)
 {
-	char *range = strdup(range0);
+	char *range = turn_strdup(range0);
 
 	char* separator = strchr(range, '-');
 
@@ -1263,14 +1446,14 @@ int add_ip_list_range(const char * range0, ip_range_list_t * list)
 
 	if (make_ioa_addr((const u08bits*) range, 0, &min) < 0) {
 		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address format: %s\n", range);
-		free(range);
+		turn_free(range,0);
 		return -1;
 	}
 
 	if (separator) {
 		if (make_ioa_addr((const u08bits*) separator + 1, 0, &max) < 0) {
 			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address format: %s\n", separator + 1);
-			free(range);
+			turn_free(range,0);
 			return -1;
 		}
 	} else {
@@ -1282,9 +1465,9 @@ int add_ip_list_range(const char * range0, ip_range_list_t * list)
 		*separator = '-';
 
 	++(list->ranges_number);
-	list->ranges = (char**) realloc(list->ranges, sizeof(char*) * list->ranges_number);
+	list->ranges = (char**) turn_realloc(list->ranges, 0, sizeof(char*) * list->ranges_number);
 	list->ranges[list->ranges_number - 1] = range;
-	list->encaddrsranges = (ioa_addr_range**) realloc(list->encaddrsranges, sizeof(ioa_addr_range*) * list->ranges_number);
+	list->encaddrsranges = (ioa_addr_range**) turn_realloc(list->encaddrsranges, 0, sizeof(ioa_addr_range*) * list->ranges_number);
 
 	list->encaddrsranges[list->ranges_number - 1] = (ioa_addr_range*) turn_malloc(sizeof(ioa_addr_range));
 

+ 11 - 4
src/apps/relay/userdb.h

@@ -86,6 +86,9 @@ void update_o_to_realm(ur_string_map * o_to_realm_new);
 struct auth_message {
 	turnserver_id id;
 	turn_credential_type ct;
+	int in_oauth;
+	int out_oauth;
+	int max_session_time;
 	u08bits username[STUN_MAX_USERNAME_SIZE + 1];
 	u08bits realm[STUN_MAX_REALM_SIZE + 1];
 	hmackey_t key;
@@ -187,14 +190,18 @@ void add_to_secrets_list(secrets_list_t *sl, const char* elem);
 
 /////////// USER DB CHECK //////////////////
 
-int get_user_key(u08bits *uname, u08bits *realm, hmackey_t key, ioa_network_buffer_handle nbh);
+int get_user_key(int in_oauth, int *out_oauth, int *max_session_time, u08bits *uname, u08bits *realm, hmackey_t key, ioa_network_buffer_handle nbh);
 int get_user_pwd(u08bits *uname, st_password_t pwd);
-u08bits *start_user_check(turnserver_id id, turn_credential_type ct, u08bits *uname, u08bits *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, u64bits ctxkey, int *postpone_reply);
-int check_new_allocation_quota(u08bits *username, u08bits *realm);
-void release_allocation_quota(u08bits *username, u08bits *realm);
+u08bits *start_user_check(turnserver_id id, turn_credential_type ct, int in_oauth, int *out_oauth, u08bits *uname, u08bits *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, u64bits ctxkey, int *postpone_reply);
+int check_new_allocation_quota(u08bits *username, int oauth, u08bits *realm);
+void release_allocation_quota(u08bits *username, int oauth, u08bits *realm);
 
 /////////// Handle user DB /////////////////
 
+#if defined(DB_TEST)
+	void run_db_test(void);
+#endif
+
 void read_userdb_file(int to_print);
 void auth_ping(redis_context_handle rch);
 void reread_realms(void);

+ 180 - 0
src/apps/rfc5769/rfc5769check.c

@@ -39,6 +39,178 @@
 #include "apputils.h"
 #include "stun_buffer.h"
 
+//////////// OAUTH //////////////////
+
+static const char* shas[]={"SHA1",
+#if !defined(OPENSSL_NO_SHA256) && defined(SHA256_DIGEST_LENGTH)
+			   "SHA256",
+#endif
+			   NULL};
+static const char* encs[]={"AES-256-CBC","AES-128-CBC",
+#if !defined(TURN_NO_GCM)
+		"AEAD_AES_128_GCM", "AEAD_AES_256_GCM",
+#endif
+		NULL};
+static const char* hmacs[]={"HMAC-SHA-1",
+#if !defined(OPENSSL_NO_SHA256) && defined(SHA256_DIGEST_LENGTH)
+			    "HMAC-SHA-256","HMAC-SHA-256-128",
+#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;i<len;++i) {
+    printf("\\x%x",(unsigned int)f[i]);
+  }
+  printf("\n<<==field %s\n",name);
+}
+
+static int check_oauth(void) {
+
+	const char server_name[33] = "blackdow.carleon.gov";
+
+	size_t i_hmacs,i_shas,i_encs;
+
+	const char long_term_password[33] = "HGkj32KJGiuy098sdfaqbNjOiaz71923";
+
+	size_t ltp_output_length=0;
+
+	const char* base64encoded_ltp = base64_encode((const unsigned char *)long_term_password,
+						      strlen(long_term_password),
+						      &ltp_output_length);
+
+	const char mac_key[33] = "ZksjpweoixXmvn67534m";
+	const size_t mac_key_length=strlen(mac_key);
+	const uint64_t token_timestamp = (uint64_t)(92470300704768LL);
+	const uint32_t token_lifetime = 3600;
+
+	const char kid[33] = "2783466234";
+	const turn_time_t key_timestamp = 1234567890;
+	const turn_time_t key_lifetime = 3600;
+
+	const char aead_nonce[OAUTH_AEAD_NONCE_SIZE+1] = "h4j3k2l2n4b5";
+
+	for (i_hmacs = 0; hmacs[i_hmacs]; ++i_hmacs) {
+
+		for (i_shas = 0; shas[i_shas]; ++i_shas) {
+
+			for (i_encs = 0; encs[i_encs]; ++i_encs) {
+
+				printf("oauth token %s:%s:%s:",hmacs[i_hmacs],shas[i_shas],encs[i_encs]);
+
+				if(print_extra)
+					printf("\n");
+
+				oauth_token ot;
+				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;
+
+				oauth_token dot;
+				ns_bzero((&dot),sizeof(dot));
+				oauth_key key;
+				ns_bzero(&key,sizeof(key));
+
+				{
+					oauth_key_data okd;
+					ns_bzero(&okd,sizeof(okd));
+
+					{
+					  oauth_key_data_raw okdr;
+					  ns_bzero(&okdr,sizeof(okdr));
+
+						STRCPY(okdr.kid,kid);
+						STRCPY(okdr.ikm_key,base64encoded_ltp);
+						STRCPY(okdr.as_rs_alg, encs[i_encs]);
+						STRCPY(okdr.auth_alg, hmacs[i_hmacs]);
+						STRCPY(okdr.hkdf_hash_func, shas[i_shas]);
+						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;
+						}
+					}
+				}
+
+				if(print_extra) {
+					print_field5769("AS-RS",key.as_rs_key,key.as_rs_key_size);
+					print_field5769("AUTH",key.auth_key,key.auth_key_size);
+				}
+
+				{
+					encoded_oauth_token etoken;
+					ns_bzero(&etoken,sizeof(etoken));
+
+					if (encode_oauth_token((const u08bits *) server_name, &etoken,
+							&key, &ot, (const u08bits*)aead_nonce) < 0) {
+						fprintf(stderr, "%s: cannot encode oauth token\n",
+								__FUNCTION__);
+						return -1;
+					}
+
+					if(print_extra) {
+						print_field5769("encoded token",etoken.token,etoken.size);
+					}
+
+					if (decode_oauth_token((const u08bits *) server_name, &etoken,
+							&key, &dot) < 0) {
+						fprintf(stderr, "%s: cannot decode oauth token\n",
+								__FUNCTION__);
+						return -1;
+					}
+				}
+
+				if (strcmp((char*) ot.enc_block.mac_key,
+						(char*) dot.enc_block.mac_key)) {
+					fprintf(stderr, "%s: wrong mac key: %s, must be %s\n",
+							__FUNCTION__, (char*) dot.enc_block.mac_key,
+							(char*) ot.enc_block.mac_key);
+					return -1;
+				}
+
+				if (ot.enc_block.key_length != dot.enc_block.key_length) {
+					fprintf(stderr, "%s: wrong key length: %d, must be %d\n",
+							__FUNCTION__, (int) dot.enc_block.key_length,
+							(int) ot.enc_block.key_length);
+					return -1;
+				}
+				if (ot.enc_block.timestamp != dot.enc_block.timestamp) {
+					fprintf(stderr, "%s: wrong timestamp: %llu, must be %llu\n",
+							__FUNCTION__,
+							(unsigned long long) dot.enc_block.timestamp,
+							(unsigned long long) ot.enc_block.timestamp);
+					return -1;
+				}
+				if (ot.enc_block.lifetime != dot.enc_block.lifetime) {
+					fprintf(stderr, "%s: wrong lifetime: %lu, must be %lu\n",
+							__FUNCTION__,
+							(unsigned long) dot.enc_block.lifetime,
+							(unsigned long) ot.enc_block.lifetime);
+					return -1;
+				}
+
+				printf("OK\n");
+			}
+		}
+	}
+
+	return 0;
+}
+
 //////////////////////////////////////////////////
 
 static SHATYPE shatype = SHATYPE_SHA1;
@@ -50,6 +222,9 @@ int main(int argc, const char **argv)
 	UNUSED_ARG(argc);
 	UNUSED_ARG(argv);
 
+	if(argc>1)
+		print_extra = 1;
+
 	set_logfile("stdout");
 	set_system_parameters(0);
 
@@ -401,5 +576,10 @@ int main(int argc, const char **argv)
 		}
 	}
 
+	{
+		if(check_oauth()<0)
+			exit(-1);
+	}
+
 	return 0;
 }

+ 42 - 1
src/apps/uclient/mainuclient.c

@@ -46,6 +46,7 @@
 
 #include <openssl/ssl.h>
 #include <openssl/opensslv.h>
+#include <openssl/rand.h>
 
 /////////////// extern definitions /////////////////////
 
@@ -97,6 +98,14 @@ band_limit_t bps = 0;
 
 int dual_allocation = 0;
 
+int oauth = 0;
+oauth_key okey_array[2];
+
+static oauth_key_data_raw okdr_array[2] = {
+		{"north","Y2FybGVvbg==",0,0,"SHA-256","AES-256-CBC","","HMAC-SHA-256-128",""},
+		{"oldempire","YXVsY3Vz",0,0,"SHA-256","AEAD-AES-256-GCM","","",""}
+};
+
 //////////////// local definitions /////////////////
 
 static char Usage[] =
@@ -130,6 +139,7 @@ static char Usage[] =
   "	-G	Generate extra requests (create permissions, channel bind).\n"
   " -B  Random disconnect after a few initial packets.\n"
   " -Z  Dual allocation.\n"
+  " -J	Use oAuth with default test key kid='north'.\n"
   "Options:\n"
   "	-l	Message length (Default: 100 Bytes).\n"
   "	-i	Certificate file (for secure connections only, optional).\n"
@@ -204,8 +214,35 @@ int main(int argc, char **argv)
 
 	ns_bzero(local_addr, sizeof(local_addr));
 
-	while ((c = getopt(argc, argv, "a:d:p:l:n:L:m:e:r:u:w:i:k:z:W:C:E:F:o:ZvsyhcxXgtTSAPDNOUHMRIGB")) != -1) {
+	while ((c = getopt(argc, argv, "a:d:p:l:n:L:m:e:r:u:w:i:k:z:W:C:E:F:o:ZvsyhcxXgtTSAPDNOUHMRIGBJ")) != -1) {
 		switch (c){
+		case 'J': {
+
+			oauth = 1;
+
+			if(use_short_term) {
+				fprintf(stderr,"Short-term mechanism cannot be used together with oAuth.\n");
+				exit(-1);
+			}
+
+			oauth_key_data okd_array[2];
+			convert_oauth_key_data_raw(&okdr_array[0], &okd_array[0]);
+			convert_oauth_key_data_raw(&okdr_array[1], &okd_array[1]);
+
+			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);
+			}
+		}
+			break;
 		case 'a':
 			bps = (band_limit_t)atol(optarg);
 			break;
@@ -262,6 +299,10 @@ int main(int argc, char **argv)
 			dual_allocation = 1;
 			break;
 		case 'A':
+			if(oauth) {
+				fprintf(stderr,"Short-term mechanism cannot be used together with oAuth.\n");
+				exit(-1);
+			}
 			use_short_term = 1;
 			break;
 		case 'u':

+ 6 - 0
src/apps/uclient/session.h

@@ -80,6 +80,12 @@ typedef struct {
   int broken;
   u08bits nonce[STUN_MAX_NONCE_SIZE+1];
   u08bits realm[STUN_MAX_REALM_SIZE+1];
+  /* oAuth */
+  int oauth;
+  u08bits server_name[STUN_MAX_SERVER_NAME_SIZE+1];
+  hmackey_t key;
+  int key_set;
+  int cok;
   /* RFC 6062 */
   app_tcp_conn_info **tcp_conn;
   size_t tcp_conn_number;

+ 30 - 42
src/apps/uclient/startuclient.c

@@ -360,9 +360,9 @@ static int clnet_allocate(int verbose,
 		}
 
 		if(!dos)
-			stun_set_allocate_request(&message, 800, af4, af6, relay_transport, mobility);
+			stun_set_allocate_request(&message, UCLIENT_SESSION_LIFETIME, af4, af6, relay_transport, mobility);
 		else
-			stun_set_allocate_request(&message, 300, af4, af6, relay_transport, mobility);
+			stun_set_allocate_request(&message, UCLIENT_SESSION_LIFETIME/3, af4, af6, relay_transport, mobility);
 
 		if(bps)
 			stun_attr_add_bandwidth_str(message.buf, (size_t*)(&(message.len)), bps);
@@ -433,13 +433,8 @@ static int clnet_allocate(int verbose,
 						allocate_finished = 1;
 
 						if(clnet_info->nonce[0] || use_short_term) {
-							SHATYPE sht = clnet_info->shatype;
-							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 allocate message received from server\n");
+							if(check_integrity(clnet_info, &message)<0)
 								return -1;
-							}
 						}
 
 						if (verbose) {
@@ -500,13 +495,14 @@ static int clnet_allocate(int verbose,
 						current_reservation_token = rtv;
 						if (verbose)
 							TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,
-									"%s: rtv=%llu\n", __FUNCTION__, rtv);
+								      "%s: rtv=%llu\n", __FUNCTION__, (long long unsigned int)rtv);
 
 						read_mobility_ticket(clnet_info, &message);
 
 					} else if (stun_is_challenge_response_str(message.buf, (size_t)message.len,
 									&err_code,err_msg,sizeof(err_msg),
-									clnet_info->realm,clnet_info->nonce)) {
+									clnet_info->realm,clnet_info->nonce,
+									clnet_info->server_name, &(clnet_info->oauth))) {
 						if(err_code == SHA_TOO_WEAK_ERROR_CODE && (clnet_info->shatype == SHATYPE_SHA1)) {
 							clnet_info->shatype = SHATYPE_SHA256;
 							recalculate_restapi_hmac();
@@ -524,13 +520,8 @@ static int clnet_allocate(int verbose,
 						if(err_code == 300) {
 
 							if(clnet_info->nonce[0] || use_short_term) {
-								SHATYPE sht = clnet_info->shatype;
-								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 allocate message received from server\n");
+								if(check_integrity(clnet_info, &message)<0)
 									return -1;
-								}
 							}
 
 							ioa_addr alternate_server;
@@ -551,7 +542,7 @@ static int clnet_allocate(int verbose,
 							return -1;
 						} else {
 							TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,
-									"trying allocate again...\n", err_code);
+									"trying allocate again %d...\n", err_code);
 							sleep(1);
 							reopen_socket = 1;
 						}
@@ -642,9 +633,8 @@ static int clnet_allocate(int verbose,
 
 			stun_buffer message;
 			stun_init_request(STUN_METHOD_REFRESH, &message);
-			uint32_t lt = htonl(600);
-			stun_attr_add(&message, STUN_ATTRIBUTE_LIFETIME, (const char*) &lt,
-					4);
+			uint32_t lt = htonl(UCLIENT_SESSION_LIFETIME);
+			stun_attr_add(&message, STUN_ATTRIBUTE_LIFETIME, (const char*) &lt, 4);
 
 			if(clnet_info->s_mobile_id[0]) {
 				stun_attr_add(&message, STUN_ATTRIBUTE_MOBILITY_TICKET, (const char*)clnet_info->s_mobile_id, strlen(clnet_info->s_mobile_id));
@@ -665,6 +655,11 @@ static int clnet_allocate(int 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, &message, 0,0);
+					}
 				} else {
 					perror("send");
 					exit(1);
@@ -682,6 +677,10 @@ static int clnet_allocate(int verbose,
 
 				int len = recv_buffer(clnet_info, &message, 1, 0);
 
+				if(clnet_info->s_mobile_id[0]) {
+					len = recv_buffer(clnet_info, &message, 1, 0);
+				}
+
 				if (len > 0) {
 					if (verbose) {
 						TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,
@@ -698,7 +697,8 @@ static int clnet_allocate(int verbose,
 						}
 					} else if (stun_is_challenge_response_str(message.buf, (size_t)message.len,
 										&err_code,err_msg,sizeof(err_msg),
-										clnet_info->realm,clnet_info->nonce)) {
+										clnet_info->realm,clnet_info->nonce,
+										clnet_info->server_name, &(clnet_info->oauth))) {
 						if(err_code == SHA_TOO_WEAK_ERROR_CODE && (clnet_info->shatype == SHATYPE_SHA1)) {
 							clnet_info->shatype = SHATYPE_SHA256;
 							recalculate_restapi_hmac();
@@ -786,13 +786,8 @@ static int turn_channel_bind(int verbose, uint16_t *chn,
 					cb_received = 1;
 
 					if(clnet_info->nonce[0] || use_short_term) {
-						SHATYPE sht = clnet_info->shatype;
-						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 channel bind message received from server\n");
+						if(check_integrity(clnet_info, &message)<0)
 							return -1;
-						}
 					}
 
 					if (verbose) {
@@ -801,7 +796,8 @@ static int turn_channel_bind(int verbose, uint16_t *chn,
 					}
 				} else if (stun_is_challenge_response_str(message.buf, (size_t)message.len,
 										&err_code,err_msg,sizeof(err_msg),
-										clnet_info->realm,clnet_info->nonce)) {
+										clnet_info->realm,clnet_info->nonce,
+										clnet_info->server_name, &(clnet_info->oauth))) {
 					if(err_code == SHA_TOO_WEAK_ERROR_CODE && (clnet_info->shatype == SHATYPE_SHA1)) {
 						clnet_info->shatype = SHATYPE_SHA256;
 						recalculate_restapi_hmac();
@@ -900,13 +896,8 @@ static int turn_create_permission(int verbose, app_ur_conn_info *clnet_info,
 					cp_received = 1;
 
 					if(clnet_info->nonce[0] || use_short_term) {
-						SHATYPE sht = clnet_info->shatype;
-						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 create permission message received from server\n");
+						if(check_integrity(clnet_info, &message)<0)
 							return -1;
-						}
 					}
 
 					if (verbose) {
@@ -914,7 +905,8 @@ static int turn_create_permission(int verbose, app_ur_conn_info *clnet_info,
 					}
 				} else if (stun_is_challenge_response_str(message.buf, (size_t)message.len,
 									&err_code,err_msg,sizeof(err_msg),
-									clnet_info->realm,clnet_info->nonce)) {
+									clnet_info->realm,clnet_info->nonce,
+									clnet_info->server_name, &(clnet_info->oauth))) {
 					if(err_code == SHA_TOO_WEAK_ERROR_CODE && (clnet_info->shatype == SHATYPE_SHA1)) {
 						clnet_info->shatype = SHATYPE_SHA256;
 						recalculate_restapi_hmac();
@@ -1486,13 +1478,8 @@ static int turn_tcp_connection_bind(int verbose, app_ur_conn_info *clnet_info, a
 				if (stun_is_success_response(&message)) {
 
 					if(clnet_info->nonce[0] || use_short_term) {
-						SHATYPE sht = clnet_info->shatype;
-						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 connect bind message received from server\n");
+						if(check_integrity(clnet_info, &message)<0)
 							return -1;
-						}
 					}
 
 					if(stun_get_method(&message)!=STUN_METHOD_CONNECTION_BIND)
@@ -1504,7 +1491,8 @@ static int turn_tcp_connection_bind(int verbose, app_ur_conn_info *clnet_info, a
 					atc->tcp_data_bound = 1;
 				} else if (stun_is_challenge_response_str(message.buf, (size_t)message.len,
 										&err_code,err_msg,sizeof(err_msg),
-										clnet_info->realm,clnet_info->nonce)) {
+										clnet_info->realm,clnet_info->nonce,
+										clnet_info->server_name, &(clnet_info->oauth))) {
 					if(err_code == SHA_TOO_WEAK_ERROR_CODE && (clnet_info->shatype == SHATYPE_SHA1)) {
 						clnet_info->shatype = SHATYPE_SHA256;
 						recalculate_restapi_hmac();

+ 92 - 15
src/apps/uclient/uclient.c

@@ -38,6 +38,7 @@
 #include <time.h>
 
 #include <openssl/err.h>
+#include <openssl/rand.h>
 
 static int verbose_packets=0;
 
@@ -510,13 +511,9 @@ static int client_read(app_ur_session *elem, int is_tcp_data, app_tcp_conn_info
 		} else if (stun_is_indication(&(elem->in_buffer))) {
 
 			if(use_short_term) {
-				SHATYPE sht = elem->pinfo.shatype;
-				if(stun_check_message_integrity_str(get_turn_credentials_type(),
-							elem->in_buffer.buf, (size_t)(elem->in_buffer.len), g_uname,
-							elem->pinfo.realm, g_upwd, sht)<1) {
-					TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Wrong integrity in indication message 0x%x received from server\n",(unsigned int)stun_get_method(&(elem->in_buffer)));
+
+				if(check_integrity(&(elem->pinfo), &(elem->in_buffer))<0)
 					return -1;
-				}
 			}
 
 			uint16_t method = stun_get_method(&elem->in_buffer);
@@ -569,13 +566,8 @@ static int client_read(app_ur_session *elem, int is_tcp_data, app_tcp_conn_info
 		} else if (stun_is_success_response(&(elem->in_buffer))) {
 
 			if(elem->pinfo.nonce[0] || use_short_term) {
-				SHATYPE sht = elem->pinfo.shatype;
-				if(stun_check_message_integrity_str(get_turn_credentials_type(),
-								elem->in_buffer.buf, (size_t)(elem->in_buffer.len), g_uname,
-								elem->pinfo.realm, g_upwd, sht)<1) {
-					TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Wrong integrity in success message 0x%x received from server\n",(unsigned int)stun_get_method(&(elem->in_buffer)));
+				if(check_integrity(&(elem->pinfo), &(elem->in_buffer))<0)
 					return -1;
-				}
 			}
 
 			if(is_TCP_relay() && (stun_get_method(&(elem->in_buffer)) == STUN_METHOD_CONNECT)) {
@@ -595,7 +587,8 @@ static int client_read(app_ur_session *elem, int is_tcp_data, app_tcp_conn_info
 			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->realm,clnet_info->nonce,
+							clnet_info->server_name, &(clnet_info->oauth))) {
 			if(err_code == SHA_TOO_WEAK_ERROR_CODE && (elem->pinfo.shatype == SHATYPE_SHA1)) {
 				elem->pinfo.shatype = SHATYPE_SHA256;
 				recalculate_restapi_hmac();
@@ -1437,9 +1430,93 @@ int add_integrity(app_ur_conn_info *clnet_info, stun_buffer *message)
 			return -1;
 		}
 	} else if(clnet_info->nonce[0]) {
-		if(stun_attr_add_integrity_by_user_str(message->buf, (size_t*)&(message->len), g_uname,
+
+		if(oauth && clnet_info->oauth) {
+
+			u16bits method = stun_get_method_str(message->buf, message->len);
+
+			int cok = clnet_info->cok;
+
+			if(((method == STUN_METHOD_ALLOCATE) || (method == STUN_METHOD_REFRESH)) || !(clnet_info->key_set))
+			{
+
+				cok=(random())%2;
+				if(cok<0) cok=-cok;
+				clnet_info->cok = cok;
+				oauth_token otoken;
+				encoded_oauth_token etoken;
+				u08bits nonce[12];
+				RAND_bytes((unsigned char*)nonce,12);
+				long halflifetime = OAUTH_SESSION_LIFETIME/2;
+				long random_lifetime = 0;
+				while(!random_lifetime) {
+					random_lifetime = random();
+				}
+				if(random_lifetime<0) random_lifetime=-random_lifetime;
+				random_lifetime = random_lifetime % halflifetime;
+				otoken.enc_block.lifetime =  (uint32_t)(halflifetime + random_lifetime);
+				otoken.enc_block.timestamp = ((uint64_t)turn_time()) << 16;
+				if(shatype == SHATYPE_SHA256) {
+					otoken.enc_block.key_length = 32;
+				} else {
+					otoken.enc_block.key_length = 20;
+				}
+				RAND_bytes((unsigned char *)(otoken.enc_block.mac_key), otoken.enc_block.key_length);
+				if(encode_oauth_token(clnet_info->server_name, &etoken, &(okey_array[cok]), &otoken, nonce)<0) {
+					TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO," Cannot encode token\n");
+					return -1;
+				}
+				stun_attr_add_str(message->buf, (size_t*)&(message->len), STUN_ATTRIBUTE_OAUTH_ACCESS_TOKEN,
+					(const u08bits*)etoken.token, (int)etoken.size);
+
+				ns_bcopy(otoken.enc_block.mac_key,clnet_info->key,otoken.enc_block.key_length);
+				clnet_info->key_set = 1;
+			}
+
+			if(stun_attr_add_integrity_by_key_str(message->buf, (size_t*)&(message->len), (u08bits*)okey_array[cok].kid,
+					clnet_info->realm, clnet_info->key, clnet_info->nonce, clnet_info->shatype)<0) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO," Cannot add integrity to the message\n");
+				return -1;
+			}
+
+			//self-test:
+			{
+				st_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, clnet_info->shatype, NULL)<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, clnet_info->shatype)<0) {
-			TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO," Cannot add integrity to the message\n");
+				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 = clnet_info->shatype;
+
+	if(oauth && clnet_info->oauth) {
+
+		st_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, NULL);
+
+	} 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;
 		}
 	}

+ 7 - 0
src/apps/uclient/uclient.h

@@ -86,6 +86,12 @@ extern int dual_allocation;
 
 extern char origin[STUN_MAX_ORIGIN_SIZE+1];
 
+extern int oauth;
+extern oauth_key okey_array[2];
+
+#define UCLIENT_SESSION_LIFETIME (777)
+#define OAUTH_SESSION_LIFETIME (555)
+
 #define is_TCP_relay() (relay_transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE)
 
 void start_mclient(const char *remote_address, int port,
@@ -100,6 +106,7 @@ void client_input_handler(evutil_socket_t fd, short what, void* arg);
 turn_credential_type get_turn_credentials_type(void);
 
 int add_integrity(app_ur_conn_info *clnet_info, stun_buffer *message);
+int check_integrity(app_ur_conn_info *clnet_info, stun_buffer *message);
 
 void recalculate_restapi_hmac(void);
 

+ 4 - 3
src/client++/TurnMsgLib.h

@@ -665,8 +665,9 @@ public:
 	 */
 	static bool isChallengeResponse(const u08bits* buf, size_t sz,
 					int &err_code, u08bits *err_msg, size_t err_msg_size,
-					u08bits *realm, u08bits *nonce) {
-		return stun_is_challenge_response_str(buf, sz, &err_code, err_msg, err_msg_size, realm, nonce);
+					u08bits *realm, u08bits *nonce,
+					u08bits *server_name, int *oauth) {
+		return stun_is_challenge_response_str(buf, sz, &err_code, err_msg, err_msg_size, realm, nonce, server_name, oauth);
 	}
 
 	/**
@@ -970,7 +971,7 @@ public:
 			size_t err_msg_size=sizeof(err_msg);
 			u08bits srealm[0xFFFF];
 			u08bits snonce[0xFFFF];
-			ret = stun_is_challenge_response_str(_buffer, _sz, &err_code, err_msg, err_msg_size, srealm, snonce);
+			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;

+ 62 - 6
src/client/ns_turn_ioaddr.c

@@ -29,6 +29,8 @@
  */
 
 #include "ns_turn_ioaddr.h"
+#include <netdb.h>
+#include <string.h>
 
 //////////////////////////////////////////////////////////////
 
@@ -202,7 +204,61 @@ int make_ioa_addr(const u08bits* saddr, int port, ioa_addr *addr) {
 #endif
     addr->s6.sin6_port = nswap16(port);
   } else {
-    return -1;
+    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((const char*)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(!found && addr_result) {
+
+    	if(addr_result->ai_family == family) {
+    		ns_bcopy(addr_result->ai_addr, addr, addr_result->ai_addrlen);
+    		if (addr_result->ai_family == AF_INET) {
+    			addr->s4.sin_port = nswap16(port);
+#if defined(TURN_HAS_SIN_LEN) /* tested when configured */
+    			addr->s4.sin_len = sizeof(struct sockaddr_in);
+#endif
+    		} else if (addr_result->ai_family == AF_INET6) {
+    			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
+    		} else {
+    			continue;
+    		}
+    		found = 1;
+    	}
+
+    	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;
@@ -257,7 +313,7 @@ int make_ioa_addr_from_full_string(const u08bits* saddr, int default_port, ioa_a
 			port = default_port;
 		ret = make_ioa_addr((u08bits*)sa,port,addr);
 	}
-	turn_free(s,strlen(s)+1);
+	free(s);
 	return ret;
 }
 
@@ -437,10 +493,10 @@ static size_t msz = 0;
 void ioa_addr_add_mapping(ioa_addr *apub, ioa_addr *apriv)
 {
 	size_t new_size = msz + sizeof(ioa_addr*);
-	public_addrs = (ioa_addr**)turn_realloc(public_addrs, msz, new_size);
-	private_addrs = (ioa_addr**)turn_realloc(private_addrs, msz, new_size);
-	public_addrs[mcount]=(ioa_addr*)turn_malloc(sizeof(ioa_addr));
-	private_addrs[mcount]=(ioa_addr*)turn_malloc(sizeof(ioa_addr));
+	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;

+ 792 - 25
src/client/ns_turn_msg.c

@@ -39,6 +39,8 @@
 #include <openssl/err.h>
 #include <openssl/rand.h>
 
+///////////
+
 #include <stdlib.h>
 
 ///////////
@@ -85,7 +87,7 @@ int stun_method_str(u16bits method, char *smethod)
 	};
 
 	if(smethod) {
-		STRCPY(smethod,s);
+		ns_bcopy(s,smethod,strlen(s)+1);
 	}
 
 	return ret;
@@ -101,7 +103,7 @@ long turn_random(void)
 
 void turn_random32_size(u32bits *ar, size_t sz)
 {
-	if(!RAND_bytes((unsigned char *)ar, sz<<2)<0) {
+	if(!RAND_bytes((unsigned char *)ar, sz<<2)) {
 		size_t i;
 		for(i=0;i<sz;++i) {
 			ar[i] = (u32bits)random();
@@ -114,14 +116,16 @@ int stun_calculate_hmac(const u08bits *buf, size_t len, const u08bits *key, size
 	ERR_clear_error();
 	UNUSED_ARG(shatype);
 
-#if !defined(OPENSSL_NO_SHA256) && defined(SSL_TXT_SHA256)
 	if(shatype == SHATYPE_SHA256) {
+#if !defined(OPENSSL_NO_SHA256) && defined(SHA256_DIGEST_LENGTH)
 	  if (!HMAC(EVP_sha256(), key, keylen, buf, len, hmac, hmac_len)) {
 	    return -1;
 	  }
-	} else
+#else
+	  fprintf(stderr,"SHA256 is not supported\n");
+	  return -1;
 #endif
-
+	} else
 	  if (!HMAC(EVP_sha1(), key, keylen, buf, len, hmac, hmac_len)) {
 	    return -1;
 	  }
@@ -139,7 +143,7 @@ int stun_produce_integrity_key_str(u08bits *uname, u08bits *realm, u08bits *upwd
 	size_t plen = strlen((s08bits*)upwd);
 	size_t sz = ulen+1+rlen+1+plen+1+10;
 	size_t strl = ulen+1+rlen+1+plen;
-	u08bits *str = (u08bits*)malloc(sz+1);
+	u08bits *str = (u08bits*)turn_malloc(sz+1);
 
 	strncpy((s08bits*)str,(s08bits*)uname,sz);
 	str[ulen]=':';
@@ -148,23 +152,26 @@ int stun_produce_integrity_key_str(u08bits *uname, u08bits *realm, u08bits *upwd
 	strncpy((s08bits*)str+ulen+1+rlen+1,(s08bits*)upwd,sz-ulen-1-rlen-1);
 	str[strl]=0;
 
-#if !defined(OPENSSL_NO_SHA256) && defined(SSL_TXT_SHA256)
 	if(shatype == SHATYPE_SHA256) {
+#if !defined(OPENSSL_NO_SHA256) && defined(SHA256_DIGEST_LENGTH)
 		unsigned int keylen = 0;
 		EVP_MD_CTX ctx;
 		EVP_DigestInit(&ctx,EVP_sha256());
 		EVP_DigestUpdate(&ctx,str,strl);
 		EVP_DigestFinal(&ctx,key,&keylen);
-	} else
+		EVP_MD_CTX_cleanup(&ctx);
+#else
+		fprintf(stderr,"SHA256 is not supported\n");
+		return -1;
 #endif
-	{
+	} else {
 		MD5_CTX ctx;
 		MD5_Init(&ctx);
 		MD5_Update(&ctx,str,strl);
 		MD5_Final(key,&ctx);
 	}
 
-	free(str);
+	turn_free(str,sz+1);
 
 	return 0;
 }
@@ -325,7 +332,7 @@ int stun_is_error_response_str(const u08bits* buf, size_t len, int *err_code, u0
 }
 
 int stun_is_challenge_response_str(const u08bits* buf, size_t len, int *err_code, u08bits *err_msg, size_t err_msg_size,
-				u08bits *realm, u08bits *nonce)
+				u08bits *realm, u08bits *nonce, u08bits *server_name, int *oauth)
 {
 	int ret = stun_is_error_response_str(buf, len, err_code, err_msg, err_msg_size);
 
@@ -333,11 +340,31 @@ int stun_is_challenge_response_str(const u08bits* buf, size_t len, int *err_code
 
 		stun_attr_ref sar = stun_attr_get_first_by_type_str(buf,len,STUN_ATTRIBUTE_REALM);
 		if(sar) {
+
+			int found_oauth = 0;
+
 			const u08bits *value = stun_attr_get_value(sar);
 			if(value) {
 				size_t vlen = (size_t)stun_attr_get_len(sar);
 				ns_bcopy(value,realm,vlen);
 				realm[vlen]=0;
+
+				{
+					stun_attr_ref sar = stun_attr_get_first_by_type_str(buf,len,STUN_ATTRIBUTE_THIRD_PARTY_AUTHORIZATION);
+					if(sar) {
+						const u08bits *value = stun_attr_get_value(sar);
+						if(value) {
+							size_t vlen = (size_t)stun_attr_get_len(sar);
+							if(vlen>0) {
+								if(server_name) {
+									ns_bcopy(value,server_name,vlen);
+								}
+								found_oauth = 1;
+							}
+						}
+					}
+				}
+
 				sar = stun_attr_get_first_by_type_str(buf,len,STUN_ATTRIBUTE_NONCE);
 				if(sar) {
 					value = stun_attr_get_value(sar);
@@ -345,6 +372,9 @@ int stun_is_challenge_response_str(const u08bits* buf, size_t len, int *err_code
 						vlen = (size_t)stun_attr_get_len(sar);
 						ns_bcopy(value,nonce,vlen);
 						nonce[vlen]=0;
+						if(oauth) {
+							*oauth = found_oauth;
+						}
 						return 1;
 					}
 				}
@@ -836,12 +866,14 @@ int stun_set_binding_response_str(u08bits* buf, size_t *len, stun_tid* tid,
 		} else {
 			old_stun_init_success_response_str(STUN_METHOD_BINDING, buf, len, tid, cookie);
 		}
-		if(!old_stun) {
+		if(!old_stun && reflexive_addr) {
 			if (stun_attr_add_addr_str(buf, len, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, reflexive_addr) < 0)
 				return -1;
 		}
-		if (stun_attr_add_addr_str(buf, len, STUN_ATTRIBUTE_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 {
@@ -936,10 +968,16 @@ void stun_tid_generate_in_message_str(u08bits* buf, stun_tid* id) {
 
 /////////////////// TIME ////////////////////////////////////////////////////////
 
-u32bits stun_adjust_allocate_lifetime(u32bits lifetime) {
-  if(!lifetime) return STUN_DEFAULT_ALLOCATE_LIFETIME;
-  else if(lifetime<STUN_MIN_ALLOCATE_LIFETIME) return STUN_MIN_ALLOCATE_LIFETIME;
-  else if(lifetime>STUN_MAX_ALLOCATE_LIFETIME) return STUN_MAX_ALLOCATE_LIFETIME;
+turn_time_t stun_adjust_allocate_lifetime(turn_time_t lifetime, turn_time_t max_lifetime) {
+
+  if(!lifetime) lifetime = STUN_DEFAULT_ALLOCATE_LIFETIME;
+  else if(lifetime<STUN_MIN_ALLOCATE_LIFETIME) lifetime = STUN_MIN_ALLOCATE_LIFETIME;
+  else if(lifetime>STUN_MAX_ALLOCATE_LIFETIME) lifetime = STUN_MAX_ALLOCATE_LIFETIME;
+
+  if(max_lifetime && (max_lifetime < lifetime)) {
+  	lifetime = max_lifetime;
+  }
+
   return lifetime;
 }
 
@@ -1442,13 +1480,8 @@ int stun_attr_add_integrity_str(turn_credential_type ct, u08bits *buf, size_t *l
 	return 0;
 }
 
-int stun_attr_add_integrity_by_user_str(u08bits *buf, size_t *len, u08bits *uname, u08bits *realm, u08bits *upwd, u08bits *nonce, SHATYPE shatype)
+int stun_attr_add_integrity_by_key_str(u08bits *buf, size_t *len, u08bits *uname, u08bits *realm, hmackey_t key, u08bits *nonce, SHATYPE shatype)
 {
-	hmackey_t key;
-
-	if(stun_produce_integrity_key_str(uname, realm, upwd, key, shatype)<0)
-		return -1;
-
 	if(stun_attr_add_str(buf, len, STUN_ATTRIBUTE_USERNAME, uname, strlen((s08bits*)uname))<0)
 			return -1;
 
@@ -1462,6 +1495,16 @@ int stun_attr_add_integrity_by_user_str(u08bits *buf, size_t *len, u08bits *unam
 	return stun_attr_add_integrity_str(TURN_CREDENTIALS_LONG_TERM, buf, len, key, p, shatype);
 }
 
+int stun_attr_add_integrity_by_user_str(u08bits *buf, size_t *len, u08bits *uname, u08bits *realm, u08bits *upwd, u08bits *nonce, SHATYPE shatype)
+{
+	hmackey_t key;
+
+	if(stun_produce_integrity_key_str(uname, realm, upwd, key, shatype)<0)
+		return -1;
+
+	return stun_attr_add_integrity_by_key_str(buf, len, uname, realm, key, nonce, shatype);
+}
+
 int stun_attr_add_integrity_by_user_short_term_str(u08bits *buf, size_t *len, u08bits *uname, st_password_t pwd, SHATYPE shatype)
 {
 	if(stun_attr_add_str(buf, len, STUN_ATTRIBUTE_USERNAME, uname, strlen((s08bits*)uname))<0)
@@ -1543,7 +1586,7 @@ int stun_check_message_integrity_by_key_str(turn_credential_type ct, u08bits *bu
 	if(bcmp(old_hmac,new_hmac,shasize))
 		return 0;
 
-	return 1;
+	return +1;
 }
 
 /*
@@ -1630,4 +1673,728 @@ int stun_attr_add_padding_str(u08bits *buf, size_t *len, u16bits 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;
+	}
+}
+
+static size_t calculate_enc_key_length(ENC_ALG a)
+{
+	switch(a) {
+	case AES_128_CBC:
+	case AEAD_AES_128_GCM:
+		return 16;
+	default:
+		break;
+	};
+
+	return 32;
+}
+
+static size_t calculate_auth_key_length(AUTH_ALG a)
+{
+	switch(a) {
+	case AUTH_ALG_HMAC_SHA_1:
+		return 20;
+	case AUTH_ALG_HMAC_SHA_256_128:
+		return 32;
+	case AUTH_ALG_HMAC_SHA_256:
+		return 32;
+	default:
+		break;
+	};
+
+	return 32;
+}
+
+static size_t calculate_auth_output_length(AUTH_ALG a)
+{
+	switch(a) {
+	case AUTH_ALG_HMAC_SHA_1:
+		return 20;
+	case AUTH_ALG_HMAC_SHA_256_128:
+		return 16;
+	case AUTH_ALG_HMAC_SHA_256:
+		return 32;
+	default:
+		break;
+	};
+
+	return 32;
+}
+
+static int calculate_key(char *key, size_t key_size, char *new_key, size_t new_key_size, SHATYPE shatype,
+		char *err_msg, size_t err_msg_size)
+{
+	//Extract:
+	u08bits prk[128];
+	unsigned int prk_len = 0;
+	stun_calculate_hmac((const u08bits *)key, key_size, (const u08bits *)"", 0, prk, &prk_len, shatype);
+
+	//Expand:
+	u08bits buf[128];
+	buf[0]=1;
+	u08bits hmac[128];
+	unsigned int hmac_len = 0;
+	stun_calculate_hmac((const u08bits *)buf, 1, prk, prk_len, hmac, &hmac_len, shatype);
+	ns_bcopy(hmac,new_key,hmac_len);
+
+	//Check
+	if(new_key_size>hmac_len) {
+		ns_bcopy(hmac,buf,hmac_len);
+		buf[hmac_len]=2;
+		u08bits hmac1[128];
+		unsigned int hmac1_len = 0;
+		stun_calculate_hmac((const u08bits *)buf, hmac_len+1, prk, prk_len, hmac1, &hmac1_len, shatype);
+		ns_bcopy(hmac1,new_key+hmac_len,hmac1_len);
+		if(new_key_size > (hmac_len + hmac1_len)) {
+			if(err_msg) {
+				snprintf(err_msg,err_msg_size,"Wrong HKDF procedure (key sizes): output.sz=%lu, hmac(1)=%lu, hmac(2)=%lu",(unsigned long)new_key_size,(unsigned long)hmac_len,(unsigned long)hmac1_len);
+			}
+			OAUTH_ERROR("Wrong HKDF procedure (key sizes): output.sz=%lu, hmac(1)=%lu, hmac(2)=%lu",(unsigned long)new_key_size,(unsigned long)hmac_len,(unsigned long)hmac1_len);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+int convert_oauth_key_data(const oauth_key_data *oakd0, oauth_key *key, char *err_msg, size_t err_msg_size)
+{
+	if(oakd0 && key) {
+
+		oauth_key_data oakd_obj;
+		ns_bcopy(oakd0,&oakd_obj,sizeof(oauth_key_data));
+		oauth_key_data *oakd = &oakd_obj;
+
+		if(!(oakd->ikm_key_size)) {
+			if(!(oakd->as_rs_key_size)) {
+				if(err_msg) {
+					snprintf(err_msg,err_msg_size,"AS-RS key is not defined");
+				}
+				OAUTH_ERROR("AS-RS key is not defined\n");
+				return -1;
+			}
+			if(!(oakd->auth_key_size)) {
+				//AEAD ?
+			}
+		}
+
+		remove_spaces(oakd->kid);
+
+		remove_spaces(oakd->hkdf_hash_func);
+		remove_spaces(oakd->as_rs_alg);
+		remove_spaces(oakd->auth_alg);
+
+		normalize_algorithm(oakd->hkdf_hash_func);
+		normalize_algorithm(oakd->as_rs_alg);
+		normalize_algorithm(oakd->auth_alg);
+
+		if(!(oakd->kid[0])) {
+			if(err_msg) {
+				snprintf(err_msg,err_msg_size,"KID is not defined");
+			}
+			OAUTH_ERROR("KID is not defined\n");
+			return -1;
+		}
+
+		ns_bzero(key,sizeof(oauth_key));
+
+		STRCPY(key->kid,oakd->kid);
+
+		ns_bcopy(oakd->as_rs_key,key->as_rs_key,sizeof(key->as_rs_key));
+		key->as_rs_key_size = oakd->as_rs_key_size;
+		ns_bcopy(oakd->auth_key,key->auth_key,sizeof(key->auth_key));
+		key->auth_key_size = oakd->auth_key_size;
+		ns_bcopy(oakd->ikm_key,key->ikm_key,sizeof(key->ikm_key));
+		key->ikm_key_size = oakd->ikm_key_size;
+
+		key->timestamp = oakd->timestamp;
+		key->lifetime = oakd->lifetime;
+
+		if(!(key->timestamp)) key->timestamp = OAUTH_DEFAULT_TIMESTAMP;
+		if(!(key->lifetime)) key->lifetime = OAUTH_DEFAULT_LIFETIME;
+
+		key->hkdf_hash_func = SHATYPE_SHA256;
+		if(!strcmp(oakd->hkdf_hash_func,"SHA1") || !strcmp(oakd->hkdf_hash_func,"SHA-1")) {
+			key->hkdf_hash_func = SHATYPE_SHA1;
+		} else if(!strcmp(oakd->hkdf_hash_func,"SHA256") || !strcmp(oakd->hkdf_hash_func,"SHA-256")) {
+			key->hkdf_hash_func = SHATYPE_SHA256;
+		} else if(oakd->hkdf_hash_func[0]) {
+			if(err_msg) {
+				snprintf(err_msg,err_msg_size,"Wrong HKDF hash function algorithm: %s",oakd->hkdf_hash_func);
+			}
+			OAUTH_ERROR("Wrong HKDF hash function algorithm: %s\n",oakd->hkdf_hash_func);
+			return -1;
+		}
+
+		key->auth_alg = AUTH_ALG_DEFAULT;
+		if(!strcmp(oakd->auth_alg,"HMAC-SHA-1") || !strcmp(oakd->auth_alg,"HMAC-SHA1")) {
+			key->auth_alg = AUTH_ALG_HMAC_SHA_1;
+		} else if(!strcmp(oakd->auth_alg,"HMAC-SHA-256")) {
+			key->auth_alg = AUTH_ALG_HMAC_SHA_256;
+		} else if(!strcmp(oakd->auth_alg,"HMAC-SHA-256-128")) {
+			key->auth_alg = AUTH_ALG_HMAC_SHA_256_128;
+		} else if(oakd->auth_alg[0]) {
+			if(err_msg) {
+				snprintf(err_msg,err_msg_size,"Wrong oAuth token hash algorithm: %s (1)\n",oakd->auth_alg);
+			}
+			key->auth_alg = AUTH_ALG_ERROR;
+			OAUTH_ERROR("Wrong oAuth token hash algorithm: %s (2)\n",oakd->auth_alg);
+			return -1;
+		}
+
+		key->as_rs_alg = ENC_ALG_DEFAULT;
+		if(!strcmp(oakd->as_rs_alg,"AES-128-CBC")) {
+			key->as_rs_alg = AES_128_CBC;
+		} else if(!strcmp(oakd->as_rs_alg,"AES-256-CBC")) {
+			key->as_rs_alg = AES_256_CBC;
+		} else if(!strcmp(oakd->as_rs_alg,"AEAD-AES-128-GCM")) {
+			key->as_rs_alg = AEAD_AES_128_GCM;
+		} else if(!strcmp(oakd->as_rs_alg,"AEAD-AES-256-GCM")) {
+			key->as_rs_alg = AEAD_AES_256_GCM;
+		} else if(oakd->as_rs_alg[0]) {
+			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(key->auth_alg == AUTH_ALG_UNDEFINED) {
+			//AEAD
+			key->auth_key_size = 0;
+			key->auth_key[0] = 0;
+		} else if(!(key->auth_key_size)) {
+			key->auth_key_size = calculate_auth_key_length(key->auth_alg);
+			if(calculate_key(key->ikm_key,key->ikm_key_size,key->auth_key,key->auth_key_size,key->hkdf_hash_func,err_msg,err_msg_size)<0) {
+				return -1;
+			}
+		}
+
+		if(!(key->as_rs_key_size)) {
+			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,key->hkdf_hash_func,err_msg,err_msg_size)<0) {
+				return -1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static const EVP_CIPHER *get_cipher_type(ENC_ALG enc_alg)
+{
+	switch(enc_alg) {
+	case AES_256_CBC:
+		return EVP_aes_256_cbc();
+	case AES_128_CBC:
+		return EVP_aes_128_cbc();
+#if !defined(TURN_NO_GCM)
+	case AEAD_AES_128_GCM:
+		return EVP_aes_128_gcm();
+	case AEAD_AES_256_GCM:
+		return EVP_aes_256_gcm();
+#endif
+	default:
+		break;
+	}
+	OAUTH_ERROR("%s: Unsupported enc algorithm: %d\n",__FUNCTION__,(int)enc_alg);
+	return NULL;
+}
+
+static const EVP_MD *get_auth_type(AUTH_ALG aa)
+{
+	switch(aa) {
+	case AUTH_ALG_HMAC_SHA_1:
+		return EVP_sha1();
+#if !defined(OPENSSL_NO_SHA256) && defined(SHA256_DIGEST_LENGTH)
+	case AUTH_ALG_HMAC_SHA_256_128:
+	case AUTH_ALG_HMAC_SHA_256:
+		return EVP_sha256();
+#endif
+	default:
+		break;
+	};
+	OAUTH_ERROR("%s: Unknown auth algorithm: %d\n",__FUNCTION__,(int)aa);
+	return NULL;
+}
+
+static void update_hmac_len(AUTH_ALG aa, unsigned int *hmac_len)
+{
+	if(hmac_len) {
+		switch(aa) {
+		case AUTH_ALG_HMAC_SHA_256_128:
+			*hmac_len = 16;
+			break;
+		default:
+			break;
+		};
+	}
+}
+
+static 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<inl)&&(++cycle<128)) {
+		int tmp_outl=0;
+		unsigned char *ptr = NULL;
+		if(out)
+			ptr = out+out_len;
+		int ret = EVP_EncryptUpdate(ctx, ptr, &tmp_outl, in+out_len, inl-out_len);
+		out_len += tmp_outl;
+		if(ret<1)
+			return ret;
+	}
+	*outl = out_len;
+	return 1;
+}
+
+static int my_EVP_DecryptUpdate(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<inl)&&(++cycle<128)) {
+		int tmp_outl=0;
+		unsigned char *ptr = NULL;
+		if(out)
+			ptr = out+out_len;
+		int ret = EVP_DecryptUpdate(ctx, ptr, &tmp_outl, in+out_len, inl-out_len);
+		out_len += tmp_outl;
+		if(ret<1)
+			return ret;
+	}
+	*outl = out_len;
+	return 1;
+}
+
+void print_field(const char* name, const unsigned char* f, size_t len);
+void print_field(const char* name, const unsigned char* f, size_t len) {
+	printf("\nfield %s==>>\n",name);
+	size_t i;
+	for(i = 0;i<len;++i) {
+		printf("<0x%x>",(unsigned int)f[i]);
+	}
+	printf("\n<<==field %s\n",name);
+}
+
+static int encode_oauth_token_normal(const u08bits *server_name, encoded_oauth_token *etoken, const oauth_key *key, const oauth_token *dtoken)
+{
+	if(server_name && etoken && key && dtoken && (dtoken->enc_block.key_length<=128)) {
+
+		unsigned char orig_field[MAX_ENCODED_OAUTH_TOKEN_SIZE];
+		ns_bzero(orig_field,sizeof(orig_field));
+
+		size_t len = 0;
+		*((uint16_t*)(orig_field+len)) = nswap16(dtoken->enc_block.key_length);
+		len +=2;
+
+		ns_bcopy(dtoken->enc_block.mac_key,orig_field+len,dtoken->enc_block.key_length);
+		len += dtoken->enc_block.key_length;
+
+		*((uint64_t*)(orig_field+len)) = nswap64(dtoken->enc_block.timestamp);
+		len += 8;
+
+		*((uint32_t*)(orig_field+len)) = nswap32(dtoken->enc_block.lifetime);
+		len += 4;
+
+		const EVP_CIPHER * cipher = get_cipher_type(key->as_rs_alg);
+		if(!cipher)
+			return -1;
+
+		unsigned char *encoded_field = (unsigned char*)etoken->token;
+
+		EVP_CIPHER_CTX ctx;
+		EVP_CIPHER_CTX_init(&ctx);
+		EVP_EncryptInit_ex(&ctx, cipher, NULL, (const unsigned char *)key->as_rs_key, NULL);
+		EVP_CIPHER_CTX_set_padding(&ctx,1);
+		int outl=0;
+		my_EVP_EncryptUpdate(&ctx, encoded_field, &outl, orig_field, (int)len);
+		if(outl % OAUTH_ENC_ALG_BLOCK_SIZE) {
+			int tmp_outl = 0;
+			EVP_EncryptFinal_ex(&ctx, encoded_field + outl, &tmp_outl);
+			outl += tmp_outl;
+		}
+
+		EVP_CIPHER_CTX_cleanup(&ctx);
+
+		size_t sn_len = strlen((const char*)server_name);
+		ns_bcopy(server_name,encoded_field+outl,sn_len);
+		outl += sn_len;
+
+		const EVP_MD *md = get_auth_type(key->auth_alg);
+		if(!md)
+			return -1;
+
+		unsigned int hmac_len = EVP_MD_size(md);
+		if (!HMAC(md, key->auth_key, key->auth_key_size, encoded_field, outl, encoded_field + outl, &hmac_len)) {
+		    return -1;
+		}
+
+		update_hmac_len(key->auth_alg, &hmac_len);
+
+		ns_bcopy(encoded_field + outl, encoded_field + outl - sn_len, hmac_len);
+		outl -= sn_len;
+		outl += hmac_len; //encoded+hmac
+
+		etoken->size = outl;
+
+		return 0;
+	}
+	return -1;
+}
+
+static int decode_oauth_token_normal(const u08bits *server_name, const encoded_oauth_token *etoken, const oauth_key *key, oauth_token *dtoken)
+{
+	if(server_name && etoken && key && dtoken) {
+
+		size_t mac_size = calculate_auth_output_length(key->auth_alg);
+		size_t min_encoded_field_size = 2+4+8+1;
+		if(etoken->size < mac_size+min_encoded_field_size) {
+			OAUTH_ERROR("%s: token size too small: %d, mac_size=%d, min_encoded_field_size=%d\n",__FUNCTION__,(int)etoken->size,(int)mac_size,(int)min_encoded_field_size);
+			return -1;
+		}
+
+		const unsigned char* encoded_field = (const unsigned char*)etoken->token;
+		unsigned int encoded_field_size = (unsigned int)etoken->size-mac_size;
+		const unsigned char* mac = ((const unsigned char*)etoken->token) + etoken->size - mac_size;
+
+		{
+			const EVP_MD *md = get_auth_type(key->auth_alg);
+			if(!md)
+				return -1;
+       		unsigned int hmac_len = EVP_MD_size(md);
+       		update_hmac_len(key->auth_alg,&hmac_len);
+       		if(hmac_len != mac_size) {
+       			OAUTH_ERROR("%s: mac size is wrong: %d, must be %d\n",__FUNCTION__,(int)mac_size,(int)hmac_len);
+       			return -1;
+       		}
+       		unsigned char efield[MAX_ENCODED_OAUTH_TOKEN_SIZE];
+       		unsigned char check_mac[MAXSHASIZE];
+       		ns_bcopy(encoded_field,efield,encoded_field_size);
+       		size_t sn_len = strlen((const char*)server_name);
+       		ns_bcopy(server_name,efield+encoded_field_size,sn_len);
+		    if (!HMAC(md, key->auth_key, key->auth_key_size, efield, encoded_field_size+sn_len, check_mac, &hmac_len)) {
+		    	return -1;
+		    }
+
+		    if(ns_bcmp(check_mac,mac,mac_size)) {
+		    	OAUTH_ERROR("%s: token integrity check failed\n",__FUNCTION__);
+		    	return -1;
+		    }
+		}
+
+		unsigned char decoded_field[MAX_ENCODED_OAUTH_TOKEN_SIZE];
+
+		const EVP_CIPHER * cipher = get_cipher_type(key->as_rs_alg);
+		if(!cipher)
+			return -1;
+
+		EVP_CIPHER_CTX ctx;
+		EVP_CIPHER_CTX_init(&ctx);
+		EVP_DecryptInit_ex(&ctx, cipher, NULL, (const unsigned char *)key->as_rs_key, NULL);
+		EVP_CIPHER_CTX_set_padding(&ctx,1);
+		int outl=0;
+		my_EVP_DecryptUpdate(&ctx, decoded_field, &outl, encoded_field, (int)encoded_field_size);
+
+		int tmp_outl = 0;
+		EVP_DecryptFinal_ex(&ctx, decoded_field + outl, &tmp_outl);
+		outl += tmp_outl;
+
+		EVP_CIPHER_CTX_cleanup(&ctx);
+
+		size_t len = 0;
+
+		dtoken->enc_block.key_length = nswap16(*((uint16_t*)(decoded_field+len)));
+		len += 2;
+
+		ns_bcopy(decoded_field+len,dtoken->enc_block.mac_key,dtoken->enc_block.key_length);
+		len += dtoken->enc_block.key_length;
+
+		dtoken->enc_block.timestamp = nswap64(*((uint64_t*)(decoded_field+len)));
+		len += 8;
+
+		dtoken->enc_block.lifetime = nswap32(*((uint32_t*)(decoded_field+len)));
+		len += 4;
+
+		return 0;
+	}
+	return -1;
+}
+
+#if !defined(TURN_NO_GCM)
+
+static void generate_random_nonce(unsigned char *nonce, size_t sz) {
+	if(!RAND_bytes(nonce, sz)) {
+		size_t i;
+		for(i=0;i<sz;++i) {
+			nonce[i] = (unsigned char)random();
+		}
+	}
+}
+
+static int encode_oauth_token_aead(const u08bits *server_name, encoded_oauth_token *etoken, const oauth_key *key, const oauth_token *dtoken, const u08bits* nonce0)
+{
+	if(server_name && etoken && key && dtoken && (dtoken->enc_block.key_length<128)) {
+
+		unsigned char orig_field[MAX_ENCODED_OAUTH_TOKEN_SIZE];
+		ns_bzero(orig_field,sizeof(orig_field));
+
+		size_t len = 0;
+		*((uint16_t*)(orig_field+len)) = nswap16(dtoken->enc_block.key_length);
+		len +=2;
+
+		ns_bcopy(dtoken->enc_block.mac_key,orig_field+len,dtoken->enc_block.key_length);
+		len += dtoken->enc_block.key_length;
+
+		*((uint64_t*)(orig_field+len)) = nswap64(dtoken->enc_block.timestamp);
+		len += 8;
+
+		*((uint32_t*)(orig_field+len)) = nswap32(dtoken->enc_block.lifetime);
+		len += 4;
+
+		const EVP_CIPHER * cipher = get_cipher_type(key->as_rs_alg);
+		if(!cipher)
+			return -1;
+
+		unsigned char *encoded_field = (unsigned char*)etoken->token;
+
+		unsigned char nonce[OAUTH_AEAD_NONCE_SIZE];
+		if(nonce0) {
+			ns_bcopy(nonce0,nonce,sizeof(nonce));
+		} else {
+			generate_random_nonce(nonce, sizeof(nonce));
+		}
+
+		EVP_CIPHER_CTX ctx;
+		EVP_CIPHER_CTX_init(&ctx);
+
+		/* Initialize the encryption operation. */
+		if(1 != EVP_EncryptInit_ex(&ctx, cipher, NULL, NULL, NULL))
+			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(&ctx, EVP_CTRL_GCM_SET_IVLEN, OAUTH_AEAD_NONCE_SIZE, NULL))
+			return -1;
+
+		/* Initialize key and IV */
+		if(1 != EVP_EncryptInit_ex(&ctx, 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(&ctx, NULL, &outl, server_name, (int)sn_len))
+			return -1;
+
+		outl=0;
+
+		if(1 != my_EVP_EncryptUpdate(&ctx, encoded_field, &outl, orig_field, (int)len))
+			return -1;
+
+		int tmp_outl = 0;
+		EVP_EncryptFinal_ex(&ctx, encoded_field + outl, &tmp_outl);
+		outl += tmp_outl;
+
+		EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, OAUTH_AEAD_TAG_SIZE, encoded_field + outl);
+		outl += OAUTH_AEAD_TAG_SIZE;
+
+		ns_bcopy(nonce, encoded_field + outl, OAUTH_AEAD_NONCE_SIZE);
+		outl += OAUTH_AEAD_NONCE_SIZE; //encoded+tag+hmac
+
+		etoken->size = outl;
+
+		EVP_CIPHER_CTX_cleanup(&ctx);
+
+		return 0;
+	}
+	return -1;
+}
+
+static int decode_oauth_token_aead(const u08bits *server_name, const encoded_oauth_token *etoken, const oauth_key *key, oauth_token *dtoken)
+{
+	if(server_name && etoken && key && dtoken) {
+
+		size_t min_encoded_field_size = 2+4+8+OAUTH_AEAD_NONCE_SIZE+OAUTH_AEAD_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;
+		unsigned int encoded_field_size = (unsigned int)etoken->size-OAUTH_AEAD_NONCE_SIZE - OAUTH_AEAD_TAG_SIZE;
+		const unsigned char* nonce = ((const unsigned char*)etoken->token) + encoded_field_size + OAUTH_AEAD_TAG_SIZE;
+
+		unsigned char tag[OAUTH_AEAD_TAG_SIZE];
+		ns_bcopy(((const unsigned char*)etoken->token) + 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;
+		}
+
+		EVP_CIPHER_CTX ctx;
+		EVP_CIPHER_CTX_init(&ctx);
+		/* Initialize the decryption operation. */
+		if(1 != EVP_DecryptInit_ex(&ctx, 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(&ctx, EVP_CTRL_GCM_SET_IVLEN, OAUTH_AEAD_NONCE_SIZE, NULL)) {
+			OAUTH_ERROR("%s: Cannot set nonce length\n",__FUNCTION__);
+			return -1;
+		}
+
+		/* Initialize key and IV */
+		if(1 != EVP_DecryptInit_ex(&ctx, 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 (&ctx, EVP_CTRL_GCM_SET_TAG, OAUTH_AEAD_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(&ctx, 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(&ctx, 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(&ctx, decoded_field + outl, &tmp_outl)<1) {
+			EVP_CIPHER_CTX_cleanup(&ctx);
+			OAUTH_ERROR("%s: token integrity check failed\n",__FUNCTION__);
+			return -1;
+		}
+		outl += tmp_outl;
+
+		EVP_CIPHER_CTX_cleanup(&ctx);
+
+		size_t len = 0;
+
+		dtoken->enc_block.key_length = nswap16(*((uint16_t*)(decoded_field+len)));
+		len += 2;
+
+		ns_bcopy(decoded_field+len,dtoken->enc_block.mac_key,dtoken->enc_block.key_length);
+		len += dtoken->enc_block.key_length;
+
+		dtoken->enc_block.timestamp = nswap64(*((uint64_t*)(decoded_field+len)));
+		len += 8;
+
+		dtoken->enc_block.lifetime = nswap32(*((uint32_t*)(decoded_field+len)));
+		len += 4;
+
+		return 0;
+	}
+	return -1;
+}
+
+#endif
+
+int encode_oauth_token(const u08bits *server_name, encoded_oauth_token *etoken, const oauth_key *key, const oauth_token *dtoken, const u08bits *nonce)
+{
+	UNUSED_ARG(nonce);
+	if(server_name && etoken && key && dtoken) {
+		switch(key->as_rs_alg) {
+		case AES_256_CBC:
+		case AES_128_CBC:
+			return encode_oauth_token_normal(server_name, etoken,key,dtoken);
+#if !defined(TURN_NO_GCM)
+		case AEAD_AES_128_GCM:
+		case AEAD_AES_256_GCM:
+			return encode_oauth_token_aead(server_name, etoken,key,dtoken,nonce);
+#endif
+		default:
+			fprintf(stderr,"Unsupported AS_RS algorithm: %d\n",(int)key->as_rs_alg);
+			break;
+		};
+	}
+	return -1;
+}
+
+int decode_oauth_token(const u08bits *server_name, const encoded_oauth_token *etoken, const oauth_key *key, oauth_token *dtoken)
+{
+	if(server_name && etoken && key && dtoken) {
+		switch(key->as_rs_alg) {
+		case AES_256_CBC:
+		case AES_128_CBC:
+			return decode_oauth_token_normal(server_name, etoken,key,dtoken);
+#if !defined(TURN_NO_GCM)
+		case AEAD_AES_128_GCM:
+		case AEAD_AES_256_GCM:
+			return decode_oauth_token_aead(server_name, etoken,key,dtoken);
+#endif
+		default:
+			fprintf(stderr,"Unsupported AS_RS algorithm: %d\n",(int)key->as_rs_alg);
+			break;
+		};
+	}
+	return -1;
+}
+
 ///////////////////////////////////////////////////////////////

+ 8 - 2
src/client/ns_turn_msg.h

@@ -90,7 +90,7 @@ u16bits stun_make_error_response(u16bits method);
 
 ///////////////////////////////////////////////////////////////
 
-u32bits stun_adjust_allocate_lifetime(u32bits lifetime);
+turn_time_t stun_adjust_allocate_lifetime(turn_time_t lifetime, turn_time_t max_lifetime);
 
 ///////////// STR ////////////////////////////////////////////////
 
@@ -116,7 +116,7 @@ int stun_is_command_message_offset_str(const u08bits* buf, size_t blen, int offs
 int stun_is_request_str(const u08bits* buf, size_t len);
 int stun_is_success_response_str(const u08bits* buf, size_t len);
 int stun_is_error_response_str(const u08bits* buf, size_t len, int *err_code, u08bits *err_msg, size_t err_msg_size);
-int stun_is_challenge_response_str(const u08bits* buf, size_t len, int *err_code, u08bits *err_msg, size_t err_msg_size, u08bits *realm, u08bits *nonce);
+int stun_is_challenge_response_str(const u08bits* buf, size_t len, int *err_code, u08bits *err_msg, size_t err_msg_size, u08bits *realm, u08bits *nonce, u08bits *server_name, int *oauth);
 int stun_is_response_str(const u08bits* buf, size_t len);
 int stun_is_indication_str(const u08bits* buf, size_t len);
 u16bits stun_get_method_str(const u08bits *buf, size_t len);
@@ -183,6 +183,7 @@ void print_bin_func(const char *name, size_t len, const void *s, const char *fun
 int stun_check_message_integrity_by_key_str(turn_credential_type ct, u08bits *buf, size_t len, hmackey_t key, st_password_t pwd, SHATYPE shatype, int *too_weak);
 int stun_check_message_integrity_str(turn_credential_type ct, u08bits *buf, size_t len, u08bits *uname, u08bits *realm, u08bits *upwd, SHATYPE shatype);
 int stun_attr_add_integrity_str(turn_credential_type ct, u08bits *buf, size_t *len, hmackey_t key, st_password_t pwd, SHATYPE shatype);
+int stun_attr_add_integrity_by_key_str(u08bits *buf, size_t *len, u08bits *uname, u08bits *realm, hmackey_t key, u08bits *nonce, SHATYPE shatype);
 int stun_attr_add_integrity_by_user_str(u08bits *buf, size_t *len, u08bits *uname, u08bits *realm, u08bits *upwd, u08bits *nonce, SHATYPE shatype);
 int stun_attr_add_integrity_by_user_short_term_str(u08bits *buf, size_t *len, u08bits *uname, st_password_t pwd, SHATYPE shatype);
 size_t get_hmackey_size(SHATYPE shatype);
@@ -209,6 +210,11 @@ int stun_attr_add_padding_str(u08bits *buf, size_t *len, u16bits padding_len);
 /* HTTP */
 int is_http_get(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 u08bits *server_name, const encoded_oauth_token *etoken, const oauth_key *key, oauth_token *dtoken);
+int encode_oauth_token(const u08bits *server_name, encoded_oauth_token *etoken, const oauth_key *key, const oauth_token *dtoken, const u08bits *nonce);
+
 ///////////////////////////////////////////////////////////////
 
 #ifdef __cplusplus

+ 1 - 0
src/client/ns_turn_msg_defs.h

@@ -43,6 +43,7 @@
 #define STUN_MAX_USERNAME_SIZE (513)
 #define STUN_MAX_REALM_SIZE (127)
 #define STUN_MAX_NONCE_SIZE (127)
+#define STUN_MAX_SERVER_NAME_SIZE (1025)
 #define STUN_MAX_PWD_SIZE (127)
 
 #define STUN_MAGIC_COOKIE (0x2112A442)

+ 115 - 4
src/client/ns_turn_msg_defs_new.h

@@ -38,6 +38,12 @@
 #define STUN_ATTRIBUTE_ORIGIN (0x802F)
 /* <<== Origin */
 
+/* Bandwidth */
+
+#define STUN_ATTRIBUTE_NEW_BANDWIDTH (0x8000 + STUN_ATTRIBUTE_BANDWIDTH)
+
+/* <<== Bandwidth */
+
 /* SHA AGILITY ==>> */
 
 #define SHA1SIZEBYTES (20)
@@ -46,7 +52,9 @@
 #define MAXSHASIZE (128)
 
 enum _SHATYPE {
-	SHATYPE_SHA1 = 0,
+	SHATYPE_ERROR = -1,
+	SHATYPE_DEFAULT=0,
+	SHATYPE_SHA1=SHATYPE_DEFAULT,
 	SHATYPE_SHA256
 };
 
@@ -58,10 +66,113 @@ typedef enum _SHATYPE SHATYPE;
 
 /* <<== SHA AGILITY */
 
-/* Bandwidth */
+/* OAUTH TOKEN ENC ALG ==> */
 
-#define STUN_ATTRIBUTE_NEW_BANDWIDTH (0x8000 + STUN_ATTRIBUTE_BANDWIDTH)
+enum _ENC_ALG {
+	ENC_ALG_ERROR=-1,
+	ENC_ALG_DEFAULT=0,
+	AES_256_CBC=ENC_ALG_DEFAULT,
+	AES_128_CBC,
+	AEAD_AES_128_GCM,
+	AEAD_AES_256_GCM,
+	ENG_ALG_NUM
+};
 
-/* <<== Bandwidth */
+typedef enum _ENC_ALG ENC_ALG;
+
+/* <<== OAUTH TOKEN ENC ALG */
+
+/* OAUTH TOKEN AUTH ALG ==> */
+
+enum _AUTH_ALG {
+	AUTH_ALG_ERROR = -1,
+	AUTH_ALG_UNDEFINED = 0,
+	AUTH_ALG_DEFAULT = 1,
+	AUTH_ALG_HMAC_SHA_256_128 = AUTH_ALG_DEFAULT,
+	AUTH_ALG_HMAC_SHA_1,
+	AUTH_ALG_HMAC_SHA_256
+};
+
+typedef enum _AUTH_ALG AUTH_ALG;
+
+/* <<== OAUTH TOKEN AUTH ALG */
+
+/**
+ * oAuth struct
+ */
+
+#define STUN_ATTRIBUTE_THIRD_PARTY_AUTHORIZATION (0x8031)
+#define STUN_ATTRIBUTE_OAUTH_ACCESS_TOKEN (0x0031)
+
+#define OAUTH_KID_SIZE (128)
+#define OAUTH_HASH_FUNC_SIZE (64)
+#define OAUTH_ALG_SIZE (64)
+#define OAUTH_KEY_SIZE (256)
+#define OAUTH_AEAD_NONCE_SIZE (12)
+#define OAUTH_AEAD_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 hkdf_hash_func[OAUTH_HASH_FUNC_SIZE+1];
+	char as_rs_alg[OAUTH_ALG_SIZE+1];
+	char as_rs_key[OAUTH_KEY_SIZE+1];
+	size_t as_rs_key_size;
+	char auth_alg[OAUTH_ALG_SIZE+1];
+	char auth_key[OAUTH_KEY_SIZE+1];
+	size_t auth_key_size;
+};
+
+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;
+	SHATYPE hkdf_hash_func;
+	ENC_ALG as_rs_alg;
+	char as_rs_key[OAUTH_KEY_SIZE+1];
+	size_t as_rs_key_size;
+	AUTH_ALG auth_alg;
+	char auth_key[OAUTH_KEY_SIZE+1];
+	size_t auth_key_size;
+};
+
+typedef struct _oauth_key oauth_key;
+
+struct _oauth_encrypted_block {
+	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_NEW__

+ 37 - 6
src/ns_turn_defs.h

@@ -31,8 +31,8 @@
 #ifndef __IOADEFS__
 #define __IOADEFS__
 
-#define TURN_SERVER_VERSION "4.1.2.1"
-#define TURN_SERVER_VERSION_NAME "Vitari"
+#define TURN_SERVER_VERSION "4.2.1.2"
+#define TURN_SERVER_VERSION_NAME "Monza"
 #define TURN_SOFTWARE "Coturn-" TURN_SERVER_VERSION " '" TURN_SERVER_VERSION_NAME "'"
 
 #if (defined(__unix__) || defined(unix)) && !defined(USG)
@@ -76,6 +76,7 @@ extern "C" {
 
 #define ns_bcopy(src,dst,sz) bcopy((src),(dst),(sz))
 #define ns_bzero(ptr,sz) bzero((ptr),(sz))
+#define ns_bcmp(ptr1,ptr2,sz) bcmp((ptr1),(ptr2),(sz))
 
 #define nswap16(s) ntohs(s)
 #define nswap32(ul) ntohl(ul)
@@ -110,10 +111,40 @@ static inline u64bits _ioa_ntoh64(u64bits v)
 #define ioa_ntoh64 _ioa_ntoh64
 #define ioa_hton64 _ioa_ntoh64
 
-#define turn_malloc(sz) malloc(sz)
-#define turn_free(ptr,sz) free(ptr)
-#define turn_realloc(ptr, old_sz, new_sz) realloc((ptr),(new_sz))
-#define turn_calloc(number, sz) calloc((number),(sz))
+#if defined(TURN_MEMORY_DEBUG)
+
+#if defined(TURN_LOG_FUNC)
+#undef TURN_LOG_FUNC
+#endif
+
+#define TURN_LOG_FUNC(level, ...) printf (__VA_ARGS__)
+
+  void tm_print_func(void);
+  void *turn_malloc_func(size_t sz, const char* file, int line);
+  void *turn_realloc_func(void *ptr, size_t old_sz, size_t new_sz, const char* file, int line);
+  void turn_free_func(void *ptr, size_t sz, const char* file, int line);
+  void turn_free_simple(void *ptr);
+  void *turn_calloc_func(size_t number, size_t size, const char* file, int line);
+  char *turn_strdup_func(const char* s, const char* file, int line);
+
+#define tm_print() tm_print_func()
+#define turn_malloc(sz) turn_malloc_func((size_t)(sz),__FUNCTION__,__LINE__)
+#define turn_free(ptr,sz) turn_free_func((ptr),(size_t)(sz),__FUNCTION__,__LINE__)
+#define turn_realloc(ptr, old_sz, new_sz) turn_realloc_func((ptr),(size_t)(old_sz),(size_t)(new_sz),__FUNCTION__,__LINE__)
+#define turn_calloc(number, sz) turn_calloc_func((number),(size_t)(sz),__FUNCTION__,__LINE__)
+#define turn_strdup(s) turn_strdup_func((s),__FUNCTION__,__LINE__)
+
+#else
+
+#define tm_print() 
+#define turn_malloc(sz) malloc((size_t)(sz))
+#define turn_free(ptr,sz) free((ptr))
+#define turn_realloc(ptr, old_sz, new_sz) realloc((ptr),(size_t)(new_sz))
+#define turn_calloc(number, sz) calloc((number),(size_t)(sz))
+#define turn_strdup(s) strdup((s))
+#define turn_free_simple free
+
+#endif
 
 #define turn_time() ((turn_time_t)time(NULL))
 

+ 2 - 2
src/server/ns_turn_ioalib.h

@@ -242,8 +242,8 @@ 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);
-int check_username_hash(ioa_socket_handle s, u08bits *username, u08bits *realm);
-void set_username_hash(ioa_socket_handle s, u08bits *username, u08bits *realm);
+int check_realm_hash(ioa_socket_handle s, u08bits *realm);
+void set_realm_hash(ioa_socket_handle s, u08bits *realm);
 
 ////////////////// Base64 /////////////////////////////
 

+ 76 - 0
src/server/ns_turn_maps.c

@@ -692,6 +692,48 @@ static void addr_list_foreach(addr_list_header* slh,  ur_addr_map_func func) {
   }
 }
 
+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;
@@ -863,6 +905,40 @@ 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 ret = 0;
+
+	if (ur_addr_map_valid(map)) {
+		u32bits i = 0;
+		for (i = 0; i < ADDR_MAP_SIZE; i++) {
+
+			const addr_list_header* slh = &(map->lists[i]);
+
+			ret += addr_list_num_elements(slh);
+		}
+	}
+
+	return ret;
+}
+
+size_t ur_addr_map_size(const ur_addr_map* map) {
+
+	size_t ret = 0;
+
+	if (ur_addr_map_valid(map)) {
+		u32bits i = 0;
+		for (i = 0; i < ADDR_MAP_SIZE; i++) {
+
+			const addr_list_header* slh = &(map->lists[i]);
+
+			ret += addr_list_size(slh);
+		}
+	}
+
+	return ret;
+}
+
 ////////////////////  STRING LISTS ///////////////////////////////////
 
 typedef struct _string_list {

+ 1 - 0
src/server/ns_turn_maps.h

@@ -216,6 +216,7 @@ int ur_addr_map_del(ur_addr_map* map, ioa_addr* key,ur_addr_map_func func);
  */
 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 //////////////////

+ 196 - 105
src/server/ns_turn_server.c

@@ -150,12 +150,12 @@ static int inc_quota(ts_ur_super_session* ss, u08bits *username)
 	if(ss && !(ss->quota_used) && ss->server && ((turn_turnserver*)ss->server)->chquotacb && username) {
 
 		if(((turn_turnserver*)ss->server)->ct == TURN_CREDENTIALS_LONG_TERM) {
-			if(!(ss->realm_set)) {
+			if(!(ss->origin_set)) {
 				return -1;
 			}
 		}
 
-		if((((turn_turnserver*)ss->server)->chquotacb)(username, (u08bits*)ss->realm_options.name)<0) {
+		if((((turn_turnserver*)ss->server)->chquotacb)(username, ss->oauth, (u08bits*)ss->realm_options.name)<0) {
 
 			return -1;
 
@@ -176,14 +176,20 @@ static void dec_quota(ts_ur_super_session* ss)
 
 		ss->quota_used = 0;
 
+		(((turn_turnserver*)ss->server)->raqcb)(ss->username, ss->oauth, (u08bits*)ss->realm_options.name);
+	}
+}
+
+static void dec_bps(ts_ur_super_session* ss)
+{
+	if(ss && ss->server) {
+
 		if(ss->bps) {
 			if(((turn_turnserver*)ss->server)->allocate_bps_func) {
 				((turn_turnserver*)ss->server)->allocate_bps_func(ss->bps,0);
 			}
 			ss->bps = 0;
 		}
-
-		(((turn_turnserver*)ss->server)->raqcb)(ss->username, (u08bits*)ss->realm_options.name);
 	}
 }
 
@@ -881,7 +887,7 @@ static int update_channel_lifetime(ts_ur_super_session *ss, ch_info* chn)
 
 /////////////// TURN ///////////////////////////
 
-#define SKIP_ATTRIBUTES case STUN_ATTRIBUTE_PRIORITY: case STUN_ATTRIBUTE_FINGERPRINT: case STUN_ATTRIBUTE_MESSAGE_INTEGRITY: break; \
+#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); \
@@ -961,7 +967,7 @@ static int handle_turn_allocate(turn_turnserver *server,
 	} else {
 
 		u08bits transport = 0;
-		u32bits lifetime = 0;
+		turn_time_t lifetime = 0;
 		int even_port = -1;
 		int dont_fragment = 0;
 		u64bits in_reservation_token = 0;
@@ -1158,7 +1164,7 @@ static int handle_turn_allocate(turn_turnserver *server,
 				}
 			}
 
-			lifetime = stun_adjust_allocate_lifetime(lifetime);
+			lifetime = stun_adjust_allocate_lifetime(lifetime, ss->max_session_time_auth);
 			u64bits out_reservation_token = 0;
 
 			if(inc_quota(ss, username)<0) {
@@ -1173,7 +1179,7 @@ static int handle_turn_allocate(turn_turnserver *server,
 					if(max_bps && (!bps || (bps && (bps>max_bps)))) {
 						bps = max_bps;
 					}
-					if(bps) {
+					if(bps && (ss->bps == 0)) {
 						ss->bps = server->allocate_bps_func(bps,1);
 						if(!(ss->bps)) {
 							*err_code = 486;
@@ -1281,8 +1287,6 @@ static int handle_turn_allocate(turn_turnserver *server,
 
 				if (*err_code) {
 
-					dec_quota(ss);
-
 					if(!(*reason)) {
 						*reason = (const u08bits *)"Cannot create relay endpoint(s)";
 					}
@@ -1364,6 +1368,24 @@ static int handle_turn_allocate(turn_turnserver *server,
 	return 0;
 }
 
+static void copy_auth_parameters(ts_ur_super_session *orig_ss, ts_ur_super_session *ss) {
+	if(orig_ss && ss) {
+		dec_quota(ss);
+		ns_bcopy(orig_ss->nonce,ss->nonce,sizeof(ss->nonce));
+		ss->nonce_expiration_time = orig_ss->nonce_expiration_time;
+		ns_bcopy(&(orig_ss->realm_options),&(ss->realm_options),sizeof(ss->realm_options));
+		ns_bcopy(orig_ss->username,ss->username,sizeof(ss->username));
+		ss->hmackey_set = orig_ss->hmackey_set;
+		ns_bcopy(orig_ss->hmackey,ss->hmackey,sizeof(ss->hmackey));
+		ss->oauth = orig_ss->oauth;
+		ns_bcopy(orig_ss->origin,ss->origin,sizeof(ss->origin));
+		ss->origin_set = orig_ss->origin_set;
+		ns_bcopy(orig_ss->pwd,ss->pwd,sizeof(ss->pwd));
+		ss->max_session_time_auth = orig_ss->max_session_time_auth;
+		inc_quota(ss,ss->username);
+	}
+}
+
 static int handle_turn_refresh(turn_turnserver *server,
 			       ts_ur_super_session *ss, stun_tid *tid, int *resp_constructed,
 			       int *err_code, 	const u08bits **reason, u16bits *unknown_attrs, u16bits *ua_num,
@@ -1378,8 +1400,8 @@ static int handle_turn_refresh(turn_turnserver *server,
 	{
 		int i;
 		for(i = 0;i<ALLOC_PROTOCOLS_NUMBER; ++i) {
-			if(a->relay_sessions[i].s) {
-				int family = get_local_addr_from_ioa_socket(a->relay_sessions[i].s)->ss.sa_family;
+			if(a->relay_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) {
@@ -1396,7 +1418,7 @@ static int handle_turn_refresh(turn_turnserver *server,
 
 	} else {
 
-		u32bits lifetime = 0;
+		turn_time_t lifetime = 0;
 		int to_delete = 0;
 		mobile_id_t mid = 0;
 		char smid[sizeof(ss->s_mobile_id)] = "\0";
@@ -1411,9 +1433,6 @@ static int handle_turn_refresh(turn_turnserver *server,
 				if(!(*(server->mobility))) {
 					*err_code = 405;
 					*reason = (const u08bits *)"Mobility forbidden";
-				} if(is_allocation_valid(a)) {
-					*err_code = 400;
-					*reason = (const u08bits *)"Mobility ticket cannot be used for a stable, already established allocation";
 				} else {
 					int smid_len = stun_attr_get_len(sar);
 					if(smid_len>0 && (((size_t)smid_len)<sizeof(smid))) {
@@ -1421,6 +1440,10 @@ static int handle_turn_refresh(turn_turnserver *server,
 						if(smid_val) {
 							ns_bcopy(smid_val, smid, (size_t)smid_len);
 							mid = string_to_mobile_id(smid);
+							if(is_allocation_valid(a) && (mid != ss->old_mobile_id)) {
+								*err_code = 400;
+								*reason = (const u08bits *)"Mobility ticket cannot be used for a stable, already established allocation";
+							}
 						}
 					} else {
 						*err_code = 400;
@@ -1526,7 +1549,7 @@ static int handle_turn_refresh(turn_turnserver *server,
 				} else {
 
 					ts_ur_super_session *orig_ss = get_session_from_mobile_map(server, mid);
-					if(!orig_ss) {
+					if(!orig_ss || orig_ss->to_be_closed || ioa_socket_tobeclosed(orig_ss->client_socket)) {
 						*err_code = 404;
 						*reason = (const u08bits *)"Allocation not found";
 					} else if(orig_ss == ss) {
@@ -1547,8 +1570,17 @@ static int handle_turn_refresh(turn_turnserver *server,
 
 						//Check security:
 						int postpone_reply = 0;
-						check_stun_auth(server, orig_ss, tid, resp_constructed, err_code, reason, in_buffer, nbh,
-								STUN_METHOD_REFRESH, &message_integrity, &postpone_reply, can_resume);
+
+						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) {
 
@@ -1560,8 +1592,9 @@ static int handle_turn_refresh(turn_turnserver *server,
 
 							if (to_delete)
 								lifetime = 0;
-							else
-								lifetime = stun_adjust_allocate_lifetime(lifetime);
+							else {
+								lifetime = stun_adjust_allocate_lifetime(lifetime, ss->max_session_time_auth);
+							}
 
 							if (af4c && refresh_relay_connection(server, orig_ss, lifetime, 0, 0, 0,
 										err_code, AF_INET) < 0) {
@@ -1579,37 +1612,41 @@ static int handle_turn_refresh(turn_turnserver *server,
 									*reason = (const u08bits *)"Cannot refresh relay connection (internal error)";
 								}
 
-							} else if(!to_delete && orig_ss && (inc_quota(orig_ss, orig_ss->username)<0)) {
-
-								*err_code = 486;
-								*reason = (const u08bits *)"Allocation Quota Reached";
-
 							} else {
 
 								//Transfer socket:
 
-								ioa_socket_handle s = detach_ioa_socket(ss->client_socket,0);
+								ioa_socket_handle s = detach_ioa_socket(ss->client_socket,1);
 
 								ss->to_be_closed = 1;
 
 								if(!s) {
-									dec_quota(orig_ss);
 									*err_code = 500;
 								} else {
 
 									if(attach_socket_to_session(server, s, orig_ss) < 0) {
-										IOA_CLOSE_SOCKET(s);
+										if(orig_ss->client_socket != s) {
+											IOA_CLOSE_SOCKET(s);
+										}
 										*err_code = 500;
-										dec_quota(orig_ss);
 									} 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);
@@ -1641,7 +1678,6 @@ static int handle_turn_refresh(turn_turnserver *server,
 
 										if ((server->fingerprint) || ss->enforce_fingerprints) {
 											if (stun_attr_add_fingerprint_str(ioa_network_buffer_data(nbh), &len) < 0) {
-												dec_quota(ss);
 												*err_code = 500;
 												ioa_network_buffer_delete(server->e, nbh);
 												return -1;
@@ -1669,8 +1705,9 @@ static int handle_turn_refresh(turn_turnserver *server,
 
 			if (to_delete)
 				lifetime = 0;
-			else
-				lifetime = stun_adjust_allocate_lifetime(lifetime);
+			else {
+				lifetime = stun_adjust_allocate_lifetime(lifetime, ss->max_session_time_auth);
+			}
 
 			if(!af4 && !af6) {
 				af4 = af4c;
@@ -1699,10 +1736,18 @@ static int handle_turn_refresh(turn_turnserver *server,
 
 				size_t len = ioa_network_buffer_get_size(nbh);
 				stun_init_success_response_str(STUN_METHOD_REFRESH, ioa_network_buffer_data(nbh), &len, tid);
-				u32bits lt = nswap32(lifetime);
 
+				if(ss->s_mobile_id[0]) {
+					stun_attr_add_str(ioa_network_buffer_data(nbh), &len,
+									STUN_ATTRIBUTE_MOBILITY_TICKET,
+									(u08bits*)ss->s_mobile_id,strlen(ss->s_mobile_id));
+					ioa_network_buffer_set_size(nbh,len);
+				}
+
+				u32bits lt = nswap32(lifetime);
 				stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_LIFETIME,
 						(const u08bits*) &lt, 4);
+
 				ioa_network_buffer_set_size(nbh,len);
 
 				*resp_constructed = 1;
@@ -1872,8 +1917,18 @@ static void tcp_peer_connection_completed_callback(int success, void *arg)
 
 		} else {
 			tc->state = TC_STATE_FAILED;
-			if(!err_code)
+			if(!err_code) {
 				err_code = 447;
+			}
+			{
+				char ls[257]="\0";
+				char rs[257]="\0";
+				ioa_addr *laddr = get_local_addr_from_ioa_socket(ss->client_socket);
+				if(laddr)
+					addr_to_string(laddr,(u08bits*)ls);
+				addr_to_string(&(tc->peer_addr),(u08bits*)rs);
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: failure to connect from %s to %s\n", __FUNCTION__, ls,rs);
+			}
 			const u08bits *reason = (const u08bits *)"Connection Timeout or Failure";
 			stun_init_error_response_str(STUN_METHOD_CONNECT, ioa_network_buffer_data(nbh), &len, err_code, reason, &(tc->tid));
 		}
@@ -1980,8 +2035,10 @@ static int tcp_start_connection_to_peer(turn_turnserver *server, ts_ur_super_ses
 	}
 
 	tc->state = TC_STATE_CLIENT_TO_PEER_CONNECTING;
-	IOA_CLOSE_SOCKET(tc->peer_s);
-	tc->peer_s = tcs;
+	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;
@@ -2004,6 +2061,11 @@ static void tcp_peer_accept_connection(ioa_socket_handle s, void *arg)
 
 		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) {
@@ -2049,8 +2111,7 @@ static void tcp_peer_accept_connection(ioa_socket_handle s, void *arg)
 
 		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__);
-			close_ioa_socket(s);
-			tc->peer_s = NULL;
+			IOA_CLOSE_SOCKET(tc->peer_s);
 			tc->state = TC_STATE_UNKNOWN;
 			FUNCEND;
 			return;
@@ -2188,7 +2249,12 @@ static int handle_turn_connection_bind(turn_turnserver *server,
 
 	u16bits method = STUN_METHOD_CONNECTION_BIND;
 
-	if (is_allocation_valid(a)) {
+	if(ss->to_be_closed) {
+
+		*err_code = 400;
+		*reason = (const u08bits *)"Bad request";
+
+	} else if (is_allocation_valid(a)) {
 
 		*err_code = 400;
 		*reason = (const u08bits *)"Bad request: CONNECTION_BIND cannot be issued after allocation";
@@ -2243,7 +2309,7 @@ static int handle_turn_connection_bind(turn_turnserver *server,
 			if(server->send_socket_to_relay) {
 				turnserver_id sid = (id & 0xFF000000)>>24;
 				ioa_socket_handle s = ss->client_socket;
-				if(s) {
+				if(s && !ioa_socket_tobeclosed(s)) {
 					ioa_socket_handle new_s = detach_ioa_socket(s,1);
 					if(new_s) {
 					  if(server->send_socket_to_relay(sid, id, tid, new_s, message_integrity, RMT_CB_SOCKET, in_buffer, can_resume)<0) {
@@ -2292,6 +2358,8 @@ int turnserver_accept_tcp_client_data_connection(turn_turnserver *server, tcp_co
 	int err_code = 0;
 	const u08bits *reason = NULL;
 
+	ioa_socket_handle s_to_delete = s;
+
 	if(tcid && tid && s) {
 
 		tc = get_tcp_connection_by_id(server->tcp_relay_connections, tcid);
@@ -2306,27 +2374,32 @@ int turnserver_accept_tcp_client_data_connection(turn_turnserver *server, tcp_co
 			} else {
 				ss = (ts_ur_super_session*)(a->owner);
 
-				//Check security:
-				int postpone_reply = 0;
-				check_stun_auth(server, ss, tid, &resp_constructed, &err_code, &reason, in_buffer, nbh,
+				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) {
+					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;
-					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);
+						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);
+						}
 					}
 				}
 			}
@@ -2374,6 +2447,7 @@ int turnserver_accept_tcp_client_data_connection(turn_turnserver *server, tcp_co
 		if(ss && !err_code) {
 			send_data_from_ioa_socket_nbh(s, NULL, nbh, TTL_IGNORE, TOS_IGNORE);
 			tcp_deliver_delayed_buffer(&(tc->ub_to_client),s,ss);
+			IOA_CLOSE_SOCKET(s_to_delete);
 			FUNCEND;
 			return 0;
 		} else {
@@ -2387,13 +2461,7 @@ int turnserver_accept_tcp_client_data_connection(turn_turnserver *server, tcp_co
 		}
 	}
 
-	if(s) {
-		if(tc && (s==tc->client_s)) {
-			;
-		} else {
-			close_ioa_socket(s);
-		}
-	}
+	IOA_CLOSE_SOCKET(s_to_delete);
 
 	FUNCEND;
 	return -1;
@@ -2691,7 +2759,7 @@ static int handle_turn_binding(turn_turnserver *server,
 
 		;
 
-	} else if(ss->client_socket) {
+	} 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,
@@ -3065,6 +3133,21 @@ static int create_challenge_response(ts_ur_super_session *ss, stun_tid *tid, int
 	char *realm = ss->realm_options.name;
 	stun_attr_add_str(ioa_network_buffer_data(nbh), &len, STUN_ATTRIBUTE_REALM,
 					(u08bits*)realm, (int)(strlen((s08bits*)(realm))));
+
+	if(ss->server) {
+		turn_turnserver* server = (turn_turnserver*)ss->server;
+		if(server->oauth) {
+			const char *server_name = server->oauth_server_name;
+			if(!(server_name && server_name[0])) {
+				server_name = realm;
+			}
+			stun_attr_add_str(ioa_network_buffer_data(nbh), &len,
+    			STUN_ATTRIBUTE_THIRD_PARTY_AUTHORIZATION,
+    			(const u08bits*)(server_name),
+    			strlen(server_name));
+		}
+    }
+
 	ioa_network_buffer_set_size(nbh,len);
 	return 0;
 }
@@ -3073,7 +3156,7 @@ static int create_challenge_response(ts_ur_super_session *ss, stun_tid *tid, int
 #define min(a,b) ((a)<=(b) ? (a) : (b))
 #endif
 
-static void resume_processing_after_username_check(int success,  hmackey_t hmackey, st_password_t pwd, turn_turnserver *server, u64bits ctxkey, ioa_net_data *in_buffer)
+static void resume_processing_after_username_check(int success,  int oauth, int max_session_time, hmackey_t hmackey, st_password_t pwd, turn_turnserver *server, u64bits ctxkey, ioa_net_data *in_buffer)
 {
 
 	if(server && in_buffer && in_buffer->nbh) {
@@ -3085,6 +3168,8 @@ static void resume_processing_after_username_check(int success,  hmackey_t hmack
 			if(success) {
 				ns_bcopy(hmackey,ss->hmackey,sizeof(hmackey_t));
 				ss->hmackey_set = 1;
+				ss->oauth = oauth;
+				ss->max_session_time_auth = (turn_time_t)max_session_time;
 				ns_bcopy(pwd,ss->pwd,sizeof(st_password_t));
 			}
 
@@ -3239,18 +3324,23 @@ static int check_stun_auth(turn_turnserver *server,
 
 	if(ss->username[0]) {
 		if(strcmp((char*)ss->username,(char*)usname)) {
-			if(method == STUN_METHOD_ALLOCATE) {
-				*err_code = 437;
-				*reason = (const u08bits*)"Allocation mismatch: wrong credentials";
+			if(ss->oauth) {
+				ss->hmackey_set = 0;
+				STRCPY(ss->username,usname);
 			} else {
-				*err_code = 441;
-				*reason = (const u08bits*)"Wrong credentials";
+				if(method == STUN_METHOD_ALLOCATE) {
+					*err_code = 437;
+					*reason = (const u08bits*)"Allocation mismatch: wrong credentials";
+				} else {
+					*err_code = 441;
+					*reason = (const u08bits*)"Wrong credentials";
+				}
+				return -1;
 			}
-			return -1;
 		}
 	} else {
 		STRCPY(ss->username,usname);
-		set_username_hash(ss->client_socket,ss->username,(u08bits*)ss->realm_options.name);
+		set_realm_hash(ss->client_socket,(u08bits*)ss->realm_options.name);
 	}
 
 	if(server->ct != TURN_CREDENTIALS_SHORT_TERM) {
@@ -3287,29 +3377,24 @@ static int check_stun_auth(turn_turnserver *server,
 
 	/* Password */
 	if(!(ss->hmackey_set) && (ss->pwd[0] == 0)) {
-		ur_string_map_value_type ukey = NULL;
 		if(can_resume) {
-			ukey = (server->userkeycb)(server->id, server->ct, usname, realm, resume_processing_after_username_check, in_buffer, ss->id, postpone_reply);
+			(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;
 			}
 		}
 		/* we always return NULL for short-term credentials here */
-		if(!ukey) {
-			/* direct user pattern is supported only for long-term credentials */
-			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,
-					"%s: Cannot find credentials of user <%s>\n",
-					__FUNCTION__, (char*)usname);
-			*err_code = 401;
-			*reason = (const u08bits*)"Unauthorised";
-			if(server->ct != TURN_CREDENTIALS_SHORT_TERM) {
-				return create_challenge_response(ss,tid,resp_constructed,err_code,reason,nbh,method);
-			} else {
-				return -1;
-			}
+		/* direct user pattern is supported only for long-term credentials */
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,
+				"%s: Cannot find credentials of user <%s>\n",
+				__FUNCTION__, (char*)usname);
+		*err_code = 401;
+		*reason = (const u08bits*)"Unauthorised";
+		if(server->ct != TURN_CREDENTIALS_SHORT_TERM) {
+			return create_challenge_response(ss,tid,resp_constructed,err_code,reason,nbh,method);
+		} else {
+			return -1;
 		}
-		ns_bcopy(ukey,ss->hmackey,16);
-		ss->hmackey_set = 1;
 	}
 
 	/* Check integrity */
@@ -3335,7 +3420,7 @@ static int check_stun_auth(turn_turnserver *server,
 		}
 
 		if(can_resume) {
-			(server->userkeycb)(server->id, server->ct, usname, realm, resume_processing_after_username_check, in_buffer, ss->id, postpone_reply);
+			(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;
 			}
@@ -3473,7 +3558,7 @@ static int handle_turn_command(turn_turnserver *server, ts_ur_super_session *ss,
 			}
 
 			/* check that the realm is the same as in the original request */
-			if(ss->realm_set) {
+			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));
 
@@ -3530,7 +3615,7 @@ static int handle_turn_command(turn_turnserver *server, ts_ur_super_session *ss,
 			}
 
 			/* get the initial origin value */
-			if(!err_code && !(ss->realm_set) && (method == STUN_METHOD_ALLOCATE)) {
+			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));
@@ -3561,7 +3646,7 @@ static int handle_turn_command(turn_turnserver *server, ts_ur_super_session *ss,
 							ioa_network_buffer_get_size(in_buffer->nbh), sar);
 				}
 
-				ss->realm_set = 1;
+				ss->origin_set = 1;
 			}
 
 			if(!err_code && !(*resp_constructed) && !no_response) {
@@ -3985,6 +4070,7 @@ int shutdown_client_connection(turn_turnserver *server, ts_ur_super_session *ss,
 
 	report_turn_session_info(server,ss,1);
 	dec_quota(ss);
+	dec_bps(ss);
 
 	if(!force && ss->is_mobile) {
 
@@ -4187,10 +4273,10 @@ static int create_relay_connection(turn_turnserver* server,
 			ns_bzero(newelem, sizeof(relay_endpoint_session));
 			newelem->s = s;
 
-			if(!check_username_hash(newelem->s,ss->username,(u08bits*)ss->realm_options.name)) {
+			if(!check_realm_hash(newelem->s,(u08bits*)ss->realm_options.name)) {
 				IOA_CLOSE_SOCKET(newelem->s);
 				*err_code = 508;
-				*reason = (const u08bits *)"Cannot find a valid reserved socket for this username";
+				*reason = (const u08bits *)"Cannot find a valid reserved socket for this realm";
 				return -1;
 			}
 
@@ -4228,11 +4314,11 @@ static int create_relay_connection(turn_turnserver* server,
 			return -1;
 		}
 
-		set_username_hash(newelem->s,ss->username,(u08bits*)ss->realm_options.name);
+		set_realm_hash(newelem->s,(u08bits*)ss->realm_options.name);
 
 		if (rtcp_s) {
 			if (out_reservation_token && *out_reservation_token) {
-				set_username_hash(rtcp_s,ss->username,(u08bits*)ss->realm_options.name);
+				set_realm_hash(rtcp_s,(u08bits*)ss->realm_options.name);
 				/* OK */
 			} else {
 				IOA_CLOSE_SOCKET(newelem->s);
@@ -4244,8 +4330,8 @@ static int create_relay_connection(turn_turnserver* server,
 		}
 
 		/* RFC6156: do not use DF when IPv6 is involved: */
-		if((get_local_addr_from_ioa_socket(newelem->s)->ss.sa_family == AF_INET6) ||
-		   (get_local_addr_from_ioa_socket(ss->client_socket)->ss.sa_family == AF_INET6))
+		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) {
@@ -4261,7 +4347,7 @@ static int create_relay_connection(turn_turnserver* server,
 		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_local_addr_from_ioa_socket(newelem->s)->ss.sa_family);
+		set_allocation_lifetime_ev(a, server->ctime + lifetime, ev, get_ioa_socket_address_family(newelem->s));
 
 		set_ioa_socket_session(newelem->s, ss);
 	}
@@ -4324,7 +4410,7 @@ static int read_client_connection(turn_turnserver *server,
 
 	FUNCSTART;
 
-	if (!server || !ss || !in_buffer || !(ss->client_socket)) {
+	if (!server || !ss || !in_buffer || !(ss->client_socket) || ss->to_be_closed || ioa_socket_tobeclosed(ss->client_socket)) {
 		FUNCEND;
 		return -1;
 	}
@@ -4444,6 +4530,7 @@ static int read_client_connection(turn_turnserver *server,
 		SOCKET_TYPE st = get_ioa_socket_type(ss->client_socket);
 		if((st == TCP_SOCKET)||(st==TLS_SOCKET)||(st==TENTATIVE_TCP_SOCKET)) {
 			if(is_http_get((char*)ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh)))
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: HTTP request: %s\n", __FUNCTION__, (char*)ioa_network_buffer_data(in_buffer->nbh));
 				write_http_echo(server,ss);
 		}
 	}
@@ -4459,7 +4546,7 @@ static int attach_socket_to_session(turn_turnserver* server, ioa_socket_handle s
 	int ret = -1;
 	FUNCSTART;
 
-	if(s && server && ss) {
+	if(s && server && ss && !ioa_socket_tobeclosed(s)) {
 
 		if(ss->client_socket != s) {
 
@@ -4692,7 +4779,8 @@ void init_turn_server(turn_turnserver* server,
 		send_socket_to_relay_cb send_socket_to_relay,
 		vintp secure_stun, SHATYPE shatype, vintp mobility, int server_relay,
 		send_turn_session_info_cb send_turn_session_info,
-		allocate_bps_cb allocate_bps_func) {
+		allocate_bps_cb allocate_bps_func,
+		int oauth, const char* oauth_server_name) {
 
 	if (!server)
 		return;
@@ -4716,6 +4804,9 @@ void init_turn_server(turn_turnserver* server,
 	server->mobility = mobility;
 	server->server_relay = server_relay;
 	server->send_turn_session_info = send_turn_session_info;
+	server->oauth = oauth;
+	if(oauth)
+		server->oauth_server_name = oauth_server_name;
 	if(mobility)
 		server->mobile_connections_map = ur_map_create();
 

+ 11 - 5
src/server/ns_turn_server.h

@@ -90,10 +90,10 @@ typedef enum {
 struct _turn_turnserver;
 typedef struct _turn_turnserver turn_turnserver;
 
-typedef void (*get_username_resume_cb)(int success, hmackey_t hmackey, st_password_t pwd, turn_turnserver *server, u64bits ctxkey, ioa_net_data *in_buffer);
-typedef u08bits *(*get_user_key_cb)(turnserver_id id, turn_credential_type ct, u08bits *uname, u08bits *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, u64bits ctxkey, int *postpone_reply);
-typedef int (*check_new_allocation_quota_cb)(u08bits *username, u08bits *realm);
-typedef void (*release_allocation_quota_cb)(u08bits *username, u08bits *realm);
+typedef void (*get_username_resume_cb)(int success, int oauth, int max_session_time, hmackey_t hmackey, st_password_t pwd, turn_turnserver *server, u64bits ctxkey, ioa_net_data *in_buffer);
+typedef u08bits *(*get_user_key_cb)(turnserver_id id, turn_credential_type ct, int in_oauth, int *out_oauth, u08bits *uname, u08bits *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, u64bits ctxkey, int *postpone_reply);
+typedef int (*check_new_allocation_quota_cb)(u08bits *username, int oauth, u08bits *realm);
+typedef void (*release_allocation_quota_cb)(u08bits *username, int oauth, u08bits *realm);
 typedef int (*send_socket_to_relay_cb)(turnserver_id id, u64bits cid, stun_tid *tid, ioa_socket_handle s, int message_integrity, MESSAGE_TO_RELAY_TYPE rmt, ioa_net_data *nd, int can_resume);
 typedef int (*send_turn_session_info_cb)(struct turn_session_info *tsi);
 
@@ -160,6 +160,10 @@ struct _turn_turnserver {
 
 	/* Bandwidth draft: */
 	allocate_bps_cb allocate_bps_func;
+
+	/* oAuth: */
+	int oauth;
+	const char* oauth_server_name;
 };
 
 ///////////////////////////////////////////
@@ -195,7 +199,9 @@ void init_turn_server(turn_turnserver* server,
 				    vintp mobility,
 				    int server_relay,
 				    send_turn_session_info_cb send_turn_session_info,
-				    allocate_bps_cb allocate_bps_func);
+				    allocate_bps_cb allocate_bps_func,
+				    int oauth,
+				    const char* oauth_server_name);
 
 ioa_engine_handle turn_server_get_engine(turn_turnserver *s);
 

+ 11 - 7
src/server/ns_turn_session.h

@@ -72,16 +72,23 @@ struct _ts_ur_super_session {
   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 */
   u08bits nonce[NONCE_MAX_SIZE];
   turn_time_t nonce_expiration_time;
   u08bits username[STUN_MAX_USERNAME_SIZE+1];
   hmackey_t hmackey;
   int hmackey_set;
   st_password_t pwd;
-  int enforce_fingerprints;
-  int is_tcp_relay;
-  int to_be_closed;
   int quota_used;
+  int oauth;
+  turn_time_t max_session_time_auth;
+  /* Realm */
+  realm_options_t realm_options;
+  int origin_set;
+  s08bits origin[STUN_MAX_ORIGIN_SIZE + 1];
   /* Stats */
   u32bits received_packets;
   u32bits sent_packets;
@@ -97,11 +104,8 @@ struct _ts_ur_super_session {
   /* Mobile */
   int is_mobile;
   mobile_id_t mobile_id;
+  mobile_id_t old_mobile_id;
   char s_mobile_id[33];
-  /* Realm */
-  realm_options_t realm_options;
-  int realm_set;
-  s08bits origin[STUN_MAX_ORIGIN_SIZE + 1];
   /* Bandwidth */
   band_limit_t bps;
 };

+ 1 - 0
turndb/schema.mongo.sh

@@ -8,6 +8,7 @@ db.turnusers_lt.ensureIndex({ realm: 1, name: 1 }, { unique: 1 });
 db.turnusers_st.ensureIndex({ name: 1 }, { unique: 1 });
 db.turn_secret.ensureIndex({ realm: 1 }, { unique: 1 });
 db.realm.ensureIndex({ realm: 1 }, { unique: 1 });
+db.oauth_key.ensureIndex({ kid: 1 }, {unique: 1 });
 
 exit
 

+ 13 - 0
turndb/schema.sql

@@ -39,3 +39,16 @@ CREATE TABLE turn_realm_option (
 	value varchar(128),
 	primary key (realm,opt)
 );
+
+CREATE TABLE oauth_key (
+	kid varchar(128),
+	ikm_key varchar(256) default '',
+	timestamp bigint default 0,
+	lifetime integer default 0,
+	hkdf_hash_func varchar(64) default '',
+	as_rs_alg varchar(64) default '',
+	as_rs_key varchar(256) default '',
+	auth_alg varchar(64) default '',
+	auth_key varchar(256) default '',
+	primary key (kid)
+);

+ 39 - 1
turndb/schema.userdb.redis

@@ -37,6 +37,41 @@ the 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.
 
+5) For the oAuth authentication, there is a hash structure with the key 
+"turn/oauth/kid/<kid-value>". The kid structure fields are:
+ 
+	ikm_key - (optional) base64-encoded key ("input keying material");
+		The ikm_key is not needed if the as_rs_key and auth_key are defined
+		explicitly in the database;
+		
+	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.
+	
+	hkdf_hash_func - (optional) hash function for HKDF procedure; the 
+		valid values are SHA-1 and SHA-256, with SHA-256 as default;
+		The hkdf_hash_func is not needed if the as_rs_key and auth_key 
+		are defined explicitly in the database;
+		
+	as_rs_alg - oAuth token encryption algorithm; the valid values are
+		"AES-128-CBC" and "AES-256-CBC", , "AEAD-AES-128-GCM",
+		"AEAD-AES-256-GCM".
+		The default value is "AES-256-CBC";
+		
+	as_rs_key - (optional) base64-encoded AS-RS key. If not defined, then 
+		calculated with ikm_key and hkdf_hash_func. The as_rs_key length 
+		is defined by as_rs_alg.
+		
+	auth_alg - (optional) oAuth token authentication algorithm; the valid values are
+		"HMAC-SHA-256-128", "HMAC-SHA-256" and "HMAC-SHA-1".		  
+		The default value is "HMAC-SHA-256-128".
+		
+	auth_key - (optional) base64-encoded AUTH key. If not defined, then 
+		calculated with ikm_key and hkdf_hash_func. The auth_key length 
+		is defined by auth_alg.
+
 II. Extra realms data in the database
 
 We can use more than one realm with the same instance of the TURN server.
@@ -68,6 +103,7 @@ This example sets user database for:
   * 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 "north" and key value "carleon". 
   
 The shell command would be:
 
@@ -109,6 +145,8 @@ set turn/denied-peer-ip/234567 "123::45"
 
 set turn/allowed-peer-ip/345678 "172.17.13.200"
 
+hmset turn/oauth/kid/north ikm_key Y2FybGVvbg== hkdf_hash_func 'SHA-256' as_rs_alg 'AES-128-CBC' auth_alg 'HMAC-SHA-256-128'
+
 save
 
 !
@@ -116,7 +154,7 @@ save
 IV. 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 
+TURN Server process 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 

+ 12 - 0
turndb/testmongosetup.sh

@@ -41,6 +41,18 @@ db.allowed_peer_ip.insert({ ip_range: '172.17.13.200' });
 db.denied_peer_ip.insert({ ip_range: '172.17.13.133-172.17.14.56' });
 db.denied_peer_ip.insert({ ip_range: '123::45' });
 
+db.oauth_key.insert({ kid: 'north', 
+					ikm_key: 'Y2FybGVvbg==', 
+					hkdf_hash_func: 'SHA-256', 
+					as_rs_alg: 'AES-256-CBC', 
+					auth_alg: 'HMAC-SHA-256-128' });
+					
+db.oauth_key.insert({ kid: 'oldempire', 
+					ikm_key: 'YXVsY3Vz', 
+					hkdf_hash_func: 'SHA-256', 
+					as_rs_alg: 'AEAD-AES-256-GCM', 
+					auth_alg: '' });
+
 exit
 
 EOF

+ 12 - 0
turndb/testredisdbsetup.sh

@@ -7,11 +7,17 @@ AUTH turn
 
 set turn/realm/north.gov/user/ninefingers/key "bc807ee29df3c9ffa736523fb2c4e8ee"
 set turn/realm/north.gov/user/gorst/key "7da2270ccfa49786e0115366d3a3d14d"
+
+set turn/realm/north.gov/user/bethod/key "3b4125e139811b8577a214c24273fee27b15ff397631c7775b980785a229e6bd"
+
 set turn/realm/crinna.org/user/whirrun/key "6972e85e51f36e53b0b61759c5a5219a"
 set turn/realm/crinna.org/user/stranger-come-knocking/key "d43cb678560259a1839bff61c19de15e"
 
 set turn/realm/north.gov/user/ninefingers/password "youhavetoberealistic"
 set turn/realm/north.gov/user/gorst/password "hero"
+
+set turn/realm/north.gov/user/bethod/password "king-of-north"
+
 set turn/realm/crinna.org/user/whirrun/password "sword"
 set turn/realm/crinna.org/user/stranger-come-knocking/password "civilization"
 
@@ -20,6 +26,9 @@ set turn/realm/crinna.org/secret/777888999 "north"
 
 set turn/user/ninefingers/password "youhavetoberealistic"
 set turn/user/gorst/password "hero"
+
+set turn/user/bethod/password "king-of-north"
+
 set turn/user/whirrun/password "sword"
 set turn/user/stranger-come-knocking/password "civilization"
 
@@ -38,6 +47,9 @@ set turn/denied-peer-ip/234567 "123::45"
 
 set turn/allowed-peer-ip/345678 "172.17.13.200"
 
+hmset turn/oauth/kid/north ikm_key Y2FybGVvbg== hkdf_hash_func 'SHA-256' as_rs_alg 'AES-256-CBC' auth_alg 'HMAC-SHA-256-128'
+hmset turn/oauth/kid/oldempire ikm_key YXVsY3Vz hkdf_hash_func 'SHA-256' as_rs_alg 'AEAD-AES-256-GCM'
+
 save
 
 !

+ 3 - 0
turndb/testsqldbsetup.sql

@@ -26,3 +26,6 @@ insert into allowed_peer_ip (ip_range) values('172.17.13.200');
 
 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 oauth_key (kid,ikm_key,timestamp,lifetime,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key) values('north','Y2FybGVvbg==',0,0,'SHA-256','AES-256-CBC','','HMAC-SHA-256-128','');
+insert into oauth_key (kid,ikm_key,timestamp,lifetime,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key) values('oldempire','YXVsY3Vz',0,0,'SHA-256','AEAD-AES-256-GCM','','','');