| // 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); |
| } |
| |
| } |