On Android, /etc/resolv.conf does not exist, causing Go's default DNS resolution to fail. This adds an init() hook that: 1. Detects missing /etc/resolv.conf (Android environment) 2. Configures a custom resolver with PreferGo: true 3. Supports multiple DNS servers via PICOCLAW_DNS_SERVER env var - Semicolon-separated: "8.8.8.8:53;1.1.1.1:53" - Single server also works: "8.8.8.8" - Auto-appends :53 if port omitted 4. Round-robin rotation across configured servers 5. Defaults to Google DNS + Cloudflare DNS Also patches http.DefaultTransport to use the custom resolver.
64 lines
1.5 KiB
Go
64 lines
1.5 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
func init() {
|
|
// 仅在 /etc/resolv.conf 不存在时才覆盖(即 Android 环境)
|
|
if _, err := os.Stat("/etc/resolv.conf"); err == nil {
|
|
return
|
|
}
|
|
|
|
// 从环境变量获取 DNS server 列表,多个用 ; 隔开
|
|
// 例如: PICOCLAW_DNS_SERVER="8.8.8.8:53;1.1.1.1:53;223.5.5.5:53"
|
|
dnsEnv := os.Getenv("PICOCLAW_DNS_SERVER")
|
|
if dnsEnv == "" {
|
|
dnsEnv = "8.8.8.8:53;1.1.1.1:53"
|
|
}
|
|
|
|
var dnsServers []string
|
|
for _, s := range strings.Split(dnsEnv, ";") {
|
|
s = strings.TrimSpace(s)
|
|
if s != "" {
|
|
// 如果没有带端口号,自动补上 :53
|
|
if _, _, err := net.SplitHostPort(s); err != nil {
|
|
s = s + ":53"
|
|
}
|
|
dnsServers = append(dnsServers, s)
|
|
}
|
|
}
|
|
|
|
// 轮询索引,在多个 DNS 服务器之间轮转
|
|
var idx uint64
|
|
|
|
customResolver := &net.Resolver{
|
|
PreferGo: true,
|
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
|
d := net.Dialer{Timeout: 5 * time.Second}
|
|
// Round-robin: 依次尝试不同的 DNS 服务器
|
|
server := dnsServers[atomic.AddUint64(&idx, 1)%uint64(len(dnsServers))]
|
|
return d.DialContext(ctx, "udp", server)
|
|
},
|
|
}
|
|
|
|
// 覆盖全局 DefaultResolver
|
|
net.DefaultResolver = customResolver
|
|
|
|
// 覆盖 http.DefaultTransport 使用自定义 DNS 解析的 DialContext
|
|
dialer := &net.Dialer{
|
|
Timeout: 30 * time.Second,
|
|
KeepAlive: 30 * time.Second,
|
|
Resolver: customResolver,
|
|
}
|
|
|
|
if tr, ok := http.DefaultTransport.(*http.Transport); ok {
|
|
tr.DialContext = dialer.DialContext
|
|
}
|
|
}
|