All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jcifs.internal.smb2.ServerMessageBlock2 Maven / Gradle / Ivy

There is a newer version: 2.1.10
Show newest version
/*
 * © 2017 AgNO3 Gmbh & Co. KG
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package jcifs.internal.smb2;


import jcifs.Configuration;
import jcifs.internal.CommonServerMessageBlock;
import jcifs.internal.CommonServerMessageBlockResponse;
import jcifs.internal.SMBProtocolDecodingException;
import jcifs.internal.SMBSigningDigest;
import jcifs.internal.util.SMBUtil;
import jcifs.smb.SmbException;
import jcifs.util.Hexdump;


/**
 * 
 * @author mbechler
 *
 */
public abstract class ServerMessageBlock2 implements CommonServerMessageBlock {

    /*
     * These are all the smbs supported by this library. This includes requests
     * and well as their responses for each type however the actual implementations
     * of the readXxxWireFormat and writeXxxWireFormat methods may not be in
     * place. For example at the time of this writing the readXxxWireFormat
     * for requests and the writeXxxWireFormat for responses are not implemented
     * and simply return 0. These would need to be completed for a server
     * implementation.
     */

    protected static final short SMB2_NEGOTIATE = 0x00;
    protected static final short SMB2_SESSION_SETUP = 0x01;
    protected static final short SMB2_LOGOFF = 0x02;
    protected static final short SMB2_TREE_CONNECT = 0x0003;
    protected static final short SMB2_TREE_DISCONNECT = 0x0004;
    protected static final short SMB2_CREATE = 0x0005;
    protected static final short SMB2_CLOSE = 0x0006;
    protected static final short SMB2_FLUSH = 0x0007;
    protected static final short SMB2_READ = 0x0008;
    protected static final short SMB2_WRITE = 0x0009;
    protected static final short SMB2_LOCK = 0x000A;
    protected static final short SMB2_IOCTL = 0x000B;
    protected static final short SMB2_CANCEL = 0x000C;
    protected static final short SMB2_ECHO = 0x000D;
    protected static final short SMB2_QUERY_DIRECTORY = 0x000E;
    protected static final short SMB2_CHANGE_NOTIFY = 0x000F;
    protected static final short SMB2_QUERY_INFO = 0x0010;
    protected static final short SMB2_SET_INFO = 0x0011;
    protected static final short SMB2_OPLOCK_BREAK = 0x0012;

    /**
     * 
     */
    public static final int SMB2_FLAGS_SERVER_TO_REDIR = 0x00000001;
    /**
     * 
     */
    public static final int SMB2_FLAGS_ASYNC_COMMAND = 0x00000002;
    /**
     * 
     */
    public static final int SMB2_FLAGS_RELATED_OPERATIONS = 0x00000004;
    /**
     * 
     */
    public static final int SMB2_FLAGS_SIGNED = 0x00000008;
    /**
     * 
     */
    public static final int SMB2_FLAGS_PRIORITY_MASK = 0x00000070;
    /**
     * 
     */
    public static final int SMB2_FLAGS_DFS_OPERATIONS = 0x10000000;
    /**
     * 
     */
    public static final int SMB2_FLAGS_REPLAY_OPERATION = 0x20000000;

    private int command;
    private int flags;
    private int length, headerStart, wordCount, byteCount;

    private byte[] signature = new byte[16];
    private Smb2SigningDigest digest = null;

    private Configuration config;

    private int creditCharge;
    private int status;
    private int credit;
    private int nextCommand;
    private int readSize;
    private boolean async;
    private int treeId;
    private long mid, asyncId, sessionId;
    private byte errorContextCount;
    private byte[] errorData;

    private boolean retainPayload;
    private byte[] rawPayload;

    private ServerMessageBlock2 next;


    protected ServerMessageBlock2 ( Configuration config ) {
        this.config = config;
    }


    protected ServerMessageBlock2 ( Configuration config, int command ) {
        this.config = config;
        this.command = command;
    }


    /**
     * @return the config
     */
    protected Configuration getConfig () {
        return this.config;
    }


