use of org.apache.plc4x.java.spi.ConversationContext in project plc4x by apache.
the class SecureChannel method onDiscoverGetEndpointsRequest.
public void onDiscoverGetEndpointsRequest(ConversationContext<OpcuaAPU> context, OpcuaOpenResponse opcuaOpenResponse, OpenSecureChannelResponse openSecureChannelResponse) throws PlcConnectionException {
tokenId.set((int) ((ChannelSecurityToken) openSecureChannelResponse.getSecurityToken()).getTokenId());
channelId.set((int) ((ChannelSecurityToken) openSecureChannelResponse.getSecurityToken()).getChannelId());
int transactionId = channelTransactionManager.getTransactionIdentifier();
int nextSequenceNumber = opcuaOpenResponse.getSequenceNumber() + 1;
int nextRequestId = opcuaOpenResponse.getRequestId() + 1;
if (!(transactionId == nextSequenceNumber)) {
LOGGER.error("Sequence number isn't as expected, we might have missed a packet. - " + transactionId + " != " + nextSequenceNumber);
throw new PlcConnectionException("Sequence number isn't as expected, we might have missed a packet. - " + transactionId + " != " + nextSequenceNumber);
}
RequestHeader requestHeader = new RequestHeader(new NodeId(authenticationToken), getCurrentDateTime(), 0L, 0L, NULL_STRING, REQUEST_TIMEOUT_LONG, NULL_EXTENSION_OBJECT);
GetEndpointsRequest endpointsRequest = new GetEndpointsRequest(requestHeader, this.endpoint, 0, null, 0, null);
ExpandedNodeId expandedNodeId = new // Namespace Uri Specified
ExpandedNodeId(// Namespace Uri Specified
false, // Server Index Specified
false, new NodeIdFourByte((short) 0, Integer.parseInt(endpointsRequest.getIdentifier())), null, null);
try {
WriteBufferByteBased buffer = new WriteBufferByteBased(endpointsRequest.getLengthInBytes(), org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN);
new ExtensionObject(expandedNodeId, null, endpointsRequest, false).serialize(buffer);
OpcuaMessageRequest messageRequest = new OpcuaMessageRequest(FINAL_CHUNK, channelId.get(), tokenId.get(), nextSequenceNumber, nextRequestId, buffer.getData());
Consumer<Integer> requestConsumer = t -> context.sendRequest(new OpcuaAPU(messageRequest, false)).expectResponse(OpcuaAPU.class, REQUEST_TIMEOUT).check(p -> p.getMessage() instanceof OpcuaMessageResponse).unwrap(p -> (OpcuaMessageResponse) p.getMessage()).check(p -> p.getRequestId() == transactionId).handle(opcuaMessageResponse -> {
try {
ExtensionObject message = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaMessageResponse.getMessage(), org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false);
if (message.getBody() instanceof ServiceFault) {
ServiceFault fault = (ServiceFault) message.getBody();
LOGGER.error("Failed to connect to opc ua server for the following reason:- {}, {}", ((ResponseHeader) fault.getResponseHeader()).getServiceResult().getStatusCode(), OpcuaStatusCode.enumForValue(((ResponseHeader) fault.getResponseHeader()).getServiceResult().getStatusCode()));
} else {
LOGGER.debug("Got Create Session Response Connection Response");
GetEndpointsResponse response = (GetEndpointsResponse) message.getBody();
List<ExtensionObjectDefinition> endpoints = response.getEndpoints();
for (ExtensionObjectDefinition endpoint : endpoints) {
EndpointDescription endpointDescription = (EndpointDescription) endpoint;
if (endpointDescription.getEndpointUrl().getStringValue().equals(this.endpoint.getStringValue()) && endpointDescription.getSecurityPolicyUri().getStringValue().equals(this.securityPolicy)) {
LOGGER.info("Found OPC UA endpoint {}", this.endpoint.getStringValue());
this.configuration.setSenderCertificate(endpointDescription.getServerCertificate().getStringValue());
}
}
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
byte[] digest = messageDigest.digest(this.configuration.getSenderCertificate());
this.configuration.setThumbprint(new PascalByteString(digest.length, digest));
} catch (NoSuchAlgorithmException e) {
LOGGER.error("Failed to find hashing algorithm");
}
onDiscoverCloseSecureChannel(context, response);
}
} catch (ParseException e) {
e.printStackTrace();
}
});
channelTransactionManager.submit(requestConsumer, transactionId);
} catch (SerializationException e) {
LOGGER.error("Unable to to Parse Create Session Request");
}
}
use of org.apache.plc4x.java.spi.ConversationContext in project plc4x by apache.
the class SecureChannel method onConnectActivateSessionRequest.
private void onConnectActivateSessionRequest(ConversationContext<OpcuaAPU> context, CreateSessionResponse opcuaMessageResponse, CreateSessionResponse sessionResponse) throws PlcConnectionException, ParseException {
senderCertificate = sessionResponse.getServerCertificate().getStringValue();
encryptionHandler.setServerCertificate(EncryptionHandler.getCertificateX509(senderCertificate));
this.senderNonce = sessionResponse.getServerNonce().getStringValue();
String[] endpoints = new String[3];
try {
InetAddress address = InetAddress.getByName(this.configuration.getHost());
endpoints[0] = "opc.tcp://" + address.getHostAddress() + ":" + configuration.getPort() + configuration.getTransportEndpoint();
endpoints[1] = "opc.tcp://" + address.getHostName() + ":" + configuration.getPort() + configuration.getTransportEndpoint();
endpoints[2] = "opc.tcp://" + address.getCanonicalHostName() + ":" + configuration.getPort() + configuration.getTransportEndpoint();
} catch (UnknownHostException e) {
e.printStackTrace();
}
selectEndpoint(sessionResponse);
if (this.policyId == null) {
throw new PlcRuntimeException("Unable to find endpoint - " + endpoints[1]);
}
ExtensionObject userIdentityToken = getIdentityToken(this.tokenType, policyId.getStringValue());
int requestHandle = getRequestHandle();
RequestHeader requestHeader = new RequestHeader(new NodeId(authenticationToken), getCurrentDateTime(), requestHandle, 0L, NULL_STRING, REQUEST_TIMEOUT_LONG, NULL_EXTENSION_OBJECT);
SignatureData clientSignature = new SignatureData(NULL_STRING, NULL_BYTE_STRING);
ActivateSessionRequest activateSessionRequest = new ActivateSessionRequest(requestHeader, clientSignature, 0, null, 0, null, userIdentityToken, clientSignature);
ExpandedNodeId expandedNodeId = new // Namespace Uri Specified
ExpandedNodeId(// Namespace Uri Specified
false, // Server Index Specified
false, new NodeIdFourByte((short) 0, Integer.parseInt(activateSessionRequest.getIdentifier())), null, null);
ExtensionObject extObject = new ExtensionObject(expandedNodeId, null, activateSessionRequest, false);
try {
WriteBufferByteBased buffer = new WriteBufferByteBased(extObject.getLengthInBytes(), org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN);
extObject.serialize(buffer);
Consumer<byte[]> consumer = opcuaResponse -> {
try {
ExtensionObject message = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse, org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false);
if (message.getBody() instanceof ServiceFault) {
ServiceFault fault = (ServiceFault) message.getBody();
LOGGER.error("Failed to connect to opc ua server for the following reason:- {}, {}", ((ResponseHeader) fault.getResponseHeader()).getServiceResult().getStatusCode(), OpcuaStatusCode.enumForValue(((ResponseHeader) fault.getResponseHeader()).getServiceResult().getStatusCode()));
} else {
LOGGER.debug("Got Activate Session Response Connection Response");
try {
ActivateSessionResponse responseMessage;
ExtensionObjectDefinition unknownExtensionObject = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse, org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false).getBody();
if (unknownExtensionObject instanceof ActivateSessionResponse) {
responseMessage = (ActivateSessionResponse) unknownExtensionObject;
long returnedRequestHandle = ((ResponseHeader) responseMessage.getResponseHeader()).getRequestHandle();
if (!(requestHandle == returnedRequestHandle)) {
LOGGER.error("Request handle isn't as expected, we might have missed a packet. {} != {}", requestHandle, returnedRequestHandle);
}
// Send an event that connection setup is complete.
keepAlive();
context.fireConnected();
} else {
ServiceFault serviceFault = (ServiceFault) unknownExtensionObject;
ResponseHeader header = (ResponseHeader) serviceFault.getResponseHeader();
LOGGER.error("Subscription ServiceFault returned from server with error code, '{}'", header.getServiceResult().toString());
}
} catch (ParseException e) {
LOGGER.error("Unable to parse the returned Subscription response");
e.printStackTrace();
}
}
} catch (ParseException e) {
e.printStackTrace();
}
};
Consumer<TimeoutException> timeout = e -> {
LOGGER.error("Timeout while waiting for activate session response");
e.printStackTrace();
};
BiConsumer<OpcuaAPU, Throwable> error = (message, e) -> {
LOGGER.error("Error while waiting for activate session response");
e.printStackTrace();
};
submit(context, timeout, error, consumer, buffer);
} catch (SerializationException e) {
LOGGER.error("Unable to to Parse Activate Session Request");
}
}
use of org.apache.plc4x.java.spi.ConversationContext in project plc4x by apache.
the class SecureChannel method submit.
public void submit(ConversationContext<OpcuaAPU> context, Consumer<TimeoutException> onTimeout, BiConsumer<OpcuaAPU, Throwable> error, Consumer<byte[]> consumer, WriteBufferByteBased buffer) {
int transactionId = channelTransactionManager.getTransactionIdentifier();
// TODO: We need to split large messages up into chunks if it is larger than the sendBufferSize
// This value is negotiated when opening a channel
OpcuaMessageRequest messageRequest = new OpcuaMessageRequest(FINAL_CHUNK, channelId.get(), tokenId.get(), transactionId, transactionId, buffer.getData());
final OpcuaAPU apu;
try {
if (this.isEncrypted) {
apu = OpcuaAPU.staticParse(encryptionHandler.encodeMessage(messageRequest, buffer.getData()), false);
} else {
apu = new OpcuaAPU(messageRequest, false);
}
} catch (ParseException e) {
throw new PlcRuntimeException("Unable to encrypt message before sending");
}
Consumer<Integer> requestConsumer = t -> {
try {
ByteArrayOutputStream messageBuffer = new ByteArrayOutputStream();
context.sendRequest(apu).expectResponse(OpcuaAPU.class, REQUEST_TIMEOUT).onTimeout(onTimeout).onError(error).unwrap(encryptionHandler::decodeMessage).unwrap(OpcuaAPU::getMessage).check(OpcuaMessageResponse.class::isInstance).unwrap(OpcuaMessageResponse.class::cast).check(p -> {
if (p.getRequestId() == transactionId) {
try {
messageBuffer.write(p.getMessage());
if (!(senderSequenceNumber.incrementAndGet() == (p.getSequenceNumber()))) {
LOGGER.error("Sequence number isn't as expected, we might have missed a packet. - {} != {}", senderSequenceNumber.incrementAndGet(), p.getSequenceNumber());
context.fireDisconnected();
}
} catch (IOException e) {
LOGGER.debug("Failed to store incoming message in buffer");
throw new PlcRuntimeException("Error while sending message");
}
return p.getChunk().equals(FINAL_CHUNK);
} else {
return false;
}
}).handle(opcuaResponse -> {
if (opcuaResponse.getChunk().equals(FINAL_CHUNK)) {
tokenId.set(opcuaResponse.getSecureTokenId());
channelId.set(opcuaResponse.getSecureChannelId());
consumer.accept(messageBuffer.toByteArray());
}
});
} catch (Exception e) {
throw new PlcRuntimeException("Error while sending message");
}
};
LOGGER.debug("Submitting Transaction to TransactionManager {}", transactionId);
channelTransactionManager.submit(requestConsumer, transactionId);
}
use of org.apache.plc4x.java.spi.ConversationContext in project plc4x by apache.
the class KnxNetIpProtocolLogic method onConnect.
@Override
public void onConnect(ConversationContext<KnxNetIpMessage> context) {
// Only the UDP transport supports login.
if (!context.isPassive()) {
LOGGER.info("KNX Driver running in ACTIVE mode.");
knxNetIpDriverContext.setPassiveMode(false);
DatagramChannel channel = (DatagramChannel) context.getChannel();
final InetSocketAddress localSocketAddress = channel.localAddress();
knxNetIpDriverContext.setLocalIPAddress(new IPAddress(localSocketAddress.getAddress().getAddress()));
knxNetIpDriverContext.setLocalPort(localSocketAddress.getPort());
// First send out a search request
// REMARK: This might be optional ... usually we would send a search request to ip 224.0.23.12
// Any KNX Gateway will respond with a search response. We're currently directly sending to the
// known gateway address, so it's sort of pointless, but at least only one device will respond.
LOGGER.info("Sending KNXnet/IP Search Request.");
SearchRequest searchRequest = new SearchRequest(new HPAIDiscoveryEndpoint(HostProtocolCode.IPV4_UDP, knxNetIpDriverContext.getLocalIPAddress(), knxNetIpDriverContext.getLocalPort()));
context.sendRequest(searchRequest).expectResponse(KnxNetIpMessage.class, Duration.ofMillis(1000)).check(p -> p instanceof SearchResponse).unwrap(p -> (SearchResponse) p).handle(searchResponse -> {
LOGGER.info("Got KNXnet/IP Search Response.");
// Check if this device supports tunneling services.
final ServiceId tunnelingService = searchResponse.getDibSuppSvcFamilies().getServiceIds().stream().filter(serviceId -> serviceId instanceof KnxNetIpTunneling).findFirst().orElse(null);
// If this device supports this type of service, tell the driver, we found a suitable device.
if (tunnelingService != null) {
// Extract the required information form the search request.
knxNetIpDriverContext.setGatewayAddress(searchResponse.getDibDeviceInfo().getKnxAddress());
knxNetIpDriverContext.setGatewayName(new String(searchResponse.getDibDeviceInfo().getDeviceFriendlyName()).trim());
LOGGER.info(String.format("Found KNXnet/IP Gateway '%s' with KNX address '%d.%d.%d'", knxNetIpDriverContext.getGatewayName(), knxNetIpDriverContext.getGatewayAddress().getMainGroup(), knxNetIpDriverContext.getGatewayAddress().getMiddleGroup(), knxNetIpDriverContext.getGatewayAddress().getSubGroup()));
// Next send a connection request to the gateway.
ConnectionRequest connectionRequest = new ConnectionRequest(new HPAIDiscoveryEndpoint(HostProtocolCode.IPV4_UDP, knxNetIpDriverContext.getLocalIPAddress(), knxNetIpDriverContext.getLocalPort()), new HPAIDataEndpoint(HostProtocolCode.IPV4_UDP, knxNetIpDriverContext.getLocalIPAddress(), knxNetIpDriverContext.getLocalPort()), new ConnectionRequestInformationTunnelConnection(knxNetIpDriverContext.getTunnelConnectionType()));
LOGGER.info("Sending KNXnet/IP Connection Request.");
context.sendRequest(connectionRequest).expectResponse(KnxNetIpMessage.class, Duration.ofMillis(1000)).check(p -> p instanceof ConnectionResponse).unwrap(p -> (ConnectionResponse) p).handle(connectionResponse -> {
// Remember the communication channel id.
knxNetIpDriverContext.setCommunicationChannelId(connectionResponse.getCommunicationChannelId());
LOGGER.info(String.format("Received KNXnet/IP Connection Response (Connection Id %s)", knxNetIpDriverContext.getCommunicationChannelId()));
// Check if everything went well.
Status status = connectionResponse.getStatus();
if (status == Status.NO_ERROR) {
final ConnectionResponseDataBlockTunnelConnection tunnelConnectionDataBlock = (ConnectionResponseDataBlockTunnelConnection) connectionResponse.getConnectionResponseDataBlock();
// Save the KNX Address the Gateway assigned to this connection.
knxNetIpDriverContext.setClientKnxAddress(tunnelConnectionDataBlock.getKnxAddress());
final KnxAddress gatewayAddress = knxNetIpDriverContext.getGatewayAddress();
final KnxAddress clientKnxAddress = knxNetIpDriverContext.getClientKnxAddress();
LOGGER.info(String.format("Successfully connected to KNXnet/IP Gateway '%s' with KNX address '%d.%d.%d' got assigned client KNX address '%d.%d.%d'", knxNetIpDriverContext.getGatewayName(), gatewayAddress.getMainGroup(), gatewayAddress.getMiddleGroup(), gatewayAddress.getSubGroup(), clientKnxAddress.getMainGroup(), clientKnxAddress.getMiddleGroup(), clientKnxAddress.getSubGroup()));
// Send an event that connection setup is complete.
context.fireConnected();
// Start a timer to check the connection state every 60 seconds.
// This keeps the connection open if no data is transported.
// Otherwise the gateway will terminate the connection.
connectionStateTimer = new Timer();
connectionStateTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
ConnectionStateRequest connectionStateRequest = new ConnectionStateRequest(knxNetIpDriverContext.getCommunicationChannelId(), new HPAIControlEndpoint(HostProtocolCode.IPV4_UDP, knxNetIpDriverContext.getLocalIPAddress(), knxNetIpDriverContext.getLocalPort()));
context.sendRequest(connectionStateRequest).expectResponse(KnxNetIpMessage.class, Duration.ofMillis(1000)).check(p -> p instanceof ConnectionStateResponse).unwrap(p -> (ConnectionStateResponse) p).handle(connectionStateResponse -> {
if (connectionStateResponse.getStatus() != Status.NO_ERROR) {
if (connectionStateResponse.getStatus() != null) {
LOGGER.error(String.format("Connection state problems. Got %s", connectionStateResponse.getStatus().name()));
} else {
LOGGER.error("Connection state problems. Got no status information.");
}
// Stop the timer.
connectionStateTimer.cancel();
}
});
}
}, 60000, 60000);
} else {
// The connection request wasn't successful.
LOGGER.error(String.format("Not connected to KNXnet/IP Gateway '%s' with KNX address '%d.%d.%d' got status: '%s'", knxNetIpDriverContext.getGatewayName(), knxNetIpDriverContext.getGatewayAddress().getMainGroup(), knxNetIpDriverContext.getGatewayAddress().getMiddleGroup(), knxNetIpDriverContext.getGatewayAddress().getSubGroup(), status.toString()));
// TODO: Actively disconnect
}
});
} else {
// This device doesn't support tunneling ... do some error handling.
LOGGER.error("Not connected to KNCnet/IP Gateway. The device doesn't support Tunneling.");
// TODO: Actively disconnect
}
});
} else // This usually when we're running a passive mode river.
{
LOGGER.info("KNX Driver running in PASSIVE mode.");
knxNetIpDriverContext.setPassiveMode(true);
// No login required, just confirm that we're connected.
context.fireConnected();
}
}
Aggregations