FileDocCategorySizeDatePackage
SmbTransport.javaAPI DocJCIFS 1.3.17 API32299Tue Oct 18 15:26:24 BST 2011jcifs.smb

SmbTransport

public class SmbTransport extends Transport implements SmbConstants

Fields Summary
static final byte[]
BUF
static final SmbComNegotiate
NEGOTIATE_REQUEST
static LogStream
log
static HashMap
dfsRoots
InetAddress
localAddr
int
localPort
UniAddress
address
Socket
socket
int
port
int
mid
OutputStream
out
InputStream
in
byte[]
sbuf
SmbComBlankResponse
key
long
sessionExpiration
LinkedList
referrals
SigningDigest
digest
LinkedList
sessions
ServerData
server
int
flags2
int
maxMpxCount
int
snd_buf_size
int
rcv_buf_size
int
capabilities
int
sessionKey
boolean
useUnicode
String
tconHostName
Constructors Summary
SmbTransport(UniAddress address, int port, InetAddress localAddr, int localPort)


              
        this.address = address;
        this.port = port;
        this.localAddr = localAddr;
        this.localPort = localPort;
    
Methods Summary
DfsReferral[]__getDfsReferrals(NtlmPasswordAuthentication auth, java.lang.String path, int rn)

        SmbTree ipc = getSmbSession( auth ).getSmbTree( "IPC$", null );
        Trans2GetDfsReferralResponse resp = new Trans2GetDfsReferralResponse();
        ipc.send( new Trans2GetDfsReferral( path ), resp );

        if (rn == 0 || resp.numReferrals < rn) {
            rn = resp.numReferrals;
        }

        DfsReferral[] drs = new DfsReferral[rn];
        String[] arr = new String[4];
        long expiration = System.currentTimeMillis() + Dfs.TTL * 1000;

        for (int di = 0; di < drs.length; di++) {
            DfsReferral dr = new DfsReferral();
                        /* NTLM HTTP Authentication must be re-negotiated
                         * with challenge from 'server' to access DFS vol. */
            dr.resolveHashes = auth.hashesExternal;
            dr.ttl = resp.referrals[di].ttl;
            dr.expiration = expiration;
            if (path.equals("")) {
                dr.server = resp.referrals[di].path.substring(1).toLowerCase();
            } else {
                dfsPathSplit(resp.referrals[di].node, arr);
                dr.server = arr[1];
                dr.share = arr[2];
                dr.path = arr[3];
            }
            dr.pathConsumed = resp.pathConsumed;
            drs[di] = dr;
        }

        return drs;
    
voidcheckStatus(ServerMessageBlock req, ServerMessageBlock resp)

        resp.errorCode = SmbException.getStatusByCode( resp.errorCode );
        switch( resp.errorCode ) {
            case NtStatus.NT_STATUS_OK:
                break;
            case NtStatus.NT_STATUS_ACCESS_DENIED:
            case NtStatus.NT_STATUS_WRONG_PASSWORD:
            case NtStatus.NT_STATUS_LOGON_FAILURE:
            case NtStatus.NT_STATUS_ACCOUNT_RESTRICTION:
            case NtStatus.NT_STATUS_INVALID_LOGON_HOURS:
            case NtStatus.NT_STATUS_INVALID_WORKSTATION:
            case NtStatus.NT_STATUS_PASSWORD_EXPIRED:
            case NtStatus.NT_STATUS_ACCOUNT_DISABLED:
            case NtStatus.NT_STATUS_ACCOUNT_LOCKED_OUT:
            case NtStatus.NT_STATUS_TRUSTED_DOMAIN_FAILURE:
                throw new SmbAuthException( resp.errorCode );
            case NtStatus.NT_STATUS_PATH_NOT_COVERED:
                if( req.auth == null ) {
                    throw new SmbException( resp.errorCode, null );
                }

                DfsReferral dr = getDfsReferrals(req.auth, req.path, 1);
                if (dr == null)
                    throw new SmbException(resp.errorCode, null);   

                SmbFile.dfs.insert(req.path, dr);
                throw dr;
            case 0x80000005:  /* STATUS_BUFFER_OVERFLOW */
                break; /* normal for DCERPC named pipes */
            case NtStatus.NT_STATUS_MORE_PROCESSING_REQUIRED:
                break; /* normal for NTLMSSP */
            default:
                throw new SmbException( resp.errorCode, null );
        }
        if (resp.verifyFailed) {
            throw new SmbException( "Signature verification failed." );
        }
    
