Skip to content

Instantly share code, notes, and snippets.

@amitavaghosh1
Created February 3, 2022 21:01
Show Gist options
  • Save amitavaghosh1/6d4b93d1f28eb7ae3c0a53b2ffd5aba1 to your computer and use it in GitHub Desktop.
Save amitavaghosh1/6d4b93d1f28eb7ae3c0a53b2ffd5aba1 to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
)
func penik(err interface{}) {
panic(err)
}
type H func(b []byte, groupLen int) uint16
type DnsHeader struct {
ID uint16
QrCode uint // 4
OpCode uint
Answer uint
Truncation uint
RecursionD uint
RecursionA uint
Z uint //3
Rcode uint //4
QDCount uint16
ANCount uint16
NSCount uint16
ARCount uint16
}
type QuestionFormat struct {
QNames []uint16
QType uint16
QClass uint16
}
type AnswerFormat struct {
Names []uint16
RData []uint16
Type uint16
Class uint16
TTL uint16
RDLength uint16
}
type DnsMessage struct {
Header DnsHeader
Question QuestionFormat
Answer AnswerFormat
}
type BitReader struct {
value []byte
ptr int
}
func (br *BitReader) Read16() []byte {
res := []byte{}
for i := 0; i < 4; i++ {
res = append(res, br.value[br.ptr])
br.ptr += 1
}
return res
}
func (br *BitReader) Read8() []byte {
res := []byte{}
for i := 0; i < 2; i++ {
res = append(res, br.value[br.ptr])
br.ptr += 1
}
return res
}
func hexer(hexMap map[rune]uint16) H {
return func(b []byte, groupLen int) uint16 {
b = bytes.ToUpper(b)
var res uint16 = 0
for i, r := range b {
shiftLen := groupLen - (4 * i)
// fmt.Println(shiftLen, rune(r), string(r))
res = (res << shiftLen) | hexMap[rune(r)]
}
return res
}
}
func getHexMap() map[rune]uint16 {
hmap := map[rune]uint16{
'A': 0xA,
'B': 0xB,
'C': 0xC,
'D': 0xD,
'E': 0xE,
'F': 0xF,
}
for i := 0; i < 10; i++ {
hmap[rune('0'+i)] = uint16(i)
}
return hmap
}
func ParseHeader(br *BitReader, toBytes H) DnsHeader {
header := DnsHeader{
ID: toBytes(br.Read16(), 16),
}
nextBits := toBytes(br.Read8(), 8)
header.QrCode = uint(nextBits >> 7) // 1st bit
header.OpCode = uint((nextBits >> 3) & maskOne) // discard last 3 and 0 out Qr
restOfHeader2 := nextBits << 5
header.Answer = uint(restOfHeader2 & maskOne)
header.Truncation = uint((restOfHeader2 << 1) & maskOne)
header.RecursionD = uint((restOfHeader2 << 2) & maskOne)
nextBits = toBytes(br.Read8(), 8)
header.RecursionA = uint(nextBits >> 7)
header.Z = uint((nextBits << 1) & 0b11100000)
header.Rcode = uint((nextBits << 4) & 0b11110000)
header.QDCount = toBytes(br.Read16(), 16)
header.ANCount = toBytes(br.Read16(), 16)
header.NSCount = toBytes(br.Read16(), 16)
header.ARCount = toBytes(br.Read16(), 16)
return header
}
func ParseQuestions(br *BitReader, qLen int, toBytes H) QuestionFormat {
question := QuestionFormat{}
for i := 0; i < qLen; i++ {
question.QNames = append(question.QNames, toBytes(br.Read16(), 16))
}
question.QType = toBytes(br.Read16(), 16)
question.QClass = toBytes(br.Read16(), 16)
return question
}
func ParseAnswer(br *BitReader, aLen int, toBytes H) AnswerFormat {
answer := AnswerFormat{}
for i := 0; i < aLen; i++ {
answer.Names = append(answer.Names, toBytes(br.Read16(), 16))
}
answer.Type = toBytes(br.Read16(), 16)
answer.Class = toBytes(br.Read16(), 16)
// ttl and others
return answer
}
const (
maskOne = 0b10000000
)
func main() {
fileName := os.Args[1]
byts, err := ioutil.ReadFile(fileName)
if err != nil {
penik(err)
}
toBytes := hexer(getHexMap())
dns := &DnsMessage{}
brr := &BitReader{value: byts, ptr: 0}
dns.Header = ParseHeader(brr, toBytes)
dns.Question = ParseQuestions(brr, int(dns.Header.QDCount), toBytes)
fmt.Printf("%+v\n", dns)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment