Exemplo n.º 1
0
        private void ProcessConnectionBuffer(ConnectionState state)
        {
            ISCSIConnectionReceiveBuffer buffer = state.ReceiveBuffer;

            while (buffer.HasCompletePDU())
            {
                ISCSIPDU pdu = null;
                try
                {
                    pdu = buffer.DequeuePDU();
                }
                catch (Exception)
                {
                    throw;
                }

                if (pdu.GetType() == typeof(ISCSIPDU))
                {
                    throw new Exception("Unsupported");
                }
                else
                {
                    ProcessPDU(pdu, state);
                }
            }
        }
Exemplo n.º 2
0
        public bool Write(ushort LUN, ulong sectorIndex, byte[] data, int bytesPerSector)
        {
            if (!m_isConnected)
            {
                throw new InvalidOperationException("iSCSI client is not connected");
            }
            SCSICommandPDU writeCommand = ClientHelper.GetWrite16Command(m_session, m_connection, LUN, sectorIndex, data, bytesPerSector);

            SendPDU(writeCommand);
            ISCSIPDU response = WaitForPDU(writeCommand.InitiatorTaskTag);

            while (response is ReadyToTransferPDU)
            {
                List <SCSIDataOutPDU> requestedData = ClientHelper.GetWriteData(m_session, m_connection, LUN, sectorIndex, data, bytesPerSector, (ReadyToTransferPDU)response);
                foreach (SCSIDataOutPDU dataOut in requestedData)
                {
                    SendPDU(dataOut);
                }
                response = WaitForPDU(writeCommand.InitiatorTaskTag);
            }

            if (response is SCSIResponsePDU)
            {
                if (((SCSIResponsePDU)response).Status == SCSIStatusCodeName.Good)
                {
                    return(true);
                }
            }
            return(false);
        }
Exemplo n.º 3
0
 public static void SetExpStatSN(ISCSIPDU pdu, uint expStatSN)
 {
     if (pdu is NOPOutPDU)
     {
         ((NOPOutPDU)pdu).ExpStatSN = expStatSN;
     }
     else if (pdu is SCSICommandPDU)
     {
         ((SCSICommandPDU)pdu).ExpStatSN = expStatSN;
     }
     else if (pdu is LoginRequestPDU)
     {
         ((LoginRequestPDU)pdu).ExpStatSN = expStatSN;
     }
     else if (pdu is TextRequestPDU)
     {
         ((TextRequestPDU)pdu).ExpStatSN = expStatSN;
     }
     else if (pdu is SCSIDataOutPDU)
     {
         ((SCSIDataOutPDU)pdu).ExpStatSN = expStatSN;
     }
     else if (pdu is LogoutRequestPDU)
     {
         ((LogoutRequestPDU)pdu).ExpStatSN = expStatSN;
     }
 }
Exemplo n.º 4
0
        public void ProcessPDU(ISCSIPDU pdu, StateObject state)
        {
            if (pdu is NOPInPDU)
            {
                if (((NOPInPDU)pdu).TargetTransferTag != 0xFFFFFFFF)
                {
                    // Send NOP-OUT
                    NOPOutPDU response = ClientHelper.GetPingResponse((NOPInPDU)pdu, m_session, m_connection);
                    SendPDU(response);
                    return;
                }
            }

            if (m_connection.StatusNumberingStarted)
            {
                uint?responseStatSN = PDUHelper.GetStatSN(pdu);
                if (m_connection.ExpStatSN == responseStatSN)
                {
                    m_connection.ExpStatSN++;
                }
            }
            lock (m_incomingQueueLock)
            {
                m_incomingQueue.Add(pdu);
            }
        }
Exemplo n.º 5
0
 public static void SetStatSN(ISCSIPDU pdu, uint statSN)
 {
     if (pdu is NOPInPDU)
     {
         ((NOPInPDU)pdu).StatSN = statSN;
     }
     else if (pdu is SCSIResponsePDU)
     {
         ((SCSIResponsePDU)pdu).StatSN = statSN;
     }
     else if (pdu is LoginResponsePDU)
     {
         ((LoginResponsePDU)pdu).StatSN = statSN;
     }
     else if (pdu is TextResponsePDU)
     {
         ((TextResponsePDU)pdu).StatSN = statSN;
     }
     else if (pdu is SCSIDataInPDU && ((SCSIDataInPDU)pdu).StatusPresent) // RFC 3720: StatSN [..] only have meaningful content if the S bit is set to 1
     {
         ((SCSIDataInPDU)pdu).StatSN = statSN;
     }
     else if (pdu is LogoutResponsePDU)
     {
         ((LogoutResponsePDU)pdu).StatSN = statSN;
     }
     else if (pdu is ReadyToTransferPDU)
     {
         ((ReadyToTransferPDU)pdu).StatSN = statSN;
     }
     else if (pdu is RejectPDU)
     {
         ((RejectPDU)pdu).StatSN = statSN;
     }
 }