public voidconnect()

        try {
            super.connect( RESPONSE_TIMEOUT );
        } catch( TransportException te ) {
            throw new SmbException( "Failed to connect: " + address, te );
        }
    
voiddfsPathSplit(java.lang.String path, java.lang.String[] result)

        int ri = 0, rlast = result.length - 1;
        int i = 0, b = 0, len = path.length();

        do {
            if (ri == rlast) {
                result[rlast] = path.substring(b);
                return;
            }
            if (i == len || path.charAt(i) == '\\") {
                result[ri++] = path.substring(b, i);
                b = i + 1;
            }
        } while (i++ < len);

        while (ri < result.length) {
            result[ri++] = "";
        }
    
protected voiddoConnect()

        /*
         * Negotiate Protocol Request / Response
         */

        SmbComNegotiateResponse resp = new SmbComNegotiateResponse( server );
        try {
            negotiate( port, resp );
        } catch( ConnectException ce ) {
            port = (port == 0 || port == DEFAULT_PORT) ? 139 : DEFAULT_PORT;
            negotiate( port, resp );
        } catch( NoRouteToHostException nr ) {
            port = (port == 0 || port == DEFAULT_PORT) ? 139 : DEFAULT_PORT;
            negotiate( port, resp );
        }

        if( resp.dialectIndex > 10 ) {
            throw new SmbException( "This client does not support the negotiated dialect." );
        }
        if ((server.capabilities & CAP_EXTENDED_SECURITY) != CAP_EXTENDED_SECURITY &&
                    server.encryptionKeyLength != 8 &&
                    LM_COMPATIBILITY == 0) {
            throw new SmbException("Unexpected encryption key length: " + server.encryptionKeyLength);
        }

        /* Adjust negotiated values */

        tconHostName = address.getHostName();
        if (server.signaturesRequired || (server.signaturesEnabled && SIGNPREF)) {
            flags2 |= ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES;
        } else {
            flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES;
        }
        maxMpxCount = Math.min( maxMpxCount, server.maxMpxCount );
        if (maxMpxCount < 1) maxMpxCount = 1;
        snd_buf_size = Math.min( snd_buf_size, server.maxBufferSize );
        capabilities &= server.capabilities;
        if ((server.capabilities & CAP_EXTENDED_SECURITY) == CAP_EXTENDED_SECURITY)
            capabilities |= CAP_EXTENDED_SECURITY; // & doesn't copy high bit

        if ((capabilities & ServerMessageBlock.CAP_UNICODE) == 0) {
            // server doesn't want unicode
            if (FORCE_UNICODE) {
                capabilities |= ServerMessageBlock.CAP_UNICODE;
            } else {
                useUnicode = false;
                flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_UNICODE;
            }
        }
    
protected voiddoDisconnect(boolean hard)

        ListIterator iter = sessions.listIterator();
        try {
            while (iter.hasNext()) {
                SmbSession ssn = (SmbSession)iter.next();
                ssn.logoff( hard );
            }
            socket.shutdownOutput();
            out.close();
            in.close();
            socket.close();
        } finally {
            digest = null;
            socket = null;
            tconHostName = null;
        }
    
protected voiddoRecv(Response response)

        ServerMessageBlock resp = (ServerMessageBlock)response;
        resp.useUnicode = useUnicode;
        resp.extendedSecurity = (capabilities & CAP_EXTENDED_SECURITY) == CAP_EXTENDED_SECURITY;

        synchronized (BUF) {
            System.arraycopy( sbuf, 0, BUF, 0, 4 + HEADER_LENGTH );
            int size = Encdec.dec_uint16be( BUF, 2 ) & 0xFFFF;
            if (size < (HEADER_LENGTH + 1) || (4 + size) > rcv_buf_size ) {
                throw new IOException( "Invalid payload size: " + size );
            }
            int errorCode = Encdec.dec_uint32le( BUF, 9 ) & 0xFFFFFFFF;
            if (resp.command == ServerMessageBlock.SMB_COM_READ_ANDX &&
                        (errorCode == 0 ||
                        errorCode == 0x80000005)) { // overflow indicator normal for pipe
                SmbComReadAndXResponse r = (SmbComReadAndXResponse)resp;
                int off = HEADER_LENGTH;
                                    /* WordCount thru dataOffset always 27 */
                readn( in, BUF, 4 + off, 27 ); off += 27;
                resp.decode( BUF, 4 );
                                              /* EMC can send pad w/o data */
                int pad = r.dataOffset - off;
                if (r.byteCount > 0 && pad > 0 && pad < 4)
                    readn( in, BUF, 4 + off, pad);

                if (r.dataLength > 0)
                    readn( in, r.b, r.off, r.dataLength );  /* read direct */
            } else {
                readn( in, BUF, 4 + 32, size - 32 );
                resp.decode( BUF, 4 );
                if (resp instanceof SmbComTransactionResponse) {
                    ((SmbComTransactionResponse)resp).nextElement();
                }
            }

            /* Verification fails (w/ W2K3 server at least) if status is not 0. This
             * suggests MS doesn't compute the signature (correctly) for error responses
             * (perhaps for DOS reasons).
             */
            if (digest != null && resp.errorCode == 0) {
                digest.verify( BUF, 4, resp );
            }

            if (log.level >= 4) {
                log.println( response );
                if (log.level >= 6) {
                    Hexdump.hexdump( log, BUF, 4, size );
                }
            }
        }
    
protected voiddoSend(Request request)

        synchronized (BUF) {
            ServerMessageBlock smb = (ServerMessageBlock)request;
            int n = smb.encode( BUF, 4 );
            Encdec.enc_uint32be( n & 0xFFFF, BUF, 0 ); /* 4 byte session message header */
            if (log.level >= 4) {
                do {
                    log.println( smb );
                } while (smb instanceof AndXServerMessageBlock &&
                        (smb = ((AndXServerMessageBlock)smb).andx) != null);
                if (log.level >= 6) {
                    Hexdump.hexdump( log, BUF, 4, n );
                }
            }
            /* For some reason this can sometimes get broken up into another
             * "NBSS Continuation Message" frame according to WireShark
             */
            out.write( BUF, 0, 4 + n );
        }
    
protected voiddoSend0(Request request)

        try {
            doSend( request );
        } catch( IOException ioe ) {
            if (log.level > 2)
                ioe.printStackTrace( log );
            try {
                disconnect( true );
            } catch( IOException ioe2 ) {
                ioe2.printStackTrace( log );
            }
            throw ioe;
        }
    
protected voiddoSkip()

        int size = Encdec.dec_uint16be( sbuf, 2 ) & 0xFFFF;
        if (size < 33 || (4 + size) > rcv_buf_size ) {
            /* log message? */
            in.skip( in.available() );
        } else {
            in.skip( size - 32 );
        }
    
DfsReferralgetDfsReferrals(NtlmPasswordAuthentication auth, java.lang.String path, int rn)

        SmbTree ipc = getSmbSession( auth ).getSmbTree( "IPC$", null );
        Trans2GetDfsReferralResponse resp = new Trans2GetDfsReferralResponse();
        ipc.send( new Trans2GetDfsReferral( path ), resp );

        if (resp.numReferrals == 0) {
            return null;
        } else if (rn == 0 || resp.numReferrals < rn) {
            rn = resp.numReferrals;
        }

        DfsReferral dr = new DfsReferral();

        String[] arr = new String[4];
        long expiration = System.currentTimeMillis() + Dfs.TTL * 1000;

        int di = 0;
        for ( ;; ) {
                        /* NTLM HTTP Authentication must be re-negotiated
                         * with challenge from 'server' to access DFS vol. */
            dr.resolveHashes = auth.hashesExternal;
            dr.ttl = resp.referrals[di].ttl;
            dr.expiration = expiration;
            if (path.equals("")) {
                dr.server = resp.referrals[di].path.substring(1).toLowerCase();
            } else {
                dfsPathSplit(resp.referrals[di].node, arr);
                dr.server = arr[1];
                dr.share = arr[2];
                dr.path = arr[3];
            }
            dr.pathConsumed = resp.pathConsumed;

            di++;
            if (di == rn)
                break;

            dr.append(new DfsReferral());
            dr = dr.next;
        }

        return dr.next;
    
