DNS response in Java


The DNS request in Java article focused on the request part. Now that that is done, the next thing is to receive the response and actually parse it.

If you’re more interested in the code without all the explanation skip to the end of the article.


I’ll be sticking to the tools I’ve already used in the first article.

As a quick reminder, the DNS protocol “speaks” in messages. The format of a message, according to RFC1035 section 4.1.1. looks like this

    +---------------------+
    |        Header       |
    +---------------------+
    |       Question      | the question for the name server
    +---------------------+
    |        Answer       | RRs answering the question
    +---------------------+
    |      Authority      | RRs pointing toward an authority
    +---------------------+
    |      Additional     | RRs holding additional information
    +---------------------+

We’ll start with the Header section of the response.


Before we can parse anything we must receive the response from the server.

As stated in the previous article Java used DatagramSockets and DatagramPackets to communicate via UDP.

Lets try to receive the response

byte[] response = new byte[1024];
DatagramPacket packet = new DatagramPacket(response, response.length);
socket.receive(packet);

And print out some information

System.out.println("\n\nReceived: " + packet.getLength() + " bytes");
for (int i = 0; i < packet.getLength(); i++) {
    System.out.print(String.format("%s", response[i]) + " ");
}
System.out.println("\n");

For the domain coderambling.com I receive this packet

Received: 60 bytes
-5 46 -127 -128 0 1 0 2 0 0 0 0 6 109 101 100 105 117 109 3 99 111 109 0 0 1 0 1 -64 12 0 1 0 1 0 0 0 1 0 4 -94 -97 -104 4 -64 12 0 1 0 1 0 0 0 1 0 4 -94 -97 -103 4

If you’re asking for A records for this domain you’ll most likely receive the same packets.

Parsing the response


As per the request, the Header response is exactly the same except the various sections contain different information.

The Header section is formatted like so

0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      ID                       |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    QDCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ANCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    NSCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ARCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

In order to read the information in the Header section we’ll have to parse it in an appropriate manner.

DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(response));

Reading the response into a dataInputStream allows for a more controlled manner of parsing.

Getting the ID is as simple as reading the first short in the input

short ID = dataInputStream.readShort();

Of course the ID is randomly generated so it’s impossible to get the same one for different requests.

However, the ID MUST be the same as the one sent in the request. The server just uses it to match various requests to the correct responses.

Mind you, readByte(or readInteger or any other read*) moves the cursor in the internal representation of the DataInputStream equal to the amount of bits read. So in this particular case it will move it by 16 bits. Thus, the next readByte will read the first byte(or 8 bits) of the flags. Of course if we were to do readShort again we’d read the whole Flags section.

Following the same logic we can move on to the next section, the Flags section.

This one is a bit more tricky to parse doing readByte will read several flags at once. A little bitshifting must be done.

Lets read the QR

short flags = dataInputStream.readByte();
int QR = (flags & 0b10000000) >>> 7;

Even for folks familiar with Java, bitshifting is not an every day occurrence since most of the heavy lifting is hidden in abstraction.

I’ve used binary to make it as obvious as possible what’s happening. Prefixing a number with 0b allows you to write its binary representation.

In this case 10000000 is 128, so flags & 128 works exactly the same, but it’s less obvious.

Lets break it down a bit more:

dataInputStream.readByte()

will read the first byte in the response, immediately after ID.

Lets assume that the first byte of the Flags section is represented in binary like so

10000001

We can boil down the above code to

(10000001 & 10000000) >>> 7

Doing binary AND (&) on the above results in 10000000 since it takes each bit in order, from left to right, of the first number and ANDs(&) it with the second number, in the same order.

10000001
&&&&&&&&
10000000

Thus, 1&1 == 1. Since true and true == true. The next bit is 0, since false and false == false. The last bit, 1&0 == 0, since true and false == false.