Exemplo n.º 6
0
 public static void SetExpCmdSN(ISCSIPDU pdu, uint expCmdSN, uint maxCmdSN)
 {
     if (pdu is NOPInPDU)
     {
         ((NOPInPDU)pdu).ExpCmdSN = expCmdSN;
         ((NOPInPDU)pdu).MaxCmdSN = maxCmdSN;
     }
     else if (pdu is SCSIResponsePDU)
     {
         ((SCSIResponsePDU)pdu).ExpCmdSN = expCmdSN;
         ((SCSIResponsePDU)pdu).MaxCmdSN = maxCmdSN;
     }
     else if (pdu is LoginResponsePDU)
     {
         ((LoginResponsePDU)pdu).ExpCmdSN = expCmdSN;
         ((LoginResponsePDU)pdu).MaxCmdSN = maxCmdSN;
     }
     else if (pdu is TextResponsePDU)
     {
         ((TextResponsePDU)pdu).ExpCmdSN = expCmdSN;
         ((TextResponsePDU)pdu).MaxCmdSN = maxCmdSN;
     }
     else if (pdu is SCSIDataInPDU)
     {
         ((SCSIDataInPDU)pdu).ExpCmdSN = expCmdSN;
         ((SCSIDataInPDU)pdu).MaxCmdSN = maxCmdSN;
     }
     else if (pdu is LogoutResponsePDU)
     {
         ((LogoutResponsePDU)pdu).ExpCmdSN = expCmdSN;
         ((LogoutResponsePDU)pdu).MaxCmdSN = maxCmdSN;
     }
     else if (pdu is ReadyToTransferPDU)
     {
         ((ReadyToTransferPDU)pdu).ExpCmdSN = expCmdSN;
         ((ReadyToTransferPDU)pdu).MaxCmdSN = maxCmdSN;
     }
     else if (pdu is RejectPDU)
     {
         ((RejectPDU)pdu).ExpCmdSN = expCmdSN;
         ((RejectPDU)pdu).MaxCmdSN = maxCmdSN;
     }
 }
Exemplo n.º 7
0
 public void SendPDU(ISCSIPDU request)
 {
     try
     {
         if (m_connection.StatusNumberingStarted)
         {
             PDUHelper.SetExpStatSN(request, m_connection.ExpStatSN);
         }
         m_clientSocket.Send(request.GetBytes());
         Log("[{0}][SendPDU] Sent request to target, Operation: {1}, Size: {2}", this.ConnectionIdentifier, (ISCSIOpCodeName)request.OpCode, request.Length);
     }
     catch (SocketException ex)
     {
         Log("[{0}][SendPDU] Failed to send PDU to target (Operation: {1}, Size: {2}), SocketException: {3}", this.ConnectionIdentifier, (ISCSIOpCodeName)request.OpCode, request.Length, ex.Message);
         m_isConnected = false;
     }
     catch (ObjectDisposedException)
     {
         m_isConnected = false;
     }
 }
Exemplo n.º 8
0
        public static void TrySendPDU(StateObject state, ISCSIPDU response)
        {
            Socket clientSocket = state.ClientSocket;

            try
            {
                PDUHelper.SetStatSN(response, state.ConnectionParameters.StatSN);
                PDUHelper.SetExpCmdSN(response, state.SessionParameters.ExpCmdSN, state.SessionParameters.ExpCmdSN + state.SessionParameters.CommandQueueSize);
                if (response is SCSIResponsePDU || (response is SCSIDataInPDU && ((SCSIDataInPDU)response).StatusPresent))
                {
                    state.ConnectionParameters.StatSN++;
                }
                clientSocket.Send(response.GetBytes());
                Log(LogLevel.Debug, "[{0}][TrySendPDU] Sent response to initator, Operation: {1}, Size: {2}", state.ConnectionIdentifier, response.OpCode, response.Length);
            }
            catch (SocketException ex)
            {
                Log(LogLevel.Warning, "[{0}][TrySendPDU] Failed to send response to initator (Operation: {1}, Size: {2}), SocketException: {3}", state.ConnectionIdentifier, response.OpCode, response.Length, ex.Message);
            }
            catch (ObjectDisposedException)
            {
            }
        }
Exemplo n.º 9
0
 public ISCSIPDU WaitForPDU(uint initiatorTaskTag)
 {
     const int TimeOut = 5000;
     Stopwatch stopwatch = new Stopwatch();
     stopwatch.Start();
     while (stopwatch.ElapsedMilliseconds < TimeOut)
     {
         lock (m_incomingQueueLock)
         {
             for (int index = 0; index < m_incomingQueue.Count; index++)
             {
                 ISCSIPDU pdu = m_incomingQueue[index];
                 if (pdu.InitiatorTaskTag == initiatorTaskTag)
                 {
                     m_incomingQueue.RemoveAt(index);
                     return pdu;
                 }
             }
         }
         m_incomingQueueEventHandle.WaitOne(100);
     }
     return null;
 }
