/* * Copyright (C) 2006-2008 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program 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 General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * As a special exception to the terms and conditions of version 2.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * and Open Source Software ("FLOSS") applications as described in Alfresco's * FLOSS exception. You should have recieved a copy of the text describing * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.jlan.smb.server.win32; import java.io.IOException; import org.alfresco.jlan.debug.Debug; import org.alfresco.jlan.netbios.win32.NetBIOS; import org.alfresco.jlan.netbios.win32.Win32NetBIOS; import org.alfresco.jlan.server.core.NoPooledMemoryException; import org.alfresco.jlan.smb.server.CIFSPacketPool; import org.alfresco.jlan.smb.server.PacketHandler; import org.alfresco.jlan.smb.server.SMBSrvPacket; /** * Win32 NetBIOS Packet Handler Class * * <p> * Uses the Win32 Netbios() call to provide the low level session layer for better integration with * Windows. * * @author gkspencer */ public class Win32NetBIOSPacketHandler extends PacketHandler { // Constants // // Receive error encoding and length masks private static final int ReceiveErrorMask = 0xFF000000; private static final int ReceiveLengthMask = 0x0000FFFF; // Network LAN adapter to use private int m_lana; // NetBIOS session id private int m_lsn; /** * Class constructor * * @param lana int * @param lsn int * @param callerName String * @param packetPool CIFSPacketPool */ public Win32NetBIOSPacketHandler(int lana, int lsn, String callerName, CIFSPacketPool packetPool) { super(SMBSrvPacket.PROTOCOL_WIN32NETBIOS, "Win32NB", "WNB", callerName, packetPool); m_lana = lana; m_lsn = lsn; } /** * Return the LANA number * * @return int */ public final int getLANA() { return m_lana; } /** * Return the NetBIOS session id * * @return int */ public final int getLSN() { return m_lsn; } /** * Return the count of available bytes in the receive input stream * * @return int * @exception IOException If a network error occurs. */ public int availableBytes() throws IOException { // Do not know the available byte count return -1; } /** * Read a packet from the client * * @return SMBSrvPacket * @throws IOException */ public SMBSrvPacket readPacket() throws IOException { // As we cannot find the length of the incoming packet we must allocate a full length packet SMBSrvPacket pkt = getPacketPool().allocatePacket( getPacketPool().getLargestSize()); // Wait for a packet on the Win32 NetBIOS session // // As Windows is handling the NetBIOS session layer we only receive the SMB packet. In order // to be compatible with the other packet handlers we allow for the 4 byte header. int pktLen = pkt.getBuffer().length; if ( pktLen > NetBIOS.MaxReceiveSize) pktLen = NetBIOS.MaxReceiveSize; int rxLen = Win32NetBIOS.Receive(m_lana, m_lsn, pkt.getBuffer(), 4, pktLen - 4); if ( (rxLen & ReceiveErrorMask) != 0) { // Check for an incomplete message status code int sts = (rxLen & ReceiveErrorMask) >> 24; if ( sts == NetBIOS.NRC_Incomp) { // DEBUG if ( hasDebug()) Debug.println("Win32NetBIOSPacketHandle: readPacket() NRC_Incomp error"); // Check if the packet buffer is already at the maximum size (we assume the maximum // size is the maximum that RFC NetBIOS can send which is 17bits) if ( pkt.getBuffer().length < getPacketPool().getMaximumOverSizedAllocation()) { // Allocate a new buffer SMBSrvPacket pkt2 = null; try { // Allocate the maximum over sized packet available, usually 128K pkt2 = getPacketPool().allocatePacket( getPacketPool().getMaximumOverSizedAllocation()); } catch ( NoPooledMemoryException ex) { // Release the original buffer back to the pool getPacketPool().releasePacket( pkt); // Rethrow the pooled memory exception throw ex; } // Copy the first part of the received data to the new buffer System.arraycopy(pkt.getBuffer(), 4, pkt2.getBuffer(), 4, pktLen - 4); // Move the new buffer in as the main packet buffer, release the original buffer getPacketPool().releasePacket( pkt); pkt = pkt2; // DEBUG if ( hasDebug()) Debug.println("readPacket() extended buffer to " + pkt.getBuffer().length); } // Set the original receive size rxLen = (rxLen & ReceiveLengthMask); // Receive the remaining data // // Note: If the second read request is issued with a size of 64K or 64K-4 it returns // with another incomplete status and returns no data. int rxLen2 = Win32NetBIOS.Receive(m_lana, m_lsn, pkt.getBuffer(), rxLen + 4, 32768); if ( (rxLen2 & ReceiveErrorMask) != 0) { sts = (rxLen2 & ReceiveErrorMask) >> 24; throw new IOException("Win32 NetBIOS multi-part receive failed, sts=0x" + sts + ", err=" + NetBIOS.getErrorString(sts)); } // DEBUG if ( hasDebug()) Debug.println("readPacket() rxlen2=" + rxLen2 + ", total read len = " + (rxLen + rxLen2)); // Set the total received data length rxLen += rxLen2; } else { // Release the packet buffer back to the pool getPacketPool().releasePacket( pkt); // Indicate that the session has closed return null; } } // Set the received packet length if ( pkt != null) pkt.setReceivedLength( rxLen); // Return the received packet return pkt; } /** * Write a packet to the client * * @param pkt SMBSrvPacket * @param len int * @paam writeRaw boolean * @throws IOException */ public void writePacket(SMBSrvPacket pkt, int len, boolean writeRaw) throws IOException { // Output the packet on the Win32 NetBIOS session // // As Windows is handling the NetBIOS session layer we do not send the 4 byte header that is // used by the NetBIOS over TCP/IP and native SMB packet handlers. Win32NetBIOS.Send(m_lana, m_lsn, pkt.getBuffer(), 4, len); // Do not check the status, if the session has been closed the next receive will fail } /** * Flush the output socket * * @exception IOException If a network error occurs */ public void flushPacket() throws IOException { // Nothing to do } /** * Close the Win32 NetBIOS packet handler. Hangup the NetBIOS session */ public void closeHandler() { super.closeHandler(); // Hangup the Win32 NetBIOS session Win32NetBIOS.Hangup(m_lana, m_lsn); } }