Detailed changes
@@ -787,6 +787,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
BIND_FAILURE,
HOST_UNKNOWN,
STREAM_ERROR,
+ SEE_OTHER_HOST,
STREAM_OPENING_ERROR,
POLICY_VIOLATION,
PAYMENT_REQUIRED,
@@ -874,6 +875,8 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
return R.string.account_status_stream_opening_error;
case PAYMENT_REQUIRED:
return R.string.payment_required;
+ case SEE_OTHER_HOST:
+ return R.string.reconnect_on_other_host;
case MISSING_INTERNET_PERMISSION:
return R.string.missing_internet_permission;
case TEMPORARY_AUTH_FAILURE:
@@ -1,5 +1,7 @@
package eu.siacs.conversations.utils;
+import com.google.common.net.InetAddresses;
+
import java.util.regex.Pattern;
public class IP {
@@ -27,4 +29,14 @@ public class IP {
}
}
+ public static String unwrapIPv6(final String host) {
+ if (host.length() > 2 && host.charAt(0) == '[' && host.charAt(host.length() - 1) == ']') {
+ final String ip = host.substring(1,host.length() -1);
+ if (InetAddresses.isInetAddress(ip)) {
+ return ip;
+ }
+ }
+ return host;
+ }
+
}
@@ -6,7 +6,10 @@ import android.util.Log;
import androidx.annotation.NonNull;
+import com.google.common.base.Strings;
import com.google.common.base.Throwables;
+import com.google.common.net.InetAddresses;
+import com.google.common.primitives.Ints;
import java.io.IOException;
import java.lang.reflect.Field;
@@ -446,6 +449,65 @@ public class Resolver {
contentValues.put(AUTHENTICATED, authenticated ? 1 : 0);
return contentValues;
}
+
+ public Result seeOtherHost(final String seeOtherHost) {
+ final String hostname = seeOtherHost.trim();
+ if (hostname.isEmpty()) {
+ return null;
+ }
+ final Result result = new Result();
+ result.directTls = this.directTls;
+ final int portSegmentStart = hostname.lastIndexOf(':');
+ if (hostname.charAt(hostname.length() - 1) != ']'
+ && portSegmentStart >= 0
+ && hostname.length() >= portSegmentStart + 1) {
+ final String hostPart = hostname.substring(0, portSegmentStart);
+ final String portPart = hostname.substring(portSegmentStart + 1);
+ final Integer port = Ints.tryParse(portPart);
+ if (port == null || Strings.isNullOrEmpty(hostPart)) {
+ return null;
+ }
+ final String host = eu.siacs.conversations.utils.IP.unwrapIPv6(hostPart);
+ result.port = port;
+ if (InetAddresses.isInetAddress(host)) {
+ final InetAddress inetAddress;
+ try {
+ inetAddress = InetAddresses.forString(host);
+ } catch (final IllegalArgumentException e) {
+ return null;
+ }
+ result.ip = inetAddress;
+ } else {
+ if (hostPart.trim().isEmpty()) {
+ return null;
+ }
+ try {
+ result.hostname = DNSName.from(hostPart.trim());
+ } catch (final Exception e) {
+ return null;
+ }
+ }
+ } else {
+ final String host = eu.siacs.conversations.utils.IP.unwrapIPv6(hostname);
+ if (InetAddresses.isInetAddress(host)) {
+ final InetAddress inetAddress;
+ try {
+ inetAddress = InetAddresses.forString(host);
+ } catch (final IllegalArgumentException e) {
+ return null;
+ }
+ result.ip = inetAddress;
+ } else {
+ try {
+ result.hostname = DNSName.from(hostname);
+ } catch (final Exception e) {
+ return null;
+ }
+ }
+ result.port = port;
+ }
+ return result;
+ }
}
}
@@ -189,6 +189,8 @@ public class XmppConnection implements Runnable {
private HashedToken.Mechanism hashTokenRequest;
private HttpUrl redirectionUrl = null;
private String verifiedHostname = null;
+ private Resolver.Result currentResolverResult;
+ private Resolver.Result seeOtherHostResolverResult;
private volatile Thread mThread;
private CountDownLatch mStreamCountDownLatch;
@@ -360,7 +362,12 @@ public class XmppConnection implements Runnable {
+ storedBackupResult);
}
}
- for (Iterator<Resolver.Result> iterator = results.iterator();
+ final Resolver.Result seeOtherHost = this.seeOtherHostResolverResult;
+ if (seeOtherHost != null) {
+ Log.d(Config.LOGTAG,account.getJid().asBareJid()+": injected see-other-host on position 0");
+ results.add(0, seeOtherHost);
+ }
+ for (final Iterator<Resolver.Result> iterator = results.iterator();
iterator.hasNext(); ) {
final Resolver.Result result = iterator.next();
if (Thread.currentThread().isInterrupted()) {
@@ -374,7 +381,6 @@ public class XmppConnection implements Runnable {
features.encryptionEnabled = result.isDirectTls();
verifiedHostname =
result.isAuthenticated() ? result.getHostname().toString() : null;
- Log.d(Config.LOGTAG, "verified hostname " + verifiedHostname);
final InetSocketAddress addr;
if (result.getIp() != null) {
addr = new InetSocketAddress(result.getIp(), result.getPort());
@@ -422,6 +428,8 @@ public class XmppConnection implements Runnable {
mXmppConnectionService.databaseBackend.saveResolverResult(
domain, result);
}
+ this.currentResolverResult = result;
+ this.seeOtherHostResolverResult = null;
break; // successfully connected to server that speaks xmpp
} else {
FileBackend.close(localSocket);
@@ -2166,6 +2174,21 @@ public class XmppConnection implements Runnable {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": policy violation. " + text);
failPendingMessages(text);
throw new StateChangingException(Account.State.POLICY_VIOLATION);
+ } else if (streamError.hasChild("see-other-host")) {
+ final String seeOtherHost = streamError.findChildContent("see-other-host");
+ final Resolver.Result currentResolverResult = this.currentResolverResult;
+ if (Strings.isNullOrEmpty(seeOtherHost) || currentResolverResult == null) {
+ Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": stream error " + streamError);
+ throw new StateChangingException(Account.State.STREAM_ERROR);
+ }
+ Log.d(Config.LOGTAG,account.getJid().asBareJid()+": see other host: "+seeOtherHost+" "+currentResolverResult);
+ final Resolver.Result seeOtherResult = currentResolverResult.seeOtherHost(seeOtherHost);
+ if (seeOtherResult != null) {
+ this.seeOtherHostResolverResult = seeOtherResult;
+ throw new StateChangingException(Account.State.SEE_OTHER_HOST);
+ } else {
+ throw new StateChangingException(Account.State.STREAM_ERROR);
+ }
} else {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": stream error " + streamError);
throw new StateChangingException(Account.State.STREAM_ERROR);
@@ -553,13 +553,13 @@ public class JingleRtpConnection extends AbstractJingleConnection
sendSessionTerminate(Reason.FAILED_APPLICATION, cause.getMessage());
return;
}
- processCandidates(receivedContentAccept.contents.entrySet());
- updateEndUserState();
Log.d(
Config.LOGTAG,
id.getAccount().getJid().asBareJid()
+ ": remote has accepted content-add "
+ ContentAddition.summary(receivedContentAccept));
+ processCandidates(receivedContentAccept.contents.entrySet());
+ updateEndUserState();
}
private void receiveContentModify(final JinglePacket jinglePacket) {
@@ -587,6 +587,7 @@
<string name="type_web">Web browser</string>
<string name="type_console">Console</string>
<string name="payment_required">Payment required</string>
+ <string name="reconnect_on_other_host">Reconnect on other host</string>
<string name="missing_internet_permission">Grant permission to use the Internet</string>
<string name="me">Me</string>
<string name="contact_asks_for_presence_subscription">Contact asks for presence subscription</string>