1 // Copyright (c) 2005 Brian Wellington (bwelling@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
UDPClient(long endTime)46 UDPClient(long endTime) throws IOException {
47 	super(DatagramChannel.open(), endTime);
48 }
49 
50 private void
bind_random(InetSocketAddress addr)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
bind(SocketAddress addr)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
connect(SocketAddress addr)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
send(byte [] data)111 send(byte [] data) throws IOException {
112 	DatagramChannel channel = (DatagramChannel) key.channel();
113 	verboseLog("UDP write", channel.socket().getLocalSocketAddress(),
114 		   channel.socket().getRemoteSocketAddress(), data);
115 	channel.write(ByteBuffer.wrap(data));
116 }
117 
118 byte []
recv(int max)119 recv(int max) throws IOException {
120 	DatagramChannel channel = (DatagramChannel) key.channel();
121 	byte [] temp = new byte[max];
122 	key.interestOps(SelectionKey.OP_READ);
123 	try {
124 		while (!key.isReadable())
125 			blockUntil(key, endTime);
126 	}
127 	finally {
128 		if (key.isValid())
129 			key.interestOps(0);
130 	}
131 	long ret = channel.read(ByteBuffer.wrap(temp));
132 	if (ret <= 0)
133 		throw new EOFException();
134 	int len = (int) ret;
135 	byte [] data = new byte[len];
136 	System.arraycopy(temp, 0, data, 0, len);
137 	verboseLog("UDP read", channel.socket().getLocalSocketAddress(),
138 		   channel.socket().getRemoteSocketAddress(), data);
139 	return data;
140 }
141 
142 static byte []
sendrecv(SocketAddress local, SocketAddress remote, byte [] data, int max, long endTime)143 sendrecv(SocketAddress local, SocketAddress remote, byte [] data, int max,
144 	 long endTime)
145 throws IOException
146 {
147 	UDPClient client = new UDPClient(endTime);
148 	try {
149 		client.bind(local);
150 		client.connect(remote);
151 		client.send(data);
152 		return client.recv(max);
153 	}
154 	finally {
155 		client.cleanup();
156 	}
157 }
158 
159 static byte []
sendrecv(SocketAddress addr, byte [] data, int max, long endTime)160 sendrecv(SocketAddress addr, byte [] data, int max, long endTime)
161 throws IOException
162 {
163 	return sendrecv(null, addr, data, max, endTime);
164 }
165 
166 }
167