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