synchronized SmbSessiongetSmbSession()

        return getSmbSession( new NtlmPasswordAuthentication( null, null, null ));
    
synchronized SmbSessiongetSmbSession(NtlmPasswordAuthentication auth)

        SmbSession ssn;
        long now;

        ListIterator iter = sessions.listIterator();
        while( iter.hasNext() ) {
            ssn = (SmbSession)iter.next();
            if( ssn.matches( auth )) {
                ssn.auth = auth;
                return ssn;
            }
        }

                                        /* logoff old sessions */
        if (SO_TIMEOUT > 0 && sessionExpiration < (now = System.currentTimeMillis())) {
            sessionExpiration = now + SO_TIMEOUT;
            iter = sessions.listIterator();
            while( iter.hasNext() ) {
                ssn = (SmbSession)iter.next();
                if( ssn.expiration < now ) {
                    ssn.logoff( false );
                }
            }
        }

        ssn = new SmbSession( address, port, localAddr, localPort, auth );
        ssn.transport = this;
        sessions.add( ssn );

        return ssn;
    
static synchronized jcifs.smb.SmbTransportgetSmbTransport(UniAddress address, int port)


             
        return getSmbTransport( address, port, LADDR, LPORT, null );
    
static synchronized jcifs.smb.SmbTransportgetSmbTransport(UniAddress address, int port, java.net.InetAddress localAddr, int localPort, java.lang.String hostName)

        SmbTransport conn;

        synchronized( CONNECTIONS ) {
            if( SSN_LIMIT != 1 ) {
                ListIterator iter = CONNECTIONS.listIterator();
                while( iter.hasNext() ) {
                    conn = (SmbTransport)iter.next();
                    if( conn.matches( address, port, localAddr, localPort, hostName ) &&
                            ( SSN_LIMIT == 0 || conn.sessions.size() < SSN_LIMIT )) {
                        return conn;
                    }
                }
            }

            conn = new SmbTransport( address, port, localAddr, localPort );
            CONNECTIONS.add( 0, conn );
        }

        return conn;
    
booleanhasCapability(int cap)

        try {
            connect( RESPONSE_TIMEOUT );
        } catch( IOException ioe ) {
            throw new SmbException( ioe.getMessage(), ioe );
        }
        return (capabilities & cap) == cap;
    
booleanisSignatureSetupRequired(NtlmPasswordAuthentication auth)

        return ( flags2 & ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES ) != 0 &&
                digest == null &&
                auth != NtlmPasswordAuthentication.NULL &&
                NtlmPasswordAuthentication.NULL.equals( auth ) == false;
    
protected voidmakeKey(Request request)

        /* The request *is* the key */
        if (++mid == 32000) mid = 1;
        ((ServerMessageBlock)request).mid = mid;
    
booleanmatches(UniAddress address, int port, java.net.InetAddress localAddr, int localPort, java.lang.String hostName)

        if (hostName == null)
            hostName = address.getHostName();
        return (this.tconHostName == null || hostName.equalsIgnoreCase(this.tconHostName)) &&
                    address.equals( this.address ) &&
                    (port == 0 || port == this.port ||
                            /* port 139 is ok if 445 was requested */
                            (port == 445 && this.port == 139)) &&
                    (localAddr == this.localAddr ||
                            (localAddr != null &&
                                    localAddr.equals( this.localAddr ))) &&
                    localPort == this.localPort;
    