We use & in conjunction with a very specific number so that we maintain truth-iness at the bit we want but remove it in bits we don’t care about.

After &-ing the numbers(10000001 & 10000000) we get 10000000.

Once we have the desired result, we simply shift(or move)the 7th bit, starting from the 0th bit, to the right(>>>) by 7 spaces.

Notice that I’ve used >>>, no this not a mistake, and yes it’s a real thing, it’s called the logical right shift(as opposed to the >>arithmetic shift right) . For those of you coming from other languages this may not make sense.

Java doesn’t have explicit signed numbers. Every number in java is signed by default.

In an arithmetic shift, the sign bit is extended to preserve the signedness of the number.

We really don’t want that(although it shouldn’t be possible to have negative numbers in this case) so we want to remove the sign and make it unsigned by default.

For those of you curios, this stackoverflow question explains it better than I every could.

Lets see how this approach works iteratively

First shift will result in

01000000

Second shift will result in

00100000

And so on and so forth until we shift for a total of 7 times, which results in

00000001

And this is our first flag, which, according to the diagram above is the QR, which specifies if the message is a Query(0) or a Response(1)

The binary number 00000001 is in fact number 1 in base 10(or decimal). So, QR here is a Response.

Now that we know how to read the flags, we must simply follow the same logic for all the other flags

short flags = dataInputStream.readByte();
int QR = (flags & 0b10000000) >>> 7;
int opCode = ( flags & 0b01111000) >>> 3;
int AA = ( flags & 0b00000100) >>> 2;
int TC = ( flags & 0b00000010) >>> 1;
int RD = flags & 0b00000001;
System.out.println("QR "+QR);
System.out.println("Opcode "+opCode);
System.out.println("AA "+AA);
System.out.println("TC "+TC);
System.out.println("RD "+RD);flags = dataInputStream.readByte();
int RA = (flags & 0b10000000) >>> 7;
int Z = ( flags & 0b01110000) >>> 4;
int RCODE = flags & 0b00001111;
System.out.println("RA "+RA);
System.out.println("Z "+ Z);
System.out.println("RCODE " +RCODE);

For example, in my own response I get:

QR 1
Opcode 0
AA 0
TC 0
RD 1
RA 1
Z 0
RCODE 0

Using Wireshark I can intercept the response and check if my code is inline with what Wireshark has.

And in fact that seems to be the case.

Now we can read the rest of the Header section: QDCOUNT, ANCOUNT, NSCOUNT, and ARCOUNT.

This is a simple matter of reading the next short, since each entry is exactly 16 bits

long.QDCOUNT = dataInputStream.readShort();
ANCOUNT = dataInputStream.readShort();
NSCOUNT = dataInputStream.readShort();
ARCOUNT = dataInputStream.readShort();

System.out.println("Questions: " + String.format("%s",QDCOUNT ));
System.out.println("Answers RRs: " + String.format("%s", ANCOUNT));
System.out.println("Authority RRs: " + String.format("%s", NSCOUNT));
System.out.println("Additional RRs: " + String.format("%s", ARCOUNT));

And in my response

Questions: 1
Answers RRs: 2
Authority RRs: 0
Additional RRs: 0

Again, using Wireshark I verify the results.

Now to parse the Question part of the response. 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5

0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                                               |
    /                     QNAME                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QTYPE                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QCLASS                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

This can be easily read simply by following the instructions in the RFC

QNAME — a domain name represented as a sequence of labels, where
 each label consists of a length octet followed by that
 number of octets. The domain name terminates with the
 zero length octet for the null label of the root. Note
 that this field may be an odd number of octets; no
 padding is used.

So we can expect: a specific length — label — 00 len octet to finish the reading.

Simply put coderambling.com would be

12coderambling3com0

12 — total length of the next word

coderambling— the words itself

0 — ends the sequence

