KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > knowgate > jcifs > netbios > NameServiceClient


1 /* jcifs smb client library in Java
2  * Copyright (C) 2000 "Michael B. Allen" <jcifs at samba dot org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */

18
19 package com.knowgate.jcifs.netbios;
20
21 import java.net.InetAddress JavaDoc;
22 import java.net.DatagramSocket JavaDoc;
23 import java.net.DatagramPacket JavaDoc;
24 import java.net.UnknownHostException JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InterruptedIOException JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.StringTokenizer JavaDoc;
29
30 import com.knowgate.debug.*;
31 import com.knowgate.misc.Gadgets;
32 import com.knowgate.jcifs.Config;
33
34 class NameServiceClient implements Runnable JavaDoc {
35
36     static final int DEFAULT_SO_TIMEOUT = 5000;
37     static final int DEFAULT_RCV_BUF_SIZE = 576;
38     static final int DEFAULT_SND_BUF_SIZE = 576;
39     static final int NAME_SERVICE_UDP_PORT = 137;
40     static final int DEFAULT_RETRY_COUNT = 2;
41     static final int DEFAULT_RETRY_TIMEOUT = 3000;
42
43     static final int RESOLVER_LMHOSTS = 1;
44     static final int RESOLVER_BCAST = 2;
45     static final int RESOLVER_WINS = 3;
46
47     private static final int SND_BUF_SIZE = Config.getInt( "jcifs.netbios.snd_buf_size", DEFAULT_SND_BUF_SIZE );
48     private static final int RCV_BUF_SIZE = Config.getInt( "jcifs.netbios.rcv_buf_size", DEFAULT_RCV_BUF_SIZE );
49     private static final int SO_TIMEOUT = Config.getInt( "jcifs.netbios.soTimeout", DEFAULT_SO_TIMEOUT );
50     private static final int RETRY_COUNT = Config.getInt( "jcifs.netbios.retryCount", DEFAULT_RETRY_COUNT );
51     private static final int RETRY_TIMEOUT = Config.getInt( "jcifs.netbios.retryTimeout", DEFAULT_RETRY_TIMEOUT);
52     private static final int LPORT = Config.getInt( "jcifs.netbios.lport", 0 );
53     private static final InetAddress JavaDoc LADDR = Config.getInetAddress( "jcifs.netbios.laddr", null );
54     private static final String JavaDoc RO = Config.getProperty( "jcifs.resolveOrder" );
55
56     private final Object JavaDoc LOCK = new Object JavaDoc();
57
58     private int lport, closeTimeout;
59     private byte[] snd_buf, rcv_buf;
60     private DatagramSocket JavaDoc socket;
61     private DatagramPacket JavaDoc in, out;
62     private HashMap JavaDoc responseTable = new HashMap JavaDoc();
63     private Thread JavaDoc thread;
64     private int nextNameTrnId = 0;
65     private int[] resolveOrder;
66
67     InetAddress JavaDoc laddr, baddr;
68
69     NameServiceClient() {
70         this( LPORT, LADDR );
71     }
72     NameServiceClient( int lport, InetAddress JavaDoc laddr ) {
73         this.lport = lport;
74         this.laddr = laddr;
75
76         try {
77             baddr = Config.getInetAddress( "jcifs.netbios.baddr",
78                         InetAddress.getByName( "255.255.255.255" ));
79         } catch( UnknownHostException JavaDoc uhe ) {
80         }
81
82         snd_buf = new byte[SND_BUF_SIZE];
83         rcv_buf = new byte[RCV_BUF_SIZE];
84         out = new DatagramPacket JavaDoc( snd_buf, SND_BUF_SIZE, baddr, NAME_SERVICE_UDP_PORT );
85         in = new DatagramPacket JavaDoc( rcv_buf, RCV_BUF_SIZE );
86
87         if( RO == null || RO.length() == 0 ) {
88
89             /* No resolveOrder has been specified, use the
90              * default which is LMHOSTS,WINS,BCAST,DNS or just
91              * LMHOSTS,BCAST,DNS if jcifs.netbios.wins has not
92              * been specified.
93              */

94
95             if( NbtAddress.getWINSAddress() == null ) {
96                 resolveOrder = new int[2];
97                 resolveOrder[0] = RESOLVER_LMHOSTS;
98                 resolveOrder[1] = RESOLVER_BCAST;
99             } else {
100                 resolveOrder = new int[3];
101                 resolveOrder[0] = RESOLVER_LMHOSTS;
102                 resolveOrder[1] = RESOLVER_WINS;
103                 resolveOrder[2] = RESOLVER_BCAST;
104             }
105         } else {
106             int[] tmp = new int[3];
107             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc( RO, "," );
108             int i = 0;
109             while( st.hasMoreTokens() ) {
110                 String JavaDoc s = st.nextToken().trim();
111                 if( s.equalsIgnoreCase( "LMHOSTS" )) {
112                     tmp[i++] = RESOLVER_LMHOSTS;
113                 } else if( s.equalsIgnoreCase( "WINS" )) {
114                     if( NbtAddress.getWINSAddress() == null ) {
115                         if( DebugFile.trace ) {
116                             DebugFile.writeln( "NetBIOS resolveOrder specifies WINS however the " +
117                                     "jcifs.netbios.wins property has not been set" );
118                         }
119                         continue;
120                     }
121                     tmp[i++] = RESOLVER_WINS;
122                 } else if( s.equalsIgnoreCase( "BCAST" )) {
123                     tmp[i++] = RESOLVER_BCAST;
124                 } else if( s.equalsIgnoreCase( "DNS" )) {
125                     ; // skip
126
} else if( DebugFile.trace ) {
127                     DebugFile.writeln( "unknown resolver method: " + s );
128                 }
129             }
130             resolveOrder = new int[i];
131             System.arraycopy( tmp, 0, resolveOrder, 0, i );
132         }
133     }
134
135     int getNextNameTrnId() {
136         if(( ++nextNameTrnId & 0xFFFF ) == 0 ) {
137             nextNameTrnId = 1;
138         }
139         return nextNameTrnId;
140     }
141     void ensureOpen( int timeout ) throws IOException JavaDoc {
142         closeTimeout = 0;
143         if( SO_TIMEOUT != 0 ) {
144             closeTimeout = Math.max( SO_TIMEOUT, timeout );
145         }
146         // If socket is still good, the new closeTimeout will
147
// be ignored; see tryClose comment.
148
if( socket == null ) {
149             socket = new DatagramSocket JavaDoc( lport, laddr );
150             thread = new Thread JavaDoc( this, "JCIFS-NameServiceClient" );
151             thread.setDaemon( true );
152             thread.start();
153         }
154     }
155     void tryClose() {
156         synchronized( LOCK ) {
157
158             /* Yes, there is the potential to drop packets
159              * because we might close the socket during a
160              * request. However the chances are slim and the
161              * retry code should ensure the overall request
162              * is serviced. The alternative complicates things
163              * more than I think is worth it.
164              */

165
166             if( socket != null ) {
167                 socket.close();
168                 socket = null;
169             }
170             thread = null;
171             responseTable.clear();
172         }
173     }
174     public void run() {
175         int nameTrnId;
176         NameServicePacket response;
177
178         while( thread == Thread.currentThread() ) {
179             in.setLength( RCV_BUF_SIZE );
180             try {
181                 socket.setSoTimeout( closeTimeout );
182                 socket.receive( in );
183             } catch( IOException JavaDoc ioe ) {
184                 tryClose();
185                 break;
186             }
187
188             if( DebugFile.trace )
189                 DebugFile.writeln( "NetBIOS: new data read from socket" );
190
191             nameTrnId = NameServicePacket.readNameTrnId( rcv_buf, 0 );
192             response = (NameServicePacket)responseTable.get( new Integer JavaDoc( nameTrnId ));
193             if( response == null || response.received ) {
194                 continue;
195             }
196             synchronized( response ) {
197                 response.readWireFormat( rcv_buf, 0 );
198                 response.received = true;
199
200                 response.notify();
201             }
202         }
203     }
204     void send( NameServicePacket request, NameServicePacket response,
205                                             int timeout ) throws IOException JavaDoc {
206         Integer JavaDoc nid = null;
207         int count = 0;
208
209         synchronized( response ) {
210             do {
211                 try {
212                     synchronized( LOCK ) {
213                         request.nameTrnId = getNextNameTrnId();
214                         nid = new Integer JavaDoc( request.nameTrnId );
215
216                         out.setAddress( request.addr );
217                         out.setLength( request.writeWireFormat( snd_buf, 0 ));
218                         response.received = false;
219
220                         responseTable.put( nid, response );
221                         ensureOpen( timeout + 1000 );
222                         socket.send( out );
223
224                     }
225
226                     response.wait( timeout );
227
228                 } catch( InterruptedException JavaDoc ie ) {
229                 } finally {
230                     responseTable.remove( nid );
231                 }
232
233                 if( !response.received &&
234                             NbtAddress.NBNS.length > 1 &&
235                             NbtAddress.isWINS( request.addr )) {
236                                 /* Message was sent to WINS but
237                                  * failed to receive response.
238                                  * Try a different WINS server.
239                                  */

240                     request.addr = NbtAddress.switchWINS();
241                     if( count == 0 ) {
242                         count = NbtAddress.NBNS.length - 1;
243                     }
244                 }
245             } while( count-- > 0 );
246         }
247     }
248
249     NbtAddress getByName( Name name, InetAddress JavaDoc addr )
250                                             throws UnknownHostException JavaDoc {
251         int n;
252         NameQueryRequest request = new NameQueryRequest( name );
253         NameQueryResponse response = new NameQueryResponse();
254
255         if( addr != null ) { /* UniAddress calls always use this
256                               * because it specifies addr
257                               */

258             request.addr = addr; /* if addr ends with 255 flag it bcast */
259             request.isBroadcast = (addr.getAddress()[3] == (byte)0xFF);
260
261             n = RETRY_COUNT;
262             do {
263                 try {
264                     send( request, response, RETRY_TIMEOUT );
265                 } catch( IOException JavaDoc ioe ) {
266                     if( DebugFile.trace )
267                         new ErrorHandler (ioe);
268                     throw new UnknownHostException JavaDoc( name.name );
269                 }
270
271                 if( response.received && response.resultCode == 0 ) {
272                     response.addrEntry.hostName.srcHashCode = addr.hashCode();
273                     return response.addrEntry;
274                 }
275             } while( --n > 0 && request.isBroadcast );
276
277             throw new UnknownHostException JavaDoc( name.name );
278         }
279
280         /* If a target address to query was not specified explicitly
281          * with the addr parameter we fall into this resolveOrder routine.
282          */

283
284         for( int i = 0; i < resolveOrder.length; i++ ) {
285             try {
286                 switch( resolveOrder[i] ) {
287                     case RESOLVER_LMHOSTS:
288                         NbtAddress ans = Lmhosts.getByName( name );
289                         if( ans != null ) {
290                             ans.hostName.srcHashCode = 0; // just has to be different
291
// from other methods
292
return ans;
293                         }
294                         break;
295                     case RESOLVER_WINS:
296                     case RESOLVER_BCAST:
297                         if( resolveOrder[i] == RESOLVER_WINS &&
298                                 name.name != NbtAddress.MASTER_BROWSER_NAME &&
299                                 name.hexCode != 0x1d ) {
300                             request.addr = NbtAddress.getWINSAddress();
301                             request.isBroadcast = false;
302                         } else {
303                             request.addr = baddr;
304                             request.isBroadcast = true;
305                         }
306
307                         n = RETRY_COUNT;
308                         while( n-- > 0 ) {
309                             try {
310                                 send( request, response, RETRY_TIMEOUT );
311                             } catch( IOException JavaDoc ioe ) {
312                                 if( DebugFile.trace )
313                                     new ErrorHandler(ioe);
314                                 throw new UnknownHostException JavaDoc( name.name );
315                             }
316                             if( response.received && response.resultCode == 0 ) {
317
318 /* Before we return, in anticipation of this address being cached we must
319  * augment the addresses name's hashCode to distinguish those resolved by
320  * Lmhosts, WINS, or BCAST. Otherwise a failed query from say WINS would
321  * get pulled out of the cache for a BCAST on the same name.
322  */

323                                 response.addrEntry.hostName.srcHashCode =
324                                                         request.addr.hashCode();
325                                 return response.addrEntry;
326                             } else if( resolveOrder[i] == RESOLVER_WINS ) {
327                                 /* If WINS reports negative, no point in retry
328                                  */

329                                 break;
330                             }
331                         }
332                         break;
333                 }
334             } catch( IOException JavaDoc ioe ) {
335             }
336         }
337         throw new UnknownHostException JavaDoc( name.name );
338     }
339     NbtAddress[] getNodeStatus( NbtAddress addr ) throws UnknownHostException JavaDoc {
340         int n, srcHashCode;
341         NodeStatusRequest request;
342         NodeStatusResponse response;
343
344         response = new NodeStatusResponse( addr );
345         request = new NodeStatusRequest(
346                             new Name( NbtAddress.ANY_HOSTS_NAME, 0x00, null));
347         request.addr = addr.getInetAddress();
348
349         n = RETRY_COUNT;
350         while( n-- > 0 ) {
351             try {
352                 send( request, response, RETRY_TIMEOUT );
353             } catch( IOException JavaDoc ioe ) {
354                 if( DebugFile.trace )
355                     new ErrorHandler(ioe);
356                 throw new UnknownHostException JavaDoc( addr.toString() );
357             }
358             if( response.received && response.resultCode == 0 ) {
359
360         /* For name queries resolved by different sources (e.g. WINS,
361          * BCAST, Node Status) we need to augment the hashcode generated
362          * for the addresses hostname or failed lookups for one type will
363          * be cached and cause other types to fail even though they may
364          * not be the authority for the name. For example, if a WINS lookup
365          * for FOO fails and caches unknownAddress for FOO, a subsequent
366          * lookup for FOO using BCAST should not fail because of that
367          * name cached from WINS.
368          *
369          * So, here we apply the source addresses hashCode to each name to
370          * make them specific to who resolved the name.
371          */

372
373                 srcHashCode = request.addr.hashCode();
374                 for( int i = 0; i < response.addressArray.length; i++ ) {
375                     response.addressArray[i].hostName.srcHashCode = srcHashCode;
376                 }
377                 return response.addressArray;
378             }
379         }
380         throw new UnknownHostException JavaDoc( addr.hostName.name );
381     }
382 }
383
Popular Tags