/* This file is part of jpcsp. Jpcsp is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Jpcsp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Jpcsp. If not, see <http://www.gnu.org/licenses/>. */ package jpcsp.network.protocols; import java.io.EOFException; import jpcsp.util.Utilities; public class DNS { // DNS packet format, see http://www.tcpipguide.com/free/t_DNSMessageHeaderandQuestionSectionFormat.htm public static final int DNS_RESOURCE_RECORD_CLASS_IN = 1; // "Internet" public static final int DNS_RESOURCE_RECORD_TYPE_A = 1; // Address (IPv4) record public static final int DNS_RESPONSE_CODE_NO_ERROR = 0; public static final int DNS_RESPONSE_CODE_NAME_ERROR = 3; public int identifier; public boolean isResponseFlag; public int opcode; public boolean authoritativeAnswer; public boolean truncationFlag; public boolean recursionDesired; public boolean recursionAvailable; public int zero; public int responseCode; public int questionCount; public int answerRecordCount; public int authorityRecordCount; public int additionalRecordCount; public DNSRecord questions[]; public DNSAnswerRecord answerRecords[]; public DNSAnswerRecord authorityRecords[]; public DNSAnswerRecord additionalRecords[]; public static class DNSRecord { // See format http://www.zytrax.com/books/dns/ch15/ public String recordName; // List of types: https://en.wikipedia.org/wiki/List_of_DNS_record_types public int recordType; // Only record class: DNS_RESOURCE_RECORD_CLASS_IN public int recordClass; public void read(NetPacket packet) throws EOFException { recordName = packet.readDnsNameNotation(); recordType = packet.read16(); recordClass = packet.read16(); } public NetPacket write(NetPacket packet) throws EOFException { packet.writeDnsNameNotation(recordName); packet.write16(recordType); packet.write16(recordClass); return packet; } public int sizeOf() { if (recordName == null || recordName.length() == 0) { return 5; } return recordName.length() + 6; } @Override public String toString() { return String.format("recordName='%s', recordType=0x%X, recordClass=0x%X", recordName, recordType, recordClass); } } public static class DNSAnswerRecord extends DNSRecord { // See format http://www.zytrax.com/books/dns/ch15/ public int ttl; public int dataLength; public byte[] data; @Override public void read(NetPacket packet) throws EOFException { super.read(packet); ttl = packet.read32(); dataLength = packet.read16(); data = packet.readBytes(dataLength); } @Override public NetPacket write(NetPacket packet) throws EOFException { super.write(packet); packet.write32(ttl); packet.write16(dataLength); packet.writeBytes(data, 0, dataLength); return packet; } @Override public int sizeOf() { return super.sizeOf() + 6 + dataLength; } @Override public String toString() { return String.format("%s, ttl=0x%X, dataLength=0x%X, data=%s", super.toString(), ttl, dataLength, Utilities.getMemoryDump(data, 0, dataLength)); } } public DNS() { } public DNS(DNS dns) { identifier = dns.identifier; isResponseFlag = dns.isResponseFlag; opcode = dns.opcode; authoritativeAnswer = dns.authoritativeAnswer; truncationFlag = dns.truncationFlag; recursionDesired = dns.recursionDesired; recursionAvailable = dns.recursionAvailable; zero = dns.zero; responseCode = dns.responseCode; questionCount = dns.questionCount; answerRecordCount = dns.answerRecordCount; authorityRecordCount = dns.authorityRecordCount; additionalRecordCount = dns.additionalRecordCount; questions = dns.questions; answerRecords = dns.answerRecords; authorityRecords = dns.authorityRecords; additionalRecords = dns.additionalRecords; } private DNSRecord[] readRecords(NetPacket packet, int count) throws EOFException { DNSRecord records[] = new DNSRecord[count]; for (int i = 0; i < count; i++) { records[i] = new DNSRecord(); records[i].read(packet); } return records; } private DNSAnswerRecord[] readAnswerRecords(NetPacket packet, int count) throws EOFException { DNSAnswerRecord records[] = new DNSAnswerRecord[count]; for (int i = 0; i < count; i++) { records[i] = new DNSAnswerRecord(); records[i].read(packet); } return records; } public void read(NetPacket packet) throws EOFException { identifier = packet.read16(); isResponseFlag = packet.readBoolean(); opcode = packet.readBits(4); authoritativeAnswer = packet.readBoolean(); truncationFlag = packet.readBoolean(); recursionDesired = packet.readBoolean(); recursionAvailable = packet.readBoolean(); zero = packet.readBits(3); responseCode = packet.readBits(4); questionCount = packet.read16(); answerRecordCount = packet.read16(); authorityRecordCount = packet.read16(); additionalRecordCount = packet.read16(); questions = readRecords(packet, questionCount); answerRecords = readAnswerRecords(packet, answerRecordCount); authorityRecords = readAnswerRecords(packet, authorityRecordCount); additionalRecords = readAnswerRecords(packet, additionalRecordCount); } private void writeRecords(NetPacket packet, int recordsCount, DNSRecord records[]) throws EOFException { for (int i = 0; i < recordsCount; i++) { records[i].write(packet); } } public NetPacket write(NetPacket packet) throws EOFException { packet.write16(identifier); packet.writeBoolean(isResponseFlag); packet.writeBits(opcode, 4); packet.writeBoolean(authoritativeAnswer); packet.writeBoolean(truncationFlag); packet.writeBoolean(recursionDesired); packet.writeBoolean(recursionAvailable); packet.writeBits(zero, 3); packet.writeBits(responseCode, 4); packet.write16(questionCount); packet.write16(answerRecordCount); packet.write16(authorityRecordCount); packet.write16(additionalRecordCount); writeRecords(packet, questionCount, questions); writeRecords(packet, answerRecordCount, answerRecords); writeRecords(packet, authorityRecordCount, authorityRecords); writeRecords(packet, additionalRecordCount, additionalRecords); return packet; } public NetPacket write() throws EOFException { return write(new NetPacket(sizeOf())); } private int sizeOf(int recordsCount, DNSRecord records[]) { int size = 0; for (int i = 0; i < recordsCount; i++) { size += records[i].sizeOf(); } return size; } public int sizeOf() { int size = 12; size += sizeOf(questionCount, questions); size += sizeOf(answerRecordCount, answerRecords); size += sizeOf(authorityRecordCount, authorityRecords); size += sizeOf(additionalRecordCount, additionalRecords); return size; } private void toString(StringBuilder s, String prefix, int recordsCount, DNSRecord records[]) { for (int i = 0; i < recordsCount; i++) { s.append(String.format(", %s#%d[%s]", prefix, i, records[i])); } } @Override public String toString() { StringBuilder s = new StringBuilder(); s.append(String.format("identifier=0x%04X, isResponseFlag=%b, opcode=0x%X, authoritativeAnswer=%b, truncationFlag=%b, recursionDesired=%b, recursionAvailable=%b, zero=0x%X, responseCode=0x%X, questionCount=%d, answerRecordCount=%d, authorityRecordCount=%d, additionalRecordCount=%d", identifier, isResponseFlag, opcode, authoritativeAnswer, truncationFlag, recursionDesired, recursionAvailable, zero, responseCode, questionCount, answerRecordCount, authorityRecordCount, additionalRecordCount)); toString(s, "question", questionCount, questions); toString(s, "answerRecord", answerRecordCount, answerRecords); toString(s, "authorityRecord", authorityRecordCount, authorityRecords); toString(s, "additionalRecord", additionalRecordCount, additionalRecords); return s.toString(); } }