Exemplo n.º 10
0
        private bool ValidateCommandNumbering(ISCSIPDU pdu, ConnectionState state)
        {
            if (state.Session == null)
            {
                return(true);
            }

            uint?cmdSN = PDUHelper.GetCmdSN(pdu);

            Log(Severity.Verbose, "[{0}] Received PDU from initiator, Operation: {1}, Size: {2}, CmdSN: {3}", state.ConnectionIdentifier, (ISCSIOpCodeName)pdu.OpCode, pdu.Length, cmdSN);
            // RFC 3720: On any connection, the iSCSI initiator MUST send the commands in increasing order of CmdSN,
            // except for commands that are retransmitted due to digest error recovery and connection recovery.
            if (cmdSN.HasValue)
            {
                if (state.Session.CommandNumberingStarted)
                {
                    if (cmdSN != state.Session.ExpCmdSN)
                    {
                        return(false);
                    }
                }
                else
                {
                    state.Session.ExpCmdSN = cmdSN.Value;
                    state.Session.CommandNumberingStarted = true;
                }

                if (pdu is LogoutRequestPDU || pdu is TextRequestPDU || pdu is SCSICommandPDU || pdu is RejectPDU)
                {
                    if (!pdu.ImmediateDelivery)
                    {
                        state.Session.ExpCmdSN++;
                    }
                }
            }
            return(true);
        }
Exemplo n.º 11
0
        public ISCSIPDU WaitForPDU(uint initiatorTaskTag)
        {
            const int TimeOut  = 5000;
            int       timespan = 0;

            while (timespan < TimeOut)
            {
                lock (m_incomingQueueLock)
                {
                    for (int index = 0; index < m_incomingQueue.Count; index++)
                    {
                        ISCSIPDU pdu = m_incomingQueue[index];
                        if (pdu.InitiatorTaskTag == initiatorTaskTag)
                        {
                            m_incomingQueue.RemoveAt(index);
                            return(pdu);
                        }
                    }
                }
                Thread.Sleep(100);
                timespan += 100;
            }
            return(null);
        }
Exemplo n.º 12
0
 public static uint?GetCmdSN(ISCSIPDU pdu)
 {
     if (pdu is NOPOutPDU)
     {
         return(((NOPOutPDU)pdu).CmdSN);
     }
     else if (pdu is SCSICommandPDU)
     {
         return(((SCSICommandPDU)pdu).CmdSN);
     }
     else if (pdu is LoginRequestPDU)
     {
         return(((LoginRequestPDU)pdu).CmdSN);
     }
     else if (pdu is TextRequestPDU)
     {
         return(((TextRequestPDU)pdu).CmdSN);
     }
     else if (pdu is LogoutRequestPDU)
     {
         return(((LogoutRequestPDU)pdu).CmdSN);
     }
     return(null);
 }