private voidnegotiate(int port, ServerMessageBlock resp)

        /* We cannot use Transport.sendrecv() yet because
         * the Transport thread is not setup until doConnect()
         * returns and we want to supress all communication
         * until we have properly negotiated.
         */
        synchronized (sbuf) {
            if (port == 139) {
                ssn139();
            } else {
                if (port == 0)
                    port = DEFAULT_PORT; // 445
/* These Socket constructors attempt to connect before SO_TIMEOUT can be applied
                if (localAddr == null) {
                    socket = new Socket( address.getHostAddress(), port );
                } else {
                    socket = new Socket( address.getHostAddress(), port, localAddr, localPort );
                }
                socket.setSoTimeout( SO_TIMEOUT );
*/
                socket = new Socket();
                if (localAddr != null)
                    socket.bind(new InetSocketAddress(localAddr, localPort));
                socket.connect(new InetSocketAddress(address.getHostAddress(), port), CONN_TIMEOUT);
                socket.setSoTimeout( SO_TIMEOUT );

                out = socket.getOutputStream();
                in = socket.getInputStream();
            }

            if (++mid == 32000) mid = 1;
            NEGOTIATE_REQUEST.mid = mid;
            int n = NEGOTIATE_REQUEST.encode( sbuf, 4 );
            Encdec.enc_uint32be( n & 0xFFFF, sbuf, 0 ); /* 4 byte ssn msg header */

            if (log.level >= 4) {
                log.println( NEGOTIATE_REQUEST );
                if (log.level >= 6) {
                    Hexdump.hexdump( log, sbuf, 4, n );
                }
            }

            out.write( sbuf, 0, 4 + n );
            out.flush();
            /* Note the Transport thread isn't running yet so we can
             * read from the socket here.
             */
            if (peekKey() == null) /* try to read header */
                throw new IOException( "transport closed in negotiate" );
            int size = Encdec.dec_uint16be( sbuf, 2 ) & 0xFFFF;
            if (size < 33 || (4 + size) > sbuf.length ) {
                throw new IOException( "Invalid payload size: " + size );
            }
            readn( in, sbuf, 4 + 32, size - 32 );
            resp.decode( sbuf, 4 );

            if (log.level >= 4) {
                log.println( resp );
                if (log.level >= 6) {
                    Hexdump.hexdump( log, sbuf, 4, n );
                }
            }
        }
    
protected RequestpeekKey()

        int n;
        do {
            if ((n = readn( in, sbuf, 0, 4 )) < 4)
                return null;
        } while (sbuf[0] == (byte)0x85);  /* Dodge NetBIOS keep-alive */
                                                   /* read smb header */
        if ((n = readn( in, sbuf, 4, 32 )) < 32)
            return null;
        if (log.level >= 4) {
            log.println( "New data read: " + this );
            jcifs.util.Hexdump.hexdump( log, sbuf, 4, 32 );
        }

        for ( ;; ) {
            /* 01234567
             * 00SSFSMB
             * 0 - 0's
             * S - size of payload
             * FSMB - 0xFF SMB magic #
             */

            if (sbuf[0] == (byte)0x00 &&
                        sbuf[1] == (byte)0x00 &&
                        sbuf[4] == (byte)0xFF &&
                        sbuf[5] == (byte)'S" &&
                        sbuf[6] == (byte)'M" &&
                        sbuf[7] == (byte)'B") {
                break; /* all good */
            }
                                        /* out of phase maybe? */
                          /* inch forward 1 byte and try again */
            for (int i = 0; i < 35; i++) {
                sbuf[i] = sbuf[i + 1];
            }
            int b;
            if ((b = in.read()) == -1) return null;
            sbuf[35] = (byte)b;
        }

        key.mid = Encdec.dec_uint16le( sbuf, 34 ) & 0xFFFF;

        /* Unless key returned is null or invalid Transport.loop() always
         * calls doRecv() after and no one else but the transport thread
         * should call doRecv(). Therefore it is ok to expect that the data
         * in sbuf will be preserved for copying into BUF in doRecv().
         */

        return key;
    
