144 lines
2.2 KiB
Go
144 lines
2.2 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"bytes"
|
||
|
"encoding/binary"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"net"
|
||
|
"os"
|
||
|
"strings"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
ARecord = 0x0001
|
||
|
INClass = 0x0001
|
||
|
)
|
||
|
|
||
|
const DNSPort = 53
|
||
|
const Timeout = 2
|
||
|
|
||
|
type dnsConn struct {
|
||
|
net.Conn
|
||
|
}
|
||
|
|
||
|
type header struct {
|
||
|
Id, Flags uint16
|
||
|
Qdcount uint16
|
||
|
Ancount uint16
|
||
|
Nscount uint16
|
||
|
Arcount uint16
|
||
|
}
|
||
|
|
||
|
type answer struct {
|
||
|
Ntype byte
|
||
|
Ptr uint16
|
||
|
Qtype uint16
|
||
|
Class uint16
|
||
|
Ttl uint32
|
||
|
Addrlen uint16
|
||
|
Addr uint32
|
||
|
}
|
||
|
|
||
|
func findNsAddr() (string, error) {
|
||
|
conf := []string{
|
||
|
"/etc/resolv.conf",
|
||
|
}
|
||
|
|
||
|
for _, i := range conf {
|
||
|
f, err := os.Open(i)
|
||
|
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
defer f.Close()
|
||
|
scanner := bufio.NewScanner(f)
|
||
|
|
||
|
for scanner.Scan() {
|
||
|
s := scanner.Text()
|
||
|
|
||
|
if s[0] == '#' {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
args := strings.Split(s, " ")
|
||
|
if args[0] == "nameserver" && len(args) > 1 {
|
||
|
return args[1], nil
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return "", errors.New("nameserver not found")
|
||
|
}
|
||
|
|
||
|
func read(buf *bytes.Buffer, data any) error {
|
||
|
return binary.Read(buf, binary.BigEndian, data)
|
||
|
}
|
||
|
|
||
|
func write(buf *bytes.Buffer, data any) error {
|
||
|
return binary.Write(buf, binary.BigEndian, data)
|
||
|
}
|
||
|
|
||
|
func writeName(buf *bytes.Buffer, name string) (n int) {
|
||
|
for _, i := range strings.Split(name, ".") {
|
||
|
buf.WriteByte(byte(len(i)))
|
||
|
n++
|
||
|
buf.WriteString(i)
|
||
|
n += len(i)
|
||
|
}
|
||
|
|
||
|
buf.WriteByte(0)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func dial(server string) (dnsConn, error) {
|
||
|
c, err := net.Dial("udp", fmt.Sprintf("%s:%d", server, DNSPort))
|
||
|
|
||
|
if err == nil {
|
||
|
c.SetDeadline(time.Now().Add(Timeout * time.Second))
|
||
|
}
|
||
|
|
||
|
return dnsConn{c}, err
|
||
|
}
|
||
|
|
||
|
func (c dnsConn) sendQuery(name string) (answer, error) {
|
||
|
buf := &bytes.Buffer{}
|
||
|
ah := &header{}
|
||
|
a := &answer{}
|
||
|
|
||
|
h := header{
|
||
|
0x4c3f,
|
||
|
1 << 8, // recursion
|
||
|
0x0001, // single question
|
||
|
0x0000,
|
||
|
0x0000,
|
||
|
0x0000,
|
||
|
}
|
||
|
|
||
|
write(buf, h)
|
||
|
nc := writeName(buf, name)
|
||
|
write(buf, []uint16{ARecord, INClass})
|
||
|
if _, err := c.Write(buf.Bytes()); err != nil {
|
||
|
return *a, err
|
||
|
}
|
||
|
|
||
|
b := make([]byte, 64)
|
||
|
if _, err := c.Read(b); err != nil {
|
||
|
return *a, err
|
||
|
}
|
||
|
|
||
|
abuf := bytes.NewBuffer(b)
|
||
|
read(abuf, ah)
|
||
|
abuf.Next(nc + 4)
|
||
|
read(abuf, a)
|
||
|
|
||
|
if a.Addrlen != 4 {
|
||
|
return *a, errors.New("invalid name")
|
||
|
}
|
||
|
|
||
|
return *a, nil
|
||
|
}
|