1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import sun.security.util.HexDumpEncoder;
  25 
  26 import java.io.PrintStream;
  27 import java.net.DatagramPacket;
  28 import java.net.DatagramSocket;
  29 import java.net.InetSocketAddress;
  30 import java.net.SocketAddress;
  31 import java.net.SocketException;
  32 import java.nio.ByteBuffer;
  33 import java.util.Arrays;
  34 
  35 /*
  36  * A DNS UDP message tracer.
  37  *
  38  * It listens for DNS UDP requests, forward request to real DNS server, receives
  39  * response message and sends back to requester, at same time dump all messages
  40  * into capture file
  41  *
  42  * The capture file contains an DNS protocol exchange in the hexadecimal
  43  * dump format emitted by HexDumpEncoder:
  44  *
  45  * xxxx: 00 11 22 33 44 55 66 77   88 99 aa bb cc dd ee ff  ................
  46  *
  47  * Typically, the capture file data will be used by DNSServer for playback
  48  */
  49 public class DNSTracer implements Runnable {
  50     public static final int DNS_DEFAULT_PORT = 53;
  51     public static final int DNS_PACKET_SIZE = 512;
  52     static HexDumpEncoder encoder = new HexDumpEncoder();
  53 
  54     private DatagramSocket inSocket;
  55     private SocketAddress dnsServerAddress;
  56     private ByteBuffer reqBuffer = ByteBuffer.allocate(DNS_PACKET_SIZE);
  57     private ByteBuffer resBuffer = ByteBuffer.allocate(DNS_PACKET_SIZE);
  58     private PrintStream out = null;
  59 
  60     public DNSTracer(DatagramSocket socket, String dnsHostname) {
  61         this(socket, dnsHostname, DNS_DEFAULT_PORT);
  62     }
  63 
  64     public DNSTracer(DatagramSocket socket, PrintStream outStream,
  65             String dnsHostname) {
  66         this(socket, outStream, dnsHostname, DNS_DEFAULT_PORT);
  67     }
  68 
  69     public DNSTracer(DatagramSocket socket, String dnsHostname, int dnsPort) {
  70         this(socket, System.out, dnsHostname, dnsPort);
  71     }
  72 
  73     public DNSTracer(DatagramSocket socket, PrintStream outStream,
  74             String dnsHostname, int dnsPort) {
  75         inSocket = socket;
  76         out = outStream;
  77         dnsServerAddress = new InetSocketAddress(dnsHostname, dnsPort);
  78     }
  79 
  80     public void run() {
  81         System.out.println(
  82                 "DNSTracer: listening on port " + inSocket.getLocalPort());
  83 
  84         System.out.println("DNSTracer: will forward request to server "
  85                 + dnsServerAddress);
  86 
  87         try (DatagramSocket outSocket = new DatagramSocket()) {
  88             while (true) {
  89                 DatagramPacket reqPacket = new DatagramPacket(reqBuffer.array(),
  90                         reqBuffer.array().length);
  91                 inSocket.receive(reqPacket);
  92 
  93                 out.println("-> " + reqPacket.getSocketAddress());
  94                 out.println();
  95                 // dump dns request data
  96                 out.println(encoder.encodeBuffer(
  97                         Arrays.copyOf(reqPacket.getData(),
  98                                 reqPacket.getLength())));
  99                 out.println();
 100 
 101                 outSocket.send(new DatagramPacket(reqPacket.getData(),
 102                         reqPacket.getLength(), dnsServerAddress));
 103                 DatagramPacket resPacket = new DatagramPacket(resBuffer.array(),
 104                         resBuffer.array().length);
 105                 outSocket.receive(resPacket);
 106 
 107                 out.println("<- " + resPacket.getSocketAddress());
 108                 out.println();
 109                 // dump dns response data
 110                 out.println(encoder.encodeBuffer(
 111                         Arrays.copyOf(resPacket.getData(),
 112                                 resPacket.getLength())));
 113                 out.println();
 114 
 115                 inSocket.send(new DatagramPacket(resPacket.getData(),
 116                         resPacket.getLength(), reqPacket.getSocketAddress()));
 117             }
 118         } catch (SocketException se) {
 119             if (inSocket.isClosed()) {
 120                 out.flush();
 121                 System.out.println("DNSTracer: Exit");
 122             } else {
 123                 se.printStackTrace();
 124             }
 125         } catch (Exception e) {
 126             e.printStackTrace();
 127         }
 128     }
 129 
 130 }