use of org.eclipse.milo.opcua.stack.core.transport.TransportProfile in project milo by eclipse.
the class UascServerAsymmetricHandler method openSecureChannel.
private OpenSecureChannelResponse openSecureChannel(ChannelHandlerContext ctx, OpenSecureChannelRequest request) throws UaException {
SecurityTokenRequestType requestType = request.getRequestType();
if (requestType == SecurityTokenRequestType.Issue) {
secureChannel.setMessageSecurityMode(request.getSecurityMode());
String endpointUrl = ctx.channel().attr(UascServerHelloHandler.ENDPOINT_URL_KEY).get();
EndpointDescription endpoint = stackServer.getEndpointDescriptions().stream().filter(e -> {
boolean transportMatch = Objects.equals(e.getTransportProfileUri(), transportProfile.getUri());
boolean pathMatch = Objects.equals(EndpointUtil.getPath(e.getEndpointUrl()), EndpointUtil.getPath(endpointUrl));
boolean securityPolicyMatch = Objects.equals(e.getSecurityPolicyUri(), secureChannel.getSecurityPolicy().getUri());
boolean securityModeMatch = Objects.equals(e.getSecurityMode(), request.getSecurityMode());
return transportMatch && pathMatch && securityPolicyMatch && securityModeMatch;
}).findFirst().orElseThrow(() -> {
String message = String.format("no matching endpoint found: transportProfile=%s, " + "endpointUrl=%s, securityPolicy=%s, securityMode=%s", transportProfile, endpointUrl, secureChannel.getSecurityPolicy(), request.getSecurityMode());
return new UaException(StatusCodes.Bad_SecurityChecksFailed, message);
});
ctx.channel().attr(ENDPOINT_KEY).set(endpoint);
}
if (requestType == SecurityTokenRequestType.Renew && secureChannel.getMessageSecurityMode() != request.getSecurityMode()) {
throw new UaException(StatusCodes.Bad_SecurityChecksFailed, "secure channel renewal requested a different MessageSecurityMode.");
}
long channelLifetime = request.getRequestedLifetime().longValue();
channelLifetime = Math.min(channelLifetime, stackServer.getConfig().getMaximumSecureChannelLifetime().longValue());
channelLifetime = Math.max(channelLifetime, stackServer.getConfig().getMinimumSecureChannelLifetime().longValue());
ChannelSecurityToken newToken = new ChannelSecurityToken(uint(secureChannel.getChannelId()), uint(stackServer.getNextTokenId()), DateTime.now(), uint(channelLifetime));
SecurityKeys newKeys = null;
if (secureChannel.isSymmetricSigningEnabled()) {
// Validate the remote nonce; it must be non-null and the correct length for the security algorithm.
ByteString remoteNonce = request.getClientNonce();
NonceUtil.validateNonce(remoteNonce, secureChannel.getSecurityPolicy());
ByteString localNonce = generateNonce(secureChannel.getSecurityPolicy());
secureChannel.setLocalNonce(localNonce);
secureChannel.setRemoteNonce(remoteNonce);
newKeys = ChannelSecurity.generateKeyPair(secureChannel, secureChannel.getRemoteNonce(), secureChannel.getLocalNonce());
}
ChannelSecurity oldSecrets = secureChannel.getChannelSecurity();
SecurityKeys oldKeys = oldSecrets != null ? oldSecrets.getCurrentKeys() : null;
ChannelSecurityToken oldToken = oldSecrets != null ? oldSecrets.getCurrentToken() : null;
ChannelSecurity newSecrets = new ChannelSecurity(newKeys, newToken, oldKeys, oldToken);
secureChannel.setChannelSecurity(newSecrets);
/*
* Cancel the previous timeout, if it exists, and start a new one.
*/
if (secureChannelTimeout == null || secureChannelTimeout.cancel()) {
final long lifetime = channelLifetime;
secureChannelTimeout = Stack.sharedWheelTimer().newTimeout(timeout -> {
logger.debug("SecureChannel renewal timed out after {}ms. id={}, channel={}", lifetime, secureChannel.getChannelId(), ctx.channel());
ctx.close();
}, channelLifetime, TimeUnit.MILLISECONDS);
}
ResponseHeader responseHeader = new ResponseHeader(DateTime.now(), request.getRequestHeader().getRequestHandle(), StatusCode.GOOD, null, null, null);
return new OpenSecureChannelResponse(responseHeader, uint(PROTOCOL_VERSION), newToken, secureChannel.getLocalNonce());
}
use of org.eclipse.milo.opcua.stack.core.transport.TransportProfile in project milo by eclipse.
the class UascServerHelloHandler method onHello.
private void onHello(ChannelHandlerContext ctx, ByteBuf buffer) throws UaException {
logger.debug("[remote={}] Received Hello message.", ctx.channel().remoteAddress());
receivedHello = true;
final HelloMessage hello = TcpMessageDecoder.decodeHello(buffer);
String endpointUrl = hello.getEndpointUrl();
boolean endpointMatch = endpointUrl != null && stackServer.getEndpointDescriptions().stream().anyMatch(endpoint -> Objects.equals(EndpointUtil.getPath(endpointUrl), EndpointUtil.getPath(endpoint.getEndpointUrl())));
if (!endpointMatch) {
throw new UaException(StatusCodes.Bad_TcpEndpointUrlInvalid, "unrecognized endpoint url: " + endpointUrl);
}
ctx.channel().attr(ENDPOINT_URL_KEY).set(endpointUrl);
long remoteProtocolVersion = hello.getProtocolVersion();
long remoteReceiveBufferSize = hello.getReceiveBufferSize();
long remoteSendBufferSize = hello.getSendBufferSize();
long remoteMaxMessageSize = hello.getMaxMessageSize();
long remoteMaxChunkCount = hello.getMaxChunkCount();
if (remoteProtocolVersion < PROTOCOL_VERSION) {
throw new UaException(StatusCodes.Bad_ProtocolVersionUnsupported, "unsupported protocol version: " + remoteProtocolVersion);
}
EncodingLimits config = stackServer.getConfig().getEncodingLimits();
/* Our receive buffer size is determined by the remote send buffer size. */
long localReceiveBufferSize = Math.min(remoteSendBufferSize, config.getMaxChunkSize());
/* Our send buffer size is determined by the remote receive buffer size. */
long localSendBufferSize = Math.min(remoteReceiveBufferSize, config.getMaxChunkSize());
/* Max chunk count the remote can send us; not influenced by remote configuration. */
long localMaxChunkCount = config.getMaxChunkCount();
/* Max message size the remote can send us. Determined by our max chunk count and receive buffer size. */
long localMaxMessageSize = Math.min(localReceiveBufferSize * localMaxChunkCount, config.getMaxMessageSize());
ChannelParameters parameters = new ChannelParameters(Ints.saturatedCast(localMaxMessageSize), Ints.saturatedCast(localReceiveBufferSize), Ints.saturatedCast(localSendBufferSize), Ints.saturatedCast(localMaxChunkCount), Ints.saturatedCast(remoteMaxMessageSize), Ints.saturatedCast(remoteReceiveBufferSize), Ints.saturatedCast(remoteSendBufferSize), Ints.saturatedCast(remoteMaxChunkCount));
SerializationQueue serializationQueue = new SerializationQueue(stackServer.getConfig().getExecutor(), parameters, stackServer.getSerializationContext());
ctx.pipeline().addLast(new UascServerAsymmetricHandler(stackServer, transportProfile, serializationQueue));
ctx.pipeline().remove(this);
logger.debug("[remote={}] Removed HelloHandler, added AsymmetricHandler.", ctx.channel().remoteAddress());
AcknowledgeMessage acknowledge = new AcknowledgeMessage(PROTOCOL_VERSION, localReceiveBufferSize, localSendBufferSize, localMaxMessageSize, localMaxChunkCount);
ByteBuf messageBuffer = TcpMessageEncoder.encode(acknowledge);
// Using ctx.executor() is necessary to ensure this handler is removed
// before the message can be written and another response arrives.
ctx.executor().execute(() -> ctx.writeAndFlush(messageBuffer));
logger.debug("[remote={}] Sent Acknowledge message.", ctx.channel().remoteAddress());
}
use of org.eclipse.milo.opcua.stack.core.transport.TransportProfile in project milo by eclipse.
the class ServerChannelManager method bootstrap.
private static CompletableFuture<Channel> bootstrap(UaStackServer stackServer, InetSocketAddress bindAddress, TransportProfile transportProfile) {
ChannelInitializer<SocketChannel> initializer;
if (transportProfile == TransportProfile.TCP_UASC_UABINARY) {
initializer = new OpcServerTcpChannelInitializer(stackServer);
} else {
initializer = new OpcServerHttpChannelInitializer(stackServer);
}
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(Stack.sharedEventLoop()).handler(new LoggingHandler(ServerChannelManager.class)).channel(NioServerSocketChannel.class).childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT).childOption(ChannelOption.TCP_NODELAY, true).childHandler(initializer);
CompletableFuture<Channel> channelFuture = new CompletableFuture<>();
bootstrap.bind(bindAddress).addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
Channel channel = future.channel();
channelFuture.complete(channel);
} else {
channelFuture.completeExceptionally(future.cause());
}
});
return channelFuture;
}
use of org.eclipse.milo.opcua.stack.core.transport.TransportProfile in project milo by eclipse.
the class OpcClientWebSocketChannelInitializer method initChannel.
@Override
protected void initChannel(SocketChannel channel) throws Exception {
String endpointUrl = client.getConfig().getEndpoint().getEndpointUrl();
String scheme = EndpointUtil.getScheme(endpointUrl);
TransportProfile transportProfile = TransportProfile.fromUri(client.getConfig().getEndpoint().getTransportProfileUri());
String subprotocol;
if (transportProfile == TransportProfile.WSS_UASC_UABINARY) {
subprotocol = "opcua+cp";
} else if (transportProfile == TransportProfile.WSS_UAJSON) {
subprotocol = "opcua+uajson";
} else {
throw new UaException(StatusCodes.Bad_InternalError, "unexpected TransportProfile: " + transportProfile);
}
if ("opc.wss".equalsIgnoreCase(scheme)) {
SslContext sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
channel.pipeline().addLast(sslContext.newHandler(channel.alloc()));
}
int maxMessageSize = client.getConfig().getEncodingLimits().getMaxMessageSize();
channel.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
channel.pipeline().addLast(new HttpClientCodec());
channel.pipeline().addLast(new HttpObjectAggregator(maxMessageSize));
channel.pipeline().addLast(new WebSocketClientProtocolHandler(WebSocketClientHandshakerFactory.newHandshaker(new URI(endpointUrl), WebSocketVersion.V13, subprotocol, true, new DefaultHttpHeaders(), client.getConfig().getEncodingLimits().getMaxChunkSize())));
channel.pipeline().addLast(new WebSocketFrameAggregator(client.getConfig().getEncodingLimits().getMaxMessageSize()));
// OpcClientWebSocketFrameCodec adds UascClientAcknowledgeHandler when the WS upgrade is done.
channel.pipeline().addLast(new OpcClientWebSocketBinaryFrameCodec(client, handshake));
}
use of org.eclipse.milo.opcua.stack.core.transport.TransportProfile in project milo by eclipse.
the class UaStackClient method create.
/**
* Create a {@link UaStackClient} with {@code config}.
* <p>
* The {@link UaTransport} instance to create will be inferred from the transport profile URI in the configured
* endpoint.
* <p>
* Supported transports:
* <ul>
* <li>TCP + UA Binary</li>
* <li>HTTP(s) + UA Binary</li>
* </ul>
* <p>
* Experimentally supported:
* <ul>
* <li>WebSocket + UA Binary</li>
* </ul>
* <p>
* Not supported:
* <ul>
* <li>HTTP(s) + UA JSON</li>
* <li>HTTP(s) + UA XML</li>
* <li>WebSocket + UA JSON</li>
* </ul>
*
* @param config the {@link UaStackClientConfig}.
* @return a {@link UaStackClient} created with {@code config}.
* @throws UaException if the transport is unsupported.
*/
public static UaStackClient create(UaStackClientConfig config) throws UaException {
String transportProfileUri = config.getEndpoint().getTransportProfileUri();
TransportProfile transportProfile = TransportProfile.fromUri(transportProfileUri);
Function<UaStackClient, UaTransport> transportFactory;
switch(transportProfile) {
case TCP_UASC_UABINARY:
transportFactory = OpcTcpTransport::new;
break;
case HTTPS_UABINARY:
transportFactory = OpcHttpTransport::new;
break;
case WSS_UASC_UABINARY:
transportFactory = OpcWebSocketTransport::new;
break;
case HTTPS_UAXML:
case HTTPS_UAJSON:
case WSS_UAJSON:
default:
throw new UaException(StatusCodes.Bad_InternalError, "unsupported transport: " + transportProfileUri);
}
return new UaStackClient(config, transportFactory);
}
Aggregations