establishDtlsHandshake static method

Future<bool> establishDtlsHandshake({
  1. required Bridge bridge,
  2. required DtlsData dtlsData,
  3. required String? token,
})

Establish a DTLS handshake with the bridge.

Returns true if the handshake was successful, false otherwise.

The bridge parameter is the bridge to establish the handshake with.

token is the access token for remote access.

May throw ExpiredAccessTokenException if trying to connect to the bridge remotely and the token is expired. If this happens, refresh the token with TokenRepo.refreshRemoteToken.

Implementation

static Future<bool> establishDtlsHandshake({
  required Bridge bridge,
  required DtlsData dtlsData,
  required String? token,
}) async {
  final String? bridgeIpAddr = bridge.ipAddress;
  final String? clientKey = bridge.clientKey;
  final String? appKey = bridge.applicationKey;

  if (bridgeIpAddr == null) return false;
  if (clientKey == null) return false;
  if (appKey == null) return false;

  final String? appId = await _fetchAppId(bridgeIpAddr, appKey, token);

  if (appId == null) return false;

  List<int> clientKeyBytes = [];
  for (int i = 0; i < clientKey.length; i += 2) {
    String hex = clientKey.substring(i, i + 2);
    clientKeyBytes.add(int.parse(hex, radix: 16));
  }

  final DtlsClientContext clientContext = DtlsClientContext(
    verify: true,
    withTrustedRoots: true,
    ciphers: 'PSK-AES128-GCM-SHA256',
    pskCredentialsCallback: (_) {
      return PskCredentials(
        identity: utf8.encode(appId),
        preSharedKey: clientKeyBytes,
      );
    },
  );

  List<String>? possibleIpAddresses = await _fetchIpAddr();

  if (possibleIpAddresses == null) return false;

  // Flush out old connection data.
  await dtlsData.tryDispose();

  for (String ipAddress in possibleIpAddresses) {
    try {
      dtlsData.dtlsClient = await DtlsClient.bind(ipAddress, 0);
    } catch (e) {
      continue;
    }

    try {
      dtlsData.connection = await dtlsData.dtlsClient!.connect(
        InternetAddress(bridgeIpAddr),
        2100,
        clientContext,
        timeout: const Duration(seconds: 5),
      );

      break;
    } catch (e) {
      await dtlsData.tryCloseClient();

      continue;
    }
  }

  if (dtlsData.dtlsClient == null) return false;

  if (dtlsData.connection == null) {
    await dtlsData.tryCloseClient();

    return false;
  }

  dtlsData.connection?.listen((_) async {});

  return true;
}