فهرست منبع

Imported Upstream version 4.4.1.1

Oleg Moskalenko 10 سال پیش
والد
کامیت
69ce057901
74فایلهای تغییر یافته به همراه6562 افزوده شده و 2775 حذف شده
  1. 11 2
      ChangeLog
  2. 44 30
      INSTALL
  3. 2 2
      Makefile.in
  4. 6 6
      README.turnadmin
  5. 32 26
      README.turnserver
  6. 1 5
      README.turnutils
  7. 7 0
      STATUS
  8. 3 12
      TODO
  9. 7 14
      examples/etc/turnserver.conf
  10. 2 2
      examples/scripts/longtermsecure/secure_relay.sh
  11. 2 2
      examples/scripts/longtermsecure/secure_relay_cert.sh
  12. 3 3
      examples/scripts/longtermsecuredb/secure_relay_with_db_mongo.sh
  13. 3 3
      examples/scripts/longtermsecuredb/secure_relay_with_db_mysql.sh
  14. 3 3
      examples/scripts/longtermsecuredb/secure_relay_with_db_mysql_ssl.sh
  15. 4 4
      examples/scripts/longtermsecuredb/secure_relay_with_db_psql.sh
  16. 3 3
      examples/scripts/longtermsecuredb/secure_relay_with_db_redis.sh
  17. 3 3
      examples/scripts/longtermsecuredb/secure_relay_with_db_sqlite.sh
  18. 2 2
      examples/scripts/mobile/mobile_relay.sh
  19. 8 6
      examples/scripts/readme.txt
  20. 2 2
      examples/scripts/restapi/secure_relay_secret.sh
  21. 2 2
      examples/scripts/restapi/secure_relay_secret_with_db_mongo.sh
  22. 2 2
      examples/scripts/restapi/secure_relay_secret_with_db_mysql.sh
  23. 2 2
      examples/scripts/restapi/secure_relay_secret_with_db_psql.sh
  24. 2 2
      examples/scripts/restapi/secure_relay_secret_with_db_redis.sh
  25. 2 2
      examples/scripts/restapi/secure_relay_secret_with_db_sqlite.sh
  26. 2 2
      examples/scripts/selfloadbalance/secure_relay.sh
  27. 0 34
      examples/scripts/shorttermsecure/secure_relay_short_term_mech.sh
  28. 0 31
      examples/scripts/shorttermsecure/secure_tcp_client_c2c_tcp_relay_short_term.sh
  29. 0 30
      examples/scripts/shorttermsecure/secure_udp_client_short_term.sh
  30. BIN
      examples/var/db/turndb
  31. 10 10
      man/man1/turnadmin.1
  32. 32 31
      man/man1/turnserver.1
  33. 2 8
      man/man1/turnutils.1
  34. 1 1
      rpm/build.settings.sh
  35. 4 8
      rpm/turnserver.spec
  36. 0 8
      src/apps/common/apputils.c
  37. 422 211
      src/apps/relay/dbdrivers/dbd_mongo.c
  38. 281 111
      src/apps/relay/dbdrivers/dbd_mysql.c
  39. 283 111
      src/apps/relay/dbdrivers/dbd_pgsql.c
  40. 275 132
      src/apps/relay/dbdrivers/dbd_redis.c
  41. 331 130
      src/apps/relay/dbdrivers/dbd_sqlite.c
  42. 10 7
      src/apps/relay/dbdrivers/dbdriver.h
  43. 3 1
      src/apps/relay/dtls_listener.c
  44. 328 7
      src/apps/relay/http_server.c
  45. 96 0
      src/apps/relay/http_server.h
  46. 32 59
      src/apps/relay/mainrelay.c
  47. 3 2
      src/apps/relay/mainrelay.h
  48. 23 30
      src/apps/relay/netengine.c
  49. 113 12
      src/apps/relay/ns_ioalib_engine_impl.c
  50. 6 0
      src/apps/relay/ns_ioalib_impl.h
  51. 3832 0
      src/apps/relay/turn_admin_server.c
  52. 22 7
      src/apps/relay/turn_admin_server.h
  53. 0 1333
      src/apps/relay/turncli.c
  54. 100 76
      src/apps/relay/userdb.c
  55. 4 9
      src/apps/relay/userdb.h
  56. 3 27
      src/apps/uclient/mainuclient.c
  57. 5 10
      src/apps/uclient/startuclient.c
  58. 4 21
      src/apps/uclient/uclient.c
  59. 3 2
      src/apps/uclient/uclient.h
  60. 22 6
      src/client/ns_turn_ioaddr.c
  61. 38 21
      src/client/ns_turn_msg.c
  62. 5 9
      src/client/ns_turn_msg.h
  63. 2 1
      src/client/ns_turn_msg_defs.h
  64. 2 2
      src/ns_turn_defs.h
  65. 1 2
      src/server/ns_turn_ioalib.h
  66. 43 59
      src/server/ns_turn_server.c
  67. 4 1
      src/server/ns_turn_server.h
  68. 2 2
      src/server/ns_turn_session.h
  69. 2 2
      turndb/schema.mongo.sh
  70. 15 13
      turndb/schema.sql
  71. 16 24
      turndb/schema.userdb.redis
  72. 7 7
      turndb/testmongosetup.sh
  73. 10 20
      turndb/testredisdbsetup.sh
  74. 5 5
      turndb/testsqldbsetup.sql

+ 11 - 2
ChangeLog

@@ -1,3 +1,12 @@
+1/24/2015 Oleg Moskalenko <[email protected]>
+Version 4.4.1.1 'Ardee West':
+	- https admin server;
+	- SSLv2 support cancelled (security concern fixed);
+	- The server-side short-term credentials mechanism support cancelled;
+	- OpenSSL 1.1.0 supported;
+	- shared secrets fixed in MongoDB: multiple secrets per realm allowed;
+	- shared secrets admin fixed in Redis;
+
 12/24/2014 Oleg Moskalenko <[email protected]>
 Version 4.3.3.1 'Tolomei':
 	- multiple authentication threads;
@@ -458,9 +467,9 @@ Version 2.6.1.1 'Harding Grim':
 			= In REST API timestamp, we are now using
 				the expiration time (Issue 31).
 		* Configurable cipher suite in the TURN server.
-		* SSL3 support.
+		* SSLv3 support.
 		* TLS 1.1 and 1.2 support.
-		* SSL2 "encapsulation" mode support.
+		* SSLv2 "encapsulation" mode support.
 		* NULL OpenSSL cipher is allowed to be negotiated between
 			server and client.
 		* -U option (NULL cipher) added to the test client.

+ 44 - 30
INSTALL

@@ -669,7 +669,7 @@ The schema description:
 # Table for long-term credentials mechanism authorization:
 #
 CREATE TABLE turnusers_lt (
-    realm varchar(512) default '',
+    realm varchar(127) default '',
     name varchar(512),
     hmackey char(128),
     PRIMARY KEY (realm,name)
@@ -684,27 +684,20 @@ 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 (
-    name varchar(512) PRIMARY KEY,
-    password varchar(512)
-);
-
 # Table holding shared secrets for secret-based authorization
 # (REST API). It can only be used together with the long-term 
 # mechanism:
 #
 CREATE TABLE turn_secret (
-	realm varchar(512) default '',
-    value varchar(512),
+	realm varchar(127) default '',
+    value varchar(127),
 	primary key (realm,value)
 );
 
 # Table holding "white" allowed peer IP ranges.
 #
 CREATE TABLE allowed_peer_ip (
-	realm varchar(512) default '',
+	realm varchar(127) default '',
 	ip_range varchar(256),
 	primary key (realm,ip_range)
 );
@@ -712,7 +705,7 @@ CREATE TABLE allowed_peer_ip (
 # Table holding "black" denied peer IP ranges.
 #
 CREATE TABLE denied_peer_ip (
-	realm varchar(512) default '',
+	realm varchar(127) default '',
 	ip_range varchar(256),
 	primary key (realm,ip_range)
 );
@@ -723,8 +716,8 @@ CREATE TABLE denied_peer_ip (
 # then the default realm is used.
 #
 CREATE TABLE turn_origin_to_realm (
-	origin varchar(512),
-	realm varchar(512),
+	origin varchar(127),
+	realm varchar(127),
 	primary key (origin,realm)
 );
 
@@ -734,7 +727,7 @@ CREATE TABLE turn_origin_to_realm (
 # Values for them are integers (in text form).
 #
 CREATE TABLE turn_realm_option (
-	realm varchar(512) default '',
+	realm varchar(127) default '',
 	opt varchar(32),
 	value varchar(128),
 	primary key (realm,opt)
@@ -753,7 +746,7 @@ CREATE TABLE oauth_key (
 	auth_alg varchar(64) default '',
 	auth_key varchar(256) default '',
 	primary key (kid)
-);
+); 
 
 The oauth_key table fields meanings are:
 
@@ -764,7 +757,7 @@ The oauth_key table fields meanings are:
 		explicitly in the database;
 		
 	timestamp - (optional) the timestamp (in seconds) when the key 
-		lifetime started;
+		lifetime starts;
 	
 	lifetime - (optional) the key lifetime in seconds; the default value 
 		is 0 - unlimited lifetime.
@@ -791,6 +784,17 @@ The oauth_key table fields meanings are:
 		calculated with ikm_key and hkdf_hash_func. The auth_key length 
 		is defined by auth_alg.
 
+# Https access admin users.
+# Leave this table empty if you do not want 
+# remote https access to the admin functions.
+#
+CREATE TABLE admin_user (
+	name varchar(32),
+	realm varchar(127),
+	password varchar(127),
+	primary key (name)
+);
+
 You can use turnadmin program to manage the database - you can either use 
 turnadmin to add/modify/delete users, or you can use turnadmin to produce 
 the hmac keys and modify the database with your favorite tools.
@@ -816,10 +820,10 @@ Fill in users, for example:
   Long-term credentials mechanism with SHA256 extension:
   $ bin/turnadmin -a -b "/var/db/turndb" -u bethod -r north.gov -p king-of-north --sha256
   
-  Short-term credentials mechanism:
+  Admin users:
    
   $ bin/turnadmin -A -b "/var/db/turndb" -u gorst -p hero
-  $ bin/turnadmin -A -b "/var/db/turndb" -u ninefingers -p youhavetoberealistic 
+  $ bin/turnadmin -A -b "/var/db/turndb" -u ninefingers -p youhavetoberealistic -r north.gov 
 
 XVI. PostgreSQL setup
 
@@ -888,8 +892,6 @@ PREFIX/share/turnserver/schema.sql file after the turnserver installation:
 $ cat turndb/schema.sql | psql -U turn turn
 	NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "turnusers_lt_pkey" for table "turnusers_lt"
 	CREATE TABLE
-	NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "turnusers_st_pkey" for table "turnusers_st"
-	CREATE TABLE
 	CREATE TABLE
 
 See the SQLite section for the detailed database schema explanation.
@@ -940,10 +942,10 @@ Fill in users, for example:
   Long-term credentials mechanism with SHA256 extension:
   $ bin/turnadmin -a -e "host=localhost dbname=coturn user=turn password=turn" -u bethod -r north.gov -p king-of-north --sha256
   
-  Short-term credentials mechanism:
+  Admin users:
    
   $ bin/turnadmin -A -e "host=localhost dbname=coturn user=turn password=turn" -u gorst -p hero
-  $ bin/turnadmin -A -e "host=localhost dbname=coturn user=turn password=turn" -u ninefingers -p youhavetoberealistic 
+  $ bin/turnadmin -A -e "host=localhost dbname=coturn user=turn password=turn" -u ninefingers -p youhavetoberealistic -r north.gov
 
 XVII. MySQL (MariaDB) setup
 
@@ -995,10 +997,10 @@ the root account.
   Long-term credentials mechanism with SHA256 extension:
   $ bin/turnadmin -a -M "host=localhost dbname=coturn user=turn password=turn" -u bethod -r north.gov -p king-of-north --sha256
   
-  Short-term credentials mechanism:
+  Admin users:
    
   $ bin/turnadmin -A -M "host=localhost dbname=coturn user=turn password=turn" -u gorst -p hero
-  $ bin/turnadmin -A -M "host=localhost dbname=coturn user=turn password=turn" -u ninefingers -p youhavetoberealistic 
+  $ bin/turnadmin -A -M "host=localhost dbname=coturn user=turn password=turn" -u ninefingers -p youhavetoberealistic -r north.gov
 
 7) Now we can use mysql in the turnserver.
 
@@ -1124,10 +1126,10 @@ Redis TURN admin commands:
   Long-term credentials mechanism with SHA256 extension:
   $ bin/turnadmin -a -N "host=localhost dbname=2 user=turn password=turn" -u bethod -r north.gov -p king-of-north --sha256
   
-  Short-term credentials mechanism:
+  Admin users:
    
   $ bin/turnadmin -A -N "host=localhost dbname=2 user=turn password=turn" -u gorst -p hero
-  $ bin/turnadmin -A -N "host=localhost dbname=2 user=turn password=turn" -u ninefingers -p youhavetoberealistic 
+  $ bin/turnadmin -A -N "host=localhost dbname=2 user=turn password=turn" -u ninefingers -p youhavetoberealistic -r north.gov
   
 See the file testredisdbsetup.sh for the data structure examples.
 
@@ -1144,7 +1146,19 @@ Read the project wiki pages: http://code.google.com/p/coturn/w/list
 Also, check the project from page links to the TURN/WebRTC configuration examples.
 It may give you an idea how it can be done.
 
-XXII. Management interface
+XXII. HTTPS Management Interface
+
+The turnserver process provides an HTTPS Web access as statistics and basic management
+interface. The turnserver listens to incoming HTTPS admin connections on the same ports
+as the main TURN/STUN listener. The Web admin pages are basic and self-explanatory.
+
+To make the HTTPS interface active, the database table admin_user must be
+populated with the admin user account(s). An admin user can be a superuser
+(if not assigned to a particular realm) or a restricted user (if assigned to
+a realm). The restricted admin users can perform only limited actions, within
+their corresponding realms.
+
+XXIII. Telnet CLI management interface
 
 You have a telnet interface (enabled by default) to access the turnserver process, 
 to view its state, to gather some statistical information, and to make some changes 
@@ -1152,7 +1166,7 @@ on-the-fly.
 
 You can access that CLI interface with telnet or putty program (in telnet mode). 
 The process by default listens to port 5766 on IP address 127.0.0.1 for the telnet
-connections. 
+connections.
 
 WARNING: all telnet communications are going unencrypted over the network. For
 security reasons, we advise using the loopback IP addresses for CLI (127.0.0.1 
@@ -1161,7 +1175,7 @@ transferred over the network unencrypted, too. So sticking to the local system
 CLI access, and accessing the turnserver system terminal with ssh only, would 
 be a wise decision.
 
-XXIII. ALPN support.
+XXIV. ALPN support.
 
 Starting with version 4.3.2.1, the TURN server supports the ALPN STUN 
 specifications (http://tools.ietf.org/html/draft-ietf-tram-alpn-08).

+ 2 - 2
Makefile.in

@@ -30,8 +30,8 @@ HIREDIS_MODS = src/apps/common/hiredis_libevent2.c
 USERDB_HEADERS = src/apps/relay/dbdrivers/dbdriver.h src/apps/relay/dbdrivers/dbd_sqlite.h src/apps/relay/dbdrivers/dbd_pgsql.h src/apps/relay/dbdrivers/dbd_mysql.h src/apps/relay/dbdrivers/dbd_mongo.h src/apps/relay/dbdrivers/dbd_redis.h
 USERDB_MODS = src/apps/relay/dbdrivers/dbdriver.c src/apps/relay/dbdrivers/dbd_sqlite.c src/apps/relay/dbdrivers/dbd_pgsql.c src/apps/relay/dbdrivers/dbd_mysql.c src/apps/relay/dbdrivers/dbd_mongo.c src/apps/relay/dbdrivers/dbd_redis.c
 
-SERVERAPP_HEADERS = src/apps/relay/userdb.h src/apps/relay/tls_listener.h src/apps/relay/mainrelay.h src/apps/relay/turncli.h src/apps/relay/dtls_listener.h src/apps/relay/libtelnet.h ${HIREDIS_HEADERS} ${USERDB_HEADERS}
-SERVERAPP_MODS = src/apps/relay/mainrelay.c src/apps/relay/netengine.c src/apps/relay/libtelnet.c src/apps/relay/turncli.c src/apps/relay/userdb.c src/apps/relay/tls_listener.c src/apps/relay/dtls_listener.c ${HIREDIS_MODS} ${USERDB_MODS}
+SERVERAPP_HEADERS = src/apps/relay/userdb.h src/apps/relay/tls_listener.h src/apps/relay/mainrelay.h src/apps/relay/turn_admin_server.h src/apps/relay/dtls_listener.h src/apps/relay/libtelnet.h ${HIREDIS_HEADERS} ${USERDB_HEADERS}
+SERVERAPP_MODS = src/apps/relay/mainrelay.c src/apps/relay/netengine.c src/apps/relay/libtelnet.c src/apps/relay/turn_admin_server.c src/apps/relay/userdb.c src/apps/relay/tls_listener.c src/apps/relay/dtls_listener.c ${HIREDIS_MODS} ${USERDB_MODS}
 SERVERAPP_DEPS = ${SERVERTURN_MODS} ${SERVERTURN_DEPS} ${SERVERAPP_MODS} ${SERVERAPP_HEADERS} ${COMMON_DEPS} ${IMPL_DEPS} lib/libturnclient.a
 
 TURN_BUILD_RESULTS = bin/turnutils_stunclient bin/turnutils_rfc5769check bin/turnutils_uclient bin/turnserver bin/turnutils_peer lib/libturnclient.a include/turn/ns_turn_defs.h sqlite_empty_db

+ 6 - 6
README.turnadmin

@@ -51,15 +51,15 @@ Commands:
 
 -a, --add       	Add or update a long-term user.
 
--A, --add-st    	Add or update a short-term credentials mechanism user.
+-A, --add-admin    	Add or update an admin user.
 
 -d, --delete		Delete a long-term user.
 
--D, --delete-st		Delete a short-term user.
+-D, --delete-admin		Delete an admin user.
 
 -l, --list		List long-term users in the database.
 
--L, --list-st		List short-term users in the database.
+-L, --list-admin		List admin users in the database.
 
 -s, --set-secret=<value> Add shared secret for TURN RESP API
 
@@ -92,7 +92,7 @@ Options with required values:
 -N, --redis-userdb	Redis user database connection string.
 			See the --redis-userdb option in the turnserver section.
 -u, --user		User name.
--r, --realm		Realm, for long-term credentials mechanism only.
+-r, --realm		Realm.
 -p, --password		Password.
 -o, --origin		Origin
 -H, --sha256	Use SHA256 as the keys hash function (a non-standard feature). 
@@ -119,7 +119,7 @@ List all long-term users in MySQL database:
 
 $ turnadmin -l --mysql-userdb="<db-connection-string>" -r <realm>
 
-List all short-term users in Redis database:
+List all admin users in Redis database:
 
 $ turnadmin -L --redis-userdb="<db-connection-string>"
 
@@ -233,7 +233,7 @@ to see the man page.
 	
 	Po-sheng Lin <[email protected]>
 	
-	Peter Dunkley <peter.dunkley@crocodilertc.net>
+	Peter Dunkley <peter.dunkley@acision.com>
 	
 	Mutsutoshi Yoshimoto <[email protected]>
 

+ 32 - 26
README.turnserver

@@ -85,8 +85,8 @@ User database settings:
 		/usr/local/var/db/turndb or /var/lib/turn/turndb).
 				  
 -e, --psql-userdb	User database connection string for PostgreSQL.
-		This database can be used for long-term and short-term 
-		credentials mechanisms, and it can store the secret value 
+		This database can be used for long-term credentials mechanism,
+		and it can store the secret value 
 		for secret-based timed authentication in TURN RESP API.
 		The connection string format is like that:
 		 
@@ -103,8 +103,8 @@ User database settings:
 		Also, see http://www.PostgreSQL.org for full PostgreSQL documentation.
 				  
 -M, --mysql-userdb	User database connection string for MySQL or MariaDB. 
-		This database can be used for long-term and short-term 
-		credentials mechanisms, and it can store the secret value for 
+		This database can be used for long-term credentials mechanism,
+		and it can store the secret value for 
 		secret-based timed authentication in TURN RESP API.
 		The connection string format is like that:
 		 
@@ -121,8 +121,8 @@ User database settings:
 		command options description).
 		
 -J, --mongo-userdb	User database connection string for MongoDB. 
-		This database can be used for long-term and short-term 
-		credentials mechanisms, and it can store the secret value 
+		This database can be used for long-term credentials mechanism,
+		and it can store the secret value 
 		for secret-based timed authentication in TURN RESP API.
 		The connection string format is like that:
 		 
@@ -134,8 +134,8 @@ User database settings:
 		for full MongoDB documentation.
 		
 -N, --redis-userdb	User database connection string for Redis. 
-		This database can be used for long-term and short-term
-		credentials mechanisms, and it can store the secret 
+		This database can be used for long-term	credentials mechanism,
+		and it can store the secret 
 		value for secret-based timed authentication in TURN RESP API.
 		The connection string format is like that:
 		 
@@ -160,8 +160,6 @@ Flags:
 
 -a, --lt-cred-mech	Use long-term credentials mechanism (this one you need for WebRTC usage).
 
--A, --st-cred-mech	Use the short-term credentials mechanism.
-
 -z, --no-auth		Do not use any credentials mechanism, allow anonymous access. 
 			Opposite to -a and -A options. This is default option when no 
 			authentication-related options are set.
@@ -182,8 +180,6 @@ Flags:
 			This option is just turns on secret-based authentication.
 			The actual value of the secret is defined either by option static-auth-secret,
 			or can be found in the turn_secret table in the database.
-			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.
 			
@@ -191,8 +187,6 @@ Flags:
 
 --dh2066		Use 2066 bits predefined DH TLS key. Default size of the key is 1066.
 
---no-sslv2		Do not allow SSLv2 protocol.
-
 --no-sslv3		Do not allow SSLv3 protocol.
 
 --no-tlsv1		Do not allow TLSv1/DTLSv1 protocol.
@@ -300,7 +294,7 @@ Options with required values:
 			endpoints (the "plain" one and the "tls" one) are equivalent in terms of
 			functionality; but we keep both endpoints to satisfy the RFC 5766 specs.
 			For secure TCP connections, we currently support SSL version 3 and 
-			TLS versions 1.0, 1.1, 1.2. SSL2 "encapsulation mode" is also supported.
+			TLS versions 1.0, 1.1, 1.2.
 			For secure UDP connections, we support DTLS version 1.
 
 --alt-listening-port	Alternative listening port for UDP and TCP listeners;
@@ -382,8 +376,7 @@ Options with required values:
 			the key must be prepended with 0x symbols.
 			The key is calculated over the user name, 
 			the user realm, and the user password.
-			This setting may not be used with TURN REST API or
-			with short-term credentials mechanism.
+			This setting may not be used with TURN REST API.
 
 -r, --realm		The default realm to be used for the users when no explicit 
 			origin/realm relationship was found in the database, or if the TURN
@@ -560,8 +553,8 @@ WEBRTC USAGE
 This is a set of notes for the WebRTC users:
 
 1) WebRTC uses long-term authentication mechanism, so you have to use -a 
-option (or --lt-cred-mech). WebRTC relaying will not work with anonymous access 
-or with short-term authentication. With -a option, do not forget to set the 
+option (or --lt-cred-mech). WebRTC relaying will not work with anonymous
+access. With -a option, do not forget to set the 
 default realm (-r option). You will also have to set up the user accounts, 
 for that you have a number of options:
 
@@ -733,9 +726,7 @@ For long-term credentials, you have to set the "keys" for the users; the "keys"
 by the turnadmin utility. For the key generation, you need username, password and the realm. 
 All users in the database must use the same realm value; if down the road you will decide 
 to change the realm name, then you will have to re-generate all user keys (that can be done 
-in a batch script). If you are using short-term credentials, then you use open passwords 
-in the database; you will have to make sure that nobody can access the database outside of 
-the TURN server box. See the file turndb/testsqldbsetup.sql as an example.
+in a batch script). See the file turndb/testsqldbsetup.sql as an example.
 
 4) The same is true for MySQL database. The same schema file is applicable. 
 The same considerations are applicable. 
@@ -744,8 +735,7 @@ The same considerations are applicable.
 it can be found (in the form of explanation) in schema.userdb.redis. 
 Also, in Redis you can store both "keys" and open passwords (for long term credentials) - 
 the "open password" option is less secure but more convenient for low-security environments. 
-For short-term credentials, you will use open passwords only. See the file 
-turndb/testredisdbsetup.sh as an example. 
+See the file turndb/testredisdbsetup.sh as an example. 
 
 6) If a database is used, then users can be divided into multiple independent realms. Each realm
 can be administered separately, and each realm can have its own set of users and its own
@@ -832,7 +822,23 @@ in the following directories:
 If all efforts failed (due to the system permission settings) then all 
 log messages are sent only to the standard output of the process.
 
-This behavior can be controlled by --log-file, --syslog and --no-stdout-log options.
+This behavior can be controlled by --log-file, --syslog and --no-stdout-log
+options.
+
+=================================
+
+HTTPS MANAGEMENT INTERFACE
+
+The turnserver process provides an HTTPS Web access as statistics and basic
+management interface. The turnserver listens to incoming HTTPS admin 
+connections on the same ports as the main TURN/STUN listener. The Web admin
+pages are basic and self-explanatory.
+
+To make the HTTPS interface active, the database table admin_user must be
+populated with the admin user account(s). An admin user can be a superuser
+(if not assigned to a particular realm) or a restricted user (if assigned to
+a realm). The restricted admin users can perform only limited actions, within
+their corresponding realms.
 
 =================================
 
@@ -941,7 +947,7 @@ https://groups.google.com/forum/?fromgroups=#!forum/turn-server-project-rfc5766-
 	
 	Po-sheng Lin <[email protected]>
 	
-	Peter Dunkley <peter.dunkley@crocodilertc.net>
+	Peter Dunkley <peter.dunkley@acision.com>
 	
 	Mutsutoshi Yoshimoto <[email protected]>
 

+ 1 - 5
README.turnutils

@@ -84,10 +84,6 @@ Flags:
 
 -g      Set DONT_FRAGMENT parameter in TURN requests.
 
--A	use short-term credentials mechanism for authentication. 
-	By default, the program uses the long-term credentials mechanism 
-	if authentication is required.
-
 -D	Do mandatory channel padding even for UDP (like pjnath).
 
 -N	do negative tests (some limited cases only).
@@ -329,7 +325,7 @@ SEE ALSO
 	
 	Po-sheng Lin <[email protected]>
 	
-	Peter Dunkley <peter.dunkley@crocodilertc.net>
+	Peter Dunkley <peter.dunkley@acision.com>
 	
 	Mutsutoshi Yoshimoto <[email protected]>
 	

+ 7 - 0
STATUS

@@ -112,6 +112,13 @@ compatibility.
 48) DTLS1.2 supported.
 
 49) ALPN stun.turn and stun.nat-discovery supported.
+
+50) SSLv2 support cancelled.
+
+51) The short-term credentials server-side support cancelled (still
+supported in the client library).
+
+52) Web HTTPS admin interface implemented.
  
 Things to be implemented in future (the development roadmap) 
 are described in the TODO file.

+ 3 - 12
TODO

@@ -75,23 +75,14 @@
 	deeply "hidden" in the network infrastructure that the 
 	significant code complication may be unjustified.
 
-2) HTTP or GUI status monitor and management.
-
-	For enterprise users, a management (configuration, status 
-	and statistics) GUI has to be implemented. Currently, all 
-	these features are available through the shell command 
-	line, telnet client and through Redis command line.
-
-3) Traffic recording (for selected allocations).
+2) Traffic recording (for selected allocations).
 
 	That would be a helpful feature for a large enterprise 
 	(for testing and security purposes). 
 
-4) Ganglia monitoring.
-
-5) Web API to the database (oAuth keys, for example).
+3) Ganglia monitoring.
 
-6) Key exchange mechanism for oAuth.
+4) Key exchange mechanism for oAuth.
 
 ==================================================================
 

+ 7 - 14
examples/etc/turnserver.conf

@@ -24,7 +24,7 @@
 # endpoints (the "plain" one and the "tls" one) are equivalent in terms of
 # functionality; but we keep both endpoints to satisfy the RFC 5766 specs.
 # For secure TCP connections, we currently support SSL version 3 and 
-# TLS version 1.0, 1.1 and 1.2. SSL2 "encapculation mode" is also supported.
+# TLS version 1.0, 1.1 and 1.2.
 # For secure UDP connections, we support DTLS version 1.
 #
 #tls-listening-port=5349
@@ -169,11 +169,6 @@
 #
 #lt-cred-mech
 
-# Uncomment to use short-term credential mechanism.
-# By default no credentials mechanism is used (any user allowed).
-#
-#st-cred-mech
-
 # This option is opposite to lt-cred-mech or st-cred-mech. 
 # (TURN Server with no-auth option allows anonymous access).
 # If neither option is defined, and no users are defined,
@@ -218,13 +213,12 @@
 #
 #server-name=blackdow.carleon.gov
 
-# Flag to support oAuth authentication.
+# Flag that allows 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
-# mechanism.
+# This option cannot be used with TURN REST API.
 # 'Static' user accounts are NOT dynamically checked by the turnserver process, 
 # so that they can NOT be changed while the turnserver is running.
 #
@@ -259,7 +253,7 @@
 
 # PostgreSQL database connection string in the case that we are using PostgreSQL
 # as the user database.
-# This database can be used for long-term and short-term credential mechanisms
+# This database can be used for long-term credential mechanism
 # and it can store the secret value for secret-based timed authentication in TURN RESP API. 
 # See http://www.postgresql.org/docs/8.4/static/libpq-connect.html for 8.x PostgreSQL
 # versions connection string format, see 
@@ -270,7 +264,7 @@
 
 # MySQL database connection string in the case that we are using MySQL
 # as the user database.
-# This database can be used for long-term and short-term credential mechanisms
+# This database can be used for long-term credential mechanism
 # and it can store the secret value for secret-based timed authentication in TURN RESP API.
 #
 # Optional connection string parameters for the secure communications (SSL): 
@@ -284,7 +278,7 @@
 
 # MongoDB database connection string in the case that we are using MongoDB
 # as the user database.
-# This database can be used for long-term and short-term credential mechanisms
+# This database can be used for long-term credential mechanism
 # and it can store the secret value for secret-based timed authentication in TURN RESP API. 
 # Use string format is described at http://hergert.me/docs/mongo-c-driver/mongoc_uri.html
 #
@@ -292,7 +286,7 @@
 
 # Redis database connection string in the case that we are using Redis
 # as the user database.
-# This database can be used for long-term and short-term credential mechanisms
+# This database can be used for long-term credential mechanism
 # and it can store the secret value for secret-based timed authentication in TURN RESP API. 
 # Use string format as below (space separated parameters, all optional):
 #
@@ -625,7 +619,6 @@
 
 # Do not allow an SSL/TLS/DTLS version of protocol
 #
-#no-sslv2
 #no-sslv3
 #no-tlsv1
 #no-tlsv1_1

+ 2 - 2
examples/scripts/longtermsecure/secure_relay.sh

@@ -22,7 +22,7 @@
 # 9) "--pkey=turn_server_pkey.pem" sets the OpenSSL private key name.
 # 10) "--log-file=stdout" means that all log output will go to the stdout. 
 # 11) "-v" means normal verbose mode (with some moderate logging).
-# 12) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
+# 12) --cipher-list=ALL means that we support all OpenSSL ciphers
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -32,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 --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:youhavetoberealistic --user=gorst:hero -r north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -v --cipher-list=ALL:SSLv2 $@
+PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:youhavetoberealistic --user=gorst:hero -r north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -v --cipher-list=ALL $@

+ 2 - 2
examples/scripts/longtermsecure/secure_relay_cert.sh

@@ -25,7 +25,7 @@
 # 10) --CA-file sets the CA file for client certificate check.
 # 11) "--log-file=stdout" means that all log output will go to the stdout.
 # 12) "-v" means normal verbose mode (with some moderate logging).
-# 13) --cipher-list="ALL:SSLv2:!eNULL:!aNULL:!NULL" measn "all ciphers, except anonymous".
+# 13) --cipher-list="ALL:!eNULL:!aNULL:!NULL" measn "all ciphers, except anonymous".
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -35,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 --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:youhavetoberealistic --user=bolt:kwyjibo -r bolt.co --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --CA-file=turn_server_cert.pem --log-file=stdout -v --cipher-list="ALL:SSLv2:!eNULL:!aNULL:!NULL" $@
+PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:youhavetoberealistic --user=bolt:kwyjibo -r bolt.co --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --CA-file=turn_server_cert.pem --log-file=stdout -v --cipher-list="ALL:!eNULL:!aNULL:!NULL" $@

+ 3 - 3
examples/scripts/longtermsecuredb/secure_relay_with_db_mongo.sh

@@ -20,8 +20,8 @@
 # 7) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. 
 # 8) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name.
 # 9) "--log-file=stdout" means that all log output will go to the stdout.
-# 10) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
-# 11) --oauth - accept oAuth security dialog
+# 10) --cipher-list=ALL means that we support all OpenSSL ciphers
+# 11) --oauth - mandate oAuth security dialog
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -31,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 --oauth $@
+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 --oauth $@

+ 3 - 3
examples/scripts/longtermsecuredb/secure_relay_with_db_mysql.sh

@@ -21,8 +21,8 @@
 # 7) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. 
 # 8) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name.
 # 9) "--log-file=stdout" means that all log output will go to the stdout.
-# 10) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
-# 11) --oauth - accept oAuth security dialog
+# 10) --cipher-list=ALL means that we support all OpenSSL ciphers
+# 11) --oauth - mandate oAuth security dialog
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -32,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 --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 $@
+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 --oauth $@

+ 3 - 3
examples/scripts/longtermsecuredb/secure_relay_with_db_mysql_ssl.sh

@@ -22,8 +22,8 @@
 # 7) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. 
 # 8) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name.
 # 9) "--log-file=stdout" means that all log output will go to the stdout.
-# 10) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
-# 11) --oauth - accept oAuth security dialog
+# 10) --cipher-list=ALL means that we support all OpenSSL ciphers
+# 11) --oauth - mandate oAuth security dialog
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -33,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 --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 $@
+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 --oauth $@

+ 4 - 4
examples/scripts/longtermsecuredb/secure_relay_with_db_psql.sh

@@ -21,8 +21,8 @@
 # 7) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. 
 # 8) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name.
 # 9) "--log-file=stdout" means that all log output will go to the stdout.
-# 10) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
-# 11) --oauth - accept oAuth security dialog
+# 10) --cipher-list=ALL means that we support all OpenSSL ciphers
+# 11) --oauth - mandate oAuth security dialog
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -32,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 --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 $@ 
+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 --oauth $@ 
 
 # Newer PostgreSQL style connection string example:
-# PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --server-name="blackdow.carleon.gov" -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --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 $@
+# 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 --oauth $@

+ 3 - 3
examples/scripts/longtermsecuredb/secure_relay_with_db_redis.sh

@@ -24,8 +24,8 @@
 # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. 
 # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name.
 # 10) "--log-file=stdout" means that all log output will go to the stdout.
-# 11) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
-# 12) --oauth - accept oAuth security dialog
+# 11) --cipher-list=ALL means that we support all OpenSSL ciphers
+# 12) --oauth - mandate oAuth security dialog
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -35,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 --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 $@
+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 --oauth $@

+ 3 - 3
examples/scripts/longtermsecuredb/secure_relay_with_db_sqlite.sh

@@ -20,8 +20,8 @@
 # 7) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. 
 # 8) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name.
 # 9) "--log-file=stdout" means that all log output will go to the stdout.
-# 10) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
-# 11) --oauth - accept oAuth security dialog
+# 10) --cipher-list=ALL means that we support all OpenSSL ciphers
+# 11) --oauth - mandate oAuth security dialog
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -31,5 +31,5 @@ fi
 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/
 export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/
 
-PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --server-name="blackdow.carleon.gov" -v --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 -r north.gov --db="var/db/turndb" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL:SSLv2 --oauth $@ 
+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 --db="var/db/turndb" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL --oauth $@ 
 

+ 2 - 2
examples/scripts/mobile/mobile_relay.sh

@@ -23,7 +23,7 @@
 # 10) "--log-file=stdout" means that all log output will go to the stdout. 
 # 11) "-v" means normal verbose mode (with some moderate logging).
 # 12) "--mobility" turns on the Mobile ICE TURN functionality.
-# 13) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
+# 13) --cipher-list=ALL means that we support all OpenSSL ciphers
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -33,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 --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee --user=gorst:hero -r north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -v --mobility --cipher-list=ALL:SSLv2 $@
+PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee --user=gorst:hero -r north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -v --mobility --cipher-list=ALL $@

+ 8 - 6
examples/scripts/readme.txt

@@ -6,8 +6,8 @@ functionality illustration.
 2) "basic" directory contains set of scripts which works together to demonstrate 
 very basic anynymous functionality of the TURN server. The "peer.sh" must be used, too.
 
-3) "longtermsecure" directory contains set of scripts demonstrating the long-term authentication
-mechanism (peer.sh to be used, too).
+3) "longtermsecure" directory contains set of scripts demonstrating the long-term
+authentication mechanism (peer.sh to be used, too).
 
 4) "longtermsecuredb" shows how to start TURN server with database. The clients from the
 directory "longtermsecure" can be used with the relay scripts in the "longtermsecuredb" 
@@ -16,11 +16,13 @@ be set for these scripts to work correctly.
 
 5) "restapi" shows how to use TURN REST API.
 
-6) "shorttermsecure" shows how to use the short-term authentication mechanism. The short term
-mechanism is always used with the database.
-
-7) "loadbalance" shows how to use the simple load-balancing mechanism based upon the
+6) "loadbalance" shows how to use the simple load-balancing mechanism based upon the
 ALTERNATE-SERVER functionality.
 
+7) "selfloadbalance" shows how to use the "self-load-balance" TURN server capabilities.
+
+8) "mobile" shows the "mobile" connections - how the TURN session can change its client
+address. 
+
 
 

+ 2 - 2
examples/scripts/restapi/secure_relay_secret.sh

@@ -22,7 +22,7 @@
 # 10) "--log-file=stdout" means that all log output will go to the stdout.
 # 11) "-q 100" means that single user can create no more than 100 sessions
 # 12) "-Q 300" means that there may be no more than 300 sessions totally
-# 13) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2.
+# 13) --cipher-list=ALL means that we support all OpenSSL ciphers
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -32,5 +32,5 @@ 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 --use-auth-secret --static-auth-secret=logen --realm=north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -q 100 -Q 300 --cipher-list=ALL: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 --use-auth-secret --static-auth-secret=logen --realm=north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -q 100 -Q 300 --cipher-list=ALL $@
 

+ 2 - 2
examples/scripts/restapi/secure_relay_secret_with_db_mongo.sh

@@ -24,7 +24,7 @@
 # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. 
 # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name.
 # 10) "--log-file=stdout" means that all log output will go to the stdout.
-# 11) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
+# 11) --cipher-list=ALL means that we support all OpenSSL ciphers
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -34,4 +34,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 --use-auth-secret --realm=north.gov --mongo-userdb="mongodb://localhost/coturn" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL: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 --use-auth-secret --realm=north.gov --mongo-userdb="mongodb://localhost/coturn" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL $@

+ 2 - 2
examples/scripts/restapi/secure_relay_secret_with_db_mysql.sh

@@ -25,7 +25,7 @@
 # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. 
 # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name.
 # 10) "--log-file=stdout" means that all log output will go to the stdout.
-# 11) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
+# 11) --cipher-list=ALL means that we support all OpenSSL ciphers
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -35,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 --use-auth-secret --realm=north.gov --mysql-userdb="host=localhost dbname=coturn user=turn password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL: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 --use-auth-secret --realm=north.gov --mysql-userdb="host=localhost dbname=coturn user=turn password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL $@

+ 2 - 2
examples/scripts/restapi/secure_relay_secret_with_db_psql.sh

@@ -25,7 +25,7 @@
 # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. 
 # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name.
 # 10) "--log-file=stdout" means that all log output will go to the stdout.
-# 11) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2.
+# 11) --cipher-list=ALL means that we support all OpenSSL ciphers
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -35,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 --use-auth-secret --realm=north.gov --psql-userdb="host=localhost dbname=coturn user=turn password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL: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 --use-auth-secret --realm=north.gov --psql-userdb="host=localhost dbname=coturn user=turn password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL $@

+ 2 - 2
examples/scripts/restapi/secure_relay_secret_with_db_redis.sh

@@ -25,7 +25,7 @@
 # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. 
 # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name.
 # 10) "--log-file=stdout" means that all log output will go to the stdout.
-# 11) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2.
+# 11) --cipher-list=ALL means that we support all OpenSSL ciphers
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -35,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 --use-auth-secret --realm=north.gov --redis-userdb="ip=127.0.0.1 dbname=2 password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --redis-statsdb="ip=127.0.0.1 dbname=3 password=turn connect_timeout=30" --cipher-list=ALL: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 --use-auth-secret --realm=north.gov --redis-userdb="ip=127.0.0.1 dbname=2 password=turn connect_timeout=30" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --redis-statsdb="ip=127.0.0.1 dbname=3 password=turn connect_timeout=30" --cipher-list=ALL $@

+ 2 - 2
examples/scripts/restapi/secure_relay_secret_with_db_sqlite.sh

@@ -24,7 +24,7 @@
 # 8) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. 
 # 9) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name.
 # 10) "--log-file=stdout" means that all log output will go to the stdout.
-# 11) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2
+# 11) --cipher-list=ALL means that we support all OpenSSL ciphers
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -34,4 +34,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 --use-auth-secret --realm=north.gov --db="var/db/turndb" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL: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 --use-auth-secret --realm=north.gov --db="var/db/turndb" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL $@

+ 2 - 2
examples/scripts/selfloadbalance/secure_relay.sh

@@ -29,7 +29,7 @@
 # 11) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name.
 # 12) "--log-file=stdout" means that all log output will go to the stdout. 
 # 13) "-v" means normal verbose mode (with some moderate logging).
-# 14) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2.
+# 14) --cipher-list=ALL means that we support all OpenSSL ciphers
 # Other parameters (config file name, etc) are default.
 
 if [ -d examples ] ; then
@@ -39,4 +39,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 --aux-server=127.0.0.1:12345 --aux-server=[::1]:12345 --aux-server=127.0.0.1:12346 --aux-server=[::1]:12346 --udp-self-balance --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:youhavetoberealistic --user=gorst:hero -r north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL:SSLv2 $@
+PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver --aux-server=127.0.0.1:12345 --aux-server=[::1]:12345 --aux-server=127.0.0.1:12346 --aux-server=[::1]:12346 --udp-self-balance --syslog -a -L 127.0.0.1 -L ::1 -E 127.0.0.1 -E ::1 --max-bps=3000000 -f -m 10 --min-port=32355 --max-port=65535 --user=ninefingers:youhavetoberealistic --user=gorst:hero -r north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout --cipher-list=ALL $@

+ 0 - 34
examples/scripts/shorttermsecure/secure_relay_short_term_mech.sh

@@ -1,34 +0,0 @@
-#!/bin/sh
-#
-# This is an example how to start a TURN Server in
-# secure mode with short-term security mechanism - see option -A
-# that means "use short-term credential mechanism".
-#
-# The user credentials are stored in the database.
-#
-# We listen on available interfaces here, and we use the "external" IPs
-# for relay endpoints allocation.
-#
-# Other options:
-#
-# 1) set bandwidth limit on client session 3000000 bytes per second (--max-bps).
-# 2) use fingerprints (-f)
-# 3) use 3 relay threads (-m 3)
-# 4) use min UDP relay port 32355 and max UDP relay port 65535
-# 5) --db="var/db/turndb" means that SQLite database "var/db/turndb" will be used.
-# 6) "--cert=example_turn_server_cert.pem" sets the OpenSSL certificate file name. 
-# 7) "--pkey=example_turn_server_pkey.pem" sets the OpenSSL private key name.
-# 8) "--log-file=stdout" means that all log output will go to the stdout.
-# 9) -E 127.0.0.1 and -E :;1 sets the relay addresses, in this case for loopback 
-# communications only.
-# 10) --cipher-list=ALL:SSLv2 means that we support all OpenSSL ciphers, including SSLv2.
-# Other parameters (config file name, etc) are default.
-
-if [ -d examples ] ; then
-       cd examples
-fi
-
-export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/
-export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:/usr/local/lib/:/usr/local/mysql/lib/
-
-PATH="./bin/:../bin/:../../bin/:${PATH}" turnserver -v --syslog -A --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535  --db="var/db/turndb" --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -E 127.0.0.1 -E ::1 --cipher-list=ALL:SSLv2 $@

+ 0 - 31
examples/scripts/shorttermsecure/secure_tcp_client_c2c_tcp_relay_short_term.sh

@@ -1,31 +0,0 @@
-#!/bin/sh
-#
-# This is an example of a script to run a "secure" TURN TCP client
-# with the short-term credentials mechanism and with
-# TCP relay endpoints (RFC 6062).
-#
-# Options:
-#
-# 1) -T is present, it means that TCP networking is used, with TCP relay endpoints (RFC 6062).
-# 5) -n 1000 means 1000 messages per single emulated client. Messages
-# are sent with interval of 20 milliseconds, to emulate an RTP stream.
-# 6) -m 10 means that 10 clients are emulated.
-# 7) -l 170 means that the payload size of the packets is 170 bytes 
-# (like average audio RTP packet).
-# 8) -y means that the clients will connect to the 'neighbor' clients, no peer app will be used.
-# 9) -g means "set DONT_FRAGMENT parameter in TURN requests".
-# 10) -A sets the short-term credentials mechanism.
-# 11) -u gorst sets the client user name.
-# 12) -w hero sets the password for the account as "hero".
-# 13) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here
-# to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back.
-#
-
-if [ -d examples ] ; then
-       cd examples
-fi
-
-export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/
-
-PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -T -n 1000 -m 10 -l 170 -y -g -A -u gorst -w hero $@ ::1
-

+ 0 - 30
examples/scripts/shorttermsecure/secure_udp_client_short_term.sh

@@ -1,30 +0,0 @@
-#!/bin/sh
-#
-# This is an example of a script to run a "secure" TURN UDP client
-# with short-term credential mechanism.
-#
-# Options:
-#
-# 1) -t is absent, it means that UDP networking is used.
-# 5) -n 1000 means 1000 messages per single emulated client. Messages
-# are sent with interval of 20 milliseconds, to emulate an RTP stream.
-# 6) -m 10 means that 10 clients are emulated.
-# 7) -l 170 means that the payload size of the packets is 170 bytes 
-# (like average audio RTP packet).
-# 8) -e 127.0.0.1 means that the clients will use peer address 127.0.0.1.
-# 9) -g means "set DONT_FRAGMENT parameter in TURN requests".
-# 10) -A means that the short-term credentials mechanism is used.
-# 11) -u ninefingers sets the client user name.
-# 12) -w youhavetoberealistic sets the password for the user account as "youhavetoberealistic".
-# 13) -s option means that the client will be using "send" indication for data trasfer.
-# 14) ::1 (the last parameter) is the TURN Server IP address. We use IPv6 here
-# to illustrate how the TURN Server convert the traffic from IPv6 to IPv4 and back.
-#
-
-if [ -d examples ] ; then
-       cd examples
-fi
-
-export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib/
-
-PATH=examples/bin/:../bin/:./bin/:${PATH} turnutils_uclient -n 1000 -m 10 -l 170 -e 127.0.0.1 -X -g -A -u ninefingers -w youhavetoberealistic -s $@ ::1

BIN
examples/var/db/turndb


+ 10 - 10
man/man1/turnadmin.1

@@ -1,5 +1,5 @@
 .\" Text automatically generated by txt2man
-.TH TURN 1 "14 December 2014" "" ""
+.TH TURN 1 "24 January 2015" "" ""
 .SH GENERAL INFORMATION
 
 \fIturnadmin\fP is a TURN administration tool. This tool can be used to manage 
@@ -74,24 +74,24 @@ Generate key for a long\-term credentials mechanism user.
 Add or update a long\-term user.
 .TP
 .B
-\fB\-A\fP, \fB\-\-add\-st\fP
-Add or update a short\-term credentials mechanism user.
+\fB\-A\fP, \fB\-\-add\-admin\fP
+Add or update an admin user.
 .TP
 .B
 \fB\-d\fP, \fB\-\-delete\fP
 Delete a long\-term user.
 .TP
 .B
-\fB\-D\fP, \fB\-\-delete\-st\fP
-Delete a short\-term user.
+\fB\-D\fP, \fB\-\-delete\-admin\fP
+Delete an admin user.
 .TP
 .B
 \fB\-l\fP, \fB\-\-list\fP
 List long\-term users in the database.
 .TP
 .B
-\fB\-L\fP, \fB\-\-list\-st\fP
-List short\-term users in the database.
+\fB\-L\fP, \fB\-\-list\-admin\fP
+List admin users in the database.
 .PP
 \fB\-s\fP, \fB\-\-set\-secret\fP=<value> Add shared secret for TURN RESP API
 .TP
@@ -162,7 +162,7 @@ User name.
 .TP
 .B
 \fB\-r\fP, \fB\-\-realm\fP
-Realm, for long\-term credentials mechanism only.
+Realm.
 .TP
 .B
 \fB\-p\fP, \fB\-\-password\fP
@@ -211,7 +211,7 @@ List all long\-term users in MySQL database:
 .PP
 $ \fIturnadmin\fP \fB\-l\fP \fB\-\-mysql\-userdb\fP="<db\-connection\-string>" \fB\-r\fP <realm>
 .PP
-List all short\-term users in Redis database:
+List all admin users in Redis database:
 .PP
 $ \fIturnadmin\fP \fB\-L\fP \fB\-\-redis\-userdb\fP="<db\-connection\-string>"
 .PP
@@ -322,7 +322,7 @@ Vladimir Tsanev <[email protected]>
 .PP
 Po\-sheng Lin <[email protected]>
 .PP
-Peter Dunkley <peter.dunkley@crocodilertc.net>
+Peter Dunkley <peter.dunkley@acision.com>
 .PP
 Mutsutoshi Yoshimoto <[email protected]>
 .PP

+ 32 - 31
man/man1/turnserver.1

@@ -1,5 +1,5 @@
 .\" Text automatically generated by txt2man
-.TH TURN 1 "14 December 2014" "" ""
+.TH TURN 1 "24 January 2015" "" ""
 .SH GENERAL INFORMATION
 
 The \fBTURN Server\fP project contains the source code of a TURN server and TURN client 
@@ -138,8 +138,8 @@ SQLite user database file name (default \- /var/db/turndb or
 .B
 \fB\-e\fP, \fB\-\-psql\-userdb\fP
 User database connection string for PostgreSQL.
-This database can be used for long\-term and short\-term 
-credentials mechanisms, and it can store the secret value 
+This database can be used for long\-term credentials mechanism,
+and it can store the secret value 
 for secret\-based timed authentication in TURN RESP API.
 The connection string format is like that:
 .RS
@@ -160,8 +160,8 @@ Also, see http://www.PostgreSQL.org for full PostgreSQL documentation.
 .B
 \fB\-M\fP, \fB\-\-mysql\-userdb\fP
 User database connection string for MySQL or MariaDB. 
-This database can be used for long\-term and short\-term 
-credentials mechanisms, and it can store the secret value for 
+This database can be used for long\-term credentials mechanism,
+and it can store the secret value for 
 secret\-based timed authentication in TURN RESP API.
 The connection string format is like that:
 .RS
@@ -182,8 +182,8 @@ command \fIoptions\fP description).
 .B
 \fB\-J\fP, \fB\-\-mongo\-userdb\fP
 User database connection string for MongoDB. 
-This database can be used for long\-term and short\-term 
-credentials mechanisms, and it can store the secret value 
+This database can be used for long\-term credentials mechanism,
+and it can store the secret value 
 for secret\-based timed authentication in TURN RESP API.
 The connection string format is like that:
 .RS
@@ -199,8 +199,8 @@ for full MongoDB documentation.
 .B
 \fB\-N\fP, \fB\-\-redis\-userdb\fP
 User database connection string for Redis. 
-This database can be used for long\-term and short\-term
-credentials mechanisms, and it can store the secret 
+This database can be used for long\-term credentials mechanism,
+and it can store the secret 
 value for secret\-based timed authentication in TURN RESP API.
 The connection string format is like that:
 .RS
@@ -239,10 +239,6 @@ per\-server setting.
 Use long\-term credentials mechanism (this one you need for WebRTC usage).
 .TP
 .B
-\fB\-A\fP, \fB\-\-st\-cred\-mech\fP
-Use the short\-term credentials mechanism.
-.TP
-.B
 \fB\-z\fP, \fB\-\-no\-auth\fP
 Do not use any credentials mechanism, allow anonymous access. 
 Opposite to \fB\-a\fP and \fB\-A\fP \fIoptions\fP. This is default option when no 
@@ -266,8 +262,6 @@ If you don't have a suitable id, the timestamp alone can be used.
 This option is just turns on secret\-based authentication.
 The actual value of the secret is defined either by option static\-auth\-secret,
 or can be found in the turn_secret table in the database.
-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
@@ -282,10 +276,6 @@ Use 566 bits predefined DH TLS key. Default size of the key is 1066.
 Use 2066 bits predefined DH TLS key. Default size of the key is 1066.
 .TP
 .B
-\fB\-\-no\-sslv2\fP
-Do not allow SSLv2 protocol.
-.TP
-.B
 \fB\-\-no\-sslv3\fP
 Do not allow SSLv3 protocol.
 .TP
@@ -453,7 +443,7 @@ Note: actually, "plain" TCP & UDP sessions can connect to the TLS & DTLS
 endpoints (the "plain" one and the "tls" one) are equivalent in terms of
 functionality; but we keep both endpoints to satisfy the RFC 5766 specs.
 For secure TCP connections, we currently support SSL version 3 and 
-TLS versions 1.0, 1.1, 1.2. SSL2 "encapsulation mode" is also supported.
+TLS versions 1.0, 1.1, 1.2.
 For secure UDP connections, we support DTLS version 1.
 .TP
 .B
@@ -559,8 +549,7 @@ by \fIturnadmin\fP command. In the second case,
 the key must be prepended with 0x symbols.
 The key is calculated over the user name, 
 the user realm, and the user password.
-This setting may not be used with TURN REST API or
-with short\-term credentials mechanism.
+This setting may not be used with TURN REST API.
 .TP
 .B
 \fB\-r\fP, \fB\-\-realm\fP
@@ -796,8 +785,8 @@ http://code.google.com/p/coturn/wiki/turn_performance_and_load_balance
 This is a set of notes for the WebRTC users:
 .IP 1) 4
 WebRTC uses long\-term authentication mechanism, so you have to use \fB\-a\fP
-option (or \fB\-\-lt\-cred\-mech\fP). WebRTC relaying will not work with anonymous access 
-or with short\-term authentication. With \fB\-a\fP option, do not forget to set the 
+option (or \fB\-\-lt\-cred\-mech\fP). WebRTC relaying will not work with anonymous
+access. With \fB\-a\fP option, do not forget to set the 
 default realm (\fB\-r\fP option). You will also have to set up the user accounts, 
 for that you have a number of \fIoptions\fP:
 .PP
@@ -988,9 +977,7 @@ For long\-term credentials, you have to set the "keys" for the users; the "keys"
 by the \fIturnadmin\fP utility. For the key generation, you need username, password and the realm. 
 All users in the database must use the same realm value; if down the road you will decide 
 to change the realm name, then you will have to re\-generate all user keys (that can be done 
-in a batch script). If you are using short\-term credentials, then you use open passwords 
-in the database; you will have to make sure that nobody can access the database outside of 
-the TURN server box. See the file turndb/testsqldbsetup.sql as an example.
+in a batch script). See the file turndb/testsqldbsetup.sql as an example.
 .IP 4) 4
 The same is true for MySQL database. The same schema file is applicable.
 The same considerations are applicable. 
@@ -999,8 +986,7 @@ The same is true for the Redis database, but the Redis database has aa different
 it can be found (in the form of explanation) in schema.userdb.redis. 
 Also, in Redis you can store both "keys" and open passwords (for long term credentials) \- 
 the "open password" option is less secure but more convenient for low\-security environments. 
-For short\-term credentials, you will use open passwords only. See the file 
-turndb/testredisdbsetup.sh as an example. 
+See the file turndb/testredisdbsetup.sh as an example. 
 .IP 6) 4
 If a database is used, then users can be divided into multiple independent realms. Each realm
 can be administered separately, and each realm can have its own set of users and its own
@@ -1089,7 +1075,22 @@ current directory
 If all efforts failed (due to the system permission settings) then all 
 log messages are sent only to the standard output of the process.
 .PP
-This behavior can be controlled by \fB\-\-log\-file\fP, \fB\-\-syslog\fP and \fB\-\-no\-stdout\-log\fP \fIoptions\fP.
+This behavior can be controlled by \fB\-\-log\-file\fP, \fB\-\-syslog\fP and \fB\-\-no\-stdout\-log\fP
+\fIoptions\fP.
+.PP
+=================================
+.SH HTTPS MANAGEMENT INTERFACE
+
+The \fIturnserver\fP process provides an HTTPS Web access as statistics and basic
+management interface. The \fIturnserver\fP listens to incoming HTTPS admin 
+connections on the same ports as the main TURN/STUN listener. The Web admin
+pages are basic and self\-explanatory.
+.PP
+To make the HTTPS interface active, the database table admin_user must be
+populated with the admin user \fBaccount\fP(s). An admin user can be a superuser
+(if not assigned to a particular realm) or a restricted user (if assigned to
+a realm). The restricted admin users can perform only limited actions, within
+their corresponding realms.
 .PP
 =================================
 .SH TELNET CLI
@@ -1191,7 +1192,7 @@ Vladimir Tsanev <[email protected]>
 .PP
 Po\-sheng Lin <[email protected]>
 .PP
-Peter Dunkley <peter.dunkley@crocodilertc.net>
+Peter Dunkley <peter.dunkley@acision.com>
 .PP
 Mutsutoshi Yoshimoto <[email protected]>
 .PP

+ 2 - 8
man/man1/turnutils.1

@@ -1,5 +1,5 @@
 .\" Text automatically generated by txt2man
-.TH TURN 1 "14 December 2014" "" ""
+.TH TURN 1 "24 January 2015" "" ""
 .SH GENERAL INFORMATION
 
 A set of turnutils_* programs provides some utility functionality to be used
@@ -122,12 +122,6 @@ IPv4 relay address explicitly requested.
 Set DONT_FRAGMENT parameter in TURN requests.
 .TP
 .B
-\fB\-A\fP
-use short\-term credentials mechanism for authentication. 
-By default, the program uses the long\-term credentials mechanism 
-if authentication is required.
-.TP
-.B
 \fB\-D\fP
 Do mandatory channel padding even for UDP (like pjnath).
 .TP
@@ -449,7 +443,7 @@ Vladimir Tsanev <[email protected]>
 .PP
 Po\-sheng Lin <[email protected]>
 .PP
-Peter Dunkley <peter.dunkley@crocodilertc.net>
+Peter Dunkley <peter.dunkley@acision.com>
 .PP
 Mutsutoshi Yoshimoto <[email protected]>
 .PP

+ 1 - 1
rpm/build.settings.sh

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

+ 4 - 8
rpm/turnserver.spec

@@ -1,5 +1,5 @@
 Name:		turnserver
-Version:	4.3.3.1
+Version:	4.4.1.1
 Release:	0%{dist}
 Summary:	Coturn TURN Server
 
@@ -44,8 +44,7 @@ STUN specs:
 The implementation fully supports the following client-to-TURN-server protocols:
 - UDP (per RFC 5766)
 - TCP (per RFC 5766 and RFC 6062)
-- TLS (per RFC 5766 and RFC 6062); SSL3/TLS1.0/TLS1.1/TLS1.2; SSL2 wrapping
-  supported
+- TLS (per RFC 5766 and RFC 6062); SSL3/TLS1.0/TLS1.1/TLS1.2
 - DTLS (experimental non-standard feature)
 
 Supported relay protocols:
@@ -62,7 +61,6 @@ authentication is required):
 Redis can also be used for status and statistics storage and notification.
 
 Supported TURN authentication mechanisms:
-- short-term
 - long-term
 - TURN REST API (a modification of the long-term mechanism, for time-limited
   secret-based authentication, for WebRTC applications)
@@ -249,10 +247,6 @@ fi
 %dir %{_datadir}/%{name}/scripts/selfloadbalance
 %{_datadir}/%{name}/scripts/selfloadbalance/secure_dos_attack.sh
 %{_datadir}/%{name}/scripts/selfloadbalance/secure_relay.sh
-%dir %{_datadir}/%{name}/scripts/shorttermsecure
-%{_datadir}/%{name}/scripts/shorttermsecure/secure_relay_short_term_mech.sh
-%{_datadir}/%{name}/scripts/shorttermsecure/secure_tcp_client_c2c_tcp_relay_short_term.sh
-%{_datadir}/%{name}/scripts/shorttermsecure/secure_udp_client_short_term.sh
 %dir %{_datadir}/%{name}/scripts/mobile
 %{_datadir}/%{name}/scripts/mobile/mobile_relay.sh
 %{_datadir}/%{name}/scripts/mobile/mobile_dtls_client.sh
@@ -294,6 +288,8 @@ fi
 %{_includedir}/turn/client/TurnMsgLib.h
 
 %changelog
+* Sat Jan 24 2015 Oleg Moskalenko <[email protected]>
+  - Sync to 4.4.1.1
 * Wed Dec 24 2014 Oleg Moskalenko <[email protected]>
   - Sync to 4.3.3.1
 * Sun Dec 14 2014 Oleg Moskalenko <[email protected]>

+ 0 - 8
src/apps/common/apputils.c

@@ -827,14 +827,6 @@ static const char* turn_get_method(const SSL_METHOD *method, const char* mdefaul
 			return mdefault;
 		else {
 
-#ifndef OPENSSL_NO_SSL2
-			if(method == SSLv2_server_method()) {
-					return "SSLv2";
-			} else if(method == SSLv2_client_method()) {
-					return "SSLv2";
-			} else
-#endif
-
 			if(method == SSLv3_server_method()) {
 				return "SSLv3";
 			} else if(method == SSLv3_client_method()) {

+ 422 - 211
src/apps/relay/dbdrivers/dbd_mongo.c

@@ -312,53 +312,6 @@ static int mongo_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
 	return ret;
 }
   
-static int mongo_get_user_pwd(u08bits *usname, st_password_t pwd) {
-  mongoc_collection_t * collection = mongo_get_collection("turnusers_st"); 
-
-	if(!collection)
-    return -1;
-    
-  bson_t query;
-  bson_init(&query);
-  BSON_APPEND_UTF8(&query, "name", (const char *)usname);
-
-  bson_t fields;
-  bson_init(&fields);
-  BSON_APPEND_INT32(&fields, "password", 1);
-  
-  mongoc_cursor_t * cursor;
-  cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 1, 0, &query, &fields, NULL);
-  
-  int ret = -1;
-
-  if (!cursor) {
-		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'turnusers_st'\n");
-  } else {
-    const bson_t * item;
-    uint32_t length;
-    bson_iter_t iter;
-    const char * value;
-    if (mongoc_cursor_next(cursor, &item)) {
-    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "password") && BSON_ITER_HOLDS_UTF8(&iter)) {
-        value = bson_iter_utf8(&iter, &length);
-
-				if(length < 1) {
-					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong password data for user %s, size in MongoDB is zero(0)\n", usname);
-				} else {
-					ns_bcopy(value, pwd, length);
-					pwd[length] = 0;
-					ret = 0;
-				}
-			}
-    }
-    mongoc_cursor_destroy(cursor);
-  }
-  mongoc_collection_destroy(collection);
-  bson_destroy(&query);
-  bson_destroy(&fields);
-  return ret;
-}
-  
 static int mongo_set_user_key(u08bits *usname, u08bits *realm, const char *key) {
   mongoc_collection_t * collection = mongo_get_collection("turnusers_lt"); 
 
@@ -425,8 +378,8 @@ static int mongo_set_oauth_key(oauth_key_data_raw *key) {
   return ret;
 }
   
-static int mongo_set_user_pwd(u08bits *usname, st_password_t pwd) {
-  mongoc_collection_t * collection = mongo_get_collection("turnusers_st");
+static int mongo_del_user(u08bits *usname, u08bits *realm) {
+  mongoc_collection_t * collection = mongo_get_collection("turnusers_lt");
 
 	if(!collection)
     return -1;
@@ -434,37 +387,7 @@ static int mongo_set_user_pwd(u08bits *usname, st_password_t pwd) {
   bson_t query;
   bson_init(&query);
   BSON_APPEND_UTF8(&query, "name", (const char *)usname);
-  
-  bson_t doc;
-  bson_init(&doc);
-  BSON_APPEND_UTF8(&doc, "name", (const char *)usname);
-  BSON_APPEND_UTF8(&doc, "password", (const char *)pwd);
-
-  int ret = -1;
-  
-  if (!mongoc_collection_update(collection, MONGOC_UPDATE_UPSERT, &query, &doc, NULL, NULL)) {
-    TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating secret key information\n");
-  } else {
-    ret = 0;
-  }
-  mongoc_collection_destroy(collection);
-  bson_destroy(&doc);
-  bson_destroy(&query);
-  return ret;
-}
-  
-static int mongo_del_user(u08bits *usname, int is_st, u08bits *realm) {
-  mongoc_collection_t * collection = mongo_get_collection(is_st ? "turnusers_st" : "turnusers_lt");
-
-	if(!collection)
-    return -1;
-    
-  bson_t query;
-  bson_init(&query);
-  BSON_APPEND_UTF8(&query, "name", (const char *)usname);
-  if(!is_st) {
-    BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
-  }
+  BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
   
   int ret = -1;    
 
@@ -501,20 +424,25 @@ static int mongo_del_oauth_key(const u08bits *kid) {
   return ret;
 }
   
-static int mongo_list_users(int is_st, u08bits *realm) {
-  const char * collection_name = is_st ? "turnusers_st" : "turnusers_lt";
-  mongoc_collection_t * collection = mongo_get_collection(collection_name); 
+static int mongo_list_users(u08bits *realm, secrets_list_t *users, secrets_list_t *realms)
+{
+  const char * collection_name = "turnusers_lt";
+  mongoc_collection_t * collection = mongo_get_collection(collection_name);
 
-	if(!collection)
+  u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+  if(!realm) realm=realm0;
+
+  if(!collection)
     return -1;
     
   bson_t query, child;
   bson_init(&query);
   bson_append_document_begin(&query, "$orderby", -1, &child);
+  bson_append_int32(&child, "realm", -1, 1);
   bson_append_int32(&child, "name", -1, 1);
   bson_append_document_end(&query, &child);
   bson_append_document_begin(&query, "$query", -1, &child);
-  if (!is_st && realm && realm[0]) {
+  if (realm && realm[0]) {
     BSON_APPEND_UTF8(&child, "realm", (const char *)realm);
   }
   bson_append_document_end(&query, &child);
@@ -522,7 +450,7 @@ static int mongo_list_users(int is_st, u08bits *realm) {
   bson_t fields;
   bson_init(&fields);
   BSON_APPEND_INT32(&fields, "name", 1);
-  if(!is_st) BSON_APPEND_INT32(&fields, "realm", 1);
+  BSON_APPEND_INT32(&fields, "realm", 1);
 
   mongoc_cursor_t * cursor;
   cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
@@ -541,14 +469,21 @@ static int mongo_list_users(int is_st, u08bits *realm) {
     	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "name") && BSON_ITER_HOLDS_UTF8(&iter)) {
     		value = bson_iter_utf8(&iter, &length);
     		if (length) {
-        		const char *realm = "";
-    			if (!is_st && bson_iter_init(&iter_realm, item) && bson_iter_find(&iter_realm, "realm") && BSON_ITER_HOLDS_UTF8(&iter_realm)) {
-    				realm = bson_iter_utf8(&iter_realm, &length);
+        		const char *rval = "";
+    			if (bson_iter_init(&iter_realm, item) && bson_iter_find(&iter_realm, "realm") && BSON_ITER_HOLDS_UTF8(&iter_realm)) {
+    				rval = bson_iter_utf8(&iter_realm, &length);
     			}
-    			if(realm && *realm) {
-    				printf("%s[%s]\n", value, realm);
+    			if(users) {
+    				add_to_secrets_list(users,value);
+    				if(realms) {
+    					if(rval && *rval) {
+    						add_to_secrets_list(realms,rval);
+    					} else {
+    						add_to_secrets_list(realms,(char*)realm);
+    					}
+    				}
     			} else {
-    				printf("%s\n", value);
+    				printf("%s[%s]\n", value, rval);
     			}
     		}
     	}
@@ -562,7 +497,7 @@ static int mongo_list_users(int is_st, u08bits *realm) {
   return ret;
 }
 
-static int mongo_list_oauth_keys(void) {
+static int mongo_list_oauth_keys(secrets_list_t *kids,secrets_list_t *hkdfs,secrets_list_t *teas,secrets_list_t *aas,secrets_list_t *tss,secrets_list_t *lts) {
 
   const char * collection_name = "oauth_key";
   mongoc_collection_t * collection = mongo_get_collection(collection_name);
@@ -635,9 +570,26 @@ static int mongo_list_oauth_keys(void) {
     	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);
+    	if(kids) {
+    		add_to_secrets_list(kids,key->kid);
+    		add_to_secrets_list(hkdfs,key->hkdf_hash_func);
+    		add_to_secrets_list(teas,key->as_rs_alg);
+    		add_to_secrets_list(aas,key->auth_alg);
+			{
+				char ts[256];
+				snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp);
+				add_to_secrets_list(tss,ts);
+			}
+			{
+				char lt[256];
+				snprintf(lt,sizeof(lt)-1,"%lu",(unsigned long)key->lifetime);
+				add_to_secrets_list(lts,lt);
+			}
+    	} else {
+    		printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, 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;
@@ -648,47 +600,76 @@ static int mongo_list_oauth_keys(void) {
   return ret;
 }
   
-static int mongo_show_secret(u08bits *realm) {
-  mongoc_collection_t * collection = mongo_get_collection("turn_secret"); 
+static int mongo_list_secrets(u08bits *realm, secrets_list_t *secrets, secrets_list_t *realms)
+{
+	mongoc_collection_t * collection = mongo_get_collection("turn_secret");
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
 
 	if(!collection)
-    return -1;
+		return -1;
     
-  bson_t query;
-  bson_init(&query);
-  BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
+	bson_t query, child;
+	bson_init(&query);
+	bson_append_document_begin(&query, "$orderby", -1, &child);
+	bson_append_int32(&child, "realm", -1, 1);
+	bson_append_int32(&child, "value", -1, 1);
+	bson_append_document_end(&query, &child);
+	bson_append_document_begin(&query, "$query", -1, &child);
+	if (realm && realm[0]) {
+		BSON_APPEND_UTF8(&child, "realm", (const char *)realm);
+	}
+	bson_append_document_end(&query, &child);
 
-  bson_t fields;
-  bson_init(&fields);
-  BSON_APPEND_INT32(&fields, "value", 1);
+	bson_t fields;
+	bson_init(&fields);
+	BSON_APPEND_INT32(&fields, "value", 1);
+	BSON_APPEND_INT32(&fields, "realm", 1);
 
-  mongoc_cursor_t * cursor;
-  cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
+	mongoc_cursor_t * cursor;
+	cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
 
-  int ret = -1;
+	int ret = -1;
   
-  if (!cursor) {
+	if (!cursor) {
 		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'turn_secret'\n");
-  } else {
-    const bson_t * item;
-    uint32_t length;
-    bson_iter_t iter;
-    const char * value;
-    while (mongoc_cursor_next(cursor, &item)) {
-    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "value") && BSON_ITER_HOLDS_UTF8(&iter)) {
-        value = bson_iter_utf8(&iter, &length);
-        if (length) {
-					printf("%s\n", value);
-        }
-      }
-    }
-    mongoc_cursor_destroy(cursor);
-    ret = 0;
-  }
-  mongoc_collection_destroy(collection);
-  bson_destroy(&query);
-  bson_destroy(&fields);
-  return ret;
+	} else {
+		const bson_t * item;
+		uint32_t length;
+		bson_iter_t iter;
+	    bson_iter_t iter_realm;
+		const char * value;
+		while (mongoc_cursor_next(cursor, &item)) {
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "value") && BSON_ITER_HOLDS_UTF8(&iter)) {
+				value = bson_iter_utf8(&iter, &length);
+				if (length) {
+					const char *rval = "";
+					if (bson_iter_init(&iter_realm, item) && bson_iter_find(&iter_realm, "realm") && BSON_ITER_HOLDS_UTF8(&iter_realm)) {
+						rval = bson_iter_utf8(&iter_realm, &length);
+					}
+					if(secrets) {
+						add_to_secrets_list(secrets,value);
+					    if(realms) {
+					    	if(rval && *rval) {
+					    		add_to_secrets_list(realms,rval);
+					    	} else {
+					    		add_to_secrets_list(realms,(char*)realm);
+					    	}
+					    }
+					} else {
+						printf("%s[%s]\n", value, rval);
+					}
+				}
+			}
+		}
+		mongoc_cursor_destroy(cursor);
+		ret = 0;
+	}
+	mongoc_collection_destroy(collection);
+	bson_destroy(&query);
+	bson_destroy(&fields);
+	return ret;
 }
   
 static int mongo_del_secret(u08bits *secret, u08bits *realm) {
@@ -702,7 +683,7 @@ static int mongo_del_secret(u08bits *secret, u08bits *realm) {
   BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
 	if(secret && (secret[0]!=0)) {
     BSON_APPEND_UTF8(&query, "value", (const char *)secret);
-	}
+  }
 
   mongoc_collection_delete(collection, MONGOC_DELETE_NONE, &query, NULL, NULL);
   mongoc_collection_destroy(collection);
@@ -732,38 +713,89 @@ static int mongo_set_secret(u08bits *secret, u08bits *realm) {
     return 0;
   }
 }
+
+static int mongo_set_permission_ip(const char *kind, u08bits *realm, const char* ip, int del)
+{
+	char sub_collection_name[129];
+	snprintf(sub_collection_name,sizeof(sub_collection_name)-1,"%s_peer_ip",kind);
+
+	mongoc_collection_t * collection = mongo_get_collection("realm");
+
+	if(!collection)
+		return -1;
+
+	int ret = -1;
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
+	bson_t query, doc, child;
+	bson_init(&query);
+	BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
+	bson_init(&doc);
+	if(del) {
+		bson_append_document_begin(&doc, "$pull", -1, &child);
+	} else {
+		bson_append_document_begin(&doc, "$addToSet", -1, &child);
+	}
+	BSON_APPEND_UTF8(&child, sub_collection_name, (const char *)ip);
+	bson_append_document_end(&doc, &child);
+
+	mongoc_update_flags_t flags = MONGOC_UPDATE_NONE;
+
+	if(del) {
+		flags = MONGOC_UPDATE_MULTI_UPDATE;
+	} else {
+		flags = MONGOC_UPDATE_UPSERT;
+	}
+
+	if (!mongoc_collection_update(collection, flags, &query, &doc, NULL, NULL)) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting permission ip information\n");
+	} else {
+		ret = 0;
+	}
+	mongoc_collection_destroy(collection);
+	bson_destroy(&query);
+	bson_destroy(&doc);
+	return ret;
+}
   
-static int mongo_add_origin(u08bits *origin, u08bits *realm) {
-  mongoc_collection_t * collection = mongo_get_collection("realm"); 
+static int mongo_add_origin(u08bits *origin, u08bits *realm)
+{
+	mongoc_collection_t * collection = mongo_get_collection("realm");
 
 	if(!collection)
-    return -1;
+		return -1;
     
-  int ret = -1;
-  
-  bson_t query, doc, child;
-  bson_init(&query);
-  BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
-  bson_init(&doc);
-  bson_append_document_begin(&doc, "$addToSet", -1, &child);
-  BSON_APPEND_UTF8(&child, "origin", (const char *)origin);
-  bson_append_document_end(&doc, &child);
+	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 realm origin information\n");
-  } else {
-    ret = 0;
-  }
-  mongoc_collection_destroy(collection);
-  bson_destroy(&query);
-  bson_destroy(&doc);
-  return ret;
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+  
+	bson_t query, doc, child;
+	bson_init(&query);
+	BSON_APPEND_UTF8(&query, "realm", (const char *)realm);
+	bson_init(&doc);
+	bson_append_document_begin(&doc, "$addToSet", -1, &child);
+	BSON_APPEND_UTF8(&child, "origin", (const char *)origin);
+	bson_append_document_end(&doc, &child);
+
+	if (!mongoc_collection_update(collection, MONGOC_UPDATE_UPSERT, &query, &doc, NULL, NULL)) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating realm origin information\n");
+	} else {
+		ret = 0;
+	}
+	mongoc_collection_destroy(collection);
+	bson_destroy(&query);
+	bson_destroy(&doc);
+	return ret;
 }
   
-static int mongo_del_origin(u08bits *origin) {
+static int mongo_del_origin(u08bits *origin)
+{
   mongoc_collection_t * collection = mongo_get_collection("realm"); 
 
-	if(!collection)
+  if(!collection)
     return -1;
     
   int ret = -1;
@@ -786,71 +818,82 @@ static int mongo_del_origin(u08bits *origin) {
   return ret;
 }
   
-static int mongo_list_origins(u08bits *realm) {
-  mongoc_collection_t * collection = mongo_get_collection("realm"); 
+static int mongo_list_origins(u08bits *realm, secrets_list_t *origins, secrets_list_t *realms)
+{
+	mongoc_collection_t * collection = mongo_get_collection("realm");
 
 	if(!collection)
-    return -1;
+		return -1;
 
-  bson_t query, child;
-  bson_init(&query);
-  bson_append_document_begin(&query, "$orderby", -1, &child);
-  BSON_APPEND_INT32(&child, "realm", 1);
-  bson_append_document_end(&query, &child);
-  bson_append_document_begin(&query, "$query", -1, &child);
-  if (realm && realm[0]) {
-    BSON_APPEND_UTF8(&child, "realm", (const char *)realm);
-  }
-  bson_append_document_end(&query, &child);
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
 
-  bson_t fields;
-  bson_init(&fields);
-  BSON_APPEND_INT32(&fields, "origin", 1);
-  BSON_APPEND_INT32(&fields, "realm", 1);
+	bson_t query, child;
+	bson_init(&query);
+	bson_append_document_begin(&query, "$orderby", -1, &child);
+	BSON_APPEND_INT32(&child, "realm", 1);
+	bson_append_document_end(&query, &child);
+	bson_append_document_begin(&query, "$query", -1, &child);
+	if (realm && realm[0]) {
+		BSON_APPEND_UTF8(&child, "realm", (const char *)realm);
+	}
+	bson_append_document_end(&query, &child);
+
+	bson_t fields;
+	bson_init(&fields);
+	BSON_APPEND_INT32(&fields, "origin", 1);
+	BSON_APPEND_INT32(&fields, "realm", 1);
   
-  mongoc_cursor_t * cursor;
-  cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
+	mongoc_cursor_t * cursor;
+	cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
 
-  int ret = -1;
+	int ret = -1;
   
-  if (!cursor) {
+	if (!cursor) {
 		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'realm'\n");
-  } else {
-    const bson_t * item;
-    uint32_t length;
-    bson_iter_t iter;
+	} else {
+		const bson_t * item;
+		uint32_t length;
+		bson_iter_t iter;
 
-    while (mongoc_cursor_next(cursor, &item)) {
-    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) {
-        const char * _realm = bson_iter_utf8(&iter, &length);
+		while (mongoc_cursor_next(cursor, &item)) {
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) {
+				const char * _realm = bson_iter_utf8(&iter, &length);
 
-        if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "origin") && BSON_ITER_HOLDS_ARRAY(&iter)) {
-          const uint8_t *docbuf = NULL;
-          uint32_t doclen = 0;
-          bson_t origin_array;
-          bson_iter_t origin_iter;
+				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);
+					bson_iter_array(&iter, &doclen, &docbuf);
+					bson_init_static(&origin_array, docbuf, doclen);
 
-          if (bson_iter_init(&origin_iter, &origin_array)) {
-            while(bson_iter_next(&origin_iter)) {
-              if (BSON_ITER_HOLDS_UTF8(&origin_iter)) {
-                const char * _origin = bson_iter_utf8(&origin_iter, &length);
-  							printf("%s ==>> %s\n", _realm, _origin);
-              }
-            }
-          }
-        }
-      }
-    }
-    mongoc_cursor_destroy(cursor);
-    ret = 0;
-  }
-  mongoc_collection_destroy(collection);
-  bson_destroy(&query);
-  bson_destroy(&fields);
-  return ret;
+					if (bson_iter_init(&origin_iter, &origin_array)) {
+						while(bson_iter_next(&origin_iter)) {
+							if (BSON_ITER_HOLDS_UTF8(&origin_iter)) {
+								const char * _origin = bson_iter_utf8(&origin_iter, &length);
+								if(origins) {
+									add_to_secrets_list(origins,_origin);
+									if(realms) {
+										add_to_secrets_list(realms,_realm);
+									}
+								} else {
+									printf("%s ==>> %s\n", _realm, _origin);
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+		mongoc_cursor_destroy(cursor);
+		ret = 0;
+	}
+	mongoc_collection_destroy(collection);
+	bson_destroy(&query);
+	bson_destroy(&fields);
+	return ret;
 }
   
 static int mongo_set_realm_option_one(u08bits *realm, unsigned long value, const char* opt) {
@@ -1172,17 +1215,180 @@ static void mongo_reread_realms(secrets_list_t * realms_list) {
 	bson_destroy(&fields);
 }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////
+
+static int mongo_get_admin_user(const u08bits *usname, u08bits *realm, password_t pwd)
+{
+	mongoc_collection_t * collection = mongo_get_collection("admin_user");
+
+	if(!collection)
+    return -1;
+
+	realm[0]=0;
+	pwd[0]=0;
+
+	bson_t query;
+	bson_init(&query);
+	BSON_APPEND_UTF8(&query, "name", (const char *)usname);
+
+	bson_t fields;
+	bson_init(&fields);
+	BSON_APPEND_INT32(&fields, "realm", 1);
+	BSON_APPEND_INT32(&fields, "password", 1);
+
+	mongoc_cursor_t * cursor;
+	cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 1, 0, &query, &fields, NULL);
+
+	int ret = -1;
+
+	if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'admin_user'\n");
+	} else {
+		const bson_t * item;
+		uint32_t length;
+		bson_iter_t iter;
+		if (mongoc_cursor_next(cursor, &item)) {
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) {
+				strncpy((char*)realm,bson_iter_utf8(&iter, &length),STUN_MAX_REALM_SIZE);
+				ret = 0;
+			}
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "password") && BSON_ITER_HOLDS_UTF8(&iter)) {
+				strncpy((char*)pwd,bson_iter_utf8(&iter, &length),STUN_MAX_PWD_SIZE);
+				ret = 0;
+			}
+		}
+		mongoc_cursor_destroy(cursor);
+	}
+	mongoc_collection_destroy(collection);
+	bson_destroy(&query);
+	bson_destroy(&fields);
+	return ret;
+}
+
+static int mongo_set_admin_user(const u08bits *usname, const u08bits *realm, const password_t pwd)
+{
+	mongoc_collection_t * collection = mongo_get_collection("admin_user");
+
+	if(!collection)
+    return -1;
+
+	bson_t query;
+	bson_init(&query);
+	BSON_APPEND_UTF8(&query, "name", (const char *)usname);
+
+	bson_t doc;
+	bson_init(&doc);
+	BSON_APPEND_UTF8(&doc, "name", (const char *)usname);
+	BSON_APPEND_UTF8(&doc, "realm", (const char *)realm);
+	BSON_APPEND_UTF8(&doc, "password", (const char *)pwd);
+
+	int ret = -1;
+
+	if (!mongoc_collection_update(collection, MONGOC_UPDATE_UPSERT, &query, &doc, NULL, NULL)) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating admin user information\n");
+	} else {
+		ret = 0;
+	}
+	mongoc_collection_destroy(collection);
+	bson_destroy(&doc);
+	bson_destroy(&query);
+	return ret;
+}
+
+static int mongo_del_admin_user(const u08bits *usname)
+{
+	mongoc_collection_t * collection = mongo_get_collection("admin_user");
+
+	if(!collection)
+		return -1;
+
+	bson_t query;
+	bson_init(&query);
+	BSON_APPEND_UTF8(&query, "name", (const char *)usname);
+
+	int ret = -1;
+
+	if (!mongoc_collection_delete(collection, MONGOC_DELETE_SINGLE_REMOVE, &query, NULL, NULL)) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting admin user information\n");
+	} else {
+		ret = 0;
+	}
+	mongoc_collection_destroy(collection);
+	bson_destroy(&query);
+	return ret;
+}
+
+static int mongo_list_admin_users(int no_print)
+{
+	const char * collection_name = "admin_user";
+	mongoc_collection_t * collection = mongo_get_collection(collection_name);
+
+	if(!collection)
+		return -1;
+
+	bson_t query, child;
+	bson_init(&query);
+	bson_append_document_begin(&query, "$orderby", -1, &child);
+	bson_append_int32(&child, "name", -1, 1);
+	bson_append_document_end(&query, &child);
+	bson_append_document_begin(&query, "$query", -1, &child);
+	bson_append_document_end(&query, &child);
+
+	bson_t fields;
+	bson_init(&fields);
+	BSON_APPEND_INT32(&fields, "name", 1);
+	BSON_APPEND_INT32(&fields, "realm", 1);
+
+	mongoc_cursor_t * cursor;
+	cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
+
+	int ret = -1;
+
+	if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection '%s'\n", collection_name);
+	} else {
+		const bson_t * item;
+		uint32_t length;
+		bson_iter_t iter;
+		bson_iter_t iter_realm;
+		const char * value;
+		ret = 0;
+		while (mongoc_cursor_next(cursor, &item)) {
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "name") && BSON_ITER_HOLDS_UTF8(&iter)) {
+				value = bson_iter_utf8(&iter, &length);
+				if (length) {
+					const char *realm = "";
+					if (bson_iter_init(&iter_realm, item) && bson_iter_find(&iter_realm, "realm") && BSON_ITER_HOLDS_UTF8(&iter_realm)) {
+						realm = bson_iter_utf8(&iter_realm, &length);
+					}
+					++ret;
+					if(!no_print) {
+						if(realm && *realm) {
+							printf("%s[%s]\n", value, realm);
+						} else {
+							printf("%s\n", value);
+						}
+					}
+				}
+			}
+		}
+		mongoc_cursor_destroy(cursor);
+	}
+	mongoc_collection_destroy(collection);
+	bson_destroy(&query);
+	bson_destroy(&fields);
+	return ret;
+}
+
+//////////////////////////////////////////////////////////
 
 static const turn_dbdriver_t driver = {
   &mongo_get_auth_secrets,
   &mongo_get_user_key,
-  &mongo_get_user_pwd,
   &mongo_set_user_key,
-  &mongo_set_user_pwd,
   &mongo_del_user,
   &mongo_list_users,
-  &mongo_show_secret,
+  &mongo_list_secrets,
   &mongo_del_secret,
   &mongo_set_secret,
   &mongo_add_origin,
@@ -1192,11 +1398,16 @@ static const turn_dbdriver_t driver = {
   &mongo_list_realm_options,
   &mongo_auth_ping,
   &mongo_get_ip_list,
+  &mongo_set_permission_ip,
   &mongo_reread_realms,
   &mongo_set_oauth_key,
   &mongo_get_oauth_key,
   &mongo_del_oauth_key,
-  &mongo_list_oauth_keys
+  &mongo_list_oauth_keys,
+  &mongo_get_admin_user,
+  &mongo_set_admin_user,
+  &mongo_del_admin_user,
+  &mongo_list_admin_users
 };
 
 const turn_dbdriver_t * get_mongo_dbdriver(void) {

+ 281 - 111
src/apps/relay/dbdrivers/dbd_mysql.c

@@ -338,47 +338,6 @@ static int mysql_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
 	}
   return ret;
 }
-  
-static int mysql_get_user_pwd(u08bits *usname, st_password_t pwd) {
-
-  int ret = -1;
-
-	char statement[TURN_LONG_STRING_SIZE];
-	snprintf(statement,sizeof(statement),"select password from turnusers_st where name='%s'",usname);
-
-	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)!=1) {
-				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement);
-			} else {
-				MYSQL_ROW row = mysql_fetch_row(mres);
-				if(row && row[0]) {
-					unsigned long *lengths = mysql_fetch_lengths(mres);
-					if(lengths) {
-						if(lengths[0]<1) {
-							TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong password data for user %s, size in MySQL DB is zero(0)\n",usname);
-						} else {
-							ns_bcopy(row[0],pwd,lengths[0]);
-							pwd[lengths[0]]=0;
-							ret = 0;
-						}
-					}
-				}
-			}
-
-			if(mres)
-				mysql_free_result(mres);
-		}
-	}
-  return ret;
-}
 
 static int mysql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
 
@@ -402,7 +361,7 @@ static int mysql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
 				if(row && row[0]) {
 					unsigned long *lengths = mysql_fetch_lengths(mres);
 					if(lengths) {
-						STRCPY((char*)key->kid,kid);
+						STRCPY(key->kid,kid);
 						ns_bcopy(row[0],key->ikm_key,lengths[0]);
 						key->ikm_key[lengths[0]]=0;
 
@@ -443,7 +402,7 @@ static int mysql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
 	return ret;
 }
 
-static int mysql_list_oauth_keys(void) {
+static int mysql_list_oauth_keys(secrets_list_t *kids,secrets_list_t *hkdfs,secrets_list_t *teas,secrets_list_t *aas,secrets_list_t *tss,secrets_list_t *lts) {
 
 	oauth_key_data_raw key_;
 	oauth_key_data_raw *key=&key_;
@@ -498,9 +457,26 @@ static int mysql_list_oauth_keys(void) {
 						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",
+						if(kids) {
+							add_to_secrets_list(kids,key->kid);
+							add_to_secrets_list(hkdfs,key->hkdf_hash_func);
+							add_to_secrets_list(teas,key->as_rs_alg);
+							add_to_secrets_list(aas,key->auth_alg);
+							{
+								char ts[256];
+								snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp);
+								add_to_secrets_list(tss,ts);
+							}
+							{
+								char lt[256];
+								snprintf(lt,sizeof(lt)-1,"%lu",(unsigned long)key->lifetime);
+								add_to_secrets_list(lts,lt);
+							}
+						} else {
+							printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, 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);
 				}
@@ -514,26 +490,32 @@ static int mysql_list_oauth_keys(void) {
 	return ret;
 }
   
-static int mysql_set_user_key(u08bits *usname, u08bits *realm, const char *key) {
+static int mysql_set_user_key(u08bits *usname, u08bits *realm, const char *key)
+{
   int ret = -1;
-	char statement[TURN_LONG_STRING_SIZE];
-	MYSQL * myc = get_mydb_connection();
-	if(myc) {
+  char statement[TURN_LONG_STRING_SIZE];
+  MYSQL * myc = get_mydb_connection();
+  if(myc) {
 	  snprintf(statement,sizeof(statement),"insert into turnusers_lt (realm,name,hmackey) values('%s','%s','%s')",realm,usname,key);
-		int res = mysql_query(myc, statement);
-		if(res) {
+	  int res = mysql_query(myc, statement);
+	  if(!res) {
+		  ret = 0;
+	  } else {
 		  snprintf(statement,sizeof(statement),"update turnusers_lt set hmackey='%s' where name='%s' and realm='%s'",key,usname,realm);
-			res = mysql_query(myc, statement);
-			if(res) {
-				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user key information: %s\n",mysql_error(myc));
-			}
-		}
-	}
+		  res = mysql_query(myc, statement);
+		  if(!res) {
+			  ret = 0;
+		  } else {
+			  TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user key information: %s\n",mysql_error(myc));
+		  }
+	  }
+  }
   return ret;
 }
 
-static int mysql_set_oauth_key(oauth_key_data_raw *key) {
-  int ret = -1;
+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) {
@@ -547,42 +529,22 @@ static int mysql_set_oauth_key(oauth_key_data_raw *key) {
 			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;
-	char statement[TURN_LONG_STRING_SIZE];
-	MYSQL * myc = get_mydb_connection();
-	if(myc) {
-	  snprintf(statement,sizeof(statement),"insert into turnusers_st values('%s','%s')",usname,pwd);
-		int res = mysql_query(myc, statement);
-		if(res) {
-		  snprintf(statement,sizeof(statement),"update turnusers_st set password='%s' where name='%s'",pwd,usname);
-			res = mysql_query(myc, statement);
-			if(res) {
-				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user key information: %s\n",mysql_error(myc));
 			} else {
-			  ret = 0;
+				ret = 0;
 			}
+		} else {
+			ret = 0;
 		}
 	}
-  return ret;
+	return ret;
 }
   
-static int mysql_del_user(u08bits *usname, int is_st, u08bits *realm) {
+static int mysql_del_user(u08bits *usname, u08bits *realm) {
   int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	MYSQL * myc = get_mydb_connection();
 	if(myc) {
-		if(is_st) {
-		  snprintf(statement,sizeof(statement),"delete from turnusers_st where name='%s'",usname);
-		} else {
-		  snprintf(statement,sizeof(statement),"delete from turnusers_lt where name='%s' and realm='%s'",usname,realm);
-		}
+		snprintf(statement,sizeof(statement),"delete from turnusers_lt where name='%s' and realm='%s'",usname,realm);
 		int res = mysql_query(myc, statement);
 		if(res) {
 			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting user key information: %s\n",mysql_error(myc));
@@ -609,17 +571,20 @@ static int mysql_del_oauth_key(const u08bits *kid) {
 	return ret;
 }
   
-static int mysql_list_users(int is_st, u08bits *realm) {
-  int ret = -1;
+static int mysql_list_users(u08bits *realm, secrets_list_t *users, secrets_list_t *realms)
+{
+	int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
 	MYSQL * myc = get_mydb_connection();
 	if(myc) {
-		if(is_st) {
-		  snprintf(statement,sizeof(statement),"select name,'' from turnusers_st order by name");
-		} else if(realm && realm[0]) {
+		if(realm[0]) {
 		  snprintf(statement,sizeof(statement),"select name, realm from turnusers_lt where realm='%s' order by name",realm);
 		} else {
-		  snprintf(statement,sizeof(statement),"select name, realm from turnusers_lt order by name");
+		  snprintf(statement,sizeof(statement),"select name, realm from turnusers_lt order by realm,name");
 		}
 		int res = mysql_query(myc, statement);
 		if(res) {
@@ -637,10 +602,17 @@ static int mysql_list_users(int is_st, u08bits *realm) {
 						break;
 					} else {
 						if(row[0]) {
-							if(row[1] && row[1][0]) {
-								printf("%s[%s]\n",row[0],row[1]);
+							if(users) {
+								add_to_secrets_list(users,row[0]);
+								if(realms) {
+									if(row[1]) {
+										add_to_secrets_list(realms,row[1]);
+									} else {
+										add_to_secrets_list(realms,(char*)realm);
+									}
+								}
 							} else {
-								printf("%s\n",row[0]);
+								printf("%s[%s]\n", row[0], row[1]);
 							}
 						}
 					}
@@ -655,10 +627,19 @@ static int mysql_list_users(int is_st, u08bits *realm) {
   return ret;
 }
   
-static int mysql_show_secret(u08bits *realm) {
-  int ret = -1;
+static int mysql_list_secrets(u08bits *realm, secrets_list_t *secrets, secrets_list_t *realms)
+{
+	int ret = -1;
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
 	char statement[TURN_LONG_STRING_SIZE];
-	snprintf(statement,sizeof(statement)-1,"select value from turn_secret where realm='%s'",realm);
+	if (realm[0]) {
+		snprintf(statement, sizeof(statement), "select value,realm from turn_secret where realm='%s' order by value", realm);
+	} else {
+		snprintf(statement, sizeof(statement), "select value,realm from turn_secret order by realm,value");
+	}
 
 	donot_print_connection_success=1;
 
@@ -671,7 +652,7 @@ static int mysql_show_secret(u08bits *realm) {
 			MYSQL_RES *mres = mysql_store_result(myc);
 			if(!mres) {
 				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc));
-			} else if(mysql_field_count(myc)!=1) {
+			} else if(mysql_field_count(myc)!=2) {
 				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement);
 			} else {
 				for(;;) {
@@ -679,19 +660,32 @@ static int mysql_show_secret(u08bits *realm) {
 					if(!row) {
 						break;
 					} else {
-						if(row[0]) {
-							printf("%s\n",row[0]);
+						const char* kval = row[0];
+						if(kval) {
+							const char* rval = row[1];
+							if(secrets) {
+								add_to_secrets_list(secrets,kval);
+								if(realms) {
+									if(rval && *rval) {
+										add_to_secrets_list(realms,rval);
+									} else {
+										add_to_secrets_list(realms,(char*)realm);
+									}
+								}
+							} else {
+								printf("%s[%s]\n",kval,rval);
+							}
 						}
 					}
 				}
-        ret = 0;
+				ret = 0;
 			}
 
 			if(mres)
 				mysql_free_result(mres);
 		}
 	}
-  return ret;
+	return ret;
 }
   
 static int mysql_del_secret(u08bits *secret, u08bits *realm) {
@@ -729,6 +723,37 @@ static int mysql_set_secret(u08bits *secret, u08bits *realm) {
 	}
   return ret;
 }
+
+static int mysql_set_permission_ip(const char *kind, u08bits *realm, const char* ip, int del)
+{
+	int ret = -1;
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
+	donot_print_connection_success = 1;
+
+	char statement[TURN_LONG_STRING_SIZE];
+
+	MYSQL * myc = get_mydb_connection();
+	if (myc) {
+		if(del) {
+			snprintf(statement, sizeof(statement), "delete from %s_peer_ip where realm = '%s'  and ip_range = '%s'", kind, (char*)realm, ip);
+		} else {
+			snprintf(statement, sizeof(statement), "insert into %s_peer_ip (realm,ip_range) values('%s','%s')", kind, (char*)realm, ip);
+		}
+		int res = mysql_query(myc, statement);
+		if (res) {
+			TURN_LOG_FUNC(
+			  TURN_LOG_LEVEL_ERROR,
+			  "Error inserting permission ip information: %s\n",
+			  mysql_error(myc));
+		} else {
+			ret = 0;
+		}
+	}
+	return ret;
+}
   
 static int mysql_add_origin(u08bits *origin, u08bits *realm) {
   int ret = -1;
@@ -768,16 +793,22 @@ static int mysql_del_origin(u08bits *origin) {
   return ret;
 }
   
-static int mysql_list_origins(u08bits *realm) {
-  int ret = -1;
+static int mysql_list_origins(u08bits *realm, secrets_list_t *origins, secrets_list_t *realms)
+{
+	int ret = -1;
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
 	donot_print_connection_success = 1;
-	char statement[TURN_LONG_STRING_SIZE];
+
 	MYSQL * myc = get_mydb_connection();
 	if(myc) {
+		char statement[TURN_LONG_STRING_SIZE];
 		if(realm && realm[0]) {
 			snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm where realm='%s' order by origin",realm);
 		} else {
-			snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm order by origin,realm");
+			snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm order by realm,origin");
 		}
 		int res = mysql_query(myc, statement);
 		if(res) {
@@ -795,7 +826,20 @@ static int mysql_list_origins(u08bits *realm) {
 						break;
 					} else {
 						if(row[0] && row[1]) {
-							printf("%s ==>> %s\n",row[0],row[1]);
+							const char* kval = row[0];
+							const char* rval = row[1];
+							if(origins) {
+								add_to_secrets_list(origins,kval);
+								if(realms) {
+									if(rval && *rval) {
+										add_to_secrets_list(realms,rval);
+									} else {
+										add_to_secrets_list(realms,(char*)realm);
+									}
+								}
+							} else {
+								printf("%s ==>> %s\n",kval,rval);
+							}
 						}
 					}
 				}
@@ -1065,17 +1109,138 @@ static void mysql_reread_realms(secrets_list_t * realms_list) {
 	}
 }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////
+
+static int mysql_get_admin_user(const u08bits *usname, u08bits *realm, password_t pwd)
+{
+  int ret = -1;
+
+  realm[0]=0;
+  pwd[0]=0;
+
+  MYSQL * myc = get_mydb_connection();
+  if(myc) {
+	char statement[TURN_LONG_STRING_SIZE];
+	snprintf(statement,sizeof(statement),"select realm,password from admin_user where name='%s'",usname);
+	int res = mysql_query(myc, statement);
+	if(res) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc));
+	} else {
+		MYSQL_RES *mres = mysql_store_result(myc);
+		if(!mres) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc));
+		} else if(mysql_field_count(myc)!=2) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement);
+		} else {
+			MYSQL_ROW row = mysql_fetch_row(mres);
+			if(row && row[0]) {
+				strncpy((char*)realm,row[0],STUN_MAX_REALM_SIZE);
+				strncpy((char*)pwd,row[1],STUN_MAX_PWD_SIZE);
+				ret = 0;
+			}
+		}
+
+		if(mres)
+			mysql_free_result(mres);
+	}
+  }
+  return ret;
+}
+
+static int mysql_set_admin_user(const u08bits *usname, const u08bits *realm, const password_t pwd)
+{
+  int ret = -1;
+  char statement[TURN_LONG_STRING_SIZE];
+  donot_print_connection_success=1;
+  MYSQL * myc = get_mydb_connection();
+  if(myc) {
+	  snprintf(statement,sizeof(statement),"insert into admin_user (realm,name,password) values('%s','%s','%s')",realm,usname,pwd);
+	  int res = mysql_query(myc, statement);
+	  if(!res) {
+		  ret = 0;
+	  } else {
+		  snprintf(statement,sizeof(statement),"update admin_user set realm='%s',password='%s' where name='%s'",realm,pwd,usname);
+		  res = mysql_query(myc, statement);
+		  if(!res) {
+			  ret = 0;
+		  } else {
+			  TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user key information: %s\n",mysql_error(myc));
+		  }
+	  }
+  }
+  return ret;
+}
+
+static int mysql_del_admin_user(const u08bits *usname)
+{
+	int ret = -1;
+	char statement[TURN_LONG_STRING_SIZE];
+	donot_print_connection_success=1;
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		snprintf(statement,sizeof(statement),"delete from admin_user where name='%s'",usname);
+		int res = mysql_query(myc, statement);
+		if(res) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting admin user information: %s\n",mysql_error(myc));
+		} else {
+		  ret = 0;
+		}
+	}
+	return ret;
+}
+
+static int mysql_list_admin_users(int no_print)
+{
+	int ret = -1;
+	char statement[TURN_LONG_STRING_SIZE];
+	donot_print_connection_success=1;
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		snprintf(statement,sizeof(statement),"select name, realm from admin_user order by realm,name");
+		int res = mysql_query(myc, statement);
+		if(res) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc));
+		} else {
+			MYSQL_RES *mres = mysql_store_result(myc);
+			if(!mres) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc));
+			} else if(mysql_field_count(myc)!=2) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement);
+			} else {
+				ret = 0;
+				for(;;) {
+					MYSQL_ROW row = mysql_fetch_row(mres);
+					if(!row) {
+						break;
+					} else {
+						++ret;
+						if(row[0] && !no_print) {
+							if(row[1] && row[1][0]) {
+								printf("%s[%s]\n",row[0],row[1]);
+							} else {
+								printf("%s\n",row[0]);
+							}
+						}
+					}
+				}
+			}
+
+			if(mres)
+				mysql_free_result(mres);
+		}
+	}
+	return ret;
+}
+
+//////////////////////////////////////////////////////
 
 static const turn_dbdriver_t driver = {
   &mysql_get_auth_secrets,
   &mysql_get_user_key,
-  &mysql_get_user_pwd,
   &mysql_set_user_key,
-  &mysql_set_user_pwd,
   &mysql_del_user,
   &mysql_list_users,
-  &mysql_show_secret,
+  &mysql_list_secrets,
   &mysql_del_secret,
   &mysql_set_secret,
   &mysql_add_origin,
@@ -1085,11 +1250,16 @@ static const turn_dbdriver_t driver = {
   &mysql_list_realm_options,
   &mysql_auth_ping,
   &mysql_get_ip_list,
+  &mysql_set_permission_ip,
   &mysql_reread_realms,
   &mysql_set_oauth_key,
   &mysql_get_oauth_key,
   &mysql_del_oauth_key,
-  &mysql_list_oauth_keys
+  &mysql_list_oauth_keys,
+  &mysql_get_admin_user,
+  &mysql_set_admin_user,
+  &mysql_del_admin_user,
+  &mysql_list_admin_users
 };
 
 const turn_dbdriver_t * get_mysql_dbdriver(void) {

+ 283 - 111
src/apps/relay/dbdrivers/dbd_pgsql.c

@@ -152,34 +152,6 @@ static int pgsql_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
 	}
   return ret;
 }
-  
-static int pgsql_get_user_pwd(u08bits *usname, st_password_t pwd) {
-  int ret = -1;
-	char statement[TURN_LONG_STRING_SIZE];
-	snprintf(statement,sizeof(statement),"select password from turnusers_st where name='%s'",usname);
-
-	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 {
-			char *kval = PQgetvalue(res,0,0);
-			if(kval) {
-				strncpy((char*)pwd,kval,sizeof(st_password_t));
-				ret = 0;
-			} else {
-				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong password data for user %s: NULL\n",usname);
-			}
-		}
-
-		if(res) {
-			PQclear(res);
-		}
-	}
-  return ret;
-}
 
 static int pgsql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
 
@@ -195,15 +167,15 @@ static int pgsql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
 		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));
+			STRCPY(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);
+			STRCPY(key->hkdf_hash_func,PQgetvalue(res,0,3));
+			STRCPY(key->as_rs_alg,PQgetvalue(res,0,4));
+			STRCPY(key->as_rs_key,PQgetvalue(res,0,5));
+			STRCPY(key->auth_alg,PQgetvalue(res,0,6));
+			STRCPY(key->auth_key,PQgetvalue(res,0,7));
+			STRCPY(key->kid,kid);
 			ret = 0;
 		}
 
@@ -215,7 +187,7 @@ static int pgsql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
 	return ret;
 }
 
-static int pgsql_list_oauth_keys(void) {
+static int pgsql_list_oauth_keys(secrets_list_t *kids,secrets_list_t *hkdfs,secrets_list_t *teas,secrets_list_t *aas,secrets_list_t *tss,secrets_list_t *lts) {
 
 	oauth_key_data_raw key_;
 	oauth_key_data_raw *key=&key_;
@@ -235,19 +207,36 @@ static int pgsql_list_oauth_keys(void) {
 			int i = 0;
 			for(i=0;i<PQntuples(res);i++) {
 
-				STRCPY((char*)key->ikm_key,PQgetvalue(res,i,0));
+				STRCPY(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",
+				STRCPY(key->hkdf_hash_func,PQgetvalue(res,i,3));
+				STRCPY(key->as_rs_alg,PQgetvalue(res,i,4));
+				STRCPY(key->as_rs_key,PQgetvalue(res,i,5));
+				STRCPY(key->auth_alg,PQgetvalue(res,i,6));
+				STRCPY(key->auth_key,PQgetvalue(res,i,7));
+				STRCPY(key->kid,PQgetvalue(res,i,8));
+
+				if(kids) {
+					add_to_secrets_list(kids,key->kid);
+					add_to_secrets_list(hkdfs,key->hkdf_hash_func);
+					add_to_secrets_list(teas,key->as_rs_alg);
+					add_to_secrets_list(aas,key->auth_alg);
+					{
+						char ts[256];
+						snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp);
+						add_to_secrets_list(tss,ts);
+					}
+					{
+						char lt[256];
+						snprintf(lt,sizeof(lt)-1,"%lu",(unsigned long)key->lifetime);
+						add_to_secrets_list(lts,lt);
+					}
+				} else {
+					printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, 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;
 			}
@@ -273,7 +262,7 @@ static int pgsql_set_user_key(u08bits *usname, u08bits *realm, const char *key)
 			if(res) {
 				PQclear(res);
 			}
-		  snprintf(statement,sizeof(statement),"update turnusers_lt set hmackey='%s' where name='%s' and realm='%s'",key,usname,realm);
+			snprintf(statement,sizeof(statement),"update turnusers_lt set hmackey='%s' where name='%s' and realm='%s'",key,usname,realm);
 			res = PQexec(pqc, statement);
 			if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
 				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user information: %s\n",PQerrorMessage(pqc));
@@ -311,50 +300,23 @@ static int pgsql_set_oauth_key(oauth_key_data_raw *key) {
 		  } else {
 			  ret = 0;
 		  }
+	  } else {
+		  ret = 0;
 	  }
+
 	  if(res) {
 		  PQclear(res);
 	  }
   }
   return ret;
 }
-
-static int pgsql_set_user_pwd(u08bits *usname, st_password_t pwd) {
-  int ret = -1;
-	char statement[TURN_LONG_STRING_SIZE];
-	PGconn *pqc = get_pqdb_connection();
-	if(pqc) {
-	  snprintf(statement,sizeof(statement),"insert into turnusers_st values('%s','%s')",usname,pwd);
-    PGresult *res = PQexec(pqc, statement);
-		if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
-			if(res) {
-				PQclear(res);
-			}
-		  snprintf(statement,sizeof(statement),"update turnusers_st set password='%s' where name='%s'",pwd,usname);
-			res = PQexec(pqc, statement);
-			if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
-				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user information: %s\n",PQerrorMessage(pqc));
-			} else {
-			  ret = 0;
-			}
-		}
-		if(res) {
-			PQclear(res);
-		}
-	}
-  return ret;
-}
   
-static int pgsql_del_user(u08bits *usname, int is_st, u08bits *realm) {
+static int pgsql_del_user(u08bits *usname, u08bits *realm) {
   int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	PGconn *pqc = get_pqdb_connection();
 	if(pqc) {
-		if(is_st) {
-		  snprintf(statement,sizeof(statement),"delete from turnusers_st where name='%s'",usname);
-		} else {
-		  snprintf(statement,sizeof(statement),"delete from turnusers_lt where name='%s' and realm='%s'",usname,realm);
-		}
+		snprintf(statement,sizeof(statement),"delete from turnusers_lt where name='%s' and realm='%s'",usname,realm);
 		PGresult *res = PQexec(pqc, statement);
 		if(res) {
 			PQclear(res);
@@ -385,17 +347,20 @@ static int pgsql_del_oauth_key(const u08bits *kid) {
   return ret;
 }
   
-static int pgsql_list_users(int is_st, u08bits *realm) {
-  int ret = -1;
+static int pgsql_list_users(u08bits *realm, secrets_list_t *users, secrets_list_t *realms)
+{
+	int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
 	PGconn *pqc = get_pqdb_connection();
 	if(pqc) {
-		if(is_st) {
-		  snprintf(statement,sizeof(statement),"select name,'' from turnusers_st order by name");
-		} else if(realm && realm[0]) {
+		if(realm[0]) {
 		  snprintf(statement,sizeof(statement),"select name,realm from turnusers_lt where realm='%s' order by name",realm);
 		} else {
-		  snprintf(statement,sizeof(statement),"select name,realm from turnusers_lt order by name");
+		  snprintf(statement,sizeof(statement),"select name,realm from turnusers_lt order by realm,name");
 		}
 		PGresult *res = PQexec(pqc, statement);
 		if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
@@ -406,10 +371,19 @@ static int pgsql_list_users(int is_st, u08bits *realm) {
 				char *kval = PQgetvalue(res,i,0);
 				if(kval) {
 					char *rval = PQgetvalue(res,i,1);
-					if(rval && *rval) {
-						printf("%s[%s]\n",kval,rval);
-					} else {
-						printf("%s\n",kval);
+					if(rval) {
+						if(users) {
+							add_to_secrets_list(users,kval);
+							if(realms) {
+								if(rval && *rval) {
+									add_to_secrets_list(realms,rval);
+								} else {
+									add_to_secrets_list(realms,(char*)realm);
+								}
+							}
+						} else {
+							printf("%s[%s]\n", kval, rval);
+						}
 					}
 				}
 			}
@@ -422,10 +396,19 @@ static int pgsql_list_users(int is_st, u08bits *realm) {
   return ret;
 }
   
-static int pgsql_show_secret(u08bits *realm) {
-  int ret = -1;
+static int pgsql_list_secrets(u08bits *realm, secrets_list_t *secrets, secrets_list_t *realms)
+{
+	int ret = -1;
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
 	char statement[TURN_LONG_STRING_SIZE];
-	snprintf(statement,sizeof(statement)-1,"select value from turn_secret where realm='%s'",realm);
+	if (realm[0]) {
+		snprintf(statement, sizeof(statement), "select value,realm from turn_secret where realm='%s' order by value", realm);
+	} else {
+		snprintf(statement, sizeof(statement), "select value,realm from turn_secret order by realm,value");
+	}
 
 	donot_print_connection_success=1;
 
@@ -439,16 +422,28 @@ static int pgsql_show_secret(u08bits *realm) {
 			for(i=0;i<PQntuples(res);i++) {
 				char *kval = PQgetvalue(res,i,0);
 				if(kval) {
-					printf("%s\n",kval);
+					char* rval = PQgetvalue(res,i,1);
+					if(secrets) {
+						add_to_secrets_list(secrets,kval);
+						if(realms) {
+							if(rval && *rval) {
+								add_to_secrets_list(realms,rval);
+							} else {
+								add_to_secrets_list(realms,(char*)realm);
+							}
+						}
+					} else {
+						printf("%s[%s]\n",kval,rval);
+					}
 				}
 			}
-      ret = 0;
+			ret = 0;
 		}
 		if(res) {
 			PQclear(res);
 		}
 	}
-  return ret;
+	return ret;
 }
   
 static int pgsql_del_secret(u08bits *secret, u08bits *realm) {
@@ -474,13 +469,13 @@ static int pgsql_del_secret(u08bits *secret, u08bits *realm) {
 static int pgsql_set_secret(u08bits *secret, u08bits *realm) {
   int ret = -1;
 	donot_print_connection_success = 1;
-  char statement[TURN_LONG_STRING_SIZE];
+	char statement[TURN_LONG_STRING_SIZE];
 	PGconn *pqc = get_pqdb_connection();
 	if (pqc) {
 	  snprintf(statement,sizeof(statement),"insert into turn_secret (realm,value) values('%s','%s')",realm,secret);
 	  PGresult *res = PQexec(pqc, statement);
 	  if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
-	    TURN_LOG_FUNC(
+		  TURN_LOG_FUNC(
 			  TURN_LOG_LEVEL_ERROR,
 			  "Error inserting/updating secret key information: %s\n",
 			  PQerrorMessage(pqc));
@@ -492,7 +487,45 @@ static int pgsql_set_secret(u08bits *secret, u08bits *realm) {
 	  }
 	}
 
-  return ret;
+	return ret;
+}
+
+static int pgsql_set_permission_ip(const char *kind, u08bits *realm, const char* ip, int del)
+{
+	int ret = -1;
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
+	donot_print_connection_success = 1;
+
+	char statement[TURN_LONG_STRING_SIZE];
+
+	PGconn *pqc = get_pqdb_connection();
+
+	if (pqc) {
+
+		if(del) {
+			snprintf(statement, sizeof(statement), "delete from %s_peer_ip where realm = '%s'  and ip_range = '%s'", kind, (char*)realm, ip);
+		} else {
+			snprintf(statement, sizeof(statement), "insert into %s_peer_ip (realm,ip_range) values('%s','%s')", kind, (char*)realm, ip);
+		}
+
+		PGresult *res = PQexec(pqc, statement);
+		if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
+			TURN_LOG_FUNC(
+				TURN_LOG_LEVEL_ERROR,
+				"Error inserting ip permission information: %s\n",
+				PQerrorMessage(pqc));
+	  } else {
+	    ret = 0;
+	  }
+	  if (res) {
+	    PQclear(res);
+	  }
+	}
+
+	return ret;
 }
   
 static int pgsql_add_origin(u08bits *origin, u08bits *realm) {
@@ -533,16 +566,25 @@ static int pgsql_del_origin(u08bits *origin) {
   return ret;
 }
   
-static int pgsql_list_origins(u08bits *realm) {
-  int ret = -1;
+static int pgsql_list_origins(u08bits *realm, secrets_list_t *origins, secrets_list_t *realms)
+{
+	int ret = -1;
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
 	donot_print_connection_success = 1;
-	char statement[TURN_LONG_STRING_SIZE];
+
 	PGconn *pqc = get_pqdb_connection();
+
 	if(pqc) {
+
+		char statement[TURN_LONG_STRING_SIZE];
+
 		if(realm && realm[0]) {
 		  snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm where realm='%s' order by origin",realm);
 		} else {
-		  snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm order by origin,realm");
+		  snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm order by realm,origin");
 		}
 		PGresult *res = PQexec(pqc, statement);
 		if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
@@ -550,21 +592,32 @@ static int pgsql_list_origins(u08bits *realm) {
 		} else {
 			int i = 0;
 			for(i=0;i<PQntuples(res);i++) {
-				char *oval = PQgetvalue(res,i,0);
-				if(oval) {
+				char *kval = PQgetvalue(res,i,0);
+				if(kval) {
 					char *rval = PQgetvalue(res,i,1);
 					if(rval) {
-						printf("%s ==>> %s\n",oval,rval);
+						if(origins) {
+							add_to_secrets_list(origins,kval);
+							if(realms) {
+								if(rval && *rval) {
+									add_to_secrets_list(realms,rval);
+								} else {
+									add_to_secrets_list(realms,(char*)realm);
+								}
+							}
+						} else {
+							printf("%s ==>> %s\n",kval,rval);
+						}
 					}
 				}
 			}
-      ret = 0;
+			ret = 0;
 		}
 		if(res) {
 			PQclear(res);
 		}
 	}
-  return ret;
+	return ret;
 }
   
 static int pgsql_set_realm_option_one(u08bits *realm, unsigned long value, const char* opt) {
@@ -785,17 +838,131 @@ static void pgsql_reread_realms(secrets_list_t * realms_list) {
 	}
 }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////
+
+static int pgsql_get_admin_user(const u08bits *usname, u08bits *realm, password_t pwd)
+{
+	int ret = -1;
+
+	realm[0]=0;
+	pwd[0]=0;
+
+	PGconn * pqc = get_pqdb_connection();
+	if(pqc) {
+		char statement[TURN_LONG_STRING_SIZE];
+		snprintf(statement,sizeof(statement),"select realm,password from admin_user where name='%s'",usname);
+		PGresult *res = PQexec(pqc, statement);
+
+		if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res)!=1)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc));
+		} else {
+			const char *kval = PQgetvalue(res,0,0);
+			if(kval) {
+				strncpy((char*)realm,kval,STUN_MAX_REALM_SIZE);
+			}
+			kval = (const char*) PQgetvalue(res,0,1);
+			if(kval) {
+				strncpy((char*)pwd,kval,STUN_MAX_PWD_SIZE);
+			}
+			ret = 0;
+		}
+
+		if(res)
+			PQclear(res);
+
+	}
+	return ret;
+}
+
+static int pgsql_set_admin_user(const u08bits *usname, const u08bits *realm, const password_t pwd)
+{
+	int ret = -1;
+	char statement[TURN_LONG_STRING_SIZE];
+	donot_print_connection_success=1;
+	PGconn *pqc = get_pqdb_connection();
+	if(pqc) {
+	  snprintf(statement,sizeof(statement),"insert into admin_user (realm,name,password) values('%s','%s','%s')",realm,usname,pwd);
+
+	  PGresult *res = PQexec(pqc, statement);
+	  if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
+		if(res) {
+			PQclear(res);
+		}
+		snprintf(statement,sizeof(statement),"update admin_user set password='%s',realm='%s' where name='%s'",pwd,realm,usname);
+		res = PQexec(pqc, statement);
+		if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user information: %s\n",PQerrorMessage(pqc));
+		} else {
+		  ret = 0;
+		}
+	  }
+	  if(res) {
+		PQclear(res);
+	  }
+	}
+	return ret;
+}
+
+static int pgsql_del_admin_user(const u08bits *usname)
+{
+	int ret = -1;
+	char statement[TURN_LONG_STRING_SIZE];
+	donot_print_connection_success=1;
+	PGconn *pqc = get_pqdb_connection();
+	if(pqc) {
+		snprintf(statement,sizeof(statement),"delete from admin_user where name='%s'",usname);
+		PGresult *res = PQexec(pqc, statement);
+		if(res) {
+			PQclear(res);
+			ret = 0;
+		}
+	}
+	return ret;
+}
+
+static int pgsql_list_admin_users(int no_print)
+{
+	int ret = -1;
+	char statement[TURN_LONG_STRING_SIZE];
+	donot_print_connection_success=1;
+	PGconn *pqc = get_pqdb_connection();
+	if(pqc) {
+		snprintf(statement,sizeof(statement),"select name,realm,password from admin_user order by realm,name");
+	}
+	PGresult *res = PQexec(pqc, statement);
+	if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc));
+	} else {
+		int i = 0;
+		ret = 0;
+		for(i=0;i<PQntuples(res);i++) {
+			char *kval = PQgetvalue(res,i,0);
+			++ret;
+			if(kval && !no_print) {
+				char *rval = PQgetvalue(res,i,1);
+				if(rval && *rval) {
+					printf("%s[%s]\n",kval,rval);
+				} else {
+					printf("%s\n",kval);
+				}
+			}
+		}
+	}
+	if(res) {
+		PQclear(res);
+	}
+	return ret;
+}
+
+/////////////////////////////////////////////////////////////
 
 static const turn_dbdriver_t driver = {
   &pgsql_get_auth_secrets,
   &pgsql_get_user_key,
-  &pgsql_get_user_pwd,
   &pgsql_set_user_key,
-  &pgsql_set_user_pwd,
   &pgsql_del_user,
   &pgsql_list_users,
-  &pgsql_show_secret,
+  &pgsql_list_secrets,
   &pgsql_del_secret,
   &pgsql_set_secret,
   &pgsql_add_origin,
@@ -805,11 +972,16 @@ static const turn_dbdriver_t driver = {
   &pgsql_list_realm_options,
   &pgsql_auth_ping,
   &pgsql_get_ip_list,
+  &pgsql_set_permission_ip,
   &pgsql_reread_realms,
   &pgsql_set_oauth_key,
   &pgsql_get_oauth_key,
   &pgsql_del_oauth_key,
-  &pgsql_list_oauth_keys
+  &pgsql_list_oauth_keys,
+  &pgsql_get_admin_user,
+  &pgsql_set_admin_user,
+  &pgsql_del_admin_user,
+  &pgsql_list_admin_users
 };
 
 const turn_dbdriver_t * get_pgsql_dbdriver(void) {

+ 275 - 132
src/apps/relay/dbdrivers/dbd_redis.c

@@ -450,23 +450,6 @@ static int redis_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
 			}
 			turnFreeRedisReply(rget);
 		}
-		if(ret == 0) {
-			snprintf(s,sizeof(s),"get turn/realm/%s/user/%s/password", (char*)realm, usname);
-			rget = (redisReply *)redisCommand(rc, s);
-			if(rget) {
-				if (rget->type == REDIS_REPLY_ERROR)
-					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str);
-				else if (rget->type != REDIS_REPLY_STRING) {
-					if (rget->type != REDIS_REPLY_NIL)
-						TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type);
-				} else {
-					if(stun_produce_integrity_key_str((u08bits*)usname, realm, (u08bits*)rget->str, key, turn_params.shatype)>=0) {
-						ret = 0;
-					}
-				}
-				turnFreeRedisReply(rget);
-			}
-		}
 	}
   return ret;
 }
@@ -519,41 +502,15 @@ static int redis_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
   return ret;
 }
   
-static int redis_get_user_pwd(u08bits *usname, st_password_t pwd) {
-  int ret = -1;
-	redisContext * rc = get_redis_connection();
-	if(rc) {
-		char s[TURN_LONG_STRING_SIZE];
-		snprintf(s,sizeof(s),"get turn/user/%s/password", usname);
-		redisReply *rget = (redisReply *)redisCommand(rc, s);
-		if(rget) {
-			if (rget->type == REDIS_REPLY_ERROR)
-				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", rget->str);
-			else if (rget->type != REDIS_REPLY_STRING) {
-				if (rget->type != REDIS_REPLY_NIL)
-					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type);
-			} else {
-				strncpy((char*)pwd,rget->str,SHORT_TERM_PASSWORD_SIZE);
-				pwd[SHORT_TERM_PASSWORD_SIZE]=0;
-				ret = 0;
-			}
-			turnFreeRedisReply(rget);
-		}
-	}
-  return ret;
-}
-  
 static int redis_set_user_key(u08bits *usname, u08bits *realm, const char *key) {
   int ret = -1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
 		char statement[TURN_LONG_STRING_SIZE];
-	  snprintf(statement,sizeof(statement),"set turn/realm/%s/user/%s/key %s",(char*)realm,usname,key);
-	  turnFreeRedisReply(redisCommand(rc, statement));
-	  snprintf(statement,sizeof(statement),"del turn/realm/%s/user/%s/password",(char*)realm,usname);
-	  turnFreeRedisReply(redisCommand(rc, statement));
+		snprintf(statement,sizeof(statement),"set turn/realm/%s/user/%s/key %s",(char*)realm,usname,key);
+		turnFreeRedisReply(redisCommand(rc, statement));
 		turnFreeRedisReply(redisCommand(rc, "save"));
-    ret = 0;
+		ret = 0;
 	}
   return ret;
 }
@@ -563,7 +520,7 @@ static int redis_set_oauth_key(oauth_key_data_raw *key) {
   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",
+	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"));
@@ -572,32 +529,14 @@ static int redis_set_oauth_key(oauth_key_data_raw *key) {
   return ret;
 }
   
-static int redis_set_user_pwd(u08bits *usname, st_password_t pwd) {
+static int redis_del_user(u08bits *usname, u08bits *realm) {
   int ret = -1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
 		char statement[TURN_LONG_STRING_SIZE];
-		snprintf(statement,sizeof(statement),"set turn/user/%s/password %s",usname,pwd);
-		turnFreeRedisReply(redisCommand(rc, statement));
-		turnFreeRedisReply(redisCommand(rc, "save"));
-		ret = 0;
-	}
-  return ret;
-}
-  
-static int redis_del_user(u08bits *usname, int is_st, u08bits *realm) {
-  int ret = -1;
-	redisContext *rc = get_redis_connection();
-	if(rc) {
-		char statement[TURN_LONG_STRING_SIZE];
-		if(is_st) {
-		  snprintf(statement,sizeof(statement),"del turn/user/%s/password",usname);
-		  turnFreeRedisReply(redisCommand(rc, statement));
-		} else {
+		{
 		  snprintf(statement,sizeof(statement),"del turn/realm/%s/user/%s/key",(char*)realm,usname);
 		  turnFreeRedisReply(redisCommand(rc, statement));
-		  snprintf(statement,sizeof(statement),"del turn/realm/%s/user/%s/password",(char*)realm,usname);
-		  turnFreeRedisReply(redisCommand(rc, statement));
 		}
 
 		turnFreeRedisReply(redisCommand(rc, "save"));
@@ -619,9 +558,14 @@ static int redis_del_oauth_key(const u08bits *kid) {
   return ret;
 }
   
-static int redis_list_users(int is_st, u08bits *realm) {
-  int ret = -1;
+static int redis_list_users(u08bits *realm, secrets_list_t *users, secrets_list_t *realms)
+{
+	int ret = -1;
 	redisContext *rc = get_redis_connection();
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
 	if(rc) {
 		secrets_list_t keys;
 		size_t isz = 0;
@@ -630,53 +574,15 @@ static int redis_list_users(int is_st, u08bits *realm) {
 
 		redisReply *reply = NULL;
 
-		if(!is_st) {
-
+		{
 			if(realm && realm[0]) {
 				reply = (redisReply*)redisCommand(rc, "keys turn/realm/%s/user/*/key", (char*)realm);
 			} else {
 				reply = (redisReply*)redisCommand(rc, "keys turn/realm/*/user/*/key");
 			}
-			if(reply) {
-
-				if (reply->type == REDIS_REPLY_ERROR)
-					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str);
-				else if (reply->type != REDIS_REPLY_ARRAY) {
-					if (reply->type != REDIS_REPLY_NIL)
-						TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type);
-				} else {
-					size_t i;
-					for (i = 0; i < reply->elements; ++i) {
-						add_to_secrets_list(&keys,reply->element[i]->str);
-					}
-				}
-				turnFreeRedisReply(reply);
-			}
 
-			if(realm && realm[0]) {
-				reply = (redisReply*)redisCommand(rc, "keys turn/realm/%s/user/*/password", (char*)realm);
-			} else {
-				reply = (redisReply*)redisCommand(rc, "keys turn/realm/*/user/*/password");
-			}
 			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);
-			}
-		} else {
-
-			reply = (redisReply*)redisCommand(rc, "keys turn/user/*/password");
-			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) {
@@ -692,25 +598,46 @@ static int redis_list_users(int is_st, u08bits *realm) {
 			}
 		}
 
+		size_t rhsz=strlen("turn/realm/");
+		size_t uhsz = strlen("user/");
+
 		for(isz=0;isz<keys.sz;++isz) {
 			char *s = keys.secrets[isz];
-			char *sh = strstr(s,"/user/");
-			if(sh) {
-				sh += 6;
-				char* st = strchr(sh,'/');
-				if(st)
-					*st=0;
-				printf("%s\n",sh);
+
+			char *sh = strstr(s,"turn/realm/");
+			if(sh != s) continue;
+			sh += rhsz;
+			char* st = strchr(sh,'/');
+			if(!st) continue;
+			*st=0;
+			char *sr = sh;
+			++st;
+
+			sh = strstr(st,"user/");
+			if(sh != st) continue;
+			sh += uhsz;
+			st = strchr(sh,'/');
+			if(!st) continue;
+			*st=0;
+			char *su = sh;
+
+			if(users) {
+				add_to_secrets_list(users,su);
+				if(realms) {
+					add_to_secrets_list(realms,sr);
+				}
+			} else {
+				printf("%s[%s]\n", su, sr);
 			}
 		}
 
 		clean_secrets_list(&keys);
 		ret = 0;
 	}
-  return ret;
+	return ret;
 }
 
-static int redis_list_oauth_keys(void) {
+static int redis_list_oauth_keys(secrets_list_t *kids,secrets_list_t *hkdfs,secrets_list_t *teas,secrets_list_t *aas,secrets_list_t *tss,secrets_list_t *lts) {
   int ret = -1;
   redisContext *rc = get_redis_connection();
   secrets_list_t keys;
@@ -746,9 +673,26 @@ static int redis_list_oauth_keys(void) {
 	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);
+		if(kids) {
+			add_to_secrets_list(kids,key->kid);
+			add_to_secrets_list(hkdfs,key->hkdf_hash_func);
+			add_to_secrets_list(teas,key->as_rs_alg);
+			add_to_secrets_list(aas,key->auth_alg);
+			{
+				char ts[256];
+				snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp);
+				add_to_secrets_list(tss,ts);
+			}
+			{
+				char lt[256];
+				snprintf(lt,sizeof(lt)-1,"%lu",(unsigned long)key->lifetime);
+				add_to_secrets_list(lts,lt);
+			}
+		} else {
+			printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, 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);
+		}
 	}
   }
 
@@ -759,9 +703,13 @@ static int redis_list_oauth_keys(void) {
 }
   
 
-static int redis_show_secret(u08bits *realm)
+static int redis_list_secrets(u08bits *realm, secrets_list_t *secrets, secrets_list_t *realms)
 {
 	int ret = -1;
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
 	donot_print_connection_success = 1;
 	redisContext *rc = get_redis_connection();
 	if (rc) {
@@ -790,6 +738,8 @@ static int redis_show_secret(u08bits *realm)
 				}
 			}
 
+			size_t rhsz=strlen("turn/realm/");
+
 			for (isz = 0; isz < keys.sz; ++isz) {
 				snprintf(s, sizeof(s), "smembers %s", keys.secrets[isz]);
 				redisReply *rget = (redisReply *) redisCommand(rc, s);
@@ -802,9 +752,32 @@ static int redis_show_secret(u08bits *realm)
 						if (rget->type != REDIS_REPLY_NIL)
 							TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", rget->type);
 					} else {
+
+						char *s = keys.secrets[isz];
+
+						char *sh = strstr(s,"turn/realm/");
+						if(sh != s) continue;
+						sh += rhsz;
+						char* st = strchr(sh,'/');
+						if(!st) continue;
+						*st=0;
+						const char *rval = sh;
+
 						size_t i;
 						for (i = 0; i < rget->elements; ++i) {
-							printf("%s\n", rget->element[i]->str);
+							const char *kval = rget->element[i]->str;
+							if(secrets) {
+								add_to_secrets_list(secrets,kval);
+								if(realms) {
+									if(rval && *rval) {
+								   		add_to_secrets_list(realms,rval);
+									} else {
+										add_to_secrets_list(realms,(char*)realm);
+									}
+								}
+							} else {
+								printf("%s[%s]\n", kval, rval);
+							}
 						}
 					}
 				}
@@ -853,6 +826,32 @@ static int redis_set_secret(u08bits *secret, u08bits *realm)
 	}
 	return ret;
 }
+
+static int redis_set_permission_ip(const char *kind, u08bits *realm, const char* ip, int del)
+{
+	int ret = -1;
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
+	donot_print_connection_success = 1;
+
+	redisContext *rc = get_redis_connection();
+	if (rc) {
+		char s[TURN_LONG_STRING_SIZE];
+
+		if(del) {
+			snprintf(s, sizeof(s), "srem turn/realm/%s/%s-peer-ip %s", (char*) realm, kind, ip);
+		} else {
+			snprintf(s, sizeof(s), "sadd turn/realm/%s/%s-peer-ip %s", (char*) realm, kind, ip);
+		}
+
+		turnFreeRedisReply(redisCommand(rc, s));
+		turnFreeRedisReply(redisCommand(rc, "save"));
+		ret = 0;
+	}
+	return ret;
+}
   
 static int redis_add_origin(u08bits *origin, u08bits *realm) {
   int ret = -1;
@@ -884,9 +883,15 @@ static int redis_del_origin(u08bits *origin) {
   return ret;
 }
   
-static int redis_list_origins(u08bits *realm) {
-  int ret = -1;
+static int redis_list_origins(u08bits *realm, secrets_list_t *origins, secrets_list_t *realms)
+{
+	int ret = -1;
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
 	donot_print_connection_success = 1;
+
 	redisContext *rc = get_redis_connection();
 	if(rc) {
 		secrets_list_t keys;
@@ -917,6 +922,7 @@ static int redis_list_origins(u08bits *realm) {
 		}
 
 		for(isz=0;isz<keys.sz;++isz) {
+
 			char *o = keys.secrets[isz];
 
 			reply = (redisReply*)redisCommand(rc, "get turn/origin/%s",o);
@@ -929,7 +935,14 @@ static int redis_list_origins(u08bits *realm) {
 						TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type);
 				} else {
 					if(!(realm && realm[0] && strcmp((char*)realm,reply->str))) {
-						printf("%s ==>> %s\n",o,reply->str);
+						if(origins) {
+							add_to_secrets_list(origins,o);
+							if(realms) {
+								add_to_secrets_list(realms,reply->str);
+							}
+						} else {
+							printf("%s ==>> %s\n",o,reply->str);
+						}
 					}
 				}
 				turnFreeRedisReply(reply);
@@ -937,9 +950,9 @@ static int redis_list_origins(u08bits *realm) {
 		}
 
 		clean_secrets_list(&keys);
-    ret = 0;
+		ret = 0;
 	}
-  return ret;
+	return ret;
 }
   
 static int redis_set_realm_option_one(u08bits *realm, unsigned long value, const char* opt) {
@@ -1203,17 +1216,142 @@ static void redis_reread_realms(secrets_list_t * realms_list) {
 	}
 }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////
+
+static int redis_get_admin_user(const u08bits *usname, u08bits *realm, password_t pwd)
+{
+	int ret = -1;
+	redisContext * rc = get_redis_connection();
+	if(rc) {
+		char s[TURN_LONG_STRING_SIZE];
+		realm[0]=0;
+		pwd[0]=0;
+		snprintf(s,sizeof(s),"hgetall turn/admin_user/%s", (const char*)usname);
+		redisReply *reply = (redisReply *)redisCommand(rc, s);
+		if(reply) {
+			if (reply->type == REDIS_REPLY_ERROR)
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str);
+			else if (reply->type != REDIS_REPLY_ARRAY) {
+				if (reply->type != REDIS_REPLY_NIL)
+					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type);
+			} else if(reply->elements > 1) {
+				size_t i;
+				for (i = 0; i < (reply->elements)/2; ++i) {
+					char *kw = reply->element[2*i]->str;
+					char *val = reply->element[2*i+1]->str;
+					if(kw) {
+						if(!strcmp(kw,"realm")) {
+							strncpy((char*)realm,val,STUN_MAX_REALM_SIZE);
+						} else if(!strcmp(kw,"password")) {
+							strncpy((char*)pwd,val,STUN_MAX_PWD_SIZE);
+							ret = 0;
+						}
+					}
+				}
+			}
+			turnFreeRedisReply(reply);
+		}
+	  }
+	  return ret;
+}
+
+static int redis_set_admin_user(const u08bits *usname, const u08bits *realm, const password_t pwd)
+{
+  int ret = -1;
+  donot_print_connection_success = 1;
+  redisContext *rc = get_redis_connection();
+  if(rc) {
+	char statement[TURN_LONG_STRING_SIZE];
+	if(realm[0]) {
+		snprintf(statement,sizeof(statement),"hmset turn/admin_user/%s realm %s password %s",usname,realm,pwd);
+	} else {
+		snprintf(statement,sizeof(statement),"hmset turn/admin_user/%s password %s",usname,pwd);
+	}
+	turnFreeRedisReply(redisCommand(rc, statement));
+	turnFreeRedisReply(redisCommand(rc, "save"));
+    ret = 0;
+  }
+  return ret;
+}
+
+static int redis_del_admin_user(const u08bits *usname) {
+  int ret = -1;
+  donot_print_connection_success = 1;
+  redisContext *rc = get_redis_connection();
+  if(rc) {
+	char statement[TURN_LONG_STRING_SIZE];
+	snprintf(statement,sizeof(statement),"del turn/admin_user/%s",(const char*)usname);
+	turnFreeRedisReply(redisCommand(rc, statement));
+	turnFreeRedisReply(redisCommand(rc, "save"));
+    ret = 0;
+  }
+  return ret;
+}
+
+static int redis_list_admin_users(int no_print)
+{
+  int ret = -1;
+  donot_print_connection_success = 1;
+  redisContext *rc = get_redis_connection();
+  secrets_list_t keys;
+  size_t isz = 0;
+  init_secrets_list(&keys);
+
+  if(rc) {
+
+	  redisReply *reply = NULL;
+
+	  reply = (redisReply*)redisCommand(rc, "keys turn/admin_user/*");
+	  if(reply) {
+
+		if (reply->type == REDIS_REPLY_ERROR) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str);
+		} else if (reply->type != REDIS_REPLY_ARRAY) {
+			if (reply->type != REDIS_REPLY_NIL) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type);
+			}
+		} else {
+			size_t i;
+			for (i = 0; i < reply->elements; ++i) {
+				add_to_secrets_list(&keys,reply->element[i]->str);
+			}
+		}
+		turnFreeRedisReply(reply);
+	}
+  }
+
+  ret = 0;
+  for(isz=0;isz<keys.sz;++isz) {
+	char *s = keys.secrets[isz];
+	s += strlen("turn/admin_user/");
+	u08bits realm[STUN_MAX_REALM_SIZE];
+	password_t pwd;
+	if(redis_get_admin_user((const u08bits*)s,realm,pwd) == 0) {
+		++ret;
+		if(!no_print) {
+			if(realm[0]) {
+				printf("%s[%s]\n",s,realm);
+			} else {
+				printf("%s\n",s);
+			}
+		}
+	}
+  }
+
+  clean_secrets_list(&keys);
+
+  return ret;
+}
+
+//////////////////////////////////////////////////////
 
 static const turn_dbdriver_t driver = {
   &redis_get_auth_secrets,
   &redis_get_user_key,
-  &redis_get_user_pwd,
   &redis_set_user_key,
-  &redis_set_user_pwd,
   &redis_del_user,
   &redis_list_users,
-  &redis_show_secret,
+  &redis_list_secrets,
   &redis_del_secret,
   &redis_set_secret,
   &redis_add_origin,
@@ -1223,11 +1361,16 @@ static const turn_dbdriver_t driver = {
   &redis_list_realm_options,
   &redis_auth_ping,
   &redis_get_ip_list,
+  &redis_set_permission_ip,
   &redis_reread_realms,
   &redis_set_oauth_key,
   &redis_get_oauth_key,
   &redis_del_oauth_key,
-  &redis_list_oauth_keys
+  &redis_list_oauth_keys,
+  &redis_get_admin_user,
+  &redis_set_admin_user,
+  &redis_del_admin_user,
+  &redis_list_admin_users
 };
 
 const turn_dbdriver_t * get_redis_dbdriver(void) {

+ 331 - 130
src/apps/relay/dbdrivers/dbd_sqlite.c

@@ -148,14 +148,14 @@ static void fix_user_directory(char *dir0) {
 static void init_sqlite_database(sqlite3 *sqliteconnection) {
 
 	const char * statements[] = {
-		"CREATE TABLE turnusers_lt ( realm varchar(512) default '', name varchar(512), hmackey char(128), PRIMARY KEY (realm,name))",
-		"CREATE TABLE turnusers_st (name varchar(512) PRIMARY KEY, password varchar(512))",
-		"CREATE TABLE turn_secret (realm varchar(512) default '', value varchar(512), primary key (realm,value))",
-		"CREATE TABLE allowed_peer_ip (realm varchar(512) default '', ip_range varchar(256), primary key (realm,ip_range))",
-		"CREATE TABLE denied_peer_ip (realm varchar(512) default '', ip_range varchar(256), primary key (realm,ip_range))",
-		"CREATE TABLE turn_origin_to_realm (origin varchar(512),realm varchar(512),primary key (origin))",
-		"CREATE TABLE turn_realm_option (realm varchar(512) default '',	opt varchar(32),	value varchar(128),	primary key (realm,opt))",
+		"CREATE TABLE turnusers_lt ( realm varchar(127) default '', name varchar(512), hmackey char(128), PRIMARY KEY (realm,name))",
+		"CREATE TABLE turn_secret (realm varchar(127) default '', value varchar(127), primary key (realm,value))",
+		"CREATE TABLE allowed_peer_ip (realm varchar(127) default '', ip_range varchar(256), primary key (realm,ip_range))",
+		"CREATE TABLE denied_peer_ip (realm varchar(127) default '', ip_range varchar(256), primary key (realm,ip_range))",
+		"CREATE TABLE turn_origin_to_realm (origin varchar(127),realm varchar(127),primary key (origin))",
+		"CREATE TABLE turn_realm_option (realm varchar(127) default '',	opt varchar(32),	value varchar(128),	primary key (realm,opt))",
 		"CREATE TABLE oauth_key (kid varchar(128),ikm_key varchar(256) 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))",
+		"CREATE TABLE admin_user (uname varchar(32), realm varchar(127), password varchar(127), primary key (uname))",
 		NULL
 	};
 
@@ -188,10 +188,12 @@ static sqlite3 * get_sqlite_connection(void) {
 				sqliteconnection=NULL;
 			}
 			turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_UNKNOWN;
-		} else if(!donot_print_connection_success){
+		} else {
 			init_sqlite_database(sqliteconnection);
-			TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SQLite DB connection success: %s\n",pud->userdb);
-			donot_print_connection_success = 1;
+			if(!donot_print_connection_success){
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "SQLite DB connection success: %s\n",pud->userdb);
+				donot_print_connection_success = 1;
+			}
 		}
 		if(sqliteconnection) {
 			(void) pthread_setspecific(connection_key, sqliteconnection);
@@ -284,42 +286,6 @@ static int sqlite_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key)
 	return ret;
 }
 
-static int sqlite_get_user_pwd(u08bits *usname, st_password_t pwd)
-{
-	int ret = -1;
-	char statement[TURN_LONG_STRING_SIZE];
-	sqlite3_stmt *st = NULL;
-	int rc = 0;
-	snprintf(statement, sizeof(statement), "select password from turnusers_st where name='%s'", usname);
-
-	sqlite3 *sqliteconnection = get_sqlite_connection();
-	if (sqliteconnection) {
-
-		sqlite_lock(0);
-
-		if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) {
-			int res = sqlite3_step(st);
-			if (res == SQLITE_ROW) {
-				const char *kval = (const char*) sqlite3_column_text(st, 0);
-				if (kval) {
-					strncpy((char*) pwd, kval, sizeof(st_password_t));
-					ret = 0;
-				} else {
-					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong password data for user %s: NULL\n", usname);
-				}
-			}
-		} else {
-			const char* errmsg = sqlite3_errmsg(sqliteconnection);
-			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg);
-		}
-
-		sqlite3_finalize(st);
-
-		sqlite_unlock(0);
-	}
-	return ret;
-}
-
 static int sqlite_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
 
 	int ret = -1;
@@ -339,15 +305,15 @@ static int sqlite_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
 			int res = sqlite3_step(st);
 			if (res == SQLITE_ROW) {
 
-				STRCPY((char*)key->ikm_key,sqlite3_column_text(st, 0));
+				STRCPY(key->ikm_key,sqlite3_column_text(st, 0));
 				key->timestamp = (u64bits)strtoll((const char*)sqlite3_column_text(st, 1),NULL,10);
 				key->lifetime = (u32bits)strtol((const char*)sqlite3_column_text(st, 2),NULL,10);
-				STRCPY((char*)key->hkdf_hash_func,sqlite3_column_text(st, 3));
-				STRCPY((char*)key->as_rs_alg,sqlite3_column_text(st, 4));
-				STRCPY((char*)key->as_rs_key,sqlite3_column_text(st, 5));
-				STRCPY((char*)key->auth_alg,sqlite3_column_text(st, 6));
-				STRCPY((char*)key->auth_key,sqlite3_column_text(st, 7));
-				STRCPY((char*)key->kid,kid);
+				STRCPY(key->hkdf_hash_func,sqlite3_column_text(st, 3));
+				STRCPY(key->as_rs_alg,sqlite3_column_text(st, 4));
+				STRCPY(key->as_rs_key,sqlite3_column_text(st, 5));
+				STRCPY(key->auth_alg,sqlite3_column_text(st, 6));
+				STRCPY(key->auth_key,sqlite3_column_text(st, 7));
+				STRCPY(key->kid,kid);
 				ret = 0;
 			}
 		} else {
@@ -363,7 +329,7 @@ static int sqlite_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
 	return ret;
 }
 
-static int sqlite_list_oauth_keys(void) {
+static int sqlite_list_oauth_keys(secrets_list_t *kids,secrets_list_t *hkdfs,secrets_list_t *teas,secrets_list_t *aas,secrets_list_t *tss,secrets_list_t *lts) {
 
 	oauth_key_data_raw key_;
 	oauth_key_data_raw *key=&key_;
@@ -389,19 +355,36 @@ static int sqlite_list_oauth_keys(void) {
 				int res = sqlite3_step(st);
 				if (res == SQLITE_ROW) {
 
-					STRCPY((char*)key->ikm_key,sqlite3_column_text(st, 0));
+					STRCPY(key->ikm_key,sqlite3_column_text(st, 0));
 					key->timestamp = (u64bits)strtoll((const char*)sqlite3_column_text(st, 1),NULL,10);
 					key->lifetime = (u32bits)strtol((const char*)sqlite3_column_text(st, 2),NULL,10);
-					STRCPY((char*)key->hkdf_hash_func,sqlite3_column_text(st, 3));
-					STRCPY((char*)key->as_rs_alg,sqlite3_column_text(st, 4));
-					STRCPY((char*)key->as_rs_key,sqlite3_column_text(st, 5));
-					STRCPY((char*)key->auth_alg,sqlite3_column_text(st, 6));
-					STRCPY((char*)key->auth_key,sqlite3_column_text(st, 7));
-					STRCPY((char*)key->kid,sqlite3_column_text(st, 7));
-
-					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);
+					STRCPY(key->hkdf_hash_func,sqlite3_column_text(st, 3));
+					STRCPY(key->as_rs_alg,sqlite3_column_text(st, 4));
+					STRCPY(key->as_rs_key,sqlite3_column_text(st, 5));
+					STRCPY(key->auth_alg,sqlite3_column_text(st, 6));
+					STRCPY(key->auth_key,sqlite3_column_text(st, 7));
+					STRCPY(key->kid,sqlite3_column_text(st, 8));
+
+					if(kids) {
+						add_to_secrets_list(kids,key->kid);
+						add_to_secrets_list(hkdfs,key->hkdf_hash_func);
+						add_to_secrets_list(teas,key->as_rs_alg);
+						add_to_secrets_list(aas,key->auth_alg);
+						{
+							char ts[256];
+							snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp);
+							add_to_secrets_list(tss,ts);
+						}
+						{
+							char lt[256];
+							snprintf(lt,sizeof(lt)-1,"%lu",(unsigned long)key->lifetime);
+							add_to_secrets_list(lts,lt);
+						}
+					} else {
+						printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, 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);
+					}
 
 				} else if (res == SQLITE_DONE) {
 					break;
@@ -481,36 +464,7 @@ static int sqlite_set_oauth_key(oauth_key_data_raw *key)
 			ret = 0;
 		} else {
 			const char* errmsg = sqlite3_errmsg(sqliteconnection);
-			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg);
-		}
-		sqlite3_finalize(st);
-
-		sqlite_unlock(1);
-	}
-	return ret;
-}
-
-static int sqlite_set_user_pwd(u08bits *usname, st_password_t pwd)
-{
-	int ret = -1;
-	char statement[TURN_LONG_STRING_SIZE];
-	sqlite3_stmt *st = NULL;
-	int rc = 0;
-
-	donot_print_connection_success=1;
-
-	sqlite3 *sqliteconnection = get_sqlite_connection();
-	if (sqliteconnection) {
-		snprintf(statement, sizeof(statement), "insert or replace into turnusers_st values('%s','%s')", usname, pwd);
-
-		sqlite_lock(1);
-
-		if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) {
-			sqlite3_step(st);
-			ret = 0;
-		} else {
-			const char* errmsg = sqlite3_errmsg(sqliteconnection);
-			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg);
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error updating SQLite DB information: %s\n", errmsg);
 		}
 		sqlite3_finalize(st);
 
@@ -519,7 +473,7 @@ static int sqlite_set_user_pwd(u08bits *usname, st_password_t pwd)
 	return ret;
 }
 
-static int sqlite_del_user(u08bits *usname, int is_st, u08bits *realm)
+static int sqlite_del_user(u08bits *usname, u08bits *realm)
 {
 	int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
@@ -530,11 +484,7 @@ static int sqlite_del_user(u08bits *usname, int is_st, u08bits *realm)
 
 	sqlite3 *sqliteconnection = get_sqlite_connection();
 	if (sqliteconnection) {
-		if (is_st) {
-			snprintf(statement, sizeof(statement), "delete from turnusers_st where name='%s'", usname);
-		} else {
-			snprintf(statement, sizeof(statement), "delete from turnusers_lt where name='%s' and realm='%s'", usname, realm);
-		}
+		snprintf(statement, sizeof(statement), "delete from turnusers_lt where name='%s' and realm='%s'", usname, realm);
 
 		sqlite_lock(1);
 
@@ -583,23 +533,24 @@ static int sqlite_del_oauth_key(const u08bits *kid)
 }
 
 
-static int sqlite_list_users(int is_st, u08bits *realm)
+static int sqlite_list_users(u08bits *realm, secrets_list_t *users, secrets_list_t *realms)
 {
 	int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	sqlite3_stmt *st = NULL;
 	int rc = 0;
 
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
 	donot_print_connection_success=1;
 
 	sqlite3 *sqliteconnection = get_sqlite_connection();
 	if (sqliteconnection) {
-		if (is_st) {
-			snprintf(statement, sizeof(statement), "select name,'' from turnusers_st order by name");
-		} else if (realm && realm[0]) {
+		if (realm[0]) {
 			snprintf(statement, sizeof(statement), "select name,realm from turnusers_lt where realm='%s' order by name", realm);
 		} else {
-			snprintf(statement, sizeof(statement), "select name,realm from turnusers_lt order by name");
+			snprintf(statement, sizeof(statement), "select name,realm from turnusers_lt order by realm,name");
 		}
 
 		sqlite_lock(0);
@@ -614,10 +565,17 @@ static int sqlite_list_users(int is_st, u08bits *realm)
 					const char* kval = (const char*) sqlite3_column_text(st, 0);
 					const char* rval = (const char*) sqlite3_column_text(st, 1);
 
-					if (rval && *rval) {
-						printf("%s[%s]\n", kval, rval);
+					if(users) {
+						add_to_secrets_list(users,kval);
+						if(realms) {
+							if(rval && *rval) {
+								add_to_secrets_list(realms,rval);
+							} else {
+								add_to_secrets_list(realms,(char*)realm);
+							}
+						}
 					} else {
-						printf("%s\n", kval);
+						printf("%s[%s]\n", kval, rval);
 					}
 
 				} else if (res == SQLITE_DONE) {
@@ -640,13 +598,22 @@ static int sqlite_list_users(int is_st, u08bits *realm)
 	return ret;
 }
 
-static int sqlite_show_secret(u08bits *realm)
+static int sqlite_list_secrets(u08bits *realm, secrets_list_t *secrets, secrets_list_t *realms)
 {
 	int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
 	sqlite3_stmt *st = NULL;
 	int rc = 0;
-	snprintf(statement,sizeof(statement)-1,"select value from turn_secret where realm='%s'",realm);
+
+	if (realm[0]) {
+		snprintf(statement, sizeof(statement), "select value,realm from turn_secret where realm='%s' order by value", realm);
+	} else {
+		snprintf(statement, sizeof(statement), "select value,realm from turn_secret order by realm,value");
+	}
 
 	donot_print_connection_success=1;
 
@@ -656,17 +623,37 @@ static int sqlite_show_secret(u08bits *realm)
 		sqlite_lock(0);
 
 		if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) {
-			int res = sqlite3_step(st);
-			if (res == SQLITE_ROW) {
-				ret = 0;
-				const char* kval = (const char*) sqlite3_column_text(st, 0);
-				if(kval) {
-					printf("%s\n",kval);
+
+			int res = 0;
+			while(1) {
+				res = sqlite3_step(st);
+				if (res == SQLITE_ROW) {
+					ret = 0;
+					const char* kval = (const char*) sqlite3_column_text(st, 0);
+					if(kval) {
+						const char* rval = (const char*) sqlite3_column_text(st, 1);
+						if(secrets) {
+							add_to_secrets_list(secrets,kval);
+							if(realms) {
+								if(rval && *rval) {
+									add_to_secrets_list(realms,rval);
+								} else {
+									add_to_secrets_list(realms,(char*)realm);
+								}
+							}
+						} else {
+							printf("%s[%s]\n",kval,rval);
+						}
+					}
+				} else if (res == SQLITE_DONE) {
+					break;
+				} else {
+					const char* errmsg = sqlite3_errmsg(sqliteconnection);
+					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg);
+					ret = -1;
+					break;
 				}
 			}
-		} else {
-			const char* errmsg = sqlite3_errmsg(sqliteconnection);
-			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg);
 		}
 		sqlite3_finalize(st);
 
@@ -791,20 +778,25 @@ static int sqlite_del_origin(u08bits *origin)
 	return ret;
 }
 
-static int sqlite_list_origins(u08bits *realm)
+static int sqlite_list_origins(u08bits *realm, secrets_list_t *origins, secrets_list_t *realms)
 {
 	int ret = -1;
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
 	donot_print_connection_success = 1;
-	char statement[TURN_LONG_STRING_SIZE];
+
 	sqlite3_stmt *st = NULL;
 	int rc = 0;
 
 	sqlite3 *sqliteconnection = get_sqlite_connection();
 	if (sqliteconnection) {
+		char statement[TURN_LONG_STRING_SIZE];
 		if (realm && realm[0]) {
 			snprintf(statement, sizeof(statement), "select origin,realm from turn_origin_to_realm where realm='%s' order by origin", realm);
 		} else {
-			snprintf(statement, sizeof(statement), "select origin,realm from turn_origin_to_realm order by origin,realm");
+			snprintf(statement, sizeof(statement), "select origin,realm from turn_origin_to_realm order by realm,origin");
 		}
 
 		sqlite_lock(0);
@@ -819,8 +811,18 @@ static int sqlite_list_origins(u08bits *realm)
 					const char* kval = (const char*) sqlite3_column_text(st, 0);
 					const char* rval = (const char*) sqlite3_column_text(st, 1);
 
-					printf("%s ==>> %s\n",kval,rval);
-
+					if(origins) {
+						add_to_secrets_list(origins,kval);
+						if(realms) {
+							if(rval && *rval) {
+								add_to_secrets_list(realms,rval);
+							} else {
+								add_to_secrets_list(realms,(char*)realm);
+							}
+						}
+					} else {
+						printf("%s ==>> %s\n",kval,rval);
+					}
 				} else if (res == SQLITE_DONE) {
 					break;
 				} else {
@@ -974,6 +976,45 @@ static int sqlite_get_ip_list(const char *kind, ip_range_list_t * list)
 	return ret;
 }
 
+static int sqlite_set_permission_ip(const char *kind, u08bits *realm, const char* ip, int del)
+{
+	int ret = -1;
+
+	u08bits realm0[STUN_MAX_REALM_SIZE+1] = "\0";
+	if(!realm) realm=realm0;
+
+	char statement[TURN_LONG_STRING_SIZE];
+
+	sqlite3_stmt *st = NULL;
+	int rc = 0;
+
+	donot_print_connection_success=1;
+
+	sqlite3 *sqliteconnection = get_sqlite_connection();
+	if (sqliteconnection) {
+
+		sqlite_lock(1);
+
+		if(del) {
+			snprintf(statement, sizeof(statement), "delete from %s_peer_ip where realm = '%s'  and ip_range = '%s'", kind, (char*)realm, ip);
+		} else {
+			snprintf(statement, sizeof(statement), "insert or replace into %s_peer_ip (realm,ip_range) values('%s','%s')", kind, (char*)realm, ip);
+		}
+
+		if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) {
+			sqlite3_step(st);
+			ret = 0;
+		} else {
+			const char* errmsg = sqlite3_errmsg(sqliteconnection);
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error updating SQLite DB information: %s\n", errmsg);
+		}
+		sqlite3_finalize(st);
+
+		sqlite_unlock(1);
+	}
+	return ret;
+}
+
 static void sqlite_reread_realms(secrets_list_t * realms_list)
 {
 	sqlite3 *sqliteconnection = get_sqlite_connection();
@@ -1098,17 +1139,172 @@ static void sqlite_reread_realms(secrets_list_t * realms_list)
 	}
 }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////
+
+static int sqlite_get_admin_user(const u08bits *usname, u08bits *realm, password_t pwd)
+{
+	int ret = -1;
+
+	realm[0]=0;
+	pwd[0]=0;
+
+	sqlite3 *sqliteconnection = get_sqlite_connection();
+	if (sqliteconnection) {
+		char statement[TURN_LONG_STRING_SIZE];
+		sqlite3_stmt *st = NULL;
+		int rc = 0;
+		snprintf(statement, sizeof(statement), "select realm,password from admin_user where name='%s'", usname);
+
+		sqlite_lock(0);
+
+		if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) {
+			int res = sqlite3_step(st);
+			if (res == SQLITE_ROW) {
+				const char *kval = (const char*) sqlite3_column_text(st, 0);
+				if(kval) {
+					strncpy((char*)realm,kval,STUN_MAX_REALM_SIZE);
+				}
+				kval = (const char*) sqlite3_column_text(st, 1);
+				if(kval) {
+					strncpy((char*)pwd,kval,STUN_MAX_PWD_SIZE);
+				}
+				ret = 0;
+			}
+		} else {
+			const char* errmsg = sqlite3_errmsg(sqliteconnection);
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg);
+		}
+
+		sqlite3_finalize(st);
+
+		sqlite_unlock(0);
+	}
+	return ret;
+}
+
+static int sqlite_set_admin_user(const u08bits *usname, const u08bits *realm, const password_t pwd)
+{
+	int ret = -1;
+	char statement[TURN_LONG_STRING_SIZE];
+	sqlite3_stmt *st = NULL;
+	int rc = 0;
+
+	donot_print_connection_success=1;
+
+	sqlite3 *sqliteconnection = get_sqlite_connection();
+	if (sqliteconnection) {
+
+		sqlite_lock(1);
+
+		snprintf(statement, sizeof(statement), "insert or replace into admin_user (realm,name,password) values('%s','%s','%s')", realm, usname, pwd);
+
+		if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) {
+			sqlite3_step(st);
+			ret = 0;
+		} else {
+			const char* errmsg = sqlite3_errmsg(sqliteconnection);
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg);
+		}
+		sqlite3_finalize(st);
+
+		sqlite_unlock(1);
+	}
+	return ret;
+}
+
+static int sqlite_del_admin_user(const u08bits *usname)
+{
+	int ret = -1;
+	char statement[TURN_LONG_STRING_SIZE];
+	sqlite3_stmt *st = NULL;
+	int rc = 0;
+
+	donot_print_connection_success=1;
+
+	sqlite3 *sqliteconnection = get_sqlite_connection();
+	if (sqliteconnection) {
+		snprintf(statement, sizeof(statement), "delete from admin_user where name='%s'", usname);
+
+		sqlite_lock(1);
+
+		if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) {
+			sqlite3_step(st);
+			ret = 0;
+		} else {
+			const char* errmsg = sqlite3_errmsg(sqliteconnection);
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg);
+		}
+		sqlite3_finalize(st);
+
+		sqlite_unlock(1);
+	}
+	return ret;
+}
+
+static int sqlite_list_admin_users(int no_print)
+{
+	int ret = -1;
+	char statement[TURN_LONG_STRING_SIZE];
+	sqlite3_stmt *st = NULL;
+	int rc = 0;
+
+	donot_print_connection_success=1;
+
+	sqlite3 *sqliteconnection = get_sqlite_connection();
+	if (sqliteconnection) {
+		snprintf(statement, sizeof(statement), "select name,realm from admin_user order by realm,name");
+
+		sqlite_lock(0);
+
+		if ((rc = sqlite3_prepare(sqliteconnection, statement, -1, &st, 0)) == SQLITE_OK) {
+
+			ret = 0;
+			while (1) {
+				int res = sqlite3_step(st);
+				if (res == SQLITE_ROW) {
+
+					const char* kval = (const char*) sqlite3_column_text(st, 0);
+					const char* rval = (const char*) sqlite3_column_text(st, 1);
+
+					if(!no_print) {
+						if (rval && *rval) {
+							printf("%s[%s]\n", kval, rval);
+						} else {
+							printf("%s\n", kval);
+						}
+					}
+
+					++ret;
+
+				} else if (res == SQLITE_DONE) {
+					break;
+				} else {
+					const char* errmsg = sqlite3_errmsg(sqliteconnection);
+					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg);
+					ret = -1;
+					break;
+				}
+			}
+		} else {
+			const char* errmsg = sqlite3_errmsg(sqliteconnection);
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving SQLite DB information: %s\n", errmsg);
+		}
+		sqlite3_finalize(st);
+
+		sqlite_unlock(0);
+	}
+	return ret;
+}
+
+///////////////////////////////////////////////////////
 
 static const turn_dbdriver_t driver = {
   &sqlite_get_auth_secrets,
   &sqlite_get_user_key,
-  &sqlite_get_user_pwd,
   &sqlite_set_user_key,
-  &sqlite_set_user_pwd,
   &sqlite_del_user,
   &sqlite_list_users,
-  &sqlite_show_secret,
+  &sqlite_list_secrets,
   &sqlite_del_secret,
   &sqlite_set_secret,
   &sqlite_add_origin,
@@ -1118,11 +1314,16 @@ static const turn_dbdriver_t driver = {
   &sqlite_list_realm_options,
   &sqlite_auth_ping,
   &sqlite_get_ip_list,
+  &sqlite_set_permission_ip,
   &sqlite_reread_realms,
   &sqlite_set_oauth_key,
   &sqlite_get_oauth_key,
   &sqlite_del_oauth_key,
-  &sqlite_list_oauth_keys
+  &sqlite_list_oauth_keys,
+  &sqlite_get_admin_user,
+  &sqlite_set_admin_user,
+  &sqlite_del_admin_user,
+  &sqlite_list_admin_users
 };
 
 //////////////////////////////////////////////////

+ 10 - 7
src/apps/relay/dbdrivers/dbdriver.h

@@ -50,26 +50,29 @@ extern pthread_once_t connection_key_once;
 typedef struct _turn_dbdriver_t {
   int (*get_auth_secrets)(secrets_list_t *sl, u08bits *realm);
   int (*get_user_key)(u08bits *usname, u08bits *realm, hmackey_t key);
-  int (*get_user_pwd)(u08bits *usname, st_password_t pwd);
   int (*set_user_key)(u08bits *usname, u08bits *realm, const char *key);
-  int (*set_user_pwd)(u08bits *usname, st_password_t pwd);
-  int (*del_user)(u08bits *usname, int is_st, u08bits *realm);
-  int (*list_users)(int is_st, u08bits *realm);
-  int (*show_secret)(u08bits *realm);
+  int (*del_user)(u08bits *usname, u08bits *realm);
+  int (*list_users)(u08bits *realm, secrets_list_t *users, secrets_list_t *realms);
+  int (*list_secrets)(u08bits *realm, secrets_list_t *secrets, secrets_list_t *realms);
   int (*del_secret)(u08bits *secret, u08bits *realm);
   int (*set_secret)(u08bits *secret, u08bits *realm);
   int (*add_origin)(u08bits *origin, u08bits *realm);
   int (*del_origin)(u08bits *origin);
-  int (*list_origins)(u08bits *realm);
+  int (*list_origins)(u08bits *realm, secrets_list_t *origins, secrets_list_t *realms);
   int (*set_realm_option_one)(u08bits *realm, unsigned long value, const char* opt);
   int (*list_realm_options)(u08bits *realm);
   void (*auth_ping)(void * rch);
   int (*get_ip_list)(const char *kind, ip_range_list_t * list);
+  int (*set_permission_ip)(const char *kind, u08bits *realm, const char* ip, int del);
   void (*reread_realms)(secrets_list_t * realms_list);
   int (*set_oauth_key)(oauth_key_data_raw *key);
   int (*get_oauth_key)(const u08bits *kid, oauth_key_data_raw *key);
   int (*del_oauth_key)(const u08bits *kid);
-  int (*list_oauth_keys)(void);
+  int (*list_oauth_keys)(secrets_list_t *kids,secrets_list_t *hkdfs,secrets_list_t *teas,secrets_list_t *aas,secrets_list_t *tss,secrets_list_t *lts);
+  int (*get_admin_user)(const u08bits *usname, u08bits *realm, password_t pwd);
+  int (*set_admin_user)(const u08bits *usname, const u08bits *realm, const password_t pwd);
+  int (*del_admin_user)(const u08bits *usname);
+  int (*list_admin_users)(int no_print);
 } turn_dbdriver_t;
 
 /////////// USER DB CHECK //////////////////

+ 3 - 1
src/apps/relay/dtls_listener.c

@@ -459,7 +459,9 @@ static int handle_udp_packet(dtls_listener_relay_server_type *server,
 			}
 			s->e = ioa_eng;
 			add_socket_to_map(s, amap);
-			open_client_connection_session(ts, &(sm->m.sm));
+			if(open_client_connection_session(ts, &(sm->m.sm))<0) {
+				return -1;
+			}
 		}
 	}
 

+ 328 - 7
src/apps/relay/http_server.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2012, 2013 Citrix Systems
+ * Copyright (C) 2011, 2012, 2013, 2014 Citrix Systems
  *
  * All rights reserved.
  *
@@ -30,7 +30,29 @@
 
 #include "ns_ioalib_impl.h"
 
-void write_http_echo(ioa_socket_handle s)
+#include "http_server.h"
+
+#include <event2/http.h>
+#include <event2/keyvalq_struct.h>
+
+#include <time.h>
+
+//////////////////////////////////////
+
+struct headers_list {
+	size_t n;
+	char **keys;
+	char **values;
+};
+
+struct http_headers {
+	struct evkeyvalq *uri_headers;
+	struct headers_list *post_headers;
+};
+
+//////////////////////////////////////
+
+static void write_http_echo(ioa_socket_handle s)
 {
 	if(s && !ioa_socket_tobeclosed(s)) {
 		SOCKET_APP_TYPE sat = get_ioa_socket_app_type(s);
@@ -41,8 +63,8 @@ void write_http_echo(ioa_socket_handle s)
 			char data_http[1025];
 			char content_http[1025];
 			const char* title = "TURN Server";
-			snprintf(content_http,sizeof(content_http)-1,"<!DOCTYPE html>\r\n<html>\r\n  <head>\r\n    <title>%s</title>\r\n  </head>\r\n  <body>\r\n    %s\r\n  </body>\r\n</html>\r\n",title,title);
-			snprintf(data_http,sizeof(data_http)-1,"HTTP/1.1 200 OK\r\nServer: %s\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: %d\r\n\r\n%s",TURN_SOFTWARE,(int)strlen(content_http),content_http);
+			snprintf(content_http,sizeof(content_http)-1,"<!DOCTYPE html>\r\n<html>\r\n  <head>\r\n    <title>%s</title>\r\n  </head>\r\n  <body>\r\n    <b>%s</b> <br> <b><i>use https connection for the admin session</i></b>\r\n  </body>\r\n</html>\r\n",title,title);
+			snprintf(data_http,sizeof(data_http)-1,"HTTP/1.0 200 OK\r\nServer: %s\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: %d\r\n\r\n%s",TURN_SOFTWARE,(int)strlen(content_http),content_http);
 			len_http = strlen(data_http);
 			ns_bcopy(data_http,data,len_http);
 			ioa_network_buffer_set_size(nbh_http,len_http);
@@ -51,8 +73,307 @@ void write_http_echo(ioa_socket_handle s)
 	}
 }
 
-void handle_https(ioa_socket_handle s, ioa_network_buffer_handle nbh) {
-	//TODO
-	UNUSED_ARG(nbh);
+void handle_http_echo(ioa_socket_handle s) {
 	write_http_echo(s);
 }
+
+const char* get_http_date_header()
+{
+	static char buffer_date[256];
+	static char buffer_header[1025];
+	static const char* wds[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
+	static const char* mons[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
+
+	time_t now = time(NULL);
+	struct tm *gmtm = gmtime(&now);
+
+	buffer_header[0]=0;
+	buffer_date[0]=0;
+	if(gmtm) {
+		snprintf(buffer_date,sizeof(buffer_date)-1,"%s, %d %s %d %d:%d:%d GMT",wds[gmtm->tm_wday], gmtm->tm_mday, mons[gmtm->tm_mon], gmtm->tm_year+1900, gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
+		buffer_date[sizeof(buffer_date)-1]=0;
+		snprintf(buffer_header,sizeof(buffer_header)-1,"Date: %s\r\n",buffer_date);
+		buffer_header[sizeof(buffer_header)-1]=0;
+	}
+
+	return buffer_header;
+}
+
+///////////////////////////////////////////////
+
+static struct headers_list * post_parse(char *data, size_t data_len)
+{
+	while((*data=='\r')||(*data=='\n')) ++data;
+	char *post_data = (char*)calloc(data_len + 1, sizeof(char));
+	memcpy(post_data, data, data_len);
+	char *fmarker = NULL;
+	char *fsplit = strtok_r(post_data, "&", &fmarker);
+	struct headers_list *list = (struct headers_list*)malloc(sizeof(struct headers_list));
+	ns_bzero(list,sizeof(struct headers_list));
+	while (fsplit != NULL) {
+		char *vmarker = NULL;
+		char *key = strtok_r(fsplit, "=", &vmarker);
+		char *value = strtok_r(NULL, "=", &vmarker);
+		char empty[1];
+		empty[0]=0;
+		value = value ? value : empty;
+		value = evhttp_decode_uri(value);
+		char *p = value;
+		while (*p) {
+			if (*p == '+')
+				*p = ' ';
+			p++;
+		}
+		list->keys = (char**)realloc(list->keys,sizeof(char*)*(list->n+1));
+		list->keys[list->n] = strdup(key);
+		list->values = (char**)realloc(list->values,sizeof(char*)*(list->n+1));
+		list->values[list->n] = value;
+		++(list->n);
+		fsplit = strtok_r(NULL, "&", &fmarker);
+	}
+	free(post_data);
+	return list;
+}
+
+static struct http_request* parse_http_request_1(struct http_request* ret, char* request, int parse_post)
+{
+
+	if(ret && request) {
+
+		char* s = strstr(request," HTTP/");
+		if(!s) {
+			free(ret);
+			ret = NULL;
+		} else {
+			*s = 0;
+
+			struct evhttp_uri *uri = evhttp_uri_parse(request);
+			if(!uri) {
+				free(ret);
+				ret = NULL;
+			} else {
+
+				const char *query = evhttp_uri_get_query(uri);
+				if(query) {
+					struct evkeyvalq* kv = (struct evkeyvalq*)malloc(sizeof(struct evkeyvalq));
+					ns_bzero(kv,sizeof(struct evkeyvalq));
+					if(evhttp_parse_query_str(query, kv)<0) {
+						free(ret);
+						ret = NULL;
+					} else {
+						ret->headers = (struct http_headers*)malloc(sizeof(struct http_headers));
+						ns_bzero(ret->headers,sizeof(struct http_headers));
+						ret->headers->uri_headers = kv;
+					}
+				}
+
+				const char *path = evhttp_uri_get_path(uri);
+				if(path)
+					ret->path = strdup(path);
+
+				evhttp_uri_free(uri);
+
+				if(parse_post) {
+					char *body = strstr(s+1,"\r\n\r\n");
+					if(body && body[0]) {
+						if(!ret->headers) {
+							ret->headers = (struct http_headers*)malloc(sizeof(struct http_headers));
+							ns_bzero(ret->headers,sizeof(struct http_headers));
+						}
+						ret->headers->post_headers = post_parse(body,strlen(body));
+					}
+				}
+			}
+
+			*s = ' ';
+		}
+	}
+
+	return ret;
+}
+
+struct http_request* parse_http_request(char* request) {
+
+	struct http_request* ret = NULL;
+
+	if(request) {
+
+		ret = (struct http_request*)malloc(sizeof(struct http_request));
+		ns_bzero(ret,sizeof(struct http_request));
+
+		if(strstr(request,"GET ") == request) {
+			ret->rtype = HRT_GET;
+			ret = parse_http_request_1(ret,request+4,0);
+		} else if(strstr(request,"HEAD ") == request) {
+			ret->rtype = HRT_HEAD;
+			ret = parse_http_request_1(ret,request+5,0);
+		} else if(strstr(request,"POST ") == request) {
+			ret->rtype = HRT_POST;
+			ret = parse_http_request_1(ret,request+5,1);
+		} else if(strstr(request,"PUT ") == request) {
+			ret->rtype = HRT_PUT;
+			ret = parse_http_request_1(ret,request+4,1);
+		} else if(strstr(request,"DELETE ") == request) {
+			ret->rtype = HRT_DELETE;
+			ret = parse_http_request_1(ret,request+7,1);
+		} else {
+			free(ret);
+			ret = NULL;
+		}
+	}
+
+	return ret;
+}
+
+static const char * get_headers_list_value(struct headers_list *h, const char* key) {
+	const char* ret = NULL;
+	if(h && h->keys && h->values && key && key[0]) {
+		size_t i = 0;
+		for(i=0;i<h->n;++i) {
+			if(h->keys[i] && !strcmp(key,h->keys[i]) && h->values[i]) {
+				ret = h->values[i];
+				break;
+			}
+		}
+	}
+	return ret;
+}
+
+static void free_headers_list(struct headers_list *h) {
+	if(h) {
+		if(h->keys) {
+			size_t i = 0;
+			for(i=0;i<h->n;++i) {
+				if(h->keys[i]) {
+					free(h->keys[i]);
+					h->keys[i]=NULL;
+				}
+			}
+			free(h->keys);
+			h->keys = NULL;
+		}
+		if(h->values) {
+			size_t i = 0;
+			for(i=0;i<h->n;++i) {
+				if(h->values[i]) {
+					free(h->values[i]);
+					h->values[i]=NULL;
+				}
+			}
+			free(h->values);
+			h->values = NULL;
+		}
+		h->n = 0;
+		free(h);
+	}
+}
+
+const char *get_http_header_value(const struct http_request *request, const char* key, const char* default_value) {
+	const char *ret = NULL;
+	if(key && key[0] && request && request->headers) {
+		if(request->headers->uri_headers) {
+			ret = evhttp_find_header(request->headers->uri_headers,key);
+		}
+		if(!ret && request->headers->post_headers) {
+			ret = get_headers_list_value(request->headers->post_headers,key);
+		}
+	}
+	if(!ret) {
+		ret = default_value;
+	}
+	return ret;
+}
+
+void free_http_request(struct http_request *request) {
+	if(request) {
+		if(request->path) {
+			free(request->path);
+			request->path = NULL;
+		}
+		if(request->headers) {
+			if(request->headers->uri_headers) {
+				evhttp_clear_headers(request->headers->uri_headers);
+				free(request->headers->uri_headers);
+				request->headers->uri_headers = NULL;
+			}
+			if(request->headers->post_headers) {
+				free_headers_list(request->headers->post_headers);
+				request->headers->post_headers = NULL;
+			}
+			free(request->headers);
+			request->headers = NULL;
+		}
+		free(request);
+	}
+}
+
+////////////////////////////////////////////
+
+struct str_buffer {
+	size_t capacity;
+	size_t sz;
+	char* buffer;
+};
+
+struct str_buffer* str_buffer_new(void)
+{
+	struct str_buffer* ret = (struct str_buffer*)malloc(sizeof(struct str_buffer));
+	ns_bzero(ret,sizeof(struct str_buffer));
+	ret->buffer = (char*)malloc(1);
+	ret->buffer[0] = 0;
+	ret->capacity = 1;
+	return ret;
+}
+
+void str_buffer_append(struct str_buffer* sb, const char* str)
+{
+	if(sb && str && str[0]) {
+		size_t len = strlen(str);
+		while(sb->sz + len + 1 > sb->capacity) {
+			sb->capacity += len + 1024;
+			sb->buffer = (char*)realloc(sb->buffer,sb->capacity);
+		}
+		ns_bcopy(str,sb->buffer+sb->sz,len+1);
+		sb->sz += len;
+	}
+}
+
+void str_buffer_append_sz(struct str_buffer* sb, size_t sz)
+{
+	char ssz[129];
+	snprintf(ssz,sizeof(ssz)-1,"%lu",(unsigned long)sz);
+	str_buffer_append(sb,ssz);
+}
+
+void str_buffer_append_sid(struct str_buffer* sb, turnsession_id sid)
+{
+	char ssz[129];
+	snprintf(ssz,sizeof(ssz)-1,"%018llu",(unsigned long long)sid);
+	str_buffer_append(sb,ssz);
+}
+
+const char* str_buffer_get_str(const struct str_buffer *sb)
+{
+	if(sb) {
+		return sb->buffer;
+	}
+	return NULL;
+}
+
+size_t str_buffer_get_str_len(const struct str_buffer *sb)
+{
+	if(sb) {
+		return sb->sz;
+	}
+	return 0;
+}
+
+void str_buffer_free(struct str_buffer *sb)
+{
+	if(sb) {
+		free(sb->buffer);
+		free(sb);
+	}
+}
+
+///////////////////////////////////////////////

+ 96 - 0
src/apps/relay/http_server.h

@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011, 2012, 2013, 2014 Citrix Systems
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __TURN_HTTP_SERVER__
+#define __TURN_HTTP_SERVER__
+
+#include "ns_turn_utils.h"
+#include "ns_turn_server.h"
+#include "apputils.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/////////  HTTP REQUEST //////////
+
+enum _HTTP_REQUEST_TYPE {
+	HRT_UNKNOWN=0,
+	HRT_GET,
+	HRT_HEAD,
+	HRT_POST,
+	HRT_PUT,
+	HRT_DELETE
+};
+
+typedef enum _HTTP_REQUEST_TYPE HTTP_REQUEST_TYPE;
+
+struct http_headers;
+
+struct http_request {
+	HTTP_REQUEST_TYPE rtype;
+	char *path;
+	struct http_headers *headers;
+};
+
+struct http_request* parse_http_request(char* request);
+const char *get_http_header_value(const struct http_request *request, const char* key, const char* def);
+void free_http_request(struct http_request *request);
+
+const char* get_http_date_header(void);
+
+////////////////////////////////////////////
+
+struct str_buffer;
+
+struct str_buffer* str_buffer_new(void);
+void str_buffer_append(struct str_buffer* sb, const char* str);
+void str_buffer_append_sz(struct str_buffer* sb, size_t sz);
+void str_buffer_append_sid(struct str_buffer* sb, turnsession_id sid);
+const char* str_buffer_get_str(const struct str_buffer *sb);
+size_t str_buffer_get_str_len(const struct str_buffer *sb);
+void str_buffer_free(struct str_buffer *sb);
+
+////////////////////////////////////////////
+
+void handle_http_echo(ioa_socket_handle s);
+
+////////////////////////////////////////////
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/// __TURN_HTTP_SERVER__///
+

+ 32 - 59
src/apps/relay/mainrelay.c

@@ -33,7 +33,6 @@
 ////// TEMPORARY data //////////
 
 static int use_lt_credentials = 0;
-static int use_st_credentials = 0;
 static int anon_credentials = 0;
 
 ////// TURNDB //////////////
@@ -81,7 +80,7 @@ NULL,
 
 DH_1066, "", "", "",
 "turn_server_cert.pem","turn_server_pkey.pem", "", "",
-0,0,0,0,0,
+0,0,0,0,
 #if !TLS_SUPPORTED
 1,
 #else
@@ -120,7 +119,7 @@ LOW_DEFAULT_PORTS_BOUNDARY,HIGH_DEFAULT_PORTS_BOUNDARY,0,0,0,"",
 /////////////// MISC PARAMS ////////////////
 0,0,0,0,0,SHATYPE_SHA1,':',0,0,TURN_CREDENTIALS_NONE,0,0,0,0,0,0,
 ///////////// Users DB //////////////
-{ (TURN_USERDB_TYPE)0, {"\0"}, {0,NULL,NULL, {NULL,0}} },
+{ (TURN_USERDB_TYPE)0, {"\0"}, {0,NULL, {NULL,0}} },
 ///////////// CPUs //////////////////
 DEFAULT_CPUS_NUMBER
 };
@@ -414,10 +413,9 @@ static char Usage[] = "Usage: turnserver [options]\n"
 " -o, --daemon					Start process as daemon (detach from current shell).\n"
 " -f, --fingerprint				Use fingerprints in the TURN messages.\n"
 " -a, --lt-cred-mech				Use the long-term credential mechanism.\n"
-" -A, --st-cred-mech				Use the short-term credential mechanism.\n"
 " -z, --no-auth					Do not use any credential mechanism, allow anonymous access.\n"
 " -u, --user			<user:pwd>	User account, in form 'username:password', for long-term credentials.\n"
-"						Cannot be used with TURN REST API or with short-term credentials.\n"
+"						Cannot be used with TURN REST API.\n"
 " -r, --realm			<realm>		The default realm to be used for the users when no explicit\n"
 "						origin/realm relationship was found in the database.\n"
 "						Must be used with long-term credentials \n"
@@ -445,7 +443,7 @@ static char Usage[] = "Usage: turnserver [options]\n"
 #endif
 #if !defined(TURN_NO_PQ)
 " -e, --psql-userdb, --sql-userdb <conn-string>	PostgreSQL database connection string, if used (default - empty, no PostreSQL DB used).\n"
-"		                                This database can be used for long-term and short-term credentials mechanisms,\n"
+"		                                This database can be used for long-term credentials mechanism users,\n"
 "		                                and it can store the secret value(s) for secret-based timed authentication in TURN RESP API.\n"
 "						See http://www.postgresql.org/docs/8.4/static/libpq-connect.html for 8.x PostgreSQL\n"
 "						versions format, see \n"
@@ -454,7 +452,7 @@ static char Usage[] = "Usage: turnserver [options]\n"
 #endif
 #if !defined(TURN_NO_MYSQL)
 " -M, --mysql-userdb	<connection-string>	MySQL database connection string, if used (default - empty, no MySQL DB used).\n"
-"	                                	This database can be used for long-term and short-term credentials mechanisms,\n"
+"	                                	This database can be used for long-term credentials mechanism users,\n"
 "		                                and it can store the secret value(s) for secret-based timed authentication in TURN RESP API.\n"
 "						The connection string my be space-separated list of parameters:\n"
 "	        	          		\"host=<ip-addr> dbname=<database-name> user=<database-user> \\\n								password=<database-user-password> port=<db-port> connect_timeout=<seconds>\".\n\n"
@@ -466,12 +464,12 @@ static char Usage[] = "Usage: turnserver [options]\n"
 #endif
 #if !defined(TURN_NO_MONGO)
 " -J, --mongo-userdb	<connection-string>	MongoDB connection string, if used (default - empty, no MongoDB used).\n"
-"	                                	This database can be used for long-term and short-term credentials mechanisms,\n"
+"	                                	This database can be used for long-term credentials mechanism users,\n"
 "		                                and it can store the secret value(s) for secret-based timed authentication in TURN RESP API.\n"
 #endif
 #if !defined(TURN_NO_HIREDIS)
 " -N, --redis-userdb	<connection-string>	Redis user database connection string, if used (default - empty, no Redis DB used).\n"
-"	                                	This database can be used for long-term and short-term credentials mechanisms,\n"
+"	                                	This database can be used for long-term credentials mechanism users,\n"
 "		                                and it can store the secret value(s) for secret-based timed authentication in TURN RESP API.\n"
 "						The connection string my be space-separated list of parameters:\n"
 "	        	          		\"host=<ip-addr> dbname=<db-number> \\\n								password=<database-user-password> port=<db-port> connect_timeout=<seconds>\".\n\n"
@@ -518,7 +516,6 @@ static char Usage[] = "Usage: turnserver [options]\n"
 " --dh2066					Use 2066 bits predefined DH TLS key. Default size of the predefined key is 1066.\n"
 " --dh-file	<dh-file-name>			Use custom DH TLS key, stored in PEM format in the file.\n"
 "						Flags --dh566 and --dh2066 are ignored when the DH key is taken from a file.\n"
-" --no-sslv2					Do not allow SSLv2 protocol.\n"
 " --no-sslv3					Do not allow SSLv3 protocol.\n"
 " --no-tlsv1					Do not allow TLSv1/DTLSv1 protocol.\n"
 " --no-tlsv1_1					Do not allow TLSv1.1 protocol.\n"
@@ -602,11 +599,11 @@ static char AdminUsage[] = "Usage: turnadmin [command] [options]\n"
 	"\nCommands:\n\n"
 	"	-k, --key			generate long-term credential mechanism key for a user\n"
 	"	-a, --add			add/update a long-term mechanism user\n"
-	"	-A, --add-st			add/update a short-term mechanism user\n"
+	"	-A, --add-admin			add/update a web admin user\n"
 	"	-d, --delete			delete a long-term mechanism user\n"
-	"	-D, --delete-st			delete a short-term mechanism user\n"
+	"	-D, --delete-admin		delete an admin user\n"
 	"	-l, --list			list all long-term mechanism users\n"
-	"	-L, --list-st			list all short-term mechanism users\n"
+	"	-L, --list-admin		list all admin users\n"
 	"	-s, --set-secret=<value>	Add shared secret for TURN RESP API\n"
 	"	-S, --show-secret		Show stored shared secrets for TURN REST API\n"
 	"	-X, --delete-secret=<value>	Delete a shared secret\n"
@@ -634,7 +631,7 @@ static char AdminUsage[] = "Usage: turnadmin [command] [options]\n"
 	"	-N, --redis-userdb		Redis user database connection string, if Redis DB is used.\n"
 #endif
 	"	-u, --user			Username\n"
-	"	-r, --realm			Realm for long-term mechanism only\n"
+	"	-r, --realm			Realm\n"
 	"	-p, --password			Password\n"
 #if !defined(TURN_NO_SQLITE) || !defined(TURN_NO_PQ) || !defined(TURN_NO_MYSQL) || !defined(TURN_NO_MONGO) || !defined(TURN_NO_HIREDIS)
 	"	-o, --origin			Origin\n"
@@ -705,7 +702,7 @@ enum EXTRA_OPTS {
 	DH566_OPT,
 	DH2066_OPT,
 	NE_TYPE_OPT,
-	NO_SSLV2_OPT,
+	NO_SSLV2_OPT, /*deprecated*/
 	NO_SSLV3_OPT,
 	NO_TLSV1_OPT,
 	NO_TLSV1_1_OPT,
@@ -747,7 +744,6 @@ static const struct myoption long_options[] = {
 				{ "min-port", required_argument, NULL, MIN_PORT_OPT },
 				{ "max-port", required_argument, NULL, MAX_PORT_OPT },
 				{ "lt-cred-mech", optional_argument, NULL, 'a' },
-				{ "st-cred-mech", optional_argument, NULL, 'A' },
 				{ "no-auth", optional_argument, NULL, 'z' },
 				{ "user", required_argument, NULL, 'u' },
 #if !defined(TURN_NO_SQLITE)
@@ -828,7 +824,7 @@ static const struct myoption long_options[] = {
 				{ "dh566", optional_argument, NULL, DH566_OPT },
 				{ "dh2066", optional_argument, NULL, DH2066_OPT },
 				{ "ne", required_argument, NULL, NE_TYPE_OPT },
-				{ "no-sslv2", optional_argument, NULL, NO_SSLV2_OPT },
+				{ "no-sslv2", optional_argument, NULL, NO_SSLV2_OPT }, /* deprecated */
 				{ "no-sslv3", optional_argument, NULL, NO_SSLV3_OPT },
 				{ "no-tlsv1", optional_argument, NULL, NO_TLSV1_OPT },
 				{ "no-tlsv1_1", optional_argument, NULL, NO_TLSV1_1_OPT },
@@ -841,13 +837,13 @@ static const struct myoption admin_long_options[] = {
 				{ "add", no_argument, NULL, 'a' },
 				{ "delete", no_argument, NULL, 'd' },
 				{ "list", no_argument, NULL, 'l' },
-				{ "list-st", no_argument, NULL, 'L' },
+				{ "list-admin", no_argument, NULL, 'L' },
 				{ "set-secret", required_argument, NULL, 's' },
 				{ "show-secret", no_argument, NULL, 'S' },
 				{ "delete-secret", required_argument, NULL, 'X' },
 				{ "delete-all-secrets", no_argument, NULL, DEL_ALL_AUTH_SECRETS_OPT },
-				{ "add-st", no_argument, NULL, 'A' },
-				{ "delete-st", no_argument, NULL, 'D' },
+				{ "add-admin", no_argument, NULL, 'A' },
+				{ "delete-admin", no_argument, NULL, 'D' },
 #if !defined(TURN_NO_SQLITE)
 				{ "userdb", required_argument, NULL, 'b' },
 				{ "db", required_argument, NULL, 'b' },
@@ -908,7 +904,7 @@ static void set_option(int c, char *value)
 	  turn_params.oauth = get_bool_value(value);
 	  break;
   case NO_SSLV2_OPT:
-	  turn_params.no_sslv2 = get_bool_value(value);
+    //deprecated
 	  break;
   case NO_SSLV3_OPT:
 	  turn_params.no_sslv3 = get_bool_value(value);
@@ -1120,15 +1116,6 @@ static void set_option(int c, char *value)
 			use_lt_credentials=0;
 		}
 		break;
-	case 'A':
-		if (get_bool_value(value)) {
-			turn_params.ct = TURN_CREDENTIALS_SHORT_TERM;
-			use_st_credentials=1;
-		} else {
-			turn_params.ct = TURN_CREDENTIALS_UNDEFINED;
-			use_st_credentials=0;
-		}
-		break;
 	case 'z':
 		if (!get_bool_value(value)) {
 			turn_params.ct = TURN_CREDENTIALS_UNDEFINED;
@@ -1142,7 +1129,7 @@ static void set_option(int c, char *value)
 		turn_params.fingerprint = get_bool_value(value);
 		break;
 	case 'u':
-		add_user_account(value,0);
+		add_static_user_account(value);
 		break;
 #if !defined(TURN_NO_SQLITE)
 	case 'b':
@@ -1431,7 +1418,8 @@ static int adminmain(int argc, char **argv)
 	int c = 0;
 
 	TURNADMIN_COMMAND_TYPE ct = TA_COMMAND_UNKNOWN;
-	int is_st = 0;
+
+	int is_admin = 0;
 
 	u08bits user[STUN_MAX_USERNAME_SIZE+1]="";
 	u08bits realm[STUN_MAX_REALM_SIZE+1]="";
@@ -1483,18 +1471,18 @@ static int adminmain(int argc, char **argv)
 			break;
 		case 'A':
 			ct = TA_UPDATE_USER;
-			is_st = 1;
+			is_admin = 1;
 			break;
 		case 'D':
 			ct = TA_DELETE_USER;
-			is_st = 1;
+			is_admin = 1;
 			break;
 		case 'l':
 			ct = TA_LIST_USERS;
 			break;
 		case 'L':
 			ct = TA_LIST_USERS;
-			is_st = 1;
+			is_admin = 1;
 			break;
 		case 's':
 			ct = TA_SET_SECRET;
@@ -1597,7 +1585,7 @@ static int adminmain(int argc, char **argv)
 		exit(-1);
 	}
 
-	return adminuser(user, realm, pwd, secret, origin, ct, is_st, &po);
+	return adminuser(user, realm, pwd, secret, origin, ct, &po, is_admin);
 }
 
 static void print_features(unsigned long mfn)
@@ -1816,7 +1804,6 @@ int main(int argc, char **argv)
 
 	ns_bzero(&turn_params.default_users_db,sizeof(default_users_db_t));
 	turn_params.default_users_db.ram_db.static_accounts = ur_string_map_create(turn_free_simple);
-	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);
@@ -1896,17 +1883,7 @@ int main(int argc, char **argv)
 		exit(-1);
 	}
 
-	if(use_st_credentials && anon_credentials) {
-		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIG ERROR: -A and -z options cannot be used together.\n");
-		exit(-1);
-	}
-
-	if(use_lt_credentials && use_st_credentials) {
-		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIG ERROR: -a and -A options cannot be used together.\n");
-		exit(-1);
-	}
-
-	if(!use_lt_credentials && !anon_credentials && !use_st_credentials) {
+	if(!use_lt_credentials && !anon_credentials) {
 		if(turn_params.default_users_db.ram_db.users_number) {
 			TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "\nCONFIGURATION ALERT: you specified long-term user accounts, (-u option) \n	but you did not specify the long-term credentials option\n	(-a or --lt-cred-mech option).\n 	I am turning --lt-cred-mech ON for you, but double-check your configuration.\n");
 			turn_params.ct = TURN_CREDENTIALS_LONG_TERM;
@@ -1928,15 +1905,9 @@ int main(int argc, char **argv)
 			TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "\nCONFIGURATION ALERT: you specified user accounts, (-u option) \n	but you also specified the anonymous user access option (-z or --no-auth option).\n 	User accounts will be ignored.\n");
 			turn_params.ct = TURN_CREDENTIALS_NONE;
 			use_lt_credentials=0;
-			use_st_credentials=0;
 		}
 	}
 
-	if(turn_params.use_auth_secret_with_timestamp && use_st_credentials) {
-		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nCONFIGURATION ERROR: Authentication secret (REST API) cannot be used with short-term credentials mechanism.\n");
-		exit(-1);
-	}
-
 	openssl_setup();
 
 	int local_listeners = 0;
@@ -2532,9 +2503,9 @@ static void set_ctx(SSL_CTX* ctx, const char *protocol)
 		int op = 0;
 
 #if defined(SSL_OP_NO_SSLv2)
-		if(turn_params.no_sslv2)
-			op |= SSL_OP_NO_SSLv2;
+		op |= SSL_OP_NO_SSLv2;
 #endif
+
 		if(turn_params.no_sslv3)
 			op |= SSL_OP_NO_SSLv3;
 
@@ -2635,15 +2606,17 @@ static void openssl_setup(void)
 		if(OPENSSL_VERSION_NUMBER < 0x10000000L) {
 			TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: TURN Server was compiled with rather old OpenSSL version, DTLS may not be working correctly.\n");
 		}
-		turn_params.dtls_ctx = SSL_CTX_new(DTLSv1_server_method());
-		set_ctx(turn_params.dtls_ctx,"DTLS");
-		SSL_CTX_set_read_ahead(turn_params.dtls_ctx, 1);
 
 #if DTLSv1_2_SUPPORTED
+		turn_params.dtls_ctx = SSL_CTX_new(DTLS_server_method());
 		turn_params.dtls_ctx_v1_2 = SSL_CTX_new(DTLSv1_2_server_method());
 		set_ctx(turn_params.dtls_ctx_v1_2,"DTLS1,2");
 		SSL_CTX_set_read_ahead(turn_params.dtls_ctx_v1_2, 1);
+#else
+		turn_params.dtls_ctx = SSL_CTX_new(DTLSv1_server_method());
 #endif
+		set_ctx(turn_params.dtls_ctx,"DTLS");
+		SSL_CTX_set_read_ahead(turn_params.dtls_ctx, 1);
 
 		TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "DTLS cipher suite: %s\n",turn_params.cipher_list);
 

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

@@ -65,12 +65,14 @@
 #include <openssl/rand.h>
 #include <openssl/crypto.h>
 #include <openssl/opensslv.h>
+#include <openssl/dh.h>
+#include <openssl/bn.h>
 
 #include "ns_turn_utils.h"
 #include "ns_turn_khash.h"
 
 #include "userdb.h"
-#include "turncli.h"
+#include "turn_admin_server.h"
 
 #include "tls_listener.h"
 #include "dtls_listener.h"
@@ -197,7 +199,6 @@ typedef struct _turn_params_ {
   char tls_password[513];
   char dh_file[1025];
   
-  int no_sslv2;
   int no_sslv3;
   int no_tlsv1;
   int no_tlsv1_1;

+ 23 - 30
src/apps/relay/netengine.c

@@ -402,17 +402,7 @@ static void auth_server_receive_message(struct bufferevent *bev, void *ptr)
       continue;
     }
     
-    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 {
-    	  ns_bcopy(pwd,am.pwd,sizeof(st_password_t));
-    	  am.success = 1;
-      }
-    } else {
+    {
       hmackey_t key;
       if(get_user_key(am.in_oauth,&(am.out_oauth),&(am.max_session_time),am.username,am.realm,key,am.in_buffer.nbh)<0) {
     	  am.success = 0;
@@ -752,7 +742,10 @@ static int handle_relay_message(relay_server_handle rs, struct message_to_relay
 				sm->m.sm.s = NULL;
 			} else {
 				s->e = rs->ioa_eng;
-				open_client_connection_session(&(rs->server), &(sm->m.sm));
+				if(open_client_connection_session(&(rs->server), &(sm->m.sm))<0) {
+					IOA_CLOSE_SOCKET(s);
+					sm->m.sm.s = NULL;
+				}
 			}
 
 			ioa_network_buffer_delete(rs->ioa_eng, sm->m.sm.nd.nbh);
@@ -790,7 +783,10 @@ static int handle_relay_message(relay_server_handle rs, struct message_to_relay
 				sm->m.sm.s = NULL;
 			} else {
 				s->e = rs->ioa_eng;
-				open_client_connection_session(&(rs->server), &(sm->m.sm));
+				if(open_client_connection_session(&(rs->server), &(sm->m.sm))<0) {
+					IOA_CLOSE_SOCKET(s);
+					sm->m.sm.s = NULL;
+				}
 			}
 
 			ioa_network_buffer_delete(rs->ioa_eng, sm->m.sm.nd.nbh);
@@ -1649,6 +1645,7 @@ 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,
+			 send_https_socket,
 			 allocate_bps,
 			 turn_params.oauth, turn_params.oauth_server_name);
 	
@@ -1776,33 +1773,33 @@ static void setup_auth_server(struct auth_server *as)
 	pthread_detach(as->thr);
 }
 
-static void* run_cli_server_thread(void *arg)
+static void* run_admin_server_thread(void *arg)
 {
 	ignore_sigpipe();
 
-	setup_cli_thread();
+	setup_admin_thread();
 
 	barrier_wait();
 
-	while(cliserver.event_base) {
-		run_events(cliserver.event_base,NULL);
+	while(adminserver.event_base) {
+		run_events(adminserver.event_base,NULL);
 	}
 
 	return arg;
 }
 
-static void setup_cli_server(void)
+static void setup_admin_server(void)
 {
-	ns_bzero(&cliserver,sizeof(struct cli_server));
-	cliserver.listen_fd = -1;
-	cliserver.verbose = turn_params.verbose;
+	ns_bzero(&adminserver,sizeof(struct admin_server));
+	adminserver.listen_fd = -1;
+	adminserver.verbose = turn_params.verbose;
 
-	if(pthread_create(&(cliserver.thr), NULL, run_cli_server_thread, &cliserver)<0) {
+	if(pthread_create(&(adminserver.thr), NULL, run_admin_server_thread, &adminserver)<0) {
 		perror("Cannot create cli thread\n");
 		exit(-1);
 	}
 
-	pthread_detach(cliserver.thr);
+	pthread_detach(adminserver.thr);
 }
 
 void setup_server(void)
@@ -1818,12 +1815,9 @@ void setup_server(void)
 #if !defined(TURN_NO_THREAD_BARRIERS)
 
 	/* relay threads plus auth threads plus main listener thread */
+	/* plus admin thread */
 	/* udp address listener thread(s) will start later */
-	barrier_count = turn_params.general_relay_servers_number+authserver_number+1;
-
-	if(use_cli) {
-		barrier_count += 1;
-	}
+	barrier_count = turn_params.general_relay_servers_number+authserver_number+1+1;
 
 #endif
 
@@ -1870,8 +1864,7 @@ void setup_server(void)
 		}
 	}
 
-	if(use_cli)
-		setup_cli_server();
+	setup_admin_server();
 
 	barrier_wait();
 }

+ 113 - 12
src/apps/relay/ns_ioalib_engine_impl.c

@@ -1633,6 +1633,7 @@ void detach_socket_net_data(ioa_socket_handle s)
 void close_ioa_socket(ioa_socket_handle s)
 {
 	if (s) {
+
 		if(s->magic != SOCKET_MAGIC) {
 			TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s wrong magic on socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, s->st, s->sat);
 			return;
@@ -1658,6 +1659,12 @@ void close_ioa_socket(ioa_socket_handle s)
 					&(s->local_addr));
 		}
 
+		if(s->special_session) {
+			turn_free(s->special_session,s->special_session_size);
+			s->special_session = NULL;
+		}
+		s->special_session_size = 0;
+
 		delete_socket_from_map(s);
 		delete_socket_from_parent(s);
 
@@ -2263,13 +2270,11 @@ static TURN_TLS_TYPE check_tentative_tls(ioa_socket_raw fd)
 		if((s[0]==22)&&(s[1]==3)&&(s[5]==1)&&(s[9]==3)) {
 			char max_supported = (char)(TURN_TLS_TOTAL-2);
 			if(s[10] >= max_supported)
-				ret = (TURN_TLS_TYPE)((((int)TURN_TLS_TOTAL)-1));
+				ret = TURN_TLS_SSL23; /* compatibility mode */
 			else
 				ret = (TURN_TLS_TYPE)(s[10]+1);
 		} else if((s[2]==1)&&(s[3]==3)) {
 			ret = TURN_TLS_SSL23; /* compatibility mode */
-		} else if((s[2]==1)&&(s[3]==0)&&(s[4]==2)) {
-			ret = TURN_TLS_SSL23; /* old mode */
 		}
 	}
 
@@ -2403,11 +2408,11 @@ static int socket_input_worker(ioa_socket_handle s)
 				TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!!%s on socket: 0x%lx, st=%d, sat=%d: bev already exist\n", __FUNCTION__,(long)s, s->st, s->sat);
 			}
 			s->bev = bufferevent_socket_new(s->e->event_base,
-							s->fd,
-							TURN_BUFFEREVENTS_OPTIONS);
+						s->fd,
+						TURN_BUFFEREVENTS_OPTIONS);
 			debug_ptr_add(s->bev);
 			bufferevent_setcb(s->bev, socket_input_handler_bev, socket_output_handler_bev,
-					eventcb_bev, s);
+				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. */
 		}
@@ -2599,6 +2604,19 @@ static void socket_input_handler(evutil_socket_t fd, short what, void* arg)
 void close_ioa_socket_after_processing_if_necessary(ioa_socket_handle s)
 {
 	if (s && ioa_socket_tobeclosed(s)) {
+
+		if(s->special_session) {
+			turn_free(s->special_session,s->special_session_size);
+			s->special_session = NULL;
+		}
+		s->special_session_size = 0;
+
+		if(!(s->session) && !(s->sub_session)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s https server socket closed: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)s, get_ioa_socket_type(s), get_ioa_socket_app_type(s));
+			IOA_CLOSE_SOCKET(s);
+			return;
+		}
+
 		switch (s->sat){
 		case TCP_CLIENT_DATA_SOCKET:
 		case TCP_RELAY_DATA_SOCKET:
@@ -2606,6 +2624,7 @@ void close_ioa_socket_after_processing_if_necessary(ioa_socket_handle s)
 			tcp_connection *tc = s->sub_session;
 			if (tc) {
 				delete_tcp_connection(tc);
+				s->sub_session = NULL;
 			}
 		}
 			break;
@@ -2772,6 +2791,20 @@ static void eventcb_bev(struct bufferevent *bev, short events, void *arg)
 
 			s->tobeclosed = 1;
 
+			if(s->special_session) {
+				turn_free(s->special_session,s->special_session_size);
+				s->special_session = NULL;
+			}
+			s->special_session_size = 0;
+
+			if(!(s->session) && !(s->sub_session)) {
+				char sraddr[129]="\0";
+				addr_to_string(&(s->remote_addr),(u08bits*)sraddr);
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s https server socket closed: 0x%lx, st=%d, sat=%d, remote addr=%s\n", __FUNCTION__,(long)s, get_ioa_socket_type(s), get_ioa_socket_app_type(s),sraddr);
+				IOA_CLOSE_SOCKET(s);
+				return;
+			}
+
 			switch (s->sat){
 			case TCP_CLIENT_DATA_SOCKET:
 			case TCP_RELAY_DATA_SOCKET:
@@ -2779,6 +2812,7 @@ static void eventcb_bev(struct bufferevent *bev, short events, void *arg)
 				tcp_connection *tc = s->sub_session;
 				if (tc) {
 					delete_tcp_connection(tc);
+					s->sub_session = NULL;
 				}
 			}
 				break;
@@ -3176,6 +3210,68 @@ int send_data_from_ioa_socket_nbh(ioa_socket_handle s, ioa_addr* dest_addr,
 	return ret;
 }
 
+int send_data_from_ioa_socket_tcp(ioa_socket_handle s, const void *data, size_t sz)
+{
+	int ret = -1;
+
+	if(s && data) {
+
+		if (s->done || (s->fd == -1) || ioa_socket_tobeclosed(s) || !(s->e)) {
+			TURN_LOG_FUNC(
+				TURN_LOG_LEVEL_INFO,
+				"!!! %s: (1) Trying to send data from bad socket: 0x%lx (1): done=%d, fd=%d, st=%d, sat=%d\n",
+				__FUNCTION__, (long) s, (int) s->done,
+				(int) s->fd, s->st, s->sat);
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "!!! %s socket: 0x%lx was closed\n", __FUNCTION__,(long)s);
+
+		} else if (s->connected && s->bev) {
+			if (s->st == TLS_SOCKET) {
+#if TLS_SUPPORTED
+				SSL *ctx = bufferevent_openssl_get_ssl(s->bev);
+				if (!ctx || SSL_get_shutdown(ctx)) {
+					s->tobeclosed = 1;
+					ret = 0;
+				}
+#endif
+			}
+
+			if (!(s->tobeclosed)) {
+
+				ret = (int)sz;
+
+				s->in_write = 1;
+				if (bufferevent_write(s->bev, data, sz) < 0) {
+					ret = -1;
+					perror("bufev send");
+					log_socket_event(s, "socket write failed, to be closed", 1);
+					s->tobeclosed = 1;
+					s->broken = 1;
+				}
+				s->in_write = 0;
+			}
+		}
+	}
+
+	return ret;
+}
+
+int send_str_from_ioa_socket_tcp(ioa_socket_handle s, const void *data)
+{
+	if(data) {
+		return send_data_from_ioa_socket_tcp(s, data, strlen((const char*)data));
+	} else {
+		return 0;
+	}
+}
+
+int send_ulong_from_ioa_socket_tcp(ioa_socket_handle s, size_t data)
+{
+	char str[129];
+	snprintf(str,sizeof(str)-1,"%lu",(unsigned long)data);
+
+	return send_str_from_ioa_socket_tcp(s,str);
+}
+
 int register_callback_on_ioa_socket(ioa_engine_handle e, ioa_socket_handle s, int event_type, ioa_net_event_handler cb, void* ctx, int clean_preexisting)
 {
 	if(s) {
@@ -3227,14 +3323,19 @@ int register_callback_on_ioa_socket(ioa_engine_handle e, ioa_socket_handle s, in
 							return -1;
 						}
 					} else {
-						s->bev = bufferevent_socket_new(s->e->event_base,
+						if(check_tentative_tls(s->fd)) {
+							s->tobeclosed = 1;
+							return -1;
+						} else {
+							s->bev = bufferevent_socket_new(s->e->event_base,
 										s->fd,
 										TURN_BUFFEREVENTS_OPTIONS);
-						debug_ptr_add(s->bev);
-						bufferevent_setcb(s->bev, socket_input_handler_bev, socket_output_handler_bev,
-							eventcb_bev, s);
-						bufferevent_setwatermark(s->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK);
-						bufferevent_enable(s->bev, EV_READ|EV_WRITE); /* Start reading. */
+							debug_ptr_add(s->bev);
+							bufferevent_setcb(s->bev, socket_input_handler_bev, socket_output_handler_bev,
+											eventcb_bev, s);
+							bufferevent_setwatermark(s->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK);
+							bufferevent_enable(s->bev, EV_READ|EV_WRITE); /* Start reading. */
+						}
 					}
 					break;
 				case TLS_SOCKET:

+ 6 - 0
src/apps/relay/ns_ioalib_impl.h

@@ -223,6 +223,8 @@ struct _ioa_socket
 	accept_cb acb;
 	void *acbarg;
 	/* <<== RFC 6062 */
+	void *special_session;
+	size_t special_session_size;
 };
 
 typedef struct _timer_event
@@ -297,6 +299,10 @@ int set_socket_options(ioa_socket_handle s);
 
 int send_session_cancellation_to_relay(turnsession_id sid);
 
+int send_data_from_ioa_socket_tcp(ioa_socket_handle s, const void *data, size_t sz);
+int send_str_from_ioa_socket_tcp(ioa_socket_handle s, const void *data);
+int send_ulong_from_ioa_socket_tcp(ioa_socket_handle s, size_t data);
+
 ///////////////////////// SUPER MEMORY ////////
 
 #define allocate_super_memory_engine(e,size) allocate_super_memory_engine_func(e, size, __FILE__, __FUNCTION__, __LINE__)

+ 3832 - 0
src/apps/relay/turn_admin_server.c

@@ -0,0 +1,3832 @@
+/*
+ * Copyright (C) 2011, 2012, 2013 Citrix Systems
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <limits.h>
+#include <ifaddrs.h>
+#include <getopt.h>
+#include <locale.h>
+#include <libgen.h>
+
+#include <pthread.h>
+
+#include <signal.h>
+
+#include "libtelnet.h"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+
+#include <event2/bufferevent.h>
+#include <event2/buffer.h>
+#include <event2/listener.h>
+#include <event2/http.h>
+
+#include "userdb.h"
+#include "mainrelay.h"
+
+#include "ns_turn_utils.h"
+
+#include "ns_turn_server.h"
+#include "ns_turn_maps.h"
+
+#include "apputils.h"
+
+#include "turn_admin_server.h"
+
+#include "http_server.h"
+
+#include "dbdrivers/dbdriver.h"
+
+///////////////////////////////
+
+struct admin_server adminserver;
+
+int use_cli = 1;
+
+ioa_addr cli_addr;
+int cli_addr_set = 0;
+
+int cli_port = CLI_DEFAULT_PORT;
+
+char cli_password[CLI_PASSWORD_LENGTH] = "";
+
+int cli_max_output_sessions = DEFAULT_CLI_MAX_OUTPUT_SESSIONS;
+
+///////////////////////////////
+
+struct cli_session {
+	evutil_socket_t fd;
+	int auth_completed;
+	size_t cmds;
+	struct bufferevent *bev;
+	ioa_addr addr;
+	telnet_t *ts;
+	FILE* f;
+	char realm[STUN_MAX_REALM_SIZE+1];
+	char origin[STUN_MAX_ORIGIN_SIZE+1];
+	realm_params_t *rp;
+};
+
+///////////////////////////////
+
+#define CLI_PASSWORD_TRY_NUMBER (5)
+
+static const char *CLI_HELP_STR[] = 
+  {"",
+   "  ?, h, help - print this text",
+   "",
+   "  quit, q, exit, bye - end CLI session",
+   "",
+   "  stop, shutdown, halt - shutdown TURN Server",
+   "",
+   "  pc - print configuration",
+   "",
+   "  sr <realm> - set CLI session realm",
+   "",
+   "  ur - unset CLI session realm",
+   "",
+   "  so <origin> - set CLI session origin",
+   "",
+   "  uo - unset CLI session origin",
+   "",
+   "  tc <param-name> - toggle a configuration parameter",
+   "     (see pc command output for togglable param names)",
+   "",
+   "  cc <param-name> <param-value> - change a configuration parameter",
+   "     (see pc command output for changeable param names)",
+   "",
+   "  ps [username] - print sessions, with optional exact user match",
+   "",
+   "  psp <usernamestr> - print sessions, with partial user string match",
+   "",
+   "  psd <file-name> - dump ps command output into file on the TURN server system",
+   "",
+   "  pu [udp|tcp|dtls|tls]- print current users",
+   "",
+   "  lr - log reset",
+   "",
+   "  aas ip[:port} - add an alternate server reference",
+   "  das ip[:port] - delete an alternate server reference",
+   "  atas ip[:port] - add a TLS alternate server reference",
+   "  dtas ip[:port] - delete a TLS alternate server reference",
+   "",
+   "  cs <session-id> - cancel session, forcefully"
+   "",
+   NULL};
+
+static const char *CLI_GREETING_STR[] = {
+  "TURN Server",
+  TURN_SOFTWARE,
+  NULL};
+
+static char CLI_CURSOR[] = "> ";
+
+static const telnet_telopt_t cli_telopts[] = {
+    { TELNET_TELOPT_ECHO,      TELNET_WONT, TELNET_DONT },
+    { TELNET_TELOPT_TTYPE,     TELNET_WONT, TELNET_DONT },
+    { TELNET_TELOPT_COMPRESS2, TELNET_WONT, TELNET_DONT },
+    { TELNET_TELOPT_ZMP,       TELNET_WONT, TELNET_DONT },
+    { TELNET_TELOPT_MSSP,      TELNET_WONT, TELNET_DONT },
+    { TELNET_TELOPT_BINARY,    TELNET_WONT, TELNET_DONT },
+    { TELNET_TELOPT_NAWS,      TELNET_WONT, TELNET_DONT },
+    { -1, 0, 0 }
+  };
+
+struct toggleable_command {
+	const char *cmd;
+	vintp data;
+};
+
+struct toggleable_command tcmds[] = {
+				{"stale-nonce",&turn_params.stale_nonce},
+				{"stun-only",&turn_params.stun_only},
+				{"no-stun",&turn_params.no_stun},
+				{"secure-stun",&turn_params.secure_stun},
+				{"no-udp-relay",&turn_params.no_udp_relay},
+				{"no-tcp-relay",&turn_params.no_tcp_relay},
+				{"no-multicast-peers",&turn_params.no_multicast_peers},
+				{"no-loopback-peers",&turn_params.no_loopback_peers},
+				{"mobility",&turn_params.mobility},
+				{NULL,NULL}
+};
+
+///////////////////////////////
+
+static void myprintf(struct cli_session *cs, const char *format, ...)
+{
+	if(cs && format) {
+		va_list args;
+		va_start (args, format);
+		if(cs->f) {
+			vfprintf(cs->f, format, args);
+		} else {
+			telnet_vprintf(cs->ts, format, args);
+		}
+		va_end (args);
+	}
+}
+
+static void log_reset(struct cli_session* cs)
+{
+	if(cs) {
+	  reset_rtpprintf();
+	  myprintf(cs,"  log reset done\n");
+	}
+}
+
+static void print_str_array(struct cli_session* cs, const char** sa)
+{
+  if(cs && sa) {
+    int i=0;
+    while(sa[i]) {
+      myprintf(cs,"%s\n",sa[i]);
+      i++;
+    }
+  }
+}
+
+static const char* get_flag(int val)
+{
+	if(val)
+		return "ON";
+	return "OFF";
+}
+
+static void cli_print_flag(struct cli_session* cs, int flag, const char* name, int changeable)
+{
+	if(cs && cs->ts && name) {
+		const char *sc="";
+		if(changeable)
+			sc=" (*)";
+		myprintf(cs,"  %s: %s%s\n",name,get_flag(flag),sc);
+	}
+}
+
+static void cli_print_uint(struct cli_session* cs, unsigned long value, const char* name, int changeable)
+{
+	if(cs && cs->ts && name) {
+		const char *sc="";
+		if(changeable==1)
+			sc=" (*)";
+		else if(changeable==2)
+			sc=" (**)";
+		myprintf(cs,"  %s: %lu%s\n",name,value,sc);
+	}
+}
+
+static void cli_print_str(struct cli_session* cs, const char *value, const char* name, int changeable)
+{
+	if(cs && cs->ts && name && value) {
+		if(value[0] == 0)
+			value="empty";
+		const char *sc="";
+		if(changeable==1)
+			sc=" (*)";
+		else if(changeable==2)
+			sc=" (**)";
+		myprintf(cs,"  %s: %s%s\n",name,value,sc);
+	}
+}
+
+static void cli_print_addr(struct cli_session* cs, ioa_addr *value, int use_port, const char* name, int changeable)
+{
+	if(cs && cs->ts && name && value) {
+		const char *sc="";
+		if(changeable==1)
+			sc=" (*)";
+		else if(changeable==2)
+			sc=" (**)";
+		char s[256];
+		if(!use_port)
+			addr_to_string_no_port(value,(u08bits*)s);
+		else
+			addr_to_string(value,(u08bits*)s);
+		myprintf(cs,"  %s: %s%s\n",name,s,sc);
+	}
+}
+
+static void cli_print_addr_list(struct cli_session* cs, turn_server_addrs_list_t *value, int use_port, const char* name, int changeable)
+{
+	if(cs && cs->ts && name && value && value->size && value->addrs) {
+		const char *sc="";
+		if(changeable==1)
+			sc=" (*)";
+		else if(changeable==2)
+			sc=" (**)";
+		char s[256];
+		size_t i;
+		for(i=0;i<value->size;i++) {
+			if(!use_port)
+				addr_to_string_no_port(&(value->addrs[i]),(u08bits*)s);
+			else
+				addr_to_string(&(value->addrs[i]),(u08bits*)s);
+			myprintf(cs,"  %s: %s%s\n",name,s,sc);
+		}
+	}
+}
+
+static void cli_print_str_array(struct cli_session* cs, char **value, size_t sz, const char* name, int changeable)
+{
+	if(cs && cs->ts && name && value && sz) {
+		const char *sc="";
+		if(changeable==1)
+			sc=" (*)";
+		else if(changeable==2)
+			sc=" (**)";
+		size_t i;
+		for(i=0;i<sz;i++) {
+			if(value[i])
+				myprintf(cs,"  %s: %s%s\n",name,value[i],sc);
+		}
+	}
+}
+
+static void cli_print_ip_range_list(struct cli_session* cs, ip_range_list_t *value, const char* name, int changeable)
+{
+	if(cs && cs->ts && name && value && value->ranges_number && value->rs) {
+		const char *sc="";
+		if(changeable==1)
+			sc=" (*)";
+		else if(changeable==2)
+			sc=" (**)";
+		size_t i;
+		for(i=0;i<value->ranges_number;++i) {
+			if(value->rs[i].realm[0]) {
+				if(cs->realm[0] && strcmp(cs->realm,value->rs[i].realm)) {
+					continue;
+				} else {
+					myprintf(cs,"  %s: %s (%s)%s\n",name,value->rs[i].str,value->rs[i].realm,sc);
+				}
+			} else {
+				myprintf(cs,"  %s: %s%s\n",name,value->rs[i].str,sc);
+			}
+		}
+	}
+}
+
+static void toggle_cli_param(struct cli_session* cs, const char* pn)
+{
+	if(cs && cs->ts && pn) {
+
+		int i=0;
+
+		while(tcmds[i].cmd && tcmds[i].data) {
+			if(strcmp(tcmds[i].cmd,pn) == 0) {
+				*(tcmds[i].data) = !(*(tcmds[i].data));
+				cli_print_flag(cs,*(tcmds[i].data),tcmds[i].cmd,0);
+				return;
+			}
+			++i;
+		}
+
+		myprintf(cs, "\n");
+		myprintf(cs, "  Error: unknown or constant parameter: %s.\n",pn);
+		myprintf(cs, "  You can toggle only the following parameters:\n");
+		myprintf(cs, "\n");
+
+		i=0;
+
+		while(tcmds[i].cmd && tcmds[i].data) {
+			cli_print_flag(cs,*(tcmds[i].data),tcmds[i].cmd,0);
+			++i;
+		}
+
+		myprintf(cs,"\n");
+	}
+}
+
+static void change_cli_param(struct cli_session* cs, const char* pn)
+{
+	if(cs && cs->ts && pn) {
+
+		if(strstr(pn,"total-quota")==pn) {
+			turn_params.total_quota = atoi(pn+strlen("total-quota"));
+			cli_print_uint(cs,(unsigned long)turn_params.total_quota,"total-quota",2);
+			return;
+		} else if(strstr(pn,"user-quota")==pn) {
+			turn_params.user_quota = atoi(pn+strlen("user-quota"));
+			cli_print_uint(cs,(unsigned long)turn_params.user_quota,"user-quota",2);
+			return;
+		} else if(strstr(pn,"max-bps")==pn) {
+			set_max_bps((band_limit_t)atol(pn+strlen("max-bps")));
+			cli_print_uint(cs,(unsigned long)get_max_bps(),"max-bps",2);
+			return;
+		} else if(strstr(pn,"bps-capacity")==pn) {
+			set_bps_capacity((band_limit_t)atol(pn+strlen("bps-capacity")));
+			cli_print_uint(cs,(unsigned long)get_bps_capacity(),"bps-capacity",2);
+			return;
+		} else if(strstr(pn,"cli-max-output-sessions")==pn) {
+			cli_max_output_sessions = atoi(pn+strlen("cli-max-output-sessions"));
+			cli_print_uint(cs,(unsigned long)cli_max_output_sessions,"cli-max-output-sessions",2);
+			return;
+		}
+
+		myprintf(cs, "\n");
+		myprintf(cs, "  Error: unknown or constant parameter: %s.\n",pn);
+		myprintf(cs, "\n");
+	}
+}
+
+struct ps_arg {
+	struct cli_session* cs;
+	size_t counter;
+	turn_time_t ct;
+	const char *username;
+	const char *pname;
+	int exact_match;
+	ur_string_map* users;
+	size_t *user_counters;
+	char **user_names;
+	size_t users_number;
+};
+
+static const char* pname(SOCKET_TYPE st)
+{
+	switch(st) {
+	case TCP_SOCKET:
+		return "TCP";
+	case UDP_SOCKET:
+		return "UDP";
+	case TLS_SOCKET:
+		return "TLS";
+	case DTLS_SOCKET:
+		return "DTLS";
+	case TENTATIVE_TCP_SOCKET:
+		return "TCP/TLS";
+	default:
+		;
+	};
+	return "UNKNOWN";
+}
+
+static int print_session(ur_map_key_type key, ur_map_value_type value, void *arg)
+{
+	if(key && value && arg) {
+		struct ps_arg *csarg = (struct ps_arg*)arg;
+		struct cli_session* cs = csarg->cs;
+		struct turn_session_info *tsi = (struct turn_session_info *)value;
+
+		if(cs->realm[0] && strcmp(cs->realm,tsi->realm))
+			return 0;
+
+		if(cs->origin[0] && strcmp(cs->origin,tsi->origin))
+					return 0;
+
+		if(csarg->users) {
+
+			const char *pn=csarg->pname;
+			if(pn[0]) {
+				if(!strcmp(pn,"TLS") || !strcmp(pn,"tls") || !strcmp(pn,"Tls")) {
+					if(tsi->client_protocol != TLS_SOCKET)
+						return 0;
+				} else if(!strcmp(pn,"DTLS") || !strcmp(pn,"dtls") || !strcmp(pn,"Dtls")) {
+					if(tsi->client_protocol != DTLS_SOCKET)
+						return 0;
+				} else if(!strcmp(pn,"TCP") || !strcmp(pn,"tcp") || !strcmp(pn,"Tcp")) {
+					if(tsi->client_protocol != TCP_SOCKET)
+						return 0;
+				} else if(!strcmp(pn,"UDP") || !strcmp(pn,"udp") || !strcmp(pn,"Udp")) {
+					if(tsi->client_protocol != UDP_SOCKET)
+						return 0;
+				} else {
+					return 0;
+				}
+			}
+
+			ur_string_map_value_type value;
+			if(!ur_string_map_get(csarg->users, (ur_string_map_key_type)(char*)tsi->username, &value)) {
+				value = (ur_string_map_value_type)csarg->users_number;
+				csarg->users_number += 1;
+				csarg->user_counters = (size_t*)turn_realloc(csarg->user_counters,
+						(size_t)value * sizeof(size_t),
+						csarg->users_number * sizeof(size_t));
+				csarg->user_names = (char**)turn_realloc(csarg->user_names,
+						(size_t)value * sizeof(char*),
+						csarg->users_number * sizeof(char*));
+				csarg->user_names[(size_t)value] = turn_strdup((char*)tsi->username);
+				csarg->user_counters[(size_t)value] = 0;
+				ur_string_map_put(csarg->users, (ur_string_map_key_type)(char*)tsi->username, value);
+			}
+			csarg->user_counters[(size_t)value] += 1;
+		} else {
+			if(csarg->username[0]) {
+				if(csarg->exact_match) {
+					if(strcmp((char*)tsi->username, csarg->username))
+						return 0;
+				} else {
+					if(!strstr((char*)tsi->username, csarg->username))
+						return 0;
+				}
+			}
+			if(cs->f || (unsigned long)csarg->counter<(unsigned long)cli_max_output_sessions) {
+				myprintf(cs, "\n");
+				myprintf(cs,"    %lu) id=%018llu, user <%s>:\n",
+								(unsigned long)(csarg->counter+1),
+								(unsigned long long)tsi->id,
+								tsi->username);
+				if(tsi->realm[0])
+					myprintf(cs,"      realm: %s\n",tsi->realm);
+				if(tsi->origin[0])
+					myprintf(cs,"      origin: %s\n",tsi->origin);
+				if(turn_time_before(csarg->ct, tsi->start_time)) {
+					myprintf(cs,"      started: undefined time\n");
+				} else {
+					myprintf(cs,"      started %lu secs ago\n",(unsigned long)(csarg->ct - tsi->start_time));
+				}
+				if(turn_time_before(tsi->expiration_time,csarg->ct)) {
+					myprintf(cs,"      expired\n");
+				} else {
+					myprintf(cs,"      expiring in %lu secs\n",(unsigned long)(tsi->expiration_time - csarg->ct));
+				}
+				myprintf(cs,"      client protocol %s, relay protocol %s\n",pname(tsi->client_protocol),pname(tsi->peer_protocol));
+				{
+					if(!tsi->local_addr_data.saddr[0])
+						addr_to_string(&(tsi->local_addr_data.addr),(u08bits*)tsi->local_addr_data.saddr);
+					if(!tsi->remote_addr_data.saddr[0])
+						addr_to_string(&(tsi->remote_addr_data.addr),(u08bits*)tsi->remote_addr_data.saddr);
+					if(!tsi->relay_addr_data_ipv4.saddr[0])
+						addr_to_string(&(tsi->relay_addr_data_ipv4.addr),(u08bits*)tsi->relay_addr_data_ipv4.saddr);
+					if(!tsi->relay_addr_data_ipv6.saddr[0])
+						addr_to_string(&(tsi->relay_addr_data_ipv6.addr),(u08bits*)tsi->relay_addr_data_ipv6.saddr);
+					myprintf(cs,"      client addr %s, server addr %s\n",
+									tsi->remote_addr_data.saddr,
+									tsi->local_addr_data.saddr);
+					if(tsi->relay_addr_data_ipv4.saddr[0]) {
+						myprintf(cs,"      relay addr %s\n", tsi->relay_addr_data_ipv4.saddr);
+					}
+					if(tsi->relay_addr_data_ipv6.saddr[0]) {
+						myprintf(cs,"      relay addr %s\n", tsi->relay_addr_data_ipv6.saddr);
+					}
+				}
+				myprintf(cs,"      fingerprints enforced: %s\n",get_flag(tsi->enforce_fingerprints));
+				myprintf(cs,"      mobile: %s\n",get_flag(tsi->is_mobile));
+				if(tsi->tls_method[0]) {
+					myprintf(cs,"      TLS method: %s\n",tsi->tls_method);
+					myprintf(cs,"      TLS cipher: %s\n",tsi->tls_cipher);
+				}
+				if(tsi->bps)
+					myprintf(cs,"      Max throughput: %lu bytes per second\n",(unsigned long)tsi->bps);
+				myprintf(cs,"      usage: rp=%lu, rb=%lu, sp=%lu, sb=%lu\n",(unsigned long)(tsi->received_packets), (unsigned long)(tsi->received_bytes),(unsigned long)(tsi->sent_packets),(unsigned long)(tsi->sent_bytes));
+				myprintf(cs,"       rate: r=%lu, s=%lu, total=%lu (bytes per sec)\n",(unsigned long)(tsi->received_rate), (unsigned long)(tsi->sent_rate),(unsigned long)(tsi->total_rate));
+				if(tsi->main_peers_size) {
+					myprintf(cs,"      peers:\n");
+					size_t i;
+					for(i=0;i<tsi->main_peers_size;++i) {
+						if(!(tsi->main_peers_data[i].saddr[0]))
+							addr_to_string(&(tsi->main_peers_data[i].addr),(u08bits*)tsi->main_peers_data[i].saddr);
+						myprintf(cs,"          %s\n",tsi->main_peers_data[i].saddr);
+					}
+					if(tsi->extra_peers_size && tsi->extra_peers_data) {
+						for(i=0;i<tsi->extra_peers_size;++i) {
+							if(!(tsi->extra_peers_data[i].saddr[0]))
+								addr_to_string(&(tsi->extra_peers_data[i].addr),(u08bits*)tsi->extra_peers_data[i].saddr);
+							myprintf(cs,"          %s\n",tsi->extra_peers_data[i].saddr);
+						}
+					}
+				}
+			}
+		}
+
+		csarg->counter += 1;
+	}
+	return 0;
+}
+
+static void cancel_session(struct cli_session* cs, const char* ssid)
+{
+	if(cs && cs->ts && ssid && *ssid) {
+		turnsession_id sid = strtoull(ssid,NULL,10);
+		send_session_cancellation_to_relay(sid);
+	}
+}
+
+static void print_sessions(struct cli_session* cs, const char* pn, int exact_match, int print_users)
+{
+	if(cs && cs->ts && pn) {
+
+		while(pn[0] == ' ') ++pn;
+		if(pn[0] == '*') ++pn;
+
+		const char *uname="";
+		if(!print_users) {
+			uname = pn;
+			pn = "";
+		}
+
+		struct ps_arg arg = {cs,0,0,uname,pn,exact_match,NULL,NULL,NULL,0};
+
+		arg.ct = turn_time();
+
+		if(print_users) {
+			arg.users = ur_string_map_create(NULL);
+		}
+
+		ur_map_foreach_arg(adminserver.sessions, (foreachcb_arg_type)print_session, &arg);
+
+		myprintf(cs,"\n");
+
+		if(!print_users && !(cs->f)) {
+			if((unsigned long)arg.counter > (unsigned long)cli_max_output_sessions) {
+				myprintf(cs,"...\n");
+				myprintf(cs,"\n");
+			}
+		} else if(arg.user_counters && arg.user_names) {
+			size_t i;
+			for(i=0;i<arg.users_number;++i) {
+				if(arg.user_names[i]) {
+					myprintf(cs,"    user: <%s>, %lu sessions\n",
+						arg.user_names[i],
+						(unsigned long)arg.user_counters[i]);
+				}
+			}
+			myprintf(cs,"\n");
+		}
+
+		{
+			char ts[1025];
+			snprintf(ts,sizeof(ts),"  Total sessions");
+			if(cs->realm[0]) {
+				snprintf(ts+strlen(ts),sizeof(ts)-strlen(ts)," for realm %s",cs->realm);
+				if(cs->origin[0])
+					snprintf(ts+strlen(ts),sizeof(ts)-strlen(ts)," and for origin %s",cs->origin);
+			} else {
+				if(cs->origin[0])
+					snprintf(ts+strlen(ts),sizeof(ts)-strlen(ts)," for origin %s",cs->origin);
+			}
+			snprintf(ts+strlen(ts),sizeof(ts)-strlen(ts),": %lu", (unsigned long)arg.counter);
+			myprintf(cs,"%s\n", ts);
+			myprintf(cs,"\n");
+		}
+
+		if(!print_users && !(cs->f)) {
+			if((unsigned long)arg.counter > (unsigned long)cli_max_output_sessions) {
+				myprintf(cs,"  Warning: too many output sessions, more than the\n");
+				myprintf(cs,"  current value of cli-max-output-sessions CLI parameter.\n");
+				myprintf(cs,"  Refine your request or increase cli-max-output-sessions value.\n");
+				myprintf(cs,"\n");
+			}
+		}
+
+		if(arg.user_counters)
+			turn_free(arg.user_counters,sizeof(size_t)*arg.users_number);
+		if(arg.user_names) {
+			size_t i;
+			for(i=0;i<arg.users_number;++i) {
+				if(arg.user_names[i])
+					turn_free(arg.user_names[i],strlen(arg.user_names[i])+1);
+			}
+			turn_free(arg.user_names,sizeof(char*) * arg.users_number);
+		}
+		if(arg.users)
+			ur_string_map_free(&arg.users);
+	}
+}
+
+static void cli_print_configuration(struct cli_session* cs)
+{
+	if(cs) {
+		myprintf(cs,"\n");
+
+		cli_print_flag(cs,turn_params.verbose,"verbose",0);
+		cli_print_flag(cs,turn_params.turn_daemon,"daemon process",0);
+		cli_print_flag(cs,turn_params.stale_nonce,"stale-nonce",1);
+		cli_print_flag(cs,turn_params.stun_only,"stun-only",1);
+		cli_print_flag(cs,turn_params.no_stun,"no-stun",1);
+		cli_print_flag(cs,turn_params.secure_stun,"secure-stun",1);
+		cli_print_flag(cs,turn_params.do_not_use_config_file,"do-not-use-config-file",0);
+		cli_print_flag(cs,turn_params.rfc5780,"RFC5780 support",0);
+		cli_print_uint(cs,(unsigned int)turn_params.net_engine_version,"net engine version",0);
+		cli_print_str(cs,turn_params.net_engine_version_txt[(int)turn_params.net_engine_version],"net engine",0);
+		cli_print_flag(cs,turn_params.fingerprint,"enforce fingerprints",0);
+		cli_print_flag(cs,turn_params.mobility,"mobility",1);
+		cli_print_flag(cs,turn_params.udp_self_balance,"udp-self-balance",0);
+		cli_print_str(cs,turn_params.pidfile,"pidfile",0);
+		cli_print_uint(cs,(unsigned long)getuid(),"process user ID",0);
+		cli_print_uint(cs,(unsigned long)getgid(),"process group ID",0);
+
+		{
+			char wd[1025];
+			if(getcwd(wd,sizeof(wd)-1)) {
+				cli_print_str(cs,wd,"process dir",0);
+			}
+		}
+
+		myprintf(cs,"\n");
+
+		if(turn_params.cipher_list[0])
+			cli_print_str(cs,turn_params.cipher_list,"cipher-list",0);
+		else
+			cli_print_str(cs,DEFAULT_CIPHER_LIST,"cipher-list",0);
+
+		cli_print_str(cs,turn_params.ec_curve_name,"ec-curve-name",0);
+		{
+			if(turn_params.dh_key_size == DH_CUSTOM)
+				cli_print_str(cs,turn_params.dh_file,"dh-file",0);
+			else {
+				unsigned int dh_key_length = 1066;
+				if(turn_params.dh_key_size == DH_566)
+					dh_key_length = 566;
+				else if(turn_params.dh_key_size == DH_2066)
+					dh_key_length = 2066;
+				cli_print_uint(cs,(unsigned long)dh_key_length,"DH-key-length",0);
+			}
+		}
+
+		cli_print_str(cs,turn_params.ca_cert_file,"Certificate Authority file",0);
+		cli_print_str(cs,turn_params.cert_file,"Certificate file",0);
+		cli_print_str(cs,turn_params.pkey_file,"Private Key file",0);
+
+		if(turn_params.shatype == SHATYPE_SHA256)
+			cli_print_str(cs,"SHA256","SHA type",0);
+		else
+			cli_print_str(cs,"SHA1","SHA type",0);
+		myprintf(cs,"\n");
+
+		cli_print_str_array(cs,turn_params.listener.addrs,turn_params.listener.addrs_number,"Listener addr",0);
+
+		if(turn_params.listener_ifname[0])
+			cli_print_str(cs,turn_params.listener_ifname,"listener-ifname",0);
+
+		cli_print_flag(cs,turn_params.no_udp,"no-udp",0);
+		cli_print_flag(cs,turn_params.no_tcp,"no-tcp",0);
+		cli_print_flag(cs,turn_params.no_dtls,"no-dtls",0);
+		cli_print_flag(cs,turn_params.no_tls,"no-tls",0);
+
+		cli_print_flag(cs,(!turn_params.no_sslv3 && !turn_params.no_tls),"SSLv3",0);
+		cli_print_flag(cs,(!turn_params.no_tlsv1 && !turn_params.no_tls),"TLSv1.0",0);
+		cli_print_flag(cs,(!turn_params.no_tlsv1_1 && !turn_params.no_tls),"TLSv1.1",0);
+		cli_print_flag(cs,(!turn_params.no_tlsv1_2 && !turn_params.no_tls),"TLSv1.2",0);
+
+		cli_print_uint(cs,(unsigned long)turn_params.listener_port,"listener-port",0);
+		cli_print_uint(cs,(unsigned long)turn_params.tls_listener_port,"tls-listener-port",0);
+		cli_print_uint(cs,(unsigned long)turn_params.alt_listener_port,"alt-listener-port",0);
+		cli_print_uint(cs,(unsigned long)turn_params.alt_tls_listener_port,"alt-tls-listener-port",0);
+
+		cli_print_addr(cs,turn_params.external_ip,0,"External public IP",0);
+
+		myprintf(cs,"\n");
+
+		cli_print_addr_list(cs,&turn_params.aux_servers_list,1,"Aux server",0);
+		cli_print_addr_list(cs,&turn_params.alternate_servers_list,1,"Alternate server",0);
+		cli_print_addr_list(cs,&turn_params.tls_alternate_servers_list,1,"TLS alternate server",0);
+
+		myprintf(cs,"\n");
+
+		cli_print_str_array(cs,turn_params.relay_addrs,turn_params.relays_number,"Relay addr",0);
+
+		if(turn_params.relay_ifname[0])
+			cli_print_str(cs,turn_params.relay_ifname,"relay-ifname",0);
+
+		cli_print_flag(cs,turn_params.server_relay,"server-relay",0);
+
+		cli_print_flag(cs,turn_params.no_udp_relay,"no-udp-relay",1);
+		cli_print_flag(cs,turn_params.no_tcp_relay,"no-tcp-relay",1);
+
+		cli_print_uint(cs,(unsigned long)turn_params.min_port,"min-port",0);
+		cli_print_uint(cs,(unsigned long)turn_params.max_port,"max-port",0);
+
+		cli_print_ip_range_list(cs,&turn_params.ip_whitelist,"Whitelist IP (static)",0);
+		{
+			ip_range_list_t* l = get_ip_list("allowed");
+			cli_print_ip_range_list(cs,l,"Whitelist IP (dynamic)",0);
+			ip_list_free(l);
+		}
+
+		cli_print_ip_range_list(cs,&turn_params.ip_blacklist,"Blacklist IP (static)",0);
+		{
+			ip_range_list_t* l = get_ip_list("denied");
+			cli_print_ip_range_list(cs,l,"Blacklist IP (dynamic)",0);
+			ip_list_free(l);
+		}
+
+		cli_print_flag(cs,turn_params.no_multicast_peers,"no-multicast-peers",1);
+		cli_print_flag(cs,turn_params.no_loopback_peers,"no-loopback-peers",1);
+
+		myprintf(cs,"\n");
+
+		if(turn_params.default_users_db.persistent_users_db.userdb[0]) {
+			switch(turn_params.default_users_db.userdb_type) {
+#if !defined(TURN_NO_SQLITE)
+			case TURN_USERDB_TYPE_SQLITE:
+				cli_print_str(cs,"SQLite","DB type",0);
+				break;
+#endif
+#if !defined(TURN_NO_PQ)
+			case TURN_USERDB_TYPE_PQ:
+				cli_print_str(cs,"Postgres","DB type",0);
+				break;
+#endif
+#if !defined(TURN_NO_MYSQL)
+			case TURN_USERDB_TYPE_MYSQL:
+				cli_print_str(cs,"MySQL/MariaDB","DB type",0);
+				break;
+#endif
+#if !defined(TURN_NO_MONGO)
+			case TURN_USERDB_TYPE_MONGO:
+				cli_print_str(cs,"MongoDB","DB type",0);
+				break;
+#endif
+#if !defined(TURN_NO_HIREDIS)
+			case TURN_USERDB_TYPE_REDIS:
+				cli_print_str(cs,"redis","DB type",0);
+				break;
+#endif
+			default:
+				cli_print_str(cs,"unknown","DB type",0);
+			};
+			cli_print_str(cs,turn_params.default_users_db.persistent_users_db.userdb,"DB",0);
+		} else {
+			cli_print_str(cs,"none","DB type",0);
+			cli_print_str(cs,"none","DB",0);
+		}
+
+#if !defined(TURN_NO_HIREDIS)
+		if(turn_params.use_redis_statsdb && turn_params.redis_statsdb[0])
+			cli_print_str(cs,turn_params.redis_statsdb,"Redis Statistics DB",0);
+#endif
+
+		myprintf(cs,"\n");
+
+
+		{
+			char * rn = get_realm(NULL)->options.name;
+			if(rn[0])
+				cli_print_str(cs,rn,"Default realm",0);
+		}
+		if(cs->realm[0])
+			cli_print_str(cs,cs->realm,"CLI session realm",0);
+		else
+			cli_print_str(cs,get_realm(NULL)->options.name,"CLI session realm",0);
+		if(cs->origin[0])
+			cli_print_str(cs,cs->origin,"CLI session origin",0);
+		if(turn_params.ct == TURN_CREDENTIALS_LONG_TERM)
+			cli_print_flag(cs,1,"Long-term authorization mechanism",0);
+		else
+			cli_print_flag(cs,1,"Anonymous credentials",0);
+		cli_print_flag(cs,turn_params.use_auth_secret_with_timestamp,"TURN REST API support",0);
+		if(turn_params.use_auth_secret_with_timestamp && turn_params.rest_api_separator)
+			cli_print_uint(cs,turn_params.rest_api_separator,"TURN REST API separator ASCII number",0);
+
+		myprintf(cs,"\n");
+
+		cli_print_uint(cs,(unsigned long)cs->rp->status.total_current_allocs,"total-current-allocs",0);
+
+		myprintf(cs,"\n");
+
+		cli_print_uint(cs,(unsigned long)turn_params.total_quota,"Default total-quota",2);
+		cli_print_uint(cs,(unsigned long)turn_params.user_quota,"Default user-quota",2);
+		cli_print_uint(cs,(unsigned long)get_bps_capacity(),"Total server bps-capacity",2);
+		cli_print_uint(cs,(unsigned long)get_bps_capacity_allocated(),"Allocated bps-capacity",0);
+		cli_print_uint(cs,(unsigned long)get_max_bps(),"Default max-bps",2);
+
+		myprintf(cs,"\n");
+
+		cli_print_uint(cs,(unsigned long)cs->rp->options.perf_options.total_quota,"current realm total-quota",0);
+		cli_print_uint(cs,(unsigned long)cs->rp->options.perf_options.user_quota,"current realm user-quota",0);
+		cli_print_uint(cs,(unsigned long)cs->rp->options.perf_options.max_bps,"current realm max-bps",0);
+
+		myprintf(cs,"\n");
+
+		cli_print_uint(cs,(unsigned long)cli_max_output_sessions,"cli-max-output-sessions",2);
+
+		{
+		  myprintf(cs,"\n");
+		  const char *str="  (Note 1: parameters with (*) are toggleable)";
+		  myprintf(cs,"%s\n",str);
+		  myprintf(cs,"\n");
+		  str="  (Note 2: parameters with (**) are changeable)";
+		  myprintf(cs,"%s\n",str);
+		  myprintf(cs,"\n");
+		}
+	}
+}
+
+static void close_cli_session(struct cli_session* cs);
+
+static int run_cli_output(struct cli_session* cs, const char *buf, unsigned int len)
+{
+	if(cs && buf && len) {
+		if(bufferevent_write(cs->bev, buf, len)< 0) {
+			return -1;
+		}
+		return 0;
+	}
+	return -1;
+}
+
+static void close_cli_session(struct cli_session* cs)
+{
+	if(cs) {
+
+		addr_debug_print(adminserver.verbose, &(cs->addr),"CLI session disconnected from");
+
+		if(cs->ts) {
+			telnet_free(cs->ts);
+			cs->ts = NULL;
+		}
+
+		BUFFEREVENT_FREE(cs->bev);
+
+		if(cs->fd>=0) {
+			close(cs->fd);
+			cs->fd = -1;
+		}
+
+		turn_free(cs,sizeof(struct cli_session));
+	}
+}
+
+static void type_cli_cursor(struct cli_session* cs)
+{
+	if(cs && (cs->bev)) {
+	  myprintf(cs, "%s", CLI_CURSOR);
+	}
+}
+
+static void cli_add_alternate_server(struct cli_session* cs, const char* pn)
+{
+	if(cs && cs->ts && pn && *pn) {
+		add_alternate_server(pn);
+	}
+}
+
+static void cli_add_tls_alternate_server(struct cli_session* cs, const char* pn)
+{
+	if(cs && cs->ts && pn && *pn) {
+		add_tls_alternate_server(pn);
+	}
+}
+
+static void cli_del_alternate_server(struct cli_session* cs, const char* pn)
+{
+	if(cs && cs->ts && pn && *pn) {
+		del_alternate_server(pn);
+	}
+}
+
+static void cli_del_tls_alternate_server(struct cli_session* cs, const char* pn)
+{
+	if(cs && cs->ts && pn && *pn) {
+		del_tls_alternate_server(pn);
+	}
+}
+
+static int run_cli_input(struct cli_session* cs, const char *buf0, unsigned int len)
+{
+	int ret = 0;
+
+	if(cs && buf0 && cs->ts && cs->bev) {
+
+		char *buf = (char*)turn_malloc(len+1);
+		ns_bcopy(buf0,buf,len);
+		buf[len]=0;
+
+		char *cmd = buf;
+
+		while((cmd[0]==' ') || (cmd[0]=='\t')) ++cmd;
+
+		size_t sl = strlen(cmd);
+
+		while(sl) {
+			char c = cmd[sl-1];
+			if((c==10)||(c==13)) {
+				cmd[sl-1]=0;
+				--sl;
+			} else {
+				break;
+			}
+		}
+
+		if(sl) {
+			cs->cmds += 1;
+			if(cli_password[0] && !(cs->auth_completed)) {
+				if(strcmp(cmd,cli_password)) {
+					if(cs->cmds>=CLI_PASSWORD_TRY_NUMBER) {
+						addr_debug_print(1, &(cs->addr),"CLI authentication error");
+						TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"CLI authentication error\n");
+						close_cli_session(cs);
+					} else {
+						const char* ipwd="Enter password: ";
+						myprintf(cs,"%s\n",ipwd);
+					}
+				} else {
+					cs->auth_completed = 1;
+					addr_debug_print(1, &(cs->addr),"CLI authentication success");
+					type_cli_cursor(cs);
+				}
+			} else if((strcmp(cmd,"bye") == 0)||(strcmp(cmd,"quit") == 0)||(strcmp(cmd,"exit") == 0)||(strcmp(cmd,"q") == 0)) {
+				const char* str="Bye !";
+				myprintf(cs,"%s\n",str);
+				close_cli_session(cs);
+				ret = -1;
+			} else if((strcmp(cmd,"halt") == 0)||(strcmp(cmd,"shutdown") == 0)||(strcmp(cmd,"stop") == 0)) {
+				addr_debug_print(1, &(cs->addr),"Shutdown command received from CLI user");
+				const char* str="TURN server is shutting down";
+				myprintf(cs,"%s\n",str);
+				close_cli_session(cs);
+				turn_params.stop_turn_server = 1;
+				sleep(10);
+				exit(0);
+			} else if((strcmp(cmd,"?") == 0)||(strcmp(cmd,"h") == 0)||(strcmp(cmd,"help") == 0)) {
+				print_str_array(cs, CLI_GREETING_STR);
+				print_str_array(cs, CLI_HELP_STR);
+				type_cli_cursor(cs);
+			} else if(strcmp(cmd,"pc")==0) {
+				cli_print_configuration(cs);
+				type_cli_cursor(cs);
+			} else if(strstr(cmd,"tc ") == cmd) {
+				toggle_cli_param(cs,cmd+3);
+			} else if(strstr(cmd,"sr ") == cmd) {
+				STRCPY(cs->realm,cmd+3);
+				cs->rp = get_realm(cs->realm);
+				type_cli_cursor(cs);
+			} else if(strcmp(cmd,"ur") == 0) {
+				cs->realm[0]=0;
+				cs->rp = get_realm(NULL);
+				type_cli_cursor(cs);
+			} else if(strstr(cmd,"so ") == cmd) {
+				STRCPY(cs->origin,cmd+3);
+				type_cli_cursor(cs);
+			} else if(strcmp(cmd,"uo") == 0) {
+				cs->origin[0]=0;
+				type_cli_cursor(cs);
+			} else if(strstr(cmd,"tc") == cmd) {
+				toggle_cli_param(cs,cmd+2);
+				type_cli_cursor(cs);
+			} else if(strstr(cmd,"psp") == cmd) {
+				print_sessions(cs,cmd+3,0,0);
+				type_cli_cursor(cs);
+			} else if(strstr(cmd,"psd") == cmd) {
+				cmd += 3;
+				while(cmd[0]==' ') ++cmd;
+				if(!(cmd[0])) {
+					const char* str="You have to provide file name for ps dump\n";
+					myprintf(cs,"%s\n",str);
+				} else {
+					cs->f = fopen(cmd,"w");
+					if(!(cs->f)) {
+						const char* str="Cannot open file for writing\n";
+						myprintf(cs,"%s\n",str);
+					} else {
+						print_sessions(cs,"",1,0);
+						fclose(cs->f);
+						cs->f = NULL;
+					}
+				}
+				type_cli_cursor(cs);
+			} else if(strstr(cmd,"pu ") == cmd) {
+				print_sessions(cs,cmd+3,0,1);
+				type_cli_cursor(cs);
+			} else if(!strcmp(cmd,"pu")) {
+				print_sessions(cs,cmd+2,0,1);
+				type_cli_cursor(cs);
+			} else if(strstr(cmd,"ps") == cmd) {
+				print_sessions(cs,cmd+2,1,0);
+				type_cli_cursor(cs);
+			} else if(strstr(cmd,"cs ") == cmd) {
+				cancel_session(cs,cmd+3);
+				type_cli_cursor(cs);
+			} else if(strstr(cmd,"lr") == cmd) {
+				log_reset(cs);
+				type_cli_cursor(cs);
+			} else if(strstr(cmd,"cc ") == cmd) {
+				change_cli_param(cs,cmd+3);
+				type_cli_cursor(cs);
+			} else if(strstr(cmd,"cc") == cmd) {
+				change_cli_param(cs,cmd+2);
+				type_cli_cursor(cs);
+			} else if(strstr(cmd,"aas ") == cmd) {
+				cli_add_alternate_server(cs,cmd+4);
+				type_cli_cursor(cs);
+			} else if(strstr(cmd,"atas ") == cmd) {
+				cli_add_tls_alternate_server(cs,cmd+5);
+				type_cli_cursor(cs);
+			} else if(strstr(cmd,"das ") == cmd) {
+				cli_del_alternate_server(cs,cmd+4);
+				type_cli_cursor(cs);
+			} else if(strstr(cmd,"dtas ") == cmd) {
+				cli_del_tls_alternate_server(cs,cmd+5);
+				type_cli_cursor(cs);
+			} else {
+				const char* str="Unknown command\n";
+				myprintf(cs,"%s\n",str);
+				type_cli_cursor(cs);
+			}
+		} else {
+			type_cli_cursor(cs);
+		}
+
+		turn_free(buf,len+1);
+	}
+
+	return ret;
+}
+
+static void cli_socket_input_handler_bev(struct bufferevent *bev, void* arg)
+{
+	if (bev && arg) {
+
+		struct cli_session* cs = (struct cli_session*) arg;
+
+		if(!(cs->ts))
+			return;
+
+		stun_buffer buf;
+
+		if(cs->bev) {
+
+			int len = (int)bufferevent_read(cs->bev, buf.buf, STUN_BUFFER_SIZE-1);
+			if(len < 0) {
+				close_cli_session(cs);
+				return;
+			} else if(len == 0) {
+				return;
+			}
+
+			buf.len = len;
+			buf.offset = 0;
+			buf.buf[len]=0;
+
+			telnet_recv(cs->ts, (const char *)buf.buf, (unsigned int)(buf.len));
+		}
+	}
+}
+
+static void cli_eventcb_bev(struct bufferevent *bev, short events, void *arg)
+{
+	UNUSED_ARG(bev);
+
+	if (events & BEV_EVENT_CONNECTED) {
+		// Connect okay
+	} else if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF)) {
+		if (arg) {
+
+			struct cli_session* cs = (struct cli_session*) arg;
+
+			close_cli_session(cs);
+		}
+	}
+}
+
+static void cli_telnet_event_handler(telnet_t *telnet, telnet_event_t *event, void *user_data)
+{
+	if (user_data && telnet) {
+
+		struct cli_session *cs = (struct cli_session *) user_data;
+
+		switch (event->type){
+		case TELNET_EV_DATA:
+			run_cli_input(cs, event->data.buffer, event->data.size);
+			break;
+		case TELNET_EV_SEND:
+			run_cli_output(cs, event->data.buffer, event->data.size);
+			break;
+		case TELNET_EV_ERROR:
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TELNET error: %s", event->error.msg);
+			break;
+		default:
+			;
+		};
+	}
+}
+
+static void cliserver_input_handler(struct evconnlistener *l, evutil_socket_t fd,
+				struct sockaddr *sa, int socklen, void *arg)
+{
+	UNUSED_ARG(l);
+	UNUSED_ARG(arg);
+	UNUSED_ARG(socklen);
+
+	addr_debug_print(adminserver.verbose, (ioa_addr*)sa,"CLI connected to");
+
+	struct cli_session *clisession = (struct cli_session*)turn_malloc(sizeof(struct cli_session));
+	ns_bzero(clisession,sizeof(struct cli_session));
+
+	clisession->rp = get_realm(NULL);
+
+	set_socket_options_fd(fd, 1, sa->sa_family);
+
+	clisession->fd = fd;
+
+	addr_cpy(&(clisession->addr),(ioa_addr*)sa);
+
+	clisession->bev = bufferevent_socket_new(adminserver.event_base,
+					fd,
+					TURN_BUFFEREVENTS_OPTIONS);
+	debug_ptr_add(clisession->bev);
+	bufferevent_setcb(clisession->bev, cli_socket_input_handler_bev, NULL,
+			cli_eventcb_bev, clisession);
+	bufferevent_setwatermark(clisession->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK);
+	bufferevent_enable(clisession->bev, EV_READ); /* Start reading. */
+
+	clisession->ts = telnet_init(cli_telopts, cli_telnet_event_handler, 0, clisession);
+
+	if(!(clisession->ts)) {
+		const char *str = "Cannot open telnet session\n";
+		addr_debug_print(adminserver.verbose, (ioa_addr*)sa,str);
+		close_cli_session(clisession);
+	} else {
+	  print_str_array(clisession, CLI_GREETING_STR);
+	  telnet_printf(clisession->ts,"\n");
+	  telnet_printf(clisession->ts,"Type '?' for help\n");
+	  if(cli_password[0]) {
+	    const char* ipwd="Enter password: ";
+	    telnet_printf(clisession->ts,"%s\n",ipwd);
+	  } else {
+	    type_cli_cursor(clisession);
+	  }
+	}
+}
+
+void setup_admin_thread(void)
+{
+	adminserver.event_base = turn_event_base_new();
+	super_memory_t* sm = new_super_memory_region();
+	adminserver.e = create_ioa_engine(sm, adminserver.event_base, turn_params.listener.tp, turn_params.relay_ifname, turn_params.relays_number, turn_params.relay_addrs,
+				turn_params.default_relays, turn_params.verbose
+	#if !defined(TURN_NO_HIREDIS)
+				,turn_params.redis_statsdb
+	#endif
+		);
+
+	TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"IO method (admin thread): %s\n",event_base_get_method(adminserver.event_base));
+
+	{
+		struct bufferevent *pair[2];
+
+		bufferevent_pair_new(adminserver.event_base, TURN_BUFFEREVENTS_OPTIONS, pair);
+
+		adminserver.in_buf = pair[0];
+		adminserver.out_buf = pair[1];
+
+		bufferevent_setcb(adminserver.in_buf, admin_server_receive_message, NULL, NULL, &adminserver);
+		bufferevent_enable(adminserver.in_buf, EV_READ);
+	}
+
+	{
+		struct bufferevent *pair[2];
+
+		bufferevent_pair_new(adminserver.event_base, TURN_BUFFEREVENTS_OPTIONS, pair);
+
+		adminserver.https_in_buf = pair[0];
+		adminserver.https_out_buf = pair[1];
+
+		bufferevent_setcb(adminserver.https_in_buf, https_admin_server_receive_message, NULL, NULL, &adminserver);
+		bufferevent_enable(adminserver.https_in_buf, EV_READ);
+	}
+
+	if(use_cli) {
+		if(!cli_addr_set) {
+			if(make_ioa_addr((const u08bits*)CLI_DEFAULT_IP,0,&cli_addr)<0) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot set cli address %s\n",CLI_DEFAULT_IP);
+				return;
+			}
+		}
+
+		addr_set_port(&cli_addr,cli_port);
+
+		adminserver.listen_fd = socket(cli_addr.ss.sa_family, SOCK_STREAM, 0);
+		if (adminserver.listen_fd < 0) {
+			perror("socket");
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot open CLI socket\n");
+			return;
+		}
+
+		if(addr_bind(adminserver.listen_fd,&cli_addr,1)<0) {
+			perror("Cannot bind CLI socket to addr");
+			char saddr[129];
+			addr_to_string(&cli_addr,(u08bits*)saddr);
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot bind CLI listener socket to addr %s\n",saddr);
+			socket_closesocket(adminserver.listen_fd);
+			return;
+		}
+
+		socket_tcp_set_keepalive(adminserver.listen_fd);
+
+		socket_set_nonblocking(adminserver.listen_fd);
+
+		adminserver.l = evconnlistener_new(adminserver.event_base,
+			  cliserver_input_handler, &adminserver,
+			  LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
+			  1024, adminserver.listen_fd);
+
+		if(!(adminserver.l)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot create CLI listener\n");
+			socket_closesocket(adminserver.listen_fd);
+			return;
+		}
+
+		addr_debug_print(adminserver.verbose, &cli_addr,"CLI listener opened on ");
+	}
+
+	adminserver.sessions = ur_map_create();
+}
+
+void admin_server_receive_message(struct bufferevent *bev, void *ptr)
+{
+	UNUSED_ARG(ptr);
+
+	struct turn_session_info *tsi = (struct turn_session_info*)turn_malloc(sizeof(struct turn_session_info));
+	turn_session_info_init(tsi);
+	int n = 0;
+	struct evbuffer *input = bufferevent_get_input(bev);
+
+	while ((n = evbuffer_remove(input, tsi, sizeof(struct turn_session_info))) > 0) {
+		if (n != sizeof(struct turn_session_info)) {
+			fprintf(stderr,"%s: Weird CLI buffer error: size=%d\n",__FUNCTION__,n);
+			continue;
+		}
+
+		ur_map_value_type t = 0;
+		if (ur_map_get(adminserver.sessions, (ur_map_key_type)tsi->id, &t) && t) {
+			struct turn_session_info *old = (struct turn_session_info*)t;
+			turn_session_info_clean(old);
+			turn_free(old,sizeof(struct turn_session_info));
+			ur_map_del(adminserver.sessions, (ur_map_key_type)tsi->id, NULL);
+		}
+
+		if(tsi->valid) {
+			ur_map_put(adminserver.sessions, (ur_map_key_type)tsi->id, (ur_map_value_type)tsi);
+			tsi = (struct turn_session_info*)turn_malloc(sizeof(struct turn_session_info));
+			turn_session_info_init(tsi);
+		} else {
+			turn_session_info_clean(tsi);
+		}
+	}
+
+	if(tsi) {
+		turn_session_info_clean(tsi);
+		turn_free(tsi,sizeof(struct turn_session_info));
+	}
+}
+
+int send_turn_session_info(struct turn_session_info* tsi)
+{
+	int ret = -1;
+
+	if(tsi) {
+		struct evbuffer *output = bufferevent_get_output(adminserver.out_buf);
+		if(output) {
+			if(evbuffer_add(output,tsi,sizeof(struct turn_session_info))>=0) {
+				ret = 0;
+			}
+		}
+	}
+
+	return ret;
+}
+
+/////////// HTTPS /////////////
+
+enum _AS_FORM {
+	AS_FORM_LOGON,
+	AS_FORM_LOGOUT,
+	AS_FORM_PC,
+	AS_FORM_HOME,
+	AS_FORM_TOGGLE,
+	AS_FORM_UPDATE,
+	AS_FORM_PS,
+	AS_FORM_USERS,
+	AS_FORM_SS,
+	AS_FORM_OS,
+	AS_FORM_OAUTH,
+	AS_FORM_OAUTH_SHOW_KEYS,
+	AS_FORM_UNKNOWN
+};
+
+typedef enum _AS_FORM AS_FORM;
+
+#define HR_USERNAME "uname"
+#define HR_PASSWORD "pwd"
+#define HR_PASSWORD1 "pwd1"
+#define HR_REALM "realm"
+#define HR_ADD_USER "add_user"
+#define HR_ADD_REALM "add_user_realm"
+#define HR_ADD_SECRET "add_secret"
+#define HR_ADD_ORIGIN "add_origin"
+#define HR_CLIENT_PROTOCOL "cprotocol"
+#define HR_USER_PATTERN "puser"
+#define HR_MAX_SESSIONS "maxsess"
+#define HR_CANCEL_SESSION "cs"
+#define HR_DELETE_USER "du"
+#define HR_DELETE_REALM "dr"
+#define HR_DELETE_SECRET "ds"
+#define HR_DELETE_ORIGIN "do"
+#define HR_DELETE_IP "dip"
+#define HR_DELETE_IP_REALM "dipr"
+#define HR_DELETE_IP_KIND "dipk"
+#define HR_ADD_IP "aip"
+#define HR_ADD_IP_REALM "aipr"
+#define HR_ADD_IP_KIND "aipk"
+#define HR_UPDATE_PARAMETER "togglepar"
+#define HR_ADD_OAUTH_KID "oauth_kid"
+#define HR_ADD_OAUTH_TS "oauth_ts"
+#define HR_ADD_OAUTH_LT "oauth_lt"
+#define HR_ADD_OAUTH_IKM "oauth_ikm"
+#define HR_ADD_OAUTH_RS_KEY "oauth_rs_key"
+#define HR_ADD_OAUTH_AUTH_KEY "oauth_auth_key"
+#define HR_ADD_OAUTH_HKDF "oauth_hkdf"
+#define HR_ADD_OAUTH_TEA "oauth_tea"
+#define HR_ADD_OAUTH_AA "oauth_aa"
+#define HR_DELETE_OAUTH_KID "oauth_kid_del"
+#define HR_OAUTH_KID "kid"
+
+struct form_name {
+	AS_FORM form;
+	const char* name;
+};
+
+static struct form_name form_names[] = {
+				{AS_FORM_LOGON,"/logon"},
+				{AS_FORM_LOGOUT,"/logout"},
+				{AS_FORM_PC,"/pc"},
+				{AS_FORM_HOME,"/home"},
+				{AS_FORM_TOGGLE,"/toggle"},
+				{AS_FORM_UPDATE,"/update"},
+				{AS_FORM_PS,"/ps"},
+				{AS_FORM_USERS,"/us"},
+				{AS_FORM_SS,"/ss"},
+				{AS_FORM_OS,"/os"},
+				{AS_FORM_OAUTH,"/oauth"},
+				{AS_FORM_OAUTH_SHOW_KEYS,"/oauth_show_keys"},
+				{AS_FORM_UNKNOWN,NULL}
+};
+
+#define admin_title "TURN Server (https admin connection)"
+#define __bold_admin_title "<b>TURN Server</b><br><i>https admin connection</i><br>\r\n"
+#define bold_admin_title get_bold_admin_title()
+
+static ioa_socket_handle current_socket = NULL;
+
+static char *get_bold_admin_title(void)
+{
+	static char sbat[1025];
+	STRCPY(sbat,__bold_admin_title);
+	if(current_socket && current_socket->special_session) {
+		struct admin_session* as = (struct admin_session*)current_socket->special_session;
+		if(as->as_ok) {
+			if(as->as_login[0]) {
+				char *dst=sbat+strlen(sbat);
+				snprintf(dst,ADMIN_USER_MAX_LENGTH*2," admin user: <b><i>%s</i></b><br>\r\n",as->as_login);
+			}
+			if(as->as_realm[0]) {
+				char *dst=sbat+strlen(sbat);
+				snprintf(dst,STUN_MAX_REALM_SIZE*2," admin session realm: <b><i>%s</i></b><br>\r\n",as->as_realm);
+			} else if(as->as_eff_realm[0]) {
+				char *dst=sbat+strlen(sbat);
+				snprintf(dst,STUN_MAX_REALM_SIZE*2," admin session realm: <b><i>%s</i></b><br>\r\n",as->as_eff_realm);
+			}
+		}
+	}
+	return sbat;
+}
+
+static int wrong_html_name(const char* s)
+{
+	int ret = 0;
+	if(s) {
+		char* v=evhttp_encode_uri(s);
+		ret = strcmp(v,s);
+		free(v);
+	}
+	return ret;
+}
+
+static int is_as_ok(ioa_socket_handle s) {
+	return (s && s->special_session &&
+			((struct admin_session*)s->special_session)->as_ok);
+}
+
+static int is_superuser(void) {
+	return (is_as_ok(current_socket) &&
+			(!((struct admin_session*)current_socket->special_session)->as_realm[0]));
+}
+
+static char* current_realm(void) {
+	if(current_socket && current_socket->special_session && ((struct admin_session*)current_socket->special_session)->as_ok) {
+		return ((struct admin_session*)current_socket->special_session)->as_realm;
+	} else {
+		static char bad_realm[1025] = "_ERROR:UNKNOWN_REALM__";
+		return bad_realm;
+	}
+}
+
+static char* current_eff_realm(void) {
+	char* r = current_realm();
+	if(r && r[0]) return r;
+	else if(current_socket && current_socket->special_session && ((struct admin_session*)current_socket->special_session)->as_ok) {
+		return ((struct admin_session*)current_socket->special_session)->as_eff_realm;
+	} else {
+		static char bad_eff_realm[1025] = "_ERROR:UNKNOWN_REALM__";
+		return bad_eff_realm;
+	}
+}
+
+static size_t current_max_output_sessions(void) {
+	if(current_socket && current_socket->special_session && ((struct admin_session*)current_socket->special_session)->as_ok) {
+		return ((struct admin_session*)current_socket->special_session)->number_of_user_sessions;
+	}
+	return DEFAULT_CLI_MAX_OUTPUT_SESSIONS;
+}
+
+static void set_current_max_output_sessions(size_t value) {
+	if(current_socket && current_socket->special_session && ((struct admin_session*)current_socket->special_session)->as_ok) {
+		((struct admin_session*)current_socket->special_session)->number_of_user_sessions = value;
+	}
+}
+
+static void https_cancel_session(const char* ssid)
+{
+	if(ssid && *ssid) {
+		turnsession_id sid = (turnsession_id)strtoull(ssid,NULL,10);
+		send_session_cancellation_to_relay(sid);
+	}
+}
+
+static void https_print_top_page_header(struct str_buffer *sb)
+{
+	str_buffer_append(sb,"<!DOCTYPE html>\r\n<html>\r\n  <head>\r\n    <title>");
+	str_buffer_append(sb,admin_title);
+	str_buffer_append(sb,"</title>\r\n <style> table, th, td { border: 1px solid black; border-collapse: collapse; text-align: left; padding: 5px;} table#msg th { color: red; background-color: white; } </style> </head>\r\n  <body>\r\n    ");
+	str_buffer_append(sb,bold_admin_title);
+}
+
+static void https_print_page_header(struct str_buffer *sb)
+{
+	https_print_top_page_header(sb);
+	str_buffer_append(sb,"<br><a href=\"/home?");
+	str_buffer_append(sb,HR_REALM);
+	str_buffer_append(sb,"=");
+	str_buffer_append(sb,current_eff_realm());
+	str_buffer_append(sb,"\">home page</a><br>\r\n<br><a href=\"/logout\">logout</a><br>\r\n");
+	str_buffer_append(sb,"<br>\r\n");
+}
+
+static void https_finish_page(struct str_buffer *sb, ioa_socket_handle s, int cclose)
+{
+	str_buffer_append(sb,"</body>\r\n</html>\r\n");
+
+	send_str_from_ioa_socket_tcp(s,"HTTP/1.1 200 OK\r\nServer: ");
+	send_str_from_ioa_socket_tcp(s,TURN_SOFTWARE);
+	send_str_from_ioa_socket_tcp(s,"\r\n");
+	send_str_from_ioa_socket_tcp(s,get_http_date_header());
+	if(cclose) {
+		send_str_from_ioa_socket_tcp(s,"Connection: close");
+	}
+	send_str_from_ioa_socket_tcp(s,"Content-Type: text/html; charset=UTF-8\r\nContent-Length: ");
+
+	send_ulong_from_ioa_socket_tcp(s,str_buffer_get_str_len(sb));
+
+	send_str_from_ioa_socket_tcp(s,"\r\n\r\n");
+	send_str_from_ioa_socket_tcp(s,str_buffer_get_str(sb));
+
+	str_buffer_free(sb);
+}
+
+static AS_FORM get_form(const char* path) {
+	if(path) {
+		size_t i = 0;
+		while(form_names[i].name) {
+			if(!strcmp(form_names[i].name,path))
+				return form_names[i].form;
+			++i;
+		}
+	}
+	return AS_FORM_UNKNOWN;
+}
+
+static void write_https_logon_page(ioa_socket_handle s)
+{
+	if(s && !ioa_socket_tobeclosed(s)) {
+
+		struct str_buffer* sb = str_buffer_new();
+
+		https_print_top_page_header(sb);
+
+		int we_have_admin_users = 0;
+		const turn_dbdriver_t * dbd = get_dbdriver();
+		if (dbd && dbd->list_admin_users) {
+			int ausers = dbd->list_admin_users(1);
+			if(ausers>0) {
+				we_have_admin_users = 1;
+			}
+		}
+
+		if(!we_have_admin_users) {
+			str_buffer_append(sb,"<br>To use the HTTPS admin connection, you have to set the database table <b><i>admin_user</i></b> with the admin user accounts.<br>\r\n");
+		} else {
+			str_buffer_append(sb,"<br><br>\r\n");
+			str_buffer_append(sb,"<form action=\"");
+			str_buffer_append(sb,form_names[AS_FORM_LOGON].name);
+			str_buffer_append(sb,"\" method=\"POST\">\r\n");
+			str_buffer_append(sb,"  <fieldset><legend>Admin user information:</legend>  user name:<br><input required type=\"text\" name=\"");
+			str_buffer_append(sb,HR_USERNAME);
+			str_buffer_append(sb,"\" value=\"\"><br>password:<br><input required type=\"password\" name=\"");
+			str_buffer_append(sb,HR_PASSWORD);
+			str_buffer_append(sb,"\" value=\"\"><br><br><input type=\"submit\" value=\"Login\"></fieldset>\r\n");
+			str_buffer_append(sb,"</form>\r\n");
+		}
+
+		https_finish_page(sb,s,!we_have_admin_users);
+	}
+}
+
+static void write_https_home_page(ioa_socket_handle s)
+{
+	if(s && !ioa_socket_tobeclosed(s)) {
+
+		if(!is_as_ok(s)) {
+			write_https_logon_page(s);
+		} else {
+
+			struct str_buffer* sb = str_buffer_new();
+
+			https_print_page_header(sb);
+
+			str_buffer_append(sb,"<form action=\"");
+			str_buffer_append(sb,form_names[AS_FORM_HOME].name);
+			str_buffer_append(sb,"\" method=\"POST\">\r\n");
+			str_buffer_append(sb,"  <fieldset><legend>Actions:</legend>\r\n");
+
+			str_buffer_append(sb,"  Realm name: <input type=\"text\" name=\"");
+			str_buffer_append(sb,HR_REALM);
+			str_buffer_append(sb,"\" value=\"");
+			str_buffer_append(sb,current_eff_realm());
+			str_buffer_append(sb,"\"");
+			if(!is_superuser()) {
+			  str_buffer_append(sb," disabled >");
+			} else {
+			  str_buffer_append(sb,"> <input type=\"submit\" value=\"Set Admin Session Realm\" >");
+			}
+
+			str_buffer_append(sb,"<br>");
+
+			str_buffer_append(sb,"<br><a href=\"");
+			str_buffer_append(sb,form_names[AS_FORM_PC].name);
+			str_buffer_append(sb,"?");
+			str_buffer_append(sb,HR_REALM);
+			str_buffer_append(sb,"=");
+			str_buffer_append(sb,current_eff_realm());
+			str_buffer_append(sb,"\">Configuration Parameters</a>");
+
+			str_buffer_append(sb,"<br><a href=\"");
+			str_buffer_append(sb,form_names[AS_FORM_PS].name);
+			str_buffer_append(sb,"?");
+			str_buffer_append(sb,HR_REALM);
+			str_buffer_append(sb,"=");
+			str_buffer_append(sb,current_eff_realm());
+			str_buffer_append(sb,"&");
+			str_buffer_append(sb,HR_MAX_SESSIONS);
+			str_buffer_append(sb,"=");
+			str_buffer_append_sz(sb,current_max_output_sessions());
+			str_buffer_append(sb,"\">TURN Sessions</a>");
+
+			str_buffer_append(sb,"<br><a href=\"");
+			str_buffer_append(sb,form_names[AS_FORM_USERS].name);
+			str_buffer_append(sb,"?");
+			str_buffer_append(sb,HR_REALM);
+			str_buffer_append(sb,"=");
+			str_buffer_append(sb,current_eff_realm());
+			str_buffer_append(sb,"\">Users</a>");
+
+			str_buffer_append(sb,"<br><a href=\"");
+			str_buffer_append(sb,form_names[AS_FORM_SS].name);
+			str_buffer_append(sb,"?");
+			str_buffer_append(sb,HR_REALM);
+			str_buffer_append(sb,"=");
+			str_buffer_append(sb,current_eff_realm());
+			str_buffer_append(sb,"\">Shared Secrets (for TURN REST API)</a>");
+
+			str_buffer_append(sb,"<br><a href=\"");
+			str_buffer_append(sb,form_names[AS_FORM_OS].name);
+			str_buffer_append(sb,"?");
+			str_buffer_append(sb,HR_REALM);
+			str_buffer_append(sb,"=");
+			str_buffer_append(sb,current_eff_realm());
+			str_buffer_append(sb,"\">Origins</a>");
+
+			if(is_superuser()) {
+			  str_buffer_append(sb,"<br><a href=\"");
+			  str_buffer_append(sb,form_names[AS_FORM_OAUTH].name);
+			  str_buffer_append(sb,"?");
+			  str_buffer_append(sb,HR_REALM);
+			  str_buffer_append(sb,"=");
+			  str_buffer_append(sb,current_eff_realm());
+			  str_buffer_append(sb,"\">oAuth keys</a>");
+			}
+
+			str_buffer_append(sb,"</fieldset>\r\n");
+			str_buffer_append(sb,"</form>\r\n");
+
+			https_finish_page(sb,s,0);
+		}
+	}
+}
+
+static void sbprintf(struct str_buffer *sb, const char *format, ...)
+{
+	if(sb && format) {
+		va_list args;
+		va_start (args, format);
+		char s[1025]="\0";
+		vsnprintf(s,sizeof(s)-1,format, args);
+		str_buffer_append(sb,s);
+		va_end (args);
+	}
+}
+
+static void https_print_flag(struct str_buffer* sb, int flag, const char* name, const char* param_name)
+{
+	if(sb && name) {
+		if(!is_superuser())
+			param_name = 0;
+		if(!param_name) {
+			sbprintf(sb,"<tr><td>%s</td><td>%s</td></tr>\r\n",name,get_flag(flag));
+		} else {
+			sbprintf(sb,"<tr><td>%s</td><td><a href=\"/toggle?%s=%s\">%s</a></td></tr>\r\n",name,HR_UPDATE_PARAMETER,param_name,get_flag(flag));
+		}
+	}
+}
+
+static void https_print_uint(struct str_buffer* sb, unsigned long value, const char* name, const char* param_name)
+{
+	if(sb && name) {
+		if(!is_superuser())
+			param_name = 0;
+		if(!param_name) {
+			if(value) {
+				sbprintf(sb,"<tr><td>%s</td><td>%lu</td></tr>\r\n",name,value);
+			} else {
+				sbprintf(sb,"<tr><td>%s</td><td> </td></tr>\r\n",name);
+			}
+		} else {
+			if(value) {
+				sbprintf(sb,"<tr><td>%s</td><td> <form action=\"%s?%s=%s\" method=\"POST\"><input type=\"text\" name=\"%s\" value=\"%lu\"><input type=\"submit\" value=\"Update\"></form> </td></tr>\r\n",name,form_names[AS_FORM_UPDATE].name,HR_UPDATE_PARAMETER,param_name,param_name,value);
+			} else {
+				sbprintf(sb,"<tr><td>%s</td><td> <form action=\"%s?%s=%s\" method=\"POST\"><input type=\"text\" name=\"%s\" value=\"\"><input type=\"submit\" value=\"Update\"></form> </td></tr>\r\n",name,form_names[AS_FORM_UPDATE].name,HR_UPDATE_PARAMETER,param_name,param_name);
+			}
+		}
+	}
+}
+
+static void https_print_str(struct str_buffer* sb, const char *value, const char* name, const char* param_name)
+{
+	if(sb && name && value) {
+		if(!is_superuser())
+			param_name = 0;
+		if(!param_name) {
+			sbprintf(sb,"<tr><td>%s</td><td>%s</td></tr>\r\n",name,value);
+		} else {
+			sbprintf(sb,"<tr><td>%s</td><td> <form action=\"%s?%s=%s\" method=\"POST\"><input type=\"text\" name=\"%s\" value=\"%s\"><input type=\"submit\" value=\"Update\"></form> </td></tr>\r\n",name,form_names[AS_FORM_UPDATE].name,HR_UPDATE_PARAMETER,param_name,param_name,value);
+		}
+	}
+}
+
+static void https_print_str_array(struct str_buffer* sb, char **value, size_t sz, const char* name)
+{
+	if(sb && name && value && sz) {
+		size_t i;
+		for(i=0;i<sz;i++) {
+			if(value[i]) {
+				sbprintf(sb,"<tr><td>  %s</td><td> %s</td></tr>\r\n",name,value[i]);
+			}
+		}
+	}
+}
+
+static void https_print_addr(struct str_buffer* sb, ioa_addr *value, int use_port, const char* name)
+{
+	if(sb && name && value) {
+		char s[256];
+		if(!use_port)
+			addr_to_string_no_port(value,(u08bits*)s);
+		else
+			addr_to_string(value,(u08bits*)s);
+		sbprintf(sb,"<tr><td>  %s</td><td> %s</td></tr>\r\n",name,s);
+	}
+}
+
+static size_t https_print_addr_list(struct str_buffer* sb, turn_server_addrs_list_t *value, int use_port, const char* name)
+{
+	if(sb && name && value && value->size && value->addrs) {
+		char s[256];
+		size_t i;
+		for(i=0;i<value->size;i++) {
+			if(!use_port)
+				addr_to_string_no_port(&(value->addrs[i]),(u08bits*)s);
+			else
+				addr_to_string(&(value->addrs[i]),(u08bits*)s);
+			sbprintf(sb,"</tr><td>  %s</td><td> %s</td></tr>\r\n",name,s);
+		}
+		return i;
+	}
+	return 0;
+}
+
+static const char* change_ip_addr_html(int dynamic,const char* kind,const char* ip,const char *realm, char *buffer, size_t sz)
+{
+	if(!buffer || !sz) {
+		return "";
+	} else {
+		buffer[0]=0;
+		if(dynamic && kind && ip) {
+
+			if(!realm) realm="";
+
+			if(current_realm()[0] && strcmp(current_realm(),realm)) {
+				//delete forbidden
+			} else {
+				char *eip = evhttp_encode_uri(ip);
+				snprintf(buffer,sz-1,"<a href=\"%s?%s=%s&%s=%s&%s=%s\">delete</a>",form_names[AS_FORM_UPDATE].name,HR_DELETE_IP_KIND,kind,HR_DELETE_IP_REALM,realm,HR_DELETE_IP,eip);
+				free(eip);
+			}
+		}
+		return buffer;
+	}
+}
+
+static void https_print_ip_range_list(struct str_buffer* sb, ip_range_list_t *value, const char* name, const char* kind, int dynamic)
+{
+	if(sb && name) {
+		if(value && value->rs) {
+			size_t i;
+			char buffer[1025];
+			for(i=0;i<value->ranges_number;++i) {
+				if(value->rs[i].realm[0]) {
+					if(current_eff_realm()[0] && strcmp(current_eff_realm(),value->rs[i].realm)) {
+						continue;
+					} else {
+						sbprintf(sb,"<tr><td>  %s</td><td> %s [%s] %s</td></tr>\r\n",name,value->rs[i].str,value->rs[i].realm, change_ip_addr_html(dynamic,kind,value->rs[i].str,value->rs[i].realm,buffer,sizeof(buffer)));
+					}
+				} else {
+					sbprintf(sb,"<tr><td>  %s</td><td> %s %s</td></tr>\r\n",name,value->rs[i].str, change_ip_addr_html(dynamic,kind,value->rs[i].str,value->rs[i].realm,buffer,sizeof(buffer)));
+				}
+			}
+		}
+
+		if(dynamic) {
+			sbprintf(sb,"<tr><td> Add %s</td><td>",name);
+			sbprintf(sb,"<form action=\"%s?%s=%s\" method=\"POST\">IP range:<input required type=\"text\" name=\"%s\" value=\"\" >",form_names[AS_FORM_UPDATE].name,HR_ADD_IP_KIND,kind,HR_ADD_IP);
+			sbprintf(sb,"Realm: <input type=\"text\" name=\"%s\" value=\"%s\" ",HR_ADD_IP_REALM,current_eff_realm());
+			if(!is_superuser()) {
+				sbprintf(sb," disabled ");
+			}
+			sbprintf(sb,">");
+			sbprintf(sb,"<input type=\"submit\" value=\"Add IP\"></form> </td></tr>\r\n");
+		}
+	}
+}
+
+static void toggle_param(const char* pn)
+{
+	if(is_superuser()) {
+		if(pn) {
+			int i=0;
+			while(tcmds[i].cmd && tcmds[i].data) {
+				if(strcmp(tcmds[i].cmd,pn) == 0) {
+					*(tcmds[i].data) = !(*(tcmds[i].data));
+					return;
+				}
+				++i;
+			}
+		}
+	}
+}
+
+static void update_param(const char* pn, const char *value)
+{
+	if(pn) {
+		if(!value)
+			value = "0";
+		if(is_superuser()) {
+			if(strstr(pn,"total-quota")==pn) {
+				turn_params.total_quota = atoi(value);
+			} else if(strstr(pn,"user-quota")==pn) {
+				turn_params.user_quota = atoi(value);
+			} else if(strstr(pn,"max-bps")==pn) {
+				set_max_bps((band_limit_t)atol(value));
+			} else if(strstr(pn,"bps-capacity")==pn) {
+				set_bps_capacity((band_limit_t)atol(value));
+			}
+		}
+		{
+			realm_params_t *rp = get_realm(current_eff_realm());
+			if(!rp) rp = get_realm(NULL);
+
+			const turn_dbdriver_t * dbd = get_dbdriver();
+			if (dbd && dbd->set_realm_option_one) {
+				if(strstr(pn,"cr-total-quota")==pn) {
+					rp->options.perf_options.total_quota = atoi(value);
+					dbd->set_realm_option_one((u08bits*)rp->options.name,rp->options.perf_options.total_quota,"total-quota");
+				} else if(strstr(pn,"cr-user-quota")==pn) {
+					rp->options.perf_options.user_quota = atoi(value);
+					dbd->set_realm_option_one((u08bits*)rp->options.name,rp->options.perf_options.user_quota,"user-quota");
+				} else if(strstr(pn,"cr-max-bps")==pn) {
+					rp->options.perf_options.max_bps = (band_limit_t)atol(value);
+					dbd->set_realm_option_one((u08bits*)rp->options.name,rp->options.perf_options.max_bps,"max-bps");
+				}
+			}
+		}
+	}
+}
+
+static void https_print_empty_row(struct str_buffer* sb, size_t span)
+{
+	str_buffer_append(sb,"<tr><td colspan=");
+	str_buffer_append_sz(sb,span);
+	str_buffer_append(sb,"><br></td></tr>");
+}
+
+static void write_pc_page(ioa_socket_handle s)
+{
+	if(s && !ioa_socket_tobeclosed(s)) {
+
+		if(!is_as_ok(s)) {
+			write_https_logon_page(s);
+		} else {
+
+			struct str_buffer* sb = str_buffer_new();
+
+			https_print_page_header(sb);
+
+			str_buffer_append(sb,"<br>\r\n");
+			str_buffer_append(sb,"<b>Configuration Parameters:</b><br><br><table  style=\"width:100%\">\r\n");
+			str_buffer_append(sb,"<tr><th>Parameter</th><th>Value</th></tr>\r\n");
+
+			{
+				https_print_flag(sb,turn_params.verbose,"verbose",0);
+				https_print_flag(sb,turn_params.turn_daemon,"daemon process",0);
+				https_print_flag(sb,turn_params.stale_nonce,"stale-nonce","stale-nonce");
+				https_print_flag(sb,turn_params.stun_only,"stun-only","stun-only");
+				https_print_flag(sb,turn_params.no_stun,"no-stun","no-stun");
+				https_print_flag(sb,turn_params.secure_stun,"secure-stun","secure-stun");
+				https_print_flag(sb,turn_params.do_not_use_config_file,"do-not-use-config-file",0);
+				https_print_flag(sb,turn_params.rfc5780,"RFC5780 support",0);
+				https_print_uint(sb,(unsigned int)turn_params.net_engine_version,"net engine version",0);
+				https_print_str(sb,turn_params.net_engine_version_txt[(int)turn_params.net_engine_version],"net engine",0);
+				https_print_flag(sb,turn_params.fingerprint,"enforce fingerprints",0);
+				https_print_flag(sb,turn_params.mobility,"mobility","mobility");
+				https_print_flag(sb,turn_params.udp_self_balance,"udp-self-balance",0);
+				https_print_str(sb,turn_params.pidfile,"pidfile",0);
+				https_print_uint(sb,(unsigned long)getuid(),"process user ID",0);
+				https_print_uint(sb,(unsigned long)getgid(),"process group ID",0);
+
+				{
+					char wd[1025];
+					if(getcwd(wd,sizeof(wd)-1)) {
+						https_print_str(sb,wd,"process dir",0);
+					}
+				}
+
+				https_print_empty_row(sb,2);
+
+				if(turn_params.cipher_list[0])
+					https_print_str(sb,turn_params.cipher_list,"cipher-list",0);
+				else
+					https_print_str(sb,DEFAULT_CIPHER_LIST,"cipher-list",0);
+
+				https_print_str(sb,turn_params.ec_curve_name,"ec-curve-name",0);
+				{
+					if(turn_params.dh_key_size == DH_CUSTOM)
+						https_print_str(sb,turn_params.dh_file,"dh-file",0);
+					else {
+						unsigned int dh_key_length = 1066;
+						if(turn_params.dh_key_size == DH_566)
+							dh_key_length = 566;
+						else if(turn_params.dh_key_size == DH_2066)
+							dh_key_length = 2066;
+						https_print_uint(sb,(unsigned long)dh_key_length,"DH-key-length",0);
+					}
+				}
+
+				https_print_str(sb,turn_params.ca_cert_file,"Certificate Authority file",0);
+				https_print_str(sb,turn_params.cert_file,"Certificate file",0);
+				https_print_str(sb,turn_params.pkey_file,"Private Key file",0);
+
+				if(turn_params.shatype == SHATYPE_SHA256)
+					https_print_str(sb,"SHA256","SHA type",0);
+				else
+					https_print_str(sb,"SHA1","SHA type",0);
+
+				https_print_empty_row(sb,2);
+
+				https_print_str_array(sb,turn_params.listener.addrs,turn_params.listener.addrs_number,"Listener addr");
+
+				if(turn_params.listener_ifname[0])
+					https_print_str(sb,turn_params.listener_ifname,"listener-ifname",0);
+
+				https_print_flag(sb,turn_params.no_udp,"no-udp",0);
+				https_print_flag(sb,turn_params.no_tcp,"no-tcp",0);
+				https_print_flag(sb,turn_params.no_dtls,"no-dtls",0);
+				https_print_flag(sb,turn_params.no_tls,"no-tls",0);
+
+				https_print_flag(sb,(!turn_params.no_sslv3 && !turn_params.no_tls),"SSLv3",0);
+				https_print_flag(sb,(!turn_params.no_tlsv1 && !turn_params.no_tls),"TLSv1.0",0);
+				https_print_flag(sb,(!turn_params.no_tlsv1_1 && !turn_params.no_tls),"TLSv1.1",0);
+				https_print_flag(sb,(!turn_params.no_tlsv1_2 && !turn_params.no_tls),"TLSv1.2",0);
+
+				https_print_uint(sb,(unsigned long)turn_params.listener_port,"listener-port",0);
+				https_print_uint(sb,(unsigned long)turn_params.tls_listener_port,"tls-listener-port",0);
+				https_print_uint(sb,(unsigned long)turn_params.alt_listener_port,"alt-listener-port",0);
+				https_print_uint(sb,(unsigned long)turn_params.alt_tls_listener_port,"alt-tls-listener-port",0);
+
+				https_print_addr(sb,turn_params.external_ip,0,"External public IP");
+
+				https_print_empty_row(sb,2);
+
+				{
+					size_t an = https_print_addr_list(sb,&turn_params.aux_servers_list,1,"Aux server");
+					an += https_print_addr_list(sb,&turn_params.alternate_servers_list,1,"Alternate server");
+					an += https_print_addr_list(sb,&turn_params.tls_alternate_servers_list,1,"TLS alternate server");
+
+					if(an) {
+						https_print_empty_row(sb,2);
+					}
+				}
+
+				https_print_str_array(sb,turn_params.relay_addrs,turn_params.relays_number,"Relay addr");
+
+				if(turn_params.relay_ifname[0])
+					https_print_str(sb,turn_params.relay_ifname,"relay-ifname",0);
+
+				https_print_flag(sb,turn_params.server_relay,"server-relay",0);
+
+				https_print_flag(sb,turn_params.no_udp_relay,"no-udp-relay","no-udp-relay");
+				https_print_flag(sb,turn_params.no_tcp_relay,"no-tcp-relay","no-tcp-relay");
+
+				https_print_uint(sb,(unsigned long)turn_params.min_port,"min-port",0);
+				https_print_uint(sb,(unsigned long)turn_params.max_port,"max-port",0);
+
+				https_print_flag(sb,turn_params.no_multicast_peers,"no-multicast-peers","no-multicast-peers");
+				https_print_flag(sb,turn_params.no_loopback_peers,"no-loopback-peers","no-loopback-peers");
+
+				https_print_empty_row(sb,2);
+
+				if(turn_params.default_users_db.persistent_users_db.userdb[0]) {
+					switch(turn_params.default_users_db.userdb_type) {
+#if !defined(TURN_NO_SQLITE)
+					case TURN_USERDB_TYPE_SQLITE:
+						https_print_str(sb,"SQLite","DB type",0);
+						break;
+#endif
+#if !defined(TURN_NO_PQ)
+					case TURN_USERDB_TYPE_PQ:
+						https_print_str(sb,"Postgres","DB type",0);
+						break;
+#endif
+#if !defined(TURN_NO_MYSQL)
+					case TURN_USERDB_TYPE_MYSQL:
+						https_print_str(sb,"MySQL/MariaDB","DB type",0);
+						break;
+#endif
+#if !defined(TURN_NO_MONGO)
+					case TURN_USERDB_TYPE_MONGO:
+						https_print_str(sb,"MongoDB","DB type",0);
+						break;
+#endif
+#if !defined(TURN_NO_HIREDIS)
+					case TURN_USERDB_TYPE_REDIS:
+						https_print_str(sb,"redis","DB type",0);
+						break;
+#endif
+					default:
+						https_print_str(sb,"unknown","DB type",0);
+					};
+					if(is_superuser()) {
+						https_print_str(sb,turn_params.default_users_db.persistent_users_db.userdb,"DB",0);
+					}
+				} else {
+					https_print_str(sb,"none","DB type",0);
+					https_print_str(sb,"none","DB",0);
+				}
+
+#if !defined(TURN_NO_HIREDIS)
+				if(is_superuser()) {
+					if(turn_params.use_redis_statsdb && turn_params.redis_statsdb[0]) {
+						https_print_str(sb,turn_params.redis_statsdb,"Redis Statistics DB",0);
+					}
+				}
+#endif
+
+				https_print_empty_row(sb,2);
+
+				if(turn_params.ct == TURN_CREDENTIALS_LONG_TERM)
+					https_print_flag(sb,1,"Long-term authorization mechanism",0);
+				else
+					https_print_flag(sb,1,"Anonymous credentials",0);
+				https_print_flag(sb,turn_params.use_auth_secret_with_timestamp,"TURN REST API support",0);
+				if(turn_params.use_auth_secret_with_timestamp) {
+
+					if(!turn_params.rest_api_separator || ((unsigned int)turn_params.rest_api_separator == (unsigned int)':')) {
+						https_print_str(sb,":","TURN REST API separator",0);
+					} else {
+						https_print_uint(sb,turn_params.rest_api_separator,"TURN REST API separator ASCII number",0);
+					}
+				}
+
+				https_print_empty_row(sb,2);
+
+				if(is_superuser()) {
+					char * rn = get_realm(NULL)->options.name;
+					if(rn[0])
+						https_print_str(sb,rn,"Default realm",0);
+				}
+
+				realm_params_t *rp = get_realm(current_eff_realm());
+				if(!rp) rp = get_realm(NULL);
+
+				https_print_str(sb,rp->options.name,"Admin session (current) realm",0);
+
+				https_print_uint(sb,(unsigned long)rp->options.perf_options.total_quota,"current realm max number of sessions (total-quota)","cr-total-quota");
+				https_print_uint(sb,(unsigned long)rp->options.perf_options.user_quota,"current realm max sessions per user (user-quota)","cr-user-quota");
+				https_print_uint(sb,(unsigned long)rp->options.perf_options.max_bps,"current realm max-bps (per session)","cr-max-bps");
+
+				https_print_empty_row(sb,2);
+
+				https_print_uint(sb,(unsigned long)rp->status.total_current_allocs,"total-current-allocs",0);
+
+				https_print_empty_row(sb,2);
+
+				https_print_uint(sb,(unsigned long)turn_params.total_quota,"Default total-quota (per realm)","total-quota");
+				https_print_uint(sb,(unsigned long)turn_params.user_quota,"Default user-quota (per realm)","user-quota");
+				https_print_uint(sb,(unsigned long)get_bps_capacity(),"Total bps-capacity (per server)","bps-capacity");
+				https_print_uint(sb,(unsigned long)get_bps_capacity_allocated(),"Allocated bps-capacity (per server)",0);
+				https_print_uint(sb,(unsigned long)get_max_bps(),"Default max-bps (per session)","max-bps");
+
+				https_print_empty_row(sb,2);
+
+				https_print_ip_range_list(sb,&turn_params.ip_whitelist,"Whitelist IP (static)",NULL,0);
+				{
+					ip_range_list_t* l = get_ip_list("allowed");
+					https_print_ip_range_list(sb,l,"Whitelist IP (dynamic)","allowed",1);
+					ip_list_free(l);
+				}
+
+				https_print_empty_row(sb,2);
+
+				https_print_ip_range_list(sb,&turn_params.ip_blacklist,"Blacklist IP (static)", NULL, 0);
+				{
+					ip_range_list_t* l = get_ip_list("denied");
+					https_print_ip_range_list(sb,l,"Blacklist IP (dynamic)", "denied", 1);
+					ip_list_free(l);
+				}
+			}
+
+			str_buffer_append(sb,"\r\n</table>\r\n");
+
+			https_finish_page(sb,s,0);
+		}
+	}
+}
+
+struct https_ps_arg {
+	struct str_buffer* sb;
+	size_t counter;
+	turn_time_t ct;
+	const char* client_protocol;
+	const char* user_pattern;
+	size_t max_sessions;
+	turnsession_id cs;
+};
+
+static int https_print_session(ur_map_key_type key, ur_map_value_type value, void *arg)
+{
+	if(key && value && arg) {
+		struct https_ps_arg *csarg = (struct https_ps_arg*)arg;
+		struct str_buffer* sb = csarg->sb;
+		struct turn_session_info *tsi = (struct turn_session_info *)value;
+
+		if(current_eff_realm()[0] && strcmp(current_eff_realm(),tsi->realm))
+			return 0;
+
+		if(csarg->user_pattern[0]) {
+			if(!strstr((char*)tsi->username,csarg->user_pattern)) {
+				return 0;
+			}
+		}
+
+		if(csarg->cs == tsi->id) {
+			return 0;
+		}
+
+		{
+			const char *pn=csarg->client_protocol;
+			if(pn[0]) {
+				if(!strcmp(pn,"TLS") || !strcmp(pn,"tls") || !strcmp(pn,"Tls")) {
+					if(tsi->client_protocol != TLS_SOCKET)
+						return 0;
+					} else if(!strcmp(pn,"DTLS") || !strcmp(pn,"dtls") || !strcmp(pn,"Dtls")) {
+						if(tsi->client_protocol != DTLS_SOCKET)
+							return 0;
+					} else if(!strcmp(pn,"TCP") || !strcmp(pn,"tcp") || !strcmp(pn,"Tcp")) {
+						if(tsi->client_protocol != TCP_SOCKET)
+							return 0;
+					} else if(!strcmp(pn,"UDP") || !strcmp(pn,"udp") || !strcmp(pn,"Udp")) {
+						if(tsi->client_protocol != UDP_SOCKET)
+							return 0;
+					} else {
+						return 0;
+					}
+				}
+		}
+
+		if((unsigned long)csarg->counter<(unsigned long)csarg->max_sessions) {
+			str_buffer_append(sb,"<tr><td>");
+			str_buffer_append_sz(sb,(size_t)(csarg->counter+1));
+			str_buffer_append(sb,"</td><td>");
+			str_buffer_append_sid(sb,tsi->id);
+			str_buffer_append(sb,"<br><a href=\"");
+			str_buffer_append(sb,form_names[AS_FORM_PS].name);
+			str_buffer_append(sb,"?cs=");
+			str_buffer_append_sid(sb,tsi->id);
+			str_buffer_append(sb,"\">cancel</a>");
+			str_buffer_append(sb,"</td><td>");
+			str_buffer_append(sb,(char*)tsi->username);
+			str_buffer_append(sb,"</td><td>");
+			str_buffer_append(sb,tsi->realm);
+			str_buffer_append(sb,"</td><td>");
+			str_buffer_append(sb,tsi->origin);
+			str_buffer_append(sb,"</td><td>");
+			if(turn_time_before(csarg->ct, tsi->start_time)) {
+				str_buffer_append(sb,"undefined time\n");
+			} else {
+				str_buffer_append_sz(sb,(size_t)(csarg->ct - tsi->start_time));
+			}
+			str_buffer_append(sb,"</td><td>");
+			if(turn_time_before(tsi->expiration_time,csarg->ct)) {
+				str_buffer_append(sb,"expired");
+			} else {
+				str_buffer_append_sz(sb,(size_t)(tsi->expiration_time - csarg->ct));
+			}
+			str_buffer_append(sb,"</td><td>");
+			str_buffer_append(sb,pname(tsi->client_protocol));
+			str_buffer_append(sb,"</td><td>");
+			str_buffer_append(sb,pname(tsi->peer_protocol));
+			str_buffer_append(sb,"</td><td>");
+			{
+				if(!tsi->local_addr_data.saddr[0])
+					addr_to_string(&(tsi->local_addr_data.addr),(u08bits*)tsi->local_addr_data.saddr);
+				if(!tsi->remote_addr_data.saddr[0])
+					addr_to_string(&(tsi->remote_addr_data.addr),(u08bits*)tsi->remote_addr_data.saddr);
+				if(!tsi->relay_addr_data_ipv4.saddr[0])
+					addr_to_string(&(tsi->relay_addr_data_ipv4.addr),(u08bits*)tsi->relay_addr_data_ipv4.saddr);
+				if(!tsi->relay_addr_data_ipv6.saddr[0])
+					addr_to_string(&(tsi->relay_addr_data_ipv6.addr),(u08bits*)tsi->relay_addr_data_ipv6.saddr);
+				str_buffer_append(sb,tsi->remote_addr_data.saddr);
+				str_buffer_append(sb,"</td><td>");
+				str_buffer_append(sb,tsi->local_addr_data.saddr);
+				str_buffer_append(sb,"</td><td>");
+				str_buffer_append(sb,tsi->relay_addr_data_ipv4.saddr);
+				str_buffer_append(sb,"</td><td>");
+				str_buffer_append(sb,tsi->relay_addr_data_ipv6.saddr);
+				str_buffer_append(sb,"</td><td>");
+				str_buffer_append(sb,get_flag(tsi->enforce_fingerprints));
+				str_buffer_append(sb,"</td><td>");
+				str_buffer_append(sb,get_flag(tsi->is_mobile));
+				str_buffer_append(sb,"</td><td>");
+				str_buffer_append(sb,tsi->tls_method);
+				str_buffer_append(sb,"</td><td>");
+				str_buffer_append(sb,tsi->tls_cipher);
+				str_buffer_append(sb,"</td><td>");
+				str_buffer_append_sz(sb,(size_t)tsi->bps);
+				str_buffer_append(sb,"</td><td>");
+				{
+					char str[1025];
+					snprintf(str,sizeof(str)-1,"rp=%lu, rb=%lu, sp=%lu, sb=%lu\n",(unsigned long)(tsi->received_packets), (unsigned long)(tsi->received_bytes),(unsigned long)(tsi->sent_packets),(unsigned long)(tsi->sent_bytes));
+					str_buffer_append(sb,str);
+					str_buffer_append(sb,"</td><td>");
+				}
+				{
+					char str[1025];
+					snprintf(str,sizeof(str)-1,"r=%lu, s=%lu, total=%lu (bytes per sec)\n",(unsigned long)(tsi->received_rate), (unsigned long)(tsi->sent_rate),(unsigned long)(tsi->total_rate));
+					str_buffer_append(sb,str);
+					str_buffer_append(sb,"</td><td>");
+				}
+
+				if(tsi->main_peers_size) {
+					size_t i;
+					for(i=0;i<tsi->main_peers_size;++i) {
+						if(!(tsi->main_peers_data[i].saddr[0]))
+							addr_to_string(&(tsi->main_peers_data[i].addr),(u08bits*)tsi->main_peers_data[i].saddr);
+						str_buffer_append(sb," ");
+						str_buffer_append(sb,tsi->main_peers_data[i].saddr);
+						str_buffer_append(sb," ");
+					}
+					if(tsi->extra_peers_size && tsi->extra_peers_data) {
+						for(i=0;i<tsi->extra_peers_size;++i) {
+							if(!(tsi->extra_peers_data[i].saddr[0]))
+								addr_to_string(&(tsi->extra_peers_data[i].addr),(u08bits*)tsi->extra_peers_data[i].saddr);
+							str_buffer_append(sb," ");
+							str_buffer_append(sb,tsi->extra_peers_data[i].saddr);
+							str_buffer_append(sb," ");
+						}
+					}
+				}
+				str_buffer_append(sb,"</td>");
+			}
+		}
+
+		csarg->counter += 1;
+	}
+	return 0;
+}
+
+static size_t https_print_sessions(struct str_buffer* sb, const char* client_protocol, const char* user_pattern, size_t max_sessions, turnsession_id cs)
+{
+	struct https_ps_arg arg = {sb,0,0,client_protocol,user_pattern,max_sessions,cs};
+
+	arg.ct = turn_time();
+
+	ur_map_foreach_arg(adminserver.sessions, (foreachcb_arg_type)https_print_session, &arg);
+
+	return arg.counter;
+}
+
+static void write_ps_page(ioa_socket_handle s, const char* client_protocol, const char* user_pattern, size_t max_sessions, turnsession_id cs)
+{
+	if(s && !ioa_socket_tobeclosed(s)) {
+
+		if(!is_as_ok(s)) {
+			write_https_logon_page(s);
+		} else {
+
+			struct str_buffer* sb = str_buffer_new();
+
+			https_print_page_header(sb);
+
+			str_buffer_append(sb,"<form action=\"");
+			str_buffer_append(sb,form_names[AS_FORM_PS].name);
+			str_buffer_append(sb,"\" method=\"POST\">\r\n");
+			str_buffer_append(sb,"  <fieldset><legend>Filter:</legend>\r\n");
+
+			str_buffer_append(sb,"  <br>Realm name: <input type=\"text\" name=\"");
+			str_buffer_append(sb,HR_REALM);
+			str_buffer_append(sb,"\" value=\"");
+			str_buffer_append(sb,current_eff_realm());
+			str_buffer_append(sb,"\"");
+			if(!is_superuser()) {
+				str_buffer_append(sb," disabled ");
+			}
+			str_buffer_append(sb,">");
+
+			str_buffer_append(sb,"  Client protocol: <input type=\"text\" name=\"");
+			str_buffer_append(sb,HR_CLIENT_PROTOCOL);
+			str_buffer_append(sb,"\" value=\"");
+			str_buffer_append(sb,client_protocol);
+			str_buffer_append(sb,"\"");
+			str_buffer_append(sb,">");
+
+			str_buffer_append(sb,"  User name contains: <input type=\"text\" name=\"");
+			str_buffer_append(sb,HR_USER_PATTERN);
+			str_buffer_append(sb,"\" value=\"");
+			str_buffer_append(sb,user_pattern);
+			str_buffer_append(sb,"\"");
+			str_buffer_append(sb,"><br><br>");
+
+			str_buffer_append(sb,"  Max number of output sessions in the page: <input type=\"text\" name=\"");
+			str_buffer_append(sb,HR_MAX_SESSIONS);
+			str_buffer_append(sb,"\" value=\"");
+			str_buffer_append_sz(sb,max_sessions);
+			str_buffer_append(sb,"\"");
+			str_buffer_append(sb,"><br>");
+
+			str_buffer_append(sb,"<br><input type=\"submit\" value=\"Filter\">");
+
+			str_buffer_append(sb,"</fieldset>\r\n");
+			str_buffer_append(sb,"</form>\r\n");
+
+			str_buffer_append(sb,"<br><b>TURN Sessions:</b><br><br><table>\r\n");
+			str_buffer_append(sb,"<tr><th>N</th><th>Session ID</th><th>User</th><th>Realm</th><th>Origin</th><th>Age, secs</th><th>Expires, secs</th><th>Client protocol</th><th>Relay protocol</th><th>Client addr</th><th>Server addr</th><th>Relay addr (IPv4)</th><th>Relay addr (IPv6)</th><th>Fingerprints</th><th>Mobile</th><th>TLS method</th><th>TLS cipher</th><th>BPS (allocated)</th><th>Packets</th><th>Rate</th><th>Peers</th></tr>\r\n");
+
+			size_t total_sz = https_print_sessions(sb,client_protocol,user_pattern,max_sessions,cs);
+
+			str_buffer_append(sb,"\r\n</table>\r\n");
+
+			str_buffer_append(sb,"<br>Total sessions = ");
+			str_buffer_append_sz(sb,total_sz);
+			str_buffer_append(sb,"<br>\r\n");
+
+			https_finish_page(sb,s,0);
+		}
+	}
+}
+
+static size_t https_print_users(struct str_buffer* sb)
+{
+	size_t ret = 0;
+	const turn_dbdriver_t * dbd = get_dbdriver();
+	if (dbd && dbd->list_users) {
+		secrets_list_t users,realms;
+		init_secrets_list(&users);
+		init_secrets_list(&realms);
+		dbd->list_users((u08bits*)current_eff_realm(),&users,&realms);
+
+		size_t sz = get_secrets_list_size(&users);
+		size_t i;
+		for(i=0;i<sz;++i) {
+			str_buffer_append(sb,"<tr><td>");
+			str_buffer_append_sz(sb,i+1);
+			str_buffer_append(sb,"</td>");
+			str_buffer_append(sb,"<td>");
+			str_buffer_append(sb,get_secrets_list_elem(&users,i));
+			str_buffer_append(sb,"</td>");
+			if(!current_eff_realm()[0]) {
+				str_buffer_append(sb,"<td>");
+				str_buffer_append(sb,get_secrets_list_elem(&realms,i));
+				str_buffer_append(sb,"</td>");
+			}
+			str_buffer_append(sb,"<td> <a href=\"");
+			str_buffer_append(sb,form_names[AS_FORM_USERS].name);
+			str_buffer_append(sb,"?");
+			str_buffer_append(sb,HR_DELETE_USER);
+			str_buffer_append(sb,"=");
+			str_buffer_append(sb,get_secrets_list_elem(&users,i));
+			str_buffer_append(sb,"&");
+			str_buffer_append(sb,HR_DELETE_REALM);
+			str_buffer_append(sb,"=");
+			str_buffer_append(sb,get_secrets_list_elem(&realms,i));
+			str_buffer_append(sb,"\">delete</a>");
+			str_buffer_append(sb,"</td>");
+			str_buffer_append(sb,"</tr>");
+			++ret;
+		}
+
+		clean_secrets_list(&users);
+		clean_secrets_list(&realms);
+	}
+
+	return ret;
+}
+
+static void write_users_page(ioa_socket_handle s, const u08bits *add_user, const u08bits *add_realm, const char* msg)
+{
+	if(s && !ioa_socket_tobeclosed(s)) {
+
+		if(!is_as_ok(s)) {
+			write_https_logon_page(s);
+		} else {
+
+			struct str_buffer* sb = str_buffer_new();
+
+			https_print_page_header(sb);
+
+			str_buffer_append(sb,"<form action=\"");
+			str_buffer_append(sb,form_names[AS_FORM_USERS].name);
+			str_buffer_append(sb,"\" method=\"POST\">\r\n");
+			str_buffer_append(sb,"  <fieldset><legend>Filter:</legend>\r\n");
+
+			str_buffer_append(sb,"  <br>Realm name: <input type=\"text\" name=\"");
+			str_buffer_append(sb,HR_REALM);
+			str_buffer_append(sb,"\" value=\"");
+			str_buffer_append(sb,current_eff_realm());
+			str_buffer_append(sb,"\"");
+			if(!is_superuser()) {
+				str_buffer_append(sb," disabled ");
+			}
+			str_buffer_append(sb,">");
+
+			str_buffer_append(sb,"<br><input type=\"submit\" value=\"Filter\">");
+
+			str_buffer_append(sb,"</fieldset>\r\n");
+			str_buffer_append(sb,"</form>\r\n");
+
+			str_buffer_append(sb,"<form action=\"");
+			str_buffer_append(sb,form_names[AS_FORM_USERS].name);
+			str_buffer_append(sb,"\" method=\"POST\">\r\n");
+			str_buffer_append(sb,"  <fieldset><legend>User:</legend>\r\n");
+
+			if(msg && msg[0]) {
+				str_buffer_append(sb,"<br><table id=\"msg\"><th>");
+				str_buffer_append(sb,msg);
+				str_buffer_append(sb,"</th></table><br>");
+			}
+
+			str_buffer_append(sb,"  <br>Realm name: <input type=\"text\" name=\"");
+			str_buffer_append(sb,HR_ADD_REALM);
+			str_buffer_append(sb,"\" value=\"");
+			str_buffer_append(sb,(const char*)add_realm);
+			str_buffer_append(sb,"\"");
+			if(!is_superuser()) {
+				str_buffer_append(sb," disabled ");
+			}
+			str_buffer_append(sb,"><br>\r\n");
+
+			str_buffer_append(sb,"  <br>User name: <input required type=\"text\" name=\"");
+			str_buffer_append(sb,HR_ADD_USER);
+			str_buffer_append(sb,"\" value=\"");
+			str_buffer_append(sb,(const char*)add_user);
+			str_buffer_append(sb,"\"");
+			str_buffer_append(sb,"><br>\r\n");
+
+			str_buffer_append(sb,"  <br>Password: <input required type=\"password\" name=\"");
+			str_buffer_append(sb,HR_PASSWORD);
+			str_buffer_append(sb,"\" value=\"");
+			str_buffer_append(sb,"");
+			str_buffer_append(sb,"\"");
+			str_buffer_append(sb,"><br>\r\n");
+
+			str_buffer_append(sb,"  <br>Confirm password: <input required type=\"password\" name=\"");
+			str_buffer_append(sb,HR_PASSWORD1);
+			str_buffer_append(sb,"\" value=\"");
+			str_buffer_append(sb,"");
+			str_buffer_append(sb,"\"");
+			str_buffer_append(sb,"><br><br>\r\n");
+
+			if(turn_params.shatype == SHATYPE_SHA256)
+				str_buffer_append(sb,"SHA type: SHA256<br>\r\n");
+			else
+				str_buffer_append(sb,"SHA type: SHA1<br>\r\n");
+
+			str_buffer_append(sb,"<br><input type=\"submit\" value=\"Add user\">");
+
+			str_buffer_append(sb,"</fieldset>\r\n");
+			str_buffer_append(sb,"</form>\r\n");
+
+			str_buffer_append(sb,"<br><b>Users:</b><br><br>\r\n");
+			str_buffer_append(sb,"<table>\r\n");
+			str_buffer_append(sb,"<tr><th>N</th><th>Name</th>");
+			if(!current_eff_realm()[0]) {
+				str_buffer_append(sb,"<th>Realm</th>");
+			}
+			str_buffer_append(sb,"<th> </th>");
+			str_buffer_append(sb,"</tr>\r\n");
+
+			size_t total_sz = https_print_users(sb);
+
+			str_buffer_append(sb,"\r\n</table>\r\n");
+
+			str_buffer_append(sb,"<br>Total users = ");
+			str_buffer_append_sz(sb,total_sz);
+			str_buffer_append(sb,"<br>\r\n");
+
+			https_finish_page(sb,s,0);
+		}
+	}
+}
+
+static size_t https_print_secrets(struct str_buffer* sb)
+{
+	size_t ret = 0;
+	const turn_dbdriver_t * dbd = get_dbdriver();
+	if (dbd && dbd->list_secrets) {
+		secrets_list_t secrets,realms;
+		init_secrets_list(&secrets);
+		init_secrets_list(&realms);
+		dbd->list_secrets((u08bits*)current_eff_realm(),&secrets,&realms);
+
+		size_t sz = get_secrets_list_size(&secrets);
+		size_t i;
+		for(i=0;i<sz;++i) {
+			str_buffer_append(sb,"<tr><td>");
+			str_buffer_append_sz(sb,i+1);
+			str_buffer_append(sb,"</td>");
+			str_buffer_append(sb,"<td>");
+			str_buffer_append(sb,get_secrets_list_elem(&secrets,i));
+			str_buffer_append(sb,"</td>");
+			if(!current_eff_realm()[0]) {
+				str_buffer_append(sb,"<td>");
+				str_buffer_append(sb,get_secrets_list_elem(&realms,i));
+				str_buffer_append(sb,"</td>");
+			}
+			str_buffer_append(sb,"<td> <a href=\"");
+			str_buffer_append(sb,form_names[AS_FORM_SS].name);
+			str_buffer_append(sb,"?");
+			str_buffer_append(sb,HR_DELETE_SECRET);
+			str_buffer_append(sb,"=");
+			str_buffer_append(sb,get_secrets_list_elem(&secrets,i));
+			str_buffer_append(sb,"&");
+			str_buffer_append(sb,HR_DELETE_REALM);
+			str_buffer_append(sb,"=");
+			str_buffer_append(sb,get_secrets_list_elem(&realms,i));
+			str_buffer_append(sb,"\">delete</a>");
+			str_buffer_append(sb,"</td>");
+			str_buffer_append(sb,"</tr>");
+			++ret;
+		}
+
+		clean_secrets_list(&secrets);
+		clean_secrets_list(&realms);
+	}
+
+	return ret;
+}
+
+static void write_shared_secrets_page(ioa_socket_handle s, const char* add_secret, const char* add_realm, const char* msg)
+{
+	if(s && !ioa_socket_tobeclosed(s)) {
+
+		if(!is_as_ok(s)) {
+			write_https_logon_page(s);
+		} else {
+
+			struct str_buffer* sb = str_buffer_new();
+
+			https_print_page_header(sb);
+
+			str_buffer_append(sb,"<form action=\"");
+			str_buffer_append(sb,form_names[AS_FORM_SS].name);
+			str_buffer_append(sb,"\" method=\"POST\">\r\n");
+			str_buffer_append(sb,"  <fieldset><legend>Filter:</legend>\r\n");
+
+			str_buffer_append(sb,"  <br>Realm name: <input type=\"text\" name=\"");
+			str_buffer_append(sb,HR_REALM);
+			str_buffer_append(sb,"\" value=\"");
+			str_buffer_append(sb,current_eff_realm());
+			str_buffer_append(sb,"\"");
+			if(!is_superuser()) {
+				str_buffer_append(sb," disabled ");
+			}
+			str_buffer_append(sb,">");
+
+			str_buffer_append(sb,"<br><input type=\"submit\" value=\"Filter\">");
+
+			str_buffer_append(sb,"</fieldset>\r\n");
+			str_buffer_append(sb,"</form>\r\n");
+
+			str_buffer_append(sb,"<form action=\"");
+			str_buffer_append(sb,form_names[AS_FORM_SS].name);
+			str_buffer_append(sb,"\" method=\"POST\">\r\n");
+			str_buffer_append(sb,"  <fieldset><legend>Secret:</legend>\r\n");
+
+			if(msg && msg[0]) {
+				str_buffer_append(sb,"<br><table id=\"msg\"><th>");
+				str_buffer_append(sb,msg);
+				str_buffer_append(sb,"</th></table><br>");
+			}
+
+			str_buffer_append(sb,"  <br>Realm name: <input type=\"text\" name=\"");
+			str_buffer_append(sb,HR_ADD_REALM);
+			str_buffer_append(sb,"\" value=\"");
+			str_buffer_append(sb,(const char*)add_realm);
+			str_buffer_append(sb,"\"");
+			if(!is_superuser()) {
+				str_buffer_append(sb," disabled ");
+			}
+			str_buffer_append(sb,"><br>\r\n");
+
+			str_buffer_append(sb,"  <br>Secret: <input required type=\"text\" name=\"");
+			str_buffer_append(sb,HR_ADD_SECRET);
+			str_buffer_append(sb,"\" value=\"");
+			str_buffer_append(sb,(const char*)add_secret);
+			str_buffer_append(sb,"\"");
+			str_buffer_append(sb,"><br>\r\n");
+
+			str_buffer_append(sb,"<br><input type=\"submit\" value=\"Add secret\">");
+
+			str_buffer_append(sb,"</fieldset>\r\n");
+			str_buffer_append(sb,"</form>\r\n");
+
+			str_buffer_append(sb,"<br><b>Shared secrets:</b><br><br>\r\n");
+			str_buffer_append(sb,"<table>\r\n");
+			str_buffer_append(sb,"<tr><th>N</th><th>Value</th>");
+			if(!current_eff_realm()[0]) {
+				str_buffer_append(sb,"<th>Realm</th>");
+			}
+			str_buffer_append(sb,"<th> </th>");
+			str_buffer_append(sb,"</tr>\r\n");
+
+			size_t total_sz = https_print_secrets(sb);
+
+			str_buffer_append(sb,"\r\n</table>\r\n");
+
+			str_buffer_append(sb,"<br>Total secrets = ");
+			str_buffer_append_sz(sb,total_sz);
+			str_buffer_append(sb,"<br>\r\n");
+
+			https_finish_page(sb,s,0);
+		}
+	}
+}
+
+static size_t https_print_origins(struct str_buffer* sb)
+{
+	size_t ret = 0;
+	const turn_dbdriver_t * dbd = get_dbdriver();
+	if (dbd && dbd->list_origins) {
+		secrets_list_t origins,realms;
+		init_secrets_list(&origins);
+		init_secrets_list(&realms);
+		dbd->list_origins((u08bits*)current_eff_realm(),&origins,&realms);
+
+		size_t sz = get_secrets_list_size(&origins);
+		size_t i;
+		for(i=0;i<sz;++i) {
+			str_buffer_append(sb,"<tr><td>");
+			str_buffer_append_sz(sb,i+1);
+			str_buffer_append(sb,"</td>");
+			str_buffer_append(sb,"<td>");
+			str_buffer_append(sb,get_secrets_list_elem(&origins,i));
+			str_buffer_append(sb,"</td>");
+			if(!current_eff_realm()[0]) {
+				str_buffer_append(sb,"<td>");
+				str_buffer_append(sb,get_secrets_list_elem(&realms,i));
+				str_buffer_append(sb,"</td>");
+			}
+			if(is_superuser()) {
+				str_buffer_append(sb,"<td> <a href=\"");
+				str_buffer_append(sb,form_names[AS_FORM_OS].name);
+				str_buffer_append(sb,"?");
+				str_buffer_append(sb,HR_DELETE_ORIGIN);
+				str_buffer_append(sb,"=");
+				str_buffer_append(sb,get_secrets_list_elem(&origins,i));
+				str_buffer_append(sb,"\">delete</a>");
+				str_buffer_append(sb,"</td>");
+			}
+			str_buffer_append(sb,"</tr>");
+			++ret;
+		}
+
+		clean_secrets_list(&origins);
+		clean_secrets_list(&realms);
+	}
+
+	return ret;
+}
+
+static void write_origins_page(ioa_socket_handle s, const char* add_origin, const char* add_realm, const char* msg)
+{
+	if(s && !ioa_socket_tobeclosed(s)) {
+
+		if(!is_as_ok(s)) {
+			write_https_logon_page(s);
+		} else {
+
+			struct str_buffer* sb = str_buffer_new();
+
+			https_print_page_header(sb);
+
+			str_buffer_append(sb,"<form action=\"");
+			str_buffer_append(sb,form_names[AS_FORM_OS].name);
+			str_buffer_append(sb,"\" method=\"POST\">\r\n");
+			str_buffer_append(sb,"  <fieldset><legend>Filter:</legend>\r\n");
+
+			str_buffer_append(sb,"  <br>Realm name: <input type=\"text\" name=\"");
+			str_buffer_append(sb,HR_REALM);
+			str_buffer_append(sb,"\" value=\"");
+			str_buffer_append(sb,current_eff_realm());
+			str_buffer_append(sb,"\"");
+			if(!is_superuser()) {
+				str_buffer_append(sb," disabled ");
+			}
+			str_buffer_append(sb,">");
+
+			str_buffer_append(sb,"<br><input type=\"submit\" value=\"Filter\">");
+
+			str_buffer_append(sb,"</fieldset>\r\n");
+			str_buffer_append(sb,"</form>\r\n");
+
+			if(is_superuser()) {
+				str_buffer_append(sb,"<form action=\"");
+				str_buffer_append(sb,form_names[AS_FORM_OS].name);
+				str_buffer_append(sb,"\" method=\"POST\">\r\n");
+				str_buffer_append(sb,"  <fieldset><legend>Origin:</legend>\r\n");
+
+				if(msg && msg[0]) {
+					str_buffer_append(sb,"<br><table id=\"msg\"><th>");
+					str_buffer_append(sb,msg);
+					str_buffer_append(sb,"</th></table><br>");
+				}
+
+				str_buffer_append(sb,"  <br>Realm name: <input required type=\"text\" name=\"");
+				str_buffer_append(sb,HR_ADD_REALM);
+				str_buffer_append(sb,"\" value=\"");
+				str_buffer_append(sb,(const char*)add_realm);
+				str_buffer_append(sb,"\"");
+				str_buffer_append(sb,"><br>\r\n");
+
+				str_buffer_append(sb,"  <br>Origin: <input required type=\"text\" name=\"");
+				str_buffer_append(sb,HR_ADD_ORIGIN);
+				str_buffer_append(sb,"\" value=\"");
+				str_buffer_append(sb,(const char*)add_origin);
+				str_buffer_append(sb,"\"");
+				str_buffer_append(sb,"><br>\r\n");
+
+				str_buffer_append(sb,"<br><input type=\"submit\" value=\"Add origin\">");
+
+				str_buffer_append(sb,"</fieldset>\r\n");
+				str_buffer_append(sb,"</form>\r\n");
+			}
+
+			str_buffer_append(sb,"<br><b>Origins:</b><br><br>\r\n");
+			str_buffer_append(sb,"<table>\r\n");
+			str_buffer_append(sb,"<tr><th>N</th><th>Value</th>");
+			if(!current_eff_realm()[0]) {
+				str_buffer_append(sb,"<th>Realm</th>");
+			}
+			if(is_superuser()) {
+				str_buffer_append(sb,"<th> </th>");
+			}
+			str_buffer_append(sb,"</tr>\r\n");
+
+			size_t total_sz = https_print_origins(sb);
+
+			str_buffer_append(sb,"\r\n</table>\r\n");
+
+			str_buffer_append(sb,"<br>Total origins = ");
+			str_buffer_append_sz(sb,total_sz);
+			str_buffer_append(sb,"<br>\r\n");
+
+			https_finish_page(sb,s,0);
+		}
+	}
+}
+
+static size_t https_print_oauth_keys(struct str_buffer* sb)
+{
+	size_t ret = 0;
+	const turn_dbdriver_t * dbd = get_dbdriver();
+	if (dbd && dbd->list_oauth_keys) {
+		secrets_list_t kids,hkdfs,teas,aas,tss,lts;
+		init_secrets_list(&kids);
+		init_secrets_list(&hkdfs);
+		init_secrets_list(&teas);
+		init_secrets_list(&aas);
+		init_secrets_list(&tss);
+		init_secrets_list(&lts);
+		dbd->list_oauth_keys(&kids,&hkdfs,&teas,&aas,&tss,&lts);
+
+		size_t sz = get_secrets_list_size(&kids);
+		size_t i;
+		for(i=0;i<sz;++i) {
+			str_buffer_append(sb,"<tr><td>");
+			str_buffer_append_sz(sb,i+1);
+			str_buffer_append(sb,"</td>");
+			str_buffer_append(sb,"<td>");
+			str_buffer_append(sb,get_secrets_list_elem(&kids,i));
+			str_buffer_append(sb,"</td>");
+
+			str_buffer_append(sb,"<td><a href=\"");
+			str_buffer_append(sb,form_names[AS_FORM_OAUTH_SHOW_KEYS].name);
+			str_buffer_append(sb,"?");
+			str_buffer_append(sb,HR_OAUTH_KID);
+			str_buffer_append(sb,"=");
+			str_buffer_append(sb,get_secrets_list_elem(&kids,i));
+			str_buffer_append(sb,"\"> show </a></td>");
+
+			str_buffer_append(sb,"<td>");
+			str_buffer_append(sb,get_secrets_list_elem(&tss,i));
+			str_buffer_append(sb,"</td>");
+			str_buffer_append(sb,"<td>");
+			str_buffer_append(sb,get_secrets_list_elem(&lts,i));
+			str_buffer_append(sb,"</td>");
+			str_buffer_append(sb,"<td>");
+			str_buffer_append(sb,get_secrets_list_elem(&hkdfs,i));
+			str_buffer_append(sb,"</td>");
+			str_buffer_append(sb,"<td>");
+			str_buffer_append(sb,get_secrets_list_elem(&teas,i));
+			str_buffer_append(sb,"</td>");
+			str_buffer_append(sb,"<td>");
+			str_buffer_append(sb,get_secrets_list_elem(&aas,i));
+			str_buffer_append(sb,"</td>");
+
+			{
+				str_buffer_append(sb,"<td> <a href=\"");
+				str_buffer_append(sb,form_names[AS_FORM_OAUTH].name);
+				str_buffer_append(sb,"?");
+				str_buffer_append(sb,HR_DELETE_OAUTH_KID);
+				str_buffer_append(sb,"=");
+				str_buffer_append(sb,get_secrets_list_elem(&kids,i));
+				str_buffer_append(sb,"\">delete</a>");
+				str_buffer_append(sb,"</td>");
+			}
+			str_buffer_append(sb,"</tr>");
+			++ret;
+		}
+
+		clean_secrets_list(&kids);
+		clean_secrets_list(&hkdfs);
+		clean_secrets_list(&teas);
+		clean_secrets_list(&aas);
+	}
+
+	return ret;
+}
+
+static void write_https_oauth_show_keys(ioa_socket_handle s, const char* kid)
+{
+	if(s && !ioa_socket_tobeclosed(s)) {
+
+		if(!is_as_ok(s)) {
+			write_https_logon_page(s);
+		} else if(!is_superuser()) {
+			write_https_home_page(s);
+		} else {
+
+			struct str_buffer* sb = str_buffer_new();
+
+			https_print_page_header(sb);
+
+			str_buffer_append(sb,"<a href=\"");
+			str_buffer_append(sb,form_names[AS_FORM_OAUTH].name);
+			str_buffer_append(sb,"\">back to oauth list</a><br><br>\r\n");
+
+			if(kid && kid[0]) {
+				const turn_dbdriver_t * dbd = get_dbdriver();
+				if (dbd && dbd->get_oauth_key) {
+					oauth_key_data_raw key;
+					if((*dbd->get_oauth_key)((const u08bits*)kid,&key)<0) {
+						str_buffer_append(sb,"data retrieval error");
+					} else {
+
+						oauth_key_data okd;
+						ns_bzero(&okd,sizeof(okd));
+
+						convert_oauth_key_data_raw(&key, &okd);
+
+						char err_msg[1025] = "\0";
+						size_t err_msg_size = sizeof(err_msg) - 1;
+
+						oauth_key okey;
+						ns_bzero(&okey,sizeof(okey));
+
+						if (convert_oauth_key_data(&okd, &okey, err_msg, err_msg_size) < 0) {
+							str_buffer_append(sb,err_msg);
+						} else {
+
+							str_buffer_append(sb,"<table>\r\n");
+
+							if(key.ikm_key[0]) {
+								str_buffer_append(sb,"<tr><td>Input Keying Material:</td><td>");
+								str_buffer_append(sb,key.ikm_key);
+								str_buffer_append(sb,"</td></tr>\r\n");
+							}
+
+							if(okey.as_rs_key_size) {
+								size_t as_rs_key_size = 0;
+								char *as_rs_key = (char*)base64_encode((unsigned char*)okey.as_rs_key,okey.as_rs_key_size,&as_rs_key_size);
+								if(as_rs_key) {
+									str_buffer_append(sb,"<tr><td>AS-RS key:</td><td>");
+									str_buffer_append(sb,as_rs_key);
+									str_buffer_append(sb,"</td></tr>\r\n");
+									turn_free(as_rs_key,as_rs_key_size);
+								}
+							}
+
+							if(okey.auth_key_size) {
+								size_t auth_key_size = 0;
+								char *auth_key = (char*)base64_encode((unsigned char*)okey.auth_key,okey.auth_key_size,&auth_key_size);
+								if(auth_key) {
+									str_buffer_append(sb,"<tr><td>AUTH key:</td><td>");
+									str_buffer_append(sb,auth_key);
+									str_buffer_append(sb,"</td></tr>\r\n");
+									turn_free(auth_key,auth_key_size);
+								}
+							}
+
+							str_buffer_append(sb,"</table>\r\n");
+						}
+					}
+				}
+			}
+
+			https_finish_page(sb,s,0);
+		}
+	}
+}
+
+static void write_https_oauth_page(ioa_socket_handle s, const char* add_kid, const char* add_ikm,
+				const char* add_hkdf_hash_func, const char* add_tea, const char* add_aa,
+				const char *add_ts, const char* add_lt,
+				const char *add_rs_key, const char *add_auth_key,
+				const char* msg)
+{
+	if(s && !ioa_socket_tobeclosed(s)) {
+
+		if(!is_as_ok(s)) {
+			write_https_logon_page(s);
+		} else if(!is_superuser()) {
+			write_https_home_page(s);
+		} else {
+
+			struct str_buffer* sb = str_buffer_new();
+
+			https_print_page_header(sb);
+
+			{
+				str_buffer_append(sb,"<form action=\"");
+				str_buffer_append(sb,form_names[AS_FORM_OAUTH].name);
+				str_buffer_append(sb,"\" method=\"POST\">\r\n");
+				str_buffer_append(sb,"  <fieldset><legend>oAuth key:</legend>\r\n");
+
+				if(msg && msg[0]) {
+					str_buffer_append(sb,"<br><table id=\"msg\"><th>");
+					str_buffer_append(sb,msg);
+					str_buffer_append(sb,"</th></table><br>");
+				}
+
+				str_buffer_append(sb,"<table><tr><td>");
+
+				{
+					if(!add_kid) add_kid="";
+
+					str_buffer_append(sb,"  <br>KID (required): <input required type=\"text\" name=\"");
+					str_buffer_append(sb,HR_ADD_OAUTH_KID);
+					str_buffer_append(sb,"\" value=\"");
+					str_buffer_append(sb,(const char*)add_kid);
+					str_buffer_append(sb,"\"><br>\r\n");
+				}
+
+				str_buffer_append(sb,"</td><td>");
+
+				{
+					if(!add_ts) add_ts="";
+
+					str_buffer_append(sb,"  <br>Timestamp, secs (optional): <input type=\"number\" min=\"0\" name=\"");
+					str_buffer_append(sb,HR_ADD_OAUTH_TS);
+					str_buffer_append(sb,"\" value=\"");
+					str_buffer_append(sb,(const char*)add_ts);
+					str_buffer_append(sb,"\"><br>\r\n");
+				}
+
+				str_buffer_append(sb,"</td><td>");
+
+				{
+					if(!add_lt) add_lt="";
+
+					str_buffer_append(sb,"  <br>Lifetime, secs (optional): <input type=\"number\" min=\"0\" name=\"");
+					str_buffer_append(sb,HR_ADD_OAUTH_LT);
+					str_buffer_append(sb,"\" value=\"");
+					str_buffer_append(sb,(const char*)add_lt);
+					str_buffer_append(sb,"\"><br>\r\n");
+				}
+
+				str_buffer_append(sb,"</td></tr><tr><td>");
+
+				{
+					str_buffer_append(sb,"<br>Hash key derivation function (optional):<br>\r\n");
+
+					if(!add_hkdf_hash_func || !add_hkdf_hash_func[0])
+						add_hkdf_hash_func = "SHA-256";
+
+					str_buffer_append(sb,"<input type=\"radio\" name=\"");
+					str_buffer_append(sb,HR_ADD_OAUTH_HKDF);
+					str_buffer_append(sb,"\" value=\"SHA-1\" ");
+					if(!strcmp("SHA-1",add_hkdf_hash_func)) {
+						str_buffer_append(sb," checked ");
+					}
+					str_buffer_append(sb,">SHA-1\r\n<br>\r\n");
+
+					str_buffer_append(sb,"<input type=\"radio\" name=\"");
+					str_buffer_append(sb,HR_ADD_OAUTH_HKDF);
+					str_buffer_append(sb,"\" value=\"SHA-256\" ");
+					if(strcmp("SHA-1",add_hkdf_hash_func)) {
+						str_buffer_append(sb," checked ");
+					}
+					str_buffer_append(sb,">SHA-256\r\n<br>\r\n");
+				}
+
+				str_buffer_append(sb,"</td><td colspan=\"2\">");
+
+				{
+					if(!add_ikm) add_ikm = "";
+
+					str_buffer_append(sb,"  <br>Base64-encoded input keying material (optional):<br><textarea wrap=\"soft\" cols=70 rows=4 name=\"");
+					str_buffer_append(sb,HR_ADD_OAUTH_IKM);
+					str_buffer_append(sb,"\" maxLength=256 >");
+					str_buffer_append(sb,(const char*)add_ikm);
+					str_buffer_append(sb,"</textarea>");
+					str_buffer_append(sb,"<br>\r\n");
+				}
+
+				str_buffer_append(sb,"</td></tr>\r\n<tr><td>");
+
+				{
+					str_buffer_append(sb,"<br>Token encryption algorithm (required):<br>\r\n");
+
+					if(!add_tea || !add_tea[0])
+						add_tea = "AES-256-CBC";
+
+					str_buffer_append(sb,"<input type=\"radio\" name=\"");
+					str_buffer_append(sb,HR_ADD_OAUTH_TEA);
+					str_buffer_append(sb,"\" value=\"AES-128-CBC\" ");
+					if(!strcmp("AES-128-CBC",add_tea)) {
+						str_buffer_append(sb," checked ");
+					}
+					str_buffer_append(sb,">AES-128-CBC\r\n<br>\r\n");
+
+					str_buffer_append(sb,"<input type=\"radio\" name=\"");
+					str_buffer_append(sb,HR_ADD_OAUTH_TEA);
+					str_buffer_append(sb,"\" value=\"AES-256-CBC\" ");
+					if(!strcmp("AES-256-CBC",add_tea)) {
+						str_buffer_append(sb," checked ");
+					}
+					str_buffer_append(sb,">AES-256-CBC\r\n<br>\r\n");
+
+					str_buffer_append(sb,"<input type=\"radio\" name=\"");
+					str_buffer_append(sb,HR_ADD_OAUTH_TEA);
+					str_buffer_append(sb,"\" value=\"AEAD-AES-128-GCM\" ");
+					if(!strcmp("AEAD-AES-128-GCM",add_tea)) {
+						str_buffer_append(sb," checked ");
+					}
+					str_buffer_append(sb,">AEAD-AES-128-GCM\r\n<br>\r\n");
+
+					str_buffer_append(sb,"<input type=\"radio\" name=\"");
+					str_buffer_append(sb,HR_ADD_OAUTH_TEA);
+					str_buffer_append(sb,"\" value=\"AEAD-AES-256-GCM\" ");
+					if(!strcmp("AEAD-AES-256-GCM",add_tea)) {
+						str_buffer_append(sb," checked ");
+					}
+					str_buffer_append(sb,">AEAD-AES-256-GCM\r\n<br>\r\n");
+				}
+
+				str_buffer_append(sb,"</td><td colspan=\"2\">");
+
+				{
+					if(!add_rs_key) add_rs_key = "";
+
+					str_buffer_append(sb,"  <br>Base64-encoded AS-RS key (optional):<br><textarea wrap=\"soft\" cols=70 rows=4 name=\"");
+					str_buffer_append(sb,HR_ADD_OAUTH_RS_KEY);
+					str_buffer_append(sb,"\" maxLength=256 >");
+					str_buffer_append(sb,(const char*)add_rs_key);
+					str_buffer_append(sb,"</textarea>");
+					str_buffer_append(sb,"<br>\r\n");
+				}
+
+				str_buffer_append(sb,"</td></tr>\r\n<tr><td>");
+
+				{
+					str_buffer_append(sb,"<br>Token authentication algorithm (required if no AEAD used):<br>\r\n");
+
+					if(!add_aa || !add_aa[0])
+						add_aa = "HMAC-SHA-256-128";
+
+					str_buffer_append(sb,"<input type=\"radio\" name=\"");
+					str_buffer_append(sb,HR_ADD_OAUTH_AA);
+					str_buffer_append(sb,"\" value=\"HMAC-SHA-256-128\" ");
+					if(!strcmp("HMAC-SHA-256-128",add_aa)) {
+						str_buffer_append(sb," checked ");
+					}
+					str_buffer_append(sb,">HMAC-SHA-256-128\r\n<br>\r\n");
+
+					str_buffer_append(sb,"<input type=\"radio\" name=\"");
+					str_buffer_append(sb,HR_ADD_OAUTH_AA);
+					str_buffer_append(sb,"\" value=\"HMAC-SHA-256\" ");
+					if(!strcmp("HMAC-SHA-256",add_aa)) {
+						str_buffer_append(sb," checked ");
+					}
+					str_buffer_append(sb,">HMAC-SHA-256\r\n<br>\r\n");
+
+					str_buffer_append(sb,"<input type=\"radio\" name=\"");
+					str_buffer_append(sb,HR_ADD_OAUTH_AA);
+					str_buffer_append(sb,"\" value=\"HMAC-SHA-1\" ");
+					if(!strcmp("HMAC-SHA-1",add_aa)) {
+						str_buffer_append(sb," checked ");
+					}
+					str_buffer_append(sb,">HMAC-SHA-1\r\n<br>\r\n");
+				}
+
+				str_buffer_append(sb,"</td><td colspan=\"2\">");
+
+				{
+					if(!add_auth_key) add_auth_key = "";
+
+					str_buffer_append(sb,"  <br>Base64-encoded AUTH key (optional):<br><textarea wrap=\"soft\" cols=70 rows=4 name=\"");
+					str_buffer_append(sb,HR_ADD_OAUTH_AUTH_KEY);
+					str_buffer_append(sb,"\" maxLength=256 >");
+					str_buffer_append(sb,(const char*)add_auth_key);
+					str_buffer_append(sb,"</textarea>");
+					str_buffer_append(sb,"<br>\r\n");
+				}
+
+				str_buffer_append(sb,"</td></tr></table>\r\n");
+
+				str_buffer_append(sb,"<br><input type=\"submit\" value=\"Add key\">");
+
+				str_buffer_append(sb,"</fieldset>\r\n");
+				str_buffer_append(sb,"</form>\r\n");
+			}
+
+			str_buffer_append(sb,"<br><b>OAuth keys:</b><br><br>\r\n");
+			str_buffer_append(sb,"<table>\r\n");
+			str_buffer_append(sb,"<tr><th>N</th><th>KID</th><th>keys</th>");
+			str_buffer_append(sb,"<th>Timestamp, secs</th>");
+			str_buffer_append(sb,"<th>Lifetime,secs</th>");
+			str_buffer_append(sb,"<th>Hash key derivation function</th>");
+			str_buffer_append(sb,"<th>Token encryption algorithm</th>");
+			str_buffer_append(sb,"<th>Token authentication algorithm</th>");
+			str_buffer_append(sb,"<th> </th>");
+			str_buffer_append(sb,"</tr>\r\n");
+
+			size_t total_sz = https_print_oauth_keys(sb);
+
+			str_buffer_append(sb,"\r\n</table>\r\n");
+
+			str_buffer_append(sb,"<br>Total oAuth keys = ");
+			str_buffer_append_sz(sb,total_sz);
+			str_buffer_append(sb,"<br>\r\n");
+
+			https_finish_page(sb,s,0);
+		}
+	}
+}
+
+static void handle_toggle_request(ioa_socket_handle s, struct http_request* hr)
+{
+	if(s && hr) {
+		const char *param = get_http_header_value(hr, HR_UPDATE_PARAMETER, NULL);
+		toggle_param(param);
+	}
+}
+
+static void handle_update_request(ioa_socket_handle s, struct http_request* hr)
+{
+	if(s && hr) {
+		{
+			const char *param = get_http_header_value(hr, HR_UPDATE_PARAMETER, NULL);
+			if(param) {
+				update_param(param,get_http_header_value(hr,param,""));
+			}
+		}
+
+		{
+			const char* eip = get_http_header_value(hr, HR_DELETE_IP, NULL);
+			if(eip && eip[0]) {
+				char* ip = evhttp_decode_uri(eip);
+				const char* r = get_http_header_value(hr, HR_DELETE_IP_REALM,"");
+				const char* kind = get_http_header_value(hr, HR_DELETE_IP_KIND,"");
+
+				const turn_dbdriver_t * dbd = get_dbdriver();
+				if (dbd && dbd->set_permission_ip) {
+
+					if(!r || !r[0]) {
+						r = current_realm();
+					}
+
+					if(current_realm()[0] && strcmp(current_realm(),r)) {
+						//forbidden
+					} else {
+
+						u08bits realm[STUN_MAX_REALM_SIZE+1]="\0";
+						STRCPY(realm,r);
+
+						dbd->set_permission_ip(kind, realm, ip, 1);
+					}
+				}
+				free(ip);
+			}
+		}
+
+		{
+			const char* eip = get_http_header_value(hr, HR_ADD_IP,NULL);
+			if(eip && eip[0]) {
+				char* ip = evhttp_decode_uri(eip);
+
+				if(check_ip_list_range(ip)<0) {
+					TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address range format: %s\n", ip);
+				} else {
+
+					const char* r = get_http_header_value(hr, HR_ADD_IP_REALM,"");
+					const char* kind = get_http_header_value(hr, HR_ADD_IP_KIND,"");
+
+					const turn_dbdriver_t * dbd = get_dbdriver();
+					if (dbd && dbd->set_permission_ip) {
+
+						if(!r || !r[0]) {
+							r = current_realm();
+						}
+
+						if(current_realm()[0] && strcmp(current_realm(),r)) {
+							//forbidden
+						} else {
+
+							u08bits realm[STUN_MAX_REALM_SIZE+1]="\0";
+							STRCPY(realm,r);
+
+							dbd->set_permission_ip(kind, realm, ip, 0);
+						}
+					}
+				}
+				free(ip);
+			}
+		}
+	}
+}
+
+static void handle_logon_request(ioa_socket_handle s, struct http_request* hr)
+{
+	if(s && hr) {
+		const char *uname = get_http_header_value(hr, HR_USERNAME, NULL);
+		const char *pwd = get_http_header_value(hr, HR_PASSWORD, NULL);
+
+		struct admin_session* as = (struct admin_session*)s->special_session;
+		if(!as) {
+			as = (struct admin_session*)turn_malloc(sizeof(struct admin_session));
+			ns_bzero(as,sizeof(struct admin_session));
+			s->special_session = as;
+			s->special_session_size = sizeof(struct admin_session);
+		}
+
+		if(!(as->as_ok) && uname && pwd) {
+			const turn_dbdriver_t * dbd = get_dbdriver();
+			if (dbd && dbd->get_admin_user) {
+
+				password_t password;
+				char realm[STUN_MAX_REALM_SIZE+1]="\0";
+				if((*(dbd->get_admin_user))((const u08bits*)uname,(u08bits*)realm,password)>=0) {
+					if(!strcmp(pwd,(char*)password)) {
+						STRCPY(as->as_login,uname);
+						STRCPY(as->as_realm,realm);
+						as->as_eff_realm[0]=0;
+						as->as_ok = 1;
+						as->number_of_user_sessions = DEFAULT_CLI_MAX_OUTPUT_SESSIONS;
+					}
+				}
+			}
+		}
+	}
+}
+
+static void handle_logout_request(ioa_socket_handle s, struct http_request* hr)
+{
+	UNUSED_ARG(hr);
+	if(s) {
+		struct admin_session* as = (struct admin_session*)s->special_session;
+		if(as) {
+			as->as_login[0] = 0;
+			as->as_ok = 0;
+			as->as_realm[0] = 0;
+			as->as_eff_realm[0] = 0;
+		}
+	}
+}
+
+static void handle_https(ioa_socket_handle s, ioa_network_buffer_handle nbh)
+{
+	current_socket = s;
+
+	if(turn_params.verbose) {
+		if(nbh) {
+			((char*)ioa_network_buffer_data(nbh))[ioa_network_buffer_get_size(nbh)] = 0;
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: HTTPS connection input: %s\n", __FUNCTION__, (char*)ioa_network_buffer_data(nbh));
+		} else {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: HTTPS connection initial input\n", __FUNCTION__);
+		}
+	}
+
+	if(!nbh) {
+		write_https_logon_page(s);
+	} else {
+		((char*)ioa_network_buffer_data(nbh))[ioa_network_buffer_get_size(nbh)] = 0;
+		struct http_request* hr = parse_http_request((char*)ioa_network_buffer_data(nbh));
+		if(!hr) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: wrong HTTPS request (I cannot parse it)\n", __FUNCTION__);
+		} else {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: HTTPS request, path %s\n", __FUNCTION__,hr->path);
+
+			AS_FORM form = get_form(hr->path);
+
+			switch(form) {
+			case AS_FORM_PC: {
+				if(is_as_ok(s)) {
+					const char *realm0 = get_http_header_value(hr, HR_REALM, current_realm());
+					if(!is_superuser())
+						realm0 = current_realm();
+					strncpy(current_eff_realm(),realm0,STUN_MAX_REALM_SIZE);
+					write_pc_page(s);
+				} else {
+					write_https_logon_page(s);
+				}
+				break;
+			}
+			case AS_FORM_PS: {
+				if(is_as_ok(s)) {
+					const char *realm0 = get_http_header_value(hr, HR_REALM, current_realm());
+					if(!is_superuser())
+						realm0 = current_realm();
+					strncpy(current_eff_realm(),realm0,STUN_MAX_REALM_SIZE);
+
+					const char* client_protocol = get_http_header_value(hr, HR_CLIENT_PROTOCOL, "");
+
+					const char* user_pattern = get_http_header_value(hr, HR_USER_PATTERN, "");
+
+					turnsession_id csid=0;
+					const char* ssid = get_http_header_value(hr, HR_CANCEL_SESSION, NULL);
+					if(ssid) {
+						https_cancel_session(ssid);
+						csid = (turnsession_id)strtoull(ssid,NULL,10);
+					}
+
+					size_t max_sessions = current_max_output_sessions();
+					const char* s_max_sessions = get_http_header_value(hr, HR_MAX_SESSIONS,NULL);
+					if(s_max_sessions) {
+						max_sessions=strtoul(s_max_sessions,NULL,10);
+						if(!max_sessions) max_sessions = current_max_output_sessions();
+						set_current_max_output_sessions(max_sessions);
+					}
+
+					if(!max_sessions) max_sessions = DEFAULT_CLI_MAX_OUTPUT_SESSIONS;
+
+					write_ps_page(s,client_protocol,user_pattern,max_sessions,csid);
+				} else {
+					write_https_logon_page(s);
+				}
+				break;
+			}
+			case AS_FORM_USERS: {
+				if(is_as_ok(s)) {
+					{
+						const char *realm0 = get_http_header_value(hr, HR_REALM, current_realm());
+						if(!is_superuser())
+							realm0 = current_realm();
+						strncpy(current_eff_realm(),realm0,STUN_MAX_REALM_SIZE);
+					}
+
+					{
+						const u08bits *user = (const u08bits*)get_http_header_value(hr, HR_DELETE_USER, NULL);
+						if(user && user[0]) {
+							const u08bits *realm = (const u08bits*)get_http_header_value(hr, HR_DELETE_REALM, "");
+							if(!is_superuser()) {
+								realm = (const u08bits*)current_realm();
+							}
+							if(realm && realm[0]) {
+								const turn_dbdriver_t * dbd = get_dbdriver();
+								if (dbd && dbd->del_user) {
+									u08bits u[STUN_MAX_USERNAME_SIZE+1];
+									u08bits r[STUN_MAX_REALM_SIZE+1];
+									STRCPY(u,user);
+									STRCPY(r,realm);
+									dbd->del_user(u,r);
+								}
+							}
+						}
+					}
+
+					const u08bits *add_realm = (const u08bits*)current_eff_realm();
+					const u08bits *add_user = (const u08bits*)get_http_header_value(hr, HR_ADD_USER,"");
+					const char* msg = "";
+					if(wrong_html_name((const char*)add_user)) {
+						msg = "Error: wrong user name";
+						add_user = (const u08bits*)"";
+					}
+					if(add_user[0]) {
+						add_realm = (const u08bits*)get_http_header_value(hr, HR_ADD_REALM, current_realm());
+						if(!is_superuser()) {
+							add_realm = (const u08bits*)current_realm();
+						}
+						if(!add_realm[0]) {
+							add_realm=(const u08bits*)current_eff_realm();
+						}
+						if(!add_realm[0]) {
+							add_realm = (const u08bits*)get_realm(NULL)->options.name;
+						}
+						if(wrong_html_name((const char*)add_realm)) {
+							msg = "Error: wrong realm name";
+							add_realm = (const u08bits*)"";
+						}
+						if(add_realm[0]) {
+							const u08bits *pwd = (const u08bits*)get_http_header_value(hr, HR_PASSWORD, NULL);
+							const u08bits *pwd1 = (const u08bits*)get_http_header_value(hr, HR_PASSWORD1, NULL);
+							if(pwd && pwd1 && pwd[0] && pwd1[0] && !strcmp((const char*)pwd,(const char*)pwd1)) {
+
+								const turn_dbdriver_t * dbd = get_dbdriver();
+								if (dbd && dbd->set_user_key) {
+
+									hmackey_t key;
+									char skey[sizeof(hmackey_t) * 2 + 1];
+
+									{
+										u08bits u[STUN_MAX_USERNAME_SIZE+1];
+										u08bits r[STUN_MAX_REALM_SIZE+1];
+										u08bits p[STUN_MAX_PWD_SIZE+1];
+										STRCPY(u,add_user);
+										STRCPY(r,add_realm);
+										STRCPY(p,pwd);
+										stun_produce_integrity_key_str(u, r, p, key, turn_params.shatype);
+										size_t i = 0;
+										size_t sz = get_hmackey_size(turn_params.shatype);
+										int maxsz = (int) (sz * 2) + 1;
+										char *s = skey;
+										for (i = 0; (i < sz) && (maxsz > 2); i++) {
+											snprintf(s, (size_t) (sz * 2), "%02x", (unsigned int) key[i]);
+											maxsz -= 2;
+											s += 2;
+										}
+										skey[sz * 2] = 0;
+
+										(*dbd->set_user_key)(u, r, skey);
+									}
+
+									add_realm=(const u08bits*)"";
+									add_user=(const u08bits*)"";
+								}
+							} else {
+								msg = "Error: wrong password";
+							}
+						}
+					}
+
+					write_users_page(s,add_user,add_realm,msg);
+
+				} else {
+					write_https_logon_page(s);
+				}
+				break;
+			}
+			case AS_FORM_SS: {
+				if(is_as_ok(s)) {
+					{
+						const char *realm0 = get_http_header_value(hr, HR_REALM, current_realm());
+						if(!is_superuser())
+							realm0 = current_realm();
+						strncpy(current_eff_realm(),realm0,STUN_MAX_REALM_SIZE);
+					}
+
+					{
+						const u08bits *secret = (const u08bits*)get_http_header_value(hr, HR_DELETE_SECRET, NULL);
+						if(secret && secret[0]) {
+							const u08bits *realm = (const u08bits*)get_http_header_value(hr, HR_DELETE_REALM, NULL);
+							if(!is_superuser()) {
+								realm = (const u08bits*)current_realm();
+							}
+							if(realm && realm[0]) {
+								const turn_dbdriver_t * dbd = get_dbdriver();
+								if (dbd && dbd->del_secret) {
+									u08bits ss[AUTH_SECRET_SIZE+1];
+									u08bits r[STUN_MAX_REALM_SIZE+1];
+									STRCPY(ss,secret);
+									STRCPY(r,realm);
+									dbd->del_secret(ss,r);
+								}
+							}
+						}
+					}
+
+					const u08bits *add_realm = (const u08bits*)current_eff_realm();
+					const u08bits *add_secret = (const u08bits*)get_http_header_value(hr, HR_ADD_SECRET, "");
+					const char* msg = "";
+					if(wrong_html_name((const char*)add_secret)) {
+						msg = "Error: wrong secret value";
+						add_secret = (const u08bits*)"";
+					}
+					if(add_secret[0]) {
+						add_realm = (const u08bits*)get_http_header_value(hr, HR_ADD_REALM, current_realm());
+						if(!is_superuser()) {
+							add_realm = (const u08bits*)current_realm();
+						}
+						if(!add_realm[0]) {
+							add_realm=(const u08bits*)current_eff_realm();
+						}
+						if(!add_realm[0]) {
+							add_realm = (const u08bits*)get_realm(NULL)->options.name;
+						}
+						if(wrong_html_name((const char*)add_realm)) {
+							msg = "Error: wrong realm name";
+							add_realm = (const u08bits*)"";
+						}
+						if(add_realm[0]) {
+							const turn_dbdriver_t * dbd = get_dbdriver();
+							if (dbd && dbd->set_secret) {
+								u08bits ss[AUTH_SECRET_SIZE+1];
+								u08bits r[STUN_MAX_REALM_SIZE+1];
+								STRCPY(ss,add_secret);
+								STRCPY(r,add_realm);
+								(*dbd->set_secret)(ss, r);
+							}
+
+							add_secret=(const u08bits*)"";
+							add_realm=(const u08bits*)"";
+						}
+					}
+
+					write_shared_secrets_page(s,(const char*)add_secret,(const char*)add_realm,msg);
+
+				} else {
+					write_https_logon_page(s);
+				}
+				break;
+			}
+			case AS_FORM_OS: {
+				if(is_as_ok(s)) {
+					{
+						const char *realm0 = get_http_header_value(hr, HR_REALM, current_realm());
+						if(!is_superuser())
+							realm0 = current_realm();
+						strncpy(current_eff_realm(),realm0,STUN_MAX_REALM_SIZE);
+					}
+
+					if(is_superuser()) {
+						const u08bits *origin = (const u08bits*)get_http_header_value(hr, HR_DELETE_ORIGIN, NULL);
+						if(origin && origin[0]) {
+							const turn_dbdriver_t * dbd = get_dbdriver();
+							if (dbd && dbd->del_origin) {
+								u08bits o[STUN_MAX_ORIGIN_SIZE+1];
+								STRCPY(o,origin);
+								dbd->del_origin(o);
+								u08bits corigin[STUN_MAX_ORIGIN_SIZE+1];
+								get_canonic_origin((const char *)origin, (char *)corigin, sizeof(corigin)-1);
+								dbd->del_origin(corigin);
+							}
+						}
+					}
+
+					const u08bits *add_realm = (const u08bits*)current_eff_realm();
+					const u08bits *add_origin = (const u08bits*)get_http_header_value(hr, HR_ADD_ORIGIN, "");
+					const char* msg = "";
+					u08bits corigin[STUN_MAX_ORIGIN_SIZE+1];
+					get_canonic_origin((const char *)add_origin, (char *)corigin, sizeof(corigin)-1);
+					if(corigin[0]) {
+						add_realm = (const u08bits*)get_http_header_value(hr, HR_ADD_REALM, current_realm());
+						if(!is_superuser()) {
+							add_realm = (const u08bits*)current_realm();
+						}
+						if(!add_realm[0]) {
+							add_realm=(const u08bits*)current_eff_realm();
+						}
+						if(!add_realm[0]) {
+							add_realm = (const u08bits*)get_realm(NULL)->options.name;
+						}
+						if(add_realm[0]) {
+							const turn_dbdriver_t * dbd = get_dbdriver();
+							if (dbd && dbd->add_origin) {
+								u08bits o[STUN_MAX_ORIGIN_SIZE+1];
+								u08bits r[STUN_MAX_REALM_SIZE+1];
+								STRCPY(o,corigin);
+								STRCPY(r,add_realm);
+								(*dbd->add_origin)(o, r);
+							}
+
+							add_origin=(const u08bits*)"";
+							add_realm=(const u08bits*)"";
+						}
+					}
+
+					write_origins_page(s,(const char*)add_origin,(const char*)add_realm,msg);
+
+				} else {
+					write_https_logon_page(s);
+				}
+				break;
+			}
+			case AS_FORM_OAUTH_SHOW_KEYS: {
+				if(!is_as_ok(s)) {
+					write_https_logon_page(s);
+				} else if(!is_superuser()) {
+					write_https_home_page(s);
+				} else {
+						const char* kid = get_http_header_value(hr,HR_OAUTH_KID,"");
+						write_https_oauth_show_keys(s,kid);
+				}
+				break;
+			}
+			case AS_FORM_OAUTH: {
+				if(!is_as_ok(s)) {
+					write_https_logon_page(s);
+				} else if(!is_superuser()) {
+					write_https_home_page(s);
+				} else {
+
+					{
+						const char* del_kid = get_http_header_value(hr,HR_DELETE_OAUTH_KID,"");
+						if(del_kid[0]) {
+							const turn_dbdriver_t * dbd = get_dbdriver();
+							if (dbd && dbd->del_oauth_key) {
+								(*dbd->del_oauth_key)((const u08bits*)del_kid);
+							}
+						}
+					}
+
+					const char* add_kid = "";
+					const char* add_ts = "0";
+					const char* add_lt = "0";
+					const char* add_ikm = "";
+					const char *add_rs_key = "";
+					const char *add_auth_key = "";
+					const char* add_hkdf_hash_func = "";
+					const char* add_tea = "";
+					const char* add_aa = "";
+					const char* msg = "";
+
+					add_kid = get_http_header_value(hr,HR_ADD_OAUTH_KID,"");
+					if(add_kid[0]) {
+						add_ikm = get_http_header_value(hr,HR_ADD_OAUTH_IKM,"");
+						add_rs_key = get_http_header_value(hr,HR_ADD_OAUTH_RS_KEY,"");
+						add_auth_key = get_http_header_value(hr,HR_ADD_OAUTH_AUTH_KEY,"");
+						add_ts = get_http_header_value(hr,HR_ADD_OAUTH_TS,"");
+						add_lt = get_http_header_value(hr,HR_ADD_OAUTH_LT,"");
+						add_hkdf_hash_func = get_http_header_value(hr,HR_ADD_OAUTH_HKDF,"");
+						add_tea = get_http_header_value(hr,HR_ADD_OAUTH_TEA,"");
+						add_aa = get_http_header_value(hr,HR_ADD_OAUTH_AA,"");
+
+						int keys_ok = 0;
+						if(add_ikm[0] && add_hkdf_hash_func[0]) {
+							keys_ok = 1;
+						} else if(add_rs_key[0] && add_auth_key[0]) {
+							keys_ok = 1;
+						} else if(strstr(add_tea,"AEAD") && add_rs_key[0]) {
+							keys_ok = 1;
+						}
+						if(!keys_ok) {
+							msg = "Provided information is insufficient for the oAuth key generation.";
+						} else {
+							oauth_key_data_raw key;
+							ns_bzero(&key,sizeof(key));
+							STRCPY(key.kid,add_kid);
+
+							if(add_lt && add_lt[0]) {
+								key.lifetime = (u32bits)strtoul(add_lt,NULL,10);
+								if(key.lifetime) {
+									if(add_ts && add_ts[0]) {
+										key.timestamp = (u64bits)strtoull(add_ts,NULL,10);
+									}
+									if(!key.timestamp) {
+										key.timestamp = (u64bits)time(NULL);
+									}
+								}
+							} else if(add_ts && add_ts[0]) {
+								key.timestamp = (u64bits)strtoull(add_ts,NULL,10);
+							}
+
+							STRCPY(key.ikm_key,add_ikm);
+							STRCPY(key.hkdf_hash_func,add_hkdf_hash_func);
+							STRCPY(key.as_rs_alg,add_tea);
+							STRCPY(key.auth_alg,add_aa);
+							STRCPY(key.as_rs_key,add_rs_key);
+							STRCPY(key.auth_key,add_auth_key);
+
+							if(strstr(key.as_rs_alg,"AEAD")) key.auth_alg[0]=0;
+
+							const turn_dbdriver_t * dbd = get_dbdriver();
+							if (dbd && dbd->set_oauth_key) {
+								if((*dbd->set_oauth_key)(&key)<0) {
+									msg = "Cannot insert oAuth key into the database";
+								} else {
+									add_kid = "";
+									add_ts = "0";
+									add_lt = "0";
+									add_ikm = "";
+									add_hkdf_hash_func = "";
+									add_tea = "";
+									add_aa = "";
+									add_rs_key = "";
+									add_auth_key = "";
+								}
+							}
+						}
+					}
+
+					write_https_oauth_page(s,add_kid,add_ikm,add_hkdf_hash_func,add_tea,add_aa,add_ts,add_lt,add_rs_key,add_auth_key,msg);
+				}
+				break;
+			}
+			case AS_FORM_TOGGLE:
+				if(is_as_ok(s)) {
+					handle_toggle_request(s,hr);
+					write_pc_page(s);
+				} else {
+					write_https_logon_page(s);
+				}
+				break;
+			case AS_FORM_UPDATE:
+				if(is_as_ok(s)) {
+					handle_update_request(s,hr);
+					write_pc_page(s);
+				} else {
+					write_https_logon_page(s);
+				}
+				break;
+			case AS_FORM_LOGON:
+				if(!is_as_ok(s)) {
+					handle_logon_request(s,hr);
+					if(is_as_ok(s)) {
+						write_https_home_page(s);
+					} else {
+						write_https_logon_page(s);
+					}
+				} else {
+					write_https_home_page(s);
+				}
+				break;
+			case AS_FORM_LOGOUT:
+				handle_logout_request(s,hr);
+				write_https_logon_page(s);
+				break;
+			default: {
+			  const char *realm0 = get_http_header_value(hr, HR_REALM, current_realm());
+			  if(!is_superuser())
+			    realm0 = current_realm();
+			  strncpy(current_eff_realm(),realm0,STUN_MAX_REALM_SIZE);
+			  write_https_home_page(s);
+			}
+			};
+			free_http_request(hr);
+		}
+	}
+
+	current_socket = NULL;
+}
+
+static void https_input_handler(ioa_socket_handle s, int event_type, ioa_net_data *data, void *arg, int can_resume) {
+
+	UNUSED_ARG(arg);
+	UNUSED_ARG(s);
+	UNUSED_ARG(event_type);
+	UNUSED_ARG(can_resume);
+
+	handle_https(s,data->nbh);
+
+	ioa_network_buffer_delete(adminserver.e, data->nbh);
+	data->nbh = NULL;
+}
+
+void https_admin_server_receive_message(struct bufferevent *bev, void *ptr)
+{
+	UNUSED_ARG(ptr);
+
+	ioa_socket_handle s= NULL;
+	int n = 0;
+	struct evbuffer *input = bufferevent_get_input(bev);
+
+	while ((n = evbuffer_remove(input, &s, sizeof(s))) > 0) {
+		if (n != sizeof(s)) {
+			fprintf(stderr,"%s: Weird HTTPS CLI buffer error: size=%d\n",__FUNCTION__,n);
+			continue;
+		}
+
+		register_callback_on_ioa_socket(adminserver.e, s, IOA_EV_READ, https_input_handler, NULL, 0);
+
+		handle_https(s,NULL);
+	}
+}
+
+void send_https_socket(ioa_socket_handle s) {
+	struct evbuffer *output = bufferevent_get_output(adminserver.https_out_buf);
+	if(output) {
+		evbuffer_add(output,&s,sizeof(s));
+	}
+}
+
+///////////////////////////////

+ 22 - 7
src/apps/relay/turncli.h → src/apps/relay/turn_admin_server.h

@@ -28,8 +28,8 @@
  * SUCH DAMAGE.
  */
 
-#ifndef __TURNCLI__
-#define __TURNCLI__
+#ifndef __TURN_ADMIN_SERVER__
+#define __TURN_ADMIN_SERVER__
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -51,20 +51,33 @@ extern "C" {
 
 ////////////////////////////////////////////
 
-struct cli_server {
+#define ADMIN_USER_MAX_LENGTH (32)
+
+struct admin_session {
+	int as_ok;
+	char as_login[ADMIN_USER_MAX_LENGTH + 1];
+	char as_realm[STUN_MAX_REALM_SIZE + 1];
+	char as_eff_realm[STUN_MAX_REALM_SIZE + 1];
+	size_t number_of_user_sessions;
+};
+
+struct admin_server {
 	evutil_socket_t listen_fd;
 	struct event_base* event_base;
+	ioa_engine_handle e;
 	int verbose;
 	struct evconnlistener *l;
 	struct bufferevent *in_buf;
 	struct bufferevent *out_buf;
+	struct bufferevent *https_in_buf;
+	struct bufferevent *https_out_buf;
 	ur_map *sessions;
 	pthread_t thr;
 };
 
 ///////////////////////////////////////////
 
-extern struct cli_server cliserver;
+extern struct admin_server adminserver;
 
 extern int use_cli;
 
@@ -83,11 +96,13 @@ extern int cli_max_output_sessions;
 
 ////////////////////////////////////////////
 
-void setup_cli_thread(void);
+void setup_admin_thread(void);
 
-void cli_server_receive_message(struct bufferevent *bev, void *ptr);
+void admin_server_receive_message(struct bufferevent *bev, void *ptr);
+void https_admin_server_receive_message(struct bufferevent *bev, void *ptr);
 
 int send_turn_session_info(struct turn_session_info* tsi);
+void send_https_socket(ioa_socket_handle s);
 
 ////////////////////////////////////////////
 
@@ -96,5 +111,5 @@ int send_turn_session_info(struct turn_session_info* tsi);
 #endif
 
 #endif
-/// __TURNCLI__///
+/// __TURN_ADMIN_SERVER__///
 

+ 0 - 1333
src/apps/relay/turncli.c

@@ -1,1333 +0,0 @@
-/*
- * Copyright (C) 2011, 2012, 2013 Citrix Systems
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the project nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <limits.h>
-#include <ifaddrs.h>
-#include <getopt.h>
-#include <locale.h>
-#include <libgen.h>
-
-#include <pthread.h>
-
-#include <signal.h>
-
-#include "libtelnet.h"
-
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <sys/resource.h>
-
-#include <event2/bufferevent.h>
-#include <event2/buffer.h>
-#include <event2/listener.h>
-
-#include "userdb.h"
-#include "mainrelay.h"
-
-#include "ns_turn_utils.h"
-
-#include "ns_turn_server.h"
-#include "ns_turn_maps.h"
-
-#include "apputils.h"
-
-#include "turncli.h"
-
-///////////////////////////////
-
-struct cli_server cliserver;
-
-int use_cli = 1;
-
-ioa_addr cli_addr;
-int cli_addr_set = 0;
-
-int cli_port = CLI_DEFAULT_PORT;
-
-char cli_password[CLI_PASSWORD_LENGTH] = "";
-
-int cli_max_output_sessions = DEFAULT_CLI_MAX_OUTPUT_SESSIONS;
-
-///////////////////////////////
-
-struct cli_session {
-	evutil_socket_t fd;
-	int auth_completed;
-	size_t cmds;
-	struct bufferevent *bev;
-	ioa_addr addr;
-	telnet_t *ts;
-	FILE* f;
-	char realm[STUN_MAX_REALM_SIZE+1];
-	char origin[STUN_MAX_ORIGIN_SIZE+1];
-	realm_params_t *rp;
-};
-
-///////////////////////////////
-
-#define CLI_PASSWORD_TRY_NUMBER (5)
-
-static const char *CLI_HELP_STR[] = 
-  {"",
-   "  ?, h, help - print this text",
-   "",
-   "  quit, q, exit, bye - end CLI session",
-   "",
-   "  stop, shutdown, halt - shutdown TURN Server",
-   "",
-   "  pc - print configuration",
-   "",
-   "  sr <realm> - set CLI session realm",
-   "",
-   "  ur - unset CLI session realm",
-   "",
-   "  so <origin> - set CLI session origin",
-   "",
-   "  uo - unset CLI session origin",
-   "",
-   "  tc <param-name> - toggle a configuration parameter",
-   "     (see pc command output for togglable param names)",
-   "",
-   "  cc <param-name> <param-value> - change a configuration parameter",
-   "     (see pc command output for changeable param names)",
-   "",
-   "  ps [username] - print sessions, with optional exact user match",
-   "",
-   "  psp <usernamestr> - print sessions, with partial user string match",
-   "",
-   "  psd <file-name> - dump ps command output into file on the TURN server system",
-   "",
-   "  pu [udp|tcp|dtls|tls]- print current users",
-   "",
-   "  lr - log reset",
-   "",
-   "  aas ip[:port} - add an alternate server reference",
-   "  das ip[:port] - delete an alternate server reference",
-   "  atas ip[:port] - add a TLS alternate server reference",
-   "  dtas ip[:port] - delete a TLS alternate server reference",
-   "",
-   "  cs <session-id> - cancel session, forcefully"
-   "",
-   NULL};
-
-static const char *CLI_GREETING_STR[] = {
-  "TURN Server",
-  TURN_SOFTWARE,
-  NULL};
-
-static char CLI_CURSOR[] = "> ";
-
-static const telnet_telopt_t cli_telopts[] = {
-    { TELNET_TELOPT_ECHO,      TELNET_WONT, TELNET_DONT },
-    { TELNET_TELOPT_TTYPE,     TELNET_WONT, TELNET_DONT },
-    { TELNET_TELOPT_COMPRESS2, TELNET_WONT, TELNET_DONT },
-    { TELNET_TELOPT_ZMP,       TELNET_WONT, TELNET_DONT },
-    { TELNET_TELOPT_MSSP,      TELNET_WONT, TELNET_DONT },
-    { TELNET_TELOPT_BINARY,    TELNET_WONT, TELNET_DONT },
-    { TELNET_TELOPT_NAWS,      TELNET_WONT, TELNET_DONT },
-    { -1, 0, 0 }
-  };
-
-struct toggleable_command {
-	const char *cmd;
-	vintp data;
-};
-
-struct toggleable_command tcmds[] = {
-				{"stale-nonce",&turn_params.stale_nonce},
-				{"stun-only",&turn_params.stun_only},
-				{"no-stun",&turn_params.no_stun},
-				{"secure-stun",&turn_params.secure_stun},
-				{"no-udp-relay",&turn_params.no_udp_relay},
-				{"no-tcp-relay",&turn_params.no_tcp_relay},
-				{"no-multicast-peers",&turn_params.no_multicast_peers},
-				{"no-loopback-peers",&turn_params.no_loopback_peers},
-				{"mobility",&turn_params.mobility},
-				{NULL,NULL}
-};
-
-///////////////////////////////
-
-static void myprintf(struct cli_session *cs, const char *format, ...)
-{
-	if(cs && format) {
-		va_list args;
-		va_start (args, format);
-		if(cs->f) {
-			vfprintf(cs->f, format, args);
-		} else {
-			telnet_vprintf(cs->ts, format, args);
-		}
-		va_end (args);
-	}
-}
-
-static void log_reset(struct cli_session* cs)
-{
-	if(cs) {
-	  reset_rtpprintf();
-	  myprintf(cs,"  log reset done\n");
-	}
-}
-
-static void print_str_array(struct cli_session* cs, const char** sa)
-{
-  if(cs && sa) {
-    int i=0;
-    while(sa[i]) {
-      myprintf(cs,"%s\n",sa[i]);
-      i++;
-    }
-  }
-}
-
-static const char* get_flag(int val)
-{
-	if(val)
-		return "ON";
-	return "OFF";
-}
-
-static void cli_print_flag(struct cli_session* cs, int flag, const char* name, int changeable)
-{
-	if(cs && cs->ts && name) {
-		const char *sc="";
-		if(changeable)
-			sc=" (*)";
-		myprintf(cs,"  %s: %s%s\n",name,get_flag(flag),sc);
-	}
-}
-
-static void cli_print_uint(struct cli_session* cs, unsigned long value, const char* name, int changeable)
-{
-	if(cs && cs->ts && name) {
-		const char *sc="";
-		if(changeable==1)
-			sc=" (*)";
-		else if(changeable==2)
-			sc=" (**)";
-		myprintf(cs,"  %s: %lu%s\n",name,value,sc);
-	}
-}
-
-static void cli_print_str(struct cli_session* cs, const char *value, const char* name, int changeable)
-{
-	if(cs && cs->ts && name && value) {
-		if(value[0] == 0)
-			value="empty";
-		const char *sc="";
-		if(changeable==1)
-			sc=" (*)";
-		else if(changeable==2)
-			sc=" (**)";
-		myprintf(cs,"  %s: %s%s\n",name,value,sc);
-	}
-}
-
-static void cli_print_addr(struct cli_session* cs, ioa_addr *value, int use_port, const char* name, int changeable)
-{
-	if(cs && cs->ts && name && value) {
-		const char *sc="";
-		if(changeable==1)
-			sc=" (*)";
-		else if(changeable==2)
-			sc=" (**)";
-		char s[256];
-		if(!use_port)
-			addr_to_string_no_port(value,(u08bits*)s);
-		else
-			addr_to_string(value,(u08bits*)s);
-		myprintf(cs,"  %s: %s%s\n",name,s,sc);
-	}
-}
-
-static void cli_print_addr_list(struct cli_session* cs, turn_server_addrs_list_t *value, int use_port, const char* name, int changeable)
-{
-	if(cs && cs->ts && name && value && value->size && value->addrs) {
-		const char *sc="";
-		if(changeable==1)
-			sc=" (*)";
-		else if(changeable==2)
-			sc=" (**)";
-		char s[256];
-		size_t i;
-		for(i=0;i<value->size;i++) {
-			if(!use_port)
-				addr_to_string_no_port(&(value->addrs[i]),(u08bits*)s);
-			else
-				addr_to_string(&(value->addrs[i]),(u08bits*)s);
-			myprintf(cs,"  %s: %s%s\n",name,s,sc);
-		}
-	}
-}
-
-static void cli_print_str_array(struct cli_session* cs, char **value, size_t sz, const char* name, int changeable)
-{
-	if(cs && cs->ts && name && value && sz) {
-		const char *sc="";
-		if(changeable==1)
-			sc=" (*)";
-		else if(changeable==2)
-			sc=" (**)";
-		size_t i;
-		for(i=0;i<sz;i++) {
-			if(value[i])
-				myprintf(cs,"  %s: %s%s\n",name,value[i],sc);
-		}
-	}
-}
-
-static void cli_print_ip_range_list(struct cli_session* cs, ip_range_list_t *value, const char* name, int changeable)
-{
-	if(cs && cs->ts && name && value && value->ranges_number && value->rs) {
-		const char *sc="";
-		if(changeable==1)
-			sc=" (*)";
-		else if(changeable==2)
-			sc=" (**)";
-		size_t i;
-		for(i=0;i<value->ranges_number;++i) {
-			if(value->rs[i].realm[0]) {
-				if(cs->realm[0] && strcmp(cs->realm,value->rs[i].realm)) {
-					continue;
-				} else {
-					myprintf(cs,"  %s: %s (%s)%s\n",name,value->rs[i].str,value->rs[i].realm,sc);
-				}
-			} else {
-				myprintf(cs,"  %s: %s%s\n",name,value->rs[i].str,sc);
-			}
-		}
-	}
-}
-
-static void toggle_cli_param(struct cli_session* cs, const char* pn)
-{
-	if(cs && cs->ts && pn) {
-
-		int i=0;
-
-		while(tcmds[i].cmd && tcmds[i].data) {
-			if(strcmp(tcmds[i].cmd,pn) == 0) {
-				*(tcmds[i].data) = !(*(tcmds[i].data));
-				cli_print_flag(cs,*(tcmds[i].data),tcmds[i].cmd,0);
-				return;
-			}
-			++i;
-		}
-
-		myprintf(cs, "\n");
-		myprintf(cs, "  Error: unknown or constant parameter: %s.\n",pn);
-		myprintf(cs, "  You can toggle only the following parameters:\n");
-		myprintf(cs, "\n");
-
-		i=0;
-
-		while(tcmds[i].cmd && tcmds[i].data) {
-			cli_print_flag(cs,*(tcmds[i].data),tcmds[i].cmd,0);
-			++i;
-		}
-
-		myprintf(cs,"\n");
-	}
-}
-
-static void change_cli_param(struct cli_session* cs, const char* pn)
-{
-	if(cs && cs->ts && pn) {
-
-		if(strstr(pn,"total-quota")==pn) {
-			turn_params.total_quota = atoi(pn+strlen("total-quota"));
-			cli_print_uint(cs,(unsigned long)turn_params.total_quota,"total-quota",2);
-			return;
-		} else if(strstr(pn,"user-quota")==pn) {
-			turn_params.user_quota = atoi(pn+strlen("user-quota"));
-			cli_print_uint(cs,(unsigned long)turn_params.user_quota,"user-quota",2);
-			return;
-		} else if(strstr(pn,"max-bps")==pn) {
-			set_max_bps((band_limit_t)atol(pn+strlen("max-bps")));
-			cli_print_uint(cs,(unsigned long)get_max_bps(),"max-bps",2);
-			return;
-		} else if(strstr(pn,"bps-capacity")==pn) {
-			set_bps_capacity((band_limit_t)atol(pn+strlen("bps-capacity")));
-			cli_print_uint(cs,(unsigned long)get_bps_capacity(),"bps-capacity",2);
-			return;
-		} else if(strstr(pn,"cli-max-output-sessions")==pn) {
-			cli_max_output_sessions = atoi(pn+strlen("cli-max-output-sessions"));
-			cli_print_uint(cs,(unsigned long)cli_max_output_sessions,"cli-max-output-sessions",2);
-			return;
-		}
-
-		myprintf(cs, "\n");
-		myprintf(cs, "  Error: unknown or constant parameter: %s.\n",pn);
-		myprintf(cs, "\n");
-	}
-}
-
-struct ps_arg {
-	struct cli_session* cs;
-	size_t counter;
-	turn_time_t ct;
-	const char *username;
-	const char *pname;
-	int exact_match;
-	ur_string_map* users;
-	size_t *user_counters;
-	char **user_names;
-	size_t users_number;
-};
-
-static const char* pname(SOCKET_TYPE st)
-{
-	switch(st) {
-	case TCP_SOCKET:
-		return "TCP";
-	case UDP_SOCKET:
-		return "UDP";
-	case TLS_SOCKET:
-		return "TLS";
-	case DTLS_SOCKET:
-		return "DTLS";
-	case TENTATIVE_TCP_SOCKET:
-		return "TCP/TLS";
-	default:
-		;
-	};
-	return "UNKNOWN";
-}
-
-static int print_session(ur_map_key_type key, ur_map_value_type value, void *arg)
-{
-	if(key && value && arg) {
-		struct ps_arg *csarg = (struct ps_arg*)arg;
-		struct cli_session* cs = csarg->cs;
-		struct turn_session_info *tsi = (struct turn_session_info *)value;
-
-		if(cs->realm[0] && strcmp(cs->realm,tsi->realm))
-			return 0;
-
-		if(cs->origin[0] && strcmp(cs->origin,tsi->origin))
-					return 0;
-
-		if(csarg->users) {
-
-			const char *pn=csarg->pname;
-			if(pn[0]) {
-				if(!strcmp(pn,"TLS") || !strcmp(pn,"tls") || !strcmp(pn,"Tls")) {
-					if(tsi->client_protocol != TLS_SOCKET)
-						return 0;
-				} else if(!strcmp(pn,"DTLS") || !strcmp(pn,"dtls") || !strcmp(pn,"Dtls")) {
-					if(tsi->client_protocol != DTLS_SOCKET)
-						return 0;
-				} else if(!strcmp(pn,"TCP") || !strcmp(pn,"tcp") || !strcmp(pn,"Tcp")) {
-					if(tsi->client_protocol != TCP_SOCKET)
-						return 0;
-				} else if(!strcmp(pn,"UDP") || !strcmp(pn,"udp") || !strcmp(pn,"Udp")) {
-					if(tsi->client_protocol != UDP_SOCKET)
-						return 0;
-				} else {
-					return 0;
-				}
-			}
-
-			ur_string_map_value_type value;
-			if(!ur_string_map_get(csarg->users, (ur_string_map_key_type)(char*)tsi->username, &value)) {
-				value = (ur_string_map_value_type)csarg->users_number;
-				csarg->users_number += 1;
-				csarg->user_counters = (size_t*)turn_realloc(csarg->user_counters,
-						(size_t)value * sizeof(size_t),
-						csarg->users_number * sizeof(size_t));
-				csarg->user_names = (char**)turn_realloc(csarg->user_names,
-						(size_t)value * sizeof(char*),
-						csarg->users_number * sizeof(char*));
-				csarg->user_names[(size_t)value] = turn_strdup((char*)tsi->username);
-				csarg->user_counters[(size_t)value] = 0;
-				ur_string_map_put(csarg->users, (ur_string_map_key_type)(char*)tsi->username, value);
-			}
-			csarg->user_counters[(size_t)value] += 1;
-		} else {
-			if(csarg->username[0]) {
-				if(csarg->exact_match) {
-					if(strcmp((char*)tsi->username, csarg->username))
-						return 0;
-				} else {
-					if(!strstr((char*)tsi->username, csarg->username))
-						return 0;
-				}
-			}
-			if(cs->f || (unsigned long)csarg->counter<(unsigned long)cli_max_output_sessions) {
-				myprintf(cs, "\n");
-				myprintf(cs,"    %lu) id=%018llu, user <%s>:\n",
-								(unsigned long)(csarg->counter+1),
-								(unsigned long long)tsi->id,
-								tsi->username);
-				if(tsi->realm[0])
-					myprintf(cs,"      realm: %s\n",tsi->realm);
-				if(tsi->origin[0])
-					myprintf(cs,"      origin: %s\n",tsi->origin);
-				if(turn_time_before(csarg->ct, tsi->start_time)) {
-					myprintf(cs,"      started: undefined time\n");
-				} else {
-					myprintf(cs,"      started %lu secs ago\n",(unsigned long)(csarg->ct - tsi->start_time));
-				}
-				if(turn_time_before(tsi->expiration_time,csarg->ct)) {
-					myprintf(cs,"      expired\n");
-				} else {
-					myprintf(cs,"      expiring in %lu secs\n",(unsigned long)(tsi->expiration_time - csarg->ct));
-				}
-				myprintf(cs,"      client protocol %s, relay protocol %s\n",pname(tsi->client_protocol),pname(tsi->peer_protocol));
-				{
-					if(!tsi->local_addr_data.saddr[0])
-						addr_to_string(&(tsi->local_addr_data.addr),(u08bits*)tsi->local_addr_data.saddr);
-					if(!tsi->remote_addr_data.saddr[0])
-						addr_to_string(&(tsi->remote_addr_data.addr),(u08bits*)tsi->remote_addr_data.saddr);
-					if(!tsi->relay_addr_data_ipv4.saddr[0])
-						addr_to_string(&(tsi->relay_addr_data_ipv4.addr),(u08bits*)tsi->relay_addr_data_ipv4.saddr);
-					if(!tsi->relay_addr_data_ipv6.saddr[0])
-						addr_to_string(&(tsi->relay_addr_data_ipv6.addr),(u08bits*)tsi->relay_addr_data_ipv6.saddr);
-					myprintf(cs,"      client addr %s, server addr %s\n",
-									tsi->remote_addr_data.saddr,
-									tsi->local_addr_data.saddr);
-					if(tsi->relay_addr_data_ipv4.saddr[0]) {
-						myprintf(cs,"      relay addr %s\n", tsi->relay_addr_data_ipv4.saddr);
-					}
-					if(tsi->relay_addr_data_ipv6.saddr[0]) {
-						myprintf(cs,"      relay addr %s\n", tsi->relay_addr_data_ipv6.saddr);
-					}
-				}
-				myprintf(cs,"      fingerprints enforced: %s\n",get_flag(tsi->enforce_fingerprints));
-				myprintf(cs,"      mobile: %s\n",get_flag(tsi->is_mobile));
-				if(tsi->tls_method[0]) {
-					myprintf(cs,"      TLS method: %s\n",tsi->tls_method);
-					myprintf(cs,"      TLS cipher: %s\n",tsi->tls_cipher);
-				}
-				if(tsi->bps)
-					myprintf(cs,"      Max throughput: %lu bytes per second\n",(unsigned long)tsi->bps);
-				myprintf(cs,"      usage: rp=%lu, rb=%lu, sp=%lu, sb=%lu\n",(unsigned long)(tsi->received_packets), (unsigned long)(tsi->received_bytes),(unsigned long)(tsi->sent_packets),(unsigned long)(tsi->sent_bytes));
-				myprintf(cs,"       rate: r=%lu, s=%lu, total=%lu (bytes per sec)\n",(unsigned long)(tsi->received_rate), (unsigned long)(tsi->sent_rate),(unsigned long)(tsi->total_rate));
-				if(tsi->main_peers_size) {
-					myprintf(cs,"      peers:\n");
-					size_t i;
-					for(i=0;i<tsi->main_peers_size;++i) {
-						if(!(tsi->main_peers_data[i].saddr[0]))
-							addr_to_string(&(tsi->main_peers_data[i].addr),(u08bits*)tsi->main_peers_data[i].saddr);
-						myprintf(cs,"          %s\n",tsi->main_peers_data[i].saddr);
-					}
-					if(tsi->extra_peers_size && tsi->extra_peers_data) {
-						for(i=0;i<tsi->extra_peers_size;++i) {
-							if(!(tsi->extra_peers_data[i].saddr[0]))
-								addr_to_string(&(tsi->extra_peers_data[i].addr),(u08bits*)tsi->extra_peers_data[i].saddr);
-							myprintf(cs,"          %s\n",tsi->extra_peers_data[i].saddr);
-						}
-					}
-				}
-			}
-		}
-
-		csarg->counter += 1;
-	}
-	return 0;
-}
-
-static void cancel_session(struct cli_session* cs, const char* ssid)
-{
-	if(cs && cs->ts && ssid && *ssid) {
-		turnsession_id sid = strtoull(ssid,NULL,10);
-		send_session_cancellation_to_relay(sid);
-	}
-}
-
-static void print_sessions(struct cli_session* cs, const char* pn, int exact_match, int print_users)
-{
-	if(cs && cs->ts && pn) {
-
-		while(pn[0] == ' ') ++pn;
-		if(pn[0] == '*') ++pn;
-
-		const char *uname="";
-		if(!print_users) {
-			uname = pn;
-			pn = "";
-		}
-
-		struct ps_arg arg = {cs,0,0,uname,pn,exact_match,NULL,NULL,NULL,0};
-
-		arg.ct = turn_time();
-
-		if(print_users) {
-			arg.users = ur_string_map_create(NULL);
-		}
-
-		ur_map_foreach_arg(cliserver.sessions, (foreachcb_arg_type)print_session, &arg);
-
-		myprintf(cs,"\n");
-
-		if(!print_users && !(cs->f)) {
-			if((unsigned long)arg.counter > (unsigned long)cli_max_output_sessions) {
-				myprintf(cs,"...\n");
-				myprintf(cs,"\n");
-			}
-		} else if(arg.user_counters && arg.user_names) {
-			size_t i;
-			for(i=0;i<arg.users_number;++i) {
-				if(arg.user_names[i]) {
-					myprintf(cs,"    user: <%s>, %lu sessions\n",
-						arg.user_names[i],
-						(unsigned long)arg.user_counters[i]);
-				}
-			}
-			myprintf(cs,"\n");
-		}
-
-		{
-			char ts[1025];
-			snprintf(ts,sizeof(ts),"  Total sessions");
-			if(cs->realm[0]) {
-				snprintf(ts+strlen(ts),sizeof(ts)-strlen(ts)," for realm %s",cs->realm);
-				if(cs->origin[0])
-					snprintf(ts+strlen(ts),sizeof(ts)-strlen(ts)," and for origin %s",cs->origin);
-			} else {
-				if(cs->origin[0])
-					snprintf(ts+strlen(ts),sizeof(ts)-strlen(ts)," for origin %s",cs->origin);
-			}
-			snprintf(ts+strlen(ts),sizeof(ts)-strlen(ts),": %lu", (unsigned long)arg.counter);
-			myprintf(cs,"%s\n", ts);
-			myprintf(cs,"\n");
-		}
-
-		if(!print_users && !(cs->f)) {
-			if((unsigned long)arg.counter > (unsigned long)cli_max_output_sessions) {
-				myprintf(cs,"  Warning: too many output sessions, more than the\n");
-				myprintf(cs,"  current value of cli-max-output-sessions CLI parameter.\n");
-				myprintf(cs,"  Refine your request or increase cli-max-output-sessions value.\n");
-				myprintf(cs,"\n");
-			}
-		}
-
-		if(arg.user_counters)
-			turn_free(arg.user_counters,sizeof(size_t)*arg.users_number);
-		if(arg.user_names) {
-			size_t i;
-			for(i=0;i<arg.users_number;++i) {
-				if(arg.user_names[i])
-					turn_free(arg.user_names[i],strlen(arg.user_names[i])+1);
-			}
-			turn_free(arg.user_names,sizeof(char*) * arg.users_number);
-		}
-		if(arg.users)
-			ur_string_map_free(&arg.users);
-	}
-}
-
-static void cli_print_configuration(struct cli_session* cs)
-{
-	if(cs) {
-		myprintf(cs,"\n");
-
-		cli_print_flag(cs,turn_params.verbose,"verbose",0);
-		cli_print_flag(cs,turn_params.turn_daemon,"daemon process",0);
-		cli_print_flag(cs,turn_params.stale_nonce,"stale-nonce",1);
-		cli_print_flag(cs,turn_params.stun_only,"stun-only",1);
-		cli_print_flag(cs,turn_params.no_stun,"no-stun",1);
-		cli_print_flag(cs,turn_params.secure_stun,"secure-stun",1);
-		cli_print_flag(cs,turn_params.do_not_use_config_file,"do-not-use-config-file",0);
-		cli_print_flag(cs,turn_params.rfc5780,"RFC5780 support",0);
-		cli_print_uint(cs,(unsigned int)turn_params.net_engine_version,"net engine version",0);
-		cli_print_str(cs,turn_params.net_engine_version_txt[(int)turn_params.net_engine_version],"net engine",0);
-		cli_print_flag(cs,turn_params.fingerprint,"enforce fingerprints",0);
-		cli_print_flag(cs,turn_params.mobility,"mobility",1);
-		cli_print_flag(cs,turn_params.udp_self_balance,"udp-self-balance",0);
-		cli_print_str(cs,turn_params.pidfile,"pidfile",0);
-		cli_print_uint(cs,(unsigned long)getuid(),"process user ID",0);
-		cli_print_uint(cs,(unsigned long)getgid(),"process group ID",0);
-
-		{
-			char wd[1025];
-			if(getcwd(wd,sizeof(wd)-1)) {
-				cli_print_str(cs,wd,"process dir",0);
-			}
-		}
-
-		myprintf(cs,"\n");
-
-		if(turn_params.cipher_list[0])
-			cli_print_str(cs,turn_params.cipher_list,"cipher-list",0);
-		else
-			cli_print_str(cs,DEFAULT_CIPHER_LIST,"cipher-list",0);
-
-		cli_print_str(cs,turn_params.ec_curve_name,"ec-curve-name",0);
-		{
-			if(turn_params.dh_key_size == DH_CUSTOM)
-				cli_print_str(cs,turn_params.dh_file,"dh-file",0);
-			else {
-				unsigned int dh_key_length = 1066;
-				if(turn_params.dh_key_size == DH_566)
-					dh_key_length = 566;
-				else if(turn_params.dh_key_size == DH_2066)
-					dh_key_length = 2066;
-				cli_print_uint(cs,(unsigned long)dh_key_length,"DH-key-length",0);
-			}
-		}
-
-		cli_print_str(cs,turn_params.ca_cert_file,"Certificate Authority file",0);
-		cli_print_str(cs,turn_params.cert_file,"Certificate file",0);
-		cli_print_str(cs,turn_params.pkey_file,"Private Key file",0);
-
-		if(turn_params.shatype == SHATYPE_SHA256)
-			cli_print_str(cs,"SHA256","SHA type",0);
-		else
-			cli_print_str(cs,"SHA1","SHA type",0);
-		myprintf(cs,"\n");
-
-		cli_print_str_array(cs,turn_params.listener.addrs,turn_params.listener.addrs_number,"Listener addr",0);
-
-		if(turn_params.listener_ifname[0])
-			cli_print_str(cs,turn_params.listener_ifname,"listener-ifname",0);
-
-		cli_print_flag(cs,turn_params.no_udp,"no-udp",0);
-		cli_print_flag(cs,turn_params.no_tcp,"no-tcp",0);
-		cli_print_flag(cs,turn_params.no_dtls,"no-dtls",0);
-		cli_print_flag(cs,turn_params.no_tls,"no-tls",0);
-
-#ifndef OPENSSL_NO_SSL2
-		cli_print_flag(cs,(!turn_params.no_sslv2 && !turn_params.no_tls),"SSLv2",0);
-#else
-		cli_print_flag(cs,0,"SSLv2",0);
-#endif
-
-		cli_print_flag(cs,(!turn_params.no_sslv3 && !turn_params.no_tls),"SSLv3",0);
-		cli_print_flag(cs,(!turn_params.no_tlsv1 && !turn_params.no_tls),"TLSv1.0",0);
-		cli_print_flag(cs,(!turn_params.no_tlsv1_1 && !turn_params.no_tls),"TLSv1.1",0);
-		cli_print_flag(cs,(!turn_params.no_tlsv1_2 && !turn_params.no_tls),"TLSv1.2",0);
-
-		cli_print_uint(cs,(unsigned long)turn_params.listener_port,"listener-port",0);
-		cli_print_uint(cs,(unsigned long)turn_params.tls_listener_port,"tls-listener-port",0);
-		cli_print_uint(cs,(unsigned long)turn_params.alt_listener_port,"alt-listener-port",0);
-		cli_print_uint(cs,(unsigned long)turn_params.alt_tls_listener_port,"alt-tls-listener-port",0);
-
-		cli_print_addr(cs,turn_params.external_ip,0,"External public IP",0);
-
-		myprintf(cs,"\n");
-
-		cli_print_addr_list(cs,&turn_params.aux_servers_list,1,"Aux server",0);
-		cli_print_addr_list(cs,&turn_params.alternate_servers_list,1,"Alternate server",0);
-		cli_print_addr_list(cs,&turn_params.tls_alternate_servers_list,1,"TLS alternate server",0);
-
-		myprintf(cs,"\n");
-
-		cli_print_str_array(cs,turn_params.relay_addrs,turn_params.relays_number,"Relay addr",0);
-
-		if(turn_params.relay_ifname[0])
-			cli_print_str(cs,turn_params.relay_ifname,"relay-ifname",0);
-
-		cli_print_flag(cs,turn_params.server_relay,"server-relay",0);
-
-		cli_print_flag(cs,turn_params.no_udp_relay,"no-udp-relay",1);
-		cli_print_flag(cs,turn_params.no_tcp_relay,"no-tcp-relay",1);
-
-		cli_print_uint(cs,(unsigned long)turn_params.min_port,"min-port",0);
-		cli_print_uint(cs,(unsigned long)turn_params.max_port,"max-port",0);
-
-		cli_print_ip_range_list(cs,&turn_params.ip_whitelist,"Whitelist IP (static)",0);
-		{
-			ip_range_list_t* l = get_ip_list("allowed");
-			cli_print_ip_range_list(cs,l,"Whitelist IP (dynamic)",0);
-			ip_list_free(l);
-		}
-
-		cli_print_ip_range_list(cs,&turn_params.ip_blacklist,"Blacklist IP (static)",0);
-		{
-			ip_range_list_t* l = get_ip_list("denied");
-			cli_print_ip_range_list(cs,l,"Blacklist IP (dynamic)",0);
-			ip_list_free(l);
-		}
-
-		cli_print_flag(cs,turn_params.no_multicast_peers,"no-multicast-peers",1);
-		cli_print_flag(cs,turn_params.no_loopback_peers,"no-loopback-peers",1);
-
-		myprintf(cs,"\n");
-
-		if(turn_params.default_users_db.persistent_users_db.userdb[0]) {
-			switch(turn_params.default_users_db.userdb_type) {
-#if !defined(TURN_NO_SQLITE)
-			case TURN_USERDB_TYPE_SQLITE:
-				cli_print_str(cs,"SQLite","DB type",0);
-				break;
-#endif
-#if !defined(TURN_NO_PQ)
-			case TURN_USERDB_TYPE_PQ:
-				cli_print_str(cs,"Postgres","DB type",0);
-				break;
-#endif
-#if !defined(TURN_NO_MYSQL)
-			case TURN_USERDB_TYPE_MYSQL:
-				cli_print_str(cs,"MySQL/MariaDB","DB type",0);
-				break;
-#endif
-#if !defined(TURN_NO_MONGO)
-			case TURN_USERDB_TYPE_MONGO:
-				cli_print_str(cs,"MongoDB","DB type",0);
-				break;
-#endif
-#if !defined(TURN_NO_HIREDIS)
-			case TURN_USERDB_TYPE_REDIS:
-				cli_print_str(cs,"redis","DB type",0);
-				break;
-#endif
-			default:
-				cli_print_str(cs,"unknown","DB type",0);
-			};
-			cli_print_str(cs,turn_params.default_users_db.persistent_users_db.userdb,"DB",0);
-		} else {
-			cli_print_str(cs,"none","DB type",0);
-			cli_print_str(cs,"none","DB",0);
-		}
-
-#if !defined(TURN_NO_HIREDIS)
-		if(turn_params.use_redis_statsdb && turn_params.redis_statsdb[0])
-			cli_print_str(cs,turn_params.redis_statsdb,"Redis Statistics DB",0);
-#endif
-
-		myprintf(cs,"\n");
-
-
-		{
-			char * rn = get_realm(NULL)->options.name;
-			if(rn[0])
-				cli_print_str(cs,rn,"Default realm",0);
-		}
-		if(cs->realm[0])
-			cli_print_str(cs,cs->realm,"CLI session realm",0);
-		else
-			cli_print_str(cs,get_realm(NULL)->options.name,"CLI session realm",0);
-		if(cs->origin[0])
-			cli_print_str(cs,cs->origin,"CLI session origin",0);
-		if(turn_params.ct == TURN_CREDENTIALS_LONG_TERM)
-			cli_print_flag(cs,1,"Long-term authorization mechanism",0);
-		else if(turn_params.ct == TURN_CREDENTIALS_SHORT_TERM)
-			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,"TURN REST API support",0);
-		if(turn_params.use_auth_secret_with_timestamp && turn_params.rest_api_separator)
-			cli_print_uint(cs,turn_params.rest_api_separator,"TURN REST API separator ASCII number",0);
-
-		myprintf(cs,"\n");
-
-		cli_print_uint(cs,(unsigned long)cs->rp->status.total_current_allocs,"total-current-allocs",0);
-
-		myprintf(cs,"\n");
-
-		cli_print_uint(cs,(unsigned long)turn_params.total_quota,"Default total-quota",2);
-		cli_print_uint(cs,(unsigned long)turn_params.user_quota,"Default user-quota",2);
-		cli_print_uint(cs,(unsigned long)get_bps_capacity(),"Total server bps-capacity",2);
-		cli_print_uint(cs,(unsigned long)get_bps_capacity_allocated(),"Allocated bps-capacity",0);
-		cli_print_uint(cs,(unsigned long)get_max_bps(),"Default max-bps",2);
-
-		myprintf(cs,"\n");
-
-		cli_print_uint(cs,(unsigned long)cs->rp->options.perf_options.total_quota,"current realm total-quota",0);
-		cli_print_uint(cs,(unsigned long)cs->rp->options.perf_options.user_quota,"current realm user-quota",0);
-		cli_print_uint(cs,(unsigned long)cs->rp->options.perf_options.max_bps,"current realm max-bps",0);
-
-		myprintf(cs,"\n");
-
-		cli_print_uint(cs,(unsigned long)cli_max_output_sessions,"cli-max-output-sessions",2);
-
-		{
-		  myprintf(cs,"\n");
-		  const char *str="  (Note 1: parameters with (*) are toggleable)";
-		  myprintf(cs,"%s\n",str);
-		  myprintf(cs,"\n");
-		  str="  (Note 2: parameters with (**) are changeable)";
-		  myprintf(cs,"%s\n",str);
-		  myprintf(cs,"\n");
-		}
-	}
-}
-
-static void close_cli_session(struct cli_session* cs);
-
-static int run_cli_output(struct cli_session* cs, const char *buf, unsigned int len)
-{
-	if(cs && buf && len) {
-		if(bufferevent_write(cs->bev, buf, len)< 0) {
-			return -1;
-		}
-		return 0;
-	}
-	return -1;
-}
-
-static void close_cli_session(struct cli_session* cs)
-{
-	if(cs) {
-
-		addr_debug_print(cliserver.verbose, &(cs->addr),"CLI session disconnected from");
-
-		if(cs->ts) {
-			telnet_free(cs->ts);
-			cs->ts = NULL;
-		}
-
-		BUFFEREVENT_FREE(cs->bev);
-
-		if(cs->fd>=0) {
-			close(cs->fd);
-			cs->fd = -1;
-		}
-
-		turn_free(cs,sizeof(struct cli_session));
-	}
-}
-
-static void type_cli_cursor(struct cli_session* cs)
-{
-	if(cs && (cs->bev)) {
-	  myprintf(cs, "%s", CLI_CURSOR);
-	}
-}
-
-static void cli_add_alternate_server(struct cli_session* cs, const char* pn)
-{
-	if(cs && cs->ts && pn && *pn) {
-		add_alternate_server(pn);
-	}
-}
-
-static void cli_add_tls_alternate_server(struct cli_session* cs, const char* pn)
-{
-	if(cs && cs->ts && pn && *pn) {
-		add_tls_alternate_server(pn);
-	}
-}
-
-static void cli_del_alternate_server(struct cli_session* cs, const char* pn)
-{
-	if(cs && cs->ts && pn && *pn) {
-		del_alternate_server(pn);
-	}
-}
-
-static void cli_del_tls_alternate_server(struct cli_session* cs, const char* pn)
-{
-	if(cs && cs->ts && pn && *pn) {
-		del_tls_alternate_server(pn);
-	}
-}
-
-static int run_cli_input(struct cli_session* cs, const char *buf0, unsigned int len)
-{
-	int ret = 0;
-
-	if(cs && buf0 && cs->ts && cs->bev) {
-
-		char *buf = (char*)turn_malloc(len+1);
-		ns_bcopy(buf0,buf,len);
-		buf[len]=0;
-
-		char *cmd = buf;
-
-		while((cmd[0]==' ') || (cmd[0]=='\t')) ++cmd;
-
-		size_t sl = strlen(cmd);
-
-		while(sl) {
-			char c = cmd[sl-1];
-			if((c==10)||(c==13)) {
-				cmd[sl-1]=0;
-				--sl;
-			} else {
-				break;
-			}
-		}
-
-		if(sl) {
-			cs->cmds += 1;
-			if(cli_password[0] && !(cs->auth_completed)) {
-				if(strcmp(cmd,cli_password)) {
-					if(cs->cmds>=CLI_PASSWORD_TRY_NUMBER) {
-						addr_debug_print(1, &(cs->addr),"CLI authentication error");
-						TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"CLI authentication error\n");
-						close_cli_session(cs);
-					} else {
-						const char* ipwd="Enter password: ";
-						myprintf(cs,"%s\n",ipwd);
-					}
-				} else {
-					cs->auth_completed = 1;
-					addr_debug_print(1, &(cs->addr),"CLI authentication success");
-					type_cli_cursor(cs);
-				}
-			} else if((strcmp(cmd,"bye") == 0)||(strcmp(cmd,"quit") == 0)||(strcmp(cmd,"exit") == 0)||(strcmp(cmd,"q") == 0)) {
-				const char* str="Bye !";
-				myprintf(cs,"%s\n",str);
-				close_cli_session(cs);
-				ret = -1;
-			} else if((strcmp(cmd,"halt") == 0)||(strcmp(cmd,"shutdown") == 0)||(strcmp(cmd,"stop") == 0)) {
-				addr_debug_print(1, &(cs->addr),"Shutdown command received from CLI user");
-				const char* str="TURN server is shutting down";
-				myprintf(cs,"%s\n",str);
-				close_cli_session(cs);
-				turn_params.stop_turn_server = 1;
-				sleep(10);
-				exit(0);
-			} else if((strcmp(cmd,"?") == 0)||(strcmp(cmd,"h") == 0)||(strcmp(cmd,"help") == 0)) {
-				print_str_array(cs, CLI_GREETING_STR);
-				print_str_array(cs, CLI_HELP_STR);
-				type_cli_cursor(cs);
-			} else if(strcmp(cmd,"pc")==0) {
-				cli_print_configuration(cs);
-				type_cli_cursor(cs);
-			} else if(strstr(cmd,"tc ") == cmd) {
-				toggle_cli_param(cs,cmd+3);
-			} else if(strstr(cmd,"sr ") == cmd) {
-				STRCPY(cs->realm,cmd+3);
-				cs->rp = get_realm(cs->realm);
-				type_cli_cursor(cs);
-			} else if(strcmp(cmd,"ur") == 0) {
-				cs->realm[0]=0;
-				cs->rp = get_realm(NULL);
-				type_cli_cursor(cs);
-			} else if(strstr(cmd,"so ") == cmd) {
-				STRCPY(cs->origin,cmd+3);
-				type_cli_cursor(cs);
-			} else if(strcmp(cmd,"uo") == 0) {
-				cs->origin[0]=0;
-				type_cli_cursor(cs);
-			} else if(strstr(cmd,"tc") == cmd) {
-				toggle_cli_param(cs,cmd+2);
-				type_cli_cursor(cs);
-			} else if(strstr(cmd,"psp") == cmd) {
-				print_sessions(cs,cmd+3,0,0);
-				type_cli_cursor(cs);
-			} else if(strstr(cmd,"psd") == cmd) {
-				cmd += 3;
-				while(cmd[0]==' ') ++cmd;
-				if(!(cmd[0])) {
-					const char* str="You have to provide file name for ps dump\n";
-					myprintf(cs,"%s\n",str);
-				} else {
-					cs->f = fopen(cmd,"w");
-					if(!(cs->f)) {
-						const char* str="Cannot open file for writing\n";
-						myprintf(cs,"%s\n",str);
-					} else {
-						print_sessions(cs,"",1,0);
-						fclose(cs->f);
-						cs->f = NULL;
-					}
-				}
-				type_cli_cursor(cs);
-			} else if(strstr(cmd,"pu ") == cmd) {
-				print_sessions(cs,cmd+3,0,1);
-				type_cli_cursor(cs);
-			} else if(!strcmp(cmd,"pu")) {
-				print_sessions(cs,cmd+2,0,1);
-				type_cli_cursor(cs);
-			} else if(strstr(cmd,"ps") == cmd) {
-				print_sessions(cs,cmd+2,1,0);
-				type_cli_cursor(cs);
-			} else if(strstr(cmd,"cs ") == cmd) {
-				cancel_session(cs,cmd+3);
-				type_cli_cursor(cs);
-			} else if(strstr(cmd,"lr") == cmd) {
-				log_reset(cs);
-				type_cli_cursor(cs);
-			} else if(strstr(cmd,"cc ") == cmd) {
-				change_cli_param(cs,cmd+3);
-				type_cli_cursor(cs);
-			} else if(strstr(cmd,"cc") == cmd) {
-				change_cli_param(cs,cmd+2);
-				type_cli_cursor(cs);
-			} else if(strstr(cmd,"aas ") == cmd) {
-				cli_add_alternate_server(cs,cmd+4);
-				type_cli_cursor(cs);
-			} else if(strstr(cmd,"atas ") == cmd) {
-				cli_add_tls_alternate_server(cs,cmd+5);
-				type_cli_cursor(cs);
-			} else if(strstr(cmd,"das ") == cmd) {
-				cli_del_alternate_server(cs,cmd+4);
-				type_cli_cursor(cs);
-			} else if(strstr(cmd,"dtas ") == cmd) {
-				cli_del_tls_alternate_server(cs,cmd+5);
-				type_cli_cursor(cs);
-			} else {
-				const char* str="Unknown command\n";
-				myprintf(cs,"%s\n",str);
-				type_cli_cursor(cs);
-			}
-		} else {
-			type_cli_cursor(cs);
-		}
-
-		turn_free(buf,len+1);
-	}
-
-	return ret;
-}
-
-static void cli_socket_input_handler_bev(struct bufferevent *bev, void* arg)
-{
-	if (bev && arg) {
-
-		struct cli_session* cs = (struct cli_session*) arg;
-
-		if(!(cs->ts))
-			return;
-
-		stun_buffer buf;
-
-		if(cs->bev) {
-
-			int len = (int)bufferevent_read(cs->bev, buf.buf, STUN_BUFFER_SIZE-1);
-			if(len < 0) {
-				close_cli_session(cs);
-				return;
-			} else if(len == 0) {
-				return;
-			}
-
-			buf.len = len;
-			buf.offset = 0;
-			buf.buf[len]=0;
-
-			telnet_recv(cs->ts, (const char *)buf.buf, (unsigned int)(buf.len));
-		}
-	}
-}
-
-static void cli_eventcb_bev(struct bufferevent *bev, short events, void *arg)
-{
-	UNUSED_ARG(bev);
-
-	if (events & BEV_EVENT_CONNECTED) {
-		// Connect okay
-	} else if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF)) {
-		if (arg) {
-
-			struct cli_session* cs = (struct cli_session*) arg;
-
-			close_cli_session(cs);
-		}
-	}
-}
-
-static void cli_telnet_event_handler(telnet_t *telnet, telnet_event_t *event, void *user_data)
-{
-	if (user_data && telnet) {
-
-		struct cli_session *cs = (struct cli_session *) user_data;
-
-		switch (event->type){
-		case TELNET_EV_DATA:
-			run_cli_input(cs, event->data.buffer, event->data.size);
-			break;
-		case TELNET_EV_SEND:
-			run_cli_output(cs, event->data.buffer, event->data.size);
-			break;
-		case TELNET_EV_ERROR:
-			TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TELNET error: %s", event->error.msg);
-			break;
-		default:
-			;
-		};
-	}
-}
-
-static void cliserver_input_handler(struct evconnlistener *l, evutil_socket_t fd,
-				struct sockaddr *sa, int socklen, void *arg)
-{
-	UNUSED_ARG(l);
-	UNUSED_ARG(arg);
-	UNUSED_ARG(socklen);
-
-	addr_debug_print(cliserver.verbose, (ioa_addr*)sa,"CLI connected to");
-
-	struct cli_session *clisession = (struct cli_session*)turn_malloc(sizeof(struct cli_session));
-	ns_bzero(clisession,sizeof(struct cli_session));
-
-	clisession->rp = get_realm(NULL);
-
-	set_socket_options_fd(fd, 1, sa->sa_family);
-
-	clisession->fd = fd;
-
-	addr_cpy(&(clisession->addr),(ioa_addr*)sa);
-
-	clisession->bev = bufferevent_socket_new(cliserver.event_base,
-					fd,
-					TURN_BUFFEREVENTS_OPTIONS);
-	debug_ptr_add(clisession->bev);
-	bufferevent_setcb(clisession->bev, cli_socket_input_handler_bev, NULL,
-			cli_eventcb_bev, clisession);
-	bufferevent_setwatermark(clisession->bev, EV_READ|EV_WRITE, 0, BUFFEREVENT_HIGH_WATERMARK);
-	bufferevent_enable(clisession->bev, EV_READ); /* Start reading. */
-
-	clisession->ts = telnet_init(cli_telopts, cli_telnet_event_handler, 0, clisession);
-
-	if(!(clisession->ts)) {
-		const char *str = "Cannot open telnet session\n";
-		addr_debug_print(cliserver.verbose, (ioa_addr*)sa,str);
-		close_cli_session(clisession);
-	} else {
-	  print_str_array(clisession, CLI_GREETING_STR);
-	  telnet_printf(clisession->ts,"\n");
-	  telnet_printf(clisession->ts,"Type '?' for help\n");
-	  if(cli_password[0]) {
-	    const char* ipwd="Enter password: ";
-	    telnet_printf(clisession->ts,"%s\n",ipwd);
-	  } else {
-	    type_cli_cursor(clisession);
-	  }
-	}
-}
-
-void setup_cli_thread(void)
-{
-	cliserver.event_base = turn_event_base_new();
-	TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"IO method (cli thread): %s\n",event_base_get_method(cliserver.event_base));
-
-	struct bufferevent *pair[2];
-
-	bufferevent_pair_new(cliserver.event_base, TURN_BUFFEREVENTS_OPTIONS, pair);
-	cliserver.in_buf = pair[0];
-	cliserver.out_buf = pair[1];
-	bufferevent_setcb(cliserver.in_buf, cli_server_receive_message, NULL, NULL, &cliserver);
-	bufferevent_enable(cliserver.in_buf, EV_READ);
-
-	if(!cli_addr_set) {
-		if(make_ioa_addr((const u08bits*)CLI_DEFAULT_IP,0,&cli_addr)<0) {
-			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot set cli address %s\n",CLI_DEFAULT_IP);
-			return;
-		}
-	}
-
-	addr_set_port(&cli_addr,cli_port);
-
-	cliserver.listen_fd = socket(cli_addr.ss.sa_family, SOCK_STREAM, 0);
-	if (cliserver.listen_fd < 0) {
-	    perror("socket");
-	    TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot open CLI socket\n");
-	    return;
-	}
-
-	if(addr_bind(cliserver.listen_fd,&cli_addr,1)<0) {
-	  perror("Cannot bind CLI socket to addr");
-	  char saddr[129];
-	  addr_to_string(&cli_addr,(u08bits*)saddr);
-	  TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot bind CLI listener socket to addr %s\n",saddr);
-	  socket_closesocket(cliserver.listen_fd);
-	  return;
-	}
-
-	socket_tcp_set_keepalive(cliserver.listen_fd);
-
-	socket_set_nonblocking(cliserver.listen_fd);
-
-	cliserver.l = evconnlistener_new(cliserver.event_base,
-			  cliserver_input_handler, &cliserver,
-			  LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
-			  1024, cliserver.listen_fd);
-
-	if(!(cliserver.l)) {
-	  TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,"Cannot create CLI listener\n");
-	  socket_closesocket(cliserver.listen_fd);
-	  return;
-	}
-
-	cliserver.sessions = ur_map_create();
-
-	addr_debug_print(cliserver.verbose, &cli_addr,"CLI listener opened on ");
-}
-
-void cli_server_receive_message(struct bufferevent *bev, void *ptr)
-{
-	UNUSED_ARG(ptr);
-
-	struct turn_session_info *tsi = (struct turn_session_info*)turn_malloc(sizeof(struct turn_session_info));
-	turn_session_info_init(tsi);
-	int n = 0;
-	struct evbuffer *input = bufferevent_get_input(bev);
-
-	while ((n = evbuffer_remove(input, tsi, sizeof(struct turn_session_info))) > 0) {
-		if (n != sizeof(struct turn_session_info)) {
-			fprintf(stderr,"%s: Weird CLI buffer error: size=%d\n",__FUNCTION__,n);
-			continue;
-		}
-
-		ur_map_value_type t = 0;
-		if (ur_map_get(cliserver.sessions, (ur_map_key_type)tsi->id, &t) && t) {
-			struct turn_session_info *old = (struct turn_session_info*)t;
-			turn_session_info_clean(old);
-			turn_free(old,sizeof(struct turn_session_info));
-			ur_map_del(cliserver.sessions, (ur_map_key_type)tsi->id, NULL);
-		}
-
-		if(tsi->valid) {
-			ur_map_put(cliserver.sessions, (ur_map_key_type)tsi->id, (ur_map_value_type)tsi);
-			tsi = (struct turn_session_info*)turn_malloc(sizeof(struct turn_session_info));
-			turn_session_info_init(tsi);
-		} else {
-			turn_session_info_clean(tsi);
-		}
-	}
-
-	if(tsi) {
-		turn_session_info_clean(tsi);
-		turn_free(tsi,sizeof(struct turn_session_info));
-	}
-}
-
-int send_turn_session_info(struct turn_session_info* tsi)
-{
-	int ret = -1;
-
-	if(!use_cli)
-		return ret;
-
-	if(tsi) {
-		struct evbuffer *output = bufferevent_get_output(cliserver.out_buf);
-		if(output) {
-			if(evbuffer_add(output,tsi,sizeof(struct turn_session_info))>=0) {
-				ret = 0;
-			}
-		}
-	}
-
-	return ret;
-}
-
-///////////////////////////////

+ 100 - 76
src/apps/relay/userdb.c

@@ -405,7 +405,7 @@ int get_user_key(int in_oauth, int *out_oauth, int *max_session_time, u08bits *u
 	if(max_session_time)
 		*max_session_time = 0;
 
-	if(in_oauth && out_oauth && usname && usname[0] && realm && realm[0]) {
+	if(in_oauth && out_oauth && usname && usname[0]) {
 
 		stun_attr_ref sar = stun_attr_get_first_by_type_str(ioa_network_buffer_data(nbh),
 								ioa_network_buffer_get_size(nbh),
@@ -471,6 +471,10 @@ int get_user_key(int in_oauth, int *out_oauth, int *max_session_time, u08bits *u
 					const char* server_name = (char*)turn_params.oauth_server_name;
 					if(!(server_name && server_name[0])) {
 						server_name = (char*)realm;
+						if(!(server_name && server_name[0])) {
+							TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot determine oAuth server name");
+							return -1;
+						}
 					}
 
 					if (decode_oauth_token((const u08bits *) server_name, &etoken,&okey, &dot) < 0) {
@@ -496,7 +500,7 @@ int get_user_key(int in_oauth, int *out_oauth, int *max_session_time, u08bits *u
 						return -1;
 					};
 
-					st_password_t pwdtmp;
+					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),
@@ -549,7 +553,7 @@ int get_user_key(int in_oauth, int *out_oauth, int *max_session_time, u08bits *u
 
 			u08bits hmac[MAXSHASIZE];
 			unsigned int hmac_len;
-			st_password_t pwdtmp;
+			password_t pwdtmp;
 
 			hmac[0] = 0;
 
@@ -620,12 +624,6 @@ int get_user_key(int in_oauth, int *out_oauth, int *max_session_time, u08bits *u
 	ur_string_map_lock(turn_params.default_users_db.ram_db.static_accounts);
 	if(ur_string_map_get(turn_params.default_users_db.ram_db.static_accounts, (ur_string_map_key_type)usname, &ukey)) {
 		ret = 0;
-	} else {
-		ur_string_map_lock(turn_params.default_users_db.ram_db.dynamic_accounts);
-		if(ur_string_map_get(turn_params.default_users_db.ram_db.dynamic_accounts, (ur_string_map_key_type)usname, &ukey)) {
-			ret = 0;
-		}
-		ur_string_map_unlock(turn_params.default_users_db.ram_db.dynamic_accounts);
 	}
 	ur_string_map_unlock(turn_params.default_users_db.ram_db.static_accounts);
 
@@ -643,21 +641,6 @@ int get_user_key(int in_oauth, int *out_oauth, int *max_session_time, u08bits *u
 	return ret;
 }
 
-/*
- * Short-term mechanism password retrieval
- */
-int get_user_pwd(u08bits *usname, st_password_t pwd)
-{
-	int ret = -1;
-
-	const turn_dbdriver_t * dbd = get_dbdriver();
-	if (dbd && dbd->get_user_pwd) {
-		ret = (*dbd->get_user_pwd)(usname, pwd);
-	}
-
-	return ret;
-}
-
 u08bits *start_user_check(turnserver_id id, turn_credential_type ct, int in_oauth, int *out_oauth, u08bits *usname, u08bits *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, u64bits ctxkey, int *postpone_reply)
 {
 	*postpone_reply = 1;
@@ -736,7 +719,7 @@ void release_allocation_quota(u08bits *user, int oauth, u08bits *realm)
 
 //////////////////////////////////
 
-int add_user_account(char *user, int dynamic)
+int add_static_user_account(char *user)
 {
 	/* Realm is either default or empty for users taken from file or command-line */
 	if(user && !turn_params.use_auth_secret_with_timestamp) {
@@ -770,11 +753,7 @@ int add_user_account(char *user, int dynamic)
 				//this is only for default realm
 				stun_produce_integrity_key_str((u08bits*)usname, (u08bits*)get_realm(NULL)->options.name, (u08bits*)s, *key, turn_params.shatype);
 			}
-			if(dynamic) {
-				ur_string_map_lock(turn_params.default_users_db.ram_db.dynamic_accounts);
-				ur_string_map_put(turn_params.default_users_db.ram_db.dynamic_accounts, (ur_string_map_key_type)usname, (ur_string_map_value_type)*key);
-				ur_string_map_unlock(turn_params.default_users_db.ram_db.dynamic_accounts);
-			} else {
+			{
 				ur_string_map_lock(turn_params.default_users_db.ram_db.static_accounts);
 				ur_string_map_put(turn_params.default_users_db.ram_db.static_accounts, (ur_string_map_key_type)usname, (ur_string_map_value_type)*key);
 				ur_string_map_unlock(turn_params.default_users_db.ram_db.static_accounts);
@@ -790,11 +769,19 @@ int add_user_account(char *user, int dynamic)
 
 ////////////////// Admin /////////////////////////
 
-static int list_users(int is_st, u08bits *realm)
+static int list_users(u08bits *realm, int is_admin)
 {
   const turn_dbdriver_t * dbd = get_dbdriver();
-  if (dbd && dbd->list_users) {
-    (*dbd->list_users)(is_st, realm);
+  if (dbd) {
+	  if(is_admin) {
+		  if(dbd->list_admin_users) {
+		  	(*dbd->list_admin_users)(0);
+		  }
+	  } else {
+		  if(dbd->list_users) {
+			  (*dbd->list_users)(realm,NULL,NULL);
+		  }
+	  }
   }
 
   return 0;
@@ -802,26 +789,24 @@ static int list_users(int is_st, u08bits *realm)
 
 static int show_secret(u08bits *realm)
 {
-	must_set_admin_realm(realm);
-
   const turn_dbdriver_t * dbd = get_dbdriver();
-  if (dbd && dbd->show_secret) {
-    (*dbd->show_secret)(realm);
-	}
+  if (dbd && dbd->list_secrets) {
+    (*dbd->list_secrets)(realm,NULL,NULL);
+  }
 
-	return 0;
+  return 0;
 }
 
 static int del_secret(u08bits *secret, u08bits *realm) {
 
 	must_set_admin_realm(realm);
 
-  const turn_dbdriver_t * dbd = get_dbdriver();
-  if (dbd && dbd->del_secret) {
-    (*dbd->del_secret)(secret, realm);
+	const turn_dbdriver_t * dbd = get_dbdriver();
+	if (dbd && dbd->del_secret) {
+		(*dbd->del_secret)(secret, realm);
 	}
 
-  return 0;
+	return 0;
 }
 
 static int set_secret(u08bits *secret, u08bits *realm) {
@@ -833,9 +818,9 @@ static int set_secret(u08bits *secret, u08bits *realm) {
 
 	del_secret(secret, realm);
 
-  const turn_dbdriver_t * dbd = get_dbdriver();
-  if (dbd && dbd->set_secret) {
-    (*dbd->set_secret)(secret, realm);
+	const turn_dbdriver_t * dbd = get_dbdriver();
+	if (dbd && dbd->set_secret) {
+		(*dbd->set_secret)(secret, realm);
 	}
 
 	return 0;
@@ -847,9 +832,9 @@ static int add_origin(u08bits *origin0, u08bits *realm)
 
 	get_canonic_origin((const char *)origin0, (char *)origin, sizeof(origin)-1);
 
-  const turn_dbdriver_t * dbd = get_dbdriver();
-  if (dbd && dbd->add_origin) {
-    (*dbd->add_origin)(origin, realm);
+	const turn_dbdriver_t * dbd = get_dbdriver();
+	if (dbd && dbd->add_origin) {
+		(*dbd->add_origin)(origin, realm);
 	}
 
 	return 0;
@@ -861,9 +846,9 @@ static int del_origin(u08bits *origin0)
 
 	get_canonic_origin((const char *)origin0, (char *)origin, sizeof(origin)-1);
 
-  const turn_dbdriver_t * dbd = get_dbdriver();
-  if (dbd && dbd->del_origin) {
-    (*dbd->del_origin)(origin);
+	const turn_dbdriver_t * dbd = get_dbdriver();
+	if (dbd && dbd->del_origin) {
+		(*dbd->del_origin)(origin);
 	}
 
 	return 0;
@@ -873,10 +858,10 @@ static int list_origins(u08bits *realm)
 {
   const turn_dbdriver_t * dbd = get_dbdriver();
   if (dbd && dbd->list_origins) {
-    (*dbd->list_origins)(realm);
-	}
+    (*dbd->list_origins)(realm,NULL,NULL);
+  }
 
-	return 0;
+  return 0;
 }
 
 static int set_realm_option_one(u08bits *realm, unsigned long value, const char* opt)
@@ -884,9 +869,9 @@ static int set_realm_option_one(u08bits *realm, unsigned long value, const char*
 	if(value == (unsigned long)-1)
 		return 0;
 
-  const turn_dbdriver_t * dbd = get_dbdriver();
-  if (dbd && dbd->set_realm_option_one) {
-    (*dbd->set_realm_option_one)(realm, value, opt);
+	const turn_dbdriver_t * dbd = get_dbdriver();
+	if (dbd && dbd->set_realm_option_one) {
+		(*dbd->set_realm_option_one)(realm, value, opt);
 	}
 
 	return 0;
@@ -910,15 +895,13 @@ static int list_realm_options(u08bits *realm)
 	return 0;
 }
 
-int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08bits *origin, TURNADMIN_COMMAND_TYPE ct, int is_st, perf_options_t *po)
+int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08bits *origin, TURNADMIN_COMMAND_TYPE ct, perf_options_t *po, int is_admin)
 {
 	hmackey_t key;
 	char skey[sizeof(hmackey_t) * 2 + 1];
 
-	st_password_t passwd;
-
 	if (ct == TA_LIST_USERS) {
-		return list_users(is_st, realm);
+		return list_users(realm, is_admin);
 	}
 
 	if (ct == TA_LIST_ORIGINS) {
@@ -963,13 +946,11 @@ int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08b
 
 	must_set_admin_user(user);
 
-	if (ct != TA_DELETE_USER) {
+	if (ct != TA_DELETE_USER && !is_admin) {
 
 		must_set_admin_pwd(pwd);
 
-		if (is_st) {
-			strncpy((char*) passwd, (char*) pwd, sizeof(st_password_t));
-		} else {
+		{
 			stun_produce_integrity_key_str(user, realm, pwd, key, turn_params.shatype);
 			size_t i = 0;
 			size_t sz = get_hmackey_size(turn_params.shatype);
@@ -988,23 +969,29 @@ int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08b
 
 	if (ct == TA_PRINT_KEY) {
 
-		if (!is_st) {
-			printf("0x%s\n", skey);
-		}
+		printf("0x%s\n", skey);
 
 	} else if (dbd) {
 
-		if (!is_st) {
+		if(!is_admin)
 			must_set_admin_realm(realm);
-		}
 
 		if (ct == TA_DELETE_USER) {
-			if (dbd->del_user)
-				(*dbd->del_user)(user, is_st, realm);
+			if(is_admin) {
+				if (dbd->del_admin_user)
+					(*dbd->del_admin_user)(user);
+			} else {
+				if (dbd->del_user)
+					(*dbd->del_user)(user, realm);
+			}
 		} else if (ct == TA_UPDATE_USER) {
-			if (is_st) {
-				if (dbd->set_user_pwd)
-					(*dbd->set_user_pwd)(user, passwd);
+			if(is_admin) {
+				must_set_admin_pwd(pwd);
+				if (dbd->set_admin_user) {
+					password_t password;
+					STRCPY(password,pwd);
+					(*dbd->set_admin_user)(user, realm, password);
+				}
 			} else {
 				if (dbd->set_user_key)
 					(*dbd->set_user_key)(user, realm, skey);
@@ -1279,6 +1266,43 @@ int add_ip_list_range(const char * range0, const char * realm, ip_range_list_t *
 	return 0;
 }
 
+int check_ip_list_range(const char * range0)
+{
+	char *range = turn_strdup(range0);
+
+	char* separator = strchr(range, '-');
+
+	if (separator) {
+		*separator = '\0';
+	}
+
+	ioa_addr min, max;
+
+	if (make_ioa_addr((const u08bits*) range, 0, &min) < 0) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address range format: %s\n", range);
+		turn_free(range,0);
+		return -1;
+	}
+
+	if (separator) {
+		if (make_ioa_addr((const u08bits*) separator + 1, 0, &max) < 0) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address range format: %s\n", separator + 1);
+			turn_free(range,0);
+			return -1;
+		}
+	} else {
+		// Doesn't have a '-' character in it, so assume that this is a single address
+		addr_cpy(&max, &min);
+	}
+
+	if (separator)
+		*separator = '-';
+
+	turn_free(range,0);
+
+	return 0;
+}
+
 /////////// REALM //////////////
 
 void reread_realms(void)

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

@@ -46,10 +46,6 @@
 extern "C" {
 #endif
 
-//////////// Defines //////////////////////////////
-
-#define AUTH_SECRET_SIZE (512)
-
 //////////// REALM //////////////
 
 struct _realm_status_t;
@@ -90,7 +86,7 @@ struct auth_message {
 	u08bits username[STUN_MAX_USERNAME_SIZE + 1];
 	u08bits realm[STUN_MAX_REALM_SIZE + 1];
 	hmackey_t key;
-	st_password_t pwd;
+	password_t pwd;
 	get_username_resume_cb resume_func;
 	ioa_net_data in_buffer;
 	u64bits ctxkey;
@@ -153,7 +149,6 @@ typedef struct _secrets_list secrets_list_t;
 typedef struct _ram_users_db_t {
 	size_t users_number;
 	ur_string_map *static_accounts;
-	ur_string_map *dynamic_accounts;
 	secrets_list_t static_auth_secrets;
 } ram_users_db_t;
 
@@ -191,7 +186,6 @@ void add_to_secrets_list(secrets_list_t *sl, const char* elem);
 /////////// USER DB CHECK //////////////////
 
 int get_user_key(int in_oauth, int *out_oauth, int *max_session_time, u08bits *uname, u08bits *realm, hmackey_t key, ioa_network_buffer_handle nbh);
-int get_user_pwd(u08bits *uname, st_password_t pwd);
 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);
@@ -204,10 +198,11 @@ void release_allocation_quota(u08bits *username, int oauth, u08bits *realm);
 
 void auth_ping(redis_context_handle rch);
 void reread_realms(void);
-int add_user_account(char *user, int dynamic);
-int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08bits *origin, TURNADMIN_COMMAND_TYPE ct, int is_st, perf_options_t* po);
+int add_static_user_account(char *user);
+int adminuser(u08bits *user, u08bits *realm, u08bits *pwd, u08bits *secret, u08bits *origin, TURNADMIN_COMMAND_TYPE ct, perf_options_t* po, int is_admin);
 
 int add_ip_list_range(const char* range, const char* realm, ip_range_list_t * list);
+int check_ip_list_range(const char* range);
 ip_range_list_t* get_ip_list(const char *kind);
 void ip_list_free(ip_range_list_t *l);
 

+ 3 - 27
src/apps/uclient/mainuclient.c

@@ -56,14 +56,13 @@ int c2c=0;
 int clnet_verbose=TURN_VERBOSE_NONE;
 int use_tcp=0;
 int use_secure=0;
-int use_short_term=0;
 int hang_on=0;
 ioa_addr peer_addr;
 int no_rtcp = 0;
 int default_address_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT;
 int dont_fragment = 0;
 u08bits g_uname[STUN_MAX_USERNAME_SIZE+1];
-st_password_t g_upwd;
+password_t g_upwd;
 char g_auth_secret[1025]="\0";
 int g_use_auth_secret_with_timestamp = 0;
 int use_fingerprints = 1;
@@ -125,8 +124,6 @@ static char Usage[] =
   "	-x	IPv6 relay address requested.\n"
   "	-X	IPv4 relay address explicitly requested.\n"
   "	-g	Include DONT_FRAGMENT option.\n"
-  "	-A	Use short-term credentials mechanism. By default, the program uses\n"
-  "		the long-term credentials mechanism if authentication is required.\n"
   "	-D	Mandatory channel padding (like in pjnath).\n"
   "	-N	Negative tests (some limited cases only).\n"
   "	-R	Negative protocol tests.\n"
@@ -139,7 +136,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"
+  "	-J	Use oAuth with default test key kid='north' or 'oldempire'.\n"
   "Options:\n"
   "	-l	Message length (Default: 100 Bytes).\n"
   "	-i	Certificate file (for secure connections only, optional).\n"
@@ -219,11 +216,6 @@ int main(int argc, char **argv)
 
 			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]);
@@ -297,13 +289,6 @@ int main(int argc, char **argv)
 		case 'Z':
 			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':
 			STRCPY(g_uname, optarg);
 			break;
@@ -409,10 +394,6 @@ int main(int argc, char **argv)
 
 	if(g_use_auth_secret_with_timestamp) {
 
-		if(use_short_term) {
-			fprintf(stderr,"ERROR: You cannot use authentication secret (REST API) with short-term credentials mechanism.\n");
-			exit(-1);
-		}
 		{
 			char new_uname[1025];
 			const unsigned long exp_time = 3600 * 24; /* one day */
@@ -496,18 +477,13 @@ int main(int argc, char **argv)
 		SSL_load_error_strings();
 		OpenSSL_add_ssl_algorithms();
 
-		const char *csuite = "ALL:SSLv2"; //"AES256-SHA" "DH"
+		const char *csuite = "ALL"; //"AES256-SHA" "DH"
 		if(use_null_cipher)
 			csuite = "eNULL";
 		else if(cipher_suite[0])
 			csuite=cipher_suite;
 
 		if(use_tcp) {
-#ifndef OPENSSL_NO_SSL2
-		  root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(SSLv2_client_method());
-		  SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite);
-		  root_tls_ctx_num++;
-#endif
 		  root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(SSLv23_client_method());
 		  SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite);
 		  root_tls_ctx_num++;

+ 5 - 10
src/apps/uclient/startuclient.c

@@ -448,7 +448,7 @@ static int clnet_allocate(int verbose,
 						allocate_received = 1;
 						allocate_finished = 1;
 
-						if(clnet_info->nonce[0] || use_short_term) {
+						if(clnet_info->nonce[0]) {
 							if(check_integrity(clnet_info, &response_message)<0)
 								return -1;
 						}
@@ -528,16 +528,11 @@ static int clnet_allocate(int verbose,
 						goto beg_allocate;
 					} else if (stun_is_error_response(&response_message, &err_code,err_msg,sizeof(err_msg))) {
 
-						if(err_code == SHA_TOO_WEAK_ERROR_CODE && (clnet_info->shatype == SHATYPE_SHA1) && use_short_term) {
-							clnet_info->shatype = SHATYPE_SHA256;
-							goto beg_allocate;
-						}
-
 						allocate_received = 1;
 
 						if(err_code == 300) {
 
-							if(clnet_info->nonce[0] || use_short_term) {
+							if(clnet_info->nonce[0]) {
 								if(check_integrity(clnet_info, &response_message)<0)
 									return -1;
 							}
@@ -797,7 +792,7 @@ static int turn_channel_bind(int verbose, uint16_t *chn,
 
 					cb_received = 1;
 
-					if(clnet_info->nonce[0] || use_short_term) {
+					if(clnet_info->nonce[0]) {
 						if(check_integrity(clnet_info, &response_message)<0)
 							return -1;
 					}
@@ -906,7 +901,7 @@ static int turn_create_permission(int verbose, app_ur_conn_info *clnet_info,
 
 					cp_received = 1;
 
-					if(clnet_info->nonce[0] || use_short_term) {
+					if(clnet_info->nonce[0]) {
 						if(check_integrity(clnet_info, &response_message)<0)
 							return -1;
 					}
@@ -1481,7 +1476,7 @@ static int turn_tcp_connection_bind(int verbose, app_ur_conn_info *clnet_info, a
 				u08bits err_msg[129];
 				if (stun_is_success_response(&response_message)) {
 
-					if(clnet_info->nonce[0] || use_short_term) {
+					if(clnet_info->nonce[0]) {
 						if(check_integrity(clnet_info, &response_message)<0)
 							return -1;
 					}

+ 4 - 21
src/apps/uclient/uclient.c

@@ -678,12 +678,6 @@ 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) {
-
-				if(check_integrity(&(elem->pinfo), &(elem->in_buffer))<0)
-					return -1;
-			}
-
 			uint16_t method = stun_get_method(&elem->in_buffer);
 
 			if((method == STUN_METHOD_CONNECTION_ATTEMPT)&& is_TCP_relay()) {
@@ -733,7 +727,7 @@ 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) {
+			if(elem->pinfo.nonce[0]) {
 				if(check_integrity(&(elem->pinfo), &(elem->in_buffer))<0)
 					return -1;
 			}
@@ -908,10 +902,6 @@ static int client_write(app_ur_session *elem) {
     if(dont_fragment)
 	    stun_attr_add(&(elem->out_buffer), STUN_ATTRIBUTE_DONT_FRAGMENT, NULL, 0);
 
-    if (use_short_term) {
-	    if(add_integrity(&(elem->pinfo), &(elem->out_buffer))<0) return -1;
-    }
-
     if(use_fingerprints)
 	    stun_attr_add_fingerprint_str(elem->out_buffer.buf,(size_t*)&(elem->out_buffer.len));
   }
@@ -1590,19 +1580,12 @@ void start_mclient(const char *remote_address, int port,
 
 turn_credential_type get_turn_credentials_type(void)
 {
-	if(use_short_term)
-		return TURN_CREDENTIALS_SHORT_TERM;
 	return TURN_CREDENTIALS_LONG_TERM;
 }
 
 int add_integrity(app_ur_conn_info *clnet_info, stun_buffer *message)
 {
-	if(use_short_term) {
-		if(stun_attr_add_integrity_by_user_short_term_str(message->buf, (size_t*)&(message->len), g_uname, g_upwd, clnet_info->shatype)<0) {
-			TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO," Cannot add integrity to the message\n");
-			return -1;
-		}
-	} else if(clnet_info->nonce[0]) {
+	if(clnet_info->nonce[0]) {
 
 		if(oauth && clnet_info->oauth) {
 
@@ -1654,7 +1637,7 @@ int add_integrity(app_ur_conn_info *clnet_info, stun_buffer *message)
 
 			//self-test:
 			{
-				st_password_t pwd;
+				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");
@@ -1679,7 +1662,7 @@ int check_integrity(app_ur_conn_info *clnet_info, stun_buffer *message)
 
 	if(oauth && clnet_info->oauth) {
 
-		st_password_t pwd;
+		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);

+ 3 - 2
src/apps/uclient/uclient.h

@@ -36,6 +36,8 @@
 #include "session.h"
 
 #include <openssl/ssl.h>
+#include <openssl/dh.h>
+#include <openssl/bn.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -51,7 +53,6 @@ extern int do_not_use_channel;
 extern int clnet_verbose;
 extern int use_tcp;
 extern int use_secure;
-extern int use_short_term;
 extern char cert_file[1025];
 extern char pkey_file[1025];
 extern int hang_on;
@@ -61,7 +62,7 @@ extern int no_rtcp;
 extern int default_address_family;
 extern int dont_fragment;
 extern u08bits g_uname[STUN_MAX_USERNAME_SIZE+1];
-extern st_password_t g_upwd;
+extern password_t g_upwd;
 extern char g_auth_secret[1025];
 extern int g_use_auth_secret_with_timestamp;
 extern int use_fingerprints;

+ 22 - 6
src/client/ns_turn_ioaddr.c

@@ -185,19 +185,35 @@ int addr_eq_no_port(const ioa_addr* a1, const ioa_addr *a2) {
   return 0;
 }
 
-int make_ioa_addr(const u08bits* saddr, int port, ioa_addr *addr) {
+int make_ioa_addr(const u08bits* saddr0, int port, ioa_addr *addr) {
 
-  if(!saddr || !addr) return -1;
+  if(!saddr0 || !addr) return -1;
+
+  char ssaddr[257];
+  STRCPY(ssaddr,saddr0);
+
+  char* saddr=ssaddr;
+  while(*saddr == ' ') ++saddr;
+
+  size_t len=strlen(saddr);
+  while(len>0) {
+	  if(saddr[len-1]==' ') {
+		  saddr[len-1]=0;
+		  --len;
+	  } else {
+		  break;
+	  }
+  }
 
   ns_bzero(addr, sizeof(ioa_addr));
-  if((strlen((const s08bits*)saddr) == 0)||
-     (inet_pton(AF_INET, (const s08bits*)saddr, &addr->s4.sin_addr) == 1)) {
+  if((len == 0)||
+     (inet_pton(AF_INET, saddr, &addr->s4.sin_addr) == 1)) {
     addr->s4.sin_family = AF_INET;
 #if defined(TURN_HAS_SIN_LEN) /* tested when configured */
     addr->s4.sin_len = sizeof(struct sockaddr_in);
 #endif
     addr->s4.sin_port = nswap16(port);
-  } else if (inet_pton(AF_INET6, (const s08bits*)saddr, &addr->s6.sin6_addr) == 1) {
+  } else if (inet_pton(AF_INET6, saddr, &addr->s6.sin6_addr) == 1) {
     addr->s6.sin6_family = AF_INET6;
 #if defined(SIN6_LEN) /* this define is required by IPv6 if used */
     addr->s6.sin6_len = sizeof(struct sockaddr_in6);
@@ -217,7 +233,7 @@ int make_ioa_addr(const u08bits* saddr, int port, ioa_addr *addr) {
     addr_hints.ai_addr = NULL;
     addr_hints.ai_next = NULL;
 
-    err = getaddrinfo((const char*)saddr, NULL, &addr_hints, &addr_result);
+    err = getaddrinfo(saddr, NULL, &addr_hints, &addr_result);
     if ((err != 0)||(!addr_result)) {
       fprintf(stderr,"error resolving '%s' hostname: %s\n",saddr,gai_strerror(err));
       return -1;

+ 38 - 21
src/client/ns_turn_msg.c

@@ -611,17 +611,22 @@ int stun_is_channel_message_str(const u08bits *buf, size_t *blen, u16bits* chnum
 
 ////////// STUN message ///////////////////////////////
 
-static inline int sheadof(const char *head, const char* full)
+static inline int sheadof(const char *head, const char* full, int ignore_case)
 {
 	while(*head) {
-		if(*head != *full)
-			return 0;
+		if(*head != *full) {
+		  if(ignore_case && (tolower((int)*head)==tolower((int)*full))) {
+				//OK
+			} else {
+				return 0;
+			}
+		}
 		++head;++full;
 	}
 	return 1;
 }
 
-static inline const char* findstr(const char *hay, size_t slen, const char *needle)
+static inline const char* findstr(const char *hay, size_t slen, const char *needle, int ignore_case)
 {
 	const char *ret = NULL;
 
@@ -632,7 +637,7 @@ static inline const char* findstr(const char *hay, size_t slen, const char *need
 			size_t i;
 			const char *sp = hay;
 			for(i=0;i<smax;++i) {
-				if(sheadof(needle,sp+i)) {
+				if(sheadof(needle,sp+i,ignore_case)) {
 					ret = sp+i;
 					break;
 				}
@@ -643,28 +648,36 @@ static inline const char* findstr(const char *hay, size_t slen, const char *need
 	return ret;
 }
 
-static inline int is_http_get_inline(const char *s, size_t blen) {
+static inline int is_http_inline(const char *s, size_t blen) {
 	if(s && blen>=12) {
-		if((s[0]=='G')&&(s[1]=='E')&&(s[2]=='T')&&(s[3]==' ')) {
-			const char *sp=findstr(s+4,blen-4,"HTTP");
+		if((strstr(s,"GET ")==s) ||(strstr(s,"POST ")==s) || (strstr(s,"DELETE ")==s) || (strstr(s,"PUT ")==s)) {
+			const char *sp=findstr(s+4,blen-4," HTTP/",0);
 			if(sp) {
-				sp += 4;
+				sp += 6;
 				size_t diff_blen = sp-s;
 				if(diff_blen+4 <= blen) {
-					sp=findstr(sp,blen-diff_blen,"\r\n\r\n");
+					sp=findstr(sp,blen-diff_blen,"\r\n\r\n",0);
 					if(sp) {
-						return (int)(sp-s+4);
+						int ret_len = (int)(sp-s+4);
+						const char* clheader = "content-length: ";
+						const char* cl = findstr(s,sp-s,clheader,1);
+						if(cl) {
+							unsigned long clen = strtoul(cl+strlen(clheader),NULL,10);
+							if(clen>0 && clen<(0x0FFFFFFF)) {
+								ret_len += (int)clen;
+							}
+						}
+						return ret_len;
 					}
 				}
 			}
-
 		}
 	}
 	return 0;
 }
 
-int is_http_get(const char *s, size_t blen) {
-	return is_http_get_inline(s, blen);
+int is_http(const char *s, size_t blen) {
+	return is_http_inline(s, blen);
 }
 
 int stun_get_message_len_str(u08bits *buf, size_t blen, int padding, size_t *app_len) {
@@ -690,7 +703,7 @@ int stun_get_message_len_str(u08bits *buf, size_t blen, int padding, size_t *app
 
 		//HTTP request ?
 		{
-			int http_len = is_http_get_inline(((char*)buf), blen);
+			int http_len = is_http_inline(((char*)buf), blen);
 			if((http_len>0) && ((size_t)http_len<=blen)) {
 				*app_len = (size_t)http_len;
 				return http_len;
@@ -1452,7 +1465,7 @@ void print_bin_func(const char *name, size_t len, const void *s, const char *fun
 	printf("]\n");
 }
 
-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_str(turn_credential_type ct, u08bits *buf, size_t *len, hmackey_t key, password_t pwd, SHATYPE shatype)
 {
 	u08bits hmac[MAXSHASIZE];
 
@@ -1491,7 +1504,7 @@ int stun_attr_add_integrity_by_key_str(u08bits *buf, size_t *len, u08bits *uname
 	if(stun_attr_add_str(buf, len, STUN_ATTRIBUTE_REALM, realm, strlen((s08bits*)realm))<0)
 			return -1;
 
-	st_password_t p;
+	password_t p;
 	return stun_attr_add_integrity_str(TURN_CREDENTIALS_LONG_TERM, buf, len, key, p, shatype);
 }
 
@@ -1505,7 +1518,7 @@ int stun_attr_add_integrity_by_user_str(u08bits *buf, size_t *len, u08bits *unam
 	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)
+int stun_attr_add_integrity_by_user_short_term_str(u08bits *buf, size_t *len, u08bits *uname, password_t pwd, SHATYPE shatype)
 {
 	if(stun_attr_add_str(buf, len, STUN_ATTRIBUTE_USERNAME, uname, strlen((s08bits*)uname))<0)
 			return -1;
@@ -1527,7 +1540,7 @@ void print_hmac(const char *name, const void *s, size_t len)
 /*
  * Return -1 if failure, 0 if the integrity is not correct, 1 if OK
  */
-int stun_check_message_integrity_by_key_str(turn_credential_type ct, u08bits *buf, size_t len, hmackey_t key, st_password_t pwd, SHATYPE shatype, int *too_weak)
+int stun_check_message_integrity_by_key_str(turn_credential_type ct, u08bits *buf, size_t len, hmackey_t key, password_t pwd, SHATYPE shatype, int *too_weak)
 {
 	int res = 0;
 	u08bits new_hmac[MAXSHASIZE];
@@ -1595,10 +1608,10 @@ int stun_check_message_integrity_by_key_str(turn_credential_type ct, u08bits *bu
 int stun_check_message_integrity_str(turn_credential_type ct, u08bits *buf, size_t len, u08bits *uname, u08bits *realm, u08bits *upwd, SHATYPE shatype)
 {
 	hmackey_t key;
-	st_password_t pwd;
+	password_t pwd;
 
 	if(ct == TURN_CREDENTIALS_SHORT_TERM)
-		strncpy((char*)pwd,(char*)upwd,sizeof(st_password_t));
+		strncpy((char*)pwd,(char*)upwd,sizeof(password_t));
 	else if (stun_produce_integrity_key_str(uname, realm, upwd, key, shatype) < 0)
 		return -1;
 
@@ -1882,6 +1895,8 @@ int convert_oauth_key_data(const oauth_key_data *oakd0, oauth_key *key, char *er
 			key->auth_alg = AUTH_ALG_ERROR;
 			OAUTH_ERROR("Wrong oAuth token hash algorithm: %s (2)\n",oakd->auth_alg);
 			return -1;
+		} else {
+			key->auth_alg = AUTH_ALG_UNDEFINED;
 		}
 
 		key->as_rs_alg = ENC_ALG_DEFAULT;
@@ -1891,8 +1906,10 @@ int convert_oauth_key_data(const oauth_key_data *oakd0, oauth_key *key, char *er
 			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;
+			key->auth_alg = AUTH_ALG_UNDEFINED;
 		} else if(!strcmp(oakd->as_rs_alg,"AEAD-AES-256-GCM")) {
 			key->as_rs_alg = AEAD_AES_256_GCM;
+			key->auth_alg = AUTH_ALG_UNDEFINED;
 		} 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);

+ 5 - 9
src/client/ns_turn_msg.h

@@ -63,11 +63,7 @@ typedef enum {
  */
 typedef u08bits hmackey_t[64];
 
-/**
- * Short-term credentials password
- */
-#define SHORT_TERM_PASSWORD_SIZE (512)
-typedef u08bits st_password_t[SHORT_TERM_PASSWORD_SIZE+1];
+typedef u08bits password_t[STUN_MAX_PWD_SIZE+1];
 typedef unsigned int band_limit_t;
 
 ///////////////////////////////////
@@ -180,12 +176,12 @@ void print_bin_func(const char *name, size_t len, const void *s, const char *fun
 /*
  * Return -1 if failure, 0 if the integrity is not correct, 1 if OK
  */
-int stun_check_message_integrity_by_key_str(turn_credential_type ct, u08bits *buf, size_t len, hmackey_t key, st_password_t pwd, SHATYPE shatype, int *too_weak);
+int stun_check_message_integrity_by_key_str(turn_credential_type ct, u08bits *buf, size_t len, hmackey_t key, 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_str(turn_credential_type ct, u08bits *buf, size_t *len, hmackey_t key, 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);
+int stun_attr_add_integrity_by_user_short_term_str(u08bits *buf, size_t *len, u08bits *uname, password_t pwd, SHATYPE shatype);
 size_t get_hmackey_size(SHATYPE shatype);
 
 /*
@@ -207,7 +203,7 @@ int stun_attr_get_padding_len_str(stun_attr_ref attr);
 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);
+int is_http(const char *s, size_t blen);
 
 /* OAUTH */
 int convert_oauth_key_data(const oauth_key_data *oakd, oauth_key *key, char *err_msg, size_t err_msg_size);

+ 2 - 1
src/client/ns_turn_msg_defs.h

@@ -40,11 +40,12 @@
 #define STUN_HEADER_LENGTH (20)
 #define STUN_CHANNEL_HEADER_LENGTH (4)
 
-#define STUN_MAX_USERNAME_SIZE (513)
+#define STUN_MAX_USERNAME_SIZE (512)
 #define STUN_MAX_REALM_SIZE (127)
 #define STUN_MAX_NONCE_SIZE (127)
 #define STUN_MAX_SERVER_NAME_SIZE (1025)
 #define STUN_MAX_PWD_SIZE (127)
+#define AUTH_SECRET_SIZE STUN_MAX_PWD_SIZE
 
 #define STUN_MAGIC_COOKIE (0x2112A442)
 

+ 2 - 2
src/ns_turn_defs.h

@@ -31,8 +31,8 @@
 #ifndef __IOADEFS__
 #define __IOADEFS__
 
-#define TURN_SERVER_VERSION "4.3.3.1"
-#define TURN_SERVER_VERSION_NAME "Tolomei"
+#define TURN_SERVER_VERSION "4.4.1.1"
+#define TURN_SERVER_VERSION_NAME "Ardee West"
 #define TURN_SOFTWARE "Coturn-" TURN_SERVER_VERSION " '" TURN_SERVER_VERSION_NAME "'"
 
 #if (defined(__unix__) || defined(unix)) && !defined(USG)

+ 1 - 2
src/server/ns_turn_ioalib.h

@@ -278,8 +278,7 @@ int get_default_protocol_port(const char* scheme, size_t slen);
 
 ///////////// HTTP ////////////////////
 
-void write_http_echo(ioa_socket_handle s);
-void handle_https(ioa_socket_handle s, ioa_network_buffer_handle nbh);
+void handle_http_echo(ioa_socket_handle s);
 
 ///////////////////////////////////////
 

+ 43 - 59
src/server/ns_turn_server.c

@@ -2148,13 +2148,6 @@ static void tcp_peer_accept_connection(ioa_socket_handle s, void *arg)
 			ioa_network_buffer_set_size(nbh, len);
 		}
 
-		/* We add integrity for short-term indication messages, only */
-		if(server->ct == TURN_CREDENTIALS_SHORT_TERM)
-		{
-			stun_attr_add_integrity_str(server->ct,ioa_network_buffer_data(nbh),&len,ss->hmackey,ss->pwd,server->shatype);
-			ioa_network_buffer_set_size(nbh,len);
-		}
-
 		if ((server->fingerprint) || ss->enforce_fingerprints) {
 			size_t len = ioa_network_buffer_get_size(nbh);
 			stun_attr_add_fingerprint_str(ioa_network_buffer_data(nbh), &len);
@@ -3118,8 +3111,6 @@ static int need_stun_authentication(turn_turnserver *server, ts_ur_super_session
 		switch(server->ct) {
 		case TURN_CREDENTIALS_LONG_TERM:
 			return 1;
-		case TURN_CREDENTIALS_SHORT_TERM:
-			return 1;
 		default:
 			;
 		};
@@ -3164,7 +3155,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,  int oauth, int max_session_time, 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, password_t pwd, turn_turnserver *server, u64bits ctxkey, ioa_net_data *in_buffer)
 {
 
 	if(server && in_buffer && in_buffer->nbh) {
@@ -3178,7 +3169,7 @@ static void resume_processing_after_username_check(int success,  int oauth, int
 				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));
+				ns_bcopy(pwd,ss->pwd,sizeof(password_t));
 			}
 
 			read_client_connection(server,ss,in_buffer,0,0);
@@ -3252,11 +3243,7 @@ static int check_stun_auth(turn_turnserver *server,
 	if(!sar) {
 		*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;
-		}
+		return create_challenge_response(ss,tid,resp_constructed,err_code,reason,nbh,method);
 	}
 
 	{
@@ -3280,7 +3267,7 @@ static int check_stun_auth(turn_turnserver *server,
 		};
 	}
 
-	if(server->ct != TURN_CREDENTIALS_SHORT_TERM) {
+	{
 
 		/* REALM ATTR: */
 
@@ -3351,7 +3338,7 @@ static int check_stun_auth(turn_turnserver *server,
 		set_realm_hash(ss->client_socket,(u08bits*)ss->realm_options.name);
 	}
 
-	if(server->ct != TURN_CREDENTIALS_SHORT_TERM) {
+	{
 		/* NONCE ATTR: */
 
 		sar = stun_attr_get_first_by_type_str(ioa_network_buffer_data(in_buffer->nbh),
@@ -3391,18 +3378,14 @@ static int check_stun_auth(turn_turnserver *server,
 				return 0;
 			}
 		}
-		/* we always return NULL for short-term credentials here */
+
 		/* 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;
-		}
+		return create_challenge_response(ss,tid,resp_constructed,err_code,reason,nbh,method);
 	}
 
 	/* Check integrity */
@@ -3420,11 +3403,7 @@ static int check_stun_auth(turn_turnserver *server,
 									__FUNCTION__, (char*)usname);
 					*err_code = SHA_TOO_WEAK_ERROR_CODE;
 					*reason = (const u08bits*)"Unauthorised: weak SHA function is used";
-					if(server->ct != TURN_CREDENTIALS_SHORT_TERM) {
-						return create_challenge_response(ss,tid,resp_constructed,err_code,reason,nbh,method);
-					} else {
-						return -1;
-					}
+					return create_challenge_response(ss,tid,resp_constructed,err_code,reason,nbh,method);
 		}
 
 		if(can_resume) {
@@ -3439,11 +3418,7 @@ static int check_stun_auth(turn_turnserver *server,
 				__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;
-		}
+		return create_challenge_response(ss,tid,resp_constructed,err_code,reason,nbh,method);
 	}
 
 	*message_integrity = 1;
@@ -3792,10 +3767,6 @@ static int handle_turn_command(turn_turnserver *server, ts_ur_super_session *ss,
 		no_response = 1;
 		int postpone = 0;
 
-		if(server->ct == TURN_CREDENTIALS_SHORT_TERM) {
-			check_stun_auth(server, ss, &tid, resp_constructed, &err_code, &reason, in_buffer, nbh, method, &message_integrity, &postpone, can_resume);
-		}
-
 		if (!postpone && !err_code) {
 
 			switch (method){
@@ -4349,8 +4320,9 @@ static int create_relay_connection(turn_turnserver* server,
 			set_do_not_use_df(newelem->s);
 
 		if(get_ioa_socket_type(newelem->s) != TCP_SOCKET) {
-			register_callback_on_ioa_socket(server->e, newelem->s, IOA_EV_READ,
-				peer_input_handler, ss, 0);
+			if(register_callback_on_ioa_socket(server->e, newelem->s, IOA_EV_READ,peer_input_handler, ss, 0)<0) {
+				return -1;
+			}
 		}
 
 		if (lifetime<1)
@@ -4441,13 +4413,15 @@ static int read_client_connection(turn_turnserver *server,
 
 	if(sat == HTTP_CLIENT_SOCKET) {
 
-		TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: HTTP connection input: %s\n", __FUNCTION__, (char*)ioa_network_buffer_data(in_buffer->nbh));
-		write_http_echo(ss->client_socket);
+		if(server->verbose) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: HTTP connection input: %s\n", __FUNCTION__, (char*)ioa_network_buffer_data(in_buffer->nbh));
+		}
+
+		handle_http_echo(ss->client_socket);
 
 	} else if(sat == HTTPS_CLIENT_SOCKET) {
 
-		TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: HTTPS connection input: %s\n", __FUNCTION__, (char*)ioa_network_buffer_data(in_buffer->nbh));
-		handle_https(ss->client_socket,in_buffer->nbh);
+		//???
 
 	} else if (stun_is_channel_message_str(ioa_network_buffer_data(in_buffer->nbh),
 					&blen,
@@ -4536,17 +4510,28 @@ static int read_client_connection(turn_turnserver *server,
 	} else {
 		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))) {
+			if(is_http((char*)ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh))) {
 				const char *proto = "HTTP";
+				ioa_network_buffer_data(in_buffer->nbh)[ioa_network_buffer_get_size(in_buffer->nbh)] = 0;
 				if(st==TLS_SOCKET) {
 					proto = "HTTPS";
 					set_ioa_socket_app_type(ss->client_socket,HTTPS_CLIENT_SOCKET);
 					TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: %s (%s %s) request: %s\n", __FUNCTION__, proto, get_ioa_socket_cipher(ss->client_socket), get_ioa_socket_ssl_method(ss->client_socket), (char*)ioa_network_buffer_data(in_buffer->nbh));
-					handle_https(ss->client_socket,in_buffer->nbh);
+					if(server->send_https_socket) {
+						TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s socket to be detached: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)ss->client_socket, get_ioa_socket_type(ss->client_socket), get_ioa_socket_app_type(ss->client_socket));
+						ioa_socket_handle new_s = detach_ioa_socket(ss->client_socket);
+						if(new_s) {
+							TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s new detached socket: 0x%lx, st=%d, sat=%d\n", __FUNCTION__,(long)new_s, get_ioa_socket_type(new_s), get_ioa_socket_app_type(new_s));
+							server->send_https_socket(new_s);
+						}
+						ss->to_be_closed = 1;
+					}
 				} else {
 					set_ioa_socket_app_type(ss->client_socket,HTTP_CLIENT_SOCKET);
-					TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: %s request: %s\n", __FUNCTION__, proto, (char*)ioa_network_buffer_data(in_buffer->nbh));
-					write_http_echo(ss->client_socket);
+					if(server->verbose) {
+						TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: %s request: %s\n", __FUNCTION__, proto, (char*)ioa_network_buffer_data(in_buffer->nbh));
+					}
+					handle_http_echo(ss->client_socket);
 				}
 				return 0;
 			}
@@ -4572,8 +4557,10 @@ static int attach_socket_to_session(turn_turnserver* server, ioa_socket_handle s
 
 			ss->client_socket = s;
 
-			register_callback_on_ioa_socket(server->e, s, IOA_EV_READ,
-					client_input_handler, ss, 0);
+			if(register_callback_on_ioa_socket(server->e, s, IOA_EV_READ,
+					client_input_handler, ss, 0)<0) {
+				return -1;
+			}
 
 			set_ioa_socket_session(s, ss);
 		}
@@ -4599,8 +4586,10 @@ int open_client_connection_session(turn_turnserver* server,
 
 	ss->client_socket = sm->s;
 
-	register_callback_on_ioa_socket(server->e, ss->client_socket, IOA_EV_READ,
-			client_input_handler, ss, 0);
+	if(register_callback_on_ioa_socket(server->e, ss->client_socket, IOA_EV_READ,
+			client_input_handler, ss, 0)<0) {
+		return -1;
+	}
 
 	set_ioa_socket_session(ss->client_socket, ss);
 
@@ -4722,13 +4711,6 @@ static void peer_input_handler(ioa_socket_handle s, int event_type,
 					ioa_network_buffer_set_size(nbh, len);
 				}
 
-				/* We add integrity for short-term indication messages, only */
-				if(server->ct == TURN_CREDENTIALS_SHORT_TERM)
-				{
-					stun_attr_add_integrity_str(server->ct,ioa_network_buffer_data(nbh),&len,ss->hmackey,ss->pwd,server->shatype);
-					ioa_network_buffer_set_size(nbh,len);
-				}
-
 				if ((server->fingerprint) || ss->enforce_fingerprints) {
 					size_t len = ioa_network_buffer_get_size(nbh);
 					stun_attr_add_fingerprint_str(ioa_network_buffer_data(nbh), &len);
@@ -4802,6 +4784,7 @@ 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,
+		send_https_socket_cb send_https_socket,
 		allocate_bps_cb allocate_bps_func,
 		int oauth, const char* oauth_server_name) {
 
@@ -4827,6 +4810,7 @@ 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->send_https_socket = send_https_socket;
 	server->oauth = oauth;
 	if(oauth)
 		server->oauth_server_name = oauth_server_name;

+ 4 - 1
src/server/ns_turn_server.h

@@ -90,12 +90,13 @@ typedef enum {
 struct _turn_turnserver;
 typedef struct _turn_turnserver turn_turnserver;
 
-typedef void (*get_username_resume_cb)(int success, int oauth, int max_session_time, hmackey_t hmackey, st_password_t pwd, turn_turnserver *server, u64bits ctxkey, ioa_net_data *in_buffer);
+typedef void (*get_username_resume_cb)(int success, int oauth, int max_session_time, hmackey_t hmackey, password_t pwd, turn_turnserver *server, u64bits ctxkey, ioa_net_data *in_buffer);
 typedef u08bits *(*get_user_key_cb)(turnserver_id id, turn_credential_type ct, int in_oauth, int *out_oauth, u08bits *uname, u08bits *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, u64bits ctxkey, int *postpone_reply);
 typedef int (*check_new_allocation_quota_cb)(u08bits *username, int oauth, u08bits *realm);
 typedef void (*release_allocation_quota_cb)(u08bits *username, int oauth, u08bits *realm);
 typedef int (*send_socket_to_relay_cb)(turnserver_id id, u64bits cid, stun_tid *tid, ioa_socket_handle s, int message_integrity, MESSAGE_TO_RELAY_TYPE rmt, ioa_net_data *nd, int can_resume);
 typedef int (*send_turn_session_info_cb)(struct turn_session_info *tsi);
+typedef void (*send_https_socket_cb)(ioa_socket_handle s);
 
 typedef band_limit_t (*allocate_bps_cb)(band_limit_t bps, int positive);
 
@@ -131,6 +132,7 @@ struct _turn_turnserver {
 	vintp no_loopback_peers;
 	vintp no_multicast_peers;
 	send_turn_session_info_cb send_turn_session_info;
+	send_https_socket_cb send_https_socket;
 
 	/* RFC 6062 ==>> */
 	vintp no_udp_relay;
@@ -199,6 +201,7 @@ void init_turn_server(turn_turnserver* server,
 				    vintp mobility,
 				    int server_relay,
 				    send_turn_session_info_cb send_turn_session_info,
+				    send_https_socket_cb send_https_socket,
 				    allocate_bps_cb allocate_bps_func,
 				    int oauth,
 				    const char* oauth_server_name);

+ 2 - 2
src/server/ns_turn_session.h

@@ -44,7 +44,7 @@ extern "C" {
 
 typedef struct _perf_options_t {
 
-	band_limit_t max_bps;
+	volatile band_limit_t max_bps;
 	vint total_quota;
 	vint user_quota;
 
@@ -81,7 +81,7 @@ struct _ts_ur_super_session {
   u08bits username[STUN_MAX_USERNAME_SIZE+1];
   hmackey_t hmackey;
   int hmackey_set;
-  st_password_t pwd;
+  password_t pwd;
   int quota_used;
   int oauth;
   turn_time_t max_session_time_auth;

+ 2 - 2
turndb/schema.mongo.sh

@@ -5,10 +5,10 @@ mongo $* <<EOF
 use coturn;
 
 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.turn_secret.ensureIndex({ realm: 1, value:1 }, { unique: 1 });
 db.realm.ensureIndex({ realm: 1 }, { unique: 1 });
 db.oauth_key.ensureIndex({ kid: 1 }, {unique: 1 });
+db.admin_user.ensureIndex({ name: 1 }, {unique: 1 });
 
 exit
 

+ 15 - 13
turndb/schema.sql

@@ -1,42 +1,37 @@
 
 CREATE TABLE turnusers_lt (
-    realm varchar(512) default '',
+    realm varchar(127) default '',
     name varchar(512),
     hmackey char(128),
     PRIMARY KEY (realm,name)
 );
 
-CREATE TABLE turnusers_st (
-    name varchar(512) PRIMARY KEY,
-    password varchar(512)
-);
-
 CREATE TABLE turn_secret (
-	realm varchar(512) default '',
-    value varchar(512),
+	realm varchar(127) default '',
+    value varchar(127),
 	primary key (realm,value)
 );
 
 CREATE TABLE allowed_peer_ip (
-	realm varchar(512) default '',
+	realm varchar(127) default '',
 	ip_range varchar(256),
 	primary key (realm,ip_range)
 );
 
 CREATE TABLE denied_peer_ip (
-	realm varchar(512) default '',
+	realm varchar(127) default '',
 	ip_range varchar(256),
 	primary key (realm,ip_range)
 );
 
 CREATE TABLE turn_origin_to_realm (
-	origin varchar(512),
-	realm varchar(512),
+	origin varchar(127),
+	realm varchar(127),
 	primary key (origin)
 );
 
 CREATE TABLE turn_realm_option (
-	realm varchar(512) default '',
+	realm varchar(127) default '',
 	opt varchar(32),
 	value varchar(128),
 	primary key (realm,opt)
@@ -54,3 +49,10 @@ CREATE TABLE oauth_key (
 	auth_key varchar(256) default '',
 	primary key (kid)
 );
+
+CREATE TABLE admin_user (
+	name varchar(32),
+	realm varchar(127),
+	password varchar(127),
+	primary key (name)
+);

+ 16 - 24
turndb/schema.userdb.redis

@@ -8,16 +8,9 @@ has the following schema:
 "turn/realm/<realm-name>/user/<username>/key" and the values must be 
 the the hmackeys. For example, for the user "gorst", realm "north.gov" 
 and password "hero", there must be key "turn/realm/north.gov/user/gorst/key" 
-with value "7da2270ccfa49786e0115366d3a3d14d". Alternatively, the password 
-may be stored in clear text format. Then the key will be 
-"turn/realm/north.gov/user/gorst/password" and the key will be simply "hero".
+with value "7da2270ccfa49786e0115366d3a3d14d".
 
-2) For the short-term credentials, the passwords are stored always in
-clear text format, with no realm name (because the short-term credentials 
-are not bound to a realm). So, there will be key "turn/user/gorst/password" 
-and the value will be "hero".
-
-3) For the shared secrets (REST API), several key/value pairs 
+2) For the shared secrets (REST API), several key/value pairs 
 may be used (same as in SQL schema). The secrets are stored as members 
 of an unordered set. The name of the set will be 
 "turn/realm/<realm-name>/secret" and the value(s) will be the secret(s).
@@ -26,7 +19,7 @@ then we will have set "turn/realm/north.gov/secret" with values
 "hero1", "hero2" and "hero3". The turnserver will try to use the 
 secrets in arbitrary order.
 
-4) The "white" and "black" peer IP ranges are stored as unordered sets
+3) The "white" and "black" peer IP ranges are stored as unordered sets
 of the following names:  
 "turn/realm/<realm>/allowed-peer-ip" and
 "turn/realm/<realm>/denied-peer-ip".
@@ -38,7 +31,7 @@ The only difference is that the turnserver option values are "static"
 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 
+4) 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");
@@ -72,6 +65,10 @@ and they will be almost immediately "seen" by the turnserver process.
 	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.
+		
+5) admin users (over https interface) are maintained as keys of form:
+"turn/admin_user/<username> with hash members "password" and,
+optionally, "realm".
 
 II. Extra realms data in the database
 
@@ -95,7 +92,6 @@ This example sets user database for:
   * long-term credentials with open passwords and 
   	with default realm "north.gov";
   * TURN REST API with shared secrets "logen", etc;
-  * short-term credentials mechanism, with open passwords;
   * Black and white IP peer lists used.
   * Information how to match ORIGIN field with extra
     realms (if used). If no origin match found
@@ -104,7 +100,9 @@ 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 oAuth data for the key with kid "north" and key value "carleon".
+  * The admin user 'skarling', realm 'north.gov', with password 'hoodless';
+  * The global admin user 'bayaz' with password 'magi';  
   
 The shell command would be:
 
@@ -118,19 +116,9 @@ set turn/realm/north.gov/user/gorst/key "7da2270ccfa49786e0115366d3a3d14d"
 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/crinna.org/user/whirrun/password "sword"
-set turn/realm/crinna.org/user/stranger-come-knocking/password "civilization"
-
 sadd turn/realm/north.gov/secret "logen" "bloody9"
 sadd turn/realm/crinna.org/secret "north" "library"
 
-set turn/user/ninefingers/password "youhavetoberealistic"
-set turn/user/gorst/password "hero"
-set turn/user/whirrun/password "sword"
-set turn/user/stranger-come-knocking/password "civilization"
-
 set turn/realm/north.gov/max-bps 500000
 set turn/realm/north.gov/total-quota 12000
 set turn/realm/north.gov/user-quota 10000
@@ -147,7 +135,11 @@ sadd turn/realm/crinna.org/allowed-peer-ip "172.17.13.202"
 sadd turn/realm/north.gov/denied-peer-ip "172.17.13.133-172.17.14.56" "172.17.17.133-172.17.19.56" "123::45"
 sadd turn/realm/crinna.org/denied-peer-ip "123::77"
 
-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'
+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'
+hmset turn/oauth/kid/oldempire ikm_key 'YXVsY3Vz' hkdf_hash_func 'SHA-256' as_rs_alg 'AEAD-AES-256-GCM'
+
+hmset turn/admin_user/skarling realm 'north.gov' password 'hoodless'
+hmset turn/admin_user/bayaz password 'magi'
 
 save
 

+ 7 - 7
turndb/testmongosetup.sh

@@ -5,23 +5,23 @@ mongo $* <<EOF
 use coturn;
 
 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.turn_secret.ensureIndex({ realm: 1, value:1 }, { unique: 1 });
 db.realm.ensureIndex({ realm: 1 }, { unique: 1 });
 db.oauth_key.ensureIndex({ kid: 1 }, {unique: 1 });
+db.admin_user.ensureIndex({ name: 1 }, {unique: 1 });
 
 db.turnusers_lt.insert({ realm: 'north.gov', name: 'ninefingers', hmackey: 'bc807ee29df3c9ffa736523fb2c4e8ee' });
 db.turnusers_lt.insert({ realm: 'north.gov', name: 'gorst', hmackey: '7da2270ccfa49786e0115366d3a3d14d' });
 db.turnusers_lt.insert({ realm: 'crinna.org', name: 'whirrun', hmackey: '6972e85e51f36e53b0b61759c5a5219a' });
 db.turnusers_lt.insert({ realm: 'crinna.org', name: 'stranger-come-knocking', hmackey: 'd43cb678560259a1839bff61c19de15e' });
 
-db.turnusers_st.insert({ name: 'ninefingers', password: 'youhavetoberealistic'});
-db.turnusers_st.insert({ name: 'gorst', password: 'hero'});
-db.turnusers_st.insert({ name: 'whirrun', password: 'sword'});
-db.turnusers_st.insert({ name: 'stranger-come-knocking', password: 'civilization'});
-
 db.turn_secret.insert({ realm: 'north.gov', value: 'logen' });
+db.turn_secret.insert({ realm: 'north.gov', value: 'bloody9' });
 db.turn_secret.insert({ realm: 'crinna.org', value: 'north' });
+db.turn_secret.insert({ realm: 'crinna.org', value: 'library' });
+
+db.admin_user.insert({ name: 'skarling', realm: 'north.gov', password: 'hoodless' });
+db.admin_user.insert({ name: 'bayaz', realm: '', password: 'magi' });
 
 db.realm.insert({
   realm: 'north.gov',

+ 10 - 20
turndb/testredisdbsetup.sh

@@ -1,5 +1,10 @@
 #!/bin/sh
 
+# ninefingers:password: youhavetoberealistic
+# gorst:password: hero
+# whirrun:password: sword
+# stranger-come-knocking:password: civilization
+
 redis-cli <<!
 
 SELECT 2
@@ -8,30 +13,12 @@ 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"
-
 sadd turn/realm/north.gov/secret "logen" "bloody9"
 sadd turn/realm/crinna.org/secret "north" "library"
 
-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"
-
 set turn/realm/north.gov/max-bps 500000
 set turn/realm/north.gov/total-quota 12000
 set turn/realm/north.gov/user-quota 10000
@@ -48,8 +35,11 @@ sadd turn/realm/crinna.org/allowed-peer-ip "172.17.13.202"
 sadd turn/realm/north.gov/denied-peer-ip "172.17.13.133-172.17.14.56" "172.17.17.133-172.17.19.56" "123::45"
 sadd turn/realm/crinna.org/denied-peer-ip "123::77"
 
-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'
+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'
+
+hmset turn/admin_user/skarling realm 'north.gov' password 'hoodless'
+hmset turn/admin_user/bayaz password 'magi'
 
 save
 

+ 5 - 5
turndb/testsqldbsetup.sql

@@ -4,13 +4,13 @@ insert into turnusers_lt (realm, name, hmackey) values('north.gov','gorst','7da2
 insert into turnusers_lt (realm, name, hmackey) values('crinna.org','whirrun','6972e85e51f36e53b0b61759c5a5219a');
 insert into turnusers_lt (realm, name, hmackey) values('crinna.org','stranger-come-knocking','d43cb678560259a1839bff61c19de15e');
 
-insert into turnusers_st (name, password) values('ninefingers','youhavetoberealistic');
-insert into turnusers_st (name, password) values('gorst','hero');
-insert into turnusers_st (name, password) values('whirrun','sword');
-insert into turnusers_st (name, password) values('stranger-come-knocking','civilization');
-
 insert into turn_secret (realm,value) values('north.gov','logen');
+insert into turn_secret (realm,value) values('north.gov','bloody9');
 insert into turn_secret (realm,value) values('crinna.org','north');
+insert into turn_secret (realm,value) values('crinna.org','library');
+
+insert into admin_user (name, realm, password) values('skarling','north.gov','hoodless');
+insert into admin_user (name, realm, password) values('bayaz','','magi');
 
 insert into turn_origin_to_realm (origin,realm) values('http://crinna.org:80','crinna.org');
 insert into turn_origin_to_realm (origin,realm) values('https://bligh.edu:443','crinna.org');