Exemplo n.º 13
0
        public void ProcessPDU(ISCSIPDU pdu, StateObject state)
        {
            Socket clientSocket = state.ClientSocket;

            uint?cmdSN = PDUHelper.GetCmdSN(pdu);

            Log(LogLevel.Debug, "[{0}][ProcessPDU] Received PDU from initiator, Operation: {1}, Size: {2}, CmdSN: {3}", state.ConnectionIdentifier, (ISCSIOpCodeName)pdu.OpCode, pdu.Length, cmdSN);
            // RFC 3720: On any connection, the iSCSI initiator MUST send the commands in increasing order of CmdSN,
            // except for commands that are retransmitted due to digest error recovery and connection recovery.
            if (cmdSN.HasValue)
            {
                if (state.SessionParameters.CommandNumberingStarted)
                {
                    if (cmdSN != state.SessionParameters.ExpCmdSN)
                    {
                        Log(LogLevel.Warning, "[{0}][ProcessPDU] CmdSN outside of expected range", state.ConnectionIdentifier);
                        // We ignore this PDU
                        return;
                    }
                }
                else
                {
                    state.SessionParameters.ExpCmdSN = cmdSN.Value;
                    state.SessionParameters.CommandNumberingStarted = true;
                }

                if (pdu is LogoutRequestPDU || pdu is TextRequestPDU || pdu is SCSICommandPDU || pdu is RejectPDU)
                {
                    if (!pdu.ImmediateDelivery)
                    {
                        state.SessionParameters.ExpCmdSN++;
                    }
                }
            }

            if (pdu is LoginRequestPDU)
            {
                LoginRequestPDU request = (LoginRequestPDU)pdu;
                Log(LogLevel.Information, "[{0}][ReceiveCallback] Login Request, current stage: {1}, next stage: {2}, parameters: {3}", state.ConnectionIdentifier, request.CurrentStage, request.NextStage, KeyValuePairUtils.ToString(request.LoginParameters));
                if (request.TSIH != 0)
                {
                    // RFC 3720: A Login Request with a non-zero TSIH and a CID equal to that of an existing
                    // connection implies a logout of the connection followed by a Login
                    lock (m_activeConnectionsLock)
                    {
                        int existingConnectionIndex = GetStateObjectIndex(m_activeConnections, request.ISID, request.TSIH, request.CID);
                        if (existingConnectionIndex >= 0)
                        {
                            // Perform implicit logout
                            Log(LogLevel.Information, "[{0}][ProcessPDU] Initiating implicit logout", state.ConnectionIdentifier);
                            SocketUtils.ReleaseSocket(m_activeConnections[existingConnectionIndex].ClientSocket);
                            if (m_activeConnections[existingConnectionIndex].Target != null)
                            {
                                lock (m_activeConnections[existingConnectionIndex].Target.IOLock)
                                {
                                    // Wait for pending I/O to complete.
                                }
                            }
                            m_activeConnections.RemoveAt(existingConnectionIndex);
                            Log(LogLevel.Information, "[{0}][ProcessPDU] Implicit logout completed", state.ConnectionIdentifier);
                        }
                    }
                }
                LoginResponsePDU response = ServerResponseHelper.GetLoginResponsePDU(request, m_targets, state.SessionParameters, state.ConnectionParameters, ref state.Target, GetNextTSIH);
                if (state.SessionParameters.IsFullFeaturePhase)
                {
                    state.SessionParameters.ISID   = request.ISID;
                    state.ConnectionParameters.CID = request.CID;
                    lock (m_activeConnectionsLock)
                    {
                        m_activeConnections.Add(state);
                    }
                }
                Log(LogLevel.Information, "[{0}][ReceiveCallback] Login Response parameters: {1}", state.ConnectionIdentifier, KeyValuePairUtils.ToString(response.LoginParameters));
                TrySendPDU(state, response);
            }
            else if (!state.SessionParameters.IsFullFeaturePhase)
            {
                // Before the Full Feature Phase is established, only Login Request and Login Response PDUs are allowed.
                Log(LogLevel.Warning, "[{0}][ProcessPDU] Improper command during login phase, OpCode: 0x{1}", state.ConnectionIdentifier, pdu.OpCode.ToString("x"));
                // A target receiving any PDU except a Login request before the Login phase is started MUST
                // immediately terminate the connection on which the PDU was received.
                // Once the Login phase has started, if the target receives any PDU except a Login request,
                // it MUST send a Login reject (with Status "invalid during login") and then disconnect.
                clientSocket.Dispose();
            }
            else // Logged in
            {
                if (pdu is TextRequestPDU)
                {
                    TextRequestPDU  request  = (TextRequestPDU)pdu;
                    TextResponsePDU response = ServerResponseHelper.GetTextResponsePDU(request, m_targets);
                    TrySendPDU(state, response);
                }
                else if (pdu is LogoutRequestPDU)
                {
                    lock (m_activeConnectionsLock)
                    {
                        int connectionIndex = GetStateObjectIndex(m_activeConnections, state.SessionParameters.ISID, state.SessionParameters.TSIH, state.ConnectionParameters.CID);
                        if (connectionIndex >= 0)
                        {
                            if (m_activeConnections[connectionIndex].Target != null)
                            {
                                lock (m_activeConnections[connectionIndex].Target.IOLock)
                                {
                                    // Wait for pending I/O to complete.
                                }
                            }
                            m_activeConnections.RemoveAt(connectionIndex);
                        }
                    }
                    LogoutRequestPDU  request  = (LogoutRequestPDU)pdu;
                    LogoutResponsePDU response = ServerResponseHelper.GetLogoutResponsePDU(request);
                    TrySendPDU(state, response);
                    clientSocket.Dispose(); // We can close the connection now
                }
                else if (state.SessionParameters.IsDiscovery)
                {
                    // The target MUST ONLY accept text requests with the SendTargets key and a logout
                    // request with the reason "close the session".  All other requests MUST be rejected.
                    Log(LogLevel.Warning, "[{0}][ProcessPDU] Improper command during discovery session, OpCode: 0x{1}", state.ConnectionIdentifier, pdu.OpCode.ToString("x"));
                    RejectPDU reject = new RejectPDU();
                    reject.Reason = RejectReason.ProtocolError;
                    reject.Data   = ByteReader.ReadBytes(pdu.GetBytes(), 0, 48);

                    TrySendPDU(state, reject);
                }
                else if (pdu is NOPOutPDU)
                {
                    NOPOutPDU request = (NOPOutPDU)pdu;
                    if (request.InitiatorTaskTag != 0xFFFFFFFF)
                    {
                        NOPInPDU response = ServerResponseHelper.GetNOPResponsePDU(request);
                        TrySendPDU(state, response);
                    }
                }
                else if (pdu is SCSIDataOutPDU)
                {
                    // FIXME: the iSCSI target layer MUST deliver the commands for execution (to the SCSI execution engin) in the order specified by CmdSN
                    // e.g. read requests should not be executed while previous write request data is being received (via R2T)
                    SCSIDataOutPDU request = (SCSIDataOutPDU)pdu;
                    Log(LogLevel.Debug, "[{0}][ProcessPDU] SCSIDataOutPDU: Target transfer tag: {1}, LUN: {2}, Buffer offset: {3}, Data segment length: {4}, DataSN: {5}, Final: {6}", state.ConnectionIdentifier, request.TargetTransferTag, (ushort)request.LUN, request.BufferOffset, request.DataSegmentLength, request.DataSN, request.Final);
                    ISCSIPDU response = TargetResponseHelper.GetSCSIDataOutResponsePDU(request, state.Target, state.SessionParameters, state.ConnectionParameters);
                    TrySendPDU(state, response);
                }
                else if (pdu is SCSICommandPDU)
                {
                    SCSICommandPDU command = (SCSICommandPDU)pdu;
                    Log(LogLevel.Debug, "[{0}][ProcessPDU] SCSICommandPDU: CmdSN: {1}, LUN: {2}, Data segment length: {3}, Expected Data Transfer Length: {4}, Final: {5}", state.ConnectionIdentifier, command.CmdSN, (ushort)command.LUN, command.DataSegmentLength, command.ExpectedDataTransferLength, command.Final);
                    List <ISCSIPDU> scsiResponseList = TargetResponseHelper.GetSCSIResponsePDU(command, state.Target, state.SessionParameters, state.ConnectionParameters);
                    foreach (ISCSIPDU response in scsiResponseList)
                    {
                        TrySendPDU(state, response);
                        if (!clientSocket.Connected)
                        {
                            return;
                        }
                    }
                }
                else
                {
                    Log(LogLevel.Warning, "[{0}][ProcessPDU] Unsupported command, OpCode: 0x{1}", state.ConnectionIdentifier, pdu.OpCode.ToString("x"));
                }
            }
        }
