JKRhb / dtls2

A DTLS library for Dart based on OpenSSL.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unable to catch DtlsException: DTLS Handshake has failed.

Ifilehk opened this issue · comments

Hello,
here an other point where I am struggling. Unable to catch DTLS Handshake has failed in this situation.

Client have a working dtls connection with the server.
Server wants to ban the client. Client's presharedkey is then removed from the server.
Client's tries to connect.

Result:

E/flutter (17787): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: DtlsException: DTLS Handshake has failed. E/flutter (17787): #0 _DtlsClientConnection._performShutdown (package:dtls2/src/dtls_client.dart:496:7) E/flutter (17787): #1 _DtlsClientConnection._connectToPeer.<anonymous closure> (package:dtls2/src/dtls_client.dart:518:15) E/flutter (17787): #2 _DtlsClientConnection._handleError (package:dtls2/src/dtls_client.dart:482:21) E/flutter (17787): #3 _DtlsClientConnection._connectToPeer (package:dtls2/src/dtls_client.dart:516:7) E/flutter (17787): #4 _DtlsClientConnection._maintainState (package:dtls2/src/dtls_client.dart:602:7) E/flutter (17787): #5 _DtlsClientConnection._incoming (package:dtls2/src/dtls_client.dart:563:5) E/flutter (17787): #6 DtlsClient._startListening.<anonymous closure> (package:dtls2/src/dtls_client.dart:87:26)

Would like to be able to catch this exeption to redirect the client to the signing page.

Thank you for your support.

Hi @Ifilehk! Thanks for opening this issue :)

Simply wrapping the connect() in a try-catch block probably wouldn't work here, right? Would creating a custom exception that is only being thrown in the case of a failed handshake be an option here? Or do you maybe have a better suggestion?

Hmm, or is the problem rather that the client is trying to reuse a connection that has been cached before?

No wrapping connect does not catch it. I suppose the exception is not transmitted up stream in the code.

exception should come out in connection.listen((...) { ... }, on DtlsException { here }

No wrapping connect does not catch it. I suppose the exception is not transmitted up stream in the code.

Ah, I see! Hmm, maybe that is caused by the fact that the exception is thrown as part of a callback?

_handleError(
ret,
() => _performShutdown(DtlsException("DTLS Handshake has failed.")),
);
}

Possible ! thus not transmitted upstream. By the way if I remember well the Dtls Exception on send() works as expected.

Possible ! thus not transmitted upstream. By the way if I remember well the Dtls Exception on send() works as expected.

Here, the exception is actually being thrown directly:

@override
int send(List<int> data) {
if (!connected) {
throw DtlsException("Sending failed: Not connected!");
}
return _dtlsClient._send(this, data);
}

I guess we figured out how to solve this problem then :) I will try to come up with a fix in the next few hours.

@Ifilehk Could you try out if #81 fixes the issue? :)

Unfortunately not yet fixed. Getting same Unhandled Exception.

E/flutter (12576): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: DtlsHandshakeException: DTLS Handshake has failed.
E/flutter (12576): #0      _DtlsClientConnection._performShutdown (package:dtls2/src/dtls_client.dart:492:7)
E/flutter (12576): #1      _DtlsClientConnection._connectToPeer (package:dtls2/src/dtls_client.dart:513:9)
E/flutter (12576): #2      _DtlsClientConnection._maintainState (package:dtls2/src/dtls_client.dart:597:7)
E/flutter (12576): #3      _DtlsClientConnection._incoming (package:dtls2/src/dtls_client.dart:558:5)
E/flutter (12576): #4      DtlsClient._startListening.<anonymous closure> (package:dtls2/src/dtls_client.dart:87:26)

Hmm, could you post a code snippet reproducing the issue? Or how the exception should be caught?

Here a test code against my dtls2 server.

  • ID0 is a valid identity and the corresponding preshared key is also valid. Connection will succeed.
  • ID1 is an invalid identity an the corresponding preshared is invalid too. Connection will reproduce the unhandled exception.

Interesting to see also, not related to this bug but maybe to understand the flow, that is you use a correct identity and a wrong presharedkey, you get DTLS Connection ERROR: TIMEOUT EXCEPTION. I would expect in this case too a DTLS Exception or what do you think ?

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:dtls2/dtls2.dart';

void main(List<String> arguments) {
  connect();
}

