KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jcifs > smb > SmbTransport


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

19
20 package jcifs.smb;
21
22 import java.io.*;
23 import java.net.*;
24 import java.util.*;
25
26 import jcifs.*;
27 import jcifs.netbios.*;
28 import jcifs.util.*;
29 import jcifs.util.transport.*;
30
31 public class SmbTransport extends Transport implements SmbConstants {
32
33     static final byte[] BUF = new byte[0xFFFF];
34     static final SmbComNegotiate NEGOTIATE_REQUEST = new SmbComNegotiate();
35     static LogStream log = LogStream.getInstance();
36
37     static synchronized SmbTransport getSmbTransport( UniAddress address, int port ) {
38         return getSmbTransport( address, port, LADDR, LPORT );
39     }
40     static synchronized SmbTransport getSmbTransport( UniAddress address, int port,
41                                     InetAddress localAddr, int localPort ) {
42         SmbTransport conn;
43
44         synchronized( CONNECTIONS ) {
45             if( SSN_LIMIT != 1 ) {
46                 ListIterator iter = CONNECTIONS.listIterator();
47                 while( iter.hasNext() ) {
48                     conn = (SmbTransport)iter.next();
49                     if( conn.matches( address, port, localAddr, localPort ) &&
50                             ( SSN_LIMIT == 0 || conn.sessions.size() < SSN_LIMIT )) {
51                         return conn;
52                     }
53                 }
54             }
55
56             conn = new SmbTransport( address, port, localAddr, localPort );
57             CONNECTIONS.add( 0, conn );
58         }
59
60         return conn;
61     }
62
63     class ServerData {
64         byte flags;
65         int flags2;
66         int maxMpxCount;
67         int maxBufferSize;
68         int sessionKey;
69         int capabilities;
70         String JavaDoc oemDomainName;
71         int securityMode;
72         int security;
73         boolean encryptedPasswords;
74         boolean signaturesEnabled;
75         boolean signaturesRequired;
76         int maxNumberVcs;
77         int maxRawSize;
78         long serverTime;
79         int serverTimeZone;
80         int encryptionKeyLength;
81         byte[] encryptionKey;
82     }
83
84     InetAddress localAddr;
85     int localPort;
86     UniAddress address;
87     Socket socket;
88     int port, mid;
89     OutputStream out;
90     InputStream in;
91     byte[] sbuf = new byte[255]; /* small local buffer */
92     SmbComBlankResponse key = new SmbComBlankResponse();
93     long sessionExpiration = System.currentTimeMillis() + SO_TIMEOUT;
94     LinkedList referrals = new LinkedList();
95     SigningDigest digest = null;
96     LinkedList sessions = new LinkedList();
97     ServerData server = new ServerData();
98     /* Negotiated values */
99     int flags2 = FLAGS2;
100     int maxMpxCount = MAX_MPX_COUNT;
101     int snd_buf_size = SND_BUF_SIZE;
102     int rcv_buf_size = RCV_BUF_SIZE;
103     int capabilities = CAPABILITIES;
104     int sessionKey = 0x00000000;
105     boolean useUnicode = USE_UNICODE;
106     String JavaDoc tconHostName;
107
108     SmbTransport( UniAddress address, int port, InetAddress localAddr, int localPort ) {
109         this.address = address;
110         this.port = port;
111         this.localAddr = localAddr;
112         this.localPort = localPort;
113     }
114
115     synchronized SmbSession getSmbSession() {
116         return getSmbSession( new NtlmPasswordAuthentication( null, null, null ));
117     }
118     synchronized SmbSession getSmbSession( NtlmPasswordAuthentication auth ) {
119         SmbSession ssn;
120         long now;
121
122         ListIterator iter = sessions.listIterator();
123         while( iter.hasNext() ) {
124             ssn = (SmbSession)iter.next();
125             if( ssn.matches( auth )) {
126                 ssn.auth = auth;
127                 return ssn;
128             }
129         }
130
131                                         /* logoff old sessions */
132         if (SO_TIMEOUT > 0 && sessionExpiration < (now = System.currentTimeMillis())) {
133             sessionExpiration = now + SO_TIMEOUT;
134             iter = sessions.listIterator();
135             while( iter.hasNext() ) {
136                 ssn = (SmbSession)iter.next();
137                 if( ssn.expiration < now ) {
138                     ssn.logoff( false );
139                 }
140             }
141         }
142
143         ssn = new SmbSession( address, port, localAddr, localPort, auth );
144         ssn.transport = this;
145         sessions.add( ssn );
146
147         return ssn;
148     }
149     boolean matches( UniAddress address, int port, InetAddress localAddr, int localPort ) {
150         return address.equals( this.address ) &&
151                     (port == 0 || port == this.port ||
152                             /* port 139 is ok if 445 was requested */
153                             (port == 445 && this.port == 139)) &&
154                     (localAddr == this.localAddr ||
155                             (localAddr != null &&
156                                     localAddr.equals( this.localAddr ))) &&
157                     localPort == this.localPort;
158     }
159     boolean hasCapability( int cap ) throws SmbException {
160         try {
161             connect( RESPONSE_TIMEOUT );
162         } catch( IOException ioe ) {
163             throw new SmbException( "", ioe );
164         }
165         return (capabilities & cap) == cap;
166     }
167     boolean isSignatureSetupRequired( NtlmPasswordAuthentication auth ) {
168         return ( flags2 & ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES ) != 0 &&
169                 digest == null &&
170                 auth != NtlmPasswordAuthentication.NULL &&
171                 NtlmPasswordAuthentication.NULL.equals( auth ) == false;
172     }
173
174     void ssn139() throws IOException {
175         Name calledName = new Name( address.firstCalledName(), 0x20, null );
176         do {
177             if (localAddr == null) {
178                 socket = new Socket( address.getHostAddress(), 139 );
179             } else {
180                 socket = new Socket( address.getHostAddress(), 139, localAddr, localPort );
181             }
182             socket.setSoTimeout( SO_TIMEOUT );
183             out = socket.getOutputStream();
184             in = socket.getInputStream();
185
186             SessionServicePacket ssp = new SessionRequestPacket( calledName,
187                     NbtAddress.getLocalName() );
188             out.write( sbuf, 0, ssp.writeWireFormat( sbuf, 0 ));
189             if (readn( in, sbuf, 0, 4 ) < 4) {
190                 throw new SmbException( "EOF during NetBIOS session request" );
191             }
192             switch( sbuf[0] & 0xFF ) {
193                 case SessionServicePacket.POSITIVE_SESSION_RESPONSE:
194                     if( log.level > 2 )
195                         log.println( "session established ok with " + address );
196                     return;
197                 case SessionServicePacket.NEGATIVE_SESSION_RESPONSE:
198                     int errorCode = (int)( in.read() & 0xFF );
199                     switch (errorCode) {
200                         case NbtException.CALLED_NOT_PRESENT:
201                         case NbtException.NOT_LISTENING_CALLED:
202                             socket.close();
203                             break;
204                         default:
205                             disconnect( true );
206                             throw new NbtException( NbtException.ERR_SSN_SRVC, errorCode );
207                     }
208                     break;
209                 case -1:
210                     disconnect( true );
211                     throw new NbtException( NbtException.ERR_SSN_SRVC,
212                             NbtException.CONNECTION_REFUSED );
213                 default:
214                     disconnect( true );
215                     throw new NbtException( NbtException.ERR_SSN_SRVC, 0 );
216             }
217         } while(( calledName.name = address.nextCalledName()) != null );
218
219         throw new IOException( "Failed to establish session with " + address );
220     }
221     private void negotiate( int port, ServerMessageBlock resp ) throws IOException {
222         /* We cannot use Transport.sendrecv() yet because
223          * the Transport thread is not setup until doConnect()
224          * returns and we want to supress all communication
225          * until we have properly negotiated.
226          */

227         synchronized (sbuf) {
228             /* If jcifs.netbios.hostname is set this *probably* means there
229              * is a policy regarding which hosts a user can connect from. This
230              * requires communicating over port 139 rather than 445.
231              */

232             if (NETBIOS_HOSTNAME != null && NETBIOS_HOSTNAME.equals( "" ) == false) {
233                 port = 139;
234             }
235             if (port == 139) {
236                 ssn139();
237             } else {
238                 if (port == 0)
239                     port = DEFAULT_PORT; // 445
240
if (localAddr == null) {
241                     socket = new Socket( address.getHostAddress(), port );
242                 } else {
243                     socket = new Socket( address.getHostAddress(), port, localAddr, localPort );
244                 }
245                 socket.setSoTimeout( SO_TIMEOUT );
246                 out = socket.getOutputStream();
247                 in = socket.getInputStream();
248             }
249
250             if (++mid == 32000) mid = 1;
251             NEGOTIATE_REQUEST.mid = mid;
252             int n = NEGOTIATE_REQUEST.encode( sbuf, 4 );
253             Encdec.enc_uint32be( n & 0xFFFF, sbuf, 0 ); /* 4 byte ssn msg header */
254             out.write( sbuf, 0, 4 + n );
255             out.flush();
256             /* Note the Transport thread isn't running yet so we can
257              * read from the socket here.
258              */

259             if (peekKey() == null) /* try to read header */
260                 throw new IOException( "transport closed in negotiate" );
261             int size = Encdec.dec_uint16be( sbuf, 2 );
262             if (size < 33 || (4 + size) > sbuf.length ) {
263                 throw new IOException( "Invalid payload size: " + size );
264             }
265             readn( in, sbuf, 4 + 32, size - 32 );
266             resp.decode( sbuf, 4 );
267         }
268     }
269     public void connect() throws SmbException {
270         try {
271             super.connect( RESPONSE_TIMEOUT );
272         } catch( TransportException te ) {
273             throw new SmbException( "", te );
274         }
275     }
276     protected void doConnect() throws IOException {
277         /*
278          * Negotiate Protocol Request / Response
279          */

280
281         SmbComNegotiateResponse resp = new SmbComNegotiateResponse( server );
282         try {
283             negotiate( port, resp );
284         } catch( ConnectException ce ) {
285             port = (port == 0 || port == DEFAULT_PORT) ? 139 : DEFAULT_PORT;
286             negotiate( port, resp );
287         }
288
289         if( resp.dialectIndex > 10 ) {
290             throw new SmbException( "This client does not support the negotiated dialect." );
291         }
292
293         /* Adjust negotiated values */
294
295         tconHostName = address.getHostName();
296         if (server.signaturesRequired || (server.signaturesEnabled && SIGNPREF)) {
297             flags2 |= ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES;
298         } else {
299             flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES;
300         }
301         maxMpxCount = Math.min( maxMpxCount, server.maxMpxCount );
302         if (maxMpxCount < 1) maxMpxCount = 1;
303         snd_buf_size = Math.min( snd_buf_size, server.maxBufferSize );
304         capabilities &= server.capabilities;
305         if ((capabilities & ServerMessageBlock.CAP_UNICODE) == 0) {
306             // server doesn't want unicode
307
if (FORCE_UNICODE) {
308                 capabilities |= ServerMessageBlock.CAP_UNICODE;
309             } else {
310                 useUnicode = false;
311                 flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_UNICODE;
312             }
313         }
314     }
315     protected void doDisconnect( boolean hard ) throws IOException {
316         ListIterator iter = sessions.listIterator();
317         while (iter.hasNext()) {
318             SmbSession ssn = (SmbSession)iter.next();
319             ssn.logoff( hard );
320         }
321         socket.shutdownOutput();
322         out.close();
323         in.close();
324         socket.close();
325         digest = null;
326     }
327
328     protected void makeKey( Request request ) throws IOException {
329         /* The request *is* the key */
330         if (++mid == 32000) mid = 1;
331         ((ServerMessageBlock)request).mid = mid;
332     }
333     protected Request peekKey() throws IOException {
334         int n;
335         do {
336             if ((n = readn( in, sbuf, 0, 4 )) < 4)
337                 return null;
338         } while (sbuf[0] == (byte)0x85); /* Dodge NetBIOS keep-alive */
339                                                    /* read smb header */
340         if ((n = readn( in, sbuf, 4, 32 )) < 32)
341             return null;
342         if (log.level > 2) {
343             log.println( "New data read: " + this );
344             jcifs.util.Hexdump.hexdump( log, sbuf, 4, 32 );
345         }
346
347         for ( ;; ) {
348             /* 01234567
349              * 00SSFSMB
350              * 0 - 0's
351              * S - size of payload
352              * FSMB - 0xFF SMB magic #
353              */

354
355             if (sbuf[0] == (byte)0x00 &&
356                         sbuf[1] == (byte)0x00 &&
357                         sbuf[4] == (byte)0xFF &&
358                         sbuf[5] == (byte)'S' &&
359                         sbuf[6] == (byte)'M' &&
360                         sbuf[7] == (byte)'B') {
361                 break; /* all good */
362             }
363                                         /* out of phase maybe? */
364                           /* inch forward 1 byte and try again */
365             for (int i = 0; i < 35; i++) {
366                 sbuf[i] = sbuf[i + 1];
367             }
368             int b;
369             if ((b = in.read()) == -1) return null;
370             sbuf[35] = (byte)b;
371         }
372
373         key.mid = Encdec.dec_uint16le( sbuf, 34 );
374
375         /* Unless key returned is null or invalid Transport.loop() always
376          * calls doRecv() after and no one else but the transport thread
377          * should call doRecv(). Therefore it is ok to expect that the data
378          * in sbuf will be preserved for copying into BUF in doRecv().
379          */

380
381         return key;
382     }
383
384     protected void doSend( Request request ) throws IOException {
385         synchronized (BUF) {
386             ServerMessageBlock smb = (ServerMessageBlock)request;
387             int n = smb.encode( BUF, 4 );
388             Encdec.enc_uint32be( n & 0xFFFF, BUF, 0 ); /* 4 byte session message header */
389             if (log.level > 3) {
390                 do {
391                     log.println( smb );
392                 } while (smb instanceof AndXServerMessageBlock &&
393                         (smb = ((AndXServerMessageBlock)smb).andx) != null);
394                 if (log.level > 5) {
395                     Hexdump.hexdump( log, BUF, 4, n );
396                 }
397             }
398             out.write( BUF, 0, 4 + n );
399         }
400     }
401     protected void doSend0( Request request ) throws IOException {
402         try {
403             doSend( request );
404         } catch( IOException ioe ) {
405             if (log.level > 2)
406                 ioe.printStackTrace( log );
407             try {
408                 disconnect( true );
409             } catch( IOException ioe2 ) {
410                 ioe2.printStackTrace( log );
411             }
412             throw ioe;
413         }
414     }
415
416     protected void doRecv( Response response ) throws IOException {
417         ServerMessageBlock resp = (ServerMessageBlock)response;
418         resp.useUnicode = useUnicode;
419
420         synchronized (BUF) {
421             System.arraycopy( sbuf, 0, BUF, 0, 4 + HEADER_LENGTH );
422             int size = Encdec.dec_uint16be( BUF, 2 );
423             if (size < (HEADER_LENGTH + 1) || (4 + size) > rcv_buf_size ) {
424                 throw new IOException( "Invalid payload size: " + size );
425             }
426             if (resp.command == ServerMessageBlock.SMB_COM_READ_ANDX) {
427                 SmbComReadAndXResponse r = (SmbComReadAndXResponse)resp;
428                 int off = HEADER_LENGTH;
429                                     /* WordCount thru dataOffset always 27 */
430                 readn( in, BUF, 4 + off, 27 ); off += 27;
431                 resp.decode( BUF, 4 );
432                 if (r.dataLength > 0) {
433                     readn( in, BUF, 4 + off, r.dataOffset - off); /* pad */
434                     readn( in, r.b, r.off, r.dataLength ); /* read direct */
435                 }
436             } else {
437                 readn( in, BUF, 4 + 32, size - 32 );
438                 resp.decode( BUF, 4 );
439                 if (resp instanceof SmbComTransactionResponse) {
440                     ((SmbComTransactionResponse)resp).nextElement();
441                 }
442             }
443
444             /* Verification fails (w/ W2K3 server at least) if status is not 0. This
445              * suggests MS doesn't compute the signature (correctly) for error responses
446              * (perhaps for DOS reasons).
447              */

448             if (digest != null && resp.errorCode == 0) {
449                 digest.verify( BUF, 4, resp );
450             }
451         }
452     }
453     protected void doSkip() throws IOException {
454         int size = Encdec.dec_uint16be( sbuf, 2 );
455         if (size < 33 || (4 + size) > rcv_buf_size ) {
456             /* log message? */
457             in.skip( in.available() );
458         } else {
459             in.skip( size - 32 );
460         }
461     }
462     void checkStatus( ServerMessageBlock req, ServerMessageBlock resp ) throws SmbException {
463         resp.errorCode = SmbException.getStatusByCode( resp.errorCode );
464         switch( resp.errorCode ) {
465             case NtStatus.NT_STATUS_OK:
466                 break;
467             case NtStatus.NT_STATUS_ACCESS_DENIED:
468             case NtStatus.NT_STATUS_WRONG_PASSWORD:
469             case NtStatus.NT_STATUS_LOGON_FAILURE:
470             case NtStatus.NT_STATUS_ACCOUNT_RESTRICTION:
471             case NtStatus.NT_STATUS_INVALID_LOGON_HOURS:
472             case NtStatus.NT_STATUS_INVALID_WORKSTATION:
473             case NtStatus.NT_STATUS_PASSWORD_EXPIRED:
474             case NtStatus.NT_STATUS_ACCOUNT_DISABLED:
475             case NtStatus.NT_STATUS_ACCOUNT_LOCKED_OUT:
476             case NtStatus.NT_STATUS_TRUSTED_DOMAIN_FAILURE:
477                 throw new SmbAuthException( resp.errorCode );
478             case NtStatus.NT_STATUS_PATH_NOT_COVERED:
479                 if( req.auth == null ) {
480                     throw new SmbException( resp.errorCode, null );
481                 }
482                 DfsReferral dr = getDfsReferral( req.auth, req.path );
483                 referrals.add( dr );
484                 throw dr;
485             case 0x80000005: /* STATUS_BUFFER_OVERFLOW */
486                 break; /* normal for DCERPC named pipes */
487             default:
488                 throw new SmbException( resp.errorCode, null );
489         }
490         if (resp.verifyFailed) {
491             throw new SmbException( "Signature verification failed." );
492         }
493     }
494     void send( ServerMessageBlock request, ServerMessageBlock response ) throws SmbException {
495
496         connect(); /* must negotiate before we can test flags2, useUnicode, etc */
497
498         request.flags2 |= flags2;
499         request.useUnicode = useUnicode;
500         request.response = response; /* needed by sign */
501         if (request.digest == null)
502             request.digest = digest; /* for sign called in encode */
503
504         try {
505             if (response == null) {
506                 doSend0( request );
507                 return;
508             } else if (request instanceof SmbComTransaction) {
509                 response.command = request.command;
510                 SmbComTransaction req = (SmbComTransaction)request;
511                 SmbComTransactionResponse resp = (SmbComTransactionResponse)response;
512
513                 req.maxBufferSize = snd_buf_size;
514                 resp.reset();
515
516                 try {
517                     BufferCache.getBuffers( req, resp );
518
519                     /*
520                      * First request w/ interim response
521                      */

522
523                     req.nextElement();
524                     if (req.hasMoreElements()) {
525                         SmbComBlankResponse interim = new SmbComBlankResponse();
526                         super.sendrecv( req, interim, RESPONSE_TIMEOUT );
527                         if (interim.errorCode != 0) {
528                             checkStatus( req, interim );
529                         }
530                         req.nextElement();
531                     } else {
532                         makeKey( req );
533                     }
534
535                     synchronized (response_map) {
536                         response.received = false;
537                         resp.isReceived = false;
538                         try {
539                             response_map.put( req, resp );
540
541                             /*
542                              * Send multiple fragments
543                              */

544
545                             do {
546                                 doSend0( req );
547                             } while( req.hasMoreElements() && req.nextElement() != null );
548
549                             /*
550                              * Receive multiple fragments
551                              */

552
553                             long timeout = RESPONSE_TIMEOUT;
554                             resp.expiration = System.currentTimeMillis() + timeout;
555                             while( resp.hasMoreElements() ) {
556                                 response_map.wait( timeout );
557                                 timeout = resp.expiration - System.currentTimeMillis();
558                                 if (timeout <= 0) {
559                                     throw new TransportException( this +
560                                             " timedout waiting for response to " +
561                                             req );
562                                 }
563                             }
564                             if (response.errorCode != 0) {
565                                 checkStatus( req, resp );
566                             }
567                         } catch( InterruptedException JavaDoc ie ) {
568                             throw new TransportException( ie );
569                         } finally {
570                             response_map.remove( req );
571                         }
572                     }
573                 } finally {
574                     BufferCache.releaseBuffer( req.txn_buf );
575                     BufferCache.releaseBuffer( resp.txn_buf );
576                 }
577
578             } else {
579                 response.command = request.command;
580                 super.sendrecv( request, response, RESPONSE_TIMEOUT );
581             }
582         } catch( SmbException se ) {
583             throw se;
584         } catch( IOException ioe ) {
585             throw new SmbException( "", ioe );
586         }
587
588         checkStatus( request, response );
589     }
590     public String JavaDoc toString() {
591         return super.toString() + "[" + address + ":" + port + "]";
592     }
593
594     /* DFS */
595
596     DfsReferral getDfsReferral( NtlmPasswordAuthentication auth,
597                 String JavaDoc path ) throws SmbException {
598         String JavaDoc subpath, node, host;
599         DfsReferral dr = new DfsReferral();
600         int p, n, i, s;
601         UniAddress addr;
602
603         SmbTree ipc = getSmbSession( auth ).getSmbTree( "IPC$", null );
604         Trans2GetDfsReferralResponse resp = new Trans2GetDfsReferralResponse();
605         ipc.send( new Trans2GetDfsReferral( path ), resp );
606
607         subpath = path.substring( 0, resp.pathConsumed );
608         node = resp.referral.node;
609         if( subpath.charAt( 0 ) != '\\' ||
610                 (i = subpath.indexOf( '\\', 1 )) < 2 ||
611                 (p = subpath.indexOf( '\\', i + 1 )) < (i + 2) ||
612                 node.charAt( 0 ) != '\\' ||
613                 (s = node.indexOf( '\\', 1 )) < 2) {
614             throw new SmbException( "Invalid DFS path: " + path );
615         }
616         if ((n = node.indexOf( '\\', s + 1 )) == -1) {
617             n = node.length();
618         }
619
620         dr.path = subpath.substring( p );
621         dr.node = node.substring( 0, n );
622         dr.nodepath = node.substring( n );
623         dr.server = node.substring( 1, s );
624         dr.share = node.substring( s + 1, n );
625         dr.resolveHashes = auth.hashesExternal;
626                         /* NTLM HTTP Authentication must be re-negotiated
627                          * with challenge from 'server' to access DFS vol. */

628         return dr;
629     }
630     DfsReferral lookupReferral( String JavaDoc unc ) {
631         synchronized( referrals ) {
632             DfsReferral dr;
633             ListIterator iter = referrals.listIterator();
634             int i, len;
635
636             while( iter.hasNext() ) {
637                 dr = (DfsReferral)iter.next();
638                 len = dr.path.length();
639                 for( i = 0; i < len && i < unc.length(); i++ ) {
640                     if( dr.path.charAt( i ) != unc.charAt( i )) {
641                         break;
642                     }
643                 }
644                 if( i == len && (len == unc.length() || unc.charAt( len ) == '\\')) {
645                     return dr;
646                 }
647             }
648         }
649
650         return null;
651     }
652 }
653
654
Popular Tags