/******************************************************************************* * gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/ * Copyright (C) 2014 SVS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.dnsProxy_v0_001; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.security.SecureRandom; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ConcurrentHashMap; import staticContent.framework.controller.Implementation; import staticContent.framework.interfaces.Layer5ApplicationClient; import staticContent.framework.routing.RoutingMode; import staticContent.framework.socket.socketInterfaces.DatagramAnonSocket; import staticContent.framework.socket.socketInterfaces.AnonSocketOptions.CommunicationDirection; import staticContent.framework.util.Util; public class ClientPlugIn extends Implementation implements Layer5ApplicationClient { private DatagramSocket stubResolverSocket; // we will receive data from local client applications (via the stub resolver) via this socket private DatagramAnonSocket mixSocket; private boolean DNSP_DEBUG; private int MAX_MSG_SIZE; private SecureRandom random = new SecureRandom(); private Map<Integer, MsgInfo> idMapping = new ConcurrentHashMap<Integer, MsgInfo>(10000); private ArrayBlockingQueue<byte[]> buffer = new ArrayBlockingQueue<byte[]>(1000); private int INTERNAL_MIX_PORT; @Override public void constructor() { this.DNSP_DEBUG = settings.getPropertyAsBoolean("DNSP_DEBUG"); this.MAX_MSG_SIZE = settings.getPropertyAsInt("DNS_MAX_MSG_SIZE"); this.INTERNAL_MIX_PORT = settings.getPropertyAsInt("INTERNAL_MIX_PORT"); try { this.stubResolverSocket = new DatagramSocket(settings.getPropertyAsInt("DNS_LISTENING_PORT")); } catch (IOException e) { System.err.println("Port busy! Exiting."); if (DNSP_DEBUG) e.printStackTrace(); } System.out.println("listening on " +stubResolverSocket.getLocalAddress() + ":" +stubResolverSocket.getLocalPort()); System.err.println("Warning: This is a test plug-in - do NOT send any sensitive data via this plug-in!"); } @Override public void initialize() { } @Override public void begin() { this.mixSocket = anonNode.createDatagramSocket( CommunicationDirection.DUPLEX, false, false, super.anonNode.ROUTING_MODE != RoutingMode.GLOBAL_ROUTING ); //this.mixSocket.connect(settings.getPropertyAsInt("INTERNAL_MIX_PORT")); new ClientListenerThread().start(); new RequestThread().start(); new ReplyThread().start(); } public class ClientListenerThread extends Thread { @Override public void run() { byte[] rcvbytes = new byte[MAX_MSG_SIZE]; while (true) { DatagramPacket rcvPkt = new DatagramPacket(rcvbytes, rcvbytes.length); try { stubResolverSocket.receive(rcvPkt); } catch (IOException e) { System.err.println("Error receiving message from stub resolver"); if(DNSP_DEBUG) e.printStackTrace(); break; } if(DNSP_DEBUG) System.out.println("client received request from stub"); byte[] payload = new byte[rcvPkt.getLength()]; System.arraycopy(rcvPkt.getData(), rcvPkt.getOffset(), payload, 0, rcvPkt.getLength()); //Message dnsQuery = Message.newQuery(Record.fromWire(payload, Section.QUESTION)); // TODO Section.QUESTION correct? //.newRecord(Name.fromString(requestedURL+"."), Type.value(dnsQueryType), DClass.IN)); byte[] msgID = generateMessageID(); int messageID = Util.byteArrayToInt(msgID); if(DNSP_DEBUG) System.out.println(" adding msgid " + messageID +" (" +Util.toHex(payload) +")"); byte[] query = Util.concatArrays(msgID, payload); if (query.length > MAX_MSG_SIZE) { System.err.println("warning: received too big request; dropping it"); continue; } idMapping.put(messageID, new MsgInfo(rcvPkt.getAddress(), rcvPkt.getPort())); buffer.add(query); } } } public class RequestThread extends Thread { @Override public void run() { while (true) { try { Payload payload = new Payload(MAX_MSG_SIZE); payload.addMessage(buffer.take()); while (buffer.peek() != null && buffer.peek().length <= payload.remaining()) payload.addMessage(buffer.take()); if(DNSP_DEBUG) System.out.println("client send mix message (" +Util.toHex(payload.getBytePayload()) +")"); mixSocket.sendMessage(INTERNAL_MIX_PORT, payload.getBytePayload()); } catch (InterruptedException e) { continue; } } } } public class ReplyThread extends Thread { @Override public void run() { while (true) { byte[] data = mixSocket.receiveMessage().getByteMessage(); if(DNSP_DEBUG) System.out.println("client received mix message (" +Util.toHex(data) +")"); Payload payload = new Payload(data, data.length); List<byte[]> dnsReplies = DNSUtils.splitArrayOnPattern(payload.getMessage(), DNSUtils.dnsTerminator, 0); for (byte[] reply: dnsReplies) { int msgID = Util.byteArrayToInt(Arrays.copyOfRange(reply, 0, 4)); byte[] dnsMsg = Arrays.copyOfRange(reply, 4, reply.length); MsgInfo msgInfo; msgInfo = idMapping.remove(msgID); if (msgInfo == null) { System.err.println("warning: received reply with unknoen id"); continue; } // send to client application DatagramPacket sendPacket = new DatagramPacket(dnsMsg, 0, dnsMsg.length, msgInfo.adr, msgInfo.port); try { stubResolverSocket.send(sendPacket); } catch (IOException e) { System.err.println("Error when sending response to stub resolver"); } if(DNSP_DEBUG) System.out.println("client sent reply"); } } } } private byte[] generateMessageID(){ return Util.intToByteArray(Math.abs(random.nextInt())); } private class MsgInfo { public InetAddress adr; public int port; public MsgInfo(InetAddress adr, int port) { this.adr = adr; this.port = port; } } }