    @Override
    public void reset () {
        this.flags = 0;
        this.digest = null;
        this.sessionId = 0;
        this.treeId = 0;
    }


    /**
     * @return the command
     */
    @Override
    public final int getCommand () {
        return this.command;
    }


    /**
     * @return offset to next compound command
     */
    public final int getNextCommandOffset () {
        return this.nextCommand;
    }


    /**
     * @param readSize
     *            the readSize to set
     */
    public void setReadSize ( int readSize ) {
        this.readSize = readSize;
    }


    /**
     * @return the async
     */
    public boolean isAsync () {
        return this.async;
    }


    /**
     * @param command
     *            the command to set
     */
    @Override
    public final void setCommand ( int command ) {
        this.command = command;
    }


    /**
     * @return the treeId
     */
    public final int getTreeId () {
        return this.treeId;
    }


    /**
     * @param treeId
     *            the treeId to set
     */
    public final void setTreeId ( int treeId ) {
        this.treeId = treeId;
        if ( this.next != null ) {
            this.next.setTreeId(treeId);
        }
    }


    /**
     * @return the asyncId
     */
    public final long getAsyncId () {
        return this.asyncId;
    }


    /**
     * @param asyncId
     *            the asyncId to set
     */
    public final void setAsyncId ( long asyncId ) {
        this.asyncId = asyncId;
    }


    /**
     * @return the credit
     */
    public final int getCredit () {
        return this.credit;
    }


    /**
     * @param credit
     *            the credit to set
     */
    public final void setCredit ( int credit ) {
        this.credit = credit;
    }


    /**
     * @return the creditCharge
     */
    public final int getCreditCharge () {
        return this.creditCharge;
    }


    @Override
    public void retainPayload () {
        this.retainPayload = true;
    }


    @Override
    public boolean isRetainPayload () {
        return this.retainPayload;
    }


    @Override
    public byte[] getRawPayload () {
        return this.rawPayload;
    }


    @Override
    public void setRawPayload ( byte[] rawPayload ) {
        this.rawPayload = rawPayload;
    }


    /**
     * {@inheritDoc}
     *
     * @see jcifs.internal.CommonServerMessageBlock#getDigest()
     */
    @Override
    public Smb2SigningDigest getDigest () {
        return this.digest;
    }


    /**
     * 
     * {@inheritDoc}
     *
     * @see jcifs.internal.CommonServerMessageBlock#setDigest(jcifs.internal.SMBSigningDigest)
     */
    @Override
    public void setDigest ( SMBSigningDigest digest ) {
        this.digest = (Smb2SigningDigest) digest;
        if ( this.next != null ) {
            this.next.setDigest(digest);
        }
    }


    /**
     * @return the status
     */
    public final int getStatus () {
        return this.status;
    }


    /**
     * @return the sessionId
     */
    public long getSessionId () {
        return this.sessionId;
    }


    /**
     * @param sessionId
     *            the sessionId to set
     */
    @Override
    public final void setSessionId ( long sessionId ) {
        this.sessionId = sessionId;
        if ( this.next != null ) {
            this.next.setSessionId(sessionId);
        }
    }


    /**
     * {@inheritDoc}
     *
     * @see jcifs.internal.CommonServerMessageBlock#setExtendedSecurity(boolean)
     */
    @Override
    public void setExtendedSecurity ( boolean extendedSecurity ) {
        // ignore
    }


    /**
     * {@inheritDoc}
     *
     * @see jcifs.internal.CommonServerMessageBlock#setUid(int)
     */
    @Override
    public void setUid ( int uid ) {
        // ignore
    }


    /**
     * @return the flags
     */
    public final int getFlags () {
        return this.flags;
    }


    /**
     * 
     * @param flag
     */
    public final void addFlags ( int flag ) {
        this.flags |= flag;
    }


    /**
     * 
     * @param flag
     */
    public final void clearFlags ( int flag ) {
        this.flags &= ~flag;
    }


    /**
     * @return the mid
     */
    @Override
    public final long getMid () {
        return this.mid;
    }


