Class AsyncSecureDatagramSocket


  • public final class AsyncSecureDatagramSocket
    extends AsyncDatagramSocket
    This class extends AsyncDatagramSocket (which see) by providing an ability to create a secure connection using DTLS. The steps to create and open a secure datagram socket are the same as AsyncDatagramSocket with the following differences

    A secure datagram socket is opened using AsyncDatagramSocket.open methods. Once opened insecure data may be transmitted to a specified address either as raw bytes or using a DatagramBufferWriter. Once connected targeted sends are not allowed - which is no change from AsyncDatagramSocket. Targeted sends may be done again once the secure datagram socket is disconnected.

    In terms of the DatagramBufferWriter, the fill method must return an address when disconnected and null when connected.

    The biggest difference is in the connection process. There is a time lag between requesting a secure connection and connection completion. When the secure connection is successfully completed the listener is informed via the SecureDatagramListener.handleConnect(AsyncSecureDatagramSocket) callback. If secure connection failed, then SecureDatagramListener.handleDisconnect(Throwable, AsyncSecureDatagramSocket) is called. While the secure connection is being set up no application data may be transmitted - either targeted or not.

    Building, Opening a Secure Async UDP Socket

    The following example code shows how to create, open, and securely connect an AsyncSecureDatagramSocket. Variable dgramListener contains a SecureDatagramListener instance and dtlsContext a DTLS-type SSLContext instance.
    final SocketAddress bindAddress = new InetSocketAddress(bindPort);
    final SocketAddress peerAddress = new InetSocketAddress(peerAddress, peerPort);
    final AsyncSecureDatagramSocket.SecureDgramSocketBuilder builder = AsyncSecureDatagramSocket.secureBuilder();
    final AsyncSecureDatagramSocket socket = builder.inputBufferSize(512) // Optional. Defaults to ENetConfigure.DEFAULT_BUFFER_SIZE.
                                                    .outputBufferSize(512) // Optional. Defaults to ENetConfigure.DEFAULT_BUFFER_SIZE.
                                                    .byteOrder(ByteOrder.BIG_ENDIAN) // Optional. Defaults to ByteOrder.LITTLE_ENDIAN
                                                    .listener(dgramListener) // Required. Must be a SecureDatagramListener instance.
                                                    .selector("dgramSelector") // Optional. Defaults to AsyncChannel.defaultSelector().
                                                    .buiild();
    
    socket.open(bindAddress);
    
    // Set to true when connect process completes, successfully or not.
    mConnectComplete = false;
    
    // Set to true when connect process successfully completes.
    mIsConnected = false;
    
    // Start the secure datagram connection process as the initiating client.
    socket.connect(peerAddress, true, dtlsContext);
    
    // Wait for the connection to complete.
    mConnectLock.lock();
    try {
        while (!mConnectComplete) {
            mConnected.await();
        }
    } finally {
        mConnectLock.unlock();
    }
    
    // Did the secure datagram connection complete successfully?
    if (mIsConnected) {
        // Yes. Start sending application data.
    } else {
        // No. Handle this situation.
    }

    Secure Datagram Listener

    The SecureDatagramListener handleConnect and handleDisconnect methods are defined as follows:
    @Override public void handleConnect(final AsyncSecureDatagramSocket socket) {
        // Secure connection successfully completed.
        mIConnected = true;
    
        mConnectLock.lock();
        try {
            mConnectComplete = true;
            mConnected.signal();
        } finally {
            mConnectLock.unlock();
        }
    }
    
    @Override public void handleDisconnect(final Throwable t, final AsyncSecureDatagramSocket socket) {
        // Secure connection unsuccessfully completed.
        mIConnected = false;
    
        mConnectLock.lock();
        try {
            mConnectComplete = true;
            mConnected.signal();
        } finally {
            mConnectLock.unlock();
        }
    }

    Disconnecting and Closing a Secure Async UDP Socket

    Since UDP is a connection-less protocol, there is no concept of closing a DTLS connection. Once a DTLS session is established between two datagram sockets there is no way to terminate this session at this API level. Calling disconnect() drops the UDP "connection" and drops the DTLS engine - but the peer is not informed of this fact. If this datagram socket then sends insecure data to the peer, the peer will treat it as secure and attempt to unwrap the data which will fails with an SSLException.

    Having both ends of a UDP "connection" stop secure data transmission is an application-layer issue. One side would initiate the "disconnect" resulting in both sides calling disconnect() and dropping their DTLS engines. The same issue applies to closing a datagram socket.

    Note: eBus does not contain any security, cipher, or encryption code itself. eBus uses the security features provided by the Java platform to implement a secure UDP connection.

    Author:
    Charles W. Rapp
    See Also:
    AsyncSecureDatagramSocket.SecureDgramSocketBuilder, AsyncDatagramSocket, SecureDatagramListener
    • Method Detail

      • connect

        public void connect​(java.net.SocketAddress remoteAddr)
                     throws java.io.IOException
        Throws UnsupportedOperationException because insecure datagram connections are not supported by AsyncSecureDatagramSocket. See connect(SocketAddress, boolean, SSLContext) for instructions on making a secure datagram connection.
        Overrides:
        connect in class AsyncDatagramSocket
        Parameters:
        remoteAddr - connect to this remote address.
        Throws:
        java.lang.UnsupportedOperationException - because insecure datagram connections are not supported.
        java.io.IOException - not thrown by this method but required for override.
        See Also:
        connect(SocketAddress, boolean, SSLContext), disconnect()
      • disconnect

        public void disconnect()
                        throws java.io.IOException
        Disconnects the UDP "connection" to the peer and drops the DTLS engine used for secure communication. This means this datagram socket may be used again for targeted insecure communications.

        Note: DTLS does not have a closing handshake protocol. That is because UDP is a connection-less communication protocol and so there is nothing to disconnect. Terminating a DTLS connection on both sides is an application-level issue. One side must inform the other that secure communications are being terminated so both sides know to stop using their DTLS engine. If one side unilaterally stops using secure data transmission, the other side will still attempt to unwrap the input which will fail.

        This method may be invoked at any time. It will not have any effect on read or write operations already in progress when invoked.

        Does nothing if this datagram socket is closed or disconnected.

        Overrides:
        disconnect in class AsyncDatagramSocket
        Throws:
        java.io.IOException - if some I/O error occurs.
        See Also:
        connect(SocketAddress, boolean, SSLContext), AbstractAsyncDatagramSocket.close(), AbstractAsyncDatagramSocket.closeNow()
      • doOpen

        protected void doOpen​(java.net.SocketAddress bindAddr,
                              java.net.ProtocolFamily family)
                       throws java.io.IOException
        After successfully opening a datagram channel, marks this datagram socket as ready for secure connection.
        Overrides:
        doOpen in class AsyncDatagramSocket
        Parameters:
        bindAddr - bind the local address to this port.
        family - communication protocol family.
        Throws:
        java.io.IOException - if attempt to open datagram channel fails.
        See Also:
        AsyncDatagramSocket.doOpen(SocketAddress, ProtocolFamily), doClose()
      • doOpen

        protected void doOpen​(java.nio.channels.DatagramChannel channel)
                       throws java.io.IOException
        Encapsulates the given datagram channel and then marks this secure datagram socket as disconnected.
        Overrides:
        doOpen in class AsyncDatagramSocket
        Parameters:
        channel - use this datagram channel.
        Throws:
        java.io.IOException - if an I/O error occurs.
        See Also:
        AsyncDatagramSocket.doOpen(DatagramChannel), doClose()
      • doRead

        protected boolean doRead()
                          throws java.io.IOException
        Returns true if a packet was successfully received (meaning there could be more packets to follow) and false if no packet was received (meaning there are no more packets available at this time).
        Overrides:
        doRead in class AbstractAsyncDatagramSocket
        Returns:
        true if a packet was successfully received.
        Throws:
        java.io.IOException - if attempt to receive datagram packet failed due to I/O problems.
      • doSend

        protected void doSend​(byte[] b,
                              int off,
                              int len,
                              java.net.SocketAddress peer)
                       throws java.io.IOException
        Places given bytes into outbound buffer, encrypts those bytes, and then sends those encrypted bytes to the targeted far-end address.
        Overrides:
        doSend in class AbstractAsyncDatagramSocket
        Parameters:
        b - end len bytes from this array starting at off offset.
        off - offset into b.
        len - send this many bytes from b.
        peer - set to null and ignored since configured far-end address is used.
        Throws:
        java.nio.channels.NotYetConnectedException - if peer is null and the opening handshake with the peer is not successfully completed.
        java.io.IOException - if the data encryption or transmission fails.
      • doSend

        protected void doSend​(DatagramBufferWriter writer)
                       throws java.io.IOException
        Uses writer to directly write data to outbound TLS buffer. This by-passes the need to write data to a byte array first and then place that array into the outbound TLS buffer. If this datagram socket is not connected to a peer, then uses AsyncDatagramSocket.doSend(DatagramBufferWriter) to transmit the data; otherwise wraps the contents before transmitting.
        Overrides:
        doSend in class AsyncDatagramSocket
        Parameters:
        writer - writes the next available packet to the outbound TLS byte buffer.
        Throws:
        java.io.IOException - if the data encryption or transmission fails.
      • isReadyToSend

        public boolean isReadyToSend()
        Returns true if DTLS handshake is successfully completed and application data may now be transmitted on this secure datagram socket. Otherwise returns false.
        Returns:
        true if ready to send application data.
      • connect

        public void connect​(java.net.SocketAddress remoteAddr,
                            boolean isClient,
                            javax.net.ssl.SSLContext sslContext)
                     throws java.io.IOException
        "Connects" this datagram to the remote address using AsyncDatagramSocket.connect(SocketAddress) to perform parameter validation and the actual connection. Once successfully connected, then creates the DTLS engine and begins the opening security handshake process.

        Note: application data may not be transmitted until the opening handshake is successfully completed. The user is informed of this completion via SecureDatagramListener.handleConnect(AsyncSecureDatagramSocket) callback. If the opening handshake fails, then this datagram socket is closed.

        Parameters:
        remoteAddr - connect to this remote address.
        isClient - true if this is the DTLS client-side and false if server-side.
        sslContext - context used to create DTLS engine.
        Throws:
        java.lang.NullPointerException - if remoteAddr or sslContext is null.
        java.lang.IllegalArgumentException - if sslContext is not DTLS.
        java.lang.IllegalStateException - if datagram socket is already connected or closed.
        java.io.IOException - if some I/O error occurs.
        See Also:
        AsyncDatagramSocket.connect(SocketAddress), disconnect()
      • secureBuilder

        public static AsyncSecureDatagramSocket.SecureDgramSocketBuilder secureBuilder()
        Returns a new secure datagram builder instance used to create an AsyncSecureDatagramSocket instance.
        Returns:
        new AsyncSecureDatagramSocket builder instance.