first dirty implementation of dns proxy
This commit is contained in:
parent
f7a6f94664
commit
f8c31c9f01
176
main.go
Normal file
176
main.go
Normal file
@ -0,0 +1,176 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ListenPort = 7548
|
||||
UsableDNSServerAddress = "127.0.0.1"
|
||||
UsableDNSServerPort = 53
|
||||
DNSMaxPackageSize = 4096
|
||||
)
|
||||
|
||||
func parseName(response []byte, pos int) (string, int) {
|
||||
var nameParts []string
|
||||
var jumped bool
|
||||
var outPos int
|
||||
responseLen := len(response)
|
||||
|
||||
for {
|
||||
length := int(response[pos])
|
||||
pos++
|
||||
if length == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if length&0xC0 == 0xC0 {
|
||||
if !jumped {
|
||||
outPos = pos + 1
|
||||
}
|
||||
pos = int(binary.BigEndian.Uint16(response[pos-1:pos+1]) & 0x3FFF)
|
||||
jumped = true
|
||||
continue
|
||||
}
|
||||
|
||||
if pos+length > responseLen {
|
||||
break
|
||||
}
|
||||
|
||||
nameParts = append(nameParts, string(response[pos:pos+length]))
|
||||
pos += length
|
||||
}
|
||||
|
||||
if !jumped {
|
||||
outPos = pos
|
||||
}
|
||||
return strings.Join(nameParts, "."), outPos
|
||||
}
|
||||
|
||||
func sendToUpstream(upstreamAddr string, request []byte) ([]byte, error) {
|
||||
conn, err := net.Dial("udp", upstreamAddr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to dial upstream DNS: %w", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
_, err = conn.Write(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to send request to upstream DNS: %w", err)
|
||||
}
|
||||
|
||||
err = conn.SetReadDeadline(time.Now().Add(5 * time.Second))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set timeout: %w", err)
|
||||
}
|
||||
|
||||
response := make([]byte, DNSMaxPackageSize)
|
||||
n, err := conn.Read(response)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response from upstream DNS: %w", err)
|
||||
}
|
||||
|
||||
return response[:n], nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
addr := fmt.Sprintf(":%d", ListenPort)
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to resolve address: %v", err)
|
||||
}
|
||||
|
||||
conn, err := net.ListenUDP("udp", udpAddr)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to listen on UDP: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
fmt.Printf("DNS server is running on %s...\n", addr)
|
||||
|
||||
for {
|
||||
buffer := make([]byte, DNSMaxPackageSize)
|
||||
n, clientAddr, err := conn.ReadFromUDP(buffer)
|
||||
if err != nil {
|
||||
log.Printf("Failed to read from UDP: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go handleDNSRequest(conn, clientAddr, buffer[:n])
|
||||
}
|
||||
}
|
||||
|
||||
func process(response []byte) {
|
||||
responseLen := len(response)
|
||||
if responseLen <= 12 {
|
||||
return
|
||||
}
|
||||
|
||||
qCount := int(binary.LittleEndian.Uint16(response[5:7]))
|
||||
aCount := int(binary.LittleEndian.Uint16(response[7:9]))
|
||||
|
||||
pos := 12
|
||||
|
||||
for i := 0; i < qCount; i++ {
|
||||
var name string
|
||||
name, pos = parseName(response, pos)
|
||||
fmt.Printf("Requested name: %s\n", name)
|
||||
pos += 4
|
||||
}
|
||||
|
||||
for i := 0; i < aCount; i++ {
|
||||
name, newPos := parseName(response, pos)
|
||||
pos = newPos
|
||||
|
||||
if pos+10 > responseLen {
|
||||
break
|
||||
}
|
||||
|
||||
qtype := binary.BigEndian.Uint16(response[pos : pos+2])
|
||||
pos += 2
|
||||
|
||||
qclass := binary.BigEndian.Uint16(response[pos : pos+2])
|
||||
pos += 2
|
||||
|
||||
ttl := binary.BigEndian.Uint32(response[pos : pos+4])
|
||||
pos += 4
|
||||
|
||||
rdlength := binary.BigEndian.Uint16(response[pos : pos+2])
|
||||
pos += 2
|
||||
|
||||
if pos+int(rdlength) > responseLen {
|
||||
break
|
||||
}
|
||||
|
||||
if qtype == 1 && qclass == 1 && rdlength == 4 {
|
||||
ip := net.IPv4(response[pos], response[pos+1], response[pos+2], response[pos+3])
|
||||
fmt.Printf("Parsed A record: %s -> %s, TTL: %d\n", name, ip, ttl)
|
||||
}
|
||||
|
||||
pos += int(rdlength)
|
||||
}
|
||||
}
|
||||
|
||||
func handleDNSRequest(conn *net.UDPConn, clientAddr *net.UDPAddr, buffer []byte) {
|
||||
upstreamAddr := fmt.Sprintf("%s:%d", UsableDNSServerAddress, UsableDNSServerPort)
|
||||
|
||||
upstreamResponse, err := sendToUpstream(upstreamAddr, buffer)
|
||||
if err != nil {
|
||||
log.Printf("Failed to get response from upstream DNS: %v", err)
|
||||
return
|
||||
}
|
||||
log.Printf("Response: %s", hex.EncodeToString(upstreamResponse))
|
||||
|
||||
process(upstreamResponse)
|
||||
|
||||
_, err = conn.WriteToUDP(upstreamResponse, clientAddr)
|
||||
if err != nil {
|
||||
log.Printf("Failed to send DNS response: %v", err)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user