discover method
Discovers external IP address and port
Implementation
Future<StunResponse> discover() async {
final socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 0);
// print('Bound socket to port ${socket.port}');
final completer = Completer<StunResponse>();
Timer? timeoutTimer;
void cleanup() {
timeoutTimer?.cancel();
socket.close();
}
try {
final request = StunMessage.createBindingRequest();
final server = await stunServer;
// Set up timeout for STUN response only
timeoutTimer = Timer(timeout, () {
if (!completer.isCompleted) {
// print('STUN request timed out');
cleanup();
completer.completeError(
TimeoutException('STUN request timed out after ${timeout.inSeconds} seconds'));
}
});
// Listen for response
socket.listen(
(event) {
if (event == RawSocketEvent.read) {
final datagram = socket.receive();
if (datagram == null) return;
// print('Received response from ${datagram.address.address}:${datagram.port}');
final response = StunMessage.decode(datagram.data);
if (response == null) {
// print('Failed to decode STUN response');
return;
}
// Extract mapped address from XOR-MAPPED-ADDRESS or MAPPED-ADDRESS
final mappedAddress = _extractMappedAddress(response);
if (mappedAddress != null && !completer.isCompleted) {
// print('Successfully extracted mapped address: ${mappedAddress.address.address}:${mappedAddress.port}');
cleanup();
completer.complete(StunResponse(
externalAddress: mappedAddress.address,
externalPort: mappedAddress.port,
natType: _determineNatType(response),
));
}
}
},
onError: (error) {
print('Socket error: $error');
if (!completer.isCompleted) {
cleanup();
completer.completeError(error);
}
},
onDone: () {
print('Socket closed');
if (!completer.isCompleted) {
cleanup();
completer.completeError(
TimeoutException('Socket closed before receiving response'));
}
},
);
// Send request
final requestData = request.encode();
// print('Sending ${requestData.length} bytes to ${server.address}:$stunPort');
final sent = socket.send(requestData, server, stunPort);
// print('Sent $sent bytes');
if (sent == 0) {
throw Exception('Failed to send STUN request');
}
return await completer.future;
} catch (e) {
print('Error in discover: $e');
cleanup();
rethrow;
}
}