voidsend(ServerMessageBlock request, ServerMessageBlock response)


        connect(); /* must negotiate before we can test flags2, useUnicode, etc */

        request.flags2 |= flags2;
        request.useUnicode = useUnicode;
        request.response = response; /* needed by sign */
        if (request.digest == null)
            request.digest = digest; /* for sign called in encode */

        try {
            if (response == null) {
                doSend0( request );
                return;
            } else if (request instanceof SmbComTransaction) {
                response.command = request.command;
                SmbComTransaction req = (SmbComTransaction)request;
                SmbComTransactionResponse resp = (SmbComTransactionResponse)response;

                req.maxBufferSize = snd_buf_size;
                resp.reset();

                try {
                    BufferCache.getBuffers( req, resp );

                    /* 
                     * First request w/ interim response
                     */ 

                    req.nextElement();
                    if (req.hasMoreElements()) {
                        SmbComBlankResponse interim = new SmbComBlankResponse();
                        super.sendrecv( req, interim, RESPONSE_TIMEOUT );
                        if (interim.errorCode != 0) {
                            checkStatus( req, interim );
                        }
                        req.nextElement();
                    } else {
                        makeKey( req );
                    }

                    synchronized (this) {
                        response.received = false;
                        resp.isReceived = false;
                        try {
                            response_map.put( req, resp );

                            /* 
                             * Send multiple fragments
                             */

                            do {
                                doSend0( req );
                            } while( req.hasMoreElements() && req.nextElement() != null );

                            /* 
                             * Receive multiple fragments
                             */

                            long timeout = RESPONSE_TIMEOUT;
                            resp.expiration = System.currentTimeMillis() + timeout;
                            while( resp.hasMoreElements() ) {
                                wait( timeout );
                                timeout = resp.expiration - System.currentTimeMillis();
                                if (timeout <= 0) {
                                    throw new TransportException( this +
                                            " timedout waiting for response to " +
                                            req );
                                }
                            }
                            if (response.errorCode != 0) {
                                checkStatus( req, resp );
                            }
                        } catch( InterruptedException ie ) {
                            throw new TransportException( ie );
                        } finally {
                            response_map.remove( req );
                        }
                    }
                } finally {
                    BufferCache.releaseBuffer( req.txn_buf );
                    BufferCache.releaseBuffer( resp.txn_buf );
                }

            } else {
                response.command = request.command;
                super.sendrecv( request, response, RESPONSE_TIMEOUT );
            }
        } catch( SmbException se ) {
            throw se;
        } catch( IOException ioe ) {
            throw new SmbException( ioe.getMessage(), ioe );
        }

        checkStatus( request, response );
    
voidssn139()

        Name calledName = new Name( address.firstCalledName(), 0x20, null );
        do {
/* These Socket constructors attempt to connect before SO_TIMEOUT can be applied
            if (localAddr == null) {
                socket = new Socket( address.getHostAddress(), 139 );
            } else {
                socket = new Socket( address.getHostAddress(), 139, localAddr, localPort );
            }
            socket.setSoTimeout( SO_TIMEOUT );
*/

            socket = new Socket();
            if (localAddr != null)
                socket.bind(new InetSocketAddress(localAddr, localPort));
            socket.connect(new InetSocketAddress(address.getHostAddress(), 139), CONN_TIMEOUT);
            socket.setSoTimeout( SO_TIMEOUT );

            out = socket.getOutputStream();
            in = socket.getInputStream();

            SessionServicePacket ssp = new SessionRequestPacket( calledName,
                    NbtAddress.getLocalName() );
            out.write( sbuf, 0, ssp.writeWireFormat( sbuf, 0 ));
            if (readn( in, sbuf, 0, 4 ) < 4) {
                try {
                    socket.close();
                } catch(IOException ioe) {
                }
                throw new SmbException( "EOF during NetBIOS session request" );
            }
            switch( sbuf[0] & 0xFF ) {
                case SessionServicePacket.POSITIVE_SESSION_RESPONSE:
                    if( log.level >= 4 )
                        log.println( "session established ok with " + address );
                    return;
                case SessionServicePacket.NEGATIVE_SESSION_RESPONSE:
                    int errorCode = (int)( in.read() & 0xFF );
                    switch (errorCode) {
                        case NbtException.CALLED_NOT_PRESENT:
                        case NbtException.NOT_LISTENING_CALLED:
                            socket.close();
                            break;
                        default:
                            disconnect( true );
                            throw new NbtException( NbtException.ERR_SSN_SRVC, errorCode );
                    }
                    break;
                case -1:
                    disconnect( true );
                    throw new NbtException( NbtException.ERR_SSN_SRVC,
                            NbtException.CONNECTION_REFUSED );
                default:
                    disconnect( true );
                    throw new NbtException( NbtException.ERR_SSN_SRVC, 0 );
            }
        } while(( calledName.name = address.nextCalledName()) != null );

        throw new IOException( "Failed to establish session with " + address );
    
public java.lang.StringtoString()

        return super.toString() + "[" + address + ":" + port + "]";