package org.jscsi.target.connection.stage.fullfeature; import java.io.IOException; import java.nio.ByteBuffer; import java.security.DigestException; import java.util.List; import java.util.Vector; import org.jscsi.exception.InternetSCSIException; import org.jscsi.parser.BasicHeaderSegment; import org.jscsi.parser.ProtocolDataUnit; import org.jscsi.target.connection.TargetPduFactory; import org.jscsi.target.connection.phase.TargetFullFeaturePhase; import org.jscsi.target.settings.SettingsException; import org.jscsi.target.settings.TextKeyword; import org.jscsi.target.settings.TextParameter; import org.jscsi.target.util.ReadWrite; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A stage for processing requests by the initiator for a list of all targets * available through the iSCSI portal and for negotiating connection parameters * in the full feature phase. * <p> * That latter functionality is currently not implemented. * * @author Andreas Ergenzinger */ public final class TextNegotiationStage extends TargetFullFeatureStage { private static final Logger LOGGER = LoggerFactory.getLogger(TextNegotiationStage.class); public TextNegotiationStage(TargetFullFeaturePhase targetFullFeaturePhase) { super(targetFullFeaturePhase); } @Override public void execute(ProtocolDataUnit pdu) throws IOException, InterruptedException, InternetSCSIException, DigestException, SettingsException { final BasicHeaderSegment bhs = pdu.getBasicHeaderSegment(); final int initiatorTaskTag = bhs.getInitiatorTaskTag(); final String textRequest = ReadWrite.byteBufferToString(pdu.getDataSegment()); LOGGER.debug("text request: " + textRequest); ByteBuffer replyDataSegment = null;// for later // tokenize key-value pairs final List<String> requestKeyValuePairs = TextParameter.tokenizeKeyValuePairs(textRequest); final List<String> responseKeyValuePairs = new Vector<String>(); // process SendTargets command if (requestKeyValuePairs != null) { // A SendTargets command consists of a single Text request PDU. This // PDU contains exactly one text key and value. String sendTargetsValue = null; if (requestKeyValuePairs.size() == 1) sendTargetsValue = TextParameter.getSuffix(requestKeyValuePairs.get(0),// string TextKeyword.SEND_TARGETS + TextKeyword.EQUALS);// prefix if (sendTargetsValue != null) { // initiator wants target information /* * ALL must be supported in discovery session and must not be * supported in operational session. * * <name-of-this-target> must be supported in discovery session * (and it does no harm to support it in the operational * session). * * <nothing> must be supported in the operational session and * will only return info on the target the initiator is * connected to, this means no info is returned in discovery * session. * * outcome table | discovery |operational| * ---------------+-----------+-----------| ALL | TN + TA | fail * | ---------------+-----------+-----------| <this target> | TA * | TA | ---------------+-----------+-----------| <other * target> | fail | fail | * ---------------+-----------+-----------| <nothing> | fail | * TA | * * TN stands for TargetName, TA for TargetAddress "fail" means * no text response. */ final boolean normal = session.isNormalSession(); final boolean sendTargetName = // see upper table !normal && sendTargetsValue.equals(TextKeyword.ALL); final boolean sendTargetAddress = // see upper table (!normal && sendTargetsValue.equals(TextKeyword.ALL)) || (session.getTargetServer().isValidTargetName(sendTargetsValue)) || (normal && sendTargetsValue.length() == 0); /* * A target record consists of a TargetName key-value pair * followed by one or more TargetAddress key-value pairs for * that TargetName. * * (table above takes precedence to these definitions) */ // add TargetName if (sendTargetName) { final String localAddress = session.getTargetServer().getConfig().getTargetAddress(connection); for (String curTargetName : session.getTargetServer().getTargetNames()) { responseKeyValuePairs.add(TextParameter.toKeyValuePair(TextKeyword.TARGET_NAME, curTargetName)); // add TargetAddress if (sendTargetAddress) responseKeyValuePairs.add(TextParameter.toKeyValuePair( TextKeyword.TARGET_ADDRESS, localAddress + // domain TextKeyword.COLON + // : session.getTargetServer().getConfig().getPort() + // port TextKeyword.COMMA + // , session.getTargetServer().getConfig().getTargetPortalGroupTag())); // groupTag) } } else { // We're here if they sent us a target name and are asking for the address (I think) if (sendTargetAddress) { final String localAddress = session.getTargetServer().getConfig().getTargetAddress(connection); responseKeyValuePairs.add(TextParameter.toKeyValuePair(TextKeyword.TARGET_ADDRESS, localAddress + // domain TextKeyword.COLON + // : session.getTargetServer().getConfig().getPort() + // port TextKeyword.COMMA + // , session.getTargetServer().getConfig().getTargetPortalGroupTag())); // groupTag) } } } else { // initiator wants to negotiate or declare parameters // TODO } // concatenate and serialize reply final String replyString = TextParameter.concatenateKeyValuePairs(responseKeyValuePairs); LOGGER.debug("text negotiation stage reply: " + replyString); replyDataSegment = ReadWrite.stringToTextDataSegments(replyString, settings.getMaxRecvDataSegmentLength())[0];// definitely // fits // into // one // data // segment } // send reply final ProtocolDataUnit responsePdu = TargetPduFactory.createTextResponsePdu(true,// finalFlag false,// continueFlag 0,// logicalUnitNumber initiatorTaskTag, 0xffffffff,// targetTransferTag replyDataSegment);// dataSegment connection.sendPdu(responsePdu); } }