ソースを参照

iSCSI Console v1.2.5

Tal Aloni 8 年 前
コミット
18468d267b

+ 23 - 13
ISCSI/Client/ClientHelper.cs

@@ -229,9 +229,11 @@ namespace ISCSI.Client
 
 
         internal static SCSICommandPDU GetReportLUNsCommand(SessionParameters session, ConnectionParameters connection, uint allocationLength)
         internal static SCSICommandPDU GetReportLUNsCommand(SessionParameters session, ConnectionParameters connection, uint allocationLength)
         {
         {
+            SCSICommandDescriptorBlock reportLUNs = SCSICommandDescriptorBlock.Create(SCSIOpCodeName.ReportLUNs);
+            reportLUNs.TransferLength = allocationLength;
+            
             SCSICommandPDU scsiCommand = new SCSICommandPDU();
             SCSICommandPDU scsiCommand = new SCSICommandPDU();
-            scsiCommand.CommandDescriptorBlock = SCSICommandDescriptorBlock.Create(SCSIOpCodeName.ReportLUNs);
-            scsiCommand.CommandDescriptorBlock.TransferLength = allocationLength;
+            scsiCommand.CommandDescriptorBlock = reportLUNs.GetBytes();
             scsiCommand.InitiatorTaskTag = session.GetNextTaskTag();
             scsiCommand.InitiatorTaskTag = session.GetNextTaskTag();
             scsiCommand.Final = true;
             scsiCommand.Final = true;
             scsiCommand.Read = true;
             scsiCommand.Read = true;
@@ -242,9 +244,11 @@ namespace ISCSI.Client
 
 
         internal static SCSICommandPDU GetReadCapacity10Command(SessionParameters session, ConnectionParameters connection, ushort LUN)
         internal static SCSICommandPDU GetReadCapacity10Command(SessionParameters session, ConnectionParameters connection, ushort LUN)
         {
         {
+            SCSICommandDescriptorBlock readCapacity10 = SCSICommandDescriptorBlock.Create(SCSIOpCodeName.ReadCapacity10);
+            readCapacity10.TransferLength = ReadCapacity10Parameter.Length;
+
             SCSICommandPDU scsiCommand = new SCSICommandPDU();
             SCSICommandPDU scsiCommand = new SCSICommandPDU();
-            scsiCommand.CommandDescriptorBlock = SCSICommandDescriptorBlock.Create(SCSIOpCodeName.ReadCapacity10);
-            scsiCommand.CommandDescriptorBlock.TransferLength = ReadCapacity10Parameter.Length;
+            scsiCommand.CommandDescriptorBlock = readCapacity10.GetBytes();
             scsiCommand.InitiatorTaskTag = session.GetNextTaskTag();
             scsiCommand.InitiatorTaskTag = session.GetNextTaskTag();
             scsiCommand.Final = true;
             scsiCommand.Final = true;
             scsiCommand.Read = true;
             scsiCommand.Read = true;
@@ -256,10 +260,12 @@ namespace ISCSI.Client
 
 
         internal static SCSICommandPDU GetReadCapacity16Command(SessionParameters session, ConnectionParameters connection, ushort LUN)
         internal static SCSICommandPDU GetReadCapacity16Command(SessionParameters session, ConnectionParameters connection, ushort LUN)
         {
         {
+            SCSICommandDescriptorBlock serviceActionIn = SCSICommandDescriptorBlock.Create(SCSIOpCodeName.ServiceActionIn);
+            serviceActionIn.ServiceAction = ServiceAction.ReadCapacity16;
+            serviceActionIn.TransferLength = ReadCapacity16Parameter.Length;
+
             SCSICommandPDU scsiCommand = new SCSICommandPDU();
             SCSICommandPDU scsiCommand = new SCSICommandPDU();
-            scsiCommand.CommandDescriptorBlock = SCSICommandDescriptorBlock.Create(SCSIOpCodeName.ServiceActionIn);
-            scsiCommand.CommandDescriptorBlock.ServiceAction = ServiceAction.ReadCapacity16;
-            scsiCommand.CommandDescriptorBlock.TransferLength = ReadCapacity16Parameter.Length;
+            scsiCommand.CommandDescriptorBlock = serviceActionIn.GetBytes();
             scsiCommand.InitiatorTaskTag = session.GetNextTaskTag();
             scsiCommand.InitiatorTaskTag = session.GetNextTaskTag();
             scsiCommand.Final = true;
             scsiCommand.Final = true;
             scsiCommand.Read = true;
             scsiCommand.Read = true;
@@ -271,10 +277,12 @@ namespace ISCSI.Client
 
 
         internal static SCSICommandPDU GetRead16Command(SessionParameters session, ConnectionParameters connection, ushort LUN, ulong sectorIndex, uint sectorCount, int bytesPerSector)
         internal static SCSICommandPDU GetRead16Command(SessionParameters session, ConnectionParameters connection, ushort LUN, ulong sectorIndex, uint sectorCount, int bytesPerSector)
         {
         {
+            SCSICommandDescriptorBlock read16 = SCSICommandDescriptorBlock.Create(SCSIOpCodeName.Read16);
+            read16.LogicalBlockAddress64 = sectorIndex;
+            read16.TransferLength = sectorCount;
+
             SCSICommandPDU scsiCommand = new SCSICommandPDU();
             SCSICommandPDU scsiCommand = new SCSICommandPDU();
-            scsiCommand.CommandDescriptorBlock = SCSICommandDescriptorBlock.Create(SCSIOpCodeName.Read16);
-            scsiCommand.CommandDescriptorBlock.LogicalBlockAddress64 = sectorIndex;
-            scsiCommand.CommandDescriptorBlock.TransferLength = sectorCount;
+            scsiCommand.CommandDescriptorBlock = read16.GetBytes();
             scsiCommand.LUN = LUN;
             scsiCommand.LUN = LUN;
             scsiCommand.InitiatorTaskTag = session.GetNextTaskTag();
             scsiCommand.InitiatorTaskTag = session.GetNextTaskTag();
             scsiCommand.Final = true;
             scsiCommand.Final = true;
@@ -286,10 +294,12 @@ namespace ISCSI.Client
 
 
         internal static SCSICommandPDU GetWrite16Command(SessionParameters session, ConnectionParameters connection, ushort LUN, ulong sectorIndex, byte[] data, int bytesPerSector)
         internal static SCSICommandPDU GetWrite16Command(SessionParameters session, ConnectionParameters connection, ushort LUN, ulong sectorIndex, byte[] data, int bytesPerSector)
         {
         {
+            SCSICommandDescriptorBlock write16 = SCSICommandDescriptorBlock.Create(SCSIOpCodeName.Write16);
+            write16.LogicalBlockAddress64 = sectorIndex;
+            write16.TransferLength = (uint)(data.Length / bytesPerSector);
+
             SCSICommandPDU scsiCommand = new SCSICommandPDU();
             SCSICommandPDU scsiCommand = new SCSICommandPDU();
-            scsiCommand.CommandDescriptorBlock = SCSICommandDescriptorBlock.Create(SCSIOpCodeName.Write16);
-            scsiCommand.CommandDescriptorBlock.LogicalBlockAddress64 = sectorIndex;
-            scsiCommand.CommandDescriptorBlock.TransferLength = (uint)(data.Length / bytesPerSector);
+            scsiCommand.CommandDescriptorBlock = write16.GetBytes();
             if (session.ImmediateData)
             if (session.ImmediateData)
             {
             {
                 int immediateDataLength = Math.Min(data.Length, session.FirstBurstLength);
                 int immediateDataLength = Math.Min(data.Length, session.FirstBurstLength);

+ 5 - 1
ISCSI/ISCSI.csproj

@@ -40,6 +40,7 @@
     <Compile Include="Client\SessionParameters.cs" />
     <Compile Include="Client\SessionParameters.cs" />
     <Compile Include="Client\StateObject.cs" />
     <Compile Include="Client\StateObject.cs" />
     <Compile Include="Client\ISCSIClient.cs" />
     <Compile Include="Client\ISCSIClient.cs" />
+    <Compile Include="PDU\Enums\ISCSIResponseName.cs" />
     <Compile Include="PDU\Enums\LogoutReasonCode.cs" />
     <Compile Include="PDU\Enums\LogoutReasonCode.cs" />
     <Compile Include="PDU\Enums\LogoutResponse.cs" />
     <Compile Include="PDU\Enums\LogoutResponse.cs" />
     <Compile Include="PDU\Enums\RejectReason.cs" />
     <Compile Include="PDU\Enums\RejectReason.cs" />
@@ -48,8 +49,12 @@
     <Compile Include="SCSI\Enums\AddressingMethod.cs" />
     <Compile Include="SCSI\Enums\AddressingMethod.cs" />
     <Compile Include="SCSI\LUNStructure.cs" />
     <Compile Include="SCSI\LUNStructure.cs" />
     <Compile Include="SCSI\SCSIReturnParameters\ModePages\ControlModePage.cs" />
     <Compile Include="SCSI\SCSIReturnParameters\ModePages\ControlModePage.cs" />
+    <Compile Include="SCSI\SCSIReturnParameters\ModePages\PowerConditionModePage.cs" />
+    <Compile Include="SCSI\SCSIReturnParameters\ModePages\PowerConsumptionModePage.cs" />
+    <Compile Include="SCSI\SCSIReturnParameters\ModePages\SubModePage.cs" />
     <Compile Include="SCSI\SCSIReturnParameters\VPDPages\BlockLimitsVPDPage.cs" />
     <Compile Include="SCSI\SCSIReturnParameters\VPDPages\BlockLimitsVPDPage.cs" />
     <Compile Include="SCSI\SCSIReturnParameters\VPDPages\BlockDeviceCharacteristicsVPDPage.cs" />
     <Compile Include="SCSI\SCSIReturnParameters\VPDPages\BlockDeviceCharacteristicsVPDPage.cs" />
+    <Compile Include="SCSI\SCSIReturnParameters\VPDPages\Enums\IdentifierTypeName.cs" />
     <Compile Include="Server\ConnectionParameters.cs" />
     <Compile Include="Server\ConnectionParameters.cs" />
     <Compile Include="PDU\Enums\ISCSIOpCodeName.cs" />
     <Compile Include="PDU\Enums\ISCSIOpCodeName.cs" />
     <Compile Include="PDU\Enums\LoginResponseStatusClassName.cs" />
     <Compile Include="PDU\Enums\LoginResponseStatusClassName.cs" />
@@ -63,7 +68,6 @@
     <Compile Include="Server\SCSITarget.cs" />
     <Compile Include="Server\SCSITarget.cs" />
     <Compile Include="Server\SessionParameters.cs" />
     <Compile Include="Server\SessionParameters.cs" />
     <Compile Include="Server\ISCSITarget.cs" />
     <Compile Include="Server\ISCSITarget.cs" />
-    <Compile Include="ISCSITester.cs" />
     <Compile Include="KeyValuePairUtils.cs" />
     <Compile Include="KeyValuePairUtils.cs" />
     <Compile Include="PDU\ISCSIPDU.cs" />
     <Compile Include="PDU\ISCSIPDU.cs" />
     <Compile Include="PDU\LoginRequestPDU.cs" />
     <Compile Include="PDU\LoginRequestPDU.cs" />

+ 0 - 110
ISCSI/ISCSITester.cs

@@ -1,110 +0,0 @@
-/* Copyright (C) 2012-2015 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
- * 
- * You can redistribute this program and/or modify it under the terms of
- * the GNU Lesser Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- */
-using System;
-using System.Collections.Generic;
-using System.Net.Sockets;
-using System.Text;
-using Utilities;
-using ISCSI.Server;
-
-namespace ISCSI
-{
-    public class ISCSITester
-    {
-        private Socket m_targetSocket;
-        private Socket m_initiatorSocket;
-        private bool m_waitingForResponse = false;
-        public List<ISCSIPDU> m_pduSent = new List<ISCSIPDU>();
-        public List<ISCSIPDU> m_pduReceived = new List<ISCSIPDU>();
-        private bool m_isTargetConnected = false;
-
-        public ISCSITester(Socket initiatorSocket)
-        {
-            m_initiatorSocket = initiatorSocket;
-            
-        }
-
-        public void Transmit(byte[] data)
-        {
-            ISCSIPDU pdu = ISCSIPDU.GetPDU(data);
-            if (pdu is LoginRequestPDU && !m_isTargetConnected)
-            {
-                m_isTargetConnected = true;
-                m_targetSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
-                m_targetSocket.Connect("tal2", 3260);
-            }
-
-            //m_pduToSend.Add(data);
-            //if (!m_waitingForResponse)
-            //{
-                //m_waitingForResponse = true;
-                //byte[] toSend = m_pduToSend[0];
-                //m_pduToSend.RemoveAt(0);
-                StateObject state = new StateObject();
-                state.ReceiveBuffer = new byte[StateObject.ReceiveBufferSize];
-                m_targetSocket.BeginReceive(state.ReceiveBuffer, 0, StateObject.ReceiveBufferSize, 0, ReceiveCallback, state);
-
-                Console.WriteLine("waiting for respone: " + m_waitingForResponse.ToString());
-                m_targetSocket.Send(data);
-                m_pduSent.Add(ISCSIPDU.GetPDU(data));
-                Console.WriteLine("Transmitted PDU to real target");
-            //}
-            
-        }
-
-        private void ReceiveCallback(IAsyncResult result)
-        {
-            StateObject state = (StateObject)result.AsyncState;
-            //Socket clientSocket = state.WorkerSocket;
-            byte[] buffer = state.ReceiveBuffer;
-
-            int bytesReceived;
-            
-            try
-            {
-                bytesReceived = m_targetSocket.EndReceive(result);
-                m_waitingForResponse = false;
-            }
-            catch (Exception)
-            {
-                //An error has occured when reading
-                Console.WriteLine("An error has occured when reading from real target");
-                return;
-            }
-
-            if (bytesReceived == 0)
-            {
-                //The connection has been closed.
-                Console.WriteLine("The connection with the real target has been closed");
-                return;
-            }
-
-            //iSCSIPDU pdu1 = GetResponcePDU((LoginRequestPDU)pdu);
-
-            Console.WriteLine("Received PDU from real target");
-            byte[] pduBytes = new byte[bytesReceived];
-            Array.Copy(buffer, pduBytes, bytesReceived);
-
-            ISCSIPDU pdu = ISCSIPDU.GetPDU(pduBytes);
-            m_pduReceived.Add(pdu);
-
-            m_initiatorSocket.Send(pduBytes);
-            Console.WriteLine("Sent PDU to real initiator, OpCode: " + pdu.OpCode);
-            if (pdu is LogoutResponsePDU)
-            {
-                m_initiatorSocket.Close();
-                m_targetSocket.Close();
-                m_isTargetConnected = false;
-                return;
-            }
-            
-            //Do something with the data object here.
-            //Then start reading from the network again.
-            m_targetSocket.BeginReceive(state.ReceiveBuffer, 0, StateObject.ReceiveBufferSize, 0, ReceiveCallback, state);
-        }
-    }
-}

+ 0 - 3
ISCSI/PDU/Enums/ISCSIOpCodeName.cs

@@ -1,6 +1,3 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
 
 
 namespace ISCSI
 namespace ISCSI
 {
 {

+ 9 - 0
ISCSI/PDU/Enums/ISCSIResponseName.cs

@@ -0,0 +1,9 @@
+
+namespace ISCSI
+{
+    public enum ISCSIResponseName : byte
+    {
+        CommandCompletedAtTarget = 0x00,
+        TargetFailure = 0x01,
+    }
+}

+ 4 - 12
ISCSI/PDU/SCSICommandPDU.cs

@@ -20,18 +20,14 @@ namespace ISCSI
         public uint ExpectedDataTransferLength; // in bytes (for the whole operation and not just this command)
         public uint ExpectedDataTransferLength; // in bytes (for the whole operation and not just this command)
         public uint CmdSN;
         public uint CmdSN;
         public uint ExpStatSN;
         public uint ExpStatSN;
-        public SCSICommandDescriptorBlock CommandDescriptorBlock;
+        public byte[] CommandDescriptorBlock;
 
 
         public SCSICommandPDU() : base()
         public SCSICommandPDU() : base()
         {
         {
             OpCode = ISCSIOpCodeName.SCSICommand;
             OpCode = ISCSIOpCodeName.SCSICommand;
         }
         }
 
 
-        public SCSICommandPDU(byte[] buffer) : this(buffer, true)
-        {
-        }
-
-        public SCSICommandPDU(byte[] buffer, bool parseCDB) : base(buffer)
+        public SCSICommandPDU(byte[] buffer) : base(buffer)
         {
         {
             Read = (OpCodeSpecificHeader[0] & 0x40) != 0;
             Read = (OpCodeSpecificHeader[0] & 0x40) != 0;
             Write = (OpCodeSpecificHeader[0] & 0x20) != 0;
             Write = (OpCodeSpecificHeader[0] & 0x20) != 0;
@@ -43,10 +39,7 @@ namespace ISCSI
             CmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 4);
             CmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 4);
             ExpStatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8);
             ExpStatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8);
 
 
-            if (parseCDB)
-            {
-                CommandDescriptorBlock = SCSICommandDescriptorBlock.FromBytes(OpCodeSpecific, 12);
-            }
+            CommandDescriptorBlock = ByteReader.ReadBytes(OpCodeSpecific, 12, 16);
         }
         }
 
 
         public override byte[] GetBytes()
         public override byte[] GetBytes()
@@ -66,8 +59,7 @@ namespace ISCSI
             Array.Copy(BigEndianConverter.GetBytes(ExpectedDataTransferLength), 0, OpCodeSpecific, 0, 4);
             Array.Copy(BigEndianConverter.GetBytes(ExpectedDataTransferLength), 0, OpCodeSpecific, 0, 4);
             Array.Copy(BigEndianConverter.GetBytes(CmdSN), 0, OpCodeSpecific, 4, 4);
             Array.Copy(BigEndianConverter.GetBytes(CmdSN), 0, OpCodeSpecific, 4, 4);
             Array.Copy(BigEndianConverter.GetBytes(ExpStatSN), 0, OpCodeSpecific, 8, 4);
             Array.Copy(BigEndianConverter.GetBytes(ExpStatSN), 0, OpCodeSpecific, 8, 4);
-            byte[] cdbBytes = CommandDescriptorBlock.GetBytes();
-            Array.Copy(cdbBytes, 0, OpCodeSpecific, 12, cdbBytes.Length);
+            Array.Copy(CommandDescriptorBlock, 0, OpCodeSpecific, 12, CommandDescriptorBlock.Length);
             
             
             return base.GetBytes();
             return base.GetBytes();
         }
         }

+ 3 - 3
ISCSI/PDU/SCSIResponsePDU.cs

@@ -17,7 +17,7 @@ namespace ISCSI
         public bool BidirectionalReadResidualUnderflow;
         public bool BidirectionalReadResidualUnderflow;
         public bool ResidualOverflow;
         public bool ResidualOverflow;
         public bool ResidualUnderflow;
         public bool ResidualUnderflow;
-        public byte Response;
+        public ISCSIResponseName Response;
         public SCSIStatusCodeName Status;
         public SCSIStatusCodeName Status;
         public uint SNACKTag;
         public uint SNACKTag;
         public uint StatSN;
         public uint StatSN;
@@ -39,7 +39,7 @@ namespace ISCSI
             BidirectionalReadResidualUnderflow = (OpCodeSpecificHeader[0] & 0x08) != 0;
             BidirectionalReadResidualUnderflow = (OpCodeSpecificHeader[0] & 0x08) != 0;
             ResidualOverflow = (OpCodeSpecificHeader[0] & 0x04) != 0;
             ResidualOverflow = (OpCodeSpecificHeader[0] & 0x04) != 0;
             ResidualUnderflow = (OpCodeSpecificHeader[0] & 0x02) != 0;
             ResidualUnderflow = (OpCodeSpecificHeader[0] & 0x02) != 0;
-            Response = OpCodeSpecificHeader[1];
+            Response = (ISCSIResponseName)OpCodeSpecificHeader[1];
             Status = (SCSIStatusCodeName)OpCodeSpecificHeader[2];
             Status = (SCSIStatusCodeName)OpCodeSpecificHeader[2];
 
 
             SNACKTag = BigEndianConverter.ToUInt32(OpCodeSpecific, 0);
             SNACKTag = BigEndianConverter.ToUInt32(OpCodeSpecific, 0);
@@ -69,7 +69,7 @@ namespace ISCSI
             {
             {
                 OpCodeSpecificHeader[0] |= 0x02;
                 OpCodeSpecificHeader[0] |= 0x02;
             }
             }
-            OpCodeSpecificHeader[1] = Response;
+            OpCodeSpecificHeader[1] = (byte)Response;
             OpCodeSpecificHeader[2] = (byte)Status;
             OpCodeSpecificHeader[2] = (byte)Status;
 
 
             Array.Copy(BigEndianConverter.GetBytes(SNACKTag), 0, OpCodeSpecific, 0, 4);
             Array.Copy(BigEndianConverter.GetBytes(SNACKTag), 0, OpCodeSpecific, 0, 4);

+ 2 - 2
ISCSI/Properties/AssemblyInfo.cs

@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
 //
 //
 // You can specify all the values or you can default the Revision and Build Numbers 
 // You can specify all the values or you can default the Revision and Build Numbers 
 // by using the '*' as shown below:
 // by using the '*' as shown below:
-[assembly: AssemblyVersion("1.2.4.0")]
-[assembly: AssemblyFileVersion("1.2.4.0")]
+[assembly: AssemblyVersion("1.2.5.0")]
+[assembly: AssemblyFileVersion("1.2.5.0")]

+ 2 - 0
ISCSI/RevisionHistory.txt

@@ -52,3 +52,5 @@ Revision History:
 		Bugfix: Unsupported SCSI commands are now properly handled.
 		Bugfix: Unsupported SCSI commands are now properly handled.
 
 
 1.2.4 - Improved separation between the iSCSI and SCSI layers.
 1.2.4 - Improved separation between the iSCSI and SCSI layers.
+
+1.2.5 - Improved compliance with the iSCSI and SCSI protocols.

+ 1 - 0
ISCSI/SCSI/Enums/ModePageCodeName.cs

@@ -6,6 +6,7 @@ namespace ISCSI
         VendorSpecificPage = 0x00, // Microsoft iSCSI initiator on Windows 2000 will request this page
         VendorSpecificPage = 0x00, // Microsoft iSCSI initiator on Windows 2000 will request this page
         CachingParametersPage = 0x08,
         CachingParametersPage = 0x08,
         ControlModePage = 0x0A,
         ControlModePage = 0x0A,
+        PowerConditionModePage = 0x1A,
         InformationalExceptionsControlModePage = 0x1C,
         InformationalExceptionsControlModePage = 0x1C,
         ReturnAllPages = 0x3F,
         ReturnAllPages = 0x3F,
     }
     }

+ 5 - 0
ISCSI/SCSI/Enums/SCSIOpCodeName.cs

@@ -8,6 +8,7 @@ namespace ISCSI
         Read6 = 0x08,
         Read6 = 0x08,
         Write6 = 0x0A,
         Write6 = 0x0A,
         Inquiry = 0x12,
         Inquiry = 0x12,
+        ModeSelect6 = 0x15,
         Reserve6 = 0x16,
         Reserve6 = 0x16,
         Release6 = 0x17,
         Release6 = 0x17,
         ModeSense6 = 0x1A,
         ModeSense6 = 0x1A,
@@ -19,6 +20,10 @@ namespace ISCSI
         ReadBuffer = 0x3C,
         ReadBuffer = 0x3C,
         SynchronizeCache10 = 0x35,
         SynchronizeCache10 = 0x35,
         WriteSame10 = 0x41,
         WriteSame10 = 0x41,
+        ModeSelect10 = 0x15,
+        ModeSense10 = 0x5A,
+        PersistentReserveIn = 0x5E,
+        PersistentReserveOut = 0x5F,
         Read16 = 0x88,
         Read16 = 0x88,
         Write16 = 0x8A,
         Write16 = 0x8A,
         Verify16 = 0x8F,
         Verify16 = 0x8F,

+ 1 - 1
ISCSI/SCSI/SCSICommandDescriptorBlock/InquiryCommandDescriptorBlock.cs

@@ -13,7 +13,7 @@ namespace ISCSI
 {
 {
     public class InquiryCommand : SCSICommandDescriptorBlock
     public class InquiryCommand : SCSICommandDescriptorBlock
     {
     {
-        public bool EVPD;
+        public bool EVPD; // Enable Vital Product Data
         public VitalProductDataPageName PageCode;
         public VitalProductDataPageName PageCode;
 
 
         public InquiryCommand() : base()
         public InquiryCommand() : base()

+ 1 - 1
ISCSI/SCSI/SCSICommandDescriptorBlock/ModeSense6CommandDescriptorBlock.cs

@@ -13,7 +13,7 @@ namespace ISCSI
 {
 {
     public class ModeSense6CommandDescriptorBlock : SCSICommandDescriptorBlock
     public class ModeSense6CommandDescriptorBlock : SCSICommandDescriptorBlock
     {
     {
-        public bool DBD;
+        public bool DBD; // Disable block descriptors
         public byte PC; // Page Control
         public byte PC; // Page Control
         public ModePageCodeName PageCode;
         public ModePageCodeName PageCode;
         public byte SubpageCode;
         public byte SubpageCode;

+ 1 - 1
ISCSI/SCSI/SCSIReturnParameters/ModePages/ControlModePage.cs

@@ -58,7 +58,7 @@ namespace ISCSI
             BigEndianWriter.WriteUInt16(buffer, 8, Obsolete2);
             BigEndianWriter.WriteUInt16(buffer, 8, Obsolete2);
             BigEndianWriter.WriteUInt16(buffer, 10, Obsolete3);
             BigEndianWriter.WriteUInt16(buffer, 10, Obsolete3);
 
 
-            return base.GetBytes();
+            return buffer;
         }
         }
     }
     }
 }
 }

+ 53 - 0
ISCSI/SCSI/SCSIReturnParameters/ModePages/PowerConditionModePage.cs

@@ -0,0 +1,53 @@
+/* Copyright (C) 2012-2016 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
+ * 
+ * You can redistribute this program and/or modify it under the terms of
+ * the GNU Lesser Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ */
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Utilities;
+
+namespace ISCSI
+{
+    public class PowerConditionModePage : ModePage0
+    {
+        public byte PmBgPrecedence;
+        public bool StandbyY;
+        public bool IdleC;
+        public bool IdleB;
+        public bool IdleA;
+        public bool StandbyZ;
+        public uint IdleATimer;
+        public uint StandbyZTimer;
+        public uint IdleBTimer;
+        public uint IdleCTimer;
+        public uint StandbyYTimer;
+        public byte CcfIdle;
+        public byte CcfStandby;
+        public byte CcfStopped;
+
+        public PowerConditionModePage() : base(ModePageCodeName.PowerConditionModePage, 38)
+        { }
+
+        public PowerConditionModePage(byte[] buffer, int offset) : base(buffer, offset)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override byte[] GetBytes()
+        {
+            byte[] buffer = base.GetBytes();
+            buffer[2] = (byte)(PmBgPrecedence << 5 | (Convert.ToByte(StandbyY) & 0x01));
+            buffer[3] = (byte)((Convert.ToByte(IdleC) & 0x01) << 4 | (Convert.ToByte(IdleB) & 0x01) << 4 | (Convert.ToByte(IdleA) & 0x01) << 4 | (Convert.ToByte(StandbyZ) & 0x01));
+            BigEndianWriter.WriteUInt32(buffer, 4, IdleATimer);
+            BigEndianWriter.WriteUInt32(buffer, 8, StandbyZTimer);
+            BigEndianWriter.WriteUInt32(buffer, 12, IdleBTimer);
+            BigEndianWriter.WriteUInt32(buffer, 16, IdleCTimer);
+            BigEndianWriter.WriteUInt32(buffer, 20, StandbyYTimer);
+            buffer[39] = (byte)((Convert.ToByte(CcfIdle) & 0x03) << 6 | (Convert.ToByte(CcfStandby) & 0x01) << 4 | (Convert.ToByte(CcfStopped) & 0x01) << 2);
+            return buffer;
+        }
+    }
+}

+ 35 - 0
ISCSI/SCSI/SCSIReturnParameters/ModePages/PowerConsumptionModePage.cs

@@ -0,0 +1,35 @@
+/* Copyright (C) 2012-2016 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
+ * 
+ * You can redistribute this program and/or modify it under the terms of
+ * the GNU Lesser Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ */
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Utilities;
+
+namespace ISCSI
+{
+    public class PowerConsumptionModePage : SubModePage
+    {
+        public byte PowerConsumptionIdentifier;
+
+        public PowerConsumptionModePage() : base(ModePageCodeName.PowerConditionModePage, 0x01, 12)
+        {
+        }
+
+        public PowerConsumptionModePage(byte[] buffer, int offset) : base(buffer, offset)
+        {
+            PowerConsumptionIdentifier = buffer[offset + 7];
+        }
+
+        public override byte[] GetBytes()
+        {
+            byte[] buffer = base.GetBytes();
+            buffer[7] = PowerConsumptionIdentifier;
+
+            return buffer;
+        }
+    }
+}

+ 63 - 0
ISCSI/SCSI/SCSIReturnParameters/ModePages/SubModePage.cs

@@ -0,0 +1,63 @@
+/* Copyright (C) 2012-2016 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
+ * 
+ * You can redistribute this program and/or modify it under the terms of
+ * the GNU Lesser Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ */
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Utilities;
+
+namespace ISCSI
+{
+    public class SubModePage : ModePage // SUB_PAGE mode page format
+    {
+        public bool PS;    // Parameter Savable
+        public bool SPF;   // SubPage Format
+        public ModePageCodeName PageCode;
+        public byte SubPageCode;
+        public ushort PageLength; // excluding this and previous bytes
+
+        protected SubModePage(ModePageCodeName pageCode, byte subPageCode, ushort pageLength)
+        {
+            PageCode = pageCode;
+            PageLength = pageLength;
+        }
+
+        public SubModePage(byte[] buffer, int offset)
+        {
+            PS = (buffer[offset + 0] & 0x80) != 0;
+            SPF = (buffer[offset + 0] & 0x40) != 0;
+            PageCode = (ModePageCodeName)(buffer[offset + 0] & 0x3F);
+            SubPageCode = buffer[offset + 1];
+            PageLength = BigEndianConverter.ToUInt16(buffer, 2);
+        }
+
+        override public byte[] GetBytes()
+        {
+            byte[] buffer = new byte[4 + PageLength];
+            if (PS)
+            {
+                buffer[0] |= 0x80;
+            }
+            if (SPF)
+            {
+                buffer[0] |= 0x40;
+            }
+            buffer[0] |= (byte)((byte)PageCode & 0x3F);
+            buffer[1] = SubPageCode;
+            BigEndianWriter.WriteUInt16(buffer, 2, PageLength);
+
+            return buffer;
+        }
+
+        public override int Length
+        {
+            get
+            {
+                return 4 + PageLength;
+            }
+        }
+    }
+}

+ 36 - 41
ISCSI/SCSI/SCSIReturnParameters/SenseDataParameter.cs

@@ -64,96 +64,91 @@ namespace ISCSI
             return buffer;
             return buffer;
         }
         }
 
 
-        public static SenseDataParameter GetNoSenseSenseData()
+        public static SenseDataParameter GetDataProtectSenseData()
         {
         {
             SenseDataParameter senseData = new SenseDataParameter();
             SenseDataParameter senseData = new SenseDataParameter();
             senseData.Valid = true;
             senseData.Valid = true;
             senseData.ResponseCode = 0x70; // current errors
             senseData.ResponseCode = 0x70; // current errors
-            senseData.SenseKey = 0x00;     // NO SENSE
-            senseData.AdditionalSenseCode = 0x00; // No Additional Sense Information
+            senseData.SenseKey = 0x07;     // DATA PROTECT
+            senseData.AdditionalSenseCode = 0x27; // Command not allowed
+            senseData.AdditionalSenseCodeQualifier = 0x00;
             return senseData;
             return senseData;
         }
         }
 
 
-        /// <summary>
-        /// Reported when CRC error is encountered
-        /// </summary>
-        public static SenseDataParameter GetWriteFaultSenseData()
+        public static SenseDataParameter GetIllegalRequestSenseData(byte additionalSenseCode, byte additionalSenseCodeQualifier)
         {
         {
             SenseDataParameter senseData = new SenseDataParameter();
             SenseDataParameter senseData = new SenseDataParameter();
             senseData.Valid = true;
             senseData.Valid = true;
             senseData.ResponseCode = 0x70; // current errors
             senseData.ResponseCode = 0x70; // current errors
-            senseData.SenseKey = 0x03;     // MEDIUM ERROR
-            senseData.AdditionalSenseCode = 0x00; // Peripheral Device Write Fault
+            senseData.SenseKey = 0x05;     // ILLEGAL REQUEST
+            senseData.AdditionalSenseCode = additionalSenseCode;
+            senseData.AdditionalSenseCodeQualifier = additionalSenseCodeQualifier;
             return senseData;
             return senseData;
         }
         }
 
 
-        public static SenseDataParameter GetIllegalRequestSenseData()
+        public static SenseDataParameter GetIllegalRequestInvalidFieldInCDBSenseData()
         {
         {
-            SenseDataParameter senseData = new SenseDataParameter();
-            senseData.Valid = true;
-            senseData.ResponseCode = 0x70; // current errors
-            senseData.SenseKey = 0x05;     // ILLEGAL REQUEST
-            senseData.AdditionalSenseCode = 0x00;
-            return senseData;
+            return GetIllegalRequestSenseData(0x24, 0x00); // Invalid field in CDB
         }
         }
 
 
-        public static SenseDataParameter GetIllegalRequestUnsupportedCommandCodeSenseData()
+        public static SenseDataParameter GetIllegalRequestInvalidLUNSenseData()
         {
         {
-            SenseDataParameter senseData = new SenseDataParameter();
-            senseData.Valid = true;
-            senseData.ResponseCode = 0x70; // current errors
-            senseData.SenseKey = 0x05;     // ILLEGAL REQUEST
-            senseData.AdditionalSenseCode = 0x20; // Invalid / unsupported command code
-            return senseData;
+            return GetIllegalRequestSenseData(0x25, 0x00); // Invalid LUN
         }
         }
 
 
         public static SenseDataParameter GetIllegalRequestLBAOutOfRangeSenseData()
         public static SenseDataParameter GetIllegalRequestLBAOutOfRangeSenseData()
         {
         {
-            SenseDataParameter senseData = new SenseDataParameter();
-            senseData.Valid = true;
-            senseData.ResponseCode = 0x70; // current errors
-            senseData.SenseKey = 0x05;     // ILLEGAL REQUEST
-            senseData.AdditionalSenseCode = 0x21; // LBA out of range
-            return senseData;
+            return GetIllegalRequestSenseData(0x21, 0x00); // LBA out of range
         }
         }
 
 
-        public static SenseDataParameter GetIllegalRequestInvalidLUNSenseData()
+        public static SenseDataParameter GetIllegalRequestUnsupportedCommandCodeSenseData()
+        {
+            return GetIllegalRequestSenseData(0x20, 0x00); // Invalid / unsupported command code
+        }
+
+        public static SenseDataParameter GetMediumErrorUnrecoverableReadErrorSenseData()
         {
         {
             SenseDataParameter senseData = new SenseDataParameter();
             SenseDataParameter senseData = new SenseDataParameter();
             senseData.Valid = true;
             senseData.Valid = true;
             senseData.ResponseCode = 0x70; // current errors
             senseData.ResponseCode = 0x70; // current errors
-            senseData.SenseKey = 0x05;     // ILLEGAL REQUEST
-            senseData.AdditionalSenseCode = 0x25; // Invalid LUN
+            senseData.SenseKey = 0x03;     // MEDIUM ERROR
+            senseData.AdditionalSenseCode = 0x11; // Peripheral Device Write Fault
+            senseData.AdditionalSenseCodeQualifier = 0x00;
             return senseData;
             return senseData;
         }
         }
 
 
-        public static SenseDataParameter GetIllegalRequestParameterNotSupportedSenseData()
+        public static SenseDataParameter GetMediumErrorWriteFaultSenseData()
         {
         {
             SenseDataParameter senseData = new SenseDataParameter();
             SenseDataParameter senseData = new SenseDataParameter();
             senseData.Valid = true;
             senseData.Valid = true;
             senseData.ResponseCode = 0x70; // current errors
             senseData.ResponseCode = 0x70; // current errors
-            senseData.SenseKey = 0x05;     // ILLEGAL REQUEST
-            senseData.AdditionalSenseCode = 0x26; // Parameter not supported
+            senseData.SenseKey = 0x03;     // MEDIUM ERROR
+            senseData.AdditionalSenseCode = 0x03; // Peripheral Device Write Fault
+            senseData.AdditionalSenseCodeQualifier = 0x00;
             return senseData;
             return senseData;
         }
         }
 
 
-        public static SenseDataParameter GetUnitAttentionSenseData()
+        public static SenseDataParameter GetNoSenseSenseData()
         {
         {
             SenseDataParameter senseData = new SenseDataParameter();
             SenseDataParameter senseData = new SenseDataParameter();
             senseData.Valid = true;
             senseData.Valid = true;
             senseData.ResponseCode = 0x70; // current errors
             senseData.ResponseCode = 0x70; // current errors
-            senseData.SenseKey = 0x06;     // UNIT ATTENTION
-            senseData.AdditionalSenseCode = 0x00;
+            senseData.SenseKey = 0x00;     // NO SENSE
+            senseData.AdditionalSenseCode = 0x00; // No Additional Sense Information
             return senseData;
             return senseData;
         }
         }
 
 
-        public static SenseDataParameter GetDataProtectSenseData()
+        /// <summary>
+        /// Reported when CRC error is encountered
+        /// </summary>
+        public static SenseDataParameter GetWriteFaultSenseData()
         {
         {
             SenseDataParameter senseData = new SenseDataParameter();
             SenseDataParameter senseData = new SenseDataParameter();
             senseData.Valid = true;
             senseData.Valid = true;
             senseData.ResponseCode = 0x70; // current errors
             senseData.ResponseCode = 0x70; // current errors
-            senseData.SenseKey = 0x07;     // DATA PROTECT
-            senseData.AdditionalSenseCode = 0x00;
+            senseData.SenseKey = 0x03;     // MEDIUM ERROR
+            senseData.AdditionalSenseCode = 0x03; // Peripheral Device Write Fault
+            senseData.AdditionalSenseCodeQualifier = 0x00;
             return senseData;
             return senseData;
         }
         }
     }
     }

+ 24 - 3
ISCSI/SCSI/SCSIReturnParameters/StandardInquiryData.cs

@@ -39,15 +39,24 @@ namespace ISCSI
         public bool CmdQue;  // Command Queuing
         public bool CmdQue;  // Command Queuing
         public bool VS2;     // Vendor specific
         public bool VS2;     // Vendor specific
 
 
+        /// <summary>
+        /// 8 characters
+        /// </summary>
         public string VendorIdentification;
         public string VendorIdentification;
+        /// <summary>
+        /// 16 characters
+        /// </summary>
         public string ProductIdentification;
         public string ProductIdentification;
+        /// <summary>
+        /// 4 characters
+        /// </summary>
         public string ProductRevisionLevel;
         public string ProductRevisionLevel;
         public ulong DriveSerialNumber;
         public ulong DriveSerialNumber;
         // Vendor Unique
         // Vendor Unique
         public byte Clocking;
         public byte Clocking;
         public bool QAS;
         public bool QAS;
         public bool IUS;
         public bool IUS;
-        public byte[] VersionDescriptor = new byte[16];
+        public List<ushort> VersionDescriptors = new List<ushort>(); // 8 descriptors (16 bytes)
 
 
         public StandardInquiryData()
         public StandardInquiryData()
         {
         {
@@ -99,7 +108,11 @@ namespace ISCSI
             QAS = (buffer[offset + 56] & 0x02) != 0;
             QAS = (buffer[offset + 56] & 0x02) != 0;
             IUS = (buffer[offset + 56] & 0x01) != 0;
             IUS = (buffer[offset + 56] & 0x01) != 0;
 
 
-            Array.Copy(buffer, offset + 58, VersionDescriptor, 0, 16);
+            for (int index = 0; index < 8; index++)
+            {
+                ushort versionDescriptor = BigEndianConverter.ToUInt16(buffer, offset + 58 + index * 2);
+                VersionDescriptors.Add(versionDescriptor);
+            }
         }
         }
 
 
         public byte[] GetBytes()
         public byte[] GetBytes()
@@ -197,7 +210,15 @@ namespace ISCSI
                 buffer[56] |= 0x01;
                 buffer[56] |= 0x01;
             }
             }
 
 
-            Array.Copy(VersionDescriptor, 0, buffer, 58, 16);
+            for (int index = 0; index < 8; index++)
+            {
+                ushort versionDescriptor = 0;
+                if (index < VersionDescriptors.Count)
+                {
+                    versionDescriptor = VersionDescriptors[index];
+                }
+                BigEndianWriter.WriteUInt16(buffer, 58 + index * 2, versionDescriptor);
+            }
 
 
             return buffer;
             return buffer;
         }
         }