Exemplo n.º 14
0
        public void ProcessCurrentBuffer(byte[] currentBuffer, StateObject state)
        {
            Socket clientSocket = state.ClientSocket;

            if (state.ConnectionBuffer.Length == 0)
            {
                state.ConnectionBuffer = currentBuffer;
            }
            else
            {
                state.ConnectionBuffer = ByteUtils.Concatenate(state.ConnectionBuffer, currentBuffer);
            }

            // we now have all PDU bytes received so far in state.ConnectionBuffer
            int bytesLeftInBuffer = state.ConnectionBuffer.Length;

            while (bytesLeftInBuffer >= 8)
            {
                int bufferOffset = state.ConnectionBuffer.Length - bytesLeftInBuffer;
                int pduLength    = ISCSIPDU.GetPDULength(state.ConnectionBuffer, bufferOffset);
                if (pduLength > bytesLeftInBuffer)
                {
                    Log(LogLevel.Debug, "[{0}][ProcessCurrentBuffer] Bytes left in receive buffer: {1}", state.ConnectionIdentifier, bytesLeftInBuffer);
                    break;
                }
                else
                {
                    byte[] pduBytes = ByteReader.ReadBytes(state.ConnectionBuffer, bufferOffset, pduLength);
                    bytesLeftInBuffer -= pduLength;
                    ISCSIPDU pdu = null;
                    try
                    {
                        pdu = ISCSIPDU.GetPDU(pduBytes);
                    }
                    catch (Exception ex)
                    {
                        Log(LogLevel.Error, "[{0}][ProcessCurrentBuffer] Failed to read PDU (Exception: {1})", state.ConnectionIdentifier, ex.Message);
                        RejectPDU reject = new RejectPDU();
                        reject.Reason = RejectReason.InvalidPDUField;
                        reject.Data   = ByteReader.ReadBytes(pduBytes, 0, 48);

                        TrySendPDU(state, reject);
                    }

                    if (pdu != null)
                    {
                        if (pdu.GetType() == typeof(ISCSIPDU))
                        {
                            Log(LogLevel.Warning, "[{0}][ProcessCurrentBuffer] Unsupported PDU (0x{1})", state.ConnectionIdentifier, pdu.OpCode.ToString("X"));
                            // Unsupported PDU
                            RejectPDU reject = new RejectPDU();
                            reject.InitiatorTaskTag = pdu.InitiatorTaskTag;
                            reject.Reason           = RejectReason.CommandNotSupported;
                            reject.Data             = ByteReader.ReadBytes(pduBytes, 0, 48);
                            TrySendPDU(state, reject);
                        }
                        else
                        {
                            ProcessPDU(pdu, state);
                        }
                    }

                    if (!clientSocket.Connected)
                    {
                        // Do not continue to process the buffer if the other side closed the connection
                        if (bytesLeftInBuffer > 0)
                        {
                            Log(LogLevel.Warning, "[{0}][ProcessCurrentBuffer] Buffer processing aborted, bytes left in receive buffer: {1}", state.ConnectionIdentifier, bytesLeftInBuffer);
                        }
                        return;
                    }
                }
            }

            if (bytesLeftInBuffer > 0)
            {
                state.ConnectionBuffer = ByteReader.ReadBytes(state.ConnectionBuffer, state.ConnectionBuffer.Length - bytesLeftInBuffer, bytesLeftInBuffer);
            }
            else
            {
                state.ConnectionBuffer = new byte[0];
            }
        }
