implement see-other-host stream error

This commit is contained in:
Daniel Gultsch 2023-10-13 08:29:23 +02:00
parent a40d244bf5
commit 5b2444ea13
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
6 changed files with 105 additions and 4 deletions

View file

@ -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:

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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);

View file

@ -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) {

View file

@ -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>