Skip to content

Commit 071ac10

Browse files
committed
feat: support MagicDNS for android
1 parent 0a5efcf commit 071ac10

5 files changed

Lines changed: 114 additions & 3 deletions

File tree

awldns/awldns.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ const (
2222
const (
2323
LocalDomain = "awl"
2424
DefaultDNSAddress = "127.0.0.66:53"
25+
DefaultNon53DNSAddress = "127.0.0.66:35353"
2526
DefaultDNSPort = "53"
2627
DefaultUpstreamDNSAddress = "1.1.1.1:53"
28+
MagicDNSIP = "100.100.100.66"
2729
)
2830

2931
type Resolver struct {

config/other.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,11 @@ func setDefaults(conf *Config, bus awlevent.Bus) {
202202
}
203203

204204
if conf.DNS.ListenAddress == "" {
205-
conf.DNS.ListenAddress = awldns.DefaultDNSAddress
205+
if runtime.GOOS == "android" {
206+
conf.DNS.ListenAddress = awldns.DefaultNon53DNSAddress
207+
} else {
208+
conf.DNS.ListenAddress = awldns.DefaultDNSAddress
209+
}
206210
}
207211
if conf.DNS.UpstreamDNSAddress == "" {
208212
conf.DNS.UpstreamDNSAddress = awldns.DefaultUpstreamDNSAddress

service/tunnel.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import (
1414
"github.com/libp2p/go-libp2p/core/network"
1515
"github.com/libp2p/go-libp2p/core/peer"
1616

17+
"encoding/binary"
18+
19+
"github.com/anywherelan/awl/awldns"
1720
"github.com/anywherelan/awl/awlevent"
1821
"github.com/anywherelan/awl/config"
1922
"github.com/anywherelan/awl/metrics"
@@ -270,6 +273,19 @@ func (t *Tunnel) HandleReadPackets(packets []*vpn.Packet) {
270273
continue
271274
}
272275

276+
// Intercept Magic DNS queries
277+
if !packet.IsIPv6 && packet.IPProtocol == vpn.IPProtocolUDP && packet.Dst.Equal(MagicDNSIP) {
278+
ipHeaderLen := int((packet.Packet[0] & 0x0F) * 4)
279+
if len(packet.Packet) >= ipHeaderLen+8 {
280+
dstPort := binary.BigEndian.Uint16(packet.Packet[ipHeaderLen+2 : ipHeaderLen+4])
281+
if dstPort == 53 {
282+
t.handleMagicDNSForward(packet)
283+
packets[i] = nil
284+
continue
285+
}
286+
}
287+
}
288+
273289
// P2P broadcast/unicast lookup
274290
vpnPeer, isP2P := t.netIPToPeer[packet.Dst.String()]
275291
if isP2P {
@@ -793,6 +809,94 @@ func isNonRoutableIP(ip net.IP) bool {
793809
return ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsMulticast()
794810
}
795811

812+
// MagicDNSIP is a dummy IP used on Android to force DNS queries into the TUN interface.
813+
// When the tunnel reads a UDP packet destined to MagicDNSIP:53, it intercepts it
814+
// and proxies the query to the local awldns UDP server.
815+
var MagicDNSIP = net.ParseIP(awldns.MagicDNSIP).To4()
816+
817+
func (t *Tunnel) handleMagicDNSForward(packet *vpn.Packet) {
818+
// Extract the original DNS query payload
819+
ipHeaderLen := int((packet.Packet[0] & 0x0F) * 4)
820+
udpHeaderOffset := ipHeaderLen
821+
if len(packet.Packet) <= udpHeaderOffset+8 {
822+
t.device.PutTempPacket(packet)
823+
return
824+
}
825+
originalSrcIP := make(net.IP, len(packet.Src))
826+
copy(originalSrcIP, packet.Src)
827+
originalSrcPort := binary.BigEndian.Uint16(packet.Packet[udpHeaderOffset : udpHeaderOffset+2])
828+
payload := packet.Packet[udpHeaderOffset+8:]
829+
830+
dnsPayload := make([]byte, len(payload))
831+
copy(dnsPayload, payload)
832+
t.device.PutTempPacket(packet)
833+
834+
dnsAddr := t.conf.DNS.ListenAddress
835+
836+
go func() {
837+
conn, err := net.Dial("udp", dnsAddr)
838+
if err != nil {
839+
t.logger.Warnf("failed to dial local dns %s: %v", dnsAddr, err)
840+
return
841+
}
842+
defer conn.Close()
843+
844+
_ = conn.SetDeadline(time.Now().Add(5 * time.Second))
845+
_, err = conn.Write(dnsPayload)
846+
if err != nil {
847+
return
848+
}
849+
850+
respBuf := make([]byte, 1500)
851+
n, err := conn.Read(respBuf)
852+
if err != nil || n == 0 {
853+
return
854+
}
855+
respPayload := respBuf[:n]
856+
857+
// Craft the return IPv4 UDP packet
858+
respPacket := t.device.GetTempPacket()
859+
totalLen := 20 + 8 + len(respPayload)
860+
if totalLen > len(respPacket.Buffer)-vpn.TunPacketOffset {
861+
t.device.PutTempPacket(respPacket)
862+
return
863+
}
864+
865+
buf := respPacket.Buffer[vpn.TunPacketOffset : vpn.TunPacketOffset+totalLen]
866+
respPacket.Packet = buf
867+
respPacket.IsIPv6 = false
868+
869+
// IPv4 Header (20 bytes)
870+
buf[0] = 0x45 // Version 4, IHL 5
871+
buf[1] = 0x00 // TOS
872+
binary.BigEndian.PutUint16(buf[2:4], uint16(totalLen))
873+
binary.BigEndian.PutUint16(buf[4:6], 0) // ID
874+
binary.BigEndian.PutUint16(buf[6:8], 0) // Flags/Frag
875+
buf[8] = 64 // TTL
876+
buf[9] = vpn.IPProtocolUDP
877+
binary.BigEndian.PutUint16(buf[10:12], 0) // Checksum (computed later)
878+
copy(buf[12:16], MagicDNSIP)
879+
copy(buf[16:20], originalSrcIP.To4())
880+
881+
// Calculate IP Checksum
882+
ipChecksum := vpn.ChecksumIPv4Header(buf[:20])
883+
binary.BigEndian.PutUint16(buf[10:12], ipChecksum)
884+
885+
// UDP Header (8 bytes)
886+
binary.BigEndian.PutUint16(buf[20:22], 53)
887+
binary.BigEndian.PutUint16(buf[22:24], originalSrcPort)
888+
binary.BigEndian.PutUint16(buf[24:26], uint16(8+len(respPayload)))
889+
binary.BigEndian.PutUint16(buf[26:28], 0) // Optional for IPv4 UDP
890+
891+
// Payload
892+
copy(buf[28:], respPayload)
893+
894+
// Write back to TUN
895+
_ = t.device.WriteBufs([][]byte{respPacket.Buf()})
896+
t.device.PutTempPacket(respPacket)
897+
}()
898+
}
899+
796900
func readBatchFromChan(ch chan *vpn.Packet, buf []*vpn.Packet, offset int) []*vpn.Packet {
797901
i := offset
798902
for {

vpn/packet.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ func (data *Packet) RecalculateChecksum() {
144144
return
145145
}
146146
copy(data.Packet[ipv4offsetChecksum:], []byte{0, 0})
147-
ipChecksum := checksumIPv4Header(data.Packet[:ipHeaderLen])
147+
ipChecksum := ChecksumIPv4Header(data.Packet[:ipHeaderLen])
148148
binary.BigEndian.PutUint16(data.Packet[ipv4offsetChecksum:], ipChecksum)
149149

150150
switch protocol := data.Packet[9]; protocol {
@@ -216,7 +216,7 @@ func (data *Packet) setAddrs() {
216216
}
217217
}
218218

219-
func checksumIPv4Header(buf []byte) uint16 {
219+
func ChecksumIPv4Header(buf []byte) uint16 {
220220
var v uint32
221221
for i := 0; i < len(buf)-1; i += 2 {
222222
v += uint32(binary.BigEndian.Uint16(buf[i:]))

vpn/vpn.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const (
2020
maxContentSize = InterfaceMTU + 100
2121
// internal tun header. see offset in tun_darwin (4) and tun_linux (virtioNetHdrLen, currently 10)
2222
tunPacketOffset = 14
23+
TunPacketOffset = tunPacketOffset
2324
// MaxPacketBodySize is the largest tunnel packet body (an IP packet, without
2425
// the internal TUN header offset) that Packet.Buffer can hold. Peers must not
2526
// send larger frames: protocol.ReadPacketHeader rejects oversized ones up front and

0 commit comments

Comments
 (0)