From 7792fe61d63e519ab21566ba440d3d0c4cb2d6f2 Mon Sep 17 00:00:00 2001 From: "arraykeys@gmail.com" Date: Tue, 11 Sep 2018 15:08:29 +0800 Subject: [PATCH] add dns --- .gitignore | 1 + README_ZH.md | 104 ++++++++++++++++++++++++++++++++++++++++++--------- main.go | 74 +++++++++++++++++++++--------------- 3 files changed, 131 insertions(+), 48 deletions(-) diff --git a/.gitignore b/.gitignore index 8d75475..b09ec29 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.so *.dylib release-* +/cache.dat /shadowtunnel # Test binary, build with `go test -c` *.test diff --git a/README_ZH.md b/README_ZH.md index 17dd3c0..e6a6578 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -10,27 +10,45 @@ local machine <----> shadowtunnel <---> service on remote. ```text Usage of ./shadowtunnel: - -E outbound connection is encrypted - -U outbound connection is udp - -c compress traffic (default true) + -E outbound connection is encrypted + -U outbound connection is udp + -c compress traffic + -cache string + dns query cache file path (default "/tmp/go-build014924401/b001/exe/cache.dat") + -daemon + daemon mode -debug - show debug info - -e inbound connection is encrypted + show debug info + -dns string + local dns server listen on address + -dns-proxy + is dns endpoint or not + -dns-server string + remote dns server to resolve domain (default "8.8.8.8:53") + -e inbound connection is encrypted -f string - forward address,such as : 127.0.0.1:8080 + forward address,such as : 127.0.0.1:8080 + -forever + forever mode -l string - local listen address, such as : 0.0.0.0:33000 (default ":50000") + local listen address, such as : 0.0.0.0:33000 (default ":50000") + -log string + logging output to file -m string - method of encrypt/decrypt, these below are supported : - aes-192-cfb,aes-128-ctr,aes-256-ctr,bf-cfb,rc4-md5-6,chacha20-ietf, - aes-128-cfb,aes-256-cfb,aes-192-ctr,des-cfb,cast5-cfb,rc4-md5,chacha20 - (default "aes-192-cfb") + method of encrypt/decrypt, these below are supported : + aes-256-cfb,aes-128-ctr,aes-192-ctr,cast5-cfb,chacha20-ietf,rc4-md5-6,chacha20,aes-128-cfb,aes-192-cfb,aes-256-ctr,des-cfb,bf-cfb,rc4-md5 (default "aes-192-cfb") + -nolog + turn off logging -p string - password of encrypt/decrypt (default "shadowtunnel") + password of encrypt/decrypt (default "shadowtunnel") + -profiling + profiling mode, in this mode, you should stopping process by : Ctrl+C or 'kill -s SIGHUP $PID_OF_shadowtunnel' -t int - connection timeout seconds (default 3) - -u inbound connection is udp - -v show version + connection timeout seconds (default 3) + -ttl int + cache seconds of dns query , if zero , default ttl used. (default 300) + -u inbound connection is udp + -v show version ``` ## 示例 @@ -52,13 +70,13 @@ wget https://github.com/snail007/shadowtunnel/releases/download/v1.1/shadowtunne 在 vps 上监听 :50000 并转发到 127.0.0.1:38080 : -shadowtunnel -e -f 127.0.0.1:38080 -l :50000 +`shadowtunnel -e -f 127.0.0.1:38080 -l :50000` 3.在本地机器上启动一个隧道 在本地机器上监听 :50000 并转发到 2.2.2.2:50000 : -shadowtunnel -E -f 2.2.2.2:50000 -l :50000 +`shadowtunnel -E -f 2.2.2.2:50000 -l :50000` 4.在 chrome 中设置 http 代理配置 @@ -104,3 +122,55 @@ ip: 127.0.0.1 port: 50000 5.完成 + +## Deamon & Forever & Log + +-daemon: + +使用参数-daemon可以让shadowtunnel脱离当前命令行,后台运行. + +-forever: + +使用参数-forever可以让shadowtunnel以创建并监控子进程的方式运行, + +如果发生异常退出,会重启子进程,保证服务永远在线. + +-log + +使用参数-log可以设置日志输出到文件,而不是在命令行输出. + +-nolog + +使用参数-nolog可以从彻底关闭日志输出,节省CPU占用. + +一般是-daemon -forever -log /tmp/st.log 三个参数联合使用,这样出问题时,也可以通过看日志发现问题原因. + +实例: + +`shadowtunnel -u -e -f 127.0.0.1:38080 -l :50000 -p your-password -daemon -forever -log /tmp/st.log` + +## DNS服务 + +shadowtunnel可以在提供本地DNS查询服务,同时具有缓存功能,可以提高解析速度. + +在本地启动端口转发的同时启动一个DNS服务,需要有上级配合. + +-dns 参数可以设置本地DNS服务监听的IP和端口,比如:0.0.0.0:5353 + +-dns-server 参数可以设置最终用来解析域名的DNS服务器,要求是服务器必须支持TCP方式的DNS查询,默认是:8.8.8.8:53. + +本地实例: + +`shadowtunnel -E -f 2.2.2.2:50000 -l :50000 -p your-password -dns :5353 -dns-server 8.8.8.8:53` + +上级实例: + +如果上级是链式,那么链条中需要执行DNS代理的上级需要加上-dns-proxy参数. + +`shadowtunnel -e -f 127.0.0.1:38080 -l :50000 -p your-password -dns-proxy` + +## DNS缓存 + +-ttl 参数可以设置DNS查询结果缓存时间,单位秒,如果是0,使用查询结果的ttl. + +-cache 参数设置DNS缓存文件位置,防止程序重启缓存消失,降低性能. \ No newline at end of file diff --git a/main.go b/main.go index 4fbfb51..a48e22b 100644 --- a/main.go +++ b/main.go @@ -142,9 +142,16 @@ func main() { //setting cache cache = gocache.New(time.Second*time.Duration(dnsTTL), time.Second*60) cache.LoadFile(cacheFile) + go func() { + timer := time.NewTicker(time.Second * 300) + for { + <-timer.C + cache.DeleteExpired() + cache.SaveFile(cacheFile) + } + }() //start dns dnsServer() - } listen = srvtransport.NewServerChannelHost(listenAddr, log) if inboundUDP { @@ -171,30 +178,29 @@ func callback(conn net.Conn) { }() remoteAddr := conn.RemoteAddr() var outconn net.Conn - target := "" + var target string if dnsProxy { - var targetDNSServer string - utils.ReadPacketData(conn, &targetDNSServer) - if targetDNSServer == "" { - debugf("[warn] dns server address is empty") + utils.ReadPacketData(conn, &target) + if target == "" { + debugf("[warn] target is empty") conn.Close() return } - outconn, err = net.DialTimeout("tcp", targetDNSServer, time.Duration(timeout)*time.Second) - target = targetDNSServer - } else { - if outboundUDP { - outconn, err = clienttransport.TOUConnectHost(forwardAddr, method, password, compress, timeout*1000) - } else { - if outboundEncrypt { - outconn, err = clienttransport.TCPSConnectHost(forwardAddr, method, password, compress, timeout*1000) - } else { - outconn, err = net.DialTimeout("tcp", forwardAddr, time.Duration(timeout)*time.Second) - } + if target == "_" { + target = forwardAddr } + } else { target = forwardAddr } - + if dnsProxy { + outconn, err = net.DialTimeout("tcp", target, time.Duration(timeout)*time.Second) + } else { + addr := "" + if dnsListen != "" { + addr = "_" + } + outconn, err = getOutconn(addr) + } if err != nil { debugf("%s <--> %s, error: %s", remoteAddr, target, err) conn.Close() @@ -332,7 +338,6 @@ func dnsServer() { }() } func dnsCallback(w dns.ResponseWriter, req *dns.Msg) { - defer func() { if err := recover(); err != nil { debugf("dns handler crashed with err : %s \nstack: %s", err, string(debug.Stack())) @@ -379,17 +384,7 @@ func dnsCallback(w dns.ResponseWriter, req *dns.Msg) { return } debugf("id: %5d resolve: %v %s", id, query, dnsServerAddress) - var outconn net.Conn - - if outboundUDP { - outconn, err = clienttransport.TOUConnectHost(forwardAddr, method, password, compress, timeout*1000) - } else { - if outboundEncrypt { - outconn, err = clienttransport.TCPSConnectHost(forwardAddr, method, password, compress, timeout*1000) - } else { - outconn, err = net.DialTimeout("tcp", forwardAddr, time.Duration(timeout)*time.Second) - } - } + outconn, err := getOutconn(dnsServerAddress) if err != nil { debugf("dns query fail,%s", err) return @@ -398,9 +393,11 @@ func dnsCallback(w dns.ResponseWriter, req *dns.Msg) { outconn.Close() }() b, _ := req.Pack() - outconn.Write(utils.BuildPacketData(dnsServerAddress)) outconn.Write(append([]byte{0, byte(len(b))}, b...)) answer, _ := ioutil.ReadAll(outconn) + defer func() { + answer = nil + }() if len(answer) < 3 { debugf("dns query fail,%s", err) outconn.Close() @@ -451,3 +448,18 @@ func debugf(v ...interface{}) { log.Printf(str, v[1:]...) } } +func getOutconn(targetAddr string) (outconn net.Conn, err error) { + if outboundUDP { + outconn, err = clienttransport.TOUConnectHost(forwardAddr, method, password, compress, timeout*1000) + } else { + if outboundEncrypt { + outconn, err = clienttransport.TCPSConnectHost(forwardAddr, method, password, compress, timeout*1000) + } else { + outconn, err = net.DialTimeout("tcp", forwardAddr, time.Duration(timeout)*time.Second) + } + } + if targetAddr != "" { + outconn.Write(utils.BuildPacketData(targetAddr)) + } + return +}