String QNAME = "";
int recLen;
while ((recLen = dataInputStream.readByte()) > 0) {
    byte[] record = new byte[recLen];
    for (int i = 0; i < recLen; i++) {
        record[i] = dataInputStream.readByte();
    }
    QNAME = new String(record, StandardCharsets.UTF_8);
}
short QTYPE = dataInputStream.readShort();
short QCLASS = dataInputStream.readShort();
System.out.println("Record: " + QNAME);
System.out.println("Record Type: " + String.format("%s", QTYPE));
System.out.println("Class: " + String.format("%s", QCLASS));

Now comes the interesting part. Reading the Answer section of the response.

Section 4.1.3. of the RFC tells us that

The answer, authority, and additional sections all share the same
format: a variable number of resource records, where the number of
records is specified in the corresponding count field in the header.

So we know via ANCOUNT, NSCOUNT, and ARCOUNT how many entries each section has. And we also know that they’re the same format.

Speaking of the format, here it is(for all sections). Mind you the RDATA format depends on the TYPE, so it will be different between say A records and CNAME records.

0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                                               |
    /                                               /
    /                      NAME                     /
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      TYPE                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     CLASS                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      TTL                      |
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                   RDLENGTH                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
    /                     RDATA                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Section 4.1.4. tells us how to read the NAME entry. But long story short:

  • 2 bits of 11 represents the start of an OFFSET
  • 2 bits of 00 represents an actual label
  • read until it ends with 00

So we must read the first 2 bits and see where we stand.byte firstBytes = dataInputStream.readByte();
int firstTwoBits = (firstBytes & 0b11000000) >>> 6;

Obviously, here we’re assuming that we got an answer from the server. A much better way to do this is to check if either ANCOUNT, NSCOUNT, and ARCOUNT are larger than 0.

But for the purpose of this exercise we know that ANCOUNT is 2.

for(int i = 0; i < ANCOUNT; i++) {
  if(firstTwoBits == 3) { // 00000011 first 2 bits
      // more code here
  }else if(firstTwoBits == 0){
      System.out.println("It's a label");
  }

  firstBytes = dataInputStream.readByte();
  firstTwoBits = (firstBytes & 0b11000000) >>> 6;
}

Read first 2 bits, if 11 then it’s an offset.

And now, from 0 to 100 really fast

byte firstBytes = dataInputStream.readByte();
int firstTwoBits = (firstBytes & 0b11000000) >>> 6;

ByteArrayOutputStream label = new ByteArrayOutputStream();
Map<String, String> domainToIp = new HashMap<>();

for(int i = 0; i < ANCOUNT; i++) {
    if(firstTwoBits == 3) {
        byte currentByte = dataInputStream.readByte();
        boolean stop = false;
        byte[] newArray = Arrays.copyOfRange(response, currentByte, response.length);
        DataInputStream sectionDataInputStream = new DataInputStream(new ByteArrayInputStream(newArray));
        ArrayList<Integer> RDATA = new ArrayList<>();
        ArrayList<String> DOMAINS = new ArrayList<>();
        while(!stop) {
            byte nextByte = sectionDataInputStream.readByte();
            if(nextByte != 0) {
                byte[] currentLabel = new byte[nextByte];
                for(int j = 0; j < nextByte; j++) {
                    currentLabel[j] = sectionDataInputStream.readByte();
                }
                label.write(currentLabel);
            } else {
                stop = true;
                short TYPE = dataInputStream.readShort();
                short CLASS = dataInputStream.readShort();
                int TTL = dataInputStream.readInt();
                int RDLENGTH = dataInputStream.readShort();
                for(int s = 0; s < RDLENGTH; s++) {
                    int nx = dataInputStream.readByte() & 255;
                    RDATA.add(nx);
                }

                System.out.println("Type: " + TYPE);
                System.out.println("Class: " + CLASS);
                System.out.println("Time to live: " + TTL);
                System.out.println("Rd Length: " + RDLENGTH);
            }

            DOMAINS.add(label.toString(StandardCharsets.UTF_8));
            label.reset();
        }

        StringBuilder ip = new StringBuilder();
        StringBuilder domainSb = new StringBuilder();
        for(Integer ipPart:RDATA) {
            ip.append(ipPart).append(".");
        }

        for(String domainPart:DOMAINS) {
            if(!domainPart.equals("")) {
                domainSb.append(domainPart).append(".");
            }
        }
        String domainFinal = domainSb.toString();
        String ipFinal = ip.toString();
        domainToIp.put(ipFinal.substring(0, ipFinal.length()-1), domainFinal.substring(0, domainFinal.length()-1));

    }else if(firstTwoBits == 0){
        System.out.println("It's a label");
    }

    firstBytes = dataInputStream.readByte();
    firstTwoBits = (firstBytes & 0b11000000) >>> 6;
}

