// Copyright (c) 2005 Brian Wellington (bwelling@xbill.org)
package org.xbill.DNS;
import java.io.*;
import java.net.*;
import java.security.SecureRandom;
import java.nio.*;
import java.nio.channels.*;
final class UDPClient extends Client {
private static final int EPHEMERAL_START = 1024;
private static final int EPHEMERAL_STOP = 65535;
private static final int EPHEMERAL_RANGE = EPHEMERAL_STOP - EPHEMERAL_START;
private static SecureRandom prng = new SecureRandom();
private static volatile boolean prng_initializing = true;
/*
* On some platforms (Windows), the SecureRandom module initialization involves
* a call to InetAddress.getLocalHost(), which can end up here if using a
* dnsjava name service provider.
*
* This can cause problems in multiple ways.
* - If the SecureRandom seed generation process calls into here, and this
* module attempts to seed the local SecureRandom object, the thread hangs.
* - If something else calls InetAddress.getLocalHost(), and that causes this
* module to seed the local SecureRandom object, the thread hangs.
*
* To avoid both of these, check at initialization time to see if InetAddress
* is in the call chain. If so, initialize the SecureRandom object in a new
* thread, and disable port randomization until it completes.
*/
static {
new Thread(new Runnable() {
public void run() {
int n = prng.nextInt();
prng_initializing = false;
}}).start();
}
private boolean bound = false;
public
UDPClient(long endTime) throws IOException {
super(DatagramChannel.open(), endTime);
}
private void
bind_random(InetSocketAddress addr) throws IOException
{
if (prng_initializing) {
try {
Thread.sleep(2);
}
catch (InterruptedException e) {
}
if (prng_initializing)
return;
}
DatagramChannel channel = (DatagramChannel) key.channel();
InetSocketAddress temp;
for (int i = 0; i < 1024; i++) {
try {
int port = prng.nextInt(EPHEMERAL_RANGE) +
EPHEMERAL_START;
if (addr != null)
temp = new InetSocketAddress(addr.getAddress(),
port);
else
temp = new InetSocketAddress(port);
channel.socket().bind(temp);
bound = true;
return;
}
catch (SocketException e) {
}
}
}
void
bind(SocketAddress addr) throws IOException {
if (addr == null ||
(addr instanceof InetSocketAddress &&
((InetSocketAddress)addr).getPort() == 0))
{
bind_random((InetSocketAddress) addr);
if (bound)
return;
}
if (addr != null) {
DatagramChannel channel = (DatagramChannel) key.channel();
channel.socket().bind(addr);
bound = true;
}
}
void
connect(SocketAddress addr) throws IOException {
if (!bound)
bind(null);
DatagramChannel channel = (DatagramChannel) key.channel();
channel.connect(addr);
}
void
send(byte [] data) throws IOException {
DatagramChannel channel = (DatagramChannel) key.channel();
verboseLog("UDP write", data);
channel.write(ByteBuffer.wrap(data));
}
byte []
recv(int max) throws IOException {
DatagramChannel channel = (DatagramChannel) key.channel();
byte [] temp = new byte[max];
key.interestOps(SelectionKey.OP_READ);
try {
while (!key.isReadable())
blockUntil(key, endTime);
}
finally {
if (key.isValid())
key.interestOps(0);
}
long ret = channel.read(ByteBuffer.wrap(temp));
if (ret <= 0)
throw new EOFException();
int len = (int) ret;
byte [] data = new byte[len];
System.arraycopy(temp, 0, data, 0, len);
verboseLog("UDP read", data);
return data;
}
static byte []
sendrecv(SocketAddress local, SocketAddress remote, byte [] data, int max,
long endTime)
throws IOException
{
UDPClient client = new UDPClient(endTime);
try {
client.bind(local);
client.connect(remote);
client.send(data);
return client.recv(max);
}
finally {
client.cleanup();
}
}
static byte []
sendrecv(SocketAddress addr, byte [] data, int max, long endTime)
throws IOException
{
return sendrecv(null, addr, data, max, endTime);
}
}