Skip to content

Commit

Permalink
Update: Project architecture change
Browse files Browse the repository at this point in the history
Encoding DNS Message
Making request to name server
Parsing Header and Question section

Answer and additional sections parsing yet to include
  • Loading branch information
s-bose7 committed Jul 3, 2024
1 parent e0f06d1 commit ff883b8
Show file tree
Hide file tree
Showing 10 changed files with 376 additions and 198 deletions.
28 changes: 28 additions & 0 deletions core/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package core

import "net"

const MAX_PACKET_SIZE = 512

func QueryNameServer(server string, query []byte) ([]byte, error) {
conn, err := net.Dial("udp", server)
if err != nil {
return nil, err
}
// Ensures that the connection is closed when the function completes,
// whether it succeeds or fails.
defer conn.Close()

_, err = conn.Write(query)
if err != nil {
return nil, err
}
// Buffer to store the response
response := make([]byte, MAX_PACKET_SIZE)
n, err := conn.Read(response)
if err != nil {
return nil, err
}

return response[:n], nil
}
22 changes: 22 additions & 0 deletions dns/dns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package dns

import (
. "resolve-on-go/lib"
"resolve-on-go/core"
)

func BuildMessage(domain string) ([]byte, error) {
message := DNSMessage{
Header: NewDNSHeader(), Question: NewDNSQuestion(domain),
}
encodedMessage, err := message.Encode()
if err != nil {
return nil, err
}
return encodedMessage, nil
}


func Query(server string, query []byte) ([]byte, error){
return core.QueryNameServer(server, query)
}
83 changes: 83 additions & 0 deletions lib/header.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package lib

import (
"bytes"
"encoding/binary"
)

const (
// Header Constants
QR_QUERY = 0
QR_RESPONSE = 1
OPCODE_QUERY = 0 // Standard query
OPCODE_IQUERY = 1 // Inverse query
OPCODE_STATUS = 2 // Server status request
RD = 1 // Recusion Desired
AA = 0 // Authoritative Answer
TC = 0 // Trucation
RA = 0 // Recursion Available
Z = 0 // Reserved for future use. Must be zero in all queries and responses.

// Header response code types
RCODE_NO_ERROR = 0
RCODE_FORMAT_ERROR = 1
RCODE_SERVER_FAILURE = 2
RCODE_NAME_ERROR = 3
RCODE_NOT_IMPLEMENTED = 4
RCODE_REFUSED = 5
)

type DNSHeader struct {
Id uint16
Flags uint16
QdCount uint16 // The number of entries in the question section.
AnCount uint16 // The number of resource records in the answer section.
NsCount uint16 // The number of name server resource records in the authority section.
ArCount uint16 // The number of resource records in the additional section.
}

func (h *DNSHeader) Encode() ([]byte, error) {

encodedHeader := new(bytes.Buffer)

binary.Write(encodedHeader, binary.BigEndian, h.Id)
binary.Write(encodedHeader, binary.BigEndian, h.Flags)
binary.Write(encodedHeader, binary.BigEndian, h.QdCount)
binary.Write(encodedHeader, binary.BigEndian, h.AnCount)
binary.Write(encodedHeader, binary.BigEndian, h.NsCount)
binary.Write(encodedHeader, binary.BigEndian, h.ArCount)

return encodedHeader.Bytes(), nil
}

func DecodeHeader(data []byte) DNSHeader {
var header DNSHeader
reader := bytes.NewReader(data)

binary.Read(reader, binary.BigEndian, &header.Id)
binary.Read(reader, binary.BigEndian, &header.Flags)
binary.Read(reader, binary.BigEndian, &header.QdCount)
binary.Read(reader, binary.BigEndian, &header.AnCount)
binary.Read(reader, binary.BigEndian, &header.NsCount)
binary.Read(reader, binary.BigEndian, &header.ArCount)

return header
}


func (h *DNSHeader) setFlags(qr, opcode, rd uint16) {
h.Flags = (qr << 15) | (opcode << 11) | (rd << 8)
}


func NewDNSHeader() DNSHeader {
header := DNSHeader{
Id: 1209,
QdCount: 1,
AnCount: 0,
NsCount: 0,
ArCount: 0,
}
header.setFlags(QR_QUERY, OPCODE_QUERY, RD)
return header
}
83 changes: 31 additions & 52 deletions lib/message.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,5 @@
package lib

const (
// Header Constants
QR_QUERY = 0
QR_RESPONSE = 1
OPCODE_QUERY = 0 // Standard query
OPCODE_IQUERY = 1 // Inverse query
OPCODE_STATUS = 2 // Server status request
RD = 1 // Recusion Desired
AA = 0 // Authoritative Answer
TC = 0 // Trucation
RA = 0 // Recursion Available
Z = 0 // Reserved for future use. Must be zero in all queries and responses.

// Header response code types
RCODE_NO_ERROR = 0
RCODE_FORMAT_ERROR = 1
RCODE_SERVER_FAILURE = 2
RCODE_NAME_ERROR = 3
RCODE_NOT_IMPLEMENTED = 4
RCODE_REFUSED = 5
)

