Added TURN media NAT option and possibility to give username/password

for STUN/TURN server.
This commit is contained in:
Juha Heinanen
2020-06-17 18:49:49 +03:00
parent 0ad0e3af8c
commit e7af96f321
7 changed files with 212 additions and 31 deletions
+58
View File
@@ -994,6 +994,64 @@ Java_com_tutpro_baresip_AccountKt_account_1set_1stun_1port(JNIEnv *env, jobject
return res; return res;
} }
JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_AccountKt_account_1stun_1user(JNIEnv *env, jobject thiz, jstring jAcc)
{
const char *native_acc = (*env)->GetStringUTFChars(env, jAcc, 0);
struct account *acc = (struct account *) strtoul(native_acc, NULL, 10);
(*env)->ReleaseStringUTFChars(env, jAcc, native_acc);
if (acc) {
const char *stun_user = account_stun_user(acc);
if (stun_user) return (*env)->NewStringUTF(env, stun_user);
}
return (*env)->NewStringUTF(env, "");
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1stun_1user(JNIEnv *env, jobject thiz,
jstring jAcc, jstring jUser) {
const char *native_acc = (*env)->GetStringUTFChars(env, jAcc, 0);
struct account *acc = (struct account *)strtoul(native_acc, NULL, 10);
(*env)->ReleaseStringUTFChars(env, jAcc, native_acc);
const char *user = (*env)->GetStringUTFChars(env, jUser, 0);
int res;
if (strlen(user) > 0)
res = account_set_stun_user(acc, user);
else
res = account_set_stun_user(acc, NULL);
(*env)->ReleaseStringUTFChars(env, jUser, user);
return res;
}
JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_AccountKt_account_1stun_1pass(JNIEnv *env, jobject thiz, jstring jAcc)
{
const char *native_acc = (*env)->GetStringUTFChars(env, jAcc, 0);
struct account *acc = (struct account *) strtoul(native_acc, NULL, 10);
(*env)->ReleaseStringUTFChars(env, jAcc, native_acc);
if (acc) {
const char *stun_pass = account_stun_pass(acc);
if (stun_pass) return (*env)->NewStringUTF(env, stun_pass);
}
return (*env)->NewStringUTF(env, "");
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1stun_1pass(JNIEnv *env, jobject thiz,
jstring jAcc, jstring jPass) {
const char *native_acc = (*env)->GetStringUTFChars(env, jAcc, 0);
struct account *acc = (struct account *)strtoul(native_acc, NULL, 10);
(*env)->ReleaseStringUTFChars(env, jAcc, native_acc);
const char *pass = (*env)->GetStringUTFChars(env, jPass, 0);
int res;
if (strlen(pass) > 0)
res = account_set_stun_pass(acc, pass);
else
res = account_set_stun_pass(acc, NULL);
(*env)->ReleaseStringUTFChars(env, jPass, pass);
return res;
}
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1mwi(JNIEnv *env, jobject thiz, Java_com_tutpro_baresip_AccountKt_account_1set_1mwi(JNIEnv *env, jobject thiz,
jstring javaAcc, jstring javaValue) { jstring javaAcc, jstring javaValue) {
@@ -14,6 +14,8 @@ class Account(val accp: String) {
var outbound = ArrayList<String>() var outbound = ArrayList<String>()
var mediaNat = account_medianat(accp) var mediaNat = account_medianat(accp)
var stunServer = "" var stunServer = ""
var stunUser = account_stun_user(accp)
var stunPass = account_stun_pass(accp)
var audioCodec = ArrayList<String>() var audioCodec = ArrayList<String>()
var videoCodec = ArrayList<String>() var videoCodec = ArrayList<String>()
var regint = account_regint(accp) var regint = account_regint(accp)
@@ -92,7 +94,11 @@ class Account(val accp: String) {
if (mediaNat != "") res = res + ";medianat=${mediaNat}" if (mediaNat != "") res = res + ";medianat=${mediaNat}"
if (stunServer != "") res = res + ";stunserver=\"stun:${stunServer}\"" if (stunServer != "") res = res + ";stunserver=\"${stunServer}\""
if (stunUser != "") res = res + ";stunuser=\"${stunUser}\""
if (stunPass != "") res = res + ";stunpass=\"${stunPass}\""
if (audioCodec.size > 0) { if (audioCodec.size > 0) {
var first = true var first = true
@@ -243,6 +249,10 @@ external fun account_stun_host(acc: String): String
external fun account_stun_port(acc: String): Int external fun account_stun_port(acc: String): Int
external fun account_set_stun_host(acc: String, host: String): Int external fun account_set_stun_host(acc: String, host: String): Int
external fun account_set_stun_port(acc: String, port: Int): Int external fun account_set_stun_port(acc: String, port: Int): Int
external fun account_stun_user(acc: String): String
external fun account_set_stun_user(acc: String, user: String): Int
external fun account_stun_pass(acc: String): String
external fun account_set_stun_pass(acc: String, pass: String): Int
external fun account_mediaenc(acc: String): String external fun account_mediaenc(acc: String): String
external fun account_set_mediaenc(acc: String, mediaenc: String): Int external fun account_set_mediaenc(acc: String, mediaenc: String): Int
external fun account_medianat(acc: String): String external fun account_medianat(acc: String): String
@@ -24,6 +24,8 @@ class AccountActivity : AppCompatActivity() {
internal lateinit var outbound2: EditText internal lateinit var outbound2: EditText
internal lateinit var mediaNat: String internal lateinit var mediaNat: String
internal lateinit var stunServer: EditText internal lateinit var stunServer: EditText
internal lateinit var stunUser: EditText
internal lateinit var stunPass: EditText
internal lateinit var regCheck: CheckBox internal lateinit var regCheck: CheckBox
internal lateinit var mediaEnc: String internal lateinit var mediaEnc: String
internal lateinit var ipV6MediaCheck: CheckBox internal lateinit var ipV6MediaCheck: CheckBox
@@ -78,8 +80,8 @@ class AccountActivity : AppCompatActivity() {
mediaNat = acc.mediaNat mediaNat = acc.mediaNat
val mediaNatSpinner = findViewById(R.id.mediaNatSpinner) as Spinner val mediaNatSpinner = findViewById(R.id.mediaNatSpinner) as Spinner
val mediaNatKeys = arrayListOf("stun", "ice", "") val mediaNatKeys = arrayListOf("stun", "turn", "ice", "")
val mediaNatVals = arrayListOf("STUN", "ICE", "-") val mediaNatVals = arrayListOf("STUN", "TURN", "ICE", "-")
var keyIx = mediaNatKeys.indexOf(acc.mediaNat) var keyIx = mediaNatKeys.indexOf(acc.mediaNat)
var keyVal = mediaNatVals.elementAt(keyIx) var keyVal = mediaNatVals.elementAt(keyIx)
mediaNatKeys.removeAt(keyIx) mediaNatKeys.removeAt(keyIx)
@@ -94,6 +96,8 @@ class AccountActivity : AppCompatActivity() {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
mediaNat = mediaNatKeys[mediaNatVals.indexOf(parent.selectedItem.toString())] mediaNat = mediaNatKeys[mediaNatVals.indexOf(parent.selectedItem.toString())]
stunServer.isEnabled = mediaNat != "" stunServer.isEnabled = mediaNat != ""
stunUser.isEnabled = mediaNat != ""
stunPass.isEnabled = mediaNat != ""
} }
override fun onNothingSelected(parent: AdapterView<*>) { override fun onNothingSelected(parent: AdapterView<*>) {
} }
@@ -103,6 +107,14 @@ class AccountActivity : AppCompatActivity() {
stunServer.setText(acc.stunServer) stunServer.setText(acc.stunServer)
stunServer.isEnabled = mediaNat != "" stunServer.isEnabled = mediaNat != ""
stunUser = findViewById(R.id.StunUser) as EditText
stunUser.setText(acc.stunUser)
stunUser.isEnabled = mediaNat != ""
stunPass = findViewById(R.id.StunPass) as EditText
stunPass.setText(acc.stunPass)
stunPass.isEnabled = mediaNat != ""
mediaEnc = acc.mediaEnc mediaEnc = acc.mediaEnc
val mediaEncSpinner = findViewById(R.id.mediaEncSpinner) as Spinner val mediaEncSpinner = findViewById(R.id.mediaEncSpinner) as Spinner
val mediaEncKeys = arrayListOf("zrtp", "dtls_srtp", "srtp-mandf", "srtp-mand", "srtp", "") val mediaEncKeys = arrayListOf("zrtp", "dtls_srtp", "srtp-mandf", "srtp-mand", "srtp", "")
@@ -312,31 +324,64 @@ class AccountActivity : AppCompatActivity() {
} }
if (mediaNat != "") { if (mediaNat != "") {
val newStunServer = stunServer.text.toString().trim() var newStunServer = stunServer.text.toString().trim()
if (newStunServer == "")
newStunServer = resources.getString(R.string.stun_server_default)
if (acc.stunServer != newStunServer) { if (acc.stunServer != newStunServer) {
if (!Utils.checkHostPort(newStunServer)) { if (!Utils.checkStunUri(newStunServer)) {
Utils.alertView(this, getString(R.string.notice), Utils.alertView(this, getString(R.string.notice),
String.format(getString(R.string.invalid_stun_server), newStunServer)) String.format(getString(R.string.invalid_stun_server), newStunServer))
return false return false
} }
val p = newStunServer.split(":")
var host = "" var host = ""
var port = 0 val port: Int
if (newStunServer != "") { if (p.size == 2) {
val hostPort = newStunServer.split(":") host = newStunServer
host = hostPort[0] port = 0
if (hostPort.size == 2) port = hostPort[1].toInt() } else {
host = newStunServer.substringBeforeLast(":")
port = newStunServer.substringAfterLast(":").toInt()
} }
if ((account_set_stun_host(acc.accp, host) == 0) && if ((account_set_stun_host(acc.accp, host) == 0) &&
(account_set_stun_port(acc.accp, port) == 0)) { (account_set_stun_port(acc.accp, port) == 0)) {
acc.stunServer = account_stun_host(acc.accp) acc.stunServer = account_stun_host(acc.accp)
if (port != 0) if (port != 0)
acc.stunServer += ":" + account_stun_port(acc.accp).toString() acc.stunServer += ":" + account_stun_port(acc.accp).toString()
Log.d("Baresip", "New StunServer is '${acc.stunServer}'") Log.d("Baresip", "New STUN/TURN server URI is '${acc.stunServer}'")
save = true save = true
} else { } else {
Log.e("Baresip", "Setting of StunServer failed") Log.e("Baresip", "Setting of STUN/TURN URI server failed")
} }
} }
val su = stunUser.text.toString().trim()
if (Account.checkAuthUser(su)) {
if (account_set_stun_user(acc.accp, su) == 0) {
acc.stunUser = account_stun_user(acc.accp);
Log.d("Baresip", "New STUN/TURN user is ${acc.stunUser}")
save = true
} else {
Log.e("Baresip", "Setting of STUN/TURN user failed")
}
} else {
Utils.alertView(this, getString(R.string.notice),
String.format(getString(R.string.invalid_stun_username), su))
return false
}
val sp = stunPass.text.toString().trim()
if (sp.isEmpty() || Account.checkAuthPass(sp)) {
if (account_set_stun_pass(acc.accp, sp) == 0) {
acc.stunPass = account_stun_pass(acc.accp);
Log.d("Baresip", "New stun pass is ${acc.stunPass}")
save = true
} else {
Log.e("Baresip", "Setting of stun pass failed")
}
} else {
Utils.alertView(this, getString(R.string.notice),
String.format(getString(R.string.invalid_stun_password), sp))
return false
}
} }
if (mediaEnc != acc.mediaEnc) { if (mediaEnc != acc.mediaEnc) {
@@ -469,6 +514,14 @@ class AccountActivity : AppCompatActivity() {
Utils.alertView(this, getString(R.string.stun_server), Utils.alertView(this, getString(R.string.stun_server),
getString(R.string.stun_server_help)) getString(R.string.stun_server_help))
} }
findViewById(R.id.StunUserTitle) as TextView -> {
Utils.alertView(this, getString(R.string.stun_username),
getString(R.string.stun_username_help))
}
findViewById(R.id.StunPassTitle) as TextView -> {
Utils.alertView(this, getString(R.string.stun_password),
getString(R.string.stun_password_help))
}
findViewById(R.id.MediaEncTitle) as TextView -> { findViewById(R.id.MediaEncTitle) as TextView -> {
Utils.alertView(this, getString(R.string.media_encryption), Utils.alertView(this, getString(R.string.media_encryption),
getString(R.string.media_encryption_help)) getString(R.string.media_encryption_help))
@@ -125,6 +125,13 @@ object Utils {
return checkUriUser(aorUser(aor)) && checkDomain(aorDomain(aor)) return checkUriUser(aorUser(aor)) && checkDomain(aorDomain(aor))
} }
fun checkStunUri(uri: String): Boolean {
if (!uri.startsWith("stun:") && !uri.startsWith("stuns:") &&
!uri.startsWith("turn:") && !uri.startsWith("turns:"))
return false
return checkHostPort(uri.substringAfter(":"))
}
private fun checkPortTransport(portTransport: String): Boolean { private fun checkPortTransport(portTransport: String): Boolean {
val pt = portTransport.split(";transport=") val pt = portTransport.split(";transport=")
if (pt.count() == 1) if (pt.count() == 1)
+49 -7
View File
@@ -154,6 +154,24 @@
android:text="@string/audio_codecs" > android:text="@string/audio_codecs" >
</TextView> </TextView>
<TextView
android:id="@+id/MediaEncTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:text="@string/media_encryption" >
</TextView>
<Spinner
android:id="@+id/mediaEncSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp" >
</Spinner>
<TextView <TextView
android:id="@+id/MediaNatTitle" android:id="@+id/MediaNatTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -198,22 +216,46 @@
</EditText> </EditText>
<TextView <TextView
android:id="@+id/MediaEncTitle" android:id="@+id/StunUserTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="10dp" android:paddingTop="10dp"
android:textSize="18sp" android:textSize="18sp"
android:textColor="@android:color/black" android:textColor="@android:color/black"
android:onClick="onClick" android:onClick="onClick"
android:text="@string/media_encryption" > android:text="@string/stun_username" >
</TextView> </TextView>
<Spinner <EditText
android:id="@+id/mediaEncSpinner" android:id="@+id/StunUser"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="8dp" > android:layout_width="fill_parent"
</Spinner> android:hint="@string/user_id"
android:textSize="18sp"
android:inputType="textEmailAddress"
android:autofillHints="username" >
</EditText>
<TextView
android:id="@+id/StunPassTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:text="@string/stun_password" >
</TextView>
<EditText
android:id="@+id/StunPass"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/password"
android:textSize="18sp"
android:inputType="textPassword"
android:importantForAutofill="no" >
</EditText>
<RelativeLayout <RelativeLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
+11 -6
View File
@@ -84,13 +84,18 @@
(vapaaehtoinen). Vaihtoehtoja ovat STUN (Session Traversal Utilities (vapaaehtoinen). Vaihtoehtoja ovat STUN (Session Traversal Utilities
for NAT, RFC 5389) ja ICE (Interactive Connectivity Establishment, RFC 5245). for NAT, RFC 5389) ja ICE (Interactive Connectivity Establishment, RFC 5245).
</string> </string>
<string name="stun_server">STUN-palvelin</string> <string name="stun_server">STUN/TURN-palvelin</string>
<string name="stun_server_help">STUN-palvelimen muotoa palvelin[:portti] <string name="stun_server_help">STUN/TURN-palvelimen muotoa \'kaava:palvelin[:portti]\'
oleva osoite. Oletusarvo on \'stun.l.google.com:19302\', joka oleva osoite, missä kaava on \'stun\', \'stuns\', \'turn\' tai \'turns\'. Oletusarvo on
osoittaa Google:n julkiseen STUN-palvelimeen. Käyttäjätunnusta ja \'stun:stun.l.google.com:19302\', joka osoittaa Google:n julkiseen STUN-palvelimeen.
salasanaa ei voi tällä hetkellä antaa.
</string> </string>
<string name="invalid_stun_server">Virheellinen STUN-palvelin \'%1$s\'</string> <string name="invalid_stun_server">Virheellinen STUN/TURN-palvelimen URI \'%1$s\'</string>
<string name="stun_username">STUN/TURN-käyttäjätunnus</string>
<string name="stun_username_help">Käyttäjätunnus jos STUN/TURN-palvelin vaatii sellaisen</string>
<string name="invalid_stun_username">Virheellinen käyttäjätunnus \'%1$s\'</string>
<string name="stun_password">STUN/TURN-salasana</string>
<string name="stun_password_help">Salasana jos STUN/TURN-palvelin vaatii sellaisen</string>
<string name="invalid_stun_password">Virheellinen salasana \'%1$s\'</string>
<string name="media_encryption">Median salaus</string> <string name="media_encryption">Median salaus</string>
<string name="media_encryption_help">Valitsee median salausprotokollan (vapaaehtoinen). <string name="media_encryption_help">Valitsee median salausprotokollan (vapaaehtoinen).
\n • ZRTP (suositeltu) tarkoittaa, että ZRTP-salausta yritetään neuvotella sen jälkeen, kun puhelu on alkanut. \n • ZRTP (suositeltu) tarkoittaa, että ZRTP-salausta yritetään neuvotella sen jälkeen, kun puhelu on alkanut.
+12 -6
View File
@@ -78,13 +78,19 @@
(Session Traversal Utilities for NAT, RFC 5389) and ICE (Interactive Connectivity (Session Traversal Utilities for NAT, RFC 5389) and ICE (Interactive Connectivity
Establishment, RFC 5245). Establishment, RFC 5245).
</string> </string>
<string name="stun_server">STUN Server</string> <string name="stun_server">STUN/TURN Server</string>
<string name="stun_server_help">A STUN Server of form host[:port]. <string name="stun_server_help">A STUN/TURN Server URI of form scheme:host[:port], where scheme
Factory default value is \'stun.l.google.com:19302\', pointing to public Google STUN server. is \'stun\', \'stuns\', \'turn\', or \'turns\'. Factory default value is
Username and password are currently not supported. \'stun:stun.l.google.com:19302\', pointing to public Google STUN server.
</string> </string>
<string name="invalid_stun_server">Invalid STUN Server \'%1$s\'</string> <string name="invalid_stun_server">Invalid STUN/TURN Server URI \'%1$s\'</string>
<string name="stun_server_default" translatable="false">stun.l.google.com:19302</string> <string name="stun_server_default" translatable="false">stun:stun.l.google.com:19302</string>
<string name="stun_username">STUN/TURN Username</string>
<string name="stun_username_help">Username if required by STUN/TURN server</string>
<string name="invalid_stun_username">Invalid Username \'%1$s\'</string>
<string name="stun_password">STUN/TURN Password</string>
<string name="stun_password_help">Password if required by STUN/TURN server</string>
<string name="invalid_stun_password">Invalid Password \'%1$s\'</string>
<string name="media_encryption">Media Encryption</string> <string name="media_encryption">Media Encryption</string>
<string name="media_encryption_help">Selects media transport encryption protocol (if any). <string name="media_encryption_help">Selects media transport encryption protocol (if any).
\n • ZRTP (recommended) means that ZRTP end-to-end media encryption negotiation is tried after \n • ZRTP (recommended) means that ZRTP end-to-end media encryption negotiation is tried after