+ 16 - 0
ISCSI/SCSI/SCSIReturnParameters/VPDPages/Enums/IdentifierTypeName.cs

@@ -0,0 +1,16 @@
+
+namespace ISCSI
+{
+    public enum IdentifierTypeName : byte
+    {
+        VendorSpecific = 0x00,
+        T10 = 0x01, // T10 vendor identification
+        EUI64 = 0x02, // EUI-64
+        NAA = 0x03,
+        RelativeTargetPort = 0x04,
+        TargetPortGroup = 0x05,
+        LogicalUnitGroup = 0x06,
+        MD5LogicalUnitIdentifier = 0x07,
+        ScsiNameString = 0x08,
+    }
+}

+ 75 - 44
ISCSI/SCSI/SCSIReturnParameters/VPDPages/IdentificationDescriptor.cs

@@ -11,13 +11,40 @@ using Utilities;
 
 
 namespace ISCSI
 namespace ISCSI
 {
 {
+    public enum ProtocolName : byte
+    {
+        FibreChannel = 0,
+        ParallelSCSI = 1,
+        SSA = 2,
+        IEEE1394 = 3,
+        SCSIRDMA = 4,
+        ISCSI = 5,
+        SAS = 6,
+        ADT = 7,
+        ATA = 8,
+    }
+
+    public enum CodeSetName : byte
+    {
+        Binary = 1,
+        ASCII = 2,
+        UTF8 = 3,
+    }
+
+    public enum AssociationName : byte
+    {
+        LogicalUnit = 0,
+        TargetPort = 1,
+        TargetDevice = 2,
+    }
+
     public class IdentificationDescriptor
     public class IdentificationDescriptor
     {
     {
-        public byte ProtocolIdentifier;
-        public byte CodeSet;
+        public ProtocolName ProtocolIdentifier;
+        public CodeSetName CodeSet;
         public bool PIV;
         public bool PIV;
-        public byte Association;
-        public byte IdentifierType;
+        public AssociationName Association;
+        public IdentifierTypeName IdentifierType;
         public byte IdentifierLength;
         public byte IdentifierLength;
         public byte[] Identifier = new byte[0];
         public byte[] Identifier = new byte[0];
 
 
@@ -25,46 +52,27 @@ namespace ISCSI
         {
         {
         }
         }
 
 
-        public IdentificationDescriptor(ulong eui64Identifier)
-        {
-            CodeSet = 0x01;
-            IdentifierType = 0x02; // EUI-64
-            Identifier = BigEndianConverter.GetBytes(eui64Identifier);
-        }
-
-        public IdentificationDescriptor(ulong eui64Identifier, ulong wwn)
+        public IdentificationDescriptor(IdentifierTypeName identifierType, byte[] identifier)
         {
         {
-            /*
-             * In Fibre Channel, the unique identity of a device is provided by a 64-bit WWN, whereas the network address is the 24-bit Fibre Channel address.
-             * The WWN convention is also accommodated by iSCSI naming as an IEEE extended unique identifier (EUI) format or “eui.”
-             * The resulting iSCSI name is simply “eui” followed by the hexidecimal WWN (for example, eui.0300732A32598D26). 
-             * */
-            CodeSet = 0x01;
-            IdentifierType = 0x03;
-            Identifier = new byte[16];
-            Array.Copy(BigEndianConverter.GetBytes(eui64Identifier), 0, Identifier, 0, 8);
-            Array.Copy(BigEndianConverter.GetBytes(wwn), 0, Identifier, 8, 8);
-        }
-
-        public IdentificationDescriptor(byte[] identifier)
-        {
-            CodeSet = 0x01; // The IDENTIFIER field shall contain binary values
+            CodeSet = CodeSetName.Binary;
+            IdentifierType = identifierType;
             Identifier = identifier;
             Identifier = identifier;
         }
         }
 
 
-        public IdentificationDescriptor(string identifier)
+        public IdentificationDescriptor(IdentifierTypeName identifierType, string identifier)
         {
         {
-            CodeSet = 0x02; // The IDENTIFIER field shall contain ASCII graphic codes
+            CodeSet = CodeSetName.ASCII;
+            IdentifierType = identifierType;
             Identifier = ASCIIEncoding.ASCII.GetBytes(identifier);
             Identifier = ASCIIEncoding.ASCII.GetBytes(identifier);
         }
         }
 
 
         public IdentificationDescriptor(byte[] buffer, int offset)
         public IdentificationDescriptor(byte[] buffer, int offset)
         {
         {
-            ProtocolIdentifier = (byte)((buffer[offset + 0] >> 4) & 0x0F);
-            CodeSet = (byte)(buffer[offset + 0] & 0x0F);
+            ProtocolIdentifier = (ProtocolName)((buffer[offset + 0] >> 4) & 0x0F);
+            CodeSet = (CodeSetName)(buffer[offset + 0] & 0x0F);
             PIV = (buffer[offset + 1] & 0x80) != 0;
             PIV = (buffer[offset + 1] & 0x80) != 0;
-            Association = (byte)((buffer[offset + 1] >> 4) & 0x03);
-            IdentifierType = (byte)(buffer[offset + 1] & 0x0F);
+            Association = (AssociationName)((buffer[offset + 1] >> 4) & 0x03);
+            IdentifierType = (IdentifierTypeName)(buffer[offset + 1] & 0x0F);
 
 
             IdentifierLength = buffer[offset + 3];
             IdentifierLength = buffer[offset + 3];
             Identifier = new byte[IdentifierLength];
             Identifier = new byte[IdentifierLength];
@@ -76,15 +84,15 @@ namespace ISCSI
             IdentifierLength = (byte)Identifier.Length;
             IdentifierLength = (byte)Identifier.Length;
 
 
             byte[] buffer = new byte[4 + Identifier.Length];
             byte[] buffer = new byte[4 + Identifier.Length];
-            buffer[0] |= (byte)(ProtocolIdentifier << 4);
-            buffer[0] |= (byte)(CodeSet & 0x0F);
+            buffer[0] |= (byte)((byte)ProtocolIdentifier << 4);
+            buffer[0] |= (byte)((byte)CodeSet & 0x0F);
             
             
             if (PIV)
             if (PIV)
             {
             {
                 buffer[1] |= 0x80;
                 buffer[1] |= 0x80;
             }
             }
-            buffer[1] |= (byte)((Association & 0x03) << 4);
-            buffer[1] |= (byte)(IdentifierType & 0x0F);
+            buffer[1] |= (byte)(((byte)Association & 0x03) << 4);
+            buffer[1] |= (byte)((byte)IdentifierType & 0x0F);
             buffer[3] = (byte)Identifier.Length;
             buffer[3] = (byte)Identifier.Length;
             Array.Copy(Identifier, 0, buffer, 4, Identifier.Length);
             Array.Copy(Identifier, 0, buffer, 4, Identifier.Length);
 
 
@@ -99,15 +107,38 @@ namespace ISCSI
             }
             }
         }
         }
 
 
-        public static IdentificationDescriptor GetISCSIIdentifier(string iqn)
+        /// <param name="oui">UInt24</param>
+        public static IdentificationDescriptor GetEUI64Identifier(uint oui, uint vendorSpecific)
+        {
+            byte[] eui64 = new byte[8];
+            WriteUInt24(eui64, 0, oui);
+            // we leave byte 3 zeroed-out
+            BigEndianWriter.WriteUInt32(eui64, 4, vendorSpecific);
+            return new IdentificationDescriptor(IdentifierTypeName.EUI64, eui64);
+        }
+
+        /// <param name="oui">UInt24</param>
+        public static IdentificationDescriptor GetNAAExtendedIdentifier(uint oui, uint vendorSpecific)
         {
         {
-            // RFC 3720: iSCSI names may be transported using both binary and ASCII-based protocols.
-            // Note: Microsoft iSCSI Target uses binary CodeSet
-            iqn += ",t,0x1"; // 't' for SCSI Target Port, 0x1 portal group tag
-            //byte[] bytes = ASCIIEncoding.ASCII.GetBytes(iqn);
-            IdentificationDescriptor result = new IdentificationDescriptor(iqn);
-            result.ProtocolIdentifier = 0x05; // iSCSI
+            byte[] identifier = new byte[8];
+            identifier[0] = 0x02 << 4;
+            WriteUInt24(identifier, 2, oui);
+            WriteUInt24(identifier, 5, vendorSpecific);
+            return new IdentificationDescriptor(IdentifierTypeName.NAA, identifier);
+        }
+
+        public static IdentificationDescriptor GetSCSINameStringIdentifier(string iqn)
+        {
+            IdentificationDescriptor result = new IdentificationDescriptor(IdentifierTypeName.ScsiNameString, iqn);
+            result.Association = AssociationName.TargetDevice;
+            result.ProtocolIdentifier = ProtocolName.ISCSI;
             return result;
             return result;
         }
         }
+
+        public static void WriteUInt24(byte[] buffer, int offset, uint value)
+        {
+            byte[] bytes = BigEndianConverter.GetBytes(value);
+            Array.Copy(bytes, 1, buffer, offset, 3);
+        }
     }
     }
 }
 }

+ 2 - 2
ISCSI/Server/ConnectionParameters.cs

@@ -29,11 +29,11 @@ namespace ISCSI.Server
         public int TargetMaxRecvDataSegmentLength = DeclaredMaxRecvDataSegmentLength;
         public int TargetMaxRecvDataSegmentLength = DeclaredMaxRecvDataSegmentLength;
 
 
         public uint StatSN = 0; // Initial StatSN, any number will do
         public uint StatSN = 0; // Initial StatSN, any number will do
-        // Dictionary of current transfers: <transfer-tag, <offset, length>>
+        // Dictionary of current transfers: <transfer-tag, <command-bytes, length>>
         // offset - logical block address (sector)
         // offset - logical block address (sector)
         // length - data transfer length in bytes
         // length - data transfer length in bytes
         // Note: here incoming means data write operations to the target
         // Note: here incoming means data write operations to the target
-        public Dictionary<uint, KeyValuePair<ulong, uint>> Transfers = new Dictionary<uint, KeyValuePair<ulong, uint>>();
+        public Dictionary<uint, KeyValuePair<byte[], uint>> Transfers = new Dictionary<uint, KeyValuePair<byte[], uint>>();
 
 
         // Dictionary of transfer data: <transfer-tag, command-data>
         // Dictionary of transfer data: <transfer-tag, command-data>
         public Dictionary<uint, byte[]> TransferData = new Dictionary<uint, byte[]>();
         public Dictionary<uint, byte[]> TransferData = new Dictionary<uint, byte[]>();

