mirror of
https://codeberg.org/monocles/monocles_chat.git
synced 2025-01-26 23:14:11 +01:00
Rework XmppConnection and DNS Resolver
(cherry picked from commit 1332036ec0
)
This commit is contained in:
parent
57d8ff2539
commit
eef175853d
3 changed files with 437 additions and 376 deletions
|
@ -20,13 +20,13 @@
|
||||||
"filters": [
|
"filters": [
|
||||||
{
|
{
|
||||||
"filterType": "ABI",
|
"filterType": "ABI",
|
||||||
"value": "armeabi-v7a"
|
"value": "x86_64"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"attributes": [],
|
"attributes": [],
|
||||||
"versionCode": 17201,
|
"versionCode": 17203,
|
||||||
"versionName": "1.7.9.6",
|
"versionName": "1.7.9.6",
|
||||||
"outputFile": "monocles chat-1.7.9.6-git-armeabi-v7a-release.apk"
|
"outputFile": "monocles chat-1.7.9.6-git-x86_64-release.apk"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "ONE_OF_MANY",
|
"type": "ONE_OF_MANY",
|
||||||
|
@ -41,19 +41,6 @@
|
||||||
"versionName": "1.7.9.6",
|
"versionName": "1.7.9.6",
|
||||||
"outputFile": "monocles chat-1.7.9.6-git-x86-release.apk"
|
"outputFile": "monocles chat-1.7.9.6-git-x86-release.apk"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "ONE_OF_MANY",
|
|
||||||
"filters": [
|
|
||||||
{
|
|
||||||
"filterType": "ABI",
|
|
||||||
"value": "x86_64"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"attributes": [],
|
|
||||||
"versionCode": 17203,
|
|
||||||
"versionName": "1.7.9.6",
|
|
||||||
"outputFile": "monocles chat-1.7.9.6-git-x86_64-release.apk"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "ONE_OF_MANY",
|
"type": "ONE_OF_MANY",
|
||||||
"filters": [
|
"filters": [
|
||||||
|
@ -66,6 +53,19 @@
|
||||||
"versionCode": 17204,
|
"versionCode": 17204,
|
||||||
"versionName": "1.7.9.6",
|
"versionName": "1.7.9.6",
|
||||||
"outputFile": "monocles chat-1.7.9.6-git-arm64-v8a-release.apk"
|
"outputFile": "monocles chat-1.7.9.6-git-arm64-v8a-release.apk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ONE_OF_MANY",
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"filterType": "ABI",
|
||||||
|
"value": "armeabi-v7a"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attributes": [],
|
||||||
|
"versionCode": 17201,
|
||||||
|
"versionName": "1.7.9.6",
|
||||||
|
"outputFile": "monocles chat-1.7.9.6-git-armeabi-v7a-release.apk"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"elementType": "File"
|
"elementType": "File"
|
||||||
|
|
|
@ -6,13 +6,16 @@ import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.net.InetAddresses;
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.net.Inet4Address;
|
import java.net.Inet4Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -20,12 +23,14 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
//import de.gultsch.minidns.AndroidDNSClient;
|
||||||
import org.minidns.AbstractDnsClient;
|
import org.minidns.AbstractDnsClient;
|
||||||
import org.minidns.DnsCache;
|
import org.minidns.DnsCache;
|
||||||
import org.minidns.DnsClient;
|
import org.minidns.DnsClient;
|
||||||
import org.minidns.cache.LruCache;
|
import org.minidns.cache.LruCache;
|
||||||
import org.minidns.dnsmessage.Question;
|
import org.minidns.dnsmessage.Question;
|
||||||
import org.minidns.dnsname.DnsName;
|
import org.minidns.dnsname.DnsName;
|
||||||
|
import org.minidns.dnssec.DnssecResultNotAuthenticException;
|
||||||
import org.minidns.dnssec.DnssecValidationFailedException;
|
import org.minidns.dnssec.DnssecValidationFailedException;
|
||||||
import org.minidns.dnsserverlookup.AndroidUsingExec;
|
import org.minidns.dnsserverlookup.AndroidUsingExec;
|
||||||
import org.minidns.hla.DnssecResolverApi;
|
import org.minidns.hla.DnssecResolverApi;
|
||||||
|
@ -40,17 +45,10 @@ import org.minidns.record.InternetAddressRR;
|
||||||
import org.minidns.record.Record;
|
import org.minidns.record.Record;
|
||||||
import org.minidns.record.SRV;
|
import org.minidns.record.SRV;
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.persistance.FileBackend;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService;
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
import eu.siacs.conversations.xmpp.Jid;
|
import eu.siacs.conversations.xmpp.Jid;
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.google.common.net.InetAddresses;
|
|
||||||
import com.google.common.primitives.Ints;
|
|
||||||
import com.google.common.base.Strings;
|
|
||||||
|
|
||||||
|
|
||||||
public class Resolver {
|
public class Resolver {
|
||||||
|
|
||||||
public static final int DEFAULT_PORT_XMPP = 5222;
|
public static final int DEFAULT_PORT_XMPP = 5222;
|
||||||
|
@ -61,114 +59,114 @@ public class Resolver {
|
||||||
private static XmppConnectionService SERVICE = null;
|
private static XmppConnectionService SERVICE = null;
|
||||||
|
|
||||||
private static List<String> DNSSECLESS_TLDS = Arrays.asList(
|
private static List<String> DNSSECLESS_TLDS = Arrays.asList(
|
||||||
"ae",
|
"ae",
|
||||||
"aero",
|
"aero",
|
||||||
"ai",
|
"ai",
|
||||||
"al",
|
"al",
|
||||||
"ao",
|
"ao",
|
||||||
"aq",
|
"aq",
|
||||||
"as",
|
"as",
|
||||||
"ba",
|
"ba",
|
||||||
"bb",
|
"bb",
|
||||||
"bd",
|
"bd",
|
||||||
"bf",
|
"bf",
|
||||||
"bi",
|
"bi",
|
||||||
"bj",
|
"bj",
|
||||||
"bn",
|
"bn",
|
||||||
"bo",
|
"bo",
|
||||||
"bs",
|
"bs",
|
||||||
"bw",
|
"bw",
|
||||||
"cd",
|
"cd",
|
||||||
"cf",
|
"cf",
|
||||||
"cg",
|
"cg",
|
||||||
"ci",
|
"ci",
|
||||||
"ck",
|
"ck",
|
||||||
"cm",
|
"cm",
|
||||||
"cu",
|
"cu",
|
||||||
"cv",
|
"cv",
|
||||||
"cw",
|
"cw",
|
||||||
"dj",
|
"dj",
|
||||||
"dm",
|
"dm",
|
||||||
"do",
|
"do",
|
||||||
"ec",
|
"ec",
|
||||||
"eg",
|
"eg",
|
||||||
"eh",
|
"eh",
|
||||||
"er",
|
"er",
|
||||||
"et",
|
"et",
|
||||||
"fj",
|
"fj",
|
||||||
"fk",
|
"fk",
|
||||||
"ga",
|
"ga",
|
||||||
"ge",
|
"ge",
|
||||||
"gf",
|
"gf",
|
||||||
"gh",
|
"gh",
|
||||||
"gm",
|
"gm",
|
||||||
"gp",
|
"gp",
|
||||||
"gq",
|
"gq",
|
||||||
"gt",
|
"gt",
|
||||||
"gu",
|
"gu",
|
||||||
"hm",
|
"hm",
|
||||||
"ht",
|
"ht",
|
||||||
"im",
|
"im",
|
||||||
"ir",
|
"ir",
|
||||||
"je",
|
"je",
|
||||||
"jm",
|
"jm",
|
||||||
"jo",
|
"jo",
|
||||||
"ke",
|
"ke",
|
||||||
"kh",
|
"kh",
|
||||||
"km",
|
"km",
|
||||||
"kn",
|
"kn",
|
||||||
"kp",
|
"kp",
|
||||||
"kz",
|
"kz",
|
||||||
"ls",
|
"ls",
|
||||||
"mg",
|
"mg",
|
||||||
"mh",
|
"mh",
|
||||||
"mk",
|
"mk",
|
||||||
"ml",
|
"ml",
|
||||||
"mm",
|
"mm",
|
||||||
"mo",
|
"mo",
|
||||||
"mp",
|
"mp",
|
||||||
"mq",
|
"mq",
|
||||||
"ms",
|
"ms",
|
||||||
"mt",
|
"mt",
|
||||||
"mu",
|
"mu",
|
||||||
"mv",
|
"mv",
|
||||||
"mw",
|
"mw",
|
||||||
"mz",
|
"mz",
|
||||||
"ne",
|
"ne",
|
||||||
"ng",
|
"ng",
|
||||||
"ni",
|
"ni",
|
||||||
"np",
|
"np",
|
||||||
"nr",
|
"nr",
|
||||||
"om",
|
"om",
|
||||||
"pa",
|
"pa",
|
||||||
"pf",
|
"pf",
|
||||||
"pg",
|
"pg",
|
||||||
"pk",
|
"pk",
|
||||||
"pn",
|
"pn",
|
||||||
"ps",
|
"ps",
|
||||||
"py",
|
"py",
|
||||||
"qa",
|
"qa",
|
||||||
"rw",
|
"rw",
|
||||||
"sd",
|
"sd",
|
||||||
"sl",
|
"sl",
|
||||||
"sm",
|
"sm",
|
||||||
"so",
|
"so",
|
||||||
"sr",
|
"sr",
|
||||||
"sv",
|
"sv",
|
||||||
"sy",
|
"sy",
|
||||||
"sz",
|
"sz",
|
||||||
"tc",
|
"tc",
|
||||||
"td",
|
"td",
|
||||||
"tg",
|
"tg",
|
||||||
"tj",
|
"tj",
|
||||||
"to",
|
"to",
|
||||||
"tr",
|
"tr",
|
||||||
"va",
|
"va",
|
||||||
"vg",
|
"vg",
|
||||||
"vi",
|
"vi",
|
||||||
"ye",
|
"ye",
|
||||||
"zm",
|
"zm",
|
||||||
"zw"
|
"zw"
|
||||||
);
|
);
|
||||||
|
|
||||||
protected static final Map<String, String> knownSRV = ImmutableMap.of(
|
protected static final Map<String, String> knownSRV = ImmutableMap.of(
|
||||||
|
@ -232,7 +230,7 @@ public class Resolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Result> resolve(final String domain) {
|
public static List<Result> resolve(final String domain) {
|
||||||
final List<Result> ipResults = fromIpAddress(domain, DEFAULT_PORT_XMPP);
|
final List<Result> ipResults = fromIpAddress(domain);
|
||||||
if (ipResults.size() > 0) {
|
if (ipResults.size() > 0) {
|
||||||
return ipResults;
|
return ipResults;
|
||||||
}
|
}
|
||||||
|
@ -264,7 +262,7 @@ public class Resolver {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
threads[2] = new Thread(() -> {
|
threads[2] = new Thread(() -> {
|
||||||
List<Result> list = resolveNoSrvRecords(DnsName.from(domain), DEFAULT_PORT_XMPP, true);
|
List<Result> list = resolveNoSrvRecords(DnsName.from(domain), true);
|
||||||
synchronized (fallbackResults) {
|
synchronized (fallbackResults) {
|
||||||
fallbackResults.addAll(list);
|
fallbackResults.addAll(list);
|
||||||
}
|
}
|
||||||
|
@ -298,14 +296,14 @@ public class Resolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Result> fromIpAddress(String domain, int port) {
|
private static List<Result> fromIpAddress(String domain) {
|
||||||
if (!IP.matches(domain)) {
|
if (!IP.matches(domain)) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Result result = new Result();
|
Result result = new Result();
|
||||||
result.ip = InetAddress.getByName(domain);
|
result.ip = InetAddress.getByName(domain);
|
||||||
result.port = port;
|
result.port = DEFAULT_PORT_XMPP;
|
||||||
result.authenticated = true;
|
result.authenticated = true;
|
||||||
return Collections.singletonList(result);
|
return Collections.singletonList(result);
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
|
@ -372,25 +370,25 @@ public class Resolver {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Result> resolveNoSrvRecords(DnsName dnsName, int port, boolean withCnames) {
|
private static List<Result> resolveNoSrvRecords(DnsName dnsName, boolean withCnames) {
|
||||||
final List<Result> results = new ArrayList<>();
|
final List<Result> results = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
ResolverResult<A> aResult = resolveWithFallback(dnsName, A.class);
|
ResolverResult<A> aResult = resolveWithFallback(dnsName, A.class);
|
||||||
for (A a : aResult.getAnswersOrEmptySet()) {
|
for (A a : aResult.getAnswersOrEmptySet()) {
|
||||||
Result r = Result.createDefault(dnsName, a.getInetAddress(), port);
|
Result r = Result.createDefault(dnsName, a.getInetAddress());
|
||||||
r.authenticated = aResult.isAuthenticData();
|
r.authenticated = aResult.isAuthenticData();
|
||||||
results.add(r);
|
results.add(r);
|
||||||
}
|
}
|
||||||
ResolverResult<AAAA> aaaaResult = resolveWithFallback(dnsName, AAAA.class);
|
ResolverResult<AAAA> aaaaResult = resolveWithFallback(dnsName, AAAA.class);
|
||||||
for (AAAA aaaa : aaaaResult.getAnswersOrEmptySet()) {
|
for (AAAA aaaa : aaaaResult.getAnswersOrEmptySet()) {
|
||||||
Result r = Result.createDefault(dnsName, aaaa.getInetAddress(), port);
|
Result r = Result.createDefault(dnsName, aaaa.getInetAddress());
|
||||||
r.authenticated = aaaaResult.isAuthenticData();
|
r.authenticated = aaaaResult.isAuthenticData();
|
||||||
results.add(r);
|
results.add(r);
|
||||||
}
|
}
|
||||||
if (results.size() == 0 && withCnames) {
|
if (results.size() == 0 && withCnames) {
|
||||||
ResolverResult<CNAME> cnameResult = resolveWithFallback(dnsName, CNAME.class);
|
ResolverResult<CNAME> cnameResult = resolveWithFallback(dnsName, CNAME.class);
|
||||||
for (CNAME cname : cnameResult.getAnswersOrEmptySet()) {
|
for (CNAME cname : cnameResult.getAnswersOrEmptySet()) {
|
||||||
for (Result r : resolveNoSrvRecords(cname.name, port, false)) {
|
for (Result r : resolveNoSrvRecords(cname.name, false)) {
|
||||||
r.authenticated = r.authenticated && cnameResult.isAuthenticData();
|
r.authenticated = r.authenticated && cnameResult.isAuthenticData();
|
||||||
results.add(r);
|
results.add(r);
|
||||||
}
|
}
|
||||||
|
@ -401,7 +399,7 @@ public class Resolver {
|
||||||
Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + "error resolving fallback records", throwable);
|
Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + "error resolving fallback records", throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
results.add(Result.createDefault(dnsName, port));
|
results.add(Result.createDefault(dnsName));
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,10 +439,6 @@ public class Resolver {
|
||||||
private boolean directTls = false;
|
private boolean directTls = false;
|
||||||
private boolean authenticated = false;
|
private boolean authenticated = false;
|
||||||
private int priority;
|
private int priority;
|
||||||
private long timeRequested;
|
|
||||||
private Socket socket;
|
|
||||||
|
|
||||||
private String logID = "";
|
|
||||||
|
|
||||||
static Result fromRecord(SRV srv, boolean directTls) {
|
static Result fromRecord(SRV srv, boolean directTls) {
|
||||||
Result result = new Result();
|
Result result = new Result();
|
||||||
|
@ -455,18 +449,32 @@ public class Resolver {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result createDefault(DnsName hostname, InetAddress ip, int port) {
|
static Result createDefault(DnsName hostname, InetAddress ip) {
|
||||||
Result result = new Result();
|
Result result = new Result();
|
||||||
result.timeRequested = System.currentTimeMillis();
|
result.port = DEFAULT_PORT_XMPP;
|
||||||
result.port = port;
|
|
||||||
result.hostname = hostname;
|
result.hostname = hostname;
|
||||||
result.ip = ip;
|
result.ip = ip;
|
||||||
result.directTls = useDirectTls(port);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result createDefault(DnsName hostname, int port) {
|
static Result createDefault(DnsName hostname) {
|
||||||
return createDefault(hostname, null, port);
|
return createDefault(hostname, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result fromCursor(Cursor cursor) {
|
||||||
|
final Result result = new Result();
|
||||||
|
try {
|
||||||
|
result.ip = InetAddress.getByAddress(cursor.getBlob(cursor.getColumnIndex(IP)));
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
result.ip = null;
|
||||||
|
}
|
||||||
|
final String hostname = cursor.getString(cursor.getColumnIndex(HOSTNAME));
|
||||||
|
result.hostname = hostname == null ? null : DnsName.from(hostname);
|
||||||
|
result.port = cursor.getInt(cursor.getColumnIndex(PORT));
|
||||||
|
result.priority = cursor.getInt(cursor.getColumnIndex(PRIORITY));
|
||||||
|
result.authenticated = cursor.getInt(cursor.getColumnIndex(AUTHENTICATED)) > 0;
|
||||||
|
result.directTls = cursor.getInt(cursor.getColumnIndex(DIRECT_TLS)) > 0;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -515,20 +523,11 @@ public class Resolver {
|
||||||
return authenticated;
|
return authenticated;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOutdated() {
|
|
||||||
return (System.currentTimeMillis() - timeRequested) > 300_000;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Socket getSocket() {
|
|
||||||
return socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Result{" +
|
return "Result{" +
|
||||||
"ip='" + (ip == null ? null : ip.getHostAddress()) + '\'' +
|
"ip='" + (ip == null ? null : ip.getHostAddress()) + '\'' +
|
||||||
", hostname='" + (hostname == null ? null : hostname.toString()) + '\'' +
|
", hostame='" + (hostname == null ? null : hostname.toString()) + '\'' +
|
||||||
", port=" + port +
|
", port=" + port +
|
||||||
", directTls=" + directTls +
|
", directTls=" + directTls +
|
||||||
", authenticated=" + authenticated +
|
", authenticated=" + authenticated +
|
||||||
|
@ -536,43 +535,6 @@ public class Resolver {
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect() {
|
|
||||||
if (this.socket != null) {
|
|
||||||
this.disconnect();
|
|
||||||
}
|
|
||||||
if (this.ip == null || this.port == 0) {
|
|
||||||
Log.d(Config.LOGTAG, "Resolver did not get IP:port (" + this.ip + ":" + this.port + ")");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final InetSocketAddress addr = new InetSocketAddress(this.ip, this.port);
|
|
||||||
this.socket = new Socket();
|
|
||||||
try {
|
|
||||||
long time = System.currentTimeMillis();
|
|
||||||
this.socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
|
|
||||||
time = System.currentTimeMillis() - time;
|
|
||||||
if (this.logID != null && !this.logID.isEmpty()) {
|
|
||||||
Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": Result (" + this.logID + ") connect: " + toString() + " after: " + time + " ms");
|
|
||||||
} else {
|
|
||||||
Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": Result connect: " + toString() + " after: " + time + " ms");
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
this.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void disconnect() {
|
|
||||||
if (this.socket != null) {
|
|
||||||
FileBackend.close(this.socket);
|
|
||||||
this.socket = null;
|
|
||||||
if (this.logID != null && !this.logID.isEmpty()) {
|
|
||||||
Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": Result (" + this.logID + ") disconnect: " + toString());
|
|
||||||
} else {
|
|
||||||
Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": Result disconnect: " + toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(@NonNull Result result) {
|
public int compareTo(@NonNull Result result) {
|
||||||
if (result.priority == priority) {
|
if (result.priority == priority) {
|
||||||
|
@ -596,31 +558,6 @@ public class Resolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result call() throws Exception {
|
|
||||||
this.connect();
|
|
||||||
if (this.socket != null && this.socket.isConnected()) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
throw new Exception("Resolver.Result was not possible to connect - should be catched by executor");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Result fromCursor(Cursor cursor) {
|
|
||||||
final Result result = new Result();
|
|
||||||
try {
|
|
||||||
result.ip = InetAddress.getByAddress(cursor.getBlob(cursor.getColumnIndex(IP)));
|
|
||||||
} catch (UnknownHostException e) {
|
|
||||||
result.ip = null;
|
|
||||||
}
|
|
||||||
final String hostname = cursor.getString(cursor.getColumnIndex(HOSTNAME));
|
|
||||||
result.hostname = hostname == null ? null : DnsName.from(hostname);
|
|
||||||
result.port = cursor.getInt(cursor.getColumnIndex(PORT));
|
|
||||||
result.directTls = cursor.getInt(cursor.getColumnIndex(DIRECT_TLS)) > 0;
|
|
||||||
result.authenticated = cursor.getInt(cursor.getColumnIndex(AUTHENTICATED)) > 0;
|
|
||||||
result.priority = cursor.getInt(cursor.getColumnIndex(PRIORITY));
|
|
||||||
result.timeRequested = cursor.getLong(cursor.getColumnIndex(TIME_REQUESTED));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContentValues toContentValues() {
|
public ContentValues toContentValues() {
|
||||||
final ContentValues contentValues = new ContentValues();
|
final ContentValues contentValues = new ContentValues();
|
||||||
contentValues.put(IP, ip == null ? null : ip.getAddress());
|
contentValues.put(IP, ip == null ? null : ip.getAddress());
|
||||||
|
@ -629,7 +566,6 @@ public class Resolver {
|
||||||
contentValues.put(PRIORITY, priority);
|
contentValues.put(PRIORITY, priority);
|
||||||
contentValues.put(DIRECT_TLS, directTls ? 1 : 0);
|
contentValues.put(DIRECT_TLS, directTls ? 1 : 0);
|
||||||
contentValues.put(AUTHENTICATED, authenticated ? 1 : 0);
|
contentValues.put(AUTHENTICATED, authenticated ? 1 : 0);
|
||||||
contentValues.put(TIME_REQUESTED, timeRequested);
|
|
||||||
return contentValues;
|
return contentValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -692,4 +628,5 @@ public class Resolver {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
import eu.siacs.conversations.crypto.sasl.HashedToken;
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -77,6 +77,7 @@ import eu.siacs.conversations.crypto.XmppDomainVerifier;
|
||||||
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
||||||
import eu.siacs.conversations.crypto.sasl.ChannelBinding;
|
import eu.siacs.conversations.crypto.sasl.ChannelBinding;
|
||||||
import eu.siacs.conversations.crypto.sasl.ChannelBindingMechanism;
|
import eu.siacs.conversations.crypto.sasl.ChannelBindingMechanism;
|
||||||
|
import eu.siacs.conversations.crypto.sasl.HashedToken;
|
||||||
import eu.siacs.conversations.crypto.sasl.SaslMechanism;
|
import eu.siacs.conversations.crypto.sasl.SaslMechanism;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
|
@ -117,8 +118,50 @@ import eu.siacs.conversations.xmpp.stanzas.streammgmt.AckPacket;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.streammgmt.EnablePacket;
|
import eu.siacs.conversations.xmpp.stanzas.streammgmt.EnablePacket;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket;
|
import eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket;
|
import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket;
|
||||||
|
|
||||||
import okhttp3.HttpUrl;
|
import okhttp3.HttpUrl;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.ConnectException;
|
||||||
|
import java.net.IDN;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
|
import javax.net.ssl.KeyManager;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
import javax.net.ssl.X509KeyManager;
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
|
||||||
public class XmppConnection implements Runnable {
|
public class XmppConnection implements Runnable {
|
||||||
|
|
||||||
private static final int PACKET_IQ = 0;
|
private static final int PACKET_IQ = 0;
|
||||||
|
@ -228,11 +271,8 @@ public class XmppConnection implements Runnable {
|
||||||
return mXmppConnectionService;
|
return mXmppConnectionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fixResource(Context context, Account account) {
|
private static void fixResource(Context context, Account account) {
|
||||||
String resource = account.getResource();
|
String resource = account.getResource();
|
||||||
if (resource != null && !resource.startsWith(context.getString(R.string.app_name) + '[' + BuildConfig.VERSION_NAME + ']')) {
|
|
||||||
account.setResource(createNewResource());
|
|
||||||
}
|
|
||||||
int fixedPartLength =
|
int fixedPartLength =
|
||||||
context.getString(R.string.app_name).length() + 1; // include the trailing dot
|
context.getString(R.string.app_name).length() + 1; // include the trailing dot
|
||||||
int randomPartLength = 4; // 3 bytes
|
int randomPartLength = 4; // 3 bytes
|
||||||
|
@ -320,7 +360,8 @@ public class XmppConnection implements Runnable {
|
||||||
mXmppConnectionService.resetSendingToWaiting(account);
|
mXmppConnectionService.resetSendingToWaiting(account);
|
||||||
}
|
}
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": connecting");
|
Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": connecting");
|
||||||
features.encryptionEnabled = false;
|
this.loginInfo = null;
|
||||||
|
this.features.encryptionEnabled = false;
|
||||||
this.inSmacksSession = false;
|
this.inSmacksSession = false;
|
||||||
this.quickStartInProgress = false;
|
this.quickStartInProgress = false;
|
||||||
this.isBound = false;
|
this.isBound = false;
|
||||||
|
@ -361,6 +402,7 @@ public class XmppConnection implements Runnable {
|
||||||
destination = account.getHostname();
|
destination = account.getHostname();
|
||||||
this.verifiedHostname = destination;
|
this.verifiedHostname = destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int port = account.getPort();
|
final int port = account.getPort();
|
||||||
final boolean directTls = Resolver.useDirectTls(port);
|
final boolean directTls = Resolver.useDirectTls(port);
|
||||||
|
|
||||||
|
@ -452,7 +494,7 @@ public class XmppConnection implements Runnable {
|
||||||
} else {
|
} else {
|
||||||
storedBackupResult =
|
storedBackupResult =
|
||||||
mXmppConnectionService.databaseBackend.findResolverResult(domain);
|
mXmppConnectionService.databaseBackend.findResolverResult(domain);
|
||||||
if (storedBackupResult != null && !results.contains(storedBackupResult) && !storedBackupResult.isOutdated()) {
|
if (storedBackupResult != null && !results.contains(storedBackupResult)) {
|
||||||
results.add(storedBackupResult);
|
results.add(storedBackupResult);
|
||||||
Log.d(
|
Log.d(
|
||||||
Config.LOGTAG,
|
Config.LOGTAG,
|
||||||
|
@ -464,12 +506,18 @@ public class XmppConnection implements Runnable {
|
||||||
final StreamId streamId = this.streamId;
|
final StreamId streamId = this.streamId;
|
||||||
final Resolver.Result resumeLocation = streamId == null ? null : streamId.location;
|
final Resolver.Result resumeLocation = streamId == null ? null : streamId.location;
|
||||||
if (resumeLocation != null) {
|
if (resumeLocation != null) {
|
||||||
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": injected resume location on position 0");
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.getJid().asBareJid()
|
||||||
|
+ ": injected resume location on position 0");
|
||||||
results.add(0, resumeLocation);
|
results.add(0, resumeLocation);
|
||||||
}
|
}
|
||||||
final Resolver.Result seeOtherHost = this.seeOtherHostResolverResult;
|
final Resolver.Result seeOtherHost = this.seeOtherHostResolverResult;
|
||||||
if (seeOtherHost != null) {
|
if (seeOtherHost != null) {
|
||||||
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": injected see-other-host on position 0");
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.getJid().asBareJid()
|
||||||
|
+ ": injected see-other-host on position 0");
|
||||||
results.add(0, seeOtherHost);
|
results.add(0, seeOtherHost);
|
||||||
}
|
}
|
||||||
for (final Iterator<Resolver.Result> iterator = results.iterator();
|
for (final Iterator<Resolver.Result> iterator = results.iterator();
|
||||||
|
@ -611,8 +659,7 @@ public class XmppConnection implements Runnable {
|
||||||
tagReader.setInputStream(socket.getInputStream());
|
tagReader.setInputStream(socket.getInputStream());
|
||||||
tagWriter.beginDocument();
|
tagWriter.beginDocument();
|
||||||
final boolean quickStart;
|
final boolean quickStart;
|
||||||
if (socket instanceof SSLSocket) {
|
if (socket instanceof SSLSocket sslSocket) {
|
||||||
final SSLSocket sslSocket = (SSLSocket) socket;
|
|
||||||
SSLSockets.log(account, sslSocket);
|
SSLSockets.log(account, sslSocket);
|
||||||
quickStart = establishStream(SSLSockets.version(sslSocket));
|
quickStart = establishStream(SSLSockets.version(sslSocket));
|
||||||
} else {
|
} else {
|
||||||
|
@ -622,7 +669,16 @@ public class XmppConnection implements Runnable {
|
||||||
if (Thread.currentThread().isInterrupted()) {
|
if (Thread.currentThread().isInterrupted()) {
|
||||||
throw new InterruptedException();
|
throw new InterruptedException();
|
||||||
}
|
}
|
||||||
final boolean success = tag != null && tag.isStart("stream", Namespace.STREAMS);
|
if (tag == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final boolean success = tag.isStart("stream", Namespace.STREAMS);
|
||||||
|
if (success) {
|
||||||
|
final var from = tag.getAttribute("from");
|
||||||
|
if (from == null || !from.equals(account.getServer())) {
|
||||||
|
throw new StateChangingException(Account.State.HOST_UNKNOWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (success && quickStart) {
|
if (success && quickStart) {
|
||||||
this.quickStartInProgress = true;
|
this.quickStartInProgress = true;
|
||||||
}
|
}
|
||||||
|
@ -679,14 +735,18 @@ public class XmppConnection implements Runnable {
|
||||||
processStreamFeatures(nextTag);
|
processStreamFeatures(nextTag);
|
||||||
} else if (nextTag.isStart("proceed", Namespace.TLS)) {
|
} else if (nextTag.isStart("proceed", Namespace.TLS)) {
|
||||||
switchOverToTls();
|
switchOverToTls();
|
||||||
|
} else if (nextTag.isStart("failure", Namespace.TLS)) {
|
||||||
|
throw new StateChangingException(Account.State.TLS_ERROR);
|
||||||
|
} else if (account.isOptionSet(Account.OPTION_REGISTER)
|
||||||
|
&& nextTag.isStart("iq", Namespace.JABBER_CLIENT)) {
|
||||||
|
processIq(nextTag);
|
||||||
|
} else if (!isSecure() || this.loginInfo == null) {
|
||||||
|
throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
|
||||||
} else if (nextTag.isStart("success")) {
|
} else if (nextTag.isStart("success")) {
|
||||||
final Element success = tagReader.readElement(nextTag);
|
final Element success = tagReader.readElement(nextTag);
|
||||||
if (processSuccess(success)) {
|
if (processSuccess(success)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (nextTag.isStart("failure", Namespace.TLS)) {
|
|
||||||
throw new StateChangingException(Account.State.TLS_ERROR);
|
|
||||||
} else if (nextTag.isStart("failure")) {
|
} else if (nextTag.isStart("failure")) {
|
||||||
final Element failure = tagReader.readElement(nextTag);
|
final Element failure = tagReader.readElement(nextTag);
|
||||||
processFailure(failure);
|
processFailure(failure);
|
||||||
|
@ -694,24 +754,33 @@ public class XmppConnection implements Runnable {
|
||||||
// two step sasl2 - we don’t support this yet
|
// two step sasl2 - we don’t support this yet
|
||||||
throw new StateChangingException(Account.State.INCOMPATIBLE_CLIENT);
|
throw new StateChangingException(Account.State.INCOMPATIBLE_CLIENT);
|
||||||
} else if (nextTag.isStart("challenge")) {
|
} else if (nextTag.isStart("challenge")) {
|
||||||
if (isSecure() && this.loginInfo != null) {
|
final Element challenge = tagReader.readElement(nextTag);
|
||||||
final Element challenge = tagReader.readElement(nextTag);
|
processChallenge(challenge);
|
||||||
processChallenge(challenge);
|
|
||||||
} else {
|
|
||||||
Log.d(
|
|
||||||
Config.LOGTAG,
|
|
||||||
account.getJid().asBareJid()
|
|
||||||
+ ": received 'challenge on an unsecure connection");
|
|
||||||
throw new StateChangingException(Account.State.INCOMPATIBLE_CLIENT);
|
|
||||||
}
|
|
||||||
} else if (!LoginInfo.isSuccess(this.loginInfo)) {
|
} else if (!LoginInfo.isSuccess(this.loginInfo)) {
|
||||||
throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
|
throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
|
||||||
|
} else if (this.streamId != null
|
||||||
|
&& nextTag.isStart("resumed", Namespace.STREAM_MANAGEMENT)) {
|
||||||
|
final Element resumed = tagReader.readElement(nextTag);
|
||||||
|
processResumed(resumed);
|
||||||
|
} else if (nextTag.isStart("failed", Namespace.STREAM_MANAGEMENT)) {
|
||||||
|
final Element failed = tagReader.readElement(nextTag);
|
||||||
|
processFailed(failed, true);
|
||||||
|
} else if (nextTag.isStart("iq", Namespace.JABBER_CLIENT)) {
|
||||||
|
processIq(nextTag);
|
||||||
|
} else if (!isBound) {
|
||||||
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.getJid().asBareJid()
|
||||||
|
+ ": server sent unexpected"
|
||||||
|
+ nextTag.identifier());
|
||||||
|
throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
|
||||||
|
} else if (nextTag.isStart("message", Namespace.JABBER_CLIENT)) {
|
||||||
|
processMessage(nextTag);
|
||||||
|
} else if (nextTag.isStart("presence", Namespace.JABBER_CLIENT)) {
|
||||||
|
processPresence(nextTag);
|
||||||
} else if (nextTag.isStart("enabled", Namespace.STREAM_MANAGEMENT)) {
|
} else if (nextTag.isStart("enabled", Namespace.STREAM_MANAGEMENT)) {
|
||||||
final Element enabled = tagReader.readElement(nextTag);
|
final Element enabled = tagReader.readElement(nextTag);
|
||||||
processEnabled(enabled);
|
processEnabled(enabled);
|
||||||
} else if (nextTag.isStart("resumed", Namespace.STREAM_MANAGEMENT)) {
|
|
||||||
final Element resumed = tagReader.readElement(nextTag);
|
|
||||||
processResumed(resumed);
|
|
||||||
} else if (nextTag.isStart("r", Namespace.STREAM_MANAGEMENT)) {
|
} else if (nextTag.isStart("r", Namespace.STREAM_MANAGEMENT)) {
|
||||||
tagReader.readElement(nextTag);
|
tagReader.readElement(nextTag);
|
||||||
if (Config.EXTENDED_SM_LOGGING) {
|
if (Config.EXTENDED_SM_LOGGING) {
|
||||||
|
@ -766,15 +835,6 @@ public class XmppConnection implements Runnable {
|
||||||
if (acknowledgedMessages) {
|
if (acknowledgedMessages) {
|
||||||
mXmppConnectionService.updateConversationUi();
|
mXmppConnectionService.updateConversationUi();
|
||||||
}
|
}
|
||||||
} else if (nextTag.isStart("failed", Namespace.STREAM_MANAGEMENT)) {
|
|
||||||
final Element failed = tagReader.readElement(nextTag);
|
|
||||||
processFailed(failed, true);
|
|
||||||
} else if (nextTag.isStart("iq", Namespace.JABBER_CLIENT)) {
|
|
||||||
processIq(nextTag);
|
|
||||||
} else if (nextTag.isStart("message", Namespace.JABBER_CLIENT)) {
|
|
||||||
processMessage(nextTag);
|
|
||||||
} else if (nextTag.isStart("presence", Namespace.JABBER_CLIENT)) {
|
|
||||||
processPresence(nextTag);
|
|
||||||
} else {
|
} else {
|
||||||
Log.e(
|
Log.e(
|
||||||
Config.LOGTAG,
|
Config.LOGTAG,
|
||||||
|
@ -810,7 +870,9 @@ public class XmppConnection implements Runnable {
|
||||||
throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
|
throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
response.setContent(currentLoginInfo.saslMechanism.getResponse(challenge.getContent(), sslSocketOrNull(socket)));
|
response.setContent(
|
||||||
|
currentLoginInfo.saslMechanism.getResponse(
|
||||||
|
challenge.getContent(), sslSocketOrNull(socket)));
|
||||||
} catch (final SaslMechanism.AuthenticationException e) {
|
} catch (final SaslMechanism.AuthenticationException e) {
|
||||||
// TODO: Send auth abort tag.
|
// TODO: Send auth abort tag.
|
||||||
Log.e(Config.LOGTAG, e.toString());
|
Log.e(Config.LOGTAG, e.toString());
|
||||||
|
@ -905,7 +967,10 @@ public class XmppConnection implements Runnable {
|
||||||
if (resumed != null && streamId != null) {
|
if (resumed != null && streamId != null) {
|
||||||
if (this.boundStreamFeatures != null) {
|
if (this.boundStreamFeatures != null) {
|
||||||
this.streamFeatures = this.boundStreamFeatures;
|
this.streamFeatures = this.boundStreamFeatures;
|
||||||
Log.d(Config.LOGTAG, "putting previous stream features back in place: " + XmlHelper.printElementNames(this.boundStreamFeatures));
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
"putting previous stream features back in place: "
|
||||||
|
+ XmlHelper.printElementNames(this.boundStreamFeatures));
|
||||||
}
|
}
|
||||||
processResumed(resumed);
|
processResumed(resumed);
|
||||||
} else if (failed != null) {
|
} else if (failed != null) {
|
||||||
|
@ -925,7 +990,7 @@ public class XmppConnection implements Runnable {
|
||||||
processEnabled(streamManagementEnabled);
|
processEnabled(streamManagementEnabled);
|
||||||
waitForDisco = true;
|
waitForDisco = true;
|
||||||
} else {
|
} else {
|
||||||
//if we did not enable stream management in bind do it now
|
// if we did not enable stream management in bind do it now
|
||||||
waitForDisco = enableStreamManagement();
|
waitForDisco = enableStreamManagement();
|
||||||
}
|
}
|
||||||
final boolean negotiatedCarbons;
|
final boolean negotiatedCarbons;
|
||||||
|
@ -957,13 +1022,22 @@ public class XmppConnection implements Runnable {
|
||||||
tokenMechanism = null;
|
tokenMechanism = null;
|
||||||
}
|
}
|
||||||
if (tokenMechanism != null && !Strings.isNullOrEmpty(token)) {
|
if (tokenMechanism != null && !Strings.isNullOrEmpty(token)) {
|
||||||
if (ChannelBinding.priority(tokenMechanism.channelBinding) >= ChannelBindingMechanism.getPriority(currentSaslMechanism)) {
|
if (ChannelBinding.priority(tokenMechanism.channelBinding)
|
||||||
|
>= ChannelBindingMechanism.getPriority(currentSaslMechanism)) {
|
||||||
this.account.setFastToken(tokenMechanism, token);
|
this.account.setFastToken(tokenMechanism, token);
|
||||||
Log.d(
|
Log.d(
|
||||||
Config.LOGTAG,
|
Config.LOGTAG,
|
||||||
account.getJid().asBareJid() + ": storing hashed token " + tokenMechanism);
|
account.getJid().asBareJid()
|
||||||
|
+ ": storing hashed token "
|
||||||
|
+ tokenMechanism);
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": not accepting hashed token "+ tokenMechanism.name()+" for log in mechanism "+currentSaslMechanism.getMechanism());
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.getJid().asBareJid()
|
||||||
|
+ ": not accepting hashed token "
|
||||||
|
+ tokenMechanism.name()
|
||||||
|
+ " for log in mechanism "
|
||||||
|
+ currentSaslMechanism.getMechanism());
|
||||||
this.account.resetFastToken();
|
this.account.resetFastToken();
|
||||||
}
|
}
|
||||||
} else if (this.hashTokenRequest != null) {
|
} else if (this.hashTokenRequest != null) {
|
||||||
|
@ -990,6 +1064,7 @@ public class XmppConnection implements Runnable {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetOutboundStanzaQueue() {
|
private void resetOutboundStanzaQueue() {
|
||||||
synchronized (this.mStanzaQueue) {
|
synchronized (this.mStanzaQueue) {
|
||||||
final ImmutableList.Builder<AbstractAcknowledgeableStanza> intermediateStanzasBuilder =
|
final ImmutableList.Builder<AbstractAcknowledgeableStanza> intermediateStanzasBuilder =
|
||||||
|
@ -1042,7 +1117,6 @@ public class XmppConnection implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void processFailure(final Element failure) throws IOException {
|
private void processFailure(final Element failure) throws IOException {
|
||||||
final SaslMechanism.Version version;
|
final SaslMechanism.Version version;
|
||||||
try {
|
try {
|
||||||
|
@ -1118,7 +1192,9 @@ public class XmppConnection implements Runnable {
|
||||||
} else {
|
} else {
|
||||||
Log.d(
|
Log.d(
|
||||||
Config.LOGTAG,
|
Config.LOGTAG,
|
||||||
account.getJid().asBareJid() + ": stream management enabled. resume at: " + streamId.location);
|
account.getJid().asBareJid()
|
||||||
|
+ ": stream management enabled. resume at: "
|
||||||
|
+ streamId.location);
|
||||||
}
|
}
|
||||||
this.streamId = streamId;
|
this.streamId = streamId;
|
||||||
this.stanzasReceived = 0;
|
this.stanzasReceived = 0;
|
||||||
|
@ -1164,8 +1240,7 @@ public class XmppConnection implements Runnable {
|
||||||
Config.LOGTAG,
|
Config.LOGTAG,
|
||||||
account.getJid().asBareJid() + ": resending " + failedStanzas.size() + " stanzas");
|
account.getJid().asBareJid() + ": resending " + failedStanzas.size() + " stanzas");
|
||||||
for (final AbstractAcknowledgeableStanza packet : failedStanzas) {
|
for (final AbstractAcknowledgeableStanza packet : failedStanzas) {
|
||||||
if (packet instanceof MessagePacket) {
|
if (packet instanceof MessagePacket message) {
|
||||||
MessagePacket message = (MessagePacket) packet;
|
|
||||||
mXmppConnectionService.markMessage(
|
mXmppConnectionService.markMessage(
|
||||||
account,
|
account,
|
||||||
message.getTo().asBareJid(),
|
message.getTo().asBareJid(),
|
||||||
|
@ -1237,8 +1312,7 @@ public class XmppConnection implements Runnable {
|
||||||
+ mStanzaQueue.keyAt(i));
|
+ mStanzaQueue.keyAt(i));
|
||||||
}
|
}
|
||||||
final AbstractAcknowledgeableStanza stanza = mStanzaQueue.valueAt(i);
|
final AbstractAcknowledgeableStanza stanza = mStanzaQueue.valueAt(i);
|
||||||
if (stanza instanceof MessagePacket && acknowledgedListener != null) {
|
if (stanza instanceof MessagePacket packet && acknowledgedListener != null) {
|
||||||
final MessagePacket packet = (MessagePacket) stanza;
|
|
||||||
final String id = packet.getId();
|
final String id = packet.getId();
|
||||||
final Jid to = packet.getTo();
|
final Jid to = packet.getTo();
|
||||||
if (id != null && to != null) {
|
if (id != null && to != null) {
|
||||||
|
@ -1255,20 +1329,13 @@ public class XmppConnection implements Runnable {
|
||||||
|
|
||||||
private @NonNull Element processPacket(final Tag currentTag, final int packetType)
|
private @NonNull Element processPacket(final Tag currentTag, final int packetType)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
final Element element;
|
final Element element =
|
||||||
switch (packetType) {
|
switch (packetType) {
|
||||||
case PACKET_IQ:
|
case PACKET_IQ -> new IqPacket();
|
||||||
element = new IqPacket();
|
case PACKET_MESSAGE -> new MessagePacket();
|
||||||
break;
|
case PACKET_PRESENCE -> new PresencePacket();
|
||||||
case PACKET_MESSAGE:
|
default -> throw new AssertionError("Should never encounter invalid type");
|
||||||
element = new MessagePacket();
|
};
|
||||||
break;
|
|
||||||
case PACKET_PRESENCE:
|
|
||||||
element = new PresencePacket();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new AssertionError("Should never encounter invalid type");
|
|
||||||
}
|
|
||||||
element.setAttributes(currentTag.getAttributes());
|
element.setAttributes(currentTag.getAttributes());
|
||||||
Tag nextTag = tagReader.readTag();
|
Tag nextTag = tagReader.readTag();
|
||||||
if (nextTag == null) {
|
if (nextTag == null) {
|
||||||
|
@ -1312,7 +1379,6 @@ public class XmppConnection implements Runnable {
|
||||||
|
|
||||||
private void processIq(final Tag currentTag) throws IOException {
|
private void processIq(final Tag currentTag) throws IOException {
|
||||||
final IqPacket packet = (IqPacket) processPacket(currentTag, PACKET_IQ);
|
final IqPacket packet = (IqPacket) processPacket(currentTag, PACKET_IQ);
|
||||||
|
|
||||||
if (!packet.valid()) {
|
if (!packet.valid()) {
|
||||||
Log.e(
|
Log.e(
|
||||||
Config.LOGTAG,
|
Config.LOGTAG,
|
||||||
|
@ -1323,58 +1389,77 @@ public class XmppConnection implements Runnable {
|
||||||
+ "'");
|
+ "'");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (Thread.currentThread().isInterrupted()) {
|
||||||
if (packet instanceof JinglePacket) {
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.getJid().asBareJid() + "Not processing iq. Thread was interrupted");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (packet instanceof JinglePacket jinglePacket && isBound) {
|
||||||
if (this.jingleListener != null) {
|
if (this.jingleListener != null) {
|
||||||
this.jingleListener.onJinglePacketReceived(account, (JinglePacket) packet);
|
this.jingleListener.onJinglePacketReceived(account, jinglePacket);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
OnIqPacketReceived callback = null;
|
final var callback = getIqPacketReceivedCallback(packet);
|
||||||
synchronized (this.packetCallbacks) {
|
if (callback == null) {
|
||||||
final Pair<IqPacket, Pair<OnIqPacketReceived, ScheduledFuture>> packetCallbackDuple =
|
Log.d(
|
||||||
packetCallbacks.get(packet.getId());
|
Config.LOGTAG,
|
||||||
if (packetCallbackDuple != null) {
|
account.getJid().asBareJid().toString()
|
||||||
ScheduledFuture timeoutFuture = packetCallbackDuple.second.second;
|
+ ": no callback registered for IQ from "
|
||||||
// Packets to the server should have responses from the server
|
+ packet.getFrom());
|
||||||
if (packetCallbackDuple.first.toServer(account)) {
|
return;
|
||||||
if (packet.fromServer(account)) {
|
|
||||||
if (timeoutFuture == null || timeoutFuture.cancel(false)) {
|
|
||||||
callback = packetCallbackDuple.second.first;
|
|
||||||
}
|
|
||||||
packetCallbacks.remove(packet.getId());
|
|
||||||
} else {
|
|
||||||
Log.e(
|
|
||||||
Config.LOGTAG,
|
|
||||||
account.getJid().asBareJid().toString()
|
|
||||||
+ ": ignoring spoofed iq packet");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (packet.getFrom() != null
|
|
||||||
&& packet.getFrom().equals(packetCallbackDuple.first.getTo())) {
|
|
||||||
if (timeoutFuture == null || timeoutFuture.cancel(false)) {
|
|
||||||
callback = packetCallbackDuple.second.first;
|
|
||||||
}
|
|
||||||
packetCallbacks.remove(packet.getId());
|
|
||||||
} else {
|
|
||||||
Log.e(
|
|
||||||
Config.LOGTAG,
|
|
||||||
account.getJid().asBareJid().toString()
|
|
||||||
+ ": ignoring spoofed iq packet");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (packet.getType() == IqPacket.TYPE.GET
|
|
||||||
|| packet.getType() == IqPacket.TYPE.SET) {
|
|
||||||
callback = this.unregisteredIqListener;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (callback != null) {
|
final ScheduledFuture timeoutFuture = callback.second;
|
||||||
try {
|
try {
|
||||||
callback.onIqPacketReceived(account, packet);
|
if (timeoutFuture == null || timeoutFuture.cancel(false)) {
|
||||||
} catch (StateChangingError error) {
|
callback.first.onIqPacketReceived(account, packet);
|
||||||
throw new StateChangingException(error.state);
|
}
|
||||||
|
} catch (final StateChangingError error) {
|
||||||
|
throw new StateChangingException(error.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pair<OnIqPacketReceived, ScheduledFuture> getIqPacketReceivedCallback(final IqPacket stanza)
|
||||||
|
throws StateChangingException {
|
||||||
|
final boolean isRequest =
|
||||||
|
stanza.getType() == IqPacket.TYPE.GET || stanza.getType() == IqPacket.TYPE.SET;
|
||||||
|
if (isRequest) {
|
||||||
|
if (isBound) {
|
||||||
|
return new Pair<>(this.unregisteredIqListener, null);
|
||||||
|
} else {
|
||||||
|
throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
synchronized (this.packetCallbacks) {
|
||||||
|
final var pair = packetCallbacks.get(stanza.getId());
|
||||||
|
if (pair == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (pair.first.toServer(account)) {
|
||||||
|
if (stanza.fromServer(account)) {
|
||||||
|
packetCallbacks.remove(stanza.getId());
|
||||||
|
return pair.second;
|
||||||
|
} else {
|
||||||
|
Log.e(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.getJid().asBareJid().toString()
|
||||||
|
+ ": ignoring spoofed iq packet");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (stanza.getFrom() != null && stanza.getFrom().equals(pair.first.getTo())) {
|
||||||
|
packetCallbacks.remove(stanza.getId());
|
||||||
|
return pair.second;
|
||||||
|
} else {
|
||||||
|
Log.e(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.getJid().asBareJid().toString()
|
||||||
|
+ ": ignoring spoofed iq packet");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processMessage(final Tag currentTag) throws IOException {
|
private void processMessage(final Tag currentTag) throws IOException {
|
||||||
|
@ -1389,11 +1474,18 @@ public class XmppConnection implements Runnable {
|
||||||
+ "'");
|
+ "'");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (Thread.currentThread().isInterrupted()) {
|
||||||
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.getJid().asBareJid()
|
||||||
|
+ "Not processing message. Thread was interrupted");
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.messageListener.onMessagePacketReceived(account, packet);
|
this.messageListener.onMessagePacketReceived(account, packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processPresence(final Tag currentTag) throws IOException {
|
private void processPresence(final Tag currentTag) throws IOException {
|
||||||
PresencePacket packet = (PresencePacket) processPacket(currentTag, PACKET_PRESENCE);
|
final PresencePacket packet = (PresencePacket) processPacket(currentTag, PACKET_PRESENCE);
|
||||||
if (!packet.valid()) {
|
if (!packet.valid()) {
|
||||||
Log.e(
|
Log.e(
|
||||||
Config.LOGTAG,
|
Config.LOGTAG,
|
||||||
|
@ -1404,6 +1496,13 @@ public class XmppConnection implements Runnable {
|
||||||
+ "'");
|
+ "'");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (Thread.currentThread().isInterrupted()) {
|
||||||
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.getJid().asBareJid()
|
||||||
|
+ "Not processing presence. Thread was interrupted");
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.presenceListener.onPresencePacketReceived(account, packet);
|
this.presenceListener.onPresencePacketReceived(account, packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1455,7 +1554,7 @@ public class XmppConnection implements Runnable {
|
||||||
this.dane = false;
|
this.dane = false;
|
||||||
final SSLSocketFactory sslSocketFactory;
|
final SSLSocketFactory sslSocketFactory;
|
||||||
try {
|
try {
|
||||||
sslSocketFactory = getSSLSocketFactory(account.getPort(), (d) -> this.dane = d);
|
sslSocketFactory = getSSLSocketFactory(socket.getPort(), (d) -> this.dane = d);
|
||||||
} catch (final NoSuchAlgorithmException | KeyManagementException e) {
|
} catch (final NoSuchAlgorithmException | KeyManagementException e) {
|
||||||
throw new StateChangingException(Account.State.TLS_ERROR);
|
throw new StateChangingException(Account.State.TLS_ERROR);
|
||||||
}
|
}
|
||||||
|
@ -1463,7 +1562,7 @@ public class XmppConnection implements Runnable {
|
||||||
final SSLSocket sslSocket =
|
final SSLSocket sslSocket =
|
||||||
(SSLSocket)
|
(SSLSocket)
|
||||||
sslSocketFactory.createSocket(
|
sslSocketFactory.createSocket(
|
||||||
socket, address.getHostAddress(), account.getPort(), true);
|
socket, address.getHostAddress(), socket.getPort(), true);
|
||||||
SSLSockets.setSecurity(sslSocket);
|
SSLSockets.setSecurity(sslSocket);
|
||||||
SSLSockets.setHostname(sslSocket, IDN.toASCII(account.getServer()));
|
SSLSockets.setHostname(sslSocket, IDN.toASCII(account.getServer()));
|
||||||
SSLSockets.setApplicationProtocol(sslSocket, "xmpp-client");
|
SSLSockets.setApplicationProtocol(sslSocket, "xmpp-client");
|
||||||
|
@ -1592,11 +1691,11 @@ public class XmppConnection implements Runnable {
|
||||||
throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
|
throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSecure() {
|
private boolean isSecure() {
|
||||||
return features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS || account.isOnion() || account.isI2P();
|
return features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS || account.isOnion() || account.isI2P();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void authenticate(final SaslMechanism.Version version) throws IOException {
|
private void authenticate(final SaslMechanism.Version version) throws IOException {
|
||||||
final Element authElement;
|
final Element authElement;
|
||||||
if (version == SaslMechanism.Version.SASL) {
|
if (version == SaslMechanism.Version.SASL) {
|
||||||
|
@ -1609,10 +1708,12 @@ public class XmppConnection implements Runnable {
|
||||||
this.streamFeatures.findChild("sasl-channel-binding", Namespace.CHANNEL_BINDING);
|
this.streamFeatures.findChild("sasl-channel-binding", Namespace.CHANNEL_BINDING);
|
||||||
final Collection<ChannelBinding> channelBindings = ChannelBinding.of(cbElement);
|
final Collection<ChannelBinding> channelBindings = ChannelBinding.of(cbElement);
|
||||||
final SaslMechanism.Factory factory = new SaslMechanism.Factory(account);
|
final SaslMechanism.Factory factory = new SaslMechanism.Factory(account);
|
||||||
final SaslMechanism saslMechanism = factory.of(mechanisms, channelBindings, version, SSLSockets.version(this.socket));
|
final SaslMechanism saslMechanism =
|
||||||
|
factory.of(mechanisms, channelBindings, version, SSLSockets.version(this.socket));
|
||||||
this.validate(saslMechanism, mechanisms);
|
this.validate(saslMechanism, mechanisms);
|
||||||
final boolean quickStartAvailable;
|
final boolean quickStartAvailable;
|
||||||
final String firstMessage = saslMechanism.getClientFirstMessage(sslSocketOrNull(this.socket));
|
final String firstMessage =
|
||||||
|
saslMechanism.getClientFirstMessage(sslSocketOrNull(this.socket));
|
||||||
final boolean usingFast = SaslMechanism.hashedToken(saslMechanism);
|
final boolean usingFast = SaslMechanism.hashedToken(saslMechanism);
|
||||||
final Element authenticate;
|
final Element authenticate;
|
||||||
if (version == SaslMechanism.Version.SASL) {
|
if (version == SaslMechanism.Version.SASL) {
|
||||||
|
@ -1621,7 +1722,7 @@ public class XmppConnection implements Runnable {
|
||||||
authenticate.setContent(firstMessage);
|
authenticate.setContent(firstMessage);
|
||||||
}
|
}
|
||||||
quickStartAvailable = false;
|
quickStartAvailable = false;
|
||||||
this.loginInfo = new LoginInfo(saslMechanism,version,Collections.emptyList());
|
this.loginInfo = new LoginInfo(saslMechanism, version, Collections.emptyList());
|
||||||
} else if (version == SaslMechanism.Version.SASL_2) {
|
} else if (version == SaslMechanism.Version.SASL_2) {
|
||||||
final Element inline = authElement.findChild("inline", Namespace.SASL_2);
|
final Element inline = authElement.findChild("inline", Namespace.SASL_2);
|
||||||
final boolean sm = inline != null && inline.hasChild("sm", Namespace.STREAM_MANAGEMENT);
|
final boolean sm = inline != null && inline.hasChild("sm", Namespace.STREAM_MANAGEMENT);
|
||||||
|
@ -1629,7 +1730,8 @@ public class XmppConnection implements Runnable {
|
||||||
if (usingFast) {
|
if (usingFast) {
|
||||||
hashTokenRequest = null;
|
hashTokenRequest = null;
|
||||||
} else {
|
} else {
|
||||||
final Element fast = inline == null ? null : inline.findChild("fast", Namespace.FAST);
|
final Element fast =
|
||||||
|
inline == null ? null : inline.findChild("fast", Namespace.FAST);
|
||||||
final Collection<String> fastMechanisms = SaslMechanism.mechanisms(fast);
|
final Collection<String> fastMechanisms = SaslMechanism.mechanisms(fast);
|
||||||
hashTokenRequest =
|
hashTokenRequest =
|
||||||
HashedToken.Mechanism.best(fastMechanisms, SSLSockets.version(this.socket));
|
HashedToken.Mechanism.best(fastMechanisms, SSLSockets.version(this.socket));
|
||||||
|
@ -1650,9 +1752,11 @@ public class XmppConnection implements Runnable {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.loginInfo = new LoginInfo(saslMechanism,version,bindFeatures);
|
this.loginInfo = new LoginInfo(saslMechanism, version, bindFeatures);
|
||||||
this.hashTokenRequest = hashTokenRequest;
|
this.hashTokenRequest = hashTokenRequest;
|
||||||
authenticate = generateAuthenticationRequest(firstMessage, usingFast, hashTokenRequest, bindFeatures, sm);
|
authenticate =
|
||||||
|
generateAuthenticationRequest(
|
||||||
|
firstMessage, usingFast, hashTokenRequest, bindFeatures, sm);
|
||||||
} else {
|
} else {
|
||||||
throw new AssertionError("Missing implementation for " + version);
|
throw new AssertionError("Missing implementation for " + version);
|
||||||
}
|
}
|
||||||
|
@ -1669,7 +1773,6 @@ public class XmppConnection implements Runnable {
|
||||||
+ "/"
|
+ "/"
|
||||||
+ LoginInfo.mechanism(this.loginInfo).getMechanism());
|
+ LoginInfo.mechanism(this.loginInfo).getMechanism());
|
||||||
authenticate.setAttribute("mechanism", LoginInfo.mechanism(this.loginInfo).getMechanism());
|
authenticate.setAttribute("mechanism", LoginInfo.mechanism(this.loginInfo).getMechanism());
|
||||||
|
|
||||||
synchronized (this.mStanzaQueue) {
|
synchronized (this.mStanzaQueue) {
|
||||||
this.stanzasSentBeforeAuthentication = this.stanzasSent;
|
this.stanzasSentBeforeAuthentication = this.stanzasSent;
|
||||||
tagWriter.writeElement(authenticate);
|
tagWriter.writeElement(authenticate);
|
||||||
|
@ -1681,7 +1784,9 @@ public class XmppConnection implements Runnable {
|
||||||
return inline != null && inline.hasChild("fast", Namespace.FAST);
|
return inline != null && inline.hasChild("fast", Namespace.FAST);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validate(final @Nullable SaslMechanism saslMechanism, Collection<String> mechanisms) throws StateChangingException {
|
private void validate(
|
||||||
|
final @Nullable SaslMechanism saslMechanism, Collection<String> mechanisms)
|
||||||
|
throws StateChangingException {
|
||||||
if (saslMechanism == null) {
|
if (saslMechanism == null) {
|
||||||
Log.d(
|
Log.d(
|
||||||
Config.LOGTAG,
|
Config.LOGTAG,
|
||||||
|
@ -1708,8 +1813,10 @@ public class XmppConnection implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Element generateAuthenticationRequest(final String firstMessage, final boolean usingFast) {
|
private Element generateAuthenticationRequest(
|
||||||
return generateAuthenticationRequest(firstMessage, usingFast, null, Bind2.QUICKSTART_FEATURES, true);
|
final String firstMessage, final boolean usingFast) {
|
||||||
|
return generateAuthenticationRequest(
|
||||||
|
firstMessage, usingFast, null, Bind2.QUICKSTART_FEATURES, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Element generateAuthenticationRequest(
|
private Element generateAuthenticationRequest(
|
||||||
|
@ -1854,6 +1961,7 @@ public class XmppConnection implements Runnable {
|
||||||
is = null;
|
is = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is != null) {
|
if (is != null) {
|
||||||
Bitmap captcha = BitmapFactory.decodeStream(is);
|
Bitmap captcha = BitmapFactory.decodeStream(is);
|
||||||
try {
|
try {
|
||||||
|
@ -1918,8 +2026,10 @@ public class XmppConnection implements Runnable {
|
||||||
resetAttemptCount(true);
|
resetAttemptCount(true);
|
||||||
resetStreamId();
|
resetStreamId();
|
||||||
clearIqCallbacks();
|
clearIqCallbacks();
|
||||||
this.stanzasSent = 0;
|
synchronized (this.mStanzaQueue) {
|
||||||
mStanzaQueue.clear();
|
this.stanzasSent = 0;
|
||||||
|
this.mStanzaQueue.clear();
|
||||||
|
}
|
||||||
this.redirectionUrl = null;
|
this.redirectionUrl = null;
|
||||||
synchronized (this.disco) {
|
synchronized (this.disco) {
|
||||||
disco.clear();
|
disco.clear();
|
||||||
|
@ -2447,19 +2557,25 @@ public class XmppConnection implements Runnable {
|
||||||
} else if (streamError.hasChild("policy-violation")) {
|
} else if (streamError.hasChild("policy-violation")) {
|
||||||
this.lastConnect = SystemClock.elapsedRealtime();
|
this.lastConnect = SystemClock.elapsedRealtime();
|
||||||
final String text = streamError.findChildContent("text");
|
final String text = streamError.findChildContent("text");
|
||||||
if (text != null) {
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": policy violation. " + text);
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": policy violation. " + text);
|
failPendingMessages(text);
|
||||||
failPendingMessages(text);
|
|
||||||
}
|
|
||||||
throw new StateChangingException(Account.State.POLICY_VIOLATION);
|
throw new StateChangingException(Account.State.POLICY_VIOLATION);
|
||||||
} else if (streamError.hasChild("see-other-host")) {
|
} else if (streamError.hasChild("see-other-host")) {
|
||||||
final String seeOtherHost = streamError.findChildContent("see-other-host");
|
final String seeOtherHost = streamError.findChildContent("see-other-host");
|
||||||
final Resolver.Result currentResolverResult = this.currentResolverResult;
|
final Resolver.Result currentResolverResult = this.currentResolverResult;
|
||||||
if (Strings.isNullOrEmpty(seeOtherHost) || currentResolverResult == null) {
|
if (Strings.isNullOrEmpty(seeOtherHost) || currentResolverResult == null) {
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": stream error " + streamError);
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.getJid().asBareJid() + ": stream error " + streamError);
|
||||||
throw new StateChangingException(Account.State.STREAM_ERROR);
|
throw new StateChangingException(Account.State.STREAM_ERROR);
|
||||||
}
|
}
|
||||||
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": see other host: "+seeOtherHost+" "+currentResolverResult);
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.getJid().asBareJid()
|
||||||
|
+ ": see other host: "
|
||||||
|
+ seeOtherHost
|
||||||
|
+ " "
|
||||||
|
+ currentResolverResult);
|
||||||
final Resolver.Result seeOtherResult = currentResolverResult.seeOtherHost(seeOtherHost);
|
final Resolver.Result seeOtherResult = currentResolverResult.seeOtherHost(seeOtherHost);
|
||||||
if (seeOtherResult != null) {
|
if (seeOtherResult != null) {
|
||||||
this.seeOtherHostResolverResult = seeOtherResult;
|
this.seeOtherHostResolverResult = seeOtherResult;
|
||||||
|
@ -2477,8 +2593,7 @@ public class XmppConnection implements Runnable {
|
||||||
synchronized (this.mStanzaQueue) {
|
synchronized (this.mStanzaQueue) {
|
||||||
for (int i = 0; i < mStanzaQueue.size(); ++i) {
|
for (int i = 0; i < mStanzaQueue.size(); ++i) {
|
||||||
final AbstractAcknowledgeableStanza stanza = mStanzaQueue.valueAt(i);
|
final AbstractAcknowledgeableStanza stanza = mStanzaQueue.valueAt(i);
|
||||||
if (stanza instanceof MessagePacket) {
|
if (stanza instanceof MessagePacket packet) {
|
||||||
final MessagePacket packet = (MessagePacket) stanza;
|
|
||||||
final String id = packet.getId();
|
final String id = packet.getId();
|
||||||
final Jid to = packet.getTo();
|
final Jid to = packet.getTo();
|
||||||
mXmppConnectionService.markMessage(
|
mXmppConnectionService.markMessage(
|
||||||
|
@ -2493,7 +2608,8 @@ public class XmppConnection implements Runnable {
|
||||||
final boolean secureConnection = sslVersion != SSLSockets.Version.NONE;
|
final boolean secureConnection = sslVersion != SSLSockets.Version.NONE;
|
||||||
final SaslMechanism quickStartMechanism;
|
final SaslMechanism quickStartMechanism;
|
||||||
if (secureConnection) {
|
if (secureConnection) {
|
||||||
quickStartMechanism = SaslMechanism.ensureAvailable(account.getQuickStartMechanism(), sslVersion);
|
quickStartMechanism =
|
||||||
|
SaslMechanism.ensureAvailable(account.getQuickStartMechanism(), sslVersion);
|
||||||
} else {
|
} else {
|
||||||
quickStartMechanism = null;
|
quickStartMechanism = null;
|
||||||
}
|
}
|
||||||
|
@ -2502,10 +2618,16 @@ public class XmppConnection implements Runnable {
|
||||||
&& quickStartMechanism != null
|
&& quickStartMechanism != null
|
||||||
&& account.isOptionSet(Account.OPTION_QUICKSTART_AVAILABLE)) {
|
&& account.isOptionSet(Account.OPTION_QUICKSTART_AVAILABLE)) {
|
||||||
mXmppConnectionService.restoredFromDatabaseLatch.await();
|
mXmppConnectionService.restoredFromDatabaseLatch.await();
|
||||||
this.loginInfo = new LoginInfo(quickStartMechanism, SaslMechanism.Version.SASL_2, Bind2.QUICKSTART_FEATURES);
|
this.loginInfo =
|
||||||
|
new LoginInfo(
|
||||||
|
quickStartMechanism,
|
||||||
|
SaslMechanism.Version.SASL_2,
|
||||||
|
Bind2.QUICKSTART_FEATURES);
|
||||||
final boolean usingFast = quickStartMechanism instanceof HashedToken;
|
final boolean usingFast = quickStartMechanism instanceof HashedToken;
|
||||||
final Element authenticate =
|
final Element authenticate =
|
||||||
generateAuthenticationRequest(quickStartMechanism.getClientFirstMessage(sslSocketOrNull(this.socket)), usingFast);
|
generateAuthenticationRequest(
|
||||||
|
quickStartMechanism.getClientFirstMessage(sslSocketOrNull(this.socket)),
|
||||||
|
usingFast);
|
||||||
authenticate.setAttribute("mechanism", quickStartMechanism.getMechanism());
|
authenticate.setAttribute("mechanism", quickStartMechanism.getMechanism());
|
||||||
sendStartStream(true, false);
|
sendStartStream(true, false);
|
||||||
synchronized (this.mStanzaQueue) {
|
synchronized (this.mStanzaQueue) {
|
||||||
|
@ -2614,17 +2736,23 @@ public class XmppConnection implements Runnable {
|
||||||
+ " do not write stanza to unbound stream "
|
+ " do not write stanza to unbound stream "
|
||||||
+ packet.toString());
|
+ packet.toString());
|
||||||
}
|
}
|
||||||
if (packet instanceof AbstractAcknowledgeableStanza) {
|
if (packet instanceof AbstractAcknowledgeableStanza stanza) {
|
||||||
AbstractAcknowledgeableStanza stanza = (AbstractAcknowledgeableStanza) packet;
|
|
||||||
if (this.mStanzaQueue.size() != 0) {
|
if (this.mStanzaQueue.size() != 0) {
|
||||||
int currentHighestKey = this.mStanzaQueue.keyAt(this.mStanzaQueue.size() - 1);
|
int currentHighestKey = this.mStanzaQueue.keyAt(this.mStanzaQueue.size() - 1);
|
||||||
if (currentHighestKey != stanzasSent) {
|
if (currentHighestKey != stanzasSent) {
|
||||||
throw new AssertionError("Stanza count messed up");
|
throw new AssertionError("Stanza count messed up");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
++stanzasSent;
|
++stanzasSent;
|
||||||
if (Config.EXTENDED_SM_LOGGING) {
|
if (Config.EXTENDED_SM_LOGGING) {
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid()+": counting outbound "+packet.getName()+" as #" + stanzasSent);
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.getJid().asBareJid()
|
||||||
|
+ ": counting outbound "
|
||||||
|
+ packet.getName()
|
||||||
|
+ " as #"
|
||||||
|
+ stanzasSent);
|
||||||
}
|
}
|
||||||
this.mStanzaQueue.append(stanzasSent, stanza);
|
this.mStanzaQueue.append(stanzasSent, stanza);
|
||||||
if (stanza instanceof MessagePacket && stanza.getId() != null && inSmacksSession) {
|
if (stanza instanceof MessagePacket && stanza.getId() != null && inSmacksSession) {
|
||||||
|
@ -2748,7 +2876,7 @@ public class XmppConnection implements Runnable {
|
||||||
this.boundStreamFeatures = null;
|
this.boundStreamFeatures = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Entry<Jid, ServiceDiscoveryResult>> findDiscoItemsByFeature(final String feature) {
|
private List<Entry<Jid, ServiceDiscoveryResult>> findDiscoItemsByFeature(final String feature) {
|
||||||
synchronized (this.disco) {
|
synchronized (this.disco) {
|
||||||
final List<Entry<Jid, ServiceDiscoveryResult>> items = new ArrayList<>();
|
final List<Entry<Jid, ServiceDiscoveryResult>> items = new ArrayList<>();
|
||||||
for (final Entry<Jid, ServiceDiscoveryResult> cursor : this.disco.entrySet()) {
|
for (final Entry<Jid, ServiceDiscoveryResult> cursor : this.disco.entrySet()) {
|
||||||
|
@ -2807,7 +2935,7 @@ public class XmppConnection implements Runnable {
|
||||||
public int getTimeToNextAttempt(final boolean aggressive) {
|
public int getTimeToNextAttempt(final boolean aggressive) {
|
||||||
final int interval;
|
final int interval;
|
||||||
if (aggressive) {
|
if (aggressive) {
|
||||||
interval = Math.min((int) (3 * Math.pow(1.3,attempt)), 60);
|
interval = Math.min((int) (3 * Math.pow(1.3, attempt)), 60);
|
||||||
} else {
|
} else {
|
||||||
final int additionalTime =
|
final int additionalTime =
|
||||||
account.getLastErrorStatus() == Account.State.POLICY_VIOLATION ? 3 : 0;
|
account.getLastErrorStatus() == Account.State.POLICY_VIOLATION ? 3 : 0;
|
||||||
|
@ -2937,7 +3065,6 @@ public class XmppConnection implements Runnable {
|
||||||
return loginInfo == null ? null : loginInfo.saslMechanism;
|
return loginInfo == null ? null : loginInfo.saslMechanism;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void success(final String challenge, final SSLSocket sslSocket)
|
public void success(final String challenge, final SSLSocket sslSocket)
|
||||||
throws SaslMechanism.AuthenticationException {
|
throws SaslMechanism.AuthenticationException {
|
||||||
final var response = this.saslMechanism.getResponse(challenge, sslSocket);
|
final var response = this.saslMechanism.getResponse(challenge, sslSocket);
|
||||||
|
@ -3029,11 +3156,6 @@ public class XmppConnection implements Runnable {
|
||||||
&& pepPublishOptions();
|
&& pepPublishOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean avatarConversion() {
|
|
||||||
return hasDiscoFeature(account.getJid().asBareJid(), Namespace.AVATAR_CONVERSION)
|
|
||||||
&& pepPublishOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean blocking() {
|
public boolean blocking() {
|
||||||
return hasDiscoFeature(account.getDomain(), Namespace.BLOCKING);
|
return hasDiscoFeature(account.getDomain(), Namespace.BLOCKING);
|
||||||
}
|
}
|
||||||
|
@ -3059,7 +3181,8 @@ public class XmppConnection implements Runnable {
|
||||||
public boolean sm() {
|
public boolean sm() {
|
||||||
return streamId != null
|
return streamId != null
|
||||||
|| (connection.streamFeatures != null
|
|| (connection.streamFeatures != null
|
||||||
&& connection.streamFeatures.hasChild("sm", Namespace.STREAM_MANAGEMENT));
|
&& connection.streamFeatures.hasChild(
|
||||||
|
"sm", Namespace.STREAM_MANAGEMENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean csi() {
|
public boolean csi() {
|
||||||
|
@ -3180,7 +3303,8 @@ public class XmppConnection implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean bookmarks2() {
|
public boolean bookmarks2() {
|
||||||
return pepPublishOptions() && hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS2_COMPAT);
|
return pepPublishOptions()
|
||||||
|
&& hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS2_COMPAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean externalServiceDiscovery() {
|
public boolean externalServiceDiscovery() {
|
||||||
|
|
Loading…
Add table
Reference in a new issue