Exemplo n.º 15
0
        private void ProcessConnectionBuffer(ConnectionState state)
        {
            Socket clientSocket = state.ClientSocket;

            ISCSIConnectionReceiveBuffer buffer = state.ReceiveBuffer;

            while (buffer.HasCompletePDU())
            {
                ISCSIPDU pdu = null;
                try
                {
                    pdu = buffer.DequeuePDU();
                }
                catch (Exception ex)
                {
                    byte[] pduBytes = buffer.DequeuePDUBytes();
                    Log(Severity.Error, "[{0}] Failed to read PDU (Exception: {1})", state.ConnectionIdentifier, ex.Message);
                    RejectPDU reject = new RejectPDU();
                    reject.Reason = RejectReason.InvalidPDUField;
                    reject.Data   = ByteReader.ReadBytes(pduBytes, 0, 48);

                    state.SendQueue.Enqueue(reject);
                }

                if (pdu != null)
                {
                    if (pdu.GetType() == typeof(ISCSIPDU))
                    {
                        Log(Severity.Error, "[{0}][ProcessCurrentBuffer] Unsupported PDU (0x{1})", state.ConnectionIdentifier, pdu.OpCode.ToString("X"));
                        // Unsupported PDU
                        RejectPDU reject = new RejectPDU();
                        reject.InitiatorTaskTag = pdu.InitiatorTaskTag;
                        reject.Reason           = RejectReason.CommandNotSupported;
                        reject.Data             = ByteReader.ReadBytes(pdu.GetBytes(), 0, 48);
                        state.SendQueue.Enqueue(reject);
                    }
                    else
                    {
                        bool valid = ValidateCommandNumbering(pdu, state);
                        if (valid)
                        {
                            ProcessPDU(pdu, state);
                        }
                        else
                        {
                            // We ignore this PDU
                            Log(Severity.Warning, "[{0}] Ignoring PDU with CmdSN outside of expected range", state.ConnectionIdentifier);
                        }
                    }
                }

                if (!clientSocket.Connected)
                {
                    // Do not continue to process the buffer if the other side closed the connection
                    if (buffer.BytesInBuffer > 0)
                    {
                        Log(Severity.Debug, "[{0}] Buffer processing aborted, bytes left in receive buffer: {1}", state.ConnectionIdentifier, buffer.BytesInBuffer);
                    }
                    return;
                }
            }
        }
Exemplo n.º 16
0
        public void ProcessCurrentBuffer(byte[] currentBuffer, StateObject state)
        {
            if (state.ConnectionBuffer.Length == 0)
            {
                state.ConnectionBuffer = currentBuffer;
            }
            else
            {
                state.ConnectionBuffer = ByteUtils.Concatenate(state.ConnectionBuffer, currentBuffer);
            }

            // we now have all PDU bytes received so far in state.ConnectionBuffer
            int bytesLeftInBuffer = state.ConnectionBuffer.Length;

            while (bytesLeftInBuffer >= 8)
            {
                int bufferOffset = state.ConnectionBuffer.Length - bytesLeftInBuffer;
                int pduLength    = ISCSIPDU.GetPDULength(state.ConnectionBuffer, bufferOffset);
                if (pduLength > bytesLeftInBuffer)
                {
                    break;
                }
                else
                {
                    byte[] pduBytes = ByteReader.ReadBytes(state.ConnectionBuffer, bufferOffset, pduLength);
                    bytesLeftInBuffer -= pduLength;
                    ISCSIPDU pdu = null;
                    try
                    {
                        pdu = ISCSIPDU.GetPDU(pduBytes);
                    }
                    catch (UnsupportedSCSICommandException)
                    {
                        throw;
                    }
                    catch (Exception)
                    {
                        throw;
                    }

                    if (pdu.GetType() == typeof(ISCSIPDU))
                    {
                        /*
                         * Log("[{0}][ProcessCurrentBuffer] Unsupported PDU (0x{1})", state.Connection.Identifier, pdu.OpCode.ToString("X"));
                         * // Unsupported PDU
                         * RejectPDU reject = new RejectPDU();
                         * reject.Reason = RejectReason.CommandNotSupported;
                         * reject.StatSN = state.Connection.StatSN;
                         * reject.ExpCmdSN = state.Connection.ExpCmdSN;
                         * reject.MaxCmdSN = state.Connection.ExpCmdSN + ISCSIServer.CommandQueueSize;
                         * reject.Data = ByteReader.ReadBytes(pduBytes, 0, 48);
                         *
                         * // StatSN is advanced after a Reject
                         * state.Connection.StatSN++;
                         *
                         * TrySendPDU(state, reject);*/
                        throw new Exception("Unsupported");
                    }
                    else
                    {
                        ProcessPDU(pdu, state);
                    }
                }
            }

            if (bytesLeftInBuffer > 0)
            {
                state.ConnectionBuffer = ByteReader.ReadBytes(state.ConnectionBuffer, state.ConnectionBuffer.Length - bytesLeftInBuffer, bytesLeftInBuffer);
            }
            else
            {
                state.ConnectionBuffer = new byte[0];
            }
        }
