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