172 lines
5.4 KiB
Java
172 lines
5.4 KiB
Java
|
// Copyright (c) 2005 Brian Wellington (bwelling@xbill.org)
|
||
|
|
||
|
package org.xbill.DNS2.clients;
|
||
|
|
||
|
import java.io.EOFException;
|
||
|
import java.io.IOException;
|
||
|
import java.net.InetSocketAddress;
|
||
|
import java.net.SocketAddress;
|
||
|
import java.net.SocketException;
|
||
|
import java.nio.ByteBuffer;
|
||
|
import java.nio.channels.DatagramChannel;
|
||
|
import java.nio.channels.SelectionKey;
|
||
|
import java.security.SecureRandom;
|
||
|
|
||
|
public 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;
|
||
|
private boolean bound = false;
|
||
|
|
||
|
/*
|
||
|
* 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() {
|
||
|
@Override
|
||
|
public
|
||
|
void run() {
|
||
|
int n = prng.nextInt();
|
||
|
prng_initializing = false;
|
||
|
}
|
||
|
}).start();
|
||
|
}
|
||
|
|
||
|
public
|
||
|
UDPClient(long endTime) throws IOException {
|
||
|
super(DatagramChannel.open(), endTime);
|
||
|
}
|
||
|
|
||
|
public static
|
||
|
byte[] sendrecv(SocketAddress addr, byte[] data, int max, long endTime) throws IOException {
|
||
|
return sendrecv(null, addr, data, max, endTime);
|
||
|
}
|
||
|
|
||
|
public 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();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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 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",
|
||
|
channel.socket()
|
||
|
.getLocalSocketAddress(),
|
||
|
channel.socket()
|
||
|
.getRemoteSocketAddress(),
|
||
|
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",
|
||
|
channel.socket()
|
||
|
.getLocalSocketAddress(),
|
||
|
channel.socket()
|
||
|
.getRemoteSocketAddress(),
|
||
|
data);
|
||
|
return data;
|
||
|
}
|
||
|
}
|