Итак, моя ситуация заключается в том, что я проксирую все пользовательские DNS-запросы на устройстве Android и либо разрешаю их с помощью локальной базы данных, либо перенаправляю их на какой-либо вышестоящий DNS-сервер. С некоторым большим интересом к DNS по сравнению с TLS в последнее время я попытался реализовать его в соответствии с rfc.
Поскольку все это делается на мобильном устройстве, я не хочу устанавливать новое TLS-соединение для каждого прокси-запроса (в сумме это составит около 6,7 КБ), потому что пользователь может использовать мобильное соединение.< br> Короче говоря, в rfc говорится следующее:
[...] этот документ предусматривает, что успешное согласование TLS указывает на готовность обеих сторон оставить незанятые соединения DNS открытыми, независимо от тайм-аутов или других рекомендаций для DNS через TCP без TLS
Я использую следующий код для отправки тестовых запросов и оценки результата (урезан для удобства чтения):
private void tlsTest(){
Socket s = establishConnection("1.1.1.1"); //Cloudflare in this example, reproducible for Quad9 as well
DataInputStream in; //Created from the Socket
DataOutputStream out; //Created from the Socket
for (int i = 0; i < 20; i++) {
byte[] data = dnsRequestTypeA("google.com");
data = new DatagramPacket(data, 0, data.length, destination, 853).getData();
out.writeShort(data.length);
out.write(data);
out.flush();
int times = 0;
while((message = readDNSMessage(in)) == null && ++times <= 10){
Thread.sleep(200);
}
System.out.println("Response: " + message);
Thread.sleep(2500);
}
}
private DNSMessage readDNSMessage(DataInputStream in) throws IOException {
byte[] lengthBytes = new byte[2];
if(in.read(lengthBytes) < 0)return null;
// Each DNS answer is preceded with an unsigned short giving the length of the packet
int length = (lengthBytes[0]&0xFF) + ((lengthBytes[1] &0xFF) << 8);
byte[] data = new byte[length];
in.read(data);
return new DNSMessage(data);
}
@NonNull
private Socket establishConnection(String host) throws IOException {
Socket socket = getSocketFactory().createSocket(host, 853);
socket.setKeepAlive(true);
return socket;
}
Проблема здесь в том, что сервер вроде бы закрывает соединение (обозначается сломанной трубой, выброшенной при попытке отправить запрос [ssl=0x7ebb528840: I/O error during system call, Broken pipe
]), когда запроса не было в течение короткого времени (около 1-2 секунд в моих тестах).
У кого-нибудь есть опыт? Я делаю что-то не так (может быть, это как-то связано с реализацией OpenSSL, которую Android использует для TLS)? Очевидно, что просто спамить сервер каждую секунду или около того фиктивными запросами будет работать, но это будет очень хакерский обходной путь.