Home | History | Annotate | Download | only in DNS
      1 // Copyright (c) 2005 Brian Wellington (bwelling (at) xbill.org)
      2 
      3 package org.xbill.DNS;
      4 
      5 import java.io.*;
      6 import java.net.*;
      7 import java.security.SecureRandom;
      8 import java.nio.*;
      9 import java.nio.channels.*;
     10 
     11 final class UDPClient extends Client {
     12 
     13 private static final int EPHEMERAL_START = 1024;
     14 private static final int EPHEMERAL_STOP  = 65535;
     15 private static final int EPHEMERAL_RANGE  = EPHEMERAL_STOP - EPHEMERAL_START;
     16 
     17 private static SecureRandom prng = new SecureRandom();
     18 private static volatile boolean prng_initializing = true;
     19 
     20 /*
     21  * On some platforms (Windows), the SecureRandom module initialization involves
     22  * a call to InetAddress.getLocalHost(), which can end up here if using a
     23  * dnsjava name service provider.
     24  *
     25  * This can cause problems in multiple ways.
     26  *   - If the SecureRandom seed generation process calls into here, and this
     27  *     module attempts to seed the local SecureRandom object, the thread hangs.
     28  *   - If something else calls InetAddress.getLocalHost(), and that causes this
     29  *     module to seed the local SecureRandom object, the thread hangs.
     30  *
     31  * To avoid both of these, check at initialization time to see if InetAddress
     32  * is in the call chain.  If so, initialize the SecureRandom object in a new
     33  * thread, and disable port randomization until it completes.
     34  */
     35 static {
     36 	new Thread(new Runnable() {
     37 			   public void run() {
     38 			   int n = prng.nextInt();
     39 			   prng_initializing = false;
     40 		   }}).start();
     41 }
     42 
     43 private boolean bound = false;
     44 
     45 public
     46 UDPClient(long endTime) throws IOException {
     47 	super(DatagramChannel.open(), endTime);
     48 }
     49 
     50 private void
     51 bind_random(InetSocketAddress addr) throws IOException
     52 {
     53 	if (prng_initializing) {
     54 		try {
     55 			Thread.sleep(2);
     56 		}
     57 		catch (InterruptedException e) {
     58 		}
     59 		if (prng_initializing)
     60 			return;
     61 	}
     62 
     63 	DatagramChannel channel = (DatagramChannel) key.channel();
     64 	InetSocketAddress temp;
     65 
     66 	for (int i = 0; i < 1024; i++) {
     67 		try {
     68 			int port = prng.nextInt(EPHEMERAL_RANGE) +
     69 				   EPHEMERAL_START;
     70 			if (addr != null)
     71 				temp = new InetSocketAddress(addr.getAddress(),
     72 							     port);
     73 			else
     74 				temp = new InetSocketAddress(port);
     75 			channel.socket().bind(temp);
     76 			bound = true;
     77 			return;
     78 		}
     79 		catch (SocketException e) {
     80 		}
     81 	}
     82 }
     83 
     84 void
     85 bind(SocketAddress addr) throws IOException {
     86 	if (addr == null ||
     87 	    (addr instanceof InetSocketAddress &&
     88 	     ((InetSocketAddress)addr).getPort() == 0))
     89 	{
     90 		bind_random((InetSocketAddress) addr);
     91 		if (bound)
     92 			return;
     93 	}
     94 
     95 	if (addr != null) {
     96 		DatagramChannel channel = (DatagramChannel) key.channel();
     97 		channel.socket().bind(addr);
     98 		bound = true;
     99 	}
    100 }
    101 
    102 void
    103 connect(SocketAddress addr) throws IOException {
    104 	if (!bound)
    105 		bind(null);
    106 	DatagramChannel channel = (DatagramChannel) key.channel();
    107 	channel.connect(addr);
    108 }
    109 
    110 void
    111 send(byte [] data) throws IOException {
    112 	DatagramChannel channel = (DatagramChannel) key.channel();
    113 	verboseLog("UDP write", data);
    114 	channel.write(ByteBuffer.wrap(data));
    115 }
    116 
    117 byte []
    118 recv(int max) throws IOException {
    119 	DatagramChannel channel = (DatagramChannel) key.channel();
    120 	byte [] temp = new byte[max];
    121 	key.interestOps(SelectionKey.OP_READ);
    122 	try {
    123 		while (!key.isReadable())
    124 			blockUntil(key, endTime);
    125 	}
    126 	finally {
    127 		if (key.isValid())
    128 			key.interestOps(0);
    129 	}
    130 	long ret = channel.read(ByteBuffer.wrap(temp));
    131 	if (ret <= 0)
    132 		throw new EOFException();
    133 	int len = (int) ret;
    134 	byte [] data = new byte[len];
    135 	System.arraycopy(temp, 0, data, 0, len);
    136 	verboseLog("UDP read", data);
    137 	return data;
    138 }
    139 
    140 static byte []
    141 sendrecv(SocketAddress local, SocketAddress remote, byte [] data, int max,
    142 	 long endTime)
    143 throws IOException
    144 {
    145 	UDPClient client = new UDPClient(endTime);
    146 	try {
    147 		client.bind(local);
    148 		client.connect(remote);
    149 		client.send(data);
    150 		return client.recv(max);
    151 	}
    152 	finally {
    153 		client.cleanup();
    154 	}
    155 }
    156 
    157 static byte []
    158 sendrecv(SocketAddress addr, byte [] data, int max, long endTime)
    159 throws IOException
    160 {
    161 	return sendrecv(null, addr, data, max, endTime);
    162 }
    163 
    164 }
    165