void connect() async {
  try {
    print("Connecting ...");

    const bindAddress = "0.0.0.0";
    DtlsClient dtlsClient = await DtlsClient.bind(bindAddress, 0);

    DtlsConnection dtlsConnection = await dtlsClient.connect(
      InternetAddress("XXX.XXX.XXX.XXX"),
      XXXX,
      DtlsClientContext(
        verify            : true,
        withTrustedRoots  : true,
        ciphers           : "PSK-AES128-CCM8",
        pskCredentialsCallback: (identityHint) {
          return PskCredentials(
            //identity      : ascii.encode('ID0'),  // Valid identity
            //preSharedKey  : ascii.encode('mAKW-X437USLUGkMZEcass5nwpgcLQbN'), // Valid key
            identity      : ascii.encode('ID1'),                            // Invalid identity
            preSharedKey  : ascii.encode('OAKW-X437USLUGkMZEcass5nwpgcLQbN'), // Invalid key
          );
        },
      ),
      timeout: const Duration(seconds: 10),
    );

    print('Connected');

    dtlsConnection.listen((datagram) async {
      print('Received datagram $datagram');

    }, onDone: () {
      print('DTLS Connection DONE');
    }, onError: (e) {
      print('DTLS Connection ERROR $e');
    },);

  } on DtlsException {
    print('DTLS Connection ERROR: DTLS EXCEPTION');

  } on SocketException {
    print('DTLS Connection ERROR: SOCKET EXCEPTION');

  } on TimeoutException {
    print('DTLS Connection ERROR: TIMEOUT EXCEPTION');

  } catch(e) {
    print('Unhandled exception $e');
  }
}

Hi @Ifilehk, thank you for the example! Unfortunately, I haven't been able to reproduce the issue using it :/ Could you maybe try using the altered example below (which uses the Californium test server) as a basis to replicate the issue (or integrate it with a server implementation)? This way I could try it out myself.

Good point regarding the TimeoutException by the way, how about a DtlsTimeoutException that extends the DtlsException class and implements TimeoutException? This way, we could have the best of both worlds, so to say. Edit: Done in #83.

// ignore_for_file: avoid_print

import "dart:async";
import "dart:convert";
import "dart:io";

import "package:dtls2/dtls2.dart";

void main(List<String> arguments) {
  connect();
}

const host = "californium.eclipseprojects.io";

Future<InternetAddress> lookupAddress() async {
  return (await InternetAddress.lookup(host, type: InternetAddressType.IPv6))
      .first;
}

Future<void> connect() async {
  final dtlsClient = await DtlsClient.bind(InternetAddress.anyIPv6, 0);

  final address = await lookupAddress();

  try {
    print("Connecting ...");

    final dtlsConnection = await dtlsClient.connect(
      address,
      5684,
      DtlsClientContext(
        withTrustedRoots: true,
        ciphers: "PSK-AES128-CCM8",
        pskCredentialsCallback: (identityHint) {
          return PskCredentials(
            identity: ascii.encode("Client_identity"),
            preSharedKey: ascii.encode("secretPSK"),
          );
        },
      ),
      timeout: const Duration(seconds: 10),
    );

    print("Connected");

    dtlsConnection.listen(
      (datagram) async {
        print("Received datagram $datagram");
      },
      onDone: () {
        print("DTLS Connection DONE");
      },
      onError: (e) {
        print("DTLS Connection ERROR $e");
      },
    );
  } on DtlsException {
    print("DTLS Connection ERROR: DTLS EXCEPTION");
  } on SocketException {
    print("DTLS Connection ERROR: SOCKET EXCEPTION");
  } on TimeoutException {
    print("DTLS Connection ERROR: TIMEOUT EXCEPTION");
  } catch (e) {
    print("Unhandled exception $e");
  }

  dtlsClient.close();
}

OK will try it.

Regarding DtlsTimeoutException. I don't understand it in this context. Of course the DtlsConnection ends with not beeing established, but the real cause is that the identity / preshared key pair is invalid. For the upper layer it makes sense to know the reason. And actually what is a DtlsTimeoutException ? I think it lacks a definition. In a connection Timeout means peer did not respond after a certain time. Here we have a response and an error that could give information to the client. What do you think ?

Cannot reproduce on your server. Could you try on mine ?
93.90.201.53 8888

Valid client: WERT
PSK: P4OPmcvtHyZIhBjDSmPgGjM8V1Ykkp2D

Cannot reproduce on your server. Could you try on mine ? 93.90.201.53 8888

Valid client: WERT PSK: P4OPmcvtHyZIhBjDSmPgGjM8V1Ykkp2D

Thank you! That helped me a lot actually :) I think I now found the root cause of the problem and opened #85 as a potential fix. The bug was that an exception was thrown in the first place instead of using the Completer – this made it uncatchable from the outside.

Regarding DtlsTimeoutException. I don't understand it in this context. Of course the DtlsConnection ends with not beeing established, but the real cause is that the identity / preshared key pair is invalid. For the upper layer it makes sense to know the reason. And actually what is a DtlsTimeoutException ? I think it lacks a definition. In a connection Timeout means peer did not respond after a certain time. Here we have a response and an error that could give information to the client. What do you think ?

That's true, I think the TimeoutException should only be thrown in the case of an actual timeout, though. With the potential fix in #85, I think this should now rather be the case. For other scenarios, I think it should be possible to determine the reason why the Handshake has failed and create more fine-grained exceptions from that. I will have a look into it.

#85 does the trick !!! Now client says:
DTLS EXCEPTION at the right place.

Thank you !!!

#85 does the trick !!! Now client says: DTLS EXCEPTION at the right place.

Thank you !!!

Awesome! Thank you for your error report! :) Then I will merge #85.