/* jcifs smb client library in Java * Copyright (C) 2000 "Michael B. Allen" <jcifs at samba dot org> * * 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.smb; import java.io.InputStream; import java.io.IOException; import jcifs.util.Hexdump; abstract class AndXServerMessageBlock extends ServerMessageBlock { 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; ServerMessageBlock andx = null; AndXServerMessageBlock() { } AndXServerMessageBlock( ServerMessageBlock andx ) { if (andx != null) { this.andx = andx; andxCommand = andx.command; } } int getBatchLimit( byte command ) { /* 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. */ int encode( byte[] dst, int dstIndex ) { int start = headerStart = dstIndex; dstIndex += writeHeaderWireFormat( dst, dstIndex ); dstIndex += writeAndXWireFormat( dst, dstIndex ); length = dstIndex - start; if( digest != null ) { digest.sign( dst, headerStart, length, this, response ); } return 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. */ int decode( byte[] buffer, int bufferIndex ) { int start = headerStart = bufferIndex; bufferIndex += readHeaderWireFormat( buffer, bufferIndex ); bufferIndex += readAndXWireFormat( buffer, bufferIndex ); length = bufferIndex - start; return length; } int writeAndXWireFormat( byte[] dst, int dstIndex ) { int start = dstIndex; wordCount = writeParameterWordsWireFormat( dst, start + ANDX_OFFSET_OFFSET + 2 ); wordCount += 4; // for command, reserved, and offset dstIndex += wordCount + 1; wordCount /= 2; dst[start] = (byte)( wordCount & 0xFF ); byteCount = writeBytesWireFormat( dst, dstIndex + 2 ); dst[dstIndex++] = (byte)( byteCount & 0xFF ); dst[dstIndex++] = (byte)(( byteCount >> 8 ) & 0xFF ); dstIndex += 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( andx == null || USE_BATCHING == false || batchLevel >= getBatchLimit( andx.command )) { andxCommand = (byte)0xFF; 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. */ andx.batchLevel = batchLevel + 1; dst[start + ANDX_COMMAND_OFFSET] = andxCommand; dst[start + ANDX_RESERVED_OFFSET] = (byte)0x00; andxOffset = dstIndex - headerStart; writeInt2( andxOffset, dst, start + ANDX_OFFSET_OFFSET ); andx.useUnicode = useUnicode; if( 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). */ andx.uid = uid; dstIndex += ((AndXServerMessageBlock)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; andx.wordCount = andx.writeParameterWordsWireFormat( dst, dstIndex ); dstIndex += andx.wordCount + 1; andx.wordCount /= 2; dst[andxStart] = (byte)( andx.wordCount & 0xFF ); andx.byteCount = andx.writeBytesWireFormat( dst, dstIndex + 2 ); dst[dstIndex++] = (byte)( andx.byteCount & 0xFF ); dst[dstIndex++] = (byte)(( andx.byteCount >> 8 ) & 0xFF ); dstIndex += andx.byteCount; } return dstIndex - start; } int readAndXWireFormat( byte[] buffer, int bufferIndex ) { int start = bufferIndex; wordCount = buffer[bufferIndex++]; if( wordCount != 0 ) { /* * these fields are common to all andx commands * so let's populate them here */ andxCommand = buffer[bufferIndex]; andxOffset = readInt2( buffer, bufferIndex + 2 ); if( andxOffset == 0 ) { /* Snap server workaround */ andxCommand = (byte)0xFF; } /* * no point in calling readParameterWordsWireFormat if there are no more * parameter words. besides, win98 doesn't return "OptionalSupport" field */ if( 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 (command == SMB_COM_NT_CREATE_ANDX && ((SmbComNTCreateAndXResponse)this).isExtended) wordCount += 8; } bufferIndex = start + 1 + (wordCount * 2); } byteCount = readInt2( buffer, bufferIndex ); bufferIndex += 2; if (byteCount != 0) { int n; n = readBytesWireFormat( buffer, bufferIndex ); bufferIndex += 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( errorCode != 0 || andxCommand == (byte)0xFF ) { andxCommand = (byte)0xFF; andx = null; } else if( andx == null ) { andxCommand = (byte)0xFF; throw new RuntimeException( "no andx command supplied with response" ); } else { /* * Set bufferIndex according to andxOffset */ bufferIndex = headerStart + andxOffset; andx.headerStart = headerStart; andx.command = andxCommand; andx.errorCode = errorCode; andx.flags = flags; andx.flags2 = flags2; andx.tid = tid; andx.pid = pid; andx.uid = uid; andx.mid = mid; andx.useUnicode = useUnicode; if( andx instanceof AndXServerMessageBlock ) { bufferIndex += ((AndXServerMessageBlock)andx).readAndXWireFormat( buffer, bufferIndex ); } else { /* * Just a plain smb. Read it as normal. */ buffer[bufferIndex++] = (byte)( andx.wordCount & 0xFF ); if( andx.wordCount != 0 ) { /* * no point in calling readParameterWordsWireFormat if there are no more * parameter words. besides, win98 doesn't return "OptionalSupport" field */ if( andx.wordCount > 2 ) { bufferIndex += andx.readParameterWordsWireFormat( buffer, bufferIndex ); } } andx.byteCount = readInt2( buffer, bufferIndex ); bufferIndex += 2; if( andx.byteCount != 0 ) { andx.readBytesWireFormat( buffer, bufferIndex ); bufferIndex += andx.byteCount; } } andx.received = true; } return bufferIndex - start; } public String toString() { return new String( super.toString() + ",andxCommand=0x" + Hexdump.toHexString( andxCommand, 2 ) + ",andxOffset=" + andxOffset ); } }