+ 12 - 25
ISCSI/Server/ISCSIServer.cs

@@ -187,7 +187,6 @@ namespace ISCSI.Server
                 return;
                 return;
             }
             }
 
 
-            Log("[{0}][ReceiveCallback] Received {1} bytes", state.ConnectionIdentifier, numberOfBytesReceived);
             byte[] currentBuffer = ByteReader.ReadBytes(state.ReceiveBuffer, 0, numberOfBytesReceived);
             byte[] currentBuffer = ByteReader.ReadBytes(state.ReceiveBuffer, 0, numberOfBytesReceived);
             ProcessCurrentBuffer(currentBuffer, state);
             ProcessCurrentBuffer(currentBuffer, state);
 
 
@@ -235,7 +234,6 @@ namespace ISCSI.Server
                 }
                 }
                 else
                 else
                 {
                 {
-                    Log("[{0}][ProcessCurrentBuffer] PDU is being processed, Length: {1}", state.ConnectionIdentifier, pduLength);
                     byte[] pduBytes = ByteReader.ReadBytes(state.ConnectionBuffer, bufferOffset, pduLength);
                     byte[] pduBytes = ByteReader.ReadBytes(state.ConnectionBuffer, bufferOffset, pduLength);
                     bytesLeftInBuffer -= pduLength;
                     bytesLeftInBuffer -= pduLength;
                     ISCSIPDU pdu = null;
                     ISCSIPDU pdu = null;
@@ -243,10 +241,6 @@ namespace ISCSI.Server
                     {
                     {
                         pdu = ISCSIPDU.GetPDU(pduBytes);
                         pdu = ISCSIPDU.GetPDU(pduBytes);
                     }
                     }
-                    catch (UnsupportedSCSICommandException)
-                    {
-                        pdu = new SCSICommandPDU(pduBytes, false);
-                    }
                     catch (Exception ex)
                     catch (Exception ex)
                     {
                     {
                         Log("[{0}][ProcessCurrentBuffer] Failed to read PDU (Exception: {1})", state.ConnectionIdentifier, ex.Message);
                         Log("[{0}][ProcessCurrentBuffer] Failed to read PDU (Exception: {1})", state.ConnectionIdentifier, ex.Message);
@@ -264,6 +258,7 @@ namespace ISCSI.Server
                             Log("[{0}][ProcessCurrentBuffer] Unsupported PDU (0x{1})", state.ConnectionIdentifier, pdu.OpCode.ToString("X"));
                             Log("[{0}][ProcessCurrentBuffer] Unsupported PDU (0x{1})", state.ConnectionIdentifier, pdu.OpCode.ToString("X"));
                             // Unsupported PDU
                             // Unsupported PDU
                             RejectPDU reject = new RejectPDU();
                             RejectPDU reject = new RejectPDU();
+                            reject.InitiatorTaskTag = pdu.InitiatorTaskTag;
                             reject.Reason = RejectReason.CommandNotSupported;
                             reject.Reason = RejectReason.CommandNotSupported;
                             reject.Data = ByteReader.ReadBytes(pduBytes, 0, 48);
                             reject.Data = ByteReader.ReadBytes(pduBytes, 0, 48);
                             TrySendPDU(state, reject);
                             TrySendPDU(state, reject);
@@ -277,7 +272,10 @@ namespace ISCSI.Server
                     if (!clientSocket.Connected)
                     if (!clientSocket.Connected)
                     {
                     {
                         // Do not continue to process the buffer if the other side closed the connection
                         // Do not continue to process the buffer if the other side closed the connection
-                        Log("[{0}][ProcessCurrentBuffer] Buffer processing aborted, bytes left in receive buffer: {1}", state.ConnectionIdentifier, bytesLeftInBuffer);
+                        if (bytesLeftInBuffer > 0)
+                        {
+                            Log("[{0}][ProcessCurrentBuffer] Buffer processing aborted, bytes left in receive buffer: {1}", state.ConnectionIdentifier, bytesLeftInBuffer);
+                        }
                         return;
                         return;
                     }
                     }
                 }
                 }
@@ -296,9 +294,9 @@ namespace ISCSI.Server
         public void ProcessPDU(ISCSIPDU pdu, StateObject state)
         public void ProcessPDU(ISCSIPDU pdu, StateObject state)
         {
         {
             Socket clientSocket = state.ClientSocket;
             Socket clientSocket = state.ClientSocket;
-            Log("[{0}][ProcessPDU] Received PDU from initiator, Operation: {1}, Size: {2}", state.ConnectionIdentifier, (ISCSIOpCodeName)pdu.OpCode, pdu.Length);
             
             
             uint? cmdSN = PDUHelper.GetCmdSN(pdu);
             uint? cmdSN = PDUHelper.GetCmdSN(pdu);
+            Log("[{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,
             // 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.
             // except for commands that are retransmitted due to digest error recovery and connection recovery.
             if (cmdSN.HasValue)
             if (cmdSN.HasValue)
@@ -307,6 +305,7 @@ namespace ISCSI.Server
                 {
                 {
                     if (cmdSN != state.SessionParameters.ExpCmdSN)
                     if (cmdSN != state.SessionParameters.ExpCmdSN)
                     {
                     {
+                        Log("[{0}][ProcessPDU] CmdSN outside of expected range", state.ConnectionIdentifier);
                         // We ignore this PDU
                         // We ignore this PDU
                         return;
                         return;
                     }
                     }
@@ -423,26 +422,14 @@ namespace ISCSI.Server
                     else if (pdu is SCSICommandPDU)
                     else if (pdu is SCSICommandPDU)
                     {
                     {
                         SCSICommandPDU command = (SCSICommandPDU)pdu;
                         SCSICommandPDU command = (SCSICommandPDU)pdu;
-                        if (command.CommandDescriptorBlock == null)
+                        ISCSIServer.Log("[{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)
                         {
                         {
-                            Log("[{0}][ProcessPDU] Unsupported SCSI Command (0x{1})", state.ConnectionIdentifier, command.OpCodeSpecific[12].ToString("X"));
-                            SCSIResponsePDU response = new SCSIResponsePDU();
-                            response.InitiatorTaskTag = command.InitiatorTaskTag;
-                            response.Status = SCSIStatusCodeName.CheckCondition;
-                            response.Data = SCSITarget.FormatSenseData(SenseDataParameter.GetIllegalRequestUnsupportedCommandCodeSenseData());
                             TrySendPDU(state, response);
                             TrySendPDU(state, response);
-                        }
-                        else
-                        {
-                            ISCSIServer.Log("[{0}][ProcessPDU] SCSICommandPDU: CmdSN: {1}, SCSI command: {2}, LUN: {3}, Data segment length: {4}, Expected Data Transfer Length: {5}, Final: {6}", state.ConnectionIdentifier, command.CmdSN, (SCSIOpCodeName)command.CommandDescriptorBlock.OpCode, (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)
+                            if (!clientSocket.Connected)
                             {
                             {
-                                TrySendPDU(state, response);
-                                if (!clientSocket.Connected)
-                                {
-                                    return;
-                                }
+                                return;
                             }
                             }
                         }
                         }
                     }
                     }

+ 98 - 53
ISCSI/Server/SCSITarget.cs

@@ -18,8 +18,26 @@ namespace ISCSI.Server
             m_disks = disks;
             m_disks = disks;
         }
         }
 
 
+        public SCSIStatusCodeName ExecuteCommand(byte[] commandBytes, LUNStructure lun, byte[] data, out byte[] response)
+        {
+            SCSICommandDescriptorBlock command;
+            try
+            {
+                command = SCSICommandDescriptorBlock.FromBytes(commandBytes, 0);
+            }
+            catch(UnsupportedSCSICommandException)
+            {
+                ISCSIServer.Log("[ExecuteCommand] Unsupported SCSI Command (0x{0})", commandBytes[0].ToString("X"));
+                response = SCSITarget.FormatSenseData(SenseDataParameter.GetIllegalRequestUnsupportedCommandCodeSenseData());
+                return SCSIStatusCodeName.CheckCondition;
+            }
+
+            return ExecuteCommand(command, lun, data, out response);
+        }
+
         public SCSIStatusCodeName ExecuteCommand(SCSICommandDescriptorBlock command, LUNStructure lun, byte[] data, out byte[] response)
         public SCSIStatusCodeName ExecuteCommand(SCSICommandDescriptorBlock command, LUNStructure lun, byte[] data, out byte[] response)
         {
         {
+            ISCSIServer.Log("[ExecuteCommand] {0}", command.OpCode);
             if (command.OpCode == SCSIOpCodeName.TestUnitReady)
             if (command.OpCode == SCSIOpCodeName.TestUnitReady)
             {
             {
                 return TestUnitReady(lun, out response);
                 return TestUnitReady(lun, out response);
@@ -96,18 +114,32 @@ namespace ISCSI.Server
 
 
             if (!command.EVPD)
             if (!command.EVPD)
             {
             {
-                StandardInquiryData inquiryData = new StandardInquiryData();
-                inquiryData.PeripheralDeviceType = 0; // Direct access block device
-                inquiryData.VendorIdentification = "iSCSIConsole";
-                inquiryData.ProductIdentification = "Disk_" + lun.ToString();
-                inquiryData.ProductRevisionLevel = "1.00";
-                inquiryData.DriveSerialNumber = 0;
-                inquiryData.CmdQue = true;
-                inquiryData.Version = 5; // Microsoft iSCSI Target report version 5
-                response = inquiryData.GetBytes();
+                if ((int)command.PageCode == 0)
+                {
+                    StandardInquiryData inquiryData = new StandardInquiryData();
+                    inquiryData.PeripheralDeviceType = 0; // Direct access block device
+                    inquiryData.VendorIdentification = "TalAloni";
+                    inquiryData.ProductIdentification = "iSCSI Disk " + ((ushort)lun).ToString();
+                    inquiryData.ProductRevisionLevel = "1.00";
+                    inquiryData.DriveSerialNumber = 0;
+                    inquiryData.CmdQue = true;
+                    inquiryData.Version = 5; // Microsoft iSCSI Target report version 5
+                    if (this is ISCSITarget)
+                    {
+                        inquiryData.VersionDescriptors.Add(0x0960); // iSCSI
+                    }
+                    response = inquiryData.GetBytes();
+                }
+                else
+                {
+                    ISCSIServer.Log("[Inquiry] Invalid request");
+                    response = FormatSenseData(SenseDataParameter.GetIllegalRequestInvalidFieldInCDBSenseData());
+                    return SCSIStatusCodeName.CheckCondition;
+                }
             }
             }
             else
             else
             {
             {
+                ISCSIServer.Log("[Inquiry] Page code: 0x{0}", command.PageCode.ToString("X"));
                 switch (command.PageCode)
                 switch (command.PageCode)
                 {
                 {
                     case VitalProductDataPageName.SupportedVPDPages:
                     case VitalProductDataPageName.SupportedVPDPages:
@@ -132,13 +164,12 @@ namespace ISCSI.Server
                     case VitalProductDataPageName.DeviceIdentification:
                     case VitalProductDataPageName.DeviceIdentification:
                         {
                         {
                             DeviceIdentificationVPDPage page = new DeviceIdentificationVPDPage();
                             DeviceIdentificationVPDPage page = new DeviceIdentificationVPDPage();
-                            // Identifiers necessity is preliminary, and has not been confirmed:
-                            // WWN identifier is needed to prevent 0xF4 BSOD during Windows setup
-                            // ISCSI identifier is needed for WinPE to pick up the disk during boot (after iPXE's sanhook)
-                            page.IdentificationDescriptorList.Add(new IdentificationDescriptor(5, lun));
+                            // NAA identifier is needed to prevent 0xF4 BSOD during Windows setup
+                            page.IdentificationDescriptorList.Add(IdentificationDescriptor.GetNAAExtendedIdentifier(5, lun));
                             if (this is ISCSITarget)
                             if (this is ISCSITarget)
                             {
                             {
-                                page.IdentificationDescriptorList.Add(IdentificationDescriptor.GetISCSIIdentifier(((ISCSITarget)this).TargetName));
+                                // ISCSI identifier is needed for WinPE to pick up the disk during boot (after iPXE's sanhook)
+                                page.IdentificationDescriptorList.Add(IdentificationDescriptor.GetSCSINameStringIdentifier(((ISCSITarget)this).TargetName));
                             }
                             }
                             response = page.GetBytes();
                             response = page.GetBytes();
                             break;
                             break;
@@ -163,7 +194,7 @@ namespace ISCSI.Server
                         }
                         }
                     default:
                     default:
                         {
                         {
-                            response = FormatSenseData(SenseDataParameter.GetIllegalRequestParameterNotSupportedSenseData());
+                            response = FormatSenseData(SenseDataParameter.GetIllegalRequestInvalidFieldInCDBSenseData());
                             ISCSIServer.Log("[Inquiry] Unsupported VPD Page request (0x{0})", command.PageCode.ToString("X"));
                             ISCSIServer.Log("[Inquiry] Unsupported VPD Page request (0x{0})", command.PageCode.ToString("X"));
                             return SCSIStatusCodeName.CheckCondition;
                             return SCSIStatusCodeName.CheckCondition;
                         }
                         }
@@ -186,16 +217,8 @@ namespace ISCSI.Server
                 return SCSIStatusCodeName.CheckCondition;
                 return SCSIStatusCodeName.CheckCondition;
             }
             }
 
 
-            ShortLBAModeParameterBlockDescriptor descriptor = new ShortLBAModeParameterBlockDescriptor();
-            descriptor.LogicalBlockLength = (uint)m_disks[lun].BytesPerSector;
-
-            ModeParameterHeader6 header = new ModeParameterHeader6();
-            header.WP = m_disks[lun].IsReadOnly; // Write protected, even when set to true, Windows does not always prevent the disk from being written to.
-            header.DPOFUA = true;  // Microsoft iSCSI Target support this
-            header.BlockDescriptorLength = (byte)descriptor.Length;
-            header.ModeDataLength += (byte)descriptor.Length;
-
-            byte[] pageData = new byte[0];
+            ISCSIServer.Log("[ModeSense6] Page code: 0x{0}, Sub page code: 0x{1}", command.PageCode.ToString("X"), command.SubpageCode.ToString("X"));
+            byte[] pageData;
 
 
             switch ((ModePageCodeName)command.PageCode)
             switch ((ModePageCodeName)command.PageCode)
             {
             {
@@ -203,35 +226,47 @@ namespace ISCSI.Server
                     {
                     {
                         CachingParametersPage page = new CachingParametersPage();
                         CachingParametersPage page = new CachingParametersPage();
                         page.RCD = true;
                         page.RCD = true;
-                        header.ModeDataLength += (byte)page.Length;
-                        pageData = new byte[page.Length];
-                        Array.Copy(page.GetBytes(), pageData, page.Length);
+                        pageData = page.GetBytes();
                         break;
                         break;
                     }
                     }
                 case ModePageCodeName.ControlModePage:
                 case ModePageCodeName.ControlModePage:
                     {
                     {
                         ControlModePage page = new ControlModePage();
                         ControlModePage page = new ControlModePage();
-                        header.ModeDataLength += (byte)page.Length;
-                        pageData = new byte[page.Length];
-                        Array.Copy(page.GetBytes(), pageData, page.Length);
+                        pageData = page.GetBytes();
                         break;
                         break;
                     }
                     }
+                case ModePageCodeName.PowerConditionModePage:
+                    {
+                        if (command.SubpageCode == 0x00)
+                        {
+                            PowerConditionModePage page = new PowerConditionModePage();
+                            pageData = page.GetBytes();
+                            break;
+                        }
+                        else if (command.SubpageCode == 0x01)
+                        {
+                            PowerConsumptionModePage page = new PowerConsumptionModePage();
+                            pageData = page.GetBytes();
+                            break;
+                        }
+                        else
+                        {
+                            response = FormatSenseData(SenseDataParameter.GetIllegalRequestInvalidFieldInCDBSenseData());
+                            ISCSIServer.Log("[ModeSense6] Power condition subpage 0x{0} is not implemented", command.SubpageCode.ToString("x"));
+                            return SCSIStatusCodeName.CheckCondition;
+                        }
+                    }
                 case ModePageCodeName.InformationalExceptionsControlModePage:
                 case ModePageCodeName.InformationalExceptionsControlModePage:
                     {
                     {
                         InformationalExceptionsControlModePage page = new InformationalExceptionsControlModePage();
                         InformationalExceptionsControlModePage page = new InformationalExceptionsControlModePage();
-                        header.ModeDataLength += (byte)page.Length;
-                        pageData = new byte[page.Length];
-                        Array.Copy(page.GetBytes(), pageData, page.Length);
+                        pageData = page.GetBytes();
                         break;
                         break;
                     }
                     }
                 case ModePageCodeName.ReturnAllPages:
                 case ModePageCodeName.ReturnAllPages:
                     {
                     {
                         CachingParametersPage page1 = new CachingParametersPage();
                         CachingParametersPage page1 = new CachingParametersPage();
                         page1.RCD = true;
                         page1.RCD = true;
-                        header.ModeDataLength += (byte)page1.Length;
-
                         InformationalExceptionsControlModePage page2 = new InformationalExceptionsControlModePage();
                         InformationalExceptionsControlModePage page2 = new InformationalExceptionsControlModePage();
-                        header.ModeDataLength += (byte)page2.Length;
 
 
                         pageData = new byte[page1.Length + page2.Length];
                         pageData = new byte[page1.Length + page2.Length];
                         Array.Copy(page1.GetBytes(), pageData, page1.Length);
                         Array.Copy(page1.GetBytes(), pageData, page1.Length);
@@ -242,22 +277,34 @@ namespace ISCSI.Server
                     {
                     {
                         // Microsoft iSCSI Target running under Windows 2000 will request this page, we immitate Microsoft iSCSI Target by sending back an empty page
                         // Microsoft iSCSI Target running under Windows 2000 will request this page, we immitate Microsoft iSCSI Target by sending back an empty page
                         VendorSpecificPage page = new VendorSpecificPage();
                         VendorSpecificPage page = new VendorSpecificPage();
-                        header.ModeDataLength += (byte)page.Length;
-                        pageData = new byte[page.Length];
-                        Array.Copy(page.GetBytes(), pageData, page.Length);
+                        pageData = page.GetBytes();
                         break;
                         break;
                     }
                     }
                 default:
                 default:
                     {
                     {
-                        response = FormatSenseData(SenseDataParameter.GetIllegalRequestParameterNotSupportedSenseData());
+                        response = FormatSenseData(SenseDataParameter.GetIllegalRequestInvalidFieldInCDBSenseData());
                         ISCSIServer.Log("[ModeSense6] ModeSense6 page 0x{0} is not implemented", command.PageCode.ToString("x"));
                         ISCSIServer.Log("[ModeSense6] ModeSense6 page 0x{0} is not implemented", command.PageCode.ToString("x"));
                         return SCSIStatusCodeName.CheckCondition;
                         return SCSIStatusCodeName.CheckCondition;
                     }
                     }
             }
             }
+
+            ModeParameterHeader6 header = new ModeParameterHeader6();
+            header.WP = m_disks[lun].IsReadOnly; // Write protected, even when set to true, Windows does not always prevent the disk from being written to.
+            header.DPOFUA = true;  // Microsoft iSCSI Target support this
+            byte[] descriptorBytes = new byte[0];
+            if (!command.DBD)
+            {
+                ShortLBAModeParameterBlockDescriptor descriptor = new ShortLBAModeParameterBlockDescriptor();
+                descriptor.LogicalBlockLength = (uint)m_disks[lun].BytesPerSector;
+                descriptorBytes = descriptor.GetBytes();
+            }
+            header.BlockDescriptorLength = (byte)descriptorBytes.Length;
+            header.ModeDataLength += (byte)(descriptorBytes.Length + pageData.Length);
+
             response = new byte[1 + header.ModeDataLength];
             response = new byte[1 + header.ModeDataLength];
             Array.Copy(header.GetBytes(), 0, response, 0, header.Length);
             Array.Copy(header.GetBytes(), 0, response, 0, header.Length);
-            Array.Copy(descriptor.GetBytes(), 0, response, header.Length, descriptor.Length);
-            Array.Copy(pageData, 0, response, header.Length + descriptor.Length, pageData.Length);
+            Array.Copy(descriptorBytes, 0, response, header.Length, descriptorBytes.Length);
+            Array.Copy(pageData, 0, response, header.Length + descriptorBytes.Length, pageData.Length);
             return SCSIStatusCodeName.Good;
             return SCSIStatusCodeName.Good;
         }
         }
 
 
@@ -311,6 +358,7 @@ namespace ISCSI.Server
             }
             }
             catch (ArgumentOutOfRangeException)
             catch (ArgumentOutOfRangeException)
             {
             {
+                ISCSIServer.Log("[Read] Read error: LBA out of range");
                 response = FormatSenseData(SenseDataParameter.GetIllegalRequestLBAOutOfRangeSenseData());
                 response = FormatSenseData(SenseDataParameter.GetIllegalRequestLBAOutOfRangeSenseData());
                 return SCSIStatusCodeName.CheckCondition;
                 return SCSIStatusCodeName.CheckCondition;
             }
             }
@@ -319,13 +367,14 @@ namespace ISCSI.Server
                 int error = Marshal.GetHRForException(ex);
                 int error = Marshal.GetHRForException(ex);
                 if (error == (int)Win32Error.ERROR_CRC)
                 if (error == (int)Win32Error.ERROR_CRC)
                 {
                 {
+                    ISCSIServer.Log("[Read] Read error: CRC error");
                     response = FormatSenseData(SenseDataParameter.GetWriteFaultSenseData());
                     response = FormatSenseData(SenseDataParameter.GetWriteFaultSenseData());
                     return SCSIStatusCodeName.CheckCondition;
                     return SCSIStatusCodeName.CheckCondition;
                 }
                 }
                 else
                 else
                 {
                 {
-                    ISCSIServer.Log("[{0}][Read] Read error:", ex.ToString());
-                    response = FormatSenseData(SenseDataParameter.GetUnitAttentionSenseData());
+                    ISCSIServer.Log("[Read] Read error: {0}", ex.ToString());
+                    response = FormatSenseData(SenseDataParameter.GetMediumErrorUnrecoverableReadErrorSenseData());
                     return SCSIStatusCodeName.CheckCondition;
                     return SCSIStatusCodeName.CheckCondition;
                 }
                 }
             }
             }
@@ -406,11 +455,6 @@ namespace ISCSI.Server
 
 
         public SCSIStatusCodeName Write(SCSICommandDescriptorBlock command, LUNStructure lun, byte[] data, out byte[] response)
         public SCSIStatusCodeName Write(SCSICommandDescriptorBlock command, LUNStructure lun, byte[] data, out byte[] response)
         {
         {
-            return Write(lun, (long)command.LogicalBlockAddress64, data, out response);
-        }
-
-        public SCSIStatusCodeName Write(LUNStructure lun, long sectorIndex, byte[] data, out byte[] response)
-        {
             if (lun >= m_disks.Count)
             if (lun >= m_disks.Count)
             {
             {
                 response = FormatSenseData(SenseDataParameter.GetIllegalRequestInvalidLUNSenseData());
                 response = FormatSenseData(SenseDataParameter.GetIllegalRequestInvalidLUNSenseData());
@@ -429,19 +473,20 @@ namespace ISCSI.Server
             {
             {
                 try
                 try
                 {
                 {
-                    disk.WriteSectors(sectorIndex, data);
+                    disk.WriteSectors((long)command.LogicalBlockAddress64, data);
                     response = new byte[0];
                     response = new byte[0];
                     return SCSIStatusCodeName.Good;
                     return SCSIStatusCodeName.Good;
                 }
                 }
                 catch (ArgumentOutOfRangeException)
                 catch (ArgumentOutOfRangeException)
                 {
                 {
+                    ISCSIServer.Log("[Write] Write error: LBA out of range");
                     response = FormatSenseData(SenseDataParameter.GetIllegalRequestLBAOutOfRangeSenseData());
                     response = FormatSenseData(SenseDataParameter.GetIllegalRequestLBAOutOfRangeSenseData());
                     return SCSIStatusCodeName.CheckCondition;
                     return SCSIStatusCodeName.CheckCondition;
                 }
                 }
                 catch (IOException ex)
                 catch (IOException ex)
                 {
                 {
-                    ISCSIServer.Log("[{0}][Write] Write error:", ex.ToString());
-                    response = FormatSenseData(SenseDataParameter.GetUnitAttentionSenseData());
+                    ISCSIServer.Log("[Write] Write error: {0}", ex.ToString());
+                    response = FormatSenseData(SenseDataParameter.GetMediumErrorWriteFaultSenseData());
                     return SCSIStatusCodeName.CheckCondition;
                     return SCSIStatusCodeName.CheckCondition;
                 }
                 }
             }
             }

+ 33 - 12
ISCSI/Server/TargetResponseHelper.cs

@@ -18,8 +18,6 @@ namespace ISCSI.Server
     {
     {
         internal static List<ISCSIPDU> GetSCSIResponsePDU(SCSICommandPDU command, ISCSITarget target, SessionParameters session, ConnectionParameters connection)
         internal static List<ISCSIPDU> GetSCSIResponsePDU(SCSICommandPDU command, ISCSITarget target, SessionParameters session, ConnectionParameters connection)
         {
         {
-            ushort LUN = command.LUN;
-
             // We return either SCSIResponsePDU or List<SCSIDataInPDU>
             // We return either SCSIResponsePDU or List<SCSIDataInPDU>
             List<ISCSIPDU> responseList = new List<ISCSIPDU>();
             List<ISCSIPDU> responseList = new List<ISCSIPDU>();
             
             
@@ -32,7 +30,7 @@ namespace ISCSI.Server
                 // Store segment (we only execute the command after receiving all of its data)
                 // Store segment (we only execute the command after receiving all of its data)
                 byte[] commandData = new byte[command.ExpectedDataTransferLength];
                 byte[] commandData = new byte[command.ExpectedDataTransferLength];
                 Array.Copy(command.Data, 0, commandData, 0, command.DataSegmentLength);
                 Array.Copy(command.Data, 0, commandData, 0, command.DataSegmentLength);
-                connection.Transfers.Add(transferTag, new KeyValuePair<ulong, uint>(command.CommandDescriptorBlock.LogicalBlockAddress64, command.ExpectedDataTransferLength));
+                connection.Transfers.Add(transferTag, new KeyValuePair<byte[], uint>(command.CommandDescriptorBlock, command.ExpectedDataTransferLength));
                 connection.TransferData.Add(transferTag, commandData);
                 connection.TransferData.Add(transferTag, commandData);
 
 
                 // Send R2T
                 // Send R2T
@@ -52,12 +50,17 @@ namespace ISCSI.Server
 
 
             byte[] scsiResponse;
             byte[] scsiResponse;
             SCSIStatusCodeName status = target.ExecuteCommand(command.CommandDescriptorBlock, command.LUN, command.Data, out scsiResponse);
             SCSIStatusCodeName status = target.ExecuteCommand(command.CommandDescriptorBlock, command.LUN, command.Data, out scsiResponse);
-            if (!command.Read)
+            if (!command.Read || status != SCSIStatusCodeName.Good)
             {
             {
+                // RFC 3720: if the command is completed with an error, then the response and sense data MUST be sent in a SCSI Response PDU
                 SCSIResponsePDU response = new SCSIResponsePDU();
                 SCSIResponsePDU response = new SCSIResponsePDU();
                 response.InitiatorTaskTag = command.InitiatorTaskTag;
                 response.InitiatorTaskTag = command.InitiatorTaskTag;
                 response.Status = status;
                 response.Status = status;
                 response.Data = scsiResponse;
                 response.Data = scsiResponse;
+                if (command.Read)
+                {
+                    EnforceExpectedDataTransferLength(response, command.ExpectedDataTransferLength);
+                }
                 responseList.Add(response);
                 responseList.Add(response);
             }
             }
             else if (scsiResponse.Length <= connection.InitiatorMaxRecvDataSegmentLength)
             else if (scsiResponse.Length <= connection.InitiatorMaxRecvDataSegmentLength)
@@ -128,10 +131,13 @@ namespace ISCSI.Server
                     // Last Data-out PDU
                     // Last Data-out PDU
                     ISCSIServer.Log("[{0}][GetSCSIDataOutResponsePDU] Last Data-out PDU", connectionIdentifier);
                     ISCSIServer.Log("[{0}][GetSCSIDataOutResponsePDU] Last Data-out PDU", connectionIdentifier);
                     
                     
-                    long sectorIndex = (long)connection.Transfers[request.TargetTransferTag].Key;
+                    byte[] commandBytes = connection.Transfers[request.TargetTransferTag].Key;
+                    byte[] scsiResponse;
+                    SCSIStatusCodeName status = target.ExecuteCommand(commandBytes, request.LUN, commandData, out scsiResponse);
                     SCSIResponsePDU response = new SCSIResponsePDU();
                     SCSIResponsePDU response = new SCSIResponsePDU();
                     response.InitiatorTaskTag = request.InitiatorTaskTag;
                     response.InitiatorTaskTag = request.InitiatorTaskTag;
-                    response.Status = target.Write(request.LUN, sectorIndex, commandData, out response.Data);
+                    response.Status = status;
+                    response.Data = scsiResponse;
                     connection.Transfers.Remove(request.TargetTransferTag);
                     connection.Transfers.Remove(request.TargetTransferTag);
                     connection.TransferData.Remove(request.TargetTransferTag);
                     connection.TransferData.Remove(request.TargetTransferTag);
                     session.NextR2TSN.Remove(request.TargetTransferTag);
                     session.NextR2TSN.Remove(request.TargetTransferTag);
@@ -153,12 +159,27 @@ namespace ISCSI.Server
             }
             }
             else
             else
             {
             {
-                ISCSIServer.Log("[{0}][GetSCSIDataOutResponsePDU] Unfamiliar TargetTransferTag", connectionIdentifier);
-                SCSIResponsePDU response = new SCSIResponsePDU();
-                response.InitiatorTaskTag = request.InitiatorTaskTag;
-                response.Status = SCSIStatusCodeName.CheckCondition;
-                response.Data = SCSITarget.FormatSenseData(SenseDataParameter.GetIllegalRequestSenseData());
-                return response;
+                ISCSIServer.Log("[{0}][GetSCSIDataOutResponsePDU] Invalid TargetTransferTag", connectionIdentifier);
+                RejectPDU reject = new RejectPDU();
+                reject.InitiatorTaskTag = request.InitiatorTaskTag;
+                reject.Reason = RejectReason.InvalidPDUField;
+                reject.Data = ByteReader.ReadBytes(request.GetBytes(), 0, 48);
+                return reject;
+            }
+        }
+
+        public static void EnforceExpectedDataTransferLength(SCSIResponsePDU response, uint expectedDataTransferLength)
+        {
+            if (response.Data.Length > expectedDataTransferLength)
+            {
+                response.ResidualOverflow = true;
+                response.ResidualCount = (uint)(expectedDataTransferLength - response.Data.Length);
+                response.Data = ByteReader.ReadBytes(response.Data, 0, (int)expectedDataTransferLength);
+            }
+            else if (response.Data.Length < expectedDataTransferLength)
+            {
+                response.ResidualUnderflow = true;
+                response.ResidualCount = (uint)(expectedDataTransferLength - response.Data.Length);
             }
             }
         }
         }
 
 

+ 2 - 2
ISCSIConsole/Properties/AssemblyInfo.cs

@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
 //      Build Number
 //      Build Number
 //      Revision
 //      Revision
 //
 //
-[assembly: AssemblyVersion("1.2.4.0")]
-[assembly: AssemblyFileVersion("1.2.4.0")]
+[assembly: AssemblyVersion("1.2.5.0")]
+[assembly: AssemblyFileVersion("1.2.5.0")]