Add question parser

This commit is contained in:
Martin 2022-01-23 18:29:13 +01:00
commit d80a398e67
Signed by: mawalu
GPG Key ID: BF556F989760A7C8
4 changed files with 138 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
server
.idea

96
lib/dns.nim Normal file
View File

@ -0,0 +1,96 @@
from std/strutils import join
import utils
type
Opcode* = enum
QUERY = 0, IQUERY = 1, STATUS = 2
type
Rcode* = enum
NO_ERROR = 0, FORMAT_ERROR = 1, SERVER_FAULURE = 2, NAME_ERROR = 3,
NOT_IMPLEMENTED = 4, REFUSED = 5
type
DnsHeader* = object
id*: uint16
qr*: bool
opcode*: Opcode
aa*: bool
tc*: bool
rd*: bool
ra*: bool
z*: uint8
rcode*: Rcode
qdcount*: uint16
ancount*: uint16
nscount*: uint16
arcount*: uint16
proc parseHeader*(data: string): DnsHeader =
assert len(data) >= 12
return DnsHeader(
id: toUInt16(data[1], data[0]),
qr: sliceBit(data[2], 0),
opcode: Opcode((toUint8(data[2]) shr 3) and 0b00001111),
aa: sliceBit(data[2], 5),
tc: sliceBit(data[2], 6),
rd: sliceBit(data[2], 7),
ra: sliceBit(data[3], 0),
z: (toUint8(data[3]) shr 4) and 0b00000111,
rcode: Rcode(toUint8(data[3]) and 0b00001111),
qdcount: toUint16(data[5], data[4]),
ancount: toUint16(data[7], data[6]),
nscount: toUint16(data[9], data[8]),
arcount: toUint16(data[11], data[10])
)
type
DnsType* = enum
A = 1, NS = 2, MD =3, MF =4, CNAME = 5, SOA = 6, MB = 7, MG = 8,
MR = 9, NULL = 10, WKS = 11, PTR = 12, HINFO = 13, MINFO = 14, MX = 15,
TXT = 16, AXFR = 252, MAILB = 253, MAILA = 254, ANY = 255
type
DnsClass* = enum
IN = 1, CS = 2, CH = 3, HS = 4
type
DnsQuestion* = object
qname*: string
qtype*: DnsType
qclass*: DnsClass
proc parseQuestion*(data: string): (DnsQuestion, uint16) =
var qname: seq[string] = @[]
var len = toUint8(data[0])
var offset: uint16 = 1
while len > 0:
qname.add(data[offset .. offset + len - 1])
offset += len + 1
len = toUint8(data[offset - 1])
return (DnsQuestion(
qname: qname.join("."),
qtype: DnsType(toUint16(data[offset + 1], data[offset])),
qclass: DnsClass(toUint16(data[offset + 3], data[offset + 2]))
), offset + 4)
type
DnsRecord* = object
name*: string
rtype*: DnsType
class*: DnsClass
ttl*: uint32
rdlength*: uint16
rdata: string
type
DnsMessage* = object
header*: DnsHeader
questions*: seq[DnsQuestion]
answer*: seq[DnsRecord]
authroity*: seq[DnsRecord]
additional*: seq[DnsRecord]

9
lib/utils.nim Normal file
View File

@ -0,0 +1,9 @@
proc toUint8*(l: char): uint8 =
return ord(l).uint8
proc toUint16*(l: char, h: char): uint16 =
return ord(l).uint16 or (ord(h).uint16 shl 8);
proc sliceBit*(s: char, i: uint8): bool =
assert i < 8
return ((toUint8(s) shr (8 - i)) and 1) == 1

31
server.nim Normal file
View File

@ -0,0 +1,31 @@
import asyncnet, asyncdispatch, nativesockets, strutils, lib/dns
proc handleDnsRequest(data: string) =
let header = parseHeader(data[0 .. 11])
var questions: seq[DnsQuestion] = @[]
var offset = 12
for i in (1.uint32)..header.qdcount:
let (question, read) = parseQuestion(data[offset .. len(data) - 1])
questions.add(question)
offset += read.int
let msg = DnsMessage(header: header, questions: questions)
echo msg
proc serve() {.async.} =
let server = newAsyncSocket(sockType=SockType.SOCK_DGRAM, protocol=Protocol.IPPROTO_UDP, buffered = false)
server.setSockOpt(OptReuseAddr, true)
server.bindAddr(Port(12345))
while true:
echo "start loop"
let request = await server.recvFrom(size=512)
echo "received"
handleDnsRequest(request.data)
proc main() =
asyncCheck serve()
runForever()
main()