    /**
     * @param mid
     *            the mid to set
     */
    @Override
    public final void setMid ( long mid ) {
        this.mid = mid;
    }


    /**
     * @param n
     * @return whether chaining was successful
     */
    public boolean chain ( ServerMessageBlock2 n ) {
        if ( this.next != null ) {
            return this.next.chain(n);
        }

        n.addFlags(SMB2_FLAGS_RELATED_OPERATIONS);
        this.next = n;
        return true;
    }


    protected ServerMessageBlock2 getNext () {
        return this.next;
    }


    protected void setNext ( ServerMessageBlock2 n ) {
        this.next = n;
    }


    /**
     * @return the response
     */
    @Override
    public ServerMessageBlock2Response getResponse () {
        return null;
    }


    /**
     * 
     * {@inheritDoc}
     *
     * @see jcifs.internal.CommonServerMessageBlock#setResponse(jcifs.internal.CommonServerMessageBlockResponse)
     */
    @Override
    public void setResponse ( CommonServerMessageBlockResponse msg ) {

    }


    /**
     * @return the errorData
     */
    public final byte[] getErrorData () {
        return this.errorData;
    }


    /**
     * @return the errorContextCount
     */
    public final byte getErrorContextCount () {
        return this.errorContextCount;
    }


    /**
     * @return the headerStart
     */
    public final int getHeaderStart () {
        return this.headerStart;
    }


    /**
     * @return the length
     */
    public final int getLength () {
        return this.length;
    }


    @Override
    public int encode ( byte[] dst, int dstIndex ) {
        int start = this.headerStart = dstIndex;
        dstIndex += writeHeaderWireFormat(dst, dstIndex);

        this.byteCount = writeBytesWireFormat(dst, dstIndex);
        dstIndex += this.byteCount;
        dstIndex += pad8(dstIndex);

        this.length = dstIndex - start;

        int len = this.length;

        if ( this.next != null ) {
            int nextStart = dstIndex;
            dstIndex += this.next.encode(dst, dstIndex);
            int off = nextStart - start;
            SMBUtil.writeInt4(off, dst, start + 20);
            len += dstIndex - nextStart;
        }

        if ( this.digest != null ) {
            this.digest.sign(dst, this.headerStart, this.length, this, getResponse());
        }

        if ( isRetainPayload() ) {
            this.rawPayload = new byte[len];
            System.arraycopy(dst, start, this.rawPayload, 0, len);
        }

        return len;
    }


    protected static final int size8 ( int size ) {
        return size8(size, 0);
    }


    protected static final int size8 ( int size, int align ) {

        int rem = size % 8 - align;
        if ( rem == 0 ) {
            return size;
        }
        if ( rem < 0 ) {
            rem = 8 + rem;
        }
        return size + 8 - rem;
    }


    /**
     * @param dstIndex
     * @return
     */
    protected final int pad8 ( int dstIndex ) {
        int fromHdr = dstIndex - this.headerStart;
        int rem = fromHdr % 8;
        if ( rem == 0 ) {
            return 0;
        }
        return 8 - rem;
    }


    @Override
    public int decode ( byte[] buffer, int bufferIndex ) throws SMBProtocolDecodingException {
        return decode(buffer, bufferIndex, false);
    }


    /**
     * @param buffer
     * @param bufferIndex
     * @param compound
     * @return decoded length
     * @throws SMBProtocolDecodingException
     */
    public int decode ( byte[] buffer, int bufferIndex, boolean compound ) throws SMBProtocolDecodingException {
        int start = this.headerStart = bufferIndex;
        bufferIndex += readHeaderWireFormat(buffer, bufferIndex);
        if ( isErrorResponseStatus() ) {
            bufferIndex += readErrorResponse(buffer, bufferIndex);
        }
        else {
            bufferIndex += readBytesWireFormat(buffer, bufferIndex);
        }

        this.length = bufferIndex - start;
        int len = this.length;

        if ( this.nextCommand != 0 ) {
            // padding becomes part of signature if this is _PART_ of a compound chain
            len += pad8(bufferIndex);
        }
        else if ( compound && this.nextCommand == 0 && this.readSize > 0 ) {
            // TODO: only apply this for actual compound chains, or is this correct for single responses, too?
            // 3.2.5.1.9 Handling Compounded Responses
            // The final response in the compounded response chain will have NextCommand equal to 0,
            // and it MUST be processed as an individual message of a size equal to the number of bytes
            // remaining in this receive.
            int rem = this.readSize - this.length;
            len += rem;
        }

        haveResponse(buffer, start, len);

        if ( this.nextCommand != 0 && this.next != null ) {
            if ( this.nextCommand % 8 != 0 ) {
                throw new SMBProtocolDecodingException("Chained command is not aligned");
            }
        }
        return len;
    }


