From 4982b9614516dde101c3e44c60a612b3bfd8d6fe Mon Sep 17 00:00:00 2001 From: DELL <DELL@WIN-3EOIPEE9ML1> Date: 星期三, 21 二月 2024 16:26:06 +0800 Subject: [PATCH] 新增IEDScout调试工具功能 --- iec61850_forFoShanAES_Model/src/org/openmuc/josistack/AcseAssociation.java | 1914 +++++++++++++++++++++++++++++----------------------------- 1 files changed, 957 insertions(+), 957 deletions(-) diff --git a/iec61850_forFoShanAES_Model/src/org/openmuc/josistack/AcseAssociation.java b/iec61850_forFoShanAES_Model/src/org/openmuc/josistack/AcseAssociation.java index 79946f4..92cf6e7 100644 --- a/iec61850_forFoShanAES_Model/src/org/openmuc/josistack/AcseAssociation.java +++ b/iec61850_forFoShanAES_Model/src/org/openmuc/josistack/AcseAssociation.java @@ -1,957 +1,957 @@ -/* - * Copyright 2011-17 Fraunhofer ISE, energy & meteo Systems GmbH and other contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package org.openmuc.josistack; - -import java.io.ByteArrayInputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.math.BigInteger; -import java.net.InetAddress; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.TimeoutException; - -import org.openmuc.jasn1.ber.ReverseByteArrayOutputStream; -import org.openmuc.jasn1.ber.types.BerAny; -import org.openmuc.jasn1.ber.types.BerInteger; -import org.openmuc.jasn1.ber.types.BerObjectIdentifier; -import org.openmuc.jasn1.ber.types.string.BerGraphicString; -import org.openmuc.josistack.internal.acse.asn1.AAREApdu; -import org.openmuc.josistack.internal.acse.asn1.AARQApdu; -import org.openmuc.josistack.internal.acse.asn1.ACSEApdu; -import org.openmuc.josistack.internal.acse.asn1.ACSERequirements; -import org.openmuc.josistack.internal.acse.asn1.AEQualifier; -import org.openmuc.josistack.internal.acse.asn1.AEQualifierForm2; -import org.openmuc.josistack.internal.acse.asn1.APTitle; -import org.openmuc.josistack.internal.acse.asn1.APTitleForm2; -import org.openmuc.josistack.internal.acse.asn1.AssociateResult; -import org.openmuc.josistack.internal.acse.asn1.AssociateSourceDiagnostic; -import org.openmuc.josistack.internal.acse.asn1.AssociationInformation; -import org.openmuc.josistack.internal.acse.asn1.AuthenticationValue; -import org.openmuc.josistack.internal.acse.asn1.MechanismName; -import org.openmuc.josistack.internal.acse.asn1.Myexternal; -import org.openmuc.josistack.internal.presentation.asn1.CPAPPDU; -import org.openmuc.josistack.internal.presentation.asn1.CPType; -import org.openmuc.josistack.internal.presentation.asn1.CalledPresentationSelector; -import org.openmuc.josistack.internal.presentation.asn1.CallingPresentationSelector; -import org.openmuc.josistack.internal.presentation.asn1.FullyEncodedData; -import org.openmuc.josistack.internal.presentation.asn1.ModeSelector; -import org.openmuc.josistack.internal.presentation.asn1.PDVList; -import org.openmuc.josistack.internal.presentation.asn1.PresentationContextDefinitionList; -import org.openmuc.josistack.internal.presentation.asn1.PresentationContextDefinitionResultList; -import org.openmuc.josistack.internal.presentation.asn1.PresentationContextIdentifier; -import org.openmuc.josistack.internal.presentation.asn1.RespondingPresentationSelector; -import org.openmuc.josistack.internal.presentation.asn1.UserData; -import org.openmuc.jositransport.ClientTSap; -import org.openmuc.jositransport.TConnection; - -public final class AcseAssociation { - - // private static final Logger logger = LoggerFactory.getLogger(AcseAssociation.class); - - private boolean connected = false; - private TConnection tConnection; - private ByteBuffer associateResponseAPDU = null; - private final RespondingPresentationSelector pSelLocalBerOctetString; - - private static final PresentationContextDefinitionList context_list = new PresentationContextDefinitionList( - new byte[] { (byte) 0x23, (byte) 0x30, (byte) 0x0f, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x06, - (byte) 0x04, (byte) 0x52, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x30, (byte) 0x04, - (byte) 0x06, (byte) 0x02, (byte) 0x51, (byte) 0x01, (byte) 0x30, (byte) 0x10, (byte) 0x02, - (byte) 0x01, (byte) 0x03, (byte) 0x06, (byte) 0x05, (byte) 0x28, (byte) 0xca, (byte) 0x22, - (byte) 0x02, (byte) 0x01, (byte) 0x30, (byte) 0x04, (byte) 0x06, (byte) 0x02, (byte) 0x51, - (byte) 0x01 }); - - private static final PresentationContextIdentifier acsePresentationContextId = new PresentationContextIdentifier( - new byte[] { (byte) 0x01, (byte) 0x01 }); - private static final ModeSelector normalModeSelector = new ModeSelector(); - - private static final PresentationContextDefinitionResultList presentationResultList = new PresentationContextDefinitionResultList( - new byte[] { (byte) 0x12, (byte) 0x30, (byte) 0x07, (byte) 0x80, (byte) 0x01, (byte) 0x00, (byte) 0x81, - (byte) 0x02, (byte) 0x51, (byte) 0x01, (byte) 0x30, (byte) 0x07, (byte) 0x80, (byte) 0x01, - (byte) 0x00, (byte) 0x81, (byte) 0x02, (byte) 0x51, (byte) 0x01 }); - - private static final AssociateResult aareAccepted = new AssociateResult(new byte[] { (byte) 0x01, (byte) 0x00 }); - - private static final AssociateSourceDiagnostic associateSourceDiagnostic = new AssociateSourceDiagnostic( - new byte[] { (byte) 0xa1, (byte) 0x03, (byte) 0x02, (byte) 0x01, (byte) 0x00 }); - - // is always equal to 1.0.9506.2.3 (MMS) - private static final BerObjectIdentifier application_context_name = new BerObjectIdentifier( - new byte[] { (byte) 0x05, (byte) 0x28, (byte) 0xca, (byte) 0x22, (byte) 0x02, (byte) 0x03 }); - - private static final BerObjectIdentifier directReference = new BerObjectIdentifier( - new byte[] { (byte) 0x02, (byte) 0x51, (byte) 0x01 }); - private static final BerInteger indirectReference = new BerInteger(new byte[] { (byte) 0x01, (byte) 0x03 }); - - private static final MechanismName default_mechanism_name = new MechanismName( - new byte[] { 0x03, 0x52, 0x03, 0x01 }); - - static { - normalModeSelector.setModeValue(new BerInteger(BigInteger.ONE)); - } - - AcseAssociation(TConnection tConnection, byte[] pSelLocal) { - this.tConnection = tConnection; - pSelLocalBerOctetString = new RespondingPresentationSelector(pSelLocal); - } - - /** - * A server that got an Association Request Indication may use this function to accept the association. - * - * @param payload - * the payload to send with the accept message - * @throws IOException - * if an error occures accepting the association - */ - public void accept(ByteBuffer payload) throws IOException { - - BerAny anyPayload = new BerAny(Arrays.copyOfRange(payload.array(), payload.position(), payload.limit())); - - Myexternal.Encoding encoding = new Myexternal.Encoding(); - encoding.setSingleASN1Type(anyPayload); - - Myexternal myExternal = new Myexternal(); - myExternal.setDirectReference(directReference); - myExternal.setIndirectReference(indirectReference); - myExternal.setEncoding(encoding); - - AssociationInformation userInformation = new AssociationInformation(); - List<Myexternal> externalList = userInformation.getMyexternal(); - externalList.add(myExternal); - - AAREApdu aare = new AAREApdu(); - aare.setApplicationContextName(application_context_name); - aare.setResult(aareAccepted); - aare.setResultSourceDiagnostic(associateSourceDiagnostic); - aare.setUserInformation(userInformation); - - ACSEApdu acse = new ACSEApdu(); - acse.setAare(aare); - - ReverseByteArrayOutputStream reverseOStream = new ReverseByteArrayOutputStream(100, true); - acse.encode(reverseOStream); - - UserData userData = getPresentationUserDataField(reverseOStream.getArray()); - CPAPPDU.NormalModeParameters normalModeParameters = new CPAPPDU.NormalModeParameters(); - normalModeParameters.setRespondingPresentationSelector(pSelLocalBerOctetString); - normalModeParameters.setPresentationContextDefinitionResultList(presentationResultList); - normalModeParameters.setUserData(userData); - - CPAPPDU cpaPPdu = new CPAPPDU(); - cpaPPdu.setModeSelector(normalModeSelector); - cpaPPdu.setNormalModeParameters(normalModeParameters); - - reverseOStream.reset(); - cpaPPdu.encode(reverseOStream, true); - - List<byte[]> ssduList = new LinkedList<>(); - List<Integer> ssduOffsets = new LinkedList<>(); - List<Integer> ssduLengths = new LinkedList<>(); - - ssduList.add(reverseOStream.buffer); - ssduOffsets.add(reverseOStream.index + 1); - ssduLengths.add(reverseOStream.buffer.length - (reverseOStream.index + 1)); - - writeSessionAccept(ssduList, ssduOffsets, ssduLengths); - - connected = true; - } - - private void writeSessionAccept(List<byte[]> ssdu, List<Integer> ssduOffsets, List<Integer> ssduLengths) - throws IOException { - byte[] sduAcceptHeader = new byte[20]; - int idx = 0; - - int ssduLength = 0; - for (int ssduElementLength : ssduLengths) { - ssduLength += ssduElementLength; - } - - // write ISO 8327-1 Header - // SPDU Type: ACCEPT (14) - sduAcceptHeader[idx++] = 0x0e; - // Length: length of session user data + 22 ( header data after length - // field ) - sduAcceptHeader[idx++] = (byte) ((ssduLength + 18) & 0xff); - - // -- start Connect Accept Item - // Parameter type: Connect Accept Item (5) - sduAcceptHeader[idx++] = 0x05; - // Parameter length - sduAcceptHeader[idx++] = 0x06; - - // Protocol options: - // Parameter Type: Protocol Options (19) - sduAcceptHeader[idx++] = 0x13; - // Parameter length - sduAcceptHeader[idx++] = 0x01; - // flags: (.... ...0 = Able to receive extended concatenated SPDU: - // False) - sduAcceptHeader[idx++] = 0x00; - - // Version number: - // Parameter type: Version Number (22) - sduAcceptHeader[idx++] = 0x16; - // Parameter length - sduAcceptHeader[idx++] = 0x01; - // flags: (.... ..1. = Protocol Version 2: True) - sduAcceptHeader[idx++] = 0x02; - // -- end Connect Accept Item - - // Session Requirement - // Parameter type: Session Requirement (20) - sduAcceptHeader[idx++] = 0x14; - // Parameter length - sduAcceptHeader[idx++] = 0x02; - // flags: (.... .... .... ..1. = Duplex functional unit: True) - sduAcceptHeader[idx++] = 0x00; - sduAcceptHeader[idx++] = 0x02; - - // Called Session Selector - // Parameter type: Called Session Selector (52) - sduAcceptHeader[idx++] = 0x34; - // Parameter length - sduAcceptHeader[idx++] = 0x02; - // Called Session Selector - sduAcceptHeader[idx++] = 0x00; - sduAcceptHeader[idx++] = 0x01; - - // Session user data - // Parameter type: Session user data (193) - sduAcceptHeader[idx++] = (byte) 0xc1; - - // Parameter length - sduAcceptHeader[idx++] = (byte) ssduLength; - - ssdu.add(0, sduAcceptHeader); - ssduOffsets.add(0, 0); - ssduLengths.add(0, sduAcceptHeader.length); - - tConnection.send(ssdu, ssduOffsets, ssduLengths); - } - - public ByteBuffer getAssociateResponseAPdu() { - ByteBuffer returnBuffer = associateResponseAPDU; - associateResponseAPDU = null; - return returnBuffer; - } - - /** - * Starts an Application Association by sending an association request and waiting for an association accept message - * - * @param payload - * payload that can be sent with the association request - * @param port - * @param address - * @param tSAP - * @param aeQualifierCalling - * @param aeQualifierCalled - * @param apTitleCalling - * @param apTitleCalled - * @throws IOException - */ - void startAssociation(ByteBuffer payload, InetAddress address, int port, InetAddress localAddr, int localPort, - String authenticationParameter, byte[] sSelRemote, byte[] sSelLocal, byte[] pSelRemote, ClientTSap tSAP, - int[] apTitleCalled, int[] apTitleCalling, int aeQualifierCalled, int aeQualifierCalling) - throws IOException { - if (connected == true) { - throw new IOException(); - } - - APTitle called_ap_title = new APTitle(); - called_ap_title.setApTitleForm2(new APTitleForm2(apTitleCalled)); - APTitle calling_ap_title = new APTitle(); - calling_ap_title.setApTitleForm2(new APTitleForm2(apTitleCalling)); - - AEQualifier called_ae_qualifier = new AEQualifier(); - called_ae_qualifier.setAeQualifierForm2(new AEQualifierForm2(aeQualifierCalled)); - AEQualifier calling_ae_qualifier = new AEQualifier(); - calling_ae_qualifier.setAeQualifierForm2(new AEQualifierForm2(aeQualifierCalling)); - - Myexternal.Encoding encoding = new Myexternal.Encoding(); - encoding.setSingleASN1Type( - new BerAny(Arrays.copyOfRange(payload.array(), payload.position(), payload.limit()))); - - Myexternal myExternal = new Myexternal(); - myExternal.setDirectReference(directReference); - myExternal.setIndirectReference(indirectReference); - myExternal.setEncoding(encoding); - - AssociationInformation userInformation = new AssociationInformation(); - List<Myexternal> externalList = userInformation.getMyexternal(); - externalList.add(myExternal); - - ACSERequirements sender_acse_requirements = null; - MechanismName mechanism_name = null; - AuthenticationValue authentication_value = null; - if (authenticationParameter != null) { - sender_acse_requirements = new ACSERequirements(new byte[] { (byte) 0x02, (byte) 0x07, (byte) 0x80 }); - mechanism_name = default_mechanism_name; - authentication_value = new AuthenticationValue(); - authentication_value.setCharstring(new BerGraphicString(authenticationParameter.getBytes())); - } - - AARQApdu aarq = new AARQApdu(); - aarq.setApplicationContextName(application_context_name); - aarq.setCalledAPTitle(called_ap_title); - aarq.setCalledAEQualifier(called_ae_qualifier); - aarq.setCallingAPTitle(calling_ap_title); - aarq.setCallingAEQualifier(calling_ae_qualifier); - aarq.setSenderAcseRequirements(sender_acse_requirements); - aarq.setMechanismName(mechanism_name); - aarq.setCallingAuthenticationValue(authentication_value); - aarq.setUserInformation(userInformation); - - ACSEApdu acse = new ACSEApdu(); - acse.setAarq(aarq); - - ReverseByteArrayOutputStream reverseOStream = new ReverseByteArrayOutputStream(200, true); - acse.encode(reverseOStream); - - UserData userData = getPresentationUserDataField(reverseOStream.getArray()); - - CPType.NormalModeParameters normalModeParameter = new CPType.NormalModeParameters(); - normalModeParameter - .setCallingPresentationSelector(new CallingPresentationSelector(pSelLocalBerOctetString.value)); - normalModeParameter.setCalledPresentationSelector(new CalledPresentationSelector(pSelRemote)); - normalModeParameter.setPresentationContextDefinitionList(context_list); - normalModeParameter.setUserData(userData); - - CPType cpType = new CPType(); - cpType.setModeSelector(normalModeSelector); - cpType.setNormalModeParameters(normalModeParameter); - - reverseOStream.reset(); - cpType.encode(reverseOStream, true); - - List<byte[]> ssduList = new LinkedList<>(); - List<Integer> ssduOffsets = new LinkedList<>(); - List<Integer> ssduLengths = new LinkedList<>(); - - ssduList.add(reverseOStream.buffer); - ssduOffsets.add(reverseOStream.index + 1); - ssduLengths.add(reverseOStream.buffer.length - (reverseOStream.index + 1)); - - ByteBuffer res = null; - res = startSConnection(ssduList, ssduOffsets, ssduLengths, address, port, localAddr, localPort, tSAP, - sSelRemote, sSelLocal); - - associateResponseAPDU = decodePConResponse(res); - - } - - private static ByteBuffer decodePConResponse(ByteBuffer ppdu) throws IOException { - - CPAPPDU cpa_ppdu = new CPAPPDU(); - InputStream iStream = new ByteBufferInputStream(ppdu); - cpa_ppdu.decode(iStream); - - iStream = new ByteArrayInputStream(cpa_ppdu.getNormalModeParameters() - .getUserData() - .getFullyEncodedData() - .getPDVList() - .get(0) - .getPresentationDataValues() - .getSingleASN1Type().value); - - ACSEApdu acseApdu = new ACSEApdu(); - acseApdu.decode(iStream, null); - return ByteBuffer.wrap( - acseApdu.getAare().getUserInformation().getMyexternal().get(0).getEncoding().getSingleASN1Type().value); - - } - - private static UserData getPresentationUserDataField(byte[] userDataBytes) { - PDVList.PresentationDataValues presDataValues = new PDVList.PresentationDataValues(); - presDataValues.setSingleASN1Type(new BerAny(userDataBytes)); - PDVList pdvList = new PDVList(); - pdvList.setPresentationContextIdentifier(acsePresentationContextId); - pdvList.setPresentationDataValues(presDataValues); - - FullyEncodedData fullyEncodedData = new FullyEncodedData(); - List<PDVList> pdvListList = fullyEncodedData.getPDVList(); - pdvListList.add(pdvList); - - UserData userData = new UserData(); - userData.setFullyEncodedData(fullyEncodedData); - return userData; - } - - /** - * Starts a session layer connection, sends a CONNECT (CN), waits for a ACCEPT (AC) and throws an IOException if not - * successful - * - * @throws IOException - */ - private ByteBuffer startSConnection(List<byte[]> ssduList, List<Integer> ssduOffsets, List<Integer> ssduLengths, - InetAddress address, int port, InetAddress localAddr, int localPort, ClientTSap tSAP, byte[] sSelRemote, - byte[] sSelLocal) throws IOException { - if (connected == true) { - throw new IOException(); - } - - byte[] spduHeader = new byte[24]; - int idx = 0; - // byte[] res = null; - - int ssduLength = 0; - for (int ssduElementLength : ssduLengths) { - ssduLength += ssduElementLength; - } - - // write ISO 8327-1 Header - // SPDU Type: CONNECT (13) - spduHeader[idx++] = 0x0d; - // Length: length of session user data + 22 ( header data after - // length field ) - spduHeader[idx++] = (byte) ((ssduLength + 22) & 0xff); - - // -- start Connect Accept Item - // Parameter type: Connect Accept Item (5) - spduHeader[idx++] = 0x05; - // Parameter length - spduHeader[idx++] = 0x06; - - // Protocol options: - // Parameter Type: Protocol Options (19) - spduHeader[idx++] = 0x13; - // Parameter length - spduHeader[idx++] = 0x01; - // flags: (.... ...0 = Able to receive extended concatenated SPDU: - // False) - spduHeader[idx++] = 0x00; - - // Version number: - // Parameter type: Version Number (22) - spduHeader[idx++] = 0x16; - // Parameter length - spduHeader[idx++] = 0x01; - // flags: (.... ..1. = Protocol Version 2: True) - spduHeader[idx++] = 0x02; - // -- end Connect Accept Item - - // Session Requirement - // Parameter type: Session Requirement (20) - spduHeader[idx++] = 0x14; - // Parameter length - spduHeader[idx++] = 0x02; - // flags: (.... .... .... ..1. = Duplex functional unit: True) - spduHeader[idx++] = 0x00; - spduHeader[idx++] = 0x02; - - // Calling Session Selector - // Parameter type: Calling Session Selector (51) - spduHeader[idx++] = 0x33; - // Parameter length - spduHeader[idx++] = 0x02; - // Calling Session Selector - spduHeader[idx++] = sSelRemote[0]; - spduHeader[idx++] = sSelRemote[1]; - - // Called Session Selector - // Parameter type: Called Session Selector (52) - spduHeader[idx++] = 0x34; - // Parameter length - spduHeader[idx++] = 0x02; - // Called Session Selector - spduHeader[idx++] = sSelLocal[0]; - spduHeader[idx++] = sSelLocal[1]; - - // Session user data - // Parameter type: Session user data (193) - spduHeader[idx++] = (byte) 0xc1; - // Parameter length - spduHeader[idx++] = (byte) (ssduLength & 0xff); - // write session user data - - ssduList.add(0, spduHeader); - ssduOffsets.add(0, 0); - ssduLengths.add(0, spduHeader.length); - - tConnection = tSAP.connectTo(address, port, localAddr, localPort); - - tConnection.send(ssduList, ssduOffsets, ssduLengths); - - // TODO how much should be allocated here? - ByteBuffer pduBuffer = ByteBuffer.allocate(500); - - try { - tConnection.receive(pduBuffer); - } catch (TimeoutException e) { - throw new IOException("ResponseTimeout waiting for connection response.", e); - } - idx = 0; - - // read ISO 8327-1 Header - // SPDU Type: ACCEPT (14) - byte spduType = pduBuffer.get(); - if (spduType != 0x0e) { - throw new IOException("ISO 8327-1 header wrong SPDU type, expected ACCEPT (14), got " - + getSPDUTypeString(spduType) + " (" + spduType + ")"); - } - pduBuffer.get(); // skip length byte - - parameter_loop: while (true) { - // read parameter type - int parameterType = pduBuffer.get() & 0xff; - // read parameter length - int parameterLength = pduBuffer.get() & 0xff; - - switch (parameterType) { - // Connect Accept Item (5) - case 0x05: - int bytesToRead = parameterLength; - while (bytesToRead > 0) { - // read parameter type - int ca_parameterType = pduBuffer.get(); - // read parameter length - // int ca_parameterLength = res[idx++]; - pduBuffer.get(); - - bytesToRead -= 2; - - switch (ca_parameterType & 0xff) { - // Protocol Options (19) - case 0x13: - // flags: .... ...0 = Able to receive extended - // concatenated SPDU: False - byte protocolOptions = pduBuffer.get(); - if (protocolOptions != 0x00) { - throw new IOException( - "SPDU Connect Accept Item/Protocol Options is " + protocolOptions + ", expected 0"); - } - - bytesToRead--; - break; - // Version Number - case 0x16: - // flags .... ..1. = Protocol Version 2: True - byte versionNumber = pduBuffer.get(); - if (versionNumber != 0x02) { - throw new IOException( - "SPDU Connect Accept Item/Version Number is " + versionNumber + ", expected 2"); - } - - bytesToRead--; - break; - default: - throw new IOException( - "SPDU Connect Accept Item: parameter not implemented: " + ca_parameterType); - } - } - break; - // Session Requirement (20) - case 0x14: - // flags: (.... .... .... ..1. = Duplex functional unit: True) - long sessionRequirement = extractInteger(pduBuffer, parameterLength); - if (sessionRequirement != 0x02) { - throw new IOException("SPDU header parameter 'Session Requirement (20)' is " + sessionRequirement - + ", expected 2"); - - } - break; - // Calling Session Selector (51) - case 0x33: - long css = extractInteger(pduBuffer, parameterLength); - if (css != 0x01) { - throw new IOException( - "SPDU header parameter 'Calling Session Selector (51)' is " + css + ", expected 1"); - - } - break; - // Called Session Selector (52) - case 0x34: - long calledSessionSelector = extractInteger(pduBuffer, parameterLength); - if (calledSessionSelector != 0x01) { - throw new IOException("SPDU header parameter 'Called Session Selector (52)' is " - + calledSessionSelector + ", expected 1"); - } - break; - // Session user data (193) - case 0xc1: - break parameter_loop; - default: - throw new IOException("SPDU header parameter type " + parameterType + " not implemented"); - } - } - - // got correct ACCEPT (AC) from the server - - connected = true; - - return pduBuffer; - } - - public void send(ByteBuffer payload) throws IOException { - - List<byte[]> ssduList = new ArrayList<>(); - List<Integer> ssduOffsets = new LinkedList<>(); - List<Integer> ssduLengths = new LinkedList<>(); - - encodePresentationLayer(payload, ssduList, ssduOffsets, ssduLengths); - - encodeSessionLayer(ssduList, ssduOffsets, ssduLengths); - - tConnection.send(ssduList, ssduOffsets, ssduLengths); - } - - private void encodePresentationLayer(ByteBuffer payload, List<byte[]> ssduList, List<Integer> ssduOffsets, - List<Integer> ssduLengths) throws IOException { - PDVList pdv_list = new PDVList(); - pdv_list.setPresentationContextIdentifier(new PresentationContextIdentifier(3l)); - - PDVList.PresentationDataValues presentationDataValues = new PDVList.PresentationDataValues(); - presentationDataValues.setSingleASN1Type( - new BerAny(Arrays.copyOfRange(payload.array(), payload.position(), payload.limit()))); - pdv_list.setPresentationDataValues(presentationDataValues); - - FullyEncodedData fully_encoded_data = new FullyEncodedData(); - List<PDVList> pdv_list_list = fully_encoded_data.getPDVList(); - pdv_list_list.add(pdv_list); - - UserData user_data = new UserData(); - user_data.setFullyEncodedData(fully_encoded_data); - - ReverseByteArrayOutputStream reverseOStream = new ReverseByteArrayOutputStream(200, true); - user_data.encode(reverseOStream); - - ssduList.add(reverseOStream.buffer); - ssduOffsets.add(reverseOStream.index + 1); - ssduLengths.add(reverseOStream.buffer.length - (reverseOStream.index + 1)); - } - - private void encodeSessionLayer(List<byte[]> ssduList, List<Integer> ssduOffsets, List<Integer> ssduLengths) - throws IOException { - - byte[] spduHeader = new byte[4]; - // --write iso 8327-1 Header-- - // write SPDU Type: give tokens PDU - spduHeader[0] = 0x01; - // length 0 - spduHeader[1] = 0; - // write SPDU Type: DATA TRANSFER (DT) - spduHeader[2] = 0x01; - // length 0 - spduHeader[3] = 0; - - ssduList.add(0, spduHeader); - ssduOffsets.add(0, 0); - ssduLengths.add(0, spduHeader.length); - - } - - /** - * Listens for a new PDU and writes it into the given buffer. Decodes all ACSE and lower layer headers. The - * resulting buffer's position points to the beginning of the ACSE SDU. The limit will point to the byte after the - * last byte of the ACSE SDU. - * - * @param pduBuffer - * buffer to write the received pdu into - * @throws DecodingException - * if a decoding error occurs - * @throws IOException - * if a non recoverable error occurs. Afterwards the association should be closed by the user - * @throws TimeoutException - * if a timeout occurs - */ - public byte[] receive(ByteBuffer pduBuffer) throws DecodingException, IOException, TimeoutException { - if (connected == false) { - throw new IllegalStateException("ACSE Association not connected"); - } - tConnection.receive(pduBuffer); - - decodeSessionLayer(pduBuffer); - - return decodePresentationLayer(pduBuffer); - } - - private byte[] decodePresentationLayer(ByteBuffer pduBuffer) throws DecodingException { - // decode PPDU header - UserData user_data = new UserData(); - - try { - user_data.decode(new ByteBufferInputStream(pduBuffer), null); - } catch (IOException e) { - throw new DecodingException("error decoding PPDU header", e); - } - - return user_data.getFullyEncodedData() - .getPDVList() - .get(0) - .getPresentationDataValues() - .getSingleASN1Type().value; - } - - private void decodeSessionLayer(ByteBuffer pduBuffer) throws EOFException, DecodingException { - int firstByte = pduBuffer.get(); - - if (firstByte == 25) { - // got an ABORT SPDU - throw new EOFException("Received an ABORT SPDU"); - } - - // -- read ISO 8327-1 header - // SPDU type: Give tokens PDU (1) - if (firstByte != 0x01) { - throw new DecodingException("SPDU header syntax errror: first SPDU type not 1"); - } - // length - if (pduBuffer.get() != 0) { - throw new DecodingException("SPDU header syntax errror: first SPDU type length not 0"); - } - // SPDU Type: DATA TRANSFER (DT) SPDU (1) - if (pduBuffer.get() != 0x01) { - throw new DecodingException("SPDU header syntax errror: second SPDU type not 1"); - } - // length - if (pduBuffer.get() != 0) { - throw new DecodingException("SPDU header syntax errror: second SPDU type length not 0"); - } - } - - /** - * Disconnects by sending a disconnect request at the Transport Layer and then closing the socket. - */ - public void disconnect() { - connected = false; - if (tConnection != null) { - tConnection.disconnect(); - } - } - - /** - * Closes the connection simply by closing the socket. - */ - public void close() { - connected = false; - if (tConnection != null) { - tConnection.close(); - } - } - - private long extractInteger(ByteBuffer buffer, int size) throws IOException { - switch (size) { - case 1: - return buffer.get(); - case 2: - return buffer.getShort(); - case 4: - return buffer.getInt(); - case 8: - return buffer.getLong(); - default: - throw new IOException("invalid length for reading numeric value"); - } - } - - ByteBuffer listenForCn(ByteBuffer pduBuffer) throws IOException, TimeoutException { - if (connected == true) { - throw new IllegalStateException("ACSE Association is already connected"); - } - int parameter; - int parameterLength; - - tConnection.receive(pduBuffer); - // start reading ISO 8327-1 header - // SPDU Type: CONNECT (CN) SPDU (13) - byte spduType = pduBuffer.get(); - if (spduType != 0x0d) { - throw new IOException("ISO 8327-1 header wrong SPDU type, expected CONNECT (13), got " - + getSPDUTypeString(spduType) + " (" + spduType + ")"); - } - pduBuffer.get(); // skip lenght byte - - parameter_loop: while (true) { - // read parameter code - parameter = pduBuffer.get() & 0xff; - // read parameter length - parameterLength = pduBuffer.get() & 0xff; - switch (parameter) { - // Connect Accept Item (5) - case 0x05: - int bytesToRead = parameterLength; - while (bytesToRead > 0) { - // read parameter type - int ca_parameterType = pduBuffer.get(); - // read parameter length - pduBuffer.get(); - - bytesToRead -= 2; - - switch (ca_parameterType & 0xff) { - // Protocol Options (19) - case 0x13: - // flags: .... ...0 = Able to receive extended - // concatenated SPDU: False - byte protocolOptions = pduBuffer.get(); - if (protocolOptions != 0x00) { - throw new IOException( - "SPDU Connect Accept Item/Protocol Options is " + protocolOptions + ", expected 0"); - } - - bytesToRead--; - break; - // Version Number - case 0x16: - // flags .... ..1. = Protocol Version 2: True - byte versionNumber = pduBuffer.get(); - if (versionNumber != 0x02) { - throw new IOException( - "SPDU Connect Accept Item/Version Number is " + versionNumber + ", expected 2"); - } - - bytesToRead--; - break; - default: - throw new IOException( - "SPDU Connect Accept Item: parameter not implemented: " + ca_parameterType); - } - } - break; - // Session Requirement (20) - case 0x14: - // flags: (.... .... .... ..1. = Duplex functional unit: True) - long sessionRequirement = extractInteger(pduBuffer, parameterLength); - if (sessionRequirement != 0x02) { - throw new IOException("SPDU header parameter 'Session Requirement (20)' is " + sessionRequirement - + ", expected 2"); - } - break; - // Calling Session Selector (51) - case 0x33: - extractInteger(pduBuffer, parameterLength); - break; - // Called Session Selector (52) - case 0x34: - long calledSessionSelector = extractInteger(pduBuffer, parameterLength); - if (calledSessionSelector != 0x01) { - throw new IOException("SPDU header parameter 'Called Session Selector (52)' is " - + calledSessionSelector + ", expected 1"); - } - break; - // Session user data (193) - case 0xc1: - break parameter_loop; - default: - throw new IOException("SPDU header parameter type " + parameter + " not implemented"); - } - - } - - CPType cpType = new CPType(); - InputStream iStream = new ByteBufferInputStream(pduBuffer); - cpType.decode(iStream, true); - - iStream = new ByteArrayInputStream(cpType.getNormalModeParameters() - .getUserData() - .getFullyEncodedData() - .getPDVList() - .get(0) - .getPresentationDataValues() - .getSingleASN1Type().value); - - ACSEApdu acseApdu = new ACSEApdu(); - acseApdu.decode(iStream, null); - return ByteBuffer.wrap( - acseApdu.getAarq().getUserInformation().getMyexternal().get(0).getEncoding().getSingleASN1Type().value); - - } - - public int getMessageTimeout() { - return tConnection.getMessageTimeout(); - } - - public void setMessageTimeout(int i) { - tConnection.setMessageTimeout(i); - } - - public static String getSPDUTypeString(byte spduType) { - switch (spduType) { - case 0: - return "EXCEPTION REPORT (ER)"; - case 1: - return "DATA TRANSFER (DT)"; - case 2: - return "PLEASE TOKENS (PT)"; - case 5: - return "EXPEDITED (EX)"; - case 7: - return "PREPARE (PR)"; - case 8: - return "NOT FINISHED (NF)"; - case 9: - return "FINISH (FN)"; - case 10: - return "DISCONNECT (DN)"; - case 12: - return "REFUSE (RF)"; - case 13: - return "CONNECT (CN)"; - case 14: - return "ACCEPT (AC)"; - case 15: - return "CONNECT DATA OVERFLOW (CDO)"; - case 16: - return "OVERFLOW ACCEPT (OA)"; - case 21: - return "GIVE TOKENS CONFIRM (GTC)"; - case 22: - return "GIVE TOKENS ACK (GTA)"; - case 25: - return "ABORT (AB)"; - case 26: - return "ABORT ACCEPT (AA)"; - case 29: - return "ACTIVITY RESUME (AR)"; - case 33: - return "TYPED DATA (TD)"; - case 34: - return "RESYNCHRONIZE ACK (RA)"; - case 41: - return "MAJOR SYNC POINT (MAP)"; - case 42: - return "MAJOR SYNC ACK (MAA)"; - case 45: - return "ACTIVITY START (AS)"; - case 48: - return "EXCEPTION DATA (ED)"; - case 49: - return "MINOR SYNC POINT (MIP)"; - case 50: - return "MINOR SYNC ACK (MIA)"; - case 53: - return "RESYNCHRONIZE (RS)"; - case 57: - return "ACTIVITY DISCARD (AD)"; - case 58: - return "ACTIVITY DISCARD ACK (ADA)"; - case 61: - return "CAPABILITY DATA (CD)"; - case 62: - return "CAPABILITY DATA ACK (CDA)"; - case 64: - return "UNIT DATA (UD)"; - default: - return "<unknown SPDU type>"; - } - } -} +/* + * Copyright 2011-17 Fraunhofer ISE, energy & meteo Systems GmbH and other contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.openmuc.josistack; + +import java.io.ByteArrayInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeoutException; + +import org.openmuc.jasn1.ber.ReverseByteArrayOutputStream; +import org.openmuc.jasn1.ber.types.BerAny; +import org.openmuc.jasn1.ber.types.BerInteger; +import org.openmuc.jasn1.ber.types.BerObjectIdentifier; +import org.openmuc.jasn1.ber.types.string.BerGraphicString; +import org.openmuc.josistack.internal.acse.asn1.AAREApdu; +import org.openmuc.josistack.internal.acse.asn1.AARQApdu; +import org.openmuc.josistack.internal.acse.asn1.ACSEApdu; +import org.openmuc.josistack.internal.acse.asn1.ACSERequirements; +import org.openmuc.josistack.internal.acse.asn1.AEQualifier; +import org.openmuc.josistack.internal.acse.asn1.AEQualifierForm2; +import org.openmuc.josistack.internal.acse.asn1.APTitle; +import org.openmuc.josistack.internal.acse.asn1.APTitleForm2; +import org.openmuc.josistack.internal.acse.asn1.AssociateResult; +import org.openmuc.josistack.internal.acse.asn1.AssociateSourceDiagnostic; +import org.openmuc.josistack.internal.acse.asn1.AssociationInformation; +import org.openmuc.josistack.internal.acse.asn1.AuthenticationValue; +import org.openmuc.josistack.internal.acse.asn1.MechanismName; +import org.openmuc.josistack.internal.acse.asn1.Myexternal; +import org.openmuc.josistack.internal.presentation.asn1.CPAPPDU; +import org.openmuc.josistack.internal.presentation.asn1.CPType; +import org.openmuc.josistack.internal.presentation.asn1.CalledPresentationSelector; +import org.openmuc.josistack.internal.presentation.asn1.CallingPresentationSelector; +import org.openmuc.josistack.internal.presentation.asn1.FullyEncodedData; +import org.openmuc.josistack.internal.presentation.asn1.ModeSelector; +import org.openmuc.josistack.internal.presentation.asn1.PDVList; +import org.openmuc.josistack.internal.presentation.asn1.PresentationContextDefinitionList; +import org.openmuc.josistack.internal.presentation.asn1.PresentationContextDefinitionResultList; +import org.openmuc.josistack.internal.presentation.asn1.PresentationContextIdentifier; +import org.openmuc.josistack.internal.presentation.asn1.RespondingPresentationSelector; +import org.openmuc.josistack.internal.presentation.asn1.UserData; +import org.openmuc.jositransport.ClientTSap; +import org.openmuc.jositransport.TConnection; + +public final class AcseAssociation { + + // private static final Logger logger = LoggerFactory.getLogger(AcseAssociation.class); + + private boolean connected = false; + private TConnection tConnection; + private ByteBuffer associateResponseAPDU = null; + private final RespondingPresentationSelector pSelLocalBerOctetString; + + private static final PresentationContextDefinitionList context_list = new PresentationContextDefinitionList( + new byte[] { (byte) 0x23, (byte) 0x30, (byte) 0x0f, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x06, + (byte) 0x04, (byte) 0x52, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x30, (byte) 0x04, + (byte) 0x06, (byte) 0x02, (byte) 0x51, (byte) 0x01, (byte) 0x30, (byte) 0x10, (byte) 0x02, + (byte) 0x01, (byte) 0x03, (byte) 0x06, (byte) 0x05, (byte) 0x28, (byte) 0xca, (byte) 0x22, + (byte) 0x02, (byte) 0x01, (byte) 0x30, (byte) 0x04, (byte) 0x06, (byte) 0x02, (byte) 0x51, + (byte) 0x01 }); + + private static final PresentationContextIdentifier acsePresentationContextId = new PresentationContextIdentifier( + new byte[] { (byte) 0x01, (byte) 0x01 }); + private static final ModeSelector normalModeSelector = new ModeSelector(); + + private static final PresentationContextDefinitionResultList presentationResultList = new PresentationContextDefinitionResultList( + new byte[] { (byte) 0x12, (byte) 0x30, (byte) 0x07, (byte) 0x80, (byte) 0x01, (byte) 0x00, (byte) 0x81, + (byte) 0x02, (byte) 0x51, (byte) 0x01, (byte) 0x30, (byte) 0x07, (byte) 0x80, (byte) 0x01, + (byte) 0x00, (byte) 0x81, (byte) 0x02, (byte) 0x51, (byte) 0x01 }); + + private static final AssociateResult aareAccepted = new AssociateResult(new byte[] { (byte) 0x01, (byte) 0x00 }); + + private static final AssociateSourceDiagnostic associateSourceDiagnostic = new AssociateSourceDiagnostic( + new byte[] { (byte) 0xa1, (byte) 0x03, (byte) 0x02, (byte) 0x01, (byte) 0x00 }); + + // is always equal to 1.0.9506.2.3 (MMS) + private static final BerObjectIdentifier application_context_name = new BerObjectIdentifier( + new byte[] { (byte) 0x05, (byte) 0x28, (byte) 0xca, (byte) 0x22, (byte) 0x02, (byte) 0x03 }); + + private static final BerObjectIdentifier directReference = new BerObjectIdentifier( + new byte[] { (byte) 0x02, (byte) 0x51, (byte) 0x01 }); + private static final BerInteger indirectReference = new BerInteger(new byte[] { (byte) 0x01, (byte) 0x03 }); + + private static final MechanismName default_mechanism_name = new MechanismName( + new byte[] { 0x03, 0x52, 0x03, 0x01 }); + + static { + normalModeSelector.setModeValue(new BerInteger(BigInteger.ONE)); + } + + AcseAssociation(TConnection tConnection, byte[] pSelLocal) { + this.tConnection = tConnection; + pSelLocalBerOctetString = new RespondingPresentationSelector(pSelLocal); + } + + /** + * A server that got an Association Request Indication may use this function to accept the association. + * + * @param payload + * the payload to send with the accept message + * @throws IOException + * if an error occures accepting the association + */ + public void accept(ByteBuffer payload) throws IOException { + + BerAny anyPayload = new BerAny(Arrays.copyOfRange(payload.array(), payload.position(), payload.limit())); + + Myexternal.Encoding encoding = new Myexternal.Encoding(); + encoding.setSingleASN1Type(anyPayload); + + Myexternal myExternal = new Myexternal(); + myExternal.setDirectReference(directReference); + myExternal.setIndirectReference(indirectReference); + myExternal.setEncoding(encoding); + + AssociationInformation userInformation = new AssociationInformation(); + List<Myexternal> externalList = userInformation.getMyexternal(); + externalList.add(myExternal); + + AAREApdu aare = new AAREApdu(); + aare.setApplicationContextName(application_context_name); + aare.setResult(aareAccepted); + aare.setResultSourceDiagnostic(associateSourceDiagnostic); + aare.setUserInformation(userInformation); + + ACSEApdu acse = new ACSEApdu(); + acse.setAare(aare); + + ReverseByteArrayOutputStream reverseOStream = new ReverseByteArrayOutputStream(100, true); + acse.encode(reverseOStream); + + UserData userData = getPresentationUserDataField(reverseOStream.getArray()); + CPAPPDU.NormalModeParameters normalModeParameters = new CPAPPDU.NormalModeParameters(); + normalModeParameters.setRespondingPresentationSelector(pSelLocalBerOctetString); + normalModeParameters.setPresentationContextDefinitionResultList(presentationResultList); + normalModeParameters.setUserData(userData); + + CPAPPDU cpaPPdu = new CPAPPDU(); + cpaPPdu.setModeSelector(normalModeSelector); + cpaPPdu.setNormalModeParameters(normalModeParameters); + + reverseOStream.reset(); + cpaPPdu.encode(reverseOStream, true); + + List<byte[]> ssduList = new LinkedList<>(); + List<Integer> ssduOffsets = new LinkedList<>(); + List<Integer> ssduLengths = new LinkedList<>(); + + ssduList.add(reverseOStream.buffer); + ssduOffsets.add(reverseOStream.index + 1); + ssduLengths.add(reverseOStream.buffer.length - (reverseOStream.index + 1)); + + writeSessionAccept(ssduList, ssduOffsets, ssduLengths); + + connected = true; + } + + private void writeSessionAccept(List<byte[]> ssdu, List<Integer> ssduOffsets, List<Integer> ssduLengths) + throws IOException { + byte[] sduAcceptHeader = new byte[20]; + int idx = 0; + + int ssduLength = 0; + for (int ssduElementLength : ssduLengths) { + ssduLength += ssduElementLength; + } + + // write ISO 8327-1 Header + // SPDU Type: ACCEPT (14) + sduAcceptHeader[idx++] = 0x0e; + // Length: length of session user data + 22 ( header data after length + // field ) + sduAcceptHeader[idx++] = (byte) ((ssduLength + 18) & 0xff); + + // -- start Connect Accept Item + // Parameter type: Connect Accept Item (5) + sduAcceptHeader[idx++] = 0x05; + // Parameter length + sduAcceptHeader[idx++] = 0x06; + + // Protocol options: + // Parameter Type: Protocol Options (19) + sduAcceptHeader[idx++] = 0x13; + // Parameter length + sduAcceptHeader[idx++] = 0x01; + // flags: (.... ...0 = Able to receive extended concatenated SPDU: + // False) + sduAcceptHeader[idx++] = 0x00; + + // Version number: + // Parameter type: Version Number (22) + sduAcceptHeader[idx++] = 0x16; + // Parameter length + sduAcceptHeader[idx++] = 0x01; + // flags: (.... ..1. = Protocol Version 2: True) + sduAcceptHeader[idx++] = 0x02; + // -- end Connect Accept Item + + // Session Requirement + // Parameter type: Session Requirement (20) + sduAcceptHeader[idx++] = 0x14; + // Parameter length + sduAcceptHeader[idx++] = 0x02; + // flags: (.... .... .... ..1. = Duplex functional unit: True) + sduAcceptHeader[idx++] = 0x00; + sduAcceptHeader[idx++] = 0x02; + + // Called Session Selector + // Parameter type: Called Session Selector (52) + sduAcceptHeader[idx++] = 0x34; + // Parameter length + sduAcceptHeader[idx++] = 0x02; + // Called Session Selector + sduAcceptHeader[idx++] = 0x00; + sduAcceptHeader[idx++] = 0x01; + + // Session user data + // Parameter type: Session user data (193) + sduAcceptHeader[idx++] = (byte) 0xc1; + + // Parameter length + sduAcceptHeader[idx++] = (byte) ssduLength; + + ssdu.add(0, sduAcceptHeader); + ssduOffsets.add(0, 0); + ssduLengths.add(0, sduAcceptHeader.length); + + tConnection.send(ssdu, ssduOffsets, ssduLengths); + } + + public ByteBuffer getAssociateResponseAPdu() { + ByteBuffer returnBuffer = associateResponseAPDU; + associateResponseAPDU = null; + return returnBuffer; + } + + /** + * Starts an Application Association by sending an association request and waiting for an association accept message + * + * @param payload + * payload that can be sent with the association request + * @param port + * @param address + * @param tSAP + * @param aeQualifierCalling + * @param aeQualifierCalled + * @param apTitleCalling + * @param apTitleCalled + * @throws IOException + */ + void startAssociation(ByteBuffer payload, InetAddress address, int port, InetAddress localAddr, int localPort, + String authenticationParameter, byte[] sSelRemote, byte[] sSelLocal, byte[] pSelRemote, ClientTSap tSAP, + int[] apTitleCalled, int[] apTitleCalling, int aeQualifierCalled, int aeQualifierCalling) + throws IOException { + if (connected == true) { + throw new IOException(); + } + + APTitle called_ap_title = new APTitle(); + called_ap_title.setApTitleForm2(new APTitleForm2(apTitleCalled)); + APTitle calling_ap_title = new APTitle(); + calling_ap_title.setApTitleForm2(new APTitleForm2(apTitleCalling)); + + AEQualifier called_ae_qualifier = new AEQualifier(); + called_ae_qualifier.setAeQualifierForm2(new AEQualifierForm2(aeQualifierCalled)); + AEQualifier calling_ae_qualifier = new AEQualifier(); + calling_ae_qualifier.setAeQualifierForm2(new AEQualifierForm2(aeQualifierCalling)); + + Myexternal.Encoding encoding = new Myexternal.Encoding(); + encoding.setSingleASN1Type( + new BerAny(Arrays.copyOfRange(payload.array(), payload.position(), payload.limit()))); + + Myexternal myExternal = new Myexternal(); + myExternal.setDirectReference(directReference); + myExternal.setIndirectReference(indirectReference); + myExternal.setEncoding(encoding); + + AssociationInformation userInformation = new AssociationInformation(); + List<Myexternal> externalList = userInformation.getMyexternal(); + externalList.add(myExternal); + + ACSERequirements sender_acse_requirements = null; + MechanismName mechanism_name = null; + AuthenticationValue authentication_value = null; + if (authenticationParameter != null) { + sender_acse_requirements = new ACSERequirements(new byte[] { (byte) 0x02, (byte) 0x07, (byte) 0x80 }); + mechanism_name = default_mechanism_name; + authentication_value = new AuthenticationValue(); + authentication_value.setCharstring(new BerGraphicString(authenticationParameter.getBytes())); + } + + AARQApdu aarq = new AARQApdu(); + aarq.setApplicationContextName(application_context_name); + aarq.setCalledAPTitle(called_ap_title); + aarq.setCalledAEQualifier(called_ae_qualifier); + aarq.setCallingAPTitle(calling_ap_title); + aarq.setCallingAEQualifier(calling_ae_qualifier); + aarq.setSenderAcseRequirements(sender_acse_requirements); + aarq.setMechanismName(mechanism_name); + aarq.setCallingAuthenticationValue(authentication_value); + aarq.setUserInformation(userInformation); + + ACSEApdu acse = new ACSEApdu(); + acse.setAarq(aarq); + + ReverseByteArrayOutputStream reverseOStream = new ReverseByteArrayOutputStream(200, true); + acse.encode(reverseOStream); + + UserData userData = getPresentationUserDataField(reverseOStream.getArray()); + + CPType.NormalModeParameters normalModeParameter = new CPType.NormalModeParameters(); + normalModeParameter + .setCallingPresentationSelector(new CallingPresentationSelector(pSelLocalBerOctetString.value)); + normalModeParameter.setCalledPresentationSelector(new CalledPresentationSelector(pSelRemote)); + normalModeParameter.setPresentationContextDefinitionList(context_list); + normalModeParameter.setUserData(userData); + + CPType cpType = new CPType(); + cpType.setModeSelector(normalModeSelector); + cpType.setNormalModeParameters(normalModeParameter); + + reverseOStream.reset(); + cpType.encode(reverseOStream, true); + + List<byte[]> ssduList = new LinkedList<>(); + List<Integer> ssduOffsets = new LinkedList<>(); + List<Integer> ssduLengths = new LinkedList<>(); + + ssduList.add(reverseOStream.buffer); + ssduOffsets.add(reverseOStream.index + 1); + ssduLengths.add(reverseOStream.buffer.length - (reverseOStream.index + 1)); + + ByteBuffer res = null; + res = startSConnection(ssduList, ssduOffsets, ssduLengths, address, port, localAddr, localPort, tSAP, + sSelRemote, sSelLocal); + + associateResponseAPDU = decodePConResponse(res); + + } + + private static ByteBuffer decodePConResponse(ByteBuffer ppdu) throws IOException { + + CPAPPDU cpa_ppdu = new CPAPPDU(); + InputStream iStream = new ByteBufferInputStream(ppdu); + cpa_ppdu.decode(iStream); + + iStream = new ByteArrayInputStream(cpa_ppdu.getNormalModeParameters() + .getUserData() + .getFullyEncodedData() + .getPDVList() + .get(0) + .getPresentationDataValues() + .getSingleASN1Type().value); + + ACSEApdu acseApdu = new ACSEApdu(); + acseApdu.decode(iStream, null); + return ByteBuffer.wrap( + acseApdu.getAare().getUserInformation().getMyexternal().get(0).getEncoding().getSingleASN1Type().value); + + } + + private static UserData getPresentationUserDataField(byte[] userDataBytes) { + PDVList.PresentationDataValues presDataValues = new PDVList.PresentationDataValues(); + presDataValues.setSingleASN1Type(new BerAny(userDataBytes)); + PDVList pdvList = new PDVList(); + pdvList.setPresentationContextIdentifier(acsePresentationContextId); + pdvList.setPresentationDataValues(presDataValues); + + FullyEncodedData fullyEncodedData = new FullyEncodedData(); + List<PDVList> pdvListList = fullyEncodedData.getPDVList(); + pdvListList.add(pdvList); + + UserData userData = new UserData(); + userData.setFullyEncodedData(fullyEncodedData); + return userData; + } + + /** + * Starts a session layer connection, sends a CONNECT (CN), waits for a ACCEPT (AC) and throws an IOException if not + * successful + * + * @throws IOException + */ + private ByteBuffer startSConnection(List<byte[]> ssduList, List<Integer> ssduOffsets, List<Integer> ssduLengths, + InetAddress address, int port, InetAddress localAddr, int localPort, ClientTSap tSAP, byte[] sSelRemote, + byte[] sSelLocal) throws IOException { + if (connected == true) { + throw new IOException(); + } + + byte[] spduHeader = new byte[24]; + int idx = 0; + // byte[] res = null; + + int ssduLength = 0; + for (int ssduElementLength : ssduLengths) { + ssduLength += ssduElementLength; + } + + // write ISO 8327-1 Header + // SPDU Type: CONNECT (13) + spduHeader[idx++] = 0x0d; + // Length: length of session user data + 22 ( header data after + // length field ) + spduHeader[idx++] = (byte) ((ssduLength + 22) & 0xff); + + // -- start Connect Accept Item + // Parameter type: Connect Accept Item (5) + spduHeader[idx++] = 0x05; + // Parameter length + spduHeader[idx++] = 0x06; + + // Protocol options: + // Parameter Type: Protocol Options (19) + spduHeader[idx++] = 0x13; + // Parameter length + spduHeader[idx++] = 0x01; + // flags: (.... ...0 = Able to receive extended concatenated SPDU: + // False) + spduHeader[idx++] = 0x00; + + // Version number: + // Parameter type: Version Number (22) + spduHeader[idx++] = 0x16; + // Parameter length + spduHeader[idx++] = 0x01; + // flags: (.... ..1. = Protocol Version 2: True) + spduHeader[idx++] = 0x02; + // -- end Connect Accept Item + + // Session Requirement + // Parameter type: Session Requirement (20) + spduHeader[idx++] = 0x14; + // Parameter length + spduHeader[idx++] = 0x02; + // flags: (.... .... .... ..1. = Duplex functional unit: True) + spduHeader[idx++] = 0x00; + spduHeader[idx++] = 0x02; + + // Calling Session Selector + // Parameter type: Calling Session Selector (51) + spduHeader[idx++] = 0x33; + // Parameter length + spduHeader[idx++] = 0x02; + // Calling Session Selector + spduHeader[idx++] = sSelRemote[0]; + spduHeader[idx++] = sSelRemote[1]; + + // Called Session Selector + // Parameter type: Called Session Selector (52) + spduHeader[idx++] = 0x34; + // Parameter length + spduHeader[idx++] = 0x02; + // Called Session Selector + spduHeader[idx++] = sSelLocal[0]; + spduHeader[idx++] = sSelLocal[1]; + + // Session user data + // Parameter type: Session user data (193) + spduHeader[idx++] = (byte) 0xc1; + // Parameter length + spduHeader[idx++] = (byte) (ssduLength & 0xff); + // write session user data + + ssduList.add(0, spduHeader); + ssduOffsets.add(0, 0); + ssduLengths.add(0, spduHeader.length); + + tConnection = tSAP.connectTo(address, port, localAddr, localPort); + + tConnection.send(ssduList, ssduOffsets, ssduLengths); + + // TODO how much should be allocated here? + ByteBuffer pduBuffer = ByteBuffer.allocate(500); + + try { + tConnection.receive(pduBuffer); + } catch (TimeoutException e) { + throw new IOException("ResponseTimeout waiting for connection response.", e); + } + idx = 0; + + // read ISO 8327-1 Header + // SPDU Type: ACCEPT (14) + byte spduType = pduBuffer.get(); + if (spduType != 0x0e) { + throw new IOException("ISO 8327-1 header wrong SPDU type, expected ACCEPT (14), got " + + getSPDUTypeString(spduType) + " (" + spduType + ")"); + } + pduBuffer.get(); // skip length byte + + parameter_loop: while (true) { + // read parameter type + int parameterType = pduBuffer.get() & 0xff; + // read parameter length + int parameterLength = pduBuffer.get() & 0xff; + + switch (parameterType) { + // Connect Accept Item (5) + case 0x05: + int bytesToRead = parameterLength; + while (bytesToRead > 0) { + // read parameter type + int ca_parameterType = pduBuffer.get(); + // read parameter length + // int ca_parameterLength = res[idx++]; + pduBuffer.get(); + + bytesToRead -= 2; + + switch (ca_parameterType & 0xff) { + // Protocol Options (19) + case 0x13: + // flags: .... ...0 = Able to receive extended + // concatenated SPDU: False + byte protocolOptions = pduBuffer.get(); + if (protocolOptions != 0x00) { + throw new IOException( + "SPDU Connect Accept Item/Protocol Options is " + protocolOptions + ", expected 0"); + } + + bytesToRead--; + break; + // Version Number + case 0x16: + // flags .... ..1. = Protocol Version 2: True + byte versionNumber = pduBuffer.get(); + if (versionNumber != 0x02) { + throw new IOException( + "SPDU Connect Accept Item/Version Number is " + versionNumber + ", expected 2"); + } + + bytesToRead--; + break; + default: + throw new IOException( + "SPDU Connect Accept Item: parameter not implemented: " + ca_parameterType); + } + } + break; + // Session Requirement (20) + case 0x14: + // flags: (.... .... .... ..1. = Duplex functional unit: True) + long sessionRequirement = extractInteger(pduBuffer, parameterLength); + if (sessionRequirement != 0x02) { + throw new IOException("SPDU header parameter 'Session Requirement (20)' is " + sessionRequirement + + ", expected 2"); + + } + break; + // Calling Session Selector (51) + case 0x33: + long css = extractInteger(pduBuffer, parameterLength); + if (css != 0x01) { + throw new IOException( + "SPDU header parameter 'Calling Session Selector (51)' is " + css + ", expected 1"); + + } + break; + // Called Session Selector (52) + case 0x34: + long calledSessionSelector = extractInteger(pduBuffer, parameterLength); + if (calledSessionSelector != 0x01) { + throw new IOException("SPDU header parameter 'Called Session Selector (52)' is " + + calledSessionSelector + ", expected 1"); + } + break; + // Session user data (193) + case 0xc1: + break parameter_loop; + default: + throw new IOException("SPDU header parameter type " + parameterType + " not implemented"); + } + } + + // got correct ACCEPT (AC) from the server + + connected = true; + + return pduBuffer; + } + + public void send(ByteBuffer payload) throws IOException { + + List<byte[]> ssduList = new ArrayList<>(); + List<Integer> ssduOffsets = new LinkedList<>(); + List<Integer> ssduLengths = new LinkedList<>(); + + encodePresentationLayer(payload, ssduList, ssduOffsets, ssduLengths); + + encodeSessionLayer(ssduList, ssduOffsets, ssduLengths); + + tConnection.send(ssduList, ssduOffsets, ssduLengths); + } + + private void encodePresentationLayer(ByteBuffer payload, List<byte[]> ssduList, List<Integer> ssduOffsets, + List<Integer> ssduLengths) throws IOException { + PDVList pdv_list = new PDVList(); + pdv_list.setPresentationContextIdentifier(new PresentationContextIdentifier(3l)); + + PDVList.PresentationDataValues presentationDataValues = new PDVList.PresentationDataValues(); + presentationDataValues.setSingleASN1Type( + new BerAny(Arrays.copyOfRange(payload.array(), payload.position(), payload.limit()))); + pdv_list.setPresentationDataValues(presentationDataValues); + + FullyEncodedData fully_encoded_data = new FullyEncodedData(); + List<PDVList> pdv_list_list = fully_encoded_data.getPDVList(); + pdv_list_list.add(pdv_list); + + UserData user_data = new UserData(); + user_data.setFullyEncodedData(fully_encoded_data); + + ReverseByteArrayOutputStream reverseOStream = new ReverseByteArrayOutputStream(200, true); + user_data.encode(reverseOStream); + + ssduList.add(reverseOStream.buffer); + ssduOffsets.add(reverseOStream.index + 1); + ssduLengths.add(reverseOStream.buffer.length - (reverseOStream.index + 1)); + } + + private void encodeSessionLayer(List<byte[]> ssduList, List<Integer> ssduOffsets, List<Integer> ssduLengths) + throws IOException { + + byte[] spduHeader = new byte[4]; + // --write iso 8327-1 Header-- + // write SPDU Type: give tokens PDU + spduHeader[0] = 0x01; + // length 0 + spduHeader[1] = 0; + // write SPDU Type: DATA TRANSFER (DT) + spduHeader[2] = 0x01; + // length 0 + spduHeader[3] = 0; + + ssduList.add(0, spduHeader); + ssduOffsets.add(0, 0); + ssduLengths.add(0, spduHeader.length); + + } + + /** + * Listens for a new PDU and writes it into the given buffer. Decodes all ACSE and lower layer headers. The + * resulting buffer's position points to the beginning of the ACSE SDU. The limit will point to the byte after the + * last byte of the ACSE SDU. + * + * @param pduBuffer + * buffer to write the received pdu into + * @throws DecodingException + * if a decoding error occurs + * @throws IOException + * if a non recoverable error occurs. Afterwards the association should be closed by the user + * @throws TimeoutException + * if a timeout occurs + */ + public byte[] receive(ByteBuffer pduBuffer) throws DecodingException, IOException, TimeoutException { + if (connected == false) { + throw new IllegalStateException("ACSE Association not connected"); + } + tConnection.receive(pduBuffer); + + decodeSessionLayer(pduBuffer); + + return decodePresentationLayer(pduBuffer); + } + + private byte[] decodePresentationLayer(ByteBuffer pduBuffer) throws DecodingException { + // decode PPDU header + UserData user_data = new UserData(); + + try { + user_data.decode(new ByteBufferInputStream(pduBuffer), null); + } catch (IOException e) { + throw new DecodingException("error decoding PPDU header", e); + } + + return user_data.getFullyEncodedData() + .getPDVList() + .get(0) + .getPresentationDataValues() + .getSingleASN1Type().value; + } + + private void decodeSessionLayer(ByteBuffer pduBuffer) throws EOFException, DecodingException { + int firstByte = pduBuffer.get(); + + if (firstByte == 25) { + // got an ABORT SPDU + throw new EOFException("Received an ABORT SPDU"); + } + + // -- read ISO 8327-1 header + // SPDU type: Give tokens PDU (1) + if (firstByte != 0x01) { + throw new DecodingException("SPDU header syntax errror: first SPDU type not 1"); + } + // length + if (pduBuffer.get() != 0) { + throw new DecodingException("SPDU header syntax errror: first SPDU type length not 0"); + } + // SPDU Type: DATA TRANSFER (DT) SPDU (1) + if (pduBuffer.get() != 0x01) { + throw new DecodingException("SPDU header syntax errror: second SPDU type not 1"); + } + // length + if (pduBuffer.get() != 0) { + throw new DecodingException("SPDU header syntax errror: second SPDU type length not 0"); + } + } + + /** + * Disconnects by sending a disconnect request at the Transport Layer and then closing the socket. + */ + public void disconnect() { + connected = false; + if (tConnection != null) { + tConnection.disconnect(); + } + } + + /** + * Closes the connection simply by closing the socket. + */ + public void close() { + connected = false; + if (tConnection != null) { + tConnection.close(); + } + } + + private long extractInteger(ByteBuffer buffer, int size) throws IOException { + switch (size) { + case 1: + return buffer.get(); + case 2: + return buffer.getShort(); + case 4: + return buffer.getInt(); + case 8: + return buffer.getLong(); + default: + throw new IOException("invalid length for reading numeric value"); + } + } + + ByteBuffer listenForCn(ByteBuffer pduBuffer) throws IOException, TimeoutException { + if (connected == true) { + throw new IllegalStateException("ACSE Association is already connected"); + } + int parameter; + int parameterLength; + + tConnection.receive(pduBuffer); + // start reading ISO 8327-1 header + // SPDU Type: CONNECT (CN) SPDU (13) + byte spduType = pduBuffer.get(); + if (spduType != 0x0d) { + throw new IOException("ISO 8327-1 header wrong SPDU type, expected CONNECT (13), got " + + getSPDUTypeString(spduType) + " (" + spduType + ")"); + } + pduBuffer.get(); // skip lenght byte + + parameter_loop: while (true) { + // read parameter code + parameter = pduBuffer.get() & 0xff; + // read parameter length + parameterLength = pduBuffer.get() & 0xff; + switch (parameter) { + // Connect Accept Item (5) + case 0x05: + int bytesToRead = parameterLength; + while (bytesToRead > 0) { + // read parameter type + int ca_parameterType = pduBuffer.get(); + // read parameter length + pduBuffer.get(); + + bytesToRead -= 2; + + switch (ca_parameterType & 0xff) { + // Protocol Options (19) + case 0x13: + // flags: .... ...0 = Able to receive extended + // concatenated SPDU: False + byte protocolOptions = pduBuffer.get(); + if (protocolOptions != 0x00) { + throw new IOException( + "SPDU Connect Accept Item/Protocol Options is " + protocolOptions + ", expected 0"); + } + + bytesToRead--; + break; + // Version Number + case 0x16: + // flags .... ..1. = Protocol Version 2: True + byte versionNumber = pduBuffer.get(); + if (versionNumber != 0x02) { + throw new IOException( + "SPDU Connect Accept Item/Version Number is " + versionNumber + ", expected 2"); + } + + bytesToRead--; + break; + default: + throw new IOException( + "SPDU Connect Accept Item: parameter not implemented: " + ca_parameterType); + } + } + break; + // Session Requirement (20) + case 0x14: + // flags: (.... .... .... ..1. = Duplex functional unit: True) + long sessionRequirement = extractInteger(pduBuffer, parameterLength); + if (sessionRequirement != 0x02) { + throw new IOException("SPDU header parameter 'Session Requirement (20)' is " + sessionRequirement + + ", expected 2"); + } + break; + // Calling Session Selector (51) + case 0x33: + extractInteger(pduBuffer, parameterLength); + break; + // Called Session Selector (52) + case 0x34: + long calledSessionSelector = extractInteger(pduBuffer, parameterLength); + if (calledSessionSelector != 0x01) { + throw new IOException("SPDU header parameter 'Called Session Selector (52)' is " + + calledSessionSelector + ", expected 1"); + } + break; + // Session user data (193) + case 0xc1: + break parameter_loop; + default: + throw new IOException("SPDU header parameter type " + parameter + " not implemented"); + } + + } + + CPType cpType = new CPType(); + InputStream iStream = new ByteBufferInputStream(pduBuffer); + cpType.decode(iStream, true); + + iStream = new ByteArrayInputStream(cpType.getNormalModeParameters() + .getUserData() + .getFullyEncodedData() + .getPDVList() + .get(0) + .getPresentationDataValues() + .getSingleASN1Type().value); + + ACSEApdu acseApdu = new ACSEApdu(); + acseApdu.decode(iStream, null); + return ByteBuffer.wrap( + acseApdu.getAarq().getUserInformation().getMyexternal().get(0).getEncoding().getSingleASN1Type().value); + + } + + public int getMessageTimeout() { + return tConnection.getMessageTimeout(); + } + + public void setMessageTimeout(int i) { + tConnection.setMessageTimeout(i); + } + + public static String getSPDUTypeString(byte spduType) { + switch (spduType) { + case 0: + return "EXCEPTION REPORT (ER)"; + case 1: + return "DATA TRANSFER (DT)"; + case 2: + return "PLEASE TOKENS (PT)"; + case 5: + return "EXPEDITED (EX)"; + case 7: + return "PREPARE (PR)"; + case 8: + return "NOT FINISHED (NF)"; + case 9: + return "FINISH (FN)"; + case 10: + return "DISCONNECT (DN)"; + case 12: + return "REFUSE (RF)"; + case 13: + return "CONNECT (CN)"; + case 14: + return "ACCEPT (AC)"; + case 15: + return "CONNECT DATA OVERFLOW (CDO)"; + case 16: + return "OVERFLOW ACCEPT (OA)"; + case 21: + return "GIVE TOKENS CONFIRM (GTC)"; + case 22: + return "GIVE TOKENS ACK (GTA)"; + case 25: + return "ABORT (AB)"; + case 26: + return "ABORT ACCEPT (AA)"; + case 29: + return "ACTIVITY RESUME (AR)"; + case 33: + return "TYPED DATA (TD)"; + case 34: + return "RESYNCHRONIZE ACK (RA)"; + case 41: + return "MAJOR SYNC POINT (MAP)"; + case 42: + return "MAJOR SYNC ACK (MAA)"; + case 45: + return "ACTIVITY START (AS)"; + case 48: + return "EXCEPTION DATA (ED)"; + case 49: + return "MINOR SYNC POINT (MIP)"; + case 50: + return "MINOR SYNC ACK (MIA)"; + case 53: + return "RESYNCHRONIZE (RS)"; + case 57: + return "ACTIVITY DISCARD (AD)"; + case 58: + return "ACTIVITY DISCARD ACK (ADA)"; + case 61: + return "CAPABILITY DATA (CD)"; + case 62: + return "CAPABILITY DATA ACK (CDA)"; + case 64: + return "UNIT DATA (UD)"; + default: + return "<unknown SPDU type>"; + } + } +} -- Gitblit v1.9.1