rawdns.mx raw
1 // Raw DNS resolver — bypasses dnsmessage which produces malformed
2 // packets under moxie's string=[]byte runtime.
3 // Uses raw syscalls to avoid re-entering the net package.
4
5 //go:build !windows
6
7 package net
8
9 import (
10 "fmt"
11 "os"
12 "syscall"
13 )
14
15 var rawDNSAddr [4]byte
16
17 func init() {
18 rawDNSAddr = [4]byte{127, 0, 0, 1}
19 data, err := os.ReadFile("/etc/resolv.conf")
20 if err != nil {
21 return
22 }
23 i := 0
24 for i < len(data) {
25 start := i
26 for i < len(data) && data[i] != '\n' {
27 i++
28 }
29 line := data[start:i]
30 if i < len(data) {
31 i++
32 }
33 if len(line) > 11 && string(line[:11]) == "nameserver " {
34 ns := line[11:]
35 for len(ns) > 0 && (ns[len(ns)-1] == ' ' || ns[len(ns)-1] == '\t' || ns[len(ns)-1] == '\r') {
36 ns = ns[:len(ns)-1]
37 }
38 if ip := rawParseIPv4(ns); ip != nil {
39 rawDNSAddr = [4]byte{ip[0], ip[1], ip[2], ip[3]}
40 return
41 }
42 }
43 }
44 }
45
46 func rawParseIPv4(s []byte) []byte {
47 var parts [4]byte
48 p := 0
49 v := 0
50 for i := 0; i <= len(s); i++ {
51 if i == len(s) || s[i] == '.' {
52 if v > 255 || p > 3 {
53 return nil
54 }
55 parts[p] = byte(v)
56 p++
57 v = 0
58 } else if s[i] >= '0' && s[i] <= '9' {
59 v = v*10 + int(s[i]-'0')
60 } else {
61 return nil
62 }
63 }
64 if p != 4 {
65 return nil
66 }
67 return parts[:]
68 }
69
70 // rawLookupHost resolves a hostname via raw UDP syscalls.
71 func rawLookupHost(host string) [][]byte {
72 fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0)
73 if err != nil {
74 return nil
75 }
76 defer syscall.Close(fd)
77
78 tv := syscall.Timeval{Sec: 5}
79 syscall.SetsockoptTimeval(fd, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &tv)
80
81 sa := &syscall.SockaddrInet4{Port: 53, Addr: rawDNSAddr}
82 if err := syscall.Connect(fd, sa); err != nil {
83 return nil
84 }
85
86 // Build DNS A-record query.
87 var pkt []byte
88 pkt = append(pkt, 0xAB, 0xCD) // ID
89 pkt = append(pkt, 0x01, 0x00) // flags: recursion desired
90 pkt = append(pkt, 0x00, 0x01) // 1 question
91 pkt = append(pkt, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) // 0 answer/auth/additional
92
93 // Encode QNAME.
94 j := 0
95 for j < len(host) {
96 dot := j
97 for dot < len(host) && host[dot] != '.' {
98 dot++
99 }
100 pkt = append(pkt, byte(dot-j))
101 pkt = append(pkt, host[j:dot]...)
102 j = dot + 1
103 }
104 pkt = append(pkt, 0x00) // root
105 pkt = append(pkt, 0x00, 0x01) // QTYPE A
106 pkt = append(pkt, 0x00, 0x01) // QCLASS IN
107
108 if err := syscall.Sendto(fd, pkt, 0, sa); err != nil {
109 return nil
110 }
111
112 buf := make([]byte, 512)
113 n, _, err := syscall.Recvfrom(fd, buf, 0)
114 if err != nil || n < 12 {
115 return nil
116 }
117
118 anCount := int(buf[6])<<8 | int(buf[7])
119 if anCount == 0 {
120 return nil
121 }
122
123 // Skip question section.
124 pos := 12
125 for pos < n {
126 if buf[pos] == 0 {
127 pos++
128 break
129 }
130 if buf[pos]&0xC0 == 0xC0 {
131 pos += 2
132 break
133 }
134 pos += int(buf[pos]) + 1
135 }
136 pos += 4 // QTYPE + QCLASS
137
138 // Parse answer records.
139 var addrs [][]byte
140 for a := 0; a < anCount && pos+10 < n; a++ {
141 if buf[pos]&0xC0 == 0xC0 {
142 pos += 2
143 } else {
144 for pos < n && buf[pos] != 0 {
145 pos += int(buf[pos]) + 1
146 }
147 pos++
148 }
149 if pos+10 > n {
150 break
151 }
152 rtype := int(buf[pos])<<8 | int(buf[pos+1])
153 rdlen := int(buf[pos+8])<<8 | int(buf[pos+9])
154 pos += 10
155 if rtype == 1 && rdlen == 4 && pos+4 <= n {
156 s := fmt.Sprintf("%d.%d.%d.%d", buf[pos], buf[pos+1], buf[pos+2], buf[pos+3])
157 addr := make([]byte, len(s))
158 copy(addr, s)
159 addrs = append(addrs, addr)
160 }
161 pos += rdlen
162 }
163 return addrs
164 }
165