View Javadoc

1   package org.jscsi.target.connection.stage.login;
2   
3   
4   import java.io.IOException;
5   import java.nio.ByteBuffer;
6   import java.security.DigestException;
7   
8   import org.jscsi.exception.InternetSCSIException;
9   import org.jscsi.parser.BasicHeaderSegment;
10  import org.jscsi.parser.OperationCode;
11  import org.jscsi.parser.ProtocolDataUnit;
12  import org.jscsi.parser.login.LoginRequestParser;
13  import org.jscsi.parser.login.LoginStage;
14  import org.jscsi.parser.login.LoginStatus;
15  import org.jscsi.target.connection.TargetPduFactory;
16  import org.jscsi.target.connection.phase.TargetLoginPhase;
17  import org.jscsi.target.connection.stage.TargetStage;
18  import org.jscsi.target.settings.ConnectionSettingsNegotiator;
19  import org.jscsi.target.settings.SettingsException;
20  import org.jscsi.target.util.ReadWrite;
21  
22  
23  /**
24   * This class is an abstract super-class for stages of the {@link TargetLoginPhase} (see <code>Connection</code> for a
25   * description of the relationship between sessions, connections, phases and sessions), namely the
26   * {@link LoginOperationalParameterNegotiationStage} and the {@link SecurityNegotiationStage}.
27   * <p>
28   * The stage is started by calling the {@link #execute(ProtocolDataUnit)} method with the first {@link ProtocolDataUnit}
29   * to be processed as part of the stage.
30   * <p>
31   * Of equal importance is the {@link #getNextStageNumber()} method, which must be used to find out which stage or phase
32   * will follow this one.
33   * 
34   * @author Andreas Ergenzinger
35   */
36  public abstract class TargetLoginStage extends TargetStage {
37  
38      /**
39       * Manages the text parameter negotiation.
40       */
41      protected final ConnectionSettingsNegotiator negotiator;
42  
43      /**
44       * The stage number used for describing this stage in Login Request PDUs.
45       * 
46       * @see LoginRequestParser#getCurrentStageNumber()
47       * @see LoginRequestParser#getNextStageNumber()
48       */
49      protected final LoginStage stageNumber;
50  
51      /**
52       * An identifier used by the initiator to identify the login task. Sent with the first Login Request PDU.
53       */
54      protected int initiatorTaskTag;
55  
56      /**
57       * A stage number describing which stage the initiator wants to transition to. This value will be updated with every
58       * received PDU.
59       * 
60       * @see #stageNumber
61       */
62      protected LoginStage requestedNextStageNumber;
63  
64      /**
65       * A stage number describing which stage must follow this stage.
66       * <p>
67       * This value is initialized with <code>null</code>, and will be changed in {@link #execute(ProtocolDataUnit)}, if
68       * the stage was finished successfully.
69       * 
70       * @see #stageNumber
71       */
72      protected LoginStage nextStageNumber;
73  
74      /**
75       * The abstract constructor.
76       * 
77       * @param targetLoginPhase the phase this stage is a part of
78       * @param stageNumber the stage number used for describing this stage in Login Request PDUs
79       */
80      public TargetLoginStage (final TargetLoginPhase targetLoginPhase, final LoginStage stageNumber) {
81          super(targetLoginPhase);
82          this.stageNumber = stageNumber;
83          negotiator = connection.getConnectionSettingsNegotiator();
84      }
85  
86      /**
87       * Returns <code>true</code>, if and only if the specified PDU is a Login Request PDU and the CSN and
88       * InitiatorTaskTag fields check out.
89       * 
90       * @param pdu the PDU to check
91       * @return <code>true</code> if the PDU checks out
92       */
93      protected boolean checkPdu (ProtocolDataUnit pdu) {
94          final BasicHeaderSegment bhs = pdu.getBasicHeaderSegment();
95          final LoginRequestParser parser = (LoginRequestParser) bhs.getParser();
96          if (bhs.getOpCode() == OperationCode.LOGIN_REQUEST && parser.getCurrentStageNumber() == stageNumber && bhs.getInitiatorTaskTag() == initiatorTaskTag) return true;
97          return false;
98      }
99  
100     /**
101      * Receives a sequence of Login Request PDUs (as indicated by the {@link LoginRequestParser#isContinueFlag()} and
102      * returns the concatenated content of the text data segments.
103      * 
104      * @return the concatenated content of the text data segments
105      * @throws DigestException
106      * @throws InternetSCSIException
107      * @throws IOException
108      * @throws SettingsException
109      * @throws InterruptedException
110      */
111     protected final String receivePduSequence () throws DigestException , InternetSCSIException , IOException , SettingsException , InterruptedException {
112         final ProtocolDataUnit pdu = connection.receivePdu();
113         return receivePduSequence(pdu);
114     }
115 
116     /**
117      * Receives a sequence of Login Request PDUs (as indicated by the {@link LoginRequestParser#isContinueFlag()} and
118      * returns the concatenated content of the text data segments.
119      * 
120      * @param pdu the first PDU of the sequence
121      * @return the concatenated content of the text data segments
122      * @throws InternetSCSIException
123      * @throws InterruptedException
124      * @throws IOException
125      * @throws DigestException
126      * @throws SettingsException
127      */
128     protected final String receivePduSequence (ProtocolDataUnit pdu) throws InternetSCSIException , InterruptedException , IOException , DigestException , SettingsException {
129 
130         // StringBuilder for the key-value pairs received during this sequence
131         final StringBuilder stringBuilder = new StringBuilder();
132 
133         // for accessing the fields of the last received PDU
134         BasicHeaderSegment bhs;
135         LoginRequestParser parser;
136 
137         // begin sequence
138         int sequenceLength = 1;
139         while (sequenceLength <= session.getTargetServer().getConfig().getInMaxRecvTextPduSequenceLength()) {
140             bhs = pdu.getBasicHeaderSegment();
141             parser = (LoginRequestParser) bhs.getParser();
142 
143             // check PDU
144             if (!checkPdu(pdu)) {
145                 // send login reject and leave stage
146                 sendRejectPdu(LoginStatus.INVALID_DURING_LOGIN);
147                 throw new InternetSCSIException("Wrong PDU in TargetLoginStage");
148             }
149 
150             // PDU is okay, so append text data segment to stringBuilder
151             ReadWrite.appendTextDataSegmentToStringBuffer(pdu.getDataSegment(), stringBuilder);
152 
153             // remember what stage the initiator wants to transition to
154             requestedNextStageNumber = parser.getNextStageNumber();
155 
156             // continue?
157             if (parser.isContinueFlag()) {
158                 // send reception confirmation
159                 pdu = TargetPduFactory.createLoginResponsePdu(false,// transitFlag
160                         false,// continueFlag
161                         stageNumber,// currentStage
162                         stageNumber,// nextStage
163                         session.getInitiatorSessionID(),// initiatorSessionID
164                         session.getTargetSessionIdentifyingHandle(),// targetSessionIdentifyingHandle
165                         initiatorTaskTag, LoginStatus.SUCCESS,// status
166                         ByteBuffer.allocate(0));// dataSegment
167                 connection.sendPdu(pdu);
168 
169                 // receive the next pdu
170                 pdu = connection.receivePdu();
171             } else
172                 // sequence is over
173                 return stringBuilder.toString();
174         }
175 
176         // initiator's text PDU sequence was too long
177         // send login reject and leave stage
178         sendRejectPdu(LoginStatus.OUT_OF_RESOURCES);
179         throw new InternetSCSIException("Wrong PDU in TargetLoginStage");
180     }
181 
182     /**
183      * Sends a Login Response PDU sequence containing the specified <i>key-value</i> pairs.
184      * 
185      * @param keyValuePairs contains <i>key-value</i> pairs to send
186      * @param nextStage indicates if the target is willing to transition to a different stage
187      * @throws SettingsException
188      * @throws InterruptedException
189      * @throws IOException
190      * @throws InternetSCSIException
191      * @throws DigestException
192      */
193     protected final void sendPduSequence (final String keyValuePairs, final LoginStage nextStage) throws SettingsException , InterruptedException , IOException , InternetSCSIException , DigestException {
194 
195         // some variables
196         ProtocolDataUnit pdu;
197         BasicHeaderSegment bhs;
198         LoginRequestParser parser;
199         boolean continueFlag = true;
200         boolean transitFlag = false;
201 
202         // split input string into text data segments
203         final ByteBuffer[] dataSegments = ReadWrite.stringToTextDataSegments(keyValuePairs,// string
204                 settings.getMaxRecvDataSegmentLength());// bufferSize
205 
206         // send all data segments (and receive confirmations)
207         for (int i = 0; i < dataSegments.length; ++i) {
208 
209             // adjust flags
210             if (i == dataSegments.length - 1) {
211                 continueFlag = false;
212                 if (stageNumber != nextStage) transitFlag = true;
213             }
214 
215             // create and send PDU
216             pdu = TargetPduFactory.createLoginResponsePdu(transitFlag,// transitFlag
217                     continueFlag,// continueFlag
218                     stageNumber,// currentStage
219                     nextStage,// nextStage
220                     session.getInitiatorSessionID(),// initiatorSessionID
221                     session.getTargetSessionIdentifyingHandle(),// targetSessionIdentifyingHandle
222                     initiatorTaskTag, LoginStatus.SUCCESS,// status
223                     dataSegments[i]);// dataSegment
224             connection.sendPdu(pdu);
225 
226             // receive confirmation
227             if (continueFlag) {
228                 // receive and check
229                 pdu = connection.receivePdu();
230                 bhs = pdu.getBasicHeaderSegment();
231                 parser = (LoginRequestParser) bhs.getParser();
232                 if (!checkPdu(pdu) || parser.isContinueFlag()) {
233                     // send login reject and leave stage
234                     sendRejectPdu(LoginStatus.INITIATOR_ERROR);
235                     throw new InternetSCSIException();
236                 }
237             }
238         }
239     }
240 
241     /**
242      * Sends a Login Response PDU informing the initiator that an error has occurred and that the connection must be
243      * closed.
244      * 
245      * @param errorStatus hints to the cause of the error
246      * @throws InterruptedException
247      * @throws IOException
248      * @throws InternetSCSIException
249      */
250     protected final void sendRejectPdu (final LoginStatus errorStatus) throws InterruptedException , IOException , InternetSCSIException {
251         final ProtocolDataUnit rejectPDU = TargetPduFactory.createLoginResponsePdu(false,// transit flag
252                 false,// continueFlag
253                 stageNumber,// currentStage
254                 stageNumber,// nextStage
255                 session.getInitiatorSessionID(),// initiatorSessionID
256                 session.getTargetSessionIdentifyingHandle(),// targetSessionIdentifyingHandle
257                 initiatorTaskTag,// initiatorTaskTag
258                 errorStatus,// status
259                 ByteBuffer.allocate(0));// dataSegment
260         connection.sendPdu(rejectPDU);
261     }
262 
263     /**
264      * Returns a stage number describing which stage of phase must follow this stage, or <code>null</code> if the
265      * initiator is not allowed to transition any further.
266      * 
267      * @return an identifier of the next stage/phase or <code>null</code>
268      */
269     public final LoginStage getNextStageNumber () {
270         return nextStageNumber;
271     }
272 }