Exemplo n.º 17
0
        private void ProcessPDU(ISCSIPDU pdu, ConnectionState state)
        {
            LogTrace("Entering ProcessPDU");

            if (state.Session == null || !state.Session.IsFullFeaturePhase)
            {
                if (pdu is LoginRequestPDU)
                {
                    LoginRequestPDU request         = (LoginRequestPDU)pdu;
                    string          loginIdentifier = String.Format("ISID={0},TSIH={1},CID={2}", request.ISID.ToString("x"), request.TSIH.ToString("x"), request.CID.ToString("x"));
                    Log(Severity.Verbose, "[{0}] Login Request, current stage: {1}, next stage: {2}, parameters: {3}", loginIdentifier, request.CurrentStage, request.NextStage, FormatNullDelimitedText(request.LoginParametersText));
                    LoginResponsePDU response = GetLoginResponsePDU(request, state.ConnectionParameters);
                    if (state.Session != null && state.Session.IsFullFeaturePhase)
                    {
                        m_connectionManager.AddConnection(state);
                    }
                    Log(Severity.Verbose, "[{0}] Login Response parameters: {1}", state.ConnectionIdentifier, FormatNullDelimitedText(response.LoginParametersText));
                    state.SendQueue.Enqueue(response);
                }
                else
                {
                    // Before the Full Feature Phase is established, only Login Request and Login Response PDUs are allowed.
                    Log(Severity.Warning, "[{0}] Initiator error: Improper command during login phase, OpCode: 0x{1}", state.ConnectionIdentifier, pdu.OpCode.ToString("x"));
                    if (state.Session == null)
                    {
                        // A target receiving any PDU except a Login request before the Login phase is started MUST
                        // immediately terminate the connection on which the PDU was received.
                        state.ClientSocket.Close();
                    }
                    else
                    {
                        // Once the Login phase has started, if the target receives any PDU except a Login request,
                        // it MUST send a Login reject (with Status "invalid during login") and then disconnect.
                        LoginResponsePDU loginResponse = new LoginResponsePDU();
                        loginResponse.TSIH   = state.Session.TSIH;
                        loginResponse.Status = LoginResponseStatusName.InvalidDuringLogon;
                        state.SendQueue.Enqueue(loginResponse);
                    }
                }
            }
            else // Logged in
            {
                if (pdu is TextRequestPDU)
                {
                    TextRequestPDU  request  = (TextRequestPDU)pdu;
                    TextResponsePDU response = GetTextResponsePDU(request, state.ConnectionParameters);
                    state.SendQueue.Enqueue(response);
                }
                else if (pdu is LogoutRequestPDU)
                {
                    LogoutRequestPDU request  = (LogoutRequestPDU)pdu;
                    ISCSIPDU         response = GetLogoutResponsePDU(request, state.ConnectionParameters);
                    state.SendQueue.Enqueue(response);
                }
                else if (state.Session.IsDiscovery)
                {
                    // The target MUST ONLY accept text requests with the SendTargets key and a logout
                    // request with the reason "close the session".  All other requests MUST be rejected.
                    Log(Severity.Warning, "[{0}] Initiator error: Improper command during discovery session, OpCode: 0x{1}", state.ConnectionIdentifier, pdu.OpCode.ToString("x"));
                    RejectPDU reject = new RejectPDU();
                    reject.Reason = RejectReason.ProtocolError;
                    reject.Data   = ByteReader.ReadBytes(pdu.GetBytes(), 0, 48);

                    state.SendQueue.Enqueue(reject);
                }
                else if (pdu is NOPOutPDU)
                {
                    NOPOutPDU request = (NOPOutPDU)pdu;
                    if (request.InitiatorTaskTag != 0xFFFFFFFF)
                    {
                        NOPInPDU response = ServerResponseHelper.GetNOPResponsePDU(request);
                        state.SendQueue.Enqueue(response);
                    }
                }
                else if (pdu is SCSIDataOutPDU || pdu is SCSICommandPDU)
                {
                    // RFC 3720: the iSCSI target layer MUST deliver the commands for execution (to the SCSI execution engine) in the order specified by CmdSN.
                    // e.g. read requests should not be executed while previous write request data is being received (via R2T)
                    List <SCSICommandPDU>     commandsToExecute   = null;
                    List <ReadyToTransferPDU> readyToTransferPDUs = new List <ReadyToTransferPDU>();
                    if (pdu is SCSIDataOutPDU)
                    {
                        SCSIDataOutPDU request = (SCSIDataOutPDU)pdu;
                        Log(Severity.Debug, "[{0}] SCSIDataOutPDU: Target transfer tag: {1}, LUN: {2}, Buffer offset: {3}, Data segment length: {4}, DataSN: {5}, Final: {6}", state.ConnectionIdentifier, request.TargetTransferTag, (ushort)request.LUN, request.BufferOffset, request.DataSegmentLength, request.DataSN, request.Final);
                        try
                        {
                            readyToTransferPDUs = TargetResponseHelper.GetReadyToTransferPDUs(request, state.ConnectionParameters, out commandsToExecute);
                        }
                        catch (InvalidTargetTransferTagException ex)
                        {
                            Log(Severity.Warning, "[{0}] Initiator error: Invalid TargetTransferTag: {1}", state.ConnectionIdentifier, ex.TargetTransferTag);
                            RejectPDU reject = new RejectPDU();
                            reject.InitiatorTaskTag = request.InitiatorTaskTag;
                            reject.Reason           = RejectReason.InvalidPDUField;
                            reject.Data             = ByteReader.ReadBytes(request.GetBytes(), 0, 48);
                            state.SendQueue.Enqueue(reject);
                        }
                    }
                    else
                    {
                        SCSICommandPDU command = (SCSICommandPDU)pdu;
                        Log(Severity.Debug, "[{0}] SCSICommandPDU: CmdSN: {1}, LUN: {2}, Data segment length: {3}, Expected Data Transfer Length: {4}, Final: {5}", state.ConnectionIdentifier, command.CmdSN, (ushort)command.LUN, command.DataSegmentLength, command.ExpectedDataTransferLength, command.Final);
                        readyToTransferPDUs = TargetResponseHelper.GetReadyToTransferPDUs(command, state.ConnectionParameters, out commandsToExecute);
                    }
                    foreach (ReadyToTransferPDU readyToTransferPDU in readyToTransferPDUs)
                    {
                        state.SendQueue.Enqueue(readyToTransferPDU);
                    }
                    if (commandsToExecute != null)
                    {
                        state.RunningSCSICommands.Add(commandsToExecute.Count);
                    }
                    foreach (SCSICommandPDU commandPDU in commandsToExecute)
                    {
                        Log(Severity.Debug, "[{0}] Queuing command: CmdSN: {1}", state.ConnectionIdentifier, commandPDU.CmdSN);
                        state.Target.QueueCommand(commandPDU.CommandDescriptorBlock, commandPDU.LUN, commandPDU.Data, commandPDU, state.OnCommandCompleted);
                    }
                }
                else if (pdu is LoginRequestPDU)
                {
                    Log(Severity.Warning, "[{0}] Initiator Error: Login request during full feature phase", state.ConnectionIdentifier);
                    // RFC 3720: Login requests and responses MUST be used exclusively during Login.
                    // On any connection, the login phase MUST immediately follow TCP connection establishment and
                    // a subsequent Login Phase MUST NOT occur before tearing down a connection
                    RejectPDU reject = new RejectPDU();
                    reject.Reason = RejectReason.ProtocolError;
                    reject.Data   = ByteReader.ReadBytes(pdu.GetBytes(), 0, 48);

                    state.SendQueue.Enqueue(reject);
                }
                else
                {
                    Log(Severity.Error, "[{0}] Unsupported command, OpCode: 0x{1}", state.ConnectionIdentifier, pdu.OpCode.ToString("x"));
                    RejectPDU reject = new RejectPDU();
                    reject.Reason = RejectReason.CommandNotSupported;
                    reject.Data   = ByteReader.ReadBytes(pdu.GetBytes(), 0, 48);

                    state.SendQueue.Enqueue(reject);
                }
            }
            LogTrace("Leaving ProcessPDU");
        }