В методе VPN VPNRunnable Run я добавил следующий код, чтобы найти пакеты DNS-запросов, а затем разрешить DNS с помощью DoH.
Код: Выделить всё
private static class VPNRunnable implements Runnable {
//...
@Override
public void run() {
//...
if (packet.isDNSQuery()) {
String domain = packet.getDomainName();
Result resolvedResult = resolveDomainOverDoH(domain);
Log.i(TAG, "DNS Packet Before: " + packet.toString());
if (resolvedResult != null && !Objects.requireNonNull(resolvedResult.getAnswer()).isEmpty()) {
Packet dnsResponsePacket = buildDNSResponse(packet, resolvedResult);
if (dnsResponsePacket != null) {
Log.i(TAG, "DNS Packet After: " + dnsResponsePacket.toString());
networkToDeviceQueue.offer(dnsResponsePacket.toByteBuffer());
Log.i(TAG, "DNS response created for domain: " + domain);
} else {
Log.e(TAG, "Failed to create DNS response for domain: " + domain);
}
} else {
Log.e(TAG, "Failed to resolve DNS query for domain: " + domain);
}
} else if (packet.isUDP()) {
Log.i(TAG, "read udp" + readBytes); // Logs UDP read operation
deviceToNetworkUDPQueue.offer(packet); // Adds packet to UDP queue
} //...
Код: Выделить всё
data class Result(
@SerializedName("Status") val status: Int,
@SerializedName("TC") val tc: Boolean?,
@SerializedName("RD") val rd: Boolean?,
@SerializedName("RA") val ra: Boolean?,
@SerializedName("AD") val ad: Boolean?,
@SerializedName("CD") val cd: Boolean?,
@SerializedName("Question") val question: List?,
@SerializedName("Answer") val answer: List?,
@SerializedName("Authority") val authority: List?,
@SerializedName("edns_client_subnet") val ednsClientSubnet: String?
)
data class Question(
@SerializedName("name") val name: String,
@SerializedName("type") val type: Int
)
data class Answer(
@SerializedName("name") val name: String,
@SerializedName("type") val type: Int,
@SerializedName("TTL") val ttl: Int,
@SerializedName("Expires") val expires: String,
@SerializedName("data") val data: String
)
data class Authority(
@SerializedName("name") val name: String,
@SerializedName("type") val type: Int,
@SerializedName("TTL") val ttl: Int,
@SerializedName("Expires") val expires: String,
@SerializedName("data") val data: String
)
Код: Выделить всё
// Perform a synchronous DNS lookup
fun lookup(domain: String, type: Int): Result? {
for (resolver in resolvers) {
val url = "${resolver.url}?name=$domain&type=$type"
val request = Request.Builder()
.url(url)
.apply {
if (resolver.method == Method.POST) post(RequestBody.create(null, ByteArray(0)))
}
.addHeader("Content-Type", "application/dns-json")
.build()
try {
val response = client.newCall(request).execute()
if (response.isSuccessful) {
response.body?.let { responseBody ->
return gson.fromJson(responseBody.string(), Result::class.java)
}
}
} catch (e: IOException) {
// Log or handle exception
e.printStackTrace()
}
}
return null
}
Код: Выделить всё
public static Packet buildDNSResponse(Packet queryPacket, Result responseInfo) {
try {
// Create a new response packet based on the query packet
ByteBuffer responseBuffer = ByteBuffer.allocate(1500); // Allocate a large enough buffer
responseBuffer.put(queryPacket.backingBuffer.array(), 0, queryPacket.backingBuffer.limit());
responseBuffer.clear();
Packet responsePacket = new Packet(responseBuffer);
// Swap source and destination IP addresses
byte[] tempIp = responsePacket.ip4Header.sourceAddress.getAddress();
responsePacket.ip4Header.sourceAddress = responsePacket.ip4Header.destinationAddress;
responsePacket.ip4Header.destinationAddress = InetAddress.getByAddress(tempIp);
// Swap source and destination ports
int tempPort = responsePacket.udpHeader.sourcePort;
responsePacket.udpHeader.sourcePort = responsePacket.udpHeader.destinationPort;
responsePacket.udpHeader.destinationPort = tempPort;
// Modify DNS header
int dnsStart = Packet.IP4_HEADER_SIZE + Packet.UDP_HEADER_SIZE;
responsePacket.backingBuffer.putShort(dnsStart + 2, (short) 0x8180); // DNS flags: Standard query response, No error
responsePacket.backingBuffer.putShort(dnsStart + 6, (short) responseInfo.getAnswer().size()); // Answer RRs
// Find the end of the question section
int questionEnd = findQuestionEnd(responsePacket.backingBuffer, dnsStart);
// Add answer section
int answerStart = questionEnd;
for (Answer answer : responseInfo.getAnswer()) {
answerStart = addAnswerRecord(responsePacket.backingBuffer, answerStart, answer);
}
// Update packet length
int totalLength = answerStart;
responsePacket.ip4Header.totalLength = totalLength;
responsePacket.backingBuffer.putShort(2, (short) totalLength);
// Update UDP length
int udpLength = totalLength - Packet.IP4_HEADER_SIZE;
responsePacket.udpHeader.length = udpLength;
responsePacket.backingBuffer.putShort(Packet.IP4_HEADER_SIZE + 4, (short) udpLength);
// Recalculate checksums
responsePacket.updateIP4Checksum();
// Adjust the limit of the backing buffer
responsePacket.backingBuffer.limit(totalLength);
return responsePacket;
} catch (Exception e) {
Log.e(TAG, "Error building DNS response", e);
return null;
}
}
private static int findQuestionEnd(ByteBuffer buffer, int dnsStart) {
int position = dnsStart + 12; // Start after DNS header
while (buffer.get(position) != 0) {
position += buffer.get(position) + 1;
}
return position + 5; // +1 for null byte, +4 for QTYPE and QCLASS
}
private static int addAnswerRecord(ByteBuffer buffer, int start, Answer answer) throws UnknownHostException {
buffer.putShort(start, (short) 0xC00C); // Pointer to question name
buffer.putShort(start + 2, (short) answer.getType());
buffer.putShort(start + 4, (short) 1); // CLASS IN
buffer.putInt(start + 6, answer.getTtl());
byte[] rdata = parseRDATA(answer.getType(), answer.getData());
buffer.putShort(start + 10, (short) rdata.length);
buffer.position(start + 12);
buffer.put(rdata);
return start + 12 + rdata.length;
}
private static byte[] parseRDATA(int type, String data) throws UnknownHostException {
switch (type) {
case 1: // A record
return InetAddress.getByName(data).getAddress();
case 28: // AAAA record
return InetAddress.getByName(data).getAddress();
case 5: // CNAME record
case 16: // TXT record
default:
return data.getBytes();
}
}
Подробнее здесь: https://stackoverflow.com/questions/790 ... olving-dns