DNSHelper.java

  1package eu.siacs.conversations.utils;
  2
  3import de.measite.minidns.Client;
  4import de.measite.minidns.DNSMessage;
  5import de.measite.minidns.Record;
  6import de.measite.minidns.Record.TYPE;
  7import de.measite.minidns.Record.CLASS;
  8import de.measite.minidns.record.SRV;
  9import de.measite.minidns.record.A;
 10import de.measite.minidns.record.AAAA;
 11import de.measite.minidns.record.Data;
 12import de.measite.minidns.util.NameUtil;
 13import eu.siacs.conversations.Config;
 14import eu.siacs.conversations.xmpp.jid.Jid;
 15
 16import java.io.IOException;
 17import java.net.InetAddress;
 18import java.net.SocketTimeoutException;
 19import java.util.ArrayList;
 20import java.util.Collections;
 21import java.util.Random;
 22import java.util.TreeMap;
 23
 24import android.os.Bundle;
 25import android.util.Log;
 26
 27public class DNSHelper {
 28	protected static Client client = new Client();
 29
 30	public static Bundle getSRVRecord(final Jid jid) throws IOException {
 31        final String host = jid.getDomainpart();
 32		String dns[] = client.findDNS();
 33
 34		if (dns != null) {
 35			for (String dnsserver : dns) {
 36				InetAddress ip = InetAddress.getByName(dnsserver);
 37				Bundle b = queryDNS(host, ip);
 38				if (b.containsKey("values")) {
 39					return b;
 40				}
 41			}
 42		}
 43		return queryDNS(host, InetAddress.getByName("8.8.8.8"));
 44	}
 45
 46	public static Bundle queryDNS(String host, InetAddress dnsServer) {
 47		Bundle bundle = new Bundle();
 48		try {
 49			String qname = "_xmpp-client._tcp." + host;
 50			Log.d(Config.LOGTAG, "using dns server: " + dnsServer.getHostAddress() + " to look up " + host);
 51			DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServer.getHostAddress());
 52
 53			TreeMap<Integer, ArrayList<SRV>> priorities = new TreeMap<>();
 54			TreeMap<String, ArrayList<String>> ips4 = new TreeMap<>();
 55			TreeMap<String, ArrayList<String>> ips6 = new TreeMap<>();
 56
 57			for (Record[] rrset : new Record[][] { message.getAnswers(), message.getAdditionalResourceRecords() }) {
 58				for (Record rr : rrset) {
 59					Data d = rr.getPayload();
 60					if (d instanceof SRV && NameUtil.idnEquals(qname, rr.getName())) {
 61						SRV srv = (SRV) d;
 62						if (!priorities.containsKey(srv.getPriority())) {
 63							priorities.put(srv.getPriority(),new ArrayList<SRV>());
 64						}
 65						priorities.get(srv.getPriority()).add(srv);
 66					}
 67					if (d instanceof A) {
 68						A a = (A) d;
 69						if (!ips4.containsKey(rr.getName())) {
 70							ips4.put(rr.getName(), new ArrayList<String>());
 71						}
 72						ips4.get(rr.getName()).add(a.toString());
 73					}
 74					if (d instanceof AAAA) {
 75						AAAA aaaa = (AAAA) d;
 76						if (!ips6.containsKey(rr.getName())) {
 77							ips6.put(rr.getName(), new ArrayList<String>());
 78						}
 79						ips6.get(rr.getName()).add("[" + aaaa.toString() + "]");
 80					}
 81				}
 82			}
 83
 84			ArrayList<SRV> result = new ArrayList<>();
 85			for (ArrayList<SRV> s : priorities.values()) {
 86				result.addAll(s);
 87			}
 88
 89			ArrayList<Bundle> values = new ArrayList<>();
 90			if (result.size() == 0) {
 91				DNSMessage response;
 92				response = client.query(host, TYPE.A, CLASS.IN, dnsServer.getHostAddress());
 93				for(int i = 0; i < response.getAnswers().length; ++i) {
 94					values.add(createNamePortBundle(host,5222,response.getAnswers()[i].getPayload()));
 95				}
 96				response = client.query(host, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
 97				for(int i = 0; i < response.getAnswers().length; ++i) {
 98					values.add(createNamePortBundle(host,5222,response.getAnswers()[i].getPayload()));
 99				}
100				bundle.putParcelableArrayList("values", values);
101				return bundle;
102			}
103			for (SRV srv : result) {
104				if (ips6.containsKey(srv.getName())) {
105					values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips6));
106				} else {
107					DNSMessage response = client.query(srv.getName(), TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
108					for(int i = 0; i < response.getAnswers().length; ++i) {
109						values.add(createNamePortBundle(host,5222,response.getAnswers()[i].getPayload()));
110					}
111				}
112				if (ips4.containsKey(srv.getName())) {
113					values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips4));
114				} else {
115					DNSMessage response = client.query(srv.getName(), TYPE.A, CLASS.IN, dnsServer.getHostAddress());
116					for(int i = 0; i < response.getAnswers().length; ++i) {
117						values.add(createNamePortBundle(host,5222,response.getAnswers()[i].getPayload()));
118					}
119				}
120				values.add(createNamePortBundle(srv.getName(), srv.getPort()));
121			}
122			bundle.putParcelableArrayList("values", values);
123		} catch (SocketTimeoutException e) {
124			bundle.putString("error", "timeout");
125		} catch (Exception e) {
126			Log.d(Config.LOGTAG,e.getMessage());
127			bundle.putString("error", "unhandled");
128		}
129		return bundle;
130	}
131
132	private static Bundle createNamePortBundle(String name, int port) {
133		Bundle namePort = new Bundle();
134		namePort.putString("name", name);
135		namePort.putInt("port", port);
136		return namePort;
137	}
138
139	private static Bundle createNamePortBundle(String name, int port, TreeMap<String, ArrayList<String>> ips) {
140		Bundle namePort = new Bundle();
141		namePort.putString("name", name);
142		namePort.putInt("port", port);
143		if (ips!=null) {
144			ArrayList<String> ip = ips.get(name);
145			Collections.shuffle(ip, new Random());
146			namePort.putString("ip", ip.get(0));
147		}
148		return namePort;
149	}
150
151	private static Bundle createNamePortBundle(String name, int port, Data data) {
152		Bundle namePort = new Bundle();
153		namePort.putString("name", name);
154		namePort.putInt("port", port);
155		if (data instanceof A) {
156			namePort.putString("ip", data.toString());
157		} else if (data instanceof AAAA) {
158			namePort.putString("ip","["+data.toString()+"]");
159		}
160		return namePort;
161	}
162
163	final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
164
165	public static String bytesToHex(byte[] bytes) {
166		char[] hexChars = new char[bytes.length * 2];
167		for (int j = 0; j < bytes.length; j++) {
168			int v = bytes[j] & 0xFF;
169			hexChars[j * 2] = hexArray[v >>> 4];
170			hexChars[j * 2 + 1] = hexArray[v & 0x0F];
171		}
172		return new String(hexChars);
173	}
174}