private LoginResponsePDU GetLoginResponsePDU(LoginRequestPDU request, ConnectionParameters connection) { // RFC 3720: The numbering fields (StatSN, ExpCmdSN, MaxCmdSN) are only valid if status-Class is 0. // RFC 3720: Command numbering starts with the first login request on the first connection of a session if (request.Continue) { connection.AddTextToSequence(request.InitiatorTaskTag, request.LoginParametersText); return(GetPartialLoginResponsePDU(request, connection)); } else { string text = connection.AddTextToSequence(request.InitiatorTaskTag, request.LoginParametersText); connection.RemoveTextSequence(request.InitiatorTaskTag); KeyValuePairList <string, string> loginParameters = KeyValuePairUtils.GetKeyValuePairList(text); if (connection.Session == null) { LoginResponseStatusName status = SetUpSession(request, loginParameters, connection); if (status != LoginResponseStatusName.Success) { LoginResponsePDU response = GetLoginResponseTemplate(request); response.Transit = request.Transit; response.Status = status; return(response); } } return(GetFinalLoginResponsePDU(request, loginParameters, connection)); } }
internal static LoginRequestPDU GetSecondStageLoginRequest(LoginResponsePDU firstStageResponse, SessionParameters session, ConnectionParameters connection, bool isDiscovery) { LoginRequestPDU request = new LoginRequestPDU(); request.ISID = firstStageResponse.ISID; request.TSIH = firstStageResponse.TSIH; request.CID = connection.CID; request.InitiatorTaskTag = session.GetNextTaskTag(); request.CmdSN = session.GetNextCmdSN(false); request.CurrentStage = firstStageResponse.NextStage; request.NextStage = 3; request.Transit = true; request.VersionMax = 0; request.VersionMin = 0; request.LoginParameters.Add("HeaderDigest", "None"); request.LoginParameters.Add("DataDigest", "None"); request.LoginParameters.Add("MaxRecvDataSegmentLength", connection.InitiatorMaxRecvDataSegmentLength.ToString()); if (!isDiscovery) { request.LoginParameters.Add("ErrorRecoveryLevel", ISCSIClient.OfferedErrorRecoveryLevel.ToString()); request.LoginParameters.Add("InitialR2T", ISCSIClient.OfferedInitialR2T ? "Yes" : "No"); request.LoginParameters.Add("ImmediateData", ISCSIClient.OfferedImmediateData ? "Yes" : "No"); request.LoginParameters.Add("MaxBurstLength", ISCSIClient.OfferedMaxBurstLength.ToString()); request.LoginParameters.Add("FirstBurstLength", ISCSIClient.OfferedFirstBurstLength.ToString()); request.LoginParameters.Add("MaxConnections", ISCSIClient.OfferedMaxConnections.ToString()); request.LoginParameters.Add("DataPDUInOrder", ISCSIClient.OfferedDataPDUInOrder ? "Yes" : "No"); request.LoginParameters.Add("DataSequenceInOrder", ISCSIClient.OfferedDataSequenceInOrder ? "Yes" : "No"); request.LoginParameters.Add("MaxOutstandingR2T", ISCSIClient.OfferedMaxOutstandingR2T.ToString()); } request.LoginParameters.Add("DefaultTime2Wait", ISCSIClient.OfferedDefaultTime2Wait.ToString()); request.LoginParameters.Add("DefaultTime2Retain", ISCSIClient.OfferedDefaultTime2Retain.ToString()); return(request); }
/// <param name="targetName">Set to null for discovery session</param> public bool Login(string targetName) { if (!m_isConnected) { throw new InvalidOperationException("iSCSI client is not connected"); } m_session.ISID = ClientHelper.GetRandomISID(); m_connection.CID = m_session.GetNextCID(); // p.s. It's possible to perform a single stage login (stage 1 to stage 3, tested against Microsoft iSCSI Target v3.1) LoginRequestPDU request = ClientHelper.GetFirstStageLoginRequest(m_initiatorName, targetName, m_session, m_connection); SendPDU(request); LoginResponsePDU response = WaitForPDU(request.InitiatorTaskTag) as LoginResponsePDU; if (response != null && response.Status == LoginResponseStatusName.Success) { // Status numbering starts with the Login response to the first Login request of the connection m_connection.StatusNumberingStarted = true; m_connection.ExpStatSN = response.StatSN + 1; request = ClientHelper.GetSecondStageLoginRequest(response, m_session, m_connection, targetName == null); SendPDU(request); response = WaitForPDU(request.InitiatorTaskTag) as LoginResponsePDU; if (response != null && response.Status == LoginResponseStatusName.Success) { ClientHelper.UpdateOperationalParameters(response.LoginParameters, m_session, m_connection); return(true); } } return(false); }
private LoginResponsePDU GetPartialLoginResponsePDU(LoginRequestPDU request, ConnectionParameters connection) { LoginResponsePDU response = GetLoginResponseTemplate(request); response.Transit = false; response.InitiatorTaskTag = request.InitiatorTaskTag; response.ExpCmdSN = request.CmdSN; // We must set ExpCmdSN ourselves because we haven't set up the session yet. if (request.Transit) { Log(Severity.Warning, "[{0}] Initiator error: Received login request with both Transit and Continue set to true", connection.ConnectionIdentifier); response.Status = LoginResponseStatusName.InitiatorError; return(response); } response.Status = LoginResponseStatusName.Success; return(response); }
private LoginResponsePDU GetLoginResponseTemplate(LoginRequestPDU request) { LoginResponsePDU response = new LoginResponsePDU(); response.Continue = false; response.CurrentStage = request.CurrentStage; response.NextStage = request.NextStage; response.VersionMax = request.VersionMax; response.VersionActive = request.VersionMin; response.ISID = request.ISID; // TSIH: With the exception of the Login Final-Response in a new session, this field should // be set to the TSIH provided by the initiator in the Login Request. response.TSIH = request.TSIH; response.InitiatorTaskTag = request.InitiatorTaskTag; return(response); }
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")); } } }
private LoginResponsePDU GetFinalLoginResponsePDU(LoginRequestPDU request, KeyValuePairList <string, string> requestParameters, ConnectionParameters connection) { LoginResponsePDU response = GetLoginResponseTemplate(request); response.Transit = request.Transit; response.TSIH = connection.Session.TSIH; string connectionIdentifier = connection.ConnectionIdentifier; response.Status = LoginResponseStatusName.Success; ISCSISession session = connection.Session; // RFC 3720: The login process proceeds in two stages - the security negotiation // stage and the operational parameter negotiation stage. Both stages are optional // but at least one of them has to be present. // The stage codes are: // 0 - SecurityNegotiation // 1 - LoginOperationalNegotiation // 3 - FullFeaturePhase if (request.CurrentStage == 0) { KeyValuePairList <string, string> responseParameters = new KeyValuePairList <string, string>(); responseParameters.Add("AuthMethod", "None"); if (session.Target != null) { // RFC 3720: During the Login Phase the iSCSI target MUST return the TargetPortalGroupTag key with the first Login Response PDU with which it is allowed to do so responseParameters.Add("TargetPortalGroupTag", "1"); } if (request.Transit) { if (request.NextStage == 3) { session.IsFullFeaturePhase = true; } else if (request.NextStage != 1) { Log(Severity.Warning, "[{0}] Initiator error: Received login request with Invalid NextStage", connectionIdentifier); response.Status = LoginResponseStatusName.InitiatorError; } } response.LoginParameters = responseParameters; } else if (request.CurrentStage == 1) { UpdateOperationalParameters(requestParameters, connection); response.LoginParameters = GetLoginResponseOperationalParameters(connection); if (request.Transit) { if (request.NextStage == 3) { session.IsFullFeaturePhase = true; } else { Log(Severity.Warning, "[{0}] Initiator error: Received login request with Invalid NextStage", connectionIdentifier); response.Status = LoginResponseStatusName.InitiatorError; } } } else { // Not valid Log(Severity.Warning, "[{0}] Initiator error: Received login request with Invalid CurrentStage", connectionIdentifier); response.Status = LoginResponseStatusName.InitiatorError; } return(response); }
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"); }
internal static LoginResponsePDU GetLoginResponsePDU(LoginRequestPDU request, List <ISCSITarget> availableTargets, SessionParameters session, ConnectionParameters connection, ref ISCSITarget target, GetNextTSIH GetNextTSIH) { LoginResponsePDU response = new LoginResponsePDU(); response.Transit = request.Transit; response.Continue = false; // The stage codes are: // 0 - SecurityNegotiation // 1 - LoginOperationalNegotiation // 3 - FullFeaturePhase response.CurrentStage = request.CurrentStage; response.NextStage = request.NextStage; response.VersionMax = request.VersionMax; response.VersionActive = request.VersionMin; response.ISID = request.ISID; response.Status = LoginResponseStatusName.Success; response.InitiatorTaskTag = request.InitiatorTaskTag; if (request.TSIH == 0) { // For a new session, the request TSIH is zero, // As part of the response, the target generates a TSIH. session.TSIH = GetNextTSIH(); } response.TSIH = session.TSIH; if (request.Transit && request.Continue) { response.Status = LoginResponseStatusName.InitiatorError; return(response); } else if (request.Continue) { response.Status = LoginResponseStatusName.Success; return(response); } // RFC 3720: The login process proceeds in two stages - the security negotiation // stage and the operational parameter negotiation stage. Both stages are optional // but at least one of them has to be present. bool firstLoginRequest = (!session.IsDiscovery && target == null); if (firstLoginRequest) { string sessionType = request.LoginParameters.ValueOf("SessionType"); if (sessionType == "Discovery") { session.IsDiscovery = true; } else //sessionType == "Normal" or unspecified (default is Normal) { session.IsDiscovery = false; // RFC 3720: For any connection within a session whose type is not "Discovery", the first Login Request MUST also include the TargetName key=value pair. if (request.LoginParameters.ContainsKey("TargetName")) { string targetName = request.LoginParameters.ValueOf("TargetName"); int targetIndex = GetTargetIndex(availableTargets, targetName); if (targetIndex >= 0) { target = availableTargets[targetIndex]; } else { response.Status = LoginResponseStatusName.NotFound; return(response); } } else { response.Status = LoginResponseStatusName.InitiatorError; return(response); } } } if (request.CurrentStage == 0) { response.LoginParameters.Add("AuthMethod", "None"); if (request.Transit) { if (request.NextStage == 3) { session.IsFullFeaturePhase = true; } else if (request.NextStage != 1) { response.Status = LoginResponseStatusName.InitiatorError; } } } else if (request.CurrentStage == 1) { UpdateOperationalParameters(request.LoginParameters, session, connection); response.LoginParameters = GetLoginOperationalParameters(session, connection); if (request.Transit) { if (request.NextStage == 3) { session.IsFullFeaturePhase = true; } else { response.Status = LoginResponseStatusName.InitiatorError; } } } else { // Not valid response.Status = LoginResponseStatusName.InitiatorError; } return(response); }