/*## (c) SECURITY EXPLORATIONS    2018 poland                                #*/
/*##     http://www.security-explorations.com                                #*/

/* THIS SOFTWARE IS PROTECTED BY DOMESTIC AND INTERNATIONAL COPYRIGHT LAWS    */
/* UNAUTHORISED COPYING OF THIS SOFTWARE IN EITHER SOURCE OR BINARY FORM IS   */
/* EXPRESSLY FORBIDDEN. ANY USE, INCLUDING THE REPRODUCTION, MODIFICATION,    */
/* DISTRIBUTION, TRANSMISSION, RE-PUBLICATION, STORAGE OR DISPLAY OF ANY      */
/* PART OF THE SOFTWARE, FOR COMMERCIAL OR ANY OTHER PURPOSES REQUIRES A      */
/* VALID LICENSE FROM THE COPYRIGHT HOLDER.                                   */

/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS    */
/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,*/
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    */
/* SECURITY EXPLORATIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF  */
/* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
/* SOFTWARE.                                                                  */

import java.lang.*;
import java.io.*;
import java.net.*;

public class DNSServer {
 public static class DNSQuery {
  byte buf[];
  short tid;
  short flags;
  short questions;
  short answers;
  short authority;
  short additional;
  String qhost;
  short type;
  short clazz;

  String read_qhost(DataInputStream dis) {
   String qhost="";

   try {
    while(true) {
     int len=dis.readByte();

     if (len==0) break;

     byte buf[]=new byte[len];
     dis.read(buf);
  
     if (qhost.length()>0) qhost+=".";
     qhost+=new String(buf);
    }
   } catch(Throwable t) {}  

   return qhost;
  }

  public DNSQuery(byte buf[]) {
   try {
    this.buf=buf;

    ByteArrayInputStream bais=new ByteArrayInputStream(buf);
    DataInputStream dis=new DataInputStream(bais);

    tid=dis.readShort();
    flags=dis.readShort();
    questions=dis.readShort();
    answers=dis.readShort();
    authority=dis.readShort();
    additional=dis.readShort();

    if (questions==1) {
     qhost=read_qhost(dis);

     type=dis.readShort();
     clazz=dis.readShort();
    }
   } catch(Throwable t) {}  
  }
 }

 public static class DNSResp {
  byte rbuf[];

  void write_qhost(byte qbuf[],DataOutputStream dos) {
   try {
    ByteArrayInputStream bais=new ByteArrayInputStream(qbuf);
    DataInputStream dis=new DataInputStream(bais);

    for(int i=0;i<12;i++) {
     dis.readByte();
    }

    while(true) {     
     int len=dis.readByte();
     dos.writeByte(len);

     if (len==0) break;

     byte buf[]=new byte[len];
     dis.read(buf);
     dos.write(buf);
    }

    dos.writeShort(0x0001);
    dos.writeShort(0x0001);
   } catch(Throwable t) {}
  }

  public DNSResp(DNSQuery query) {
   try {
    ByteArrayOutputStream baos=new ByteArrayOutputStream();
    DataOutputStream dos=new DataOutputStream(baos);

    dos.writeShort(query.tid);
    dos.writeShort(0x8580);
    dos.writeShort(0x0001);
    dos.writeShort(0x0001);
    dos.writeShort(0x0000);
    dos.writeShort(0x0000);

    write_qhost(query.buf,dos);

    //answer
    dos.writeShort(0xc00c);
    dos.writeShort(0x0001);
    dos.writeShort(0x0001);    

    //ttl
    dos.writeShort(0x0000);
    dos.writeShort(0x9600);

    dos.writeShort(host_ip.length);

    for(int i=0;i<host_ip.length;i++) {
     dos.writeByte(host_ip[i]);
    }

    dos.flush();

    rbuf=baos.toByteArray();    
   } catch(Throwable t) {}
  }

  public byte[] getBytes() {
   return rbuf;
  }
 }

 public static class ReqHandler extends Thread {
  InetAddress host;
  int port;
  byte data[];
  int size;

  public ReqHandler(InetAddress host,int port,byte data[],int size) {
   this.host=host;
   this.port=port;
   this.data=data;
   this.size=size;
  }

  public void run() {
   try {
    DNSQuery query=new DNSQuery(data);

    if ((query.questions==1)&&(query.type==1)&&(query.clazz==1)) {
     System.out.println("- DNS query from: "+host+" name "+query.qhost);

     byte ip[]=null;

     if (redirect_host(query.qhost)) {
      ip=host_ip;
     } else

     if (resolved_host(query.qhost)) {
      ip=InetAddress.getByName(query.qhost).getAddress();
     }

     if (ip!=null) {
      System.out.println("- sending response");

      DNSResp resp=new DNSResp(query);
      byte data[]=resp.getBytes();

      DatagramPacket rpkt=new DatagramPacket(data,data.length,host,port);
      srv.send(rpkt);
     } 
    }
   } catch(Throwable t) {
    t.printStackTrace();
   }
  }
 }

 public static class Server extends Thread {
  static final int BUFSIZE  = 1024;

  public void run() {
   try {
    while(true) {
     byte[] buf=new byte[BUFSIZE];

     DatagramPacket pkt=new DatagramPacket(buf,buf.length);
     srv.receive(pkt);

     new ReqHandler(pkt.getAddress(),pkt.getPort(),pkt.getData(),pkt.getLength()).start();
    }
   } catch(Throwable t) {}
  }
 }

 static final int dns_port = 53;

 static String host_addr="";
 static byte[] host_ip;

 static DatagramSocket srv;

 static String redirect_hosts[]={
  "www.onet.pl",
  "portal49.ncplus.pl",
  "go.microsoft.com",
  "dek.ncplus.pl",
  "cs.n.onet.pl",
  "www.adbglobal.com"
 };

 static String resolved_hosts[]={
 };

 static boolean redirect_host(String host) {
  for(int i=0;i<redirect_hosts.length;i++) {
   if (redirect_hosts[i].equals(host)) return true;
  }
  
  return false;
 }

 static boolean resolved_host(String host) {
  for(int i=0;i<resolved_hosts.length;i++) {
   if (resolved_hosts[i].equals(host)) return true;
  }
  
  return false;
 }

 public static void start(String host_addr) {
  try {
   host_ip=InetAddress.getByName(host_addr).getAddress();

   srv=new DatagramSocket(dns_port);

   System.out.println("DNS server listening on: "+dns_port);

   (new Server()).start();   
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}
