initial commit

This commit is contained in:
ipc 2023-05-28 17:52:52 +02:00
commit 43d20527ba
4 changed files with 199 additions and 0 deletions

4
README.md Normal file
View File

@ -0,0 +1,4 @@
# nlup
nlup is a small domain name lookup utility that interacts
directly with the nameserver.

143
dns.go Normal file
View File

@ -0,0 +1,143 @@
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
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module github.com/rtcall/nlup
go 1.20

49
main.go Normal file
View File

@ -0,0 +1,49 @@
package main
import (
"flag"
"fmt"
"os"
)
func main() {
ns := flag.String("n", "", "nameserver to use")
v := flag.Bool("v", false, "verbose output")
flag.Parse()
if len(flag.Args()) == 0 {
fmt.Printf("usage: %s [-n nameserver] [-v] name\n", os.Args[0])
os.Exit(1)
}
if *ns == "" {
if s, err := findNsAddr(); err != nil {
fmt.Printf("error: %s\n", err)
os.Exit(1)
} else {
*ns = s
}
}
name := flag.Arg(0)
c, err := dial(*ns)
if err != nil {
fmt.Printf("error: %s\n", err)
os.Exit(1)
}
ans, err := c.sendQuery(name)
if err != nil {
fmt.Printf("error: %s\n", err)
os.Exit(1)
}
if *v {
fmt.Printf("nameserver: %s\ntype: %d\nttl: %d\n",
*ns, ans.Ntype, ans.Ttl)
}
fmt.Printf("%s: %d.%d.%d.%d\n", name,
(ans.Addr>>24)&0xff, (ans.Addr>>16)&0xff, (ans.Addr>>8)&0xff,
ans.Addr&0xff)
}