domainToIp.forEach((key, value) -> System.out.println(key + " : " + value));

Which results in

Type: 1
Class: 1
Time to live: 291
Rd Length: 4
Type: 1
Class: 1
Time to live: 291
Rd Length: 4
156.154.132.200 : coderambling.com
156.154.133.200 : coderambling.com

But lets take a closer look at what’s happening here since it’s important.

The most important part is the compression of the domain name.

I feel that section 4.1.4. does a much better job at explaining it than I ever could, but the most important takeaway from said section is this diagram

For example, a datagram might need to use the domain names F.ISI.ARPA,
FOO.F.ISI.ARPA, ARPA, and the root.  Ignoring the other fields of the
message, these domain names might be represented as:
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    20 |           1           |           F           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    22 |           3           |           I           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    24 |           S           |           I           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    26 |           4           |           A           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    28 |           R           |           P           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    30 |           A           |           0           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    40 |           3           |           F           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    42 |           O           |           O           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    44 | 1  1|                20                       |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    64 | 1  1|                26                       |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    92 |           0           |                       |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
The domain name for F.ISI.ARPA is shown at offset 20.  The domain name
FOO.F.ISI.ARPA is shown at offset 40; this definition uses a pointer to
concatenate a label for FOO to the previously defined F.ISI.ARPA.  The
domain name ARPA is defined at offset 64 using a pointer to the ARPA
component of the name F.ISI.ARPA at 20; note that this pointer relies on
ARPA being the last label in the string at 20.

In our case the NAME is in fact a pointer to the Question section’s QNAME. Why? Because it’s exactly the same domain, thus there’s no point(hehe) in writing it again. Saving much needed space in the response.

I won’t go thru piece of code there since it’s fairly easy to understand, it just looks a bit intimidating at first. However, some of you may have noticed this little part

for(int s = 0; s < RDLENGTH; s++) {
    int nx = dataInputStream.readByte() & 255;
    RDATA.add(nx);
}

Especially

dataInputStream.readByte() & 255;

This is the RDATA section that we’re reading, specifically for A records.

Each record type has his own specific RDATA format, so it’s important we read it in the correct way.

Section 3.4.1. tells us that for A records the RDATA section looks like this

    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ADDRESS                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

where:

ADDRESS         A 32 bit Internet address.

This is pretty straight forward.

However, an IP address(a IPv4 that is) is in fact a series of 4 bytes separated by a dot. Unfortunately java doesn’t have signed numbers(well…all integer types are signed(except char), it doesn’t have explicitly declared signed integers), so for example a 255.255.255.255 ip address will overflow into the negative(ain’t that fun) since java bytes hold numbers with minimum value of -128 and a maximum value of 127 (inclusive).

Thus we have to “underflow” it back to the correct number. Using a bitwise operation(&) with 11111111 will give it the correct value. If it’s already a correct value, it will leave it as is.

The other sections NSCOUNT and ARCOUNT are read exactly the same as ANCOUNT since they’re exactly the same format.

Of course you have to keep in mind that the RDATA is TYPE specific.

