PageRenderTime 24ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/cmwrap-1.5.3beta1/src/net/biaji/android/cmwrap/services/DNSServer.java

http://cmwrap.googlecode.com/
Java | 518 lines | 345 code | 78 blank | 95 comment | 36 complexity | de4c10483de25681f3c0d2bc06d9084c MD5 | raw file
  1. package net.biaji.android.cmwrap.services;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.DataInputStream;
  4. import java.io.DataOutputStream;
  5. import java.io.File;
  6. import java.io.FileInputStream;
  7. import java.io.FileNotFoundException;
  8. import java.io.FileOutputStream;
  9. import java.io.IOException;
  10. import java.io.ObjectInputStream;
  11. import java.io.ObjectOutputStream;
  12. import java.io.Serializable;
  13. import java.net.DatagramPacket;
  14. import java.net.DatagramSocket;
  15. import java.net.InetAddress;
  16. import java.net.Socket;
  17. import java.net.SocketException;
  18. import java.net.UnknownHostException;
  19. import java.util.ConcurrentModificationException;
  20. import java.util.Hashtable;
  21. import net.biaji.android.cmwrap.Logger;
  22. import net.biaji.android.cmwrap.utils.Utils;
  23. /**
  24. * ?????DNS??
  25. *
  26. * @author biaji
  27. *
  28. */
  29. public class DNSServer implements WrapServer {
  30. private final String TAG = "CMWRAP->DNSServer";
  31. private String homePath;
  32. private final String CACHE_PATH = "/cache";
  33. private final String CACHE_FILE = "/dnscache";
  34. private DatagramSocket srvSocket;
  35. private int srvPort = 7442;
  36. private String name;
  37. protected String proxyHost, dnsHost;
  38. protected int proxyPort, dnsPort;
  39. public final int DNS_PKG_HEADER_LEN = 12;
  40. final private int[] DNS_HEADERS = { 0, 0, 0x81, 0x80, 0, 0, 0, 0, 0, 0, 0,
  41. 0 };
  42. final private int[] DNS_PAYLOAD = { 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01,
  43. 0x00, 0x00, 0x00, 0x3c, 0x00, 0x04 };
  44. private boolean inService = false;
  45. private Hashtable<String, DnsResponse> dnsCache = new Hashtable<String, DnsResponse>();
  46. /**
  47. * ???????
  48. *
  49. */
  50. private Hashtable<String, String> orgCache = new Hashtable<String, String>();
  51. private String target = "8.8.4.4:53";
  52. 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" };
  53. public DNSServer(String name, int port, String proxyHost, int proxyPort,
  54. String dnsHost, int dnsPort) {
  55. this.name = name;
  56. this.srvPort = port;
  57. this.proxyHost = proxyHost;
  58. this.proxyPort = proxyPort;
  59. this.dnsHost = dnsHost;
  60. this.dnsPort = dnsPort;
  61. if (dnsHost != null && !dnsHost.equals(""))
  62. target = dnsHost + ":" + dnsPort;
  63. Utils.flushDns(dnsHost);
  64. initOrgCache();
  65. try {
  66. srvSocket = new DatagramSocket(srvPort, InetAddress
  67. .getByName("127.0.0.1"));
  68. inService = true;
  69. Logger.d(TAG, this.name + "?????? " + port);
  70. } catch (SocketException e) {
  71. Logger.e(TAG, "DNSServer?????????" + port, e);
  72. } catch (UnknownHostException e) {
  73. Logger.e(TAG, "DNSServer?????????" + port, e);
  74. }
  75. }
  76. public void run() {
  77. loadCache();
  78. byte[] qbuffer = new byte[576];
  79. long starTime = System.currentTimeMillis();
  80. while (true) {
  81. try {
  82. DatagramPacket dnsq = new DatagramPacket(qbuffer,
  83. qbuffer.length);
  84. srvSocket.receive(dnsq);
  85. // ????DNS?????
  86. byte[] data = dnsq.getData();
  87. int dnsqLength = dnsq.getLength();
  88. byte[] udpreq = new byte[dnsqLength];
  89. System.arraycopy(data, 0, udpreq, 0, dnsqLength);
  90. // ???????????
  91. String questDomain = getRequestDomain(udpreq);
  92. Logger.d(TAG, "??" + questDomain);
  93. if (dnsCache.containsKey(questDomain)) {
  94. sendDns(dnsCache.get(questDomain).getDnsResponse(), dnsq,
  95. srvSocket);
  96. Logger.d(TAG, "????");
  97. } else if (orgCache.containsKey(questDomain)) { // ??????????
  98. byte[] answer = createDNSResponse(udpreq, orgCache
  99. .get(questDomain));
  100. addToCache(questDomain, answer);
  101. sendDns(answer, dnsq, srvSocket);
  102. Logger.d(TAG, "?????" + orgCache);
  103. } else {
  104. starTime = System.currentTimeMillis();
  105. byte[] answer = fetchAnswer(udpreq);
  106. if (answer != null && answer.length != 0) {
  107. addToCache(questDomain, answer);
  108. sendDns(answer, dnsq, srvSocket);
  109. Logger.d(TAG, "????DNS??????"
  110. + answer.length + " ???"
  111. + (System.currentTimeMillis() - starTime)
  112. / 1000 + "s");
  113. } else {
  114. Logger.e(TAG, "??DNS???0");
  115. }
  116. }
  117. } catch (SocketException e) {
  118. Logger.e(TAG, e.getLocalizedMessage());
  119. break;
  120. } catch (IOException e) {
  121. Logger.e(TAG, e.getLocalizedMessage());
  122. }
  123. }
  124. }
  125. /**
  126. * ???DNS??TCP????
  127. *
  128. * @param quest
  129. * ??DNS??
  130. * @return
  131. */
  132. protected byte[] fetchAnswer(byte[] quest) {
  133. Socket innerSocket = new InnerSocketBuilder(proxyHost, proxyPort,
  134. target).getSocket();
  135. DataInputStream in;
  136. DataOutputStream out;
  137. byte[] result = null;
  138. try {
  139. if (innerSocket != null && innerSocket.isConnected()) {
  140. // ??TCP DNS?
  141. int dnsqLength = quest.length;
  142. byte[] tcpdnsq = new byte[dnsqLength + 2];
  143. System.arraycopy(Utils.int2byte(dnsqLength), 0, tcpdnsq, 1, 1);
  144. System.arraycopy(quest, 0, tcpdnsq, 2, dnsqLength);
  145. // ??DNS
  146. in = new DataInputStream(innerSocket.getInputStream());
  147. out = new DataOutputStream(innerSocket.getOutputStream());
  148. out.write(tcpdnsq);
  149. out.flush();
  150. ByteArrayOutputStream bout = new ByteArrayOutputStream();
  151. int b = -1;
  152. while ((b = in.read()) != -1) {
  153. bout.write(b);
  154. }
  155. byte[] tcpdnsr = bout.toByteArray();
  156. if (tcpdnsr != null && tcpdnsr.length > 2) {
  157. result = new byte[tcpdnsr.length - 2];
  158. System.arraycopy(tcpdnsr, 2, result, 0, tcpdnsr.length - 2);
  159. }
  160. innerSocket.close();
  161. }
  162. } catch (IOException e) {
  163. Logger.e(TAG, "", e);
  164. }
  165. return result;
  166. }
  167. /**
  168. * ?????dns??
  169. *
  170. * @param response
  171. * ???
  172. * @param dnsq
  173. * ???
  174. * @param srvSocket
  175. * ??Socket
  176. */
  177. private void sendDns(byte[] response, DatagramPacket dnsq,
  178. DatagramSocket srvSocket) {
  179. // ??identifier
  180. System.arraycopy(dnsq.getData(), 0, response, 0, 2);
  181. DatagramPacket resp = new DatagramPacket(response, 0, response.length);
  182. resp.setPort(dnsq.getPort());
  183. resp.setAddress(dnsq.getAddress());
  184. try {
  185. srvSocket.send(resp);
  186. } catch (IOException e) {
  187. Logger.e(TAG, "", e);
  188. }
  189. }
  190. /**
  191. * ??UDP DNS?????
  192. *
  193. * @param request
  194. * dns udp?
  195. * @return ?????
  196. */
  197. protected String getRequestDomain(byte[] request) {
  198. String requestDomain = "";
  199. int reqLength = request.length;
  200. if (reqLength > 13) { // ????
  201. byte[] question = new byte[reqLength - 12];
  202. System.arraycopy(request, 12, question, 0, reqLength - 12);
  203. requestDomain = parseDomain(question);
  204. requestDomain = requestDomain.substring(0,
  205. requestDomain.length() - 1);
  206. }
  207. return requestDomain;
  208. }
  209. /*
  210. * Create a DNS response packet, which will send back to application.
  211. *
  212. * @author yanghong
  213. *
  214. * Reference to:
  215. *
  216. * Mini Fake DNS server (Python)
  217. * http://code.activestate.com/recipes/491264-mini-fake-dns-server/
  218. *
  219. * DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
  220. * http://www.ietf.org/rfc/rfc1035.txt
  221. */
  222. protected byte[] createDNSResponse(byte[] quest, String ip) {
  223. byte[] response = null;
  224. int start = 0;
  225. response = new byte[128];
  226. for (int val : DNS_HEADERS) {
  227. response[start] = (byte) val;
  228. start++;
  229. }
  230. System.arraycopy(quest, 0, response, 0, 2); /* 0:2 */
  231. System.arraycopy(quest, 4, response, 4, 2); /* 4:6 -> 4:6 */
  232. System.arraycopy(quest, 4, response, 6, 2); /* 4:6 -> 7:9 */
  233. System.arraycopy(quest, DNS_PKG_HEADER_LEN, response, start,
  234. quest.length - DNS_PKG_HEADER_LEN); /* 12:~ -> 15:~ */
  235. start += quest.length - DNS_PKG_HEADER_LEN;
  236. for (int val : DNS_PAYLOAD) {
  237. response[start] = (byte) val;
  238. start++;
  239. }
  240. /* IP address in response */
  241. String[] ips = ip.split("\\.");
  242. Logger.d(TAG, "Start parse ip string: " + ip + ", Sectons: "
  243. + ips.length);
  244. for (String section : ips) {
  245. try {
  246. response[start] = (byte) Integer.parseInt(section);
  247. start++;
  248. } catch (NumberFormatException e) {
  249. Logger.e(TAG, "Malformed IP string section: " + section);
  250. }
  251. }
  252. byte[] result = new byte[start];
  253. System.arraycopy(response, 0, result, 0, start);
  254. Logger.d(TAG, "DNS Response package size: " + start);
  255. return result;
  256. }
  257. /**
  258. * ????
  259. *
  260. * @param request
  261. * @return
  262. */
  263. private String parseDomain(byte[] request) {
  264. String result = "";
  265. int length = request.length;
  266. int partLength = request[0];
  267. if (partLength == 0)
  268. return result;
  269. try {
  270. byte[] left = new byte[length - partLength - 1];
  271. System.arraycopy(request, partLength + 1, left, 0, length
  272. - partLength - 1);
  273. result = new String(request, 1, partLength) + ".";
  274. result += parseDomain(left);
  275. } catch (Exception e) {
  276. Logger.e(TAG, e.getLocalizedMessage());
  277. }
  278. return result;
  279. }
  280. /**
  281. * ???????????
  282. */
  283. private void loadCache() {
  284. ObjectInputStream ois = null;
  285. File cache = new File(homePath + CACHE_PATH + CACHE_FILE);
  286. try {
  287. if (!cache.exists())
  288. return;
  289. ois = new ObjectInputStream(new FileInputStream(cache));
  290. dnsCache = (Hashtable<String, DnsResponse>) ois.readObject();
  291. ois.close();
  292. ois = null;
  293. for (DnsResponse resp : dnsCache.values()) {
  294. // ??????(??)
  295. if ((System.currentTimeMillis() - resp.getTimestamp()) > 864000000L) {
  296. Logger.d(TAG, "??" + resp.getRequest() + "??");
  297. dnsCache.remove(resp.getRequest());
  298. }
  299. }
  300. } catch (ClassCastException e) {
  301. Logger.e(TAG, e.getLocalizedMessage(), e);
  302. } catch (FileNotFoundException e) {
  303. Logger.e(TAG, e.getLocalizedMessage(), e);
  304. } catch (IOException e) {
  305. Logger.e(TAG, e.getLocalizedMessage(), e);
  306. } catch (ClassNotFoundException e) {
  307. Logger.e(TAG, e.getLocalizedMessage(), e);
  308. } catch (ConcurrentModificationException e) {
  309. Logger.e(TAG, e.getLocalizedMessage(), e);
  310. } finally {
  311. try {
  312. if (ois != null)
  313. ois.close();
  314. } catch (IOException e) {
  315. }
  316. }
  317. }
  318. /**
  319. * ??????????
  320. */
  321. private void saveCache() {
  322. ObjectOutputStream oos = null;
  323. File cache = new File(homePath + CACHE_PATH + CACHE_FILE);
  324. try {
  325. if (!cache.exists()) {
  326. File cacheDir = new File(homePath + CACHE_PATH);
  327. if (!cacheDir.exists()) { // android?createNewFile??????????
  328. cacheDir.mkdir();
  329. }
  330. cache.createNewFile();
  331. }
  332. oos = new ObjectOutputStream(new FileOutputStream(cache));
  333. oos.writeObject(dnsCache);
  334. oos.flush();
  335. oos.close();
  336. oos = null;
  337. } catch (FileNotFoundException e) {
  338. Logger.e(TAG, e.getLocalizedMessage(), e);
  339. } catch (IOException e) {
  340. Logger.e(TAG, e.getLocalizedMessage(), e);
  341. } finally {
  342. try {
  343. if (oos != null)
  344. oos.close();
  345. } catch (IOException e) {
  346. }
  347. }
  348. }
  349. /**
  350. * ????????????
  351. *
  352. * @param questDomainName
  353. * ??
  354. * @param answer
  355. * ????
  356. */
  357. private void addToCache(String questDomainName, byte[] answer) {
  358. DnsResponse response = new DnsResponse(questDomainName);
  359. response.setDnsResponse(answer);
  360. dnsCache.put(questDomainName, response);
  361. saveCache();
  362. }
  363. public void close() throws IOException {
  364. inService = false;
  365. srvSocket.close();
  366. saveCache();
  367. Logger.i(TAG, "DNS????");
  368. }
  369. public int getServPort() {
  370. return this.srvPort;
  371. }
  372. public boolean isClosed() {
  373. return srvSocket.isClosed();
  374. }
  375. public void setTarget(String target) {
  376. this.target = target;
  377. }
  378. public void setProxyHost(String host) {
  379. this.proxyHost = host;
  380. }
  381. public void setProxyPort(int port) {
  382. this.proxyPort = port;
  383. }
  384. public void setBasePath(String path) {
  385. this.homePath = path;
  386. }
  387. public String[] getRules() {
  388. return iptablesRules;
  389. }
  390. private void initOrgCache() {
  391. // TODO: ?Preference??
  392. // TODO: ??
  393. orgCache.put("dn5r3l4y.appspot.com", "74.125.153.141");
  394. }
  395. }
  396. /**
  397. * ???????Dns??
  398. *
  399. * @author biaji
  400. *
  401. */
  402. class DnsResponse implements Serializable {
  403. private static final long serialVersionUID = -6693216674221293274L;
  404. private String request;
  405. private long timestamp = System.currentTimeMillis();;
  406. private int reqTimes;
  407. private byte[] dnsResponse;
  408. public DnsResponse(String request) {
  409. this.request = request;
  410. }
  411. public String getRequest() {
  412. return this.request;
  413. }
  414. /**
  415. * @return the timestamp
  416. */
  417. public long getTimestamp() {
  418. return timestamp;
  419. }
  420. /**
  421. * @return the reqTimes
  422. */
  423. public int getReqTimes() {
  424. return reqTimes;
  425. }
  426. /**
  427. * @return the dnsResponse
  428. */
  429. public byte[] getDnsResponse() {
  430. this.reqTimes++;
  431. return dnsResponse;
  432. }
  433. /**
  434. * @param dnsResponse
  435. * the dnsResponse to set
  436. */
  437. public void setDnsResponse(byte[] dnsResponse) {
  438. this.dnsResponse = dnsResponse;
  439. }
  440. }