/* * Copyright (C) 2005 Alfresco, Inc. * * Licensed under the Mozilla Public License version 1.1 * with a permitted attribution clause. You may obtain a * copy of the License at * * http://www.alfresco.org/legal/license.txt * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific * language governing permissions and limitations under the * License. */ package org.alfresco.filesys.smb.server; import java.io.IOException; import org.alfresco.filesys.netbios.RFCNetBIOSProtocol; import org.alfresco.filesys.smb.PacketType; import org.alfresco.filesys.smb.TransactBuffer; import org.alfresco.filesys.util.DataBuffer; import org.alfresco.filesys.util.DataPacker; /** * SMB server transact packet class */ class SMBSrvTransPacket extends SMBTransPacket { // Define the number of standard parameters for a server response private static final int StandardParamsResponse = 10; // Offset to the setup response paramaters protected static final int SetupOffsetResponse = PARAMWORDS + (StandardParamsResponse * 2); /** * Construct an SMB transaction packet * * @param buf Buffer that contains the SMB transaction packet. */ public SMBSrvTransPacket(byte[] buf) { super(buf); } /** * Construct an SMB transaction packet * * @param siz Size of packet to allocate. */ public SMBSrvTransPacket(int siz) { super(siz); // Set the multiplex id for this transaction setMultiplexId(getNextMultiplexId()); } /** * Initialize the transact reply parameters. * * @param pkt Reply SMB packet. * @param prmCnt Count of returned parameter bytes. * @param prmPos Starting offset to the parameter block. * @param dataCnt Count of returned data bytes. * @param dataPos Starting offset to the data block. */ public final static void initTransactReply(SMBSrvPacket pkt, int prmCnt, int prmPos, int dataCnt, int dataPos) { // Set the total parameter words pkt.setParameterCount(10); // Set the total parameter/data bytes pkt.setParameter(0, prmCnt); pkt.setParameter(1, dataCnt); // Clear the reserved parameter pkt.setParameter(2, 0); // Set the parameter byte count/offset for this packet pkt.setParameter(3, prmCnt); pkt.setParameter(4, prmPos - RFCNetBIOSProtocol.HEADER_LEN); // Set the parameter displacement pkt.setParameter(5, 0); // Set the data byte count/offset for this packet pkt.setParameter(6, dataCnt); pkt.setParameter(7, dataPos - RFCNetBIOSProtocol.HEADER_LEN); // Set the data displacement pkt.setParameter(8, 0); // Set up word count pkt.setParameter(9, 0); } /** * Calculate the data item size from the data descriptor string. * * @param desc java.lang.String * @return int */ protected final static int CalculateDataItemSize(String desc) { // Scan the data descriptor string and calculate the data item size int len = 0; int pos = 0; while (pos < desc.length()) { // Get the current data item type char dtype = desc.charAt(pos++); int dlen = 1; // Check if a data length has been specified if (pos < desc.length() && Character.isDigit(desc.charAt(pos))) { // Convert the data length string int numlen = 1; int numpos = pos + 1; while (numpos < desc.length() && Character.isDigit(desc.charAt(numpos++))) numlen++; // Set the data length dlen = Integer.parseInt(desc.substring(pos, pos + numlen)); // Update the descriptor string position pos = numpos - 1; } // Convert the current data item switch (dtype) { // Word (16 bit) data type case 'W': len += 2; break; // Integer (32 bit) data type case 'D': len += 4; break; // Byte data type, may be multiple bytes if 'B' case 'B': len += dlen; break; // Null terminated string data type, offset into buffer only case 'z': len += 4; break; // Skip 'n' bytes in the buffer case '.': len += dlen; break; // Integer (32 bit) data type converted to a date/time value case 'T': len += 4; break; } // end switch data type } // end while descriptor string // Return the data length of each item return len; } /** * Return the offset to the data block within the SMB packet. The data block is word aligned * within the byte buffer area of the SMB packet. This method must be called after the parameter * count has been set. * * @param prmLen Parameter block length, in bytes. * @return int Offset to the data block area. */ public final int getDataBlockOffset(int prmLen) { // Get the position of the parameter block int pos = getParameterBlockOffset() + prmLen; if ((pos & 0x01) != 0) pos++; return pos; } /** * Return the data block offset. * * @return int Offset to data block within packet. */ public final int getRxDataBlock() { return getParameter(12) + RFCNetBIOSProtocol.HEADER_LEN; } /** * Return the received transaction data block length. * * @return int */ public final int getRxDataBlockLength() { return getParameter(11); } /** * Get the required transact parameter word (16 bit). * * @param prmIdx int * @return int */ public final int getRxParameter(int prmIdx) { // Get the parameter block offset int pos = getRxParameterBlock(); // Get the required transact parameter word. pos += prmIdx * 2; // 16 bit words return DataPacker.getIntelShort(getBuffer(), pos); } /** * Return the position of the parameter block within the received packet. * * @param prmblk Array to unpack the parameter block words into. */ public final int getRxParameterBlock() { // Get the offset to the parameter words, add the NetBIOS header length // to the offset. return getParameter(10) + RFCNetBIOSProtocol.HEADER_LEN; } /** * Return the received transaction parameter block length. * * @return int */ public final int getRxParameterBlockLength() { return getParameter(9); } /** * Return the received transaction setup parameter count. * * @return int */ public final int getRxParameterCount() { return getParameterCount() - STD_PARAMS; } /** * Get the required transact parameter int value (32-bit). * * @param prmIdx int * @return int */ public final int getRxParameterInt(int prmIdx) { // Get the parameter block offset int pos = getRxParameterBlock(); // Get the required transact parameter word. pos += prmIdx * 2; // 16 bit words return DataPacker.getIntelInt(getBuffer(), pos); } /** * Get the required transact parameter string. * * @param pos Offset to the string within the parameter block. * @param uni Unicode if true, else ASCII * @return int */ public final String getRxParameterString(int pos, boolean uni) { // Get the parameter block offset pos += getRxParameterBlock(); // Get the transact parameter string byte[] buf = getBuffer(); int len = (buf[pos++] & 0x00FF); return DataPacker.getString(buf, pos, len, uni); } /** * Get the required transact parameter string. * * @param pos Offset to the string within the parameter block. * @param len Length of the string. * @param uni Unicode if true, else ASCII * @return int */ public final String getRxParameterString(int pos, int len, boolean uni) { // Get the parameter block offset pos += getRxParameterBlock(); // Get the transact parameter string byte[] buf = getBuffer(); return DataPacker.getString(buf, pos, len, uni); } /** * Return the received transaction name. * * @return java.lang.String */ public final String getRxTransactName() { // Check if the transaction has a name if (getCommand() == PacketType.Transaction2) return ""; // Unpack the transaction name string int pos = getByteOffset(); return DataPacker.getString(getBuffer(), pos, getByteCount()); } /** * Return the setup parameter count * * @return int */ public final int getSetupCount() { return getParameter(13) & 0xFF; } /** * Return the buffer offset to the setup parameters * * @return int */ public final int getSetupOffset() { return WORDCNT + 29; // 14 setup words + word count byte } /** * Return the specified transaction setup parameter. * * @param idx Setup parameter index. * @return int */ public final int getSetupParameter(int idx) { // Check if the setup parameter index is valid if (idx >= getRxParameterCount()) throw new java.lang.ArrayIndexOutOfBoundsException(); // Get the setup parameter return getParameter(idx + STD_PARAMS); } /** * Return the maximum return paramater byte count * * @return int */ public final int getMaximumReturnParameterCount() { return getParameter(2); } /** * Return the maximum return data byte count * * @return int */ public final int getMaximumReturnDataCount() { return getParameter(3); } /** * Return the maximum return setup count * * @return int */ public final int getMaximumReturnSetupCount() { return getParameter(4); } /** * Return the specified transaction setup parameter 32bit value. * * @param idx Setup parameter index. * @return int */ public final int getSetupParameterInt(int idx) { // Check if the setup parameter index is valid if (idx >= getRxParameterCount()) throw new java.lang.ArrayIndexOutOfBoundsException(); // Get the setup parameter return getParameterLong(idx + STD_PARAMS); } /** * Set the total parameter block length, in bytes * * @param cnt int */ public final void setTotalParameterCount(int cnt) { setParameter(0, cnt); } /** * Set the total data block length, in bytes * * @param cnt int */ public final void setTotalDataCount(int cnt) { setParameter(1, cnt); } /** * Set the parameter block count for this packet * * @param len int */ public final void setParameterBlockCount(int len) { setParameter(3, len); } /** * Set the parameter block offset * * @param off int */ public final void setParameterBlockOffset(int off) { setParameter(4, off != 0 ? off - RFCNetBIOSProtocol.HEADER_LEN : 0); } /** * Set the parameter block displacement within the total parameter block * * @param disp int */ public final void setParameterBlockDisplacement(int disp) { setParameter(5, disp); } /** * Set the data block count for this packet * * @param len int */ public final void setDataBlockCount(int len) { setParameter(6, len); } /** * Set the data block offset, from the start of the packet * * @param off int */ public final void setDataBlockOffset(int off) { setParameter(7, off != 0 ? off - RFCNetBIOSProtocol.HEADER_LEN : 0); } /** * Set the data block displacement within the total data block * * @param disp int */ public final void setDataBlockDisplacement(int disp) { setParameter(8, disp); } /** * Send one or more transaction response SMBs to the client * * @param sess SMBSrvSession * @param tbuf TransactBuffer * @exception java.io.IOException If an I/O error occurs. */ protected final void doTransactionResponse(SMBSrvSession sess, TransactBuffer tbuf) throws IOException { // Initialize the transaction response packet setCommand(tbuf.isType()); // Get the individual buffers from the transact buffer tbuf.setEndOfBuffer(); DataBuffer setupBuf = tbuf.getSetupBuffer(); DataBuffer paramBuf = tbuf.getParameterBuffer(); DataBuffer dataBuf = tbuf.getDataBuffer(); // Set the parameter count if (tbuf.hasSetupBuffer()) setParameterCount(StandardParamsResponse + setupBuf.getLengthInWords()); else setParameterCount(StandardParamsResponse); // Clear the parameters for (int i = 0; i < getParameterCount(); i++) setParameter(i, 0); // Get the total parameter/data block lengths int totParamLen = paramBuf != null ? paramBuf.getLength() : 0; int totDataLen = dataBuf != null ? dataBuf.getLength() : 0; // Initialize the parameters setTotalParameterCount(totParamLen); setTotalDataCount(totDataLen); // Get the available data space within the packet int availBuf = getAvailableLength(); int clientLen = getAvailableLength(sess.getClientMaximumBufferSize()); if (availBuf > clientLen) availBuf = clientLen; // Check if the transaction parameter block and data block will fit within a single request // packet int plen = totParamLen; int dlen = totDataLen; if ((plen + dlen) > availBuf) { // Calculate the parameter/data block sizes to send in the first request packet if (plen > 0) { // Check if the parameter block can fit into the packet if (plen <= availBuf) { // Pack all of the parameter block and fill the remaining buffer with the data // block if (dlen > 0) dlen = availBuf - plen; } else { // Split the parameter/data space in the packet plen = availBuf / 2; dlen = plen; } } else if (dlen > availBuf) { // Fill the packet with the first section of the data block dlen = availBuf; } } // Set the parameter/data block counts for this packet setParameterBlockCount(plen); setDataBlockCount(dlen); // Pack the setup bytes if (setupBuf != null) setupBuf.copyData(getBuffer(), SetupOffsetResponse); // Pack the parameter block int pos = DataPacker.wordAlign(getByteOffset()); setPosition(pos); // Set the parameter block offset, from the start of the SMB packet setParameterBlockCount(plen); setParameterBlockOffset(pos); int packLen = -1; if (paramBuf != null) { // Pack the parameter block packLen = paramBuf.copyData(getBuffer(), pos, plen); // Update the buffer position for the data block pos = DataPacker.longwordAlign(pos + packLen); setPosition(pos); } // Set the data block offset setDataBlockCount(dlen); setDataBlockOffset(pos); // Pack the data block if (dataBuf != null) { // Pack the data block packLen = dataBuf.copyData(getBuffer(), pos, dlen); // Update the end of buffer position setPosition(pos + packLen); } // Set the byte count for the SMB packet setByteCount(); // Send the start of the transaction request sess.sendResponseSMB(this); // Get the available parameter/data block buffer space for the secondary packet availBuf = getAvailableLength(); if (availBuf > clientLen) availBuf = clientLen; // Loop until all parameter/data block data has been sent to the server TransactBuffer rxBuf = null; while ((paramBuf != null && paramBuf.getAvailableLength() > 0) || (dataBuf != null && dataBuf.getAvailableLength() > 0)) { // Setup the NT transaction secondary packet to send the remaining parameter/data blocks setCommand(tbuf.isType()); // Get the remaining parameter/data block lengths plen = paramBuf != null ? paramBuf.getAvailableLength() : 0; dlen = dataBuf != null ? dataBuf.getAvailableLength() : 0; if ((plen + dlen) > availBuf) { // Calculate the parameter/data block sizes to send in the first request packet if (plen > 0) { // Check if the remaining parameter block can fit into the packet if (plen <= availBuf) { // Pack all of the parameter block and fill the remaining buffer with the // data block if (dlen > 0) dlen = availBuf - plen; } else { // Split the parameter/data space in the packet plen = availBuf / 2; dlen = plen; } } else if (dlen > availBuf) { // Fill the packet with the first section of the data block dlen = availBuf; } } // Pack the parameter block data, if any resetBytePointerAlign(); packLen = -1; pos = getPosition(); if (plen > 0 && paramBuf != null) { // Set the parameter block offset, from the start of the SMB packet setParameterBlockOffset(pos); setParameterBlockCount(plen); setParameterBlockDisplacement(paramBuf.getDisplacement()); // Pack the parameter block packLen = paramBuf.copyData(getBuffer(), pos, plen); // Update the buffer position for the data block pos = DataPacker.wordAlign(pos + packLen); setPosition(pos); } else { // No parameter data, clear the count/offset setParameterBlockCount(0); setParameterBlockOffset(pos); } // Pack the data block, if any if (dlen > 0 && dataBuf != null) { // Set the data block offset setDataBlockOffset(pos); setDataBlockCount(dlen); setDataBlockDisplacement(dataBuf.getDisplacement()); // Pack the data block packLen = dataBuf.copyData(getBuffer(), pos, dlen); // Update the end of buffer position setPosition(pos + packLen); } else { // No data, clear the count/offset setDataBlockCount(0); setDataBlockOffset(pos); } // Set the byte count for the SMB packet to set the overall length setByteCount(); // Send the transaction response packet sess.sendResponseSMB(this); } } }