< prev index next >

test/jdk/com/sun/jndi/dns/lib/DNSServer.java

Print this page




   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.IOException;
  27 import java.net.DatagramPacket;
  28 import java.net.DatagramSocket;


  29 import java.nio.ByteBuffer;
  30 import java.nio.file.Paths;
  31 import java.util.ArrayList;
  32 import java.util.Arrays;
  33 import java.util.List;
  34 import java.util.Scanner;
  35 import java.util.regex.MatchResult;
  36 
  37 /*
  38  * A dummy DNS server.
  39  *
  40  * Loads a sequence of DNS messages from a capture file into its cache.
  41  * It listens for DNS UDP requests, finds match request in cache and sends the
  42  * corresponding DNS responses.
  43  *
  44  * The capture file contains an DNS protocol exchange in the hexadecimal
  45  * dump format emitted by HexDumpEncoder:
  46  *
  47  * xxxx: 00 11 22 33 44 55 66 77   88 99 aa bb cc dd ee ff  ................
  48  *
  49  * Typically, DNS protocol exchange is generated by DNSTracer who captures
  50  * communication messages between DNS application program and real DNS server
  51  */
  52 public class DNSServer implements Runnable {
  53 
  54     public class Pair<F, S> {
  55         private F first;
  56         private S second;
  57 
  58         public Pair(F first, S second) {
  59             this.first = first;
  60             this.second = second;
  61         }
  62 
  63         public void setFirst(F first) {
  64             this.first = first;
  65         }
  66 
  67         public void setSecond(S second) {
  68             this.second = second;
  69         }
  70 
  71         public F getFirst() {
  72             return first;
  73         }
  74 
  75         public S getSecond() {
  76             return second;
  77         }
  78     }
  79 
  80     public static final int DNS_HEADER_SIZE = 12;
  81     public static final int DNS_PACKET_SIZE = 512;
  82 
  83     static HexDumpEncoder encoder = new HexDumpEncoder();
  84 
  85     private DatagramSocket socket;
  86     private String filename;
  87     private boolean loop;
  88     private final List<Pair<byte[], byte[]>> cache = new ArrayList<>();
  89     private ByteBuffer reqBuffer = ByteBuffer.allocate(DNS_PACKET_SIZE);

  90 
  91     public DNSServer(DatagramSocket socket, String filename) {
  92         this(socket, filename, false);
  93     }
  94 
  95     public DNSServer(DatagramSocket socket, String filename, boolean loop) {
  96         this.socket = socket;
  97         this.filename = filename;
  98         this.loop = loop;
  99     }
 100 
 101     public void run() {
 102         try {

 103             System.out.println(
 104                     "DNSServer: Loading DNS cache data from : " + filename);
 105             loadCaptureFile(filename);
 106 
 107             System.out.println(
 108                     "DNSServer: listening on port " + socket.getLocalPort());
 109 
 110             System.out.println("DNSServer: loop playback: " + loop);
 111 
 112             int playbackIndex = 0;
 113 
 114             while (playbackIndex < cache.size()) {
 115                 DatagramPacket reqPacket = new DatagramPacket(reqBuffer.array(),
 116                         reqBuffer.array().length);
 117                 socket.receive(reqPacket);
 118 
 119                 System.out.println(
 120                         "DNSServer: received query message from " + reqPacket
 121                                 .getSocketAddress());
 122 
 123                 if (!verifyRequestMsg(reqPacket, playbackIndex)) {






 124                     throw new RuntimeException(
 125                             "DNSServer: Error: Failed to verify DNS request. "
 126                                     + "Not identical request message : \n"
 127                                     + encoder.encodeBuffer(
 128                                     Arrays.copyOf(reqPacket.getData(),
 129                                             reqPacket.getLength())));
 130                 }

 131 
 132                 byte[] payload = generateResponsePayload(reqPacket,
 133                         playbackIndex);
 134                 socket.send(new DatagramPacket(payload, payload.length,
 135                         reqPacket.getSocketAddress()));
 136                 System.out.println(
 137                         "DNSServer: send response message to " + reqPacket
 138                                 .getSocketAddress());
 139 
 140                 playbackIndex++;
 141                 if (loop && playbackIndex >= cache.size()) {
 142                     playbackIndex = 0;
 143                 }
 144             }
 145 
 146             System.out.println(
 147                     "DNSServer: Done for all cached messages playback");


















 148         } catch (Exception e) {

 149             System.err.println("DNSServer: Error: " + e);



 150         }
 151     }





















 152 
 153     /*
 154      * Load a capture file containing an DNS protocol exchange in the
 155      * hexadecimal dump format emitted by sun.misc.HexDumpEncoder:
 156      *
 157      * xxxx: 00 11 22 33 44 55 66 77   88 99 aa bb cc dd ee ff  ................
 158      */
 159     private void loadCaptureFile(String filename) throws IOException {
 160         StringBuilder hexString = new StringBuilder();
 161         String pattern = "(....): (..) (..) (..) (..) (..) (..) (..) (..)   "
 162                 + "(..) (..) (..) (..) (..) (..) (..) (..).*";
 163 
 164         try (Scanner fileScanner = new Scanner(Paths.get(filename))) {
 165             while (fileScanner.hasNextLine()) {
 166 
 167                 try (Scanner lineScanner = new Scanner(
 168                         fileScanner.nextLine())) {
 169                     if (lineScanner.findInLine(pattern) == null) {
 170                         continue;
 171                     }


 282             }
 283 
 284             out[i / 2] = (byte) (h * 16 + l);
 285         }
 286 
 287         return out;
 288     }
 289 
 290     private static int hexToBin(char ch) {
 291         if ('0' <= ch && ch <= '9') {
 292             return ch - '0';
 293         }
 294         if ('A' <= ch && ch <= 'F') {
 295             return ch - 'A' + 10;
 296         }
 297         if ('a' <= ch && ch <= 'f') {
 298             return ch - 'a' + 10;
 299         }
 300         return -1;
 301     }



















 302 }


   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.IOException;
  27 import java.net.DatagramPacket;
  28 import java.net.DatagramSocket;
  29 import java.net.InetAddress;
  30 import java.net.SocketException;
  31 import java.nio.ByteBuffer;
  32 import java.nio.file.Paths;
  33 import java.util.ArrayList;
  34 import java.util.Arrays;
  35 import java.util.List;
  36 import java.util.Scanner;
  37 import java.util.regex.MatchResult;
  38 
  39 /*
  40  * A dummy DNS server.
  41  *
  42  * Loads a sequence of DNS messages from a capture file into its cache.
  43  * It listens for DNS UDP requests, finds match request in cache and sends the
  44  * corresponding DNS responses.
  45  *
  46  * The capture file contains an DNS protocol exchange in the hexadecimal
  47  * dump format emitted by HexDumpEncoder:
  48  *
  49  * xxxx: 00 11 22 33 44 55 66 77   88 99 aa bb cc dd ee ff  ................
  50  *
  51  * Typically, DNS protocol exchange is generated by DNSTracer who captures
  52  * communication messages between DNS application program and real DNS server
  53  */
  54 public class DNSServer extends Thread implements Server {
  55 
  56     public class Pair<F, S> {
  57         private F first;
  58         private S second;
  59 
  60         public Pair(F first, S second) {
  61             this.first = first;
  62             this.second = second;
  63         }
  64 
  65         public void setFirst(F first) {
  66             this.first = first;
  67         }
  68 
  69         public void setSecond(S second) {
  70             this.second = second;
  71         }
  72 
  73         public F getFirst() {
  74             return first;
  75         }
  76 
  77         public S getSecond() {
  78             return second;
  79         }
  80     }
  81 
  82     public static final int DNS_HEADER_SIZE = 12;
  83     public static final int DNS_PACKET_SIZE = 512;
  84 
  85     static HexDumpEncoder encoder = new HexDumpEncoder();
  86 
  87     private DatagramSocket socket;
  88     private String filename;
  89     private boolean loop;
  90     private final List<Pair<byte[], byte[]>> cache = new ArrayList<>();
  91     private ByteBuffer reqBuffer = ByteBuffer.allocate(DNS_PACKET_SIZE);
  92     private volatile boolean isRunning;
  93 
  94     public DNSServer(String filename) throws SocketException {
  95         this(filename, false);
  96     }
  97 
  98     public DNSServer(String filename, boolean loop) throws SocketException {
  99         this.socket = new DatagramSocket(0, InetAddress.getLoopbackAddress());
 100         this.filename = filename;
 101         this.loop = loop;
 102     }
 103 
 104     public void run() {
 105         try {
 106             isRunning = true;
 107             System.out.println(
 108                     "DNSServer: Loading DNS cache data from : " + filename);
 109             loadCaptureFile(filename);
 110 
 111             System.out.println(
 112                     "DNSServer: listening on port " + socket.getLocalPort());
 113 
 114             System.out.println("DNSServer: loop playback: " + loop);
 115 
 116             int playbackIndex = 0;
 117 
 118             while (playbackIndex < cache.size()) {
 119                 DatagramPacket reqPacket = receiveQuery();






 120 
 121                 if (!verifyRequestMsg(reqPacket, playbackIndex)) {
 122                     if (playbackIndex > 0 && verifyRequestMsg(reqPacket,
 123                             playbackIndex - 1)) {
 124                         System.out.println(
 125                                 "DNSServer: received retry query, resend");
 126                         playbackIndex--;
 127                     } else {
 128                         throw new RuntimeException(
 129                                 "DNSServer: Error: Failed to verify DNS request. "
 130                                         + "Not identical request message : \n"
 131                                         + encoder.encodeBuffer(
 132                                         Arrays.copyOf(reqPacket.getData(),
 133                                                 reqPacket.getLength())));
 134                     }
 135                 }
 136 
 137                 sendResponse(reqPacket, playbackIndex);






 138 
 139                 playbackIndex++;
 140                 if (loop && playbackIndex >= cache.size()) {
 141                     playbackIndex = 0;
 142                 }
 143             }
 144 
 145             System.out.println(
 146                     "DNSServer: Done for all cached messages playback");
 147 
 148             System.out.println(
 149                     "DNSServer: Still listening for possible retry query");
 150             while (true) {
 151                 DatagramPacket reqPacket = receiveQuery();
 152 
 153                 // here we only handle the retry query for last one
 154                 if (!verifyRequestMsg(reqPacket, playbackIndex - 1)) {
 155                     throw new RuntimeException(
 156                             "DNSServer: Error: Failed to verify DNS request. "
 157                                     + "Not identical request message : \n"
 158                                     + encoder.encodeBuffer(
 159                                     Arrays.copyOf(reqPacket.getData(),
 160                                             reqPacket.getLength())));
 161                 }
 162 
 163                 sendResponse(reqPacket, playbackIndex - 1);
 164             }
 165         } catch (Exception e) {
 166             if (isRunning) {
 167                 System.err.println("DNSServer: Error: " + e);
 168                 e.printStackTrace();
 169             } else {
 170                 System.out.println("DNSServer: Exit");
 171             }
 172         }
 173     }
 174 
 175     private DatagramPacket receiveQuery() throws IOException {
 176         DatagramPacket reqPacket = new DatagramPacket(reqBuffer.array(),
 177                 reqBuffer.array().length);
 178         socket.receive(reqPacket);
 179 
 180         System.out.println("DNSServer: received query message from " + reqPacket
 181                 .getSocketAddress());
 182 
 183         return reqPacket;
 184     }
 185 
 186     private void sendResponse(DatagramPacket reqPacket, int playbackIndex)
 187             throws IOException {
 188         byte[] payload = generateResponsePayload(reqPacket, playbackIndex);
 189         socket.send(new DatagramPacket(payload, payload.length,
 190                 reqPacket.getSocketAddress()));
 191         System.out.println("DNSServer: send response message to " + reqPacket
 192                 .getSocketAddress());
 193     }
 194 
 195     /*
 196      * Load a capture file containing an DNS protocol exchange in the
 197      * hexadecimal dump format emitted by sun.misc.HexDumpEncoder:
 198      *
 199      * xxxx: 00 11 22 33 44 55 66 77   88 99 aa bb cc dd ee ff  ................
 200      */
 201     private void loadCaptureFile(String filename) throws IOException {
 202         StringBuilder hexString = new StringBuilder();
 203         String pattern = "(....): (..) (..) (..) (..) (..) (..) (..) (..)   "
 204                 + "(..) (..) (..) (..) (..) (..) (..) (..).*";
 205 
 206         try (Scanner fileScanner = new Scanner(Paths.get(filename))) {
 207             while (fileScanner.hasNextLine()) {
 208 
 209                 try (Scanner lineScanner = new Scanner(
 210                         fileScanner.nextLine())) {
 211                     if (lineScanner.findInLine(pattern) == null) {
 212                         continue;
 213                     }


 324             }
 325 
 326             out[i / 2] = (byte) (h * 16 + l);
 327         }
 328 
 329         return out;
 330     }
 331 
 332     private static int hexToBin(char ch) {
 333         if ('0' <= ch && ch <= '9') {
 334             return ch - '0';
 335         }
 336         if ('A' <= ch && ch <= 'F') {
 337             return ch - 'A' + 10;
 338         }
 339         if ('a' <= ch && ch <= 'f') {
 340             return ch - 'a' + 10;
 341         }
 342         return -1;
 343     }
 344 
 345     @Override public void stopServer() {
 346         isRunning = false;
 347         if (socket != null) {
 348             try {
 349                 socket.close();
 350             } catch (Exception e) {
 351                 // ignore
 352             }
 353         }
 354     }
 355 
 356     @Override public int getPort() {
 357         if (socket != null) {
 358             return socket.getLocalPort();
 359         } else {
 360             return -1;
 361         }
 362     }
 363 }
< prev index next >