    protected boolean isErrorResponseStatus () {
        return getStatus() != 0;
    }


    /**
     * @param buffer
     * @param start
     * @param len
     * @throws SMBProtocolDecodingException
     */
    protected void haveResponse ( byte[] buffer, int start, int len ) throws SMBProtocolDecodingException {}


    /**
     * @param buffer
     * @param bufferIndex
     * @return
     * @throws Smb2ProtocolDecodingException
     */
    protected int readErrorResponse ( byte[] buffer, int bufferIndex ) throws SMBProtocolDecodingException {
        int start = bufferIndex;
        int structureSize = SMBUtil.readInt2(buffer, bufferIndex);
        if ( structureSize != 9 ) {
            throw new SMBProtocolDecodingException("Error structureSize should be 9");
        }
        this.errorContextCount = buffer[ bufferIndex + 2 ];
        bufferIndex += 4;

        int bc = SMBUtil.readInt4(buffer, bufferIndex);
        bufferIndex += 4;

        if ( bc > 0 ) {
            this.errorData = new byte[bc];
            System.arraycopy(buffer, bufferIndex, this.errorData, 0, bc);
            bufferIndex += bc;
        }
        return bufferIndex - start;
    }


    protected int writeHeaderWireFormat ( byte[] dst, int dstIndex ) {
        System.arraycopy(SMBUtil.SMB2_HEADER, 0, dst, dstIndex, SMBUtil.SMB2_HEADER.length);

        SMBUtil.writeInt2(this.creditCharge, dst, dstIndex + 6);
        SMBUtil.writeInt2(this.command, dst, dstIndex + 12);
        SMBUtil.writeInt2(this.credit, dst, dstIndex + 14);
        SMBUtil.writeInt4(this.flags, dst, dstIndex + 16);
        SMBUtil.writeInt4(this.nextCommand, dst, dstIndex + 20);
        SMBUtil.writeInt8(this.mid, dst, dstIndex + 24);

        if ( this.async ) {
            SMBUtil.writeInt8(this.asyncId, dst, dstIndex + 32);
            SMBUtil.writeInt8(this.sessionId, dst, dstIndex + 40);
        }
        else {
            // 4 reserved
            SMBUtil.writeInt4(this.treeId, dst, dstIndex + 36);
            SMBUtil.writeInt8(this.sessionId, dst, dstIndex + 40);
            // + signature
        }

        return Smb2Constants.SMB2_HEADER_LENGTH;
    }


