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