query static method
Future<List<DNSResponseRecord> >
query({
- required String domain,
- required DNSRecordType dnsRecordType,
- required DNSServer dnsServer,
- int timeout = 5000,
Made a DNS query.
Implementation
static Future<List<DNSResponseRecord>> query({
required String domain,
required DNSRecordType dnsRecordType,
required DNSServer dnsServer,
int timeout = 5000, // 5 seconds by default (in milliseconds)
}) async {
final List<DNSResponseRecord> records = [ ];
final Completer<List<DNSResponseRecord>> completer = Completer();
if(dnsServer.protocol == DNSProtocol.udp) { // UDP connection
// Construye el paquete de consulta DNS
final Uint8List query = _buildQuery(domain, dnsRecordType);
// Transform hostname to ip address
InternetAddress? addr = InternetAddress.tryParse(dnsServer.host);
if(addr == null) {
addr = (await InternetAddress.lookup(dnsServer.host))
.where((i) => i.type == InternetAddressType.IPv4)
.firstOrNull;
if(addr == null) {
throw ArgumentError('Host not found: ${dnsServer.host}');
}
}
// Envia el paquete al servidor DNS vía UDP
final RawDatagramSocket socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 0);
socket.send(query, addr, dnsServer.port);
// Timeout
final timer = Timer(Duration(milliseconds: timeout), () {
if (!completer.isCompleted) {
socket.close();
completer.completeError(TimeoutException('DNS query timeout'));
}
});
// Event receiver
socket.listen((RawSocketEvent event) {
if (event == RawSocketEvent.read) {
final Datagram? response = socket.receive();
if (response != null) {
records.addAll(_parseResponse(response.data));
completer.complete(records);
socket.close(); // TODO: Multistream unsupported.
timer.cancel();
}
// } else if (event == RawSocketEvent.write) {
} else if(event == RawSocketEvent.closed) {
if(!completer.isCompleted) {
completer.complete(records);
}
timer.cancel();
}
});
} else if(dnsServer.protocol == DNSProtocol.tcp) { // TCP connection
// Construye el paquete de consulta DNS
final Uint8List query = _buildQuery(domain, dnsRecordType);
// Connect to server using TCP
final Socket socket = await Socket.connect(dnsServer.host, dnsServer.port);
// DNS sobre TCP requiere una cabecera de 2 bytes con la longitud
final Uint8List lengthPrefix = Uint8List(2);
lengthPrefix[0] = (query.length >> 8) & 0xFF;
lengthPrefix[1] = query.length & 0xFF;
// Escribe la longitud y luego el query
socket.add(lengthPrefix);
socket.add(query);
// Timeout
final Timer timer = Timer(Duration(milliseconds: timeout), () {
// Finalize thread with error
if (!completer.isCompleted) {
completer.completeError(TimeoutException('DNS TCP query timeout'));
}
// Destroy socket connection
socket.destroy();
});
// Stream parts
int responseLength = 0;
List<int> responseBuffer = [ ];
// Bind socket events
socket.listen(
// On receive data event
(List<int> bytes) {
// First stream part
if(responseBuffer.isEmpty) {
if (bytes.length < 2) {
// Cancel timeout
timer.cancel();
// Finalize thread with error
if (!completer.isCompleted) {
completer.completeError(FormatException('TCP response too short'));
}
// Destroy socket connection
socket.destroy();
// Finalize function
return;
}
// Leer longitud de la respuesta
responseLength = (bytes[0] << 8) | bytes[1];
if (bytes.length - 2 < responseLength) {
// Cancel timeout
timer.cancel();
// Finalize thread with error
if (!completer.isCompleted) {
completer.completeError(FormatException('Incomplete answer'));
}
// Destroy socket connection
socket.destroy();
// Finalize function
return;
}
}
// Add stream part
responseBuffer.addAll(bytes);
// End TCP parts
if(responseBuffer.length >= (responseLength + 2)) {
// Cancel timeout
timer.cancel();
// Transform response bytes
records.addAll(_parseResponse(Uint8List.fromList(responseBuffer.sublist(2, responseLength + 2))));
// Finalize thread with the results
if (!completer.isCompleted) {
completer.complete(records);
}
// Destroy socket connection
socket.destroy();
} // else { await for other TCP parts ... }
},
onError: (error) {
// Cancel timeout
timer.cancel();
// Finalize thread with errors
if (!completer.isCompleted) {
completer.completeError(error);
}
},
cancelOnError: true,
);
} else if(dnsServer.protocol == DNSProtocol.doh) { // DoH (HTTPS) connection
// Create the http client
final client = http.Client();
// Create http POST request
client.post(
Uri.parse('https://${dnsServer.host}:${dnsServer.port}${dnsServer.path}'),
headers: dnsServer.headers,
body: _buildQuery(domain, dnsRecordType),
)
// Manual timeout
.timeout(Duration(milliseconds: timeout))
.then((http.Response response) {
// Process response bytes
records.addAll(_parseResponse(response.bodyBytes));
// Finalize thread with the results
if (!completer.isCompleted) {
completer.complete(records);
}
// End http connection
client.close();
})
.catchError((error) {
// Finalize thread with errors
if (!completer.isCompleted) {
completer.completeError(error);
}
});
}
return completer.future;
}