jcifs.internal.smb1.AndXServerMessageBlock Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jcifs-ng Show documentation
Show all versions of jcifs-ng Show documentation
A pure-java CIFS/SMB client library
/* jcifs smb client library in Java
* Copyright (C) 2000 "Michael B. Allen"
*
* 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.smb1;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jcifs.Configuration;
import jcifs.RuntimeCIFSException;
import jcifs.internal.SMBProtocolDecodingException;
import jcifs.internal.smb1.com.SmbComNTCreateAndXResponse;
import jcifs.internal.util.SMBUtil;
import jcifs.util.Hexdump;
/**
*
*/
public abstract class AndXServerMessageBlock extends ServerMessageBlock {
private static final Logger log = LoggerFactory.getLogger(AndXServerMessageBlock.class);
private static final int ANDX_COMMAND_OFFSET = 1;
private static final int ANDX_RESERVED_OFFSET = 2;
private static final int ANDX_OFFSET_OFFSET = 3;
private byte andxCommand = (byte) 0xFF;
private int andxOffset = 0;
private ServerMessageBlock andx;
protected AndXServerMessageBlock ( Configuration config, byte command, String name, ServerMessageBlock andx ) {
super(config, command, name);
this.andx = andx;
if ( andx != null ) {
this.andxCommand = (byte) andx.getCommand();
}
}
protected AndXServerMessageBlock ( Configuration config, byte command ) {
this(config, command, null);
}
protected AndXServerMessageBlock ( Configuration config, byte command, ServerMessageBlock andx ) {
super(config, command);
this.andx = andx;
if ( andx != null ) {
this.andxCommand = (byte) andx.getCommand();
}
}
protected AndXServerMessageBlock ( Configuration config ) {
this(config, null);
}
protected AndXServerMessageBlock ( Configuration config, ServerMessageBlock andx ) {
super(config);
this.andx = andx;
if ( andx != null ) {
this.andxCommand = (byte) andx.getCommand();
}
}
/**
* @return the andx
*/
public final ServerMessageBlock getAndx () {
return this.andx;
}
/**
* {@inheritDoc}
*
* @see jcifs.internal.smb1.ServerMessageBlock#getNext()
*/
@Override
public ServerMessageBlock getNext () {
return this.andx;
}
/**
* {@inheritDoc}
*
* @see jcifs.internal.CommonServerMessageBlockResponse#getNextResponse()
*/
@Override
public ServerMessageBlock getNextResponse () {
return this.andx;
}
protected int getBatchLimit ( Configuration cfg, byte cmd ) {
/*
* the default limit is 0 batched messages before this
* one, meaning this message cannot be batched.
*/
return 0;
}
/*
* We overload this method from ServerMessageBlock because
* we want writeAndXWireFormat to write the parameterWords
* and bytes. This is so we can write batched smbs because
* all but the first smb of the chaain do not have a header
* and therefore we do not want to writeHeaderWireFormat. We
* just recursivly call writeAndXWireFormat.
*/
@Override
public int encode ( byte[] dst, int dstIndex ) {
int start = this.headerStart = dstIndex;
dstIndex += writeHeaderWireFormat(dst, dstIndex);
dstIndex += writeAndXWireFormat(dst, dstIndex);
this.length = dstIndex - start;
if ( this.digest != null ) {
this.digest.sign(dst, this.headerStart, this.length, this, this.getResponse());
}
return this.length;
}
/*
* We overload this because we want readAndXWireFormat to
* read the parameter words and bytes. This is so when
* commands are batched together we can recursivly call
* readAndXWireFormat without reading the non-existent header.
*/
@Override
public int decode ( byte[] buffer, int bufferIndex ) throws SMBProtocolDecodingException {
int start = this.headerStart = bufferIndex;
bufferIndex += readHeaderWireFormat(buffer, bufferIndex);
bufferIndex += readAndXWireFormat(buffer, bufferIndex);
int len = bufferIndex - start;
this.length = len;
if ( isRetainPayload() ) {
byte[] payload = new byte[len];
System.arraycopy(buffer, 4, payload, 0, len);
setRawPayload(payload);
}
if ( !verifySignature(buffer, 4, len) ) {
throw new SMBProtocolDecodingException("Signature verification failed for " + this.getClass().getName());
}
return len;
}
protected int writeAndXWireFormat ( byte[] dst, int dstIndex ) {
int start = dstIndex;
this.wordCount = writeParameterWordsWireFormat(dst, start + ANDX_OFFSET_OFFSET + 2);
this.wordCount += 4; // for command, reserved, and offset
dstIndex += this.wordCount + 1;
this.wordCount /= 2;
dst[ start ] = (byte) ( this.wordCount & 0xFF );
this.byteCount = writeBytesWireFormat(dst, dstIndex + 2);
dst[ dstIndex++ ] = (byte) ( this.byteCount & 0xFF );
dst[ dstIndex++ ] = (byte) ( ( this.byteCount >> 8 ) & 0xFF );
dstIndex += this.byteCount;
/*
* Normally, without intervention everything would batch
* with everything else. If the below clause evaluates true
* the andx command will not be written and therefore the
* response will not read a batched command and therefore
* the 'received' member of the response object will not
* be set to true indicating the send and sendTransaction
* methods that the next part should be sent. This is a
* very indirect and simple batching control mechanism.
*/
if ( this.andx == null || getConfig().isUseBatching() || this.batchLevel >= getBatchLimit(getConfig(), (byte) this.andx.getCommand()) ) {
this.andxCommand = (byte) 0xFF;
this.andx = null;
dst[ start + ANDX_COMMAND_OFFSET ] = (byte) 0xFF;
dst[ start + ANDX_RESERVED_OFFSET ] = (byte) 0x00;
// dst[start + ANDX_OFFSET_OFFSET] = (byte)0x00;
// dst[start + ANDX_OFFSET_OFFSET + 1] = (byte)0x00;
dst[ start + ANDX_OFFSET_OFFSET ] = (byte) 0xde;
dst[ start + ANDX_OFFSET_OFFSET + 1 ] = (byte) 0xde;
// andx not used; return
return dstIndex - start;
}
/*
* The message provided to batch has a batchLimit that is
* higher than the current batchLevel so we will now encode
* that chained message. Before doing so we must increment
* the batchLevel of the andx message in case it itself is an
* andx message and needs to perform the same check as above.
*/
this.andx.batchLevel = this.batchLevel + 1;
dst[ start + ANDX_COMMAND_OFFSET ] = this.andxCommand;
dst[ start + ANDX_RESERVED_OFFSET ] = (byte) 0x00;
this.andxOffset = dstIndex - this.headerStart;
SMBUtil.writeInt2(this.andxOffset, dst, start + ANDX_OFFSET_OFFSET);
this.andx.setUseUnicode(this.isUseUnicode());
if ( this.andx instanceof AndXServerMessageBlock ) {
/*
* A word about communicating header info to andx smbs
*
* This is where we recursively invoke the provided andx smb
* object to write it's parameter words and bytes to our outgoing
* array. Incedentally when these andx smbs are created they are not
* necessarily populated with header data because they're not writing
* the header, only their body. But for whatever reason one might wish
* to populate fields if the writeXxx operation needs this header data
* for whatever reason. I copy over the uid here so it appears correct
* in logging output. Logging of andx segments of messages inadvertantly
* print header information because of the way toString always makes a
* super.toString() call(see toString() at the end of all smbs classes).
*/
this.andx.uid = this.uid;
dstIndex += ( (AndXServerMessageBlock) this.andx ).writeAndXWireFormat(dst, dstIndex);
}
else {
// the andx smb is not of type andx so lets just write it here and
// were done.
int andxStart = dstIndex;
this.andx.wordCount = this.andx.writeParameterWordsWireFormat(dst, dstIndex);
dstIndex += this.andx.wordCount + 1;
this.andx.wordCount /= 2;
dst[ andxStart ] = (byte) ( this.andx.wordCount & 0xFF );
this.andx.byteCount = this.andx.writeBytesWireFormat(dst, dstIndex + 2);
dst[ dstIndex++ ] = (byte) ( this.andx.byteCount & 0xFF );
dst[ dstIndex++ ] = (byte) ( ( this.andx.byteCount >> 8 ) & 0xFF );
dstIndex += this.andx.byteCount;
}
return dstIndex - start;
}
protected int readAndXWireFormat ( byte[] buffer, int bufferIndex ) throws SMBProtocolDecodingException {
int start = bufferIndex;
this.wordCount = buffer[ bufferIndex++ ];
if ( this.wordCount != 0 ) {
/*
* these fields are common to all andx commands
* so let's populate them here
*/
this.andxCommand = buffer[ bufferIndex ];
this.andxOffset = SMBUtil.readInt2(buffer, bufferIndex + 2);
if ( this.andxOffset == 0 ) { /* Snap server workaround */
this.andxCommand = (byte) 0xFF;
}
/*
* no point in calling readParameterWordsWireFormat if there are no more
* parameter words. besides, win98 doesn't return "OptionalSupport" field
*/
if ( this.wordCount > 2 ) {
readParameterWordsWireFormat(buffer, bufferIndex + 4);
/*
* The SMB_COM_NT_CREATE_ANDX response wordCount is wrong. There's an
* extra 16 bytes for some "Offline Files (CSC or Client Side Caching)"
* junk. We need to bump up the wordCount here so that this method returns
* the correct number of bytes for signing purposes. Otherwise we get a
* signing verification failure.
*/
if ( this.getCommand() == SMB_COM_NT_CREATE_ANDX && ( (SmbComNTCreateAndXResponse) this ).isExtended()
&& ( (SmbComNTCreateAndXResponse) this ).getFileType() != 1 ) {
this.wordCount += 8;
}
}
bufferIndex = start + 1 + ( this.wordCount * 2 );
}
this.byteCount = SMBUtil.readInt2(buffer, bufferIndex);
bufferIndex += 2;
if ( this.byteCount != 0 ) {
// TODO: is this really correct?
int n = readBytesWireFormat(buffer, bufferIndex);
if ( n != this.byteCount && log.isTraceEnabled() ) {
log.trace("Short read, have " + n + ", want " + this.byteCount);
}
bufferIndex += this.byteCount;
}
/*
* if there is an andx and it itself is an andx then just recur by
* calling this method for it. otherwise just read it's parameter words
* and bytes as usual. Note how we can't just call andx.readWireFormat
* because there's no header.
*/
if ( this.errorCode != 0 || this.andxCommand == (byte) 0xFF ) {
this.andxCommand = (byte) 0xFF;
this.andx = null;
}
else if ( this.andx == null ) {
this.andxCommand = (byte) 0xFF;
throw new RuntimeCIFSException("no andx command supplied with response");
}
else {
/*
* Set bufferIndex according to andxOffset
*/
bufferIndex = this.headerStart + this.andxOffset;
this.andx.headerStart = this.headerStart;
this.andx.setCommand(this.andxCommand);
this.andx.setErrorCode(getErrorCode());
this.andx.setFlags(getFlags());
this.andx.setFlags2(getFlags2());
this.andx.setTid(getTid());
this.andx.setPid(getPid());
this.andx.setUid(getUid());
this.andx.setMid(getMid());
this.andx.setUseUnicode(this.isUseUnicode());
if ( this.andx instanceof AndXServerMessageBlock ) {
bufferIndex += ( (AndXServerMessageBlock) this.andx ).readAndXWireFormat(buffer, bufferIndex);
}
else {
/*
* Just a plain smb. Read it as normal.
*/
buffer[ bufferIndex++ ] = (byte) ( this.andx.wordCount & 0xFF );
if ( this.andx.wordCount != 0 ) {
/*
* no point in calling readParameterWordsWireFormat if there are no more
* parameter words. besides, win98 doesn't return "OptionalSupport" field
*/
if ( this.andx.wordCount > 2 ) {
bufferIndex += this.andx.readParameterWordsWireFormat(buffer, bufferIndex);
}
}
this.andx.byteCount = SMBUtil.readInt2(buffer, bufferIndex);
bufferIndex += 2;
if ( this.andx.byteCount != 0 ) {
this.andx.readBytesWireFormat(buffer, bufferIndex);
bufferIndex += this.andx.byteCount;
}
}
this.andx.received();
}
return bufferIndex - start;
}
@Override
public String toString () {
return new String(super.toString() + ",andxCommand=0x" + Hexdump.toHexString(this.andxCommand, 2) + ",andxOffset=" + this.andxOffset);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy