KickJava   Java API By Example, From Geeks To Geeks.

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

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

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

252                     request.addr = NbtAddress.switchWINS();
253                     if( count == 0 ) {
254                         count = NbtAddress.NBNS.length - 1;
255                     }
256                 }
257             } while( count-- > 0 );
258         }
259     }
260
261     NbtAddress[] getAllByName( Name name, InetAddress JavaDoc addr )
262                                             throws UnknownHostException JavaDoc {
263         int n;
264         NameQueryRequest request = new NameQueryRequest( name );
265         NameQueryResponse response = new NameQueryResponse();
266
267         request.addr = addr != null ? addr : NbtAddress.getWINSAddress();
268         request.isBroadcast = request.addr == null;
269
270         if( request.isBroadcast ) {
271             request.addr = baddr;
272             n = RETRY_COUNT;
273         } else {
274             request.isBroadcast = false;
275             n = 1;
276         }
277
278         do {
279             try {
280                 send( request, response, RETRY_TIMEOUT );
281             } catch( IOException JavaDoc ioe ) {
282                 if( log.level > 1 )
283                     ioe.printStackTrace( log );
284                 throw new UnknownHostException JavaDoc( name.name );
285             }
286
287             if( response.received && response.resultCode == 0 ) {
288                 return response.addrEntry;
289             }
290         } while( --n > 0 && request.isBroadcast );
291
292         throw new UnknownHostException JavaDoc( name.name );
293     }
294     NbtAddress getByName( Name name, InetAddress JavaDoc addr )
295                                             throws UnknownHostException JavaDoc {
296         int n;
297         NameQueryRequest request = new NameQueryRequest( name );
298         NameQueryResponse response = new NameQueryResponse();
299
300         if( addr != null ) { /* UniAddress calls always use this
301                               * because it specifies addr
302                               */

303             request.addr = addr; /* if addr ends with 255 flag it bcast */
304             request.isBroadcast = (addr.getAddress()[3] == (byte)0xFF);
305
306             n = RETRY_COUNT;
307             do {
308                 try {
309                     send( request, response, RETRY_TIMEOUT );
310                 } catch( IOException JavaDoc ioe ) {
311                     if( log.level > 1 )
312                         ioe.printStackTrace( log );
313                     throw new UnknownHostException JavaDoc( name.name );
314                 }
315
316                 if( response.received && response.resultCode == 0 ) {
317                     int last = response.addrEntry.length - 1;
318                     response.addrEntry[last].hostName.srcHashCode = addr.hashCode();
319                     return response.addrEntry[last];
320                 }
321             } while( --n > 0 && request.isBroadcast );
322
323             throw new UnknownHostException JavaDoc( name.name );
324         }
325
326         /* If a target address to query was not specified explicitly
327          * with the addr parameter we fall into this resolveOrder routine.
328          */

329
330         for( int i = 0; i < resolveOrder.length; i++ ) {
331             try {
332                 switch( resolveOrder[i] ) {
333                     case RESOLVER_LMHOSTS:
334                         NbtAddress ans = Lmhosts.getByName( name );
335                         if( ans != null ) {
336                             ans.hostName.srcHashCode = 0; // just has to be different
337
// from other methods
338
return ans;
339                         }
340                         break;
341                     case RESOLVER_WINS:
342                     case RESOLVER_BCAST:
343                         if( resolveOrder[i] == RESOLVER_WINS &&
344                                 name.name != NbtAddress.MASTER_BROWSER_NAME &&
345                                 name.hexCode != 0x1d ) {
346                             request.addr = NbtAddress.getWINSAddress();
347                             request.isBroadcast = false;
348                         } else {
349                             request.addr = baddr;
350                             request.isBroadcast = true;
351                         }
352
353                         n = RETRY_COUNT;
354                         while( n-- > 0 ) {
355                             try {
356                                 send( request, response, RETRY_TIMEOUT );
357                             } catch( IOException JavaDoc ioe ) {
358                                 if( log.level > 1 )
359                                     ioe.printStackTrace( log );
360                                 throw new UnknownHostException JavaDoc( name.name );
361                             }
362                             if( response.received && response.resultCode == 0 ) {
363
364 /* Before we return, in anticipation of this address being cached we must
365  * augment the addresses name's hashCode to distinguish those resolved by
366  * Lmhosts, WINS, or BCAST. Otherwise a failed query from say WINS would
367  * get pulled out of the cache for a BCAST on the same name.
368  */

369                                 response.addrEntry[0].hostName.srcHashCode =
370                                                         request.addr.hashCode();
371                                 return response.addrEntry[0];
372                             } else if( resolveOrder[i] == RESOLVER_WINS ) {
373                                 /* If WINS reports negative, no point in retry
374                                  */

375                                 break;
376                             }
377                         }
378                         break;
379                 }
380             } catch( IOException JavaDoc ioe ) {
381             }
382         }
383         throw new UnknownHostException JavaDoc( name.name );
384     }
385     NbtAddress[] getNodeStatus( NbtAddress addr ) throws UnknownHostException JavaDoc {
386         int n, srcHashCode;
387         NodeStatusRequest request;
388         NodeStatusResponse response;
389
390         response = new NodeStatusResponse( addr );
391         request = new NodeStatusRequest(
392                             new Name( NbtAddress.ANY_HOSTS_NAME, 0x00, null));
393         request.addr = addr.getInetAddress();
394
395         n = RETRY_COUNT;
396         while( n-- > 0 ) {
397             try {
398                 send( request, response, RETRY_TIMEOUT );
399             } catch( IOException JavaDoc ioe ) {
400                 if( log.level > 1 )
401                     ioe.printStackTrace( log );
402                 throw new UnknownHostException JavaDoc( addr.toString() );
403             }
404             if( response.received && response.resultCode == 0 ) {
405
406         /* For name queries resolved by different sources (e.g. WINS,
407          * BCAST, Node Status) we need to augment the hashcode generated
408          * for the addresses hostname or failed lookups for one type will
409          * be cached and cause other types to fail even though they may
410          * not be the authority for the name. For example, if a WINS lookup
411          * for FOO fails and caches unknownAddress for FOO, a subsequent
412          * lookup for FOO using BCAST should not fail because of that
413          * name cached from WINS.
414          *
415          * So, here we apply the source addresses hashCode to each name to
416          * make them specific to who resolved the name.
417          */

418
419                 srcHashCode = request.addr.hashCode();
420                 for( int i = 0; i < response.addressArray.length; i++ ) {
421                     response.addressArray[i].hostName.srcHashCode = srcHashCode;
422                 }
423                 return response.addressArray;
424             }
425         }
426         throw new UnknownHostException JavaDoc( addr.hostName.name );
427     }
428 }
429
Popular Tags