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?
dtls2/lib/src/dtls_client.dart
Lines 516 to 520 in c25e3bd
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:
dtls2/lib/src/dtls_client.dart
Lines 523 to 530 in c25e3bd
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.
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 aDtlsTimeoutException
? 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 !!!