/tags/cmwrap-1.5.3beta1/src/net/biaji/android/cmwrap/services/DNSServer.java
Java | 518 lines | 345 code | 78 blank | 95 comment | 36 complexity | de4c10483de25681f3c0d2bc06d9084c MD5 | raw file
- package net.biaji.android.cmwrap.services;
- import java.io.ByteArrayOutputStream;
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.io.Serializable;
- import java.net.DatagramPacket;
- import java.net.DatagramSocket;
- import java.net.InetAddress;
- import java.net.Socket;
- import java.net.SocketException;
- import java.net.UnknownHostException;
- import java.util.ConcurrentModificationException;
- import java.util.Hashtable;
- import net.biaji.android.cmwrap.Logger;
- import net.biaji.android.cmwrap.utils.Utils;
- /**
- * ?????DNS??
- *
- * @author biaji
- *
- */
- public class DNSServer implements WrapServer {
- private final String TAG = "CMWRAP->DNSServer";
- private String homePath;
- private final String CACHE_PATH = "/cache";
- private final String CACHE_FILE = "/dnscache";
- private DatagramSocket srvSocket;
- private int srvPort = 7442;
- private String name;
- protected String proxyHost, dnsHost;
- protected int proxyPort, dnsPort;
- public final int DNS_PKG_HEADER_LEN = 12;
- final private int[] DNS_HEADERS = { 0, 0, 0x81, 0x80, 0, 0, 0, 0, 0, 0, 0,
- 0 };
- final private int[] DNS_PAYLOAD = { 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x3c, 0x00, 0x04 };
- private boolean inService = false;
- private Hashtable<String, DnsResponse> dnsCache = new Hashtable<String, DnsResponse>();
- /**
- * ???????
- *
- */
- private Hashtable<String, String> orgCache = new Hashtable<String, String>();
- private String target = "8.8.4.4:53";
- private final String[] iptablesRules = new String[] { "iptables -t nat -A OUTPUT %1$s -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:7442" };
- public DNSServer(String name, int port, String proxyHost, int proxyPort,
- String dnsHost, int dnsPort) {
- this.name = name;
- this.srvPort = port;
- this.proxyHost = proxyHost;
- this.proxyPort = proxyPort;
- this.dnsHost = dnsHost;
- this.dnsPort = dnsPort;
- if (dnsHost != null && !dnsHost.equals(""))
- target = dnsHost + ":" + dnsPort;
- Utils.flushDns(dnsHost);
- initOrgCache();
- try {
- srvSocket = new DatagramSocket(srvPort, InetAddress
- .getByName("127.0.0.1"));
- inService = true;
- Logger.d(TAG, this.name + "?????? " + port);
- } catch (SocketException e) {
- Logger.e(TAG, "DNSServer?????????" + port, e);
- } catch (UnknownHostException e) {
- Logger.e(TAG, "DNSServer?????????" + port, e);
- }
- }
- public void run() {
- loadCache();
- byte[] qbuffer = new byte[576];
- long starTime = System.currentTimeMillis();
- while (true) {
- try {
- DatagramPacket dnsq = new DatagramPacket(qbuffer,
- qbuffer.length);
- srvSocket.receive(dnsq);
- // ????DNS?????
- byte[] data = dnsq.getData();
- int dnsqLength = dnsq.getLength();
- byte[] udpreq = new byte[dnsqLength];
- System.arraycopy(data, 0, udpreq, 0, dnsqLength);
- // ???????????
- String questDomain = getRequestDomain(udpreq);
- Logger.d(TAG, "??" + questDomain);
- if (dnsCache.containsKey(questDomain)) {
- sendDns(dnsCache.get(questDomain).getDnsResponse(), dnsq,
- srvSocket);
- Logger.d(TAG, "????");
- } else if (orgCache.containsKey(questDomain)) { // ??????????
- byte[] answer = createDNSResponse(udpreq, orgCache
- .get(questDomain));
- addToCache(questDomain, answer);
- sendDns(answer, dnsq, srvSocket);
- Logger.d(TAG, "?????" + orgCache);
- } else {
- starTime = System.currentTimeMillis();
- byte[] answer = fetchAnswer(udpreq);
- if (answer != null && answer.length != 0) {
- addToCache(questDomain, answer);
- sendDns(answer, dnsq, srvSocket);
- Logger.d(TAG, "????DNS??????"
- + answer.length + " ???"
- + (System.currentTimeMillis() - starTime)
- / 1000 + "s");
- } else {
- Logger.e(TAG, "??DNS???0");
- }
- }
- } catch (SocketException e) {
- Logger.e(TAG, e.getLocalizedMessage());
- break;
- } catch (IOException e) {
- Logger.e(TAG, e.getLocalizedMessage());
- }
- }
- }
- /**
- * ???DNS??TCP????
- *
- * @param quest
- * ??DNS??
- * @return
- */
- protected byte[] fetchAnswer(byte[] quest) {
- Socket innerSocket = new InnerSocketBuilder(proxyHost, proxyPort,
- target).getSocket();
- DataInputStream in;
- DataOutputStream out;
- byte[] result = null;
- try {
- if (innerSocket != null && innerSocket.isConnected()) {
- // ??TCP DNS?
- int dnsqLength = quest.length;
- byte[] tcpdnsq = new byte[dnsqLength + 2];
- System.arraycopy(Utils.int2byte(dnsqLength), 0, tcpdnsq, 1, 1);
- System.arraycopy(quest, 0, tcpdnsq, 2, dnsqLength);
- // ??DNS
- in = new DataInputStream(innerSocket.getInputStream());
- out = new DataOutputStream(innerSocket.getOutputStream());
- out.write(tcpdnsq);
- out.flush();
- ByteArrayOutputStream bout = new ByteArrayOutputStream();
- int b = -1;
- while ((b = in.read()) != -1) {
- bout.write(b);
- }
- byte[] tcpdnsr = bout.toByteArray();
- if (tcpdnsr != null && tcpdnsr.length > 2) {
- result = new byte[tcpdnsr.length - 2];
- System.arraycopy(tcpdnsr, 2, result, 0, tcpdnsr.length - 2);
- }
- innerSocket.close();
- }
- } catch (IOException e) {
- Logger.e(TAG, "", e);
- }
- return result;
- }
- /**
- * ?????dns??
- *
- * @param response
- * ???
- * @param dnsq
- * ???
- * @param srvSocket
- * ??Socket
- */
- private void sendDns(byte[] response, DatagramPacket dnsq,
- DatagramSocket srvSocket) {
- // ??identifier
- System.arraycopy(dnsq.getData(), 0, response, 0, 2);
- DatagramPacket resp = new DatagramPacket(response, 0, response.length);
- resp.setPort(dnsq.getPort());
- resp.setAddress(dnsq.getAddress());
- try {
- srvSocket.send(resp);
- } catch (IOException e) {
- Logger.e(TAG, "", e);
- }
- }
- /**
- * ??UDP DNS?????
- *
- * @param request
- * dns udp?
- * @return ?????
- */
- protected String getRequestDomain(byte[] request) {
- String requestDomain = "";
- int reqLength = request.length;
- if (reqLength > 13) { // ????
- byte[] question = new byte[reqLength - 12];
- System.arraycopy(request, 12, question, 0, reqLength - 12);
- requestDomain = parseDomain(question);
- requestDomain = requestDomain.substring(0,
- requestDomain.length() - 1);
- }
- return requestDomain;
- }
- /*
- * Create a DNS response packet, which will send back to application.
- *
- * @author yanghong
- *
- * Reference to:
- *
- * Mini Fake DNS server (Python)
- * http://code.activestate.com/recipes/491264-mini-fake-dns-server/
- *
- * DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
- * http://www.ietf.org/rfc/rfc1035.txt
- */
- protected byte[] createDNSResponse(byte[] quest, String ip) {
- byte[] response = null;
- int start = 0;
- response = new byte[128];
- for (int val : DNS_HEADERS) {
- response[start] = (byte) val;
- start++;
- }
- System.arraycopy(quest, 0, response, 0, 2); /* 0:2 */
- System.arraycopy(quest, 4, response, 4, 2); /* 4:6 -> 4:6 */
- System.arraycopy(quest, 4, response, 6, 2); /* 4:6 -> 7:9 */
- System.arraycopy(quest, DNS_PKG_HEADER_LEN, response, start,
- quest.length - DNS_PKG_HEADER_LEN); /* 12:~ -> 15:~ */
- start += quest.length - DNS_PKG_HEADER_LEN;
- for (int val : DNS_PAYLOAD) {
- response[start] = (byte) val;
- start++;
- }
- /* IP address in response */
- String[] ips = ip.split("\\.");
- Logger.d(TAG, "Start parse ip string: " + ip + ", Sectons: "
- + ips.length);
- for (String section : ips) {
- try {
- response[start] = (byte) Integer.parseInt(section);
- start++;
- } catch (NumberFormatException e) {
- Logger.e(TAG, "Malformed IP string section: " + section);
- }
- }
- byte[] result = new byte[start];
- System.arraycopy(response, 0, result, 0, start);
- Logger.d(TAG, "DNS Response package size: " + start);
- return result;
- }
- /**
- * ????
- *
- * @param request
- * @return
- */
- private String parseDomain(byte[] request) {
- String result = "";
- int length = request.length;
- int partLength = request[0];
- if (partLength == 0)
- return result;
- try {
- byte[] left = new byte[length - partLength - 1];
- System.arraycopy(request, partLength + 1, left, 0, length
- - partLength - 1);
- result = new String(request, 1, partLength) + ".";
- result += parseDomain(left);
- } catch (Exception e) {
- Logger.e(TAG, e.getLocalizedMessage());
- }
- return result;
- }
- /**
- * ???????????
- */
- private void loadCache() {
- ObjectInputStream ois = null;
- File cache = new File(homePath + CACHE_PATH + CACHE_FILE);
- try {
- if (!cache.exists())
- return;
- ois = new ObjectInputStream(new FileInputStream(cache));
- dnsCache = (Hashtable<String, DnsResponse>) ois.readObject();
- ois.close();
- ois = null;
- for (DnsResponse resp : dnsCache.values()) {
- // ??????(??)
- if ((System.currentTimeMillis() - resp.getTimestamp()) > 864000000L) {
- Logger.d(TAG, "??" + resp.getRequest() + "??");
- dnsCache.remove(resp.getRequest());
- }
- }
- } catch (ClassCastException e) {
- Logger.e(TAG, e.getLocalizedMessage(), e);
- } catch (FileNotFoundException e) {
- Logger.e(TAG, e.getLocalizedMessage(), e);
- } catch (IOException e) {
- Logger.e(TAG, e.getLocalizedMessage(), e);
- } catch (ClassNotFoundException e) {
- Logger.e(TAG, e.getLocalizedMessage(), e);
- } catch (ConcurrentModificationException e) {
- Logger.e(TAG, e.getLocalizedMessage(), e);
- } finally {
- try {
- if (ois != null)
- ois.close();
- } catch (IOException e) {
- }
- }
- }
- /**
- * ??????????
- */
- private void saveCache() {
- ObjectOutputStream oos = null;
- File cache = new File(homePath + CACHE_PATH + CACHE_FILE);
- try {
- if (!cache.exists()) {
- File cacheDir = new File(homePath + CACHE_PATH);
- if (!cacheDir.exists()) { // android?createNewFile??????????
- cacheDir.mkdir();
- }
- cache.createNewFile();
- }
- oos = new ObjectOutputStream(new FileOutputStream(cache));
- oos.writeObject(dnsCache);
- oos.flush();
- oos.close();
- oos = null;
- } catch (FileNotFoundException e) {
- Logger.e(TAG, e.getLocalizedMessage(), e);
- } catch (IOException e) {
- Logger.e(TAG, e.getLocalizedMessage(), e);
- } finally {
- try {
- if (oos != null)
- oos.close();
- } catch (IOException e) {
- }
- }
- }
- /**
- * ????????????
- *
- * @param questDomainName
- * ??
- * @param answer
- * ????
- */
- private void addToCache(String questDomainName, byte[] answer) {
- DnsResponse response = new DnsResponse(questDomainName);
- response.setDnsResponse(answer);
- dnsCache.put(questDomainName, response);
- saveCache();
- }
- public void close() throws IOException {
- inService = false;
- srvSocket.close();
- saveCache();
- Logger.i(TAG, "DNS????");
- }
- public int getServPort() {
- return this.srvPort;
- }
- public boolean isClosed() {
- return srvSocket.isClosed();
- }
- public void setTarget(String target) {
- this.target = target;
- }
- public void setProxyHost(String host) {
- this.proxyHost = host;
- }
- public void setProxyPort(int port) {
- this.proxyPort = port;
- }
- public void setBasePath(String path) {
- this.homePath = path;
- }
- public String[] getRules() {
- return iptablesRules;
- }
- private void initOrgCache() {
- // TODO: ?Preference??
- // TODO: ??
- orgCache.put("dn5r3l4y.appspot.com", "74.125.153.141");
- }
- }
- /**
- * ???????Dns??
- *
- * @author biaji
- *
- */
- class DnsResponse implements Serializable {
- private static final long serialVersionUID = -6693216674221293274L;
- private String request;
- private long timestamp = System.currentTimeMillis();;
- private int reqTimes;
- private byte[] dnsResponse;
- public DnsResponse(String request) {
- this.request = request;
- }
- public String getRequest() {
- return this.request;
- }
- /**
- * @return the timestamp
- */
- public long getTimestamp() {
- return timestamp;
- }
- /**
- * @return the reqTimes
- */
- public int getReqTimes() {
- return reqTimes;
- }
- /**
- * @return the dnsResponse
- */
- public byte[] getDnsResponse() {
- this.reqTimes++;
- return dnsResponse;
- }
- /**
- * @param dnsResponse
- * the dnsResponse to set
- */
- public void setDnsResponse(byte[] dnsResponse) {
- this.dnsResponse = dnsResponse;
- }
- }