Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
_output/
37 changes: 37 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# ==============================================================================
# 定义全局 Makefile 变量方便后面引用
PROJ_NAME = dnsproxy

COMMON_SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
# 项目根目录
ROOT_DIR := $(abspath $(shell cd $(COMMON_SELF_DIR)/ && pwd -P))
# 构建产物、临时文件存放目录
OUTPUT_DIR := $(ROOT_DIR)/_output

# ==============================================================================
# 定义 Makefile all 伪目标,执行 `make` 时,会默认会执行 all 伪目标
.PHONY: all
all: add-copyright format build

# ==============================================================================
# 定义其他需要的伪目标

.PHONY: build
build: tidy # 编译源码,依赖 tidy 目标自动添加/移除依赖包.
@go build -v -o $(OUTPUT_DIR)/${PROJ_NAME} $(ROOT_DIR)/dnsproxy.go

.PHONY: format
format: # 格式化 Go 源码.
@gofmt -s -w ./

.PHONY: tidy
tidy: # 自动添加/移除依赖包.
@go mod tidy

.PHONY: clean
clean: # 清理构建产物、临时文件等.
@-rm -vrf $(OUTPUT_DIR)

.PHONY: run
run:
sudo $(OUTPUT_DIR)/${PROJ_NAME}
55 changes: 55 additions & 0 deletions README-en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
## Proxy DNS query use TCP in go lang

苦于本地DNS污染,连github.com这种都经常解析不了。最近愈发频繁,所以写了这个程序。

- 采用多个dns地址轮询。
- dns 请求时,默认 read/write 都为 100ms 超时,实测已经足够,更长时间会导致网页访问变慢。
- 使用 TCP 做 DNS 解析,转发正常的 UDP 请求。
- go-cache 做缓存,默认一小时失效,pure go,无需安装其它组件。

另有使用 redis 做缓存的版本在 redis-cache 分支

Dependencies:

go get github.com/miekg/dns
go get github.com/pmylund/go-cache

跨平台编译后放到了我的 arm 开发板 pcDuino 上,现在又可以作为 DNS服务器 了 ^_^
Build for special platform:

GOOS=linux GOARCH=arm go build src/dnsproxy.go

数台电脑,移动设备,平稳运行两天,正常解析。

## Using

Supported arguments, all these could use as commandline flags like `-xxx=xxx`:

dnss = flag.String("dns", "192.168.2.1:53,8.8.8.8:53,8.8.4.4:53", "dns address, use `,` as sep")
local = flag.String("local", ":53", "local listen address")
debug = flag.Int("debug", 0, "debug level 0 1 2")
cache = flag.Bool("cache", true, "enable go-cache")
expire = flag.Int64("expire", 3600, "default cache expire time")
ipv6 = flag.Bool("6", false, "skip ipv6 record query AAAA")
hostfile = flag.String("hostfile", "_output/host-file.txt, "host file for dns result intercept & substitute")

`make build` to build the executable file: `dnsproxy`

> [New Feature] To replace DNS response results using rules defined in the hostfile, you need to manually place the edited hostfile into the _output directory.

`make run` to run

Run manually:

sudo ./dnsproxy

Set dns address,using `,` as the delimiter.

sudo ./dnsproxy -dns=x.x.x.x:53,x.x.x.x:53

Using `-debug` flag could print the dns query log.

sudo ./dnsproxy -debug=1

### Thanks
Part of source code from [HERE](https://gist.github.com/mrluanma/3722792)
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
##Proxy DNS query use TCP in go lang
## Proxy DNS query use TCP in go lang

苦于本地DNS污染,连github.com这种都经常解析不了。最近愈发频繁,所以写了这个程序。

Expand All @@ -20,7 +20,7 @@

数台电脑,移动设备,平稳运行两天,正常解析。

##使用方法
## 使用方法

支持的参数:

Expand All @@ -30,9 +30,15 @@
cache = flag.Bool("cache", true, "enable go-cache")
expire = flag.Int64("expire", 3600, "default cache expire time")
ipv6 = flag.Bool("6", false, "skip ipv6 record query AAAA")
hostfile = flag.String("hostfile", "_output/host-file.txt, "host file for dns result intercept & substitute")

build 生成 dnsproxy 文件后
执行:
`make build` 生成 dnsproxy 文件后

> 如要使用hostfile内规则替换DNS响应结果,需手动将编辑好的hostfile文件放入`_output`目录下

`make run`运行

手动执行:

sudo ./dnsproxy

Expand All @@ -44,5 +50,5 @@ build 生成 dnsproxy 文件后

sudo ./dnsproxy -debug=1

###Thanks
### Thanks
部分代码源自 [这里](https://gist.github.com/mrluanma/3722792)
75 changes: 65 additions & 10 deletions dnsproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ package main

import (
"crypto/md5"
"dnsproxy/regex"
"encoding/hex"
"flag"
"fmt"
"github.com/miekg/dns"
"github.com/pmylund/go-cache"
"log"
"net"
"os"
Expand All @@ -17,17 +16,21 @@ import (
"strings"
"syscall"
"time"

"github.com/miekg/dns"
"github.com/pmylund/go-cache"
)

var (
dnss = flag.String("dns", "192.168.2.1:53:udp,8.8.8.8:53:udp,8.8.4.4:53:udp,8.8.8.8:53:tcp,8.8.4.4:53:tcp", "dns address, use `,` as sep")
local = flag.String("local", ":53", "local listen address")
debug = flag.Int("debug", 0, "debug level 0 1 2")
encache = flag.Bool("cache", true, "enable go-cache")
expire = flag.Int64("expire", 3600, "default cache expire seconds, -1 means use doamin ttl time")
file = flag.String("file", filepath.Join(path.Dir(os.Args[0]), "cache.dat"), "cached file")
ipv6 = flag.Bool("6", false, "skip ipv6 record query AAAA")
timeout = flag.Int("timeout", 200, "read/write timeout")
dnss = flag.String("dns", "127.0.0.53:53:udp,127.0.0.53:53:tcp", "dns address, use `,` as sep")
local = flag.String("local", "127.0.0.1:53", "local listen address")
debug = flag.Int("debug", 0, "debug level 0 1 2")
encache = flag.Bool("cache", true, "enable go-cache")
expire = flag.Int64("expire", 3600, "default cache expire seconds, -1 means use doamin ttl time")
file = flag.String("file", filepath.Join(path.Dir(os.Args[0]), "cache.dat"), "cached file")
ipv6 = flag.Bool("6", false, "skip ipv6 record query AAAA")
timeout = flag.Int("timeout", 200, "read/write timeout")
hostfile = flag.String("hostfile", filepath.Join(path.Dir(os.Args[0]), "host-file.txt"), "hosts with wildcard")

clientTCP *dns.Client
clientUDP *dns.Client
Expand Down Expand Up @@ -125,6 +128,8 @@ func init() {
log.Fatalln("dns address must be not empty")
}

regex.Init(*hostfile)

signal.Notify(saveSig, syscall.SIGINT, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGQUIT)
}

Expand Down Expand Up @@ -157,7 +162,14 @@ func proxyServe(w dns.ResponseWriter, req *dns.Msg) {
query []string
questions []dns.Question
used string
reqName string
)
if len(req.Question) > 0 {
for _, v := range req.Question {
// log.Printf("%s\t,Recv:%s", v.Name, v.String())
reqName = v.Name
}
}

defer func() {
if err := recover(); err != nil {
Expand Down Expand Up @@ -192,6 +204,13 @@ func proxyServe(w dns.ResponseWriter, req *dns.Msg) {

if ENCACHE {
if reply, ok := conn.Get(key); ok {
if DEBUG > 1 {
if ok {
log.Printf("Cache find [%s]", questions[0].Name)
} else {
log.Printf("Cache MISS [%s]", questions[0].Name)
}
}
data, _ = reply.([]byte)
}
if data != nil && len(data) > 0 {
Expand Down Expand Up @@ -234,6 +253,8 @@ func proxyServe(w dns.ResponseWriter, req *dns.Msg) {
}
}

substituteResponse(reqName, m)

if err == nil {
if DEBUG > 0 {
if tried {
Expand All @@ -260,12 +281,17 @@ func proxyServe(w dns.ResponseWriter, req *dns.Msg) {
}
}
conn.Set(key, data, time.Second*time.Duration(ttl))
// log.Printf("Cache Set [%s]", questions[0].Name)
m.Id = id
if DEBUG > 0 {
log.Printf("id: %5d cache: CACHED %v TTL %v\n", id, query, ttl)
}
}
} else {
log.Printf("Response write fail [%s]", questions[0].Name)
}
} else {
log.Printf("Response pack fail [%s]", questions[0].Name)
}
}

Expand All @@ -284,3 +310,32 @@ end:
fmt.Println("====================================================")
}
}

func substituteResponse(reqName string, m *dns.Msg) error {
if regex.MappingTree == nil || len(reqName) < 1 {
return nil
}
defer func() {
if err := recover(); err != nil {
log.Fatalln(err)
}
}()
// log.Printf("Begin replace [%s]", reqName)
// Check if the last character is a period
reqName = strings.TrimSuffix(reqName, ".")

ip := regex.MappingTree.FindReverse(reqName)
if ip == "" {
// log.Printf(" --- Replace not Found [%s]", reqName)
return nil
}
log.Printf("Do replace [%s] with IP [%s]", reqName, ip)
newResponse, err := dns.NewRR(reqName + " 3600 IN A " + ip)
if err != nil {
log.Printf(" --- Replace ERROR [%s]", reqName)
return err
}
m.Answer = make([]dns.RR, 1)
m.Answer[0] = newResponse
return nil
}
15 changes: 15 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module dnsproxy

go 1.21.4

require (
github.com/miekg/dns v1.1.57
github.com/pmylund/go-cache v2.1.0+incompatible
)

require (
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/tools v0.13.0 // indirect
)
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/pmylund/go-cache v2.1.0+incompatible h1:n+7K51jLz6a3sCvff3BppuCAkixuDHuJ/C57Vw/XjTE=
github.com/pmylund/go-cache v2.1.0+incompatible/go.mod h1:hmz95dGvINpbRZGsqPcd7B5xXY5+EKb5PpGhQY3NTHk=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
Loading