Here’s the response in wireshark


And the complete code, including the request as well as the response

import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class Main {
    private static final int DNS_SERVER_PORT = 53;
    public static void main(String[] args) throws IOException {
            InetAddress ipAddress = InetAddress.getByName("1.1.1.1");
            Random random = new Random();
            short ID = (short)random.nextInt(32767);
            System.out.println(ID);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
            short requestFlags = Short.parseShort("0000000100000000", 2);
            ByteBuffer byteBuffer = ByteBuffer.allocate(2).putShort(requestFlags);
            byte[] flagsByteArray = byteBuffer.array();
            short QDCOUNT = 1;
            short ANCOUNT = 0;
            short NSCOUNT = 0;
            short ARCOUNT = 0;
            dataOutputStream.writeShort(ID);
            dataOutputStream.write(flagsByteArray);
            dataOutputStream.writeShort(QDCOUNT);
            dataOutputStream.writeShort(ANCOUNT);
            dataOutputStream.writeShort(NSCOUNT);
            dataOutputStream.writeShort(ARCOUNT);
            String domain = "coderambling.com";
            String[] domainParts = domain.split("\\.");
            for (int i = 0; i < domainParts.length; i++) {
                byte[] domainBytes = domainParts[i].getBytes(StandardCharsets.UTF_8);
                dataOutputStream.writeByte(domainBytes.length);
                dataOutputStream.write(domainBytes);
            }
            // No more parts
            dataOutputStream.writeByte(0);
            // Type 0x01 = A (Host Request)
            dataOutputStream.writeShort(1);
            // Class 0x01 = IN
            dataOutputStream.writeShort(1);
            byte[] dnsFrame = byteArrayOutputStream.toByteArray();
            System.out.println("SendataInputStreamg: " + dnsFrame.length + " bytes");
            for (int i = 0; i < dnsFrame.length; i++) {
                System.out.print(String.format("%s", dnsFrame[i]) + " ");
            }
            DatagramSocket socket = new DatagramSocket();
            DatagramPacket dnsReqPacket = new DatagramPacket(dnsFrame, dnsFrame.length, ipAddress, DNS_SERVER_PORT);
            socket.send(dnsReqPacket);
            byte[] response = new byte[1024];
            DatagramPacket packet = new DatagramPacket(response, response.length);
            socket.receive(packet);
            System.out.println("\n\nReceived: " + packet.getLength() + " bytes");
            for (int i = 0; i < packet.getLength(); i++) {
                System.out.print(String.format("%s", response[i]) + " ");
            }
            System.out.println("\n");
            DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(response));
            System.out.println("\n\nStart response decode");
            System.out.println("Transaction ID: " + dataInputStream.readShort()); // ID
            short flags = dataInputStream.readByte();
            int QR = (flags & 0b10000000) >>> 7;
            int opCode = ( flags & 0b01111000) >>> 3;
            int AA = ( flags & 0b00000100) >>> 2;
            int TC = ( flags & 0b00000010) >>> 1;
            int RD = flags & 0b00000001;
            System.out.println("QR "+QR);
            System.out.println("Opcode "+opCode);
            System.out.println("AA "+AA);
            System.out.println("TC "+TC);
            System.out.println("RD "+RD);
            flags = dataInputStream.readByte();
            int RA = (flags & 0b10000000) >>> 7;
            int Z = ( flags & 0b01110000) >>> 4;
            int RCODE = flags & 0b00001111;
            System.out.println("RA "+RA);
            System.out.println("Z "+ Z);
            System.out.println("RCODE " +RCODE);
            QDCOUNT = dataInputStream.readShort();
            ANCOUNT = dataInputStream.readShort();
            NSCOUNT = dataInputStream.readShort();
            ARCOUNT = dataInputStream.readShort();
            System.out.println("Questions: " + String.format("%s",QDCOUNT ));
            System.out.println("Answers RRs: " + String.format("%s", ANCOUNT));
            System.out.println("Authority RRs: " + String.format("%s", NSCOUNT));
            System.out.println("Additional RRs: " + String.format("%s", ARCOUNT));
            String QNAME = "";
            int recLen;
            while ((recLen = dataInputStream.readByte()) > 0) {
                byte[] record = new byte[recLen];
                for (int i = 0; i < recLen; i++) {
                    record[i] = dataInputStream.readByte();
                }
                QNAME = new String(record, StandardCharsets.UTF_8);
            }
            short QTYPE = dataInputStream.readShort();
            short QCLASS = dataInputStream.readShort();
            System.out.println("Record: " + QNAME);
            System.out.println("Record Type: " + String.format("%s", QTYPE));
            System.out.println("Class: " + String.format("%s", QCLASS));
            System.out.println("\n\nstart answer, authority, and additional sections\n");
            byte firstBytes = dataInputStream.readByte();
            int firstTwoBits = (firstBytes & 0b11000000) >>> 6;
            ByteArrayOutputStream label = new ByteArrayOutputStream();
            Map<String, String> domainToIp = new HashMap<>();
            for(int i = 0; i < ANCOUNT; i++) {
                if(firstTwoBits == 3) {
                    byte currentByte = dataInputStream.readByte();
                    boolean stop = false;
                    byte[] newArray = Arrays.copyOfRange(response, currentByte, response.length);
                    DataInputStream sectionDataInputStream = new DataInputStream(new ByteArrayInputStream(newArray));
                    List<Integer> RDATA = new ArrayList<>();
                    List<String> DOMAINS = new ArrayList<>();
                    while(!stop) {
                        byte nextByte = sectionDataInputStream.readByte();
                        if(nextByte != 0) {
                            byte[] currentLabel = new byte[nextByte];
                            for(int j = 0; j < nextByte; j++) {
                                currentLabel[j] = sectionDataInputStream.readByte();
                            }
                            label.write(currentLabel);
                        } else {
                            stop = true;
                            short TYPE = dataInputStream.readShort();
                            short CLASS = dataInputStream.readShort();
                            int TTL = dataInputStream.readInt();
                            int RDLENGTH = dataInputStream.readShort();
                            for(int s = 0; s < RDLENGTH; s++) {
                                int nx = dataInputStream.readByte() & 255;// and with 255 to
                                RDATA.add(nx);
                            }
                            System.out.println("Type: " + TYPE);
                            System.out.println("Class: " + CLASS);
                            System.out.println("Time to live: " + TTL);
                            System.out.println("Rd Length: " + RDLENGTH);
                        }
                        DOMAINS.add(label.toString(StandardCharsets.UTF_8));
                        label.reset();
                    }
                    StringBuilder ip = new StringBuilder();
                    StringBuilder domainSb = new StringBuilder();
                    for(Integer ipPart:RDATA) {
                        ip.append(ipPart).append(".");
                    }
                    for(String domainPart:DOMAINS) {
                        if(!domainPart.equals("")) {
                            domainSb.append(domainPart).append(".");
                        }
                    }
                    String domainFinal = domainSb.toString();
                    String ipFinal = ip.toString();
                    domainToIp.put(ipFinal.substring(0, ipFinal.length()-1), domainFinal.substring(0, domainFinal.length()-1));
                }else if(firstTwoBits == 0){
                    System.out.println("It's a label");
                }
                firstBytes = dataInputStream.readByte();
                firstTwoBits = (firstBytes & 0b11000000) >>> 6;
            }
            domainToIp.forEach((key, value) -> System.out.println(key + " : " + value));
        }
    }

If you think this looks insanely ugly, I agree with you.

I actually have planned to upload a much cleaner version to my GitHub. In the meantime, you can probably use dnsjava if for whatever reason you’d like a java dns.


Posted

in

by

Tags: