1// Copyright (c) 2005 Brian Wellington (bwelling@xbill.org)
2
3package org.xbill.DNS;
4
5import java.io.*;
6import java.net.*;
7import java.security.SecureRandom;
8import java.nio.*;
9import java.nio.channels.*;
10
11final class UDPClient extends Client {
12
13private static final int EPHEMERAL_START = 1024;
14private static final int EPHEMERAL_STOP  = 65535;
15private static final int EPHEMERAL_RANGE  = EPHEMERAL_STOP - EPHEMERAL_START;
16
17private static SecureRandom prng = new SecureRandom();
18private 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 */
35static {
36	new Thread(new Runnable() {
37			   public void run() {
38			   int n = prng.nextInt();
39			   prng_initializing = false;
40		   }}).start();
41}
42
43private boolean bound = false;
44
45public
46UDPClient(long endTime) throws IOException {
47	super(DatagramChannel.open(), endTime);
48}
49
50private void
51bind_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
84void
85bind(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
102void
103connect(SocketAddress addr) throws IOException {
104	if (!bound)
105		bind(null);
106	DatagramChannel channel = (DatagramChannel) key.channel();
107	channel.connect(addr);
108}
109
110void
111send(byte [] data) throws IOException {
112	DatagramChannel channel = (DatagramChannel) key.channel();
113	verboseLog("UDP write", data);
114	channel.write(ByteBuffer.wrap(data));
115}
116
117byte []
118recv(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
140static byte []
141sendrecv(SocketAddress local, SocketAddress remote, byte [] data, int max,
142	 long endTime)
143throws 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
157static byte []
158sendrecv(SocketAddress addr, byte [] data, int max, long endTime)
159throws IOException
160{
161	return sendrecv(null, addr, data, max, endTime);
162}
163
164}
165