type DNSHeader struct {
ID uint16
Flags uint16
QDCount uint16 // The number of entries in the question section.
ANCount uint16 // The number of resource records in the answer section.
NSCount uint16 // The number of name server resource records in the authority section.
ARCount uint16 // The number of resource records in the additional section.
}

type DNSQuestion struct {
QName []byte // A domain name represented as a sequence of labels.
QType uint16 // The type of the query
QClass uint16 // The class of the query.
}

type DNSResourceRecord struct {
Name []byte // A domain name to which this resource record pertains.
Expand All @@ -47,22 +11,6 @@ type DNSResourceRecord struct {
RData []byte // A a variable length string of octets that describes the resource.
}

/*
DNS Query Format
+---------------------+
| Header | Always present
+---------------------+
| Question | The question for the name server
+---------------------+
| Answer | RRs answering the question
+---------------------+
| Authority | RRs pointing toward an authority
+---------------------+
| Additional | RRs holding additional information
+---------------------+
*/

type DNSMessage struct {
Header DNSHeader
Expand All @@ -71,3 +19,34 @@ type DNSMessage struct {
Authority []DNSResourceRecord
Additional []DNSResourceRecord
}


func (m *DNSMessage) Encode() ([]byte, error) {
header, err := m.Header.Encode()
if err != nil {
return nil, err
}

question, err := m.Question.Encode()
if err != nil {
return nil, err
}

message := append(header, question...)
return message, nil
}

func DecodeResponse(response []byte) (DNSMessage, error) {
header := DecodeHeader(response[:12])
question, err := DecodeQuestion(response[12:34])
if err != nil {
return DNSMessage{}, err
}

dnsMessageResponse := DNSMessage {
Header: header,
Question: question,
}

return dnsMessageResponse, nil
}
72 changes: 72 additions & 0 deletions lib/question.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package lib

import (
"bytes"
"encoding/binary"
"resolve-on-go/util"
)


type DNSQuestion struct {
Name []byte // A domain name represented as a sequence of labels.
Type uint16 // The type of the query
Class uint16 // The class of the query.
}


func (q *DNSQuestion) Encode() ([]byte, error) {
buf := new(bytes.Buffer)

buf.Write(q.Name)
binary.Write(buf, binary.BigEndian, q.Type)
binary.Write(buf, binary.BigEndian, q.Class)

return buf.Bytes(), nil
}

func DecodeQuestion(data []byte) (DNSQuestion, error) {
var question DNSQuestion
reader := bytes.NewReader(data)

// Decode the domain name
var name []byte
for {
length, err := reader.ReadByte()
if err != nil {
return question, err
}
if length == 0 {
break
}

label := make([]byte, length)
if _, err := reader.Read(label); err != nil {
return question, err
}
name = append(name, length)
name = append(name, label...)
}
name = append(name, 0) // Append the null byte at the end of the name
question.Name = name

// Decode the type
if err := binary.Read(reader, binary.BigEndian, &question.Type); err != nil {
return question, err
}
// Decode the class
if err := binary.Read(reader, binary.BigEndian, &question.Class); err != nil {
return question, err
}

return question, nil
}


func NewDNSQuestion(domain string) DNSQuestion {
encodedDomain := util.EncodeString(domain)
return DNSQuestion{
Name: encodedDomain,
Type: 1, // IPv4
Class: 1,
}
}
81 changes: 81 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package main

/*
Copyright (c) 2024 s-bose7
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

import (
"fmt"
"resolve-on-go/util"
"resolve-on-go/dns"
"resolve-on-go/lib"
)

func printHeader(dnsHeader lib.DNSHeader) {
fmt.Print("\n")
fmt.Printf("Id: %d\n", dnsHeader.Id)
fmt.Printf("Flag: %d\n", dnsHeader.Flags)
fmt.Printf("QdCount: %d\n", dnsHeader.QdCount)
fmt.Printf("AnCount: %d\n", dnsHeader.AnCount)
fmt.Printf("NsCount: %d\n", dnsHeader.NsCount)
fmt.Printf("ArCount: %d\n", dnsHeader.ArCount)
}

func printQuestion(dnsQuestion lib.DNSQuestion) {
fmt.Print("\n")
name, err := util.DecodeString(dnsQuestion.Name)
if err != nil {
return
}
fmt.Println("Name:", name)
fmt.Printf("Type: %d\n", dnsQuestion.Type)
fmt.Printf("Class: %d\n", dnsQuestion.Class)
}

func main() {

domain := "dns.google.com"

query, err := dns.BuildMessage(domain)
if err != nil {
util.PrintStackTrace("Error building query: ", err)
return
}
util.PrintBytes("Encoded DNS Message:", query)

server := "8.8.8.8:53"
response, err := dns.Query(server, query)
if err != nil {
util.PrintStackTrace("Error Querying Google's DNS server: ", err)
return
}
util.PrintBytes("\nGoogle's DNS server response:", response)

resolvedMessage, err := lib.DecodeResponse(response)
if err != nil {
util.PrintStackTrace("Error parsing response: ", err)
return
}

printHeader(resolvedMessage.Header)
printQuestion(resolvedMessage.Question)
}

Loading

0 comments on commit ff883b8

Please sign in to comment.