Files
goproxy/README_DNS.md
T
2025-03-13 17:53:08 +08:00

7.7 KiB
Raw Blame History

GoProxy自定义DNS解析功能

该功能允许GoProxy使用自定义DNS解析器,实现以下功能:

  • 自定义域名解析
  • 动态变更后端服务器IP
  • 自定义后端服务器端口
  • 泛解析(通配符域名)支持
  • 负载均衡和故障转移
  • 绕过DNS污染
  • 高效的DNS缓存

特性

  • 自定义记录:直接设置域名到IP的映射
  • 自定义端口:为每个域名指定自定义端口,无需在URL中指定
  • 泛解析:支持通配符域名(如*.example.com)自动匹配多个子域名
  • 多级泛解析:支持复杂的通配符模式(如api.*.example.com
  • 备用解析:在自定义记录未找到时可选择使用系统DNS
  • DNS缓存:缓存解析结果以提高性能
  • 自动重试:解析失败时可配置重试策略
  • 加载配置:从JSON文件或hosts格式文件加载配置
  • 自定义拨号器:与net标准库兼容的拨号器

安装

确保已安装Go(建议1.22或更高版本):

git clone https://github.com/ouqiang/goproxy
cd goproxy
go build ./...

使用方法

1. HTTP代理(自定义DNS

以下命令会启动一个HTTP代理(监听端口8080),它使用自定义DNS解析:

go run cmd/custom_dns_proxy/main.go

2. 自定义端口代理

支持为不同域名指定不同端口的代理:

go run cmd/custom_port_proxy/main.go

3. 泛解析DNS代理

支持通配符域名解析的代理:

# 使用默认的示例泛解析规则
go run cmd/wildcard_dns_proxy/main.go

# 透明代理模式(使用请求中的Host进行匹配)
go run cmd/wildcard_dns_proxy/main.go -target ""

# 使用自定义配置文件
go run cmd/wildcard_dns_proxy/main.go -dns examples/wildcard_dns_config.json

# 使用hosts格式配置文件
go run cmd/wildcard_dns_proxy/main.go -hosts examples/wildcard_hosts.txt

参数说明

  • -listen: 监听地址,默认 :8080
  • -target: 目标主机名,空字符串表示使用请求中的Host头
  • -port: 默认目标端口,默认 443
  • -dns: DNS配置文件(JSON格式)
  • -hosts: hosts格式配置文件

DNS配置格式

JSON格式

{
  "records": {
    "example.com": "93.184.216.34",
    "api.example.com": "93.184.216.35:8443",
    
    "*.github.com": "140.82.121.3",
    "github.com": "140.82.121.4",
    
    "*.dev.local": "127.0.0.1:3000",
    "api.*.dev.local": "127.0.0.1:3001"
  },
  "use_fallback": true,
  "ttl": 300
}

Hosts格式

# 精确匹配
93.184.216.34 example.com
93.184.216.35:8443 api.example.com

# 泛解析(通配符域名)
140.82.121.3 *.github.com
127.0.0.1:3000 *.dev.local
127.0.0.1:3001 api.*.dev.local

编程接口

使用DNS解析器

import "github.com/goproxy/internal/dns"

// 创建解析器
resolver := dns.NewResolver()

// 添加标准记录(使用默认端口)
resolver.Add("example.com", "93.184.216.34")

// 添加带端口的记录
resolver.AddWithPort("api.example.com", "93.184.216.35", 8443)

// 添加泛解析记录(使用默认端口)
resolver.AddWildcard("*.example.com", "93.184.216.36")

// 添加带端口的泛解析记录
resolver.AddWildcardWithPort("*.api.example.com", "93.184.216.37", 8444)

// 解析域名(只获取IP
ip, err := resolver.Resolve("example.com")
if err != nil {
    log.Fatalf("解析失败: %v", err)
}
fmt.Printf("解析结果IP: %s\n", ip)

// 测试泛解析功能
ip, err = resolver.Resolve("sub.example.com")
if err != nil {
    log.Fatalf("解析失败: %v", err)
}
fmt.Printf("泛解析结果IP: %s\n", ip)

// 测试多级泛解析功能
endpoint, err := resolver.ResolveWithPort("test.api.example.com", 443)
if err != nil {
    log.Fatalf("解析失败: %v", err)
}
fmt.Printf("多级泛解析结果: IP=%s, 端口=%d\n", endpoint.IP, endpoint.Port)

使用DNS拨号器

import "github.com/goproxy/internal/dns"

// 创建解析器
resolver := dns.NewResolver()
resolver.Add("example.com", "93.184.216.34")
resolver.AddWildcard("*.example.com", "93.184.216.36")

// 创建拨号器
dialer := dns.NewDialer(resolver)

// 使用拨号器连接(会自动应用泛解析)
conn, err := dialer.Dial("tcp", "sub.example.com:443")
if err != nil {
    log.Fatalf("连接失败: %v", err)
}
defer conn.Close()

// 或者获取用于http.Transport的拨号上下文函数
transport := &http.Transport{
    DialContext: dialer.DialContext,
}
client := &http.Client{Transport: transport}

高级用法

泛解析模式

泛解析支持以下模式:

  1. 单级通配符*.example.com 匹配 a.example.comb.example.com

  2. 多级通配符*.*.example.com 匹配 a.b.example.comc.d.example.com

  3. 中间通配符api.*.example.com 匹配 api.v1.example.comapi.beta.example.com

  4. 前缀通配符api-*.example.com 匹配 api-v1.example.comapi-beta.example.com

域名匹配优先级

当有多条规则可以匹配同一个域名时,解析器会按照以下优先级选择:

  1. 精确匹配(如 example.com
  2. 最具体的通配符匹配(如 *.test.example.com*.example.com 更优先)
  3. 后添加的通配符规则优先于先添加的规则

透明代理模式

泛解析代理支持透明模式,这种模式下代理会使用请求中的Host头来进行DNS解析,而不是固定的目标主机:

go run cmd/wildcard_dns_proxy/main.go -target ""

这样,通过修改请求的Host头或使用不同的域名访问代理,可以自动路由到不同的后端服务器。

自定义DNS后端

可以实现自己的dns.Resolver接口来创建更复杂的DNS解析策略:

type MyResolver struct {
    // 你的字段
}

// 实现Resolver接口的所有方法
func (r *MyResolver) Resolve(hostname string) (string, error) {
    // 自定义逻辑
}

func (r *MyResolver) ResolveWithPort(hostname string, defaultPort int) (*dns.Endpoint, error) {
    // 自定义逻辑
}

func (r *MyResolver) Add(hostname, ip string) error {
    // 自定义逻辑
}

func (r *MyResolver) AddWithPort(hostname, ip string, port int) error {
    // 自定义逻辑
}

func (r *MyResolver) AddWildcard(wildcardDomain, ip string) error {
    // 自定义逻辑
}

func (r *MyResolver) AddWildcardWithPort(wildcardDomain, ip string, port int) error {
    // 自定义逻辑
}

func (r *MyResolver) Remove(hostname string) error {
    // 自定义逻辑
}

func (r *MyResolver) Clear() {
    // 自定义逻辑
}

应用场景

多环境测试

使用泛解析可以为不同环境的所有服务配置不同的后端:

# 测试环境的所有服务
192.168.1.100 *.test.example.com

# 预发布环境的所有服务
192.168.1.101 *.staging.example.com

# 生产环境的所有服务
192.168.1.102 *.production.example.com

微服务架构

为不同类型的微服务提供统一的路由模式:

10.0.0.1:8001 *.auth.internal
10.0.0.2:8002 *.user.internal
10.0.0.3:8003 *.payment.internal

多租户系统

在多租户系统中为每个租户路由到不同后端:

# 每个租户的子域名指向专用服务器
192.168.1.10 tenant1.*.example.com
192.168.1.11 tenant2.*.example.com
192.168.1.12 tenant3.*.example.com

本地开发环境

在本地开发中快速模拟复杂的服务架构:

127.0.0.1:3000 *.local
127.0.0.1:3001 api.*.local
127.0.0.1:5432 db.*.local

注意事项

  1. 通配符域名只在我们的自定义DNS解析器中有效,不会影响系统DNS
  2. 泛解析规则的顺序会影响匹配结果,后添加的规则优先级更高
  3. 过多的泛解析规则可能会影响性能,建议合理组织规则
  4. 当域名同时匹配多个规则时,精确匹配优先于通配符匹配
  5. 自签名证书会导致浏览器警告,仅用于测试目的