
com.beanit.josistack.AcseAssociation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openiec61850 Show documentation
Show all versions of openiec61850 Show documentation
OpenIEC61850 is a Java library implementing the IEC 61850 MMS communication standard for clients and servers.
The newest version!
/*
* Copyright 2011 The OpenIEC61850 Authors
*
* 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 com.beanit.josistack;
import com.beanit.jasn1.ber.ReverseByteArrayOutputStream;
import com.beanit.jasn1.ber.types.BerAny;
import com.beanit.jasn1.ber.types.BerInteger;
import com.beanit.jasn1.ber.types.BerObjectIdentifier;
import com.beanit.jasn1.ber.types.string.BerGraphicString;
import com.beanit.josistack.internal.acse.asn1.AAREApdu;
import com.beanit.josistack.internal.acse.asn1.AARQApdu;
import com.beanit.josistack.internal.acse.asn1.ACSEApdu;
import com.beanit.josistack.internal.acse.asn1.ACSERequirements;
import com.beanit.josistack.internal.acse.asn1.AEQualifier;
import com.beanit.josistack.internal.acse.asn1.AEQualifierForm2;
import com.beanit.josistack.internal.acse.asn1.APTitle;
import com.beanit.josistack.internal.acse.asn1.APTitleForm2;
import com.beanit.josistack.internal.acse.asn1.AssociateResult;
import com.beanit.josistack.internal.acse.asn1.AssociateSourceDiagnostic;
import com.beanit.josistack.internal.acse.asn1.AssociationInformation;
import com.beanit.josistack.internal.acse.asn1.AuthenticationValue;
import com.beanit.josistack.internal.acse.asn1.MechanismName;
import com.beanit.josistack.internal.acse.asn1.Myexternal;
import com.beanit.josistack.internal.presentation.asn1.CPAPPDU;
import com.beanit.josistack.internal.presentation.asn1.CPType;
import com.beanit.josistack.internal.presentation.asn1.CalledPresentationSelector;
import com.beanit.josistack.internal.presentation.asn1.CallingPresentationSelector;
import com.beanit.josistack.internal.presentation.asn1.FullyEncodedData;
import com.beanit.josistack.internal.presentation.asn1.ModeSelector;
import com.beanit.josistack.internal.presentation.asn1.PDVList;
import com.beanit.josistack.internal.presentation.asn1.PresentationContextDefinitionList;
import com.beanit.josistack.internal.presentation.asn1.PresentationContextDefinitionResultList;
import com.beanit.josistack.internal.presentation.asn1.PresentationContextIdentifier;
import com.beanit.josistack.internal.presentation.asn1.RespondingPresentationSelector;
import com.beanit.josistack.internal.presentation.asn1.UserData;
import com.beanit.jositransport.ClientTSap;
import com.beanit.jositransport.TConnection;
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;
public final class AcseAssociation {
// private static final Logger logger = LoggerFactory.getLogger(AcseAssociation.class);
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));
}
private final RespondingPresentationSelector pSelLocalBerOctetString;
private boolean connected = false;
private TConnection tConnection;
private ByteBuffer associateResponseAPDU = null;
AcseAssociation(TConnection tConnection, byte[] pSelLocal) {
this.tConnection = tConnection;
pSelLocalBerOctetString = new RespondingPresentationSelector(pSelLocal);
}
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 pdvListList = fullyEncodedData.getPDVList();
pdvListList.add(pdvList);
UserData userData = new UserData();
userData.setFullyEncodedData(fullyEncodedData);
return userData;
}
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 "";
}
}
/**
* 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 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 ssduList = new LinkedList<>();
List ssduOffsets = new LinkedList<>();
List 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 ssdu, List ssduOffsets, List 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 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 ssduList = new LinkedList<>();
List ssduOffsets = new LinkedList<>();
List 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);
}
/**
* 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 ssduList,
List ssduOffsets,
List 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 ssduList = new ArrayList<>();
List ssduOffsets = new LinkedList<>();
List ssduLengths = new LinkedList<>();
encodePresentationLayer(payload, ssduList, ssduOffsets, ssduLengths);
encodeSessionLayer(ssduList, ssduOffsets, ssduLengths);
tConnection.send(ssduList, ssduOffsets, ssduLengths);
}
private void encodePresentationLayer(
ByteBuffer payload,
List ssduList,
List ssduOffsets,
List 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 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 ssduList, List ssduOffsets, List 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
* @return the received PDU
* @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);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy