|
1 | 1 | package layers |
2 | 2 |
|
3 | | -// https://en.wikipedia.org/wiki/Domain_Name_System |
4 | | -// port 53 |
5 | | -type DNSMessage struct{} |
| 3 | +import ( |
| 4 | + "encoding/binary" |
| 5 | + "fmt" |
| 6 | +) |
| 7 | + |
| 8 | +const dnsHeaderSize = 12 |
| 9 | + |
| 10 | +type DNSMessage struct { |
| 11 | + TransactionID uint16 // Used for matching response to queries. |
| 12 | + Flags uint16 // Flags specify the requested operation and a response code. |
| 13 | + Questions uint16 // Count of entries in the queries section. |
| 14 | + AnswerRRs uint16 // Count of entries in the answers section. |
| 15 | + AuthorityRRs uint16 // Count of entries in the authority section. |
| 16 | + AdditionalRRs uint16 // Count of entries in the additional section. |
| 17 | + payload []byte |
| 18 | +} |
6 | 19 |
|
7 | 20 | func (d *DNSMessage) String() string { |
8 | | - return "" |
| 21 | + return fmt.Sprintf(`DNS Message: |
| 22 | +- Transaction ID: %#04x |
| 23 | +- Flags: %#04x |
| 24 | +%s |
| 25 | +- Questions: %d |
| 26 | +- Answer RRs: %d |
| 27 | +- Authority RRs: %d |
| 28 | +- Additional RRs: %d |
| 29 | +- Payload: %d bytes |
| 30 | +`, |
| 31 | + d.TransactionID, |
| 32 | + d.Flags, |
| 33 | + d.flags(), |
| 34 | + d.Questions, |
| 35 | + d.AnswerRRs, |
| 36 | + d.AuthorityRRs, |
| 37 | + d.AdditionalRRs, |
| 38 | + len(d.payload), |
| 39 | + ) |
9 | 40 | } |
10 | 41 |
|
| 42 | +// Parse parses the given byte data into a DNSMessage struct. |
11 | 43 | func (d *DNSMessage) Parse(data []byte) error { |
| 44 | + if len(data) < dnsHeaderSize { |
| 45 | + return fmt.Errorf("minimum header size for DNS is %d bytes, got %d bytes", dnsHeaderSize, len(data)) |
| 46 | + } |
| 47 | + d.TransactionID = binary.BigEndian.Uint16(data[0:2]) |
| 48 | + d.Flags = binary.BigEndian.Uint16(data[2:4]) |
| 49 | + d.Questions = binary.BigEndian.Uint16(data[4:6]) |
| 50 | + d.AnswerRRs = binary.BigEndian.Uint16(data[6:8]) |
| 51 | + d.AuthorityRRs = binary.BigEndian.Uint16(data[8:10]) |
| 52 | + d.AdditionalRRs = binary.BigEndian.Uint16(data[10:dnsHeaderSize]) |
| 53 | + d.payload = data[dnsHeaderSize:] |
12 | 54 | return nil |
13 | 55 | } |
14 | 56 |
|
15 | 57 | func (d *DNSMessage) NextLayer() (string, []byte) { |
16 | 58 | return "", nil |
17 | 59 | } |
| 60 | + |
| 61 | +func (d *DNSMessage) flags() string { |
| 62 | + // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml |
| 63 | + var flags string |
| 64 | + opcode := (d.Flags >> 11) & 15 |
| 65 | + var opcodes string |
| 66 | + switch opcode { |
| 67 | + case 0: |
| 68 | + opcodes = "Standard query" |
| 69 | + case 1: |
| 70 | + opcodes = "Inverse query" |
| 71 | + case 2: |
| 72 | + opcodes = "Server status request" |
| 73 | + case 4: |
| 74 | + opcodes = "Notify" |
| 75 | + case 5: |
| 76 | + opcodes = "Update" |
| 77 | + case 6: |
| 78 | + opcodes = "Stateful operation" |
| 79 | + default: |
| 80 | + opcodes = "Unknown" |
| 81 | + } |
| 82 | + qr := (d.Flags >> 15) & 1 |
| 83 | + var qrs string |
| 84 | + switch qr { |
| 85 | + case 0: |
| 86 | + qrs = "query" |
| 87 | + tc := (d.Flags >> 9) & 1 |
| 88 | + rd := (d.Flags >> 8) & 1 |
| 89 | + z := (d.Flags >> 6) & 1 |
| 90 | + na := (d.Flags >> 4) & 1 |
| 91 | + flags = fmt.Sprintf(` - Response: Message is a %s (%d) |
| 92 | + - Opcode: %s (%d) |
| 93 | + - Truncated: %d |
| 94 | + - Recursion desired: %d |
| 95 | + - Reserved: %d |
| 96 | + - Non-authenticated data: %d`, qrs, qr, opcodes, opcode, tc, rd, z, na) |
| 97 | + case 1: |
| 98 | + qrs = "reply" |
| 99 | + a := (d.Flags >> 10) & 1 |
| 100 | + tc := (d.Flags >> 9) & 1 |
| 101 | + rd := (d.Flags >> 8) & 1 |
| 102 | + ra := (d.Flags >> 7) & 1 |
| 103 | + z := (d.Flags >> 6) & 1 |
| 104 | + aa := (d.Flags >> 5) & 1 |
| 105 | + na := (d.Flags >> 4) & 1 |
| 106 | + rcode := d.Flags & 15 |
| 107 | + var rcodes string |
| 108 | + switch rcode { |
| 109 | + case 0: |
| 110 | + rcodes = "No error" |
| 111 | + case 1: |
| 112 | + rcodes = "Format error" |
| 113 | + case 2: |
| 114 | + rcodes = "Server failed to complete the DNS request" |
| 115 | + case 3: |
| 116 | + rcodes = "Domain name does not exist" |
| 117 | + case 4: |
| 118 | + rcodes = "Function not implemented" |
| 119 | + case 5: |
| 120 | + rcodes = "The server refused to answer for the query" |
| 121 | + case 6: |
| 122 | + rcodes = "Name that should not exist, does exist" |
| 123 | + case 7: |
| 124 | + rcodes = "RRset that should not exist, does exist" |
| 125 | + case 8: |
| 126 | + rcodes = "Server not authoritative for the zone" |
| 127 | + case 9: |
| 128 | + rcodes = "Name not in zone" |
| 129 | + default: |
| 130 | + rcodes = "Unknown" |
| 131 | + } |
| 132 | + flags = fmt.Sprintf(` - Response: Message is a %s (%d) |
| 133 | + - Opcode: %s (%d) |
| 134 | + - Authoritative: %d |
| 135 | + - Truncated: %d |
| 136 | + - Recursion desired: %d |
| 137 | + - Recursion available: %d |
| 138 | + - Reserved: %d |
| 139 | + - Answer authenticated: %d |
| 140 | + - Non-authenticated data: %d |
| 141 | + - Reply code: %s (%d)`, qrs, qr, opcodes, opcode, a, tc, rd, ra, z, aa, na, rcodes, rcode) |
| 142 | + } |
| 143 | + return flags |
| 144 | +} |
0 commit comments