@@ -52,6 +52,7 @@ import eu.siacs.conversations.entities.ServiceDiscoveryResult;
import eu.siacs.conversations.services.ShortcutService;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.MimeUtils;
+import eu.siacs.conversations.utils.Resolver;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.mam.MamReference;
@@ -61,7 +62,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history";
- private static final int DATABASE_VERSION = 38;
+ private static final int DATABASE_VERSION = 39;
private static String CREATE_CONTATCS_STATEMENT = "create table "
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@@ -148,9 +149,18 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ ") ON CONFLICT IGNORE"
+ ");";
- private static String START_TIMES_TABLE = "start_times";
-
- private static String CREATE_START_TIMES_TABLE = "create table "+START_TIMES_TABLE+" (timestamp NUMBER);";
+ private static String RESOLVER_RESULTS_TABLENAME = "resolver_results";
+
+ private static String CREATE_RESOLVER_RESULTS_TABLE = "create table "+RESOLVER_RESULTS_TABLENAME+"("
+ + Resolver.Result.DOMAIN + " TEXT,"
+ + Resolver.Result.HOSTNAME + " TEXT,"
+ + Resolver.Result.IP + " BLOB,"
+ + Resolver.Result.PRIORITY + " NUMBER,"
+ + Resolver.Result.DIRECT_TLS + " NUMBER,"
+ + Resolver.Result.AUTHENTICATED + " NUMBER,"
+ + Resolver.Result.PORT + " NUMBER,"
+ + "UNIQUE("+Resolver.Result.DOMAIN+") ON CONFLICT REPLACE"
+ + ");";
private static String CREATE_MESSAGE_TIME_INDEX = "create INDEX message_time_index ON "+Message.TABLENAME+"("+Message.TIME_SENT+")";
private static String CREATE_MESSAGE_CONVERSATION_INDEX = "create INDEX message_conversation_index ON "+Message.TABLENAME+"("+Message.CONVERSATION+")";
@@ -213,7 +223,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
db.execSQL(CREATE_IDENTITIES_STATEMENT);
db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
- db.execSQL(CREATE_START_TIMES_TABLE);
+ db.execSQL(CREATE_RESOLVER_RESULTS_TABLE);
}
@Override
@@ -369,9 +379,6 @@ public class DatabaseBackend extends SQLiteOpenHelper {
if (oldVersion < 29 && newVersion >= 29) {
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.ERROR_MESSAGE + " TEXT");
}
- if (oldVersion < 30 && newVersion >= 30) {
- db.execSQL(CREATE_START_TIMES_TABLE);
- }
if (oldVersion >= 15 && oldVersion < 31 && newVersion >= 31) {
db.execSQL("ALTER TABLE "+ SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN "+SQLiteAxolotlStore.TRUST + " TEXT");
db.execSQL("ALTER TABLE "+ SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN "+SQLiteAxolotlStore.ACTIVE + " NUMBER");
@@ -465,6 +472,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
if (oldVersion < 38 && newVersion >= 38) {
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.MARKABLE + " NUMBER DEFAULT 0");
}
+
+ if (oldVersion < 39 && newVersion >= 39) {
+ db.execSQL(CREATE_RESOLVER_RESULTS_TABLE);
+ }
}
private static ContentValues createFingerprintStatusContentValues(FingerprintStatus.Trust trust, boolean active) {
@@ -605,6 +616,28 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return result;
}
+ public void saveResolverResult(String domain, Resolver.Result result) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ ContentValues contentValues = result.toContentValues();
+ contentValues.put(Resolver.Result.DOMAIN,domain);
+ db.insert(RESOLVER_RESULTS_TABLENAME, null, contentValues);
+ }
+
+ public Resolver.Result findResolverResult(String domain) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String where = Resolver.Result.DOMAIN+"=?";
+ String[] whereArgs = {domain};
+ Cursor cursor = db.query(RESOLVER_RESULTS_TABLENAME,null,where,whereArgs,null,null,null);
+ Resolver.Result result = null;
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ result = Resolver.Result.fromCursor(cursor);
+ }
+ cursor.close();
+ }
+ return result;
+ }
+
public void insertPresenceTemplate(PresenceTemplate template) {
SQLiteDatabase db = this.getWritableDatabase();
db.insert(PresenceTemplate.TABELNAME, null, template.getContentValues());
@@ -1,12 +1,14 @@
package eu.siacs.conversations.utils;
-import android.content.Context;
+import android.content.ContentValues;
+import android.database.Cursor;
import android.support.annotation.NonNull;
import android.util.Log;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -34,231 +36,293 @@ import eu.siacs.conversations.services.XmppConnectionService;
public class Resolver {
- private static final String DIRECT_TLS_SERVICE = "_xmpps-client";
- private static final String STARTTLS_SERICE = "_xmpp-client";
-
- private static final String NETWORK_IS_UNREACHABLE = "Network is unreachable";
-
- private static XmppConnectionService SERVICE = null;
-
-
- public static void init(XmppConnectionService service) {
- Resolver.SERVICE = service;
- DNSClient.removeDNSServerLookupMechanism(AndroidUsingExec.INSTANCE);
- DNSClient.addDnsServerLookupMechanism(AndroidUsingExecLowPriority.INSTANCE);
- DNSClient.addDnsServerLookupMechanism(new AndroidUsingLinkProperties(service));
- }
-
- public static List<Result> resolve(String domain) throws NetworkIsUnreachableException {
- List<Result> results = new ArrayList<>();
- HashSet<String> messages = new HashSet<>();
- try {
- results.addAll(resolveSrv(domain, true));
- } catch (MultipleIoException e) {
- messages.addAll(extractMessages(e));
- } catch (Throwable throwable) {
- Log.d(Config.LOGTAG,Resolver.class.getSimpleName()+": error resolving SRV record (direct TLS)",throwable);
- }
- try {
- results.addAll(resolveSrv(domain, false));
- } catch (MultipleIoException e) {
- messages.addAll(extractMessages(e));
- } catch (Throwable throwable) {
- Log.d(Config.LOGTAG,Resolver.class.getSimpleName()+": error resolving SRV record (STARTTLS)",throwable);
- }
- if (results.size() == 0) {
- if (messages.size() == 1 && messages.contains(NETWORK_IS_UNREACHABLE)) {
- throw new NetworkIsUnreachableException();
- }
- results.addAll(resolveNoSrvRecords(DNSName.from(domain),true));
- }
- Collections.sort(results);
- Log.d(Config.LOGTAG,Resolver.class.getSimpleName()+": "+results.toString());
- return results;
- }
-
- private static HashSet<String> extractMessages(MultipleIoException e) {
- HashSet<String> messages = new HashSet<>();
- for(Exception inner : e.getExceptions()) {
- if (inner instanceof MultipleIoException) {
- messages.addAll(extractMessages((MultipleIoException) inner));
- } else {
- messages.add(inner.getMessage());
- }
- }
- return messages;
- }
-
- private static List<Result> resolveSrv(String domain, final boolean directTls) throws IOException {
- if (Thread.currentThread().isInterrupted()) {
- return Collections.emptyList();
- }
- DNSName dnsName = DNSName.from((directTls ? DIRECT_TLS_SERVICE : STARTTLS_SERICE)+"._tcp."+domain);
- ResolverResult<SRV> result = resolveWithFallback(dnsName,SRV.class);
- List<Result> results = new ArrayList<>();
- for(SRV record : result.getAnswersOrEmptySet()) {
- final boolean addedIPv4 = results.addAll(resolveIp(record,A.class,result.isAuthenticData(),directTls));
- results.addAll(resolveIp(record,AAAA.class,result.isAuthenticData(),directTls));
- if (!addedIPv4 && !Thread.currentThread().isInterrupted()) {
- Result resolverResult = Result.fromRecord(record, directTls);
- resolverResult.authenticated = resolverResult.isAuthenticated();
- results.add(resolverResult);
- }
- }
- return results;
- }
-
- private static <D extends InternetAddressRR> List<Result> resolveIp(SRV srv, Class<D> type, boolean authenticated, boolean directTls) {
- if (Thread.currentThread().isInterrupted()) {
- return Collections.emptyList();
- }
- List<Result> list = new ArrayList<>();
- try {
- ResolverResult<D> results = resolveWithFallback(srv.name,type, authenticated);
- for (D record : results.getAnswersOrEmptySet()) {
- Result resolverResult = Result.fromRecord(srv, directTls);
- resolverResult.authenticated = results.isAuthenticData() && authenticated;
- resolverResult.ip = record.getInetAddress();
- list.add(resolverResult);
- }
- } catch (Throwable t) {
- Log.d(Config.LOGTAG,Resolver.class.getSimpleName()+": error resolving "+type.getSimpleName()+" "+t.getMessage());
- }
- return list;
- }
-
- private static List<Result> resolveNoSrvRecords(DNSName dnsName, boolean withCnames) {
- List<Result> results = new ArrayList<>();
- try {
- for(A a : resolveWithFallback(dnsName,A.class,false).getAnswersOrEmptySet()) {
- results.add(Result.createDefault(dnsName,a.getInetAddress()));
- }
- for(AAAA aaaa : resolveWithFallback(dnsName,AAAA.class,false).getAnswersOrEmptySet()) {
- results.add(Result.createDefault(dnsName,aaaa.getInetAddress()));
- }
- if (results.size() == 0 && withCnames) {
- for (CNAME cname : resolveWithFallback(dnsName, CNAME.class, false).getAnswersOrEmptySet()) {
- results.addAll(resolveNoSrvRecords(cname.name, false));
- }
- }
- } catch (Throwable throwable) {
- Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + "error resolving fallback records",throwable);
- }
- results.add(Result.createDefault(dnsName));
- return results;
- }
-
- private static <D extends Data> ResolverResult<D> resolveWithFallback(DNSName dnsName, Class<D> type) throws IOException {
- return resolveWithFallback(dnsName,type,validateHostname());
- }
-
- private static <D extends Data> ResolverResult<D> resolveWithFallback(DNSName dnsName, Class<D> type, boolean validateHostname) throws IOException {
- final Question question = new Question(dnsName, Record.TYPE.getType(type));
- if (!validateHostname) {
- return ResolverApi.INSTANCE.resolve(question);
- }
- try {
- return DnssecResolverApi.INSTANCE.resolveDnssecReliable(question);
- } catch (DNSSECResultNotAuthenticException e) {
- Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": error resolving " + type.getSimpleName() + " with DNSSEC. Trying DNS instead.", e);
- } catch (IOException e) {
- throw e;
- } catch (Throwable throwable) {
- Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": error resolving " + type.getSimpleName() + " with DNSSEC. Trying DNS instead.", throwable);
- }
- return ResolverApi.INSTANCE.resolve(question);
- }
-
- private static boolean validateHostname() {
- return SERVICE != null && SERVICE.getBooleanPreference("validate_hostname", R.bool.validate_hostname);
- }
-
- public static class Result implements Comparable<Result> {
- private InetAddress ip;
- private DNSName hostname;
- private int port = 5222;
- private boolean directTls = false;
- private boolean authenticated =false;
- private int priority;
-
- public InetAddress getIp() {
- return ip;
- }
-
- public int getPort() {
- return port;
- }
-
- public DNSName getHostname() {
- return hostname;
- }
-
- public boolean isDirectTls() {
- return directTls;
- }
-
- public boolean isAuthenticated() {
- return authenticated;
- }
-
- @Override
- public String toString() {
- return "Result{" +
- "ip='" + (ip==null?null:ip.getHostAddress()) + '\'' +
- ", hostame='" + hostname.toString() + '\'' +
- ", port=" + port +
- ", directTls=" + directTls +
- ", authenticated=" + authenticated +
- ", priority=" + priority +
- '}';
- }
-
- @Override
- public int compareTo(@NonNull Result result) {
- if (result.priority == priority) {
- if (directTls == result.directTls) {
- if (ip == null && result.ip == null) {
- return 0;
- } else if (ip != null && result.ip != null) {
- if (ip instanceof Inet4Address && result.ip instanceof Inet4Address) {
- return 0;
- } else {
- return ip instanceof Inet4Address ? -1 : 1;
- }
- } else {
- return ip != null ? -1 : 1;
- }
- } else {
- return directTls ? -1 : 1;
- }
- } else {
- return priority - result.priority;
- }
- }
-
- public static Result fromRecord(SRV srv, boolean directTls) {
- Result result = new Result();
- result.port = srv.port;
- result.hostname = srv.name;
- result.directTls = directTls;
- result.priority = srv.priority;
- return result;
- }
-
- public static Result createDefault(DNSName hostname, InetAddress ip) {
- Result result = new Result();
- result.port = 5222;
- result.hostname = hostname;
- result.ip = ip;
- return result;
- }
-
- public static Result createDefault(DNSName hostname) {
- return createDefault(hostname,null);
- }
- }
- public static class NetworkIsUnreachableException extends Exception {
-
- }
+ private static final String DIRECT_TLS_SERVICE = "_xmpps-client";
+ private static final String STARTTLS_SERICE = "_xmpp-client";
+
+ private static final String NETWORK_IS_UNREACHABLE = "Network is unreachable";
+
+ private static XmppConnectionService SERVICE = null;
+
+
+ public static void init(XmppConnectionService service) {
+ Resolver.SERVICE = service;
+ DNSClient.removeDNSServerLookupMechanism(AndroidUsingExec.INSTANCE);
+ DNSClient.addDnsServerLookupMechanism(AndroidUsingExecLowPriority.INSTANCE);
+ DNSClient.addDnsServerLookupMechanism(new AndroidUsingLinkProperties(service));
+ }
+
+ public static List<Result> resolve(String domain) throws NetworkIsUnreachableException {
+ List<Result> results = new ArrayList<>();
+ HashSet<String> messages = new HashSet<>();
+ try {
+ results.addAll(resolveSrv(domain, true));
+ } catch (MultipleIoException e) {
+ messages.addAll(extractMessages(e));
+ } catch (Throwable throwable) {
+ Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": error resolving SRV record (direct TLS)", throwable);
+ }
+ try {
+ results.addAll(resolveSrv(domain, false));
+ } catch (MultipleIoException e) {
+ messages.addAll(extractMessages(e));
+ } catch (Throwable throwable) {
+ Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": error resolving SRV record (STARTTLS)", throwable);
+ }
+ if (results.size() == 0) {
+ if (messages.size() == 1 && messages.contains(NETWORK_IS_UNREACHABLE)) {
+ throw new NetworkIsUnreachableException();
+ }
+ results.addAll(resolveNoSrvRecords(DNSName.from(domain), true));
+ }
+ Collections.sort(results);
+ Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": " + results.toString());
+ return results;
+ }
+
+ private static HashSet<String> extractMessages(MultipleIoException e) {
+ HashSet<String> messages = new HashSet<>();
+ for (Exception inner : e.getExceptions()) {
+ if (inner instanceof MultipleIoException) {
+ messages.addAll(extractMessages((MultipleIoException) inner));
+ } else {
+ messages.add(inner.getMessage());
+ }
+ }
+ return messages;
+ }
+
+ private static List<Result> resolveSrv(String domain, final boolean directTls) throws IOException {
+ if (Thread.currentThread().isInterrupted()) {
+ return Collections.emptyList();
+ }
+ DNSName dnsName = DNSName.from((directTls ? DIRECT_TLS_SERVICE : STARTTLS_SERICE) + "._tcp." + domain);
+ ResolverResult<SRV> result = resolveWithFallback(dnsName, SRV.class);
+ List<Result> results = new ArrayList<>();
+ for (SRV record : result.getAnswersOrEmptySet()) {
+ final boolean addedIPv4 = results.addAll(resolveIp(record, A.class, result.isAuthenticData(), directTls));
+ results.addAll(resolveIp(record, AAAA.class, result.isAuthenticData(), directTls));
+ if (!addedIPv4 && !Thread.currentThread().isInterrupted()) {
+ Result resolverResult = Result.fromRecord(record, directTls);
+ resolverResult.authenticated = resolverResult.isAuthenticated();
+ results.add(resolverResult);
+ }
+ }
+ return results;
+ }
+
+ private static <D extends InternetAddressRR> List<Result> resolveIp(SRV srv, Class<D> type, boolean authenticated, boolean directTls) {
+ if (Thread.currentThread().isInterrupted()) {
+ return Collections.emptyList();
+ }
+ List<Result> list = new ArrayList<>();
+ try {
+ ResolverResult<D> results = resolveWithFallback(srv.name, type, authenticated);
+ for (D record : results.getAnswersOrEmptySet()) {
+ Result resolverResult = Result.fromRecord(srv, directTls);
+ resolverResult.authenticated = results.isAuthenticData() && authenticated;
+ resolverResult.ip = record.getInetAddress();
+ list.add(resolverResult);
+ }
+ } catch (Throwable t) {
+ Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": error resolving " + type.getSimpleName() + " " + t.getMessage());
+ }
+ return list;
+ }
+
+ private static List<Result> resolveNoSrvRecords(DNSName dnsName, boolean withCnames) {
+ List<Result> results = new ArrayList<>();
+ try {
+ for (A a : resolveWithFallback(dnsName, A.class, false).getAnswersOrEmptySet()) {
+ results.add(Result.createDefault(dnsName, a.getInetAddress()));
+ }
+ for (AAAA aaaa : resolveWithFallback(dnsName, AAAA.class, false).getAnswersOrEmptySet()) {
+ results.add(Result.createDefault(dnsName, aaaa.getInetAddress()));
+ }
+ if (results.size() == 0 && withCnames) {
+ for (CNAME cname : resolveWithFallback(dnsName, CNAME.class, false).getAnswersOrEmptySet()) {
+ results.addAll(resolveNoSrvRecords(cname.name, false));
+ }
+ }
+ } catch (Throwable throwable) {
+ Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + "error resolving fallback records", throwable);
+ }
+ results.add(Result.createDefault(dnsName));
+ return results;
+ }
+
+ private static <D extends Data> ResolverResult<D> resolveWithFallback(DNSName dnsName, Class<D> type) throws IOException {
+ return resolveWithFallback(dnsName, type, validateHostname());
+ }
+
+ private static <D extends Data> ResolverResult<D> resolveWithFallback(DNSName dnsName, Class<D> type, boolean validateHostname) throws IOException {
+ final Question question = new Question(dnsName, Record.TYPE.getType(type));
+ if (!validateHostname) {
+ return ResolverApi.INSTANCE.resolve(question);
+ }
+ try {
+ return DnssecResolverApi.INSTANCE.resolveDnssecReliable(question);
+ } catch (DNSSECResultNotAuthenticException e) {
+ Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": error resolving " + type.getSimpleName() + " with DNSSEC. Trying DNS instead.", e);
+ } catch (IOException e) {
+ throw e;
+ } catch (Throwable throwable) {
+ Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": error resolving " + type.getSimpleName() + " with DNSSEC. Trying DNS instead.", throwable);
+ }
+ return ResolverApi.INSTANCE.resolve(question);
+ }
+
+ private static boolean validateHostname() {
+ return SERVICE != null && SERVICE.getBooleanPreference("validate_hostname", R.bool.validate_hostname);
+ }
+
+ public static class Result implements Comparable<Result> {
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Result result = (Result) o;
+
+ if (port != result.port) return false;
+ if (directTls != result.directTls) return false;
+ if (authenticated != result.authenticated) return false;
+ if (priority != result.priority) return false;
+ if (ip != null ? !ip.equals(result.ip) : result.ip != null) return false;
+ return hostname != null ? hostname.equals(result.hostname) : result.hostname == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = ip != null ? ip.hashCode() : 0;
+ result = 31 * result + (hostname != null ? hostname.hashCode() : 0);
+ result = 31 * result + port;
+ result = 31 * result + (directTls ? 1 : 0);
+ result = 31 * result + (authenticated ? 1 : 0);
+ result = 31 * result + priority;
+ return result;
+ }
+
+ public static final String DOMAIN = "domain";
+
+ public static final String IP = "ip";
+ public static final String HOSTNAME = "hostname";
+ public static final String PORT = "port";
+ public static final String PRIORITY = "priority";
+ public static final String DIRECT_TLS = "directTls";
+ public static final String AUTHENTICATED = "authenticated";
+
+ private InetAddress ip;
+ private DNSName hostname;
+ private int port = 5222;
+ private boolean directTls = false;
+ private boolean authenticated = false;
+ private int priority;
+
+ public InetAddress getIp() {
+ return ip;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public DNSName getHostname() {
+ return hostname;
+ }
+
+ public boolean isDirectTls() {
+ return directTls;
+ }
+
+ public boolean isAuthenticated() {
+ return authenticated;
+ }
+
+ @Override
+ public String toString() {
+ return "Result{" +
+ "ip='" + (ip == null ? null : ip.getHostAddress()) + '\'' +
+ ", hostame='" + hostname.toString() + '\'' +
+ ", port=" + port +
+ ", directTls=" + directTls +
+ ", authenticated=" + authenticated +
+ ", priority=" + priority +
+ '}';
+ }
+
+ @Override
+ public int compareTo(@NonNull Result result) {
+ if (result.priority == priority) {
+ if (directTls == result.directTls) {
+ if (ip == null && result.ip == null) {
+ return 0;
+ } else if (ip != null && result.ip != null) {
+ if (ip instanceof Inet4Address && result.ip instanceof Inet4Address) {
+ return 0;
+ } else {
+ return ip instanceof Inet4Address ? -1 : 1;
+ }
+ } else {
+ return ip != null ? -1 : 1;
+ }
+ } else {
+ return directTls ? -1 : 1;
+ }
+ } else {
+ return priority - result.priority;
+ }
+ }
+
+ public static Result fromRecord(SRV srv, boolean directTls) {
+ Result result = new Result();
+ result.port = srv.port;
+ result.hostname = srv.name;
+ result.directTls = directTls;
+ result.priority = srv.priority;
+ return result;
+ }
+
+ public static Result createDefault(DNSName hostname, InetAddress ip) {
+ Result result = new Result();
+ result.port = 5222;
+ result.hostname = hostname;
+ result.ip = ip;
+ return result;
+ }
+
+ public static Result createDefault(DNSName hostname) {
+ 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;
+ }
+ result.hostname = DNSName.from(cursor.getString(cursor.getColumnIndex(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;
+ }
+
+ public ContentValues toContentValues() {
+ final ContentValues contentValues = new ContentValues();
+ contentValues.put(IP, ip.getAddress());
+ contentValues.put(HOSTNAME, hostname.toString());
+ contentValues.put(PORT, port);
+ contentValues.put(PRIORITY, priority);
+ contentValues.put(DIRECT_TLS, directTls ? 1 : 0);
+ contentValues.put(AUTHENTICATED, authenticated ? 1 : 0);
+ return contentValues;
+ }
+ }
+
+ public static class NetworkIsUnreachableException extends Exception {
+
+ }
}