2020
2121package org .jnode .net .ipv4 .util ;
2222
23+ import java .io .InterruptedIOException ;
24+ import java .io .IOException ;
25+ import java .net .DatagramPacket ;
26+ import java .net .DatagramSocket ;
27+ import java .net .InetAddress ;
2328import java .net .UnknownHostException ;
2429import java .security .AccessController ;
2530import java .security .PrivilegedAction ;
2833import java .util .HashMap ;
2934import java .util .Map ;
3035import java .util .Collection ;
36+ import java .util .Vector ;
3137
3238import org .jnode .driver .net .NetworkException ;
3339import org .jnode .net .ProtocolAddress ;
3440import org .jnode .net .Resolver ;
3541import org .jnode .net .ipv4 .IPv4Address ;
3642import org .jnode .annotation .SharedStatics ;
43+ import org .xbill .DNS .DClass ;
3744import org .xbill .DNS .ExtendedResolver ;
45+ import org .xbill .DNS .Flags ;
3846import org .xbill .DNS .Lookup ;
47+ import org .xbill .DNS .Message ;
48+ import org .xbill .DNS .Name ;
49+ import org .xbill .DNS .Rcode ;
3950import org .xbill .DNS .Record ;
51+ import org .xbill .DNS .Section ;
4052import org .xbill .DNS .SimpleResolver ;
4153import org .xbill .DNS .TextParseException ;
54+ import org .xbill .DNS .Type ;
4255
4356/**
4457 * @author Martin Hartvig
@@ -50,13 +63,17 @@ public class ResolverImpl implements Resolver {
5063 // FIXME ... this class looks like it is supposed to implement
5164 // the Singleton pattern. So how come the management methods
5265 // and a lot of the state is 'static'?
53- private static final int DNS_TIMEOUT_SECONDS = 5 ;
54- private static final int DNS_RETRIES = 1 ;
66+ private static final int DNS_TIMEOUT_SECONDS = 1 ;
67+ private static final int DNS_RETRIES = 0 ;
68+ private static final int DNS_LOOKUP_TIMEOUT_MILLIS = 1000 ;
69+ private static final int DNS_SERVER_PORT = 53 ;
5570
5671 private static ExtendedResolver resolver ;
5772
5873 private static Map <String , org .xbill .DNS .Resolver > resolvers ;
5974
75+ private static Vector <String > dnsServers ;
76+
6077 private static Map <String , ProtocolAddress []> hosts ;
6178
6279 private static Resolver res = null ;
@@ -68,6 +85,7 @@ public class ResolverImpl implements Resolver {
6885 ProtocolAddress [] protocolAddresses = new ProtocolAddress [] {new IPv4Address ("127.0.0.1" )};
6986 hosts .put (localhost , protocolAddresses );
7087 resolvers = new HashMap <String , org .xbill .DNS .Resolver >();
88+ dnsServers = new Vector <String >();
7189 }
7290
7391 private ResolverImpl () {
@@ -97,7 +115,9 @@ public Object run() {
97115 * Get list all the dns servers
98116 */
99117 public static Collection <String > getDnsServers () {
100- return resolvers .keySet ();
118+ synchronized (ResolverImpl .class ) {
119+ return new Vector <String >(resolvers .keySet ());
120+ }
101121 }
102122
103123 /**
@@ -107,42 +127,43 @@ public static Collection<String> getDnsServers() {
107127 * @throws NetworkException
108128 */
109129 public static void addDnsServer (ProtocolAddress _dnsserver ) throws NetworkException {
110- try {
111- if (resolver == null ) {
112- final String [] server = new String [] {_dnsserver .toString ()};
113- try {
114- AccessController .doPrivileged (new PrivilegedExceptionAction <Object >() {
115- public Object run () throws Exception {
116- resolver = new ExtendedResolver (server );
117- configureResolver (resolver );
118- Lookup .setDefaultResolver (resolver );
119- return null ;
130+ final String key = _dnsserver .toString ();
131+ synchronized (ResolverImpl .class ) {
132+ try {
133+ if (resolver == null ) {
134+ try {
135+ AccessController .doPrivileged (new PrivilegedExceptionAction <Object >() {
136+ public Object run () throws Exception {
137+ resolver = new ExtendedResolver (new String [] {key });
138+ configureResolver (resolver );
139+ Lookup .setDefaultResolver (resolver );
140+ return null ;
141+ }
142+ });
143+ } catch (PrivilegedActionException x ) {
144+ Exception ee = x .getException ();
145+ if (ee instanceof UnknownHostException ) {
146+ throw (UnknownHostException ) ee ;
147+ } else {
148+ throw new RuntimeException (ee );
120149 }
121- });
122- } catch (PrivilegedActionException x ) {
123- Exception ee = x .getException ();
124- if (ee instanceof UnknownHostException ) {
125- throw (UnknownHostException ) ee ;
126- } else {
127- throw new RuntimeException (ee );
128150 }
151+ dnsServers .clear ();
152+ dnsServers .add (key );
153+ resolvers .put (key , resolver );
154+ return ;
129155 }
130- resolvers .put (_dnsserver .toString (), resolver );
131- }
132- } catch (UnknownHostException e ) {
133- throw new NetworkException ("Can't add DNS server" , e );
134- }
135156
136- try {
137- String key = _dnsserver .toString ();
138- if (!resolvers .containsKey (key )) {
139- SimpleResolver simpleResolver = new SimpleResolver (key );
140- resolver .addResolver (simpleResolver );
141- configureResolver (resolver );
142- resolvers .put (key , simpleResolver );
157+ if (!resolvers .containsKey (key )) {
158+ SimpleResolver simpleResolver = new SimpleResolver (key );
159+ resolver .addResolver (simpleResolver );
160+ configureResolver (resolver );
161+ resolvers .put (key , simpleResolver );
162+ dnsServers .add (key );
163+ }
164+ } catch (UnknownHostException e ) {
165+ throw new NetworkException ("Can't add DNS server" , e );
143166 }
144- } catch (UnknownHostException e ) {
145- throw new NetworkException ("Can't add DNS server" , e );
146167 }
147168 }
148169
@@ -152,23 +173,165 @@ public Object run() throws Exception {
152173 * @param _dnsserver
153174 */
154175 public static void removeDnsServer (ProtocolAddress _dnsserver ) {
155- if (resolver == null ) {
156- return ;
157- }
158176 String key = _dnsserver .toString ();
159- if (resolvers .containsKey (key )) {
160- org .xbill .DNS .Resolver resolv = resolvers .remove (key );
161- if (resolver .getResolvers ().length == 1 ) {
162- resolver = null ;
163- } else {
164- resolver .deleteResolver (resolv );
177+ synchronized (ResolverImpl .class ) {
178+ if (resolver == null ) {
179+ return ;
180+ }
181+ if (resolvers .containsKey (key )) {
182+ org .xbill .DNS .Resolver resolv = resolvers .remove (key );
183+ dnsServers .remove (key );
184+ if (resolver .getResolvers ().length == 1 ) {
185+ resolver = null ;
186+ dnsServers .clear ();
187+ } else {
188+ resolver .deleteResolver (resolv );
189+ }
190+ }
191+ }
192+ }
193+
194+ static void configureResolver (ExtendedResolver resolver ) {
195+ configureResolver (resolver , DNS_TIMEOUT_SECONDS , DNS_RETRIES );
196+ }
197+
198+ static void configureResolver (ExtendedResolver resolver , int timeoutSeconds , int retries ) {
199+ resolver .setTimeout (timeoutSeconds );
200+ resolver .setRetries (retries );
201+ }
202+
203+ static Record [] resolve (final String hostname , int timeoutMillis ) throws UnknownHostException {
204+ final String [] servers ;
205+ synchronized (ResolverImpl .class ) {
206+ if ((dnsServers == null ) || dnsServers .isEmpty ()) {
207+ throw new UnknownHostException ("No DNS server configured" );
208+ }
209+ servers = dnsServers .toArray (new String [dnsServers .size ()]);
210+ }
211+ UnknownHostException lastException = null ;
212+ for (String server : servers ) {
213+ try {
214+ return resolveFromServer (hostname , server , timeoutMillis );
215+ } catch (UnknownHostException ex ) {
216+ lastException = ex ;
217+ }
218+ }
219+ if (lastException != null ) {
220+ throw lastException ;
221+ }
222+ throw new UnknownHostException ("DNS lookup failed" );
223+ }
224+
225+ private static Record [] resolveFromServer (String hostname , String server , int timeoutMillis )
226+ throws UnknownHostException {
227+ try {
228+ String dnsName = hostname .endsWith ("." ) ? hostname : hostname + "." ;
229+ Name name = Name .fromString (dnsName );
230+ Record question = Record .newRecord (name , Type .A , DClass .IN );
231+ Message query = Message .newQuery (question );
232+ query .getHeader ().setFlag (Flags .RD );
233+ byte [] response = sendUdpWithCloseTimeout (InetAddress .getByName (server ), DNS_SERVER_PORT ,
234+ query .toWire (), timeoutMillis );
235+ Message responseMessage = new Message (response );
236+ if (responseMessage .getHeader ().getID () != query .getHeader ().getID ()) {
237+ throw new UnknownHostException ("Invalid DNS response id" );
238+ }
239+ int rcode = responseMessage .getRcode ();
240+ if (rcode != Rcode .NOERROR ) {
241+ throw new UnknownHostException ("DNS lookup failed: " + Rcode .string (rcode ));
165242 }
243+ Record [] answers = responseMessage .getSectionArray (Section .ANSWER );
244+ if ((answers == null ) || (answers .length == 0 )) {
245+ throw new UnknownHostException ("No DNS answer for " + hostname );
246+ }
247+ return answers ;
248+ } catch (TextParseException ex ) {
249+ throw new UnknownHostException (hostname );
250+ } catch (IOException ex ) {
251+ throw new UnknownHostException (ex .getMessage ());
166252 }
167253 }
168254
169- private static void configureResolver (ExtendedResolver resolver ) {
170- resolver .setTimeout (DNS_TIMEOUT_SECONDS );
171- resolver .setRetries (DNS_RETRIES );
255+ static byte [] sendUdpWithCloseTimeout (final InetAddress address , final int port ,
256+ final byte [] request , final int timeoutMillis ) throws IOException {
257+ final byte [] requestCopy = copy (request , 0 , request .length );
258+ final UdpResult result = new UdpResult ();
259+ Thread lookupThread = new Thread (new Runnable () {
260+ public void run () {
261+ DatagramSocket socket = null ;
262+ try {
263+ socket = new DatagramSocket ();
264+ result .socket = socket ;
265+ socket .setSoTimeout (timeoutMillis );
266+ DatagramPacket requestPacket = new DatagramPacket (requestCopy , requestCopy .length , address , port );
267+ socket .send (requestPacket );
268+ byte [] buffer = new byte [512 ];
269+ DatagramPacket responsePacket = new DatagramPacket (buffer , buffer .length );
270+ socket .receive (responsePacket );
271+ result .data = copy (responsePacket .getData (), 0 , responsePacket .getLength ());
272+ } catch (Throwable t ) {
273+ result .throwable = t ;
274+ } finally {
275+ if (socket != null ) {
276+ socket .close ();
277+ }
278+ }
279+ }
280+ });
281+ lookupThread .setDaemon (true );
282+ lookupThread .start ();
283+ long deadline = System .currentTimeMillis () + timeoutMillis ;
284+ try {
285+ while (lookupThread .isAlive ()) {
286+ long remaining = deadline - System .currentTimeMillis ();
287+ if (remaining <= 0 ) {
288+ break ;
289+ }
290+ Thread .sleep (Math .min (50 , remaining ));
291+ }
292+ } catch (InterruptedException e ) {
293+ Thread .currentThread ().interrupt ();
294+ if (result .socket != null ) {
295+ result .socket .close ();
296+ }
297+ throw new InterruptedIOException ("DNS lookup interrupted" );
298+ }
299+ if (lookupThread .isAlive ()) {
300+ if (result .socket != null ) {
301+ result .socket .close ();
302+ }
303+ try {
304+ lookupThread .join (100 );
305+ } catch (InterruptedException e ) {
306+ Thread .currentThread ().interrupt ();
307+ }
308+ throw new InterruptedIOException ("DNS lookup timed out" );
309+ }
310+ if (result .throwable instanceof Error ) {
311+ throw (Error ) result .throwable ;
312+ }
313+ if (result .throwable instanceof RuntimeException ) {
314+ throw (RuntimeException ) result .throwable ;
315+ }
316+ if (result .throwable instanceof IOException ) {
317+ throw (IOException ) result .throwable ;
318+ }
319+ if (result .throwable != null ) {
320+ throw new IOException (result .throwable .getMessage ());
321+ }
322+ return result .data ;
323+ }
324+
325+ private static byte [] copy (byte [] source , int offset , int length ) {
326+ byte [] copy = new byte [length ];
327+ System .arraycopy (source , offset , copy , 0 , length );
328+ return copy ;
329+ }
330+
331+ private static class UdpResult {
332+ private volatile DatagramSocket socket ;
333+ private volatile byte [] data ;
334+ private volatile Throwable throwable ;
172335 }
173336
174337 /**
@@ -198,7 +361,11 @@ public ProtocolAddress[] getByName(final String hostname) throws UnknownHostExce
198361 // FIXME ... why is this a special case? Comment please or fix it.
199362 throw new UnknownHostException ("*" );
200363 }
201- if (resolver == null ) {
364+ final ExtendedResolver dnsResolver ;
365+ synchronized (ResolverImpl .class ) {
366+ dnsResolver = resolver ;
367+ }
368+ if (dnsResolver == null ) {
202369 throw new UnknownHostException (hostname );
203370 }
204371
@@ -215,29 +382,14 @@ public ProtocolAddress[] run() throws UnknownHostException {
215382 return protocolAddresses ;
216383 }
217384
218- Lookup .setDefaultResolver (resolver );
219-
220- final Lookup lookup ;
221- try {
222- lookup = new Lookup (hostname );
223- } catch (TextParseException e ) {
224- throw new UnknownHostException (hostname );
225- }
226-
227- lookup .run ();
385+ final Record [] records = resolve (hostname , DNS_LOOKUP_TIMEOUT_MILLIS );
386+ final int recordCount = records .length ;
228387
229- if (lookup .getResult () == Lookup .SUCCESSFUL ) {
230- final Record [] records = lookup .getAnswers ();
231- final int recordCount = records .length ;
388+ protocolAddresses = new ProtocolAddress [recordCount ];
232389
233- protocolAddresses = new ProtocolAddress [recordCount ];
234-
235- for (int i = 0 ; i < recordCount ; i ++) {
236- final Record record = records [i ];
237- protocolAddresses [i ] = new IPv4Address (record .rdataToString ());
238- }
239- } else {
240- throw new UnknownHostException (lookup .getErrorString ());
390+ for (int i = 0 ; i < recordCount ; i ++) {
391+ final Record record = records [i ];
392+ protocolAddresses [i ] = new IPv4Address (record .rdataToString ());
241393 }
242394
243395 return protocolAddresses ;
0 commit comments