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 import jcifs.dcerpc.*;
31 import jcifs.dcerpc.msrpc.*;
32 
33 public class SmbTransport extends Transport implements SmbConstants {
34 
35     static final byte[] BUF = new byte[0xFFFF];
36     static final SmbComNegotiate NEGOTIATE_REQUEST = new SmbComNegotiate();
37     static LogStream log = LogStream.getInstance();
38     static HashMap dfsRoots = null;
39 
getSmbTransport( UniAddress address, int port )40     static synchronized SmbTransport getSmbTransport( UniAddress address, int port ) {
41         return getSmbTransport( address, port, LADDR, LPORT, null );
42     }
getSmbTransport( UniAddress address, int port, InetAddress localAddr, int localPort, String hostName )43     static synchronized SmbTransport getSmbTransport( UniAddress address, int port,
44                                     InetAddress localAddr, int localPort, String hostName ) {
45         SmbTransport conn;
46 
47         synchronized( CONNECTIONS ) {
48             if( SSN_LIMIT != 1 ) {
49                 ListIterator iter = CONNECTIONS.listIterator();
50                 while( iter.hasNext() ) {
51                     conn = (SmbTransport)iter.next();
52                     if( conn.matches( address, port, localAddr, localPort, hostName ) &&
53                             ( SSN_LIMIT == 0 || conn.sessions.size() < SSN_LIMIT )) {
54                         return conn;
55                     }
56                 }
57             }
58 
59             conn = new SmbTransport( address, port, localAddr, localPort );
60             CONNECTIONS.add( 0, conn );
61         }
62 
63         return conn;
64     }
65 
66     class ServerData {
67         byte flags;
68         int flags2;
69         int maxMpxCount;
70         int maxBufferSize;
71         int sessionKey;
72         int capabilities;
73         String oemDomainName;
74         int securityMode;
75         int security;
76         boolean encryptedPasswords;
77         boolean signaturesEnabled;
78         boolean signaturesRequired;
79         int maxNumberVcs;
80         int maxRawSize;
81         long serverTime;
82         int serverTimeZone;
83         int encryptionKeyLength;
84         byte[] encryptionKey;
85         byte[] guid;
86     }
87 
88     InetAddress localAddr;
89     int localPort;
90     UniAddress address;
91     Socket socket;
92     int port, mid;
93     OutputStream out;
94     InputStream in;
95     byte[] sbuf = new byte[512]; /* small local buffer */
96     SmbComBlankResponse key = new SmbComBlankResponse();
97     long sessionExpiration = System.currentTimeMillis() + SO_TIMEOUT;
98     LinkedList referrals = new LinkedList();
99     SigningDigest digest = null;
100     LinkedList sessions = new LinkedList();
101     ServerData server = new ServerData();
102     /* Negotiated values */
103     int flags2 = FLAGS2;
104     int maxMpxCount = MAX_MPX_COUNT;
105     int snd_buf_size = SND_BUF_SIZE;
106     int rcv_buf_size = RCV_BUF_SIZE;
107     int capabilities = CAPABILITIES;
108     int sessionKey = 0x00000000;
109     boolean useUnicode = USE_UNICODE;
110     String tconHostName = null;
111 
SmbTransport( UniAddress address, int port, InetAddress localAddr, int localPort )112     SmbTransport( UniAddress address, int port, InetAddress localAddr, int localPort ) {
113         this.address = address;
114         this.port = port;
115         this.localAddr = localAddr;
116         this.localPort = localPort;
117     }
118 
getSmbSession()119     synchronized SmbSession getSmbSession() {
120         return getSmbSession( new NtlmPasswordAuthentication( null, null, null ));
121     }
getSmbSession( NtlmPasswordAuthentication auth )122     synchronized SmbSession getSmbSession( NtlmPasswordAuthentication auth ) {
123         SmbSession ssn;
124         long now;
125 
126         ListIterator iter = sessions.listIterator();
127         while( iter.hasNext() ) {
128             ssn = (SmbSession)iter.next();
129             if( ssn.matches( auth )) {
130                 ssn.auth = auth;
131                 return ssn;
132             }
133         }
134 
135                                         /* logoff old sessions */
136         if (SO_TIMEOUT > 0 && sessionExpiration < (now = System.currentTimeMillis())) {
137             sessionExpiration = now + SO_TIMEOUT;
138             iter = sessions.listIterator();
139             while( iter.hasNext() ) {
140                 ssn = (SmbSession)iter.next();
141                 if( ssn.expiration < now ) {
142                     ssn.logoff( false );
143                 }
144             }
145         }
146 
147         ssn = new SmbSession( address, port, localAddr, localPort, auth );
148         ssn.transport = this;
149         sessions.add( ssn );
150 
151         return ssn;
152     }
matches( UniAddress address, int port, InetAddress localAddr, int localPort, String hostName )153     boolean matches( UniAddress address, int port, InetAddress localAddr, int localPort, String hostName ) {
154         if (hostName == null)
155             hostName = address.getHostName();
156         return (this.tconHostName == null || hostName.equalsIgnoreCase(this.tconHostName)) &&
157                     address.equals( this.address ) &&
158                     (port == 0 || port == this.port ||
159                             /* port 139 is ok if 445 was requested */
160                             (port == 445 && this.port == 139)) &&
161                     (localAddr == this.localAddr ||
162                             (localAddr != null &&
163                                     localAddr.equals( this.localAddr ))) &&
164                     localPort == this.localPort;
165     }
hasCapability( int cap )166     boolean hasCapability( int cap ) throws SmbException {
167         try {
168             connect( RESPONSE_TIMEOUT );
169         } catch( IOException ioe ) {
170             throw new SmbException( ioe.getMessage(), ioe );
171         }
172         return (capabilities & cap) == cap;
173     }
isSignatureSetupRequired( NtlmPasswordAuthentication auth )174     boolean isSignatureSetupRequired( NtlmPasswordAuthentication auth ) {
175         return ( flags2 & ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES ) != 0 &&
176                 digest == null &&
177                 auth != NtlmPasswordAuthentication.NULL &&
178                 NtlmPasswordAuthentication.NULL.equals( auth ) == false;
179     }
180 
ssn139()181     void ssn139() throws IOException {
182         Name calledName = new Name( address.firstCalledName(), 0x20, null );
183         do {
184 /* These Socket constructors attempt to connect before SO_TIMEOUT can be applied
185             if (localAddr == null) {
186                 socket = new Socket( address.getHostAddress(), 139 );
187             } else {
188                 socket = new Socket( address.getHostAddress(), 139, localAddr, localPort );
189             }
190             socket.setSoTimeout( SO_TIMEOUT );
191 */
192 
193             socket = new Socket();
194             if (localAddr != null)
195                 socket.bind(new InetSocketAddress(localAddr, localPort));
196             socket.connect(new InetSocketAddress(address.getHostAddress(), 139), CONN_TIMEOUT);
197             socket.setSoTimeout( SO_TIMEOUT );
198 
199             out = socket.getOutputStream();
200             in = socket.getInputStream();
201 
202             SessionServicePacket ssp = new SessionRequestPacket( calledName,
203                     NbtAddress.getLocalName() );
204             out.write( sbuf, 0, ssp.writeWireFormat( sbuf, 0 ));
205             if (readn( in, sbuf, 0, 4 ) < 4) {
206                 try {
207                     socket.close();
208                 } catch(IOException ioe) {
209                 }
210                 throw new SmbException( "EOF during NetBIOS session request" );
211             }
212             switch( sbuf[0] & 0xFF ) {
213                 case SessionServicePacket.POSITIVE_SESSION_RESPONSE:
214                     if( log.level >= 4 )
215                         log.println( "session established ok with " + address );
216                     return;
217                 case SessionServicePacket.NEGATIVE_SESSION_RESPONSE:
218                     int errorCode = (int)( in.read() & 0xFF );
219                     switch (errorCode) {
220                         case NbtException.CALLED_NOT_PRESENT:
221                         case NbtException.NOT_LISTENING_CALLED:
222                             socket.close();
223                             break;
224                         default:
225                             disconnect( true );
226                             throw new NbtException( NbtException.ERR_SSN_SRVC, errorCode );
227                     }
228                     break;
229                 case -1:
230                     disconnect( true );
231                     throw new NbtException( NbtException.ERR_SSN_SRVC,
232                             NbtException.CONNECTION_REFUSED );
233                 default:
234                     disconnect( true );
235                     throw new NbtException( NbtException.ERR_SSN_SRVC, 0 );
236             }
237         } while(( calledName.name = address.nextCalledName()) != null );
238 
239         throw new IOException( "Failed to establish session with " + address );
240     }
negotiate( int port, ServerMessageBlock resp )241     private void negotiate( int port, ServerMessageBlock resp ) throws IOException {
242         /* We cannot use Transport.sendrecv() yet because
243          * the Transport thread is not setup until doConnect()
244          * returns and we want to supress all communication
245          * until we have properly negotiated.
246          */
247         synchronized (sbuf) {
248             if (port == 139) {
249                 ssn139();
250             } else {
251                 if (port == 0)
252                     port = DEFAULT_PORT; // 445
253 /* These Socket constructors attempt to connect before SO_TIMEOUT can be applied
254                 if (localAddr == null) {
255                     socket = new Socket( address.getHostAddress(), port );
256                 } else {
257                     socket = new Socket( address.getHostAddress(), port, localAddr, localPort );
258                 }
259                 socket.setSoTimeout( SO_TIMEOUT );
260 */
261                 socket = new Socket();
262                 if (localAddr != null)
263                     socket.bind(new InetSocketAddress(localAddr, localPort));
264                 socket.connect(new InetSocketAddress(address.getHostAddress(), port), CONN_TIMEOUT);
265                 socket.setSoTimeout( SO_TIMEOUT );
266 
267                 out = socket.getOutputStream();
268                 in = socket.getInputStream();
269             }
270 
271             if (++mid == 32000) mid = 1;
272             NEGOTIATE_REQUEST.mid = mid;
273             int n = NEGOTIATE_REQUEST.encode( sbuf, 4 );
274             Encdec.enc_uint32be( n & 0xFFFF, sbuf, 0 ); /* 4 byte ssn msg header */
275 
276             if (log.level >= 4) {
277                 log.println( NEGOTIATE_REQUEST );
278                 if (log.level >= 6) {
279                     Hexdump.hexdump( log, sbuf, 4, n );
280                 }
281             }
282 
283             out.write( sbuf, 0, 4 + n );
284             out.flush();
285             /* Note the Transport thread isn't running yet so we can
286              * read from the socket here.
287              */
288             if (peekKey() == null) /* try to read header */
289                 throw new IOException( "transport closed in negotiate" );
290             int size = Encdec.dec_uint16be( sbuf, 2 ) & 0xFFFF;
291             if (size < 33 || (4 + size) > sbuf.length ) {
292                 throw new IOException( "Invalid payload size: " + size );
293             }
294             readn( in, sbuf, 4 + 32, size - 32 );
295             resp.decode( sbuf, 4 );
296 
297             if (log.level >= 4) {
298                 log.println( resp );
299                 if (log.level >= 6) {
300                     Hexdump.hexdump( log, sbuf, 4, n );
301                 }
302             }
303         }
304     }
connect()305     public void connect() throws SmbException {
306         try {
307             super.connect( RESPONSE_TIMEOUT );
308         } catch( TransportException te ) {
309             throw new SmbException( "Failed to connect: " + address, te );
310         }
311     }
doConnect()312     protected void doConnect() throws IOException {
313         /*
314          * Negotiate Protocol Request / Response
315          */
316 
317         SmbComNegotiateResponse resp = new SmbComNegotiateResponse( server );
318         try {
319             negotiate( port, resp );
320         } catch( ConnectException ce ) {
321             port = (port == 0 || port == DEFAULT_PORT) ? 139 : DEFAULT_PORT;
322             negotiate( port, resp );
323         } catch( NoRouteToHostException nr ) {
324             port = (port == 0 || port == DEFAULT_PORT) ? 139 : DEFAULT_PORT;
325             negotiate( port, resp );
326         }
327 
328         if( resp.dialectIndex > 10 ) {
329             throw new SmbException( "This client does not support the negotiated dialect." );
330         }
331         if ((server.capabilities & CAP_EXTENDED_SECURITY) != CAP_EXTENDED_SECURITY &&
332                     server.encryptionKeyLength != 8 &&
333                     LM_COMPATIBILITY == 0) {
334             throw new SmbException("Unexpected encryption key length: " + server.encryptionKeyLength);
335         }
336 
337         /* Adjust negotiated values */
338 
339         tconHostName = address.getHostName();
340         if (server.signaturesRequired || (server.signaturesEnabled && SIGNPREF)) {
341             flags2 |= ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES;
342         } else {
343             flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES;
344         }
345         maxMpxCount = Math.min( maxMpxCount, server.maxMpxCount );
346         if (maxMpxCount < 1) maxMpxCount = 1;
347         snd_buf_size = Math.min( snd_buf_size, server.maxBufferSize );
348         capabilities &= server.capabilities;
349         if ((server.capabilities & CAP_EXTENDED_SECURITY) == CAP_EXTENDED_SECURITY)
350             capabilities |= CAP_EXTENDED_SECURITY; // & doesn't copy high bit
351 
352         if ((capabilities & ServerMessageBlock.CAP_UNICODE) == 0) {
353             // server doesn't want unicode
354             if (FORCE_UNICODE) {
355                 capabilities |= ServerMessageBlock.CAP_UNICODE;
356             } else {
357                 useUnicode = false;
358                 flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_UNICODE;
359             }
360         }
361     }
doDisconnect( boolean hard )362     protected void doDisconnect( boolean hard ) throws IOException {
363         ListIterator iter = sessions.listIterator();
364         try {
365             while (iter.hasNext()) {
366                 SmbSession ssn = (SmbSession)iter.next();
367                 ssn.logoff( hard );
368             }
369             socket.shutdownOutput();
370             out.close();
371             in.close();
372             socket.close();
373         } finally {
374             digest = null;
375             socket = null;
376             tconHostName = null;
377         }
378     }
379 
makeKey( Request request )380     protected void makeKey( Request request ) throws IOException {
381         /* The request *is* the key */
382         if (++mid == 32000) mid = 1;
383         ((ServerMessageBlock)request).mid = mid;
384     }
peekKey()385     protected Request peekKey() throws IOException {
386         int n;
387         do {
388             if ((n = readn( in, sbuf, 0, 4 )) < 4)
389                 return null;
390         } while (sbuf[0] == (byte)0x85);  /* Dodge NetBIOS keep-alive */
391                                                    /* read smb header */
392         if ((n = readn( in, sbuf, 4, 32 )) < 32)
393             return null;
394         if (log.level >= 4) {
395             log.println( "New data read: " + this );
396             jcifs.util.Hexdump.hexdump( log, sbuf, 4, 32 );
397         }
398 
399         for ( ;; ) {
400             /* 01234567
401              * 00SSFSMB
402              * 0 - 0's
403              * S - size of payload
404              * FSMB - 0xFF SMB magic #
405              */
406 
407             if (sbuf[0] == (byte)0x00 &&
408                         sbuf[1] == (byte)0x00 &&
409                         sbuf[4] == (byte)0xFF &&
410                         sbuf[5] == (byte)'S' &&
411                         sbuf[6] == (byte)'M' &&
412                         sbuf[7] == (byte)'B') {
413                 break; /* all good */
414             }
415                                         /* out of phase maybe? */
416                           /* inch forward 1 byte and try again */
417             for (int i = 0; i < 35; i++) {
418                 sbuf[i] = sbuf[i + 1];
419             }
420             int b;
421             if ((b = in.read()) == -1) return null;
422             sbuf[35] = (byte)b;
423         }
424 
425         key.mid = Encdec.dec_uint16le( sbuf, 34 ) & 0xFFFF;
426 
427         /* Unless key returned is null or invalid Transport.loop() always
428          * calls doRecv() after and no one else but the transport thread
429          * should call doRecv(). Therefore it is ok to expect that the data
430          * in sbuf will be preserved for copying into BUF in doRecv().
431          */
432 
433         return key;
434     }
435 
doSend( Request request )436     protected void doSend( Request request ) throws IOException {
437         synchronized (BUF) {
438             ServerMessageBlock smb = (ServerMessageBlock)request;
439             int n = smb.encode( BUF, 4 );
440             Encdec.enc_uint32be( n & 0xFFFF, BUF, 0 ); /* 4 byte session message header */
441             if (log.level >= 4) {
442                 do {
443                     log.println( smb );
444                 } while (smb instanceof AndXServerMessageBlock &&
445                         (smb = ((AndXServerMessageBlock)smb).andx) != null);
446                 if (log.level >= 6) {
447                     Hexdump.hexdump( log, BUF, 4, n );
448                 }
449             }
450             /* For some reason this can sometimes get broken up into another
451              * "NBSS Continuation Message" frame according to WireShark
452              */
453             out.write( BUF, 0, 4 + n );
454         }
455     }
doSend0( Request request )456     protected void doSend0( Request request ) throws IOException {
457         try {
458             doSend( request );
459         } catch( IOException ioe ) {
460             if (log.level > 2)
461                 ioe.printStackTrace( log );
462             try {
463                 disconnect( true );
464             } catch( IOException ioe2 ) {
465                 ioe2.printStackTrace( log );
466             }
467             throw ioe;
468         }
469     }
470 
doRecv( Response response )471     protected void doRecv( Response response ) throws IOException {
472         ServerMessageBlock resp = (ServerMessageBlock)response;
473         resp.useUnicode = useUnicode;
474         resp.extendedSecurity = (capabilities & CAP_EXTENDED_SECURITY) == CAP_EXTENDED_SECURITY;
475 
476         synchronized (BUF) {
477             System.arraycopy( sbuf, 0, BUF, 0, 4 + HEADER_LENGTH );
478             int size = Encdec.dec_uint16be( BUF, 2 ) & 0xFFFF;
479             if (size < (HEADER_LENGTH + 1) || (4 + size) > rcv_buf_size ) {
480                 throw new IOException( "Invalid payload size: " + size );
481             }
482             int errorCode = Encdec.dec_uint32le( BUF, 9 ) & 0xFFFFFFFF;
483             if (resp.command == ServerMessageBlock.SMB_COM_READ_ANDX &&
484                         (errorCode == 0 ||
485                         errorCode == 0x80000005)) { // overflow indicator normal for pipe
486                 SmbComReadAndXResponse r = (SmbComReadAndXResponse)resp;
487                 int off = HEADER_LENGTH;
488                                     /* WordCount thru dataOffset always 27 */
489                 readn( in, BUF, 4 + off, 27 ); off += 27;
490                 resp.decode( BUF, 4 );
491                                               /* EMC can send pad w/o data */
492                 int pad = r.dataOffset - off;
493                 if (r.byteCount > 0 && pad > 0 && pad < 4)
494                     readn( in, BUF, 4 + off, pad);
495 
496                 if (r.dataLength > 0)
497                     readn( in, r.b, r.off, r.dataLength );  /* read direct */
498             } else {
499                 readn( in, BUF, 4 + 32, size - 32 );
500                 resp.decode( BUF, 4 );
501                 if (resp instanceof SmbComTransactionResponse) {
502                     ((SmbComTransactionResponse)resp).nextElement();
503                 }
504             }
505 
506             /* Verification fails (w/ W2K3 server at least) if status is not 0. This
507              * suggests MS doesn't compute the signature (correctly) for error responses
508              * (perhaps for DOS reasons).
509              */
510             if (digest != null && resp.errorCode == 0) {
511                 digest.verify( BUF, 4, resp );
512             }
513 
514             if (log.level >= 4) {
515                 log.println( response );
516                 if (log.level >= 6) {
517                     Hexdump.hexdump( log, BUF, 4, size );
518                 }
519             }
520         }
521     }
doSkip()522     protected void doSkip() throws IOException {
523         int size = Encdec.dec_uint16be( sbuf, 2 ) & 0xFFFF;
524         if (size < 33 || (4 + size) > rcv_buf_size ) {
525             /* log message? */
526             in.skip( in.available() );
527         } else {
528             in.skip( size - 32 );
529         }
530     }
checkStatus( ServerMessageBlock req, ServerMessageBlock resp )531     void checkStatus( ServerMessageBlock req, ServerMessageBlock resp ) throws SmbException {
532         resp.errorCode = SmbException.getStatusByCode( resp.errorCode );
533         switch( resp.errorCode ) {
534             case NtStatus.NT_STATUS_OK:
535                 break;
536             case NtStatus.NT_STATUS_ACCESS_DENIED:
537             case NtStatus.NT_STATUS_WRONG_PASSWORD:
538             case NtStatus.NT_STATUS_LOGON_FAILURE:
539             case NtStatus.NT_STATUS_ACCOUNT_RESTRICTION:
540             case NtStatus.NT_STATUS_INVALID_LOGON_HOURS:
541             case NtStatus.NT_STATUS_INVALID_WORKSTATION:
542             case NtStatus.NT_STATUS_PASSWORD_EXPIRED:
543             case NtStatus.NT_STATUS_ACCOUNT_DISABLED:
544             case NtStatus.NT_STATUS_ACCOUNT_LOCKED_OUT:
545             case NtStatus.NT_STATUS_TRUSTED_DOMAIN_FAILURE:
546                 throw new SmbAuthException( resp.errorCode );
547             case NtStatus.NT_STATUS_PATH_NOT_COVERED:
548                 if( req.auth == null ) {
549                     throw new SmbException( resp.errorCode, null );
550                 }
551 
552                 DfsReferral dr = getDfsReferrals(req.auth, req.path, 1);
553                 if (dr == null)
554                     throw new SmbException(resp.errorCode, null);
555 
556                 SmbFile.dfs.insert(req.path, dr);
557                 throw dr;
558             case 0x80000005:  /* STATUS_BUFFER_OVERFLOW */
559                 break; /* normal for DCERPC named pipes */
560             case NtStatus.NT_STATUS_MORE_PROCESSING_REQUIRED:
561                 break; /* normal for NTLMSSP */
562             default:
563                 throw new SmbException( resp.errorCode, null );
564         }
565         if (resp.verifyFailed) {
566             throw new SmbException( "Signature verification failed." );
567         }
568     }
send( ServerMessageBlock request, ServerMessageBlock response )569     void send( ServerMessageBlock request, ServerMessageBlock response ) throws SmbException {
570 
571         connect(); /* must negotiate before we can test flags2, useUnicode, etc */
572 
573         request.flags2 |= flags2;
574         request.useUnicode = useUnicode;
575         request.response = response; /* needed by sign */
576         if (request.digest == null)
577             request.digest = digest; /* for sign called in encode */
578 
579         try {
580             if (response == null) {
581                 doSend0( request );
582                 return;
583             } else if (request instanceof SmbComTransaction) {
584                 response.command = request.command;
585                 SmbComTransaction req = (SmbComTransaction)request;
586                 SmbComTransactionResponse resp = (SmbComTransactionResponse)response;
587 
588                 req.maxBufferSize = snd_buf_size;
589                 resp.reset();
590 
591                 try {
592                     BufferCache.getBuffers( req, resp );
593 
594                     /*
595                      * First request w/ interim response
596                      */
597 
598                     req.nextElement();
599                     if (req.hasMoreElements()) {
600                         SmbComBlankResponse interim = new SmbComBlankResponse();
601                         super.sendrecv( req, interim, RESPONSE_TIMEOUT );
602                         if (interim.errorCode != 0) {
603                             checkStatus( req, interim );
604                         }
605                         req.nextElement();
606                     } else {
607                         makeKey( req );
608                     }
609 
610                     synchronized (this) {
611                         response.received = false;
612                         resp.isReceived = false;
613                         try {
614                             response_map.put( req, resp );
615 
616                             /*
617                              * Send multiple fragments
618                              */
619 
620                             do {
621                                 doSend0( req );
622                             } while( req.hasMoreElements() && req.nextElement() != null );
623 
624                             /*
625                              * Receive multiple fragments
626                              */
627 
628                             long timeout = RESPONSE_TIMEOUT;
629                             resp.expiration = System.currentTimeMillis() + timeout;
630                             while( resp.hasMoreElements() ) {
631                                 wait( timeout );
632                                 timeout = resp.expiration - System.currentTimeMillis();
633                                 if (timeout <= 0) {
634                                     throw new TransportException( this +
635                                             " timedout waiting for response to " +
636                                             req );
637                                 }
638                             }
639                             if (response.errorCode != 0) {
640                                 checkStatus( req, resp );
641                             }
642                         } catch( InterruptedException ie ) {
643                             throw new TransportException( ie );
644                         } finally {
645                             response_map.remove( req );
646                         }
647                     }
648                 } finally {
649                     BufferCache.releaseBuffer( req.txn_buf );
650                     BufferCache.releaseBuffer( resp.txn_buf );
651                 }
652 
653             } else {
654                 response.command = request.command;
655                 super.sendrecv( request, response, RESPONSE_TIMEOUT );
656             }
657         } catch( SmbException se ) {
658             throw se;
659         } catch( IOException ioe ) {
660             throw new SmbException( ioe.getMessage(), ioe );
661         }
662 
663         checkStatus( request, response );
664     }
toString()665     public String toString() {
666         return super.toString() + "[" + address + ":" + port + "]";
667     }
668 
669     /* DFS */
670 
671     /* Split DFS path like \fs1.example.com\root5\link2\foo\bar.txt into at
672      * most 3 components (not including the first index which is always empty):
673      * result[0] = ""
674      * result[1] = "fs1.example.com"
675      * result[2] = "root5"
676      * result[3] = "link2\foo\bar.txt"
677      */
dfsPathSplit(String path, String[] result)678     void dfsPathSplit(String path, String[] result)
679     {
680         int ri = 0, rlast = result.length - 1;
681         int i = 0, b = 0, len = path.length();
682 
683         do {
684             if (ri == rlast) {
685                 result[rlast] = path.substring(b);
686                 return;
687             }
688             if (i == len || path.charAt(i) == '\\') {
689                 result[ri++] = path.substring(b, i);
690                 b = i + 1;
691             }
692         } while (i++ < len);
693 
694         while (ri < result.length) {
695             result[ri++] = "";
696         }
697     }
getDfsReferrals(NtlmPasswordAuthentication auth, String path, int rn)698     DfsReferral getDfsReferrals(NtlmPasswordAuthentication auth,
699                 String path,
700                 int rn) throws SmbException {
701         SmbTree ipc = getSmbSession( auth ).getSmbTree( "IPC$", null );
702         Trans2GetDfsReferralResponse resp = new Trans2GetDfsReferralResponse();
703         ipc.send( new Trans2GetDfsReferral( path ), resp );
704 
705         if (resp.numReferrals == 0) {
706             return null;
707         } else if (rn == 0 || resp.numReferrals < rn) {
708             rn = resp.numReferrals;
709         }
710 
711         DfsReferral dr = new DfsReferral();
712 
713         String[] arr = new String[4];
714         long expiration = System.currentTimeMillis() + Dfs.TTL * 1000;
715 
716         int di = 0;
717         for ( ;; ) {
718                         /* NTLM HTTP Authentication must be re-negotiated
719                          * with challenge from 'server' to access DFS vol. */
720             dr.resolveHashes = auth.hashesExternal;
721             dr.ttl = resp.referrals[di].ttl;
722             dr.expiration = expiration;
723             if (path.equals("")) {
724                 dr.server = resp.referrals[di].path.substring(1).toLowerCase();
725             } else {
726                 dfsPathSplit(resp.referrals[di].node, arr);
727                 dr.server = arr[1];
728                 dr.share = arr[2];
729                 dr.path = arr[3];
730             }
731             dr.pathConsumed = resp.pathConsumed;
732 
733             di++;
734             if (di == rn)
735                 break;
736 
737             dr.append(new DfsReferral());
738             dr = dr.next;
739         }
740 
741         return dr.next;
742     }
__getDfsReferrals(NtlmPasswordAuthentication auth, String path, int rn)743     DfsReferral[] __getDfsReferrals(NtlmPasswordAuthentication auth,
744                 String path,
745                 int rn) throws SmbException {
746         SmbTree ipc = getSmbSession( auth ).getSmbTree( "IPC$", null );
747         Trans2GetDfsReferralResponse resp = new Trans2GetDfsReferralResponse();
748         ipc.send( new Trans2GetDfsReferral( path ), resp );
749 
750         if (rn == 0 || resp.numReferrals < rn) {
751             rn = resp.numReferrals;
752         }
753 
754         DfsReferral[] drs = new DfsReferral[rn];
755         String[] arr = new String[4];
756         long expiration = System.currentTimeMillis() + Dfs.TTL * 1000;
757 
758         for (int di = 0; di < drs.length; di++) {
759             DfsReferral dr = new DfsReferral();
760                         /* NTLM HTTP Authentication must be re-negotiated
761                          * with challenge from 'server' to access DFS vol. */
762             dr.resolveHashes = auth.hashesExternal;
763             dr.ttl = resp.referrals[di].ttl;
764             dr.expiration = expiration;
765             if (path.equals("")) {
766                 dr.server = resp.referrals[di].path.substring(1).toLowerCase();
767             } else {
768                 dfsPathSplit(resp.referrals[di].node, arr);
769                 dr.server = arr[1];
770                 dr.share = arr[2];
771                 dr.path = arr[3];
772             }
773             dr.pathConsumed = resp.pathConsumed;
774             drs[di] = dr;
775         }
776 
777         return drs;
778     }
779 
780 //    FileEntry[] getDfsRoots(String domainName, NtlmPasswordAuthentication auth) throws IOException {
781 //        MsrpcDfsRootEnum rpc;
782 //        DcerpcHandle handle = null;
783 //
784 //        /* Procedure:
785 //         * Lookup a DC in the target domain
786 //         * Ask the DC for a referral for the domain (e.g. "\example.com")
787 //         * Do NetrDfsEnumEx on the server returned in the referral to
788 //         * get roots in target domain
789 //         */
790 //
791 //        UniAddress dc = UniAddress.getByName(domainName);
792 //        SmbTransport trans = SmbTransport.getSmbTransport(dc, 0);
793 //        DfsReferral[] dr = trans.getDfsReferrals(auth, "\\" + domainName, 1);
794 //
795 //        handle = DcerpcHandle.getHandle("ncacn_np:" +
796 //                    UniAddress.getByName(dr[0].server).getHostAddress() +
797 //                    "[\\PIPE\\netdfs]", auth);
798 //        try {
799 //            rpc = new MsrpcDfsRootEnum(domainName);
800 //            handle.sendrecv(rpc);
801 //            if (rpc.retval != 0)
802 //                throw new SmbException(rpc.retval, true);
803 //            return rpc.getEntries();
804 //        } finally {
805 //            try {
806 //                handle.close();
807 //            } catch(IOException ioe) {
808 //                if (log.level >= 4)
809 //                    ioe.printStackTrace(log);
810 //            }
811 //        }
812 //    }
813 }
814 
815