    protected int readHeaderWireFormat ( byte[] buffer, int bufferIndex ) {
        // these are common between SYNC/ASYNC
        SMBUtil.readInt4(buffer, bufferIndex);
        bufferIndex += 4;
        SMBUtil.readInt2(buffer, bufferIndex);
        this.creditCharge = SMBUtil.readInt2(buffer, bufferIndex + 2);
        bufferIndex += 4;
        this.status = SMBUtil.readInt4(buffer, bufferIndex);
        bufferIndex += 4;
        this.command = SMBUtil.readInt2(buffer, bufferIndex);
        this.credit = SMBUtil.readInt2(buffer, bufferIndex + 2);
        bufferIndex += 4;

        this.flags = SMBUtil.readInt4(buffer, bufferIndex);
        bufferIndex += 4;
        this.nextCommand = SMBUtil.readInt4(buffer, bufferIndex);
        bufferIndex += 4;
        this.mid = SMBUtil.readInt8(buffer, bufferIndex);
        bufferIndex += 8;

        if ( ( this.flags & SMB2_FLAGS_ASYNC_COMMAND ) == SMB2_FLAGS_ASYNC_COMMAND ) {
            // async
            this.async = true;
            this.asyncId = SMBUtil.readInt8(buffer, bufferIndex);
            bufferIndex += 8;
            this.sessionId = SMBUtil.readInt8(buffer, bufferIndex);
            bufferIndex += 8;
            System.arraycopy(buffer, bufferIndex, this.signature, 0, 16);
            bufferIndex += 16;
        }
        else {
            // sync
            this.async = false;
            bufferIndex += 4; // reserved
            this.treeId = SMBUtil.readInt4(buffer, bufferIndex);
            bufferIndex += 4;
            this.sessionId = SMBUtil.readInt8(buffer, bufferIndex);
            bufferIndex += 8;
            System.arraycopy(buffer, bufferIndex, this.signature, 0, 16);
            bufferIndex += 16;
        }

        return Smb2Constants.SMB2_HEADER_LENGTH;
    }


    boolean isResponse () {
        return ( this.flags & SMB2_FLAGS_SERVER_TO_REDIR ) == SMB2_FLAGS_SERVER_TO_REDIR;
    }


    protected abstract int writeBytesWireFormat ( byte[] dst, int dstIndex );


    protected abstract int readBytesWireFormat ( byte[] buffer, int bufferIndex ) throws SMBProtocolDecodingException;


    @Override
    public int hashCode () {
        return (int) this.mid;
    }


    @Override
    public boolean equals ( Object obj ) {
        return obj instanceof ServerMessageBlock2 && ( (ServerMessageBlock2) obj ).mid == this.mid;
    }


    @Override
    public String toString () {
        String c;
        switch ( this.command ) {

        case SMB2_NEGOTIATE:
            c = "SMB2_NEGOTIATE";
            break;
        case SMB2_SESSION_SETUP:
            c = "SMB2_SESSION_SETUP";
            break;
        case SMB2_LOGOFF:
            c = "SMB2_LOGOFF";
            break;
        case SMB2_TREE_CONNECT:
            c = "SMB2_TREE_CONNECT";
            break;
        case SMB2_TREE_DISCONNECT:
            c = "SMB2_TREE_DISCONNECT";
            break;
        case SMB2_CREATE:
            c = "SMB2_CREATE";
            break;
        case SMB2_CLOSE:
            c = "SMB2_CLOSE";
            break;
        case SMB2_FLUSH:
            c = "SMB2_FLUSH";
            break;
        case SMB2_READ:
            c = "SMB2_READ";
            break;
        case SMB2_WRITE:
            c = "SMB2_WRITE";
            break;
        case SMB2_LOCK:
            c = "SMB2_LOCK";
            break;
        case SMB2_IOCTL:
            c = "SMB2_IOCTL";
            break;
        case SMB2_CANCEL:
            c = "SMB2_CANCEL";
            break;
        case SMB2_ECHO:
            c = "SMB2_ECHO";
            break;
        case SMB2_QUERY_DIRECTORY:
            c = "SMB2_QUERY_DIRECTORY";
            break;
        case SMB2_CHANGE_NOTIFY:
            c = "SMB2_CHANGE_NOTIFY";
            break;
        case SMB2_QUERY_INFO:
            c = "SMB2_QUERY_INFO";
            break;
        case SMB2_SET_INFO:
            c = "SMB2_SET_INFO";
            break;
        case SMB2_OPLOCK_BREAK:
            c = "SMB2_OPLOCK_BREAK";
            break;
        default:
            c = "UNKNOWN";
        }
        String str = this.status == 0 ? "0" : SmbException.getMessageByCode(this.status);
        return new String(
            "command=" + c + ",status=" + str + ",flags=0x" + Hexdump.toHexString(this.flags, 4) + ",mid=" + this.mid + ",wordCount=" + this.wordCount
                    + ",byteCount=" + this.byteCount);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy