diff --git a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/endpoint/coaps/CoapsClientEndpointFactory.java b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/endpoint/coaps/CoapsClientEndpointFactory.java index 2d11be1e3d..3cb452c44e 100644 --- a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/endpoint/coaps/CoapsClientEndpointFactory.java +++ b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/endpoint/coaps/CoapsClientEndpointFactory.java @@ -67,7 +67,6 @@ import org.eclipse.leshan.core.CertificateUsage; import org.eclipse.leshan.core.SecurityMode; import org.eclipse.leshan.core.californium.DefaultExceptionTranslator; -import org.eclipse.leshan.core.californium.EndpointContextUtil; import org.eclipse.leshan.core.californium.ExceptionTranslator; import org.eclipse.leshan.core.californium.Lwm2mEndpointContextMatcher; import org.eclipse.leshan.core.californium.identity.IdentityHandler; @@ -76,6 +75,7 @@ import org.eclipse.leshan.core.request.Identity; import org.eclipse.leshan.core.request.exception.TimeoutException; import org.eclipse.leshan.core.request.exception.TimeoutException.Type; +import org.eclipse.leshan.core.util.X509CertUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -353,7 +353,7 @@ public Identity getIdentity(Message receivedMessage) { return Identity.rpk(peerAddress, publicKey); } else if (senderIdentity instanceof X500Principal || senderIdentity instanceof X509CertPath) { // Extract common name - String x509CommonName = EndpointContextUtil.extractCN(senderIdentity.getName()); + String x509CommonName = X509CertUtil.extractCN(senderIdentity.getName()); return Identity.x509(peerAddress, x509CommonName); } throw new IllegalStateException( diff --git a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/AsyncRequestObserver.java b/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/AsyncRequestObserver.java index 0ec9b853ac..b0469d233c 100644 --- a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/AsyncRequestObserver.java +++ b/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/AsyncRequestObserver.java @@ -33,11 +33,6 @@ */ public abstract class AsyncRequestObserver extends CoapAsyncRequestObserver { - public AsyncRequestObserver(Request coapRequest, final ResponseCallback responseCallback, - final ErrorCallback errorCallback, long timeoutInMs, ScheduledExecutorService executor) { - this(coapRequest, responseCallback, errorCallback, timeoutInMs, executor, new TemporaryExceptionTranslator()); - } - /** * A Californium message observer for a CoAP request helping to get results asynchronously dedicated for LWM2M * requests. diff --git a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/DefaultEndpointFactory.java b/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/DefaultEndpointFactory.java deleted file mode 100644 index 58da8d3dbb..0000000000 --- a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/DefaultEndpointFactory.java +++ /dev/null @@ -1,201 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019 Sierra Wireless and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.html. - * - * Contributors: - * Sierra Wireless - initial API and implementation - *******************************************************************************/ -package org.eclipse.leshan.core.californium; - -import java.net.InetSocketAddress; -import java.security.Principal; - -import org.eclipse.californium.core.network.CoapEndpoint; -import org.eclipse.californium.core.network.CoapEndpoint.Builder; -import org.eclipse.californium.core.network.EndpointContextMatcherFactory; -import org.eclipse.californium.core.observe.ObservationStore; -import org.eclipse.californium.elements.Connector; -import org.eclipse.californium.elements.EndpointContextMatcher; -import org.eclipse.californium.elements.PrincipalEndpointContextMatcher; -import org.eclipse.californium.elements.UDPConnector; -import org.eclipse.californium.elements.config.Configuration; -import org.eclipse.californium.oscore.OSCoreCoapStackFactory; -import org.eclipse.californium.oscore.OSCoreCtxDB; -import org.eclipse.californium.scandium.DTLSConnector; -import org.eclipse.californium.scandium.config.DtlsConnectorConfig; - -/** - * A default implementation of {@link EndpointFactory}. - *

- * Extends this class to create a custom {@link EndpointFactory}. - *

- * E.g. This could be useful if you need to use a custom {@link Connector}. - */ -public class DefaultEndpointFactory implements EndpointFactory { - - protected EndpointContextMatcher securedContextMatcher; - protected EndpointContextMatcher unsecuredContextMatcher; - protected String loggingTag; - - public DefaultEndpointFactory() { - this(null); - } - - public DefaultEndpointFactory(String loggingTag) { - this(loggingTag, false); - } - - /** - * @param loggingTag Logging tag - * @param isClient Indication whether this factory is for client or for server. - */ - public DefaultEndpointFactory(String loggingTag, boolean isClient) { - securedContextMatcher = createSecuredContextMatcher(isClient); - unsecuredContextMatcher = createUnsecuredContextMatcher(); - if (loggingTag != null) { - this.loggingTag = loggingTag; - } - } - - /** - * For server {@link Lwm2mEndpointContextMatcher} is created.
- * For client {@link PrincipalEndpointContextMatcher} is created. - *

- * This method is intended to be overridden. - * - * @return the {@link EndpointContextMatcher} used for secured communication - * @param isClient Indication whether to use client side endpoint context matcher of server side. - */ - protected EndpointContextMatcher createSecuredContextMatcher(boolean isClient) { - if (isClient) { - return new PrincipalEndpointContextMatcher() { - @Override - protected boolean matchPrincipals(Principal requestedPrincipal, Principal availablePrincipal) { - // As we are using 1 connector/endpoint by server at client side, - // and connector strongly limit connection from/to the expected foreign peer, - // we don't need to re-check principal at EndpointContextMatcher level. - return true; - } - }; - } else { - return new Lwm2mEndpointContextMatcher(); - } - } - - /** - * By default the Californium default one is used. See {@link EndpointContextMatcherFactory} for more details. - *

- * This method is intended to be overridden. - * - * @return the {@link EndpointContextMatcher} used for unsecured communication - */ - protected EndpointContextMatcher createUnsecuredContextMatcher() { - return null; - } - - @Override - public CoapEndpoint createUnsecuredEndpoint(InetSocketAddress address, Configuration coapConfig, - ObservationStore store, OSCoreCtxDB db) { - Builder builder = createUnsecuredEndpointBuilder(address, coapConfig, store); - if (db != null) { - builder.setCustomCoapStackArgument(db).setCoapStackFactory(new OSCoreCoapStackFactory()); - } - return builder.build(); - } - - /** - * This method is intended to be overridden. - * - * @param address the IP address and port, if null the connector is bound to an ephemeral port on the wildcard - * address. - * @param coapConfig the CoAP config used to create this endpoint. - * @param store the CoAP observation store used to create this endpoint. - * @return the {@link Builder} used for unsecured communication. - */ - protected CoapEndpoint.Builder createUnsecuredEndpointBuilder(InetSocketAddress address, Configuration coapConfig, - ObservationStore store) { - CoapEndpoint.Builder builder = new CoapEndpoint.Builder(); - builder.setConnector(createUnsecuredConnector(address, coapConfig)); - builder.setConfiguration(coapConfig); - if (loggingTag != null) { - builder.setLoggingTag("[" + loggingTag + "-coap://]"); - } else { - builder.setLoggingTag("[coap://]"); - } - if (unsecuredContextMatcher != null) { - builder.setEndpointContextMatcher(unsecuredContextMatcher); - } - if (store != null) { - builder.setObservationStore(store); - } - return builder; - } - - /** - * By default create an {@link UDPConnector}. - *

- * This method is intended to be overridden. - * - * @param address the IP address and port, if null the connector is bound to an ephemeral port on the wildcard - * address - * @param coapConfig the Configuration - * @return the {@link Connector} used for unsecured {@link CoapEndpoint} - */ - protected Connector createUnsecuredConnector(InetSocketAddress address, Configuration coapConfig) { - return new UDPConnector(address, coapConfig); - } - - @Override - public CoapEndpoint createSecuredEndpoint(DtlsConnectorConfig dtlsConfig, Configuration coapConfig, - ObservationStore store, OSCoreCtxDB db) { - // TODO OSCORE : add support of OSCORE to secured (DTLS) endpoint. - return createSecuredEndpointBuilder(dtlsConfig, coapConfig, store).build(); - } - - /** - * This method is intended to be overridden. - * - * @param dtlsConfig the DTLS config used to create this endpoint. - * @param coapConfig the CoAP config used to create this endpoint. - * @param store the CoAP observation store used to create this endpoint. - * @return the {@link Builder} used for secured communication. - */ - protected CoapEndpoint.Builder createSecuredEndpointBuilder(DtlsConnectorConfig dtlsConfig, - Configuration coapConfig, ObservationStore store) { - CoapEndpoint.Builder builder = new CoapEndpoint.Builder(); - builder.setConnector(createSecuredConnector(dtlsConfig)); - builder.setConfiguration(coapConfig); - if (loggingTag != null) { - builder.setLoggingTag("[" + loggingTag + "-coaps://]"); - } else { - builder.setLoggingTag("[coaps://]"); - } - if (securedContextMatcher != null) { - builder.setEndpointContextMatcher(securedContextMatcher); - } - if (store != null) { - builder.setObservationStore(store); - } - return builder; - } - - /** - * By default create a {@link DTLSConnector}. - *

- * This method is intended to be overridden. - * - * @param dtlsConfig the DTLS config used to create the Secured Connector. - * @return the {@link Connector} used for unsecured {@link CoapEndpoint} - */ - protected Connector createSecuredConnector(DtlsConnectorConfig dtlsConfig) { - return new DTLSConnector(dtlsConfig); - } -} diff --git a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/EndpointContextUtil.java b/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/EndpointContextUtil.java deleted file mode 100644 index 599f97a8c9..0000000000 --- a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/EndpointContextUtil.java +++ /dev/null @@ -1,135 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Sierra Wireless and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.html. - * - * Contributors: - * Sierra Wireless - initial API and implementation - * Achim Kraus (Bosch Software Innovations GmbH) - add support for californium - * endpoint context - * Rikard Höglund (RISE SICS) - Additions to support OSCORE - *******************************************************************************/ -package org.eclipse.leshan.core.californium; - -import java.net.InetSocketAddress; -import java.security.Principal; -import java.security.PublicKey; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.security.auth.x500.X500Principal; - -import org.eclipse.californium.elements.AddressEndpointContext; -import org.eclipse.californium.elements.DtlsEndpointContext; -import org.eclipse.californium.elements.EndpointContext; -import org.eclipse.californium.elements.MapBasedEndpointContext; -import org.eclipse.californium.elements.MapBasedEndpointContext.Attributes; -import org.eclipse.californium.elements.auth.PreSharedKeyIdentity; -import org.eclipse.californium.elements.auth.RawPublicKeyIdentity; -import org.eclipse.californium.elements.auth.X509CertPath; -import org.eclipse.californium.oscore.OSCoreEndpointContextInfo; -import org.eclipse.leshan.core.oscore.OscoreIdentity; -import org.eclipse.leshan.core.request.Identity; -import org.eclipse.leshan.core.util.Hex; - -//TODO TL : to be delete when no more class use it (at the end of the refactoring) - -/** - * Utility class used to handle Californium {@link EndpointContext} in Leshan. - *

- * Able to translate Californium {@link EndpointContext} to Leshan {@link Identity} and vice-versa. - */ -public class EndpointContextUtil { - - /** - * Create Leshan {@link Identity} from Californium {@link EndpointContext}. - * - * @param context The Californium {@link EndpointContext} to convert. - * @return The corresponding Leshan {@link Identity}. - * @throws IllegalStateException if we are not able to extract {@link Identity}. - */ - public static Identity extractIdentity(EndpointContext context) { - InetSocketAddress peerAddress = context.getPeerAddress(); - Principal senderIdentity = context.getPeerIdentity(); - if (senderIdentity != null) { - if (senderIdentity instanceof PreSharedKeyIdentity) { - return Identity.psk(peerAddress, ((PreSharedKeyIdentity) senderIdentity).getIdentity()); - } else if (senderIdentity instanceof RawPublicKeyIdentity) { - PublicKey publicKey = ((RawPublicKeyIdentity) senderIdentity).getKey(); - return Identity.rpk(peerAddress, publicKey); - } else if (senderIdentity instanceof X500Principal || senderIdentity instanceof X509CertPath) { - // Extract common name - String x509CommonName = extractCN(senderIdentity.getName()); - return Identity.x509(peerAddress, x509CommonName); - } - throw new IllegalStateException( - String.format("Unable to extract sender identity : unexpected type of Principal %s [%s]", - senderIdentity.getClass(), senderIdentity.toString())); - } else { - // Build identity for OSCORE if it is used - if (context.get(OSCoreEndpointContextInfo.OSCORE_RECIPIENT_ID) != null) { - String recipient = context.get(OSCoreEndpointContextInfo.OSCORE_RECIPIENT_ID); - return Identity.oscoreOnly(peerAddress, new OscoreIdentity(Hex.decodeHex(recipient.toCharArray()))); - } - } - return Identity.unsecure(peerAddress); - } - - /** - * Create Californium {@link EndpointContext} from Leshan {@link Identity}. - *

- * OSCORE does not use a Principal but automatically sets properties in the endpoint context at message - * transmission/reception. - * - * @param identity The Leshan {@link Identity} to convert. - * @param allowConnectionInitiation This request can initiate a Handshake if there is no DTLS connection. - * - * @return The corresponding Californium {@link EndpointContext}. - */ - public static EndpointContext extractContext(Identity identity, boolean allowConnectionInitiation) { - Principal peerIdentity = null; - if (identity != null) { - if (identity.isPSK()) { - peerIdentity = new PreSharedKeyIdentity(identity.getPskIdentity()); - } else if (identity.isRPK()) { - peerIdentity = new RawPublicKeyIdentity(identity.getRawPublicKey()); - } else if (identity.isX509()) { - /* simplify distinguished name to CN= part */ - peerIdentity = new X500Principal("CN=" + identity.getX509CommonName()); - } - } - - // TODO OSCORE : should we add properties to endpoint context ? - - if (peerIdentity != null && allowConnectionInitiation) { - return new MapBasedEndpointContext(identity.getPeerAddress(), peerIdentity, new Attributes() - .add(DtlsEndpointContext.KEY_HANDSHAKE_MODE, DtlsEndpointContext.HANDSHAKE_MODE_AUTO)); - } - return new AddressEndpointContext(identity.getPeerAddress(), peerIdentity); - } - - /** - * Extract "common name" from "distinguished name". - * - * @param dn The distinguished name. - * @return The extracted common name. - * @throws IllegalStateException if no CN is contained in DN. - */ - public static String extractCN(String dn) { - // Extract common name - Matcher endpointMatcher = Pattern.compile("CN=(.*?)(,|$)").matcher(dn); - if (endpointMatcher.find()) { - return endpointMatcher.group(1); - } else { - throw new IllegalStateException( - "Unable to extract sender identity : can not get common name in certificate"); - } - } -} diff --git a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/EndpointFactory.java b/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/EndpointFactory.java deleted file mode 100644 index 76cbf6ba87..0000000000 --- a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/EndpointFactory.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Sierra Wireless and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.html. - * - * Contributors: - * Sierra Wireless - initial API and implementation - * Rikard Höglund (RISE SICS) - Additions to support OSCORE - *******************************************************************************/ -package org.eclipse.leshan.core.californium; - -import java.net.InetSocketAddress; - -import org.eclipse.californium.core.network.CoapEndpoint; -import org.eclipse.californium.core.observe.ObservationStore; -import org.eclipse.californium.elements.config.Configuration; -import org.eclipse.californium.oscore.OSCoreCtxDB; -import org.eclipse.californium.scandium.config.DtlsConnectorConfig; - -/** - * Class used to delegate CoAP endpoint creation in all Leshan Builders. - * - * @see DefaultEndpointFactory - */ -public interface EndpointFactory { - - CoapEndpoint createUnsecuredEndpoint(InetSocketAddress address, Configuration coapConfig, ObservationStore store, - OSCoreCtxDB db); - - CoapEndpoint createSecuredEndpoint(DtlsConnectorConfig dtlsConfig, Configuration coapConfig, ObservationStore store, - OSCoreCtxDB db); -} diff --git a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/LwM2mCoapResource.java b/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/LwM2mCoapResource.java index 0429929c43..8556b8e429 100644 --- a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/LwM2mCoapResource.java +++ b/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/LwM2mCoapResource.java @@ -105,18 +105,6 @@ protected void handleInvalidRequest(Exchange exchange, String message, Throwable exchange.sendResponse(response); } - /** - * Create Leshan {@link Identity} from Californium {@link EndpointContext}. - * - * @param context The Californium {@link EndpointContext} to convert. - * @return The corresponding Leshan {@link Identity}. - * @throws IllegalStateException if we are not able to extract {@link Identity}. - */ - protected Identity extractIdentity(EndpointContext context) { - // TODO TL : to delete once server / client / bootstrap server will use new design - return EndpointContextUtil.extractIdentity(context); - } - protected Identity getForeignPeerIdentity(Exchange exchange, Message receivedMessage) { IdentityHandler identityHandler = identityHandlerProvider.getIdentityHandler(exchange.getEndpoint()); if (identityHandler != null) { @@ -132,11 +120,7 @@ protected Identity getForeignPeerIdentity(Exchange exchange, Message receivedMes */ protected Identity extractIdentitySafely(Exchange exchange, Message receivedMessage) { try { - if (identityHandlerProvider == null) { - return extractIdentity(receivedMessage.getSourceContext()); - } else { - return getForeignPeerIdentity(exchange, receivedMessage); - } + return getForeignPeerIdentity(exchange, receivedMessage); } catch (RuntimeException e) { LOG.error("Unable to extract identity", e); return null; diff --git a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/Lwm2mEndpointContextMatcher.java b/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/Lwm2mEndpointContextMatcher.java index 962138dcef..3db46b0a0d 100644 --- a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/Lwm2mEndpointContextMatcher.java +++ b/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/Lwm2mEndpointContextMatcher.java @@ -21,6 +21,7 @@ import org.eclipse.californium.elements.PrincipalEndpointContextMatcher; import org.eclipse.leshan.core.request.Identity; +import org.eclipse.leshan.core.util.X509CertUtil; // TODO TL: to be move in californium.identity package @@ -54,8 +55,8 @@ public String getName() { protected boolean matchPrincipals(Principal requestedPrincipal, Principal availablePrincipal) { if (requestedPrincipal instanceof X500Principal || availablePrincipal instanceof X500Principal) { try { - String requestedCommonName = EndpointContextUtil.extractCN(requestedPrincipal.getName()); - String availableCommonName = EndpointContextUtil.extractCN(availablePrincipal.getName()); + String requestedCommonName = X509CertUtil.extractCN(requestedPrincipal.getName()); + String availableCommonName = X509CertUtil.extractCN(availablePrincipal.getName()); return requestedCommonName.equals(availableCommonName); } catch (IllegalStateException e) { return false; diff --git a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/SyncRequestObserver.java b/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/SyncRequestObserver.java index ee572efaff..0253ba3b99 100644 --- a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/SyncRequestObserver.java +++ b/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/SyncRequestObserver.java @@ -33,10 +33,6 @@ */ public abstract class SyncRequestObserver extends CoapSyncRequestObserver { - public SyncRequestObserver(Request coapRequest, long timeout) { - this(coapRequest, timeout, new TemporaryExceptionTranslator()); - } - public SyncRequestObserver(Request coapRequest, long timeout, ExceptionTranslator exceptionTranslator) { super(coapRequest, timeout, exceptionTranslator); } diff --git a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/TemporaryExceptionTranslator.java b/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/TemporaryExceptionTranslator.java deleted file mode 100644 index 787099afed..0000000000 --- a/leshan-core-cf/src/main/java/org/eclipse/leshan/core/californium/TemporaryExceptionTranslator.java +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 Sierra Wireless and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.html. - * - * Contributors: - * Sierra Wireless - initial API and implementation - *******************************************************************************/ -package org.eclipse.leshan.core.californium; - -import org.eclipse.californium.core.coap.Request; -import org.eclipse.californium.scandium.dtls.DtlsHandshakeTimeoutException; -import org.eclipse.leshan.core.request.exception.TimeoutException; -import org.eclipse.leshan.core.request.exception.TimeoutException.Type; - -// TODO TL : this is just a class for backward compatibility waiting TL refactoring was done. -// It should be deleted at the end. -public class TemporaryExceptionTranslator extends DefaultExceptionTranslator { - - @Override - public Exception translate(Request coapRequest, Throwable error) { - if (error instanceof DtlsHandshakeTimeoutException) { - return new TimeoutException(Type.DTLS_HANDSHAKE_TIMEOUT, error, - "Request %s timeout : dtls handshake timeout", coapRequest.getURI()); - } else { - return super.translate(coapRequest, error); - } - } - -} diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/util/X509CertUtil.java b/leshan-core/src/main/java/org/eclipse/leshan/core/util/X509CertUtil.java index 990cedf45f..241ad31498 100644 --- a/leshan-core/src/main/java/org/eclipse/leshan/core/util/X509CertUtil.java +++ b/leshan-core/src/main/java/org/eclipse/leshan/core/util/X509CertUtil.java @@ -23,6 +23,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.security.auth.x500.X500Principal; @@ -137,6 +139,26 @@ private static boolean isHex(char ch) { return (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f'); } + private static Pattern extactCNPattern = Pattern.compile("CN=(.*?)(,|$)"); + + /** + * Extract "common name" from "distinguished name". + * + * @param dn The distinguished name. + * @return The extracted common name. + * @throws IllegalStateException if no CN is contained in DN. + */ + public static String extractCN(String dn) { + // Extract common name + Matcher endpointMatcher = extactCNPattern.matcher(dn); + if (endpointMatcher.find()) { + return endpointMatcher.group(1); + } else { + throw new IllegalStateException( + "Unable to extract sender identity : can not get common name in certificate"); + } + } + /** * Parses RFC 2253 name string. *

diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/ConnectionCleaner.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/ConnectionCleaner.java index be686d0af6..70bf35f16e 100644 --- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/ConnectionCleaner.java +++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/ConnectionCleaner.java @@ -25,7 +25,7 @@ import org.eclipse.californium.elements.auth.X509CertPath; import org.eclipse.californium.elements.util.LeastRecentlyUsedCache.Predicate; import org.eclipse.californium.scandium.DTLSConnector; -import org.eclipse.leshan.core.californium.EndpointContextUtil; +import org.eclipse.leshan.core.util.X509CertUtil; import org.eclipse.leshan.server.security.SecurityInfo; /** @@ -33,7 +33,7 @@ */ public class ConnectionCleaner { - private DTLSConnector connector; + private final DTLSConnector connector; public ConnectionCleaner(DTLSConnector connector) { this.connector = connector; @@ -64,7 +64,7 @@ else if (info.useRPK() && principal instanceof RawPublicKeyIdentity) { else if (info.useX509Cert() && principal instanceof X500Principal || principal instanceof X509CertPath) { // Extract common name - String x509CommonName = EndpointContextUtil.extractCN(principal.getName()); + String x509CommonName = X509CertUtil.extractCN(principal.getName()); if (x509CommonName.equals(info.getEndpoint())) { return true; } diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/CaliforniumLwM2mBootstrapRequestSender.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/CaliforniumLwM2mBootstrapRequestSender.java deleted file mode 100644 index a1a515aa30..0000000000 --- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/CaliforniumLwM2mBootstrapRequestSender.java +++ /dev/null @@ -1,138 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2013-2015 Sierra Wireless and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.html. - * - * Contributors: - * Zebra Technologies - initial API and implementation - * Michał Wadowski (Orange) - Improved compliance with rfc6690 - *******************************************************************************/ -package org.eclipse.leshan.server.californium.bootstrap; - -import org.eclipse.californium.core.network.Endpoint; -import org.eclipse.leshan.core.Destroyable; -import org.eclipse.leshan.core.link.lwm2m.LwM2mLinkParser; -import org.eclipse.leshan.core.node.LwM2mNode; -import org.eclipse.leshan.core.node.codec.CodecException; -import org.eclipse.leshan.core.node.codec.LwM2mDecoder; -import org.eclipse.leshan.core.node.codec.LwM2mEncoder; -import org.eclipse.leshan.core.request.DownlinkRequest; -import org.eclipse.leshan.core.request.exception.InvalidResponseException; -import org.eclipse.leshan.core.request.exception.RequestCanceledException; -import org.eclipse.leshan.core.request.exception.RequestRejectedException; -import org.eclipse.leshan.core.request.exception.SendFailedException; -import org.eclipse.leshan.core.request.exception.TimeoutException; -import org.eclipse.leshan.core.response.ErrorCallback; -import org.eclipse.leshan.core.response.LwM2mResponse; -import org.eclipse.leshan.core.response.ResponseCallback; -import org.eclipse.leshan.server.bootstrap.BootstrapSession; -import org.eclipse.leshan.server.bootstrap.LwM2mBootstrapRequestSender; -import org.eclipse.leshan.server.californium.request.RequestSender; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * An implementation of {@link LwM2mBootstrapRequestSender} based on Californium. - */ -public class CaliforniumLwM2mBootstrapRequestSender implements LwM2mBootstrapRequestSender, Destroyable { - - static final Logger LOG = LoggerFactory.getLogger(CaliforniumLwM2mBootstrapRequestSender.class); - - private final RequestSender sender; - - /** - * @param secureEndpoint The endpoint used to send coaps request. - * @param nonSecureEndpoint The endpoint used to send coap request. - * @param encoder The {@link LwM2mEncoder} used to encode {@link LwM2mNode}. - * @param decoder The {@link LwM2mDecoder} used to encode {@link LwM2mNode}. - * @param linkParser a parser {@link LwM2mLinkParser} used to parse a CoRE Link. - */ - public CaliforniumLwM2mBootstrapRequestSender(Endpoint secureEndpoint, Endpoint nonSecureEndpoint, - LwM2mEncoder encoder, LwM2mDecoder decoder, LwM2mLinkParser linkParser) { - this.sender = new RequestSender(secureEndpoint, nonSecureEndpoint, encoder, decoder, linkParser); - } - - /** - * Send a Lightweight M2M request synchronously. Will block until a response is received from the remote server. - *

- * The synchronous way could block a thread during a long time so it is more recommended to use the asynchronous - * way. - * - * @param destination The {@link BootstrapSession} associate to the device we want to sent the request. - * @param request The request to send to the client. - * @param timeoutInMs The global timeout to wait in milliseconds (see - * https://github.com/eclipse/leshan/wiki/Request-Timeout) - * @return the LWM2M response. The response can be null if the timeout expires (see - * https://github.com/eclipse/leshan/wiki/Request-Timeout). - * - * @throws CodecException if request payload can not be encoded. - * @throws InterruptedException if the thread was interrupted. - * @throws RequestRejectedException if the request is rejected by foreign peer. - * @throws RequestCanceledException if the request is cancelled. - * @throws SendFailedException if the request can not be sent. E.g. error at CoAP or DTLS/UDP layer. - * @throws InvalidResponseException if the response received is malformed. - */ - @Override - public T send(BootstrapSession destination, DownlinkRequest request, long timeoutInMs) - throws InterruptedException { - - return sender.sendLwm2mRequest(destination.getEndpoint(), destination.getIdentity(), destination.getId(), - destination.getModel(), null, request, null, timeoutInMs, false); - } - - /** - * Send a Lightweight M2M {@link DownlinkRequest} asynchronously to a LWM2M client. - * - * The Californium API does not ensure that message callback are exclusive. E.g. In some race condition, you can get - * a onReponse call and a onCancel one. This method ensures that you will receive only one event. Meaning, you get - * either 1 response or 1 error. - * - * @param destination The {@link BootstrapSession} associate to the device we want to sent the request. - * @param request The request to send to the client. - * @param timeoutInMs The global timeout to wait in milliseconds (see - * https://github.com/eclipse/leshan/wiki/Request-Timeout) - * @param responseCallback a callback called when a response is received (successful or error response). This - * callback MUST NOT be null. - * @param errorCallback a callback called when an error or exception occurred when response is received. It can be : - *

- * This callback MUST NOT be null. - * @throws CodecException if request payload can not be encoded. - */ - @Override - public void send(BootstrapSession destination, DownlinkRequest request, - long timeoutInMs, ResponseCallback responseCallback, ErrorCallback errorCallback) { - - sender.sendLwm2mRequest(destination.getEndpoint(), destination.getIdentity(), destination.getId(), - destination.getModel(), null, request, null, timeoutInMs, responseCallback, errorCallback, false); - } - - /** - * Cancel all ongoing requests for a given bootstrap session. - * - * @param session the bootstrap session for which we need to cancel requests. - */ - @Override - public void cancelOngoingRequests(BootstrapSession session) { - sender.cancelRequests(session.getId()); - } - - @Override - public void destroy() { - sender.destroy(); - } -} diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/endpoint/coaps/CoapsBootstrapServerEndpointFactory.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/endpoint/coaps/CoapsBootstrapServerEndpointFactory.java index f7dc4f3071..6bd1275a36 100644 --- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/endpoint/coaps/CoapsBootstrapServerEndpointFactory.java +++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/endpoint/coaps/CoapsBootstrapServerEndpointFactory.java @@ -48,7 +48,6 @@ import org.eclipse.californium.scandium.dtls.x509.SingleCertificateProvider; import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier; import org.eclipse.leshan.core.californium.DefaultExceptionTranslator; -import org.eclipse.leshan.core.californium.EndpointContextUtil; import org.eclipse.leshan.core.californium.ExceptionTranslator; import org.eclipse.leshan.core.californium.Lwm2mEndpointContextMatcher; import org.eclipse.leshan.core.californium.identity.IdentityHandler; @@ -57,6 +56,7 @@ import org.eclipse.leshan.core.request.Identity; import org.eclipse.leshan.core.request.exception.TimeoutException; import org.eclipse.leshan.core.request.exception.TimeoutException.Type; +import org.eclipse.leshan.core.util.X509CertUtil; import org.eclipse.leshan.server.bootstrap.LeshanBootstrapServer; import org.eclipse.leshan.server.californium.bootstrap.LwM2mBootstrapPskStore; import org.eclipse.leshan.server.californium.bootstrap.endpoint.CaliforniumBootstrapServerEndpointFactory; @@ -247,7 +247,7 @@ public Identity getIdentity(Message receivedMessage) { return Identity.rpk(peerAddress, publicKey); } else if (senderIdentity instanceof X500Principal || senderIdentity instanceof X509CertPath) { // Extract common name - String x509CommonName = EndpointContextUtil.extractCN(senderIdentity.getName()); + String x509CommonName = X509CertUtil.extractCN(senderIdentity.getName()); return Identity.x509(peerAddress, x509CommonName); } throw new IllegalStateException( diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/endpoint/coaps/CoapsServerEndpointFactory.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/endpoint/coaps/CoapsServerEndpointFactory.java index f0c416415d..428e4a321a 100644 --- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/endpoint/coaps/CoapsServerEndpointFactory.java +++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/endpoint/coaps/CoapsServerEndpointFactory.java @@ -50,7 +50,6 @@ import org.eclipse.californium.scandium.dtls.x509.SingleCertificateProvider; import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier; import org.eclipse.leshan.core.californium.DefaultExceptionTranslator; -import org.eclipse.leshan.core.californium.EndpointContextUtil; import org.eclipse.leshan.core.californium.ExceptionTranslator; import org.eclipse.leshan.core.californium.Lwm2mEndpointContextMatcher; import org.eclipse.leshan.core.californium.identity.IdentityHandler; @@ -59,6 +58,7 @@ import org.eclipse.leshan.core.request.Identity; import org.eclipse.leshan.core.request.exception.TimeoutException; import org.eclipse.leshan.core.request.exception.TimeoutException.Type; +import org.eclipse.leshan.core.util.X509CertUtil; import org.eclipse.leshan.server.LeshanServer; import org.eclipse.leshan.server.californium.ConnectionCleaner; import org.eclipse.leshan.server.californium.LwM2mPskStore; @@ -267,7 +267,7 @@ public Identity getIdentity(Message receivedMessage) { return Identity.rpk(peerAddress, publicKey); } else if (senderIdentity instanceof X500Principal || senderIdentity instanceof X509CertPath) { // Extract common name - String x509CommonName = EndpointContextUtil.extractCN(senderIdentity.getName()); + String x509CommonName = X509CertUtil.extractCN(senderIdentity.getName()); return Identity.x509(peerAddress, x509CommonName); } throw new IllegalStateException( diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/CoapRequestBuilder.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/CoapRequestBuilder.java index fd8aa52275..a7b00e7eae 100644 --- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/CoapRequestBuilder.java +++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/CoapRequestBuilder.java @@ -24,7 +24,6 @@ import org.eclipse.californium.core.coap.Request; import org.eclipse.californium.elements.EndpointContext; import org.eclipse.californium.elements.util.Bytes; -import org.eclipse.leshan.core.californium.EndpointContextUtil; import org.eclipse.leshan.core.californium.ObserveUtil; import org.eclipse.leshan.core.californium.identity.IdentityHandler; import org.eclipse.leshan.core.model.LwM2mModel; @@ -355,15 +354,8 @@ protected void setURI(Request coapRequest, LwM2mPath path) { } protected void setSecurityContext(Request coapRequest) { - if (identityHandler != null) { - EndpointContext context = identityHandler.createEndpointContext(destination, allowConnectionInitiation); - coapRequest.setDestinationContext(context); - - } else { - EndpointContext context = EndpointContextUtil.extractContext(destination, allowConnectionInitiation); - coapRequest.setDestinationContext(context); - - } + EndpointContext context = identityHandler.createEndpointContext(destination, allowConnectionInitiation); + coapRequest.setDestinationContext(context); if (destination.isOSCORE()) { coapRequest.getOptions().setOscore(Bytes.EMPTY); } diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/CoapRequestSender.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/CoapRequestSender.java deleted file mode 100644 index d3f9dfdf4d..0000000000 --- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/CoapRequestSender.java +++ /dev/null @@ -1,86 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2018 Sierra Wireless and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.html. - * - * Contributors: - * Sierra Wireless - initial API and implementation - *******************************************************************************/ -package org.eclipse.leshan.server.californium.request; - -import org.eclipse.californium.core.coap.Request; -import org.eclipse.californium.core.coap.Response; -import org.eclipse.leshan.core.californium.CoapResponseCallback; -import org.eclipse.leshan.core.request.exception.ClientSleepingException; -import org.eclipse.leshan.core.request.exception.RequestCanceledException; -import org.eclipse.leshan.core.request.exception.RequestRejectedException; -import org.eclipse.leshan.core.request.exception.SendFailedException; -import org.eclipse.leshan.core.request.exception.TimeoutException; -import org.eclipse.leshan.core.request.exception.UnconnectedPeerException; -import org.eclipse.leshan.core.response.ErrorCallback; -import org.eclipse.leshan.core.response.ResponseCallback; -import org.eclipse.leshan.server.registration.Registration; - -/** - * A {@link CoapRequestSender} is responsible to send CoAP {@link Request} for a given {@link Registration}. - */ -public interface CoapRequestSender { - - /** - * Send a CoAP {@link Request} synchronously to a LWM2M client. Will block until a response is received from the - * remote client. - *

- * The synchronous way could block a thread during a long time so it is more recommended to use the asynchronous - * way. - * - * @param destination The registration linked to the LWM2M client to which the request must be sent. - * @param coapRequest The request to send to the client. - * @param timeoutInMs The response timeout to wait in milliseconds (see - * https://github.com/eclipse/leshan/wiki/Request-Timeout) - * @return the response or null if the timeout expires (see - * https://github.com/eclipse/leshan/wiki/Request-Timeout). - * - * @throws InterruptedException if the thread was interrupted. - * @throws RequestRejectedException if the request is rejected by foreign peer. - * @throws RequestCanceledException if the request is cancelled. - * @throws SendFailedException if the request can not be sent. E.g. error at CoAP or DTLS/UDP layer. - * @throws UnconnectedPeerException if client is not connected (no dtls connection available). - * @throws ClientSleepingException if client is currently sleeping. - */ - Response sendCoapRequest(final Registration destination, final Request coapRequest, long timeoutInMs) - throws InterruptedException; - - /** - * Sends a CoAP {@link Request} asynchronously to a LWM2M client. - * - * {@link ResponseCallback} and {@link ErrorCallback} are exclusively called. - * - * @param destination The registration linked to the LWM2M client to which the request must be sent. - * @param coapRequest The request to send to the client. - * @param timeoutInMs The response timeout to wait in milliseconds (see - * https://github.com/eclipse/leshan/wiki/Request-Timeout) - * @param responseCallback a callback called when a response is received (successful or error response). This - * callback MUST NOT be null. - * @param errorCallback a callback called when an error or exception occurred when response is received. It can be : - *

    - *
  • {@link RequestRejectedException} if the request is rejected by foreign peer.
  • - *
  • {@link RequestCanceledException} if the request is cancelled.
  • - *
  • {@link SendFailedException} if the request can not be sent. E.g. error at CoAP or DTLS/UDP layer.
  • - *
  • {@link ClientSleepingException} if client is currently sleeping.
  • - *
  • {@link UnconnectedPeerException} if client is not connected (no dtls connection available).
  • - *
  • {@link TimeoutException} if the timeout expires (see - * https://github.com/eclipse/leshan/wiki/Request-Timeout).
  • - *
  • or any other RuntimeException for unexpected issue. - *
- * This callback MUST NOT be null. - */ - void sendCoapRequest(final Registration destination, final Request coapRequest, long timeoutInMs, - CoapResponseCallback responseCallback, ErrorCallback errorCallback); -} diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/RequestSender.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/RequestSender.java deleted file mode 100644 index dff2280fd5..0000000000 --- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/RequestSender.java +++ /dev/null @@ -1,454 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019 Sierra Wireless and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.html. - * - * Contributors: - * Sierra Wireless - initial API and implementation - * Michał Wadowski (Orange) - Improved compliance with rfc6690 - * Rikard Höglund (RISE SICS) - Additions to support OSCORE - *******************************************************************************/ -package org.eclipse.leshan.server.californium.request; - -import java.util.SortedMap; -import java.util.concurrent.ConcurrentNavigableMap; -import java.util.concurrent.ConcurrentSkipListMap; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -import org.eclipse.californium.core.coap.MessageObserver; -import org.eclipse.californium.core.coap.MessageObserverAdapter; -import org.eclipse.californium.core.coap.Request; -import org.eclipse.californium.core.coap.Response; -import org.eclipse.californium.core.network.Endpoint; -import org.eclipse.californium.elements.EndpointContext; -import org.eclipse.leshan.core.Destroyable; -import org.eclipse.leshan.core.californium.AsyncRequestObserver; -import org.eclipse.leshan.core.californium.CoapAsyncRequestObserver; -import org.eclipse.leshan.core.californium.CoapResponseCallback; -import org.eclipse.leshan.core.californium.CoapSyncRequestObserver; -import org.eclipse.leshan.core.californium.EndpointContextUtil; -import org.eclipse.leshan.core.californium.SyncRequestObserver; -import org.eclipse.leshan.core.californium.TemporaryExceptionTranslator; -import org.eclipse.leshan.core.link.lwm2m.LwM2mLinkParser; -import org.eclipse.leshan.core.model.LwM2mModel; -import org.eclipse.leshan.core.node.LwM2mNode; -import org.eclipse.leshan.core.node.codec.CodecException; -import org.eclipse.leshan.core.node.codec.LwM2mDecoder; -import org.eclipse.leshan.core.node.codec.LwM2mEncoder; -import org.eclipse.leshan.core.request.DownlinkRequest; -import org.eclipse.leshan.core.request.Identity; -import org.eclipse.leshan.core.request.exception.InvalidResponseException; -import org.eclipse.leshan.core.request.exception.RequestCanceledException; -import org.eclipse.leshan.core.request.exception.RequestRejectedException; -import org.eclipse.leshan.core.request.exception.SendFailedException; -import org.eclipse.leshan.core.request.exception.TimeoutException; -import org.eclipse.leshan.core.request.exception.UnconnectedPeerException; -import org.eclipse.leshan.core.response.ErrorCallback; -import org.eclipse.leshan.core.response.LwM2mResponse; -import org.eclipse.leshan.core.response.ResponseCallback; -import org.eclipse.leshan.core.util.NamedThreadFactory; -import org.eclipse.leshan.core.util.Validate; -import org.eclipse.leshan.server.request.LowerLayerConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This sender is able to send LWM2M or CoAP request in a synchronous or asynchronous way. - *

- * It can also link requests to a kind of "session" and cancel all ongoing requests associated to a given "session". - */ -public class RequestSender implements Destroyable { - - static final Logger LOG = LoggerFactory.getLogger(RequestSender.class); - - private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, - new NamedThreadFactory("Leshan Async Request timeout")); - - private final Endpoint nonSecureEndpoint; - private final Endpoint secureEndpoint; - private final LwM2mDecoder decoder; - private final LwM2mEncoder encoder; - private final LwM2mLinkParser linkParser; - - // A map which contains all ongoing CoAP requests - // This is used to be able to cancel request - private final ConcurrentNavigableMap ongoingRequests = new ConcurrentSkipListMap<>(); - - /** - * @param secureEndpoint The endpoint used to send coaps request. - * @param nonSecureEndpoint The endpoint used to send coap request. - * @param encoder The {@link LwM2mEncoder} used to encode {@link LwM2mNode}. - * @param decoder The {@link LwM2mDecoder} used to encode {@link LwM2mNode}. - * @param linkParser a parser {@link LwM2mLinkParser} used to parse a CoRE Link. - */ - public RequestSender(Endpoint secureEndpoint, Endpoint nonSecureEndpoint, LwM2mEncoder encoder, - LwM2mDecoder decoder, LwM2mLinkParser linkParser) { - this.secureEndpoint = secureEndpoint; - this.nonSecureEndpoint = nonSecureEndpoint; - this.encoder = encoder; - this.decoder = decoder; - this.linkParser = linkParser; - } - - /** - * Sends a Lightweight M2M {@link DownlinkRequest} synchronously to a LWM2M client. Will block until a response is - * received from the remote client. - *

- * The synchronous way could block a thread during a long time so it is more recommended to use the asynchronous - * way. - * - * @param endpointName the LWM2M client endpoint name. - * @param destination the LWM2M client {@link Identity}. - * @param sessionId A session Identifier which could be reused to cancel all ongoing request related to this - * sessionId. See {@link #cancelRequests(String)}. - * @param model The {@link LwM2mModel} used to encode payload in request and decode payload in response. - * @param rootPath a rootpath to prefix to the LWM2M path to create the CoAP path. (see 8.2.2 Alternate Path in - * LWM2M specification) - * @param request The request to send to the client. - * @param lowerLayerConfig to tweak lower layer request (e.g. coap request) - * @param timeoutInMs The response timeout to wait in milliseconds (see - * https://github.com/eclipse/leshan/wiki/Request-Timeout) - * @param allowConnectionInitiation This request can initiate a Handshake if there is no DTLS connection. - * @return the response or null if the timeout expires (see - * https://github.com/eclipse/leshan/wiki/Request-Timeout). - * - * @throws CodecException if request payload can not be encoded. - * @throws InterruptedException if the thread was interrupted. - * @throws RequestRejectedException if the request is rejected by foreign peer. - * @throws RequestCanceledException if the request is cancelled. - * @throws SendFailedException if the request can not be sent. E.g. error at CoAP or DTLS/UDP layer. - * @throws UnconnectedPeerException if client is not connected (no dtls connection available). - * @throws InvalidResponseException if the response received is malformed. - */ - public T sendLwm2mRequest(final String endpointName, Identity destination, - String sessionId, final LwM2mModel model, String rootPath, final DownlinkRequest request, - LowerLayerConfig lowerLayerConfig, long timeoutInMs, boolean allowConnectionInitiation) - throws InterruptedException { - - // Create the CoAP request from LwM2m request - CoapRequestBuilder coapClientRequestBuilder = new CoapRequestBuilder(destination, rootPath, sessionId, - endpointName, model, encoder, allowConnectionInitiation, lowerLayerConfig, null); - request.accept(coapClientRequestBuilder); - final Request coapRequest = coapClientRequestBuilder.getRequest(); - - // Send CoAP request synchronously - SyncRequestObserver syncMessageObserver = new SyncRequestObserver(coapRequest, timeoutInMs) { - @Override - public T buildResponse(Response coapResponse) { - // Build LwM2m response - LwM2mResponseBuilder lwm2mResponseBuilder = new LwM2mResponseBuilder<>(coapRequest, coapResponse, - endpointName, model, decoder, linkParser); - request.accept(lwm2mResponseBuilder); - return lwm2mResponseBuilder.getResponse(); - } - }; - coapRequest.addMessageObserver(syncMessageObserver); - - // Store pending request to be able to cancel it later - addOngoingRequest(sessionId, coapRequest); - - // Send CoAP request asynchronously - if (destination.isSecure()) - secureEndpoint.sendRequest(coapRequest); - else - nonSecureEndpoint.sendRequest(coapRequest); - - // Wait for response, then return it - return syncMessageObserver.waitForResponse(); - } - - /** - * Send a Lightweight M2M {@link DownlinkRequest} asynchronously to a LWM2M client. - * - * The Californium API does not ensure that message callback are exclusive. E.g. In some race condition, you can get - * a onReponse call and a onCancel one. This method ensures that you will receive only one event. Meaning, you get - * either 1 response or 1 error. - * - * @param endpointName the LWM2M client endpoint name. - * @param destination the LWM2M client {@link Identity}. - * @param sessionId A session Identifier which could be reused to cancel all ongoing request related to this - * sessionId. See {@link #cancelRequests(String)}. - * @param model The {@link LwM2mModel} used to encode payload in request and decode payload in response. - * @param rootPath a rootpath to prefix to the LWM2M path to create the CoAP path. (see 8.2.2 Alternate Path in - * LWM2M specification) - * @param request The request to send to the client. - * @param lowerLayerConfig to tweak lower layer request (e.g. coap request) - * @param timeoutInMs The response timeout to wait in milliseconds (see - * https://github.com/eclipse/leshan/wiki/Request-Timeout) - * @param responseCallback a callback called when a response is received (successful or error response). This - * callback MUST NOT be null. - * @param errorCallback a callback called when an error or exception occurred when response is received. It can be : - *

    - *
  • {@link RequestRejectedException} if the request is rejected by foreign peer.
  • - *
  • {@link RequestCanceledException} if the request is cancelled.
  • - *
  • {@link SendFailedException} if the request can not be sent. E.g. error at CoAP or DTLS/UDP layer.
  • - *
  • {@link InvalidResponseException} if the response received is malformed.
  • - *
  • {@link UnconnectedPeerException} if client is not connected (no dtls connection available).
  • - *
  • {@link TimeoutException} if the timeout expires (see - * https://github.com/eclipse/leshan/wiki/Request-Timeout).
  • - *
  • or any other RuntimeException for unexpected issue. - *
- * This callback MUST NOT be null. - * @param allowConnectionInitiation This request can initiate a Handshake if there is no DTLS connection. - * @throws CodecException if request payload can not be encoded. - */ - - public void sendLwm2mRequest(final String endpointName, Identity destination, - String sessionId, final LwM2mModel model, String rootPath, final DownlinkRequest request, - LowerLayerConfig lowerLayerConfig, long timeoutInMs, ResponseCallback responseCallback, - ErrorCallback errorCallback, boolean allowConnectionInitiation) { - - Validate.notNull(responseCallback); - Validate.notNull(errorCallback); - - // Create the CoAP request from LwM2m request - CoapRequestBuilder coapClientRequestBuilder = new CoapRequestBuilder(destination, rootPath, sessionId, - endpointName, model, encoder, allowConnectionInitiation, lowerLayerConfig, null); - request.accept(coapClientRequestBuilder); - final Request coapRequest = coapClientRequestBuilder.getRequest(); - - // Add CoAP request callback - MessageObserver obs = new AsyncRequestObserver(coapRequest, responseCallback, errorCallback, timeoutInMs, - executor) { - @Override - public T buildResponse(Response coapResponse) { - // Build LwM2m response - LwM2mResponseBuilder lwm2mResponseBuilder = new LwM2mResponseBuilder<>(coapRequest, coapResponse, - endpointName, model, decoder, linkParser); - request.accept(lwm2mResponseBuilder); - return lwm2mResponseBuilder.getResponse(); - } - }; - coapRequest.addMessageObserver(obs); - - // Store pending request to be able to cancel it later - addOngoingRequest(sessionId, coapRequest); - - // Send CoAP request asynchronously - if (destination.isSecure()) - secureEndpoint.sendRequest(coapRequest); - else - nonSecureEndpoint.sendRequest(coapRequest); - } - - /** - * Send a CoAP {@link Request} synchronously to a LWM2M client. Will block until a response is received from the - * remote client. - *

- * The synchronous way could block a thread during a long time so it is more recommended to use the asynchronous - * way. - * - * @param destination the LWM2M client {@link Identity}. - * @param sessionId A session Identifier which could be reused to cancel all ongoing request related to this one. - * See {@link #cancelRequests(String)}. - * @param coapRequest The request to send to the client. - * @param timeoutInMs The response timeout to wait in milliseconds (see - * https://github.com/eclipse/leshan/wiki/Request-Timeout) - * @param allowConnectionInitiation This request can initiate a Handshake if there is no DTLS connection. - * @return the response or null if the timeout expires (see - * https://github.com/eclipse/leshan/wiki/Request-Timeout). - * - * @throws InterruptedException if the thread was interrupted. - * @throws RequestRejectedException if the request is rejected by foreign peer. - * @throws RequestCanceledException if the request is cancelled. - * @throws SendFailedException if the request can not be sent. E.g. error at CoAP or DTLS/UDP layer. - * @throws UnconnectedPeerException if client is not connected (no dtls connection available). - */ - public Response sendCoapRequest(Identity destination, String sessionId, Request coapRequest, long timeoutInMs, - boolean allowConnectionInitiation) throws InterruptedException { - - // Define destination - if (coapRequest.getDestinationContext() == null) { - EndpointContext context = EndpointContextUtil.extractContext(destination, allowConnectionInitiation); - coapRequest.setDestinationContext(context); - } else { - LOG.warn( - "Destination context was not set by Leshan for this request. The context is used to ensure you talk to the right peer. Bad usage could bring to security issue. {}", - coapRequest); - } - - // TODO OSCORE : should we add the OSCORE option automatically here too ? - - // Send CoAP request synchronously - CoapSyncRequestObserver syncMessageObserver = new CoapSyncRequestObserver(coapRequest, timeoutInMs, - new TemporaryExceptionTranslator()); - coapRequest.addMessageObserver(syncMessageObserver); - - // Store pending request to be able to cancel it later - addOngoingRequest(sessionId, coapRequest); - - // Send CoAP request asynchronously - if (destination.isSecure()) - secureEndpoint.sendRequest(coapRequest); - else - nonSecureEndpoint.sendRequest(coapRequest); - - // Wait for response, then return it - return syncMessageObserver.waitForCoapResponse(); - } - - /** - * Sends a CoAP {@link Request} asynchronously to a LWM2M client. - * - * The Californium API does not ensure that message callback are exclusive. E.g. In some race condition, you can get - * a onReponse call and a onCancel one. This method ensures that you will receive only one event. Meaning, you get - * either 1 response or 1 error. - * - * @param destination the LWM2M client {@link Identity}. - * @param sessionId A session Identifier which could be reused to cancel all ongoing request related to this one. - * See {@link #cancelRequests(String)}. - * @param coapRequest The request to send to the client. - * @param timeoutInMs The response timeout to wait in milliseconds (see - * https://github.com/eclipse/leshan/wiki/Request-Timeout) - * @param responseCallback a callback called when a response is received (successful or error response). This - * callback MUST NOT be null. - * @param errorCallback a callback called when an error or exception occurred when response is received. It can be : - *

    - *
  • {@link RequestRejectedException} if the request is rejected by foreign peer.
  • - *
  • {@link RequestCanceledException} if the request is cancelled.
  • - *
  • {@link SendFailedException} if the request can not be sent. E.g. error at CoAP or DTLS/UDP layer.
  • - *
  • {@link UnconnectedPeerException} if client is not connected (no dtls connection available).
  • - *
  • {@link TimeoutException} if the timeout expires (see - * https://github.com/eclipse/leshan/wiki/Request-Timeout).
  • - *
  • or any other RuntimeException for unexpected issue. - *
- * This callback MUST NOT be null. - * @param allowConnectionInitiation This request can initiate a Handshake if there is no DTLS connection. - */ - public void sendCoapRequest(Identity destination, String sessionId, Request coapRequest, long timeoutInMs, - CoapResponseCallback responseCallback, ErrorCallback errorCallback, boolean allowConnectionInitiation) { - - Validate.notNull(responseCallback); - Validate.notNull(errorCallback); - - // Define destination - if (coapRequest.getDestinationContext() == null) { - EndpointContext context = EndpointContextUtil.extractContext(destination, allowConnectionInitiation); - coapRequest.setDestinationContext(context); - } else { - LOG.warn( - "Destination context was not set by Leshan for this request. The context is used to ensure you talk to the right peer. Bad usage could bring to security issue.{}", - coapRequest); - } - - // TODO OSCORE : should we add the OSCORE option automatically here too ? - - // Add CoAP request callback - MessageObserver obs = new CoapAsyncRequestObserver(coapRequest, responseCallback, errorCallback, timeoutInMs, - executor, new TemporaryExceptionTranslator()); - coapRequest.addMessageObserver(obs); - - // Store pending request to be able to cancel it later - addOngoingRequest(sessionId, coapRequest); - - // Send CoAP request asynchronously - if (destination.isSecure()) - secureEndpoint.sendRequest(coapRequest); - else - nonSecureEndpoint.sendRequest(coapRequest); - } - - /** - * Cancel all ongoing requests for the given sessionID. - * - * @param sessionID the Id associated to the ongoing requests you want to cancel. - * - * @see "All others send methods." - */ - public void cancelRequests(String sessionID) { - Validate.notNull(sessionID); - SortedMap requests = ongoingRequests.subMap(getFloorKey(sessionID), getCeilingKey(sessionID)); - for (Request coapRequest : requests.values()) { - coapRequest.cancel(); - } - requests.clear(); - } - - private static String getFloorKey(String sessionID) { - // The key format is sessionid#long, So we need a key which is always before this pattern (in natural order). - return sessionID + '#'; - } - - private static String getCeilingKey(String sessionID) { - // The key format is sessionid#long, So we need a key which is always after this pattern (in natural order). - return sessionID + "#A"; - } - - private static String getKey(String sessionID, long requestId) { - return sessionID + '#' + requestId; - } - - private void addOngoingRequest(String sessionID, Request coapRequest) { - if (sessionID != null) { - CleanerMessageObserver observer = new CleanerMessageObserver(sessionID, coapRequest); - coapRequest.addMessageObserver(observer); - ongoingRequests.put(observer.getRequestKey(), coapRequest); - } - } - - private void removeOngoingRequest(String key, Request coapRequest) { - Validate.notNull(key); - ongoingRequests.remove(key, coapRequest); - } - - private final AtomicLong idGenerator = new AtomicLong(0l); - - private class CleanerMessageObserver extends MessageObserverAdapter { - - private final String requestKey; - private final Request coapRequest; - - public CleanerMessageObserver(String sessionID, Request coapRequest) { - super(); - requestKey = getKey(sessionID, idGenerator.incrementAndGet()); - this.coapRequest = coapRequest; - } - - public String getRequestKey() { - return requestKey; - } - - @Override - public void onRetransmission() { - } - - @Override - public void onResponse(Response response) { - removeOngoingRequest(requestKey, coapRequest); - } - - @Override - public void onAcknowledgement() { - } - - @Override - protected void failed() { - removeOngoingRequest(requestKey, coapRequest); - } - - @Override - public void onCancel() { - removeOngoingRequest(requestKey, coapRequest); - } - } - - @Override - public void destroy() { - executor.shutdownNow(); - try { - executor.awaitTermination(5, TimeUnit.SECONDS); - } catch (InterruptedException e) { - LOG.warn("Destroying RequestSender was interrupted.", e); - } - } -}